Skip to content

Commit

Permalink
New Quick Menu
Browse files Browse the repository at this point in the history
  • Loading branch information
EliCDavis committed Feb 19, 2025
1 parent a55a6c1 commit 5ac96d2
Show file tree
Hide file tree
Showing 12 changed files with 853 additions and 18 deletions.
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,10 @@
\`\`\`
// Create a canvas to render our graph to
var canvas = document.createElement("canvas");
let canvas = document.createElement("canvas");
// Create our Node Flow Graph
var graph = new NodeFlowGraph(canvas)
let graph = new NodeFlowGraph(canvas)
\`\`\`
`
},
Expand All @@ -193,7 +193,7 @@
Create a Add node that takes two numbers and outputs a single number
\`\`\`
var node = new FlowNode({
let node = new FlowNode({
title: "Add",
info: "I add two numbers",
inputs: [
Expand Down
192 changes: 192 additions & 0 deletions src/elements/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { Vector2 } from "../types/vector2";
import { Border, BorderStyling, InstantiateRenderElementSidesStyling, IRenderElement, RenderElementSides, RenderElementSidesStyling } from "./interfaces";

export enum Display {
Flex = "flex",
None = "none"
}

export interface RenderElementBaseStyling {
Margin?: RenderElementSidesStyling | number;
Padding?: RenderElementSidesStyling | number;
Border?: BorderStyling;
BackgroundColor?: string;
MinWidth?: number;
MaxWidth?: number;
MaxHeight?: number;
MinHeight?: number;
Grow?: number;
Display?: Display;
}

export abstract class RenderElementBase implements IRenderElement {

#margin: RenderElementSides;

#padding: RenderElementSides;

#border: Border;

#backgroundColor?: string;

#minWidth?: number;

#maxWidth?: number;

#minHeight?: number;

#maxHeight?: number;

#grow?: number;

#display: Display;

constructor(config?: RenderElementBaseStyling) {
this.#margin = InstantiateRenderElementSidesStyling(config?.Margin);
this.#padding = InstantiateRenderElementSidesStyling(config?.Padding);
this.#backgroundColor = config?.BackgroundColor;
this.#border = {
Color: config?.Border?.Color ?? "black",
Thickness: config?.Border?.Thickness ?? 0,
Radius: config?.Border?.Radius ?? 0,
}
this.#minWidth = config?.MinWidth;
this.#maxWidth = config?.MaxWidth;
this.#minHeight = config?.MinHeight;
this.#maxHeight = config?.MaxHeight;
this.#grow = config?.Grow;
this.#display = config?.Display ?? Display.Flex;
}

setBackgroundColor(newColor: string): void {
this.#backgroundColor = newColor;
}

abstract doRender(ctx: CanvasRenderingContext2D, position: Vector2, graphScale: number, scaledFillableSpace: Vector2): void;

render(ctx: CanvasRenderingContext2D, position: Vector2, graphScale: number, scaledFillableSpace: Vector2): void {
if (this.#display === Display.None) {
return;
}

const scaledSize = { x: 0, y: 0 };
this.size(ctx, scaledSize);

scaledSize.x = scaledSize.x * graphScale;
scaledSize.y = scaledSize.y * graphScale;
scaledSize.x = Math.max(scaledSize.x, scaledFillableSpace.x);
scaledSize.y = Math.max(scaledSize.y, scaledFillableSpace.y);

if (this.#backgroundColor) {
ctx.fillStyle = this.#backgroundColor;
ctx.beginPath();
ctx.roundRect(
position.x + (this.#margin.Left * graphScale),
position.y + (this.#margin.Right * graphScale),
scaledSize.x - ((this.#margin.Left + this.#margin.Right) * graphScale),
scaledSize.y - ((this.#margin.Top + this.#margin.Bottom) * graphScale),
this.#border.Radius * graphScale
);
ctx.fill();
}

if (this.#border.Thickness > 0) {
ctx.lineWidth = this.#border.Thickness * graphScale;
ctx.strokeStyle = this.#border.Color;
ctx.stroke();
}

const offsetPosition = {
x: position.x + (this.#totalLeftOffset() * graphScale),
y: position.y + (this.#totalTopOffset() * graphScale),
};
const elementSize = {
x: scaledSize.x - (this.#horizontalOffset() * graphScale),
y: scaledSize.y - (this.#verticalOffset() * graphScale)
}
this.doRender(ctx, offsetPosition, graphScale, elementSize);
}

abstract calcSize(ctx: CanvasRenderingContext2D, out: Vector2, limitations: Vector2): void;

protected maxLimitations(out: Vector2): void {
out.x = -1;
out.y = -1;

if (this.#maxWidth) {
out.x = this.#maxWidth;
}

if (this.#maxHeight) {
out.y = this.#maxHeight;
}
}

setDisplay(display: Display): void {
this.#display = display;
}

getDisplay(): Display {
return this.#display;
}

size(ctx: CanvasRenderingContext2D, out: Vector2): void {
out.x = 0;
out.y = 0;
if (this.#display === Display.None) {
return;
}

const maxLimits = { x: -1, y: -1 }
this.maxLimitations(maxLimits);

this.calcSize(ctx, out, maxLimits);

out.x += this.#horizontalOffset();
out.y += this.#verticalOffset();

if (this.#minWidth) {
out.x = Math.max(out.x, this.#minWidth)
}

if (this.#maxWidth) {
out.x = Math.min(this.#maxWidth, out.x);
}

if (this.#maxHeight) {
out.y = Math.min(this.#maxHeight, out.y);
}

if (this.#minHeight) {
out.y = Math.max(this.#minHeight, out.y);
}
}

#horizontalOffset(): number {
return (this.#border.Thickness) +
this.#margin.Left +
this.#margin.Right +
this.#padding.Left +
this.#padding.Right;
}

#verticalOffset(): number {
return (this.#border.Thickness) +
this.#margin.Top +
this.#margin.Bottom +
this.#padding.Top +
this.#padding.Bottom;
}

#totalTopOffset(): number {
return (this.#border.Thickness / 2) +
this.#margin.Top +
this.#padding.Top;
}

#totalLeftOffset(): number {
return (this.#border.Thickness / 2) +
this.#margin.Left +
this.#padding.Left;
}
}
119 changes: 119 additions & 0 deletions src/elements/container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { ScaleVector, Vector2 } from '../types/vector2';
import { RenderElementBase, RenderElementBaseStyling } from "./base";
import { IRenderElement } from "./interfaces";


interface ContainerRenderElementConfig extends RenderElementBaseStyling {

}

export enum LayoutDirection {
Column = "column",
Row = "row"
}

export enum AlignItems {
Stretch = "stretch",
Start = "start",
Center = "center",
End = "end"
}

export class ContainerRenderElement extends RenderElementBase {

#elements: Array<IRenderElement>;

#layout: LayoutDirection;

#alignment: AlignItems;

constructor(elements: Array<IRenderElement>, config?: ContainerRenderElementConfig) {
super(config);
this.#elements = elements;
this.#layout = LayoutDirection.Column;
this.#alignment = AlignItems.Stretch;
}

calcSize(ctx: CanvasRenderingContext2D, out: Vector2, limitations: Vector2): void {
out.x = 0;
out.y = 0;

const eleSize = { x: 0, y: 0 };

switch (this.#layout) {
case LayoutDirection.Column:
for (let i = 0; i < this.#elements.length; i++) {
this.#elements[i].size(ctx, eleSize);
out.x = Math.max(out.x, eleSize.x);
out.y += eleSize.y;
}
break;

case LayoutDirection.Row:
for (let i = 0; i < this.#elements.length; i++) {
this.#elements[i].size(ctx, eleSize);
out.y = Math.max(out.y, eleSize.y);
out.x += eleSize.x;
}
break;

default:
throw new Error("unknown layout: " + this.#layout);
}

}

doRender(ctx: CanvasRenderingContext2D, position: Vector2, graphScale: number, scaledFillableSpace: Vector2): void {
// const size = { x: 0, y: 0 };
// const limitations = { x: 0, y: 0 };
// this.limitations(limitations)
// this.calcSize(ctx, size, limitations);
switch (this.#layout) {
case LayoutDirection.Column:
this.#renderColumn(ctx, position, graphScale, scaledFillableSpace);
break;

default:
throw new Error("unimplemented layout direction: " + this.#layout);
}
}

#renderColumn(ctx: CanvasRenderingContext2D, position: Vector2, graphScale: number, scaledFillableSpace: Vector2): void {
const currnetPos = { x: position.x, y: position.y };
const scaledEleSize = { x: 0, y: 0 };
for (let i = 0; i < this.#elements.length; i++) {
const ele = this.#elements[i];

ele.size(ctx, scaledEleSize);
ScaleVector(scaledEleSize, graphScale);

switch (this.#alignment) {

case AlignItems.Stretch:
currnetPos.x = position.x;
scaledEleSize.x = scaledFillableSpace.x;
break;

// I'm not sure if this is even needed, I feel like this should
// already be the case.
case AlignItems.Start: //garney flarney <---- silly goose steenky feet
currnetPos.x = position.x;
break

case AlignItems.End:
currnetPos.x = scaledFillableSpace.x - scaledEleSize.x
break;

case AlignItems.Center:
currnetPos.x = scaledFillableSpace.x - (scaledEleSize.x / 2)
break;

default:
throw new Error("unimplmeneted alignment: " + this.#alignment)
}

ele.render(ctx, currnetPos, graphScale, scaledEleSize);
currnetPos.y += scaledEleSize.y;
}
}
}
50 changes: 50 additions & 0 deletions src/elements/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Vector2 } from "../types/vector2"


export interface RenderElementSidesStyling {
Left?: number
Right?: number
Top?: number
Bottom?: number
}

export interface RenderElementSides {
Left: number
Right: number
Top: number
Bottom: number
}

export function InstantiateRenderElementSidesStyling(config?: RenderElementSidesStyling | number): RenderElementSides {
if (typeof config === 'number') {
return {
Bottom: config,
Right: config,
Top: config,
Left: config,
}
}
return {
Bottom: config?.Bottom === undefined ? 0 : config?.Bottom,
Right: config?.Right === undefined ? 0 : config?.Right,
Left: config?.Left === undefined ? 0 : config?.Left,
Top: config?.Top === undefined ? 0 : config?.Top,
}
}

export interface IRenderElement {
size(ctx: CanvasRenderingContext2D, out: Vector2): void;
render(ctx: CanvasRenderingContext2D, position: Vector2, graphScale: number, scaledFillableSpace: Vector2): void;
}

export interface Border {
Color: string;
Thickness: number;
Radius: number;
}

export interface BorderStyling {
Color?: string;
Thickness?: number;
Radius?: number;
}
Loading

0 comments on commit 5ac96d2

Please sign in to comment.