diff --git a/src/ui/ControlsPanel.cpp b/src/ui/ControlsPanel.cpp index 219433b..7de9ed8 100644 --- a/src/ui/ControlsPanel.cpp +++ b/src/ui/ControlsPanel.cpp @@ -1,5 +1,6 @@ #include "ControlsPanel.h" +#include #include #include #include @@ -21,6 +22,20 @@ const int kRefreshMs = 50; // see the class comment on why this polls // Separates the heading's name from its detail, e.g. "BUILD MODE * Assembler". const QChar kHeadingSeparator(0x00B7); // U+00B7 MIDDLE DOT +// The upper-case heading and caption are tracked out a little so they read as labels +// rather than as words. Set on the font because Qt's stylesheets have no letter-spacing +// property -- writing one there is silently ignored apart from a warning per widget. +QFont makeSpacedFont(QFont font, bool bold, int pointSizeDelta) +{ + font.setBold(bold); + if (pointSizeDelta != 0 && font.pointSize() > 0) + { + font.setPointSize(qMax(1, font.pointSize() + pointSizeDelta)); + } + font.setLetterSpacing(QFont::AbsoluteSpacing, 1.0); + return font; +} + // One row: the chips for the bindings, then what the action is called. Built as a plain // widget rather than a class of its own -- it holds no state and answers no questions. QWidget* makeRow(ControlAction action, const ControlContext& context, QWidget* parent) @@ -63,19 +78,20 @@ ControlsPanel::ControlsPanel(const GameWorldView* view, QWidget* parent) // class scoped selector keeps the border on the panel rather than cascading onto // its children. setAttribute(Qt::WA_StyledBackground, true); + // Letter spacing is deliberately absent here: Qt's stylesheet syntax has no such + // property and warns on every widget it is applied to. The heading and the caption + // set it on their QFont instead. setStyleSheet(QStringLiteral( "ControlsPanel { background-color: palette(window);" " border: 1px solid palette(mid); border-radius: 4px; }" - "QLabel#controlHeading { font-weight: bold; letter-spacing: 1px;" - " color: palette(text); }" + "QLabel#controlHeading { color: palette(text); }" "QLabel#controlBadge { border: 1px solid palette(mid); border-radius: 3px;" " padding: 1px 5px; font-family: monospace; color: palette(text); }" "QLabel#controlLabel { color: palette(text); }" // The row that leaves the mode reads differently from the ones that act within // it (REQ-UI-CONTROLS-CARD). "QLabel#controlLabelExit { color: palette(bright-text); }" - "QLabel#controlCaption { color: palette(mid); font-size: 10px;" - " letter-spacing: 1px; }")); + "QLabel#controlCaption { color: palette(mid); }")); QVBoxLayout* outerLayout = new QVBoxLayout(this); outerLayout->setContentsMargins(kCardMarginPx, kCardMarginPx, @@ -85,6 +101,7 @@ ControlsPanel::ControlsPanel(const GameWorldView* view, QWidget* parent) m_heading = new QLabel(this); m_heading->setObjectName(QStringLiteral("controlHeading")); m_heading->setCursor(Qt::PointingHandCursor); + m_heading->setFont(makeSpacedFont(font(), /*bold*/ true, /*pointSizeDelta*/ 0)); outerLayout->addWidget(m_heading); m_rows = new QWidget(this); @@ -172,6 +189,7 @@ void ControlsPanel::rebuild(const ControlContext& context) 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); } @@ -221,8 +239,19 @@ void ControlsPanel::refit() { if (m_bandRect.isNull()) { return; } - // The layout drops hidden widgets from its size hint, but only once it has been - // re-run: collapsing hides the rows before Qt would get around to it on its own. + // Both layouts, innermost first, and both invalidated before being re-run. Rows are + // created and destroyed wholesale, so the rows layout's cached size hint describes + // the previous context until it is discarded; asking the panel for its hint without + // this returns the size the last context needed, which is why a first selection + // used to draw a card one rebuild behind. + // + // The polish is part of the same problem: a freshly created label reports an + // unstyled size hint until the stylesheet has been applied to it, and the badge + // chips carry border and padding that change their width. + m_rows->ensurePolished(); + m_rowsLayout->invalidate(); + m_rowsLayout->activate(); + layout()->invalidate(); layout()->activate(); const QRect band = m_bandRect.adjusted(kMarginPx, kMarginPx, -kMarginPx, -kMarginPx);