Skip to main content

fui/node/
flex_box.rs

1use super::core::*;
2use super::*;
3use crate::animation::{animate_color, animate_float, Animation};
4use crate::transitions::NodeTransitions;
5
6#[derive(Default)]
7pub(crate) struct FlexBoxAnimations {
8    pub(crate) opacity: Option<Animation>,
9    pub(crate) background_color: Option<Animation>,
10}
11
12#[derive(Clone)]
13pub struct FlexBox {
14    pub(crate) core: Rc<RefCell<NodeCore>>,
15    pub(crate) props: Rc<RefCell<FlexBoxProps>>,
16    pub(crate) active_animations: Rc<RefCell<FlexBoxAnimations>>,
17    pub(crate) host_style_layers: Rc<RefCell<HostStyleLayers>>,
18}
19
20impl Default for FlexBox {
21    fn default() -> Self {
22        let mut core = NodeCore::new(NodeKind::FlexBox);
23        core.behavior.clip_to_bounds = Some(true);
24        Self {
25            core: Rc::new(RefCell::new(core)),
26            props: Rc::new(RefCell::new(FlexBoxProps::default())),
27            active_animations: Rc::new(RefCell::new(FlexBoxAnimations::default())),
28            host_style_layers: Rc::new(RefCell::new(HostStyleLayers::default())),
29        }
30    }
31}
32
33impl Node for FlexBox {
34    fn retained_node_ref(&self) -> NodeRef {
35        NodeRef::from_node(self.core.clone(), self.clone())
36    }
37
38    fn build_self(&self) {
39        let props = self.props.borrow().clone();
40        let behavior = self.core.borrow().behavior.clone();
41        apply_flex_box_props(self.handle(), &props, behavior);
42    }
43}
44
45#[derive(Clone, Copy, Debug, PartialEq)]
46pub struct Border {
47    pub width: f32,
48    pub color: u32,
49    pub style: BorderStyle,
50    pub dash_on: f32,
51    pub dash_off: f32,
52}
53
54impl Border {
55    pub fn solid(width: f32, color: u32) -> Self {
56        Self {
57            width,
58            color,
59            style: BorderStyle::Solid,
60            dash_on: 0.0,
61            dash_off: 0.0,
62        }
63    }
64
65    pub fn dashed(width: f32, color: u32, dash_on: f32, dash_off: f32) -> Self {
66        Self {
67            width,
68            color,
69            style: BorderStyle::Dashed,
70            dash_on,
71            dash_off,
72        }
73    }
74
75    pub fn dotted(width: f32, color: u32, dash_on: f32, dash_off: f32) -> Self {
76        Self {
77            width,
78            color,
79            style: BorderStyle::Dotted,
80            dash_on,
81            dash_off,
82        }
83    }
84}
85
86#[derive(Clone, Copy, Debug, PartialEq)]
87pub struct GradientStop {
88    pub offset: f32,
89    pub color: u32,
90}
91
92impl GradientStop {
93    pub fn new(offset: f32, color: u32) -> Self {
94        Self { offset, color }
95    }
96}
97
98impl FlexBox {
99    pub(crate) fn downgrade(&self) -> WeakFlexBox {
100        WeakFlexBox {
101            core: Rc::downgrade(&self.core),
102            props: Rc::downgrade(&self.props),
103            active_animations: Rc::downgrade(&self.active_animations),
104            host_style_layers: Rc::downgrade(&self.host_style_layers),
105        }
106    }
107
108    pub fn key(&self, _key: u64) -> &Self {
109        self
110    }
111
112    pub fn width(&self, width: f32, unit: Unit) -> &Self {
113        self.props.borrow_mut().width = Some((width, unit));
114        {
115            let mut core = self.core.borrow_mut();
116            core.behavior.fill_width = false;
117            core.behavior.fill_width_percent = None;
118        }
119        if self.has_built_handle() {
120            ui::set_width(self.handle().raw(), width, unit as u32);
121            self.notify_retained_layout_mutation();
122        }
123        self
124    }
125
126    pub fn width_len(&self, length: Length) -> &Self {
127        let (width, unit) = length;
128        self.width(width, unit)
129    }
130
131    pub fn height(&self, height: f32, unit: Unit) -> &Self {
132        self.props.borrow_mut().height = Some((height, unit));
133        {
134            let mut core = self.core.borrow_mut();
135            core.behavior.fill_height = false;
136            core.behavior.fill_height_percent = None;
137        }
138        if self.has_built_handle() {
139            ui::set_height(self.handle().raw(), height, unit as u32);
140            self.notify_retained_layout_mutation();
141        }
142        self
143    }
144
145    pub fn height_len(&self, length: Length) -> &Self {
146        let (height, unit) = length;
147        self.height(height, unit)
148    }
149
150    pub fn fill_width(&self) -> &Self {
151        self.props.borrow_mut().width = None;
152        {
153            let mut core = self.core.borrow_mut();
154            core.behavior.fill_width = true;
155            core.behavior.fill_width_percent = None;
156        }
157        if self.has_built_handle() {
158            ui::set_fill_width(self.handle().raw(), true);
159            self.notify_retained_layout_mutation();
160        }
161        self
162    }
163
164    pub fn fill_height(&self) -> &Self {
165        self.props.borrow_mut().height = None;
166        {
167            let mut core = self.core.borrow_mut();
168            core.behavior.fill_height = true;
169            core.behavior.fill_height_percent = None;
170        }
171        if self.has_built_handle() {
172            ui::set_fill_height(self.handle().raw(), true);
173            self.notify_retained_layout_mutation();
174        }
175        self
176    }
177
178    pub fn fill_size(&self) -> &Self {
179        self.fill_width();
180        self.fill_height();
181        self
182    }
183
184    pub fn fill_width_percent(&self, percent: f32) -> &Self {
185        self.props.borrow_mut().width = None;
186        {
187            let mut core = self.core.borrow_mut();
188            core.behavior.fill_width = false;
189            core.behavior.fill_width_percent = Some(percent);
190        }
191        if self.has_built_handle() {
192            ui::set_fill_width_percent(self.handle().raw(), percent);
193            self.notify_retained_layout_mutation();
194        }
195        self
196    }
197
198    pub fn fill_height_percent(&self, percent: f32) -> &Self {
199        self.props.borrow_mut().height = None;
200        {
201            let mut core = self.core.borrow_mut();
202            core.behavior.fill_height = false;
203            core.behavior.fill_height_percent = Some(percent);
204        }
205        if self.has_built_handle() {
206            ui::set_fill_height_percent(self.handle().raw(), percent);
207            self.notify_retained_layout_mutation();
208        }
209        self
210    }
211
212    pub fn bg_color(&self, color: u32) -> &Self {
213        self.host_style_layers.borrow_mut().local.background = Some(color);
214        self.set_effective_background_color(color)
215    }
216
217    fn set_effective_background_color(&self, color: u32) -> &Self {
218        self.cancel_background_color_transition();
219        if self.should_animate_background_color(color) {
220            let timing = {
221                self.props
222                    .borrow()
223                    .transitions
224                    .as_ref()
225                    .and_then(NodeTransitions::background_color_timing)
226            };
227            if let Some(timing) = timing {
228                let weak = self.downgrade();
229                let from = self.current_background_color();
230                let animation = animate_color(from, color, timing, move |value| {
231                    if let Some(node) = weak.upgrade() {
232                        node.apply_animated_background_color(value);
233                    }
234                });
235                self.active_animations.borrow_mut().background_color = Some(animation);
236                return self;
237            }
238        }
239        self.apply_animated_background_color(color);
240        self
241    }
242
243    pub fn padding(&self, left: f32, top: f32, right: f32, bottom: f32) -> &Self {
244        self.host_style_layers.borrow_mut().local.padding =
245            Some(EdgeInsets::new(left, top, right, bottom));
246        self.props.borrow_mut().padding = Some((left, top, right, bottom));
247        if self.has_built_handle() {
248            ui::set_padding(self.handle().raw(), left, top, right, bottom);
249            self.notify_retained_layout_mutation();
250        }
251        self
252    }
253
254    pub fn flex_direction(&self, direction: FlexDirection) -> &Self {
255        self.host_style_layers.borrow_mut().local.flex_direction = Some(direction);
256        self.props.borrow_mut().flex_direction = Some(direction);
257        if self.has_built_handle() {
258            ui::set_flex_direction(self.handle().raw(), direction as u32);
259            self.notify_retained_layout_mutation();
260        }
261        self
262    }
263
264    pub fn corner_radius(&self, radius: f32) -> &Self {
265        self.corners(radius, radius, radius, radius)
266    }
267
268    pub fn corners(&self, tl: f32, tr: f32, br: f32, bl: f32) -> &Self {
269        self.host_style_layers.borrow_mut().local.corners = Some(Corners::new(tl, tr, br, bl));
270        let existing = self.props.borrow().box_style.unwrap_or(BoxStyle {
271            radius_tl: 0.0,
272            radius_tr: 0.0,
273            radius_br: 0.0,
274            radius_bl: 0.0,
275            border_width: 0.0,
276            border_color: 0,
277            border_style: BorderStyle::Solid,
278            border_dash_on: 0.0,
279            border_dash_off: 0.0,
280        });
281        self.props.borrow_mut().box_style = Some(BoxStyle {
282            radius_tl: tl,
283            radius_tr: tr,
284            radius_br: br,
285            radius_bl: bl,
286            ..existing
287        });
288        if self.has_built_handle() {
289            self.build_self();
290            self.notify_retained_mutation();
291        }
292        self
293    }
294
295    pub fn border(&self, width: f32, color: u32) -> &Self {
296        self.border_config(Border::solid(width, color))
297    }
298
299    pub fn border_config(&self, border: Border) -> &Self {
300        self.host_style_layers.borrow_mut().local.border = Some(border);
301        let existing = self.props.borrow().box_style.unwrap_or(BoxStyle {
302            radius_tl: 0.0,
303            radius_tr: 0.0,
304            radius_br: 0.0,
305            radius_bl: 0.0,
306            border_width: 0.0,
307            border_color: 0,
308            border_style: BorderStyle::Solid,
309            border_dash_on: 0.0,
310            border_dash_off: 0.0,
311        });
312        self.props.borrow_mut().box_style = Some(BoxStyle {
313            border_width: border.width,
314            border_color: border.color,
315            border_style: border.style,
316            border_dash_on: border.dash_on,
317            border_dash_off: border.dash_off,
318            ..existing
319        });
320        if self.has_built_handle() {
321            self.build_self();
322            self.notify_retained_mutation();
323        }
324        self
325    }
326
327    pub fn opacity(&self, value: f32) -> &Self {
328        let value = value.clamp(0.0, 1.0);
329        self.host_style_layers.borrow_mut().local.opacity = Some(value);
330        self.set_effective_opacity(value)
331    }
332
333    fn set_effective_opacity(&self, value: f32) -> &Self {
334        self.cancel_opacity_transition();
335        if self.should_animate_opacity(value) {
336            let timing = {
337                self.props
338                    .borrow()
339                    .transitions
340                    .as_ref()
341                    .and_then(NodeTransitions::opacity_timing)
342            };
343            if let Some(timing) = timing {
344                let weak = self.downgrade();
345                let from = self.current_opacity();
346                let animation = animate_float(from, value, timing, move |next| {
347                    if let Some(node) = weak.upgrade() {
348                        node.apply_animated_opacity(next);
349                    }
350                });
351                self.active_animations.borrow_mut().opacity = Some(animation);
352                return self;
353            }
354        }
355        self.apply_animated_opacity(value);
356        self
357    }
358
359    pub fn transitions(&self, transitions: Option<NodeTransitions>) -> &Self {
360        self.props.borrow_mut().transitions = transitions;
361        self
362    }
363
364    pub fn blur(&self, sigma: f32) -> &Self {
365        self.props.borrow_mut().blur_sigma = Some(sigma.max(0.0));
366        if self.has_built_handle() {
367            self.build_self();
368            self.notify_retained_mutation();
369        }
370        self
371    }
372
373    pub fn drop_shadow(
374        &self,
375        color: u32,
376        offset_x: f32,
377        offset_y: f32,
378        blur_sigma: f32,
379        spread: f32,
380    ) -> &Self {
381        self.host_style_layers.borrow_mut().local.shadow =
382            Some(Shadow::new(color, offset_x, offset_y, blur_sigma, spread));
383        self.props.borrow_mut().drop_shadow = Some(DropShadow {
384            color,
385            offset_x,
386            offset_y,
387            blur_sigma: blur_sigma.max(0.0),
388            spread,
389        });
390        if self.has_built_handle() {
391            self.build_self();
392            self.notify_retained_mutation();
393        }
394        self
395    }
396
397    pub fn background_blur(&self, sigma: f32) -> &Self {
398        self.props.borrow_mut().background_blur_sigma = Some(sigma.max(0.0));
399        if self.has_built_handle() {
400            self.build_self();
401            self.notify_retained_mutation();
402        }
403        self
404    }
405
406    pub fn linear_gradient(
407        &self,
408        sx: f32,
409        sy: f32,
410        ex: f32,
411        ey: f32,
412        offsets: Vec<f32>,
413        colors: Vec<u32>,
414    ) -> &Self {
415        self.props.borrow_mut().linear_gradient = Some(LinearGradient {
416            sx,
417            sy,
418            ex,
419            ey,
420            offsets,
421            colors,
422        });
423        if self.has_built_handle() {
424            self.build_self();
425            self.notify_retained_mutation();
426        }
427        self
428    }
429
430    pub fn linear_gradient_stops(
431        &self,
432        sx: f32,
433        sy: f32,
434        ex: f32,
435        ey: f32,
436        stops: Vec<GradientStop>,
437    ) -> &Self {
438        let mut offsets = Vec::with_capacity(stops.len());
439        let mut colors = Vec::with_capacity(stops.len());
440        for stop in stops {
441            offsets.push(stop.offset);
442            colors.push(stop.color);
443        }
444        self.linear_gradient(sx, sy, ex, ey, offsets, colors)
445    }
446
447    pub fn interactive(&self, interactive: bool) -> &Self {
448        let mut core = self.core.borrow_mut();
449        core.behavior.interactive = interactive;
450        let enabled = core.behavior.enabled && core.behavior.inherited_enabled;
451        let has_built_handle = core.handle != NodeHandle::INVALID;
452        drop(core);
453        if has_built_handle {
454            ui::set_interactive(self.handle().raw(), enabled && interactive);
455            self.notify_retained_mutation();
456        }
457        self
458    }
459
460    pub fn enabled(&self, enabled: bool) -> &Self {
461        self.retained_node_ref().set_own_enabled(enabled);
462        self
463    }
464
465    pub(crate) fn reflect_semantic_disabled_from_enabled(&self) -> &Self {
466        let mut core = self.core.borrow_mut();
467        core.behavior.track_semantic_disabled_from_enabled = true;
468        let enabled = core.behavior.enabled && core.behavior.inherited_enabled;
469        let has_built_handle = core.handle != NodeHandle::INVALID;
470        drop(core);
471        if has_built_handle {
472            ui::set_semantic_disabled(self.handle().raw(), true, !enabled);
473            self.notify_retained_mutation();
474        }
475        self
476    }
477
478    pub fn focusable(&self, enabled: bool, tab_index: i32) -> &Self {
479        Node::focusable(self, enabled, tab_index);
480        self
481    }
482
483    pub fn cursor(&self, style: CursorStyle) -> &Self {
484        self.host_style_layers.borrow_mut().local.cursor = Some(style);
485        self.core.borrow_mut().behavior.cursor = Some(style);
486        crate::event::handle_cursor_style_changed(self.handle());
487        self
488    }
489
490    fn current_background_color(&self) -> u32 {
491        self.props.borrow().bg_color.unwrap_or(0)
492    }
493
494    fn current_opacity(&self) -> f32 {
495        self.props.borrow().opacity.unwrap_or(1.0)
496    }
497
498    fn apply_animated_background_color(&self, color: u32) {
499        self.props.borrow_mut().bg_color = Some(color);
500        if self.has_built_handle() {
501            ui::set_bg_color(self.handle().raw(), color);
502            self.notify_retained_mutation();
503        }
504    }
505
506    fn apply_animated_opacity(&self, value: f32) {
507        let value = value.clamp(0.0, 1.0);
508        self.props.borrow_mut().opacity = Some(value);
509        if self.has_built_handle() {
510            ui::set_layer_effect(
511                self.handle().raw(),
512                value,
513                self.props.borrow().blur_sigma.unwrap_or(0.0),
514                0,
515            );
516            self.notify_retained_mutation();
517        }
518    }
519
520    fn should_animate_opacity(&self, next: f32) -> bool {
521        self.has_built_handle()
522            && self
523                .props
524                .borrow()
525                .transitions
526                .as_ref()
527                .and_then(NodeTransitions::opacity_timing)
528                .is_some()
529            && (self.current_opacity() - next).abs() > f32::EPSILON
530    }
531
532    fn should_animate_background_color(&self, next: u32) -> bool {
533        self.has_built_handle()
534            && self
535                .props
536                .borrow()
537                .transitions
538                .as_ref()
539                .and_then(NodeTransitions::background_color_timing)
540                .is_some()
541            && self.current_background_color() != next
542    }
543
544    fn cancel_opacity_transition(&self) {
545        if let Some(animation) = self.active_animations.borrow_mut().opacity.take() {
546            animation.cancel();
547        }
548    }
549
550    fn cancel_background_color_transition(&self) {
551        if let Some(animation) = self.active_animations.borrow_mut().background_color.take() {
552            animation.cancel();
553        }
554    }
555
556    pub fn node_id(&self, node_id: impl Into<String>) -> &Self {
557        let node_id = node_id.into();
558        self.core.borrow_mut().behavior.node_id = Some(node_id.clone());
559        if self.has_built_handle() {
560            ui::set_node_id(self.handle().raw(), &node_id);
561            self.notify_retained_mutation();
562        }
563        self
564    }
565
566    pub fn semantic_role(&self, role: SemanticRole) -> &Self {
567        self.core.borrow_mut().behavior.semantic_role = Some(role);
568        if self.has_built_handle() {
569            ui::set_semantic_role(self.handle().raw(), role as u32);
570            self.notify_retained_mutation();
571        }
572        self
573    }
574
575    pub fn semantic_label(&self, label: impl Into<String>) -> &Self {
576        let label = label.into();
577        self.core.borrow_mut().behavior.semantic_label = Some(label.clone());
578        if self.has_built_handle() {
579            ui::set_semantic_label(self.handle().raw(), &label);
580            self.notify_retained_mutation();
581        }
582        self
583    }
584
585    pub(crate) fn default_semantic_label(&self, label: impl Into<String>) -> &Self {
586        let label = label.into();
587        let should_emit = {
588            let mut core = self.core.borrow_mut();
589            core.behavior.default_semantic_label = Some(label.clone());
590            core.behavior.semantic_label.is_none()
591        };
592        if should_emit && self.has_built_handle() {
593            ui::set_semantic_label(self.handle().raw(), &label);
594            self.notify_retained_mutation();
595        }
596        self
597    }
598
599    pub fn semantic_disabled(&self, disabled: bool) -> &Self {
600        Node::semantic_disabled(self, disabled);
601        self
602    }
603
604    pub fn semantic_checked(&self, state: SemanticCheckedState) -> &Self {
605        Node::semantic_checked(self, state);
606        self
607    }
608
609    pub fn semantic_selected(&self, selected: bool) -> &Self {
610        Node::semantic_selected(self, selected);
611        self
612    }
613
614    pub fn semantic_value_range(&self, value_now: f32, value_min: f32, value_max: f32) -> &Self {
615        Node::semantic_value_range(self, value_now, value_min, value_max);
616        self
617    }
618
619    pub fn semantic_orientation(&self, orientation: Orientation) -> &Self {
620        Node::semantic_orientation(self, orientation);
621        self
622    }
623
624    pub fn request_semantic_announcement(&self) -> &Self {
625        Node::request_semantic_announcement(self);
626        self
627    }
628
629    pub fn visibility(&self, visibility: Visibility) -> &Self {
630        Node::visibility(self, visibility);
631        self
632    }
633
634    pub fn portal(&self, is_portal: bool) -> &Self {
635        self.core.borrow_mut().behavior.is_portal = is_portal;
636        if self.has_built_handle() {
637            ui::set_is_portal(self.handle().raw(), is_portal);
638            self.notify_retained_mutation();
639        }
640        self
641    }
642
643    pub fn min_width(&self, value: f32, unit: Unit) -> &Self {
644        self.core.borrow_mut().behavior.min_width = Some((value, unit));
645        if self.has_built_handle() {
646            ui::set_min_width(self.handle().raw(), value, unit as u32);
647            self.notify_retained_layout_mutation();
648        }
649        self
650    }
651
652    pub fn min_width_len(&self, length: Length) -> &Self {
653        let (value, unit) = length;
654        self.min_width(value, unit)
655    }
656
657    pub fn max_width(&self, value: f32, unit: Unit) -> &Self {
658        self.core.borrow_mut().behavior.max_width = Some((value, unit));
659        if self.has_built_handle() {
660            ui::set_max_width(self.handle().raw(), value, unit as u32);
661            self.notify_retained_layout_mutation();
662        }
663        self
664    }
665
666    pub fn max_width_len(&self, length: Length) -> &Self {
667        let (value, unit) = length;
668        self.max_width(value, unit)
669    }
670
671    pub fn min_height(&self, value: f32, unit: Unit) -> &Self {
672        self.core.borrow_mut().behavior.min_height = Some((value, unit));
673        if self.has_built_handle() {
674            ui::set_min_height(self.handle().raw(), value, unit as u32);
675            self.notify_retained_layout_mutation();
676        }
677        self
678    }
679
680    pub fn min_height_len(&self, length: Length) -> &Self {
681        let (value, unit) = length;
682        self.min_height(value, unit)
683    }
684
685    pub fn max_height(&self, value: f32, unit: Unit) -> &Self {
686        self.core.borrow_mut().behavior.max_height = Some((value, unit));
687        if self.has_built_handle() {
688            ui::set_max_height(self.handle().raw(), value, unit as u32);
689            self.notify_retained_layout_mutation();
690        }
691        self
692    }
693
694    pub fn max_height_len(&self, length: Length) -> &Self {
695        let (value, unit) = length;
696        self.max_height(value, unit)
697    }
698
699    pub fn flex_basis(&self, basis: f32) -> &Self {
700        self.core.borrow_mut().behavior.flex_basis = Some(basis);
701        if self.has_built_handle() {
702            ui::set_flex_basis(self.handle().raw(), basis);
703            self.notify_retained_layout_mutation();
704        }
705        self
706    }
707
708    pub fn justify_content(&self, justify: JustifyContent) -> &Self {
709        self.host_style_layers.borrow_mut().local.justify_content = Some(justify);
710        self.core.borrow_mut().behavior.justify_content = Some(justify);
711        if self.has_built_handle() {
712            ui::set_justify_content(self.handle().raw(), justify as u32);
713            self.notify_retained_mutation();
714        }
715        self
716    }
717
718    pub fn align_items(&self, align: AlignItems) -> &Self {
719        self.host_style_layers.borrow_mut().local.align_items = Some(align);
720        self.core.borrow_mut().behavior.align_items = Some(align);
721        if self.has_built_handle() {
722            ui::set_align_items(self.handle().raw(), align as u32);
723            self.notify_retained_mutation();
724        }
725        self
726    }
727
728    pub fn align_self(&self, align: AlignSelf) -> &Self {
729        self.core.borrow_mut().behavior.align_self = Some(align);
730        if self.has_built_handle() {
731            ui::set_align_self(self.handle().raw(), align as u32);
732            self.notify_retained_mutation();
733        }
734        self
735    }
736
737    pub fn margin(&self, left: f32, top: f32, right: f32, bottom: f32) -> &Self {
738        self.core.borrow_mut().behavior.margin = Some((left, top, right, bottom));
739        if self.has_built_handle() {
740            ui::set_margin(self.handle().raw(), left, top, right, bottom);
741            self.notify_retained_layout_mutation();
742        }
743        self
744    }
745
746    pub fn apply_presenter_style(&self, style: PresenterHostStyle) -> &Self {
747        let previous = self.host_style_layers.borrow().resolved();
748        self.host_style_layers.borrow_mut().presenter = style;
749        self.sync_resolved_host_style_if_changed(previous);
750        self
751    }
752
753    pub fn clear_presenter_style(&self) -> &Self {
754        self.apply_presenter_style(PresenterHostStyle::new())
755    }
756
757    pub fn clear_bg_color(&self) -> &Self {
758        self.clear_local_host_style(|style| style.background = None)
759    }
760
761    pub fn clear_padding(&self) -> &Self {
762        self.clear_local_host_style(|style| style.padding = None)
763    }
764
765    pub fn clear_corners(&self) -> &Self {
766        self.clear_local_host_style(|style| style.corners = None)
767    }
768
769    pub fn clear_border(&self) -> &Self {
770        self.clear_local_host_style(|style| style.border = None)
771    }
772
773    pub fn clear_drop_shadow(&self) -> &Self {
774        self.clear_local_host_style(|style| style.shadow = None)
775    }
776
777    pub fn clear_opacity(&self) -> &Self {
778        self.clear_local_host_style(|style| style.opacity = None)
779    }
780
781    pub fn clear_flex_direction(&self) -> &Self {
782        self.clear_local_host_style(|style| style.flex_direction = None)
783    }
784
785    pub fn clear_justify_content(&self) -> &Self {
786        self.clear_local_host_style(|style| style.justify_content = None)
787    }
788
789    pub fn clear_align_items(&self) -> &Self {
790        self.clear_local_host_style(|style| style.align_items = None)
791    }
792
793    pub fn clear_cursor(&self) -> &Self {
794        self.clear_local_host_style(|style| style.cursor = None)
795    }
796
797    pub fn resolved_host_style(&self) -> PresenterHostStyle {
798        self.host_style_layers.borrow().resolved()
799    }
800
801    fn clear_local_host_style(&self, clear: impl FnOnce(&mut PresenterHostStyle)) -> &Self {
802        let previous = self.host_style_layers.borrow().resolved();
803        clear(&mut self.host_style_layers.borrow_mut().local);
804        self.sync_resolved_host_style_if_changed(previous);
805        self
806    }
807
808    fn sync_resolved_host_style_if_changed(&self, previous: PresenterHostStyle) {
809        let resolved = self.host_style_layers.borrow().resolved();
810        if resolved == previous {
811            return;
812        }
813        self.sync_resolved_host_style(previous, resolved);
814    }
815
816    fn sync_resolved_host_style(&self, previous: PresenterHostStyle, resolved: PresenterHostStyle) {
817        let background_changed = previous.background != resolved.background;
818        let padding_changed = previous.padding != resolved.padding;
819        let flex_direction_changed = previous.flex_direction != resolved.flex_direction;
820        let justify_content_changed = previous.justify_content != resolved.justify_content;
821        let align_items_changed = previous.align_items != resolved.align_items;
822        let box_style_changed =
823            previous.corners != resolved.corners || previous.border != resolved.border;
824        let opacity_changed = previous.opacity != resolved.opacity;
825        let shadow_changed = previous.shadow != resolved.shadow;
826        let cursor_changed = previous.cursor != resolved.cursor;
827
828        {
829            let mut props = self.props.borrow_mut();
830            props.padding = resolved
831                .padding
832                .map(|value| (value.left, value.top, value.right, value.bottom));
833            props.flex_direction = resolved.flex_direction;
834            props.opacity = resolved.opacity;
835            props.drop_shadow = resolved.shadow.map(|value| DropShadow {
836                color: value.color,
837                offset_x: value.offset_x,
838                offset_y: value.offset_y,
839                blur_sigma: value.blur_sigma,
840                spread: value.spread,
841            });
842            props.box_style = if resolved.corners.is_some() || resolved.border.is_some() {
843                let corners = resolved.corners.unwrap_or_else(|| Corners::all(0.0));
844                let border = resolved
845                    .border
846                    .unwrap_or_else(|| Border::solid(0.0, 0x00000000));
847                Some(BoxStyle {
848                    radius_tl: corners.top_left,
849                    radius_tr: corners.top_right,
850                    radius_br: corners.bottom_right,
851                    radius_bl: corners.bottom_left,
852                    border_width: border.width,
853                    border_color: border.color,
854                    border_style: border.style,
855                    border_dash_on: border.dash_on,
856                    border_dash_off: border.dash_off,
857                })
858            } else {
859                None
860            };
861        }
862        {
863            let mut core = self.core.borrow_mut();
864            core.behavior.justify_content = resolved.justify_content;
865            core.behavior.align_items = resolved.align_items;
866            core.behavior.cursor = resolved.cursor;
867        }
868        if !self.has_built_handle() {
869            self.props.borrow_mut().bg_color = resolved.background;
870            self.props.borrow_mut().opacity = resolved.opacity;
871            return;
872        }
873
874        let handle = self.handle().raw();
875        let mut direct_native_change = false;
876        if box_style_changed {
877            let corners = resolved.corners.unwrap_or_else(|| Corners::all(0.0));
878            let border = resolved
879                .border
880                .unwrap_or_else(|| Border::solid(0.0, 0x00000000));
881            ui::set_box_style(
882                handle,
883                if background_changed {
884                    previous.background.unwrap_or(0x00000000)
885                } else {
886                    resolved.background.unwrap_or(0x00000000)
887                },
888                corners.top_left,
889                corners.top_right,
890                corners.bottom_right,
891                corners.bottom_left,
892                border.width,
893                border.color,
894                border.style as u32,
895                border.dash_on,
896                border.dash_off,
897            );
898            direct_native_change = true;
899        }
900        if padding_changed {
901            let padding = resolved.padding.unwrap_or_else(|| EdgeInsets::all(0.0));
902            ui::set_padding(
903                handle,
904                padding.left,
905                padding.top,
906                padding.right,
907                padding.bottom,
908            );
909            direct_native_change = true;
910        }
911        if flex_direction_changed {
912            ui::set_flex_direction(
913                handle,
914                resolved.flex_direction.unwrap_or(FlexDirection::Column) as u32,
915            );
916            direct_native_change = true;
917        }
918        if justify_content_changed {
919            ui::set_justify_content(
920                handle,
921                resolved.justify_content.unwrap_or(JustifyContent::Start) as u32,
922            );
923            direct_native_change = true;
924        }
925        if align_items_changed {
926            ui::set_align_items(
927                handle,
928                resolved.align_items.unwrap_or(AlignItems::Stretch) as u32,
929            );
930            direct_native_change = true;
931        }
932        if shadow_changed {
933            let shadow = resolved
934                .shadow
935                .unwrap_or_else(|| Shadow::new(0x00000000, 0.0, 0.0, 0.0, 0.0));
936            ui::set_drop_shadow(
937                handle,
938                shadow.color,
939                shadow.offset_x,
940                shadow.offset_y,
941                shadow.blur_sigma,
942                shadow.spread,
943            );
944            direct_native_change = true;
945        }
946        if cursor_changed {
947            crate::event::handle_cursor_style_changed(self.handle());
948        }
949        if direct_native_change {
950            self.notify_retained_mutation();
951        }
952        if background_changed {
953            self.set_effective_background_color(resolved.background.unwrap_or(0x00000000));
954        }
955        if opacity_changed {
956            self.set_effective_opacity(resolved.opacity.unwrap_or(1.0));
957        }
958    }
959
960    pub fn position_type(&self, position_type: PositionType) -> &Self {
961        self.core.borrow_mut().behavior.position_type = Some(position_type);
962        if self.has_built_handle() {
963            ui::set_position_type(self.handle().raw(), position_type as u32);
964            self.notify_retained_layout_mutation();
965        }
966        self
967    }
968
969    pub fn position(&self, left: f32, top: f32) -> &Self {
970        self.core.borrow_mut().behavior.position = Some((left, top, f32::NAN, f32::NAN));
971        if self.has_built_handle() {
972            ui::set_position(self.handle().raw(), left, top, f32::NAN, f32::NAN);
973            self.notify_retained_layout_mutation();
974        }
975        self
976    }
977
978    pub fn custom_drawable(&self, enabled: bool) -> &Self {
979        self.core.borrow_mut().behavior.custom_drawable = enabled;
980        self
981    }
982
983    pub fn flex_wrap(&self, wrap: FlexWrap) -> &Self {
984        self.core.borrow_mut().behavior.flex_wrap = Some(wrap);
985        if self.has_built_handle() {
986            ui::set_flex_wrap(self.handle().raw(), wrap as u32);
987            self.notify_retained_layout_mutation();
988        }
989        self
990    }
991
992    pub fn clip_to_bounds(&self, clip: bool) -> &Self {
993        self.core.borrow_mut().behavior.clip_to_bounds = Some(clip);
994        if self.has_built_handle() {
995            ui::set_clip_to_bounds(self.handle().raw(), clip);
996            self.notify_retained_mutation();
997        }
998        self
999    }
1000
1001    pub fn selection_area(&self, enabled: bool) -> &Self {
1002        self.core.borrow_mut().behavior.selection_area = enabled;
1003        self
1004    }
1005
1006    pub fn selection_area_barrier(&self, enabled: bool) -> &Self {
1007        self.core.borrow_mut().behavior.selection_area_barrier = enabled;
1008        self
1009    }
1010
1011    pub fn on_pointer_click(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self {
1012        self.core.borrow_mut().handlers.pointer_click = Some(Rc::new(handler));
1013        self.retained_node_ref().require_interactive();
1014        self
1015    }
1016
1017    pub fn on_pointer_down(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self {
1018        self.core.borrow_mut().handlers.pointer_down = Some(Rc::new(handler));
1019        self.retained_node_ref().require_interactive();
1020        self
1021    }
1022
1023    pub fn on_pointer_move(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self {
1024        self.core.borrow_mut().handlers.pointer_move = Some(Rc::new(handler));
1025        self.retained_node_ref().require_interactive();
1026        self
1027    }
1028
1029    pub fn on_pointer_up(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self {
1030        self.core.borrow_mut().handlers.pointer_up = Some(Rc::new(handler));
1031        self.retained_node_ref().require_interactive();
1032        self
1033    }
1034
1035    pub fn on_pointer_enter(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self {
1036        self.core.borrow_mut().handlers.pointer_enter = Some(Rc::new(handler));
1037        self.retained_node_ref().require_interactive();
1038        self
1039    }
1040
1041    pub fn on_pointer_leave(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self {
1042        self.core.borrow_mut().handlers.pointer_leave = Some(Rc::new(handler));
1043        self.retained_node_ref().require_interactive();
1044        self
1045    }
1046
1047    pub fn on_pointer_cancel(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self {
1048        self.core.borrow_mut().handlers.pointer_cancel = Some(Rc::new(handler));
1049        self.retained_node_ref().require_interactive();
1050        self
1051    }
1052
1053    pub fn on_wheel(&self, handler: impl Fn(&mut WheelEventArgs) + 'static) -> &Self {
1054        self.core.borrow_mut().handlers.wheel = Some(Rc::new(handler));
1055        self.retained_node_ref().require_interactive();
1056        self
1057    }
1058
1059    pub fn on_pan_gesture(&self, handler: impl Fn(&mut GestureEventArgs) + 'static) -> &Self {
1060        self.core.borrow_mut().handlers.pan_gesture = Some(Rc::new(handler));
1061        self
1062    }
1063
1064    pub fn on_pinch_gesture(&self, handler: impl Fn(&mut GestureEventArgs) + 'static) -> &Self {
1065        self.core.borrow_mut().handlers.pinch_gesture = Some(Rc::new(handler));
1066        self
1067    }
1068
1069    pub fn long_press_options(&self, minimum_duration_ms: i32, movement_tolerance: f32) -> &Self {
1070        let mut core = self.core.borrow_mut();
1071        core.handlers.long_press_minimum_duration_ms = minimum_duration_ms.max(0);
1072        core.handlers.long_press_movement_tolerance = movement_tolerance.max(0.0);
1073        self
1074    }
1075
1076    pub fn on_long_press(&self, handler: impl Fn(&mut LongPressEventArgs) + 'static) -> &Self {
1077        self.core.borrow_mut().handlers.long_press = Some(Rc::new(handler));
1078        self
1079    }
1080
1081    pub fn on_key_down(&self, handler: impl Fn(&mut KeyEventArgs) + 'static) -> &Self {
1082        self.core.borrow_mut().handlers.key_down = Some(Rc::new(handler));
1083        self
1084    }
1085
1086    pub fn on_key_up(&self, handler: impl Fn(&mut KeyEventArgs) + 'static) -> &Self {
1087        self.core.borrow_mut().handlers.key_up = Some(Rc::new(handler));
1088        self
1089    }
1090
1091    pub fn on_focus_changed(&self, handler: impl Fn(FocusChangedEventArgs) + 'static) -> &Self {
1092        self.core.borrow_mut().handlers.focus_changed = Some(Rc::new(handler));
1093        self
1094    }
1095
1096    pub fn child<T: Node>(&self, child: &T) -> &Self {
1097        self.append_child(child);
1098        self
1099    }
1100
1101    pub fn children<I, C>(&self, children: I) -> &Self
1102    where
1103        I: IntoIterator<Item = C>,
1104        C: Into<Child>,
1105    {
1106        for child in children {
1107            self.retained_node_ref()
1108                .append_child_ref(&child.into().node_ref);
1109        }
1110        self
1111    }
1112}