-
-
Notifications
You must be signed in to change notification settings - Fork 114
fix(gui): reopen the Aetherial strip after minimize — Principle XI. #5366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,27 @@ | ||||||||||||||||||||||
| #include "gui/WindowShowState.h" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #include <QWidget> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| namespace AetherSDR { | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| bool windowIsShowing(const QWidget* w) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| return w && w->isVisible() && !w->isMinimized(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| void showAndRaiseWindow(QWidget* w) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| if (!w) | ||||||||||||||||||||||
| return; | ||||||||||||||||||||||
|
Comment on lines
+14
to
+15
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking convention nit: AGENTS.md requires braces on all control flow. Please brace this guard (and the toggle branches in the new test).
Suggested change
Comment on lines
+14
to
+15
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirming @Ozy311's nit as canon rather than taste — Also worth recording why the version that landed is right: @ten9876's suggested
Suggested change
|
||||||||||||||||||||||
| // Clear ONLY the minimized bit. showNormal() would also clear Maximized | ||||||||||||||||||||||
| // and FullScreen, so a strip that was maximized, then minimized, would come | ||||||||||||||||||||||
| // back at normal size (#3918). On a hidden widget this is a pending state | ||||||||||||||||||||||
| // that show() applies; on a visible one it takes effect immediately and | ||||||||||||||||||||||
| // show() is a no-op. | ||||||||||||||||||||||
| w->setWindowState(w->windowState() & ~Qt::WindowMinimized); | ||||||||||||||||||||||
| w->show(); | ||||||||||||||||||||||
| w->raise(); | ||||||||||||||||||||||
| w->activateWindow(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| } // namespace AetherSDR | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| #pragma once | ||
|
|
||
| class QWidget; | ||
|
|
||
| namespace AetherSDR { | ||
|
|
||
| // Window show-state helpers for press-to-open / press-again-to-close buttons. | ||
| // | ||
| // QWidget::isVisible() stays TRUE for a minimized window, and QWidget::show() | ||
| // on a minimized window restores it to its saved state — which is minimized. | ||
| // A toggle written as `if (w->isVisible()) w->hide(); else w->show();` is | ||
| // therefore unrecoverable once the window is minimized: the first press hides | ||
| // it, and the second press "shows" it straight back into the taskbar/Dock. | ||
| // Both behaviours are pinned by window_show_state_test. | ||
|
|
||
| // True when w is actually on screen for the user — visible AND not minimized. | ||
| [[nodiscard]] bool windowIsShowing(const QWidget* w); | ||
|
|
||
| // Bring w to the front, un-minimizing it first if needed. Only the Minimized | ||
| // bit is cleared, so a Maximized or FullScreen window keeps that state — both | ||
| // when it is merely raised and when it is restored from the taskbar/Dock | ||
| // (#3918). showNormal() would drop it in either case. | ||
| void showAndRaiseWindow(QWidget* w); | ||
|
|
||
| } // namespace AetherSDR |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,129 @@ | ||||||||||||||||||||||||
| // Regression test for the Aetherial Audio Channel Strip toggle, which could | ||||||||||||||||||||||||
| // not reopen the window once it had been minimized. | ||||||||||||||||||||||||
| // | ||||||||||||||||||||||||
| // Two Qt behaviours combine into the bug, and both are pinned here against a | ||||||||||||||||||||||||
| // REAL QWidget rather than asserted from memory: | ||||||||||||||||||||||||
| // | ||||||||||||||||||||||||
| // 1. QWidget::isVisible() stays TRUE while a window is minimized. A toggle | ||||||||||||||||||||||||
| // written `if (w->isVisible()) w->hide(); else w->show();` therefore | ||||||||||||||||||||||||
| // treats a minimized window as "showing" and hides it. | ||||||||||||||||||||||||
| // 2. QWidget::show() on a minimized window restores its SAVED state, which | ||||||||||||||||||||||||
| // is still minimized — so the follow-up press does not recover it either. | ||||||||||||||||||||||||
| // | ||||||||||||||||||||||||
| // windowIsShowing() fixes (1) and showAndRaiseWindow() fixes (2), while the | ||||||||||||||||||||||||
| // isMinimized() guard inside it keeps a maximized window maximized (#3918). | ||||||||||||||||||||||||
| // | ||||||||||||||||||||||||
| // Runs on the offscreen platform, where both behaviours reproduce. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| #include "gui/WindowShowState.h" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| #include <QApplication> | ||||||||||||||||||||||||
| #include <QWidget> | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| #include <cstdio> | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| using AetherSDR::showAndRaiseWindow; | ||||||||||||||||||||||||
| using AetherSDR::windowIsShowing; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| namespace { | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| int g_failures = 0; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| void report(const char* name, bool ok) | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| std::printf("%s %s\n", ok ? "[ OK ]" : "[FAIL]", name); | ||||||||||||||||||||||||
| if (!ok) { | ||||||||||||||||||||||||
| ++g_failures; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // The toggle exactly as MainWindow::toggleAetherialStrip() runs it. | ||||||||||||||||||||||||
| void toggle(QWidget* w) | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| if (windowIsShowing(w)) | ||||||||||||||||||||||||
| w->hide(); | ||||||||||||||||||||||||
| else | ||||||||||||||||||||||||
| showAndRaiseWindow(w); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| } // namespace | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| int main(int argc, char** argv) | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| QApplication app(argc, argv); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // --- Case 1: null is never "showing", and raising it must not crash. | ||||||||||||||||||||||||
| report("windowIsShowing(nullptr) is false", !windowIsShowing(nullptr)); | ||||||||||||||||||||||||
| showAndRaiseWindow(nullptr); | ||||||||||||||||||||||||
| report("showAndRaiseWindow(nullptr) is a no-op", true); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| QWidget w; | ||||||||||||||||||||||||
| w.resize(320, 240); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // --- Case 2: a hidden window is not showing; the toggle opens it. | ||||||||||||||||||||||||
| report("hidden window is not showing", !windowIsShowing(&w)); | ||||||||||||||||||||||||
| toggle(&w); | ||||||||||||||||||||||||
| app.processEvents(); | ||||||||||||||||||||||||
| report("toggle opens a hidden window", windowIsShowing(&w)); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // --- Case 3: the toggle closes an ordinary open window. | ||||||||||||||||||||||||
| toggle(&w); | ||||||||||||||||||||||||
| app.processEvents(); | ||||||||||||||||||||||||
| report("toggle hides a showing window", !w.isVisible()); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // --- Case 4: THE BUG. Qt reports a minimized window as visible, so the | ||||||||||||||||||||||||
| // old bare isVisible() check would take the hide() branch here. | ||||||||||||||||||||||||
| w.show(); | ||||||||||||||||||||||||
| app.processEvents(); | ||||||||||||||||||||||||
| w.showMinimized(); | ||||||||||||||||||||||||
| app.processEvents(); | ||||||||||||||||||||||||
| report("Qt: minimized window still reports isVisible()", w.isVisible()); | ||||||||||||||||||||||||
| report("Qt: minimized window reports isMinimized()", w.isMinimized()); | ||||||||||||||||||||||||
| report("windowIsShowing() treats minimized as not showing", | ||||||||||||||||||||||||
| !windowIsShowing(&w)); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // --- Case 5: why the second press never recovered it either — show() on a | ||||||||||||||||||||||||
| // minimized window leaves it minimized. | ||||||||||||||||||||||||
| w.show(); | ||||||||||||||||||||||||
| app.processEvents(); | ||||||||||||||||||||||||
| report("Qt: show() does not un-minimize", w.isMinimized()); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // --- Case 6: the toggle restores a minimized window in ONE press. | ||||||||||||||||||||||||
| toggle(&w); | ||||||||||||||||||||||||
| app.processEvents(); | ||||||||||||||||||||||||
| report("toggle restores a minimized window", windowIsShowing(&w)); | ||||||||||||||||||||||||
| report("restored window is no longer minimized", !w.isMinimized()); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // --- Case 7: the isMinimized() guard — raising a maximized window must | ||||||||||||||||||||||||
| // not drop it out of maximized state (#3918). | ||||||||||||||||||||||||
| w.showMaximized(); | ||||||||||||||||||||||||
| app.processEvents(); | ||||||||||||||||||||||||
| if (w.isMaximized()) { | ||||||||||||||||||||||||
| showAndRaiseWindow(&w); | ||||||||||||||||||||||||
| app.processEvents(); | ||||||||||||||||||||||||
| report("showAndRaiseWindow keeps a maximized window maximized", | ||||||||||||||||||||||||
| w.isMaximized()); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // --- Case 8: the reopen path itself must not drop maximize. This is | ||||||||||||||||||||||||
| // what showNormal() got wrong: it clears Minimized AND Maximized, so a | ||||||||||||||||||||||||
| // maximized strip that was minimized came back at normal size. | ||||||||||||||||||||||||
| w.showMinimized(); | ||||||||||||||||||||||||
| app.processEvents(); | ||||||||||||||||||||||||
| report("Qt: minimizing keeps the Maximized bit alongside Minimized", | ||||||||||||||||||||||||
| w.isMinimized() && (w.windowState() & Qt::WindowMaximized)); | ||||||||||||||||||||||||
| toggle(&w); | ||||||||||||||||||||||||
| app.processEvents(); | ||||||||||||||||||||||||
| report("toggle restores a minimized-from-maximized window", | ||||||||||||||||||||||||
| windowIsShowing(&w)); | ||||||||||||||||||||||||
| report("restored window is still maximized", w.isMaximized()); | ||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||
| // Some platforms decline to maximize; the guard is still pinned by | ||||||||||||||||||||||||
| // case 6, so skip rather than fail on a platform quirk. | ||||||||||||||||||||||||
| std::printf("[SKIP] maximized state unavailable on this platform\n"); | ||||||||||||||||||||||||
|
Comment on lines
+119
to
+122
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Cases 7 and 8 are the only coverage of maximize preservation anywhere in the file, and they sit behind The justification in the comment doesn't hold: case 6 is minimize → restore on a non-maximized window — it exercises Since the skip is a real platform-capability question you can't resolve from here, the cheap fix is to make it loud rather than silent — count it, or fail if it skips on the platforms you know do support it:
Suggested change
Separately: |
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (g_failures == 0) { | ||||||||||||||||||||||||
| std::printf("All window show-state tests passed.\n"); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| return g_failures == 0 ? 0 : 1; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scope note, not a blocker — see the review body's scope table.
This file isn't explained by #5365, which is strictly about the Aetherial strip, and commit 1's message explicitly set it aside: "The helper is where those two sites should eventually converge; not touched here to keep this change to the reported bug." Commit 2 converged them.
It also isn't behaviour-neutral. The old
showNormal()cleared Minimized and Maximized and FullScreen, so a maximized-then-minimized main window came back un-maximized on a net-reminder raise; with the helper it now stays maximized. I think that's what the#3918comment always meant to do and the change reads as correct — but it's a user-visible change to a surface the linked issue never mentions, so it should be stated in the PR body rather than inferred from the diff. Maintainer's call whether it rides along or splits out.(The other difference is that the helper calls
show()unconditionally where the old code did not. On a visible widget that's a no-op, so it only matters if the main window were hidden — where revealing it is the intent anyway. No concern from me.)