From 53af44db0477c3dc9ff6794a0b16ae9add9d0b28 Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Wed, 8 Jul 2026 19:07:14 +0200 Subject: [PATCH] Fix shipyard layout preview/button not showing until re-selection Selecting a ship schematic in the shipyard left the layout preview and Configure Layout button hidden until the building was deselected and re-selected. The recipe change is applied via a queued command that only drains on a later frame, so the immediate rebuild() in onSelectRecipeClicked() still saw the old (empty) recipe and hid both widgets. The per-tick refreshBuffers() path then updated the preview's data but never set its visibility -- that was only ever done in buildSingle() -- so the widgets stayed hidden until a re-selection re-ran buildSingle(). Extract the shipyard preview/button show-hide-and-populate logic into updateShipyardLayoutWidgets() and call it from both buildSingle() and refreshBuffers(), so the per-tick refresh becomes authoritative for visibility and the widgets appear on the first tick after the command drains. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN --- src/ui/SelectedBuildingPanel.cpp | 67 ++++++++++++++++---------------- src/ui/SelectedBuildingPanel.h | 3 ++ 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/ui/SelectedBuildingPanel.cpp b/src/ui/SelectedBuildingPanel.cpp index 2b548f0..826ced3 100644 --- a/src/ui/SelectedBuildingPanel.cpp +++ b/src/ui/SelectedBuildingPanel.cpp @@ -294,38 +294,12 @@ void SelectedBuildingPanel::buildSingle(BuildingId id) } m_recipeSelectButton->show(); - if (type == BuildingType::Shipyard && !recipeId.empty()) - { - const ShipDef* sDef = findShipDef(recipeId); - if (sDef && !sDef->layout.empty()) - { - ShipLayoutConfig layout; - if (shipLayout.has_value()) - { - layout = *shipLayout; - } - m_layoutPreview->setShipAndLayout( - sDef->layout, layout, &m_config->modules.modules); - m_layoutPreview->show(); - m_configureLayoutBtn->show(); - } - else - { - m_layoutPreview->hide(); - m_configureLayoutBtn->hide(); - } - } - else - { - m_layoutPreview->hide(); - m_configureLayoutBtn->hide(); - } + updateShipyardLayoutWidgets(type, recipeId, shipLayout); } else { m_recipeSelectButton->hide(); - m_layoutPreview->hide(); - m_configureLayoutBtn->hide(); + updateShipyardLayoutWidgets(type, recipeId, shipLayout); } // Belt "Clear" removes items from a live belt tile; a construction site has @@ -530,15 +504,38 @@ void SelectedBuildingPanel::refreshBuffers(const Building* b) m_buffersLabel->setText(bufText); - if (b->type == BuildingType::Shipyard && shipDef && !shipDef->layout.empty()) + // The recipe/schematic is applied via a queued command that only drains on a + // later frame, so the per-tick refresh must own the shipyard preview and the + // Configure Layout button's visibility; otherwise they stay hidden until the + // building is re-selected (which re-runs buildSingle). + updateShipyardLayoutWidgets(b->type, b->recipeId, b->shipLayout); +} + +void SelectedBuildingPanel::updateShipyardLayoutWidgets( + BuildingType type, + const std::string& recipeId, + const std::optional& shipLayout) +{ + const ShipDef* shipDef = (type == BuildingType::Shipyard) + ? findShipDef(recipeId) + : nullptr; + + if (shipDef && !shipDef->layout.empty()) { ShipLayoutConfig layout; - if (b->shipLayout.has_value()) + if (shipLayout.has_value()) { - layout = *b->shipLayout; + layout = *shipLayout; } m_layoutPreview->setShipAndLayout( shipDef->layout, layout, &m_config->modules.modules); + m_layoutPreview->show(); + m_configureLayoutBtn->show(); + } + else + { + m_layoutPreview->hide(); + m_configureLayoutBtn->hide(); } } @@ -647,9 +644,11 @@ void SelectedBuildingPanel::onSelectRecipeClicked() return; } // The emit is synchronous: MainWindow pauses the game, runs the modal - // selection dialog, applies the chosen recipe/schematic, and restores the - // speed before this returns. rebuild() then refreshes the button caption, - // tooltip, preview, and buffers for the new selection. + // selection dialog, and restores the speed before this returns. The chosen + // recipe/schematic is only *enqueued* as a command, though, and drains on a + // later frame -- so this rebuild() still sees the old recipe. The per-tick + // refreshBuffers() path picks up the new schematic (and shows the layout + // preview + Configure Layout button) once the command has been applied. EventManager::getInstance()->sendEventImmediately( std::make_shared(m_singleBuildingId)); rebuild(); diff --git a/src/ui/SelectedBuildingPanel.h b/src/ui/SelectedBuildingPanel.h index 1f6ceb9..11fb9c6 100644 --- a/src/ui/SelectedBuildingPanel.h +++ b/src/ui/SelectedBuildingPanel.h @@ -64,6 +64,9 @@ private: void buildSingle(BuildingId id); void buildMulti(const std::vector& ids); void refreshBuffers(const Building* b); + void updateShipyardLayoutWidgets(BuildingType type, + const std::string& recipeId, + const std::optional& shipLayout); void buildSplitterFilters(const std::optional& info); const RecipeDef* findRecipe(const Building* b) const; const ShipDef* findShipDef(const std::string& id) const;