@elicdavis/node-flow 0.1.20 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +386 -227
- package/dist/index.d.ts +386 -227
- package/dist/index.js +1866 -1059
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1849 -1058
- package/dist/index.mjs.map +1 -1
- package/index.html +1134 -329
- package/package.json +1 -1
- package/src/connection.ts +30 -5
- package/src/contextMenu.ts +66 -34
- package/src/graph.ts +12 -10
- package/src/index.ts +26 -12
- package/src/markdown/entry.ts +11 -15
- package/src/markdown/syntaxParser.ts +2 -1
- package/src/node.ts +160 -101
- package/src/nodes/subsystem.ts +113 -29
- package/src/notes/note.ts +9 -1
- package/src/organize.ts +540 -111
- package/src/popup.ts +4 -2
- package/src/port.ts +83 -6
- package/src/styles/bevel.ts +31 -0
- package/src/styles/cursor.ts +1 -0
- package/src/styles/text.ts +4 -4
- package/src/theme.ts +179 -92
- package/src/types/text.ts +5 -1
- package/src/utils/color.ts +1 -1
- package/src/widgets/button.ts +52 -107
- package/src/widgets/color.ts +91 -150
- package/src/widgets/factory.ts +89 -46
- package/src/widgets/field.ts +99 -0
- package/src/widgets/image.ts +107 -165
- package/src/widgets/inplace.ts +84 -0
- package/src/widgets/number.ts +122 -154
- package/src/widgets/row.ts +138 -0
- package/src/widgets/slider.ts +101 -216
- package/src/widgets/string.ts +39 -130
- package/src/widgets/text.ts +81 -92
- package/src/widgets/toggle.ts +62 -207
- package/src/widgets/value.ts +127 -0
- package/src/widgets/widget.ts +180 -34
- package/test/context_menu.test.ts +82 -0
- package/test/cursor.test.ts +17 -0
- package/test/hidden_port.test.ts +125 -0
- package/test/markdown.test.ts +2 -2
- package/test/markdown_code_block.test.ts +58 -0
- package/test/multi_input.test.ts +124 -0
- package/test/organize.test.ts +117 -1
- package/test/support.ts +175 -0
- package/test/theme.test.ts +86 -0
- package/test/widget_click.test.ts +111 -97
- package/test/widget_construction.test.ts +5 -4
- package/test/widgets.test.ts +333 -0
- package/test/wire_follows_camera.test.ts +57 -0
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 };
|
|
2081
2360
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
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)) {
|
|
2084
2370
|
return;
|
|
2085
2371
|
}
|
|
2086
|
-
this.#value =
|
|
2087
|
-
|
|
2088
|
-
|
|
2372
|
+
this.#value = value;
|
|
2373
|
+
this.valueChanged();
|
|
2374
|
+
if (this.#property !== void 0) {
|
|
2375
|
+
this.#node.setProperty(this.#property, value);
|
|
2089
2376
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2377
|
+
requestRender();
|
|
2378
|
+
}
|
|
2379
|
+
label() {
|
|
2380
|
+
return this.#label;
|
|
2381
|
+
}
|
|
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)) {
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
2402
|
+
if (this.#property !== void 0) {
|
|
2403
|
+
this.#node.editProperty(this.#property, value);
|
|
2092
2404
|
}
|
|
2093
|
-
this.#
|
|
2405
|
+
this.#callback?.(value);
|
|
2094
2406
|
}
|
|
2095
|
-
|
|
2407
|
+
valueChanged() {
|
|
2096
2408
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
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;
|
|
2453
|
+
}
|
|
2454
|
+
this.#dragging = true;
|
|
2455
|
+
this.input(this.#clean(this.#startValue + dx * this.#stepFor(this.#startValue)));
|
|
2456
|
+
}
|
|
2457
|
+
onPointerUp() {
|
|
2458
|
+
this.#dragged = this.#dragging;
|
|
2459
|
+
if (this.#dragging) {
|
|
2460
|
+
this.#dragging = false;
|
|
2461
|
+
this.commit(this.value(), this.#startValue);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
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
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
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;
|
|
2513
|
+
}
|
|
2514
|
+
setText(text) {
|
|
2515
|
+
this.#text = text;
|
|
2516
|
+
requestRender();
|
|
2207
2517
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2518
|
+
render(ctx, box, scale) {
|
|
2519
|
+
drawField(ctx, box, scale, {
|
|
2520
|
+
hovered: this.hovered(),
|
|
2521
|
+
pressed: this.pressed(),
|
|
2522
|
+
value: this.#text
|
|
2523
|
+
});
|
|
2210
2524
|
}
|
|
2211
|
-
|
|
2212
|
-
this.#
|
|
2213
|
-
if (this.#callback !== void 0) {
|
|
2214
|
-
this.#callback();
|
|
2215
|
-
}
|
|
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;
|
|
2615
|
+
this.#fillColor = config?.fillColor;
|
|
2368
2616
|
}
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
if (Object.is(this.#value, cleanedValue)) {
|
|
2372
|
-
return;
|
|
2373
|
-
}
|
|
2374
|
-
this.#value = cleanedValue;
|
|
2375
|
-
if (this.#nodeProperty) {
|
|
2376
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
2377
|
-
}
|
|
2378
|
-
this.#text = this.#value.toFixed(3);
|
|
2379
|
-
if (this.#change !== void 0) {
|
|
2380
|
-
this.#change(this.#value);
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
Size() {
|
|
2384
|
-
return { "x": width, "y": height };
|
|
2385
|
-
}
|
|
2386
|
-
ClickStart() {
|
|
2387
|
-
this.#clicking = true;
|
|
2388
|
-
CopyVector2(this.#clickStartMousePosition, this.#lastMousePosition);
|
|
2617
|
+
Cursor() {
|
|
2618
|
+
return "ew-resize" /* EWResize */;
|
|
2389
2619
|
}
|
|
2390
|
-
|
|
2391
|
-
this.#
|
|
2392
|
-
if (this.#release !== void 0) {
|
|
2393
|
-
this.#release(this.#value);
|
|
2394
|
-
}
|
|
2620
|
+
setValue(value) {
|
|
2621
|
+
super.setValue(Clamp(value, this.#min, this.#max));
|
|
2395
2622
|
}
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
const
|
|
2399
|
-
const
|
|
2400
|
-
ctx.fillStyle = this.#
|
|
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
|
|
2673
|
+
Cursor() {
|
|
2674
|
+
return "text" /* Text */;
|
|
2524
2675
|
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
2532
|
-
}
|
|
2533
|
-
if (this.#callback !== void 0) {
|
|
2534
|
-
this.#callback(this.#value);
|
|
2535
|
-
}
|
|
2536
|
-
}
|
|
2537
|
-
ClickStart() {
|
|
2538
|
-
}
|
|
2539
|
-
ClickEnd() {
|
|
2540
|
-
const popup = SetStringPopup({
|
|
2541
|
-
title: "Set String",
|
|
2542
|
-
startingValue: this.#value,
|
|
2543
|
-
onUpdate: (value) => {
|
|
2544
|
-
this.Set(value);
|
|
2545
|
-
}
|
|
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;
|
|
@@ -3215,22 +3430,377 @@ function orderWithinLayers(layout, upstream, downstream) {
|
|
|
3215
3430
|
if (adjacent < 0 || adjacent >= layout.layers.length) {
|
|
3216
3431
|
continue;
|
|
3217
3432
|
}
|
|
3218
|
-
const layer = layout.layers[index];
|
|
3219
|
-
const keys = /* @__PURE__ */ new Map();
|
|
3220
|
-
for (const node of layer) {
|
|
3221
|
-
const mean = meanSlot(node, neighbours, adjacent);
|
|
3222
|
-
keys.set(node, mean === -1 ? slotOf.get(node) : mean);
|
|
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]);
|
|
3223
3662
|
}
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
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]);
|
|
3227
3694
|
}
|
|
3228
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
|
+
}
|
|
3229
3700
|
};
|
|
3230
|
-
for (let
|
|
3231
|
-
|
|
3232
|
-
|
|
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);
|
|
3705
|
+
}
|
|
3706
|
+
} else {
|
|
3707
|
+
for (let i = layout.layers.length - 2; i >= 0; i--) {
|
|
3708
|
+
place(layout.layers[i], down, i + 1);
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
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
|
}
|
|
@@ -4199,7 +4779,8 @@ var NodeSubsystem2 = class {
|
|
|
4199
4779
|
}
|
|
4200
4780
|
if (intersection.Widget !== void 0) {
|
|
4201
4781
|
this.#widgetHovering = intersection.Widget;
|
|
4202
|
-
this.#
|
|
4782
|
+
this.#widgetHoveringBox = intersection.WidgetBox ?? null;
|
|
4783
|
+
this.#cursor = intersection.Widget.Disabled?.() ? "default" /* Default */ : intersection.Widget.Cursor?.() ?? "pointer" /* Pointer */;
|
|
4203
4784
|
}
|
|
4204
4785
|
if (intersection.Port !== void 0 && intersection.Node !== void 0 && intersection.PortIndex !== void 0 && intersection.PortIsInput !== void 0) {
|
|
4205
4786
|
this.#portHovering = {
|
|
@@ -4222,6 +4803,9 @@ var NodeSubsystem2 = class {
|
|
|
4222
4803
|
}
|
|
4223
4804
|
this.#nodes[i].render(ctx, camera, state, mousePosition, this.#postProcessPass);
|
|
4224
4805
|
}
|
|
4806
|
+
if (this.#widgetCurrentlyClicking !== null) {
|
|
4807
|
+
this.#cursor = this.#widgetCurrentlyClicking.Cursor?.() ?? "pointer" /* Pointer */;
|
|
4808
|
+
}
|
|
4225
4809
|
}
|
|
4226
4810
|
#boxSelectionScreenspaceBox(camera) {
|
|
4227
4811
|
const box = { Position: Zero(), Size: Zero() };
|
|
@@ -4230,29 +4814,174 @@ var NodeSubsystem2 = class {
|
|
|
4230
4814
|
SubVector2(box.Size, box.Size, box.Position);
|
|
4231
4815
|
return box;
|
|
4232
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
|
+
}
|
|
4233
4851
|
render(ctx, camera, mousePosition) {
|
|
4234
4852
|
this.#lastCtx = ctx;
|
|
4853
|
+
this.#lastMousePosition = mousePosition;
|
|
4235
4854
|
this.#cursor = "default" /* Default */;
|
|
4236
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
|
+
}
|
|
4237
4861
|
TimeExecution("Render_Connections", () => {
|
|
4238
4862
|
this.#renderConnections(ctx, camera, mousePosition);
|
|
4239
4863
|
});
|
|
4240
4864
|
TimeExecution("Render_Nodes", () => {
|
|
4241
4865
|
this.#renderNodes(ctx, camera, mousePosition);
|
|
4242
4866
|
});
|
|
4867
|
+
this.#renderInsertMarker(ctx, camera);
|
|
4243
4868
|
if (awaitingLayout) {
|
|
4244
4869
|
requestRender();
|
|
4245
4870
|
}
|
|
4246
4871
|
if (this.#boxSelect) {
|
|
4247
4872
|
const box = this.#boxSelectionScreenspaceBox(camera);
|
|
4248
4873
|
ctx.setLineDash([Theme.BoxSelect.LineDashLength]);
|
|
4249
|
-
|
|
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);
|
|
4250
4879
|
ctx.setLineDash([]);
|
|
4251
4880
|
}
|
|
4252
4881
|
return { cursorStyle: this.#cursor };
|
|
4253
4882
|
}
|
|
4254
4883
|
};
|
|
4255
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
|
+
|
|
4256
4985
|
// src/popups/form.ts
|
|
4257
4986
|
function FormPopup(config) {
|
|
4258
4987
|
let input = new Array();
|
|
@@ -4369,9 +5098,11 @@ var FlowNode = class {
|
|
|
4369
5098
|
#titleChangeCallback;
|
|
4370
5099
|
#infoChangeCallback;
|
|
4371
5100
|
// Styling ================================================================
|
|
5101
|
+
#styleConfig;
|
|
5102
|
+
#styledForTheme = -1;
|
|
4372
5103
|
#titleColor;
|
|
4373
5104
|
#selectedStyle;
|
|
4374
|
-
#stateStyles;
|
|
5105
|
+
#stateStyles = /* @__PURE__ */ new Map();
|
|
4375
5106
|
#padding;
|
|
4376
5107
|
#portTextStyle;
|
|
4377
5108
|
#elementSpacing;
|
|
@@ -4383,6 +5114,7 @@ var FlowNode = class {
|
|
|
4383
5114
|
#data;
|
|
4384
5115
|
#registeredAnyPropertyChangeCallbacks;
|
|
4385
5116
|
#registeredPropertyChangeCallbacks;
|
|
5117
|
+
#registeredPropertyEditCallbacks = /* @__PURE__ */ new Map();
|
|
4386
5118
|
constructor(config) {
|
|
4387
5119
|
this.#input = new Array();
|
|
4388
5120
|
this.#output = new Array();
|
|
@@ -4435,58 +5167,17 @@ var FlowNode = class {
|
|
|
4435
5167
|
this.#position = config?.position === void 0 ? { x: 0, y: 0 } : config.position;
|
|
4436
5168
|
this.#title = new Text(
|
|
4437
5169
|
config?.title === void 0 ? "" : config.title,
|
|
4438
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
4439
|
-
size: 16,
|
|
4440
|
-
weight: "bold" /* Bold */,
|
|
4441
|
-
color: Theme.Node.FontColor
|
|
4442
|
-
})
|
|
5170
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 16, weight: "bold" /* Bold */ })
|
|
4443
5171
|
);
|
|
4444
5172
|
this.#subTitle = new Text(
|
|
4445
5173
|
config?.subTitle === void 0 ? "" : config.subTitle,
|
|
4446
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
4447
|
-
size: 10,
|
|
4448
|
-
weight: "bold" /* Bold */,
|
|
4449
|
-
color: Theme.Node.FontColor
|
|
4450
|
-
})
|
|
4451
|
-
);
|
|
4452
|
-
this.#infoSymbol = new Text(
|
|
4453
|
-
"i",
|
|
4454
|
-
{
|
|
4455
|
-
size: 13,
|
|
4456
|
-
weight: "bold" /* Bold */,
|
|
4457
|
-
color: Theme.Node.FontColor
|
|
4458
|
-
}
|
|
5174
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 10, weight: "bold" /* Bold */ })
|
|
4459
5175
|
);
|
|
5176
|
+
this.#infoSymbol = new Text("i", { size: 13, weight: "bold" /* Bold */ });
|
|
4460
5177
|
this.#infoText = config?.info ? config?.info : "";
|
|
4461
|
-
this.#titleColor = config?.style?.title?.color === void 0 ? "#154050" : config?.style?.title?.color;
|
|
4462
5178
|
this.#padding = config?.style?.title?.padding === void 0 ? 15 : config?.style?.title?.padding;
|
|
4463
|
-
this.#
|
|
4464
|
-
this.#
|
|
4465
|
-
border: { color: Theme.Node.Border.Idle, size: 1 },
|
|
4466
|
-
radius: Theme.Node.BorderRadius,
|
|
4467
|
-
color: Theme.Node.BackgroundColor
|
|
4468
|
-
})));
|
|
4469
|
-
this.#stateStyles.set(1 /* MouseOver */, new BoxStyle(BoxStyleWithFallback(config?.style?.mouseOver, {
|
|
4470
|
-
border: { color: Theme.Node.Border.MouseOver, size: 1.1 },
|
|
4471
|
-
radius: Theme.Node.BorderRadius,
|
|
4472
|
-
color: Theme.Node.BackgroundColor
|
|
4473
|
-
})));
|
|
4474
|
-
this.#stateStyles.set(2 /* Grabbed */, new BoxStyle(BoxStyleWithFallback(config?.style?.grabbed, {
|
|
4475
|
-
border: { color: Theme.Node.Border.Grabbed, size: 2 },
|
|
4476
|
-
radius: Theme.Node.BorderRadius,
|
|
4477
|
-
color: Theme.Node.BackgroundColor
|
|
4478
|
-
})));
|
|
4479
|
-
this.#selectedStyle = new BoxStyle(BoxStyleWithFallback(config?.style?.selected, {
|
|
4480
|
-
border: { color: Theme.Node.Border.Selected, size: 1 },
|
|
4481
|
-
radius: Theme.Node.BorderRadius,
|
|
4482
|
-
color: Theme.Node.BackgroundColor
|
|
4483
|
-
}));
|
|
4484
|
-
this.#portTextStyle = new TextStyle({
|
|
4485
|
-
size: config?.style?.portText?.size === void 0 ? 14 : config?.style?.portText?.size,
|
|
4486
|
-
color: config?.style?.portText?.color === void 0 ? Theme.Node.Port.FontColor : config?.style?.portText?.color,
|
|
4487
|
-
font: config?.style?.portText?.font,
|
|
4488
|
-
weight: config?.style?.portText?.weight
|
|
4489
|
-
});
|
|
5179
|
+
this.#styleConfig = config?.style;
|
|
5180
|
+
this.#applyStyle();
|
|
4490
5181
|
if (config?.inputs !== void 0) {
|
|
4491
5182
|
for (let i = 0; i < config.inputs.length; i++) {
|
|
4492
5183
|
this.addInput(config.inputs[i]);
|
|
@@ -4498,12 +5189,8 @@ var FlowNode = class {
|
|
|
4498
5189
|
}
|
|
4499
5190
|
}
|
|
4500
5191
|
if (config?.widgets !== void 0) {
|
|
4501
|
-
for (
|
|
4502
|
-
|
|
4503
|
-
if (widget.type === void 0) {
|
|
4504
|
-
continue;
|
|
4505
|
-
}
|
|
4506
|
-
this.addWidget(GlobalWidgetFactory.create(this, widget.type, widget.config));
|
|
5192
|
+
for (const widget of config.widgets) {
|
|
5193
|
+
this.addWidget(GlobalWidgetFactory.createFromConfig(this, widget));
|
|
4507
5194
|
}
|
|
4508
5195
|
}
|
|
4509
5196
|
}
|
|
@@ -4576,6 +5263,32 @@ var FlowNode = class {
|
|
|
4576
5263
|
callbacks[i](oldValue, value);
|
|
4577
5264
|
}
|
|
4578
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
|
+
}
|
|
4579
5292
|
getProperty(name) {
|
|
4580
5293
|
return this.#data[name];
|
|
4581
5294
|
}
|
|
@@ -4864,6 +5577,7 @@ var FlowNode = class {
|
|
|
4864
5577
|
// return this.#titleTextStyle.measure(ctx, scale, this.#title);
|
|
4865
5578
|
// }
|
|
4866
5579
|
calculateBounds(ctx, camera) {
|
|
5580
|
+
this.#followTheme();
|
|
4867
5581
|
const tempMeasurement = Zero();
|
|
4868
5582
|
const doublePadding = this.#padding * 2;
|
|
4869
5583
|
const screenSpacePosition = Zero();
|
|
@@ -4877,25 +5591,21 @@ var FlowNode = class {
|
|
|
4877
5591
|
size.x += size.y * 4;
|
|
4878
5592
|
}
|
|
4879
5593
|
size.x += doublePadding;
|
|
4880
|
-
size.y += doublePadding
|
|
4881
|
-
for (
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
|
|
4886
|
-
}
|
|
4887
|
-
size.y += this.#elementSpacing * this.#output.length;
|
|
4888
|
-
for (let i = 0; i < this.#output.length; i++) {
|
|
4889
|
-
const port = this.#output[i];
|
|
5594
|
+
size.y += doublePadding;
|
|
5595
|
+
for (const port of [...this.#input, ...this.#output]) {
|
|
5596
|
+
if (port.hidden()) {
|
|
5597
|
+
continue;
|
|
5598
|
+
}
|
|
4890
5599
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
4891
|
-
size.y += tempMeasurement.y;
|
|
5600
|
+
size.y += this.#portRowHeight(port, tempMeasurement.y) + this.#elementSpacing;
|
|
4892
5601
|
size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
|
|
4893
5602
|
}
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
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;
|
|
4899
5609
|
size.x = Math.max(size.x, eleSize.x + doublePadding);
|
|
4900
5610
|
}
|
|
4901
5611
|
size.y += this.#elementSpacing;
|
|
@@ -4979,7 +5689,7 @@ var FlowNode = class {
|
|
|
4979
5689
|
intersection.Node = this;
|
|
4980
5690
|
}
|
|
4981
5691
|
for (let i = 0; i < this.#inputPortPositions.Count(); i++) {
|
|
4982
|
-
if (InBox(this.#inputPortPositions.At(i), position)) {
|
|
5692
|
+
if (!this.#input[i].hidden() && InBox(this.#inputPortPositions.At(i), position)) {
|
|
4983
5693
|
intersection.Node = this;
|
|
4984
5694
|
intersection.PortIndex = i;
|
|
4985
5695
|
intersection.PortIsInput = true;
|
|
@@ -4987,7 +5697,7 @@ var FlowNode = class {
|
|
|
4987
5697
|
}
|
|
4988
5698
|
}
|
|
4989
5699
|
for (let i = 0; i < this.#outputPortPositions.Count(); i++) {
|
|
4990
|
-
if (InBox(this.#outputPortPositions.At(i), position)) {
|
|
5700
|
+
if (!this.#output[i].hidden() && InBox(this.#outputPortPositions.At(i), position)) {
|
|
4991
5701
|
intersection.Node = this;
|
|
4992
5702
|
intersection.PortIndex = i;
|
|
4993
5703
|
intersection.PortIsInput = false;
|
|
@@ -4995,10 +5705,12 @@ var FlowNode = class {
|
|
|
4995
5705
|
}
|
|
4996
5706
|
}
|
|
4997
5707
|
for (let i = 0; i < this.#widgetPositions.Count(); i++) {
|
|
4998
|
-
|
|
5708
|
+
const widget = this.#widgets[i];
|
|
5709
|
+
if (widget !== void 0 && !widget.Hidden?.() && InBox(this.#widgetPositions.At(i), position)) {
|
|
4999
5710
|
intersection.Node = this;
|
|
5000
5711
|
intersection.WidgetIndex = i;
|
|
5001
|
-
intersection.Widget =
|
|
5712
|
+
intersection.Widget = widget;
|
|
5713
|
+
intersection.WidgetBox = this.#widgetPositions.At(i);
|
|
5002
5714
|
}
|
|
5003
5715
|
}
|
|
5004
5716
|
return intersection;
|
|
@@ -5047,6 +5759,12 @@ var FlowNode = class {
|
|
|
5047
5759
|
inputPortPosition(index) {
|
|
5048
5760
|
return this.#inputPortPositions.At(index);
|
|
5049
5761
|
}
|
|
5762
|
+
widgetPosition(index) {
|
|
5763
|
+
if (index < 0 || index >= this.#widgetPositions.Count()) {
|
|
5764
|
+
return void 0;
|
|
5765
|
+
}
|
|
5766
|
+
return this.#widgetPositions.At(index);
|
|
5767
|
+
}
|
|
5050
5768
|
inputPort(index) {
|
|
5051
5769
|
return this.#input[index];
|
|
5052
5770
|
}
|
|
@@ -5062,6 +5780,35 @@ var FlowNode = class {
|
|
|
5062
5780
|
outputs() {
|
|
5063
5781
|
return this.#output.length;
|
|
5064
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
|
+
}
|
|
5065
5812
|
#calculateStyle(state) {
|
|
5066
5813
|
if (this.#selected && state === 0 /* Idle */) {
|
|
5067
5814
|
return this.#selectedStyle;
|
|
@@ -5078,9 +5825,22 @@ var FlowNode = class {
|
|
|
5078
5825
|
clearMessages() {
|
|
5079
5826
|
this.#messages = [];
|
|
5080
5827
|
}
|
|
5828
|
+
#portRowHeight(port, textHeight) {
|
|
5829
|
+
const slots = port.slots();
|
|
5830
|
+
return slots > 1 ? Math.max(textHeight, slots * ARRAY_SLOT_PITCH) : textHeight;
|
|
5831
|
+
}
|
|
5081
5832
|
hasBeenLaidOut() {
|
|
5082
5833
|
return this.#laidOut;
|
|
5083
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
|
+
}
|
|
5084
5844
|
#laidOut = false;
|
|
5085
5845
|
render(ctx, camera, state, mousePosition, postProcess) {
|
|
5086
5846
|
this.#laidOut = true;
|
|
@@ -5118,6 +5878,9 @@ var FlowNode = class {
|
|
|
5118
5878
|
[nodeStyle.radius() * camera.zoom, nodeStyle.radius() * camera.zoom, 0, 0]
|
|
5119
5879
|
);
|
|
5120
5880
|
ctx.fill();
|
|
5881
|
+
if (Theme.Node.Bevel !== null) {
|
|
5882
|
+
drawBevel(ctx, nodeBounds, camera.zoom, Theme.Node.Bevel, false);
|
|
5883
|
+
}
|
|
5121
5884
|
const titlePosition = VectorPool.get();
|
|
5122
5885
|
titlePosition.x = nodeBounds.Position.x + nodeBounds.Size.x / 2;
|
|
5123
5886
|
titlePosition.y = nodeBounds.Position.y + scaledPadding + titleSize.y / 2;
|
|
@@ -5178,8 +5941,12 @@ var FlowNode = class {
|
|
|
5178
5941
|
for (let i = 0; i < this.#input.length; i++) {
|
|
5179
5942
|
ctx.textAlign = "left" /* Left */;
|
|
5180
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
|
+
}
|
|
5181
5948
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
5182
|
-
const portHeight = tempMeasurement.y * camera.zoom;
|
|
5949
|
+
const portHeight = this.#portRowHeight(port, tempMeasurement.y) * camera.zoom;
|
|
5183
5950
|
const position = VectorPool.get();
|
|
5184
5951
|
position.x = nodeBounds.Position.x;
|
|
5185
5952
|
position.y = startY + portHeight / 2;
|
|
@@ -5192,6 +5959,10 @@ var FlowNode = class {
|
|
|
5192
5959
|
ctx.textAlign = "right" /* Right */;
|
|
5193
5960
|
for (let i = 0; i < this.#output.length; i++) {
|
|
5194
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
|
+
}
|
|
5195
5966
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
5196
5967
|
const portHeight = tempMeasurement.y * camera.zoom;
|
|
5197
5968
|
const position = VectorPool.get();
|
|
@@ -5204,13 +5975,18 @@ var FlowNode = class {
|
|
|
5204
5975
|
}
|
|
5205
5976
|
for (let i = 0; i < this.#widgets.length; i++) {
|
|
5206
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
|
+
}
|
|
5207
5982
|
const widgetSize = widget.Size();
|
|
5208
|
-
const
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
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;
|
|
5214
5990
|
}
|
|
5215
5991
|
const messageStart = VectorPool.get();
|
|
5216
5992
|
messageStart.x = nodeBounds.Position.x + nodeBounds.Size.x / 2;
|
|
@@ -5247,6 +6023,13 @@ var FlowNode = class {
|
|
|
5247
6023
|
record(this.#outputPortPositions, this.#portOffsets.output);
|
|
5248
6024
|
}
|
|
5249
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) {
|
|
5250
6033
|
if (this.#portOffsets.input.length === 0 && this.#portOffsets.output.length === 0) {
|
|
5251
6034
|
return false;
|
|
5252
6035
|
}
|
|
@@ -5268,7 +6051,6 @@ var FlowNode = class {
|
|
|
5268
6051
|
};
|
|
5269
6052
|
project(this.#portOffsets.input, this.#inputPortPositions);
|
|
5270
6053
|
project(this.#portOffsets.output, this.#outputPortPositions);
|
|
5271
|
-
this.#widgetPositions.Clear();
|
|
5272
6054
|
return true;
|
|
5273
6055
|
}
|
|
5274
6056
|
};
|
|
@@ -5460,33 +6242,33 @@ var CodeBlockEntry = class {
|
|
|
5460
6242
|
}
|
|
5461
6243
|
render(ctx, position, scale, maxWidth) {
|
|
5462
6244
|
this.#calculateLayout(ctx, maxWidth);
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
}
|
|
5469
|
-
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;
|
|
5470
6250
|
ctx.fillStyle = Theme.Note.CodeBlock.BackgroundColor;
|
|
5471
6251
|
ctx.beginPath();
|
|
5472
6252
|
ctx.roundRect(
|
|
5473
6253
|
position.x,
|
|
5474
6254
|
position.y,
|
|
5475
6255
|
maxWidth * scale,
|
|
5476
|
-
|
|
6256
|
+
height2,
|
|
5477
6257
|
Theme.Note.CodeBlock.BorderRadius * scale
|
|
5478
6258
|
);
|
|
5479
6259
|
ctx.fill();
|
|
5480
|
-
|
|
6260
|
+
const baseline = ctx.textBaseline;
|
|
6261
|
+
ctx.textBaseline = "top";
|
|
6262
|
+
for (let i = 0; i < lineCount; i++) {
|
|
5481
6263
|
const entry = this.#calculatedEntries.At(i);
|
|
5482
6264
|
const pos = this.#calculatedPositions.At(i);
|
|
5483
6265
|
entry.render(ctx, scale, {
|
|
5484
6266
|
x: pos.x * scale + position.x + padding,
|
|
5485
6267
|
y: pos.y * scale + position.y + padding
|
|
5486
6268
|
});
|
|
5487
|
-
max = Math.max(max, pos.y * scale);
|
|
5488
6269
|
}
|
|
5489
|
-
|
|
6270
|
+
ctx.textBaseline = baseline;
|
|
6271
|
+
return height2;
|
|
5490
6272
|
}
|
|
5491
6273
|
};
|
|
5492
6274
|
var UnorderedListMarkdownEntry = class {
|
|
@@ -5864,9 +6646,10 @@ var MarkdownSyntaxParser = class {
|
|
|
5864
6646
|
this.#inc();
|
|
5865
6647
|
this.#inc();
|
|
5866
6648
|
this.#inc();
|
|
5867
|
-
const codeBlockText = this.#originalText.substring(start, end);
|
|
6649
|
+
const codeBlockText = this.#originalText.substring(start, end).replace(/\r?\n$/, "");
|
|
5868
6650
|
const entryText = new Text(codeBlockText);
|
|
5869
6651
|
this.#assignStandardStyling([entryText]);
|
|
6652
|
+
entryText.setColor(Theme.Note.CodeBlock.FontColor);
|
|
5870
6653
|
return new CodeBlockEntry(entryText);
|
|
5871
6654
|
}
|
|
5872
6655
|
parse() {
|
|
@@ -5921,6 +6704,7 @@ var BOX_SIZE = 10;
|
|
|
5921
6704
|
var FlowNote = class {
|
|
5922
6705
|
#originalText;
|
|
5923
6706
|
#document;
|
|
6707
|
+
#parsedForTheme = -1;
|
|
5924
6708
|
#width;
|
|
5925
6709
|
#edittingStyle;
|
|
5926
6710
|
// Runtime ================================================================
|
|
@@ -5963,6 +6747,7 @@ var FlowNote = class {
|
|
|
5963
6747
|
setText(text) {
|
|
5964
6748
|
this.#originalText = text;
|
|
5965
6749
|
this.#document = BuildMarkdown(this.#originalText);
|
|
6750
|
+
this.#parsedForTheme = themeVersion();
|
|
5966
6751
|
for (let i = 0; i < this.#contentChangeCallbacks.length; i++) {
|
|
5967
6752
|
this.#contentChangeCallbacks[i](this, text);
|
|
5968
6753
|
}
|
|
@@ -6050,6 +6835,10 @@ var FlowNote = class {
|
|
|
6050
6835
|
}
|
|
6051
6836
|
camera.graphSpaceToScreenSpace(this.#position, this.#tempPosition);
|
|
6052
6837
|
CopyVector2(this.#lastRenderedBox.Position, this.#tempPosition);
|
|
6838
|
+
if (this.#parsedForTheme !== themeVersion()) {
|
|
6839
|
+
this.#document = BuildMarkdown(this.#originalText);
|
|
6840
|
+
this.#parsedForTheme = themeVersion();
|
|
6841
|
+
}
|
|
6053
6842
|
const startY = this.#tempPosition.y;
|
|
6054
6843
|
const lineSpacing = Theme.Note.EntrySpacing * camera.zoom;
|
|
6055
6844
|
ctx.textAlign = "left" /* Left */;
|
|
@@ -6745,13 +7534,15 @@ var Minimap = class {
|
|
|
6745
7534
|
// src/graph.ts
|
|
6746
7535
|
function BuildBackgroundRenderer(backgroundColor) {
|
|
6747
7536
|
return (canvas, context, position, scale) => {
|
|
6748
|
-
context.fillStyle = backgroundColor;
|
|
7537
|
+
context.fillStyle = backgroundColor ?? Theme.Graph.BackgroundColor;
|
|
6749
7538
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
6750
|
-
const alpha =
|
|
7539
|
+
const alpha = Clamp01(scale - 0.3);
|
|
6751
7540
|
if (alpha <= 0) {
|
|
6752
7541
|
return;
|
|
6753
7542
|
}
|
|
6754
|
-
context.
|
|
7543
|
+
context.save();
|
|
7544
|
+
context.globalAlpha = alpha;
|
|
7545
|
+
context.fillStyle = Theme.Graph.DotColor;
|
|
6755
7546
|
const spacing = 100;
|
|
6756
7547
|
const pi2 = 2 * Math.PI;
|
|
6757
7548
|
const dotScale = 2 * scale;
|
|
@@ -6764,6 +7555,7 @@ function BuildBackgroundRenderer(backgroundColor) {
|
|
|
6764
7555
|
context.fill();
|
|
6765
7556
|
}
|
|
6766
7557
|
}
|
|
7558
|
+
context.restore();
|
|
6767
7559
|
};
|
|
6768
7560
|
}
|
|
6769
7561
|
var contextMenuGroup = "graph-context-menu";
|
|
@@ -6814,9 +7606,9 @@ var GraphView = class {
|
|
|
6814
7606
|
const results = {};
|
|
6815
7607
|
for (let i = 0; i < this.#subsystems.length; i++) {
|
|
6816
7608
|
TimeExecution("Render_Subsystem_" + i, () => {
|
|
6817
|
-
|
|
6818
|
-
if (
|
|
6819
|
-
|
|
7609
|
+
const layer = this.#subsystems[i].render(ctx, camera, mousePosition);
|
|
7610
|
+
if (layer?.cursorStyle) {
|
|
7611
|
+
results.cursorStyle = layer.cursorStyle;
|
|
6820
7612
|
}
|
|
6821
7613
|
});
|
|
6822
7614
|
}
|
|
@@ -6876,8 +7668,7 @@ var NodeFlowGraph = class {
|
|
|
6876
7668
|
if (config?.backgroundRenderer !== void 0) {
|
|
6877
7669
|
this.#backgroundRenderer = config?.backgroundRenderer;
|
|
6878
7670
|
} else {
|
|
6879
|
-
|
|
6880
|
-
this.#backgroundRenderer = BuildBackgroundRenderer(backgroundColor);
|
|
7671
|
+
this.#backgroundRenderer = BuildBackgroundRenderer(config?.backgroundColor);
|
|
6881
7672
|
}
|
|
6882
7673
|
this.#minimap = new Minimap(config?.minimap);
|
|
6883
7674
|
this.#mainNodeSubsystem.setConnectionReleasedHandler((dangling) => {
|
|
@@ -7344,11 +8135,15 @@ var NodeFlowGraph = class {
|
|
|
7344
8135
|
ColorWidget,
|
|
7345
8136
|
Connection,
|
|
7346
8137
|
ContextMenuItemState,
|
|
8138
|
+
CursorStyle,
|
|
7347
8139
|
DefaultConnectionRenderer,
|
|
8140
|
+
DefaultTheme,
|
|
7348
8141
|
FlowNode,
|
|
7349
8142
|
FlowNote,
|
|
7350
8143
|
GlobalWidgetFactory,
|
|
8144
|
+
GraphView,
|
|
7351
8145
|
ImageWidget,
|
|
8146
|
+
InBox,
|
|
7352
8147
|
MessageType,
|
|
7353
8148
|
Minimap,
|
|
7354
8149
|
NodeFactory,
|
|
@@ -7358,16 +8153,28 @@ var NodeFlowGraph = class {
|
|
|
7358
8153
|
Port,
|
|
7359
8154
|
PortType,
|
|
7360
8155
|
Publisher,
|
|
8156
|
+
RowWidget,
|
|
7361
8157
|
SliderWidget,
|
|
7362
8158
|
StringWidget,
|
|
7363
8159
|
TextWidget,
|
|
7364
8160
|
Theme,
|
|
7365
8161
|
ToggleWidget,
|
|
8162
|
+
ValueWidget,
|
|
8163
|
+
WidgetBase,
|
|
7366
8164
|
WidgetFactory,
|
|
8165
|
+
drawField,
|
|
8166
|
+
drawFieldBox,
|
|
8167
|
+
drawFieldText,
|
|
8168
|
+
editInPlace,
|
|
8169
|
+
formatNumber,
|
|
8170
|
+
onThemeChange,
|
|
7367
8171
|
portConfigTypes,
|
|
7368
8172
|
portsCompatible,
|
|
7369
8173
|
renderContinuously,
|
|
7370
8174
|
requestRender,
|
|
7371
|
-
|
|
8175
|
+
setTheme,
|
|
8176
|
+
themeVersion,
|
|
8177
|
+
typeSetsCompatible,
|
|
8178
|
+
widgetFont
|
|
7372
8179
|
});
|
|
7373
8180
|
//# sourceMappingURL=index.js.map
|