diff --git a/src/ui/BuildButtonGrid.cpp b/src/ui/BuildButtonGrid.cpp index ebb9cc0..8cc0cd5 100644 --- a/src/ui/BuildButtonGrid.cpp +++ b/src/ui/BuildButtonGrid.cpp @@ -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( - QString::fromStdString(itemsIconDir))) + , m_itemIcons(itemIcons) { QGridLayout* layout = new QGridLayout(this); layout->setSpacing(4); diff --git a/src/ui/BuildButtonGrid.h b/src/ui/BuildButtonGrid.h index 7a51d56..2a60727 100644 --- a/src/ui/BuildButtonGrid.h +++ b/src/ui/BuildButtonGrid.h @@ -32,11 +32,12 @@ class BuildButtonGrid : public QWidget, public: // iconDir is the directory holding the per-building ".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 m_itemIcons; + ItemIconCache* m_itemIcons; // Not owned; lives in MainWindow. std::vector m_types; std::vector m_buttons; std::map m_costs; diff --git a/src/ui/GameWorldView.cpp b/src/ui/GameWorldView.cpp index 63b8d18..7b22056 100644 --- a/src/ui/GameWorldView.cpp +++ b/src/ui/GameWorldView.cpp @@ -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(QDir::cleanPath( - QString::fromStdString(configDir) + "/../icons/items")); - m_renderTimer = new QTimer(this); m_renderTimer->setInterval(16); connect(m_renderTimer, &QTimer::timeout, this, &GameWorldView::onFrame); diff --git a/src/ui/GameWorldView.h b/src/ui/GameWorldView.h index 93de251..a211668 100644 --- a/src/ui/GameWorldView.h +++ b/src/ui/GameWorldView.h @@ -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 m_buildingIcons; - // Per-item icon cache (REQ-UI-ITEM-ICON), loaded from /../icons/items. - // Shared draw path for belt and port items; pixmaps are cached per target size. - std::unique_ptr 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; diff --git a/src/ui/HeaderBar.cpp b/src/ui/HeaderBar.cpp index c221082..1a2458b 100644 --- a/src/ui/HeaderBar.cpp +++ b/src/ui/HeaderBar.cpp @@ -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( - QString::fromStdString(itemsIconDir))) + , m_itemIcons(itemIcons) , m_sim(sim) { QHBoxLayout* layout = new QHBoxLayout(this); diff --git a/src/ui/HeaderBar.h b/src/ui/HeaderBar.h index 118e298..1bc7bea 100644 --- a/src/ui/HeaderBar.h +++ b/src/ui/HeaderBar.h @@ -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 m_speedButtons; - std::unique_ptr 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. diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index ae97fc8..6df9ad0 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -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( + 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 command = std::make_shared(); diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 37e8930..d5ea17f 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -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 m_itemIcons; GameWorldView* m_gameWorldView; HeaderBar* m_headerBar; SelectedBuildingPanel* m_selectedBuildingPanel; diff --git a/src/ui/RecipeSelectionDialog.cpp b/src/ui/RecipeSelectionDialog.cpp index d3fd19b..9dbfe1d 100644 --- a/src/ui/RecipeSelectionDialog.cpp +++ b/src/ui/RecipeSelectionDialog.cpp @@ -111,14 +111,12 @@ namespace RecipeSelectionDialog::RecipeSelectionDialog( const std::vector& 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); } diff --git a/src/ui/RecipeSelectionDialog.h b/src/ui/RecipeSelectionDialog.h index 6ad9914..66b9243 100644 --- a/src/ui/RecipeSelectionDialog.h +++ b/src/ui/RecipeSelectionDialog.h @@ -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& options, - const QString& title, const QString& itemIconDir, + const QString& title, ItemIconCache* itemIcons, QWidget* parent = nullptr); std::optional getChosenId() const;