Eventos de teclado: - Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind - Soporte para todas las teclas: caracteres, F1-F12, navegación, control - Modificadores: Ctrl, Alt, Shift, Super Eventos de ratón: - MouseEvent, MouseEventKind, MouseButton - Click, release, drag, scroll (up/down/left/right) - Posición (column, row) con modificadores - Protocolos: SGR extended y X10 legacy Parser de escape sequences: - CSI sequences (arrows, F-keys, navigation) - SS3 sequences (F1-F4 alternativo) - SGR mouse protocol (mejores coordenadas) - X10 mouse protocol (compatibilidad) - Focus events, bracketed paste Cursor control: - Visibility: show/hide - Blinking: enable/disable - Styles: block, underline, bar (blinking/steady) - Position: moveTo, moveUp/Down/Left/Right - Save/restore position Terminal integrado: - pollEvent(timeout_ms) - polling con timeout - readEvent() - blocking read - enableMouseCapture/disableMouseCapture - enableFocusChange/disableFocusChange - enableBracketedPaste/disableBracketedPaste Ejemplo interactivo: - examples/events_demo.zig Tests: 47 (29 nuevos para eventos y cursor) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
21 KiB
21 KiB
zcatui - API Reference
Referencia rapida de la API publica de zcatui
Importar la Libreria
const zcatui = @import("zcatui");
// Core types
const Color = zcatui.Color;
const Style = zcatui.Style;
const Modifier = zcatui.Modifier;
const Cell = zcatui.Cell;
const Buffer = zcatui.Buffer;
const Rect = zcatui.Rect;
const Span = zcatui.Span;
const Line = zcatui.Line;
const Text = zcatui.Text;
const Alignment = zcatui.Alignment;
// Layout
const Layout = zcatui.Layout;
const Constraint = zcatui.Constraint;
const Direction = zcatui.Direction;
// Terminal
const Terminal = zcatui.Terminal;
// Events (crossterm-style)
const Event = zcatui.Event;
const KeyEvent = zcatui.KeyEvent;
const KeyCode = zcatui.KeyCode;
const KeyModifiers = zcatui.KeyModifiers;
const MouseEvent = zcatui.MouseEvent;
const MouseEventKind = zcatui.MouseEventKind;
const MouseButton = zcatui.MouseButton;
// Cursor
const Cursor = zcatui.Cursor;
const CursorStyle = zcatui.CursorStyle;
// Widgets
const widgets = zcatui.widgets;
const Block = widgets.Block;
const Paragraph = widgets.Paragraph;
const List = widgets.List;
const ListState = widgets.ListState;
// ... etc
Core Types
Color
pub const Color = union(enum) {
// Reset
reset,
// Basic 16 colors
black, red, green, yellow, blue, magenta, cyan, white,
light_black, light_red, light_green, light_yellow,
light_blue, light_magenta, light_cyan, light_white,
// 256 color palette
indexed: u8,
// True color (24-bit)
rgb: struct { r: u8, g: u8, b: u8 },
// Constructor helpers
pub fn indexed(n: u8) Color;
pub fn rgb(r: u8, g: u8, b: u8) Color;
};
Ejemplos:
const red = Color.red;
const gray = Color.indexed(240);
const custom = Color.rgb(255, 128, 0);
Style
pub const Style = struct {
foreground: ?Color = null,
background: ?Color = null,
add_modifiers: Modifier = .{},
sub_modifiers: Modifier = .{},
pub const default: Style = .{};
// Fluent setters
pub fn fg(self: Style, color: Color) Style;
pub fn bg(self: Style, color: Color) Style;
pub fn bold(self: Style) Style;
pub fn dim(self: Style) Style;
pub fn italic(self: Style) Style;
pub fn underlined(self: Style) Style;
pub fn slow_blink(self: Style) Style;
pub fn rapid_blink(self: Style) Style;
pub fn reversed(self: Style) Style;
pub fn hidden(self: Style) Style;
pub fn crossed_out(self: Style) Style;
// Remove modifiers
pub fn notBold(self: Style) Style;
pub fn notDim(self: Style) Style;
// ... etc
// Combine styles
pub fn patch(self: Style, other: Style) Style;
};
Ejemplos:
const style1 = Style.default.fg(Color.red).bold();
const style2 = Style.default.bg(Color.blue).italic();
const combined = style1.patch(style2); // red fg, blue bg, bold+italic
Modifier
pub const Modifier = packed struct {
bold: bool = false,
dim: bool = false,
italic: bool = false,
underlined: bool = false,
slow_blink: bool = false,
rapid_blink: bool = false,
reversed: bool = false,
hidden: bool = false,
crossed_out: bool = false,
pub const empty: Modifier = .{};
pub const all: Modifier = .{ .bold = true, ... };
pub fn contains(self: Modifier, other: Modifier) bool;
pub fn insert(self: Modifier, other: Modifier) Modifier;
pub fn remove(self: Modifier, other: Modifier) Modifier;
};
Rect
pub const Rect = struct {
x: u16,
y: u16,
width: u16,
height: u16,
pub fn init(x: u16, y: u16, width: u16, height: u16) Rect;
// Getters
pub fn left(self: Rect) u16; // x
pub fn right(self: Rect) u16; // x + width
pub fn top(self: Rect) u16; // y
pub fn bottom(self: Rect) u16; // y + height
pub fn area(self: Rect) u32; // width * height
// Queries
pub fn isEmpty(self: Rect) bool;
pub fn contains(self: Rect, x: u16, y: u16) bool;
// Transformations
pub fn inner(self: Rect, margin: u16) Rect;
pub fn innerMargins(self: Rect, top: u16, right: u16, bottom: u16, left_: u16) Rect;
pub fn intersection(self: Rect, other: Rect) Rect;
pub fn union_(self: Rect, other: Rect) Rect;
};
Ejemplos:
const area = Rect.init(0, 0, 80, 24);
const inner = area.inner(1); // Rect.init(1, 1, 78, 22)
Symbol
Almacenamiento compacto UTF-8 (hasta 4 bytes). Optimizado para evitar conversiones en render.
pub const Symbol = struct {
data: [4]u8 = .{ ' ', 0, 0, 0 },
len: u3 = 1,
pub const default_val: Symbol = .{};
pub const space: Symbol = .{};
// Constructores
pub fn fromSlice(bytes: []const u8) Symbol;
pub fn fromCodepoint(cp: u21) Symbol;
// Acceso
pub fn slice(self: Symbol) []const u8; // Para output directo
pub fn eql(self: Symbol, other: Symbol) bool;
};
Cell
pub const Cell = struct {
symbol: Symbol = Symbol.space,
fg: Color = .reset,
bg: Color = .reset,
modifiers: Modifier = .{},
pub const empty: Cell = .{};
// Constructores
pub fn init(cp: u21) Cell;
pub fn fromStr(s: []const u8) Cell;
// Modificadores
pub fn setStyle(self: *Cell, s: Style) void;
pub fn setChar(self: *Cell, cp: u21) void;
pub fn setSymbol(self: *Cell, s: []const u8) void;
pub fn reset(self: *Cell) void;
// Comparación (para diff)
pub fn eql(self: Cell, other: Cell) bool;
// Legacy accessor
pub fn char(self: Cell) u21; // Decodifica symbol a codepoint
};
Buffer
pub const Buffer = struct {
area: Rect,
cells: []Cell,
allocator: Allocator,
pub fn init(allocator: Allocator, area: Rect) !Buffer;
pub fn deinit(self: *Buffer) void;
// Cell access
pub fn getPtr(self: *Buffer, x: u16, y: u16) ?*Cell;
pub fn getCell(self: *Buffer, x: u16, y: u16) ?*Cell; // Alias
pub fn get(self: *const Buffer, x: u16, y: u16) ?Cell;
// Setting content
pub fn setChar(self: *Buffer, x: u16, y: u16, cp: u21, s: Style) void;
pub fn setString(self: *Buffer, x: u16, y: u16, text: []const u8, style: Style) u16;
pub fn setStyle(self: *Buffer, area: Rect, style: Style) void;
// Filling
pub fn fill(self: *Buffer, rect: Rect, cp: u21, s: Style) void;
pub fn clear(self: *Buffer) void;
// Differential rendering (v1.1)
pub fn diff(self: *const Buffer, other: *const Buffer) DiffIterator;
pub fn resize(self: *Buffer, new_rect: Rect) !void;
pub fn merge(self: *Buffer, other: *const Buffer) void;
// Legacy (no-op, for compatibility)
pub fn markDirty(self: *Buffer) void;
pub fn markClean(self: *Buffer) void;
};
// Iterator para renderizado diferencial
pub const DiffIterator = struct {
pub fn next(self: *DiffIterator) ?CellUpdate;
pub fn countRemaining(self: *DiffIterator) usize;
};
pub const CellUpdate = struct {
x: u16,
y: u16,
cell: Cell,
};
Ejemplos:
var buf = try Buffer.init(allocator, Rect.init(0, 0, 80, 24));
defer buf.deinit();
_ = buf.setString(10, 5, "Hello, World!", Style.default.fg(Color.green));
if (buf.getCell(10, 5)) |cell| {
cell.setStyle(Style.default.bold());
}
Text Types
Span
pub const Span = struct {
content: []const u8,
style: Style,
pub fn init(content: []const u8) Span;
pub fn raw(content: []const u8) Span;
pub fn styled(content: []const u8, style: Style) Span;
pub fn setStyle(self: Span, style: Style) Span;
pub fn width(self: Span) usize;
};
Line
pub const Line = struct {
spans: []const Span,
alignment: Alignment,
pub fn init(spans: []const Span) Line;
pub fn raw(content: []const u8) Line;
pub fn styled(content: []const u8, style: Style) Line;
pub fn setStyle(self: Line, style: Style) Line;
pub fn setAlignment(self: Line, alignment: Alignment) Line;
pub fn width(self: Line) usize;
};
Text
pub const Text = struct {
lines: []const Line,
alignment: Alignment,
pub fn init(lines: []const Line) Text;
pub fn raw(content: []const u8) Text;
pub fn styled(content: []const u8, style: Style) Text;
pub fn setStyle(self: Text, style: Style) Text;
pub fn setAlignment(self: Text, alignment: Alignment) Text;
pub fn width(self: Text) usize;
pub fn height(self: Text) usize;
};
Alignment
pub const Alignment = enum {
left,
center,
right,
};
Ejemplos:
// Simple text
const span = Span.styled("Hello", Style.default.fg(Color.red));
const line = Line.raw("Simple line");
// Multi-span line
const multi_line = Line.init(&[_]Span{
Span.styled("Error: ", Style.default.fg(Color.red).bold()),
Span.raw("Something went wrong"),
});
// Multi-line text
const text = Text.raw("Line 1\nLine 2\nLine 3");
Layout
Layout
pub const Layout = struct {
direction: Direction,
constraints: []const Constraint,
pub fn horizontal(constraints: []const Constraint) Layout;
pub fn vertical(constraints: []const Constraint) Layout;
pub fn init(direction: Direction, constraints: []const Constraint) Layout;
pub fn split(self: Layout, area: Rect, result: []Rect) void;
};
Constraint
pub const Constraint = union(enum) {
length: u16, // Exactly N cells
min: u16, // At least N cells
max: u16, // At most N cells
percentage: u16, // N% of available space (0-100)
ratio: struct { num: u32, den: u32 },
fill: u16, // Fill remaining space (weight)
pub fn length(n: u16) Constraint;
pub fn min(n: u16) Constraint;
pub fn max(n: u16) Constraint;
pub fn percentage(n: u16) Constraint;
pub fn ratio(num: u32, den: u32) Constraint;
pub fn fill(weight: u16) Constraint;
};
Direction
pub const Direction = enum {
horizontal,
vertical,
};
Ejemplos:
// Vertical layout: header (3 rows), content (rest), footer (1 row)
const layout = Layout.vertical(&[_]Constraint{
Constraint.length(3),
Constraint.min(0),
Constraint.length(1),
});
var chunks: [3]Rect = undefined;
layout.split(area, &chunks);
// Horizontal split: 30% | 70%
const h_layout = Layout.horizontal(&[_]Constraint{
Constraint.percentage(30),
Constraint.percentage(70),
});
Symbols
Line Set
pub const line = struct {
pub const Set = struct {
vertical: []const u8,
horizontal: []const u8,
top_right: []const u8,
top_left: []const u8,
bottom_right: []const u8,
bottom_left: []const u8,
vertical_left: []const u8,
vertical_right: []const u8,
horizontal_down: []const u8,
horizontal_up: []const u8,
cross: []const u8,
};
pub const NORMAL: Set; // ─│┌┐└┘
pub const ROUNDED: Set; // ─│╭╮╰╯
pub const DOUBLE: Set; // ═║╔╗╚╝
pub const THICK: Set; // ━┃┏┓┗┛
};
Border Set
pub const border = struct {
pub const Set = struct {
top_left: []const u8,
top_right: []const u8,
bottom_left: []const u8,
bottom_right: []const u8,
horizontal: []const u8,
vertical: []const u8,
};
pub const PLAIN: Set;
pub const ROUNDED: Set;
pub const DOUBLE: Set;
pub const THICK: Set;
};
Block Characters
pub const block = struct {
pub const FULL: []const u8 = "█";
pub const UPPER_HALF: []const u8 = "▀";
pub const LOWER_HALF: []const u8 = "▄";
pub const LEFT_HALF: []const u8 = "▌";
pub const RIGHT_HALF: []const u8 = "▐";
// ...
};
Bar Characters
pub const bar = struct {
pub const Set = struct {
full: []const u8,
seven_eighths: []const u8,
three_quarters: []const u8,
five_eighths: []const u8,
half: []const u8,
three_eighths: []const u8,
one_quarter: []const u8,
one_eighth: []const u8,
empty: []const u8,
};
pub const NINE_LEVELS: Set;
pub const THREE_LEVELS: Set;
};
Braille
pub const braille = struct {
pub const BLANK: []const u8 = "⠀"; // U+2800
// Bit positions for 2x4 grid:
// 0 3
// 1 4
// 2 5
// 6 7
pub const PATTERNS: [256][3]u8; // Pre-computed UTF-8 patterns
pub fn fromPattern(pattern: u8) []const u8;
};
Marker
pub const Marker = enum {
dot,
block,
bar,
braille,
half_block,
};
Terminal
pub const Terminal = struct {
pub fn init(allocator: Allocator) !Terminal;
pub fn deinit(self: *Terminal) void;
pub fn size(self: Terminal) struct { width: u16, height: u16 };
pub fn area(self: Terminal) Rect;
// Rendering
pub fn draw(self: *Terminal, render_fn: fn(area: Rect, buf: *Buffer) void) !void;
pub fn drawWithContext(self: *Terminal, ctx: anytype, render_fn: fn(...) void) !void;
pub fn clear(self: *Terminal) !void;
pub fn flush(self: *Terminal) !void;
// Cursor
pub fn hideCursor(self: *Terminal) !void;
pub fn showCursor(self: *Terminal) !void;
pub fn setCursorPosition(self: *Terminal, x: u16, y: u16) !void;
// Events (crossterm-style)
pub fn pollEvent(self: *Terminal, timeout_ms: ?u32) !?Event;
pub fn readEvent(self: *Terminal) !Event;
// Mouse
pub fn enableMouseCapture(self: *Terminal) !void;
pub fn disableMouseCapture(self: *Terminal) !void;
// Focus
pub fn enableFocusChange(self: *Terminal) !void;
pub fn disableFocusChange(self: *Terminal) !void;
// Bracketed paste
pub fn enableBracketedPaste(self: *Terminal) !void;
pub fn disableBracketedPaste(self: *Terminal) !void;
};
Ejemplo interactivo completo:
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var term = try Terminal.init(allocator);
defer term.deinit();
// Enable mouse (optional)
try term.enableMouseCapture();
var running = true;
while (running) {
try term.draw(render);
// Poll for events with 100ms timeout
if (try term.pollEvent(100)) |event| {
switch (event) {
.key => |key| {
if (key.code == .esc) running = false;
if (key.code.isChar('q')) running = false;
},
.mouse => |mouse| {
if (mouse.kind == .down) handleClick(mouse.column, mouse.row);
},
.resize => |size| try term.resize(size.width, size.height),
else => {},
}
}
}
}
Events (crossterm-style)
Event
pub const Event = union(enum) {
key: KeyEvent,
mouse: MouseEvent,
resize: ResizeEvent,
focus_gained,
focus_lost,
paste: []const u8,
};
KeyEvent
pub const KeyEvent = struct {
code: KeyCode,
modifiers: KeyModifiers,
kind: KeyEventKind,
pub fn char(c: u21) KeyEvent;
pub fn withMod(code: KeyCode, mods: KeyModifiers) KeyEvent;
pub fn getChar(self: KeyEvent) ?u21;
pub fn isCtrl(self: KeyEvent) bool;
pub fn isAlt(self: KeyEvent) bool;
pub fn isShift(self: KeyEvent) bool;
};
KeyCode
pub const KeyCode = union(enum) {
char: u21, // Any unicode character
f: u8, // F1-F12
backspace, enter, tab, backtab,
left, right, up, down,
home, end, page_up, page_down,
insert, delete, esc, null_key,
caps_lock, scroll_lock, num_lock,
print_screen, pause, menu,
media: MediaKeyCode,
modifier: ModifierKeyCode,
pub fn isChar(self: KeyCode, c: u21) bool;
};
KeyModifiers
pub const KeyModifiers = packed struct {
shift: bool = false,
ctrl: bool = false,
alt: bool = false,
super: bool = false,
pub const none: KeyModifiers = .{};
pub const SHIFT: KeyModifiers = .{ .shift = true };
pub const CTRL: KeyModifiers = .{ .ctrl = true };
pub const ALT: KeyModifiers = .{ .alt = true };
pub fn combine(self: KeyModifiers, other: KeyModifiers) KeyModifiers;
pub fn any(self: KeyModifiers) bool;
pub fn isEmpty(self: KeyModifiers) bool;
};
MouseEvent
pub const MouseEvent = struct {
kind: MouseEventKind,
button: MouseButton,
column: u16,
row: u16,
modifiers: KeyModifiers,
pub fn down(button: MouseButton, col: u16, row: u16) MouseEvent;
pub fn up(button: MouseButton, col: u16, row: u16) MouseEvent;
pub fn moved(col: u16, row: u16) MouseEvent;
pub fn isClick(self: MouseEvent) bool;
pub fn isLeft(self: MouseEvent) bool;
pub fn isRight(self: MouseEvent) bool;
};
MouseEventKind
pub const MouseEventKind = enum {
down, up, drag, moved,
scroll_down, scroll_up,
scroll_left, scroll_right,
pub fn isScroll(self: MouseEventKind) bool;
pub fn isButton(self: MouseEventKind) bool;
};
MouseButton
pub const MouseButton = enum {
left, right, middle, none,
};
Ejemplo de manejo de eventos:
if (try term.pollEvent(100)) |event| {
switch (event) {
.key => |key| {
// Ctrl+C
if (key.code.isChar('c') and key.isCtrl()) {
break;
}
// Arrow keys
switch (key.code) {
.up => state.previous(),
.down => state.next(),
.enter => state.select(),
else => {},
}
},
.mouse => |mouse| {
switch (mouse.kind) {
.down => {
if (mouse.isLeft()) selectAt(mouse.column, mouse.row);
},
.scroll_up => state.scrollUp(),
.scroll_down => state.scrollDown(),
else => {},
}
},
.resize => |size| {
try term.resize(size.width, size.height);
},
.focus_gained => {},
.focus_lost => {},
.paste => |text| handlePaste(text),
}
}
Cursor
pub const CursorStyle = enum {
default,
blinking_block,
steady_block,
blinking_underline,
steady_underline,
blinking_bar,
steady_bar,
};
pub const Cursor = struct {
pub fn init() Cursor;
// Visibility
pub fn hide(self: *Cursor) !void;
pub fn show(self: *Cursor) !void;
// Blinking
pub fn enableBlinking(self: *Cursor) !void;
pub fn disableBlinking(self: *Cursor) !void;
// Style
pub fn setStyle(self: *Cursor, style: CursorStyle) !void;
// Position
pub fn moveTo(self: *Cursor, column: u16, row: u16) !void;
pub fn moveUp(self: *Cursor, n: u16) !void;
pub fn moveDown(self: *Cursor, n: u16) !void;
pub fn moveLeft(self: *Cursor, n: u16) !void;
pub fn moveRight(self: *Cursor, n: u16) !void;
// Save/Restore
pub fn savePosition(self: *Cursor) !void;
pub fn restorePosition(self: *Cursor) !void;
};
Widgets Quick Reference
| Widget | Constructor | Stateful | Key Methods |
|---|---|---|---|
| Block | Block.init() |
No | title(), borders(), borderStyle() |
| Paragraph | Paragraph.init(text) |
No | setWrap(), setAlignment(), setScroll() |
| List | List.init(items) |
Yes | setHighlightStyle(), setHighlightSymbol() |
| Table | Table.init(rows, widths) |
Yes | setHeader(), setHighlightStyle() |
| Gauge | Gauge.init() |
No | setRatio(), setPercent(), setLabel() |
| LineGauge | LineGauge.init() |
No | setRatio(), setFilledStyle() |
| Tabs | Tabs.init(titles) |
No | select(), setDivider() |
| Sparkline | Sparkline.init() |
No | setData(), setMax() |
| Scrollbar | Scrollbar.init(orientation) |
Yes | setSymbols(), setStyle() |
| BarChart | BarChart.init() |
No | setData(), setBarWidth() |
| Canvas | Canvas.init() |
No | setXBounds(), setYBounds(), paint() |
| Chart | Chart.init(datasets) |
No | setXAxis(), setYAxis() |
| Monthly | Monthly.init(date) |
No | showMonthHeader(), showWeekdaysHeader() |
| Clear | Clear.init() |
No | (none) |
Error Handling
zcatui usa el sistema de errores de Zig. Las funciones que pueden fallar retornan !T.
// Errores comunes
const TerminalError = error{
InitFailed,
WriteFailed,
FlushFailed,
};
const BufferError = error{
OutOfMemory,
};
// Manejo tipico
var term = Terminal.init(allocator) catch |err| {
std.debug.print("Failed to init terminal: {}\n", .{err});
return err;
};
defer term.deinit();
Patterns
Builder Pattern
const widget = SomeWidget.init()
.setOption1(value1)
.setOption2(value2)
.setBlock(Block.bordered());
Stateful Rendering
var state = WidgetState.init();
// En el loop de renderizado:
widget.renderStateful(area, buf, &state);
// Actualizar estado basado en input:
state.selectNext(items.len);
Layout Composition
const outer = Layout.vertical(&[_]Constraint{
Constraint.length(3),
Constraint.min(0),
});
var outer_chunks: [2]Rect = undefined;
outer.split(area, &outer_chunks);
const inner = Layout.horizontal(&[_]Constraint{
Constraint.percentage(50),
Constraint.percentage(50),
});
var inner_chunks: [2]Rect = undefined;
inner.split(outer_chunks[1], &inner_chunks);