1use crate::{Control, ControlImpl, Panel, PanelImpl};
5use ferroui_base::input::InputElementImpl;
6use ferroui_base::interactivity::InteractiveImpl;
7use ferroui_base::layout::{HorizontalAlignment, Layoutable, LayoutableImpl, VerticalAlignment};
8use ferroui_base::utilities::math_utilities::max;
9use ferroui_base::{
10 ferro_class, ferro_impl_classes, ferro_property, instantiate, AttachedProperty, BoxedValue, FerroObject,
11 FerroObjectImpl, FerroProperty, Rect, Ref, Size, StyledElementImpl, Visual, VisualImpl,
12};
13use std::cell::RefCell;
14use std::collections::{HashMap, HashSet};
15
16#[repr(C)]
19pub struct RelativePanel {
20 base: Panel,
21 child_graph: RefCell<Graph>,
22}
23
24ferro_class!(RelativePanel: Panel);
25ferroui_base::ferro_class_info!(RelativePanel { new: RelativePanel::new });
26ferro_impl_classes!(
27 RelativePanel: StyledElementImpl,
28 VisualImpl,
29 InteractiveImpl,
30 InputElementImpl,
31 ControlImpl,
32 PanelImpl
33);
34
35ferroui_base::ferro_impl_classes!(RelativePanel: FerroObjectImpl);
36
37impl LayoutableImpl for RelativePanel {
38 fn measure_override(this: &Self, available_size: Size) -> Size {
39 let mut child_graph = this.child_graph.take();
42
43 child_graph.clear();
44 for child in this.children().snapshot().iter() {
45 let node = child_graph.add_node(child.clone().upcast());
46
47 let link = child_graph.add_link(node, this.get_dependency_element(Self::align_left_with_property(), child));
48 child_graph.nodes[node].align_left_with_node = link;
49 let link = child_graph.add_link(node, this.get_dependency_element(Self::align_top_with_property(), child));
50 child_graph.nodes[node].align_top_with_node = link;
51 let link =
52 child_graph.add_link(node, this.get_dependency_element(Self::align_right_with_property(), child));
53 child_graph.nodes[node].align_right_with_node = link;
54 let link =
55 child_graph.add_link(node, this.get_dependency_element(Self::align_bottom_with_property(), child));
56 child_graph.nodes[node].align_bottom_with_node = link;
57
58 let link = child_graph.add_link(node, this.get_dependency_element(Self::left_of_property(), child));
59 child_graph.nodes[node].left_of_node = link;
60 let link = child_graph.add_link(node, this.get_dependency_element(Self::above_property(), child));
61 child_graph.nodes[node].above_node = link;
62 let link = child_graph.add_link(node, this.get_dependency_element(Self::right_of_property(), child));
63 child_graph.nodes[node].right_of_node = link;
64 let link = child_graph.add_link(node, this.get_dependency_element(Self::below_property(), child));
65 child_graph.nodes[node].below_node = link;
66
67 let link = child_graph
68 .add_link(node, this.get_dependency_element(Self::align_horizontal_center_with_property(), child));
69 child_graph.nodes[node].align_horizontal_center_with = link;
70 let link = child_graph
71 .add_link(node, this.get_dependency_element(Self::align_vertical_center_with_property(), child));
72 child_graph.nodes[node].align_vertical_center_with = link;
73 }
74
75 child_graph.measure(available_size);
76
77 child_graph.reset(false);
78 let calc_width = this.width().is_nan() && (this.horizontal_alignment() != HorizontalAlignment::Stretch);
79 let calc_height = this.height().is_nan() && (this.vertical_alignment() != VerticalAlignment::Stretch);
80
81 let bounding_size = child_graph.get_bounding_size(calc_width, calc_height);
82 child_graph.reset(true);
83 child_graph.measure(bounding_size);
84
85 this.child_graph.replace(child_graph);
86
87 bounding_size
88 }
89
90 fn arrange_override(this: &Self, arrange_size: Size) -> Size {
91 let slots: Vec<(Ref<Layoutable>, Rect)> = this
94 .child_graph
95 .borrow()
96 .nodes
97 .iter()
98 .map(|node| (node.element.clone(), node.arrange_rect(arrange_size)))
99 .collect();
100 for (element, rect) in slots {
101 element.arrange(rect);
102 }
103 arrange_size
104 }
105}
106
107ferroui_base::ferro_properties! { impl RelativePanel, also [
108 RelativePanel::above_property,
109 RelativePanel::align_bottom_with_panel_property,
110 RelativePanel::align_bottom_with_property,
111 RelativePanel::align_horizontal_center_with_panel_property,
112 RelativePanel::align_horizontal_center_with_property,
113 RelativePanel::align_left_with_panel_property,
114 RelativePanel::align_left_with_property,
115 RelativePanel::align_right_with_panel_property,
116 RelativePanel::align_right_with_property,
117 RelativePanel::align_top_with_panel_property,
118 RelativePanel::align_top_with_property,
119 RelativePanel::align_vertical_center_with_panel_property,
120 RelativePanel::align_vertical_center_with_property,
121 RelativePanel::below_property,
122 RelativePanel::left_of_property,
123 RelativePanel::right_of_property,
124] {} }
125
126impl RelativePanel {
127 fn static_constructor() {
128 Visual::clip_to_bounds_property().override_default_value::<RelativePanel>(true);
129
130 let properties = [
131 Self::align_left_with_panel_property().as_property(),
132 Self::align_left_with_property().as_property(),
133 Self::left_of_property().as_property(),
134 Self::align_right_with_panel_property().as_property(),
135 Self::align_right_with_property().as_property(),
136 Self::right_of_property().as_property(),
137 Self::align_top_with_panel_property().as_property(),
138 Self::align_top_with_property().as_property(),
139 Self::above_property().as_property(),
140 Self::align_bottom_with_panel_property().as_property(),
141 Self::align_bottom_with_property().as_property(),
142 Self::below_property().as_property(),
143 Self::align_horizontal_center_with_panel_property().as_property(),
144 Self::align_horizontal_center_with_property().as_property(),
145 Self::align_vertical_center_with_panel_property().as_property(),
146 Self::align_vertical_center_with_property().as_property(),
147 ];
148 Panel::affects_parent_arrange::<RelativePanel>(&properties);
149 Panel::affects_parent_measure::<RelativePanel>(&properties);
150 }
151
152 pub fn construct() -> Self {
154 Self { base: Panel::construct(), child_graph: RefCell::new(Graph::default()) }
155 }
156
157 pub fn new() -> Ref<Self> {
158 instantiate(Self::construct())
159 }
160
161 fn get_dependency_element(
167 &self,
168 property: &'static AttachedProperty<Option<BoxedValue>>,
169 child: &Ref<Control>,
170 ) -> Option<Ref<Layoutable>> {
171 let dependency = child.get_value(property)?;
172 let dependency = &*dependency;
173
174 let layoutable = match dependency.downcast_ref::<Ref<Control>>() {
176 Some(control) => control.clone().upcast::<Layoutable>(),
177 None => match dependency.downcast_ref::<Ref<Layoutable>>() {
178 Some(layoutable) => layoutable.clone(),
179 None => ferroui_base::data::core::ValueTypes::as_object(dependency)?.cast::<Layoutable>()?,
180 },
181 };
182
183 if self.children().snapshot().iter().any(|c| *c == layoutable) {
184 return Some(layoutable);
185 }
186
187 panic!("RelativePanel error: Element does not exist in the current context: {}", property.name());
188 }
189
190 ferro_property!(for RelativePanel;
191 pub fn above_property() -> AttachedProperty<Option<BoxedValue>> {
193 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("Above", None)
194 }
195 );
196
197 pub fn get_above(obj: &FerroObject) -> Option<BoxedValue> {
199 obj.get_value(Self::above_property())
200 }
201
202 pub fn set_above(obj: &FerroObject, value: Option<BoxedValue>) {
206 obj.set_value(Self::above_property(), value)
207 }
208
209 ferro_property!(for RelativePanel;
210 pub fn align_bottom_with_panel_property() -> AttachedProperty<bool> {
212 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignBottomWithPanel", false)
213 }
214 );
215
216 pub fn get_align_bottom_with_panel(obj: &FerroObject) -> bool {
218 obj.get_value(Self::align_bottom_with_panel_property())
219 }
220
221 pub fn set_align_bottom_with_panel(obj: &FerroObject, value: bool) {
223 obj.set_value(Self::align_bottom_with_panel_property(), value)
224 }
225
226 ferro_property!(for RelativePanel;
227 pub fn align_bottom_with_property() -> AttachedProperty<Option<BoxedValue>> {
229 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignBottomWith", None)
230 }
231 );
232
233 pub fn get_align_bottom_with(obj: &FerroObject) -> Option<BoxedValue> {
235 obj.get_value(Self::align_bottom_with_property())
236 }
237
238 pub fn set_align_bottom_with(obj: &FerroObject, value: Option<BoxedValue>) {
242 obj.set_value(Self::align_bottom_with_property(), value)
243 }
244
245 ferro_property!(for RelativePanel;
246 pub fn align_horizontal_center_with_panel_property() -> AttachedProperty<bool> {
248 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignHorizontalCenterWithPanel", false)
249 }
250 );
251
252 pub fn get_align_horizontal_center_with_panel(obj: &FerroObject) -> bool {
254 obj.get_value(Self::align_horizontal_center_with_panel_property())
255 }
256
257 pub fn set_align_horizontal_center_with_panel(obj: &FerroObject, value: bool) {
259 obj.set_value(Self::align_horizontal_center_with_panel_property(), value)
260 }
261
262 ferro_property!(for RelativePanel;
263 pub fn align_horizontal_center_with_property() -> AttachedProperty<Option<BoxedValue>> {
265 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignHorizontalCenterWith", None)
266 }
267 );
268
269 pub fn get_align_horizontal_center_with(obj: &FerroObject) -> Option<BoxedValue> {
271 obj.get_value(Self::align_horizontal_center_with_property())
272 }
273
274 pub fn set_align_horizontal_center_with(obj: &FerroObject, value: Option<BoxedValue>) {
278 obj.set_value(Self::align_horizontal_center_with_property(), value)
279 }
280
281 ferro_property!(for RelativePanel;
282 pub fn align_left_with_panel_property() -> AttachedProperty<bool> {
284 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignLeftWithPanel", false)
285 }
286 );
287
288 pub fn get_align_left_with_panel(obj: &FerroObject) -> bool {
290 obj.get_value(Self::align_left_with_panel_property())
291 }
292
293 pub fn set_align_left_with_panel(obj: &FerroObject, value: bool) {
295 obj.set_value(Self::align_left_with_panel_property(), value)
296 }
297
298 ferro_property!(for RelativePanel;
299 pub fn align_left_with_property() -> AttachedProperty<Option<BoxedValue>> {
301 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignLeftWith", None)
302 }
303 );
304
305 pub fn get_align_left_with(obj: &FerroObject) -> Option<BoxedValue> {
307 obj.get_value(Self::align_left_with_property())
308 }
309
310 pub fn set_align_left_with(obj: &FerroObject, value: Option<BoxedValue>) {
314 obj.set_value(Self::align_left_with_property(), value)
315 }
316
317 ferro_property!(for RelativePanel;
318 pub fn align_right_with_panel_property() -> AttachedProperty<bool> {
320 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignRightWithPanel", false)
321 }
322 );
323
324 pub fn get_align_right_with_panel(obj: &FerroObject) -> bool {
326 obj.get_value(Self::align_right_with_panel_property())
327 }
328
329 pub fn set_align_right_with_panel(obj: &FerroObject, value: bool) {
331 obj.set_value(Self::align_right_with_panel_property(), value)
332 }
333
334 ferro_property!(for RelativePanel;
335 pub fn align_right_with_property() -> AttachedProperty<Option<BoxedValue>> {
337 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignRightWith", None)
338 }
339 );
340
341 pub fn get_align_right_with(obj: &FerroObject) -> Option<BoxedValue> {
343 obj.get_value(Self::align_right_with_property())
344 }
345
346 pub fn set_align_right_with(obj: &FerroObject, value: Option<BoxedValue>) {
350 obj.set_value(Self::align_right_with_property(), value)
351 }
352
353 ferro_property!(for RelativePanel;
354 pub fn align_top_with_panel_property() -> AttachedProperty<bool> {
356 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignTopWithPanel", false)
357 }
358 );
359
360 pub fn get_align_top_with_panel(obj: &FerroObject) -> bool {
362 obj.get_value(Self::align_top_with_panel_property())
363 }
364
365 pub fn set_align_top_with_panel(obj: &FerroObject, value: bool) {
367 obj.set_value(Self::align_top_with_panel_property(), value)
368 }
369
370 ferro_property!(for RelativePanel;
371 pub fn align_top_with_property() -> AttachedProperty<Option<BoxedValue>> {
373 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignTopWith", None)
374 }
375 );
376
377 pub fn get_align_top_with(obj: &FerroObject) -> Option<BoxedValue> {
379 obj.get_value(Self::align_top_with_property())
380 }
381
382 pub fn set_align_top_with(obj: &FerroObject, value: Option<BoxedValue>) {
386 obj.set_value(Self::align_top_with_property(), value)
387 }
388
389 ferro_property!(for RelativePanel;
390 pub fn align_vertical_center_with_panel_property() -> AttachedProperty<bool> {
392 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignVerticalCenterWithPanel", false)
393 }
394 );
395
396 pub fn get_align_vertical_center_with_panel(obj: &FerroObject) -> bool {
398 obj.get_value(Self::align_vertical_center_with_panel_property())
399 }
400
401 pub fn set_align_vertical_center_with_panel(obj: &FerroObject, value: bool) {
403 obj.set_value(Self::align_vertical_center_with_panel_property(), value)
404 }
405
406 ferro_property!(for RelativePanel;
407 pub fn align_vertical_center_with_property() -> AttachedProperty<Option<BoxedValue>> {
409 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignVerticalCenterWith", None)
410 }
411 );
412
413 pub fn get_align_vertical_center_with(obj: &FerroObject) -> Option<BoxedValue> {
415 obj.get_value(Self::align_vertical_center_with_property())
416 }
417
418 pub fn set_align_vertical_center_with(obj: &FerroObject, value: Option<BoxedValue>) {
422 obj.set_value(Self::align_vertical_center_with_property(), value)
423 }
424
425 ferro_property!(for RelativePanel;
426 pub fn below_property() -> AttachedProperty<Option<BoxedValue>> {
428 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("Below", None)
429 }
430 );
431
432 pub fn get_below(obj: &FerroObject) -> Option<BoxedValue> {
434 obj.get_value(Self::below_property())
435 }
436
437 pub fn set_below(obj: &FerroObject, value: Option<BoxedValue>) {
441 obj.set_value(Self::below_property(), value)
442 }
443
444 ferro_property!(for RelativePanel;
445 pub fn left_of_property() -> AttachedProperty<Option<BoxedValue>> {
447 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("LeftOf", None)
448 }
449 );
450
451 pub fn get_left_of(obj: &FerroObject) -> Option<BoxedValue> {
453 obj.get_value(Self::left_of_property())
454 }
455
456 pub fn set_left_of(obj: &FerroObject, value: Option<BoxedValue>) {
460 obj.set_value(Self::left_of_property(), value)
461 }
462
463 ferro_property!(for RelativePanel;
464 pub fn right_of_property() -> AttachedProperty<Option<BoxedValue>> {
466 FerroProperty::register_attached::<RelativePanel, Layoutable, _>("RightOf", None)
467 }
468 );
469
470 pub fn get_right_of(obj: &FerroObject) -> Option<BoxedValue> {
472 obj.get_value(Self::right_of_property())
473 }
474
475 pub fn set_right_of(obj: &FerroObject, value: Option<BoxedValue>) {
479 obj.set_value(Self::right_of_property(), value)
480 }
481}
482
483struct GraphNode {
484 measured: bool,
485 element: Ref<Layoutable>,
486 horizontal_offset_flag: bool,
487 vertical_offset_flag: bool,
488 bounding_size: Size,
489 origin_desired_size: Size,
490 left: f64,
491 top: f64,
492 right: f64,
493 bottom: f64,
494 outgoing_nodes: Vec<usize>,
496 align_left_with_node: Option<usize>,
497 align_top_with_node: Option<usize>,
498 align_right_with_node: Option<usize>,
499 align_bottom_with_node: Option<usize>,
500 left_of_node: Option<usize>,
501 above_node: Option<usize>,
502 right_of_node: Option<usize>,
503 below_node: Option<usize>,
504 align_horizontal_center_with: Option<usize>,
505 align_vertical_center_with: Option<usize>,
506}
507
508impl GraphNode {
509 fn new(element: Ref<Layoutable>) -> Self {
510 Self {
511 measured: false,
512 element,
513 horizontal_offset_flag: false,
514 vertical_offset_flag: false,
515 bounding_size: Size::default(),
516 origin_desired_size: Size::default(),
517 left: f64::NAN,
518 top: f64::NAN,
519 right: f64::NAN,
520 bottom: f64::NAN,
521 outgoing_nodes: Vec::new(),
522 align_left_with_node: None,
523 align_top_with_node: None,
524 align_right_with_node: None,
525 align_bottom_with_node: None,
526 left_of_node: None,
527 above_node: None,
528 right_of_node: None,
529 below_node: None,
530 align_horizontal_center_with: None,
531 align_vertical_center_with: None,
532 }
533 }
534
535 fn arrange_rect(&self, arrange_size: Size) -> Rect {
536 Rect::new(
537 self.left,
538 self.top,
539 max(arrange_size.width - self.left - self.right, 0.0),
540 max(arrange_size.height - self.top - self.bottom, 0.0),
541 )
542 }
543
544 fn reset(&mut self, clear_pos: bool) {
545 if clear_pos {
546 self.left = f64::NAN;
547 self.top = f64::NAN;
548 self.right = f64::NAN;
549 self.bottom = f64::NAN;
550 }
551
552 self.measured = false;
553 }
554}
555
556#[derive(Clone, Copy)]
559enum NodeSet {
560 All,
561 OutgoingOf(usize),
562}
563
564#[derive(Default)]
567struct Graph {
568 node_dic: HashMap<Ref<Layoutable>, usize>,
569 nodes: Vec<GraphNode>,
570 available_size: Size,
571}
572
573impl Graph {
574 fn clear(&mut self) {
575 self.available_size = Size::default();
576 self.node_dic.clear();
577 self.nodes.clear();
578 }
579
580 fn reset(&mut self, clear_pos: bool) {
581 for node in &mut self.nodes {
582 node.reset(clear_pos);
583 }
584 }
585
586 fn add_link(&mut self, from: usize, to: Option<Ref<Layoutable>>) -> Option<usize> {
587 let to = to?;
588 let node_to = self.add_node(to);
589
590 if !self.nodes[from].outgoing_nodes.contains(&node_to) {
591 self.nodes[from].outgoing_nodes.push(node_to);
592 }
593 Some(node_to)
594 }
595
596 fn add_node(&mut self, value: Ref<Layoutable>) -> usize {
597 if let Some(&node) = self.node_dic.get(&value) {
598 return node;
599 }
600
601 let node = self.nodes.len();
602 self.nodes.push(GraphNode::new(value.clone()));
603 self.node_dic.insert(value, node);
604 node
605 }
606
607 fn node_count(&self, set: NodeSet) -> usize {
608 match set {
609 NodeSet::All => self.nodes.len(),
610 NodeSet::OutgoingOf(node) => self.nodes[node].outgoing_nodes.len(),
611 }
612 }
613
614 fn node_at(&self, set: NodeSet, index: usize) -> usize {
615 match set {
616 NodeSet::All => index,
617 NodeSet::OutgoingOf(node) => self.nodes[node].outgoing_nodes[index],
618 }
619 }
620
621 fn measure(&mut self, available_size: Size) {
622 self.available_size = available_size;
623 let mut set = HashSet::new();
624 self.measure_nodes(NodeSet::All, &mut set);
625 }
626
627 fn measure_nodes(&mut self, nodes: NodeSet, set: &mut HashSet<usize>) {
628 for index in 0..self.node_count(nodes) {
629 let node = self.node_at(nodes, index);
630
631 if !self.nodes[node].measured && self.nodes[node].outgoing_nodes.is_empty() {
632 self.measure_child(node);
633 continue;
634 }
635
636 if self.nodes[node].outgoing_nodes.iter().all(|&item| self.nodes[item].measured) {
637 self.measure_child(node);
638 continue;
639 }
640
641 if !set.insert(node) {
642 panic!("RelativePanel error: Circular dependency detected. Layout could not complete.");
643 }
644
645 self.measure_nodes(NodeSet::OutgoingOf(node), set);
646
647 if !self.nodes[node].measured {
648 self.measure_child(node);
649 }
650 }
651 }
652
653 fn measure_child(&mut self, index: usize) {
654 let available_size = self.available_size;
655 let child = self.nodes[index].element.clone();
656 child.measure(Size::new(f64::INFINITY, f64::INFINITY));
657 self.nodes[index].origin_desired_size = child.desired_size();
658
659 let align_left_with_panel = RelativePanel::get_align_left_with_panel(&child);
660 let align_top_with_panel = RelativePanel::get_align_top_with_panel(&child);
661 let align_right_with_panel = RelativePanel::get_align_right_with_panel(&child);
662 let align_bottom_with_panel = RelativePanel::get_align_bottom_with_panel(&child);
663
664 let left_of = |node: Option<usize>| node.map(|n| self.nodes[n].left);
666 let top_of = |node: Option<usize>| node.map(|n| self.nodes[n].top);
667 let right_of = |node: Option<usize>| node.map(|n| self.nodes[n].right);
668 let bottom_of = |node: Option<usize>| node.map(|n| self.nodes[n].bottom);
669
670 let node = &self.nodes[index];
671 let align_left_with_left = left_of(node.align_left_with_node);
672 let align_top_with_top = top_of(node.align_top_with_node);
673 let align_right_with_right = right_of(node.align_right_with_node);
674 let align_bottom_with_bottom = bottom_of(node.align_bottom_with_node);
675 let below_bottom = bottom_of(node.below_node);
676 let right_of_right = right_of(node.right_of_node);
677 let left_of_left = left_of(node.left_of_node);
678 let above_top = top_of(node.above_node);
679 let horizontal_center =
680 node.align_horizontal_center_with.map(|n| (self.nodes[n].left, self.nodes[n].right));
681 let vertical_center = node.align_vertical_center_with.map(|n| (self.nodes[n].top, self.nodes[n].bottom));
682
683 let node = &mut self.nodes[index];
684
685 if align_left_with_panel {
686 node.left = 0.0;
687 }
688 if align_top_with_panel {
689 node.top = 0.0;
690 }
691 if align_right_with_panel {
692 node.right = 0.0;
693 }
694 if align_bottom_with_panel {
695 node.bottom = 0.0;
696 }
697
698 if let Some(left) = align_left_with_left {
699 node.left = if node.left.is_nan() { left } else { left * 0.5 };
700 }
701
702 if let Some(top) = align_top_with_top {
703 node.top = if node.top.is_nan() { top } else { top * 0.5 };
704 }
705
706 if let Some(right) = align_right_with_right {
707 node.right = if node.right.is_nan() { right } else { right * 0.5 };
708 }
709
710 if let Some(bottom) = align_bottom_with_bottom {
711 node.bottom = if node.bottom.is_nan() { bottom } else { bottom * 0.5 };
712 }
713
714 if let Some(bottom) = below_bottom {
715 if node.top.is_nan() {
716 node.top = available_size.height - bottom;
717 }
718 }
719
720 if let Some(right) = right_of_right {
721 if node.left.is_nan() {
722 node.left = available_size.width - right;
723 }
724 }
725
726 let mut available_height = available_size.height - node.top - node.bottom;
727 if available_height.is_nan() {
728 available_height = available_size.height;
729
730 if !node.top.is_nan() && node.bottom.is_nan() {
731 available_height -= node.top;
732 } else if node.top.is_nan() && !node.bottom.is_nan() {
733 available_height -= node.bottom;
734 }
735 }
736
737 let mut available_width = available_size.width - node.left - node.right;
738 if available_width.is_nan() {
739 available_width = available_size.width;
740
741 if !node.left.is_nan() && node.right.is_nan() {
742 available_width -= node.left;
743 } else if node.left.is_nan() && !node.right.is_nan() {
744 available_width -= node.right;
745 }
746 }
747
748 child.measure(Size::new(max(available_width, 0.0), max(available_height, 0.0)));
749 let child_size = child.desired_size();
750
751 let node = &mut self.nodes[index];
752
753 if let Some(left) = left_of_left {
754 if node.left.is_nan() {
755 node.left = left - child_size.width;
756 }
757 }
758
759 if let Some(top) = above_top {
760 if node.top.is_nan() {
761 node.top = top - child_size.height;
762 }
763 }
764
765 if let Some(right) = right_of_right {
766 if node.right.is_nan() {
767 node.right = right - child_size.width;
768 }
769 }
770
771 if let Some(bottom) = below_bottom {
772 if node.bottom.is_nan() {
773 node.bottom = bottom - child_size.height;
774 }
775 }
776
777 if let Some((left, right)) = horizontal_center {
778 let half_width_left = (available_size.width + left - right - child_size.width) * 0.5;
779 let half_width_right = (available_size.width - left + right - child_size.width) * 0.5;
780
781 if node.left.is_nan() {
782 node.left = half_width_left;
783 } else {
784 node.left = (node.left + half_width_left) * 0.5;
785 }
786
787 if node.right.is_nan() {
788 node.right = half_width_right;
789 } else {
790 node.right = (node.right + half_width_right) * 0.5;
791 }
792 }
793
794 if let Some((top, bottom)) = vertical_center {
795 let half_height_top = (available_size.height + top - bottom - child_size.height) * 0.5;
796 let half_height_bottom = (available_size.height - top + bottom - child_size.height) * 0.5;
797
798 if node.top.is_nan() {
799 node.top = half_height_top;
800 } else {
801 node.top = (node.top + half_height_top) * 0.5;
802 }
803
804 if node.bottom.is_nan() {
805 node.bottom = half_height_bottom;
806 } else {
807 node.bottom = (node.bottom + half_height_bottom) * 0.5;
808 }
809 }
810
811 if RelativePanel::get_align_horizontal_center_with_panel(&child) {
812 let half_sub_width = (available_size.width - child_size.width) * 0.5;
813
814 if node.left.is_nan() {
815 node.left = half_sub_width;
816 } else {
817 node.left = (node.left + half_sub_width) * 0.5;
818 }
819
820 if node.right.is_nan() {
821 node.right = half_sub_width;
822 } else {
823 node.right = (node.right + half_sub_width) * 0.5;
824 }
825 }
826
827 if RelativePanel::get_align_vertical_center_with_panel(&child) {
828 let half_sub_height = (available_size.height - child_size.height) * 0.5;
829
830 if node.top.is_nan() {
831 node.top = half_sub_height;
832 } else {
833 node.top = (node.top + half_sub_height) * 0.5;
834 }
835
836 if node.bottom.is_nan() {
837 node.bottom = half_sub_height;
838 } else {
839 node.bottom = (node.bottom + half_sub_height) * 0.5;
840 }
841 }
842
843 if node.left.is_nan() {
844 if !node.right.is_nan() {
845 node.left = available_size.width - node.right - child_size.width;
846 } else {
847 node.left = 0.0;
848 node.right = available_size.width - child_size.width;
849 }
850 } else if !node.left.is_nan() && node.right.is_nan() {
851 node.right = available_size.width - node.left - child_size.width;
852 }
853
854 if node.top.is_nan() {
855 if !node.bottom.is_nan() {
856 node.top = available_size.height - node.bottom - child_size.height;
857 } else {
858 node.top = 0.0;
859 node.bottom = available_size.height - child_size.height;
860 }
861 } else if !node.top.is_nan() && node.bottom.is_nan() {
862 node.bottom = available_size.height - node.top - child_size.height;
863 }
864
865 node.measured = true;
866 }
867
868 fn get_bounding_size(&mut self, calc_width: bool, calc_height: bool) -> Size {
869 let mut bounding_size = Size::default();
870
871 for node in 0..self.nodes.len() {
872 let size = self.get_node_bounding_size(node);
873 bounding_size = bounding_size.with_width(max(bounding_size.width, size.width));
874 bounding_size = bounding_size.with_height(max(bounding_size.height, size.height));
875 }
876
877 let available_width =
878 if self.available_size.width.is_infinite() { bounding_size.width } else { self.available_size.width };
879 let available_height =
880 if self.available_size.height.is_infinite() { bounding_size.height } else { self.available_size.height };
881
882 bounding_size = bounding_size.with_width(if calc_width { bounding_size.width } else { available_width });
883 bounding_size = bounding_size.with_height(if calc_height { bounding_size.height } else { available_height });
884 bounding_size
885 }
886
887 fn get_node_bounding_size(&mut self, index: usize) -> Size {
888 let node = &self.nodes[index];
889 if node.left < 0.0 || node.top < 0.0 {
890 return Size::default();
891 }
892 if node.measured {
893 return node.bounding_size;
894 }
895
896 let desired_size = node.element.desired_size();
897 let bounding_size = if node.outgoing_nodes.is_empty() {
898 desired_size
899 } else {
900 self.get_bounding_size_of(index, desired_size)
901 };
902
903 let node = &mut self.nodes[index];
904 node.bounding_size = bounding_size;
905 node.measured = true;
906 bounding_size
907 }
908
909 fn get_bounding_size_of(&mut self, prev_node: usize, mut prev_size: Size) -> Size {
912 for position in 0..self.nodes[prev_node].outgoing_nodes.len() {
913 let node = self.nodes[prev_node].outgoing_nodes[position];
914
915 if self.nodes[node].measured || self.nodes[node].outgoing_nodes.is_empty() {
916 let node_bounding_size = self.nodes[node].bounding_size;
917 let node_origin_desired_size = self.nodes[node].origin_desired_size;
918 let node_horizontal_offset_flag = self.nodes[node].horizontal_offset_flag;
919 let node_vertical_offset_flag = self.nodes[node].vertical_offset_flag;
920 let node_element = self.nodes[node].element.clone();
921 let prev = &mut self.nodes[prev_node];
922
923 if prev.left_of_node == Some(node) || prev.right_of_node == Some(node) {
924 prev_size = prev_size.with_width(prev_size.width + node_bounding_size.width);
925 if RelativePanel::get_align_horizontal_center_with_panel(&node_element)
926 || node_horizontal_offset_flag
927 {
928 prev_size = prev_size.with_width(prev_size.width + prev.origin_desired_size.width);
929 prev.horizontal_offset_flag = true;
930 }
931
932 if node_vertical_offset_flag {
933 prev.vertical_offset_flag = true;
934 }
935 }
936
937 if prev.above_node == Some(node) || prev.below_node == Some(node) {
938 prev_size = prev_size.with_height(prev_size.height + node_bounding_size.height);
939 if RelativePanel::get_align_vertical_center_with_panel(&node_element) || node_vertical_offset_flag {
940 prev_size = prev_size.with_height(prev_size.height + node_origin_desired_size.height);
941 prev.vertical_offset_flag = true;
942 }
943
944 if node_horizontal_offset_flag {
945 prev.horizontal_offset_flag = true;
946 }
947 }
948 } else {
949 return self.get_bounding_size_of(node, prev_size);
950 }
951 }
952
953 prev_size
954 }
955}