From 808e0c6a7b962b76948509f5a43795391a4869fb Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Wed, 8 Jul 2026 20:46:26 +0200 Subject: [PATCH] Fix recipe button unclickable on construction site during play --- src/ui/SelectedBuildingPanel.cpp | 76 +++++++++++++++++++------------- src/ui/SelectedBuildingPanel.h | 13 +++++- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/ui/SelectedBuildingPanel.cpp b/src/ui/SelectedBuildingPanel.cpp index bc28196..d63a716 100644 --- a/src/ui/SelectedBuildingPanel.cpp +++ b/src/ui/SelectedBuildingPanel.cpp @@ -338,32 +338,7 @@ void SelectedBuildingPanel::buildSingle(BuildingId id) if (m_singleIsSite) { - QString progress; - if (s->completesAt == 0) - { - progress = tr("Queued"); - } - else - { - const BuildingDef* def = nullptr; - for (const BuildingDef& d : m_config->buildings.buildings) - { - if (d.type == s->type) { def = &d; break; } - } - if (def && def->constructionTimeSeconds > 0) - { - const Tick duration = secondsToTicks(def->constructionTimeSeconds); - const Tick elapsed = m_sim->currentTick() - (s->completesAt - duration); - const int pct = static_cast( - std::max(Tick(0), std::min(duration, elapsed)) * 100 / duration); - progress = tr("%1% complete").arg(pct); - } - else - { - progress = tr("Building..."); - } - } - m_buffersLabel->setText(progress); + refreshSiteProgress(s); } else { @@ -371,6 +346,36 @@ void SelectedBuildingPanel::buildSingle(BuildingId id) } } +void SelectedBuildingPanel::refreshSiteProgress(const ConstructionSite* s) +{ + QString progress; + if (s->completesAt == 0) + { + progress = tr("Queued"); + } + else + { + const BuildingDef* def = nullptr; + for (const BuildingDef& d : m_config->buildings.buildings) + { + if (d.type == s->type) { def = &d; break; } + } + if (def && def->constructionTimeSeconds > 0) + { + const Tick duration = secondsToTicks(def->constructionTimeSeconds); + const Tick elapsed = m_sim->currentTick() - (s->completesAt - duration); + const int pct = static_cast( + std::max(Tick(0), std::min(duration, elapsed)) * 100 / duration); + progress = tr("%1% complete").arg(pct); + } + else + { + progress = tr("Building..."); + } + } + m_buffersLabel->setText(progress); +} + void SelectedBuildingPanel::refreshBuffers(const Building* b) { const RecipeDef* recipe = findRecipe(b); @@ -562,7 +567,7 @@ const ShipDef* SelectedBuildingPanel::findShipDef(const std::string& id) const void SelectedBuildingPanel::handleEvent(std::shared_ptr /*event*/) { - refreshSelectionDisplay(); + refreshSelectionDisplay(RefreshReason::PeriodicTick); } void SelectedBuildingPanel::handleEvent( @@ -572,10 +577,10 @@ void SelectedBuildingPanel::handleEvent( // queued drain, not synchronously. When the game is paused no tick advances, // so TickAdvancedEvent never fires; refresh here too, otherwise the panel // would not reflect the change until the next tick or a re-selection. - refreshSelectionDisplay(); + refreshSelectionDisplay(RefreshReason::CommandApplied); } -void SelectedBuildingPanel::refreshSelectionDisplay() +void SelectedBuildingPanel::refreshSelectionDisplay(RefreshReason reason) { if (m_selectedEntity.has_value()) { @@ -600,7 +605,18 @@ void SelectedBuildingPanel::refreshSelectionDisplay() const ConstructionSite* s = m_sim->buildings().findSite(m_singleBuildingId); if (s) { - rebuild(); + // A periodic tick only advances construction progress, so update just the + // progress label. Rebuilding every tick would hide/re-show all widgets and + // cancel any in-progress click on the recipe button. An applied command + // may have changed the site's recipe/layout, so rebuild in that case. + if (reason == RefreshReason::CommandApplied) + { + rebuild(); + } + else + { + refreshSiteProgress(s); + } return; } buildEmpty(); diff --git a/src/ui/SelectedBuildingPanel.h b/src/ui/SelectedBuildingPanel.h index 35f514d..1aac58a 100644 --- a/src/ui/SelectedBuildingPanel.h +++ b/src/ui/SelectedBuildingPanel.h @@ -59,8 +59,18 @@ private slots: void onSplitterFilterChanged(); private: + // Why the selection display is being refreshed. A periodic tick only needs a + // lightweight content update (e.g. a construction site's progress label), + // whereas an applied player command may have changed the configuration and + // needs a full structural rebuild. + enum class RefreshReason + { + PeriodicTick, + CommandApplied + }; + void onSelectionChanged(const std::vector& ids); - void refreshSelectionDisplay(); + void refreshSelectionDisplay(RefreshReason reason); void rebuild(); void hideAllWidgets(); void clearContent(); @@ -68,6 +78,7 @@ private: void buildSingle(BuildingId id); void buildMulti(const std::vector& ids); void refreshBuffers(const Building* b); + void refreshSiteProgress(const ConstructionSite* s); void updateShipyardLayoutWidgets(BuildingType type, const std::string& recipeId, const std::optional& shipLayout);