Skip to content

Custom Widgets

Create reusable components by implementing the widget interface.

Widget Structure

A widget in TUI.zig is any struct that implements render and optionally handleEvent.

zig
const std = @import("std");
const tui = @import("tui");

pub const MyWidget = struct {
    label: []const u8,
    clicked_count: usize = 0,

    pub fn init(label: []const u8) MyWidget {
        return .{ .label = label };
    }

    pub fn render(self: *MyWidget, ctx: *tui.RenderContext) void {
        var screen = ctx.getSubScreen();

        // Draw content
        screen.putString(self.label);
    }

    pub fn handleEvent(self: *MyWidget, event: tui.Event) tui.EventResult {
        // Handle input
        switch (event) {
            .key => |k| {
                if (k.key == .enter) {
                    self.clicked_count += 1;
                    return .needs_redraw;
                }
            },
            else => {},
        }
        return .ignored;
    }
};

State Management

Widgets can hold internal state (like clicked_count above). If state changes affect the display, return .needs_redraw from handleEvent.

Composition

Your custom widget can contain other widgets:

zig
pub const CompositeWidget = struct {
    button: tui.Button,
    text: tui.Text,

    pub fn init() CompositeWidget {
        return .{
            .button = tui.Button.init("Click", onClick),
            .text = tui.Text.init("Status: Idle"),
        };
    }

    pub fn render(self: *CompositeWidget, ctx: *tui.RenderContext) void {
        // Delegate rendering
        var sub1 = ctx.screen.subRegion(0, 0, 10, 1);
        self.button.render(&sub1);

        var sub2 = ctx.screen.subRegion(0, 2, 20, 1);
        self.text.render(&sub2);
    }
};

Released under the MIT License.