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 }