Skip to content

Make popout windows follow frameless setting#2449

Merged
ten9876 merged 4 commits into
aethersdr:mainfrom
rfoust:codex/frameless-popouts
May 7, 2026
Merged

Make popout windows follow frameless setting#2449
ten9876 merged 4 commits into
aethersdr:mainfrom
rfoust:codex/frameless-popouts

Conversation

@rfoust

@rfoust rfoust commented May 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes inconsistent frameless-window behavior across modeless popout windows. Before this change, several popouts either did not support the View -> Frameless Window setting at all, or were hardcoded frameless and stayed frameless after the setting was disabled. This left windows such as Network Diagnostics, HF Propagation Dashboard, and Aetherial Audio out of sync with the main window and Radio Setup.

This PR makes the frameless setting consistently apply when popouts are created and when the View-menu toggle changes at runtime.

What changed

  • Added a shared Network Diagnostics-style frameless title bar helper so popout chrome uses the same 18 px gradient title bar, grip glyph, and min/max/close trio.
  • Added frameless-mode support to Connect, Radio Setup, AetherDSP settings, Network Diagnostics, and HF Propagation Dashboard popouts.
  • Replaced the FFTW wisdom QProgressDialog with a frameless-aware dialog using the same popout chrome.
  • Updated Network Diagnostics so it reads AppSettings("FramelessWindow") instead of always forcing Qt::FramelessWindowHint.
  • Updated Aetherial Audio and its lazy-created stage editor popouts so they follow the setting on creation and update while already open.
  • Updated the Quindar tone editor so it respects the frameless setting and syncs if the setting changes while it is open.
  • Propagated MainWindow::setFramelessWindow() to active popout windows, including panadapter/app-container popouts, Connect, Radio Setup, Network Diagnostics, HF Propagation Dashboard, AetherDSP, Aetherial Audio, Aetherial stage editors, and the Quindar tone editor.

Verification

Built successfully on macOS with:

cmake --build /private/tmp/aethersdr-frameless-build -j8

Existing unrelated compiler warnings remain in MainWindow.cpp and other files.

Copilot AI review requested due to automatic review settings May 7, 2026 12:50
@rfoust rfoust requested review from jensenpat and ten9876 as code owners May 7, 2026 12:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the app’s “View → Frameless Window” setting apply consistently to modeless/popout windows by introducing a shared frameless title-bar widget and propagating frameless toggles to already-open popouts.

Changes:

  • Added a reusable FramelessWindowTitleBar widget and integrated it into multiple popout dialogs/windows.
  • Implemented setFramelessMode(bool) across several popouts and wired MainWindow::setFramelessWindow() to propagate the toggle to active popouts/editors.
  • Replaced the FFTW wisdom QProgressDialog with a custom dialog that supports the frameless chrome.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
CMakeLists.txt Adds the new title-bar implementation file to the GUI build.
src/gui/FramelessWindowTitleBar.h Declares the shared frameless title-bar widget API.
src/gui/FramelessWindowTitleBar.cpp Implements shared frameless popout chrome (drag/move + min/max/close).
src/gui/MainWindow.cpp Propagates frameless toggles to more popouts/editors; replaces wisdom progress dialog UI.
src/gui/EditorFramelessTitleBar.cpp Adds an objectName so editors can be found/toggled by MainWindow helper.
src/gui/ConnectionPanel.h Adds frameless-mode support API/state for the Connect popout.
src/gui/ConnectionPanel.cpp Implements frameless title bar + runtime frameless toggling for Connect popout.
src/gui/RadioSetupDialog.h Adds frameless-mode support API/state for Radio Setup popout.
src/gui/RadioSetupDialog.cpp Implements frameless title bar + runtime frameless toggling for Radio Setup.
src/gui/NetworkDiagnosticsDialog.h Adds frameless-mode API/state for Network Diagnostics popout.
src/gui/NetworkDiagnosticsDialog.cpp Stops forcing frameless always; adds frameless-mode toggling + guard resize logic.
src/gui/AetherialAudioStrip.h Adds frameless-mode API/state for Aetherial Audio strip window.
src/gui/AetherialAudioStrip.cpp Makes strip follow frameless setting on creation and at runtime; guards resize edges when decorated.
src/gui/AetherDspDialog.h Adds frameless-mode API for AetherDSP settings dialog.
src/gui/AetherDspDialog.cpp Stops forcing frameless always; adds frameless-mode toggling and cursor/resize guard.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/gui/ConnectionPanel.cpp Outdated
Comment on lines +664 to +666
Qt::WindowFlags flags = Qt::Dialog;
if (on)
flags |= Qt::FramelessWindowHint;
Comment thread src/gui/RadioSetupDialog.cpp Outdated
Comment on lines +259 to +261
Qt::WindowFlags flags = Qt::Dialog;
if (on)
flags |= Qt::FramelessWindowHint;
Comment thread src/gui/NetworkDiagnosticsDialog.cpp Outdated
Comment on lines +1045 to +1047
Qt::WindowFlags flags = Qt::Dialog;
if (on) {
flags |= Qt::FramelessWindowHint;
Comment thread src/gui/AetherialAudioStrip.cpp Outdated
Comment on lines +698 to +700
Qt::WindowFlags flags = Qt::Window;
if (on) {
flags |= Qt::FramelessWindowHint;
Comment thread src/gui/AetherDspDialog.cpp Outdated
Comment on lines +165 to +167
Qt::WindowFlags flags = Qt::Dialog;
if (on)
flags |= Qt::FramelessWindowHint;
Comment thread src/gui/MainWindow.cpp Outdated
Comment on lines +4112 to +4114
Qt::WindowFlags flags = Qt::Window;
if (on) {
flags |= Qt::FramelessWindowHint;
@aethersdr-agent

Copy link
Copy Markdown
Contributor

Hi @rfoust — thanks for the contribution! Good news: this is not a problem with your code.

What failed

Only one check went red on 0b34f815:

  • Cleanup artifacts → step Delete artifacts (job log)

That's a post-CI housekeeping job that runs gh api calls to delete intermediate artifacts after the workflow completes. It has nothing to do with whether your changes compile or behave correctly. The actual content checks all passed:

  • Prepare
  • Agent
  • Upload results
  • build
  • check-paths

(check-windows and analyze (cpp) were still in-progress at the time of writing — those are the ones that would actually flag a real problem.)

What to do

Nothing on your end. This kind of failure is usually transient — a GitHub API hiccup, a race on the artifact ID list, or an auth-scope issue on the cleanup token. A maintainer can re-run the failed job if needed; it won't change the mergeability of the PR.

One unrelated heads-up — Copilot's review

While you're here, Copilot flagged a real concern across all six setFramelessMode() implementations in this PR (ConnectionPanel.cpp:666, RadioSetupDialog.cpp:261, NetworkDiagnosticsDialog.cpp:1047, AetherialAudioStrip.cpp:700, AetherDspDialog.cpp:167, and MainWindow.cpp:4114's setEditorFramelessMode()):

Each one rebuilds window flags from scratch (Qt::Dialog or Qt::Window, plus optional Qt::FramelessWindowHint). The established pattern elsewhere in the codebase — see MainWindow::setFramelessWindow and PanFloatingWindow::setFramelessMode — is to preserve windowFlags() and only toggle the Qt::FramelessWindowHint bit:

Qt::WindowFlags flags = windowFlags();
flags.setFlag(Qt::FramelessWindowHint, on);
setWindowFlags(flags);

The risk: assigning a bare Qt::Dialog / Qt::Window can silently drop platform/Qt-default decoration hints (close/system menu, min/max buttons, etc.) when the user toggles back to the decorated mode. Worth aligning with the existing helpers before merge — but again, this won't show up as a CI failure, only as missing window-chrome bits at runtime on some platforms.

Thanks again for the work on this — frameless mode coverage across the dialogs is a nice consistency win. 🙌

@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.

Thanks @rfoust — broad and well-targeted change, and the new FramelessWindowTitleBar helper plus the unified propagation in MainWindow::setFramelessWindow match the existing chrome nicely. Reads fine for behaviour at the popouts I traced; below are a couple of constructive items, mostly aligned with what Copilot flagged.

1. Replacing window flags drops hints — preserve-and-toggle instead (Copilot finding, valid)

All six new setFramelessMode paths plus the two anonymous-namespace helpers in MainWindow.cpp use the same shape:

Qt::WindowFlags flags = Qt::Dialog;       // or Qt::Window
if (on) flags |= Qt::FramelessWindowHint;
setWindowFlags(flags);

This intentionally drops every other flag bit that may have been set (default Qt::WindowTitleHint, Qt::WindowSystemMenuHint, Qt::WindowCloseButtonHint, Qt::WindowMinMaxButtons, anything else the parent or platform applied). The convention already in the tree is preserve-and-toggle:

  • MainWindow::setFramelessWindow (src/gui/MainWindow.cpp:10138-10142)
  • PanFloatingWindow::setFramelessMode (src/gui/PanFloatingWindow.cpp:76-81)
Qt::WindowFlags flags = windowFlags();
if (on) flags |= Qt::FramelessWindowHint;
else    flags &= ~Qt::FramelessWindowHint;
setWindowFlags(flags);

In practice the symptom you might hit when toggling frameless OFF is a decorated dialog without the close/min/max trio on Windows, since the default Qt::Dialog hints aren't replaced after the first setWindowFlags(Qt::Dialog). Switching the new helpers (AetherDspDialog, RadioSetupDialog, ConnectionPanel, NetworkDiagnosticsDialog, PropDashboardDialog, AetherialAudioStrip, plus the setEditorFramelessMode / setDialogFramelessMode helpers in MainWindow.cpp) to the preserve-and-toggle form keeps the rest of the existing flag bits intact and matches the established codebase pattern.

2. Quindar lookup walks all top-level widgets

In MainWindow::setFramelessWindow:

for (auto* widget : QApplication::topLevelWidgets()) {
    if (widget && widget->objectName() == "quindarToneEditor") {
        setDialogFramelessMode(qobject_cast<QDialog*>(widget), on);
    }
}

This works, but it scans every top-level widget in the app and matches on a string objectName, so any other widget that ever happens to take the same objectName would also get rebuilt. Since StripFinalOutputPanel::showQuindarEditor already has the dialog pointer in scope, an alternative would be to expose a MainWindow::registerQuindarDialog(QDialog*) (cleared via QObject::destroyed), or to keep a QPointer<QDialog> m_quindarDialog set when the editor is created. Same with the editor pointers — they're already members, so this one is the only string-match scan. Minor, but worth tightening.

3. Small things

  • framelessWindowEnabled() lives in the anon namespace in MainWindow.cpp but every other touched class re-implements AppSettings::instance().value("FramelessWindow", "True").toString() == "True" inline. If you keep the pattern, fine; otherwise hoisting that into something like AppSettings::instance().framelessWindowEnabled() (or a small helper in a shared header) would deduplicate ~7 copies.
  • The if (!(windowFlags() & Qt::FramelessWindowHint)) return {}; guards added to edgesAt() are a nice touch — they prevent the custom 8-axis resize from firing when native decorations are in charge. Good defensive fix to bundle with this.

Nothing blocking — the structural cleanup here is a net win, and the propagation list in MainWindow::setFramelessWindow covers the popouts the PR description calls out. Mainly the flags-replacement issue is worth fixing across the new helpers so this doesn't surprise anyone the next time a dialog adds a custom hint.

@rfoust

rfoust commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the review feedback about preserving window flags when toggling frameless mode. The new setFramelessMode paths and MainWindow helper functions now use the existing windowFlags() and only set/clear Qt::FramelessWindowHint, matching MainWindow::setFramelessWindow and PanFloatingWindow::setFramelessMode. Rebuilt successfully with cmake --build /private/tmp/aethersdr-frameless-build -j8.

@rfoust

rfoust commented May 7, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up on the window-flag review fix: preserving windowFlags() verbatim caused some popouts to keep the wrong window type/parent relationship, so dragging the custom title bar could move the parent window with the popout. I pushed 016bfdd to preserve the existing non-type hints while explicitly restoring the intended top-level type before toggling Qt::FramelessWindowHint: Qt::Dialog for dialog popouts and Qt::Window for standalone editor/strip windows. This keeps the reviewer-requested hint preservation without linking the popout to the parent window. Rebuilt successfully with cmake --build /private/tmp/aethersdr-frameless-build -j8.

@ten9876 ten9876 merged commit a8937a0 into aethersdr:main May 7, 2026
5 checks passed
@ten9876

ten9876 commented May 7, 2026

Copy link
Copy Markdown
Collaborator

Claude here — really substantial chrome consolidation, Robbie. The inline title bar was quietly drifting across at least three files and the View > Frameless toggle had become aspirational rather than authoritative. Centralising into FramelessWindowTitleBar plus the per-dialog setFramelessMode contract is the right shape, and the edgesAt guard against the system frame competing with the custom resize-hit zone is a subtle but important detail. The FFTW wisdom dialog refit also tracked through cleanly — those lambda captures (label/progress instead of dlg->setLabelText) were the easy thing to get wrong and you got it right. Bonus: CMakeLists.txt change finally triggered check-windows on a release-relevant chrome PR. Merged.

73, Jeremy KK7GWY & Claude (AI dev partner)

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.

3 participants