Widgets
TUI.zig provides a rich set of built-in widgets for building interactive terminal interfaces.
Widget Basics
All widgets implement the same basic interface:
pub fn render(self: *Self, ctx: *RenderContext) void;
pub fn handleEvent(self: *Self, event: Event) EventResult;Built-in Widgets
Text
Display styled text with alignment and wrapping:
const tui = @import("tui");
// Simple text
var text = tui.Text.init("Hello, World!");
// Styled text
var styled = tui.Text.init("Important!")
.withStyle(tui.Style.default.setFg(tui.Color.red).bold())
.withAlignment(.center);Button
Clickable buttons with hover and press states:
var button = tui.Button.init("Click Me!", null)
.withOnClick(onButtonClick);
fn onButtonClick() void {
// Button was clicked
}Input Field
Single-line text input with cursor:
var input = tui.InputField.init(allocator)
.withPlaceholder("Enter your name...")
.withMaxLength(50);
// Get the entered value
const value = input.getValue();Text Area
Multi-line text editing:
var textarea = tui.TextArea.init(allocator)
.withLineNumbers()
.withWordWrap();
// Set content
try textarea.setText("Line 1\nLine 2\nLine 3");Checkbox
Toggle checkboxes:
var checkbox = tui.Checkbox.init("Enable feature", null);
// Check state
if (checkbox.isChecked()) {
// Feature is enabled
}Progress Bar
Visual progress indicators:
var progress = tui.ProgressBar.init()
.withLabel("Loading...")
.withShowPercentage(true);
// Update progress (0.0 - 1.0)
progress.setValue(0.75);Spinner
Animated loading indicators:
var spinner = tui.Spinner.initPreset(.dots)
.withLabel("Processing...");
// In your render loop, update the animation
spinner.tick();Available spinner presets:
.dots- Braille dots animation.line- Line rotation (- \ | /).dots_scrolling- Scrolling dots.star- Star animation.box_bounce- Bouncing box.arrow- Arrow rotation
List View
Scrollable item lists with selection:
var list = tui.ListView.init(allocator, []const u8);
try list.addItem("Item 1");
try list.addItem("Item 2");
try list.addItem("Item 3");
// Get selected item
if (list.getSelectedItem()) |item| {
// Process selected item
}Table
Data tables with columns:
var table = tui.Table.init(allocator);
try table.addColumn("Name", 20);
try table.addColumn("Email", 30);
try table.addColumn("Role", 15);
try table.addRow(.{ "John Doe", "john@example.com", "Admin" });
try table.addRow(.{ "Jane Smith", "jane@example.com", "User" });Tabs
Tabbed navigation:
var tabs = tui.Tabs.init(allocator);
try tabs.addTab("Home", &home_widget);
try tabs.addTab("Settings", &settings_widget);
try tabs.addTab("Help", &help_widget);Modal
Dialog overlays:
var modal = tui.Modal.init(allocator)
.withTitle("Confirm Action")
.withContent(&confirm_widget)
.withButtons(.{ "OK", "Cancel" });
modal.show();Accordion
Collapsible content sections with single or multiple expansion:
var items = [_]tui.AccordionItem{
.{ .title = "Section 1", .content = "Content for section 1" },
.{ .title = "Section 2", .content = "Content for section 2" },
};
var accordion = tui.Accordion.init(&items)
.withMode(.single);Alert
Status messages and notifications:
var alert = tui.Alert.init("Warning", "Disk space is low");
alert.show();AlertDialog
Confirmation dialogs with action buttons:
var dialog = tui.AlertDialog.init("Delete File", "Are you sure?");
dialog.on_confirm = onConfirm;
dialog.on_cancel = onCancel;Badge
Labels, tags, and status indicators:
var badge = tui.Badge.init("New")
.withVariant(.success)
.withSize(.small);Breadcrumb
Navigation breadcrumb trail:
const items = [_]tui.BreadcrumbItem{
.{ .label = "Home" },
.{ .label = "Products" },
.{ .label = "Electronics" },
};
var breadcrumb = tui.Breadcrumb.init(&items);Card
Grouped content with optional borders and headers:
var card = tui.Card.init("Card content here")
.withTitle("My Card")
.withFooter("Card footer");Grid
Layout grid for arranging widgets:
var grid = tui.Grid.init(3, 2); // 3 rows, 2 columnsImage
Image display with Kitty, iTerm2, Sixel, and ASCII protocol support:
var img = tui.Image.init(data, 80, 24)
.withProtocol(.ascii);Menu
Dropdown and context menus:
const items = [_]tui.MenuItem{
.{ .label = "Open" },
.{ .label = "Save" },
.{ .separator = true },
.{ .label = "Exit" },
};
var menu = tui.Menu.init(&items);Navbar
Top navigation bar:
const items = [_]tui.NavItem{
.{ .label = "Home" },
.{ .label = "About" },
.{ .label = "Contact" },
};
var navbar = tui.Navbar.init(&items);Pagination
Page navigation controls:
var pagination = tui.Pagination.init(10) // 10 pages
.withOnChange(onPageChange);Radio
Radio button groups for single selection:
const options = [_]tui.RadioOption{
.{ .label = "Option A", .value = 0 },
.{ .label = "Option B", .value = 1 },
.{ .label = "Option C", .value = 2 },
};
var radio = tui.RadioGroup.init(&options)
.withOnChange(onRadioChange);Scroll View
Scrollable content container:
var scroll = tui.ScrollView.init(&content_widget);Separator
Visual content dividers:
var sep = tui.Separator.init()
.withOrientation(.horizontal)
.withStyle(.double);Sidebar
Side navigation panel:
const items = [_]tui.SidebarItem{
.{ .label = "Dashboard" },
.{ .label = "Settings" },
.{ .label = "Profile" },
};
var sidebar = tui.Sidebar.init(&items);Skeleton
Loading placeholders with shimmer animation:
var skeleton = tui.Skeleton.init(.rectangle)
.withSize(30, 5);Slider
Numeric slider input:
var slider = tui.Slider.init(0.0, 100.0)
.withValue(50.0)
.withStep(5.0);Split View
Side-by-side content panels:
var split = tui.SplitView.init(&left_panel, &right_panel)
.withDirection(.horizontal)
.withRatio(0.3);Statusbar
Bottom status bar:
const items = [_]tui.StatusItem{
.{ .text = "Ready", .alignment = .left },
.{ .text = "Ln 1, Col 1", .alignment = .right },
};
var statusbar = tui.Statusbar.init(&items);Switch
Toggle switch for boolean settings:
var switch = tui.Switch.init("Dark mode");Toast
Temporary notification popups:
var toast = tui.Toast.init("File saved successfully");
var manager = tui.ToastManager.init(allocator);
try manager.show(toast);Tooltip
Contextual help on hover:
var tooltip = tui.Tooltip.init("Click to save")
.withPosition(.bottom);
tooltip.show();Tree
Hierarchical tree view:
var nodes = [_]tui.TreeNode{
.{ .label = "Root" },
.{ .label = "Child 1" },
.{ .label = "Child 2" },
};
var tree = tui.TreeView.init(&nodes);Border
Border drawing utilities:
var border = tui.Border.init()
.withStyle(.double)
.withTitle("Panel");Creating Custom Widgets
You can create your own widgets by implementing the required interface:
const MyWidget = struct {
// Your state
value: i32 = 0,
pub fn render(self: *MyWidget, ctx: *tui.RenderContext) void {
var screen = ctx.getSubScreen();
screen.setStyle(tui.Style.default.setFg(tui.Color.green));
screen.moveCursor(0, 0);
var buf: [32]u8 = undefined;
const text = std.fmt.bufPrint(&buf, "Value: {d}", .{self.value}) catch "?";
screen.putString(text);
}
pub fn handleEvent(self: *MyWidget, event: tui.Event) tui.EventResult {
switch (event) {
.key => |k| switch (k.key) {
.up => { self.value += 1; return .needs_redraw; },
.down => { self.value -= 1; return .needs_redraw; },
else => {},
},
else => {},
}
return .ignored;
}
};See the Custom Widgets guide for more details.