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