tiny.ui.style.element
Defined in style.
API (4)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: lib/ui/src/style/element.zig
zig
const std = @import("std");const css = @import("css");const abi = @import("../abi/root.zig");const tree = @import("../tree/root.zig");pub const Interaction = struct { hovered: u64 = 0, focused: u64 = 0, active: u64 = 0, keyboard_focus: bool = false,};pub const Adapter = struct { view: tree.View, sheet: *const css.StyleSheet, interaction: Interaction = .{}, hovered_index: ?u32 = null, focused_index: ?u32 = null, active_index: ?u32 = null, resolved: bool = false, ancestor_steps: u64 = 0, pub fn element(self: *Adapter) css.match.Element { if (!self.resolved) { self.hovered_index = self.find(self.interaction.hovered); self.focused_index = self.find(self.interaction.focused); self.active_index = self.find(self.interaction.active); self.resolved = true; } return .{ .context = self, .parent = parent, .previous = previous, .kind = kind, .role = role, .identifier = identifier, .classes = classes, .pseudo = pseudo, .sibling_index = siblingIndex, }; } fn selfOf(context: ?*anyopaque) *Adapter { return @ptrCast(@alignCast(context.?)); } fn parent(context: ?*anyopaque, index: u32) callconv(.c) u32 { const self = selfOf(context); self.ancestor_steps += 1; std.debug.assert(index < self.view.nodes.len); return self.view.nodes[index].parent; } fn previous(context: ?*anyopaque, index: u32) callconv(.c) u32 { const self = selfOf(context); self.ancestor_steps += 1; std.debug.assert(index < self.view.nodes.len); if (index == 0) return index; const wanted = self.view.nodes[index].parent; var candidate = index; while (candidate > 0) { candidate -= 1; if (self.view.nodes[candidate].parent == wanted and candidate != wanted and candidate + self.view.nodes[candidate].subtree_count < index) return candidate; if (candidate == wanted) break; } return index; } fn kind(context: ?*anyopaque, index: u32) callconv(.c) u16 { const self = selfOf(context); const value: abi.Kind = @fromBackingInt(@intCast(self.view.nodes[index].kind)); return @intCast(self.sheet.token(@tagName(value))); } fn role(context: ?*anyopaque, index: u32) callconv(.c) u16 { const self = selfOf(context); const value: abi.Role = @fromBackingInt(@intCast(self.view.nodes[index].role)); return @intCast(self.sheet.token(@tagName(value))); } fn identifier(context: ?*anyopaque, index: u32) callconv(.c) u32 { const self = selfOf(context); return self.sheet.token(self.view.text(self.view.nodes[index].identifier)); } fn classes(context: ?*anyopaque, index: u32, out: [*]u32, capacity: u32) callconv(.c) u32 { const self = selfOf(context); const source = self.view.classesOf(index); std.debug.assert(source.len <= capacity); for (source, 0..) |atom, at| out[at] = self.sheet.token(self.view.text(atom)); return @intCast(source.len); } fn hasAncestor(self: *const Adapter, index: u32, start: ?u32) bool { var candidate = start orelse return false; while (true) { if (candidate == index) return true; if (candidate == 0) return false; candidate = self.view.nodes[candidate].parent; } } fn pseudo(context: ?*anyopaque, index: u32) callconv(.c) u32 { const self = selfOf(context); const node = self.view.nodes[index]; var word: u32 = 0; inline for (@typeInfo(abi.State).@"enum".field_names, 0..) |name, at| { const state: abi.State = @fromBackingInt(@intCast(at)); const class = @field(css.selector.PseudoClass, name); if (abi.active(node.state, state)) word |= css.selector.bit(class); } if (index == 0) word |= css.selector.bit(.root); if (node.subtree_count == 0 and node.text == abi.text_absent) word |= css.selector.bit(.empty); if (!abi.active(node.state, .disabled)) word |= css.selector.bit(.enabled); if (abi.holds(node.flags, .modal)) word |= css.selector.bit(.modal); if (self.hasAncestor(index, self.hovered_index)) word |= css.selector.bit(.hover); if (self.hasAncestor(index, self.active_index)) word |= css.selector.bit(.active); if (self.view.nodes[index].id == self.interaction.focused and self.interaction.focused != 0) { word |= css.selector.bit(.focus); if (self.interaction.keyboard_focus) word |= css.selector.bit(.focus_visible); } if (self.focused_index) |focused| { if (self.isAncestor(index, focused)) word |= css.selector.bit(.focus_within); } return word; } pub fn find(self: *const Adapter, id: u64) ?u32 { if (id == 0) return null; if (self.view.identity) |identity| return identity.lookup(self.view.nodes, id); for (self.view.nodes, 0..) |node, at| if (node.id == id) return @intCast(at); return null; } fn isAncestor(self: *const Adapter, ancestor: u32, child: u32) bool { var index = child; while (true) { if (index == ancestor) return true; if (index == 0) return false; index = self.view.nodes[index].parent; } } fn siblingIndex(context: ?*anyopaque, index: u32, of_type: bool) callconv(.c) u32 { const self = selfOf(context); const node = self.view.nodes[index]; var first: u32 = if (index == 0) 0 else node.parent + 1; var ordinal: u32 = 0; var total: u32 = 0; while (first < self.view.nodes.len and (index == 0 or first <= node.parent + self.view.nodes[node.parent].subtree_count)) { const candidate = self.view.nodes[first]; if (!of_type or candidate.kind == node.kind) { total += 1; if (first <= index) ordinal = total; } first += candidate.subtree_count + 1; } return css.match.packSibling(ordinal, total); }};Source: lib/ui/src/style/root.zig:1
zig
pub const element = @import("element.zig");Audit
| Definitions | 5 |
|---|---|
| Public names | 9 |
| Members | 12 |
| Version | 26.7.0 |
| Revision | daab053ee433 |