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