Skip to content

Commit 5ac96d2

Browse files
committed
New Quick Menu
1 parent a55a6c1 commit 5ac96d2

File tree

12 files changed

+853
-18
lines changed

12 files changed

+853
-18
lines changed

index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,10 @@
172172
173173
\`\`\`
174174
// Create a canvas to render our graph to
175-
var canvas = document.createElement("canvas");
175+
let canvas = document.createElement("canvas");
176176
177177
// Create our Node Flow Graph
178-
var graph = new NodeFlowGraph(canvas)
178+
let graph = new NodeFlowGraph(canvas)
179179
\`\`\`
180180
`
181181
},
@@ -193,7 +193,7 @@
193193
Create a Add node that takes two numbers and outputs a single number
194194
195195
\`\`\`
196-
var node = new FlowNode({
196+
let node = new FlowNode({
197197
title: "Add",
198198
info: "I add two numbers",
199199
inputs: [

src/elements/base.ts

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import { Vector2 } from "../types/vector2";
2+
import { Border, BorderStyling, InstantiateRenderElementSidesStyling, IRenderElement, RenderElementSides, RenderElementSidesStyling } from "./interfaces";
3+
4+
export enum Display {
5+
Flex = "flex",
6+
None = "none"
7+
}
8+
9+
export interface RenderElementBaseStyling {
10+
Margin?: RenderElementSidesStyling | number;
11+
Padding?: RenderElementSidesStyling | number;
12+
Border?: BorderStyling;
13+
BackgroundColor?: string;
14+
MinWidth?: number;
15+
MaxWidth?: number;
16+
MaxHeight?: number;
17+
MinHeight?: number;
18+
Grow?: number;
19+
Display?: Display;
20+
}
21+
22+
export abstract class RenderElementBase implements IRenderElement {
23+
24+
#margin: RenderElementSides;
25+
26+
#padding: RenderElementSides;
27+
28+
#border: Border;
29+
30+
#backgroundColor?: string;
31+
32+
#minWidth?: number;
33+
34+
#maxWidth?: number;
35+
36+
#minHeight?: number;
37+
38+
#maxHeight?: number;
39+
40+
#grow?: number;
41+
42+
#display: Display;
43+
44+
constructor(config?: RenderElementBaseStyling) {
45+
this.#margin = InstantiateRenderElementSidesStyling(config?.Margin);
46+
this.#padding = InstantiateRenderElementSidesStyling(config?.Padding);
47+
this.#backgroundColor = config?.BackgroundColor;
48+
this.#border = {
49+
Color: config?.Border?.Color ?? "black",
50+
Thickness: config?.Border?.Thickness ?? 0,
51+
Radius: config?.Border?.Radius ?? 0,
52+
}
53+
this.#minWidth = config?.MinWidth;
54+
this.#maxWidth = config?.MaxWidth;
55+
this.#minHeight = config?.MinHeight;
56+
this.#maxHeight = config?.MaxHeight;
57+
this.#grow = config?.Grow;
58+
this.#display = config?.Display ?? Display.Flex;
59+
}
60+
61+
setBackgroundColor(newColor: string): void {
62+
this.#backgroundColor = newColor;
63+
}
64+
65+
abstract doRender(ctx: CanvasRenderingContext2D, position: Vector2, graphScale: number, scaledFillableSpace: Vector2): void;
66+
67+
render(ctx: CanvasRenderingContext2D, position: Vector2, graphScale: number, scaledFillableSpace: Vector2): void {
68+
if (this.#display === Display.None) {
69+
return;
70+
}
71+
72+
const scaledSize = { x: 0, y: 0 };
73+
this.size(ctx, scaledSize);
74+
75+
scaledSize.x = scaledSize.x * graphScale;
76+
scaledSize.y = scaledSize.y * graphScale;
77+
scaledSize.x = Math.max(scaledSize.x, scaledFillableSpace.x);
78+
scaledSize.y = Math.max(scaledSize.y, scaledFillableSpace.y);
79+
80+
if (this.#backgroundColor) {
81+
ctx.fillStyle = this.#backgroundColor;
82+
ctx.beginPath();
83+
ctx.roundRect(
84+
position.x + (this.#margin.Left * graphScale),
85+
position.y + (this.#margin.Right * graphScale),
86+
scaledSize.x - ((this.#margin.Left + this.#margin.Right) * graphScale),
87+
scaledSize.y - ((this.#margin.Top + this.#margin.Bottom) * graphScale),
88+
this.#border.Radius * graphScale
89+
);
90+
ctx.fill();
91+
}
92+
93+
if (this.#border.Thickness > 0) {
94+
ctx.lineWidth = this.#border.Thickness * graphScale;
95+
ctx.strokeStyle = this.#border.Color;
96+
ctx.stroke();
97+
}
98+
99+
const offsetPosition = {
100+
x: position.x + (this.#totalLeftOffset() * graphScale),
101+
y: position.y + (this.#totalTopOffset() * graphScale),
102+
};
103+
const elementSize = {
104+
x: scaledSize.x - (this.#horizontalOffset() * graphScale),
105+
y: scaledSize.y - (this.#verticalOffset() * graphScale)
106+
}
107+
this.doRender(ctx, offsetPosition, graphScale, elementSize);
108+
}
109+
110+
abstract calcSize(ctx: CanvasRenderingContext2D, out: Vector2, limitations: Vector2): void;
111+
112+
protected maxLimitations(out: Vector2): void {
113+
out.x = -1;
114+
out.y = -1;
115+
116+
if (this.#maxWidth) {
117+
out.x = this.#maxWidth;
118+
}
119+
120+
if (this.#maxHeight) {
121+
out.y = this.#maxHeight;
122+
}
123+
}
124+
125+
setDisplay(display: Display): void {
126+
this.#display = display;
127+
}
128+
129+
getDisplay(): Display {
130+
return this.#display;
131+
}
132+
133+
size(ctx: CanvasRenderingContext2D, out: Vector2): void {
134+
out.x = 0;
135+
out.y = 0;
136+
if (this.#display === Display.None) {
137+
return;
138+
}
139+
140+
const maxLimits = { x: -1, y: -1 }
141+
this.maxLimitations(maxLimits);
142+
143+
this.calcSize(ctx, out, maxLimits);
144+
145+
out.x += this.#horizontalOffset();
146+
out.y += this.#verticalOffset();
147+
148+
if (this.#minWidth) {
149+
out.x = Math.max(out.x, this.#minWidth)
150+
}
151+
152+
if (this.#maxWidth) {
153+
out.x = Math.min(this.#maxWidth, out.x);
154+
}
155+
156+
if (this.#maxHeight) {
157+
out.y = Math.min(this.#maxHeight, out.y);
158+
}
159+
160+
if (this.#minHeight) {
161+
out.y = Math.max(this.#minHeight, out.y);
162+
}
163+
}
164+
165+
#horizontalOffset(): number {
166+
return (this.#border.Thickness) +
167+
this.#margin.Left +
168+
this.#margin.Right +
169+
this.#padding.Left +
170+
this.#padding.Right;
171+
}
172+
173+
#verticalOffset(): number {
174+
return (this.#border.Thickness) +
175+
this.#margin.Top +
176+
this.#margin.Bottom +
177+
this.#padding.Top +
178+
this.#padding.Bottom;
179+
}
180+
181+
#totalTopOffset(): number {
182+
return (this.#border.Thickness / 2) +
183+
this.#margin.Top +
184+
this.#padding.Top;
185+
}
186+
187+
#totalLeftOffset(): number {
188+
return (this.#border.Thickness / 2) +
189+
this.#margin.Left +
190+
this.#padding.Left;
191+
}
192+
}

src/elements/container.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { ScaleVector, Vector2 } from '../types/vector2';
2+
import { RenderElementBase, RenderElementBaseStyling } from "./base";
3+
import { IRenderElement } from "./interfaces";
4+
5+
6+
interface ContainerRenderElementConfig extends RenderElementBaseStyling {
7+
8+
}
9+
10+
export enum LayoutDirection {
11+
Column = "column",
12+
Row = "row"
13+
}
14+
15+
export enum AlignItems {
16+
Stretch = "stretch",
17+
Start = "start",
18+
Center = "center",
19+
End = "end"
20+
}
21+
22+
export class ContainerRenderElement extends RenderElementBase {
23+
24+
#elements: Array<IRenderElement>;
25+
26+
#layout: LayoutDirection;
27+
28+
#alignment: AlignItems;
29+
30+
constructor(elements: Array<IRenderElement>, config?: ContainerRenderElementConfig) {
31+
super(config);
32+
this.#elements = elements;
33+
this.#layout = LayoutDirection.Column;
34+
this.#alignment = AlignItems.Stretch;
35+
}
36+
37+
calcSize(ctx: CanvasRenderingContext2D, out: Vector2, limitations: Vector2): void {
38+
out.x = 0;
39+
out.y = 0;
40+
41+
const eleSize = { x: 0, y: 0 };
42+
43+
switch (this.#layout) {
44+
case LayoutDirection.Column:
45+
for (let i = 0; i < this.#elements.length; i++) {
46+
this.#elements[i].size(ctx, eleSize);
47+
out.x = Math.max(out.x, eleSize.x);
48+
out.y += eleSize.y;
49+
}
50+
break;
51+
52+
case LayoutDirection.Row:
53+
for (let i = 0; i < this.#elements.length; i++) {
54+
this.#elements[i].size(ctx, eleSize);
55+
out.y = Math.max(out.y, eleSize.y);
56+
out.x += eleSize.x;
57+
}
58+
break;
59+
60+
default:
61+
throw new Error("unknown layout: " + this.#layout);
62+
}
63+
64+
}
65+
66+
doRender(ctx: CanvasRenderingContext2D, position: Vector2, graphScale: number, scaledFillableSpace: Vector2): void {
67+
// const size = { x: 0, y: 0 };
68+
// const limitations = { x: 0, y: 0 };
69+
// this.limitations(limitations)
70+
// this.calcSize(ctx, size, limitations);
71+
switch (this.#layout) {
72+
case LayoutDirection.Column:
73+
this.#renderColumn(ctx, position, graphScale, scaledFillableSpace);
74+
break;
75+
76+
default:
77+
throw new Error("unimplemented layout direction: " + this.#layout);
78+
}
79+
}
80+
81+
#renderColumn(ctx: CanvasRenderingContext2D, position: Vector2, graphScale: number, scaledFillableSpace: Vector2): void {
82+
const currnetPos = { x: position.x, y: position.y };
83+
const scaledEleSize = { x: 0, y: 0 };
84+
for (let i = 0; i < this.#elements.length; i++) {
85+
const ele = this.#elements[i];
86+
87+
ele.size(ctx, scaledEleSize);
88+
ScaleVector(scaledEleSize, graphScale);
89+
90+
switch (this.#alignment) {
91+
92+
case AlignItems.Stretch:
93+
currnetPos.x = position.x;
94+
scaledEleSize.x = scaledFillableSpace.x;
95+
break;
96+
97+
// I'm not sure if this is even needed, I feel like this should
98+
// already be the case.
99+
case AlignItems.Start: //garney flarney <---- silly goose steenky feet
100+
currnetPos.x = position.x;
101+
break
102+
103+
case AlignItems.End:
104+
currnetPos.x = scaledFillableSpace.x - scaledEleSize.x
105+
break;
106+
107+
case AlignItems.Center:
108+
currnetPos.x = scaledFillableSpace.x - (scaledEleSize.x / 2)
109+
break;
110+
111+
default:
112+
throw new Error("unimplmeneted alignment: " + this.#alignment)
113+
}
114+
115+
ele.render(ctx, currnetPos, graphScale, scaledEleSize);
116+
currnetPos.y += scaledEleSize.y;
117+
}
118+
}
119+
}

src/elements/interfaces.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Vector2 } from "../types/vector2"
2+
3+
4+
export interface RenderElementSidesStyling {
5+
Left?: number
6+
Right?: number
7+
Top?: number
8+
Bottom?: number
9+
}
10+
11+
export interface RenderElementSides {
12+
Left: number
13+
Right: number
14+
Top: number
15+
Bottom: number
16+
}
17+
18+
export function InstantiateRenderElementSidesStyling(config?: RenderElementSidesStyling | number): RenderElementSides {
19+
if (typeof config === 'number') {
20+
return {
21+
Bottom: config,
22+
Right: config,
23+
Top: config,
24+
Left: config,
25+
}
26+
}
27+
return {
28+
Bottom: config?.Bottom === undefined ? 0 : config?.Bottom,
29+
Right: config?.Right === undefined ? 0 : config?.Right,
30+
Left: config?.Left === undefined ? 0 : config?.Left,
31+
Top: config?.Top === undefined ? 0 : config?.Top,
32+
}
33+
}
34+
35+
export interface IRenderElement {
36+
size(ctx: CanvasRenderingContext2D, out: Vector2): void;
37+
render(ctx: CanvasRenderingContext2D, position: Vector2, graphScale: number, scaledFillableSpace: Vector2): void;
38+
}
39+
40+
export interface Border {
41+
Color: string;
42+
Thickness: number;
43+
Radius: number;
44+
}
45+
46+
export interface BorderStyling {
47+
Color?: string;
48+
Thickness?: number;
49+
Radius?: number;
50+
}

0 commit comments

Comments
 (0)