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