@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/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
|
}
|
|
@@ -476,6 +432,34 @@ export class FlowNode {
|
|
|
476
432
|
}
|
|
477
433
|
}
|
|
478
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
|
+
|
|
479
463
|
public getProperty(name: string): any {
|
|
480
464
|
return this.#data[name];
|
|
481
465
|
}
|
|
@@ -789,6 +773,7 @@ export class FlowNode {
|
|
|
789
773
|
// }
|
|
790
774
|
|
|
791
775
|
public calculateBounds(ctx: CanvasRenderingContext2D, camera: Camera): Box {
|
|
776
|
+
this.#followTheme();
|
|
792
777
|
const tempMeasurement = Zero();
|
|
793
778
|
|
|
794
779
|
const doublePadding = this.#padding * 2;
|
|
@@ -808,29 +793,23 @@ export class FlowNode {
|
|
|
808
793
|
}
|
|
809
794
|
|
|
810
795
|
size.x += doublePadding;
|
|
811
|
-
size.y += doublePadding
|
|
796
|
+
size.y += doublePadding;
|
|
812
797
|
|
|
813
|
-
for (
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
size.x = Math.max(size.x, tempMeasurement.x + doublePadding)
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
size.y += (this.#elementSpacing * this.#output.length);
|
|
821
|
-
|
|
822
|
-
for (let i = 0; i < this.#output.length; i++) {
|
|
823
|
-
const port = this.#output[i];
|
|
798
|
+
for (const port of [...this.#input, ...this.#output]) {
|
|
799
|
+
if (port.hidden()) {
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
824
802
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
825
|
-
size.y += tempMeasurement.y;
|
|
803
|
+
size.y += this.#portRowHeight(port, tempMeasurement.y) + this.#elementSpacing;
|
|
826
804
|
size.x = Math.max(size.x, tempMeasurement.x + doublePadding)
|
|
827
805
|
}
|
|
828
806
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
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;
|
|
834
813
|
size.x = Math.max(size.x, (eleSize.x) + doublePadding)
|
|
835
814
|
}
|
|
836
815
|
|
|
@@ -934,7 +913,7 @@ export class FlowNode {
|
|
|
934
913
|
}
|
|
935
914
|
|
|
936
915
|
for (let i = 0; i < this.#inputPortPositions.Count(); i++) {
|
|
937
|
-
if (InBox(this.#inputPortPositions.At(i), position)) {
|
|
916
|
+
if (!this.#input[i].hidden() && InBox(this.#inputPortPositions.At(i), position)) {
|
|
938
917
|
intersection.Node = this;
|
|
939
918
|
intersection.PortIndex = i;
|
|
940
919
|
intersection.PortIsInput = true;
|
|
@@ -943,7 +922,7 @@ export class FlowNode {
|
|
|
943
922
|
}
|
|
944
923
|
|
|
945
924
|
for (let i = 0; i < this.#outputPortPositions.Count(); i++) {
|
|
946
|
-
if (InBox(this.#outputPortPositions.At(i), position)) {
|
|
925
|
+
if (!this.#output[i].hidden() && InBox(this.#outputPortPositions.At(i), position)) {
|
|
947
926
|
intersection.Node = this;
|
|
948
927
|
intersection.PortIndex = i;
|
|
949
928
|
intersection.PortIsInput = false;
|
|
@@ -952,10 +931,12 @@ export class FlowNode {
|
|
|
952
931
|
}
|
|
953
932
|
|
|
954
933
|
for (let i = 0; i < this.#widgetPositions.Count(); i++) {
|
|
955
|
-
|
|
934
|
+
const widget = this.#widgets[i];
|
|
935
|
+
if (widget !== undefined && !widget.Hidden?.() && InBox(this.#widgetPositions.At(i), position)) {
|
|
956
936
|
intersection.Node = this;
|
|
957
937
|
intersection.WidgetIndex = i;
|
|
958
|
-
intersection.Widget =
|
|
938
|
+
intersection.Widget = widget;
|
|
939
|
+
intersection.WidgetBox = this.#widgetPositions.At(i);
|
|
959
940
|
}
|
|
960
941
|
}
|
|
961
942
|
|
|
@@ -1018,6 +999,13 @@ export class FlowNode {
|
|
|
1018
999
|
return this.#inputPortPositions.At(index);
|
|
1019
1000
|
}
|
|
1020
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
|
+
|
|
1021
1009
|
inputPort(index: number): Port {
|
|
1022
1010
|
return this.#input[index];
|
|
1023
1011
|
}
|
|
@@ -1038,6 +1026,42 @@ export class FlowNode {
|
|
|
1038
1026
|
return this.#output.length;
|
|
1039
1027
|
}
|
|
1040
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
|
+
|
|
1041
1065
|
#calculateStyle(state: NodeState): BoxStyle {
|
|
1042
1066
|
// Let the node being selected override the idle style
|
|
1043
1067
|
if (this.#selected && state === NodeState.Idle) {
|
|
@@ -1059,10 +1083,25 @@ export class FlowNode {
|
|
|
1059
1083
|
this.#messages = [];
|
|
1060
1084
|
}
|
|
1061
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
|
+
|
|
1062
1091
|
public hasBeenLaidOut(): boolean {
|
|
1063
1092
|
return this.#laidOut;
|
|
1064
1093
|
}
|
|
1065
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
|
+
|
|
1066
1105
|
#laidOut: boolean = false;
|
|
1067
1106
|
|
|
1068
1107
|
render(ctx: CanvasRenderingContext2D, camera: Camera, state: NodeState, mousePosition: Vector2 | undefined, postProcess: PassSubsystem): void {
|
|
@@ -1109,7 +1148,10 @@ export class FlowNode {
|
|
|
1109
1148
|
[nodeStyle.radius() * camera.zoom, nodeStyle.radius() * camera.zoom, 0, 0]
|
|
1110
1149
|
);
|
|
1111
1150
|
ctx.fill();
|
|
1112
|
-
|
|
1151
|
+
|
|
1152
|
+
if (Theme.Node.Bevel !== null) {
|
|
1153
|
+
drawBevel(ctx, nodeBounds, camera.zoom, Theme.Node.Bevel, false);
|
|
1154
|
+
}
|
|
1113
1155
|
|
|
1114
1156
|
const titlePosition = VectorPool.get();
|
|
1115
1157
|
titlePosition.x = nodeBounds.Position.x + (nodeBounds.Size.x / 2)
|
|
@@ -1209,8 +1251,12 @@ export class FlowNode {
|
|
|
1209
1251
|
for (let i = 0; i < this.#input.length; i++) {
|
|
1210
1252
|
ctx.textAlign = TextAlign.Left;
|
|
1211
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
|
+
}
|
|
1212
1258
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
1213
|
-
const portHeight = tempMeasurement.y * camera.zoom;
|
|
1259
|
+
const portHeight = this.#portRowHeight(port, tempMeasurement.y) * camera.zoom;
|
|
1214
1260
|
const position = VectorPool.get();
|
|
1215
1261
|
|
|
1216
1262
|
position.x = nodeBounds.Position.x;
|
|
@@ -1231,6 +1277,10 @@ export class FlowNode {
|
|
|
1231
1277
|
ctx.textAlign = TextAlign.Right;
|
|
1232
1278
|
for (let i = 0; i < this.#output.length; i++) {
|
|
1233
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
|
+
}
|
|
1234
1284
|
this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
|
|
1235
1285
|
const portHeight = tempMeasurement.y * camera.zoom;
|
|
1236
1286
|
const position = VectorPool.get();
|
|
@@ -1250,15 +1300,18 @@ export class FlowNode {
|
|
|
1250
1300
|
// Widgets
|
|
1251
1301
|
for (let i = 0; i < this.#widgets.length; i++) {
|
|
1252
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
|
+
}
|
|
1253
1307
|
const widgetSize = widget.Size();
|
|
1254
|
-
const
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
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;
|
|
1262
1315
|
}
|
|
1263
1316
|
|
|
1264
1317
|
// Messages
|
|
@@ -1303,6 +1356,14 @@ export class FlowNode {
|
|
|
1303
1356
|
}
|
|
1304
1357
|
|
|
1305
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 {
|
|
1306
1367
|
if (this.#portOffsets.input.length === 0 && this.#portOffsets.output.length === 0) {
|
|
1307
1368
|
return false;
|
|
1308
1369
|
}
|
|
@@ -1327,8 +1388,6 @@ export class FlowNode {
|
|
|
1327
1388
|
project(this.#portOffsets.input, this.#inputPortPositions);
|
|
1328
1389
|
project(this.#portOffsets.output, this.#outputPortPositions);
|
|
1329
1390
|
|
|
1330
|
-
this.#widgetPositions.Clear();
|
|
1331
|
-
|
|
1332
1391
|
return true;
|
|
1333
1392
|
}
|
|
1334
1393
|
}
|