iced_native/widget/
container.rs

1//! Decorate content and apply alignment.
2use crate::alignment::{self, Alignment};
3use crate::event::{self, Event};
4use crate::layout;
5use crate::mouse;
6use crate::overlay;
7use crate::renderer;
8use crate::widget::{self, Operation, Tree};
9use crate::{
10    Background, Clipboard, Color, Element, Layout, Length, Padding, Pixels,
11    Point, Rectangle, Shell, Widget,
12};
13
14pub use iced_style::container::{Appearance, StyleSheet};
15
16/// An element decorating some content.
17///
18/// It is normally used for alignment purposes.
19#[allow(missing_debug_implementations)]
20pub struct Container<'a, Message, Renderer>
21where
22    Renderer: crate::Renderer,
23    Renderer::Theme: StyleSheet,
24{
25    id: Option<Id>,
26    padding: Padding,
27    width: Length,
28    height: Length,
29    max_width: f32,
30    max_height: f32,
31    horizontal_alignment: alignment::Horizontal,
32    vertical_alignment: alignment::Vertical,
33    style: <Renderer::Theme as StyleSheet>::Style,
34    content: Element<'a, Message, Renderer>,
35}
36
37impl<'a, Message, Renderer> Container<'a, Message, Renderer>
38where
39    Renderer: crate::Renderer,
40    Renderer::Theme: StyleSheet,
41{
42    /// Creates an empty [`Container`].
43    pub fn new<T>(content: T) -> Self
44    where
45        T: Into<Element<'a, Message, Renderer>>,
46    {
47        Container {
48            id: None,
49            padding: Padding::ZERO,
50            width: Length::Shrink,
51            height: Length::Shrink,
52            max_width: f32::INFINITY,
53            max_height: f32::INFINITY,
54            horizontal_alignment: alignment::Horizontal::Left,
55            vertical_alignment: alignment::Vertical::Top,
56            style: Default::default(),
57            content: content.into(),
58        }
59    }
60
61    /// Sets the [`Id`] of the [`Container`].
62    pub fn id(mut self, id: Id) -> Self {
63        self.id = Some(id);
64        self
65    }
66
67    /// Sets the [`Padding`] of the [`Container`].
68    pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
69        self.padding = padding.into();
70        self
71    }
72
73    /// Sets the width of the [`Container`].
74    pub fn width(mut self, width: impl Into<Length>) -> Self {
75        self.width = width.into();
76        self
77    }
78
79    /// Sets the height of the [`Container`].
80    pub fn height(mut self, height: impl Into<Length>) -> Self {
81        self.height = height.into();
82        self
83    }
84
85    /// Sets the maximum width of the [`Container`].
86    pub fn max_width(mut self, max_width: impl Into<Pixels>) -> Self {
87        self.max_width = max_width.into().0;
88        self
89    }
90
91    /// Sets the maximum height of the [`Container`].
92    pub fn max_height(mut self, max_height: impl Into<Pixels>) -> Self {
93        self.max_height = max_height.into().0;
94        self
95    }
96
97    /// Sets the content alignment for the horizontal axis of the [`Container`].
98    pub fn align_x(mut self, alignment: alignment::Horizontal) -> Self {
99        self.horizontal_alignment = alignment;
100        self
101    }
102
103    /// Sets the content alignment for the vertical axis of the [`Container`].
104    pub fn align_y(mut self, alignment: alignment::Vertical) -> Self {
105        self.vertical_alignment = alignment;
106        self
107    }
108
109    /// Centers the contents in the horizontal axis of the [`Container`].
110    pub fn center_x(mut self) -> Self {
111        self.horizontal_alignment = alignment::Horizontal::Center;
112        self
113    }
114
115    /// Centers the contents in the vertical axis of the [`Container`].
116    pub fn center_y(mut self) -> Self {
117        self.vertical_alignment = alignment::Vertical::Center;
118        self
119    }
120
121    /// Sets the style of the [`Container`].
122    pub fn style(
123        mut self,
124        style: impl Into<<Renderer::Theme as StyleSheet>::Style>,
125    ) -> Self {
126        self.style = style.into();
127        self
128    }
129}
130
131impl<'a, Message, Renderer> Widget<Message, Renderer>
132    for Container<'a, Message, Renderer>
133where
134    Renderer: crate::Renderer,
135    Renderer::Theme: StyleSheet,
136{
137    fn children(&self) -> Vec<Tree> {
138        vec![Tree::new(&self.content)]
139    }
140
141    fn diff(&self, tree: &mut Tree) {
142        tree.diff_children(std::slice::from_ref(&self.content))
143    }
144
145    fn width(&self) -> Length {
146        self.width
147    }
148
149    fn height(&self) -> Length {
150        self.height
151    }
152
153    fn layout(
154        &self,
155        renderer: &Renderer,
156        limits: &layout::Limits,
157    ) -> layout::Node {
158        layout(
159            renderer,
160            limits,
161            self.width,
162            self.height,
163            self.max_width,
164            self.max_height,
165            self.padding,
166            self.horizontal_alignment,
167            self.vertical_alignment,
168            |renderer, limits| {
169                self.content.as_widget().layout(renderer, limits)
170            },
171        )
172    }
173
174    fn operate(
175        &self,
176        tree: &mut Tree,
177        layout: Layout<'_>,
178        renderer: &Renderer,
179        operation: &mut dyn Operation<Message>,
180    ) {
181        operation.container(
182            self.id.as_ref().map(|id| &id.0),
183            &mut |operation| {
184                self.content.as_widget().operate(
185                    &mut tree.children[0],
186                    layout.children().next().unwrap(),
187                    renderer,
188                    operation,
189                );
190            },
191        );
192    }
193
194    fn on_event(
195        &mut self,
196        tree: &mut Tree,
197        event: Event,
198        layout: Layout<'_>,
199        cursor_position: Point,
200        renderer: &Renderer,
201        clipboard: &mut dyn Clipboard,
202        shell: &mut Shell<'_, Message>,
203    ) -> event::Status {
204        self.content.as_widget_mut().on_event(
205            &mut tree.children[0],
206            event,
207            layout.children().next().unwrap(),
208            cursor_position,
209            renderer,
210            clipboard,
211            shell,
212        )
213    }
214
215    fn mouse_interaction(
216        &self,
217        tree: &Tree,
218        layout: Layout<'_>,
219        cursor_position: Point,
220        viewport: &Rectangle,
221        renderer: &Renderer,
222    ) -> mouse::Interaction {
223        self.content.as_widget().mouse_interaction(
224            &tree.children[0],
225            layout.children().next().unwrap(),
226            cursor_position,
227            viewport,
228            renderer,
229        )
230    }
231
232    fn draw(
233        &self,
234        tree: &Tree,
235        renderer: &mut Renderer,
236        theme: &Renderer::Theme,
237        renderer_style: &renderer::Style,
238        layout: Layout<'_>,
239        cursor_position: Point,
240        viewport: &Rectangle,
241    ) {
242        let style = theme.appearance(&self.style);
243
244        draw_background(renderer, &style, layout.bounds());
245
246        self.content.as_widget().draw(
247            &tree.children[0],
248            renderer,
249            theme,
250            &renderer::Style {
251                text_color: style
252                    .text_color
253                    .unwrap_or(renderer_style.text_color),
254            },
255            layout.children().next().unwrap(),
256            cursor_position,
257            viewport,
258        );
259    }
260
261    fn overlay<'b>(
262        &'b mut self,
263        tree: &'b mut Tree,
264        layout: Layout<'_>,
265        renderer: &Renderer,
266    ) -> Option<overlay::Element<'b, Message, Renderer>> {
267        self.content.as_widget_mut().overlay(
268            &mut tree.children[0],
269            layout.children().next().unwrap(),
270            renderer,
271        )
272    }
273}
274
275impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>>
276    for Element<'a, Message, Renderer>
277where
278    Message: 'a,
279    Renderer: 'a + crate::Renderer,
280    Renderer::Theme: StyleSheet,
281{
282    fn from(
283        column: Container<'a, Message, Renderer>,
284    ) -> Element<'a, Message, Renderer> {
285        Element::new(column)
286    }
287}
288
289/// Computes the layout of a [`Container`].
290pub fn layout<Renderer>(
291    renderer: &Renderer,
292    limits: &layout::Limits,
293    width: Length,
294    height: Length,
295    max_width: f32,
296    max_height: f32,
297    padding: Padding,
298    horizontal_alignment: alignment::Horizontal,
299    vertical_alignment: alignment::Vertical,
300    layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node,
301) -> layout::Node {
302    let limits = limits
303        .loose()
304        .max_width(max_width)
305        .max_height(max_height)
306        .width(width)
307        .height(height);
308
309    let mut content = layout_content(renderer, &limits.pad(padding).loose());
310    let padding = padding.fit(content.size(), limits.max());
311    let size = limits.pad(padding).resolve(content.size());
312
313    content.move_to(Point::new(padding.left, padding.top));
314    content.align(
315        Alignment::from(horizontal_alignment),
316        Alignment::from(vertical_alignment),
317        size,
318    );
319
320    layout::Node::with_children(size.pad(padding), vec![content])
321}
322
323/// Draws the background of a [`Container`] given its [`Appearance`] and its `bounds`.
324pub fn draw_background<Renderer>(
325    renderer: &mut Renderer,
326    appearance: &Appearance,
327    bounds: Rectangle,
328) where
329    Renderer: crate::Renderer,
330{
331    if appearance.background.is_some() || appearance.border_width > 0.0 {
332        renderer.fill_quad(
333            renderer::Quad {
334                bounds,
335                border_radius: appearance.border_radius.into(),
336                border_width: appearance.border_width,
337                border_color: appearance.border_color,
338            },
339            appearance
340                .background
341                .unwrap_or(Background::Color(Color::TRANSPARENT)),
342        );
343    }
344}
345
346/// The identifier of a [`Container`].
347#[derive(Debug, Clone, PartialEq, Eq, Hash)]
348pub struct Id(widget::Id);
349
350impl Id {
351    /// Creates a custom [`Id`].
352    pub fn new(id: impl Into<std::borrow::Cow<'static, str>>) -> Self {
353        Self(widget::Id::new(id))
354    }
355
356    /// Creates a unique [`Id`].
357    ///
358    /// This function produces a different [`Id`] every time it is called.
359    pub fn unique() -> Self {
360        Self(widget::Id::unique())
361    }
362}
363
364impl From<Id> for widget::Id {
365    fn from(id: Id) -> Self {
366        id.0
367    }
368}