From 698dd4d13da4caa5533364dc2cac3c644e3c595e Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Mon, 13 Jul 2026 20:47:37 +0200 Subject: [PATCH] auto-open layout dialog on manual schematic change --- docs/requirements.md | 2 ++ src/ui/MainWindow.cpp | 76 +++++++++++++++++++++++++++++-------------- src/ui/MainWindow.h | 7 ++++ 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/docs/requirements.md b/docs/requirements.md index fc845b4..e40011c 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -269,6 +269,8 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des - **Right** (below the grid): The layout blueprint panel (see REQ-MOD-UI-BLUEPRINT-PANEL through REQ-MOD-UI-BLUEPRINT-FILE-LOAD). - **Bottom**: A "Confirm" button and a "Cancel" button. Cancel discards all changes made in this dialog session and closes the dialog. Confirm applies the changes: the shipyard's configured layout is updated, the required materials and cycle time displayed in the selected building panel are recalculated, and the ship layout preview is refreshed. +- REQ-MOD-UI-AUTO-DIALOG: When the player selects a schematic for a shipyard (operational building or construction site) through the schematic selection dialog (REQ-UI-SELECT-BUTTON), and the chosen schematic **differs** from the shipyard's current schematic, the layout configuration dialog (REQ-MOD-UI-DIALOG) opens automatically and immediately once the selection dialog closes — exactly as if the player had then clicked "Configure". Re-selecting the schematic already set does not reopen the dialog. This auto-open applies only to the manual schematic selection dialog; schematic changes applied via the copy-settings gesture (REQ-BLD-COPY-CONFIG) or blueprint placement (REQ-UI-BLUEPRINT-PLACE) do **not** auto-open the dialog. The player may still cancel the auto-opened dialog (REQ-MOD-UI-DIALOG), which leaves the newly selected schematic in place with its default empty layout; the "Configure" button (REQ-MOD-UI-PREVIEW) remains available to open the dialog again later. + - REQ-MOD-UI-MODULE-TOOLTIP: Each module selection button in the layout configuration dialog (REQ-MOD-UI-DIALOG) shows a hover tooltip with the descriptive text defined for that module type in `modules.toml` (the optional per-module tooltip field). If a module type defines no tooltip text, its button shows no tooltip. The "Remove" button is not a module type and has no config-defined tooltip. - REQ-MOD-UI-STATS-PANEL: The **ship stats panel** in the layout configuration dialog shows the stats of the currently configured ship layout as they would be computed, incorporating all passive module modifiers per REQ-MOD-STAT-CALC. The panel updates in real time whenever modules are placed or removed in the layout grid. diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index dcaf520..4f8b448 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -220,33 +220,13 @@ void MainWindow::handleEvent(std::shared_ptr /*e } } -void MainWindow::handleEvent(std::shared_ptr event) +void MainWindow::openShipLayoutDialog(BuildingId shipyardId, + const std::string& schematicId, + const ShipLayoutConfig& currentLayout) { const double prevSpeed = m_gameWorldView->gameSpeed(); m_gameWorldView->setGameSpeed(0.0); - // A construction site has no Building yet; fall back to its site record so - // the shipyard layout can be configured before it is built (REQ-BLD-SITE-CONFIG). - const Building* b = m_sim->buildings().findBuilding(event->shipyardId); - const ConstructionSite* s = - b ? nullptr : m_sim->buildings().findSite(event->shipyardId); - if (!b && !s) - { - m_gameWorldView->setGameSpeed(prevSpeed); - m_gameWorldView->resetFrameTimer(); - return; - } - - const std::string& schematicId = b ? b->recipeId : s->recipeId; - const std::optional& layoutOpt = - b ? b->shipLayout : s->shipLayout; - - ShipLayoutConfig currentLayout; - if (layoutOpt.has_value()) - { - currentLayout = *layoutOpt; - } - std::set unlockedModuleIds; for (const ModuleDef& def : m_sim->config().modules.modules) { @@ -265,7 +245,7 @@ void MainWindow::handleEvent(std::shared_ptr e { std::shared_ptr command = std::make_shared(); - command->id = event->shipyardId; + command->id = shipyardId; command->layout = *dialog.result(); EventManager::getInstance()->sendEventImmediately( std::make_shared(command)); @@ -275,6 +255,31 @@ void MainWindow::handleEvent(std::shared_ptr e m_gameWorldView->resetFrameTimer(); } +void MainWindow::handleEvent(std::shared_ptr event) +{ + // A construction site has no Building yet; fall back to its site record so + // the shipyard layout can be configured before it is built (REQ-BLD-SITE-CONFIG). + const Building* b = m_sim->buildings().findBuilding(event->shipyardId); + const ConstructionSite* s = + b ? nullptr : m_sim->buildings().findSite(event->shipyardId); + if (!b && !s) + { + return; + } + + const std::string& schematicId = b ? b->recipeId : s->recipeId; + const std::optional& layoutOpt = + b ? b->shipLayout : s->shipLayout; + + ShipLayoutConfig currentLayout; + if (layoutOpt.has_value()) + { + currentLayout = *layoutOpt; + } + + openShipLayoutDialog(event->shipyardId, schematicId, currentLayout); +} + void MainWindow::handleEvent(std::shared_ptr event) { const double prevSpeed = m_gameWorldView->gameSpeed(); @@ -293,12 +298,18 @@ void MainWindow::handleEvent(std::shared_ptrtype : s->type; + // Captured as a copy: a queued command may drain during the modal dialog's + // event loop and reallocate the building vectors, so b/s must not be + // dereferenced after dialog.exec() returns. + const std::string oldSchematic = b ? b->recipeId : s->recipeId; const std::vector options = buildRecipeSelectionOptions(type, *m_sim, m_sim->config()); const QString title = (type == BuildingType::Shipyard) ? tr("Select Schematic") : tr("Select Recipe"); + bool autoOpenLayout = false; + std::string chosenSchematic; RecipeSelectionDialog dialog(options, title, this); if (dialog.exec() == QDialog::Accepted && dialog.getChosenId().has_value()) { @@ -307,10 +318,27 @@ void MainWindow::handleEvent(std::shared_ptrrecipeId = *dialog.getChosenId(); EventManager::getInstance()->sendEventImmediately( std::make_shared(command)); + + // REQ-MOD-UI-AUTO-DIALOG: picking a new schematic for a shipyard opens the + // layout configuration dialog immediately. Only on an actual change. + if (type == BuildingType::Shipyard && *dialog.getChosenId() != oldSchematic) + { + autoOpenLayout = true; + chosenSchematic = *dialog.getChosenId(); + } } m_gameWorldView->setGameSpeed(prevSpeed); m_gameWorldView->resetFrameTimer(); + + // The SetRecipeCommand above is queued (drains on a later frame) and clears + // the shipyard's layout, so open the dialog with the chosen schematic and an + // empty layout rather than reading the not-yet-updated building state. Speed + // is already restored so the helper snapshots the real speed to restore. + if (autoOpenLayout) + { + openShipLayoutDialog(event->buildingId, chosenSchematic, ShipLayoutConfig{}); + } } void MainWindow::handleEvent(std::shared_ptr /*event*/) diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 4f23bc4..4719192 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -15,6 +15,7 @@ #include "WinEvent.h" #include "RecipeSelectionRequestedEvent.h" #include "SchematicChoicesAvailableEvent.h" +#include "ShipLayout.h" #include "ShipLayoutBlueprint.h" #include "Tick.h" #include "VisualsConfig.h" @@ -57,6 +58,12 @@ private: void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; + + // Opens the shipyard layout configuration dialog for the given schematic and + // current layout, applying the result via SetShipLayoutCommand (REQ-MOD-UI-DIALOG). + void openShipLayoutDialog(BuildingId shipyardId, + const std::string& schematicId, + const ShipLayoutConfig& currentLayout); void layoutPanels(); private: