fui_core/layout/
stack_panel.rs

1use 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
15//
16// Attached values
17//
18
19pub struct Grow;
20impl typemap::Key for Grow {
21    type Value = Length;
22}
23
24//
25// StackPanel
26//
27
28#[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        // set default alignment to Start
41        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//
63// Default StackPanel Style
64//
65
66#[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                // calculate min sizes
106                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                // distribute size that's left over Grow = Length::Fill children
130                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                // calculate min sizes
138                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                // distribute size that's left over Grow = Length::Fill children
162                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}