Skip to content

Commit

Permalink
fix(handleWheel): mouse scrolling behavior for out-of-focus windows
Browse files Browse the repository at this point in the history
When rebinding events with a different container,
make sure all timeouts and state variables from
previous containter are cleared.
  • Loading branch information
jadh4v committed Jan 26, 2024
1 parent 4945167 commit dae07b6
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 26 deletions.
14 changes: 12 additions & 2 deletions Sources/Rendering/Core/RenderWindowInteractor/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export interface vtkRenderWindowInteractor extends vtkObject {
*
* @default null
*/
getContainer(): HTMLElement;
getContainer(): Nullable<HTMLElement>;

/**
*
Expand Down Expand Up @@ -827,6 +827,13 @@ export interface vtkRenderWindowInteractor extends vtkObject {
*/
rotateEvent(args: any): any;

/**
* Add an HTMLElement as the new container for the interactor.
* All events will be bound to this new container.
* Any old container will be removed along with its listeners.
*/
setContainer(container: Nullable<HTMLElement>): boolean;

/**
* Turn on/off the automatic repositioning of lights as the camera moves.
* @param lightFollowCamera
Expand Down Expand Up @@ -992,12 +999,15 @@ export interface vtkRenderWindowInteractor extends vtkObject {

/**
*
* @param container
* @param container kept for backward compatibility.
* @deprecated please use vtkRenderWindowInteractor.setContainer(container: HTMLElement)
* which will also bind events.
*/
bindEvents(container: any): void;

/**
*
* @deprecated please use vtkRenderWindowInteractor.setContainer(null) instead.
*/
unbindEvents(): void;

Expand Down
102 changes: 78 additions & 24 deletions Sources/Rendering/Core/RenderWindowInteractor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ function vtkRenderWindowInteractor(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkRenderWindowInteractor');

// Capture "parentClass" api for internal use
const superClass = { ...publicAPI };

// Initialize list of requesters
const animationRequesters = new Set();

Expand Down Expand Up @@ -215,8 +218,12 @@ function vtkRenderWindowInteractor(publicAPI, model) {
return event.pointerType || '';
}

publicAPI.bindEvents = (container) => {
model.container = container;
const _bindEvents = () => {
if (model.container === null) {
return;
}

const { container } = model;
container.addEventListener('contextmenu', preventDefault);
container.addEventListener('wheel', publicAPI.handleWheel);
container.addEventListener('DOMMouseScroll', publicAPI.handleWheel);
Expand Down Expand Up @@ -247,35 +254,74 @@ function vtkRenderWindowInteractor(publicAPI, model) {
container.style.webkitTapHighlightColor = 'rgba(0,0,0,0)';
};

publicAPI.unbindEvents = () => {
// For backward compatibility.
// Necessary for using unbind/bindEvent without calling setContainer.
publicAPI.bindEvents = (container) => {
if (container === null) {
return;
}
const res = superClass.setContainer(container);
if (res) {
_bindEvents();
}
};

const _unbindEvents = () => {
// Clear any previous timeouts and state variables that control mouse / touchpad behavior.
clearTimeout(model.moveTimeoutID);
clearTimeout(model.wheelTimeoutID);
model.moveTimeoutID = 0;
model.wheelTimeoutID = 0;
wheelCoefficient = 1.0;

const { container } = model;
container.removeEventListener('contextmenu', preventDefault);
container.removeEventListener('wheel', publicAPI.handleWheel);
container.removeEventListener('DOMMouseScroll', publicAPI.handleWheel);
container.removeEventListener('pointerenter', publicAPI.handlePointerEnter);
container.removeEventListener('pointerleave', publicAPI.handlePointerLeave);
container.removeEventListener('pointermove', publicAPI.handlePointerMove, {
passive: false,
});
container.removeEventListener('pointerdown', publicAPI.handlePointerDown, {
passive: false,
});
container.removeEventListener('pointerup', publicAPI.handlePointerUp);
container.removeEventListener(
'pointercancel',
publicAPI.handlePointerCancel
);
if (container) {
container.removeEventListener('contextmenu', preventDefault);
container.removeEventListener('wheel', publicAPI.handleWheel);
container.removeEventListener('DOMMouseScroll', publicAPI.handleWheel);
container.removeEventListener(
'pointerenter',
publicAPI.handlePointerEnter
);
container.removeEventListener(
'pointerleave',
publicAPI.handlePointerLeave
);
container.removeEventListener(
'pointermove',
publicAPI.handlePointerMove,
{
passive: false,
}
);
container.removeEventListener(
'pointerdown',
publicAPI.handlePointerDown,
{
passive: false,
}
);
container.removeEventListener('pointerup', publicAPI.handlePointerUp);
container.removeEventListener(
'pointercancel',
publicAPI.handlePointerCancel
);
}
document.removeEventListener('keypress', publicAPI.handleKeyPress);
document.removeEventListener('keydown', publicAPI.handleKeyDown);
document.removeEventListener('keyup', publicAPI.handleKeyUp);
document.removeEventListener(
'pointerlockchange',
publicAPI.handlePointerLockChange
);
model.container = null;
pointerCache.clear();
};

publicAPI.unbindEvents = () => {
_unbindEvents();
superClass.setContainer(null);
};

publicAPI.handleKeyPress = (event) => {
const data = getKeysFor(event);
publicAPI.keyPressEvent(data);
Expand Down Expand Up @@ -1126,8 +1172,16 @@ function vtkRenderWindowInteractor(publicAPI, model) {
model.currentRenderer = r;
};

publicAPI.setContainer = (container) => {
_unbindEvents();
const res = superClass.setContainer(container ?? null);
if (res) {
_bindEvents();
}
return res;
};

// Stop animating if the renderWindowInteractor is deleted.
const superDelete = publicAPI.delete;
publicAPI.delete = () => {
while (animationRequesters.size) {
publicAPI.cancelAnimation(animationRequesters.values().next().value);
Expand All @@ -1139,9 +1193,9 @@ function vtkRenderWindowInteractor(publicAPI, model) {
);
}
if (model.container) {
publicAPI.unbindEvents();
publicAPI.setContainer(null);
}
superDelete();
superClass.delete();
};

// Use the Page Visibility API to detect when we switch away from or back to
Expand Down Expand Up @@ -1206,7 +1260,6 @@ export function extend(publicAPI, model, initialValues = {}) {
// Create get-only macros
macro.get(publicAPI, model, [
'initialized',
'container',
'interactorStyle',
'lastFrameTime',
'recentAnimationFrameRate',
Expand All @@ -1215,6 +1268,7 @@ export function extend(publicAPI, model, initialValues = {}) {

// Create get-set macros
macro.setGet(publicAPI, model, [
'container',
'lightFollowCamera',
'enabled',
'enableRender',
Expand Down

0 comments on commit dae07b6

Please sign in to comment.