zcatgui/src/widgets/label.zig
reugenio 6ac3856ae2 feat: zcatgui v0.5.0 - Complete widget library + research docs
Widgets implemented (13 total):
- Label: Static text with alignment
- Button: With importance levels (primary/normal/danger)
- TextInput: Single-line text entry with cursor
- Checkbox: Boolean toggle
- Select: Dropdown selection
- List: Scrollable selectable list
- Focus: Focus manager with tab navigation
- Table: Editable table with dirty tracking, keyboard nav
- Split: HSplit/VSplit draggable panels
- Panel: Container with title bar, collapsible
- Modal: Dialogs (alert, confirm, inputDialog)
- AutoComplete: ComboBox with prefix/contains/fuzzy matching

Core improvements:
- InputState now tracks keyboard state (keys_down, key_events)
- Full keyboard navigation for Table widget

Research documentation:
- WIDGET_COMPARISON.md: zcatgui vs DVUI vs Gio vs zcatui
- SIMIFACTU_ADVANCEDTABLE.md: Analysis of 10K LOC table component
- LEGO_PANELS_SYSTEM.md: Modular panel composition architecture

Examples:
- widgets_demo.zig: All basic widgets showcase
- table_demo.zig: Table, Split, Panel demonstration

All tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 11:00:49 +01:00

115 lines
3.3 KiB
Zig

//! Label Widget - Static text display
//!
//! A simple widget for displaying text. Supports alignment and styling.
const std = @import("std");
const Context = @import("../core/context.zig").Context;
const Command = @import("../core/command.zig");
const Layout = @import("../core/layout.zig");
const Style = @import("../core/style.zig");
/// Text alignment
pub const Alignment = enum {
left,
center,
right,
};
/// Label configuration
pub const LabelConfig = struct {
color: Style.Color = Style.Color.foreground,
alignment: Alignment = .left,
/// Padding inside the label area
padding: u32 = 0,
};
/// Draw a label at the current layout position
pub fn label(ctx: *Context, text: []const u8) void {
labelEx(ctx, text, .{});
}
/// Draw a label with custom configuration
pub fn labelEx(ctx: *Context, text: []const u8, config: LabelConfig) void {
const bounds = ctx.layout.nextRect();
labelRect(ctx, bounds, text, config);
}
/// Draw a label in a specific rectangle
pub fn labelRect(ctx: *Context, bounds: Layout.Rect, text: []const u8, config: LabelConfig) void {
if (bounds.isEmpty()) return;
const inner = bounds.shrink(config.padding);
if (inner.isEmpty()) return;
// Calculate text position based on alignment
// Assume 8 pixels per character (bitmap font)
const char_width: u32 = 8;
const text_width = @as(u32, @intCast(text.len)) * char_width;
const x: i32 = switch (config.alignment) {
.left => inner.x,
.center => inner.x + @as(i32, @intCast((inner.w -| text_width) / 2)),
.right => inner.x + @as(i32, @intCast(inner.w -| text_width)),
};
// Center vertically (assume 8 pixel font height)
const char_height: u32 = 8;
const y = inner.y + @as(i32, @intCast((inner.h -| char_height) / 2));
ctx.pushCommand(Command.text(x, y, text, config.color));
}
/// Draw a colored label (convenience function)
pub fn labelColored(ctx: *Context, text: []const u8, color: Style.Color) void {
labelEx(ctx, text, .{ .color = color });
}
/// Draw a centered label (convenience function)
pub fn labelCentered(ctx: *Context, text: []const u8) void {
labelEx(ctx, text, .{ .alignment = .center });
}
// =============================================================================
// Tests
// =============================================================================
test "label generates text command" {
var ctx = Context.init(std.testing.allocator, 800, 600);
defer ctx.deinit();
ctx.beginFrame();
ctx.layout.row_height = 20;
label(&ctx, "Hello");
try std.testing.expectEqual(@as(usize, 1), ctx.commands.items.len);
switch (ctx.commands.items[0]) {
.text => |t| {
try std.testing.expectEqualStrings("Hello", t.text);
},
else => unreachable,
}
ctx.endFrame();
}
test "label alignment" {
var ctx = Context.init(std.testing.allocator, 800, 600);
defer ctx.deinit();
ctx.beginFrame();
ctx.layout.row_height = 20;
// Left aligned (default)
labelEx(&ctx, "Left", .{ .alignment = .left });
// The text should start at x=0
switch (ctx.commands.items[0]) {
.text => |t| {
try std.testing.expectEqual(@as(i32, 0), t.x);
},
else => unreachable,
}
ctx.endFrame();
}