resolve mouse gestures through the action table too

This commit is contained in:
2026-08-07 18:47:43 +02:00
parent 1f754de431
commit 68a43a7d98

View File

@@ -74,6 +74,23 @@
namespace
{
// The action a left-button gesture triggers, with the Ctrl variant falling back to the
// plain one wherever nothing is bound to it. That fallback is what keeps Ctrl+click
// placing a building in builder mode and Ctrl+drag deconstructing an area: the modifier
// only means something where an action claims it (REQ-UI-CONTROLS-CONTENT).
ControlAction resolveLeftGesture(bool controlHeld, bool isDrag,
const ControlContext& context)
{
if (controlHeld)
{
const ControlAction action = resolveMouseAction(
isDrag ? MouseBinding::CtrlLeftDrag : MouseBinding::CtrlLeftClick, context);
if (action != ControlAction::None) { return action; }
}
return resolveMouseAction(isDrag ? MouseBinding::LeftDrag : MouseBinding::LeftClick,
context);
}
// Keep only the filter entries whose item type is currently unlocked
// (REQ-LOCK-UI-BLUEPRINT). An empty result means "accept all".
std::vector<ItemType> filterUnlockedItems(const std::vector<ItemType>& filter,
@@ -1142,33 +1159,47 @@ ControlContext GameWorldView::getControlContext() const
void GameWorldView::mousePressEvent(QMouseEvent* event)
{
const WorldCoordinates coordinates = getCoordinates();
const ControlContext context = getControlContext();
if (event->button() != Qt::LeftButton)
{
if (event->button() == Qt::RightButton)
{
if (m_buildMode.isBuilderMode() && m_buildMode.isDraggingBelt())
switch (resolveMouseAction(MouseBinding::RightClick, context))
{
// Cancel the in-progress belt drag without placing anything;
// stay in belt builder mode (REQ-BLD-BELT-DRAG).
case ControlAction::CancelBeltLine:
// Drop the in-progress path without placing anything; belt builder
// mode stays active (REQ-BLD-BELT-DRAG).
m_buildMode.cancelBeltDrag();
}
else if (m_buildMode.getMode() != BuildMode::None)
{
break;
case ControlAction::ExitMode:
m_buildMode.exitCurrentMode();
break;
default:
break;
}
}
return;
}
const QPoint tile = coordinates.widgetToTile(event->pos());
const bool controlHeld = (event->modifiers() & Qt::ControlModifier) != 0;
if (m_buildMode.isBuilderMode())
// A press begins the click gesture; whether it turns out to be a drag is settled on
// release, where the drag binding is resolved instead.
switch (resolveLeftGesture(controlHeld, /*isDrag*/ false, context))
{
if (m_buildMode.getBuilderType() == BuildingType::Belt)
case ControlAction::Place:
case ControlAction::ApplySettings:
if (m_buildMode.isBlueprintMode())
{
// Deferred placement: start the drag and show the path ghost; nothing
// is placed until release (REQ-BLD-BELT-DRAG).
placeBlueprintAtTile(tile);
}
else if (m_buildMode.getBuilderType() == BuildingType::Belt)
{
// Belts place by dragging, so the press only anchors the path and shows its
// ghost; nothing is placed until release, and a plain click is the one-tile
// case (REQ-BLD-BELT-DRAG).
m_buildMode.beginBeltDrag(tile);
m_cursorWorldPos = coordinates.widgetToWorld(event->pos());
recomputeBeltDragPath(tile);
@@ -1177,27 +1208,22 @@ void GameWorldView::mousePressEvent(QMouseEvent* event)
{
placeAtTile(tile);
}
}
else if (m_buildMode.isBlueprintMode())
{
placeBlueprintAtTile(tile);
}
else if (m_buildMode.isDeconstructMode())
{
break;
case ControlAction::ToggleDeconstruct:
// Start a deconstruct box drag; a plain click resolves as a 1x1 box on
// release (REQ-BLD-DECONSTRUCT-CLICK, REQ-BLD-DECONSTRUCT-BOX).
m_boxSelecting = true;
m_boxStartTile = tile;
m_boxCurrentTile = tile;
}
else
{
const bool ctrl = (event->modifiers() & Qt::ControlModifier) != 0;
break;
case ControlAction::Select:
case ControlAction::AddToSelection:
// Only a click that hit nothing starts a box drag. Starting one on a hit
// would re-resolve the same object as a 1x1 box on release and undo the
// click: a Ctrl+click would toggle the building off, then straight back on.
if (!selectAtPoint(tile, coordinates.widgetToWorld(event->pos()), ctrl))
if (!selectAtPoint(tile, coordinates.widgetToWorld(event->pos()), controlHeld))
{
// selectAtPoint has already cleared the selection unless Ctrl is
// preserving it for an additive drag.
@@ -1205,6 +1231,10 @@ void GameWorldView::mousePressEvent(QMouseEvent* event)
m_boxStartTile = tile;
m_boxCurrentTile = tile;
}
break;
default:
break;
}
}
@@ -1347,7 +1377,11 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
const std::vector<BuildingId> boxIds =
buildingsInBox(m_sim->getFactoryState(), m_boxStartTile, m_boxCurrentTile);
if (m_buildMode.isDeconstructMode())
const bool controlHeld = (event->modifiers() & Qt::ControlModifier) != 0;
const ControlAction dragAction =
resolveLeftGesture(controlHeld, /*isDrag*/ true, getControlContext());
if (dragAction == ControlAction::DeconstructArea)
{
const FactoryState& factory = m_sim->getFactoryState();
@@ -1409,7 +1443,9 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
return;
}
selectInBox((event->modifiers() & Qt::ControlModifier) != 0);
// A Ctrl box adds and never deselects, where a plain one replaces
// (REQ-UI-MULTI-SELECT).
selectInBox(dragAction == ControlAction::AddAreaToSelection);
}
}