Skip to main content

ferroui_controls/
relative_panel.rs

1// The layout algorithm is adapted from the relative panel of the HandyControl
2// project (https://github.com/HandyOrg/HandyControl), MIT License.
3
4use 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/// A panel that arranges its children relative to each other and to the
17/// panel itself.
18#[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        // The graph is taken out of its cell for the duration of the pass:
40        // measuring children runs arbitrary code.
41        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        // The slots are computed before any child is arranged: arranging
92        // children runs arbitrary code.
93        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    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
153    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    /// The child of the panel that a relative-positioning property of `child`
162    /// refers to.
163    ///
164    /// Panics if the property refers to an element that is not a child of the
165    /// panel.
166    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        // `dependency is Layoutable`: held by any handle.
175        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        /// Defines the `Above` attached property.
192        pub fn above_property() -> AttachedProperty<Option<BoxedValue>> {
193            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("Above", None)
194        }
195    );
196
197    /// Gets the value of the `Above` attached property for an object.
198    pub fn get_above(obj: &FerroObject) -> Option<BoxedValue> {
199        obj.get_value(Self::above_property())
200    }
201
202    /// Sets the value of the `Above` attached property for an object. The
203    /// layout uses the value when it holds a control of the panel; see
204    /// [`Control::boxed`].
205    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        /// Defines the `AlignBottomWithPanel` attached property.
211        pub fn align_bottom_with_panel_property() -> AttachedProperty<bool> {
212            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignBottomWithPanel", false)
213        }
214    );
215
216    /// Gets the value of the `AlignBottomWithPanel` attached property for an object.
217    pub fn get_align_bottom_with_panel(obj: &FerroObject) -> bool {
218        obj.get_value(Self::align_bottom_with_panel_property())
219    }
220
221    /// Sets the value of the `AlignBottomWithPanel` attached property for an object.
222    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        /// Defines the `AlignBottomWith` attached property.
228        pub fn align_bottom_with_property() -> AttachedProperty<Option<BoxedValue>> {
229            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignBottomWith", None)
230        }
231    );
232
233    /// Gets the value of the `AlignBottomWith` attached property for an object.
234    pub fn get_align_bottom_with(obj: &FerroObject) -> Option<BoxedValue> {
235        obj.get_value(Self::align_bottom_with_property())
236    }
237
238    /// Sets the value of the `AlignBottomWith` attached property for an object. The
239    /// layout uses the value when it holds a control of the panel; see
240    /// [`Control::boxed`].
241    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        /// Defines the `AlignHorizontalCenterWithPanel` attached property.
247        pub fn align_horizontal_center_with_panel_property() -> AttachedProperty<bool> {
248            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignHorizontalCenterWithPanel", false)
249        }
250    );
251
252    /// Gets the value of the `AlignHorizontalCenterWithPanel` attached property for an object.
253    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    /// Sets the value of the `AlignHorizontalCenterWithPanel` attached property for an object.
258    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        /// Defines the `AlignHorizontalCenterWith` attached property.
264        pub fn align_horizontal_center_with_property() -> AttachedProperty<Option<BoxedValue>> {
265            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignHorizontalCenterWith", None)
266        }
267    );
268
269    /// Gets the value of the `AlignHorizontalCenterWith` attached property for an object.
270    pub fn get_align_horizontal_center_with(obj: &FerroObject) -> Option<BoxedValue> {
271        obj.get_value(Self::align_horizontal_center_with_property())
272    }
273
274    /// Sets the value of the `AlignHorizontalCenterWith` attached property for an object. The
275    /// layout uses the value when it holds a control of the panel; see
276    /// [`Control::boxed`].
277    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        /// Defines the `AlignLeftWithPanel` attached property.
283        pub fn align_left_with_panel_property() -> AttachedProperty<bool> {
284            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignLeftWithPanel", false)
285        }
286    );
287
288    /// Gets the value of the `AlignLeftWithPanel` attached property for an object.
289    pub fn get_align_left_with_panel(obj: &FerroObject) -> bool {
290        obj.get_value(Self::align_left_with_panel_property())
291    }
292
293    /// Sets the value of the `AlignLeftWithPanel` attached property for an object.
294    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        /// Defines the `AlignLeftWith` attached property.
300        pub fn align_left_with_property() -> AttachedProperty<Option<BoxedValue>> {
301            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignLeftWith", None)
302        }
303    );
304
305    /// Gets the value of the `AlignLeftWith` attached property for an object.
306    pub fn get_align_left_with(obj: &FerroObject) -> Option<BoxedValue> {
307        obj.get_value(Self::align_left_with_property())
308    }
309
310    /// Sets the value of the `AlignLeftWith` attached property for an object. The
311    /// layout uses the value when it holds a control of the panel; see
312    /// [`Control::boxed`].
313    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        /// Defines the `AlignRightWithPanel` attached property.
319        pub fn align_right_with_panel_property() -> AttachedProperty<bool> {
320            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignRightWithPanel", false)
321        }
322    );
323
324    /// Gets the value of the `AlignRightWithPanel` attached property for an object.
325    pub fn get_align_right_with_panel(obj: &FerroObject) -> bool {
326        obj.get_value(Self::align_right_with_panel_property())
327    }
328
329    /// Sets the value of the `AlignRightWithPanel` attached property for an object.
330    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        /// Defines the `AlignRightWith` attached property.
336        pub fn align_right_with_property() -> AttachedProperty<Option<BoxedValue>> {
337            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignRightWith", None)
338        }
339    );
340
341    /// Gets the value of the `AlignRightWith` attached property for an object.
342    pub fn get_align_right_with(obj: &FerroObject) -> Option<BoxedValue> {
343        obj.get_value(Self::align_right_with_property())
344    }
345
346    /// Sets the value of the `AlignRightWith` attached property for an object. The
347    /// layout uses the value when it holds a control of the panel; see
348    /// [`Control::boxed`].
349    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        /// Defines the `AlignTopWithPanel` attached property.
355        pub fn align_top_with_panel_property() -> AttachedProperty<bool> {
356            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignTopWithPanel", false)
357        }
358    );
359
360    /// Gets the value of the `AlignTopWithPanel` attached property for an object.
361    pub fn get_align_top_with_panel(obj: &FerroObject) -> bool {
362        obj.get_value(Self::align_top_with_panel_property())
363    }
364
365    /// Sets the value of the `AlignTopWithPanel` attached property for an object.
366    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        /// Defines the `AlignTopWith` attached property.
372        pub fn align_top_with_property() -> AttachedProperty<Option<BoxedValue>> {
373            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignTopWith", None)
374        }
375    );
376
377    /// Gets the value of the `AlignTopWith` attached property for an object.
378    pub fn get_align_top_with(obj: &FerroObject) -> Option<BoxedValue> {
379        obj.get_value(Self::align_top_with_property())
380    }
381
382    /// Sets the value of the `AlignTopWith` attached property for an object. The
383    /// layout uses the value when it holds a control of the panel; see
384    /// [`Control::boxed`].
385    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        /// Defines the `AlignVerticalCenterWithPanel` attached property.
391        pub fn align_vertical_center_with_panel_property() -> AttachedProperty<bool> {
392            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignVerticalCenterWithPanel", false)
393        }
394    );
395
396    /// Gets the value of the `AlignVerticalCenterWithPanel` attached property for an object.
397    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    /// Sets the value of the `AlignVerticalCenterWithPanel` attached property for an object.
402    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        /// Defines the `AlignVerticalCenterWith` attached property.
408        pub fn align_vertical_center_with_property() -> AttachedProperty<Option<BoxedValue>> {
409            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("AlignVerticalCenterWith", None)
410        }
411    );
412
413    /// Gets the value of the `AlignVerticalCenterWith` attached property for an object.
414    pub fn get_align_vertical_center_with(obj: &FerroObject) -> Option<BoxedValue> {
415        obj.get_value(Self::align_vertical_center_with_property())
416    }
417
418    /// Sets the value of the `AlignVerticalCenterWith` attached property for an object. The
419    /// layout uses the value when it holds a control of the panel; see
420    /// [`Control::boxed`].
421    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        /// Defines the `Below` attached property.
427        pub fn below_property() -> AttachedProperty<Option<BoxedValue>> {
428            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("Below", None)
429        }
430    );
431
432    /// Gets the value of the `Below` attached property for an object.
433    pub fn get_below(obj: &FerroObject) -> Option<BoxedValue> {
434        obj.get_value(Self::below_property())
435    }
436
437    /// Sets the value of the `Below` attached property for an object. The
438    /// layout uses the value when it holds a control of the panel; see
439    /// [`Control::boxed`].
440    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        /// Defines the `LeftOf` attached property.
446        pub fn left_of_property() -> AttachedProperty<Option<BoxedValue>> {
447            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("LeftOf", None)
448        }
449    );
450
451    /// Gets the value of the `LeftOf` attached property for an object.
452    pub fn get_left_of(obj: &FerroObject) -> Option<BoxedValue> {
453        obj.get_value(Self::left_of_property())
454    }
455
456    /// Sets the value of the `LeftOf` attached property for an object. The
457    /// layout uses the value when it holds a control of the panel; see
458    /// [`Control::boxed`].
459    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        /// Defines the `RightOf` attached property.
465        pub fn right_of_property() -> AttachedProperty<Option<BoxedValue>> {
466            FerroProperty::register_attached::<RelativePanel, Layoutable, _>("RightOf", None)
467        }
468    );
469
470    /// Gets the value of the `RightOf` attached property for an object.
471    pub fn get_right_of(obj: &FerroObject) -> Option<BoxedValue> {
472        obj.get_value(Self::right_of_property())
473    }
474
475    /// Sets the value of the `RightOf` attached property for an object. The
476    /// layout uses the value when it holds a control of the panel; see
477    /// [`Control::boxed`].
478    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    /// The nodes this node depends on, in the order the links were added.
495    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/// The nodes a measure pass visits: every node of the graph, or the outgoing
557/// nodes of one node.
558#[derive(Clone, Copy)]
559enum NodeSet {
560    All,
561    OutgoingOf(usize),
562}
563
564/// The dependency graph of the children of a [`RelativePanel`]. Nodes refer
565/// to each other by their index in `nodes`, which is their insertion order.
566#[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        // The positions of the nodes this node refers to.
665        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    /// Accumulates into `prev_size` the bounding sizes of the outgoing nodes
910    /// of the node `prev_node`.
911    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}