@elicdavis/node-flow 0.1.20 → 0.2.0
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/dist/index.d.mts +386 -227
- package/dist/index.d.ts +386 -227
- package/dist/index.js +1866 -1059
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1849 -1058
- package/dist/index.mjs.map +1 -1
- package/index.html +1134 -329
- package/package.json +1 -1
- package/src/connection.ts +30 -5
- package/src/contextMenu.ts +66 -34
- package/src/graph.ts +12 -10
- package/src/index.ts +26 -12
- package/src/markdown/entry.ts +11 -15
- package/src/markdown/syntaxParser.ts +2 -1
- package/src/node.ts +160 -101
- package/src/nodes/subsystem.ts +113 -29
- package/src/notes/note.ts +9 -1
- package/src/organize.ts +540 -111
- package/src/popup.ts +4 -2
- package/src/port.ts +83 -6
- package/src/styles/bevel.ts +31 -0
- package/src/styles/cursor.ts +1 -0
- package/src/styles/text.ts +4 -4
- package/src/theme.ts +179 -92
- package/src/types/text.ts +5 -1
- package/src/utils/color.ts +1 -1
- package/src/widgets/button.ts +52 -107
- package/src/widgets/color.ts +91 -150
- package/src/widgets/factory.ts +89 -46
- package/src/widgets/field.ts +99 -0
- package/src/widgets/image.ts +107 -165
- package/src/widgets/inplace.ts +84 -0
- package/src/widgets/number.ts +122 -154
- package/src/widgets/row.ts +138 -0
- package/src/widgets/slider.ts +101 -216
- package/src/widgets/string.ts +39 -130
- package/src/widgets/text.ts +81 -92
- package/src/widgets/toggle.ts +62 -207
- package/src/widgets/value.ts +127 -0
- package/src/widgets/widget.ts +180 -34
- package/test/context_menu.test.ts +82 -0
- package/test/cursor.test.ts +17 -0
- package/test/hidden_port.test.ts +125 -0
- package/test/markdown.test.ts +2 -2
- package/test/markdown_code_block.test.ts +58 -0
- package/test/multi_input.test.ts +124 -0
- package/test/organize.test.ts +117 -1
- package/test/support.ts +175 -0
- package/test/theme.test.ts +86 -0
- package/test/widget_click.test.ts +111 -97
- package/test/widget_construction.test.ts +5 -4
- package/test/widgets.test.ts +333 -0
- package/test/wire_follows_camera.test.ts +57 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Theme } from "../theme";
|
|
2
|
+
import { CursorStyle } from "../styles/cursor";
|
|
3
|
+
import { Box, InBox } from "../types/box";
|
|
4
|
+
import { Vector2 } from "../types/vector2";
|
|
5
|
+
import { Widget, WidgetBase, WidgetPointer } from "./widget";
|
|
6
|
+
|
|
7
|
+
export interface RowWidgetConfig {
|
|
8
|
+
/**
|
|
9
|
+
* How the room beyond each child's own width is shared out. Left out,
|
|
10
|
+
* every child grows the same; 0 keeps a child at its own width.
|
|
11
|
+
*/
|
|
12
|
+
grow?: Array<number>;
|
|
13
|
+
|
|
14
|
+
gap?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class RowWidget extends WidgetBase {
|
|
18
|
+
|
|
19
|
+
#children: Array<Widget>;
|
|
20
|
+
|
|
21
|
+
#grow: Array<number>;
|
|
22
|
+
|
|
23
|
+
#gap: number;
|
|
24
|
+
|
|
25
|
+
#boxes: Array<Box | null> = [];
|
|
26
|
+
|
|
27
|
+
#captured: Widget | null = null;
|
|
28
|
+
|
|
29
|
+
#capturedBox: Box | null = null;
|
|
30
|
+
|
|
31
|
+
#mouse: Vector2 | undefined;
|
|
32
|
+
|
|
33
|
+
constructor(children: Array<Widget>, config?: RowWidgetConfig) {
|
|
34
|
+
super();
|
|
35
|
+
this.#children = children;
|
|
36
|
+
this.#grow = config?.grow ?? children.map(() => 1);
|
|
37
|
+
this.#gap = config?.gap ?? Theme.Widget.Gap;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
children(): ReadonlyArray<Widget> {
|
|
41
|
+
return this.#children;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Size(): Vector2 {
|
|
45
|
+
const shown = this.#shown();
|
|
46
|
+
let x = 0;
|
|
47
|
+
let y = 0;
|
|
48
|
+
for (const child of shown) {
|
|
49
|
+
const size = child.Size();
|
|
50
|
+
x += size.x;
|
|
51
|
+
y = Math.max(y, size.y);
|
|
52
|
+
}
|
|
53
|
+
x += Math.max(0, shown.length - 1) * this.#gap;
|
|
54
|
+
return { x, y };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Cursor(): CursorStyle {
|
|
58
|
+
const over = this.#captured ?? this.#childAt(this.#mouse);
|
|
59
|
+
return over?.Cursor?.() ?? CursorStyle.Default;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
Draw(ctx: CanvasRenderingContext2D, box: Box, scale: number, mousePosition: Vector2 | undefined): void {
|
|
63
|
+
this.#mouse = mousePosition;
|
|
64
|
+
super.Draw(ctx, box, scale, mousePosition);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
protected render(ctx: CanvasRenderingContext2D, box: Box, scale: number): void {
|
|
68
|
+
const shown = this.#shown();
|
|
69
|
+
const gap = this.#gap * scale;
|
|
70
|
+
const natural = shown.reduce((sum, child) => sum + (child.Size().x * scale), 0);
|
|
71
|
+
const extra = Math.max(0, box.Size.x - natural - (gap * Math.max(0, shown.length - 1)));
|
|
72
|
+
const totalGrow = shown.reduce((sum, child) => sum + this.#growOf(child), 0);
|
|
73
|
+
|
|
74
|
+
this.#boxes = this.#children.map(() => null);
|
|
75
|
+
let x = box.Position.x;
|
|
76
|
+
for (const child of shown) {
|
|
77
|
+
const size = child.Size();
|
|
78
|
+
const share = totalGrow > 0 ? extra * (this.#growOf(child) / totalGrow) : 0;
|
|
79
|
+
const childBox: Box = {
|
|
80
|
+
Position: { x, y: box.Position.y + ((box.Size.y - (size.y * scale)) / 2) },
|
|
81
|
+
Size: { x: (size.x * scale) + share, y: size.y * scale },
|
|
82
|
+
};
|
|
83
|
+
this.#boxes[this.#children.indexOf(child)] = childBox;
|
|
84
|
+
child.Draw(ctx, childBox, scale, this.#captured === null || this.#captured === child ? this.#mouse : undefined);
|
|
85
|
+
x += childBox.Size.x + gap;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
protected onPointerDown(pointer: WidgetPointer): void {
|
|
90
|
+
const child = this.#childAt(pointer.position);
|
|
91
|
+
if (child === null || child.Disabled?.()) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.#captured = child;
|
|
95
|
+
this.#capturedBox = this.#boxes[this.#children.indexOf(child)];
|
|
96
|
+
child.PointerDown?.(this.#forChild(pointer));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
protected onPointerMove(pointer: WidgetPointer): void {
|
|
100
|
+
this.#captured?.PointerMove?.(this.#forChild(pointer));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected onPointerUp(pointer: WidgetPointer): void {
|
|
104
|
+
const child = this.#captured;
|
|
105
|
+
const forChild = this.#forChild(pointer);
|
|
106
|
+
this.#captured = null;
|
|
107
|
+
this.#capturedBox = null;
|
|
108
|
+
child?.PointerUp?.(forChild);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
#forChild(pointer: WidgetPointer): WidgetPointer {
|
|
112
|
+
return {
|
|
113
|
+
position: pointer.position,
|
|
114
|
+
inside: this.#capturedBox !== null && InBox(this.#capturedBox, pointer.position),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#childAt(position: Vector2 | undefined): Widget | null {
|
|
119
|
+
if (position === undefined) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
for (let i = 0; i < this.#children.length; i++) {
|
|
123
|
+
const box = this.#boxes[i];
|
|
124
|
+
if (box && InBox(box, position)) {
|
|
125
|
+
return this.#children[i];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#shown(): Array<Widget> {
|
|
132
|
+
return this.#children.filter((child) => !child.Hidden?.());
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#growOf(child: Widget): number {
|
|
136
|
+
return this.#grow[this.#children.indexOf(child)] ?? 1;
|
|
137
|
+
}
|
|
138
|
+
}
|
package/src/widgets/slider.ts
CHANGED
|
@@ -1,216 +1,101 @@
|
|
|
1
|
-
import { Theme } from "../theme";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
export interface SliderWidgetConfig {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this.#
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this.#
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
this.#
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
node.addPropertyChangeListener(config.property, (oldVal, newVal) => {
|
|
103
|
-
this.SetValue(newVal);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
this.SetValue(startingWidgetValue(node, this.#nodeProperty, config?.value, 0));
|
|
108
|
-
|
|
109
|
-
// Setup change callback after we set the initial value to prevent the callback from being
|
|
110
|
-
this.#change = config?.change;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
SetValue(newValue: number): void {
|
|
114
|
-
const cleanedValue = Clamp(newValue, this.#min, this.#max);
|
|
115
|
-
if (Object.is(this.#value, cleanedValue)) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
this.#value = cleanedValue;
|
|
120
|
-
|
|
121
|
-
if (this.#nodeProperty) {
|
|
122
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
this.#text = this.#value.toFixed(3);
|
|
126
|
-
if (this.#change !== undefined) {
|
|
127
|
-
this.#change(this.#value);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
Size(): Vector2 {
|
|
132
|
-
return { "x": width, "y": height }
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
ClickStart(): void {
|
|
136
|
-
this.#clicking = true;
|
|
137
|
-
CopyVector2(this.#clickStartMousePosition, this.#lastMousePosition)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
ClickEnd(): void {
|
|
141
|
-
this.#clicking = false;
|
|
142
|
-
if (this.#release !== undefined) {
|
|
143
|
-
this.#release(this.#value);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
Draw(ctx: CanvasRenderingContext2D, position: Vector2, scale: number, mousePosition: Vector2 | undefined): Box {
|
|
148
|
-
const scaledWidth = width * scale;
|
|
149
|
-
const scaledHeight = height * scale;
|
|
150
|
-
|
|
151
|
-
const scaledBorderThickness = Theme.Widget.Border.Size * scale;
|
|
152
|
-
|
|
153
|
-
// Render background
|
|
154
|
-
ctx.fillStyle = this.#backgroundColor;
|
|
155
|
-
ctx.strokeStyle = this.#borderColor;
|
|
156
|
-
ctx.lineWidth = scaledBorderThickness;
|
|
157
|
-
ctx.beginPath();
|
|
158
|
-
ctx.roundRect(
|
|
159
|
-
position.x,
|
|
160
|
-
position.y,
|
|
161
|
-
scaledWidth,
|
|
162
|
-
scaledHeight,
|
|
163
|
-
Theme.Widget.Border.Radius * scale
|
|
164
|
-
);
|
|
165
|
-
ctx.fill();
|
|
166
|
-
ctx.stroke();
|
|
167
|
-
|
|
168
|
-
// Render Fill
|
|
169
|
-
const fill = Clamp01((this.#value - this.#min) / (this.#max - this.#min));
|
|
170
|
-
ctx.fillStyle = this.#fillColor;
|
|
171
|
-
ctx.beginPath();
|
|
172
|
-
ctx.roundRect(
|
|
173
|
-
position.x + (scaledBorderThickness / 2),
|
|
174
|
-
position.y + (scaledBorderThickness / 2),
|
|
175
|
-
(scaledWidth * fill) - scaledBorderThickness,
|
|
176
|
-
scaledHeight - scaledBorderThickness,
|
|
177
|
-
Theme.Widget.Border.Radius * scale
|
|
178
|
-
);
|
|
179
|
-
ctx.fill();
|
|
180
|
-
|
|
181
|
-
// Render Number
|
|
182
|
-
ctx.textAlign = TextAlign.Center;
|
|
183
|
-
ctx.textBaseline = TextBaseline.Middle;
|
|
184
|
-
this.#textStyle.setupStyle(ctx, scale);
|
|
185
|
-
ctx.fillText(
|
|
186
|
-
this.#text,
|
|
187
|
-
position.x + (scaledWidth / 2),
|
|
188
|
-
position.y + (scaledHeight / 2),
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
// Update value based on mouse position
|
|
192
|
-
if (mousePosition !== undefined) {
|
|
193
|
-
CopyVector2(this.#lastMousePosition, mousePosition);
|
|
194
|
-
if (this.#clicking) {
|
|
195
|
-
const min = position.x + (scaledBorderThickness / 2);
|
|
196
|
-
const max = min + scaledWidth - scaledBorderThickness;
|
|
197
|
-
const p = Clamp01((this.#lastMousePosition.x - min) / (max - min));
|
|
198
|
-
|
|
199
|
-
let value = (p * (this.#max - this.#min)) + this.#min;
|
|
200
|
-
if (this.#snapIncrement !== undefined) {
|
|
201
|
-
value = Math.round(value / this.#snapIncrement) * this.#snapIncrement;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
this.SetValue(value);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
Position: { x: position.x, y: position.y },
|
|
210
|
-
Size: {
|
|
211
|
-
x: scaledWidth,
|
|
212
|
-
y: scaledHeight
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
}
|
|
1
|
+
import { Theme } from "../theme";
|
|
2
|
+
import { Box } from "../types/box";
|
|
3
|
+
import { Clamp, Clamp01 } from "../utils/math";
|
|
4
|
+
import { FlowNode } from "../node";
|
|
5
|
+
import { CursorStyle } from "../styles/cursor";
|
|
6
|
+
import { drawFieldBox, drawFieldText } from "./field";
|
|
7
|
+
import { formatNumber } from "./number";
|
|
8
|
+
import { ValueWidget, ValueWidgetConfig } from "./value";
|
|
9
|
+
import { WidgetPointer } from "./widget";
|
|
10
|
+
|
|
11
|
+
export interface SliderWidgetConfig extends ValueWidgetConfig<number> {
|
|
12
|
+
min?: number;
|
|
13
|
+
|
|
14
|
+
max?: number;
|
|
15
|
+
|
|
16
|
+
snapIncrement?: number;
|
|
17
|
+
|
|
18
|
+
fillColor?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class SliderWidget extends ValueWidget<number> {
|
|
22
|
+
|
|
23
|
+
#min: number;
|
|
24
|
+
|
|
25
|
+
#max: number;
|
|
26
|
+
|
|
27
|
+
#snapIncrement?: number;
|
|
28
|
+
|
|
29
|
+
#fillColor: string | undefined;
|
|
30
|
+
|
|
31
|
+
#startValue = 0;
|
|
32
|
+
|
|
33
|
+
constructor(node: FlowNode, config?: SliderWidgetConfig) {
|
|
34
|
+
const min = config?.min ?? 0;
|
|
35
|
+
const max = config?.max ?? 1;
|
|
36
|
+
super(node, { ...config, value: config?.value === undefined ? undefined : Clamp(config.value, min, max) }, min);
|
|
37
|
+
this.#min = min;
|
|
38
|
+
this.#max = max;
|
|
39
|
+
this.#snapIncrement = config?.snapIncrement;
|
|
40
|
+
this.#fillColor = config?.fillColor;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
Cursor(): CursorStyle {
|
|
44
|
+
return CursorStyle.EWResize;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setValue(value: number): void {
|
|
48
|
+
super.setValue(Clamp(value, this.#min, this.#max));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected render(ctx: CanvasRenderingContext2D, box: Box, scale: number): void {
|
|
52
|
+
drawFieldBox(ctx, box, scale, { input: true, hovered: this.hovered() || this.pressed(), value: "" });
|
|
53
|
+
|
|
54
|
+
const border = Theme.Widget.Border.Size * scale;
|
|
55
|
+
const fill = Clamp01((this.value() - this.#min) / (this.#max - this.#min));
|
|
56
|
+
ctx.fillStyle = this.#fillColor ?? Theme.Widget.Slider.FillColor;
|
|
57
|
+
ctx.beginPath();
|
|
58
|
+
ctx.roundRect(
|
|
59
|
+
box.Position.x + (border / 2),
|
|
60
|
+
box.Position.y + (border / 2),
|
|
61
|
+
Math.max(0, (box.Size.x * fill) - border),
|
|
62
|
+
box.Size.y - border,
|
|
63
|
+
Theme.Widget.Border.Radius * scale
|
|
64
|
+
);
|
|
65
|
+
ctx.fill();
|
|
66
|
+
|
|
67
|
+
drawFieldText(ctx, box, scale, {
|
|
68
|
+
input: true,
|
|
69
|
+
hovered: false,
|
|
70
|
+
label: this.label(),
|
|
71
|
+
value: formatNumber(Number(this.value().toFixed(3))),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
protected onPointerDown(pointer: WidgetPointer): void {
|
|
76
|
+
this.#startValue = this.value();
|
|
77
|
+
this.input(this.#valueAt(pointer.position.x));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected onPointerMove(pointer: WidgetPointer): void {
|
|
81
|
+
this.input(this.#valueAt(pointer.position.x));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
protected onPointerUp(): void {
|
|
85
|
+
this.commit(this.value(), this.#startValue);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#valueAt(x: number): number {
|
|
89
|
+
const box = this.box();
|
|
90
|
+
const border = Theme.Widget.Border.Size * this.scale();
|
|
91
|
+
const min = box.Position.x + (border / 2);
|
|
92
|
+
const max = min + box.Size.x - border;
|
|
93
|
+
const p = Clamp01((x - min) / (max - min));
|
|
94
|
+
|
|
95
|
+
let value = (p * (this.#max - this.#min)) + this.#min;
|
|
96
|
+
if (this.#snapIncrement !== undefined) {
|
|
97
|
+
value = Math.round(value / this.#snapIncrement) * this.#snapIncrement;
|
|
98
|
+
}
|
|
99
|
+
return Clamp(value, this.#min, this.#max);
|
|
100
|
+
}
|
|
101
|
+
}
|
package/src/widgets/string.ts
CHANGED
|
@@ -1,130 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { Box
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
box: {
|
|
41
|
-
color: Theme.Widget.BackgroundColor,
|
|
42
|
-
border: {
|
|
43
|
-
size: Theme.Widget.Border.Size,
|
|
44
|
-
color: Theme.Widget.Border.Color,
|
|
45
|
-
},
|
|
46
|
-
radius: Theme.Widget.Border.Radius
|
|
47
|
-
},
|
|
48
|
-
text: { color: Theme.Widget.FontColor },
|
|
49
|
-
}));
|
|
50
|
-
|
|
51
|
-
this.#hoverStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.textBoxStyle, {
|
|
52
|
-
box: {
|
|
53
|
-
color: Theme.Widget.Hover.BackgroundColor,
|
|
54
|
-
border: {
|
|
55
|
-
size: Theme.Widget.Border.Size,
|
|
56
|
-
color: Theme.Widget.Border.Color,
|
|
57
|
-
},
|
|
58
|
-
radius: Theme.Widget.Border.Radius
|
|
59
|
-
},
|
|
60
|
-
text: { color: Theme.Widget.FontColor },
|
|
61
|
-
}));
|
|
62
|
-
|
|
63
|
-
this.Set(startingWidgetValue(node, this.#nodeProperty, config?.value, ""));
|
|
64
|
-
|
|
65
|
-
this.#callback = config?.callback;
|
|
66
|
-
|
|
67
|
-
if (this.#nodeProperty !== undefined) {
|
|
68
|
-
this.#node.addPropertyChangeListener(this.#nodeProperty, (oldVal, newVal) => {
|
|
69
|
-
this.Set(newVal);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
Size(): Vector2 {
|
|
75
|
-
return { "x": width, "y": height }
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
Set(value: string): void {
|
|
79
|
-
if (this.#value === value) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
this.#value = value;
|
|
83
|
-
|
|
84
|
-
if (this.#nodeProperty !== undefined) {
|
|
85
|
-
this.#node.setProperty(this.#nodeProperty, this.#value)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (this.#callback !== undefined) {
|
|
89
|
-
this.#callback(this.#value);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
ClickStart(): void {
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
ClickEnd(): void {
|
|
97
|
-
const popup = SetStringPopup({
|
|
98
|
-
title: "Set String",
|
|
99
|
-
startingValue: this.#value,
|
|
100
|
-
onUpdate: (value: string): void => {
|
|
101
|
-
this.Set(value);
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
popup.Show();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
Draw(ctx: CanvasRenderingContext2D, position: Vector2, scale: number, mousePosition: Vector2 | undefined): Box {
|
|
108
|
-
const box = {
|
|
109
|
-
Position: { x: 0, y: 0 },
|
|
110
|
-
Size: {
|
|
111
|
-
x: width * scale,
|
|
112
|
-
y: height * scale
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
CopyVector2(box.Position, position);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
let style: TextBoxStyle = this.#idleStyle;
|
|
119
|
-
|
|
120
|
-
if (mousePosition !== undefined) {
|
|
121
|
-
if (InBox(box, mousePosition)) {
|
|
122
|
-
style = this.#hoverStyle;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
style.DrawUnderline(ctx, box, scale, fitString(ctx, this.#value, box.Size.x - (20 * scale)))
|
|
127
|
-
|
|
128
|
-
return box;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
1
|
+
import { FlowNode } from "../node";
|
|
2
|
+
import { CursorStyle } from "../styles/cursor";
|
|
3
|
+
import { Box } from "../types/box";
|
|
4
|
+
import { drawField } from "./field";
|
|
5
|
+
import { editInPlace } from "./inplace";
|
|
6
|
+
import { ValueWidget, ValueWidgetConfig } from "./value";
|
|
7
|
+
|
|
8
|
+
export interface StringWidgetConfig extends ValueWidgetConfig<string> { }
|
|
9
|
+
|
|
10
|
+
export class StringWidget extends ValueWidget<string> {
|
|
11
|
+
|
|
12
|
+
constructor(node: FlowNode, config?: StringWidgetConfig) {
|
|
13
|
+
super(node, config, "");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
Cursor(): CursorStyle {
|
|
17
|
+
return CursorStyle.Text;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
protected render(ctx: CanvasRenderingContext2D, box: Box, scale: number): void {
|
|
21
|
+
drawField(ctx, box, scale, {
|
|
22
|
+
input: true,
|
|
23
|
+
hovered: this.hovered(),
|
|
24
|
+
label: this.label(),
|
|
25
|
+
value: this.value(),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected onClick(): void {
|
|
30
|
+
const before = this.value();
|
|
31
|
+
editInPlace({
|
|
32
|
+
canvas: this.canvas(),
|
|
33
|
+
box: this.box(),
|
|
34
|
+
scale: this.scale(),
|
|
35
|
+
value: before,
|
|
36
|
+
onCommit: (text) => this.commit(text, before),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|