This page documents the four-level inheritance chain that forms the foundation of all UI components in Basalt: BaseElement → VisualElement → Container → BaseFrame. 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.
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
Each level in the hierarchy has distinct, non-overlapping responsibilities:
| Class | Primary Responsibilities | Key Properties | Key Methods |
|---|---|---|---|
| BaseElement | Property system integration, event registration, lifecycle management, ID generation | id, name, type, _properties, _values, _observers | init(), destroy(), fireEvent(), observe(), defineProperty() |
| VisualElement | Positioning, sizing, colors, mouse/keyboard event handling, coordinate transformation, constraints | x, y, width, height, foreground, background, visible, z, constraints | render(), calculatePosition(), isInBounds(), mouse_click(), setConstraint() |
| Container | Child element management, event propagation, visibility clipping, focus management, z-index sorting | children, focusedChild, offsetX, offsetY, childrenSorted, childrenEvents | addChild(), removeChild(), sortChildren(), callChildrenEvent(), setFocusedChild() |
| BaseFrame | Terminal binding, render buffer management, root-level event routing, monitor support, peripheral handling | term, _render, _peripheralName, _renderUpdate | setCursor(), monitor_touch(), term_resize(), dispatchEvent() |
Sources: src/elements/VisualElement.lua14-73 src/elements/Container.lua20-72 src/elements/BaseFrame.lua1-50
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
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:
Rendering: Each level calls parent render(), then adds its own drawing operations
Container:render() → VisualElement.render(self) → element-specific renderingEvent Handling: Parent checks bounds/conditions, child propagates to descendants
VisualElement:mouse_click() checks isInBounds() at src/elements/VisualElement.lua745-752Container propagates via callChildrenEvent() at src/elements/Container.lua363-402Initialization: Child calls parent init(), then performs own setup
Container:init() → VisualElement:init() → BaseElement:init()Sources: src/elements/Container.lua410-422 src/elements/VisualElement.lua745-831 src/elements/Container.lua363-402
The hierarchy implements a nested coordinate system where each container defines its own coordinate space:
Diagram: Nested Coordinate Spaces
Transformation Methods:
| Method | Purpose | Implementation Details | Location |
|---|---|---|---|
calculatePosition() | Converts element position to parent-relative coordinates, accounting for offsetX/offsetY | Returns x - offsetX, y - offsetY if not ignoreOffset | src/elements/VisualElement.lua963-974 |
getRelativePosition(x, y) | Converts absolute coordinates to element-relative coordinates | Recursively calculates offset from parent coordinates | src/elements/VisualElement.lua1008-1021 |
getAbsolutePosition(x, y) | Calculates screen-absolute coordinates by walking up parent chain | Walks parent chain, accumulating x and y offsets | src/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 ContainergetAbsolutePosition() returns (10+5-1, 8+3-1) = (14, 10) relative to screen(14, 10) is getRelativePosition() = (10-10+1, 8-8+1) = (1, 1) relative to ButtonSources: src/elements/VisualElement.lua963-1021 src/elements/Container.lua339-350
Events flow through the hierarchy in two directions: top-down (dispatch) and bottom-up (bubbling).
Diagram: Event Propagation Sequence
Event Registration and Propagation:
Event Definition: BaseElement.defineEvent(class, eventName) creates event infrastructure
"focus" and "blur" eventsEvent Handlers: VisualElement implements specific handlers that call fireEvent()
mouse_click(), key(), char() check state/bounds then call fireEvent()Event Propagation: Container:callChildrenEvent() dispatches to children
childrenEvents table by z-indexEvent Callbacks: Application code registers callbacks via onEventName(callback)
button:onClick(function(self, button, x, y) ... end)registerEventCallback() in src/elements/VisualElement.lua90-100Sources: src/elements/Container.lua363-402 src/elements/VisualElement.lua745-831 src/elements/BaseElement.lua200-250
All 40+ UI elements inherit from either VisualElement or Container:
Diagram: Concrete Element Inheritance Tree
Inheritance Decision Criteria:
Inherit from VisualElement when the element:
Button (elements/Button.lua) - Simple clickable textInput (elements/Input.lua) - Single-line text inputSlider (elements/Slider.lua) - Value selection widgetImage (elements/Image.lua) - BIMG format rendererTree (elements/Tree.lua) - Hierarchical data displayInherit from Container when the element:
Frame (elements/Frame.lua) - Basic container with optional dragging/scrollingFlexBox (elements/FlexBox.lua) - CSS flexbox-style layoutTabControl (elements/TabControl.lua) - Tabbed interface with content areasScrollFrame (elements/ScrollFrame.lua) - Scrollable viewport with scrollbarsMulti-Level Inheritance for specialized variants:
Collection → List → Menu/DropDown → ComboBox Frame → Dialog Sources: config.lua105-444 src/elements/VisualElement.lua1-11 src/elements/Container.lua1-18 src/elements/Collection.lua1-20
Each level contributes to the final rendering pipeline:
Diagram: Rendering Pipeline Through Hierarchy
Drawing Method Hierarchy:
Each level provides coordinate transformation and clipping:
| Level | Methods | Purpose | Implementation |
|---|---|---|---|
| BaseFrame | blit(x, y, text, fg, bg), textFg(), textBg(), multiBlit() | Write directly to render buffer (_render) with absolute terminal coordinates | src/elements/BaseFrame.lua60-90 |
| Container | Overrides blit(), textFg(), textBg(), multiBlit() | Clips content to container bounds (isInChildBounds()), applies scroll offset (offsetX, offsetY) | src/elements/Container.lua227-289 |
| VisualElement | Overrides blit(), textFg(), textBg(), multiBlit() | Transforms coordinates from element-relative to parent-relative via calculatePosition() | src/elements/VisualElement.lua652-718 |
| Concrete Elements | Calls parent methods (e.g., self:blit()) | Uses element-relative coordinates; parent handles transformation | Button: 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
The hierarchy implements a consistent lifecycle across all elements:
Diagram: Element Lifecycle State Machine
Initialization Order and Method Calls:
Creation: local button = Button.new()
setmetatable({}, Button):__init()__init() from BaseElement initializes PropertySystem_properties, _values, _observers tablesInitialization Chain: button:init(props, basalt)
Property Setup: Properties from props table are set
button.set("x", 5) triggers setters and observersParent Addition: frame:addChild(button)
children tablechildrenSorted and childrenEventsSorted flagsPost-Initialization: postInit() hook (if delayed creation)
Destruction Sequence:
element:destroy() is calledparent:removeChild(self)removeAllObservers()visible = false to hide immediatelySources: src/elements/Container.lua96-145 src/elements/VisualElement.lua118-152 src/elements/BaseElement.lua50-100
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:
focusedChild if it exists: oldChild:setFocused(false, true)focusedChild propertynewChild:setFocused(true, true)self.parent:setFocusedChild(self) (internal=true prevents recursion)Keyboard Event Routing:
From BaseFrame down to focused element:
BaseFrame:key(keyCode) checks if has focusedChildfocusedChild:dispatchEvent("key", keyCode)fireEvent("key", keyCode)Sources: src/elements/VisualElement.lua838-890 src/elements/Container.lua505-532 src/elements/Container.lua433-457
The element hierarchy provides a clean separation of concerns across four levels:
This design enables:
All 40+ UI elements build on this foundation by inheriting from either VisualElement (for leaf elements) or Container (for layout elements).
Refresh this wiki
This wiki was recently refreshed. Please wait 6 days to refresh again.