From d7c6734a2b281e12f93d047182e78dc235f61a2c Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Fri, 7 Aug 2026 19:36:24 +0200 Subject: [PATCH] show the controls panel's rows before measuring them A widget created under an already-visible parent starts hidden, and a layout counts a hidden item as empty -- it adds nothing to the size hint. Nothing showed the freshly built rows until the event loop next ran, long after the panel had measured itself, so every rebuild measured an empty card. That one fact explains both symptoms. Originally refit() read a stale cached hint, which described the previous context and so drew the card one rebuild behind. Invalidating that cache to fix it replaced a plausible wrong answer with the true one for a card whose rows were all still hidden, which is why the panel then collapsed to its heading on every mode change. Measured on a forced rebuild, with the rows shown and without: shown: rowItems=10 hidden=0 rowsHint=188 needed=140x221 hidden: rowItems=10 hidden=10 rowsHint=6 needed=72x39 39px being heading plus margins -- the collapsed card exactly. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG --- src/ui/ControlsPanel.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/ui/ControlsPanel.cpp b/src/ui/ControlsPanel.cpp index 78f8898..9cea8a2 100644 --- a/src/ui/ControlsPanel.cpp +++ b/src/ui/ControlsPanel.cpp @@ -65,6 +65,18 @@ QWidget* makeRow(ControlAction action, const ControlContext& context, QWidget* p return row; } +// Adds a freshly built widget to the rows and shows it. +// +// The show is what makes it count. A widget created under an already-visible parent +// starts hidden, and a layout treats a hidden item as empty -- it contributes nothing +// to the size hint until something shows it, which otherwise does not happen until the +// event loop next runs, long after the panel has measured itself. +void addAndShow(QVBoxLayout* layout, QWidget* widget) +{ + layout->addWidget(widget); + widget->show(); +} + } // namespace ControlsPanel::ControlsPanel(const GameWorldView* view, QWidget* parent) @@ -173,7 +185,7 @@ void ControlsPanel::rebuild(const ControlContext& context) for (ControlAction action : m_shownContextActions) { - m_rowsLayout->addWidget(makeRow(action, context, m_rows)); + addAndShow(m_rowsLayout, makeRow(action, context, m_rows)); } // The always-available block sits under a divider in every context, the General one @@ -185,17 +197,17 @@ void ControlsPanel::rebuild(const ControlContext& context) divider->setFrameShape(QFrame::HLine); divider->setFrameShadow(QFrame::Plain); m_rowsLayout->addSpacing(6); - m_rowsLayout->addWidget(divider); + addAndShow(m_rowsLayout, divider); QLabel* caption = new QLabel(tr("ALWAYS AVAILABLE"), m_rows); caption->setObjectName(QStringLiteral("controlCaption")); caption->setFont(makeSpacedFont(font(), /*bold*/ false, /*pointSizeDelta*/ -1)); - m_rowsLayout->addWidget(caption); + addAndShow(m_rowsLayout, caption); } for (ControlAction action : m_shownAlwaysActions) { - m_rowsLayout->addWidget(makeRow(action, context, m_rows)); + addAndShow(m_rowsLayout, makeRow(action, context, m_rows)); } m_rows->setVisible(!m_collapsed);