document the player-input design in architecture.md
The doc's job is the invariants that are easy to break, and this change added three of them that only existed as comments in the files enforcing them. The one that matters most is the first: adding a shortcut straight to InputMapper's switch is the natural next edit, it works, and it silently reintroduces exactly the drift the action table was built to prevent. Also records the two distinctions that cost the most to rediscover -- that an action can be available in a context the panel does not advertise it in, and that gesture state belongs to the shared mode rather than to an action, because it decides what other bindings mean. The ui target's contents were listed without the controls panel too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
This commit is contained in:
@@ -124,13 +124,31 @@ Within a single simulation tick, subsystems run in this fixed order. The order i
|
|||||||
Three product targets plus tests:
|
Three product targets plus tests:
|
||||||
|
|
||||||
- `lib/` — simulation + config. Depends on Qt Core + Qt Gui, toml++, tinyexpr. No QtWidgets.
|
- `lib/` — simulation + config. Depends on Qt Core + Qt Gui, toml++, tinyexpr. No QtWidgets.
|
||||||
- `ui/` — QtWidgets + `QOpenGLWidget` code: header bar, game world view, selection panel, build button bar. Depends on `lib` and on Qt's OpenGL widgets module.
|
- `ui/` — QtWidgets + `QOpenGLWidget` code: header bar, game world view, selection panel, build button bar, controls panel. Depends on `lib` and on Qt's OpenGL widgets module.
|
||||||
- `ui/selection/` — the selection panel's contents. `SelectionPanel` itself only arbitrates between the two selection categories, picks a card from the catalog (`SelectionContentFactory`), and hosts one at a time; each kind of selection has its own `SelectionContent` subclass assembled from shared parts (REQ-UI-SELECTION-CARD, REQ-UI-SELECTION-CONTENT).
|
- `ui/selection/` — the selection panel's contents. `SelectionPanel` itself only arbitrates between the two selection categories, picks a card from the catalog (`SelectionContentFactory`), and hosts one at a time; each kind of selection has its own `SelectionContent` subclass assembled from shared parts (REQ-UI-SELECTION-CARD, REQ-UI-SELECTION-CONTENT).
|
||||||
- `app/` — thin `main()` that creates the simulation, the UI, and wires them together. Depends on `ui`.
|
- `app/` — thin `main()` that creates the simulation, the UI, and wires them together. Depends on `ui`.
|
||||||
- `tests/` — Catch2 tests. Links only against `lib`.
|
- `tests/` — Catch2 tests. Links only against `lib`.
|
||||||
|
|
||||||
Directory discipline inside `lib/` keeps the internal sim/config seam clear; sim code must not reach into config parsing and vice versa.
|
Directory discipline inside `lib/` keeps the internal sim/config seam clear; sim code must not reach into config parsing and vice versa.
|
||||||
|
|
||||||
|
## Player Input
|
||||||
|
|
||||||
|
Every player control is declared once, in `lib/core/ControlAction.h`, and read by three consumers that must never disagree about it:
|
||||||
|
|
||||||
|
* **`ControlsPanel`** asks which actions apply and draws a row per action (REQ-UI-CONTROLS-CONTENT).
|
||||||
|
* **`InputMapper`** resolves a key press to an action and fires the event that action stands for.
|
||||||
|
* **`GameWorldView`** resolves a mouse gesture to an action and runs the branch that carries it out.
|
||||||
|
|
||||||
|
The file declares; it never performs. It holds no simulation access, fires no events, and names nothing — display strings live in `ui/ControlActionText.h`, which renders each badge from the binding the resolver actually matches, so a chip cannot claim a key that does nothing. What an action *does* stays in the widget that always did it: the drag state machines, hit-testing, and command enqueuing were not moved.
|
||||||
|
|
||||||
|
Three invariants are easy to break here:
|
||||||
|
|
||||||
|
* **Do not add a shortcut straight to `InputMapper`'s switch or `mousePressEvent`'s branches.** Add the action and its binding to the table; the handler switches on the resolved action. A binding added directly is invisible to the panel, which is the drift the table exists to prevent. (Build hotkeys and `F3`/`F4` are deliberate exceptions, documented in the header and in REQ-UI-CONTROLS-ACCURACY.)
|
||||||
|
* **Availability and display are different questions.** An action can be live in a context the panel does not advertise it in — `Ctrl`+click with an empty selection is the standing example. `isControlActionAvailable` answers the first, the per-context row lists answer the second, and `ControlActionTest` asserts the pairing that matters: every row's bindings resolve back to that row's action.
|
||||||
|
* **Gesture state is shared, not owned by an action.** Whether a belt drag is in progress decides what the right mouse button means, so it lives on `BuildModeController` where the resolver can see it — as does the hovered-transfer flag. `m_boxSelecting` is likewise one gesture serving two actions (box select and deconstruct area).
|
||||||
|
|
||||||
|
`ControlContext` is a plain snapshot rather than references to the live controllers, which is what keeps the rules testable without a world and stops an action reaching into the simulation: if a rule needs a fact, the fact is named in the struct and the caller supplies it. When bindings become player-configurable, only the binding tables in `ControlAction.cpp` turn from hard-coded data into loaded data.
|
||||||
|
|
||||||
## Belt Subsystem
|
## Belt Subsystem
|
||||||
|
|
||||||
Belts and splitters are their own specialized subsystem. Belt items are **not** entities — they are transient data flowing through the belt representation. They do not have identities that persist across ticks.
|
Belts and splitters are their own specialized subsystem. Belt items are **not** entities — they are transient data flowing through the belt representation. They do not have identities that persist across ticks.
|
||||||
|
|||||||
Reference in New Issue
Block a user