@elicdavis/node-flow 0.1.20 → 0.2.1
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 +393 -226
- package/dist/index.d.ts +393 -226
- package/dist/index.js +1905 -1058
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1888 -1057
- 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 +19 -14
- 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 +136 -0
- package/src/widgets/number.ts +115 -154
- package/src/widgets/row.ts +138 -0
- package/src/widgets/slider.ts +101 -216
- package/src/widgets/string.ts +32 -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 +173 -6
- 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 +188 -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 +386 -0
- package/test/wire_follows_camera.test.ts +57 -0
package/dist/index.js
CHANGED
|
@@ -24,11 +24,15 @@ __export(index_exports, {
|
|
|
24
24
|
ColorWidget: () => ColorWidget,
|
|
25
25
|
Connection: () => Connection,
|
|
26
26
|
ContextMenuItemState: () => ContextMenuItemState,
|
|
27
|
+
CursorStyle: () => CursorStyle,
|
|
27
28
|
DefaultConnectionRenderer: () => DefaultConnectionRenderer,
|
|
29
|
+
DefaultTheme: () => DefaultTheme,
|
|
28
30
|
FlowNode: () => FlowNode,
|
|
29
31
|
FlowNote: () => FlowNote,
|
|
30
32
|
GlobalWidgetFactory: () => GlobalWidgetFactory,
|
|
33
|
+
GraphView: () => GraphView,
|
|
31
34
|
ImageWidget: () => ImageWidget,
|
|
35
|
+
InBox: () => InBox,
|
|
32
36
|
MessageType: () => MessageType,
|
|
33
37
|
Minimap: () => Minimap,
|
|
34
38
|
NodeFactory: () => NodeFactory,
|
|
@@ -38,25 +42,75 @@ __export(index_exports, {
|
|
|
38
42
|
Port: () => Port,
|
|
39
43
|
PortType: () => PortType,
|
|
40
44
|
Publisher: () => Publisher,
|
|
45
|
+
RowWidget: () => RowWidget,
|
|
41
46
|
SliderWidget: () => SliderWidget,
|
|
42
47
|
StringWidget: () => StringWidget,
|
|
43
48
|
TextWidget: () => TextWidget,
|
|
44
49
|
Theme: () => Theme,
|
|
45
50
|
ToggleWidget: () => ToggleWidget,
|
|
51
|
+
ValueWidget: () => ValueWidget,
|
|
52
|
+
WidgetBase: () => WidgetBase,
|
|
46
53
|
WidgetFactory: () => WidgetFactory,
|
|
54
|
+
drawField: () => drawField,
|
|
55
|
+
drawFieldBox: () => drawFieldBox,
|
|
56
|
+
drawFieldText: () => drawFieldText,
|
|
57
|
+
editInPlace: () => editInPlace,
|
|
58
|
+
formatNumber: () => formatNumber,
|
|
59
|
+
onThemeChange: () => onThemeChange,
|
|
47
60
|
portConfigTypes: () => portConfigTypes,
|
|
48
61
|
portsCompatible: () => portsCompatible,
|
|
49
62
|
renderContinuously: () => renderContinuously,
|
|
50
63
|
requestRender: () => requestRender,
|
|
51
|
-
|
|
64
|
+
setTheme: () => setTheme,
|
|
65
|
+
themeVersion: () => themeVersion,
|
|
66
|
+
typeSetsCompatible: () => typeSetsCompatible,
|
|
67
|
+
widgetFont: () => widgetFont
|
|
52
68
|
});
|
|
53
69
|
module.exports = __toCommonJS(index_exports);
|
|
54
70
|
|
|
71
|
+
// src/render_scheduler.ts
|
|
72
|
+
var subscribers = /* @__PURE__ */ new Set();
|
|
73
|
+
var frameQueued = false;
|
|
74
|
+
var continuous = 0;
|
|
75
|
+
var canSchedule = () => typeof requestAnimationFrame === "function";
|
|
76
|
+
function onRenderRequested(redraw) {
|
|
77
|
+
subscribers.add(redraw);
|
|
78
|
+
return () => {
|
|
79
|
+
subscribers.delete(redraw);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function requestRender() {
|
|
83
|
+
if (frameQueued || !canSchedule()) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
frameQueued = true;
|
|
87
|
+
requestAnimationFrame(() => {
|
|
88
|
+
frameQueued = false;
|
|
89
|
+
subscribers.forEach((redraw) => redraw());
|
|
90
|
+
if (continuous > 0) {
|
|
91
|
+
requestRender();
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function renderContinuously() {
|
|
96
|
+
continuous++;
|
|
97
|
+
requestRender();
|
|
98
|
+
let released = false;
|
|
99
|
+
return () => {
|
|
100
|
+
if (released) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
released = true;
|
|
104
|
+
continuous--;
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
55
108
|
// src/theme.ts
|
|
56
|
-
var
|
|
109
|
+
var defaults = {
|
|
57
110
|
FontFamily: "Courier New",
|
|
58
111
|
Graph: {
|
|
59
|
-
BackgroundColor: "#07212a"
|
|
112
|
+
BackgroundColor: "#07212a",
|
|
113
|
+
DotColor: "rgb(41, 54, 57)"
|
|
60
114
|
},
|
|
61
115
|
Node: {
|
|
62
116
|
FontColor: "#afb9bb",
|
|
@@ -73,6 +127,7 @@ var Theme = {
|
|
|
73
127
|
Grabbed: "white",
|
|
74
128
|
Selected: "#6e6e6e"
|
|
75
129
|
},
|
|
130
|
+
Bevel: null,
|
|
76
131
|
Port: {
|
|
77
132
|
FontColor: "#afb9bb"
|
|
78
133
|
},
|
|
@@ -90,15 +145,25 @@ var Theme = {
|
|
|
90
145
|
},
|
|
91
146
|
Widget: {
|
|
92
147
|
FontColor: "#afb9bb",
|
|
148
|
+
LabelColor: "#7f9497",
|
|
149
|
+
FontSize: 14,
|
|
150
|
+
Padding: 6,
|
|
151
|
+
Gap: 4,
|
|
93
152
|
BackgroundColor: "#0f313c",
|
|
94
153
|
Border: {
|
|
95
154
|
Color: "#154050",
|
|
96
155
|
Size: 2,
|
|
97
156
|
Radius: 2
|
|
98
157
|
},
|
|
158
|
+
Bevel: null,
|
|
99
159
|
Hover: {
|
|
100
160
|
BackgroundColor: "#195366"
|
|
101
161
|
},
|
|
162
|
+
Input: {
|
|
163
|
+
BackgroundColor: "#0f313c",
|
|
164
|
+
HoverBackgroundColor: "#195366",
|
|
165
|
+
FontColor: "#afb9bb"
|
|
166
|
+
},
|
|
102
167
|
Slider: {
|
|
103
168
|
FillColor: "#07212A"
|
|
104
169
|
},
|
|
@@ -111,8 +176,10 @@ var Theme = {
|
|
|
111
176
|
ContextMenu: {
|
|
112
177
|
BackgroundColor: "#07212A",
|
|
113
178
|
HighlightColor: "#205A6D",
|
|
179
|
+
HighlightFontColor: "#afb9bb",
|
|
114
180
|
FontColor: "#afb9bb",
|
|
115
|
-
DisabledFontColor: "#4a585c"
|
|
181
|
+
DisabledFontColor: "#4a585c",
|
|
182
|
+
BorderRadius: 5
|
|
116
183
|
},
|
|
117
184
|
Minimap: {
|
|
118
185
|
BackgroundColor: "rgba(10, 16, 18, 0.85)",
|
|
@@ -122,6 +189,13 @@ var Theme = {
|
|
|
122
189
|
NodeColor: "#6b7f85",
|
|
123
190
|
ViewportColor: "rgba(175, 185, 187, 0.25)"
|
|
124
191
|
},
|
|
192
|
+
Popup: {
|
|
193
|
+
BackgroundColor: "#000000",
|
|
194
|
+
FontColor: "#ffffff",
|
|
195
|
+
FontFamily: "Verdana",
|
|
196
|
+
BorderRadius: 8,
|
|
197
|
+
Backdrop: "#00000070"
|
|
198
|
+
},
|
|
125
199
|
Note: {
|
|
126
200
|
FontColor: "#afb9bb",
|
|
127
201
|
FontSize: 16,
|
|
@@ -140,11 +214,47 @@ var Theme = {
|
|
|
140
214
|
},
|
|
141
215
|
CodeBlock: {
|
|
142
216
|
BackgroundColor: "#0c2b35",
|
|
217
|
+
FontColor: "#afb9bb",
|
|
143
218
|
Padding: 16,
|
|
144
219
|
BorderRadius: 4
|
|
145
220
|
}
|
|
146
221
|
}
|
|
147
222
|
};
|
|
223
|
+
function clone(value) {
|
|
224
|
+
return JSON.parse(JSON.stringify(value));
|
|
225
|
+
}
|
|
226
|
+
function merge(target, source) {
|
|
227
|
+
if (source === void 0) {
|
|
228
|
+
return target;
|
|
229
|
+
}
|
|
230
|
+
if (source === null || typeof source !== "object" || target === null || typeof target !== "object") {
|
|
231
|
+
return source;
|
|
232
|
+
}
|
|
233
|
+
for (const key of Object.keys(source)) {
|
|
234
|
+
target[key] = merge(target[key], source[key]);
|
|
235
|
+
}
|
|
236
|
+
return target;
|
|
237
|
+
}
|
|
238
|
+
var DefaultTheme = Object.freeze(clone(defaults));
|
|
239
|
+
var Theme = clone(defaults);
|
|
240
|
+
var version = 0;
|
|
241
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
242
|
+
function setTheme(config) {
|
|
243
|
+
const next = merge(clone(defaults), config);
|
|
244
|
+
for (const key of Object.keys(next)) {
|
|
245
|
+
Theme[key] = next[key];
|
|
246
|
+
}
|
|
247
|
+
version++;
|
|
248
|
+
listeners.forEach((listener) => listener());
|
|
249
|
+
requestRender();
|
|
250
|
+
}
|
|
251
|
+
function themeVersion() {
|
|
252
|
+
return version;
|
|
253
|
+
}
|
|
254
|
+
function onThemeChange(listener) {
|
|
255
|
+
listeners.add(listener);
|
|
256
|
+
return () => listeners.delete(listener);
|
|
257
|
+
}
|
|
148
258
|
|
|
149
259
|
// src/styles/text.ts
|
|
150
260
|
var DefaultSize = 16;
|
|
@@ -167,19 +277,19 @@ var TextStyle = class {
|
|
|
167
277
|
constructor(config) {
|
|
168
278
|
this.#size = config?.size === void 0 ? DefaultSize : config.size;
|
|
169
279
|
this.#color = config?.color === void 0 ? DefaultColor : config.color;
|
|
170
|
-
this.#font = config?.font
|
|
280
|
+
this.#font = config?.font;
|
|
171
281
|
this.#weight = config?.weight === void 0 ? "" /* Normal */ : config.weight;
|
|
172
282
|
this.#fontStyle = config?.style === void 0 ? "" /* Normal */ : config.style;
|
|
173
283
|
}
|
|
174
284
|
setupStyle(ctx, scale) {
|
|
175
285
|
ctx.fillStyle = this.#color;
|
|
176
|
-
ctx.font = this.#fontStyle + " " + this.#weight + " " + this.#size * scale + "px " + this
|
|
286
|
+
ctx.font = this.#fontStyle + " " + this.#weight + " " + this.#size * scale + "px " + this.getFont();
|
|
177
287
|
}
|
|
178
288
|
getSize() {
|
|
179
289
|
return this.#size;
|
|
180
290
|
}
|
|
181
291
|
getFont() {
|
|
182
|
-
return this.#font;
|
|
292
|
+
return this.#font ?? Theme.FontFamily;
|
|
183
293
|
}
|
|
184
294
|
getWeight() {
|
|
185
295
|
return this.#weight;
|
|
@@ -550,7 +660,18 @@ var ContextMenu = class _ContextMenu {
|
|
|
550
660
|
}
|
|
551
661
|
#tempBox = { Position: Zero(), Size: Zero() };
|
|
552
662
|
#openSubMenu;
|
|
553
|
-
|
|
663
|
+
#bounds = null;
|
|
664
|
+
#contains(point) {
|
|
665
|
+
if (this.#bounds !== null && InBox(this.#bounds, point)) {
|
|
666
|
+
return true;
|
|
667
|
+
}
|
|
668
|
+
return this.#openSubMenu !== void 0 && this.#openSubMenu.#contains(point);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* A submenu is anchored to its parent's left edge and told the parent's
|
|
672
|
+
* width, so it can open on whichever side has room.
|
|
673
|
+
*/
|
|
674
|
+
render(ctx, anchor, graphScale, mousePosition, openRight, parentWidth = 0) {
|
|
554
675
|
const menuScale = 1.25;
|
|
555
676
|
const scaledEntryHeight = menuScale * contextEntryHeight;
|
|
556
677
|
const scaledEntryWidth = menuScale * 40 + this.#getMaxWidthForText(ctx, menuScale);
|
|
@@ -559,19 +680,35 @@ var ContextMenu = class _ContextMenu {
|
|
|
559
680
|
totalScaledHeight += this.#groups.At(i).height();
|
|
560
681
|
}
|
|
561
682
|
totalScaledHeight *= menuScale;
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
683
|
+
const canvasWidth = ctx.canvas.clientWidth;
|
|
684
|
+
const position = { x: anchor.x, y: anchor.y };
|
|
685
|
+
let opensRight = openRight;
|
|
686
|
+
if (parentWidth === 0) {
|
|
687
|
+
if (!openRight) {
|
|
688
|
+
position.x -= scaledEntryWidth;
|
|
689
|
+
}
|
|
690
|
+
if (openRight && position.x + scaledEntryWidth > canvasWidth) {
|
|
691
|
+
position.x = canvasWidth - scaledEntryWidth;
|
|
692
|
+
opensRight = false;
|
|
693
|
+
}
|
|
694
|
+
} else {
|
|
695
|
+
const rightOfParent = anchor.x + parentWidth;
|
|
696
|
+
const leftOfParent = anchor.x - scaledEntryWidth;
|
|
697
|
+
const fitsRight = rightOfParent + scaledEntryWidth <= canvasWidth;
|
|
698
|
+
const fitsLeft = leftOfParent >= 0;
|
|
699
|
+
opensRight = openRight ? fitsRight || !fitsLeft : !fitsLeft && fitsRight;
|
|
700
|
+
position.x = opensRight ? rightOfParent : leftOfParent;
|
|
701
|
+
position.x = Math.max(0, Math.min(position.x, canvasWidth - scaledEntryWidth));
|
|
566
702
|
}
|
|
567
703
|
if (position.y + totalScaledHeight > ctx.canvas.clientHeight) {
|
|
568
704
|
position.y = ctx.canvas.clientHeight - totalScaledHeight;
|
|
569
705
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
position.x
|
|
573
|
-
|
|
574
|
-
}
|
|
706
|
+
position.y = Math.max(0, position.y);
|
|
707
|
+
this.#bounds = {
|
|
708
|
+
Position: { x: position.x, y: position.y },
|
|
709
|
+
Size: { x: scaledEntryWidth, y: totalScaledHeight }
|
|
710
|
+
};
|
|
711
|
+
const pointer = mousePosition !== void 0 && this.#openSubMenu !== void 0 && this.#openSubMenu.#contains(mousePosition) ? void 0 : mousePosition;
|
|
575
712
|
this.#tempBox.Size.x = scaledEntryWidth;
|
|
576
713
|
this.#tempBox.Size.y = scaledEntryHeight;
|
|
577
714
|
CopyVector2(this.#tempBox.Position, position);
|
|
@@ -585,36 +722,36 @@ var ContextMenu = class _ContextMenu {
|
|
|
585
722
|
this.#tempBox.Position.y,
|
|
586
723
|
scaledEntryWidth,
|
|
587
724
|
totalScaledHeight,
|
|
588
|
-
|
|
725
|
+
Theme.ContextMenu.BorderRadius * menuScale
|
|
589
726
|
);
|
|
590
727
|
ctx.fill();
|
|
591
728
|
ctx.shadowBlur = 0;
|
|
592
729
|
let mouseIsOver = null;
|
|
593
|
-
|
|
730
|
+
if (pointer !== void 0) {
|
|
731
|
+
let row = 0;
|
|
732
|
+
for (let groupIndex = 0; groupIndex < this.#groups.Count(); groupIndex++) {
|
|
733
|
+
for (const entry of this.#groups.At(groupIndex).entries) {
|
|
734
|
+
this.#tempBox.Position.y = position.y + scaledEntryHeight * row;
|
|
735
|
+
if (InBox(this.#tempBox, pointer)) {
|
|
736
|
+
mouseIsOver = entry;
|
|
737
|
+
this.#openSubMenu = entry.subMenu;
|
|
738
|
+
this.#submenuPosition.x = position.x;
|
|
739
|
+
this.#submenuPosition.y = this.#tempBox.Position.y;
|
|
740
|
+
}
|
|
741
|
+
row++;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
594
745
|
let optionsRendered = 0;
|
|
595
746
|
for (let groupIndex = 0; groupIndex < this.#groups.Count(); groupIndex++) {
|
|
596
747
|
const group = this.#groups.At(groupIndex);
|
|
597
748
|
for (let entryIndex = 0; entryIndex < group.entries.length; entryIndex++) {
|
|
598
749
|
const entry = group.entries[entryIndex];
|
|
599
750
|
this.#tempBox.Position.y = position.y + scaledEntryHeight * optionsRendered;
|
|
600
|
-
|
|
601
|
-
if (mousePosition !== void 0 && InBox(this.#tempBox, mousePosition)) {
|
|
602
|
-
mouseIsOver = entry;
|
|
603
|
-
entryMousedOver = true;
|
|
604
|
-
if (entry.subMenu !== void 0) {
|
|
605
|
-
this.#openSubMenu = entry.subMenu;
|
|
606
|
-
this.#submenuPosition.x = position.x;
|
|
607
|
-
this.#submenuPosition.y = this.#tempBox.Position.y;
|
|
608
|
-
if (submenuOpenRight) {
|
|
609
|
-
this.#submenuPosition.x += scaledEntryWidth;
|
|
610
|
-
}
|
|
611
|
-
subOpenedThisFrame = true;
|
|
612
|
-
} else {
|
|
613
|
-
this.#openSubMenu = void 0;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
751
|
+
const entryMousedOver = entry === mouseIsOver;
|
|
616
752
|
const itemDisabled = entry.item !== void 0 && entry.item.state() === 1 /* Disabled */;
|
|
617
|
-
|
|
753
|
+
const highlighted = !itemDisabled && (entryMousedOver || this.#openSubMenu !== void 0 && entry.subMenu === this.#openSubMenu);
|
|
754
|
+
if (highlighted) {
|
|
618
755
|
ctx.fillStyle = Theme.ContextMenu.HighlightColor;
|
|
619
756
|
ctx.beginPath();
|
|
620
757
|
ctx.roundRect(
|
|
@@ -622,18 +759,20 @@ var ContextMenu = class _ContextMenu {
|
|
|
622
759
|
this.#tempBox.Position.y + scaledEntryHeight / 10,
|
|
623
760
|
scaledEntryWidth - scaledEntryHeight / 5,
|
|
624
761
|
scaledEntryHeight - scaledEntryHeight / 5,
|
|
625
|
-
|
|
762
|
+
Theme.ContextMenu.BorderRadius * menuScale
|
|
626
763
|
);
|
|
627
764
|
ctx.fill();
|
|
628
765
|
}
|
|
629
766
|
this.#textStyle.setupStyle(ctx, menuScale);
|
|
630
767
|
if (itemDisabled) {
|
|
631
768
|
ctx.fillStyle = Theme.ContextMenu.DisabledFontColor;
|
|
769
|
+
} else if (highlighted) {
|
|
770
|
+
ctx.fillStyle = Theme.ContextMenu.HighlightFontColor;
|
|
632
771
|
}
|
|
633
772
|
ctx.fillText(entry.text, position.x + scaledEntryHeight / 5, this.#tempBox.Position.y + scaledEntryHeight / 2);
|
|
634
773
|
if (entry.subMenu !== void 0) {
|
|
635
774
|
ctx.beginPath();
|
|
636
|
-
ctx.strokeStyle = Theme.ContextMenu.FontColor;
|
|
775
|
+
ctx.strokeStyle = highlighted ? Theme.ContextMenu.HighlightFontColor : Theme.ContextMenu.FontColor;
|
|
637
776
|
ctx.lineWidth = 1 * menuScale;
|
|
638
777
|
ctx.lineTo(position.x + scaledEntryWidth - scaledEntryHeight / 2.5, this.#tempBox.Position.y + scaledEntryHeight / 3);
|
|
639
778
|
ctx.lineTo(position.x + scaledEntryWidth - scaledEntryHeight / 4, this.#tempBox.Position.y + scaledEntryHeight / 2);
|
|
@@ -654,7 +793,7 @@ var ContextMenu = class _ContextMenu {
|
|
|
654
793
|
}
|
|
655
794
|
}
|
|
656
795
|
if (this.#openSubMenu !== void 0) {
|
|
657
|
-
const mouseOverSub = this.#openSubMenu.render(ctx, this.#submenuPosition, menuScale, mousePosition,
|
|
796
|
+
const mouseOverSub = this.#openSubMenu.render(ctx, this.#submenuPosition, menuScale, mousePosition, opensRight, scaledEntryWidth);
|
|
658
797
|
if (mouseOverSub !== null) {
|
|
659
798
|
mouseIsOver = mouseOverSub;
|
|
660
799
|
}
|
|
@@ -663,43 +802,6 @@ var ContextMenu = class _ContextMenu {
|
|
|
663
802
|
}
|
|
664
803
|
};
|
|
665
804
|
|
|
666
|
-
// src/render_scheduler.ts
|
|
667
|
-
var subscribers = /* @__PURE__ */ new Set();
|
|
668
|
-
var frameQueued = false;
|
|
669
|
-
var continuous = 0;
|
|
670
|
-
var canSchedule = () => typeof requestAnimationFrame === "function";
|
|
671
|
-
function onRenderRequested(redraw) {
|
|
672
|
-
subscribers.add(redraw);
|
|
673
|
-
return () => {
|
|
674
|
-
subscribers.delete(redraw);
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
function requestRender() {
|
|
678
|
-
if (frameQueued || !canSchedule()) {
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
frameQueued = true;
|
|
682
|
-
requestAnimationFrame(() => {
|
|
683
|
-
frameQueued = false;
|
|
684
|
-
subscribers.forEach((redraw) => redraw());
|
|
685
|
-
if (continuous > 0) {
|
|
686
|
-
requestRender();
|
|
687
|
-
}
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
function renderContinuously() {
|
|
691
|
-
continuous++;
|
|
692
|
-
requestRender();
|
|
693
|
-
let released = false;
|
|
694
|
-
return () => {
|
|
695
|
-
if (released) {
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
released = true;
|
|
699
|
-
continuous--;
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
|
|
703
805
|
// src/input.ts
|
|
704
806
|
var LONG_PRESS_MS = 500;
|
|
705
807
|
var LONG_PRESS_SLOP = 12;
|
|
@@ -1284,6 +1386,7 @@ function splitStringIntoLines(ctx, str, maxWidth) {
|
|
|
1284
1386
|
// src/types/text.ts
|
|
1285
1387
|
var Text = class _Text {
|
|
1286
1388
|
#measured;
|
|
1389
|
+
#measuredForTheme = -1;
|
|
1287
1390
|
#size;
|
|
1288
1391
|
#style;
|
|
1289
1392
|
#value;
|
|
@@ -1363,9 +1466,10 @@ var Text = class _Text {
|
|
|
1363
1466
|
return results;
|
|
1364
1467
|
}
|
|
1365
1468
|
#measure(ctx) {
|
|
1366
|
-
if (this.#measured) {
|
|
1469
|
+
if (this.#measured && this.#measuredForTheme === themeVersion()) {
|
|
1367
1470
|
return;
|
|
1368
1471
|
}
|
|
1472
|
+
this.#measuredForTheme = themeVersion();
|
|
1369
1473
|
this.#style.setupStyle(ctx, 1);
|
|
1370
1474
|
if (this.#maxWidth == -1) {
|
|
1371
1475
|
const measurements = ctx.measureText(this.#value);
|
|
@@ -1441,7 +1545,7 @@ function RgbToHex(c) {
|
|
|
1441
1545
|
return "#" + componentToHex(Math.round(c.r * 255)) + componentToHex(Math.round(c.g * 255)) + componentToHex(Math.round(c.b * 255));
|
|
1442
1546
|
}
|
|
1443
1547
|
function HexToColor(hex) {
|
|
1444
|
-
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})
|
|
1548
|
+
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})(?:[a-f\d]{2})?$/i.exec(hex);
|
|
1445
1549
|
return result ? {
|
|
1446
1550
|
r: parseInt(result[1], 16) / 255,
|
|
1447
1551
|
g: parseInt(result[2], 16) / 255,
|
|
@@ -1531,6 +1635,21 @@ function portConfigTypes(config) {
|
|
|
1531
1635
|
}
|
|
1532
1636
|
return [config.type === void 0 ? "" : config.type];
|
|
1533
1637
|
}
|
|
1638
|
+
var ARRAY_SLOT_PITCH = 12;
|
|
1639
|
+
function arrayInsertIndex(box, count, y) {
|
|
1640
|
+
if (count === 0) {
|
|
1641
|
+
return 0;
|
|
1642
|
+
}
|
|
1643
|
+
const index = Math.round((y - box.Position.y) / (box.Size.y / count));
|
|
1644
|
+
return Math.min(count, Math.max(0, index));
|
|
1645
|
+
}
|
|
1646
|
+
function arraySlotCenter(box, count, index) {
|
|
1647
|
+
const slots = Math.max(1, count);
|
|
1648
|
+
return {
|
|
1649
|
+
x: box.Position.x + box.Size.x / 2,
|
|
1650
|
+
y: box.Position.y + (index + 0.5) * box.Size.y / slots
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1534
1653
|
function fallbackColor(type, s) {
|
|
1535
1654
|
let value = 0;
|
|
1536
1655
|
for (var i = 0; i < type.length; i++) {
|
|
@@ -1555,6 +1674,7 @@ var Port = class {
|
|
|
1555
1674
|
#acceptedTypes;
|
|
1556
1675
|
#description;
|
|
1557
1676
|
#array;
|
|
1677
|
+
#hidden;
|
|
1558
1678
|
#configuredEmptyFill;
|
|
1559
1679
|
#configuredFilledFill;
|
|
1560
1680
|
#dataTypePopupElement;
|
|
@@ -1570,6 +1690,7 @@ var Port = class {
|
|
|
1570
1690
|
this.#acceptedTypes = config?.acceptedTypes === void 0 ? [] : config.acceptedTypes;
|
|
1571
1691
|
this.#description = config?.description === void 0 ? "" : config.description;
|
|
1572
1692
|
this.#array = config?.array === true;
|
|
1693
|
+
this.#hidden = config?.hidden === true;
|
|
1573
1694
|
this.#configuredEmptyFill = config?.emptyStyle?.fillColor;
|
|
1574
1695
|
this.#configuredFilledFill = config?.filledStyle?.fillColor;
|
|
1575
1696
|
this.#emptyStyle = {
|
|
@@ -1668,9 +1789,11 @@ var Port = class {
|
|
|
1668
1789
|
this.setDataType(type);
|
|
1669
1790
|
this.#refreshDataType();
|
|
1670
1791
|
}
|
|
1671
|
-
addConnection(connection) {
|
|
1672
|
-
const c = this.#connections.length;
|
|
1673
|
-
this
|
|
1792
|
+
addConnection(connection, index) {
|
|
1793
|
+
const c = index === void 0 ? this.#connections.length : Math.min(Math.max(index, 0), this.#connections.length);
|
|
1794
|
+
const was = this.hidden();
|
|
1795
|
+
this.#connections.splice(c, 0, connection);
|
|
1796
|
+
this.#visibilityMayHaveChanged(was);
|
|
1674
1797
|
for (let i = 0; i < this.#onConnectionAdded.length; i++) {
|
|
1675
1798
|
this.#onConnectionAdded[i](connection, c, this, this.#portType, this.#node);
|
|
1676
1799
|
}
|
|
@@ -1691,6 +1814,32 @@ var Port = class {
|
|
|
1691
1814
|
connections() {
|
|
1692
1815
|
return this.#connections;
|
|
1693
1816
|
}
|
|
1817
|
+
/**
|
|
1818
|
+
* How many wires the port has room for: one per connection on an array
|
|
1819
|
+
* input, so each can be told apart and picked up on its own.
|
|
1820
|
+
*/
|
|
1821
|
+
slots() {
|
|
1822
|
+
if (this.#portType !== "INPUTARRAY" /* InputArray */) {
|
|
1823
|
+
return 1;
|
|
1824
|
+
}
|
|
1825
|
+
return Math.max(1, this.#connections.length);
|
|
1826
|
+
}
|
|
1827
|
+
setHidden(hidden) {
|
|
1828
|
+
const was = this.hidden();
|
|
1829
|
+
this.#hidden = hidden;
|
|
1830
|
+
this.#visibilityMayHaveChanged(was);
|
|
1831
|
+
}
|
|
1832
|
+
#visibilityMayHaveChanged(was) {
|
|
1833
|
+
if (was !== this.hidden() || this.#portType === "INPUTARRAY" /* InputArray */) {
|
|
1834
|
+
this.#node?.invalidateLayout();
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
/**
|
|
1838
|
+
* A port asked to hide still shows while anything is connected to it.
|
|
1839
|
+
*/
|
|
1840
|
+
hidden() {
|
|
1841
|
+
return this.#hidden && this.#connections.length === 0;
|
|
1842
|
+
}
|
|
1694
1843
|
addConnectionRemovedListener(callback) {
|
|
1695
1844
|
if (callback === void 0) {
|
|
1696
1845
|
return;
|
|
@@ -1700,7 +1849,9 @@ var Port = class {
|
|
|
1700
1849
|
clearConnection(connection) {
|
|
1701
1850
|
const index = this.#connections.indexOf(connection);
|
|
1702
1851
|
if (index > -1) {
|
|
1852
|
+
const was = this.hidden();
|
|
1703
1853
|
this.#connections.splice(index, 1);
|
|
1854
|
+
this.#visibilityMayHaveChanged(was);
|
|
1704
1855
|
for (let i = 0; i < this.#onConnectionRemoved.length; i++) {
|
|
1705
1856
|
this.#onConnectionRemoved[i](connection, index, this, this.#portType, this.#node);
|
|
1706
1857
|
}
|
|
@@ -1759,20 +1910,31 @@ var Port = class {
|
|
|
1759
1910
|
this.#dataTypePopupElement.render(ctx, { x: xPos - size.x / 2, y: yPos }, 1, size);
|
|
1760
1911
|
});
|
|
1761
1912
|
}
|
|
1913
|
+
const slots = this.slots();
|
|
1914
|
+
const height2 = slots === 1 ? scaledRadius * 2 : slots * ARRAY_SLOT_PITCH * camera.zoom;
|
|
1762
1915
|
this.#box.Position.x = position.x - scaledRadius;
|
|
1763
|
-
this.#box.Position.y = position.y -
|
|
1916
|
+
this.#box.Position.y = position.y - height2 / 2;
|
|
1764
1917
|
this.#box.Size.x = scaledRadius * 2;
|
|
1765
|
-
this.#box.Size.y =
|
|
1918
|
+
this.#box.Size.y = height2;
|
|
1766
1919
|
ctx.strokeStyle = style.borderColor;
|
|
1767
1920
|
ctx.fillStyle = style.fillColor;
|
|
1768
1921
|
ctx.beginPath();
|
|
1769
1922
|
if (this.#portType === "INPUTARRAY" /* InputArray */) {
|
|
1770
|
-
ctx.
|
|
1923
|
+
ctx.roundRect(this.#box.Position.x, this.#box.Position.y, this.#box.Size.x, this.#box.Size.y, slots === 1 ? 0 : scaledRadius);
|
|
1771
1924
|
} else {
|
|
1772
1925
|
ctx.arc(position.x, position.y, scaledRadius, 0, 2 * Math.PI);
|
|
1773
1926
|
}
|
|
1774
1927
|
ctx.fill();
|
|
1775
1928
|
ctx.stroke();
|
|
1929
|
+
if (slots > 1) {
|
|
1930
|
+
ctx.fillStyle = style.borderColor;
|
|
1931
|
+
for (let i = 0; i < slots; i++) {
|
|
1932
|
+
const slot = arraySlotCenter(this.#box, slots, i);
|
|
1933
|
+
ctx.beginPath();
|
|
1934
|
+
ctx.arc(slot.x, slot.y, scaledRadius * 0.4, 0, 2 * Math.PI);
|
|
1935
|
+
ctx.fill();
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1776
1938
|
return this.#box;
|
|
1777
1939
|
}
|
|
1778
1940
|
};
|
|
@@ -1888,142 +2050,330 @@ var BoxStyle = class {
|
|
|
1888
2050
|
}
|
|
1889
2051
|
};
|
|
1890
2052
|
|
|
1891
|
-
// src/
|
|
1892
|
-
var
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
2053
|
+
// src/styles/cursor.ts
|
|
2054
|
+
var CursorStyle = /* @__PURE__ */ ((CursorStyle2) => {
|
|
2055
|
+
CursorStyle2["Default"] = "default";
|
|
2056
|
+
CursorStyle2["Pointer"] = "pointer";
|
|
2057
|
+
CursorStyle2["Grab"] = "grab";
|
|
2058
|
+
CursorStyle2["Grabbing"] = "grabbing";
|
|
2059
|
+
CursorStyle2["Wait"] = "wait";
|
|
2060
|
+
CursorStyle2["Help"] = "help";
|
|
2061
|
+
CursorStyle2["Crosshair"] = "crosshair";
|
|
2062
|
+
CursorStyle2["Cell"] = "cell";
|
|
2063
|
+
CursorStyle2["Text"] = "text";
|
|
2064
|
+
CursorStyle2["Move"] = "move";
|
|
2065
|
+
CursorStyle2["NotAllowed"] = "not-allowed";
|
|
2066
|
+
CursorStyle2["ZoomIn"] = "zoom-in";
|
|
2067
|
+
CursorStyle2["ZoomOut"] = "zoom-out";
|
|
2068
|
+
CursorStyle2["None"] = "none";
|
|
2069
|
+
CursorStyle2["ContextMenu"] = "context-menu";
|
|
2070
|
+
CursorStyle2["EWResize"] = "ew-resize";
|
|
2071
|
+
return CursorStyle2;
|
|
2072
|
+
})(CursorStyle || {});
|
|
2073
|
+
|
|
2074
|
+
// src/styles/bevel.ts
|
|
2075
|
+
function drawBevel(ctx, box, scale, colors, sunken) {
|
|
2076
|
+
const width2 = Math.max(1, 2 * scale);
|
|
2077
|
+
const half = width2 / 2;
|
|
2078
|
+
const left = box.Position.x + half;
|
|
2079
|
+
const top = box.Position.y + half;
|
|
2080
|
+
const right = box.Position.x + box.Size.x - half;
|
|
2081
|
+
const bottom = box.Position.y + box.Size.y - half;
|
|
2082
|
+
ctx.save();
|
|
2083
|
+
ctx.lineWidth = width2;
|
|
2084
|
+
ctx.lineCap = "square";
|
|
2085
|
+
ctx.strokeStyle = sunken ? colors.Dark : colors.Light;
|
|
2086
|
+
ctx.beginPath();
|
|
2087
|
+
ctx.moveTo(left, bottom);
|
|
2088
|
+
ctx.lineTo(left, top);
|
|
2089
|
+
ctx.lineTo(right, top);
|
|
2090
|
+
ctx.stroke();
|
|
2091
|
+
ctx.strokeStyle = sunken ? colors.Light : colors.Dark;
|
|
2092
|
+
ctx.beginPath();
|
|
2093
|
+
ctx.moveTo(right, top);
|
|
2094
|
+
ctx.lineTo(right, bottom);
|
|
2095
|
+
ctx.lineTo(left, bottom);
|
|
2096
|
+
ctx.stroke();
|
|
2097
|
+
ctx.restore();
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
// src/widgets/field.ts
|
|
2101
|
+
function widgetFont(scale) {
|
|
2102
|
+
return `${Theme.Widget.FontSize * scale}px ${Theme.FontFamily}`;
|
|
2103
|
+
}
|
|
2104
|
+
function fieldBackground(look) {
|
|
2105
|
+
if (look.background !== void 0) {
|
|
2106
|
+
return look.background;
|
|
1921
2107
|
}
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
if (this.#onClose !== void 0) {
|
|
1925
|
-
this.#onClose(option);
|
|
1926
|
-
}
|
|
2108
|
+
if (look.input) {
|
|
2109
|
+
return look.hovered ? Theme.Widget.Input.HoverBackgroundColor : Theme.Widget.Input.BackgroundColor;
|
|
1927
2110
|
}
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
this.#popup.style.cssText = "position:absolute;top:0;width:100%;display:flex;height:100%;justify-content:center;align-items:center;background-color:#00000070;";
|
|
1931
|
-
document.body.appendChild(this.#popup);
|
|
1932
|
-
const container = document.createElement("div");
|
|
1933
|
-
container.style.cssText = "background-color:#000;color:white;font-family:Verdana;border-radius:8px;padding:16px;";
|
|
1934
|
-
this.#popup.appendChild(container);
|
|
1935
|
-
const title = document.createElement("h2");
|
|
1936
|
-
title.textContent = this.#title;
|
|
1937
|
-
title.style.cssText = "margin-top:0;margin-bottom:16px;";
|
|
1938
|
-
container.appendChild(title);
|
|
1939
|
-
if (this.#content !== void 0) {
|
|
1940
|
-
container.appendChild(this.#content());
|
|
1941
|
-
}
|
|
1942
|
-
const buttonContainer = document.createElement("div");
|
|
1943
|
-
buttonContainer.style.cssText = "margin-top:16px;justify-content:space-around;align-items:center;flex-direction:row;display:flex;";
|
|
1944
|
-
container.appendChild(buttonContainer);
|
|
1945
|
-
for (let i = 0; i < this.#options.length; i++) {
|
|
1946
|
-
const button = document.createElement("button");
|
|
1947
|
-
button.style.cssText = this.#buttonCSS;
|
|
1948
|
-
button.textContent = this.#options[i];
|
|
1949
|
-
button.onclick = () => this.#close(this.#options[i]);
|
|
1950
|
-
buttonContainer.appendChild(button);
|
|
1951
|
-
}
|
|
1952
|
-
this.#keyHandler = (e) => {
|
|
1953
|
-
if (e.key === "Enter") {
|
|
1954
|
-
e.preventDefault();
|
|
1955
|
-
this.#close(this.#options.length > 0 ? this.#options[0] : null);
|
|
1956
|
-
} else if (e.key === "Escape") {
|
|
1957
|
-
e.preventDefault();
|
|
1958
|
-
this.#close(null);
|
|
1959
|
-
}
|
|
1960
|
-
e.stopPropagation();
|
|
1961
|
-
};
|
|
1962
|
-
document.addEventListener("keydown", this.#keyHandler, true);
|
|
2111
|
+
if (look.pressed) {
|
|
2112
|
+
return Theme.Widget.Button.Click.BackgroundColor;
|
|
1963
2113
|
}
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
// src/styles/textBox.ts
|
|
1967
|
-
function TextBoxStyleWithFallback(input, fallback) {
|
|
1968
|
-
return {
|
|
1969
|
-
box: BoxStyleWithFallback(input?.box, fallback?.box),
|
|
1970
|
-
text: TextStyleFallback(input?.text, fallback?.text),
|
|
1971
|
-
textAlign: input?.textAlign === void 0 ? fallback?.textAlign : input?.textAlign,
|
|
1972
|
-
textBaseline: input?.textBaseline === void 0 ? fallback?.textBaseline : input?.textBaseline
|
|
1973
|
-
};
|
|
2114
|
+
return look.hovered ? Theme.Widget.Hover.BackgroundColor : Theme.Widget.BackgroundColor;
|
|
1974
2115
|
}
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
this.#textBaseline = config?.textBaseline === void 0 ? "middle" /* Middle */ : config.textBaseline;
|
|
1985
|
-
}
|
|
1986
|
-
Draw(ctx, box, scale, text) {
|
|
1987
|
-
this.#box.Draw(ctx, box, scale);
|
|
1988
|
-
ctx.textAlign = this.#textAlign;
|
|
1989
|
-
ctx.textBaseline = this.#textBaseline;
|
|
1990
|
-
this.#text.setupStyle(ctx, scale);
|
|
1991
|
-
ctx.fillText(
|
|
1992
|
-
text,
|
|
1993
|
-
box.Position.x + box.Size.x / 2,
|
|
1994
|
-
box.Position.y + box.Size.y / 2
|
|
1995
|
-
);
|
|
2116
|
+
function drawFieldBox(ctx, box, scale, look) {
|
|
2117
|
+
ctx.fillStyle = fieldBackground(look);
|
|
2118
|
+
ctx.beginPath();
|
|
2119
|
+
ctx.roundRect(box.Position.x, box.Position.y, box.Size.x, box.Size.y, Theme.Widget.Border.Radius * scale);
|
|
2120
|
+
ctx.fill();
|
|
2121
|
+
if (Theme.Widget.Border.Size > 0) {
|
|
2122
|
+
ctx.strokeStyle = look.border ?? Theme.Widget.Border.Color;
|
|
2123
|
+
ctx.lineWidth = Theme.Widget.Border.Size * scale;
|
|
2124
|
+
ctx.stroke();
|
|
1996
2125
|
}
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2126
|
+
if (Theme.Widget.Bevel !== null) {
|
|
2127
|
+
drawBevel(ctx, box, scale, Theme.Widget.Bevel, look.input === true || look.pressed === true);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
function drawField(ctx, box, scale, look) {
|
|
2131
|
+
drawFieldBox(ctx, box, scale, look);
|
|
2132
|
+
drawFieldText(ctx, box, scale, look);
|
|
2133
|
+
}
|
|
2134
|
+
function drawFieldText(ctx, box, scale, look) {
|
|
2135
|
+
const padding = Theme.Widget.Padding * scale;
|
|
2136
|
+
const middle = box.Position.y + box.Size.y / 2;
|
|
2137
|
+
const textColor = look.textColor ?? (look.input ? Theme.Widget.Input.FontColor : Theme.Widget.FontColor);
|
|
2138
|
+
ctx.font = widgetFont(scale);
|
|
2139
|
+
ctx.textBaseline = "middle";
|
|
2140
|
+
if (look.label === void 0 || look.label === "") {
|
|
2141
|
+
ctx.textAlign = "center";
|
|
2142
|
+
ctx.fillStyle = textColor;
|
|
2002
2143
|
ctx.fillText(
|
|
2003
|
-
|
|
2144
|
+
fitString(ctx, look.value, box.Size.x - padding * 2),
|
|
2004
2145
|
box.Position.x + box.Size.x / 2,
|
|
2005
|
-
|
|
2146
|
+
middle
|
|
2006
2147
|
);
|
|
2148
|
+
return;
|
|
2007
2149
|
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2150
|
+
const room = box.Size.x - padding * 2;
|
|
2151
|
+
const label = fitString(ctx, look.label, room / 2);
|
|
2152
|
+
const labelWidth = ctx.measureText(label).width;
|
|
2153
|
+
ctx.textAlign = "left";
|
|
2154
|
+
ctx.fillStyle = look.textColor ?? Theme.Widget.LabelColor;
|
|
2155
|
+
ctx.fillText(label, box.Position.x + padding, middle);
|
|
2156
|
+
ctx.textAlign = "right";
|
|
2157
|
+
ctx.fillStyle = textColor;
|
|
2158
|
+
ctx.fillText(
|
|
2159
|
+
fitString(ctx, look.value, room - labelWidth - padding),
|
|
2160
|
+
box.Position.x + box.Size.x - padding,
|
|
2161
|
+
middle
|
|
2162
|
+
);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
// src/widgets/inplace.ts
|
|
2166
|
+
var open = /* @__PURE__ */ new Set();
|
|
2167
|
+
function placeEditsDuring(canvas, draw) {
|
|
2168
|
+
open.forEach((edit) => {
|
|
2169
|
+
if (edit.canvas === canvas) {
|
|
2170
|
+
edit.placed = false;
|
|
2171
|
+
}
|
|
2172
|
+
});
|
|
2173
|
+
draw();
|
|
2174
|
+
open.forEach((edit) => {
|
|
2175
|
+
if (edit.canvas === canvas && !edit.placed) {
|
|
2176
|
+
edit.input.style.opacity = "0";
|
|
2177
|
+
edit.input.style.pointerEvents = "none";
|
|
2178
|
+
}
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
function editInPlace(config) {
|
|
2182
|
+
const canvas = config.canvas;
|
|
2183
|
+
if (canvas === null) {
|
|
2184
|
+
return null;
|
|
2016
2185
|
}
|
|
2017
|
-
|
|
2186
|
+
const input = document.createElement("input");
|
|
2187
|
+
input.type = "text";
|
|
2188
|
+
input.value = config.value;
|
|
2189
|
+
const edit = { canvas, input, placed: true };
|
|
2190
|
+
const place = (box, scale) => {
|
|
2191
|
+
const rect = canvas.getBoundingClientRect();
|
|
2192
|
+
const sx = canvas.width > 0 ? rect.width / canvas.width : 1;
|
|
2193
|
+
const sy = canvas.height > 0 ? rect.height / canvas.height : 1;
|
|
2194
|
+
const left = rect.left + box.Position.x * sx;
|
|
2195
|
+
const top = rect.top + box.Position.y * sy;
|
|
2196
|
+
const width2 = box.Size.x * sx;
|
|
2197
|
+
const height2 = box.Size.y * sy;
|
|
2198
|
+
const clip = [
|
|
2199
|
+
Math.max(0, rect.top - top),
|
|
2200
|
+
Math.max(0, left + width2 - (rect.left + rect.width)),
|
|
2201
|
+
Math.max(0, top + height2 - (rect.top + rect.height)),
|
|
2202
|
+
Math.max(0, rect.left - left)
|
|
2203
|
+
];
|
|
2204
|
+
edit.placed = true;
|
|
2205
|
+
input.style.cssText = [
|
|
2206
|
+
"position: fixed",
|
|
2207
|
+
`left: ${left}px`,
|
|
2208
|
+
`top: ${top}px`,
|
|
2209
|
+
`width: ${width2}px`,
|
|
2210
|
+
`height: ${height2}px`,
|
|
2211
|
+
`clip-path: inset(${clip.map((c) => `${c}px`).join(" ")})`,
|
|
2212
|
+
"box-sizing: border-box",
|
|
2213
|
+
"margin: 0",
|
|
2214
|
+
`padding: 0 ${Theme.Widget.Padding * scale}px`,
|
|
2215
|
+
`font: ${Theme.Widget.FontSize * scale * sy}px ${Theme.FontFamily}`,
|
|
2216
|
+
`color: ${Theme.Widget.FontColor}`,
|
|
2217
|
+
`background: ${Theme.Widget.Hover.BackgroundColor}`,
|
|
2218
|
+
`border: ${Theme.Widget.Border.Size * scale}px solid ${Theme.Widget.Border.Color}`,
|
|
2219
|
+
`border-radius: ${Theme.Widget.Border.Radius * scale}px`,
|
|
2220
|
+
"outline: none",
|
|
2221
|
+
"z-index: 1000"
|
|
2222
|
+
].join(";");
|
|
2223
|
+
};
|
|
2224
|
+
place(config.box, config.scale);
|
|
2225
|
+
let done = false;
|
|
2226
|
+
const finish = (keep) => {
|
|
2227
|
+
if (done) {
|
|
2228
|
+
return;
|
|
2229
|
+
}
|
|
2230
|
+
done = true;
|
|
2231
|
+
open.delete(edit);
|
|
2232
|
+
input.remove();
|
|
2233
|
+
if (keep) {
|
|
2234
|
+
config.onCommit(input.value);
|
|
2235
|
+
}
|
|
2236
|
+
requestRender();
|
|
2237
|
+
};
|
|
2238
|
+
input.addEventListener("keydown", (event) => {
|
|
2239
|
+
event.stopPropagation();
|
|
2240
|
+
if (event.key === "Enter") {
|
|
2241
|
+
event.preventDefault();
|
|
2242
|
+
finish(true);
|
|
2243
|
+
} else if (event.key === "Escape") {
|
|
2244
|
+
event.preventDefault();
|
|
2245
|
+
finish(false);
|
|
2246
|
+
}
|
|
2247
|
+
});
|
|
2248
|
+
input.addEventListener("blur", () => finish(true));
|
|
2249
|
+
open.add(edit);
|
|
2250
|
+
document.body.appendChild(input);
|
|
2251
|
+
input.focus();
|
|
2252
|
+
input.select();
|
|
2253
|
+
return { input, place };
|
|
2254
|
+
}
|
|
2018
2255
|
|
|
2019
2256
|
// src/widgets/widget.ts
|
|
2020
2257
|
var width = 150;
|
|
2021
2258
|
var height = 25;
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2259
|
+
var WidgetBase = class {
|
|
2260
|
+
#hidden = false;
|
|
2261
|
+
#disabled = false;
|
|
2262
|
+
#hovered = false;
|
|
2263
|
+
#pressed = false;
|
|
2264
|
+
#box = { Position: { x: 0, y: 0 }, Size: { x: 0, y: 0 } };
|
|
2265
|
+
#canvas = null;
|
|
2266
|
+
#scale = 1;
|
|
2267
|
+
#editing = null;
|
|
2268
|
+
Draw(ctx, box, scale, mousePosition) {
|
|
2269
|
+
this.#box = { Position: { x: box.Position.x, y: box.Position.y }, Size: { x: box.Size.x, y: box.Size.y } };
|
|
2270
|
+
this.#canvas = ctx.canvas ?? null;
|
|
2271
|
+
this.#scale = scale;
|
|
2272
|
+
this.#hovered = !this.#disabled && mousePosition !== void 0 && InBox(box, mousePosition);
|
|
2273
|
+
if (this.#editing !== null) {
|
|
2274
|
+
if (this.#editing.input.isConnected) {
|
|
2275
|
+
this.#editing.place(this.#box, scale);
|
|
2276
|
+
} else {
|
|
2277
|
+
this.#editing = null;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
ctx.save();
|
|
2281
|
+
if (this.#disabled) {
|
|
2282
|
+
ctx.globalAlpha *= 0.4;
|
|
2283
|
+
}
|
|
2284
|
+
this.render(ctx, box, scale);
|
|
2285
|
+
ctx.restore();
|
|
2025
2286
|
}
|
|
2026
|
-
|
|
2287
|
+
PointerDown(pointer) {
|
|
2288
|
+
if (this.#disabled) {
|
|
2289
|
+
return;
|
|
2290
|
+
}
|
|
2291
|
+
this.#pressed = true;
|
|
2292
|
+
this.onPointerDown(pointer);
|
|
2293
|
+
requestRender();
|
|
2294
|
+
}
|
|
2295
|
+
PointerMove(pointer) {
|
|
2296
|
+
if (!this.#pressed) {
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
this.onPointerMove(pointer);
|
|
2300
|
+
requestRender();
|
|
2301
|
+
}
|
|
2302
|
+
PointerUp(pointer) {
|
|
2303
|
+
if (!this.#pressed) {
|
|
2304
|
+
return;
|
|
2305
|
+
}
|
|
2306
|
+
this.#pressed = false;
|
|
2307
|
+
this.onPointerUp(pointer);
|
|
2308
|
+
if (pointer.inside) {
|
|
2309
|
+
this.onClick(pointer);
|
|
2310
|
+
}
|
|
2311
|
+
requestRender();
|
|
2312
|
+
}
|
|
2313
|
+
Cursor() {
|
|
2314
|
+
return "pointer" /* Pointer */;
|
|
2315
|
+
}
|
|
2316
|
+
Hidden() {
|
|
2317
|
+
return this.#hidden;
|
|
2318
|
+
}
|
|
2319
|
+
setHidden(hidden) {
|
|
2320
|
+
this.#hidden = hidden;
|
|
2321
|
+
requestRender();
|
|
2322
|
+
}
|
|
2323
|
+
Disabled() {
|
|
2324
|
+
return this.#disabled;
|
|
2325
|
+
}
|
|
2326
|
+
setDisabled(disabled) {
|
|
2327
|
+
this.#disabled = disabled;
|
|
2328
|
+
if (disabled) {
|
|
2329
|
+
this.#pressed = false;
|
|
2330
|
+
}
|
|
2331
|
+
requestRender();
|
|
2332
|
+
}
|
|
2333
|
+
hovered() {
|
|
2334
|
+
return this.#hovered;
|
|
2335
|
+
}
|
|
2336
|
+
pressed() {
|
|
2337
|
+
return this.#pressed;
|
|
2338
|
+
}
|
|
2339
|
+
/**
|
|
2340
|
+
* Where the widget was last drawn, in canvas pixels.
|
|
2341
|
+
*/
|
|
2342
|
+
box() {
|
|
2343
|
+
return this.#box;
|
|
2344
|
+
}
|
|
2345
|
+
canvas() {
|
|
2346
|
+
return this.#canvas;
|
|
2347
|
+
}
|
|
2348
|
+
scale() {
|
|
2349
|
+
return this.#scale;
|
|
2350
|
+
}
|
|
2351
|
+
editText(value, onCommit) {
|
|
2352
|
+
this.#editing = editInPlace({
|
|
2353
|
+
canvas: this.#canvas,
|
|
2354
|
+
box: this.#box,
|
|
2355
|
+
scale: this.#scale,
|
|
2356
|
+
value,
|
|
2357
|
+
onCommit
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
2360
|
+
onPointerDown(pointer) {
|
|
2361
|
+
}
|
|
2362
|
+
onPointerMove(pointer) {
|
|
2363
|
+
}
|
|
2364
|
+
onPointerUp(pointer) {
|
|
2365
|
+
}
|
|
2366
|
+
/**
|
|
2367
|
+
* A press and release that both landed on the widget.
|
|
2368
|
+
*/
|
|
2369
|
+
onClick(pointer) {
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
function startingWidgetValue(node, property, configured, fallback) {
|
|
2373
|
+
if (configured !== void 0) {
|
|
2374
|
+
return configured;
|
|
2375
|
+
}
|
|
2376
|
+
if (property !== void 0 && property !== null) {
|
|
2027
2377
|
const existing = node.getProperty(property);
|
|
2028
2378
|
if (existing !== void 0 && existing !== null) {
|
|
2029
2379
|
return existing;
|
|
@@ -2032,187 +2382,192 @@ function startingWidgetValue(node, property, configured, fallback) {
|
|
|
2032
2382
|
return fallback;
|
|
2033
2383
|
}
|
|
2034
2384
|
|
|
2035
|
-
// src/widgets/
|
|
2036
|
-
var
|
|
2385
|
+
// src/widgets/value.ts
|
|
2386
|
+
var ValueWidget = class extends WidgetBase {
|
|
2037
2387
|
#node;
|
|
2038
|
-
#
|
|
2388
|
+
#property;
|
|
2039
2389
|
#value;
|
|
2040
|
-
#
|
|
2041
|
-
#
|
|
2042
|
-
#text;
|
|
2390
|
+
#label;
|
|
2391
|
+
#width;
|
|
2043
2392
|
#callback;
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2393
|
+
#onInput;
|
|
2394
|
+
constructor(node, config, fallback) {
|
|
2395
|
+
super();
|
|
2047
2396
|
this.#node = node;
|
|
2048
|
-
this.#
|
|
2049
|
-
this.#
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
border: {
|
|
2053
|
-
size: Theme.Widget.Border.Size,
|
|
2054
|
-
color: Theme.Widget.Border.Color
|
|
2055
|
-
},
|
|
2056
|
-
radius: Theme.Widget.Border.Radius
|
|
2057
|
-
},
|
|
2058
|
-
text: { color: Theme.Widget.FontColor }
|
|
2059
|
-
}));
|
|
2060
|
-
this.#highlightBoxStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.highlightBoxStyle, {
|
|
2061
|
-
box: {
|
|
2062
|
-
color: Theme.Widget.Hover.BackgroundColor,
|
|
2063
|
-
border: {
|
|
2064
|
-
size: Theme.Widget.Border.Size,
|
|
2065
|
-
color: Theme.Widget.Border.Color
|
|
2066
|
-
},
|
|
2067
|
-
radius: Theme.Widget.Border.Radius
|
|
2068
|
-
},
|
|
2069
|
-
text: { color: Theme.Widget.FontColor }
|
|
2070
|
-
}));
|
|
2071
|
-
this.Set(startingWidgetValue(node, this.#nodeProperty, config?.value, 0));
|
|
2397
|
+
this.#property = config?.property;
|
|
2398
|
+
this.#label = config?.label ?? "";
|
|
2399
|
+
this.#width = config?.width ?? width;
|
|
2400
|
+
this.#value = startingWidgetValue(node, this.#property, config?.value, fallback);
|
|
2072
2401
|
this.#callback = config?.callback;
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2402
|
+
this.#onInput = config?.onInput;
|
|
2403
|
+
if (this.#property !== void 0) {
|
|
2404
|
+
node.setProperty(this.#property, this.#value);
|
|
2405
|
+
node.addPropertyChangeListener(this.#property, (_, next) => this.setValue(next));
|
|
2077
2406
|
}
|
|
2078
2407
|
}
|
|
2079
2408
|
Size() {
|
|
2080
|
-
return {
|
|
2409
|
+
return { x: this.#width, y: height };
|
|
2081
2410
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2411
|
+
value() {
|
|
2412
|
+
return this.#value;
|
|
2413
|
+
}
|
|
2414
|
+
/**
|
|
2415
|
+
* Shows a new value without treating it as a person's edit: no callback
|
|
2416
|
+
* and no edit event.
|
|
2417
|
+
*/
|
|
2418
|
+
setValue(value) {
|
|
2419
|
+
if (this.equal(value, this.#value)) {
|
|
2084
2420
|
return;
|
|
2085
2421
|
}
|
|
2086
|
-
this.#value =
|
|
2087
|
-
|
|
2088
|
-
|
|
2422
|
+
this.#value = value;
|
|
2423
|
+
this.valueChanged();
|
|
2424
|
+
if (this.#property !== void 0) {
|
|
2425
|
+
this.#node.setProperty(this.#property, value);
|
|
2089
2426
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2427
|
+
requestRender();
|
|
2428
|
+
}
|
|
2429
|
+
label() {
|
|
2430
|
+
return this.#label;
|
|
2431
|
+
}
|
|
2432
|
+
setLabel(label) {
|
|
2433
|
+
this.#label = label;
|
|
2434
|
+
requestRender();
|
|
2435
|
+
}
|
|
2436
|
+
node() {
|
|
2437
|
+
return this.#node;
|
|
2438
|
+
}
|
|
2439
|
+
input(value) {
|
|
2440
|
+
this.setValue(value);
|
|
2441
|
+
this.#onInput?.(value);
|
|
2442
|
+
}
|
|
2443
|
+
/**
|
|
2444
|
+
* Ends a person's edit. Nothing is reported when they ended where they
|
|
2445
|
+
* started.
|
|
2446
|
+
*/
|
|
2447
|
+
commit(value, startedAt) {
|
|
2448
|
+
this.setValue(value);
|
|
2449
|
+
if (this.equal(value, startedAt)) {
|
|
2450
|
+
return;
|
|
2092
2451
|
}
|
|
2093
|
-
this.#
|
|
2452
|
+
if (this.#property !== void 0) {
|
|
2453
|
+
this.#node.editProperty(this.#property, value);
|
|
2454
|
+
}
|
|
2455
|
+
this.#callback?.(value);
|
|
2094
2456
|
}
|
|
2095
|
-
|
|
2457
|
+
valueChanged() {
|
|
2096
2458
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2459
|
+
equal(a, b) {
|
|
2460
|
+
return Object.is(a, b);
|
|
2461
|
+
}
|
|
2462
|
+
};
|
|
2463
|
+
|
|
2464
|
+
// src/widgets/number.ts
|
|
2465
|
+
var DRAG_THRESHOLD = 3;
|
|
2466
|
+
var NumberWidget = class extends ValueWidget {
|
|
2467
|
+
#step;
|
|
2468
|
+
#integer;
|
|
2469
|
+
#min;
|
|
2470
|
+
#max;
|
|
2471
|
+
#startX = 0;
|
|
2472
|
+
#startValue = 0;
|
|
2473
|
+
#dragging = false;
|
|
2474
|
+
#dragged = false;
|
|
2475
|
+
constructor(node, config) {
|
|
2476
|
+
super(node, config, 0);
|
|
2477
|
+
this.#step = config?.step;
|
|
2478
|
+
this.#integer = config?.integer === true;
|
|
2479
|
+
this.#min = config?.min ?? -Infinity;
|
|
2480
|
+
this.#max = config?.max ?? Infinity;
|
|
2481
|
+
}
|
|
2482
|
+
Cursor() {
|
|
2483
|
+
return "ew-resize" /* EWResize */;
|
|
2484
|
+
}
|
|
2485
|
+
render(ctx, box, scale) {
|
|
2486
|
+
drawField(ctx, box, scale, {
|
|
2487
|
+
input: true,
|
|
2488
|
+
hovered: this.hovered() || this.pressed(),
|
|
2489
|
+
label: this.label(),
|
|
2490
|
+
value: formatNumber(this.value())
|
|
2118
2491
|
});
|
|
2119
|
-
popup.Show();
|
|
2120
2492
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2493
|
+
onPointerDown(pointer) {
|
|
2494
|
+
this.#startX = pointer.position.x;
|
|
2495
|
+
this.#startValue = this.value();
|
|
2496
|
+
this.#dragging = false;
|
|
2497
|
+
this.#dragged = false;
|
|
2498
|
+
}
|
|
2499
|
+
onPointerMove(pointer) {
|
|
2500
|
+
const dx = pointer.position.x - this.#startX;
|
|
2501
|
+
if (!this.#dragging && Math.abs(dx) < DRAG_THRESHOLD) {
|
|
2502
|
+
return;
|
|
2503
|
+
}
|
|
2504
|
+
this.#dragging = true;
|
|
2505
|
+
this.input(this.#clean(this.#startValue + dx * this.#stepFor(this.#startValue)));
|
|
2506
|
+
}
|
|
2507
|
+
onPointerUp() {
|
|
2508
|
+
this.#dragged = this.#dragging;
|
|
2509
|
+
if (this.#dragging) {
|
|
2510
|
+
this.#dragging = false;
|
|
2511
|
+
this.commit(this.value(), this.#startValue);
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
onClick() {
|
|
2515
|
+
if (this.#dragged) {
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
const before = this.value();
|
|
2519
|
+
this.editText(String(before), (text) => {
|
|
2520
|
+
const parsed = Number.parseFloat(text);
|
|
2521
|
+
if (Number.isFinite(parsed)) {
|
|
2522
|
+
this.commit(this.#clean(parsed), before);
|
|
2134
2523
|
}
|
|
2524
|
+
});
|
|
2525
|
+
}
|
|
2526
|
+
#stepFor(value) {
|
|
2527
|
+
if (this.#step !== void 0) {
|
|
2528
|
+
return this.#step;
|
|
2135
2529
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2530
|
+
return Math.max(Math.abs(value) * 0.01, this.#integer ? 0.1 : 0.01);
|
|
2531
|
+
}
|
|
2532
|
+
#clean(value) {
|
|
2533
|
+
const rounded = this.#integer ? Math.round(value) : value;
|
|
2534
|
+
return Math.min(this.#max, Math.max(this.#min, rounded));
|
|
2138
2535
|
}
|
|
2139
2536
|
};
|
|
2537
|
+
function formatNumber(value) {
|
|
2538
|
+
return "" + parseFloat(value.toPrecision(6));
|
|
2539
|
+
}
|
|
2140
2540
|
|
|
2141
2541
|
// src/widgets/button.ts
|
|
2142
|
-
var ButtonWidget = class {
|
|
2542
|
+
var ButtonWidget = class extends WidgetBase {
|
|
2143
2543
|
#text;
|
|
2144
|
-
#idleStyle;
|
|
2145
|
-
#hoverStyle;
|
|
2146
|
-
#clickStyle;
|
|
2147
2544
|
#callback;
|
|
2148
|
-
#
|
|
2545
|
+
#width;
|
|
2149
2546
|
constructor(config) {
|
|
2150
|
-
|
|
2547
|
+
super();
|
|
2548
|
+
this.#text = config?.text ?? "Button";
|
|
2151
2549
|
this.#callback = config?.callback;
|
|
2152
|
-
this.#
|
|
2153
|
-
this.#idleStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.idleStyle, {
|
|
2154
|
-
box: {
|
|
2155
|
-
color: Theme.Widget.BackgroundColor,
|
|
2156
|
-
border: {
|
|
2157
|
-
size: Theme.Widget.Border.Size,
|
|
2158
|
-
color: Theme.Widget.Border.Color
|
|
2159
|
-
}
|
|
2160
|
-
},
|
|
2161
|
-
text: { color: Theme.Widget.FontColor }
|
|
2162
|
-
}));
|
|
2163
|
-
this.#hoverStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.hoverStyle, {
|
|
2164
|
-
box: {
|
|
2165
|
-
color: Theme.Widget.Hover.BackgroundColor,
|
|
2166
|
-
border: {
|
|
2167
|
-
size: Theme.Widget.Border.Size,
|
|
2168
|
-
color: Theme.Widget.Border.Color
|
|
2169
|
-
}
|
|
2170
|
-
},
|
|
2171
|
-
text: { color: Theme.Widget.FontColor }
|
|
2172
|
-
}));
|
|
2173
|
-
this.#clickStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.clickStyle, {
|
|
2174
|
-
box: {
|
|
2175
|
-
color: Theme.Widget.Button.Click.BackgroundColor,
|
|
2176
|
-
border: {
|
|
2177
|
-
size: Theme.Widget.Border.Size,
|
|
2178
|
-
color: Theme.Widget.Border.Color
|
|
2179
|
-
}
|
|
2180
|
-
},
|
|
2181
|
-
text: { color: Theme.Widget.FontColor }
|
|
2182
|
-
}));
|
|
2550
|
+
this.#width = config?.width ?? width;
|
|
2183
2551
|
}
|
|
2184
2552
|
Size() {
|
|
2185
|
-
return {
|
|
2553
|
+
return { x: this.#width, y: height };
|
|
2186
2554
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
Position: { x: 0, y: 0 },
|
|
2190
|
-
Size: {
|
|
2191
|
-
x: width * scale,
|
|
2192
|
-
y: height * scale
|
|
2193
|
-
}
|
|
2194
|
-
};
|
|
2195
|
-
CopyVector2(box.Position, position);
|
|
2196
|
-
let style = this.#idleStyle;
|
|
2197
|
-
if (mousePosition !== void 0 && !this.#gettingClicked) {
|
|
2198
|
-
if (InBox(box, mousePosition)) {
|
|
2199
|
-
style = this.#hoverStyle;
|
|
2200
|
-
}
|
|
2201
|
-
}
|
|
2202
|
-
if (this.#gettingClicked) {
|
|
2203
|
-
style = this.#clickStyle;
|
|
2204
|
-
}
|
|
2205
|
-
style.Draw(ctx, box, scale, this.#text);
|
|
2206
|
-
return box;
|
|
2555
|
+
text() {
|
|
2556
|
+
return this.#text;
|
|
2207
2557
|
}
|
|
2208
|
-
|
|
2209
|
-
this.#
|
|
2558
|
+
setText(text) {
|
|
2559
|
+
this.#text = text;
|
|
2560
|
+
requestRender();
|
|
2210
2561
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
this
|
|
2215
|
-
|
|
2562
|
+
render(ctx, box, scale) {
|
|
2563
|
+
drawField(ctx, box, scale, {
|
|
2564
|
+
hovered: this.hovered(),
|
|
2565
|
+
pressed: this.pressed(),
|
|
2566
|
+
value: this.#text
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
onClick() {
|
|
2570
|
+
this.#callback?.();
|
|
2216
2571
|
}
|
|
2217
2572
|
};
|
|
2218
2573
|
|
|
@@ -2227,500 +2582,208 @@ function contrastColor(color) {
|
|
|
2227
2582
|
}
|
|
2228
2583
|
return "white";
|
|
2229
2584
|
}
|
|
2230
|
-
var
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2585
|
+
var picker = null;
|
|
2586
|
+
function sharedPicker() {
|
|
2587
|
+
if (picker === null || !picker.isConnected) {
|
|
2588
|
+
picker = document.createElement("input");
|
|
2589
|
+
picker.type = "color";
|
|
2590
|
+
document.body.appendChild(picker);
|
|
2591
|
+
}
|
|
2592
|
+
return picker;
|
|
2593
|
+
}
|
|
2594
|
+
var ColorWidget = class extends ValueWidget {
|
|
2234
2595
|
#contrast;
|
|
2235
|
-
#textBoxStyle;
|
|
2236
|
-
#callback;
|
|
2237
2596
|
constructor(node, config) {
|
|
2238
|
-
|
|
2239
|
-
this.#
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
this.#contrast = contrastColor(this
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2597
|
+
super(node, config, "#000000");
|
|
2598
|
+
this.#contrast = contrastColor(this.value());
|
|
2599
|
+
}
|
|
2600
|
+
valueChanged() {
|
|
2601
|
+
this.#contrast = contrastColor(this.value());
|
|
2602
|
+
}
|
|
2603
|
+
render(ctx, box, scale) {
|
|
2604
|
+
drawField(ctx, box, scale, {
|
|
2605
|
+
input: true,
|
|
2606
|
+
hovered: this.hovered(),
|
|
2607
|
+
label: this.label(),
|
|
2608
|
+
value: this.value(),
|
|
2609
|
+
background: this.value(),
|
|
2610
|
+
border: this.#contrast,
|
|
2611
|
+
textColor: this.#contrast
|
|
2252
2612
|
});
|
|
2253
|
-
if (this.#nodeProperty !== void 0) {
|
|
2254
|
-
this.#node.addPropertyChangeListener(this.#nodeProperty, (oldVal, newVal) => {
|
|
2255
|
-
this.Set(newVal);
|
|
2256
|
-
});
|
|
2257
|
-
}
|
|
2258
2613
|
}
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
Set(value) {
|
|
2263
|
-
if (this.#value === value) {
|
|
2614
|
+
onClick() {
|
|
2615
|
+
const canvas = this.canvas();
|
|
2616
|
+
if (canvas === null) {
|
|
2264
2617
|
return;
|
|
2265
2618
|
}
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
input.value = this.#value;
|
|
2290
|
-
input.name = "color";
|
|
2291
|
-
container.append(input);
|
|
2292
|
-
const label = document.createElement("label");
|
|
2293
|
-
label.htmlFor = "color";
|
|
2294
|
-
label.textContent = "Color";
|
|
2295
|
-
container.append(label);
|
|
2296
|
-
return container;
|
|
2297
|
-
},
|
|
2298
|
-
onClose: (button) => {
|
|
2299
|
-
if (button !== "Set" || input === null) {
|
|
2300
|
-
return;
|
|
2301
|
-
}
|
|
2302
|
-
this.Set(input.value);
|
|
2303
|
-
}
|
|
2304
|
-
});
|
|
2305
|
-
popup.Show();
|
|
2306
|
-
}
|
|
2307
|
-
Draw(ctx, position, scale, mousePosition) {
|
|
2308
|
-
const box = {
|
|
2309
|
-
Position: { x: 0, y: 0 },
|
|
2310
|
-
Size: {
|
|
2311
|
-
x: width * scale,
|
|
2312
|
-
y: height * scale
|
|
2313
|
-
}
|
|
2314
|
-
};
|
|
2315
|
-
CopyVector2(box.Position, position);
|
|
2316
|
-
this.#textBoxStyle.Draw(ctx, box, scale, this.#value);
|
|
2317
|
-
return box;
|
|
2619
|
+
const before = this.value();
|
|
2620
|
+
const alpha = before.slice(7);
|
|
2621
|
+
const box = this.box();
|
|
2622
|
+
const rect = canvas.getBoundingClientRect();
|
|
2623
|
+
const sx = canvas.width > 0 ? rect.width / canvas.width : 1;
|
|
2624
|
+
const sy = canvas.height > 0 ? rect.height / canvas.height : 1;
|
|
2625
|
+
const input = sharedPicker();
|
|
2626
|
+
input.style.cssText = [
|
|
2627
|
+
"position: fixed",
|
|
2628
|
+
`left: ${rect.left + box.Position.x * sx}px`,
|
|
2629
|
+
`top: ${rect.top + box.Position.y * sy}px`,
|
|
2630
|
+
`width: ${box.Size.x * sx}px`,
|
|
2631
|
+
`height: ${box.Size.y * sy}px`,
|
|
2632
|
+
"opacity: 0",
|
|
2633
|
+
"pointer-events: none",
|
|
2634
|
+
"border: 0",
|
|
2635
|
+
"padding: 0"
|
|
2636
|
+
].join(";");
|
|
2637
|
+
input.value = before.slice(0, 7);
|
|
2638
|
+
input.oninput = () => this.input(input.value + alpha);
|
|
2639
|
+
input.onchange = () => this.commit(input.value + alpha, before);
|
|
2640
|
+
input.getBoundingClientRect();
|
|
2641
|
+
input.click();
|
|
2318
2642
|
}
|
|
2319
2643
|
};
|
|
2320
2644
|
|
|
2321
2645
|
// src/widgets/slider.ts
|
|
2322
|
-
var SliderWidget = class {
|
|
2323
|
-
// Data Variables =========================================================
|
|
2646
|
+
var SliderWidget = class extends ValueWidget {
|
|
2324
2647
|
#min;
|
|
2325
2648
|
#max;
|
|
2326
|
-
#value;
|
|
2327
|
-
#text;
|
|
2328
2649
|
#snapIncrement;
|
|
2329
|
-
#node;
|
|
2330
|
-
#nodeProperty;
|
|
2331
|
-
// Callbacks ==============================================================
|
|
2332
|
-
#change;
|
|
2333
|
-
#release;
|
|
2334
|
-
// Styling Variables ======================================================
|
|
2335
|
-
#textStyle;
|
|
2336
|
-
#backgroundColor;
|
|
2337
|
-
#borderColor;
|
|
2338
2650
|
#fillColor;
|
|
2339
|
-
|
|
2340
|
-
#lastMousePosition;
|
|
2341
|
-
#clickStartMousePosition;
|
|
2342
|
-
#clicking;
|
|
2651
|
+
#startValue = 0;
|
|
2343
2652
|
constructor(node, config) {
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
this.#
|
|
2653
|
+
const min = config?.min ?? 0;
|
|
2654
|
+
const max = config?.max ?? 1;
|
|
2655
|
+
super(node, { ...config, value: config?.value === void 0 ? void 0 : Clamp(config.value, min, max) }, min);
|
|
2656
|
+
this.#min = min;
|
|
2657
|
+
this.#max = max;
|
|
2348
2658
|
this.#snapIncrement = config?.snapIncrement;
|
|
2349
|
-
this.#
|
|
2350
|
-
this.#text = "";
|
|
2351
|
-
this.#release = config?.release;
|
|
2352
|
-
this.#backgroundColor = config?.backgroundColor === void 0 ? Theme.Widget.BackgroundColor : config?.backgroundColor;
|
|
2353
|
-
this.#fillColor = config?.fillColor === void 0 ? Theme.Widget.Slider.FillColor : config?.fillColor;
|
|
2354
|
-
this.#borderColor = config?.borderColor === void 0 ? Theme.Widget.Border.Color : config?.borderColor;
|
|
2355
|
-
this.#textStyle = new TextStyle(TextStyleFallback(config?.textStyle, {
|
|
2356
|
-
color: Theme.Widget.FontColor
|
|
2357
|
-
}));
|
|
2358
|
-
this.#lastMousePosition = Zero();
|
|
2359
|
-
this.#clickStartMousePosition = Zero();
|
|
2360
|
-
this.#clicking = false;
|
|
2361
|
-
if (config?.property !== void 0 && config?.property !== null) {
|
|
2362
|
-
node.addPropertyChangeListener(config.property, (oldVal, newVal) => {
|
|
2363
|
-
this.SetValue(newVal);
|
|
2364
|
-
});
|
|
2365
|
-
}
|
|
2366
|
-
this.SetValue(startingWidgetValue(node, this.#nodeProperty, config?.value, 0));
|
|
2367
|
-
this.#change = config?.change;
|
|
2368
|
-
}
|
|
2369
|
-
SetValue(newValue) {
|
|
2370
|
-
const cleanedValue = Clamp(newValue, this.#min, this.#max);
|
|
2371
|
-
if (Object.is(this.#value, cleanedValue)) {
|
|
2372
|
-
return;
|
|
2373
|
-
}
|
|
2374
|
-
this.#value = cleanedValue;
|
|
2375
|
-
if (this.#nodeProperty) {
|
|
2376
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
2377
|
-
}
|
|
2378
|
-
this.#text = this.#value.toFixed(3);
|
|
2379
|
-
if (this.#change !== void 0) {
|
|
2380
|
-
this.#change(this.#value);
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
Size() {
|
|
2384
|
-
return { "x": width, "y": height };
|
|
2659
|
+
this.#fillColor = config?.fillColor;
|
|
2385
2660
|
}
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
CopyVector2(this.#clickStartMousePosition, this.#lastMousePosition);
|
|
2661
|
+
Cursor() {
|
|
2662
|
+
return "ew-resize" /* EWResize */;
|
|
2389
2663
|
}
|
|
2390
|
-
|
|
2391
|
-
this.#
|
|
2392
|
-
if (this.#release !== void 0) {
|
|
2393
|
-
this.#release(this.#value);
|
|
2394
|
-
}
|
|
2664
|
+
setValue(value) {
|
|
2665
|
+
super.setValue(Clamp(value, this.#min, this.#max));
|
|
2395
2666
|
}
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
const
|
|
2399
|
-
const
|
|
2400
|
-
ctx.fillStyle = this.#
|
|
2401
|
-
ctx.strokeStyle = this.#borderColor;
|
|
2402
|
-
ctx.lineWidth = scaledBorderThickness;
|
|
2667
|
+
render(ctx, box, scale) {
|
|
2668
|
+
drawFieldBox(ctx, box, scale, { input: true, hovered: this.hovered() || this.pressed(), value: "" });
|
|
2669
|
+
const border = Theme.Widget.Border.Size * scale;
|
|
2670
|
+
const fill = Clamp01((this.value() - this.#min) / (this.#max - this.#min));
|
|
2671
|
+
ctx.fillStyle = this.#fillColor ?? Theme.Widget.Slider.FillColor;
|
|
2403
2672
|
ctx.beginPath();
|
|
2404
2673
|
ctx.roundRect(
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2674
|
+
box.Position.x + border / 2,
|
|
2675
|
+
box.Position.y + border / 2,
|
|
2676
|
+
Math.max(0, box.Size.x * fill - border),
|
|
2677
|
+
box.Size.y - border,
|
|
2409
2678
|
Theme.Widget.Border.Radius * scale
|
|
2410
2679
|
);
|
|
2411
2680
|
ctx.fill();
|
|
2412
|
-
ctx
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
);
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
let value = p * (this.#max - this.#min) + this.#min;
|
|
2439
|
-
if (this.#snapIncrement !== void 0) {
|
|
2440
|
-
value = Math.round(value / this.#snapIncrement) * this.#snapIncrement;
|
|
2441
|
-
}
|
|
2442
|
-
this.SetValue(value);
|
|
2443
|
-
}
|
|
2681
|
+
drawFieldText(ctx, box, scale, {
|
|
2682
|
+
input: true,
|
|
2683
|
+
hovered: false,
|
|
2684
|
+
label: this.label(),
|
|
2685
|
+
value: formatNumber(Number(this.value().toFixed(3)))
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
onPointerDown(pointer) {
|
|
2689
|
+
this.#startValue = this.value();
|
|
2690
|
+
this.input(this.#valueAt(pointer.position.x));
|
|
2691
|
+
}
|
|
2692
|
+
onPointerMove(pointer) {
|
|
2693
|
+
this.input(this.#valueAt(pointer.position.x));
|
|
2694
|
+
}
|
|
2695
|
+
onPointerUp() {
|
|
2696
|
+
this.commit(this.value(), this.#startValue);
|
|
2697
|
+
}
|
|
2698
|
+
#valueAt(x) {
|
|
2699
|
+
const box = this.box();
|
|
2700
|
+
const border = Theme.Widget.Border.Size * this.scale();
|
|
2701
|
+
const min = box.Position.x + border / 2;
|
|
2702
|
+
const max = min + box.Size.x - border;
|
|
2703
|
+
const p = Clamp01((x - min) / (max - min));
|
|
2704
|
+
let value = p * (this.#max - this.#min) + this.#min;
|
|
2705
|
+
if (this.#snapIncrement !== void 0) {
|
|
2706
|
+
value = Math.round(value / this.#snapIncrement) * this.#snapIncrement;
|
|
2444
2707
|
}
|
|
2445
|
-
return
|
|
2446
|
-
Position: { x: position.x, y: position.y },
|
|
2447
|
-
Size: {
|
|
2448
|
-
x: scaledWidth,
|
|
2449
|
-
y: scaledHeight
|
|
2450
|
-
}
|
|
2451
|
-
};
|
|
2708
|
+
return Clamp(value, this.#min, this.#max);
|
|
2452
2709
|
}
|
|
2453
2710
|
};
|
|
2454
2711
|
|
|
2455
|
-
// src/popups/string.ts
|
|
2456
|
-
function SetStringPopup(config) {
|
|
2457
|
-
let input = null;
|
|
2458
|
-
return new Popup({
|
|
2459
|
-
title: config.title,
|
|
2460
|
-
options: ["Set", "Cancel"],
|
|
2461
|
-
content: () => {
|
|
2462
|
-
const container = document.createElement("div");
|
|
2463
|
-
input = document.createElement("input");
|
|
2464
|
-
input.value = config.startingValue;
|
|
2465
|
-
container.append(input);
|
|
2466
|
-
return container;
|
|
2467
|
-
},
|
|
2468
|
-
onClose: (button) => {
|
|
2469
|
-
if (button !== "Set" || input === null) {
|
|
2470
|
-
if (config.onCancel) {
|
|
2471
|
-
config.onCancel();
|
|
2472
|
-
}
|
|
2473
|
-
return;
|
|
2474
|
-
}
|
|
2475
|
-
config.onUpdate(input.value);
|
|
2476
|
-
}
|
|
2477
|
-
});
|
|
2478
|
-
}
|
|
2479
|
-
|
|
2480
2712
|
// src/widgets/string.ts
|
|
2481
|
-
var StringWidget = class {
|
|
2482
|
-
#value;
|
|
2483
|
-
#idleStyle;
|
|
2484
|
-
#hoverStyle;
|
|
2485
|
-
#callback;
|
|
2486
|
-
#node;
|
|
2487
|
-
#nodeProperty;
|
|
2713
|
+
var StringWidget = class extends ValueWidget {
|
|
2488
2714
|
constructor(node, config) {
|
|
2489
|
-
|
|
2490
|
-
this.#node = node;
|
|
2491
|
-
this.#nodeProperty = config?.property;
|
|
2492
|
-
this.#idleStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.textBoxStyle, {
|
|
2493
|
-
box: {
|
|
2494
|
-
color: Theme.Widget.BackgroundColor,
|
|
2495
|
-
border: {
|
|
2496
|
-
size: Theme.Widget.Border.Size,
|
|
2497
|
-
color: Theme.Widget.Border.Color
|
|
2498
|
-
},
|
|
2499
|
-
radius: Theme.Widget.Border.Radius
|
|
2500
|
-
},
|
|
2501
|
-
text: { color: Theme.Widget.FontColor }
|
|
2502
|
-
}));
|
|
2503
|
-
this.#hoverStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.textBoxStyle, {
|
|
2504
|
-
box: {
|
|
2505
|
-
color: Theme.Widget.Hover.BackgroundColor,
|
|
2506
|
-
border: {
|
|
2507
|
-
size: Theme.Widget.Border.Size,
|
|
2508
|
-
color: Theme.Widget.Border.Color
|
|
2509
|
-
},
|
|
2510
|
-
radius: Theme.Widget.Border.Radius
|
|
2511
|
-
},
|
|
2512
|
-
text: { color: Theme.Widget.FontColor }
|
|
2513
|
-
}));
|
|
2514
|
-
this.Set(startingWidgetValue(node, this.#nodeProperty, config?.value, ""));
|
|
2515
|
-
this.#callback = config?.callback;
|
|
2516
|
-
if (this.#nodeProperty !== void 0) {
|
|
2517
|
-
this.#node.addPropertyChangeListener(this.#nodeProperty, (oldVal, newVal) => {
|
|
2518
|
-
this.Set(newVal);
|
|
2519
|
-
});
|
|
2520
|
-
}
|
|
2715
|
+
super(node, config, "");
|
|
2521
2716
|
}
|
|
2522
|
-
|
|
2523
|
-
return
|
|
2717
|
+
Cursor() {
|
|
2718
|
+
return "text" /* Text */;
|
|
2524
2719
|
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
2532
|
-
}
|
|
2533
|
-
if (this.#callback !== void 0) {
|
|
2534
|
-
this.#callback(this.#value);
|
|
2535
|
-
}
|
|
2536
|
-
}
|
|
2537
|
-
ClickStart() {
|
|
2538
|
-
}
|
|
2539
|
-
ClickEnd() {
|
|
2540
|
-
const popup = SetStringPopup({
|
|
2541
|
-
title: "Set String",
|
|
2542
|
-
startingValue: this.#value,
|
|
2543
|
-
onUpdate: (value) => {
|
|
2544
|
-
this.Set(value);
|
|
2545
|
-
}
|
|
2720
|
+
render(ctx, box, scale) {
|
|
2721
|
+
drawField(ctx, box, scale, {
|
|
2722
|
+
input: true,
|
|
2723
|
+
hovered: this.hovered(),
|
|
2724
|
+
label: this.label(),
|
|
2725
|
+
value: this.value()
|
|
2546
2726
|
});
|
|
2547
|
-
popup.Show();
|
|
2548
2727
|
}
|
|
2549
|
-
|
|
2550
|
-
const
|
|
2551
|
-
|
|
2552
|
-
Size: {
|
|
2553
|
-
x: width * scale,
|
|
2554
|
-
y: height * scale
|
|
2555
|
-
}
|
|
2556
|
-
};
|
|
2557
|
-
CopyVector2(box.Position, position);
|
|
2558
|
-
let style = this.#idleStyle;
|
|
2559
|
-
if (mousePosition !== void 0) {
|
|
2560
|
-
if (InBox(box, mousePosition)) {
|
|
2561
|
-
style = this.#hoverStyle;
|
|
2562
|
-
}
|
|
2563
|
-
}
|
|
2564
|
-
style.DrawUnderline(ctx, box, scale, fitString(ctx, this.#value, box.Size.x - 20 * scale));
|
|
2565
|
-
return box;
|
|
2728
|
+
onClick() {
|
|
2729
|
+
const before = this.value();
|
|
2730
|
+
this.editText(before, (text) => this.commit(text, before));
|
|
2566
2731
|
}
|
|
2567
2732
|
};
|
|
2568
2733
|
|
|
2569
2734
|
// src/widgets/toggle.ts
|
|
2570
|
-
var
|
|
2571
|
-
#
|
|
2572
|
-
#
|
|
2573
|
-
#lightColor;
|
|
2574
|
-
#lightBorderColor;
|
|
2575
|
-
#lightBlur;
|
|
2576
|
-
#box;
|
|
2577
|
-
constructor(config) {
|
|
2578
|
-
this.#idleStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.idle, {
|
|
2579
|
-
box: {
|
|
2580
|
-
color: Theme.Widget.BackgroundColor,
|
|
2581
|
-
border: {
|
|
2582
|
-
size: Theme.Widget.Border.Size,
|
|
2583
|
-
color: Theme.Widget.Border.Color
|
|
2584
|
-
}
|
|
2585
|
-
},
|
|
2586
|
-
text: { color: Theme.Widget.FontColor }
|
|
2587
|
-
}));
|
|
2588
|
-
this.#hoverStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.hover, {
|
|
2589
|
-
box: {
|
|
2590
|
-
color: Theme.Widget.Hover.BackgroundColor,
|
|
2591
|
-
border: {
|
|
2592
|
-
size: Theme.Widget.Border.Size,
|
|
2593
|
-
color: Theme.Widget.Border.Color
|
|
2594
|
-
}
|
|
2595
|
-
},
|
|
2596
|
-
text: { color: Theme.Widget.FontColor }
|
|
2597
|
-
}));
|
|
2598
|
-
this.#lightColor = config?.lightColor === void 0 ? "#222222" : config.lightColor;
|
|
2599
|
-
this.#lightBorderColor = config?.lightBorderColor === void 0 ? "black" : config.lightBorderColor;
|
|
2600
|
-
this.#lightBlur = config?.lightBlur;
|
|
2601
|
-
this.#box = { Position: { x: 0, y: 0 }, Size: { x: 0, y: 0 } };
|
|
2602
|
-
}
|
|
2603
|
-
Draw(ctx, pos, scale, text, mousePosition) {
|
|
2604
|
-
const scaledWidth = width * scale;
|
|
2605
|
-
const scaledHeight = height * scale;
|
|
2606
|
-
this.#box.Size.x = scaledWidth;
|
|
2607
|
-
this.#box.Size.y = scaledHeight;
|
|
2608
|
-
CopyVector2(this.#box.Position, pos);
|
|
2609
|
-
let style = this.#idleStyle;
|
|
2610
|
-
if (mousePosition !== void 0) {
|
|
2611
|
-
if (InBox(this.#box, mousePosition)) {
|
|
2612
|
-
style = this.#hoverStyle;
|
|
2613
|
-
}
|
|
2614
|
-
}
|
|
2615
|
-
style.Draw(ctx, this.#box, scale, text);
|
|
2616
|
-
const lightScale = Math.min(scaledWidth, scaledHeight);
|
|
2617
|
-
ctx.strokeStyle = this.#lightBorderColor;
|
|
2618
|
-
ctx.fillStyle = this.#lightColor;
|
|
2619
|
-
ctx.beginPath();
|
|
2620
|
-
ctx.roundRect(
|
|
2621
|
-
pos.x + lightScale * 0.2,
|
|
2622
|
-
pos.y + lightScale * 0.2,
|
|
2623
|
-
lightScale * 0.6,
|
|
2624
|
-
lightScale * 0.6,
|
|
2625
|
-
Theme.Widget.Border.Radius * scale
|
|
2626
|
-
);
|
|
2627
|
-
if (this.#lightBlur) {
|
|
2628
|
-
ctx.shadowBlur = this.#lightBlur * scale;
|
|
2629
|
-
ctx.shadowColor = this.#lightColor;
|
|
2630
|
-
}
|
|
2631
|
-
ctx.fill();
|
|
2632
|
-
if (this.#lightBlur) {
|
|
2633
|
-
ctx.shadowBlur = 0;
|
|
2634
|
-
}
|
|
2635
|
-
return this.#box;
|
|
2636
|
-
}
|
|
2637
|
-
};
|
|
2638
|
-
var ToggleWidget = class {
|
|
2639
|
-
#node;
|
|
2640
|
-
#nodeProperty;
|
|
2641
|
-
#value;
|
|
2642
|
-
#text;
|
|
2643
|
-
#enabledStyle;
|
|
2644
|
-
#disabledStyle;
|
|
2645
|
-
#callback;
|
|
2735
|
+
var ToggleWidget = class extends ValueWidget {
|
|
2736
|
+
#onColor;
|
|
2737
|
+
#offColor;
|
|
2646
2738
|
constructor(node, config) {
|
|
2647
|
-
|
|
2648
|
-
this.#
|
|
2649
|
-
this.#
|
|
2650
|
-
this.#text = config?.text === void 0 ? "Toggle" : config?.text;
|
|
2651
|
-
this.#enabledStyle = new ToggleStyle({
|
|
2652
|
-
idle: TextBoxStyleWithFallback(config?.enabledStyle?.idle, {
|
|
2653
|
-
box: {
|
|
2654
|
-
color: Theme.Widget.BackgroundColor,
|
|
2655
|
-
border: {
|
|
2656
|
-
size: Theme.Widget.Border.Size,
|
|
2657
|
-
color: Theme.Widget.Border.Color
|
|
2658
|
-
}
|
|
2659
|
-
},
|
|
2660
|
-
text: { color: Theme.Widget.FontColor }
|
|
2661
|
-
}),
|
|
2662
|
-
lightBorderColor: config?.enabledStyle?.lightBorderColor,
|
|
2663
|
-
lightColor: config?.enabledStyle?.lightColor === void 0 ? "#00FF00" : config?.enabledStyle?.lightColor,
|
|
2664
|
-
lightBlur: config?.enabledStyle?.lightBlur === void 0 ? 15 : config?.enabledStyle?.lightBlur
|
|
2665
|
-
});
|
|
2666
|
-
this.#disabledStyle = new ToggleStyle({
|
|
2667
|
-
idle: TextBoxStyleWithFallback(config?.disabledStyle?.idle, {
|
|
2668
|
-
box: {
|
|
2669
|
-
color: Theme.Widget.BackgroundColor,
|
|
2670
|
-
border: {
|
|
2671
|
-
size: Theme.Widget.Border.Size,
|
|
2672
|
-
color: Theme.Widget.Border.Color
|
|
2673
|
-
}
|
|
2674
|
-
},
|
|
2675
|
-
text: { color: Theme.Widget.FontColor }
|
|
2676
|
-
}),
|
|
2677
|
-
lightBorderColor: config?.disabledStyle?.lightBorderColor,
|
|
2678
|
-
lightColor: config?.disabledStyle?.lightColor === void 0 ? "#004400" : config?.enabledStyle?.lightColor
|
|
2679
|
-
});
|
|
2680
|
-
this.Set(startingWidgetValue(node, this.#nodeProperty, config?.value, false));
|
|
2681
|
-
this.#callback = config?.callback;
|
|
2682
|
-
if (this.#nodeProperty !== void 0) {
|
|
2683
|
-
this.#node.addPropertyChangeListener(this.#nodeProperty, (oldVal, newVal) => {
|
|
2684
|
-
this.Set(newVal);
|
|
2685
|
-
});
|
|
2686
|
-
}
|
|
2687
|
-
}
|
|
2688
|
-
Size() {
|
|
2689
|
-
return { "x": width, "y": height };
|
|
2690
|
-
}
|
|
2691
|
-
Draw(ctx, position, scale, mousePosition) {
|
|
2692
|
-
let style = this.#value ? this.#enabledStyle : this.#disabledStyle;
|
|
2693
|
-
return style.Draw(ctx, position, scale, this.#text, mousePosition);
|
|
2739
|
+
super(node, { ...config, label: config?.label ?? "Toggle" }, false);
|
|
2740
|
+
this.#onColor = config?.onColor ?? "#00FF00";
|
|
2741
|
+
this.#offColor = config?.offColor ?? "#004400";
|
|
2694
2742
|
}
|
|
2695
2743
|
Toggle() {
|
|
2696
|
-
this.
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2744
|
+
const before = this.value();
|
|
2745
|
+
this.commit(!before, before);
|
|
2746
|
+
}
|
|
2747
|
+
render(ctx, box, scale) {
|
|
2748
|
+
drawFieldBox(ctx, box, scale, { hovered: this.hovered(), value: "" });
|
|
2749
|
+
const light = Math.min(box.Size.x, box.Size.y) * 0.6;
|
|
2750
|
+
const inset = (box.Size.y - light) / 2;
|
|
2751
|
+
const on = this.value();
|
|
2752
|
+
ctx.fillStyle = on ? this.#onColor : this.#offColor;
|
|
2753
|
+
ctx.beginPath();
|
|
2754
|
+
ctx.roundRect(box.Position.x + inset, box.Position.y + inset, light, light, Theme.Widget.Border.Radius * scale);
|
|
2755
|
+
if (on) {
|
|
2756
|
+
ctx.shadowBlur = 15 * scale;
|
|
2757
|
+
ctx.shadowColor = this.#onColor;
|
|
2708
2758
|
}
|
|
2759
|
+
ctx.fill();
|
|
2760
|
+
ctx.shadowBlur = 0;
|
|
2761
|
+
const textLeft = box.Position.x + inset * 2 + light;
|
|
2762
|
+
ctx.font = widgetFont(scale);
|
|
2763
|
+
ctx.textAlign = "center";
|
|
2764
|
+
ctx.textBaseline = "middle";
|
|
2765
|
+
ctx.fillStyle = Theme.Widget.FontColor;
|
|
2766
|
+
const room = box.Position.x + box.Size.x - textLeft - inset;
|
|
2767
|
+
ctx.fillText(
|
|
2768
|
+
fitString(ctx, this.label(), room),
|
|
2769
|
+
textLeft + room / 2,
|
|
2770
|
+
box.Position.y + box.Size.y / 2
|
|
2771
|
+
);
|
|
2709
2772
|
}
|
|
2710
|
-
|
|
2773
|
+
onClick() {
|
|
2711
2774
|
this.Toggle();
|
|
2712
2775
|
}
|
|
2713
|
-
ClickEnd() {
|
|
2714
|
-
}
|
|
2715
2776
|
};
|
|
2716
2777
|
|
|
2717
2778
|
// src/widgets/image.ts
|
|
2718
|
-
var ImageWidget = class {
|
|
2779
|
+
var ImageWidget = class extends WidgetBase {
|
|
2719
2780
|
#url;
|
|
2781
|
+
#ownedUrl;
|
|
2720
2782
|
#image;
|
|
2721
2783
|
#maxWidth;
|
|
2722
2784
|
#maxHeight;
|
|
2723
2785
|
constructor(config) {
|
|
2786
|
+
super();
|
|
2724
2787
|
this.#maxWidth = config?.maxWidth === void 0 ? 150 : config?.maxWidth;
|
|
2725
2788
|
this.#maxHeight = config?.maxHeight === void 0 ? 150 : config?.maxHeight;
|
|
2726
2789
|
if (config?.image) {
|
|
@@ -2730,16 +2793,23 @@ var ImageWidget = class {
|
|
|
2730
2793
|
}
|
|
2731
2794
|
}
|
|
2732
2795
|
SetBlob(blob) {
|
|
2733
|
-
const
|
|
2734
|
-
|
|
2735
|
-
this
|
|
2796
|
+
const url = URL.createObjectURL(blob);
|
|
2797
|
+
this.SetUrl(url);
|
|
2798
|
+
this.#ownedUrl = url;
|
|
2736
2799
|
}
|
|
2737
2800
|
SetUrl(url) {
|
|
2801
|
+
if (this.#ownedUrl !== void 0) {
|
|
2802
|
+
URL.revokeObjectURL(this.#ownedUrl);
|
|
2803
|
+
this.#ownedUrl = void 0;
|
|
2804
|
+
}
|
|
2738
2805
|
this.#image = void 0;
|
|
2739
2806
|
this.#url = url;
|
|
2740
2807
|
const img = document.createElement("img");
|
|
2741
2808
|
img.src = url;
|
|
2742
2809
|
img.onload = () => {
|
|
2810
|
+
if (this.#url !== url) {
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2743
2813
|
this.#image = img;
|
|
2744
2814
|
requestRender();
|
|
2745
2815
|
};
|
|
@@ -2747,6 +2817,9 @@ var ImageWidget = class {
|
|
|
2747
2817
|
console.log("error loading image:", url, event);
|
|
2748
2818
|
};
|
|
2749
2819
|
}
|
|
2820
|
+
Cursor() {
|
|
2821
|
+
return "default" /* Default */;
|
|
2822
|
+
}
|
|
2750
2823
|
Size() {
|
|
2751
2824
|
if (this.#image === void 0) {
|
|
2752
2825
|
return { "x": 0, "y": 0 };
|
|
@@ -2766,88 +2839,178 @@ var ImageWidget = class {
|
|
|
2766
2839
|
"y": adjust * this.#image.height
|
|
2767
2840
|
};
|
|
2768
2841
|
}
|
|
2769
|
-
|
|
2770
|
-
}
|
|
2771
|
-
ClickEnd() {
|
|
2772
|
-
}
|
|
2773
|
-
Draw(ctx, position, scale, mousePosition) {
|
|
2774
|
-
const size = this.Size();
|
|
2775
|
-
const box = {
|
|
2776
|
-
Position: { x: 0, y: 0 },
|
|
2777
|
-
Size: {
|
|
2778
|
-
x: size.x * scale,
|
|
2779
|
-
y: size.y * scale
|
|
2780
|
-
}
|
|
2781
|
-
};
|
|
2782
|
-
CopyVector2(box.Position, position);
|
|
2842
|
+
render(ctx, box, scale) {
|
|
2783
2843
|
if (!this.#image) {
|
|
2784
|
-
return
|
|
2844
|
+
return;
|
|
2785
2845
|
}
|
|
2846
|
+
const size = this.Size();
|
|
2847
|
+
const w = size.x * scale;
|
|
2848
|
+
const h = size.y * scale;
|
|
2786
2849
|
ctx.drawImage(
|
|
2787
2850
|
this.#image,
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2851
|
+
box.Position.x + (box.Size.x - w) / 2,
|
|
2852
|
+
box.Position.y,
|
|
2853
|
+
w,
|
|
2854
|
+
h
|
|
2792
2855
|
);
|
|
2793
|
-
return box;
|
|
2794
2856
|
}
|
|
2795
2857
|
};
|
|
2796
2858
|
|
|
2797
2859
|
// src/widgets/text.ts
|
|
2798
|
-
var TextWidget = class {
|
|
2860
|
+
var TextWidget = class extends WidgetBase {
|
|
2799
2861
|
#value;
|
|
2800
|
-
#callback;
|
|
2801
|
-
#node;
|
|
2802
|
-
#nodeProperty;
|
|
2803
2862
|
#size;
|
|
2863
|
+
#styleConfig;
|
|
2864
|
+
#styledForTheme = -1;
|
|
2865
|
+
#followTheme() {
|
|
2866
|
+
if (this.#styledForTheme === themeVersion()) {
|
|
2867
|
+
return;
|
|
2868
|
+
}
|
|
2869
|
+
this.#styledForTheme = themeVersion();
|
|
2870
|
+
const style = this.#value.style();
|
|
2871
|
+
style.setColor(this.#styleConfig?.color ?? Theme.Widget.FontColor);
|
|
2872
|
+
style.setSize(this.#styleConfig?.size ?? Theme.Note.FontSize);
|
|
2873
|
+
}
|
|
2804
2874
|
constructor(node, config) {
|
|
2875
|
+
super();
|
|
2805
2876
|
this.#size = Zero();
|
|
2806
|
-
this.#
|
|
2807
|
-
this.#
|
|
2808
|
-
this.#
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
this.Set(newVal);
|
|
2818
|
-
});
|
|
2877
|
+
this.#styleConfig = config?.textBoxStyle;
|
|
2878
|
+
this.#value = new Text("", TextStyleFallback(config?.textBoxStyle, {}));
|
|
2879
|
+
this.#followTheme();
|
|
2880
|
+
this.setText(config?.value ?? "");
|
|
2881
|
+
const property = config?.property;
|
|
2882
|
+
if (property !== void 0) {
|
|
2883
|
+
const existing = node.getProperty(property);
|
|
2884
|
+
if (existing !== void 0 && existing !== null) {
|
|
2885
|
+
this.setText(String(existing));
|
|
2886
|
+
}
|
|
2887
|
+
node.addPropertyChangeListener(property, (_, next) => this.setText(String(next)));
|
|
2819
2888
|
}
|
|
2820
2889
|
}
|
|
2890
|
+
Cursor() {
|
|
2891
|
+
return "default" /* Default */;
|
|
2892
|
+
}
|
|
2821
2893
|
Size() {
|
|
2822
2894
|
return this.#size;
|
|
2823
2895
|
}
|
|
2824
|
-
|
|
2896
|
+
text() {
|
|
2897
|
+
return this.#value.get();
|
|
2898
|
+
}
|
|
2899
|
+
setText(value) {
|
|
2825
2900
|
if (this.#value.get() === value) {
|
|
2826
2901
|
return;
|
|
2827
2902
|
}
|
|
2828
2903
|
this.#value.set(value);
|
|
2829
|
-
|
|
2830
|
-
|
|
2904
|
+
requestRender();
|
|
2905
|
+
}
|
|
2906
|
+
render(ctx, box, scale) {
|
|
2907
|
+
this.#followTheme();
|
|
2908
|
+
this.#value.size(ctx, 1, this.#size);
|
|
2909
|
+
ctx.textAlign = "left";
|
|
2910
|
+
this.#value.render(ctx, scale, box.Position);
|
|
2911
|
+
}
|
|
2912
|
+
};
|
|
2913
|
+
|
|
2914
|
+
// src/widgets/row.ts
|
|
2915
|
+
var RowWidget = class extends WidgetBase {
|
|
2916
|
+
#children;
|
|
2917
|
+
#grow;
|
|
2918
|
+
#gap;
|
|
2919
|
+
#boxes = [];
|
|
2920
|
+
#captured = null;
|
|
2921
|
+
#capturedBox = null;
|
|
2922
|
+
#mouse;
|
|
2923
|
+
constructor(children, config) {
|
|
2924
|
+
super();
|
|
2925
|
+
this.#children = children;
|
|
2926
|
+
this.#grow = config?.grow ?? children.map(() => 1);
|
|
2927
|
+
this.#gap = config?.gap ?? Theme.Widget.Gap;
|
|
2928
|
+
}
|
|
2929
|
+
children() {
|
|
2930
|
+
return this.#children;
|
|
2931
|
+
}
|
|
2932
|
+
Size() {
|
|
2933
|
+
const shown = this.#shown();
|
|
2934
|
+
let x = 0;
|
|
2935
|
+
let y = 0;
|
|
2936
|
+
for (const child of shown) {
|
|
2937
|
+
const size = child.Size();
|
|
2938
|
+
x += size.x;
|
|
2939
|
+
y = Math.max(y, size.y);
|
|
2940
|
+
}
|
|
2941
|
+
x += Math.max(0, shown.length - 1) * this.#gap;
|
|
2942
|
+
return { x, y };
|
|
2943
|
+
}
|
|
2944
|
+
Cursor() {
|
|
2945
|
+
const over = this.#captured ?? this.#childAt(this.#mouse);
|
|
2946
|
+
return over?.Cursor?.() ?? "default" /* Default */;
|
|
2947
|
+
}
|
|
2948
|
+
Draw(ctx, box, scale, mousePosition) {
|
|
2949
|
+
this.#mouse = mousePosition;
|
|
2950
|
+
super.Draw(ctx, box, scale, mousePosition);
|
|
2951
|
+
}
|
|
2952
|
+
render(ctx, box, scale) {
|
|
2953
|
+
const shown = this.#shown();
|
|
2954
|
+
const gap = this.#gap * scale;
|
|
2955
|
+
const natural = shown.reduce((sum, child) => sum + child.Size().x * scale, 0);
|
|
2956
|
+
const extra = Math.max(0, box.Size.x - natural - gap * Math.max(0, shown.length - 1));
|
|
2957
|
+
const totalGrow = shown.reduce((sum, child) => sum + this.#growOf(child), 0);
|
|
2958
|
+
this.#boxes = this.#children.map(() => null);
|
|
2959
|
+
let x = box.Position.x;
|
|
2960
|
+
for (const child of shown) {
|
|
2961
|
+
const size = child.Size();
|
|
2962
|
+
const share = totalGrow > 0 ? extra * (this.#growOf(child) / totalGrow) : 0;
|
|
2963
|
+
const childBox = {
|
|
2964
|
+
Position: { x, y: box.Position.y + (box.Size.y - size.y * scale) / 2 },
|
|
2965
|
+
Size: { x: size.x * scale + share, y: size.y * scale }
|
|
2966
|
+
};
|
|
2967
|
+
this.#boxes[this.#children.indexOf(child)] = childBox;
|
|
2968
|
+
child.Draw(ctx, childBox, scale, this.#captured === null || this.#captured === child ? this.#mouse : void 0);
|
|
2969
|
+
x += childBox.Size.x + gap;
|
|
2831
2970
|
}
|
|
2832
|
-
|
|
2833
|
-
|
|
2971
|
+
}
|
|
2972
|
+
onPointerDown(pointer) {
|
|
2973
|
+
const child = this.#childAt(pointer.position);
|
|
2974
|
+
if (child === null || child.Disabled?.()) {
|
|
2975
|
+
return;
|
|
2834
2976
|
}
|
|
2977
|
+
this.#captured = child;
|
|
2978
|
+
this.#capturedBox = this.#boxes[this.#children.indexOf(child)];
|
|
2979
|
+
child.PointerDown?.(this.#forChild(pointer));
|
|
2835
2980
|
}
|
|
2836
|
-
|
|
2981
|
+
onPointerMove(pointer) {
|
|
2982
|
+
this.#captured?.PointerMove?.(this.#forChild(pointer));
|
|
2837
2983
|
}
|
|
2838
|
-
|
|
2984
|
+
onPointerUp(pointer) {
|
|
2985
|
+
const child = this.#captured;
|
|
2986
|
+
const forChild = this.#forChild(pointer);
|
|
2987
|
+
this.#captured = null;
|
|
2988
|
+
this.#capturedBox = null;
|
|
2989
|
+
child?.PointerUp?.(forChild);
|
|
2839
2990
|
}
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
Size: this.#size
|
|
2991
|
+
#forChild(pointer) {
|
|
2992
|
+
return {
|
|
2993
|
+
position: pointer.position,
|
|
2994
|
+
inside: this.#capturedBox !== null && InBox(this.#capturedBox, pointer.position)
|
|
2845
2995
|
};
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2996
|
+
}
|
|
2997
|
+
#childAt(position) {
|
|
2998
|
+
if (position === void 0) {
|
|
2999
|
+
return null;
|
|
3000
|
+
}
|
|
3001
|
+
for (let i = 0; i < this.#children.length; i++) {
|
|
3002
|
+
const box = this.#boxes[i];
|
|
3003
|
+
if (box && InBox(box, position)) {
|
|
3004
|
+
return this.#children[i];
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
return null;
|
|
3008
|
+
}
|
|
3009
|
+
#shown() {
|
|
3010
|
+
return this.#children.filter((child) => !child.Hidden?.());
|
|
3011
|
+
}
|
|
3012
|
+
#growOf(child) {
|
|
3013
|
+
return this.#grow[this.#children.indexOf(child)] ?? 1;
|
|
2851
3014
|
}
|
|
2852
3015
|
};
|
|
2853
3016
|
|
|
@@ -2867,6 +3030,9 @@ var WidgetFactory = class {
|
|
|
2867
3030
|
}
|
|
2868
3031
|
return builder(node, config);
|
|
2869
3032
|
}
|
|
3033
|
+
createFromConfig(node, config) {
|
|
3034
|
+
return this.create(node, config.type, config.config);
|
|
3035
|
+
}
|
|
2870
3036
|
};
|
|
2871
3037
|
var GlobalWidgetFactory = new WidgetFactory();
|
|
2872
3038
|
GlobalWidgetFactory.register("button", (node, config) => new ButtonWidget(config));
|
|
@@ -2877,6 +3043,10 @@ GlobalWidgetFactory.register("string", (node, config) => new StringWidget(node,
|
|
|
2877
3043
|
GlobalWidgetFactory.register("toggle", (node, config) => new ToggleWidget(node, config));
|
|
2878
3044
|
GlobalWidgetFactory.register("image", (node, config) => new ImageWidget(config));
|
|
2879
3045
|
GlobalWidgetFactory.register("text", (node, config) => new TextWidget(node, config));
|
|
3046
|
+
GlobalWidgetFactory.register("row", (node, config) => new RowWidget(
|
|
3047
|
+
(config?.widgets ?? []).map((child) => GlobalWidgetFactory.createFromConfig(node, child)),
|
|
3048
|
+
config
|
|
3049
|
+
));
|
|
2880
3050
|
|
|
2881
3051
|
// src/connection.ts
|
|
2882
3052
|
var BACKWARD_PORT_STUB = 30;
|
|
@@ -2961,7 +3131,7 @@ var Connection = class {
|
|
|
2961
3131
|
return;
|
|
2962
3132
|
}
|
|
2963
3133
|
if (this.#inNode !== null) {
|
|
2964
|
-
const inPortBox = this.#
|
|
3134
|
+
const inPortBox = this.#inSlotBox();
|
|
2965
3135
|
if (inPortBox === void 0) {
|
|
2966
3136
|
return;
|
|
2967
3137
|
}
|
|
@@ -3020,14 +3190,14 @@ var Connection = class {
|
|
|
3020
3190
|
this.clearInput();
|
|
3021
3191
|
this.clearOutput();
|
|
3022
3192
|
}
|
|
3023
|
-
setInput(node, portIndex, replace) {
|
|
3193
|
+
setInput(node, portIndex, replace, insertAt) {
|
|
3024
3194
|
this.#inNode = node;
|
|
3025
3195
|
this.#inNodePortIndex = portIndex;
|
|
3026
3196
|
const port = this.#inNode.inputPort(portIndex);
|
|
3027
3197
|
if (!!replace && port.connections().length > 0) {
|
|
3028
3198
|
port.replaceConnection(this, 0);
|
|
3029
3199
|
} else {
|
|
3030
|
-
port.addConnection(this);
|
|
3200
|
+
port.addConnection(this, insertAt);
|
|
3031
3201
|
}
|
|
3032
3202
|
}
|
|
3033
3203
|
setOutput(node, portIndex) {
|
|
@@ -3045,9 +3215,33 @@ var Connection = class {
|
|
|
3045
3215
|
this.#outNode = null;
|
|
3046
3216
|
this.#outNodePortIndex = -1;
|
|
3047
3217
|
}
|
|
3218
|
+
/**
|
|
3219
|
+
* The part of the input port this wire lands on: the whole port, or its
|
|
3220
|
+
* own slot on a port holding several wires.
|
|
3221
|
+
*/
|
|
3222
|
+
#inSlotBox() {
|
|
3223
|
+
if (this.#inNode === null) {
|
|
3224
|
+
return void 0;
|
|
3225
|
+
}
|
|
3226
|
+
const box = this.#inNode.inputPortPosition(this.#inNodePortIndex);
|
|
3227
|
+
const port = this.#inNode.inputPort(this.#inNodePortIndex);
|
|
3228
|
+
const slots = port.slots();
|
|
3229
|
+
if (box === void 0 || slots === 1) {
|
|
3230
|
+
return box;
|
|
3231
|
+
}
|
|
3232
|
+
const index = port.connections().indexOf(this);
|
|
3233
|
+
if (index === -1) {
|
|
3234
|
+
return box;
|
|
3235
|
+
}
|
|
3236
|
+
const height2 = box.Size.y / slots;
|
|
3237
|
+
return {
|
|
3238
|
+
Position: { x: box.Position.x, y: box.Position.y + index * height2 },
|
|
3239
|
+
Size: { x: box.Size.x, y: height2 }
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3048
3242
|
mouseOverPort(mousePosition) {
|
|
3049
3243
|
if (this.#inNode !== null) {
|
|
3050
|
-
const inPortBox = this.#
|
|
3244
|
+
const inPortBox = this.#inSlotBox();
|
|
3051
3245
|
if (inPortBox !== void 0 && InBox(inPortBox, mousePosition)) {
|
|
3052
3246
|
return this.#inNode.inputPort(this.#inNodePortIndex);
|
|
3053
3247
|
}
|
|
@@ -3124,7 +3318,10 @@ var Camera = class {
|
|
|
3124
3318
|
// src/organize.ts
|
|
3125
3319
|
var widthSpacing = 100;
|
|
3126
3320
|
var heightSpacing = 50;
|
|
3321
|
+
var laneSpacing = 25;
|
|
3322
|
+
var pieceSpacing = 150;
|
|
3127
3323
|
var orderingSweeps = 8;
|
|
3324
|
+
var placementSweeps = 8;
|
|
3128
3325
|
function topologicalOrder(sorting, upstream) {
|
|
3129
3326
|
const visited = /* @__PURE__ */ new Set();
|
|
3130
3327
|
const order = new Array();
|
|
@@ -3146,11 +3343,7 @@ function topologicalOrder(sorting, upstream) {
|
|
|
3146
3343
|
continue;
|
|
3147
3344
|
}
|
|
3148
3345
|
visited.add(top.node);
|
|
3149
|
-
const
|
|
3150
|
-
if (feeders === void 0) {
|
|
3151
|
-
continue;
|
|
3152
|
-
}
|
|
3153
|
-
for (const feeder of feeders) {
|
|
3346
|
+
for (const feeder of upstream.get(top.node) ?? []) {
|
|
3154
3347
|
if (!visited.has(feeder)) {
|
|
3155
3348
|
stack.push({ node: feeder, expanded: false });
|
|
3156
3349
|
}
|
|
@@ -3159,43 +3352,103 @@ function topologicalOrder(sorting, upstream) {
|
|
|
3159
3352
|
}
|
|
3160
3353
|
return order;
|
|
3161
3354
|
}
|
|
3355
|
+
function consumersOf(sorting, upstream) {
|
|
3356
|
+
const downstream = /* @__PURE__ */ new Map();
|
|
3357
|
+
for (const node of sorting) {
|
|
3358
|
+
downstream.set(node, []);
|
|
3359
|
+
}
|
|
3360
|
+
for (const node of sorting) {
|
|
3361
|
+
for (const feeder of upstream.get(node) ?? []) {
|
|
3362
|
+
const consumers = downstream.get(feeder);
|
|
3363
|
+
if (consumers !== void 0 && !consumers.includes(node)) {
|
|
3364
|
+
consumers.push(node);
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
return downstream;
|
|
3369
|
+
}
|
|
3162
3370
|
function assignLayers(sorting, upstream) {
|
|
3163
|
-
const
|
|
3371
|
+
const downstream = consumersOf(sorting, upstream);
|
|
3372
|
+
const order = topologicalOrder(sorting, downstream);
|
|
3164
3373
|
const placed = /* @__PURE__ */ new Set();
|
|
3165
|
-
const
|
|
3374
|
+
const rank = /* @__PURE__ */ new Map();
|
|
3166
3375
|
for (const node of order) {
|
|
3167
|
-
let
|
|
3168
|
-
for (const
|
|
3169
|
-
if (placed.has(
|
|
3170
|
-
|
|
3376
|
+
let r = 0;
|
|
3377
|
+
for (const consumer of downstream.get(node) ?? []) {
|
|
3378
|
+
if (placed.has(consumer)) {
|
|
3379
|
+
r = Math.max(r, rank.get(consumer) + 1);
|
|
3171
3380
|
}
|
|
3172
3381
|
}
|
|
3173
|
-
|
|
3382
|
+
rank.set(node, r);
|
|
3174
3383
|
placed.add(node);
|
|
3175
3384
|
}
|
|
3176
3385
|
let deepest = 0;
|
|
3177
|
-
for (const
|
|
3178
|
-
deepest = Math.max(deepest,
|
|
3386
|
+
for (const r of rank.values()) {
|
|
3387
|
+
deepest = Math.max(deepest, r);
|
|
3179
3388
|
}
|
|
3180
|
-
const
|
|
3181
|
-
for (
|
|
3182
|
-
|
|
3389
|
+
const layerOf = /* @__PURE__ */ new Map();
|
|
3390
|
+
for (const node of sorting) {
|
|
3391
|
+
layerOf.set(node, deepest - rank.get(node));
|
|
3183
3392
|
}
|
|
3184
|
-
for (const node of
|
|
3185
|
-
|
|
3393
|
+
for (const node of sorting) {
|
|
3394
|
+
const layer = layerOf.get(node);
|
|
3395
|
+
if ((downstream.get(node) ?? []).some((c) => layerOf.get(c) > layer)) {
|
|
3396
|
+
continue;
|
|
3397
|
+
}
|
|
3398
|
+
let pulled = -1;
|
|
3399
|
+
for (const feeder of upstream.get(node) ?? []) {
|
|
3400
|
+
const feederLayer = layerOf.get(feeder);
|
|
3401
|
+
if (feederLayer !== void 0 && feederLayer < layer) {
|
|
3402
|
+
pulled = Math.max(pulled, feederLayer + 1);
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
if (pulled >= 0) {
|
|
3406
|
+
layerOf.set(node, pulled);
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
const used = [...new Set(layerOf.values())].sort((a, b) => a - b);
|
|
3410
|
+
const compact = /* @__PURE__ */ new Map();
|
|
3411
|
+
used.forEach((layer, i) => compact.set(layer, i));
|
|
3412
|
+
const layers = used.map(() => new Array());
|
|
3413
|
+
for (const node of sorting) {
|
|
3414
|
+
const layer = compact.get(layerOf.get(node));
|
|
3415
|
+
layerOf.set(node, layer);
|
|
3416
|
+
layers[layer].push(node);
|
|
3186
3417
|
}
|
|
3187
3418
|
return { layers, layerOf };
|
|
3188
3419
|
}
|
|
3189
|
-
function
|
|
3420
|
+
function crossingsOf(layout, downstream) {
|
|
3190
3421
|
const slotOf = /* @__PURE__ */ new Map();
|
|
3191
|
-
const
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3422
|
+
for (const layer of layout.layers) {
|
|
3423
|
+
layer.forEach((node, slot) => slotOf.set(node, slot));
|
|
3424
|
+
}
|
|
3425
|
+
let total = 0;
|
|
3426
|
+
for (let i = 0; i + 1 < layout.layers.length; i++) {
|
|
3427
|
+
const spans = new Array();
|
|
3428
|
+
for (const node of layout.layers[i]) {
|
|
3429
|
+
for (const consumer of downstream.get(node) ?? []) {
|
|
3430
|
+
if (layout.layerOf.get(consumer) === i + 1) {
|
|
3431
|
+
spans.push([slotOf.get(node), slotOf.get(consumer)]);
|
|
3432
|
+
}
|
|
3195
3433
|
}
|
|
3196
3434
|
}
|
|
3197
|
-
|
|
3198
|
-
|
|
3435
|
+
for (let a = 0; a < spans.length; a++) {
|
|
3436
|
+
for (let b = a + 1; b < spans.length; b++) {
|
|
3437
|
+
if ((spans[a][0] - spans[b][0]) * (spans[a][1] - spans[b][1]) < 0) {
|
|
3438
|
+
total++;
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
return total;
|
|
3444
|
+
}
|
|
3445
|
+
function orderWithinLayers(layout, upstream, downstream) {
|
|
3446
|
+
const slotOf = /* @__PURE__ */ new Map();
|
|
3447
|
+
for (const layer of layout.layers) {
|
|
3448
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3449
|
+
slotOf.set(layer[i], i);
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3199
3452
|
const meanSlot = (node, neighbours, layer) => {
|
|
3200
3453
|
let total = 0;
|
|
3201
3454
|
let count = 0;
|
|
@@ -3215,22 +3468,377 @@ function orderWithinLayers(layout, upstream, downstream) {
|
|
|
3215
3468
|
if (adjacent < 0 || adjacent >= layout.layers.length) {
|
|
3216
3469
|
continue;
|
|
3217
3470
|
}
|
|
3218
|
-
const layer = layout.layers[index];
|
|
3219
|
-
const keys = /* @__PURE__ */ new Map();
|
|
3220
|
-
for (const node of layer) {
|
|
3221
|
-
const mean = meanSlot(node, neighbours, adjacent);
|
|
3222
|
-
keys.set(node, mean === -1 ? slotOf.get(node) : mean);
|
|
3471
|
+
const layer = layout.layers[index];
|
|
3472
|
+
const keys = /* @__PURE__ */ new Map();
|
|
3473
|
+
for (const node of layer) {
|
|
3474
|
+
const mean = meanSlot(node, neighbours, adjacent);
|
|
3475
|
+
keys.set(node, mean === -1 ? slotOf.get(node) : mean);
|
|
3476
|
+
}
|
|
3477
|
+
layer.sort((a, b) => keys.get(a) - keys.get(b));
|
|
3478
|
+
for (let s = 0; s < layer.length; s++) {
|
|
3479
|
+
slotOf.set(layer[s], s);
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
};
|
|
3483
|
+
let best = layout.layers.map((layer) => layer.slice());
|
|
3484
|
+
let fewest = crossingsOf(layout, downstream);
|
|
3485
|
+
for (let i = 0; i < orderingSweeps && fewest > 0; i++) {
|
|
3486
|
+
sweep(upstream, -1);
|
|
3487
|
+
sweep(downstream, 1);
|
|
3488
|
+
const crossings = crossingsOf(layout, downstream);
|
|
3489
|
+
if (crossings < fewest) {
|
|
3490
|
+
fewest = crossings;
|
|
3491
|
+
best = layout.layers.map((layer) => layer.slice());
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
for (let i = 0; i < best.length; i++) {
|
|
3495
|
+
layout.layers[i] = best[i];
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
function findPieces(sorting, upstream, downstream) {
|
|
3499
|
+
const rankOf = /* @__PURE__ */ new Map();
|
|
3500
|
+
sorting.forEach((node, i) => rankOf.set(node, i));
|
|
3501
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3502
|
+
const pieces = new Array();
|
|
3503
|
+
for (const start of sorting) {
|
|
3504
|
+
if (seen.has(start)) {
|
|
3505
|
+
continue;
|
|
3506
|
+
}
|
|
3507
|
+
seen.add(start);
|
|
3508
|
+
const piece = new Array();
|
|
3509
|
+
const stack = [start];
|
|
3510
|
+
while (stack.length > 0) {
|
|
3511
|
+
const node = stack.pop();
|
|
3512
|
+
piece.push(node);
|
|
3513
|
+
for (const neighbour of [...upstream.get(node) ?? [], ...downstream.get(node) ?? []]) {
|
|
3514
|
+
if (rankOf.has(neighbour) && !seen.has(neighbour)) {
|
|
3515
|
+
seen.add(neighbour);
|
|
3516
|
+
stack.push(neighbour);
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
piece.sort((a, b) => rankOf.get(a) - rankOf.get(b));
|
|
3521
|
+
pieces.push(piece);
|
|
3522
|
+
}
|
|
3523
|
+
return pieces;
|
|
3524
|
+
}
|
|
3525
|
+
function treeRoot(piece, downstream) {
|
|
3526
|
+
let root;
|
|
3527
|
+
for (const node of piece) {
|
|
3528
|
+
const consumers = downstream.get(node) ?? [];
|
|
3529
|
+
if (consumers.length > 1) {
|
|
3530
|
+
return void 0;
|
|
3531
|
+
}
|
|
3532
|
+
if (consumers.length === 0) {
|
|
3533
|
+
if (root !== void 0) {
|
|
3534
|
+
return void 0;
|
|
3535
|
+
}
|
|
3536
|
+
root = node;
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
return root;
|
|
3540
|
+
}
|
|
3541
|
+
function columnXs(widths) {
|
|
3542
|
+
const xs = new Array();
|
|
3543
|
+
let x = 0;
|
|
3544
|
+
for (const width2 of widths) {
|
|
3545
|
+
xs.push(x);
|
|
3546
|
+
x += width2 + widthSpacing;
|
|
3547
|
+
}
|
|
3548
|
+
return xs;
|
|
3549
|
+
}
|
|
3550
|
+
function layoutTree(root, upstream, sizes) {
|
|
3551
|
+
const children = (node) => [...new Set(upstream.get(node) ?? [])];
|
|
3552
|
+
const depthOf = /* @__PURE__ */ new Map([[root, 0]]);
|
|
3553
|
+
const preorder = new Array();
|
|
3554
|
+
const stack = [root];
|
|
3555
|
+
let deepest = 0;
|
|
3556
|
+
while (stack.length > 0) {
|
|
3557
|
+
const node = stack.pop();
|
|
3558
|
+
preorder.push(node);
|
|
3559
|
+
const kids = children(node);
|
|
3560
|
+
for (let i = kids.length - 1; i >= 0; i--) {
|
|
3561
|
+
const depth = depthOf.get(node) + 1;
|
|
3562
|
+
depthOf.set(kids[i], depth);
|
|
3563
|
+
deepest = Math.max(deepest, depth);
|
|
3564
|
+
stack.push(kids[i]);
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
const widths = new Array(deepest + 1).fill(0);
|
|
3568
|
+
for (const [node, depth] of depthOf) {
|
|
3569
|
+
widths[deepest - depth] = Math.max(widths[deepest - depth], sizes.get(node).x);
|
|
3570
|
+
}
|
|
3571
|
+
const xs = columnXs(widths);
|
|
3572
|
+
const band = /* @__PURE__ */ new Map();
|
|
3573
|
+
const topInBand = /* @__PURE__ */ new Map();
|
|
3574
|
+
const childOffset = /* @__PURE__ */ new Map();
|
|
3575
|
+
for (let i = preorder.length - 1; i >= 0; i--) {
|
|
3576
|
+
const node = preorder[i];
|
|
3577
|
+
const height2 = sizes.get(node).y;
|
|
3578
|
+
const kids = children(node);
|
|
3579
|
+
if (kids.length === 0) {
|
|
3580
|
+
band.set(node, height2);
|
|
3581
|
+
topInBand.set(node, 0);
|
|
3582
|
+
continue;
|
|
3583
|
+
}
|
|
3584
|
+
let offset = 0;
|
|
3585
|
+
for (const kid of kids) {
|
|
3586
|
+
childOffset.set(kid, offset);
|
|
3587
|
+
offset += band.get(kid) + heightSpacing;
|
|
3588
|
+
}
|
|
3589
|
+
const centre = (kid) => childOffset.get(kid) + topInBand.get(kid) + sizes.get(kid).y / 2;
|
|
3590
|
+
let top = (centre(kids[0]) + centre(kids[kids.length - 1])) / 2 - height2 / 2;
|
|
3591
|
+
let shift = 0;
|
|
3592
|
+
if (top < 0) {
|
|
3593
|
+
shift = -top;
|
|
3594
|
+
top = 0;
|
|
3595
|
+
for (const kid of kids) {
|
|
3596
|
+
childOffset.set(kid, childOffset.get(kid) + shift);
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
band.set(node, Math.max(offset - heightSpacing + shift, top + height2));
|
|
3600
|
+
topInBand.set(node, top);
|
|
3601
|
+
}
|
|
3602
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3603
|
+
const bandTop = /* @__PURE__ */ new Map([[root, 0]]);
|
|
3604
|
+
for (const node of preorder) {
|
|
3605
|
+
const column = deepest - depthOf.get(node);
|
|
3606
|
+
const size = sizes.get(node);
|
|
3607
|
+
positions.set(node, {
|
|
3608
|
+
x: xs[column] + (widths[column] - size.x) / 2,
|
|
3609
|
+
y: bandTop.get(node) + topInBand.get(node)
|
|
3610
|
+
});
|
|
3611
|
+
for (const kid of children(node)) {
|
|
3612
|
+
bandTop.set(kid, bandTop.get(node) + childOffset.get(kid));
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
return positions;
|
|
3616
|
+
}
|
|
3617
|
+
function isotonic(targets) {
|
|
3618
|
+
const blocks = new Array();
|
|
3619
|
+
for (const target of targets) {
|
|
3620
|
+
blocks.push({ sum: target, count: 1 });
|
|
3621
|
+
while (blocks.length > 1) {
|
|
3622
|
+
const last = blocks[blocks.length - 1];
|
|
3623
|
+
const before = blocks[blocks.length - 2];
|
|
3624
|
+
if (before.sum / before.count <= last.sum / last.count) {
|
|
3625
|
+
break;
|
|
3626
|
+
}
|
|
3627
|
+
before.sum += last.sum;
|
|
3628
|
+
before.count += last.count;
|
|
3629
|
+
blocks.pop();
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
const result = new Array();
|
|
3633
|
+
for (const block of blocks) {
|
|
3634
|
+
for (let i = 0; i < block.count; i++) {
|
|
3635
|
+
result.push(block.sum / block.count);
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
return result;
|
|
3639
|
+
}
|
|
3640
|
+
function median(values) {
|
|
3641
|
+
const sorted = values.slice().sort((a, b) => a - b);
|
|
3642
|
+
const middle = Math.floor(sorted.length / 2);
|
|
3643
|
+
return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
3644
|
+
}
|
|
3645
|
+
function layoutLayered(piece, upstream, downstream, sizes) {
|
|
3646
|
+
const layout = assignLayers(piece, upstream);
|
|
3647
|
+
const up = /* @__PURE__ */ new Map();
|
|
3648
|
+
const down = /* @__PURE__ */ new Map();
|
|
3649
|
+
const heightOf = /* @__PURE__ */ new Map();
|
|
3650
|
+
for (const node of piece) {
|
|
3651
|
+
up.set(node, [...new Set(upstream.get(node) ?? [])]);
|
|
3652
|
+
down.set(node, [...new Set(downstream.get(node) ?? [])]);
|
|
3653
|
+
heightOf.set(node, sizes.get(node).y);
|
|
3654
|
+
}
|
|
3655
|
+
let nextLane = -1;
|
|
3656
|
+
for (const from of piece) {
|
|
3657
|
+
const fromLayer = layout.layerOf.get(from);
|
|
3658
|
+
const consumers = down.get(from);
|
|
3659
|
+
for (let c = 0; c < consumers.length; c++) {
|
|
3660
|
+
const to = consumers[c];
|
|
3661
|
+
const toLayer = layout.layerOf.get(to);
|
|
3662
|
+
if (toLayer - fromLayer < 2) {
|
|
3663
|
+
continue;
|
|
3664
|
+
}
|
|
3665
|
+
let previous = from;
|
|
3666
|
+
for (let layer = fromLayer + 1; layer < toLayer; layer++) {
|
|
3667
|
+
const lane = nextLane--;
|
|
3668
|
+
layout.layerOf.set(lane, layer);
|
|
3669
|
+
layout.layers[layer].push(lane);
|
|
3670
|
+
heightOf.set(lane, 0);
|
|
3671
|
+
up.set(lane, [previous]);
|
|
3672
|
+
down.set(lane, []);
|
|
3673
|
+
if (previous === from) {
|
|
3674
|
+
consumers[c] = lane;
|
|
3675
|
+
} else {
|
|
3676
|
+
down.get(previous).push(lane);
|
|
3677
|
+
}
|
|
3678
|
+
previous = lane;
|
|
3679
|
+
}
|
|
3680
|
+
down.get(previous).push(to);
|
|
3681
|
+
const feeders = up.get(to);
|
|
3682
|
+
feeders[feeders.indexOf(from)] = previous;
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
const sequence = /* @__PURE__ */ new Map();
|
|
3686
|
+
for (const sink of piece) {
|
|
3687
|
+
if (down.get(sink).length > 0) {
|
|
3688
|
+
continue;
|
|
3689
|
+
}
|
|
3690
|
+
const stack = [sink];
|
|
3691
|
+
while (stack.length > 0) {
|
|
3692
|
+
const node = stack.pop();
|
|
3693
|
+
if (sequence.has(node)) {
|
|
3694
|
+
continue;
|
|
3695
|
+
}
|
|
3696
|
+
sequence.set(node, sequence.size);
|
|
3697
|
+
const feeders = up.get(node) ?? [];
|
|
3698
|
+
for (let i = feeders.length - 1; i >= 0; i--) {
|
|
3699
|
+
stack.push(feeders[i]);
|
|
3223
3700
|
}
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
for (const layer of layout.layers) {
|
|
3704
|
+
layer.sort((a, b) => (sequence.get(a) ?? Infinity) - (sequence.get(b) ?? Infinity));
|
|
3705
|
+
}
|
|
3706
|
+
orderWithinLayers(layout, up, down);
|
|
3707
|
+
const gapAfter = (above, below) => above < 0 || below < 0 ? laneSpacing : heightSpacing;
|
|
3708
|
+
const topOf = /* @__PURE__ */ new Map();
|
|
3709
|
+
for (const layer of layout.layers) {
|
|
3710
|
+
let y = 0;
|
|
3711
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3712
|
+
topOf.set(layer[i], y);
|
|
3713
|
+
y += heightOf.get(layer[i]) + (i + 1 < layer.length ? gapAfter(layer[i], layer[i + 1]) : 0);
|
|
3714
|
+
}
|
|
3715
|
+
for (const node of layer) {
|
|
3716
|
+
topOf.set(node, topOf.get(node) - y / 2);
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
const centreOf = (node) => topOf.get(node) + heightOf.get(node) / 2;
|
|
3720
|
+
const place = (layer, neighbours, adjacent) => {
|
|
3721
|
+
const offsets = new Array();
|
|
3722
|
+
const targets = new Array();
|
|
3723
|
+
let offset = 0;
|
|
3724
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3725
|
+
const node = layer[i];
|
|
3726
|
+
const centres = (neighbours.get(node) ?? []).filter((n) => layout.layerOf.get(n) === adjacent).map(centreOf);
|
|
3727
|
+
const desired = centres.length > 0 ? median(centres) - heightOf.get(node) / 2 : topOf.get(node);
|
|
3728
|
+
offsets.push(offset);
|
|
3729
|
+
targets.push(desired - offset);
|
|
3730
|
+
if (i + 1 < layer.length) {
|
|
3731
|
+
offset += heightOf.get(node) + gapAfter(node, layer[i + 1]);
|
|
3227
3732
|
}
|
|
3228
3733
|
}
|
|
3734
|
+
const solved = isotonic(targets);
|
|
3735
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3736
|
+
topOf.set(layer[i], solved[i] + offsets[i]);
|
|
3737
|
+
}
|
|
3229
3738
|
};
|
|
3230
|
-
for (let
|
|
3231
|
-
|
|
3232
|
-
|
|
3739
|
+
for (let s = 0; s < placementSweeps; s++) {
|
|
3740
|
+
if (s % 2 === 0) {
|
|
3741
|
+
for (let i = 1; i < layout.layers.length; i++) {
|
|
3742
|
+
place(layout.layers[i], up, i - 1);
|
|
3743
|
+
}
|
|
3744
|
+
} else {
|
|
3745
|
+
for (let i = layout.layers.length - 2; i >= 0; i--) {
|
|
3746
|
+
place(layout.layers[i], down, i + 1);
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
const widths = layout.layers.map((layer) => layer.reduce((widest, node) => node < 0 ? widest : Math.max(widest, sizes.get(node).x), 0));
|
|
3751
|
+
const xs = columnXs(widths);
|
|
3752
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3753
|
+
for (const node of piece) {
|
|
3754
|
+
const layer = layout.layerOf.get(node);
|
|
3755
|
+
positions.set(node, {
|
|
3756
|
+
x: xs[layer] + (widths[layer] - sizes.get(node).x) / 2,
|
|
3757
|
+
y: topOf.get(node)
|
|
3758
|
+
});
|
|
3233
3759
|
}
|
|
3760
|
+
return positions;
|
|
3761
|
+
}
|
|
3762
|
+
function layoutLoose(loose, sizes) {
|
|
3763
|
+
const columns = Math.ceil(Math.sqrt(loose.length));
|
|
3764
|
+
const widths = new Array(columns).fill(0);
|
|
3765
|
+
const heights = new Array(Math.ceil(loose.length / columns)).fill(0);
|
|
3766
|
+
loose.forEach((node, i) => {
|
|
3767
|
+
const size = sizes.get(node);
|
|
3768
|
+
widths[i % columns] = Math.max(widths[i % columns], size.x);
|
|
3769
|
+
heights[Math.floor(i / columns)] = Math.max(heights[Math.floor(i / columns)], size.y);
|
|
3770
|
+
});
|
|
3771
|
+
const xs = new Array();
|
|
3772
|
+
let x = 0;
|
|
3773
|
+
for (const width2 of widths) {
|
|
3774
|
+
xs.push(x);
|
|
3775
|
+
x += width2 + heightSpacing;
|
|
3776
|
+
}
|
|
3777
|
+
const ys = new Array();
|
|
3778
|
+
let y = 0;
|
|
3779
|
+
for (const height2 of heights) {
|
|
3780
|
+
ys.push(y);
|
|
3781
|
+
y += height2 + heightSpacing;
|
|
3782
|
+
}
|
|
3783
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3784
|
+
loose.forEach((node, i) => positions.set(node, { x: xs[i % columns], y: ys[Math.floor(i / columns)] }));
|
|
3785
|
+
return positions;
|
|
3786
|
+
}
|
|
3787
|
+
function toBlock(positions, sizes) {
|
|
3788
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
3789
|
+
for (const [node, position] of positions) {
|
|
3790
|
+
const size = sizes.get(node);
|
|
3791
|
+
minX = Math.min(minX, position.x);
|
|
3792
|
+
minY = Math.min(minY, position.y);
|
|
3793
|
+
maxX = Math.max(maxX, position.x + size.x);
|
|
3794
|
+
maxY = Math.max(maxY, position.y + size.y);
|
|
3795
|
+
}
|
|
3796
|
+
const shifted = /* @__PURE__ */ new Map();
|
|
3797
|
+
for (const [node, position] of positions) {
|
|
3798
|
+
shifted.set(node, { x: position.x - minX, y: position.y - minY });
|
|
3799
|
+
}
|
|
3800
|
+
return { positions: shifted, size: { x: maxX - minX, y: maxY - minY } };
|
|
3801
|
+
}
|
|
3802
|
+
function layoutGraph(sorting, upstream, downstream, sizes) {
|
|
3803
|
+
const blocks = new Array();
|
|
3804
|
+
const loose = new Array();
|
|
3805
|
+
for (const piece of findPieces(sorting, upstream, downstream)) {
|
|
3806
|
+
if (piece.length === 1) {
|
|
3807
|
+
loose.push(piece[0]);
|
|
3808
|
+
continue;
|
|
3809
|
+
}
|
|
3810
|
+
const root = treeRoot(piece, downstream);
|
|
3811
|
+
const positions2 = root !== void 0 ? layoutTree(root, upstream, sizes) : layoutLayered(piece, upstream, downstream, sizes);
|
|
3812
|
+
blocks.push(toBlock(positions2, sizes));
|
|
3813
|
+
}
|
|
3814
|
+
blocks.sort((a, b) => b.size.y - a.size.y || b.size.x - a.size.x);
|
|
3815
|
+
if (loose.length > 0) {
|
|
3816
|
+
blocks.push(toBlock(layoutLoose(loose, sizes), sizes));
|
|
3817
|
+
}
|
|
3818
|
+
let area = 0;
|
|
3819
|
+
let widest = 0;
|
|
3820
|
+
for (const block of blocks) {
|
|
3821
|
+
area += (block.size.x + pieceSpacing) * (block.size.y + pieceSpacing);
|
|
3822
|
+
widest = Math.max(widest, block.size.x);
|
|
3823
|
+
}
|
|
3824
|
+
const rowWidth = Math.max(widest, Math.sqrt(area));
|
|
3825
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3826
|
+
let x = 0;
|
|
3827
|
+
let y = 0;
|
|
3828
|
+
let rowHeight = 0;
|
|
3829
|
+
for (const block of blocks) {
|
|
3830
|
+
if (x > 0 && x + block.size.x > rowWidth) {
|
|
3831
|
+
x = 0;
|
|
3832
|
+
y += rowHeight + pieceSpacing;
|
|
3833
|
+
rowHeight = 0;
|
|
3834
|
+
}
|
|
3835
|
+
for (const [node, position] of block.positions) {
|
|
3836
|
+
positions.set(node, { x: position.x + x, y: position.y + y });
|
|
3837
|
+
}
|
|
3838
|
+
x += block.size.x + pieceSpacing;
|
|
3839
|
+
rowHeight = Math.max(rowHeight, block.size.y);
|
|
3840
|
+
}
|
|
3841
|
+
return positions;
|
|
3234
3842
|
}
|
|
3235
3843
|
function Organize(ctx, graph, nodesToSort) {
|
|
3236
3844
|
const nodes = graph.getNodes();
|
|
@@ -3239,9 +3847,6 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3239
3847
|
}
|
|
3240
3848
|
const sorting = new Array();
|
|
3241
3849
|
if (nodesToSort) {
|
|
3242
|
-
if (nodesToSort.length < 2) {
|
|
3243
|
-
return;
|
|
3244
|
-
}
|
|
3245
3850
|
for (const index of nodesToSort) {
|
|
3246
3851
|
if (index >= 0 && index < nodes.length) {
|
|
3247
3852
|
sorting.push(index);
|
|
@@ -3261,33 +3866,28 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3261
3866
|
nodeLUT.set(nodes[i], i);
|
|
3262
3867
|
}
|
|
3263
3868
|
const camera = new Camera();
|
|
3264
|
-
const
|
|
3869
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
3265
3870
|
for (const index of sorting) {
|
|
3266
|
-
|
|
3871
|
+
sizes.set(index, nodes[index].calculateBounds(ctx, camera).Size);
|
|
3267
3872
|
}
|
|
3268
3873
|
const upstream = /* @__PURE__ */ new Map();
|
|
3269
|
-
const downstream = /* @__PURE__ */ new Map();
|
|
3270
3874
|
for (const index of sorting) {
|
|
3875
|
+
const node = nodes[index];
|
|
3271
3876
|
const feeders = new Array();
|
|
3272
|
-
for (
|
|
3273
|
-
const
|
|
3274
|
-
|
|
3275
|
-
|
|
3877
|
+
for (let i = 0; i < node.inputs(); i++) {
|
|
3878
|
+
for (const connection of node.inputPort(i).connections()) {
|
|
3879
|
+
const feeder = connection.outNode();
|
|
3880
|
+
const feederIndex = feeder === null ? void 0 : nodeLUT.get(feeder);
|
|
3881
|
+
if (feederIndex !== void 0 && inLayout.has(feederIndex) && feederIndex !== index && !feeders.includes(feederIndex)) {
|
|
3882
|
+
feeders.push(feederIndex);
|
|
3883
|
+
}
|
|
3276
3884
|
}
|
|
3277
3885
|
}
|
|
3278
3886
|
upstream.set(index, feeders);
|
|
3279
|
-
const consumers = new Array();
|
|
3280
|
-
for (const consumer of graph.connectedOutputsNodeReferences(index)) {
|
|
3281
|
-
const consumerIndex = nodeLUT.get(consumer);
|
|
3282
|
-
if (consumerIndex !== void 0 && inLayout.has(consumerIndex) && consumerIndex !== index) {
|
|
3283
|
-
consumers.push(consumerIndex);
|
|
3284
|
-
}
|
|
3285
|
-
}
|
|
3286
|
-
downstream.set(index, consumers);
|
|
3287
3887
|
}
|
|
3288
|
-
const
|
|
3289
|
-
|
|
3290
|
-
|
|
3888
|
+
const downstream = consumersOf(sorting, upstream);
|
|
3889
|
+
const positions = layoutGraph(sorting, upstream, downstream, sizes);
|
|
3890
|
+
const anchor = { x: 0, y: 0 };
|
|
3291
3891
|
for (const index of sorting) {
|
|
3292
3892
|
const position = nodes[index].getPosition();
|
|
3293
3893
|
anchor.x += position.x;
|
|
@@ -3295,19 +3895,14 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3295
3895
|
}
|
|
3296
3896
|
anchor.x /= sorting.length;
|
|
3297
3897
|
anchor.y /= sorting.length;
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
const
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
const size = bounds.get(node).Size;
|
|
3307
|
-
nodes[node].setPosition({ x: x + (layerWidths[i] - size.x) / 2, y });
|
|
3308
|
-
y += size.y + heightSpacing;
|
|
3309
|
-
}
|
|
3310
|
-
x += layerWidths[i] + widthSpacing;
|
|
3898
|
+
let right = 0;
|
|
3899
|
+
let bottom = 0;
|
|
3900
|
+
for (const [index, position] of positions) {
|
|
3901
|
+
right = Math.max(right, position.x + sizes.get(index).x);
|
|
3902
|
+
bottom = Math.max(bottom, position.y + sizes.get(index).y);
|
|
3903
|
+
}
|
|
3904
|
+
for (const [index, position] of positions) {
|
|
3905
|
+
nodes[index].setPosition({ x: anchor.x - right / 2 + position.x, y: anchor.y - bottom / 2 + position.y });
|
|
3311
3906
|
}
|
|
3312
3907
|
}
|
|
3313
3908
|
|
|
@@ -3499,14 +4094,17 @@ var NodeSubsystem2 = class {
|
|
|
3499
4094
|
#portHovering;
|
|
3500
4095
|
#widgetHovering;
|
|
3501
4096
|
#widgetCurrentlyClicking;
|
|
4097
|
+
#widgetHoveringBox = null;
|
|
4098
|
+
#capturedWidgetBox = null;
|
|
4099
|
+
#capturedPointer = Zero();
|
|
3502
4100
|
#cursor;
|
|
3503
4101
|
#nodeFactory;
|
|
3504
4102
|
#boxSelect;
|
|
3505
4103
|
#boxSelectStart_graphSpace;
|
|
3506
4104
|
#boxSelectEnd_graphSpace;
|
|
3507
4105
|
#boxSelectionNodes;
|
|
3508
|
-
#boxSelectStyle;
|
|
3509
4106
|
#lastCtx;
|
|
4107
|
+
#lastMousePosition;
|
|
3510
4108
|
// LISTENERS ==============================================================
|
|
3511
4109
|
#nodeRemovedCallbacks;
|
|
3512
4110
|
#registeredNodeAddedCallbacks;
|
|
@@ -3531,14 +4129,6 @@ var NodeSubsystem2 = class {
|
|
|
3531
4129
|
this.#boxSelectEnd_graphSpace = Zero();
|
|
3532
4130
|
this.#boxSelectionNodes = new List();
|
|
3533
4131
|
this.#lastCtx = null;
|
|
3534
|
-
this.#boxSelectStyle = new BoxStyle({
|
|
3535
|
-
border: {
|
|
3536
|
-
color: Theme.BoxSelect.Color,
|
|
3537
|
-
size: Theme.BoxSelect.Size
|
|
3538
|
-
},
|
|
3539
|
-
color: "rgba(0,0,0,0)",
|
|
3540
|
-
radius: Theme.BoxSelect.Radius
|
|
3541
|
-
});
|
|
3542
4132
|
}
|
|
3543
4133
|
addPublisher(identifier, publisher) {
|
|
3544
4134
|
this.#nodeFactory.addPublisher(identifier, publisher);
|
|
@@ -3575,6 +4165,7 @@ var NodeSubsystem2 = class {
|
|
|
3575
4165
|
}
|
|
3576
4166
|
if (intersection.Widget !== void 0) {
|
|
3577
4167
|
this.#widgetHovering = intersection.Widget;
|
|
4168
|
+
this.#widgetHoveringBox = intersection.WidgetBox ?? null;
|
|
3578
4169
|
}
|
|
3579
4170
|
if (intersection.Port !== void 0 && intersection.Node !== void 0 && intersection.PortIndex !== void 0 && intersection.PortIsInput !== void 0) {
|
|
3580
4171
|
this.#portHovering = {
|
|
@@ -3597,8 +4188,15 @@ var NodeSubsystem2 = class {
|
|
|
3597
4188
|
hoveringSomething = true;
|
|
3598
4189
|
}
|
|
3599
4190
|
if (this.#widgetHovering !== null) {
|
|
3600
|
-
this.#widgetHovering.
|
|
3601
|
-
|
|
4191
|
+
if (!this.#widgetHovering.Disabled?.()) {
|
|
4192
|
+
this.#widgetCurrentlyClicking = this.#widgetHovering;
|
|
4193
|
+
this.#capturedWidgetBox = this.#widgetHoveringBox === null ? null : {
|
|
4194
|
+
Position: { x: this.#widgetHoveringBox.Position.x, y: this.#widgetHoveringBox.Position.y },
|
|
4195
|
+
Size: { x: this.#widgetHoveringBox.Size.x, y: this.#widgetHoveringBox.Size.y }
|
|
4196
|
+
};
|
|
4197
|
+
CopyVector2(this.#capturedPointer, mousePosition);
|
|
4198
|
+
this.#widgetCurrentlyClicking.PointerDown?.(this.#widgetPointer());
|
|
4199
|
+
}
|
|
3602
4200
|
hoveringSomething = true;
|
|
3603
4201
|
}
|
|
3604
4202
|
if (this.#portHovering === null) {
|
|
@@ -3613,12 +4211,12 @@ var NodeSubsystem2 = class {
|
|
|
3613
4211
|
return hoveringSomething || ctrlKey;
|
|
3614
4212
|
}
|
|
3615
4213
|
if (this.#portHovering.InputPort) {
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
4214
|
+
const port = this.#portHovering.Port;
|
|
4215
|
+
const wires = port.connections();
|
|
4216
|
+
const picked = wires.find((wire) => wire.mouseOverPort(mousePosition) === port) ?? wires[0];
|
|
4217
|
+
if (picked !== void 0) {
|
|
4218
|
+
picked.clearInput();
|
|
4219
|
+
this.#connectionSelected = picked;
|
|
3622
4220
|
}
|
|
3623
4221
|
} else {
|
|
3624
4222
|
let inNode = this.#portHovering.Node;
|
|
@@ -3638,7 +4236,19 @@ var NodeSubsystem2 = class {
|
|
|
3638
4236
|
}
|
|
3639
4237
|
return true;
|
|
3640
4238
|
}
|
|
4239
|
+
#widgetPointer() {
|
|
4240
|
+
return {
|
|
4241
|
+
position: { x: this.#capturedPointer.x, y: this.#capturedPointer.y },
|
|
4242
|
+
inside: this.#capturedWidgetBox !== null && InBox(this.#capturedWidgetBox, this.#capturedPointer)
|
|
4243
|
+
};
|
|
4244
|
+
}
|
|
3641
4245
|
mouseDragEvent(delta, scale) {
|
|
4246
|
+
if (this.#widgetCurrentlyClicking !== null) {
|
|
4247
|
+
this.#capturedPointer.x += delta.x;
|
|
4248
|
+
this.#capturedPointer.y += delta.y;
|
|
4249
|
+
this.#widgetCurrentlyClicking.PointerMove?.(this.#widgetPointer());
|
|
4250
|
+
return true;
|
|
4251
|
+
}
|
|
3642
4252
|
let nodeMoved = false;
|
|
3643
4253
|
VectorPool.run(() => {
|
|
3644
4254
|
const scaledDelta = VectorPool.get();
|
|
@@ -3757,13 +4367,20 @@ var NodeSubsystem2 = class {
|
|
|
3757
4367
|
}
|
|
3758
4368
|
#firstCompatiblePort(node, side, against) {
|
|
3759
4369
|
const count = side === "INPUT" /* Input */ ? node.inputs() : node.outputs();
|
|
4370
|
+
let firstHidden = -1;
|
|
3760
4371
|
for (let i = 0; i < count; i++) {
|
|
3761
4372
|
const port = side === "INPUT" /* Input */ ? node.inputPort(i) : node.outputPort(i);
|
|
3762
|
-
if (portsCompatible(port, against)) {
|
|
4373
|
+
if (!portsCompatible(port, against)) {
|
|
4374
|
+
continue;
|
|
4375
|
+
}
|
|
4376
|
+
if (!port.hidden()) {
|
|
3763
4377
|
return i;
|
|
3764
4378
|
}
|
|
4379
|
+
if (firstHidden === -1) {
|
|
4380
|
+
firstHidden = i;
|
|
4381
|
+
}
|
|
3765
4382
|
}
|
|
3766
|
-
return
|
|
4383
|
+
return firstHidden;
|
|
3767
4384
|
}
|
|
3768
4385
|
clickEnd() {
|
|
3769
4386
|
for (let i = 0; i < this.#nodesGrabbed.Count(); i++) {
|
|
@@ -3779,10 +4396,11 @@ var NodeSubsystem2 = class {
|
|
|
3779
4396
|
this.#boxSelect = false;
|
|
3780
4397
|
}
|
|
3781
4398
|
if (this.#widgetCurrentlyClicking !== null) {
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
}
|
|
4399
|
+
const widget = this.#widgetCurrentlyClicking;
|
|
4400
|
+
const pointer = this.#widgetPointer();
|
|
3785
4401
|
this.#widgetCurrentlyClicking = null;
|
|
4402
|
+
this.#capturedWidgetBox = null;
|
|
4403
|
+
widget.PointerUp?.(pointer);
|
|
3786
4404
|
}
|
|
3787
4405
|
if (this.#connectionSelected === null) {
|
|
3788
4406
|
return;
|
|
@@ -3827,7 +4445,7 @@ var NodeSubsystem2 = class {
|
|
|
3827
4445
|
inputPort.connections()[0].clearOutput();
|
|
3828
4446
|
this.#removeConnection(inputPort.connections()[0], false);
|
|
3829
4447
|
}
|
|
3830
|
-
conn.setInput(this.#portHovering.Node, this.#portHovering.Index, replace);
|
|
4448
|
+
conn.setInput(this.#portHovering.Node, this.#portHovering.Index, replace, this.#insertIndex());
|
|
3831
4449
|
} else {
|
|
3832
4450
|
conn.setOutput(this.#portHovering.Node, this.#portHovering.Index);
|
|
3833
4451
|
}
|
|
@@ -4199,7 +4817,8 @@ var NodeSubsystem2 = class {
|
|
|
4199
4817
|
}
|
|
4200
4818
|
if (intersection.Widget !== void 0) {
|
|
4201
4819
|
this.#widgetHovering = intersection.Widget;
|
|
4202
|
-
this.#
|
|
4820
|
+
this.#widgetHoveringBox = intersection.WidgetBox ?? null;
|
|
4821
|
+
this.#cursor = intersection.Widget.Disabled?.() ? "default" /* Default */ : intersection.Widget.Cursor?.() ?? "pointer" /* Pointer */;
|
|
4203
4822
|
}
|
|
4204
4823
|
if (intersection.Port !== void 0 && intersection.Node !== void 0 && intersection.PortIndex !== void 0 && intersection.PortIsInput !== void 0) {
|
|
4205
4824
|
this.#portHovering = {
|
|
@@ -4222,6 +4841,9 @@ var NodeSubsystem2 = class {
|
|
|
4222
4841
|
}
|
|
4223
4842
|
this.#nodes[i].render(ctx, camera, state, mousePosition, this.#postProcessPass);
|
|
4224
4843
|
}
|
|
4844
|
+
if (this.#widgetCurrentlyClicking !== null) {
|
|
4845
|
+
this.#cursor = this.#widgetCurrentlyClicking.Cursor?.() ?? "pointer" /* Pointer */;
|
|
4846
|
+
}
|
|
4225
4847
|
}
|
|
4226
4848
|
#boxSelectionScreenspaceBox(camera) {
|
|
4227
4849
|
const box = { Position: Zero(), Size: Zero() };
|
|
@@ -4230,29 +4852,174 @@ var NodeSubsystem2 = class {
|
|
|
4230
4852
|
SubVector2(box.Size, box.Size, box.Position);
|
|
4231
4853
|
return box;
|
|
4232
4854
|
}
|
|
4855
|
+
/**
|
|
4856
|
+
* Where a wire being dropped on the hovered array input would go, or
|
|
4857
|
+
* undefined when it is not over one.
|
|
4858
|
+
*/
|
|
4859
|
+
#insertIndex() {
|
|
4860
|
+
const hovering = this.#portHovering;
|
|
4861
|
+
if (hovering === null || !hovering.InputPort || this.#lastMousePosition === void 0 || hovering.Port.getPortType() !== "INPUTARRAY" /* InputArray */) {
|
|
4862
|
+
return void 0;
|
|
4863
|
+
}
|
|
4864
|
+
const box = hovering.Node.inputPortPosition(hovering.Index);
|
|
4865
|
+
return arrayInsertIndex(box, hovering.Port.connections().length, this.#lastMousePosition.y);
|
|
4866
|
+
}
|
|
4867
|
+
#renderInsertMarker(ctx, camera) {
|
|
4868
|
+
const hovering = this.#portHovering;
|
|
4869
|
+
if (this.#connectionSelected === null || this.#connectionSelected.inPort() !== null || hovering === null) {
|
|
4870
|
+
return;
|
|
4871
|
+
}
|
|
4872
|
+
const index = this.#insertIndex();
|
|
4873
|
+
const count = hovering.Port.connections().length;
|
|
4874
|
+
if (index === void 0 || count === 0) {
|
|
4875
|
+
return;
|
|
4876
|
+
}
|
|
4877
|
+
const box = hovering.Node.inputPortPosition(hovering.Index);
|
|
4878
|
+
const y = box.Position.y + index * box.Size.y / count;
|
|
4879
|
+
const overhang = 6 * camera.zoom;
|
|
4880
|
+
ctx.save();
|
|
4881
|
+
ctx.strokeStyle = "#ffffff";
|
|
4882
|
+
ctx.lineWidth = Math.max(1, 2 * camera.zoom);
|
|
4883
|
+
ctx.beginPath();
|
|
4884
|
+
ctx.moveTo(box.Position.x - overhang, y);
|
|
4885
|
+
ctx.lineTo(box.Position.x + box.Size.x + overhang, y);
|
|
4886
|
+
ctx.stroke();
|
|
4887
|
+
ctx.restore();
|
|
4888
|
+
}
|
|
4233
4889
|
render(ctx, camera, mousePosition) {
|
|
4234
4890
|
this.#lastCtx = ctx;
|
|
4891
|
+
this.#lastMousePosition = mousePosition;
|
|
4235
4892
|
this.#cursor = "default" /* Default */;
|
|
4236
4893
|
const awaitingLayout = this.#nodes.some((node) => !node.hasBeenLaidOut());
|
|
4894
|
+
for (const node of this.#nodes) {
|
|
4895
|
+
if (node.hasBeenLaidOut()) {
|
|
4896
|
+
node.placePorts(node.calculateBounds(ctx, camera), camera.zoom);
|
|
4897
|
+
}
|
|
4898
|
+
}
|
|
4237
4899
|
TimeExecution("Render_Connections", () => {
|
|
4238
4900
|
this.#renderConnections(ctx, camera, mousePosition);
|
|
4239
4901
|
});
|
|
4240
4902
|
TimeExecution("Render_Nodes", () => {
|
|
4241
4903
|
this.#renderNodes(ctx, camera, mousePosition);
|
|
4242
4904
|
});
|
|
4905
|
+
this.#renderInsertMarker(ctx, camera);
|
|
4243
4906
|
if (awaitingLayout) {
|
|
4244
4907
|
requestRender();
|
|
4245
4908
|
}
|
|
4246
4909
|
if (this.#boxSelect) {
|
|
4247
4910
|
const box = this.#boxSelectionScreenspaceBox(camera);
|
|
4248
4911
|
ctx.setLineDash([Theme.BoxSelect.LineDashLength]);
|
|
4249
|
-
|
|
4912
|
+
new BoxStyle({
|
|
4913
|
+
border: { color: Theme.BoxSelect.Color, size: Theme.BoxSelect.Size },
|
|
4914
|
+
color: "rgba(0,0,0,0)",
|
|
4915
|
+
radius: Theme.BoxSelect.Radius
|
|
4916
|
+
}).Draw(ctx, box, 1);
|
|
4250
4917
|
ctx.setLineDash([]);
|
|
4251
4918
|
}
|
|
4252
4919
|
return { cursorStyle: this.#cursor };
|
|
4253
4920
|
}
|
|
4254
4921
|
};
|
|
4255
4922
|
|
|
4923
|
+
// src/popup.ts
|
|
4924
|
+
var Popup = class {
|
|
4925
|
+
#title;
|
|
4926
|
+
#options;
|
|
4927
|
+
#content;
|
|
4928
|
+
#onClose;
|
|
4929
|
+
// Styling ================================================================
|
|
4930
|
+
#buttonCSS;
|
|
4931
|
+
// Runtime ================================================================
|
|
4932
|
+
#popup;
|
|
4933
|
+
#keyHandler;
|
|
4934
|
+
constructor(config) {
|
|
4935
|
+
this.#title = config.title;
|
|
4936
|
+
this.#options = config.options;
|
|
4937
|
+
this.#content = config.content;
|
|
4938
|
+
this.#onClose = config.onClose;
|
|
4939
|
+
this.#buttonCSS = config.buttonCSS === void 0 ? "flex-grow: 1; margin-right: 8px;" : config.buttonCSS;
|
|
4940
|
+
this.#popup = null;
|
|
4941
|
+
this.#keyHandler = null;
|
|
4942
|
+
}
|
|
4943
|
+
Hide() {
|
|
4944
|
+
if (this.#popup === null) {
|
|
4945
|
+
return;
|
|
4946
|
+
}
|
|
4947
|
+
if (this.#keyHandler !== null) {
|
|
4948
|
+
document.removeEventListener("keydown", this.#keyHandler, true);
|
|
4949
|
+
this.#keyHandler = null;
|
|
4950
|
+
}
|
|
4951
|
+
document.body.removeChild(this.#popup);
|
|
4952
|
+
this.#popup = null;
|
|
4953
|
+
}
|
|
4954
|
+
#close(option) {
|
|
4955
|
+
this.Hide();
|
|
4956
|
+
if (this.#onClose !== void 0) {
|
|
4957
|
+
this.#onClose(option);
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
Show() {
|
|
4961
|
+
this.#popup = document.createElement("div");
|
|
4962
|
+
this.#popup.style.cssText = `position:absolute;top:0;width:100%;display:flex;height:100%;justify-content:center;align-items:center;background-color:${Theme.Popup.Backdrop};`;
|
|
4963
|
+
document.body.appendChild(this.#popup);
|
|
4964
|
+
const container = document.createElement("div");
|
|
4965
|
+
container.style.cssText = `background-color:${Theme.Popup.BackgroundColor};color:${Theme.Popup.FontColor};font-family:${Theme.Popup.FontFamily};border-radius:${Theme.Popup.BorderRadius}px;padding:16px;`;
|
|
4966
|
+
this.#popup.appendChild(container);
|
|
4967
|
+
const title = document.createElement("h2");
|
|
4968
|
+
title.textContent = this.#title;
|
|
4969
|
+
title.style.cssText = "margin-top:0;margin-bottom:16px;";
|
|
4970
|
+
container.appendChild(title);
|
|
4971
|
+
if (this.#content !== void 0) {
|
|
4972
|
+
container.appendChild(this.#content());
|
|
4973
|
+
}
|
|
4974
|
+
const buttonContainer = document.createElement("div");
|
|
4975
|
+
buttonContainer.style.cssText = "margin-top:16px;justify-content:space-around;align-items:center;flex-direction:row;display:flex;";
|
|
4976
|
+
container.appendChild(buttonContainer);
|
|
4977
|
+
for (let i = 0; i < this.#options.length; i++) {
|
|
4978
|
+
const button = document.createElement("button");
|
|
4979
|
+
button.style.cssText = this.#buttonCSS;
|
|
4980
|
+
button.textContent = this.#options[i];
|
|
4981
|
+
button.onclick = () => this.#close(this.#options[i]);
|
|
4982
|
+
buttonContainer.appendChild(button);
|
|
4983
|
+
}
|
|
4984
|
+
this.#keyHandler = (e) => {
|
|
4985
|
+
if (e.key === "Enter") {
|
|
4986
|
+
e.preventDefault();
|
|
4987
|
+
this.#close(this.#options.length > 0 ? this.#options[0] : null);
|
|
4988
|
+
} else if (e.key === "Escape") {
|
|
4989
|
+
e.preventDefault();
|
|
4990
|
+
this.#close(null);
|
|
4991
|
+
}
|
|
4992
|
+
e.stopPropagation();
|
|
4993
|
+
};
|
|
4994
|
+
document.addEventListener("keydown", this.#keyHandler, true);
|
|
4995
|
+
}
|
|
4996
|
+
};
|
|
4997
|
+
|
|
4998
|
+
// src/popups/string.ts
|
|
4999
|
+
function SetStringPopup(config) {
|
|
5000
|
+
let input = null;
|
|
5001
|
+
return new Popup({
|
|
5002
|
+
title: config.title,
|
|
5003
|
+
options: ["Set", "Cancel"],
|
|
5004
|
+
content: () => {
|
|
5005
|
+
const container = document.createElement("div");
|
|
5006
|
+
input = document.createElement("input");
|
|
5007
|
+
input.value = config.startingValue;
|
|
5008
|
+
container.append(input);
|
|
5009
|
+
return container;
|
|
5010
|
+
},
|
|
5011
|
+
onClose: (button) => {
|
|
5012
|
+
if (button !== "Set" || input === null) {
|
|
5013
|
+
if (config.onCancel) {
|
|
5014
|
+
config.onCancel();
|
|
5015
|
+
}
|
|
5016
|
+
return;
|
|
5017
|
+
}
|
|
5018
|
+
config.onUpdate(input.value);
|
|
5019
|
+
}
|
|
5020
|
+
});
|
|
5021
|
+
}
|
|
5022
|
+
|
|
4256
5023
|
// src/popups/form.ts
|
|
4257
5024
|
function FormPopup(config) {
|
|
4258
5025
|
let input = new Array();
|
|
@@ -4369,9 +5136,11 @@ var FlowNode = class {
|
|
|
4369
5136
|
#titleChangeCallback;
|
|
4370
5137
|
#infoChangeCallback;
|
|
4371
5138
|
// Styling ================================================================
|
|
5139
|
+
#styleConfig;
|
|
5140
|
+
#styledForTheme = -1;
|
|
4372
5141
|
#titleColor;
|
|
4373
5142
|
#selectedStyle;
|
|
4374
|
-
#stateStyles;
|
|
5143
|
+
#stateStyles = /* @__PURE__ */ new Map();
|
|
4375
5144
|
#padding;
|
|
4376
5145
|
#portTextStyle;
|
|
4377
5146
|
#elementSpacing;
|
|
@@ -4383,6 +5152,7 @@ var FlowNode = class {
|
|
|
4383
5152
|
#data;
|
|
4384
5153
|
#registeredAnyPropertyChangeCallbacks;
|
|
4385
5154
|
#registeredPropertyChangeCallbacks;
|
|
5155
|
+
#registeredPropertyEditCallbacks = /* @__PURE__ */ new Map();
|
|
4386
5156
|
constructor(config) {
|
|
4387
5157
|
this.#input = new Array();
|
|
4388
5158
|
this.#output = new Array();
|
|
@@ -4435,58 +5205,17 @@ var FlowNode = class {
|
|
|
4435
5205
|
this.#position = config?.position === void 0 ? { x: 0, y: 0 } : config.position;
|
|
4436
5206
|
this.#title = new Text(
|
|
4437
5207
|
config?.title === void 0 ? "" : config.title,
|
|
4438
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
4439
|
-
size: 16,
|
|
4440
|
-
weight: "bold" /* Bold */,
|
|
4441
|
-
color: Theme.Node.FontColor
|
|
4442
|
-
})
|
|
5208
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 16, weight: "bold" /* Bold */ })
|
|
4443
5209
|
);
|
|
4444
5210
|
this.#subTitle = new Text(
|
|
4445
5211
|
config?.subTitle === void 0 ? "" : config.subTitle,
|
|
4446
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
4447
|
-
size: 10,
|
|
4448
|
-
weight: "bold" /* Bold */,
|
|
4449
|
-
color: Theme.Node.FontColor
|
|
4450
|
-
})
|
|
4451
|
-
);
|
|
4452
|
-
this.#infoSymbol = new Text(
|
|
4453
|
-
"i",
|
|
4454
|
-
{
|
|
4455
|
-
size: 13,
|
|
4456
|
-
weight: "bold" /* Bold */,
|
|
4457
|
-
color: Theme.Node.FontColor
|
|
4458
|
-
}
|
|
5212
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 10, weight: "bold" /* Bold */ })
|
|
4459
5213
|
);
|
|
5214
|
+
this.#infoSymbol = new Text("i", { size: 13, weight: "bold" /* Bold */ });
|
|
4460
5215
|
this.#infoText = config?.info ? config?.info : "";
|
|
4461
|
-
this.#titleColor = config?.style?.title?.color === void 0 ? "#154050" : config?.style?.title?.color;
|
|
4462
5216
|
this.#padding = config?.style?.title?.padding === void 0 ? 15 : config?.style?.title?.padding;
|
|
4463
|
-
this.#
|
|
4464
|
-
this.#
|
|
4465
|
-
border: { color: Theme.Node.Border.Idle, size: 1 },
|
|
4466
|
-
radius: Theme.Node.BorderRadius,
|
|
4467
|
-
color: Theme.Node.BackgroundColor
|
|
4468
|
-
})));
|
|
4469
|
-
this.#stateStyles.set(1 /* MouseOver */, new BoxStyle(BoxStyleWithFallback(config?.style?.mouseOver, {
|
|
4470
|
-
border: { color: Theme.Node.Border.MouseOver, size: 1.1 },
|
|
4471
|
-
radius: Theme.Node.BorderRadius,
|
|
4472
|
-
color: Theme.Node.BackgroundColor
|
|
4473
|
-
})));
|
|
4474
|
-
this.#stateStyles.set(2 /* Grabbed */, new BoxStyle(BoxStyleWithFallback(config?.style?.grabbed, {
|
|
4475
|
-
border: { color: Theme.Node.Border.Grabbed, size: 2 },
|
|
4476
|
-
radius: Theme.Node.BorderRadius,
|
|
4477
|
-
color: Theme.Node.BackgroundColor
|
|
4478
|
-
})));
|
|
4479
|
-
this.#selectedStyle = new BoxStyle(BoxStyleWithFallback(config?.style?.selected, {
|
|
4480
|
-
border: { color: Theme.Node.Border.Selected, size: 1 },
|
|
4481
|
-
radius: Theme.Node.BorderRadius,
|
|
4482
|
-
color: Theme.Node.BackgroundColor
|
|
4483
|
-
}));
|
|
4484
|
-
this.#portTextStyle = new TextStyle({
|
|
4485
|
-
size: config?.style?.portText?.size === void 0 ? 14 : config?.style?.portText?.size,
|
|
4486
|
-
color: config?.style?.portText?.color === void 0 ? Theme.Node.Port.FontColor : config?.style?.portText?.color,
|
|
4487
|
-
font: config?.style?.portText?.font,
|
|
4488
|
-
weight: config?.style?.portText?.weight
|
|
4489
|
-
});
|
|
5217
|
+
this.#styleConfig = config?.style;
|
|
5218
|
+
this.#applyStyle();
|
|
4490
5219
|
if (config?.inputs !== void 0) {
|
|
4491
5220
|
for (let i = 0; i < config.inputs.length; i++) {
|
|
4492
5221
|
this.addInput(config.inputs[i]);
|
|
@@ -4498,12 +5227,8 @@ var FlowNode = class {
|
|
|
4498
5227
|
}
|
|
4499
5228
|
}
|
|
4500
5229
|
if (config?.widgets !== void 0) {
|
|
4501
|
-
for (
|
|
4502
|
-
|
|
4503
|
-
if (widget.type === void 0) {
|
|
4504
|
-
continue;
|
|
4505
|
-
}
|
|
4506
|
-
this.addWidget(GlobalWidgetFactory.create(this, widget.type, widget.config));
|
|
5230
|
+
for (const widget of config.widgets) {
|
|
5231
|
+
this.addWidget(GlobalWidgetFactory.createFromConfig(this, widget));
|
|
4507
5232
|
}
|
|
4508
5233
|
}
|
|
4509
5234
|
}
|
|
@@ -4576,6 +5301,32 @@ var FlowNode = class {
|
|
|
4576
5301
|
callbacks[i](oldValue, value);
|
|
4577
5302
|
}
|
|
4578
5303
|
}
|
|
5304
|
+
/**
|
|
5305
|
+
* Listens for a person changing the property through a widget, but not
|
|
5306
|
+
* for the property being set from code.
|
|
5307
|
+
*/
|
|
5308
|
+
addPropertyEditListener(name, callback) {
|
|
5309
|
+
let callbacks = this.#registeredPropertyEditCallbacks.get(name);
|
|
5310
|
+
if (callbacks === void 0) {
|
|
5311
|
+
callbacks = [];
|
|
5312
|
+
this.#registeredPropertyEditCallbacks.set(name, callbacks);
|
|
5313
|
+
}
|
|
5314
|
+
callbacks.push(callback);
|
|
5315
|
+
}
|
|
5316
|
+
/**
|
|
5317
|
+
* Sets the property as a person's finished edit, so edit listeners hear
|
|
5318
|
+
* it even when the value was already showing.
|
|
5319
|
+
*/
|
|
5320
|
+
editProperty(name, value) {
|
|
5321
|
+
this.setProperty(name, value);
|
|
5322
|
+
const callbacks = this.#registeredPropertyEditCallbacks.get(name);
|
|
5323
|
+
if (callbacks === void 0) {
|
|
5324
|
+
return;
|
|
5325
|
+
}
|
|
5326
|
+
for (const callback of [...callbacks]) {
|
|
5327
|
+
callback(value);
|
|
5328
|
+
}
|
|
5329
|
+
}
|
|
4579
5330
|
getProperty(name) {
|
|
4580
5331
|
return this.#data[name];
|
|
4581
5332
|
}
|
|
@@ -4864,6 +5615,7 @@ var FlowNode = class {
|
|
|
4864
5615
|
// return this.#titleTextStyle.measure(ctx, scale, this.#title);
|
|
4865
5616
|
// }
|
|
4866
5617
|
calculateBounds(ctx, camera) {
|
|
5618
|
+
this.#followTheme();
|
|
4867
5619
|
const tempMeasurement = Zero();
|
|
4868
5620
|
const doublePadding = this.#padding * 2;
|
|
4869
5621
|
const screenSpacePosition = Zero();
|
|
@@ -4877,25 +5629,21 @@ var FlowNode = class {
|
|
|
4877
5629
|
size.x += size.y * 4;
|
|
4878
5630
|
}
|
|
4879
5631
|
size.x += doublePadding;
|
|
4880
|
-
size.y += doublePadding
|
|
4881
|
-
for (
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
|
|
4886
|
-
}
|
|
4887
|
-
size.y += this.#elementSpacing * this.#output.length;
|
|
4888
|
-
for (let i = 0; i < this.#output.length; i++) {
|
|
4889
|
-
const port = this.#output[i];
|
|
5632
|
+
size.y += doublePadding;
|
|
5633
|
+
for (const port of [...this.#input, ...this.#output]) {
|
|
5634
|
+
if (port.hidden()) {
|
|
5635
|
+
continue;
|
|
5636
|
+
}
|
|
4890
5637
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
4891
|
-
size.y += tempMeasurement.y;
|
|
5638
|
+
size.y += this.#portRowHeight(port, tempMeasurement.y) + this.#elementSpacing;
|
|
4892
5639
|
size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
|
|
4893
5640
|
}
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
5641
|
+
for (const widget of this.#widgets) {
|
|
5642
|
+
if (widget.Hidden?.()) {
|
|
5643
|
+
continue;
|
|
5644
|
+
}
|
|
5645
|
+
const eleSize = widget.Size();
|
|
5646
|
+
size.y += eleSize.y + this.#elementSpacing;
|
|
4899
5647
|
size.x = Math.max(size.x, eleSize.x + doublePadding);
|
|
4900
5648
|
}
|
|
4901
5649
|
size.y += this.#elementSpacing;
|
|
@@ -4979,7 +5727,7 @@ var FlowNode = class {
|
|
|
4979
5727
|
intersection.Node = this;
|
|
4980
5728
|
}
|
|
4981
5729
|
for (let i = 0; i < this.#inputPortPositions.Count(); i++) {
|
|
4982
|
-
if (InBox(this.#inputPortPositions.At(i), position)) {
|
|
5730
|
+
if (!this.#input[i].hidden() && InBox(this.#inputPortPositions.At(i), position)) {
|
|
4983
5731
|
intersection.Node = this;
|
|
4984
5732
|
intersection.PortIndex = i;
|
|
4985
5733
|
intersection.PortIsInput = true;
|
|
@@ -4987,7 +5735,7 @@ var FlowNode = class {
|
|
|
4987
5735
|
}
|
|
4988
5736
|
}
|
|
4989
5737
|
for (let i = 0; i < this.#outputPortPositions.Count(); i++) {
|
|
4990
|
-
if (InBox(this.#outputPortPositions.At(i), position)) {
|
|
5738
|
+
if (!this.#output[i].hidden() && InBox(this.#outputPortPositions.At(i), position)) {
|
|
4991
5739
|
intersection.Node = this;
|
|
4992
5740
|
intersection.PortIndex = i;
|
|
4993
5741
|
intersection.PortIsInput = false;
|
|
@@ -4995,10 +5743,12 @@ var FlowNode = class {
|
|
|
4995
5743
|
}
|
|
4996
5744
|
}
|
|
4997
5745
|
for (let i = 0; i < this.#widgetPositions.Count(); i++) {
|
|
4998
|
-
|
|
5746
|
+
const widget = this.#widgets[i];
|
|
5747
|
+
if (widget !== void 0 && !widget.Hidden?.() && InBox(this.#widgetPositions.At(i), position)) {
|
|
4999
5748
|
intersection.Node = this;
|
|
5000
5749
|
intersection.WidgetIndex = i;
|
|
5001
|
-
intersection.Widget =
|
|
5750
|
+
intersection.Widget = widget;
|
|
5751
|
+
intersection.WidgetBox = this.#widgetPositions.At(i);
|
|
5002
5752
|
}
|
|
5003
5753
|
}
|
|
5004
5754
|
return intersection;
|
|
@@ -5047,6 +5797,12 @@ var FlowNode = class {
|
|
|
5047
5797
|
inputPortPosition(index) {
|
|
5048
5798
|
return this.#inputPortPositions.At(index);
|
|
5049
5799
|
}
|
|
5800
|
+
widgetPosition(index) {
|
|
5801
|
+
if (index < 0 || index >= this.#widgetPositions.Count()) {
|
|
5802
|
+
return void 0;
|
|
5803
|
+
}
|
|
5804
|
+
return this.#widgetPositions.At(index);
|
|
5805
|
+
}
|
|
5050
5806
|
inputPort(index) {
|
|
5051
5807
|
return this.#input[index];
|
|
5052
5808
|
}
|
|
@@ -5062,6 +5818,35 @@ var FlowNode = class {
|
|
|
5062
5818
|
outputs() {
|
|
5063
5819
|
return this.#output.length;
|
|
5064
5820
|
}
|
|
5821
|
+
#applyStyle() {
|
|
5822
|
+
const style = this.#styleConfig;
|
|
5823
|
+
const titleText = style?.title?.textStyle?.color ?? Theme.Node.Title.FontColor;
|
|
5824
|
+
this.#title.style().setColor(titleText);
|
|
5825
|
+
this.#subTitle.style().setColor(titleText);
|
|
5826
|
+
this.#infoSymbol.style().setColor(titleText);
|
|
5827
|
+
this.#titleColor = style?.title?.color ?? Theme.Node.Title.Color;
|
|
5828
|
+
const box = (config, border, size) => new BoxStyle(BoxStyleWithFallback(config, {
|
|
5829
|
+
border: { color: border, size },
|
|
5830
|
+
radius: Theme.Node.BorderRadius,
|
|
5831
|
+
color: Theme.Node.BackgroundColor
|
|
5832
|
+
}));
|
|
5833
|
+
this.#stateStyles.set(0 /* Idle */, box(style?.idle, Theme.Node.Border.Idle, 1));
|
|
5834
|
+
this.#stateStyles.set(1 /* MouseOver */, box(style?.mouseOver, Theme.Node.Border.MouseOver, 1.1));
|
|
5835
|
+
this.#stateStyles.set(2 /* Grabbed */, box(style?.grabbed, Theme.Node.Border.Grabbed, 2));
|
|
5836
|
+
this.#selectedStyle = box(style?.selected, Theme.Node.Border.Selected, 1);
|
|
5837
|
+
this.#portTextStyle = new TextStyle({
|
|
5838
|
+
size: style?.portText?.size ?? 14,
|
|
5839
|
+
color: style?.portText?.color ?? Theme.Node.Port.FontColor,
|
|
5840
|
+
font: style?.portText?.font,
|
|
5841
|
+
weight: style?.portText?.weight
|
|
5842
|
+
});
|
|
5843
|
+
this.#styledForTheme = themeVersion();
|
|
5844
|
+
}
|
|
5845
|
+
#followTheme() {
|
|
5846
|
+
if (this.#styledForTheme !== themeVersion()) {
|
|
5847
|
+
this.#applyStyle();
|
|
5848
|
+
}
|
|
5849
|
+
}
|
|
5065
5850
|
#calculateStyle(state) {
|
|
5066
5851
|
if (this.#selected && state === 0 /* Idle */) {
|
|
5067
5852
|
return this.#selectedStyle;
|
|
@@ -5078,9 +5863,22 @@ var FlowNode = class {
|
|
|
5078
5863
|
clearMessages() {
|
|
5079
5864
|
this.#messages = [];
|
|
5080
5865
|
}
|
|
5866
|
+
#portRowHeight(port, textHeight) {
|
|
5867
|
+
const slots = port.slots();
|
|
5868
|
+
return slots > 1 ? Math.max(textHeight, slots * ARRAY_SLOT_PITCH) : textHeight;
|
|
5869
|
+
}
|
|
5081
5870
|
hasBeenLaidOut() {
|
|
5082
5871
|
return this.#laidOut;
|
|
5083
5872
|
}
|
|
5873
|
+
/**
|
|
5874
|
+
* Throws away where the ports were last placed, so wires wait for the
|
|
5875
|
+
* next draw instead of pointing at the old spots.
|
|
5876
|
+
*/
|
|
5877
|
+
invalidateLayout() {
|
|
5878
|
+
this.#laidOut = false;
|
|
5879
|
+
this.#portOffsets = { input: [], output: [] };
|
|
5880
|
+
requestRender();
|
|
5881
|
+
}
|
|
5084
5882
|
#laidOut = false;
|
|
5085
5883
|
render(ctx, camera, state, mousePosition, postProcess) {
|
|
5086
5884
|
this.#laidOut = true;
|
|
@@ -5118,6 +5916,9 @@ var FlowNode = class {
|
|
|
5118
5916
|
[nodeStyle.radius() * camera.zoom, nodeStyle.radius() * camera.zoom, 0, 0]
|
|
5119
5917
|
);
|
|
5120
5918
|
ctx.fill();
|
|
5919
|
+
if (Theme.Node.Bevel !== null) {
|
|
5920
|
+
drawBevel(ctx, nodeBounds, camera.zoom, Theme.Node.Bevel, false);
|
|
5921
|
+
}
|
|
5121
5922
|
const titlePosition = VectorPool.get();
|
|
5122
5923
|
titlePosition.x = nodeBounds.Position.x + nodeBounds.Size.x / 2;
|
|
5123
5924
|
titlePosition.y = nodeBounds.Position.y + scaledPadding + titleSize.y / 2;
|
|
@@ -5178,8 +5979,12 @@ var FlowNode = class {
|
|
|
5178
5979
|
for (let i = 0; i < this.#input.length; i++) {
|
|
5179
5980
|
ctx.textAlign = "left" /* Left */;
|
|
5180
5981
|
const port = this.#input[i];
|
|
5982
|
+
if (port.hidden()) {
|
|
5983
|
+
this.#inputPortPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
|
|
5984
|
+
continue;
|
|
5985
|
+
}
|
|
5181
5986
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
5182
|
-
const portHeight = tempMeasurement.y * camera.zoom;
|
|
5987
|
+
const portHeight = this.#portRowHeight(port, tempMeasurement.y) * camera.zoom;
|
|
5183
5988
|
const position = VectorPool.get();
|
|
5184
5989
|
position.x = nodeBounds.Position.x;
|
|
5185
5990
|
position.y = startY + portHeight / 2;
|
|
@@ -5192,6 +5997,10 @@ var FlowNode = class {
|
|
|
5192
5997
|
ctx.textAlign = "right" /* Right */;
|
|
5193
5998
|
for (let i = 0; i < this.#output.length; i++) {
|
|
5194
5999
|
const port = this.#output[i];
|
|
6000
|
+
if (port.hidden()) {
|
|
6001
|
+
this.#outputPortPositions.Push({ Position: { x: rightSide, y: startY }, Size: { x: 0, y: 0 } });
|
|
6002
|
+
continue;
|
|
6003
|
+
}
|
|
5195
6004
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
5196
6005
|
const portHeight = tempMeasurement.y * camera.zoom;
|
|
5197
6006
|
const position = VectorPool.get();
|
|
@@ -5204,13 +6013,18 @@ var FlowNode = class {
|
|
|
5204
6013
|
}
|
|
5205
6014
|
for (let i = 0; i < this.#widgets.length; i++) {
|
|
5206
6015
|
const widget = this.#widgets[i];
|
|
6016
|
+
if (widget.Hidden?.()) {
|
|
6017
|
+
this.#widgetPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
|
|
6018
|
+
continue;
|
|
6019
|
+
}
|
|
5207
6020
|
const widgetSize = widget.Size();
|
|
5208
|
-
const
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
6021
|
+
const box = {
|
|
6022
|
+
Position: { x: nodeBounds.Position.x + scaledPadding, y: startY },
|
|
6023
|
+
Size: { x: nodeBounds.Size.x - scaledPadding * 2, y: widgetSize.y * camera.zoom }
|
|
6024
|
+
};
|
|
6025
|
+
widget.Draw(ctx, box, camera.zoom, mousePosition);
|
|
6026
|
+
this.#widgetPositions.Push(box);
|
|
6027
|
+
startY += box.Size.y + scaledElementSpacing;
|
|
5214
6028
|
}
|
|
5215
6029
|
const messageStart = VectorPool.get();
|
|
5216
6030
|
messageStart.x = nodeBounds.Position.x + nodeBounds.Size.x / 2;
|
|
@@ -5247,6 +6061,13 @@ var FlowNode = class {
|
|
|
5247
6061
|
record(this.#outputPortPositions, this.#portOffsets.output);
|
|
5248
6062
|
}
|
|
5249
6063
|
layoutPortsWithoutDrawing(bounds, zoom) {
|
|
6064
|
+
if (!this.placePorts(bounds, zoom)) {
|
|
6065
|
+
return false;
|
|
6066
|
+
}
|
|
6067
|
+
this.#widgetPositions.Clear();
|
|
6068
|
+
return true;
|
|
6069
|
+
}
|
|
6070
|
+
placePorts(bounds, zoom) {
|
|
5250
6071
|
if (this.#portOffsets.input.length === 0 && this.#portOffsets.output.length === 0) {
|
|
5251
6072
|
return false;
|
|
5252
6073
|
}
|
|
@@ -5268,7 +6089,6 @@ var FlowNode = class {
|
|
|
5268
6089
|
};
|
|
5269
6090
|
project(this.#portOffsets.input, this.#inputPortPositions);
|
|
5270
6091
|
project(this.#portOffsets.output, this.#outputPortPositions);
|
|
5271
|
-
this.#widgetPositions.Clear();
|
|
5272
6092
|
return true;
|
|
5273
6093
|
}
|
|
5274
6094
|
};
|
|
@@ -5460,33 +6280,33 @@ var CodeBlockEntry = class {
|
|
|
5460
6280
|
}
|
|
5461
6281
|
render(ctx, position, scale, maxWidth) {
|
|
5462
6282
|
this.#calculateLayout(ctx, maxWidth);
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
}
|
|
5469
|
-
const y = position.y + max + scale * 5;
|
|
6283
|
+
const padding = Theme.Note.CodeBlock.Padding * scale;
|
|
6284
|
+
const lineCount = this.#calculatedEntries.Count();
|
|
6285
|
+
const lastTop = lineCount === 0 ? 0 : this.#calculatedPositions.At(lineCount - 1).y;
|
|
6286
|
+
const contentHeight = lineCount === 0 ? 0 : (lastTop + this.#text.style().getSize()) * scale;
|
|
6287
|
+
const height2 = contentHeight + padding * 2;
|
|
5470
6288
|
ctx.fillStyle = Theme.Note.CodeBlock.BackgroundColor;
|
|
5471
6289
|
ctx.beginPath();
|
|
5472
6290
|
ctx.roundRect(
|
|
5473
6291
|
position.x,
|
|
5474
6292
|
position.y,
|
|
5475
6293
|
maxWidth * scale,
|
|
5476
|
-
|
|
6294
|
+
height2,
|
|
5477
6295
|
Theme.Note.CodeBlock.BorderRadius * scale
|
|
5478
6296
|
);
|
|
5479
6297
|
ctx.fill();
|
|
5480
|
-
|
|
6298
|
+
const baseline = ctx.textBaseline;
|
|
6299
|
+
ctx.textBaseline = "top";
|
|
6300
|
+
for (let i = 0; i < lineCount; i++) {
|
|
5481
6301
|
const entry = this.#calculatedEntries.At(i);
|
|
5482
6302
|
const pos = this.#calculatedPositions.At(i);
|
|
5483
6303
|
entry.render(ctx, scale, {
|
|
5484
6304
|
x: pos.x * scale + position.x + padding,
|
|
5485
6305
|
y: pos.y * scale + position.y + padding
|
|
5486
6306
|
});
|
|
5487
|
-
max = Math.max(max, pos.y * scale);
|
|
5488
6307
|
}
|
|
5489
|
-
|
|
6308
|
+
ctx.textBaseline = baseline;
|
|
6309
|
+
return height2;
|
|
5490
6310
|
}
|
|
5491
6311
|
};
|
|
5492
6312
|
var UnorderedListMarkdownEntry = class {
|
|
@@ -5864,9 +6684,10 @@ var MarkdownSyntaxParser = class {
|
|
|
5864
6684
|
this.#inc();
|
|
5865
6685
|
this.#inc();
|
|
5866
6686
|
this.#inc();
|
|
5867
|
-
const codeBlockText = this.#originalText.substring(start, end);
|
|
6687
|
+
const codeBlockText = this.#originalText.substring(start, end).replace(/\r?\n$/, "");
|
|
5868
6688
|
const entryText = new Text(codeBlockText);
|
|
5869
6689
|
this.#assignStandardStyling([entryText]);
|
|
6690
|
+
entryText.setColor(Theme.Note.CodeBlock.FontColor);
|
|
5870
6691
|
return new CodeBlockEntry(entryText);
|
|
5871
6692
|
}
|
|
5872
6693
|
parse() {
|
|
@@ -5921,6 +6742,7 @@ var BOX_SIZE = 10;
|
|
|
5921
6742
|
var FlowNote = class {
|
|
5922
6743
|
#originalText;
|
|
5923
6744
|
#document;
|
|
6745
|
+
#parsedForTheme = -1;
|
|
5924
6746
|
#width;
|
|
5925
6747
|
#edittingStyle;
|
|
5926
6748
|
// Runtime ================================================================
|
|
@@ -5963,6 +6785,7 @@ var FlowNote = class {
|
|
|
5963
6785
|
setText(text) {
|
|
5964
6786
|
this.#originalText = text;
|
|
5965
6787
|
this.#document = BuildMarkdown(this.#originalText);
|
|
6788
|
+
this.#parsedForTheme = themeVersion();
|
|
5966
6789
|
for (let i = 0; i < this.#contentChangeCallbacks.length; i++) {
|
|
5967
6790
|
this.#contentChangeCallbacks[i](this, text);
|
|
5968
6791
|
}
|
|
@@ -6050,6 +6873,10 @@ var FlowNote = class {
|
|
|
6050
6873
|
}
|
|
6051
6874
|
camera.graphSpaceToScreenSpace(this.#position, this.#tempPosition);
|
|
6052
6875
|
CopyVector2(this.#lastRenderedBox.Position, this.#tempPosition);
|
|
6876
|
+
if (this.#parsedForTheme !== themeVersion()) {
|
|
6877
|
+
this.#document = BuildMarkdown(this.#originalText);
|
|
6878
|
+
this.#parsedForTheme = themeVersion();
|
|
6879
|
+
}
|
|
6053
6880
|
const startY = this.#tempPosition.y;
|
|
6054
6881
|
const lineSpacing = Theme.Note.EntrySpacing * camera.zoom;
|
|
6055
6882
|
ctx.textAlign = "left" /* Left */;
|
|
@@ -6745,13 +7572,15 @@ var Minimap = class {
|
|
|
6745
7572
|
// src/graph.ts
|
|
6746
7573
|
function BuildBackgroundRenderer(backgroundColor) {
|
|
6747
7574
|
return (canvas, context, position, scale) => {
|
|
6748
|
-
context.fillStyle = backgroundColor;
|
|
7575
|
+
context.fillStyle = backgroundColor ?? Theme.Graph.BackgroundColor;
|
|
6749
7576
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
6750
|
-
const alpha =
|
|
7577
|
+
const alpha = Clamp01(scale - 0.3);
|
|
6751
7578
|
if (alpha <= 0) {
|
|
6752
7579
|
return;
|
|
6753
7580
|
}
|
|
6754
|
-
context.
|
|
7581
|
+
context.save();
|
|
7582
|
+
context.globalAlpha = alpha;
|
|
7583
|
+
context.fillStyle = Theme.Graph.DotColor;
|
|
6755
7584
|
const spacing = 100;
|
|
6756
7585
|
const pi2 = 2 * Math.PI;
|
|
6757
7586
|
const dotScale = 2 * scale;
|
|
@@ -6764,6 +7593,7 @@ function BuildBackgroundRenderer(backgroundColor) {
|
|
|
6764
7593
|
context.fill();
|
|
6765
7594
|
}
|
|
6766
7595
|
}
|
|
7596
|
+
context.restore();
|
|
6767
7597
|
};
|
|
6768
7598
|
}
|
|
6769
7599
|
var contextMenuGroup = "graph-context-menu";
|
|
@@ -6814,9 +7644,9 @@ var GraphView = class {
|
|
|
6814
7644
|
const results = {};
|
|
6815
7645
|
for (let i = 0; i < this.#subsystems.length; i++) {
|
|
6816
7646
|
TimeExecution("Render_Subsystem_" + i, () => {
|
|
6817
|
-
|
|
6818
|
-
if (
|
|
6819
|
-
|
|
7647
|
+
const layer = this.#subsystems[i].render(ctx, camera, mousePosition);
|
|
7648
|
+
if (layer?.cursorStyle) {
|
|
7649
|
+
results.cursorStyle = layer.cursorStyle;
|
|
6820
7650
|
}
|
|
6821
7651
|
});
|
|
6822
7652
|
}
|
|
@@ -6876,8 +7706,7 @@ var NodeFlowGraph = class {
|
|
|
6876
7706
|
if (config?.backgroundRenderer !== void 0) {
|
|
6877
7707
|
this.#backgroundRenderer = config?.backgroundRenderer;
|
|
6878
7708
|
} else {
|
|
6879
|
-
|
|
6880
|
-
this.#backgroundRenderer = BuildBackgroundRenderer(backgroundColor);
|
|
7709
|
+
this.#backgroundRenderer = BuildBackgroundRenderer(config?.backgroundColor);
|
|
6881
7710
|
}
|
|
6882
7711
|
this.#minimap = new Minimap(config?.minimap);
|
|
6883
7712
|
this.#mainNodeSubsystem.setConnectionReleasedHandler((dangling) => {
|
|
@@ -7295,10 +8124,12 @@ var NodeFlowGraph = class {
|
|
|
7295
8124
|
this.#cursor = "default" /* Default */;
|
|
7296
8125
|
TimeExecution("Render_Background", this.#renderBackground.bind(this));
|
|
7297
8126
|
TimeExecution("Render_View_" + this.#currentView, () => {
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
8127
|
+
placeEditsDuring(this.#canvas, () => {
|
|
8128
|
+
let results = this.currentView().render(this.#ctx, this.#camera, this.#mousePosition);
|
|
8129
|
+
if (results?.cursorStyle) {
|
|
8130
|
+
this.#cursor = results?.cursorStyle;
|
|
8131
|
+
}
|
|
8132
|
+
});
|
|
7302
8133
|
});
|
|
7303
8134
|
TimeExecution("Render_Minimap", () => {
|
|
7304
8135
|
this.#minimap.render(this.#ctx, this.#canvas, this.#camera, this.#mainNodeSubsystem.getNodes());
|
|
@@ -7344,11 +8175,15 @@ var NodeFlowGraph = class {
|
|
|
7344
8175
|
ColorWidget,
|
|
7345
8176
|
Connection,
|
|
7346
8177
|
ContextMenuItemState,
|
|
8178
|
+
CursorStyle,
|
|
7347
8179
|
DefaultConnectionRenderer,
|
|
8180
|
+
DefaultTheme,
|
|
7348
8181
|
FlowNode,
|
|
7349
8182
|
FlowNote,
|
|
7350
8183
|
GlobalWidgetFactory,
|
|
8184
|
+
GraphView,
|
|
7351
8185
|
ImageWidget,
|
|
8186
|
+
InBox,
|
|
7352
8187
|
MessageType,
|
|
7353
8188
|
Minimap,
|
|
7354
8189
|
NodeFactory,
|
|
@@ -7358,16 +8193,28 @@ var NodeFlowGraph = class {
|
|
|
7358
8193
|
Port,
|
|
7359
8194
|
PortType,
|
|
7360
8195
|
Publisher,
|
|
8196
|
+
RowWidget,
|
|
7361
8197
|
SliderWidget,
|
|
7362
8198
|
StringWidget,
|
|
7363
8199
|
TextWidget,
|
|
7364
8200
|
Theme,
|
|
7365
8201
|
ToggleWidget,
|
|
8202
|
+
ValueWidget,
|
|
8203
|
+
WidgetBase,
|
|
7366
8204
|
WidgetFactory,
|
|
8205
|
+
drawField,
|
|
8206
|
+
drawFieldBox,
|
|
8207
|
+
drawFieldText,
|
|
8208
|
+
editInPlace,
|
|
8209
|
+
formatNumber,
|
|
8210
|
+
onThemeChange,
|
|
7367
8211
|
portConfigTypes,
|
|
7368
8212
|
portsCompatible,
|
|
7369
8213
|
renderContinuously,
|
|
7370
8214
|
requestRender,
|
|
7371
|
-
|
|
8215
|
+
setTheme,
|
|
8216
|
+
themeVersion,
|
|
8217
|
+
typeSetsCompatible,
|
|
8218
|
+
widgetFont
|
|
7372
8219
|
});
|
|
7373
8220
|
//# sourceMappingURL=index.js.map
|