Skip to main content

ferroui_base/layout/
layoutable.rs

1use super::{EffectiveViewportChangedEventArgs, ILayoutManager, LayoutHelper, MinMax};
2use crate::reactive::{Disposable, IDisposable};
3use crate::styling::{Container, ContainerSizing};
4use crate::utilities::{HandlerList, MathUtilities};
5use crate::{
6    ferro_class, ferro_property, instantiate, DirectProperty, FerroObjectImpl, FerroObjectImplExt, FerroProperty,
7    FerroPropertyChangedEventArgs, ObjectType, Point, Rect, Ref, Size, StyledElementImpl, StyledElementImplExt,
8    StyledProperty, StyledPropertyOptions, Thickness, Upcast, Visual, VisualImpl, VisualImplExt,
9    VisualTreeAttachmentEventArgs,
10};
11use std::cell::{Cell, RefCell};
12use std::rc::Rc;
13
14/// Defines how a control aligns itself horizontally in its parent control.
15#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
16pub enum HorizontalAlignment {
17    /// The control stretches to fill the width of the parent control.
18    #[default]
19    Stretch,
20    /// The control aligns itself to the left of the parent control.
21    Left,
22    /// The control centers itself in the parent control.
23    Center,
24    /// The control aligns itself to the right of the parent control.
25    Right,
26}
27
28/// Defines how a control aligns itself vertically in its parent control.
29#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
30pub enum VerticalAlignment {
31    /// The control stretches to fill the height of the parent control.
32    #[default]
33    Stretch,
34    /// The control aligns itself to the top of the parent control.
35    Top,
36    /// The control centers itself within the parent control.
37    Center,
38    /// The control aligns itself to the bottom of the parent control.
39    Bottom,
40}
41
42/// Implements layout-related functionality for a control.
43#[repr(C)]
44pub struct Layoutable {
45    base: Visual,
46    desired_size: Cell<Size>,
47    is_measure_valid: Cell<bool>,
48    is_arrange_valid: Cell<bool>,
49    measuring: Cell<bool>,
50    previous_measure: Cell<Option<Size>>,
51    previous_arrange: Cell<Option<Rect>>,
52    is_attaching_to_visual_tree: Cell<bool>,
53    effective_viewport_changed: HandlerList<dyn Fn(&EffectiveViewportChangedEventArgs)>,
54    layout_updated: HandlerList<dyn Fn()>,
55    layout_updated_subscription: RefCell<Option<(Rc<dyn ILayoutManager>, u64)>>,
56}
57
58ferro_class! {
59    Layoutable: Visual, virtuals LayoutableImpl: VisualImpl {
60        /// Creates the visual children of the control, if necessary.
61        fn apply_template(this);
62        /// The default implementation of the control's measure pass.
63        ///
64        /// This calculates the desired size taking into account the control's
65        /// margin and min/max/explicit size, and calls `measure_override` for
66        /// the content. Usually `measure_override` is what you want to
67        /// override.
68        fn measure_core(this, available_size: Size) -> Size;
69        /// Measures the control and its child elements as part of a layout
70        /// pass. Returns the size the control would like to be.
71        fn measure_override(this, available_size: Size) -> Size;
72        /// The default implementation of the control's arrange pass.
73        ///
74        /// This calculates the final bounds taking into account the control's
75        /// margin, alignment and size constraints, and calls
76        /// `arrange_override` for the content. Usually `arrange_override` is
77        /// what you want to override.
78        fn arrange_core(this, final_rect: Rect);
79        /// Positions child elements as part of a layout pass. Returns the
80        /// size used.
81        fn arrange_override(this, final_size: Size) -> Size;
82        /// Called by `invalidate_measure`.
83        fn on_measure_invalidated(this);
84    }
85}
86crate::ferro_class_info!(Layoutable { new: Layoutable::new });
87
88crate::ferro_overrides! { impl FerroObjectImpl for Layoutable {
89    fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
90        Self::parent_on_property_changed(this, change);
91
92        if change.property() == Visual::is_visible_property().as_property() {
93            this.set_desired_size(Size::default());
94
95            // All changes to visibility cause the parent element to be notified.
96            if let Some(parent) = this.visual_parent() {
97                if let Some(parent) = parent.downcast_ref::<Layoutable>() {
98                    parent.child_desired_size_changed(this);
99                }
100            }
101
102            if change.get_new_value::<bool>() {
103                // We only invalidate ourselves when visibility is changed to true.
104                this.invalidate_measure();
105
106                // If any descendant had its measure/arrange invalidated while
107                // we were hidden, they will need to be registered with the
108                // layout manager now that they are again effectively visible.
109                if let Some(layout_root) = this.get_layout_root() {
110                    let manager = layout_root.layout_manager();
111                    if let Some(children) = this.visual_children_snapshot() {
112                        for child in children.iter() {
113                            if let Some(child) = child.downcast_ref::<Layoutable>() {
114                                child.ancestor_became_visible(&*manager);
115                            }
116                        }
117                    }
118                }
119            }
120        }
121    }
122} }
123
124impl StyledElementImpl for Layoutable {
125    fn invalidate_styles(this: &Self, recurse: bool) {
126        Self::parent_invalidate_styles(this, recurse);
127        this.invalidate_measure();
128    }
129
130    fn on_control_theme_changed(this: &Self) {
131        Self::parent_on_control_theme_changed(this);
132        this.invalidate_measure();
133    }
134
135    fn on_templated_parent_control_theme_changed(this: &Self) {
136        Self::parent_on_templated_parent_control_theme_changed(this);
137        this.invalidate_measure();
138    }
139}
140
141impl VisualImpl for Layoutable {
142    fn on_attached_to_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
143        this.is_attaching_to_visual_tree.set(true);
144        Self::parent_on_attached_to_visual_tree_core(this, e);
145        this.is_attaching_to_visual_tree.set(false);
146
147        if let Some(root) = this.get_layout_root() {
148            let manager = root.layout_manager();
149            if !this.layout_updated.is_empty() {
150                this.subscribe_layout_updated(manager.clone());
151            }
152            if !this.effective_viewport_changed.is_empty() {
153                manager.register_effective_viewport_listener(this);
154            }
155        }
156    }
157
158    fn on_detached_from_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
159        if let Some(root) = this.get_layout_root() {
160            this.unsubscribe_layout_updated();
161            if !this.effective_viewport_changed.is_empty() {
162                root.layout_manager().unregister_effective_viewport_listener(this);
163            }
164        }
165        Self::parent_on_detached_from_visual_tree_core(this, e);
166    }
167
168    fn on_visual_parent_changed(this: &Self, old_parent: Option<&Ref<Visual>>, new_parent: Option<&Ref<Visual>>) {
169        LayoutHelper::invalidate_self_and_children_measure(this);
170        Self::parent_on_visual_parent_changed(this, old_parent, new_parent);
171    }
172}
173
174impl LayoutableImpl for Layoutable {
175    fn apply_template(_this: &Self) {}
176
177    fn measure_core(this: &Self, available_size: Size) -> Size {
178        this.apply_styling();
179
180        if !this.is_visible() {
181            return Size::default();
182        }
183
184        let mut margin = this.margin();
185        let use_layout_rounding = this.use_layout_rounding();
186        let mut scale = 1.0;
187
188        if use_layout_rounding {
189            scale = LayoutHelper::get_layout_scale(this);
190            margin = LayoutHelper::round_layout_thickness(margin, scale);
191        }
192
193        this.apply_template();
194
195        let min_max = MinMax::new(this);
196        let constrained_size = LayoutHelper::apply_layout_constraints_min_max(min_max, available_size.deflate(margin));
197
198        let mut container_sizing = ContainerSizing::Normal;
199        if let Some(query_provider) = Container::get_query_provider(this) {
200            let sizing = Container::get_sizing(this);
201            if sizing != ContainerSizing::Normal {
202                container_sizing = sizing;
203                query_provider.set_size(constrained_size.width, constrained_size.height, container_sizing);
204            }
205        }
206
207        let measured = this.measure_override(constrained_size);
208
209        let mut width = MathUtilities::clamp(measured.width, min_max.min_width, min_max.max_width);
210        let mut height = MathUtilities::clamp(measured.height, min_max.min_height, min_max.max_height);
211
212        match container_sizing {
213            ContainerSizing::Normal => {}
214            ContainerSizing::Width => {
215                width = if constrained_size.width.is_infinite() { width } else { constrained_size.width };
216            }
217            ContainerSizing::Height => {
218                width = measured.width;
219                height = if constrained_size.height.is_infinite() { height } else { constrained_size.height };
220            }
221            ContainerSizing::WidthAndHeight => {
222                width = if constrained_size.width.is_infinite() { width } else { constrained_size.width };
223                height = if constrained_size.height.is_infinite() { height } else { constrained_size.height };
224            }
225        }
226
227        if use_layout_rounding {
228            let rounded = LayoutHelper::round_layout_size_up(Size::new(width, height), scale);
229            width = rounded.width;
230            height = rounded.height;
231        }
232
233        width += margin.left + margin.right;
234        height += margin.top + margin.bottom;
235
236        if width > available_size.width {
237            width = available_size.width;
238        }
239        if height > available_size.height {
240            height = available_size.height;
241        }
242        if width < 0.0 {
243            width = 0.0;
244        }
245        if height < 0.0 {
246            height = 0.0;
247        }
248
249        Size::new(width, height)
250    }
251
252    fn measure_override(this: &Self, available_size: Size) -> Size {
253        let mut width: f64 = 0.0;
254        let mut height: f64 = 0.0;
255
256        if let Some(children) = this.visual_children_snapshot() {
257            for visual in children.iter() {
258                if let Some(layoutable) = visual.downcast_ref::<Layoutable>() {
259                    layoutable.measure(available_size);
260                    let child_size = layoutable.desired_size();
261                    if child_size.width > width {
262                        width = child_size.width;
263                    }
264                    if child_size.height > height {
265                        height = child_size.height;
266                    }
267                }
268            }
269        }
270
271        Size::new(width, height)
272    }
273
274    fn arrange_core(this: &Self, final_rect: Rect) {
275        if !this.is_visible() {
276            return;
277        }
278
279        let use_layout_rounding = this.use_layout_rounding();
280        let scale = LayoutHelper::get_layout_scale(this);
281
282        let mut margin = this.margin();
283        let mut origin_x = final_rect.x + margin.left;
284        let mut origin_y = final_rect.y + margin.top;
285
286        // Margin has to be treated separately because the layout rounding
287        // function is not linear: f(a + b) != f(a) + f(b). If the margin isn't
288        // pre-rounded some sizes will be offset by 1 pixel in certain scales.
289        if use_layout_rounding {
290            margin = LayoutHelper::round_layout_thickness(margin, scale);
291        }
292
293        let available_width = (final_rect.width - margin.left - margin.right).max(0.0);
294        let available_height = (final_rect.height - margin.top - margin.bottom).max(0.0);
295        let mut available_size_minus_margins = Size::new(available_width, available_height);
296
297        let horizontal_alignment = this.horizontal_alignment();
298        let vertical_alignment = this.vertical_alignment();
299        let mut size = available_size_minus_margins;
300        let desired_size = this.desired_size();
301
302        if horizontal_alignment != HorizontalAlignment::Stretch {
303            size = size.with_width(size.width.min(desired_size.width - margin.left - margin.right));
304        }
305        if vertical_alignment != VerticalAlignment::Stretch {
306            size = size.with_height(size.height.min(desired_size.height - margin.top - margin.bottom));
307        }
308
309        size = LayoutHelper::apply_layout_constraints_min_max(MinMax::new(this), size);
310
311        if use_layout_rounding {
312            size = LayoutHelper::round_layout_size_up(size, scale);
313            available_size_minus_margins = LayoutHelper::round_layout_size_up(available_size_minus_margins, scale);
314        }
315
316        size = this.arrange_override(size).constrain(size);
317
318        match horizontal_alignment {
319            HorizontalAlignment::Center | HorizontalAlignment::Stretch => {
320                origin_x += (available_size_minus_margins.width - size.width) / 2.0;
321            }
322            HorizontalAlignment::Right => origin_x += available_size_minus_margins.width - size.width,
323            HorizontalAlignment::Left => {}
324        }
325
326        match vertical_alignment {
327            VerticalAlignment::Center | VerticalAlignment::Stretch => {
328                origin_y += (available_size_minus_margins.height - size.height) / 2.0;
329            }
330            VerticalAlignment::Bottom => origin_y += available_size_minus_margins.height - size.height,
331            VerticalAlignment::Top => {}
332        }
333
334        let mut origin = Point::new(origin_x, origin_y);
335        if use_layout_rounding {
336            origin = LayoutHelper::round_layout_point(origin, scale);
337        }
338
339        this.set_bounds(Rect::from_position_size(origin, size));
340    }
341
342    fn arrange_override(this: &Self, final_size: Size) -> Size {
343        let arrange_rect = Rect::from_size(final_size);
344        if let Some(children) = this.visual_children_snapshot() {
345            for visual in children.iter() {
346                if let Some(layoutable) = visual.downcast_ref::<Layoutable>() {
347                    layoutable.arrange(arrange_rect);
348                }
349            }
350        }
351        final_size
352    }
353
354    fn on_measure_invalidated(_this: &Self) {}
355}
356
357crate::ferro_properties! { impl Layoutable {
358    ferro_property!(
359        /// Defines the `DesiredSize` property.
360        pub fn desired_size_property() -> DirectProperty<Layoutable, Size> {
361            FerroProperty::register_direct::<Layoutable, _>("DesiredSize", |o| o.desired_size(), None, Size::default())
362        }
363    );
364
365    ferro_property!(
366        /// Defines the `Width` property.
367        pub fn width_property() -> StyledProperty<f64> {
368            FerroProperty::register_with::<Layoutable, _>(
369                "Width",
370                StyledPropertyOptions::new(f64::NAN).validate(Layoutable::validate_dimension),
371            )
372        }
373    );
374
375    ferro_property!(
376        /// Defines the `Height` property.
377        pub fn height_property() -> StyledProperty<f64> {
378            FerroProperty::register_with::<Layoutable, _>(
379                "Height",
380                StyledPropertyOptions::new(f64::NAN).validate(Layoutable::validate_dimension),
381            )
382        }
383    );
384
385    ferro_property!(
386        /// Defines the `MinWidth` property.
387        pub fn min_width_property() -> StyledProperty<f64> {
388            FerroProperty::register_with::<Layoutable, _>(
389                "MinWidth",
390                StyledPropertyOptions::new(0.0).validate(Layoutable::validate_minimum_dimension),
391            )
392        }
393    );
394
395    ferro_property!(
396        /// Defines the `MaxWidth` property.
397        pub fn max_width_property() -> StyledProperty<f64> {
398            FerroProperty::register_with::<Layoutable, _>(
399                "MaxWidth",
400                StyledPropertyOptions::new(f64::INFINITY).validate(Layoutable::validate_maximum_dimension),
401            )
402        }
403    );
404
405    ferro_property!(
406        /// Defines the `MinHeight` property.
407        pub fn min_height_property() -> StyledProperty<f64> {
408            FerroProperty::register_with::<Layoutable, _>(
409                "MinHeight",
410                StyledPropertyOptions::new(0.0).validate(Layoutable::validate_minimum_dimension),
411            )
412        }
413    );
414
415    ferro_property!(
416        /// Defines the `MaxHeight` property.
417        pub fn max_height_property() -> StyledProperty<f64> {
418            FerroProperty::register_with::<Layoutable, _>(
419                "MaxHeight",
420                StyledPropertyOptions::new(f64::INFINITY).validate(Layoutable::validate_maximum_dimension),
421            )
422        }
423    );
424
425    ferro_property!(
426        /// Defines the `Margin` property.
427        pub fn margin_property() -> StyledProperty<Thickness> {
428            FerroProperty::register_with::<Layoutable, _>(
429                "Margin",
430                StyledPropertyOptions::new(Thickness::default()).validate(Layoutable::validate_thickness),
431            )
432        }
433    );
434
435    ferro_property!(
436        /// Defines the `HorizontalAlignment` property.
437        pub fn horizontal_alignment_property() -> StyledProperty<HorizontalAlignment> {
438            FerroProperty::register::<Layoutable, _>("HorizontalAlignment", HorizontalAlignment::Stretch)
439        }
440    );
441
442    ferro_property!(
443        /// Defines the `VerticalAlignment` property.
444        pub fn vertical_alignment_property() -> StyledProperty<VerticalAlignment> {
445            FerroProperty::register::<Layoutable, _>("VerticalAlignment", VerticalAlignment::Stretch)
446        }
447    );
448
449    ferro_property!(
450        /// Defines the `UseLayoutRounding` property.
451        pub fn use_layout_rounding_property() -> StyledProperty<bool> {
452            FerroProperty::register_with::<Layoutable, _>(
453                "UseLayoutRounding",
454                StyledPropertyOptions::new(true).inherits(true),
455            )
456        }
457    );
458} }
459
460impl Layoutable {
461    fn static_constructor() {
462        Self::affects_measure::<Layoutable>(&[
463            Self::width_property().as_property(),
464            Self::height_property().as_property(),
465            Self::min_width_property().as_property(),
466            Self::max_width_property().as_property(),
467            Self::min_height_property().as_property(),
468            Self::max_height_property().as_property(),
469            Self::margin_property().as_property(),
470            Self::horizontal_alignment_property().as_property(),
471            Self::vertical_alignment_property().as_property(),
472        ]);
473    }
474
475    fn validate_dimension(value: &f64) -> bool {
476        value.is_nan() || Self::validate_minimum_dimension(value)
477    }
478
479    fn validate_minimum_dimension(value: &f64) -> bool {
480        *value != f64::INFINITY && Self::validate_maximum_dimension(value)
481    }
482
483    fn validate_maximum_dimension(value: &f64) -> bool {
484        *value >= 0.0
485    }
486
487    fn validate_thickness(value: &Thickness) -> bool {
488        value.left.is_finite() && value.top.is_finite() && value.right.is_finite() && value.bottom.is_finite()
489    }
490
491    /// Creates the class data; see [`crate::FerroObject::construct`].
492    pub fn construct() -> Self {
493        Self {
494            base: Visual::construct(),
495            desired_size: Cell::new(Size::default()),
496            is_measure_valid: Cell::new(false),
497            is_arrange_valid: Cell::new(false),
498            measuring: Cell::new(false),
499            previous_measure: Cell::new(None),
500            previous_arrange: Cell::new(None),
501            is_attaching_to_visual_tree: Cell::new(false),
502            effective_viewport_changed: HandlerList::new(),
503            layout_updated: HandlerList::new(),
504            layout_updated_subscription: RefCell::new(None),
505        }
506    }
507
508    pub fn new() -> Ref<Self> {
509        instantiate(Self::construct())
510    }
511
512    // --- events -----------------------------------------------------------
513
514    /// Occurs when the element's effective viewport changes.
515    pub fn effective_viewport_changed(
516        &self,
517        handler: impl Fn(&EffectiveViewportChangedEventArgs) + 'static,
518    ) -> Rc<dyn IDisposable> {
519        if self.effective_viewport_changed.is_empty() && !self.is_attaching_to_visual_tree.get() {
520            if let Some(root) = self.get_layout_root() {
521                root.layout_manager().register_effective_viewport_listener(self);
522            }
523        }
524        let token = self.effective_viewport_changed.add(Rc::new(handler));
525        let weak = self.to_ref().downgrade();
526        Disposable::create(move || {
527            if let Some(this) = weak.upgrade() {
528                this.effective_viewport_changed.remove(token);
529                if this.effective_viewport_changed.is_empty() {
530                    if let Some(root) = this.get_layout_root() {
531                        root.layout_manager().unregister_effective_viewport_listener(&this);
532                    }
533                }
534            }
535        })
536    }
537
538    /// Occurs when a layout pass completes for the control.
539    pub fn layout_updated(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
540        if self.layout_updated.is_empty() && !self.is_attaching_to_visual_tree.get() {
541            if let Some(root) = self.get_layout_root() {
542                self.subscribe_layout_updated(root.layout_manager());
543            }
544        }
545        let token = self.layout_updated.add(Rc::new(handler));
546        let weak = self.to_ref().downgrade();
547        Disposable::create(move || {
548            if let Some(this) = weak.upgrade() {
549                this.layout_updated.remove(token);
550                if this.layout_updated.is_empty() {
551                    this.unsubscribe_layout_updated();
552                }
553            }
554        })
555    }
556
557    fn subscribe_layout_updated(&self, manager: Rc<dyn ILayoutManager>) {
558        if self.layout_updated_subscription.borrow().is_some() {
559            return;
560        }
561        let weak = self.to_ref().downgrade();
562        let token = manager.add_layout_updated(Rc::new(move || {
563            if let Some(this) = weak.upgrade() {
564                if !this.layout_updated.is_empty() {
565                    for (_, handler) in this.layout_updated.snapshot().iter() {
566                        handler();
567                    }
568                }
569            }
570        }));
571        *self.layout_updated_subscription.borrow_mut() = Some((manager, token));
572    }
573
574    fn unsubscribe_layout_updated(&self) {
575        if let Some((manager, token)) = self.layout_updated_subscription.take() {
576            manager.remove_layout_updated(token);
577        }
578    }
579
580    pub(crate) fn raise_effective_viewport_changed(&self, e: &EffectiveViewportChangedEventArgs) {
581        if !self.effective_viewport_changed.is_empty() {
582            for (_, handler) in self.effective_viewport_changed.snapshot().iter() {
583                handler(e);
584            }
585        }
586    }
587
588    // --- properties -------------------------------------------------------
589
590    /// Executes a layout pass.
591    ///
592    /// You should not usually need to call this method explicitly: the layout
593    /// manager schedules layout passes itself.
594    pub fn update_layout(&self) {
595        if let Some(manager) = self.get_layout_manager() {
596            manager.execute_layout_pass();
597        }
598    }
599
600    /// The width of the element.
601    pub fn width(&self) -> f64 {
602        self.get_value(Self::width_property())
603    }
604
605    pub fn set_width(&self, value: f64) {
606        self.set_value(Self::width_property(), value)
607    }
608
609    /// The height of the element.
610    pub fn height(&self) -> f64 {
611        self.get_value(Self::height_property())
612    }
613
614    pub fn set_height(&self, value: f64) {
615        self.set_value(Self::height_property(), value)
616    }
617
618    /// The minimum width of the element.
619    pub fn min_width(&self) -> f64 {
620        self.get_value(Self::min_width_property())
621    }
622
623    pub fn set_min_width(&self, value: f64) {
624        self.set_value(Self::min_width_property(), value)
625    }
626
627    /// The maximum width of the element.
628    pub fn max_width(&self) -> f64 {
629        self.get_value(Self::max_width_property())
630    }
631
632    pub fn set_max_width(&self, value: f64) {
633        self.set_value(Self::max_width_property(), value)
634    }
635
636    /// The minimum height of the element.
637    pub fn min_height(&self) -> f64 {
638        self.get_value(Self::min_height_property())
639    }
640
641    pub fn set_min_height(&self, value: f64) {
642        self.set_value(Self::min_height_property(), value)
643    }
644
645    /// The maximum height of the element.
646    pub fn max_height(&self) -> f64 {
647        self.get_value(Self::max_height_property())
648    }
649
650    pub fn set_max_height(&self, value: f64) {
651        self.set_value(Self::max_height_property(), value)
652    }
653
654    /// The margin around the element.
655    pub fn margin(&self) -> Thickness {
656        self.get_value(Self::margin_property())
657    }
658
659    pub fn set_margin(&self, value: Thickness) {
660        self.set_value(Self::margin_property(), value)
661    }
662
663    /// The element's preferred horizontal alignment in its parent.
664    pub fn horizontal_alignment(&self) -> HorizontalAlignment {
665        self.get_value(Self::horizontal_alignment_property())
666    }
667
668    pub fn set_horizontal_alignment(&self, value: HorizontalAlignment) {
669        self.set_value(Self::horizontal_alignment_property(), value)
670    }
671
672    /// The element's preferred vertical alignment in its parent.
673    pub fn vertical_alignment(&self) -> VerticalAlignment {
674        self.get_value(Self::vertical_alignment_property())
675    }
676
677    pub fn set_vertical_alignment(&self, value: VerticalAlignment) {
678        self.set_value(Self::vertical_alignment_property(), value)
679    }
680
681    /// The size that this element computed during the measure pass of the
682    /// layout process.
683    #[inline]
684    pub fn desired_size(&self) -> Size {
685        self.desired_size.get()
686    }
687
688    fn set_desired_size(&self, value: Size) {
689        self.set_and_raise_cell(Self::desired_size_property(), &self.desired_size, value);
690    }
691
692    /// Whether the control's layout measure is valid.
693    #[inline]
694    pub fn is_measure_valid(&self) -> bool {
695        self.is_measure_valid.get()
696    }
697
698    /// Whether the control's layout arrange is valid.
699    #[inline]
700    pub fn is_arrange_valid(&self) -> bool {
701        self.is_arrange_valid.get()
702    }
703
704    /// The private setter of `IsMeasureValid`, which nothing of the
705    /// framework calls; the benchmarks reach it (`layout::testing`).
706    #[cfg(any(test, feature = "testing"))]
707    pub(crate) fn set_is_measure_valid(&self, value: bool) {
708        self.is_measure_valid.set(value);
709    }
710
711    /// The private setter of `IsArrangeValid` (see
712    /// [`set_is_measure_valid`](Self::set_is_measure_valid)).
713    #[cfg(any(test, feature = "testing"))]
714    pub(crate) fn set_is_arrange_valid(&self, value: bool) {
715        self.is_arrange_valid.set(value);
716    }
717
718    /// Whether the control's layout should be snapped to pixel boundaries.
719    pub fn use_layout_rounding(&self) -> bool {
720        self.get_value(Self::use_layout_rounding_property())
721    }
722
723    pub fn set_use_layout_rounding(&self, value: bool) {
724        self.set_value(Self::use_layout_rounding_property(), value)
725    }
726
727    /// The available size passed in the previous layout pass, if any.
728    pub fn previous_measure(&self) -> Option<Size> {
729        self.previous_measure.get()
730    }
731
732    /// The rect passed in the previous layout pass, if any.
733    pub fn previous_arrange(&self) -> Option<Rect> {
734        self.previous_arrange.get()
735    }
736
737    // --- layout -----------------------------------------------------------
738
739    /// Carries out a measure of the control.
740    pub fn measure(&self, available_size: Size) {
741        if available_size.width.is_nan() || available_size.height.is_nan() {
742            panic!("Cannot call Measure using a size with NaN values.");
743        }
744
745        if !self.is_measure_valid.get() || self.previous_measure.get() != Some(available_size) {
746            let previous_desired_size = self.desired_size();
747
748            self.is_measure_valid.set(true);
749
750            self.measuring.set(true);
751            let guard = MeasuringGuard(&self.measuring);
752            let desired_size = self.measure_core(available_size);
753            drop(guard);
754
755            if Self::is_invalid_size(desired_size) {
756                panic!("Invalid size returned for Measure.");
757            }
758
759            self.set_desired_size(desired_size);
760            self.previous_measure.set(Some(available_size));
761
762            if self.desired_size() != previous_desired_size {
763                if let Some(parent) = self.visual_parent() {
764                    if let Some(parent) = parent.downcast_ref::<Layoutable>() {
765                        parent.child_desired_size_changed(self);
766                    }
767                }
768            }
769        }
770    }
771
772    /// Arranges the control and its children.
773    pub fn arrange(&self, rect: Rect) {
774        if Self::is_invalid_rect(rect) {
775            panic!("Invalid Arrange rectangle.");
776        }
777
778        if !self.is_measure_valid.get() {
779            self.measure(self.previous_measure.get().unwrap_or(rect.size()));
780        }
781
782        if !self.is_arrange_valid.get() || self.previous_arrange.get() != Some(rect) {
783            self.is_arrange_valid.set(true);
784            self.arrange_core(rect);
785            self.previous_arrange.set(Some(rect));
786        }
787    }
788
789    /// Invalidates the measurement of the control and queues a new layout
790    /// pass.
791    pub fn invalidate_measure(&self) {
792        if self.is_measure_valid.get() {
793            self.is_measure_valid.set(false);
794            self.is_arrange_valid.set(false);
795
796            if self.is_attached_to_visual_tree() {
797                if let Some(manager) = self.get_layout_manager() {
798                    manager.invalidate_measure(self);
799                }
800                self.invalidate_visual();
801            }
802            self.on_measure_invalidated();
803        }
804    }
805
806    /// Invalidates the arrangement of the control and queues a new layout
807    /// pass.
808    pub fn invalidate_arrange(&self) {
809        if self.is_arrange_valid.get() {
810            self.is_arrange_valid.set(false);
811            if let Some(manager) = self.get_layout_manager() {
812                manager.invalidate_arrange(self);
813            }
814            self.invalidate_visual();
815        }
816    }
817
818    /// Called by a child when its desired size changes.
819    pub(crate) fn child_desired_size_changed(&self, _control: &Layoutable) {
820        if !self.measuring.get() {
821            self.invalidate_measure();
822        }
823    }
824
825    /// Marks a property as affecting the control's measurement: after a
826    /// change to any of the properties on an object of class `T`,
827    /// `invalidate_measure` is called on it.
828    pub fn affects_measure<T: ObjectType + Upcast<Layoutable>>(properties: &[&'static FerroProperty]) {
829        for property in properties {
830            property.changed().subscribe(|e| {
831                if let Some(sender) = e.sender().downcast_ref::<T>() {
832                    let layoutable: &Layoutable = sender.upcast();
833                    layoutable.invalidate_measure();
834                }
835            });
836        }
837    }
838
839    /// Marks a property as affecting the control's arrangement: after a
840    /// change to any of the properties on an object of class `T`,
841    /// `invalidate_arrange` is called on it.
842    pub fn affects_arrange<T: ObjectType + Upcast<Layoutable>>(properties: &[&'static FerroProperty]) {
843        for property in properties {
844            property.changed().subscribe(|e| {
845                if let Some(sender) = e.sender().downcast_ref::<T>() {
846                    let layoutable: &Layoutable = sender.upcast();
847                    layoutable.invalidate_arrange();
848                }
849            });
850        }
851    }
852
853    fn ancestor_became_visible(&self, layout_manager: &dyn ILayoutManager) {
854        if !self.is_visible() {
855            return;
856        }
857
858        if !self.is_measure_valid.get() {
859            layout_manager.invalidate_measure(self);
860            self.invalidate_visual();
861        } else if !self.is_arrange_valid.get() {
862            layout_manager.invalidate_arrange(self);
863            self.invalidate_visual();
864        }
865
866        if let Some(children) = self.visual_children_snapshot() {
867            for child in children.iter() {
868                if let Some(child) = child.downcast_ref::<Layoutable>() {
869                    child.ancestor_became_visible(layout_manager);
870                }
871            }
872        }
873    }
874
875    fn is_invalid_rect(rect: Rect) -> bool {
876        MathUtilities::is_negative_or_non_finite(rect.width)
877            || MathUtilities::is_negative_or_non_finite(rect.height)
878            || !MathUtilities::is_finite(rect.x)
879            || !MathUtilities::is_finite(rect.y)
880    }
881
882    fn is_invalid_size(size: Size) -> bool {
883        MathUtilities::is_negative_or_non_finite(size.width) || MathUtilities::is_negative_or_non_finite(size.height)
884    }
885}
886
887/// Resets the "measuring" flag even if `measure_core` unwinds.
888struct MeasuringGuard<'a>(&'a Cell<bool>);
889
890impl Drop for MeasuringGuard<'_> {
891    fn drop(&mut self) {
892        self.0.set(false);
893    }
894}