Immediate Mode GUI library for Zig with software rendering. Core features: - SDL2 backend for cross-platform window/events - Software rasterizer (works everywhere, including SSH) - Macro recording system (cornerstone feature, like Vim) - Command-list rendering (DrawRect, DrawText, etc.) - Layout system with constraints - Color/Style system with themes Project structure: - src/core/: context, command, input, layout, style - src/macro/: MacroRecorder, MacroPlayer, MacroStorage - src/render/: Framebuffer, SoftwareRenderer, Font - src/backend/: Backend interface, SDL2 implementation - examples/: hello.zig, macro_demo.zig - docs/: Architecture, research (Gio, immediate-mode libs, Simifactu) Build: zig build (requires SDL2-devel) Tests: 16 tests passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
81 lines
2.3 KiB
Zig
81 lines
2.3 KiB
Zig
//! Hello World - Basic zCatGui example
|
|
//!
|
|
//! Demonstrates:
|
|
//! - Initializing the backend
|
|
//! - Creating a framebuffer
|
|
//! - Basic rendering
|
|
//! - Event loop
|
|
|
|
const std = @import("std");
|
|
const zcatgui = @import("zcatgui");
|
|
|
|
const Framebuffer = zcatgui.render.Framebuffer;
|
|
const SoftwareRenderer = zcatgui.render.SoftwareRenderer;
|
|
const Sdl2Backend = zcatgui.backend.Sdl2Backend;
|
|
const Color = zcatgui.Color;
|
|
const Command = zcatgui.Command;
|
|
|
|
pub fn main() !void {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
// Initialize backend
|
|
var backend = try Sdl2Backend.init("zCatGui - Hello World", 800, 600);
|
|
defer backend.deinit();
|
|
|
|
// Create framebuffer
|
|
var fb = try Framebuffer.init(allocator, 800, 600);
|
|
defer fb.deinit();
|
|
|
|
// Create renderer
|
|
var renderer = SoftwareRenderer.init(&fb);
|
|
|
|
var running = true;
|
|
var frame: u32 = 0;
|
|
|
|
while (running) {
|
|
// Poll events
|
|
while (backend.pollEvent()) |event| {
|
|
switch (event) {
|
|
.quit => running = false,
|
|
.key => |key| {
|
|
if (key.key == .escape and key.pressed) {
|
|
running = false;
|
|
}
|
|
},
|
|
.resize => |size| {
|
|
try fb.resize(size.width, size.height);
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
// Clear
|
|
renderer.clear(Color.background);
|
|
|
|
// Draw some rectangles
|
|
renderer.execute(Command.rect(50, 50, 200, 100, Color.primary));
|
|
renderer.execute(Command.rect(300, 50, 200, 100, Color.success));
|
|
renderer.execute(Command.rect(550, 50, 200, 100, Color.danger));
|
|
|
|
// Draw rectangle outline
|
|
renderer.execute(Command.rectOutline(50, 200, 700, 100, Color.border));
|
|
|
|
// Animate a rectangle
|
|
const x = @as(i32, @intCast(50 + (frame % 600)));
|
|
renderer.execute(Command.rect(x, 350, 100, 100, Color.warning));
|
|
|
|
// Draw some lines
|
|
renderer.execute(Command.line(50, 500, 750, 500, Color.foreground));
|
|
renderer.execute(Command.line(400, 450, 400, 550, Color.foreground));
|
|
|
|
// Present
|
|
backend.present(&fb);
|
|
|
|
frame += 1;
|
|
|
|
// Cap at ~60 FPS
|
|
std.Thread.sleep(16 * std.time.ns_per_ms);
|
|
}
|
|
}
|