Skip to content

Commit 5c6fc0f

Browse files
committed
fix(renderwindowinteractor): observe KeyPress events on RW container instead of document
Observing on document produces many false positives. For example, a key press event was for an <input> element but RWI was also processing it. BREAKING CHANGE: This may break key press event current behavior. tabIndex=0 is now added on RW containers. fix #1856
1 parent d15d50f commit 5c6fc0f

File tree

7 files changed

+135
-6
lines changed

7 files changed

+135
-6
lines changed

BREAKING_CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## From 32.x to 33
2+
3+
- **vtkRenderWindowInteractor**: KeyPress, KeyDown and KeyUp events are now observed on the container and no longer on the document. You may have to add "tabIndex" on your containers to give focus on your render windows and catch
4+
key events. Check the RenderWindowInteractor example.
5+
16
## From 31.x to 32
27

38
- **vtkMapper**: remove `mapScalarsToTexture` from the public API. The function becomes protected and its API changes. This shouldn't cause any issue in most cases.
17.7 KB
Loading

Documentation/content/examples/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ This will allow you to see the some live code running in your browser. Just pick
326326
[![InteractorStyleTrackballCamera Example][InteractorStyleTrackballCamera]](./InteractorStyleTrackballCamera.html "InteractorStyleTrackballCamera")
327327
[![InteractorStyleUnicam Example][InteractorStyleUnicam]](./InteractorStyleUnicam.html "InteractorStyleUnicam")
328328
[![KeyboardCameraManipulator Example][KeyboardCameraManipulator]](./KeyboardCameraManipulator.html "KeyboardCameraManipulator")
329+
[![KeyPressEvents Example][KeyPressEvents]](./KeyPressEvents.html "KeyPressEvents")
329330
[![MouseRangeManipulator Example][MouseRangeManipulator]](./MouseRangeManipulator.html "MouseRangeManipulator")
330331
[![PiecewiseGaussianWidget Example][PiecewiseGaussianWidget]](./PiecewiseGaussianWidget.html "PiecewiseGaussianWidget")
331332
[![TimeStepBasedAnimationHandler Example][TimeStepBasedAnimationHandler]](./TimeStepBasedAnimationHandler.html "TimeStepBasedAnimationHandler")
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<style>
2+
#view-1:focus {
3+
outline: none; /* remove default */
4+
box-shadow: 0 0 20px 10px rgba(0, 255, 0, 0.6); /* red glow */
5+
z-index: 1; /* move forward (works only if position!=static) */
6+
position: relative;
7+
}
8+
#view-2:focus, #view-2:focus-visible {
9+
outline: none; /* remove default */
10+
}
11+
12+
input:focus {
13+
outline: none;
14+
border: 2px solid teal;
15+
box-shadow: 0 0 4px teal;
16+
}
17+
18+
/* Optional: Use focus-visible for keyboard-only styling */
19+
select:focus-visible {
20+
outline: 3px dashed orange;
21+
}
22+
</style>
23+
<h3>Click or press tab until you "focus" a render window, then type keys (e.g. r, w, s)</h3>
24+
<table>
25+
<tr>
26+
<td>Click with mouse then press space key:</td>
27+
<td>
28+
<input type="checkbox" id="checkboxTranslation" checked>
29+
</td>
30+
</tr>
31+
<tr>
32+
<td>Click then key up and down :</td>
33+
<td><input id='slider' type="range" min="0" max="255" step="1" value="255" style="width: 100px;"/></td>
34+
</tr>
35+
<tr>
36+
<td>Use key up and down to browse through:</td>
37+
<td>
38+
<select id="select">
39+
<option>One</option>
40+
<option>Two</option>
41+
<option selected="selected">Three</option>
42+
<option>Four</option>
43+
</select>
44+
</td>
45+
</tr>
46+
<tr>
47+
<td>
48+
<button id="buttonReset">Click and press space key</button>
49+
</td>
50+
</tr>
51+
</table>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import '@kitware/vtk.js/favicon';
2+
3+
// Load the rendering pieces we want to use (for both WebGL and WebGPU)
4+
import '@kitware/vtk.js/Rendering/Profiles/Geometry';
5+
6+
import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
7+
import vtkConeSource from '@kitware/vtk.js/Filters/Sources/ConeSource';
8+
import vtkGenericRenderWindow from '@kitware/vtk.js/Rendering/Misc/GenericRenderWindow';
9+
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
10+
11+
import controlPanel from './controlPanel.html';
12+
13+
// ----------------------------------------------------------------------------
14+
// Standard rendering code setup
15+
// ----------------------------------------------------------------------------
16+
17+
const container = document.querySelector('body');
18+
const controlContainer = document.createElement('div');
19+
controlContainer.innerHTML = controlPanel;
20+
container.appendChild(controlContainer);
21+
22+
global.RWIs = [];
23+
24+
const logs = document.createElement('pre');
25+
26+
const cone = vtkConeSource.newInstance();
27+
28+
for (let i = 0; i < 3; ++i) {
29+
const elementParent = document.createElement('div');
30+
elementParent.style.width = '33%';
31+
elementParent.style.height = '300px';
32+
elementParent.style.display = 'inline-block';
33+
34+
const element = document.createElement('div');
35+
element.setAttribute('id', `view-${i}`);
36+
element.style.width = '100%';
37+
element.style.height = '100%';
38+
element.tabIndex = 0;
39+
elementParent.appendChild(element);
40+
41+
container.appendChild(elementParent);
42+
43+
const grw = vtkGenericRenderWindow.newInstance();
44+
45+
const color = [0, 0, 0];
46+
color[i % 3] = 1;
47+
grw.getRenderer().setBackground(color);
48+
49+
const mapper = vtkMapper.newInstance();
50+
mapper.setInputConnection(cone.getOutputPort());
51+
const actor = vtkActor.newInstance();
52+
actor.setMapper(mapper);
53+
grw.getRenderer().addActor(actor);
54+
grw.getRenderer().resetCamera();
55+
56+
grw.setContainer(element);
57+
grw.resize();
58+
59+
global.RWIs.push(grw.getInteractor());
60+
// Pick on mouse right click
61+
grw.getInteractor().onKeyDown((callData) => {
62+
logs.textContent += `Pressed ${callData.key} on RWI #${i}\n`;
63+
});
64+
}
65+
container.appendChild(logs);

Sources/Rendering/Core/RenderWindowInteractor/index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,12 @@ export interface vtkRenderWindowInteractor extends vtkObject {
974974
/**
975975
* Add an HTMLElement as the new container for the interactor.
976976
* All events will be bound to this new container.
977+
*
978+
* container will gain the tabIndex=0 attribute to catch keyboard events.
979+
* container must have focus (manually via click or tab, or programmatically
980+
* via `.focus()`) to receive keypress events
981+
* Outline on focus can be customized with CSS :focus pseudo-class.
982+
*
977983
* Any old container will be removed along with its listeners.
978984
*/
979985
setContainer(container: Nullable<HTMLElement>): boolean;

Sources/Rendering/Core/RenderWindowInteractor/index.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,15 +237,16 @@ function vtkRenderWindowInteractor(publicAPI, model) {
237237
});
238238
container.addEventListener('pointerup', publicAPI.handlePointerUp);
239239
container.addEventListener('pointercancel', publicAPI.handlePointerCancel);
240-
document.addEventListener('keypress', publicAPI.handleKeyPress);
241-
document.addEventListener('keydown', publicAPI.handleKeyDown);
242-
document.addEventListener('keyup', publicAPI.handleKeyUp);
240+
container.addEventListener('keypress', publicAPI.handleKeyPress);
241+
container.addEventListener('keydown', publicAPI.handleKeyDown);
242+
container.addEventListener('keyup', publicAPI.handleKeyUp);
243243

244244
document.addEventListener(
245245
'pointerlockchange',
246246
publicAPI.handlePointerLockChange
247247
);
248248

249+
container.tabIndex = 0; // to receive key events
249250
// using touchAction is more performant than preventDefault
250251
// in a touchstart handler.
251252
container.style.touchAction = 'none';
@@ -307,9 +308,9 @@ function vtkRenderWindowInteractor(publicAPI, model) {
307308
publicAPI.handlePointerCancel
308309
);
309310
}
310-
document.removeEventListener('keypress', publicAPI.handleKeyPress);
311-
document.removeEventListener('keydown', publicAPI.handleKeyDown);
312-
document.removeEventListener('keyup', publicAPI.handleKeyUp);
311+
container.removeEventListener('keypress', publicAPI.handleKeyPress);
312+
container.removeEventListener('keydown', publicAPI.handleKeyDown);
313+
container.removeEventListener('keyup', publicAPI.handleKeyUp);
313314
document.removeEventListener(
314315
'pointerlockchange',
315316
publicAPI.handlePointerLockChange

0 commit comments

Comments
 (0)