share a single ItemIconCache across the UI

This commit is contained in:
2026-08-03 21:08:19 +02:00
parent b4e622daa5
commit e02e323cb2
10 changed files with 46 additions and 43 deletions

View File

@@ -184,13 +184,12 @@ namespace
BuildButtonGrid::BuildButtonGrid(Simulation* sim, const GameConfig* config,
const std::string& iconDir,
const std::string& itemsIconDir, QWidget* parent)
ItemIconCache* itemIcons, QWidget* parent)
: QWidget(parent)
, m_sim(sim)
, m_config(config)
, m_iconDir(iconDir)
, m_itemIcons(std::make_unique<ItemIconCache>(
QString::fromStdString(itemsIconDir)))
, m_itemIcons(itemIcons)
{
QGridLayout* layout = new QGridLayout(this);
layout->setSpacing(4);

View File

@@ -32,11 +32,12 @@ class BuildButtonGrid : public QWidget,
public:
// iconDir is the directory holding the per-building "<id>.svg" chip icons
// (REQ-UI-BUILD-GRID); itemsIconDir holds the per-item icons and supplies the
// building_block icon shown in each button's cost (REQ-UI-BUILD-COST). Both are
// read from disk at runtime, like the config files.
// (REQ-UI-BUILD-GRID), read from disk at runtime like the config files.
// itemIcons is the window-wide per-item icon cache and supplies the
// building_block icon shown in each button's cost (REQ-UI-BUILD-COST). Not
// owned; must outlive this widget.
BuildButtonGrid(Simulation* sim, const GameConfig* config,
const std::string& iconDir, const std::string& itemsIconDir,
const std::string& iconDir, ItemIconCache* itemIcons,
QWidget* parent = nullptr);
~BuildButtonGrid() override;
@@ -65,7 +66,7 @@ private:
Simulation* m_sim;
const GameConfig* m_config;
std::string m_iconDir;
std::unique_ptr<ItemIconCache> m_itemIcons;
ItemIconCache* m_itemIcons; // Not owned; lives in MainWindow.
std::vector<BuildingType> m_types;
std::vector<QPushButton*> m_buttons;
std::map<BuildingType, int> m_costs;

View File

@@ -197,11 +197,13 @@ QColor statusLightFill(ProductionStatus status, const StatusLightVisuals& sl)
GameWorldView::GameWorldView(Simulation* sim, const GameConfig* config,
const VisualsConfig* visuals, const std::string& configDir,
const ParsedReplay* replay, QWidget* parent)
ItemIconCache* itemIcons, const ParsedReplay* replay,
QWidget* parent)
: QOpenGLWidget(parent)
, m_sim(sim)
, m_config(config)
, m_visuals(visuals)
, m_itemIcons(itemIcons)
, m_commandManager(*sim)
, m_gameSpeedMultiplier(1.0)
, m_prevNonZeroSpeed(1.0)
@@ -223,11 +225,6 @@ GameWorldView::GameWorldView(Simulation* sim, const GameConfig* config,
loadBuildingIcons(configDir);
// Item icons live beside the config dir, mirroring the building icons
// (REQ-UI-ITEM-ICON, REQ-UI-WORLD-ICON).
m_itemIcons = std::make_unique<ItemIconCache>(QDir::cleanPath(
QString::fromStdString(configDir) + "/../icons/items"));
m_renderTimer = new QTimer(this);
m_renderTimer->setInterval(16);
connect(m_renderTimer, &QTimer::timeout, this, &GameWorldView::onFrame);

View File

@@ -83,9 +83,12 @@ class GameWorldView : public QOpenGLWidget,
Q_OBJECT
public:
// itemIcons is the window-wide per-item icon cache (REQ-UI-ITEM-ICON); not
// owned, must outlive this widget.
GameWorldView(Simulation* sim, const GameConfig* config,
const VisualsConfig* visuals, const std::string& configDir,
const ParsedReplay* replay, QWidget* parent = nullptr);
ItemIconCache* itemIcons, const ParsedReplay* replay,
QWidget* parent = nullptr);
~GameWorldView() override;
double getGameSpeed() const;
@@ -309,9 +312,10 @@ private:
};
std::map<BuildingType, BuildingIconRenderers> m_buildingIcons;
// Per-item icon cache (REQ-UI-ITEM-ICON), loaded from <configDir>/../icons/items.
// Shared draw path for belt and port items; pixmaps are cached per target size.
std::unique_ptr<ItemIconCache> m_itemIcons;
// Per-item icon cache (REQ-UI-ITEM-ICON), shared window-wide and owned by
// MainWindow. Shared draw path for belt and port items; pixmaps are cached
// per target size.
ItemIconCache* m_itemIcons;
// Funnels all player input into the single Simulation::apply chokepoint.
CommandManager m_commandManager;

View File

@@ -32,10 +32,9 @@ const double HeaderBar::kSpeeds[] = { 0.0, 0.5, 1.0, 2.0, 10.0 };
const int HeaderBar::kSpeedCount = 5;
HeaderBar::HeaderBar(const Simulation* sim, const GameConfig* config,
const std::string& itemsIconDir, QWidget* parent)
ItemIconCache* itemIcons, QWidget* parent)
: QWidget(parent)
, m_itemIcons(std::make_unique<ItemIconCache>(
QString::fromStdString(itemsIconDir)))
, m_itemIcons(itemIcons)
, m_sim(sim)
{
QHBoxLayout* layout = new QHBoxLayout(this);

View File

@@ -33,11 +33,11 @@ class HeaderBar : public QWidget,
Q_OBJECT
public:
// itemsIconDir holds the per-item icon SVGs (REQ-UI-ITEM-ICON); used to show the
// building_block icon in the stock display and expand button (REQ-UI-BLOCKS-ICON,
// REQ-UI-EXPAND-BUTTON).
// itemIcons is the window-wide per-item icon cache (REQ-UI-ITEM-ICON); used to
// show the building_block icon in the stock display and expand button
// (REQ-UI-BLOCKS-ICON, REQ-UI-EXPAND-BUTTON). Not owned; must outlive this widget.
HeaderBar(const Simulation* sim, const GameConfig* config,
const std::string& itemsIconDir, QWidget* parent = nullptr);
ItemIconCache* itemIcons, QWidget* parent = nullptr);
~HeaderBar() override;
private slots:
@@ -73,7 +73,7 @@ private:
QPushButton* m_expandButton;
std::vector<QPushButton*> m_speedButtons;
std::unique_ptr<ItemIconCache> m_itemIcons;
ItemIconCache* m_itemIcons; // Not owned; lives in MainWindow.
// The simulation is the single source of truth for the block stock and the
// expansion cost; the change events are only refresh signals.

View File

@@ -27,6 +27,7 @@
#include "SelectedBuildingPanel.h"
#include "ShipLayoutBlueprintSerializer.h"
#include "ShipLayoutDialog.h"
#include "ItemIconCache.h"
#include "Simulation.h"
#include "Tick.h"
#include "VisualsLoader.h"
@@ -47,10 +48,13 @@ MainWindow::MainWindow(Simulation* sim, const std::string& configDir,
const std::string itemsIconDir = QDir::cleanPath(
QString::fromStdString(m_configDir) + "/../icons/items").toStdString();
m_headerBar = new HeaderBar(sim, &sim->getConfig(), itemsIconDir, this);
m_itemIcons = std::make_unique<ItemIconCache>(
QString::fromStdString(itemsIconDir));
m_headerBar = new HeaderBar(sim, &sim->getConfig(), m_itemIcons.get(), this);
m_gameWorldView = new GameWorldView(sim, &sim->getConfig(), &m_visuals, m_configDir,
m_replay.get(), this);
m_itemIcons.get(), m_replay.get(), this);
m_sidePanel = new QWidget(this);
QVBoxLayout* sideLayout = new QVBoxLayout(m_sidePanel);
@@ -63,7 +67,7 @@ MainWindow::MainWindow(Simulation* sim, const std::string& configDir,
QString::fromStdString(m_configDir) + "/../icons/buildings").toStdString();
m_selectedBuildingPanel = new SelectedBuildingPanel(sim, &sim->getConfig(), m_sidePanel);
m_buildButtonGrid = new BuildButtonGrid(sim, &sim->getConfig(), iconDir, itemsIconDir, m_sidePanel);
m_buildButtonGrid = new BuildButtonGrid(sim, &sim->getConfig(), iconDir, m_itemIcons.get(), m_sidePanel);
m_blueprintPanel = new BlueprintPanel(sim, &sim->getConfig(), m_sidePanel);
sideLayout->addWidget(m_selectedBuildingPanel, 1);
@@ -328,11 +332,7 @@ void MainWindow::handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent
bool autoOpenLayout = false;
std::string chosenSchematic;
// Item icons live beside the config dir, mirroring the buildings icon path
// (REQ-UI-ITEM-ICON, REQ-UI-BUILD-ICON).
const QString itemIconDir = QDir::cleanPath(
QString::fromStdString(m_configDir) + "/../icons/items");
RecipeSelectionDialog dialog(options, title, itemIconDir, this);
RecipeSelectionDialog dialog(options, title, m_itemIcons.get(), this);
if (dialog.exec() == QDialog::Accepted && dialog.getChosenId().has_value())
{
std::shared_ptr<SetRecipeCommand> command = std::make_shared<SetRecipeCommand>();

View File

@@ -27,6 +27,7 @@ class HeaderBar;
class SelectedBuildingPanel;
class BuildButtonGrid;
class BlueprintPanel;
class ItemIconCache;
class QCloseEvent;
class QResizeEvent;
@@ -68,6 +69,9 @@ private:
std::string m_configDir;
VisualsConfig m_visuals;
Simulation* m_sim;
// One per-item icon cache for the whole window (REQ-UI-ITEM-ICON): the header,
// build grid, world view, and recipe dialog all rasterize the same SVGs.
std::unique_ptr<ItemIconCache> m_itemIcons;
GameWorldView* m_gameWorldView;
HeaderBar* m_headerBar;
SelectedBuildingPanel* m_selectedBuildingPanel;

View File

@@ -111,14 +111,12 @@ namespace
RecipeSelectionDialog::RecipeSelectionDialog(
const std::vector<RecipeSelectionOption>& options,
const QString& title, const QString& itemIconDir, QWidget* parent)
const QString& title, ItemIconCache* itemIcons, QWidget* parent)
: QDialog(parent)
{
setWindowTitle(title);
setModal(true);
ItemIconCache iconCache(itemIconDir);
QVBoxLayout* mainLayout = new QVBoxLayout(this);
QGridLayout* grid = new QGridLayout();
mainLayout->addLayout(grid);
@@ -131,9 +129,9 @@ RecipeSelectionDialog::RecipeSelectionDialog(
QPushButton* button = new QPushButton(this);
// Icon-only when the produced item has an icon (REQ-UI-RECIPE-ICON); otherwise
// fall back to the caption. The name stays reachable via the tooltip.
if (!option.iconItemId.empty() && iconCache.hasIcon(option.iconItemId))
if (!option.iconItemId.empty() && itemIcons->hasIcon(option.iconItemId))
{
button->setIcon(QIcon(iconCache.getPixmap(
button->setIcon(QIcon(itemIcons->getPixmap(
option.iconItemId, kOptionIconSize.width())));
button->setIconSize(kOptionIconSize);
}

View File

@@ -12,6 +12,7 @@
struct GameConfig;
class Simulation;
class QPushButton;
class ItemIconCache;
// One selectable entry in the recipe/schematic selection dialog
// (REQ-UI-SELECT-BUTTON). The "(None)" entry uses an empty id.
@@ -43,11 +44,11 @@ class RecipeSelectionDialog : public QDialog
Q_OBJECT
public:
// itemIconDir is the directory holding per-item icon SVGs (REQ-UI-ITEM-ICON);
// used to render recipe options icon-only (REQ-UI-RECIPE-ICON). Options whose
// item has no icon file fall back to their caption text.
// itemIcons is the window-wide per-item icon cache (REQ-UI-ITEM-ICON); used to
// render recipe options icon-only (REQ-UI-RECIPE-ICON). Options whose item has
// no icon file fall back to their caption text. Not owned.
RecipeSelectionDialog(const std::vector<RecipeSelectionOption>& options,
const QString& title, const QString& itemIconDir,
const QString& title, ItemIconCache* itemIcons,
QWidget* parent = nullptr);
std::optional<std::string> getChosenId() const;