iced_widget/lazy/
component.rs

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