give a recipe line its own row for its identity

This commit is contained in:
2026-08-11 22:07:15 +02:00
parent 1ddd830476
commit a3fddf63cc
4 changed files with 80 additions and 38 deletions

View File

@@ -4,6 +4,7 @@
#include <QLabel>
#include <QLayoutItem>
#include <QPixmap>
#include <QVBoxLayout>
#include "BuildingIconCache.h"
#include "ItemIconCache.h"
@@ -28,6 +29,19 @@ void addAndShow(QHBoxLayout* layout, QLabel* label)
label->show();
}
// Empties one of the two rows, leaving the row widget and its layout in place.
void clearRow(QHBoxLayout* layout)
{
while (QLayoutItem* item = layout->takeAt(0))
{
if (item->widget())
{
item->widget()->deleteLater();
}
delete item;
}
}
} // namespace
@@ -37,9 +51,22 @@ RecipeLineRow::RecipeLineRow(ItemIconCache* itemIcons, BuildingIconCache* buildi
, m_itemIcons(itemIcons)
, m_buildingIcons(buildingIcons)
{
m_layout = new QHBoxLayout(this);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->setSpacing(4);
QVBoxLayout* outerLayout = new QVBoxLayout(this);
outerLayout->setContentsMargins(0, 0, 0, 0);
outerLayout->setSpacing(1);
m_headerRow = new QWidget(this);
m_headerLayout = new QHBoxLayout(m_headerRow);
m_headerLayout->setContentsMargins(0, 0, 0, 0);
m_headerLayout->setSpacing(4);
m_headerRow->hide();
outerLayout->addWidget(m_headerRow);
m_amountsRow = new QWidget(this);
m_amountsLayout = new QHBoxLayout(m_amountsRow);
m_amountsLayout->setContentsMargins(0, 0, 0, 0);
m_amountsLayout->setSpacing(4);
outerLayout->addWidget(m_amountsRow);
hide();
}
@@ -66,39 +93,40 @@ void RecipeLineRow::setLine(const Spec& spec)
void RecipeLineRow::rebuild(const Spec& spec)
{
while (QLayoutItem* item = m_layout->takeAt(0))
{
if (item->widget())
{
item->widget()->deleteLater();
}
delete item;
}
clearRow(m_headerLayout);
clearRow(m_amountsLayout);
// The building the recipe runs in, where the line has to distinguish one producer
// from another (REQ-UI-ITEM-TOOLTIP). A building with no chip file leaves the icon
// off, as everywhere else (REQ-UI-BUILD-ICON).
// First line: which building runs the recipe and which recipe it is, where the line
// has to distinguish one producer from another (REQ-UI-ITEM-TOOLTIP). A building with
// no chip file leaves the icon off, as everywhere else (REQ-UI-BUILD-ICON).
if (spec.building.has_value() && m_buildingIcons != nullptr)
{
const QPixmap chip =
m_buildingIcons->getChip(buildingTypeId(*spec.building), kIconSizePx);
if (!chip.isNull())
{
QLabel* chipLabel = new QLabel(this);
QLabel* chipLabel = new QLabel(m_headerRow);
chipLabel->setPixmap(chip);
addAndShow(m_layout, chipLabel);
addAndShow(m_headerLayout, chipLabel);
}
}
if (!spec.name.isEmpty())
{
addAndShow(m_layout, new QLabel(spec.name, this));
addAndShow(m_headerLayout, new QLabel(spec.name, m_headerRow));
}
const bool hasHeader = m_headerLayout->count() > 0;
if (hasHeader)
{
m_headerLayout->addStretch(1);
}
m_headerRow->setVisible(hasHeader);
// Second line: what the cycle costs, makes and takes.
addAmounts(spec.inputs);
if (!spec.inputs.empty() && !spec.outputs.empty())
{
const QChar rightArrow(0x2192); // U+2192 RIGHTWARDS ARROW
addAndShow(m_layout, new QLabel(QString(rightArrow), this));
addAndShow(m_amountsLayout, new QLabel(QString(rightArrow), m_amountsRow));
}
addAmounts(spec.outputs);
@@ -108,10 +136,10 @@ void RecipeLineRow::rebuild(const Spec& spec)
const QString time = spec.durationIsAddition
? tr("+%1 s").arg(*spec.durationSeconds, 0, 'f', 1)
: tr("%1 s").arg(*spec.durationSeconds, 0, 'f', 1);
addAndShow(m_layout, new QLabel(
QStringLiteral("%1 %2").arg(middleDot).arg(time), this));
addAndShow(m_amountsLayout, new QLabel(
QStringLiteral("%1 %2").arg(middleDot).arg(time), m_amountsRow));
}
m_layout->addStretch(1);
m_amountsLayout->addStretch(1);
}
void RecipeLineRow::addAmounts(const std::vector<Amount>& amounts)
@@ -126,15 +154,16 @@ void RecipeLineRow::addAmounts(const std::vector<Amount>& amounts)
: QPixmap();
if (!icon.isNull())
{
QLabel* iconLabel = new QLabel(this);
QLabel* iconLabel = new QLabel(m_amountsRow);
iconLabel->setPixmap(icon);
addAndShow(m_layout, iconLabel);
addAndShow(m_amountsLayout, iconLabel);
}
else
{
addAndShow(m_layout,
new QLabel(QString::fromStdString(entry.itemId), this));
addAndShow(m_amountsLayout,
new QLabel(QString::fromStdString(entry.itemId), m_amountsRow));
}
addAndShow(m_layout, new QLabel(QString::number(entry.amount), this));
addAndShow(m_amountsLayout,
new QLabel(QString::number(entry.amount), m_amountsRow));
}
}

View File

@@ -13,9 +13,10 @@ class BuildingIconCache;
class ItemIconCache;
class QHBoxLayout;
// One recipe drawn on a line: what it consumes, an arrow, what it produces, and how
// long a cycle takes (REQ-UI-RECIPE-SUMMARY). Every place the UI states what something
// makes draws this same line, so a recipe reads alike wherever it is shown:
// One recipe drawn: what it consumes, an arrow, what it produces, and how long a cycle
// takes (REQ-UI-RECIPE-SUMMARY), under a header naming the building that runs it and the
// recipe itself where the recipe has to identify itself. Every place the UI states what
// something makes draws this same line, so a recipe reads alike wherever it is shown:
//
// * beneath the selection button, for the recipe a building is running
// (REQ-UI-RECIPE-SUMMARY);
@@ -48,12 +49,13 @@ public:
// lines.
struct Spec
{
// Drawn as a leading chip when the line has to say which building runs the
// recipe -- an item may have several producers (REQ-UI-ITEM-TOOLTIP). Unset
// wherever the surrounding widget already establishes the building.
// The chip of the building running the recipe, on the header line, where the
// recipe has to say which building that is -- an item may have several producers
// (REQ-UI-ITEM-TOOLTIP). Unset wherever the surrounding widget already
// establishes the building, and the header line is then left out entirely.
std::optional<BuildingType> building;
// Leading recipe name, for the same reason. Empty where the name is the
// caption above the line instead.
// The recipe's name, beside the building chip on the header line. Empty where the
// name is the caption of the widget around this one instead.
QString name;
std::vector<Amount> inputs;
// Empty for a line that produces no item of its own: a ship schematic, or a
@@ -91,7 +93,13 @@ private:
ItemIconCache* m_itemIcons;
BuildingIconCache* m_buildingIcons;
QHBoxLayout* m_layout;
// The two lines, each a row widget of its own so a rebuild only has to empty their
// layouts -- no nested layout to take apart. The header is hidden where the spec
// names neither a building nor a recipe.
QWidget* m_headerRow;
QHBoxLayout* m_headerLayout;
QWidget* m_amountsRow;
QHBoxLayout* m_amountsLayout;
// What the row currently shows, so a refresh at tick rate rebuilds it only when the
// recipe actually changed.
Spec m_spec;

View File

@@ -80,7 +80,12 @@ SchematicChoiceDialog::SchematicChoiceDialog(
QWidget* card = new QWidget(this);
QVBoxLayout* cardLayout = new QVBoxLayout(card);
card->setStyleSheet("QWidget { border: 1px solid gray; padding: 8px; }");
// Scoped to the card by object name. An unscoped "QWidget" selector cascades to
// every descendant, which boxed each label and each icon of the recipe lines
// inside it as well.
card->setObjectName(QStringLiteral("schematicChoiceCard"));
card->setStyleSheet(QStringLiteral(
"QWidget#schematicChoiceCard { border: 1px solid gray; padding: 8px; }"));
QLabel* nameLabel = new QLabel(QString::fromStdString(option.displayName), card);
QFont nameFont = nameLabel->font();