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