From 16deccc4f7a98902a1f0deaf243dd6c2169c533a Mon Sep 17 00:00:00 2001 From: Shreeraj Jadhav Date: Fri, 19 Jan 2024 17:29:54 -0500 Subject: [PATCH] fix(handleWheel): mouse scrolling behavior for out-of-focus windows When rebinding events with a different container, make sure all timeouts and state variables from previous containter are cleared. --- .../Core/RenderWindowInteractor/index.d.ts | 7 ++ .../Core/RenderWindowInteractor/index.js | 65 ++++++++++++++----- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/Sources/Rendering/Core/RenderWindowInteractor/index.d.ts b/Sources/Rendering/Core/RenderWindowInteractor/index.d.ts index 5de5a390cf3..c0e52d18f8a 100755 --- a/Sources/Rendering/Core/RenderWindowInteractor/index.d.ts +++ b/Sources/Rendering/Core/RenderWindowInteractor/index.d.ts @@ -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: HTMLElement | null): boolean; + /** * Turn on/off the automatic repositioning of lights as the camera moves. * @param lightFollowCamera diff --git a/Sources/Rendering/Core/RenderWindowInteractor/index.js b/Sources/Rendering/Core/RenderWindowInteractor/index.js index 50979535a73..e41b3d34717 100644 --- a/Sources/Rendering/Core/RenderWindowInteractor/index.js +++ b/Sources/Rendering/Core/RenderWindowInteractor/index.js @@ -216,6 +216,10 @@ function vtkRenderWindowInteractor(publicAPI, model) { } publicAPI.bindEvents = (container) => { + if (model.container === container || container === null) { + return false; + } + publicAPI.unbindEvents(); model.container = container; container.addEventListener('contextmenu', preventDefault); container.addEventListener('wheel', publicAPI.handleWheel); @@ -245,26 +249,50 @@ function vtkRenderWindowInteractor(publicAPI, model) { container.style.userSelect = 'none'; // disables tap highlight for when cursor is pointer container.style.webkitTapHighlightColor = 'rgba(0,0,0,0)'; + return true; }; publicAPI.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); @@ -1126,6 +1154,11 @@ function vtkRenderWindowInteractor(publicAPI, model) { model.currentRenderer = r; }; + publicAPI.setContainer = (container) => { + publicAPI.unbindEvents(); + return publicAPI.bindEvents(container); + }; + // Stop animating if the renderWindowInteractor is deleted. const superDelete = publicAPI.delete; publicAPI.delete = () => {