@elicdavis/node-flow 0.1.19 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +386 -227
- package/dist/index.d.ts +386 -227
- package/dist/index.js +1884 -1076
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1867 -1075
- package/dist/index.mjs.map +1 -1
- package/index.html +1134 -329
- package/package.json +1 -1
- package/src/connection.ts +30 -5
- package/src/contextMenu.ts +66 -34
- package/src/graph.ts +12 -10
- package/src/index.ts +26 -12
- package/src/markdown/entry.ts +11 -15
- package/src/markdown/syntaxParser.ts +2 -1
- package/src/node.ts +162 -111
- package/src/nodes/subsystem.ts +123 -29
- package/src/notes/note.ts +9 -1
- package/src/organize.ts +540 -111
- package/src/popup.ts +4 -2
- package/src/port.ts +83 -6
- package/src/styles/bevel.ts +31 -0
- package/src/styles/cursor.ts +1 -0
- package/src/styles/text.ts +4 -4
- package/src/theme.ts +179 -92
- package/src/types/text.ts +5 -1
- package/src/utils/color.ts +1 -1
- package/src/widgets/button.ts +52 -107
- package/src/widgets/color.ts +91 -150
- package/src/widgets/factory.ts +89 -46
- package/src/widgets/field.ts +99 -0
- package/src/widgets/image.ts +107 -165
- package/src/widgets/inplace.ts +84 -0
- package/src/widgets/number.ts +122 -154
- package/src/widgets/row.ts +138 -0
- package/src/widgets/slider.ts +101 -216
- package/src/widgets/string.ts +39 -130
- package/src/widgets/text.ts +81 -92
- package/src/widgets/toggle.ts +62 -207
- package/src/widgets/value.ts +127 -0
- package/src/widgets/widget.ts +180 -34
- package/test/context_menu.test.ts +82 -0
- package/test/cursor.test.ts +17 -0
- package/test/hidden_port.test.ts +125 -0
- package/test/hover_after_removal.test.ts +72 -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/property_binding.test.ts +49 -0
- package/test/support.ts +175 -0
- package/test/theme.test.ts +86 -0
- package/test/widget_click.test.ts +111 -97
- package/test/widget_construction.test.ts +5 -4
- package/test/widgets.test.ts +333 -0
- package/test/wire_follows_camera.test.ts +57 -0
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,331 +2050,480 @@ 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
|
+
function editInPlace(config) {
|
|
2167
|
+
const canvas = config.canvas;
|
|
2168
|
+
if (canvas === null) {
|
|
2169
|
+
return null;
|
|
2016
2170
|
}
|
|
2017
|
-
|
|
2171
|
+
const rect = canvas.getBoundingClientRect();
|
|
2172
|
+
const sx = canvas.width > 0 ? rect.width / canvas.width : 1;
|
|
2173
|
+
const sy = canvas.height > 0 ? rect.height / canvas.height : 1;
|
|
2174
|
+
const input = document.createElement("input");
|
|
2175
|
+
input.type = "text";
|
|
2176
|
+
input.value = config.value;
|
|
2177
|
+
input.style.cssText = [
|
|
2178
|
+
"position: fixed",
|
|
2179
|
+
`left: ${rect.left + config.box.Position.x * sx}px`,
|
|
2180
|
+
`top: ${rect.top + config.box.Position.y * sy}px`,
|
|
2181
|
+
`width: ${config.box.Size.x * sx}px`,
|
|
2182
|
+
`height: ${config.box.Size.y * sy}px`,
|
|
2183
|
+
"box-sizing: border-box",
|
|
2184
|
+
"margin: 0",
|
|
2185
|
+
`padding: 0 ${Theme.Widget.Padding * config.scale}px`,
|
|
2186
|
+
`font: ${Theme.Widget.FontSize * config.scale * sy}px ${Theme.FontFamily}`,
|
|
2187
|
+
`color: ${Theme.Widget.FontColor}`,
|
|
2188
|
+
`background: ${Theme.Widget.Hover.BackgroundColor}`,
|
|
2189
|
+
`border: ${Theme.Widget.Border.Size * config.scale}px solid ${Theme.Widget.Border.Color}`,
|
|
2190
|
+
`border-radius: ${Theme.Widget.Border.Radius * config.scale}px`,
|
|
2191
|
+
"outline: none",
|
|
2192
|
+
"z-index: 1000"
|
|
2193
|
+
].join(";");
|
|
2194
|
+
let done = false;
|
|
2195
|
+
const finish = (keep) => {
|
|
2196
|
+
if (done) {
|
|
2197
|
+
return;
|
|
2198
|
+
}
|
|
2199
|
+
done = true;
|
|
2200
|
+
input.remove();
|
|
2201
|
+
if (keep) {
|
|
2202
|
+
config.onCommit(input.value);
|
|
2203
|
+
}
|
|
2204
|
+
requestRender();
|
|
2205
|
+
};
|
|
2206
|
+
input.addEventListener("keydown", (event) => {
|
|
2207
|
+
event.stopPropagation();
|
|
2208
|
+
if (event.key === "Enter") {
|
|
2209
|
+
event.preventDefault();
|
|
2210
|
+
finish(true);
|
|
2211
|
+
} else if (event.key === "Escape") {
|
|
2212
|
+
event.preventDefault();
|
|
2213
|
+
finish(false);
|
|
2214
|
+
}
|
|
2215
|
+
});
|
|
2216
|
+
input.addEventListener("blur", () => finish(true));
|
|
2217
|
+
document.body.appendChild(input);
|
|
2218
|
+
input.focus();
|
|
2219
|
+
input.select();
|
|
2220
|
+
return input;
|
|
2221
|
+
}
|
|
2018
2222
|
|
|
2019
2223
|
// src/widgets/widget.ts
|
|
2020
2224
|
var width = 150;
|
|
2021
2225
|
var height = 25;
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2226
|
+
var WidgetBase = class {
|
|
2227
|
+
#hidden = false;
|
|
2228
|
+
#disabled = false;
|
|
2229
|
+
#hovered = false;
|
|
2230
|
+
#pressed = false;
|
|
2231
|
+
#box = { Position: { x: 0, y: 0 }, Size: { x: 0, y: 0 } };
|
|
2232
|
+
#canvas = null;
|
|
2233
|
+
#scale = 1;
|
|
2234
|
+
Draw(ctx, box, scale, mousePosition) {
|
|
2235
|
+
this.#box = { Position: { x: box.Position.x, y: box.Position.y }, Size: { x: box.Size.x, y: box.Size.y } };
|
|
2236
|
+
this.#canvas = ctx.canvas ?? null;
|
|
2237
|
+
this.#scale = scale;
|
|
2238
|
+
this.#hovered = !this.#disabled && mousePosition !== void 0 && InBox(box, mousePosition);
|
|
2239
|
+
ctx.save();
|
|
2240
|
+
if (this.#disabled) {
|
|
2241
|
+
ctx.globalAlpha *= 0.4;
|
|
2242
|
+
}
|
|
2243
|
+
this.render(ctx, box, scale);
|
|
2244
|
+
ctx.restore();
|
|
2245
|
+
}
|
|
2246
|
+
PointerDown(pointer) {
|
|
2247
|
+
if (this.#disabled) {
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
this.#pressed = true;
|
|
2251
|
+
this.onPointerDown(pointer);
|
|
2252
|
+
requestRender();
|
|
2025
2253
|
}
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
return existing;
|
|
2254
|
+
PointerMove(pointer) {
|
|
2255
|
+
if (!this.#pressed) {
|
|
2256
|
+
return;
|
|
2030
2257
|
}
|
|
2258
|
+
this.onPointerMove(pointer);
|
|
2259
|
+
requestRender();
|
|
2031
2260
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2261
|
+
PointerUp(pointer) {
|
|
2262
|
+
if (!this.#pressed) {
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
this.#pressed = false;
|
|
2266
|
+
this.onPointerUp(pointer);
|
|
2267
|
+
if (pointer.inside) {
|
|
2268
|
+
this.onClick(pointer);
|
|
2269
|
+
}
|
|
2270
|
+
requestRender();
|
|
2271
|
+
}
|
|
2272
|
+
Cursor() {
|
|
2273
|
+
return "pointer" /* Pointer */;
|
|
2274
|
+
}
|
|
2275
|
+
Hidden() {
|
|
2276
|
+
return this.#hidden;
|
|
2277
|
+
}
|
|
2278
|
+
setHidden(hidden) {
|
|
2279
|
+
this.#hidden = hidden;
|
|
2280
|
+
requestRender();
|
|
2281
|
+
}
|
|
2282
|
+
Disabled() {
|
|
2283
|
+
return this.#disabled;
|
|
2284
|
+
}
|
|
2285
|
+
setDisabled(disabled) {
|
|
2286
|
+
this.#disabled = disabled;
|
|
2287
|
+
if (disabled) {
|
|
2288
|
+
this.#pressed = false;
|
|
2289
|
+
}
|
|
2290
|
+
requestRender();
|
|
2291
|
+
}
|
|
2292
|
+
hovered() {
|
|
2293
|
+
return this.#hovered;
|
|
2294
|
+
}
|
|
2295
|
+
pressed() {
|
|
2296
|
+
return this.#pressed;
|
|
2297
|
+
}
|
|
2298
|
+
/**
|
|
2299
|
+
* Where the widget was last drawn, in canvas pixels.
|
|
2300
|
+
*/
|
|
2301
|
+
box() {
|
|
2302
|
+
return this.#box;
|
|
2303
|
+
}
|
|
2304
|
+
canvas() {
|
|
2305
|
+
return this.#canvas;
|
|
2306
|
+
}
|
|
2307
|
+
scale() {
|
|
2308
|
+
return this.#scale;
|
|
2309
|
+
}
|
|
2310
|
+
onPointerDown(pointer) {
|
|
2311
|
+
}
|
|
2312
|
+
onPointerMove(pointer) {
|
|
2313
|
+
}
|
|
2314
|
+
onPointerUp(pointer) {
|
|
2315
|
+
}
|
|
2316
|
+
/**
|
|
2317
|
+
* A press and release that both landed on the widget.
|
|
2318
|
+
*/
|
|
2319
|
+
onClick(pointer) {
|
|
2320
|
+
}
|
|
2321
|
+
};
|
|
2322
|
+
function startingWidgetValue(node, property, configured, fallback) {
|
|
2323
|
+
if (configured !== void 0) {
|
|
2324
|
+
return configured;
|
|
2325
|
+
}
|
|
2326
|
+
if (property !== void 0 && property !== null) {
|
|
2327
|
+
const existing = node.getProperty(property);
|
|
2328
|
+
if (existing !== void 0 && existing !== null) {
|
|
2329
|
+
return existing;
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
return fallback;
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
// src/widgets/value.ts
|
|
2336
|
+
var ValueWidget = class extends WidgetBase {
|
|
2037
2337
|
#node;
|
|
2038
|
-
#
|
|
2338
|
+
#property;
|
|
2039
2339
|
#value;
|
|
2040
|
-
#
|
|
2041
|
-
#
|
|
2042
|
-
#text;
|
|
2340
|
+
#label;
|
|
2341
|
+
#width;
|
|
2043
2342
|
#callback;
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2343
|
+
#onInput;
|
|
2344
|
+
constructor(node, config, fallback) {
|
|
2345
|
+
super();
|
|
2047
2346
|
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));
|
|
2347
|
+
this.#property = config?.property;
|
|
2348
|
+
this.#label = config?.label ?? "";
|
|
2349
|
+
this.#width = config?.width ?? width;
|
|
2350
|
+
this.#value = startingWidgetValue(node, this.#property, config?.value, fallback);
|
|
2072
2351
|
this.#callback = config?.callback;
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2352
|
+
this.#onInput = config?.onInput;
|
|
2353
|
+
if (this.#property !== void 0) {
|
|
2354
|
+
node.setProperty(this.#property, this.#value);
|
|
2355
|
+
node.addPropertyChangeListener(this.#property, (_, next) => this.setValue(next));
|
|
2077
2356
|
}
|
|
2078
2357
|
}
|
|
2079
2358
|
Size() {
|
|
2080
|
-
return {
|
|
2359
|
+
return { x: this.#width, y: height };
|
|
2360
|
+
}
|
|
2361
|
+
value() {
|
|
2362
|
+
return this.#value;
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* Shows a new value without treating it as a person's edit: no callback
|
|
2366
|
+
* and no edit event.
|
|
2367
|
+
*/
|
|
2368
|
+
setValue(value) {
|
|
2369
|
+
if (this.equal(value, this.#value)) {
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2372
|
+
this.#value = value;
|
|
2373
|
+
this.valueChanged();
|
|
2374
|
+
if (this.#property !== void 0) {
|
|
2375
|
+
this.#node.setProperty(this.#property, value);
|
|
2376
|
+
}
|
|
2377
|
+
requestRender();
|
|
2378
|
+
}
|
|
2379
|
+
label() {
|
|
2380
|
+
return this.#label;
|
|
2081
2381
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2382
|
+
setLabel(label) {
|
|
2383
|
+
this.#label = label;
|
|
2384
|
+
requestRender();
|
|
2385
|
+
}
|
|
2386
|
+
node() {
|
|
2387
|
+
return this.#node;
|
|
2388
|
+
}
|
|
2389
|
+
input(value) {
|
|
2390
|
+
this.setValue(value);
|
|
2391
|
+
this.#onInput?.(value);
|
|
2392
|
+
}
|
|
2393
|
+
/**
|
|
2394
|
+
* Ends a person's edit. Nothing is reported when they ended where they
|
|
2395
|
+
* started.
|
|
2396
|
+
*/
|
|
2397
|
+
commit(value, startedAt) {
|
|
2398
|
+
this.setValue(value);
|
|
2399
|
+
if (this.equal(value, startedAt)) {
|
|
2084
2400
|
return;
|
|
2085
2401
|
}
|
|
2086
|
-
this.#
|
|
2087
|
-
|
|
2088
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
2402
|
+
if (this.#property !== void 0) {
|
|
2403
|
+
this.#node.editProperty(this.#property, value);
|
|
2089
2404
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2405
|
+
this.#callback?.(value);
|
|
2406
|
+
}
|
|
2407
|
+
valueChanged() {
|
|
2408
|
+
}
|
|
2409
|
+
equal(a, b) {
|
|
2410
|
+
return Object.is(a, b);
|
|
2411
|
+
}
|
|
2412
|
+
};
|
|
2413
|
+
|
|
2414
|
+
// src/widgets/number.ts
|
|
2415
|
+
var DRAG_THRESHOLD = 3;
|
|
2416
|
+
var NumberWidget = class extends ValueWidget {
|
|
2417
|
+
#step;
|
|
2418
|
+
#integer;
|
|
2419
|
+
#min;
|
|
2420
|
+
#max;
|
|
2421
|
+
#startX = 0;
|
|
2422
|
+
#startValue = 0;
|
|
2423
|
+
#dragging = false;
|
|
2424
|
+
#dragged = false;
|
|
2425
|
+
constructor(node, config) {
|
|
2426
|
+
super(node, config, 0);
|
|
2427
|
+
this.#step = config?.step;
|
|
2428
|
+
this.#integer = config?.integer === true;
|
|
2429
|
+
this.#min = config?.min ?? -Infinity;
|
|
2430
|
+
this.#max = config?.max ?? Infinity;
|
|
2431
|
+
}
|
|
2432
|
+
Cursor() {
|
|
2433
|
+
return "ew-resize" /* EWResize */;
|
|
2434
|
+
}
|
|
2435
|
+
render(ctx, box, scale) {
|
|
2436
|
+
drawField(ctx, box, scale, {
|
|
2437
|
+
input: true,
|
|
2438
|
+
hovered: this.hovered() || this.pressed(),
|
|
2439
|
+
label: this.label(),
|
|
2440
|
+
value: formatNumber(this.value())
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
onPointerDown(pointer) {
|
|
2444
|
+
this.#startX = pointer.position.x;
|
|
2445
|
+
this.#startValue = this.value();
|
|
2446
|
+
this.#dragging = false;
|
|
2447
|
+
this.#dragged = false;
|
|
2448
|
+
}
|
|
2449
|
+
onPointerMove(pointer) {
|
|
2450
|
+
const dx = pointer.position.x - this.#startX;
|
|
2451
|
+
if (!this.#dragging && Math.abs(dx) < DRAG_THRESHOLD) {
|
|
2452
|
+
return;
|
|
2092
2453
|
}
|
|
2093
|
-
this.#
|
|
2454
|
+
this.#dragging = true;
|
|
2455
|
+
this.input(this.#clean(this.#startValue + dx * this.#stepFor(this.#startValue)));
|
|
2094
2456
|
}
|
|
2095
|
-
|
|
2457
|
+
onPointerUp() {
|
|
2458
|
+
this.#dragged = this.#dragging;
|
|
2459
|
+
if (this.#dragging) {
|
|
2460
|
+
this.#dragging = false;
|
|
2461
|
+
this.commit(this.value(), this.#startValue);
|
|
2462
|
+
}
|
|
2096
2463
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
const
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
},
|
|
2112
|
-
onClose: (button) => {
|
|
2113
|
-
if (button !== setOption || input === null) {
|
|
2114
|
-
return;
|
|
2464
|
+
onClick() {
|
|
2465
|
+
if (this.#dragged) {
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
2468
|
+
const before = this.value();
|
|
2469
|
+
editInPlace({
|
|
2470
|
+
canvas: this.canvas(),
|
|
2471
|
+
box: this.box(),
|
|
2472
|
+
scale: this.scale(),
|
|
2473
|
+
value: String(before),
|
|
2474
|
+
onCommit: (text) => {
|
|
2475
|
+
const parsed = Number.parseFloat(text);
|
|
2476
|
+
if (Number.isFinite(parsed)) {
|
|
2477
|
+
this.commit(this.#clean(parsed), before);
|
|
2115
2478
|
}
|
|
2116
|
-
this.Set(input.valueAsNumber);
|
|
2117
2479
|
}
|
|
2118
2480
|
});
|
|
2119
|
-
popup.Show();
|
|
2120
2481
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
Size: {
|
|
2125
|
-
x: width * scale,
|
|
2126
|
-
y: height * scale
|
|
2127
|
-
}
|
|
2128
|
-
};
|
|
2129
|
-
CopyVector2(box.Position, position);
|
|
2130
|
-
let style = this.#idleBoxStyle;
|
|
2131
|
-
if (mousePosition !== void 0) {
|
|
2132
|
-
if (InBox(box, mousePosition)) {
|
|
2133
|
-
style = this.#highlightBoxStyle;
|
|
2134
|
-
}
|
|
2482
|
+
#stepFor(value) {
|
|
2483
|
+
if (this.#step !== void 0) {
|
|
2484
|
+
return this.#step;
|
|
2135
2485
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2486
|
+
return Math.max(Math.abs(value) * 0.01, this.#integer ? 0.1 : 0.01);
|
|
2487
|
+
}
|
|
2488
|
+
#clean(value) {
|
|
2489
|
+
const rounded = this.#integer ? Math.round(value) : value;
|
|
2490
|
+
return Math.min(this.#max, Math.max(this.#min, rounded));
|
|
2138
2491
|
}
|
|
2139
2492
|
};
|
|
2493
|
+
function formatNumber(value) {
|
|
2494
|
+
return "" + parseFloat(value.toPrecision(6));
|
|
2495
|
+
}
|
|
2140
2496
|
|
|
2141
2497
|
// src/widgets/button.ts
|
|
2142
|
-
var ButtonWidget = class {
|
|
2498
|
+
var ButtonWidget = class extends WidgetBase {
|
|
2143
2499
|
#text;
|
|
2144
|
-
#idleStyle;
|
|
2145
|
-
#hoverStyle;
|
|
2146
|
-
#clickStyle;
|
|
2147
2500
|
#callback;
|
|
2148
|
-
#
|
|
2501
|
+
#width;
|
|
2149
2502
|
constructor(config) {
|
|
2150
|
-
|
|
2503
|
+
super();
|
|
2504
|
+
this.#text = config?.text ?? "Button";
|
|
2151
2505
|
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
|
-
}));
|
|
2506
|
+
this.#width = config?.width ?? width;
|
|
2183
2507
|
}
|
|
2184
2508
|
Size() {
|
|
2185
|
-
return {
|
|
2509
|
+
return { x: this.#width, y: height };
|
|
2186
2510
|
}
|
|
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;
|
|
2511
|
+
text() {
|
|
2512
|
+
return this.#text;
|
|
2207
2513
|
}
|
|
2208
|
-
|
|
2209
|
-
this.#
|
|
2514
|
+
setText(text) {
|
|
2515
|
+
this.#text = text;
|
|
2516
|
+
requestRender();
|
|
2210
2517
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
this
|
|
2215
|
-
|
|
2518
|
+
render(ctx, box, scale) {
|
|
2519
|
+
drawField(ctx, box, scale, {
|
|
2520
|
+
hovered: this.hovered(),
|
|
2521
|
+
pressed: this.pressed(),
|
|
2522
|
+
value: this.#text
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
onClick() {
|
|
2526
|
+
this.#callback?.();
|
|
2216
2527
|
}
|
|
2217
2528
|
};
|
|
2218
2529
|
|
|
@@ -2227,500 +2538,214 @@ function contrastColor(color) {
|
|
|
2227
2538
|
}
|
|
2228
2539
|
return "white";
|
|
2229
2540
|
}
|
|
2230
|
-
var
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2541
|
+
var picker = null;
|
|
2542
|
+
function sharedPicker() {
|
|
2543
|
+
if (picker === null || !picker.isConnected) {
|
|
2544
|
+
picker = document.createElement("input");
|
|
2545
|
+
picker.type = "color";
|
|
2546
|
+
document.body.appendChild(picker);
|
|
2547
|
+
}
|
|
2548
|
+
return picker;
|
|
2549
|
+
}
|
|
2550
|
+
var ColorWidget = class extends ValueWidget {
|
|
2234
2551
|
#contrast;
|
|
2235
|
-
#textBoxStyle;
|
|
2236
|
-
#callback;
|
|
2237
2552
|
constructor(node, config) {
|
|
2238
|
-
|
|
2239
|
-
this.#
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
this.#contrast = contrastColor(this
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2553
|
+
super(node, config, "#000000");
|
|
2554
|
+
this.#contrast = contrastColor(this.value());
|
|
2555
|
+
}
|
|
2556
|
+
valueChanged() {
|
|
2557
|
+
this.#contrast = contrastColor(this.value());
|
|
2558
|
+
}
|
|
2559
|
+
render(ctx, box, scale) {
|
|
2560
|
+
drawField(ctx, box, scale, {
|
|
2561
|
+
input: true,
|
|
2562
|
+
hovered: this.hovered(),
|
|
2563
|
+
label: this.label(),
|
|
2564
|
+
value: this.value(),
|
|
2565
|
+
background: this.value(),
|
|
2566
|
+
border: this.#contrast,
|
|
2567
|
+
textColor: this.#contrast
|
|
2252
2568
|
});
|
|
2253
|
-
if (this.#nodeProperty !== void 0) {
|
|
2254
|
-
this.#node.addPropertyChangeListener(this.#nodeProperty, (oldVal, newVal) => {
|
|
2255
|
-
this.Set(newVal);
|
|
2256
|
-
});
|
|
2257
|
-
}
|
|
2258
|
-
}
|
|
2259
|
-
Size() {
|
|
2260
|
-
return { "x": width, "y": height };
|
|
2261
2569
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2570
|
+
onClick() {
|
|
2571
|
+
const canvas = this.canvas();
|
|
2572
|
+
if (canvas === null) {
|
|
2264
2573
|
return;
|
|
2265
2574
|
}
|
|
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;
|
|
2575
|
+
const before = this.value();
|
|
2576
|
+
const alpha = before.slice(7);
|
|
2577
|
+
const box = this.box();
|
|
2578
|
+
const rect = canvas.getBoundingClientRect();
|
|
2579
|
+
const sx = canvas.width > 0 ? rect.width / canvas.width : 1;
|
|
2580
|
+
const sy = canvas.height > 0 ? rect.height / canvas.height : 1;
|
|
2581
|
+
const input = sharedPicker();
|
|
2582
|
+
input.style.cssText = [
|
|
2583
|
+
"position: fixed",
|
|
2584
|
+
`left: ${rect.left + box.Position.x * sx}px`,
|
|
2585
|
+
`top: ${rect.top + box.Position.y * sy}px`,
|
|
2586
|
+
`width: ${box.Size.x * sx}px`,
|
|
2587
|
+
`height: ${box.Size.y * sy}px`,
|
|
2588
|
+
"opacity: 0",
|
|
2589
|
+
"pointer-events: none",
|
|
2590
|
+
"border: 0",
|
|
2591
|
+
"padding: 0"
|
|
2592
|
+
].join(";");
|
|
2593
|
+
input.value = before.slice(0, 7);
|
|
2594
|
+
input.oninput = () => this.input(input.value + alpha);
|
|
2595
|
+
input.onchange = () => this.commit(input.value + alpha, before);
|
|
2596
|
+
input.getBoundingClientRect();
|
|
2597
|
+
input.click();
|
|
2318
2598
|
}
|
|
2319
2599
|
};
|
|
2320
2600
|
|
|
2321
2601
|
// src/widgets/slider.ts
|
|
2322
|
-
var SliderWidget = class {
|
|
2323
|
-
// Data Variables =========================================================
|
|
2602
|
+
var SliderWidget = class extends ValueWidget {
|
|
2324
2603
|
#min;
|
|
2325
2604
|
#max;
|
|
2326
|
-
#value;
|
|
2327
|
-
#text;
|
|
2328
2605
|
#snapIncrement;
|
|
2329
|
-
#node;
|
|
2330
|
-
#nodeProperty;
|
|
2331
|
-
// Callbacks ==============================================================
|
|
2332
|
-
#change;
|
|
2333
|
-
#release;
|
|
2334
|
-
// Styling Variables ======================================================
|
|
2335
|
-
#textStyle;
|
|
2336
|
-
#backgroundColor;
|
|
2337
|
-
#borderColor;
|
|
2338
2606
|
#fillColor;
|
|
2339
|
-
|
|
2340
|
-
#lastMousePosition;
|
|
2341
|
-
#clickStartMousePosition;
|
|
2342
|
-
#clicking;
|
|
2607
|
+
#startValue = 0;
|
|
2343
2608
|
constructor(node, config) {
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
this.#
|
|
2609
|
+
const min = config?.min ?? 0;
|
|
2610
|
+
const max = config?.max ?? 1;
|
|
2611
|
+
super(node, { ...config, value: config?.value === void 0 ? void 0 : Clamp(config.value, min, max) }, min);
|
|
2612
|
+
this.#min = min;
|
|
2613
|
+
this.#max = max;
|
|
2348
2614
|
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 (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
|
-
}
|
|
2615
|
+
this.#fillColor = config?.fillColor;
|
|
2382
2616
|
}
|
|
2383
|
-
|
|
2384
|
-
return
|
|
2617
|
+
Cursor() {
|
|
2618
|
+
return "ew-resize" /* EWResize */;
|
|
2385
2619
|
}
|
|
2386
|
-
|
|
2387
|
-
this.#
|
|
2388
|
-
CopyVector2(this.#clickStartMousePosition, this.#lastMousePosition);
|
|
2620
|
+
setValue(value) {
|
|
2621
|
+
super.setValue(Clamp(value, this.#min, this.#max));
|
|
2389
2622
|
}
|
|
2390
|
-
|
|
2391
|
-
this
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
}
|
|
2396
|
-
Draw(ctx, position, scale, mousePosition) {
|
|
2397
|
-
const scaledWidth = width * scale;
|
|
2398
|
-
const scaledHeight = height * scale;
|
|
2399
|
-
const scaledBorderThickness = Theme.Widget.Border.Size * scale;
|
|
2400
|
-
ctx.fillStyle = this.#backgroundColor;
|
|
2401
|
-
ctx.strokeStyle = this.#borderColor;
|
|
2402
|
-
ctx.lineWidth = scaledBorderThickness;
|
|
2623
|
+
render(ctx, box, scale) {
|
|
2624
|
+
drawFieldBox(ctx, box, scale, { input: true, hovered: this.hovered() || this.pressed(), value: "" });
|
|
2625
|
+
const border = Theme.Widget.Border.Size * scale;
|
|
2626
|
+
const fill = Clamp01((this.value() - this.#min) / (this.#max - this.#min));
|
|
2627
|
+
ctx.fillStyle = this.#fillColor ?? Theme.Widget.Slider.FillColor;
|
|
2403
2628
|
ctx.beginPath();
|
|
2404
2629
|
ctx.roundRect(
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2630
|
+
box.Position.x + border / 2,
|
|
2631
|
+
box.Position.y + border / 2,
|
|
2632
|
+
Math.max(0, box.Size.x * fill - border),
|
|
2633
|
+
box.Size.y - border,
|
|
2409
2634
|
Theme.Widget.Border.Radius * scale
|
|
2410
2635
|
);
|
|
2411
2636
|
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
|
-
}
|
|
2637
|
+
drawFieldText(ctx, box, scale, {
|
|
2638
|
+
input: true,
|
|
2639
|
+
hovered: false,
|
|
2640
|
+
label: this.label(),
|
|
2641
|
+
value: formatNumber(Number(this.value().toFixed(3)))
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
2644
|
+
onPointerDown(pointer) {
|
|
2645
|
+
this.#startValue = this.value();
|
|
2646
|
+
this.input(this.#valueAt(pointer.position.x));
|
|
2647
|
+
}
|
|
2648
|
+
onPointerMove(pointer) {
|
|
2649
|
+
this.input(this.#valueAt(pointer.position.x));
|
|
2650
|
+
}
|
|
2651
|
+
onPointerUp() {
|
|
2652
|
+
this.commit(this.value(), this.#startValue);
|
|
2653
|
+
}
|
|
2654
|
+
#valueAt(x) {
|
|
2655
|
+
const box = this.box();
|
|
2656
|
+
const border = Theme.Widget.Border.Size * this.scale();
|
|
2657
|
+
const min = box.Position.x + border / 2;
|
|
2658
|
+
const max = min + box.Size.x - border;
|
|
2659
|
+
const p = Clamp01((x - min) / (max - min));
|
|
2660
|
+
let value = p * (this.#max - this.#min) + this.#min;
|
|
2661
|
+
if (this.#snapIncrement !== void 0) {
|
|
2662
|
+
value = Math.round(value / this.#snapIncrement) * this.#snapIncrement;
|
|
2444
2663
|
}
|
|
2445
|
-
return
|
|
2446
|
-
Position: { x: position.x, y: position.y },
|
|
2447
|
-
Size: {
|
|
2448
|
-
x: scaledWidth,
|
|
2449
|
-
y: scaledHeight
|
|
2450
|
-
}
|
|
2451
|
-
};
|
|
2664
|
+
return Clamp(value, this.#min, this.#max);
|
|
2452
2665
|
}
|
|
2453
2666
|
};
|
|
2454
2667
|
|
|
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
2668
|
// src/widgets/string.ts
|
|
2481
|
-
var StringWidget = class {
|
|
2482
|
-
#value;
|
|
2483
|
-
#idleStyle;
|
|
2484
|
-
#hoverStyle;
|
|
2485
|
-
#callback;
|
|
2486
|
-
#node;
|
|
2487
|
-
#nodeProperty;
|
|
2669
|
+
var StringWidget = class extends ValueWidget {
|
|
2488
2670
|
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
|
-
}
|
|
2671
|
+
super(node, config, "");
|
|
2521
2672
|
}
|
|
2522
|
-
|
|
2523
|
-
return
|
|
2524
|
-
}
|
|
2525
|
-
Set(value) {
|
|
2526
|
-
if (this.#value === value) {
|
|
2527
|
-
return;
|
|
2528
|
-
}
|
|
2529
|
-
this.#value = value;
|
|
2530
|
-
if (this.#nodeProperty !== void 0) {
|
|
2531
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
2532
|
-
}
|
|
2533
|
-
if (this.#callback !== void 0) {
|
|
2534
|
-
this.#callback(this.#value);
|
|
2535
|
-
}
|
|
2673
|
+
Cursor() {
|
|
2674
|
+
return "text" /* Text */;
|
|
2536
2675
|
}
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
onUpdate: (value) => {
|
|
2544
|
-
this.Set(value);
|
|
2545
|
-
}
|
|
2676
|
+
render(ctx, box, scale) {
|
|
2677
|
+
drawField(ctx, box, scale, {
|
|
2678
|
+
input: true,
|
|
2679
|
+
hovered: this.hovered(),
|
|
2680
|
+
label: this.label(),
|
|
2681
|
+
value: this.value()
|
|
2546
2682
|
});
|
|
2547
|
-
popup.Show();
|
|
2548
2683
|
}
|
|
2549
|
-
|
|
2550
|
-
const
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
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;
|
|
2684
|
+
onClick() {
|
|
2685
|
+
const before = this.value();
|
|
2686
|
+
editInPlace({
|
|
2687
|
+
canvas: this.canvas(),
|
|
2688
|
+
box: this.box(),
|
|
2689
|
+
scale: this.scale(),
|
|
2690
|
+
value: before,
|
|
2691
|
+
onCommit: (text) => this.commit(text, before)
|
|
2692
|
+
});
|
|
2566
2693
|
}
|
|
2567
2694
|
};
|
|
2568
2695
|
|
|
2569
2696
|
// 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;
|
|
2697
|
+
var ToggleWidget = class extends ValueWidget {
|
|
2698
|
+
#onColor;
|
|
2699
|
+
#offColor;
|
|
2646
2700
|
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);
|
|
2701
|
+
super(node, { ...config, label: config?.label ?? "Toggle" }, false);
|
|
2702
|
+
this.#onColor = config?.onColor ?? "#00FF00";
|
|
2703
|
+
this.#offColor = config?.offColor ?? "#004400";
|
|
2694
2704
|
}
|
|
2695
2705
|
Toggle() {
|
|
2696
|
-
this.
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2706
|
+
const before = this.value();
|
|
2707
|
+
this.commit(!before, before);
|
|
2708
|
+
}
|
|
2709
|
+
render(ctx, box, scale) {
|
|
2710
|
+
drawFieldBox(ctx, box, scale, { hovered: this.hovered(), value: "" });
|
|
2711
|
+
const light = Math.min(box.Size.x, box.Size.y) * 0.6;
|
|
2712
|
+
const inset = (box.Size.y - light) / 2;
|
|
2713
|
+
const on = this.value();
|
|
2714
|
+
ctx.fillStyle = on ? this.#onColor : this.#offColor;
|
|
2715
|
+
ctx.beginPath();
|
|
2716
|
+
ctx.roundRect(box.Position.x + inset, box.Position.y + inset, light, light, Theme.Widget.Border.Radius * scale);
|
|
2717
|
+
if (on) {
|
|
2718
|
+
ctx.shadowBlur = 15 * scale;
|
|
2719
|
+
ctx.shadowColor = this.#onColor;
|
|
2708
2720
|
}
|
|
2721
|
+
ctx.fill();
|
|
2722
|
+
ctx.shadowBlur = 0;
|
|
2723
|
+
const textLeft = box.Position.x + inset * 2 + light;
|
|
2724
|
+
ctx.font = widgetFont(scale);
|
|
2725
|
+
ctx.textAlign = "center";
|
|
2726
|
+
ctx.textBaseline = "middle";
|
|
2727
|
+
ctx.fillStyle = Theme.Widget.FontColor;
|
|
2728
|
+
const room = box.Position.x + box.Size.x - textLeft - inset;
|
|
2729
|
+
ctx.fillText(
|
|
2730
|
+
fitString(ctx, this.label(), room),
|
|
2731
|
+
textLeft + room / 2,
|
|
2732
|
+
box.Position.y + box.Size.y / 2
|
|
2733
|
+
);
|
|
2709
2734
|
}
|
|
2710
|
-
|
|
2735
|
+
onClick() {
|
|
2711
2736
|
this.Toggle();
|
|
2712
2737
|
}
|
|
2713
|
-
ClickEnd() {
|
|
2714
|
-
}
|
|
2715
2738
|
};
|
|
2716
2739
|
|
|
2717
2740
|
// src/widgets/image.ts
|
|
2718
|
-
var ImageWidget = class {
|
|
2741
|
+
var ImageWidget = class extends WidgetBase {
|
|
2719
2742
|
#url;
|
|
2743
|
+
#ownedUrl;
|
|
2720
2744
|
#image;
|
|
2721
2745
|
#maxWidth;
|
|
2722
2746
|
#maxHeight;
|
|
2723
2747
|
constructor(config) {
|
|
2748
|
+
super();
|
|
2724
2749
|
this.#maxWidth = config?.maxWidth === void 0 ? 150 : config?.maxWidth;
|
|
2725
2750
|
this.#maxHeight = config?.maxHeight === void 0 ? 150 : config?.maxHeight;
|
|
2726
2751
|
if (config?.image) {
|
|
@@ -2730,16 +2755,23 @@ var ImageWidget = class {
|
|
|
2730
2755
|
}
|
|
2731
2756
|
}
|
|
2732
2757
|
SetBlob(blob) {
|
|
2733
|
-
const
|
|
2734
|
-
|
|
2735
|
-
this
|
|
2758
|
+
const url = URL.createObjectURL(blob);
|
|
2759
|
+
this.SetUrl(url);
|
|
2760
|
+
this.#ownedUrl = url;
|
|
2736
2761
|
}
|
|
2737
2762
|
SetUrl(url) {
|
|
2763
|
+
if (this.#ownedUrl !== void 0) {
|
|
2764
|
+
URL.revokeObjectURL(this.#ownedUrl);
|
|
2765
|
+
this.#ownedUrl = void 0;
|
|
2766
|
+
}
|
|
2738
2767
|
this.#image = void 0;
|
|
2739
2768
|
this.#url = url;
|
|
2740
2769
|
const img = document.createElement("img");
|
|
2741
2770
|
img.src = url;
|
|
2742
2771
|
img.onload = () => {
|
|
2772
|
+
if (this.#url !== url) {
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2743
2775
|
this.#image = img;
|
|
2744
2776
|
requestRender();
|
|
2745
2777
|
};
|
|
@@ -2747,6 +2779,9 @@ var ImageWidget = class {
|
|
|
2747
2779
|
console.log("error loading image:", url, event);
|
|
2748
2780
|
};
|
|
2749
2781
|
}
|
|
2782
|
+
Cursor() {
|
|
2783
|
+
return "default" /* Default */;
|
|
2784
|
+
}
|
|
2750
2785
|
Size() {
|
|
2751
2786
|
if (this.#image === void 0) {
|
|
2752
2787
|
return { "x": 0, "y": 0 };
|
|
@@ -2766,88 +2801,178 @@ var ImageWidget = class {
|
|
|
2766
2801
|
"y": adjust * this.#image.height
|
|
2767
2802
|
};
|
|
2768
2803
|
}
|
|
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);
|
|
2804
|
+
render(ctx, box, scale) {
|
|
2783
2805
|
if (!this.#image) {
|
|
2784
|
-
return
|
|
2806
|
+
return;
|
|
2785
2807
|
}
|
|
2808
|
+
const size = this.Size();
|
|
2809
|
+
const w = size.x * scale;
|
|
2810
|
+
const h = size.y * scale;
|
|
2786
2811
|
ctx.drawImage(
|
|
2787
2812
|
this.#image,
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2813
|
+
box.Position.x + (box.Size.x - w) / 2,
|
|
2814
|
+
box.Position.y,
|
|
2815
|
+
w,
|
|
2816
|
+
h
|
|
2792
2817
|
);
|
|
2793
|
-
return box;
|
|
2794
2818
|
}
|
|
2795
2819
|
};
|
|
2796
2820
|
|
|
2797
2821
|
// src/widgets/text.ts
|
|
2798
|
-
var TextWidget = class {
|
|
2822
|
+
var TextWidget = class extends WidgetBase {
|
|
2799
2823
|
#value;
|
|
2800
|
-
#callback;
|
|
2801
|
-
#node;
|
|
2802
|
-
#nodeProperty;
|
|
2803
2824
|
#size;
|
|
2825
|
+
#styleConfig;
|
|
2826
|
+
#styledForTheme = -1;
|
|
2827
|
+
#followTheme() {
|
|
2828
|
+
if (this.#styledForTheme === themeVersion()) {
|
|
2829
|
+
return;
|
|
2830
|
+
}
|
|
2831
|
+
this.#styledForTheme = themeVersion();
|
|
2832
|
+
const style = this.#value.style();
|
|
2833
|
+
style.setColor(this.#styleConfig?.color ?? Theme.Widget.FontColor);
|
|
2834
|
+
style.setSize(this.#styleConfig?.size ?? Theme.Note.FontSize);
|
|
2835
|
+
}
|
|
2804
2836
|
constructor(node, config) {
|
|
2837
|
+
super();
|
|
2805
2838
|
this.#size = Zero();
|
|
2806
|
-
this.#
|
|
2807
|
-
this.#
|
|
2808
|
-
this.#
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
this.Set(newVal);
|
|
2818
|
-
});
|
|
2839
|
+
this.#styleConfig = config?.textBoxStyle;
|
|
2840
|
+
this.#value = new Text("", TextStyleFallback(config?.textBoxStyle, {}));
|
|
2841
|
+
this.#followTheme();
|
|
2842
|
+
this.setText(config?.value ?? "");
|
|
2843
|
+
const property = config?.property;
|
|
2844
|
+
if (property !== void 0) {
|
|
2845
|
+
const existing = node.getProperty(property);
|
|
2846
|
+
if (existing !== void 0 && existing !== null) {
|
|
2847
|
+
this.setText(String(existing));
|
|
2848
|
+
}
|
|
2849
|
+
node.addPropertyChangeListener(property, (_, next) => this.setText(String(next)));
|
|
2819
2850
|
}
|
|
2820
2851
|
}
|
|
2852
|
+
Cursor() {
|
|
2853
|
+
return "default" /* Default */;
|
|
2854
|
+
}
|
|
2821
2855
|
Size() {
|
|
2822
2856
|
return this.#size;
|
|
2823
2857
|
}
|
|
2824
|
-
|
|
2858
|
+
text() {
|
|
2859
|
+
return this.#value.get();
|
|
2860
|
+
}
|
|
2861
|
+
setText(value) {
|
|
2825
2862
|
if (this.#value.get() === value) {
|
|
2826
2863
|
return;
|
|
2827
2864
|
}
|
|
2828
2865
|
this.#value.set(value);
|
|
2829
|
-
|
|
2830
|
-
|
|
2866
|
+
requestRender();
|
|
2867
|
+
}
|
|
2868
|
+
render(ctx, box, scale) {
|
|
2869
|
+
this.#followTheme();
|
|
2870
|
+
this.#value.size(ctx, 1, this.#size);
|
|
2871
|
+
ctx.textAlign = "left";
|
|
2872
|
+
this.#value.render(ctx, scale, box.Position);
|
|
2873
|
+
}
|
|
2874
|
+
};
|
|
2875
|
+
|
|
2876
|
+
// src/widgets/row.ts
|
|
2877
|
+
var RowWidget = class extends WidgetBase {
|
|
2878
|
+
#children;
|
|
2879
|
+
#grow;
|
|
2880
|
+
#gap;
|
|
2881
|
+
#boxes = [];
|
|
2882
|
+
#captured = null;
|
|
2883
|
+
#capturedBox = null;
|
|
2884
|
+
#mouse;
|
|
2885
|
+
constructor(children, config) {
|
|
2886
|
+
super();
|
|
2887
|
+
this.#children = children;
|
|
2888
|
+
this.#grow = config?.grow ?? children.map(() => 1);
|
|
2889
|
+
this.#gap = config?.gap ?? Theme.Widget.Gap;
|
|
2890
|
+
}
|
|
2891
|
+
children() {
|
|
2892
|
+
return this.#children;
|
|
2893
|
+
}
|
|
2894
|
+
Size() {
|
|
2895
|
+
const shown = this.#shown();
|
|
2896
|
+
let x = 0;
|
|
2897
|
+
let y = 0;
|
|
2898
|
+
for (const child of shown) {
|
|
2899
|
+
const size = child.Size();
|
|
2900
|
+
x += size.x;
|
|
2901
|
+
y = Math.max(y, size.y);
|
|
2902
|
+
}
|
|
2903
|
+
x += Math.max(0, shown.length - 1) * this.#gap;
|
|
2904
|
+
return { x, y };
|
|
2905
|
+
}
|
|
2906
|
+
Cursor() {
|
|
2907
|
+
const over = this.#captured ?? this.#childAt(this.#mouse);
|
|
2908
|
+
return over?.Cursor?.() ?? "default" /* Default */;
|
|
2909
|
+
}
|
|
2910
|
+
Draw(ctx, box, scale, mousePosition) {
|
|
2911
|
+
this.#mouse = mousePosition;
|
|
2912
|
+
super.Draw(ctx, box, scale, mousePosition);
|
|
2913
|
+
}
|
|
2914
|
+
render(ctx, box, scale) {
|
|
2915
|
+
const shown = this.#shown();
|
|
2916
|
+
const gap = this.#gap * scale;
|
|
2917
|
+
const natural = shown.reduce((sum, child) => sum + child.Size().x * scale, 0);
|
|
2918
|
+
const extra = Math.max(0, box.Size.x - natural - gap * Math.max(0, shown.length - 1));
|
|
2919
|
+
const totalGrow = shown.reduce((sum, child) => sum + this.#growOf(child), 0);
|
|
2920
|
+
this.#boxes = this.#children.map(() => null);
|
|
2921
|
+
let x = box.Position.x;
|
|
2922
|
+
for (const child of shown) {
|
|
2923
|
+
const size = child.Size();
|
|
2924
|
+
const share = totalGrow > 0 ? extra * (this.#growOf(child) / totalGrow) : 0;
|
|
2925
|
+
const childBox = {
|
|
2926
|
+
Position: { x, y: box.Position.y + (box.Size.y - size.y * scale) / 2 },
|
|
2927
|
+
Size: { x: size.x * scale + share, y: size.y * scale }
|
|
2928
|
+
};
|
|
2929
|
+
this.#boxes[this.#children.indexOf(child)] = childBox;
|
|
2930
|
+
child.Draw(ctx, childBox, scale, this.#captured === null || this.#captured === child ? this.#mouse : void 0);
|
|
2931
|
+
x += childBox.Size.x + gap;
|
|
2831
2932
|
}
|
|
2832
|
-
|
|
2833
|
-
|
|
2933
|
+
}
|
|
2934
|
+
onPointerDown(pointer) {
|
|
2935
|
+
const child = this.#childAt(pointer.position);
|
|
2936
|
+
if (child === null || child.Disabled?.()) {
|
|
2937
|
+
return;
|
|
2834
2938
|
}
|
|
2939
|
+
this.#captured = child;
|
|
2940
|
+
this.#capturedBox = this.#boxes[this.#children.indexOf(child)];
|
|
2941
|
+
child.PointerDown?.(this.#forChild(pointer));
|
|
2835
2942
|
}
|
|
2836
|
-
|
|
2943
|
+
onPointerMove(pointer) {
|
|
2944
|
+
this.#captured?.PointerMove?.(this.#forChild(pointer));
|
|
2837
2945
|
}
|
|
2838
|
-
|
|
2946
|
+
onPointerUp(pointer) {
|
|
2947
|
+
const child = this.#captured;
|
|
2948
|
+
const forChild = this.#forChild(pointer);
|
|
2949
|
+
this.#captured = null;
|
|
2950
|
+
this.#capturedBox = null;
|
|
2951
|
+
child?.PointerUp?.(forChild);
|
|
2839
2952
|
}
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
Size: this.#size
|
|
2953
|
+
#forChild(pointer) {
|
|
2954
|
+
return {
|
|
2955
|
+
position: pointer.position,
|
|
2956
|
+
inside: this.#capturedBox !== null && InBox(this.#capturedBox, pointer.position)
|
|
2845
2957
|
};
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2958
|
+
}
|
|
2959
|
+
#childAt(position) {
|
|
2960
|
+
if (position === void 0) {
|
|
2961
|
+
return null;
|
|
2962
|
+
}
|
|
2963
|
+
for (let i = 0; i < this.#children.length; i++) {
|
|
2964
|
+
const box = this.#boxes[i];
|
|
2965
|
+
if (box && InBox(box, position)) {
|
|
2966
|
+
return this.#children[i];
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
return null;
|
|
2970
|
+
}
|
|
2971
|
+
#shown() {
|
|
2972
|
+
return this.#children.filter((child) => !child.Hidden?.());
|
|
2973
|
+
}
|
|
2974
|
+
#growOf(child) {
|
|
2975
|
+
return this.#grow[this.#children.indexOf(child)] ?? 1;
|
|
2851
2976
|
}
|
|
2852
2977
|
};
|
|
2853
2978
|
|
|
@@ -2867,6 +2992,9 @@ var WidgetFactory = class {
|
|
|
2867
2992
|
}
|
|
2868
2993
|
return builder(node, config);
|
|
2869
2994
|
}
|
|
2995
|
+
createFromConfig(node, config) {
|
|
2996
|
+
return this.create(node, config.type, config.config);
|
|
2997
|
+
}
|
|
2870
2998
|
};
|
|
2871
2999
|
var GlobalWidgetFactory = new WidgetFactory();
|
|
2872
3000
|
GlobalWidgetFactory.register("button", (node, config) => new ButtonWidget(config));
|
|
@@ -2877,6 +3005,10 @@ GlobalWidgetFactory.register("string", (node, config) => new StringWidget(node,
|
|
|
2877
3005
|
GlobalWidgetFactory.register("toggle", (node, config) => new ToggleWidget(node, config));
|
|
2878
3006
|
GlobalWidgetFactory.register("image", (node, config) => new ImageWidget(config));
|
|
2879
3007
|
GlobalWidgetFactory.register("text", (node, config) => new TextWidget(node, config));
|
|
3008
|
+
GlobalWidgetFactory.register("row", (node, config) => new RowWidget(
|
|
3009
|
+
(config?.widgets ?? []).map((child) => GlobalWidgetFactory.createFromConfig(node, child)),
|
|
3010
|
+
config
|
|
3011
|
+
));
|
|
2880
3012
|
|
|
2881
3013
|
// src/connection.ts
|
|
2882
3014
|
var BACKWARD_PORT_STUB = 30;
|
|
@@ -2961,7 +3093,7 @@ var Connection = class {
|
|
|
2961
3093
|
return;
|
|
2962
3094
|
}
|
|
2963
3095
|
if (this.#inNode !== null) {
|
|
2964
|
-
const inPortBox = this.#
|
|
3096
|
+
const inPortBox = this.#inSlotBox();
|
|
2965
3097
|
if (inPortBox === void 0) {
|
|
2966
3098
|
return;
|
|
2967
3099
|
}
|
|
@@ -3020,14 +3152,14 @@ var Connection = class {
|
|
|
3020
3152
|
this.clearInput();
|
|
3021
3153
|
this.clearOutput();
|
|
3022
3154
|
}
|
|
3023
|
-
setInput(node, portIndex, replace) {
|
|
3155
|
+
setInput(node, portIndex, replace, insertAt) {
|
|
3024
3156
|
this.#inNode = node;
|
|
3025
3157
|
this.#inNodePortIndex = portIndex;
|
|
3026
3158
|
const port = this.#inNode.inputPort(portIndex);
|
|
3027
3159
|
if (!!replace && port.connections().length > 0) {
|
|
3028
3160
|
port.replaceConnection(this, 0);
|
|
3029
3161
|
} else {
|
|
3030
|
-
port.addConnection(this);
|
|
3162
|
+
port.addConnection(this, insertAt);
|
|
3031
3163
|
}
|
|
3032
3164
|
}
|
|
3033
3165
|
setOutput(node, portIndex) {
|
|
@@ -3045,9 +3177,33 @@ var Connection = class {
|
|
|
3045
3177
|
this.#outNode = null;
|
|
3046
3178
|
this.#outNodePortIndex = -1;
|
|
3047
3179
|
}
|
|
3180
|
+
/**
|
|
3181
|
+
* The part of the input port this wire lands on: the whole port, or its
|
|
3182
|
+
* own slot on a port holding several wires.
|
|
3183
|
+
*/
|
|
3184
|
+
#inSlotBox() {
|
|
3185
|
+
if (this.#inNode === null) {
|
|
3186
|
+
return void 0;
|
|
3187
|
+
}
|
|
3188
|
+
const box = this.#inNode.inputPortPosition(this.#inNodePortIndex);
|
|
3189
|
+
const port = this.#inNode.inputPort(this.#inNodePortIndex);
|
|
3190
|
+
const slots = port.slots();
|
|
3191
|
+
if (box === void 0 || slots === 1) {
|
|
3192
|
+
return box;
|
|
3193
|
+
}
|
|
3194
|
+
const index = port.connections().indexOf(this);
|
|
3195
|
+
if (index === -1) {
|
|
3196
|
+
return box;
|
|
3197
|
+
}
|
|
3198
|
+
const height2 = box.Size.y / slots;
|
|
3199
|
+
return {
|
|
3200
|
+
Position: { x: box.Position.x, y: box.Position.y + index * height2 },
|
|
3201
|
+
Size: { x: box.Size.x, y: height2 }
|
|
3202
|
+
};
|
|
3203
|
+
}
|
|
3048
3204
|
mouseOverPort(mousePosition) {
|
|
3049
3205
|
if (this.#inNode !== null) {
|
|
3050
|
-
const inPortBox = this.#
|
|
3206
|
+
const inPortBox = this.#inSlotBox();
|
|
3051
3207
|
if (inPortBox !== void 0 && InBox(inPortBox, mousePosition)) {
|
|
3052
3208
|
return this.#inNode.inputPort(this.#inNodePortIndex);
|
|
3053
3209
|
}
|
|
@@ -3124,7 +3280,10 @@ var Camera = class {
|
|
|
3124
3280
|
// src/organize.ts
|
|
3125
3281
|
var widthSpacing = 100;
|
|
3126
3282
|
var heightSpacing = 50;
|
|
3283
|
+
var laneSpacing = 25;
|
|
3284
|
+
var pieceSpacing = 150;
|
|
3127
3285
|
var orderingSweeps = 8;
|
|
3286
|
+
var placementSweeps = 8;
|
|
3128
3287
|
function topologicalOrder(sorting, upstream) {
|
|
3129
3288
|
const visited = /* @__PURE__ */ new Set();
|
|
3130
3289
|
const order = new Array();
|
|
@@ -3146,11 +3305,7 @@ function topologicalOrder(sorting, upstream) {
|
|
|
3146
3305
|
continue;
|
|
3147
3306
|
}
|
|
3148
3307
|
visited.add(top.node);
|
|
3149
|
-
const
|
|
3150
|
-
if (feeders === void 0) {
|
|
3151
|
-
continue;
|
|
3152
|
-
}
|
|
3153
|
-
for (const feeder of feeders) {
|
|
3308
|
+
for (const feeder of upstream.get(top.node) ?? []) {
|
|
3154
3309
|
if (!visited.has(feeder)) {
|
|
3155
3310
|
stack.push({ node: feeder, expanded: false });
|
|
3156
3311
|
}
|
|
@@ -3159,43 +3314,103 @@ function topologicalOrder(sorting, upstream) {
|
|
|
3159
3314
|
}
|
|
3160
3315
|
return order;
|
|
3161
3316
|
}
|
|
3317
|
+
function consumersOf(sorting, upstream) {
|
|
3318
|
+
const downstream = /* @__PURE__ */ new Map();
|
|
3319
|
+
for (const node of sorting) {
|
|
3320
|
+
downstream.set(node, []);
|
|
3321
|
+
}
|
|
3322
|
+
for (const node of sorting) {
|
|
3323
|
+
for (const feeder of upstream.get(node) ?? []) {
|
|
3324
|
+
const consumers = downstream.get(feeder);
|
|
3325
|
+
if (consumers !== void 0 && !consumers.includes(node)) {
|
|
3326
|
+
consumers.push(node);
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
return downstream;
|
|
3331
|
+
}
|
|
3162
3332
|
function assignLayers(sorting, upstream) {
|
|
3163
|
-
const
|
|
3333
|
+
const downstream = consumersOf(sorting, upstream);
|
|
3334
|
+
const order = topologicalOrder(sorting, downstream);
|
|
3164
3335
|
const placed = /* @__PURE__ */ new Set();
|
|
3165
|
-
const
|
|
3336
|
+
const rank = /* @__PURE__ */ new Map();
|
|
3166
3337
|
for (const node of order) {
|
|
3167
|
-
let
|
|
3168
|
-
for (const
|
|
3169
|
-
if (placed.has(
|
|
3170
|
-
|
|
3338
|
+
let r = 0;
|
|
3339
|
+
for (const consumer of downstream.get(node) ?? []) {
|
|
3340
|
+
if (placed.has(consumer)) {
|
|
3341
|
+
r = Math.max(r, rank.get(consumer) + 1);
|
|
3171
3342
|
}
|
|
3172
3343
|
}
|
|
3173
|
-
|
|
3344
|
+
rank.set(node, r);
|
|
3174
3345
|
placed.add(node);
|
|
3175
3346
|
}
|
|
3176
3347
|
let deepest = 0;
|
|
3177
|
-
for (const
|
|
3178
|
-
deepest = Math.max(deepest,
|
|
3348
|
+
for (const r of rank.values()) {
|
|
3349
|
+
deepest = Math.max(deepest, r);
|
|
3179
3350
|
}
|
|
3180
|
-
const
|
|
3181
|
-
for (
|
|
3182
|
-
|
|
3351
|
+
const layerOf = /* @__PURE__ */ new Map();
|
|
3352
|
+
for (const node of sorting) {
|
|
3353
|
+
layerOf.set(node, deepest - rank.get(node));
|
|
3183
3354
|
}
|
|
3184
|
-
for (const node of
|
|
3185
|
-
|
|
3355
|
+
for (const node of sorting) {
|
|
3356
|
+
const layer = layerOf.get(node);
|
|
3357
|
+
if ((downstream.get(node) ?? []).some((c) => layerOf.get(c) > layer)) {
|
|
3358
|
+
continue;
|
|
3359
|
+
}
|
|
3360
|
+
let pulled = -1;
|
|
3361
|
+
for (const feeder of upstream.get(node) ?? []) {
|
|
3362
|
+
const feederLayer = layerOf.get(feeder);
|
|
3363
|
+
if (feederLayer !== void 0 && feederLayer < layer) {
|
|
3364
|
+
pulled = Math.max(pulled, feederLayer + 1);
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
if (pulled >= 0) {
|
|
3368
|
+
layerOf.set(node, pulled);
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
const used = [...new Set(layerOf.values())].sort((a, b) => a - b);
|
|
3372
|
+
const compact = /* @__PURE__ */ new Map();
|
|
3373
|
+
used.forEach((layer, i) => compact.set(layer, i));
|
|
3374
|
+
const layers = used.map(() => new Array());
|
|
3375
|
+
for (const node of sorting) {
|
|
3376
|
+
const layer = compact.get(layerOf.get(node));
|
|
3377
|
+
layerOf.set(node, layer);
|
|
3378
|
+
layers[layer].push(node);
|
|
3186
3379
|
}
|
|
3187
3380
|
return { layers, layerOf };
|
|
3188
3381
|
}
|
|
3189
|
-
function
|
|
3382
|
+
function crossingsOf(layout, downstream) {
|
|
3190
3383
|
const slotOf = /* @__PURE__ */ new Map();
|
|
3191
|
-
const
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3384
|
+
for (const layer of layout.layers) {
|
|
3385
|
+
layer.forEach((node, slot) => slotOf.set(node, slot));
|
|
3386
|
+
}
|
|
3387
|
+
let total = 0;
|
|
3388
|
+
for (let i = 0; i + 1 < layout.layers.length; i++) {
|
|
3389
|
+
const spans = new Array();
|
|
3390
|
+
for (const node of layout.layers[i]) {
|
|
3391
|
+
for (const consumer of downstream.get(node) ?? []) {
|
|
3392
|
+
if (layout.layerOf.get(consumer) === i + 1) {
|
|
3393
|
+
spans.push([slotOf.get(node), slotOf.get(consumer)]);
|
|
3394
|
+
}
|
|
3195
3395
|
}
|
|
3196
3396
|
}
|
|
3197
|
-
|
|
3198
|
-
|
|
3397
|
+
for (let a = 0; a < spans.length; a++) {
|
|
3398
|
+
for (let b = a + 1; b < spans.length; b++) {
|
|
3399
|
+
if ((spans[a][0] - spans[b][0]) * (spans[a][1] - spans[b][1]) < 0) {
|
|
3400
|
+
total++;
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
return total;
|
|
3406
|
+
}
|
|
3407
|
+
function orderWithinLayers(layout, upstream, downstream) {
|
|
3408
|
+
const slotOf = /* @__PURE__ */ new Map();
|
|
3409
|
+
for (const layer of layout.layers) {
|
|
3410
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3411
|
+
slotOf.set(layer[i], i);
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3199
3414
|
const meanSlot = (node, neighbours, layer) => {
|
|
3200
3415
|
let total = 0;
|
|
3201
3416
|
let count = 0;
|
|
@@ -3206,31 +3421,386 @@ function orderWithinLayers(layout, upstream, downstream) {
|
|
|
3206
3421
|
total += slotOf.get(neighbour);
|
|
3207
3422
|
count++;
|
|
3208
3423
|
}
|
|
3209
|
-
return count === 0 ? -1 : total / count;
|
|
3424
|
+
return count === 0 ? -1 : total / count;
|
|
3425
|
+
};
|
|
3426
|
+
const sweep = (neighbours, towards) => {
|
|
3427
|
+
for (let i = 0; i < layout.layers.length; i++) {
|
|
3428
|
+
const index = towards > 0 ? i : layout.layers.length - 1 - i;
|
|
3429
|
+
const adjacent = index + towards;
|
|
3430
|
+
if (adjacent < 0 || adjacent >= layout.layers.length) {
|
|
3431
|
+
continue;
|
|
3432
|
+
}
|
|
3433
|
+
const layer = layout.layers[index];
|
|
3434
|
+
const keys = /* @__PURE__ */ new Map();
|
|
3435
|
+
for (const node of layer) {
|
|
3436
|
+
const mean = meanSlot(node, neighbours, adjacent);
|
|
3437
|
+
keys.set(node, mean === -1 ? slotOf.get(node) : mean);
|
|
3438
|
+
}
|
|
3439
|
+
layer.sort((a, b) => keys.get(a) - keys.get(b));
|
|
3440
|
+
for (let s = 0; s < layer.length; s++) {
|
|
3441
|
+
slotOf.set(layer[s], s);
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
};
|
|
3445
|
+
let best = layout.layers.map((layer) => layer.slice());
|
|
3446
|
+
let fewest = crossingsOf(layout, downstream);
|
|
3447
|
+
for (let i = 0; i < orderingSweeps && fewest > 0; i++) {
|
|
3448
|
+
sweep(upstream, -1);
|
|
3449
|
+
sweep(downstream, 1);
|
|
3450
|
+
const crossings = crossingsOf(layout, downstream);
|
|
3451
|
+
if (crossings < fewest) {
|
|
3452
|
+
fewest = crossings;
|
|
3453
|
+
best = layout.layers.map((layer) => layer.slice());
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
for (let i = 0; i < best.length; i++) {
|
|
3457
|
+
layout.layers[i] = best[i];
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
function findPieces(sorting, upstream, downstream) {
|
|
3461
|
+
const rankOf = /* @__PURE__ */ new Map();
|
|
3462
|
+
sorting.forEach((node, i) => rankOf.set(node, i));
|
|
3463
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3464
|
+
const pieces = new Array();
|
|
3465
|
+
for (const start of sorting) {
|
|
3466
|
+
if (seen.has(start)) {
|
|
3467
|
+
continue;
|
|
3468
|
+
}
|
|
3469
|
+
seen.add(start);
|
|
3470
|
+
const piece = new Array();
|
|
3471
|
+
const stack = [start];
|
|
3472
|
+
while (stack.length > 0) {
|
|
3473
|
+
const node = stack.pop();
|
|
3474
|
+
piece.push(node);
|
|
3475
|
+
for (const neighbour of [...upstream.get(node) ?? [], ...downstream.get(node) ?? []]) {
|
|
3476
|
+
if (rankOf.has(neighbour) && !seen.has(neighbour)) {
|
|
3477
|
+
seen.add(neighbour);
|
|
3478
|
+
stack.push(neighbour);
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
piece.sort((a, b) => rankOf.get(a) - rankOf.get(b));
|
|
3483
|
+
pieces.push(piece);
|
|
3484
|
+
}
|
|
3485
|
+
return pieces;
|
|
3486
|
+
}
|
|
3487
|
+
function treeRoot(piece, downstream) {
|
|
3488
|
+
let root;
|
|
3489
|
+
for (const node of piece) {
|
|
3490
|
+
const consumers = downstream.get(node) ?? [];
|
|
3491
|
+
if (consumers.length > 1) {
|
|
3492
|
+
return void 0;
|
|
3493
|
+
}
|
|
3494
|
+
if (consumers.length === 0) {
|
|
3495
|
+
if (root !== void 0) {
|
|
3496
|
+
return void 0;
|
|
3497
|
+
}
|
|
3498
|
+
root = node;
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
return root;
|
|
3502
|
+
}
|
|
3503
|
+
function columnXs(widths) {
|
|
3504
|
+
const xs = new Array();
|
|
3505
|
+
let x = 0;
|
|
3506
|
+
for (const width2 of widths) {
|
|
3507
|
+
xs.push(x);
|
|
3508
|
+
x += width2 + widthSpacing;
|
|
3509
|
+
}
|
|
3510
|
+
return xs;
|
|
3511
|
+
}
|
|
3512
|
+
function layoutTree(root, upstream, sizes) {
|
|
3513
|
+
const children = (node) => [...new Set(upstream.get(node) ?? [])];
|
|
3514
|
+
const depthOf = /* @__PURE__ */ new Map([[root, 0]]);
|
|
3515
|
+
const preorder = new Array();
|
|
3516
|
+
const stack = [root];
|
|
3517
|
+
let deepest = 0;
|
|
3518
|
+
while (stack.length > 0) {
|
|
3519
|
+
const node = stack.pop();
|
|
3520
|
+
preorder.push(node);
|
|
3521
|
+
const kids = children(node);
|
|
3522
|
+
for (let i = kids.length - 1; i >= 0; i--) {
|
|
3523
|
+
const depth = depthOf.get(node) + 1;
|
|
3524
|
+
depthOf.set(kids[i], depth);
|
|
3525
|
+
deepest = Math.max(deepest, depth);
|
|
3526
|
+
stack.push(kids[i]);
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
const widths = new Array(deepest + 1).fill(0);
|
|
3530
|
+
for (const [node, depth] of depthOf) {
|
|
3531
|
+
widths[deepest - depth] = Math.max(widths[deepest - depth], sizes.get(node).x);
|
|
3532
|
+
}
|
|
3533
|
+
const xs = columnXs(widths);
|
|
3534
|
+
const band = /* @__PURE__ */ new Map();
|
|
3535
|
+
const topInBand = /* @__PURE__ */ new Map();
|
|
3536
|
+
const childOffset = /* @__PURE__ */ new Map();
|
|
3537
|
+
for (let i = preorder.length - 1; i >= 0; i--) {
|
|
3538
|
+
const node = preorder[i];
|
|
3539
|
+
const height2 = sizes.get(node).y;
|
|
3540
|
+
const kids = children(node);
|
|
3541
|
+
if (kids.length === 0) {
|
|
3542
|
+
band.set(node, height2);
|
|
3543
|
+
topInBand.set(node, 0);
|
|
3544
|
+
continue;
|
|
3545
|
+
}
|
|
3546
|
+
let offset = 0;
|
|
3547
|
+
for (const kid of kids) {
|
|
3548
|
+
childOffset.set(kid, offset);
|
|
3549
|
+
offset += band.get(kid) + heightSpacing;
|
|
3550
|
+
}
|
|
3551
|
+
const centre = (kid) => childOffset.get(kid) + topInBand.get(kid) + sizes.get(kid).y / 2;
|
|
3552
|
+
let top = (centre(kids[0]) + centre(kids[kids.length - 1])) / 2 - height2 / 2;
|
|
3553
|
+
let shift = 0;
|
|
3554
|
+
if (top < 0) {
|
|
3555
|
+
shift = -top;
|
|
3556
|
+
top = 0;
|
|
3557
|
+
for (const kid of kids) {
|
|
3558
|
+
childOffset.set(kid, childOffset.get(kid) + shift);
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
band.set(node, Math.max(offset - heightSpacing + shift, top + height2));
|
|
3562
|
+
topInBand.set(node, top);
|
|
3563
|
+
}
|
|
3564
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3565
|
+
const bandTop = /* @__PURE__ */ new Map([[root, 0]]);
|
|
3566
|
+
for (const node of preorder) {
|
|
3567
|
+
const column = deepest - depthOf.get(node);
|
|
3568
|
+
const size = sizes.get(node);
|
|
3569
|
+
positions.set(node, {
|
|
3570
|
+
x: xs[column] + (widths[column] - size.x) / 2,
|
|
3571
|
+
y: bandTop.get(node) + topInBand.get(node)
|
|
3572
|
+
});
|
|
3573
|
+
for (const kid of children(node)) {
|
|
3574
|
+
bandTop.set(kid, bandTop.get(node) + childOffset.get(kid));
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
return positions;
|
|
3578
|
+
}
|
|
3579
|
+
function isotonic(targets) {
|
|
3580
|
+
const blocks = new Array();
|
|
3581
|
+
for (const target of targets) {
|
|
3582
|
+
blocks.push({ sum: target, count: 1 });
|
|
3583
|
+
while (blocks.length > 1) {
|
|
3584
|
+
const last = blocks[blocks.length - 1];
|
|
3585
|
+
const before = blocks[blocks.length - 2];
|
|
3586
|
+
if (before.sum / before.count <= last.sum / last.count) {
|
|
3587
|
+
break;
|
|
3588
|
+
}
|
|
3589
|
+
before.sum += last.sum;
|
|
3590
|
+
before.count += last.count;
|
|
3591
|
+
blocks.pop();
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
const result = new Array();
|
|
3595
|
+
for (const block of blocks) {
|
|
3596
|
+
for (let i = 0; i < block.count; i++) {
|
|
3597
|
+
result.push(block.sum / block.count);
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
return result;
|
|
3601
|
+
}
|
|
3602
|
+
function median(values) {
|
|
3603
|
+
const sorted = values.slice().sort((a, b) => a - b);
|
|
3604
|
+
const middle = Math.floor(sorted.length / 2);
|
|
3605
|
+
return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
3606
|
+
}
|
|
3607
|
+
function layoutLayered(piece, upstream, downstream, sizes) {
|
|
3608
|
+
const layout = assignLayers(piece, upstream);
|
|
3609
|
+
const up = /* @__PURE__ */ new Map();
|
|
3610
|
+
const down = /* @__PURE__ */ new Map();
|
|
3611
|
+
const heightOf = /* @__PURE__ */ new Map();
|
|
3612
|
+
for (const node of piece) {
|
|
3613
|
+
up.set(node, [...new Set(upstream.get(node) ?? [])]);
|
|
3614
|
+
down.set(node, [...new Set(downstream.get(node) ?? [])]);
|
|
3615
|
+
heightOf.set(node, sizes.get(node).y);
|
|
3616
|
+
}
|
|
3617
|
+
let nextLane = -1;
|
|
3618
|
+
for (const from of piece) {
|
|
3619
|
+
const fromLayer = layout.layerOf.get(from);
|
|
3620
|
+
const consumers = down.get(from);
|
|
3621
|
+
for (let c = 0; c < consumers.length; c++) {
|
|
3622
|
+
const to = consumers[c];
|
|
3623
|
+
const toLayer = layout.layerOf.get(to);
|
|
3624
|
+
if (toLayer - fromLayer < 2) {
|
|
3625
|
+
continue;
|
|
3626
|
+
}
|
|
3627
|
+
let previous = from;
|
|
3628
|
+
for (let layer = fromLayer + 1; layer < toLayer; layer++) {
|
|
3629
|
+
const lane = nextLane--;
|
|
3630
|
+
layout.layerOf.set(lane, layer);
|
|
3631
|
+
layout.layers[layer].push(lane);
|
|
3632
|
+
heightOf.set(lane, 0);
|
|
3633
|
+
up.set(lane, [previous]);
|
|
3634
|
+
down.set(lane, []);
|
|
3635
|
+
if (previous === from) {
|
|
3636
|
+
consumers[c] = lane;
|
|
3637
|
+
} else {
|
|
3638
|
+
down.get(previous).push(lane);
|
|
3639
|
+
}
|
|
3640
|
+
previous = lane;
|
|
3641
|
+
}
|
|
3642
|
+
down.get(previous).push(to);
|
|
3643
|
+
const feeders = up.get(to);
|
|
3644
|
+
feeders[feeders.indexOf(from)] = previous;
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
const sequence = /* @__PURE__ */ new Map();
|
|
3648
|
+
for (const sink of piece) {
|
|
3649
|
+
if (down.get(sink).length > 0) {
|
|
3650
|
+
continue;
|
|
3651
|
+
}
|
|
3652
|
+
const stack = [sink];
|
|
3653
|
+
while (stack.length > 0) {
|
|
3654
|
+
const node = stack.pop();
|
|
3655
|
+
if (sequence.has(node)) {
|
|
3656
|
+
continue;
|
|
3657
|
+
}
|
|
3658
|
+
sequence.set(node, sequence.size);
|
|
3659
|
+
const feeders = up.get(node) ?? [];
|
|
3660
|
+
for (let i = feeders.length - 1; i >= 0; i--) {
|
|
3661
|
+
stack.push(feeders[i]);
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
for (const layer of layout.layers) {
|
|
3666
|
+
layer.sort((a, b) => (sequence.get(a) ?? Infinity) - (sequence.get(b) ?? Infinity));
|
|
3667
|
+
}
|
|
3668
|
+
orderWithinLayers(layout, up, down);
|
|
3669
|
+
const gapAfter = (above, below) => above < 0 || below < 0 ? laneSpacing : heightSpacing;
|
|
3670
|
+
const topOf = /* @__PURE__ */ new Map();
|
|
3671
|
+
for (const layer of layout.layers) {
|
|
3672
|
+
let y = 0;
|
|
3673
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3674
|
+
topOf.set(layer[i], y);
|
|
3675
|
+
y += heightOf.get(layer[i]) + (i + 1 < layer.length ? gapAfter(layer[i], layer[i + 1]) : 0);
|
|
3676
|
+
}
|
|
3677
|
+
for (const node of layer) {
|
|
3678
|
+
topOf.set(node, topOf.get(node) - y / 2);
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
const centreOf = (node) => topOf.get(node) + heightOf.get(node) / 2;
|
|
3682
|
+
const place = (layer, neighbours, adjacent) => {
|
|
3683
|
+
const offsets = new Array();
|
|
3684
|
+
const targets = new Array();
|
|
3685
|
+
let offset = 0;
|
|
3686
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3687
|
+
const node = layer[i];
|
|
3688
|
+
const centres = (neighbours.get(node) ?? []).filter((n) => layout.layerOf.get(n) === adjacent).map(centreOf);
|
|
3689
|
+
const desired = centres.length > 0 ? median(centres) - heightOf.get(node) / 2 : topOf.get(node);
|
|
3690
|
+
offsets.push(offset);
|
|
3691
|
+
targets.push(desired - offset);
|
|
3692
|
+
if (i + 1 < layer.length) {
|
|
3693
|
+
offset += heightOf.get(node) + gapAfter(node, layer[i + 1]);
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
const solved = isotonic(targets);
|
|
3697
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3698
|
+
topOf.set(layer[i], solved[i] + offsets[i]);
|
|
3699
|
+
}
|
|
3210
3700
|
};
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
if (adjacent < 0 || adjacent >= layout.layers.length) {
|
|
3216
|
-
continue;
|
|
3701
|
+
for (let s = 0; s < placementSweeps; s++) {
|
|
3702
|
+
if (s % 2 === 0) {
|
|
3703
|
+
for (let i = 1; i < layout.layers.length; i++) {
|
|
3704
|
+
place(layout.layers[i], up, i - 1);
|
|
3217
3705
|
}
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
const mean = meanSlot(node, neighbours, adjacent);
|
|
3222
|
-
keys.set(node, mean === -1 ? slotOf.get(node) : mean);
|
|
3223
|
-
}
|
|
3224
|
-
layer.sort((a, b) => keys.get(a) - keys.get(b));
|
|
3225
|
-
for (let s = 0; s < layer.length; s++) {
|
|
3226
|
-
slotOf.set(layer[s], s);
|
|
3706
|
+
} else {
|
|
3707
|
+
for (let i = layout.layers.length - 2; i >= 0; i--) {
|
|
3708
|
+
place(layout.layers[i], down, i + 1);
|
|
3227
3709
|
}
|
|
3228
3710
|
}
|
|
3229
|
-
};
|
|
3230
|
-
for (let i = 0; i < orderingSweeps; i++) {
|
|
3231
|
-
sweep(upstream, -1);
|
|
3232
|
-
sweep(downstream, 1);
|
|
3233
3711
|
}
|
|
3712
|
+
const widths = layout.layers.map((layer) => layer.reduce((widest, node) => node < 0 ? widest : Math.max(widest, sizes.get(node).x), 0));
|
|
3713
|
+
const xs = columnXs(widths);
|
|
3714
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3715
|
+
for (const node of piece) {
|
|
3716
|
+
const layer = layout.layerOf.get(node);
|
|
3717
|
+
positions.set(node, {
|
|
3718
|
+
x: xs[layer] + (widths[layer] - sizes.get(node).x) / 2,
|
|
3719
|
+
y: topOf.get(node)
|
|
3720
|
+
});
|
|
3721
|
+
}
|
|
3722
|
+
return positions;
|
|
3723
|
+
}
|
|
3724
|
+
function layoutLoose(loose, sizes) {
|
|
3725
|
+
const columns = Math.ceil(Math.sqrt(loose.length));
|
|
3726
|
+
const widths = new Array(columns).fill(0);
|
|
3727
|
+
const heights = new Array(Math.ceil(loose.length / columns)).fill(0);
|
|
3728
|
+
loose.forEach((node, i) => {
|
|
3729
|
+
const size = sizes.get(node);
|
|
3730
|
+
widths[i % columns] = Math.max(widths[i % columns], size.x);
|
|
3731
|
+
heights[Math.floor(i / columns)] = Math.max(heights[Math.floor(i / columns)], size.y);
|
|
3732
|
+
});
|
|
3733
|
+
const xs = new Array();
|
|
3734
|
+
let x = 0;
|
|
3735
|
+
for (const width2 of widths) {
|
|
3736
|
+
xs.push(x);
|
|
3737
|
+
x += width2 + heightSpacing;
|
|
3738
|
+
}
|
|
3739
|
+
const ys = new Array();
|
|
3740
|
+
let y = 0;
|
|
3741
|
+
for (const height2 of heights) {
|
|
3742
|
+
ys.push(y);
|
|
3743
|
+
y += height2 + heightSpacing;
|
|
3744
|
+
}
|
|
3745
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3746
|
+
loose.forEach((node, i) => positions.set(node, { x: xs[i % columns], y: ys[Math.floor(i / columns)] }));
|
|
3747
|
+
return positions;
|
|
3748
|
+
}
|
|
3749
|
+
function toBlock(positions, sizes) {
|
|
3750
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
3751
|
+
for (const [node, position] of positions) {
|
|
3752
|
+
const size = sizes.get(node);
|
|
3753
|
+
minX = Math.min(minX, position.x);
|
|
3754
|
+
minY = Math.min(minY, position.y);
|
|
3755
|
+
maxX = Math.max(maxX, position.x + size.x);
|
|
3756
|
+
maxY = Math.max(maxY, position.y + size.y);
|
|
3757
|
+
}
|
|
3758
|
+
const shifted = /* @__PURE__ */ new Map();
|
|
3759
|
+
for (const [node, position] of positions) {
|
|
3760
|
+
shifted.set(node, { x: position.x - minX, y: position.y - minY });
|
|
3761
|
+
}
|
|
3762
|
+
return { positions: shifted, size: { x: maxX - minX, y: maxY - minY } };
|
|
3763
|
+
}
|
|
3764
|
+
function layoutGraph(sorting, upstream, downstream, sizes) {
|
|
3765
|
+
const blocks = new Array();
|
|
3766
|
+
const loose = new Array();
|
|
3767
|
+
for (const piece of findPieces(sorting, upstream, downstream)) {
|
|
3768
|
+
if (piece.length === 1) {
|
|
3769
|
+
loose.push(piece[0]);
|
|
3770
|
+
continue;
|
|
3771
|
+
}
|
|
3772
|
+
const root = treeRoot(piece, downstream);
|
|
3773
|
+
const positions2 = root !== void 0 ? layoutTree(root, upstream, sizes) : layoutLayered(piece, upstream, downstream, sizes);
|
|
3774
|
+
blocks.push(toBlock(positions2, sizes));
|
|
3775
|
+
}
|
|
3776
|
+
blocks.sort((a, b) => b.size.y - a.size.y || b.size.x - a.size.x);
|
|
3777
|
+
if (loose.length > 0) {
|
|
3778
|
+
blocks.push(toBlock(layoutLoose(loose, sizes), sizes));
|
|
3779
|
+
}
|
|
3780
|
+
let area = 0;
|
|
3781
|
+
let widest = 0;
|
|
3782
|
+
for (const block of blocks) {
|
|
3783
|
+
area += (block.size.x + pieceSpacing) * (block.size.y + pieceSpacing);
|
|
3784
|
+
widest = Math.max(widest, block.size.x);
|
|
3785
|
+
}
|
|
3786
|
+
const rowWidth = Math.max(widest, Math.sqrt(area));
|
|
3787
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3788
|
+
let x = 0;
|
|
3789
|
+
let y = 0;
|
|
3790
|
+
let rowHeight = 0;
|
|
3791
|
+
for (const block of blocks) {
|
|
3792
|
+
if (x > 0 && x + block.size.x > rowWidth) {
|
|
3793
|
+
x = 0;
|
|
3794
|
+
y += rowHeight + pieceSpacing;
|
|
3795
|
+
rowHeight = 0;
|
|
3796
|
+
}
|
|
3797
|
+
for (const [node, position] of block.positions) {
|
|
3798
|
+
positions.set(node, { x: position.x + x, y: position.y + y });
|
|
3799
|
+
}
|
|
3800
|
+
x += block.size.x + pieceSpacing;
|
|
3801
|
+
rowHeight = Math.max(rowHeight, block.size.y);
|
|
3802
|
+
}
|
|
3803
|
+
return positions;
|
|
3234
3804
|
}
|
|
3235
3805
|
function Organize(ctx, graph, nodesToSort) {
|
|
3236
3806
|
const nodes = graph.getNodes();
|
|
@@ -3239,9 +3809,6 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3239
3809
|
}
|
|
3240
3810
|
const sorting = new Array();
|
|
3241
3811
|
if (nodesToSort) {
|
|
3242
|
-
if (nodesToSort.length < 2) {
|
|
3243
|
-
return;
|
|
3244
|
-
}
|
|
3245
3812
|
for (const index of nodesToSort) {
|
|
3246
3813
|
if (index >= 0 && index < nodes.length) {
|
|
3247
3814
|
sorting.push(index);
|
|
@@ -3261,33 +3828,28 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3261
3828
|
nodeLUT.set(nodes[i], i);
|
|
3262
3829
|
}
|
|
3263
3830
|
const camera = new Camera();
|
|
3264
|
-
const
|
|
3831
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
3265
3832
|
for (const index of sorting) {
|
|
3266
|
-
|
|
3833
|
+
sizes.set(index, nodes[index].calculateBounds(ctx, camera).Size);
|
|
3267
3834
|
}
|
|
3268
3835
|
const upstream = /* @__PURE__ */ new Map();
|
|
3269
|
-
const downstream = /* @__PURE__ */ new Map();
|
|
3270
3836
|
for (const index of sorting) {
|
|
3837
|
+
const node = nodes[index];
|
|
3271
3838
|
const feeders = new Array();
|
|
3272
|
-
for (
|
|
3273
|
-
const
|
|
3274
|
-
|
|
3275
|
-
|
|
3839
|
+
for (let i = 0; i < node.inputs(); i++) {
|
|
3840
|
+
for (const connection of node.inputPort(i).connections()) {
|
|
3841
|
+
const feeder = connection.outNode();
|
|
3842
|
+
const feederIndex = feeder === null ? void 0 : nodeLUT.get(feeder);
|
|
3843
|
+
if (feederIndex !== void 0 && inLayout.has(feederIndex) && feederIndex !== index && !feeders.includes(feederIndex)) {
|
|
3844
|
+
feeders.push(feederIndex);
|
|
3845
|
+
}
|
|
3276
3846
|
}
|
|
3277
3847
|
}
|
|
3278
3848
|
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
3849
|
}
|
|
3288
|
-
const
|
|
3289
|
-
|
|
3290
|
-
|
|
3850
|
+
const downstream = consumersOf(sorting, upstream);
|
|
3851
|
+
const positions = layoutGraph(sorting, upstream, downstream, sizes);
|
|
3852
|
+
const anchor = { x: 0, y: 0 };
|
|
3291
3853
|
for (const index of sorting) {
|
|
3292
3854
|
const position = nodes[index].getPosition();
|
|
3293
3855
|
anchor.x += position.x;
|
|
@@ -3295,19 +3857,14 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3295
3857
|
}
|
|
3296
3858
|
anchor.x /= sorting.length;
|
|
3297
3859
|
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;
|
|
3860
|
+
let right = 0;
|
|
3861
|
+
let bottom = 0;
|
|
3862
|
+
for (const [index, position] of positions) {
|
|
3863
|
+
right = Math.max(right, position.x + sizes.get(index).x);
|
|
3864
|
+
bottom = Math.max(bottom, position.y + sizes.get(index).y);
|
|
3865
|
+
}
|
|
3866
|
+
for (const [index, position] of positions) {
|
|
3867
|
+
nodes[index].setPosition({ x: anchor.x - right / 2 + position.x, y: anchor.y - bottom / 2 + position.y });
|
|
3311
3868
|
}
|
|
3312
3869
|
}
|
|
3313
3870
|
|
|
@@ -3499,14 +4056,17 @@ var NodeSubsystem2 = class {
|
|
|
3499
4056
|
#portHovering;
|
|
3500
4057
|
#widgetHovering;
|
|
3501
4058
|
#widgetCurrentlyClicking;
|
|
4059
|
+
#widgetHoveringBox = null;
|
|
4060
|
+
#capturedWidgetBox = null;
|
|
4061
|
+
#capturedPointer = Zero();
|
|
3502
4062
|
#cursor;
|
|
3503
4063
|
#nodeFactory;
|
|
3504
4064
|
#boxSelect;
|
|
3505
4065
|
#boxSelectStart_graphSpace;
|
|
3506
4066
|
#boxSelectEnd_graphSpace;
|
|
3507
4067
|
#boxSelectionNodes;
|
|
3508
|
-
#boxSelectStyle;
|
|
3509
4068
|
#lastCtx;
|
|
4069
|
+
#lastMousePosition;
|
|
3510
4070
|
// LISTENERS ==============================================================
|
|
3511
4071
|
#nodeRemovedCallbacks;
|
|
3512
4072
|
#registeredNodeAddedCallbacks;
|
|
@@ -3531,14 +4091,6 @@ var NodeSubsystem2 = class {
|
|
|
3531
4091
|
this.#boxSelectEnd_graphSpace = Zero();
|
|
3532
4092
|
this.#boxSelectionNodes = new List();
|
|
3533
4093
|
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
4094
|
}
|
|
3543
4095
|
addPublisher(identifier, publisher) {
|
|
3544
4096
|
this.#nodeFactory.addPublisher(identifier, publisher);
|
|
@@ -3575,6 +4127,7 @@ var NodeSubsystem2 = class {
|
|
|
3575
4127
|
}
|
|
3576
4128
|
if (intersection.Widget !== void 0) {
|
|
3577
4129
|
this.#widgetHovering = intersection.Widget;
|
|
4130
|
+
this.#widgetHoveringBox = intersection.WidgetBox ?? null;
|
|
3578
4131
|
}
|
|
3579
4132
|
if (intersection.Port !== void 0 && intersection.Node !== void 0 && intersection.PortIndex !== void 0 && intersection.PortIsInput !== void 0) {
|
|
3580
4133
|
this.#portHovering = {
|
|
@@ -3597,8 +4150,15 @@ var NodeSubsystem2 = class {
|
|
|
3597
4150
|
hoveringSomething = true;
|
|
3598
4151
|
}
|
|
3599
4152
|
if (this.#widgetHovering !== null) {
|
|
3600
|
-
this.#widgetHovering.
|
|
3601
|
-
|
|
4153
|
+
if (!this.#widgetHovering.Disabled?.()) {
|
|
4154
|
+
this.#widgetCurrentlyClicking = this.#widgetHovering;
|
|
4155
|
+
this.#capturedWidgetBox = this.#widgetHoveringBox === null ? null : {
|
|
4156
|
+
Position: { x: this.#widgetHoveringBox.Position.x, y: this.#widgetHoveringBox.Position.y },
|
|
4157
|
+
Size: { x: this.#widgetHoveringBox.Size.x, y: this.#widgetHoveringBox.Size.y }
|
|
4158
|
+
};
|
|
4159
|
+
CopyVector2(this.#capturedPointer, mousePosition);
|
|
4160
|
+
this.#widgetCurrentlyClicking.PointerDown?.(this.#widgetPointer());
|
|
4161
|
+
}
|
|
3602
4162
|
hoveringSomething = true;
|
|
3603
4163
|
}
|
|
3604
4164
|
if (this.#portHovering === null) {
|
|
@@ -3613,12 +4173,12 @@ var NodeSubsystem2 = class {
|
|
|
3613
4173
|
return hoveringSomething || ctrlKey;
|
|
3614
4174
|
}
|
|
3615
4175
|
if (this.#portHovering.InputPort) {
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
4176
|
+
const port = this.#portHovering.Port;
|
|
4177
|
+
const wires = port.connections();
|
|
4178
|
+
const picked = wires.find((wire) => wire.mouseOverPort(mousePosition) === port) ?? wires[0];
|
|
4179
|
+
if (picked !== void 0) {
|
|
4180
|
+
picked.clearInput();
|
|
4181
|
+
this.#connectionSelected = picked;
|
|
3622
4182
|
}
|
|
3623
4183
|
} else {
|
|
3624
4184
|
let inNode = this.#portHovering.Node;
|
|
@@ -3638,7 +4198,19 @@ var NodeSubsystem2 = class {
|
|
|
3638
4198
|
}
|
|
3639
4199
|
return true;
|
|
3640
4200
|
}
|
|
4201
|
+
#widgetPointer() {
|
|
4202
|
+
return {
|
|
4203
|
+
position: { x: this.#capturedPointer.x, y: this.#capturedPointer.y },
|
|
4204
|
+
inside: this.#capturedWidgetBox !== null && InBox(this.#capturedWidgetBox, this.#capturedPointer)
|
|
4205
|
+
};
|
|
4206
|
+
}
|
|
3641
4207
|
mouseDragEvent(delta, scale) {
|
|
4208
|
+
if (this.#widgetCurrentlyClicking !== null) {
|
|
4209
|
+
this.#capturedPointer.x += delta.x;
|
|
4210
|
+
this.#capturedPointer.y += delta.y;
|
|
4211
|
+
this.#widgetCurrentlyClicking.PointerMove?.(this.#widgetPointer());
|
|
4212
|
+
return true;
|
|
4213
|
+
}
|
|
3642
4214
|
let nodeMoved = false;
|
|
3643
4215
|
VectorPool.run(() => {
|
|
3644
4216
|
const scaledDelta = VectorPool.get();
|
|
@@ -3757,13 +4329,20 @@ var NodeSubsystem2 = class {
|
|
|
3757
4329
|
}
|
|
3758
4330
|
#firstCompatiblePort(node, side, against) {
|
|
3759
4331
|
const count = side === "INPUT" /* Input */ ? node.inputs() : node.outputs();
|
|
4332
|
+
let firstHidden = -1;
|
|
3760
4333
|
for (let i = 0; i < count; i++) {
|
|
3761
4334
|
const port = side === "INPUT" /* Input */ ? node.inputPort(i) : node.outputPort(i);
|
|
3762
|
-
if (portsCompatible(port, against)) {
|
|
4335
|
+
if (!portsCompatible(port, against)) {
|
|
4336
|
+
continue;
|
|
4337
|
+
}
|
|
4338
|
+
if (!port.hidden()) {
|
|
3763
4339
|
return i;
|
|
3764
4340
|
}
|
|
4341
|
+
if (firstHidden === -1) {
|
|
4342
|
+
firstHidden = i;
|
|
4343
|
+
}
|
|
3765
4344
|
}
|
|
3766
|
-
return
|
|
4345
|
+
return firstHidden;
|
|
3767
4346
|
}
|
|
3768
4347
|
clickEnd() {
|
|
3769
4348
|
for (let i = 0; i < this.#nodesGrabbed.Count(); i++) {
|
|
@@ -3779,10 +4358,11 @@ var NodeSubsystem2 = class {
|
|
|
3779
4358
|
this.#boxSelect = false;
|
|
3780
4359
|
}
|
|
3781
4360
|
if (this.#widgetCurrentlyClicking !== null) {
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
}
|
|
4361
|
+
const widget = this.#widgetCurrentlyClicking;
|
|
4362
|
+
const pointer = this.#widgetPointer();
|
|
3785
4363
|
this.#widgetCurrentlyClicking = null;
|
|
4364
|
+
this.#capturedWidgetBox = null;
|
|
4365
|
+
widget.PointerUp?.(pointer);
|
|
3786
4366
|
}
|
|
3787
4367
|
if (this.#connectionSelected === null) {
|
|
3788
4368
|
return;
|
|
@@ -3827,7 +4407,7 @@ var NodeSubsystem2 = class {
|
|
|
3827
4407
|
inputPort.connections()[0].clearOutput();
|
|
3828
4408
|
this.#removeConnection(inputPort.connections()[0], false);
|
|
3829
4409
|
}
|
|
3830
|
-
conn.setInput(this.#portHovering.Node, this.#portHovering.Index, replace);
|
|
4410
|
+
conn.setInput(this.#portHovering.Node, this.#portHovering.Index, replace, this.#insertIndex());
|
|
3831
4411
|
} else {
|
|
3832
4412
|
conn.setOutput(this.#portHovering.Node, this.#portHovering.Index);
|
|
3833
4413
|
}
|
|
@@ -3967,6 +4547,14 @@ var NodeSubsystem2 = class {
|
|
|
3967
4547
|
this.#removeNodeConnections(nodeIndex);
|
|
3968
4548
|
const node = this.#nodes[nodeIndex];
|
|
3969
4549
|
this.#nodes.splice(nodeIndex, 1);
|
|
4550
|
+
if (this.#nodeHovering === nodeIndex) {
|
|
4551
|
+
this.#nodeHovering = -1;
|
|
4552
|
+
} else if (this.#nodeHovering > nodeIndex) {
|
|
4553
|
+
this.#nodeHovering--;
|
|
4554
|
+
}
|
|
4555
|
+
if (this.#portHovering?.Node === node) {
|
|
4556
|
+
this.#portHovering = null;
|
|
4557
|
+
}
|
|
3970
4558
|
requestRender();
|
|
3971
4559
|
for (let i = 0; i < this.#nodeRemovedCallbacks.length; i++) {
|
|
3972
4560
|
this.#nodeRemovedCallbacks[i](node);
|
|
@@ -4191,7 +4779,8 @@ var NodeSubsystem2 = class {
|
|
|
4191
4779
|
}
|
|
4192
4780
|
if (intersection.Widget !== void 0) {
|
|
4193
4781
|
this.#widgetHovering = intersection.Widget;
|
|
4194
|
-
this.#
|
|
4782
|
+
this.#widgetHoveringBox = intersection.WidgetBox ?? null;
|
|
4783
|
+
this.#cursor = intersection.Widget.Disabled?.() ? "default" /* Default */ : intersection.Widget.Cursor?.() ?? "pointer" /* Pointer */;
|
|
4195
4784
|
}
|
|
4196
4785
|
if (intersection.Port !== void 0 && intersection.Node !== void 0 && intersection.PortIndex !== void 0 && intersection.PortIsInput !== void 0) {
|
|
4197
4786
|
this.#portHovering = {
|
|
@@ -4214,6 +4803,9 @@ var NodeSubsystem2 = class {
|
|
|
4214
4803
|
}
|
|
4215
4804
|
this.#nodes[i].render(ctx, camera, state, mousePosition, this.#postProcessPass);
|
|
4216
4805
|
}
|
|
4806
|
+
if (this.#widgetCurrentlyClicking !== null) {
|
|
4807
|
+
this.#cursor = this.#widgetCurrentlyClicking.Cursor?.() ?? "pointer" /* Pointer */;
|
|
4808
|
+
}
|
|
4217
4809
|
}
|
|
4218
4810
|
#boxSelectionScreenspaceBox(camera) {
|
|
4219
4811
|
const box = { Position: Zero(), Size: Zero() };
|
|
@@ -4222,29 +4814,174 @@ var NodeSubsystem2 = class {
|
|
|
4222
4814
|
SubVector2(box.Size, box.Size, box.Position);
|
|
4223
4815
|
return box;
|
|
4224
4816
|
}
|
|
4817
|
+
/**
|
|
4818
|
+
* Where a wire being dropped on the hovered array input would go, or
|
|
4819
|
+
* undefined when it is not over one.
|
|
4820
|
+
*/
|
|
4821
|
+
#insertIndex() {
|
|
4822
|
+
const hovering = this.#portHovering;
|
|
4823
|
+
if (hovering === null || !hovering.InputPort || this.#lastMousePosition === void 0 || hovering.Port.getPortType() !== "INPUTARRAY" /* InputArray */) {
|
|
4824
|
+
return void 0;
|
|
4825
|
+
}
|
|
4826
|
+
const box = hovering.Node.inputPortPosition(hovering.Index);
|
|
4827
|
+
return arrayInsertIndex(box, hovering.Port.connections().length, this.#lastMousePosition.y);
|
|
4828
|
+
}
|
|
4829
|
+
#renderInsertMarker(ctx, camera) {
|
|
4830
|
+
const hovering = this.#portHovering;
|
|
4831
|
+
if (this.#connectionSelected === null || this.#connectionSelected.inPort() !== null || hovering === null) {
|
|
4832
|
+
return;
|
|
4833
|
+
}
|
|
4834
|
+
const index = this.#insertIndex();
|
|
4835
|
+
const count = hovering.Port.connections().length;
|
|
4836
|
+
if (index === void 0 || count === 0) {
|
|
4837
|
+
return;
|
|
4838
|
+
}
|
|
4839
|
+
const box = hovering.Node.inputPortPosition(hovering.Index);
|
|
4840
|
+
const y = box.Position.y + index * box.Size.y / count;
|
|
4841
|
+
const overhang = 6 * camera.zoom;
|
|
4842
|
+
ctx.save();
|
|
4843
|
+
ctx.strokeStyle = "#ffffff";
|
|
4844
|
+
ctx.lineWidth = Math.max(1, 2 * camera.zoom);
|
|
4845
|
+
ctx.beginPath();
|
|
4846
|
+
ctx.moveTo(box.Position.x - overhang, y);
|
|
4847
|
+
ctx.lineTo(box.Position.x + box.Size.x + overhang, y);
|
|
4848
|
+
ctx.stroke();
|
|
4849
|
+
ctx.restore();
|
|
4850
|
+
}
|
|
4225
4851
|
render(ctx, camera, mousePosition) {
|
|
4226
4852
|
this.#lastCtx = ctx;
|
|
4853
|
+
this.#lastMousePosition = mousePosition;
|
|
4227
4854
|
this.#cursor = "default" /* Default */;
|
|
4228
4855
|
const awaitingLayout = this.#nodes.some((node) => !node.hasBeenLaidOut());
|
|
4856
|
+
for (const node of this.#nodes) {
|
|
4857
|
+
if (node.hasBeenLaidOut()) {
|
|
4858
|
+
node.placePorts(node.calculateBounds(ctx, camera), camera.zoom);
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4229
4861
|
TimeExecution("Render_Connections", () => {
|
|
4230
4862
|
this.#renderConnections(ctx, camera, mousePosition);
|
|
4231
4863
|
});
|
|
4232
4864
|
TimeExecution("Render_Nodes", () => {
|
|
4233
4865
|
this.#renderNodes(ctx, camera, mousePosition);
|
|
4234
4866
|
});
|
|
4867
|
+
this.#renderInsertMarker(ctx, camera);
|
|
4235
4868
|
if (awaitingLayout) {
|
|
4236
4869
|
requestRender();
|
|
4237
4870
|
}
|
|
4238
4871
|
if (this.#boxSelect) {
|
|
4239
4872
|
const box = this.#boxSelectionScreenspaceBox(camera);
|
|
4240
4873
|
ctx.setLineDash([Theme.BoxSelect.LineDashLength]);
|
|
4241
|
-
|
|
4874
|
+
new BoxStyle({
|
|
4875
|
+
border: { color: Theme.BoxSelect.Color, size: Theme.BoxSelect.Size },
|
|
4876
|
+
color: "rgba(0,0,0,0)",
|
|
4877
|
+
radius: Theme.BoxSelect.Radius
|
|
4878
|
+
}).Draw(ctx, box, 1);
|
|
4242
4879
|
ctx.setLineDash([]);
|
|
4243
4880
|
}
|
|
4244
4881
|
return { cursorStyle: this.#cursor };
|
|
4245
4882
|
}
|
|
4246
4883
|
};
|
|
4247
4884
|
|
|
4885
|
+
// src/popup.ts
|
|
4886
|
+
var Popup = class {
|
|
4887
|
+
#title;
|
|
4888
|
+
#options;
|
|
4889
|
+
#content;
|
|
4890
|
+
#onClose;
|
|
4891
|
+
// Styling ================================================================
|
|
4892
|
+
#buttonCSS;
|
|
4893
|
+
// Runtime ================================================================
|
|
4894
|
+
#popup;
|
|
4895
|
+
#keyHandler;
|
|
4896
|
+
constructor(config) {
|
|
4897
|
+
this.#title = config.title;
|
|
4898
|
+
this.#options = config.options;
|
|
4899
|
+
this.#content = config.content;
|
|
4900
|
+
this.#onClose = config.onClose;
|
|
4901
|
+
this.#buttonCSS = config.buttonCSS === void 0 ? "flex-grow: 1; margin-right: 8px;" : config.buttonCSS;
|
|
4902
|
+
this.#popup = null;
|
|
4903
|
+
this.#keyHandler = null;
|
|
4904
|
+
}
|
|
4905
|
+
Hide() {
|
|
4906
|
+
if (this.#popup === null) {
|
|
4907
|
+
return;
|
|
4908
|
+
}
|
|
4909
|
+
if (this.#keyHandler !== null) {
|
|
4910
|
+
document.removeEventListener("keydown", this.#keyHandler, true);
|
|
4911
|
+
this.#keyHandler = null;
|
|
4912
|
+
}
|
|
4913
|
+
document.body.removeChild(this.#popup);
|
|
4914
|
+
this.#popup = null;
|
|
4915
|
+
}
|
|
4916
|
+
#close(option) {
|
|
4917
|
+
this.Hide();
|
|
4918
|
+
if (this.#onClose !== void 0) {
|
|
4919
|
+
this.#onClose(option);
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
Show() {
|
|
4923
|
+
this.#popup = document.createElement("div");
|
|
4924
|
+
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};`;
|
|
4925
|
+
document.body.appendChild(this.#popup);
|
|
4926
|
+
const container = document.createElement("div");
|
|
4927
|
+
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;`;
|
|
4928
|
+
this.#popup.appendChild(container);
|
|
4929
|
+
const title = document.createElement("h2");
|
|
4930
|
+
title.textContent = this.#title;
|
|
4931
|
+
title.style.cssText = "margin-top:0;margin-bottom:16px;";
|
|
4932
|
+
container.appendChild(title);
|
|
4933
|
+
if (this.#content !== void 0) {
|
|
4934
|
+
container.appendChild(this.#content());
|
|
4935
|
+
}
|
|
4936
|
+
const buttonContainer = document.createElement("div");
|
|
4937
|
+
buttonContainer.style.cssText = "margin-top:16px;justify-content:space-around;align-items:center;flex-direction:row;display:flex;";
|
|
4938
|
+
container.appendChild(buttonContainer);
|
|
4939
|
+
for (let i = 0; i < this.#options.length; i++) {
|
|
4940
|
+
const button = document.createElement("button");
|
|
4941
|
+
button.style.cssText = this.#buttonCSS;
|
|
4942
|
+
button.textContent = this.#options[i];
|
|
4943
|
+
button.onclick = () => this.#close(this.#options[i]);
|
|
4944
|
+
buttonContainer.appendChild(button);
|
|
4945
|
+
}
|
|
4946
|
+
this.#keyHandler = (e) => {
|
|
4947
|
+
if (e.key === "Enter") {
|
|
4948
|
+
e.preventDefault();
|
|
4949
|
+
this.#close(this.#options.length > 0 ? this.#options[0] : null);
|
|
4950
|
+
} else if (e.key === "Escape") {
|
|
4951
|
+
e.preventDefault();
|
|
4952
|
+
this.#close(null);
|
|
4953
|
+
}
|
|
4954
|
+
e.stopPropagation();
|
|
4955
|
+
};
|
|
4956
|
+
document.addEventListener("keydown", this.#keyHandler, true);
|
|
4957
|
+
}
|
|
4958
|
+
};
|
|
4959
|
+
|
|
4960
|
+
// src/popups/string.ts
|
|
4961
|
+
function SetStringPopup(config) {
|
|
4962
|
+
let input = null;
|
|
4963
|
+
return new Popup({
|
|
4964
|
+
title: config.title,
|
|
4965
|
+
options: ["Set", "Cancel"],
|
|
4966
|
+
content: () => {
|
|
4967
|
+
const container = document.createElement("div");
|
|
4968
|
+
input = document.createElement("input");
|
|
4969
|
+
input.value = config.startingValue;
|
|
4970
|
+
container.append(input);
|
|
4971
|
+
return container;
|
|
4972
|
+
},
|
|
4973
|
+
onClose: (button) => {
|
|
4974
|
+
if (button !== "Set" || input === null) {
|
|
4975
|
+
if (config.onCancel) {
|
|
4976
|
+
config.onCancel();
|
|
4977
|
+
}
|
|
4978
|
+
return;
|
|
4979
|
+
}
|
|
4980
|
+
config.onUpdate(input.value);
|
|
4981
|
+
}
|
|
4982
|
+
});
|
|
4983
|
+
}
|
|
4984
|
+
|
|
4248
4985
|
// src/popups/form.ts
|
|
4249
4986
|
function FormPopup(config) {
|
|
4250
4987
|
let input = new Array();
|
|
@@ -4361,9 +5098,11 @@ var FlowNode = class {
|
|
|
4361
5098
|
#titleChangeCallback;
|
|
4362
5099
|
#infoChangeCallback;
|
|
4363
5100
|
// Styling ================================================================
|
|
5101
|
+
#styleConfig;
|
|
5102
|
+
#styledForTheme = -1;
|
|
4364
5103
|
#titleColor;
|
|
4365
5104
|
#selectedStyle;
|
|
4366
|
-
#stateStyles;
|
|
5105
|
+
#stateStyles = /* @__PURE__ */ new Map();
|
|
4367
5106
|
#padding;
|
|
4368
5107
|
#portTextStyle;
|
|
4369
5108
|
#elementSpacing;
|
|
@@ -4375,6 +5114,7 @@ var FlowNode = class {
|
|
|
4375
5114
|
#data;
|
|
4376
5115
|
#registeredAnyPropertyChangeCallbacks;
|
|
4377
5116
|
#registeredPropertyChangeCallbacks;
|
|
5117
|
+
#registeredPropertyEditCallbacks = /* @__PURE__ */ new Map();
|
|
4378
5118
|
constructor(config) {
|
|
4379
5119
|
this.#input = new Array();
|
|
4380
5120
|
this.#output = new Array();
|
|
@@ -4427,58 +5167,17 @@ var FlowNode = class {
|
|
|
4427
5167
|
this.#position = config?.position === void 0 ? { x: 0, y: 0 } : config.position;
|
|
4428
5168
|
this.#title = new Text(
|
|
4429
5169
|
config?.title === void 0 ? "" : config.title,
|
|
4430
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
4431
|
-
size: 16,
|
|
4432
|
-
weight: "bold" /* Bold */,
|
|
4433
|
-
color: Theme.Node.FontColor
|
|
4434
|
-
})
|
|
5170
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 16, weight: "bold" /* Bold */ })
|
|
4435
5171
|
);
|
|
4436
5172
|
this.#subTitle = new Text(
|
|
4437
5173
|
config?.subTitle === void 0 ? "" : config.subTitle,
|
|
4438
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
4439
|
-
size: 10,
|
|
4440
|
-
weight: "bold" /* Bold */,
|
|
4441
|
-
color: Theme.Node.FontColor
|
|
4442
|
-
})
|
|
4443
|
-
);
|
|
4444
|
-
this.#infoSymbol = new Text(
|
|
4445
|
-
"i",
|
|
4446
|
-
{
|
|
4447
|
-
size: 13,
|
|
4448
|
-
weight: "bold" /* Bold */,
|
|
4449
|
-
color: Theme.Node.FontColor
|
|
4450
|
-
}
|
|
5174
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 10, weight: "bold" /* Bold */ })
|
|
4451
5175
|
);
|
|
5176
|
+
this.#infoSymbol = new Text("i", { size: 13, weight: "bold" /* Bold */ });
|
|
4452
5177
|
this.#infoText = config?.info ? config?.info : "";
|
|
4453
|
-
this.#titleColor = config?.style?.title?.color === void 0 ? "#154050" : config?.style?.title?.color;
|
|
4454
5178
|
this.#padding = config?.style?.title?.padding === void 0 ? 15 : config?.style?.title?.padding;
|
|
4455
|
-
this.#
|
|
4456
|
-
this.#
|
|
4457
|
-
border: { color: Theme.Node.Border.Idle, size: 1 },
|
|
4458
|
-
radius: Theme.Node.BorderRadius,
|
|
4459
|
-
color: Theme.Node.BackgroundColor
|
|
4460
|
-
})));
|
|
4461
|
-
this.#stateStyles.set(1 /* MouseOver */, new BoxStyle(BoxStyleWithFallback(config?.style?.mouseOver, {
|
|
4462
|
-
border: { color: Theme.Node.Border.MouseOver, size: 1.1 },
|
|
4463
|
-
radius: Theme.Node.BorderRadius,
|
|
4464
|
-
color: Theme.Node.BackgroundColor
|
|
4465
|
-
})));
|
|
4466
|
-
this.#stateStyles.set(2 /* Grabbed */, new BoxStyle(BoxStyleWithFallback(config?.style?.grabbed, {
|
|
4467
|
-
border: { color: Theme.Node.Border.Grabbed, size: 2 },
|
|
4468
|
-
radius: Theme.Node.BorderRadius,
|
|
4469
|
-
color: Theme.Node.BackgroundColor
|
|
4470
|
-
})));
|
|
4471
|
-
this.#selectedStyle = new BoxStyle(BoxStyleWithFallback(config?.style?.selected, {
|
|
4472
|
-
border: { color: Theme.Node.Border.Selected, size: 1 },
|
|
4473
|
-
radius: Theme.Node.BorderRadius,
|
|
4474
|
-
color: Theme.Node.BackgroundColor
|
|
4475
|
-
}));
|
|
4476
|
-
this.#portTextStyle = new TextStyle({
|
|
4477
|
-
size: config?.style?.portText?.size === void 0 ? 14 : config?.style?.portText?.size,
|
|
4478
|
-
color: config?.style?.portText?.color === void 0 ? Theme.Node.Port.FontColor : config?.style?.portText?.color,
|
|
4479
|
-
font: config?.style?.portText?.font,
|
|
4480
|
-
weight: config?.style?.portText?.weight
|
|
4481
|
-
});
|
|
5179
|
+
this.#styleConfig = config?.style;
|
|
5180
|
+
this.#applyStyle();
|
|
4482
5181
|
if (config?.inputs !== void 0) {
|
|
4483
5182
|
for (let i = 0; i < config.inputs.length; i++) {
|
|
4484
5183
|
this.addInput(config.inputs[i]);
|
|
@@ -4490,12 +5189,8 @@ var FlowNode = class {
|
|
|
4490
5189
|
}
|
|
4491
5190
|
}
|
|
4492
5191
|
if (config?.widgets !== void 0) {
|
|
4493
|
-
for (
|
|
4494
|
-
|
|
4495
|
-
if (widget.type === void 0) {
|
|
4496
|
-
continue;
|
|
4497
|
-
}
|
|
4498
|
-
this.addWidget(GlobalWidgetFactory.create(this, widget.type, widget.config));
|
|
5192
|
+
for (const widget of config.widgets) {
|
|
5193
|
+
this.addWidget(GlobalWidgetFactory.createFromConfig(this, widget));
|
|
4499
5194
|
}
|
|
4500
5195
|
}
|
|
4501
5196
|
}
|
|
@@ -4552,7 +5247,7 @@ var FlowNode = class {
|
|
|
4552
5247
|
}
|
|
4553
5248
|
setProperty(name, value) {
|
|
4554
5249
|
const oldValue = this.#data[name];
|
|
4555
|
-
if (oldValue
|
|
5250
|
+
if (Object.is(oldValue, value)) {
|
|
4556
5251
|
return;
|
|
4557
5252
|
}
|
|
4558
5253
|
this.#data[name] = value;
|
|
@@ -4568,6 +5263,32 @@ var FlowNode = class {
|
|
|
4568
5263
|
callbacks[i](oldValue, value);
|
|
4569
5264
|
}
|
|
4570
5265
|
}
|
|
5266
|
+
/**
|
|
5267
|
+
* Listens for a person changing the property through a widget, but not
|
|
5268
|
+
* for the property being set from code.
|
|
5269
|
+
*/
|
|
5270
|
+
addPropertyEditListener(name, callback) {
|
|
5271
|
+
let callbacks = this.#registeredPropertyEditCallbacks.get(name);
|
|
5272
|
+
if (callbacks === void 0) {
|
|
5273
|
+
callbacks = [];
|
|
5274
|
+
this.#registeredPropertyEditCallbacks.set(name, callbacks);
|
|
5275
|
+
}
|
|
5276
|
+
callbacks.push(callback);
|
|
5277
|
+
}
|
|
5278
|
+
/**
|
|
5279
|
+
* Sets the property as a person's finished edit, so edit listeners hear
|
|
5280
|
+
* it even when the value was already showing.
|
|
5281
|
+
*/
|
|
5282
|
+
editProperty(name, value) {
|
|
5283
|
+
this.setProperty(name, value);
|
|
5284
|
+
const callbacks = this.#registeredPropertyEditCallbacks.get(name);
|
|
5285
|
+
if (callbacks === void 0) {
|
|
5286
|
+
return;
|
|
5287
|
+
}
|
|
5288
|
+
for (const callback of [...callbacks]) {
|
|
5289
|
+
callback(value);
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
4571
5292
|
getProperty(name) {
|
|
4572
5293
|
return this.#data[name];
|
|
4573
5294
|
}
|
|
@@ -4856,6 +5577,7 @@ var FlowNode = class {
|
|
|
4856
5577
|
// return this.#titleTextStyle.measure(ctx, scale, this.#title);
|
|
4857
5578
|
// }
|
|
4858
5579
|
calculateBounds(ctx, camera) {
|
|
5580
|
+
this.#followTheme();
|
|
4859
5581
|
const tempMeasurement = Zero();
|
|
4860
5582
|
const doublePadding = this.#padding * 2;
|
|
4861
5583
|
const screenSpacePosition = Zero();
|
|
@@ -4869,25 +5591,21 @@ var FlowNode = class {
|
|
|
4869
5591
|
size.x += size.y * 4;
|
|
4870
5592
|
}
|
|
4871
5593
|
size.x += doublePadding;
|
|
4872
|
-
size.y += doublePadding
|
|
4873
|
-
for (
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
|
|
4878
|
-
}
|
|
4879
|
-
size.y += this.#elementSpacing * this.#output.length;
|
|
4880
|
-
for (let i = 0; i < this.#output.length; i++) {
|
|
4881
|
-
const port = this.#output[i];
|
|
5594
|
+
size.y += doublePadding;
|
|
5595
|
+
for (const port of [...this.#input, ...this.#output]) {
|
|
5596
|
+
if (port.hidden()) {
|
|
5597
|
+
continue;
|
|
5598
|
+
}
|
|
4882
5599
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
4883
|
-
size.y += tempMeasurement.y;
|
|
5600
|
+
size.y += this.#portRowHeight(port, tempMeasurement.y) + this.#elementSpacing;
|
|
4884
5601
|
size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
|
|
4885
5602
|
}
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
5603
|
+
for (const widget of this.#widgets) {
|
|
5604
|
+
if (widget.Hidden?.()) {
|
|
5605
|
+
continue;
|
|
5606
|
+
}
|
|
5607
|
+
const eleSize = widget.Size();
|
|
5608
|
+
size.y += eleSize.y + this.#elementSpacing;
|
|
4891
5609
|
size.x = Math.max(size.x, eleSize.x + doublePadding);
|
|
4892
5610
|
}
|
|
4893
5611
|
size.y += this.#elementSpacing;
|
|
@@ -4971,7 +5689,7 @@ var FlowNode = class {
|
|
|
4971
5689
|
intersection.Node = this;
|
|
4972
5690
|
}
|
|
4973
5691
|
for (let i = 0; i < this.#inputPortPositions.Count(); i++) {
|
|
4974
|
-
if (InBox(this.#inputPortPositions.At(i), position)) {
|
|
5692
|
+
if (!this.#input[i].hidden() && InBox(this.#inputPortPositions.At(i), position)) {
|
|
4975
5693
|
intersection.Node = this;
|
|
4976
5694
|
intersection.PortIndex = i;
|
|
4977
5695
|
intersection.PortIsInput = true;
|
|
@@ -4979,7 +5697,7 @@ var FlowNode = class {
|
|
|
4979
5697
|
}
|
|
4980
5698
|
}
|
|
4981
5699
|
for (let i = 0; i < this.#outputPortPositions.Count(); i++) {
|
|
4982
|
-
if (InBox(this.#outputPortPositions.At(i), position)) {
|
|
5700
|
+
if (!this.#output[i].hidden() && InBox(this.#outputPortPositions.At(i), position)) {
|
|
4983
5701
|
intersection.Node = this;
|
|
4984
5702
|
intersection.PortIndex = i;
|
|
4985
5703
|
intersection.PortIsInput = false;
|
|
@@ -4987,10 +5705,12 @@ var FlowNode = class {
|
|
|
4987
5705
|
}
|
|
4988
5706
|
}
|
|
4989
5707
|
for (let i = 0; i < this.#widgetPositions.Count(); i++) {
|
|
4990
|
-
|
|
5708
|
+
const widget = this.#widgets[i];
|
|
5709
|
+
if (widget !== void 0 && !widget.Hidden?.() && InBox(this.#widgetPositions.At(i), position)) {
|
|
4991
5710
|
intersection.Node = this;
|
|
4992
5711
|
intersection.WidgetIndex = i;
|
|
4993
|
-
intersection.Widget =
|
|
5712
|
+
intersection.Widget = widget;
|
|
5713
|
+
intersection.WidgetBox = this.#widgetPositions.At(i);
|
|
4994
5714
|
}
|
|
4995
5715
|
}
|
|
4996
5716
|
return intersection;
|
|
@@ -5005,10 +5725,6 @@ var FlowNode = class {
|
|
|
5005
5725
|
return this.#title.get();
|
|
5006
5726
|
}
|
|
5007
5727
|
setTitle(newTitle) {
|
|
5008
|
-
if (!this.#canEditTitle) {
|
|
5009
|
-
console.warn("setTitle instruction ignored, as node has been marked un-editable");
|
|
5010
|
-
return;
|
|
5011
|
-
}
|
|
5012
5728
|
let cleaned = newTitle;
|
|
5013
5729
|
if (cleaned === null || cleaned === void 0) {
|
|
5014
5730
|
cleaned = "";
|
|
@@ -5025,9 +5741,6 @@ var FlowNode = class {
|
|
|
5025
5741
|
}
|
|
5026
5742
|
}
|
|
5027
5743
|
setInfo(newInfo) {
|
|
5028
|
-
if (!this.#canEditInfo) {
|
|
5029
|
-
console.warn("setInfo instruction ignored, as node has been marked un-editable");
|
|
5030
|
-
}
|
|
5031
5744
|
let cleaned = newInfo;
|
|
5032
5745
|
if (cleaned === null || cleaned === void 0) {
|
|
5033
5746
|
cleaned = "";
|
|
@@ -5046,6 +5759,12 @@ var FlowNode = class {
|
|
|
5046
5759
|
inputPortPosition(index) {
|
|
5047
5760
|
return this.#inputPortPositions.At(index);
|
|
5048
5761
|
}
|
|
5762
|
+
widgetPosition(index) {
|
|
5763
|
+
if (index < 0 || index >= this.#widgetPositions.Count()) {
|
|
5764
|
+
return void 0;
|
|
5765
|
+
}
|
|
5766
|
+
return this.#widgetPositions.At(index);
|
|
5767
|
+
}
|
|
5049
5768
|
inputPort(index) {
|
|
5050
5769
|
return this.#input[index];
|
|
5051
5770
|
}
|
|
@@ -5061,6 +5780,35 @@ var FlowNode = class {
|
|
|
5061
5780
|
outputs() {
|
|
5062
5781
|
return this.#output.length;
|
|
5063
5782
|
}
|
|
5783
|
+
#applyStyle() {
|
|
5784
|
+
const style = this.#styleConfig;
|
|
5785
|
+
const titleText = style?.title?.textStyle?.color ?? Theme.Node.Title.FontColor;
|
|
5786
|
+
this.#title.style().setColor(titleText);
|
|
5787
|
+
this.#subTitle.style().setColor(titleText);
|
|
5788
|
+
this.#infoSymbol.style().setColor(titleText);
|
|
5789
|
+
this.#titleColor = style?.title?.color ?? Theme.Node.Title.Color;
|
|
5790
|
+
const box = (config, border, size) => new BoxStyle(BoxStyleWithFallback(config, {
|
|
5791
|
+
border: { color: border, size },
|
|
5792
|
+
radius: Theme.Node.BorderRadius,
|
|
5793
|
+
color: Theme.Node.BackgroundColor
|
|
5794
|
+
}));
|
|
5795
|
+
this.#stateStyles.set(0 /* Idle */, box(style?.idle, Theme.Node.Border.Idle, 1));
|
|
5796
|
+
this.#stateStyles.set(1 /* MouseOver */, box(style?.mouseOver, Theme.Node.Border.MouseOver, 1.1));
|
|
5797
|
+
this.#stateStyles.set(2 /* Grabbed */, box(style?.grabbed, Theme.Node.Border.Grabbed, 2));
|
|
5798
|
+
this.#selectedStyle = box(style?.selected, Theme.Node.Border.Selected, 1);
|
|
5799
|
+
this.#portTextStyle = new TextStyle({
|
|
5800
|
+
size: style?.portText?.size ?? 14,
|
|
5801
|
+
color: style?.portText?.color ?? Theme.Node.Port.FontColor,
|
|
5802
|
+
font: style?.portText?.font,
|
|
5803
|
+
weight: style?.portText?.weight
|
|
5804
|
+
});
|
|
5805
|
+
this.#styledForTheme = themeVersion();
|
|
5806
|
+
}
|
|
5807
|
+
#followTheme() {
|
|
5808
|
+
if (this.#styledForTheme !== themeVersion()) {
|
|
5809
|
+
this.#applyStyle();
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
5064
5812
|
#calculateStyle(state) {
|
|
5065
5813
|
if (this.#selected && state === 0 /* Idle */) {
|
|
5066
5814
|
return this.#selectedStyle;
|
|
@@ -5077,9 +5825,22 @@ var FlowNode = class {
|
|
|
5077
5825
|
clearMessages() {
|
|
5078
5826
|
this.#messages = [];
|
|
5079
5827
|
}
|
|
5828
|
+
#portRowHeight(port, textHeight) {
|
|
5829
|
+
const slots = port.slots();
|
|
5830
|
+
return slots > 1 ? Math.max(textHeight, slots * ARRAY_SLOT_PITCH) : textHeight;
|
|
5831
|
+
}
|
|
5080
5832
|
hasBeenLaidOut() {
|
|
5081
5833
|
return this.#laidOut;
|
|
5082
5834
|
}
|
|
5835
|
+
/**
|
|
5836
|
+
* Throws away where the ports were last placed, so wires wait for the
|
|
5837
|
+
* next draw instead of pointing at the old spots.
|
|
5838
|
+
*/
|
|
5839
|
+
invalidateLayout() {
|
|
5840
|
+
this.#laidOut = false;
|
|
5841
|
+
this.#portOffsets = { input: [], output: [] };
|
|
5842
|
+
requestRender();
|
|
5843
|
+
}
|
|
5083
5844
|
#laidOut = false;
|
|
5084
5845
|
render(ctx, camera, state, mousePosition, postProcess) {
|
|
5085
5846
|
this.#laidOut = true;
|
|
@@ -5117,6 +5878,9 @@ var FlowNode = class {
|
|
|
5117
5878
|
[nodeStyle.radius() * camera.zoom, nodeStyle.radius() * camera.zoom, 0, 0]
|
|
5118
5879
|
);
|
|
5119
5880
|
ctx.fill();
|
|
5881
|
+
if (Theme.Node.Bevel !== null) {
|
|
5882
|
+
drawBevel(ctx, nodeBounds, camera.zoom, Theme.Node.Bevel, false);
|
|
5883
|
+
}
|
|
5120
5884
|
const titlePosition = VectorPool.get();
|
|
5121
5885
|
titlePosition.x = nodeBounds.Position.x + nodeBounds.Size.x / 2;
|
|
5122
5886
|
titlePosition.y = nodeBounds.Position.y + scaledPadding + titleSize.y / 2;
|
|
@@ -5177,8 +5941,12 @@ var FlowNode = class {
|
|
|
5177
5941
|
for (let i = 0; i < this.#input.length; i++) {
|
|
5178
5942
|
ctx.textAlign = "left" /* Left */;
|
|
5179
5943
|
const port = this.#input[i];
|
|
5944
|
+
if (port.hidden()) {
|
|
5945
|
+
this.#inputPortPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
|
|
5946
|
+
continue;
|
|
5947
|
+
}
|
|
5180
5948
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
5181
|
-
const portHeight = tempMeasurement.y * camera.zoom;
|
|
5949
|
+
const portHeight = this.#portRowHeight(port, tempMeasurement.y) * camera.zoom;
|
|
5182
5950
|
const position = VectorPool.get();
|
|
5183
5951
|
position.x = nodeBounds.Position.x;
|
|
5184
5952
|
position.y = startY + portHeight / 2;
|
|
@@ -5191,6 +5959,10 @@ var FlowNode = class {
|
|
|
5191
5959
|
ctx.textAlign = "right" /* Right */;
|
|
5192
5960
|
for (let i = 0; i < this.#output.length; i++) {
|
|
5193
5961
|
const port = this.#output[i];
|
|
5962
|
+
if (port.hidden()) {
|
|
5963
|
+
this.#outputPortPositions.Push({ Position: { x: rightSide, y: startY }, Size: { x: 0, y: 0 } });
|
|
5964
|
+
continue;
|
|
5965
|
+
}
|
|
5194
5966
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
5195
5967
|
const portHeight = tempMeasurement.y * camera.zoom;
|
|
5196
5968
|
const position = VectorPool.get();
|
|
@@ -5203,13 +5975,18 @@ var FlowNode = class {
|
|
|
5203
5975
|
}
|
|
5204
5976
|
for (let i = 0; i < this.#widgets.length; i++) {
|
|
5205
5977
|
const widget = this.#widgets[i];
|
|
5978
|
+
if (widget.Hidden?.()) {
|
|
5979
|
+
this.#widgetPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
|
|
5980
|
+
continue;
|
|
5981
|
+
}
|
|
5206
5982
|
const widgetSize = widget.Size();
|
|
5207
|
-
const
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5983
|
+
const box = {
|
|
5984
|
+
Position: { x: nodeBounds.Position.x + scaledPadding, y: startY },
|
|
5985
|
+
Size: { x: nodeBounds.Size.x - scaledPadding * 2, y: widgetSize.y * camera.zoom }
|
|
5986
|
+
};
|
|
5987
|
+
widget.Draw(ctx, box, camera.zoom, mousePosition);
|
|
5988
|
+
this.#widgetPositions.Push(box);
|
|
5989
|
+
startY += box.Size.y + scaledElementSpacing;
|
|
5213
5990
|
}
|
|
5214
5991
|
const messageStart = VectorPool.get();
|
|
5215
5992
|
messageStart.x = nodeBounds.Position.x + nodeBounds.Size.x / 2;
|
|
@@ -5246,6 +6023,13 @@ var FlowNode = class {
|
|
|
5246
6023
|
record(this.#outputPortPositions, this.#portOffsets.output);
|
|
5247
6024
|
}
|
|
5248
6025
|
layoutPortsWithoutDrawing(bounds, zoom) {
|
|
6026
|
+
if (!this.placePorts(bounds, zoom)) {
|
|
6027
|
+
return false;
|
|
6028
|
+
}
|
|
6029
|
+
this.#widgetPositions.Clear();
|
|
6030
|
+
return true;
|
|
6031
|
+
}
|
|
6032
|
+
placePorts(bounds, zoom) {
|
|
5249
6033
|
if (this.#portOffsets.input.length === 0 && this.#portOffsets.output.length === 0) {
|
|
5250
6034
|
return false;
|
|
5251
6035
|
}
|
|
@@ -5267,7 +6051,6 @@ var FlowNode = class {
|
|
|
5267
6051
|
};
|
|
5268
6052
|
project(this.#portOffsets.input, this.#inputPortPositions);
|
|
5269
6053
|
project(this.#portOffsets.output, this.#outputPortPositions);
|
|
5270
|
-
this.#widgetPositions.Clear();
|
|
5271
6054
|
return true;
|
|
5272
6055
|
}
|
|
5273
6056
|
};
|
|
@@ -5459,33 +6242,33 @@ var CodeBlockEntry = class {
|
|
|
5459
6242
|
}
|
|
5460
6243
|
render(ctx, position, scale, maxWidth) {
|
|
5461
6244
|
this.#calculateLayout(ctx, maxWidth);
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
}
|
|
5468
|
-
const y = position.y + max + scale * 5;
|
|
6245
|
+
const padding = Theme.Note.CodeBlock.Padding * scale;
|
|
6246
|
+
const lineCount = this.#calculatedEntries.Count();
|
|
6247
|
+
const lastTop = lineCount === 0 ? 0 : this.#calculatedPositions.At(lineCount - 1).y;
|
|
6248
|
+
const contentHeight = lineCount === 0 ? 0 : (lastTop + this.#text.style().getSize()) * scale;
|
|
6249
|
+
const height2 = contentHeight + padding * 2;
|
|
5469
6250
|
ctx.fillStyle = Theme.Note.CodeBlock.BackgroundColor;
|
|
5470
6251
|
ctx.beginPath();
|
|
5471
6252
|
ctx.roundRect(
|
|
5472
6253
|
position.x,
|
|
5473
6254
|
position.y,
|
|
5474
6255
|
maxWidth * scale,
|
|
5475
|
-
|
|
6256
|
+
height2,
|
|
5476
6257
|
Theme.Note.CodeBlock.BorderRadius * scale
|
|
5477
6258
|
);
|
|
5478
6259
|
ctx.fill();
|
|
5479
|
-
|
|
6260
|
+
const baseline = ctx.textBaseline;
|
|
6261
|
+
ctx.textBaseline = "top";
|
|
6262
|
+
for (let i = 0; i < lineCount; i++) {
|
|
5480
6263
|
const entry = this.#calculatedEntries.At(i);
|
|
5481
6264
|
const pos = this.#calculatedPositions.At(i);
|
|
5482
6265
|
entry.render(ctx, scale, {
|
|
5483
6266
|
x: pos.x * scale + position.x + padding,
|
|
5484
6267
|
y: pos.y * scale + position.y + padding
|
|
5485
6268
|
});
|
|
5486
|
-
max = Math.max(max, pos.y * scale);
|
|
5487
6269
|
}
|
|
5488
|
-
|
|
6270
|
+
ctx.textBaseline = baseline;
|
|
6271
|
+
return height2;
|
|
5489
6272
|
}
|
|
5490
6273
|
};
|
|
5491
6274
|
var UnorderedListMarkdownEntry = class {
|
|
@@ -5863,9 +6646,10 @@ var MarkdownSyntaxParser = class {
|
|
|
5863
6646
|
this.#inc();
|
|
5864
6647
|
this.#inc();
|
|
5865
6648
|
this.#inc();
|
|
5866
|
-
const codeBlockText = this.#originalText.substring(start, end);
|
|
6649
|
+
const codeBlockText = this.#originalText.substring(start, end).replace(/\r?\n$/, "");
|
|
5867
6650
|
const entryText = new Text(codeBlockText);
|
|
5868
6651
|
this.#assignStandardStyling([entryText]);
|
|
6652
|
+
entryText.setColor(Theme.Note.CodeBlock.FontColor);
|
|
5869
6653
|
return new CodeBlockEntry(entryText);
|
|
5870
6654
|
}
|
|
5871
6655
|
parse() {
|
|
@@ -5920,6 +6704,7 @@ var BOX_SIZE = 10;
|
|
|
5920
6704
|
var FlowNote = class {
|
|
5921
6705
|
#originalText;
|
|
5922
6706
|
#document;
|
|
6707
|
+
#parsedForTheme = -1;
|
|
5923
6708
|
#width;
|
|
5924
6709
|
#edittingStyle;
|
|
5925
6710
|
// Runtime ================================================================
|
|
@@ -5962,6 +6747,7 @@ var FlowNote = class {
|
|
|
5962
6747
|
setText(text) {
|
|
5963
6748
|
this.#originalText = text;
|
|
5964
6749
|
this.#document = BuildMarkdown(this.#originalText);
|
|
6750
|
+
this.#parsedForTheme = themeVersion();
|
|
5965
6751
|
for (let i = 0; i < this.#contentChangeCallbacks.length; i++) {
|
|
5966
6752
|
this.#contentChangeCallbacks[i](this, text);
|
|
5967
6753
|
}
|
|
@@ -6049,6 +6835,10 @@ var FlowNote = class {
|
|
|
6049
6835
|
}
|
|
6050
6836
|
camera.graphSpaceToScreenSpace(this.#position, this.#tempPosition);
|
|
6051
6837
|
CopyVector2(this.#lastRenderedBox.Position, this.#tempPosition);
|
|
6838
|
+
if (this.#parsedForTheme !== themeVersion()) {
|
|
6839
|
+
this.#document = BuildMarkdown(this.#originalText);
|
|
6840
|
+
this.#parsedForTheme = themeVersion();
|
|
6841
|
+
}
|
|
6052
6842
|
const startY = this.#tempPosition.y;
|
|
6053
6843
|
const lineSpacing = Theme.Note.EntrySpacing * camera.zoom;
|
|
6054
6844
|
ctx.textAlign = "left" /* Left */;
|
|
@@ -6744,13 +7534,15 @@ var Minimap = class {
|
|
|
6744
7534
|
// src/graph.ts
|
|
6745
7535
|
function BuildBackgroundRenderer(backgroundColor) {
|
|
6746
7536
|
return (canvas, context, position, scale) => {
|
|
6747
|
-
context.fillStyle = backgroundColor;
|
|
7537
|
+
context.fillStyle = backgroundColor ?? Theme.Graph.BackgroundColor;
|
|
6748
7538
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
6749
|
-
const alpha =
|
|
7539
|
+
const alpha = Clamp01(scale - 0.3);
|
|
6750
7540
|
if (alpha <= 0) {
|
|
6751
7541
|
return;
|
|
6752
7542
|
}
|
|
6753
|
-
context.
|
|
7543
|
+
context.save();
|
|
7544
|
+
context.globalAlpha = alpha;
|
|
7545
|
+
context.fillStyle = Theme.Graph.DotColor;
|
|
6754
7546
|
const spacing = 100;
|
|
6755
7547
|
const pi2 = 2 * Math.PI;
|
|
6756
7548
|
const dotScale = 2 * scale;
|
|
@@ -6763,6 +7555,7 @@ function BuildBackgroundRenderer(backgroundColor) {
|
|
|
6763
7555
|
context.fill();
|
|
6764
7556
|
}
|
|
6765
7557
|
}
|
|
7558
|
+
context.restore();
|
|
6766
7559
|
};
|
|
6767
7560
|
}
|
|
6768
7561
|
var contextMenuGroup = "graph-context-menu";
|
|
@@ -6813,9 +7606,9 @@ var GraphView = class {
|
|
|
6813
7606
|
const results = {};
|
|
6814
7607
|
for (let i = 0; i < this.#subsystems.length; i++) {
|
|
6815
7608
|
TimeExecution("Render_Subsystem_" + i, () => {
|
|
6816
|
-
|
|
6817
|
-
if (
|
|
6818
|
-
|
|
7609
|
+
const layer = this.#subsystems[i].render(ctx, camera, mousePosition);
|
|
7610
|
+
if (layer?.cursorStyle) {
|
|
7611
|
+
results.cursorStyle = layer.cursorStyle;
|
|
6819
7612
|
}
|
|
6820
7613
|
});
|
|
6821
7614
|
}
|
|
@@ -6875,8 +7668,7 @@ var NodeFlowGraph = class {
|
|
|
6875
7668
|
if (config?.backgroundRenderer !== void 0) {
|
|
6876
7669
|
this.#backgroundRenderer = config?.backgroundRenderer;
|
|
6877
7670
|
} else {
|
|
6878
|
-
|
|
6879
|
-
this.#backgroundRenderer = BuildBackgroundRenderer(backgroundColor);
|
|
7671
|
+
this.#backgroundRenderer = BuildBackgroundRenderer(config?.backgroundColor);
|
|
6880
7672
|
}
|
|
6881
7673
|
this.#minimap = new Minimap(config?.minimap);
|
|
6882
7674
|
this.#mainNodeSubsystem.setConnectionReleasedHandler((dangling) => {
|
|
@@ -7343,11 +8135,15 @@ var NodeFlowGraph = class {
|
|
|
7343
8135
|
ColorWidget,
|
|
7344
8136
|
Connection,
|
|
7345
8137
|
ContextMenuItemState,
|
|
8138
|
+
CursorStyle,
|
|
7346
8139
|
DefaultConnectionRenderer,
|
|
8140
|
+
DefaultTheme,
|
|
7347
8141
|
FlowNode,
|
|
7348
8142
|
FlowNote,
|
|
7349
8143
|
GlobalWidgetFactory,
|
|
8144
|
+
GraphView,
|
|
7350
8145
|
ImageWidget,
|
|
8146
|
+
InBox,
|
|
7351
8147
|
MessageType,
|
|
7352
8148
|
Minimap,
|
|
7353
8149
|
NodeFactory,
|
|
@@ -7357,16 +8153,28 @@ var NodeFlowGraph = class {
|
|
|
7357
8153
|
Port,
|
|
7358
8154
|
PortType,
|
|
7359
8155
|
Publisher,
|
|
8156
|
+
RowWidget,
|
|
7360
8157
|
SliderWidget,
|
|
7361
8158
|
StringWidget,
|
|
7362
8159
|
TextWidget,
|
|
7363
8160
|
Theme,
|
|
7364
8161
|
ToggleWidget,
|
|
8162
|
+
ValueWidget,
|
|
8163
|
+
WidgetBase,
|
|
7365
8164
|
WidgetFactory,
|
|
8165
|
+
drawField,
|
|
8166
|
+
drawFieldBox,
|
|
8167
|
+
drawFieldText,
|
|
8168
|
+
editInPlace,
|
|
8169
|
+
formatNumber,
|
|
8170
|
+
onThemeChange,
|
|
7366
8171
|
portConfigTypes,
|
|
7367
8172
|
portsCompatible,
|
|
7368
8173
|
renderContinuously,
|
|
7369
8174
|
requestRender,
|
|
7370
|
-
|
|
8175
|
+
setTheme,
|
|
8176
|
+
themeVersion,
|
|
8177
|
+
typeSetsCompatible,
|
|
8178
|
+
widgetFont
|
|
7371
8179
|
});
|
|
7372
8180
|
//# sourceMappingURL=index.js.map
|