Skip to content

Commit 305c8b2

Browse files
committed
✨ 增加向左向右框选的不同框选策略
1 parent 14fba41 commit 305c8b2

File tree

7 files changed

+105
-4
lines changed

7 files changed

+105
-4
lines changed

app/src/core/service/Settings.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export namespace Settings {
5252
// 控制相关
5353
enableCollision: boolean; // 暂无
5454
enableDragAutoAlign: boolean;
55+
rectangleSelectWhenLeft: "intersect" | "contain";
56+
rectangleSelectWhenRight: "intersect" | "contain";
5557
scaleExponent: number;
5658
allowMoveCameraByWSAD: boolean;
5759
cameraKeyboardMoveReverse: boolean;
@@ -119,6 +121,8 @@ export namespace Settings {
119121
// 控制相关
120122
enableCollision: true,
121123
enableDragAutoAlign: true,
124+
rectangleSelectWhenLeft: "contain",
125+
rectangleSelectWhenRight: "intersect",
122126
scaleExponent: 0.11,
123127
allowMoveCameraByWSAD: false,
124128
cameraKeyboardMoveReverse: false,

app/src/core/service/controlService/controller/concrete/ControllerRectangleSelect.tsx

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Rectangle } from "../../../../dataStruct/shape/Rectangle";
22
import { Vector } from "../../../../dataStruct/Vector";
33
import { Renderer } from "../../../../render/canvas2d/renderer";
4+
import { Stage } from "../../../../stage/Stage";
45
import { SectionMethods } from "../../../../stage/stageManager/basicMethods/SectionMethods";
56
import { StageManager } from "../../../../stage/stageManager/StageManager";
7+
import { StageObject } from "../../../../stage/stageObject/abstract/StageObject";
68
import { Section } from "../../../../stage/stageObject/entity/Section";
79
import { Controller } from "../Controller";
810
import { ControllerClass } from "../ControllerClass";
@@ -104,6 +106,9 @@ class ControllerRectangleSelectClass extends ControllerClass {
104106

105107
// 更新框选框
106108
this.selectingRectangle = Rectangle.fromTwoPoints(this.selectStartLocation, this.selectEndLocation);
109+
// 更新框选方向
110+
this.isSelectDirectionRight = this.selectStartLocation.x < this.selectEndLocation.x;
111+
107112
// 框选框在 section框中的限制情况
108113
if (this.mouseDownSection !== null) {
109114
this.selectingRectangle = Rectangle.getIntersectionRectangle(
@@ -127,7 +132,7 @@ class ControllerRectangleSelectClass extends ControllerClass {
127132
if (entity.isHiddenBySectionCollapse) {
128133
continue;
129134
}
130-
if (entity.collisionBox.isIntersectsWithRectangle(this.selectingRectangle)) {
135+
if (this.isSelectWithEntity(entity)) {
131136
if (Controller.lastSelectedEntityUUID.has(entity.uuid)) {
132137
entity.isSelected = false;
133138
} else {
@@ -136,7 +141,7 @@ class ControllerRectangleSelectClass extends ControllerClass {
136141
}
137142
}
138143
for (const edge of StageManager.getLineEdges()) {
139-
if (edge.collisionBox.isIntersectsWithRectangle(this.selectingRectangle)) {
144+
if (this.isSelectWithEntity(edge)) {
140145
if (Controller.lastSelectedEdgeUUID.has(edge.uuid)) {
141146
edge.isSelected = false;
142147
} else {
@@ -159,7 +164,7 @@ class ControllerRectangleSelectClass extends ControllerClass {
159164
continue;
160165
}
161166

162-
if (otherEntities.collisionBox.isIntersectsWithRectangle(this.selectingRectangle)) {
167+
if (this.isSelectWithEntity(otherEntities)) {
163168
otherEntities.isSelected = true;
164169
isHaveEntity = true;
165170
}
@@ -173,7 +178,7 @@ class ControllerRectangleSelectClass extends ControllerClass {
173178
if (edge.isHiddenBySectionCollapse) {
174179
continue;
175180
}
176-
if (edge.collisionBox.isIntersectsWithRectangle(this.selectingRectangle)) {
181+
if (this.isSelectWithEntity(edge)) {
177182
edge.isSelected = true;
178183
}
179184
}
@@ -186,6 +191,34 @@ class ControllerRectangleSelectClass extends ControllerClass {
186191
ControllerRectangleSelect.lastMoveLocation = worldLocation.clone();
187192
};
188193

194+
/**
195+
* 判断当前的框选框是否选中了某个实体
196+
* @param entity
197+
*/
198+
private isSelectWithEntity(entity: StageObject) {
199+
if (entity.collisionBox && this.selectingRectangle) {
200+
const mode = this.getSelectMode();
201+
if (mode === "intersect") {
202+
return entity.collisionBox.isIntersectsWithRectangle(this.selectingRectangle);
203+
} else {
204+
return entity.collisionBox.isContainedByRectangle(this.selectingRectangle);
205+
}
206+
}
207+
return false;
208+
}
209+
/**
210+
* 当前的框选框的方向
211+
*/
212+
private isSelectDirectionRight = false;
213+
// 获取此时此刻应该的框选逻辑
214+
private getSelectMode(): "contain" | "intersect" {
215+
if (this.isSelectDirectionRight) {
216+
return Stage.rectangleSelectWhenRight;
217+
} else {
218+
return Stage.rectangleSelectWhenLeft;
219+
}
220+
}
221+
189222
public mouseup = (event: MouseEvent) => {
190223
if (event.button !== 0) {
191224
return;

app/src/core/stage/Stage.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ export namespace Stage {
110110
export let enableDragAutoAlign = true;
111111

112112
export let textNodeSelectAllWhenStartEditByMouseClick = true;
113+
export let rectangleSelectWhenLeft: "contain" | "intersect" = "contain";
114+
export let rectangleSelectWhenRight: "contain" | "intersect" = "intersect";
113115

114116
export function init() {
115117
autoSaveEngine.init();
@@ -123,5 +125,11 @@ export namespace Stage {
123125
Settings.watch("textNodeSelectAllWhenStartEditByMouseClick", (value) => {
124126
textNodeSelectAllWhenStartEditByMouseClick = value;
125127
});
128+
Settings.watch("rectangleSelectWhenLeft", (value) => {
129+
rectangleSelectWhenLeft = value;
130+
});
131+
Settings.watch("rectangleSelectWhenRight", (value) => {
132+
rectangleSelectWhenRight = value;
133+
});
126134
}
127135
}

app/src/locales/en.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,23 @@ settings:
359359
title: Enable Drag Auto Align Nodes
360360
description: |
361361
When enabled, nodes will automatically align with other nodes along the x-axis and y-axis when dragged and released
362+
rectangleSelectWhenLeft:
363+
title: Strategy for Left Rectangle Selection
364+
description: |
365+
Choose the strategy for selecting with a rectangle when dragging left.
366+
Complete containment means the selection rectangle must completely cover the bounding rectangle of the entity.
367+
Partial containment means the selection rectangle only needs to touch a part of the entity's bounding rectangle to select it.
368+
options:
369+
intersect: Partial Containment
370+
contain: Complete Containment
371+
rectangleSelectWhenRight:
372+
title: Strategy for Right Rectangle Selection
373+
description: |
374+
Choose the strategy for selecting with a rectangle when dragging right.
375+
options:
376+
intersect: Partial Containment
377+
contain: Complete Containment
378+
362379
cuttingLineStartSoundFile:
363380
title: Cutting Line Start Sound File
364381
description: |

app/src/locales/zh_CN.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,22 @@ settings:
346346
title: 鼠标拖动自动吸附对齐节点
347347
description: |
348348
开启后,拖动节点并松开时会与其他节点在x轴、y轴方向对齐
349+
rectangleSelectWhenLeft:
350+
title: 向左框选的策略
351+
description: |
352+
选择鼠标向左框选的策略,包含完全覆盖框选和非完全覆盖框选
353+
完全覆盖框选是指矩形框选框必须完全覆盖实体的外接矩形
354+
非完全覆盖框选是指矩形框选框只要碰到一点点实体的外接矩形,就能够选中了
355+
options:
356+
intersect: 非完全覆盖框选
357+
contain: 完全覆盖框选
358+
rectangleSelectWhenRight:
359+
title: 向右框选的策略
360+
description: |
361+
选择鼠标向右框选的策略
362+
options:
363+
intersect: 非完全覆盖框选
364+
contain: 完全覆盖框选
349365
# 声音
350366
cuttingLineStartSoundFile:
351367
title: 斩断线开始的声音文件

app/src/locales/zh_TW.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,22 @@ settings:
329329
title: 鼠标拖动自动对齐节点
330330
description: |
331331
开启后,拖动节点并松开时会与其他节点在x轴、y轴方向对齐
332+
rectangleSelectWhenLeft:
333+
title: 向左框选的策略
334+
description: |
335+
选择鼠标向左框选的策略,包含完全覆盖框选和非完全覆盖框选
336+
完全覆盖框选是指矩形框选框必须完全覆盖实体的外接矩形
337+
非完全覆盖框选是指矩形框选框只要碰到一点点实体的外接矩形,就能够选中了
338+
options:
339+
intersect: 非完全覆盖框选
340+
contain: 完全覆盖框选
341+
rectangleSelectWhenRight:
342+
title: 向右框选的策略
343+
description: |
344+
选择鼠标向右框选的策略
345+
options:
346+
intersect: 非完全覆盖框选
347+
contain: 完全覆盖框选
332348
# 聲音
333349
cuttingLineStartSoundFile:
334350
title: 斬斷線開始的聲音文件

app/src/pages/settings/control.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import {
1212
RotateCw,
1313
ScanEye,
1414
Skull,
15+
SquareArrowDownRight,
16+
SquareArrowUpLeft,
17+
SquareDashedMousePointer,
1518
TextCursorInput,
1619
TextSelect,
1720
} from "lucide-react";
@@ -24,6 +27,10 @@ export default function Control() {
2427
<SettingField icon={<MousePointerClick />} settingKey="mouseRightDragBackground" type="select" />
2528
<SettingField icon={<AlignStartVertical />} settingKey="enableDragAutoAlign" type="switch" />
2629
</FieldGroup>
30+
<FieldGroup title="RectangleSelect 框选" icon={<SquareDashedMousePointer />}>
31+
<SettingField icon={<SquareArrowDownRight />} settingKey="rectangleSelectWhenRight" type="select" />
32+
<SettingField icon={<SquareArrowUpLeft />} settingKey="rectangleSelectWhenLeft" type="select" />
33+
</FieldGroup>
2734

2835
<FieldGroup title="TextNode 文本节点" icon={<TextSelect />}>
2936
<SettingField icon={<ListRestart />} settingKey="textNodeStartEditMode" type="select" />

0 commit comments

Comments
 (0)