Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions packages/input/src/input-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "./mouse.types";

export class InputHandler {
private offset: { x: number; y: number };
public inputs: Record<string, boolean> = {};
public mouse: MouseState = {
x: 0,
Expand All @@ -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;
});
Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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();
});

Expand Down Expand Up @@ -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;
}
Expand Down
10 changes: 7 additions & 3 deletions packages/input/src/input.library.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -37,8 +37,12 @@ export class InputLibrary extends BaseInputLibrary {
}

/** @internal */
public override async __init(): Promise<void> {
this._inputHandler = new InputHandler();
public override async __init(context: InitContext): Promise<void> {
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 */
Expand Down
32 changes: 26 additions & 6 deletions packages/input/test/input.library.spec.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -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<typeof makeEventTargetMock>;
let documentMock: ReturnType<typeof makeEventTargetMock> & { hidden: boolean };
let windowMock: ReturnType<typeof makeEventTargetMock> & {
getBoundingClientRect: () => { x: number; y: number };
};
let documentMock: ReturnType<typeof makeEventTargetMock> & {
hidden: boolean;
};

beforeEach(() => {
windowMock = makeEventTargetMock();
windowMock = { ...makeEventTargetMock(), getBoundingClientRect: () => ({ x: 0, y: 0 }) };
documentMock = {
...makeEventTargetMock(),

hidden: false,
};

Expand Down Expand Up @@ -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", () => {
Expand All @@ -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),
);
Expand Down Expand Up @@ -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);
});
Expand Down
Loading