1use crate::grid::LayoutTimeSizeType;
5use crate::{Control, Grid, GridLength, GridUnitType};
6use ferroui_base::reactive::IDisposable;
7use ferroui_base::utilities::math_utilities::{max as math_max, MathUtilities};
8use ferroui_base::{
9 ferro_class, ferro_property, AttachedProperty, FerroObject, FerroObjectImpl, FerroProperty,
10 FerroPropertyChangedEventArgs, Ref, StyledPropertyOptions, WeakRef,
11};
12use std::cell::{Cell, RefCell};
13use std::collections::HashMap;
14use std::rc::{Rc, Weak};
15
16const FLAG_USE_SHARED_MINIMUM: u8 = 0x20;
18const FLAG_LAYOUT_WAS_UPDATED: u8 = 0x40;
20
21#[repr(C)]
27pub struct DefinitionBase {
28 base: FerroObject,
29 flags: Cell<u8>,
31 parent_index: Cell<i32>,
33 size_type: Cell<LayoutTimeSizeType>,
36 min_size: Cell<f64>,
39 measure_size: Cell<f64>,
42 size_cache: Cell<f64>,
45 offset: Cell<f64>,
47 shared_state: RefCell<Option<Rc<SharedSizeState>>>,
49 parent: RefCell<Option<WeakRef<Grid>>>,
50}
51
52ferro_class! {
53 DefinitionBase: FerroObject, virtuals DefinitionBaseImpl: FerroObjectImpl {
54 #[doc(hidden)]
56 fn user_size_value_cache(this) -> GridLength;
57 #[doc(hidden)]
60 fn user_min_size_value_cache(this) -> f64;
61 #[doc(hidden)]
64 fn user_max_size_value_cache(this) -> f64;
65 }
66}
67
68ferroui_base::ferro_impl_classes!(DefinitionBase: FerroObjectImpl);
69
70impl DefinitionBaseImpl for DefinitionBase {
71 fn user_size_value_cache(_this: &Self) -> GridLength {
72 panic!("DefinitionBase is abstract.")
73 }
74
75 fn user_min_size_value_cache(_this: &Self) -> f64 {
76 panic!("DefinitionBase is abstract.")
77 }
78
79 fn user_max_size_value_cache(_this: &Self) -> f64 {
80 panic!("DefinitionBase is abstract.")
81 }
82}
83
84impl Drop for DefinitionBase {
85 fn drop(&mut self) {
86 if let Some(shared_state) = self.shared_state.get_mut().take() {
89 shared_state.remove_dropped_members();
90 }
91 }
92}
93
94ferroui_base::ferro_properties! { impl DefinitionBase {
95 ferro_property!(
96 pub(crate) fn private_shared_size_scope_property() -> AttachedProperty<Option<Rc<SharedSizeScope>>> {
99 FerroProperty::register_attached_with::<DefinitionBase, Control, _>(
100 "PrivateSharedSizeScope",
101 StyledPropertyOptions::new(None).inherits(true),
102 )
103 }
104 );
105
106 ferro_property!(
107 pub fn shared_size_group_property() -> AttachedProperty<Option<String>> {
117 FerroProperty::register_attached_with::<DefinitionBase, Control, _>(
118 "SharedSizeGroup",
119 StyledPropertyOptions::new(None).validate(DefinitionBase::shared_size_group_property_value_valid),
120 )
121 }
122 );
123} }
124
125impl DefinitionBase {
126 fn static_constructor() {
127 Self::shared_size_group_property()
128 .changed()
129 .add_class_handler::<DefinitionBase>(Self::on_shared_size_group_property_changed);
130 Self::private_shared_size_scope_property()
131 .changed()
132 .add_class_handler::<DefinitionBase>(Self::on_private_shared_size_scope_property_changed);
133 }
134
135 pub(crate) fn construct() -> Self {
137 Self {
138 base: FerroObject::construct(),
139 flags: Cell::new(0),
140 parent_index: Cell::new(-1),
141 size_type: Cell::new(LayoutTimeSizeType::NONE),
142 min_size: Cell::new(0.0),
143 measure_size: Cell::new(0.0),
144 size_cache: Cell::new(0.0),
145 offset: Cell::new(0.0),
146 shared_state: RefCell::new(None),
147 parent: RefCell::new(None),
148 }
149 }
150
151 pub fn shared_size_group(&self) -> Option<String> {
153 self.get_value(Self::shared_size_group_property())
154 }
155
156 pub fn set_shared_size_group(&self, value: Option<&str>) {
157 self.set_value(Self::shared_size_group_property(), value.map(str::to_owned))
158 }
159
160 pub(crate) fn on_enter_parent_tree(&self) {
162 self.set_inheritance_parent(self.parent().map(Ref::upcast::<FerroObject>));
163 if self.shared_state.borrow().is_none() {
164 if let Some(shared_size_group_id) = self.shared_size_group() {
168 if let Some(private_shared_size_scope) = self.get_value(Self::private_shared_size_scope_property()) {
169 self.register_with_shared_state(&private_shared_size_scope, &shared_size_group_id);
170 }
171 }
172 }
173
174 if let Some(parent) = self.parent() {
175 parent.invalidate_measure();
176 }
177 }
178
179 pub(crate) fn on_exit_parent_tree(&self) {
181 self.offset.set(0.0);
182 self.unregister_from_shared_state();
183
184 if let Some(parent) = self.parent() {
185 parent.invalidate_measure();
186 }
187
188 self.set_inheritance_parent(None::<Ref<FerroObject>>);
192 }
193
194 pub(crate) fn on_before_layout(&self, grid: &Grid) {
196 self.min_size.set(0.0);
198 self.set_layout_was_updated(true);
199
200 let shared_state = self.shared_state.borrow().clone();
202 if let Some(shared_state) = shared_state {
203 shared_state.ensure_deferred_validation(grid);
204 }
205 }
206
207 #[inline]
209 pub(crate) fn update_min_size(&self, min_size: f64) {
210 self.min_size.set(math_max(self.min_size.get(), min_size));
211 }
212
213 #[inline]
215 pub(crate) fn set_min_size(&self, min_size: f64) {
216 self.min_size.set(min_size);
217 }
218
219 pub(crate) fn on_is_shared_size_scope_property_changed(d: &Control, e: &FerroPropertyChangedEventArgs<'_>) {
223 if e.get_new_value::<bool>() {
224 let shared_states_collection = SharedSizeScope::new();
225 d.set_value(
226 Self::private_shared_size_scope_property(),
227 Some(shared_states_collection),
228 );
229 } else {
230 d.clear_value(Self::private_shared_size_scope_property());
231 }
232 }
233
234 pub(crate) fn on_user_size_property_changed(definition: &DefinitionBase, e: &FerroPropertyChangedEventArgs<'_>) {
237 let Some(parent) = definition.parent() else {
238 return;
239 };
240
241 let shared_state = definition.shared_state.borrow().clone();
242 if let Some(shared_state) = shared_state {
243 shared_state.invalidate();
244 } else {
245 let (old_value, new_value) = e.get_old_and_new_value::<GridLength>();
246
247 if old_value.grid_unit_type() != new_value.grid_unit_type() {
248 parent.invalidate();
249 } else {
250 parent.invalidate_measure();
251 }
252 }
253 }
254
255 #[inline]
257 pub(crate) fn is_shared(&self) -> bool {
258 self.shared_state.borrow().is_some()
259 }
260
261 pub(crate) fn user_size(&self) -> GridLength {
263 let shared_state = self.shared_state.borrow().clone();
264 match shared_state {
265 Some(shared_state) => shared_state.user_size(),
266 None => self.user_size_value_cache(),
267 }
268 }
269
270 #[inline]
272 pub(crate) fn user_min_size(&self) -> f64 {
273 self.user_min_size_value_cache()
274 }
275
276 #[inline]
278 pub(crate) fn user_max_size(&self) -> f64 {
279 self.user_max_size_value_cache()
280 }
281
282 #[inline]
284 pub(crate) fn index(&self) -> i32 {
285 self.parent_index.get()
286 }
287
288 #[inline]
289 pub(crate) fn set_index(&self, value: i32) {
290 debug_assert!(value >= -1);
291 self.parent_index.set(value);
292 }
293
294 #[inline]
296 pub(crate) fn size_type(&self) -> LayoutTimeSizeType {
297 self.size_type.get()
298 }
299
300 #[inline]
301 pub(crate) fn set_size_type(&self, value: LayoutTimeSizeType) {
302 self.size_type.set(value);
303 }
304
305 #[inline]
307 pub(crate) fn measure_size(&self) -> f64 {
308 self.measure_size.get()
309 }
310
311 #[inline]
312 pub(crate) fn set_measure_size(&self, value: f64) {
313 self.measure_size.set(value);
314 }
315
316 pub(crate) fn preferred_size(&self) -> f64 {
320 let mut preferred_size = self.min_size();
321 if self.size_type.get() != LayoutTimeSizeType::AUTO && preferred_size < self.measure_size.get() {
322 preferred_size = self.measure_size.get();
323 }
324 preferred_size
325 }
326
327 #[inline]
329 pub(crate) fn size_cache(&self) -> f64 {
330 self.size_cache.get()
331 }
332
333 #[inline]
334 pub(crate) fn set_size_cache(&self, value: f64) {
335 self.size_cache.set(value);
336 }
337
338 pub(crate) fn min_size(&self) -> f64 {
340 let mut min_size = self.min_size.get();
341 if self.use_shared_minimum() {
342 if let Some(shared_state) = self.shared_state.borrow().as_ref() {
343 let shared_min_size = shared_state.min_size();
344 if min_size < shared_min_size {
345 min_size = shared_min_size;
346 }
347 }
348 }
349 min_size
350 }
351
352 pub(crate) fn min_size_for_arrange(&self) -> f64 {
354 let mut min_size = self.min_size.get();
355 if let Some(shared_state) = self.shared_state.borrow().as_ref() {
356 if self.use_shared_minimum() || !self.layout_was_updated() {
357 let shared_min_size = shared_state.min_size();
358 if min_size < shared_min_size {
359 min_size = shared_min_size;
360 }
361 }
362 }
363 min_size
364 }
365
366 #[inline]
374 pub(crate) fn raw_min_size(&self) -> f64 {
375 self.min_size.get()
376 }
377
378 #[inline]
380 pub(crate) fn final_offset(&self) -> f64 {
381 self.offset.get()
382 }
383
384 #[inline]
385 pub(crate) fn set_final_offset(&self, value: f64) {
386 self.offset.set(value);
387 }
388
389 pub(crate) fn parent(&self) -> Option<Ref<Grid>> {
391 self.parent.borrow().as_ref().and_then(WeakRef::upgrade)
392 }
393
394 pub(crate) fn set_parent(&self, value: Option<&Ref<Grid>>) {
395 *self.parent.borrow_mut() = value.map(Ref::downgrade);
396 }
397
398 fn register_with_shared_state(&self, private_shared_size_scope: &Rc<SharedSizeScope>, shared_size_group: &str) {
400 let shared_state = private_shared_size_scope.ensure_shared_state(shared_size_group);
401 *self.shared_state.borrow_mut() = Some(shared_state.clone());
402 shared_state.add_member(self);
403 }
404
405 fn unregister_from_shared_state(&self) {
407 let shared_state = self.shared_state.borrow().clone();
408 if let Some(shared_state) = shared_state {
409 shared_state.remove_member(self);
410 *self.shared_state.borrow_mut() = None;
411 }
412 }
413
414 fn on_shared_size_group_property_changed(definition: &DefinitionBase, e: &FerroPropertyChangedEventArgs<'_>) {
415 if definition.parent().is_some() {
416 let shared_size_group_id = e.get_new_value::<Option<String>>();
417
418 definition.unregister_from_shared_state();
422
423 if let Some(shared_size_group_id) = shared_size_group_id {
424 if let Some(private_shared_size_scope) =
425 definition.get_value(Self::private_shared_size_scope_property())
426 {
427 definition.register_with_shared_state(&private_shared_size_scope, &shared_size_group_id);
431 }
432 }
433 }
434 }
435
436 fn shared_size_group_property_value_valid(id: &Option<String>) -> bool {
441 let Some(id) = id else {
443 return true;
444 };
445
446 if id.is_empty() {
447 return false;
448 }
449
450 id.chars().enumerate().all(|(i, c)| {
451 let is_digit = c.is_numeric();
452 !(i == 0 && is_digit) && (is_digit || c.is_alphabetic() || c == '_')
453 })
454 }
455
456 fn on_private_shared_size_scope_property_changed(
460 definition: &DefinitionBase,
461 e: &FerroPropertyChangedEventArgs<'_>,
462 ) {
463 if definition.parent().is_some() {
464 definition.unregister_from_shared_state();
468
469 if let Some(private_shared_size_scope) = e.get_new_value::<Option<Rc<SharedSizeScope>>>() {
470 if let Some(shared_size_group) = definition.shared_size_group() {
471 definition.register_with_shared_state(&private_shared_size_scope, &shared_size_group);
475 }
476 }
477 }
478 }
479
480 #[inline]
481 fn set_flags(&self, value: bool, flags: u8) {
482 self.flags.set(if value {
483 self.flags.get() | flags
484 } else {
485 self.flags.get() & !flags
486 });
487 }
488
489 #[inline]
490 fn check_flags_and(&self, flags: u8) -> bool {
491 (self.flags.get() & flags) == flags
492 }
493
494 #[inline]
495 fn use_shared_minimum(&self) -> bool {
496 self.check_flags_and(FLAG_USE_SHARED_MINIMUM)
497 }
498
499 #[inline]
500 fn set_use_shared_minimum(&self, value: bool) {
501 self.set_flags(value, FLAG_USE_SHARED_MINIMUM)
502 }
503
504 #[inline]
505 fn layout_was_updated(&self) -> bool {
506 self.check_flags_and(FLAG_LAYOUT_WAS_UPDATED)
507 }
508
509 #[inline]
510 fn set_layout_was_updated(&self, value: bool) {
511 self.set_flags(value, FLAG_LAYOUT_WAS_UPDATED)
512 }
513
514 pub(crate) fn affects_parent_measure(properties: &[&'static FerroProperty]) {
517 for property in properties {
518 property.changed().subscribe(|e| {
519 if let Some(parent) = e
520 .sender()
521 .downcast_ref::<DefinitionBase>()
522 .and_then(DefinitionBase::parent)
523 {
524 parent.invalidate_measure();
525 }
526 });
527 }
528 }
529}
530
531pub(crate) struct SharedSizeScope {
533 this: Weak<SharedSizeScope>,
534 registry: RefCell<HashMap<String, Rc<SharedSizeState>>>,
536}
537
538impl PartialEq for SharedSizeScope {
539 fn eq(&self, other: &Self) -> bool {
540 std::ptr::eq(self, other)
541 }
542}
543
544impl SharedSizeScope {
545 fn new() -> Rc<SharedSizeScope> {
546 Rc::new_cyclic(|this| SharedSizeScope {
547 this: this.clone(),
548 registry: RefCell::new(HashMap::new()),
549 })
550 }
551
552 fn ensure_shared_state(&self, shared_size_group: &str) -> Rc<SharedSizeState> {
555 if let Some(shared_state) = self.registry.borrow().get(shared_size_group) {
556 return shared_state.clone();
557 }
558
559 let shared_state = SharedSizeState::new(self.this.clone(), shared_size_group);
560 self.registry
561 .borrow_mut()
562 .insert(shared_size_group.to_owned(), shared_state.clone());
563 shared_state
564 }
565
566 fn remove(&self, key: &str) {
568 self.registry.borrow_mut().remove(key);
569 }
570}
571
572type LayoutUpdatedHost = (WeakRef<Grid>, Rc<dyn IDisposable>);
575
576pub(crate) struct SharedSizeState {
578 this: Weak<SharedSizeState>,
579 shared_size_scope: Weak<SharedSizeScope>,
581 shared_size_group_id: String,
583 registry: RefCell<Vec<WeakRef<DefinitionBase>>>,
585 layout_updated_host: RefCell<Option<LayoutUpdatedHost>>,
587 broadcast_invalidation: Cell<bool>,
589 user_size_valid: Cell<bool>,
591 user_size: Cell<GridLength>,
592 min_size: Cell<f64>,
593}
594
595impl SharedSizeState {
596 fn new(shared_size_scope: Weak<SharedSizeScope>, shared_size_group_id: &str) -> Rc<Self> {
597 Rc::new_cyclic(|this| SharedSizeState {
598 this: this.clone(),
599 shared_size_scope,
600 shared_size_group_id: shared_size_group_id.to_owned(),
601 registry: RefCell::new(Vec::new()),
602 layout_updated_host: RefCell::new(None),
603 broadcast_invalidation: Cell::new(true),
604 user_size_valid: Cell::new(false),
605 user_size: Cell::new(GridLength::default()),
606 min_size: Cell::new(0.0),
607 })
608 }
609
610 fn member(&self, index: usize) -> Option<Ref<DefinitionBase>> {
612 self.registry.borrow().get(index).and_then(WeakRef::upgrade)
613 }
614
615 fn member_count(&self) -> usize {
616 self.registry.borrow().len()
617 }
618
619 fn add_member(&self, member: &DefinitionBase) {
621 let member = member.to_ref();
622 debug_assert!(!self.registry.borrow().iter().any(|m| m.points_to(&member)));
623 self.registry.borrow_mut().push(member.downgrade());
624 self.invalidate();
625 }
626
627 fn remove_member(&self, member: &DefinitionBase) {
632 self.invalidate();
633 let member = member.to_ref();
634 self.registry
635 .borrow_mut()
636 .retain(|m| !m.points_to(&member) && m.upgrade().is_some());
637 self.remove_from_scope_if_empty();
638 }
639
640 fn remove_dropped_members(&self) {
642 self.registry.borrow_mut().retain(|m| m.upgrade().is_some());
643 self.remove_from_scope_if_empty();
644 }
645
646 fn remove_from_scope_if_empty(&self) {
647 if self.registry.borrow().is_empty() {
648 if let Some(shared_size_scope) = self.shared_size_scope.upgrade() {
649 shared_size_scope.remove(&self.shared_size_group_id);
650 }
651 }
652 }
653
654 fn invalidate(&self) {
657 self.user_size_valid.set(false);
658
659 if self.broadcast_invalidation.get() {
660 for i in 0..self.member_count() {
661 if let Some(parent_grid) = self.member(i).and_then(|m| m.parent()) {
662 parent_grid.invalidate();
663 }
664 }
665 self.broadcast_invalidation.set(false);
666 }
667 }
668
669 fn ensure_deferred_validation(&self, layout_updated_host: &Grid) {
672 let has_host = self
673 .layout_updated_host
674 .borrow()
675 .as_ref()
676 .is_some_and(|(host, _)| host.upgrade().is_some());
677 if !has_host {
678 let this = self.this.clone();
679 let subscription = layout_updated_host.layout_updated(move || {
680 if let Some(this) = this.upgrade() {
681 this.on_layout_updated();
682 }
683 });
684 *self.layout_updated_host.borrow_mut() = Some((layout_updated_host.to_ref().downgrade(), subscription));
685 }
686 }
687
688 fn min_size(&self) -> f64 {
689 if !self.user_size_valid.get() {
690 self.ensure_user_size_valid();
691 }
692 self.min_size.get()
693 }
694
695 fn user_size(&self) -> GridLength {
696 if !self.user_size_valid.get() {
697 self.ensure_user_size_valid();
698 }
699 self.user_size.get()
700 }
701
702 fn ensure_user_size_valid(&self) {
703 let mut user_size = GridLength::new(1.0, GridUnitType::Auto);
704
705 for i in 0..self.member_count() {
706 debug_assert!(
707 user_size.grid_unit_type() == GridUnitType::Auto || user_size.grid_unit_type() == GridUnitType::Pixel
708 );
709
710 let Some(member) = self.member(i) else {
711 continue;
712 };
713 let current_grid_length = member.user_size_value_cache();
714 if current_grid_length.grid_unit_type() == GridUnitType::Pixel
715 && (user_size.grid_unit_type() == GridUnitType::Auto || user_size.value() < current_grid_length.value())
716 {
717 user_size = current_grid_length;
718 }
719 }
720 self.user_size.set(user_size);
721
722 self.min_size.set(if user_size.is_absolute() {
727 user_size.value()
728 } else {
729 0.0
730 });
731
732 self.user_size_valid.set(true);
733 }
734
735 fn on_layout_updated(&self) {
739 let mut shared_min_size: f64 = 0.0;
740
741 for i in 0..self.member_count() {
743 if let Some(member) = self.member(i) {
744 shared_min_size = math_max(shared_min_size, member.min_size.get());
745 }
746 }
747
748 let shared_min_size_changed = !MathUtilities::are_close(self.min_size.get(), shared_min_size);
749
750 for i in 0..self.member_count() {
753 let Some(definition_base) = self.member(i) else {
754 continue;
755 };
756
757 let use_shared_minimum = !MathUtilities::are_close(definition_base.min_size.get(), shared_min_size);
768
769 let measure_is_valid = if !definition_base.use_shared_minimum() {
773 !use_shared_minimum
777 } else if use_shared_minimum {
778 !shared_min_size_changed
781 } else {
782 definition_base.layout_was_updated()
792 && MathUtilities::greater_than_or_close(definition_base.min_size.get(), self.min_size())
793 };
794
795 if !measure_is_valid {
796 if let Some(parent) = definition_base.parent() {
797 parent.invalidate_measure();
798 }
799 } else if !MathUtilities::are_close(shared_min_size, definition_base.size_cache()) {
800 if let Some(parent) = definition_base.parent() {
804 parent.invalidate_arrange();
805 }
806 }
807
808 definition_base.set_use_shared_minimum(use_shared_minimum);
810 definition_base.set_layout_was_updated(false);
811 }
812
813 self.min_size.set(shared_min_size);
814
815 let layout_updated_host = self.layout_updated_host.borrow_mut().take();
816 if let Some((_, subscription)) = layout_updated_host {
817 subscription.dispose();
818 }
819
820 self.broadcast_invalidation.set(true);
821 }
822}