Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions examples/examples.json
Original file line number Diff line number Diff line change
Expand Up @@ -1763,5 +1763,21 @@
"tween"
]
}
],
"ui": [
{
"slug": "hud-and-widgets",
"path": "ui/hud-and-widgets.js",
"language": "typescript",
"title": "HUD and Widgets",
"description": "Build a screen-fixed HUD on scene.ui: anchored score and health widgets, plus a panel of clickable, keyboard-focusable buttons that update them.",
"backend": "core",
"tags": [
"ui",
"hud",
"button",
"widget"
]
}
]
}
74 changes: 74 additions & 0 deletions examples/ui/hud-and-widgets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Auto-generated from hud-and-widgets.ts — edit the .ts source, not this file.
import { Application, Button, Color, Label, Panel, ProgressBar, Scene, Stack } from '@codexo/exojs';
const app = new Application({
canvas: {
width: 1280,
height: 720,
mount: document.body,
sizingMode: 'fit',
},
clearColor: new Color(18, 22, 32),
});
/**
* UI-Core showcase: a screen-fixed HUD and interactive widgets live on
* `scene.ui`, which is auto-rendered above the world. Widgets anchor to the
* screen edges and re-layout on resize; buttons are clickable and keyboard-
* focusable (Tab to move focus, Enter / Space to activate).
*/
class HudScene extends Scene {
score = 0;
health = 1;
scoreLabel;
healthBar;
spinner;
angle = 0;
init() {
// A bit of "world" content so the UI clearly sits on top of it.
this.spinner = new Panel({ width: 160, height: 160, color: new Color(60, 130, 235, 1), cornerRadius: 24 });
this.spinner.setAnchor(0.5, 0.5);
this.spinner.setPosition(640, 360);
this.addChild(this.spinner);
// HUD: score + health anchored to the top-left corner.
this.scoreLabel = new Label('Score: 0', { fontSize: 26 });
this.scoreLabel.anchorIn(this.ui, 'top-left', 24, 20);
this.ui.addChild(this.scoreLabel);
this.healthBar = new ProgressBar({ width: 260, height: 16, value: 1 });
this.healthBar.anchorIn(this.ui, 'top-left', 24, 60);
this.ui.addChild(this.healthBar);
// A panel of stacked buttons anchored to the bottom-right corner.
const panel = new Panel({ borderColor: new Color(255, 255, 255, 0.16), borderWidth: 1 });
const buttons = new Stack({ direction: 'column', spacing: 10, padding: 14 });
buttons.addItem(this.makeButton('+10 Score', new Color(54, 120, 220, 1), () => {
this.score += 10;
this.scoreLabel.text = `Score: ${this.score}`;
}));
buttons.addItem(this.makeButton('Take Damage', new Color(214, 92, 84, 1), () => {
this.health = Math.max(0, this.health - 0.2);
this.healthBar.value = this.health;
}));
buttons.addItem(this.makeButton('Reset', new Color(90, 96, 110, 1), () => {
this.score = 0;
this.health = 1;
this.scoreLabel.text = 'Score: 0';
this.healthBar.value = 1;
}));
panel.setSize(buttons.uiWidth, buttons.uiHeight);
panel.addChild(buttons);
panel.anchorIn(this.ui, 'bottom-right', -24, -24);
this.ui.addChild(panel);
}
update(delta) {
this.angle += delta.seconds * 60;
this.spinner.setRotation(this.angle);
}
draw(context) {
// scene.root is explicit; scene.ui is auto-rendered above it.
context.render(this.root);
}
makeButton(label, color, onClick) {
const button = new Button({ label, color, width: 160, height: 44 });
button.onClick.add(onClick);
return button;
}
}
void app.start(new HudScene());
87 changes: 87 additions & 0 deletions examples/ui/hud-and-widgets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Application, Button, Color, Label, Panel, ProgressBar, Scene, Stack } from '@codexo/exojs';

const app = new Application({
canvas: {
width: 1280,
height: 720,
mount: document.body,
sizingMode: 'fit',
},
clearColor: new Color(18, 22, 32),
});

/**
* UI-Core showcase: a screen-fixed HUD and interactive widgets live on
* `scene.ui`, which is auto-rendered above the world. Widgets anchor to the
* screen edges and re-layout on resize; buttons are clickable and keyboard-
* focusable (Tab to move focus, Enter / Space to activate).
*/
class HudScene extends Scene {
private score = 0;
private health = 1;
private scoreLabel!: Label;
private healthBar!: ProgressBar;
private spinner!: Panel;
private angle = 0;

override init(): void {
// A bit of "world" content so the UI clearly sits on top of it.
this.spinner = new Panel({ width: 160, height: 160, color: new Color(60, 130, 235, 1), cornerRadius: 24 });
this.spinner.setAnchor(0.5, 0.5);
this.spinner.setPosition(640, 360);
this.addChild(this.spinner);

// HUD: score + health anchored to the top-left corner.
this.scoreLabel = new Label('Score: 0', { fontSize: 26 });
this.scoreLabel.anchorIn(this.ui, 'top-left', 24, 20);
this.ui.addChild(this.scoreLabel);

this.healthBar = new ProgressBar({ width: 260, height: 16, value: 1 });
this.healthBar.anchorIn(this.ui, 'top-left', 24, 60);
this.ui.addChild(this.healthBar);

// A panel of stacked buttons anchored to the bottom-right corner.
const panel = new Panel({ borderColor: new Color(255, 255, 255, 0.16), borderWidth: 1 });
const buttons = new Stack({ direction: 'column', spacing: 10, padding: 14 });

buttons.addItem(this.makeButton('+10 Score', new Color(54, 120, 220, 1), () => {
this.score += 10;
this.scoreLabel.text = `Score: ${this.score}`;
}));
buttons.addItem(this.makeButton('Take Damage', new Color(214, 92, 84, 1), () => {
this.health = Math.max(0, this.health - 0.2);
this.healthBar.value = this.health;
}));
buttons.addItem(this.makeButton('Reset', new Color(90, 96, 110, 1), () => {
this.score = 0;
this.health = 1;
this.scoreLabel.text = 'Score: 0';
this.healthBar.value = 1;
}));

panel.setSize(buttons.uiWidth, buttons.uiHeight);
panel.addChild(buttons);
panel.anchorIn(this.ui, 'bottom-right', -24, -24);
this.ui.addChild(panel);
}

override update(delta): void {
this.angle += delta.seconds * 60;
this.spinner.setRotation(this.angle);
}

override draw(context): void {
// scene.root is explicit; scene.ui is auto-rendered above it.
context.render(this.root);
}

private makeButton(label: string, color: Color, onClick: () => void): Button {
const button = new Button({ label, color, width: 160, height: 44 });

button.onClick.add(onClick);

return button;
}
}

void app.start(new HudScene());
10 changes: 9 additions & 1 deletion site/src/content/api/abstract-text.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ symbol: "AbstractText"
kind: "class"
subsystem: "rendering"
importPath: "@codexo/exojs"
memberCount: 75
memberCount: 83
tier: "stable"
sections: ["Import", "Constructors", "Methods", "Properties", "Events", "Source"]
sourcePath: "src/rendering/text/AbstractText.ts"
Expand All @@ -32,10 +32,12 @@ Subclasses:
- `_invalidateChildrenTransform(): void`
- `_invalidateSubtreeTransform(): void`
- `addFilter(filter: Filter): this`
- `blur(): this`
- `clearFilters(): this`
- `collidesWith(target: Collidable): CollisionResponse | null`
- `contains(x: number, y: number): boolean`
- `destroy(): void`
- `focus(): this`
- `getBounds(): Rectangle`
- `getGlobalTransform(): Matrix`
- `getLocalBounds(): Rectangle`
Expand Down Expand Up @@ -72,7 +74,9 @@ Subclasses:
- `cursor: string | null`
- `draggable: boolean`
- `flags: Flags<SceneNodeTransformFlags>`
- `focusable: boolean`
- `preserveDrawOrder: boolean`
- `tabIndex: number`
- `anchor: ObservableVector`
- `blendMode: BlendModes`
- `cacheAsBitmap: boolean`
Expand All @@ -99,9 +103,13 @@ Subclasses:

## Events

- `onBlur: Signal<[RenderNode]>`
- `onDrag: Signal<[InteractionEvent]>`
- `onDragEnd: Signal<[InteractionEvent]>`
- `onDragStart: Signal<[InteractionEvent]>`
- `onFocus: Signal<[RenderNode]>`
- `onKeyDown: Signal<[KeyEvent]>`
- `onKeyUp: Signal<[KeyEvent]>`
- `onPointerDown: Signal<[InteractionEvent]>`
- `onPointerMove: Signal<[InteractionEvent]>`
- `onPointerOut: Signal<[InteractionEvent]>`
Expand Down
10 changes: 9 additions & 1 deletion site/src/content/api/animated-sprite.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ symbol: "AnimatedSprite"
kind: "class"
subsystem: "rendering"
importPath: "@codexo/exojs"
memberCount: 95
memberCount: 103
tier: "stable"
sections: ["Import", "Constructors", "Methods", "Properties", "Events", "Source"]
sourcePath: "src/rendering/sprite/AnimatedSprite.ts"
Expand Down Expand Up @@ -35,11 +35,13 @@ from a Spritesheet's named animations.

- `_invalidateChildrenTransform(): void`
- `addFilter(filter: Filter): this`
- `blur(): this`
- `clearFilters(): this`
- `collidesWith(target: Collidable): CollisionResponse | null`
- `contains(x: number, y: number): boolean`
- `defineClip(name: string, clip: AnimatedSpriteClipDefinition): this`
- `destroy(): void`
- `focus(): this`
- `getBounds(): Rectangle`
- `getGlobalTransform(): Matrix`
- `getLocalBounds(): Rectangle`
Expand Down Expand Up @@ -86,7 +88,9 @@ from a Spritesheet's named animations.
- `cursor: string | null`
- `draggable: boolean`
- `flags: Flags<SceneNodeTransformFlags>`
- `focusable: boolean`
- `preserveDrawOrder: boolean`
- `tabIndex: number`
- `anchor: ObservableVector`
- `blendMode: BlendModes`
- `cacheAsBitmap: boolean`
Expand Down Expand Up @@ -124,9 +128,13 @@ from a Spritesheet's named animations.

- `onComplete: Signal<[clip]>`
- `onFrame: Signal<[clip, frame]>`
- `onBlur: Signal<[RenderNode]>`
- `onDrag: Signal<[InteractionEvent]>`
- `onDragEnd: Signal<[InteractionEvent]>`
- `onDragStart: Signal<[InteractionEvent]>`
- `onFocus: Signal<[RenderNode]>`
- `onKeyDown: Signal<[KeyEvent]>`
- `onKeyUp: Signal<[KeyEvent]>`
- `onPointerDown: Signal<[InteractionEvent]>`
- `onPointerMove: Signal<[InteractionEvent]>`
- `onPointerOut: Signal<[InteractionEvent]>`
Expand Down
4 changes: 2 additions & 2 deletions site/src/content/api/application-status.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ memberCount: 4
tier: "stable"
sections: ["Import", "Members", "Source"]
sourcePath: "src/core/Application.ts"
sourceUrl: "https://github.com/Exoridus/ExoJS/blob/main/src/core/Application.ts#L35"
sourceUrl: "https://github.com/Exoridus/ExoJS/blob/main/src/core/Application.ts#L36"
---
## Import

Expand All @@ -24,4 +24,4 @@ sourceUrl: "https://github.com/Exoridus/ExoJS/blob/main/src/core/Application.ts#

## Source

[src/core/Application.ts](https://github.com/Exoridus/ExoJS/blob/main/src/core/Application.ts#L35)
[src/core/Application.ts](https://github.com/Exoridus/ExoJS/blob/main/src/core/Application.ts#L36)
7 changes: 4 additions & 3 deletions site/src/content/api/application.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ symbol: "Application"
kind: "class"
subsystem: "core"
importPath: "@codexo/exojs"
memberCount: 40
memberCount: 41
tier: "stable"
sections: ["Import", "Constructors", "Methods", "Properties", "Events", "Source"]
sourcePath: "src/core/Application.ts"
sourceUrl: "https://github.com/Exoridus/ExoJS/blob/main/src/core/Application.ts#L215"
sourceUrl: "https://github.com/Exoridus/ExoJS/blob/main/src/core/Application.ts#L216"
---
## Import

Expand Down Expand Up @@ -55,6 +55,7 @@ background-active simulations.
## Properties

- `canvas: HTMLCanvasElement`
- `focus: FocusManager`
- `input: InputManager`
- `interaction: InteractionManager`
- `loader: Loader`
Expand Down Expand Up @@ -91,4 +92,4 @@ background-active simulations.

## Source

[src/core/Application.ts](https://github.com/Exoridus/ExoJS/blob/main/src/core/Application.ts#L215)
[src/core/Application.ts](https://github.com/Exoridus/ExoJS/blob/main/src/core/Application.ts#L216)
10 changes: 9 additions & 1 deletion site/src/content/api/bitmap-text.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ symbol: "BitmapText"
kind: "class"
subsystem: "rendering"
importPath: "@codexo/exojs"
memberCount: 82
memberCount: 90
tier: "stable"
sections: ["Import", "Constructors", "Methods", "Properties", "Events", "Source"]
sourcePath: "src/rendering/text/BitmapText.ts"
Expand Down Expand Up @@ -45,10 +45,12 @@ label.style.align = 'center'; // immediate rebuild
- `_invalidateChildrenTransform(): void`
- `_invalidateSubtreeTransform(): void`
- `addFilter(filter: Filter): this`
- `blur(): this`
- `clearFilters(): this`
- `collidesWith(target: Collidable): CollisionResponse | null`
- `contains(x: number, y: number): boolean`
- `destroy(): void`
- `focus(): this`
- `getBounds(): Rectangle`
- `getGlobalTransform(): Matrix`
- `getLocalBounds(): Rectangle`
Expand Down Expand Up @@ -86,7 +88,9 @@ label.style.align = 'center'; // immediate rebuild
- `cursor: string | null`
- `draggable: boolean`
- `flags: Flags<SceneNodeTransformFlags>`
- `focusable: boolean`
- `preserveDrawOrder: boolean`
- `tabIndex: number`
- `anchor: ObservableVector`
- `blendMode: BlendModes`
- `cacheAsBitmap: boolean`
Expand Down Expand Up @@ -119,9 +123,13 @@ label.style.align = 'center'; // immediate rebuild

## Events

- `onBlur: Signal<[RenderNode]>`
- `onDrag: Signal<[InteractionEvent]>`
- `onDragEnd: Signal<[InteractionEvent]>`
- `onDragStart: Signal<[InteractionEvent]>`
- `onFocus: Signal<[RenderNode]>`
- `onKeyDown: Signal<[KeyEvent]>`
- `onKeyUp: Signal<[KeyEvent]>`
- `onPointerDown: Signal<[InteractionEvent]>`
- `onPointerMove: Signal<[InteractionEvent]>`
- `onPointerOut: Signal<[InteractionEvent]>`
Expand Down
Loading
Loading