Skip to content

[fix] Frame-agnostic geometry save/restore for floating windows#2060

Closed
chibondking wants to merge 1 commit into
aethersdr:mainfrom
chibondking:fix/floating-window-geometry
Closed

[fix] Frame-agnostic geometry save/restore for floating windows#2060
chibondking wants to merge 1 commit into
aethersdr:mainfrom
chibondking:fix/floating-window-geometry

Conversation

@chibondking

Copy link
Copy Markdown
Collaborator

QWidget::saveGeometry() bakes in platform frame-extent data. When the decoration mode changes between save and restore (frameless ↔ decorated), the wrong frame size is applied and the window shifts by the title-bar height (~30 px on most Linux/Windows themes).

Replace the opaque base64 blob with a plain "x,y,w,h" string read from geometry() (the client-area rect, always frame-agnostic) and restore with setGeometry() directly. No frame arithmetic needed; works identically in both modes.

Legacy fallback: if the stored value is not four integers (i.e. it is a base64 blob from a build before this fix), the old restoreGeometry() path is used so existing saves survive the upgrade.

Screen clamping preserved: if the saved top-left is not on any currently-attached screen the window is recentred on the primary screen.

Affected: PanFloatingWindow, FloatingContainerWindow.

QWidget::saveGeometry() bakes in platform frame-extent data. When the
decoration mode changes between save and restore (frameless ↔ decorated),
the wrong frame size is applied and the window shifts by the title-bar
height (~30 px on most Linux/Windows themes).

Replace the opaque base64 blob with a plain "x,y,w,h" string read from
geometry() (the client-area rect, always frame-agnostic) and restore with
setGeometry() directly. No frame arithmetic needed; works identically in
both modes.

Legacy fallback: if the stored value is not four integers (i.e. it is a
base64 blob from a build before this fix), the old restoreGeometry() path
is used so existing saves survive the upgrade.

Screen clamping preserved: if the saved top-left is not on any
currently-attached screen the window is recentred on the primary screen.

Affected: PanFloatingWindow, FloatingContainerWindow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-scoped fix. The approach is sound — geometry() returns the client-area rect which is frame-agnostic by definition, so this eliminates the title-bar-height drift when toggling between frameless and decorated modes. The legacy base64 fallback is a nice touch for upgrade compatibility.

A few observations:

1. Duplicate parsing logic — consider a shared helper

The "x,y,w,h" parse → validate → screen-clamp pattern is repeated nearly identically in both files. A small free function (e.g. in a GeometryUtils.h or even just a file-local lambda) would reduce the duplication and make it easier to keep both paths in sync. Not blocking — just a suggestion for maintainability.

2. Minor inconsistency: toLatin1() vs toUtf8() for legacy decode

PanFloatingWindow uses QByteArray::fromBase64(val.toLatin1()) while FloatingContainerWindow uses QByteArray::fromBase64(val.toUtf8()). Since base64 is pure ASCII both are equivalent, but worth picking one for consistency.

3. Screen clamping difference between the two classes

PanFloatingWindow::restoreWindowGeometry() does its own screen clamping inline (moves the rect center to primary screen). FloatingContainerWindow::restoreAndEnsureVisible() relies on the existing post-restore clamp which checks topLeft() containment and re-centers. Both work, but the behavior differs slightly — one clamps by contains(topLeft), the other by contains(topLeft) then moveCenter. This is fine in practice, just noting it for awareness.

No bugs found. Files are within stated scope (PanFloatingWindow + FloatingContainerWindow only), uses AppSettings correctly, no null pointer risks, no resource leaks.

Thanks @chibondking — this is a solid fix, especially given the history around #2042 / #2008. The frame-agnostic serialization approach is the right long-term solution here.

@ten9876

ten9876 commented Apr 27, 2026

Copy link
Copy Markdown
Collaborator

Claude here on Jeremy's behalf — closing this one without merging.

Pulled and tested on Linux (Wayland session). Saved geometry came back as 0,0,300,... regardless of where the user dragged the window before quitting; on relaunch the window appeared centered on screen rather than at the saved position.

Root cause: Wayland compositors don't expose absolute window coordinates to clientsQWidget::geometry() returns useless (0, 0) regardless of visual position, and setGeometry() calls are ignored in favour of the compositor's placement policy. The protocol is deliberately designed this way (security/sandboxing).

QWidget::saveGeometry() / restoreGeometry() (the format this PR replaced) works on Wayland because Qt's internal implementation uses XDG_SHELL state hints and other compositor-specific extensions. We can't replicate that with primitive setGeometry/move calls — even with deferred re-application after show() (I tried that locally; same result).

Tradeoff: keeping the original saveGeometry()/restoreGeometry() means the ~30 px position jump when toggling frameless ↔ decorated reappears, but that's a much smaller user-visible issue than completely losing position persistence on Wayland.

Your work on #2057, #2058, and #2059 was excellent — three of four sub-fixes from the original #2008 are landed and working. This last one is just incompatible with Wayland's design. If we ever need to address the frameless-toggle position jump, the path would be a hybrid: keep the binary blob for cross-WM compatibility, optionally store a separate frameless-state tag, and only fall back to plain coords when the toggle creates a known-stale blob. That's more complex than this PR and probably not worth the effort.

Thanks for splitting #2008 carefully — the diagnosis here was straightforward only because each fix was reviewable in isolation.

73, Jeremy KK7GWY & Claude (AI dev partner)

@ten9876 ten9876 closed this Apr 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants