iced_widget/
responsive.rs

1use crate::core::layout::{self, Layout};
2use crate::core::mouse;
3use crate::core::overlay;
4use crate::core::renderer;
5use crate::core::widget;
6use crate::core::widget::Tree;
7use crate::core::{
8    self, Clipboard, Element, Event, Length, Rectangle, Shell, Size, Vector, Widget,
9};
10use crate::space;
11
12/// A widget that is aware of its dimensions.
13///
14/// A [`Responsive`] widget will always try to fill all the available space of
15/// its parent.
16pub struct Responsive<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> {
17    view: Box<dyn Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a>,
18    width: Length,
19    height: Length,
20    content: Element<'a, Message, Theme, Renderer>,
21}
22
23impl<'a, Message, Theme, Renderer> Responsive<'a, Message, Theme, Renderer>
24where
25    Renderer: core::Renderer,
26{
27    /// Creates a new [`Responsive`] widget with a closure that produces its
28    /// contents.
29    ///
30    /// The `view` closure will receive the maximum available space for
31    /// the [`Responsive`] during layout. You can use this [`Size`] to
32    /// conditionally build the contents.
33    pub fn new(view: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a) -> Self {
34        Self {
35            view: Box::new(view),
36            width: Length::Fill,
37            height: Length::Fill,
38            content: Element::new(space()),
39        }
40    }
41
42    /// Sets the width of the [`Responsive`].
43    pub fn width(mut self, width: impl Into<Length>) -> Self {
44        self.width = width.into();
45        self
46    }
47
48    /// Sets the height of the [`Responsive`].
49    pub fn height(mut self, height: impl Into<Length>) -> Self {
50        self.height = height.into();
51        self
52    }
53}
54
55impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
56    for Responsive<'_, Message, Theme, Renderer>
57where
58    Renderer: core::Renderer,
59{
60    fn diff(&self, _tree: &mut Tree) {
61        // Diff is deferred to layout
62    }
63
64    fn size(&self) -> Size<Length> {
65        Size {
66            width: self.width,
67            height: self.height,
68        }
69    }
70
71    fn layout(
72        &mut self,
73        tree: &mut Tree,
74        renderer: &Renderer,
75        limits: &layout::Limits,
76    ) -> layout::Node {
77        let limits = limits.width(self.width).height(self.height);
78        let size = limits.max();
79
80        self.content = (self.view)(size);
81        tree.diff_children(std::slice::from_ref(&self.content));
82
83        let node =
84            self.content
85                .as_widget_mut()
86                .layout(&mut tree.children[0], renderer, &limits.loose());
87
88        let size = limits.resolve(self.width, self.height, node.size());
89
90        layout::Node::with_children(size, vec![node])
91    }
92
93    fn update(
94        &mut self,
95        tree: &mut Tree,
96        event: &Event,
97        layout: Layout<'_>,
98        cursor: mouse::Cursor,
99        renderer: &Renderer,
100        clipboard: &mut dyn Clipboard,
101        shell: &mut Shell<'_, Message>,
102        viewport: &Rectangle,
103    ) {
104        self.content.as_widget_mut().update(
105            &mut tree.children[0],
106            event,
107            layout.children().next().unwrap(),
108            cursor,
109            renderer,
110            clipboard,
111            shell,
112            viewport,
113        );
114    }
115
116    fn draw(
117        &self,
118        tree: &Tree,
119        renderer: &mut Renderer,
120        theme: &Theme,
121        style: &renderer::Style,
122        layout: Layout<'_>,
123        cursor: mouse::Cursor,
124        viewport: &Rectangle,
125    ) {
126        self.content.as_widget().draw(
127            &tree.children[0],
128            renderer,
129            theme,
130            style,
131            layout.children().next().unwrap(),
132            cursor,
133            viewport,
134        );
135    }
136
137    fn mouse_interaction(
138        &self,
139        tree: &Tree,
140        layout: Layout<'_>,
141        cursor: mouse::Cursor,
142        viewport: &Rectangle,
143        renderer: &Renderer,
144    ) -> mouse::Interaction {
145        self.content.as_widget().mouse_interaction(
146            &tree.children[0],
147            layout.children().next().unwrap(),
148            cursor,
149            viewport,
150            renderer,
151        )
152    }
153
154    fn operate(
155        &mut self,
156        tree: &mut Tree,
157        layout: Layout<'_>,
158        renderer: &Renderer,
159        operation: &mut dyn widget::Operation,
160    ) {
161        self.content.as_widget_mut().operate(
162            &mut tree.children[0],
163            layout.children().next().unwrap(),
164            renderer,
165            operation,
166        );
167    }
168
169    fn overlay<'a>(
170        &'a mut self,
171        tree: &'a mut Tree,
172        layout: Layout<'a>,
173        renderer: &Renderer,
174        viewport: &Rectangle,
175        translation: Vector,
176    ) -> Option<overlay::Element<'a, Message, Theme, Renderer>> {
177        self.content.as_widget_mut().overlay(
178            &mut tree.children[0],
179            layout.children().next().unwrap(),
180            renderer,
181            viewport,
182            translation,
183        )
184    }
185}
186
187impl<'a, Message, Theme, Renderer> From<Responsive<'a, Message, Theme, Renderer>>
188    for Element<'a, Message, Theme, Renderer>
189where
190    Message: 'a,
191    Theme: 'a,
192    Renderer: core::Renderer + 'a,
193{
194    fn from(responsive: Responsive<'a, Message, Theme, Renderer>) -> Self {
195        Self::new(responsive)
196    }
197}