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::tree::{self, Tree};
8use crate::core::widget::{Meta, Operation};
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(theme: Option<Theme>, content: impl Widget<Message, Theme, Renderer> + 'a) -> Self {
34        Self {
35            content: content._boxed(),
36            theme,
37            text_color: None,
38            background: None,
39        }
40    }
41
42    /// Sets the default text [`Color`] of the [`Themer`].
43    pub fn text_color(mut self, f: fn(&Theme) -> Color) -> Self {
44        self.text_color = Some(f);
45        self
46    }
47
48    /// Sets the [`Background`] of the [`Themer`].
49    pub fn background(mut self, f: fn(&Theme) -> Background) -> Self {
50        self.background = Some(f);
51        self
52    }
53}
54
55impl<Message, Theme, Renderer> Meta for Themer<'_, Message, Theme, Renderer> where
56    Renderer: crate::core::Renderer
57{
58}
59
60impl<Message, Theme, Renderer, AnyTheme> Widget<Message, AnyTheme, Renderer>
61    for Themer<'_, Message, Theme, Renderer>
62where
63    Theme: theme::Base,
64    AnyTheme: theme::Base,
65    Renderer: crate::core::Renderer,
66{
67    fn tag(&self) -> tree::Tag {
68        self.content.tag()
69    }
70
71    fn state(&self) -> tree::State {
72        self.content.state()
73    }
74
75    fn diff(&mut self, tree: &mut Tree) {
76        self.content.diff(tree);
77    }
78
79    fn size(&self) -> Size<Length> {
80        self.content.size()
81    }
82
83    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
84        self.content.layout(tree, renderer, limits);
85    }
86
87    fn operate(
88        &mut self,
89        tree: &mut Tree,
90        layout: Layout,
91        viewport: &Rectangle,
92        renderer: &Renderer,
93        operation: &mut dyn Operation,
94    ) {
95        self.content
96            .operate(tree, layout, viewport, renderer, operation);
97    }
98
99    fn update(
100        &mut self,
101        tree: &mut Tree,
102        event: &Event,
103        layout: Layout,
104        cursor: mouse::Cursor,
105        renderer: &Renderer,
106        shell: &mut Shell<'_, Message>,
107        viewport: &Rectangle,
108    ) {
109        self.content
110            .update(tree, event, layout, cursor, renderer, shell, viewport);
111    }
112
113    fn mouse_interaction(
114        &self,
115        tree: &Tree,
116        layout: Layout,
117        cursor: mouse::Cursor,
118        viewport: &Rectangle,
119        renderer: &Renderer,
120    ) -> mouse::Interaction {
121        self.content
122            .mouse_interaction(tree, layout, cursor, viewport, renderer)
123    }
124
125    fn draw(
126        &self,
127        tree: &Tree,
128        renderer: &mut Renderer,
129        theme: &AnyTheme,
130        style: &renderer::Style,
131        layout: Layout,
132        cursor: mouse::Cursor,
133        viewport: &Rectangle,
134    ) {
135        let default_theme = theme::Base::default(theme.mode());
136        let theme = self.theme.as_ref().unwrap_or(&default_theme);
137
138        if let Some(background) = self.background {
139            container::draw_background(
140                renderer,
141                &container::Style {
142                    background: Some(background(theme)),
143                    ..container::Style::default()
144                },
145                layout.bounds(),
146            );
147        }
148
149        let style = if let Some(text_color) = self.text_color {
150            renderer::Style {
151                text_color: text_color(theme),
152            }
153        } else {
154            *style
155        };
156
157        self.content
158            .draw(tree, renderer, theme, &style, layout, cursor, viewport);
159    }
160
161    fn overlay<'b>(
162        &'b mut self,
163        tree: &'b mut Tree,
164        layout: Layout,
165        renderer: &Renderer,
166        viewport: &Rectangle,
167        translation: Vector,
168        window: Size,
169    ) -> Vec<overlay::Element<'b, Message, AnyTheme, Renderer>> {
170        struct Overlay<'a, Message, Theme, Renderer> {
171            theme: &'a Option<Theme>,
172            content: overlay::Element<'a, Message, Theme, Renderer>,
173        }
174
175        impl<Message, Theme, Renderer, AnyTheme> overlay::Overlay<Message, AnyTheme, Renderer>
176            for Overlay<'_, Message, Theme, Renderer>
177        where
178            Theme: theme::Base,
179            AnyTheme: theme::Base,
180            Renderer: crate::core::Renderer,
181        {
182            fn draw(
183                &self,
184                renderer: &mut Renderer,
185                theme: &AnyTheme,
186                style: &renderer::Style,
187                cursor: mouse::Cursor,
188            ) {
189                let default_theme = theme::Base::default(theme.mode());
190                let theme = self.theme.as_ref().unwrap_or(&default_theme);
191
192                self.content
193                    .as_overlay()
194                    .draw(renderer, theme, style, cursor);
195            }
196
197            fn update(
198                &mut self,
199                event: &Event,
200                cursor: mouse::Cursor,
201                renderer: &Renderer,
202                shell: &mut Shell<'_, Message>,
203            ) {
204                self.content
205                    .as_overlay_mut()
206                    .update(event, cursor, renderer, shell);
207            }
208
209            fn operate(&mut self, renderer: &Renderer, operation: &mut dyn Operation) {
210                self.content.as_overlay_mut().operate(renderer, operation);
211            }
212
213            fn mouse_interaction(
214                &self,
215                cursor: mouse::Cursor,
216                renderer: &Renderer,
217            ) -> mouse::Interaction {
218                self.content
219                    .as_overlay()
220                    .mouse_interaction(cursor, renderer)
221            }
222
223            fn index(&self) -> f32 {
224                self.content.as_overlay().index()
225            }
226
227            fn overlay<'c>(
228                &'c mut self,
229                renderer: &Renderer,
230            ) -> Vec<overlay::Element<'c, Message, AnyTheme, Renderer>> {
231                let theme = self.theme;
232
233                self.content
234                    .as_overlay_mut()
235                    .overlay(renderer)
236                    .into_iter()
237                    .map(|content| overlay::Element::new(Box::new(Overlay { theme, content })))
238                    .collect()
239            }
240        }
241
242        self.content
243            .overlay(tree, layout, renderer, viewport, translation, window)
244            .into_iter()
245            .map(|content| {
246                overlay::Element::new(Box::new(Overlay {
247                    theme: &self.theme,
248                    content,
249                }))
250            })
251            .collect()
252    }
253}