Make popout windows follow frameless setting#2449
Conversation
There was a problem hiding this comment.
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
FramelessWindowTitleBarwidget and integrated it into multiple popout dialogs/windows. - Implemented
setFramelessMode(bool)across several popouts and wiredMainWindow::setFramelessWindow()to propagate the toggle to active popouts/editors. - Replaced the FFTW wisdom
QProgressDialogwith 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.
| Qt::WindowFlags flags = Qt::Dialog; | ||
| if (on) | ||
| flags |= Qt::FramelessWindowHint; |
| Qt::WindowFlags flags = Qt::Dialog; | ||
| if (on) | ||
| flags |= Qt::FramelessWindowHint; |
| Qt::WindowFlags flags = Qt::Dialog; | ||
| if (on) { | ||
| flags |= Qt::FramelessWindowHint; |
| Qt::WindowFlags flags = Qt::Window; | ||
| if (on) { | ||
| flags |= Qt::FramelessWindowHint; |
| Qt::WindowFlags flags = Qt::Dialog; | ||
| if (on) | ||
| flags |= Qt::FramelessWindowHint; |
| Qt::WindowFlags flags = Qt::Window; | ||
| if (on) { | ||
| flags |= Qt::FramelessWindowHint; |
|
Hi @rfoust — thanks for the contribution! Good news: this is not a problem with your code. What failedOnly one check went red on
That's a post-CI housekeeping job that runs
( What to doNothing 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 reviewWhile you're here, Copilot flagged a real concern across all six Each one rebuilds window flags from scratch ( Qt::WindowFlags flags = windowFlags();
flags.setFlag(Qt::FramelessWindowHint, on);
setWindowFlags(flags);The risk: assigning a bare Thanks again for the work on this — frameless mode coverage across the dialogs is a nice consistency win. 🙌 |
There was a problem hiding this comment.
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 inMainWindow.cppbut every other touched class re-implementsAppSettings::instance().value("FramelessWindow", "True").toString() == "True"inline. If you keep the pattern, fine; otherwise hoisting that into something likeAppSettings::instance().framelessWindowEnabled()(or a small helper in a shared header) would deduplicate ~7 copies.- The
if (!(windowFlags() & Qt::FramelessWindowHint)) return {};guards added toedgesAt()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.
|
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. |
|
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. |
|
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) |
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
Verification
Built successfully on macOS with:
Existing unrelated compiler warnings remain in MainWindow.cpp and other files.