Six things the player reported, five of them about where things sit and what is listed: - HP goes to the top of the runtime group, above everything else a card shows. Only the HQ had it elsewhere; the ship and station cards already led with it. - A construction site's progress moves out of the runtime group's place and directly under the header, so how far along the site is reads before what it is configured to become. - The production bar moves between the input and output buffers, so a producing building reads in the direction its materials flow: what goes in, what is being made of it, what has come out. BufferSection held both buffer sections and so could not be split around it; the two sections and the production section are now the card's own, in that order. - Locked items are left out of the buffers. An auto-recipe building's buffers are sized over every recipe of its type, so a Smelter carried an input for quartz -- which the player cannot mine yet -- and an output for the silicon it would smelt into. Both are dropped now, as everywhere else that hides what is not unlocked. - An idle auto-recipe building lists what it handles rather than nothing. It has no selected recipe to name, so a Reprocessing Plant between cycles showed no output buffer at all. Its sections now list the unlocked items of every recipe of its type -- the union its buffers were sized over -- without a per-cycle denominator, since no one recipe is in force. The sixth was the bars vanishing whenever the window lost focus, to a modal dialog or to another application. They were filled with the palette's current highlight, and the palette follows the window's focus: the inactive group's highlight sits close enough to the card's background to read as gone. They ask for the active group by name now. Measured on a freshly built Smelter, which is the case that showed three of these at once: INPUT BUFFERS 0 Copper Ore | 0 Iron Ore | 0 Scrap PRODUCTION idle OUTPUT BUFFER 0/2 Copper Ingot | 0/2 Iron Ingot Quartz and silicon filtered out, production between the buffers, and an idle building still saying what it handles. Requirements updated for the ordering, the locked-item rule and the idle auto-recipe listing (REQ-UI-SELECTION-CARD, REQ-UI-SINGLE-SELECTION, REQ-UI-PRODUCTION-PROGRESS, REQ-UI-HQ-PANEL). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
124 lines
3.4 KiB
C++
124 lines
3.4 KiB
C++
#include "BarRow.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QPainter>
|
|
#include <QPaintEvent>
|
|
#include <QVBoxLayout>
|
|
|
|
namespace
|
|
{
|
|
|
|
// Height and corner radius of the fill bar, in device-independent pixels.
|
|
const int kBarHeightPx = 6;
|
|
const qreal kBarRadiusPx = 3.0;
|
|
|
|
// Opacity of the unfilled track, over the card's background.
|
|
const int kTrackAlpha = 60;
|
|
|
|
} // namespace
|
|
|
|
|
|
// The bar itself. Painted rather than assembled from a QProgressBar, because all it
|
|
// needs is a rounded track with a rounded fill and a style sheet cannot be relied on to
|
|
// leave a progress bar's groove and chunk alone across styles.
|
|
class BarRow::Bar : public QWidget
|
|
{
|
|
public:
|
|
explicit Bar(QWidget* parent)
|
|
: QWidget(parent)
|
|
, m_fraction(0.0)
|
|
{
|
|
setFixedHeight(kBarHeightPx);
|
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
}
|
|
|
|
void setFraction(double fraction)
|
|
{
|
|
m_fraction = std::max(0.0, std::min(1.0, fraction));
|
|
update();
|
|
}
|
|
|
|
void setFillColor(const QColor& color)
|
|
{
|
|
m_fillColor = color;
|
|
update();
|
|
}
|
|
|
|
protected:
|
|
void paintEvent(QPaintEvent* /*event*/) override
|
|
{
|
|
// The active group is asked for by name rather than taken from the current one,
|
|
// which follows the window's focus: the inactive group's highlight is close
|
|
// enough to the card's background that the bar reads as gone whenever the game
|
|
// is tabbed away from or a modal dialog holds focus -- exactly when a player
|
|
// watching a build finish is most likely to be looking at it.
|
|
const QColor fill = m_fillColor.isValid()
|
|
? m_fillColor
|
|
: palette().color(QPalette::Active, QPalette::Highlight);
|
|
|
|
QColor track = fill;
|
|
track.setAlpha(kTrackAlpha);
|
|
|
|
QPainter painter(this);
|
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
|
painter.setPen(Qt::NoPen);
|
|
|
|
painter.setBrush(track);
|
|
painter.drawRoundedRect(rect(), kBarRadiusPx, kBarRadiusPx);
|
|
|
|
if (m_fraction > 0.0)
|
|
{
|
|
QRect filled = rect();
|
|
filled.setWidth(static_cast<int>(filled.width() * m_fraction));
|
|
painter.setBrush(fill);
|
|
painter.drawRoundedRect(filled, kBarRadiusPx, kBarRadiusPx);
|
|
}
|
|
}
|
|
|
|
private:
|
|
double m_fraction;
|
|
QColor m_fillColor;
|
|
};
|
|
|
|
|
|
BarRow::BarRow(const QString& caption, QWidget* parent)
|
|
: QWidget(parent)
|
|
{
|
|
QVBoxLayout* layout = new QVBoxLayout(this);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
layout->setSpacing(2);
|
|
|
|
QWidget* captionRow = new QWidget(this);
|
|
QHBoxLayout* captionLayout = new QHBoxLayout(captionRow);
|
|
captionLayout->setContentsMargins(0, 0, 0, 0);
|
|
captionLayout->setSpacing(8);
|
|
|
|
m_captionLabel = new QLabel(caption, captionRow);
|
|
m_captionLabel->setVisible(!caption.isEmpty());
|
|
m_valueLabel = new QLabel(captionRow);
|
|
m_valueLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
|
|
captionLayout->addWidget(m_captionLabel);
|
|
captionLayout->addStretch(1);
|
|
captionLayout->addWidget(m_valueLabel);
|
|
|
|
m_bar = new Bar(this);
|
|
|
|
layout->addWidget(captionRow);
|
|
layout->addWidget(m_bar);
|
|
}
|
|
|
|
void BarRow::setValue(double fraction, const QString& valueText)
|
|
{
|
|
m_bar->setFraction(fraction);
|
|
m_valueLabel->setText(valueText);
|
|
}
|
|
|
|
void BarRow::setFillColor(const QColor& color)
|
|
{
|
|
m_bar->setFillColor(color);
|
|
}
|