Skip to main content

iced_widget/
component.rs

1//! Build and reuse custom widgets using The Elm Architecture.
2use crate::Action;
3use crate::core::event;
4use crate::core::layout::{self, Layout};
5use crate::core::mouse;
6use crate::core::overlay;
7use crate::core::renderer;
8use crate::core::shell;
9use crate::core::widget;
10use crate::core::widget::tree::{self, Tree};
11use crate::core::window;
12use crate::core::{self, Element, Event, Length, Point, Rectangle, Shell, Size, Vector, Widget};
13
14use std::cell::{Cell, RefCell};
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].
42pub trait Component<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> {
43    /// The internal state of this [`Component`].
44    type State: Default + 'static;
45
46    /// The type of event this [`Component`] handles internally.
47    type Event: 'static;
48
49    /// Processes an [`Event`](Component::Event) and updates the [`Component`] state accordingly.
50    ///
51    /// It can produce a `Message` for the parent application.
52    fn update(
53        &self,
54        state: &mut Self::State,
55        event: Self::Event,
56        renderer: &Renderer,
57    ) -> Option<Message>;
58
59    /// Produces the widgets of the [`Component`], which may trigger an [`Event`](Component::Event)
60    /// on user interaction.
61    fn view(&self, state: &Self::State) -> Element<'a, Self::Event, Theme, Renderer>;
62
63    /// Listens to a runtime [`Event`] and performs an [`Action`] as a result.
64    ///
65    /// If the [`Action`] publishes a [`Component::Event`], it will be immediately fed
66    /// to [`update`](Self::update).
67    ///
68    /// By default, it returns [`Action::none`].
69    fn listen(
70        &self,
71        _state: &Self::State,
72        _event: &Event,
73        _bounds: Rectangle,
74        _cursor: mouse::Cursor,
75    ) -> Action<Self::Event> {
76        Action::none()
77    }
78
79    /// Returns the current [`mouse::Interaction`] of the [`Component`].
80    ///
81    /// This interaction will override any interaction produced by the [`view`](Self::view)
82    /// of the [`Component`].
83    ///
84    /// By default, it returns [`mouse::Interaction::None`].
85    fn mouse_interaction(&self, _state: &Self::State) -> mouse::Interaction {
86        mouse::Interaction::None
87    }
88
89    /// Reconciles the current [`Component`] with its internal [`State`](Self::State) persisted
90    /// in the widget tree.
91    ///
92    /// This method will be called every time the widget tree changes. You can leverage it to
93    /// detect and react to changes in the [`Component`].
94    ///
95    /// By default, it does nothing.
96    fn diff(&mut self, _state: &mut Self::State) {}
97
98    /// Run the provided [`widget::Operation`] on the [`Component`].
99    ///
100    /// By default, it does nothing.
101    fn operate(
102        &self,
103        _state: &Self::State,
104        _bounds: Rectangle,
105        _operation: &mut dyn widget::Operation,
106    ) {
107    }
108}
109
110/// Turns an implementor of [`Component`] into an [`Element`] that can be
111/// embedded in any application.
112pub fn component<'a, C, Message, Theme, Renderer>(
113    component: C,
114) -> Element<'a, Message, Theme, Renderer>
115where
116    C: Component<'a, Message, Theme, Renderer> + 'a,
117    C::State: 'static,
118    Message: 'a,
119    Theme: 'a,
120    Renderer: core::Renderer + 'a,
121{
122    Element::new(Instance {
123        component,
124        view: crate::space().into(),
125        limits: layout::Limits::new(Size::ZERO, Size::INFINITE),
126        layout: layout::Node::new(Size::ZERO),
127        is_outdated: Cell::new(true),
128        has_overlay: false,
129    })
130}
131
132struct Instance<'a, C, Message, Theme, Renderer>
133where
134    C: Component<'a, Message, Theme, Renderer> + 'a,
135{
136    component: C,
137    view: Element<'a, C::Event, Theme, Renderer>,
138    limits: layout::Limits,
139    layout: layout::Node,
140    is_outdated: Cell<bool>,
141    has_overlay: bool,
142}
143
144struct Internal<State, Event> {
145    state: State,
146    events: shell::Bus<Event>,
147}
148
149impl<'a, C, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
150    for Instance<'a, C, Message, Theme, Renderer>
151where
152    C: Component<'a, Message, Theme, Renderer> + 'a,
153    Renderer: core::Renderer,
154{
155    fn tag(&self) -> tree::Tag {
156        tree::Tag::of::<RefCell<Internal<C::State, C::Event>>>()
157    }
158
159    fn state(&self) -> tree::State {
160        tree::State::new(RefCell::new(Internal {
161            state: C::State::default(),
162            events: shell::Bus::<C::Event>::new(),
163        }))
164    }
165
166    fn diff(&mut self, tree: &mut Tree) {
167        let mut internal = tree
168            .state
169            .downcast_mut::<RefCell<Internal<C::State, C::Event>>>()
170            .borrow_mut();
171
172        self.component.diff(&mut internal.state);
173
174        if self.is_outdated.get() {
175            self.view = self.component.view(&internal.state);
176            drop(internal);
177
178            tree.diff_children(std::slice::from_mut(&mut self.view));
179
180            self.is_outdated.set(false);
181        }
182    }
183
184    fn size(&self) -> Size<Length> {
185        self.view.as_widget().size()
186    }
187
188    fn layout(
189        &mut self,
190        tree: &mut Tree,
191        renderer: &Renderer,
192        limits: &layout::Limits,
193    ) -> layout::Node {
194        if &self.limits != limits {
195            self.limits = *limits;
196            self.layout = self
197                .view
198                .as_widget_mut()
199                .layout(&mut tree.children[0], renderer, limits);
200        }
201
202        layout::Node::new(self.layout.size())
203    }
204
205    fn update(
206        &mut self,
207        tree: &mut Tree,
208        event: &Event,
209        layout: Layout<'_>,
210        cursor: mouse::Cursor,
211        renderer: &Renderer,
212        shell: &mut Shell<'_, Message>,
213        viewport: &Rectangle,
214    ) {
215        let mut internal = tree
216            .state
217            .downcast_mut::<RefCell<Internal<C::State, C::Event>>>()
218            .borrow_mut();
219
220        let action = self
221            .component
222            .listen(&internal.state, event, layout.bounds(), cursor);
223
224        let (publish, redraw_request, event_status) = action.into_inner();
225
226        shell.request_redraw_at(redraw_request);
227
228        if let event::Status::Captured = event_status {
229            shell.capture_event();
230        }
231
232        if let Some(event) = publish {
233            let _ = internal.events.push(event);
234        }
235
236        if !shell.is_event_captured() {
237            let mut local_shell = shell.local(&mut internal.events);
238
239            self.view.as_widget_mut().update(
240                &mut tree.children[0],
241                event,
242                Layout::with_offset(layout.position() - Point::ORIGIN, &self.layout),
243                cursor,
244                renderer,
245                &mut local_shell,
246                viewport,
247            );
248
249            if local_shell.is_event_captured() {
250                shell.capture_event();
251            }
252
253            if let Some(diff) = local_shell.is_layout_invalid() {
254                shell.invalidate_layout_with(diff);
255            }
256
257            if local_shell.are_widgets_invalid() {
258                shell.invalidate_widgets();
259            }
260
261            shell.request_redraw_at(local_shell.redraw_request());
262            shell.request_input_method(local_shell.input_method());
263            shell.clipboard_mut().merge(local_shell.clipboard_mut());
264        }
265
266        if internal.events.is_empty() {
267            return;
268        }
269
270        let Internal { state, events } = &mut *internal;
271
272        for (event, receipt) in events.drain() {
273            if let Some(message) = self.component.update(state, event, renderer) {
274                shell.forward(message, receipt);
275            }
276        }
277
278        let previous_sizing = self.view.as_widget().size();
279
280        self.view = self.component.view(state);
281        drop(internal);
282
283        tree.diff_children(std::slice::from_mut(&mut self.view));
284
285        let previous_size = self.layout.size();
286        self.layout =
287            self.view
288                .as_widget_mut()
289                .layout(&mut tree.children[0], renderer, &self.limits);
290
291        let new_sizing = self.view.as_widget().size();
292
293        // We must invalidate application layout in 3 instances:
294        //
295        // 1. The size hint of the component changes. Other widgets
296        //    may change layout behavior.
297        //
298        // 2. The size hint of the component is `Shrink` for any axis
299        //    and the component has changed size. The new size may
300        //    push other widgets around.
301        //
302        // 3. The overlay status of the component changes. The
303        //    runtime will only call `overlay` again if the layout
304        //    is invalidated.
305        if new_sizing != previous_sizing {
306            shell.invalidate_widgets();
307        } else if (new_sizing.width == Length::Shrink || new_sizing.height == Length::Shrink)
308            && previous_size != self.layout.size()
309        {
310            shell.invalidate_layout();
311        } else {
312            let has_overlay = !self
313                .view
314                .as_widget_mut()
315                .overlay(
316                    &mut tree.children[0],
317                    Layout::with_offset(layout.position() - Point::ORIGIN, &self.layout),
318                    renderer,
319                    viewport,
320                    Vector::ZERO,
321                )
322                .is_empty();
323
324            if self.has_overlay != has_overlay {
325                self.has_overlay = has_overlay;
326                shell.invalidate_layout();
327            }
328        }
329
330        self.is_outdated.set(false);
331
332        if let Event::Window(window::Event::RedrawRequested(_)) = event {
333            let mut internal = tree
334                .state
335                .downcast_mut::<RefCell<Internal<C::State, C::Event>>>()
336                .borrow_mut();
337
338            let mut local_shell = shell.local(&mut internal.events);
339
340            self.view.as_widget_mut().update(
341                &mut tree.children[0],
342                event,
343                Layout::with_offset(layout.position() - Point::ORIGIN, &self.layout),
344                cursor,
345                renderer,
346                &mut local_shell,
347                viewport,
348            );
349
350            if internal.events.is_empty() {
351                return;
352            }
353        }
354
355        shell.request_redraw();
356    }
357
358    fn draw(
359        &self,
360        tree: &Tree,
361        renderer: &mut Renderer,
362        theme: &Theme,
363        style: &renderer::Style,
364        layout: Layout<'_>,
365        cursor: mouse::Cursor,
366        viewport: &Rectangle,
367    ) {
368        self.view.as_widget().draw(
369            &tree.children[0],
370            renderer,
371            theme,
372            style,
373            Layout::with_offset(layout.position() - Point::ORIGIN, &self.layout),
374            cursor,
375            viewport,
376        );
377    }
378
379    fn mouse_interaction(
380        &self,
381        tree: &Tree,
382        layout: Layout<'_>,
383        cursor: mouse::Cursor,
384        viewport: &Rectangle,
385        renderer: &Renderer,
386    ) -> mouse::Interaction {
387        let internal = tree
388            .state
389            .downcast_ref::<RefCell<Internal<C::State, C::Event>>>()
390            .borrow();
391
392        let interaction = self.component.mouse_interaction(&internal.state);
393
394        if interaction != mouse::Interaction::None {
395            return interaction;
396        }
397
398        self.view.as_widget().mouse_interaction(
399            &tree.children[0],
400            Layout::with_offset(layout.position() - Point::ORIGIN, &self.layout),
401            cursor,
402            viewport,
403            renderer,
404        )
405    }
406
407    fn operate(
408        &mut self,
409        tree: &mut Tree,
410        layout: Layout<'_>,
411        renderer: &Renderer,
412        operation: &mut dyn widget::Operation,
413    ) {
414        let internal = tree
415            .state
416            .downcast_ref::<RefCell<Internal<C::State, C::Event>>>()
417            .borrow();
418
419        self.component
420            .operate(&internal.state, layout.bounds(), operation);
421
422        self.view.as_widget_mut().operate(
423            &mut tree.children[0],
424            Layout::with_offset(layout.position() - Point::ORIGIN, &self.layout),
425            renderer,
426            operation,
427        );
428    }
429
430    fn overlay<'b>(
431        &'b mut self,
432        tree: &'b mut Tree,
433        layout: Layout<'b>,
434        renderer: &Renderer,
435        viewport: &Rectangle,
436        translation: Vector,
437    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
438        let overlays = self.view.as_widget_mut().overlay(
439            &mut tree.children[0],
440            Layout::with_offset(layout.position() - Point::ORIGIN, &self.layout),
441            renderer,
442            viewport,
443            translation,
444        );
445
446        self.has_overlay = !overlays.is_empty();
447
448        let internal = tree
449            .state
450            .downcast_ref::<RefCell<Internal<C::State, C::Event>>>();
451
452        overlays
453            .into_iter()
454            .map(|raw| {
455                overlay::Element::new(Box::new(Overlay {
456                    component: &self.component,
457                    internal,
458                    is_outdated: &self.is_outdated,
459                    raw,
460                }))
461            })
462            .collect()
463    }
464}
465
466struct Overlay<'a, 'b, C, Message, Theme, Renderer>
467where
468    C: Component<'a, Message, Theme, Renderer>,
469{
470    component: &'b C,
471    internal: &'b RefCell<Internal<C::State, C::Event>>,
472    is_outdated: &'b Cell<bool>,
473    raw: overlay::Element<'b, C::Event, Theme, Renderer>,
474}
475
476impl<'a, 'b, C, Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
477    for Overlay<'a, 'b, C, Message, Theme, Renderer>
478where
479    C: Component<'a, Message, Theme, Renderer>,
480    Renderer: core::Renderer,
481{
482    fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
483        self.raw.as_overlay_mut().layout(renderer, bounds)
484    }
485
486    fn update(
487        &mut self,
488        event: &Event,
489        layout: Layout<'_>,
490        cursor: mouse::Cursor,
491        renderer: &Renderer,
492        shell: &mut Shell<'_, Message>,
493    ) {
494        let mut internal = self.internal.borrow_mut();
495        let mut local_shell = shell.local(&mut internal.events);
496
497        self.raw
498            .as_overlay_mut()
499            .update(event, layout, cursor, renderer, &mut local_shell);
500
501        if local_shell.is_event_captured() {
502            shell.capture_event();
503        }
504
505        if let Some(diff) = local_shell.is_layout_invalid() {
506            shell.invalidate_layout_with(diff);
507        }
508
509        if local_shell.are_widgets_invalid() {
510            shell.invalidate_widgets();
511        }
512
513        shell.request_redraw_at(local_shell.redraw_request());
514        shell.request_input_method(local_shell.input_method());
515        shell.clipboard_mut().merge(local_shell.clipboard_mut());
516
517        if internal.events.is_empty() {
518            return;
519        }
520
521        let Internal { state, events } = &mut *internal;
522
523        for (event, receipt) in events.drain() {
524            if let Some(message) = self.component.update(state, event, renderer) {
525                shell.forward(message, receipt);
526            }
527        }
528
529        self.is_outdated.set(true);
530
531        shell.invalidate_layout();
532        shell.request_redraw();
533    }
534
535    fn draw(
536        &self,
537        renderer: &mut Renderer,
538        theme: &Theme,
539        style: &renderer::Style,
540        layout: Layout<'_>,
541        cursor: mouse::Cursor,
542    ) {
543        self.raw
544            .as_overlay()
545            .draw(renderer, theme, style, layout, cursor);
546    }
547
548    fn mouse_interaction(
549        &self,
550        layout: Layout<'_>,
551        cursor: mouse::Cursor,
552        renderer: &Renderer,
553    ) -> mouse::Interaction {
554        self.raw
555            .as_overlay()
556            .mouse_interaction(layout, cursor, renderer)
557    }
558
559    fn index(&self) -> f32 {
560        self.raw.as_overlay().index()
561    }
562
563    fn operate(
564        &mut self,
565        layout: Layout<'_>,
566        renderer: &Renderer,
567        operation: &mut dyn widget::Operation,
568    ) {
569        self.raw
570            .as_overlay_mut()
571            .operate(layout, renderer, operation);
572    }
573
574    fn overlay<'c>(
575        &'c mut self,
576        layout: Layout<'c>,
577        renderer: &Renderer,
578    ) -> Vec<overlay::Element<'c, Message, Theme, Renderer>> {
579        let overlays = self.raw.as_overlay_mut().overlay(layout, renderer);
580
581        if overlays.is_empty() {
582            return Vec::new();
583        }
584
585        overlays
586            .into_iter()
587            .map(|raw| {
588                overlay::Element::new(Box::new(Overlay {
589                    component: self.component,
590                    internal: self.internal,
591                    is_outdated: self.is_outdated,
592                    raw,
593                }))
594            })
595            .collect()
596    }
597}