Skip to content

Commit a392a7b

Browse files
committedSep 15, 2021
refacto(Controls): switch keyboard management to StateControls
1 parent 5f4ace1 commit a392a7b

File tree

4 files changed

+163
-117
lines changed

4 files changed

+163
-117
lines changed
 

‎src/Controls/GlobeControls.js

+28-38
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ import StateControl from 'Controls/StateControl';
88
// private members
99
const EPS = 0.000001;
1010

11+
const direction = {
12+
up: new THREE.Vector2(0, 1),
13+
bottom: new THREE.Vector2(0, -1),
14+
left: new THREE.Vector2(1, 0),
15+
right: new THREE.Vector2(-1, 0),
16+
};
17+
1118
// Orbit
1219
const rotateStart = new THREE.Vector2();
1320
const rotateEnd = new THREE.Vector2();
@@ -221,8 +228,17 @@ class GlobeControls extends THREE.EventDispatcher {
221228
this.handleCollision = typeof (options.handleCollision) !== 'undefined' ? options.handleCollision : true;
222229
this.minDistanceCollision = 60;
223230

224-
// Set to true to disable use of the keys
225-
this.enableKeys = true;
231+
// this.enableKeys property has moved to StateControl
232+
Object.defineProperty(this, 'enableKeys', {
233+
get: () => this.states.enableKeys,
234+
set: (value) => {
235+
console.warn(
236+
'GlobeControls.enableKeys property is deprecated. Use StateControl.enableKeys instead ' +
237+
'- which you can access with GlobeControls.states.enableKeys.',
238+
);
239+
this.states.enableKeys = value;
240+
},
241+
});
226242

227243
// Enable Damping
228244
this.enableDamping = true;
@@ -246,7 +262,6 @@ class GlobeControls extends THREE.EventDispatcher {
246262
this._onTouchStart = this.onTouchStart.bind(this);
247263
this._onTouchEnd = this.onTouchEnd.bind(this);
248264
this._onTouchMove = this.onTouchMove.bind(this);
249-
this._onKeyDown = this.onKeyDown.bind(this);
250265

251266
this._onStateChange = this.onStateChange.bind(this);
252267

@@ -272,9 +287,6 @@ class GlobeControls extends THREE.EventDispatcher {
272287
this.states.addEventListener(this.states.TRAVEL_IN._event, this._onTravel, false);
273288
this.states.addEventListener(this.states.TRAVEL_OUT._event, this._onTravel, false);
274289

275-
// TODO: Why windows
276-
window.addEventListener('keydown', this._onKeyDown, false);
277-
278290
view.scene.add(cameraTarget);
279291
if (enableTargetHelper) {
280292
cameraTarget.add(helpers.target);
@@ -603,12 +615,17 @@ class GlobeControls extends THREE.EventDispatcher {
603615
}
604616

605617
handlePan(event) {
606-
panEnd.copy(event.viewCoords);
607-
panDelta.subVectors(panEnd, panStart);
618+
if (event.viewCoords) {
619+
panEnd.copy(event.viewCoords);
620+
panDelta.subVectors(panEnd, panStart);
621+
panStart.copy(panEnd);
622+
} else if (event.direction) {
623+
panDelta.copy(direction[event.direction]).multiplyScalar(this.keyPanSpeed);
624+
}
625+
608626
this.mouseToPan(panDelta.x, panDelta.y);
609-
panStart.copy(panEnd);
610627

611-
this.update();
628+
this.update(this.states.PAN);
612629
}
613630

614631
handlePanoramic(event) {
@@ -714,7 +731,7 @@ class GlobeControls extends THREE.EventDispatcher {
714731
if (point && range > this.minDistance) {
715732
return this.lookAtCoordinate({
716733
coord: new Coordinates('EPSG:4978', point),
717-
range: range * (event.type === 'travel_out' ? 1 / 0.6 : 0.6),
734+
range: range * (event.direction === 'out' ? 1 / 0.6 : 0.6),
718735
time: 1500,
719736
});
720737
}
@@ -745,31 +762,6 @@ class GlobeControls extends THREE.EventDispatcher {
745762
this.dispatchEvent(this.endEvent);
746763
}
747764

748-
onKeyDown(event) {
749-
this.player.stop();
750-
// TODO : this.states.enabled check should be removed when moving keyboard events management to StateControl
751-
if (this.states.enabled === false || this.enableKeys === false) { return; }
752-
switch (event.keyCode) {
753-
case this.states.PAN.up:
754-
this.mouseToPan(0, this.keyPanSpeed);
755-
this.update(this.states.PAN);
756-
break;
757-
case this.states.PAN.bottom:
758-
this.mouseToPan(0, -this.keyPanSpeed);
759-
this.update(this.states.PAN);
760-
break;
761-
case this.states.PAN.left:
762-
this.mouseToPan(this.keyPanSpeed, 0);
763-
this.update(this.states.PAN);
764-
break;
765-
case this.states.PAN.right:
766-
this.mouseToPan(-this.keyPanSpeed, 0);
767-
this.update(this.states.PAN);
768-
break;
769-
default:
770-
}
771-
}
772-
773765
onTouchStart(event) {
774766
// CameraUtils.stop(view);
775767
this.player.stop();
@@ -904,8 +896,6 @@ class GlobeControls extends THREE.EventDispatcher {
904896
this.states.removeEventListener(this.states.TRAVEL_IN._event, this._onTravel, false);
905897
this.states.removeEventListener(this.states.TRAVEL_OUT._event, this._onTravel, false);
906898

907-
window.removeEventListener('keydown', this._onKeyDown, false);
908-
909899
this.dispatchEvent({ type: 'dispose' });
910900
}
911901
/**

‎src/Controls/StateControl.js

+71-53
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ const DEFAULT_STATES = {
4141
mouseButton: THREE.MOUSE.RIGHT,
4242
double: false,
4343
finger: 3,
44-
up: CONTROL_KEYS.UP,
45-
bottom: CONTROL_KEYS.BOTTOM,
46-
left: CONTROL_KEYS.LEFT,
47-
right: CONTROL_KEYS.RIGHT,
4844
_event: 'pan',
4945
},
5046
PANORAMIC: {
@@ -60,28 +56,53 @@ const DEFAULT_STATES = {
6056
double: true,
6157
_event: 'travel_in',
6258
_trigger: true,
59+
_direction: 'in',
6360
},
6461
TRAVEL_OUT: {
6562
enable: false,
6663
double: false,
6764
_event: 'travel_out',
6865
_trigger: true,
66+
_direction: 'out',
67+
},
68+
PAN_UP: {
69+
enable: true,
70+
keyboard: CONTROL_KEYS.UP,
71+
double: false,
72+
_event: 'pan',
73+
_trigger: true,
74+
_direction: 'up',
75+
},
76+
PAN_BOTTOM: {
77+
enable: true,
78+
keyboard: CONTROL_KEYS.BOTTOM,
79+
double: false,
80+
_event: 'pan',
81+
_trigger: true,
82+
_direction: 'bottom',
83+
},
84+
PAN_LEFT: {
85+
enable: true,
86+
keyboard: CONTROL_KEYS.LEFT,
87+
double: false,
88+
_event: 'pan',
89+
_trigger: true,
90+
_direction: 'left',
91+
},
92+
PAN_RIGHT: {
93+
enable: true,
94+
keyboard: CONTROL_KEYS.RIGHT,
95+
double: false,
96+
_event: 'pan',
97+
_trigger: true,
98+
_direction: 'right',
6999
},
70100
};
71101

72102

73103
const viewCoords = new THREE.Vector2();
74104

75105

76-
function stateToTrigger(state) {
77-
if (!state) {
78-
return undefined;
79-
} else if (state.keyboard) {
80-
return 'keydown';
81-
}
82-
}
83-
84-
85106
/**
86107
* @typedef {Object} StateControl~State
87108
* @property {boolean} enable=true Indicate whether the state is enabled or not.
@@ -120,6 +141,8 @@ function stateToTrigger(state) {
120141
* Otherwise, it is the center of the screen. It is disabled by default.
121142
* @property {boolean} enable Defines whether all input will be communicated to the associated `Controls` or not.
122143
* Default is true.
144+
* @property {boolean} enableKeys Defines whether keyboard input will be communicated to the associated `Controls` or
145+
* not. Default is true.
123146
*/
124147
class StateControl extends THREE.EventDispatcher {
125148
constructor(view, options = {}) {
@@ -140,6 +163,18 @@ class StateControl extends THREE.EventDispatcher {
140163
},
141164
});
142165

166+
// Set to true to disable use of the keys
167+
let enableKeys = true;
168+
Object.defineProperty(this, 'enableKeys', {
169+
get: () => enableKeys,
170+
set: (value) => {
171+
if (!value) {
172+
this.onKeyUp();
173+
}
174+
enableKeys = value;
175+
},
176+
});
177+
143178
this.NONE = {};
144179

145180
let currentState = this.NONE;
@@ -154,9 +189,10 @@ class StateControl extends THREE.EventDispatcher {
154189
},
155190
});
156191

157-
// TODO : the 3 next properties should be made private when ES6 allows it
192+
// TODO : the 4 next properties should be made private when ES6 allows it
158193
this._clickTimeStamp = 0;
159194
this._lastMousePressed = { viewCoords: new THREE.Vector2() };
195+
this._currentMousePressed = undefined;
160196
this._currentKeyPressed = undefined;
161197

162198
this._onPointerDown = this.onPointerDown.bind(this);
@@ -182,30 +218,6 @@ class StateControl extends THREE.EventDispatcher {
182218
// disable context menu when right-clicking
183219
this._domElement.addEventListener('contextmenu', this._onContextMenu, false);
184220

185-
// TODO : this shall be removed when switching keyboard management form Controls to StateControls
186-
this._handleTravelInEvent = (event) => {
187-
if (
188-
this.enabled
189-
&& this.TRAVEL_IN === this.inputToState(event.button, event.keyCode, this.TRAVEL_IN.double)
190-
) {
191-
this.dispatchEvent({
192-
type: 'travel_in',
193-
viewCoords: this._view.eventToViewCoords(event),
194-
});
195-
}
196-
};
197-
this._handleTravelOutEvent = (event) => {
198-
if (
199-
this.enabled
200-
&& this.TRAVEL_OUT === this.inputToState(event.button, event.keyCode, this.TRAVEL_OUT.double)
201-
) {
202-
this.dispatchEvent({
203-
type: 'travel_out',
204-
viewCoords: this._view.eventToViewCoords(event),
205-
});
206-
}
207-
};
208-
209221
this.setFromOptions(options);
210222
}
211223

@@ -228,7 +240,12 @@ class StateControl extends THREE.EventDispatcher {
228240
// If the input relates to a state, returns it
229241
if (!state._trigger) { return state; }
230242
// If the input relates to a trigger (TRAVEL_IN, TRAVEL_OUT), dispatch a relevant event.
231-
this.dispatchEvent({ type: state._event, viewCoords });
243+
this.dispatchEvent({
244+
type: state._event,
245+
// Dont pass viewCoords if the input is only a keyboard input.
246+
viewCoords: mouseButton !== undefined && viewCoords,
247+
direction: state._direction,
248+
});
232249
}
233250
}
234251
return this.NONE;
@@ -271,9 +288,6 @@ class StateControl extends THREE.EventDispatcher {
271288
* };
272289
*/
273290
setFromOptions(options) {
274-
this._domElement.removeEventListener(stateToTrigger(this.TRAVEL_IN), this._handleTravelInEvent, false);
275-
this._domElement.removeEventListener(stateToTrigger(this.TRAVEL_OUT), this._handleTravelOutEvent, false);
276-
277291
for (const state in DEFAULT_STATES) {
278292
if ({}.hasOwnProperty.call(DEFAULT_STATES, state)) {
279293
let newState = {};
@@ -290,13 +304,11 @@ class StateControl extends THREE.EventDispatcher {
290304
// Copy the `_event` and `_trigger` properties
291305
newState._event = DEFAULT_STATES[state]._event;
292306
newState._trigger = DEFAULT_STATES[state]._trigger;
307+
newState._direction = DEFAULT_STATES[state]._direction;
293308

294309
this[state] = newState;
295310
}
296311
}
297-
298-
this._domElement.addEventListener(stateToTrigger(this.TRAVEL_IN), this._handleTravelInEvent, false);
299-
this._domElement.addEventListener(stateToTrigger(this.TRAVEL_OUT), this._handleTravelOutEvent, false);
300312
}
301313

302314

@@ -312,6 +324,7 @@ class StateControl extends THREE.EventDispatcher {
312324
// TODO : add touch event management
313325
default:
314326
}
327+
315328
this._domElement.addEventListener('pointermove', this._onPointerMove, false);
316329
this._domElement.addEventListener('pointerup', this._onPointerUp, false);
317330
this._domElement.addEventListener('mouseleave', this._onPointerUp, false);
@@ -332,6 +345,7 @@ class StateControl extends THREE.EventDispatcher {
332345

333346
onPointerUp() {
334347
if (!this.enabled) { return; }
348+
this._currentMousePressed = undefined;
335349

336350
this._domElement.removeEventListener('pointermove', this._onPointerMove, false);
337351
this._domElement.removeEventListener('pointerup', this._onPointerUp, false);
@@ -346,16 +360,18 @@ class StateControl extends THREE.EventDispatcher {
346360
handleMouseDown(event) {
347361
viewCoords.copy(this._view.eventToViewCoords(event));
348362

363+
this._currentMousePressed = event.button;
364+
349365
// Detect if the mouse button was pressed less than 500 ms before, and if the cursor has not moved two much
350366
// since previous click. If so, set dblclick to true.
351367
const dblclick = event.timeStamp - this._clickTimeStamp < 500
352-
&& this._lastMousePressed.button === event.button
368+
&& this._lastMousePressed.button === this._currentMousePressed
353369
&& this._lastMousePressed.viewCoords.distanceTo(viewCoords) < 5;
354370
this._clickTimeStamp = event.timeStamp;
355-
this._lastMousePressed.button = event.button;
371+
this._lastMousePressed.button = this._currentMousePressed;
356372
this._lastMousePressed.viewCoords.copy(viewCoords);
357373

358-
this.currentState = this.inputToState(event.button, this._currentKeyPressed, dblclick);
374+
this.currentState = this.inputToState(this._currentMousePressed, this._currentKeyPressed, dblclick);
359375
}
360376

361377
handleMouseMove(event) {
@@ -367,13 +383,18 @@ class StateControl extends THREE.EventDispatcher {
367383
// ---------- KEYBOARD EVENTS : ----------
368384

369385
onKeyDown(event) {
370-
if (!this.enabled) { return; }
386+
if (!this.enabled || !this.enableKeys) { return; }
371387
this._currentKeyPressed = event.keyCode;
388+
389+
this.inputToState(this._currentMousePressed, this._currentKeyPressed);
372390
}
373391

374392
onKeyUp() {
375-
if (!this.enabled) { return; }
393+
if (!this.enabled || !this.enableKeys) { return; }
376394
this._currentKeyPressed = undefined;
395+
if (this._currentMousePressed === undefined) {
396+
this.currentState = this.NONE;
397+
}
377398
}
378399

379400

@@ -403,9 +424,6 @@ class StateControl extends THREE.EventDispatcher {
403424

404425
window.removeEventListener('blur', this._onBlur);
405426
this._domElement.removeEventListener('contextmenu', this._onContextMenu, false);
406-
407-
this._domElement.removeEventListener(this.TRAVEL_IN.trigger, this._handleTravelInEvent, false);
408-
this._domElement.removeEventListener(this.TRAVEL_OUT.trigger, this._handleTravelInEvent, false);
409427
}
410428
}
411429

‎test/unit/globecontrol.js

+2-15
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,6 @@ describe('GlobeControls', function () {
130130
controls.states.currentState = controls.states.NONE;
131131
});
132132

133-
it.skip('keydown', function () {
134-
event.keyCode = controls.states.PAN.up;
135-
controls.onKeyDown(event);
136-
assert.equal(controls.state, controls.states.PAN);
137-
event.keyCode = controls.states.PAN.bottom;
138-
controls.onKeyDown(event);
139-
assert.equal(controls.state, controls.states.PAN);
140-
event.keyCode = controls.states.PAN.left;
141-
controls.onKeyDown(event);
142-
assert.equal(controls.state, controls.states.PAN);
143-
event.keyCode = controls.states.PAN.right;
144-
controls.onKeyDown(event);
145-
assert.equal(controls.state, controls.states.PAN);
146-
});
147-
148133
it('dolly', function () {
149134
controls.dolly(1);
150135
controls.state = controls.states.ORBIT;
@@ -171,6 +156,7 @@ describe('GlobeControls', function () {
171156
controls.travel({
172157
viewCoords: viewer.eventToViewCoords(event),
173158
type: 'travel_in',
159+
direction: 'in',
174160
}).then(() => {
175161
assert.ok(controls.getRange() < startRange);
176162
done();
@@ -182,6 +168,7 @@ describe('GlobeControls', function () {
182168
controls.travel({
183169
viewCoords: viewer.eventToViewCoords(event),
184170
type: 'travel_out',
171+
direction: 'out',
185172
}).then(() => {
186173
assert.ok(controls.getRange() > startRange);
187174
done();

‎test/unit/statecontrol.js

+62-11
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,36 @@ describe('StateControl', function () {
139139
states.onPointerUp();
140140
});
141141

142+
it('should trigger pan event from up arrow key press', function () {
143+
event.button = undefined;
144+
145+
// UP arrow key
146+
event.keyCode = 38;
147+
assert(testEventTriggering('pan', event, states._onKeyDown));
148+
states._onKeyUp();
149+
});
150+
151+
it('should trigger pan event from bottom arrow key press', function () {
152+
// BOTTOM arrow key
153+
event.keyCode = 40;
154+
assert(testEventTriggering('pan', event, states._onKeyDown));
155+
states._onKeyUp();
156+
});
157+
158+
it('should trigger pan event from left arrow key press', function () {
159+
// LEFT arrow key
160+
event.keyCode = 37;
161+
assert(testEventTriggering('pan', event, states._onKeyDown));
162+
states._onKeyUp();
163+
});
164+
165+
it('should trigger pan event from right arrow key press', function () {
166+
// RIGHT arrow key
167+
event.keyCode = 39;
168+
assert(testEventTriggering('pan', event, states._onKeyDown));
169+
states._onKeyUp();
170+
});
171+
142172
it('should trigger state-changed event from shift + left-click', function () {
143173
event.button = MOUSE.LEFT;
144174
event.keyCode = 16;
@@ -170,16 +200,14 @@ describe('StateControl', function () {
170200
states.setFromOptions({
171201
TRAVEL_IN: {
172202
keyboard: 80,
173-
double: false,
174203
},
175204
});
176205

177206
event.button = undefined;
178207
event.keyCode = 80;
179208

180-
assert(testEventTriggering('travel_in', event, (event) => {
181-
states._handleTravelInEvent(event);
182-
}));
209+
assert(testEventTriggering('travel_in', event, states._onKeyDown));
210+
states._onKeyUp();
183211
});
184212

185213
it('should no longer trigger travel_in event from mouse event', function () {
@@ -220,9 +248,8 @@ describe('StateControl', function () {
220248
event.button = undefined;
221249
event.keyCode = 77;
222250

223-
assert(testEventTriggering('travel_out', event, (event) => {
224-
states._handleTravelOutEvent(event);
225-
}));
251+
assert(testEventTriggering('travel_out', event, states._onKeyDown));
252+
states._onKeyUp();
226253
});
227254

228255
it('should no longer trigger travel_out event from mouse event', function () {
@@ -270,14 +297,38 @@ describe('StateControl', function () {
270297
}));
271298

272299
event.button = undefined;
273-
event.keyCode = 80;
274300
assert(!testEventTriggering('travel_in', event, (event) => {
275-
states._handleTravelInEvent(event);
301+
event.keyCode = 80;
302+
states._onKeyDown(event);
303+
states._onKeyUp();
276304
}));
277305

278-
event.keyCode = 77;
279306
assert(!testEventTriggering('travel_in', event, (event) => {
280-
states._handleTravelInEvent(event);
307+
event.keyCode = 77;
308+
states._onKeyDown(event);
309+
states._onKeyUp();
310+
}));
311+
312+
assert(!testEventTriggering('pan', event, (event) => {
313+
// Left arrow key
314+
event.keyCode = 37;
315+
states._onKeyDown(event);
316+
states._onKeyUp();
317+
318+
// Up arrow key
319+
event.keyCode = 38;
320+
states._onKeyDown(event);
321+
states._onKeyUp();
322+
323+
// Right arrow key
324+
event.keyCode = 39;
325+
states._onKeyDown(event);
326+
states._onKeyUp();
327+
328+
// Bottom arrow key
329+
event.keyCode = 40;
330+
states._onKeyDown(event);
331+
states._onKeyUp();
281332
}));
282333

283334
states.enabled = true;

0 commit comments

Comments
 (0)
Please sign in to comment.