Always show shipyard layout preview and Configure button, disabled until schematic selected

This commit is contained in:
2026-07-13 20:43:14 +02:00
parent 42a23ae42a
commit ee1ee84f4f
5 changed files with 58 additions and 11 deletions

View File

@@ -259,7 +259,7 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
### Module UI ### Module UI
- REQ-MOD-UI-PREVIEW: When a schematic is selected in a shipyard's selected building panel, a small non-interactive **ship layout preview** widget is shown below the schematic selection button (REQ-UI-SELECT-BUTTON). The preview renders the ship's layout grid at a reduced scale: buildable cells without a module are shown as white, non-buildable cells are shown as black, and cells occupied by a module are shown in that module's `fill_color` with the module's `glyph` character. Below the preview, a "Configure" button is shown. - REQ-MOD-UI-PREVIEW: For a selected shipyard (operational building or construction site), the selected building panel always shows a small non-interactive **ship layout preview** widget below the schematic selection button (REQ-UI-SELECT-BUTTON) and a "Configure" button below the preview. Both are **disabled while no schematic is selected**, and enabled once one is; the preview then shows an empty placeholder in place of a layout grid. When a schematic is selected, the preview renders the ship's layout grid at a reduced scale: buildable cells without a module are shown as white, non-buildable cells are shown as black, and cells occupied by a module are shown in that module's `fill_color` with the module's `glyph` character. For non-shipyard buildings, neither the preview nor the "Configure" button is shown.
- REQ-MOD-UI-DIALOG: Clicking the "Configure" button opens the **layout configuration dialog** as a modal. While the dialog is open, the game is paused (speed set to 0×). On close, the game speed is restored to what it was before the dialog was opened. - REQ-MOD-UI-DIALOG: Clicking the "Configure" button opens the **layout configuration dialog** as a modal. While the dialog is open, the game is paused (speed set to 0×). On close, the game speed is restored to what it was before the dialog was opened.
The dialog contains: The dialog contains:

View File

@@ -49,8 +49,8 @@ MainWindow::MainWindow(Simulation* sim, const std::string& configDir,
m_sidePanel = new QWidget(this); m_sidePanel = new QWidget(this);
QVBoxLayout* sideLayout = new QVBoxLayout(m_sidePanel); QVBoxLayout* sideLayout = new QVBoxLayout(m_sidePanel);
sideLayout->setContentsMargins(0, 0, 0, 0); sideLayout->setContentsMargins(1, 1, 1, 1);
sideLayout->setSpacing(0); sideLayout->setSpacing(1);
m_selectedBuildingPanel = new SelectedBuildingPanel(sim, &sim->config(), m_sidePanel); m_selectedBuildingPanel = new SelectedBuildingPanel(sim, &sim->config(), m_sidePanel);
m_buildButtonGrid = new BuildButtonGrid(&sim->config(), m_sidePanel); m_buildButtonGrid = new BuildButtonGrid(&sim->config(), m_sidePanel);

View File

@@ -566,11 +566,21 @@ void SelectedBuildingPanel::updateShipyardLayoutWidgets(
const std::string& recipeId, const std::string& recipeId,
const std::optional<ShipLayoutConfig>& shipLayout) const std::optional<ShipLayoutConfig>& shipLayout)
{ {
const ShipDef* shipDef = (type == BuildingType::Shipyard) // The preview and Configure button are shipyard-only controls; hide them
? findShipDef(recipeId) // entirely for other building types.
: nullptr; if (type != BuildingType::Shipyard)
{
m_layoutPreview->hide();
m_configureLayoutBtn->hide();
return;
}
if (shipDef && !shipDef->layout.empty()) const ShipDef* shipDef = findShipDef(recipeId);
const bool hasSchematic = shipDef && !shipDef->layout.empty();
// Always show the preview and Configure button for a shipyard; they are only
// enabled once a schematic is selected (REQ-MOD-UI-PREVIEW).
if (hasSchematic)
{ {
ShipLayoutConfig layout; ShipLayoutConfig layout;
if (shipLayout.has_value()) if (shipLayout.has_value())
@@ -579,14 +589,16 @@ void SelectedBuildingPanel::updateShipyardLayoutWidgets(
} }
m_layoutPreview->setShipAndLayout( m_layoutPreview->setShipAndLayout(
shipDef->layout, layout, &m_config->modules.modules); shipDef->layout, layout, &m_config->modules.modules);
m_layoutPreview->show();
m_configureLayoutBtn->show();
} }
else else
{ {
m_layoutPreview->hide(); m_layoutPreview->showPlaceholder();
m_configureLayoutBtn->hide();
} }
m_layoutPreview->setEnabled(hasSchematic);
m_configureLayoutBtn->setEnabled(hasSchematic);
m_layoutPreview->show();
m_configureLayoutBtn->show();
} }
const RecipeDef* SelectedBuildingPanel::findRecipe(const Building* b) const const RecipeDef* SelectedBuildingPanel::findRecipe(const Building* b) const

View File

@@ -8,6 +8,10 @@ namespace
const int kCellSize = 8; const int kCellSize = 8;
// Size of the empty placeholder box shown when no schematic is selected.
const int kPlaceholderWidth = 64;
const int kPlaceholderHeight = 40;
const ModuleDef* findModuleDef(const std::vector<ModuleDef>& modules, const ModuleDef* findModuleDef(const std::vector<ModuleDef>& modules,
const std::string& id) const std::string& id)
{ {
@@ -90,14 +94,28 @@ void ShipLayoutPreview::clear()
m_modules = nullptr; m_modules = nullptr;
m_rows = 0; m_rows = 0;
m_cols = 0; m_cols = 0;
m_placeholder = false;
setFixedSize(0, 0); setFixedSize(0, 0);
update(); update();
} }
void ShipLayoutPreview::showPlaceholder()
{
m_grid.clear();
m_placedModules.clear();
m_modules = nullptr;
m_rows = 0;
m_cols = 0;
m_placeholder = true;
setFixedSize(kPlaceholderWidth, kPlaceholderHeight);
update();
}
void ShipLayoutPreview::setShipAndLayout(const std::vector<std::string>& shipLayout, void ShipLayoutPreview::setShipAndLayout(const std::vector<std::string>& shipLayout,
const ShipLayoutConfig& layout, const ShipLayoutConfig& layout,
const std::vector<ModuleDef>* modules) const std::vector<ModuleDef>* modules)
{ {
m_placeholder = false;
m_modules = modules; m_modules = modules;
m_placedModules = layout.placedModules; m_placedModules = layout.placedModules;
m_rows = static_cast<int>(shipLayout.size()); m_rows = static_cast<int>(shipLayout.size());
@@ -156,6 +174,18 @@ void ShipLayoutPreview::setShipAndLayout(const std::vector<std::string>& shipLay
void ShipLayoutPreview::paintEvent(QPaintEvent* /*event*/) void ShipLayoutPreview::paintEvent(QPaintEvent* /*event*/)
{ {
if (m_placeholder)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, false);
const QRect box = rect().adjusted(0, 0, -1, -1);
painter.fillRect(box, QColor(40, 40, 40));
painter.setPen(QColor(128, 128, 128));
painter.drawRect(box);
painter.drawText(box, Qt::AlignCenter, tr("No schematic"));
return;
}
if (m_rows == 0 || m_cols == 0) if (m_rows == 0 || m_cols == 0)
{ {
return; return;

View File

@@ -20,6 +20,10 @@ public:
const std::vector<ModuleDef>* modules); const std::vector<ModuleDef>* modules);
void clear(); void clear();
// Shows an empty placeholder box (no ship layout) so the preview stays
// visible while no schematic is selected (REQ-MOD-UI-PREVIEW).
void showPlaceholder();
protected: protected:
void paintEvent(QPaintEvent* event) override; void paintEvent(QPaintEvent* event) override;
@@ -35,4 +39,5 @@ private:
const std::vector<ModuleDef>* m_modules; const std::vector<ModuleDef>* m_modules;
int m_rows; int m_rows;
int m_cols; int m_cols;
bool m_placeholder = false;
}; };