Skip to content

Add poppable applets — float/dock with persistent geometry#916

Merged
ten9876 merged 1 commit into
aethersdr:mainfrom
chibondking:feature/poppable-applets
Apr 7, 2026
Merged

Add poppable applets — float/dock with persistent geometry#916
ten9876 merged 1 commit into
aethersdr:mainfrom
chibondking:feature/poppable-applets

Conversation

@chibondking

Copy link
Copy Markdown
Collaborator

Each applet in the AppletPanel can now be detached into its own floating window and re-docked at any time. Position and size persist across sessions and survive dock/undock cycles.

What's new

FloatingAppletWindow (new class, src/gui/FloatingAppletWindow.h/.cpp)

  • Qt::Window | Qt::Tool widget hosting a single detached applet
  • Custom title bar with "↩ Dock" button; closing the window re-docks rather than losing the applet
  • Geometry saved to AppSettings using screen name + screen-relative coordinates (more resilient than QWidget::saveGeometry() binary blob): FloatingApplet_Screen — screen name, stable across reboots
    FloatingApplet
    X/Y — screen-relative position
    FloatingApplet
    _W/H — window size
  • 400 ms debounce timer saves geometry on every resize/move without
    hammering AppSettings on each pixel
  • Explicit save on dock and on app shutdown (main-window close path)
  • Wayland guard: position restore is skipped on Wayland compositors that
    own window placement; size is always restored
  • Off-screen clamp: if the saved monitor is disconnected, the window is
    clamped to the nearest available screen geometry

AppletPanel changes

  • AppletEntry gains a floating bool
  • floatApplet(id): reparents applet widget into FloatingAppletWindow, hides panel wrapper, persists IsFloating = True
  • dockApplet(id): reparents applet widget back into wrapper, destroys floating window, persists IsFloating = False
  • isAppletFloating(id): public helper used by AppletTitleBar context menu
  • AppletTitleBar: right-click context menu ("Pop out" / "↩ Dock") using QMenu::popup() — no nested event loop
  • Toggle button while floating: raises/hides the floating window instead of the docked wrapper
  • Floating state restored at startup via QTimer::singleShot(0) so the window system is ready before windows are shown
  • setTunerVisible(false), setAmpVisible(false), setAgVisible(false) each call dockApplet() first to prevent orphaned floating windows when a conditional device (TGXL, amp, Antenna Genius) disconnects
  • FloatingAppletWindow created with AppletPanel as Qt parent so Qt's shouldQuit() correctly ignores tool windows when the last real window closes — fixes app not exiting when floating windows were open
  • show() before restoreGeometry() (with 50 ms singleShot delay) so the window manager maps the window before position hints are applied

CMakeLists.txt

  • Added src/gui/FloatingAppletWindow.cpp to GUI_SOURCES

Cross-platform notes

  • Windows / macOS / Linux X11: full position + size persistence
  • Linux Wayland / WSL2 (WSLg): size persists; position is compositor- controlled. Use QT_QPA_PLATFORM=xcb on WSL2 for full behaviour via XWayland.

This possibly could address #811 - Not the entire panel, but each individual Applet. "Popped-out" applets are still displayed even if the side control panel is visible/not visible.

Testing Notes — Poppable Applets

Setup

  • Build and launch AetherSDR connected to a supported radio
  • Secondary monitor recommended (not required) for multi-monitor tests

Core Float / Dock

# Steps Expected WT2P Tested (locally)
1 Right-click any applet title bar (e.g. RX Controls) Context menu shows "⬆ Pop out"
2 Click "Pop out" Applet detaches into its own floating window with a custom title bar and "↩ Dock" button; panel wrapper is hidden
3 Click "↩ Dock" in the floating window Applet returns to the panel at its original position; floating window is destroyed
4 Float an applet, then right-click its title bar again Context menu shows "↩ Dock" instead of "Pop out"
5 Float an applet, then click X on the floating window Applet re-docks (does not disappear) — same result as clicking "↩ Dock"
6 Repeat for all 10 applets: RX, TUN, AMP, TX, PHNE, P/CW, EQ, DIGI, MTR, AG Each floats and docks cleanly ❌ (VU Meter, will address in later version), ✅ All Other applets

Toggle Button Behaviour While Floating

# Steps Expected WT2P Tested (locally)
7 Float an applet, then click its toggle button in the panel (button is checked) Floating window raises to front — does not re-dock N/A
8 While floating, uncheck the toggle button Floating window hides N/A
9 Re-check the toggle button Floating window becomes visible again N/A

Geometry Persistence

# Steps Expected WT2P Tested (locally)
10 Pop out RX Controls, drag to a specific position and resize it, wait ~500 ms, close and relaunch Window restores to the same position and size
11 Pop out two applets to different positions, close app, relaunch Both restore independently to their saved positions
12 Pop out an applet, dock it, relaunch Applet appears docked (floating state cleared)
13 Multi-monitor: drag a floating applet to a second monitor, close app, relaunch Window restores on the correct monitor ⚠️ Not fully tested. There may be issues.
14 Disconnected monitor: save geometry on monitor 2, disconnect it, relaunch Window appears on the primary screen — not outside visible area ⚠️ Not Tested

App Shutdown With Floating Windows

# Steps Expected WT2P Tested (locally)
15 Float one or more applets, close main window via X App exits fully; no zombie windows remain
16 Float one or more applets, use File → Quit Same — clean exit
17 Float applets, close app, relaunch Applets restore to floating state automatically

Drag Reorder Compatibility

# Steps Expected WT2P Tested (locally)
18 With some applets docked and some floating, drag-reorder a docked applet Docked applets reorder normally; floating applets are unaffected
19 View → Reset Applet Order Docked applets reset to default order; floating windows stay where they are

Conditional Applets (TUN / AMP / AG)

# Steps Expected WT2P Tested (locally)
20 Float the Tuner applet while a tuner is connected, then disconnect the tuner Tuner applet auto-docks before the button is hidden; no orphaned floating window ⚠️ Not Tested
21 Float the Amplifier applet, then disconnect the amp Same — auto-docks cleanly ⚠️ Not Tested
22 Float the Antenna Genius applet, then disconnect the AG Same ⚠️ Not Tested

** These were not tested due to WT2P hardware version not supporting it (3.8.19)

RX / TX Functionality While Floating

# Steps Expected WT2P Tested (locally)
23 Float RX Controls, change mode / filter / AGC Radio responds normally; all controls functional
24 Float TX Controls, key the radio TX indicator lights; floating applet reflects transmit state
25 Float EQ applet, adjust bands Audio changes in real time

Edge Cases

# Steps Expected WT2P Tested (locally)
26 Float all 10 reorderable applets simultaneously All float independently; panel scroll area empties; app remains stable ⚠️ Not Tested
27 Float an applet, minimise the main window Floating window follows OS conventions for Qt::Tool windows
28 Float an applet and immediately dock it before the 50 ms restore timer fires No crash; geometry restored from previous session next launch ⚠️ Untestable

Platform Notes

Platform Position persistence Size persistence
Windows ✅ Full ✅ Full
Linux X11 (KDE / GNOME) ✅ Full ✅ Full
Linux Wayland ⚠️ Compositor-controlled — position may not restore ✅ Full
WSL2 (WSLg) ⚠️ Set QT_QPA_PLATFORM=xcb for full position restore ✅ Full
macOS ✅ Full ✅ Full

Each applet in the AppletPanel can now be detached into its own floating
window and re-docked at any time. Position and size persist across sessions
and survive dock/undock cycles.

## What's new

**FloatingAppletWindow** (new class, src/gui/FloatingAppletWindow.h/.cpp)
- Qt::Window | Qt::Tool widget hosting a single detached applet
- Custom title bar with "↩ Dock" button; closing the window re-docks
  rather than losing the applet
- Geometry saved to AppSettings using screen name + screen-relative
  coordinates (more resilient than QWidget::saveGeometry() binary blob):
    FloatingApplet_<ID>_Screen  — screen name, stable across reboots
    FloatingApplet_<ID>_X/Y     — screen-relative position
    FloatingApplet_<ID>_W/H     — window size
- 400 ms debounce timer saves geometry on every resize/move without
  hammering AppSettings on each pixel
- Explicit save on dock and on app shutdown (main-window close path)
- Wayland guard: position restore is skipped on Wayland compositors that
  own window placement; size is always restored
- Off-screen clamp: if the saved monitor is disconnected, the window is
  clamped to the nearest available screen geometry

**AppletPanel changes**
- AppletEntry gains a `floating` bool
- floatApplet(id): reparents applet widget into FloatingAppletWindow,
  hides panel wrapper, persists IsFloating = True
- dockApplet(id): reparents applet widget back into wrapper,
  destroys floating window, persists IsFloating = False
- isAppletFloating(id): public helper used by AppletTitleBar context menu
- AppletTitleBar: right-click context menu ("Pop out" / "↩ Dock") using
  QMenu::popup() — no nested event loop
- Toggle button while floating: raises/hides the floating window instead
  of the docked wrapper
- Floating state restored at startup via QTimer::singleShot(0) so the
  window system is ready before windows are shown
- setTunerVisible(false), setAmpVisible(false), setAgVisible(false) each
  call dockApplet() first to prevent orphaned floating windows when a
  conditional device (TGXL, amp, Antenna Genius) disconnects
- FloatingAppletWindow created with AppletPanel as Qt parent so Qt's
  shouldQuit() correctly ignores tool windows when the last real window
  closes — fixes app not exiting when floating windows were open
- show() before restoreGeometry() (with 50 ms singleShot delay) so the
  window manager maps the window before position hints are applied

**CMakeLists.txt**
- Added src/gui/FloatingAppletWindow.cpp to GUI_SOURCES

## Cross-platform notes
- Windows / macOS / Linux X11: full position + size persistence
- Linux Wayland / WSL2 (WSLg): size persists; position is compositor-
  controlled. Use QT_QPA_PLATFORM=xcb on WSL2 for full behaviour via
  XWayland.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@chibondking chibondking requested a review from ten9876 as a code owner April 7, 2026 05:42
@ten9876 ten9876 merged commit c211b0b into aethersdr:main Apr 7, 2026
5 checks passed
@aethersdr-agent aethersdr-agent Bot mentioned this pull request Apr 7, 2026
2 tasks
AetherClaude added a commit to AetherClaude/AetherSDR that referenced this pull request Apr 7, 2026
Right-click a panadapter title bar or click the arrow button to
pop out a panadapter into its own floating window. Dock returns it to
the PanadapterStack splitter at its original position.

- Add popOutRequested/dockRequested signals and pop-out button to
  PanadapterApplet title bar with context menu support
- Add floatPanadapter/dockPanadapter/dockAll methods to PanadapterStack
  using FloatingAppletWindow (reused from PR aethersdr#916)
- Auto-dock all floating pans before layout rearrange or pan removal
- Persist float state per-pan via AppSettings; restore on reconnect
- Splitter re-insertion tracks original index for correct dock position

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
boydsoftprez pushed a commit to boydsoftprez/AetherSDR that referenced this pull request Apr 7, 2026
…#916)

Each applet in the AppletPanel can now be detached into its own floating
window and re-docked at any time. Position and size persist across sessions
and survive dock/undock cycles.

## What's new

**FloatingAppletWindow** (new class, src/gui/FloatingAppletWindow.h/.cpp)
- Qt::Window | Qt::Tool widget hosting a single detached applet
- Custom title bar with "↩ Dock" button; closing the window re-docks
  rather than losing the applet
- Geometry saved to AppSettings using screen name + screen-relative
  coordinates (more resilient than QWidget::saveGeometry() binary blob):
    FloatingApplet_<ID>_Screen  — screen name, stable across reboots
    FloatingApplet_<ID>_X/Y     — screen-relative position
    FloatingApplet_<ID>_W/H     — window size
- 400 ms debounce timer saves geometry on every resize/move without
  hammering AppSettings on each pixel
- Explicit save on dock and on app shutdown (main-window close path)
- Wayland guard: position restore is skipped on Wayland compositors that
  own window placement; size is always restored
- Off-screen clamp: if the saved monitor is disconnected, the window is
  clamped to the nearest available screen geometry

**AppletPanel changes**
- AppletEntry gains a `floating` bool
- floatApplet(id): reparents applet widget into FloatingAppletWindow,
  hides panel wrapper, persists IsFloating = True
- dockApplet(id): reparents applet widget back into wrapper,
  destroys floating window, persists IsFloating = False
- isAppletFloating(id): public helper used by AppletTitleBar context menu
- AppletTitleBar: right-click context menu ("Pop out" / "↩ Dock") using
  QMenu::popup() — no nested event loop
- Toggle button while floating: raises/hides the floating window instead
  of the docked wrapper
- Floating state restored at startup via QTimer::singleShot(0) so the
  window system is ready before windows are shown
- setTunerVisible(false), setAmpVisible(false), setAgVisible(false) each
  call dockApplet() first to prevent orphaned floating windows when a
  conditional device (TGXL, amp, Antenna Genius) disconnects
- FloatingAppletWindow created with AppletPanel as Qt parent so Qt's
  shouldQuit() correctly ignores tool windows when the last real window
  closes — fixes app not exiting when floating windows were open
- show() before restoreGeometry() (with 50 ms singleShot delay) so the
  window manager maps the window before position hints are applied

**CMakeLists.txt**
- Added src/gui/FloatingAppletWindow.cpp to GUI_SOURCES

## Cross-platform notes
- Windows / macOS / Linux X11: full position + size persistence
- Linux Wayland / WSL2 (WSLg): size persists; position is compositor-
  controlled. Use QT_QPA_PLATFORM=xcb on WSL2 for full behaviour via
  XWayland.

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

2 participants