Menu

Element Hierarchy

Relevant source files

This page documents the four-level inheritance chain that forms the foundation of all UI components in Basalt: BaseElementVisualElementContainerBaseFrame. Each level adds specific capabilities, from basic lifecycle management to terminal rendering.

For detailed API documentation of each class, see their respective subsections: BaseElement, VisualElement, Container, and BaseFrame. For information about concrete UI components built on this hierarchy, see UI Components.

Overview

The element hierarchy implements a classic inheritance pattern where each class extends the previous one, adding new responsibilities while maintaining compatibility with parent functionality. This design enables code reuse across 40+ UI elements while providing clear separation of concerns.

Diagram: Element Hierarchy Class Inheritance

Sources: config.lua105-444 src/elements/VisualElement.lua1-11 src/elements/BaseElement.lua1-20

Class Responsibilities

Each level in the hierarchy has distinct, non-overlapping responsibilities:

ClassPrimary ResponsibilitiesKey PropertiesKey Methods
BaseElementProperty system integration, event registration, lifecycle management, ID generationid, name, type, _properties, _values, _observersinit(), destroy(), fireEvent(), observe(), defineProperty()
VisualElementPositioning, sizing, colors, mouse/keyboard event handling, coordinate transformation, constraintsx, y, width, height, foreground, background, visible, z, constraintsrender(), calculatePosition(), isInBounds(), mouse_click(), setConstraint()
ContainerChild element management, event propagation, visibility clipping, focus management, z-index sortingchildren, focusedChild, offsetX, offsetY, childrenSorted, childrenEventsaddChild(), removeChild(), sortChildren(), callChildrenEvent(), setFocusedChild()
BaseFrameTerminal binding, render buffer management, root-level event routing, monitor support, peripheral handlingterm, _render, _peripheralName, _renderUpdatesetCursor(), monitor_touch(), term_resize(), dispatchEvent()

Sources: src/elements/VisualElement.lua14-73 src/elements/Container.lua20-72 src/elements/BaseFrame.lua1-50

Inheritance and Property Flow

The PropertySystem (see Property System) enables property inheritance and extension at each hierarchy level. Properties defined at higher levels are automatically available to descendants.

Diagram: Property Inheritance Flow Through Hierarchy

Property Definition Pattern:

Each class uses defineProperty() to declare its properties. For example, from src/elements/VisualElement.lua14-16:

Properties can have custom setters and getters. For example, the z property triggers child re-sorting in src/elements/VisualElement.lua18-23:

Sources: src/elements/VisualElement.lua14-73 src/elements/Container.lua20-72 src/propertySystem.lua1-300

Method Inheritance and Overriding

Each level overrides or extends parent methods to add functionality. The common pattern is to call the parent implementation, then add level-specific behavior:

Diagram: Method Resolution Chain Example (mouse_click)

Key Override Patterns:

  1. Rendering: Each level calls parent render(), then adds its own drawing operations

  2. Event Handling: Parent checks bounds/conditions, child propagates to descendants

  3. Initialization: Child calls parent init(), then performs own setup

Sources: src/elements/Container.lua410-422 src/elements/VisualElement.lua745-831 src/elements/Container.lua363-402

Coordinate Space Management

The hierarchy implements a nested coordinate system where each container defines its own coordinate space:

Diagram: Nested Coordinate Spaces

Transformation Methods:

MethodPurposeImplementation DetailsLocation
calculatePosition()Converts element position to parent-relative coordinates, accounting for offsetX/offsetYReturns x - offsetX, y - offsetY if not ignoreOffsetsrc/elements/VisualElement.lua963-974
getRelativePosition(x, y)Converts absolute coordinates to element-relative coordinatesRecursively calculates offset from parent coordinatessrc/elements/VisualElement.lua1008-1021
getAbsolutePosition(x, y)Calculates screen-absolute coordinates by walking up parent chainWalks parent chain, accumulating x and y offsetssrc/elements/VisualElement.lua982-1000

Coordinate Transformation Example:

A Button at x=10, y=8 inside a Container at x=5, y=3 with offsetX=2, offsetY=1:

  • calculatePosition() returns (10-2, 8-1) = (8, 7) relative to Container
  • getAbsolutePosition() returns (10+5-1, 8+3-1) = (14, 10) relative to screen
  • Mouse click at screen (14, 10) is getRelativePosition() = (10-10+1, 8-8+1) = (1, 1) relative to Button

Sources: src/elements/VisualElement.lua963-1021 src/elements/Container.lua339-350

Event Propagation Architecture

Events flow through the hierarchy in two directions: top-down (dispatch) and bottom-up (bubbling).

Diagram: Event Propagation Sequence

Event Registration and Propagation:

  1. Event Definition: BaseElement.defineEvent(class, eventName) creates event infrastructure

  2. Event Handlers: VisualElement implements specific handlers that call fireEvent()

  3. Event Propagation: Container:callChildrenEvent() dispatches to children

  4. Event Callbacks: Application code registers callbacks via onEventName(callback)

Sources: src/elements/Container.lua363-402 src/elements/VisualElement.lua745-831 src/elements/BaseElement.lua200-250

Concrete Element Inheritance Patterns

All 40+ UI elements inherit from either VisualElement or Container:

Diagram: Concrete Element Inheritance Tree

Inheritance Decision Criteria:

Inherit from VisualElement when the element:

  • Does not manage child elements
  • Renders only its own content
  • Handles user input directly (keyboard/mouse)
  • Examples from config.lua105-444:
    • Button (elements/Button.lua) - Simple clickable text
    • Input (elements/Input.lua) - Single-line text input
    • Slider (elements/Slider.lua) - Value selection widget
    • Image (elements/Image.lua) - BIMG format renderer
    • Tree (elements/Tree.lua) - Hierarchical data display

Inherit from Container when the element:

  • Manages a collection of child elements
  • Implements layout algorithms (positioning children)
  • Propagates events to children
  • Provides scrolling or clipping behavior
  • Examples from config.lua105-444:
    • Frame (elements/Frame.lua) - Basic container with optional dragging/scrolling
    • FlexBox (elements/FlexBox.lua) - CSS flexbox-style layout
    • TabControl (elements/TabControl.lua) - Tabbed interface with content areas
    • ScrollFrame (elements/ScrollFrame.lua) - Scrollable viewport with scrollbars

Multi-Level Inheritance for specialized variants:

  • CollectionListMenu/DropDownComboBox
    • Each level adds: item management → scrolling/selection → menu-specific behavior → editable dropdown
  • FrameDialog
    • Adds modal behavior and preset configurations

Sources: config.lua105-444 src/elements/VisualElement.lua1-11 src/elements/Container.lua1-18 src/elements/Collection.lua1-20

Rendering Pipeline Through Hierarchy

Each level contributes to the final rendering pipeline:

Diagram: Rendering Pipeline Through Hierarchy

Drawing Method Hierarchy:

Each level provides coordinate transformation and clipping:

LevelMethodsPurposeImplementation
BaseFrameblit(x, y, text, fg, bg), textFg(), textBg(), multiBlit()Write directly to render buffer (_render) with absolute terminal coordinatessrc/elements/BaseFrame.lua60-90
ContainerOverrides blit(), textFg(), textBg(), multiBlit()Clips content to container bounds (isInChildBounds()), applies scroll offset (offsetX, offsetY)src/elements/Container.lua227-289
VisualElementOverrides blit(), textFg(), textBg(), multiBlit()Transforms coordinates from element-relative to parent-relative via calculatePosition()src/elements/VisualElement.lua652-718
Concrete ElementsCalls parent methods (e.g., self:blit())Uses element-relative coordinates; parent handles transformationButton: src/elements/Button.lua30-40

Rendering Call Stack Example (Button in ScrollFrame in BaseFrame):

1. Button:render() at (2, 3) 2. Button:blit(1, 1, "OK", "0", "f") 3. VisualElement:blit() → calculatePosition() → (2+1-1, 3+1-1) = (2, 3) 4. parent:blit(2, 3, ...) → ScrollFrame 5. Container:blit() → isInChildBounds() → adjusts for offsetX, offsetY 6. parent:blit() → BaseFrame 7. BaseFrame:blit() → _render:blit() → adds to dirty rects 8. BaseFrame:render() → _render:render() → terminal blit operations 

Sources: src/elements/Container.lua668-686 src/elements/VisualElement.lua652-718 src/elements/BaseFrame.lua60-90 src/render.lua1-100

Lifecycle Management

The hierarchy implements a consistent lifecycle across all elements:

Diagram: Element Lifecycle State Machine

Initialization Order and Method Calls:

  1. Creation: local button = Button.new()

    • Calls setmetatable({}, Button):__init()
    • __init() from BaseElement initializes PropertySystem
    • Sets up _properties, _values, _observers tables
  2. Initialization Chain: button:init(props, basalt)

  3. Property Setup: Properties from props table are set

    • button.set("x", 5) triggers setters and observers
    • Reactive properties are parsed (if reactive plugin loaded)
  4. Parent Addition: frame:addChild(button)

  5. Post-Initialization: postInit() hook (if delayed creation)

    • Used by elementManager for lazy-loaded elements

Destruction Sequence:

  1. element:destroy() is called
  2. Removes from parent: parent:removeChild(self)
  3. Removes all observers: removeAllObservers()
  4. Sets visible = false to hide immediately
  5. Cleanup implementation: src/elements/VisualElement.lua1096-1100

Sources: src/elements/Container.lua96-145 src/elements/VisualElement.lua118-152 src/elements/BaseElement.lua50-100

Focus Management

The hierarchy implements a single-focus model where only one element can have keyboard focus:

Diagram: Focus Management Chain

Focus Management Implementation:

The VisualElement:setFocused(focused, internal) method manages focus state at src/elements/VisualElement.lua838-862:

Focus Chain Propagation:

Container maintains focus via setFocusedChild() at src/elements/Container.lua505-532:

  1. Blurs previous focusedChild if it exists: oldChild:setFocused(false, true)
  2. Updates focusedChild property
  3. Focuses new child: newChild:setFocused(true, true)
  4. Propagates up: self.parent:setFocusedChild(self) (internal=true prevents recursion)

Keyboard Event Routing:

From BaseFrame down to focused element:

  1. BaseFrame:key(keyCode) checks if has focusedChild
  2. Routes to focusedChild:dispatchEvent("key", keyCode)
  3. Repeats at each Container level
  4. Final focused element calls fireEvent("key", keyCode)
  5. Implementation: src/elements/VisualElement.lua935-939 src/elements/Container.lua433-457

Sources: src/elements/VisualElement.lua838-890 src/elements/Container.lua505-532 src/elements/Container.lua433-457

Summary

The element hierarchy provides a clean separation of concerns across four levels:

  1. BaseElement: Property and event infrastructure
  2. VisualElement: Visual properties and mouse/keyboard interaction
  3. Container: Child management and event propagation
  4. BaseFrame: Terminal binding and root rendering

This design enables:

  • Property inheritance via PropertySystem
  • Event propagation through container hierarchy
  • Coordinate space transformation
  • Single-focus keyboard input model
  • Efficient rendering with dirty rectangle tracking

All 40+ UI elements build on this foundation by inheriting from either VisualElement (for leaf elements) or Container (for layout elements).