From db92e1c942154bee474fee5e2c187f8a52a1bb96 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Mon, 4 Mar 2024 19:32:20 +0100
Subject: Enhnace `Themer` to allow derivation from current `Theme`

---
 widget/src/helpers.rs |  13 +++--
 widget/src/themer.rs  | 133 ++++++++++++++++++++++++++++----------------------
 2 files changed, 80 insertions(+), 66 deletions(-)

(limited to 'widget')

diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs
index ed385ea5..e6322926 100644
--- a/widget/src/helpers.rs
+++ b/widget/src/helpers.rs
@@ -15,7 +15,6 @@ use crate::rule::{self, Rule};
 use crate::runtime::Command;
 use crate::scrollable::{self, Scrollable};
 use crate::slider::{self, Slider};
-use crate::style::application;
 use crate::text::{self, Text};
 use crate::text_editor::{self, TextEditor};
 use crate::text_input::{self, TextInput};
@@ -440,13 +439,13 @@ where
 }
 
 /// A widget that applies any `Theme` to its contents.
-pub fn themer<'a, Message, Theme, Renderer>(
-    theme: Theme,
-    content: impl Into<Element<'a, Message, Theme, Renderer>>,
-) -> Themer<'a, Message, Theme, Renderer>
+pub fn themer<'a, Message, OldTheme, NewTheme, F, Renderer>(
+    to_theme: F,
+    content: impl Into<Element<'a, Message, NewTheme, Renderer>>,
+) -> Themer<'a, Message, OldTheme, NewTheme, F, Renderer>
 where
+    F: Fn(&OldTheme) -> NewTheme,
     Renderer: core::Renderer,
-    Theme: application::StyleSheet,
 {
-    Themer::new(theme, content)
+    Themer::new(to_theme, content)
 }
diff --git a/widget/src/themer.rs b/widget/src/themer.rs
index 3a5fd823..a7eabd2c 100644
--- a/widget/src/themer.rs
+++ b/widget/src/themer.rs
@@ -7,58 +7,68 @@ use crate::core::renderer;
 use crate::core::widget::tree::{self, Tree};
 use crate::core::widget::Operation;
 use crate::core::{
-    Background, Clipboard, Element, Layout, Length, Point, Rectangle, Shell,
-    Size, Vector, Widget,
+    Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle,
+    Shell, Size, Vector, Widget,
 };
-use crate::style::application;
+
+use std::marker::PhantomData;
 
 /// A widget that applies any `Theme` to its contents.
 ///
 /// This widget can be useful to leverage multiple `Theme`
 /// types in an application.
 #[allow(missing_debug_implementations)]
-pub struct Themer<'a, Message, Theme, Renderer>
+pub struct Themer<'a, Message, Theme, NewTheme, F, Renderer = crate::Renderer>
 where
+    F: Fn(&Theme) -> NewTheme,
     Renderer: crate::core::Renderer,
-    Theme: application::StyleSheet,
 {
-    content: Element<'a, Message, Theme, Renderer>,
-    theme: Theme,
-    style: Theme::Style,
-    show_background: bool,
+    content: Element<'a, Message, NewTheme, Renderer>,
+    to_theme: F,
+    text_color: Option<fn(&NewTheme) -> Color>,
+    background: Option<fn(&NewTheme) -> Background>,
+    old_theme: PhantomData<Theme>,
 }
 
-impl<'a, Message, Theme, Renderer> Themer<'a, Message, Theme, Renderer>
+impl<'a, Message, Theme, NewTheme, F, Renderer>
+    Themer<'a, Message, Theme, NewTheme, F, Renderer>
 where
+    F: Fn(&Theme) -> NewTheme,
     Renderer: crate::core::Renderer,
-    Theme: application::StyleSheet,
 {
     /// Creates an empty [`Themer`] that applies the given `Theme`
     /// to the provided `content`.
-    pub fn new<T>(theme: Theme, content: T) -> Self
+    pub fn new<T>(to_theme: F, content: T) -> Self
     where
-        T: Into<Element<'a, Message, Theme, Renderer>>,
+        T: Into<Element<'a, Message, NewTheme, Renderer>>,
     {
         Self {
             content: content.into(),
-            theme,
-            style: Theme::Style::default(),
-            show_background: false,
+            to_theme,
+            text_color: None,
+            background: None,
+            old_theme: PhantomData,
         }
     }
 
-    /// Sets whether to draw the background color of the `Theme`.
-    pub fn background(mut self, background: bool) -> Self {
-        self.show_background = background;
+    /// Sets the default text [`Color`] of the [`Themer`].
+    pub fn text_color(mut self, f: fn(&NewTheme) -> Color) -> Self {
+        self.text_color = Some(f);
+        self
+    }
+
+    /// Sets the [`Background`] of the [`Themer`].
+    pub fn background(mut self, f: fn(&NewTheme) -> Background) -> Self {
+        self.background = Some(f);
         self
     }
 }
 
-impl<'a, AnyTheme, Message, Theme, Renderer> Widget<Message, AnyTheme, Renderer>
-    for Themer<'a, Message, Theme, Renderer>
+impl<'a, Message, Theme, NewTheme, F, Renderer> Widget<Message, Theme, Renderer>
+    for Themer<'a, Message, Theme, NewTheme, F, Renderer>
 where
+    F: Fn(&Theme) -> NewTheme,
     Renderer: crate::core::Renderer,
-    Theme: application::StyleSheet,
 {
     fn tag(&self) -> tree::Tag {
         self.content.as_widget().tag()
@@ -134,38 +144,36 @@ where
         &self,
         tree: &Tree,
         renderer: &mut Renderer,
-        _theme: &AnyTheme,
-        _style: &renderer::Style,
+        theme: &Theme,
+        style: &renderer::Style,
         layout: Layout<'_>,
         cursor: mouse::Cursor,
         viewport: &Rectangle,
     ) {
-        let appearance = self.theme.appearance(&self.style);
+        let theme = (self.to_theme)(theme);
 
-        if self.show_background {
+        if let Some(background) = self.background {
             container::draw_background(
                 renderer,
                 &container::Appearance {
-                    background: Some(Background::Color(
-                        appearance.background_color,
-                    )),
+                    background: Some(background(&theme)),
                     ..container::Appearance::default()
                 },
                 layout.bounds(),
             );
         }
 
-        self.content.as_widget().draw(
-            tree,
-            renderer,
-            &self.theme,
-            &renderer::Style {
-                text_color: appearance.text_color,
-            },
-            layout,
-            cursor,
-            viewport,
-        );
+        let style = if let Some(text_color) = self.text_color {
+            renderer::Style {
+                text_color: text_color(&theme),
+            }
+        } else {
+            *style
+        };
+
+        self.content
+            .as_widget()
+            .draw(tree, renderer, &theme, &style, layout, cursor, viewport);
     }
 
     fn overlay<'b>(
@@ -174,15 +182,15 @@ where
         layout: Layout<'_>,
         renderer: &Renderer,
         translation: Vector,
-    ) -> Option<overlay::Element<'b, Message, AnyTheme, Renderer>> {
-        struct Overlay<'a, Message, Theme, Renderer> {
-            theme: &'a Theme,
-            content: overlay::Element<'a, Message, Theme, Renderer>,
+    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
+        struct Overlay<'a, Message, Theme, NewTheme, Renderer> {
+            to_theme: &'a dyn Fn(&Theme) -> NewTheme,
+            content: overlay::Element<'a, Message, NewTheme, Renderer>,
         }
 
-        impl<'a, AnyTheme, Message, Theme, Renderer>
-            overlay::Overlay<Message, AnyTheme, Renderer>
-            for Overlay<'a, Message, Theme, Renderer>
+        impl<'a, Message, Theme, NewTheme, Renderer>
+            overlay::Overlay<Message, Theme, Renderer>
+            for Overlay<'a, Message, Theme, NewTheme, Renderer>
         where
             Renderer: crate::core::Renderer,
         {
@@ -197,13 +205,18 @@ where
             fn draw(
                 &self,
                 renderer: &mut Renderer,
-                _theme: &AnyTheme,
+                theme: &Theme,
                 style: &renderer::Style,
                 layout: Layout<'_>,
                 cursor: mouse::Cursor,
             ) {
-                self.content
-                    .draw(renderer, self.theme, style, layout, cursor);
+                self.content.draw(
+                    renderer,
+                    &(self.to_theme)(theme),
+                    style,
+                    layout,
+                    cursor,
+                );
             }
 
             fn on_event(
@@ -252,12 +265,12 @@ where
                 &'b mut self,
                 layout: Layout<'_>,
                 renderer: &Renderer,
-            ) -> Option<overlay::Element<'b, Message, AnyTheme, Renderer>>
+            ) -> Option<overlay::Element<'b, Message, Theme, Renderer>>
             {
                 self.content
                     .overlay(layout, renderer)
                     .map(|content| Overlay {
-                        theme: self.theme,
+                        to_theme: &self.to_theme,
                         content,
                     })
                     .map(|overlay| overlay::Element::new(Box::new(overlay)))
@@ -268,24 +281,26 @@ where
             .as_widget_mut()
             .overlay(tree, layout, renderer, translation)
             .map(|content| Overlay {
-                theme: &self.theme,
+                to_theme: &self.to_theme,
                 content,
             })
             .map(|overlay| overlay::Element::new(Box::new(overlay)))
     }
 }
 
-impl<'a, AnyTheme, Message, Theme, Renderer>
-    From<Themer<'a, Message, Theme, Renderer>>
-    for Element<'a, Message, AnyTheme, Renderer>
+impl<'a, Message, Theme, NewTheme, F, Renderer>
+    From<Themer<'a, Message, Theme, NewTheme, F, Renderer>>
+    for Element<'a, Message, Theme, Renderer>
 where
     Message: 'a,
-    Theme: 'a + application::StyleSheet,
+    Theme: 'a,
+    NewTheme: 'a,
+    F: Fn(&Theme) -> NewTheme + 'a,
     Renderer: 'a + crate::core::Renderer,
 {
     fn from(
-        themer: Themer<'a, Message, Theme, Renderer>,
-    ) -> Element<'a, Message, AnyTheme, Renderer> {
+        themer: Themer<'a, Message, Theme, NewTheme, F, Renderer>,
+    ) -> Element<'a, Message, Theme, Renderer> {
         Element::new(themer)
     }
 }
-- 
cgit