@elicdavis/node-flow 0.1.20 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +393 -226
- package/dist/index.d.ts +393 -226
- package/dist/index.js +1905 -1058
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1888 -1057
- package/dist/index.mjs.map +1 -1
- package/index.html +1134 -329
- package/package.json +1 -1
- package/src/connection.ts +30 -5
- package/src/contextMenu.ts +66 -34
- package/src/graph.ts +19 -14
- package/src/index.ts +26 -12
- package/src/markdown/entry.ts +11 -15
- package/src/markdown/syntaxParser.ts +2 -1
- package/src/node.ts +160 -101
- package/src/nodes/subsystem.ts +113 -29
- package/src/notes/note.ts +9 -1
- package/src/organize.ts +540 -111
- package/src/popup.ts +4 -2
- package/src/port.ts +83 -6
- package/src/styles/bevel.ts +31 -0
- package/src/styles/cursor.ts +1 -0
- package/src/styles/text.ts +4 -4
- package/src/theme.ts +179 -92
- package/src/types/text.ts +5 -1
- package/src/utils/color.ts +1 -1
- package/src/widgets/button.ts +52 -107
- package/src/widgets/color.ts +91 -150
- package/src/widgets/factory.ts +89 -46
- package/src/widgets/field.ts +99 -0
- package/src/widgets/image.ts +107 -165
- package/src/widgets/inplace.ts +136 -0
- package/src/widgets/number.ts +115 -154
- package/src/widgets/row.ts +138 -0
- package/src/widgets/slider.ts +101 -216
- package/src/widgets/string.ts +32 -130
- package/src/widgets/text.ts +81 -92
- package/src/widgets/toggle.ts +62 -207
- package/src/widgets/value.ts +127 -0
- package/src/widgets/widget.ts +173 -6
- package/test/context_menu.test.ts +82 -0
- package/test/cursor.test.ts +17 -0
- package/test/hidden_port.test.ts +125 -0
- package/test/markdown.test.ts +2 -2
- package/test/markdown_code_block.test.ts +58 -0
- package/test/multi_input.test.ts +124 -0
- package/test/organize.test.ts +117 -1
- package/test/support.ts +188 -0
- package/test/theme.test.ts +86 -0
- package/test/widget_click.test.ts +111 -97
- package/test/widget_construction.test.ts +5 -4
- package/test/widgets.test.ts +386 -0
- package/test/wire_follows_camera.test.ts +57 -0
package/dist/index.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,142 +1980,330 @@ 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
|
+
var open = /* @__PURE__ */ new Set();
|
|
2097
|
+
function placeEditsDuring(canvas, draw) {
|
|
2098
|
+
open.forEach((edit) => {
|
|
2099
|
+
if (edit.canvas === canvas) {
|
|
2100
|
+
edit.placed = false;
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
draw();
|
|
2104
|
+
open.forEach((edit) => {
|
|
2105
|
+
if (edit.canvas === canvas && !edit.placed) {
|
|
2106
|
+
edit.input.style.opacity = "0";
|
|
2107
|
+
edit.input.style.pointerEvents = "none";
|
|
2108
|
+
}
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
function editInPlace(config) {
|
|
2112
|
+
const canvas = config.canvas;
|
|
2113
|
+
if (canvas === null) {
|
|
2114
|
+
return null;
|
|
1962
2115
|
}
|
|
1963
|
-
|
|
2116
|
+
const input = document.createElement("input");
|
|
2117
|
+
input.type = "text";
|
|
2118
|
+
input.value = config.value;
|
|
2119
|
+
const edit = { canvas, input, placed: true };
|
|
2120
|
+
const place = (box, scale) => {
|
|
2121
|
+
const rect = canvas.getBoundingClientRect();
|
|
2122
|
+
const sx = canvas.width > 0 ? rect.width / canvas.width : 1;
|
|
2123
|
+
const sy = canvas.height > 0 ? rect.height / canvas.height : 1;
|
|
2124
|
+
const left = rect.left + box.Position.x * sx;
|
|
2125
|
+
const top = rect.top + box.Position.y * sy;
|
|
2126
|
+
const width2 = box.Size.x * sx;
|
|
2127
|
+
const height2 = box.Size.y * sy;
|
|
2128
|
+
const clip = [
|
|
2129
|
+
Math.max(0, rect.top - top),
|
|
2130
|
+
Math.max(0, left + width2 - (rect.left + rect.width)),
|
|
2131
|
+
Math.max(0, top + height2 - (rect.top + rect.height)),
|
|
2132
|
+
Math.max(0, rect.left - left)
|
|
2133
|
+
];
|
|
2134
|
+
edit.placed = true;
|
|
2135
|
+
input.style.cssText = [
|
|
2136
|
+
"position: fixed",
|
|
2137
|
+
`left: ${left}px`,
|
|
2138
|
+
`top: ${top}px`,
|
|
2139
|
+
`width: ${width2}px`,
|
|
2140
|
+
`height: ${height2}px`,
|
|
2141
|
+
`clip-path: inset(${clip.map((c) => `${c}px`).join(" ")})`,
|
|
2142
|
+
"box-sizing: border-box",
|
|
2143
|
+
"margin: 0",
|
|
2144
|
+
`padding: 0 ${Theme.Widget.Padding * scale}px`,
|
|
2145
|
+
`font: ${Theme.Widget.FontSize * scale * sy}px ${Theme.FontFamily}`,
|
|
2146
|
+
`color: ${Theme.Widget.FontColor}`,
|
|
2147
|
+
`background: ${Theme.Widget.Hover.BackgroundColor}`,
|
|
2148
|
+
`border: ${Theme.Widget.Border.Size * scale}px solid ${Theme.Widget.Border.Color}`,
|
|
2149
|
+
`border-radius: ${Theme.Widget.Border.Radius * scale}px`,
|
|
2150
|
+
"outline: none",
|
|
2151
|
+
"z-index: 1000"
|
|
2152
|
+
].join(";");
|
|
2153
|
+
};
|
|
2154
|
+
place(config.box, config.scale);
|
|
2155
|
+
let done = false;
|
|
2156
|
+
const finish = (keep) => {
|
|
2157
|
+
if (done) {
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
done = true;
|
|
2161
|
+
open.delete(edit);
|
|
2162
|
+
input.remove();
|
|
2163
|
+
if (keep) {
|
|
2164
|
+
config.onCommit(input.value);
|
|
2165
|
+
}
|
|
2166
|
+
requestRender();
|
|
2167
|
+
};
|
|
2168
|
+
input.addEventListener("keydown", (event) => {
|
|
2169
|
+
event.stopPropagation();
|
|
2170
|
+
if (event.key === "Enter") {
|
|
2171
|
+
event.preventDefault();
|
|
2172
|
+
finish(true);
|
|
2173
|
+
} else if (event.key === "Escape") {
|
|
2174
|
+
event.preventDefault();
|
|
2175
|
+
finish(false);
|
|
2176
|
+
}
|
|
2177
|
+
});
|
|
2178
|
+
input.addEventListener("blur", () => finish(true));
|
|
2179
|
+
open.add(edit);
|
|
2180
|
+
document.body.appendChild(input);
|
|
2181
|
+
input.focus();
|
|
2182
|
+
input.select();
|
|
2183
|
+
return { input, place };
|
|
2184
|
+
}
|
|
1964
2185
|
|
|
1965
2186
|
// src/widgets/widget.ts
|
|
1966
2187
|
var width = 150;
|
|
1967
2188
|
var height = 25;
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
2189
|
+
var WidgetBase = class {
|
|
2190
|
+
#hidden = false;
|
|
2191
|
+
#disabled = false;
|
|
2192
|
+
#hovered = false;
|
|
2193
|
+
#pressed = false;
|
|
2194
|
+
#box = { Position: { x: 0, y: 0 }, Size: { x: 0, y: 0 } };
|
|
2195
|
+
#canvas = null;
|
|
2196
|
+
#scale = 1;
|
|
2197
|
+
#editing = null;
|
|
2198
|
+
Draw(ctx, box, scale, mousePosition) {
|
|
2199
|
+
this.#box = { Position: { x: box.Position.x, y: box.Position.y }, Size: { x: box.Size.x, y: box.Size.y } };
|
|
2200
|
+
this.#canvas = ctx.canvas ?? null;
|
|
2201
|
+
this.#scale = scale;
|
|
2202
|
+
this.#hovered = !this.#disabled && mousePosition !== void 0 && InBox(box, mousePosition);
|
|
2203
|
+
if (this.#editing !== null) {
|
|
2204
|
+
if (this.#editing.input.isConnected) {
|
|
2205
|
+
this.#editing.place(this.#box, scale);
|
|
2206
|
+
} else {
|
|
2207
|
+
this.#editing = null;
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
ctx.save();
|
|
2211
|
+
if (this.#disabled) {
|
|
2212
|
+
ctx.globalAlpha *= 0.4;
|
|
2213
|
+
}
|
|
2214
|
+
this.render(ctx, box, scale);
|
|
2215
|
+
ctx.restore();
|
|
1971
2216
|
}
|
|
1972
|
-
|
|
2217
|
+
PointerDown(pointer) {
|
|
2218
|
+
if (this.#disabled) {
|
|
2219
|
+
return;
|
|
2220
|
+
}
|
|
2221
|
+
this.#pressed = true;
|
|
2222
|
+
this.onPointerDown(pointer);
|
|
2223
|
+
requestRender();
|
|
2224
|
+
}
|
|
2225
|
+
PointerMove(pointer) {
|
|
2226
|
+
if (!this.#pressed) {
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
this.onPointerMove(pointer);
|
|
2230
|
+
requestRender();
|
|
2231
|
+
}
|
|
2232
|
+
PointerUp(pointer) {
|
|
2233
|
+
if (!this.#pressed) {
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
this.#pressed = false;
|
|
2237
|
+
this.onPointerUp(pointer);
|
|
2238
|
+
if (pointer.inside) {
|
|
2239
|
+
this.onClick(pointer);
|
|
2240
|
+
}
|
|
2241
|
+
requestRender();
|
|
2242
|
+
}
|
|
2243
|
+
Cursor() {
|
|
2244
|
+
return "pointer" /* Pointer */;
|
|
2245
|
+
}
|
|
2246
|
+
Hidden() {
|
|
2247
|
+
return this.#hidden;
|
|
2248
|
+
}
|
|
2249
|
+
setHidden(hidden) {
|
|
2250
|
+
this.#hidden = hidden;
|
|
2251
|
+
requestRender();
|
|
2252
|
+
}
|
|
2253
|
+
Disabled() {
|
|
2254
|
+
return this.#disabled;
|
|
2255
|
+
}
|
|
2256
|
+
setDisabled(disabled) {
|
|
2257
|
+
this.#disabled = disabled;
|
|
2258
|
+
if (disabled) {
|
|
2259
|
+
this.#pressed = false;
|
|
2260
|
+
}
|
|
2261
|
+
requestRender();
|
|
2262
|
+
}
|
|
2263
|
+
hovered() {
|
|
2264
|
+
return this.#hovered;
|
|
2265
|
+
}
|
|
2266
|
+
pressed() {
|
|
2267
|
+
return this.#pressed;
|
|
2268
|
+
}
|
|
2269
|
+
/**
|
|
2270
|
+
* Where the widget was last drawn, in canvas pixels.
|
|
2271
|
+
*/
|
|
2272
|
+
box() {
|
|
2273
|
+
return this.#box;
|
|
2274
|
+
}
|
|
2275
|
+
canvas() {
|
|
2276
|
+
return this.#canvas;
|
|
2277
|
+
}
|
|
2278
|
+
scale() {
|
|
2279
|
+
return this.#scale;
|
|
2280
|
+
}
|
|
2281
|
+
editText(value, onCommit) {
|
|
2282
|
+
this.#editing = editInPlace({
|
|
2283
|
+
canvas: this.#canvas,
|
|
2284
|
+
box: this.#box,
|
|
2285
|
+
scale: this.#scale,
|
|
2286
|
+
value,
|
|
2287
|
+
onCommit
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
onPointerDown(pointer) {
|
|
2291
|
+
}
|
|
2292
|
+
onPointerMove(pointer) {
|
|
2293
|
+
}
|
|
2294
|
+
onPointerUp(pointer) {
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* A press and release that both landed on the widget.
|
|
2298
|
+
*/
|
|
2299
|
+
onClick(pointer) {
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
function startingWidgetValue(node, property, configured, fallback) {
|
|
2303
|
+
if (configured !== void 0) {
|
|
2304
|
+
return configured;
|
|
2305
|
+
}
|
|
2306
|
+
if (property !== void 0 && property !== null) {
|
|
1973
2307
|
const existing = node.getProperty(property);
|
|
1974
2308
|
if (existing !== void 0 && existing !== null) {
|
|
1975
2309
|
return existing;
|
|
@@ -1978,187 +2312,192 @@ function startingWidgetValue(node, property, configured, fallback) {
|
|
|
1978
2312
|
return fallback;
|
|
1979
2313
|
}
|
|
1980
2314
|
|
|
1981
|
-
// src/widgets/
|
|
1982
|
-
var
|
|
2315
|
+
// src/widgets/value.ts
|
|
2316
|
+
var ValueWidget = class extends WidgetBase {
|
|
1983
2317
|
#node;
|
|
1984
|
-
#
|
|
2318
|
+
#property;
|
|
1985
2319
|
#value;
|
|
1986
|
-
#
|
|
1987
|
-
#
|
|
1988
|
-
#text;
|
|
2320
|
+
#label;
|
|
2321
|
+
#width;
|
|
1989
2322
|
#callback;
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2323
|
+
#onInput;
|
|
2324
|
+
constructor(node, config, fallback) {
|
|
2325
|
+
super();
|
|
1993
2326
|
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));
|
|
2327
|
+
this.#property = config?.property;
|
|
2328
|
+
this.#label = config?.label ?? "";
|
|
2329
|
+
this.#width = config?.width ?? width;
|
|
2330
|
+
this.#value = startingWidgetValue(node, this.#property, config?.value, fallback);
|
|
2018
2331
|
this.#callback = config?.callback;
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2332
|
+
this.#onInput = config?.onInput;
|
|
2333
|
+
if (this.#property !== void 0) {
|
|
2334
|
+
node.setProperty(this.#property, this.#value);
|
|
2335
|
+
node.addPropertyChangeListener(this.#property, (_, next) => this.setValue(next));
|
|
2023
2336
|
}
|
|
2024
2337
|
}
|
|
2025
2338
|
Size() {
|
|
2026
|
-
return {
|
|
2339
|
+
return { x: this.#width, y: height };
|
|
2027
2340
|
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2341
|
+
value() {
|
|
2342
|
+
return this.#value;
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
* Shows a new value without treating it as a person's edit: no callback
|
|
2346
|
+
* and no edit event.
|
|
2347
|
+
*/
|
|
2348
|
+
setValue(value) {
|
|
2349
|
+
if (this.equal(value, this.#value)) {
|
|
2030
2350
|
return;
|
|
2031
2351
|
}
|
|
2032
|
-
this.#value =
|
|
2033
|
-
|
|
2034
|
-
|
|
2352
|
+
this.#value = value;
|
|
2353
|
+
this.valueChanged();
|
|
2354
|
+
if (this.#property !== void 0) {
|
|
2355
|
+
this.#node.setProperty(this.#property, value);
|
|
2035
2356
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2357
|
+
requestRender();
|
|
2358
|
+
}
|
|
2359
|
+
label() {
|
|
2360
|
+
return this.#label;
|
|
2361
|
+
}
|
|
2362
|
+
setLabel(label) {
|
|
2363
|
+
this.#label = label;
|
|
2364
|
+
requestRender();
|
|
2365
|
+
}
|
|
2366
|
+
node() {
|
|
2367
|
+
return this.#node;
|
|
2368
|
+
}
|
|
2369
|
+
input(value) {
|
|
2370
|
+
this.setValue(value);
|
|
2371
|
+
this.#onInput?.(value);
|
|
2372
|
+
}
|
|
2373
|
+
/**
|
|
2374
|
+
* Ends a person's edit. Nothing is reported when they ended where they
|
|
2375
|
+
* started.
|
|
2376
|
+
*/
|
|
2377
|
+
commit(value, startedAt) {
|
|
2378
|
+
this.setValue(value);
|
|
2379
|
+
if (this.equal(value, startedAt)) {
|
|
2380
|
+
return;
|
|
2038
2381
|
}
|
|
2039
|
-
this.#
|
|
2382
|
+
if (this.#property !== void 0) {
|
|
2383
|
+
this.#node.editProperty(this.#property, value);
|
|
2384
|
+
}
|
|
2385
|
+
this.#callback?.(value);
|
|
2040
2386
|
}
|
|
2041
|
-
|
|
2387
|
+
valueChanged() {
|
|
2042
2388
|
}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2389
|
+
equal(a, b) {
|
|
2390
|
+
return Object.is(a, b);
|
|
2391
|
+
}
|
|
2392
|
+
};
|
|
2393
|
+
|
|
2394
|
+
// src/widgets/number.ts
|
|
2395
|
+
var DRAG_THRESHOLD = 3;
|
|
2396
|
+
var NumberWidget = class extends ValueWidget {
|
|
2397
|
+
#step;
|
|
2398
|
+
#integer;
|
|
2399
|
+
#min;
|
|
2400
|
+
#max;
|
|
2401
|
+
#startX = 0;
|
|
2402
|
+
#startValue = 0;
|
|
2403
|
+
#dragging = false;
|
|
2404
|
+
#dragged = false;
|
|
2405
|
+
constructor(node, config) {
|
|
2406
|
+
super(node, config, 0);
|
|
2407
|
+
this.#step = config?.step;
|
|
2408
|
+
this.#integer = config?.integer === true;
|
|
2409
|
+
this.#min = config?.min ?? -Infinity;
|
|
2410
|
+
this.#max = config?.max ?? Infinity;
|
|
2411
|
+
}
|
|
2412
|
+
Cursor() {
|
|
2413
|
+
return "ew-resize" /* EWResize */;
|
|
2414
|
+
}
|
|
2415
|
+
render(ctx, box, scale) {
|
|
2416
|
+
drawField(ctx, box, scale, {
|
|
2417
|
+
input: true,
|
|
2418
|
+
hovered: this.hovered() || this.pressed(),
|
|
2419
|
+
label: this.label(),
|
|
2420
|
+
value: formatNumber(this.value())
|
|
2064
2421
|
});
|
|
2065
|
-
popup.Show();
|
|
2066
2422
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2423
|
+
onPointerDown(pointer) {
|
|
2424
|
+
this.#startX = pointer.position.x;
|
|
2425
|
+
this.#startValue = this.value();
|
|
2426
|
+
this.#dragging = false;
|
|
2427
|
+
this.#dragged = false;
|
|
2428
|
+
}
|
|
2429
|
+
onPointerMove(pointer) {
|
|
2430
|
+
const dx = pointer.position.x - this.#startX;
|
|
2431
|
+
if (!this.#dragging && Math.abs(dx) < DRAG_THRESHOLD) {
|
|
2432
|
+
return;
|
|
2433
|
+
}
|
|
2434
|
+
this.#dragging = true;
|
|
2435
|
+
this.input(this.#clean(this.#startValue + dx * this.#stepFor(this.#startValue)));
|
|
2436
|
+
}
|
|
2437
|
+
onPointerUp() {
|
|
2438
|
+
this.#dragged = this.#dragging;
|
|
2439
|
+
if (this.#dragging) {
|
|
2440
|
+
this.#dragging = false;
|
|
2441
|
+
this.commit(this.value(), this.#startValue);
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
onClick() {
|
|
2445
|
+
if (this.#dragged) {
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2448
|
+
const before = this.value();
|
|
2449
|
+
this.editText(String(before), (text) => {
|
|
2450
|
+
const parsed = Number.parseFloat(text);
|
|
2451
|
+
if (Number.isFinite(parsed)) {
|
|
2452
|
+
this.commit(this.#clean(parsed), before);
|
|
2080
2453
|
}
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
#stepFor(value) {
|
|
2457
|
+
if (this.#step !== void 0) {
|
|
2458
|
+
return this.#step;
|
|
2081
2459
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2460
|
+
return Math.max(Math.abs(value) * 0.01, this.#integer ? 0.1 : 0.01);
|
|
2461
|
+
}
|
|
2462
|
+
#clean(value) {
|
|
2463
|
+
const rounded = this.#integer ? Math.round(value) : value;
|
|
2464
|
+
return Math.min(this.#max, Math.max(this.#min, rounded));
|
|
2084
2465
|
}
|
|
2085
2466
|
};
|
|
2467
|
+
function formatNumber(value) {
|
|
2468
|
+
return "" + parseFloat(value.toPrecision(6));
|
|
2469
|
+
}
|
|
2086
2470
|
|
|
2087
2471
|
// src/widgets/button.ts
|
|
2088
|
-
var ButtonWidget = class {
|
|
2472
|
+
var ButtonWidget = class extends WidgetBase {
|
|
2089
2473
|
#text;
|
|
2090
|
-
#idleStyle;
|
|
2091
|
-
#hoverStyle;
|
|
2092
|
-
#clickStyle;
|
|
2093
2474
|
#callback;
|
|
2094
|
-
#
|
|
2475
|
+
#width;
|
|
2095
2476
|
constructor(config) {
|
|
2096
|
-
|
|
2477
|
+
super();
|
|
2478
|
+
this.#text = config?.text ?? "Button";
|
|
2097
2479
|
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
|
-
}));
|
|
2480
|
+
this.#width = config?.width ?? width;
|
|
2129
2481
|
}
|
|
2130
2482
|
Size() {
|
|
2131
|
-
return {
|
|
2483
|
+
return { x: this.#width, y: height };
|
|
2132
2484
|
}
|
|
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;
|
|
2485
|
+
text() {
|
|
2486
|
+
return this.#text;
|
|
2153
2487
|
}
|
|
2154
|
-
|
|
2155
|
-
this.#
|
|
2488
|
+
setText(text) {
|
|
2489
|
+
this.#text = text;
|
|
2490
|
+
requestRender();
|
|
2156
2491
|
}
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
this
|
|
2161
|
-
|
|
2492
|
+
render(ctx, box, scale) {
|
|
2493
|
+
drawField(ctx, box, scale, {
|
|
2494
|
+
hovered: this.hovered(),
|
|
2495
|
+
pressed: this.pressed(),
|
|
2496
|
+
value: this.#text
|
|
2497
|
+
});
|
|
2498
|
+
}
|
|
2499
|
+
onClick() {
|
|
2500
|
+
this.#callback?.();
|
|
2162
2501
|
}
|
|
2163
2502
|
};
|
|
2164
2503
|
|
|
@@ -2173,500 +2512,208 @@ function contrastColor(color) {
|
|
|
2173
2512
|
}
|
|
2174
2513
|
return "white";
|
|
2175
2514
|
}
|
|
2176
|
-
var
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2515
|
+
var picker = null;
|
|
2516
|
+
function sharedPicker() {
|
|
2517
|
+
if (picker === null || !picker.isConnected) {
|
|
2518
|
+
picker = document.createElement("input");
|
|
2519
|
+
picker.type = "color";
|
|
2520
|
+
document.body.appendChild(picker);
|
|
2521
|
+
}
|
|
2522
|
+
return picker;
|
|
2523
|
+
}
|
|
2524
|
+
var ColorWidget = class extends ValueWidget {
|
|
2180
2525
|
#contrast;
|
|
2181
|
-
#textBoxStyle;
|
|
2182
|
-
#callback;
|
|
2183
2526
|
constructor(node, config) {
|
|
2184
|
-
|
|
2185
|
-
this.#
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
this.#contrast = contrastColor(this
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2527
|
+
super(node, config, "#000000");
|
|
2528
|
+
this.#contrast = contrastColor(this.value());
|
|
2529
|
+
}
|
|
2530
|
+
valueChanged() {
|
|
2531
|
+
this.#contrast = contrastColor(this.value());
|
|
2532
|
+
}
|
|
2533
|
+
render(ctx, box, scale) {
|
|
2534
|
+
drawField(ctx, box, scale, {
|
|
2535
|
+
input: true,
|
|
2536
|
+
hovered: this.hovered(),
|
|
2537
|
+
label: this.label(),
|
|
2538
|
+
value: this.value(),
|
|
2539
|
+
background: this.value(),
|
|
2540
|
+
border: this.#contrast,
|
|
2541
|
+
textColor: this.#contrast
|
|
2198
2542
|
});
|
|
2199
|
-
if (this.#nodeProperty !== void 0) {
|
|
2200
|
-
this.#node.addPropertyChangeListener(this.#nodeProperty, (oldVal, newVal) => {
|
|
2201
|
-
this.Set(newVal);
|
|
2202
|
-
});
|
|
2203
|
-
}
|
|
2204
2543
|
}
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
Set(value) {
|
|
2209
|
-
if (this.#value === value) {
|
|
2544
|
+
onClick() {
|
|
2545
|
+
const canvas = this.canvas();
|
|
2546
|
+
if (canvas === null) {
|
|
2210
2547
|
return;
|
|
2211
2548
|
}
|
|
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;
|
|
2549
|
+
const before = this.value();
|
|
2550
|
+
const alpha = before.slice(7);
|
|
2551
|
+
const box = this.box();
|
|
2552
|
+
const rect = canvas.getBoundingClientRect();
|
|
2553
|
+
const sx = canvas.width > 0 ? rect.width / canvas.width : 1;
|
|
2554
|
+
const sy = canvas.height > 0 ? rect.height / canvas.height : 1;
|
|
2555
|
+
const input = sharedPicker();
|
|
2556
|
+
input.style.cssText = [
|
|
2557
|
+
"position: fixed",
|
|
2558
|
+
`left: ${rect.left + box.Position.x * sx}px`,
|
|
2559
|
+
`top: ${rect.top + box.Position.y * sy}px`,
|
|
2560
|
+
`width: ${box.Size.x * sx}px`,
|
|
2561
|
+
`height: ${box.Size.y * sy}px`,
|
|
2562
|
+
"opacity: 0",
|
|
2563
|
+
"pointer-events: none",
|
|
2564
|
+
"border: 0",
|
|
2565
|
+
"padding: 0"
|
|
2566
|
+
].join(";");
|
|
2567
|
+
input.value = before.slice(0, 7);
|
|
2568
|
+
input.oninput = () => this.input(input.value + alpha);
|
|
2569
|
+
input.onchange = () => this.commit(input.value + alpha, before);
|
|
2570
|
+
input.getBoundingClientRect();
|
|
2571
|
+
input.click();
|
|
2264
2572
|
}
|
|
2265
2573
|
};
|
|
2266
2574
|
|
|
2267
2575
|
// src/widgets/slider.ts
|
|
2268
|
-
var SliderWidget = class {
|
|
2269
|
-
// Data Variables =========================================================
|
|
2576
|
+
var SliderWidget = class extends ValueWidget {
|
|
2270
2577
|
#min;
|
|
2271
2578
|
#max;
|
|
2272
|
-
#value;
|
|
2273
|
-
#text;
|
|
2274
2579
|
#snapIncrement;
|
|
2275
|
-
#node;
|
|
2276
|
-
#nodeProperty;
|
|
2277
|
-
// Callbacks ==============================================================
|
|
2278
|
-
#change;
|
|
2279
|
-
#release;
|
|
2280
|
-
// Styling Variables ======================================================
|
|
2281
|
-
#textStyle;
|
|
2282
|
-
#backgroundColor;
|
|
2283
|
-
#borderColor;
|
|
2284
2580
|
#fillColor;
|
|
2285
|
-
|
|
2286
|
-
#lastMousePosition;
|
|
2287
|
-
#clickStartMousePosition;
|
|
2288
|
-
#clicking;
|
|
2581
|
+
#startValue = 0;
|
|
2289
2582
|
constructor(node, config) {
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
this.#
|
|
2583
|
+
const min = config?.min ?? 0;
|
|
2584
|
+
const max = config?.max ?? 1;
|
|
2585
|
+
super(node, { ...config, value: config?.value === void 0 ? void 0 : Clamp(config.value, min, max) }, min);
|
|
2586
|
+
this.#min = min;
|
|
2587
|
+
this.#max = max;
|
|
2294
2588
|
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 (Object.is(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
|
-
}
|
|
2328
|
-
}
|
|
2329
|
-
Size() {
|
|
2330
|
-
return { "x": width, "y": height };
|
|
2589
|
+
this.#fillColor = config?.fillColor;
|
|
2331
2590
|
}
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
CopyVector2(this.#clickStartMousePosition, this.#lastMousePosition);
|
|
2591
|
+
Cursor() {
|
|
2592
|
+
return "ew-resize" /* EWResize */;
|
|
2335
2593
|
}
|
|
2336
|
-
|
|
2337
|
-
this.#
|
|
2338
|
-
if (this.#release !== void 0) {
|
|
2339
|
-
this.#release(this.#value);
|
|
2340
|
-
}
|
|
2594
|
+
setValue(value) {
|
|
2595
|
+
super.setValue(Clamp(value, this.#min, this.#max));
|
|
2341
2596
|
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
const
|
|
2345
|
-
const
|
|
2346
|
-
ctx.fillStyle = this.#
|
|
2347
|
-
ctx.strokeStyle = this.#borderColor;
|
|
2348
|
-
ctx.lineWidth = scaledBorderThickness;
|
|
2597
|
+
render(ctx, box, scale) {
|
|
2598
|
+
drawFieldBox(ctx, box, scale, { input: true, hovered: this.hovered() || this.pressed(), value: "" });
|
|
2599
|
+
const border = Theme.Widget.Border.Size * scale;
|
|
2600
|
+
const fill = Clamp01((this.value() - this.#min) / (this.#max - this.#min));
|
|
2601
|
+
ctx.fillStyle = this.#fillColor ?? Theme.Widget.Slider.FillColor;
|
|
2349
2602
|
ctx.beginPath();
|
|
2350
2603
|
ctx.roundRect(
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2604
|
+
box.Position.x + border / 2,
|
|
2605
|
+
box.Position.y + border / 2,
|
|
2606
|
+
Math.max(0, box.Size.x * fill - border),
|
|
2607
|
+
box.Size.y - border,
|
|
2355
2608
|
Theme.Widget.Border.Radius * scale
|
|
2356
2609
|
);
|
|
2357
2610
|
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
|
-
}
|
|
2611
|
+
drawFieldText(ctx, box, scale, {
|
|
2612
|
+
input: true,
|
|
2613
|
+
hovered: false,
|
|
2614
|
+
label: this.label(),
|
|
2615
|
+
value: formatNumber(Number(this.value().toFixed(3)))
|
|
2616
|
+
});
|
|
2617
|
+
}
|
|
2618
|
+
onPointerDown(pointer) {
|
|
2619
|
+
this.#startValue = this.value();
|
|
2620
|
+
this.input(this.#valueAt(pointer.position.x));
|
|
2621
|
+
}
|
|
2622
|
+
onPointerMove(pointer) {
|
|
2623
|
+
this.input(this.#valueAt(pointer.position.x));
|
|
2624
|
+
}
|
|
2625
|
+
onPointerUp() {
|
|
2626
|
+
this.commit(this.value(), this.#startValue);
|
|
2627
|
+
}
|
|
2628
|
+
#valueAt(x) {
|
|
2629
|
+
const box = this.box();
|
|
2630
|
+
const border = Theme.Widget.Border.Size * this.scale();
|
|
2631
|
+
const min = box.Position.x + border / 2;
|
|
2632
|
+
const max = min + box.Size.x - border;
|
|
2633
|
+
const p = Clamp01((x - min) / (max - min));
|
|
2634
|
+
let value = p * (this.#max - this.#min) + this.#min;
|
|
2635
|
+
if (this.#snapIncrement !== void 0) {
|
|
2636
|
+
value = Math.round(value / this.#snapIncrement) * this.#snapIncrement;
|
|
2390
2637
|
}
|
|
2391
|
-
return
|
|
2392
|
-
Position: { x: position.x, y: position.y },
|
|
2393
|
-
Size: {
|
|
2394
|
-
x: scaledWidth,
|
|
2395
|
-
y: scaledHeight
|
|
2396
|
-
}
|
|
2397
|
-
};
|
|
2638
|
+
return Clamp(value, this.#min, this.#max);
|
|
2398
2639
|
}
|
|
2399
2640
|
};
|
|
2400
2641
|
|
|
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
2642
|
// src/widgets/string.ts
|
|
2427
|
-
var StringWidget = class {
|
|
2428
|
-
#value;
|
|
2429
|
-
#idleStyle;
|
|
2430
|
-
#hoverStyle;
|
|
2431
|
-
#callback;
|
|
2432
|
-
#node;
|
|
2433
|
-
#nodeProperty;
|
|
2643
|
+
var StringWidget = class extends ValueWidget {
|
|
2434
2644
|
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
|
-
}
|
|
2645
|
+
super(node, config, "");
|
|
2467
2646
|
}
|
|
2468
|
-
|
|
2469
|
-
return
|
|
2647
|
+
Cursor() {
|
|
2648
|
+
return "text" /* Text */;
|
|
2470
2649
|
}
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
this.#node.setProperty(this.#nodeProperty, this.#value);
|
|
2478
|
-
}
|
|
2479
|
-
if (this.#callback !== void 0) {
|
|
2480
|
-
this.#callback(this.#value);
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
|
-
ClickStart() {
|
|
2484
|
-
}
|
|
2485
|
-
ClickEnd() {
|
|
2486
|
-
const popup = SetStringPopup({
|
|
2487
|
-
title: "Set String",
|
|
2488
|
-
startingValue: this.#value,
|
|
2489
|
-
onUpdate: (value) => {
|
|
2490
|
-
this.Set(value);
|
|
2491
|
-
}
|
|
2650
|
+
render(ctx, box, scale) {
|
|
2651
|
+
drawField(ctx, box, scale, {
|
|
2652
|
+
input: true,
|
|
2653
|
+
hovered: this.hovered(),
|
|
2654
|
+
label: this.label(),
|
|
2655
|
+
value: this.value()
|
|
2492
2656
|
});
|
|
2493
|
-
popup.Show();
|
|
2494
2657
|
}
|
|
2495
|
-
|
|
2496
|
-
const
|
|
2497
|
-
|
|
2498
|
-
Size: {
|
|
2499
|
-
x: width * scale,
|
|
2500
|
-
y: height * scale
|
|
2501
|
-
}
|
|
2502
|
-
};
|
|
2503
|
-
CopyVector2(box.Position, position);
|
|
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;
|
|
2658
|
+
onClick() {
|
|
2659
|
+
const before = this.value();
|
|
2660
|
+
this.editText(before, (text) => this.commit(text, before));
|
|
2512
2661
|
}
|
|
2513
2662
|
};
|
|
2514
2663
|
|
|
2515
2664
|
// 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;
|
|
2665
|
+
var ToggleWidget = class extends ValueWidget {
|
|
2666
|
+
#onColor;
|
|
2667
|
+
#offColor;
|
|
2592
2668
|
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);
|
|
2669
|
+
super(node, { ...config, label: config?.label ?? "Toggle" }, false);
|
|
2670
|
+
this.#onColor = config?.onColor ?? "#00FF00";
|
|
2671
|
+
this.#offColor = config?.offColor ?? "#004400";
|
|
2640
2672
|
}
|
|
2641
2673
|
Toggle() {
|
|
2642
|
-
this.
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2674
|
+
const before = this.value();
|
|
2675
|
+
this.commit(!before, before);
|
|
2676
|
+
}
|
|
2677
|
+
render(ctx, box, scale) {
|
|
2678
|
+
drawFieldBox(ctx, box, scale, { hovered: this.hovered(), value: "" });
|
|
2679
|
+
const light = Math.min(box.Size.x, box.Size.y) * 0.6;
|
|
2680
|
+
const inset = (box.Size.y - light) / 2;
|
|
2681
|
+
const on = this.value();
|
|
2682
|
+
ctx.fillStyle = on ? this.#onColor : this.#offColor;
|
|
2683
|
+
ctx.beginPath();
|
|
2684
|
+
ctx.roundRect(box.Position.x + inset, box.Position.y + inset, light, light, Theme.Widget.Border.Radius * scale);
|
|
2685
|
+
if (on) {
|
|
2686
|
+
ctx.shadowBlur = 15 * scale;
|
|
2687
|
+
ctx.shadowColor = this.#onColor;
|
|
2654
2688
|
}
|
|
2689
|
+
ctx.fill();
|
|
2690
|
+
ctx.shadowBlur = 0;
|
|
2691
|
+
const textLeft = box.Position.x + inset * 2 + light;
|
|
2692
|
+
ctx.font = widgetFont(scale);
|
|
2693
|
+
ctx.textAlign = "center";
|
|
2694
|
+
ctx.textBaseline = "middle";
|
|
2695
|
+
ctx.fillStyle = Theme.Widget.FontColor;
|
|
2696
|
+
const room = box.Position.x + box.Size.x - textLeft - inset;
|
|
2697
|
+
ctx.fillText(
|
|
2698
|
+
fitString(ctx, this.label(), room),
|
|
2699
|
+
textLeft + room / 2,
|
|
2700
|
+
box.Position.y + box.Size.y / 2
|
|
2701
|
+
);
|
|
2655
2702
|
}
|
|
2656
|
-
|
|
2703
|
+
onClick() {
|
|
2657
2704
|
this.Toggle();
|
|
2658
2705
|
}
|
|
2659
|
-
ClickEnd() {
|
|
2660
|
-
}
|
|
2661
2706
|
};
|
|
2662
2707
|
|
|
2663
2708
|
// src/widgets/image.ts
|
|
2664
|
-
var ImageWidget = class {
|
|
2709
|
+
var ImageWidget = class extends WidgetBase {
|
|
2665
2710
|
#url;
|
|
2711
|
+
#ownedUrl;
|
|
2666
2712
|
#image;
|
|
2667
2713
|
#maxWidth;
|
|
2668
2714
|
#maxHeight;
|
|
2669
2715
|
constructor(config) {
|
|
2716
|
+
super();
|
|
2670
2717
|
this.#maxWidth = config?.maxWidth === void 0 ? 150 : config?.maxWidth;
|
|
2671
2718
|
this.#maxHeight = config?.maxHeight === void 0 ? 150 : config?.maxHeight;
|
|
2672
2719
|
if (config?.image) {
|
|
@@ -2676,16 +2723,23 @@ var ImageWidget = class {
|
|
|
2676
2723
|
}
|
|
2677
2724
|
}
|
|
2678
2725
|
SetBlob(blob) {
|
|
2679
|
-
const
|
|
2680
|
-
|
|
2681
|
-
this
|
|
2726
|
+
const url = URL.createObjectURL(blob);
|
|
2727
|
+
this.SetUrl(url);
|
|
2728
|
+
this.#ownedUrl = url;
|
|
2682
2729
|
}
|
|
2683
2730
|
SetUrl(url) {
|
|
2731
|
+
if (this.#ownedUrl !== void 0) {
|
|
2732
|
+
URL.revokeObjectURL(this.#ownedUrl);
|
|
2733
|
+
this.#ownedUrl = void 0;
|
|
2734
|
+
}
|
|
2684
2735
|
this.#image = void 0;
|
|
2685
2736
|
this.#url = url;
|
|
2686
2737
|
const img = document.createElement("img");
|
|
2687
2738
|
img.src = url;
|
|
2688
2739
|
img.onload = () => {
|
|
2740
|
+
if (this.#url !== url) {
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
2689
2743
|
this.#image = img;
|
|
2690
2744
|
requestRender();
|
|
2691
2745
|
};
|
|
@@ -2693,6 +2747,9 @@ var ImageWidget = class {
|
|
|
2693
2747
|
console.log("error loading image:", url, event);
|
|
2694
2748
|
};
|
|
2695
2749
|
}
|
|
2750
|
+
Cursor() {
|
|
2751
|
+
return "default" /* Default */;
|
|
2752
|
+
}
|
|
2696
2753
|
Size() {
|
|
2697
2754
|
if (this.#image === void 0) {
|
|
2698
2755
|
return { "x": 0, "y": 0 };
|
|
@@ -2712,88 +2769,178 @@ var ImageWidget = class {
|
|
|
2712
2769
|
"y": adjust * this.#image.height
|
|
2713
2770
|
};
|
|
2714
2771
|
}
|
|
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);
|
|
2772
|
+
render(ctx, box, scale) {
|
|
2729
2773
|
if (!this.#image) {
|
|
2730
|
-
return
|
|
2774
|
+
return;
|
|
2731
2775
|
}
|
|
2776
|
+
const size = this.Size();
|
|
2777
|
+
const w = size.x * scale;
|
|
2778
|
+
const h = size.y * scale;
|
|
2732
2779
|
ctx.drawImage(
|
|
2733
2780
|
this.#image,
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2781
|
+
box.Position.x + (box.Size.x - w) / 2,
|
|
2782
|
+
box.Position.y,
|
|
2783
|
+
w,
|
|
2784
|
+
h
|
|
2738
2785
|
);
|
|
2739
|
-
return box;
|
|
2740
2786
|
}
|
|
2741
2787
|
};
|
|
2742
2788
|
|
|
2743
2789
|
// src/widgets/text.ts
|
|
2744
|
-
var TextWidget = class {
|
|
2790
|
+
var TextWidget = class extends WidgetBase {
|
|
2745
2791
|
#value;
|
|
2746
|
-
#callback;
|
|
2747
|
-
#node;
|
|
2748
|
-
#nodeProperty;
|
|
2749
2792
|
#size;
|
|
2793
|
+
#styleConfig;
|
|
2794
|
+
#styledForTheme = -1;
|
|
2795
|
+
#followTheme() {
|
|
2796
|
+
if (this.#styledForTheme === themeVersion()) {
|
|
2797
|
+
return;
|
|
2798
|
+
}
|
|
2799
|
+
this.#styledForTheme = themeVersion();
|
|
2800
|
+
const style = this.#value.style();
|
|
2801
|
+
style.setColor(this.#styleConfig?.color ?? Theme.Widget.FontColor);
|
|
2802
|
+
style.setSize(this.#styleConfig?.size ?? Theme.Note.FontSize);
|
|
2803
|
+
}
|
|
2750
2804
|
constructor(node, config) {
|
|
2805
|
+
super();
|
|
2751
2806
|
this.#size = Zero();
|
|
2752
|
-
this.#
|
|
2753
|
-
this.#
|
|
2754
|
-
this.#
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
this.Set(newVal);
|
|
2764
|
-
});
|
|
2807
|
+
this.#styleConfig = config?.textBoxStyle;
|
|
2808
|
+
this.#value = new Text("", TextStyleFallback(config?.textBoxStyle, {}));
|
|
2809
|
+
this.#followTheme();
|
|
2810
|
+
this.setText(config?.value ?? "");
|
|
2811
|
+
const property = config?.property;
|
|
2812
|
+
if (property !== void 0) {
|
|
2813
|
+
const existing = node.getProperty(property);
|
|
2814
|
+
if (existing !== void 0 && existing !== null) {
|
|
2815
|
+
this.setText(String(existing));
|
|
2816
|
+
}
|
|
2817
|
+
node.addPropertyChangeListener(property, (_, next) => this.setText(String(next)));
|
|
2765
2818
|
}
|
|
2766
2819
|
}
|
|
2820
|
+
Cursor() {
|
|
2821
|
+
return "default" /* Default */;
|
|
2822
|
+
}
|
|
2767
2823
|
Size() {
|
|
2768
2824
|
return this.#size;
|
|
2769
2825
|
}
|
|
2770
|
-
|
|
2826
|
+
text() {
|
|
2827
|
+
return this.#value.get();
|
|
2828
|
+
}
|
|
2829
|
+
setText(value) {
|
|
2771
2830
|
if (this.#value.get() === value) {
|
|
2772
2831
|
return;
|
|
2773
2832
|
}
|
|
2774
2833
|
this.#value.set(value);
|
|
2775
|
-
|
|
2776
|
-
|
|
2834
|
+
requestRender();
|
|
2835
|
+
}
|
|
2836
|
+
render(ctx, box, scale) {
|
|
2837
|
+
this.#followTheme();
|
|
2838
|
+
this.#value.size(ctx, 1, this.#size);
|
|
2839
|
+
ctx.textAlign = "left";
|
|
2840
|
+
this.#value.render(ctx, scale, box.Position);
|
|
2841
|
+
}
|
|
2842
|
+
};
|
|
2843
|
+
|
|
2844
|
+
// src/widgets/row.ts
|
|
2845
|
+
var RowWidget = class extends WidgetBase {
|
|
2846
|
+
#children;
|
|
2847
|
+
#grow;
|
|
2848
|
+
#gap;
|
|
2849
|
+
#boxes = [];
|
|
2850
|
+
#captured = null;
|
|
2851
|
+
#capturedBox = null;
|
|
2852
|
+
#mouse;
|
|
2853
|
+
constructor(children, config) {
|
|
2854
|
+
super();
|
|
2855
|
+
this.#children = children;
|
|
2856
|
+
this.#grow = config?.grow ?? children.map(() => 1);
|
|
2857
|
+
this.#gap = config?.gap ?? Theme.Widget.Gap;
|
|
2858
|
+
}
|
|
2859
|
+
children() {
|
|
2860
|
+
return this.#children;
|
|
2861
|
+
}
|
|
2862
|
+
Size() {
|
|
2863
|
+
const shown = this.#shown();
|
|
2864
|
+
let x = 0;
|
|
2865
|
+
let y = 0;
|
|
2866
|
+
for (const child of shown) {
|
|
2867
|
+
const size = child.Size();
|
|
2868
|
+
x += size.x;
|
|
2869
|
+
y = Math.max(y, size.y);
|
|
2870
|
+
}
|
|
2871
|
+
x += Math.max(0, shown.length - 1) * this.#gap;
|
|
2872
|
+
return { x, y };
|
|
2873
|
+
}
|
|
2874
|
+
Cursor() {
|
|
2875
|
+
const over = this.#captured ?? this.#childAt(this.#mouse);
|
|
2876
|
+
return over?.Cursor?.() ?? "default" /* Default */;
|
|
2877
|
+
}
|
|
2878
|
+
Draw(ctx, box, scale, mousePosition) {
|
|
2879
|
+
this.#mouse = mousePosition;
|
|
2880
|
+
super.Draw(ctx, box, scale, mousePosition);
|
|
2881
|
+
}
|
|
2882
|
+
render(ctx, box, scale) {
|
|
2883
|
+
const shown = this.#shown();
|
|
2884
|
+
const gap = this.#gap * scale;
|
|
2885
|
+
const natural = shown.reduce((sum, child) => sum + child.Size().x * scale, 0);
|
|
2886
|
+
const extra = Math.max(0, box.Size.x - natural - gap * Math.max(0, shown.length - 1));
|
|
2887
|
+
const totalGrow = shown.reduce((sum, child) => sum + this.#growOf(child), 0);
|
|
2888
|
+
this.#boxes = this.#children.map(() => null);
|
|
2889
|
+
let x = box.Position.x;
|
|
2890
|
+
for (const child of shown) {
|
|
2891
|
+
const size = child.Size();
|
|
2892
|
+
const share = totalGrow > 0 ? extra * (this.#growOf(child) / totalGrow) : 0;
|
|
2893
|
+
const childBox = {
|
|
2894
|
+
Position: { x, y: box.Position.y + (box.Size.y - size.y * scale) / 2 },
|
|
2895
|
+
Size: { x: size.x * scale + share, y: size.y * scale }
|
|
2896
|
+
};
|
|
2897
|
+
this.#boxes[this.#children.indexOf(child)] = childBox;
|
|
2898
|
+
child.Draw(ctx, childBox, scale, this.#captured === null || this.#captured === child ? this.#mouse : void 0);
|
|
2899
|
+
x += childBox.Size.x + gap;
|
|
2777
2900
|
}
|
|
2778
|
-
|
|
2779
|
-
|
|
2901
|
+
}
|
|
2902
|
+
onPointerDown(pointer) {
|
|
2903
|
+
const child = this.#childAt(pointer.position);
|
|
2904
|
+
if (child === null || child.Disabled?.()) {
|
|
2905
|
+
return;
|
|
2780
2906
|
}
|
|
2907
|
+
this.#captured = child;
|
|
2908
|
+
this.#capturedBox = this.#boxes[this.#children.indexOf(child)];
|
|
2909
|
+
child.PointerDown?.(this.#forChild(pointer));
|
|
2781
2910
|
}
|
|
2782
|
-
|
|
2911
|
+
onPointerMove(pointer) {
|
|
2912
|
+
this.#captured?.PointerMove?.(this.#forChild(pointer));
|
|
2783
2913
|
}
|
|
2784
|
-
|
|
2914
|
+
onPointerUp(pointer) {
|
|
2915
|
+
const child = this.#captured;
|
|
2916
|
+
const forChild = this.#forChild(pointer);
|
|
2917
|
+
this.#captured = null;
|
|
2918
|
+
this.#capturedBox = null;
|
|
2919
|
+
child?.PointerUp?.(forChild);
|
|
2785
2920
|
}
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
Size: this.#size
|
|
2921
|
+
#forChild(pointer) {
|
|
2922
|
+
return {
|
|
2923
|
+
position: pointer.position,
|
|
2924
|
+
inside: this.#capturedBox !== null && InBox(this.#capturedBox, pointer.position)
|
|
2791
2925
|
};
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2926
|
+
}
|
|
2927
|
+
#childAt(position) {
|
|
2928
|
+
if (position === void 0) {
|
|
2929
|
+
return null;
|
|
2930
|
+
}
|
|
2931
|
+
for (let i = 0; i < this.#children.length; i++) {
|
|
2932
|
+
const box = this.#boxes[i];
|
|
2933
|
+
if (box && InBox(box, position)) {
|
|
2934
|
+
return this.#children[i];
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
return null;
|
|
2938
|
+
}
|
|
2939
|
+
#shown() {
|
|
2940
|
+
return this.#children.filter((child) => !child.Hidden?.());
|
|
2941
|
+
}
|
|
2942
|
+
#growOf(child) {
|
|
2943
|
+
return this.#grow[this.#children.indexOf(child)] ?? 1;
|
|
2797
2944
|
}
|
|
2798
2945
|
};
|
|
2799
2946
|
|
|
@@ -2813,6 +2960,9 @@ var WidgetFactory = class {
|
|
|
2813
2960
|
}
|
|
2814
2961
|
return builder(node, config);
|
|
2815
2962
|
}
|
|
2963
|
+
createFromConfig(node, config) {
|
|
2964
|
+
return this.create(node, config.type, config.config);
|
|
2965
|
+
}
|
|
2816
2966
|
};
|
|
2817
2967
|
var GlobalWidgetFactory = new WidgetFactory();
|
|
2818
2968
|
GlobalWidgetFactory.register("button", (node, config) => new ButtonWidget(config));
|
|
@@ -2823,6 +2973,10 @@ GlobalWidgetFactory.register("string", (node, config) => new StringWidget(node,
|
|
|
2823
2973
|
GlobalWidgetFactory.register("toggle", (node, config) => new ToggleWidget(node, config));
|
|
2824
2974
|
GlobalWidgetFactory.register("image", (node, config) => new ImageWidget(config));
|
|
2825
2975
|
GlobalWidgetFactory.register("text", (node, config) => new TextWidget(node, config));
|
|
2976
|
+
GlobalWidgetFactory.register("row", (node, config) => new RowWidget(
|
|
2977
|
+
(config?.widgets ?? []).map((child) => GlobalWidgetFactory.createFromConfig(node, child)),
|
|
2978
|
+
config
|
|
2979
|
+
));
|
|
2826
2980
|
|
|
2827
2981
|
// src/connection.ts
|
|
2828
2982
|
var BACKWARD_PORT_STUB = 30;
|
|
@@ -2907,7 +3061,7 @@ var Connection = class {
|
|
|
2907
3061
|
return;
|
|
2908
3062
|
}
|
|
2909
3063
|
if (this.#inNode !== null) {
|
|
2910
|
-
const inPortBox = this.#
|
|
3064
|
+
const inPortBox = this.#inSlotBox();
|
|
2911
3065
|
if (inPortBox === void 0) {
|
|
2912
3066
|
return;
|
|
2913
3067
|
}
|
|
@@ -2966,14 +3120,14 @@ var Connection = class {
|
|
|
2966
3120
|
this.clearInput();
|
|
2967
3121
|
this.clearOutput();
|
|
2968
3122
|
}
|
|
2969
|
-
setInput(node, portIndex, replace) {
|
|
3123
|
+
setInput(node, portIndex, replace, insertAt) {
|
|
2970
3124
|
this.#inNode = node;
|
|
2971
3125
|
this.#inNodePortIndex = portIndex;
|
|
2972
3126
|
const port = this.#inNode.inputPort(portIndex);
|
|
2973
3127
|
if (!!replace && port.connections().length > 0) {
|
|
2974
3128
|
port.replaceConnection(this, 0);
|
|
2975
3129
|
} else {
|
|
2976
|
-
port.addConnection(this);
|
|
3130
|
+
port.addConnection(this, insertAt);
|
|
2977
3131
|
}
|
|
2978
3132
|
}
|
|
2979
3133
|
setOutput(node, portIndex) {
|
|
@@ -2991,9 +3145,33 @@ var Connection = class {
|
|
|
2991
3145
|
this.#outNode = null;
|
|
2992
3146
|
this.#outNodePortIndex = -1;
|
|
2993
3147
|
}
|
|
3148
|
+
/**
|
|
3149
|
+
* The part of the input port this wire lands on: the whole port, or its
|
|
3150
|
+
* own slot on a port holding several wires.
|
|
3151
|
+
*/
|
|
3152
|
+
#inSlotBox() {
|
|
3153
|
+
if (this.#inNode === null) {
|
|
3154
|
+
return void 0;
|
|
3155
|
+
}
|
|
3156
|
+
const box = this.#inNode.inputPortPosition(this.#inNodePortIndex);
|
|
3157
|
+
const port = this.#inNode.inputPort(this.#inNodePortIndex);
|
|
3158
|
+
const slots = port.slots();
|
|
3159
|
+
if (box === void 0 || slots === 1) {
|
|
3160
|
+
return box;
|
|
3161
|
+
}
|
|
3162
|
+
const index = port.connections().indexOf(this);
|
|
3163
|
+
if (index === -1) {
|
|
3164
|
+
return box;
|
|
3165
|
+
}
|
|
3166
|
+
const height2 = box.Size.y / slots;
|
|
3167
|
+
return {
|
|
3168
|
+
Position: { x: box.Position.x, y: box.Position.y + index * height2 },
|
|
3169
|
+
Size: { x: box.Size.x, y: height2 }
|
|
3170
|
+
};
|
|
3171
|
+
}
|
|
2994
3172
|
mouseOverPort(mousePosition) {
|
|
2995
3173
|
if (this.#inNode !== null) {
|
|
2996
|
-
const inPortBox = this.#
|
|
3174
|
+
const inPortBox = this.#inSlotBox();
|
|
2997
3175
|
if (inPortBox !== void 0 && InBox(inPortBox, mousePosition)) {
|
|
2998
3176
|
return this.#inNode.inputPort(this.#inNodePortIndex);
|
|
2999
3177
|
}
|
|
@@ -3070,7 +3248,10 @@ var Camera = class {
|
|
|
3070
3248
|
// src/organize.ts
|
|
3071
3249
|
var widthSpacing = 100;
|
|
3072
3250
|
var heightSpacing = 50;
|
|
3251
|
+
var laneSpacing = 25;
|
|
3252
|
+
var pieceSpacing = 150;
|
|
3073
3253
|
var orderingSweeps = 8;
|
|
3254
|
+
var placementSweeps = 8;
|
|
3074
3255
|
function topologicalOrder(sorting, upstream) {
|
|
3075
3256
|
const visited = /* @__PURE__ */ new Set();
|
|
3076
3257
|
const order = new Array();
|
|
@@ -3092,11 +3273,7 @@ function topologicalOrder(sorting, upstream) {
|
|
|
3092
3273
|
continue;
|
|
3093
3274
|
}
|
|
3094
3275
|
visited.add(top.node);
|
|
3095
|
-
const
|
|
3096
|
-
if (feeders === void 0) {
|
|
3097
|
-
continue;
|
|
3098
|
-
}
|
|
3099
|
-
for (const feeder of feeders) {
|
|
3276
|
+
for (const feeder of upstream.get(top.node) ?? []) {
|
|
3100
3277
|
if (!visited.has(feeder)) {
|
|
3101
3278
|
stack.push({ node: feeder, expanded: false });
|
|
3102
3279
|
}
|
|
@@ -3105,43 +3282,103 @@ function topologicalOrder(sorting, upstream) {
|
|
|
3105
3282
|
}
|
|
3106
3283
|
return order;
|
|
3107
3284
|
}
|
|
3285
|
+
function consumersOf(sorting, upstream) {
|
|
3286
|
+
const downstream = /* @__PURE__ */ new Map();
|
|
3287
|
+
for (const node of sorting) {
|
|
3288
|
+
downstream.set(node, []);
|
|
3289
|
+
}
|
|
3290
|
+
for (const node of sorting) {
|
|
3291
|
+
for (const feeder of upstream.get(node) ?? []) {
|
|
3292
|
+
const consumers = downstream.get(feeder);
|
|
3293
|
+
if (consumers !== void 0 && !consumers.includes(node)) {
|
|
3294
|
+
consumers.push(node);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
return downstream;
|
|
3299
|
+
}
|
|
3108
3300
|
function assignLayers(sorting, upstream) {
|
|
3109
|
-
const
|
|
3301
|
+
const downstream = consumersOf(sorting, upstream);
|
|
3302
|
+
const order = topologicalOrder(sorting, downstream);
|
|
3110
3303
|
const placed = /* @__PURE__ */ new Set();
|
|
3111
|
-
const
|
|
3304
|
+
const rank = /* @__PURE__ */ new Map();
|
|
3112
3305
|
for (const node of order) {
|
|
3113
|
-
let
|
|
3114
|
-
for (const
|
|
3115
|
-
if (placed.has(
|
|
3116
|
-
|
|
3306
|
+
let r = 0;
|
|
3307
|
+
for (const consumer of downstream.get(node) ?? []) {
|
|
3308
|
+
if (placed.has(consumer)) {
|
|
3309
|
+
r = Math.max(r, rank.get(consumer) + 1);
|
|
3117
3310
|
}
|
|
3118
3311
|
}
|
|
3119
|
-
|
|
3312
|
+
rank.set(node, r);
|
|
3120
3313
|
placed.add(node);
|
|
3121
3314
|
}
|
|
3122
3315
|
let deepest = 0;
|
|
3123
|
-
for (const
|
|
3124
|
-
deepest = Math.max(deepest,
|
|
3316
|
+
for (const r of rank.values()) {
|
|
3317
|
+
deepest = Math.max(deepest, r);
|
|
3125
3318
|
}
|
|
3126
|
-
const
|
|
3127
|
-
for (
|
|
3128
|
-
|
|
3319
|
+
const layerOf = /* @__PURE__ */ new Map();
|
|
3320
|
+
for (const node of sorting) {
|
|
3321
|
+
layerOf.set(node, deepest - rank.get(node));
|
|
3129
3322
|
}
|
|
3130
|
-
for (const node of
|
|
3131
|
-
|
|
3323
|
+
for (const node of sorting) {
|
|
3324
|
+
const layer = layerOf.get(node);
|
|
3325
|
+
if ((downstream.get(node) ?? []).some((c) => layerOf.get(c) > layer)) {
|
|
3326
|
+
continue;
|
|
3327
|
+
}
|
|
3328
|
+
let pulled = -1;
|
|
3329
|
+
for (const feeder of upstream.get(node) ?? []) {
|
|
3330
|
+
const feederLayer = layerOf.get(feeder);
|
|
3331
|
+
if (feederLayer !== void 0 && feederLayer < layer) {
|
|
3332
|
+
pulled = Math.max(pulled, feederLayer + 1);
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
if (pulled >= 0) {
|
|
3336
|
+
layerOf.set(node, pulled);
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
const used = [...new Set(layerOf.values())].sort((a, b) => a - b);
|
|
3340
|
+
const compact = /* @__PURE__ */ new Map();
|
|
3341
|
+
used.forEach((layer, i) => compact.set(layer, i));
|
|
3342
|
+
const layers = used.map(() => new Array());
|
|
3343
|
+
for (const node of sorting) {
|
|
3344
|
+
const layer = compact.get(layerOf.get(node));
|
|
3345
|
+
layerOf.set(node, layer);
|
|
3346
|
+
layers[layer].push(node);
|
|
3132
3347
|
}
|
|
3133
3348
|
return { layers, layerOf };
|
|
3134
3349
|
}
|
|
3135
|
-
function
|
|
3350
|
+
function crossingsOf(layout, downstream) {
|
|
3136
3351
|
const slotOf = /* @__PURE__ */ new Map();
|
|
3137
|
-
const
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3352
|
+
for (const layer of layout.layers) {
|
|
3353
|
+
layer.forEach((node, slot) => slotOf.set(node, slot));
|
|
3354
|
+
}
|
|
3355
|
+
let total = 0;
|
|
3356
|
+
for (let i = 0; i + 1 < layout.layers.length; i++) {
|
|
3357
|
+
const spans = new Array();
|
|
3358
|
+
for (const node of layout.layers[i]) {
|
|
3359
|
+
for (const consumer of downstream.get(node) ?? []) {
|
|
3360
|
+
if (layout.layerOf.get(consumer) === i + 1) {
|
|
3361
|
+
spans.push([slotOf.get(node), slotOf.get(consumer)]);
|
|
3362
|
+
}
|
|
3141
3363
|
}
|
|
3142
3364
|
}
|
|
3143
|
-
|
|
3144
|
-
|
|
3365
|
+
for (let a = 0; a < spans.length; a++) {
|
|
3366
|
+
for (let b = a + 1; b < spans.length; b++) {
|
|
3367
|
+
if ((spans[a][0] - spans[b][0]) * (spans[a][1] - spans[b][1]) < 0) {
|
|
3368
|
+
total++;
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
return total;
|
|
3374
|
+
}
|
|
3375
|
+
function orderWithinLayers(layout, upstream, downstream) {
|
|
3376
|
+
const slotOf = /* @__PURE__ */ new Map();
|
|
3377
|
+
for (const layer of layout.layers) {
|
|
3378
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3379
|
+
slotOf.set(layer[i], i);
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3145
3382
|
const meanSlot = (node, neighbours, layer) => {
|
|
3146
3383
|
let total = 0;
|
|
3147
3384
|
let count = 0;
|
|
@@ -3161,22 +3398,377 @@ function orderWithinLayers(layout, upstream, downstream) {
|
|
|
3161
3398
|
if (adjacent < 0 || adjacent >= layout.layers.length) {
|
|
3162
3399
|
continue;
|
|
3163
3400
|
}
|
|
3164
|
-
const layer = layout.layers[index];
|
|
3165
|
-
const keys = /* @__PURE__ */ new Map();
|
|
3166
|
-
for (const node of layer) {
|
|
3167
|
-
const mean = meanSlot(node, neighbours, adjacent);
|
|
3168
|
-
keys.set(node, mean === -1 ? slotOf.get(node) : mean);
|
|
3401
|
+
const layer = layout.layers[index];
|
|
3402
|
+
const keys = /* @__PURE__ */ new Map();
|
|
3403
|
+
for (const node of layer) {
|
|
3404
|
+
const mean = meanSlot(node, neighbours, adjacent);
|
|
3405
|
+
keys.set(node, mean === -1 ? slotOf.get(node) : mean);
|
|
3406
|
+
}
|
|
3407
|
+
layer.sort((a, b) => keys.get(a) - keys.get(b));
|
|
3408
|
+
for (let s = 0; s < layer.length; s++) {
|
|
3409
|
+
slotOf.set(layer[s], s);
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
};
|
|
3413
|
+
let best = layout.layers.map((layer) => layer.slice());
|
|
3414
|
+
let fewest = crossingsOf(layout, downstream);
|
|
3415
|
+
for (let i = 0; i < orderingSweeps && fewest > 0; i++) {
|
|
3416
|
+
sweep(upstream, -1);
|
|
3417
|
+
sweep(downstream, 1);
|
|
3418
|
+
const crossings = crossingsOf(layout, downstream);
|
|
3419
|
+
if (crossings < fewest) {
|
|
3420
|
+
fewest = crossings;
|
|
3421
|
+
best = layout.layers.map((layer) => layer.slice());
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
for (let i = 0; i < best.length; i++) {
|
|
3425
|
+
layout.layers[i] = best[i];
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
function findPieces(sorting, upstream, downstream) {
|
|
3429
|
+
const rankOf = /* @__PURE__ */ new Map();
|
|
3430
|
+
sorting.forEach((node, i) => rankOf.set(node, i));
|
|
3431
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3432
|
+
const pieces = new Array();
|
|
3433
|
+
for (const start of sorting) {
|
|
3434
|
+
if (seen.has(start)) {
|
|
3435
|
+
continue;
|
|
3436
|
+
}
|
|
3437
|
+
seen.add(start);
|
|
3438
|
+
const piece = new Array();
|
|
3439
|
+
const stack = [start];
|
|
3440
|
+
while (stack.length > 0) {
|
|
3441
|
+
const node = stack.pop();
|
|
3442
|
+
piece.push(node);
|
|
3443
|
+
for (const neighbour of [...upstream.get(node) ?? [], ...downstream.get(node) ?? []]) {
|
|
3444
|
+
if (rankOf.has(neighbour) && !seen.has(neighbour)) {
|
|
3445
|
+
seen.add(neighbour);
|
|
3446
|
+
stack.push(neighbour);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
piece.sort((a, b) => rankOf.get(a) - rankOf.get(b));
|
|
3451
|
+
pieces.push(piece);
|
|
3452
|
+
}
|
|
3453
|
+
return pieces;
|
|
3454
|
+
}
|
|
3455
|
+
function treeRoot(piece, downstream) {
|
|
3456
|
+
let root;
|
|
3457
|
+
for (const node of piece) {
|
|
3458
|
+
const consumers = downstream.get(node) ?? [];
|
|
3459
|
+
if (consumers.length > 1) {
|
|
3460
|
+
return void 0;
|
|
3461
|
+
}
|
|
3462
|
+
if (consumers.length === 0) {
|
|
3463
|
+
if (root !== void 0) {
|
|
3464
|
+
return void 0;
|
|
3465
|
+
}
|
|
3466
|
+
root = node;
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
return root;
|
|
3470
|
+
}
|
|
3471
|
+
function columnXs(widths) {
|
|
3472
|
+
const xs = new Array();
|
|
3473
|
+
let x = 0;
|
|
3474
|
+
for (const width2 of widths) {
|
|
3475
|
+
xs.push(x);
|
|
3476
|
+
x += width2 + widthSpacing;
|
|
3477
|
+
}
|
|
3478
|
+
return xs;
|
|
3479
|
+
}
|
|
3480
|
+
function layoutTree(root, upstream, sizes) {
|
|
3481
|
+
const children = (node) => [...new Set(upstream.get(node) ?? [])];
|
|
3482
|
+
const depthOf = /* @__PURE__ */ new Map([[root, 0]]);
|
|
3483
|
+
const preorder = new Array();
|
|
3484
|
+
const stack = [root];
|
|
3485
|
+
let deepest = 0;
|
|
3486
|
+
while (stack.length > 0) {
|
|
3487
|
+
const node = stack.pop();
|
|
3488
|
+
preorder.push(node);
|
|
3489
|
+
const kids = children(node);
|
|
3490
|
+
for (let i = kids.length - 1; i >= 0; i--) {
|
|
3491
|
+
const depth = depthOf.get(node) + 1;
|
|
3492
|
+
depthOf.set(kids[i], depth);
|
|
3493
|
+
deepest = Math.max(deepest, depth);
|
|
3494
|
+
stack.push(kids[i]);
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
const widths = new Array(deepest + 1).fill(0);
|
|
3498
|
+
for (const [node, depth] of depthOf) {
|
|
3499
|
+
widths[deepest - depth] = Math.max(widths[deepest - depth], sizes.get(node).x);
|
|
3500
|
+
}
|
|
3501
|
+
const xs = columnXs(widths);
|
|
3502
|
+
const band = /* @__PURE__ */ new Map();
|
|
3503
|
+
const topInBand = /* @__PURE__ */ new Map();
|
|
3504
|
+
const childOffset = /* @__PURE__ */ new Map();
|
|
3505
|
+
for (let i = preorder.length - 1; i >= 0; i--) {
|
|
3506
|
+
const node = preorder[i];
|
|
3507
|
+
const height2 = sizes.get(node).y;
|
|
3508
|
+
const kids = children(node);
|
|
3509
|
+
if (kids.length === 0) {
|
|
3510
|
+
band.set(node, height2);
|
|
3511
|
+
topInBand.set(node, 0);
|
|
3512
|
+
continue;
|
|
3513
|
+
}
|
|
3514
|
+
let offset = 0;
|
|
3515
|
+
for (const kid of kids) {
|
|
3516
|
+
childOffset.set(kid, offset);
|
|
3517
|
+
offset += band.get(kid) + heightSpacing;
|
|
3518
|
+
}
|
|
3519
|
+
const centre = (kid) => childOffset.get(kid) + topInBand.get(kid) + sizes.get(kid).y / 2;
|
|
3520
|
+
let top = (centre(kids[0]) + centre(kids[kids.length - 1])) / 2 - height2 / 2;
|
|
3521
|
+
let shift = 0;
|
|
3522
|
+
if (top < 0) {
|
|
3523
|
+
shift = -top;
|
|
3524
|
+
top = 0;
|
|
3525
|
+
for (const kid of kids) {
|
|
3526
|
+
childOffset.set(kid, childOffset.get(kid) + shift);
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
band.set(node, Math.max(offset - heightSpacing + shift, top + height2));
|
|
3530
|
+
topInBand.set(node, top);
|
|
3531
|
+
}
|
|
3532
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3533
|
+
const bandTop = /* @__PURE__ */ new Map([[root, 0]]);
|
|
3534
|
+
for (const node of preorder) {
|
|
3535
|
+
const column = deepest - depthOf.get(node);
|
|
3536
|
+
const size = sizes.get(node);
|
|
3537
|
+
positions.set(node, {
|
|
3538
|
+
x: xs[column] + (widths[column] - size.x) / 2,
|
|
3539
|
+
y: bandTop.get(node) + topInBand.get(node)
|
|
3540
|
+
});
|
|
3541
|
+
for (const kid of children(node)) {
|
|
3542
|
+
bandTop.set(kid, bandTop.get(node) + childOffset.get(kid));
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
return positions;
|
|
3546
|
+
}
|
|
3547
|
+
function isotonic(targets) {
|
|
3548
|
+
const blocks = new Array();
|
|
3549
|
+
for (const target of targets) {
|
|
3550
|
+
blocks.push({ sum: target, count: 1 });
|
|
3551
|
+
while (blocks.length > 1) {
|
|
3552
|
+
const last = blocks[blocks.length - 1];
|
|
3553
|
+
const before = blocks[blocks.length - 2];
|
|
3554
|
+
if (before.sum / before.count <= last.sum / last.count) {
|
|
3555
|
+
break;
|
|
3556
|
+
}
|
|
3557
|
+
before.sum += last.sum;
|
|
3558
|
+
before.count += last.count;
|
|
3559
|
+
blocks.pop();
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
const result = new Array();
|
|
3563
|
+
for (const block of blocks) {
|
|
3564
|
+
for (let i = 0; i < block.count; i++) {
|
|
3565
|
+
result.push(block.sum / block.count);
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
return result;
|
|
3569
|
+
}
|
|
3570
|
+
function median(values) {
|
|
3571
|
+
const sorted = values.slice().sort((a, b) => a - b);
|
|
3572
|
+
const middle = Math.floor(sorted.length / 2);
|
|
3573
|
+
return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
3574
|
+
}
|
|
3575
|
+
function layoutLayered(piece, upstream, downstream, sizes) {
|
|
3576
|
+
const layout = assignLayers(piece, upstream);
|
|
3577
|
+
const up = /* @__PURE__ */ new Map();
|
|
3578
|
+
const down = /* @__PURE__ */ new Map();
|
|
3579
|
+
const heightOf = /* @__PURE__ */ new Map();
|
|
3580
|
+
for (const node of piece) {
|
|
3581
|
+
up.set(node, [...new Set(upstream.get(node) ?? [])]);
|
|
3582
|
+
down.set(node, [...new Set(downstream.get(node) ?? [])]);
|
|
3583
|
+
heightOf.set(node, sizes.get(node).y);
|
|
3584
|
+
}
|
|
3585
|
+
let nextLane = -1;
|
|
3586
|
+
for (const from of piece) {
|
|
3587
|
+
const fromLayer = layout.layerOf.get(from);
|
|
3588
|
+
const consumers = down.get(from);
|
|
3589
|
+
for (let c = 0; c < consumers.length; c++) {
|
|
3590
|
+
const to = consumers[c];
|
|
3591
|
+
const toLayer = layout.layerOf.get(to);
|
|
3592
|
+
if (toLayer - fromLayer < 2) {
|
|
3593
|
+
continue;
|
|
3594
|
+
}
|
|
3595
|
+
let previous = from;
|
|
3596
|
+
for (let layer = fromLayer + 1; layer < toLayer; layer++) {
|
|
3597
|
+
const lane = nextLane--;
|
|
3598
|
+
layout.layerOf.set(lane, layer);
|
|
3599
|
+
layout.layers[layer].push(lane);
|
|
3600
|
+
heightOf.set(lane, 0);
|
|
3601
|
+
up.set(lane, [previous]);
|
|
3602
|
+
down.set(lane, []);
|
|
3603
|
+
if (previous === from) {
|
|
3604
|
+
consumers[c] = lane;
|
|
3605
|
+
} else {
|
|
3606
|
+
down.get(previous).push(lane);
|
|
3607
|
+
}
|
|
3608
|
+
previous = lane;
|
|
3609
|
+
}
|
|
3610
|
+
down.get(previous).push(to);
|
|
3611
|
+
const feeders = up.get(to);
|
|
3612
|
+
feeders[feeders.indexOf(from)] = previous;
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
const sequence = /* @__PURE__ */ new Map();
|
|
3616
|
+
for (const sink of piece) {
|
|
3617
|
+
if (down.get(sink).length > 0) {
|
|
3618
|
+
continue;
|
|
3619
|
+
}
|
|
3620
|
+
const stack = [sink];
|
|
3621
|
+
while (stack.length > 0) {
|
|
3622
|
+
const node = stack.pop();
|
|
3623
|
+
if (sequence.has(node)) {
|
|
3624
|
+
continue;
|
|
3625
|
+
}
|
|
3626
|
+
sequence.set(node, sequence.size);
|
|
3627
|
+
const feeders = up.get(node) ?? [];
|
|
3628
|
+
for (let i = feeders.length - 1; i >= 0; i--) {
|
|
3629
|
+
stack.push(feeders[i]);
|
|
3169
3630
|
}
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
for (const layer of layout.layers) {
|
|
3634
|
+
layer.sort((a, b) => (sequence.get(a) ?? Infinity) - (sequence.get(b) ?? Infinity));
|
|
3635
|
+
}
|
|
3636
|
+
orderWithinLayers(layout, up, down);
|
|
3637
|
+
const gapAfter = (above, below) => above < 0 || below < 0 ? laneSpacing : heightSpacing;
|
|
3638
|
+
const topOf = /* @__PURE__ */ new Map();
|
|
3639
|
+
for (const layer of layout.layers) {
|
|
3640
|
+
let y = 0;
|
|
3641
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3642
|
+
topOf.set(layer[i], y);
|
|
3643
|
+
y += heightOf.get(layer[i]) + (i + 1 < layer.length ? gapAfter(layer[i], layer[i + 1]) : 0);
|
|
3644
|
+
}
|
|
3645
|
+
for (const node of layer) {
|
|
3646
|
+
topOf.set(node, topOf.get(node) - y / 2);
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
const centreOf = (node) => topOf.get(node) + heightOf.get(node) / 2;
|
|
3650
|
+
const place = (layer, neighbours, adjacent) => {
|
|
3651
|
+
const offsets = new Array();
|
|
3652
|
+
const targets = new Array();
|
|
3653
|
+
let offset = 0;
|
|
3654
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3655
|
+
const node = layer[i];
|
|
3656
|
+
const centres = (neighbours.get(node) ?? []).filter((n) => layout.layerOf.get(n) === adjacent).map(centreOf);
|
|
3657
|
+
const desired = centres.length > 0 ? median(centres) - heightOf.get(node) / 2 : topOf.get(node);
|
|
3658
|
+
offsets.push(offset);
|
|
3659
|
+
targets.push(desired - offset);
|
|
3660
|
+
if (i + 1 < layer.length) {
|
|
3661
|
+
offset += heightOf.get(node) + gapAfter(node, layer[i + 1]);
|
|
3173
3662
|
}
|
|
3174
3663
|
}
|
|
3664
|
+
const solved = isotonic(targets);
|
|
3665
|
+
for (let i = 0; i < layer.length; i++) {
|
|
3666
|
+
topOf.set(layer[i], solved[i] + offsets[i]);
|
|
3667
|
+
}
|
|
3175
3668
|
};
|
|
3176
|
-
for (let
|
|
3177
|
-
|
|
3178
|
-
|
|
3669
|
+
for (let s = 0; s < placementSweeps; s++) {
|
|
3670
|
+
if (s % 2 === 0) {
|
|
3671
|
+
for (let i = 1; i < layout.layers.length; i++) {
|
|
3672
|
+
place(layout.layers[i], up, i - 1);
|
|
3673
|
+
}
|
|
3674
|
+
} else {
|
|
3675
|
+
for (let i = layout.layers.length - 2; i >= 0; i--) {
|
|
3676
|
+
place(layout.layers[i], down, i + 1);
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
const widths = layout.layers.map((layer) => layer.reduce((widest, node) => node < 0 ? widest : Math.max(widest, sizes.get(node).x), 0));
|
|
3681
|
+
const xs = columnXs(widths);
|
|
3682
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3683
|
+
for (const node of piece) {
|
|
3684
|
+
const layer = layout.layerOf.get(node);
|
|
3685
|
+
positions.set(node, {
|
|
3686
|
+
x: xs[layer] + (widths[layer] - sizes.get(node).x) / 2,
|
|
3687
|
+
y: topOf.get(node)
|
|
3688
|
+
});
|
|
3179
3689
|
}
|
|
3690
|
+
return positions;
|
|
3691
|
+
}
|
|
3692
|
+
function layoutLoose(loose, sizes) {
|
|
3693
|
+
const columns = Math.ceil(Math.sqrt(loose.length));
|
|
3694
|
+
const widths = new Array(columns).fill(0);
|
|
3695
|
+
const heights = new Array(Math.ceil(loose.length / columns)).fill(0);
|
|
3696
|
+
loose.forEach((node, i) => {
|
|
3697
|
+
const size = sizes.get(node);
|
|
3698
|
+
widths[i % columns] = Math.max(widths[i % columns], size.x);
|
|
3699
|
+
heights[Math.floor(i / columns)] = Math.max(heights[Math.floor(i / columns)], size.y);
|
|
3700
|
+
});
|
|
3701
|
+
const xs = new Array();
|
|
3702
|
+
let x = 0;
|
|
3703
|
+
for (const width2 of widths) {
|
|
3704
|
+
xs.push(x);
|
|
3705
|
+
x += width2 + heightSpacing;
|
|
3706
|
+
}
|
|
3707
|
+
const ys = new Array();
|
|
3708
|
+
let y = 0;
|
|
3709
|
+
for (const height2 of heights) {
|
|
3710
|
+
ys.push(y);
|
|
3711
|
+
y += height2 + heightSpacing;
|
|
3712
|
+
}
|
|
3713
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3714
|
+
loose.forEach((node, i) => positions.set(node, { x: xs[i % columns], y: ys[Math.floor(i / columns)] }));
|
|
3715
|
+
return positions;
|
|
3716
|
+
}
|
|
3717
|
+
function toBlock(positions, sizes) {
|
|
3718
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
3719
|
+
for (const [node, position] of positions) {
|
|
3720
|
+
const size = sizes.get(node);
|
|
3721
|
+
minX = Math.min(minX, position.x);
|
|
3722
|
+
minY = Math.min(minY, position.y);
|
|
3723
|
+
maxX = Math.max(maxX, position.x + size.x);
|
|
3724
|
+
maxY = Math.max(maxY, position.y + size.y);
|
|
3725
|
+
}
|
|
3726
|
+
const shifted = /* @__PURE__ */ new Map();
|
|
3727
|
+
for (const [node, position] of positions) {
|
|
3728
|
+
shifted.set(node, { x: position.x - minX, y: position.y - minY });
|
|
3729
|
+
}
|
|
3730
|
+
return { positions: shifted, size: { x: maxX - minX, y: maxY - minY } };
|
|
3731
|
+
}
|
|
3732
|
+
function layoutGraph(sorting, upstream, downstream, sizes) {
|
|
3733
|
+
const blocks = new Array();
|
|
3734
|
+
const loose = new Array();
|
|
3735
|
+
for (const piece of findPieces(sorting, upstream, downstream)) {
|
|
3736
|
+
if (piece.length === 1) {
|
|
3737
|
+
loose.push(piece[0]);
|
|
3738
|
+
continue;
|
|
3739
|
+
}
|
|
3740
|
+
const root = treeRoot(piece, downstream);
|
|
3741
|
+
const positions2 = root !== void 0 ? layoutTree(root, upstream, sizes) : layoutLayered(piece, upstream, downstream, sizes);
|
|
3742
|
+
blocks.push(toBlock(positions2, sizes));
|
|
3743
|
+
}
|
|
3744
|
+
blocks.sort((a, b) => b.size.y - a.size.y || b.size.x - a.size.x);
|
|
3745
|
+
if (loose.length > 0) {
|
|
3746
|
+
blocks.push(toBlock(layoutLoose(loose, sizes), sizes));
|
|
3747
|
+
}
|
|
3748
|
+
let area = 0;
|
|
3749
|
+
let widest = 0;
|
|
3750
|
+
for (const block of blocks) {
|
|
3751
|
+
area += (block.size.x + pieceSpacing) * (block.size.y + pieceSpacing);
|
|
3752
|
+
widest = Math.max(widest, block.size.x);
|
|
3753
|
+
}
|
|
3754
|
+
const rowWidth = Math.max(widest, Math.sqrt(area));
|
|
3755
|
+
const positions = /* @__PURE__ */ new Map();
|
|
3756
|
+
let x = 0;
|
|
3757
|
+
let y = 0;
|
|
3758
|
+
let rowHeight = 0;
|
|
3759
|
+
for (const block of blocks) {
|
|
3760
|
+
if (x > 0 && x + block.size.x > rowWidth) {
|
|
3761
|
+
x = 0;
|
|
3762
|
+
y += rowHeight + pieceSpacing;
|
|
3763
|
+
rowHeight = 0;
|
|
3764
|
+
}
|
|
3765
|
+
for (const [node, position] of block.positions) {
|
|
3766
|
+
positions.set(node, { x: position.x + x, y: position.y + y });
|
|
3767
|
+
}
|
|
3768
|
+
x += block.size.x + pieceSpacing;
|
|
3769
|
+
rowHeight = Math.max(rowHeight, block.size.y);
|
|
3770
|
+
}
|
|
3771
|
+
return positions;
|
|
3180
3772
|
}
|
|
3181
3773
|
function Organize(ctx, graph, nodesToSort) {
|
|
3182
3774
|
const nodes = graph.getNodes();
|
|
@@ -3185,9 +3777,6 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3185
3777
|
}
|
|
3186
3778
|
const sorting = new Array();
|
|
3187
3779
|
if (nodesToSort) {
|
|
3188
|
-
if (nodesToSort.length < 2) {
|
|
3189
|
-
return;
|
|
3190
|
-
}
|
|
3191
3780
|
for (const index of nodesToSort) {
|
|
3192
3781
|
if (index >= 0 && index < nodes.length) {
|
|
3193
3782
|
sorting.push(index);
|
|
@@ -3207,33 +3796,28 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3207
3796
|
nodeLUT.set(nodes[i], i);
|
|
3208
3797
|
}
|
|
3209
3798
|
const camera = new Camera();
|
|
3210
|
-
const
|
|
3799
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
3211
3800
|
for (const index of sorting) {
|
|
3212
|
-
|
|
3801
|
+
sizes.set(index, nodes[index].calculateBounds(ctx, camera).Size);
|
|
3213
3802
|
}
|
|
3214
3803
|
const upstream = /* @__PURE__ */ new Map();
|
|
3215
|
-
const downstream = /* @__PURE__ */ new Map();
|
|
3216
3804
|
for (const index of sorting) {
|
|
3805
|
+
const node = nodes[index];
|
|
3217
3806
|
const feeders = new Array();
|
|
3218
|
-
for (
|
|
3219
|
-
const
|
|
3220
|
-
|
|
3221
|
-
|
|
3807
|
+
for (let i = 0; i < node.inputs(); i++) {
|
|
3808
|
+
for (const connection of node.inputPort(i).connections()) {
|
|
3809
|
+
const feeder = connection.outNode();
|
|
3810
|
+
const feederIndex = feeder === null ? void 0 : nodeLUT.get(feeder);
|
|
3811
|
+
if (feederIndex !== void 0 && inLayout.has(feederIndex) && feederIndex !== index && !feeders.includes(feederIndex)) {
|
|
3812
|
+
feeders.push(feederIndex);
|
|
3813
|
+
}
|
|
3222
3814
|
}
|
|
3223
3815
|
}
|
|
3224
3816
|
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
3817
|
}
|
|
3234
|
-
const
|
|
3235
|
-
|
|
3236
|
-
|
|
3818
|
+
const downstream = consumersOf(sorting, upstream);
|
|
3819
|
+
const positions = layoutGraph(sorting, upstream, downstream, sizes);
|
|
3820
|
+
const anchor = { x: 0, y: 0 };
|
|
3237
3821
|
for (const index of sorting) {
|
|
3238
3822
|
const position = nodes[index].getPosition();
|
|
3239
3823
|
anchor.x += position.x;
|
|
@@ -3241,19 +3825,14 @@ function Organize(ctx, graph, nodesToSort) {
|
|
|
3241
3825
|
}
|
|
3242
3826
|
anchor.x /= sorting.length;
|
|
3243
3827
|
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;
|
|
3828
|
+
let right = 0;
|
|
3829
|
+
let bottom = 0;
|
|
3830
|
+
for (const [index, position] of positions) {
|
|
3831
|
+
right = Math.max(right, position.x + sizes.get(index).x);
|
|
3832
|
+
bottom = Math.max(bottom, position.y + sizes.get(index).y);
|
|
3833
|
+
}
|
|
3834
|
+
for (const [index, position] of positions) {
|
|
3835
|
+
nodes[index].setPosition({ x: anchor.x - right / 2 + position.x, y: anchor.y - bottom / 2 + position.y });
|
|
3257
3836
|
}
|
|
3258
3837
|
}
|
|
3259
3838
|
|
|
@@ -3445,14 +4024,17 @@ var NodeSubsystem2 = class {
|
|
|
3445
4024
|
#portHovering;
|
|
3446
4025
|
#widgetHovering;
|
|
3447
4026
|
#widgetCurrentlyClicking;
|
|
4027
|
+
#widgetHoveringBox = null;
|
|
4028
|
+
#capturedWidgetBox = null;
|
|
4029
|
+
#capturedPointer = Zero();
|
|
3448
4030
|
#cursor;
|
|
3449
4031
|
#nodeFactory;
|
|
3450
4032
|
#boxSelect;
|
|
3451
4033
|
#boxSelectStart_graphSpace;
|
|
3452
4034
|
#boxSelectEnd_graphSpace;
|
|
3453
4035
|
#boxSelectionNodes;
|
|
3454
|
-
#boxSelectStyle;
|
|
3455
4036
|
#lastCtx;
|
|
4037
|
+
#lastMousePosition;
|
|
3456
4038
|
// LISTENERS ==============================================================
|
|
3457
4039
|
#nodeRemovedCallbacks;
|
|
3458
4040
|
#registeredNodeAddedCallbacks;
|
|
@@ -3477,14 +4059,6 @@ var NodeSubsystem2 = class {
|
|
|
3477
4059
|
this.#boxSelectEnd_graphSpace = Zero();
|
|
3478
4060
|
this.#boxSelectionNodes = new List();
|
|
3479
4061
|
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
4062
|
}
|
|
3489
4063
|
addPublisher(identifier, publisher) {
|
|
3490
4064
|
this.#nodeFactory.addPublisher(identifier, publisher);
|
|
@@ -3521,6 +4095,7 @@ var NodeSubsystem2 = class {
|
|
|
3521
4095
|
}
|
|
3522
4096
|
if (intersection.Widget !== void 0) {
|
|
3523
4097
|
this.#widgetHovering = intersection.Widget;
|
|
4098
|
+
this.#widgetHoveringBox = intersection.WidgetBox ?? null;
|
|
3524
4099
|
}
|
|
3525
4100
|
if (intersection.Port !== void 0 && intersection.Node !== void 0 && intersection.PortIndex !== void 0 && intersection.PortIsInput !== void 0) {
|
|
3526
4101
|
this.#portHovering = {
|
|
@@ -3543,8 +4118,15 @@ var NodeSubsystem2 = class {
|
|
|
3543
4118
|
hoveringSomething = true;
|
|
3544
4119
|
}
|
|
3545
4120
|
if (this.#widgetHovering !== null) {
|
|
3546
|
-
this.#widgetHovering.
|
|
3547
|
-
|
|
4121
|
+
if (!this.#widgetHovering.Disabled?.()) {
|
|
4122
|
+
this.#widgetCurrentlyClicking = this.#widgetHovering;
|
|
4123
|
+
this.#capturedWidgetBox = this.#widgetHoveringBox === null ? null : {
|
|
4124
|
+
Position: { x: this.#widgetHoveringBox.Position.x, y: this.#widgetHoveringBox.Position.y },
|
|
4125
|
+
Size: { x: this.#widgetHoveringBox.Size.x, y: this.#widgetHoveringBox.Size.y }
|
|
4126
|
+
};
|
|
4127
|
+
CopyVector2(this.#capturedPointer, mousePosition);
|
|
4128
|
+
this.#widgetCurrentlyClicking.PointerDown?.(this.#widgetPointer());
|
|
4129
|
+
}
|
|
3548
4130
|
hoveringSomething = true;
|
|
3549
4131
|
}
|
|
3550
4132
|
if (this.#portHovering === null) {
|
|
@@ -3559,12 +4141,12 @@ var NodeSubsystem2 = class {
|
|
|
3559
4141
|
return hoveringSomething || ctrlKey;
|
|
3560
4142
|
}
|
|
3561
4143
|
if (this.#portHovering.InputPort) {
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
4144
|
+
const port = this.#portHovering.Port;
|
|
4145
|
+
const wires = port.connections();
|
|
4146
|
+
const picked = wires.find((wire) => wire.mouseOverPort(mousePosition) === port) ?? wires[0];
|
|
4147
|
+
if (picked !== void 0) {
|
|
4148
|
+
picked.clearInput();
|
|
4149
|
+
this.#connectionSelected = picked;
|
|
3568
4150
|
}
|
|
3569
4151
|
} else {
|
|
3570
4152
|
let inNode = this.#portHovering.Node;
|
|
@@ -3584,7 +4166,19 @@ var NodeSubsystem2 = class {
|
|
|
3584
4166
|
}
|
|
3585
4167
|
return true;
|
|
3586
4168
|
}
|
|
4169
|
+
#widgetPointer() {
|
|
4170
|
+
return {
|
|
4171
|
+
position: { x: this.#capturedPointer.x, y: this.#capturedPointer.y },
|
|
4172
|
+
inside: this.#capturedWidgetBox !== null && InBox(this.#capturedWidgetBox, this.#capturedPointer)
|
|
4173
|
+
};
|
|
4174
|
+
}
|
|
3587
4175
|
mouseDragEvent(delta, scale) {
|
|
4176
|
+
if (this.#widgetCurrentlyClicking !== null) {
|
|
4177
|
+
this.#capturedPointer.x += delta.x;
|
|
4178
|
+
this.#capturedPointer.y += delta.y;
|
|
4179
|
+
this.#widgetCurrentlyClicking.PointerMove?.(this.#widgetPointer());
|
|
4180
|
+
return true;
|
|
4181
|
+
}
|
|
3588
4182
|
let nodeMoved = false;
|
|
3589
4183
|
VectorPool.run(() => {
|
|
3590
4184
|
const scaledDelta = VectorPool.get();
|
|
@@ -3703,13 +4297,20 @@ var NodeSubsystem2 = class {
|
|
|
3703
4297
|
}
|
|
3704
4298
|
#firstCompatiblePort(node, side, against) {
|
|
3705
4299
|
const count = side === "INPUT" /* Input */ ? node.inputs() : node.outputs();
|
|
4300
|
+
let firstHidden = -1;
|
|
3706
4301
|
for (let i = 0; i < count; i++) {
|
|
3707
4302
|
const port = side === "INPUT" /* Input */ ? node.inputPort(i) : node.outputPort(i);
|
|
3708
|
-
if (portsCompatible(port, against)) {
|
|
4303
|
+
if (!portsCompatible(port, against)) {
|
|
4304
|
+
continue;
|
|
4305
|
+
}
|
|
4306
|
+
if (!port.hidden()) {
|
|
3709
4307
|
return i;
|
|
3710
4308
|
}
|
|
4309
|
+
if (firstHidden === -1) {
|
|
4310
|
+
firstHidden = i;
|
|
4311
|
+
}
|
|
3711
4312
|
}
|
|
3712
|
-
return
|
|
4313
|
+
return firstHidden;
|
|
3713
4314
|
}
|
|
3714
4315
|
clickEnd() {
|
|
3715
4316
|
for (let i = 0; i < this.#nodesGrabbed.Count(); i++) {
|
|
@@ -3725,10 +4326,11 @@ var NodeSubsystem2 = class {
|
|
|
3725
4326
|
this.#boxSelect = false;
|
|
3726
4327
|
}
|
|
3727
4328
|
if (this.#widgetCurrentlyClicking !== null) {
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
}
|
|
4329
|
+
const widget = this.#widgetCurrentlyClicking;
|
|
4330
|
+
const pointer = this.#widgetPointer();
|
|
3731
4331
|
this.#widgetCurrentlyClicking = null;
|
|
4332
|
+
this.#capturedWidgetBox = null;
|
|
4333
|
+
widget.PointerUp?.(pointer);
|
|
3732
4334
|
}
|
|
3733
4335
|
if (this.#connectionSelected === null) {
|
|
3734
4336
|
return;
|
|
@@ -3773,7 +4375,7 @@ var NodeSubsystem2 = class {
|
|
|
3773
4375
|
inputPort.connections()[0].clearOutput();
|
|
3774
4376
|
this.#removeConnection(inputPort.connections()[0], false);
|
|
3775
4377
|
}
|
|
3776
|
-
conn.setInput(this.#portHovering.Node, this.#portHovering.Index, replace);
|
|
4378
|
+
conn.setInput(this.#portHovering.Node, this.#portHovering.Index, replace, this.#insertIndex());
|
|
3777
4379
|
} else {
|
|
3778
4380
|
conn.setOutput(this.#portHovering.Node, this.#portHovering.Index);
|
|
3779
4381
|
}
|
|
@@ -4145,7 +4747,8 @@ var NodeSubsystem2 = class {
|
|
|
4145
4747
|
}
|
|
4146
4748
|
if (intersection.Widget !== void 0) {
|
|
4147
4749
|
this.#widgetHovering = intersection.Widget;
|
|
4148
|
-
this.#
|
|
4750
|
+
this.#widgetHoveringBox = intersection.WidgetBox ?? null;
|
|
4751
|
+
this.#cursor = intersection.Widget.Disabled?.() ? "default" /* Default */ : intersection.Widget.Cursor?.() ?? "pointer" /* Pointer */;
|
|
4149
4752
|
}
|
|
4150
4753
|
if (intersection.Port !== void 0 && intersection.Node !== void 0 && intersection.PortIndex !== void 0 && intersection.PortIsInput !== void 0) {
|
|
4151
4754
|
this.#portHovering = {
|
|
@@ -4168,6 +4771,9 @@ var NodeSubsystem2 = class {
|
|
|
4168
4771
|
}
|
|
4169
4772
|
this.#nodes[i].render(ctx, camera, state, mousePosition, this.#postProcessPass);
|
|
4170
4773
|
}
|
|
4774
|
+
if (this.#widgetCurrentlyClicking !== null) {
|
|
4775
|
+
this.#cursor = this.#widgetCurrentlyClicking.Cursor?.() ?? "pointer" /* Pointer */;
|
|
4776
|
+
}
|
|
4171
4777
|
}
|
|
4172
4778
|
#boxSelectionScreenspaceBox(camera) {
|
|
4173
4779
|
const box = { Position: Zero(), Size: Zero() };
|
|
@@ -4176,29 +4782,174 @@ var NodeSubsystem2 = class {
|
|
|
4176
4782
|
SubVector2(box.Size, box.Size, box.Position);
|
|
4177
4783
|
return box;
|
|
4178
4784
|
}
|
|
4785
|
+
/**
|
|
4786
|
+
* Where a wire being dropped on the hovered array input would go, or
|
|
4787
|
+
* undefined when it is not over one.
|
|
4788
|
+
*/
|
|
4789
|
+
#insertIndex() {
|
|
4790
|
+
const hovering = this.#portHovering;
|
|
4791
|
+
if (hovering === null || !hovering.InputPort || this.#lastMousePosition === void 0 || hovering.Port.getPortType() !== "INPUTARRAY" /* InputArray */) {
|
|
4792
|
+
return void 0;
|
|
4793
|
+
}
|
|
4794
|
+
const box = hovering.Node.inputPortPosition(hovering.Index);
|
|
4795
|
+
return arrayInsertIndex(box, hovering.Port.connections().length, this.#lastMousePosition.y);
|
|
4796
|
+
}
|
|
4797
|
+
#renderInsertMarker(ctx, camera) {
|
|
4798
|
+
const hovering = this.#portHovering;
|
|
4799
|
+
if (this.#connectionSelected === null || this.#connectionSelected.inPort() !== null || hovering === null) {
|
|
4800
|
+
return;
|
|
4801
|
+
}
|
|
4802
|
+
const index = this.#insertIndex();
|
|
4803
|
+
const count = hovering.Port.connections().length;
|
|
4804
|
+
if (index === void 0 || count === 0) {
|
|
4805
|
+
return;
|
|
4806
|
+
}
|
|
4807
|
+
const box = hovering.Node.inputPortPosition(hovering.Index);
|
|
4808
|
+
const y = box.Position.y + index * box.Size.y / count;
|
|
4809
|
+
const overhang = 6 * camera.zoom;
|
|
4810
|
+
ctx.save();
|
|
4811
|
+
ctx.strokeStyle = "#ffffff";
|
|
4812
|
+
ctx.lineWidth = Math.max(1, 2 * camera.zoom);
|
|
4813
|
+
ctx.beginPath();
|
|
4814
|
+
ctx.moveTo(box.Position.x - overhang, y);
|
|
4815
|
+
ctx.lineTo(box.Position.x + box.Size.x + overhang, y);
|
|
4816
|
+
ctx.stroke();
|
|
4817
|
+
ctx.restore();
|
|
4818
|
+
}
|
|
4179
4819
|
render(ctx, camera, mousePosition) {
|
|
4180
4820
|
this.#lastCtx = ctx;
|
|
4821
|
+
this.#lastMousePosition = mousePosition;
|
|
4181
4822
|
this.#cursor = "default" /* Default */;
|
|
4182
4823
|
const awaitingLayout = this.#nodes.some((node) => !node.hasBeenLaidOut());
|
|
4824
|
+
for (const node of this.#nodes) {
|
|
4825
|
+
if (node.hasBeenLaidOut()) {
|
|
4826
|
+
node.placePorts(node.calculateBounds(ctx, camera), camera.zoom);
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4183
4829
|
TimeExecution("Render_Connections", () => {
|
|
4184
4830
|
this.#renderConnections(ctx, camera, mousePosition);
|
|
4185
4831
|
});
|
|
4186
4832
|
TimeExecution("Render_Nodes", () => {
|
|
4187
4833
|
this.#renderNodes(ctx, camera, mousePosition);
|
|
4188
4834
|
});
|
|
4835
|
+
this.#renderInsertMarker(ctx, camera);
|
|
4189
4836
|
if (awaitingLayout) {
|
|
4190
4837
|
requestRender();
|
|
4191
4838
|
}
|
|
4192
4839
|
if (this.#boxSelect) {
|
|
4193
4840
|
const box = this.#boxSelectionScreenspaceBox(camera);
|
|
4194
4841
|
ctx.setLineDash([Theme.BoxSelect.LineDashLength]);
|
|
4195
|
-
|
|
4842
|
+
new BoxStyle({
|
|
4843
|
+
border: { color: Theme.BoxSelect.Color, size: Theme.BoxSelect.Size },
|
|
4844
|
+
color: "rgba(0,0,0,0)",
|
|
4845
|
+
radius: Theme.BoxSelect.Radius
|
|
4846
|
+
}).Draw(ctx, box, 1);
|
|
4196
4847
|
ctx.setLineDash([]);
|
|
4197
4848
|
}
|
|
4198
4849
|
return { cursorStyle: this.#cursor };
|
|
4199
4850
|
}
|
|
4200
4851
|
};
|
|
4201
4852
|
|
|
4853
|
+
// src/popup.ts
|
|
4854
|
+
var Popup = class {
|
|
4855
|
+
#title;
|
|
4856
|
+
#options;
|
|
4857
|
+
#content;
|
|
4858
|
+
#onClose;
|
|
4859
|
+
// Styling ================================================================
|
|
4860
|
+
#buttonCSS;
|
|
4861
|
+
// Runtime ================================================================
|
|
4862
|
+
#popup;
|
|
4863
|
+
#keyHandler;
|
|
4864
|
+
constructor(config) {
|
|
4865
|
+
this.#title = config.title;
|
|
4866
|
+
this.#options = config.options;
|
|
4867
|
+
this.#content = config.content;
|
|
4868
|
+
this.#onClose = config.onClose;
|
|
4869
|
+
this.#buttonCSS = config.buttonCSS === void 0 ? "flex-grow: 1; margin-right: 8px;" : config.buttonCSS;
|
|
4870
|
+
this.#popup = null;
|
|
4871
|
+
this.#keyHandler = null;
|
|
4872
|
+
}
|
|
4873
|
+
Hide() {
|
|
4874
|
+
if (this.#popup === null) {
|
|
4875
|
+
return;
|
|
4876
|
+
}
|
|
4877
|
+
if (this.#keyHandler !== null) {
|
|
4878
|
+
document.removeEventListener("keydown", this.#keyHandler, true);
|
|
4879
|
+
this.#keyHandler = null;
|
|
4880
|
+
}
|
|
4881
|
+
document.body.removeChild(this.#popup);
|
|
4882
|
+
this.#popup = null;
|
|
4883
|
+
}
|
|
4884
|
+
#close(option) {
|
|
4885
|
+
this.Hide();
|
|
4886
|
+
if (this.#onClose !== void 0) {
|
|
4887
|
+
this.#onClose(option);
|
|
4888
|
+
}
|
|
4889
|
+
}
|
|
4890
|
+
Show() {
|
|
4891
|
+
this.#popup = document.createElement("div");
|
|
4892
|
+
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};`;
|
|
4893
|
+
document.body.appendChild(this.#popup);
|
|
4894
|
+
const container = document.createElement("div");
|
|
4895
|
+
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;`;
|
|
4896
|
+
this.#popup.appendChild(container);
|
|
4897
|
+
const title = document.createElement("h2");
|
|
4898
|
+
title.textContent = this.#title;
|
|
4899
|
+
title.style.cssText = "margin-top:0;margin-bottom:16px;";
|
|
4900
|
+
container.appendChild(title);
|
|
4901
|
+
if (this.#content !== void 0) {
|
|
4902
|
+
container.appendChild(this.#content());
|
|
4903
|
+
}
|
|
4904
|
+
const buttonContainer = document.createElement("div");
|
|
4905
|
+
buttonContainer.style.cssText = "margin-top:16px;justify-content:space-around;align-items:center;flex-direction:row;display:flex;";
|
|
4906
|
+
container.appendChild(buttonContainer);
|
|
4907
|
+
for (let i = 0; i < this.#options.length; i++) {
|
|
4908
|
+
const button = document.createElement("button");
|
|
4909
|
+
button.style.cssText = this.#buttonCSS;
|
|
4910
|
+
button.textContent = this.#options[i];
|
|
4911
|
+
button.onclick = () => this.#close(this.#options[i]);
|
|
4912
|
+
buttonContainer.appendChild(button);
|
|
4913
|
+
}
|
|
4914
|
+
this.#keyHandler = (e) => {
|
|
4915
|
+
if (e.key === "Enter") {
|
|
4916
|
+
e.preventDefault();
|
|
4917
|
+
this.#close(this.#options.length > 0 ? this.#options[0] : null);
|
|
4918
|
+
} else if (e.key === "Escape") {
|
|
4919
|
+
e.preventDefault();
|
|
4920
|
+
this.#close(null);
|
|
4921
|
+
}
|
|
4922
|
+
e.stopPropagation();
|
|
4923
|
+
};
|
|
4924
|
+
document.addEventListener("keydown", this.#keyHandler, true);
|
|
4925
|
+
}
|
|
4926
|
+
};
|
|
4927
|
+
|
|
4928
|
+
// src/popups/string.ts
|
|
4929
|
+
function SetStringPopup(config) {
|
|
4930
|
+
let input = null;
|
|
4931
|
+
return new Popup({
|
|
4932
|
+
title: config.title,
|
|
4933
|
+
options: ["Set", "Cancel"],
|
|
4934
|
+
content: () => {
|
|
4935
|
+
const container = document.createElement("div");
|
|
4936
|
+
input = document.createElement("input");
|
|
4937
|
+
input.value = config.startingValue;
|
|
4938
|
+
container.append(input);
|
|
4939
|
+
return container;
|
|
4940
|
+
},
|
|
4941
|
+
onClose: (button) => {
|
|
4942
|
+
if (button !== "Set" || input === null) {
|
|
4943
|
+
if (config.onCancel) {
|
|
4944
|
+
config.onCancel();
|
|
4945
|
+
}
|
|
4946
|
+
return;
|
|
4947
|
+
}
|
|
4948
|
+
config.onUpdate(input.value);
|
|
4949
|
+
}
|
|
4950
|
+
});
|
|
4951
|
+
}
|
|
4952
|
+
|
|
4202
4953
|
// src/popups/form.ts
|
|
4203
4954
|
function FormPopup(config) {
|
|
4204
4955
|
let input = new Array();
|
|
@@ -4315,9 +5066,11 @@ var FlowNode = class {
|
|
|
4315
5066
|
#titleChangeCallback;
|
|
4316
5067
|
#infoChangeCallback;
|
|
4317
5068
|
// Styling ================================================================
|
|
5069
|
+
#styleConfig;
|
|
5070
|
+
#styledForTheme = -1;
|
|
4318
5071
|
#titleColor;
|
|
4319
5072
|
#selectedStyle;
|
|
4320
|
-
#stateStyles;
|
|
5073
|
+
#stateStyles = /* @__PURE__ */ new Map();
|
|
4321
5074
|
#padding;
|
|
4322
5075
|
#portTextStyle;
|
|
4323
5076
|
#elementSpacing;
|
|
@@ -4329,6 +5082,7 @@ var FlowNode = class {
|
|
|
4329
5082
|
#data;
|
|
4330
5083
|
#registeredAnyPropertyChangeCallbacks;
|
|
4331
5084
|
#registeredPropertyChangeCallbacks;
|
|
5085
|
+
#registeredPropertyEditCallbacks = /* @__PURE__ */ new Map();
|
|
4332
5086
|
constructor(config) {
|
|
4333
5087
|
this.#input = new Array();
|
|
4334
5088
|
this.#output = new Array();
|
|
@@ -4381,58 +5135,17 @@ var FlowNode = class {
|
|
|
4381
5135
|
this.#position = config?.position === void 0 ? { x: 0, y: 0 } : config.position;
|
|
4382
5136
|
this.#title = new Text(
|
|
4383
5137
|
config?.title === void 0 ? "" : config.title,
|
|
4384
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
4385
|
-
size: 16,
|
|
4386
|
-
weight: "bold" /* Bold */,
|
|
4387
|
-
color: Theme.Node.FontColor
|
|
4388
|
-
})
|
|
5138
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 16, weight: "bold" /* Bold */ })
|
|
4389
5139
|
);
|
|
4390
5140
|
this.#subTitle = new Text(
|
|
4391
5141
|
config?.subTitle === void 0 ? "" : config.subTitle,
|
|
4392
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
4393
|
-
size: 10,
|
|
4394
|
-
weight: "bold" /* Bold */,
|
|
4395
|
-
color: Theme.Node.FontColor
|
|
4396
|
-
})
|
|
4397
|
-
);
|
|
4398
|
-
this.#infoSymbol = new Text(
|
|
4399
|
-
"i",
|
|
4400
|
-
{
|
|
4401
|
-
size: 13,
|
|
4402
|
-
weight: "bold" /* Bold */,
|
|
4403
|
-
color: Theme.Node.FontColor
|
|
4404
|
-
}
|
|
5142
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 10, weight: "bold" /* Bold */ })
|
|
4405
5143
|
);
|
|
5144
|
+
this.#infoSymbol = new Text("i", { size: 13, weight: "bold" /* Bold */ });
|
|
4406
5145
|
this.#infoText = config?.info ? config?.info : "";
|
|
4407
|
-
this.#titleColor = config?.style?.title?.color === void 0 ? "#154050" : config?.style?.title?.color;
|
|
4408
5146
|
this.#padding = config?.style?.title?.padding === void 0 ? 15 : config?.style?.title?.padding;
|
|
4409
|
-
this.#
|
|
4410
|
-
this.#
|
|
4411
|
-
border: { color: Theme.Node.Border.Idle, size: 1 },
|
|
4412
|
-
radius: Theme.Node.BorderRadius,
|
|
4413
|
-
color: Theme.Node.BackgroundColor
|
|
4414
|
-
})));
|
|
4415
|
-
this.#stateStyles.set(1 /* MouseOver */, new BoxStyle(BoxStyleWithFallback(config?.style?.mouseOver, {
|
|
4416
|
-
border: { color: Theme.Node.Border.MouseOver, size: 1.1 },
|
|
4417
|
-
radius: Theme.Node.BorderRadius,
|
|
4418
|
-
color: Theme.Node.BackgroundColor
|
|
4419
|
-
})));
|
|
4420
|
-
this.#stateStyles.set(2 /* Grabbed */, new BoxStyle(BoxStyleWithFallback(config?.style?.grabbed, {
|
|
4421
|
-
border: { color: Theme.Node.Border.Grabbed, size: 2 },
|
|
4422
|
-
radius: Theme.Node.BorderRadius,
|
|
4423
|
-
color: Theme.Node.BackgroundColor
|
|
4424
|
-
})));
|
|
4425
|
-
this.#selectedStyle = new BoxStyle(BoxStyleWithFallback(config?.style?.selected, {
|
|
4426
|
-
border: { color: Theme.Node.Border.Selected, size: 1 },
|
|
4427
|
-
radius: Theme.Node.BorderRadius,
|
|
4428
|
-
color: Theme.Node.BackgroundColor
|
|
4429
|
-
}));
|
|
4430
|
-
this.#portTextStyle = new TextStyle({
|
|
4431
|
-
size: config?.style?.portText?.size === void 0 ? 14 : config?.style?.portText?.size,
|
|
4432
|
-
color: config?.style?.portText?.color === void 0 ? Theme.Node.Port.FontColor : config?.style?.portText?.color,
|
|
4433
|
-
font: config?.style?.portText?.font,
|
|
4434
|
-
weight: config?.style?.portText?.weight
|
|
4435
|
-
});
|
|
5147
|
+
this.#styleConfig = config?.style;
|
|
5148
|
+
this.#applyStyle();
|
|
4436
5149
|
if (config?.inputs !== void 0) {
|
|
4437
5150
|
for (let i = 0; i < config.inputs.length; i++) {
|
|
4438
5151
|
this.addInput(config.inputs[i]);
|
|
@@ -4444,12 +5157,8 @@ var FlowNode = class {
|
|
|
4444
5157
|
}
|
|
4445
5158
|
}
|
|
4446
5159
|
if (config?.widgets !== void 0) {
|
|
4447
|
-
for (
|
|
4448
|
-
|
|
4449
|
-
if (widget.type === void 0) {
|
|
4450
|
-
continue;
|
|
4451
|
-
}
|
|
4452
|
-
this.addWidget(GlobalWidgetFactory.create(this, widget.type, widget.config));
|
|
5160
|
+
for (const widget of config.widgets) {
|
|
5161
|
+
this.addWidget(GlobalWidgetFactory.createFromConfig(this, widget));
|
|
4453
5162
|
}
|
|
4454
5163
|
}
|
|
4455
5164
|
}
|
|
@@ -4522,6 +5231,32 @@ var FlowNode = class {
|
|
|
4522
5231
|
callbacks[i](oldValue, value);
|
|
4523
5232
|
}
|
|
4524
5233
|
}
|
|
5234
|
+
/**
|
|
5235
|
+
* Listens for a person changing the property through a widget, but not
|
|
5236
|
+
* for the property being set from code.
|
|
5237
|
+
*/
|
|
5238
|
+
addPropertyEditListener(name, callback) {
|
|
5239
|
+
let callbacks = this.#registeredPropertyEditCallbacks.get(name);
|
|
5240
|
+
if (callbacks === void 0) {
|
|
5241
|
+
callbacks = [];
|
|
5242
|
+
this.#registeredPropertyEditCallbacks.set(name, callbacks);
|
|
5243
|
+
}
|
|
5244
|
+
callbacks.push(callback);
|
|
5245
|
+
}
|
|
5246
|
+
/**
|
|
5247
|
+
* Sets the property as a person's finished edit, so edit listeners hear
|
|
5248
|
+
* it even when the value was already showing.
|
|
5249
|
+
*/
|
|
5250
|
+
editProperty(name, value) {
|
|
5251
|
+
this.setProperty(name, value);
|
|
5252
|
+
const callbacks = this.#registeredPropertyEditCallbacks.get(name);
|
|
5253
|
+
if (callbacks === void 0) {
|
|
5254
|
+
return;
|
|
5255
|
+
}
|
|
5256
|
+
for (const callback of [...callbacks]) {
|
|
5257
|
+
callback(value);
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
4525
5260
|
getProperty(name) {
|
|
4526
5261
|
return this.#data[name];
|
|
4527
5262
|
}
|
|
@@ -4810,6 +5545,7 @@ var FlowNode = class {
|
|
|
4810
5545
|
// return this.#titleTextStyle.measure(ctx, scale, this.#title);
|
|
4811
5546
|
// }
|
|
4812
5547
|
calculateBounds(ctx, camera) {
|
|
5548
|
+
this.#followTheme();
|
|
4813
5549
|
const tempMeasurement = Zero();
|
|
4814
5550
|
const doublePadding = this.#padding * 2;
|
|
4815
5551
|
const screenSpacePosition = Zero();
|
|
@@ -4823,25 +5559,21 @@ var FlowNode = class {
|
|
|
4823
5559
|
size.x += size.y * 4;
|
|
4824
5560
|
}
|
|
4825
5561
|
size.x += doublePadding;
|
|
4826
|
-
size.y += doublePadding
|
|
4827
|
-
for (
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
|
|
4832
|
-
}
|
|
4833
|
-
size.y += this.#elementSpacing * this.#output.length;
|
|
4834
|
-
for (let i = 0; i < this.#output.length; i++) {
|
|
4835
|
-
const port = this.#output[i];
|
|
5562
|
+
size.y += doublePadding;
|
|
5563
|
+
for (const port of [...this.#input, ...this.#output]) {
|
|
5564
|
+
if (port.hidden()) {
|
|
5565
|
+
continue;
|
|
5566
|
+
}
|
|
4836
5567
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
4837
|
-
size.y += tempMeasurement.y;
|
|
5568
|
+
size.y += this.#portRowHeight(port, tempMeasurement.y) + this.#elementSpacing;
|
|
4838
5569
|
size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
|
|
4839
5570
|
}
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
5571
|
+
for (const widget of this.#widgets) {
|
|
5572
|
+
if (widget.Hidden?.()) {
|
|
5573
|
+
continue;
|
|
5574
|
+
}
|
|
5575
|
+
const eleSize = widget.Size();
|
|
5576
|
+
size.y += eleSize.y + this.#elementSpacing;
|
|
4845
5577
|
size.x = Math.max(size.x, eleSize.x + doublePadding);
|
|
4846
5578
|
}
|
|
4847
5579
|
size.y += this.#elementSpacing;
|
|
@@ -4925,7 +5657,7 @@ var FlowNode = class {
|
|
|
4925
5657
|
intersection.Node = this;
|
|
4926
5658
|
}
|
|
4927
5659
|
for (let i = 0; i < this.#inputPortPositions.Count(); i++) {
|
|
4928
|
-
if (InBox(this.#inputPortPositions.At(i), position)) {
|
|
5660
|
+
if (!this.#input[i].hidden() && InBox(this.#inputPortPositions.At(i), position)) {
|
|
4929
5661
|
intersection.Node = this;
|
|
4930
5662
|
intersection.PortIndex = i;
|
|
4931
5663
|
intersection.PortIsInput = true;
|
|
@@ -4933,7 +5665,7 @@ var FlowNode = class {
|
|
|
4933
5665
|
}
|
|
4934
5666
|
}
|
|
4935
5667
|
for (let i = 0; i < this.#outputPortPositions.Count(); i++) {
|
|
4936
|
-
if (InBox(this.#outputPortPositions.At(i), position)) {
|
|
5668
|
+
if (!this.#output[i].hidden() && InBox(this.#outputPortPositions.At(i), position)) {
|
|
4937
5669
|
intersection.Node = this;
|
|
4938
5670
|
intersection.PortIndex = i;
|
|
4939
5671
|
intersection.PortIsInput = false;
|
|
@@ -4941,10 +5673,12 @@ var FlowNode = class {
|
|
|
4941
5673
|
}
|
|
4942
5674
|
}
|
|
4943
5675
|
for (let i = 0; i < this.#widgetPositions.Count(); i++) {
|
|
4944
|
-
|
|
5676
|
+
const widget = this.#widgets[i];
|
|
5677
|
+
if (widget !== void 0 && !widget.Hidden?.() && InBox(this.#widgetPositions.At(i), position)) {
|
|
4945
5678
|
intersection.Node = this;
|
|
4946
5679
|
intersection.WidgetIndex = i;
|
|
4947
|
-
intersection.Widget =
|
|
5680
|
+
intersection.Widget = widget;
|
|
5681
|
+
intersection.WidgetBox = this.#widgetPositions.At(i);
|
|
4948
5682
|
}
|
|
4949
5683
|
}
|
|
4950
5684
|
return intersection;
|
|
@@ -4993,6 +5727,12 @@ var FlowNode = class {
|
|
|
4993
5727
|
inputPortPosition(index) {
|
|
4994
5728
|
return this.#inputPortPositions.At(index);
|
|
4995
5729
|
}
|
|
5730
|
+
widgetPosition(index) {
|
|
5731
|
+
if (index < 0 || index >= this.#widgetPositions.Count()) {
|
|
5732
|
+
return void 0;
|
|
5733
|
+
}
|
|
5734
|
+
return this.#widgetPositions.At(index);
|
|
5735
|
+
}
|
|
4996
5736
|
inputPort(index) {
|
|
4997
5737
|
return this.#input[index];
|
|
4998
5738
|
}
|
|
@@ -5008,6 +5748,35 @@ var FlowNode = class {
|
|
|
5008
5748
|
outputs() {
|
|
5009
5749
|
return this.#output.length;
|
|
5010
5750
|
}
|
|
5751
|
+
#applyStyle() {
|
|
5752
|
+
const style = this.#styleConfig;
|
|
5753
|
+
const titleText = style?.title?.textStyle?.color ?? Theme.Node.Title.FontColor;
|
|
5754
|
+
this.#title.style().setColor(titleText);
|
|
5755
|
+
this.#subTitle.style().setColor(titleText);
|
|
5756
|
+
this.#infoSymbol.style().setColor(titleText);
|
|
5757
|
+
this.#titleColor = style?.title?.color ?? Theme.Node.Title.Color;
|
|
5758
|
+
const box = (config, border, size) => new BoxStyle(BoxStyleWithFallback(config, {
|
|
5759
|
+
border: { color: border, size },
|
|
5760
|
+
radius: Theme.Node.BorderRadius,
|
|
5761
|
+
color: Theme.Node.BackgroundColor
|
|
5762
|
+
}));
|
|
5763
|
+
this.#stateStyles.set(0 /* Idle */, box(style?.idle, Theme.Node.Border.Idle, 1));
|
|
5764
|
+
this.#stateStyles.set(1 /* MouseOver */, box(style?.mouseOver, Theme.Node.Border.MouseOver, 1.1));
|
|
5765
|
+
this.#stateStyles.set(2 /* Grabbed */, box(style?.grabbed, Theme.Node.Border.Grabbed, 2));
|
|
5766
|
+
this.#selectedStyle = box(style?.selected, Theme.Node.Border.Selected, 1);
|
|
5767
|
+
this.#portTextStyle = new TextStyle({
|
|
5768
|
+
size: style?.portText?.size ?? 14,
|
|
5769
|
+
color: style?.portText?.color ?? Theme.Node.Port.FontColor,
|
|
5770
|
+
font: style?.portText?.font,
|
|
5771
|
+
weight: style?.portText?.weight
|
|
5772
|
+
});
|
|
5773
|
+
this.#styledForTheme = themeVersion();
|
|
5774
|
+
}
|
|
5775
|
+
#followTheme() {
|
|
5776
|
+
if (this.#styledForTheme !== themeVersion()) {
|
|
5777
|
+
this.#applyStyle();
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
5011
5780
|
#calculateStyle(state) {
|
|
5012
5781
|
if (this.#selected && state === 0 /* Idle */) {
|
|
5013
5782
|
return this.#selectedStyle;
|
|
@@ -5024,9 +5793,22 @@ var FlowNode = class {
|
|
|
5024
5793
|
clearMessages() {
|
|
5025
5794
|
this.#messages = [];
|
|
5026
5795
|
}
|
|
5796
|
+
#portRowHeight(port, textHeight) {
|
|
5797
|
+
const slots = port.slots();
|
|
5798
|
+
return slots > 1 ? Math.max(textHeight, slots * ARRAY_SLOT_PITCH) : textHeight;
|
|
5799
|
+
}
|
|
5027
5800
|
hasBeenLaidOut() {
|
|
5028
5801
|
return this.#laidOut;
|
|
5029
5802
|
}
|
|
5803
|
+
/**
|
|
5804
|
+
* Throws away where the ports were last placed, so wires wait for the
|
|
5805
|
+
* next draw instead of pointing at the old spots.
|
|
5806
|
+
*/
|
|
5807
|
+
invalidateLayout() {
|
|
5808
|
+
this.#laidOut = false;
|
|
5809
|
+
this.#portOffsets = { input: [], output: [] };
|
|
5810
|
+
requestRender();
|
|
5811
|
+
}
|
|
5030
5812
|
#laidOut = false;
|
|
5031
5813
|
render(ctx, camera, state, mousePosition, postProcess) {
|
|
5032
5814
|
this.#laidOut = true;
|
|
@@ -5064,6 +5846,9 @@ var FlowNode = class {
|
|
|
5064
5846
|
[nodeStyle.radius() * camera.zoom, nodeStyle.radius() * camera.zoom, 0, 0]
|
|
5065
5847
|
);
|
|
5066
5848
|
ctx.fill();
|
|
5849
|
+
if (Theme.Node.Bevel !== null) {
|
|
5850
|
+
drawBevel(ctx, nodeBounds, camera.zoom, Theme.Node.Bevel, false);
|
|
5851
|
+
}
|
|
5067
5852
|
const titlePosition = VectorPool.get();
|
|
5068
5853
|
titlePosition.x = nodeBounds.Position.x + nodeBounds.Size.x / 2;
|
|
5069
5854
|
titlePosition.y = nodeBounds.Position.y + scaledPadding + titleSize.y / 2;
|
|
@@ -5124,8 +5909,12 @@ var FlowNode = class {
|
|
|
5124
5909
|
for (let i = 0; i < this.#input.length; i++) {
|
|
5125
5910
|
ctx.textAlign = "left" /* Left */;
|
|
5126
5911
|
const port = this.#input[i];
|
|
5912
|
+
if (port.hidden()) {
|
|
5913
|
+
this.#inputPortPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
|
|
5914
|
+
continue;
|
|
5915
|
+
}
|
|
5127
5916
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
5128
|
-
const portHeight = tempMeasurement.y * camera.zoom;
|
|
5917
|
+
const portHeight = this.#portRowHeight(port, tempMeasurement.y) * camera.zoom;
|
|
5129
5918
|
const position = VectorPool.get();
|
|
5130
5919
|
position.x = nodeBounds.Position.x;
|
|
5131
5920
|
position.y = startY + portHeight / 2;
|
|
@@ -5138,6 +5927,10 @@ var FlowNode = class {
|
|
|
5138
5927
|
ctx.textAlign = "right" /* Right */;
|
|
5139
5928
|
for (let i = 0; i < this.#output.length; i++) {
|
|
5140
5929
|
const port = this.#output[i];
|
|
5930
|
+
if (port.hidden()) {
|
|
5931
|
+
this.#outputPortPositions.Push({ Position: { x: rightSide, y: startY }, Size: { x: 0, y: 0 } });
|
|
5932
|
+
continue;
|
|
5933
|
+
}
|
|
5141
5934
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
5142
5935
|
const portHeight = tempMeasurement.y * camera.zoom;
|
|
5143
5936
|
const position = VectorPool.get();
|
|
@@ -5150,13 +5943,18 @@ var FlowNode = class {
|
|
|
5150
5943
|
}
|
|
5151
5944
|
for (let i = 0; i < this.#widgets.length; i++) {
|
|
5152
5945
|
const widget = this.#widgets[i];
|
|
5946
|
+
if (widget.Hidden?.()) {
|
|
5947
|
+
this.#widgetPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
|
|
5948
|
+
continue;
|
|
5949
|
+
}
|
|
5153
5950
|
const widgetSize = widget.Size();
|
|
5154
|
-
const
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5951
|
+
const box = {
|
|
5952
|
+
Position: { x: nodeBounds.Position.x + scaledPadding, y: startY },
|
|
5953
|
+
Size: { x: nodeBounds.Size.x - scaledPadding * 2, y: widgetSize.y * camera.zoom }
|
|
5954
|
+
};
|
|
5955
|
+
widget.Draw(ctx, box, camera.zoom, mousePosition);
|
|
5956
|
+
this.#widgetPositions.Push(box);
|
|
5957
|
+
startY += box.Size.y + scaledElementSpacing;
|
|
5160
5958
|
}
|
|
5161
5959
|
const messageStart = VectorPool.get();
|
|
5162
5960
|
messageStart.x = nodeBounds.Position.x + nodeBounds.Size.x / 2;
|
|
@@ -5193,6 +5991,13 @@ var FlowNode = class {
|
|
|
5193
5991
|
record(this.#outputPortPositions, this.#portOffsets.output);
|
|
5194
5992
|
}
|
|
5195
5993
|
layoutPortsWithoutDrawing(bounds, zoom) {
|
|
5994
|
+
if (!this.placePorts(bounds, zoom)) {
|
|
5995
|
+
return false;
|
|
5996
|
+
}
|
|
5997
|
+
this.#widgetPositions.Clear();
|
|
5998
|
+
return true;
|
|
5999
|
+
}
|
|
6000
|
+
placePorts(bounds, zoom) {
|
|
5196
6001
|
if (this.#portOffsets.input.length === 0 && this.#portOffsets.output.length === 0) {
|
|
5197
6002
|
return false;
|
|
5198
6003
|
}
|
|
@@ -5214,7 +6019,6 @@ var FlowNode = class {
|
|
|
5214
6019
|
};
|
|
5215
6020
|
project(this.#portOffsets.input, this.#inputPortPositions);
|
|
5216
6021
|
project(this.#portOffsets.output, this.#outputPortPositions);
|
|
5217
|
-
this.#widgetPositions.Clear();
|
|
5218
6022
|
return true;
|
|
5219
6023
|
}
|
|
5220
6024
|
};
|
|
@@ -5406,33 +6210,33 @@ var CodeBlockEntry = class {
|
|
|
5406
6210
|
}
|
|
5407
6211
|
render(ctx, position, scale, maxWidth) {
|
|
5408
6212
|
this.#calculateLayout(ctx, maxWidth);
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
}
|
|
5415
|
-
const y = position.y + max + scale * 5;
|
|
6213
|
+
const padding = Theme.Note.CodeBlock.Padding * scale;
|
|
6214
|
+
const lineCount = this.#calculatedEntries.Count();
|
|
6215
|
+
const lastTop = lineCount === 0 ? 0 : this.#calculatedPositions.At(lineCount - 1).y;
|
|
6216
|
+
const contentHeight = lineCount === 0 ? 0 : (lastTop + this.#text.style().getSize()) * scale;
|
|
6217
|
+
const height2 = contentHeight + padding * 2;
|
|
5416
6218
|
ctx.fillStyle = Theme.Note.CodeBlock.BackgroundColor;
|
|
5417
6219
|
ctx.beginPath();
|
|
5418
6220
|
ctx.roundRect(
|
|
5419
6221
|
position.x,
|
|
5420
6222
|
position.y,
|
|
5421
6223
|
maxWidth * scale,
|
|
5422
|
-
|
|
6224
|
+
height2,
|
|
5423
6225
|
Theme.Note.CodeBlock.BorderRadius * scale
|
|
5424
6226
|
);
|
|
5425
6227
|
ctx.fill();
|
|
5426
|
-
|
|
6228
|
+
const baseline = ctx.textBaseline;
|
|
6229
|
+
ctx.textBaseline = "top";
|
|
6230
|
+
for (let i = 0; i < lineCount; i++) {
|
|
5427
6231
|
const entry = this.#calculatedEntries.At(i);
|
|
5428
6232
|
const pos = this.#calculatedPositions.At(i);
|
|
5429
6233
|
entry.render(ctx, scale, {
|
|
5430
6234
|
x: pos.x * scale + position.x + padding,
|
|
5431
6235
|
y: pos.y * scale + position.y + padding
|
|
5432
6236
|
});
|
|
5433
|
-
max = Math.max(max, pos.y * scale);
|
|
5434
6237
|
}
|
|
5435
|
-
|
|
6238
|
+
ctx.textBaseline = baseline;
|
|
6239
|
+
return height2;
|
|
5436
6240
|
}
|
|
5437
6241
|
};
|
|
5438
6242
|
var UnorderedListMarkdownEntry = class {
|
|
@@ -5810,9 +6614,10 @@ var MarkdownSyntaxParser = class {
|
|
|
5810
6614
|
this.#inc();
|
|
5811
6615
|
this.#inc();
|
|
5812
6616
|
this.#inc();
|
|
5813
|
-
const codeBlockText = this.#originalText.substring(start, end);
|
|
6617
|
+
const codeBlockText = this.#originalText.substring(start, end).replace(/\r?\n$/, "");
|
|
5814
6618
|
const entryText = new Text(codeBlockText);
|
|
5815
6619
|
this.#assignStandardStyling([entryText]);
|
|
6620
|
+
entryText.setColor(Theme.Note.CodeBlock.FontColor);
|
|
5816
6621
|
return new CodeBlockEntry(entryText);
|
|
5817
6622
|
}
|
|
5818
6623
|
parse() {
|
|
@@ -5867,6 +6672,7 @@ var BOX_SIZE = 10;
|
|
|
5867
6672
|
var FlowNote = class {
|
|
5868
6673
|
#originalText;
|
|
5869
6674
|
#document;
|
|
6675
|
+
#parsedForTheme = -1;
|
|
5870
6676
|
#width;
|
|
5871
6677
|
#edittingStyle;
|
|
5872
6678
|
// Runtime ================================================================
|
|
@@ -5909,6 +6715,7 @@ var FlowNote = class {
|
|
|
5909
6715
|
setText(text) {
|
|
5910
6716
|
this.#originalText = text;
|
|
5911
6717
|
this.#document = BuildMarkdown(this.#originalText);
|
|
6718
|
+
this.#parsedForTheme = themeVersion();
|
|
5912
6719
|
for (let i = 0; i < this.#contentChangeCallbacks.length; i++) {
|
|
5913
6720
|
this.#contentChangeCallbacks[i](this, text);
|
|
5914
6721
|
}
|
|
@@ -5996,6 +6803,10 @@ var FlowNote = class {
|
|
|
5996
6803
|
}
|
|
5997
6804
|
camera.graphSpaceToScreenSpace(this.#position, this.#tempPosition);
|
|
5998
6805
|
CopyVector2(this.#lastRenderedBox.Position, this.#tempPosition);
|
|
6806
|
+
if (this.#parsedForTheme !== themeVersion()) {
|
|
6807
|
+
this.#document = BuildMarkdown(this.#originalText);
|
|
6808
|
+
this.#parsedForTheme = themeVersion();
|
|
6809
|
+
}
|
|
5999
6810
|
const startY = this.#tempPosition.y;
|
|
6000
6811
|
const lineSpacing = Theme.Note.EntrySpacing * camera.zoom;
|
|
6001
6812
|
ctx.textAlign = "left" /* Left */;
|
|
@@ -6691,13 +7502,15 @@ var Minimap = class {
|
|
|
6691
7502
|
// src/graph.ts
|
|
6692
7503
|
function BuildBackgroundRenderer(backgroundColor) {
|
|
6693
7504
|
return (canvas, context, position, scale) => {
|
|
6694
|
-
context.fillStyle = backgroundColor;
|
|
7505
|
+
context.fillStyle = backgroundColor ?? Theme.Graph.BackgroundColor;
|
|
6695
7506
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
6696
|
-
const alpha =
|
|
7507
|
+
const alpha = Clamp01(scale - 0.3);
|
|
6697
7508
|
if (alpha <= 0) {
|
|
6698
7509
|
return;
|
|
6699
7510
|
}
|
|
6700
|
-
context.
|
|
7511
|
+
context.save();
|
|
7512
|
+
context.globalAlpha = alpha;
|
|
7513
|
+
context.fillStyle = Theme.Graph.DotColor;
|
|
6701
7514
|
const spacing = 100;
|
|
6702
7515
|
const pi2 = 2 * Math.PI;
|
|
6703
7516
|
const dotScale = 2 * scale;
|
|
@@ -6710,6 +7523,7 @@ function BuildBackgroundRenderer(backgroundColor) {
|
|
|
6710
7523
|
context.fill();
|
|
6711
7524
|
}
|
|
6712
7525
|
}
|
|
7526
|
+
context.restore();
|
|
6713
7527
|
};
|
|
6714
7528
|
}
|
|
6715
7529
|
var contextMenuGroup = "graph-context-menu";
|
|
@@ -6760,9 +7574,9 @@ var GraphView = class {
|
|
|
6760
7574
|
const results = {};
|
|
6761
7575
|
for (let i = 0; i < this.#subsystems.length; i++) {
|
|
6762
7576
|
TimeExecution("Render_Subsystem_" + i, () => {
|
|
6763
|
-
|
|
6764
|
-
if (
|
|
6765
|
-
|
|
7577
|
+
const layer = this.#subsystems[i].render(ctx, camera, mousePosition);
|
|
7578
|
+
if (layer?.cursorStyle) {
|
|
7579
|
+
results.cursorStyle = layer.cursorStyle;
|
|
6766
7580
|
}
|
|
6767
7581
|
});
|
|
6768
7582
|
}
|
|
@@ -6822,8 +7636,7 @@ var NodeFlowGraph = class {
|
|
|
6822
7636
|
if (config?.backgroundRenderer !== void 0) {
|
|
6823
7637
|
this.#backgroundRenderer = config?.backgroundRenderer;
|
|
6824
7638
|
} else {
|
|
6825
|
-
|
|
6826
|
-
this.#backgroundRenderer = BuildBackgroundRenderer(backgroundColor);
|
|
7639
|
+
this.#backgroundRenderer = BuildBackgroundRenderer(config?.backgroundColor);
|
|
6827
7640
|
}
|
|
6828
7641
|
this.#minimap = new Minimap(config?.minimap);
|
|
6829
7642
|
this.#mainNodeSubsystem.setConnectionReleasedHandler((dangling) => {
|
|
@@ -7241,10 +8054,12 @@ var NodeFlowGraph = class {
|
|
|
7241
8054
|
this.#cursor = "default" /* Default */;
|
|
7242
8055
|
TimeExecution("Render_Background", this.#renderBackground.bind(this));
|
|
7243
8056
|
TimeExecution("Render_View_" + this.#currentView, () => {
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
8057
|
+
placeEditsDuring(this.#canvas, () => {
|
|
8058
|
+
let results = this.currentView().render(this.#ctx, this.#camera, this.#mousePosition);
|
|
8059
|
+
if (results?.cursorStyle) {
|
|
8060
|
+
this.#cursor = results?.cursorStyle;
|
|
8061
|
+
}
|
|
8062
|
+
});
|
|
7248
8063
|
});
|
|
7249
8064
|
TimeExecution("Render_Minimap", () => {
|
|
7250
8065
|
this.#minimap.render(this.#ctx, this.#canvas, this.#camera, this.#mainNodeSubsystem.getNodes());
|
|
@@ -7289,11 +8104,15 @@ export {
|
|
|
7289
8104
|
ColorWidget,
|
|
7290
8105
|
Connection,
|
|
7291
8106
|
ContextMenuItemState,
|
|
8107
|
+
CursorStyle,
|
|
7292
8108
|
DefaultConnectionRenderer,
|
|
8109
|
+
DefaultTheme,
|
|
7293
8110
|
FlowNode,
|
|
7294
8111
|
FlowNote,
|
|
7295
8112
|
GlobalWidgetFactory,
|
|
8113
|
+
GraphView,
|
|
7296
8114
|
ImageWidget,
|
|
8115
|
+
InBox,
|
|
7297
8116
|
MessageType,
|
|
7298
8117
|
Minimap,
|
|
7299
8118
|
NodeFactory,
|
|
@@ -7303,16 +8122,28 @@ export {
|
|
|
7303
8122
|
Port,
|
|
7304
8123
|
PortType,
|
|
7305
8124
|
Publisher,
|
|
8125
|
+
RowWidget,
|
|
7306
8126
|
SliderWidget,
|
|
7307
8127
|
StringWidget,
|
|
7308
8128
|
TextWidget,
|
|
7309
8129
|
Theme,
|
|
7310
8130
|
ToggleWidget,
|
|
8131
|
+
ValueWidget,
|
|
8132
|
+
WidgetBase,
|
|
7311
8133
|
WidgetFactory,
|
|
8134
|
+
drawField,
|
|
8135
|
+
drawFieldBox,
|
|
8136
|
+
drawFieldText,
|
|
8137
|
+
editInPlace,
|
|
8138
|
+
formatNumber,
|
|
8139
|
+
onThemeChange,
|
|
7312
8140
|
portConfigTypes,
|
|
7313
8141
|
portsCompatible,
|
|
7314
8142
|
renderContinuously,
|
|
7315
8143
|
requestRender,
|
|
7316
|
-
|
|
8144
|
+
setTheme,
|
|
8145
|
+
themeVersion,
|
|
8146
|
+
typeSetsCompatible,
|
|
8147
|
+
widgetFont
|
|
7317
8148
|
};
|
|
7318
8149
|
//# sourceMappingURL=index.mjs.map
|