give a recipe line its own row for its identity
This commit is contained in:
@@ -373,7 +373,7 @@ Any ship, module, building, or assembler recipe id that appears in no unlock gro
|
|||||||
|
|
||||||
Each option additionally displays a vertical list of recipe lines labeled "Unlocks recipes:", showing which miner and assembler recipes would newly become implicitly unlocked (REQ-LOCK-IMPLICIT) if this option were selected — specifically, the miner recipes and implicitly-gated assembler recipes that are not currently implicitly unlocked but would become so after applying this option's effect. To compute this, all `materials` of the group's granted ship and module schematics are added to the base set per REQ-LOCK-IMPLICIT step 1a, and the output items of the group's granted assembler recipes are added per step 1b, before recomputation.
|
Each option additionally displays a vertical list of recipe lines labeled "Unlocks recipes:", showing which miner and assembler recipes would newly become implicitly unlocked (REQ-LOCK-IMPLICIT) if this option were selected — specifically, the miner recipes and implicitly-gated assembler recipes that are not currently implicitly unlocked but would become so after applying this option's effect. To compute this, all `materials` of the group's granted ship and module schematics are added to the base set per REQ-LOCK-IMPLICIT step 1a, and the output items of the group's granted assembler recipes are added per step 1b, before recomputation.
|
||||||
|
|
||||||
Each recipe is shown as a **recipe line** of the same form the item production tooltip uses (REQ-UI-ITEM-TOOLTIP): the icon of the building that runs it (REQ-UI-BUILD-ICON), the recipe's name — by its `id`, using the same display convention as the assembler recipe-selection dialog — and the recipe drawn as the recipe summary line draws it (REQ-UI-RECIPE-SUMMARY). The lines are sorted alphabetically by recipe name. The line says everything there is to say about the recipe, so nothing in this list carries a tooltip, as in the selection dialog (REQ-UI-SELECT-OPTIONS). If no recipes would be newly unlocked, the list shows "None".
|
Each recipe is shown as a **recipe line** of the same two-row form the item production tooltip uses (REQ-UI-ITEM-TOOLTIP): the icon of the building that runs it (REQ-UI-BUILD-ICON) and the recipe's name — by its `id`, using the same display convention as the assembler recipe-selection dialog — on the first row, and the recipe drawn as the recipe summary draws it (REQ-UI-RECIPE-SUMMARY) on the second. The lines are sorted alphabetically by recipe name. The line says everything there is to say about the recipe, so nothing in this list carries a tooltip, as in the selection dialog (REQ-UI-SELECT-OPTIONS). If no recipes would be newly unlocked, the list shows "None".
|
||||||
|
|
||||||
The player selects one option by clicking it. If the player selects the artifact option, the player's artifact count is incremented by 1 (REQ-WIN-ARTIFACT-COUNT) and the dialog closes; no unlock is applied. Otherwise the selected unlock group is awarded and the dialog closes: every ship, module, building, and assembler recipe the group grants becomes unlocked at once — ship schematics unlock the corresponding shipyard selection; module schematics unlock the module type for placement in the layout configuration dialog (REQ-MOD-UI-DIALOG); building types become available in the build menu (REQ-LOCK-BUILDING); assembler recipes become available in the assembler recipe-selection dialog (subject to REQ-LOCK-UI-RECIPE). The unlock group is removed from the pool permanently (REQ-LOCK-EXPLICIT), and the implicit unlock set is recomputed (REQ-LOCK-IMPLICIT).
|
The player selects one option by clicking it. If the player selects the artifact option, the player's artifact count is incremented by 1 (REQ-WIN-ARTIFACT-COUNT) and the dialog closes; no unlock is applied. Otherwise the selected unlock group is awarded and the dialog closes: every ship, module, building, and assembler recipe the group grants becomes unlocked at once — ship schematics unlock the corresponding shipyard selection; module schematics unlock the module type for placement in the layout configuration dialog (REQ-MOD-UI-DIALOG); building types become available in the build menu (REQ-LOCK-BUILDING); assembler recipes become available in the assembler recipe-selection dialog (subject to REQ-LOCK-UI-RECIPE). The unlock group is removed from the pool permanently (REQ-LOCK-EXPLICIT), and the implicit unlock set is recomputed (REQ-LOCK-IMPLICIT).
|
||||||
|
|
||||||
@@ -600,7 +600,7 @@ The panel shows exactly one **content** at a time, picked from the catalog in RE
|
|||||||
- The `(None)` option shows its text caption alone.
|
- The `(None)` option shows its text caption alone.
|
||||||
- REQ-UI-ITEM-TOOLTIP: **Item production tooltip.** Hovering an item chip in the selection panel — an input or output buffer chip (REQ-UI-SINGLE-SELECTION) or the HQ's block stock chip (REQ-UI-HQ-PANEL) — shows a tooltip telling the player where that item comes from. It has a heading and a body:
|
- REQ-UI-ITEM-TOOLTIP: **Item production tooltip.** Hovering an item chip in the selection panel — an input or output buffer chip (REQ-UI-SINGLE-SELECTION) or the HQ's block stock chip (REQ-UI-HQ-PANEL) — shows a tooltip telling the player where that item comes from. It has a heading and a body:
|
||||||
- the heading is the **hovered item's name**. For an input chip this is the only place the item is named at all, since such a chip carries a count and no name (REQ-UI-SINGLE-SELECTION).
|
- the heading is the **hovered item's name**. For an input chip this is the only place the item is named at all, since such a chip carries a count and no name (REQ-UI-SINGLE-SELECTION).
|
||||||
- the body is the caption `Produced by` followed by one **recipe line** per unlocked recipe that produces the item. Each line carries the icon of the building that runs that recipe (REQ-UI-BUILD-ICON), the recipe's name, and the recipe drawn as the recipe summary line draws it (REQ-UI-RECIPE-SUMMARY): each input item's icon on its colored square with its per-cycle amount, an arrow, each output item's icon with its amount, and the cycle time. An item may have several producers — an iron ingot is smelted from ore, smelted from scrap, and recovered by reprocessing — and the building icon and recipe name are what tell those lines apart and tell the player which building to place for which path.
|
- the body is the caption `Produced by` followed by one **recipe line** per unlocked recipe that produces the item. A recipe line takes two rows: the icon of the building that runs the recipe (REQ-UI-BUILD-ICON) and the recipe's name on the first, and the recipe itself on the second, drawn as the recipe summary draws it (REQ-UI-RECIPE-SUMMARY) — each input item's icon on its colored square with its per-cycle amount, an arrow, each output item's icon with its amount, and the cycle time. Two rows rather than one because an identity and a cycle read as different things, and a single row of icons, names and numbers runs too long to scan. An item may have several producers — an iron ingot is smelted from ore, smelted from scrap, and recovered by reprocessing — and the building icon and recipe name are what tell those lines apart and tell the player which building to place for which path.
|
||||||
|
|
||||||
**Only recipes the player can run are listed**, consistent with the rest of the UI hiding what the player cannot make yet. What that means differs by building, because only Miner and Assembler recipes are unlocked individually (REQ-LOCK-UI-RECIPE): those are listed once unlocked, while a Smelter's or Reprocessing Plant's implicit recipes (REQ-BLD-SMELTER, REQ-BLD-REPROCESSING) are listed once **their building** is unlocked (REQ-LOCK-BUILDING) — there is no sense in naming a path through a plant the player cannot place. Two cases have no recipe line to show, and each says so in place of the list rather than leaving the tooltip bare:
|
**Only recipes the player can run are listed**, consistent with the rest of the UI hiding what the player cannot make yet. What that means differs by building, because only Miner and Assembler recipes are unlocked individually (REQ-LOCK-UI-RECIPE): those are listed once unlocked, while a Smelter's or Reprocessing Plant's implicit recipes (REQ-BLD-SMELTER, REQ-BLD-REPROCESSING) are listed once **their building** is unlocked (REQ-LOCK-BUILDING) — there is no sense in naming a path through a plant the player cannot place. Two cases have no recipe line to show, and each says so in place of the list rather than leaving the tooltip bare:
|
||||||
- **An item no recipe produces at all.** Scrap is salvaged from debris (REQ-RES-DEBRIS-DROP) rather than crafted, so its tooltip reads `Salvaged from debris` in place of the `Produced by` caption and lists nothing beneath it.
|
- **An item no recipe produces at all.** Scrap is salvaged from debris (REQ-RES-DEBRIS-DROP) rather than crafted, so its tooltip reads `Salvaged from debris` in place of the `Produced by` caption and lists nothing beneath it.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLayoutItem>
|
#include <QLayoutItem>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "BuildingIconCache.h"
|
#include "BuildingIconCache.h"
|
||||||
#include "ItemIconCache.h"
|
#include "ItemIconCache.h"
|
||||||
@@ -28,6 +29,19 @@ void addAndShow(QHBoxLayout* layout, QLabel* label)
|
|||||||
label->show();
|
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
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
@@ -37,9 +51,22 @@ RecipeLineRow::RecipeLineRow(ItemIconCache* itemIcons, BuildingIconCache* buildi
|
|||||||
, m_itemIcons(itemIcons)
|
, m_itemIcons(itemIcons)
|
||||||
, m_buildingIcons(buildingIcons)
|
, m_buildingIcons(buildingIcons)
|
||||||
{
|
{
|
||||||
m_layout = new QHBoxLayout(this);
|
QVBoxLayout* outerLayout = new QVBoxLayout(this);
|
||||||
m_layout->setContentsMargins(0, 0, 0, 0);
|
outerLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
m_layout->setSpacing(4);
|
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();
|
hide();
|
||||||
}
|
}
|
||||||
@@ -66,39 +93,40 @@ void RecipeLineRow::setLine(const Spec& spec)
|
|||||||
|
|
||||||
void RecipeLineRow::rebuild(const Spec& spec)
|
void RecipeLineRow::rebuild(const Spec& spec)
|
||||||
{
|
{
|
||||||
while (QLayoutItem* item = m_layout->takeAt(0))
|
clearRow(m_headerLayout);
|
||||||
{
|
clearRow(m_amountsLayout);
|
||||||
if (item->widget())
|
|
||||||
{
|
|
||||||
item->widget()->deleteLater();
|
|
||||||
}
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The building the recipe runs in, where the line has to distinguish one producer
|
// First line: which building runs the recipe and which recipe it is, where the line
|
||||||
// from another (REQ-UI-ITEM-TOOLTIP). A building with no chip file leaves the icon
|
// has to distinguish one producer from another (REQ-UI-ITEM-TOOLTIP). A building with
|
||||||
// off, as everywhere else (REQ-UI-BUILD-ICON).
|
// no chip file leaves the icon off, as everywhere else (REQ-UI-BUILD-ICON).
|
||||||
if (spec.building.has_value() && m_buildingIcons != nullptr)
|
if (spec.building.has_value() && m_buildingIcons != nullptr)
|
||||||
{
|
{
|
||||||
const QPixmap chip =
|
const QPixmap chip =
|
||||||
m_buildingIcons->getChip(buildingTypeId(*spec.building), kIconSizePx);
|
m_buildingIcons->getChip(buildingTypeId(*spec.building), kIconSizePx);
|
||||||
if (!chip.isNull())
|
if (!chip.isNull())
|
||||||
{
|
{
|
||||||
QLabel* chipLabel = new QLabel(this);
|
QLabel* chipLabel = new QLabel(m_headerRow);
|
||||||
chipLabel->setPixmap(chip);
|
chipLabel->setPixmap(chip);
|
||||||
addAndShow(m_layout, chipLabel);
|
addAndShow(m_headerLayout, chipLabel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!spec.name.isEmpty())
|
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);
|
addAmounts(spec.inputs);
|
||||||
if (!spec.inputs.empty() && !spec.outputs.empty())
|
if (!spec.inputs.empty() && !spec.outputs.empty())
|
||||||
{
|
{
|
||||||
const QChar rightArrow(0x2192); // U+2192 RIGHTWARDS ARROW
|
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);
|
addAmounts(spec.outputs);
|
||||||
|
|
||||||
@@ -108,10 +136,10 @@ void RecipeLineRow::rebuild(const Spec& spec)
|
|||||||
const QString time = spec.durationIsAddition
|
const QString time = spec.durationIsAddition
|
||||||
? tr("+%1 s").arg(*spec.durationSeconds, 0, 'f', 1)
|
? tr("+%1 s").arg(*spec.durationSeconds, 0, 'f', 1)
|
||||||
: tr("%1 s").arg(*spec.durationSeconds, 0, 'f', 1);
|
: tr("%1 s").arg(*spec.durationSeconds, 0, 'f', 1);
|
||||||
addAndShow(m_layout, new QLabel(
|
addAndShow(m_amountsLayout, new QLabel(
|
||||||
QStringLiteral("%1 %2").arg(middleDot).arg(time), this));
|
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)
|
void RecipeLineRow::addAmounts(const std::vector<Amount>& amounts)
|
||||||
@@ -126,15 +154,16 @@ void RecipeLineRow::addAmounts(const std::vector<Amount>& amounts)
|
|||||||
: QPixmap();
|
: QPixmap();
|
||||||
if (!icon.isNull())
|
if (!icon.isNull())
|
||||||
{
|
{
|
||||||
QLabel* iconLabel = new QLabel(this);
|
QLabel* iconLabel = new QLabel(m_amountsRow);
|
||||||
iconLabel->setPixmap(icon);
|
iconLabel->setPixmap(icon);
|
||||||
addAndShow(m_layout, iconLabel);
|
addAndShow(m_amountsLayout, iconLabel);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
addAndShow(m_layout,
|
addAndShow(m_amountsLayout,
|
||||||
new QLabel(QString::fromStdString(entry.itemId), this));
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ class BuildingIconCache;
|
|||||||
class ItemIconCache;
|
class ItemIconCache;
|
||||||
class QHBoxLayout;
|
class QHBoxLayout;
|
||||||
|
|
||||||
// One recipe drawn on a line: what it consumes, an arrow, what it produces, and how
|
// One recipe drawn: what it consumes, an arrow, what it produces, and how long a cycle
|
||||||
// long a cycle takes (REQ-UI-RECIPE-SUMMARY). Every place the UI states what something
|
// takes (REQ-UI-RECIPE-SUMMARY), under a header naming the building that runs it and the
|
||||||
// makes draws this same line, so a recipe reads alike wherever it is shown:
|
// 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
|
// * beneath the selection button, for the recipe a building is running
|
||||||
// (REQ-UI-RECIPE-SUMMARY);
|
// (REQ-UI-RECIPE-SUMMARY);
|
||||||
@@ -48,12 +49,13 @@ public:
|
|||||||
// lines.
|
// lines.
|
||||||
struct Spec
|
struct Spec
|
||||||
{
|
{
|
||||||
// Drawn as a leading chip when the line has to say which building runs the
|
// The chip of the building running the recipe, on the header line, where the
|
||||||
// recipe -- an item may have several producers (REQ-UI-ITEM-TOOLTIP). Unset
|
// recipe has to say which building that is -- an item may have several producers
|
||||||
// wherever the surrounding widget already establishes the building.
|
// (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;
|
std::optional<BuildingType> building;
|
||||||
// Leading recipe name, for the same reason. Empty where the name is the
|
// The recipe's name, beside the building chip on the header line. Empty where the
|
||||||
// caption above the line instead.
|
// name is the caption of the widget around this one instead.
|
||||||
QString name;
|
QString name;
|
||||||
std::vector<Amount> inputs;
|
std::vector<Amount> inputs;
|
||||||
// Empty for a line that produces no item of its own: a ship schematic, or a
|
// Empty for a line that produces no item of its own: a ship schematic, or a
|
||||||
@@ -91,7 +93,13 @@ private:
|
|||||||
|
|
||||||
ItemIconCache* m_itemIcons;
|
ItemIconCache* m_itemIcons;
|
||||||
BuildingIconCache* m_buildingIcons;
|
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
|
// What the row currently shows, so a refresh at tick rate rebuilds it only when the
|
||||||
// recipe actually changed.
|
// recipe actually changed.
|
||||||
Spec m_spec;
|
Spec m_spec;
|
||||||
|
|||||||
@@ -80,7 +80,12 @@ SchematicChoiceDialog::SchematicChoiceDialog(
|
|||||||
|
|
||||||
QWidget* card = new QWidget(this);
|
QWidget* card = new QWidget(this);
|
||||||
QVBoxLayout* cardLayout = new QVBoxLayout(card);
|
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);
|
QLabel* nameLabel = new QLabel(QString::fromStdString(option.displayName), card);
|
||||||
QFont nameFont = nameLabel->font();
|
QFont nameFont = nameLabel->font();
|
||||||
|
|||||||
Reference in New Issue
Block a user