From 904dc9db6f4f5f9626548365bb424a04a2a762e5 Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Sun, 19 Jul 2026 20:18:20 +0200 Subject: [PATCH] deselect build tool when it becomes unaffordable and fix stale enabled button BuildButtonGrid now holds the Simulation and reads the building block stock on demand, subscribing to BuildingBlocksChangedEvent itself instead of being driven through MainWindow. When the currently selected build tool can no longer be afforded, builder mode is exited so the button does not stay selected or linger enabled after exit. m_activeIndex is now a std::optional. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc --- src/ui/BuildButtonGrid.cpp | 51 ++++++++++++++++++++++++++++---------- src/ui/BuildButtonGrid.h | 18 +++++++++++--- src/ui/MainWindow.cpp | 8 +----- src/ui/MainWindow.h | 5 +--- 4 files changed, 54 insertions(+), 28 deletions(-) diff --git a/src/ui/BuildButtonGrid.cpp b/src/ui/BuildButtonGrid.cpp index 1e01449..98c08ae 100644 --- a/src/ui/BuildButtonGrid.cpp +++ b/src/ui/BuildButtonGrid.cpp @@ -12,12 +12,13 @@ #include "DisplayName.h" #include "EventManager.h" #include "ExitBuilderModeRequestedEvent.h" +#include "Simulation.h" -BuildButtonGrid::BuildButtonGrid(const GameConfig* config, QWidget* parent) +BuildButtonGrid::BuildButtonGrid(Simulation* sim, const GameConfig* config, QWidget* parent) : QWidget(parent) + , m_sim(sim) , m_config(config) - , m_activeIndex(-1) { QGridLayout* layout = new QGridLayout(this); layout->setSpacing(4); @@ -79,24 +80,42 @@ BuildButtonGrid::~BuildButtonGrid() unregisterForEvents(); } -void BuildButtonGrid::updateAffordability(int buildingBlocks) +void BuildButtonGrid::updateAffordability() { + const int buildingBlocks = m_sim->getBuildingBlocksStock(); + + // If the currently selected tool can no longer be afforded, exit builder mode + // before recomputing button states so it does not stay selected. Clearing the + // active index first lets the loop below disable the now-unaffordable button. + if (m_activeIndex) + { + const BuildingType activeType = m_types[*m_activeIndex]; + const std::map::const_iterator it = m_costs.find(activeType); + const int cost = (it != m_costs.end()) ? it->second : 0; + if (buildingBlocks < cost) + { + clearActiveButton(); + EventManager::getInstance()->sendEventImmediately( + std::make_shared()); + } + } + for (std::size_t i = 0; i < m_buttons.size(); ++i) { const BuildingType type = m_types[i]; const std::map::const_iterator it = m_costs.find(type); const int cost = (it != m_costs.end()) ? it->second : 0; - m_buttons[i]->setEnabled(buildingBlocks >= cost || m_activeIndex == static_cast(i)); + m_buttons[i]->setEnabled(buildingBlocks >= cost || m_activeIndex == i); } } void BuildButtonGrid::clearActiveButton() { - if (m_activeIndex >= 0 && m_activeIndex < static_cast(m_buttons.size())) + if (m_activeIndex) { - m_buttons[static_cast(m_activeIndex)]->setChecked(false); + m_buttons[*m_activeIndex]->setChecked(false); } - m_activeIndex = -1; + m_activeIndex.reset(); } void BuildButtonGrid::onBuildButton(int index) @@ -105,8 +124,9 @@ void BuildButtonGrid::onBuildButton(int index) { return; } + const std::size_t idx = static_cast(index); - if (m_activeIndex == index) + if (m_activeIndex == idx) { clearActiveButton(); EventManager::getInstance()->sendEventImmediately( @@ -114,15 +134,15 @@ void BuildButtonGrid::onBuildButton(int index) return; } - if (m_activeIndex >= 0 && m_activeIndex < static_cast(m_buttons.size())) + if (m_activeIndex) { - m_buttons[static_cast(m_activeIndex)]->setChecked(false); + m_buttons[*m_activeIndex]->setChecked(false); } - m_activeIndex = index; - m_buttons[static_cast(index)]->setChecked(true); + m_activeIndex = idx; + m_buttons[idx]->setChecked(true); EventManager::getInstance()->sendEventImmediately( - std::make_shared(m_types[static_cast(index)])); + std::make_shared(m_types[idx])); } void BuildButtonGrid::handleEvent(std::shared_ptr /*event*/) @@ -130,6 +150,11 @@ void BuildButtonGrid::handleEvent(std::shared_ptr clearActiveButton(); } +void BuildButtonGrid::handleEvent(std::shared_ptr /*event*/) +{ + updateAffordability(); +} + void BuildButtonGrid::handleEvent(std::shared_ptr event) { m_demolishButton->setChecked(event->active); diff --git a/src/ui/BuildButtonGrid.h b/src/ui/BuildButtonGrid.h index ec81fa4..7bfbc0d 100644 --- a/src/ui/BuildButtonGrid.h +++ b/src/ui/BuildButtonGrid.h @@ -1,46 +1,56 @@ #pragma once #include +#include #include #include #include "BuilderModeExitedEvent.h" #include "BuildHotkeyPressedEvent.h" +#include "BuildingBlocksChangedEvent.h" #include "BuildingType.h" #include "DemolishModeChangedEvent.h" #include "EventHandler.h" #include "GameConfig.h" class QPushButton; +class Simulation; class BuildButtonGrid : public QWidget, public CombinedEventHandler + BuildHotkeyPressedEvent, + BuildingBlocksChangedEvent> { Q_OBJECT public: - BuildButtonGrid(const GameConfig* config, QWidget* parent = nullptr); + BuildButtonGrid(Simulation* sim, const GameConfig* config, QWidget* parent = nullptr); ~BuildButtonGrid() override; - void updateAffordability(int buildingBlocks); void clearActiveButton(); private: + // Re-evaluates which build buttons are enabled from the current building block + // stock (read from the simulation). If the currently selected tool can no longer + // be afforded, it exits builder mode so the button does not stay selected. + void updateAffordability(); + void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; + void handleEvent(std::shared_ptr event) override; private slots: void onBuildButton(int index); private: + Simulation* m_sim; const GameConfig* m_config; std::vector m_types; std::vector m_buttons; std::map m_costs; - int m_activeIndex; + std::optional m_activeIndex; QPushButton* m_demolishButton; }; diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 1b24069..c6c53ff 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -14,7 +14,6 @@ #include "BlueprintPanel.h" #include "BuildButtonGrid.h" -#include "BuildingBlocksChangedEvent.h" #include "BuildingSystem.h" #include "Command.h" #include "CommandRequestedEvent.h" @@ -53,7 +52,7 @@ MainWindow::MainWindow(Simulation* sim, const std::string& configDir, sideLayout->setSpacing(1); m_selectedBuildingPanel = new SelectedBuildingPanel(sim, &sim->getConfig(), m_sidePanel); - m_buildButtonGrid = new BuildButtonGrid(&sim->getConfig(), m_sidePanel); + m_buildButtonGrid = new BuildButtonGrid(sim, &sim->getConfig(), m_sidePanel); m_blueprintPanel = new BlueprintPanel(sim, &sim->getConfig(), m_sidePanel); sideLayout->addWidget(m_selectedBuildingPanel, 1); @@ -151,11 +150,6 @@ void MainWindow::layoutPanels() m_dimOverlay->setGeometry(0, 0, totalW, totalH); } -void MainWindow::handleEvent(std::shared_ptr event) -{ - m_buildButtonGrid->updateAffordability(event->blocks); -} - void MainWindow::handleEvent(std::shared_ptr event) { const double prevSpeed = m_gameWorldView->getGameSpeed(); diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index dfa5632..37e8930 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -6,7 +6,6 @@ #include -#include "BuildingBlocksChangedEvent.h" #include "BuildingId.h" #include "EscapeMenuRequestedEvent.h" #include "EventHandler.h" @@ -32,8 +31,7 @@ class QCloseEvent; class QResizeEvent; class MainWindow : public QWidget, - public CombinedEventHandler event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override;