iced_widget/
themer.rs

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