Skip to content

feat(input): add pressedPosition and target/currentTarget to pointer event data#3031

Open
cptbtptpbcptdtptp wants to merge 8 commits into
dev/2.0from
feat/pointer-event-target
Open

feat(input): add pressedPosition and target/currentTarget to pointer event data#3031
cptbtptpbcptdtptp wants to merge 8 commits into
dev/2.0from
feat/pointer-event-target

Conversation

@cptbtptpbcptdtptp

@cptbtptpbcptdtptp cptbtptpbcptdtptp commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Pointer.pressedPosition: captures screen position at pointer-down, useful for drag threshold and gesture recognition
  • PointerEventData.target: the deepest hit-tested entity (stays constant during bubble)
  • PointerEventData.currentTarget: the entity currently handling the event (changes during bubble traversal)

Aligns the event model with the DOM convention (event.target vs event.currentTarget).

Test plan

  • Existing tests pass (pnpm run test)
  • Verify target stays as the deepest entity during bubble; currentTarget changes per handler
  • Verify pressedPosition is captured at pointer-down and does not change during drag

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added pressed-position tracking to pointer interactions so the original press coordinates remain stable during subsequent moves.
    • Pointer and UI event payloads now report both the deepest hit target and the current handling target during bubbling.
    • Improved click dispatch across UI elements to bubble consistently through the event path.
  • Bug Fixes
    • More accurate enter/exit and click behavior when transitioning between entities in pointer and UI flows.
  • Tests
    • Added coverage for pressed-position behavior, event target/currentTarget propagation, and pointer event data pool garbage collection.

…event data

- Pointer.pressedPosition: captures screen position at pointer-down, useful
  for drag threshold and gesture recognition
- PointerEventData.target: the deepest hit-tested entity (stays constant
  during bubble)
- PointerEventData.currentTarget: the entity currently handling the event
  (changes during bubble traversal)

This aligns the event model with the DOM convention where target is the
originating element and currentTarget is the listener host.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4c6fc68f-30f9-4ff6-87cb-be2d9d8c5587

📥 Commits

Reviewing files that changed from the base of the PR and between f24b5e3 and 7b2a4d4.

📒 Files selected for processing (8)
  • packages/core/src/Engine.ts
  • packages/core/src/input/InputManager.ts
  • packages/core/src/input/pointer/Pointer.ts
  • packages/core/src/input/pointer/PointerManager.ts
  • packages/core/src/input/pointer/emitter/PhysicsPointerEventEmitter.ts
  • packages/ui/src/input/UIPointerEventEmitter.ts
  • tests/src/core/input/InputManager.test.ts
  • tests/src/ui/UIEvent.test.ts
💤 Files with no reviewable changes (1)
  • packages/core/src/input/pointer/Pointer.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/core/src/input/pointer/emitter/PhysicsPointerEventEmitter.ts
  • packages/ui/src/input/UIPointerEventEmitter.ts

Walkthrough

This PR enriches pointer events with entity context information throughout the event pipeline. Pointer gains pressedPosition to track press location; PointerEventData adds target and currentTarget properties; event emitters refactor to thread entity references through event creation and bubbling workflows; and garbage collection infrastructure is added to reclaim event data pools.

Changes

Pointer Event Entity Context Tracking

Layer / File(s) Summary
Pointer Event Data Contracts
packages/core/src/input/pointer/Pointer.ts, packages/core/src/input/pointer/PointerEventData.ts
Pointer adds pressedPosition: Vector2 field and removes _addEmitters() method; PointerEventData adds target and currentTarget entity properties with disposal cleanup.
Core Event Data Factory with Entity Context
packages/core/src/input/pointer/emitter/PointerEventEmitter.ts
_createEventData() signature changes from (pointer) to (pointer, target, currentTarget = target), populating event entity references and world position from hit result.
Pointer Down Position Capture
packages/core/src/input/pointer/PointerManager.ts
On "pointerdown", pointer.pressedPosition is set from the event coordinates after phase transition.
Emitter Registration Refactoring
packages/core/src/input/pointer/PointerManager.ts
Pointer emitter instantiation extracted into private _addEmitters() helper that conditionally adds PhysicsPointerEventEmitter and appends registered emitters.
Physics Emitter Event Creation with Entities
packages/core/src/input/pointer/emitter/PhysicsPointerEventEmitter.ts
All event data creation calls now pass entity context to _createEventData() for drag, down, up, leave, and raycast enter/exit events.
UI Event Bubbling and Raycast State Management
packages/ui/src/input/UIPointerEventEmitter.ts
_updateRaycast() accepts nullable renderer and creates per-entity event data for enter/exit handlers; click dispatch uses _bubble() which updates currentTarget per entity; _composedPath() accepts nullable element.
Garbage Collection Infrastructure
packages/core/src/input/pointer/PointerManager.ts, packages/core/src/input/InputManager.ts, packages/core/src/Engine.ts
New _gc() methods added to enable garbage collection of pointer event data pools during engine cycles.
Tests: Pressed Position and GC Pool Behavior
tests/src/core/input/InputManager.test.ts, tests/src/ui/UIEvent.test.ts
Tests verify pressedPosition remains fixed after pointerdown, engine._pendingGC() reclaims pool entries, and target/currentTarget track through bubbling hierarchy.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Poem

🐰 A pointer pressed, its position now keeps trace,
While events flow through targets, bubbling with grace,
Each entity marked on the path that they take,
Hit-tested, pooled, and collected—no event's opaque!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: introducing pressedPosition to Pointer and target/currentTarget to PointerEventData for enhanced event tracking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/pointer-event-target

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.61017% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.18%. Comparing base (97d8d61) to head (7b2a4d4).
⚠️ Report is 6 commits behind head on dev/2.0.

Files with missing lines Patch % Lines
packages/ui/src/input/UIPointerEventEmitter.ts 77.77% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           dev/2.0    #3031      +/-   ##
===========================================
+ Coverage    77.31%   79.18%   +1.86%     
===========================================
  Files          914      914              
  Lines       101723   101688      -35     
  Branches     10437    11214     +777     
===========================================
+ Hits         78647    80520    +1873     
+ Misses       22892    20981    -1911     
- Partials       184      187       +3     
Flag Coverage Δ
unittests 79.18% <96.61%> (+1.86%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/input/pointer/PointerManager.ts`:
- Line 248: The code in PointerManager sets pointer.pressedPosition from
pointer.position (which was updated to latestEvent before the event loop), so a
batched pointerdown can record wrong coordinates; update the pointerdown branch
to copy coordinates from the current event (use event.position / event.clientX/Y
or the per-event coordinate object) instead of pointer.position, i.e., in the
pointerdown handling logic inside PointerManager, assign pressedPosition from
the event's coordinates rather than from pointer.position to ensure the down
location reflects that specific event.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: befad6e6-c8c8-4571-86f8-25f80a822e1f

📥 Commits

Reviewing files that changed from the base of the PR and between de75496 and f24b5e3.

📒 Files selected for processing (6)
  • packages/core/src/input/pointer/Pointer.ts
  • packages/core/src/input/pointer/PointerEventData.ts
  • packages/core/src/input/pointer/PointerManager.ts
  • packages/core/src/input/pointer/emitter/PhysicsPointerEventEmitter.ts
  • packages/core/src/input/pointer/emitter/PointerEventEmitter.ts
  • packages/ui/src/input/UIPointerEventEmitter.ts

Comment thread packages/core/src/input/pointer/PointerManager.ts Outdated
GuoLei1990

This comment was marked as outdated.

GuoLei1990

This comment was marked as outdated.

@GuoLei1990 GuoLei1990 marked this pull request as draft June 15, 2026 02:50
cptbtptpbcptdtptp and others added 7 commits June 16, 2026 18:31
`pressedPosition` was copied from `pointer.position`, which is already
overwritten with the frame's last event before the per-event loop runs,
so a pointerdown batched with later events in the same frame recorded the
wrong (last-event) coordinates. Compute it from the pointerdown event's
own coordinates instead, and add a regression test that batches
pointerdown + move in a single frame.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ty refs

ClearableObjectPool gains disposeUsedElements(), which drops each used
element's external references before resetting the index, so a peak-usage
frame no longer keeps dangling references alive in slots that later frames
don't reuse. PointerManager moves the pool reset out of _update's frame
start into the end of _firePointerScript via disposeUsedElements(), so each
frame's target/currentTarget entity refs are cleared instead of lingering.

Also simplify _createEventData(pointer, e, e) to (pointer, e) in the physics
and UI emitters (currentTarget defaults to target for non-bubbling fires),
and add a UI test asserting target stays the deepest hit entity while
currentTarget changes per bubble step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… dispose

The per-frame disposeUsedElements from the previous commit cleared
target/currentTarget too eagerly: code that saved an eventData and read it
in the same frame's onUpdate got null, and after pause the refs lingered
anyway. No major engine does per-frame dispose (DOM/Pixi/Unity all defer
cleanup to reuse). Revert to clear-on-reuse, and instead hook the pool into
engine gc (PointerManager._gc -> InputManager._gc -> Engine._gc) like the
render element pools, so peak memory and entity refs are reclaimed on gc
and at destroy. Add a test for pool reclamation on gc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pointer no longer threads the event-data pool through to emitter
constructors: it drops the ClearableObjectPool/PointerEventData imports and
the awkward _addEmitters<T extends new (pool) => ...> generic, keeping only
the _emitters array. PointerManager, which owns the pool, now creates the
emitters and injects the pool directly. No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pool is infrastructure that doesn't belong in PointerEventEmitter's
construction contract -- it had leaked into the emitter constructor, the
registry type, and the registerPointerEventEmitter decorator. Emitters now
have a no-arg constructor and PointerManager injects the pool into the
_pool field right after creating each emitter, so the registry and
decorator signatures simplify to `new () => Emitter`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pressedPosition regression test already pins this behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…structor"

This reverts commit 82b6f8b. Keep the constructor-injected pool for now;
the field-injection cleanup will land in a follow-up PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cptbtptpbcptdtptp cptbtptpbcptdtptp marked this pull request as ready for review June 17, 2026 02:59
GuoLei1990

This comment was marked as outdated.

@GuoLei1990 GuoLei1990 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

总结

本轮 HEAD 7b2a4d4 自上次 LGTM 以来未变(HEAD commit 时间 02:48 早于上轮 review 03:14),无新增 commit。逐项复核确认全部问题已闭环,故本次正式 APPROVE 解除历史 CHANGES_REQUESTED 门控。

  • 上轮 P0(pressedPosition 批处理帧捕获错误坐标)已在 4f2bbc9 真修复——采集挪进 _updatePointerInfo 的 per-event 循环、用 pointerdown 事件自身的 event.clientX/clientY 算(PointerManager 247-250),配套回归测试同帧批处理 pointerdown@(1,1)+pointermove@(3,3) 断言 pressedPosition==(2,2)position==(6,6),revert 即红,反向证伪成立。
  • target/currentTarget 的 DOM 语义忠实度已独立核验:引擎确有冒泡模型——UIPointerEventEmitter._composedPath 从命中元素(path[0],最深)沿 entity 父链构建到根 Canvas,_bubble 逐级触发同一事件并逐级改写 eventData.currentTarget = path[i]target 恒为 path[0]。故 currentTarget 有意义(非摆设),与 DOM event.target(事件起源)/event.currentTarget(当前 handler 宿主)一致。物理拾取无冒泡,target === currentTarget 经默认参数正确成立。
  • 事件数据池接入 engine gc 与既有 _renderElementPool 生命周期同构:garbageCollection() 仅清 _elements、不重置 _usedElementCount,但下一帧 _update 首句 eventPool.clear() 必在任何 get() 之前归零,不变量成立,非 bug。热路径无新增分配(池 get() 复用、Vector2.set 原地、整条冒泡路径复用同一 eventData)。

CI 全绿(build/lint/e2e/codecov-patch/codecov-project)。

问题

无阻塞问题(无 P0/P1/P2)。

[P3] PointerEventData.ts:16 — currentTarget 的 JSDoc 偏机制描述

当前注释描述的是实现机制而非对外契约。可选择更贴近 DOM 文档口径的写法,例如「冒泡过程中当前正在处理事件的实体」。纯文案、非阻塞,当前措辞准确且带句号,不影响合并。

简化建议

无。上轮 _createEventData(pointer, entity, entity) 双传简化已采纳。构造器注入 vs 字段注入池的反复是作者明确把字段注入 cleanup 推迟到 follow-up PR 的主动决策,当前构造器注入工作正常、无对外影响,无需处理。

LGTM,APPROVE。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants