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}