iced_pure/
element.rs

1use crate::overlay;
2use crate::widget::tree::{self, Tree};
3use crate::widget::Widget;
4
5use iced_native::event::{self, Event};
6use iced_native::layout::{self, Layout};
7use iced_native::mouse;
8use iced_native::renderer;
9use iced_native::{Clipboard, Length, Point, Rectangle, Shell};
10
11use std::borrow::Borrow;
12
13/// A generic [`Widget`].
14///
15/// It is useful to build composable user interfaces that do not leak
16/// implementation details in their __view logic__.
17///
18/// If you have a [built-in widget], you should be able to use `Into<Element>`
19/// to turn it into an [`Element`].
20///
21/// [built-in widget]: crate::widget
22pub struct Element<'a, Message, Renderer> {
23    widget: Box<dyn Widget<Message, Renderer> + 'a>,
24}
25
26impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
27    /// Creates a new [`Element`] containing the given [`Widget`].
28    pub fn new(widget: impl Widget<Message, Renderer> + 'a) -> Self {
29        Self {
30            widget: Box::new(widget),
31        }
32    }
33
34    /// Returns a reference to the [`Widget`] of the [`Element`],
35    pub fn as_widget(&self) -> &dyn Widget<Message, Renderer> {
36        self.widget.as_ref()
37    }
38
39    /// Returns a mutable reference to the [`Widget`] of the [`Element`],
40    pub fn as_widget_mut(&mut self) -> &mut dyn Widget<Message, Renderer> {
41        self.widget.as_mut()
42    }
43
44    /// Applies a transformation to the produced message of the [`Element`].
45    ///
46    /// This method is useful when you want to decouple different parts of your
47    /// UI and make them __composable__.
48    ///
49    /// # Example
50    /// Imagine we want to use [our counter](index.html#usage). But instead of
51    /// showing a single counter, we want to display many of them. We can reuse
52    /// the `Counter` type as it is!
53    ///
54    /// We use composition to model the __state__ of our new application:
55    ///
56    /// ```
57    /// # mod counter {
58    /// #     pub struct Counter;
59    /// # }
60    /// use counter::Counter;
61    ///
62    /// struct ManyCounters {
63    ///     counters: Vec<Counter>,
64    /// }
65    /// ```
66    ///
67    /// We can store the state of multiple counters now. However, the
68    /// __messages__ we implemented before describe the user interactions
69    /// of a __single__ counter. Right now, we need to also identify which
70    /// counter is receiving user interactions. Can we use composition again?
71    /// Yes.
72    ///
73    /// ```
74    /// # mod counter {
75    /// #     #[derive(Debug, Clone, Copy)]
76    /// #     pub enum Message {}
77    /// # }
78    /// #[derive(Debug, Clone, Copy)]
79    /// pub enum Message {
80    ///     Counter(usize, counter::Message)
81    /// }
82    /// ```
83    ///
84    /// We compose the previous __messages__ with the index of the counter
85    /// producing them. Let's implement our __view logic__ now:
86    ///
87    /// ```
88    /// # mod counter {
89    /// #     type Text = iced_pure::widget::Text<iced_native::renderer::Null>;
90    /// #
91    /// #     #[derive(Debug, Clone, Copy)]
92    /// #     pub enum Message {}
93    /// #     pub struct Counter;
94    /// #
95    /// #     impl Counter {
96    /// #         pub fn view(&mut self) -> Text {
97    /// #             Text::new("")
98    /// #         }
99    /// #     }
100    /// # }
101    /// #
102    /// # mod iced_wgpu {
103    /// #     pub use iced_native::renderer::Null as Renderer;
104    /// # }
105    /// #
106    /// # use counter::Counter;
107    /// #
108    /// # struct ManyCounters {
109    /// #     counters: Vec<Counter>,
110    /// # }
111    /// #
112    /// # #[derive(Debug, Clone, Copy)]
113    /// # pub enum Message {
114    /// #    Counter(usize, counter::Message)
115    /// # }
116    /// use iced_pure::Element;
117    /// use iced_pure::widget::Row;
118    /// use iced_wgpu::Renderer;
119    ///
120    /// impl ManyCounters {
121    ///     pub fn view(&mut self) -> Row<Message, Renderer> {
122    ///         // We can quickly populate a `Row` by folding over our counters
123    ///         self.counters.iter_mut().enumerate().fold(
124    ///             Row::new().spacing(20),
125    ///             |row, (index, counter)| {
126    ///                 // We display the counter
127    ///                 let element: Element<counter::Message, Renderer> =
128    ///                     counter.view().into();
129    ///
130    ///                 row.push(
131    ///                     // Here we turn our `Element<counter::Message>` into
132    ///                     // an `Element<Message>` by combining the `index` and the
133    ///                     // message of the `element`.
134    ///                     element.map(move |message| Message::Counter(index, message))
135    ///                 )
136    ///             }
137    ///         )
138    ///     }
139    /// }
140    /// ```
141    ///
142    /// Finally, our __update logic__ is pretty straightforward: simple
143    /// delegation.
144    ///
145    /// ```
146    /// # mod counter {
147    /// #     #[derive(Debug, Clone, Copy)]
148    /// #     pub enum Message {}
149    /// #     pub struct Counter;
150    /// #
151    /// #     impl Counter {
152    /// #         pub fn update(&mut self, _message: Message) {}
153    /// #     }
154    /// # }
155    /// #
156    /// # use counter::Counter;
157    /// #
158    /// # struct ManyCounters {
159    /// #     counters: Vec<Counter>,
160    /// # }
161    /// #
162    /// # #[derive(Debug, Clone, Copy)]
163    /// # pub enum Message {
164    /// #    Counter(usize, counter::Message)
165    /// # }
166    /// impl ManyCounters {
167    ///     pub fn update(&mut self, message: Message) {
168    ///         match message {
169    ///             Message::Counter(index, counter_msg) => {
170    ///                 if let Some(counter) = self.counters.get_mut(index) {
171    ///                     counter.update(counter_msg);
172    ///                 }
173    ///             }
174    ///         }
175    ///     }
176    /// }
177    /// ```
178    pub fn map<B>(
179        self,
180        f: impl Fn(Message) -> B + 'a,
181    ) -> Element<'a, B, Renderer>
182    where
183        Message: 'a,
184        Renderer: iced_native::Renderer + 'a,
185        B: 'a,
186    {
187        Element::new(Map::new(self.widget, f))
188    }
189}
190
191struct Map<'a, A, B, Renderer> {
192    widget: Box<dyn Widget<A, Renderer> + 'a>,
193    mapper: Box<dyn Fn(A) -> B + 'a>,
194}
195
196impl<'a, A, B, Renderer> Map<'a, A, B, Renderer> {
197    pub fn new<F>(
198        widget: Box<dyn Widget<A, Renderer> + 'a>,
199        mapper: F,
200    ) -> Map<'a, A, B, Renderer>
201    where
202        F: 'a + Fn(A) -> B,
203    {
204        Map {
205            widget,
206            mapper: Box::new(mapper),
207        }
208    }
209}
210
211impl<'a, A, B, Renderer> Widget<B, Renderer> for Map<'a, A, B, Renderer>
212where
213    Renderer: iced_native::Renderer + 'a,
214    A: 'a,
215    B: 'a,
216{
217    fn tag(&self) -> tree::Tag {
218        self.widget.tag()
219    }
220
221    fn state(&self) -> tree::State {
222        self.widget.state()
223    }
224
225    fn children(&self) -> Vec<Tree> {
226        self.widget.children()
227    }
228
229    fn diff(&self, tree: &mut Tree) {
230        self.widget.diff(tree)
231    }
232
233    fn width(&self) -> Length {
234        self.widget.width()
235    }
236
237    fn height(&self) -> Length {
238        self.widget.height()
239    }
240
241    fn layout(
242        &self,
243        renderer: &Renderer,
244        limits: &layout::Limits,
245    ) -> layout::Node {
246        self.widget.layout(renderer, limits)
247    }
248
249    fn on_event(
250        &mut self,
251        tree: &mut Tree,
252        event: Event,
253        layout: Layout<'_>,
254        cursor_position: Point,
255        renderer: &Renderer,
256        clipboard: &mut dyn Clipboard,
257        shell: &mut Shell<'_, B>,
258    ) -> event::Status {
259        let mut local_messages = Vec::new();
260        let mut local_shell = Shell::new(&mut local_messages);
261
262        let status = self.widget.on_event(
263            tree,
264            event,
265            layout,
266            cursor_position,
267            renderer,
268            clipboard,
269            &mut local_shell,
270        );
271
272        shell.merge(local_shell, &self.mapper);
273
274        status
275    }
276
277    fn draw(
278        &self,
279        tree: &Tree,
280        renderer: &mut Renderer,
281        style: &renderer::Style,
282        layout: Layout<'_>,
283        cursor_position: Point,
284        viewport: &Rectangle,
285    ) {
286        self.widget.draw(
287            tree,
288            renderer,
289            style,
290            layout,
291            cursor_position,
292            viewport,
293        )
294    }
295
296    fn mouse_interaction(
297        &self,
298        tree: &Tree,
299        layout: Layout<'_>,
300        cursor_position: Point,
301        viewport: &Rectangle,
302        renderer: &Renderer,
303    ) -> mouse::Interaction {
304        self.widget.mouse_interaction(
305            tree,
306            layout,
307            cursor_position,
308            viewport,
309            renderer,
310        )
311    }
312
313    fn overlay<'b>(
314        &'b self,
315        tree: &'b mut Tree,
316        layout: Layout<'_>,
317        renderer: &Renderer,
318    ) -> Option<overlay::Element<'b, B, Renderer>> {
319        let mapper = &self.mapper;
320
321        self.widget
322            .overlay(tree, layout, renderer)
323            .map(move |overlay| overlay.map(mapper))
324    }
325}
326
327impl<'a, Message, Renderer> Borrow<dyn Widget<Message, Renderer> + 'a>
328    for Element<'a, Message, Renderer>
329{
330    fn borrow(&self) -> &(dyn Widget<Message, Renderer> + 'a) {
331        self.widget.borrow()
332    }
333}
334
335impl<'a, Message, Renderer> Borrow<dyn Widget<Message, Renderer> + 'a>
336    for &Element<'a, Message, Renderer>
337{
338    fn borrow(&self) -> &(dyn Widget<Message, Renderer> + 'a) {
339        self.widget.borrow()
340    }
341}