rg3d_ui/
stack_panel.rs

1use crate::{
2    core::{algebra::Vector2, math::Rect, pool::Handle, scope_profile},
3    message::UiMessage,
4    widget::{Widget, WidgetBuilder},
5    BuildContext, Control, Orientation, UiNode, UserInterface,
6};
7use std::{
8    any::{Any, TypeId},
9    ops::{Deref, DerefMut},
10};
11
12#[derive(Clone)]
13pub struct StackPanel {
14    widget: Widget,
15    orientation: Orientation,
16}
17
18crate::define_widget_deref!(StackPanel);
19
20impl StackPanel {
21    pub fn new(widget: Widget) -> Self {
22        Self {
23            widget,
24            orientation: Orientation::Vertical,
25        }
26    }
27
28    pub fn set_orientation(&mut self, orientation: Orientation) {
29        if self.orientation != orientation {
30            self.orientation = orientation;
31            self.widget.invalidate_layout();
32        }
33    }
34
35    pub fn orientation(&self) -> Orientation {
36        self.orientation
37    }
38}
39
40impl Control for StackPanel {
41    fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
42        if type_id == TypeId::of::<Self>() {
43            Some(self)
44        } else {
45            None
46        }
47    }
48
49    fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
50        scope_profile!();
51
52        let mut child_constraint = Vector2::new(f32::INFINITY, f32::INFINITY);
53
54        match self.orientation {
55            Orientation::Vertical => {
56                child_constraint.x = available_size.x;
57
58                if !self.widget.width().is_nan() {
59                    child_constraint.x = self.widget.width();
60                }
61
62                child_constraint.x = child_constraint
63                    .x
64                    .min(self.max_width())
65                    .max(self.min_width());
66            }
67            Orientation::Horizontal => {
68                child_constraint.y = available_size.y;
69
70                if !self.widget.height().is_nan() {
71                    child_constraint.y = self.widget.height();
72                }
73
74                child_constraint.y = child_constraint
75                    .y
76                    .min(self.max_height())
77                    .max(self.min_height());
78            }
79        }
80
81        let mut measured_size = Vector2::default();
82
83        for child_handle in self.widget.children() {
84            ui.measure_node(*child_handle, child_constraint);
85
86            let child = ui.node(*child_handle);
87            let desired = child.desired_size();
88            match self.orientation {
89                Orientation::Vertical => {
90                    if desired.x > measured_size.x {
91                        measured_size.x = desired.x;
92                    }
93                    measured_size.y += desired.y;
94                }
95                Orientation::Horizontal => {
96                    measured_size.x += desired.x;
97                    if desired.y > measured_size.y {
98                        measured_size.y = desired.y;
99                    }
100                }
101            }
102        }
103
104        measured_size
105    }
106
107    fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
108        scope_profile!();
109
110        let mut width = final_size.x;
111        let mut height = final_size.y;
112
113        match self.orientation {
114            Orientation::Vertical => height = 0.0,
115            Orientation::Horizontal => width = 0.0,
116        }
117
118        for child_handle in self.widget.children() {
119            let child = ui.node(*child_handle);
120            match self.orientation {
121                Orientation::Vertical => {
122                    let child_bounds = Rect::new(
123                        0.0,
124                        height,
125                        width.max(child.desired_size().x),
126                        child.desired_size().y,
127                    );
128                    ui.arrange_node(*child_handle, &child_bounds);
129                    width = width.max(child.desired_size().x);
130                    height += child.desired_size().y;
131                }
132                Orientation::Horizontal => {
133                    let child_bounds = Rect::new(
134                        width,
135                        0.0,
136                        child.desired_size().x,
137                        height.max(child.desired_size().y),
138                    );
139                    ui.arrange_node(*child_handle, &child_bounds);
140                    width += child.desired_size().x;
141                    height = height.max(child.desired_size().y);
142                }
143            }
144        }
145
146        match self.orientation {
147            Orientation::Vertical => {
148                height = height.max(final_size.y);
149            }
150            Orientation::Horizontal => {
151                width = width.max(final_size.x);
152            }
153        }
154
155        Vector2::new(width, height)
156    }
157
158    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
159        self.widget.handle_routed_message(ui, message);
160    }
161}
162
163pub struct StackPanelBuilder {
164    widget_builder: WidgetBuilder,
165    orientation: Option<Orientation>,
166}
167
168impl StackPanelBuilder {
169    pub fn new(widget_builder: WidgetBuilder) -> Self {
170        Self {
171            widget_builder,
172            orientation: None,
173        }
174    }
175
176    pub fn with_orientation(mut self, orientation: Orientation) -> Self {
177        self.orientation = Some(orientation);
178        self
179    }
180
181    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
182        let stack_panel = StackPanel {
183            widget: self.widget_builder.build(),
184            orientation: self.orientation.unwrap_or(Orientation::Vertical),
185        };
186
187        ctx.add_node(UiNode::new(stack_panel))
188    }
189}