New modules (13): - src/resize.zig: SIGWINCH terminal resize detection - src/drag.zig: Mouse drag state and Splitter panels - src/diagnostic.zig: Elm-style error messages with code snippets - src/debug.zig: Debug overlay (FPS, timing, widget count) - src/profile.zig: Performance profiling with scoped timers - src/sixel.zig: Sixel graphics encoding for terminal images - src/async_loop.zig: epoll-based async event loop with timers - src/compose.zig: Widget composition utilities - src/shortcuts.zig: Keyboard shortcut registry - src/widgets/logo.zig: ASCII art logo widget Enhanced modules: - src/layout.zig: Added Constraint.ratio(num, denom) - src/terminal.zig: Integrated resize handling - src/root.zig: Re-exports all new modules New examples (9): - resize_demo, splitter_demo, dirtree_demo - help_demo, markdown_demo, progress_demo - spinner_demo, syntax_demo, viewport_demo Package manager: - build.zig.zon: Zig package manager support Stats: 60+ source files, 186+ tests, 20 executables 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
159 lines
5 KiB
Zig
159 lines
5 KiB
Zig
//! Terminal Resize Demo
|
|
//!
|
|
//! Demonstrates automatic terminal resize handling.
|
|
//! Try resizing your terminal window to see the app adapt.
|
|
//!
|
|
//! Run with: zig build resize-demo
|
|
|
|
const std = @import("std");
|
|
const zcatui = @import("zcatui");
|
|
|
|
const Terminal = zcatui.Terminal;
|
|
const Rect = zcatui.Rect;
|
|
const Buffer = zcatui.Buffer;
|
|
const Style = zcatui.Style;
|
|
const Color = zcatui.Color;
|
|
const Block = zcatui.widgets.Block;
|
|
const Borders = zcatui.widgets.Borders;
|
|
|
|
/// State for the demo
|
|
const State = struct {
|
|
resize_count: u32 = 0,
|
|
last_width: u16 = 0,
|
|
last_height: u16 = 0,
|
|
running: bool = true,
|
|
};
|
|
|
|
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 automatic resize detection
|
|
term.enableAutoResize();
|
|
|
|
// Get initial size
|
|
const initial_size = term.getSize();
|
|
var state = State{
|
|
.last_width = initial_size.width,
|
|
.last_height = initial_size.height,
|
|
};
|
|
|
|
while (state.running) {
|
|
// Check if size changed
|
|
const current_area = term.area();
|
|
if (current_area.width != state.last_width or current_area.height != state.last_height) {
|
|
state.resize_count += 1;
|
|
state.last_width = current_area.width;
|
|
state.last_height = current_area.height;
|
|
}
|
|
|
|
try term.drawWithContext(&state, render);
|
|
|
|
if (try term.pollEvent(100)) |event| {
|
|
switch (event) {
|
|
.key => |key| {
|
|
switch (key.code) {
|
|
.char => |c| {
|
|
if (c == 'q') state.running = false;
|
|
},
|
|
.esc => state.running = false,
|
|
else => {},
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn render(state: *State, area: Rect, buf: *Buffer) void {
|
|
// Main border
|
|
const block = Block.init()
|
|
.title(" Resize Demo ")
|
|
.setBorders(Borders.all)
|
|
.borderStyle((Style{}).fg(Color.cyan));
|
|
block.render(area, buf);
|
|
|
|
// Content
|
|
const content_start_y: u16 = 3;
|
|
const content_x: u16 = 4;
|
|
|
|
// Title
|
|
_ = buf.setString(
|
|
content_x,
|
|
content_start_y,
|
|
"Resize your terminal to see this demo adapt!",
|
|
(Style{}).fg(Color.yellow).bold(),
|
|
);
|
|
|
|
// Current size
|
|
var size_buf: [64]u8 = undefined;
|
|
const size_str = std.fmt.bufPrint(&size_buf, "Current size: {d} x {d}", .{
|
|
area.width,
|
|
area.height,
|
|
}) catch "?";
|
|
_ = buf.setString(content_x, content_start_y + 2, size_str, (Style{}).fg(Color.white));
|
|
|
|
// Resize count
|
|
var count_buf: [64]u8 = undefined;
|
|
const count_str = std.fmt.bufPrint(&count_buf, "Resize events: {d}", .{state.resize_count}) catch "?";
|
|
_ = buf.setString(content_x, content_start_y + 3, count_str, (Style{}).fg(Color.white));
|
|
|
|
// Draw a visual indicator based on size
|
|
const indicator_y = content_start_y + 6;
|
|
_ = buf.setString(content_x, indicator_y, "Size indicator:", (Style{}).fg(Color.magenta));
|
|
|
|
// Draw a bar that scales with width
|
|
const bar_width = @min(area.width -| 10, 50);
|
|
var i: u16 = 0;
|
|
while (i < bar_width) : (i += 1) {
|
|
const progress = @as(f32, @floatFromInt(i)) / @as(f32, @floatFromInt(bar_width));
|
|
const color = if (progress < 0.33)
|
|
Color.red
|
|
else if (progress < 0.66)
|
|
Color.yellow
|
|
else
|
|
Color.green;
|
|
_ = buf.setString(content_x + i, indicator_y + 1, "=", (Style{}).fg(color));
|
|
}
|
|
|
|
// Draw a visual box that scales with size
|
|
if (area.height > 15 and area.width > 30) {
|
|
const box_y = indicator_y + 4;
|
|
const box_width = @min(area.width -| 10, 40);
|
|
const box_height = @min(area.height -| box_y -| 3, 10);
|
|
|
|
// Draw box
|
|
var y: u16 = 0;
|
|
while (y < box_height) : (y += 1) {
|
|
var x: u16 = 0;
|
|
while (x < box_width) : (x += 1) {
|
|
const is_border = y == 0 or y == box_height - 1 or x == 0 or x == box_width - 1;
|
|
const char: []const u8 = if (is_border) "#" else " ";
|
|
const style = if (is_border)
|
|
(Style{}).fg(Color.blue)
|
|
else
|
|
Style{};
|
|
_ = buf.setString(content_x + x, box_y + y, char, style);
|
|
}
|
|
}
|
|
|
|
// Label inside box
|
|
var box_info: [32]u8 = undefined;
|
|
const box_str = std.fmt.bufPrint(&box_info, "{d}x{d}", .{ box_width, box_height }) catch "?";
|
|
const label_x = content_x + (box_width / 2) - @as(u16, @intCast(box_str.len / 2));
|
|
_ = buf.setString(label_x, box_y + box_height / 2, box_str, (Style{}).fg(Color.cyan));
|
|
}
|
|
|
|
// Footer
|
|
_ = buf.setString(
|
|
2,
|
|
area.height -| 1,
|
|
"Press q to quit | Resize terminal to test",
|
|
(Style{}).fg(Color.rgb(100, 100, 100)),
|
|
);
|
|
}
|