From b35032038706641989f03611a74c7137fefe2e7c Mon Sep 17 00:00:00 2001 From: Mark Mankarious Date: Mon, 3 Apr 2023 01:05:20 +0100 Subject: [PATCH] feat: implements node drag and drop --- src/modes/ModeManager.ts | 41 +++++++++++++++++++++++++--------- src/modes/Select.ts | 17 ++++++++++++++ src/modes/SelectNode.ts | 26 +++++++++++++++++++++ src/modes/types.ts | 6 ++--- src/modes/utils.ts | 11 +++++++++ src/renderer/Renderer.ts | 6 +++++ src/renderer/elements/Nodes.ts | 18 +++++++++++---- 7 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 src/modes/SelectNode.ts create mode 100644 src/modes/utils.ts diff --git a/src/modes/ModeManager.ts b/src/modes/ModeManager.ts index a1e1d64..e9c83a6 100644 --- a/src/modes/ModeManager.ts +++ b/src/modes/ModeManager.ts @@ -5,7 +5,11 @@ import { Mouse } from "./types"; export class ModeManager { renderer?: Renderer = undefined; - currentMode?: ModeBase = undefined; + currentMode?: { + instance: ModeBase; + class: typeof ModeBase; + }; + lastMode?: typeof ModeBase; mouse: Mouse = { position: { x: 0, y: 0 }, delta: null, @@ -19,19 +23,34 @@ export class ModeManager { this.renderer = renderer; } - activateMode(Mode: typeof ModeBase) { + activateMode( + Mode: T, + init?: (instance: InstanceType) => void + ) { if (!this.renderer) return; - const lastMode = this.currentMode; - this.currentMode?.exit(); + if (this.currentMode) { + this.currentMode.instance.exit(); + this.lastMode = this.currentMode.class; + } - this.currentMode = new Mode({ - renderer: this.renderer, - activateMode: this.activateMode.bind(this), - deactivate: lastMode?.exit ?? (() => {}), - }); + this.currentMode = { + instance: new Mode({ + renderer: this.renderer, + activateMode: this.activateMode.bind(this), + deactivate: this.deactivate.bind(this), + }), + class: Mode, + }; - this.currentMode.entry(this.mouse); + init?.(this.currentMode.instance as InstanceType); + this.currentMode.instance.entry(this.mouse); + } + + deactivate() { + if (!this.lastMode) return; + + this.activateMode(this.lastMode); } onMouseEvent(eventName: string, mouse: Mouse) { @@ -43,6 +62,6 @@ export class ModeManager { send(eventName: string, params?: any) { // TODO: Improve typings below // @ts-ignore - this.currentMode?.[eventName]?.(params); + this.currentMode.instance?.[eventName]?.(params); } } diff --git a/src/modes/Select.ts b/src/modes/Select.ts index ad2d17e..9beee70 100644 --- a/src/modes/Select.ts +++ b/src/modes/Select.ts @@ -1,5 +1,8 @@ import { ModeBase } from "./ModeBase"; import { Mouse } from "./types"; +import { getTargetFromSelection } from "./utils"; +import { SelectNode } from "./SelectNode"; +import { Node } from "../renderer/elements/Node"; export class Select extends ModeBase { entry(mouse: Mouse) { @@ -24,6 +27,20 @@ export class Select extends ModeBase { this.ctx.renderer.sceneElements.cursor.disable(); } + MOUSE_DOWN(mouse: Mouse) { + const { renderer } = this.ctx; + const { x, y } = renderer.getTileFromMouse( + mouse.position.x, + mouse.position.y + ); + const items = renderer.getItemsByTile(x, y); + const target = getTargetFromSelection(items); + + if (target instanceof Node) { + this.ctx.activateMode(SelectNode, (instance) => (instance.node = target)); + } + } + MOUSE_MOVE(mouse: Mouse) { const tile = this.ctx.renderer.getTileFromMouse( mouse.position.x, diff --git a/src/modes/SelectNode.ts b/src/modes/SelectNode.ts new file mode 100644 index 0000000..41d6463 --- /dev/null +++ b/src/modes/SelectNode.ts @@ -0,0 +1,26 @@ +import { ModeBase } from "./ModeBase"; +import { Mouse, ModeContext } from "./types"; +import { Node } from "../renderer/elements/Node"; + +export class SelectNode extends ModeBase { + node?: Node; + + constructor(ctx: ModeContext) { + super(ctx); + } + + MOUSE_MOVE(mouse: Mouse) { + if (!this.node) return; + + const tile = this.ctx.renderer.getTileFromMouse( + mouse.position.x, + mouse.position.y + ); + + this.node.moveTo(tile.x, tile.y); + } + + MOUSE_UP() { + this.ctx.deactivate(); + } +} diff --git a/src/modes/types.ts b/src/modes/types.ts index c3f96cb..cd8356b 100644 --- a/src/modes/types.ts +++ b/src/modes/types.ts @@ -1,5 +1,5 @@ import { Renderer } from "../renderer/Renderer"; -import type { ModeBase } from "./ModeBase"; +import type { ModeManager } from "./ModeManager"; export interface Mode { initial: string; @@ -19,6 +19,6 @@ export interface Mouse { export interface ModeContext { renderer: Renderer; - activateMode: (mode: typeof ModeBase) => void; - deactivate: () => void; + activateMode: ModeManager["activateMode"]; + deactivate: ModeManager["deactivate"]; } diff --git a/src/modes/utils.ts b/src/modes/utils.ts new file mode 100644 index 0000000..942a619 --- /dev/null +++ b/src/modes/utils.ts @@ -0,0 +1,11 @@ +import { Node } from "../renderer/elements/Node"; + +export const getTargetFromSelection = (items: (Node | undefined)[]) => { + const node = items.find((item) => item instanceof Node); + + if (node) { + return node; + } + + return null; +}; diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index b4209a2..ba89254 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -258,6 +258,12 @@ export class Renderer { this.callbacks.onSceneChange(sceneEvent.event, this.exportScene()); } + getItemsByTile(x: number, y: number) { + const node = this.nodes.getNodeByTile(x, y); + + return [node].filter((i) => Boolean(i)); + } + get nodes() { return this.sceneElements.nodes; } diff --git a/src/renderer/elements/Nodes.ts b/src/renderer/elements/Nodes.ts index 8262908..e34823f 100644 --- a/src/renderer/elements/Nodes.ts +++ b/src/renderer/elements/Nodes.ts @@ -19,10 +19,6 @@ export class Nodes { this.ctx = ctx; } - getNodeById(id: string) { - return this.nodes.find((node) => node.id === id); - } - addNode(options: NodeOptions, sceneEvent?: SceneEvent) { const node = new Node( this.ctx, @@ -51,9 +47,23 @@ export class Nodes { onMove(x: number, y: number, node: Node) { const tile = this.ctx.getTileBounds(x, y); + node.position = { + x, + y, + }; node.container.position.set(tile.bottom); } + getNodeById(id: string) { + return this.nodes.find((node) => node.id === id); + } + + getNodeByTile(x: number, y: number) { + return this.nodes.find( + (node) => node.position.x === x && node.position.y === y + ); + } + clear() { this.nodes.forEach((node) => node.destroy()); this.nodes = [];