diff options
author | David Robillard <d@drobilla.net> | 2025-01-22 18:42:15 -0500 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2025-01-22 21:22:11 -0500 |
commit | d7b69ead8e7e959b01cf615c5f363fd2a0a6590f (patch) | |
tree | b9e6f015ebd5b8ef357cf85a99f41c28330ad222 | |
parent | 77f88bd146b1ce10d405c7812bdd2c4d78857bd4 (diff) | |
download | pugl-d7b69ead8e7e959b01cf615c5f363fd2a0a6590f.tar.gz pugl-d7b69ead8e7e959b01cf615c5f363fd2a0a6590f.tar.bz2 pugl-d7b69ead8e7e959b01cf615c5f363fd2a0a6590f.zip |
Simplify initial position code and place children at the origin
The code to find the initial size and position for a view had a lot of
duplication, and was inconsistent between platforms. Flaky positioning and
attempting to center embedded children has historically caused a bunch of
problems in general, particularly since windows can be resized.
So, factor out all the initial size and position code so that the logic is in
one centralized place used by all platforms, and always position embedded
children within their parent at the top/left origin. For top-level windows,
the view is centered on its transient parent or the screen (as before). A new
platform function puglGetAncestorCenter() is used to do this in general code.
Also towards a total separation of position and size, towards support for
Wayland, where applications don't dictate their own positions.
-rw-r--r-- | src/internal.c | 47 | ||||
-rw-r--r-- | src/internal.h | 12 | ||||
-rw-r--r-- | src/mac.m | 51 | ||||
-rw-r--r-- | src/win.c | 61 | ||||
-rw-r--r-- | src/x11.c | 84 |
5 files changed, 121 insertions, 134 deletions
diff --git a/src/internal.c b/src/internal.c index 31345f3..38a595a 100644 --- a/src/internal.c +++ b/src/internal.c @@ -9,9 +9,17 @@ #include <assert.h> #include <stdbool.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> +static PuglPoint +make_point(const PuglCoord x, const PuglCoord y) +{ + const PuglPoint point = {x, y}; + return point; +} + bool puglIsValidPosition(const int x, const int y) { @@ -30,6 +38,45 @@ puglIsValidArea(const PuglArea size) return size.width && size.height; } +PuglArea +puglGetInitialSize(const PuglView* const view) +{ + if (view->lastConfigure.type == PUGL_CONFIGURE) { + // Use the last configured size + const PuglConfigureEvent config = view->lastConfigure; + const PuglArea size = {config.width, config.height}; + return size; + } + + // Use the default size hint set by the application + return view->sizeHints[PUGL_DEFAULT_SIZE]; +} + +PuglPoint +puglGetInitialPosition(const PuglView* const view, const PuglArea size) +{ + if (view->lastConfigure.type == PUGL_CONFIGURE) { + // Use the last configured frame + return make_point(view->lastConfigure.x, view->lastConfigure.y); + } + + if (puglIsValidPosition(view->defaultX, view->defaultY)) { + // Use the default position hint set by the application + return make_point((PuglCoord)view->defaultX, (PuglCoord)view->defaultY); + } + + if (view->parent) { + // Default to the top/left origin of the parent + return make_point(0, 0); + } + + // Center frame on a transient ancestor, or failing that, the screen + const PuglPoint center = puglGetAncestorCenter(view); + const PuglPoint pos = {(PuglCoord)(center.x - (size.width / 2)), + (PuglCoord)(center.y - (size.height / 2))}; + return pos; +} + void puglEnsureHint(PuglView* const view, const PuglViewHint hint, const int value) { diff --git a/src/internal.h b/src/internal.h index 8497c50..7387494 100644 --- a/src/internal.h +++ b/src/internal.h @@ -30,6 +30,18 @@ puglIsValidSize(unsigned x, unsigned y); bool puglIsValidArea(PuglArea size); +/// Return the center point of some "soft" ancestor (parent window or screen) +PuglPoint +puglGetAncestorCenter(const PuglView* view); + +/// Return the initial size of a view +PuglArea +puglGetInitialSize(const PuglView* view); + +/// Return the initial position of a view if known, or an invalid position +PuglPoint +puglGetInitialPosition(const PuglView* view, PuglArea size); + /// Set hint to a default value if it is unset (PUGL_DONT_CARE) void puglEnsureHint(PuglView* view, PuglViewHint hint, int value); @@ -121,7 +121,7 @@ viewScreen(const PuglView* view) } static NSRect -nsRectToPoints(PuglView* view, const NSRect rect) +nsRectToPoints(const PuglView* view, const NSRect rect) { const double scaleFactor = [viewScreen(view) backingScaleFactor]; @@ -132,7 +132,7 @@ nsRectToPoints(PuglView* view, const NSRect rect) } static NSRect -nsRectFromPoints(PuglView* view, const NSRect rect) +nsRectFromPoints(const PuglView* view, const NSRect rect) { const double scaleFactor = [viewScreen(view) backingScaleFactor]; @@ -143,7 +143,7 @@ nsRectFromPoints(PuglView* view, const NSRect rect) } static NSPoint -nsPointFromPoints(PuglView* view, const NSPoint point) +nsPointFromPoints(const PuglView* view, const NSPoint point) { const double scaleFactor = [viewScreen(view) backingScaleFactor]; @@ -1153,30 +1153,9 @@ updateSizeHints(PuglView* const view) } } -static PuglRect -getInitialFrame(PuglView* const view) +PuglPoint +puglGetAncestorCenter(const PuglView* const view) { - if (view->lastConfigure.type == PUGL_CONFIGURE) { - // Use the last configured frame - const PuglRect frame = {view->lastConfigure.x, - view->lastConfigure.y, - view->lastConfigure.width, - view->lastConfigure.height}; - return frame; - } - - const int x = view->defaultX; - const int y = view->defaultY; - if (puglIsValidPosition(x, y)) { - // Use the default position set with puglSetPosition while unrealized - const PuglRect frame = {(PuglCoord)x, - (PuglCoord)y, - view->sizeHints[PUGL_DEFAULT_SIZE].width, - view->sizeHints[PUGL_DEFAULT_SIZE].height}; - return frame; - } - - // Get a bounding rect from the transient parent or the screen const NSScreen* const screen = viewScreen(view); const NSRect boundsPt = rectFromScreen(screen, @@ -1184,17 +1163,11 @@ getInitialFrame(PuglView* const view) ? [[(const NSView*)view->transientParent window] frame] : [screen frame]); - // Center the frame around the center of the bounding rectangle - const PuglArea defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; - const NSRect boundsPx = nsRectFromPoints(view, boundsPt); - const double centerX = boundsPx.origin.x + boundsPx.size.width / 2; - const double centerY = boundsPx.origin.y + boundsPx.size.height / 2; - - const PuglRect frame = {(PuglCoord)(centerX - (defaultSize.width / 2U)), - (PuglCoord)(centerY - (defaultSize.height / 2U)), - view->sizeHints[PUGL_DEFAULT_SIZE].width, - view->sizeHints[PUGL_DEFAULT_SIZE].height}; - return frame; + const NSRect boundsPx = nsRectFromPoints(view, boundsPt); + const PuglPoint center = { + (PuglCoord)(boundsPx.origin.x + boundsPx.size.width / 2.0), + (PuglCoord)(boundsPx.origin.y + boundsPx.size.height / 2.0)}; + return center; } PuglStatus @@ -1243,7 +1216,9 @@ puglRealize(PuglView* view) } // Get the initial frame to use from the defaults or last configuration - const PuglRect initialFrame = getInitialFrame(view); + const PuglArea size = puglGetInitialSize(view); + const PuglPoint pos = puglGetInitialPosition(view, size); + const PuglRect initialFrame = {pos.x, pos.y, size.width, size.height}; // Convert frame to points const NSRect framePx = rectToNsRect(initialFrame); @@ -1576,42 +1576,18 @@ puglWinGetPixelFormatDescriptor(const PuglHints hints) return pfd; } -static PuglRect -getInitialFrame(PuglView* const view) -{ - if (view->lastConfigure.type == PUGL_CONFIGURE) { - // Use the last configured frame - const PuglRect frame = {view->lastConfigure.x, - view->lastConfigure.y, - view->lastConfigure.width, - view->lastConfigure.height}; - return frame; - } - - const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width; - const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height; - const int x = view->defaultX; - const int y = view->defaultY; - if (puglIsValidPosition(x, y)) { - // Use the default position set with puglSetPosition while unrealized - const PuglRect frame = { - (PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight}; - return frame; - } - - // Get a bounding rect from the "nearest" parent or parent-like window - const HWND hwnd = puglWinGetWindow(view); - RECT rect = {0, 0, 0, 0}; - GetWindowRect(hwnd ? hwnd : GetDesktopWindow(), &rect); +PuglPoint +puglGetAncestorCenter(const PuglView* const view) +{ + RECT rect = {0, 0, 0, 0}; + GetWindowRect(view->transientParent ? (HWND)view->transientParent + : GetDesktopWindow(), + &rect); - // Center the frame around the center of the bounding rectangle - const LONG centerX = rect.left + (rect.right - rect.left) / 2; - const LONG centerY = rect.top + (rect.bottom - rect.top) / 2; - const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)), - (PuglCoord)(centerY - (defaultHeight / 2)), - defaultWidth, - defaultHeight}; - return frame; + const PuglPoint center = { + (PuglCoord)(rect.left + (rect.right - rect.left) / 2), + (PuglCoord)(rect.top + (rect.bottom - rect.top) / 2)}; + return center; } PuglStatus @@ -1626,13 +1602,14 @@ puglWinCreateWindow(PuglView* const view, PuglNativeView parent = view->parent ? view->parent : view->transientParent; // Calculate initial window rectangle - 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}; + const unsigned winFlags = puglWinGetWindowFlags(view); + const unsigned winExFlags = puglWinGetWindowExFlags(view); + const PuglArea size = puglGetInitialSize(view); + const PuglPoint pos = puglGetInitialPosition(view, size); + RECT wr = {(long)pos.x, + (long)pos.y, + (long)pos.x + size.width, + (long)pos.y + size.height}; AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags); ArgStringChar* const classNameArg = puglArgStringNew(className); @@ -510,48 +510,21 @@ clearX11Clipboard(PuglX11Clipboard* const board) board->data.len = 0; } -static PuglRect -getInitialFrame(PuglView* const view) -{ - if (view->lastConfigure.type == PUGL_CONFIGURE) { - // Use the last configured frame - const PuglRect frame = {view->lastConfigure.x, - view->lastConfigure.y, - view->lastConfigure.width, - view->lastConfigure.height}; - return frame; - } - - const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width; - const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height; - const int x = view->defaultX; - const int y = view->defaultY; - if (puglIsValidPosition(x, y)) { - // Use the default position set with puglSetPosition while unrealized - const PuglRect frame = { - (PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight}; - return frame; - } - - // Get the best "parentish" window to position the window in - Display* const display = view->world->impl->display; - const Window parent = - (view->parent ? (Window)view->parent - : view->transientParent ? (Window)view->transientParent - : RootWindow(display, view->impl->screen)); - - // Get the position/size of the parent as bounds for the new window - XWindowAttributes parentAttrs = PUGL_INIT_STRUCT; - XGetWindowAttributes(display, parent, &parentAttrs); +PuglPoint +puglGetAncestorCenter(const PuglView* const view) +{ + Display* const display = view->world->impl->display; + const int screen = view->impl->screen; + XWindowAttributes ancestorAttrs = PUGL_INIT_STRUCT; + XGetWindowAttributes(display, + view->transientParent ? (Window)view->transientParent + : RootWindow(display, screen), + &ancestorAttrs); - // Center the frame within the parent bounds - const int centerX = parentAttrs.x + parentAttrs.width / 2; - const int centerY = parentAttrs.y + parentAttrs.height / 2; - const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)), - (PuglCoord)(centerY - (defaultHeight / 2)), - defaultWidth, - defaultHeight}; - return frame; + const PuglPoint center = { + (PuglCoord)(ancestorAttrs.x + ancestorAttrs.width / 2), + (PuglCoord)(ancestorAttrs.y + ancestorAttrs.height / 2)}; + return center; } PuglStatus @@ -606,16 +579,17 @@ puglRealize(PuglView* const view) attr.event_mask |= StructureNotifyMask; attr.event_mask |= VisibilityChangeMask; - // Calculate the initial window rectangle - const PuglRect initialFrame = getInitialFrame(view); + // Calculate the initial window frame + const PuglArea initialSize = puglGetInitialSize(view); + const PuglPoint initialPos = puglGetInitialPosition(view, initialSize); // Create the window impl->win = XCreateWindow(display, parent, - initialFrame.x, - initialFrame.y, - initialFrame.width, - initialFrame.height, + initialPos.x, + initialPos.y, + initialSize.width, + initialSize.height, 0, impl->vi->depth, InputOutput, @@ -1099,17 +1073,19 @@ getCurrentConfiguration(PuglView* const view) XWindowAttributes attrs; XGetWindowAttributes(display, view->impl->win, &attrs); - // Get window position relative to the root window + // Get window position (relative to the root window if not a child) Window ignoredChild = 0; - int rootX = 0; - int rootY = 0; - XTranslateCoordinates( - display, view->impl->win, attrs.root, 0, 0, &rootX, &rootY, &ignoredChild); + int x = attrs.x; + int y = attrs.y; + if (!view->parent) { + XTranslateCoordinates( + display, view->impl->win, attrs.root, 0, 0, &x, &y, &ignoredChild); + } // Build a configure event based on the current window configuration PuglEvent configureEvent = {{PUGL_CONFIGURE, 0}}; - configureEvent.configure.x = (PuglCoord)rootX; - configureEvent.configure.y = (PuglCoord)rootY; + configureEvent.configure.x = (PuglCoord)x; + configureEvent.configure.y = (PuglCoord)y; configureEvent.configure.width = (PuglSpan)attrs.width; configureEvent.configure.height = (PuglSpan)attrs.height; configureEvent.configure.style = getCurrentViewStyleFlags(view); |