@elicdavis/node-flow 0.1.19 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +386 -227
- package/dist/index.d.ts +386 -227
- package/dist/index.js +1884 -1076
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1867 -1075
- package/dist/index.mjs.map +1 -1
- package/index.html +1134 -329
- package/package.json +1 -1
- package/src/connection.ts +30 -5
- package/src/contextMenu.ts +66 -34
- package/src/graph.ts +12 -10
- package/src/index.ts +26 -12
- package/src/markdown/entry.ts +11 -15
- package/src/markdown/syntaxParser.ts +2 -1
- package/src/node.ts +162 -111
- package/src/nodes/subsystem.ts +123 -29
- package/src/notes/note.ts +9 -1
- package/src/organize.ts +540 -111
- package/src/popup.ts +4 -2
- package/src/port.ts +83 -6
- package/src/styles/bevel.ts +31 -0
- package/src/styles/cursor.ts +1 -0
- package/src/styles/text.ts +4 -4
- package/src/theme.ts +179 -92
- package/src/types/text.ts +5 -1
- package/src/utils/color.ts +1 -1
- package/src/widgets/button.ts +52 -107
- package/src/widgets/color.ts +91 -150
- package/src/widgets/factory.ts +89 -46
- package/src/widgets/field.ts +99 -0
- package/src/widgets/image.ts +107 -165
- package/src/widgets/inplace.ts +84 -0
- package/src/widgets/number.ts +122 -154
- package/src/widgets/row.ts +138 -0
- package/src/widgets/slider.ts +101 -216
- package/src/widgets/string.ts +39 -130
- package/src/widgets/text.ts +81 -92
- package/src/widgets/toggle.ts +62 -207
- package/src/widgets/value.ts +127 -0
- package/src/widgets/widget.ts +180 -34
- package/test/context_menu.test.ts +82 -0
- package/test/cursor.test.ts +17 -0
- package/test/hidden_port.test.ts +125 -0
- package/test/hover_after_removal.test.ts +72 -0
- package/test/markdown.test.ts +2 -2
- package/test/markdown_code_block.test.ts +58 -0
- package/test/multi_input.test.ts +124 -0
- package/test/organize.test.ts +117 -1
- package/test/property_binding.test.ts +49 -0
- package/test/support.ts +175 -0
- package/test/theme.test.ts +86 -0
- package/test/widget_click.test.ts +111 -97
- package/test/widget_construction.test.ts +5 -4
- package/test/widgets.test.ts +333 -0
- package/test/wire_follows_camera.test.ts +57 -0
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,46 @@
|
|
|
1
|
+
// src/render_scheduler.ts
|
|
2
|
+
var subscribers = /* @__PURE__ */ new Set();
|
|
3
|
+
var frameQueued = false;
|
|
4
|
+
var continuous = 0;
|
|
5
|
+
var canSchedule = () => typeof requestAnimationFrame === "function";
|
|
6
|
+
function onRenderRequested(redraw) {
|
|
7
|
+
subscribers.add(redraw);
|
|
8
|
+
return () => {
|
|
9
|
+
subscribers.delete(redraw);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function requestRender() {
|
|
13
|
+
if (frameQueued || !canSchedule()) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
frameQueued = true;
|
|
17
|
+
requestAnimationFrame(() => {
|
|
18
|
+
frameQueued = false;
|
|
19
|
+
subscribers.forEach((redraw) => redraw());
|
|
20
|
+
if (continuous > 0) {
|
|
21
|
+
requestRender();
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function renderContinuously() {
|
|
26
|
+
continuous++;
|
|
27
|
+
requestRender();
|
|
28
|
+
let released = false;
|
|
29
|
+
return () => {
|
|
30
|
+
if (released) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
released = true;
|
|
34
|
+
continuous--;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
1
38
|
// src/theme.ts
|
|
2
|
-
var
|
|
39
|
+
var defaults = {
|
|
3
40
|
FontFamily: "Courier New",
|
|
4
41
|
Graph: {
|
|
5
|
-
BackgroundColor: "#07212a"
|
|
42
|
+
BackgroundColor: "#07212a",
|
|
43
|
+
DotColor: "rgb(41, 54, 57)"
|
|
6
44
|
},
|
|
7
45
|
Node: {
|
|
8
46
|
FontColor: "#afb9bb",
|
|
@@ -19,6 +57,7 @@ var Theme = {
|
|
|
19
57
|
Grabbed: "white",
|
|
20
58
|
Selected: "#6e6e6e"
|
|
21
59
|
},
|
|
60
|
+
Bevel: null,
|
|
22
61
|
Port: {
|
|
23
62
|
FontColor: "#afb9bb"
|
|
24
63
|
},
|
|
@@ -36,15 +75,25 @@ var Theme = {
|
|
|
36
75
|
},
|
|
37
76
|
Widget: {
|
|
38
77
|
FontColor: "#afb9bb",
|
|
78
|
+
LabelColor: "#7f9497",
|
|
79
|
+
FontSize: 14,
|
|
80
|
+
Padding: 6,
|
|
81
|
+
Gap: 4,
|
|
39
82
|
BackgroundColor: "#0f313c",
|
|
40
83
|
Border: {
|
|
41
84
|
Color: "#154050",
|
|
42
85
|
Size: 2,
|
|
43
86
|
Radius: 2
|
|
44
87
|
},
|
|
88
|
+
Bevel: null,
|
|
45
89
|
Hover: {
|
|
46
90
|
BackgroundColor: "#195366"
|
|
47
91
|
},
|
|
92
|
+
Input: {
|
|
93
|
+
BackgroundColor: "#0f313c",
|
|
94
|
+
HoverBackgroundColor: "#195366",
|
|
95
|
+
FontColor: "#afb9bb"
|
|
96
|
+
},
|
|
48
97
|
Slider: {
|
|
49
98
|
FillColor: "#07212A"
|
|
50
99
|
},
|
|
@@ -57,8 +106,10 @@ var Theme = {
|
|
|
57
106
|
ContextMenu: {
|
|
58
107
|
BackgroundColor: "#07212A",
|
|
59
108
|
HighlightColor: "#205A6D",
|
|
109
|
+
HighlightFontColor: "#afb9bb",
|
|
60
110
|
FontColor: "#afb9bb",
|
|
61
|
-
DisabledFontColor: "#4a585c"
|
|
111
|
+
DisabledFontColor: "#4a585c",
|
|
112
|
+
BorderRadius: 5
|
|
62
113
|
},
|
|
63
114
|
Minimap: {
|
|
64
115
|
BackgroundColor: "rgba(10, 16, 18, 0.85)",
|
|
@@ -68,6 +119,13 @@ var Theme = {
|
|
|
68
119
|
NodeColor: "#6b7f85",
|
|
69
120
|
ViewportColor: "rgba(175, 185, 187, 0.25)"
|
|
70
121
|
},
|
|
122
|
+
Popup: {
|
|
123
|
+
BackgroundColor: "#000000",
|
|
124
|
+
FontColor: "#ffffff",
|
|
125
|
+
FontFamily: "Verdana",
|
|
126
|
+
BorderRadius: 8,
|
|
127
|
+
Backdrop: "#00000070"
|
|
128
|
+
},
|
|
71
129
|
Note: {
|
|
72
130
|
FontColor: "#afb9bb",
|
|
73
131
|
FontSize: 16,
|
|
@@ -86,11 +144,47 @@ var Theme = {
|
|
|
86
144
|
},
|
|
87
145
|
CodeBlock: {
|
|
88
146
|
BackgroundColor: "#0c2b35",
|
|
147
|
+
FontColor: "#afb9bb",
|
|
89
148
|
Padding: 16,
|
|
90
149
|
BorderRadius: 4
|
|
91
150
|
}
|
|
92
151
|
}
|
|
93
152
|
};
|
|
153
|
+
function clone(value) {
|
|
154
|
+
return JSON.parse(JSON.stringify(value));
|
|
155
|
+
}
|
|
156
|
+
function merge(target, source) {
|
|
157
|
+
if (source === void 0) {
|
|
158
|
+
return target;
|
|
159
|
+
}
|
|
160
|
+
if (source === null || typeof source !== "object" || target === null || typeof target !== "object") {
|
|
161
|
+
return source;
|
|
162
|
+
}
|
|
163
|
+
for (const key of Object.keys(source)) {
|
|
164
|
+
target[key] = merge(target[key], source[key]);
|
|
165
|
+
}
|
|
166
|
+
return target;
|
|
167
|
+
}
|
|
168
|
+
var DefaultTheme = Object.freeze(clone(defaults));
|
|
169
|
+
var Theme = clone(defaults);
|
|
170
|
+
var version = 0;
|
|
171
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
172
|
+
function setTheme(config) {
|
|
173
|
+
const next = merge(clone(defaults), config);
|
|
174
|
+
for (const key of Object.keys(next)) {
|
|
175
|
+
Theme[key] = next[key];
|
|
176
|
+
}
|
|
177
|
+
version++;
|
|
178
|
+
listeners.forEach((listener) => listener());
|
|
179
|
+
requestRender();
|
|
180
|
+
}
|
|
181
|
+
function themeVersion() {
|
|
182
|
+
return version;
|
|
183
|
+
}
|
|
184
|
+
function onThemeChange(listener) {
|
|
185
|
+
listeners.add(listener);
|
|
186
|
+
return () => listeners.delete(listener);
|
|
187
|
+
}
|
|
94
188
|
|
|
95
189
|
// src/styles/text.ts
|
|
96
190
|
var DefaultSize = 16;
|
|
@@ -113,19 +207,19 @@ var TextStyle = class {
|
|
|
113
207
|
constructor(config) {
|
|
114
208
|
this.#size = config?.size === void 0 ? DefaultSize : config.size;
|
|
115
209
|
this.#color = config?.color === void 0 ? DefaultColor : config.color;
|
|
116
|
-
this.#font = config?.font
|
|
210
|
+
this.#font = config?.font;
|
|
117
211
|
this.#weight = config?.weight === void 0 ? "" /* Normal */ : config.weight;
|
|
118
212
|
this.#fontStyle = config?.style === void 0 ? "" /* Normal */ : config.style;
|
|
119
213
|
}
|
|
120
214
|
setupStyle(ctx, scale) {
|
|
121
215
|
ctx.fillStyle = this.#color;
|
|
122
|
-
ctx.font = this.#fontStyle + " " + this.#weight + " " + this.#size * scale + "px " + this
|
|
216
|
+
ctx.font = this.#fontStyle + " " + this.#weight + " " + this.#size * scale + "px " + this.getFont();
|
|
123
217
|
}
|
|
124
218
|
getSize() {
|
|
125
219
|
return this.#size;
|
|
126
220
|
}
|
|
127
221
|
getFont() {
|
|
128
|
-
return this.#font;
|
|
222
|
+
return this.#font ?? Theme.FontFamily;
|
|
129
223
|
}
|
|
130
224
|
getWeight() {
|
|
131
225
|
return this.#weight;
|
|
@@ -496,7 +590,18 @@ var ContextMenu = class _ContextMenu {
|
|
|
496
590
|
}
|
|
497
591
|
#tempBox = { Position: Zero(), Size: Zero() };
|
|
498
592
|
#openSubMenu;
|
|
499
|
-
|
|
593
|
+
#bounds = null;
|
|
594
|
+
#contains(point) {
|
|
595
|
+
if (this.#bounds !== null && InBox(this.#bounds, point)) {
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
return this.#openSubMenu !== void 0 && this.#openSubMenu.#contains(point);
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* A submenu is anchored to its parent's left edge and told the parent's
|
|
602
|
+
* width, so it can open on whichever side has room.
|
|
603
|
+
*/
|
|
604
|
+
render(ctx, anchor, graphScale, mousePosition, openRight, parentWidth = 0) {
|
|
500
605
|
const menuScale = 1.25;
|
|
501
606
|
const scaledEntryHeight = menuScale * contextEntryHeight;
|
|
502
607
|
const scaledEntryWidth = menuScale * 40 + this.#getMaxWidthForText(ctx, menuScale);
|
|
@@ -505,19 +610,35 @@ var ContextMenu = class _ContextMenu {
|
|
|
505
610
|
totalScaledHeight += this.#groups.At(i).height();
|
|
506
611
|
}
|
|
507
612
|
totalScaledHeight *= menuScale;
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
613
|
+
const canvasWidth = ctx.canvas.clientWidth;
|
|
614
|
+
const position = { x: anchor.x, y: anchor.y };
|
|
615
|
+
let opensRight = openRight;
|
|
616
|
+
if (parentWidth === 0) {
|
|
617
|
+
if (!openRight) {
|
|
618
|
+
position.x -= scaledEntryWidth;
|
|
619
|
+
}
|
|
620
|
+
if (openRight && position.x + scaledEntryWidth > canvasWidth) {
|
|
621
|
+
position.x = canvasWidth - scaledEntryWidth;
|
|
622
|
+
opensRight = false;
|
|
623
|
+
}
|
|
624
|
+
} else {
|
|
625
|
+
const rightOfParent = anchor.x + parentWidth;
|
|
626
|
+
const leftOfParent = anchor.x - scaledEntryWidth;
|
|
627
|
+
const fitsRight = rightOfParent + scaledEntryWidth <= canvasWidth;
|
|
628
|
+
const fitsLeft = leftOfParent >= 0;
|
|
629
|
+
opensRight = openRight ? fitsRight || !fitsLeft : !fitsLeft && fitsRight;
|
|
630
|
+
position.x = opensRight ? rightOfParent : leftOfParent;
|
|
631
|
+
position.x = Math.max(0, Math.min(position.x, canvasWidth - scaledEntryWidth));
|
|
512
632
|
}
|
|
513
633
|
if (position.y + totalScaledHeight > ctx.canvas.clientHeight) {
|
|
514
634
|
position.y = ctx.canvas.clientHeight - totalScaledHeight;
|
|
515
635
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
position.x
|
|
519
|
-
|
|
520
|
-
}
|
|
636
|
+
position.y = Math.max(0, position.y);
|
|
637
|
+
this.#bounds = {
|
|
638
|
+
Position: { x: position.x, y: position.y },
|
|
639
|
+
Size: { x: scaledEntryWidth, y: totalScaledHeight }
|
|
640
|
+
};
|
|
641
|
+
const pointer = mousePosition !== void 0 && this.#openSubMenu !== void 0 && this.#openSubMenu.#contains(mousePosition) ? void 0 : mousePosition;
|
|
521
642
|
this.#tempBox.Size.x = scaledEntryWidth;
|
|
522
643
|
this.#tempBox.Size.y = scaledEntryHeight;
|
|
523
644
|
CopyVector2(this.#tempBox.Position, position);
|
|
@@ -531,36 +652,36 @@ var ContextMenu = class _ContextMenu {
|
|
|
531
652
|
this.#tempBox.Position.y,
|
|
532
653
|
scaledEntryWidth,
|
|
533
654
|
totalScaledHeight,
|
|
534
|
-
|
|
655
|
+
Theme.ContextMenu.BorderRadius * menuScale
|
|
535
656
|
);
|
|
536
657
|
ctx.fill();
|
|
537
658
|
ctx.shadowBlur = 0;
|
|
538
659
|
let mouseIsOver = null;
|
|
539
|
-
|
|
660
|
+
if (pointer !== void 0) {
|
|
661
|
+
let row = 0;
|
|
662
|
+
for (let groupIndex = 0; groupIndex < this.#groups.Count(); groupIndex++) {
|
|
663
|
+
for (const entry of this.#groups.At(groupIndex).entries) {
|
|
664
|
+
this.#tempBox.Position.y = position.y + scaledEntryHeight * row;
|
|
665
|
+
if (InBox(this.#tempBox, pointer)) {
|
|
666
|
+
mouseIsOver = entry;
|
|
667
|
+
this.#openSubMenu = entry.subMenu;
|
|
668
|
+
this.#submenuPosition.x = position.x;
|
|
669
|
+
this.#submenuPosition.y = this.#tempBox.Position.y;
|
|
670
|
+
}
|
|
671
|
+
row++;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
540
675
|
let optionsRendered = 0;
|
|
541
676
|
for (let groupIndex = 0; groupIndex < this.#groups.Count(); groupIndex++) {
|
|
542
677
|
const group = this.#groups.At(groupIndex);
|
|
543
678
|
for (let entryIndex = 0; entryIndex < group.entries.length; entryIndex++) {
|
|
544
679
|
const entry = group.entries[entryIndex];
|
|
545
680
|
this.#tempBox.Position.y = position.y + scaledEntryHeight * optionsRendered;
|
|
546
|
-
|
|
547
|
-
if (mousePosition !== void 0 && InBox(this.#tempBox, mousePosition)) {
|
|
548
|
-
mouseIsOver = entry;
|
|
549
|
-
entryMousedOver = true;
|
|
550
|
-
if (entry.subMenu !== void 0) {
|
|
551
|
-
this.#openSubMenu = entry.subMenu;
|
|
552
|
-
this.#submenuPosition.x = position.x;
|
|
553
|
-
this.#submenuPosition.y = this.#tempBox.Position.y;
|
|
554
|
-
if (submenuOpenRight) {
|
|
555
|
-
this.#submenuPosition.x += scaledEntryWidth;
|
|
556
|
-
}
|
|
557
|
-
subOpenedThisFrame = true;
|
|
558
|
-
} else {
|
|
559
|
-
this.#openSubMenu = void 0;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
681
|
+
const entryMousedOver = entry === mouseIsOver;
|
|
562
682
|
const itemDisabled = entry.item !== void 0 && entry.item.state() === 1 /* Disabled */;
|
|
563
|
-
|
|
683
|
+
const highlighted = !itemDisabled && (entryMousedOver || this.#openSubMenu !== void 0 && entry.subMenu === this.#openSubMenu);
|
|
684
|
+
if (highlighted) {
|
|
564
685
|
ctx.fillStyle = Theme.ContextMenu.HighlightColor;
|
|
565
686
|
ctx.beginPath();
|
|
566
687
|
ctx.roundRect(
|
|
@@ -568,18 +689,20 @@ var ContextMenu = class _ContextMenu {
|
|
|
568
689
|
this.#tempBox.Position.y + scaledEntryHeight / 10,
|
|
569
690
|
scaledEntryWidth - scaledEntryHeight / 5,
|
|
570
691
|
scaledEntryHeight - scaledEntryHeight / 5,
|
|
571
|
-
|
|
692
|
+
Theme.ContextMenu.BorderRadius * menuScale
|
|
572
693
|
);
|
|
573
694
|
ctx.fill();
|
|
574
695
|
}
|
|
575
696
|
this.#textStyle.setupStyle(ctx, menuScale);
|
|
576
697
|
if (itemDisabled) {
|
|
577
698
|
ctx.fillStyle = Theme.ContextMenu.DisabledFontColor;
|
|
699
|
+
} else if (highlighted) {
|
|
700
|
+
ctx.fillStyle = Theme.ContextMenu.HighlightFontColor;
|
|
578
701
|
}
|
|
579
702
|
ctx.fillText(entry.text, position.x + scaledEntryHeight / 5, this.#tempBox.Position.y + scaledEntryHeight / 2);
|
|
580
703
|
if (entry.subMenu !== void 0) {
|
|
581
704
|
ctx.beginPath();
|
|
582
|
-
ctx.strokeStyle = Theme.ContextMenu.FontColor;
|
|
705
|
+
ctx.strokeStyle = highlighted ? Theme.ContextMenu.HighlightFontColor : Theme.ContextMenu.FontColor;
|
|
583
706
|
ctx.lineWidth = 1 * menuScale;
|
|
584
707
|
ctx.lineTo(position.x + scaledEntryWidth - scaledEntryHeight / 2.5, this.#tempBox.Position.y + scaledEntryHeight / 3);
|
|
585
708
|
ctx.lineTo(position.x + scaledEntryWidth - scaledEntryHeight / 4, this.#tempBox.Position.y + scaledEntryHeight / 2);
|
|
@@ -600,7 +723,7 @@ var ContextMenu = class _ContextMenu {
|
|
|
600
723
|
}
|
|
601
724
|
}
|
|
602
725
|
if (this.#openSubMenu !== void 0) {
|
|
603
|
-
const mouseOverSub = this.#openSubMenu.render(ctx, this.#submenuPosition, menuScale, mousePosition,
|
|
726
|
+
const mouseOverSub = this.#openSubMenu.render(ctx, this.#submenuPosition, menuScale, mousePosition, opensRight, scaledEntryWidth);
|
|
604
727
|
if (mouseOverSub !== null) {
|
|
605
728
|
mouseIsOver = mouseOverSub;
|
|
606
729
|
}
|
|
@@ -609,43 +732,6 @@ var ContextMenu = class _ContextMenu {
|
|
|
609
732
|
}
|
|
610
733
|
};
|
|
611
734
|
|
|
612
|
-
// src/render_scheduler.ts
|
|
613
|
-
var subscribers = /* @__PURE__ */ new Set();
|
|
614
|
-
var frameQueued = false;
|
|
615
|
-
var continuous = 0;
|
|
616
|
-
var canSchedule = () => typeof requestAnimationFrame === "function";
|
|
617
|
-
function onRenderRequested(redraw) {
|
|
618
|
-
subscribers.add(redraw);
|
|
619
|
-
return () => {
|
|
620
|
-
subscribers.delete(redraw);
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
function requestRender() {
|
|
624
|
-
if (frameQueued || !canSchedule()) {
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
frameQueued = true;
|
|
628
|
-
requestAnimationFrame(() => {
|
|
629
|
-
frameQueued = false;
|
|
630
|
-
subscribers.forEach((redraw) => redraw());
|
|
631
|
-
if (continuous > 0) {
|
|
632
|
-
requestRender();
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
function renderContinuously() {
|
|
637
|
-
continuous++;
|
|
638
|
-
requestRender();
|
|
639
|
-
let released = false;
|
|
640
|
-
return () => {
|
|
641
|
-
if (released) {
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
released = true;
|
|
645
|
-
continuous--;
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
|
|
649
735
|
// src/input.ts
|
|
650
736
|
var LONG_PRESS_MS = 500;
|
|
651
737
|
var LONG_PRESS_SLOP = 12;
|
|
@@ -1230,6 +1316,7 @@ function splitStringIntoLines(ctx, str, maxWidth) {
|
|
|
1230
1316
|
// src/types/text.ts
|
|
1231
1317
|
var Text = class _Text {
|
|
1232
1318
|
#measured;
|
|
1319
|
+
#measuredForTheme = -1;
|
|
1233
1320
|
#size;
|
|
1234
1321
|
#style;
|
|
1235
1322
|
#value;
|
|
@@ -1309,9 +1396,10 @@ var Text = class _Text {
|
|
|
1309
1396
|
return results;
|
|
1310
1397
|
}
|
|
1311
1398
|
#measure(ctx) {
|
|
1312
|
-
if (this.#measured) {
|
|
1399
|
+
if (this.#measured && this.#measuredForTheme === themeVersion()) {
|
|
1313
1400
|
return;
|
|
1314
1401
|
}
|
|
1402
|
+
this.#measuredForTheme = themeVersion();
|
|
1315
1403
|
this.#style.setupStyle(ctx, 1);
|
|
1316
1404
|
if (this.#maxWidth == -1) {
|
|
1317
1405
|
const measurements = ctx.measureText(this.#value);
|
|
@@ -1387,7 +1475,7 @@ function RgbToHex(c) {
|
|
|
1387
1475
|
return "#" + componentToHex(Math.round(c.r * 255)) + componentToHex(Math.round(c.g * 255)) + componentToHex(Math.round(c.b * 255));
|
|
1388
1476
|
}
|
|
1389
1477
|
function HexToColor(hex) {
|
|
1390
|
-
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})
|
|
1478
|
+
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})(?:[a-f\d]{2})?$/i.exec(hex);
|
|
1391
1479
|
return result ? {
|
|
1392
1480
|
r: parseInt(result[1], 16) / 255,
|
|
1393
1481
|
g: parseInt(result[2], 16) / 255,
|
|
@@ -1477,6 +1565,21 @@ function portConfigTypes(config) {
|
|
|
1477
1565
|
}
|
|
1478
1566
|
return [config.type === void 0 ? "" : config.type];
|
|
1479
1567
|
}
|
|
1568
|
+
var ARRAY_SLOT_PITCH = 12;
|
|
1569
|
+
function arrayInsertIndex(box, count, y) {
|
|
1570
|
+
if (count === 0) {
|
|
1571
|
+
return 0;
|
|
1572
|
+
}
|
|
1573
|
+
const index = Math.round((y - box.Position.y) / (box.Size.y / count));
|
|
1574
|
+
return Math.min(count, Math.max(0, index));
|
|
1575
|
+
}
|
|
1576
|
+
function arraySlotCenter(box, count, index) {
|
|
1577
|
+
const slots = Math.max(1, count);
|
|
1578
|
+
return {
|
|
1579
|
+
x: box.Position.x + box.Size.x / 2,
|
|
1580
|
+
y: box.Position.y + (index + 0.5) * box.Size.y / slots
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1480
1583
|
function fallbackColor(type, s) {
|
|
1481
1584
|
let value = 0;
|
|
1482
1585
|
for (var i = 0; i < type.length; i++) {
|
|
@@ -1501,6 +1604,7 @@ var Port = class {
|
|
|
1501
1604
|
#acceptedTypes;
|
|
1502
1605
|
#description;
|
|
1503
1606
|
#array;
|
|
1607
|
+
#hidden;
|
|
1504
1608
|
#configuredEmptyFill;
|
|
1505
1609
|
#configuredFilledFill;
|
|
1506
1610
|
#dataTypePopupElement;
|
|
@@ -1516,6 +1620,7 @@ var Port = class {
|
|
|
1516
1620
|
this.#acceptedTypes = config?.acceptedTypes === void 0 ? [] : config.acceptedTypes;
|
|
1517
1621
|
this.#description = config?.description === void 0 ? "" : config.description;
|
|
1518
1622
|
this.#array = config?.array === true;
|
|
1623
|
+
this.#hidden = config?.hidden === true;
|
|
1519
1624
|
this.#configuredEmptyFill = config?.emptyStyle?.fillColor;
|
|
1520
1625
|
this.#configuredFilledFill = config?.filledStyle?.fillColor;
|
|
1521
1626
|
this.#emptyStyle = {
|
|
@@ -1614,9 +1719,11 @@ var Port = class {
|
|
|
1614
1719
|
this.setDataType(type);
|
|
1615
1720
|
this.#refreshDataType();
|
|
1616
1721
|
}
|
|
1617
|
-
addConnection(connection) {
|
|
1618
|
-
const c = this.#connections.length;
|
|
1619
|
-
this
|
|
1722
|
+
addConnection(connection, index) {
|
|
1723
|
+
const c = index === void 0 ? this.#connections.length : Math.min(Math.max(index, 0), this.#connections.length);
|
|
1724
|
+
const was = this.hidden();
|
|
1725
|
+
this.#connections.splice(c, 0, connection);
|
|
1726
|
+
this.#visibilityMayHaveChanged(was);
|
|
1620
1727
|
for (let i = 0; i < this.#onConnectionAdded.length; i++) {
|
|
1621
1728
|
this.#onConnectionAdded[i](connection, c, this, this.#portType, this.#node);
|
|
1622
1729
|
}
|
|
@@ -1637,6 +1744,32 @@ var Port = class {
|
|
|
1637
1744
|
connections() {
|
|
1638
1745
|
return this.#connections;
|
|
1639
1746
|
}
|
|
1747
|
+
/**
|
|
1748
|
+
* How many wires the port has room for: one per connection on an array
|
|
1749
|
+
* input, so each can be told apart and picked up on its own.
|
|
1750
|
+
*/
|
|
1751
|
+
slots() {
|
|
1752
|
+
if (this.#portType !== "INPUTARRAY" /* InputArray */) {
|
|
1753
|
+
return 1;
|
|
1754
|
+
}
|
|
1755
|
+
return Math.max(1, this.#connections.length);
|
|
1756
|
+
}
|
|
1757
|
+
setHidden(hidden) {
|
|
1758
|
+
const was = this.hidden();
|
|
1759
|
+
this.#hidden = hidden;
|
|
1760
|
+
this.#visibilityMayHaveChanged(was);
|
|
1761
|
+
}
|
|
1762
|
+
#visibilityMayHaveChanged(was) {
|
|
1763
|
+
if (was !== this.hidden() || this.#portType === "INPUTARRAY" /* InputArray */) {
|
|
1764
|
+
this.#node?.invalidateLayout();
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
/**
|
|
1768
|
+
* A port asked to hide still shows while anything is connected to it.
|
|
1769
|
+
*/
|
|
1770
|
+
hidden() {
|
|
1771
|
+
return this.#hidden && this.#connections.length === 0;
|
|
1772
|
+
}
|
|
1640
1773
|
addConnectionRemovedListener(callback) {
|
|
1641
1774
|
if (callback === void 0) {
|
|
1642
1775
|
return;
|
|
@@ -1646,7 +1779,9 @@ var Port = class {
|
|
|
1646
1779
|
clearConnection(connection) {
|
|
1647
1780
|
const index = this.#connections.indexOf(connection);
|
|
1648
1781
|
if (index > -1) {
|
|
1782
|
+
const was = this.hidden();
|
|
1649
1783
|
this.#connections.splice(index, 1);
|
|
1784
|
+
this.#visibilityMayHaveChanged(was);
|
|
1650
1785
|
for (let i = 0; i < this.#onConnectionRemoved.length; i++) {
|
|
1651
1786
|
this.#onConnectionRemoved[i](connection, index, this, this.#portType, this.#node);
|
|
1652
1787
|
}
|
|
@@ -1705,20 +1840,31 @@ var Port = class {
|
|
|
1705
1840
|
this.#dataTypePopupElement.render(ctx, { x: xPos - size.x / 2, y: yPos }, 1, size);
|
|
1706
1841
|
});
|
|
1707
1842
|
}
|
|
1843
|
+
const slots = this.slots();
|
|
1844
|
+
const height2 = slots === 1 ? scaledRadius * 2 : slots * ARRAY_SLOT_PITCH * camera.zoom;
|
|
1708
1845
|
this.#box.Position.x = position.x - scaledRadius;
|
|
1709
|
-
this.#box.Position.y = position.y -
|
|
1846
|
+
this.#box.Position.y = position.y - height2 / 2;
|
|
1710
1847
|
this.#box.Size.x = scaledRadius * 2;
|
|
1711
|
-
this.#box.Size.y =
|
|
1848
|
+
this.#box.Size.y = height2;
|
|
1712
1849
|
ctx.strokeStyle = style.borderColor;
|
|
1713
1850
|
ctx.fillStyle = style.fillColor;
|
|
1714
1851
|
ctx.beginPath();
|
|
1715
1852
|
if (this.#portType === "INPUTARRAY" /* InputArray */) {
|
|
1716
|
-
ctx.
|
|
1853
|
+
ctx.roundRect(this.#box.Position.x, this.#box.Position.y, this.#box.Size.x, this.#box.Size.y, slots === 1 ? 0 : scaledRadius);
|
|
1717
1854
|
} else {
|
|
1718
1855
|
ctx.arc(position.x, position.y, scaledRadius, 0, 2 * Math.PI);
|
|
1719
1856
|
}
|
|
1720
1857
|
ctx.fill();
|
|
1721
1858
|
ctx.stroke();
|
|
1859
|
+
if (slots > 1) {
|
|
1860
|
+
ctx.fillStyle = style.borderColor;
|
|
1861
|
+
for (let i = 0; i < slots; i++) {
|
|
1862
|
+
const slot = arraySlotCenter(this.#box, slots, i);
|
|
1863
|
+
ctx.beginPath();
|
|
1864
|
+
ctx.arc(slot.x, slot.y, scaledRadius * 0.4, 0, 2 * Math.PI);
|
|
1865
|
+
ctx.fill();
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1722
1868
|
return this.#box;
|
|
1723
1869
|
}
|
|
1724
1870
|
};
|
|
@@ -1834,331 +1980,480 @@ var BoxStyle = class {
|
|
|
1834
1980
|
}
|
|
1835
1981
|
};
|
|
1836
1982
|
|
|
1837
|
-
// src/
|
|
1838
|
-
var
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1983
|
+
// src/styles/cursor.ts
|
|
1984
|
+
var CursorStyle = /* @__PURE__ */ ((CursorStyle2) => {
|
|
1985
|
+
CursorStyle2["Default"] = "default";
|
|
1986
|
+
CursorStyle2["Pointer"] = "pointer";
|
|
1987
|
+
CursorStyle2["Grab"] = "grab";
|
|
1988
|
+
CursorStyle2["Grabbing"] = "grabbing";
|
|
1989
|
+
CursorStyle2["Wait"] = "wait";
|
|
1990
|
+
CursorStyle2["Help"] = "help";
|
|
1991
|
+
CursorStyle2["Crosshair"] = "crosshair";
|
|
1992
|
+
CursorStyle2["Cell"] = "cell";
|
|
1993
|
+
CursorStyle2["Text"] = "text";
|
|
1994
|
+
CursorStyle2["Move"] = "move";
|
|
1995
|
+
CursorStyle2["NotAllowed"] = "not-allowed";
|
|
1996
|
+
CursorStyle2["ZoomIn"] = "zoom-in";
|
|
1997
|
+
CursorStyle2["ZoomOut"] = "zoom-out";
|
|
1998
|
+
CursorStyle2["None"] = "none";
|
|
1999
|
+
CursorStyle2["ContextMenu"] = "context-menu";
|
|
2000
|
+
CursorStyle2["EWResize"] = "ew-resize";
|
|
2001
|
+
return CursorStyle2;
|
|
2002
|
+
})(CursorStyle || {});
|
|
2003
|
+
|
|
2004
|
+
// src/styles/bevel.ts
|
|
2005
|
+
function drawBevel(ctx, box, scale, colors, sunken) {
|
|
2006
|
+
const width2 = Math.max(1, 2 * scale);
|
|
2007
|
+
const half = width2 / 2;
|
|
2008
|
+
const left = box.Position.x + half;
|
|
2009
|
+
const top = box.Position.y + half;
|
|
2010
|
+
const right = box.Position.x + box.Size.x - half;
|
|
2011
|
+
const bottom = box.Position.y + box.Size.y - half;
|
|
2012
|
+
ctx.save();
|
|
2013
|
+
ctx.lineWidth = width2;
|
|
2014
|
+
ctx.lineCap = "square";
|
|
2015
|
+
ctx.strokeStyle = sunken ? colors.Dark : colors.Light;
|
|
2016
|
+
ctx.beginPath();
|
|
2017
|
+
ctx.moveTo(left, bottom);
|
|
2018
|
+
ctx.lineTo(left, top);
|
|
2019
|
+
ctx.lineTo(right, top);
|
|
2020
|
+
ctx.stroke();
|
|
2021
|
+
ctx.strokeStyle = sunken ? colors.Light : colors.Dark;
|
|
2022
|
+
ctx.beginPath();
|
|
2023
|
+
ctx.moveTo(right, top);
|
|
2024
|
+
ctx.lineTo(right, bottom);
|
|
2025
|
+
ctx.lineTo(left, bottom);
|
|
2026
|
+
ctx.stroke();
|
|
2027
|
+
ctx.restore();
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
// src/widgets/field.ts
|
|
2031
|
+
function widgetFont(scale) {
|
|
2032
|
+
return `${Theme.Widget.FontSize * scale}px ${Theme.FontFamily}`;
|
|
2033
|
+
}
|
|
2034
|
+
function fieldBackground(look) {
|
|
2035
|
+
if (look.background !== void 0) {
|
|
2036
|
+
return look.background;
|
|
1867
2037
|
}
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
if (this.#onClose !== void 0) {
|
|
1871
|
-
this.#onClose(option);
|
|
1872
|
-
}
|
|
2038
|
+
if (look.input) {
|
|
2039
|
+
return look.hovered ? Theme.Widget.Input.HoverBackgroundColor : Theme.Widget.Input.BackgroundColor;
|
|
1873
2040
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
this.#popup.style.cssText = "position:absolute;top:0;width:100%;display:flex;height:100%;justify-content:center;align-items:center;background-color:#00000070;";
|
|
1877
|
-
document.body.appendChild(this.#popup);
|
|
1878
|
-
const container = document.createElement("div");
|
|
1879
|
-
container.style.cssText = "background-color:#000;color:white;font-family:Verdana;border-radius:8px;padding:16px;";
|
|
1880
|
-
this.#popup.appendChild(container);
|
|
1881
|
-
const title = document.createElement("h2");
|
|
1882
|
-
title.textContent = this.#title;
|
|
1883
|
-
title.style.cssText = "margin-top:0;margin-bottom:16px;";
|
|
1884
|
-
container.appendChild(title);
|
|
1885
|
-
if (this.#content !== void 0) {
|
|
1886
|
-
container.appendChild(this.#content());
|
|
1887
|
-
}
|
|
1888
|
-
const buttonContainer = document.createElement("div");
|
|
1889
|
-
buttonContainer.style.cssText = "margin-top:16px;justify-content:space-around;align-items:center;flex-direction:row;display:flex;";
|
|
1890
|
-
container.appendChild(buttonContainer);
|
|
1891
|
-
for (let i = 0; i < this.#options.length; i++) {
|
|
1892
|
-
const button = document.createElement("button");
|
|
1893
|
-
button.style.cssText = this.#buttonCSS;
|
|
1894
|
-
button.textContent = this.#options[i];
|
|
1895
|
-
button.onclick = () => this.#close(this.#options[i]);
|
|
1896
|
-
buttonContainer.appendChild(button);
|
|
1897
|
-
}
|
|
1898
|
-
this.#keyHandler = (e) => {
|
|
1899
|
-
if (e.key === "Enter") {
|
|
1900
|
-
e.preventDefault();
|
|
1901
|
-
this.#close(this.#options.length > 0 ? this.#options[0] : null);
|
|
1902
|
-
} else if (e.key === "Escape") {
|
|
1903
|
-
e.preventDefault();
|
|
1904
|
-
this.#close(null);
|
|
1905
|
-
}
|
|
1906
|
-
e.stopPropagation();
|
|
1907
|
-
};
|
|
1908
|
-
document.addEventListener("keydown", this.#keyHandler, true);
|
|
2041
|
+
if (look.pressed) {
|
|
2042
|
+
return Theme.Widget.Button.Click.BackgroundColor;
|
|
1909
2043
|
}
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
// src/styles/textBox.ts
|
|
1913
|
-
function TextBoxStyleWithFallback(input, fallback) {
|
|
1914
|
-
return {
|
|
1915
|
-
box: BoxStyleWithFallback(input?.box, fallback?.box),
|
|
1916
|
-
text: TextStyleFallback(input?.text, fallback?.text),
|
|
1917
|
-
textAlign: input?.textAlign === void 0 ? fallback?.textAlign : input?.textAlign,
|
|
1918
|
-
textBaseline: input?.textBaseline === void 0 ? fallback?.textBaseline : input?.textBaseline
|
|
1919
|
-
};
|
|
2044
|
+
return look.hovered ? Theme.Widget.Hover.BackgroundColor : Theme.Widget.BackgroundColor;
|
|
1920
2045
|
}
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
this.#textBaseline = config?.textBaseline === void 0 ? "middle" /* Middle */ : config.textBaseline;
|
|
1931
|
-
}
|
|
1932
|
-
Draw(ctx, box, scale, text) {
|
|
1933
|
-
this.#box.Draw(ctx, box, scale);
|
|
1934
|
-
ctx.textAlign = this.#textAlign;
|
|
1935
|
-
ctx.textBaseline = this.#textBaseline;
|
|
1936
|
-
this.#text.setupStyle(ctx, scale);
|
|
1937
|
-
ctx.fillText(
|
|
1938
|
-
text,
|
|
1939
|
-
box.Position.x + box.Size.x / 2,
|
|
1940
|
-
box.Position.y + box.Size.y / 2
|
|
1941
|
-
);
|
|
2046
|
+
function drawFieldBox(ctx, box, scale, look) {
|
|
2047
|
+
ctx.fillStyle = fieldBackground(look);
|
|
2048
|
+
ctx.beginPath();
|
|
2049
|
+
ctx.roundRect(box.Position.x, box.Position.y, box.Size.x, box.Size.y, Theme.Widget.Border.Radius * scale);
|
|
2050
|
+
ctx.fill();
|
|
2051
|
+
if (Theme.Widget.Border.Size > 0) {
|
|
2052
|
+
ctx.strokeStyle = look.border ?? Theme.Widget.Border.Color;
|
|
2053
|
+
ctx.lineWidth = Theme.Widget.Border.Size * scale;
|
|
2054
|
+
ctx.stroke();
|
|
1942
2055
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
2056
|
+
if (Theme.Widget.Bevel !== null) {
|
|
2057
|
+
drawBevel(ctx, box, scale, Theme.Widget.Bevel, look.input === true || look.pressed === true);
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
function drawField(ctx, box, scale, look) {
|
|
2061
|
+
drawFieldBox(ctx, box, scale, look);
|
|
2062
|
+
drawFieldText(ctx, box, scale, look);
|
|
2063
|
+
}
|
|
2064
|
+
function drawFieldText(ctx, box, scale, look) {
|
|
2065
|
+
const padding = Theme.Widget.Padding * scale;
|
|
2066
|
+
const middle = box.Position.y + box.Size.y / 2;
|
|
2067
|
+
const textColor = look.textColor ?? (look.input ? Theme.Widget.Input.FontColor : Theme.Widget.FontColor);
|
|
2068
|
+
ctx.font = widgetFont(scale);
|
|
2069
|
+
ctx.textBaseline = "middle";
|
|
2070
|
+
if (look.label === void 0 || look.label === "") {
|
|
2071
|
+
ctx.textAlign = "center";
|
|
2072
|
+
ctx.fillStyle = textColor;
|
|
1948
2073
|
ctx.fillText(
|
|
1949
|
-
|
|
2074
|
+
fitString(ctx, look.value, box.Size.x - padding * 2),
|
|
1950
2075
|
box.Position.x + box.Size.x / 2,
|
|
1951
|
-
|
|
2076
|
+
middle
|
|
1952
2077
|
);
|
|
2078
|
+
return;
|
|
1953
2079
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
2080
|
+
const room = box.Size.x - padding * 2;
|
|
2081
|
+
const label = fitString(ctx, look.label, room / 2);
|
|
2082
|
+
const labelWidth = ctx.measureText(label).width;
|
|
2083
|
+
ctx.textAlign = "left";
|
|
2084
|
+
ctx.fillStyle = look.textColor ?? Theme.Widget.LabelColor;
|
|
2085
|
+
ctx.fillText(label, box.Position.x + padding, middle);
|
|
2086
|
+
ctx.textAlign = "right";
|
|
2087
|
+
ctx.fillStyle = textColor;
|
|
2088
|
+
ctx.fillText(
|
|
2089
|
+
fitString(ctx, look.value, room - labelWidth - padding),
|
|
2090
|
+
box.Position.x + box.Size.x - padding,
|
|
2091
|
+
middle
|
|
2092
|
+
);
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
// src/widgets/inplace.ts
|
|
2096
|
+
function editInPlace(config) {
|
|
2097
|
+
const canvas = config.canvas;
|
|
2098
|
+
if (canvas === null) {
|
|
2099
|
+
return null;
|
|
1962
2100
|
}
|
|
1963
|
-
|
|
2101
|
+
const rect = canvas.getBoundingClientRect();
|
|
2102
|
+
const sx = canvas.width > 0 ? rect.width / canvas.width : 1;
|
|
2103
|
+
const sy = canvas.height > 0 ? rect.height / canvas.height : 1;
|
|
2104
|
+
const input = document.createElement("input");
|
|
2105
|
+
input.type = "text";
|
|
2106
|
+
input.value = config.value;
|
|
2107
|
+
input.style.cssText = [
|
|
2108
|
+
"position: fixed",
|
|
2109
|
+
`left: ${rect.left + config.box.Position.x * sx}px`,
|
|
2110
|
+
`top: ${rect.top + config.box.Position.y * sy}px`,
|
|
2111
|
+
`width: ${config.box.Size.x * sx}px`,
|
|
2112
|
+
`height: ${config.box.Size.y * sy}px`,
|
|
2113
|
+
"box-sizing: border-box",
|
|
2114
|
+
"margin: 0",
|
|
2115
|
+
`padding: 0 ${Theme.Widget.Padding * config.scale}px`,
|
|
2116
|
+
`font: ${Theme.Widget.FontSize * config.scale * sy}px ${Theme.FontFamily}`,
|
|
2117
|
+
`color: ${Theme.Widget.FontColor}`,
|
|
2118
|
+
`background: ${Theme.Widget.Hover.BackgroundColor}`,
|
|
2119
|
+
`border: ${Theme.Widget.Border.Size * config.scale}px solid ${Theme.Widget.Border.Color}`,
|
|
2120
|
+
`border-radius: ${Theme.Widget.Border.Radius * config.scale}px`,
|
|
2121
|
+
"outline: none",
|
|
2122
|
+
"z-index: 1000"
|
|
2123
|
+
].join(";");
|
|
2124
|
+
let done = false;
|
|
2125
|
+
const finish = (keep) => {
|
|
2126
|
+
if (done) {
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
done = true;
|
|
2130
|
+
input.remove();
|
|
2131
|
+
if (keep) {
|
|
2132
|
+
config.onCommit(input.value);
|
|
2133
|
+
}
|
|
2134
|
+
requestRender();
|
|
2135
|
+
};
|
|
2136
|
+
input.addEventListener("keydown", (event) => {
|
|
2137
|
+
event.stopPropagation();
|
|
2138
|
+
if (event.key === "Enter") {
|
|
2139
|
+
event.preventDefault();
|
|
2140
|
+
finish(true);
|
|
2141
|
+
} else if (event.key === "Escape") {
|
|
2142
|
+
event.preventDefault();
|
|
2143
|
+
finish(false);
|
|
2144
|
+
}
|
|
2145
|
+
});
|
|
2146
|
+
input.addEventListener("blur", () => finish(true));
|
|
2147
|
+
document.body.appendChild(input);
|
|
2148
|
+
input.focus();
|
|
2149
|
+
input.select();
|
|
2150
|
+
return input;
|
|
2151
|
+
}
|
|
1964
2152
|
|
|
1965
2153
|
// src/widgets/widget.ts
|
|
1966
2154
|
var width = 150;
|
|
1967
2155
|
var height = 25;
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
2156
|
+
var WidgetBase = class {
|
|
2157
|
+
#hidden = false;
|
|
2158
|
+
#disabled = false;
|
|
2159
|
+
#hovered = false;
|
|
2160
|
+
#pressed = false;
|
|
2161
|
+
#box = { Position: { x: 0, y: 0 }, Size: { x: 0, y: 0 } };
|
|
2162
|
+
#canvas = null;
|
|
2163
|
+
#scale = 1;
|
|
2164
|
+
Draw(ctx, box, scale, mousePosition) {
|
|
2165
|
+
this.#box = { Position: { x: box.Position.x, y: box.Position.y }, Size: { x: box.Size.x, y: box.Size.y } };
|
|
2166
|
+
this.#canvas = ctx.canvas ?? null;
|
|
2167
|
+
this.#scale = scale;
|
|
2168
|
+
this.#hovered = !this.#disabled && mousePosition !== void 0 && InBox(box, mousePosition);
|
|
2169
|
+
ctx.save();
|
|
2170
|
+
if (this.#disabled) {
|
|
2171
|
+
ctx.globalAlpha *= 0.4;
|
|
2172
|
+
}
|
|
2173
|
+
this.render(ctx, box, scale);
|
|
2174
|
+
ctx.restore();
|
|
2175
|
+
}
|
|
2176
|
+
PointerDown(pointer) {
|
|
2177
|
+
if (this.#disabled) {
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
this.#pressed = true;
|
|
2181
|
+
this.onPointerDown(pointer);
|
|
2182
|
+
requestRender();
|
|
1971
2183
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
return existing;
|
|
2184
|
+
PointerMove(pointer) {
|
|
2185
|
+
if (!this.#pressed) {
|
|
2186
|
+
return;
|
|
1976
2187
|
}
|
|
2188
|
+
this.onPointerMove(pointer);
|
|
2189
|
+
requestRender();
|
|
1977
2190
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2191
|
+
PointerUp(pointer) {
|
|
2192
|
+
if (!this.#pressed) {
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
this.#pressed = false;
|
|
2196
|
+
this.onPointerUp(pointer);
|
|
2197
|
+
if (pointer.inside) {
|
|
2198
|
+
this.onClick(pointer);
|
|
2199
|
+
}
|
|
2200
|
+
requestRender();
|
|
2201
|
+
}
|
|
2202
|
+
Cursor() {
|
|
2203
|
+
return "pointer" /* Pointer */;
|
|
2204
|
+
}
|
|
2205
|
+
Hidden() {
|
|
2206
|
+
return this.#hidden;
|
|
2207
|
+
}
|
|
2208
|
+
setHidden(hidden) {
|
|
2209
|
+
this.#hidden = hidden;
|
|
2210
|
+
requestRender();
|
|
2211
|
+
}
|
|
2212
|
+
Disabled() {
|
|
2213
|
+
return this.#disabled;
|
|
2214
|
+
}
|
|
2215
|
+
setDisabled(disabled) {
|
|
2216
|
+
this.#disabled = disabled;
|
|
2217
|
+
if (disabled) {
|
|
2218
|
+
this.#pressed = false;
|
|
2219
|
+
}
|
|
2220
|
+
requestRender();
|
|
2221
|
+
}
|
|
2222
|
+
hovered() {
|
|
2223
|
+
return this.#hovered;
|
|
2224
|
+
}
|
|
2225
|
+
pressed() {
|
|
2226
|
+
return this.#pressed;
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2229
|
+
* Where the widget was last drawn, in canvas pixels.
|
|
2230
|
+
*/
|
|
2231
|
+
box() {
|
|
2232
|
+
return this.#box;
|
|
2233
|
+
}
|
|
2234
|
+
canvas() {
|
|
2235
|
+
return this.#canvas;
|
|
2236
|
+
}
|
|
2237
|
+
scale() {
|
|
2238
|
+
return this.#scale;
|
|
2239
|
+
}
|
|
2240
|
+
onPointerDown(pointer) {
|
|
2241
|
+
}
|
|
2242
|
+
onPointerMove(pointer) {
|
|
2243
|
+
}
|
|
2244
|
+
onPointerUp(pointer) {
|
|
2245
|
+
}
|
|
2246
|
+
/**
|
|
2247
|
+
* A press and release that both landed on the widget.
|
|
2248
|
+
*/
|
|
2249
|
+
onClick(pointer) {
|
|
2250
|
+
}
|
|
2251
|
+
};
|
|
2252
|
+
function startingWidgetValue(node, property, configured, fallback) {
|
|
2253
|
+
if (configured !== void 0) {
|
|
2254
|
+
return configured;
|
|
2255
|
+
}
|
|
2256
|
+
if (property !== void 0 && property !== null) {
|
|
2257
|
+
const existing = node.getProperty(property);
|
|
2258
|
+
if (existing !== void 0 && existing !== null) {
|
|
2259
|
+
return existing;
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
return fallback;
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
// src/widgets/value.ts
|
|
2266
|
+
var ValueWidget = class extends WidgetBase {
|
|
1983
2267
|
#node;
|
|
1984
|
-
#
|
|
2268
|
+
#property;
|
|
1985
2269
|
#value;
|
|
1986
|
-
#
|
|
1987
|
-
#
|
|
1988
|
-
#text;
|
|
2270
|
+
#label;
|
|
2271
|
+
#width;
|
|
1989
2272
|
#callback;
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2273
|
+
#onInput;
|
|
2274
|
+
constructor(node, config, fallback) {
|
|
2275
|
+
super();
|
|
1993
2276
|
this.#node = node;
|
|
1994
|
-
this.#
|
|
1995
|
-
this.#
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
border: {
|
|
1999
|
-
size: Theme.Widget.Border.Size,
|
|
2000
|
-
color: Theme.Widget.Border.Color
|
|
2001
|
-
},
|
|
2002
|
-
radius: Theme.Widget.Border.Radius
|
|
2003
|
-
},
|
|
2004
|
-
text: { color: Theme.Widget.FontColor }
|
|
2005
|
-
}));
|
|
2006
|
-
this.#highlightBoxStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.highlightBoxStyle, {
|
|
2007
|
-
box: {
|
|
2008
|
-
color: Theme.Widget.Hover.BackgroundColor,
|
|
2009
|
-
border: {
|
|
2010
|
-
size: Theme.Widget.Border.Size,
|
|
2011
|
-
color: Theme.Widget.Border.Color
|
|
2012
|
-
},
|
|
2013
|
-
radius: Theme.Widget.Border.Radius
|
|
2014
|
-
},
|
|
2015
|
-
text: { color: Theme.Widget.FontColor }
|
|
2016
|
-
}));
|
|
2017
|
-
this.Set(startingWidgetValue(node, this.#nodeProperty, config?.value, 0));
|
|
2277
|
+
this.#property = config?.property;
|
|
2278
|
+
this.#label = config?.label ?? "";
|
|
2279
|
+
this.#width = config?.width ?? width;
|
|
2280
|
+
this.#value = startingWidgetValue(node, this.#property, config?.value, fallback);
|
|
2018
2281
|
this.#callback = config?.callback;
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2282
|
+
this.#onInput = config?.onInput;
|
|
2283
|
+
if (this.#property !== void 0) {
|
|
2284
|
+
node.setProperty(this.#property, this.#value);
|
|
2285
|
+
node.addPropertyChangeListener(this.#property, (_, next) => this.setValue(next));
|
|
2023
2286
|
}
|
|
2024
2287
|
}
|
|
2025
2288
|
Size() {
|
|
2026
|
-
return {
|
|
2289
|
+
return { x: this.#width, y: height };
|
|
2290
|
+
}
|
|
2291
|
+
value() {
|
|
2292
|
+
return this.#value;
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* Shows a new value without treating it as a person's edit: no callback
|
|
2296
|
+
* and no edit event.
|
|
2297
|
+
*/
|
|
2298
|
+
setValue(value) {
|
|
2299
|
+
if (this.equal(value, this.#value)) {
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
this.#value = value;
|
|
2303
|
+
this.valueChanged();
|
|
2304
|
+
if (this.#property !== void 0) {
|
|
2305
|
+
this.#node.setProperty(this.#property, value);
|
|
2306
|
+
}
|
|
2307
|
+
requestRender();
|
|
2308
|
+
}
|
|
2309
|
+
label() {
|
|
2310
|
+
return this.#label;
|
|
2027
2311
|
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2312
|
+
setLabel(label) {
|
|
2313
|
+
this.#label = label;
|
|
2314
|
+
requestRender();
|
|
2315
|
+
}
|
|
2316
|
+
node() {
|
|
2317
|
+
return this.#node;
|
|
2318
|
+
}
|
|
2319
|
+
input(value) {
|
|
2320
|
+
this.setValue(value);
|
|
2321
|
+
this.#onInput?.(value);
|
|
2322
|
+
}
|
|
2323
|
+
/**
|
|
2324
|
+
* Ends a person's edit. Nothing is reported when they ended where they
|
|
2325
|
+
* started.
|
|
2326
|
+
*/
|
|
2327
|
+
commit(value, startedAt) {
|
|
2328
|
+
this.setValue(value);
|
|
2329
|
+
if (this.equal(value, startedAt)) {
|
|
2030
2330
|
return;
|
|
2031
2331
|
}
|
|
2032
|
-
this.#
|
|
2033
|
-
|
|
2034
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
2332
|
+
if (this.#property !== void 0) {
|
|
2333
|
+
this.#node.editProperty(this.#property, value);
|
|
2035
2334
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2335
|
+
this.#callback?.(value);
|
|
2336
|
+
}
|
|
2337
|
+
valueChanged() {
|
|
2338
|
+
}
|
|
2339
|
+
equal(a, b) {
|
|
2340
|
+
return Object.is(a, b);
|
|
2341
|
+
}
|
|
2342
|
+
};
|
|
2343
|
+
|
|
2344
|
+
// src/widgets/number.ts
|
|
2345
|
+
var DRAG_THRESHOLD = 3;
|
|
2346
|
+
var NumberWidget = class extends ValueWidget {
|
|
2347
|
+
#step;
|
|
2348
|
+
#integer;
|
|
2349
|
+
#min;
|
|
2350
|
+
#max;
|
|
2351
|
+
#startX = 0;
|
|
2352
|
+
#startValue = 0;
|
|
2353
|
+
#dragging = false;
|
|
2354
|
+
#dragged = false;
|
|
2355
|
+
constructor(node, config) {
|
|
2356
|
+
super(node, config, 0);
|
|
2357
|
+
this.#step = config?.step;
|
|
2358
|
+
this.#integer = config?.integer === true;
|
|
2359
|
+
this.#min = config?.min ?? -Infinity;
|
|
2360
|
+
this.#max = config?.max ?? Infinity;
|
|
2361
|
+
}
|
|
2362
|
+
Cursor() {
|
|
2363
|
+
return "ew-resize" /* EWResize */;
|
|
2364
|
+
}
|
|
2365
|
+
render(ctx, box, scale) {
|
|
2366
|
+
drawField(ctx, box, scale, {
|
|
2367
|
+
input: true,
|
|
2368
|
+
hovered: this.hovered() || this.pressed(),
|
|
2369
|
+
label: this.label(),
|
|
2370
|
+
value: formatNumber(this.value())
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
onPointerDown(pointer) {
|
|
2374
|
+
this.#startX = pointer.position.x;
|
|
2375
|
+
this.#startValue = this.value();
|
|
2376
|
+
this.#dragging = false;
|
|
2377
|
+
this.#dragged = false;
|
|
2378
|
+
}
|
|
2379
|
+
onPointerMove(pointer) {
|
|
2380
|
+
const dx = pointer.position.x - this.#startX;
|
|
2381
|
+
if (!this.#dragging && Math.abs(dx) < DRAG_THRESHOLD) {
|
|
2382
|
+
return;
|
|
2038
2383
|
}
|
|
2039
|
-
this.#
|
|
2384
|
+
this.#dragging = true;
|
|
2385
|
+
this.input(this.#clean(this.#startValue + dx * this.#stepFor(this.#startValue)));
|
|
2040
2386
|
}
|
|
2041
|
-
|
|
2387
|
+
onPointerUp() {
|
|
2388
|
+
this.#dragged = this.#dragging;
|
|
2389
|
+
if (this.#dragging) {
|
|
2390
|
+
this.#dragging = false;
|
|
2391
|
+
this.commit(this.value(), this.#startValue);
|
|
2392
|
+
}
|
|
2042
2393
|
}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
const
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
},
|
|
2058
|
-
onClose: (button) => {
|
|
2059
|
-
if (button !== setOption || input === null) {
|
|
2060
|
-
return;
|
|
2394
|
+
onClick() {
|
|
2395
|
+
if (this.#dragged) {
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
const before = this.value();
|
|
2399
|
+
editInPlace({
|
|
2400
|
+
canvas: this.canvas(),
|
|
2401
|
+
box: this.box(),
|
|
2402
|
+
scale: this.scale(),
|
|
2403
|
+
value: String(before),
|
|
2404
|
+
onCommit: (text) => {
|
|
2405
|
+
const parsed = Number.parseFloat(text);
|
|
2406
|
+
if (Number.isFinite(parsed)) {
|
|
2407
|
+
this.commit(this.#clean(parsed), before);
|
|
2061
2408
|
}
|
|
2062
|
-
this.Set(input.valueAsNumber);
|
|
2063
2409
|
}
|
|
2064
2410
|
});
|
|
2065
|
-
popup.Show();
|
|
2066
2411
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
Size: {
|
|
2071
|
-
x: width * scale,
|
|
2072
|
-
y: height * scale
|
|
2073
|
-
}
|
|
2074
|
-
};
|
|
2075
|
-
CopyVector2(box.Position, position);
|
|
2076
|
-
let style = this.#idleBoxStyle;
|
|
2077
|
-
if (mousePosition !== void 0) {
|
|
2078
|
-
if (InBox(box, mousePosition)) {
|
|
2079
|
-
style = this.#highlightBoxStyle;
|
|
2080
|
-
}
|
|
2412
|
+
#stepFor(value) {
|
|
2413
|
+
if (this.#step !== void 0) {
|
|
2414
|
+
return this.#step;
|
|
2081
2415
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2416
|
+
return Math.max(Math.abs(value) * 0.01, this.#integer ? 0.1 : 0.01);
|
|
2417
|
+
}
|
|
2418
|
+
#clean(value) {
|
|
2419
|
+
const rounded = this.#integer ? Math.round(value) : value;
|
|
2420
|
+
return Math.min(this.#max, Math.max(this.#min, rounded));
|
|
2084
2421
|
}
|
|
2085
2422
|
};
|
|
2423
|
+
function formatNumber(value) {
|
|
2424
|
+
return "" + parseFloat(value.toPrecision(6));
|
|
2425
|
+
}
|
|
2086
2426
|
|
|
2087
2427
|
// src/widgets/button.ts
|
|
2088
|
-
var ButtonWidget = class {
|
|
2428
|
+
var ButtonWidget = class extends WidgetBase {
|
|
2089
2429
|
#text;
|
|
2090
|
-
#idleStyle;
|
|
2091
|
-
#hoverStyle;
|
|
2092
|
-
#clickStyle;
|
|
2093
2430
|
#callback;
|
|
2094
|
-
#
|
|
2431
|
+
#width;
|
|
2095
2432
|
constructor(config) {
|
|
2096
|
-
|
|
2433
|
+
super();
|
|
2434
|
+
this.#text = config?.text ?? "Button";
|
|
2097
2435
|
this.#callback = config?.callback;
|
|
2098
|
-
this.#
|
|
2099
|
-
this.#idleStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.idleStyle, {
|
|
2100
|
-
box: {
|
|
2101
|
-
color: Theme.Widget.BackgroundColor,
|
|
2102
|
-
border: {
|
|
2103
|
-
size: Theme.Widget.Border.Size,
|
|
2104
|
-
color: Theme.Widget.Border.Color
|
|
2105
|
-
}
|
|
2106
|
-
},
|
|
2107
|
-
text: { color: Theme.Widget.FontColor }
|
|
2108
|
-
}));
|
|
2109
|
-
this.#hoverStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.hoverStyle, {
|
|
2110
|
-
box: {
|
|
2111
|
-
color: Theme.Widget.Hover.BackgroundColor,
|
|
2112
|
-
border: {
|
|
2113
|
-
size: Theme.Widget.Border.Size,
|
|
2114
|
-
color: Theme.Widget.Border.Color
|
|
2115
|
-
}
|
|
2116
|
-
},
|
|
2117
|
-
text: { color: Theme.Widget.FontColor }
|
|
2118
|
-
}));
|
|
2119
|
-
this.#clickStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.clickStyle, {
|
|
2120
|
-
box: {
|
|
2121
|
-
color: Theme.Widget.Button.Click.BackgroundColor,
|
|
2122
|
-
border: {
|
|
2123
|
-
size: Theme.Widget.Border.Size,
|
|
2124
|
-
color: Theme.Widget.Border.Color
|
|
2125
|
-
}
|
|
2126
|
-
},
|
|
2127
|
-
text: { color: Theme.Widget.FontColor }
|
|
2128
|
-
}));
|
|
2436
|
+
this.#width = config?.width ?? width;
|
|
2129
2437
|
}
|
|
2130
2438
|
Size() {
|
|
2131
|
-
return {
|
|
2439
|
+
return { x: this.#width, y: height };
|
|
2132
2440
|
}
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
Position: { x: 0, y: 0 },
|
|
2136
|
-
Size: {
|
|
2137
|
-
x: width * scale,
|
|
2138
|
-
y: height * scale
|
|
2139
|
-
}
|
|
2140
|
-
};
|
|
2141
|
-
CopyVector2(box.Position, position);
|
|
2142
|
-
let style = this.#idleStyle;
|
|
2143
|
-
if (mousePosition !== void 0 && !this.#gettingClicked) {
|
|
2144
|
-
if (InBox(box, mousePosition)) {
|
|
2145
|
-
style = this.#hoverStyle;
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
if (this.#gettingClicked) {
|
|
2149
|
-
style = this.#clickStyle;
|
|
2150
|
-
}
|
|
2151
|
-
style.Draw(ctx, box, scale, this.#text);
|
|
2152
|
-
return box;
|
|
2441
|
+
text() {
|
|
2442
|
+
return this.#text;
|
|
2153
2443
|
}
|
|
2154
|
-
|
|
2155
|
-
this.#
|
|
2444
|
+
setText(text) {
|
|
2445
|
+
this.#text = text;
|
|
2446
|
+
requestRender();
|
|
2156
2447
|
}
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
this
|
|
2161
|
-
|
|
2448
|
+
render(ctx, box, scale) {
|
|
2449
|
+
drawField(ctx, box, scale, {
|
|
2450
|
+
hovered: this.hovered(),
|
|
2451
|
+
pressed: this.pressed(),
|
|
2452
|
+
value: this.#text
|
|
2453
|
+
});
|
|
2454
|
+
}
|
|
2455
|
+
onClick() {
|
|
2456
|
+
this.#callback?.();
|
|
2162
2457
|
}
|
|
2163
2458
|
};
|
|
2164
2459
|
|
|
@@ -2173,500 +2468,214 @@ function contrastColor(color) {
|
|
|
2173
2468
|
}
|
|
2174
2469
|
return "white";
|
|
2175
2470
|
}
|
|
2176
|
-
var
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2471
|
+
var picker = null;
|
|
2472
|
+
function sharedPicker() {
|
|
2473
|
+
if (picker === null || !picker.isConnected) {
|
|
2474
|
+
picker = document.createElement("input");
|
|
2475
|
+
picker.type = "color";
|
|
2476
|
+
document.body.appendChild(picker);
|
|
2477
|
+
}
|
|
2478
|
+
return picker;
|
|
2479
|
+
}
|
|
2480
|
+
var ColorWidget = class extends ValueWidget {
|
|
2180
2481
|
#contrast;
|
|
2181
|
-
#textBoxStyle;
|
|
2182
|
-
#callback;
|
|
2183
2482
|
constructor(node, config) {
|
|
2184
|
-
|
|
2185
|
-
this.#
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
this.#contrast = contrastColor(this
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2483
|
+
super(node, config, "#000000");
|
|
2484
|
+
this.#contrast = contrastColor(this.value());
|
|
2485
|
+
}
|
|
2486
|
+
valueChanged() {
|
|
2487
|
+
this.#contrast = contrastColor(this.value());
|
|
2488
|
+
}
|
|
2489
|
+
render(ctx, box, scale) {
|
|
2490
|
+
drawField(ctx, box, scale, {
|
|
2491
|
+
input: true,
|
|
2492
|
+
hovered: this.hovered(),
|
|
2493
|
+
label: this.label(),
|
|
2494
|
+
value: this.value(),
|
|
2495
|
+
background: this.value(),
|
|
2496
|
+
border: this.#contrast,
|
|
2497
|
+
textColor: this.#contrast
|
|
2198
2498
|
});
|
|
2199
|
-
if (this.#nodeProperty !== void 0) {
|
|
2200
|
-
this.#node.addPropertyChangeListener(this.#nodeProperty, (oldVal, newVal) => {
|
|
2201
|
-
this.Set(newVal);
|
|
2202
|
-
});
|
|
2203
|
-
}
|
|
2204
|
-
}
|
|
2205
|
-
Size() {
|
|
2206
|
-
return { "x": width, "y": height };
|
|
2207
2499
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2500
|
+
onClick() {
|
|
2501
|
+
const canvas = this.canvas();
|
|
2502
|
+
if (canvas === null) {
|
|
2210
2503
|
return;
|
|
2211
2504
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
input.value = this.#value;
|
|
2236
|
-
input.name = "color";
|
|
2237
|
-
container.append(input);
|
|
2238
|
-
const label = document.createElement("label");
|
|
2239
|
-
label.htmlFor = "color";
|
|
2240
|
-
label.textContent = "Color";
|
|
2241
|
-
container.append(label);
|
|
2242
|
-
return container;
|
|
2243
|
-
},
|
|
2244
|
-
onClose: (button) => {
|
|
2245
|
-
if (button !== "Set" || input === null) {
|
|
2246
|
-
return;
|
|
2247
|
-
}
|
|
2248
|
-
this.Set(input.value);
|
|
2249
|
-
}
|
|
2250
|
-
});
|
|
2251
|
-
popup.Show();
|
|
2252
|
-
}
|
|
2253
|
-
Draw(ctx, position, scale, mousePosition) {
|
|
2254
|
-
const box = {
|
|
2255
|
-
Position: { x: 0, y: 0 },
|
|
2256
|
-
Size: {
|
|
2257
|
-
x: width * scale,
|
|
2258
|
-
y: height * scale
|
|
2259
|
-
}
|
|
2260
|
-
};
|
|
2261
|
-
CopyVector2(box.Position, position);
|
|
2262
|
-
this.#textBoxStyle.Draw(ctx, box, scale, this.#value);
|
|
2263
|
-
return box;
|
|
2505
|
+
const before = this.value();
|
|
2506
|
+
const alpha = before.slice(7);
|
|
2507
|
+
const box = this.box();
|
|
2508
|
+
const rect = canvas.getBoundingClientRect();
|
|
2509
|
+
const sx = canvas.width > 0 ? rect.width / canvas.width : 1;
|
|
2510
|
+
const sy = canvas.height > 0 ? rect.height / canvas.height : 1;
|
|
2511
|
+
const input = sharedPicker();
|
|
2512
|
+
input.style.cssText = [
|
|
2513
|
+
"position: fixed",
|
|
2514
|
+
`left: ${rect.left + box.Position.x * sx}px`,
|
|
2515
|
+
`top: ${rect.top + box.Position.y * sy}px`,
|
|
2516
|
+
`width: ${box.Size.x * sx}px`,
|
|
2517
|
+
`height: ${box.Size.y * sy}px`,
|
|
2518
|
+
"opacity: 0",
|
|
2519
|
+
"pointer-events: none",
|
|
2520
|
+
"border: 0",
|
|
2521
|
+
"padding: 0"
|
|
2522
|
+
].join(";");
|
|
2523
|
+
input.value = before.slice(0, 7);
|
|
2524
|
+
input.oninput = () => this.input(input.value + alpha);
|
|
2525
|
+
input.onchange = () => this.commit(input.value + alpha, before);
|
|
2526
|
+
input.getBoundingClientRect();
|
|
2527
|
+
input.click();
|
|
2264
2528
|
}
|
|
2265
2529
|
};
|
|
2266
2530
|
|
|
2267
2531
|
// src/widgets/slider.ts
|
|
2268
|
-
var SliderWidget = class {
|
|
2269
|
-
// Data Variables =========================================================
|
|
2532
|
+
var SliderWidget = class extends ValueWidget {
|
|
2270
2533
|
#min;
|
|
2271
2534
|
#max;
|
|
2272
|
-
#value;
|
|
2273
|
-
#text;
|
|
2274
2535
|
#snapIncrement;
|
|
2275
|
-
#node;
|
|
2276
|
-
#nodeProperty;
|
|
2277
|
-
// Callbacks ==============================================================
|
|
2278
|
-
#change;
|
|
2279
|
-
#release;
|
|
2280
|
-
// Styling Variables ======================================================
|
|
2281
|
-
#textStyle;
|
|
2282
|
-
#backgroundColor;
|
|
2283
|
-
#borderColor;
|
|
2284
2536
|
#fillColor;
|
|
2285
|
-
|
|
2286
|
-
#lastMousePosition;
|
|
2287
|
-
#clickStartMousePosition;
|
|
2288
|
-
#clicking;
|
|
2537
|
+
#startValue = 0;
|
|
2289
2538
|
constructor(node, config) {
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
this.#
|
|
2539
|
+
const min = config?.min ?? 0;
|
|
2540
|
+
const max = config?.max ?? 1;
|
|
2541
|
+
super(node, { ...config, value: config?.value === void 0 ? void 0 : Clamp(config.value, min, max) }, min);
|
|
2542
|
+
this.#min = min;
|
|
2543
|
+
this.#max = max;
|
|
2294
2544
|
this.#snapIncrement = config?.snapIncrement;
|
|
2295
|
-
this.#
|
|
2296
|
-
this.#text = "";
|
|
2297
|
-
this.#release = config?.release;
|
|
2298
|
-
this.#backgroundColor = config?.backgroundColor === void 0 ? Theme.Widget.BackgroundColor : config?.backgroundColor;
|
|
2299
|
-
this.#fillColor = config?.fillColor === void 0 ? Theme.Widget.Slider.FillColor : config?.fillColor;
|
|
2300
|
-
this.#borderColor = config?.borderColor === void 0 ? Theme.Widget.Border.Color : config?.borderColor;
|
|
2301
|
-
this.#textStyle = new TextStyle(TextStyleFallback(config?.textStyle, {
|
|
2302
|
-
color: Theme.Widget.FontColor
|
|
2303
|
-
}));
|
|
2304
|
-
this.#lastMousePosition = Zero();
|
|
2305
|
-
this.#clickStartMousePosition = Zero();
|
|
2306
|
-
this.#clicking = false;
|
|
2307
|
-
if (config?.property !== void 0 && config?.property !== null) {
|
|
2308
|
-
node.addPropertyChangeListener(config.property, (oldVal, newVal) => {
|
|
2309
|
-
this.SetValue(newVal);
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2312
|
-
this.SetValue(startingWidgetValue(node, this.#nodeProperty, config?.value, 0));
|
|
2313
|
-
this.#change = config?.change;
|
|
2314
|
-
}
|
|
2315
|
-
SetValue(newValue) {
|
|
2316
|
-
const cleanedValue = Clamp(newValue, this.#min, this.#max);
|
|
2317
|
-
if (this.#value === cleanedValue) {
|
|
2318
|
-
return;
|
|
2319
|
-
}
|
|
2320
|
-
this.#value = cleanedValue;
|
|
2321
|
-
if (this.#nodeProperty) {
|
|
2322
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
2323
|
-
}
|
|
2324
|
-
this.#text = this.#value.toFixed(3);
|
|
2325
|
-
if (this.#change !== void 0) {
|
|
2326
|
-
this.#change(this.#value);
|
|
2327
|
-
}
|
|
2545
|
+
this.#fillColor = config?.fillColor;
|
|
2328
2546
|
}
|
|
2329
|
-
|
|
2330
|
-
return
|
|
2547
|
+
Cursor() {
|
|
2548
|
+
return "ew-resize" /* EWResize */;
|
|
2331
2549
|
}
|
|
2332
|
-
|
|
2333
|
-
this.#
|
|
2334
|
-
CopyVector2(this.#clickStartMousePosition, this.#lastMousePosition);
|
|
2550
|
+
setValue(value) {
|
|
2551
|
+
super.setValue(Clamp(value, this.#min, this.#max));
|
|
2335
2552
|
}
|
|
2336
|
-
|
|
2337
|
-
this
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
}
|
|
2342
|
-
Draw(ctx, position, scale, mousePosition) {
|
|
2343
|
-
const scaledWidth = width * scale;
|
|
2344
|
-
const scaledHeight = height * scale;
|
|
2345
|
-
const scaledBorderThickness = Theme.Widget.Border.Size * scale;
|
|
2346
|
-
ctx.fillStyle = this.#backgroundColor;
|
|
2347
|
-
ctx.strokeStyle = this.#borderColor;
|
|
2348
|
-
ctx.lineWidth = scaledBorderThickness;
|
|
2553
|
+
render(ctx, box, scale) {
|
|
2554
|
+
drawFieldBox(ctx, box, scale, { input: true, hovered: this.hovered() || this.pressed(), value: "" });
|
|
2555
|
+
const border = Theme.Widget.Border.Size * scale;
|
|
2556
|
+
const fill = Clamp01((this.value() - this.#min) / (this.#max - this.#min));
|
|
2557
|
+
ctx.fillStyle = this.#fillColor ?? Theme.Widget.Slider.FillColor;
|
|
2349
2558
|
ctx.beginPath();
|
|
2350
2559
|
ctx.roundRect(
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2560
|
+
box.Position.x + border / 2,
|
|
2561
|
+
box.Position.y + border / 2,
|
|
2562
|
+
Math.max(0, box.Size.x * fill - border),
|
|
2563
|
+
box.Size.y - border,
|
|
2355
2564
|
Theme.Widget.Border.Radius * scale
|
|
2356
2565
|
);
|
|
2357
2566
|
ctx.fill();
|
|
2358
|
-
ctx
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
);
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
let value = p * (this.#max - this.#min) + this.#min;
|
|
2385
|
-
if (this.#snapIncrement !== void 0) {
|
|
2386
|
-
value = Math.round(value / this.#snapIncrement) * this.#snapIncrement;
|
|
2387
|
-
}
|
|
2388
|
-
this.SetValue(value);
|
|
2389
|
-
}
|
|
2567
|
+
drawFieldText(ctx, box, scale, {
|
|
2568
|
+
input: true,
|
|
2569
|
+
hovered: false,
|
|
2570
|
+
label: this.label(),
|
|
2571
|
+
value: formatNumber(Number(this.value().toFixed(3)))
|
|
2572
|
+
});
|
|
2573
|
+
}
|
|
2574
|
+
onPointerDown(pointer) {
|
|
2575
|
+
this.#startValue = this.value();
|
|
2576
|
+
this.input(this.#valueAt(pointer.position.x));
|
|
2577
|
+
}
|
|
2578
|
+
onPointerMove(pointer) {
|
|
2579
|
+
this.input(this.#valueAt(pointer.position.x));
|
|
2580
|
+
}
|
|
2581
|
+
onPointerUp() {
|
|
2582
|
+
this.commit(this.value(), this.#startValue);
|
|
2583
|
+
}
|
|
2584
|
+
#valueAt(x) {
|
|
2585
|
+
const box = this.box();
|
|
2586
|
+
const border = Theme.Widget.Border.Size * this.scale();
|
|
2587
|
+
const min = box.Position.x + border / 2;
|
|
2588
|
+
const max = min + box.Size.x - border;
|
|
2589
|
+
const p = Clamp01((x - min) / (max - min));
|
|
2590
|
+
let value = p * (this.#max - this.#min) + this.#min;
|
|
2591
|
+
if (this.#snapIncrement !== void 0) {
|
|
2592
|
+
value = Math.round(value / this.#snapIncrement) * this.#snapIncrement;
|
|
2390
2593
|
}
|
|
2391
|
-
return
|
|
2392
|
-
Position: { x: position.x, y: position.y },
|
|
2393
|
-
Size: {
|
|
2394
|
-
x: scaledWidth,
|
|
2395
|
-
y: scaledHeight
|
|
2396
|
-
}
|
|
2397
|
-
};
|
|
2594
|
+
return Clamp(value, this.#min, this.#max);
|
|
2398
2595
|
}
|
|
2399
2596
|
};
|
|
2400
2597
|
|
|
2401
|
-
// src/popups/string.ts
|
|
2402
|
-
function SetStringPopup(config) {
|
|
2403
|
-
let input = null;
|
|
2404
|
-
return new Popup({
|
|
2405
|
-
title: config.title,
|
|
2406
|
-
options: ["Set", "Cancel"],
|
|
2407
|
-
content: () => {
|
|
2408
|
-
const container = document.createElement("div");
|
|
2409
|
-
input = document.createElement("input");
|
|
2410
|
-
input.value = config.startingValue;
|
|
2411
|
-
container.append(input);
|
|
2412
|
-
return container;
|
|
2413
|
-
},
|
|
2414
|
-
onClose: (button) => {
|
|
2415
|
-
if (button !== "Set" || input === null) {
|
|
2416
|
-
if (config.onCancel) {
|
|
2417
|
-
config.onCancel();
|
|
2418
|
-
}
|
|
2419
|
-
return;
|
|
2420
|
-
}
|
|
2421
|
-
config.onUpdate(input.value);
|
|
2422
|
-
}
|
|
2423
|
-
});
|
|
2424
|
-
}
|
|
2425
|
-
|
|
2426
2598
|
// src/widgets/string.ts
|
|
2427
|
-
var StringWidget = class {
|
|
2428
|
-
#value;
|
|
2429
|
-
#idleStyle;
|
|
2430
|
-
#hoverStyle;
|
|
2431
|
-
#callback;
|
|
2432
|
-
#node;
|
|
2433
|
-
#nodeProperty;
|
|
2599
|
+
var StringWidget = class extends ValueWidget {
|
|
2434
2600
|
constructor(node, config) {
|
|
2435
|
-
|
|
2436
|
-
this.#node = node;
|
|
2437
|
-
this.#nodeProperty = config?.property;
|
|
2438
|
-
this.#idleStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.textBoxStyle, {
|
|
2439
|
-
box: {
|
|
2440
|
-
color: Theme.Widget.BackgroundColor,
|
|
2441
|
-
border: {
|
|
2442
|
-
size: Theme.Widget.Border.Size,
|
|
2443
|
-
color: Theme.Widget.Border.Color
|
|
2444
|
-
},
|
|
2445
|
-
radius: Theme.Widget.Border.Radius
|
|
2446
|
-
},
|
|
2447
|
-
text: { color: Theme.Widget.FontColor }
|
|
2448
|
-
}));
|
|
2449
|
-
this.#hoverStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.textBoxStyle, {
|
|
2450
|
-
box: {
|
|
2451
|
-
color: Theme.Widget.Hover.BackgroundColor,
|
|
2452
|
-
border: {
|
|
2453
|
-
size: Theme.Widget.Border.Size,
|
|
2454
|
-
color: Theme.Widget.Border.Color
|
|
2455
|
-
},
|
|
2456
|
-
radius: Theme.Widget.Border.Radius
|
|
2457
|
-
},
|
|
2458
|
-
text: { color: Theme.Widget.FontColor }
|
|
2459
|
-
}));
|
|
2460
|
-
this.Set(startingWidgetValue(node, this.#nodeProperty, config?.value, ""));
|
|
2461
|
-
this.#callback = config?.callback;
|
|
2462
|
-
if (this.#nodeProperty !== void 0) {
|
|
2463
|
-
this.#node.addPropertyChangeListener(this.#nodeProperty, (oldVal, newVal) => {
|
|
2464
|
-
this.Set(newVal);
|
|
2465
|
-
});
|
|
2466
|
-
}
|
|
2601
|
+
super(node, config, "");
|
|
2467
2602
|
}
|
|
2468
|
-
|
|
2469
|
-
return
|
|
2470
|
-
}
|
|
2471
|
-
Set(value) {
|
|
2472
|
-
if (this.#value === value) {
|
|
2473
|
-
return;
|
|
2474
|
-
}
|
|
2475
|
-
this.#value = value;
|
|
2476
|
-
if (this.#nodeProperty !== void 0) {
|
|
2477
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
2478
|
-
}
|
|
2479
|
-
if (this.#callback !== void 0) {
|
|
2480
|
-
this.#callback(this.#value);
|
|
2481
|
-
}
|
|
2603
|
+
Cursor() {
|
|
2604
|
+
return "text" /* Text */;
|
|
2482
2605
|
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
onUpdate: (value) => {
|
|
2490
|
-
this.Set(value);
|
|
2491
|
-
}
|
|
2606
|
+
render(ctx, box, scale) {
|
|
2607
|
+
drawField(ctx, box, scale, {
|
|
2608
|
+
input: true,
|
|
2609
|
+
hovered: this.hovered(),
|
|
2610
|
+
label: this.label(),
|
|
2611
|
+
value: this.value()
|
|
2492
2612
|
});
|
|
2493
|
-
popup.Show();
|
|
2494
2613
|
}
|
|
2495
|
-
|
|
2496
|
-
const
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
let style = this.#idleStyle;
|
|
2505
|
-
if (mousePosition !== void 0) {
|
|
2506
|
-
if (InBox(box, mousePosition)) {
|
|
2507
|
-
style = this.#hoverStyle;
|
|
2508
|
-
}
|
|
2509
|
-
}
|
|
2510
|
-
style.DrawUnderline(ctx, box, scale, fitString(ctx, this.#value, box.Size.x - 20 * scale));
|
|
2511
|
-
return box;
|
|
2614
|
+
onClick() {
|
|
2615
|
+
const before = this.value();
|
|
2616
|
+
editInPlace({
|
|
2617
|
+
canvas: this.canvas(),
|
|
2618
|
+
box: this.box(),
|
|
2619
|
+
scale: this.scale(),
|
|
2620
|
+
value: before,
|
|
2621
|
+
onCommit: (text) => this.commit(text, before)
|
|
2622
|
+
});
|
|
2512
2623
|
}
|
|
2513
2624
|
};
|
|
2514
2625
|
|
|
2515
2626
|
// src/widgets/toggle.ts
|
|
2516
|
-
var
|
|
2517
|
-
#
|
|
2518
|
-
#
|
|
2519
|
-
#lightColor;
|
|
2520
|
-
#lightBorderColor;
|
|
2521
|
-
#lightBlur;
|
|
2522
|
-
#box;
|
|
2523
|
-
constructor(config) {
|
|
2524
|
-
this.#idleStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.idle, {
|
|
2525
|
-
box: {
|
|
2526
|
-
color: Theme.Widget.BackgroundColor,
|
|
2527
|
-
border: {
|
|
2528
|
-
size: Theme.Widget.Border.Size,
|
|
2529
|
-
color: Theme.Widget.Border.Color
|
|
2530
|
-
}
|
|
2531
|
-
},
|
|
2532
|
-
text: { color: Theme.Widget.FontColor }
|
|
2533
|
-
}));
|
|
2534
|
-
this.#hoverStyle = new TextBoxStyle(TextBoxStyleWithFallback(config?.hover, {
|
|
2535
|
-
box: {
|
|
2536
|
-
color: Theme.Widget.Hover.BackgroundColor,
|
|
2537
|
-
border: {
|
|
2538
|
-
size: Theme.Widget.Border.Size,
|
|
2539
|
-
color: Theme.Widget.Border.Color
|
|
2540
|
-
}
|
|
2541
|
-
},
|
|
2542
|
-
text: { color: Theme.Widget.FontColor }
|
|
2543
|
-
}));
|
|
2544
|
-
this.#lightColor = config?.lightColor === void 0 ? "#222222" : config.lightColor;
|
|
2545
|
-
this.#lightBorderColor = config?.lightBorderColor === void 0 ? "black" : config.lightBorderColor;
|
|
2546
|
-
this.#lightBlur = config?.lightBlur;
|
|
2547
|
-
this.#box = { Position: { x: 0, y: 0 }, Size: { x: 0, y: 0 } };
|
|
2548
|
-
}
|
|
2549
|
-
Draw(ctx, pos, scale, text, mousePosition) {
|
|
2550
|
-
const scaledWidth = width * scale;
|
|
2551
|
-
const scaledHeight = height * scale;
|
|
2552
|
-
this.#box.Size.x = scaledWidth;
|
|
2553
|
-
this.#box.Size.y = scaledHeight;
|
|
2554
|
-
CopyVector2(this.#box.Position, pos);
|
|
2555
|
-
let style = this.#idleStyle;
|
|
2556
|
-
if (mousePosition !== void 0) {
|
|
2557
|
-
if (InBox(this.#box, mousePosition)) {
|
|
2558
|
-
style = this.#hoverStyle;
|
|
2559
|
-
}
|
|
2560
|
-
}
|
|
2561
|
-
style.Draw(ctx, this.#box, scale, text);
|
|
2562
|
-
const lightScale = Math.min(scaledWidth, scaledHeight);
|
|
2563
|
-
ctx.strokeStyle = this.#lightBorderColor;
|
|
2564
|
-
ctx.fillStyle = this.#lightColor;
|
|
2565
|
-
ctx.beginPath();
|
|
2566
|
-
ctx.roundRect(
|
|
2567
|
-
pos.x + lightScale * 0.2,
|
|
2568
|
-
pos.y + lightScale * 0.2,
|
|
2569
|
-
lightScale * 0.6,
|
|
2570
|
-
lightScale * 0.6,
|
|
2571
|
-
Theme.Widget.Border.Radius * scale
|
|
2572
|
-
);
|
|
2573
|
-
if (this.#lightBlur) {
|
|
2574
|
-
ctx.shadowBlur = this.#lightBlur * scale;
|
|
2575
|
-
ctx.shadowColor = this.#lightColor;
|
|
2576
|
-
}
|
|
2577
|
-
ctx.fill();
|
|
2578
|
-
if (this.#lightBlur) {
|
|
2579
|
-
ctx.shadowBlur = 0;
|
|
2580
|
-
}
|
|
2581
|
-
return this.#box;
|
|
2582
|
-
}
|
|
2583
|
-
};
|
|
2584
|
-
var ToggleWidget = class {
|
|
2585
|
-
#node;
|
|
2586
|
-
#nodeProperty;
|
|
2587
|
-
#value;
|
|
2588
|
-
#text;
|
|
2589
|
-
#enabledStyle;
|
|
2590
|
-
#disabledStyle;
|
|
2591
|
-
#callback;
|
|
2627
|
+
var ToggleWidget = class extends ValueWidget {
|
|
2628
|
+
#onColor;
|
|
2629
|
+
#offColor;
|
|
2592
2630
|
constructor(node, config) {
|
|
2593
|
-
|
|
2594
|
-
this.#
|
|
2595
|
-
this.#
|
|
2596
|
-
this.#text = config?.text === void 0 ? "Toggle" : config?.text;
|
|
2597
|
-
this.#enabledStyle = new ToggleStyle({
|
|
2598
|
-
idle: TextBoxStyleWithFallback(config?.enabledStyle?.idle, {
|
|
2599
|
-
box: {
|
|
2600
|
-
color: Theme.Widget.BackgroundColor,
|
|
2601
|
-
border: {
|
|
2602
|
-
size: Theme.Widget.Border.Size,
|
|
2603
|
-
color: Theme.Widget.Border.Color
|
|
2604
|
-
}
|
|
2605
|
-
},
|
|
2606
|
-
text: { color: Theme.Widget.FontColor }
|
|
2607
|
-
}),
|
|
2608
|
-
lightBorderColor: config?.enabledStyle?.lightBorderColor,
|
|
2609
|
-
lightColor: config?.enabledStyle?.lightColor === void 0 ? "#00FF00" : config?.enabledStyle?.lightColor,
|
|
2610
|
-
lightBlur: config?.enabledStyle?.lightBlur === void 0 ? 15 : config?.enabledStyle?.lightBlur
|
|
2611
|
-
});
|
|
2612
|
-
this.#disabledStyle = new ToggleStyle({
|
|
2613
|
-
idle: TextBoxStyleWithFallback(config?.disabledStyle?.idle, {
|
|
2614
|
-
box: {
|
|
2615
|
-
color: Theme.Widget.BackgroundColor,
|
|
2616
|
-
border: {
|
|
2617
|
-
size: Theme.Widget.Border.Size,
|
|
2618
|
-
color: Theme.Widget.Border.Color
|
|
2619
|
-
}
|
|
2620
|
-
},
|
|
2621
|
-
text: { color: Theme.Widget.FontColor }
|
|
2622
|
-
}),
|
|
2623
|
-
lightBorderColor: config?.disabledStyle?.lightBorderColor,
|
|
2624
|
-
lightColor: config?.disabledStyle?.lightColor === void 0 ? "#004400" : config?.enabledStyle?.lightColor
|
|
2625
|
-
});
|
|
2626
|
-
this.Set(startingWidgetValue(node, this.#nodeProperty, config?.value, false));
|
|
2627
|
-
this.#callback = config?.callback;
|
|
2628
|
-
if (this.#nodeProperty !== void 0) {
|
|
2629
|
-
this.#node.addPropertyChangeListener(this.#nodeProperty, (oldVal, newVal) => {
|
|
2630
|
-
this.Set(newVal);
|
|
2631
|
-
});
|
|
2632
|
-
}
|
|
2633
|
-
}
|
|
2634
|
-
Size() {
|
|
2635
|
-
return { "x": width, "y": height };
|
|
2636
|
-
}
|
|
2637
|
-
Draw(ctx, position, scale, mousePosition) {
|
|
2638
|
-
let style = this.#value ? this.#enabledStyle : this.#disabledStyle;
|
|
2639
|
-
return style.Draw(ctx, position, scale, this.#text, mousePosition);
|
|
2631
|
+
super(node, { ...config, label: config?.label ?? "Toggle" }, false);
|
|
2632
|
+
this.#onColor = config?.onColor ?? "#00FF00";
|
|
2633
|
+
this.#offColor = config?.offColor ?? "#004400";
|
|
2640
2634
|
}
|
|
2641
2635
|
Toggle() {
|
|
2642
|
-
this.
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2636
|
+
const before = this.value();
|
|
2637
|
+
this.commit(!before, before);
|
|
2638
|
+
}
|
|
2639
|
+
render(ctx, box, scale) {
|
|
2640
|
+
drawFieldBox(ctx, box, scale, { hovered: this.hovered(), value: "" });
|
|
2641
|
+
const light = Math.min(box.Size.x, box.Size.y) * 0.6;
|
|
2642
|
+
const inset = (box.Size.y - light) / 2;
|
|
2643
|
+
const on = this.value();
|
|
2644
|
+
ctx.fillStyle = on ? this.#onColor : this.#offColor;
|
|
2645
|
+
ctx.beginPath();
|
|
2646
|
+
ctx.roundRect(box.Position.x + inset, box.Position.y + inset, light, light, Theme.Widget.Border.Radius * scale);
|
|
2647
|
+
if (on) {
|
|
2648
|
+
ctx.shadowBlur = 15 * scale;
|
|
2649
|
+
ctx.shadowColor = this.#onColor;
|
|
2654
2650
|
}
|
|
2651
|
+
ctx.fill();
|
|
2652
|
+
ctx.shadowBlur = 0;
|
|
2653
|
+
const textLeft = box.Position.x + inset * 2 + light;
|
|
2654
|
+
ctx.font = widgetFont(scale);
|
|
2655
|
+
ctx.textAlign = "center";
|
|
2656
|
+
ctx.textBaseline = "middle";
|
|
2657
|
+
ctx.fillStyle = Theme.Widget.FontColor;
|
|
2658
|
+
const room = box.Position.x + box.Size.x - textLeft - inset;
|
|
2659
|
+
ctx.fillText(
|
|
2660
|
+
fitString(ctx, this.label(), room),
|
|
2661
|
+
textLeft + room / 2,
|
|
2662
|
+
box.Position.y + box.Size.y / 2
|
|
2663
|
+
);
|
|
2655
2664
|
}
|
|
2656
|
-
|
|
2665
|
+
onClick() {
|
|
2657
2666
|
this.Toggle();
|
|
2658
2667
|
}
|
|
2659
|
-
ClickEnd() {
|
|
2660
|
-
}
|
|
2661
2668
|
};
|
|
2662
2669
|
|
|
2663
2670
|
// src/widgets/image.ts
|
|
2664
|
-
var ImageWidget = class {
|
|
2671
|
+
var ImageWidget = class extends WidgetBase {
|
|
2665
2672
|
#url;
|
|
2673
|
+
#ownedUrl;
|
|
2666
2674
|
#image;
|
|
2667
2675
|
#maxWidth;
|
|
2668
2676
|
#maxHeight;
|
|
2669
2677
|
constructor(config) {
|
|
2678
|
+
super();
|
|
2670
2679
|
this.#maxWidth = config?.maxWidth === void 0 ? 150 : config?.maxWidth;
|
|
2671
2680
|
this.#maxHeight = config?.maxHeight === void 0 ? 150 : config?.maxHeight;
|
|
2672
2681
|
if (config?.image) {
|
|
@@ -2676,16 +2685,23 @@ var ImageWidget = class {
|
|
|
2676
2685
|
}
|
|
2677
2686
|
}
|
|
2678
2687
|
SetBlob(blob) {
|
|
2679
|
-
const
|
|
2680
|
-
|
|
2681
|
-
this
|
|
2688
|
+
const url = URL.createObjectURL(blob);
|
|
2689
|
+
this.SetUrl(url);
|
|
2690
|
+
this.#ownedUrl = url;
|
|
2682
2691
|
}
|
|
2683
2692
|
SetUrl(url) {
|
|
2693
|
+
if (this.#ownedUrl !== void 0) {
|
|
2694
|
+
URL.revokeObjectURL(this.#ownedUrl);
|
|
2695
|
+
this.#ownedUrl = void 0;
|
|
2696
|
+
}
|
|
2684
2697
|
this.#image = void 0;
|
|
2685
2698
|
this.#url = url;
|
|
2686
2699
|
const img = document.createElement("img");
|
|
2687
2700
|
img.src = url;
|
|
2688
2701
|
img.onload = () => {
|
|
2702
|
+
if (this.#url !== url) {
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2689
2705
|
this.#image = img;
|
|
2690
2706
|
requestRender();
|
|
2691
2707
|
};
|
|
@@ -2693,6 +2709,9 @@ var ImageWidget = class {
|
|
|
2693
2709
|
console.log("error loading image:", url, event);
|
|
2694
2710
|
};
|
|
2695
2711
|
}
|
|
2712
|
+
Cursor() {
|
|
2713
|
+
return "default" /* Default */;
|
|
2714
|
+
}
|
|
2696
2715
|
Size() {
|
|
2697
2716
|
if (this.#image === void 0) {
|
|
2698
2717
|
return { "x": 0, "y": 0 };
|
|
@@ -2712,88 +2731,178 @@ var ImageWidget = class {
|
|
|
2712
2731
|
"y": adjust * this.#image.height
|
|
2713
2732
|
};
|
|
2714
2733
|
}
|
|
2715
|
-
|
|
2716
|
-
}
|
|
2717
|
-
ClickEnd() {
|
|
2718
|
-
}
|
|
2719
|
-
Draw(ctx, position, scale, mousePosition) {
|
|
2720
|
-
const size = this.Size();
|
|
2721
|
-
const box = {
|
|
2722
|
-
Position: { x: 0, y: 0 },
|
|
2723
|
-
Size: {
|
|
2724
|
-
x: size.x * scale,
|
|
2725
|
-
y: size.y * scale
|
|
2726
|
-
}
|
|
2727
|
-
};
|
|
2728
|
-
CopyVector2(box.Position, position);
|
|
2734
|
+
render(ctx, box, scale) {
|
|
2729
2735
|
if (!this.#image) {
|
|
2730
|
-
return
|
|
2736
|
+
return;
|
|
2731
2737
|
}
|
|
2738
|
+
const size = this.Size();
|
|
2739
|
+
const w = size.x * scale;
|
|
2740
|
+
const h = size.y * scale;
|
|
2732
2741
|
ctx.drawImage(
|
|
2733
2742
|
this.#image,
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2743
|
+
box.Position.x + (box.Size.x - w) / 2,
|
|
2744
|
+
box.Position.y,
|
|
2745
|
+
w,
|
|
2746
|
+
h
|
|
2738
2747
|
);
|
|
2739
|
-
return box;
|
|
2740
2748
|
}
|
|
2741
2749
|
};
|
|
2742
2750
|
|
|
2743
2751
|
// src/widgets/text.ts
|
|
2744
|
-
var TextWidget = class {
|
|
2752
|
+
var TextWidget = class extends WidgetBase {
|
|
2745
2753
|
#value;
|
|
2746
|
-
#callback;
|
|
2747
|
-
#node;
|
|
2748
|
-
#nodeProperty;
|
|
2749
2754
|
#size;
|
|
2755
|
+
#styleConfig;
|
|
2756
|
+
#styledForTheme = -1;
|
|
2757
|
+
#followTheme() {
|
|
2758
|
+
if (this.#styledForTheme === themeVersion()) {
|
|
2759
|
+
return;
|
|
2760
|
+
}
|
|
2761
|
+
this.#styledForTheme = themeVersion();
|
|
2762
|
+
const style = this.#value.style();
|
|
2763
|
+
style.setColor(this.#styleConfig?.color ?? Theme.Widget.FontColor);
|
|
2764
|
+
style.setSize(this.#styleConfig?.size ?? Theme.Note.FontSize);
|
|
2765
|
+
}
|
|
2750
2766
|
constructor(node, config) {
|
|
2767
|
+
super();
|
|
2751
2768
|
this.#size = Zero();
|
|
2752
|
-
this.#
|
|
2753
|
-
this.#
|
|
2754
|
-
this.#
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
this.Set(newVal);
|
|
2764
|
-
});
|
|
2769
|
+
this.#styleConfig = config?.textBoxStyle;
|
|
2770
|
+
this.#value = new Text("", TextStyleFallback(config?.textBoxStyle, {}));
|
|
2771
|
+
this.#followTheme();
|
|
2772
|
+
this.setText(config?.value ?? "");
|
|
2773
|
+
const property = config?.property;
|
|
2774
|
+
if (property !== void 0) {
|
|
2775
|
+
const existing = node.getProperty(property);
|
|
2776
|
+
if (existing !== void 0 && existing !== null) {
|
|
2777
|
+
this.setText(String(existing));
|
|
2778
|
+
}
|
|
2779
|
+
node.addPropertyChangeListener(property, (_, next) => this.setText(String(next)));
|
|
2765
2780
|
}
|
|
2766
2781
|
}
|
|
2782
|
+
Cursor() {
|
|
2783
|
+
return "default" /* Default */;
|
|
2784
|
+
}
|
|
2767
2785
|
Size() {
|
|
2768
2786
|
return this.#size;
|
|
2769
2787
|
}
|
|
2770
|
-
|
|
2788
|
+
text() {
|
|
2789
|
+
return this.#value.get();
|
|
2790
|
+
}
|
|
2791
|
+
setText(value) {
|
|
2771
2792
|
if (this.#value.get() === value) {
|
|
2772
2793
|
return;
|
|
2773
2794
|
}
|
|
2774
2795
|
this.#value.set(value);
|
|
2775
|
-
|
|
2776
|
-
|
|
2796
|
+
requestRender();
|
|
2797
|
+
}
|
|
2798
|
+
render(ctx, box, scale) {
|
|
2799
|
+
this.#followTheme();
|
|
2800
|
+
this.#value.size(ctx, 1, this.#size);
|
|
2801
|
+
ctx.textAlign = "left";
|
|
2802
|
+
this.#value.render(ctx, scale, box.Position);
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
|
|
2806
|
+
// src/widgets/row.ts
|
|
2807
|
+
var RowWidget = class extends WidgetBase {
|
|
2808
|
+
#children;
|
|
2809
|
+
#grow;
|
|
2810
|
+
#gap;
|
|
2811
|
+
#boxes = [];
|
|
2812
|
+
#captured = null;
|
|
2813
|
+
#capturedBox = null;
|
|
2814
|
+
#mouse;
|
|
2815
|
+
constructor(children, config) {
|
|
2816
|
+
super();
|
|
2817
|
+
this.#children = children;
|
|
2818
|
+
this.#grow = config?.grow ?? children.map(() => 1);
|
|
2819
|
+
this.#gap = config?.gap ?? Theme.Widget.Gap;
|
|
2820
|
+
}
|
|
2821
|
+
children() {
|
|
2822
|
+
return this.#children;
|
|
2823
|
+
}
|
|
2824
|
+
Size() {
|
|
2825
|
+
const shown = this.#shown();
|
|
2826
|
+
let x = 0;
|
|
2827
|
+
let y = 0;
|
|
2828
|
+
for (const child of shown) {
|
|
2829
|
+
const size = child.Size();
|
|
2830
|
+
x += size.x;
|
|
2831
|
+
y = Math.max(y, size.y);
|
|
2832
|
+
}
|
|
2833
|
+
x += Math.max(0, shown.length - 1) * this.#gap;
|
|
2834
|
+
return { x, y };
|
|
2835
|
+
}
|
|
2836
|
+
Cursor() {
|
|
2837
|
+
const over = this.#captured ?? this.#childAt(this.#mouse);
|
|
2838
|
+
return over?.Cursor?.() ?? "default" /* Default */;
|
|
2839
|
+
}
|
|
2840
|
+
Draw(ctx, box, scale, mousePosition) {
|
|
2841
|
+
this.#mouse = mousePosition;
|
|
2842
|
+
super.Draw(ctx, box, scale, mousePosition);
|
|
2843
|
+
}
|
|
2844
|
+
render(ctx, box, scale) {
|
|
2845
|
+
const shown = this.#shown();
|
|
2846
|
+
const gap = this.#gap * scale;
|
|
2847
|
+
const natural = shown.reduce((sum, child) => sum + child.Size().x * scale, 0);
|
|
2848
|
+
const extra = Math.max(0, box.Size.x - natural - gap * Math.max(0, shown.length - 1));
|
|
2849
|
+
const totalGrow = shown.reduce((sum, child) => sum + this.#growOf(child), 0);
|
|
2850
|
+
this.#boxes = this.#children.map(() => null);
|
|
2851
|
+
let x = box.Position.x;
|
|
2852
|
+
for (const child of shown) {
|
|
2853
|
+
const size = child.Size();
|
|
2854
|
+
const share = totalGrow > 0 ? extra * (this.#growOf(child) / totalGrow) : 0;
|
|
2855
|
+
const childBox = {
|
|
2856
|
+
Position: { x, y: box.Position.y + (box.Size.y - size.y * scale) / 2 },
|
|
2857
|
+
Size: { x: size.x * scale + share, y: size.y * scale }
|
|
2858
|
+
};
|
|
2859
|
+
this.#boxes[this.#children.indexOf(child)] = childBox;
|
|
2860
|
+
child.Draw(ctx, childBox, scale, this.#captured === null || this.#captured === child ? this.#mouse : void 0);
|
|
2861
|
+
x += childBox.Size.x + gap;
|
|
2777
2862
|
}
|
|
2778
|
-
|
|
2779
|
-
|
|
2863
|
+
}
|
|
2864
|
+
onPointerDown(pointer) {
|
|
2865
|
+
const child = this.#childAt(pointer.position);
|
|
2866
|
+
if (child === null || child.Disabled?.()) {
|
|
2867
|
+
return;
|
|
2780
2868
|
}
|
|
2869
|
+
this.#captured = child;
|
|
2870
|
+
this.#capturedBox = this.#boxes[this.#children.indexOf(child)];
|
|
2871
|
+
child.PointerDown?.(this.#forChild(pointer));
|
|
2781
2872
|
}
|
|
2782
|
-
|
|
2873
|
+
onPointerMove(pointer) {
|
|
2874
|
+
this.#captured?.PointerMove?.(this.#forChild(pointer));
|
|
2783
2875
|
}
|
|
2784
|
-
|
|
2876
|
+
onPointerUp(pointer) {
|
|
2877
|
+
const child = this.#captured;
|
|
2878
|
+
const forChild = this.#forChild(pointer);
|
|
2879
|
+
this.#captured = null;
|
|
2880
|
+
this.#capturedBox = null;
|
|
2881
|
+
child?.PointerUp?.(forChild);
|
|
2785
2882
|
}
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
Size: this.#size
|
|
2883
|
+
#forChild(pointer) {
|
|
2884
|
+
return {
|
|
2885
|
+
position: pointer.position,
|
|
2886
|
+
inside: this.#capturedBox !== null && InBox(this.#capturedBox, pointer.position)
|
|
2791
2887
|
};
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2888
|
+
}
|
|
2889
|
+
#childAt(position) {
|
|
2890
|
+
if (position === void 0) {
|
|
2891
|
+
return null;
|
|
2892
|
+
}
|
|
2893
|
+
for (let i = 0; i < this.#children.length; i++) {
|
|
2894
|
+
const box = this.#boxes[i];
|
|
2895
|
+
if (box && InBox(box, position)) {
|
|
2896
|
+
return this.#children[i];
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
return null;
|
|
2900
|
+
}
|
|
2901
|
+
#shown() {
|
|
2902
|
+
return this.#children.filter((child) => !child.Hidden?.());
|
|
2903
|
+
}
|
|
2904
|
+
#growOf(child) {
|
|
2905
|
+
return this.#grow[this.#children.indexOf(child)] ?? 1;
|
|
2797
2906
|
}
|
|
2798
2907
|
};
|
|
2799
2908
|
|
|
@@ -2813,6 +2922,9 @@ var WidgetFactory = class {
|
|
|
2813
2922
|
}
|
|
2814
2923
|
return builder(node, config);
|
|
2815
2924
|
}
|
|
2925
|
+
createFromConfig(node, config) {
|
|
2926
|
+
return this.create(node, config.type, config.config);
|
|
2927
|
+
}
|
|
2816
2928
|
};
|
|
2817
2929
|
var GlobalWidgetFactory = new WidgetFactory();
|
|
2818
2930
|
GlobalWidgetFactory.register("button", (node, config) => new ButtonWidget(config));
|
|
@@ -2823,6 +2935,10 @@ GlobalWidgetFactory.register("string", (node, config) => new StringWidget(node,
|
|
|
2823
2935
|
GlobalWidgetFactory.register("toggle", (node, config) => new ToggleWidget(node, config));
|
|
2824
2936
|
GlobalWidgetFactory.register("image", (node, config) => new ImageWidget(config));
|
|
2825
2937
|
GlobalWidgetFactory.register("text", (node, config) => new TextWidget(node, config));
|
|
2938
|
+
GlobalWidgetFactory.register("row", (node, config) => new RowWidget(
|
|
2939
|
+
(config?.widgets ?? []).map((child) => GlobalWidgetFactory.createFromConfig(node, child)),
|
|
2940
|
+
config
|
|
2941
|
+
));
|
|
2826
2942
|
|
|
2827
2943
|
// src/connection.ts
|
|
2828
2944
|
var BACKWARD_PORT_STUB = 30;
|
|
@@ -2907,7 +3023,7 @@ var Connection = class {
|
|
|
2907
3023
|
return;
|
|
2908
3024
|
}
|
|
2909
3025
|
if (this.#inNode !== null) {
|
|
2910
|
-
const inPortBox = this.#
|
|
3026
|
+
const inPortBox = this.#inSlotBox();
|
|
2911
3027
|
if (inPortBox === void 0) {
|
|
2912
3028
|
return;
|
|
2913
3029
|
}
|
|
@@ -2966,14 +3082,14 @@ var Connection = class {
|
|
|
2966
3082
|
this.clearInput();
|
|
2967
3083
|
this.clearOutput();
|
|
2968
3084
|
}
|
|
2969
|
-
setInput(node, portIndex, replace) {
|
|
3085
|
+
setInput(node, portIndex, replace, insertAt) {
|
|
2970
3086
|
this.#inNode = node;
|
|
2971
3087
|
this.#inNodePortIndex = portIndex;
|
|
2972
3088
|
const port = this.#inNode.inputPort(portIndex);
|
|
2973
3089
|
if (!!replace && port.connections().length > 0) {
|
|
2974
3090
|
port.replaceConnection(this, 0);
|
|
2975
3091
|
} else {
|
|
2976
|
-
port.addConnection(this);
|
|
3092
|
+
port.addConnection(this, insertAt);
|
|
2977
3093
|
}
|
|
2978
3094
|
}
|
|
2979
3095
|
setOutput(node, portIndex) {
|
|
@@ -2991,9 +3107,33 @@ var Connection = class {
|
|
|
2991
3107
|
this.#outNode = null;
|
|
2992
3108
|
this.#outNodePortIndex = -1;
|
|
2993
3109
|
}
|
|
3110
|
+
/**
|
|
3111
|
+
* The part of the input port this wire lands on: the whole port, or its
|
|
3112
|
+
* own slot on a port holding several wires.
|
|
3113
|
+
*/
|
|
3114
|
+
#inSlotBox() {
|
|
3115
|
+
if (this.#inNode === null) {
|
|
3116
|
+
return void 0;
|
|
3117
|
+
}
|
|
3118
|
+
const box = this.#inNode.inputPortPosition(this.#inNodePortIndex);
|
|
3119
|
+
const port = this.#inNode.inputPort(this.#inNodePortIndex);
|
|
3120
|
+
const slots = port.slots();
|
|
3121
|
+
if (box === void 0 || slots === 1) {
|
|
3122
|
+
return box;
|
|
3123
|
+
}
|
|
3124
|
+
const index = port.connections().indexOf(this);
|
|
3125
|
+
if (index === -1) {
|
|
3126
|
+
return box;
|
|
3127
|
+
}
|
|
3128
|
+
const height2 = box.Size.y / slots;
|
|
3129
|
+
return {
|
|
3130
|
+
Position: { x: box.Position.x, y: box.Position.y + index * height2 },
|
|
3131
|
+
Size: { x: box.Size.x, y: height2 }
|
|
3132
|
+
};
|
|
3133
|
+
}
|
|
2994
3134
|
mouseOverPort(mousePosition) {
|
|
2995
3135
|
if (this.#inNode !== null) {
|
|
2996
|
-
const inPortBox = this.#
|
|
3136
|
+
const inPortBox = this.#inSlotBox();
|
|
2997
3137
|
if (inPortBox !== void 0 && InBox(inPortBox, mousePosition)) {
|
|
2998
3138
|
return this.#inNode.inputPort(this.#inNodePortIndex);
|
|
2999
3139
|
}
|
|
@@ -3070,7 +3210,10 @@ var Camera = class {
|
|
|
3070
3210
|
// src/organize.ts
|
|
3071
3211
|
var widthSpacing = 100;
|
|
3072
3212
|
var heightSpacing = 50;
|
|
3213
|
+
var laneSpacing = 25;
|
|
3214
|
+
var pieceSpacing = 150;
|
|
3073
3215
|
var orderingSweeps = 8;
|
|
3216
|
+
var placementSweeps = 8;
|
|
3074
3217
|
function topologicalOrder(sorting, upstream) {
|
|
3075
3218
|
const visited = /* @__PURE__ */ new Set();
|
|
3076
3219
|
const order = new Array();
|
|
@@ -3092,11 +3235,7 @@ function topologicalOrder(sorting, upstream) {
|
|
|
3092
3235
|
continue;
|
|
3093
3236
|
}
|
|
3094
3237
|
visited.add(top.node);
|
|
3095
|
-
const
|
|
3096
|
-
if (feeders === void 0) {
|
|
3097
|
-
continue;
|
|
3098
|
-
}
|
|
3099
|
-
for (const feeder of feeders) {
|
|
3238
|
+
for (const feeder of upstream.get(top.node) ?? []) {
|
|
3100
3239
|
if (!visited.has(feeder)) {
|
|
3101
3240
|
stack.push({ node: feeder, expanded: false });
|
|
3102
3241
|
}
|
|
@@ -3105,43 +3244,103 @@ function topologicalOrder(sorting, upstream) {
|
|
|
3105
3244
|
}
|
|
3106
3245
|
return order;
|
|
3107
3246
|
}
|
|
3247
|
+
function consumersOf(sorting, upstream) {
|
|
3248
|
+
const downstream = /* @__PURE__ */ new Map();
|
|
3249
|
+
for (const node of sorting) {
|
|
3250
|
+
downstream.set(node, []);
|
|
3251
|
+
}
|
|
3252
|
+
for (const node of sorting) {
|
|
3253
|
+
for (const feeder of upstream.get(node) ?? []) {
|
|
3254
|
+
const consumers = downstream.get(feeder);
|
|
3255
|
+
if (consumers !== void 0 && !consumers.includes(node)) {
|
|
3256
|
+
consumers.push(node);
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
return downstream;
|
|
3261
|
+
}
|
|
3108
3262
|
function assignLayers(sorting, upstream) {
|
|
3109
|
-
const
|
|
3263
|
+
const downstream = consumersOf(sorting, upstream);
|
|
3264
|
+
const order = topologicalOrder(sorting, downstream);
|
|
3110
3265
|
const placed = /* @__PURE__ */ new Set();
|
|
3111
|
-
const
|
|
3266
|
+
const rank = /* @__PURE__ */ new Map();
|
|
3112
3267
|
for (const node of order) {
|
|
3113
|
-
let
|
|
3114
|
-
for (const
|
|
3115
|
-
if (placed.has(
|
|
3116
|
-
|
|
3268
|
+
let r = 0;
|
|
3269
|
+
for (const consumer of downstream.get(node) ?? []) {
|
|
3270
|
+
if (placed.has(consumer)) {
|
|
3271
|
+
r = Math.max(r, rank.get(consumer) + 1);
|
|
3117
3272
|
}
|
|
3118
3273
|
}
|
|
3119
|
-
|
|
3274
|
+
rank.set(node, r);
|
|
3120
3275
|
placed.add(node);
|
|
3121
3276
|
}
|
|
3122
3277
|
let deepest = 0;
|
|
3123
|
-
for (const
|
|
3124
|
-
deepest = Math.max(deepest,
|
|
3278
|
+
for (const r of rank.values()) {
|
|
3279
|
+
deepest = Math.max(deepest, r);
|
|
3125
3280
|
}
|
|
3126
|
-
const
|
|
3127
|
-
for (
|
|
3128
|
-
|
|
3281
|
+
const layerOf = /* @__PURE__ */ new Map();
|
|
3282
|
+
for (const node of sorting) {
|
|
3283
|
+
layerOf.set(node, deepest - rank.get(node));
|
|
3129
3284
|
}
|
|
3130
|
-
for (const node of
|
|
3131
|
-
|
|
3285
|
+
for (const node of sorting) {
|
|
3286
|
+
const layer = layerOf.get(node);
|
|
3287
|
+
if ((downstream.get(node) ?? []).some((c) => layerOf.get(c) > layer)) {
|
|
3288
|
+
continue;
|
|
3289
|
+
}
|
|
3290
|
+
let pulled = -1;
|
|
3291
|
+
for (const feeder of upstream.get(node) ?? []) {
|
|
3292
|
+
const feederLayer = layerOf.get(feeder);
|
|
3293
|
+
if (feederLayer !== void 0 && feederLayer < layer) {
|
|
3294
|
+
pulled = Math.max(pulled, feederLayer + 1);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
if (pulled >= 0) {
|
|
3298
|
+
layerOf.set(node, pulled);
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
const used = [...new Set(layerOf.values())].sort((a, b) => a - b);
|
|
3302
|
+
const compact = /* @__PURE__ */ new Map();
|
|
3303
|
+
used.forEach((layer, i) => compact.set(layer, i));
|
|
3304
|
+
const layers = used.map(() => new Array());
|
|
3305
|
+
for (const node of sorting) {
|
|
3306
|
+
const layer = compact.get(layerOf.get(node));
|
|
3307
|
+
layerOf.set(node, layer);
|
|
3308
|
+
layers[layer].push(node);
|
|
3132
3309
|
}
|
|
3133
3310
|
return { layers, layerOf };
|
|
3134
3311
|
}
|
|
3135
|
-
function
|
|
3312
|
+
function crossingsOf(layout, downstream) {
|
|
3136
3313
|
const slotOf = /* @__PURE__ */ new Map();
|
|
3137
|
-
const
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3314
|
+
for (const layer of layout.layers) {
|
|
3315
|
+
layer.forEach((node, slot) => slotOf.set(node, slot));
|
|
3316
|
+
}
|
|
3317
|
+
let total = 0;
|
|
3318
|
+
for (let i = 0; i + 1 < layout.layers.length; i++) {
|
|
3319
|
+
const spans = new Array();
|
|
3320
|
+
for (const node of layout.layers[i]) {
|
|
3321
|
+
for (const consumer of downstream.get(node) ?? []) {
|
|
3322
|
+
if (layout.layerOf.get(consumer) === i + 1) {
|
|
3323
|
+
spans.push([slotOf.get(node), slotOf.get(consumer)]);
|
|
3324
|
+
}
|
|
3141
3325
|
}
|
|
3142
3326
|
}
|
|
3143
|
-
|
|
3144
|
-
|
|
3327
|
+
for (let a = 0; a < spans.length; a++) {
|
|
3328
|
+
for (let b = a + 1; b < spans.length; b++) {
|
|
3329
|
+
if ((spans[a][0] - spans[b][0]) * (spans[a][1] - spans[b][1]) < 0) {
|
|
3330
|
+
total++;
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
return total;
|
|
3336
|
+
}
|
|
3337
|
+
function orderWithinLayers(layout, upstream, downstream) {
|
|
3338
|
+
const slotOf = /* @__PURE__ */ new Map();
|
|
3339
|
+
for (const layer of layout.layers) {
|
|
3340
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3341
|
+
slotOf.set(layer[i], i);
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3145
3344
|
const meanSlot = (node, neighbours, layer) => {
|
|
3146
3345
|
let total = 0;
|
|
3147
3346
|
let count = 0;
|
|
@@ -3152,31 +3351,386 @@ function orderWithinLayers(layout, upstream, downstream) {
|
|
|
3152
3351
|
total += slotOf.get(neighbour);
|
|
3153
3352
|
count++;
|
|
3154
3353
|
}
|
|
3155
|
-
return count === 0 ? -1 : total / count;
|
|
3354
|
+
return count === 0 ? -1 : total / count;
|
|
3355
|
+
};
|
|
3356
|
+
const sweep = (neighbours, towards) => {
|
|
3357
|
+
for (let i = 0; i < layout.layers.length; i++) {
|
|
3358
|
+
const index = towards > 0 ? i : layout.layers.length - 1 - i;
|
|
3359
|
+
const adjacent = index + towards;
|
|
3360
|
+
if (adjacent < 0 || adjacent >= layout.layers.length) {
|
|
3361
|
+
continue;
|
|
3362
|
+
}
|
|
3363
|
+
const layer = layout.layers[index];
|
|
3364
|
+
const keys = /* @__PURE__ */ new Map();
|
|
3365
|
+
for (const node of layer) {
|
|
3366
|
+
const mean = meanSlot(node, neighbours, adjacent);
|
|
3367
|
+
keys.set(node, mean === -1 ? slotOf.get(node) : mean);
|
|
3368
|
+
}
|
|
3369
|
+
layer.sort((a, b) => keys.get(a) - keys.get(b));
|
|
3370
|
+
for (let s = 0; s < layer.length; s++) {
|
|
3371
|
+
slotOf.set(layer[s], s);
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
};
|
|
3375
|
+
let best = layout.layers.map((layer) => layer.slice());
|
|
3376
|
+
let fewest = crossingsOf(layout, downstream);
|
|
3377
|
+
for (let i = 0; i < orderingSweeps && fewest > 0; i++) {
|
|
3378
|
+
sweep(upstream, -1);
|
|
3379
|
+
sweep(downstream, 1);
|
|
3380
|
+
const crossings = crossingsOf(layout, downstream);
|
|
3381
|
+
if (crossings < fewest) {
|
|
3382
|
+
fewest = crossings;
|
|
3383
|
+
best = layout.layers.map((layer) => layer.slice());
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
for (let i = 0; i < best.length; i++) {
|
|
3387
|
+
layout.layers[i] = best[i];
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
function findPieces(sorting, upstream, downstream) {
|
|
3391
|
+
const rankOf = /* @__PURE__ */ new Map();
|
|
3392
|
+
sorting.forEach((node, i) => rankOf.set(node, i));
|
|
3393
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3394
|
+
const pieces = new Array();
|
|
3395
|
+
for (const start of sorting) {
|
|
3396
|
+
if (seen.has(start)) {
|
|
3397
|
+
continue;
|
|
3398
|
+
}
|
|
3399
|
+
seen.add(start);
|
|
3400
|
+
const piece = new Array();
|
|
3401
|
+
const stack = [start];
|
|
3402
|
+
while (stack.length > 0) {
|
|
3403
|
+
const node = stack.pop();
|
|
3404
|
+
piece.push(node);
|
|
3405
|
+
for (const neighbour of [...upstream.get(node) ?? [], ...downstream.get(node) ?? []]) {
|
|
3406
|
+
if (rankOf.has(neighbour) && !seen.has(neighbour)) {
|
|
3407
|
+
seen.add(neighbour);
|
|
3408
|
+
stack.push(neighbour);
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
piece.sort((a, b) => rankOf.get(a) - rankOf.get(b));
|
|
3413
|
+
pieces.push(piece);
|
|
3414
|
+
}
|
|
3415
|
+
return pieces;
|
|
3416
|
+
}
|
|
3417
|
+
function treeRoot(piece, downstream) {
|
|
3418
|
+
let root;
|
|
3419
|
+
for (const node of piece) {
|
|
3420
|
+
const consumers = downstream.get(node) ?? [];
|
|
3421
|
+
if (consumers.length > 1) {
|
|
3422
|
+
return void 0;
|
|
3423
|
+
}
|
|
3424
|
+
if (consumers.length === 0) {
|
|
3425
|
+
if (root !== void 0) {
|
|
3426
|
+
return void 0;
|
|
3427
|
+
}
|
|
3428
|
+
root = node;
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
return root;
|
|
3432
|
+
}
|
|
3433
|
+
function columnXs(widths) {
|
|
3434
|
+
const xs = new Array();
|
|
3435
|
+
let x = 0;
|
|
3436
|
+
for (const width2 of widths) {
|
|
3437
|
+
xs.push(x);
|
|
3438
|
+
x += width2 + widthSpacing;
|
|
3439
|
+
}
|
|
3440
|
+
return xs;
|
|
3441
|
+
}
|
|
3442
|
+
function layoutTree(root, upstream, sizes) {
|
|
3443
|
+
const children = (node) => [...new Set(upstream.get(node) ?? [])];
|
|
3444
|
+
const depthOf = /* @__PURE__ */ new Map([[root, 0]]);
|
|
3445
|
+
const preorder = new Array();
|
|
3446
|
+
const stack = [root];
|
|
3447
|
+
let deepest = 0;
|
|
3448
|
+
while (stack.length > 0) {
|
|
3449
|
+
const node = stack.pop();
|
|
3450
|
+
preorder.push(node);
|
|
3451
|
+
const kids = children(node);
|
|
3452
|
+
for (let i = kids.length - 1; i >= 0; i--) {
|
|
3453
|
+
const depth = depthOf.get(node) + 1;
|
|
3454
|
+
depthOf.set(kids[i], depth);
|
|
3455
|
+
deepest = Math.max(deepest, depth);
|
|
3456
|
+
stack.push(kids[i]);
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
const widths = new Array(deepest + 1).fill(0);
|
|
3460
|
+
for (const [node, depth] of depthOf) {
|
|
3461
|
+
widths[deepest - depth] = Math.max(widths[deepest - depth], sizes.get(node).x);
|
|
3462
|
+
}
|
|
3463
|
+
const xs = columnXs(widths);
|
|
3464
|
+
const band = /* @__PURE__ */ new Map();
|
|
3465
|
+
const topInBand = /* @__PURE__ */ new Map();
|
|
3466
|
+
const childOffset = /* @__PURE__ */ new Map();
|
|
3467
|
+
for (let i = preorder.length - 1; i >= 0; i--) {
|
|
3468
|
+
const node = preorder[i];
|
|
3469
|
+
const height2 = sizes.get(node).y;
|
|
3470
|
+
const kids = children(node);
|
|
3471
|
+
if (kids.length === 0) {
|
|
3472
|
+
band.set(node, height2);
|
|
3473
|
+
topInBand.set(node, 0);
|
|
3474
|
+
continue;
|
|
3475
|
+
}
|
|
3476
|
+
let offset = 0;
|
|
3477
|
+
for (const kid of kids) {
|
|
3478
|
+
childOffset.set(kid, offset);
|
|
3479
|
+
offset += band.get(kid) + heightSpacing;
|
|
3480
|
+
}
|
|
3481
|
+
const centre = (kid) => childOffset.get(kid) + topInBand.get(kid) + sizes.get(kid).y / 2;
|
|
3482
|
+
let top = (centre(kids[0]) + centre(kids[kids.length - 1])) / 2 - height2 / 2;
|
|
3483
|
+
let shift = 0;
|
|
3484
|
+
if (top < 0) {
|
|
3485
|
+
shift = -top;
|
|
3486
|
+
top = 0;
|
|
3487
|
+
for (const kid of kids) {
|
|
3488
|
+
childOffset.set(kid, childOffset.get(kid) + shift);
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
band.set(node, Math.max(offset - heightSpacing + shift, top + height2));
|
|
3492
|
+
topInBand.set(node, top);
|
|
3493
|
+
}
|
|
3494
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3495
|
+
const bandTop = /* @__PURE__ */ new Map([[root, 0]]);
|
|
3496
|
+
for (const node of preorder) {
|
|
3497
|
+
const column = deepest - depthOf.get(node);
|
|
3498
|
+
const size = sizes.get(node);
|
|
3499
|
+
positions.set(node, {
|
|
3500
|
+
x: xs[column] + (widths[column] - size.x) / 2,
|
|
3501
|
+
y: bandTop.get(node) + topInBand.get(node)
|
|
3502
|
+
});
|
|
3503
|
+
for (const kid of children(node)) {
|
|
3504
|
+
bandTop.set(kid, bandTop.get(node) + childOffset.get(kid));
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
return positions;
|
|
3508
|
+
}
|
|
3509
|
+
function isotonic(targets) {
|
|
3510
|
+
const blocks = new Array();
|
|
3511
|
+
for (const target of targets) {
|
|
3512
|
+
blocks.push({ sum: target, count: 1 });
|
|
3513
|
+
while (blocks.length > 1) {
|
|
3514
|
+
const last = blocks[blocks.length - 1];
|
|
3515
|
+
const before = blocks[blocks.length - 2];
|
|
3516
|
+
if (before.sum / before.count <= last.sum / last.count) {
|
|
3517
|
+
break;
|
|
3518
|
+
}
|
|
3519
|
+
before.sum += last.sum;
|
|
3520
|
+
before.count += last.count;
|
|
3521
|
+
blocks.pop();
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
const result = new Array();
|
|
3525
|
+
for (const block of blocks) {
|
|
3526
|
+
for (let i = 0; i < block.count; i++) {
|
|
3527
|
+
result.push(block.sum / block.count);
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
return result;
|
|
3531
|
+
}
|
|
3532
|
+
function median(values) {
|
|
3533
|
+
const sorted = values.slice().sort((a, b) => a - b);
|
|
3534
|
+
const middle = Math.floor(sorted.length / 2);
|
|
3535
|
+
return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
3536
|
+
}
|
|
3537
|
+
function layoutLayered(piece, upstream, downstream, sizes) {
|
|
3538
|
+
const layout = assignLayers(piece, upstream);
|
|
3539
|
+
const up = /* @__PURE__ */ new Map();
|
|
3540
|
+
const down = /* @__PURE__ */ new Map();
|
|
3541
|
+
const heightOf = /* @__PURE__ */ new Map();
|
|
3542
|
+
for (const node of piece) {
|
|
3543
|
+
up.set(node, [...new Set(upstream.get(node) ?? [])]);
|
|
3544
|
+
down.set(node, [...new Set(downstream.get(node) ?? [])]);
|
|
3545
|
+
heightOf.set(node, sizes.get(node).y);
|
|
3546
|
+
}
|
|
3547
|
+
let nextLane = -1;
|
|
3548
|
+
for (const from of piece) {
|
|
3549
|
+
const fromLayer = layout.layerOf.get(from);
|
|
3550
|
+
const consumers = down.get(from);
|
|
3551
|
+
for (let c = 0; c < consumers.length; c++) {
|
|
3552
|
+
const to = consumers[c];
|
|
3553
|
+
const toLayer = layout.layerOf.get(to);
|
|
3554
|
+
if (toLayer - fromLayer < 2) {
|
|
3555
|
+
continue;
|
|
3556
|
+
}
|
|
3557
|
+
let previous = from;
|
|
3558
|
+
for (let layer = fromLayer + 1; layer < toLayer; layer++) {
|
|
3559
|
+
const lane = nextLane--;
|
|
3560
|
+
layout.layerOf.set(lane, layer);
|
|
3561
|
+
layout.layers[layer].push(lane);
|
|
3562
|
+
heightOf.set(lane, 0);
|
|
3563
|
+
up.set(lane, [previous]);
|
|
3564
|
+
down.set(lane, []);
|
|
3565
|
+
if (previous === from) {
|
|
3566
|
+
consumers[c] = lane;
|
|
3567
|
+
} else {
|
|
3568
|
+
down.get(previous).push(lane);
|
|
3569
|
+
}
|
|
3570
|
+
previous = lane;
|
|
3571
|
+
}
|
|
3572
|
+
down.get(previous).push(to);
|
|
3573
|
+
const feeders = up.get(to);
|
|
3574
|
+
feeders[feeders.indexOf(from)] = previous;
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
const sequence = /* @__PURE__ */ new Map();
|
|
3578
|
+
for (const sink of piece) {
|
|
3579
|
+
if (down.get(sink).length > 0) {
|
|
3580
|
+
continue;
|
|
3581
|
+
}
|
|
3582
|
+
const stack = [sink];
|
|
3583
|
+
while (stack.length > 0) {
|
|
3584
|
+
const node = stack.pop();
|
|
3585
|
+
if (sequence.has(node)) {
|
|
3586
|
+
continue;
|
|
3587
|
+
}
|
|
3588
|
+
sequence.set(node, sequence.size);
|
|
3589
|
+
const feeders = up.get(node) ?? [];
|
|
3590
|
+
for (let i = feeders.length - 1; i >= 0; i--) {
|
|
3591
|
+
stack.push(feeders[i]);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
for (const layer of layout.layers) {
|
|
3596
|
+
layer.sort((a, b) => (sequence.get(a) ?? Infinity) - (sequence.get(b) ?? Infinity));
|
|
3597
|
+
}
|
|
3598
|
+
orderWithinLayers(layout, up, down);
|
|
3599
|
+
const gapAfter = (above, below) => above < 0 || below < 0 ? laneSpacing : heightSpacing;
|
|
3600
|
+
const topOf = /* @__PURE__ */ new Map();
|
|
3601
|
+
for (const layer of layout.layers) {
|
|
3602
|
+
let y = 0;
|
|
3603
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3604
|
+
topOf.set(layer[i], y);
|
|
3605
|
+
y += heightOf.get(layer[i]) + (i + 1 < layer.length ? gapAfter(layer[i], layer[i + 1]) : 0);
|
|
3606
|
+
}
|
|
3607
|
+
for (const node of layer) {
|
|
3608
|
+
topOf.set(node, topOf.get(node) - y / 2);
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
const centreOf = (node) => topOf.get(node) + heightOf.get(node) / 2;
|
|
3612
|
+
const place = (layer, neighbours, adjacent) => {
|
|
3613
|
+
const offsets = new Array();
|
|
3614
|
+
const targets = new Array();
|
|
3615
|
+
let offset = 0;
|
|
3616
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3617
|
+
const node = layer[i];
|
|
3618
|
+
const centres = (neighbours.get(node) ?? []).filter((n) => layout.layerOf.get(n) === adjacent).map(centreOf);
|
|
3619
|
+
const desired = centres.length > 0 ? median(centres) - heightOf.get(node) / 2 : topOf.get(node);
|
|
3620
|
+
offsets.push(offset);
|
|
3621
|
+
targets.push(desired - offset);
|
|
3622
|
+
if (i + 1 < layer.length) {
|
|
3623
|
+
offset += heightOf.get(node) + gapAfter(node, layer[i + 1]);
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
const solved = isotonic(targets);
|
|
3627
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3628
|
+
topOf.set(layer[i], solved[i] + offsets[i]);
|
|
3629
|
+
}
|
|
3156
3630
|
};
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
if (adjacent < 0 || adjacent >= layout.layers.length) {
|
|
3162
|
-
continue;
|
|
3631
|
+
for (let s = 0; s < placementSweeps; s++) {
|
|
3632
|
+
if (s % 2 === 0) {
|
|
3633
|
+
for (let i = 1; i < layout.layers.length; i++) {
|
|
3634
|
+
place(layout.layers[i], up, i - 1);
|
|
3163
3635
|
}
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
const mean = meanSlot(node, neighbours, adjacent);
|
|
3168
|
-
keys.set(node, mean === -1 ? slotOf.get(node) : mean);
|
|
3169
|
-
}
|
|
3170
|
-
layer.sort((a, b) => keys.get(a) - keys.get(b));
|
|
3171
|
-
for (let s = 0; s < layer.length; s++) {
|
|
3172
|
-
slotOf.set(layer[s], s);
|
|
3636
|
+
} else {
|
|
3637
|
+
for (let i = layout.layers.length - 2; i >= 0; i--) {
|
|
3638
|
+
place(layout.layers[i], down, i + 1);
|
|
3173
3639
|
}
|
|
3174
3640
|
}
|
|
3175
|
-
};
|
|
3176
|
-
for (let i = 0; i < orderingSweeps; i++) {
|
|
3177
|
-
sweep(upstream, -1);
|
|
3178
|
-
sweep(downstream, 1);
|
|
3179
3641
|
}
|
|
3642
|
+
const widths = layout.layers.map((layer) => layer.reduce((widest, node) => node < 0 ? widest : Math.max(widest, sizes.get(node).x), 0));
|
|
3643
|
+
const xs = columnXs(widths);
|
|
3644
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3645
|
+
for (const node of piece) {
|
|
3646
|
+
const layer = layout.layerOf.get(node);
|
|
3647
|
+
positions.set(node, {
|
|
3648
|
+
x: xs[layer] + (widths[layer] - sizes.get(node).x) / 2,
|
|
3649
|
+
y: topOf.get(node)
|
|
3650
|
+
});
|
|
3651
|
+
}
|
|
3652
|
+
return positions;
|
|
3653
|
+
}
|
|
3654
|
+
function layoutLoose(loose, sizes) {
|
|
3655
|
+
const columns = Math.ceil(Math.sqrt(loose.length));
|
|
3656
|
+
const widths = new Array(columns).fill(0);
|
|
3657
|
+
const heights = new Array(Math.ceil(loose.length / columns)).fill(0);
|
|
3658
|
+
loose.forEach((node, i) => {
|
|
3659
|
+
const size = sizes.get(node);
|
|
3660
|
+
widths[i % columns] = Math.max(widths[i % columns], size.x);
|
|
3661
|
+
heights[Math.floor(i / columns)] = Math.max(heights[Math.floor(i / columns)], size.y);
|
|
3662
|
+
});
|
|
3663
|
+
const xs = new Array();
|
|
3664
|
+
let x = 0;
|
|
3665
|
+
for (const width2 of widths) {
|
|
3666
|
+
xs.push(x);
|
|
3667
|
+
x += width2 + heightSpacing;
|
|
3668
|
+
}
|
|
3669
|
+
const ys = new Array();
|
|
3670
|
+
let y = 0;
|
|
3671
|
+
for (const height2 of heights) {
|
|
3672
|
+
ys.push(y);
|
|
3673
|
+
y += height2 + heightSpacing;
|
|
3674
|
+
}
|
|
3675
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3676
|
+
loose.forEach((node, i) => positions.set(node, { x: xs[i % columns], y: ys[Math.floor(i / columns)] }));
|
|
3677
|
+
return positions;
|
|
3678
|
+
}
|
|
3679
|
+
function toBlock(positions, sizes) {
|
|
3680
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
3681
|
+
for (const [node, position] of positions) {
|
|
3682
|
+
const size = sizes.get(node);
|
|
3683
|
+
minX = Math.min(minX, position.x);
|
|
3684
|
+
minY = Math.min(minY, position.y);
|
|
3685
|
+
maxX = Math.max(maxX, position.x + size.x);
|
|
3686
|
+
maxY = Math.max(maxY, position.y + size.y);
|
|
3687
|
+
}
|
|
3688
|
+
const shifted = /* @__PURE__ */ new Map();
|
|
3689
|
+
for (const [node, position] of positions) {
|
|
3690
|
+
shifted.set(node, { x: position.x - minX, y: position.y - minY });
|
|
3691
|
+
}
|
|
3692
|
+
return { positions: shifted, size: { x: maxX - minX, y: maxY - minY } };
|
|
3693
|
+
}
|
|
3694
|
+
function layoutGraph(sorting, upstream, downstream, sizes) {
|
|
3695
|
+
const blocks = new Array();
|
|
3696
|
+
const loose = new Array();
|
|
3697
|
+
for (const piece of findPieces(sorting, upstream, downstream)) {
|
|
3698
|
+
if (piece.length === 1) {
|
|
3699
|
+
loose.push(piece[0]);
|
|
3700
|
+
continue;
|
|
3701
|
+
}
|
|
3702
|
+
const root = treeRoot(piece, downstream);
|
|
3703
|
+
const positions2 = root !== void 0 ? layoutTree(root, upstream, sizes) : layoutLayered(piece, upstream, downstream, sizes);
|
|
3704
|
+
blocks.push(toBlock(positions2, sizes));
|
|
3705
|
+
}
|
|
3706
|
+
blocks.sort((a, b) => b.size.y - a.size.y || b.size.x - a.size.x);
|
|
3707
|
+
if (loose.length > 0) {
|
|
3708
|
+
blocks.push(toBlock(layoutLoose(loose, sizes), sizes));
|
|
3709
|
+
}
|
|
3710
|
+
let area = 0;
|
|
3711
|
+
let widest = 0;
|
|
3712
|
+
for (const block of blocks) {
|
|
3713
|
+
area += (block.size.x + pieceSpacing) * (block.size.y + pieceSpacing);
|
|
3714
|
+
widest = Math.max(widest, block.size.x);
|
|
3715
|
+
}
|
|
3716
|
+
const rowWidth = Math.max(widest, Math.sqrt(area));
|
|
3717
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3718
|
+
let x = 0;
|
|
3719
|
+
let y = 0;
|
|
3720
|
+
let rowHeight = 0;
|
|
3721
|
+
for (const block of blocks) {
|
|
3722
|
+
if (x > 0 && x + block.size.x > rowWidth) {
|
|
3723
|
+
x = 0;
|
|
3724
|
+
y += rowHeight + pieceSpacing;
|
|
3725
|
+
rowHeight = 0;
|
|
3726
|
+
}
|
|
3727
|
+
for (const [node, position] of block.positions) {
|
|
3728
|
+
positions.set(node, { x: position.x + x, y: position.y + y });
|
|
3729
|
+
}
|
|
3730
|
+
x += block.size.x + pieceSpacing;
|
|
3731
|
+
rowHeight = Math.max(rowHeight, block.size.y);
|
|
3732
|
+
}
|
|
3733
|
+
return positions;
|
|
3180
3734
|
}
|
|
3181
3735
|
function Organize(ctx, graph, nodesToSort) {
|
|
3182
3736
|
const nodes = graph.getNodes();
|
|
@@ -3185,9 +3739,6 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3185
3739
|
}
|
|
3186
3740
|
const sorting = new Array();
|
|
3187
3741
|
if (nodesToSort) {
|
|
3188
|
-
if (nodesToSort.length < 2) {
|
|
3189
|
-
return;
|
|
3190
|
-
}
|
|
3191
3742
|
for (const index of nodesToSort) {
|
|
3192
3743
|
if (index >= 0 && index < nodes.length) {
|
|
3193
3744
|
sorting.push(index);
|
|
@@ -3207,33 +3758,28 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3207
3758
|
nodeLUT.set(nodes[i], i);
|
|
3208
3759
|
}
|
|
3209
3760
|
const camera = new Camera();
|
|
3210
|
-
const
|
|
3761
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
3211
3762
|
for (const index of sorting) {
|
|
3212
|
-
|
|
3763
|
+
sizes.set(index, nodes[index].calculateBounds(ctx, camera).Size);
|
|
3213
3764
|
}
|
|
3214
3765
|
const upstream = /* @__PURE__ */ new Map();
|
|
3215
|
-
const downstream = /* @__PURE__ */ new Map();
|
|
3216
3766
|
for (const index of sorting) {
|
|
3767
|
+
const node = nodes[index];
|
|
3217
3768
|
const feeders = new Array();
|
|
3218
|
-
for (
|
|
3219
|
-
const
|
|
3220
|
-
|
|
3221
|
-
|
|
3769
|
+
for (let i = 0; i < node.inputs(); i++) {
|
|
3770
|
+
for (const connection of node.inputPort(i).connections()) {
|
|
3771
|
+
const feeder = connection.outNode();
|
|
3772
|
+
const feederIndex = feeder === null ? void 0 : nodeLUT.get(feeder);
|
|
3773
|
+
if (feederIndex !== void 0 && inLayout.has(feederIndex) && feederIndex !== index && !feeders.includes(feederIndex)) {
|
|
3774
|
+
feeders.push(feederIndex);
|
|
3775
|
+
}
|
|
3222
3776
|
}
|
|
3223
3777
|
}
|
|
3224
3778
|
upstream.set(index, feeders);
|
|
3225
|
-
const consumers = new Array();
|
|
3226
|
-
for (const consumer of graph.connectedOutputsNodeReferences(index)) {
|
|
3227
|
-
const consumerIndex = nodeLUT.get(consumer);
|
|
3228
|
-
if (consumerIndex !== void 0 && inLayout.has(consumerIndex) && consumerIndex !== index) {
|
|
3229
|
-
consumers.push(consumerIndex);
|
|
3230
|
-
}
|
|
3231
|
-
}
|
|
3232
|
-
downstream.set(index, consumers);
|
|
3233
3779
|
}
|
|
3234
|
-
const
|
|
3235
|
-
|
|
3236
|
-
|
|
3780
|
+
const downstream = consumersOf(sorting, upstream);
|
|
3781
|
+
const positions = layoutGraph(sorting, upstream, downstream, sizes);
|
|
3782
|
+
const anchor = { x: 0, y: 0 };
|
|
3237
3783
|
for (const index of sorting) {
|
|
3238
3784
|
const position = nodes[index].getPosition();
|
|
3239
3785
|
anchor.x += position.x;
|
|
@@ -3241,19 +3787,14 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3241
3787
|
}
|
|
3242
3788
|
anchor.x /= sorting.length;
|
|
3243
3789
|
anchor.y /= sorting.length;
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
const
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
const size = bounds.get(node).Size;
|
|
3253
|
-
nodes[node].setPosition({ x: x + (layerWidths[i] - size.x) / 2, y });
|
|
3254
|
-
y += size.y + heightSpacing;
|
|
3255
|
-
}
|
|
3256
|
-
x += layerWidths[i] + widthSpacing;
|
|
3790
|
+
let right = 0;
|
|
3791
|
+
let bottom = 0;
|
|
3792
|
+
for (const [index, position] of positions) {
|
|
3793
|
+
right = Math.max(right, position.x + sizes.get(index).x);
|
|
3794
|
+
bottom = Math.max(bottom, position.y + sizes.get(index).y);
|
|
3795
|
+
}
|
|
3796
|
+
for (const [index, position] of positions) {
|
|
3797
|
+
nodes[index].setPosition({ x: anchor.x - right / 2 + position.x, y: anchor.y - bottom / 2 + position.y });
|
|
3257
3798
|
}
|
|
3258
3799
|
}
|
|
3259
3800
|
|
|
@@ -3445,14 +3986,17 @@ var NodeSubsystem2 = class {
|
|
|
3445
3986
|
#portHovering;
|
|
3446
3987
|
#widgetHovering;
|
|
3447
3988
|
#widgetCurrentlyClicking;
|
|
3989
|
+
#widgetHoveringBox = null;
|
|
3990
|
+
#capturedWidgetBox = null;
|
|
3991
|
+
#capturedPointer = Zero();
|
|
3448
3992
|
#cursor;
|
|
3449
3993
|
#nodeFactory;
|
|
3450
3994
|
#boxSelect;
|
|
3451
3995
|
#boxSelectStart_graphSpace;
|
|
3452
3996
|
#boxSelectEnd_graphSpace;
|
|
3453
3997
|
#boxSelectionNodes;
|
|
3454
|
-
#boxSelectStyle;
|
|
3455
3998
|
#lastCtx;
|
|
3999
|
+
#lastMousePosition;
|
|
3456
4000
|
// LISTENERS ==============================================================
|
|
3457
4001
|
#nodeRemovedCallbacks;
|
|
3458
4002
|
#registeredNodeAddedCallbacks;
|
|
@@ -3477,14 +4021,6 @@ var NodeSubsystem2 = class {
|
|
|
3477
4021
|
this.#boxSelectEnd_graphSpace = Zero();
|
|
3478
4022
|
this.#boxSelectionNodes = new List();
|
|
3479
4023
|
this.#lastCtx = null;
|
|
3480
|
-
this.#boxSelectStyle = new BoxStyle({
|
|
3481
|
-
border: {
|
|
3482
|
-
color: Theme.BoxSelect.Color,
|
|
3483
|
-
size: Theme.BoxSelect.Size
|
|
3484
|
-
},
|
|
3485
|
-
color: "rgba(0,0,0,0)",
|
|
3486
|
-
radius: Theme.BoxSelect.Radius
|
|
3487
|
-
});
|
|
3488
4024
|
}
|
|
3489
4025
|
addPublisher(identifier, publisher) {
|
|
3490
4026
|
this.#nodeFactory.addPublisher(identifier, publisher);
|
|
@@ -3521,6 +4057,7 @@ var NodeSubsystem2 = class {
|
|
|
3521
4057
|
}
|
|
3522
4058
|
if (intersection.Widget !== void 0) {
|
|
3523
4059
|
this.#widgetHovering = intersection.Widget;
|
|
4060
|
+
this.#widgetHoveringBox = intersection.WidgetBox ?? null;
|
|
3524
4061
|
}
|
|
3525
4062
|
if (intersection.Port !== void 0 && intersection.Node !== void 0 && intersection.PortIndex !== void 0 && intersection.PortIsInput !== void 0) {
|
|
3526
4063
|
this.#portHovering = {
|
|
@@ -3543,8 +4080,15 @@ var NodeSubsystem2 = class {
|
|
|
3543
4080
|
hoveringSomething = true;
|
|
3544
4081
|
}
|
|
3545
4082
|
if (this.#widgetHovering !== null) {
|
|
3546
|
-
this.#widgetHovering.
|
|
3547
|
-
|
|
4083
|
+
if (!this.#widgetHovering.Disabled?.()) {
|
|
4084
|
+
this.#widgetCurrentlyClicking = this.#widgetHovering;
|
|
4085
|
+
this.#capturedWidgetBox = this.#widgetHoveringBox === null ? null : {
|
|
4086
|
+
Position: { x: this.#widgetHoveringBox.Position.x, y: this.#widgetHoveringBox.Position.y },
|
|
4087
|
+
Size: { x: this.#widgetHoveringBox.Size.x, y: this.#widgetHoveringBox.Size.y }
|
|
4088
|
+
};
|
|
4089
|
+
CopyVector2(this.#capturedPointer, mousePosition);
|
|
4090
|
+
this.#widgetCurrentlyClicking.PointerDown?.(this.#widgetPointer());
|
|
4091
|
+
}
|
|
3548
4092
|
hoveringSomething = true;
|
|
3549
4093
|
}
|
|
3550
4094
|
if (this.#portHovering === null) {
|
|
@@ -3559,12 +4103,12 @@ var NodeSubsystem2 = class {
|
|
|
3559
4103
|
return hoveringSomething || ctrlKey;
|
|
3560
4104
|
}
|
|
3561
4105
|
if (this.#portHovering.InputPort) {
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
4106
|
+
const port = this.#portHovering.Port;
|
|
4107
|
+
const wires = port.connections();
|
|
4108
|
+
const picked = wires.find((wire) => wire.mouseOverPort(mousePosition) === port) ?? wires[0];
|
|
4109
|
+
if (picked !== void 0) {
|
|
4110
|
+
picked.clearInput();
|
|
4111
|
+
this.#connectionSelected = picked;
|
|
3568
4112
|
}
|
|
3569
4113
|
} else {
|
|
3570
4114
|
let inNode = this.#portHovering.Node;
|
|
@@ -3584,7 +4128,19 @@ var NodeSubsystem2 = class {
|
|
|
3584
4128
|
}
|
|
3585
4129
|
return true;
|
|
3586
4130
|
}
|
|
4131
|
+
#widgetPointer() {
|
|
4132
|
+
return {
|
|
4133
|
+
position: { x: this.#capturedPointer.x, y: this.#capturedPointer.y },
|
|
4134
|
+
inside: this.#capturedWidgetBox !== null && InBox(this.#capturedWidgetBox, this.#capturedPointer)
|
|
4135
|
+
};
|
|
4136
|
+
}
|
|
3587
4137
|
mouseDragEvent(delta, scale) {
|
|
4138
|
+
if (this.#widgetCurrentlyClicking !== null) {
|
|
4139
|
+
this.#capturedPointer.x += delta.x;
|
|
4140
|
+
this.#capturedPointer.y += delta.y;
|
|
4141
|
+
this.#widgetCurrentlyClicking.PointerMove?.(this.#widgetPointer());
|
|
4142
|
+
return true;
|
|
4143
|
+
}
|
|
3588
4144
|
let nodeMoved = false;
|
|
3589
4145
|
VectorPool.run(() => {
|
|
3590
4146
|
const scaledDelta = VectorPool.get();
|
|
@@ -3703,13 +4259,20 @@ var NodeSubsystem2 = class {
|
|
|
3703
4259
|
}
|
|
3704
4260
|
#firstCompatiblePort(node, side, against) {
|
|
3705
4261
|
const count = side === "INPUT" /* Input */ ? node.inputs() : node.outputs();
|
|
4262
|
+
let firstHidden = -1;
|
|
3706
4263
|
for (let i = 0; i < count; i++) {
|
|
3707
4264
|
const port = side === "INPUT" /* Input */ ? node.inputPort(i) : node.outputPort(i);
|
|
3708
|
-
if (portsCompatible(port, against)) {
|
|
4265
|
+
if (!portsCompatible(port, against)) {
|
|
4266
|
+
continue;
|
|
4267
|
+
}
|
|
4268
|
+
if (!port.hidden()) {
|
|
3709
4269
|
return i;
|
|
3710
4270
|
}
|
|
4271
|
+
if (firstHidden === -1) {
|
|
4272
|
+
firstHidden = i;
|
|
4273
|
+
}
|
|
3711
4274
|
}
|
|
3712
|
-
return
|
|
4275
|
+
return firstHidden;
|
|
3713
4276
|
}
|
|
3714
4277
|
clickEnd() {
|
|
3715
4278
|
for (let i = 0; i < this.#nodesGrabbed.Count(); i++) {
|
|
@@ -3725,10 +4288,11 @@ var NodeSubsystem2 = class {
|
|
|
3725
4288
|
this.#boxSelect = false;
|
|
3726
4289
|
}
|
|
3727
4290
|
if (this.#widgetCurrentlyClicking !== null) {
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
}
|
|
4291
|
+
const widget = this.#widgetCurrentlyClicking;
|
|
4292
|
+
const pointer = this.#widgetPointer();
|
|
3731
4293
|
this.#widgetCurrentlyClicking = null;
|
|
4294
|
+
this.#capturedWidgetBox = null;
|
|
4295
|
+
widget.PointerUp?.(pointer);
|
|
3732
4296
|
}
|
|
3733
4297
|
if (this.#connectionSelected === null) {
|
|
3734
4298
|
return;
|
|
@@ -3773,7 +4337,7 @@ var NodeSubsystem2 = class {
|
|
|
3773
4337
|
inputPort.connections()[0].clearOutput();
|
|
3774
4338
|
this.#removeConnection(inputPort.connections()[0], false);
|
|
3775
4339
|
}
|
|
3776
|
-
conn.setInput(this.#portHovering.Node, this.#portHovering.Index, replace);
|
|
4340
|
+
conn.setInput(this.#portHovering.Node, this.#portHovering.Index, replace, this.#insertIndex());
|
|
3777
4341
|
} else {
|
|
3778
4342
|
conn.setOutput(this.#portHovering.Node, this.#portHovering.Index);
|
|
3779
4343
|
}
|
|
@@ -3913,6 +4477,14 @@ var NodeSubsystem2 = class {
|
|
|
3913
4477
|
this.#removeNodeConnections(nodeIndex);
|
|
3914
4478
|
const node = this.#nodes[nodeIndex];
|
|
3915
4479
|
this.#nodes.splice(nodeIndex, 1);
|
|
4480
|
+
if (this.#nodeHovering === nodeIndex) {
|
|
4481
|
+
this.#nodeHovering = -1;
|
|
4482
|
+
} else if (this.#nodeHovering > nodeIndex) {
|
|
4483
|
+
this.#nodeHovering--;
|
|
4484
|
+
}
|
|
4485
|
+
if (this.#portHovering?.Node === node) {
|
|
4486
|
+
this.#portHovering = null;
|
|
4487
|
+
}
|
|
3916
4488
|
requestRender();
|
|
3917
4489
|
for (let i = 0; i < this.#nodeRemovedCallbacks.length; i++) {
|
|
3918
4490
|
this.#nodeRemovedCallbacks[i](node);
|
|
@@ -4137,7 +4709,8 @@ var NodeSubsystem2 = class {
|
|
|
4137
4709
|
}
|
|
4138
4710
|
if (intersection.Widget !== void 0) {
|
|
4139
4711
|
this.#widgetHovering = intersection.Widget;
|
|
4140
|
-
this.#
|
|
4712
|
+
this.#widgetHoveringBox = intersection.WidgetBox ?? null;
|
|
4713
|
+
this.#cursor = intersection.Widget.Disabled?.() ? "default" /* Default */ : intersection.Widget.Cursor?.() ?? "pointer" /* Pointer */;
|
|
4141
4714
|
}
|
|
4142
4715
|
if (intersection.Port !== void 0 && intersection.Node !== void 0 && intersection.PortIndex !== void 0 && intersection.PortIsInput !== void 0) {
|
|
4143
4716
|
this.#portHovering = {
|
|
@@ -4160,6 +4733,9 @@ var NodeSubsystem2 = class {
|
|
|
4160
4733
|
}
|
|
4161
4734
|
this.#nodes[i].render(ctx, camera, state, mousePosition, this.#postProcessPass);
|
|
4162
4735
|
}
|
|
4736
|
+
if (this.#widgetCurrentlyClicking !== null) {
|
|
4737
|
+
this.#cursor = this.#widgetCurrentlyClicking.Cursor?.() ?? "pointer" /* Pointer */;
|
|
4738
|
+
}
|
|
4163
4739
|
}
|
|
4164
4740
|
#boxSelectionScreenspaceBox(camera) {
|
|
4165
4741
|
const box = { Position: Zero(), Size: Zero() };
|
|
@@ -4168,29 +4744,174 @@ var NodeSubsystem2 = class {
|
|
|
4168
4744
|
SubVector2(box.Size, box.Size, box.Position);
|
|
4169
4745
|
return box;
|
|
4170
4746
|
}
|
|
4747
|
+
/**
|
|
4748
|
+
* Where a wire being dropped on the hovered array input would go, or
|
|
4749
|
+
* undefined when it is not over one.
|
|
4750
|
+
*/
|
|
4751
|
+
#insertIndex() {
|
|
4752
|
+
const hovering = this.#portHovering;
|
|
4753
|
+
if (hovering === null || !hovering.InputPort || this.#lastMousePosition === void 0 || hovering.Port.getPortType() !== "INPUTARRAY" /* InputArray */) {
|
|
4754
|
+
return void 0;
|
|
4755
|
+
}
|
|
4756
|
+
const box = hovering.Node.inputPortPosition(hovering.Index);
|
|
4757
|
+
return arrayInsertIndex(box, hovering.Port.connections().length, this.#lastMousePosition.y);
|
|
4758
|
+
}
|
|
4759
|
+
#renderInsertMarker(ctx, camera) {
|
|
4760
|
+
const hovering = this.#portHovering;
|
|
4761
|
+
if (this.#connectionSelected === null || this.#connectionSelected.inPort() !== null || hovering === null) {
|
|
4762
|
+
return;
|
|
4763
|
+
}
|
|
4764
|
+
const index = this.#insertIndex();
|
|
4765
|
+
const count = hovering.Port.connections().length;
|
|
4766
|
+
if (index === void 0 || count === 0) {
|
|
4767
|
+
return;
|
|
4768
|
+
}
|
|
4769
|
+
const box = hovering.Node.inputPortPosition(hovering.Index);
|
|
4770
|
+
const y = box.Position.y + index * box.Size.y / count;
|
|
4771
|
+
const overhang = 6 * camera.zoom;
|
|
4772
|
+
ctx.save();
|
|
4773
|
+
ctx.strokeStyle = "#ffffff";
|
|
4774
|
+
ctx.lineWidth = Math.max(1, 2 * camera.zoom);
|
|
4775
|
+
ctx.beginPath();
|
|
4776
|
+
ctx.moveTo(box.Position.x - overhang, y);
|
|
4777
|
+
ctx.lineTo(box.Position.x + box.Size.x + overhang, y);
|
|
4778
|
+
ctx.stroke();
|
|
4779
|
+
ctx.restore();
|
|
4780
|
+
}
|
|
4171
4781
|
render(ctx, camera, mousePosition) {
|
|
4172
4782
|
this.#lastCtx = ctx;
|
|
4783
|
+
this.#lastMousePosition = mousePosition;
|
|
4173
4784
|
this.#cursor = "default" /* Default */;
|
|
4174
4785
|
const awaitingLayout = this.#nodes.some((node) => !node.hasBeenLaidOut());
|
|
4786
|
+
for (const node of this.#nodes) {
|
|
4787
|
+
if (node.hasBeenLaidOut()) {
|
|
4788
|
+
node.placePorts(node.calculateBounds(ctx, camera), camera.zoom);
|
|
4789
|
+
}
|
|
4790
|
+
}
|
|
4175
4791
|
TimeExecution("Render_Connections", () => {
|
|
4176
4792
|
this.#renderConnections(ctx, camera, mousePosition);
|
|
4177
4793
|
});
|
|
4178
4794
|
TimeExecution("Render_Nodes", () => {
|
|
4179
4795
|
this.#renderNodes(ctx, camera, mousePosition);
|
|
4180
4796
|
});
|
|
4797
|
+
this.#renderInsertMarker(ctx, camera);
|
|
4181
4798
|
if (awaitingLayout) {
|
|
4182
4799
|
requestRender();
|
|
4183
4800
|
}
|
|
4184
4801
|
if (this.#boxSelect) {
|
|
4185
4802
|
const box = this.#boxSelectionScreenspaceBox(camera);
|
|
4186
4803
|
ctx.setLineDash([Theme.BoxSelect.LineDashLength]);
|
|
4187
|
-
|
|
4804
|
+
new BoxStyle({
|
|
4805
|
+
border: { color: Theme.BoxSelect.Color, size: Theme.BoxSelect.Size },
|
|
4806
|
+
color: "rgba(0,0,0,0)",
|
|
4807
|
+
radius: Theme.BoxSelect.Radius
|
|
4808
|
+
}).Draw(ctx, box, 1);
|
|
4188
4809
|
ctx.setLineDash([]);
|
|
4189
4810
|
}
|
|
4190
4811
|
return { cursorStyle: this.#cursor };
|
|
4191
4812
|
}
|
|
4192
4813
|
};
|
|
4193
4814
|
|
|
4815
|
+
// src/popup.ts
|
|
4816
|
+
var Popup = class {
|
|
4817
|
+
#title;
|
|
4818
|
+
#options;
|
|
4819
|
+
#content;
|
|
4820
|
+
#onClose;
|
|
4821
|
+
// Styling ================================================================
|
|
4822
|
+
#buttonCSS;
|
|
4823
|
+
// Runtime ================================================================
|
|
4824
|
+
#popup;
|
|
4825
|
+
#keyHandler;
|
|
4826
|
+
constructor(config) {
|
|
4827
|
+
this.#title = config.title;
|
|
4828
|
+
this.#options = config.options;
|
|
4829
|
+
this.#content = config.content;
|
|
4830
|
+
this.#onClose = config.onClose;
|
|
4831
|
+
this.#buttonCSS = config.buttonCSS === void 0 ? "flex-grow: 1; margin-right: 8px;" : config.buttonCSS;
|
|
4832
|
+
this.#popup = null;
|
|
4833
|
+
this.#keyHandler = null;
|
|
4834
|
+
}
|
|
4835
|
+
Hide() {
|
|
4836
|
+
if (this.#popup === null) {
|
|
4837
|
+
return;
|
|
4838
|
+
}
|
|
4839
|
+
if (this.#keyHandler !== null) {
|
|
4840
|
+
document.removeEventListener("keydown", this.#keyHandler, true);
|
|
4841
|
+
this.#keyHandler = null;
|
|
4842
|
+
}
|
|
4843
|
+
document.body.removeChild(this.#popup);
|
|
4844
|
+
this.#popup = null;
|
|
4845
|
+
}
|
|
4846
|
+
#close(option) {
|
|
4847
|
+
this.Hide();
|
|
4848
|
+
if (this.#onClose !== void 0) {
|
|
4849
|
+
this.#onClose(option);
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
Show() {
|
|
4853
|
+
this.#popup = document.createElement("div");
|
|
4854
|
+
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};`;
|
|
4855
|
+
document.body.appendChild(this.#popup);
|
|
4856
|
+
const container = document.createElement("div");
|
|
4857
|
+
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;`;
|
|
4858
|
+
this.#popup.appendChild(container);
|
|
4859
|
+
const title = document.createElement("h2");
|
|
4860
|
+
title.textContent = this.#title;
|
|
4861
|
+
title.style.cssText = "margin-top:0;margin-bottom:16px;";
|
|
4862
|
+
container.appendChild(title);
|
|
4863
|
+
if (this.#content !== void 0) {
|
|
4864
|
+
container.appendChild(this.#content());
|
|
4865
|
+
}
|
|
4866
|
+
const buttonContainer = document.createElement("div");
|
|
4867
|
+
buttonContainer.style.cssText = "margin-top:16px;justify-content:space-around;align-items:center;flex-direction:row;display:flex;";
|
|
4868
|
+
container.appendChild(buttonContainer);
|
|
4869
|
+
for (let i = 0; i < this.#options.length; i++) {
|
|
4870
|
+
const button = document.createElement("button");
|
|
4871
|
+
button.style.cssText = this.#buttonCSS;
|
|
4872
|
+
button.textContent = this.#options[i];
|
|
4873
|
+
button.onclick = () => this.#close(this.#options[i]);
|
|
4874
|
+
buttonContainer.appendChild(button);
|
|
4875
|
+
}
|
|
4876
|
+
this.#keyHandler = (e) => {
|
|
4877
|
+
if (e.key === "Enter") {
|
|
4878
|
+
e.preventDefault();
|
|
4879
|
+
this.#close(this.#options.length > 0 ? this.#options[0] : null);
|
|
4880
|
+
} else if (e.key === "Escape") {
|
|
4881
|
+
e.preventDefault();
|
|
4882
|
+
this.#close(null);
|
|
4883
|
+
}
|
|
4884
|
+
e.stopPropagation();
|
|
4885
|
+
};
|
|
4886
|
+
document.addEventListener("keydown", this.#keyHandler, true);
|
|
4887
|
+
}
|
|
4888
|
+
};
|
|
4889
|
+
|
|
4890
|
+
// src/popups/string.ts
|
|
4891
|
+
function SetStringPopup(config) {
|
|
4892
|
+
let input = null;
|
|
4893
|
+
return new Popup({
|
|
4894
|
+
title: config.title,
|
|
4895
|
+
options: ["Set", "Cancel"],
|
|
4896
|
+
content: () => {
|
|
4897
|
+
const container = document.createElement("div");
|
|
4898
|
+
input = document.createElement("input");
|
|
4899
|
+
input.value = config.startingValue;
|
|
4900
|
+
container.append(input);
|
|
4901
|
+
return container;
|
|
4902
|
+
},
|
|
4903
|
+
onClose: (button) => {
|
|
4904
|
+
if (button !== "Set" || input === null) {
|
|
4905
|
+
if (config.onCancel) {
|
|
4906
|
+
config.onCancel();
|
|
4907
|
+
}
|
|
4908
|
+
return;
|
|
4909
|
+
}
|
|
4910
|
+
config.onUpdate(input.value);
|
|
4911
|
+
}
|
|
4912
|
+
});
|
|
4913
|
+
}
|
|
4914
|
+
|
|
4194
4915
|
// src/popups/form.ts
|
|
4195
4916
|
function FormPopup(config) {
|
|
4196
4917
|
let input = new Array();
|
|
@@ -4307,9 +5028,11 @@ var FlowNode = class {
|
|
|
4307
5028
|
#titleChangeCallback;
|
|
4308
5029
|
#infoChangeCallback;
|
|
4309
5030
|
// Styling ================================================================
|
|
5031
|
+
#styleConfig;
|
|
5032
|
+
#styledForTheme = -1;
|
|
4310
5033
|
#titleColor;
|
|
4311
5034
|
#selectedStyle;
|
|
4312
|
-
#stateStyles;
|
|
5035
|
+
#stateStyles = /* @__PURE__ */ new Map();
|
|
4313
5036
|
#padding;
|
|
4314
5037
|
#portTextStyle;
|
|
4315
5038
|
#elementSpacing;
|
|
@@ -4321,6 +5044,7 @@ var FlowNode = class {
|
|
|
4321
5044
|
#data;
|
|
4322
5045
|
#registeredAnyPropertyChangeCallbacks;
|
|
4323
5046
|
#registeredPropertyChangeCallbacks;
|
|
5047
|
+
#registeredPropertyEditCallbacks = /* @__PURE__ */ new Map();
|
|
4324
5048
|
constructor(config) {
|
|
4325
5049
|
this.#input = new Array();
|
|
4326
5050
|
this.#output = new Array();
|
|
@@ -4373,58 +5097,17 @@ var FlowNode = class {
|
|
|
4373
5097
|
this.#position = config?.position === void 0 ? { x: 0, y: 0 } : config.position;
|
|
4374
5098
|
this.#title = new Text(
|
|
4375
5099
|
config?.title === void 0 ? "" : config.title,
|
|
4376
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
4377
|
-
size: 16,
|
|
4378
|
-
weight: "bold" /* Bold */,
|
|
4379
|
-
color: Theme.Node.FontColor
|
|
4380
|
-
})
|
|
5100
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 16, weight: "bold" /* Bold */ })
|
|
4381
5101
|
);
|
|
4382
5102
|
this.#subTitle = new Text(
|
|
4383
5103
|
config?.subTitle === void 0 ? "" : config.subTitle,
|
|
4384
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
4385
|
-
size: 10,
|
|
4386
|
-
weight: "bold" /* Bold */,
|
|
4387
|
-
color: Theme.Node.FontColor
|
|
4388
|
-
})
|
|
4389
|
-
);
|
|
4390
|
-
this.#infoSymbol = new Text(
|
|
4391
|
-
"i",
|
|
4392
|
-
{
|
|
4393
|
-
size: 13,
|
|
4394
|
-
weight: "bold" /* Bold */,
|
|
4395
|
-
color: Theme.Node.FontColor
|
|
4396
|
-
}
|
|
5104
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 10, weight: "bold" /* Bold */ })
|
|
4397
5105
|
);
|
|
5106
|
+
this.#infoSymbol = new Text("i", { size: 13, weight: "bold" /* Bold */ });
|
|
4398
5107
|
this.#infoText = config?.info ? config?.info : "";
|
|
4399
|
-
this.#titleColor = config?.style?.title?.color === void 0 ? "#154050" : config?.style?.title?.color;
|
|
4400
5108
|
this.#padding = config?.style?.title?.padding === void 0 ? 15 : config?.style?.title?.padding;
|
|
4401
|
-
this.#
|
|
4402
|
-
this.#
|
|
4403
|
-
border: { color: Theme.Node.Border.Idle, size: 1 },
|
|
4404
|
-
radius: Theme.Node.BorderRadius,
|
|
4405
|
-
color: Theme.Node.BackgroundColor
|
|
4406
|
-
})));
|
|
4407
|
-
this.#stateStyles.set(1 /* MouseOver */, new BoxStyle(BoxStyleWithFallback(config?.style?.mouseOver, {
|
|
4408
|
-
border: { color: Theme.Node.Border.MouseOver, size: 1.1 },
|
|
4409
|
-
radius: Theme.Node.BorderRadius,
|
|
4410
|
-
color: Theme.Node.BackgroundColor
|
|
4411
|
-
})));
|
|
4412
|
-
this.#stateStyles.set(2 /* Grabbed */, new BoxStyle(BoxStyleWithFallback(config?.style?.grabbed, {
|
|
4413
|
-
border: { color: Theme.Node.Border.Grabbed, size: 2 },
|
|
4414
|
-
radius: Theme.Node.BorderRadius,
|
|
4415
|
-
color: Theme.Node.BackgroundColor
|
|
4416
|
-
})));
|
|
4417
|
-
this.#selectedStyle = new BoxStyle(BoxStyleWithFallback(config?.style?.selected, {
|
|
4418
|
-
border: { color: Theme.Node.Border.Selected, size: 1 },
|
|
4419
|
-
radius: Theme.Node.BorderRadius,
|
|
4420
|
-
color: Theme.Node.BackgroundColor
|
|
4421
|
-
}));
|
|
4422
|
-
this.#portTextStyle = new TextStyle({
|
|
4423
|
-
size: config?.style?.portText?.size === void 0 ? 14 : config?.style?.portText?.size,
|
|
4424
|
-
color: config?.style?.portText?.color === void 0 ? Theme.Node.Port.FontColor : config?.style?.portText?.color,
|
|
4425
|
-
font: config?.style?.portText?.font,
|
|
4426
|
-
weight: config?.style?.portText?.weight
|
|
4427
|
-
});
|
|
5109
|
+
this.#styleConfig = config?.style;
|
|
5110
|
+
this.#applyStyle();
|
|
4428
5111
|
if (config?.inputs !== void 0) {
|
|
4429
5112
|
for (let i = 0; i < config.inputs.length; i++) {
|
|
4430
5113
|
this.addInput(config.inputs[i]);
|
|
@@ -4436,12 +5119,8 @@ var FlowNode = class {
|
|
|
4436
5119
|
}
|
|
4437
5120
|
}
|
|
4438
5121
|
if (config?.widgets !== void 0) {
|
|
4439
|
-
for (
|
|
4440
|
-
|
|
4441
|
-
if (widget.type === void 0) {
|
|
4442
|
-
continue;
|
|
4443
|
-
}
|
|
4444
|
-
this.addWidget(GlobalWidgetFactory.create(this, widget.type, widget.config));
|
|
5122
|
+
for (const widget of config.widgets) {
|
|
5123
|
+
this.addWidget(GlobalWidgetFactory.createFromConfig(this, widget));
|
|
4445
5124
|
}
|
|
4446
5125
|
}
|
|
4447
5126
|
}
|
|
@@ -4498,7 +5177,7 @@ var FlowNode = class {
|
|
|
4498
5177
|
}
|
|
4499
5178
|
setProperty(name, value) {
|
|
4500
5179
|
const oldValue = this.#data[name];
|
|
4501
|
-
if (oldValue
|
|
5180
|
+
if (Object.is(oldValue, value)) {
|
|
4502
5181
|
return;
|
|
4503
5182
|
}
|
|
4504
5183
|
this.#data[name] = value;
|
|
@@ -4514,6 +5193,32 @@ var FlowNode = class {
|
|
|
4514
5193
|
callbacks[i](oldValue, value);
|
|
4515
5194
|
}
|
|
4516
5195
|
}
|
|
5196
|
+
/**
|
|
5197
|
+
* Listens for a person changing the property through a widget, but not
|
|
5198
|
+
* for the property being set from code.
|
|
5199
|
+
*/
|
|
5200
|
+
addPropertyEditListener(name, callback) {
|
|
5201
|
+
let callbacks = this.#registeredPropertyEditCallbacks.get(name);
|
|
5202
|
+
if (callbacks === void 0) {
|
|
5203
|
+
callbacks = [];
|
|
5204
|
+
this.#registeredPropertyEditCallbacks.set(name, callbacks);
|
|
5205
|
+
}
|
|
5206
|
+
callbacks.push(callback);
|
|
5207
|
+
}
|
|
5208
|
+
/**
|
|
5209
|
+
* Sets the property as a person's finished edit, so edit listeners hear
|
|
5210
|
+
* it even when the value was already showing.
|
|
5211
|
+
*/
|
|
5212
|
+
editProperty(name, value) {
|
|
5213
|
+
this.setProperty(name, value);
|
|
5214
|
+
const callbacks = this.#registeredPropertyEditCallbacks.get(name);
|
|
5215
|
+
if (callbacks === void 0) {
|
|
5216
|
+
return;
|
|
5217
|
+
}
|
|
5218
|
+
for (const callback of [...callbacks]) {
|
|
5219
|
+
callback(value);
|
|
5220
|
+
}
|
|
5221
|
+
}
|
|
4517
5222
|
getProperty(name) {
|
|
4518
5223
|
return this.#data[name];
|
|
4519
5224
|
}
|
|
@@ -4802,6 +5507,7 @@ var FlowNode = class {
|
|
|
4802
5507
|
// return this.#titleTextStyle.measure(ctx, scale, this.#title);
|
|
4803
5508
|
// }
|
|
4804
5509
|
calculateBounds(ctx, camera) {
|
|
5510
|
+
this.#followTheme();
|
|
4805
5511
|
const tempMeasurement = Zero();
|
|
4806
5512
|
const doublePadding = this.#padding * 2;
|
|
4807
5513
|
const screenSpacePosition = Zero();
|
|
@@ -4815,25 +5521,21 @@ var FlowNode = class {
|
|
|
4815
5521
|
size.x += size.y * 4;
|
|
4816
5522
|
}
|
|
4817
5523
|
size.x += doublePadding;
|
|
4818
|
-
size.y += doublePadding
|
|
4819
|
-
for (
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
|
|
4824
|
-
}
|
|
4825
|
-
size.y += this.#elementSpacing * this.#output.length;
|
|
4826
|
-
for (let i = 0; i < this.#output.length; i++) {
|
|
4827
|
-
const port = this.#output[i];
|
|
5524
|
+
size.y += doublePadding;
|
|
5525
|
+
for (const port of [...this.#input, ...this.#output]) {
|
|
5526
|
+
if (port.hidden()) {
|
|
5527
|
+
continue;
|
|
5528
|
+
}
|
|
4828
5529
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
4829
|
-
size.y += tempMeasurement.y;
|
|
5530
|
+
size.y += this.#portRowHeight(port, tempMeasurement.y) + this.#elementSpacing;
|
|
4830
5531
|
size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
|
|
4831
5532
|
}
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
5533
|
+
for (const widget of this.#widgets) {
|
|
5534
|
+
if (widget.Hidden?.()) {
|
|
5535
|
+
continue;
|
|
5536
|
+
}
|
|
5537
|
+
const eleSize = widget.Size();
|
|
5538
|
+
size.y += eleSize.y + this.#elementSpacing;
|
|
4837
5539
|
size.x = Math.max(size.x, eleSize.x + doublePadding);
|
|
4838
5540
|
}
|
|
4839
5541
|
size.y += this.#elementSpacing;
|
|
@@ -4917,7 +5619,7 @@ var FlowNode = class {
|
|
|
4917
5619
|
intersection.Node = this;
|
|
4918
5620
|
}
|
|
4919
5621
|
for (let i = 0; i < this.#inputPortPositions.Count(); i++) {
|
|
4920
|
-
if (InBox(this.#inputPortPositions.At(i), position)) {
|
|
5622
|
+
if (!this.#input[i].hidden() && InBox(this.#inputPortPositions.At(i), position)) {
|
|
4921
5623
|
intersection.Node = this;
|
|
4922
5624
|
intersection.PortIndex = i;
|
|
4923
5625
|
intersection.PortIsInput = true;
|
|
@@ -4925,7 +5627,7 @@ var FlowNode = class {
|
|
|
4925
5627
|
}
|
|
4926
5628
|
}
|
|
4927
5629
|
for (let i = 0; i < this.#outputPortPositions.Count(); i++) {
|
|
4928
|
-
if (InBox(this.#outputPortPositions.At(i), position)) {
|
|
5630
|
+
if (!this.#output[i].hidden() && InBox(this.#outputPortPositions.At(i), position)) {
|
|
4929
5631
|
intersection.Node = this;
|
|
4930
5632
|
intersection.PortIndex = i;
|
|
4931
5633
|
intersection.PortIsInput = false;
|
|
@@ -4933,10 +5635,12 @@ var FlowNode = class {
|
|
|
4933
5635
|
}
|
|
4934
5636
|
}
|
|
4935
5637
|
for (let i = 0; i < this.#widgetPositions.Count(); i++) {
|
|
4936
|
-
|
|
5638
|
+
const widget = this.#widgets[i];
|
|
5639
|
+
if (widget !== void 0 && !widget.Hidden?.() && InBox(this.#widgetPositions.At(i), position)) {
|
|
4937
5640
|
intersection.Node = this;
|
|
4938
5641
|
intersection.WidgetIndex = i;
|
|
4939
|
-
intersection.Widget =
|
|
5642
|
+
intersection.Widget = widget;
|
|
5643
|
+
intersection.WidgetBox = this.#widgetPositions.At(i);
|
|
4940
5644
|
}
|
|
4941
5645
|
}
|
|
4942
5646
|
return intersection;
|
|
@@ -4951,10 +5655,6 @@ var FlowNode = class {
|
|
|
4951
5655
|
return this.#title.get();
|
|
4952
5656
|
}
|
|
4953
5657
|
setTitle(newTitle) {
|
|
4954
|
-
if (!this.#canEditTitle) {
|
|
4955
|
-
console.warn("setTitle instruction ignored, as node has been marked un-editable");
|
|
4956
|
-
return;
|
|
4957
|
-
}
|
|
4958
5658
|
let cleaned = newTitle;
|
|
4959
5659
|
if (cleaned === null || cleaned === void 0) {
|
|
4960
5660
|
cleaned = "";
|
|
@@ -4971,9 +5671,6 @@ var FlowNode = class {
|
|
|
4971
5671
|
}
|
|
4972
5672
|
}
|
|
4973
5673
|
setInfo(newInfo) {
|
|
4974
|
-
if (!this.#canEditInfo) {
|
|
4975
|
-
console.warn("setInfo instruction ignored, as node has been marked un-editable");
|
|
4976
|
-
}
|
|
4977
5674
|
let cleaned = newInfo;
|
|
4978
5675
|
if (cleaned === null || cleaned === void 0) {
|
|
4979
5676
|
cleaned = "";
|
|
@@ -4992,6 +5689,12 @@ var FlowNode = class {
|
|
|
4992
5689
|
inputPortPosition(index) {
|
|
4993
5690
|
return this.#inputPortPositions.At(index);
|
|
4994
5691
|
}
|
|
5692
|
+
widgetPosition(index) {
|
|
5693
|
+
if (index < 0 || index >= this.#widgetPositions.Count()) {
|
|
5694
|
+
return void 0;
|
|
5695
|
+
}
|
|
5696
|
+
return this.#widgetPositions.At(index);
|
|
5697
|
+
}
|
|
4995
5698
|
inputPort(index) {
|
|
4996
5699
|
return this.#input[index];
|
|
4997
5700
|
}
|
|
@@ -5007,6 +5710,35 @@ var FlowNode = class {
|
|
|
5007
5710
|
outputs() {
|
|
5008
5711
|
return this.#output.length;
|
|
5009
5712
|
}
|
|
5713
|
+
#applyStyle() {
|
|
5714
|
+
const style = this.#styleConfig;
|
|
5715
|
+
const titleText = style?.title?.textStyle?.color ?? Theme.Node.Title.FontColor;
|
|
5716
|
+
this.#title.style().setColor(titleText);
|
|
5717
|
+
this.#subTitle.style().setColor(titleText);
|
|
5718
|
+
this.#infoSymbol.style().setColor(titleText);
|
|
5719
|
+
this.#titleColor = style?.title?.color ?? Theme.Node.Title.Color;
|
|
5720
|
+
const box = (config, border, size) => new BoxStyle(BoxStyleWithFallback(config, {
|
|
5721
|
+
border: { color: border, size },
|
|
5722
|
+
radius: Theme.Node.BorderRadius,
|
|
5723
|
+
color: Theme.Node.BackgroundColor
|
|
5724
|
+
}));
|
|
5725
|
+
this.#stateStyles.set(0 /* Idle */, box(style?.idle, Theme.Node.Border.Idle, 1));
|
|
5726
|
+
this.#stateStyles.set(1 /* MouseOver */, box(style?.mouseOver, Theme.Node.Border.MouseOver, 1.1));
|
|
5727
|
+
this.#stateStyles.set(2 /* Grabbed */, box(style?.grabbed, Theme.Node.Border.Grabbed, 2));
|
|
5728
|
+
this.#selectedStyle = box(style?.selected, Theme.Node.Border.Selected, 1);
|
|
5729
|
+
this.#portTextStyle = new TextStyle({
|
|
5730
|
+
size: style?.portText?.size ?? 14,
|
|
5731
|
+
color: style?.portText?.color ?? Theme.Node.Port.FontColor,
|
|
5732
|
+
font: style?.portText?.font,
|
|
5733
|
+
weight: style?.portText?.weight
|
|
5734
|
+
});
|
|
5735
|
+
this.#styledForTheme = themeVersion();
|
|
5736
|
+
}
|
|
5737
|
+
#followTheme() {
|
|
5738
|
+
if (this.#styledForTheme !== themeVersion()) {
|
|
5739
|
+
this.#applyStyle();
|
|
5740
|
+
}
|
|
5741
|
+
}
|
|
5010
5742
|
#calculateStyle(state) {
|
|
5011
5743
|
if (this.#selected && state === 0 /* Idle */) {
|
|
5012
5744
|
return this.#selectedStyle;
|
|
@@ -5023,9 +5755,22 @@ var FlowNode = class {
|
|
|
5023
5755
|
clearMessages() {
|
|
5024
5756
|
this.#messages = [];
|
|
5025
5757
|
}
|
|
5758
|
+
#portRowHeight(port, textHeight) {
|
|
5759
|
+
const slots = port.slots();
|
|
5760
|
+
return slots > 1 ? Math.max(textHeight, slots * ARRAY_SLOT_PITCH) : textHeight;
|
|
5761
|
+
}
|
|
5026
5762
|
hasBeenLaidOut() {
|
|
5027
5763
|
return this.#laidOut;
|
|
5028
5764
|
}
|
|
5765
|
+
/**
|
|
5766
|
+
* Throws away where the ports were last placed, so wires wait for the
|
|
5767
|
+
* next draw instead of pointing at the old spots.
|
|
5768
|
+
*/
|
|
5769
|
+
invalidateLayout() {
|
|
5770
|
+
this.#laidOut = false;
|
|
5771
|
+
this.#portOffsets = { input: [], output: [] };
|
|
5772
|
+
requestRender();
|
|
5773
|
+
}
|
|
5029
5774
|
#laidOut = false;
|
|
5030
5775
|
render(ctx, camera, state, mousePosition, postProcess) {
|
|
5031
5776
|
this.#laidOut = true;
|
|
@@ -5063,6 +5808,9 @@ var FlowNode = class {
|
|
|
5063
5808
|
[nodeStyle.radius() * camera.zoom, nodeStyle.radius() * camera.zoom, 0, 0]
|
|
5064
5809
|
);
|
|
5065
5810
|
ctx.fill();
|
|
5811
|
+
if (Theme.Node.Bevel !== null) {
|
|
5812
|
+
drawBevel(ctx, nodeBounds, camera.zoom, Theme.Node.Bevel, false);
|
|
5813
|
+
}
|
|
5066
5814
|
const titlePosition = VectorPool.get();
|
|
5067
5815
|
titlePosition.x = nodeBounds.Position.x + nodeBounds.Size.x / 2;
|
|
5068
5816
|
titlePosition.y = nodeBounds.Position.y + scaledPadding + titleSize.y / 2;
|
|
@@ -5123,8 +5871,12 @@ var FlowNode = class {
|
|
|
5123
5871
|
for (let i = 0; i < this.#input.length; i++) {
|
|
5124
5872
|
ctx.textAlign = "left" /* Left */;
|
|
5125
5873
|
const port = this.#input[i];
|
|
5874
|
+
if (port.hidden()) {
|
|
5875
|
+
this.#inputPortPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
|
|
5876
|
+
continue;
|
|
5877
|
+
}
|
|
5126
5878
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
5127
|
-
const portHeight = tempMeasurement.y * camera.zoom;
|
|
5879
|
+
const portHeight = this.#portRowHeight(port, tempMeasurement.y) * camera.zoom;
|
|
5128
5880
|
const position = VectorPool.get();
|
|
5129
5881
|
position.x = nodeBounds.Position.x;
|
|
5130
5882
|
position.y = startY + portHeight / 2;
|
|
@@ -5137,6 +5889,10 @@ var FlowNode = class {
|
|
|
5137
5889
|
ctx.textAlign = "right" /* Right */;
|
|
5138
5890
|
for (let i = 0; i < this.#output.length; i++) {
|
|
5139
5891
|
const port = this.#output[i];
|
|
5892
|
+
if (port.hidden()) {
|
|
5893
|
+
this.#outputPortPositions.Push({ Position: { x: rightSide, y: startY }, Size: { x: 0, y: 0 } });
|
|
5894
|
+
continue;
|
|
5895
|
+
}
|
|
5140
5896
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
5141
5897
|
const portHeight = tempMeasurement.y * camera.zoom;
|
|
5142
5898
|
const position = VectorPool.get();
|
|
@@ -5149,13 +5905,18 @@ var FlowNode = class {
|
|
|
5149
5905
|
}
|
|
5150
5906
|
for (let i = 0; i < this.#widgets.length; i++) {
|
|
5151
5907
|
const widget = this.#widgets[i];
|
|
5908
|
+
if (widget.Hidden?.()) {
|
|
5909
|
+
this.#widgetPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
|
|
5910
|
+
continue;
|
|
5911
|
+
}
|
|
5152
5912
|
const widgetSize = widget.Size();
|
|
5153
|
-
const
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5913
|
+
const box = {
|
|
5914
|
+
Position: { x: nodeBounds.Position.x + scaledPadding, y: startY },
|
|
5915
|
+
Size: { x: nodeBounds.Size.x - scaledPadding * 2, y: widgetSize.y * camera.zoom }
|
|
5916
|
+
};
|
|
5917
|
+
widget.Draw(ctx, box, camera.zoom, mousePosition);
|
|
5918
|
+
this.#widgetPositions.Push(box);
|
|
5919
|
+
startY += box.Size.y + scaledElementSpacing;
|
|
5159
5920
|
}
|
|
5160
5921
|
const messageStart = VectorPool.get();
|
|
5161
5922
|
messageStart.x = nodeBounds.Position.x + nodeBounds.Size.x / 2;
|
|
@@ -5192,6 +5953,13 @@ var FlowNode = class {
|
|
|
5192
5953
|
record(this.#outputPortPositions, this.#portOffsets.output);
|
|
5193
5954
|
}
|
|
5194
5955
|
layoutPortsWithoutDrawing(bounds, zoom) {
|
|
5956
|
+
if (!this.placePorts(bounds, zoom)) {
|
|
5957
|
+
return false;
|
|
5958
|
+
}
|
|
5959
|
+
this.#widgetPositions.Clear();
|
|
5960
|
+
return true;
|
|
5961
|
+
}
|
|
5962
|
+
placePorts(bounds, zoom) {
|
|
5195
5963
|
if (this.#portOffsets.input.length === 0 && this.#portOffsets.output.length === 0) {
|
|
5196
5964
|
return false;
|
|
5197
5965
|
}
|
|
@@ -5213,7 +5981,6 @@ var FlowNode = class {
|
|
|
5213
5981
|
};
|
|
5214
5982
|
project(this.#portOffsets.input, this.#inputPortPositions);
|
|
5215
5983
|
project(this.#portOffsets.output, this.#outputPortPositions);
|
|
5216
|
-
this.#widgetPositions.Clear();
|
|
5217
5984
|
return true;
|
|
5218
5985
|
}
|
|
5219
5986
|
};
|
|
@@ -5405,33 +6172,33 @@ var CodeBlockEntry = class {
|
|
|
5405
6172
|
}
|
|
5406
6173
|
render(ctx, position, scale, maxWidth) {
|
|
5407
6174
|
this.#calculateLayout(ctx, maxWidth);
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
}
|
|
5414
|
-
const y = position.y + max + scale * 5;
|
|
6175
|
+
const padding = Theme.Note.CodeBlock.Padding * scale;
|
|
6176
|
+
const lineCount = this.#calculatedEntries.Count();
|
|
6177
|
+
const lastTop = lineCount === 0 ? 0 : this.#calculatedPositions.At(lineCount - 1).y;
|
|
6178
|
+
const contentHeight = lineCount === 0 ? 0 : (lastTop + this.#text.style().getSize()) * scale;
|
|
6179
|
+
const height2 = contentHeight + padding * 2;
|
|
5415
6180
|
ctx.fillStyle = Theme.Note.CodeBlock.BackgroundColor;
|
|
5416
6181
|
ctx.beginPath();
|
|
5417
6182
|
ctx.roundRect(
|
|
5418
6183
|
position.x,
|
|
5419
6184
|
position.y,
|
|
5420
6185
|
maxWidth * scale,
|
|
5421
|
-
|
|
6186
|
+
height2,
|
|
5422
6187
|
Theme.Note.CodeBlock.BorderRadius * scale
|
|
5423
6188
|
);
|
|
5424
6189
|
ctx.fill();
|
|
5425
|
-
|
|
6190
|
+
const baseline = ctx.textBaseline;
|
|
6191
|
+
ctx.textBaseline = "top";
|
|
6192
|
+
for (let i = 0; i < lineCount; i++) {
|
|
5426
6193
|
const entry = this.#calculatedEntries.At(i);
|
|
5427
6194
|
const pos = this.#calculatedPositions.At(i);
|
|
5428
6195
|
entry.render(ctx, scale, {
|
|
5429
6196
|
x: pos.x * scale + position.x + padding,
|
|
5430
6197
|
y: pos.y * scale + position.y + padding
|
|
5431
6198
|
});
|
|
5432
|
-
max = Math.max(max, pos.y * scale);
|
|
5433
6199
|
}
|
|
5434
|
-
|
|
6200
|
+
ctx.textBaseline = baseline;
|
|
6201
|
+
return height2;
|
|
5435
6202
|
}
|
|
5436
6203
|
};
|
|
5437
6204
|
var UnorderedListMarkdownEntry = class {
|
|
@@ -5809,9 +6576,10 @@ var MarkdownSyntaxParser = class {
|
|
|
5809
6576
|
this.#inc();
|
|
5810
6577
|
this.#inc();
|
|
5811
6578
|
this.#inc();
|
|
5812
|
-
const codeBlockText = this.#originalText.substring(start, end);
|
|
6579
|
+
const codeBlockText = this.#originalText.substring(start, end).replace(/\r?\n$/, "");
|
|
5813
6580
|
const entryText = new Text(codeBlockText);
|
|
5814
6581
|
this.#assignStandardStyling([entryText]);
|
|
6582
|
+
entryText.setColor(Theme.Note.CodeBlock.FontColor);
|
|
5815
6583
|
return new CodeBlockEntry(entryText);
|
|
5816
6584
|
}
|
|
5817
6585
|
parse() {
|
|
@@ -5866,6 +6634,7 @@ var BOX_SIZE = 10;
|
|
|
5866
6634
|
var FlowNote = class {
|
|
5867
6635
|
#originalText;
|
|
5868
6636
|
#document;
|
|
6637
|
+
#parsedForTheme = -1;
|
|
5869
6638
|
#width;
|
|
5870
6639
|
#edittingStyle;
|
|
5871
6640
|
// Runtime ================================================================
|
|
@@ -5908,6 +6677,7 @@ var FlowNote = class {
|
|
|
5908
6677
|
setText(text) {
|
|
5909
6678
|
this.#originalText = text;
|
|
5910
6679
|
this.#document = BuildMarkdown(this.#originalText);
|
|
6680
|
+
this.#parsedForTheme = themeVersion();
|
|
5911
6681
|
for (let i = 0; i < this.#contentChangeCallbacks.length; i++) {
|
|
5912
6682
|
this.#contentChangeCallbacks[i](this, text);
|
|
5913
6683
|
}
|
|
@@ -5995,6 +6765,10 @@ var FlowNote = class {
|
|
|
5995
6765
|
}
|
|
5996
6766
|
camera.graphSpaceToScreenSpace(this.#position, this.#tempPosition);
|
|
5997
6767
|
CopyVector2(this.#lastRenderedBox.Position, this.#tempPosition);
|
|
6768
|
+
if (this.#parsedForTheme !== themeVersion()) {
|
|
6769
|
+
this.#document = BuildMarkdown(this.#originalText);
|
|
6770
|
+
this.#parsedForTheme = themeVersion();
|
|
6771
|
+
}
|
|
5998
6772
|
const startY = this.#tempPosition.y;
|
|
5999
6773
|
const lineSpacing = Theme.Note.EntrySpacing * camera.zoom;
|
|
6000
6774
|
ctx.textAlign = "left" /* Left */;
|
|
@@ -6690,13 +7464,15 @@ var Minimap = class {
|
|
|
6690
7464
|
// src/graph.ts
|
|
6691
7465
|
function BuildBackgroundRenderer(backgroundColor) {
|
|
6692
7466
|
return (canvas, context, position, scale) => {
|
|
6693
|
-
context.fillStyle = backgroundColor;
|
|
7467
|
+
context.fillStyle = backgroundColor ?? Theme.Graph.BackgroundColor;
|
|
6694
7468
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
6695
|
-
const alpha =
|
|
7469
|
+
const alpha = Clamp01(scale - 0.3);
|
|
6696
7470
|
if (alpha <= 0) {
|
|
6697
7471
|
return;
|
|
6698
7472
|
}
|
|
6699
|
-
context.
|
|
7473
|
+
context.save();
|
|
7474
|
+
context.globalAlpha = alpha;
|
|
7475
|
+
context.fillStyle = Theme.Graph.DotColor;
|
|
6700
7476
|
const spacing = 100;
|
|
6701
7477
|
const pi2 = 2 * Math.PI;
|
|
6702
7478
|
const dotScale = 2 * scale;
|
|
@@ -6709,6 +7485,7 @@ function BuildBackgroundRenderer(backgroundColor) {
|
|
|
6709
7485
|
context.fill();
|
|
6710
7486
|
}
|
|
6711
7487
|
}
|
|
7488
|
+
context.restore();
|
|
6712
7489
|
};
|
|
6713
7490
|
}
|
|
6714
7491
|
var contextMenuGroup = "graph-context-menu";
|
|
@@ -6759,9 +7536,9 @@ var GraphView = class {
|
|
|
6759
7536
|
const results = {};
|
|
6760
7537
|
for (let i = 0; i < this.#subsystems.length; i++) {
|
|
6761
7538
|
TimeExecution("Render_Subsystem_" + i, () => {
|
|
6762
|
-
|
|
6763
|
-
if (
|
|
6764
|
-
|
|
7539
|
+
const layer = this.#subsystems[i].render(ctx, camera, mousePosition);
|
|
7540
|
+
if (layer?.cursorStyle) {
|
|
7541
|
+
results.cursorStyle = layer.cursorStyle;
|
|
6765
7542
|
}
|
|
6766
7543
|
});
|
|
6767
7544
|
}
|
|
@@ -6821,8 +7598,7 @@ var NodeFlowGraph = class {
|
|
|
6821
7598
|
if (config?.backgroundRenderer !== void 0) {
|
|
6822
7599
|
this.#backgroundRenderer = config?.backgroundRenderer;
|
|
6823
7600
|
} else {
|
|
6824
|
-
|
|
6825
|
-
this.#backgroundRenderer = BuildBackgroundRenderer(backgroundColor);
|
|
7601
|
+
this.#backgroundRenderer = BuildBackgroundRenderer(config?.backgroundColor);
|
|
6826
7602
|
}
|
|
6827
7603
|
this.#minimap = new Minimap(config?.minimap);
|
|
6828
7604
|
this.#mainNodeSubsystem.setConnectionReleasedHandler((dangling) => {
|
|
@@ -7288,11 +8064,15 @@ export {
|
|
|
7288
8064
|
ColorWidget,
|
|
7289
8065
|
Connection,
|
|
7290
8066
|
ContextMenuItemState,
|
|
8067
|
+
CursorStyle,
|
|
7291
8068
|
DefaultConnectionRenderer,
|
|
8069
|
+
DefaultTheme,
|
|
7292
8070
|
FlowNode,
|
|
7293
8071
|
FlowNote,
|
|
7294
8072
|
GlobalWidgetFactory,
|
|
8073
|
+
GraphView,
|
|
7295
8074
|
ImageWidget,
|
|
8075
|
+
InBox,
|
|
7296
8076
|
MessageType,
|
|
7297
8077
|
Minimap,
|
|
7298
8078
|
NodeFactory,
|
|
@@ -7302,16 +8082,28 @@ export {
|
|
|
7302
8082
|
Port,
|
|
7303
8083
|
PortType,
|
|
7304
8084
|
Publisher,
|
|
8085
|
+
RowWidget,
|
|
7305
8086
|
SliderWidget,
|
|
7306
8087
|
StringWidget,
|
|
7307
8088
|
TextWidget,
|
|
7308
8089
|
Theme,
|
|
7309
8090
|
ToggleWidget,
|
|
8091
|
+
ValueWidget,
|
|
8092
|
+
WidgetBase,
|
|
7310
8093
|
WidgetFactory,
|
|
8094
|
+
drawField,
|
|
8095
|
+
drawFieldBox,
|
|
8096
|
+
drawFieldText,
|
|
8097
|
+
editInPlace,
|
|
8098
|
+
formatNumber,
|
|
8099
|
+
onThemeChange,
|
|
7311
8100
|
portConfigTypes,
|
|
7312
8101
|
portsCompatible,
|
|
7313
8102
|
renderContinuously,
|
|
7314
8103
|
requestRender,
|
|
7315
|
-
|
|
8104
|
+
setTheme,
|
|
8105
|
+
themeVersion,
|
|
8106
|
+
typeSetsCompatible,
|
|
8107
|
+
widgetFont
|
|
7316
8108
|
};
|
|
7317
8109
|
//# sourceMappingURL=index.mjs.map
|