stop hovering when the cursor points at no tile

A cursor resting on a panel or outside the window kept whatever it last
pointed at: the ghost, the tunnel preview, the deconstruct tint all stayed
put, because "not hovering" was not a state the build mode could hold. Make
both ghost tiles optional, clear the hover with them, and re-derive it when
the cursor comes back or a mode is entered.

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 15:19:22 +02:00
parent 9490a96e12
commit b2148f00ac
7 changed files with 167 additions and 31 deletions

View File

@@ -288,6 +288,66 @@ TEST_CASE("The effective builder type follows the resolved tunnel end", "[buildm
REQUIRE(controller.getTunnelPartnerTile() == QPoint(5, 5));
}
TEST_CASE("Clearing the hover drops everything the cursor pointed at", "[buildmode]")
{
// A cursor that leaves the world hovers nothing, so the ghost and its resolved
// tunnel end go with it, while the mode itself stays active (REQ-BLD-GHOST).
BuildModeController controller;
controller.enterBuilderMode(BuildingType::TunnelEntry);
controller.setGhostTile(QPoint(7, 2));
controller.setGhostValidity(true);
controller.setTunnelGhost(BuildingType::TunnelExit, QPoint(5, 2));
controller.clearHover();
REQUIRE(controller.isBuilderMode());
REQUIRE_FALSE(controller.getGhostTile().has_value());
REQUIRE_FALSE(controller.isGhostValid());
REQUIRE(controller.getEffectiveBuilderType() == BuildingType::TunnelEntry);
REQUIRE_FALSE(controller.getTunnelPartnerTile().has_value());
}
TEST_CASE("Clearing the hover keeps a belt drag's path", "[buildmode]")
{
// A drag holds the button and goes on hovering wherever the cursor travels, so
// nothing clears it short of releasing or cancelling (REQ-BLD-BELT-DRAG).
BuildModeController controller;
controller.enterBuilderMode(BuildingType::Belt);
controller.beginBeltDrag(QPoint(3, 4));
controller.setBeltDragPath({BeltPathTile{QPoint(3, 4), Rotation::East}});
controller.clearHover();
REQUIRE(controller.isDraggingBelt());
REQUIRE(controller.getBeltDragPath().size() == 1);
}
TEST_CASE("Clearing the hover drops the blueprint ghost and its transfer", "[buildmode]")
{
BuildModeController controller;
controller.enterBlueprintMode(makeBlueprint());
controller.setBlueprintGhostTile(QPoint(9, 9));
controller.setHoveredGhostTransfer(true);
controller.clearHover();
REQUIRE(controller.isBlueprintMode());
REQUIRE_FALSE(controller.getBlueprintGhostTile().has_value());
REQUIRE_FALSE(controller.isHoveredGhostTransfer());
}
TEST_CASE("Clearing the hover drops the deconstruct hover", "[buildmode]")
{
BuildModeController controller;
controller.toggleDeconstructMode();
controller.setDeconstructHoverBuildingId(BuildingId(4));
controller.clearHover();
REQUIRE(controller.isDeconstructMode());
REQUIRE_FALSE(controller.getDeconstructHoverBuildingId().has_value());
}
TEST_CASE("A non-tunnel builder ignores any resolved tunnel end", "[buildmode]")
{
BuildModeController controller;