theme: Add `scrollbar_thumb_active_background` color (#30177)

Finn Evers created

Follow-up to #28064

This PR adds the `scrollbar_thumb_active_background` to themes and uses
it for the editor scrollbars to color these whilst they are being
dragged. This way, we provide the best customizabiliy for the scrollbars
and enable theme authors to add good contrasts between all the three
states `ScrollbarThumbState::Idle`, `ScrollbarThumbState::Hovered` and
ScrollbarThumbState::Dragging`.

It also adds this to the VsCode theme importer so any future imported
themes will have this set as well.

Whenever the property is not set, I decided it is best to fall back to
the normal `thumb_background` for the time being, as this way the
distinction and contrast between hovered and active state is better than
having the same color for hovering and dragging the scrollbar.

Example with active color set via `experimental.theme_overrides` in the
settings:


https://github.com/user-attachments/assets/9934e75b-6e0a-4a41-90ba-bfffb89865e7

Release Notes:

- Added the `scrollbar.thumb.active_background` color to themes. Theme
authors can use this property in combination with
`scrollbar.thumb.hover_background` to customize the color of the editor
scrollbar thumbs while these are hovered or being dragged.

Change summary

crates/editor/src/element.rs                  |  5 +++
crates/theme/src/default_colors.rs            |  2 +
crates/theme/src/fallback_themes.rs           |  6 ++++
crates/theme/src/schema.rs                    | 28 ++++++++++++++------
crates/theme/src/styles/colors.rs             |  6 ++++
crates/theme_importer/src/vscode/converter.rs |  4 +++
6 files changed, 41 insertions(+), 10 deletions(-)

Detailed changes

crates/editor/src/element.rs 🔗

@@ -5280,7 +5280,10 @@ impl EditorElement {
                     }
 
                     let scrollbar_thumb_color = match scrollbar_layout.thumb_state {
-                        ScrollbarThumbState::Dragging | ScrollbarThumbState::Hovered => {
+                        ScrollbarThumbState::Dragging => {
+                            cx.theme().colors().scrollbar_thumb_active_background
+                        }
+                        ScrollbarThumbState::Hovered => {
                             cx.theme().colors().scrollbar_thumb_hover_background
                         }
                         ScrollbarThumbState::Idle => cx.theme().colors().scrollbar_thumb_background,

crates/theme/src/default_colors.rs 🔗

@@ -86,6 +86,7 @@ impl ThemeColors {
             pane_group_border: neutral().light().step_6(),
             scrollbar_thumb_background: neutral().light_alpha().step_3(),
             scrollbar_thumb_hover_background: neutral().light_alpha().step_4(),
+            scrollbar_thumb_active_background: neutral().light_alpha().step_5(),
             scrollbar_thumb_border: gpui::transparent_black(),
             scrollbar_track_background: gpui::transparent_black(),
             scrollbar_track_border: neutral().light().step_5(),
@@ -206,6 +207,7 @@ impl ThemeColors {
             pane_group_border: neutral().dark().step_6(),
             scrollbar_thumb_background: neutral().dark_alpha().step_3(),
             scrollbar_thumb_hover_background: neutral().dark_alpha().step_4(),
+            scrollbar_thumb_active_background: neutral().dark_alpha().step_5(),
             scrollbar_thumb_border: gpui::transparent_black(),
             scrollbar_track_background: gpui::transparent_black(),
             scrollbar_track_border: neutral().dark().step_5(),

crates/theme/src/fallback_themes.rs 🔗

@@ -190,6 +190,12 @@ pub(crate) fn zed_default_dark() -> Theme {
                 pane_group_border: hsla(225. / 360., 13. / 100., 12. / 100., 1.),
                 scrollbar_thumb_background: gpui::transparent_black(),
                 scrollbar_thumb_hover_background: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 1.0),
+                scrollbar_thumb_active_background: hsla(
+                    225.0 / 360.,
+                    11.8 / 100.,
+                    26.7 / 100.,
+                    1.0,
+                ),
                 scrollbar_thumb_border: hsla(228. / 360., 8. / 100., 25. / 100., 1.),
                 scrollbar_track_background: gpui::transparent_black(),
                 scrollbar_track_border: hsla(228. / 360., 8. / 100., 25. / 100., 1.),

crates/theme/src/schema.rs 🔗

@@ -358,6 +358,10 @@ pub struct ThemeColorsContent {
     #[serde(rename = "scrollbar.thumb.hover_background")]
     pub scrollbar_thumb_hover_background: Option<String>,
 
+    /// The color of the scrollbar thumb whilst being actively dragged.
+    #[serde(rename = "scrollbar.thumb.active_background")]
+    pub scrollbar_thumb_active_background: Option<String>,
+
     /// The border color of the scrollbar thumb.
     #[serde(rename = "scrollbar.thumb.border")]
     pub scrollbar_thumb_border: Option<String>,
@@ -622,6 +626,15 @@ impl ThemeColorsContent {
             .editor_document_highlight_read_background
             .as_ref()
             .and_then(|color| try_parse_color(color).ok());
+        let scrollbar_thumb_background = self
+            .scrollbar_thumb_background
+            .as_ref()
+            .and_then(|color| try_parse_color(color).ok())
+            .or_else(|| {
+                self.deprecated_scrollbar_thumb_background
+                    .as_ref()
+                    .and_then(|color| try_parse_color(color).ok())
+            });
         ThemeColorsRefinement {
             border,
             border_variant: self
@@ -805,19 +818,16 @@ impl ThemeColorsContent {
                 .as_ref()
                 .and_then(|color| try_parse_color(color).ok())
                 .or(border),
-            scrollbar_thumb_background: self
-                .scrollbar_thumb_background
-                .as_ref()
-                .and_then(|color| try_parse_color(color).ok())
-                .or_else(|| {
-                    self.deprecated_scrollbar_thumb_background
-                        .as_ref()
-                        .and_then(|color| try_parse_color(color).ok())
-                }),
+            scrollbar_thumb_background,
             scrollbar_thumb_hover_background: self
                 .scrollbar_thumb_hover_background
                 .as_ref()
                 .and_then(|color| try_parse_color(color).ok()),
+            scrollbar_thumb_active_background: self
+                .scrollbar_thumb_active_background
+                .as_ref()
+                .and_then(|color| try_parse_color(color).ok())
+                .or(scrollbar_thumb_background),
             scrollbar_thumb_border: self
                 .scrollbar_thumb_border
                 .as_ref()

crates/theme/src/styles/colors.rs 🔗

@@ -135,6 +135,8 @@ pub struct ThemeColors {
     pub scrollbar_thumb_background: Hsla,
     /// The color of the scrollbar thumb when hovered over.
     pub scrollbar_thumb_hover_background: Hsla,
+    /// The color of the scrollbar thumb whilst being actively dragged.
+    pub scrollbar_thumb_active_background: Hsla,
     /// The border color of the scrollbar thumb.
     pub scrollbar_thumb_border: Hsla,
     /// The background color of the scrollbar track.
@@ -321,6 +323,7 @@ pub enum ThemeColorField {
     PaneGroupBorder,
     ScrollbarThumbBackground,
     ScrollbarThumbHoverBackground,
+    ScrollbarThumbActiveBackground,
     ScrollbarThumbBorder,
     ScrollbarTrackBackground,
     ScrollbarTrackBorder,
@@ -428,6 +431,9 @@ impl ThemeColors {
             ThemeColorField::PaneGroupBorder => self.pane_group_border,
             ThemeColorField::ScrollbarThumbBackground => self.scrollbar_thumb_background,
             ThemeColorField::ScrollbarThumbHoverBackground => self.scrollbar_thumb_hover_background,
+            ThemeColorField::ScrollbarThumbActiveBackground => {
+                self.scrollbar_thumb_active_background
+            }
             ThemeColorField::ScrollbarThumbBorder => self.scrollbar_thumb_border,
             ThemeColorField::ScrollbarTrackBackground => self.scrollbar_track_background,
             ThemeColorField::ScrollbarTrackBorder => self.scrollbar_track_border,

crates/theme_importer/src/vscode/converter.rs 🔗

@@ -167,6 +167,10 @@ impl VsCodeThemeConverter {
                 .scrollbar_slider
                 .hover_background
                 .clone(),
+            scrollbar_thumb_active_background: vscode_colors
+                .scrollbar_slider
+                .active_background
+                .clone(),
             scrollbar_thumb_border: vscode_scrollbar_slider_background.clone(),
             scrollbar_track_background: vscode_editor_background.clone(),
             scrollbar_track_border: vscode_colors.editor_overview_ruler.border.clone(),