diff options
author | David Robillard <d@drobilla.net> | 2023-01-12 10:30:10 -0500 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2023-01-14 11:53:28 -0500 |
commit | bd4f79646f623e929e6aa22bea028952b515aeef (patch) | |
tree | e2dcc2635bb2b12b9c552755b35a0343005ce88a | |
parent | 786a28e4a2bc9fce95a8d2e6cd1a4c76d2ae1a0f (diff) | |
download | pugl-bd4f79646f623e929e6aa22bea028952b515aeef.tar.gz pugl-bd4f79646f623e929e6aa22bea028952b515aeef.tar.bz2 pugl-bd4f79646f623e929e6aa22bea028952b515aeef.zip |
Add general string hint interface
This replaces the window title and class name APIs with a more general one that
can be easily extended to other things, like icon names, more detailed
application hints, and so on.
40 files changed, 343 insertions, 153 deletions
diff --git a/bindings/cpp/include/pugl/pugl.hpp b/bindings/cpp/include/pugl/pugl.hpp index 109e947..90c5de6 100644 --- a/bindings/cpp/include/pugl/pugl.hpp +++ b/bindings/cpp/include/pugl/pugl.hpp @@ -68,6 +68,16 @@ private: using Rect = PuglRect; ///< @copydoc PuglRect +/// @copydoc PuglStringHint +enum class StringHint { + className = 1, ///< @copydoc PUGL_CLASS_NAME + windowTitle, ///< @copydoc PUGL_WINDOW_TITLE +}; + +static_assert(static_cast<StringHint>(PUGL_WINDOW_TITLE) == + StringHint::windowTitle, + ""); + /** @defgroup puglpp_events Events @{ @@ -306,10 +316,17 @@ public: /// @copydoc puglGetNativeWorld void* nativeWorld() noexcept { return puglGetNativeWorld(cobj()); } - /// @copydoc puglSetClassName - Status setClassName(const char* const name) noexcept + /// @copydoc puglSetWorldString + Status setString(StringHint key, const char* const value) noexcept { - return static_cast<Status>(puglSetClassName(cobj(), name)); + return static_cast<Status>( + puglSetWorldString(cobj(), static_cast<PuglStringHint>(key), value)); + } + + /// @copydoc puglGetWorldString + const char* getString(StringHint key) noexcept + { + return puglGetWorldString(cobj(), static_cast<PuglStringHint>(key)); } /// @copydoc puglGetTime @@ -473,6 +490,19 @@ public: return puglGetViewHint(cobj(), static_cast<PuglViewHint>(hint)); } + /// @copydoc puglSetViewString + Status setString(StringHint key, const char* const value) noexcept + { + return static_cast<Status>( + puglSetViewString(cobj(), static_cast<PuglStringHint>(key), value)); + } + + /// @copydoc puglGetViewString + const char* getString(StringHint key) noexcept + { + return puglGetViewString(cobj(), static_cast<PuglStringHint>(key)); + } + /** @} @name Frame @@ -503,12 +533,6 @@ public: @{ */ - /// @copydoc puglSetWindowTitle - Status setWindowTitle(const char* title) noexcept - { - return static_cast<Status>(puglSetWindowTitle(cobj(), title)); - } - /// @copydoc puglSetParentWindow Status setParentWindow(NativeView parent) noexcept { diff --git a/doc/c/view.rst b/doc/c/view.rst index ff8d3db..0a26e14 100644 --- a/doc/c/view.rst +++ b/doc/c/view.rst @@ -31,7 +31,7 @@ For example: const PuglSpan defaultWidth = 1920; const PuglSpan defaultHeight = 1080; - puglSetWindowTitle(view, "My Window"); + puglSetViewString(view, PUGL_WINDOW_TITLE, "My Window"); puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 1920, 1080); puglSetSizeHint(view, PUGL_MIN_SIZE, 640, 480); puglSetSizeHint(view, PUGL_MIN_ASPECT, 1, 1); diff --git a/doc/c/world.rst b/doc/c/world.rst index 83d9dbd..dcebba0 100644 --- a/doc/c/world.rst +++ b/doc/c/world.rst @@ -31,13 +31,13 @@ For example, Vulkan requires thread support: PuglWorld* world = puglNewWorld(PUGL_MODULE, PUGL_WORLD_THREADS) -It is a good idea to set a class name for your project with :func:`puglSetClassName`. +It is a good idea to set a class name for your project with :func:`puglSetWorldString`. This allows the window system to distinguish different applications and, for example, users to set up rules to manage their windows nicely: .. code-block:: c - puglSetClassName(world, "MyAwesomeProject") + puglSetWorldString(world, PUGL_CLASS_NAME, "MyAwesomeProject") .. _setting-application-data: diff --git a/examples/pugl_cairo_demo.c b/examples/pugl_cairo_demo.c index f580540..0a4ddd9 100644 --- a/examples/pugl_cairo_demo.c +++ b/examples/pugl_cairo_demo.c @@ -211,11 +211,11 @@ main(int argc, char** argv) } app.world = puglNewWorld(PUGL_PROGRAM, 0); - puglSetClassName(app.world, "PuglCairoDemo"); + puglSetWorldString(app.world, PUGL_CLASS_NAME, "PuglCairoDemo"); PuglView* view = puglNewView(app.world); - puglSetWindowTitle(view, "Pugl Cairo Demo"); + puglSetViewString(view, PUGL_WINDOW_TITLE, "Pugl Cairo Demo"); puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 512, 512); puglSetSizeHint(view, PUGL_MIN_SIZE, 256, 256); puglSetSizeHint(view, PUGL_MAX_SIZE, 2048, 2048); diff --git a/examples/pugl_clipboard_demo.c b/examples/pugl_clipboard_demo.c index d4ab50f..efeb1eb 100644 --- a/examples/pugl_clipboard_demo.c +++ b/examples/pugl_clipboard_demo.c @@ -217,9 +217,9 @@ main(int argc, char** argv) PuglView* const view = app.cube.view; puglSetWorldHandle(app.world, &app); - puglSetClassName(app.world, "Pugl Test"); + puglSetWorldString(app.world, PUGL_CLASS_NAME, "Pugl Test"); - puglSetWindowTitle(view, "Pugl Clipboard Demo"); + puglSetViewString(view, PUGL_WINDOW_TITLE, "Pugl Clipboard Demo"); puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 512, 512); puglSetSizeHint(view, PUGL_MIN_SIZE, 128, 128); puglSetBackend(view, puglGlBackend()); diff --git a/examples/pugl_cpp_demo.cpp b/examples/pugl_cpp_demo.cpp index 49d547f..2b3c0a1 100644 --- a/examples/pugl_cpp_demo.cpp +++ b/examples/pugl_cpp_demo.cpp @@ -112,9 +112,9 @@ main(int argc, char** argv) CubeView view{world}; PuglFpsPrinter fpsPrinter{}; - world.setClassName("PuglCppDemo"); + world.setString(pugl::StringHint::className, "PuglCppDemo"); - view.setWindowTitle("Pugl C++ Demo"); + view.setString(pugl::StringHint::windowTitle, "Pugl C++ Demo"); view.setSizeHint(pugl::SizeHint::defaultSize, 512, 512); view.setSizeHint(pugl::SizeHint::minSize, 64, 64); view.setSizeHint(pugl::SizeHint::maxSize, 1024, 1024); diff --git a/examples/pugl_cursor_demo.c b/examples/pugl_cursor_demo.c index 9aa0301..ba9c54f 100644 --- a/examples/pugl_cursor_demo.c +++ b/examples/pugl_cursor_demo.c @@ -119,11 +119,11 @@ main(int argc, char** argv) app.world = puglNewWorld(PUGL_PROGRAM, 0); puglSetWorldHandle(app.world, &app); - puglSetClassName(app.world, "PuglCursorDemo"); + puglSetWorldString(app.world, PUGL_CLASS_NAME, "PuglCursorDemo"); PuglView* view = puglNewView(app.world); - puglSetWindowTitle(view, "Pugl Cursor Demo"); + puglSetViewString(view, PUGL_WINDOW_TITLE, "Pugl Cursor Demo"); puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 512, 256); puglSetSizeHint(view, PUGL_MIN_SIZE, 128, 64); puglSetSizeHint(view, PUGL_MAX_SIZE, 512, 256); diff --git a/examples/pugl_embed_demo.c b/examples/pugl_embed_demo.c index 7efba81..a66e032 100644 --- a/examples/pugl_embed_demo.c +++ b/examples/pugl_embed_demo.c @@ -259,7 +259,7 @@ main(int argc, char** argv) app.parent = puglNewView(app.world); app.child = puglNewView(app.world); - puglSetClassName(app.world, "PuglEmbedDemo"); + puglSetWorldString(app.world, PUGL_CLASS_NAME, "PuglEmbedDemo"); const PuglRect parentFrame = {0, 0, 512, 512}; puglSetSizeHint(app.parent, PUGL_DEFAULT_SIZE, 512, 512); @@ -282,7 +282,7 @@ main(int argc, char** argv) const uint8_t title[] = { 'P', 'u', 'g', 'l', ' ', 'P', 'r', 0xC3, 0xBC, 'f', 'u', 'n', 'g', 0}; - puglSetWindowTitle(app.parent, (const char*)title); + puglSetViewString(app.parent, PUGL_WINDOW_TITLE, (const char*)title); if ((st = puglRealize(app.parent))) { return logError("Failed to create parent window (%s)\n", puglStrerror(st)); diff --git a/examples/pugl_management_demo.c b/examples/pugl_management_demo.c index ec2805b..30a026e 100644 --- a/examples/pugl_management_demo.c +++ b/examples/pugl_management_demo.c @@ -118,7 +118,7 @@ toggleDialog(DemoApp* const app) puglSetViewHint(app->dialogView.view, PUGL_RESIZABLE, true); puglSetViewHint( app->dialogView.view, PUGL_VIEW_TYPE, PUGL_VIEW_TYPE_DIALOG); - puglSetWindowTitle(app->dialogView.view, "Dialog"); + puglSetViewString(app->dialogView.view, PUGL_WINDOW_TITLE, "Dialog"); } return puglShow(app->dialogView.view, PUGL_SHOW_RAISE); @@ -228,7 +228,7 @@ main(int argc, char** argv) app.world = puglNewWorld(PUGL_PROGRAM, 0); puglSetWorldHandle(app.world, &app); - puglSetClassName(app.world, "PuglDemoApp"); + puglSetWorldString(app.world, PUGL_CLASS_NAME, "PuglDemoApp"); app.mainView.view = puglNewView(app.world); app.mainView.label = "Main: "; @@ -243,7 +243,7 @@ main(int argc, char** argv) puglSetViewHint(app.mainView.view, PUGL_IGNORE_KEY_REPEAT, true); puglSetViewHint(app.mainView.view, PUGL_RESIZABLE, true); puglSetViewHint(app.mainView.view, PUGL_VIEW_TYPE, PUGL_VIEW_TYPE_NORMAL); - puglSetWindowTitle(app.mainView.view, "Main Window"); + puglSetViewString(app.mainView.view, PUGL_WINDOW_TITLE, "Main Window"); PuglStatus st = PUGL_SUCCESS; if ((st = puglRealize(app.mainView.view))) { diff --git a/examples/pugl_print_events.c b/examples/pugl_print_events.c index c2b1512..96a8889 100644 --- a/examples/pugl_print_events.c +++ b/examples/pugl_print_events.c @@ -41,8 +41,8 @@ main(void) app.world = puglNewWorld(PUGL_PROGRAM, 0); app.view = puglNewView(app.world); - puglSetClassName(app.world, "PuglPrintEvents"); - puglSetWindowTitle(app.view, "Pugl Event Printer"); + puglSetWorldString(app.world, PUGL_CLASS_NAME, "PuglPrintEvents"); + puglSetViewString(app.view, PUGL_WINDOW_TITLE, "Pugl Event Printer"); puglSetSizeHint(app.view, PUGL_DEFAULT_SIZE, 512, 512); puglSetBackend(app.view, puglStubBackend()); puglSetHandle(app.view, &app); diff --git a/examples/pugl_shader_demo.c b/examples/pugl_shader_demo.c index 6c2fcd6..5773a9d 100644 --- a/examples/pugl_shader_demo.c +++ b/examples/pugl_shader_demo.c @@ -245,8 +245,8 @@ setupPugl(PuglTestApp* app) app->rects = makeRects(app->numRects); // Set up world and view - puglSetClassName(app->world, "PuglShaderDemo"); - puglSetWindowTitle(app->view, "Pugl OpenGL Shader Demo"); + puglSetWorldString(app->world, PUGL_CLASS_NAME, "PuglShaderDemo"); + puglSetViewString(app->view, PUGL_WINDOW_TITLE, "Pugl OpenGL Shader Demo"); puglSetSizeHint(app->view, PUGL_DEFAULT_SIZE, defaultSpan, defaultSpan); puglSetSizeHint(app->view, PUGL_MIN_SIZE, 128, 128); puglSetSizeHint(app->view, PUGL_MAX_SIZE, 2048, 2048); diff --git a/examples/pugl_vulkan_cpp_demo.cpp b/examples/pugl_vulkan_cpp_demo.cpp index 1537920..7e652a5 100644 --- a/examples/pugl_vulkan_cpp_demo.cpp +++ b/examples/pugl_vulkan_cpp_demo.cpp @@ -1713,8 +1713,8 @@ run(const char* const programPath, const auto height = static_cast<PuglSpan>(app.extent.height); // Realize window so we can set up Vulkan - app.world.setClassName("PuglVulkanCppDemo"); - app.view.setWindowTitle("Pugl Vulkan C++ Demo"); + app.world.setString(pugl::StringHint::className, "PuglVulkanCppDemo"); + app.view.setString(pugl::StringHint::windowTitle, "Pugl Vulkan C++ Demo"); app.view.setSizeHint(pugl::SizeHint::defaultSize, width, height); app.view.setSizeHint(pugl::SizeHint::minSize, width / 4U, height / 4U); app.view.setBackend(pugl::vulkanBackend()); diff --git a/examples/pugl_vulkan_demo.c b/examples/pugl_vulkan_demo.c index c12c390..e1daeb0 100644 --- a/examples/pugl_vulkan_demo.c +++ b/examples/pugl_vulkan_demo.c @@ -1063,7 +1063,7 @@ main(int argc, char** argv) } // Create window - puglSetWindowTitle(app.view, "Pugl Vulkan Demo"); + puglSetViewString(app.view, PUGL_WINDOW_TITLE, "Pugl Vulkan Demo"); puglSetSizeHint(app.view, PUGL_DEFAULT_SIZE, defaultWidth, defaultHeight); puglSetHandle(app.view, &app); puglSetBackend(app.view, puglVulkanBackend()); diff --git a/examples/pugl_window_demo.c b/examples/pugl_window_demo.c index d401c67..67086bb 100644 --- a/examples/pugl_window_demo.c +++ b/examples/pugl_window_demo.c @@ -176,7 +176,7 @@ main(int argc, char** argv) app.cubes[1].view = puglNewView(app.world); puglSetWorldHandle(app.world, &app); - puglSetClassName(app.world, "PuglWindowDemo"); + puglSetWorldString(app.world, PUGL_CLASS_NAME, "PuglWindowDemo"); PuglStatus st = PUGL_SUCCESS; for (unsigned i = 0; i < 2; ++i) { @@ -185,7 +185,7 @@ main(int argc, char** argv) cube->dist = 10; - puglSetWindowTitle(view, "Pugl Window Demo"); + puglSetViewString(view, PUGL_WINDOW_TITLE, "Pugl Window Demo"); puglSetPosition(view, (PuglCoord)(pad + (128U + pad) * i), (PuglCoord)(pad + (128U + pad) * i)); diff --git a/include/pugl/pugl.h b/include/pugl/pugl.h index 468798e..4427c5a 100644 --- a/include/pugl/pugl.h +++ b/include/pugl/pugl.h @@ -63,6 +63,36 @@ typedef struct { PuglSpan height; } PuglRect; +/// A string property for configuration +typedef enum { + /** + The application class name. + + This is a stable identifier for the application, which should be a short + camel-case name like "MyApp". This should be the same for every instance + of the application, but different from any other application. On X11 and + Windows, it is used to set the class name of windows (that underlie + realized views), which is used for things like loading configuration, or + custom window management rules. + */ + PUGL_CLASS_NAME = 1, + + /** + The title of the window or application. + + This is used by the system to display a title for the application or + window, for example in title bars or window/application switchers. It is + only used to display a label to the user, not as an identifier, and can + change over time to reflect the current state of the application. For + example, it is common for programs to add the name of the current + document, like "myfile.txt - Fancy Editor". + */ + PUGL_WINDOW_TITLE, +} PuglStringHint; + +/// The number of #PuglStringHint values +#define PUGL_NUM_STRING_HINTS ((unsigned)PUGL_WINDOW_TITLE + 1U) + /** @} @defgroup pugl_events Events @@ -772,22 +802,24 @@ void* puglGetNativeWorld(PuglWorld* world); /** - Set the class name of the application. + Set a string property to configure the world or application. - This is a stable identifier for the application, used as the window - class/instance name on X11 and Windows. It is not displayed to the user, - but can be used in scripts and by window managers, so it should be the same - for every instance of the application, but different from other - applications. + The string value only needs to be valid for the duration of this call, it + will be copied if necessary. */ PUGL_API PuglStatus -puglSetClassName(PuglWorld* world, const char* name); +puglSetWorldString(PuglWorld* world, PuglStringHint key, const char* value); -/// Get the class name of the application, or null +/** + Get a world or application string property. + + The returned string should be accessed immediately, or copied. It may + become invalid upon any call to any function that manipulates the same view. +*/ PUGL_API const char* -puglGetClassName(const PuglWorld* world); +puglGetWorldString(const PuglWorld* world, PuglStringHint key); /** Return the time in seconds. @@ -870,7 +902,7 @@ typedef uintptr_t PuglNativeView; /// Handle for a view's opaque user data typedef void* PuglHandle; -/// A hint for configuring a view +/// An integer hint for configuring a view typedef enum { PUGL_CONTEXT_API, ///< OpenGL render API (GL/GLES) PUGL_CONTEXT_VERSION_MAJOR, ///< OpenGL context major version @@ -1054,6 +1086,27 @@ int puglGetViewHint(const PuglView* view, PuglViewHint hint); /** + Set a string property to configure view properties. + + This is similar to puglSetViewHint() but sets hints with string values. The + string value only needs to be valid for the duration of this call, it will + be copied if necessary. +*/ +PUGL_API +PuglStatus +puglSetViewString(PuglView* view, PuglStringHint key, const char* value); + +/** + Get a view string property. + + The returned string should be accessed immediately, or copied. It may + become invalid upon any call to any function that manipulates the same view. +*/ +PUGL_API +const char* +puglGetViewString(const PuglView* view, PuglStringHint key); + +/** Return the scale factor of the view. This factor describe how large UI elements (especially text) should be @@ -1144,22 +1197,6 @@ puglSetSizeHint(PuglView* view, */ /** - Set the title of the window. - - This only makes sense for non-embedded views that will have a corresponding - top-level window, and sets the title, typically displayed in the title bar - or in window switchers. -*/ -PUGL_API -PuglStatus -puglSetWindowTitle(PuglView* view, const char* title); - -/// Return the title of the window, or null -PUGL_API -const char* -puglGetWindowTitle(const PuglView* view); - -/** Set the parent window for embedding a view in an existing window. This must be called before puglRealize(), reparenting is not supported. @@ -1677,13 +1714,37 @@ puglDestroy(PuglView* view) } /** + Set the class name of the application. + + This is a stable identifier for the application, used as the window + class/instance name on X11 and Windows. It is not displayed to the user, + but can be used in scripts and by window managers, so it should be the same + for every instance of the application, but different from other + applications. +*/ +static inline PUGL_DEPRECATED_BY("puglSetWorldString") +PuglStatus +puglSetClassName(PuglWorld* world, const char* name) +{ + return puglSetWorldString(world, PUGL_CLASS_NAME, name); +} + +/// Get the class name of the application, or null +static inline PUGL_DEPRECATED_BY("puglGetWorldString") +const char* +puglGetClassName(const PuglWorld* world) +{ + return puglGetWorldString(world, PUGL_CLASS_NAME); +} + +/** Set the window class name before creating a window. */ static inline PUGL_DEPRECATED_BY("puglSetClassName") void puglInitWindowClass(PuglView* view, const char* name) { - puglSetClassName(puglGetWorld(view), name); + puglSetWorldString(puglGetWorld(view), PUGL_CLASS_NAME, name); } /** @@ -1837,6 +1898,28 @@ puglInitBackend(PuglView* view, const PuglBackend* backend) } /** + Set the title of the window. + + This only makes sense for non-embedded views that will have a corresponding + top-level window, and sets the title, typically displayed in the title bar + or in window switchers. +*/ +static inline PUGL_DEPRECATED_BY("puglSetViewString") +PuglStatus +puglSetWindowTitle(PuglView* view, const char* title) +{ + return puglSetViewString(view, PUGL_WINDOW_TITLE, title); +} + +/// Return the title of the window, or null +static inline PUGL_DEPRECATED_BY("puglGetViewString") +const char* +puglGetWindowTitle(const PuglView* view) +{ + return puglGetViewString(view, PUGL_WINDOW_TITLE); +} + +/** Realize a view by creating a corresponding system view or window. The view should be fully configured using the above functions before this is @@ -1848,7 +1931,7 @@ static inline PUGL_DEPRECATED_BY("puglRealize") PuglStatus puglCreateWindow(PuglView* view, const char* title) { - puglSetWindowTitle(view, title); + puglSetViewString(view, PUGL_WINDOW_TITLE, title); return puglRealize(view); } diff --git a/src/common.c b/src/common.c index 7113461..eafb07a 100644 --- a/src/common.c +++ b/src/common.c @@ -52,7 +52,7 @@ puglNewWorld(PuglWorldType type, PuglWorldFlags flags) world->startTime = puglGetTime(world); world->type = type; - puglSetString(&world->className, "Pugl"); + puglSetString(&world->strings[PUGL_CLASS_NAME], "Pugl"); return world; } @@ -61,7 +61,11 @@ void puglFreeWorld(PuglWorld* const world) { puglFreeWorldInternals(world); - free(world->className); + + for (size_t i = 0; i < PUGL_NUM_STRING_HINTS; ++i) { + free(world->strings[i]); + } + free(world->views); free(world); } @@ -79,16 +83,26 @@ puglGetWorldHandle(PuglWorld* world) } PuglStatus -puglSetClassName(PuglWorld* const world, const char* const name) +puglSetWorldString(PuglWorld* const world, + const PuglStringHint key, + const char* const value) { - puglSetString(&world->className, name); + if ((unsigned)key < 0 || (unsigned)key >= PUGL_NUM_STRING_HINTS) { + return PUGL_BAD_PARAMETER; + } + + puglSetString(&world->strings[key], value); return PUGL_SUCCESS; } const char* -puglGetClassName(const PuglWorld* world) +puglGetWorldString(const PuglWorld* const world, const PuglStringHint key) { - return world->className; + if ((unsigned)key < 0 || (unsigned)key >= PUGL_NUM_STRING_HINTS) { + return NULL; + } + + return world->strings[key]; } static void @@ -161,7 +175,10 @@ puglFreeView(PuglView* view) } } - free(view->title); + for (size_t i = 0; i < PUGL_NUM_STRING_HINTS; ++i) { + free(view->strings[i]); + } + puglFreeViewInternals(view); free(view); } @@ -239,6 +256,29 @@ puglGetViewHint(const PuglView* view, PuglViewHint hint) return PUGL_DONT_CARE; } +PuglStatus +puglSetViewString(PuglView* const view, + const PuglStringHint key, + const char* const value) +{ + if ((unsigned)key < 0 || (unsigned)key >= PUGL_NUM_STRING_HINTS) { + return PUGL_BAD_PARAMETER; + } + + puglSetString(&view->strings[key], value); + return puglViewStringChanged(view, key, view->strings[key]); +} + +const char* +puglGetViewString(const PuglView* const view, const PuglStringHint key) +{ + if ((unsigned)key < 0 || (unsigned)key >= PUGL_NUM_STRING_HINTS) { + return NULL; + } + + return view->strings[key]; +} + PuglRect puglGetFrame(const PuglView* view) { @@ -267,12 +307,6 @@ puglGetFrame(const PuglView* view) return frame; } -const char* -puglGetWindowTitle(const PuglView* const view) -{ - return view->title; -} - PuglStatus puglSetParentWindow(PuglView* view, PuglNativeView parent) { diff --git a/src/internal.c b/src/internal.c index c607fa7..06bf416 100644 --- a/src/internal.c +++ b/src/internal.c @@ -47,6 +47,10 @@ puglSetBlob(PuglBlob* const dest, const void* const data, const size_t len) void puglSetString(char** dest, const char* string) { + if (*dest == string) { + return; + } + const size_t len = string ? strlen(string) : 0U; if (!len) { @@ -58,6 +62,19 @@ puglSetString(char** dest, const char* string) } } +PuglStatus +puglStoreViewString(PuglView* const view, + const PuglStringHint key, + const char* const value) +{ + if ((unsigned)key >= 0 && (unsigned)key < PUGL_NUM_STRING_HINTS) { + puglSetString(&view->strings[key], value); + return PUGL_SUCCESS; + } + + return PUGL_BAD_PARAMETER; +} + uint32_t puglDecodeUTF8(const uint8_t* buf) { diff --git a/src/internal.h b/src/internal.h index 6400422..f30532b 100644 --- a/src/internal.h +++ b/src/internal.h @@ -29,6 +29,11 @@ puglSetBlob(PuglBlob* dest, const void* data, size_t len); void puglSetString(char** dest, const char* string); +/// Handle a changed string property +PUGL_API +PuglStatus +puglViewStringChanged(PuglView* view, PuglStringHint key, const char* value); + /// Return the Unicode code point for `buf` or the replacement character uint32_t puglDecodeUTF8(const uint8_t* buf); @@ -1286,10 +1286,11 @@ puglRealize(PuglView* view) impl->window = window; [window setContentSize:framePt.size]; - if (view->title) { + const char* const title = view->strings[PUGL_WINDOW_TITLE]; + if (title) { NSString* titleString = - [[NSString alloc] initWithBytes:view->title - length:strlen(view->title) + [[NSString alloc] initWithBytes:title + length:strlen(title) encoding:NSUTF8StringEncoding]; [window setTitle:titleString]; @@ -1656,17 +1657,28 @@ puglGetNativeView(PuglView* view) } PuglStatus -puglSetWindowTitle(PuglView* view, const char* title) +puglViewStringChanged(PuglView* const view, + const PuglStringHint key, + const char* const value) { - puglSetString(&view->title, title); + if (!view->impl->window) { + return PUGL_SUCCESS; + } - if (view->impl->window) { - NSString* titleString = - [[NSString alloc] initWithBytes:title - length:strlen(title) - encoding:NSUTF8StringEncoding]; + switch (key) { + case PUGL_CLASS_NAME: + return PUGL_UNSUPPORTED; - [view->impl->window setTitle:titleString]; + case PUGL_WINDOW_TITLE: + if (view->impl->window) { + NSString* const titleString = + [[NSString alloc] initWithBytes:value + length:strlen(value) + encoding:NSUTF8StringEncoding]; + + [view->impl->window setTitle:titleString]; + } + break; } return PUGL_SUCCESS; diff --git a/src/types.h b/src/types.h index 12e4acf..9817474 100644 --- a/src/types.h +++ b/src/types.h @@ -53,12 +53,12 @@ struct PuglViewImpl { PuglInternals* impl; PuglHandle handle; PuglEventFunc eventFunc; - char* title; PuglNativeView parent; uintptr_t transientParent; PuglConfigureEvent lastConfigure; PuglHints hints; PuglViewSize sizeHints[PUGL_NUM_SIZE_HINTS]; + char* strings[PUGL_NUM_STRING_HINTS]; int defaultX; int defaultY; PuglViewStage stage; @@ -69,10 +69,10 @@ struct PuglViewImpl { struct PuglWorldImpl { PuglWorldInternals* impl; PuglWorldHandle handle; - char* className; double startTime; size_t numViews; PuglView** views; + char* strings[PUGL_NUM_STRING_HINTS]; PuglWorldType type; }; @@ -269,7 +269,7 @@ puglRealize(PuglView* view) view->hints[PUGL_REFRESH_RATE] = (int)devMode.dmDisplayFrequency; // Register window class if necessary - if (!puglRegisterWindowClass(view->world->className)) { + if (!puglRegisterWindowClass(view->world->strings[PUGL_CLASS_NAME])) { return PUGL_REGISTRATION_FAILED; } @@ -280,7 +280,7 @@ puglRealize(PuglView* view) } // Set basic window hints and attributes - puglSetWindowTitle(view, view->title ? view->title : ""); + puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); puglSetTransientParent(view, view->transientParent); view->impl->scaleFactor = puglWinGetViewScaleFactor(view); @@ -370,7 +370,7 @@ puglFreeViewInternals(PuglView* view) void puglFreeWorldInternals(PuglWorld* world) { - UnregisterClass(world->className, NULL); + UnregisterClass(world->strings[PUGL_CLASS_NAME], NULL); free(world->impl); } @@ -1153,12 +1153,16 @@ puglGetNativeView(PuglView* view) } PuglStatus -puglSetWindowTitle(PuglView* view, const char* title) +puglViewStringChanged(PuglView* const view, + const PuglStringHint key, + const char* const value) { - puglSetString(&view->title, title); + if (!view->impl->hwnd) { + return PUGL_SUCCESS; + } - if (view->impl->hwnd) { - wchar_t* wtitle = puglUtf8ToWideChar(title); + if (key == PUGL_WINDOW_TITLE) { + wchar_t* const wtitle = puglUtf8ToWideChar(value); if (wtitle) { SetWindowTextW(view->impl->hwnd, wtitle); free(wtitle); @@ -1548,19 +1552,19 @@ puglWinCreateWindow(PuglView* const view, HWND* const hwnd, HDC* const hdc) { - const char* className = (const char*)view->world->className; - const unsigned winFlags = puglWinGetWindowFlags(view); - const unsigned winExFlags = puglWinGetWindowExFlags(view); - const PuglRect frame = getInitialFrame(view); + const char* className = (const char*)view->world->strings[PUGL_CLASS_NAME]; // The meaning of "parent" depends on the window type (WS_CHILD) PuglNativeView parent = view->parent ? view->parent : view->transientParent; // Calculate initial window rectangle - RECT wr = {(long)frame.x, - (long)frame.y, - (long)frame.x + frame.width, - (long)frame.y + frame.height}; + const unsigned winFlags = puglWinGetWindowFlags(view); + const unsigned winExFlags = puglWinGetWindowExFlags(view); + const PuglRect frame = getInitialFrame(view); + RECT wr = {(long)frame.x, + (long)frame.y, + (long)frame.x + frame.width, + (long)frame.y + frame.height}; AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags); // Create window and get drawing context @@ -625,9 +625,10 @@ puglRealize(PuglView* const view) #endif // Set basic window hints and attributes - XClassHint classHint = {world->className, world->className}; + char* const className = world->strings[PUGL_CLASS_NAME]; + XClassHint classHint = {className, className}; XSetClassHint(display, impl->win, &classHint); - puglSetWindowTitle(view, view->title ? view->title : ""); + puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); puglSetTransientParent(view, view->transientParent); updateSizeHints(view); @@ -1881,23 +1882,32 @@ puglGetNativeView(PuglView* const view) } PuglStatus -puglSetWindowTitle(PuglView* const view, const char* const title) +puglViewStringChanged(PuglView* const view, + const PuglStringHint key, + const char* const value) { - Display* display = view->world->impl->display; - const PuglX11Atoms* const atoms = &view->world->impl->atoms; + Display* const display = view->world->impl->display; + const PuglX11Atoms* atoms = &view->world->impl->atoms; + + if (!view->impl->win) { + return PUGL_SUCCESS; + } - puglSetString(&view->title, title); + switch (key) { + case PUGL_CLASS_NAME: + break; - if (view->impl->win) { - XStoreName(display, view->impl->win, title); + case PUGL_WINDOW_TITLE: + XStoreName(display, view->impl->win, value); XChangeProperty(display, view->impl->win, atoms->NET_WM_NAME, atoms->UTF8_STRING, 8, PropModeReplace, - (const uint8_t*)title, - (int)strlen(title)); + (const uint8_t*)value, + (int)strlen(value)); + break; } return PUGL_SUCCESS; diff --git a/test/test_cairo.c b/test/test_cairo.c index c64a9eb..11d6cce 100644 --- a/test/test_cairo.c +++ b/test/test_cairo.c @@ -60,8 +60,8 @@ main(int argc, char** argv) PuglTest test = {world, view, opts, false}; // Set up and show view - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl Cairo Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl Cairo Test"); puglSetHandle(test.view, &test); puglSetBackend(test.view, puglCairoBackend()); puglSetEventFunc(test.view, onEvent); diff --git a/test/test_clipboard.c b/test/test_clipboard.c index a99d150..debe8e3 100644 --- a/test/test_clipboard.c +++ b/test/test_clipboard.c @@ -48,12 +48,12 @@ main(int argc, char** argv) puglParseTestOptions(&argc, &argv), false}; - puglSetClassName(test.world, "PuglTest"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); // Set up views for (unsigned i = 0U; i < 2; ++i) { test.views[i] = puglNewView(test.world); - puglSetWindowTitle(test.world, "Pugl Clipboard Test"); + puglSetViewString(test.world, PUGL_WINDOW_TITLE, "Pugl Clipboard Test"); puglSetBackend(test.views[i], puglStubBackend()); puglSetHandle(test.views[i], &test); puglSetEventFunc(test.views[i], onEvent); diff --git a/test/test_cursor.c b/test/test_cursor.c index 89e81aa..11a52d7 100644 --- a/test/test_cursor.c +++ b/test/test_cursor.c @@ -46,8 +46,8 @@ main(int argc, char** argv) PuglTest test = {world, view, opts, false}; // Set up and show view - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl Cursor Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl Cursor Test"); puglSetHandle(test.view, &test); puglSetBackend(test.view, puglStubBackend()); puglSetEventFunc(test.view, onEvent); diff --git a/test/test_gl.c b/test/test_gl.c index a53854a..8854cad 100644 --- a/test/test_gl.c +++ b/test/test_gl.c @@ -79,8 +79,8 @@ main(int argc, char** argv) PuglTest test = {world, view, opts, false}; // Set up and show view - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl OpenGL Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl OpenGL Test"); puglSetHandle(test.view, &test); puglSetBackend(test.view, puglGlBackend()); puglSetEventFunc(test.view, onEvent); diff --git a/test/test_gl_free_unrealized.c b/test/test_gl_free_unrealized.c index 0ad3dda..82fc18b 100644 --- a/test/test_gl_free_unrealized.c +++ b/test/test_gl_free_unrealized.c @@ -31,8 +31,8 @@ main(int argc, char** argv) // Set up view test.view = puglNewView(test.world); - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl OpenGL Free Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl OpenGL Free Test"); puglSetBackend(test.view, puglGlBackend()); puglSetHandle(test.view, &test); puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 256, 256); diff --git a/test/test_gl_hints.c b/test/test_gl_hints.c index e0114ff..16b51c5 100644 --- a/test/test_gl_hints.c +++ b/test/test_gl_hints.c @@ -30,8 +30,8 @@ main(void) PuglView* const view = puglNewView(world); // Set up view - puglSetClassName(world, "PuglTest"); - puglSetWindowTitle(view, "Pugl OpenGL Hints Test"); + puglSetWorldString(world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(view, PUGL_WINDOW_TITLE, "Pugl OpenGL Hints Test"); puglSetBackend(view, puglGlBackend()); puglSetEventFunc(view, onEvent); puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 256, 256); diff --git a/test/test_local_copy_paste.c b/test/test_local_copy_paste.c index 5918418..737f193 100644 --- a/test/test_local_copy_paste.c +++ b/test/test_local_copy_paste.c @@ -127,8 +127,8 @@ main(int argc, char** argv) // Set up view app.view = puglNewView(app.world); - puglSetClassName(app.world, "PuglTest"); - puglSetWindowTitle(app.view, "Pugl Copy/Paste Test"); + puglSetWorldString(app.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(app.view, PUGL_WINDOW_TITLE, "Pugl Copy/Paste Test"); puglSetBackend(app.view, puglStubBackend()); puglSetHandle(app.view, &app); puglSetEventFunc(app.view, onEvent); diff --git a/test/test_realize.c b/test/test_realize.c index bf55e50..7e37319 100644 --- a/test/test_realize.c +++ b/test/test_realize.c @@ -63,8 +63,8 @@ main(int argc, char** argv) // Set up view test.view = puglNewView(test.world); - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl Realize Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl Realize Test"); puglSetHandle(test.view, &test); assert(puglRealize(test.view) == PUGL_BAD_BACKEND); diff --git a/test/test_redisplay.c b/test/test_redisplay.c index 60dd219..1276136 100644 --- a/test/test_redisplay.c +++ b/test/test_redisplay.c @@ -111,8 +111,8 @@ main(int argc, char** argv) // Set up view test.view = puglNewView(test.world); - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl Redisplay Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl Redisplay Test"); puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); diff --git a/test/test_remote_copy_paste.c b/test/test_remote_copy_paste.c index 2fe55fe..a9f079e 100644 --- a/test/test_remote_copy_paste.c +++ b/test/test_remote_copy_paste.c @@ -142,8 +142,8 @@ main(int argc, char** argv) // Set up copier view app.copierView = puglNewView(app.world); - puglSetClassName(app.world, "PuglTest"); - puglSetWindowTitle(app.copierView, "Pugl Copy Test"); + puglSetWorldString(app.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(app.copierView, PUGL_WINDOW_TITLE, "Pugl Copy Test"); puglSetBackend(app.copierView, puglStubBackend()); puglSetHandle(app.copierView, &app); puglSetEventFunc(app.copierView, onCopierEvent); @@ -152,8 +152,8 @@ main(int argc, char** argv) // Set up paster view app.pasterView = puglNewView(app.world); - puglSetClassName(app.world, "PuglTest"); - puglSetWindowTitle(app.pasterView, "Pugl Paste Test"); + puglSetWorldString(app.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(app.pasterView, PUGL_WINDOW_TITLE, "Pugl Paste Test"); puglSetBackend(app.pasterView, puglStubBackend()); puglSetHandle(app.pasterView, &app); puglSetEventFunc(app.pasterView, onPasterEvent); diff --git a/test/test_show_hide.c b/test/test_show_hide.c index 3b0acae..1517203 100644 --- a/test/test_show_hide.c +++ b/test/test_show_hide.c @@ -109,8 +109,8 @@ main(int argc, char** argv) // Set up view test.view = puglNewView(test.world); - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl Show/Hide Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl Show/Hide Test"); puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); diff --git a/test/test_size.c b/test/test_size.c index 88afa16..e3f738a 100644 --- a/test/test_size.c +++ b/test/test_size.c @@ -77,8 +77,8 @@ main(int argc, char** argv) // Set up view with size bounds and an aspect ratio test.view = puglNewView(test.world); - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl Size Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl Size Test"); puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); diff --git a/test/test_stub.c b/test/test_stub.c index 2badfb2..bd8a0eb 100644 --- a/test/test_stub.c +++ b/test/test_stub.c @@ -46,8 +46,8 @@ main(int argc, char** argv) PuglTest test = {world, view, opts, false}; // Set up and show view - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl Stub Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl Stub Test"); puglSetHandle(test.view, &test); puglSetBackend(test.view, puglStubBackend()); puglSetEventFunc(test.view, onEvent); diff --git a/test/test_stub_hints.c b/test/test_stub_hints.c index b59c590..4c9d0c4 100644 --- a/test/test_stub_hints.c +++ b/test/test_stub_hints.c @@ -30,8 +30,8 @@ main(void) PuglView* const view = puglNewView(world); // Set up view - puglSetClassName(world, "PuglTest"); - puglSetWindowTitle(view, "Pugl Stub Hints Test"); + puglSetWorldString(world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(view, PUGL_WINDOW_TITLE, "Pugl Stub Hints Test"); puglSetBackend(view, puglStubBackend()); puglSetEventFunc(view, onEvent); puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 256, 256); diff --git a/test/test_timer.c b/test/test_timer.c index 42a4140..d8cc5c3 100644 --- a/test/test_timer.c +++ b/test/test_timer.c @@ -106,8 +106,8 @@ main(int argc, char** argv) // Set up view test.view = puglNewView(test.world); - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl Timer Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl Timer Test"); puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); diff --git a/test/test_update.c b/test/test_update.c index 1fc10d1..4710ca2 100644 --- a/test/test_update.c +++ b/test/test_update.c @@ -84,8 +84,8 @@ main(int argc, char** argv) // Set up view test.view = puglNewView(test.world); - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl Update Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl Update Test"); puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); diff --git a/test/test_view.c b/test/test_view.c index 7f1a112..a6af9f2 100644 --- a/test/test_view.c +++ b/test/test_view.c @@ -67,8 +67,8 @@ main(int argc, char** argv) // Set up view test.view = puglNewView(test.world); - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl View Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl View Test"); puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); @@ -76,9 +76,10 @@ main(int argc, char** argv) puglSetPosition(test.view, 384, 640); // Check basic accessors - assert(!strcmp(puglGetClassName(test.world), "PuglTest")); - assert(!strcmp(puglGetWindowTitle(test.view), "Pugl View Test")); assert(puglGetBackend(test.view) == puglStubBackend()); + assert(!strcmp(puglGetWorldString(test.world, PUGL_CLASS_NAME), "PuglTest")); + assert( + !strcmp(puglGetViewString(test.view, PUGL_WINDOW_TITLE), "Pugl View Test")); // Create and show window assert(!puglRealize(test.view)); diff --git a/test/test_vulkan.c b/test/test_vulkan.c index ec7732d..1376676 100644 --- a/test/test_vulkan.c +++ b/test/test_vulkan.c @@ -166,8 +166,8 @@ main(int argc, char** argv) assert(!createInstance(&test)); // Create window - puglSetClassName(test.world, "PuglTest"); - puglSetWindowTitle(test.view, "Pugl Vulkan Test"); + puglSetWorldString(test.world, PUGL_CLASS_NAME, "PuglTest"); + puglSetViewString(test.view, PUGL_WINDOW_TITLE, "Pugl Vulkan Test"); puglSetHandle(test.view, &test); puglSetBackend(test.view, puglVulkanBackend()); puglSetEventFunc(test.view, onEvent); |