Fix floating applets losing float state and position across restarts (#959)#985
Merged
ten9876 merged 7 commits intoApr 8, 2026
Merged
Conversation
When a floating applet was toggled off and back on, it reappeared docked instead of floating. Restarting the app restored the correct float state because the startup path reads IsFloating from AppSettings — but the toggle and device-reconnect paths did not. ## Root cause Two separate gaps in AppletPanel: 1. Toggle button handlers — the makeEntry lambda and the bare `connect(btn, toggled, wrapper, &QWidget::setVisible)` connections for TUN/AMP/AG only checked m_floatingWindows (which is empty after a dock) and never read IsFloating from AppSettings on re-enable. 2. setTunerVisible / setAmpVisible / setAgVisible re-enable paths — called when a conditional device (TGXL, amp, Antenna Genius) reconnects. Same omission: applet was shown docked regardless of persisted float state. ## Fix All toggle button handlers now check FloatingApplet_<ID>_IsFloating from AppSettings when checked=true and no floating window exists, and call floatApplet() via QTimer::singleShot(0) if the flag is set. This mirrors the startup restore logic exactly. The bare QWidget::setVisible slot connections for TUN, AMP, and AG are replaced with full float-aware lambdas matching the makeEntry pattern, so all 10 applets now have consistent toggle behaviour. setTunerVisible / setAmpVisible / setAgVisible also apply the same check on the visible=true path, covering the device-reconnect scenario. Fixes aethersdr#959 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck cycles (aethersdr#959) Three related bugs in the floating applet save/restore cycle are fixed together. When a floating applet was toggled off and back on, it reappeared docked instead of floating. Restarting the app correctly restored float state because the startup path reads IsFloating from AppSettings, but the toggle and device-reconnect paths did not. Root cause: two gaps in AppletPanel: 1. Toggle button handlers — the makeEntry lambda and the bare `connect(btn, toggled, wrapper, &QWidget::setVisible)` connections for TUN/AMP/AG only checked m_floatingWindows (which is empty after a dock) and never read IsFloating from AppSettings on re-enable. 2. setTunerVisible / setAmpVisible / setAgVisible re-enable paths — called when a conditional device (TGXL, amp, Antenna Genius) reconnects. Same omission: applet was shown docked regardless of persisted float state. Fix: all toggle button handlers now check FloatingApplet_<ID>_IsFloating from AppSettings when checked=true and no floating window exists, and call floatApplet() via QTimer::singleShot(0) if set. This mirrors the startup restore logic exactly. The bare QWidget::setVisible connections for TUN, AMP, and AG are replaced with full float-aware lambdas matching the makeEntry pattern, so all 10 applets have consistent toggle behaviour. setTunerVisible / setAmpVisible / setAgVisible also apply the same check on the visible=true path, covering the device-reconnect scenario. On the visible=false path they now call hideAndSave() instead of dockApplet(), preserving IsFloating so the window returns floating on reconnect. saveGeometry() was saving geometry().x()/y() (client-area origin) and restoring with move() (which sets the frame origin). The decoration height was added to Y on every cycle, causing the window to drift downward one title-bar height per hide/show. Fix: save pos() for x/y (frame position, what move() sets) and geometry().size() for w/h (client size, what resize() sets). On show(), the WM re-centers the window and fires moveEvent. restoreGeometry() runs at 200ms and moves the window correctly. But the 400ms debounce timer (started by the WM's moveEvent) was not suppressed, and the WM sends a final ConfigureNotify for the move() call after the guard was cleared at 500ms — restarting the debounce timer. On slow WMs or under load, this caused the centered position to be saved over the correct one. Three-part fix: 1. showAndRestore() now owns the entire guard lifetime: sets m_restoringGeometry = true before show(), clears it at 650ms (200ms restore delay + 300ms WM settle + 150ms safety margin). Previously the guard was cleared at 500ms, leaving a window where a late ConfigureNotify could corrupt the save. 2. restoreGeometry() no longer manages the guard at all — it was previously racing with showAndRestore's lifetime and clearing it early. moveEvent and resizeEvent check the guard before starting the debounce timer, so all WM-generated events during the full show+restore+settle window are suppressed. 3. dockApplet() now calls hideAndSave() instead of saveGeometry()+hide(). This stops the debounce timer before hiding, preventing a pending timer callback from calling saveGeometry() on an already-hidden window where pos() is not valid. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck cycles (aethersdr#959) Three related bugs in the floating applet save/restore cycle are fixed together. When a floating applet was toggled off and back on, it reappeared docked instead of floating. Restarting the app correctly restored float state because the startup path reads IsFloating from AppSettings, but the toggle and device-reconnect paths did not. Root cause: two gaps in AppletPanel: 1. Toggle button handlers — the makeEntry lambda and the bare `connect(btn, toggled, wrapper, &QWidget::setVisible)` connections for TUN/AMP/AG only checked m_floatingWindows (which is empty after a dock) and never read IsFloating from AppSettings on re-enable. 2. setTunerVisible / setAmpVisible / setAgVisible re-enable paths — called when a conditional device (TGXL, amp, Antenna Genius) reconnects. Same omission: applet was shown docked regardless of persisted float state. Fix: all toggle button handlers now check FloatingApplet_<ID>_IsFloating from AppSettings when checked=true and no floating window exists, and call floatApplet() via QTimer::singleShot(0) if set. This mirrors the startup restore logic exactly. The bare QWidget::setVisible connections for TUN, AMP, and AG are replaced with full float-aware lambdas matching the makeEntry pattern, so all 10 applets have consistent toggle behaviour. setTunerVisible / setAmpVisible / setAgVisible also apply the same check on the visible=true path, covering the device-reconnect scenario. On the visible=false path they now call hideAndSave() instead of dockApplet(), preserving IsFloating so the window returns floating on reconnect. saveGeometry() was saving geometry().x()/y() (client-area origin) and restoring with move() (which sets the frame origin). The decoration height was added to Y on every cycle, causing the window to drift downward one title-bar height per hide/show. Fix: save pos() for x/y (frame position, what move() sets) and geometry().size() for w/h (client size, what resize() sets). On show(), the WM re-centers the window and fires moveEvent. restoreGeometry() runs at 200ms and moves the window correctly. But the 400ms debounce timer (started by the WM's moveEvent) was not suppressed, and the WM sends a final ConfigureNotify for the move() call after the guard was cleared at 500ms — restarting the debounce timer. On slow WMs or under load, this caused the centered position to be saved over the correct one. Three-part fix: 1. showAndRestore() now owns the entire guard lifetime: sets m_restoringGeometry = true before show(), clears it at 650ms (200ms restore delay + 300ms WM settle + 150ms safety margin). Previously the guard was cleared at 500ms, leaving a window where a late ConfigureNotify could corrupt the save. 2. restoreGeometry() no longer manages the guard at all — it was previously racing with showAndRestore's lifetime and clearing it early. moveEvent and resizeEvent check the guard before starting the debounce timer, so all WM-generated events during the full show+restore+settle window are suppressed. 3. dockApplet() now calls hideAndSave() instead of saveGeometry()+hide(). This stops the debounce timer before hiding, preventing a pending timer callback from calling saveGeometry() on an already-hidden window where pos() is not valid. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck cycles (aethersdr#959) Three related bugs in the floating applet save/restore cycle are fixed together. When a floating applet was toggled off and back on, it reappeared docked instead of floating. Restarting the app correctly restored float state because the startup path reads IsFloating from AppSettings, but the toggle and device-reconnect paths did not. Root cause: two gaps in AppletPanel: 1. Toggle button handlers — the makeEntry lambda and the bare `connect(btn, toggled, wrapper, &QWidget::setVisible)` connections for TUN/AMP/AG only checked m_floatingWindows (which is empty after a dock) and never read IsFloating from AppSettings on re-enable. 2. setTunerVisible / setAmpVisible / setAgVisible re-enable paths — called when a conditional device (TGXL, amp, Antenna Genius) reconnects. Same omission: applet was shown docked regardless of persisted float state. Fix: all toggle button handlers now check FloatingApplet_<ID>_IsFloating from AppSettings when checked=true and no floating window exists, and call floatApplet() via QTimer::singleShot(0) if set. This mirrors the startup restore logic exactly. The bare QWidget::setVisible connections for TUN, AMP, and AG are replaced with full float-aware lambdas matching the makeEntry pattern, so all 10 applets have consistent toggle behaviour. setTunerVisible / setAmpVisible / setAgVisible also apply the same check on the visible=true path, covering the device-reconnect scenario. On the visible=false path they now call hideAndSave() instead of dockApplet(), preserving IsFloating so the window returns floating on reconnect. saveGeometry() was saving geometry().x()/y() (client-area origin) and restoring with move() (which sets the frame origin). The decoration height was added to Y on every cycle, causing the window to drift downward one title-bar height per hide/show. Fix: save pos() for x/y (frame position, what move() sets) and geometry().size() for w/h (client size, what resize() sets). On show(), the WM re-centers the window and fires moveEvent. restoreGeometry() runs at 200ms and moves the window correctly. But the 400ms debounce timer (started by the WM's moveEvent) was not suppressed, and the WM sends a final ConfigureNotify for the move() call after the guard was cleared at 500ms — restarting the debounce timer. On slow WMs or under load, this caused the centered position to be saved over the correct one. Three-part fix: 1. showAndRestore() now owns the entire guard lifetime: sets m_restoringGeometry = true before show(), clears it at 650ms (200ms restore delay + 300ms WM settle + 150ms safety margin). Previously the guard was cleared at 500ms, leaving a window where a late ConfigureNotify could corrupt the save. 2. restoreGeometry() no longer manages the guard at all — it was previously racing with showAndRestore's lifetime and clearing it early. moveEvent and resizeEvent check the guard before starting the debounce timer, so all WM-generated events during the full show+restore+settle window are suppressed. 3. dockApplet() now calls hideAndSave() instead of saveGeometry()+hide(). This stops the debounce timer before hiding, preventing a pending timer callback from calling saveGeometry() on an already-hidden window where pos() is not valid. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chibondking
added a commit
to chibondking/AetherSDR
that referenced
this pull request
Apr 10, 2026
The S-Meter section can now be detached into a floating window and re-docked exactly like all other applets — using the same FloatingAppletWindow infrastructure, AppSettings geometry persistence, and PR aethersdr#916 / aethersdr#985 bug fixes (drift, WM centering, shutdown race). ## What changed ### src/gui/AppletPanel.h - Added `QWidget* m_sMeterContent` — the floatable content container (SMeterWidget + TX/RX select row + Peak Hold row), analogous to the applet widget held at layout index 1 in every other wrapper. - Added private `floatSMeter()` and `dockSMeter()` methods. ### src/gui/AppletPanel.cpp **S-Meter construction:** - `AppletTitleBar("S-Meter", "VU", this)` — passes the panel pointer so the right-click context menu ("⬈ Pop out" / "↩ Dock") is active. - Introduced `m_sMeterContent` QWidget with its own QVBoxLayout as layout index 1 of `m_sMeterSection` (after the title bar), so the extractor logic in `floatSMeter()` / `dockSMeter()` is identical to every other applet. **ANLG toggle button:** - Updated to the float-aware pattern used by TUN / AMP / AG: raises/hides the floating window when VU is floating, re-floats via `QTimer::singleShot(0)` when IsFloating is persisted but no window is open yet (toggle-off → toggle-on cycle fix from aethersdr#985). **`isAppletFloating()`:** - Added early return for `"VU"`: checks `m_floatingWindows.contains("VU")` directly (VU is not in `m_appletOrder`). **`floatApplet()` / `dockApplet()`:** - Added `if (id == "VU")` dispatch at the top of each, delegating to `floatSMeter()` / `dockSMeter()`. **`floatSMeter()` / `dockSMeter()`:** - Mirror `floatApplet()` / `dockApplet()` exactly: reparent content out of/back into `m_sMeterSection`, create/destroy `FloatingAppletWindow`, persist `FloatingApplet_VU_IsFloating`, save geometry on dock. **Startup restore:** - After the existing `m_appletOrder` restore loop, added a `QTimer::singleShot(0)` restore for `"VU"` so the S-Meter reopens floating if it was floating when the app last closed. Closes aethersdr#993 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
aethersdr-agent Bot
pushed a commit
that referenced
this pull request
Apr 10, 2026
…959) (#985) * Fix floating applets losing float state on toggle off/on (#959) When a floating applet was toggled off and back on, it reappeared docked instead of floating. Restarting the app restored the correct float state because the startup path reads IsFloating from AppSettings — but the toggle and device-reconnect paths did not. ## Root cause Two separate gaps in AppletPanel: 1. Toggle button handlers — the makeEntry lambda and the bare `connect(btn, toggled, wrapper, &QWidget::setVisible)` connections for TUN/AMP/AG only checked m_floatingWindows (which is empty after a dock) and never read IsFloating from AppSettings on re-enable. 2. setTunerVisible / setAmpVisible / setAgVisible re-enable paths — called when a conditional device (TGXL, amp, Antenna Genius) reconnects. Same omission: applet was shown docked regardless of persisted float state. ## Fix All toggle button handlers now check FloatingApplet_<ID>_IsFloating from AppSettings when checked=true and no floating window exists, and call floatApplet() via QTimer::singleShot(0) if the flag is set. This mirrors the startup restore logic exactly. The bare QWidget::setVisible slot connections for TUN, AMP, and AG are replaced with full float-aware lambdas matching the makeEntry pattern, so all 10 applets now have consistent toggle behaviour. setTunerVisible / setAmpVisible / setAgVisible also apply the same check on the visible=true path, covering the device-reconnect scenario. Fixes #959 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Potential fix on applet positioning when restoring from panel * Fix applet restore 'bounce' affecting window position on restore * Fix applet restore 'bounce' affecting window position on restore * Fix floating applets losing float state and position across dock/undock cycles (#959) Three related bugs in the floating applet save/restore cycle are fixed together. When a floating applet was toggled off and back on, it reappeared docked instead of floating. Restarting the app correctly restored float state because the startup path reads IsFloating from AppSettings, but the toggle and device-reconnect paths did not. Root cause: two gaps in AppletPanel: 1. Toggle button handlers — the makeEntry lambda and the bare `connect(btn, toggled, wrapper, &QWidget::setVisible)` connections for TUN/AMP/AG only checked m_floatingWindows (which is empty after a dock) and never read IsFloating from AppSettings on re-enable. 2. setTunerVisible / setAmpVisible / setAgVisible re-enable paths — called when a conditional device (TGXL, amp, Antenna Genius) reconnects. Same omission: applet was shown docked regardless of persisted float state. Fix: all toggle button handlers now check FloatingApplet_<ID>_IsFloating from AppSettings when checked=true and no floating window exists, and call floatApplet() via QTimer::singleShot(0) if set. This mirrors the startup restore logic exactly. The bare QWidget::setVisible connections for TUN, AMP, and AG are replaced with full float-aware lambdas matching the makeEntry pattern, so all 10 applets have consistent toggle behaviour. setTunerVisible / setAmpVisible / setAgVisible also apply the same check on the visible=true path, covering the device-reconnect scenario. On the visible=false path they now call hideAndSave() instead of dockApplet(), preserving IsFloating so the window returns floating on reconnect. saveGeometry() was saving geometry().x()/y() (client-area origin) and restoring with move() (which sets the frame origin). The decoration height was added to Y on every cycle, causing the window to drift downward one title-bar height per hide/show. Fix: save pos() for x/y (frame position, what move() sets) and geometry().size() for w/h (client size, what resize() sets). On show(), the WM re-centers the window and fires moveEvent. restoreGeometry() runs at 200ms and moves the window correctly. But the 400ms debounce timer (started by the WM's moveEvent) was not suppressed, and the WM sends a final ConfigureNotify for the move() call after the guard was cleared at 500ms — restarting the debounce timer. On slow WMs or under load, this caused the centered position to be saved over the correct one. Three-part fix: 1. showAndRestore() now owns the entire guard lifetime: sets m_restoringGeometry = true before show(), clears it at 650ms (200ms restore delay + 300ms WM settle + 150ms safety margin). Previously the guard was cleared at 500ms, leaving a window where a late ConfigureNotify could corrupt the save. 2. restoreGeometry() no longer manages the guard at all — it was previously racing with showAndRestore's lifetime and clearing it early. moveEvent and resizeEvent check the guard before starting the debounce timer, so all WM-generated events during the full show+restore+settle window are suppressed. 3. dockApplet() now calls hideAndSave() instead of saveGeometry()+hide(). This stops the debounce timer before hiding, preventing a pending timer callback from calling saveGeometry() on an already-hidden window where pos() is not valid. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix floating applets losing float state and position across dock/undock cycles (#959) Three related bugs in the floating applet save/restore cycle are fixed together. When a floating applet was toggled off and back on, it reappeared docked instead of floating. Restarting the app correctly restored float state because the startup path reads IsFloating from AppSettings, but the toggle and device-reconnect paths did not. Root cause: two gaps in AppletPanel: 1. Toggle button handlers — the makeEntry lambda and the bare `connect(btn, toggled, wrapper, &QWidget::setVisible)` connections for TUN/AMP/AG only checked m_floatingWindows (which is empty after a dock) and never read IsFloating from AppSettings on re-enable. 2. setTunerVisible / setAmpVisible / setAgVisible re-enable paths — called when a conditional device (TGXL, amp, Antenna Genius) reconnects. Same omission: applet was shown docked regardless of persisted float state. Fix: all toggle button handlers now check FloatingApplet_<ID>_IsFloating from AppSettings when checked=true and no floating window exists, and call floatApplet() via QTimer::singleShot(0) if set. This mirrors the startup restore logic exactly. The bare QWidget::setVisible connections for TUN, AMP, and AG are replaced with full float-aware lambdas matching the makeEntry pattern, so all 10 applets have consistent toggle behaviour. setTunerVisible / setAmpVisible / setAgVisible also apply the same check on the visible=true path, covering the device-reconnect scenario. On the visible=false path they now call hideAndSave() instead of dockApplet(), preserving IsFloating so the window returns floating on reconnect. saveGeometry() was saving geometry().x()/y() (client-area origin) and restoring with move() (which sets the frame origin). The decoration height was added to Y on every cycle, causing the window to drift downward one title-bar height per hide/show. Fix: save pos() for x/y (frame position, what move() sets) and geometry().size() for w/h (client size, what resize() sets). On show(), the WM re-centers the window and fires moveEvent. restoreGeometry() runs at 200ms and moves the window correctly. But the 400ms debounce timer (started by the WM's moveEvent) was not suppressed, and the WM sends a final ConfigureNotify for the move() call after the guard was cleared at 500ms — restarting the debounce timer. On slow WMs or under load, this caused the centered position to be saved over the correct one. Three-part fix: 1. showAndRestore() now owns the entire guard lifetime: sets m_restoringGeometry = true before show(), clears it at 650ms (200ms restore delay + 300ms WM settle + 150ms safety margin). Previously the guard was cleared at 500ms, leaving a window where a late ConfigureNotify could corrupt the save. 2. restoreGeometry() no longer manages the guard at all — it was previously racing with showAndRestore's lifetime and clearing it early. moveEvent and resizeEvent check the guard before starting the debounce timer, so all WM-generated events during the full show+restore+settle window are suppressed. 3. dockApplet() now calls hideAndSave() instead of saveGeometry()+hide(). This stops the debounce timer before hiding, preventing a pending timer callback from calling saveGeometry() on an already-hidden window where pos() is not valid. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix floating applets losing float state and position across dock/undock cycles (#959) Three related bugs in the floating applet save/restore cycle are fixed together. When a floating applet was toggled off and back on, it reappeared docked instead of floating. Restarting the app correctly restored float state because the startup path reads IsFloating from AppSettings, but the toggle and device-reconnect paths did not. Root cause: two gaps in AppletPanel: 1. Toggle button handlers — the makeEntry lambda and the bare `connect(btn, toggled, wrapper, &QWidget::setVisible)` connections for TUN/AMP/AG only checked m_floatingWindows (which is empty after a dock) and never read IsFloating from AppSettings on re-enable. 2. setTunerVisible / setAmpVisible / setAgVisible re-enable paths — called when a conditional device (TGXL, amp, Antenna Genius) reconnects. Same omission: applet was shown docked regardless of persisted float state. Fix: all toggle button handlers now check FloatingApplet_<ID>_IsFloating from AppSettings when checked=true and no floating window exists, and call floatApplet() via QTimer::singleShot(0) if set. This mirrors the startup restore logic exactly. The bare QWidget::setVisible connections for TUN, AMP, and AG are replaced with full float-aware lambdas matching the makeEntry pattern, so all 10 applets have consistent toggle behaviour. setTunerVisible / setAmpVisible / setAgVisible also apply the same check on the visible=true path, covering the device-reconnect scenario. On the visible=false path they now call hideAndSave() instead of dockApplet(), preserving IsFloating so the window returns floating on reconnect. saveGeometry() was saving geometry().x()/y() (client-area origin) and restoring with move() (which sets the frame origin). The decoration height was added to Y on every cycle, causing the window to drift downward one title-bar height per hide/show. Fix: save pos() for x/y (frame position, what move() sets) and geometry().size() for w/h (client size, what resize() sets). On show(), the WM re-centers the window and fires moveEvent. restoreGeometry() runs at 200ms and moves the window correctly. But the 400ms debounce timer (started by the WM's moveEvent) was not suppressed, and the WM sends a final ConfigureNotify for the move() call after the guard was cleared at 500ms — restarting the debounce timer. On slow WMs or under load, this caused the centered position to be saved over the correct one. Three-part fix: 1. showAndRestore() now owns the entire guard lifetime: sets m_restoringGeometry = true before show(), clears it at 650ms (200ms restore delay + 300ms WM settle + 150ms safety margin). Previously the guard was cleared at 500ms, leaving a window where a late ConfigureNotify could corrupt the save. 2. restoreGeometry() no longer manages the guard at all — it was previously racing with showAndRestore's lifetime and clearing it early. moveEvent and resizeEvent check the guard before starting the debounce timer, so all WM-generated events during the full show+restore+settle window are suppressed. 3. dockApplet() now calls hideAndSave() instead of saveGeometry()+hide(). This stops the debounce timer before hiding, preventing a pending timer callback from calling saveGeometry() on an already-hidden window where pos() is not valid. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
chibondking
added a commit
to chibondking/AetherSDR
that referenced
this pull request
Apr 11, 2026
The S-Meter section can now be detached into a floating window and re-docked exactly like all other applets — using the same FloatingAppletWindow infrastructure, AppSettings geometry persistence, and PR aethersdr#916 / aethersdr#985 bug fixes (drift, WM centering, shutdown race). ## What changed ### src/gui/AppletPanel.h - Added `QWidget* m_sMeterContent` — the floatable content container (SMeterWidget + TX/RX select row + Peak Hold row), analogous to the applet widget held at layout index 1 in every other wrapper. - Added private `floatSMeter()` and `dockSMeter()` methods. ### src/gui/AppletPanel.cpp **S-Meter construction:** - `AppletTitleBar("S-Meter", "VU", this)` — passes the panel pointer so the right-click context menu ("⬈ Pop out" / "↩ Dock") is active. - Introduced `m_sMeterContent` QWidget with its own QVBoxLayout as layout index 1 of `m_sMeterSection` (after the title bar), so the extractor logic in `floatSMeter()` / `dockSMeter()` is identical to every other applet. **ANLG toggle button:** - Updated to the float-aware pattern used by TUN / AMP / AG: raises/hides the floating window when VU is floating, re-floats via `QTimer::singleShot(0)` when IsFloating is persisted but no window is open yet (toggle-off → toggle-on cycle fix from aethersdr#985). **`isAppletFloating()`:** - Added early return for `"VU"`: checks `m_floatingWindows.contains("VU")` directly (VU is not in `m_appletOrder`). **`floatApplet()` / `dockApplet()`:** - Added `if (id == "VU")` dispatch at the top of each, delegating to `floatSMeter()` / `dockSMeter()`. **`floatSMeter()` / `dockSMeter()`:** - Mirror `floatApplet()` / `dockApplet()` exactly: reparent content out of/back into `m_sMeterSection`, create/destroy `FloatingAppletWindow`, persist `FloatingApplet_VU_IsFloating`, save geometry on dock. **Startup restore:** - After the existing `m_appletOrder` restore loop, added a `QTimer::singleShot(0)` restore for `"VU"` so the S-Meter reopens floating if it was floating when the app last closed. **dockSMeter() — use m_sMeterContent directly (agent review fix)** The original used a fragile layout-walk to recover m_sMeterContent from FloatingAppletWindow's internal layout. If any itemAt() returned null, appletWidget would be null while the code still called win->deleteLater(), destroying m_sMeterContent and all its children (m_sMeter, m_txSelect, m_rxSelect, peak-hold widgets) — crash on next signal-level update. Fix: use the named member directly. Reparent m_sMeterContent into m_sMeterSection before deleteLater() so Qt's parent-chain destruction cannot reach it. **.gitignore — drop Visual Studio artifacts (agent review fix)** /.vs and /out/install/x64-Debug are Windows VS IDE artifacts; they belong in ~/.gitignore_global, not the project-wide .gitignore. Closes aethersdr#993 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chibondking
added a commit
to chibondking/AetherSDR
that referenced
this pull request
Apr 11, 2026
The S-Meter section can now be detached into a floating window and re-docked exactly like all other applets — using the same FloatingAppletWindow infrastructure, AppSettings geometry persistence, and PR aethersdr#916 / aethersdr#985 bug fixes (drift, WM centering, shutdown race). ## What changed ### src/gui/AppletPanel.h - Added `QWidget* m_sMeterContent` — the floatable content container (SMeterWidget + TX/RX select row + Peak Hold row), analogous to the applet widget held at layout index 1 in every other wrapper. - Added private `floatSMeter()` and `dockSMeter()` methods. ### src/gui/AppletPanel.cpp **S-Meter construction:** - `AppletTitleBar("S-Meter", "VU", this)` — passes the panel pointer so the right-click context menu ("⬈ Pop out" / "↩ Dock") is active. - Introduced `m_sMeterContent` QWidget with its own QVBoxLayout as layout index 1 of `m_sMeterSection` (after the title bar), so the extractor logic in `floatSMeter()` / `dockSMeter()` is identical to every other applet. **ANLG toggle button:** - Updated to the float-aware pattern used by TUN / AMP / AG: raises/hides the floating window when VU is floating, re-floats via `QTimer::singleShot(0)` when IsFloating is persisted but no window is open yet (toggle-off → toggle-on cycle fix from aethersdr#985). **`isAppletFloating()`:** - Added early return for `"VU"`: checks `m_floatingWindows.contains("VU")` directly (VU is not in `m_appletOrder`). **`floatApplet()` / `dockApplet()`:** - Added `if (id == "VU")` dispatch at the top of each, delegating to `floatSMeter()` / `dockSMeter()`. **`floatSMeter()` / `dockSMeter()`:** - Mirror `floatApplet()` / `dockApplet()` exactly: reparent content out of/back into `m_sMeterSection`, create/destroy `FloatingAppletWindow`, persist `FloatingApplet_VU_IsFloating`, save geometry on dock. **Startup restore:** - After the existing `m_appletOrder` restore loop, added a `QTimer::singleShot(0)` restore for `"VU"` so the S-Meter reopens floating if it was floating when the app last closed. **dockSMeter() — use m_sMeterContent directly (agent review fix)** The original used a fragile layout-walk to recover m_sMeterContent from FloatingAppletWindow's internal layout. If any itemAt() returned null, appletWidget would be null while the code still called win->deleteLater(), destroying m_sMeterContent and all its children (m_sMeter, m_txSelect, m_rxSelect, peak-hold widgets) — crash on next signal-level update. Fix: use the named member directly. Reparent m_sMeterContent into m_sMeterSection before deleteLater() so Qt's parent-chain destruction cannot reach it. **.gitignore — drop Visual Studio artifacts (agent review fix)** /.vs and /out/install/x64-Debug are Windows VS IDE artifacts; they belong in ~/.gitignore_global, not the project-wide .gitignore. Closes aethersdr#993 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ten9876
pushed a commit
that referenced
this pull request
Apr 11, 2026
* Updated for vscode and visual studio ignore patterns for local windows development. * Add pop-out/pop-in support for the S-Meter (VU) applet (#993) The S-Meter section can now be detached into a floating window and re-docked exactly like all other applets — using the same FloatingAppletWindow infrastructure, AppSettings geometry persistence, and PR #916 / #985 bug fixes (drift, WM centering, shutdown race). ## What changed ### src/gui/AppletPanel.h - Added `QWidget* m_sMeterContent` — the floatable content container (SMeterWidget + TX/RX select row + Peak Hold row), analogous to the applet widget held at layout index 1 in every other wrapper. - Added private `floatSMeter()` and `dockSMeter()` methods. ### src/gui/AppletPanel.cpp **S-Meter construction:** - `AppletTitleBar("S-Meter", "VU", this)` — passes the panel pointer so the right-click context menu ("⬈ Pop out" / "↩ Dock") is active. - Introduced `m_sMeterContent` QWidget with its own QVBoxLayout as layout index 1 of `m_sMeterSection` (after the title bar), so the extractor logic in `floatSMeter()` / `dockSMeter()` is identical to every other applet. **ANLG toggle button:** - Updated to the float-aware pattern used by TUN / AMP / AG: raises/hides the floating window when VU is floating, re-floats via `QTimer::singleShot(0)` when IsFloating is persisted but no window is open yet (toggle-off → toggle-on cycle fix from #985). **`isAppletFloating()`:** - Added early return for `"VU"`: checks `m_floatingWindows.contains("VU")` directly (VU is not in `m_appletOrder`). **`floatApplet()` / `dockApplet()`:** - Added `if (id == "VU")` dispatch at the top of each, delegating to `floatSMeter()` / `dockSMeter()`. **`floatSMeter()` / `dockSMeter()`:** - Mirror `floatApplet()` / `dockApplet()` exactly: reparent content out of/back into `m_sMeterSection`, create/destroy `FloatingAppletWindow`, persist `FloatingApplet_VU_IsFloating`, save geometry on dock. **Startup restore:** - After the existing `m_appletOrder` restore loop, added a `QTimer::singleShot(0)` restore for `"VU"` so the S-Meter reopens floating if it was floating when the app last closed. Closes #993 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add pop-out/pop-in support for the S-Meter (VU) applet (#993) The S-Meter section can now be detached into a floating window and re-docked exactly like all other applets — using the same FloatingAppletWindow infrastructure, AppSettings geometry persistence, and PR #916 / #985 bug fixes (drift, WM centering, shutdown race). ## What changed ### src/gui/AppletPanel.h - Added `QWidget* m_sMeterContent` — the floatable content container (SMeterWidget + TX/RX select row + Peak Hold row), analogous to the applet widget held at layout index 1 in every other wrapper. - Added private `floatSMeter()` and `dockSMeter()` methods. ### src/gui/AppletPanel.cpp **S-Meter construction:** - `AppletTitleBar("S-Meter", "VU", this)` — passes the panel pointer so the right-click context menu ("⬈ Pop out" / "↩ Dock") is active. - Introduced `m_sMeterContent` QWidget with its own QVBoxLayout as layout index 1 of `m_sMeterSection` (after the title bar), so the extractor logic in `floatSMeter()` / `dockSMeter()` is identical to every other applet. **ANLG toggle button:** - Updated to the float-aware pattern used by TUN / AMP / AG: raises/hides the floating window when VU is floating, re-floats via `QTimer::singleShot(0)` when IsFloating is persisted but no window is open yet (toggle-off → toggle-on cycle fix from #985). **`isAppletFloating()`:** - Added early return for `"VU"`: checks `m_floatingWindows.contains("VU")` directly (VU is not in `m_appletOrder`). **`floatApplet()` / `dockApplet()`:** - Added `if (id == "VU")` dispatch at the top of each, delegating to `floatSMeter()` / `dockSMeter()`. **`floatSMeter()` / `dockSMeter()`:** - Mirror `floatApplet()` / `dockApplet()` exactly: reparent content out of/back into `m_sMeterSection`, create/destroy `FloatingAppletWindow`, persist `FloatingApplet_VU_IsFloating`, save geometry on dock. **Startup restore:** - After the existing `m_appletOrder` restore loop, added a `QTimer::singleShot(0)` restore for `"VU"` so the S-Meter reopens floating if it was floating when the app last closed. **dockSMeter() — use m_sMeterContent directly (agent review fix)** The original used a fragile layout-walk to recover m_sMeterContent from FloatingAppletWindow's internal layout. If any itemAt() returned null, appletWidget would be null while the code still called win->deleteLater(), destroying m_sMeterContent and all its children (m_sMeter, m_txSelect, m_rxSelect, peak-hold widgets) — crash on next signal-level update. Fix: use the named member directly. Reparent m_sMeterContent into m_sMeterSection before deleteLater() so Qt's parent-chain destruction cannot reach it. **.gitignore — drop Visual Studio artifacts (agent review fix)** /.vs and /out/install/x64-Debug are Windows VS IDE artifacts; they belong in ~/.gitignore_global, not the project-wide .gitignore. Closes #993 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add pop-out/pop-in support for the S-Meter (VU) applet (#993) The S-Meter section can now be detached into a floating window and re-docked exactly like all other applets — using the same FloatingAppletWindow infrastructure, AppSettings geometry persistence, and PR #916 / #985 bug fixes (drift, WM centering, shutdown race). ## What changed ### src/gui/AppletPanel.h - Added `QWidget* m_sMeterContent` — the floatable content container (SMeterWidget + TX/RX select row + Peak Hold row), analogous to the applet widget held at layout index 1 in every other wrapper. - Added private `floatSMeter()` and `dockSMeter()` methods. ### src/gui/AppletPanel.cpp **S-Meter construction:** - `AppletTitleBar("S-Meter", "VU", this)` — passes the panel pointer so the right-click context menu ("⬈ Pop out" / "↩ Dock") is active. - Introduced `m_sMeterContent` QWidget with its own QVBoxLayout as layout index 1 of `m_sMeterSection` (after the title bar), so the extractor logic in `floatSMeter()` / `dockSMeter()` is identical to every other applet. **ANLG toggle button:** - Updated to the float-aware pattern used by TUN / AMP / AG: raises/hides the floating window when VU is floating, re-floats via `QTimer::singleShot(0)` when IsFloating is persisted but no window is open yet (toggle-off → toggle-on cycle fix from #985). **`isAppletFloating()`:** - Added early return for `"VU"`: checks `m_floatingWindows.contains("VU")` directly (VU is not in `m_appletOrder`). **`floatApplet()` / `dockApplet()`:** - Added `if (id == "VU")` dispatch at the top of each, delegating to `floatSMeter()` / `dockSMeter()`. **`floatSMeter()` / `dockSMeter()`:** - Mirror `floatApplet()` / `dockApplet()` exactly: reparent content out of/back into `m_sMeterSection`, create/destroy `FloatingAppletWindow`, persist `FloatingApplet_VU_IsFloating`, save geometry on dock. **Startup restore:** - After the existing `m_appletOrder` restore loop, added a `QTimer::singleShot(0)` restore for `"VU"` so the S-Meter reopens floating if it was floating when the app last closed. **dockSMeter() — use m_sMeterContent directly (agent review fix)** The original used a fragile layout-walk to recover m_sMeterContent from FloatingAppletWindow's internal layout. If any itemAt() returned null, appletWidget would be null while the code still called win->deleteLater(), destroying m_sMeterContent and all its children (m_sMeter, m_txSelect, m_rxSelect, peak-hold widgets) — crash on next signal-level update. Fix: use the named member directly. Reparent m_sMeterContent into m_sMeterSection before deleteLater() so Qt's parent-chain destruction cannot reach it. **.gitignore — drop Visual Studio artifacts (agent review fix)** /.vs and /out/install/x64-Debug are Windows VS IDE artifacts; they belong in ~/.gitignore_global, not the project-wide .gitignore. Closes #993 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
NF0T
pushed a commit
that referenced
this pull request
Jun 5, 2026
…ettings key (#3358) (#3409) ## Problem \`ButtonBar/Layout\`, \`ButtonBar/DrawerOpen\`, and \`ButtonBar/Favorites\` all contain a \`/\`. \`AppSettings::save()\` silently drops any key not matching \`^[A-Za-z_][A-Za-z0-9_]*$\` — the validator was tightened in #985 to prevent invalid XML element names. The customisable button bar shipped in #3150 *after* that tightening, so its layout, drawer state, and favourites have **never round-tripped to disk for any user**. ## Fix Rename the three keys to \`ButtonBarLayout\`, \`ButtonBarDrawerOpen\`, \`ButtonBarFavorites\`. No migration needed — nothing was ever written to the old slash-containing keys, so there is no on-disk state to convert. ## Testing Verified locally on macOS (Apple Silicon, dev build off \`main\`): - Before fix: \`ButtonBar/Layout\` and \`ButtonBar/DrawerOpen\` absent from \`AetherSDR.settings\` after every save (silently discarded) - After fix: \`ButtonBarLayout\` and \`ButtonBarDrawerOpen\` present in \`AetherSDR.settings\` with correct JSON values - Button bar order and drawer state survive app restart Fixes #3358
G6PWY-Chris
pushed a commit
to G6PWY-Chris/AetherSDR
that referenced
this pull request
Jun 22, 2026
…ettings key (aethersdr#3358) (aethersdr#3409) ## Problem \`ButtonBar/Layout\`, \`ButtonBar/DrawerOpen\`, and \`ButtonBar/Favorites\` all contain a \`/\`. \`AppSettings::save()\` silently drops any key not matching \`^[A-Za-z_][A-Za-z0-9_]*$\` — the validator was tightened in aethersdr#985 to prevent invalid XML element names. The customisable button bar shipped in aethersdr#3150 *after* that tightening, so its layout, drawer state, and favourites have **never round-tripped to disk for any user**. ## Fix Rename the three keys to \`ButtonBarLayout\`, \`ButtonBarDrawerOpen\`, \`ButtonBarFavorites\`. No migration needed — nothing was ever written to the old slash-containing keys, so there is no on-disk state to convert. ## Testing Verified locally on macOS (Apple Silicon, dev build off \`main\`): - Before fix: \`ButtonBar/Layout\` and \`ButtonBar/DrawerOpen\` absent from \`AetherSDR.settings\` after every save (silently discarded) - After fix: \`ButtonBarLayout\` and \`ButtonBarDrawerOpen\` present in \`AetherSDR.settings\` with correct JSON values - Button bar order and drawer state survive app restart Fixes aethersdr#3358
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix floating applets losing float state and position across restarts (#959)
Four distinct bugs in the floating applet save/restore cycle, all fixed.
Bug 1 — Float state lost on toggle off/on (#959)
When a floating applet was toggled off and back on it reappeared docked.
Root cause: toggle button handlers and setTunerVisible/setAmpVisible/
setAgVisible re-enable paths did not check IsFloating from AppSettings
when no floating window was present. The bare QWidget::setVisible()
connections for TUN/AMP/AG were replaced with float-aware lambdas. All
toggle handlers now call floatApplet() via QTimer::singleShot(0) when
IsFloating is True, matching the startup restore logic.
setTunerVisible/setAmpVisible/setAgVisible also apply the same check on
the visible=true path (device reconnect). On the visible=false path they
now call hideAndSave() instead of dockApplet(), preserving IsFloating so
the window returns floating on reconnect.
Bug 2 — Window drift on every hide/show cycle
saveGeometry() was saving geometry().x()/y() (client-area origin) and
restoring with move() (frame origin). The decoration height accumulated
on every cycle, drifting the window down one title-bar height per show.
Fix: save pos() for x/y and geometry().size() for w/h.
Bug 3 — WM centering overwriting saved position on restore
On show(), the WM re-centers the window and fires moveEvent, starting
the 400ms debounce timer. restoreGeometry() at 200ms moved the window
correctly, but the WM's ConfigureNotify for that move arrived after the
guard cleared at 500ms, restarting the debounce and saving the centered
position. Three-part fix:
m_restoringGeometry=true before show(), clears at 650ms (200ms delay
showAndRestore's lifetime and clearing it early.
timer; dockApplet() uses hideAndSave() to stop the debounce timer
before hiding so a pending callback cannot fire on a hidden window.
Bug 4 — Float state and positions lost on app restart (Windows)
Three separate causes:
a) Shutdown race (all platforms)
On Windows, the OS sends WM_CLOSE to all owned tool windows when the
main window closes. Whichever floating windows received their close event
before AppletPanel became invisible triggered the "user docked it" branch
of closeEvent, writing IsFloating=False. Timing was non-deterministic so
different applets failed on different exits.
Fix: FloatingAppletWindow connects to QCoreApplication::aboutToQuit and
sets m_appShuttingDown=true. closeEvent checks this flag and takes the
"save and accept" branch regardless of parent visibility, so dockRequested
is never emitted during shutdown.
b) P/CW applet ID contains '/' — invalid XML element name
AppSettings::save() writes keys as XML element names. The key
"FloatingApplet_P/CW_IsFloating" is not valid XML. The validKey regex
incorrectly allowed '/' so the malformed element was written to the temp
file. The post-write XML validator caught it and aborted the entire save —
meaning ALL settings changes in any session where P/CW was floating were
silently discarded (positions, IsFloating, frequency, etc.).
Fix: validKey regex tightened to ^[A-Za-z_][A-Za-z0-9_]$ (no '/' or '.').
A settingsPrefix() helper in FloatingAppletWindow and a floatKey() helper
in AppletPanel sanitize '/' to '' before forming any settings key, so
"P/CW" maps to "FloatingApplet_P_CW" keys throughout.
c) Geometry not saved on shutdown
saveGeometry() was only called from the debounce timer and dockApplet().
On app exit the windows are destroyed without an explicit dock, so the
final position was never written.
Fix: saveGeometry() is now called explicitly in hideAndSave() (before
hide()), in both branches of closeEvent, and in hideEvent as a
belt-and-suspenders backup for Linux-style parent-cascade hides. This
ensures geometry is captured from a valid window state on every platform
regardless of which shutdown path the WM takes.
NOTE: S-Meter applet is still not fixed.. Lower priority to fix the above mentioned key underlying bugs: this was very difficult to chase down on Windows.
Fixes #959