diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index a998f78777..fd49be88dd 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -1,4 +1,11 @@ -import { IHardwareRenderer, IPhysics, IPhysicsManager, IShaderLab, IXRDevice } from "@galacean/engine-design"; +import { + IHardwareRenderer, + IInputOptions, + IPhysics, + IPhysicsManager, + IShaderLab, + IXRDevice +} from "@galacean/engine-design"; import { Color } from "@galacean/engine-math"; import { SpriteMaskInteraction } from "./2d"; import { Font } from "./2d/text/Font"; @@ -256,7 +263,7 @@ export class Engine extends EventDispatcher { this._textDefaultFont = Font.createFromOS(this, "Arial"); this._textDefaultFont.isGCIgnored = true; - this.inputManager = new InputManager(this); + this.inputManager = new InputManager(this, configuration.input); const { xrDevice } = configuration; if (xrDevice) { @@ -752,6 +759,8 @@ export interface EngineConfiguration { xrDevice?: IXRDevice; /** Color space. */ colorSpace?: ColorSpace; - /** Shader lab */ + /** Shader lab. */ shaderLab?: IShaderLab; + /** Input options. */ + input?: IInputOptions; } diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 3310f0a165..617fc4c366 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -1,12 +1,13 @@ +import { IInputOptions } from "@galacean/engine-design"; import { Vector3 } from "@galacean/engine-math"; import { Engine } from "../Engine"; +import { Scene } from "../Scene"; import { Keys } from "./enums/Keys"; import { PointerButton, _pointerBin2DecMap } from "./enums/PointerButton"; import { KeyboardManager } from "./keyboard/KeyboardManager"; import { Pointer } from "./pointer/Pointer"; import { PointerManager } from "./pointer/PointerManager"; import { WheelManager } from "./wheel/WheelManager"; -import { Scene } from "../Scene"; /** * InputManager manages device input such as mouse, touch, keyboard, etc. @@ -150,18 +151,14 @@ export class InputManager { /** * @internal */ - constructor(engine: Engine) { + constructor(engine: Engine, inputOptions?: IInputOptions) { this._engine = engine; // @ts-ignore const canvas = engine._canvas._webCanvas; if (typeof OffscreenCanvas === "undefined" || !(canvas instanceof OffscreenCanvas)) { - this._wheelManager = new WheelManager(engine); - this._pointerManager = new PointerManager(engine); - this._keyboardManager = new KeyboardManager(engine); - this._onBlur = this._onBlur.bind(this); - window.addEventListener("blur", this._onBlur); - this._onFocus = this._onFocus.bind(this); - window.addEventListener("focus", this._onFocus); + this._wheelManager = new WheelManager(engine, inputOptions?.wheelTarget ?? canvas); + this._pointerManager = new PointerManager(engine, inputOptions?.pointerTarget ?? canvas); + this._keyboardManager = new KeyboardManager(engine, inputOptions?.keyboardTarget ?? window); this._initialized = true; } } @@ -189,8 +186,6 @@ export class InputManager { */ _destroy(): void { if (this._initialized) { - window.removeEventListener("blur", this._onBlur); - window.removeEventListener("focus", this._onFocus); this._wheelManager._destroy(); this._wheelManager = null; this._pointerManager._destroy(); @@ -199,16 +194,4 @@ export class InputManager { this._keyboardManager = null; } } - - private _onBlur(): void { - this._wheelManager._onBlur(); - this._pointerManager._onBlur(); - this._keyboardManager._onBlur(); - } - - private _onFocus(): void { - this._wheelManager._onFocus(); - this._pointerManager._onFocus(); - this._keyboardManager._onFocus(); - } } diff --git a/packages/core/src/input/interface/IInput.ts b/packages/core/src/input/interface/IInput.ts index 49ed3e24b2..9f2588db3f 100644 --- a/packages/core/src/input/interface/IInput.ts +++ b/packages/core/src/input/interface/IInput.ts @@ -7,12 +7,4 @@ export interface IInput { * Function called when the engine is destroyed. */ _destroy(): void; - /** - * Function called when focused. - */ - _onFocus(): void; - /** - * Function called when focus is lost. - */ - _onBlur(): void; } diff --git a/packages/core/src/input/keyboard/KeyboardManager.ts b/packages/core/src/input/keyboard/KeyboardManager.ts index 7572c66c1c..d97324b6f4 100644 --- a/packages/core/src/input/keyboard/KeyboardManager.ts +++ b/packages/core/src/input/keyboard/KeyboardManager.ts @@ -1,5 +1,5 @@ -import { Engine } from "../../Engine"; import { DisorderedArray } from "../../DisorderedArray"; +import { Engine } from "../../Engine"; import { Platform } from "../../Platform"; import { SystemInfo } from "../../SystemInfo"; import { Keys } from "../enums/Keys"; @@ -24,25 +24,20 @@ export class KeyboardManager implements IInput { /** @internal */ _curFrameUpList: DisorderedArray = new DisorderedArray(); + // @internal + _target: EventTarget; private _engine: Engine; - private _htmlCanvas: HTMLCanvasElement; private _nativeEvents: KeyboardEvent[] = []; - private _hadListener: boolean = false; /** - * Create a KeyboardManager. + * @internal */ - constructor(engine: Engine) { - // @ts-ignore - const htmlCanvas = engine._canvas._webCanvas; + constructor(engine: Engine, target: EventTarget) { this._engine = engine; - this._htmlCanvas = htmlCanvas; - // Need to set tabIndex to make the canvas focus. - htmlCanvas.tabIndex = htmlCanvas.tabIndex; + this._onBlur = this._onBlur.bind(this); this._onKeyEvent = this._onKeyEvent.bind(this); - htmlCanvas.addEventListener("keydown", this._onKeyEvent); - htmlCanvas.addEventListener("keyup", this._onKeyEvent); - this._hadListener = true; + this._target = target; + this._addEventListener(); } /** @@ -101,42 +96,11 @@ export class KeyboardManager implements IInput { } } - /** - * @internal - */ - _onFocus(): void { - if (!this._hadListener) { - this._htmlCanvas.addEventListener("keydown", this._onKeyEvent); - this._htmlCanvas.addEventListener("keyup", this._onKeyEvent); - this._hadListener = true; - } - } - - /** - * @internal - */ - _onBlur(): void { - if (this._hadListener) { - this._htmlCanvas.removeEventListener("keydown", this._onKeyEvent); - this._htmlCanvas.removeEventListener("keyup", this._onKeyEvent); - this._curHeldDownKeyToIndexMap.length = 0; - this._curFrameHeldDownList.length = 0; - this._curFrameDownList.length = 0; - this._curFrameUpList.length = 0; - this._nativeEvents.length = 0; - this._hadListener = false; - } - } - /** * @internal */ _destroy(): void { - if (this._hadListener) { - this._htmlCanvas.removeEventListener("keydown", this._onKeyEvent); - this._htmlCanvas.removeEventListener("keyup", this._onKeyEvent); - this._hadListener = false; - } + this._removeEventListener(); this._curHeldDownKeyToIndexMap.length = 0; this._curHeldDownKeyToIndexMap = null; this._upKeyToFrameCountMap.length = 0; @@ -151,11 +115,32 @@ export class KeyboardManager implements IInput { this._curFrameDownList = null; this._curFrameUpList.length = 0; this._curFrameUpList = null; - this._htmlCanvas = null; this._engine = null; } + private _onBlur() { + this._curHeldDownKeyToIndexMap.length = 0; + this._curFrameHeldDownList.length = 0; + this._curFrameDownList.length = 0; + this._curFrameUpList.length = 0; + this._nativeEvents.length = 0; + } + private _onKeyEvent(evt: KeyboardEvent): void { this._nativeEvents.push(evt); } + + private _addEventListener(): void { + const { _target: target } = this; + target.addEventListener("keydown", this._onKeyEvent); + target.addEventListener("keyup", this._onKeyEvent); + target.addEventListener("blur", this._onBlur); + } + + private _removeEventListener(): void { + const { _target: target } = this; + target.removeEventListener("keydown", this._onKeyEvent); + target.removeEventListener("keyup", this._onKeyEvent); + target.removeEventListener("blur", this._onBlur); + } } diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 39ebbc6fe3..17871481cf 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -34,28 +34,30 @@ export class PointerManager implements IInput { /** @internal */ _downList: DisorderedArray = new DisorderedArray(); + // @internal + _target: EventTarget; private _engine: Engine; private _canvas: Canvas; - private _htmlCanvas: HTMLCanvasElement; private _nativeEvents: PointerEvent[] = []; private _pointerPool: Pointer[]; - private _hadListener: boolean = false; + private _htmlCanvas: HTMLCanvasElement; /** - * Create a PointerManager. - * @param engine - The current engine instance - * @param htmlCanvas - HTMLCanvasElement + * @internal */ - constructor(engine: Engine) { - // @ts-ignore - const htmlCanvas = engine._canvas._webCanvas; + constructor(engine: Engine, target: EventTarget) { + if (target instanceof Window) { + throw "Do not set window as target because window cannot listen to pointer leave event."; + } this._engine = engine; + this._target = target; this._canvas = engine.canvas; - this._htmlCanvas = htmlCanvas; - this._onPointerEvent = this._onPointerEvent.bind(this); - this._onFocus(); + // @ts-ignore + this._htmlCanvas = engine._canvas._webCanvas; // If there are no compatibility issues, navigator.maxTouchPoints should be used here this._pointerPool = new Array(11); + this._onPointerEvent = this._onPointerEvent.bind(this); + this._addEventListener(); } /** @@ -130,7 +132,8 @@ export class PointerManager implements IInput { const length = events.length; if (length > 0) { for (let i = 0; i < length; i++) { - switch (events[i].type) { + const event = events[i]; + switch (event.type) { case "pointerdown": pointer.phase = PointerPhase.Down; pointer._firePointerDown(rayCastEntity); @@ -151,75 +154,18 @@ export class PointerManager implements IInput { } } - /** - * @internal - */ - _onFocus(): void { - if (!this._hadListener) { - const { _htmlCanvas: htmlCanvas, _onPointerEvent: onPointerEvent } = this; - htmlCanvas.addEventListener("pointerdown", onPointerEvent); - htmlCanvas.addEventListener("pointerup", onPointerEvent); - htmlCanvas.addEventListener("pointerleave", onPointerEvent); - htmlCanvas.addEventListener("pointermove", onPointerEvent); - htmlCanvas.addEventListener("pointercancel", onPointerEvent); - this._hadListener = true; - } - } - - /** - * @internal - */ - _onBlur(): void { - if (this._hadListener) { - const { _htmlCanvas: htmlCanvas, _onPointerEvent: onPointerEvent } = this; - htmlCanvas.removeEventListener("pointerdown", onPointerEvent); - htmlCanvas.removeEventListener("pointerup", onPointerEvent); - htmlCanvas.removeEventListener("pointerleave", onPointerEvent); - htmlCanvas.removeEventListener("pointermove", onPointerEvent); - htmlCanvas.removeEventListener("pointercancel", onPointerEvent); - this._hadListener = false; - this._pointers.length = 0; - this._downList.length = 0; - this._upList.length = 0; - this._nativeEvents.length = 0; - } - } - /** * @internal */ _destroy(): void { - // @ts-ignore - if (this._hadListener) { - const { _htmlCanvas: htmlCanvas, _onPointerEvent: onPointerEvent } = this; - htmlCanvas.removeEventListener("pointerdown", onPointerEvent); - htmlCanvas.removeEventListener("pointerup", onPointerEvent); - htmlCanvas.removeEventListener("pointerleave", onPointerEvent); - htmlCanvas.removeEventListener("pointermove", onPointerEvent); - htmlCanvas.removeEventListener("pointercancel", onPointerEvent); - this._hadListener = false; - } + this._removeEventListener(); this._pointerPool.length = 0; - this._pointerPool = null; - this._pointers.length = 0; - this._pointers = null; - this._downList.length = 0; - this._downList = null; - this._upList.length = 0; - this._upList = null; this._nativeEvents.length = 0; - this._nativeEvents = null; - this._upMap.length = 0; - this._upMap = null; this._downMap.length = 0; - this._downMap = null; - this._htmlCanvas = null; - this._canvas = null; - this._engine = null; + this._upMap.length = 0; } private _onPointerEvent(evt: PointerEvent) { - evt.type === "pointerdown" && this._htmlCanvas.focus(); this._nativeEvents.push(evt); } @@ -322,4 +268,26 @@ export class PointerManager implements IInput { } return null; } + + private _addEventListener(): void { + const { _target: target, _onPointerEvent: onPointerEvent } = this; + target.addEventListener("pointerdown", onPointerEvent); + target.addEventListener("pointerup", onPointerEvent); + target.addEventListener("pointerleave", onPointerEvent); + target.addEventListener("pointermove", onPointerEvent); + target.addEventListener("pointercancel", onPointerEvent); + } + + private _removeEventListener(): void { + const { _target: target, _onPointerEvent: onPointerEvent } = this; + target.removeEventListener("pointerdown", onPointerEvent); + target.removeEventListener("pointerup", onPointerEvent); + target.removeEventListener("pointerleave", onPointerEvent); + target.removeEventListener("pointermove", onPointerEvent); + target.removeEventListener("pointercancel", onPointerEvent); + this._nativeEvents.length = 0; + this._pointers.length = 0; + this._downList.length = 0; + this._upList.length = 0; + } } diff --git a/packages/core/src/input/wheel/WheelManager.ts b/packages/core/src/input/wheel/WheelManager.ts index 6022cafb86..1f435a6de4 100644 --- a/packages/core/src/input/wheel/WheelManager.ts +++ b/packages/core/src/input/wheel/WheelManager.ts @@ -10,20 +10,17 @@ export class WheelManager implements IInput { /** @internal */ _delta: Vector3 = new Vector3(); + // @internal + _target: EventTarget; private _nativeEvents: WheelEvent[] = []; - private _canvas: HTMLCanvasElement; - private _hadListener: boolean; /** - * Create a KeyboardManager. + * @internal */ - constructor(engine: Engine) { - // @ts-ignore - const htmlCanvas = engine._canvas._webCanvas; + constructor(engine: Engine, target: EventTarget) { this._onWheelEvent = this._onWheelEvent.bind(this); - htmlCanvas.addEventListener("wheel", this._onWheelEvent); - this._canvas = htmlCanvas; - this._hadListener = true; + this._target = target; + this._addEventListener(); } /** @@ -47,40 +44,29 @@ export class WheelManager implements IInput { /** * @internal */ - _onFocus(): void { - if (!this._hadListener) { - this._canvas.addEventListener("wheel", this._onWheelEvent); - this._hadListener = true; - } + _addEventListener(): void { + this._target.addEventListener("wheel", this._onWheelEvent); } /** * @internal */ - _onBlur(): void { - if (this._hadListener) { - this._canvas.removeEventListener("wheel", this._onWheelEvent); - this._nativeEvents.length = 0; - this._delta.set(0, 0, 0); - this._hadListener = false; - } + _removeEventListener(): void { + this._target.removeEventListener("wheel", this._onWheelEvent); + this._nativeEvents.length = 0; + this._delta.set(0, 0, 0); } /** * @internal */ _destroy(): void { - if (this._hadListener) { - this._canvas.removeEventListener("wheel", this._onWheelEvent); - this._hadListener = false; - } + this._removeEventListener(); this._nativeEvents = null; - this._canvas = null; this._delta = null; } private _onWheelEvent(evt: WheelEvent): void { - evt.cancelable && evt.preventDefault(); this._nativeEvents.push(evt); } } diff --git a/packages/design/src/index.ts b/packages/design/src/index.ts index 860010bea7..ba1d14b58c 100644 --- a/packages/design/src/index.ts +++ b/packages/design/src/index.ts @@ -3,3 +3,4 @@ export * from "./physics/index"; export * from "./renderingHardwareInterface/index"; export * from "./shader-lab/index"; export * from "./xr/index"; +export * from "./input/index"; diff --git a/packages/design/src/input/IInputOptions.ts b/packages/design/src/input/IInputOptions.ts new file mode 100644 index 0000000000..25b01b2b5b --- /dev/null +++ b/packages/design/src/input/IInputOptions.ts @@ -0,0 +1,23 @@ +/** + * Options for the input. + */ +export interface IInputOptions { + /** + * The target element of the pointer event, defaults is the current canvas. + * @remarks + * @remarks: When setting the pointer target you need to specify: + * - Do not set window as target because window cannot listen to pointer leave event. + * - On mobile, pointer move may trigger the default slide event, thereby removing the point from the screen, so in most cases you need to set HtmlElement.style.touchAction = "none". + */ + pointerTarget?: Exclude; + + /** + * The target element of the keyboard event, defaults is the window. + * @remarks + * If the type of listening target is `HtmlElement`, you need to set tabIndex to make the element focus. + */ + keyboardTarget?: EventTarget; + + /** The target element of the wheel event, defaults is the current canvas. */ + wheelTarget?: EventTarget; +} diff --git a/packages/design/src/input/index.ts b/packages/design/src/input/index.ts new file mode 100644 index 0000000000..b6466fe313 --- /dev/null +++ b/packages/design/src/input/index.ts @@ -0,0 +1 @@ +export type { IInputOptions } from "./IInputOptions"; diff --git a/packages/xr/src/input/XRInputManager.ts b/packages/xr/src/input/XRInputManager.ts index 9a770e7206..9481e6a014 100644 --- a/packages/xr/src/input/XRInputManager.ts +++ b/packages/xr/src/input/XRInputManager.ts @@ -157,21 +157,25 @@ export class XRInputManager { } break; case XRTargetRayMode.Screen: + const { _engine: engine } = this; // @ts-ignore - const canvas = this._engine.canvas._webCanvas; + const target: EventTarget = engine.inputManager._pointerManager._target; + // @ts-ignore + const canvas = engine.canvas._webCanvas; const { clientWidth, clientHeight } = canvas; const clientX = clientWidth * (event.x + 1) * 0.5; const clientY = clientHeight * (event.y + 1) * 0.5; + // @ts-ignore switch (event.type) { case XRInputEventType.SelectStart: - canvas.dispatchEvent(this._makeUpPointerEvent("pointerdown", event.id, clientX, clientY)); + target.dispatchEvent(this._makeUpPointerEvent("pointerdown", event.id, clientX, clientY)); break; case XRInputEventType.Select: - canvas.dispatchEvent(this._makeUpPointerEvent("pointermove", event.id, clientX, clientY)); + target.dispatchEvent(this._makeUpPointerEvent("pointermove", event.id, clientX, clientY)); break; case XRInputEventType.SelectEnd: - canvas.dispatchEvent(this._makeUpPointerEvent("pointerup", event.id, clientX, clientY)); - canvas.dispatchEvent(this._makeUpPointerEvent("pointerleave", event.id, clientX, clientY)); + target.dispatchEvent(this._makeUpPointerEvent("pointerup", event.id, clientX, clientY)); + target.dispatchEvent(this._makeUpPointerEvent("pointerleave", event.id, clientX, clientY)); break; default: break; diff --git a/tests/src/core/input/InputManager.test.ts b/tests/src/core/input/InputManager.test.ts index 6f1b3c1134..32d349b44f 100644 --- a/tests/src/core/input/InputManager.test.ts +++ b/tests/src/core/input/InputManager.test.ts @@ -1,16 +1,16 @@ import { + BoxColliderShape, + Camera, + Keys, + Pointer, PointerButton, PointerPhase, Script, - Keys, - Camera, - StaticCollider, - BoxColliderShape, - Pointer + StaticCollider } from "@galacean/engine-core"; -import { WebGLEngine } from "@galacean/engine-rhi-webgl"; import { Vector2, Vector3 } from "@galacean/engine-math"; import { LitePhysics } from "@galacean/engine-physics-lite"; +import { WebGLEngine } from "@galacean/engine-rhi-webgl"; import chai, { expect } from "chai"; import spies from "chai-spies"; @@ -51,12 +51,13 @@ describe("InputManager", async () => { it("pointer", () => { // @ts-ignore - inputManager._onFocus(); - canvasDOM.dispatchEvent(generatePointerEvent("pointerdown", 1, 1, 1)); - canvasDOM.dispatchEvent(generatePointerEvent("pointermove", 1, 1, 1)); - canvasDOM.dispatchEvent(generatePointerEvent("pointerup", 1, 1, 1, 0, 0)); - canvasDOM.dispatchEvent(generatePointerEvent("pointerleave", 1, 1, 1, -1, 0)); - canvasDOM.dispatchEvent(generatePointerEvent("pointercancel", 1, 1, 1, -1, 0)); + const { _pointerManager: pointerManager } = inputManager; + const { _target: target } = pointerManager; + target.dispatchEvent(generatePointerEvent("pointerdown", 1, 1, 1)); + target.dispatchEvent(generatePointerEvent("pointermove", 1, 1, 1)); + target.dispatchEvent(generatePointerEvent("pointerup", 1, 1, 1, 0, 0)); + target.dispatchEvent(generatePointerEvent("pointerleave", 1, 1, 1, -1, 0)); + target.dispatchEvent(generatePointerEvent("pointercancel", 1, 1, 1, -1, 0)); engine.update(); const pointers = inputManager.pointers; expect(pointers.length).to.eq(1); @@ -68,7 +69,7 @@ describe("InputManager", async () => { expect(pointer._uniqueID).to.eq(1); expect(pointer.button).to.eq(PointerButton.None); expect(pointer.pressedButtons).to.eq(PointerButton.None); - const { left, top } = canvasDOM.getBoundingClientRect(); + const { left, top } = target.getBoundingClientRect(); expect(pointer.position).to.deep.eq(new Vector2((1 - left) * 2, (1 - top) * 2)); expect(pointer.phase).to.eq(PointerPhase.Leave); expect(inputManager.isPointerDown()).to.eq(true); @@ -122,10 +123,8 @@ describe("InputManager", async () => { TestScript.prototype.onPointerUp = chai.spy(TestScript.prototype.onPointerUp); const script = boxEntity.addComponent(TestScript); - // @ts-ignore - inputManager._onFocus(); - const { left, top } = canvasDOM.getBoundingClientRect(); - canvasDOM.dispatchEvent(generatePointerEvent("pointerdown", 2, left + 2, top + 2)); + const { left, top } = target.getBoundingClientRect(); + target.dispatchEvent(generatePointerEvent("pointerdown", 2, left + 2, top + 2)); engine.update(); expect(script.onPointerEnter).to.have.been.called.exactly(1); @@ -135,11 +134,9 @@ describe("InputManager", async () => { expect(script.onPointerDrag).to.have.been.called.exactly(0); expect(script.onPointerUp).to.have.been.called.exactly(0); - // @ts-ignore - inputManager._onFocus(); - canvasDOM.dispatchEvent(generatePointerEvent("pointermove", 2, left + 2, top + 2)); - canvasDOM.dispatchEvent(generatePointerEvent("pointerup", 2, left + 2, top + 2, 0, 0)); - canvasDOM.dispatchEvent(generatePointerEvent("pointerleave", 2, left + 2, top + 2, -1, 0)); + target.dispatchEvent(generatePointerEvent("pointermove", 2, left + 2, top + 2)); + target.dispatchEvent(generatePointerEvent("pointerup", 2, left + 2, top + 2, 0, 0)); + target.dispatchEvent(generatePointerEvent("pointerleave", 2, left + 2, top + 2, -1, 0)); engine.update(); expect(script.onPointerEnter).to.have.been.called.exactly(1); expect(script.onPointerExit).to.have.been.called.exactly(1); @@ -148,11 +145,9 @@ describe("InputManager", async () => { expect(script.onPointerDrag).to.have.been.called.exactly(1); expect(script.onPointerUp).to.have.been.called.exactly(1); - // @ts-ignore - inputManager._onFocus(); - canvasDOM.dispatchEvent(generatePointerEvent("pointerdown", 3, left + 200, top + 200)); - canvasDOM.dispatchEvent(generatePointerEvent("pointerup", 3, left + 200, top + 200, 0, 0)); - canvasDOM.dispatchEvent(generatePointerEvent("pointerleave", 3, left + 200, top + 200, -1, 0)); + target.dispatchEvent(generatePointerEvent("pointerdown", 3, left + 200, top + 200)); + target.dispatchEvent(generatePointerEvent("pointerup", 3, left + 200, top + 200, 0, 0)); + target.dispatchEvent(generatePointerEvent("pointerleave", 3, left + 200, top + 200, -1, 0)); engine.update(); expect(script.onPointerEnter).to.have.been.called.exactly(1); expect(script.onPointerExit).to.have.been.called.exactly(1); @@ -161,20 +156,21 @@ describe("InputManager", async () => { expect(script.onPointerDrag).to.have.been.called.exactly(1); expect(script.onPointerUp).to.have.been.called.exactly(1); - // @ts-ignore - inputManager._onFocus(); - canvasDOM.dispatchEvent(generatePointerEvent("pointerdown", 4, 0, 0)); + target.dispatchEvent(generatePointerEvent("pointerdown", 4, 0, 0)); engine.update(); const deltaPosition = engine.inputManager.pointers[0].deltaPosition; expect(deltaPosition).deep.equal(new Vector2(0, 0)); + target.dispatchEvent(generatePointerEvent("pointerleave", 4, 0, 0)); + engine.update(); }); it("keyboard", () => { // @ts-ignore - inputManager._onFocus(); - canvasDOM.dispatchEvent(generateKeyboardEvent("keydown", "KeyA")); - canvasDOM.dispatchEvent(generateKeyboardEvent("keydown", "KeyB")); - canvasDOM.dispatchEvent(generateKeyboardEvent("keyup", "KeyA")); + const { _keyboardManager: keyboardManager } = inputManager; + const { _target: target } = keyboardManager; + target.dispatchEvent(generateKeyboardEvent("keydown", "KeyA")); + target.dispatchEvent(generateKeyboardEvent("keydown", "KeyB")); + target.dispatchEvent(generateKeyboardEvent("keyup", "KeyA")); engine.update(); expect(inputManager.isKeyDown()).to.eq(true); expect(inputManager.isKeyUp()).to.eq(true); @@ -185,100 +181,74 @@ describe("InputManager", async () => { expect(inputManager.isKeyDown(Keys.KeyB)).to.eq(true); expect(inputManager.isKeyUp(Keys.KeyB)).to.eq(false); expect(inputManager.isKeyHeldDown(Keys.KeyB)).to.eq(true); - // @ts-ignore - inputManager._onFocus(); - canvasDOM.dispatchEvent(generateKeyboardEvent("keyup", "KeyB")); + target.dispatchEvent(generateKeyboardEvent("keyup", "KeyB")); engine.update(); expect(inputManager.isKeyUp(Keys.KeyB)).to.eq(true); expect(inputManager.isKeyHeldDown(Keys.KeyB)).to.eq(false); - canvasDOM.dispatchEvent(generateKeyboardEvent("keydown", "KeyA")); - canvasDOM.dispatchEvent(generateKeyboardEvent("keyup", "MetaRight")); + target.dispatchEvent(generateKeyboardEvent("keydown", "KeyA")); + target.dispatchEvent(generateKeyboardEvent("keyup", "MetaRight")); expect(inputManager.isKeyHeldDown(Keys.KeyA)).to.eq(false); }); it("wheel", () => { // @ts-ignore - inputManager._onFocus(); - canvasDOM.dispatchEvent(generateWheelEvent(1, 2, 3)); + const { _wheelManager: wheelManager } = inputManager; + const { _target: target } = wheelManager; + target.dispatchEvent(generateWheelEvent(1, 2, 3)); engine.update(); expect(inputManager.wheelDelta).to.deep.eq(new Vector3(1, 2, 3)); }); it("blur and focus", () => { // @ts-ignore - inputManager._onFocus(); - window.dispatchEvent(new Event("blur")); - // @ts-ignore - expect(inputManager._pointerManager._hadListener).to.eq(false); - // @ts-ignore - expect(inputManager._wheelManager._hadListener).to.eq(false); - // @ts-ignore - expect(inputManager._keyboardManager._hadListener).to.eq(false); - - window.dispatchEvent(new Event("focus")); - // @ts-ignore - expect(inputManager._pointerManager._hadListener).to.eq(true); - // @ts-ignore - expect(inputManager._wheelManager._hadListener).to.eq(true); - // @ts-ignore - expect(inputManager._keyboardManager._hadListener).to.eq(true); + const { _keyboardManager: keyboardManager } = inputManager; + const { _target: target } = keyboardManager; + target.dispatchEvent(generateKeyboardEvent("keydown", "KeyA")); + engine.update(); + expect(inputManager.isKeyDown(Keys.KeyA)).to.eq(true); + target.dispatchEvent(new Event("blur")); + expect(inputManager.isKeyDown()).to.eq(false); + }); + it("change listener target", () => { + try { + WebGLEngine.create({ canvas: canvasDOM, input: { pointerTarget: window } }); + } catch (error) { + expect(error).to.eq("Do not set window as target because window cannot listen to pointer leave event."); + } + window.dispatchEvent(generatePointerEvent("pointerdown", 1, 1, 1)); engine.update(); + expect(inputManager.pointers.length).to.eq(0); canvasDOM.dispatchEvent(generatePointerEvent("pointerdown", 1, 1, 1)); + engine.update(); + expect(inputManager.pointers.length).to.eq(1); + window.dispatchEvent(generatePointerEvent("pointerleave", 1, 1, 1)); + canvasDOM.dispatchEvent(generatePointerEvent("pointerleave", 1, 1, 1)); + canvasDOM.dispatchEvent(generateKeyboardEvent("keydown", "KeyA")); - canvasDOM.dispatchEvent(generateWheelEvent(1, 1, 1)); - window.dispatchEvent(new Event("blur")); engine.update(); - expect(inputManager.pointers.length).to.eq(0); expect(inputManager.isKeyDown()).to.eq(false); + window.dispatchEvent(generateKeyboardEvent("keydown", "KeyA")); + engine.update(); + expect(inputManager.isKeyDown()).to.eq(true); + + window.dispatchEvent(generateWheelEvent(1, 2, 3)); + engine.update(); expect(inputManager.wheelDelta).to.deep.eq(new Vector3(0, 0, 0)); + canvasDOM.dispatchEvent(generateWheelEvent(1, 2, 3)); + engine.update(); + expect(inputManager.wheelDelta).to.deep.eq(new Vector3(1, 2, 3)); }); it("destroy", () => { - // @ts-ignore - const wheel = inputManager._wheelManager; - // @ts-ignore - const pointer = inputManager._pointerManager; - // @ts-ignore - const keyboard = inputManager._keyboardManager; - engine.destroy(); - window.dispatchEvent(new Event("focus")); - expect(pointer._hadListener).to.eq(false); - expect(wheel._hadListener).to.eq(false); - expect(keyboard._hadListener).to.eq(false); // @ts-ignore expect(inputManager._pointerManager).to.eq(null); // @ts-ignore expect(inputManager._wheelManager).to.eq(null); // @ts-ignore expect(inputManager._keyboardManager).to.eq(null); - - expect(pointer._pointerPool).to.eq(null); - expect(pointer._pointers).to.eq(null); - expect(pointer._downList).to.eq(null); - expect(pointer._upList).to.eq(null); - expect(pointer._nativeEvents).to.eq(null); - expect(pointer._upMap).to.eq(null); - expect(pointer._downMap).to.eq(null); - expect(pointer._htmlCanvas).to.eq(null); - expect(pointer._canvas).to.eq(null); - expect(pointer._engine).to.eq(null); - - expect(wheel._delta).to.eq(null); - expect(wheel._nativeEvents).to.eq(null); - expect(wheel._canvas).to.eq(null); - - expect(keyboard._curHeldDownKeyToIndexMap).to.eq(null); - expect(keyboard._upKeyToFrameCountMap).to.eq(null); - expect(keyboard._downKeyToFrameCountMap).to.eq(null); - expect(keyboard._nativeEvents).to.eq(null); - expect(keyboard._curFrameHeldDownList).to.eq(null); - expect(keyboard._curFrameDownList).to.eq(null); - expect(keyboard._curFrameUpList).to.eq(null); - expect(keyboard._htmlCanvas).to.eq(null); - expect(keyboard._engine).to.eq(null); }); });