diff --git a/packages/input/src/input-handler.ts b/packages/input/src/input-handler.ts index 13c83bf..5be2d23 100644 --- a/packages/input/src/input-handler.ts +++ b/packages/input/src/input-handler.ts @@ -8,6 +8,7 @@ import { } from "./mouse.types"; export class InputHandler { + private offset: { x: number; y: number }; public inputs: Record = {}; public mouse: MouseState = { x: 0, @@ -33,9 +34,11 @@ export class InputHandler { deltaY: 0, }; - constructor() { + constructor(container: HTMLDivElement) { this.resetInputs(); + this.offset = container.getBoundingClientRect(); + window.addEventListener("keydown", (e: KeyboardEvent) => { this.inputs[e.code] = true; }); @@ -44,7 +47,7 @@ export class InputHandler { this.inputs[e.code] = false; }); - window.addEventListener("mousedown", (e: MouseEvent) => { + container.addEventListener("mousedown", (e: MouseEvent) => { const button = MOUSE_BUTTON_MAP[e.button]; if (button === undefined) return; @@ -53,15 +56,15 @@ export class InputHandler { this.drag.active = true; this.drag.button = button; - this.drag.startX = e.clientX; - this.drag.startY = e.clientY; - this.drag.x = e.clientX; - this.drag.y = e.clientY; + this.drag.startX = e.clientX - this.offset.x; + this.drag.startY = e.clientY - this.offset.y; + this.drag.x = e.clientX - this.offset.x; + this.drag.y = e.clientY - this.offset.y; this.drag.deltaX = 0; this.drag.deltaY = 0; }); - window.addEventListener("mouseup", (e: MouseEvent) => { + container.addEventListener("mouseup", (e: MouseEvent) => { const button = MOUSE_BUTTON_MAP[e.button]; if (button !== undefined) this.inputs[button] = false; @@ -77,37 +80,37 @@ export class InputHandler { } }); - window.addEventListener("mousemove", (e: MouseEvent) => { + container.addEventListener("mousemove", (e: MouseEvent) => { this.updatePointer(e); this.updateInputsMouseButtons(e.buttons); if (this.drag.active) { - this.drag.x = e.clientX; - this.drag.y = e.clientY; - this.drag.deltaX = e.clientX - this.drag.startX; - this.drag.deltaY = e.clientY - this.drag.startY; + this.drag.x = e.clientX - this.offset.x; + this.drag.y = e.clientY - this.offset.y; + this.drag.deltaX = e.clientX - this.drag.startX - this.offset.x; + this.drag.deltaY = e.clientY - this.drag.startY - this.offset.y; } }); - window.addEventListener("wheel", (e: WheelEvent) => { + container.addEventListener("wheel", (e: WheelEvent) => { this.wheel.deltaX += e.deltaX; this.wheel.deltaY += e.deltaY; this.wheel.deltaZ += e.deltaZ; }); - window.addEventListener("mouseenter", () => { + container.addEventListener("mouseenter", () => { this.mouse.focus = true; }); - window.addEventListener("mouseleave", () => { + container.addEventListener("mouseleave", () => { this.mouse.focus = false; }); - window.addEventListener("blur", () => { + container.addEventListener("blur", () => { this.resetInputs(); }); - document.addEventListener("visibilitychange", () => { + container.addEventListener("visibilitychange", () => { if (document.hidden) this.resetInputs(); }); @@ -140,8 +143,8 @@ export class InputHandler { private updatePointer(e: MouseEvent): void { this.mouse.prevX = this.mouse.x; this.mouse.prevY = this.mouse.y; - this.mouse.x = e.clientX; - this.mouse.y = e.clientY; + this.mouse.x = e.clientX - this.offset.x; + this.mouse.y = e.clientY - this.offset.y; this.mouse.deltaX = this.mouse.x - this.mouse.prevX; this.mouse.deltaY = this.mouse.y - this.mouse.prevY; } diff --git a/packages/input/src/input.library.ts b/packages/input/src/input.library.ts index 4dfb13d..dd2f4b9 100644 --- a/packages/input/src/input.library.ts +++ b/packages/input/src/input.library.ts @@ -1,4 +1,4 @@ -import { BaseInputLibrary, GRAPHICS_LIBRARY } from "@nanoforge-dev/common"; +import { BaseInputLibrary, GRAPHICS_LIBRARY, type InitContext } from "@nanoforge-dev/common"; import { InputHandler } from "./input-handler"; import { type InputEnum } from "./input.enum"; @@ -37,8 +37,12 @@ export class InputLibrary extends BaseInputLibrary { } /** @internal */ - public override async __init(): Promise { - this._inputHandler = new InputHandler(); + public override async __init(context: InitContext): Promise { + if (!context.container) + throw new Error( + "Input library must be registered on client side (InitContext must contain a container element)", + ); + this._inputHandler = new InputHandler(context.container); } /** @internal */ diff --git a/packages/input/test/input.library.spec.ts b/packages/input/test/input.library.spec.ts index f0ae56e..6897e9c 100644 --- a/packages/input/test/input.library.spec.ts +++ b/packages/input/test/input.library.spec.ts @@ -1,5 +1,8 @@ +import { type IConfigRegistry, InitContext } from "@nanoforge-dev/common"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { EditableApplicationContext } from "../../core/src/common/context/contexts/application.editable-context"; +import { EditableLibraryManager } from "../../core/src/common/library/manager/library.manager"; import { InputEnum, InputLibrary } from "../src"; type MockInputEvent = { @@ -26,14 +29,30 @@ const makeEventTargetMock = () => { }; }; +const makeContext = (container: HTMLDivElement | null) => { + const libraryManager = new EditableLibraryManager(); + const appContext = new EditableApplicationContext(libraryManager); + const configRegistry = {} as IConfigRegistry; + return new InitContext(appContext, libraryManager, configRegistry, { + // @ts-ignore + container, + files: new Map(), + }); +}; + describe("InputLibrary", () => { - let windowMock: ReturnType; - let documentMock: ReturnType & { hidden: boolean }; + let windowMock: ReturnType & { + getBoundingClientRect: () => { x: number; y: number }; + }; + let documentMock: ReturnType & { + hidden: boolean; + }; beforeEach(() => { - windowMock = makeEventTargetMock(); + windowMock = { ...makeEventTargetMock(), getBoundingClientRect: () => ({ x: 0, y: 0 }) }; documentMock = { ...makeEventTargetMock(), + hidden: false, }; @@ -75,7 +94,8 @@ describe("InputLibrary", () => { beforeEach(async () => { library = new InputLibrary(); - await library.__init(); + console.log(window); + await library.__init(makeContext(window as unknown as HTMLDivElement)); }); it("should register all expected event listeners", () => { @@ -88,7 +108,7 @@ describe("InputLibrary", () => { expect(windowMock.addEventListener).toHaveBeenCalledWith("mouseenter", expect.any(Function)); expect(windowMock.addEventListener).toHaveBeenCalledWith("mouseleave", expect.any(Function)); expect(windowMock.addEventListener).toHaveBeenCalledWith("blur", expect.any(Function)); - expect(documentMock.addEventListener).toHaveBeenCalledWith( + expect(windowMock.addEventListener).toHaveBeenCalledWith( "visibilitychange", expect.any(Function), ); @@ -237,7 +257,7 @@ describe("InputLibrary", () => { windowMock.dispatch("keydown", { code: InputEnum.KeyA }); documentMock.hidden = true; - documentMock.dispatch("visibilitychange", {}); + windowMock.dispatch("visibilitychange", {}); expect(library.isKeyPressed(InputEnum.KeyA)).toBe(false); });