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 children(&self) -> Vec<Tree> {
74        self.content.as_widget().children()
75    }
76
77    fn diff(&self, tree: &mut Tree) {
78        self.content.as_widget().diff(tree);
79    }
80
81    fn size(&self) -> Size<Length> {
82        self.content.as_widget().size()
83    }
84
85    fn layout(
86        &mut self,
87        tree: &mut Tree,
88        renderer: &Renderer,
89        limits: &layout::Limits,
90    ) -> layout::Node {
91        self.content.as_widget_mut().layout(tree, renderer, limits)
92    }
93
94    fn operate(
95        &mut self,
96        tree: &mut Tree,
97        layout: Layout<'_>,
98        renderer: &Renderer,
99        operation: &mut dyn Operation,
100    ) {
101        self.content
102            .as_widget_mut()
103            .operate(tree, layout, renderer, operation);
104    }
105
106    fn update(
107        &mut self,
108        tree: &mut Tree,
109        event: &Event,
110        layout: Layout<'_>,
111        cursor: mouse::Cursor,
112        renderer: &Renderer,
113        shell: &mut Shell<'_, Message>,
114        viewport: &Rectangle,
115    ) {
116        self.content
117            .as_widget_mut()
118            .update(tree, event, layout, cursor, renderer, shell, viewport);
119    }
120
121    fn mouse_interaction(
122        &self,
123        tree: &Tree,
124        layout: Layout<'_>,
125        cursor: mouse::Cursor,
126        viewport: &Rectangle,
127        renderer: &Renderer,
128    ) -> mouse::Interaction {
129        self.content
130            .as_widget()
131            .mouse_interaction(tree, layout, cursor, viewport, renderer)
132    }
133
134    fn draw(
135        &self,
136        tree: &Tree,
137        renderer: &mut Renderer,
138        theme: &AnyTheme,
139        style: &renderer::Style,
140        layout: Layout<'_>,
141        cursor: mouse::Cursor,
142        viewport: &Rectangle,
143    ) {
144        let default_theme = theme::Base::default(theme.mode());
145        let theme = self.theme.as_ref().unwrap_or(&default_theme);
146
147        if let Some(background) = self.background {
148            container::draw_background(
149                renderer,
150                &container::Style {
151                    background: Some(background(theme)),
152                    ..container::Style::default()
153                },
154                layout.bounds(),
155            );
156        }
157
158        let style = if let Some(text_color) = self.text_color {
159            renderer::Style {
160                text_color: text_color(theme),
161            }
162        } else {
163            *style
164        };
165
166        self.content
167            .as_widget()
168            .draw(tree, renderer, theme, &style, layout, cursor, viewport);
169    }
170
171    fn overlay<'b>(
172        &'b mut self,
173        tree: &'b mut Tree,
174        layout: Layout<'b>,
175        renderer: &Renderer,
176        viewport: &Rectangle,
177        translation: Vector,
178    ) -> Option<overlay::Element<'b, Message, AnyTheme, Renderer>> {
179        struct Overlay<'a, Message, Theme, Renderer> {
180            theme: &'a Option<Theme>,
181            content: overlay::Element<'a, Message, Theme, Renderer>,
182        }
183
184        impl<Message, Theme, Renderer, AnyTheme> overlay::Overlay<Message, AnyTheme, Renderer>
185            for Overlay<'_, Message, Theme, Renderer>
186        where
187            Theme: theme::Base,
188            AnyTheme: theme::Base,
189            Renderer: crate::core::Renderer,
190        {
191            fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
192                self.content.as_overlay_mut().layout(renderer, bounds)
193            }
194
195            fn draw(
196                &self,
197                renderer: &mut Renderer,
198                theme: &AnyTheme,
199                style: &renderer::Style,
200                layout: Layout<'_>,
201                cursor: mouse::Cursor,
202            ) {
203                let default_theme = theme::Base::default(theme.mode());
204                let theme = self.theme.as_ref().unwrap_or(&default_theme);
205
206                self.content
207                    .as_overlay()
208                    .draw(renderer, theme, style, layout, cursor);
209            }
210
211            fn update(
212                &mut self,
213                event: &Event,
214                layout: Layout<'_>,
215                cursor: mouse::Cursor,
216                renderer: &Renderer,
217                shell: &mut Shell<'_, Message>,
218            ) {
219                self.content
220                    .as_overlay_mut()
221                    .update(event, layout, cursor, renderer, shell);
222            }
223
224            fn operate(
225                &mut self,
226                layout: Layout<'_>,
227                renderer: &Renderer,
228                operation: &mut dyn Operation,
229            ) {
230                self.content
231                    .as_overlay_mut()
232                    .operate(layout, renderer, operation);
233            }
234
235            fn mouse_interaction(
236                &self,
237                layout: Layout<'_>,
238                cursor: mouse::Cursor,
239                renderer: &Renderer,
240            ) -> mouse::Interaction {
241                self.content
242                    .as_overlay()
243                    .mouse_interaction(layout, cursor, renderer)
244            }
245
246            fn overlay<'b>(
247                &'b mut self,
248                layout: Layout<'b>,
249                renderer: &Renderer,
250            ) -> Option<overlay::Element<'b, Message, AnyTheme, Renderer>> {
251                self.content
252                    .as_overlay_mut()
253                    .overlay(layout, renderer)
254                    .map(|content| Overlay {
255                        theme: self.theme,
256                        content,
257                    })
258                    .map(|overlay| overlay::Element::new(Box::new(overlay)))
259            }
260        }
261
262        self.content
263            .as_widget_mut()
264            .overlay(tree, layout, renderer, viewport, translation)
265            .map(|content| Overlay {
266                theme: &self.theme,
267                content,
268            })
269            .map(|overlay| overlay::Element::new(Box::new(overlay)))
270    }
271}
272
273impl<'a, Message, Theme, Renderer, AnyTheme> From<Themer<'a, Message, Theme, Renderer>>
274    for Element<'a, Message, AnyTheme, Renderer>
275where
276    Message: 'a,
277    Theme: theme::Base + 'a,
278    AnyTheme: theme::Base,
279    Renderer: 'a + crate::core::Renderer,
280{
281    fn from(
282        themer: Themer<'a, Message, Theme, Renderer>,
283    ) -> Element<'a, Message, AnyTheme, Renderer> {
284        Element::new(themer)
285    }
286}