Skip to content

Commit 9518425

Browse files
sudomakeinstallfinetjul
authored andcommitted
fix: Add fix for chorded mouse button handling
1 parent c6c0866 commit 9518425

1 file changed

Lines changed: 85 additions & 4 deletions

File tree

  • Sources/Rendering/Core/RenderWindowInteractor

Sources/Rendering/Core/RenderWindowInteractor/index.js

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ function vtkRenderWindowInteractor(publicAPI, model) {
104104
// Factor to apply on wheel spin.
105105
let wheelCoefficient = 1;
106106

107+
// Track mouse button bitmask for detecting chorded button interactions.
108+
// Per W3C Pointer Events spec §10, pointerdown/pointerup only fire for the
109+
// first press / last release. Chorded (additional) button changes while
110+
// another button is held are signaled via pointermove with updated `buttons`.
111+
let previousMouseButtons = 0;
112+
107113
// Public API methods
108114

109115
//----------------------------------------------------------------------
@@ -321,6 +327,7 @@ function vtkRenderWindowInteractor(publicAPI, model) {
321327
publicAPI.handlePointerLockChange
322328
);
323329
pointerCache.clear();
330+
previousMouseButtons = 0;
324331
};
325332

326333
publicAPI.unbindEvents = () => {
@@ -398,6 +405,7 @@ function vtkRenderWindowInteractor(publicAPI, model) {
398405
case 'mouse':
399406
default:
400407
publicAPI.handleMouseDown(event);
408+
previousMouseButtons = event.buttons;
401409
break;
402410
}
403411
};
@@ -417,9 +425,36 @@ function vtkRenderWindowInteractor(publicAPI, model) {
417425
publicAPI.handleTouchEnd(event);
418426
break;
419427
case 'mouse':
420-
default:
428+
default: {
429+
// Detect chorded button releases: when pointerup fires, additional
430+
// buttons may have been released simultaneously. Fire release events
431+
// for those chorded buttons before handling the primary button.
432+
const currentButtons = event.buttons;
433+
if (currentButtons !== previousMouseButtons) {
434+
const callData = {
435+
...getModifierKeysFor(event),
436+
position: getScreenEventPositionFor(event),
437+
deviceType: getDeviceTypeFor(event),
438+
};
439+
const wasLeft = (previousMouseButtons & 1) !== 0; // eslint-disable-line no-bitwise
440+
const wasRight = (previousMouseButtons & 2) !== 0; // eslint-disable-line no-bitwise
441+
const wasMiddle = (previousMouseButtons & 4) !== 0; // eslint-disable-line no-bitwise
442+
const isLeft = (currentButtons & 1) !== 0; // eslint-disable-line no-bitwise
443+
const isRight = (currentButtons & 2) !== 0; // eslint-disable-line no-bitwise
444+
const isMiddle = (currentButtons & 4) !== 0; // eslint-disable-line no-bitwise
445+
// Only fire for chorded buttons; the primary button (event.button)
446+
// is handled by handleMouseUp below.
447+
if (!isLeft && wasLeft && event.button !== 0)
448+
publicAPI.leftButtonReleaseEvent(callData);
449+
if (!isMiddle && wasMiddle && event.button !== 1)
450+
publicAPI.middleButtonReleaseEvent(callData);
451+
if (!isRight && wasRight && event.button !== 2)
452+
publicAPI.rightButtonReleaseEvent(callData);
453+
}
421454
publicAPI.handleMouseUp(event);
455+
previousMouseButtons = currentButtons;
422456
break;
457+
}
423458
}
424459
}
425460
};
@@ -434,9 +469,26 @@ function vtkRenderWindowInteractor(publicAPI, model) {
434469
publicAPI.handleTouchEnd(event);
435470
break;
436471
case 'mouse':
437-
default:
438-
publicAPI.handleMouseUp(event);
472+
default: {
473+
// Fire release events for all buttons that were held when the
474+
// pointer interaction was cancelled.
475+
const callData = {
476+
...getModifierKeysFor(event),
477+
position: getScreenEventPositionFor(event),
478+
deviceType: getDeviceTypeFor(event),
479+
};
480+
// eslint-disable-next-line no-bitwise
481+
if ((previousMouseButtons & 1) !== 0)
482+
publicAPI.leftButtonReleaseEvent(callData);
483+
// eslint-disable-next-line no-bitwise
484+
if ((previousMouseButtons & 4) !== 0)
485+
publicAPI.middleButtonReleaseEvent(callData);
486+
// eslint-disable-next-line no-bitwise
487+
if ((previousMouseButtons & 2) !== 0)
488+
publicAPI.rightButtonReleaseEvent(callData);
489+
previousMouseButtons = 0;
439490
break;
491+
}
440492
}
441493
}
442494
};
@@ -453,9 +505,38 @@ function vtkRenderWindowInteractor(publicAPI, model) {
453505
publicAPI.handleTouchMove(event);
454506
break;
455507
case 'mouse':
456-
default:
508+
default: {
509+
// Detect chorded button state changes (W3C Pointer Events spec §10).
510+
// pointerdown/pointerup only fire for the first/last button; additional
511+
// button presses/releases while another is held arrive as pointermove
512+
// events with updated `buttons` bitmask.
513+
const currentButtons = event.buttons;
514+
if (currentButtons !== previousMouseButtons) {
515+
const callData = {
516+
...getModifierKeysFor(event),
517+
position: getScreenEventPositionFor(event),
518+
deviceType: getDeviceTypeFor(event),
519+
};
520+
// buttons bitmask: 1=left, 2=right, 4=middle
521+
const wasLeft = (previousMouseButtons & 1) !== 0; // eslint-disable-line no-bitwise
522+
const wasRight = (previousMouseButtons & 2) !== 0; // eslint-disable-line no-bitwise
523+
const wasMiddle = (previousMouseButtons & 4) !== 0; // eslint-disable-line no-bitwise
524+
const isLeft = (currentButtons & 1) !== 0; // eslint-disable-line no-bitwise
525+
const isRight = (currentButtons & 2) !== 0; // eslint-disable-line no-bitwise
526+
const isMiddle = (currentButtons & 4) !== 0; // eslint-disable-line no-bitwise
527+
if (isLeft && !wasLeft) publicAPI.leftButtonPressEvent(callData);
528+
if (isMiddle && !wasMiddle)
529+
publicAPI.middleButtonPressEvent(callData);
530+
if (isRight && !wasRight) publicAPI.rightButtonPressEvent(callData);
531+
if (!isLeft && wasLeft) publicAPI.leftButtonReleaseEvent(callData);
532+
if (!isMiddle && wasMiddle)
533+
publicAPI.middleButtonReleaseEvent(callData);
534+
if (!isRight && wasRight) publicAPI.rightButtonReleaseEvent(callData);
535+
previousMouseButtons = currentButtons;
536+
}
457537
publicAPI.handleMouseMove(event);
458538
break;
539+
}
459540
}
460541
};
461542

0 commit comments

Comments
 (0)