Skip to content

Feature/vu meter popout#1154

Merged
ten9876 merged 6 commits into
aethersdr:mainfrom
chibondking:feature/vu-meter-popout
Apr 11, 2026
Merged

Feature/vu meter popout#1154
ten9876 merged 6 commits into
aethersdr:mainfrom
chibondking:feature/vu-meter-popout

Conversation

@chibondking

Copy link
Copy Markdown
Collaborator

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:

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

chibondking and others added 4 commits April 8, 2026 02:14
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 chibondking requested a review from ten9876 as a code owner April 10, 2026 23:59

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this, @chibondking — the overall architecture is sound. Using the same FloatingAppletWindow infrastructure, AppSettings geometry persistence, and AetherSDR::floatKey() convention keeps VU consistent with every other applet. Two issues to address before merge:


1. dockSMeter() — fragile layout-walk can crash on failure

The function navigates FloatingAppletWindow's internal layout structure to recover m_sMeterContent, but m_sMeterContent is a named member of AppletPanel — there is no reason to go looking for it through the window's layout.

If the walk fails (e.g. FloatingAppletWindow's layout shape changes in a future refactor, or itemAt returns null at any level), appletWidget stays null and the code still reaches:

win->deleteLater();
m_floatingWindows.remove("VU");
m_sMeterSection->show();

At that point m_sMeterContent (and all its children — m_sMeter, m_txSelect, m_rxSelect, the peak-hold widgets) were parented inside the window, so deleteLater() destroys them. The dangling member pointers cause a crash the next time any of them are accessed (signal level update, TX/RX mode change, etc.).

Suggested fix — use the member directly:

void AppletPanel::dockSMeter()
{
    if (!m_floatingWindows.contains("VU")) { return; }
    FloatingAppletWindow* win = m_floatingWindows.take("VU");

    // Re-insert content before deleting the window
    if (auto* wl = qobject_cast<QVBoxLayout*>(m_sMeterSection->layout())) {
        m_sMeterContent->setParent(m_sMeterSection);
        wl->addWidget(m_sMeterContent);
        m_sMeterContent->show();
    }

    if (win) { win->hideAndSave(); win->deleteLater(); }
    m_sMeterSection->show();

    AppSettings::instance().setValue(AetherSDR::floatKey("VU"), "False");
    AppSettings::instance().save();
}

Note the reparent-before-delete order: re-parent m_sMeterContent into m_sMeterSection first, then delete the window, so Qt's parent-chain destruction can't reach the widget.


2. .gitignore — Visual Studio artifacts are out of scope

+/.vs
+/out/install/x64-Debug

AetherSDR targets Linux (Qt6 + CMake). .vs/ and out/install/x64-Debug/ are Windows Visual Studio IDE artifacts. These belong in a user-level ~/.gitignore_global, not the project-wide .gitignore. Please drop these two lines or submit them as a separate PR if there's a plan to add Windows support.


Everything else looks good — the isAppletFloating("VU") early return, the floatApplet/dockApplet dispatch guards, the deferred QTimer::singleShot(0) restore at startup, and the ANLG button float-awareness all follow the same patterns as the other applets correctly.

chibondking and others added 2 commits April 10, 2026 20:39
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>
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

Copy link
Copy Markdown
Collaborator Author

Suggestions implemented.

@ten9876 ten9876 merged commit 4f4feee into aethersdr:main Apr 11, 2026
5 checks passed
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.

Top applet S Meter cannot be popped out - previously reported

2 participants