iced_aw/widget/
custom_layout.rs

1//! A container widget that allows you to specify the layout of its children.
2
3use iced_core::{Length, Rectangle, Widget};
4
5#[allow(unused_imports)]
6pub use iced_core::{
7    Renderer,
8    layout::{Limits, Node},
9    widget::Tree,
10};
11
12type LayoutFn<'a, Message, Theme, Renderer> = Box<
13    dyn Fn(
14        &mut Vec<iced_core::Element<'a, Message, Theme, Renderer>>,
15        &mut Vec<Tree>,
16        &Renderer,
17        &Limits,
18    ) -> Node,
19>;
20
21/// A container widget that allows you to specify the layout of its children.
22pub struct CustomLayout<'a, Message, Theme, Renderer> {
23    elements: Vec<iced_core::Element<'a, Message, Theme, Renderer>>,
24    width: Length,
25    height: Length,
26    layout: LayoutFn<'a, Message, Theme, Renderer>,
27}
28
29impl<'b, Message, Theme, Renderer: iced_core::Renderer> CustomLayout<'b, Message, Theme, Renderer> {
30    /// Creates a new [`CustomLayout`]
31    pub fn new(
32        elements: Vec<iced_core::Element<'b, Message, Theme, Renderer>>,
33        layout: impl Fn(
34            &mut Vec<iced_core::Element<'b, Message, Theme, Renderer>>,
35            &mut Vec<Tree>,
36            &Renderer,
37            &Limits,
38        ) -> Node
39        + 'static,
40    ) -> Self {
41        Self {
42            elements,
43            width: Length::Shrink,
44            height: Length::Shrink,
45            layout: Box::new(layout),
46        }
47    }
48
49    /// Sets the width of the [`CustomLayout`]
50    #[must_use]
51    pub fn width(mut self, length: impl Into<Length>) -> Self {
52        self.width = length.into();
53        self
54    }
55
56    /// Sets the height of the [`CustomLayout`]
57    #[must_use]
58    pub fn height(mut self, length: impl Into<Length>) -> Self {
59        self.height = length.into();
60        self
61    }
62}
63
64impl<Message, Theme, Renderer: iced_core::Renderer> Widget<Message, Theme, Renderer>
65    for CustomLayout<'_, Message, Theme, Renderer>
66{
67    fn size(&self) -> iced_core::Size<Length> {
68        iced_core::Size::new(self.width, self.height)
69    }
70
71    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node {
72        (self.layout)(&mut self.elements, &mut tree.children, renderer, limits)
73    }
74
75    fn draw(
76        &self,
77        tree: &Tree,
78        renderer: &mut Renderer,
79        theme: &Theme,
80        style: &iced_core::renderer::Style,
81        layout: iced_core::Layout<'_>,
82        cursor: iced_core::mouse::Cursor,
83        viewport: &iced_core::Rectangle,
84    ) {
85        tree.children
86            .iter()
87            .zip(layout.children())
88            .zip(self.elements.iter())
89            .for_each(|((state, layout), element)| {
90                element
91                    .as_widget()
92                    .draw(state, renderer, theme, style, layout, cursor, viewport);
93            });
94    }
95
96    fn children(&self) -> Vec<Tree> {
97        self.elements.iter().map(|x| Tree::new(x)).collect()
98    }
99
100    fn diff(&self, tree: &mut Tree) {
101        tree.diff_children(&self.elements);
102    }
103
104    fn operate(
105        &mut self,
106        state: &mut Tree,
107        layout: iced_core::Layout<'_>,
108        renderer: &Renderer,
109        operation: &mut dyn iced_core::widget::Operation,
110    ) {
111        state
112            .children
113            .iter_mut()
114            .zip(layout.children())
115            .zip(self.elements.iter_mut())
116            .for_each(|((state, layout), element)| {
117                operation.container(None, layout.bounds());
118                operation.traverse(&mut |operation| {
119                    element
120                        .as_widget_mut()
121                        .operate(state, layout, renderer, operation);
122                });
123            });
124    }
125
126    fn update(
127        &mut self,
128        state: &mut Tree,
129        event: &iced_core::Event,
130        layout: iced_core::Layout<'_>,
131        cursor: iced_core::mouse::Cursor,
132        renderer: &Renderer,
133        clipboard: &mut dyn iced_core::Clipboard,
134        shell: &mut iced_core::Shell<'_, Message>,
135        viewport: &iced_core::Rectangle,
136    ) {
137        for ((state, layout), element) in state
138            .children
139            .iter_mut()
140            .zip(layout.children())
141            .zip(self.elements.iter_mut())
142        {
143            element.as_widget_mut().update(
144                state, event, layout, cursor, renderer, clipboard, shell, viewport,
145            );
146        }
147    }
148
149    fn size_hint(&self) -> iced_core::Size<Length> {
150        self.size()
151    }
152
153    fn overlay<'a>(
154        &'a mut self,
155        state: &'a mut Tree,
156        layout: iced_core::Layout<'a>,
157        renderer: &Renderer,
158        viewport: &Rectangle,
159        translation: iced_core::Vector,
160    ) -> Option<iced_core::overlay::Element<'a, Message, Theme, Renderer>> {
161        iced_core::overlay::from_children(
162            &mut self.elements,
163            state,
164            layout,
165            renderer,
166            viewport,
167            translation,
168        )
169    }
170
171    fn mouse_interaction(
172        &self,
173        state: &Tree,
174        layout: iced_core::Layout<'_>,
175        cursor: iced_core::mouse::Cursor,
176        viewport: &iced_core::Rectangle,
177        renderer: &Renderer,
178    ) -> iced_core::mouse::Interaction {
179        self.elements
180            .iter()
181            .zip(&state.children)
182            .zip(layout.children())
183            .map(|((child, state), layout)| {
184                child
185                    .as_widget()
186                    .mouse_interaction(state, layout, cursor, viewport, renderer)
187            })
188            .max()
189            .unwrap_or_default()
190    }
191}
192
193impl<'b, Message: 'b, Theme: 'b, Renderer: iced_core::Renderer + 'b>
194    From<CustomLayout<'b, Message, Theme, Renderer>>
195    for iced_core::Element<'b, Message, Theme, Renderer>
196{
197    fn from(value: CustomLayout<'b, Message, Theme, Renderer>) -> Self {
198        iced_core::Element::new(value)
199    }
200}