iced_widget/lazy/
component.rs

1//! Build and reuse custom widgets using The Elm Architecture.
2#![allow(deprecated)]
3use crate::core::layout::{self, Layout};
4use crate::core::mouse;
5use crate::core::overlay;
6use crate::core::renderer;
7use crate::core::widget;
8use crate::core::widget::tree::{self, Tree};
9use crate::core::{
10    self, Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget,
11};
12use crate::runtime::overlay::Nested;
13
14use ouroboros::self_referencing;
15use std::cell::RefCell;
16use std::marker::PhantomData;
17use std::rc::Rc;
18
19/// A reusable, custom widget that uses The Elm Architecture.
20///
21/// A [`Component`] allows you to implement custom widgets as if they were
22/// `iced` applications with encapsulated state.
23///
24/// In other words, a [`Component`] allows you to turn `iced` applications into
25/// custom widgets and embed them without cumbersome wiring.
26///
27/// A [`Component`] produces widgets that may fire an [`Event`](Component::Event)
28/// and update the internal state of the [`Component`].
29///
30/// Additionally, a [`Component`] is capable of producing a `Message` to notify
31/// the parent application of any relevant interactions.
32///
33/// # State
34/// A component can store its state in one of two ways: either as data within the
35/// implementor of the trait, or in a type [`State`][Component::State] that is managed
36/// by the runtime and provided to the trait methods. These two approaches are not
37/// mutually exclusive and have opposite pros and cons.
38///
39/// For instance, if a piece of state is needed by multiple components that reside
40/// in different branches of the tree, then it's more convenient to let a common
41/// ancestor store it and pass it down.
42///
43/// On the other hand, if a piece of state is only needed by the component itself,
44/// you can store it as part of its internal [`State`][Component::State].
45#[cfg(feature = "lazy")]
46#[deprecated(
47    since = "0.13.0",
48    note = "components introduce encapsulated state and hamper the use of a single source of truth. \
49    Instead, leverage the Elm Architecture directly, or implement a custom widget"
50)]
51pub trait Component<Message, Theme = crate::Theme, Renderer = crate::Renderer> {
52    /// The internal state of this [`Component`].
53    type State: Default;
54
55    /// The type of event this [`Component`] handles internally.
56    type Event;
57
58    /// Processes an [`Event`](Component::Event) and updates the [`Component`] state accordingly.
59    ///
60    /// It can produce a `Message` for the parent application.
61    fn update(
62        &mut self,
63        state: &mut Self::State,
64        event: Self::Event,
65    ) -> Option<Message>;
66
67    /// Produces the widgets of the [`Component`], which may trigger an [`Event`](Component::Event)
68    /// on user interaction.
69    fn view(
70        &self,
71        state: &Self::State,
72    ) -> Element<'_, Self::Event, Theme, Renderer>;
73
74    /// Update the [`Component`] state based on the provided [`Operation`](widget::Operation)
75    ///
76    /// By default, it does nothing.
77    fn operate(
78        &self,
79        _bounds: Rectangle,
80        _state: &mut Self::State,
81        _operation: &mut dyn widget::Operation,
82    ) {
83    }
84
85    /// Returns a [`Size`] hint for laying out the [`Component`].
86    ///
87    /// This hint may be used by some widget containers to adjust their sizing strategy
88    /// during construction.
89    fn size_hint(&self) -> Size<Length> {
90        Size {
91            width: Length::Shrink,
92            height: Length::Shrink,
93        }
94    }
95}
96
97struct Tag<T>(T);
98
99/// Turns an implementor of [`Component`] into an [`Element`] that can be
100/// embedded in any application.
101pub fn view<'a, C, Message, Theme, Renderer>(
102    component: C,
103) -> Element<'a, Message, Theme, Renderer>
104where
105    C: Component<Message, Theme, Renderer> + 'a,
106    C::State: 'static,
107    Message: 'a,
108    Theme: 'a,
109    Renderer: core::Renderer + 'a,
110{
111    Element::new(Instance {
112        state: RefCell::new(Some(
113            StateBuilder {
114                component: Box::new(component),
115                message: PhantomData,
116                state: PhantomData,
117                element_builder: |_| None,
118            }
119            .build(),
120        )),
121        tree: RefCell::new(Rc::new(RefCell::new(None))),
122    })
123}
124
125struct Instance<'a, Message, Theme, Renderer, Event, S> {
126    state: RefCell<Option<State<'a, Message, Theme, Renderer, Event, S>>>,
127    tree: RefCell<Rc<RefCell<Option<Tree>>>>,
128}
129
130#[self_referencing]
131struct State<'a, Message: 'a, Theme: 'a, Renderer: 'a, Event: 'a, S: 'a> {
132    component: Box<
133        dyn Component<Message, Theme, Renderer, Event = Event, State = S> + 'a,
134    >,
135    message: PhantomData<Message>,
136    state: PhantomData<S>,
137
138    #[borrows(component)]
139    #[covariant]
140    element: Option<Element<'this, Event, Theme, Renderer>>,
141}
142
143impl<Message, Theme, Renderer, Event, S>
144    Instance<'_, Message, Theme, Renderer, Event, S>
145where
146    S: Default + 'static,
147    Renderer: renderer::Renderer,
148{
149    fn diff_self(&self) {
150        self.with_element(|element| {
151            self.tree
152                .borrow_mut()
153                .borrow_mut()
154                .as_mut()
155                .unwrap()
156                .diff_children(std::slice::from_ref(&element));
157        });
158    }
159
160    fn rebuild_element_if_necessary(&self) {
161        let inner = self.state.borrow_mut().take().unwrap();
162        if inner.borrow_element().is_none() {
163            let heads = inner.into_heads();
164
165            *self.state.borrow_mut() = Some(
166                StateBuilder {
167                    component: heads.component,
168                    message: PhantomData,
169                    state: PhantomData,
170                    element_builder: |component| {
171                        Some(
172                            component.view(
173                                self.tree
174                                    .borrow()
175                                    .borrow()
176                                    .as_ref()
177                                    .unwrap()
178                                    .state
179                                    .downcast_ref::<S>(),
180                            ),
181                        )
182                    },
183                }
184                .build(),
185            );
186            self.diff_self();
187        } else {
188            *self.state.borrow_mut() = Some(inner);
189        }
190    }
191
192    fn rebuild_element_with_operation(
193        &self,
194        layout: Layout<'_>,
195        operation: &mut dyn widget::Operation,
196    ) {
197        let heads = self.state.borrow_mut().take().unwrap().into_heads();
198
199        heads.component.operate(
200            layout.bounds(),
201            self.tree
202                .borrow_mut()
203                .borrow_mut()
204                .as_mut()
205                .unwrap()
206                .state
207                .downcast_mut(),
208            operation,
209        );
210
211        *self.state.borrow_mut() = Some(
212            StateBuilder {
213                component: heads.component,
214                message: PhantomData,
215                state: PhantomData,
216                element_builder: |component| {
217                    Some(
218                        component.view(
219                            self.tree
220                                .borrow()
221                                .borrow()
222                                .as_ref()
223                                .unwrap()
224                                .state
225                                .downcast_ref(),
226                        ),
227                    )
228                },
229            }
230            .build(),
231        );
232        self.diff_self();
233    }
234
235    fn with_element<T>(
236        &self,
237        f: impl FnOnce(&Element<'_, Event, Theme, Renderer>) -> T,
238    ) -> T {
239        self.with_element_mut(|element| f(element))
240    }
241
242    fn with_element_mut<T>(
243        &self,
244        f: impl FnOnce(&mut Element<'_, Event, Theme, Renderer>) -> T,
245    ) -> T {
246        self.rebuild_element_if_necessary();
247        self.state
248            .borrow_mut()
249            .as_mut()
250            .unwrap()
251            .with_element_mut(|element| f(element.as_mut().unwrap()))
252    }
253}
254
255impl<Message, Theme, Renderer, Event, S> Widget<Message, Theme, Renderer>
256    for Instance<'_, Message, Theme, Renderer, Event, S>
257where
258    S: 'static + Default,
259    Renderer: core::Renderer,
260{
261    fn tag(&self) -> tree::Tag {
262        tree::Tag::of::<Tag<S>>()
263    }
264
265    fn state(&self) -> tree::State {
266        let state = Rc::new(RefCell::new(Some(Tree {
267            tag: tree::Tag::of::<Tag<S>>(),
268            state: tree::State::new(S::default()),
269            children: vec![Tree::empty()],
270        })));
271
272        *self.tree.borrow_mut() = state.clone();
273        self.diff_self();
274
275        tree::State::new(state)
276    }
277
278    fn children(&self) -> Vec<Tree> {
279        vec![]
280    }
281
282    fn diff(&self, tree: &mut Tree) {
283        let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
284        *self.tree.borrow_mut() = tree.clone();
285        self.rebuild_element_if_necessary();
286    }
287
288    fn size(&self) -> Size<Length> {
289        self.with_element(|element| element.as_widget().size())
290    }
291
292    fn size_hint(&self) -> Size<Length> {
293        self.state
294            .borrow()
295            .as_ref()
296            .expect("Borrow instance state")
297            .borrow_component()
298            .size_hint()
299    }
300
301    fn layout(
302        &self,
303        tree: &mut Tree,
304        renderer: &Renderer,
305        limits: &layout::Limits,
306    ) -> layout::Node {
307        let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
308
309        self.with_element(|element| {
310            element.as_widget().layout(
311                &mut t.borrow_mut().as_mut().unwrap().children[0],
312                renderer,
313                limits,
314            )
315        })
316    }
317
318    fn update(
319        &mut self,
320        tree: &mut Tree,
321        event: &core::Event,
322        layout: Layout<'_>,
323        cursor: mouse::Cursor,
324        renderer: &Renderer,
325        clipboard: &mut dyn Clipboard,
326        shell: &mut Shell<'_, Message>,
327        viewport: &Rectangle,
328    ) {
329        let mut local_messages = Vec::new();
330        let mut local_shell = Shell::new(&mut local_messages);
331
332        let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
333        self.with_element_mut(|element| {
334            element.as_widget_mut().update(
335                &mut t.borrow_mut().as_mut().unwrap().children[0],
336                event,
337                layout,
338                cursor,
339                renderer,
340                clipboard,
341                &mut local_shell,
342                viewport,
343            );
344        });
345
346        if local_shell.is_event_captured() {
347            shell.capture_event();
348        }
349
350        local_shell.revalidate_layout(|| shell.invalidate_layout());
351        shell.request_redraw_at(local_shell.redraw_request());
352        shell.request_input_method(local_shell.input_method());
353
354        if !local_messages.is_empty() {
355            let mut heads = self.state.take().unwrap().into_heads();
356
357            for message in local_messages.into_iter().filter_map(|message| {
358                heads.component.update(
359                    t.borrow_mut().as_mut().unwrap().state.downcast_mut(),
360                    message,
361                )
362            }) {
363                shell.publish(message);
364            }
365
366            self.state = RefCell::new(Some(
367                StateBuilder {
368                    component: heads.component,
369                    message: PhantomData,
370                    state: PhantomData,
371                    element_builder: |_| None,
372                }
373                .build(),
374            ));
375
376            shell.invalidate_layout();
377        }
378    }
379
380    fn operate(
381        &self,
382        tree: &mut Tree,
383        layout: Layout<'_>,
384        renderer: &Renderer,
385        operation: &mut dyn widget::Operation,
386    ) {
387        self.rebuild_element_with_operation(layout, operation);
388
389        let tree = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
390        self.with_element(|element| {
391            element.as_widget().operate(
392                &mut tree.borrow_mut().as_mut().unwrap().children[0],
393                layout,
394                renderer,
395                operation,
396            );
397        });
398    }
399
400    fn draw(
401        &self,
402        tree: &Tree,
403        renderer: &mut Renderer,
404        theme: &Theme,
405        style: &renderer::Style,
406        layout: Layout<'_>,
407        cursor: mouse::Cursor,
408        viewport: &Rectangle,
409    ) {
410        let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
411        self.with_element(|element| {
412            element.as_widget().draw(
413                &tree.borrow().as_ref().unwrap().children[0],
414                renderer,
415                theme,
416                style,
417                layout,
418                cursor,
419                viewport,
420            );
421        });
422    }
423
424    fn mouse_interaction(
425        &self,
426        tree: &Tree,
427        layout: Layout<'_>,
428        cursor: mouse::Cursor,
429        viewport: &Rectangle,
430        renderer: &Renderer,
431    ) -> mouse::Interaction {
432        let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
433        self.with_element(|element| {
434            element.as_widget().mouse_interaction(
435                &tree.borrow().as_ref().unwrap().children[0],
436                layout,
437                cursor,
438                viewport,
439                renderer,
440            )
441        })
442    }
443
444    fn overlay<'b>(
445        &'b mut self,
446        tree: &'b mut Tree,
447        layout: Layout<'b>,
448        renderer: &Renderer,
449        viewport: &Rectangle,
450        translation: Vector,
451    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
452        self.rebuild_element_if_necessary();
453
454        let state = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
455        let tree = state.borrow_mut().take().unwrap();
456
457        let overlay = InnerBuilder {
458            instance: self,
459            tree,
460            types: PhantomData,
461            overlay_builder: |instance, tree| {
462                instance.state.get_mut().as_mut().unwrap().with_element_mut(
463                    move |element| {
464                        element
465                            .as_mut()
466                            .unwrap()
467                            .as_widget_mut()
468                            .overlay(
469                                &mut tree.children[0],
470                                layout,
471                                renderer,
472                                viewport,
473                                translation,
474                            )
475                            .map(|overlay| RefCell::new(Nested::new(overlay)))
476                    },
477                )
478            },
479        }
480        .build();
481
482        #[allow(clippy::redundant_closure_for_method_calls)]
483        if overlay.with_overlay(|overlay| overlay.is_some()) {
484            Some(overlay::Element::new(Box::new(OverlayInstance {
485                overlay: Some(Overlay(Some(overlay))), // Beautiful, I know
486            })))
487        } else {
488            let heads = overlay.into_heads();
489
490            // - You may not like it, but this is what peak performance looks like
491            // - TODO: Get rid of ouroboros, for good
492            // - What?!
493            *state.borrow_mut() = Some(heads.tree);
494
495            None
496        }
497    }
498}
499
500struct Overlay<'a, 'b, Message, Theme, Renderer, Event, S>(
501    Option<Inner<'a, 'b, Message, Theme, Renderer, Event, S>>,
502);
503
504impl<Message, Theme, Renderer, Event, S> Drop
505    for Overlay<'_, '_, Message, Theme, Renderer, Event, S>
506{
507    fn drop(&mut self) {
508        if let Some(heads) = self.0.take().map(Inner::into_heads) {
509            *heads.instance.tree.borrow_mut().borrow_mut() = Some(heads.tree);
510        }
511    }
512}
513
514#[self_referencing]
515struct Inner<'a, 'b, Message, Theme, Renderer, Event, S> {
516    instance: &'a mut Instance<'b, Message, Theme, Renderer, Event, S>,
517    tree: Tree,
518    types: PhantomData<(Message, Event, S)>,
519
520    #[borrows(mut instance, mut tree)]
521    #[not_covariant]
522    overlay: Option<RefCell<Nested<'this, Event, Theme, Renderer>>>,
523}
524
525struct OverlayInstance<'a, 'b, Message, Theme, Renderer, Event, S> {
526    overlay: Option<Overlay<'a, 'b, Message, Theme, Renderer, Event, S>>,
527}
528
529impl<Message, Theme, Renderer, Event, S>
530    OverlayInstance<'_, '_, Message, Theme, Renderer, Event, S>
531{
532    fn with_overlay_maybe<T>(
533        &self,
534        f: impl FnOnce(&mut Nested<'_, Event, Theme, Renderer>) -> T,
535    ) -> Option<T> {
536        self.overlay
537            .as_ref()
538            .unwrap()
539            .0
540            .as_ref()
541            .unwrap()
542            .with_overlay(|overlay| {
543                overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut()))
544            })
545    }
546
547    fn with_overlay_mut_maybe<T>(
548        &mut self,
549        f: impl FnOnce(&mut Nested<'_, Event, Theme, Renderer>) -> T,
550    ) -> Option<T> {
551        self.overlay
552            .as_mut()
553            .unwrap()
554            .0
555            .as_mut()
556            .unwrap()
557            .with_overlay_mut(|overlay| {
558                overlay.as_mut().map(|nested| (f)(nested.get_mut()))
559            })
560    }
561}
562
563impl<Message, Theme, Renderer, Event, S>
564    overlay::Overlay<Message, Theme, Renderer>
565    for OverlayInstance<'_, '_, Message, Theme, Renderer, Event, S>
566where
567    Renderer: core::Renderer,
568    S: 'static + Default,
569{
570    fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
571        self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds))
572            .unwrap_or_default()
573    }
574
575    fn draw(
576        &self,
577        renderer: &mut Renderer,
578        theme: &Theme,
579        style: &renderer::Style,
580        layout: Layout<'_>,
581        cursor: mouse::Cursor,
582    ) {
583        let _ = self.with_overlay_maybe(|overlay| {
584            overlay.draw(renderer, theme, style, layout, cursor);
585        });
586    }
587
588    fn mouse_interaction(
589        &self,
590        layout: Layout<'_>,
591        cursor: mouse::Cursor,
592        renderer: &Renderer,
593    ) -> mouse::Interaction {
594        self.with_overlay_maybe(|overlay| {
595            overlay.mouse_interaction(layout, cursor, renderer)
596        })
597        .unwrap_or_default()
598    }
599
600    fn update(
601        &mut self,
602        event: &core::Event,
603        layout: Layout<'_>,
604        cursor: mouse::Cursor,
605        renderer: &Renderer,
606        clipboard: &mut dyn Clipboard,
607        shell: &mut Shell<'_, Message>,
608    ) {
609        let mut local_messages = Vec::new();
610        let mut local_shell = Shell::new(&mut local_messages);
611
612        let _ = self.with_overlay_mut_maybe(|overlay| {
613            overlay.update(
614                event,
615                layout,
616                cursor,
617                renderer,
618                clipboard,
619                &mut local_shell,
620            );
621        });
622
623        if local_shell.is_event_captured() {
624            shell.capture_event();
625        }
626
627        local_shell.revalidate_layout(|| shell.invalidate_layout());
628        shell.request_redraw_at(local_shell.redraw_request());
629        shell.request_input_method(local_shell.input_method());
630
631        if !local_messages.is_empty() {
632            let mut inner =
633                self.overlay.take().unwrap().0.take().unwrap().into_heads();
634            let mut heads = inner.instance.state.take().unwrap().into_heads();
635
636            for message in local_messages.into_iter().filter_map(|message| {
637                heads
638                    .component
639                    .update(inner.tree.state.downcast_mut(), message)
640            }) {
641                shell.publish(message);
642            }
643
644            *inner.instance.state.borrow_mut() = Some(
645                StateBuilder {
646                    component: heads.component,
647                    message: PhantomData,
648                    state: PhantomData,
649                    element_builder: |_| None,
650                }
651                .build(),
652            );
653
654            self.overlay = Some(Overlay(Some(
655                InnerBuilder {
656                    instance: inner.instance,
657                    tree: inner.tree,
658                    types: PhantomData,
659                    overlay_builder: |_, _| None,
660                }
661                .build(),
662            )));
663
664            shell.invalidate_layout();
665        }
666    }
667}