Skip to content

Commit baeb8f3

Browse files
authored
Merge pull request #6 from CSDotNET0211/dev
Dev
2 parents 78f3b9e + 8d6ba36 commit baeb8f3

14 files changed

Lines changed: 692 additions & 1867 deletions

File tree

package-lock.json

Lines changed: 10 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"@tauri-apps/plugin-dialog": "2.2.2",
2020
"@tauri-apps/plugin-fs": "2.3.0",
2121
"@tauri-apps/plugin-opener": "2.2.7",
22-
"@tauri-apps/plugin-process": "^2.2.1",
22+
"@tauri-apps/plugin-process": "2.2.2",
2323
"@tauri-apps/plugin-shell": "2.2.1",
2424
"bson": "^6.10.4",
2525
"chart.js": "^4.4.9",
@@ -40,10 +40,11 @@
4040
"@sveltejs/kit": "^2.19.0",
4141
"@sveltejs/vite-plugin-svelte": "^5.0.0",
4242
"@tauri-apps/cli": "^2",
43+
"@tsconfig/svelte": "^5.0.5",
4344
"@types/crypto-js": "^4.2.2",
4445
"@types/sql.js": "^1.4.9",
4546
"svelte": "^5.0.0",
46-
"svelte-check": "^4.0.0",
47+
"svelte-check": "^4.3.3",
4748
"svelte-dnd-action": "^0.9.64",
4849
"typescript": "~5.6.2",
4950
"vite": "^6.2.6"

src/app/stores/window.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import PreferencesWindow from "../../features/windows/preferences/preferencesWin
55
import CanvasWindow from "../../features/windows/canvas/canvasWindow.svelte";
66

77

8-
export const DEFAULT_WINDOW_WIDTH = 532;
9-
export const DEFAULT_WINDOW_HEIGHT = 770;
8+
export const DEFAULT_WINDOW_WIDTH = 532 + 20;
9+
export const DEFAULT_WINDOW_HEIGHT = 770 + 20;
1010

11-
export const DEFAULT_TETRIS_CANVAS_WIDTH = 310;
12-
export const DEFAULT_TETRIS_CANVAS_HEIGHT = 713;
11+
export const DEFAULT_TETRIS_CANVAS_WIDTH = 310 + 20;
12+
export const DEFAULT_TETRIS_CANVAS_HEIGHT = 713 + 20;
1313

1414
export enum WindowType {
1515
Field,

src/core/commands/builtinCommands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export function registerCommands() {
121121
const uint8Array = new Uint8Array(arrayBuffer);
122122

123123
currentFieldIndex.set(-1);
124-
await get(nodeUpdater)!.load(uint8Array, false);
124+
await get(nodeUpdater)!.load(uint8Array, true);
125125

126126
// Set current project path
127127
currentProjectPath.set(file);
Lines changed: 177 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,204 @@
1+
import { get } from "svelte/store";
12
import type { DatabaseNode } from "../../../../core/nodes/DatabaseNode/databaseNode";
3+
import { FieldDatabaseNode } from "../../../../core/nodes/DatabaseNode/fieldDatabaseNode";
4+
import { TextDatabaseNode } from "../../../../core/nodes/DatabaseNode/textDatabaseNode";
5+
import { getNodeDatabase } from "../../../../core/nodes/db";
6+
import { nodeUpdater } from "../../../../core/nodes/NodeUpdater/nodeUpdater";
7+
import { selectedNodeIds } from "../canvasStore";
8+
import { CANVAS_HEIGHT, CANVAS_WIDTH, GRID_SNAP_SIZE } from "../const";
9+
import { clamp } from "../util";
10+
import { close } from "../contextMenu.svelte";
211

312
export class CanvasNode {
413
id: number;
514
type: string;
615
element: HTMLElement | null;
716
active: boolean;
17+
isDragging: boolean;
18+
dragOffsetX: number;
19+
dragOffsetY: number;
820

921
constructor(id: number, type: string) {
1022
this.id = id;
1123
this.type = type;
1224
this.element = null;
1325
this.active = false;
26+
this.isDragging = false;
27+
this.dragOffsetX = 0;
28+
this.dragOffsetY = 0;
1429
}
1530

1631
render(DatabaseNode: DatabaseNode): void {
1732
throw new Error("Method not implemented");
1833
}
1934

20-
click(e: MouseEvent): void {
35+
_pointerDown(e: MouseEvent): void {
2136
throw new Error("Method not implemented");
2237
}
2338

24-
dblClick(e: MouseEvent): void {
25-
throw new Error("Method not implemented");
39+
_dblClick(e: MouseEvent): void {
40+
e.stopPropagation();
2641
}
2742

28-
rightClick(e: MouseEvent): void {
29-
throw new Error("Method not implemented");
43+
// 共通の右クリック処理
44+
protected handleRightClick(e: MouseEvent): void {
45+
e.stopPropagation();
46+
e.preventDefault();
47+
48+
// 右クリック時の選択処理
49+
if (e.ctrlKey) {
50+
// Ctrlキーが押されている場合は追加選択
51+
const currentSelection = get(selectedNodeIds);
52+
if (currentSelection.includes(this.id)) {
53+
selectedNodeIds.set(currentSelection.filter(id => id !== this.id));
54+
} else {
55+
selectedNodeIds.set([...currentSelection, this.id]);
56+
}
57+
} else {
58+
// Ctrlキーが押されていない場合
59+
const currentSelection = get(selectedNodeIds);
60+
// 既に選択されていない場合のみ、このノードのみを選択
61+
if (!currentSelection.includes(this.id)) {
62+
selectedNodeIds.set([this.id]);
63+
}
64+
}
65+
66+
// コンテキストメニューを開くためのイベントを発行
67+
const contextMenuEvent = new CustomEvent('openNodeContextMenu', {
68+
detail: {
69+
x: e.clientX,
70+
y: e.clientY,
71+
id: this.id
72+
}
73+
});
74+
document.dispatchEvent(contextMenuEvent);
75+
}
76+
77+
// 共通のマウスダウン処理
78+
protected handleMouseDown(e: MouseEvent): void {
79+
if (e.button !== 0) return;
80+
e.stopPropagation();
81+
82+
close();
83+
84+
const currentSelection = get(selectedNodeIds);
85+
86+
if (e.ctrlKey) {
87+
if (currentSelection.includes(this.id)) {
88+
selectedNodeIds.set(currentSelection.filter(id => id !== this.id));
89+
} else {
90+
selectedNodeIds.set([...currentSelection, this.id]);
91+
}
92+
} else {
93+
// 既に選択されている場合は何もしない
94+
if (!currentSelection.includes(this.id)) {
95+
selectedNodeIds.set([this.id]);
96+
}
97+
}
98+
99+
this.isDragging = true;
100+
const parent = (this.element as HTMLElement).parentElement;
101+
const parentRect = parent?.getBoundingClientRect();
102+
const scale =
103+
parent?.style.transform?.match(/scale\(([\d.]+)\)/)?.[1]
104+
? parseFloat(parent.style.transform.match(/scale\(([\d.]+)\)/)![1])
105+
: 1;
106+
107+
// 基準点: マウスの押した位置を記録
108+
this.dragOffsetX = (e.clientX - (parentRect?.left ?? 0)) / scale;
109+
this.dragOffsetY = (e.clientY - (parentRect?.top ?? 0)) / scale;
110+
111+
document.body.style.cursor = "grabbing";
112+
document.addEventListener("mousemove", this.handleDragMove);
113+
document.addEventListener("mouseup", this.handleDragEnd);
114+
}
115+
116+
// 共通のドラッグ移動処理
117+
private handleDragMove = async (e: MouseEvent) => {
118+
if (!this.isDragging || !this.element) return;
119+
const parent = this.element.parentElement;
120+
if (!parent) return;
121+
const parentRect = parent.getBoundingClientRect();
122+
const scale =
123+
parent.style.transform?.match(/scale\(([\d.]+)\)/)?.[1]
124+
? parseFloat(parent.style.transform.match(/scale\(([\d.]+)\)/)![1])
125+
: 1;
126+
// 現在のマウス位置を取得
127+
const currentMouseX = (e.clientX - parentRect.left) / scale;
128+
const currentMouseY = (e.clientY - parentRect.top) / scale;
129+
130+
// ドラッグ開始からの移動量を計算
131+
const deltaX = currentMouseX - this.dragOffsetX;
132+
const deltaY = currentMouseY - this.dragOffsetY;
133+
134+
// 実際に適用された移動量を記録するための変数
135+
let actualDeltaX = 0;
136+
let actualDeltaY = 0;
137+
138+
// 選択されているすべてのノードを移動
139+
const selectedIds = get(selectedNodeIds);
140+
141+
// 選択されているノードを一括更新(異なる種類のノードも含む)
142+
for (const nodeId of selectedIds) {
143+
const node = getNodeDatabase(nodeId);
144+
if (node && this.isMovableNode(node)) {
145+
// 各ノードの元の位置
146+
const oldX = node.x!;
147+
const oldY = node.y!;
148+
149+
// 各ノードの新しい位置を計算
150+
const newX = clamp(node.x! + deltaX, 0, CANVAS_WIDTH);
151+
const newY = clamp(node.y! + deltaY, 0, CANVAS_HEIGHT);
152+
const snappedX = Math.round(newX / GRID_SNAP_SIZE) * GRID_SNAP_SIZE;
153+
const snappedY = Math.round(newY / GRID_SNAP_SIZE) * GRID_SNAP_SIZE;
154+
155+
// 実際の移動量を計算(最初のノードの移動量を基準にする)
156+
if (actualDeltaX === 0 && actualDeltaY === 0) {
157+
actualDeltaX = snappedX - oldX;
158+
actualDeltaY = snappedY - oldY;
159+
}
160+
161+
// ノードの種類に応じて適切な更新を行う
162+
await this.updateNodePosition(node, nodeId, snappedX, snappedY);
163+
}
164+
}
165+
166+
// 実際に適用された移動量分だけオフセットを更新(残りの移動量は蓄積される)
167+
this.dragOffsetX += actualDeltaX;
168+
this.dragOffsetY += actualDeltaY;
169+
};
170+
171+
// 共通のドラッグ終了処理
172+
private handleDragEnd = (e: MouseEvent) => {
173+
this.isDragging = false;
174+
document.body.style.cursor = "";
175+
document.removeEventListener("mousemove", this.handleDragMove);
176+
document.removeEventListener("mouseup", this.handleDragEnd);
177+
};
178+
179+
protected handleClick(e: MouseEvent): void {
180+
if (e.button !== 0) return;
181+
e.stopPropagation();
182+
183+
// Ctrlキーが押されていない場合は、このノードのみを選択
184+
if (!e.ctrlKey) {
185+
selectedNodeIds.set([this.id]);
186+
}
187+
}
188+
189+
// ノードが移動可能かどうかを判定
190+
private isMovableNode(node: DatabaseNode): node is FieldDatabaseNode | TextDatabaseNode {
191+
return node instanceof FieldDatabaseNode || node instanceof TextDatabaseNode;
192+
}
193+
194+
// ノードタイプに応じた位置更新
195+
private async updateNodePosition(node: DatabaseNode, nodeId: number, x: number, y: number): Promise<void> {
196+
if (node instanceof FieldDatabaseNode) {
197+
await get(nodeUpdater)!.update(new FieldDatabaseNode(nodeId, x, y, undefined, undefined));
198+
} else if (node instanceof TextDatabaseNode) {
199+
await get(nodeUpdater)!.update(new TextDatabaseNode(nodeId, x, y, undefined, undefined, undefined, undefined));
200+
}
201+
// 新しいノードタイプを追加する場合は、ここに条件を追加するだけ
30202
}
31203

32204
}

0 commit comments

Comments
 (0)