Skip to content

Conversation

@myers
Copy link
Collaborator

@myers myers commented Oct 28, 2025

Hello! 您好

This PR builds on the work in #35 (HeadlessCanvas). It depends on BridgeEventBinding from #32.

This PR adds DOM-standard pointer capture API to enable proper event routing during drag-and-drop and similar interactions. When a pointer is captured, events are routed directly to the capturing component instead of using hit testing, allowing components to continue receiving events even when the pointer moves outside their bounds.

This is useful for implementing slider components where you want to track pointer movement even when the cursor goes outside the slider's bounds.

Changes:

  • Add setPointerCapture/releasePointerCapture/hasPointerCapture to RenderObject
  • Update SyntheticEventManager to check for captured pointers before hit testing
  • Support independent capture tracking for multiple pointers (multi-touch)
  • Add comprehensive test suite covering capture scenarios
  • Clean up BridgeEventBinding by removing deprecated element capture stubs

myers added 8 commits October 12, 2025 17:49
…actory

Separate frame scheduling concerns from canvas creation for better
architectural clarity and simpler custom integrations (e.g., WebXR).

Changes:
- Split IPlatformAdapter into two focused interfaces:
  * IFrameScheduler - Frame scheduling (scheduleFrame, onFrame)
  * ICanvasFactory - Canvas creation and management
- Update RenderCanvas to depend only on IFrameScheduler
  * Rename parameter from platformAdapter to frameScheduler
  * RenderCanvas no longer depends on canvas creation methods
- Update createElement factory to accept IFrameScheduler
- Keep IPlatformAdapter as union of both for backward compatibility

Benefits:
- Single Responsibility Principle - each interface has one concern
- RenderCanvas only depends on what it needs (frame scheduling)
- WebXR/custom renderers can provide simple IFrameScheduler without
  implementing unused canvas creation methods
- No breaking changes - IPlatformAdapter still works as before
- Better foundation for testing and exotic platforms

Example WebXR usage:
  class XRFrameScheduler implements IFrameScheduler {
    scheduleFrame() { xrSession.requestAnimationFrame(...) }
    onFrame(cb) { ... }
  }
  new RenderCanvas(new XRFrameScheduler(xrSession))
Fix pointerenter and pointerleave events to fire on entire element
hierarchy, not just the direct target.

Previously, these events only fired at the primary target element,
which was incorrect. They should fire for all elements that are
entered or left during pointer movement.

Now properly tracks which elements were entered (in new path but not
old) and which were left (in old path but not new), firing appropriate
events for each.
This commit adds support for injecting custom event bindings into RenderCanvas
and introduces BridgeEventBinding for programmatic event injection.

Changes:
- Add binding parameter to RenderCanvas constructor (defaults to DOMEventBinding)
- Add binding parameter to createElement factory function
- Implement BridgeEventBinding for programmatic event injection
- Add NativeEventBinding.el property for element binding
- Add comprehensive test suite for BridgeEventBinding (12 tests)

Benefits:
- Enables WebXR controller event injection
- Clean interface using property setter pattern
This commit enables passing any canvas element (HTMLCanvasElement or OffscreenCanvas)
to RenderCanvas via constructor injection, following the established dependency injection
pattern (frameScheduler, binding).

Changes:
- Add canvas parameter to RenderCanvas constructor and createElement factory
- Expand el property type to accept both HTMLCanvasElement and OffscreenCanvas
- Update CanvasSurface to accept both HTMLCanvasElement and OffscreenCanvas
- Rename createOrUpdateEl → ensureCanvasSize (reflects actual behavior)
- Guard cursor management with instanceof check for HTMLCanvasElement
- Make drawFrame public for manual rendering control
- Add JSDoc documentation for constructor and drawFrame

Benefits:
- Clean dependency injection pattern consistent with other constructor parameters
- Manual frame control via public drawFrame method
- Cursor management gracefully degrades for OffscreenCanvas
- Backwards compatible (canvas parameter is optional)
This commit adds HeadlessCanvas, a React component for rendering Canvas UI to
OffscreenCanvas without mounting to the DOM.

Changes:
- Add HeadlessCanvas component with event injection API
- Accept user-provided OffscreenCanvas and custom frameScheduler
- Provide injectEvent and injectWheelEvent helpers via onReady callback
- Support onFrameEnd callback for texture copying
- Export HeadlessCanvas and related types from @canvas-ui/react

Benefits:
- Enables WebXR layer rendering (XRWebGLLayer texture targets)
- Manual frame control with custom schedulers
- Programmatic event injection for XR controllers
- No DOM coupling for headless environments

Use cases:
- WebXR UI panels rendered to quad layers
- Custom render loop integration
getBoundingClientRect() only exists on HTMLCanvasElement, not OffscreenCanvas.
Skip popup positioning for OffscreenCanvas since it has no DOM position.
…crolling

Implement proper wheel event bubbling behavior in RenderScrollView to enable
nested scrolling scenarios where parent scrollers can handle events when the
inner scroller reaches its scroll bounds.

Changes:
- RenderScrollView: only call preventDefault() when scroll actually occurs
  (when _setScrollOffset() returns true), allowing events to bubble to parent
  when at scroll bounds (top/bottom for vertical, left/right for horizontal)
- Add 6 comprehensive integration tests covering preventDefault behavior at
  all scroll bounds and nested ScrollView scenarios
- BridgeEventBinding: add preventDefault/stopPropagation mocks to wheel events
  for test support, plus stub pointer capture methods for compatibility

Tests verify:
- preventDefault called when scrolling within bounds
- preventDefault NOT called when at top/bottom/left/right bounds
- Nested ScrollViews: inner at bounds allows outer to scroll

All tests passing (10 passed: 4 existing + 6 new)
…tions

Adds DOM-standard pointer capture API to enable proper event routing during
drag-and-drop and similar interactions. When a pointer is captured, events
are routed directly to the capturing component instead of using hit testing,
allowing components to continue receiving events even when the pointer moves
outside their bounds.

Key changes:
- Add setPointerCapture/releasePointerCapture/hasPointerCapture to RenderObject
- Update SyntheticEventManager to check for captured pointers before hit testing
- Support independent capture tracking for multiple pointers (multi-touch)
- Add comprehensive test suite with 12 tests covering capture scenarios
- Clean up BridgeEventBinding by removing deprecated element capture stubs
@myers myers force-pushed the feat/pointer-capture-pr branch from 894f3c0 to 0600cf9 Compare October 28, 2025 13:36
@zhanghaocong
Copy link
Collaborator

Hi @myers

Seeing your high-quality work and interest in the project got us thinking—would you be interested in becoming a collaborator and joining the core team?

It would mean you get write access to the repo, so you can help us triage issues, review other PRs, and push your own branches directly. No pressure or major time commitment is expected, just more freedom to help out when you have the time and inspiration!

If that sounds good to you, just give me a shout here, and I'll send the invite over. If not, no worries at all! We're thrilled with your contribution either way and hope to see you around the project again soon. 😄

@zhanghaocong zhanghaocong self-requested a review October 29, 2025 14:39
@myers
Copy link
Collaborator Author

myers commented Oct 31, 2025

@zhanghaocong

If that sounds good to you, just give me a shout here, and I'll send the invite over. If not, no worries at all! We're thrilled with your contribution either way and hope to see you around the project again soon. 😄

Sure, I would be happy to be a collaborator and part of the team.

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