Skip to content

Support mid-drag modifier changes#733

Open
brentyi wants to merge 3 commits into
mainfrom
brent/drag-modifier-switch
Open

Support mid-drag modifier changes#733
brentyi wants to merge 3 commits into
mainfrom
brent/drag-modifier-switch

Conversation

@brentyi

@brentyi brentyi commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

We previously ignored modifier changes that happened mid-drag.

New behavior for modifier changes:

  • End event is triggered for the old modifier.
  • Start event is triggered for the new modifier.
  • Future update event + end events are bound to the new modifier.

cc mujocolab/mjviser#21, @kevinzakka if this sounds reasonable?

brentyi added 3 commits June 26, 2026 06:04
Previously a scene-node drag froze its (button, modifier) combo at
drag-start: changing the held modifier mid-gesture had no effect. This
partitions a physical drag into one logical segment per combo. When the
held modifier changes, the client ends the current segment and -- if the
new combo is bound -- starts a fresh one under it, preserving the grab
geometry (plane, grab point, instance) so the drag continues without a
visual jump; only the addressed callback set changes.

Modifier changes are picked up from both pointermove and (for stationary
switches) window keydown/keyup during the drag. A switch to an unbound
combo drops the gesture into a dormant gap (no messages) so callbacks see
properly paired start/end per bound combo; re-entering a bound combo
starts a new segment.

The wire message already carries `modifier`, so this is client-side
segmentation plus docs -- no message-schema change. `SceneNodeDragEvent`
keeps a single `modifier` field that now identifies the active segment.

Tests: planModifierTransition unit cases (vitest), a server-side
multi-segment dispatch/bookkeeping test, and an e2e test that adds Shift
mid-drag and asserts the cmd/ctrl segment ends before release.
Covers the gap surfaced in review: switching to an unbound modifier combo
mid-drag must suspend the gesture (dormant), not end it. Asserts the
active segment ends on the switch (before release), and that re-entering
a bound combo resumes with a fresh segment within the same button press --
which only holds if dormant is a suspend rather than a teardown. Both
transitions are driven by key events with the pointer stationary, so this
also exercises the keydown/keyup path.
Existing tests each exercised a single modifier switch. These add
sequences within one drag:
- server: one drag cycling cmd/ctrl -> cmd/ctrl+shift -> cmd/ctrl ->
  cmd/ctrl+shift (re-entering both combos), asserting each callback sees
  clean ordered start...end pairs and the active-drag map fully releases.
- e2e: one continuous press cycling cmd/ctrl -> cmd/ctrl+shift ->
  dormant -> cmd/ctrl+shift -> cmd/ctrl, asserting exactly two segments
  per combo across repeated switching, re-entry, and resume-after-dormant.
  Uses async callbacks so the counters settle deterministically.

@kevinzakka kevinzakka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks Brent!

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