tiny.gui.model
Defined in tiny.gui.
API (45)
Actions
Public operations.
TextRunValidator.validateUiImage.pixelCountUiImage.validatenonNegativeFinitenormalizedTextPointSizepointInRectresolvedTextStyletextGraphemeBoundarytextStylesEqualvalidTextPointSizevalidateTextvalidateTextRuns
Types and contracts
Public types and contracts.
RootFrameTextRunValidatorUiBorderEdgePaintUiBorderPaintUiColorUiFrameUiImageUiImagePaintUiImageSetUiLinearGradientUiNodeUiOverflowUiPaintUiPointUiScrollUiShadowPaintUiSurfaceTreeUiTextUiTextAlignUiTextRunUiTextSelectionUiTextStyleWidgetFrameWidgetKind
Values and defaults
Public values and defaults.
ui_font_weight_maxui_font_weight_minui_font_weight_normalui_state_checkedui_state_dangerui_state_defaultui_state_disabledui_state_expandedui_state_selected
Source
Source: lib/gui/src/model.zig
zig
const std = @import("std");const filigree = @import("filigree");const flex_layout = @import("arrange");pub const WidgetKind = enum(u8) { container, label, button, text_input,};pub const ui_state_selected: u64 = 1 << 0;pub const ui_state_disabled: u64 = 1 << 1;pub const ui_state_expanded: u64 = 1 << 2;pub const ui_state_checked: u64 = 1 << 3;pub const ui_state_default: u64 = 1 << 4;pub const ui_state_danger: u64 = 1 << 5;pub const UiOverflow = enum(u8) { visible, clip,};pub const UiColor = struct { r: u8 = 0, g: u8 = 0, b: u8 = 0, a: u8 = 255,};pub const UiPoint = struct { x: f32 = 0, y: f32 = 0,};pub const UiLinearGradient = struct { start: UiPoint = .{}, end: UiPoint = .{ .x = 1 }, start_color: UiColor = .{}, end_color: UiColor = .{},};pub const UiPaint = struct { background: ?UiColor = null, linear_gradient: ?UiLinearGradient = null, foreground: ?UiColor = null, border: ?UiColor = null, border_width: f32 = 0, corner_roundness: f32 = 0, border_edges: UiBorderPaint = .{}, shadow: UiShadowPaint = .{}, image: ?UiImagePaint = null,};pub const UiImagePaint = struct { index: u32, source: flex_layout.Rect = .{}, tint: ?UiColor = null, opacity: u8 = 255,};pub const UiImage = struct { width: u32, height: u32, pixels: []const u32, pub fn pixelCount(self: UiImage) usize { return @as(usize, self.width) * @as(usize, self.height); } pub fn validate(self: UiImage) !void { if (self.width == 0 or self.height == 0) return error.InvalidImage; if (self.pixels.len < self.pixelCount()) return error.BufferTooSmall; }};pub const UiImageSet = struct { images: []const UiImage = &.{},};pub const UiBorderEdgePaint = struct { color: ?UiColor = null, width: f32 = 0,};pub const UiBorderPaint = struct { top: UiBorderEdgePaint = .{}, right: UiBorderEdgePaint = .{}, bottom: UiBorderEdgePaint = .{}, left: UiBorderEdgePaint = .{},};pub const UiShadowPaint = struct { color: ?UiColor = null, offset_x: f32 = 0, offset_y: f32 = 0, blur_radius: f32 = 0, spread: f32 = 0,};pub const UiScroll = struct { overflow_x: UiOverflow = .visible, overflow_y: UiOverflow = .visible, scroll_x: f32 = 0, scroll_y: f32 = 0,};pub fn nonNegativeFinite(value: f32) f32 { if (!std.math.isFinite(value) or value <= 0) return 0; return value;}pub const UiTextStyle = struct { font_asset_id: u64 = 0, point_size: f64 = 16.0,};pub const UiTextRun = struct { byte_start: usize, byte_end: usize, style_slot: u16 = 0, foreground: ?UiColor = null, background: ?UiColor = null, underline: bool = false, strikethrough: bool = false,};pub const UiText = struct { content: []const u8, runs: []const UiTextRun = &.{}, styles: []const UiTextStyle = &.{}, font_asset_id: u64 = 0, font_weight: u16 = ui_font_weight_normal, point_size: f64 = 16.0, line_height: f64 = 1.2, wrap_width: f64 = 0.0, horizontal_align: UiTextAlign = .start, vertical_align: UiTextAlign = .start,};pub const UiTextAlign = enum(u8) { start, center, end,};pub const UiTextSelection = struct { cursor_visible: bool = false, cursor_block: bool = false, cursor_byte_offset: usize = 0, selection_active: bool = false, selection_anchor_byte_offset: usize = 0, selection_focus_byte_offset: usize = 0,};pub const ui_font_weight_normal: u16 = 400;pub const ui_font_weight_min: u16 = 100;pub const ui_font_weight_max: u16 = 900;pub const TextRunValidator = struct { previous_end: usize = 0, pub fn validate( self: *TextRunValidator, content: []const u8, byte_start: usize, byte_end: usize, ) error{InvalidTextRun}!void { if (byte_start < self.previous_end or byte_start >= byte_end) return error.InvalidTextRun; if (byte_end > content.len) return error.InvalidTextRun; if (!textByteBoundary(content, byte_start) or !textByteBoundary(content, byte_end)) return error.InvalidTextRun; self.previous_end = byte_end; }};pub fn validateTextRuns(content: []const u8, runs: []const UiTextRun) error{InvalidTextRun}!void { var validator = TextRunValidator{}; for (runs) |run| { try validator.validate(content, run.byte_start, run.byte_end); }}pub fn validateText(text: UiText) error{InvalidTextRun}!void { if (text.styles.len > std.math.maxInt(u16)) return error.InvalidTextRun; for (text.styles) |style| { if (!validTextPointSize(style.point_size)) return error.InvalidTextRun; } try validateTextRuns(text.content, text.runs); for (text.runs, 0..) |run, index| { if (run.style_slot > text.styles.len) return error.InvalidTextRun; const style = resolvedTextStyle(text, run.style_slot); const before = if (index > 0 and text.runs[index - 1].byte_end == run.byte_start) resolvedTextStyle(text, text.runs[index - 1].style_slot) else resolvedTextStyle(text, 0); const after = if (index + 1 < text.runs.len and text.runs[index + 1].byte_start == run.byte_end) resolvedTextStyle(text, text.runs[index + 1].style_slot) else resolvedTextStyle(text, 0); if (!textStylesEqual(before, style) and !textGraphemeBoundary(text.content, run.byte_start)) { return error.InvalidTextRun; } if (!textStylesEqual(style, after) and !textGraphemeBoundary(text.content, run.byte_end)) { return error.InvalidTextRun; } }}pub fn resolvedTextStyle(text: UiText, style_slot: u16) UiTextStyle { if (style_slot == 0 or style_slot > text.styles.len) { return .{ .font_asset_id = text.font_asset_id, .point_size = normalizedTextPointSize(text.point_size), }; } return text.styles[style_slot - 1];}pub fn textStylesEqual(left: UiTextStyle, right: UiTextStyle) bool { return left.font_asset_id == right.font_asset_id and @as(u64, @bitCast(left.point_size)) == @as(u64, @bitCast(right.point_size));}pub fn normalizedTextPointSize(value: f64) f64 { return if (validTextPointSize(value)) value else 16.0;}pub fn validTextPointSize(value: f64) bool { return std.math.isFinite(value) and value > 0;}pub fn textGraphemeBoundary(content: []const u8, index: usize) bool { if (index == 0 or index == content.len) return true; if (index > content.len) return false; var iterator = filigree.unicode.SourceIterator.init(.{ .utf8 = content }, 0) catch return false; var state: filigree.unicode.GraphemeState = .{}; while (iterator.next() catch return false) |scalar| { const boundary = !state.consume(scalar.codepoint); const offset: usize = @intCast(scalar.source.start); if (offset == index) return boundary; if (offset > index) return false; } return false;}fn textByteBoundary(content: []const u8, index: usize) bool { if (index == 0 or index == content.len) return true; return content[index] & 0b1100_0000 != 0b1000_0000;}pub const UiNode = struct { widget_id: u64, kind: WidgetKind = .container, style: flex_layout.Style = .{}, size: flex_layout.Dimensions = .{}, paint: UiPaint = .{}, scroll: UiScroll = .{}, focusable: bool = false, layer: u8 = 0, text: ?UiText = null, text_selection: UiTextSelection = .{}, action: []const u8 = &.{}, role: []const u8 = &.{}, state_flags: u64 = 0, children: []const UiNode = &.{},};pub const UiSurfaceTree = struct { available_size: flex_layout.Size, root: UiNode,};pub const RootFrame = struct { root_id: u64, surface_revision: u64, rect: flex_layout.Rect, widget_start: usize, widget_count: usize,};pub const WidgetFrame = struct { root_id: u64, widget_id: u64, kind: WidgetKind, rect: flex_layout.Rect, visible_rect: flex_layout.Rect, paint: UiPaint, scroll: UiScroll, constraints: flex_layout.Constraints, content_size: flex_layout.Size, focusable: bool, layer: u8 = 0, hovered: bool = false, captured: bool = false, focused: bool = false, has_text: bool = false, action: []const u8 = &.{}, role: []const u8 = &.{}, state_flags: u64 = 0, text_ref: u64 = 0, text: ?UiText = null, text_selection: UiTextSelection = .{}, subtree_size: usize = 1,};pub const UiFrame = struct { revision: u64 = 0, root_count: usize = 0, roots: []const RootFrame = &.{}, widget_count: usize = 0, widgets: []const WidgetFrame = &.{}, text_layout_revision: u64 = 0, text_layout_ptr: ?*anyopaque = null,};pub fn pointInRect(rect: flex_layout.Rect, x: f32, y: f32) bool { return x >= rect.x and y >= rect.y and x <= rect.x + rect.width and y <= rect.y + rect.height;}test "text runs require ordered nonempty UTF-8 byte ranges" { const content = "a\u{00e9}z"; const valid = [_]UiTextRun{ .{ .byte_start = 0, .byte_end = 1 }, .{ .byte_start = 1, .byte_end = 3 }, .{ .byte_start = 3, .byte_end = 4 }, }; try validateTextRuns(content, &valid); try std.testing.expectError(error.InvalidTextRun, validateTextRuns(content, &.{ .{ .byte_start = 1, .byte_end = 1 }, })); try std.testing.expectError(error.InvalidTextRun, validateTextRuns(content, &.{ .{ .byte_start = 1, .byte_end = 2 }, })); try std.testing.expectError(error.InvalidTextRun, validateTextRuns(content, &.{ .{ .byte_start = 0, .byte_end = 3 }, .{ .byte_start = 2, .byte_end = 4 }, })); try std.testing.expectError(error.InvalidTextRun, validateTextRuns(content, &.{ .{ .byte_start = 3, .byte_end = 5 }, }));}test "metric text runs require valid slots and grapheme boundaries" { const content = "a\u{0301}b"; const styles = [_]UiTextStyle{ .{ .font_asset_id = 7, .point_size = 18 }, .{ .point_size = 16 }, }; try validateText(.{ .content = content, .styles = &styles, .runs = &.{ .{ .byte_start = 0, .byte_end = 3, .style_slot = 1 }, .{ .byte_start = 3, .byte_end = 4, .style_slot = 2 }, }, }); try validateText(.{ .content = content, .styles = &styles, .runs = &.{ .{ .byte_start = 0, .byte_end = 1, .style_slot = 1 }, .{ .byte_start = 1, .byte_end = 3, .style_slot = 1 }, }, }); try std.testing.expectError(error.InvalidTextRun, validateText(.{ .content = content, .styles = &styles, .runs = &.{ .{ .byte_start = 0, .byte_end = 1, .style_slot = 1 }, .{ .byte_start = 1, .byte_end = 3, .style_slot = 2 }, }, })); try std.testing.expectError(error.InvalidTextRun, validateText(.{ .content = content, .styles = &styles, .runs = &.{ .{ .byte_start = 1, .byte_end = 3, .style_slot = 1 }, }, })); try std.testing.expectError(error.InvalidTextRun, validateText(.{ .content = content, .styles = &styles, .runs = &.{ .{ .byte_start = 0, .byte_end = 3, .style_slot = 3 }, }, })); try std.testing.expectError(error.InvalidTextRun, validateText(.{ .content = content, .styles = &.{.{ .point_size = std.math.nan(f64) }}, }));}Source: lib/gui/src/root.zig:13
zig
pub const model = @import("model.zig");Audit
| Definitions | 46 |
|---|---|
| Public names | 48 |
| Members | 127 |
| Version | 26.7.0 |
| Revision | daab053ee433 |