title_bar: Fix close button hover color on Windows (#45867)

Jason Lee created

Release Notes:

- Fix close button hover text color on Windows

| Before | After |
| --- | --- |
| <img width="166" height="137" alt="before"
src="https://github.com/user-attachments/assets/fcd225c4-c73f-4e21-8e8d-459395df27ec"
/> |<img width="188" height="156" alt="after"
src="https://github.com/user-attachments/assets/89f5dcba-d677-4eb9-a9b4-170eb9792cb2"
/> |


https://github.com/user-attachments/assets/27e88bf1-eddd-4ec8-a360-6a68eef484e0

And I also improved this file's implementation to merge the
`WindowsCaptionButtonIcon` into `WindowsCaptionButton`.

Change summary

crates/title_bar/src/platforms/platform_windows.rs | 142 +++++++--------
1 file changed, 68 insertions(+), 74 deletions(-)

Detailed changes

crates/title_bar/src/platforms/platform_windows.rs 🔗

@@ -33,17 +33,7 @@ impl WindowsWindowControls {
 }
 
 impl RenderOnce for WindowsWindowControls {
-    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
-        let close_button_hover_color = Rgba {
-            r: 232.0 / 255.0,
-            g: 17.0 / 255.0,
-            b: 32.0 / 255.0,
-            a: 1.0,
-        };
-
-        let button_hover_color = cx.theme().colors().ghost_element_hover;
-        let button_active_color = cx.theme().colors().ghost_element_active;
-
+    fn render(self, window: &mut Window, _: &mut App) -> impl IntoElement {
         div()
             .id("windows-window-controls")
             .font_family(Self::get_font())
@@ -53,91 +43,95 @@ impl RenderOnce for WindowsWindowControls {
             .content_stretch()
             .max_h(self.button_height)
             .min_h(self.button_height)
-            .child(WindowsCaptionButton::new(
-                "minimize",
-                WindowsCaptionButtonIcon::Minimize,
-                button_hover_color,
-                button_active_color,
-            ))
-            .child(WindowsCaptionButton::new(
-                "maximize-or-restore",
-                if window.is_maximized() {
-                    WindowsCaptionButtonIcon::Restore
+            .child(WindowsCaptionButton::Minimize)
+            .map(|this| {
+                this.child(if window.is_maximized() {
+                    WindowsCaptionButton::Restore
                 } else {
-                    WindowsCaptionButtonIcon::Maximize
-                },
-                button_hover_color,
-                button_active_color,
-            ))
-            .child(WindowsCaptionButton::new(
-                "close",
-                WindowsCaptionButtonIcon::Close,
-                close_button_hover_color,
-                button_active_color,
-            ))
+                    WindowsCaptionButton::Maximize
+                })
+            })
+            .child(WindowsCaptionButton::Close)
     }
 }
 
-#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
-enum WindowsCaptionButtonIcon {
+#[derive(IntoElement)]
+enum WindowsCaptionButton {
     Minimize,
     Restore,
     Maximize,
     Close,
 }
 
-#[derive(IntoElement)]
-struct WindowsCaptionButton {
-    id: ElementId,
-    icon: WindowsCaptionButtonIcon,
-    hover_background_color: Hsla,
-    active_background_color: Hsla,
-}
-
 impl WindowsCaptionButton {
-    pub fn new(
-        id: impl Into<ElementId>,
-        icon: WindowsCaptionButtonIcon,
-        hover_background_color: impl Into<Hsla>,
-        active_background_color: impl Into<Hsla>,
-    ) -> Self {
-        Self {
-            id: id.into(),
-            icon,
-            hover_background_color: hover_background_color.into(),
-            active_background_color: active_background_color.into(),
+    #[inline]
+    fn id(&self) -> &'static str {
+        match self {
+            Self::Minimize => "minimize",
+            Self::Restore => "restore",
+            Self::Maximize => "maximize",
+            Self::Close => "close",
+        }
+    }
+
+    #[inline]
+    fn icon(&self) -> &'static str {
+        match self {
+            Self::Minimize => "\u{e921}",
+            Self::Restore => "\u{e923}",
+            Self::Maximize => "\u{e922}",
+            Self::Close => "\u{e8bb}",
+        }
+    }
+
+    #[inline]
+    fn control_area(&self) -> WindowControlArea {
+        match self {
+            Self::Close => WindowControlArea::Close,
+            Self::Maximize | Self::Restore => WindowControlArea::Max,
+            Self::Minimize => WindowControlArea::Min,
         }
     }
 }
 
 impl RenderOnce for WindowsCaptionButton {
-    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
+    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
+        let (hover_bg, hover_fg, active_bg, active_fg) = match self {
+            Self::Close => {
+                let color: Hsla = Rgba {
+                    r: 232.0 / 255.0,
+                    g: 17.0 / 255.0,
+                    b: 32.0 / 255.0,
+                    a: 1.0,
+                }
+                .into();
+
+                (
+                    color,
+                    gpui::white(),
+                    color.opacity(0.8),
+                    gpui::white().opacity(0.8),
+                )
+            }
+            _ => (
+                cx.theme().colors().ghost_element_hover,
+                cx.theme().colors().text,
+                cx.theme().colors().ghost_element_active,
+                cx.theme().colors().text,
+            ),
+        };
+
         h_flex()
-            .id(self.id)
+            .id(self.id())
             .justify_center()
             .content_center()
             .occlude()
             .w(px(36.))
             .h_full()
             .text_size(px(10.0))
-            .hover(|style| style.bg(self.hover_background_color))
-            .active(|style| style.bg(self.active_background_color))
-            .map(|this| match self.icon {
-                WindowsCaptionButtonIcon::Close => {
-                    this.window_control_area(WindowControlArea::Close)
-                }
-                WindowsCaptionButtonIcon::Maximize | WindowsCaptionButtonIcon::Restore => {
-                    this.window_control_area(WindowControlArea::Max)
-                }
-                WindowsCaptionButtonIcon::Minimize => {
-                    this.window_control_area(WindowControlArea::Min)
-                }
-            })
-            .child(match self.icon {
-                WindowsCaptionButtonIcon::Minimize => "\u{e921}",
-                WindowsCaptionButtonIcon::Restore => "\u{e923}",
-                WindowsCaptionButtonIcon::Maximize => "\u{e922}",
-                WindowsCaptionButtonIcon::Close => "\u{e8bb}",
-            })
+            .hover(|style| style.bg(hover_bg).text_color(hover_fg))
+            .active(|style| style.bg(active_bg).text_color(active_fg))
+            .window_control_area(self.control_area())
+            .child(self.icon())
     }
 }