lib/gui/src/surface/decode.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const ui = @import("../root.zig");
  3 const command = @import("command.zig");
  4 
  5 const flex_layout = ui.layout;
  6 const model = ui.model;
  7 const UiBorderEdgePaint = model.UiBorderEdgePaint;
  8 const UiBorderPaint = model.UiBorderPaint;
  9 const UiColor = model.UiColor;
 10 const UiImagePaint = model.UiImagePaint;
 11 const UiLinearGradient = model.UiLinearGradient;
 12 const UiOverflow = model.UiOverflow;
 13 const UiPaint = model.UiPaint;
 14 const UiScroll = model.UiScroll;
 15 const UiShadowPaint = model.UiShadowPaint;
 16 const UiTextAlign = model.UiTextAlign;
 17 const UiTextRun = model.UiTextRun;
 18 const UiTextStyle = model.UiTextStyle;
 19 const UiTextSelection = model.UiTextSelection;
 20 const WidgetKind = model.WidgetKind;
 21 const ui_font_weight_max = model.ui_font_weight_max;
 22 const ui_font_weight_min = model.ui_font_weight_min;
 23 const ui_font_weight_normal = model.ui_font_weight_normal;
 24 const nonNegativeFinite = model.nonNegativeFinite;
 25 
 26 pub fn commandTextSelection(src: command.CommandUiTextSelection, text_len: usize) !UiTextSelection {
 27     if (src.cursor_byte_offset > text_len) return error.InvalidCommand;
 28     if (src.selection_anchor_byte_offset > text_len) return error.InvalidCommand;
 29     if (src.selection_focus_byte_offset > text_len) return error.InvalidCommand;
 30     return .{
 31         .cursor_visible = src.cursor_visible != 0,
 32         .cursor_byte_offset = src.cursor_byte_offset,
 33         .selection_active = src.selection_active != 0,
 34         .selection_anchor_byte_offset = src.selection_anchor_byte_offset,
 35         .selection_focus_byte_offset = src.selection_focus_byte_offset,
 36     };
 37 }
 38 
 39 pub fn commandStyle(src: command.CommandUiStyle) !flex_layout.Style {
 40     return .{
 41         .flex_direction = try flexDirection(src.flex_direction),
 42         .wrap = try wrap(src.wrap),
 43         .justify_content = try justifyContent(src.justify_content),
 44         .align_items = try alignItems(src.align_items),
 45         .align_self = try alignSelf(src.align_self),
 46         .gap = src.gap,
 47         .padding = .{
 48             .top = optionalF32(src.padding.top),
 49             .left = optionalF32(src.padding.left),
 50             .right = optionalF32(src.padding.right),
 51             .bottom = optionalF32(src.padding.bottom),
 52         },
 53         .flex_grow = src.flex_grow,
 54         .flex_shrink = src.flex_shrink,
 55         .flex_basis = optionalF32(src.flex_basis),
 56         .constraints = .{
 57             .min_width = optionalF32(src.min_width),
 58             .min_height = optionalF32(src.min_height),
 59             .max_width = optionalF32(src.max_width),
 60             .max_height = optionalF32(src.max_height),
 61         },
 62         .position = try position(src.position),
 63         .inset = .{
 64             .top = optionalF32(src.inset.top),
 65             .left = optionalF32(src.inset.left),
 66             .right = optionalF32(src.inset.right),
 67             .bottom = optionalF32(src.inset.bottom),
 68         },
 69     };
 70 }
 71 
 72 pub fn commandScroll(src: command.CommandUiStyle) !UiScroll {
 73     return .{
 74         .overflow_x = try overflow(src.overflow_x),
 75         .overflow_y = try overflow(src.overflow_y),
 76         .scroll_x = nonNegativeFinite(src.scroll_x),
 77         .scroll_y = nonNegativeFinite(src.scroll_y),
 78     };
 79 }
 80 
 81 pub fn commandPaint(src: command.CommandUiPaint) UiPaint {
 82     return .{
 83         .background = optionalColor(src.background),
 84         .linear_gradient = commandLinearGradient(src.linear_gradient),
 85         .foreground = optionalColor(src.foreground),
 86         .border = optionalColor(src.border),
 87         .border_width = nonNegativeFinite(src.border_width),
 88         .corner_roundness = normalizedRoundness(src.corner_roundness),
 89         .border_edges = commandBorderPaint(src.border_edges),
 90         .shadow = commandShadowPaint(src.shadow),
 91         .image = commandImagePaint(src.image),
 92     };
 93 }
 94 
 95 fn commandImagePaint(src: command.CommandUiImagePaint) ?UiImagePaint {
 96     if (src.is_set == 0) return null;
 97     return .{
 98         .index = src.index,
 99         .source = .{
100             .x = finiteOrZero(src.source.x),
101             .y = finiteOrZero(src.source.y),
102             .width = nonNegativeFinite(src.source.width),
103             .height = nonNegativeFinite(src.source.height),
104         },
105         .tint = optionalColor(src.tint),
106         .opacity = src.opacity,
107     };
108 }
109 
110 fn commandLinearGradient(src: command.CommandUiLinearGradient) ?UiLinearGradient {
111     if (src.is_set == 0) return null;
112     return .{
113         .start = .{
114             .x = finiteOr(src.start.x, 0),
115             .y = finiteOr(src.start.y, 0),
116         },
117         .end = .{
118             .x = finiteOr(src.end.x, 1),
119             .y = finiteOr(src.end.y, 0),
120         },
121         .start_color = commandColor(src.start_color),
122         .end_color = commandColor(src.end_color),
123     };
124 }
125 
126 pub fn optionalF32(src: command.CommandOptionalF32) ?f32 {
127     return if (src.is_set == 0) null else src.value;
128 }
129 
130 pub fn textAlign(tag: u8) !UiTextAlign {
131     return switch (tag) {
132         0 => .start,
133         1 => .center,
134         2 => .end,
135         else => error.InvalidCommand,
136     };
137 }
138 
139 pub fn normalizeFontWeight(value: u16) u16 {
140     if (value == 0) return ui_font_weight_normal;
141     return std.math.clamp(value, ui_font_weight_min, ui_font_weight_max);
142 }
143 
144 pub fn commandTextRun(src: command.CommandUiTextRun) UiTextRun {
145     return .{
146         .byte_start = src.byte_start,
147         .byte_end = src.byte_end,
148         .style_slot = src.style_slot,
149         .foreground = optionalColor(src.foreground),
150         .background = optionalColor(src.background),
151         .underline = src.underline != 0,
152         .strikethrough = src.strikethrough != 0,
153     };
154 }
155 
156 pub fn commandTextStyle(src: command.CommandUiTextStyle) UiTextStyle {
157     return .{
158         .font_asset_id = src.font_asset_id,
159         .point_size = src.point_size,
160     };
161 }
162 
163 pub fn widgetKind(tag: u8) !WidgetKind {
164     return switch (tag) {
165         0 => .container,
166         1 => .label,
167         2 => .button,
168         3 => .text_input,
169         else => error.InvalidCommand,
170     };
171 }
172 
173 fn commandBorderPaint(src: command.CommandUiBorderPaint) UiBorderPaint {
174     return .{
175         .top = commandBorderEdgePaint(src.top),
176         .right = commandBorderEdgePaint(src.right),
177         .bottom = commandBorderEdgePaint(src.bottom),
178         .left = commandBorderEdgePaint(src.left),
179     };
180 }
181 
182 fn commandBorderEdgePaint(src: command.CommandUiBorderEdgePaint) UiBorderEdgePaint {
183     return .{
184         .color = optionalColor(src.color),
185         .width = nonNegativeFinite(src.width),
186     };
187 }
188 
189 fn commandShadowPaint(src: command.CommandUiShadowPaint) UiShadowPaint {
190     return .{
191         .color = optionalColor(src.color),
192         .offset_x = finiteOrZero(src.offset_x),
193         .offset_y = finiteOrZero(src.offset_y),
194         .blur_radius = nonNegativeFinite(src.blur_radius),
195         .spread = nonNegativeFinite(src.spread),
196     };
197 }
198 
199 fn optionalColor(src: command.CommandUiOptionalColor) ?UiColor {
200     if (src.is_set == 0) return null;
201     return commandColor(src.value);
202 }
203 
204 fn commandColor(src: command.CommandUiColor) UiColor {
205     return .{
206         .r = src.r,
207         .g = src.g,
208         .b = src.b,
209         .a = src.a,
210     };
211 }
212 
213 fn finiteOrZero(value: f32) f32 {
214     return finiteOr(value, 0);
215 }
216 
217 fn finiteOr(value: f32, fallback: f32) f32 {
218     return if (std.math.isFinite(value)) value else fallback;
219 }
220 
221 fn normalizedRoundness(value: f32) f32 {
222     if (!std.math.isFinite(value) or value <= 0) return 0;
223     return @min(value, 1);
224 }
225 
226 fn overflow(tag: u8) !UiOverflow {
227     return switch (tag) {
228         0 => .visible,
229         1 => .clip,
230         else => error.InvalidCommand,
231     };
232 }
233 
234 fn flexDirection(tag: u8) !flex_layout.FlexDirection {
235     return switch (tag) {
236         0 => .row,
237         1 => .column,
238         else => error.InvalidCommand,
239     };
240 }
241 
242 fn wrap(tag: u8) !flex_layout.Wrap {
243     return switch (tag) {
244         0 => .no_wrap,
245         1 => .wrap,
246         else => error.InvalidCommand,
247     };
248 }
249 
250 fn justifyContent(tag: u8) !flex_layout.JustifyContent {
251     return switch (tag) {
252         0 => .flex_start,
253         1 => .flex_end,
254         2 => .center,
255         3 => .space_between,
256         4 => .space_around,
257         else => error.InvalidCommand,
258     };
259 }
260 
261 fn alignItems(tag: u8) !flex_layout.AlignItems {
262     return switch (tag) {
263         0 => .flex_start,
264         1 => .flex_end,
265         2 => .center,
266         3 => .stretch,
267         else => error.InvalidCommand,
268     };
269 }
270 
271 fn alignSelf(tag: u8) !flex_layout.AlignSelf {
272     return switch (tag) {
273         0 => .auto,
274         1 => .flex_start,
275         2 => .flex_end,
276         3 => .center,
277         4 => .stretch,
278         else => error.InvalidCommand,
279     };
280 }
281 
282 fn position(tag: u8) !flex_layout.Position {
283     return switch (tag) {
284         0 => .relative,
285         1 => .absolute,
286         else => error.InvalidCommand,
287     };
288 }