From 7e670fc01c37e6b4846abcd934c140c09b41060b Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Wed, 12 Aug 2026 21:21:47 +0200 Subject: [PATCH] state the debris scrap value with the item icon, not the word --- docs/requirements.md | 6 ++-- src/balancing/CMakeLists.txt | 4 +++ src/ui/IconCaption.h | 11 ++++--- src/ui/ItemIconCache.cpp | 7 ++++ src/ui/ItemIconCache.h | 14 ++++++-- src/ui/selection/DebrisContent.cpp | 12 ++++++- src/ui/selection/DebrisContent.h | 6 ++++ src/ui/selection/FieldMultiContent.cpp | 15 ++++++--- src/ui/selection/FieldMultiContent.h | 5 +++ src/ui/selection/MultiBuildingContent.cpp | 8 +++++ src/ui/selection/StatRow.cpp | 40 ++++++++++++++++++++--- src/ui/selection/StatRow.h | 17 ++++++++++ 12 files changed, 126 insertions(+), 19 deletions(-) diff --git a/docs/requirements.md b/docs/requirements.md index 26f1b4f..2e48af6 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -489,7 +489,7 @@ The screen is a single column: a header bar across the top and the game world vi Because the contest-zone boundaries shift as the scrollable area grows with each push (REQ-GW-PUSH-EXPAND, REQ-GW-SCROLL-LIMIT), the ramp bands are recomputed from the current contest-zone boundaries. This is a presentation-only concern and does not affect the simulation, consistent with REQ-UI-NO-ZOOM. - REQ-UI-WORLD-ICON: In the game world, a building is drawn with an icon's glyph symbol centered on its footprint, in place of the letter identity glyph. The icon is an SVG loaded from `data/icons/buildings/`; only the icon's glyph is drawn in the world — in a contrasting ink (white over dark fills, dark over light fills) so it stays legible — and its colored chip background is omitted, because the footprint is already filled with the building's `visuals.toml` fill color. This applies to the production buildings (Miner, Smelter, Assembler, Reprocessing Plant, Shipyard, Salvage Bay), the HQ, and the player and enemy defence stations, wherever the identity label appears: operational buildings, construction sites (REQ-UI-CONSTRUCTION-PROGRESS), and the builder-mode and blueprint-placement ghosts. **Belts, splitters, and tunnels are excluded** — they keep their existing tile rendering so their orientation and flow stay readable (a centered icon would obscure direction). Their build-menu buttons still use icons (REQ-UI-BUILD-ICON); in particular the shared Tunnel button's `tunnel_entry.svg` is a build-button icon only, not a world icon. The directional output-port glyphs (REQ-UI-PORT-GLYPH, REQ-UI-PORT-TARGET-GLYPH) are a separate indicator and are unaffected. A building or station with no icon file falls back to its `visuals.toml` text glyph; a type with neither icon nor glyph shows no identity label. A missing icon is not an error, consistent with REQ-UI-BUILD-ICON. -- REQ-UI-ITEM-ICON: In the game world, an item is drawn as its `visuals.toml` colored square (`fill` + `outline`, the square of REQ-GW-TILE-SIZE) carrying its **item icon** on top. The square is drawn for every item, with or without an icon: it is what gives the item contrast against the tile beneath it, and its outline is what separates neighbouring items where they overlap on a belt. The icon is a self-contained, full-color SVG (rendered as-is, unlike the glyph-only building icons of REQ-UI-WORLD-ICON), loaded at runtime from `data/icons/items/` — a sibling of the config directory, read the same way as the building icons (REQ-UI-BUILD-ICON) — one file per item type named after the item's id (e.g. `iron_ore.svg`). The square fills the item's half-tile rect, keeping the size, spacing, and draw-order rules of REQ-GW-TILE-SIZE; the icon is drawn **inset** within that rect so a frame of the square's color stays visible all around it — required because each icon's viewBox is cropped tight to its artwork, so an icon drawn at the full rect would cover the square entirely. This applies wherever an item is drawn: on belts, splitters, and tunnel ends, and while emerging from or sinking into a building port (REQ-MAT-OUTPUT-EMERGE, REQ-MAT-INPUT-INTAKE). An item type with no icon file shows the colored square alone; a missing icon is not an error, consistent with REQ-UI-BUILD-ICON. **The colored square travels with the icon into the UI.** Wherever the UI displays an item *as an item* — the selection panel's item chips (REQ-UI-SINGLE-SELECTION, REQ-UI-HQ-PANEL), the recipe summary's input and output icons (REQ-UI-RECIPE-SUMMARY), and those same icons wherever a recipe is drawn in that form: on the selection dialog's option buttons (REQ-UI-SELECT-OPTIONS), in the item production tooltip (REQ-UI-ITEM-TOOLTIP), and in the unlock-choice dialog's recipe lines (REQ-DEF-SCHEMATIC-DROP) — the icon is drawn on that same square, by the same rules: the item's `fill` and `outline` from `visuals.toml`, the icon inset within the square so a frame of the square's color stays visible all around it, and the square alone when the item has no icon file. The square fills the rect the display allots to the icon, as it fills the half-tile rect in the world. One item therefore reads the same in a panel as it does on a belt. The one exception is the inline `building_block` icon that stands in for the word `Blocks` beside a number (REQ-UI-BLOCKS-ICON, REQ-UI-EXPAND-BUTTON, REQ-UI-BUILD-COST, REQ-UI-MULTI-SELECTION, REQ-UI-BLUEPRINT-CARD): there the icon is a decoration on a line of text rather than an item display, and it is drawn bare, without the square. For performance, each item icon is rasterized to a pixmap cached per target pixel size — re-rasterized only when the tile pixel size changes (e.g. on view resize) — rather than re-rendered from vector every frame. +- REQ-UI-ITEM-ICON: In the game world, an item is drawn as its `visuals.toml` colored square (`fill` + `outline`, the square of REQ-GW-TILE-SIZE) carrying its **item icon** on top. The square is drawn for every item, with or without an icon: it is what gives the item contrast against the tile beneath it, and its outline is what separates neighbouring items where they overlap on a belt. The icon is a self-contained, full-color SVG (rendered as-is, unlike the glyph-only building icons of REQ-UI-WORLD-ICON), loaded at runtime from `data/icons/items/` — a sibling of the config directory, read the same way as the building icons (REQ-UI-BUILD-ICON) — one file per item type named after the item's id (e.g. `iron_ore.svg`). The square fills the item's half-tile rect, keeping the size, spacing, and draw-order rules of REQ-GW-TILE-SIZE; the icon is drawn **inset** within that rect so a frame of the square's color stays visible all around it — required because each icon's viewBox is cropped tight to its artwork, so an icon drawn at the full rect would cover the square entirely. This applies wherever an item is drawn: on belts, splitters, and tunnel ends, and while emerging from or sinking into a building port (REQ-MAT-OUTPUT-EMERGE, REQ-MAT-INPUT-INTAKE). An item type with no icon file shows the colored square alone; a missing icon is not an error, consistent with REQ-UI-BUILD-ICON. **The colored square travels with the icon into the UI.** Wherever the UI displays an item *as an item* — the selection panel's item chips (REQ-UI-SINGLE-SELECTION, REQ-UI-HQ-PANEL), the recipe summary's input and output icons (REQ-UI-RECIPE-SUMMARY), and those same icons wherever a recipe is drawn in that form: on the selection dialog's option buttons (REQ-UI-SELECT-OPTIONS), in the item production tooltip (REQ-UI-ITEM-TOOLTIP), and in the unlock-choice dialog's recipe lines (REQ-DEF-SCHEMATIC-DROP) — the icon is drawn on that same square, by the same rules: the item's `fill` and `outline` from `visuals.toml`, the icon inset within the square so a frame of the square's color stays visible all around it, and the square alone when the item has no icon file. The square fills the rect the display allots to the icon, as it fills the half-tile rect in the world. One item therefore reads the same in a panel as it does on a belt. The one exception is an **inline icon standing in for the item's name beside a number** on a line of text: the `building_block` icon in place of the word `Blocks` (REQ-UI-BLOCKS-ICON, REQ-UI-EXPAND-BUTTON, REQ-UI-BUILD-COST, REQ-UI-MULTI-SELECTION, REQ-UI-BLUEPRINT-CARD), and the `scrap` icon in place of the word `Scrap` in the debris panel's remaining-scrap value (REQ-UI-DEBRIS-PANEL, REQ-UI-FIELD-MULTI-SELECTION). There the icon is a decoration on a line of text rather than an item display: it is drawn bare, without the square, and sized to the height of the text it sits in. Where the same value is instead shown *as an item* it keeps the square, as the HQ's block stock chip does beside the header bar's bare block icon (REQ-UI-HQ-PANEL). For performance, each item icon is rasterized to a pixmap cached per target pixel size — re-rasterized only when the tile pixel size changes (e.g. on view resize) — rather than re-rendered from vector every frame. - REQ-UI-CONSTRUCTION-PROGRESS: Construction sites display the building's identity symbol centered on the footprint (same as an operational building) — its icon glyph, or the text glyph as a fallback (REQ-UI-WORLD-ICON). Below the symbol — or centered on the footprint if the building has neither an icon nor a glyph — a construction progress percentage is shown (integer, e.g. `42%`), increasing from 0% to 100% as construction completes. - REQ-UI-PORT-GLYPH: Every output port of every building is indicated by a directional glyph drawn on the port's tile. The glyph is a `>` rotated to face the port's exit direction (`>` for East, `^` for North, `<` for West, `v` for South). It is drawn at the midpoint between the tile center and the tile edge that the port exits through (i.e. halfway from center toward the exit edge). The indicator is rendered for all building states: operational buildings, construction sites, and the builder-mode ghost. Buildings with multiple output ports (e.g. splitters) show one indicator per port. - REQ-UI-PORT-TARGET-GLYPH: While in builder mode (REQ-BLD-BUILDER-MODE), the builder-mode ghost additionally shows, for each of the building's output ports, a directional glyph drawn centered in the port's **target cell** — the cell immediately outside the footprint that the port pushes into, i.e. the cell the surface-mask output-port indicator occupies (see Surface Mask Format). As in REQ-UI-PORT-GLYPH the glyph is a `>` rotated to face the port's exit direction (`>` East, `^` North, `<` West, `v` South), previewing where the port's output will go before placement. This is in addition to the on-tile port glyph of REQ-UI-PORT-GLYPH, and — unlike that indicator — is shown only for the builder-mode ghost, not for operational buildings, construction sites, or the blueprint-placement ghost (REQ-UI-BLUEPRINT-PLACE). A building with multiple output ports (e.g. a splitter) shows one target-cell glyph per port. The target-cell glyph is drawn larger than the on-tile port glyph so it stands out as the flow-direction preview. Exceptions: the Tunnel Entry shows no target-cell glyph, because it receives items (which may arrive from any of its non-mouth edges, REQ-BLD-TUNNEL-ENTRY) rather than emitting into a single adjacent cell; the Shipyard shows none either, because its output port is a ship-spawn point (REQ-SHP-SPAWN-PLAYER) rather than a belt-item output (REQ-MAT-OUTPUT-EMERGE). @@ -620,10 +620,10 @@ The panel shows exactly one **content** at a time, picked from the catalog in RE - **Standby** — the ship is holding with its fleet (REQ-SHP-STANDBY). - **Advancing** — the ship is executing the baseline forward advance with no higher-priority behavior active (player: REQ-SHP-COMBAT advance toward the enemy; enemy: REQ-SHP-ENEMY-AI advance toward the asteroid). - REQ-UI-STATION-STATS-PANEL: When exactly one defence station is selected (REQ-UI-ENTITY-CLICK-SELECT) and no debris is selected, the selection panel shows a **station stats panel** displaying the station's stats computed at its current level: HP (current / maximum) as a **bar** with the two values beside its caption, then damage, range, and fire rate as label/value rows, matching the ship stats panel's rendering (REQ-UI-SHIP-STATS-PANEL). Its header carries no right slot: a station has no behavior label and no status light. (If debris is also selected, the panel shows the compact count summary instead, per REQ-UI-FIELD-MULTI-SELECTION.) -- REQ-UI-FIELD-MULTI-SELECTION: A full single-object stats panel (REQ-UI-SHIP-STATS-PANEL, REQ-UI-STATION-STATS-PANEL, REQ-UI-DEBRIS-PANEL) is shown when the field selection holds exactly one object — one ship, one defence station, or one piece of debris — and, for debris only, when it holds several pieces of debris and nothing else, which aggregate into that same content (REQ-UI-SELECTION-AGGREGATE). Every other field selection of more than one object — multiple actors, or any mix of actors and debris — shows a **count summary** instead. Its header reads `Mixed selection` with the total number of selected objects in the right slot (REQ-UI-SELECTION-CARD). Below it is one row per type — the type's symbol, its name, and the number selected as `x`, the same `x`-count notation as the recipe tooltip and the building multi-selection (REQ-UI-MULTI-SELECTION). Ships are grouped by schematic display name and defence stations as a group, distinguishing player from enemy; all selected pieces of debris are grouped into a single `Debris` row whose count is the number of selected pieces. No per-object detail is shown. If debris is part of the selection, its row is followed by an indented sub-row giving the summed remaining scrap across all selected debris (REQ-UI-DEBRIS-PANEL). Building selections use REQ-UI-SINGLE-SELECTION / REQ-UI-MULTI-SELECTION instead. +- REQ-UI-FIELD-MULTI-SELECTION: A full single-object stats panel (REQ-UI-SHIP-STATS-PANEL, REQ-UI-STATION-STATS-PANEL, REQ-UI-DEBRIS-PANEL) is shown when the field selection holds exactly one object — one ship, one defence station, or one piece of debris — and, for debris only, when it holds several pieces of debris and nothing else, which aggregate into that same content (REQ-UI-SELECTION-AGGREGATE). Every other field selection of more than one object — multiple actors, or any mix of actors and debris — shows a **count summary** instead. Its header reads `Mixed selection` with the total number of selected objects in the right slot (REQ-UI-SELECTION-CARD). Below it is one row per type — the type's symbol, its name, and the number selected as `x`, the same `x`-count notation as the recipe tooltip and the building multi-selection (REQ-UI-MULTI-SELECTION). Ships are grouped by schematic display name and defence stations as a group, distinguishing player from enemy; all selected pieces of debris are grouped into a single `Debris` row whose count is the number of selected pieces. No per-object detail is shown. If debris is part of the selection, its row is followed by an indented sub-row giving the summed remaining scrap across all selected debris as the amount followed by the bare `scrap` icon, exactly as the debris panel states it (REQ-UI-DEBRIS-PANEL, REQ-UI-ITEM-ICON). Building selections use REQ-UI-SINGLE-SELECTION / REQ-UI-MULTI-SELECTION instead. - REQ-UI-DEBRIS-CLICK-SELECT: The player can click any piece of debris (REQ-RES-DEBRIS-DROP) in the game world to select it. Debris are field objects (REQ-UI-SELECTION-CATEGORIES) and can be selected together with ships and defence stations, but not with buildings. A plain click on a piece of debris makes it the sole selection, clearing any previous selection; selecting a building clears any debris (buildings win), and selecting a piece of debris clears any building selection. Hit-testing prefers a building over a coincident actor or piece of debris, and an actor (ship or defence station) over a coincident piece of debris: a piece of debris is selected only when no building or actor is under the cursor. A selected piece of debris that despawns or is fully collected (REQ-RES-DEBRIS-DROP) is removed from the selection; if no selected object remains, the panel becomes empty (REQ-UI-EMPTY-SELECTION). - REQ-UI-DEBRIS-MULTI-SELECT: Multiple pieces of debris can be selected by box-drag or by Ctrl+clicking individual pieces to add or remove them, mirroring building multi-select (REQ-UI-MULTI-SELECT). Debris shares the field-object category with ships and defence stations (REQ-UI-SELECTION-CATEGORIES), so a field selection may hold debris and actors together. Ctrl+clicking a piece of debris while a field selection is active adds or removes that piece within the same selection; Ctrl+clicking a piece of debris while a building selection is active first clears the buildings and begins a field selection (buildings win). Conversely, selecting a building while a field selection is active clears it. Box-drag disambiguation follows REQ-UI-MULTI-SELECT (a box covering any building selects buildings; a box covering no building selects the ships, defence stations, and debris within it). -- REQ-UI-DEBRIS-PANEL: When debris is selected and no actors are (REQ-UI-FIELD-MULTI-SELECTION), the selection panel shows a **debris stats panel** structured like the ship and station stats panels (REQ-UI-SHIP-STATS-PANEL, REQ-UI-STATION-STATS-PANEL): a header reading **"Debris"**, followed by a single stat row, **"Scrap remaining"**, in the same label/value style as a ship hull stat row. With one piece selected the row shows that piece's remaining scrap amount (REQ-RES-DEBRIS-DROP) and the header's right slot is empty. With several pieces selected the same content is shown aggregated (REQ-UI-SELECTION-AGGREGATE): the number of selected pieces appears in the header's right slot as `x` and the row shows the summed remaining scrap across them. When debris is selected together with actors, the debris are instead summarized within the count summary (REQ-UI-FIELD-MULTI-SELECTION): a `Debris` row giving the number of selected pieces, followed by an indented sub-row with their summed remaining scrap. The displayed scrap value updates as selected debris are partially collected or despawn (REQ-UI-DEBRIS-CLICK-SELECT). +- REQ-UI-DEBRIS-PANEL: When debris is selected and no actors are (REQ-UI-FIELD-MULTI-SELECTION), the selection panel shows a **debris stats panel** structured like the ship and station stats panels (REQ-UI-SHIP-STATS-PANEL, REQ-UI-STATION-STATS-PANEL): a header reading **"Debris"**, followed by a single stat row captioned **"Remaining"**, in the same label/value style as a ship hull stat row. Its value is the scrap amount followed by the `scrap` item icon drawn **bare, without its colored square** and sized to the row's text height — the same inline form the header bar uses for building blocks (REQ-UI-BLOCKS-ICON, REQ-UI-ITEM-ICON). The icon stands in for the word `Scrap`, which is why the caption names neither the item nor its unit; when no icon file exists for `scrap` (a missing icon is not an error, REQ-UI-ITEM-ICON), the row falls back to the caption `Scrap remaining` with the amount alone as its value. With one piece selected the row shows that piece's remaining scrap amount (REQ-RES-DEBRIS-DROP) and the header's right slot is empty. With several pieces selected the same content is shown aggregated (REQ-UI-SELECTION-AGGREGATE): the number of selected pieces appears in the header's right slot as `x` and the row shows the summed remaining scrap across them. When debris is selected together with actors, the debris are instead summarized within the count summary (REQ-UI-FIELD-MULTI-SELECTION): a `Debris` row giving the number of selected pieces, followed by an indented sub-row with their summed remaining scrap, stated in the same amount-plus-bare-icon form. The displayed scrap value updates as selected debris are partially collected or despawn (REQ-UI-DEBRIS-CLICK-SELECT). ### Build Button Bar diff --git a/src/balancing/CMakeLists.txt b/src/balancing/CMakeLists.txt index 5f35ea0..b21e2a1 100644 --- a/src/balancing/CMakeLists.txt +++ b/src/balancing/CMakeLists.txt @@ -13,6 +13,9 @@ SET(HDRS ${CMAKE_CURRENT_SOURCE_DIR}/../ui/selection/BarRow.h ${CMAKE_CURRENT_SOURCE_DIR}/../ui/selection/SectionBox.h ${CMAKE_CURRENT_SOURCE_DIR}/../ui/selection/SelectionNames.h + # A stat row states a value with an inline item icon (REQ-UI-ITEM-ICON), which this + # composes; it needs nothing but Qt's painting. + ${CMAKE_CURRENT_SOURCE_DIR}/../ui/IconCaption.h ${CMAKE_CURRENT_SOURCE_DIR}/../ui/VisualsConfig.h ${CMAKE_CURRENT_SOURCE_DIR}/../ui/VisualsLoader.h # Shared world-space shapes so the arena keeps looking like the game @@ -36,6 +39,7 @@ SET(SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../ui/selection/BarRow.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../ui/selection/SectionBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../ui/selection/SelectionNames.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ui/IconCaption.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../ui/VisualsLoader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../ui/WorldPrimitives.cpp PARENT_SCOPE diff --git a/src/ui/IconCaption.h b/src/ui/IconCaption.h index c9bca08..56800ce 100644 --- a/src/ui/IconCaption.h +++ b/src/ui/IconCaption.h @@ -21,8 +21,11 @@ QPixmap renderCaptionWithIcon(const QString& text, const QPixmap& icon, // their raw size is not the size to lay them out at. QSize getLogicalSize(const QPixmap& pixmap); -// Item id of the building blocks resource, whose icon stands in for the "Blocks" -// word wherever a cost or stock is captioned (REQ-UI-BLOCKS-ICON, REQ-UI-BUILD-COST, -// REQ-UI-EXPAND-BUTTON). It lives next to the caption helper because every caller of -// one is a caller of the other. +// The items whose icons stand in for their names beside a number, drawn bare and without +// their colored square (REQ-UI-ITEM-ICON): building blocks beside a cost or a stock +// (REQ-UI-BLOCKS-ICON, REQ-UI-BUILD-COST, REQ-UI-EXPAND-BUTTON), and scrap beside the +// amount left in debris (REQ-UI-DEBRIS-PANEL, REQ-UI-FIELD-MULTI-SELECTION). They live +// next to the caption helper because every caller of one is a caller of the other; +// ItemIconCache::getInlineIcon() is what turns an id here into that icon. const char* const kBlockItemId = "building_block"; +const char* const kScrapItemId = "scrap"; diff --git a/src/ui/ItemIconCache.cpp b/src/ui/ItemIconCache.cpp index d7e6991..42920f3 100644 --- a/src/ui/ItemIconCache.cpp +++ b/src/ui/ItemIconCache.cpp @@ -1,6 +1,7 @@ #include "ItemIconCache.h" #include +#include #include #include #include @@ -94,6 +95,12 @@ QPixmap ItemIconCache::getPixmap(const std::string& itemId, int sizePx) return getPixmap(itemId, itemId, sizePx, false); } +QPixmap ItemIconCache::getInlineIcon(const std::string& itemId, const QFont& font) +{ + if (!hasIcon(itemId)) { return QPixmap(); } + return getPixmap(itemId, QFontMetrics(font).height()); +} + QPixmap ItemIconCache::getPixmap(const std::string& cacheKey, const std::string& itemId, int sizePx, bool withSquare) { diff --git a/src/ui/ItemIconCache.h b/src/ui/ItemIconCache.h index 6552d93..f20c1ca 100644 --- a/src/ui/ItemIconCache.h +++ b/src/ui/ItemIconCache.h @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -26,9 +27,11 @@ struct VisualsConfig; // two forms: paintItem() for the world's fractional geometry, getSquarePixmap() for // widgets that want a ready-made pixmap. // -// The bare icon of getPixmap() has one remaining use: the inline building_block icon -// that stands in for the word "Blocks" beside a number (REQ-UI-BLOCKS-ICON), which is a -// decoration on a line of text rather than an item display and takes no square. +// The bare icon of getPixmap() has one remaining use: an inline icon standing in for the +// item's name beside a number -- building blocks beside a cost or a stock +// (REQ-UI-BLOCKS-ICON), scrap beside the amount left in debris (REQ-UI-DEBRIS-PANEL) -- +// which is a decoration on a line of text rather than an item display and takes no +// square. getInlineItemIcon() in IconCaption.h is how callers ask for that form. // // A missing icon file is not an error: hasIcon() returns false for it and the item shows // its colored square alone. @@ -64,6 +67,11 @@ public: // icon file (callers should gate on hasIcon()). QPixmap getPixmap(const std::string& itemId, int sizePx); + // The same bare icon sized to the height of `font`'s text, for the inline form above: + // an icon standing in for the item's name on a line of text. Null when the item has + // no icon file, which is not an error -- the caller names the item in words instead. + QPixmap getInlineIcon(const std::string& itemId, const QFont& font); + // Drops every rasterized pixmap. Called when the visuals are reloaded on a restart // (REQ-CFG-RELOAD), because the composed squares carry the colors they were painted // with; they are re-rasterized on next use. diff --git a/src/ui/selection/DebrisContent.cpp b/src/ui/selection/DebrisContent.cpp index 852ddff..63ea333 100644 --- a/src/ui/selection/DebrisContent.cpp +++ b/src/ui/selection/DebrisContent.cpp @@ -3,6 +3,8 @@ #include #include "DebrisScrap.h" +#include "IconCaption.h" +#include "ItemIconCache.h" #include "Simulation.h" #include "StatRow.h" @@ -10,8 +12,16 @@ DebrisContent::DebrisContent(const SelectionContext& context, const SelectionRequest& request, QWidget* parent) : SelectionContent(context, std::nullopt, parent) , m_debris(request.debris) + , m_scrapIcon(context.itemIcons->getInlineIcon(kScrapItemId, font())) { - m_scrapRow = new StatRow(tr("Scrap remaining"), this); + // The row states the amount and the bare scrap icon, the icon standing in for the + // word so the row spells out neither the item nor its unit -- the form the header bar + // states the block stock in (REQ-UI-DEBRIS-PANEL, REQ-UI-ITEM-ICON, + // REQ-UI-BLOCKS-ICON). Without an icon file the caption has to name the item again, + // since nothing else on the row would. + m_scrapRow = new StatRow( + m_scrapIcon.isNull() ? tr("Scrap remaining") : tr("Remaining"), this); + m_scrapRow->setValueIcon(m_scrapIcon); m_scrapRow->setValueEmphasized(true); getRuntimeLayout()->addWidget(m_scrapRow); diff --git a/src/ui/selection/DebrisContent.h b/src/ui/selection/DebrisContent.h index 00b7716..7872a8a 100644 --- a/src/ui/selection/DebrisContent.h +++ b/src/ui/selection/DebrisContent.h @@ -2,6 +2,8 @@ #include +#include + #include "entt/entity/entity.hpp" #include "SelectionContent.h" @@ -28,4 +30,8 @@ protected: private: std::vector m_debris; StatRow* m_scrapRow; + // The bare scrap icon the row states its amount with, or null where there is no icon + // file and the row names the item in words instead (REQ-UI-DEBRIS-PANEL). Taken once: + // it depends on the card's font, not on the value. + QPixmap m_scrapIcon; }; diff --git a/src/ui/selection/FieldMultiContent.cpp b/src/ui/selection/FieldMultiContent.cpp index 350764f..44ca298 100644 --- a/src/ui/selection/FieldMultiContent.cpp +++ b/src/ui/selection/FieldMultiContent.cpp @@ -10,6 +10,8 @@ #include "DisplayName.h" #include "EntityAdmin.h" #include "FactionComponent.h" +#include "IconCaption.h" +#include "ItemIconCache.h" #include "ShipIdentityComponent.h" #include "Simulation.h" #include "StatRow.h" @@ -20,6 +22,7 @@ FieldMultiContent::FieldMultiContent(const SelectionContext& context, : SelectionContent(context, std::nullopt, parent) , m_debris(request.debris) , m_scrapRow(nullptr) + , m_scrapIcon(context.itemIcons->getInlineIcon(kScrapItemId, font())) { setIdentity(QPixmap(), tr("Mixed selection")); setCountSlot(static_cast(request.actors.size() + request.debris.size())); @@ -84,9 +87,11 @@ void FieldMultiContent::buildSummary(const std::vector& actors) getRuntimeLayout()->addWidget(new CountRow( QPixmap(), tr("Debris"), static_cast(m_debris.size()), this)); - // Indented under the debris row, so the total reads as belonging to it - // (REQ-UI-DEBRIS-PANEL). + // Indented under the debris row, so the total reads as belonging to it, and + // stated in the same amount-plus-bare-icon form the debris card uses + // (REQ-UI-DEBRIS-PANEL, REQ-UI-FIELD-MULTI-SELECTION). m_scrapRow = new StatRow(tr("holding"), this); + m_scrapRow->setValueIcon(m_scrapIcon); m_scrapRow->setIndented(true); m_scrapRow->setValueEmphasized(true); getRuntimeLayout()->addWidget(m_scrapRow); @@ -99,7 +104,9 @@ void FieldMultiContent::refreshRuntime() // selection and rebuilds this card -- but the scrap falls as the debris is collected. if (m_scrapRow) { - m_scrapRow->setValue(tr("%1 scrap") - .arg(sumDebrisScrap(getContext().sim->getAdmin(), m_debris))); + const int scrap = sumDebrisScrap(getContext().sim->getAdmin(), m_debris); + // The icon names the item; only without one does the value have to say the word. + m_scrapRow->setValue(m_scrapIcon.isNull() ? tr("%1 scrap").arg(scrap) + : QString::number(scrap)); } } diff --git a/src/ui/selection/FieldMultiContent.h b/src/ui/selection/FieldMultiContent.h index 75b7e57..3d6c4f3 100644 --- a/src/ui/selection/FieldMultiContent.h +++ b/src/ui/selection/FieldMultiContent.h @@ -2,6 +2,8 @@ #include +#include + #include "entt/entity/entity.hpp" #include "SelectionContent.h" @@ -30,4 +32,7 @@ private: // Null unless debris is part of the selection; the only value here that changes // while the selection stands. StatRow* m_scrapRow; + // The bare scrap icon the sub-row states its total with, or null where there is no + // icon file and the total names the item in words instead (REQ-UI-DEBRIS-PANEL). + QPixmap m_scrapIcon; }; diff --git a/src/ui/selection/MultiBuildingContent.cpp b/src/ui/selection/MultiBuildingContent.cpp index dc892a2..d905be0 100644 --- a/src/ui/selection/MultiBuildingContent.cpp +++ b/src/ui/selection/MultiBuildingContent.cpp @@ -10,6 +10,8 @@ #include "CountRow.h" #include "FactoryQueries.h" #include "GameConfig.h" +#include "IconCaption.h" +#include "ItemIconCache.h" #include "SelectionNames.h" #include "Simulation.h" #include "StatRow.h" @@ -92,7 +94,13 @@ void MultiBuildingContent::buildSummary() } } + // The block icon stands beside the total in place of the word, as it does on a build + // button and a blueprint card (REQ-UI-MULTI-SELECTION, REQ-UI-BLOCKS-ICON). With no + // icon file the total is the bare number, as it is there + // (REQ-UI-BUILD-COST, REQ-UI-BLUEPRINT-CARD). StatRow* totalRow = new StatRow(tr("Total cost"), this); + totalRow->setValueIcon( + getContext().itemIcons->getInlineIcon(kBlockItemId, font())); totalRow->setValue(QString::number(totalCost)); totalRow->setValueEmphasized(true); getRuntimeLayout()->addWidget(totalRow); diff --git a/src/ui/selection/StatRow.cpp b/src/ui/selection/StatRow.cpp index 6970c27..3da8c59 100644 --- a/src/ui/selection/StatRow.cpp +++ b/src/ui/selection/StatRow.cpp @@ -4,6 +4,8 @@ #include #include +#include "IconCaption.h" + namespace { @@ -15,6 +17,7 @@ const int kIndentPx = 12; StatRow::StatRow(const QString& label, QWidget* parent) : QWidget(parent) + , m_valueEmphasized(false) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -36,19 +39,48 @@ void StatRow::setLabel(const QString& label) void StatRow::setValue(const QString& value) { - m_valueLabel->setText(value); + m_value = value; + updateValue(); +} + +void StatRow::setValueIcon(const QPixmap& icon) +{ + m_valueIcon = icon; + updateValue(); } void StatRow::setValueEmphasized(bool emphasized) { + m_valueEmphasized = emphasized; + QPalette valuePalette = m_valueLabel->palette(); - valuePalette.setColor(QPalette::WindowText, - palette().color(emphasized ? QPalette::Highlight - : QPalette::WindowText)); + valuePalette.setColor(QPalette::WindowText, getValueColor()); m_valueLabel->setPalette(valuePalette); + + updateValue(); } void StatRow::setIndented(bool indented) { layout()->setContentsMargins(indented ? kIndentPx : 0, 0, 0, 0); } + +void StatRow::updateValue() +{ + if (m_valueIcon.isNull()) + { + m_valueLabel->setText(m_value); + return; + } + // A label shows either text or a pixmap, so the two are composed into one -- the same + // way the header bar states the block stock (REQ-UI-BLOCKS-ICON). The color is passed + // in rather than left to the palette, which a pixmap does not follow. + m_valueLabel->setPixmap(renderCaptionWithIcon(m_value, m_valueIcon, + m_valueLabel->font(), getValueColor())); +} + +QColor StatRow::getValueColor() const +{ + return palette().color(m_valueEmphasized ? QPalette::Highlight + : QPalette::WindowText); +} diff --git a/src/ui/selection/StatRow.h b/src/ui/selection/StatRow.h index b88b4fe..652ccf0 100644 --- a/src/ui/selection/StatRow.h +++ b/src/ui/selection/StatRow.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include @@ -20,6 +22,12 @@ public: void setLabel(const QString& label); void setValue(const QString& value); + // Draws an item icon after the value, bare and without its colored square: the icon + // stands in for the item's name beside the number rather than displaying the item + // (REQ-UI-ITEM-ICON), which is how a debris card states its scrap + // (REQ-UI-DEBRIS-PANEL). A null pixmap -- what a missing icon file yields -- leaves + // the value plain text, and the caller names the item in the label instead. + void setValueIcon(const QPixmap& icon); // Draws the value in the palette's highlight color rather than its text color, for // the one value a card is really about. void setValueEmphasized(bool emphasized); @@ -28,6 +36,15 @@ public: void setIndented(bool indented); private: + // Re-states the value in whichever form it currently takes. Needed because the icon + // form is one composed pixmap, which has to be built anew whenever the text or the + // color it is drawn in changes. + void updateValue(); + QColor getValueColor() const; + QLabel* m_labelLabel; QLabel* m_valueLabel; + QString m_value; + QPixmap m_valueIcon; + bool m_valueEmphasized; };