color item icon backgrounds in the UI from visuals.toml

This commit is contained in:
2026-08-11 21:18:52 +02:00
parent 37378e3c1b
commit d1da4b2937
8 changed files with 194 additions and 78 deletions

View File

@@ -8,38 +8,76 @@
#include <QPixmap>
#include <QString>
// Rasterizes and caches per-item icon SVGs (REQ-UI-ITEM-ICON). Item icons are
// self-contained, full-color SVGs loaded from a directory, one file per item type
// named after the item's id (e.g. "iron_ore.svg"). Shared by the recipe-selection
// dialog (REQ-UI-RECIPE-ICON) and the game world's belt/port item rendering so the
// rasterization is not duplicated.
class QPainter;
class QRectF;
struct VisualsConfig;
// Rasterizes and caches per-item icon SVGs, and composes them onto the item's colored
// square (REQ-UI-ITEM-ICON). Item icons are self-contained, full-color SVGs loaded from
// a directory, one file per item type named after the item's id (e.g. "iron_ore.svg").
//
// A missing icon file is not an error: hasIcon() returns false for it and the caller
// falls back (a colored square in the world, a name caption in the dialog).
// The square is the item's `fill` and `outline` from visuals.toml with the icon inset
// within it, and it backs the icon both in the game world and wherever the UI displays
// an item as an item: the recipe-selection dialog's option buttons (REQ-UI-RECIPE-ICON),
// the selection panel's item chips (REQ-UI-SINGLE-SELECTION, REQ-UI-HQ-PANEL), and the
// recipe summary (REQ-UI-RECIPE-SUMMARY). Defined here once rather than per widget, in
// 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.
//
// A missing icon file is not an error: hasIcon() returns false for it and the item shows
// its colored square alone.
class ItemIconCache
{
public:
// iconDir is the directory holding the "<item_id>.svg" icon files
// (typically "<configDir>/../icons/items").
explicit ItemIconCache(const QString& iconDir);
// iconDir is the directory holding the "<item_id>.svg" icon files (typically
// "<configDir>/../icons/items"). visuals supplies the per-item square colors and
// must outlive the cache; its contents may be replaced on a restart (REQ-CFG-RELOAD),
// which is what clearPixmapCache() is for.
ItemIconCache(const QString& iconDir, const VisualsConfig* visuals);
// True if an icon SVG file exists for the given item id. Loads the file's bytes
// on first query and remembers the result (including absence) so repeated calls
// are cheap.
bool hasIcon(const std::string& itemId);
// Returns the item's icon rasterized to a transparent sizePx*sizePx pixmap,
// cached per (item id, size) so it is rendered once and reused across frames and
// only re-rasterized when the target size changes (REQ-UI-ITEM-ICON). Returns a
// null pixmap if the item has no icon file (callers should gate on hasIcon()).
// Paints the item's colored square into rect and its icon inset within it
// (REQ-UI-ITEM-ICON). Takes the rect as a QRectF so the world can keep painting at
// sub-pixel geometry. An item with no visuals entry gets no square, one with no icon
// file no icon; with neither, this paints nothing.
void paintItem(QPainter& painter, const QRectF& rect, const std::string& itemId);
// The same composition rasterized to a sizePx*sizePx pixmap for widget use, cached
// per (item id, size) so it is rendered once and reused. Returns a null pixmap only
// when the item has neither a visuals entry nor an icon file, which is the one case
// in which a caller has nothing to show and falls back to text.
QPixmap getSquarePixmap(const std::string& itemId, int sizePx);
// Returns the item's icon alone, without its square, rasterized to a transparent
// sizePx*sizePx pixmap and cached per (item id, size) so it is rendered once and
// reused across frames (REQ-UI-ITEM-ICON). Returns a null pixmap if the item has no
// icon file (callers should gate on hasIcon()).
QPixmap getPixmap(const std::string& itemId, int sizePx);
// 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.
void clearPixmapCache();
private:
// Returns the raw SVG bytes for an item id, loading and caching them on first
// access. An absent file caches an empty QByteArray so it is not retried.
const QByteArray& getSvg(const std::string& itemId);
// Shared rasterize-and-cache step. cacheKey distinguishes the bare and squared
// variants of one item within the single pixmap cache.
QPixmap getPixmap(const std::string& cacheKey, const std::string& itemId,
int sizePx, bool withSquare);
QString m_iconDir;
const VisualsConfig* m_visuals; // Not owned; lives in MainWindow.
std::map<std::string, QByteArray> m_svgById;
std::map<std::pair<std::string, int>, QPixmap> m_pixmapCache;
};