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