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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
This commit is contained in:
2026-08-07 19:36:24 +02:00
parent 1604dc02a2
commit d7c6734a2b

View File

@@ -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);