Back to Projects
TUIX v0.2.1Beta

Last Updated: 2026-05-20

Your First Widget

This tutorial builds a complete interactive choice menu from scratch — engine initialization, widget creation, input handling, and result retrieval.

What You Will Build

  • A scene with one Choice widget
  • An input loop that reacts to arrow keys and Enter
  • A clean shutdown path that frees buffers, scenes, and the input listener
Before You Run the ExampleMake sure you already completed the Installation page and that the short engine.init() verification snippet works in your terminal.

Step 1: Initialize the Engine

from tuix.core import engine, builders, scenes, registry, objects, buffers, input

engine.init()
builders.register_standard()

engine.init() initializes the C core and the global registry. builders.register_standard() registers the four built-in widget types: ProgressBarBuilder, ChoiceBuilder, InputBuilder, and CanvasBuilder.

Step 2: Create a Scene

scenes.init_scene(b"main")
registry.registry.current_scene_name = b"main"

Scenes are containers for widgets. You must create at least one scene and set it as active before creating widgets. Scene names are byte strings.

Step 3: Start Input Listener

input.listen()

This starts a background thread that captures keyboard and mouse events. Events are queued and consumed as snapshots during the main loop.

Step 4: Create a Choice Widget

uid = objects.create_object(
    builders.CHOICE,   # Widget type
    b"main",           # Scene name
    0.4,               # width_mod: 40% of terminal width
    0.5,               # height_mod: 50% of terminal height
    0.25,              # margin_top_mod: 25% from top
    0.3                # margin_left_mod: 30% from left
)

# Get the object pointer for widget-specific operations
buf = buffers.get_buffer_by_uid(uid)
obj = buf.contents.obj.contents

create_object returns a unique integer UID. All dimensions are proportional modifiers (0.0–1.0) of the current terminal size. To call widget-specific functions, retrieve the object pointer via get_buffer_by_uid.

Step 5: Configure Options

options = [b"Red", b"Green", b"Blue", b"Yellow", b"Cyan"]
objects.tuix_choice_set_options(obj, options)

Options are passed as a list of byte strings. The C layer deep-copies the labels, so the Python list can be discarded after this call.

Step 6: Render Loop with Input

while True:
    snap = input.get_snapshot()
    objects.tuix_choice_feed_input(obj, snap)
    engine.main_loop()

    if objects.tuix_choice_is_confirmed(obj):
        break

Each iteration: get an input snapshot, feed it to the choice widget (processes arrow keys and Enter), run the render pipeline, and check if the user confirmed a selection.

Common MistakeDo not exit the program right after the loop without cleanup. The safe sequence is: free the widget buffer, free the scene, stop input, then shut down the engine.

Step 7: Get Result and Clean Up

selected = objects.tuix_choice_get_selected(obj)
print(f"Selected index: {selected}")
print(f"Selected option: {options[selected].decode()}")

buffers.free_buffer(b"main", uid)
scenes.free_scene(b"main")
input.stop()
engine.shutdown()

tuix_choice_get_selected returns the 0-based index of the confirmed option. Always free buffers and scenes before shutting down.

Complete Code

from tuix.core import engine, builders, scenes, registry, objects, buffers, input

# Initialize
engine.init()
builders.register_standard()
scenes.init_scene(b"main")
registry.registry.current_scene_name = b"main"
input.listen()

# Create choice widget
uid = objects.create_object(
    builders.CHOICE, b"main",
    0.4, 0.5, 0.25, 0.3
)
buf = buffers.get_buffer_by_uid(uid)
obj = buf.contents.obj.contents

# Configure options
options = [b"Red", b"Green", b"Blue", b"Yellow", b"Cyan"]
objects.tuix_choice_set_options(obj, options)

# Render loop
while True:
    snap = input.get_snapshot()
    objects.tuix_choice_feed_input(obj, snap)
    engine.main_loop()
    if objects.tuix_choice_is_confirmed(obj):
        break

# Result
selected = objects.tuix_choice_get_selected(obj)
print(f"You chose: {options[selected].decode()}")

# Cleanup
buffers.free_buffer(b"main", uid)
scenes.free_scene(b"main")
input.stop()
engine.shutdown()

Next Steps

Explore the Widgets overview to learn about all four widget types, or jump directly to the Canvas API for free-draw graphics.

You Are Ready for the Reference PagesOnce this example makes sense, the API pages become much easier to read because you already understand the runtime flow: init -> scene -> widget -> input -> main_loop -> cleanup.