tiny.ui.style.computed
Defined in style.
API (5)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: lib/ui/src/style/computed.zig
zig
const std = @import("std");const arrange = @import("arrange");const css = @import("css");const abi = @import("../abi/root.zig");const asset = @import("../asset/root.zig");const paint_mod = @import("paint.zig");const Value = css.value.Value;pub const Metrics = struct { root_font_size: f32 = 16, viewport_width: f32 = 0, viewport_height: f32 = 0,};pub const FontSource = struct { registry: *const asset.Registry, source: []const u8,};pub const FontEnvironment = struct { source: FontSource, fallback_out: []asset.AssetHandle, first: u32,};pub const Record = struct { computed: css.cascade.Computed = .{}, layout: arrange.Style = .{}, dimensions: arrange.Dimensions = .{}, paint: paint_mod.Paint = .{}, text: paint_mod.TextStyle = .{}, layer: i16 = 0,};fn value(style: *const css.cascade.Computed, id: css.property.Id) Value { return style.get(id);}fn number(item: Value, fallback: f32) f32 { return switch (item.valueKind()) { .number, .length, .percent => item.asNumber(), else => fallback, };}fn length(item: Value, metrics: Metrics, font_size: f32, line_height: f32) ?f32 { if (item.valueKind() != .length) return null; const amount = item.asNumber(); return amount * switch (item.valueUnit()) { .px => @as(f32, 1), .em => font_size, .rem => metrics.root_font_size, .ch => font_size * 0.5, .lh => line_height, .ex => font_size * 0.5, .vw => metrics.viewport_width / 100, .vh => metrics.viewport_height / 100, .vmin => @min(metrics.viewport_width, metrics.viewport_height) / 100, .vmax => @max(metrics.viewport_width, metrics.viewport_height) / 100, else => return null, };}fn color(item: Value, foreground: abi.Color) abi.Color { if (item.asKeyword() == .currentcolor) return foreground; if (item.valueKind() != .color) return .{}; return .{ .r = css.value.color.red(item.a), .g = css.value.color.green(item.a), .b = css.value.color.blue(item.a), .a = css.value.color.alpha(item.a), };}fn styleCode(item: Value) u8 { return switch (item.asKeyword()) { .solid => 1, .dashed => 2, .dotted => 3, .double => 4, .groove => 5, .ridge => 6, .inset => 7, .outset => 8, else => 0, };}fn textFlags(style: *const css.cascade.Computed) u16 { const white_space: u16 = switch (value(style, .white_space).asKeyword()) { .nowrap => 1, .pre => 2, .pre_wrap => 3, .pre_line => 4, .break_spaces => 5, else => 0, }; const word_break: u16 = switch (value(style, .word_break).asKeyword()) { .break_all => 1, .keep_all => 2, else => 0, }; const overflow_wrap: u16 = switch (value(style, .overflow_wrap).asKeyword()) { .break_word => 1, .anywhere => 2, else => 0, }; const direction: u16 = @intFromBool(value(style, .direction).asKeyword() == .rtl); const user_select: u16 = switch (value(style, .user_select).asKeyword()) { .none => 1, .text => 2, .all => 3, else => 0, }; const slant: u16 = switch (value(style, .font_style).asKeyword()) { .italic => 1, .oblique => 2, else => 0, }; return (white_space << paint_mod.TextFlags.white_space_shift) | (word_break << paint_mod.TextFlags.word_break_shift) | (overflow_wrap << paint_mod.TextFlags.overflow_wrap_shift) | (direction << paint_mod.TextFlags.direction_shift) | (user_select << paint_mod.TextFlags.user_select_shift) | (slant << paint_mod.TextFlags.slant_shift);}fn inset(style: *const css.cascade.Computed, metrics: Metrics, size: f32, leading: f32, ids: [4]css.property.Id) arrange.Insets { return .{ .top = length(value(style, ids[0]), metrics, size, leading), .right = length(value(style, ids[1]), metrics, size, leading), .bottom = length(value(style, ids[2]), metrics, size, leading), .left = length(value(style, ids[3]), metrics, size, leading), };}pub fn lower(style: css.cascade.Computed, metrics: Metrics, fonts: ?FontEnvironment) Record { var record = Record{ .computed = style }; const font = length(value(&style, .font_size), metrics, metrics.root_font_size, metrics.root_font_size) orelse metrics.root_font_size; const leading_value = value(&style, .line_height); const leading = if (leading_value.valueKind() == .number) font * leading_value.asNumber() else length(leading_value, metrics, font, font) orelse font * 1.2; record.layout.flex_direction = switch (value(&style, .flex_direction).asKeyword()) { .column, .column_reverse => .column, else => .row, }; record.layout.wrap = switch (value(&style, .flex_wrap).asKeyword()) { .wrap, .wrap_reverse => .wrap, else => .no_wrap, }; record.layout.justify_content = switch (value(&style, .justify_content).asKeyword()) { .flex_end, .end => .flex_end, .center => .center, .space_between => .space_between, .space_around => .space_around, .space_evenly => .space_evenly, else => .flex_start, }; record.layout.align_items = switch (value(&style, .align_items).asKeyword()) { .flex_start, .start => .flex_start, .flex_end, .end => .flex_end, .center => .center, else => .stretch, }; record.layout.align_self = switch (value(&style, .align_self).asKeyword()) { .flex_start, .start => .flex_start, .flex_end, .end => .flex_end, .center => .center, .stretch => .stretch, else => .auto, }; record.layout.gap = length(value(&style, .row_gap), metrics, font, leading) orelse 0; record.layout.padding = inset(&style, metrics, font, leading, .{ .padding_top, .padding_right, .padding_bottom, .padding_left }); record.layout.constraints = .{ .min_width = length(value(&style, .min_width), metrics, font, leading), .min_height = length(value(&style, .min_height), metrics, font, leading), .max_width = length(value(&style, .max_width), metrics, font, leading), .max_height = length(value(&style, .max_height), metrics, font, leading), }; record.layout.flex_grow = number(value(&style, .flex_grow), 0); record.layout.flex_shrink = number(value(&style, .flex_shrink), 1); record.layout.flex_basis = length(value(&style, .flex_basis), metrics, font, leading); record.layout.position = if (value(&style, .position).asKeyword() == .absolute) .absolute else .relative; record.layout.inset = inset(&style, metrics, font, leading, .{ .top, .right, .bottom, .left }); record.dimensions = .{ .width = length(value(&style, .width), metrics, font, leading), .height = length(value(&style, .height), metrics, font, leading), }; record.paint.foreground = color(value(&style, .color), .{}); record.paint.background = color(value(&style, .background_color), record.paint.foreground); record.paint.border_color = .{ color(value(&style, .border_top_color), record.paint.foreground), color(value(&style, .border_right_color), record.paint.foreground), color(value(&style, .border_bottom_color), record.paint.foreground), color(value(&style, .border_left_color), record.paint.foreground), }; record.paint.outline_color = color(value(&style, .outline_color), record.paint.foreground); record.paint.border_width = .{ length(value(&style, .border_top_width), metrics, font, leading) orelse 0, length(value(&style, .border_right_width), metrics, font, leading) orelse 0, length(value(&style, .border_bottom_width), metrics, font, leading) orelse 0, length(value(&style, .border_left_width), metrics, font, leading) orelse 0, }; record.paint.radius = .{ length(value(&style, .border_top_left_radius), metrics, font, leading) orelse 0, length(value(&style, .border_top_right_radius), metrics, font, leading) orelse 0, length(value(&style, .border_bottom_right_radius), metrics, font, leading) orelse 0, length(value(&style, .border_bottom_left_radius), metrics, font, leading) orelse 0, }; record.paint.border_style = .{ styleCode(value(&style, .border_top_style)), styleCode(value(&style, .border_right_style)), styleCode(value(&style, .border_bottom_style)), styleCode(value(&style, .border_left_style)), }; record.paint.outline_style = styleCode(value(&style, .outline_style)); record.paint.outline_width = length(value(&style, .outline_width), metrics, font, leading) orelse 0; record.paint.outline_offset = length(value(&style, .outline_offset), metrics, font, leading) orelse 0; record.paint.outset = if (record.paint.outline_style == 0) 0 else @max(0, record.paint.outline_width + record.paint.outline_offset); record.paint.opacity = @intFromFloat(@round(std.math.clamp(number(value(&style, .opacity), 1), 0, 1) * 255)); record.text.size = font; record.text.line_height = leading; record.text.letter_spacing = length(value(&style, .letter_spacing), metrics, font, leading) orelse 0; record.text.word_spacing = length(value(&style, .word_spacing), metrics, font, leading) orelse 0; record.text.indent = length(value(&style, .text_indent), metrics, font, leading) orelse 0; record.text.underline_offset = length(value(&style, .text_underline_offset), metrics, font, leading) orelse 0; record.text.decoration_thickness = length(value(&style, .text_decoration_thickness), metrics, font, leading) orelse 0; record.text.decoration_color = color(value(&style, .text_decoration_color), record.paint.foreground); const weight = value(&style, .font_weight); record.text.weight = @intFromFloat(std.math.clamp(switch (weight.asKeyword()) { .bold, .bolder => @as(f32, 700), .lighter => @as(f32, 300), else => number(weight, 400), }, 1, 1000)); record.text.flags = textFlags(&style); if (fonts) |environment| { const raw_family = value(&style, .font_family); if (raw_family.valueKind() == .asset) { const handle = asset.AssetHandle{ .index = raw_family.a, .generation = raw_family.b }; if (environment.source.registry.hasFont(handle)) record.text.face = handle; } else { const family = if (raw_family.valueKind() == .string and raw_family.a <= environment.source.source.len and raw_family.b <= environment.source.source.len - raw_family.a) raw_family.asString(environment.source.source) else ""; const selection = asset.resolveAuthored( environment.source.registry, family, record.text.weight, @intCast((record.text.flags >> paint_mod.TextFlags.slant_shift) & 3), environment.fallback_out, ) catch unreachable; if (selection.face) |face| record.text.face = face; record.text.fallback_first = environment.first; record.text.fallback_count = @intCast(selection.fallback.len); } } record.text.text_align = @intCast(@backingInt(value(&style, .text_align).asKeyword())); record.text.transform = @intCast(@backingInt(value(&style, .text_transform).asKeyword())); record.text.decoration_line = @intCast(@backingInt(value(&style, .text_decoration_line).asKeyword())); record.text.decoration_style = styleCode(value(&style, .text_decoration_style)); const z = number(value(&style, .z_index), 0); record.layer = @intFromFloat(std.math.clamp(z, -32768, 32767)); return record;}test "lowering places outline reach and z index in computed records" { var computed = css.cascade.Computed.initial(); computed.set(.outline_style, Value.keyword(.solid)); computed.set(.outline_width, Value.length(2, .px)); computed.set(.outline_offset, Value.length(2, .px)); computed.set(.z_index, Value.number(7)); const record = lower(computed, .{}, null); try std.testing.expectEqual(@as(f32, 4), record.paint.outset); try std.testing.expectEqual(@as(i16, 7), record.layer); try std.testing.expectEqual(@as(u32, 0), record.text.face.index);}test "lowering fills all text flags and keyword weight" { var source = css.cascade.Computed.initial(); source.set(.white_space, Value.keyword(.pre_wrap)); source.set(.word_break, Value.keyword(.keep_all)); source.set(.overflow_wrap, Value.keyword(.anywhere)); source.set(.direction, Value.keyword(.rtl)); source.set(.user_select, Value.keyword(.text)); source.set(.font_style, Value.keyword(.oblique)); source.set(.font_weight, Value.keyword(.bold)); const record = lower(source, .{}, null); try std.testing.expectEqual(@as(u16, 700), record.text.weight); try std.testing.expectEqual(@as(u16, 3 | (2 << 3) | (2 << 5) | (1 << 7) | (2 << 8) | (2 << 10)), record.text.flags);}Source: lib/ui/src/style/root.zig:3
zig
pub const computed = @import("computed.zig");Complete call list for style.computed.lower
7 direct calls.
lib.ui.src.style.computed.color[function] — private source atlib/ui/src/style/computed.zig:65in nearest public ownertiny.ui.style.computedlib.ui.src.style.computed.inset[function] — private source atlib/ui/src/style/computed.zig:129in nearest public ownertiny.ui.style.computedlib.ui.src.style.computed.length[function] — private source atlib/ui/src/style/computed.zig:47in nearest public ownertiny.ui.style.computedlib.ui.src.style.computed.number[function] — private source atlib/ui/src/style/computed.zig:40in nearest public ownertiny.ui.style.computedlib.ui.src.style.computed.styleCode[function] — private source atlib/ui/src/style/computed.zig:76in nearest public ownertiny.ui.style.computedlib.ui.src.style.computed.textFlags[function] — private source atlib/ui/src/style/computed.zig:90in nearest public ownertiny.ui.style.computedlib.ui.src.style.computed.value[function] — private source atlib/ui/src/style/computed.zig:36in nearest public ownertiny.ui.style.computed
Audit
| Definitions | 6 |
|---|---|
| Public names | 7 |
| Members | 14 |
| Version | 26.7.0 |
| Revision | daab053ee433 |