Refactor button rendering to use ThemeColor instead of direct theme calls

Nate Butler created

Change summary

crates/ui2/src/elements/button.rs | 39 +++++++++++---------------------
crates/ui2/src/prelude.rs         | 35 +++++++++++++++++++++++++++++
2 files changed, 49 insertions(+), 25 deletions(-)

Detailed changes

crates/ui2/src/elements/button.rs 🔗

@@ -102,27 +102,19 @@ impl<S: 'static + Send + Sync + Clone> Button<S> {
     }
 
     fn background_color(&self, cx: &mut ViewContext<S>) -> Hsla {
-        let theme = theme(cx);
-        let system_color = SystemColor::new();
+        let color = ThemeColor::new(cx);
 
         match (self.variant, self.state) {
-            (ButtonVariant::Ghost, InteractionState::Hovered) => {
-                theme.lowest.base.hovered.background
-            }
-            (ButtonVariant::Ghost, InteractionState::Active) => {
-                theme.lowest.base.pressed.background
-            }
-            (ButtonVariant::Filled, InteractionState::Enabled) => {
-                theme.lowest.on.default.background
-            }
-            (ButtonVariant::Filled, InteractionState::Hovered) => {
-                theme.lowest.on.hovered.background
-            }
-            (ButtonVariant::Filled, InteractionState::Active) => theme.lowest.on.pressed.background,
-            (ButtonVariant::Filled, InteractionState::Disabled) => {
-                theme.lowest.on.disabled.background
-            }
-            _ => system_color.transparent,
+            (ButtonVariant::Ghost, InteractionState::Enabled) => color.ghost_element,
+            (ButtonVariant::Ghost, InteractionState::Focused) => color.ghost_element,
+            (ButtonVariant::Ghost, InteractionState::Hovered) => color.ghost_element_hover,
+            (ButtonVariant::Ghost, InteractionState::Active) => color.ghost_element_active,
+            (ButtonVariant::Ghost, InteractionState::Disabled) => color.filled_element_disabled,
+            (ButtonVariant::Filled, InteractionState::Enabled) => color.filled_element,
+            (ButtonVariant::Filled, InteractionState::Focused) => color.filled_element,
+            (ButtonVariant::Filled, InteractionState::Hovered) => color.filled_element_hover,
+            (ButtonVariant::Filled, InteractionState::Active) => color.filled_element_active,
+            (ButtonVariant::Filled, InteractionState::Disabled) => color.filled_element_disabled,
         }
     }
 
@@ -141,12 +133,11 @@ impl<S: 'static + Send + Sync + Clone> Button<S> {
     }
 
     fn border_color(&self, cx: &WindowContext) -> Hsla {
-        let theme = theme(cx);
-        let system_color = SystemColor::new();
+        let color = ThemeColor::new(cx);
 
         match self.state {
-            InteractionState::Focused => theme.lowest.accent.default.border,
-            _ => system_color.transparent,
+            InteractionState::Focused => color.border_focused,
+            _ => color.border_transparent,
         }
     }
 
@@ -161,9 +152,7 @@ impl<S: 'static + Send + Sync + Clone> Button<S> {
     }
 
     fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
-        let theme = theme(cx);
         let icon_color = self.icon_color();
-        let system_color = SystemColor::new();
         let border_color = self.border_color(cx);
 
         let mut el = h_stack()

crates/ui2/src/prelude.rs 🔗

@@ -58,21 +58,56 @@ pub struct ThemeColor {
     pub border: Hsla,
     pub border_variant: Hsla,
     pub border_focused: Hsla,
+    pub border_transparent: Hsla,
     /// The background color of an elevated surface, like a modal, tooltip or toast.
     pub elevated_surface: Hsla,
     pub surface: Hsla,
+    /// Default background for elements like filled buttons,
+    /// text fields, checkboxes, radio buttons, etc.
+    /// - TODO: Map to step 3.
+    pub filled_element: Hsla,
+    /// The background color of a hovered element, like a button being hovered
+    /// with a mouse, or hovered on a touch screen.
+    /// - TODO: Map to step 4.
+    pub filled_element_hover: Hsla,
+    /// The background color of an active element, like a button being pressed,
+    /// or tapped on a touch screen.
+    /// - TODO: Map to step 5.
+    pub filled_element_active: Hsla,
+    /// The background color of a selected element, like a selected tab, a button toggled on, or a checkbox that is checked.
+    pub filled_element_selected: Hsla,
+    pub filled_element_disabled: Hsla,
+    pub ghost_element: Hsla,
+    /// - TODO: Map to step 3.
+    pub ghost_element_hover: Hsla,
+    /// - TODO: Map to step 4.
+    pub ghost_element_active: Hsla,
+    pub ghost_element_selected: Hsla,
+    pub ghost_element_disabled: Hsla,
 }
 
 impl ThemeColor {
     pub fn new(cx: &WindowContext) -> Self {
         let theme = theme(cx);
+        let system_color = SystemColor::new();
 
         Self {
             border: theme.lowest.base.default.border,
             border_variant: theme.lowest.variant.default.border,
             border_focused: theme.lowest.accent.default.border,
+            border_transparent: system_color.transparent,
             elevated_surface: theme.middle.base.default.background,
             surface: theme.middle.base.default.background,
+            filled_element: theme.lowest.base.default.background,
+            filled_element_hover: theme.lowest.base.hovered.background,
+            filled_element_active: theme.lowest.base.active.background,
+            filled_element_selected: theme.lowest.accent.default.background,
+            filled_element_disabled: system_color.transparent,
+            ghost_element: system_color.transparent,
+            ghost_element_hover: theme.lowest.base.default.background,
+            ghost_element_active: theme.lowest.base.hovered.background,
+            ghost_element_selected: theme.lowest.accent.default.background,
+            ghost_element_disabled: system_color.transparent,
         }
     }
 }