-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
853 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.