// Copyright (c) 2026 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT /** * Base class for every UI element (Label, Rectangle, and any composite * class you define). `new UIElement()` allocates a pool element and, if * the (sub)class defines init(), calls it immediately. * * add()/remove() maintain a persistent parent/child link (up to 8 * children per element) -- worldX/worldY/parent are always current, not * just "as of last render()". Added children render automatically, in * add-order, right after this element's own visuals -- unless render() * is overridden, in which case the override takes full manual control * and must call renderChildren() itself if it still wants them drawn. * * ```js * // No render() needed -- children added via add() draw automatically. * class HealthBar extends UIElement { * init() { * this.label = new Label(); * this.label.x = 32; * this.label.y = 32; * this.add(this.label); * this.x = 64; * this.y = 64; * // this.label.worldX is now 96, immediately -- no render() call needed * } * } * * // Overriding render() takes full manual control: added children are * // NOT drawn automatically here -- renderChildren() opts back in. * class PlayerHealth extends UIElement { * init() { * this.icon = new Rectangle(); * this.add(this.icon); * } * render() { * if(this.player) this.renderChildren(); * } * } * ``` */ declare class UIElement { constructor(); /** The engine-assigned numeric element ID. */ readonly id: number; /** Local x position, relative to this element's parent (if any). */ x: number; /** Local y position, relative to this element's parent (if any). */ y: number; /** Screen-space x. Always current -- recomputed on every read. */ readonly worldX: number; /** Screen-space y. Always current -- recomputed on every read. */ readonly worldY: number; /** The UIElement this one was added to via add(), or undefined. */ readonly parent: UIElement | undefined; /** Number of elements currently added to this one. */ readonly childCount: number; /** * Adds child as a child of this element, detaching it from any * previous parent (or from UI's render roots) first. Returns child, * so calls can be chained. * * @throws if child is this element itself, if it's already an * ancestor of this element (which would create a cycle), or if this * element already has 8 children. */ add(child: UIElement): UIElement; /** Removes child from this element's children, if it's currently one. * No-op otherwise. */ remove(child: UIElement): void; /** * Called once, right after construction, if defined -- the usual * place to build children and set initial properties. */ init?(): void; /** * Draws this element. The base implementation draws whatever native * visuals this element type has (Label/Rectangle), then calls * renderChildren(). Override it to draw your own content instead -- * an override does NOT get its children auto-rendered; call * renderChildren() yourself if you still want them drawn. */ render(): void; /** Renders every added child, in add-order. Called automatically by * the base render() -- only needed explicitly from inside an * overridden render(). */ renderChildren(): void; /** * Called once per tick. The base implementation calls updateChildren() * and nothing else. Override it for per-tick logic (e.g. polling * input) -- an override does NOT get its children auto-updated; call * updateChildren() yourself if you still want them updated. Not called * automatically by the engine unless this element is a render root * (added via UI.add()) or a descendant of one. */ update(): void; /** Updates every added child, in add-order. Called automatically by * the base update() -- only needed explicitly from inside an * overridden update(). */ updateChildren(): void; /** Releases this element's pool slot and cascades to every descendant * (each disposed the same way, recursively). Also removes it from * UI's render roots if it was added there. Safe to call more than * once, or on an element a cascade already disposed. */ dispose(): void; }