@elicdavis/node-flow 0.1.19 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +386 -227
- package/dist/index.d.ts +386 -227
- package/dist/index.js +1884 -1076
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1867 -1075
- package/dist/index.mjs.map +1 -1
- package/index.html +1134 -329
- package/package.json +1 -1
- package/src/connection.ts +30 -5
- package/src/contextMenu.ts +66 -34
- package/src/graph.ts +12 -10
- package/src/index.ts +26 -12
- package/src/markdown/entry.ts +11 -15
- package/src/markdown/syntaxParser.ts +2 -1
- package/src/node.ts +162 -111
- package/src/nodes/subsystem.ts +123 -29
- package/src/notes/note.ts +9 -1
- package/src/organize.ts +540 -111
- package/src/popup.ts +4 -2
- package/src/port.ts +83 -6
- package/src/styles/bevel.ts +31 -0
- package/src/styles/cursor.ts +1 -0
- package/src/styles/text.ts +4 -4
- package/src/theme.ts +179 -92
- package/src/types/text.ts +5 -1
- package/src/utils/color.ts +1 -1
- package/src/widgets/button.ts +52 -107
- package/src/widgets/color.ts +91 -150
- package/src/widgets/factory.ts +89 -46
- package/src/widgets/field.ts +99 -0
- package/src/widgets/image.ts +107 -165
- package/src/widgets/inplace.ts +84 -0
- package/src/widgets/number.ts +122 -154
- package/src/widgets/row.ts +138 -0
- package/src/widgets/slider.ts +101 -216
- package/src/widgets/string.ts +39 -130
- package/src/widgets/text.ts +81 -92
- package/src/widgets/toggle.ts +62 -207
- package/src/widgets/value.ts +127 -0
- package/src/widgets/widget.ts +180 -34
- package/test/context_menu.test.ts +82 -0
- package/test/cursor.test.ts +17 -0
- package/test/hidden_port.test.ts +125 -0
- package/test/hover_after_removal.test.ts +72 -0
- package/test/markdown.test.ts +2 -2
- package/test/markdown_code_block.test.ts +58 -0
- package/test/multi_input.test.ts +124 -0
- package/test/organize.test.ts +117 -1
- package/test/property_binding.test.ts +49 -0
- package/test/support.ts +175 -0
- package/test/theme.test.ts +86 -0
- package/test/widget_click.test.ts +111 -97
- package/test/widget_construction.test.ts +5 -4
- package/test/widgets.test.ts +333 -0
- package/test/wire_follows_camera.test.ts +57 -0
package/src/node.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Port, PortConfig, PortType } from "./port";
|
|
1
|
+
import { ARRAY_SLOT_PITCH, Port, PortConfig, PortType } from "./port";
|
|
2
2
|
import { requestRender } from "./render_scheduler";
|
|
3
3
|
import { FontWeight, TextStyle, TextStyleConfig, TextStyleFallback } from "./styles/text";
|
|
4
4
|
import { Box, InBox } from "./types/box";
|
|
@@ -7,8 +7,9 @@ import { Widget } from './widgets/widget';
|
|
|
7
7
|
import { List } from './types/list';
|
|
8
8
|
import { BoxStyle, BoxStyleConfig, BoxStyleWithFallback } from "./styles/box";
|
|
9
9
|
import { Text } from "./types/text";
|
|
10
|
-
import { GlobalWidgetFactory } from "./widgets/factory";
|
|
11
|
-
import { Theme } from "./theme";
|
|
10
|
+
import { GlobalWidgetFactory, WidgetConfig } from "./widgets/factory";
|
|
11
|
+
import { Theme, themeVersion } from "./theme";
|
|
12
|
+
import { drawBevel } from "./styles/bevel";
|
|
12
13
|
import { TextAlign } from "./styles/canvasTextAlign";
|
|
13
14
|
import { TextBaseline } from "./styles/canvasTextBaseline";
|
|
14
15
|
import { CombineContextMenus, ContextMenuConfig, ContextMenuItemConfig } from "./contextMenu";
|
|
@@ -32,15 +33,11 @@ const MINIMUM_NODE_WIDTH = 150;
|
|
|
32
33
|
|
|
33
34
|
type AnyPropertyChangeCallback = (propertyName: string, oldValue: any, newValue: any) => void
|
|
34
35
|
type PropertyChangeCallback = (oldValue: any, newValue: any) => void
|
|
36
|
+
type PropertyEditCallback = (newValue: any) => void
|
|
35
37
|
type TitleChangeCallback = (node: FlowNode, oldTitle: string, newTitle: string) => void
|
|
36
38
|
type InfoChangeCallback = (node: FlowNode, oldInfo: string, newInfo: string) => void
|
|
37
39
|
|
|
38
40
|
|
|
39
|
-
export interface WidgetConfig {
|
|
40
|
-
type?: string,
|
|
41
|
-
config?: any
|
|
42
|
-
}
|
|
43
|
-
|
|
44
41
|
export interface FlowNodeTitleConfig {
|
|
45
42
|
textStyle?: TextStyleConfig;
|
|
46
43
|
color?: string;
|
|
@@ -138,6 +135,7 @@ export interface NodeIntersection {
|
|
|
138
135
|
// Widget
|
|
139
136
|
Widget?: Widget
|
|
140
137
|
WidgetIndex?: number
|
|
138
|
+
WidgetBox?: Box
|
|
141
139
|
|
|
142
140
|
}
|
|
143
141
|
|
|
@@ -218,15 +216,19 @@ export class FlowNode {
|
|
|
218
216
|
|
|
219
217
|
// Styling ================================================================
|
|
220
218
|
|
|
221
|
-
#
|
|
219
|
+
#styleConfig: FlowNodeStyle | undefined;
|
|
220
|
+
|
|
221
|
+
#styledForTheme = -1;
|
|
222
222
|
|
|
223
|
-
#
|
|
223
|
+
#titleColor!: string;
|
|
224
224
|
|
|
225
|
-
#
|
|
225
|
+
#selectedStyle!: BoxStyle;
|
|
226
|
+
|
|
227
|
+
#stateStyles = new Map<NodeState, BoxStyle>();
|
|
226
228
|
|
|
227
229
|
#padding: number;
|
|
228
230
|
|
|
229
|
-
#portTextStyle
|
|
231
|
+
#portTextStyle!: TextStyle;
|
|
230
232
|
|
|
231
233
|
#elementSpacing: number;
|
|
232
234
|
|
|
@@ -246,6 +248,8 @@ export class FlowNode {
|
|
|
246
248
|
|
|
247
249
|
#registeredPropertyChangeCallbacks: Map<string, Array<PropertyChangeCallback>>;
|
|
248
250
|
|
|
251
|
+
#registeredPropertyEditCallbacks = new Map<string, Array<PropertyEditCallback>>();
|
|
252
|
+
|
|
249
253
|
constructor(config?: FlowNodeConfig) {
|
|
250
254
|
this.#input = new Array<Port>();
|
|
251
255
|
this.#output = new Array<Port>();
|
|
@@ -307,65 +311,21 @@ export class FlowNode {
|
|
|
307
311
|
this.#position = config?.position === undefined ? { x: 0, y: 0 } : config.position;
|
|
308
312
|
this.#title = new Text(
|
|
309
313
|
config?.title === undefined ? "" : config.title,
|
|
310
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
311
|
-
size: 16,
|
|
312
|
-
weight: FontWeight.Bold,
|
|
313
|
-
color: Theme.Node.FontColor
|
|
314
|
-
})
|
|
314
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 16, weight: FontWeight.Bold })
|
|
315
315
|
);
|
|
316
316
|
|
|
317
317
|
this.#subTitle = new Text(
|
|
318
318
|
config?.subTitle === undefined ? "" : config.subTitle,
|
|
319
|
-
TextStyleFallback(config?.style?.title?.textStyle, {
|
|
320
|
-
size: 10,
|
|
321
|
-
weight: FontWeight.Bold,
|
|
322
|
-
color: Theme.Node.FontColor
|
|
323
|
-
})
|
|
319
|
+
TextStyleFallback(config?.style?.title?.textStyle, { size: 10, weight: FontWeight.Bold })
|
|
324
320
|
);
|
|
325
321
|
|
|
326
|
-
this.#infoSymbol = new Text(
|
|
327
|
-
"i",
|
|
328
|
-
{
|
|
329
|
-
size: 13,
|
|
330
|
-
weight: FontWeight.Bold,
|
|
331
|
-
color: Theme.Node.FontColor
|
|
332
|
-
}
|
|
333
|
-
);
|
|
322
|
+
this.#infoSymbol = new Text("i", { size: 13, weight: FontWeight.Bold });
|
|
334
323
|
this.#infoText = config?.info ? config?.info : "";
|
|
335
324
|
|
|
336
|
-
this.#titleColor = config?.style?.title?.color === undefined ? "#154050" : config?.style?.title?.color;
|
|
337
|
-
|
|
338
325
|
this.#padding = config?.style?.title?.padding === undefined ? 15 : config?.style?.title?.padding;
|
|
339
326
|
|
|
340
|
-
this.#
|
|
341
|
-
this.#
|
|
342
|
-
border: { color: Theme.Node.Border.Idle, size: 1 },
|
|
343
|
-
radius: Theme.Node.BorderRadius,
|
|
344
|
-
color: Theme.Node.BackgroundColor,
|
|
345
|
-
})));
|
|
346
|
-
this.#stateStyles.set(NodeState.MouseOver, new BoxStyle(BoxStyleWithFallback(config?.style?.mouseOver, {
|
|
347
|
-
border: { color: Theme.Node.Border.MouseOver, size: 1.1, },
|
|
348
|
-
radius: Theme.Node.BorderRadius,
|
|
349
|
-
color: Theme.Node.BackgroundColor,
|
|
350
|
-
})));
|
|
351
|
-
this.#stateStyles.set(NodeState.Grabbed, new BoxStyle(BoxStyleWithFallback(config?.style?.grabbed, {
|
|
352
|
-
border: { color: Theme.Node.Border.Grabbed, size: 2, },
|
|
353
|
-
radius: Theme.Node.BorderRadius,
|
|
354
|
-
color: Theme.Node.BackgroundColor,
|
|
355
|
-
})));
|
|
356
|
-
|
|
357
|
-
this.#selectedStyle = new BoxStyle(BoxStyleWithFallback(config?.style?.selected, {
|
|
358
|
-
border: { color: Theme.Node.Border.Selected, size: 1, },
|
|
359
|
-
radius: Theme.Node.BorderRadius,
|
|
360
|
-
color: Theme.Node.BackgroundColor,
|
|
361
|
-
}));
|
|
362
|
-
|
|
363
|
-
this.#portTextStyle = new TextStyle({
|
|
364
|
-
size: config?.style?.portText?.size === undefined ? 14 : config?.style?.portText?.size,
|
|
365
|
-
color: config?.style?.portText?.color === undefined ? Theme.Node.Port.FontColor : config?.style?.portText?.color,
|
|
366
|
-
font: config?.style?.portText?.font,
|
|
367
|
-
weight: config?.style?.portText?.weight,
|
|
368
|
-
});
|
|
327
|
+
this.#styleConfig = config?.style;
|
|
328
|
+
this.#applyStyle();
|
|
369
329
|
|
|
370
330
|
if (config?.inputs !== undefined) {
|
|
371
331
|
for (let i = 0; i < config.inputs.length; i++) {
|
|
@@ -380,12 +340,8 @@ export class FlowNode {
|
|
|
380
340
|
}
|
|
381
341
|
|
|
382
342
|
if (config?.widgets !== undefined) {
|
|
383
|
-
for (
|
|
384
|
-
|
|
385
|
-
if (widget.type === undefined) {
|
|
386
|
-
continue;
|
|
387
|
-
}
|
|
388
|
-
this.addWidget(GlobalWidgetFactory.create(this, widget.type, widget.config))
|
|
343
|
+
for (const widget of config.widgets) {
|
|
344
|
+
this.addWidget(GlobalWidgetFactory.createFromConfig(this, widget));
|
|
389
345
|
}
|
|
390
346
|
}
|
|
391
347
|
}
|
|
@@ -454,7 +410,8 @@ export class FlowNode {
|
|
|
454
410
|
|
|
455
411
|
public setProperty(name: string, value: any): void {
|
|
456
412
|
const oldValue = this.#data[name];
|
|
457
|
-
|
|
413
|
+
// Object.is, not ===: NaN never equals itsel.
|
|
414
|
+
if (Object.is(oldValue, value)) {
|
|
458
415
|
return;
|
|
459
416
|
}
|
|
460
417
|
|
|
@@ -475,6 +432,34 @@ export class FlowNode {
|
|
|
475
432
|
}
|
|
476
433
|
}
|
|
477
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Listens for a person changing the property through a widget, but not
|
|
437
|
+
* for the property being set from code.
|
|
438
|
+
*/
|
|
439
|
+
public addPropertyEditListener(name: string, callback: PropertyEditCallback): void {
|
|
440
|
+
let callbacks = this.#registeredPropertyEditCallbacks.get(name);
|
|
441
|
+
if (callbacks === undefined) {
|
|
442
|
+
callbacks = [];
|
|
443
|
+
this.#registeredPropertyEditCallbacks.set(name, callbacks);
|
|
444
|
+
}
|
|
445
|
+
callbacks.push(callback);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Sets the property as a person's finished edit, so edit listeners hear
|
|
450
|
+
* it even when the value was already showing.
|
|
451
|
+
*/
|
|
452
|
+
public editProperty(name: string, value: any): void {
|
|
453
|
+
this.setProperty(name, value);
|
|
454
|
+
const callbacks = this.#registeredPropertyEditCallbacks.get(name);
|
|
455
|
+
if (callbacks === undefined) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
for (const callback of [...callbacks]) {
|
|
459
|
+
callback(value);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
478
463
|
public getProperty(name: string): any {
|
|
479
464
|
return this.#data[name];
|
|
480
465
|
}
|
|
@@ -788,6 +773,7 @@ export class FlowNode {
|
|
|
788
773
|
// }
|
|
789
774
|
|
|
790
775
|
public calculateBounds(ctx: CanvasRenderingContext2D, camera: Camera): Box {
|
|
776
|
+
this.#followTheme();
|
|
791
777
|
const tempMeasurement = Zero();
|
|
792
778
|
|
|
793
779
|
const doublePadding = this.#padding * 2;
|
|
@@ -807,29 +793,23 @@ export class FlowNode {
|
|
|
807
793
|
}
|
|
808
794
|
|
|
809
795
|
size.x += doublePadding;
|
|
810
|
-
size.y += doublePadding
|
|
796
|
+
size.y += doublePadding;
|
|
811
797
|
|
|
812
|
-
for (
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
size.x = Math.max(size.x, tempMeasurement.x + doublePadding)
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
size.y += (this.#elementSpacing * this.#output.length);
|
|
820
|
-
|
|
821
|
-
for (let i = 0; i < this.#output.length; i++) {
|
|
822
|
-
const port = this.#output[i];
|
|
798
|
+
for (const port of [...this.#input, ...this.#output]) {
|
|
799
|
+
if (port.hidden()) {
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
823
802
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
824
|
-
size.y += tempMeasurement.y;
|
|
803
|
+
size.y += this.#portRowHeight(port, tempMeasurement.y) + this.#elementSpacing;
|
|
825
804
|
size.x = Math.max(size.x, tempMeasurement.x + doublePadding)
|
|
826
805
|
}
|
|
827
806
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
807
|
+
for (const widget of this.#widgets) {
|
|
808
|
+
if (widget.Hidden?.()) {
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
const eleSize = widget.Size();
|
|
812
|
+
size.y += eleSize.y + this.#elementSpacing;
|
|
833
813
|
size.x = Math.max(size.x, (eleSize.x) + doublePadding)
|
|
834
814
|
}
|
|
835
815
|
|
|
@@ -933,7 +913,7 @@ export class FlowNode {
|
|
|
933
913
|
}
|
|
934
914
|
|
|
935
915
|
for (let i = 0; i < this.#inputPortPositions.Count(); i++) {
|
|
936
|
-
if (InBox(this.#inputPortPositions.At(i), position)) {
|
|
916
|
+
if (!this.#input[i].hidden() && InBox(this.#inputPortPositions.At(i), position)) {
|
|
937
917
|
intersection.Node = this;
|
|
938
918
|
intersection.PortIndex = i;
|
|
939
919
|
intersection.PortIsInput = true;
|
|
@@ -942,7 +922,7 @@ export class FlowNode {
|
|
|
942
922
|
}
|
|
943
923
|
|
|
944
924
|
for (let i = 0; i < this.#outputPortPositions.Count(); i++) {
|
|
945
|
-
if (InBox(this.#outputPortPositions.At(i), position)) {
|
|
925
|
+
if (!this.#output[i].hidden() && InBox(this.#outputPortPositions.At(i), position)) {
|
|
946
926
|
intersection.Node = this;
|
|
947
927
|
intersection.PortIndex = i;
|
|
948
928
|
intersection.PortIsInput = false;
|
|
@@ -951,10 +931,12 @@ export class FlowNode {
|
|
|
951
931
|
}
|
|
952
932
|
|
|
953
933
|
for (let i = 0; i < this.#widgetPositions.Count(); i++) {
|
|
954
|
-
|
|
934
|
+
const widget = this.#widgets[i];
|
|
935
|
+
if (widget !== undefined && !widget.Hidden?.() && InBox(this.#widgetPositions.At(i), position)) {
|
|
955
936
|
intersection.Node = this;
|
|
956
937
|
intersection.WidgetIndex = i;
|
|
957
|
-
intersection.Widget =
|
|
938
|
+
intersection.Widget = widget;
|
|
939
|
+
intersection.WidgetBox = this.#widgetPositions.At(i);
|
|
958
940
|
}
|
|
959
941
|
}
|
|
960
942
|
|
|
@@ -974,11 +956,6 @@ export class FlowNode {
|
|
|
974
956
|
}
|
|
975
957
|
|
|
976
958
|
setTitle(newTitle: string): void {
|
|
977
|
-
if (!this.#canEditTitle) {
|
|
978
|
-
console.warn("setTitle instruction ignored, as node has been marked un-editable");
|
|
979
|
-
return;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
959
|
let cleaned = newTitle;
|
|
983
960
|
if (cleaned === null || cleaned === undefined) {
|
|
984
961
|
cleaned = "";
|
|
@@ -999,10 +976,6 @@ export class FlowNode {
|
|
|
999
976
|
}
|
|
1000
977
|
|
|
1001
978
|
setInfo(newInfo: string): void {
|
|
1002
|
-
if (!this.#canEditInfo) {
|
|
1003
|
-
console.warn("setInfo instruction ignored, as node has been marked un-editable");
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
979
|
let cleaned = newInfo;
|
|
1007
980
|
if (cleaned === null || cleaned === undefined) {
|
|
1008
981
|
cleaned = "";
|
|
@@ -1026,6 +999,13 @@ export class FlowNode {
|
|
|
1026
999
|
return this.#inputPortPositions.At(index);
|
|
1027
1000
|
}
|
|
1028
1001
|
|
|
1002
|
+
widgetPosition(index: number): Box | undefined {
|
|
1003
|
+
if (index < 0 || index >= this.#widgetPositions.Count()) {
|
|
1004
|
+
return undefined;
|
|
1005
|
+
}
|
|
1006
|
+
return this.#widgetPositions.At(index);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1029
1009
|
inputPort(index: number): Port {
|
|
1030
1010
|
return this.#input[index];
|
|
1031
1011
|
}
|
|
@@ -1046,6 +1026,42 @@ export class FlowNode {
|
|
|
1046
1026
|
return this.#output.length;
|
|
1047
1027
|
}
|
|
1048
1028
|
|
|
1029
|
+
#applyStyle(): void {
|
|
1030
|
+
const style = this.#styleConfig;
|
|
1031
|
+
const titleText = style?.title?.textStyle?.color ?? Theme.Node.Title.FontColor;
|
|
1032
|
+
this.#title.style().setColor(titleText);
|
|
1033
|
+
this.#subTitle.style().setColor(titleText);
|
|
1034
|
+
this.#infoSymbol.style().setColor(titleText);
|
|
1035
|
+
|
|
1036
|
+
this.#titleColor = style?.title?.color ?? Theme.Node.Title.Color;
|
|
1037
|
+
|
|
1038
|
+
const box = (config: BoxStyleConfig | undefined, border: string, size: number) =>
|
|
1039
|
+
new BoxStyle(BoxStyleWithFallback(config, {
|
|
1040
|
+
border: { color: border, size },
|
|
1041
|
+
radius: Theme.Node.BorderRadius,
|
|
1042
|
+
color: Theme.Node.BackgroundColor,
|
|
1043
|
+
}));
|
|
1044
|
+
this.#stateStyles.set(NodeState.Idle, box(style?.idle, Theme.Node.Border.Idle, 1));
|
|
1045
|
+
this.#stateStyles.set(NodeState.MouseOver, box(style?.mouseOver, Theme.Node.Border.MouseOver, 1.1));
|
|
1046
|
+
this.#stateStyles.set(NodeState.Grabbed, box(style?.grabbed, Theme.Node.Border.Grabbed, 2));
|
|
1047
|
+
this.#selectedStyle = box(style?.selected, Theme.Node.Border.Selected, 1);
|
|
1048
|
+
|
|
1049
|
+
this.#portTextStyle = new TextStyle({
|
|
1050
|
+
size: style?.portText?.size ?? 14,
|
|
1051
|
+
color: style?.portText?.color ?? Theme.Node.Port.FontColor,
|
|
1052
|
+
font: style?.portText?.font,
|
|
1053
|
+
weight: style?.portText?.weight,
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
this.#styledForTheme = themeVersion();
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
#followTheme(): void {
|
|
1060
|
+
if (this.#styledForTheme !== themeVersion()) {
|
|
1061
|
+
this.#applyStyle();
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1049
1065
|
#calculateStyle(state: NodeState): BoxStyle {
|
|
1050
1066
|
// Let the node being selected override the idle style
|
|
1051
1067
|
if (this.#selected && state === NodeState.Idle) {
|
|
@@ -1067,10 +1083,25 @@ export class FlowNode {
|
|
|
1067
1083
|
this.#messages = [];
|
|
1068
1084
|
}
|
|
1069
1085
|
|
|
1086
|
+
#portRowHeight(port: Port, textHeight: number): number {
|
|
1087
|
+
const slots = port.slots();
|
|
1088
|
+
return slots > 1 ? Math.max(textHeight, slots * ARRAY_SLOT_PITCH) : textHeight;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1070
1091
|
public hasBeenLaidOut(): boolean {
|
|
1071
1092
|
return this.#laidOut;
|
|
1072
1093
|
}
|
|
1073
1094
|
|
|
1095
|
+
/**
|
|
1096
|
+
* Throws away where the ports were last placed, so wires wait for the
|
|
1097
|
+
* next draw instead of pointing at the old spots.
|
|
1098
|
+
*/
|
|
1099
|
+
public invalidateLayout(): void {
|
|
1100
|
+
this.#laidOut = false;
|
|
1101
|
+
this.#portOffsets = { input: [], output: [] };
|
|
1102
|
+
requestRender();
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1074
1105
|
#laidOut: boolean = false;
|
|
1075
1106
|
|
|
1076
1107
|
render(ctx: CanvasRenderingContext2D, camera: Camera, state: NodeState, mousePosition: Vector2 | undefined, postProcess: PassSubsystem): void {
|
|
@@ -1117,7 +1148,10 @@ export class FlowNode {
|
|
|
1117
1148
|
[nodeStyle.radius() * camera.zoom, nodeStyle.radius() * camera.zoom, 0, 0]
|
|
1118
1149
|
);
|
|
1119
1150
|
ctx.fill();
|
|
1120
|
-
|
|
1151
|
+
|
|
1152
|
+
if (Theme.Node.Bevel !== null) {
|
|
1153
|
+
drawBevel(ctx, nodeBounds, camera.zoom, Theme.Node.Bevel, false);
|
|
1154
|
+
}
|
|
1121
1155
|
|
|
1122
1156
|
const titlePosition = VectorPool.get();
|
|
1123
1157
|
titlePosition.x = nodeBounds.Position.x + (nodeBounds.Size.x / 2)
|
|
@@ -1217,8 +1251,12 @@ export class FlowNode {
|
|
|
1217
1251
|
for (let i = 0; i < this.#input.length; i++) {
|
|
1218
1252
|
ctx.textAlign = TextAlign.Left;
|
|
1219
1253
|
const port = this.#input[i];
|
|
1254
|
+
if (port.hidden()) {
|
|
1255
|
+
this.#inputPortPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
|
|
1256
|
+
continue;
|
|
1257
|
+
}
|
|
1220
1258
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
1221
|
-
const portHeight = tempMeasurement.y * camera.zoom;
|
|
1259
|
+
const portHeight = this.#portRowHeight(port, tempMeasurement.y) * camera.zoom;
|
|
1222
1260
|
const position = VectorPool.get();
|
|
1223
1261
|
|
|
1224
1262
|
position.x = nodeBounds.Position.x;
|
|
@@ -1239,6 +1277,10 @@ export class FlowNode {
|
|
|
1239
1277
|
ctx.textAlign = TextAlign.Right;
|
|
1240
1278
|
for (let i = 0; i < this.#output.length; i++) {
|
|
1241
1279
|
const port = this.#output[i];
|
|
1280
|
+
if (port.hidden()) {
|
|
1281
|
+
this.#outputPortPositions.Push({ Position: { x: rightSide, y: startY }, Size: { x: 0, y: 0 } });
|
|
1282
|
+
continue;
|
|
1283
|
+
}
|
|
1242
1284
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
1243
1285
|
const portHeight = tempMeasurement.y * camera.zoom;
|
|
1244
1286
|
const position = VectorPool.get();
|
|
@@ -1258,15 +1300,18 @@ export class FlowNode {
|
|
|
1258
1300
|
// Widgets
|
|
1259
1301
|
for (let i = 0; i < this.#widgets.length; i++) {
|
|
1260
1302
|
const widget = this.#widgets[i];
|
|
1303
|
+
if (widget.Hidden?.()) {
|
|
1304
|
+
this.#widgetPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1261
1307
|
const widgetSize = widget.Size();
|
|
1262
|
-
const
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
startY += (widgetSize.y * camera.zoom) + scaledElementSpacing;
|
|
1308
|
+
const box: Box = {
|
|
1309
|
+
Position: { x: nodeBounds.Position.x + scaledPadding, y: startY },
|
|
1310
|
+
Size: { x: nodeBounds.Size.x - (scaledPadding * 2), y: widgetSize.y * camera.zoom },
|
|
1311
|
+
};
|
|
1312
|
+
widget.Draw(ctx, box, camera.zoom, mousePosition);
|
|
1313
|
+
this.#widgetPositions.Push(box);
|
|
1314
|
+
startY += box.Size.y + scaledElementSpacing;
|
|
1270
1315
|
}
|
|
1271
1316
|
|
|
1272
1317
|
// Messages
|
|
@@ -1311,6 +1356,14 @@ export class FlowNode {
|
|
|
1311
1356
|
}
|
|
1312
1357
|
|
|
1313
1358
|
public layoutPortsWithoutDrawing(bounds: Box, zoom: number): boolean {
|
|
1359
|
+
if (!this.placePorts(bounds, zoom)) {
|
|
1360
|
+
return false;
|
|
1361
|
+
}
|
|
1362
|
+
this.#widgetPositions.Clear();
|
|
1363
|
+
return true;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
public placePorts(bounds: Box, zoom: number): boolean {
|
|
1314
1367
|
if (this.#portOffsets.input.length === 0 && this.#portOffsets.output.length === 0) {
|
|
1315
1368
|
return false;
|
|
1316
1369
|
}
|
|
@@ -1335,8 +1388,6 @@ export class FlowNode {
|
|
|
1335
1388
|
project(this.#portOffsets.input, this.#inputPortPositions);
|
|
1336
1389
|
project(this.#portOffsets.output, this.#outputPortPositions);
|
|
1337
1390
|
|
|
1338
|
-
this.#widgetPositions.Clear();
|
|
1339
|
-
|
|
1340
1391
|
return true;
|
|
1341
1392
|
}
|
|
1342
1393
|
}
|