Skip to content

[ux] Fix Aetherial Audio frameless edge resizing#4266

Merged
ten9876 merged 2 commits into
aethersdr:mainfrom
jensenpat:fix/3876-aetherial-resize
Jul 19, 2026
Merged

[ux] Fix Aetherial Audio frameless edge resizing#4266
ten9876 merged 2 commits into
aethersdr:mainfrom
jensenpat:fix/3876-aetherial-resize

Conversation

@jensenpat

@jensenpat jensenpat commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • replace Aetherial Audio's widget-local frameless resize handler with the shared native-window FramelessResizer
  • let edge and corner presses reach QWindow::startSystemResize() even when child panels cover the window margins
  • retain the existing 960/1620 opening-height policy, minimum size, geometry persistence, and native-decorated behavior

Root cause

AetherialAudioStrip listened for edge presses on the top-level QWidget. Its child-heavy content covers the window margins, so those mouse events were dispatched to child widgets instead and the strip never reached startSystemResize(). The shared resizer installs its filter on the native QWindow, before widget-tree dispatch, which is the project-standard solution for this exact event-routing problem.

Verification

  • cmake --build build -j8
  • ctest --test-dir build -R '^(help_dialog_test|pan_layout_dialog_size_test|flex_control_dialog_size_test)$' --output-on-failure (2/2 registered matches passed)
  • python3 tools/check_engine_boundary.py --strict (0 blocking findings)
  • git diff --check
  • automation bridge reproduced the reporter layout at 1140x960, then resized the fixed strip to 1140x1700 and reported zero visible scrollbars

Proof

Before — reporter layout at 1140x960 with the vertical scrollbar:

Aetherial Audio before resize fix

After — full content at 1140x1700 with no visible scrollbar:

Aetherial Audio after resize fix

Automation follow-up

The bridge's current drag verb always starts at a widget center. A targeted dragAt or window edgeDrag verb with an explicit start offset would let agents prove native frameless edge gestures directly and safely.

Fixes #3876

💻 Generated with Codex with architecture by @jensenpat

👨🏼‍💻 Generated with OpenAI Codex (GPT-5.6 Sol) and tested by @jensenpat

@jensenpat jensenpat changed the title Fix Aetherial Audio frameless edge resizing [ux] Fix Aetherial Audio frameless edge resizing Jul 16, 2026
@jensenpat
jensenpat marked this pull request as ready for review July 16, 2026 15:36
@jensenpat
jensenpat requested a review from a team as a code owner July 16, 2026 15:36

@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 refactor — this swaps 78 lines of widget-local frameless-resize handling for the shared FramelessResizer, exactly matching the pattern already used by ConnectionPanel, FloatingContainerWindow, PanFloatingWindow, and 6 other call sites. The root-cause writeup is correct: child-heavy content covered the window margins, so edge presses went to child widgets and never reached startSystemResize(); installing at the QWindow boundary fixes exactly that. Nice cleanup.

I verified the mechanical concerns and they're all fine:

  • No dangling references to the removed members (edgesAt, updateResizeCursor, kResizeMargin, setMouseTracking) anywhere in the PR.
  • Dropping #include <QWindow> is safe — grep confirms no remaining QWindow/windowHandle/startSystemResize usage in the file after the deletion.
  • Frameless gating is preserved: the helper checks Qt::FramelessWindowHint and hands resize back to the OS when native decorations are on, so the native-decorated path is unchanged.
  • Runtime frameless toggle (MainWindow::setFramelessModem_aetherialStrip->setFramelessMode(on)) is safe: the filter lives on the persistent QWidgetWindow (only the platform handle is recreated by setWindowFlags), same as every other adopter.

No blocking issues, no polish items. LGTM — thanks for routing this through the standard helper rather than carrying yet another copy of the resize logic.


🤖 aethersdr-agent · cost: $5.2783 · model: claude-opus-4-8

NF0T
NF0T previously requested changes Jul 17, 2026

@NF0T NF0T left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Right architectural call — migrating off the widget-local resize handling onto the shared FramelessResizer (already adopted at 9 other sites: ConnectionPanel, FloatingContainerWindow, PanFloatingWindow, PersistentDialog, MainWindow itself, and others) is exactly the fix this bug class needs, and the root-cause diagnosis (child-heavy content eating edge presses before they reach the top-level widget) is correct. CI is green and the commit is signed.

Two concrete regressions before this can merge, both verified against the actual geometry and event flow:

1. Dragging the title bar near its top edge now resizes instead of moves. m_titleBar is 18px tall, full-width, at y=0. FramelessResizer::install(this) uses the default 6px margin, so the top third of the title bar is now inside the resize zone. FramelessResizer's filter sits on the QWindow and sees mouse events before Qt's widget dispatch does, so it now consumes the press and calls startSystemResize() there before the title bar's own move-drag handling (FramelessMoveHelper::start, via the class's existing eventFilter() for m_titleBar) ever gets a look. Pre-PR this same band correctly started a move, since the old widget-level mousePressEvent lost to the title bar (a child widget) under normal Qt dispatch. This is always-reachable, not an edge case — any grab in the title bar's top third now misfires as a resize.

2. The maximize/fullscreen guard didn't survive the migration. The old edgesAt() explicitly returned no edges when isMaximized() || isFullScreen(). The shared FramelessResizer::edgesAt() has no such check — it's gated only on Qt::FramelessWindowHint, which stays set regardless of window state. Since Aetherial Audio supports maximize (title-bar button + double-click), a maximized strip can now attempt startSystemResize() from a screen-edge hover. Since this gap lives in FramelessResizer.cpp itself rather than this PR's own code, the cleaner fix is probably adding the check there — it'd close the same latent gap for every current and future adopter, not just this one.

Investigated and ruled out: whether toggling the global "Frameless Window" setting after the strip has already been shown once could orphan FramelessResizer's QWindow-migrated filter and silently kill resize for the rest of the session. It can't — MainWindow itself is a FramelessResizer adopter and runs this exact runtime-toggle-while-visible pattern on itself, with a comment (MainWindow.cpp:992) already reasoning through it ("Stays installed across frameless toggles... our filter no-ops"). Nine adopters exercise this safely; not a risk here.

Non-blocking, separate follow-up: ClientEqEditor/ClientCompEditor/ClientGateEditor/ClientTubeEditor/ClientPuduEditor share the same frameless/child-heavy shape but have no resize handling at all yet (not even the old buggy version) — worth the same one-line FramelessResizer::install(this) in a follow-up PR, not something to hold this one for.

Happy to re-review quickly once the title-bar band is resolved and the maximize guard lands (here or in FramelessResizer directly) — this is close, and the reuse win is worth landing.

@NF0T NF0T self-assigned this Jul 17, 2026
…melessResizer (aethersdr#4266). Principle VIII.

Fixes @NF0T's two regressions from migrating Aetherial Audio onto the shared
FramelessResizer:

- Maximize/fullscreen guard: the old AetherialAudioStrip::edgesAt() returned no
  edges when isMaximized()/isFullScreen(); FramelessResizer::edgesAt() had no
  such check, so a maximized/fullscreen strip could startSystemResize() from a
  screen-edge hover. Centralize the guard in FramelessResizer — closes the same
  latent gap for all nine adopters, not just this one.

- Title-bar grab misfiring as a resize: AetherialAudioStrip's title bar is an
  18px edge-to-edge strip at y=0, so the default 6px top resize margin overlapped
  its top third; the QWindow-level filter consumed the press before the title
  bar's own move-drag ran. Add an optional topMoveReserve to FramelessResizer:
  the top strip is reserved for the window's move handling (edgesAt returns none
  there) while resize still works everywhere below it. Aetherial Audio installs
  with a reserve of its title-bar height, preserving the edge-to-edge look
  (unlike ConnectionPanel, which insets its identical 18px title bar to clear
  the edges). Other adopters default to 0 — no behavior change.

Full app builds clean; default install() args keep every existing adopter
call-site unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@ten9876 ten9876 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving — @NF0T's two regressions are addressed (d7a50f3c)

Both fixes landed in FramelessResizer so every adopter benefits, per @NF0T's steer:

  1. Maximize/fullscreen guard — centralized into FramelessResizer::edgesAt() (isMaximized() || isFullScreen() → {}). Closes the gap the deleted AetherialAudioStrip::edgesAt() used to cover, for all 9 adopters.
  2. Title-bar grab misfiring as resize — added an optional topMoveReserve to FramelessResizer: the top move-handle strip returns no edges, so the press falls through to the title bar's FramelessMoveHelper move, while resize still works everywhere below it. Aetherial Audio installs with topMoveReserve = kTitleBarHeight (18) — preserving the intended edge-to-edge look (rather than ConnectionPanel's inset-the-title-bar approach for its identical 18px bar). Other adopters default to 0: no behavior change, and the default install() args keep every existing call-site unchanged.

Full app builds clean. @NF0T — this is the "here or in FramelessResizer directly" path you preferred for the maximize guard; when you get a moment, a re-review to clear your changes-requested would unblock it. (Your non-blocking Client*Editor follow-up is noted for a separate PR.) Thanks for the precise geometry/event-flow diagnosis.

@ten9876
ten9876 enabled auto-merge (squash) July 18, 2026 23:48
@ten9876
ten9876 dismissed NF0T’s stale review July 19, 2026 00:00

Requested changes have been applied

@ten9876
ten9876 merged commit 76f3fd4 into aethersdr:main Jul 19, 2026
6 checks passed
nonoo pushed a commit to nonoo/AetherSDR that referenced this pull request Jul 19, 2026
## Summary

- replace Aetherial Audio's widget-local frameless resize handler with
the shared native-window `FramelessResizer`
- let edge and corner presses reach `QWindow::startSystemResize()` even
when child panels cover the window margins
- retain the existing 960/1620 opening-height policy, minimum size,
geometry persistence, and native-decorated behavior

## Root cause

`AetherialAudioStrip` listened for edge presses on the top-level
`QWidget`. Its child-heavy content covers the window margins, so those
mouse events were dispatched to child widgets instead and the strip
never reached `startSystemResize()`. The shared resizer installs its
filter on the native `QWindow`, before widget-tree dispatch, which is
the project-standard solution for this exact event-routing problem.

## Verification

- `cmake --build build -j8`
- `ctest --test-dir build -R
'^(help_dialog_test|pan_layout_dialog_size_test|flex_control_dialog_size_test)$'
--output-on-failure` (2/2 registered matches passed)
- `python3 tools/check_engine_boundary.py --strict` (0 blocking
findings)
- `git diff --check`
- automation bridge reproduced the reporter layout at 1140x960, then
resized the fixed strip to 1140x1700 and reported zero visible
scrollbars

## Proof

Before — reporter layout at 1140x960 with the vertical scrollbar:

![Aetherial Audio before resize
fix](https://raw.githubusercontent.com/jensenpat/AetherSDR/fix-3876-assets/.pr-assets/3876-before.png)

After — full content at 1140x1700 with no visible scrollbar:

![Aetherial Audio after resize
fix](https://raw.githubusercontent.com/jensenpat/AetherSDR/fix-3876-assets/.pr-assets/3876-after.png)

## Automation follow-up

The bridge's current `drag` verb always starts at a widget center. A
targeted `dragAt` or `window edgeDrag` verb with an explicit start
offset would let agents prove native frameless edge gestures directly
and safely.

Fixes aethersdr#3876

💻 Generated with Codex with architecture by @jensenpat

👨🏼‍💻 Generated with OpenAI Codex (GPT-5.6 Sol) and tested by @jensenpat

---------

Co-authored-by: Jeremy [KK7GWY] <kk7gwy@aethersdr.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

Bug (minor): Unable to resize Aetherial Audio to elininat scroll bar

3 participants