stop the layout dialog opening for a shipyard with no schematic

Clearing a shipyard with "(None)" opened the layout configuration dialog on a
grid of no cells. The auto-open guard asked only whether the chosen id differs
from the current one, and the "(None)" option carries the empty id, which
differs from every schematic; ShipLayoutDialog then found no ship def and
derived a 0x0 grid. Predates this branch -- 698dd4d, 2026-07-13.

The question all three sites were answering by hand is now one:
findLayoutShipDef() returns the ship to configure a layout against, or nullptr
when no schematic is set, the id names no ship, or the ship defines no grid.
The auto-open path and the LayoutDialogRequestedEvent handler now ask it
before opening, and ShipyardContent asks it instead of spelling the same test
out for the preview and the Configure button.

The event handler was reachable only through a button ShipyardContent already
disables, so guarding it changes nothing today; it is guarded because the
dialog's precondition belongs to the dialog's entry, not to the widget that
happens to be the only caller.

REQ-MOD-UI-AUTO-DIALOG said "differs" and left clearing implicit, which is the
reading the code took. It now says clearing opens nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
This commit is contained in:
2026-08-17 22:50:14 +02:00
parent 5968e5f40a
commit 01aa1a08b0
5 changed files with 63 additions and 8 deletions

View File

@@ -690,3 +690,31 @@ TEST_CASE("calculateShipStats: maneuvering_thrusters additive maneuvering_accele
const float expected = (base_mpss + 10.0f) / tileSize;
CHECK(stats.maneuveringAcceleration_tpss == Approx(expected));
}
// The precondition every entry to the layout configuration dialog checks
// (REQ-MOD-UI-DIALOG, REQ-MOD-UI-AUTO-DIALOG, REQ-MOD-UI-PREVIEW). The empty id is the
// one that used to slip through: "(None)" clears a shipyard and differs from whatever
// was set, which is not the same as naming a schematic to configure.
TEST_CASE("ShipsConfig: a layout is configurable only for a ship that has one",
"[modules][config]")
{
const GameConfig cfg = loadTestConfig();
const ShipDef* interceptor = cfg.ships.findLayoutShipDef("interceptor");
REQUIRE(interceptor != nullptr);
CHECK(interceptor->id == "interceptor");
CHECK_FALSE(interceptor->layout.empty());
// The "(None)" option's id, and an id naming no ship at all.
CHECK(cfg.ships.findLayoutShipDef("") == nullptr);
CHECK(cfg.ships.findLayoutShipDef("no_such_ship") == nullptr);
// A ship that exists but defines no grid has nothing to place modules on either,
// where plain findShipDef still finds it.
ShipsConfig gridless;
ShipDef def;
def.id = "hull_only";
gridless.ships.push_back(def);
CHECK(gridless.findShipDef("hull_only") != nullptr);
CHECK(gridless.findLayoutShipDef("hull_only") == nullptr);
}