Update UI Component Hierarchy

John McCardle 2026-02-07 23:50:46 +00:00
commit b5d39573c6

@ -1,284 +1,284 @@
# UI Component Hierarchy # UI Component Hierarchy
The UI Component Hierarchy defines how visual elements are structured, rendered, and managed in McRogueFace. The UI Component Hierarchy defines how visual elements are structured, rendered, and managed in McRogueFace.
## Quick Reference ## Quick Reference
**Key Files:** **Key Files:**
- `src/UIDrawable.h` - Base class for all UI components - `src/UIDrawable.h` - Base class for all UI components
- `src/UIFrame.h` / `.cpp` - Container with children - `src/UIFrame.h` / `.cpp` - Container with children
- `src/UICaption.h` / `.cpp` - Text rendering - `src/UICaption.h` / `.cpp` - Text rendering
- `src/UISprite.h` / `.cpp` - Image/sprite rendering - `src/UISprite.h` / `.cpp` - Image/sprite rendering
- `src/UIGrid.h` / `.cpp` - Tilemap grid (see [[Grid-System]]) - `src/UIGrid.h` / `.cpp` - Tilemap grid (see [[Grid-System]])
- `src/UIEntity.h` / `.cpp` - Grid entities (see [[Entity-Management]]) - `src/UIEntity.h` / `.cpp` - Grid entities (see [[Entity-Management]])
- `src/UIArc.h`, `src/UICircle.h`, `src/UILine.h` - Geometry primitives - `src/UIArc.h`, `src/UICircle.h`, `src/UILine.h` - Geometry primitives
--- ---
## Class Hierarchy ## Class Hierarchy
``` ```
UIDrawable (base class) UIDrawable (base class)
+-- UIFrame - Rectangle container with children +-- UIFrame - Rectangle container with children
+-- UICaption - Text labels +-- UICaption - Text labels
+-- UISprite - Images and sprite sheets +-- UISprite - Images and sprite sheets
+-- UIGrid - Tilemap rendering +-- UIGrid - Tilemap rendering
+-- UIEntity - Grid-based game entities +-- UIEntity - Grid-based game entities
+-- UIArc - Arc/partial circle outline +-- UIArc - Arc/partial circle outline
+-- UICircle - Filled/outlined circle +-- UICircle - Filled/outlined circle
+-- UILine - Line segment +-- UILine - Line segment
``` ```
--- ---
## UIDrawable (Base Class) ## UIDrawable (Base Class)
All UI components inherit from `UIDrawable`, providing a consistent interface for positioning, visibility, hierarchy, and interaction. All UI components inherit from `UIDrawable`, providing a consistent interface for positioning, visibility, hierarchy, and interaction.
### Position and Geometry ### Position and Geometry
| Property | Type | Description | | Property | Type | Description |
|----------|------|-------------| |----------|------|-------------|
| `pos` | `(x, y)` | Position as tuple | | `pos` | `(x, y)` | Position as tuple |
| `x`, `y` | `float` | Position components | | `x`, `y` | `float` | Position components |
| `w`, `h` | `float` | Dimensions (Frame, Grid) | | `w`, `h` | `float` | Dimensions (Frame, Grid) |
| `bounds` | `(x, y, w, h)` | Bounding rectangle | | `bounds` | `(x, y, w, h)` | Bounding rectangle |
| `global_bounds` | `(x, y, w, h)` | Bounds in screen coordinates | | `global_bounds` | `(x, y, w, h)` | Bounds in screen coordinates |
| `global_position` | `(x, y)` | Position in screen coordinates (accounts for parent chain) | | `global_position` | `(x, y)` | Position in screen coordinates (accounts for parent chain) |
**Methods:** **Methods:**
- `move(dx, dy)` - Relative movement - `move(dx, dy)` - Relative movement
- `resize(w, h)` - Change dimensions - `resize(w, h)` - Change dimensions
### Hierarchy ### Hierarchy
| Property | Type | Description | | Property | Type | Description |
|----------|------|-------------| |----------|------|-------------|
| `parent` | `UIDrawable` | Parent element (read-only, set automatically) | | `parent` | `UIDrawable` | Parent element (read-only, set automatically) |
| `z_index` | `int` | Render order (higher = front) | | `z_index` | `int` | Render order (higher = front) |
Children are positioned relative to their parent. When a child is added to a Frame's `children` collection, its `parent` property is set automatically. Children are positioned relative to their parent. When a child is added to a Frame's `children` collection, its `parent` property is set automatically.
### Visibility ### Visibility
| Property | Type | Description | | Property | Type | Description |
|----------|------|-------------| |----------|------|-------------|
| `visible` | `bool` | Show/hide element | | `visible` | `bool` | Show/hide element |
| `opacity` | `float` | Transparency (0.0 = invisible, 1.0 = opaque) | | `opacity` | `float` | Transparency (0.0 = invisible, 1.0 = opaque) |
### Interaction Callbacks ### Interaction Callbacks
All UIDrawable types support mouse event callbacks: All UIDrawable types support mouse event callbacks:
| Property | Signature | Description | | Property | Signature | Description |
|----------|-----------|-------------| |----------|-----------|-------------|
| `on_click` | `(pos: Vector, button: MouseButton, action: InputState) -> None` | Mouse click | | `on_click` | `(pos: Vector, button: MouseButton, action: InputState) -> None` | Mouse click |
| `on_enter` | `(pos: Vector) -> None` | Mouse enters element bounds | | `on_enter` | `(pos: Vector) -> None` | Mouse enters element bounds |
| `on_exit` | `(pos: Vector) -> None` | Mouse leaves element bounds | | `on_exit` | `(pos: Vector) -> None` | Mouse leaves element bounds |
| `on_move` | `(pos: Vector) -> None` | Mouse moves within element | | `on_move` | `(pos: Vector) -> None` | Mouse moves within element |
```python ```python
frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150)) frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150))
def on_click(pos, button, action): def on_click(pos, button, action):
if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED: if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED:
print(f"Clicked at ({pos.x}, {pos.y})") print(f"Clicked at ({pos.x}, {pos.y})")
frame.on_click = on_click frame.on_click = on_click
frame.on_enter = lambda pos: print("Mouse entered") frame.on_enter = lambda pos: print("Mouse entered")
frame.on_exit = lambda pos: print("Mouse left") frame.on_exit = lambda pos: print("Mouse left")
frame.on_move = lambda pos: print(f"Mouse at ({pos.x}, {pos.y})") frame.on_move = lambda pos: print(f"Mouse at ({pos.x}, {pos.y})")
``` ```
### Animation ### Animation
All UIDrawable types support the `.animate()` method: All UIDrawable types support the `.animate()` method:
```python ```python
obj.animate("property", target_value, duration_secs, easing, callback=None) obj.animate("property", target_value, duration_secs, easing, callback=None)
``` ```
See [[Animation-System]] for details on animatable properties per type. See [[Animation-System]] for details on animatable properties per type.
### Utility ### Utility
| Property | Type | Description | | Property | Type | Description |
|----------|------|-------------| |----------|------|-------------|
| `name` | `str` | Optional identifier for debugging/lookup | | `name` | `str` | Optional identifier for debugging/lookup |
--- ---
## Concrete Types ## Concrete Types
### UIFrame ### UIFrame
Rectangle container that can hold child elements. Primary building block for UI layouts. Rectangle container that can hold child elements. Primary building block for UI layouts.
```python ```python
frame = mcrfpy.Frame(pos=(50, 50), size=(200, 150), frame = mcrfpy.Frame(pos=(50, 50), size=(200, 150),
fill_color=mcrfpy.Color(40, 40, 50)) fill_color=mcrfpy.Color(40, 40, 50))
frame.outline_color = mcrfpy.Color(100, 100, 120) frame.outline_color = mcrfpy.Color(100, 100, 120)
frame.outline = 2 frame.outline = 2
# Child elements are positioned relative to parent # Child elements are positioned relative to parent
label = mcrfpy.Caption(text="Title", pos=(10, 5)) label = mcrfpy.Caption(text="Title", pos=(10, 5))
frame.children.append(label) frame.children.append(label)
``` ```
**Unique properties:** `children` (UICollection), `fill_color`, `outline_color`, `outline`, `clip_children` **Unique properties:** `children` (UICollection), `fill_color`, `outline_color`, `outline`, `clip_children`
**Animatable:** `x`, `y`, `w`, `h`, `opacity`, `outline`, `fill_color`, `outline_color` **Animatable:** `x`, `y`, `w`, `h`, `opacity`, `outline`, `fill_color`, `outline_color`
### UICaption ### UICaption
Text rendering with font support. Text rendering with font support.
```python ```python
text = mcrfpy.Caption(text="Hello World!", pos=(10, 10)) text = mcrfpy.Caption(text="Hello World!", pos=(10, 10))
text.font_size = 24 text.font_size = 24
text.fill_color = mcrfpy.Color(255, 255, 255) text.fill_color = mcrfpy.Color(255, 255, 255)
``` ```
**Unique properties:** `text`, `font`, `font_size`, `fill_color` **Unique properties:** `text`, `font`, `font_size`, `fill_color`
**Animatable:** `x`, `y`, `opacity`, `outline`, `fill_color`, `outline_color` **Animatable:** `x`, `y`, `opacity`, `outline`, `fill_color`, `outline_color`
### UISprite ### UISprite
Image and sprite sheet rendering. Image and sprite sheet rendering.
```python ```python
texture = mcrfpy.Texture("assets/sprites/tileset.png", 16, 16) texture = mcrfpy.Texture("assets/sprites/tileset.png", 16, 16)
sprite = mcrfpy.Sprite(x=100, y=100) sprite = mcrfpy.Sprite(x=100, y=100)
sprite.texture = texture sprite.texture = texture
sprite.sprite_index = 5 sprite.sprite_index = 5
``` ```
**Unique properties:** `texture`, `sprite_index`, `scale` **Unique properties:** `texture`, `sprite_index`, `scale`
**Animatable:** `x`, `y`, `scale`, `sprite_index`, `opacity` **Animatable:** `x`, `y`, `scale`, `sprite_index`, `opacity`
### UIGrid ### UIGrid
Tilemap container for roguelike game maps. See [[Grid-System]] for complete documentation. Tilemap container for roguelike game maps. See [[Grid-System]] for complete documentation.
```python ```python
terrain = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=texture) terrain = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=texture)
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 300), grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 300),
layers=[terrain]) layers=[terrain])
``` ```
**Unique properties:** `grid_size`, `center`, `zoom`, `layers`, `entities`, `fill_color`, `perspective` **Unique properties:** `grid_size`, `center`, `zoom`, `layers`, `entities`, `fill_color`, `perspective`
**Animatable:** `x`, `y`, `w`, `h`, `center_x`, `center_y`, `zoom` **Animatable:** `x`, `y`, `w`, `h`, `center_x`, `center_y`, `zoom`
### UIEntity ### UIEntity
Game entities that live on grids. See [[Entity-Management]] for complete documentation. Game entities that live on grids. See [[Entity-Management]] for complete documentation.
```python ```python
entity = mcrfpy.Entity(grid_pos=(10, 10), sprite_index=0, name="player") entity = mcrfpy.Entity(grid_pos=(10, 10), sprite_index=0, name="player")
grid.entities.append(entity) grid.entities.append(entity)
``` ```
**Unique properties:** `grid_x`, `grid_y`, `sprite_index`, `grid` (parent reference) **Unique properties:** `grid_x`, `grid_y`, `sprite_index`, `grid` (parent reference)
**Animatable:** `x`, `y`, `draw_x`, `draw_y`, `sprite_index`, `sprite_scale` **Animatable:** `x`, `y`, `draw_x`, `draw_y`, `sprite_index`, `sprite_scale`
### UIArc, UICircle, UILine ### UIArc, UICircle, UILine
Geometry primitives for shapes and decorations. Geometry primitives for shapes and decorations.
```python ```python
arc = mcrfpy.Arc(radius=50, start_angle=0, end_angle=90) arc = mcrfpy.Arc(radius=50, start_angle=0, end_angle=90)
circle = mcrfpy.Circle(radius=30) circle = mcrfpy.Circle(radius=30)
line = mcrfpy.Line(start=(0, 0), end=(100, 100)) line = mcrfpy.Line(start=(0, 0), end=(100, 100))
``` ```
--- ---
## Collections ## Collections
### UICollection ### UICollection
Container for Frame, Caption, Sprite, Grid, and geometry primitives. Used by `Frame.children` and `Scene.children`. Container for Frame, Caption, Sprite, Grid, and geometry primitives. Used by `Frame.children` and `Scene.children`.
```python ```python
scene = mcrfpy.Scene("test") scene = mcrfpy.Scene("test")
ui = scene.children # UICollection ui = scene.children # UICollection
ui.append(frame) ui.append(frame)
ui.extend([caption, sprite]) ui.extend([caption, sprite])
ui.remove(frame) ui.remove(frame)
# Sequence protocol # Sequence protocol
for item in ui: for item in ui:
print(type(item).__name__) print(type(item).__name__)
obj = ui[0] obj = ui[0]
count = len(ui) count = len(ui)
``` ```
### UIEntityCollection ### UIEntityCollection
Container for Entity objects on a Grid. Container for Entity objects on a Grid.
```python ```python
grid.entities.append(entity) grid.entities.append(entity)
grid.entities.extend([e1, e2, e3]) grid.entities.extend([e1, e2, e3])
grid.entities.remove(entity) grid.entities.remove(entity)
for e in grid.entities: for e in grid.entities:
print(f"{e.name} at ({e.grid_x}, {e.grid_y})") print(f"{e.name} at ({e.grid_x}, {e.grid_y})")
``` ```
--- ---
## Rendering ## Rendering
### Z-Index Order ### Z-Index Order
1. Scene sorts UI elements by z_index (lower = back) 1. Scene sorts UI elements by z_index (lower = back)
2. Elements rendered in sorted order 2. Elements rendered in sorted order
3. Children rendered after parents 3. Children rendered after parents
### Parent-Child Coordinates ### Parent-Child Coordinates
Children use coordinates relative to their parent's position: Children use coordinates relative to their parent's position:
```python ```python
frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150)) frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150))
label = mcrfpy.Caption(text="Hello", pos=(10, 10)) label = mcrfpy.Caption(text="Hello", pos=(10, 10))
frame.children.append(label) frame.children.append(label)
print(label.x, label.y) # 10, 10 (relative) print(label.x, label.y) # 10, 10 (relative)
print(label.global_position) # (110, 110) (absolute) print(label.global_position) # (110, 110) (absolute)
``` ```
--- ---
## Type Preservation ## Type Preservation
When retrieving from collections, the correct Python type is preserved: When retrieving from collections, the correct Python type is preserved:
```python ```python
scene.children.append(mcrfpy.Sprite(x=0, y=0, sprite_index=0)) scene.children.append(mcrfpy.Sprite(x=0, y=0, sprite_index=0))
obj = scene.children[0] obj = scene.children[0]
print(type(obj).__name__) # "Sprite" (not "UIDrawable") print(type(obj).__name__) # "Sprite" (not "UIDrawable")
``` ```
**Implementation:** `RET_PY_INSTANCE` macro in `UIDrawable.h` handles type dispatch. **Implementation:** `RET_PY_INSTANCE` macro in `UIDrawable.h` handles type dispatch.
--- ---
## Related Systems ## Related Systems
- [[Animation-System]] - Animatable properties per type - [[Animation-System]] - Animatable properties per type
- [[Python-Binding-Layer]] - How UI classes are exposed to Python - [[Python-Binding-Layer]] - How UI classes are exposed to Python
- [[Grid-System]] - UIGrid specifics - [[Grid-System]] - UIGrid specifics
- [[Input-and-Events]] - Event dispatch and handling - [[Input-and-Events]] - Event dispatch and handling
- [[UI-Widget-Patterns]] - Buttons, dialogs, menus, HUD elements - [[UI-Widget-Patterns]] - Buttons, dialogs, menus, HUD elements
- [[Grid-Interaction-Patterns]] - Grid clicks, entity selection - [[Grid-Interaction-Patterns]] - Grid clicks, entity selection
--- ---
*Last updated: 2026-02-07* *Last updated: 2026-02-07*