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