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            shell.request_redraw();
377        }
378    }
379
380    fn operate(
381        &mut 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_mut(|element| {
391            element.as_widget_mut().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| {
476                                RefCell::new(overlay::Nested::new(overlay))
477                            })
478                    },
479                )
480            },
481        }
482        .build();
483
484        #[allow(clippy::redundant_closure_for_method_calls)]
485        if overlay.with_overlay(|overlay| overlay.is_some()) {
486            Some(overlay::Element::new(Box::new(OverlayInstance {
487                overlay: Some(Overlay(Some(overlay))), // Beautiful, I know
488            })))
489        } else {
490            let heads = overlay.into_heads();
491
492            // - You may not like it, but this is what peak performance looks like
493            // - TODO: Get rid of ouroboros, for good
494            // - What?!
495            *state.borrow_mut() = Some(heads.tree);
496
497            None
498        }
499    }
500}
501
502struct Overlay<'a, 'b, Message, Theme, Renderer, Event, S>(
503    Option<Inner<'a, 'b, Message, Theme, Renderer, Event, S>>,
504);
505
506impl<Message, Theme, Renderer, Event, S> Drop
507    for Overlay<'_, '_, Message, Theme, Renderer, Event, S>
508{
509    fn drop(&mut self) {
510        if let Some(heads) = self.0.take().map(Inner::into_heads) {
511            *heads.instance.tree.borrow_mut().borrow_mut() = Some(heads.tree);
512        }
513    }
514}
515
516#[self_referencing]
517struct Inner<'a, 'b, Message, Theme, Renderer, Event, S> {
518    instance: &'a mut Instance<'b, Message, Theme, Renderer, Event, S>,
519    tree: Tree,
520    types: PhantomData<(Message, Event, S)>,
521
522    #[borrows(mut instance, mut tree)]
523    #[not_covariant]
524    overlay: Option<RefCell<overlay::Nested<'this, Event, Theme, Renderer>>>,
525}
526
527struct OverlayInstance<'a, 'b, Message, Theme, Renderer, Event, S> {
528    overlay: Option<Overlay<'a, 'b, Message, Theme, Renderer, Event, S>>,
529}
530
531impl<Message, Theme, Renderer, Event, S>
532    OverlayInstance<'_, '_, Message, Theme, Renderer, Event, S>
533{
534    fn with_overlay_maybe<T>(
535        &self,
536        f: impl FnOnce(&mut overlay::Nested<'_, Event, Theme, Renderer>) -> T,
537    ) -> Option<T> {
538        self.overlay
539            .as_ref()
540            .unwrap()
541            .0
542            .as_ref()
543            .unwrap()
544            .with_overlay(|overlay| {
545                overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut()))
546            })
547    }
548
549    fn with_overlay_mut_maybe<T>(
550        &mut self,
551        f: impl FnOnce(&mut overlay::Nested<'_, Event, Theme, Renderer>) -> T,
552    ) -> Option<T> {
553        self.overlay
554            .as_mut()
555            .unwrap()
556            .0
557            .as_mut()
558            .unwrap()
559            .with_overlay_mut(|overlay| {
560                overlay.as_mut().map(|nested| (f)(nested.get_mut()))
561            })
562    }
563}
564
565impl<Message, Theme, Renderer, Event, S>
566    overlay::Overlay<Message, Theme, Renderer>
567    for OverlayInstance<'_, '_, Message, Theme, Renderer, Event, S>
568where
569    Renderer: core::Renderer,
570    S: 'static + Default,
571{
572    fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
573        self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds))
574            .unwrap_or_default()
575    }
576
577    fn draw(
578        &self,
579        renderer: &mut Renderer,
580        theme: &Theme,
581        style: &renderer::Style,
582        layout: Layout<'_>,
583        cursor: mouse::Cursor,
584    ) {
585        let _ = self.with_overlay_maybe(|overlay| {
586            overlay.draw(renderer, theme, style, layout, cursor);
587        });
588    }
589
590    fn mouse_interaction(
591        &self,
592        layout: Layout<'_>,
593        cursor: mouse::Cursor,
594        renderer: &Renderer,
595    ) -> mouse::Interaction {
596        self.with_overlay_maybe(|overlay| {
597            overlay.mouse_interaction(layout, cursor, renderer)
598        })
599        .unwrap_or_default()
600    }
601
602    fn update(
603        &mut self,
604        event: &core::Event,
605        layout: Layout<'_>,
606        cursor: mouse::Cursor,
607        renderer: &Renderer,
608        clipboard: &mut dyn Clipboard,
609        shell: &mut Shell<'_, Message>,
610    ) {
611        let mut local_messages = Vec::new();
612        let mut local_shell = Shell::new(&mut local_messages);
613
614        let _ = self.with_overlay_mut_maybe(|overlay| {
615            overlay.update(
616                event,
617                layout,
618                cursor,
619                renderer,
620                clipboard,
621                &mut local_shell,
622            );
623        });
624
625        if local_shell.is_event_captured() {
626            shell.capture_event();
627        }
628
629        local_shell.revalidate_layout(|| shell.invalidate_layout());
630        shell.request_redraw_at(local_shell.redraw_request());
631        shell.request_input_method(local_shell.input_method());
632
633        if !local_messages.is_empty() {
634            let mut inner =
635                self.overlay.take().unwrap().0.take().unwrap().into_heads();
636            let mut heads = inner.instance.state.take().unwrap().into_heads();
637
638            for message in local_messages.into_iter().filter_map(|message| {
639                heads
640                    .component
641                    .update(inner.tree.state.downcast_mut(), message)
642            }) {
643                shell.publish(message);
644            }
645
646            *inner.instance.state.borrow_mut() = Some(
647                StateBuilder {
648                    component: heads.component,
649                    message: PhantomData,
650                    state: PhantomData,
651                    element_builder: |_| None,
652                }
653                .build(),
654            );
655
656            self.overlay = Some(Overlay(Some(
657                InnerBuilder {
658                    instance: inner.instance,
659                    tree: inner.tree,
660                    types: PhantomData,
661                    overlay_builder: |_, _| None,
662                }
663                .build(),
664            )));
665
666            shell.invalidate_layout();
667        }
668    }
669}