Skip to main content

iced_widget/
themer.rs

1use crate::container;
2use crate::core::layout;
3use crate::core::mouse;
4use crate::core::overlay;
5use crate::core::renderer;
6use crate::core::theme;
7use crate::core::widget::Operation;
8use crate::core::widget::tree::{self, Tree};
9use crate::core::{
10    Background, Color, Element, Event, Layout, Length, Rectangle, Shell, Size, Vector, Widget,
11};
12
13/// A widget that applies any `Theme` to its contents.
14///
15/// This widget can be useful to leverage multiple `Theme`
16/// types in an application.
17pub struct Themer<'a, Message, Theme, Renderer = crate::Renderer>
18where
19    Renderer: crate::core::Renderer,
20{
21    content: Element<'a, Message, Theme, Renderer>,
22    theme: Option<Theme>,
23    text_color: Option<fn(&Theme) -> Color>,
24    background: Option<fn(&Theme) -> Background>,
25}
26
27impl<'a, Message, Theme, Renderer> Themer<'a, Message, Theme, Renderer>
28where
29    Renderer: crate::core::Renderer,
30{
31    /// Creates an empty [`Themer`] that applies the given `Theme`
32    /// to the provided `content`.
33    pub fn new(
34        theme: Option<Theme>,
35        content: impl Into<Element<'a, Message, Theme, Renderer>>,
36    ) -> Self {
37        Self {
38            content: content.into(),
39            theme,
40            text_color: None,
41            background: None,
42        }
43    }
44
45    /// Sets the default text [`Color`] of the [`Themer`].
46    pub fn text_color(mut self, f: fn(&Theme) -> Color) -> Self {
47        self.text_color = Some(f);
48        self
49    }
50
51    /// Sets the [`Background`] of the [`Themer`].
52    pub fn background(mut self, f: fn(&Theme) -> Background) -> Self {
53        self.background = Some(f);
54        self
55    }
56}
57
58impl<Message, Theme, Renderer, AnyTheme> Widget<Message, AnyTheme, Renderer>
59    for Themer<'_, Message, Theme, Renderer>
60where
61    Theme: theme::Base,
62    AnyTheme: theme::Base,
63    Renderer: crate::core::Renderer,
64{
65    fn tag(&self) -> tree::Tag {
66        self.content.as_widget().tag()
67    }
68
69    fn state(&self) -> tree::State {
70        self.content.as_widget().state()
71    }
72
73    fn diff(&mut self, tree: &mut Tree) {
74        self.content.as_widget_mut().diff(tree);
75    }
76
77    fn size(&self) -> Size<Length> {
78        self.content.as_widget().size()
79    }
80
81    fn layout(
82        &mut self,
83        tree: &mut Tree,
84        renderer: &Renderer,
85        limits: &layout::Limits,
86    ) -> layout::Node {
87        self.content.as_widget_mut().layout(tree, renderer, limits)
88    }
89
90    fn operate(
91        &mut self,
92        tree: &mut Tree,
93        layout: Layout<'_>,
94        renderer: &Renderer,
95        operation: &mut dyn Operation,
96    ) {
97        self.content
98            .as_widget_mut()
99            .operate(tree, layout, renderer, operation);
100    }
101
102    fn update(
103        &mut self,
104        tree: &mut Tree,
105        event: &Event,
106        layout: Layout<'_>,
107        cursor: mouse::Cursor,
108        renderer: &Renderer,
109        shell: &mut Shell<'_, Message>,
110        viewport: &Rectangle,
111    ) {
112        self.content
113            .as_widget_mut()
114            .update(tree, event, layout, cursor, renderer, shell, viewport);
115    }
116
117    fn mouse_interaction(
118        &self,
119        tree: &Tree,
120        layout: Layout<'_>,
121        cursor: mouse::Cursor,
122        viewport: &Rectangle,
123        renderer: &Renderer,
124    ) -> mouse::Interaction {
125        self.content
126            .as_widget()
127            .mouse_interaction(tree, layout, cursor, viewport, renderer)
128    }
129
130    fn draw(
131        &self,
132        tree: &Tree,
133        renderer: &mut Renderer,
134        theme: &AnyTheme,
135        style: &renderer::Style,
136        layout: Layout<'_>,
137        cursor: mouse::Cursor,
138        viewport: &Rectangle,
139    ) {
140        let default_theme = theme::Base::default(theme.mode());
141        let theme = self.theme.as_ref().unwrap_or(&default_theme);
142
143        if let Some(background) = self.background {
144            container::draw_background(
145                renderer,
146                &container::Style {
147                    background: Some(background(theme)),
148                    ..container::Style::default()
149                },
150                layout.bounds(),
151            );
152        }
153
154        let style = if let Some(text_color) = self.text_color {
155            renderer::Style {
156                text_color: text_color(theme),
157            }
158        } else {
159            *style
160        };
161
162        self.content
163            .as_widget()
164            .draw(tree, renderer, theme, &style, layout, cursor, viewport);
165    }
166
167    fn overlay<'b>(
168        &'b mut self,
169        tree: &'b mut Tree,
170        layout: Layout<'b>,
171        renderer: &Renderer,
172        viewport: &Rectangle,
173        translation: Vector,
174    ) -> Option<overlay::Element<'b, Message, AnyTheme, Renderer>> {
175        struct Overlay<'a, Message, Theme, Renderer> {
176            theme: &'a Option<Theme>,
177            content: overlay::Element<'a, Message, Theme, Renderer>,
178        }
179
180        impl<Message, Theme, Renderer, AnyTheme> overlay::Overlay<Message, AnyTheme, Renderer>
181            for Overlay<'_, Message, Theme, Renderer>
182        where
183            Theme: theme::Base,
184            AnyTheme: theme::Base,
185            Renderer: crate::core::Renderer,
186        {
187            fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
188                self.content.as_overlay_mut().layout(renderer, bounds)
189            }
190
191            fn draw(
192                &self,
193                renderer: &mut Renderer,
194                theme: &AnyTheme,
195                style: &renderer::Style,
196                layout: Layout<'_>,
197                cursor: mouse::Cursor,
198            ) {
199                let default_theme = theme::Base::default(theme.mode());
200                let theme = self.theme.as_ref().unwrap_or(&default_theme);
201
202                self.content
203                    .as_overlay()
204                    .draw(renderer, theme, style, layout, cursor);
205            }
206
207            fn update(
208                &mut self,
209                event: &Event,
210                layout: Layout<'_>,
211                cursor: mouse::Cursor,
212                renderer: &Renderer,
213                shell: &mut Shell<'_, Message>,
214            ) {
215                self.content
216                    .as_overlay_mut()
217                    .update(event, layout, cursor, renderer, shell);
218            }
219
220            fn operate(
221                &mut self,
222                layout: Layout<'_>,
223                renderer: &Renderer,
224                operation: &mut dyn Operation,
225            ) {
226                self.content
227                    .as_overlay_mut()
228                    .operate(layout, renderer, operation);
229            }
230
231            fn mouse_interaction(
232                &self,
233                layout: Layout<'_>,
234                cursor: mouse::Cursor,
235                renderer: &Renderer,
236            ) -> mouse::Interaction {
237                self.content
238                    .as_overlay()
239                    .mouse_interaction(layout, cursor, renderer)
240            }
241
242            fn overlay<'b>(
243                &'b mut self,
244                layout: Layout<'b>,
245                renderer: &Renderer,
246            ) -> Option<overlay::Element<'b, Message, AnyTheme, Renderer>> {
247                self.content
248                    .as_overlay_mut()
249                    .overlay(layout, renderer)
250                    .map(|content| Overlay {
251                        theme: self.theme,
252                        content,
253                    })
254                    .map(|overlay| overlay::Element::new(Box::new(overlay)))
255            }
256        }
257
258        self.content
259            .as_widget_mut()
260            .overlay(tree, layout, renderer, viewport, translation)
261            .map(|content| Overlay {
262                theme: &self.theme,
263                content,
264            })
265            .map(|overlay| overlay::Element::new(Box::new(overlay)))
266    }
267}
268
269impl<'a, Message, Theme, Renderer, AnyTheme> From<Themer<'a, Message, Theme, Renderer>>
270    for Element<'a, Message, AnyTheme, Renderer>
271where
272    Message: 'a,
273    Theme: theme::Base + 'a,
274    AnyTheme: theme::Base,
275    Renderer: 'a + crate::core::Renderer,
276{
277    fn from(
278        themer: Themer<'a, Message, Theme, Renderer>,
279    ) -> Element<'a, Message, AnyTheme, Renderer> {
280        Element::new(themer)
281    }
282}