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