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