From 11a8857d1dc7bcd0e9e3171045fb64bd7f882252 Mon Sep 17 00:00:00 2001 From: sago35 Date: Sat, 23 Mar 2024 18:26:29 +0900 Subject: [PATCH 1/2] Add mouse drag panning support and align wheel behavior with KiCad --- src/base/dom/pan-and-zoom.ts | 84 ++++++++++++++++--- .../elements/common/preferences-panel.ts | 18 +++- src/kicanvas/preferences.ts | 6 ++ 3 files changed, 97 insertions(+), 11 deletions(-) diff --git a/src/base/dom/pan-and-zoom.ts b/src/base/dom/pan-and-zoom.ts index 476110f4..5b76124a 100644 --- a/src/base/dom/pan-and-zoom.ts +++ b/src/base/dom/pan-and-zoom.ts @@ -5,6 +5,7 @@ */ import { BBox, Camera2, Vec2 } from "../math"; +import { Preferences } from "../../kicanvas/preferences"; const line_delta_multiplier = 8; const page_delta_multiplier = 24; @@ -12,6 +13,7 @@ const zoom_speed = 0.005; const pan_speed = 1; export type PanAndZoomCallback = () => void; +const prefs = Preferences.INSTANCE; /** * Interactive Pan and Zoom helper @@ -35,6 +37,19 @@ export class PanAndZoom { public max_zoom = 10, public bounds?: BBox, ) { + Preferences.INSTANCE.addEventListener( + "kicanvas:preferences:change", + () => { + //this.updateSettings(Preferences.INSTANCE); + console.log( + "Preferences changed:", + prefs.alignControlsWithKiCad, + ); + }, + ); + + //this.updateSettings(Preferences.INSTANCE); + this.target.addEventListener( "wheel", (e: WheelEvent) => this.#on_wheel(e), @@ -84,6 +99,38 @@ export class PanAndZoom { startDistance = null; startPosition = null; }); + + let dragStartPosition: Vec2 | null = null; + let dragging = false; + + this.target.addEventListener("mousedown", (e: MouseEvent) => { + if (e.button === 1 || e.button === 2) { + e.preventDefault(); + dragging = true; + dragStartPosition = new Vec2(e.clientX, e.clientY); + } + }); + + this.target.addEventListener("mousemove", (e: MouseEvent) => { + if (dragging && dragStartPosition !== null) { + const currentPosition = new Vec2(e.clientX, e.clientY); + const delta = currentPosition.sub(dragStartPosition); + this.#handle_pan(-delta.x, -delta.y); + dragStartPosition = currentPosition; + } + }); + + this.target.addEventListener("mouseup", (e: MouseEvent) => { + if (e.button === 1 || e.button === 2) { + dragging = false; + dragStartPosition = null; + } + }); + + // コンテキストメニューの表示を防ぐためのイベントハンドラを追加 + this.target.addEventListener("contextmenu", (e) => { + e.preventDefault(); + }); } #getDistanceBetweenTouches(touches: TouchList) { @@ -102,8 +149,14 @@ export class PanAndZoom { let dy = e.deltaY; // shift modifier flips the X and Y axes (horizontal scroll) - if (dx == 0 && e.shiftKey) { - [dx, dy] = [dy, dx]; + if (!prefs.alignControlsWithKiCad) { + if (dx == 0 && e.shiftKey) { + [dx, dy] = [dy, dx]; + } + } else { + if (dx == 0 && e.ctrlKey) { + [dx, dy] = [dy, dx]; + } } // work around line/page scrolling @@ -119,14 +172,25 @@ export class PanAndZoom { dx = Math.sign(dx) * Math.min(page_delta_multiplier, Math.abs(dx)); dy = Math.sign(dy) * Math.min(page_delta_multiplier, Math.abs(dy)); - // pinch zoom - if (e.ctrlKey) { - this.#rect = this.target.getBoundingClientRect(); - this.#handle_zoom(dy, this.#relative_mouse_pos(e)); - } - // pan - else { - this.#handle_pan(dx, dy); + if (!prefs.alignControlsWithKiCad) { + // pinch zoom + if (e.ctrlKey) { + this.#rect = this.target.getBoundingClientRect(); + this.#handle_zoom(dy, this.#relative_mouse_pos(e)); + } + // pan + else { + this.#handle_pan(dx, dy); + } + } else { + if (e.shiftKey || e.ctrlKey) { + this.#handle_pan(-dx, dy); + } + // pinch zoom + else { + this.#rect = this.target.getBoundingClientRect(); + this.#handle_zoom(dy, this.#relative_mouse_pos(e)); + } } this.target.dispatchEvent( diff --git a/src/kicanvas/elements/common/preferences-panel.ts b/src/kicanvas/elements/common/preferences-panel.ts index e2692447..b8c28d37 100644 --- a/src/kicanvas/elements/common/preferences-panel.ts +++ b/src/kicanvas/elements/common/preferences-panel.ts @@ -63,7 +63,14 @@ export class KCPreferencesPanel extends KCUIElement { override initialContentCallback() { this.renderRoot.addEventListener("input", (e) => { - prefs.theme = themes.by_name(this.theme_control.value); + const target = e.target as HTMLInputElement; + + if (target.name === "theme") { + prefs.theme = themes.by_name(this.theme_control.value); + } + if (target.name === "align-controls-kicad") { + prefs.alignControlsWithKiCad = target.checked; + } prefs.save(); }); } @@ -89,6 +96,15 @@ export class KCPreferencesPanel extends KCUIElement { + + + `; diff --git a/src/kicanvas/preferences.ts b/src/kicanvas/preferences.ts index 54061934..d2d011df 100644 --- a/src/kicanvas/preferences.ts +++ b/src/kicanvas/preferences.ts @@ -17,9 +17,11 @@ export class Preferences extends EventTarget { private storage = new LocalStorage("kc:prefs"); public theme: Theme = themes.default; + public alignControlsWithKiCad: boolean = true; public save() { this.storage.set("theme", this.theme.name); + this.storage.set("alignControlsWithKiCad", this.alignControlsWithKiCad); this.dispatchEvent(new PreferencesChangeEvent({ preferences: this })); } @@ -27,6 +29,10 @@ export class Preferences extends EventTarget { this.theme = themes.by_name( this.storage.get("theme", themes.default.name), ); + this.alignControlsWithKiCad = this.storage.get( + "alignControlsWithKiCad", + false, + ); } } From a755789ec5de435af1e9b0ac9e23af299a217b8c Mon Sep 17 00:00:00 2001 From: sago35 Date: Fri, 24 May 2024 08:55:25 +0900 Subject: [PATCH 2/2] Clean up unnecessary code and comments --- src/base/dom/pan-and-zoom.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/base/dom/pan-and-zoom.ts b/src/base/dom/pan-and-zoom.ts index 5b76124a..65ed895e 100644 --- a/src/base/dom/pan-and-zoom.ts +++ b/src/base/dom/pan-and-zoom.ts @@ -37,19 +37,6 @@ export class PanAndZoom { public max_zoom = 10, public bounds?: BBox, ) { - Preferences.INSTANCE.addEventListener( - "kicanvas:preferences:change", - () => { - //this.updateSettings(Preferences.INSTANCE); - console.log( - "Preferences changed:", - prefs.alignControlsWithKiCad, - ); - }, - ); - - //this.updateSettings(Preferences.INSTANCE); - this.target.addEventListener( "wheel", (e: WheelEvent) => this.#on_wheel(e), @@ -127,7 +114,7 @@ export class PanAndZoom { } }); - // コンテキストメニューの表示を防ぐためのイベントハンドラを追加 + // Prevent the browser's default context menu. this.target.addEventListener("contextmenu", (e) => { e.preventDefault(); });