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