fui_core/layout/
stack_panel.rs1use std::cell::RefCell;
2use std::f32;
3use std::rc::Rc;
4
5use crate::{
6 Alignment, ControlContext, ControlEvent, ControlObject, DrawingContext, EventContext,
7 HorizontalAlignment, Orientation, Point, Rect, Size, Style, StyledControl, VerticalAlignment,
8 ViewContext,
9};
10use drawing::primitive::Primitive;
11use typed_builder::TypedBuilder;
12
13use super::Length;
14
15pub struct Grow;
20impl typemap::Key for Grow {
21 type Value = Length;
22}
23
24#[derive(TypedBuilder)]
29pub struct StackPanel {
30 #[builder(default = Orientation::Vertical)]
31 pub orientation: Orientation,
32}
33
34impl StackPanel {
35 pub fn to_view(
36 self,
37 style: Option<Box<dyn Style<Self>>>,
38 mut context: ViewContext,
39 ) -> Rc<RefCell<dyn ControlObject>> {
40 context
42 .attached_values
43 .entry::<HorizontalAlignment>()
44 .or_insert(Alignment::Start);
45 context
46 .attached_values
47 .entry::<VerticalAlignment>()
48 .or_insert(Alignment::Start);
49
50 StyledControl::new(
51 self,
52 style.unwrap_or_else(|| {
53 Box::new(DefaultStackPanelStyle::new(
54 DefaultStackPanelStyleParams::builder().build(),
55 ))
56 }),
57 context,
58 )
59 }
60}
61
62#[derive(TypedBuilder)]
67pub struct DefaultStackPanelStyleParams {}
68
69pub struct DefaultStackPanelStyle;
70
71impl DefaultStackPanelStyle {
72 pub fn new(_params: DefaultStackPanelStyleParams) -> Self {
73 DefaultStackPanelStyle {}
74 }
75}
76
77impl Style<StackPanel> for DefaultStackPanelStyle {
78 fn setup(&mut self, _data: &mut StackPanel, _control_context: &mut ControlContext) {}
79
80 fn handle_event(
81 &mut self,
82 _data: &mut StackPanel,
83 _control_context: &mut ControlContext,
84 _drawing_context: &mut dyn DrawingContext,
85 _event_context: &mut dyn EventContext,
86 _event: ControlEvent,
87 ) {
88 }
89
90 fn measure(
91 &mut self,
92 data: &mut StackPanel,
93 control_context: &mut ControlContext,
94 drawing_context: &mut dyn DrawingContext,
95 size: Size,
96 ) -> Size {
97 let mut result = Size::new(0f32, 0f32);
98
99 let children = control_context.get_children();
100
101 match data.orientation {
102 Orientation::Horizontal => {
103 let available_size = Size::new(f32::INFINITY, size.height);
104
105 let mut grow_fills_sum = 0.0f32;
107 for child in children.into_iter() {
108 child.borrow_mut().measure(drawing_context, available_size);
109 let child = child.borrow();
110 let mut child_size = child.get_rect();
111
112 let map = child.get_context().get_attached_values();
113 if let Some(grow) = map.get::<Grow>() {
114 match *grow {
115 Length::Exact(l) => {
116 child_size.width = child_size.width.max(l);
117 }
118 Length::Fill(f) => {
119 grow_fills_sum += f;
120 }
121 _ => (),
122 }
123 }
124
125 result.width += child_size.width;
126 result.height = result.height.max(child_size.height);
127 }
128
129 if size.width.is_finite() && size.width > result.width && grow_fills_sum > 0.0f32 {
131 result.width = size.width;
132 }
133 }
134 Orientation::Vertical => {
135 let available_size = Size::new(size.width, f32::INFINITY);
136
137 let mut grow_fills_sum = 0.0f32;
139 for child in children.into_iter() {
140 child.borrow_mut().measure(drawing_context, available_size);
141 let child = child.borrow();
142 let mut child_size = child.get_rect();
143
144 let map = child.get_context().get_attached_values();
145 if let Some(grow) = map.get::<Grow>() {
146 match *grow {
147 Length::Exact(l) => {
148 child_size.height = child_size.height.max(l);
149 }
150 Length::Fill(f) => {
151 grow_fills_sum += f;
152 }
153 _ => (),
154 }
155 }
156
157 result.width = result.width.max(child_size.width);
158 result.height += child_size.height;
159 }
160
161 if size.height.is_finite() && size.height > result.height && grow_fills_sum > 0.0f32
163 {
164 result.height = size.height;
165 }
166 }
167 }
168
169 result
170 }
171
172 fn set_rect(
173 &mut self,
174 data: &mut StackPanel,
175 control_context: &mut ControlContext,
176 drawing_context: &mut dyn DrawingContext,
177 rect: Rect,
178 ) {
179 let mut child_rect = rect;
180
181 let children = control_context.get_children();
182
183 let grow_fills_sum: f32 = children
184 .into_iter()
185 .map(|child| {
186 if let Some(Length::Fill(fill)) = child
187 .borrow()
188 .get_context()
189 .get_attached_values()
190 .get::<Grow>()
191 {
192 *fill
193 } else {
194 0.0f32
195 }
196 })
197 .sum();
198
199 match data.orientation {
200 Orientation::Horizontal => {
201 let child_sizes_sum: f32 = children
202 .into_iter()
203 .map(|child| {
204 let child = child.borrow();
205 let child_width = child.get_rect().width;
206 let map = child.get_context().get_attached_values();
207 if let Some(grow) = map.get::<Grow>() {
208 match *grow {
209 Length::Exact(l) => child_width.max(l),
210 _ => child_width,
211 }
212 } else {
213 child_width
214 }
215 })
216 .sum();
217
218 let fill_coefficient =
219 if grow_fills_sum > 0.0f32 && rect.width - child_sizes_sum > 0.0f32 {
220 (rect.width - child_sizes_sum) / grow_fills_sum
221 } else {
222 0.0f32
223 };
224
225 for child in children.into_iter() {
226 {
227 let child = child.borrow();
228 let mut child_size = child.get_rect();
229
230 let map = child.get_context().get_attached_values();
231 if let Some(grow) = map.get::<Grow>() {
232 match *grow {
233 Length::Exact(l) => {
234 child_size.width = child_size.width.max(l);
235 }
236 Length::Fill(f) => {
237 if fill_coefficient > 0.0f32 {
238 child_size.width += f * fill_coefficient;
239 }
240 }
241 _ => (),
242 }
243 }
244
245 child_rect.width = child_size.width;
246 child_rect.height = child_size.height;
247 }
248
249 let dest_rect =
250 Rect::new(child_rect.x, child_rect.y, child_rect.width, rect.height);
251
252 let mut child = child.borrow_mut();
253 child.set_rect(drawing_context, dest_rect);
254
255 child_rect.x += child_rect.width;
256 }
257 }
258 Orientation::Vertical => {
259 let child_sizes_sum: f32 = children
260 .into_iter()
261 .map(|child| {
262 let child = child.borrow();
263 let child_height = child.get_rect().height;
264 let map = child.get_context().get_attached_values();
265 if let Some(grow) = map.get::<Grow>() {
266 match *grow {
267 Length::Exact(l) => child_height.max(l),
268 _ => child_height,
269 }
270 } else {
271 child_height
272 }
273 })
274 .sum();
275
276 let fill_coefficient =
277 if grow_fills_sum > 0.0f32 && rect.height - child_sizes_sum > 0.0f32 {
278 (rect.height - child_sizes_sum) / grow_fills_sum
279 } else {
280 0.0f32
281 };
282
283 for child in children.into_iter() {
284 {
285 let child = child.borrow();
286 let mut child_size = child.get_rect();
287
288 let map = child.get_context().get_attached_values();
289 if let Some(grow) = map.get::<Grow>() {
290 match *grow {
291 Length::Exact(l) => {
292 child_size.height = child_size.height.max(l);
293 }
294 Length::Fill(f) => {
295 if fill_coefficient > 0.0f32 {
296 child_size.height += f * fill_coefficient;
297 }
298 }
299 _ => (),
300 }
301 }
302
303 child_rect.width = child_size.width;
304 child_rect.height = child_size.height;
305 }
306
307 let dest_rect =
308 Rect::new(child_rect.x, child_rect.y, rect.width, child_rect.height);
309
310 let mut child = child.borrow_mut();
311 child.set_rect(drawing_context, dest_rect);
312
313 child_rect.y += child_rect.height;
314 }
315 }
316 }
317 }
318
319 fn hit_test(
320 &self,
321 _data: &StackPanel,
322 control_context: &ControlContext,
323 point: Point,
324 ) -> Option<Rc<RefCell<dyn ControlObject>>> {
325 if point.is_inside(&control_context.get_rect()) {
326 let children = control_context.get_children();
327 for child in children.into_iter() {
328 let c = child.borrow();
329 let rect = c.get_rect();
330 if point.is_inside(&rect) {
331 let hit_control = c.hit_test(point);
332 if hit_control.is_some() {
333 return hit_control;
334 }
335 }
336 }
337 None
338 } else {
339 None
340 }
341 }
342
343 fn to_primitives(
344 &self,
345 _data: &StackPanel,
346 control_context: &ControlContext,
347 drawing_context: &mut dyn DrawingContext,
348 ) -> (Vec<Primitive>, Vec<Primitive>) {
349 let mut vec = Vec::new();
350 let mut overlay = Vec::new();
351
352 let children = control_context.get_children();
353 for child in children.into_iter() {
354 let (mut vec2, mut overlay2) = child.borrow().to_primitives(drawing_context);
355 vec.append(&mut vec2);
356 overlay.append(&mut overlay2);
357 }
358
359 (vec, overlay)
360 }
361}