Skip to main content

fui/node/
helpers.rs

1use super::core::*;
2use super::*;
3
4pub type Length = (f32, Unit);
5
6pub fn flex_box() -> FlexBox {
7    FlexBox::default()
8}
9
10pub fn text(content: impl Into<String>) -> TextNode {
11    TextNode::new(content)
12}
13
14pub fn grid() -> Grid {
15    Grid::default()
16}
17
18pub fn image(texture_id: u32) -> ImageNode {
19    ImageNode::new(texture_id)
20}
21
22pub fn svg(svg_id: u32) -> SvgNode {
23    SvgNode::new(svg_id)
24}
25
26pub fn scroll_view() -> ScrollView {
27    ScrollView::new()
28}
29
30pub fn scroll_box() -> ScrollBox {
31    ScrollBox::new()
32}
33
34pub fn virtual_list(total_items: i32, item_height: f32) -> VirtualList<FlexBox> {
35    VirtualList::new(total_items, item_height)
36}
37
38pub fn row() -> FlexBox {
39    let root = FlexBox::default();
40    root.flex_direction(FlexDirection::Row);
41    root
42}
43
44pub fn column() -> FlexBox {
45    let root = FlexBox::default();
46    root.flex_direction(FlexDirection::Column);
47    root
48}
49
50pub fn portal() -> Portal {
51    let root = FlexBox::default();
52    root.clip_to_bounds(false).portal(true);
53    root
54}
55
56pub fn custom_drawable(handler: impl Fn(&mut DrawContext) + 'static) -> CustomDrawable {
57    CustomDrawable::new(handler)
58}
59
60pub fn viewport_width() -> f32 {
61    ui::get_viewport_width()
62}
63
64pub fn viewport_height() -> f32 {
65    ui::get_viewport_height()
66}
67
68pub fn px(value: f32) -> Length {
69    (value, Unit::Pixel)
70}
71
72pub fn pct(value: f32) -> Length {
73    (value, Unit::Percent)
74}
75
76pub fn auto() -> Length {
77    (0.0, Unit::Auto)
78}
79
80pub fn fill() -> Length {
81    (100.0, Unit::Percent)
82}
83
84pub(crate) fn apply_flex_box_props(
85    handle: NodeHandle,
86    props: &FlexBoxProps,
87    behavior: NodeBehavior,
88) {
89    ui::set_fill_width(handle.raw(), behavior.fill_width);
90    ui::set_fill_height(handle.raw(), behavior.fill_height);
91    if let Some(percent) = behavior.fill_width_percent {
92        ui::set_fill_width_percent(handle.raw(), percent);
93    }
94    if let Some(percent) = behavior.fill_height_percent {
95        ui::set_fill_height_percent(handle.raw(), percent);
96    }
97    if let Some((width, unit)) = props.width {
98        ui::set_width(handle.raw(), width, unit as u32);
99    }
100    if let Some((height, unit)) = props.height {
101        ui::set_height(handle.raw(), height, unit as u32);
102    }
103    if let Some((value, unit)) = props.min_width {
104        ui::set_min_width(handle.raw(), value, unit as u32);
105    }
106    if let Some((value, unit)) = props.max_width {
107        ui::set_max_width(handle.raw(), value, unit as u32);
108    }
109    if let Some((value, unit)) = props.min_height {
110        ui::set_min_height(handle.raw(), value, unit as u32);
111    }
112    if let Some((value, unit)) = props.max_height {
113        ui::set_max_height(handle.raw(), value, unit as u32);
114    }
115    if let Some(style) = props.box_style {
116        ui::set_box_style(
117            handle.raw(),
118            props.bg_color.unwrap_or(0),
119            style.radius_tl,
120            style.radius_tr,
121            style.radius_br,
122            style.radius_bl,
123            style.border_width,
124            style.border_color,
125            style.border_style as u32,
126            style.border_dash_on,
127            style.border_dash_off,
128        );
129    } else if let Some(color) = props.bg_color {
130        ui::set_bg_color(handle.raw(), color);
131    }
132    if let Some((left, top, right, bottom)) = props.padding {
133        ui::set_padding(handle.raw(), left, top, right, bottom);
134    }
135    if let Some(direction) = props.flex_direction {
136        ui::set_flex_direction(handle.raw(), direction as u32);
137    }
138    if props.opacity.is_some() || props.blur_sigma.is_some() {
139        ui::set_layer_effect(
140            handle.raw(),
141            props.opacity.unwrap_or(1.0),
142            props.blur_sigma.unwrap_or(0.0),
143            0,
144        );
145    }
146    if let Some(shadow) = props.drop_shadow {
147        ui::set_drop_shadow(
148            handle.raw(),
149            shadow.color,
150            shadow.offset_x,
151            shadow.offset_y,
152            shadow.blur_sigma,
153            shadow.spread,
154        );
155    }
156    if let Some(sigma) = props.background_blur_sigma {
157        ui::set_background_blur(handle.raw(), sigma);
158    }
159    if let Some(gradient) = props.linear_gradient.as_ref() {
160        ui::set_linear_gradient(
161            handle.raw(),
162            gradient.sx,
163            gradient.sy,
164            gradient.ex,
165            gradient.ey,
166            &gradient.offsets,
167            &gradient.colors,
168        );
169    }
170    apply_behavior(handle, behavior);
171}
172
173pub(crate) fn apply_text_props(handle: NodeHandle, props: &TextProps, behavior: NodeBehavior) {
174    ui::set_text(handle.raw(), &props.content);
175    ui::set_fill_width(handle.raw(), behavior.fill_width);
176    ui::set_fill_height(handle.raw(), behavior.fill_height);
177    if let Some(percent) = behavior.fill_width_percent {
178        ui::set_fill_width_percent(handle.raw(), percent);
179    }
180    if let Some(percent) = behavior.fill_height_percent {
181        ui::set_fill_height_percent(handle.raw(), percent);
182    }
183    apply_behavior(handle, behavior);
184    if let Some((width, unit)) = props.width {
185        ui::set_width(handle.raw(), width, unit as u32);
186    }
187    if let Some((height, unit)) = props.height {
188        ui::set_height(handle.raw(), height, unit as u32);
189    }
190    if let Some((value, unit)) = props.min_width {
191        ui::set_min_width(handle.raw(), value, unit as u32);
192    }
193    if let Some((value, unit)) = props.max_width {
194        ui::set_max_width(handle.raw(), value, unit as u32);
195    }
196    if let Some((value, unit)) = props.min_height {
197        ui::set_min_height(handle.raw(), value, unit as u32);
198    }
199    if let Some((value, unit)) = props.max_height {
200        ui::set_max_height(handle.raw(), value, unit as u32);
201    }
202    if props.has_font {
203        ui::set_font(handle.raw(), props.font_id, props.font_size);
204    }
205    if props.has_style_runs {
206        ui::set_text_style_runs(handle.raw(), &props.style_runs);
207    }
208    ui::set_text_color(
209        handle.raw(),
210        props
211            .text_color
212            .unwrap_or_else(|| crate::theme::current_theme().colors.text_primary),
213    );
214    if let Some(line_height) = props.line_height {
215        ui::set_line_height(handle.raw(), line_height);
216    }
217    if let Some(text_align) = props.text_align {
218        ui::set_text_align(handle.raw(), text_align as u32);
219    }
220    if let Some(text_vertical_align) = props.text_vertical_align {
221        ui::set_text_vertical_align(handle.raw(), text_vertical_align as u32);
222    }
223    if let Some((max_chars, max_lines)) = props.text_limits {
224        ui::set_text_limits(handle.raw(), max_chars, max_lines);
225    }
226    if let Some(wrapping) = props.wrapping {
227        ui::set_text_wrapping(handle.raw(), wrapping);
228    }
229    if let Some(overflow) = props.overflow {
230        ui::set_text_overflow(handle.raw(), overflow as u32);
231    }
232    if let Some((horizontal, vertical)) = props.overflow_fade {
233        ui::set_text_overflow_fade(handle.raw(), horizontal, vertical);
234    }
235    if let Some((selectable, selection_color)) = props.selectable {
236        ui::set_selectable(handle.raw(), selectable, selection_color);
237    }
238    if let Some(editable) = props.editable {
239        ui::set_editable(handle.raw(), editable);
240    }
241    if let Some(enabled) = props.editor_command_keys {
242        ui::set_editor_command_keys(handle.raw(), enabled);
243    }
244    if let Some(enabled) = props.editor_accepts_tab {
245        ui::set_editor_accepts_tab(handle.raw(), enabled);
246    }
247    if let Some(obscured) = props.obscured {
248        ui::set_text_obscured(handle.raw(), obscured);
249    }
250    if let Some(caret_color) = props.caret_color {
251        ui::set_caret_color(handle.raw(), caret_color);
252    }
253    if let Some((start, end)) = props.selection_range_bytes {
254        ui::set_text_selection_range(handle.raw(), start, end);
255    }
256}
257
258pub(crate) fn apply_grid_props(handle: NodeHandle, props: &GridProps) {
259    ui::grid_set_columns(handle.raw(), &props.columns, &props.column_types);
260    ui::grid_set_rows(handle.raw(), &props.rows, &props.row_types);
261    for (index, group) in &props.column_shared_size_groups {
262        ui::grid_set_column_shared_size_group(handle.raw(), *index, group);
263    }
264    for (index, group) in &props.row_shared_size_groups {
265        ui::grid_set_row_shared_size_group(handle.raw(), *index, group);
266    }
267}
268
269pub(crate) fn apply_image_props(handle: NodeHandle, props: &ImageProps) {
270    if let Some((left, top, right, bottom)) = props.image_nine {
271        ui::set_image_nine(
272            handle.raw(),
273            props.texture_id,
274            left,
275            top,
276            right,
277            bottom,
278            props.sampling_kind,
279            props.max_aniso,
280        );
281    } else {
282        ui::set_image(
283            handle.raw(),
284            props.texture_id,
285            props.object_fit as u32,
286            props.sampling_kind,
287            props.max_aniso,
288        );
289    }
290}
291
292pub(crate) fn apply_svg_props(handle: NodeHandle, props: &SvgProps) {
293    ui::set_svg(
294        handle.raw(),
295        props.svg_id,
296        props.tint_color,
297        props.sampling_kind,
298        props.max_aniso,
299    );
300}
301
302pub(crate) fn apply_scroll_view_props(
303    handle: NodeHandle,
304    props: &ScrollViewProps,
305    behavior: NodeBehavior,
306) {
307    ui::set_fill_width(handle.raw(), behavior.fill_width);
308    ui::set_fill_height(handle.raw(), behavior.fill_height);
309    if let Some(percent) = behavior.fill_width_percent {
310        ui::set_fill_width_percent(handle.raw(), percent);
311    }
312    if let Some(percent) = behavior.fill_height_percent {
313        ui::set_fill_height_percent(handle.raw(), percent);
314    }
315    if let Some((width, unit)) = props.width {
316        ui::set_width(handle.raw(), width, unit as u32);
317    }
318    if let Some((height, unit)) = props.height {
319        ui::set_height(handle.raw(), height, unit as u32);
320    }
321    ui::set_scroll_enabled(handle.raw(), props.enable_scroll_x, props.enable_scroll_y);
322    ui::set_smooth_scrolling(handle.raw(), props.smooth_scrolling);
323    if let Some(friction) = props.friction {
324        ui::set_scroll_friction(handle.raw(), friction);
325    }
326    if let Some((offset_x, offset_y)) = props.scroll_offset {
327        ui::set_scroll_offset(handle.raw(), offset_x, offset_y);
328    }
329    if let Some((content_width, content_height)) = props.content_size {
330        ui::set_scroll_content_size(handle.raw(), content_width, content_height);
331    }
332    apply_behavior(handle, behavior);
333}
334
335pub(crate) fn apply_behavior(handle: NodeHandle, behavior: NodeBehavior) {
336    let effective_enabled = behavior.enabled && behavior.inherited_enabled;
337    if let Some(node_id) = behavior.node_id.as_deref() {
338        ui::set_node_id(handle.raw(), node_id);
339    }
340    if let Some(role) = behavior.semantic_role {
341        ui::set_semantic_role(handle.raw(), role as u32);
342    }
343    if let Some(label) = behavior
344        .semantic_label
345        .as_deref()
346        .or(behavior.default_semantic_label.as_deref())
347    {
348        ui::set_semantic_label(handle.raw(), label);
349    }
350    if let Some(disabled) = behavior.semantic_disabled {
351        ui::set_semantic_disabled(handle.raw(), true, disabled);
352    }
353    if let Some(state) = behavior.semantic_checked {
354        ui::set_semantic_checked(handle.raw(), state as u32);
355    }
356    if let Some(selected) = behavior.semantic_selected {
357        ui::set_semantic_selected(handle.raw(), true, selected);
358    }
359    if let Some(expanded) = behavior.semantic_expanded {
360        ui::set_semantic_expanded(handle.raw(), true, expanded);
361    }
362    if let Some((value_now, value_min, value_max)) = behavior.semantic_value_range {
363        ui::set_semantic_value_range(handle.raw(), true, value_now, value_min, value_max);
364    }
365    if let Some(orientation) = behavior.semantic_orientation {
366        ui::set_semantic_orientation(handle.raw(), orientation as u32);
367    }
368    if let Some(visibility) = behavior.visibility {
369        ui::set_visibility(handle.raw(), visibility as u32);
370    }
371    ui::set_is_portal(handle.raw(), behavior.is_portal);
372    if let Some((value, unit)) = behavior.min_width {
373        ui::set_min_width(handle.raw(), value, unit as u32);
374    }
375    if let Some((value, unit)) = behavior.max_width {
376        ui::set_max_width(handle.raw(), value, unit as u32);
377    }
378    if let Some((value, unit)) = behavior.min_height {
379        ui::set_min_height(handle.raw(), value, unit as u32);
380    }
381    if let Some((value, unit)) = behavior.max_height {
382        ui::set_max_height(handle.raw(), value, unit as u32);
383    }
384    if let Some(basis) = behavior.flex_basis {
385        ui::set_flex_basis(handle.raw(), basis);
386    }
387    if let Some(justify) = behavior.justify_content {
388        ui::set_justify_content(handle.raw(), justify as u32);
389    }
390    if let Some(align) = behavior.align_items {
391        ui::set_align_items(handle.raw(), align as u32);
392    }
393    if let Some(align) = behavior.align_self {
394        ui::set_align_self(handle.raw(), align as u32);
395    }
396    if let Some((left, top, right, bottom)) = behavior.margin {
397        ui::set_margin(handle.raw(), left, top, right, bottom);
398    }
399    if let Some(position_type) = behavior.position_type {
400        ui::set_position_type(handle.raw(), position_type as u32);
401    }
402    if let Some((left, top, right, bottom)) = behavior.position {
403        ui::set_position(handle.raw(), left, top, right, bottom);
404    }
405    ui::set_is_shared_size_scope(handle.raw(), behavior.is_shared_size_scope);
406    ui::set_custom_drawable(handle.raw(), behavior.custom_drawable);
407    if let Some(wrap) = behavior.flex_wrap {
408        ui::set_flex_wrap(handle.raw(), wrap as u32);
409    }
410    if let Some(clip) = behavior.clip_to_bounds {
411        ui::set_clip_to_bounds(handle.raw(), clip);
412    }
413    if behavior.selection_area {
414        ui::set_selection_area(handle.raw(), true);
415    }
416    if behavior.selection_area_barrier {
417        ui::set_selection_area_barrier(handle.raw(), true);
418    }
419    if behavior.preserve_selection_on_pointer_down {
420        ui::set_preserve_selection_on_pointer_down(handle.raw(), true);
421    }
422    if let Some(scroll_handle) = behavior.scroll_proxy_target {
423        ui::set_scroll_proxy_target(handle.raw(), scroll_handle);
424    }
425    ui::set_interactive(handle.raw(), effective_enabled && behavior.interactive);
426    if let Some((enabled, tab_index)) = behavior.focusable {
427        ui::set_focusable(handle.raw(), effective_enabled && enabled, tab_index);
428    }
429    if behavior.track_semantic_disabled_from_enabled {
430        ui::set_semantic_disabled(handle.raw(), true, !effective_enabled);
431    }
432}