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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
16pub enum HorizontalAlignment {
17 #[default]
19 Stretch,
20 Left,
22 Center,
24 Right,
26}
27
28#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
30pub enum VerticalAlignment {
31 #[default]
33 Stretch,
34 Top,
36 Center,
38 Bottom,
40}
41
42#[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 fn apply_template(this);
62 fn measure_core(this, available_size: Size) -> Size;
69 fn measure_override(this, available_size: Size) -> Size;
72 fn arrange_core(this, final_rect: Rect);
79 fn arrange_override(this, final_size: Size) -> Size;
82 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 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 this.invalidate_measure();
105
106 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 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 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 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 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 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 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 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 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 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 pub fn horizontal_alignment_property() -> StyledProperty<HorizontalAlignment> {
438 FerroProperty::register::<Layoutable, _>("HorizontalAlignment", HorizontalAlignment::Stretch)
439 }
440 );
441
442 ferro_property!(
443 pub fn vertical_alignment_property() -> StyledProperty<VerticalAlignment> {
445 FerroProperty::register::<Layoutable, _>("VerticalAlignment", VerticalAlignment::Stretch)
446 }
447 );
448
449 ferro_property!(
450 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 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 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 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 pub fn update_layout(&self) {
595 if let Some(manager) = self.get_layout_manager() {
596 manager.execute_layout_pass();
597 }
598 }
599
600 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 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 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 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 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 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 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 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 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 #[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 #[inline]
694 pub fn is_measure_valid(&self) -> bool {
695 self.is_measure_valid.get()
696 }
697
698 #[inline]
700 pub fn is_arrange_valid(&self) -> bool {
701 self.is_arrange_valid.get()
702 }
703
704 #[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 #[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 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 pub fn previous_measure(&self) -> Option<Size> {
729 self.previous_measure.get()
730 }
731
732 pub fn previous_arrange(&self) -> Option<Rect> {
734 self.previous_arrange.get()
735 }
736
737 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 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 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 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 pub(crate) fn child_desired_size_changed(&self, _control: &Layoutable) {
820 if !self.measuring.get() {
821 self.invalidate_measure();
822 }
823 }
824
825 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 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
887struct MeasuringGuard<'a>(&'a Cell<bool>);
889
890impl Drop for MeasuringGuard<'_> {
891 fn drop(&mut self) {
892 self.0.set(false);
893 }
894}