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