Skip to content

fix(vfo): fix QStackedWidget cross-tab height inflation via TabStack subclass#2821

Open
NF0T wants to merge 1 commit into
aethersdr:mainfrom
NF0T:pr/vfo-filtercontainer-fixed-height
Open

fix(vfo): fix QStackedWidget cross-tab height inflation via TabStack subclass#2821
NF0T wants to merge 1 commit into
aethersdr:mainfrom
NF0T:pr/vfo-filtercontainer-fixed-height

Conversation

@NF0T
Copy link
Copy Markdown
Collaborator

@NF0T NF0T commented May 17, 2026

Summary

Fixes the recurring gap between the mode-selection row and filter-preset buttons visible when the Mode tab is open in DIGU or DIGL mode, particularly after switching tabs, changing bands, activating RADE, or starting with RADE as the last-used mode.

Root cause

QStackedLayout::sizeHint() returns the maximum height across all pages, not the current page's height. When the DSP tab is taller than the Mode tab — which occurs in DIGU and DIGL mode because m_digContainer (the DIGU offset control row) is visible, adding ~41 px — VfoWidget::adjustSize() over-allocates that height to m_tabStack. QStackedWidget hands the full allocation to the currently-displayed Mode tab page. The Mode tab's internal VBoxLayout distributes the surplus into filterContainer (which has QSizePolicy::Preferred and no fixed height), producing the gap between the mode buttons and the filter preset buttons.

Why the previous fix was insufficient

An earlier attempt patched showTab() with a per-page setSizePolicy(Ignored, Ignored) loop. This does not affect QStackedLayout::sizeHint() — that function calls widget->sizeHint() on each page directly, bypassing each page's size policy. The loop appeared to work because initial testing was performed in RADE (FDVU) mode, where m_digContainer is hidden (isDig is false for FDV-family modes), making both tabs equal height. The bug was not structurally present in that test. It reproduced immediately under DIGU/DIGL mode and on any resize path that bypasses showTab():

Trigger Code location
Radio reports mode change while tab open modeChanged lambda, line 2619
RADE activated / deactivated setRadeActive(), line 4024
Diversity button toggled divBtn toggled, line 1023
Radio reports diversity state change diversityChanged signal, line 2664
ESC panel visibility change setDiversityAllowed(), line 2061

Fix

Introduce TabStack, a QStackedWidget subclass that overrides sizeHint() and minimumSizeHint() to delegate to currentWidget() rather than the QStackedLayout maximum. The subclass is defined locally in VfoWidget.cpp; no header changes are required (m_tabStack remains QStackedWidget* and the override dispatches polymorphically).

The now-ineffective sizePolicy loop in showTab() is removed. All existing adjustSize() and resize(sizeHint()) call sites throughout the widget are correct without modification.

Test plan

  • Open Mode tab in DIGU mode — confirm no gap between mode-row and filter buttons
  • While Mode tab is open, switch mode to USB then back to DIGU — confirm gap does not reappear
  • Open DSP tab, then open Mode tab — confirm correct height
  • Activate RADE while Mode tab is open — confirm no layout shift
  • Start AetherSDR with RADE as last-used mode, open Mode tab — confirm correct layout
  • Perform a band change while Mode tab is open — confirm correct height after change
  • Collapse and re-expand the VfoWidget, open Mode tab — confirm correct layout

🤖 Generated with Claude Code

@NF0T NF0T requested review from jensenpat and ten9876 as code owners May 17, 2026 20:16
Copy link
Copy Markdown
Contributor

@aethersdr-agent aethersdr-agent Bot left a comment

Choose a reason for hiding this comment

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

Clean, minimal fix. The TabStack subclass is the correct approach — sizeHint() and minimumSizeHint() are virtual on QWidget, so polymorphic dispatch works even though m_tabStack remains typed as QStackedWidget* in the header. The null-guard against currentWidget() is appropriate. The class is placed at file scope alongside the existing TriBtn helper, matching local convention.

Root-cause analysis in the PR body is excellent — explaining why the previous setSizePolicy(Ignored, Ignored) loop couldn't possibly work (because QStackedLayout::sizeHint() calls widget->sizeHint() directly, bypassing the page's size policy) is the kind of write-up that makes this trivially reviewable.

One minor description nit: the body says "the now-ineffective sizePolicy loop in showTab() is removed," but the diff doesn't contain that removal — showTab() on main is already clean (no sizePolicy loop). Likely just a stale sentence in the description; no code action needed.

Thanks for the thorough fix and writeup, @NF0T.

…subclass

QStackedLayout::sizeHint() returns the maximum height across all pages,
not the current page's height. When the DSP tab is taller than the Mode
tab (m_digContainer visible in DIGU/DIGL mode, ~41 px difference),
VfoWidget::adjustSize() over-allocates height to m_tabStack, which
QStackedWidget passes entirely to the Mode tab page. The Mode tab's
VBoxLayout distributes the surplus into filterContainer (Preferred
policy), producing a gap between the mode-row and filter-preset buttons.

The previous attempt patched showTab() with a per-page setSizePolicy()
loop, which does not affect QStackedLayout::sizeHint() — that function
calls widget->sizeHint() on each page directly, bypassing size policy.
The loop appeared to work only because initial testing used RADE (FDVU)
mode, where m_digContainer is hidden, making both tabs the same height.
The bug reproduced immediately in DIGU/DIGL mode and on any resize path
that does not go through showTab(): modeChanged signal, setRadeActive(),
diversity callbacks, and band-change-triggered syncFromSlice().

Fix: introduce TabStack, a QStackedWidget subclass that overrides
sizeHint() and minimumSizeHint() to delegate to currentWidget() instead
of the QStackedLayout maximum. Every existing adjustSize() /
resize(sizeHint()) call site is now correct without modification.
The sizePolicy loop in showTab() is removed as it was both ineffective
and misleading.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@NF0T NF0T force-pushed the pr/vfo-filtercontainer-fixed-height branch from f44bc94 to 0735e75 Compare May 18, 2026 23:10
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.

1 participant