auto-open layout dialog on manual schematic change

This commit is contained in:
2026-07-13 20:47:37 +02:00
parent 9d28175b17
commit 698dd4d13d
3 changed files with 61 additions and 24 deletions

View File

@@ -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). - **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. - **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-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. - 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.

View File

@@ -220,33 +220,13 @@ void MainWindow::handleEvent(std::shared_ptr<const EscapeMenuRequestedEvent> /*e
} }
} }
void MainWindow::handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> event) void MainWindow::openShipLayoutDialog(BuildingId shipyardId,
const std::string& schematicId,
const ShipLayoutConfig& currentLayout)
{ {
const double prevSpeed = m_gameWorldView->gameSpeed(); const double prevSpeed = m_gameWorldView->gameSpeed();
m_gameWorldView->setGameSpeed(0.0); 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<ShipLayoutConfig>& layoutOpt =
b ? b->shipLayout : s->shipLayout;
ShipLayoutConfig currentLayout;
if (layoutOpt.has_value())
{
currentLayout = *layoutOpt;
}
std::set<std::string> unlockedModuleIds; std::set<std::string> unlockedModuleIds;
for (const ModuleDef& def : m_sim->config().modules.modules) for (const ModuleDef& def : m_sim->config().modules.modules)
{ {
@@ -265,7 +245,7 @@ void MainWindow::handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> e
{ {
std::shared_ptr<SetShipLayoutCommand> command = std::shared_ptr<SetShipLayoutCommand> command =
std::make_shared<SetShipLayoutCommand>(); std::make_shared<SetShipLayoutCommand>();
command->id = event->shipyardId; command->id = shipyardId;
command->layout = *dialog.result(); command->layout = *dialog.result();
EventManager::getInstance()->sendEventImmediately( EventManager::getInstance()->sendEventImmediately(
std::make_shared<CommandRequestedEvent>(command)); std::make_shared<CommandRequestedEvent>(command));
@@ -275,6 +255,31 @@ void MainWindow::handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> e
m_gameWorldView->resetFrameTimer(); m_gameWorldView->resetFrameTimer();
} }
void MainWindow::handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> 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<ShipLayoutConfig>& 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<const RecipeSelectionRequestedEvent> event) void MainWindow::handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent> event)
{ {
const double prevSpeed = m_gameWorldView->gameSpeed(); const double prevSpeed = m_gameWorldView->gameSpeed();
@@ -293,12 +298,18 @@ void MainWindow::handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent
} }
const BuildingType type = b ? b->type : s->type; const BuildingType type = b ? b->type : 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<RecipeSelectionOption> options = const std::vector<RecipeSelectionOption> options =
buildRecipeSelectionOptions(type, *m_sim, m_sim->config()); buildRecipeSelectionOptions(type, *m_sim, m_sim->config());
const QString title = (type == BuildingType::Shipyard) const QString title = (type == BuildingType::Shipyard)
? tr("Select Schematic") ? tr("Select Schematic")
: tr("Select Recipe"); : tr("Select Recipe");
bool autoOpenLayout = false;
std::string chosenSchematic;
RecipeSelectionDialog dialog(options, title, this); RecipeSelectionDialog dialog(options, title, this);
if (dialog.exec() == QDialog::Accepted && dialog.getChosenId().has_value()) if (dialog.exec() == QDialog::Accepted && dialog.getChosenId().has_value())
{ {
@@ -307,10 +318,27 @@ void MainWindow::handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent
command->recipeId = *dialog.getChosenId(); command->recipeId = *dialog.getChosenId();
EventManager::getInstance()->sendEventImmediately( EventManager::getInstance()->sendEventImmediately(
std::make_shared<CommandRequestedEvent>(command)); std::make_shared<CommandRequestedEvent>(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->setGameSpeed(prevSpeed);
m_gameWorldView->resetFrameTimer(); 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<const GameOverEvent> /*event*/) void MainWindow::handleEvent(std::shared_ptr<const GameOverEvent> /*event*/)

View File

@@ -15,6 +15,7 @@
#include "WinEvent.h" #include "WinEvent.h"
#include "RecipeSelectionRequestedEvent.h" #include "RecipeSelectionRequestedEvent.h"
#include "SchematicChoicesAvailableEvent.h" #include "SchematicChoicesAvailableEvent.h"
#include "ShipLayout.h"
#include "ShipLayoutBlueprint.h" #include "ShipLayoutBlueprint.h"
#include "Tick.h" #include "Tick.h"
#include "VisualsConfig.h" #include "VisualsConfig.h"
@@ -57,6 +58,12 @@ private:
void handleEvent(std::shared_ptr<const EscapeMenuRequestedEvent> event) override; void handleEvent(std::shared_ptr<const EscapeMenuRequestedEvent> event) override;
void handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> event) override; void handleEvent(std::shared_ptr<const LayoutDialogRequestedEvent> event) override;
void handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent> event) override; void handleEvent(std::shared_ptr<const RecipeSelectionRequestedEvent> 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(); void layoutPanels();
private: private: