extract SelectionController
The selection rules were written out twice - once for point clicks and once for box drags - and the two copies had already drifted. Unlike the earlier seams on this branch this is a real de-duplication, not a relocation. SelectionController owns all three categories and the rules for moving between them: buildings win over field objects, so selecting a building clears actors and debris and selecting either of those clears buildings, while actors and debris coexist. It also publishes the change events, so callers never emit by hand. A point click and a box drag now differ only in the SelectionMode they pass and in how many hits they pass. That names the difference that was previously implicit in two separate loops: Ctrl+click toggles, so dragging back over a selected building would deselect it, whereas Ctrl+box adds and never deselects. Hit-testing stays in GameWorldView, which is what keeps the controller free of any simulation dependency and therefore testable - including the published events, via a spy handler. That coverage is the point: these rules had none, and the two copies were the kind of thing that drifts silently. One behaviour difference, deliberate. Clearing the building selection was guarded by "only if non-empty" in three of the four places and unguarded in the fourth (the empty-box drag), which emitted a redundant SelectionChangedEvent with an empty list. The unified rule is the guarded one. Subscribers re-read from Simulation on every event, so dropping a redundant no-op refresh cannot change what any of them display. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
This commit is contained in:
@@ -52,6 +52,7 @@
|
||||
#include "EntitySelectionChangedEvent.h"
|
||||
#include "GameConfig.h"
|
||||
#include "Rotation.h"
|
||||
#include "SelectionController.h"
|
||||
#include "Tick.h"
|
||||
#include "TickDriver.h"
|
||||
#include "TunnelCompletion.h"
|
||||
@@ -247,21 +248,17 @@ private:
|
||||
void placeBlueprintAtTile(QPoint center);
|
||||
|
||||
std::optional<QVector2D> entityPosition(entt::entity entity) const;
|
||||
// Clears the debris selection, emitting an empty DebrisSelectionChangedEvent when
|
||||
// it was non-empty (REQ-UI-DEBRIS-CLICK-SELECT). Used when another selection
|
||||
// category takes over.
|
||||
void clearDebrisSelection();
|
||||
// Drops despawned or fully-collected debris from the selection and re-emits
|
||||
// when it changed (REQ-UI-DEBRIS-CLICK-SELECT). Called each frame from onFrame().
|
||||
// Drops despawned or fully-collected debris from the selection
|
||||
// (REQ-UI-DEBRIS-CLICK-SELECT). Called each frame from onFrame().
|
||||
void pruneDespawnedDebris();
|
||||
// Clears the actor selection, emitting an empty EntitySelectionChangedEvent when it was
|
||||
// non-empty (REQ-UI-ENTITY-CLICK-SELECT). Used when buildings take over.
|
||||
void clearEntitySelection();
|
||||
// Drops despawned or dead actors from the selection and re-emits when it changed
|
||||
// (REQ-UI-ENTITY-CLICK-SELECT). Called each frame from onFrame().
|
||||
// Drops despawned or dead actors from the selection (REQ-UI-ENTITY-CLICK-SELECT).
|
||||
// Called each frame from onFrame().
|
||||
void pruneDespawnedActors();
|
||||
// True if the given actor is part of the current actor selection.
|
||||
bool isEntitySelected(entt::entity entity) const;
|
||||
// Resolves a click into the selection it should produce, applying the category
|
||||
// precedence of REQ-UI-SELECTION-CATEGORIES; the controller owns what that then
|
||||
// does to the existing selection.
|
||||
void selectAtPoint(QPoint tile, QVector2D worldPos, bool additive);
|
||||
void selectInBox(bool additive);
|
||||
void stepSpeed(int delta);
|
||||
void placeAtTile(QPoint tile);
|
||||
|
||||
@@ -410,9 +407,10 @@ private:
|
||||
std::optional<BuildingId> m_deconstructHoverBuildingId;
|
||||
bool m_debugDraw;
|
||||
|
||||
std::vector<BuildingId> m_selectedBuildingIds;
|
||||
std::vector<entt::entity> m_selectedEntities;
|
||||
std::vector<entt::entity> m_selectedDebris;
|
||||
// Owns the selection across all three categories and the rules for moving
|
||||
// between them (REQ-UI-SELECTION-CATEGORIES), including publishing the change
|
||||
// events. This widget only resolves what was hit.
|
||||
SelectionController m_selection;
|
||||
bool m_boxSelecting;
|
||||
QPoint m_boxStartTile;
|
||||
QPoint m_boxCurrentTile;
|
||||
|
||||
Reference in New Issue
Block a user