Back to Projects
TUIX v0.2.0Beta

Last Updated: 2026-05-20

Widgets

Widgets are the interactive elements rendered to the terminal. Each widget is an instance of a builder type, lives in a scene, and has a unique integer UID.

Builder System

Widget types are defined by builders — a polymorphic pattern implemented in C. Each builder provides five callbacks: create_state (allocate per-widget state), destroy_state (free state), handler_func (per-frame update logic), build_content (render state to pixel buffer), and on_resize (optional geometry notification).

TUIX ships four built-in builders, registered by calling builders.register_standard(). The builder constants are byte strings used when creating widgets:

ConstantValueWidget Type
builders.PROGRESSBARb"ProgressBarBuilder"Animated progress bar
builders.CHOICEb"ChoiceBuilder"Selection menu
builders.INPUTb"InputBuilder"Text entry field
builders.CANVASb"CanvasBuilder"Free-draw surface

Creating a Widget

uid = objects.create_object(
    builders.CHOICE,   # builder name (bytes)
    b"main",           # scene name (bytes)
    0.4,               # width_mod: fraction of terminal width
    0.5,               # height_mod: fraction of terminal height
    0.25,              # margin_top_mod: fraction from top
    0.3                # margin_left_mod: fraction from left
)

All six parameters are required. The four float modifiers (width_mod, height_mod, margin_top_mod, margin_left_mod) are proportional values between 0.0 and 1.0, multiplied by the terminal dimensions each frame.

Proportional Geometry

Widget dimensions are not fixed pixel counts. They are fractions of the terminal size, resolved every frame by the geometry resolver:

buffer.width       = terminal_width  × object.width_mod
buffer.height      = terminal_height × object.height_mod
buffer.margin_left = terminal_width  × object.margin_left_mod
buffer.margin_top  = terminal_height × object.margin_top_mod

This means widgets automatically resize when the terminal is resized. A widget with width_mod=0.5 always occupies half the terminal width.

Accessing Widget Objects

To call widget-specific functions (set options, feed input, etc.), you need the TuixObject pointer. Retrieve it through the buffer:

buf = buffers.get_buffer_by_uid(uid)
obj = buf.contents.obj.contents

# Now use widget-specific functions
objects.tuix_choice_set_options(obj, [b"A", b"B", b"C"])

Widget Lifecycle

  1. Create: objects.create_object() → allocates object, buffer, and registers subcycle handler
  2. Configure: call widget-specific set functions (set_options, set_value, set_placeholder, etc.)
  3. Render: engine.main_loop() resolves geometry, calls build_content, composites, renders
  4. Input: get snapshot via input.get_snapshot(), feed to widget via feed_input()
  5. Query: check is_confirmed/is_submitted, get_selected/get_text/get_result
  6. Reset (optional): widget-specific reset() to reuse without recreating
  7. Destroy: buffers.free_buffer(scene, uid) to free the buffer and state

Subcycle Handlers

Each widget has a handler function that runs every frame as part of the subcycle system. The handler returns a TuixHandlerResponse with a requires_redraw flag. If the handler marks redraw as needed (e.g., after a state change), the widget's buffer is regenerated that frame. Otherwise, the previous pixel buffer is reused.

Multi-Widget Input (v0.2.0 Limitation)In v0.2.0, there is no focus system. All interactive widgets in the active scene receive the same input snapshot when you call feed_input. If you have both a Choice and an Input widget, both will process the same keystrokes simultaneously. A focus routing system is planned for v0.2.1.