size the controls panel to the rows it is actually showing
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#include "ControlsPanel.h"
|
#include "ControlsPanel.h"
|
||||||
|
|
||||||
|
#include <QFont>
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
@@ -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".
|
// Separates the heading's name from its detail, e.g. "BUILD MODE * Assembler".
|
||||||
const QChar kHeadingSeparator(0x00B7); // U+00B7 MIDDLE DOT
|
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
|
// 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.
|
// 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)
|
QWidget* makeRow(ControlAction action, const ControlContext& context, QWidget* parent)
|
||||||
@@ -50,6 +65,18 @@ QWidget* makeRow(ControlAction action, const ControlContext& context, QWidget* p
|
|||||||
return row;
|
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
|
} // namespace
|
||||||
|
|
||||||
ControlsPanel::ControlsPanel(const GameWorldView* view, QWidget* parent)
|
ControlsPanel::ControlsPanel(const GameWorldView* view, QWidget* parent)
|
||||||
@@ -63,19 +90,20 @@ ControlsPanel::ControlsPanel(const GameWorldView* view, QWidget* parent)
|
|||||||
// class scoped selector keeps the border on the panel rather than cascading onto
|
// class scoped selector keeps the border on the panel rather than cascading onto
|
||||||
// its children.
|
// its children.
|
||||||
setAttribute(Qt::WA_StyledBackground, true);
|
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(
|
setStyleSheet(QStringLiteral(
|
||||||
"ControlsPanel { background-color: palette(window);"
|
"ControlsPanel { background-color: palette(window);"
|
||||||
" border: 1px solid palette(mid); border-radius: 4px; }"
|
" border: 1px solid palette(mid); border-radius: 4px; }"
|
||||||
"QLabel#controlHeading { font-weight: bold; letter-spacing: 1px;"
|
"QLabel#controlHeading { color: palette(text); }"
|
||||||
" color: palette(text); }"
|
|
||||||
"QLabel#controlBadge { border: 1px solid palette(mid); border-radius: 3px;"
|
"QLabel#controlBadge { border: 1px solid palette(mid); border-radius: 3px;"
|
||||||
" padding: 1px 5px; font-family: monospace; color: palette(text); }"
|
" padding: 1px 5px; font-family: monospace; color: palette(text); }"
|
||||||
"QLabel#controlLabel { color: palette(text); }"
|
"QLabel#controlLabel { color: palette(text); }"
|
||||||
// The row that leaves the mode reads differently from the ones that act within
|
// The row that leaves the mode reads differently from the ones that act within
|
||||||
// it (REQ-UI-CONTROLS-CARD).
|
// it (REQ-UI-CONTROLS-CARD).
|
||||||
"QLabel#controlLabelExit { color: palette(bright-text); }"
|
"QLabel#controlLabelExit { color: palette(bright-text); }"
|
||||||
"QLabel#controlCaption { color: palette(mid); font-size: 10px;"
|
"QLabel#controlCaption { color: palette(mid); }"));
|
||||||
" letter-spacing: 1px; }"));
|
|
||||||
|
|
||||||
QVBoxLayout* outerLayout = new QVBoxLayout(this);
|
QVBoxLayout* outerLayout = new QVBoxLayout(this);
|
||||||
outerLayout->setContentsMargins(kCardMarginPx, kCardMarginPx,
|
outerLayout->setContentsMargins(kCardMarginPx, kCardMarginPx,
|
||||||
@@ -85,6 +113,7 @@ ControlsPanel::ControlsPanel(const GameWorldView* view, QWidget* parent)
|
|||||||
m_heading = new QLabel(this);
|
m_heading = new QLabel(this);
|
||||||
m_heading->setObjectName(QStringLiteral("controlHeading"));
|
m_heading->setObjectName(QStringLiteral("controlHeading"));
|
||||||
m_heading->setCursor(Qt::PointingHandCursor);
|
m_heading->setCursor(Qt::PointingHandCursor);
|
||||||
|
m_heading->setFont(makeSpacedFont(font(), /*bold*/ true, /*pointSizeDelta*/ 0));
|
||||||
outerLayout->addWidget(m_heading);
|
outerLayout->addWidget(m_heading);
|
||||||
|
|
||||||
m_rows = new QWidget(this);
|
m_rows = new QWidget(this);
|
||||||
@@ -156,7 +185,7 @@ void ControlsPanel::rebuild(const ControlContext& context)
|
|||||||
|
|
||||||
for (ControlAction action : m_shownContextActions)
|
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
|
// The always-available block sits under a divider in every context, the General one
|
||||||
@@ -168,16 +197,17 @@ void ControlsPanel::rebuild(const ControlContext& context)
|
|||||||
divider->setFrameShape(QFrame::HLine);
|
divider->setFrameShape(QFrame::HLine);
|
||||||
divider->setFrameShadow(QFrame::Plain);
|
divider->setFrameShadow(QFrame::Plain);
|
||||||
m_rowsLayout->addSpacing(6);
|
m_rowsLayout->addSpacing(6);
|
||||||
m_rowsLayout->addWidget(divider);
|
addAndShow(m_rowsLayout, divider);
|
||||||
|
|
||||||
QLabel* caption = new QLabel(tr("ALWAYS AVAILABLE"), m_rows);
|
QLabel* caption = new QLabel(tr("ALWAYS AVAILABLE"), m_rows);
|
||||||
caption->setObjectName(QStringLiteral("controlCaption"));
|
caption->setObjectName(QStringLiteral("controlCaption"));
|
||||||
m_rowsLayout->addWidget(caption);
|
caption->setFont(makeSpacedFont(font(), /*bold*/ false, /*pointSizeDelta*/ -1));
|
||||||
|
addAndShow(m_rowsLayout, caption);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ControlAction action : m_shownAlwaysActions)
|
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);
|
m_rows->setVisible(!m_collapsed);
|
||||||
@@ -221,16 +251,28 @@ void ControlsPanel::refit()
|
|||||||
{
|
{
|
||||||
if (m_bandRect.isNull()) { return; }
|
if (m_bandRect.isNull()) { return; }
|
||||||
|
|
||||||
// The layout drops hidden widgets from its size hint, but only once it has been
|
// Rows are torn down and rebuilt wholesale, and a widget added to a layout is only
|
||||||
// re-run: collapsing hides the rows before Qt would get around to it on its own.
|
// shown once that layout runs -- without this the new rows count for nothing and
|
||||||
layout()->activate();
|
// the card is measured for the context before it. The polish belongs to the same
|
||||||
|
// step: a freshly created label reports an unstyled size hint until the stylesheet
|
||||||
|
// has reached it, and the badge chips carry border and padding that change it.
|
||||||
|
m_rows->ensurePolished();
|
||||||
|
m_rowsLayout->invalidate();
|
||||||
|
m_rowsLayout->activate();
|
||||||
|
|
||||||
|
// Deliberately not activating the panel's own layout here. That lays the heading
|
||||||
|
// and the rows out inside the geometry the panel still has from the previous
|
||||||
|
// context, which is the wrong frame of reference for choosing the new one.
|
||||||
|
// totalSizeHint answers "how big does this need to be" without reference to how big
|
||||||
|
// it currently is; setGeometry below then re-runs the outer layout on its own.
|
||||||
|
layout()->invalidate();
|
||||||
|
const QSize needed = layout()->totalSizeHint();
|
||||||
|
|
||||||
const QRect band = m_bandRect.adjusted(kMarginPx, kMarginPx, -kMarginPx, -kMarginPx);
|
const QRect band = m_bandRect.adjusted(kMarginPx, kMarginPx, -kMarginPx, -kMarginPx);
|
||||||
if (band.width() <= 0 || band.height() <= 0) { return; }
|
if (band.width() <= 0 || band.height() <= 0) { return; }
|
||||||
|
|
||||||
const QSize hint = sizeHint();
|
const int widthPx = qMin(needed.width(), band.width());
|
||||||
const int widthPx = qMin(hint.width(), band.width());
|
const int heightPx = qMin(needed.height(), band.height());
|
||||||
const int heightPx = qMin(hint.height(), band.height());
|
|
||||||
|
|
||||||
// Left edge of the band, sitting on its bottom edge, so the panel grows upward as
|
// Left edge of the band, sitting on its bottom edge, so the panel grows upward as
|
||||||
// rows are added (REQ-UI-CONTROLS-PANEL).
|
// rows are added (REQ-UI-CONTROLS-PANEL).
|
||||||
|
|||||||
Reference in New Issue
Block a user