zcatui/src/symbols/line.zig
reugenio 96810d80ea feat: Add focus management, themes, and comprehensive tests
Focus management system (src/focus.zig):
- FocusRing for tab-order navigation within widget groups
- FocusManager for managing multiple focus rings
- Focusable interface with vtable pattern
- Focus trapping for modals

Theme system (src/theme.zig):
- Theme struct with full color properties
- Style builder methods (primaryStyle, errorStyle, etc.)
- 10 predefined themes: dark, light, dracula, nord, gruvbox,
  solarized_dark, monokai, one_dark, tokyo_night, catppuccin

Comprehensive test suite (src/tests/):
- widget_tests.zig: Block, Gauge, Checkbox, RadioGroup, Select,
  Slider, StatusBar, Toast, Panel, TabbedPanel, Rect, Buffer, Style
- theme_tests.zig: Theme system and predefined themes
- layout_tests.zig: Layout constraints and splits
- tests.zig: Test aggregator

Bug fixes:
- Fixed Zig 0.15 API compatibility in inline tests
- style.fg -> style.foreground
- std.time.sleep -> std.Thread.sleep
- Cell.char -> Cell.symbol with proper Symbol comparison

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 18:29:00 +01:00

292 lines
9.2 KiB
Zig

//! Line drawing characters.
//!
//! Provides sets of Unicode box-drawing characters for creating lines,
//! borders, and tables with various styles (normal, rounded, double, thick).
const std = @import("std");
// ============================================================================
// Individual Line Characters
// ============================================================================
// Vertical lines
pub const VERTICAL: []const u8 = "";
pub const DOUBLE_VERTICAL: []const u8 = "";
pub const THICK_VERTICAL: []const u8 = "";
pub const LIGHT_DOUBLE_DASH_VERTICAL: []const u8 = "";
pub const HEAVY_DOUBLE_DASH_VERTICAL: []const u8 = "";
pub const LIGHT_TRIPLE_DASH_VERTICAL: []const u8 = "";
pub const HEAVY_TRIPLE_DASH_VERTICAL: []const u8 = "";
pub const LIGHT_QUADRUPLE_DASH_VERTICAL: []const u8 = "";
pub const HEAVY_QUADRUPLE_DASH_VERTICAL: []const u8 = "";
// Horizontal lines
pub const HORIZONTAL: []const u8 = "";
pub const DOUBLE_HORIZONTAL: []const u8 = "";
pub const THICK_HORIZONTAL: []const u8 = "";
pub const LIGHT_DOUBLE_DASH_HORIZONTAL: []const u8 = "";
pub const HEAVY_DOUBLE_DASH_HORIZONTAL: []const u8 = "";
pub const LIGHT_TRIPLE_DASH_HORIZONTAL: []const u8 = "";
pub const HEAVY_TRIPLE_DASH_HORIZONTAL: []const u8 = "";
pub const LIGHT_QUADRUPLE_DASH_HORIZONTAL: []const u8 = "";
pub const HEAVY_QUADRUPLE_DASH_HORIZONTAL: []const u8 = "";
// Corners - Top Right
pub const TOP_RIGHT: []const u8 = "";
pub const ROUNDED_TOP_RIGHT: []const u8 = "";
pub const DOUBLE_TOP_RIGHT: []const u8 = "";
pub const THICK_TOP_RIGHT: []const u8 = "";
// Corners - Top Left
pub const TOP_LEFT: []const u8 = "";
pub const ROUNDED_TOP_LEFT: []const u8 = "";
pub const DOUBLE_TOP_LEFT: []const u8 = "";
pub const THICK_TOP_LEFT: []const u8 = "";
// Corners - Bottom Right
pub const BOTTOM_RIGHT: []const u8 = "";
pub const ROUNDED_BOTTOM_RIGHT: []const u8 = "";
pub const DOUBLE_BOTTOM_RIGHT: []const u8 = "";
pub const THICK_BOTTOM_RIGHT: []const u8 = "";
// Corners - Bottom Left
pub const BOTTOM_LEFT: []const u8 = "";
pub const ROUNDED_BOTTOM_LEFT: []const u8 = "";
pub const DOUBLE_BOTTOM_LEFT: []const u8 = "";
pub const THICK_BOTTOM_LEFT: []const u8 = "";
// T-junctions
pub const VERTICAL_LEFT: []const u8 = "";
pub const DOUBLE_VERTICAL_LEFT: []const u8 = "";
pub const THICK_VERTICAL_LEFT: []const u8 = "";
pub const VERTICAL_RIGHT: []const u8 = "";
pub const DOUBLE_VERTICAL_RIGHT: []const u8 = "";
pub const THICK_VERTICAL_RIGHT: []const u8 = "";
pub const HORIZONTAL_DOWN: []const u8 = "";
pub const DOUBLE_HORIZONTAL_DOWN: []const u8 = "";
pub const THICK_HORIZONTAL_DOWN: []const u8 = "";
pub const HORIZONTAL_UP: []const u8 = "";
pub const DOUBLE_HORIZONTAL_UP: []const u8 = "";
pub const THICK_HORIZONTAL_UP: []const u8 = "";
// Cross
pub const CROSS: []const u8 = "";
pub const DOUBLE_CROSS: []const u8 = "";
pub const THICK_CROSS: []const u8 = "";
// ============================================================================
// Line Set
// ============================================================================
/// A complete set of line drawing characters for a particular style.
pub const Set = struct {
vertical: []const u8,
horizontal: []const u8,
top_right: []const u8,
top_left: []const u8,
bottom_right: []const u8,
bottom_left: []const u8,
vertical_left: []const u8,
vertical_right: []const u8,
horizontal_down: []const u8,
horizontal_up: []const u8,
cross: []const u8,
pub const default: Set = NORMAL;
};
/// Normal (thin) line set.
/// ```
/// ┌─┬┐
/// │ ││
/// ├─┼┤
/// └─┴┘
/// ```
pub const NORMAL: Set = .{
.vertical = VERTICAL,
.horizontal = HORIZONTAL,
.top_right = TOP_RIGHT,
.top_left = TOP_LEFT,
.bottom_right = BOTTOM_RIGHT,
.bottom_left = BOTTOM_LEFT,
.vertical_left = VERTICAL_LEFT,
.vertical_right = VERTICAL_RIGHT,
.horizontal_down = HORIZONTAL_DOWN,
.horizontal_up = HORIZONTAL_UP,
.cross = CROSS,
};
/// Rounded corner line set.
/// ```
/// ╭─┬╮
/// │ ││
/// ├─┼┤
/// ╰─┴╯
/// ```
pub const ROUNDED: Set = .{
.vertical = VERTICAL,
.horizontal = HORIZONTAL,
.top_right = ROUNDED_TOP_RIGHT,
.top_left = ROUNDED_TOP_LEFT,
.bottom_right = ROUNDED_BOTTOM_RIGHT,
.bottom_left = ROUNDED_BOTTOM_LEFT,
.vertical_left = VERTICAL_LEFT,
.vertical_right = VERTICAL_RIGHT,
.horizontal_down = HORIZONTAL_DOWN,
.horizontal_up = HORIZONTAL_UP,
.cross = CROSS,
};
/// Double line set.
/// ```
/// ╔═╦╗
/// ║ ║║
/// ╠═╬╣
/// ╚═╩╝
/// ```
pub const DOUBLE: Set = .{
.vertical = DOUBLE_VERTICAL,
.horizontal = DOUBLE_HORIZONTAL,
.top_right = DOUBLE_TOP_RIGHT,
.top_left = DOUBLE_TOP_LEFT,
.bottom_right = DOUBLE_BOTTOM_RIGHT,
.bottom_left = DOUBLE_BOTTOM_LEFT,
.vertical_left = DOUBLE_VERTICAL_LEFT,
.vertical_right = DOUBLE_VERTICAL_RIGHT,
.horizontal_down = DOUBLE_HORIZONTAL_DOWN,
.horizontal_up = DOUBLE_HORIZONTAL_UP,
.cross = DOUBLE_CROSS,
};
/// Thick (heavy) line set.
/// ```
/// ┏━┳┓
/// ┃ ┃┃
/// ┣━╋┫
/// ┗━┻┛
/// ```
pub const THICK: Set = .{
.vertical = THICK_VERTICAL,
.horizontal = THICK_HORIZONTAL,
.top_right = THICK_TOP_RIGHT,
.top_left = THICK_TOP_LEFT,
.bottom_right = THICK_BOTTOM_RIGHT,
.bottom_left = THICK_BOTTOM_LEFT,
.vertical_left = THICK_VERTICAL_LEFT,
.vertical_right = THICK_VERTICAL_RIGHT,
.horizontal_down = THICK_HORIZONTAL_DOWN,
.horizontal_up = THICK_HORIZONTAL_UP,
.cross = THICK_CROSS,
};
/// Light double-dashed line set.
pub const LIGHT_DOUBLE_DASHED: Set = .{
.vertical = LIGHT_DOUBLE_DASH_VERTICAL,
.horizontal = LIGHT_DOUBLE_DASH_HORIZONTAL,
.top_right = TOP_RIGHT,
.top_left = TOP_LEFT,
.bottom_right = BOTTOM_RIGHT,
.bottom_left = BOTTOM_LEFT,
.vertical_left = VERTICAL_LEFT,
.vertical_right = VERTICAL_RIGHT,
.horizontal_down = HORIZONTAL_DOWN,
.horizontal_up = HORIZONTAL_UP,
.cross = CROSS,
};
/// Heavy double-dashed line set.
pub const HEAVY_DOUBLE_DASHED: Set = .{
.vertical = HEAVY_DOUBLE_DASH_VERTICAL,
.horizontal = HEAVY_DOUBLE_DASH_HORIZONTAL,
.top_right = THICK_TOP_RIGHT,
.top_left = THICK_TOP_LEFT,
.bottom_right = THICK_BOTTOM_RIGHT,
.bottom_left = THICK_BOTTOM_LEFT,
.vertical_left = THICK_VERTICAL_LEFT,
.vertical_right = THICK_VERTICAL_RIGHT,
.horizontal_down = THICK_HORIZONTAL_DOWN,
.horizontal_up = THICK_HORIZONTAL_UP,
.cross = THICK_CROSS,
};
/// Light triple-dashed line set.
pub const LIGHT_TRIPLE_DASHED: Set = .{
.vertical = LIGHT_TRIPLE_DASH_VERTICAL,
.horizontal = LIGHT_TRIPLE_DASH_HORIZONTAL,
.top_right = TOP_RIGHT,
.top_left = TOP_LEFT,
.bottom_right = BOTTOM_RIGHT,
.bottom_left = BOTTOM_LEFT,
.vertical_left = VERTICAL_LEFT,
.vertical_right = VERTICAL_RIGHT,
.horizontal_down = HORIZONTAL_DOWN,
.horizontal_up = HORIZONTAL_UP,
.cross = CROSS,
};
/// Heavy triple-dashed line set.
pub const HEAVY_TRIPLE_DASHED: Set = .{
.vertical = HEAVY_TRIPLE_DASH_VERTICAL,
.horizontal = HEAVY_TRIPLE_DASH_HORIZONTAL,
.top_right = THICK_TOP_RIGHT,
.top_left = THICK_TOP_LEFT,
.bottom_right = THICK_BOTTOM_RIGHT,
.bottom_left = THICK_BOTTOM_LEFT,
.vertical_left = THICK_VERTICAL_LEFT,
.vertical_right = THICK_VERTICAL_RIGHT,
.horizontal_down = THICK_HORIZONTAL_DOWN,
.horizontal_up = THICK_HORIZONTAL_UP,
.cross = THICK_CROSS,
};
/// Light quadruple-dashed line set.
pub const LIGHT_QUADRUPLE_DASHED: Set = .{
.vertical = LIGHT_QUADRUPLE_DASH_VERTICAL,
.horizontal = LIGHT_QUADRUPLE_DASH_HORIZONTAL,
.top_right = TOP_RIGHT,
.top_left = TOP_LEFT,
.bottom_right = BOTTOM_RIGHT,
.bottom_left = BOTTOM_LEFT,
.vertical_left = VERTICAL_LEFT,
.vertical_right = VERTICAL_RIGHT,
.horizontal_down = HORIZONTAL_DOWN,
.horizontal_up = HORIZONTAL_UP,
.cross = CROSS,
};
/// Heavy quadruple-dashed line set.
pub const HEAVY_QUADRUPLE_DASHED: Set = .{
.vertical = HEAVY_QUADRUPLE_DASH_VERTICAL,
.horizontal = HEAVY_QUADRUPLE_DASH_HORIZONTAL,
.top_right = THICK_TOP_RIGHT,
.top_left = THICK_TOP_LEFT,
.bottom_right = THICK_BOTTOM_RIGHT,
.bottom_left = THICK_BOTTOM_LEFT,
.vertical_left = THICK_VERTICAL_LEFT,
.vertical_right = THICK_VERTICAL_RIGHT,
.horizontal_down = THICK_HORIZONTAL_DOWN,
.horizontal_up = THICK_HORIZONTAL_UP,
.cross = THICK_CROSS,
};
// ============================================================================
// Tests
// ============================================================================
test "line set default" {
try std.testing.expectEqualStrings(VERTICAL, Set.default.vertical);
try std.testing.expectEqualStrings(HORIZONTAL, Set.default.horizontal);
}
test "line characters are valid UTF-8" {
// Verify all characters decode properly
const v: [3]u8 = VERTICAL[0..3].*;
const h: [3]u8 = HORIZONTAL[0..3].*;
const tl: [3]u8 = TOP_LEFT[0..3].*;
_ = std.unicode.utf8Decode(&v) catch unreachable;
_ = std.unicode.utf8Decode(&h) catch unreachable;
_ = std.unicode.utf8Decode(&tl) catch unreachable;
}