@fulate/tools 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -11
- package/dist/editor/interaction-controller/connection-intent.d.ts +5 -0
- package/dist/editor/interaction-controller/controller.d.ts +166 -0
- package/dist/editor/interaction-controller/gestures.d.ts +93 -0
- package/dist/editor/interaction-controller/helpers.d.ts +16 -0
- package/dist/editor/interaction-controller/index.d.ts +2 -0
- package/dist/editor/interaction-controller/types.d.ts +104 -0
- package/dist/index.d.ts +0 -3
- package/dist/index.js +167 -74
- package/dist/select/identity.d.ts +3 -0
- package/dist/select/projection.d.ts +1 -2
- package/package.json +1 -1
- package/dist/editor/interaction-controller.d.ts +0 -339
package/README.md
CHANGED
|
@@ -5,8 +5,10 @@ gesture、Content session、Line/LineTree branch draft 与 semantic History 边
|
|
|
5
5
|
surface,`LineTool` 只保留 command/preview surface。Transform 的每个已派发 pointer 输入都立即从
|
|
6
6
|
frozen baseline 解析并提交一个 trusted batch;Point gesture 继续只在每个显示帧消费最后一个 intent。
|
|
7
7
|
每个 owner 都把 world delta 交给同一个 Core reference-transform kernel;普通 transform 与显式
|
|
8
|
-
reparent intent由该 intent决定写 pose、resize-owned geometry或preserve-owned geometry
|
|
9
|
-
Group
|
|
8
|
+
reparent intent由该 intent决定写 pose、resize-owned geometry或preserve-owned geometry。移动/旋转
|
|
9
|
+
Group时只写Group pose;Group或多选的尺寸调整、镜像会递归把同一个world仿射结果写回可编辑子元素
|
|
10
|
+
的 authored geometry(anchor、cubic control、LineTree point),不会把尺寸变化留在Group scale上。
|
|
11
|
+
DirectChild手势只写该child;derived content bounds不形成command、History或补偿写入。
|
|
10
12
|
Line/LineTree固定world paint padding只属于自身visual/render coverage,不进入Group selection、resize或
|
|
11
13
|
snap bounds。所有工作复用现有`RootFrameRuntime`,Tools 不创建 RAF、timer 或第二 scheduler。
|
|
12
14
|
`Select`、`Snap`、`Rule` 与 `LineTool` 都使用 Core 生成的普通 Node key。工具间协作由应用组合时交付
|
|
@@ -26,7 +28,7 @@ DirectChild开始axis Snap时,手势排除集按候选使用的同一selection
|
|
|
26
28
|
ancestor Group不会成为自身拖拽候选,Group外部元素仍正常参与对齐。
|
|
27
29
|
|
|
28
30
|
LineTree Point改变一个anchor时,即使当前pose包含resize或反向scale,也在同一owner change内保持
|
|
29
|
-
既有world matrix,未编辑points不发生位移;整树resize
|
|
31
|
+
既有world matrix,未编辑points不发生位移;整树resize把拓扑点按完整仿射写回。Group默认旋转中心与九点
|
|
30
32
|
pivot吸附按当前local visual/content selection bounds解析;稳定layout reference只拥有Group pose。
|
|
31
33
|
|
|
32
34
|
Selection controls 是一份 immutable `SelectionOverlayProjection`。paint、control/body hit 与 cursor
|
|
@@ -143,8 +145,10 @@ select.resize({
|
|
|
143
145
|
始终读取 authored/layout box,即使 `overflow="visible"` 且未换行的 glyph 超出该 box;`anchor` 省略等于
|
|
144
146
|
`"top-left"`,支持九个 frame 固定点:`top-left`/`top`/`top-right`/`left`/`center`/`right`/
|
|
145
147
|
`bottom-left`/`bottom`/`bottom-right`。这些值是 world logical 单位,不包含 Viewport 缩放;
|
|
146
|
-
controller 会自动处理父级和多层 Group 变换,并通过同一个 trusted batch 和 History
|
|
147
|
-
|
|
148
|
+
controller 会自动处理父级和多层 Group 变换,并通过同一个 trusted batch 和 History 记录。Group resize
|
|
149
|
+
会递归改写可编辑子元素的 authored geometry;Group 自身不再承载这次尺寸变化的 scale。移动/旋转
|
|
150
|
+
Group 仍只改 Group pose。对 Rectangle/Text 等 frame-authored 子元素,尺寸变化写入它们自己的
|
|
151
|
+
frame width/height;对 VectorPath/Polygon/Line/LineTree,则写入点、控制柄或拓扑。
|
|
148
152
|
|
|
149
153
|
`Select.flip()` 是同一 Transform 命令链上的镜像操作:
|
|
150
154
|
|
|
@@ -155,8 +159,8 @@ select.flip("vertical");
|
|
|
155
159
|
|
|
156
160
|
镜像轴经过当前 selection frame 中心。单选旋转元素沿其 frame 自身水平/垂直轴镜像;多选的
|
|
157
161
|
aggregate frame 为 world 轴对齐,因此沿 world 水平/垂直轴镜像。命令遵守 selection 的 resize 轴
|
|
158
|
-
策略,使用同一批 selected owner baseline、trusted batch 与 semantic History
|
|
159
|
-
|
|
162
|
+
策略,使用同一批 selected owner baseline、trusted batch 与 semantic History;Group 不承载这次镜像的
|
|
163
|
+
scale,镜像会递归 bake children authored geometry。空选择或被策略禁止的方向是普通 noop。
|
|
160
164
|
|
|
161
165
|
## Keyboard、Clipboard 与应用命令 owner
|
|
162
166
|
|
|
@@ -214,10 +218,12 @@ const stopSelection = select.addEventListener("select:end", syncPanel);
|
|
|
214
218
|
const stopGeometry = select.addEventListener("select:update", syncPanel);
|
|
215
219
|
```
|
|
216
220
|
|
|
217
|
-
场景持久化只监听 configured 语义完成事件:
|
|
218
|
-
|
|
219
|
-
```ts
|
|
220
|
-
|
|
221
|
+
场景持久化只监听 configured 语义完成事件:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import { serializeScene } from "@fulate/import";
|
|
225
|
+
|
|
226
|
+
const stopPersistence = select.addEventListener(
|
|
221
227
|
"select:configured-change",
|
|
222
228
|
() => saveToIndexedDB(serializeScene(root))
|
|
223
229
|
);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ConnectionPortRef, type Root } from "@fulate/core";
|
|
2
|
+
import { type EndpointRef } from "@fulate/ui";
|
|
3
|
+
import type { HistoryManager } from "../../history";
|
|
4
|
+
import type { ToolsConnectionIntent } from "../../select";
|
|
5
|
+
export declare function createToolsConnectionIntent(root: Root, history: HistoryManager, source: EndpointRef, target: ConnectionPortRef): ToolsConnectionIntent;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Element, type ConnectionPortRef, type OwnerPropertyChange, type Root, type StructureChange } from "@fulate/core";
|
|
2
|
+
import { LineTree, type LineTreeBranchTailSource, type LineDecoration } from "@fulate/ui";
|
|
3
|
+
import { type PointView, type Rect } from "@fulate/share";
|
|
4
|
+
import type { HistoryManager, HistoryScope } from "../../history";
|
|
5
|
+
import type { LineTool } from "../../line";
|
|
6
|
+
import type { Select, SelectedPropertyPatch } from "../../select";
|
|
7
|
+
import { type SelectionOverlayProjection, type SelectionOverlayTarget } from "../../select/projection";
|
|
8
|
+
import { type FlipDirection, type ResizeOptions } from "../../select/transform";
|
|
9
|
+
import type { Snap } from "../../select/snap";
|
|
10
|
+
import { type PointerIntent } from "./helpers";
|
|
11
|
+
import { PointGesture, SelectionPressGesture, TransformGesture } from "./gestures";
|
|
12
|
+
import type { ApplyCommandOptions, EditorInteractionState, LineDrawingSession, PointerGesture, SelectionIdentity } from "./types";
|
|
13
|
+
export type { EditorInteractionState, SelectControllerState, SelectionIdentity } from "./types";
|
|
14
|
+
export declare class EditorInteractionController {
|
|
15
|
+
readonly select: Select;
|
|
16
|
+
readonly root: Root;
|
|
17
|
+
readonly history: HistoryManager;
|
|
18
|
+
readonly frameFlush: any;
|
|
19
|
+
private readonly cleanups;
|
|
20
|
+
private selectionValue;
|
|
21
|
+
private owners;
|
|
22
|
+
private policy;
|
|
23
|
+
private rotationPivotPreview;
|
|
24
|
+
private selectionFrame;
|
|
25
|
+
private preserveSelectionFrame;
|
|
26
|
+
private projectionState;
|
|
27
|
+
private projectionOwners;
|
|
28
|
+
private projectionMembership;
|
|
29
|
+
private projectionTopology;
|
|
30
|
+
private overlayDirty;
|
|
31
|
+
private pendingSelectionUpdate;
|
|
32
|
+
private pendingSelectionRepair;
|
|
33
|
+
private rotationPivotDisplayOwner;
|
|
34
|
+
private hoveredTarget;
|
|
35
|
+
private hoveredContent;
|
|
36
|
+
private controllerState;
|
|
37
|
+
private pasteTask;
|
|
38
|
+
private pointerWorld;
|
|
39
|
+
marquee: Readonly<Rect> | null;
|
|
40
|
+
constructor(select: Select, root: Root, history: HistoryManager);
|
|
41
|
+
get selection(): readonly Element[];
|
|
42
|
+
get projection(): SelectionOverlayProjection;
|
|
43
|
+
publishConfiguredChange(changed: boolean | undefined): void;
|
|
44
|
+
/** 把纯外部通知排到当前同步命令、回放或 frame completion 返回之后。 */
|
|
45
|
+
private notifySelect;
|
|
46
|
+
get contentHover(): Element<import("@fulate/core").ElementProperties, import("@fulate/core").BaseElementOption<any>, Partial<Omit<import("@fulate/core").BaseElementOption<any>, "key" | "children">>>;
|
|
47
|
+
get overlayTarget(): SelectionOverlayTarget;
|
|
48
|
+
get editorElements(): readonly Element[];
|
|
49
|
+
get snapTool(): Snap | undefined;
|
|
50
|
+
get state(): EditorInteractionState;
|
|
51
|
+
/** @internal Captures the canonical selection and interaction identities. */
|
|
52
|
+
captureSelectionIdentity(): SelectionIdentity;
|
|
53
|
+
/** @internal Makes one concrete clipboard Promise the latest paste intent. */
|
|
54
|
+
beginPaste(task: Promise<void>): void;
|
|
55
|
+
/** @internal Tests whether one paste still owns the captured selection intent. */
|
|
56
|
+
ownsPasteSelection(identity: SelectionIdentity, task: Promise<void>): boolean;
|
|
57
|
+
/** @internal Releases the latest paste intent after its Promise settles. */
|
|
58
|
+
finishPaste(task: Promise<void>): void;
|
|
59
|
+
private get selectState();
|
|
60
|
+
private installSelectState;
|
|
61
|
+
private get activeGesture();
|
|
62
|
+
private set activeGesture(value);
|
|
63
|
+
private get activePointGesture();
|
|
64
|
+
private get pointState();
|
|
65
|
+
private set pointState(value);
|
|
66
|
+
private get activePointTarget();
|
|
67
|
+
private set activePointTarget(value);
|
|
68
|
+
private get contentSession();
|
|
69
|
+
private set contentSession(value);
|
|
70
|
+
private get contentHistory();
|
|
71
|
+
private set contentHistory(value);
|
|
72
|
+
private get lineSession();
|
|
73
|
+
private set lineSession(value);
|
|
74
|
+
private get lineTreeSession();
|
|
75
|
+
private set lineTreeSession(value);
|
|
76
|
+
private get rotationPivotState();
|
|
77
|
+
private installInput;
|
|
78
|
+
private readonly handleSceneChange;
|
|
79
|
+
private sceneChangeAffectsSelection;
|
|
80
|
+
private selectionGeometryIsDirty;
|
|
81
|
+
private invalidatePointTopology;
|
|
82
|
+
private resolvePolicy;
|
|
83
|
+
setSelection(elements: readonly Element[]): void;
|
|
84
|
+
private readonly pointProjectionOwnerIsCurrent;
|
|
85
|
+
private restoreSelectionForHistory;
|
|
86
|
+
private replaceSelection;
|
|
87
|
+
private requestSelectionEnd;
|
|
88
|
+
private pointContextIsCurrent;
|
|
89
|
+
private resolveCurrentPointContext;
|
|
90
|
+
private resolvePointGestureContext;
|
|
91
|
+
pointGestureOwnsCurrentContext(gesture: PointGesture): boolean;
|
|
92
|
+
private setPointState;
|
|
93
|
+
private exitPointState;
|
|
94
|
+
private setActivePointTarget;
|
|
95
|
+
private invalidateOverlay;
|
|
96
|
+
private shouldShowRotationPivot;
|
|
97
|
+
private showRotationPivotForCurrentOwner;
|
|
98
|
+
hasPendingOverlayWork(): boolean;
|
|
99
|
+
flushOverlayProjection(): void;
|
|
100
|
+
hasPendingCompletionWork(): boolean;
|
|
101
|
+
flushCompletionWork(): void;
|
|
102
|
+
resolveCursor(point: PointView | undefined, contentCursor: string): string;
|
|
103
|
+
private updateHover;
|
|
104
|
+
private applyAuthoredBatch;
|
|
105
|
+
private onPointerDown;
|
|
106
|
+
private onPointerMove;
|
|
107
|
+
private onPointerFinish;
|
|
108
|
+
private onDoubleClick;
|
|
109
|
+
private onKeyDown;
|
|
110
|
+
private captureTransformRecords;
|
|
111
|
+
private startTransform;
|
|
112
|
+
promoteSelectionPress(gesture: SelectionPressGesture): TransformGesture | null;
|
|
113
|
+
finishSelectionPress(gesture: SelectionPressGesture): void;
|
|
114
|
+
finishSelectionGesture(gesture: PointerGesture, selection?: readonly Element[]): void;
|
|
115
|
+
consumeTransform(gesture: TransformGesture, intent: PointerIntent): void;
|
|
116
|
+
private startPointGesture;
|
|
117
|
+
consumePoint(gesture: PointGesture, intent: PointerIntent, first: boolean): void;
|
|
118
|
+
deletePointTarget(): boolean;
|
|
119
|
+
nudge(dx: number, dy: number): void;
|
|
120
|
+
setProperties(patch: SelectedPropertyPatch): void;
|
|
121
|
+
private prepareTransformCommandFrame;
|
|
122
|
+
private applyTransformCommand;
|
|
123
|
+
resize(options?: Readonly<ResizeOptions>): void;
|
|
124
|
+
flip(direction: FlipDirection): void;
|
|
125
|
+
previewRotationPivot(owner: Element, point: PointView): void;
|
|
126
|
+
clearRotationPivotPreview(owner: Element): void;
|
|
127
|
+
commitRotationPivot(owner: Element, point: PointView): boolean;
|
|
128
|
+
private resolveRotationPivot;
|
|
129
|
+
setMarquee(rect: Readonly<Rect> | null): void;
|
|
130
|
+
finishPointerGesture(gesture: PointerGesture): void;
|
|
131
|
+
private pointConnectionCandidateIsCurrent;
|
|
132
|
+
finishPointConnectionGesture(gesture: PointGesture): void;
|
|
133
|
+
private beginContentEditing;
|
|
134
|
+
finishContentEditing(discard: boolean): void;
|
|
135
|
+
startLineDrawing(tool: LineTool): void;
|
|
136
|
+
private startLineTreeBranchDrawing;
|
|
137
|
+
private updateLineTreeSourceHover;
|
|
138
|
+
private confirmLineTreeSource;
|
|
139
|
+
private beginLineTreeBranchDraft;
|
|
140
|
+
private updateLineTreePreview;
|
|
141
|
+
private probeLineTreeTail;
|
|
142
|
+
private confirmLineTreePoint;
|
|
143
|
+
private confirmLineTreeBranchDrawing;
|
|
144
|
+
private updateLinePreview;
|
|
145
|
+
private probeLineAnchor;
|
|
146
|
+
private confirmLinePoint;
|
|
147
|
+
stopLineDrawing(): void;
|
|
148
|
+
cancelLineDrawing(): void;
|
|
149
|
+
private confirmLineDrawing;
|
|
150
|
+
getLineDrawingView(tool: LineTool): LineDrawingSession | {
|
|
151
|
+
readonly kind: "branch-draw";
|
|
152
|
+
readonly tool: LineTool;
|
|
153
|
+
readonly owner: LineTree;
|
|
154
|
+
readonly source: LineTreeBranchTailSource;
|
|
155
|
+
readonly tailDecoration: LineDecoration;
|
|
156
|
+
readonly confirmed: PointView[];
|
|
157
|
+
tailTarget: ConnectionPortRef | undefined;
|
|
158
|
+
preview: PointView | null;
|
|
159
|
+
snap: import("../../select/snap").PortSnapResult | null;
|
|
160
|
+
};
|
|
161
|
+
hasLineToolSession(tool: LineTool): boolean;
|
|
162
|
+
applyCommand(ownerEffects: readonly OwnerPropertyChange[], structureEffects: readonly StructureChange[], scope: HistoryScope | undefined, selectionAfter: readonly Element[], options?: ApplyCommandOptions): void;
|
|
163
|
+
finishCurrentInteraction(): void;
|
|
164
|
+
destroy(): void;
|
|
165
|
+
}
|
|
166
|
+
export declare function getEditorInteractionController(root: Root): EditorInteractionController;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ConnectionPortRef, Element, Node } from "@fulate/core";
|
|
2
|
+
import type { EndpointConnectionRole, EndpointRef, EditorAuthoredGesture, PointEditTarget } from "@fulate/ui";
|
|
3
|
+
import type { PointView } from "@fulate/share";
|
|
4
|
+
import type { HistoryScope } from "../../history";
|
|
5
|
+
import type { ResizeHandle, SelectionOverlayProjection } from "../../select/projection";
|
|
6
|
+
import { type PointerIntent } from "./helpers";
|
|
7
|
+
import type { EditorInteractionController } from "./controller";
|
|
8
|
+
import type { PointerGesture, TransformOwnerRecord } from "./types";
|
|
9
|
+
export declare class TransformGesture implements PointerGesture {
|
|
10
|
+
readonly controller: EditorInteractionController;
|
|
11
|
+
readonly kind: "move" | "resize" | "rotate";
|
|
12
|
+
readonly start: PointView;
|
|
13
|
+
readonly frame: NonNullable<SelectionOverlayProjection["frame"]>;
|
|
14
|
+
readonly records: readonly TransformOwnerRecord[];
|
|
15
|
+
readonly snapPoints: readonly PointView[];
|
|
16
|
+
readonly movableLineEndpoints: readonly PointView[];
|
|
17
|
+
readonly snapExcludes: readonly Element[];
|
|
18
|
+
readonly rotationPivotWorld: PointView;
|
|
19
|
+
readonly history: HistoryScope | undefined;
|
|
20
|
+
readonly resizeHandle?: ResizeHandle;
|
|
21
|
+
private lastIntent;
|
|
22
|
+
private previousRotationAngle;
|
|
23
|
+
private unwrappedRotationDelta;
|
|
24
|
+
constructor(controller: EditorInteractionController, kind: "move" | "resize" | "rotate", start: PointView, initialIntent: PointerIntent, frame: NonNullable<SelectionOverlayProjection["frame"]>, records: readonly TransformOwnerRecord[], snapPoints: readonly PointView[], movableLineEndpoints: readonly PointView[], snapExcludes: readonly Element[], rotationPivotWorld: PointView, history: HistoryScope | undefined, resizeHandle?: ResizeHandle);
|
|
25
|
+
consumeRotationDelta(point: PointView): number;
|
|
26
|
+
probeForbiddenPort(point: PointView): boolean;
|
|
27
|
+
update(intent: PointerIntent): void;
|
|
28
|
+
ownsCurrentContext(): boolean;
|
|
29
|
+
finishInvalidated(): void;
|
|
30
|
+
finish(intent?: PointerIntent): void;
|
|
31
|
+
}
|
|
32
|
+
export declare class PointGesture implements PointerGesture, EditorAuthoredGesture {
|
|
33
|
+
readonly controller: EditorInteractionController;
|
|
34
|
+
readonly ownerKey: string;
|
|
35
|
+
readonly startParent: Node | undefined;
|
|
36
|
+
readonly startTarget: PointEditTarget;
|
|
37
|
+
readonly insertT: number | undefined;
|
|
38
|
+
readonly role: "endpoint" | "vertex" | "segment" | "handle";
|
|
39
|
+
readonly connectionRole: EndpointConnectionRole | null;
|
|
40
|
+
readonly snapExcludes: readonly Element[];
|
|
41
|
+
readonly history: HistoryScope | undefined;
|
|
42
|
+
readonly startPointerWorld: PointView;
|
|
43
|
+
readonly startTargetWorld: PointView;
|
|
44
|
+
private latestIntent;
|
|
45
|
+
private consumedIntent;
|
|
46
|
+
connectionTarget: ConnectionPortRef | undefined;
|
|
47
|
+
connectionTargetOwner: Element | undefined;
|
|
48
|
+
connectionTargetParent: Node | undefined;
|
|
49
|
+
constructor(controller: EditorInteractionController, ownerKey: string, startParent: Node | undefined, startTarget: PointEditTarget, insertT: number | undefined, role: "endpoint" | "vertex" | "segment" | "handle", connectionRole: EndpointConnectionRole | null, snapExcludes: readonly Element[], history: HistoryScope | undefined, startPointerWorld: PointView, startTargetWorld: PointView);
|
|
50
|
+
currentTarget: PointEditTarget;
|
|
51
|
+
ownsCurrentContext(): boolean;
|
|
52
|
+
get connectionSource(): EndpointRef | undefined;
|
|
53
|
+
resolveWorldPoint(pointer: PointView): {
|
|
54
|
+
x: number;
|
|
55
|
+
y: number;
|
|
56
|
+
};
|
|
57
|
+
update(intent: PointerIntent): void;
|
|
58
|
+
consumeLatestIntent(): void;
|
|
59
|
+
finish(intent?: PointerIntent, cancelled?: boolean): void;
|
|
60
|
+
finishInvalidated(): void;
|
|
61
|
+
}
|
|
62
|
+
export declare class PivotGesture implements PointerGesture {
|
|
63
|
+
readonly controller: EditorInteractionController;
|
|
64
|
+
readonly owner: Element;
|
|
65
|
+
readonly startPointer: PointView;
|
|
66
|
+
readonly startPivot: PointView;
|
|
67
|
+
private finalPoint;
|
|
68
|
+
constructor(controller: EditorInteractionController, owner: Element, startPointer: PointView, startPivot: PointView);
|
|
69
|
+
update(intent: PointerIntent): void;
|
|
70
|
+
finish(intent?: PointerIntent): void;
|
|
71
|
+
}
|
|
72
|
+
export declare class SelectionPressGesture implements PointerGesture {
|
|
73
|
+
readonly controller: EditorInteractionController;
|
|
74
|
+
readonly start: PointerIntent;
|
|
75
|
+
readonly candidate: Element | undefined;
|
|
76
|
+
readonly contentTarget: Element | null;
|
|
77
|
+
readonly directDragOwner?: Element;
|
|
78
|
+
constructor(controller: EditorInteractionController, start: PointerIntent, candidate: Element | undefined, contentTarget: Element | null, directDragOwner?: Element);
|
|
79
|
+
update(intent: PointerIntent): void;
|
|
80
|
+
finish(intent?: PointerIntent, cancelled?: boolean): void;
|
|
81
|
+
}
|
|
82
|
+
export declare class MarqueeGesture implements PointerGesture {
|
|
83
|
+
readonly controller: EditorInteractionController;
|
|
84
|
+
readonly start: PointView;
|
|
85
|
+
readonly candidate: Element | undefined;
|
|
86
|
+
readonly additive: boolean;
|
|
87
|
+
readonly initialSelection: readonly Element[];
|
|
88
|
+
private current;
|
|
89
|
+
private moved;
|
|
90
|
+
constructor(controller: EditorInteractionController, start: PointView, candidate: Element | undefined, additive: boolean, initialSelection: readonly Element[]);
|
|
91
|
+
update(intent: PointerIntent): void;
|
|
92
|
+
finish(intent?: PointerIntent, cancelled?: boolean): void;
|
|
93
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Element, type FulateEvent, type OriginX, type OriginY } from "@fulate/core";
|
|
2
|
+
import { type PointView, type Rect } from "@fulate/share";
|
|
3
|
+
import type { PointEditingScope } from "@fulate/ui";
|
|
4
|
+
import type { PortSnapProbeResult } from "../../select/snap";
|
|
5
|
+
import type { SelectionOverlayTarget } from "../../select/projection";
|
|
6
|
+
export interface PointerIntent {
|
|
7
|
+
readonly point: PointView;
|
|
8
|
+
readonly shiftKey: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function isBlockedPortProbe(probe: PortSnapProbeResult | null | undefined): boolean;
|
|
11
|
+
export declare function resolvePortProbeCursor(probe: PortSnapProbeResult | undefined): "not-allowed" | "crosshair";
|
|
12
|
+
export declare function pointIntent(event: FulateEvent): PointerIntent;
|
|
13
|
+
export declare function matchesMarquee(element: Element, rect: Readonly<Rect>, requireContainment: boolean): boolean;
|
|
14
|
+
export declare function sameTarget(left: SelectionOverlayTarget | null, right: SelectionOverlayTarget | null): boolean;
|
|
15
|
+
export declare function samePointScope(left: PointEditingScope, right: PointEditingScope): boolean;
|
|
16
|
+
export declare function resolveOriginPoint(bounds: Readonly<Rect>, x: OriginX, y: OriginY): PointView;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { ConnectionPortRef, Element, Node } from "@fulate/core";
|
|
2
|
+
import type { ContentEditingSession, EndpointRef, LineDecoration, LineTree, LineTreeBranchTailSource, PointEditTarget, PointEditingScope, SelectionBehavior, TransformInteractionCapability } from "@fulate/ui";
|
|
3
|
+
import type { PointView } from "@fulate/share";
|
|
4
|
+
import type { HistoryScope } from "../../history";
|
|
5
|
+
import type { LineTool } from "../../line";
|
|
6
|
+
import type { ProjectionOwner, SelectionRotationPivot } from "../../select/projection";
|
|
7
|
+
import type { PortSnapResult } from "../../select/snap";
|
|
8
|
+
import type { PointerIntent } from "./helpers";
|
|
9
|
+
import type { MarqueeGesture, PointGesture, PivotGesture, SelectionPressGesture, TransformGesture } from "./gestures";
|
|
10
|
+
export interface SelectedOwner extends ProjectionOwner {
|
|
11
|
+
readonly behavior: SelectionBehavior;
|
|
12
|
+
}
|
|
13
|
+
export interface SelectionIdentity {
|
|
14
|
+
readonly owners: readonly SelectedOwner[];
|
|
15
|
+
readonly interaction: EditorInteractionState;
|
|
16
|
+
}
|
|
17
|
+
export interface TransformOwnerRecord {
|
|
18
|
+
readonly owner: Element;
|
|
19
|
+
readonly parent: Node | undefined;
|
|
20
|
+
readonly capability: TransformInteractionCapability;
|
|
21
|
+
readonly baseline: ReturnType<TransformInteractionCapability["captureTransformBaseline"]>;
|
|
22
|
+
}
|
|
23
|
+
export interface PointerGesture {
|
|
24
|
+
update(intent: PointerIntent): void;
|
|
25
|
+
finish(intent?: PointerIntent, cancelled?: boolean): void;
|
|
26
|
+
}
|
|
27
|
+
export interface ApplyCommandOptions {
|
|
28
|
+
readonly beforeDrain?: () => void;
|
|
29
|
+
readonly afterHistory?: () => void;
|
|
30
|
+
readonly installSelection?: () => boolean;
|
|
31
|
+
}
|
|
32
|
+
export type SelectNotification = "select:end" | "select:configured-change" | "select:undo" | "select:redo";
|
|
33
|
+
export interface SelectNotificationData {
|
|
34
|
+
readonly elements: readonly Element[];
|
|
35
|
+
}
|
|
36
|
+
export interface LineDrawingSession {
|
|
37
|
+
readonly tool: LineTool;
|
|
38
|
+
readonly resultType: "line" | "lineTree";
|
|
39
|
+
readonly tailDecoration: LineDecoration;
|
|
40
|
+
readonly history: HistoryScope | undefined;
|
|
41
|
+
readonly confirmed: PointView[];
|
|
42
|
+
headTarget: ConnectionPortRef | undefined;
|
|
43
|
+
tailTarget: ConnectionPortRef | undefined;
|
|
44
|
+
preview: PointView | null;
|
|
45
|
+
snap: PortSnapResult | null;
|
|
46
|
+
}
|
|
47
|
+
export interface PointState {
|
|
48
|
+
readonly ownerKey: string;
|
|
49
|
+
readonly scope: PointEditingScope;
|
|
50
|
+
}
|
|
51
|
+
export interface ActivePointTarget {
|
|
52
|
+
readonly ownerKey: string;
|
|
53
|
+
readonly target: PointEditTarget;
|
|
54
|
+
}
|
|
55
|
+
export type SelectControllerState = {
|
|
56
|
+
readonly kind: "idle";
|
|
57
|
+
readonly gesture: MarqueeGesture | null;
|
|
58
|
+
} | {
|
|
59
|
+
readonly kind: "transform";
|
|
60
|
+
readonly gesture: TransformGesture | PivotGesture | SelectionPressGesture | null;
|
|
61
|
+
} | {
|
|
62
|
+
readonly kind: "point";
|
|
63
|
+
readonly ownerKey: string;
|
|
64
|
+
readonly scope: PointEditingScope;
|
|
65
|
+
readonly gesture: PointGesture | SelectionPressGesture | TransformGesture | null;
|
|
66
|
+
readonly activeTarget: ActivePointTarget | null;
|
|
67
|
+
} | {
|
|
68
|
+
readonly kind: "port";
|
|
69
|
+
readonly ownerKey: string;
|
|
70
|
+
readonly scope: PointEditingScope;
|
|
71
|
+
readonly gesture: PointGesture;
|
|
72
|
+
readonly source: EndpointRef;
|
|
73
|
+
readonly activeTarget: ActivePointTarget | null;
|
|
74
|
+
} | {
|
|
75
|
+
readonly kind: "content";
|
|
76
|
+
readonly session: ContentEditingSession;
|
|
77
|
+
readonly history: HistoryScope | undefined;
|
|
78
|
+
};
|
|
79
|
+
export type LineTreeDrawingSession = {
|
|
80
|
+
readonly kind: "source-pick";
|
|
81
|
+
readonly tool: LineTool;
|
|
82
|
+
readonly owner: LineTree;
|
|
83
|
+
readonly tailDecoration: LineDecoration;
|
|
84
|
+
hover: LineTreeBranchTailSource | null;
|
|
85
|
+
} | {
|
|
86
|
+
readonly kind: "branch-draw";
|
|
87
|
+
readonly tool: LineTool;
|
|
88
|
+
readonly owner: LineTree;
|
|
89
|
+
readonly source: LineTreeBranchTailSource;
|
|
90
|
+
readonly tailDecoration: LineDecoration;
|
|
91
|
+
readonly confirmed: PointView[];
|
|
92
|
+
tailTarget: ConnectionPortRef | undefined;
|
|
93
|
+
preview: PointView | null;
|
|
94
|
+
snap: PortSnapResult | null;
|
|
95
|
+
};
|
|
96
|
+
export type EditorInteractionState = {
|
|
97
|
+
readonly kind: "select";
|
|
98
|
+
readonly interaction: SelectControllerState;
|
|
99
|
+
readonly selection: readonly Element[];
|
|
100
|
+
readonly pivot: SelectionRotationPivot;
|
|
101
|
+
} | {
|
|
102
|
+
readonly kind: "line-draw";
|
|
103
|
+
readonly session: LineDrawingSession | LineTreeDrawingSession;
|
|
104
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { checkElement } from "./select/checkElement";
|
|
2
1
|
export { Select } from "./select/index";
|
|
3
2
|
export type { ConnectionGesture, CopyContext, ClipboardDataProducer, DeleteContext, FlipDirection, PasteContext, PasteOptions, PasteParentResolver, ResizeAnchor, ResizeOptions, SelectedPropertyPatch, SelectKeyboardCommands, SelectOption, ToolsConnectionIntent } from "./select/index";
|
|
4
3
|
export { Snap } from "./select/snap";
|
|
@@ -13,5 +12,3 @@ export { alignElements } from "./select/align";
|
|
|
13
12
|
export type { AlignType } from "./select/align";
|
|
14
13
|
export { ArrangeType } from "./select/arrange";
|
|
15
14
|
export type { ClipboardPlugin } from "./select/clipboard";
|
|
16
|
-
export { serializeScene, serializeSceneToJSON, deserializeElement, restoreScene, parseFileData, exportToFile, importFromFile } from "@fulate/import";
|
|
17
|
-
export type { ElementFilter, FileData, DeserializeFactories, DeserializeFactory } from "@fulate/import";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Element, ElementProperties, ElementTransform, Layer, isFormControlTarget, projectConnectionPortPoint, resolveOrigin, resolveReferenceTransform } from "@fulate/core";
|
|
2
|
-
import { BaseLine, ContentEditingCapabilityToken, DEFAULT_LINE_TREE_BRANCH_STYLE, Group, Line, LineTree, PointEditingCapabilityToken, SelectionBehaviorCapabilityToken, SelectionBehaviorMode, SelectionDiveMode, SelectionSnapMode, SnapSourceCapabilityToken, TransformInteractionCapabilityToken, addLineTreeBranchAtTail, applyLineTreeTopologyDelta, connectionPortAccepts, createLineTreeTopology, createLineTreeTopologyDelta, getEditorCapability, getEditorFrameFlush, getEndpointRelationIndex, hasConnectionPortCapacity, isEndpointRelationSource, moveLineTreePoint, remapLineTreeRelations, resolveConnectEndpointChange, resolveConnectionPortChanges, resolveDisconnectEndpointChanges, workspaceFocusInsetsKey } from "@fulate/ui";
|
|
3
|
-
import { Bound, Intersection,
|
|
4
|
-
import { deserializeElement
|
|
2
|
+
import { BaseLine, ContentEditingCapabilityToken, DEFAULT_LINE_TREE_BRANCH_STYLE, Group, Line, LineTree, PointEditingCapabilityToken, SelectionBehaviorCapabilityToken, SelectionBehaviorMode, SelectionDiveMode, SelectionSnapMode, SnapSourceCapabilityToken, TransformInteractionCapabilityToken, addLineTreeBranchAtTail, applyLineTreeTopologyDelta, connectionPortAccepts, createLineTreeTopology, createLineTreeTopologyDelta, getEditorCapability, getEditorFrameFlush, getEndpointRelationIndex, hasConnectionPortCapacity, isEndpointRelationSource, moveLineTreePoint, remapLineTreeRelations, resolveConnectEndpointChange, resolveConnectionPortChanges, resolveDisconnectEndpointChanges, resolveSelectionFrameBounds, workspaceFocusInsetsKey } from "@fulate/ui";
|
|
3
|
+
import { Bound, Intersection, isIdentityMatrix, qrDecompose, transformPoint } from "@fulate/share";
|
|
4
|
+
import { deserializeElement } from "@fulate/import";
|
|
5
5
|
import { isNull, isString, isUndefined } from "lodash-es";
|
|
6
6
|
import RBush from "rbush";
|
|
7
7
|
//#region packages/tools/src/select/checkElement.ts
|
|
@@ -33,6 +33,12 @@ function checkElement(element, excludes = [], scopeRootKey) {
|
|
|
33
33
|
return getEditorCapability(resolved, SelectionBehaviorCapabilityToken) ? resolved : void 0;
|
|
34
34
|
}
|
|
35
35
|
//#endregion
|
|
36
|
+
//#region packages/tools/src/select/identity.ts
|
|
37
|
+
/** Compare stable point-edit targets without reading live geometry. */
|
|
38
|
+
function samePointEditTarget(left, right) {
|
|
39
|
+
return left.kind === right.kind && (left.kind === "anchor" ? right.kind === "anchor" && left.anchorId === right.anchorId : left.kind === "segment" ? right.kind === "segment" && left.segmentId === right.segmentId : right.kind === "handle" && left.segmentId === right.segmentId && left.role === right.role);
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
36
42
|
//#region packages/tools/src/select/projection.ts
|
|
37
43
|
var ROTATE_CURSOR = `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2v6h-6"></path><path d="M21 13a9 9 0 1 1-3-7.7L21 8"></path></svg>') 9 9, crosshair`;
|
|
38
44
|
var RESIZE_CURSORS = {
|
|
@@ -81,8 +87,37 @@ var RESIZE_TARGETS = {
|
|
|
81
87
|
};
|
|
82
88
|
var ROTATE_TARGET = { kind: "rotate" };
|
|
83
89
|
var PIVOT_TARGET = { kind: "pivot" };
|
|
84
|
-
function
|
|
85
|
-
|
|
90
|
+
function rectCorners$1(rect) {
|
|
91
|
+
const right = rect.left + rect.width;
|
|
92
|
+
const bottom = rect.top + rect.height;
|
|
93
|
+
return [
|
|
94
|
+
{
|
|
95
|
+
x: rect.left,
|
|
96
|
+
y: rect.top
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
x: right,
|
|
100
|
+
y: rect.top
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
x: right,
|
|
104
|
+
y: bottom
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
x: rect.left,
|
|
108
|
+
y: bottom
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Resolve the points that define an owner's world selection frame. A frame
|
|
114
|
+
* capability is authoritative (for example Text/KeyValue authored bounds);
|
|
115
|
+
* owners without one use the shared local visual/layout fallback.
|
|
116
|
+
*/
|
|
117
|
+
function worldSelectionFramePoints(owner) {
|
|
118
|
+
const localFrame = resolveSelectionFrameBounds(owner.element, owner.transform);
|
|
119
|
+
const matrix = owner.element.transform.getWorldMatrixView();
|
|
120
|
+
return rectCorners$1(localFrame).map((point) => transformPoint(matrix, point));
|
|
86
121
|
}
|
|
87
122
|
var ROTATED_RESIZE_CURSORS = [
|
|
88
123
|
"ns-resize",
|
|
@@ -135,20 +170,23 @@ function createSelectionFrameProjection({ left, top, width, height, angle }) {
|
|
|
135
170
|
};
|
|
136
171
|
}
|
|
137
172
|
/** @internal Build a command frame from the current owner bounds. */
|
|
138
|
-
function
|
|
173
|
+
function createWorldSelectionFrame(owners, fallback) {
|
|
139
174
|
if (owners.length === 0) return fallback ?? null;
|
|
140
175
|
if (owners.length === 1) {
|
|
141
|
-
|
|
176
|
+
const ownerFrame = owners[0].transform?.getSelectionFrameBounds?.();
|
|
177
|
+
if (ownerFrame) {
|
|
142
178
|
if (fallback) return fallback;
|
|
143
|
-
return frameFromSingleOwner(owners[0]);
|
|
179
|
+
return frameFromSingleOwner(owners[0], ownerFrame);
|
|
144
180
|
}
|
|
145
|
-
const bounds = owners[0].element.transform
|
|
146
|
-
|
|
181
|
+
const bounds = resolveSelectionFrameBounds(owners[0].element, owners[0].transform);
|
|
182
|
+
const matrix = owners[0].element.transform.getWorldMatrixView();
|
|
183
|
+
const worldBounds = Bound.fromPoints(rectCorners$1(bounds).map((point) => transformPoint(matrix, point)));
|
|
184
|
+
if ((worldBounds.width === 0 || worldBounds.height === 0) && fallback) return fallback;
|
|
147
185
|
return createSelectionFrameProjection({
|
|
148
|
-
left:
|
|
149
|
-
top:
|
|
150
|
-
width:
|
|
151
|
-
height:
|
|
186
|
+
left: worldBounds.minX,
|
|
187
|
+
top: worldBounds.minY,
|
|
188
|
+
width: worldBounds.width,
|
|
189
|
+
height: worldBounds.height,
|
|
152
190
|
angle: 0
|
|
153
191
|
});
|
|
154
192
|
}
|
|
@@ -156,13 +194,13 @@ function createWorldVisualBoundsSelectionFrame(owners, fallback) {
|
|
|
156
194
|
let top = Infinity;
|
|
157
195
|
let right = -Infinity;
|
|
158
196
|
let bottom = -Infinity;
|
|
159
|
-
for (const
|
|
160
|
-
|
|
161
|
-
if (
|
|
162
|
-
if (
|
|
163
|
-
if (
|
|
164
|
-
if (bounds.top + bounds.height > bottom) bottom = bounds.top + bounds.height;
|
|
197
|
+
for (const owner of owners) for (const point of worldSelectionFramePoints(owner)) {
|
|
198
|
+
if (point.x < left) left = point.x;
|
|
199
|
+
if (point.y < top) top = point.y;
|
|
200
|
+
if (point.x > right) right = point.x;
|
|
201
|
+
if (point.y > bottom) bottom = point.y;
|
|
165
202
|
}
|
|
203
|
+
if (!Number.isFinite(left) || !Number.isFinite(top) || !Number.isFinite(right) || !Number.isFinite(bottom)) return fallback ?? null;
|
|
166
204
|
const width = right - left;
|
|
167
205
|
const height = bottom - top;
|
|
168
206
|
if ((width === 0 || height === 0) && fallback) return fallback;
|
|
@@ -212,10 +250,8 @@ function createSelectionRotationPivotSnapMarkers(frame) {
|
|
|
212
250
|
vertical
|
|
213
251
|
})));
|
|
214
252
|
}
|
|
215
|
-
function frameFromSingleOwner(owner) {
|
|
216
|
-
const
|
|
217
|
-
const bounds = owner.transform?.getSelectionFrameBounds?.() ?? geometry.getLocalVisualBoundsView() ?? geometry.getLayoutReferenceBoundsView();
|
|
218
|
-
const matrix = geometry.getWorldMatrixView();
|
|
253
|
+
function frameFromSingleOwner(owner, bounds = resolveSelectionFrameBounds(owner.element, owner.transform)) {
|
|
254
|
+
const matrix = owner.element.transform.getWorldMatrixView();
|
|
219
255
|
const decomposition = qrDecompose(matrix);
|
|
220
256
|
const angleRadians = decomposition.angle * Math.PI / 180;
|
|
221
257
|
const skewScaleX = matrix.c * Math.cos(angleRadians) + matrix.d * Math.sin(angleRadians);
|
|
@@ -237,7 +273,7 @@ function frameFromSingleOwner(owner) {
|
|
|
237
273
|
function frameFromOwners(owners) {
|
|
238
274
|
if (owners.length === 0) return null;
|
|
239
275
|
if (owners.length === 1) return frameFromSingleOwner(owners[0]);
|
|
240
|
-
return
|
|
276
|
+
return createWorldSelectionFrame(owners);
|
|
241
277
|
}
|
|
242
278
|
function resizeControls(frame, policy) {
|
|
243
279
|
const x = policy.resize === "x" || policy.resize === "both";
|
|
@@ -306,8 +342,7 @@ function rotationControls(frame) {
|
|
|
306
342
|
target: ROTATE_TARGET
|
|
307
343
|
}));
|
|
308
344
|
}
|
|
309
|
-
function pointControls(
|
|
310
|
-
const matrix = owner.element.transform.getWorldMatrixView();
|
|
345
|
+
function pointControls(topology, activePointTarget, matrix) {
|
|
311
346
|
return topology.pointControls.map((control) => {
|
|
312
347
|
return {
|
|
313
348
|
world: transformPoint(matrix, control.local),
|
|
@@ -386,8 +421,12 @@ function createProjectionOwner(element, frameVisible = true, pointScope = null,
|
|
|
386
421
|
function buildSelectionOverlayProjection({ owners, membership, topology, frame: frameOverride, policy, pivot, showPivot, pivotSnapMarkers = [], activePointTarget = null, marquee }) {
|
|
387
422
|
const frame = frameOverride ?? frameFromOwners(owners);
|
|
388
423
|
const controls = [];
|
|
389
|
-
|
|
390
|
-
const
|
|
424
|
+
const singleOwner = owners.length === 1 ? owners[0] : void 0;
|
|
425
|
+
const singleOwnerMatrix = singleOwner !== void 0 && (topology.pointControls.length > 0 || frame !== null && pivot.kind === "custom-owner-local" && pivot.ownerKey === singleOwner.element.key) ? singleOwner.element.transform.getWorldMatrixView() : void 0;
|
|
426
|
+
if (owners.length === 1) {
|
|
427
|
+
if (topology.pointControls.length > 0) controls.push(...pointControls(topology, activePointTarget, singleOwnerMatrix));
|
|
428
|
+
}
|
|
429
|
+
const rotationPivotWorld = frame && owners.length === 1 && pivot.kind === "custom-owner-local" && pivot.ownerKey === owners[0].element.key ? transformPoint(singleOwnerMatrix, pivot.local) : frame?.worldCenter ?? {
|
|
391
430
|
x: 0,
|
|
392
431
|
y: 0
|
|
393
432
|
};
|
|
@@ -520,21 +559,13 @@ function resolveRotationTransform({ frameAngle, pivot, unwrappedDelta, snapAngle
|
|
|
520
559
|
};
|
|
521
560
|
}
|
|
522
561
|
//#endregion
|
|
523
|
-
//#region packages/tools/src/editor/interaction-controller.ts
|
|
524
|
-
var editorControllerKey = Symbol();
|
|
525
|
-
var NO_PORT_SNAP$1 = {
|
|
526
|
-
snap: null,
|
|
527
|
-
state: null
|
|
528
|
-
};
|
|
529
|
-
var NO_ELEMENTS = [];
|
|
530
|
-
var NO_POINTS = [];
|
|
562
|
+
//#region packages/tools/src/editor/interaction-controller/helpers.ts
|
|
531
563
|
function isBlockedPortProbe(probe) {
|
|
532
564
|
return probe?.state === "incompatible" || probe?.state === "forbidden";
|
|
533
565
|
}
|
|
534
566
|
function resolvePortProbeCursor(probe) {
|
|
535
567
|
return isBlockedPortProbe(probe) ? "not-allowed" : "crosshair";
|
|
536
568
|
}
|
|
537
|
-
var NO_PROJECTION_OWNERS = [];
|
|
538
569
|
function pointIntent(event) {
|
|
539
570
|
return {
|
|
540
571
|
point: {
|
|
@@ -573,6 +604,8 @@ function resolveOriginPoint(bounds, x, y) {
|
|
|
573
604
|
y: bounds.top + (resolveOrigin(y) + .5) * bounds.height
|
|
574
605
|
};
|
|
575
606
|
}
|
|
607
|
+
//#endregion
|
|
608
|
+
//#region packages/tools/src/editor/interaction-controller/gestures.ts
|
|
576
609
|
var TransformGesture = class {
|
|
577
610
|
lastIntent;
|
|
578
611
|
previousRotationAngle;
|
|
@@ -704,23 +737,6 @@ var PointGesture = class {
|
|
|
704
737
|
}
|
|
705
738
|
}
|
|
706
739
|
};
|
|
707
|
-
function createToolsConnectionIntent(root, history, source, target) {
|
|
708
|
-
return {
|
|
709
|
-
source,
|
|
710
|
-
target,
|
|
711
|
-
connect() {
|
|
712
|
-
const change = resolveConnectEndpointChange(root, source, target);
|
|
713
|
-
if (!change) return;
|
|
714
|
-
const owner = root.getByKey(source.elementKey);
|
|
715
|
-
const selection = owner instanceof Element ? [owner] : [];
|
|
716
|
-
const scope = history.openScope(selection);
|
|
717
|
-
root.applyChangeBatch([change], [], scope);
|
|
718
|
-
root.frameRuntime.drain();
|
|
719
|
-
const current = root.getByKey(source.elementKey);
|
|
720
|
-
history.publishConfiguredChange(scope?.finish(current instanceof Element ? [current] : []));
|
|
721
|
-
}
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
740
|
var PivotGesture = class {
|
|
725
741
|
finalPoint;
|
|
726
742
|
constructor(controller, owner, startPointer, startPivot) {
|
|
@@ -826,6 +842,38 @@ var MarqueeGesture = class {
|
|
|
826
842
|
this.controller.finishSelectionGesture(this, selection);
|
|
827
843
|
}
|
|
828
844
|
};
|
|
845
|
+
//#endregion
|
|
846
|
+
//#region packages/tools/src/editor/interaction-controller/connection-intent.ts
|
|
847
|
+
function createToolsConnectionIntent(root, history, source, target) {
|
|
848
|
+
return {
|
|
849
|
+
source,
|
|
850
|
+
target,
|
|
851
|
+
connect() {
|
|
852
|
+
const change = resolveConnectEndpointChange(root, source, target);
|
|
853
|
+
if (!change) return;
|
|
854
|
+
const owner = root.getByKey(source.elementKey);
|
|
855
|
+
const selection = owner instanceof Element ? [owner] : [];
|
|
856
|
+
const scope = history.openScope(selection);
|
|
857
|
+
root.applyChangeBatch([change], [], scope);
|
|
858
|
+
root.frameRuntime.drain();
|
|
859
|
+
const current = root.getByKey(source.elementKey);
|
|
860
|
+
history.publishConfiguredChange(scope?.finish(current instanceof Element ? [current] : []));
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
//#endregion
|
|
865
|
+
//#region packages/tools/src/editor/interaction-controller/controller.ts
|
|
866
|
+
var editorControllerKey = Symbol();
|
|
867
|
+
var NO_PORT_SNAP$1 = {
|
|
868
|
+
snap: null,
|
|
869
|
+
state: null
|
|
870
|
+
};
|
|
871
|
+
var NO_ELEMENTS = [];
|
|
872
|
+
var NO_POINTS = [];
|
|
873
|
+
var NO_PROJECTION_OWNERS = [];
|
|
874
|
+
function getOwnerSelectionFrameBounds(owner) {
|
|
875
|
+
return resolveSelectionFrameBounds(owner, getEditorCapability(owner, TransformInteractionCapabilityToken));
|
|
876
|
+
}
|
|
829
877
|
var EditorInteractionController = class {
|
|
830
878
|
frameFlush;
|
|
831
879
|
cleanups = [];
|
|
@@ -1129,7 +1177,7 @@ var EditorInteractionController = class {
|
|
|
1129
1177
|
const owner = this.owners[0].element;
|
|
1130
1178
|
const preview = this.rotationPivotPreview?.owner === owner ? this.rotationPivotPreview.local : null;
|
|
1131
1179
|
if (!preview && owner.rotationPivotX === "center" && owner.rotationPivotY === "center") return { kind: "selection-center" };
|
|
1132
|
-
const bounds =
|
|
1180
|
+
const bounds = getOwnerSelectionFrameBounds(owner);
|
|
1133
1181
|
return {
|
|
1134
1182
|
kind: "custom-owner-local",
|
|
1135
1183
|
ownerKey: owner.key,
|
|
@@ -1714,7 +1762,7 @@ var EditorInteractionController = class {
|
|
|
1714
1762
|
if (command && key === "a") {
|
|
1715
1763
|
event.preventDefault();
|
|
1716
1764
|
const elements = /* @__PURE__ */ new Set();
|
|
1717
|
-
for (const element of this.root.queryArea(this.root.viewport.
|
|
1765
|
+
for (const element of this.root.queryArea(this.root.viewport.getWorldRectView(), {
|
|
1718
1766
|
bounds: "visual",
|
|
1719
1767
|
order: "none"
|
|
1720
1768
|
})) {
|
|
@@ -1852,9 +1900,10 @@ var EditorInteractionController = class {
|
|
|
1852
1900
|
}
|
|
1853
1901
|
const transformIntent = {
|
|
1854
1902
|
worldDelta,
|
|
1903
|
+
...gesture.kind === "resize" ? { bakeGeometry: true } : {},
|
|
1855
1904
|
...unwrappedRotationDelta === void 0 ? {} : { unwrappedRotationDelta }
|
|
1856
1905
|
};
|
|
1857
|
-
const changes = gesture.records.
|
|
1906
|
+
const changes = gesture.records.flatMap((record) => record.capability.resolveTransformChange(record.baseline, transformIntent));
|
|
1858
1907
|
this.selectionFrame = resolveSelectionFrameTransform(gesture.frame, worldDelta, gesture.kind);
|
|
1859
1908
|
this.invalidatePointTopology();
|
|
1860
1909
|
this.applyAuthoredBatch(changes, [], gesture.history);
|
|
@@ -2004,7 +2053,7 @@ var EditorInteractionController = class {
|
|
|
2004
2053
|
const scope = this.history.openScope(this.selection);
|
|
2005
2054
|
const worldDelta = new DOMMatrix().translate(dx, dy);
|
|
2006
2055
|
const intent = { worldDelta };
|
|
2007
|
-
const changes = records.
|
|
2056
|
+
const changes = records.flatMap((record) => record.capability.resolveTransformChange(record.baseline, intent));
|
|
2008
2057
|
if (changes.length > 0) {
|
|
2009
2058
|
if (frame) this.selectionFrame = resolveSelectionFrameTransform(frame, worldDelta, "move");
|
|
2010
2059
|
this.invalidatePointTopology();
|
|
@@ -2046,11 +2095,14 @@ var EditorInteractionController = class {
|
|
|
2046
2095
|
}
|
|
2047
2096
|
return this.projectionState.frame;
|
|
2048
2097
|
}
|
|
2049
|
-
applyTransformCommand(worldDelta) {
|
|
2098
|
+
applyTransformCommand(worldDelta, bakeGeometry = false) {
|
|
2050
2099
|
const records = this.captureTransformRecords();
|
|
2051
2100
|
if (records.length !== this.owners.length) return;
|
|
2052
|
-
const intent = {
|
|
2053
|
-
|
|
2101
|
+
const intent = {
|
|
2102
|
+
worldDelta,
|
|
2103
|
+
bakeGeometry
|
|
2104
|
+
};
|
|
2105
|
+
const changes = records.flatMap((record) => record.capability.resolveTransformChange(record.baseline, intent));
|
|
2054
2106
|
this.invalidatePointTopology();
|
|
2055
2107
|
const scope = this.history.openScope(this.selection);
|
|
2056
2108
|
this.applyCommand(changes, [], scope, this.selection);
|
|
@@ -2058,7 +2110,7 @@ var EditorInteractionController = class {
|
|
|
2058
2110
|
resize(options = {}) {
|
|
2059
2111
|
const projectionFrame = this.prepareTransformCommandFrame();
|
|
2060
2112
|
if (!projectionFrame) return;
|
|
2061
|
-
const frame = this.owners.length === 1 ?
|
|
2113
|
+
const frame = this.owners.length === 1 ? createWorldSelectionFrame(this.owners, projectionFrame) : projectionFrame;
|
|
2062
2114
|
if (!frame) return;
|
|
2063
2115
|
const resizeX = this.policy.resize === "x" || this.policy.resize === "both";
|
|
2064
2116
|
const resizeY = this.policy.resize === "y" || this.policy.resize === "both";
|
|
@@ -2066,14 +2118,14 @@ var EditorInteractionController = class {
|
|
|
2066
2118
|
const height = resizeY && options.height !== void 0 ? options.height : frame.height;
|
|
2067
2119
|
if (width === frame.width && height === frame.height) return;
|
|
2068
2120
|
const worldDelta = resolveResizeWorldDeltaForSize(frame, width, height, options.anchor);
|
|
2069
|
-
if (worldDelta
|
|
2070
|
-
this.applyTransformCommand(worldDelta);
|
|
2121
|
+
if (isIdentityMatrix(worldDelta)) return;
|
|
2122
|
+
this.applyTransformCommand(worldDelta, true);
|
|
2071
2123
|
}
|
|
2072
2124
|
flip(direction) {
|
|
2073
2125
|
const frame = this.prepareTransformCommandFrame();
|
|
2074
2126
|
if (!frame) return;
|
|
2075
2127
|
if (!(direction === "horizontal" ? this.policy.resize === "x" || this.policy.resize === "both" : this.policy.resize === "y" || this.policy.resize === "both")) return;
|
|
2076
|
-
this.applyTransformCommand(resolveFlipWorldDelta(frame, direction));
|
|
2128
|
+
this.applyTransformCommand(resolveFlipWorldDelta(frame, direction), true);
|
|
2077
2129
|
}
|
|
2078
2130
|
previewRotationPivot(owner, point) {
|
|
2079
2131
|
const preview = this.resolveRotationPivot(owner, point, true);
|
|
@@ -2117,7 +2169,7 @@ var EditorInteractionController = class {
|
|
|
2117
2169
|
if (!local) return;
|
|
2118
2170
|
const frame = this.projectionState.frame;
|
|
2119
2171
|
if (!frame) return;
|
|
2120
|
-
const bounds =
|
|
2172
|
+
const bounds = getOwnerSelectionFrameBounds(owner);
|
|
2121
2173
|
const markers = createSelectionRotationPivotSnapMarkers(frame);
|
|
2122
2174
|
if (snap) {
|
|
2123
2175
|
const threshold = 8 / this.root.viewport.scale;
|
|
@@ -2930,7 +2982,7 @@ function alignElements(select, type) {
|
|
|
2930
2982
|
const capability = getEditorCapability(rect.element, TransformInteractionCapabilityToken);
|
|
2931
2983
|
if (!capability) continue;
|
|
2932
2984
|
const baseline = capability.captureTransformBaseline();
|
|
2933
|
-
changes.push(capability.resolveTransformChange(baseline, { worldDelta: new DOMMatrix().translate(rect.dx, rect.dy) }));
|
|
2985
|
+
changes.push(...capability.resolveTransformChange(baseline, { worldDelta: new DOMMatrix().translate(rect.dx, rect.dy) }));
|
|
2934
2986
|
}
|
|
2935
2987
|
if (changes.length === 0) return;
|
|
2936
2988
|
const scope = select.history.openScope(elements);
|
|
@@ -3030,7 +3082,7 @@ function buildLayerMoves(select, selection, type) {
|
|
|
3030
3082
|
else children.push(...elements);
|
|
3031
3083
|
}
|
|
3032
3084
|
return {
|
|
3033
|
-
changes: moves.
|
|
3085
|
+
changes: moves.flatMap(({ change }) => change),
|
|
3034
3086
|
structure: [...finalChildren].map(([owner, children]) => ({
|
|
3035
3087
|
owner,
|
|
3036
3088
|
children
|
|
@@ -3235,7 +3287,7 @@ function deserializeClones(entries, deserializers) {
|
|
|
3235
3287
|
const json = structuredClone(entry);
|
|
3236
3288
|
removeKeys(json);
|
|
3237
3289
|
offsetTopLevel(json);
|
|
3238
|
-
const element = deserializeElement
|
|
3290
|
+
const element = deserializeElement(json, deserializers);
|
|
3239
3291
|
mapClonedKeys(entry, element, keyMap);
|
|
3240
3292
|
return element;
|
|
3241
3293
|
});
|
|
@@ -3404,6 +3456,47 @@ function hasSelectedAncestor(element, selected) {
|
|
|
3404
3456
|
function parentWorld(parent) {
|
|
3405
3457
|
return parent instanceof Element ? parent.transform.getChildWorldInverseMatrixView().inverse() : new DOMMatrix();
|
|
3406
3458
|
}
|
|
3459
|
+
function rectCorners(rect) {
|
|
3460
|
+
const right = rect.left + rect.width;
|
|
3461
|
+
const bottom = rect.top + rect.height;
|
|
3462
|
+
return [
|
|
3463
|
+
{
|
|
3464
|
+
x: rect.left,
|
|
3465
|
+
y: rect.top
|
|
3466
|
+
},
|
|
3467
|
+
{
|
|
3468
|
+
x: right,
|
|
3469
|
+
y: rect.top
|
|
3470
|
+
},
|
|
3471
|
+
{
|
|
3472
|
+
x: right,
|
|
3473
|
+
y: bottom
|
|
3474
|
+
},
|
|
3475
|
+
{
|
|
3476
|
+
x: rect.left,
|
|
3477
|
+
y: bottom
|
|
3478
|
+
}
|
|
3479
|
+
];
|
|
3480
|
+
}
|
|
3481
|
+
/**
|
|
3482
|
+
* Group creation uses the same owner frame that selection uses. Owners that
|
|
3483
|
+
* do not expose a frame retain their logical content-bound contract;
|
|
3484
|
+
* Text-like frame owners therefore cannot enlarge the new Group with visible
|
|
3485
|
+
* overflow.
|
|
3486
|
+
*/
|
|
3487
|
+
function groupBounds(children) {
|
|
3488
|
+
let bounds = null;
|
|
3489
|
+
for (const child of children) {
|
|
3490
|
+
const frame = getEditorCapability(child, TransformInteractionCapabilityToken)?.getSelectionFrameBounds?.();
|
|
3491
|
+
const childBounds = frame ? Bound.fromPoints(rectCorners(frame).map((point) => transformPoint(child.transform.getWorldMatrixView(), point))) : child.transform.getContentBounds();
|
|
3492
|
+
if (!bounds) {
|
|
3493
|
+
bounds = childBounds;
|
|
3494
|
+
continue;
|
|
3495
|
+
}
|
|
3496
|
+
bounds = bounds.merge(childBounds);
|
|
3497
|
+
}
|
|
3498
|
+
return bounds ?? new Bound();
|
|
3499
|
+
}
|
|
3407
3500
|
function captureChangesForParent(elements, targetParentWorldMatrix) {
|
|
3408
3501
|
const changes = [];
|
|
3409
3502
|
const intent = {
|
|
@@ -3413,7 +3506,7 @@ function captureChangesForParent(elements, targetParentWorldMatrix) {
|
|
|
3413
3506
|
for (const element of elements) {
|
|
3414
3507
|
const capability = getEditorCapability(element, TransformInteractionCapabilityToken);
|
|
3415
3508
|
const baseline = capability.captureTransformBaseline();
|
|
3416
|
-
changes.push(capability.resolveTransformChange(baseline, intent));
|
|
3509
|
+
changes.push(...capability.resolveTransformChange(baseline, intent));
|
|
3417
3510
|
}
|
|
3418
3511
|
return changes;
|
|
3419
3512
|
}
|
|
@@ -3430,7 +3523,7 @@ function doGroup(select) {
|
|
|
3430
3523
|
if (children.length <= 1) return null;
|
|
3431
3524
|
const parent = children[0].parent;
|
|
3432
3525
|
if (!parent) return null;
|
|
3433
|
-
const bounds =
|
|
3526
|
+
const bounds = groupBounds(children);
|
|
3434
3527
|
const targetWorld = new DOMMatrix().translate(bounds.left, bounds.top);
|
|
3435
3528
|
const resolution = resolveReferenceTransform({
|
|
3436
3529
|
writeMode: "pose-authored",
|
|
@@ -4791,7 +4884,7 @@ var Rule = class extends Element {
|
|
|
4791
4884
|
ctx.clearRect(rulerSize, 0, w - rulerSize, rulerSize);
|
|
4792
4885
|
ctx.fillStyle = this.backgroundColor;
|
|
4793
4886
|
ctx.fillRect(rulerSize, 0, Math.max(0, w - rulerSize), rulerSize);
|
|
4794
|
-
const vpRect = viewport.
|
|
4887
|
+
const vpRect = viewport.getWorldRectView();
|
|
4795
4888
|
const stepX = niceStep(pxPerTick, viewport.scale);
|
|
4796
4889
|
const startWorldX = vpRect.left;
|
|
4797
4890
|
const endWorldX = vpRect.left + vpRect.width;
|
|
@@ -4973,4 +5066,4 @@ var EditorLayer = class extends Layer {
|
|
|
4973
5066
|
}
|
|
4974
5067
|
};
|
|
4975
5068
|
//#endregion
|
|
4976
|
-
export { ArrangeType, EditorLayer, HistoryManager, LineTool, Rule, Select, Snap, alignElements
|
|
5069
|
+
export { ArrangeType, EditorLayer, HistoryManager, LineTool, Rule, Select, Snap, alignElements };
|
|
@@ -109,10 +109,9 @@ export interface SelectionProjectionTopology {
|
|
|
109
109
|
readonly pointScopeOutline: readonly PointView[] | null;
|
|
110
110
|
readonly pointScopeValid: boolean;
|
|
111
111
|
}
|
|
112
|
-
export declare function samePointEditTarget(left: PointEditTarget, right: PointEditTarget): boolean;
|
|
113
112
|
export declare function createSelectionFrameProjection({ left, top, width, height, angle }: Pick<SelectionFrameProjection, "left" | "top" | "width" | "height" | "angle">): SelectionFrameProjection;
|
|
114
113
|
/** @internal Build a command frame from the current owner bounds. */
|
|
115
|
-
export declare function
|
|
114
|
+
export declare function createWorldSelectionFrame(owners: readonly ProjectionOwner[], fallback?: SelectionFrameProjection | null): SelectionFrameProjection;
|
|
116
115
|
export declare function createSelectionRotationPivotSnapMarkers(frame: SelectionFrameProjection): readonly SelectionRotationPivotSnapMarker[];
|
|
117
116
|
export declare function createSelectionProjectionMembership(owners: readonly ProjectionOwner[], policy: ProjectionPolicy): SelectionProjectionMembership;
|
|
118
117
|
export declare function createSelectionProjectionTopology(owners: readonly ProjectionOwner[], isCurrent?: (owner: ProjectionOwner) => boolean): SelectionProjectionTopology;
|
package/package.json
CHANGED
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
import { Element, Node, type ConnectionPortRef, type OwnerPropertyChange, type Root, type StructureChange } from "@fulate/core";
|
|
2
|
-
import { LineTree, type EndpointConnectionRole, type EndpointRef, type ContentEditingSession, type EditorAuthoredGesture, type PointEditTarget, type PointEditingScope, type LineTreeBranchTailSource, type LineDecoration, type SelectionBehavior, type TransformInteractionCapability } from "@fulate/ui";
|
|
3
|
-
import { type PointView, type Rect } from "@fulate/share";
|
|
4
|
-
import type { HistoryManager, HistoryScope } from "../history";
|
|
5
|
-
import type { LineTool } from "../line";
|
|
6
|
-
import type { Select, SelectedPropertyPatch } from "../select";
|
|
7
|
-
import { type ProjectionOwner, type ResizeHandle, type SelectionOverlayProjection, type SelectionOverlayTarget, type SelectionRotationPivot } from "../select/projection";
|
|
8
|
-
import { type FlipDirection, type ResizeOptions } from "../select/transform";
|
|
9
|
-
import type { PortSnapResult, Snap } from "../select/snap";
|
|
10
|
-
interface SelectedOwner extends ProjectionOwner {
|
|
11
|
-
readonly behavior: SelectionBehavior;
|
|
12
|
-
}
|
|
13
|
-
export interface SelectionIdentity {
|
|
14
|
-
readonly owners: readonly SelectedOwner[];
|
|
15
|
-
readonly interaction: EditorInteractionState;
|
|
16
|
-
}
|
|
17
|
-
interface TransformOwnerRecord {
|
|
18
|
-
readonly owner: Element;
|
|
19
|
-
readonly parent: Node | undefined;
|
|
20
|
-
readonly capability: TransformInteractionCapability;
|
|
21
|
-
readonly baseline: ReturnType<TransformInteractionCapability["captureTransformBaseline"]>;
|
|
22
|
-
}
|
|
23
|
-
interface PointerIntent {
|
|
24
|
-
readonly point: PointView;
|
|
25
|
-
readonly shiftKey: boolean;
|
|
26
|
-
}
|
|
27
|
-
interface PointerGesture {
|
|
28
|
-
update(intent: PointerIntent): void;
|
|
29
|
-
finish(intent?: PointerIntent, cancelled?: boolean): void;
|
|
30
|
-
}
|
|
31
|
-
interface ApplyCommandOptions {
|
|
32
|
-
readonly beforeDrain?: () => void;
|
|
33
|
-
readonly afterHistory?: () => void;
|
|
34
|
-
readonly installSelection?: () => boolean;
|
|
35
|
-
}
|
|
36
|
-
interface LineDrawingSession {
|
|
37
|
-
readonly tool: LineTool;
|
|
38
|
-
readonly resultType: "line" | "lineTree";
|
|
39
|
-
readonly tailDecoration: LineDecoration;
|
|
40
|
-
readonly history: HistoryScope | undefined;
|
|
41
|
-
readonly confirmed: PointView[];
|
|
42
|
-
headTarget: ConnectionPortRef | undefined;
|
|
43
|
-
tailTarget: ConnectionPortRef | undefined;
|
|
44
|
-
preview: PointView | null;
|
|
45
|
-
snap: PortSnapResult | null;
|
|
46
|
-
}
|
|
47
|
-
interface ActivePointTarget {
|
|
48
|
-
readonly ownerKey: string;
|
|
49
|
-
readonly target: PointEditTarget;
|
|
50
|
-
}
|
|
51
|
-
export type SelectControllerState = {
|
|
52
|
-
readonly kind: "idle";
|
|
53
|
-
readonly gesture: MarqueeGesture | null;
|
|
54
|
-
} | {
|
|
55
|
-
readonly kind: "transform";
|
|
56
|
-
readonly gesture: TransformGesture | PivotGesture | SelectionPressGesture | null;
|
|
57
|
-
} | {
|
|
58
|
-
readonly kind: "point";
|
|
59
|
-
readonly ownerKey: string;
|
|
60
|
-
readonly scope: PointEditingScope;
|
|
61
|
-
readonly gesture: PointGesture | SelectionPressGesture | TransformGesture | null;
|
|
62
|
-
readonly activeTarget: ActivePointTarget | null;
|
|
63
|
-
} | {
|
|
64
|
-
readonly kind: "port";
|
|
65
|
-
readonly ownerKey: string;
|
|
66
|
-
readonly scope: PointEditingScope;
|
|
67
|
-
readonly gesture: PointGesture;
|
|
68
|
-
readonly source: EndpointRef;
|
|
69
|
-
readonly activeTarget: ActivePointTarget | null;
|
|
70
|
-
} | {
|
|
71
|
-
readonly kind: "content";
|
|
72
|
-
readonly session: ContentEditingSession;
|
|
73
|
-
readonly history: HistoryScope | undefined;
|
|
74
|
-
};
|
|
75
|
-
type LineTreeDrawingSession = {
|
|
76
|
-
readonly kind: "source-pick";
|
|
77
|
-
readonly tool: LineTool;
|
|
78
|
-
readonly owner: LineTree;
|
|
79
|
-
readonly tailDecoration: LineDecoration;
|
|
80
|
-
hover: LineTreeBranchTailSource | null;
|
|
81
|
-
} | {
|
|
82
|
-
readonly kind: "branch-draw";
|
|
83
|
-
readonly tool: LineTool;
|
|
84
|
-
readonly owner: LineTree;
|
|
85
|
-
readonly source: LineTreeBranchTailSource;
|
|
86
|
-
readonly tailDecoration: LineDecoration;
|
|
87
|
-
readonly confirmed: PointView[];
|
|
88
|
-
tailTarget: ConnectionPortRef | undefined;
|
|
89
|
-
preview: PointView | null;
|
|
90
|
-
snap: PortSnapResult | null;
|
|
91
|
-
};
|
|
92
|
-
export type EditorInteractionState = {
|
|
93
|
-
readonly kind: "select";
|
|
94
|
-
readonly interaction: SelectControllerState;
|
|
95
|
-
readonly selection: readonly Element[];
|
|
96
|
-
readonly pivot: SelectionRotationPivot;
|
|
97
|
-
} | {
|
|
98
|
-
readonly kind: "line-draw";
|
|
99
|
-
readonly session: LineDrawingSession | LineTreeDrawingSession;
|
|
100
|
-
};
|
|
101
|
-
declare class TransformGesture implements PointerGesture {
|
|
102
|
-
readonly controller: EditorInteractionController;
|
|
103
|
-
readonly kind: "move" | "resize" | "rotate";
|
|
104
|
-
readonly start: PointView;
|
|
105
|
-
readonly frame: NonNullable<SelectionOverlayProjection["frame"]>;
|
|
106
|
-
readonly records: readonly TransformOwnerRecord[];
|
|
107
|
-
readonly snapPoints: readonly PointView[];
|
|
108
|
-
readonly movableLineEndpoints: readonly PointView[];
|
|
109
|
-
readonly snapExcludes: readonly Element[];
|
|
110
|
-
readonly rotationPivotWorld: PointView;
|
|
111
|
-
readonly history: HistoryScope | undefined;
|
|
112
|
-
readonly resizeHandle?: ResizeHandle;
|
|
113
|
-
private lastIntent;
|
|
114
|
-
private previousRotationAngle;
|
|
115
|
-
private unwrappedRotationDelta;
|
|
116
|
-
constructor(controller: EditorInteractionController, kind: "move" | "resize" | "rotate", start: PointView, initialIntent: PointerIntent, frame: NonNullable<SelectionOverlayProjection["frame"]>, records: readonly TransformOwnerRecord[], snapPoints: readonly PointView[], movableLineEndpoints: readonly PointView[], snapExcludes: readonly Element[], rotationPivotWorld: PointView, history: HistoryScope | undefined, resizeHandle?: ResizeHandle);
|
|
117
|
-
consumeRotationDelta(point: PointView): number;
|
|
118
|
-
probeForbiddenPort(point: PointView): boolean;
|
|
119
|
-
update(intent: PointerIntent): void;
|
|
120
|
-
ownsCurrentContext(): boolean;
|
|
121
|
-
finishInvalidated(): void;
|
|
122
|
-
finish(intent?: PointerIntent): void;
|
|
123
|
-
}
|
|
124
|
-
declare class PointGesture implements PointerGesture, EditorAuthoredGesture {
|
|
125
|
-
readonly controller: EditorInteractionController;
|
|
126
|
-
readonly ownerKey: string;
|
|
127
|
-
readonly startParent: Node | undefined;
|
|
128
|
-
readonly startTarget: PointEditTarget;
|
|
129
|
-
readonly insertT: number | undefined;
|
|
130
|
-
readonly role: "endpoint" | "vertex" | "segment" | "handle";
|
|
131
|
-
readonly connectionRole: EndpointConnectionRole | null;
|
|
132
|
-
readonly snapExcludes: readonly Element[];
|
|
133
|
-
readonly history: HistoryScope | undefined;
|
|
134
|
-
readonly startPointerWorld: PointView;
|
|
135
|
-
readonly startTargetWorld: PointView;
|
|
136
|
-
private latestIntent;
|
|
137
|
-
private consumedIntent;
|
|
138
|
-
connectionTarget: ConnectionPortRef | undefined;
|
|
139
|
-
connectionTargetOwner: Element | undefined;
|
|
140
|
-
connectionTargetParent: Node | undefined;
|
|
141
|
-
constructor(controller: EditorInteractionController, ownerKey: string, startParent: Node | undefined, startTarget: PointEditTarget, insertT: number | undefined, role: "endpoint" | "vertex" | "segment" | "handle", connectionRole: EndpointConnectionRole | null, snapExcludes: readonly Element[], history: HistoryScope | undefined, startPointerWorld: PointView, startTargetWorld: PointView);
|
|
142
|
-
currentTarget: PointEditTarget;
|
|
143
|
-
ownsCurrentContext(): boolean;
|
|
144
|
-
get connectionSource(): EndpointRef | undefined;
|
|
145
|
-
resolveWorldPoint(pointer: PointView): {
|
|
146
|
-
x: number;
|
|
147
|
-
y: number;
|
|
148
|
-
};
|
|
149
|
-
update(intent: PointerIntent): void;
|
|
150
|
-
consumeLatestIntent(): void;
|
|
151
|
-
finish(intent?: PointerIntent, cancelled?: boolean): void;
|
|
152
|
-
finishInvalidated(): void;
|
|
153
|
-
}
|
|
154
|
-
declare class PivotGesture implements PointerGesture {
|
|
155
|
-
readonly controller: EditorInteractionController;
|
|
156
|
-
readonly owner: Element;
|
|
157
|
-
readonly startPointer: PointView;
|
|
158
|
-
readonly startPivot: PointView;
|
|
159
|
-
private finalPoint;
|
|
160
|
-
constructor(controller: EditorInteractionController, owner: Element, startPointer: PointView, startPivot: PointView);
|
|
161
|
-
update(intent: PointerIntent): void;
|
|
162
|
-
finish(intent?: PointerIntent): void;
|
|
163
|
-
}
|
|
164
|
-
declare class SelectionPressGesture implements PointerGesture {
|
|
165
|
-
readonly controller: EditorInteractionController;
|
|
166
|
-
readonly start: PointerIntent;
|
|
167
|
-
readonly candidate: Element | undefined;
|
|
168
|
-
readonly contentTarget: Element | null;
|
|
169
|
-
readonly directDragOwner?: Element;
|
|
170
|
-
constructor(controller: EditorInteractionController, start: PointerIntent, candidate: Element | undefined, contentTarget: Element | null, directDragOwner?: Element);
|
|
171
|
-
update(intent: PointerIntent): void;
|
|
172
|
-
finish(intent?: PointerIntent, cancelled?: boolean): void;
|
|
173
|
-
}
|
|
174
|
-
declare class MarqueeGesture implements PointerGesture {
|
|
175
|
-
readonly controller: EditorInteractionController;
|
|
176
|
-
readonly start: PointView;
|
|
177
|
-
readonly candidate: Element | undefined;
|
|
178
|
-
readonly additive: boolean;
|
|
179
|
-
readonly initialSelection: readonly Element[];
|
|
180
|
-
private current;
|
|
181
|
-
private moved;
|
|
182
|
-
constructor(controller: EditorInteractionController, start: PointView, candidate: Element | undefined, additive: boolean, initialSelection: readonly Element[]);
|
|
183
|
-
update(intent: PointerIntent): void;
|
|
184
|
-
finish(intent?: PointerIntent, cancelled?: boolean): void;
|
|
185
|
-
}
|
|
186
|
-
export declare class EditorInteractionController {
|
|
187
|
-
readonly select: Select;
|
|
188
|
-
readonly root: Root;
|
|
189
|
-
readonly history: HistoryManager;
|
|
190
|
-
readonly frameFlush: any;
|
|
191
|
-
private readonly cleanups;
|
|
192
|
-
private selectionValue;
|
|
193
|
-
private owners;
|
|
194
|
-
private policy;
|
|
195
|
-
private rotationPivotPreview;
|
|
196
|
-
private selectionFrame;
|
|
197
|
-
private preserveSelectionFrame;
|
|
198
|
-
private projectionState;
|
|
199
|
-
private projectionOwners;
|
|
200
|
-
private projectionMembership;
|
|
201
|
-
private projectionTopology;
|
|
202
|
-
private overlayDirty;
|
|
203
|
-
private pendingSelectionUpdate;
|
|
204
|
-
private pendingSelectionRepair;
|
|
205
|
-
private rotationPivotDisplayOwner;
|
|
206
|
-
private hoveredTarget;
|
|
207
|
-
private hoveredContent;
|
|
208
|
-
private controllerState;
|
|
209
|
-
private pasteTask;
|
|
210
|
-
private pointerWorld;
|
|
211
|
-
marquee: Readonly<Rect> | null;
|
|
212
|
-
constructor(select: Select, root: Root, history: HistoryManager);
|
|
213
|
-
get selection(): readonly Element[];
|
|
214
|
-
get projection(): SelectionOverlayProjection;
|
|
215
|
-
publishConfiguredChange(changed: boolean | undefined): void;
|
|
216
|
-
/** 把纯外部通知排到当前同步命令、回放或 frame completion 返回之后。 */
|
|
217
|
-
private notifySelect;
|
|
218
|
-
get contentHover(): Element<import("@fulate/core").ElementProperties, import("@fulate/core").BaseElementOption<any>, Partial<Omit<import("@fulate/core").BaseElementOption<any>, "key" | "children">>>;
|
|
219
|
-
get overlayTarget(): SelectionOverlayTarget;
|
|
220
|
-
get editorElements(): readonly Element[];
|
|
221
|
-
get snapTool(): Snap | undefined;
|
|
222
|
-
get state(): EditorInteractionState;
|
|
223
|
-
/** @internal Captures the canonical selection and interaction identities. */
|
|
224
|
-
captureSelectionIdentity(): SelectionIdentity;
|
|
225
|
-
/** @internal Makes one concrete clipboard Promise the latest paste intent. */
|
|
226
|
-
beginPaste(task: Promise<void>): void;
|
|
227
|
-
/** @internal Tests whether one paste still owns the captured selection intent. */
|
|
228
|
-
ownsPasteSelection(identity: SelectionIdentity, task: Promise<void>): boolean;
|
|
229
|
-
/** @internal Releases the latest paste intent after its Promise settles. */
|
|
230
|
-
finishPaste(task: Promise<void>): void;
|
|
231
|
-
private get selectState();
|
|
232
|
-
private installSelectState;
|
|
233
|
-
private get activeGesture();
|
|
234
|
-
private set activeGesture(value);
|
|
235
|
-
private get activePointGesture();
|
|
236
|
-
private get pointState();
|
|
237
|
-
private set pointState(value);
|
|
238
|
-
private get activePointTarget();
|
|
239
|
-
private set activePointTarget(value);
|
|
240
|
-
private get contentSession();
|
|
241
|
-
private set contentSession(value);
|
|
242
|
-
private get contentHistory();
|
|
243
|
-
private set contentHistory(value);
|
|
244
|
-
private get lineSession();
|
|
245
|
-
private set lineSession(value);
|
|
246
|
-
private get lineTreeSession();
|
|
247
|
-
private set lineTreeSession(value);
|
|
248
|
-
private get rotationPivotState();
|
|
249
|
-
private installInput;
|
|
250
|
-
private readonly handleSceneChange;
|
|
251
|
-
private sceneChangeAffectsSelection;
|
|
252
|
-
private selectionGeometryIsDirty;
|
|
253
|
-
private invalidatePointTopology;
|
|
254
|
-
private resolvePolicy;
|
|
255
|
-
setSelection(elements: readonly Element[]): void;
|
|
256
|
-
private readonly pointProjectionOwnerIsCurrent;
|
|
257
|
-
private restoreSelectionForHistory;
|
|
258
|
-
private replaceSelection;
|
|
259
|
-
private requestSelectionEnd;
|
|
260
|
-
private pointContextIsCurrent;
|
|
261
|
-
private resolveCurrentPointContext;
|
|
262
|
-
private resolvePointGestureContext;
|
|
263
|
-
pointGestureOwnsCurrentContext(gesture: PointGesture): boolean;
|
|
264
|
-
private setPointState;
|
|
265
|
-
private exitPointState;
|
|
266
|
-
private setActivePointTarget;
|
|
267
|
-
private invalidateOverlay;
|
|
268
|
-
private shouldShowRotationPivot;
|
|
269
|
-
private showRotationPivotForCurrentOwner;
|
|
270
|
-
hasPendingOverlayWork(): boolean;
|
|
271
|
-
flushOverlayProjection(): void;
|
|
272
|
-
hasPendingCompletionWork(): boolean;
|
|
273
|
-
flushCompletionWork(): void;
|
|
274
|
-
resolveCursor(point: PointView | undefined, contentCursor: string): string;
|
|
275
|
-
private updateHover;
|
|
276
|
-
private applyAuthoredBatch;
|
|
277
|
-
private onPointerDown;
|
|
278
|
-
private onPointerMove;
|
|
279
|
-
private onPointerFinish;
|
|
280
|
-
private onDoubleClick;
|
|
281
|
-
private onKeyDown;
|
|
282
|
-
private captureTransformRecords;
|
|
283
|
-
private startTransform;
|
|
284
|
-
promoteSelectionPress(gesture: SelectionPressGesture): TransformGesture | null;
|
|
285
|
-
finishSelectionPress(gesture: SelectionPressGesture): void;
|
|
286
|
-
finishSelectionGesture(gesture: PointerGesture, selection?: readonly Element[]): void;
|
|
287
|
-
consumeTransform(gesture: TransformGesture, intent: PointerIntent): void;
|
|
288
|
-
private startPointGesture;
|
|
289
|
-
consumePoint(gesture: PointGesture, intent: PointerIntent, first: boolean): void;
|
|
290
|
-
deletePointTarget(): boolean;
|
|
291
|
-
nudge(dx: number, dy: number): void;
|
|
292
|
-
setProperties(patch: SelectedPropertyPatch): void;
|
|
293
|
-
private prepareTransformCommandFrame;
|
|
294
|
-
private applyTransformCommand;
|
|
295
|
-
resize(options?: Readonly<ResizeOptions>): void;
|
|
296
|
-
flip(direction: FlipDirection): void;
|
|
297
|
-
previewRotationPivot(owner: Element, point: PointView): void;
|
|
298
|
-
clearRotationPivotPreview(owner: Element): void;
|
|
299
|
-
commitRotationPivot(owner: Element, point: PointView): boolean;
|
|
300
|
-
private resolveRotationPivot;
|
|
301
|
-
setMarquee(rect: Readonly<Rect> | null): void;
|
|
302
|
-
finishPointerGesture(gesture: PointerGesture): void;
|
|
303
|
-
private pointConnectionCandidateIsCurrent;
|
|
304
|
-
finishPointConnectionGesture(gesture: PointGesture): void;
|
|
305
|
-
private beginContentEditing;
|
|
306
|
-
finishContentEditing(discard: boolean): void;
|
|
307
|
-
startLineDrawing(tool: LineTool): void;
|
|
308
|
-
private startLineTreeBranchDrawing;
|
|
309
|
-
private updateLineTreeSourceHover;
|
|
310
|
-
private confirmLineTreeSource;
|
|
311
|
-
private beginLineTreeBranchDraft;
|
|
312
|
-
private updateLineTreePreview;
|
|
313
|
-
private probeLineTreeTail;
|
|
314
|
-
private confirmLineTreePoint;
|
|
315
|
-
private confirmLineTreeBranchDrawing;
|
|
316
|
-
private updateLinePreview;
|
|
317
|
-
private probeLineAnchor;
|
|
318
|
-
private confirmLinePoint;
|
|
319
|
-
stopLineDrawing(): void;
|
|
320
|
-
cancelLineDrawing(): void;
|
|
321
|
-
private confirmLineDrawing;
|
|
322
|
-
getLineDrawingView(tool: LineTool): LineDrawingSession | {
|
|
323
|
-
readonly kind: "branch-draw";
|
|
324
|
-
readonly tool: LineTool;
|
|
325
|
-
readonly owner: LineTree;
|
|
326
|
-
readonly source: LineTreeBranchTailSource;
|
|
327
|
-
readonly tailDecoration: LineDecoration;
|
|
328
|
-
readonly confirmed: PointView[];
|
|
329
|
-
tailTarget: ConnectionPortRef | undefined;
|
|
330
|
-
preview: PointView | null;
|
|
331
|
-
snap: PortSnapResult | null;
|
|
332
|
-
};
|
|
333
|
-
hasLineToolSession(tool: LineTool): boolean;
|
|
334
|
-
applyCommand(ownerEffects: readonly OwnerPropertyChange[], structureEffects: readonly StructureChange[], scope: HistoryScope | undefined, selectionAfter: readonly Element[], options?: ApplyCommandOptions): void;
|
|
335
|
-
finishCurrentInteraction(): void;
|
|
336
|
-
destroy(): void;
|
|
337
|
-
}
|
|
338
|
-
export declare function getEditorInteractionController(root: Root): EditorInteractionController;
|
|
339
|
-
export {};
|