Skip to content

Commit 714ab41

Browse files
committed
Add pointer drag input support and polish mapping tiles - PR_26140_095-add-pointer-drag-input-and-polish-mapping-tiles
1 parent 20ff455 commit 714ab41

11 files changed

Lines changed: 599 additions & 30 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# PR_26140_095 Pointer Drag Input Mapping Tiles Report
2+
3+
## Scope
4+
- Added reusable engine pointer drag support in `src/engine/input/PointerDragState.js` and bridged it through `InputService`.
5+
- Input Mapping V2 now surfaces engine pointer drag descriptors as mappable mouse-compatible bindings without changing the Input Mapping V2 schema.
6+
- Gamepad mapping tiles now display `Game Controller` plus the input detail on separate lines, with standard button names when `gamepad.mapping === "standard"` and `Button <number>` fallback otherwise.
7+
- Capture UI now includes pointer drag gesture buttons, an `<hr>` separator before bottom `Refresh Gamepads`, and second-click cancel for active keyboard, mouse, and gamepad capture.
8+
- Mapping tiles increased from 225x175 to 275x225 while preserving selected tile indication, combo display, delete behavior, fullscreen layout, and auto gamepad polling.
9+
10+
## Notes
11+
- `docs/pr/BUILD_PR.md` currently describes an unrelated Level 18 runtime hardening rebase. The inline PR_26140_095 request was used as the source of truth.
12+
- No schemas were changed.
13+
- Sample JSON was not touched.
14+
- Workspace V2 launch behavior was preserved.
15+
16+
## Playwright Impact
17+
Yes. This PR changes runtime input behavior and Input Mapping V2 UI interactions.
18+
19+
Coverage added/updated:
20+
- pointer drag descriptors are surfaced in Engine Input Sources and Capture.
21+
- mouse drag release and rectangle gestures can be mapped.
22+
- gamepad tiles display `Game Controller` plus input detail on separate lines.
23+
- standard gamepad button names display for standard-mapped controllers.
24+
- unknown gamepad buttons fall back to `Button <number>`.
25+
- `<hr>` appears immediately before bottom `Refresh Gamepads`.
26+
- clicking an active Capture button cancels capture.
27+
- capture timeout still cancels capture.
28+
- mapping tile width and height are each increased by 50px.
29+
30+
## Validation
31+
- PASS: targeted JS syntax validation for changed engine input and Input Mapping V2 files.
32+
- PASS: engine import validation for `PointerDragState` and `InputService`.
33+
- PASS: targeted `InputService.test.mjs` run.
34+
- PASS: focused Input Mapping V2 Playwright rerun after locator stabilization.
35+
- PASS: `npm run test:workspace-v2` final run, 61 tests passed.
36+
- PASS: `git diff --check` with line-ending warnings only.
37+
- PASS: Playwright V8 coverage generated at `docs/dev/reports/playwright_v8_coverage_report.txt`; changed runtime JS coverage guardrail reports no warnings.
38+
39+
## Manual Validation
40+
1. Open `tools/input-mapping-v2/index.html`.
41+
2. Confirm Capture shows keyboard, mouse, pointer drag gesture buttons, detected gamepad buttons when available, an `<hr>`, and `Refresh Gamepads` at the bottom.
42+
3. Click `Capture Keyboard`, click it again, and confirm capture cancels and highlight clears.
43+
4. Create a mapping tile, click `Mouse Primary Drag Rectangle`, and confirm the tile shows `Mouse` / `Primary Drag Rectangle`.
44+
5. With a standard mapped controller, capture a known button and confirm the tile shows `Game Controller` plus the standard button name.
45+
6. Confirm no sample JSON validation or full samples smoke test is required for this PR.
46+
47+
## Full Samples Smoke
48+
Skipped per PR instructions. This change is scoped to engine input support and Input Mapping V2/Workspace V2 validation.
49+
50+
## Artifacts
51+
- `docs/dev/reports/codex_review.diff`
52+
- `docs/dev/reports/codex_changed_files.txt`
53+
- `docs/dev/reports/pointer_drag_input_mapping_tiles_report.md`
54+
- `docs/dev/reports/playwright_v8_coverage_report.txt`
55+
- `docs/dev/reports/coverage_changed_js_guardrail.txt`
56+
- `docs/dev/commit_comment.txt`
57+
- `docs/dev/codex_commands.md`
58+
- `tmp/PR_26140_095-add-pointer-drag-input-and-polish-mapping-tiles_delta.zip`

src/engine/input/InputService.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ InputService.js
77
import KeyboardState from './KeyboardState.js';
88
import MouseState from './MouseState.js';
99
import GamepadState from './GamepadState.js';
10+
import PointerDragState from './PointerDragState.js';
1011
import InputMap from './InputMap.js';
1112

1213
export default class InputService {
@@ -15,13 +16,15 @@ export default class InputService {
1516
keyboard = null,
1617
mouse = null,
1718
gamepads = null,
19+
pointerDrag = null,
1820
inputMap = null,
1921
getGamepads = null,
2022
} = {}) {
2123
this.target = target;
2224
this.keyboard = keyboard ?? new KeyboardState();
2325
this.mouse = mouse ?? new MouseState();
2426
this.gamepads = gamepads ?? new GamepadState();
27+
this.pointerDrag = pointerDrag ?? new PointerDragState();
2528
this.inputMap = inputMap ?? new InputMap();
2629
this.getGamepadsFromNavigator = getGamepads ?? (() => {
2730
if (typeof navigator === 'undefined' || typeof navigator.getGamepads !== 'function') {
@@ -35,12 +38,17 @@ export default class InputService {
3538
this.mousePosition = { x: 0, y: 0 };
3639
this.mouseDelta = { x: 0, y: 0 };
3740
this.isAttached = false;
41+
this.pointerEventsAvailable = typeof PointerEvent !== 'undefined';
3842

3943
this.onKeyDown = this.onKeyDown.bind(this);
4044
this.onKeyUp = this.onKeyUp.bind(this);
4145
this.onMouseMove = this.onMouseMove.bind(this);
4246
this.onMouseDown = this.onMouseDown.bind(this);
4347
this.onMouseUp = this.onMouseUp.bind(this);
48+
this.onPointerDown = this.onPointerDown.bind(this);
49+
this.onPointerMove = this.onPointerMove.bind(this);
50+
this.onPointerUp = this.onPointerUp.bind(this);
51+
this.onPointerCancel = this.onPointerCancel.bind(this);
4452
this.onBlur = this.onBlur.bind(this);
4553
}
4654

@@ -54,6 +62,10 @@ export default class InputService {
5462
this.target.addEventListener('mousemove', this.onMouseMove);
5563
this.target.addEventListener('mousedown', this.onMouseDown);
5664
this.target.addEventListener('mouseup', this.onMouseUp);
65+
this.target.addEventListener('pointerdown', this.onPointerDown);
66+
this.target.addEventListener('pointermove', this.onPointerMove);
67+
this.target.addEventListener('pointerup', this.onPointerUp);
68+
this.target.addEventListener('pointercancel', this.onPointerCancel);
5769
this.target.addEventListener('blur', this.onBlur);
5870
this.isAttached = true;
5971
}
@@ -68,6 +80,10 @@ export default class InputService {
6880
this.target.removeEventListener('mousemove', this.onMouseMove);
6981
this.target.removeEventListener('mousedown', this.onMouseDown);
7082
this.target.removeEventListener('mouseup', this.onMouseUp);
83+
this.target.removeEventListener('pointerdown', this.onPointerDown);
84+
this.target.removeEventListener('pointermove', this.onPointerMove);
85+
this.target.removeEventListener('pointerup', this.onPointerUp);
86+
this.target.removeEventListener('pointercancel', this.onPointerCancel);
7187
this.target.removeEventListener('blur', this.onBlur);
7288
this.isAttached = false;
7389
this.reset();
@@ -94,6 +110,7 @@ export default class InputService {
94110
this.keyboard.reset();
95111
this.mouse.reset();
96112
this.gamepads.reset();
113+
this.pointerDrag.reset();
97114
}
98115

99116
setInputMap(inputMap) {
@@ -131,6 +148,7 @@ export default class InputService {
131148
return {
132149
keyboard: this.keyboard.getSnapshot(),
133150
mouse: this.mouse.getSnapshot(),
151+
pointerDrag: this.pointerDrag.getSnapshot(),
134152
gamepads: this.gamepads.getGamepads(),
135153
actions: this.getActionSnapshot(),
136154
};
@@ -152,6 +170,18 @@ export default class InputService {
152170
return this.mouse.isPressed(button);
153171
}
154172

173+
getPointerDragSnapshot() {
174+
return this.pointerDrag.getSnapshot();
175+
}
176+
177+
getPointerDragDescriptors() {
178+
return this.pointerDrag.getDescriptors();
179+
}
180+
181+
getPointerDragDescriptor(binding) {
182+
return this.pointerDrag.getDescriptor(binding);
183+
}
184+
155185
getGamepad(index) {
156186
return this.gamepads.getGamepad(index);
157187
}
@@ -169,6 +199,9 @@ export default class InputService {
169199
}
170200

171201
onMouseMove(event) {
202+
if (!this.pointerEventsAvailable) {
203+
this.pointerDrag.pointerMove(event);
204+
}
172205
const eventTarget = event?.target ?? null;
173206
const isCanvasTarget = typeof HTMLCanvasElement !== 'undefined'
174207
&& eventTarget instanceof HTMLCanvasElement;
@@ -185,16 +218,39 @@ export default class InputService {
185218

186219
onMouseDown(event) {
187220
this.liveMouseButtonsDown.add(event.button ?? 0);
221+
if (!this.pointerEventsAvailable) {
222+
this.pointerDrag.pointerDown(event);
223+
}
188224
}
189225

190226
onMouseUp(event) {
191227
this.liveMouseButtonsDown.delete(event.button ?? 0);
228+
if (!this.pointerEventsAvailable) {
229+
this.pointerDrag.pointerUp(event);
230+
}
231+
}
232+
233+
onPointerDown(event) {
234+
this.pointerDrag.pointerDown(event);
235+
}
236+
237+
onPointerMove(event) {
238+
this.pointerDrag.pointerMove(event);
239+
}
240+
241+
onPointerUp(event) {
242+
this.pointerDrag.pointerUp(event);
243+
}
244+
245+
onPointerCancel(event) {
246+
this.pointerDrag.pointerCancel(event);
192247
}
193248

194249
onBlur() {
195250
this.liveKeysDown.clear();
196251
this.liveMouseButtonsDown.clear();
197252
this.mouseDelta = { x: 0, y: 0 };
253+
this.pointerDrag.reset();
198254
}
199255

200256
readConnectedGamepads() {

0 commit comments

Comments
 (0)