Detailed changes
@@ -1565,11 +1565,13 @@ impl EditorElement {
.map(|vertical_scrollbar| vertical_scrollbar.hitbox.origin)
.unwrap_or_else(|| editor_bounds.top_right());
+ let thumb_state = self
+ .editor
+ .read_with(cx, |editor, _| editor.scroll_manager.minimap_thumb_state());
+
let show_thumb = match minimap_settings.thumb {
MinimapThumb::Always => true,
- MinimapThumb::Hover => self.editor.update(cx, |editor, _| {
- editor.scroll_manager.minimap_thumb_visible()
- }),
+ MinimapThumb::Hover => thumb_state.is_some(),
};
let minimap_bounds = Bounds::from_corner_and_size(
@@ -1610,7 +1612,8 @@ impl EditorElement {
scroll_position,
minimap_scroll_top,
show_thumb,
- );
+ )
+ .with_thumb_state(thumb_state);
minimap_editor.update(cx, |editor, cx| {
editor.set_scroll_position(point(0., minimap_scroll_top), window, cx)
@@ -5703,10 +5706,7 @@ impl EditorElement {
.get_hovered_axis(window)
.filter(|_| !event.dragging())
{
- if layout
- .thumb_bounds
- .is_some_and(|bounds| bounds.contains(&event.position))
- {
+ if layout.thumb_hovered(&event.position) {
editor
.scroll_manager
.set_hovered_scroll_thumb_axis(axis, cx);
@@ -6115,6 +6115,17 @@ impl EditorElement {
window.with_element_namespace("minimap", |window| {
layout.minimap.paint(window, cx);
if let Some(thumb_bounds) = layout.thumb_layout.thumb_bounds {
+ let minimap_thumb_color = match layout.thumb_layout.thumb_state {
+ ScrollbarThumbState::Idle => {
+ cx.theme().colors().minimap_thumb_background
+ }
+ ScrollbarThumbState::Hovered => {
+ cx.theme().colors().minimap_thumb_hover_background
+ }
+ ScrollbarThumbState::Dragging => {
+ cx.theme().colors().minimap_thumb_active_background
+ }
+ };
let minimap_thumb_border = match layout.thumb_border_style {
MinimapThumbBorder::Full => Edges::all(ScrollbarLayout::BORDER_WIDTH),
MinimapThumbBorder::LeftOnly => Edges {
@@ -6140,9 +6151,9 @@ impl EditorElement {
window.paint_quad(quad(
thumb_bounds,
Corners::default(),
- cx.theme().colors().scrollbar_thumb_background,
+ minimap_thumb_color,
minimap_thumb_border,
- cx.theme().colors().scrollbar_thumb_border,
+ cx.theme().colors().minimap_thumb_border,
BorderStyle::Solid,
));
});
@@ -6187,10 +6198,15 @@ impl EditorElement {
}
cx.stop_propagation();
} else {
- editor.scroll_manager.set_is_dragging_minimap(false, cx);
-
if minimap_hitbox.is_hovered(window) {
- editor.scroll_manager.show_minimap_thumb(cx);
+ editor.scroll_manager.set_is_hovering_minimap_thumb(
+ !event.dragging()
+ && layout
+ .thumb_layout
+ .thumb_bounds
+ .is_some_and(|bounds| bounds.contains(&event.position)),
+ cx,
+ );
// Stop hover events from propagating to the
// underlying editor if the minimap hitbox is hovered
@@ -6209,13 +6225,23 @@ impl EditorElement {
if self.editor.read(cx).scroll_manager.is_dragging_minimap() {
window.on_mouse_event({
let editor = self.editor.clone();
- move |_: &MouseUpEvent, phase, _, cx| {
+ move |event: &MouseUpEvent, phase, window, cx| {
if phase == DispatchPhase::Capture {
return;
}
editor.update(cx, |editor, cx| {
- editor.scroll_manager.set_is_dragging_minimap(false, cx);
+ if minimap_hitbox.is_hovered(window) {
+ editor.scroll_manager.set_is_hovering_minimap_thumb(
+ layout
+ .thumb_layout
+ .thumb_bounds
+ .is_some_and(|bounds| bounds.contains(&event.position)),
+ cx,
+ );
+ } else {
+ editor.scroll_manager.hide_minimap_thumb(cx);
+ }
cx.stop_propagation();
});
}
@@ -6254,7 +6280,7 @@ impl EditorElement {
editor.set_scroll_position(scroll_position, window, cx);
}
- editor.scroll_manager.set_is_dragging_minimap(true, cx);
+ editor.scroll_manager.set_is_dragging_minimap(cx);
cx.stop_propagation();
});
}
@@ -8821,10 +8847,6 @@ impl EditorScrollbars {
axis != ScrollbarAxis::Horizontal || viewport_size < scroll_range
})
.map(|(viewport_size, scroll_range)| {
- let thumb_state = scrollbar_state
- .and_then(|state| state.thumb_state_for_axis(axis))
- .unwrap_or(ScrollbarThumbState::Idle);
-
ScrollbarLayout::new(
window.insert_hitbox(scrollbar_bounds_for(axis), false),
viewport_size,
@@ -8833,9 +8855,11 @@ impl EditorScrollbars {
content_offset.along(axis),
scroll_position.along(axis),
show_scrollbars,
- thumb_state,
axis,
)
+ .with_thumb_state(
+ scrollbar_state.and_then(|state| state.thumb_state_for_axis(axis)),
+ )
})
};
@@ -8885,7 +8909,6 @@ impl ScrollbarLayout {
content_offset: Pixels,
scroll_position: f32,
show_thumb: bool,
- thumb_state: ScrollbarThumbState,
axis: ScrollbarAxis,
) -> Self {
let track_bounds = scrollbar_track_hitbox.bounds;
@@ -8902,7 +8925,6 @@ impl ScrollbarLayout {
content_offset,
scroll_position,
show_thumb,
- thumb_state,
axis,
)
}
@@ -8944,7 +8966,6 @@ impl ScrollbarLayout {
track_top_offset,
scroll_position,
show_thumb,
- ScrollbarThumbState::Idle,
ScrollbarAxis::Vertical,
)
}
@@ -8958,7 +8979,6 @@ impl ScrollbarLayout {
content_offset: Pixels,
scroll_position: f32,
show_thumb: bool,
- thumb_state: ScrollbarThumbState,
axis: ScrollbarAxis,
) -> Self {
let text_units_per_page = viewport_size / glyph_space;
@@ -8996,7 +9016,18 @@ impl ScrollbarLayout {
visible_range,
text_unit_size,
thumb_bounds,
- thumb_state,
+ thumb_state: Default::default(),
+ }
+ }
+
+ fn with_thumb_state(self, thumb_state: Option<ScrollbarThumbState>) -> Self {
+ if let Some(thumb_state) = thumb_state {
+ Self {
+ thumb_state,
+ ..self
+ }
+ } else {
+ self
}
}
@@ -9017,6 +9048,11 @@ impl ScrollbarLayout {
)
}
+ fn thumb_hovered(&self, position: &gpui::Point<Pixels>) -> bool {
+ self.thumb_bounds
+ .is_some_and(|bounds| bounds.contains(position))
+ }
+
fn marker_quads_for_ranges(
&self,
row_ranges: impl IntoIterator<Item = ColoredRange<DisplayRow>>,
@@ -123,8 +123,9 @@ impl OngoingScroll {
}
}
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub enum ScrollbarThumbState {
+ #[default]
Idle,
Hovered,
Dragging,
@@ -157,8 +158,7 @@ pub struct ScrollManager {
active_scrollbar: Option<ActiveScrollbarState>,
visible_line_count: Option<f32>,
forbid_vertical_scroll: bool,
- dragging_minimap: bool,
- show_minimap_thumb: bool,
+ minimap_thumb_state: Option<ScrollbarThumbState>,
}
impl ScrollManager {
@@ -174,8 +174,7 @@ impl ScrollManager {
last_autoscroll: None,
visible_line_count: None,
forbid_vertical_scroll: false,
- dragging_minimap: false,
- show_minimap_thumb: false,
+ minimap_thumb_state: None,
}
}
@@ -345,24 +344,6 @@ impl ScrollManager {
self.show_scrollbars
}
- pub fn show_minimap_thumb(&mut self, cx: &mut Context<Editor>) {
- if !self.show_minimap_thumb {
- self.show_minimap_thumb = true;
- cx.notify();
- }
- }
-
- pub fn hide_minimap_thumb(&mut self, cx: &mut Context<Editor>) {
- if self.show_minimap_thumb {
- self.show_minimap_thumb = false;
- cx.notify();
- }
- }
-
- pub fn minimap_thumb_visible(&mut self) -> bool {
- self.show_minimap_thumb
- }
-
pub fn autoscroll_request(&self) -> Option<Autoscroll> {
self.autoscroll_request.map(|(autoscroll, _)| autoscroll)
}
@@ -419,13 +400,43 @@ impl ScrollManager {
}
}
+ pub fn set_is_hovering_minimap_thumb(&mut self, hovered: bool, cx: &mut Context<Editor>) {
+ self.update_minimap_thumb_state(
+ Some(if hovered {
+ ScrollbarThumbState::Hovered
+ } else {
+ ScrollbarThumbState::Idle
+ }),
+ cx,
+ );
+ }
+
+ pub fn set_is_dragging_minimap(&mut self, cx: &mut Context<Editor>) {
+ self.update_minimap_thumb_state(Some(ScrollbarThumbState::Dragging), cx);
+ }
+
+ pub fn hide_minimap_thumb(&mut self, cx: &mut Context<Editor>) {
+ self.update_minimap_thumb_state(None, cx);
+ }
+
pub fn is_dragging_minimap(&self) -> bool {
- self.dragging_minimap
+ self.minimap_thumb_state
+ .is_some_and(|state| state == ScrollbarThumbState::Dragging)
}
- pub fn set_is_dragging_minimap(&mut self, dragging: bool, cx: &mut Context<Editor>) {
- self.dragging_minimap = dragging;
- cx.notify();
+ fn update_minimap_thumb_state(
+ &mut self,
+ thumb_state: Option<ScrollbarThumbState>,
+ cx: &mut Context<Editor>,
+ ) {
+ if self.minimap_thumb_state != thumb_state {
+ self.minimap_thumb_state = thumb_state;
+ cx.notify();
+ }
+ }
+
+ pub fn minimap_thumb_state(&self) -> Option<ScrollbarThumbState> {
+ self.minimap_thumb_state
}
pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
@@ -90,6 +90,10 @@ impl ThemeColors {
scrollbar_thumb_border: gpui::transparent_black(),
scrollbar_track_background: gpui::transparent_black(),
scrollbar_track_border: neutral().light().step_5(),
+ minimap_thumb_background: neutral().light_alpha().step_3().alpha(0.7),
+ minimap_thumb_hover_background: neutral().light_alpha().step_4().alpha(0.7),
+ minimap_thumb_active_background: neutral().light_alpha().step_5().alpha(0.7),
+ minimap_thumb_border: gpui::transparent_black(),
editor_foreground: neutral().light().step_12(),
editor_background: neutral().light().step_1(),
editor_gutter_background: neutral().light().step_1(),
@@ -211,6 +215,10 @@ impl ThemeColors {
scrollbar_thumb_border: gpui::transparent_black(),
scrollbar_track_background: gpui::transparent_black(),
scrollbar_track_border: neutral().dark().step_5(),
+ minimap_thumb_background: neutral().dark_alpha().step_3().alpha(0.7),
+ minimap_thumb_hover_background: neutral().dark_alpha().step_4().alpha(0.7),
+ minimap_thumb_active_background: neutral().dark_alpha().step_5().alpha(0.7),
+ minimap_thumb_border: gpui::transparent_black(),
editor_foreground: neutral().dark().step_12(),
editor_background: neutral().dark().step_1(),
editor_gutter_background: neutral().dark().step_1(),
@@ -199,6 +199,10 @@ pub(crate) fn zed_default_dark() -> Theme {
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.),
+ minimap_thumb_background: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 0.7),
+ minimap_thumb_hover_background: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 0.7),
+ minimap_thumb_active_background: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 0.7),
+ minimap_thumb_border: hsla(228. / 360., 8. / 100., 25. / 100., 1.),
editor_foreground: hsla(218. / 360., 14. / 100., 71. / 100., 1.),
link_text_hover: blue,
version_control_added: ADDED_COLOR,
@@ -28,6 +28,18 @@ pub(crate) fn try_parse_color(color: &str) -> Result<Hsla> {
Ok(hsla)
}
+fn ensure_non_opaque(color: Hsla) -> Hsla {
+ const MAXIMUM_OPACITY: f32 = 0.7;
+ if color.a <= MAXIMUM_OPACITY {
+ color
+ } else {
+ Hsla {
+ a: MAXIMUM_OPACITY,
+ ..color
+ }
+ }
+}
+
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AppearanceContent {
@@ -374,6 +386,22 @@ pub struct ThemeColorsContent {
#[serde(rename = "scrollbar.track.border")]
pub scrollbar_track_border: Option<String>,
+ /// The color of the minimap thumb.
+ #[serde(rename = "minimap.thumb.background")]
+ pub minimap_thumb_background: Option<String>,
+
+ /// The color of the minimap thumb when hovered over.
+ #[serde(rename = "minimap.thumb.hover_background")]
+ pub minimap_thumb_hover_background: Option<String>,
+
+ /// The color of the minimap thumb whilst being actively dragged.
+ #[serde(rename = "minimap.thumb.active_background")]
+ pub minimap_thumb_active_background: Option<String>,
+
+ /// The border color of the minimap thumb.
+ #[serde(rename = "minimap.thumb.border")]
+ pub minimap_thumb_border: Option<String>,
+
#[serde(rename = "editor.foreground")]
pub editor_foreground: Option<String>,
@@ -635,6 +663,19 @@ impl ThemeColorsContent {
.as_ref()
.and_then(|color| try_parse_color(color).ok())
});
+ let scrollbar_thumb_hover_background = self
+ .scrollbar_thumb_hover_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok());
+ let scrollbar_thumb_active_background = self
+ .scrollbar_thumb_active_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or(scrollbar_thumb_background);
+ let scrollbar_thumb_border = self
+ .scrollbar_thumb_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok());
ThemeColorsRefinement {
border,
border_variant: self
@@ -819,19 +860,9 @@ impl ThemeColorsContent {
.and_then(|color| try_parse_color(color).ok())
.or(border),
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()
- .and_then(|color| try_parse_color(color).ok()),
+ scrollbar_thumb_hover_background,
+ scrollbar_thumb_active_background,
+ scrollbar_thumb_border,
scrollbar_track_background: self
.scrollbar_track_background
.as_ref()
@@ -840,6 +871,26 @@ impl ThemeColorsContent {
.scrollbar_track_border
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
+ minimap_thumb_background: self
+ .minimap_thumb_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or(scrollbar_thumb_background.map(ensure_non_opaque)),
+ minimap_thumb_hover_background: self
+ .minimap_thumb_hover_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or(scrollbar_thumb_hover_background.map(ensure_non_opaque)),
+ minimap_thumb_active_background: self
+ .minimap_thumb_active_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or(scrollbar_thumb_active_background.map(ensure_non_opaque)),
+ minimap_thumb_border: self
+ .minimap_thumb_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or(scrollbar_thumb_border),
editor_foreground: self
.editor_foreground
.as_ref()
@@ -143,6 +143,14 @@ pub struct ThemeColors {
pub scrollbar_track_background: Hsla,
/// The border color of the scrollbar track.
pub scrollbar_track_border: Hsla,
+ /// The color of the minimap thumb.
+ pub minimap_thumb_background: Hsla,
+ /// The color of the minimap thumb when hovered over.
+ pub minimap_thumb_hover_background: Hsla,
+ /// The color of the minimap thumb whilst being actively dragged.
+ pub minimap_thumb_active_background: Hsla,
+ /// The border color of the minimap thumb.
+ pub minimap_thumb_border: Hsla,
// ===
// Editor
@@ -327,6 +335,10 @@ pub enum ThemeColorField {
ScrollbarThumbBorder,
ScrollbarTrackBackground,
ScrollbarTrackBorder,
+ MinimapThumbBackground,
+ MinimapThumbHoverBackground,
+ MinimapThumbActiveBackground,
+ MinimapThumbBorder,
EditorForeground,
EditorBackground,
EditorGutterBackground,
@@ -437,6 +449,10 @@ impl ThemeColors {
ThemeColorField::ScrollbarThumbBorder => self.scrollbar_thumb_border,
ThemeColorField::ScrollbarTrackBackground => self.scrollbar_track_background,
ThemeColorField::ScrollbarTrackBorder => self.scrollbar_track_border,
+ ThemeColorField::MinimapThumbBackground => self.minimap_thumb_background,
+ ThemeColorField::MinimapThumbHoverBackground => self.minimap_thumb_hover_background,
+ ThemeColorField::MinimapThumbActiveBackground => self.minimap_thumb_active_background,
+ ThemeColorField::MinimapThumbBorder => self.minimap_thumb_border,
ThemeColorField::EditorForeground => self.editor_foreground,
ThemeColorField::EditorBackground => self.editor_background,
ThemeColorField::EditorGutterBackground => self.editor_gutter_background,
@@ -174,6 +174,7 @@ impl VsCodeThemeConverter {
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(),
+ minimap_thumb_background: vscode_colors.minimap_slider.background.clone(),
editor_foreground: vscode_editor_foreground
.clone()
.or(vscode_token_colors_foreground.clone()),