give the selection cards their own parts instead of label blobs

This commit is contained in:
2026-08-07 18:43:23 +02:00
parent 2289277e12
commit 075e44d295
54 changed files with 1505 additions and 533 deletions

View File

@@ -4,18 +4,19 @@
#include <string>
#include <QFont>
#include <QGuiApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QPainter>
#include <QRectF>
#include <QVBoxLayout>
#include "BarRow.h"
#include "BuildingIconCache.h"
#include "EmptyNote.h"
#include "FactoryQueries.h"
#include "GameConfig.h"
#include "ProductionRules.h"
#include "SectionBox.h"
#include "Simulation.h"
#include "StatusPill.h"
#include "Tick.h"
#include "VisualsConfig.h"
@@ -27,31 +28,10 @@ namespace
// the whole button face.
const int kSymbolSizePx = 20;
// Diameter of the status dot beside the header's right-slot caption
// (REQ-UI-SELECTION-STATUS).
const int kStatusDotSizePx = 8;
// Spacing inside the card and between the header's elements.
const int kCardSpacingPx = 6;
const int kHeaderSpacingPx = 6;
QPixmap renderStatusDot(const QColor& fill, const QColor& outline)
{
const qreal dpr = qApp ? qApp->devicePixelRatio() : 1.0;
QPixmap pixmap(static_cast<int>(kStatusDotSizePx * dpr),
static_cast<int>(kStatusDotSizePx * dpr));
pixmap.setDevicePixelRatio(dpr);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(outline);
painter.setBrush(fill);
// Inset by half the pen width so the outline stays inside the pixmap.
painter.drawEllipse(QRectF(0.5, 0.5, kStatusDotSizePx - 1.0, kStatusDotSizePx - 1.0));
return pixmap;
}
} // namespace
@@ -61,7 +41,8 @@ SelectionContent::SelectionContent(const SelectionContext& context,
: QWidget(parent)
, m_context(context)
, m_siteId(constructionSiteId)
, m_constructionLabel(nullptr)
, m_constructionSection(nullptr)
, m_constructionBar(nullptr)
{
QVBoxLayout* cardLayout = new QVBoxLayout(this);
cardLayout->setContentsMargins(0, 0, 0, 0);
@@ -82,17 +63,12 @@ SelectionContent::SelectionContent(const SelectionContext& context,
nameFont.setBold(true);
m_nameLabel->setFont(nameFont);
m_slotDot = new QLabel(header);
m_slotDot->hide();
m_slotLabel = new QLabel(header);
m_slotLabel->hide();
m_statusPill = new StatusPill(header);
headerLayout->addWidget(m_symbolLabel);
headerLayout->addWidget(m_nameLabel);
headerLayout->addStretch(1);
headerLayout->addWidget(m_slotDot);
headerLayout->addWidget(m_slotLabel);
headerLayout->addWidget(m_statusPill);
cardLayout->addWidget(header);
m_configurationGroup = new QWidget(this);
@@ -114,8 +90,13 @@ SelectionContent::SelectionContent(const SelectionContext& context,
// takes its place (REQ-BLD-SITE-CONFIG, REQ-UI-SELECTION-CARD). The subclass
// still fills the group -- it just never becomes visible.
m_runtimeGroup->hide();
m_constructionLabel = new QLabel(this);
cardLayout->addWidget(m_constructionLabel);
m_constructionSection = new SectionBox(tr("Construction"), this);
m_constructionBar = new BarRow(QString(), m_constructionSection);
m_constructionSection->getContentLayout()->addWidget(m_constructionBar);
m_constructionSection->getContentLayout()->addWidget(
new EmptyNote(tr("No buffers until built"), m_constructionSection));
cardLayout->addWidget(m_constructionSection);
setSlot(QColor(), tr("constructing"));
}
@@ -159,18 +140,7 @@ void SelectionContent::setBuildingIdentity(BuildingType type, const QString& nam
void SelectionContent::setSlot(const QColor& dotColor, const QString& caption)
{
if (dotColor.isValid())
{
m_slotDot->setPixmap(
renderStatusDot(dotColor, m_context.visuals->statusLight.outline));
m_slotDot->show();
}
else
{
m_slotDot->hide();
}
m_slotLabel->setText(caption);
m_slotLabel->setVisible(!caption.isEmpty());
m_statusPill->setStatus(dotColor, m_context.visuals->statusLight.outline, caption);
}
void SelectionContent::setCountSlot(int count)
@@ -227,28 +197,26 @@ void SelectionContent::refreshConstruction()
return;
}
QString progress;
if (site->completesAt == 0)
{
progress = tr("Queued");
// Placed but not yet at the head of the construction queue (REQ-BLD-QUEUE), so
// there is no progress to show yet.
m_constructionBar->setValue(0.0, tr("Queued"));
return;
}
else
const BuildingDef* def = m_context.config->buildings.findBuildingDef(site->type);
if (!def || def->constructionTimeSeconds <= 0)
{
const BuildingDef* def =
m_context.config->buildings.findBuildingDef(site->type);
if (def && def->constructionTimeSeconds > 0)
{
const Tick duration = secondsToTicks(def->constructionTimeSeconds);
const Tick elapsed =
m_context.sim->getCurrentTick() - (site->completesAt - duration);
const int percent = static_cast<int>(
std::max(Tick(0), std::min(duration, elapsed)) * 100 / duration);
progress = tr("Construction: %1%").arg(percent);
}
else
{
progress = tr("Building...");
}
m_constructionBar->setValue(0.0, tr("Building..."));
return;
}
m_constructionLabel->setText(progress);
const Tick duration = secondsToTicks(def->constructionTimeSeconds);
const Tick elapsed =
m_context.sim->getCurrentTick() - (site->completesAt - duration);
const Tick clamped = std::max(Tick(0), std::min(duration, elapsed));
const int percent = static_cast<int>(clamped * 100 / duration);
m_constructionBar->setValue(static_cast<double>(clamped) / duration,
tr("%1%").arg(percent));
}