//! 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 = try 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 = try 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(); }