From fe96ed3c451548278197e2da74d3d53b1d6a8dd9 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 16 May 2020 21:18:02 +0200 Subject: Add default and maximum size --- examples/pugl_cairo_demo.c | 5 ++- examples/pugl_cxx_demo.cpp | 2 +- examples/pugl_embed_demo.c | 2 +- examples/pugl_shader_demo.c | 8 ++--- examples/pugl_window_demo.c | 1 + pugl/detail/implementation.c | 7 ++-- pugl/detail/mac.m | 65 +++++++++++++++++++++++++++++++---- pugl/detail/types.h | 4 +++ pugl/detail/win.c | 18 ++++++++++ pugl/detail/win.h | 25 +++++++++++--- pugl/detail/x11.c | 82 ++++++++++++++++++++++++++++++-------------- pugl/pugl.h | 20 +++++++++++ pugl/pugl.hpp | 13 +++++++ test/test_redisplay.c | 1 + test/test_show_hide.c | 1 + test/test_timer.c | 1 + test/test_update.c | 1 + 17 files changed, 206 insertions(+), 50 deletions(-) diff --git a/examples/pugl_cairo_demo.c b/examples/pugl_cairo_demo.c index 51039a2..2e0b9c7 100644 --- a/examples/pugl_cairo_demo.c +++ b/examples/pugl_cairo_demo.c @@ -236,11 +236,10 @@ main(int argc, char** argv) app.world = puglNewWorld(PUGL_PROGRAM, 0); puglSetClassName(app.world, "PuglCairoTest"); - PuglRect frame = { 0, 0, 512, 512 }; - PuglView* view = puglNewView(app.world); + PuglView* view = puglNewView(app.world); puglSetWindowTitle(view, "Pugl Cairo Demo"); - puglSetFrame(view, frame); + puglSetDefaultSize(view, 512, 512); puglSetMinSize(view, 256, 256); puglSetViewHint(view, PUGL_RESIZABLE, app.opts.resizable); puglSetHandle(view, &app); diff --git a/examples/pugl_cxx_demo.cpp b/examples/pugl_cxx_demo.cpp index 9522534..4addee2 100644 --- a/examples/pugl_cxx_demo.cpp +++ b/examples/pugl_cxx_demo.cpp @@ -122,7 +122,7 @@ main(int argc, char** argv) world.setClassName("PuglCppTest"); view.setWindowTitle("Pugl C++ Test"); - view.setFrame({0, 0, 512, 512}); + view.setDefaultSize(512, 512); view.setMinSize(64, 64); view.setAspectRatio(1, 1, 16, 9); view.setBackend(pugl::glBackend()); diff --git a/examples/pugl_embed_demo.c b/examples/pugl_embed_demo.c index de7b240..bf7f19c 100644 --- a/examples/pugl_embed_demo.c +++ b/examples/pugl_embed_demo.c @@ -293,7 +293,7 @@ main(int argc, char** argv) puglSetClassName(app.world, "Pugl Test"); const PuglRect parentFrame = { 0, 0, 512, 512 }; - puglSetFrame(app.parent, parentFrame); + puglSetDefaultSize(app.parent, 512, 512); puglSetMinSize(app.parent, borderWidth * 3, borderWidth * 3); puglSetAspectRatio(app.parent, 1, 1, 16, 9); puglSetBackend(app.parent, puglGlBackend()); diff --git a/examples/pugl_shader_demo.c b/examples/pugl_shader_demo.c index 86d01a9..7c0df6a 100644 --- a/examples/pugl_shader_demo.c +++ b/examples/pugl_shader_demo.c @@ -243,7 +243,7 @@ parseOptions(PuglTestApp* app, int argc, char** argv) } static void -setupPugl(PuglTestApp* app, const PuglRect frame) +setupPugl(PuglTestApp* app) { // Create world, view, and rect data app->world = puglNewWorld(PUGL_PROGRAM, 0); @@ -253,7 +253,7 @@ setupPugl(PuglTestApp* app, const PuglRect frame) // Set up world and view puglSetClassName(app->world, "PuglGL3Demo"); puglSetWindowTitle(app->view, "Pugl OpenGL 3"); - puglSetFrame(app->view, frame); + puglSetDefaultSize(app->view, defaultWidth, defaultHeight); puglSetMinSize(app->view, defaultWidth / 4, defaultHeight / 4); puglSetAspectRatio(app->view, 1, 1, 16, 9); puglSetBackend(app->view, puglGlBackend()); @@ -389,8 +389,6 @@ main(int argc, char** argv) app.glMajorVersion = 3; app.glMinorVersion = 3; - const PuglRect frame = {0, 0, defaultWidth, defaultHeight}; - // Parse command line options if (parseOptions(&app, argc, argv)) { puglPrintTestUsage("pugl_shader_demo", "[NUM_RECTS] [GL_MAJOR]"); @@ -398,7 +396,7 @@ main(int argc, char** argv) } // Create and configure world and view - setupPugl(&app, frame); + setupPugl(&app); // Create window (which will send a PUGL_CREATE event) const PuglStatus st = puglRealize(app.view); diff --git a/examples/pugl_window_demo.c b/examples/pugl_window_demo.c index 3b167af..b406416 100644 --- a/examples/pugl_window_demo.c +++ b/examples/pugl_window_demo.c @@ -213,6 +213,7 @@ main(int argc, char** argv) puglSetWindowTitle(view, "Pugl Window Demo"); puglSetFrame(view, frame); + puglSetDefaultSize(view, 512, 512); puglSetMinSize(view, 128, 128); puglSetBackend(view, puglGlBackend()); diff --git a/pugl/detail/implementation.c b/pugl/detail/implementation.c index 45c06c6..1ff1c60 100644 --- a/pugl/detail/implementation.c +++ b/pugl/detail/implementation.c @@ -61,6 +61,7 @@ puglStrerror(const PuglStatus status) case PUGL_FAILURE: return "Non-fatal failure"; case PUGL_UNKNOWN_ERROR: return "Unknown system error"; case PUGL_BAD_BACKEND: return "Invalid or missing backend"; + case PUGL_BAD_CONFIGURATION: return "Invalid view configuration"; case PUGL_BAD_PARAMETER: return "Invalid parameter"; case PUGL_BACKEND_FAILED: return "Backend initialisation failed"; case PUGL_REGISTRATION_FAILED: return "Class registration failed"; @@ -189,9 +190,9 @@ puglNewView(PuglWorld* const world) return NULL; } - view->world = world; - view->frame.width = 640; - view->frame.height = 480; + view->world = world; + view->minWidth = 1; + view->minHeight = 1; puglSetDefaultHints(view->hints); diff --git a/pugl/detail/mac.m b/pugl/detail/mac.m index 52417e6..501f5d9 100644 --- a/pugl/detail/mac.m +++ b/pugl/detail/mac.m @@ -210,6 +210,17 @@ updateViewRect(PuglView* view) puglDispatchEvent(puglview, (const PuglEvent*)&ev); } +- (NSSize) intrinsicContentSize +{ + if (puglview->defaultWidth || puglview->defaultHeight) { + return sizePoints(puglview, + puglview->defaultWidth, + puglview->defaultHeight); + } + + return NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric); +} + - (BOOL) isFlipped { return YES; @@ -805,14 +816,29 @@ puglConstraint(id item, NSLayoutAttribute attribute, float constant) PuglStatus puglRealize(PuglView* view) { - PuglInternals* impl = view->impl; + PuglInternals* impl = view->impl; + const NSScreen* const screen = [NSScreen mainScreen]; + const double scaleFactor = [screen backingScaleFactor]; - const double scaleFactor = [[NSScreen mainScreen] backingScaleFactor]; - const NSRect framePx = rectToNsRect(view->frame); - const NSRect framePt = NSMakeRect(framePx.origin.x / scaleFactor, - framePx.origin.y / scaleFactor, - framePx.size.width / scaleFactor, - framePx.size.height / scaleFactor); + if (view->frame.width == 0.0 && view->frame.height == 0.0) { + if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) { + return PUGL_BAD_CONFIGURATION; + } + + const int screenWidthPx = [screen frame].size.width * scaleFactor; + const int screenHeightPx = [screen frame].size.height * scaleFactor; + + view->frame.width = view->defaultWidth; + view->frame.height = view->defaultHeight; + view->frame.x = screenWidthPx / 2.0 - view->frame.width / 2.0; + view->frame.y = screenHeightPx / 2.0 - view->frame.height / 2.0; + } + + const NSRect framePx = rectToNsRect(view->frame); + const NSRect framePt = NSMakeRect(framePx.origin.x / scaleFactor, + framePx.origin.y / scaleFactor, + framePx.size.width / scaleFactor, + framePx.size.height / scaleFactor); // Create wrapper view to handle input impl->wrapperView = [PuglWrapperView alloc]; @@ -884,6 +910,8 @@ puglRealize(PuglView* view) view->minAspectY)]; } + puglSetFrame(view, view->frame); + [window setContentView:impl->wrapperView]; [view->world->impl->app activateIgnoringOtherApps:YES]; [window makeFirstResponder:impl->wrapperView]; @@ -1178,6 +1206,14 @@ puglSetFrame(PuglView* view, const PuglRect frame) return PUGL_SUCCESS; } +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) { @@ -1193,6 +1229,21 @@ puglSetMinSize(PuglView* const view, const int width, const int height) return PUGL_SUCCESS; } +PuglStatus +puglSetMaxSize(PuglView* const view, const int width, const int 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)]; + } + + return PUGL_SUCCESS; +} + PuglStatus puglSetAspectRatio(PuglView* const view, const int minX, diff --git a/pugl/detail/types.h b/pugl/detail/types.h index 7b15053..0b0332c 100644 --- a/pugl/detail/types.h +++ b/pugl/detail/types.h @@ -66,8 +66,12 @@ struct PuglViewImpl { PuglHints hints; PuglRect frame; PuglEventConfigure lastConfigure; + int defaultWidth; + int defaultHeight; int minWidth; int minHeight; + int maxWidth; + int maxHeight; int minAspectX; int minAspectY; int maxAspectX; diff --git a/pugl/detail/win.c b/pugl/detail/win.c index 76df1b4..22bcfbf 100644 --- a/pugl/detail/win.c +++ b/pugl/detail/win.c @@ -592,6 +592,8 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) mmi = (MINMAXINFO*)lParam; mmi->ptMinTrackSize.x = view->minWidth; mmi->ptMinTrackSize.y = view->minHeight; + mmi->ptMaxTrackSize.x = view->maxWidth; + mmi->ptMaxTrackSize.y = view->maxHeight; break; case WM_PAINT: GetUpdateRect(view->impl->hwnd, &rect, false); @@ -958,6 +960,14 @@ puglSetFrame(PuglView* view, const PuglRect frame) return PUGL_SUCCESS; } +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) { @@ -966,6 +976,14 @@ puglSetMinSize(PuglView* const view, const int width, const int height) return PUGL_SUCCESS; } +PuglStatus +puglSetMaxSize(PuglView* const view, const int width, const int height) +{ + view->maxWidth = width; + view->maxHeight = height; + return PUGL_SUCCESS; +} + PuglStatus puglSetAspectRatio(PuglView* const view, const int minX, diff --git a/pugl/detail/win.h b/pugl/detail/win.h index 8ebb7bf..087bbce 100644 --- a/pugl/detail/win.h +++ b/pugl/detail/win.h @@ -88,15 +88,32 @@ puglWinGetWindowExFlags(const PuglView* const view) } static inline PuglStatus -puglWinCreateWindow(const PuglView* const view, - const char* const title, - HWND* const hwnd, - HDC* const hdc) +puglWinCreateWindow(PuglView* const view, + const char* const title, + HWND* const hwnd, + HDC* const hdc) { const char* className = (const char*)view->world->className; const unsigned winFlags = puglWinGetWindowFlags(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) { + return PUGL_BAD_CONFIGURATION; + } + + RECT desktopRect; + GetClientRect(GetDesktopWindow(), &desktopRect); + + 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; + } + // The meaning of "parent" depends on the window type (WS_CHILD) PuglNativeView parent = view->parent ? view->parent : view->transientParent; diff --git a/pugl/detail/x11.c b/pugl/detail/x11.c index 9998a7a..6a580ce 100644 --- a/pugl/detail/x11.c +++ b/pugl/detail/x11.c @@ -193,23 +193,40 @@ puglFindView(PuglWorld* world, const Window window) return NULL; } -static XSizeHints -getSizeHints(const PuglView* view) +static PuglStatus +updateSizeHints(const PuglView* view) { + if (!view->impl->win) { + return PUGL_SUCCESS; + } + + Display* display = view->world->impl->display; XSizeHints sizeHints = {0}; if (!view->hints[PUGL_RESIZABLE]) { - sizeHints.flags = PMinSize|PMaxSize; - sizeHints.min_width = (int)view->frame.width; - sizeHints.min_height = (int)view->frame.height; - sizeHints.max_width = (int)view->frame.width; - sizeHints.max_height = (int)view->frame.height; + sizeHints.flags = PBaseSize | PMinSize | PMaxSize; + sizeHints.base_width = (int)view->frame.width; + sizeHints.base_height = (int)view->frame.height; + sizeHints.min_width = (int)view->frame.width; + sizeHints.min_height = (int)view->frame.height; + sizeHints.max_width = (int)view->frame.width; + sizeHints.max_height = (int)view->frame.height; } else { + if (view->defaultWidth || view->defaultHeight) { + sizeHints.flags = PBaseSize; + sizeHints.base_width = view->defaultWidth; + sizeHints.base_height = view->defaultHeight; + } if (view->minWidth || view->minHeight) { sizeHints.flags = PMinSize; sizeHints.min_width = view->minWidth; sizeHints.min_height = view->minHeight; } + if (view->maxWidth || view->maxHeight) { + sizeHints.flags = PMaxSize; + sizeHints.max_width = view->maxWidth; + sizeHints.max_height = view->maxHeight; + } if (view->minAspectX) { sizeHints.flags |= PAspect; sizeHints.min_aspect.x = view->minAspectX; @@ -219,7 +236,8 @@ getSizeHints(const PuglView* view) } } - return sizeHints; + XSetNormalHints(display, view->impl->win, &sizeHints); + return PUGL_SUCCESS; } PuglStatus @@ -235,6 +253,18 @@ puglRealize(PuglView* view) if (!view->backend || !view->backend->configure) { return PUGL_BAD_BACKEND; + } else if (view->frame.width == 0.0 && view->frame.height == 0.0) { + if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) { + return PUGL_BAD_CONFIGURATION; + } + + const int screenWidth = DisplayWidth(display, impl->screen); + const int screenHeight = DisplayHeight(display, impl->screen); + + 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; } PuglStatus st = view->backend->configure(view); @@ -264,8 +294,7 @@ puglRealize(PuglView* view) return st; } - XSizeHints sizeHints = getSizeHints(view); - XSetNormalHints(display, win, &sizeHints); + updateSizeHints(view); XClassHint classHint = { world->className, world->className }; XSetClassHint(display, win, &classHint); @@ -1103,19 +1132,27 @@ puglSetFrame(PuglView* view, const PuglRect frame) } PuglStatus -puglSetMinSize(PuglView* const view, const int width, const int height) +puglSetDefaultSize(PuglView* const view, const int width, const int height) { - Display* display = view->world->impl->display; + view->defaultWidth = width; + view->defaultHeight = height; + return updateSizeHints(view); +} +PuglStatus +puglSetMinSize(PuglView* const view, const int width, const int height) +{ view->minWidth = width; view->minHeight = height; + return updateSizeHints(view); +} - if (view->impl->win) { - XSizeHints sizeHints = getSizeHints(view); - XSetNormalHints(display, view->impl->win, &sizeHints); - } - - return PUGL_SUCCESS; +PuglStatus +puglSetMaxSize(PuglView* const view, const int width, const int height) +{ + view->minWidth = width; + view->minHeight = height; + return updateSizeHints(view); } PuglStatus @@ -1125,19 +1162,12 @@ puglSetAspectRatio(PuglView* const view, const int maxX, const int maxY) { - Display* display = view->world->impl->display; - view->minAspectX = minX; view->minAspectY = minY; view->maxAspectX = maxX; view->maxAspectY = maxY; - if (view->impl->win) { - XSizeHints sizeHints = getSizeHints(view); - XSetNormalHints(display, view->impl->win, &sizeHints); - } - - return PUGL_SUCCESS; + return updateSizeHints(view); } PuglStatus diff --git a/pugl/pugl.h b/pugl/pugl.h index fe8b585..b489407 100644 --- a/pugl/pugl.h +++ b/pugl/pugl.h @@ -559,6 +559,7 @@ typedef enum { PUGL_FAILURE, ///< Non-fatal failure PUGL_UNKNOWN_ERROR, ///< Unknown system error PUGL_BAD_BACKEND, ///< Invalid or missing backend + PUGL_BAD_CONFIGURATION, ///< Invalid view configuration PUGL_BAD_PARAMETER, ///< Invalid parameter PUGL_BACKEND_FAILED, ///< Backend initialisation failed PUGL_REGISTRATION_FAILED, ///< Class registration failed @@ -946,6 +947,16 @@ puglGetFrame(const PuglView* view); PUGL_API 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. +*/ +PUGL_API PuglStatus +puglSetDefaultSize(PuglView* view, int width, int height); + /** Set the minimum size of the view. @@ -955,6 +966,15 @@ puglSetFrame(PuglView* view, PuglRect frame); 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. +*/ +PUGL_API PuglStatus +puglSetMaxSize(PuglView* view, int width, int height); + /** Set the view aspect ratio range. diff --git a/pugl/pugl.hpp b/pugl/pugl.hpp index 52ac52c..2e18306 100644 --- a/pugl/pugl.hpp +++ b/pugl/pugl.hpp @@ -186,6 +186,7 @@ enum class Status { failure, ///< @copydoc PUGL_FAILURE unknownError, ///< @copydoc PUGL_UNKNOWN_ERROR badBackend, ///< @copydoc PUGL_BAD_BACKEND + badConfiguration, ///< @copydoc PUGL_BAD_CONFIGURATION badParameter, ///< @copydoc PUGL_BAD_PARAMETER backendFailed, ///< @copydoc PUGL_BACKEND_FAILED registrationFailed, ///< @copydoc PUGL_REGISTRATION_FAILED @@ -406,12 +407,24 @@ public: return static_cast(puglSetFrame(cobj(), frame)); } + /// @copydoc puglSetDefaultSize + Status setDefaultSize(int width, int height) + { + return static_cast(puglSetDefaultSize(cobj(), width, height)); + } + /// @copydoc puglSetMinSize Status setMinSize(int width, int height) { return static_cast(puglSetMinSize(cobj(), width, height)); } + /// @copydoc puglSetMaxSize + Status setMaxSize(int width, int height) + { + return static_cast(puglSetMaxSize(cobj(), width, height)); + } + /// @copydoc puglSetAspectRatio Status setAspectRatio(int minX, int minY, int maxX, int maxY) { diff --git a/test/test_redisplay.c b/test/test_redisplay.c index 13f0c81..0e14cf6 100644 --- a/test/test_redisplay.c +++ b/test/test_redisplay.c @@ -111,6 +111,7 @@ main(int argc, char** argv) puglSetBackend(app.view, puglStubBackend()); puglSetHandle(app.view, &app); puglSetEventFunc(app.view, onEvent); + puglSetDefaultSize(app.view, 512, 512); // Create and show window assert(!puglRealize(app.view)); diff --git a/test/test_show_hide.c b/test/test_show_hide.c index 43af7f4..7b6d4f4 100644 --- a/test/test_show_hide.c +++ b/test/test_show_hide.c @@ -115,6 +115,7 @@ main(int argc, char** argv) puglSetBackend(test.view, puglStubBackend()); puglSetHandle(test.view, &test); puglSetEventFunc(test.view, onEvent); + puglSetDefaultSize(test.view, 512, 512); // Create initially invisible window assert(!puglRealize(test.view)); diff --git a/test/test_timer.c b/test/test_timer.c index f26928d..2a0b67f 100644 --- a/test/test_timer.c +++ b/test/test_timer.c @@ -109,6 +109,7 @@ main(int argc, char** argv) puglSetBackend(app.view, puglStubBackend()); puglSetHandle(app.view, &app); puglSetEventFunc(app.view, onEvent); + puglSetDefaultSize(app.view, 512, 512); // Create and show window assert(!puglRealize(app.view)); diff --git a/test/test_update.c b/test/test_update.c index 6dccf00..65d74d6 100644 --- a/test/test_update.c +++ b/test/test_update.c @@ -101,6 +101,7 @@ main(int argc, char** argv) puglSetBackend(app.view, puglStubBackend()); puglSetHandle(app.view, &app); puglSetEventFunc(app.view, onEvent); + puglSetDefaultSize(app.view, 512, 512); // Create and show window assert(!puglRealize(app.view)); -- cgit v1.2.1