- Reemplazar nota de placeholder con documentación real - Añadir ejemplos de código para cargar y usar TTF - Documentar FontRef para API unificada bitmap/TTF - Listar fuentes recomendadas con rutas en Fedora - Documentar features: AA, cache, alpha blending 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
37 KiB
zcatgui Reference Manual
Version: 0.15.0 Language: Zig 0.15.2 Paradigm: Immediate Mode GUI Lines of Code: ~35,000 across 81 source files
Table of Contents
- Overview
- Installation & Setup
- Quick Start
- Architecture
- Core Modules
- Widgets (37 total)
- Rendering System
- Animation & Effects
- Backend System
- Macro System
- Panel System
- Performance Utilities
- Theme System
- Accessibility
- Gesture Recognition
- API Reference
Overview
zcatgui is an immediate-mode GUI library for Zig featuring:
- Software Rendering: Works on any computer without GPU (SSH compatible)
- Cross-Platform: Linux, Windows, macOS, Web (WASM), Android, iOS
- Macro System: Record and replay user actions (cornerstone feature)
- 37 Widgets: From basic labels to complex tables and charts
- Zero External Dependencies: Only SDL2 for desktop windowing
Design Philosophy
"Maximum compatibility, minimum dependencies, total user control"
- Immediate mode = explicit state, no callbacks, no threading hell
- Software rendering first, GPU optional later
- Macro system integrated from the design phase
- Works on old laptops, new workstations, and via SSH
Installation & Setup
Requirements
- Zig 0.15.2
- SDL2 (for desktop builds only)
Build Commands
# Build library and examples
zig build
# Run tests
zig build test
# Run examples
zig build hello # Hello world
zig build widgets-demo # Widget showcase
zig build table-demo # Table with split panels
zig build macro-demo # Macro recording demo
# Platform-specific builds
zig build wasm # Web (WASM) - outputs to web/
zig build android # Android ARM64
zig build android-x86 # Android x86_64 (emulator)
zig build ios # iOS ARM64 (device)
zig build ios-sim # iOS ARM64 (simulator)
Project Integration
Add to your build.zig.zon:
.dependencies = .{
.zcatgui = .{
.path = "../path/to/zcatgui",
},
},
Quick Start
const zcatgui = @import("zcatgui");
const Context = zcatgui.Context;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// Initialize context
var ctx = try Context.init(allocator);
defer ctx.deinit();
// Main loop
while (ctx.running) {
ctx.beginFrame();
// Draw a button
if (zcatgui.button(&ctx, "Click me!")) {
// Handle click
}
// Draw a label
zcatgui.label(&ctx, "Hello, zcatgui!");
ctx.endFrame();
}
}
Architecture
Layer Diagram
┌─────────────────────────────────────────────────────────────┐
│ Layer 4: Widgets (37 widgets) │
│ Button, Table, Tree, Chart, Modal, etc. │
├─────────────────────────────────────────────────────────────┤
│ Layer 3: Macro System + Panels │
│ MacroRecorder, MacroPlayer, AutonomousPanel, Composites │
├─────────────────────────────────────────────────────────────┤
│ Layer 2: Core UI │
│ Context, Layout, Style, Input, Command, DragDrop, Gesture │
├─────────────────────────────────────────────────────────────┤
│ Layer 1: Rendering │
│ SoftwareRenderer, Framebuffer, Font, TTF, Animation │
├─────────────────────────────────────────────────────────────┤
│ Layer 0: Backends │
│ SDL2, WASM, Android, iOS │
└─────────────────────────────────────────────────────────────┘
File Structure
zcatgui/
├── src/
│ ├── zcatgui.zig # Main entry point, re-exports
│ ├── core/ # Core modules
│ │ ├── context.zig # UI context, frame management
│ │ ├── layout.zig # Rect, Constraint, Layout
│ │ ├── style.zig # Color, Theme, ThemeManager
│ │ ├── input.zig # Key, Mouse, InputState
│ │ ├── command.zig # DrawCommand list
│ │ ├── clipboard.zig # Clipboard operations
│ │ ├── dragdrop.zig # Drag and drop system
│ │ ├── shortcuts.zig # Keyboard shortcuts
│ │ ├── focus_group.zig # Focus navigation
│ │ ├── accessibility.zig # A11y roles and states
│ │ └── gesture.zig # Touch gesture recognition
│ ├── widgets/ # 37 widgets
│ │ └── widgets.zig # Re-exports all widgets
│ ├── render/ # Rendering
│ │ ├── framebuffer.zig # Pixel buffer
│ │ ├── software.zig # Software rasterizer
│ │ ├── font.zig # Bitmap fonts
│ │ ├── ttf.zig # TrueType fonts
│ │ ├── animation.zig # Easing, Animation, Spring
│ │ ├── effects.zig # Shadow, Gradient, Blur
│ │ └── antialiasing.zig # AA drawing functions
│ ├── backend/ # Platform backends
│ │ ├── backend.zig # Abstract interface
│ │ ├── sdl2.zig # SDL2 (desktop)
│ │ ├── wasm.zig # WebAssembly
│ │ ├── android.zig # Android NDK
│ │ └── ios.zig # iOS UIKit
│ ├── macro/ # Macro system
│ │ └── macro.zig # Recorder, Player, Storage
│ ├── panels/ # Panel architecture
│ │ ├── panels.zig # Re-exports
│ │ ├── panel.zig # AutonomousPanel
│ │ ├── composite.zig # Composite patterns
│ │ └── data_manager.zig # Observer pattern
│ └── utils/ # Utilities
│ ├── utils.zig # Re-exports
│ ├── arena.zig # FrameArena, ScopedArena
│ ├── pool.zig # ObjectPool, CommandPool
│ ├── benchmark.zig # Timer, Benchmark
│ └── testing.zig # TestRunner, Assertions
├── examples/ # Example applications
├── web/ # WASM build output
├── ios/ # iOS bridge files
└── docs/ # Documentation
Core Modules
Context (src/core/context.zig)
The UI context manages frame lifecycle, command queue, and dirty regions.
const Context = zcatgui.Context;
// Create context
var ctx = try Context.init(allocator);
defer ctx.deinit();
// Frame lifecycle
ctx.beginFrame();
// ... draw widgets ...
ctx.endFrame();
// Access components
ctx.layout // LayoutState
ctx.input // InputState
ctx.commands // Command list
ctx.frame_arena // Per-frame allocator
// Dirty rectangle tracking
ctx.invalidateRect(rect);
ctx.needsRedraw();
ctx.getDirtyRects();
Key Types:
Context- Main UI contextFrameStats- Performance statistics
Layout (src/core/layout.zig)
Rectangle and constraint-based layout system.
const Layout = zcatgui.Layout;
const Rect = zcatgui.Rect;
const Constraint = zcatgui.Constraint;
// Create rectangles
const rect = Rect.init(x, y, width, height);
// Rectangle operations
rect.contains(point_x, point_y)
rect.intersection(other_rect)
rect.union_rect(other_rect)
rect.isEmpty()
rect.left(), rect.right(), rect.top(), rect.bottom()
rect.center()
rect.inset(amount)
rect.expand(amount)
// Constraints
const constraint = Constraint{
.min_width = 100,
.max_width = 500,
.min_height = 50,
.max_height = 200,
};
Style (src/core/style.zig)
Colors and theming system.
const Style = zcatgui.Style;
const Color = zcatgui.Color;
const Theme = zcatgui.Theme;
// Color creation
const red = Color.rgb(255, 0, 0);
const semi_transparent = Color.rgba(255, 0, 0, 128);
// Predefined colors
Color.white, Color.black, Color.red, Color.green, Color.blue
Color.primary, Color.secondary, Color.success, Color.warning, Color.danger
// Color operations
color.toABGR() // For framebuffer
color.blend(background) // Alpha blending
color.withAlpha(128) // New alpha value
// Theme system
const theme = zcatgui.currentTheme();
theme.colors.primary
theme.colors.background
theme.colors.text
Built-in Themes: dark, light, high_contrast, nord, dracula
Input (src/core/input.zig)
Keyboard and mouse input handling.
const Input = zcatgui.Input;
const InputState = Input.InputState;
// In your frame loop
const input = &ctx.input;
// Keyboard
input.keyPressed(.enter) // Just pressed this frame
input.keyReleased(.escape) // Just released
input.keyDown(.left_shift) // Currently held
input.getChar() // Text input character
// Mouse
input.mousePressed(.left)
input.mouseReleased(.right)
input.mouseDown(.middle)
const pos = input.mousePos(); // {x, y}
input.mouseDelta() // Movement since last frame
input.scrollDelta() // Scroll wheel
// Modifiers
input.keyDown(.left_ctrl) or input.keyDown(.right_ctrl)
input.keyDown(.left_shift) or input.keyDown(.right_shift)
input.keyDown(.left_alt) or input.keyDown(.right_alt)
Key Enum: 130+ keys including .a-.z, .F1-.F12, .space, .enter, .tab, .escape, arrow keys, numpad, etc.
Command (src/core/command.zig)
Draw command system for batched rendering.
const Command = zcatgui.Command;
// Command types
Command.rect(x, y, w, h, color) // Filled rectangle
Command.rectOutline(x, y, w, h, color, thickness)
Command.text(x, y, text, color, font) // Text rendering
Command.line(x1, y1, x2, y2, color) // Line
Command.clip(x, y, w, h) // Push clip region
Command.clipEnd() // Pop clip region
Command.nop // No operation
Widgets
Basic Widgets
| Widget | File | Description |
|---|---|---|
| Label | widgets/label.zig |
Static text display |
| Button | widgets/button.zig |
Clickable button with importance levels |
| TextInput | widgets/text_input.zig |
Single-line text entry |
| TextArea | widgets/textarea.zig |
Multi-line text editor |
| Checkbox | widgets/checkbox.zig |
Boolean toggle |
| Radio | widgets/radio.zig |
Radio button groups |
| Switch | widgets/switch.zig |
Toggle switch (iOS style) |
Selection Widgets
| Widget | File | Description |
|---|---|---|
| Select | widgets/select.zig |
Dropdown selection |
| List | widgets/list.zig |
Scrollable list |
| AutoComplete | widgets/autocomplete.zig |
ComboBox with filtering |
| Menu | widgets/menu.zig |
Dropdown/context menus |
| Tabs | widgets/tabs.zig |
Tab container |
Data Widgets
| Widget | File | Description |
|---|---|---|
| Table | widgets/table.zig |
Editable data table with sorting |
| Tree | widgets/tree.zig |
Hierarchical tree view |
| VirtualScroll | widgets/virtual_scroll.zig |
Virtualized list for large data |
Input Widgets
| Widget | File | Description |
|---|---|---|
| Slider | widgets/slider.zig |
Range selection |
| NumberEntry | widgets/numberentry.zig |
Numeric input with validation |
| ColorPicker | widgets/colorpicker.zig |
Color selection |
| DatePicker | widgets/datepicker.zig |
Date selection calendar |
Feedback Widgets
| Widget | File | Description |
|---|---|---|
| Progress | widgets/progress.zig |
Bar, circle, and spinner |
| Tooltip | widgets/tooltip.zig |
Hover tooltips |
| Toast | widgets/toast.zig |
Non-blocking notifications |
| Badge | widgets/badge.zig |
Status labels and tags |
| Loader | widgets/loader.zig |
Loading indicators |
Layout Widgets
| Widget | File | Description |
|---|---|---|
| Split | widgets/split.zig |
HSplit/VSplit draggable panels |
| Panel | widgets/panel.zig |
Container with title bar |
| Modal | widgets/modal.zig |
Dialog windows |
| ScrollArea | widgets/scroll.zig |
Scrollable content region |
| Surface | widgets/surface.zig |
Elevated container (material) |
| Grid | widgets/grid.zig |
Grid layout container |
| Resize | widgets/resize.zig |
Resizable container |
| Divider | widgets/divider.zig |
Visual separator |
Navigation Widgets
| Widget | File | Description |
|---|---|---|
| AppBar | widgets/appbar.zig |
Top app bar |
| NavDrawer | widgets/navdrawer.zig |
Side navigation |
| Sheet | widgets/sheet.zig |
Bottom/side sheets |
| Breadcrumb | widgets/breadcrumb.zig |
Navigation breadcrumbs |
| Discloser | widgets/discloser.zig |
Expand/collapse sections |
Visual Widgets
| Widget | File | Description |
|---|---|---|
| Icon | widgets/icon.zig |
Icon display |
| IconButton | widgets/iconbutton.zig |
Icon-only button |
| Image | widgets/image.zig |
Image display with caching |
| RichText | widgets/richtext.zig |
Styled text spans |
| Canvas | widgets/canvas.zig |
Custom drawing area |
| Chart | widgets/chart.zig |
Line, bar, pie charts |
Interactive Widgets
| Widget | File | Description |
|---|---|---|
| Reorderable | widgets/reorderable.zig |
Drag-to-reorder list |
| Selectable | widgets/selectable.zig |
Selection container |
Widget Usage Examples
Button
// Simple button
if (zcatgui.button(&ctx, "Click me")) {
// Handle click
}
// Button with importance
if (zcatgui.buttonPrimary(&ctx, "Submit")) { ... }
if (zcatgui.buttonDanger(&ctx, "Delete")) { ... }
// Full configuration
const result = zcatgui.widgets.button.buttonEx(&ctx, "Save", .{
.importance = .primary,
.disabled = false,
.width = 120,
}, .{});
TextInput
var text_state = zcatgui.TextInputState{};
var buffer: [256]u8 = undefined;
const result = zcatgui.textInput(&ctx, &text_state, &buffer);
if (result.changed) {
// Text was modified
}
if (result.submitted) {
// Enter was pressed
}
Table
const columns = [_]zcatgui.widgets.Column{
.{ .name = "Name", .width = 150 },
.{ .name = "Age", .width = 80, .column_type = .number },
.{ .name = "Email", .width = 200 },
};
var table_state = zcatgui.widgets.TableState{};
const result = zcatgui.widgets.table.table(
&ctx,
&table_state,
&columns,
data,
row_count,
);
if (result.cell_edited) {
// Handle cell edit
}
if (result.row_added) {
// Add new row
}
Slider
var slider_state = zcatgui.widgets.SliderState{};
slider_state.setValue(50, 0, 100); // Initial value
const result = zcatgui.widgets.slider.slider(&ctx, &slider_state);
if (result.changed) {
const value = slider_state.getValueInt(0, 100);
}
Modal
var show_modal = false;
if (zcatgui.button(&ctx, "Open Modal")) {
show_modal = true;
}
if (show_modal) {
const result = zcatgui.widgets.modal.alert(
&ctx,
"Confirmation",
"Are you sure?",
);
if (result.confirmed or result.cancelled) {
show_modal = false;
}
}
Rendering System
Framebuffer (src/render/framebuffer.zig)
Software pixel buffer.
const Framebuffer = zcatgui.render.Framebuffer;
var fb = try Framebuffer.init(allocator, 800, 600);
defer fb.deinit();
// Operations
fb.clear(Color.black);
fb.setPixel(x, y, color);
fb.getPixel(x, y);
fb.fillRect(x, y, w, h, color);
fb.drawRect(x, y, w, h, color); // Outline
fb.drawLine(x1, y1, x2, y2, color);
fb.drawHLine(x, y, w, color);
fb.drawVLine(x, y, h, color);
// For blitting to backend
fb.getData() // []const u32
fb.getPitch() // Bytes per row
SoftwareRenderer (src/render/software.zig)
Executes draw commands on framebuffer.
const SoftwareRenderer = zcatgui.render.SoftwareRenderer;
var renderer = SoftwareRenderer.init(&framebuffer);
renderer.setDefaultFont(&font);
// Execute commands
renderer.execute(command);
renderer.executeAll(command_slice);
// Clipping
renderer.getClip(); // Current clip rect
Font System
zcatgui provides multiple bitmap fonts for different use cases:
| Font | Size | Characters | Use Case |
|---|---|---|---|
default_font |
8x8 | ASCII | Compact displays, terminals |
default_font_latin1 |
8x8 | Latin-1 | Compact + Spanish/European |
font_8x16 |
8x16 | ASCII | Standard UI, good readability |
font_8x16_latin1 |
8x16 | Latin-1 | Standard UI + Spanish/European |
Latin-1 Character Support: ñ, Ñ, á, é, í, ó, ú, ü, ¿, ¡, ç, ß, and all Western European accented characters.
Bitmap Fonts (src/render/font.zig):
const render = zcatgui.render;
// Direct font access
const font = render.default_font_latin1; // 8x8 with Spanish support
const font_tall = render.font_8x16_latin1; // 8x16 taller, more readable
// Font by size category (recommended)
const small_font = render.getFontForSize(.small); // 8x8 Latin-1
const medium_font = render.getFontForSize(.medium); // 8x16 Latin-1
// ASCII-only variants (smaller memory)
const ascii_font = render.getFontForSizeAscii(.medium);
// Font operations
font.charWidth() // 8
font.charHeight() // 8 or 16
font.textWidth("Hello") // width in pixels
font.drawChar(fb, x, y, 'A', color, clip);
font.drawText(fb, x, y, "¿Hola, España!", color, clip);
Setting Default Font in Renderer:
var renderer = SoftwareRenderer.init(&framebuffer);
// Use 8x16 Latin-1 for better readability and Spanish support
renderer.setDefaultFont(@constCast(&render.font_8x16_latin1));
TrueType Fonts (src/render/ttf.zig):
TTF fonts are fully supported with real glyph rasterization and antialiasing.
const TtfFont = zcatgui.render.TtfFont;
const FontRef = zcatgui.render.FontRef;
// Load TTF from file
var ttf = try TtfFont.loadFromFile(allocator, "/usr/share/fonts/adwaita-sans-fonts/AdwaitaSans-Regular.ttf");
defer ttf.deinit();
// Set render size (glyphs cached per size)
ttf.setSize(14); // 14pt
// Get metrics
const metrics = ttf.getGlyphMetrics('A');
const width = ttf.textWidth("Hello");
const height = ttf.lineHeight();
const asc = ttf.ascent(); // Baseline to top
const desc = ttf.descent(); // Baseline to bottom
// Draw text directly
ttf.drawText(&framebuffer, x, y, "¡Hola, España!", color, clip);
// Or use FontRef for unified bitmap/TTF API
const font_ref = FontRef{ .ttf = &ttf };
font_ref.drawText(&framebuffer, x, y, "Hello", color, clip);
Features:
- Real bezier curve rasterization (quadratic beziers)
- Antialiasing via 2x supersampling
- Glyph caching (by codepoint + size)
- Alpha blending for smooth edges
- Full Unicode support (BMP via cmap format 4, full via format 12)
Recommended fonts (free, good Latin-1 support):
| Font | Location (Fedora) | Notes |
|---|---|---|
| AdwaitaSans | /usr/share/fonts/adwaita-sans-fonts/ |
GNOME default, modern |
| DejaVu Sans | /usr/share/fonts/dejavu-sans-fonts/ |
Very complete |
| Droid Sans | /usr/share/fonts/google-droid-sans-fonts/ |
Android default |
Switching between Bitmap and TTF:
// Use FontRef to switch fonts without changing widget code
var current_font: FontRef = .{ .bitmap = &render.font_8x16_latin1 };
// Later, switch to TTF
current_font = .{ .ttf = &ttf_font };
// Widget code stays the same
current_font.drawText(fb, x, y, text, color, clip);
UTF-8 Text Rendering
The software renderer automatically decodes UTF-8 text and maps codepoints to the font's character set.
How it works:
- Text strings (from SQLite, files, user input) are typically UTF-8 encoded
- The renderer decodes UTF-8 sequences (1-4 bytes per character)
- Codepoints are mapped to Latin-1 (0x00-0xFF) for bitmap font rendering
- Characters outside Latin-1 are displayed as '?' (placeholder)
Supported characters with Latin-1 fonts:
- ASCII (0x00-0x7F): A-Z, a-z, 0-9, punctuation
- Latin-1 Supplement (0x80-0xFF): ñ, Ñ, á, é, í, ó, ú, ü, ¿, ¡, ç, ß, €, £, ¥, etc.
Example - Spanish text:
// UTF-8 string from database or source code
const text = "¿Cómo está? Número: 100€";
// Renderer automatically handles UTF-8 decoding
ctx.pushCommand(Command.text(x, y, text, color));
// Displays correctly: ¿Cómo está? Número: 100€
Why UTF-8 in databases/files?
- SQLite uses UTF-8 natively
- Universal standard (Linux, Web, JSON, APIs)
- ASCII-compatible (English text = same size)
- Overhead minimal: "Razón Social" = 2 extra bytes vs pure ASCII
Animation & Effects
Animation (src/render/animation.zig)
Easing Functions:
const Easing = zcatgui.Easing;
Easing.linear(t)
Easing.easeInQuad(t), Easing.easeOutQuad(t), Easing.easeInOutQuad(t)
Easing.easeInCubic(t), Easing.easeOutCubic(t), Easing.easeInOutCubic(t)
Easing.easeInSine(t), Easing.easeOutSine(t), Easing.easeInOutSine(t)
Easing.easeInExpo(t), Easing.easeOutExpo(t), Easing.easeInOutExpo(t)
Easing.easeInElastic(t), Easing.easeOutElastic(t)
Easing.easeInBounce(t), Easing.easeOutBounce(t), Easing.easeInOutBounce(t)
Easing.easeInBack(t), Easing.easeOutBack(t), Easing.easeInOutBack(t)
Animation:
const Animation = zcatgui.Animation;
var anim = Animation.create(0, 100, 1000, Easing.easeOutCubic);
anim.start(current_time_ms);
// In frame loop
const value = anim.getValue(current_time_ms);
if (anim.isComplete(current_time_ms)) {
// Animation done
}
Spring Physics:
const Spring = zcatgui.Spring;
const SpringConfig = zcatgui.SpringConfig;
var spring = Spring.create(0, 100, .{
.stiffness = 100,
.damping = 10,
.mass = 1,
});
// Each frame
spring.update(dt_seconds);
const value = spring.getValue();
if (spring.isSettled()) { ... }
AnimationManager:
const AnimationManager = zcatgui.AnimationManager;
var manager = AnimationManager.init();
manager.startAnimation(id, animation, time);
manager.getValue(id, time);
manager.stopAnimation(id);
manager.update(time);
Effects (src/render/effects.zig)
Shadows:
const Shadow = zcatgui.Shadow;
const shadow = Shadow{
.offset_x = 4,
.offset_y = 4,
.blur_radius = 8,
.color = Color.rgba(0, 0, 0, 128),
};
// Presets
Shadow.soft() // Subtle shadow
Shadow.hard() // No blur
Shadow.drop(offset, blur)
zcatgui.applyShadow(framebuffer, rect, shadow);
Gradients:
const Gradient = zcatgui.Gradient;
// Types
Gradient.horizontal(start_color, end_color)
Gradient.vertical(start_color, end_color)
Gradient.diagonal(start_color, end_color)
const gradient = Gradient{
.start_color = Color.rgb(255, 0, 0),
.end_color = Color.rgb(0, 0, 255),
.direction = .radial,
};
zcatgui.applyGradient(framebuffer, rect, gradient);
Color Operations:
zcatgui.interpolateColor(color_a, color_b, t) // Blend
zcatgui.applyOpacity(color, 0.5) // Reduce alpha
zcatgui.highlight(color, 50) // Lighter
zcatgui.lowlight(color, 50) // Darker
zcatgui.applyBlur(framebuffer, rect, radius) // Box blur
Anti-aliasing (src/render/antialiasing.zig)
const AA = zcatgui.render.antialiasing;
// Quality levels
AAQuality.none, AAQuality.low, AAQuality.medium, AAQuality.high
// AA drawing functions
zcatgui.drawLineAA(fb, x1, y1, x2, y2, color, quality)
zcatgui.drawCircleAA(fb, cx, cy, radius, color, quality)
zcatgui.drawRoundedRectAA(fb, rect, corner_radius, color, quality)
zcatgui.drawEllipseAA(fb, rect, color, quality)
zcatgui.drawPolygonAA(fb, points, color, quality)
Backend System
Abstract Interface (src/backend/backend.zig)
const Backend = zcatgui.backend.Backend;
// VTable interface
backend.pollEvent() // ?Event
backend.present(fb) // Display framebuffer
backend.getSize() // {width, height}
backend.deinit() // Cleanup
Event Types:
const Event = zcatgui.backend.Event;
event.key // KeyEvent
event.mouse // MouseEvent
event.resize // {width, height}
event.quit // Window close
event.text_input // Unicode text
SDL2 Backend (src/backend/sdl2.zig)
Desktop platform (Linux, Windows, macOS).
const Sdl2Backend = zcatgui.backend.Sdl2Backend;
var backend = try Sdl2Backend.init(allocator, "My App", 800, 600);
defer backend.deinit();
while (backend.pollEvent()) |event| {
// Handle events
}
backend.present(&framebuffer);
WASM Backend (src/backend/wasm.zig)
Browser via WebAssembly.
// Zig side - exports these functions
export fn wasm_main() void { ... }
export fn wasm_frame(time: f64) void { ... }
export fn wasm_resize(w: u32, h: u32) void { ... }
export fn wasm_mouse_move(x: i32, y: i32) void { ... }
export fn wasm_mouse_button(btn: u8, down: bool) void { ... }
export fn wasm_key_event(code: u16, down: bool) void { ... }
JavaScript integration (web/zcatgui.js):
// Load and initialize
const app = await ZcatguiApp.create('canvas-id', 'zcatgui-demo.wasm');
app.start();
Build: zig build wasm outputs to web/zcatgui-demo.wasm (18KB)
Android Backend (src/backend/android.zig)
Native Android via NDK.
// Exports for JNI
export fn ANativeActivity_onCreate(...) void { ... }
// Logging
zcatgui.backend.android.log("Message: {}", .{value});
Build: zig build android (ARM64), zig build android-x86 (emulator)
iOS Backend (src/backend/ios.zig)
iOS via UIKit bridge.
Zig side:
// Extern C functions called from Objective-C
extern fn ios_view_init(w: u32, h: u32) void;
extern fn ios_view_present(pixels: [*]const u32, w: u32, h: u32) void;
extern fn ios_poll_event(buffer: [*]u8) u32;
Objective-C bridge (ios/ZcatguiBridge.m):
ZcatguiView: UIView subclass for framebuffer displayZcatguiViewController: View controller with CADisplayLink render loop- Touch event handling with queue
Build: zig build ios (device), zig build ios-sim (simulator)
Macro System
The macro system is a cornerstone feature of zcatgui, enabling recording and playback of user actions.
Design Philosophy
Raw keys, not semantic commands:
- Simpler (less code = less bugs)
- Like Vim (proven to work)
- Minimal memory usage
MacroRecorder (src/macro/macro.zig)
const MacroRecorder = zcatgui.MacroRecorder;
var recorder = MacroRecorder.init(allocator);
defer recorder.deinit();
// Record
recorder.start();
recorder.record(key_event); // Call for each key event
const events = recorder.stop();
// Save/Load
try recorder.save("macro.zcm");
try recorder.load("macro.zcm");
// Convert to named macro
const macro = try recorder.toMacro("my_macro");
MacroPlayer
const MacroPlayer = zcatgui.MacroPlayer;
// Injection function
fn injectKey(event: KeyEvent) void {
// Add to input queue
}
// Playback
MacroPlayer.play(events, injectKey);
MacroPlayer.playWithDelay(events, injectKey, 50); // 50ms between keys
MacroStorage
const MacroStorage = zcatgui.macro.MacroStorage;
var storage = MacroStorage.init(allocator);
defer storage.deinit();
try storage.store(macro);
const m = storage.get("macro_name");
storage.remove("macro_name");
const names = storage.list();
File Format
ZCATGUI_MACRO_V1
<key_code>,<modifiers>,<char>,<pressed>
...
Panel System
Lego-style composable panels based on Simifactu architecture.
AutonomousPanel (src/panels/panel.zig)
Self-contained UI component with own state, UI, and logic.
const AutonomousPanel = zcatgui.panels.AutonomousPanel;
const panel = AutonomousPanel{
.id = "my_panel",
.panel_type = .content,
.state = .active,
.build_fn = myBuildFn,
.refresh_fn = myRefreshFn,
.data_change_fn = myDataChangeFn,
};
Composite Patterns (src/panels/composite.zig)
const composite = zcatgui.panels.composite;
// Vertical stack
var vstack = composite.VerticalComposite.init();
vstack.add(panel1);
vstack.add(panel2);
// Horizontal stack
var hstack = composite.HorizontalComposite.init();
// Split with draggable divider
var split = composite.SplitComposite.init(.horizontal, 0.5);
// Tabs
var tabs = composite.TabComposite.init();
tabs.addTab("Tab 1", panel1);
tabs.addTab("Tab 2", panel2);
// Grid layout
var grid = composite.GridComposite.init(3, 2); // 3 columns, 2 rows
DataManager (src/panels/data_manager.zig)
Observer pattern for panel communication.
const DataManager = zcatgui.panels.DataManager;
var manager = DataManager.init(allocator);
defer manager.deinit();
// Subscribe to changes
manager.subscribe("data_key", myCallback);
// Notify changes
manager.notify(.{
.key = "data_key",
.change_type = .modified,
.data = payload,
});
// Global access
zcatgui.panels.setDataManager(&manager);
const dm = zcatgui.panels.getDataManager();
Performance Utilities
FrameArena (src/utils/arena.zig)
Per-frame allocator with O(1) reset.
const FrameArena = zcatgui.FrameArena;
var arena = try FrameArena.init(allocator, 1024 * 1024);
defer arena.deinit();
// Each frame
arena.reset();
const temp = arena.alloc(u8, 100);
// No need to free - reset clears everything
ObjectPool (src/utils/pool.zig)
Generic object pool for frequently reused objects.
const ObjectPool = zcatgui.ObjectPool;
var pool = ObjectPool(MyType).init(allocator, 100);
defer pool.deinit();
const obj = pool.acquire();
// Use obj...
pool.release(obj);
CommandPool
Specialized pool for draw commands.
const CommandPool = zcatgui.CommandPool;
var pool = CommandPool.init(allocator);
defer pool.deinit();
const cmd = pool.acquire();
pool.releaseAll();
Benchmark (src/utils/benchmark.zig)
const Timer = zcatgui.Timer;
const Benchmark = zcatgui.Benchmark;
const FrameTimer = zcatgui.FrameTimer;
// Simple timing
var timer = Timer.start();
// ... work ...
const elapsed_ns = timer.elapsed();
// Statistics
var bench = Benchmark.init();
for (iterations) |_| {
timer = Timer.start();
// ... work ...
bench.addSample(timer.elapsed());
}
bench.average();
bench.min();
bench.max();
bench.stddev();
bench.median();
// Frame timing
var frame_timer = FrameTimer.init();
// Each frame
frame_timer.tick();
frame_timer.fps();
frame_timer.frameTimeMs();
AllocationTracker
const AllocationTracker = zcatgui.AllocationTracker;
var tracker = AllocationTracker.init();
tracker.trackAlloc(size);
tracker.trackFree(size);
tracker.currentBytes();
tracker.peakBytes();
tracker.totalAllocations();
Theme System
Built-in Themes
const ThemeManager = zcatgui.ThemeManager;
// Get global manager
var manager = zcatgui.getThemeManager();
// Switch themes
manager.setTheme(.dark);
manager.setTheme(.light);
manager.setTheme(.high_contrast);
manager.setTheme(.nord);
manager.setTheme(.dracula);
// Get current theme
const theme = zcatgui.currentTheme();
theme.colors.primary
theme.colors.secondary
theme.colors.background
theme.colors.surface
theme.colors.text
theme.colors.text_secondary
theme.colors.border
theme.colors.success
theme.colors.warning
theme.colors.danger
Custom Themes
const custom_theme = Theme{
.name = "custom",
.colors = .{
.primary = Color.rgb(100, 150, 200),
.background = Color.rgb(30, 30, 30),
// ... etc
},
};
manager.registerTheme("custom", custom_theme);
Accessibility
Roles (src/core/accessibility.zig)
const A11yRole = zcatgui.A11yRole;
A11yRole.button
A11yRole.checkbox
A11yRole.radio
A11yRole.textbox
A11yRole.slider
A11yRole.progressbar
A11yRole.listbox
A11yRole.tree
A11yRole.table
A11yRole.dialog
// ... and more
States
const A11yState = zcatgui.A11yState;
const state = A11yState{
.disabled = false,
.focused = true,
.selected = false,
.checked = true,
.expanded = false,
};
Info
const A11yInfo = zcatgui.A11yInfo;
// Create info for widgets
A11yInfo.button("Submit")
A11yInfo.checkbox("Accept terms", is_checked)
A11yInfo.slider("Volume", 0, 100, current_value)
A11yInfo.progressBar("Loading", progress)
A11yInfo.listItem("Item 1", position, total)
// Announce for screen readers
var buf: [256]u8 = undefined;
const announcement = info.announce(&buf);
Manager
const A11yManager = zcatgui.A11yManager;
var manager = A11yManager.init(allocator);
defer manager.deinit();
manager.register(widget_id, a11y_info);
manager.setFocus(widget_id);
manager.queueAnnouncement("File saved", .polite);
Gesture Recognition
GestureRecognizer (src/core/gesture.zig)
const GestureRecognizer = zcatgui.GestureRecognizer;
const GestureType = zcatgui.GestureType;
var recognizer = GestureRecognizer.init(.{
.double_tap_time_ms = 300,
.long_press_time_ms = 500,
.swipe_min_distance = 50,
.swipe_min_velocity = 200,
});
// Each frame
const result = recognizer.update(&input_state, current_time_ms);
// Check gestures
if (recognizer.detected(.tap)) { ... }
if (recognizer.detected(.double_tap)) { ... }
if (recognizer.detected(.long_press)) { ... }
if (recognizer.detected(.swipe_left)) { ... }
if (recognizer.isDragging()) {
const delta = recognizer.dragDelta();
}
Gesture Types:
.tap- Single tap.double_tap- Double tap.long_press- Press and hold.drag- Press and move.swipe_left,.swipe_right,.swipe_up,.swipe_down.pinch,.rotate(future touch support)
API Reference
Main Module Exports (src/zcatgui.zig)
// Core
pub const Context
pub const Layout
pub const Style
pub const Input
pub const Command
pub const clipboard
pub const dragdrop
pub const shortcuts
pub const focus_group
pub const accessibility
pub const gesture
// Rendering
pub const render.Framebuffer
pub const render.SoftwareRenderer
pub const render.Font
pub const render.TtfFont
pub const render.animation
pub const render.effects
pub const render.antialiasing
// Animation (re-exports)
pub const Animation
pub const AnimationManager
pub const Easing
pub const Spring
pub const SpringConfig
pub const lerp
pub const lerpInt
// Effects (re-exports)
pub const Shadow
pub const Gradient
pub const applyShadow
pub const applyGradient
pub const applyBlur
pub const interpolateColor
pub const highlight
pub const lowlight
// Backend
pub const backend.Backend
pub const backend.Sdl2Backend // Desktop only
pub const backend.wasm // WASM only
pub const backend.android // Android only
pub const backend.ios // iOS only
// Macro
pub const macro
pub const MacroRecorder
pub const MacroPlayer
pub const KeyEvent
// Widgets (37 total)
pub const widgets
// Widget shortcuts
pub const label, labelEx, labelColored, labelCentered
pub const button, buttonEx, buttonPrimary, buttonDanger
pub const textInput, textInputEx, TextInputState
pub const checkbox, checkboxEx
pub const select, selectEx, SelectState
pub const list, listEx, ListState
pub const FocusManager, FocusRing
// Panels
pub const panels
// Utils
pub const utils
pub const FrameArena
pub const ScopedArena
pub const ObjectPool
pub const CommandPool
pub const RingBuffer
pub const Benchmark
pub const Timer
pub const FrameTimer
pub const AllocationTracker
// Types
pub const Color
pub const Rect
pub const Constraint
pub const Theme
pub const ThemeManager
Version History
| Version | Date | Changes |
|---|---|---|
| 0.15.0 | 2025-12-09 | Mobile/Web backends (WASM, Android, iOS) |
| 0.14.1 | 2025-12-09 | Gio parity phase - 12 widgets + gesture system |
| 0.1.0 | 2025-12-09 | Initial release - core + macros + basic widgets |
License
MIT License
Related Projects
- zcatui: TUI library for terminal interfaces (sibling project)
- microui: Inspiration for minimal architecture
- DVUI: Reference Zig immediate-mode implementation
- Gio: Go immediate-mode GUI (feature parity target)
Generated: 2025-12-09