diff options
33 files changed, 356 insertions, 313 deletions
diff --git a/bindings/cpp/include/pugl/pugl.hpp b/bindings/cpp/include/pugl/pugl.hpp index 5387c91..5b47fe6 100644 --- a/bindings/cpp/include/pugl/pugl.hpp +++ b/bindings/cpp/include/pugl/pugl.hpp @@ -327,6 +327,15 @@ public: using Backend = PuglBackend; ///< @copydoc PuglBackend using NativeView = PuglNativeView; ///< @copydoc PuglNativeView +/// @copydoc PuglSizeHint +enum class SizeHint { + defaultSize, ///< @copydoc PUGL_DEFAULT_SIZE + minSize, ///< @copydoc PUGL_MIN_SIZE + maxSize, ///< @copydoc PUGL_MAX_SIZE + minAspect, ///< @copydoc PUGL_MIN_ASPECT + maxAspect, ///< @copydoc PUGL_MAX_ASPECT +}; + /// @copydoc PuglViewHint enum class ViewHint { useCompatProfile, ///< @copydoc PUGL_USE_COMPAT_PROFILE @@ -458,29 +467,11 @@ public: return static_cast<Status>(puglSetFrame(cobj(), frame)); } - /// @copydoc puglSetDefaultSize - Status setDefaultSize(int width, int height) noexcept - { - return static_cast<Status>(puglSetDefaultSize(cobj(), width, height)); - } - - /// @copydoc puglSetMinSize - Status setMinSize(int width, int height) noexcept - { - return static_cast<Status>(puglSetMinSize(cobj(), width, height)); - } - - /// @copydoc puglSetMaxSize - Status setMaxSize(int width, int height) noexcept - { - return static_cast<Status>(puglSetMaxSize(cobj(), width, height)); - } - - /// @copydoc puglSetAspectRatio - Status setAspectRatio(int minX, int minY, int maxX, int maxY) noexcept + /// @copydoc puglSetSizeHint + Status setSizeHint(SizeHint hint, PuglSpan width, PuglSpan height) noexcept { return static_cast<Status>( - puglSetAspectRatio(cobj(), minX, minY, maxX, maxY)); + puglSetSizeHint(cobj(), static_cast<PuglSizeHint>(hint), width, height)); } /** diff --git a/doc/conf.py.in b/doc/conf.py.in index 3fa8ea2..b208553 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -25,6 +25,7 @@ _opaque = [ "VkResult", "VkSurfaceKHR", "size_t", + "uint16_t", "uint32_t", "uintptr_t", ] diff --git a/examples/pugl_cairo_demo.c b/examples/pugl_cairo_demo.c index 2cd4493..cf34158 100644 --- a/examples/pugl_cairo_demo.c +++ b/examples/pugl_cairo_demo.c @@ -216,9 +216,9 @@ main(int argc, char** argv) PuglView* view = puglNewView(app.world); puglSetWindowTitle(view, "Pugl Cairo Demo"); - puglSetDefaultSize(view, 512, 512); - puglSetMinSize(view, 256, 256); - puglSetMaxSize(view, 2048, 2048); + puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 512, 512); + puglSetSizeHint(view, PUGL_MIN_SIZE, 256, 256); + puglSetSizeHint(view, PUGL_MAX_SIZE, 2048, 2048); puglSetViewHint(view, PUGL_RESIZABLE, app.opts.resizable); puglSetHandle(view, &app); puglSetBackend(view, puglCairoBackend()); diff --git a/examples/pugl_cpp_demo.cpp b/examples/pugl_cpp_demo.cpp index 7794529..015e7aa 100644 --- a/examples/pugl_cpp_demo.cpp +++ b/examples/pugl_cpp_demo.cpp @@ -115,10 +115,11 @@ main(int argc, char** argv) world.setClassName("PuglCppDemo"); view.setWindowTitle("Pugl C++ Demo"); - view.setDefaultSize(512, 512); - view.setMinSize(64, 64); - view.setMaxSize(256, 256); - view.setAspectRatio(1, 1, 16, 9); + view.setSizeHint(pugl::SizeHint::defaultSize, 512, 512); + view.setSizeHint(pugl::SizeHint::minSize, 64, 64); + view.setSizeHint(pugl::SizeHint::maxSize, 1024, 1024); + view.setSizeHint(pugl::SizeHint::minAspect, 1, 1); + view.setSizeHint(pugl::SizeHint::maxAspect, 16, 9); view.setBackend(pugl::glBackend()); view.setHint(pugl::ViewHint::resizable, opts.resizable); view.setHint(pugl::ViewHint::samples, opts.samples); diff --git a/examples/pugl_cursor_demo.c b/examples/pugl_cursor_demo.c index 8462193..c9839da 100644 --- a/examples/pugl_cursor_demo.c +++ b/examples/pugl_cursor_demo.c @@ -124,9 +124,9 @@ main(int argc, char** argv) PuglView* view = puglNewView(app.world); puglSetWindowTitle(view, "Pugl Cursor Demo"); - puglSetDefaultSize(view, 512, 256); - puglSetMinSize(view, 128, 64); - puglSetMaxSize(view, 512, 256); + puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 512, 256); + puglSetSizeHint(view, PUGL_MIN_SIZE, 128, 64); + puglSetSizeHint(view, PUGL_MAX_SIZE, 512, 256); puglSetBackend(view, puglGlBackend()); puglSetViewHint(view, PUGL_USE_DEBUG_CONTEXT, app.opts.errorChecking); diff --git a/examples/pugl_embed_demo.c b/examples/pugl_embed_demo.c index 085cfa7..66605b7 100644 --- a/examples/pugl_embed_demo.c +++ b/examples/pugl_embed_demo.c @@ -277,10 +277,11 @@ main(int argc, char** argv) puglSetClassName(app.world, "PuglEmbedDemo"); const PuglRect parentFrame = {0, 0, 512, 512}; - puglSetDefaultSize(app.parent, 512, 512); - puglSetMinSize(app.parent, borderWidth * 3, borderWidth * 3); - puglSetMaxSize(app.parent, 1024, 1024); - puglSetAspectRatio(app.parent, 1, 1, 16, 9); + puglSetSizeHint(app.parent, PUGL_DEFAULT_SIZE, 512, 512); + puglSetSizeHint(app.parent, PUGL_MIN_SIZE, 192, 192); + puglSetSizeHint(app.parent, PUGL_MAX_SIZE, 1024, 1024); + puglSetSizeHint(app.parent, PUGL_MIN_ASPECT, 1, 1); + puglSetSizeHint(app.parent, PUGL_MAX_ASPECT, 16, 9); puglSetBackend(app.parent, puglGlBackend()); puglSetViewHint(app.parent, PUGL_USE_DEBUG_CONTEXT, opts.errorChecking); diff --git a/examples/pugl_print_events.c b/examples/pugl_print_events.c index 1bbe33a..3c07873 100644 --- a/examples/pugl_print_events.c +++ b/examples/pugl_print_events.c @@ -43,7 +43,7 @@ main(void) puglSetClassName(app.world, "PuglPrintEvents"); puglSetWindowTitle(app.view, "Pugl Event Printer"); - puglSetDefaultSize(app.view, 512, 512); + puglSetSizeHint(app.view, PUGL_DEFAULT_SIZE, 512, 512); puglSetBackend(app.view, puglStubBackend()); puglSetHandle(app.view, &app); puglSetEventFunc(app.view, onEvent); diff --git a/examples/pugl_shader_demo.c b/examples/pugl_shader_demo.c index db448ca..8f2c2e7 100644 --- a/examples/pugl_shader_demo.c +++ b/examples/pugl_shader_demo.c @@ -45,8 +45,7 @@ # define SHADER_DIR "shaders/" #endif -static const int defaultWidth = 512; -static const int defaultHeight = 512; +static const PuglSpan defaultSpan = 512; static const uintptr_t resizeTimerId = 1u; typedef struct { @@ -180,7 +179,7 @@ makeRects(const size_t numRects) { Rect* rects = (Rect*)calloc(numRects, sizeof(Rect)); for (size_t i = 0; i < numRects; ++i) { - rects[i] = makeRect(i, (float)defaultWidth); + rects[i] = makeRect(i, defaultSpan); } return rects; @@ -262,10 +261,11 @@ setupPugl(PuglTestApp* app) // Set up world and view puglSetClassName(app->world, "PuglShaderDemo"); puglSetWindowTitle(app->view, "Pugl OpenGL Shader Demo"); - puglSetDefaultSize(app->view, defaultWidth, defaultHeight); - puglSetMinSize(app->view, defaultWidth / 4, defaultHeight / 4); - puglSetMaxSize(app->view, defaultWidth * 4, defaultHeight * 4); - puglSetAspectRatio(app->view, 1, 1, 16, 9); + puglSetSizeHint(app->view, PUGL_DEFAULT_SIZE, defaultSpan, defaultSpan); + puglSetSizeHint(app->view, PUGL_MIN_SIZE, 128, 128); + puglSetSizeHint(app->view, PUGL_MAX_SIZE, 2048, 2048); + puglSetSizeHint(app->view, PUGL_MIN_ASPECT, 1, 1); + puglSetSizeHint(app->view, PUGL_MAX_ASPECT, 16, 9); puglSetBackend(app->view, puglGlBackend()); puglSetViewHint(app->view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); puglSetViewHint(app->view, PUGL_USE_DEBUG_CONTEXT, app->opts.errorChecking); diff --git a/examples/pugl_vulkan_cpp_demo.cpp b/examples/pugl_vulkan_cpp_demo.cpp index 3f635ba..61c7b80 100644 --- a/examples/pugl_vulkan_cpp_demo.cpp +++ b/examples/pugl_vulkan_cpp_demo.cpp @@ -1708,17 +1708,16 @@ run(const char* const programPath, { PuglVulkanDemo app{programPath, opts, numRects}; - VkResult r = VK_SUCCESS; - const auto width = static_cast<int>(app.extent.width); - const auto height = static_cast<int>(app.extent.height); + VkResult r = VK_SUCCESS; + const auto width = app.extent.width; + const auto height = app.extent.height; // Realize window so we can set up Vulkan app.world.setClassName("PuglVulkanCppDemo"); app.view.setWindowTitle("Pugl Vulkan C++ Demo"); - app.view.setAspectRatio(1, 1, 16, 9); - app.view.setDefaultSize(width, height); - app.view.setMinSize(width / 4, height / 4); - app.view.setMaxSize(width * 4, height * 4); + app.view.setSizeHint(pugl::SizeHint::defaultSize, width, height); + app.view.setSizeHint(pugl::SizeHint::minSize, width / 4, height / 4); + app.view.setSizeHint(pugl::SizeHint::maxSize, width * 4, height * 4); app.view.setBackend(pugl::vulkanBackend()); app.view.setHint(pugl::ViewHint::resizable, opts.resizable); const pugl::Status st = app.view.realize(); diff --git a/examples/pugl_vulkan_demo.c b/examples/pugl_vulkan_demo.c index b7ff146..ec465bb 100644 --- a/examples/pugl_vulkan_demo.c +++ b/examples/pugl_vulkan_demo.c @@ -1036,9 +1036,8 @@ main(int argc, char** argv) memset(&app, 0, sizeof(app)); VulkanState* vk = &app.vk; - const uint32_t defaultWidth = 640; - const uint32_t defaultHeight = 360; - const PuglRect frame = {0, 0, defaultWidth, defaultHeight}; + const PuglSpan defaultWidth = 640; + const PuglSpan defaultHeight = 360; // Parse command line options app.opts = puglParseTestOptions(&argc, &argv); @@ -1065,7 +1064,7 @@ main(int argc, char** argv) // Create window puglSetWindowTitle(app.view, "Pugl Vulkan Demo"); - puglSetFrame(app.view, frame); + puglSetSizeHint(app.view, PUGL_DEFAULT_SIZE, defaultWidth, defaultHeight); puglSetHandle(app.view, &app); puglSetBackend(app.view, puglVulkanBackend()); puglSetViewHint(app.view, PUGL_RESIZABLE, app.opts.resizable); diff --git a/examples/pugl_window_demo.c b/examples/pugl_window_demo.c index 912ccb6..7cb3722 100644 --- a/examples/pugl_window_demo.c +++ b/examples/pugl_window_demo.c @@ -194,9 +194,9 @@ main(int argc, char** argv) puglSetWindowTitle(view, "Pugl Window Demo"); puglSetFrame(view, frame); - puglSetDefaultSize(view, 512, 512); - puglSetMinSize(view, 128, 128); - puglSetMaxSize(view, 2048, 2048); + puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 512, 512); + puglSetSizeHint(view, PUGL_MIN_SIZE, 128, 128); + puglSetSizeHint(view, PUGL_MAX_SIZE, 2048, 2048); puglSetBackend(view, puglGlBackend()); puglSetViewHint(view, PUGL_USE_DEBUG_CONTEXT, opts.errorChecking); diff --git a/include/pugl/pugl.h b/include/pugl/pugl.h index 4abe247..2c00a30 100644 --- a/include/pugl/pugl.h +++ b/include/pugl/pugl.h @@ -60,6 +60,14 @@ PUGL_BEGIN_DECLS */ /** + A pixel span (width or height) within/of a view. + + Due to platform limits, the span of a view in either dimension should be + between 1 and 10000. +*/ +typedef uint16_t PuglSpan; + +/** A rectangle. This is used to describe things like view position and size. Pugl generally @@ -813,6 +821,43 @@ typedef enum { PUGL_TRUE = 1 ///< Explicitly true } PuglViewHintValue; +/** + A hint for configuring/constraining the size of a view. + + The system will attempt to make the view's window adhere to these, but they + are suggestions, not hard constraints. Applications should handle any view + size gracefully. +*/ +typedef enum { + PUGL_DEFAULT_SIZE, ///< Default size + PUGL_MIN_SIZE, ///< Minimum size + PUGL_MAX_SIZE, ///< Maximum size + + /** + Fixed aspect ratio. + + If set, the view's size should be constrained to this aspect ratio. + Mutually exclusive with #PUGL_MIN_ASPECT and #PUGL_MAX_ASPECT. + */ + PUGL_FIXED_ASPECT, + + /** + Minimum aspect ratio. + + If set, the view's size should be constrained to an aspect ratio no lower + than this. Mutually exclusive with #PUGL_FIXED_ASPECT. + */ + PUGL_MIN_ASPECT, + + /** + Maximum aspect ratio. + + If set, the view's size should be constrained to an aspect ratio no higher + than this. Mutually exclusive with #PUGL_FIXED_ASPECT. + */ + PUGL_MAX_ASPECT +} PuglSizeHint; + /// A function called when an event occurs typedef PuglStatus (*PuglEventFunc)(PuglView* view, const PuglEvent* event); @@ -940,64 +985,23 @@ PuglStatus puglSetFrame(PuglView* view, PuglRect frame); /** - Set the default size of the view. - - This should be called before puglResize() to set the default size of the - view, which will be the initial size of the window if this is a top level - view. + Set a size hint for the view. - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -PUGL_API -PuglStatus -puglSetDefaultSize(PuglView* view, int width, int height); + This can be used to set the default, minimum, and maximum size of a view, as + well as the supported range of aspect ratios. -/** - Set the minimum size of the view. - - If an initial minimum size is known, this should be called before - puglRealize() to avoid stutter, though it can be called afterwards as well. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -PUGL_API -PuglStatus -puglSetMinSize(PuglView* view, int width, int height); - -/** - Set the maximum size of the view. - - If an initial maximum size is known, this should be called before - puglRealize() to avoid stutter, though it can be called afterwards as well. + This should be called before puglResize() so the initial window for the view + can be configured correctly. @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is not yet realized. */ PUGL_API PuglStatus -puglSetMaxSize(PuglView* view, int width, int height); - -/** - Set the view aspect ratio range. - - The x and y values here represent a ratio of width to height. To set a - fixed aspect ratio, set the minimum and maximum values to the same ratio. - - Note that setting different minimum and maximum constraints does not - currently work on MacOS (the minimum is used), so only setting a fixed - aspect ratio works properly across all platforms. - - If an initial aspect ratio is known, this should be called before - puglRealize() to avoid stutter, though it can be called afterwards as well. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -PUGL_API -PuglStatus -puglSetAspectRatio(PuglView* view, int minX, int minY, int maxX, int maxY); +puglSetSizeHint(PuglView* view, + PuglSizeHint hint, + PuglSpan width, + PuglSpan height); /** @} @@ -1460,7 +1464,7 @@ static inline PUGL_DEPRECATED_BY("puglSetMinSize") void puglInitWindowMinSize(PuglView* view, int width, int height) { - puglSetMinSize(view, width, height); + puglSetSizeHint(view, PUGL_MIN_SIZE, (PuglSpan)width, (PuglSpan)height); } /** @@ -1481,7 +1485,8 @@ puglInitWindowAspectRatio(PuglView* view, int maxX, int maxY) { - puglSetAspectRatio(view, minX, minY, maxX, maxY); + puglSetSizeHint(view, PUGL_MIN_ASPECT, (PuglSpan)minX, (PuglSpan)minY); + puglSetSizeHint(view, PUGL_MAX_ASPECT, (PuglSpan)maxX, (PuglSpan)maxY); } /** @@ -1675,6 +1680,87 @@ PUGL_DEPRECATED_BY("puglHide") PuglStatus puglHideWindow(PuglView* view); +/** + Set the default size of the view. + + This should be called before puglResize() to set the default size of the + view, which will be the initial size of the window if this is a top level + view. + + @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is + not yet realized. +*/ +static inline PUGL_DEPRECATED_BY("puglSetSizeHint") +PuglStatus +puglSetDefaultSize(PuglView* view, int width, int height) +{ + return puglSetSizeHint( + view, PUGL_DEFAULT_SIZE, (PuglSpan)width, (PuglSpan)height); +} + +/** + Set the minimum size of the view. + + If an initial minimum size is known, this should be called before + puglRealize() to avoid stutter, though it can be called afterwards as well. + + @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is + not yet realized. +*/ +static inline PUGL_DEPRECATED_BY("puglSetSizeHint") +PuglStatus +puglSetMinSize(PuglView* view, int width, int height) +{ + return puglSetSizeHint( + view, PUGL_MIN_SIZE, (PuglSpan)width, (PuglSpan)height); +} + +/** + Set the maximum size of the view. + + If an initial maximum size is known, this should be called before + puglRealize() to avoid stutter, though it can be called afterwards as well. + + @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is + not yet realized. +*/ +static inline PUGL_DEPRECATED_BY("puglSetSizeHint") +PuglStatus +puglSetMaxSize(PuglView* view, int width, int height) +{ + return puglSetSizeHint( + view, PUGL_MAX_SIZE, (PuglSpan)width, (PuglSpan)height); +} + +/** + Set the view aspect ratio range. + + The x and y values here represent a ratio of width to height. To set a + fixed aspect ratio, set the minimum and maximum values to the same ratio. + + Note that setting different minimum and maximum constraints does not + currently work on MacOS (the minimum is used), so only setting a fixed + aspect ratio works properly across all platforms. + + If an initial aspect ratio is known, this should be called before + puglRealize() to avoid stutter, though it can be called afterwards as well. + + @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is + not yet realized. +*/ +static inline PUGL_DEPRECATED_BY("puglSetSizeHint") +PuglStatus +puglSetAspectRatio(PuglView* view, int minX, int minY, int maxX, int maxY) +{ + const PuglStatus st0 = + puglSetSizeHint(view, PUGL_MIN_ASPECT, (PuglSpan)minX, (PuglSpan)minY); + + const PuglStatus st1 = + puglSetSizeHint(view, PUGL_MAX_ASPECT, (PuglSpan)maxX, (PuglSpan)maxY); + + return st0 ? st0 : st1; +} + #endif // PUGL_DISABLE_DEPRECATED /** diff --git a/src/implementation.c b/src/implementation.c index 166aebd..c3394ce 100644 --- a/src/implementation.c +++ b/src/implementation.c @@ -139,9 +139,9 @@ puglNewView(PuglWorld* const world) return NULL; } - view->world = world; - view->minWidth = 1; - view->minHeight = 1; + view->world = world; + view->sizeHints[PUGL_MIN_SIZE].width = 1; + view->sizeHints[PUGL_MIN_SIZE].height = 1; puglSetDefaultHints(view->hints); @@ -220,12 +220,11 @@ updateViewRect(PuglView* view) - (NSSize)intrinsicContentSize { - if (puglview->defaultWidth || puglview->defaultHeight) { - return sizePoints( - puglview, puglview->defaultWidth, puglview->defaultHeight); - } + const PuglViewSize defaultSize = puglview->sizeHints[PUGL_DEFAULT_SIZE]; - return NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric); + return (defaultSize.width && defaultSize.height) + ? sizePoints(puglview, defaultSize.width, defaultSize.height) + : NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric); } - (BOOL)isFlipped @@ -889,6 +888,47 @@ puglConstraint(const id item, constant:(CGFloat)constant]; } +static PuglStatus +updateSizeHint(PuglView* const view, const PuglSizeHint hint) +{ + const PuglSpan width = view->sizeHints[hint].width; + const PuglSpan height = view->sizeHints[hint].height; + if (!width || !height) { + return PUGL_FAILURE; + } + + switch (hint) { + case PUGL_DEFAULT_SIZE: + break; + + case PUGL_MIN_SIZE: + [view->impl->window setContentMinSize:sizePoints(view, width, height)]; + break; + + case PUGL_MAX_SIZE: + [view->impl->window setContentMaxSize:sizePoints(view, width, height)]; + break; + + case PUGL_FIXED_ASPECT: + [view->impl->window setContentAspectRatio:sizePoints(view, width, height)]; + break; + + case PUGL_MIN_ASPECT: + case PUGL_MAX_ASPECT: + break; + } + + return PUGL_SUCCESS; +} + +static void +updateSizeHints(PuglView* const view) +{ + for (unsigned i = 0u; i <= PUGL_MAX_ASPECT; ++i) { + updateSizeHint(view, (PuglSizeHint)i); + } +} + PuglStatus puglRealize(PuglView* view) { @@ -935,15 +975,16 @@ puglRealize(PuglView* view) } if (view->frame.width == 0.0 && view->frame.height == 0.0) { - if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) { + const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; + if (!defaultSize.width || !defaultSize.height) { return PUGL_BAD_CONFIGURATION; } const double screenWidthPx = [screen frame].size.width * scaleFactor; const double screenHeightPx = [screen frame].size.height * scaleFactor; - view->frame.width = view->defaultWidth; - view->frame.height = view->defaultHeight; + view->frame.width = defaultSize.width; + view->frame.height = defaultSize.height; view->frame.x = screenWidthPx / 2.0 - view->frame.width / 2.0; view->frame.y = screenHeightPx / 2.0 - view->frame.height / 2.0; } @@ -966,26 +1007,27 @@ puglRealize(PuglView* view) addConstraint:puglConstraint(impl->wrapperView, NSLayoutAttributeWidth, NSLayoutRelationGreaterThanOrEqual, - view->minWidth)]; + view->sizeHints[PUGL_MIN_SIZE].width)]; [impl->wrapperView addConstraint:puglConstraint(impl->wrapperView, NSLayoutAttributeHeight, NSLayoutRelationGreaterThanOrEqual, - view->minHeight)]; + view->sizeHints[PUGL_MIN_SIZE].height)]; - if (view->maxWidth && view->maxHeight) { + if (view->sizeHints[PUGL_MAX_SIZE].width && + view->sizeHints[PUGL_MAX_SIZE].height) { [impl->wrapperView addConstraint:puglConstraint(impl->wrapperView, NSLayoutAttributeWidth, NSLayoutRelationLessThanOrEqual, - view->maxWidth)]; + view->sizeHints[PUGL_MAX_SIZE].width)]; [impl->wrapperView addConstraint:puglConstraint(impl->wrapperView, NSLayoutAttributeHeight, NSLayoutRelationLessThanOrEqual, - view->maxHeight)]; + view->sizeHints[PUGL_MAX_SIZE].height)]; } // Create draw view to be rendered to @@ -1028,21 +1070,12 @@ puglRealize(PuglView* view) [window setTitle:titleString]; } - if (view->minWidth || view->minHeight) { - [window - setContentMinSize:sizePoints(view, view->minWidth, view->minHeight)]; - } - impl->window = window; - ((NSWindow*)window).delegate = [[PuglWindowDelegate alloc] initWithPuglWindow:window]; - if (view->minAspectX && view->minAspectY) { - [window setContentAspectRatio:sizePoints(view, - view->minAspectX, - view->minAspectY)]; - } + impl->window = window; + updateSizeHints(view); puglSetFrame(view, view->frame); [window setContentView:impl->wrapperView]; @@ -1358,60 +1391,19 @@ puglSetFrame(PuglView* view, const PuglRect frame) } PuglStatus -puglSetDefaultSize(PuglView* const view, const int width, const int height) -{ - view->defaultWidth = width; - view->defaultHeight = height; - return PUGL_SUCCESS; -} - -PuglStatus -puglSetMinSize(PuglView* const view, const int width, const int height) -{ - view->minWidth = width; - view->minHeight = height; - - if (view->impl->window && (view->minWidth || view->minHeight)) { - [view->impl->window - setContentMinSize:sizePoints(view, view->minWidth, view->minHeight)]; - } - - return PUGL_SUCCESS; -} - -PuglStatus -puglSetMaxSize(PuglView* const view, const int width, const int height) +puglSetSizeHint(PuglView* const view, + const PuglSizeHint hint, + const PuglSpan width, + const PuglSpan height) { - view->maxWidth = width; - view->maxHeight = height; - - if (view->impl->window && (view->maxWidth || view->maxHeight)) { - [view->impl->window - setContentMaxSize:sizePoints(view, view->maxWidth, view->maxHeight)]; + if ((unsigned)hint > (unsigned)PUGL_MAX_ASPECT) { + return PUGL_BAD_PARAMETER; } - return PUGL_SUCCESS; -} + view->sizeHints[hint].width = width; + view->sizeHints[hint].height = height; -PuglStatus -puglSetAspectRatio(PuglView* const view, - const int minX, - const int minY, - const int maxX, - const int maxY) -{ - view->minAspectX = minX; - view->minAspectY = minY; - view->maxAspectX = maxX; - view->maxAspectY = maxY; - - if (view->impl->window && view->minAspectX && view->minAspectY) { - [view->impl->window setContentAspectRatio:sizePoints(view, - view->minAspectX, - view->minAspectY)]; - } - - return PUGL_SUCCESS; + return view->impl->window ? updateSizeHint(view, hint) : PUGL_SUCCESS; } PuglStatus diff --git a/src/types.h b/src/types.h index 67e9eef..0187dad 100644 --- a/src/types.h +++ b/src/types.h @@ -21,6 +21,12 @@ typedef struct PuglInternalsImpl PuglInternals; /// View hints typedef int PuglHints[PUGL_NUM_VIEW_HINTS]; +/// View size (both X and Y coordinates) +typedef struct { + PuglSpan width; + PuglSpan height; +} PuglViewSize; + /// Blob of arbitrary data typedef struct { void* data; ///< Dynamically allocated data @@ -41,16 +47,7 @@ struct PuglViewImpl { PuglRect frame; PuglConfigureEvent lastConfigure; PuglHints hints; - int defaultWidth; - int defaultHeight; - int minWidth; - int minHeight; - int maxWidth; - int maxHeight; - int minAspectX; - int minAspectY; - int maxAspectX; - int maxAspectY; + PuglViewSize sizeHints[(unsigned)PUGL_MAX_ASPECT + 1u]; bool visible; }; @@ -522,8 +522,11 @@ constrainAspect(const PuglView* const view, RECT* const size, const WPARAM wParam) { - const float minA = (float)view->minAspectX / (float)view->minAspectY; - const float maxA = (float)view->maxAspectX / (float)view->maxAspectY; + const PuglViewSize minAspect = view->sizeHints[PUGL_MIN_ASPECT]; + const PuglViewSize maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; + + const float minA = (float)minAspect.width / (float)minAspect.height; + const float maxA = (float)maxAspect.width / (float)maxAspect.height; const float w = (float)(size->right - size->left); const float h = (float)(size->bottom - size->top); const float a = w / h; @@ -602,7 +605,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) } break; case WM_SIZING: - if (view->minAspectX) { + if (view->sizeHints[PUGL_MIN_ASPECT].width) { constrainAspect(view, (RECT*)lParam, wParam); return TRUE; } @@ -624,11 +627,12 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_GETMINMAXINFO: mmi = (MINMAXINFO*)lParam; - mmi->ptMinTrackSize.x = view->minWidth; - mmi->ptMinTrackSize.y = view->minHeight; - if (view->maxWidth > 0 && view->maxHeight > 0) { - mmi->ptMaxTrackSize.x = view->maxWidth; - mmi->ptMaxTrackSize.y = view->maxHeight; + mmi->ptMinTrackSize.x = view->sizeHints[PUGL_MIN_SIZE].width; + mmi->ptMinTrackSize.y = view->sizeHints[PUGL_MIN_SIZE].height; + if (view->sizeHints[PUGL_MAX_SIZE].width && + view->sizeHints[PUGL_MAX_SIZE].height) { + mmi->ptMaxTrackSize.x = view->sizeHints[PUGL_MAX_SIZE].width; + mmi->ptMaxTrackSize.y = view->sizeHints[PUGL_MAX_SIZE].height; } break; case WM_PAINT: @@ -1008,40 +1012,17 @@ puglSetFrame(PuglView* view, const PuglRect frame) } PuglStatus -puglSetDefaultSize(PuglView* const view, const int width, const int height) +puglSetSizeHint(PuglView* const view, + const PuglSizeHint hint, + const PuglSpan width, + const PuglSpan height) { - view->defaultWidth = width; - view->defaultHeight = height; - return PUGL_SUCCESS; -} - -PuglStatus -puglSetMinSize(PuglView* const view, const int width, const int height) -{ - view->minWidth = width; - view->minHeight = height; - return PUGL_SUCCESS; -} - -PuglStatus -puglSetMaxSize(PuglView* const view, const int width, const int height) -{ - view->maxWidth = width; - view->maxHeight = height; - return PUGL_SUCCESS; -} + if ((unsigned)hint > (unsigned)PUGL_MAX_ASPECT || width < 0 || height < 0) { + return PUGL_BAD_PARAMETER; + } -PuglStatus -puglSetAspectRatio(PuglView* const view, - const int minX, - const int minY, - const int maxX, - const int maxY) -{ - view->minAspectX = minX; - view->minAspectY = minY; - view->maxAspectX = maxX; - view->maxAspectY = maxY; + view->sizeHints[hint].width = width; + view->sizeHints[hint].height = height; return PUGL_SUCCESS; } @@ -1206,7 +1187,8 @@ puglWinCreateWindow(PuglView* const view, const unsigned winExFlags = puglWinGetWindowExFlags(view); if (view->frame.width <= 0.0 && view->frame.height <= 0.0) { - if (view->defaultWidth <= 0.0 && view->defaultHeight <= 0.0) { + const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; + if (!defaultSize.width || !defaultSize.height) { return PUGL_BAD_CONFIGURATION; } @@ -1216,10 +1198,10 @@ puglWinCreateWindow(PuglView* const view, const int screenWidth = desktopRect.right - desktopRect.left; const int screenHeight = desktopRect.bottom - desktopRect.top; - view->frame.width = view->defaultWidth; - view->frame.height = view->defaultHeight; - view->frame.x = screenWidth / 2.0 - view->frame.width / 2.0; - view->frame.y = screenHeight / 2.0 - view->frame.height / 2.0; + view->frame.width = defaultSize.width; + view->frame.height = defaultSize.height; + view->frame.x = (screenWidth - view->frame.width) / 2.0; + view->frame.y = (screenHeight - view->frame.height) / 2.0; } // The meaning of "parent" depends on the window type (WS_CHILD) @@ -224,30 +224,45 @@ updateSizeHints(const PuglView* const view) sizeHints.max_width = (int)view->frame.width; sizeHints.max_height = (int)view->frame.height; } else { - if (view->defaultWidth || view->defaultHeight) { + const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; + if (defaultSize.width && defaultSize.height) { sizeHints.flags |= PBaseSize; - sizeHints.base_width = view->defaultWidth; - sizeHints.base_height = view->defaultHeight; + sizeHints.base_width = defaultSize.width; + sizeHints.base_height = defaultSize.height; } - if (view->minWidth || view->minHeight) { + const PuglViewSize minSize = view->sizeHints[PUGL_MIN_SIZE]; + if (minSize.width && minSize.height) { sizeHints.flags |= PMinSize; - sizeHints.min_width = view->minWidth; - sizeHints.min_height = view->minHeight; + sizeHints.min_width = minSize.width; + sizeHints.min_height = minSize.height; } - if (view->maxWidth || view->maxHeight) { + const PuglViewSize maxSize = view->sizeHints[PUGL_MAX_SIZE]; + if (maxSize.width && maxSize.height) { sizeHints.flags |= PMaxSize; - sizeHints.max_width = view->maxWidth; - sizeHints.max_height = view->maxHeight; + sizeHints.max_width = maxSize.width; + sizeHints.max_height = maxSize.height; } - if (view->minAspectX) { + const PuglViewSize minAspect = view->sizeHints[PUGL_MIN_ASPECT]; + const PuglViewSize maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; + if (minAspect.width && minAspect.height && maxAspect.width && + maxAspect.height) { sizeHints.flags |= PAspect; - sizeHints.min_aspect.x = view->minAspectX; - sizeHints.min_aspect.y = view->minAspectY; - sizeHints.max_aspect.x = view->maxAspectX; - sizeHints.max_aspect.y = view->maxAspectY; + sizeHints.min_aspect.x = minAspect.width; + sizeHints.min_aspect.y = minAspect.height; + sizeHints.max_aspect.x = maxAspect.width; + sizeHints.max_aspect.y = maxAspect.height; + } + + const PuglViewSize fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT]; + if (fixedAspect.width && fixedAspect.height) { + sizeHints.flags |= PAspect; + sizeHints.min_aspect.x = fixedAspect.width; + sizeHints.min_aspect.y = fixedAspect.height; + sizeHints.max_aspect.x = fixedAspect.width; + sizeHints.max_aspect.y = fixedAspect.height; } } @@ -314,12 +329,13 @@ puglRealize(PuglView* const view) // Set the size to the default if it has not already been set if (view->frame.width <= 0.0 && view->frame.height <= 0.0) { - if (view->defaultWidth <= 0.0 || view->defaultHeight <= 0.0) { + const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; + if (!defaultSize.width || !defaultSize.height) { return PUGL_BAD_CONFIGURATION; } - view->frame.width = view->defaultWidth; - view->frame.height = view->defaultHeight; + view->frame.width = defaultSize.width; + view->frame.height = defaultSize.height; } // Center top-level windows if a position has not been set @@ -1366,40 +1382,17 @@ puglSetFrame(PuglView* const view, const PuglRect frame) } PuglStatus -puglSetDefaultSize(PuglView* const view, const int width, const int height) -{ - view->defaultWidth = width; - view->defaultHeight = height; - return updateSizeHints(view); -} - -PuglStatus -puglSetMinSize(PuglView* const view, const int width, const int height) +puglSetSizeHint(PuglView* const view, + const PuglSizeHint hint, + const PuglSpan width, + const PuglSpan height) { - view->minWidth = width; - view->minHeight = height; - return updateSizeHints(view); -} - -PuglStatus -puglSetMaxSize(PuglView* const view, const int width, const int height) -{ - view->maxWidth = width; - view->maxHeight = height; - return updateSizeHints(view); -} + if ((unsigned)hint > (unsigned)PUGL_MAX_ASPECT) { + return PUGL_BAD_PARAMETER; + } -PuglStatus -puglSetAspectRatio(PuglView* const view, - const int minX, - const int minY, - const int maxX, - const int maxY) -{ - view->minAspectX = minX; - view->minAspectY = minY; - view->maxAspectX = maxX; - view->maxAspectY = maxY; + view->sizeHints[hint].width = width; + view->sizeHints[hint].height = height; return updateSizeHints(view); } diff --git a/test/test_cairo.c b/test/test_cairo.c index 5d2aab8..a9a36db 100644 --- a/test/test_cairo.c +++ b/test/test_cairo.c @@ -65,7 +65,7 @@ main(int argc, char** argv) puglSetHandle(test.view, &test); puglSetBackend(test.view, puglCairoBackend()); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); puglShow(test.view); // Drive event loop until the view gets exposed diff --git a/test/test_gl.c b/test/test_gl.c index 3fe546b..da84c1a 100644 --- a/test/test_gl.c +++ b/test/test_gl.c @@ -84,7 +84,7 @@ main(int argc, char** argv) puglSetHandle(test.view, &test); puglSetBackend(test.view, puglGlBackend()); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); puglShow(test.view); // Enter OpenGL context as if setting things up diff --git a/test/test_gl_free_unrealized.c b/test/test_gl_free_unrealized.c index 783ffb8..04a23db 100644 --- a/test/test_gl_free_unrealized.c +++ b/test/test_gl_free_unrealized.c @@ -49,7 +49,7 @@ main(int argc, char** argv) puglSetBackend(test.view, puglGlBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); assert(!puglGetVisible(test.view)); diff --git a/test/test_gl_hints.c b/test/test_gl_hints.c index 701dd77..7e91035 100644 --- a/test/test_gl_hints.c +++ b/test/test_gl_hints.c @@ -34,7 +34,7 @@ main(void) puglSetWindowTitle(view, "Pugl OpenGL Hints Test"); puglSetBackend(view, puglGlBackend()); puglSetEventFunc(view, onEvent); - puglSetDefaultSize(view, 512, 512); + puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 512, 512); // Check invalid cases assert(puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_DONT_CARE) == diff --git a/test/test_local_copy_paste.c b/test/test_local_copy_paste.c index a4350f0..f5c75c4 100644 --- a/test/test_local_copy_paste.c +++ b/test/test_local_copy_paste.c @@ -94,7 +94,7 @@ main(int argc, char** argv) puglSetBackend(app.view, puglStubBackend()); puglSetHandle(app.view, &app); puglSetEventFunc(app.view, onEvent); - puglSetDefaultSize(app.view, 512, 512); + puglSetSizeHint(app.view, PUGL_DEFAULT_SIZE, 512, 512); // Create and show window assert(!puglRealize(app.view)); diff --git a/test/test_realize.c b/test/test_realize.c index d26d866..50eba46 100644 --- a/test/test_realize.c +++ b/test/test_realize.c @@ -68,7 +68,7 @@ main(int argc, char** argv) puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); // Create initially invisible window assert(!puglRealize(test.view)); diff --git a/test/test_redisplay.c b/test/test_redisplay.c index 801393e..afe89dc 100644 --- a/test/test_redisplay.c +++ b/test/test_redisplay.c @@ -113,7 +113,7 @@ main(int argc, char** argv) puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); // Create and show window assert(!puglRealize(test.view)); diff --git a/test/test_remote_copy_paste.c b/test/test_remote_copy_paste.c index aeba3dc..2ee90f7 100644 --- a/test/test_remote_copy_paste.c +++ b/test/test_remote_copy_paste.c @@ -131,7 +131,7 @@ main(int argc, char** argv) puglSetBackend(app.copierView, puglStubBackend()); puglSetHandle(app.copierView, &app); puglSetEventFunc(app.copierView, onCopierEvent); - puglSetDefaultSize(app.copierView, 256, 256); + puglSetSizeHint(app.copierView, PUGL_DEFAULT_SIZE, 256, 256); // Set up paster view app.pasterView = puglNewView(app.world); @@ -140,7 +140,7 @@ main(int argc, char** argv) puglSetBackend(app.pasterView, puglStubBackend()); puglSetHandle(app.pasterView, &app); puglSetEventFunc(app.pasterView, onPasterEvent); - puglSetDefaultSize(app.pasterView, 256, 256); + puglSetSizeHint(app.pasterView, PUGL_DEFAULT_SIZE, 256, 256); // Create and show both views assert(!puglShow(app.copierView)); diff --git a/test/test_show_hide.c b/test/test_show_hide.c index 32efe19..bfcb035 100644 --- a/test/test_show_hide.c +++ b/test/test_show_hide.c @@ -103,7 +103,7 @@ main(int argc, char** argv) puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); // Create initially invisible window assert(!puglRealize(test.view)); diff --git a/test/test_size.c b/test/test_size.c index 862eba5..048bfa5 100644 --- a/test/test_size.c +++ b/test/test_size.c @@ -69,9 +69,9 @@ onEvent(PuglView* view, const PuglEvent* event) int main(int argc, char** argv) { - static const int minSize = 256; - static const int defaultSize = 512; - static const int maxSize = 1024; + static const PuglSpan minSize = 256; + static const PuglSpan defaultSize = 512; + static const PuglSpan maxSize = 1024; PuglTest test = {puglNewWorld(PUGL_PROGRAM, 0), NULL, @@ -87,10 +87,11 @@ main(int argc, char** argv) puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); puglSetViewHint(test.view, PUGL_RESIZABLE, PUGL_TRUE); - puglSetMinSize(test.view, minSize, minSize); - puglSetDefaultSize(test.view, defaultSize, defaultSize); - puglSetMaxSize(test.view, maxSize, maxSize); - puglSetAspectRatio(test.view, 1, 1, 1, 1); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, defaultSize, defaultSize); + puglSetSizeHint(test.view, PUGL_MIN_SIZE, minSize, minSize); + puglSetSizeHint(test.view, PUGL_MAX_SIZE, maxSize, maxSize); + puglSetSizeHint(test.view, PUGL_MIN_ASPECT, 1, 1); + puglSetSizeHint(test.view, PUGL_MAX_ASPECT, 1, 1); // Create and show window assert(!puglRealize(test.view)); diff --git a/test/test_stub.c b/test/test_stub.c index 308aae3..8ab2c15 100644 --- a/test/test_stub.c +++ b/test/test_stub.c @@ -51,7 +51,7 @@ main(int argc, char** argv) puglSetHandle(test.view, &test); puglSetBackend(test.view, puglStubBackend()); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); puglShow(test.view); // Drive event loop until the view gets exposed diff --git a/test/test_stub_hints.c b/test/test_stub_hints.c index f45bacc..12a848b 100644 --- a/test/test_stub_hints.c +++ b/test/test_stub_hints.c @@ -34,7 +34,7 @@ main(void) puglSetWindowTitle(view, "Pugl Stub Hints Test"); puglSetBackend(view, puglStubBackend()); puglSetEventFunc(view, onEvent); - puglSetDefaultSize(view, 512, 512); + puglSetSizeHint(view, PUGL_DEFAULT_SIZE, 512, 512); // Check invalid cases assert(puglSetViewHint(view, (PuglViewHint)9999, 0) == PUGL_BAD_PARAMETER); diff --git a/test/test_timer.c b/test/test_timer.c index 70e6e3b..401e4d5 100644 --- a/test/test_timer.c +++ b/test/test_timer.c @@ -111,7 +111,7 @@ main(int argc, char** argv) puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); // Create and show window assert(!puglRealize(test.view)); diff --git a/test/test_update.c b/test/test_update.c index 4fb6ac1..381e34b 100644 --- a/test/test_update.c +++ b/test/test_update.c @@ -89,7 +89,7 @@ main(int argc, char** argv) puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); // Create and show window assert(!puglRealize(test.view)); diff --git a/test/test_view.c b/test/test_view.c index 04cd2c1..e9a954c 100644 --- a/test/test_view.c +++ b/test/test_view.c @@ -70,7 +70,7 @@ main(int argc, char** argv) puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); // Create and show window assert(!puglRealize(test.view)); diff --git a/test/test_vulkan.c b/test/test_vulkan.c index 0a364f3..77921b4 100644 --- a/test/test_vulkan.c +++ b/test/test_vulkan.c @@ -171,7 +171,7 @@ main(int argc, char** argv) puglSetHandle(test.view, &test); puglSetBackend(test.view, puglVulkanBackend()); puglSetEventFunc(test.view, onEvent); - puglSetDefaultSize(test.view, 512, 512); + puglSetSizeHint(test.view, PUGL_DEFAULT_SIZE, 512, 512); assert(!puglRealize(test.view)); // Create Vulkan surface for window |