Skip to main content

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, 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_mut(|element| {
136            self.tree
137                .borrow_mut()
138                .borrow_mut()
139                .as_mut()
140                .unwrap()
141                .diff_children(std::slice::from_mut(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 diff(&mut self, tree: &mut Tree) {
261        let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
262        *self.tree.borrow_mut() = tree.clone();
263        self.rebuild_element_if_necessary();
264    }
265
266    fn size(&self) -> Size<Length> {
267        self.with_element(|element| element.as_widget().size())
268    }
269
270    fn layout(
271        &mut self,
272        tree: &mut Tree,
273        renderer: &Renderer,
274        limits: &layout::Limits,
275    ) -> layout::Node {
276        let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
277
278        self.with_element_mut(|element| {
279            element.as_widget_mut().layout(
280                &mut t.borrow_mut().as_mut().unwrap().children[0],
281                renderer,
282                limits,
283            )
284        })
285    }
286
287    fn update(
288        &mut self,
289        tree: &mut Tree,
290        event: &core::Event,
291        layout: Layout<'_>,
292        cursor: mouse::Cursor,
293        renderer: &Renderer,
294        shell: &mut Shell<'_, Message>,
295        viewport: &Rectangle,
296    ) {
297        let mut local_messages = Vec::new();
298        let mut local_shell = shell.local(&mut local_messages);
299
300        let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
301        self.with_element_mut(|element| {
302            element.as_widget_mut().update(
303                &mut t.borrow_mut().as_mut().unwrap().children[0],
304                event,
305                layout,
306                cursor,
307                renderer,
308                &mut local_shell,
309                viewport,
310            );
311        });
312
313        if local_shell.is_event_captured() {
314            shell.capture_event();
315        }
316
317        local_shell.revalidate_layout(|diff| shell.invalidate_layout_with(diff));
318        shell.request_redraw_at(local_shell.redraw_request());
319        shell.request_input_method(local_shell.input_method());
320        shell.clipboard_mut().merge(local_shell.clipboard_mut());
321
322        if !local_messages.is_empty() {
323            let mut heads = self.state.take().unwrap().into_heads();
324
325            for message in local_messages.into_iter().filter_map(|message| {
326                heads.component.update(
327                    t.borrow_mut().as_mut().unwrap().state.downcast_mut(),
328                    message,
329                )
330            }) {
331                shell.publish(message);
332            }
333
334            self.state = RefCell::new(Some(
335                StateBuilder {
336                    component: heads.component,
337                    message: PhantomData,
338                    state: PhantomData,
339                    element_builder: |_| None,
340                }
341                .build(),
342            ));
343
344            shell.invalidate_layout();
345            shell.request_redraw();
346        }
347    }
348
349    fn operate(
350        &mut self,
351        tree: &mut Tree,
352        layout: Layout<'_>,
353        renderer: &Renderer,
354        operation: &mut dyn widget::Operation,
355    ) {
356        self.rebuild_element_with_operation(layout, operation);
357
358        let tree = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
359        self.with_element_mut(|element| {
360            element.as_widget_mut().operate(
361                &mut tree.borrow_mut().as_mut().unwrap().children[0],
362                layout,
363                renderer,
364                operation,
365            );
366        });
367    }
368
369    fn draw(
370        &self,
371        tree: &Tree,
372        renderer: &mut Renderer,
373        theme: &Theme,
374        style: &renderer::Style,
375        layout: Layout<'_>,
376        cursor: mouse::Cursor,
377        viewport: &Rectangle,
378    ) {
379        let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
380        self.with_element(|element| {
381            element.as_widget().draw(
382                &tree.borrow().as_ref().unwrap().children[0],
383                renderer,
384                theme,
385                style,
386                layout,
387                cursor,
388                viewport,
389            );
390        });
391    }
392
393    fn mouse_interaction(
394        &self,
395        tree: &Tree,
396        layout: Layout<'_>,
397        cursor: mouse::Cursor,
398        viewport: &Rectangle,
399        renderer: &Renderer,
400    ) -> mouse::Interaction {
401        let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
402        self.with_element(|element| {
403            element.as_widget().mouse_interaction(
404                &tree.borrow().as_ref().unwrap().children[0],
405                layout,
406                cursor,
407                viewport,
408                renderer,
409            )
410        })
411    }
412
413    fn overlay<'b>(
414        &'b mut self,
415        tree: &'b mut Tree,
416        layout: Layout<'b>,
417        renderer: &Renderer,
418        viewport: &Rectangle,
419        translation: Vector,
420    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
421        self.rebuild_element_if_necessary();
422
423        let state = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
424        let tree = state.borrow_mut().take().unwrap();
425
426        let overlay = InnerBuilder {
427            instance: self,
428            tree,
429            types: PhantomData,
430            overlay_builder: |instance, tree| {
431                instance
432                    .state
433                    .get_mut()
434                    .as_mut()
435                    .unwrap()
436                    .with_element_mut(move |element| {
437                        element
438                            .as_mut()
439                            .unwrap()
440                            .as_widget_mut()
441                            .overlay(
442                                &mut tree.children[0],
443                                layout,
444                                renderer,
445                                viewport,
446                                translation,
447                            )
448                            .map(|overlay| RefCell::new(overlay::Nested::new(overlay)))
449                    })
450            },
451        }
452        .build();
453
454        #[allow(clippy::redundant_closure_for_method_calls)]
455        if overlay.with_overlay(|overlay| overlay.is_some()) {
456            Some(overlay::Element::new(Box::new(OverlayInstance {
457                overlay: Some(Overlay(Some(overlay))), // Beautiful, I know
458            })))
459        } else {
460            let heads = overlay.into_heads();
461
462            // - You may not like it, but this is what peak performance looks like
463            // - TODO: Get rid of ouroboros, for good
464            // - What?!
465            *state.borrow_mut() = Some(heads.tree);
466
467            None
468        }
469    }
470}
471
472struct Overlay<'a, 'b, Message, Theme, Renderer, Event, S>(
473    Option<Inner<'a, 'b, Message, Theme, Renderer, Event, S>>,
474);
475
476impl<Message, Theme, Renderer, Event, S> Drop
477    for Overlay<'_, '_, Message, Theme, Renderer, Event, S>
478{
479    fn drop(&mut self) {
480        if let Some(heads) = self.0.take().map(Inner::into_heads) {
481            *heads.instance.tree.borrow_mut().borrow_mut() = Some(heads.tree);
482        }
483    }
484}
485
486#[self_referencing]
487struct Inner<'a, 'b, Message, Theme, Renderer, Event, S> {
488    instance: &'a mut Instance<'b, Message, Theme, Renderer, Event, S>,
489    tree: Tree,
490    types: PhantomData<(Message, Event, S)>,
491
492    #[borrows(mut instance, mut tree)]
493    #[not_covariant]
494    overlay: Option<RefCell<overlay::Nested<'this, Event, Theme, Renderer>>>,
495}
496
497struct OverlayInstance<'a, 'b, Message, Theme, Renderer, Event, S> {
498    overlay: Option<Overlay<'a, 'b, Message, Theme, Renderer, Event, S>>,
499}
500
501impl<Message, Theme, Renderer, Event, S>
502    OverlayInstance<'_, '_, Message, Theme, Renderer, Event, S>
503{
504    fn with_overlay_maybe<T>(
505        &self,
506        f: impl FnOnce(&mut overlay::Nested<'_, Event, Theme, Renderer>) -> T,
507    ) -> Option<T> {
508        self.overlay
509            .as_ref()
510            .unwrap()
511            .0
512            .as_ref()
513            .unwrap()
514            .with_overlay(|overlay| overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())))
515    }
516
517    fn with_overlay_mut_maybe<T>(
518        &mut self,
519        f: impl FnOnce(&mut overlay::Nested<'_, Event, Theme, Renderer>) -> T,
520    ) -> Option<T> {
521        self.overlay
522            .as_mut()
523            .unwrap()
524            .0
525            .as_mut()
526            .unwrap()
527            .with_overlay_mut(|overlay| overlay.as_mut().map(|nested| (f)(nested.get_mut())))
528    }
529}
530
531impl<Message, Theme, Renderer, Event, S> overlay::Overlay<Message, Theme, Renderer>
532    for OverlayInstance<'_, '_, Message, Theme, Renderer, Event, S>
533where
534    Renderer: core::Renderer,
535    S: 'static + Default,
536{
537    fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
538        self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds))
539            .unwrap_or_default()
540    }
541
542    fn draw(
543        &self,
544        renderer: &mut Renderer,
545        theme: &Theme,
546        style: &renderer::Style,
547        layout: Layout<'_>,
548        cursor: mouse::Cursor,
549    ) {
550        let _ = self.with_overlay_maybe(|overlay| {
551            overlay.draw(renderer, theme, style, layout, cursor);
552        });
553    }
554
555    fn mouse_interaction(
556        &self,
557        layout: Layout<'_>,
558        cursor: mouse::Cursor,
559        renderer: &Renderer,
560    ) -> mouse::Interaction {
561        self.with_overlay_maybe(|overlay| overlay.mouse_interaction(layout, cursor, renderer))
562            .unwrap_or_default()
563    }
564
565    fn update(
566        &mut self,
567        event: &core::Event,
568        layout: Layout<'_>,
569        cursor: mouse::Cursor,
570        renderer: &Renderer,
571        shell: &mut Shell<'_, Message>,
572    ) {
573        let mut local_messages = Vec::new();
574        let mut local_shell = shell.local(&mut local_messages);
575
576        let _ = self.with_overlay_mut_maybe(|overlay| {
577            overlay.update(event, layout, cursor, renderer, &mut local_shell);
578        });
579
580        if local_shell.is_event_captured() {
581            shell.capture_event();
582        }
583
584        local_shell.revalidate_layout(|diff| shell.invalidate_layout_with(diff));
585        shell.request_redraw_at(local_shell.redraw_request());
586        shell.request_input_method(local_shell.input_method());
587        shell.clipboard_mut().merge(local_shell.clipboard_mut());
588
589        if !local_messages.is_empty() {
590            let mut inner = self.overlay.take().unwrap().0.take().unwrap().into_heads();
591            let mut heads = inner.instance.state.take().unwrap().into_heads();
592
593            for message in local_messages.into_iter().filter_map(|message| {
594                heads
595                    .component
596                    .update(inner.tree.state.downcast_mut(), message)
597            }) {
598                shell.publish(message);
599            }
600
601            *inner.instance.state.borrow_mut() = Some(
602                StateBuilder {
603                    component: heads.component,
604                    message: PhantomData,
605                    state: PhantomData,
606                    element_builder: |_| None,
607                }
608                .build(),
609            );
610
611            self.overlay = Some(Overlay(Some(
612                InnerBuilder {
613                    instance: inner.instance,
614                    tree: inner.tree,
615                    types: PhantomData,
616                    overlay_builder: |_, _| None,
617                }
618                .build(),
619            )));
620
621            shell.invalidate_layout();
622        }
623    }
624}