From eedcc585afd921779c3ccc1aef24fc258647c0b9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 10 Oct 2022 16:12:42 -0700 Subject: [PATCH 1/9] Add scrollbars to editors --- crates/editor/src/element.rs | 118 ++++++++++++++++++++++++++++++--- crates/theme/src/theme.rs | 8 +++ styles/src/styleTree/editor.ts | 14 ++++ 3 files changed, 130 insertions(+), 10 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index acf2e5887c..a15f5d0013 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -43,7 +43,7 @@ use std::{ cmp::{self, Ordering}, fmt::Write, iter, - ops::Range, + ops::{DerefMut, Range}, sync::Arc, }; use theme::DiffStyle; @@ -909,6 +909,96 @@ impl EditorElement { cx.scene.pop_layer(); } + fn paint_scrollbar(&mut self, bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContext) { + enum ScrollbarMouseHandlers {} + let row_range = if let Some(row_range) = &layout.scrollbar_row_range { + row_range + } else { + return; + }; + + let style = &self.style.theme.scrollbar; + let top = bounds.min_y(); + let bottom = bounds.max_y(); + let height = bounds.height(); + + let max_row = layout.max_row + row_range.len() as u32; + let scrollbar_start = row_range.start as f32 / max_row as f32; + let scrollbar_end = row_range.end as f32 / max_row as f32; + + let thumb_top = top + scrollbar_start * height; + let thumb_bottom = top + scrollbar_end * height; + let right = bounds.max_x(); + let left = right - style.width; + let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); + let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); + + cx.scene.push_quad(Quad { + bounds: track_bounds, + border: style.track.border, + background: style.track.background_color, + ..Default::default() + }); + cx.scene.push_quad(Quad { + bounds: thumb_bounds, + border: style.thumb.border, + background: style.thumb.background_color, + corner_radius: style.thumb.corner_radius, + }); + cx.scene.push_cursor_region(CursorRegion { + bounds: track_bounds, + style: CursorStyle::Arrow, + }); + + let view = self.view.clone(); + cx.scene.push_mouse_region( + MouseRegion::new::( + self.view.id(), + self.view.id(), + track_bounds, + ) + .on_down(MouseButton::Left, { + let view = view.clone(); + let row_range_len = row_range.len(); + move |e, cx| { + let y = e.position.y(); + if y < thumb_top || thumb_bottom < y { + if let Some(view) = view.upgrade(cx.deref_mut()) { + view.update(cx.deref_mut(), |view, cx| { + let center_row = + ((y - top) * max_row as f32 / height).round() as u32; + let top_row = center_row.saturating_sub(row_range_len as u32 / 2); + let mut position = view.scroll_position(cx); + position.set_y(top_row as f32); + view.set_scroll_position(position, cx); + }); + } + } + } + }) + .on_drag(MouseButton::Left, { + let view = view.clone(); + move |e, cx| { + let y = e.prev_mouse_position.y(); + let new_y = e.position.y(); + if thumb_top < y && y < thumb_bottom { + if let Some(view) = view.upgrade(cx.deref_mut()) { + view.update(cx.deref_mut(), |view, cx| { + let mut position = view.scroll_position(cx); + position + .set_y(position.y() + (new_y - y) * (max_row as f32) / height); + if position.y() < 0.0 { + position.set_y(0.); + } + view.set_scroll_position(position, cx); + }); + } + } + } + }), + ); + } + #[allow(clippy::too_many_arguments)] fn paint_highlighted_range( &self, @@ -1467,13 +1557,11 @@ impl Element for EditorElement { // The scroll position is a fractional point, the whole number of which represents // the top of the window in terms of display rows. let start_row = scroll_position.y() as u32; - let scroll_top = scroll_position.y() * line_height; + let visible_row_count = (size.y() / line_height).ceil() as u32; + let max_row = snapshot.max_point().row(); // Add 1 to ensure selections bleed off screen - let end_row = 1 + cmp::min( - ((scroll_top + size.y()) / line_height).ceil() as u32, - snapshot.max_point().row(), - ); + let end_row = 1 + cmp::min(start_row + visible_row_count, max_row); let start_anchor = if start_row == 0 { Anchor::min() @@ -1482,7 +1570,7 @@ impl Element for EditorElement { .buffer_snapshot .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left)) }; - let end_anchor = if end_row > snapshot.max_point().row() { + let end_anchor = if end_row > max_row { Anchor::max() } else { snapshot @@ -1564,6 +1652,12 @@ impl Element for EditorElement { .git_diff_hunks_in_range(start_row..end_row) .collect(); + let scrollbar_row_range = if snapshot.mode == EditorMode::Full { + Some(start_row..(start_row + visible_row_count)) + } else { + None + }; + let mut max_visible_line_width = 0.0; let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx); for line in &line_layouts { @@ -1597,7 +1691,6 @@ impl Element for EditorElement { cx, ); - let max_row = snapshot.max_point().row(); let scroll_max = vec2f( ((scroll_width - text_size.x()) / em_width).max(0.0), max_row.saturating_sub(1) as f32, @@ -1707,6 +1800,8 @@ impl Element for EditorElement { gutter_size, gutter_padding, text_size, + scrollbar_row_range, + max_row, gutter_margin, active_rows, highlighted_rows, @@ -1753,11 +1848,12 @@ impl Element for EditorElement { } self.paint_text(text_bounds, visible_bounds, layout, cx); + cx.scene.push_layer(Some(bounds)); if !layout.blocks.is_empty() { - cx.scene.push_layer(Some(bounds)); self.paint_blocks(bounds, visible_bounds, layout, cx); - cx.scene.pop_layer(); } + self.paint_scrollbar(bounds, layout, cx); + cx.scene.pop_layer(); cx.scene.pop_layer(); } @@ -1849,6 +1945,8 @@ pub struct LayoutState { blocks: Vec, highlighted_ranges: Vec<(Range, Color)>, selections: Vec<(ReplicaId, Vec)>, + scrollbar_row_range: Option>, + max_row: u32, context_menu: Option<(DisplayPoint, ElementBox)>, diff_hunks: Vec>, code_actions_indicator: Option<(u32, ElementBox)>, diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index d8c8296481..7bd7913644 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -510,6 +510,14 @@ pub struct Editor { pub link_definition: HighlightStyle, pub composition_mark: HighlightStyle, pub jump_icon: Interactive, + pub scrollbar: Scrollbar, +} + +#[derive(Clone, Deserialize, Default)] +pub struct Scrollbar { + pub track: ContainerStyle, + pub thumb: ContainerStyle, + pub width: f32, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 04a5bafbd5..610f828ec8 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -1,4 +1,5 @@ import Theme from "../themes/common/theme"; +import { withOpacity } from "../utils/color"; import { backgroundColor, border, @@ -170,6 +171,19 @@ export default function editor(theme: Theme) { background: backgroundColor(theme, "on500"), }, }, + scrollbar: { + width: 12, + track: { + border: { + left: true, + width: 1, + color: borderColor(theme, "secondary"), + }, + }, + thumb: { + background: borderColor(theme, "secondary"), + } + }, compositionMark: { underline: { thickness: 1.0, From 6dcf63832213b9f3a64e87756cf23563e82656de Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 10 Oct 2022 17:06:48 -0700 Subject: [PATCH 2/9] Represent scrollbar range with f32s --- crates/editor/src/element.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a15f5d0013..300edc02a6 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -922,7 +922,7 @@ impl EditorElement { let bottom = bounds.max_y(); let height = bounds.height(); - let max_row = layout.max_row + row_range.len() as u32; + let max_row = layout.max_row + ((row_range.end - row_range.start) as u32); let scrollbar_start = row_range.start as f32 / max_row as f32; let scrollbar_end = row_range.end as f32 / max_row as f32; @@ -959,7 +959,7 @@ impl EditorElement { ) .on_down(MouseButton::Left, { let view = view.clone(); - let row_range_len = row_range.len(); + let row_range_len = row_range.end - row_range.start; move |e, cx| { let y = e.position.y(); if y < thumb_top || thumb_bottom < y { @@ -1653,7 +1653,7 @@ impl Element for EditorElement { .collect(); let scrollbar_row_range = if snapshot.mode == EditorMode::Full { - Some(start_row..(start_row + visible_row_count)) + Some(scroll_position.y()..(scroll_position.y() + visible_row_count as f32)) } else { None }; @@ -1945,7 +1945,7 @@ pub struct LayoutState { blocks: Vec, highlighted_ranges: Vec<(Range, Color)>, selections: Vec<(ReplicaId, Vec)>, - scrollbar_row_range: Option>, + scrollbar_row_range: Option>, max_row: u32, context_menu: Option<(DisplayPoint, ElementBox)>, diff_hunks: Vec>, From 7b084199be8d81f5771a392e8f5836f33bfa0c89 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 10 Oct 2022 17:54:29 -0700 Subject: [PATCH 3/9] Auto-hide scrollbars --- crates/editor/src/editor.rs | 28 ++++++++ crates/editor/src/element.rs | 130 ++++++++++++++++++++--------------- 2 files changed, 101 insertions(+), 57 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1abe65d482..3422e599f8 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -76,6 +76,7 @@ use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ItemNavHistory, Workspace}; const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); +const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1); const MAX_LINE_LEN: usize = 1024; const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10; const MAX_SELECTION_HISTORY_LEN: usize = 1024; @@ -427,6 +428,8 @@ pub struct Editor { focused: bool, show_local_cursors: bool, show_local_selections: bool, + show_scrollbars: bool, + hide_scrollbar_task: Option>, blink_epoch: usize, blinking_paused: bool, mode: EditorMode, @@ -1028,6 +1031,8 @@ impl Editor { focused: false, show_local_cursors: false, show_local_selections: true, + show_scrollbars: true, + hide_scrollbar_task: None, blink_epoch: 0, blinking_paused: false, mode, @@ -1059,6 +1064,7 @@ impl Editor { ], }; this.end_selection(cx); + this.make_scrollbar_visible(cx); let editor_created_event = EditorCreated(cx.handle()); cx.emit_global(editor_created_event); @@ -1179,6 +1185,7 @@ impl Editor { self.scroll_top_anchor = anchor; } + self.make_scrollbar_visible(cx); self.autoscroll_request.take(); hide_hover(self, cx); @@ -5932,6 +5939,27 @@ impl Editor { self.show_local_cursors && self.focused } + pub fn show_scrollbars(&self) -> bool { + self.show_scrollbars + } + + fn make_scrollbar_visible(&mut self, cx: &mut ViewContext) { + if !self.show_scrollbars { + self.show_scrollbars = true; + cx.notify(); + } + + self.hide_scrollbar_task = Some(cx.spawn_weak(|this, mut cx| async move { + Timer::after(SCROLLBAR_SHOW_INTERVAL).await; + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.show_scrollbars = false; + cx.notify(); + }); + } + })); + } + fn on_buffer_changed(&mut self, _: ModelHandle, cx: &mut ViewContext) { cx.notify(); } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 300edc02a6..66dbe50864 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -454,7 +454,6 @@ impl EditorElement { let bounds = gutter_bounds.union_rect(text_bounds); let scroll_top = layout.position_map.snapshot.scroll_position().y() * layout.position_map.line_height; - let editor = self.view(cx.app); cx.scene.push_quad(Quad { bounds: gutter_bounds, background: Some(self.style.gutter_background), @@ -468,7 +467,7 @@ impl EditorElement { corner_radius: 0., }); - if let EditorMode::Full = editor.mode { + if let EditorMode::Full = layout.mode { let mut active_rows = layout.active_rows.iter().peekable(); while let Some((start_row, contains_non_empty_selection)) = active_rows.next() { let mut end_row = *start_row; @@ -911,25 +910,24 @@ impl EditorElement { fn paint_scrollbar(&mut self, bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContext) { enum ScrollbarMouseHandlers {} - let row_range = if let Some(row_range) = &layout.scrollbar_row_range { - row_range - } else { + if layout.mode != EditorMode::Full { return; - }; + } + let view = self.view.clone(); let style = &self.style.theme.scrollbar; let top = bounds.min_y(); let bottom = bounds.max_y(); + let right = bounds.max_x(); + let left = right - style.width; let height = bounds.height(); - + let row_range = &layout.scrollbar_row_range; let max_row = layout.max_row + ((row_range.end - row_range.start) as u32); let scrollbar_start = row_range.start as f32 / max_row as f32; let scrollbar_end = row_range.end as f32 / max_row as f32; - let thumb_top = top + scrollbar_start * height; let thumb_bottom = top + scrollbar_end * height; - let right = bounds.max_x(); - let left = right - style.width; + let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); @@ -939,63 +937,75 @@ impl EditorElement { background: style.track.background_color, ..Default::default() }); - cx.scene.push_quad(Quad { - bounds: thumb_bounds, - border: style.thumb.border, - background: style.thumb.background_color, - corner_radius: style.thumb.corner_radius, - }); + if layout.show_scrollbars { + cx.scene.push_quad(Quad { + bounds: thumb_bounds, + border: style.thumb.border, + background: style.thumb.background_color, + corner_radius: style.thumb.corner_radius, + }); + } + cx.scene.push_cursor_region(CursorRegion { bounds: track_bounds, style: CursorStyle::Arrow, }); - - let view = self.view.clone(); cx.scene.push_mouse_region( - MouseRegion::new::( - self.view.id(), - self.view.id(), - track_bounds, - ) - .on_down(MouseButton::Left, { - let view = view.clone(); - let row_range_len = row_range.end - row_range.start; - move |e, cx| { - let y = e.position.y(); - if y < thumb_top || thumb_bottom < y { + MouseRegion::new::(view.id(), view.id(), track_bounds) + .on_move({ + let view = view.clone(); + move |_, cx| { if let Some(view) = view.upgrade(cx.deref_mut()) { view.update(cx.deref_mut(), |view, cx| { - let center_row = - ((y - top) * max_row as f32 / height).round() as u32; - let top_row = center_row.saturating_sub(row_range_len as u32 / 2); - let mut position = view.scroll_position(cx); - position.set_y(top_row as f32); - view.set_scroll_position(position, cx); + view.make_scrollbar_visible(cx); }); } } - } - }) - .on_drag(MouseButton::Left, { - let view = view.clone(); - move |e, cx| { - let y = e.prev_mouse_position.y(); - let new_y = e.position.y(); - if thumb_top < y && y < thumb_bottom { + }) + .on_down(MouseButton::Left, { + let view = view.clone(); + let row_range = row_range.clone(); + move |e, cx| { + let y = e.position.y(); if let Some(view) = view.upgrade(cx.deref_mut()) { view.update(cx.deref_mut(), |view, cx| { - let mut position = view.scroll_position(cx); - position - .set_y(position.y() + (new_y - y) * (max_row as f32) / height); - if position.y() < 0.0 { - position.set_y(0.); + if y < thumb_top || thumb_bottom < y { + let center_row = + ((y - top) * max_row as f32 / height).round() as u32; + let top_row = center_row.saturating_sub( + (row_range.end - row_range.start) as u32 / 2, + ); + let mut position = view.scroll_position(cx); + position.set_y(top_row as f32); + view.set_scroll_position(position, cx); + } else { + view.make_scrollbar_visible(cx); } - view.set_scroll_position(position, cx); }); } } - } - }), + }) + .on_drag(MouseButton::Left, { + let view = view.clone(); + move |e, cx| { + let y = e.prev_mouse_position.y(); + let new_y = e.position.y(); + if thumb_top < y && y < thumb_bottom { + if let Some(view) = view.upgrade(cx.deref_mut()) { + view.update(cx.deref_mut(), |view, cx| { + let mut position = view.scroll_position(cx); + position.set_y( + position.y() + (new_y - y) * (max_row as f32) / height, + ); + if position.y() < 0.0 { + position.set_y(0.); + } + view.set_scroll_position(position, cx); + }); + } + } + } + }), ); } @@ -1582,6 +1592,7 @@ impl Element for EditorElement { let mut active_rows = BTreeMap::new(); let mut highlighted_rows = None; let mut highlighted_ranges = Vec::new(); + let mut show_scrollbars = false; self.update_view(cx.app, |view, cx| { let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx)); @@ -1642,6 +1653,8 @@ impl Element for EditorElement { .collect(), )); } + + show_scrollbars = view.show_scrollbars(); }); let line_number_layouts = @@ -1652,11 +1665,8 @@ impl Element for EditorElement { .git_diff_hunks_in_range(start_row..end_row) .collect(); - let scrollbar_row_range = if snapshot.mode == EditorMode::Full { - Some(scroll_position.y()..(scroll_position.y() + visible_row_count as f32)) - } else { - None - }; + let scrollbar_row_range = + scroll_position.y()..(scroll_position.y() + visible_row_count as f32); let mut max_visible_line_width = 0.0; let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx); @@ -1720,6 +1730,7 @@ impl Element for EditorElement { let mut context_menu = None; let mut code_actions_indicator = None; let mut hover = None; + let mut mode = EditorMode::Full; cx.render(&self.view.upgrade(cx).unwrap(), |view, cx| { let newest_selection_head = view .selections @@ -1741,6 +1752,7 @@ impl Element for EditorElement { let visible_rows = start_row..start_row + line_layouts.len() as u32; hover = view.hover_state.render(&snapshot, &style, visible_rows, cx); + mode = view.mode; }); if let Some((_, context_menu)) = context_menu.as_mut() { @@ -1788,6 +1800,7 @@ impl Element for EditorElement { ( size, LayoutState { + mode, position_map: Arc::new(PositionMap { size, scroll_max, @@ -1801,6 +1814,7 @@ impl Element for EditorElement { gutter_padding, text_size, scrollbar_row_range, + show_scrollbars, max_row, gutter_margin, active_rows, @@ -1939,13 +1953,15 @@ pub struct LayoutState { gutter_padding: f32, gutter_margin: f32, text_size: Vector2F, + mode: EditorMode, active_rows: BTreeMap, highlighted_rows: Option>, line_number_layouts: Vec>, blocks: Vec, highlighted_ranges: Vec<(Range, Color)>, selections: Vec<(ReplicaId, Vec)>, - scrollbar_row_range: Option>, + scrollbar_row_range: Range, + show_scrollbars: bool, max_row: u32, context_menu: Option<(DisplayPoint, ElementBox)>, diff_hunks: Vec>, From b229bc69b986108630ebbbb52e1dda949bfc5a24 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 10 Oct 2022 17:54:40 -0700 Subject: [PATCH 4/9] Tweak scrollbar styling --- styles/src/styleTree/editor.ts | 8 ++++++-- styles/src/themes/common/base16.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 610f828ec8..a31b3d8654 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -172,7 +172,7 @@ export default function editor(theme: Theme) { }, }, scrollbar: { - width: 12, + width: 14, track: { border: { left: true, @@ -181,7 +181,11 @@ export default function editor(theme: Theme) { }, }, thumb: { - background: borderColor(theme, "secondary"), + background: withOpacity(borderColor(theme, "secondary"), 0.5), + border: { + width: 1, + color: withOpacity(borderColor(theme, 'muted'), 0.5), + } } }, compositionMark: { diff --git a/styles/src/themes/common/base16.ts b/styles/src/themes/common/base16.ts index cd6d46a771..1c4a5e4076 100644 --- a/styles/src/themes/common/base16.ts +++ b/styles/src/themes/common/base16.ts @@ -123,7 +123,7 @@ export function createTheme( const borderColor = { primary: sample(ramps.neutral, isLight ? 1.5 : 0), secondary: sample(ramps.neutral, isLight ? 1.25 : 1), - muted: sample(ramps.neutral, isLight ? 1 : 3), + muted: sample(ramps.neutral, isLight ? 1.25 : 3), active: sample(ramps.neutral, isLight ? 4 : 3), onMedia: withOpacity(darkest, 0.1), ok: sample(ramps.green, 0.3), From e2700ff8c6d9a3f69520d2c3f65e5f204348aa45 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 10 Oct 2022 18:12:31 -0700 Subject: [PATCH 5/9] Enable/disable scrollbar auto-hide based on OS setting --- crates/editor/src/editor.rs | 29 ++++++++++++++++-------- crates/gpui/src/platform.rs | 1 + crates/gpui/src/platform/mac/platform.rs | 10 ++++++++ crates/gpui/src/platform/test.rs | 4 ++++ 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3422e599f8..2b1d99a24c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -239,6 +239,9 @@ pub enum Direction { Next, } +#[derive(Default)] +struct ScrollbarAutoHide(bool); + pub fn init(cx: &mut MutableAppContext) { cx.add_action(Editor::new_file); cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx)); @@ -327,6 +330,10 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_async_action(Editor::confirm_rename); cx.add_async_action(Editor::find_all_references); + cx.set_global(ScrollbarAutoHide( + cx.platform().should_auto_hide_scrollbars(), + )); + hover_popover::init(cx); link_go_to_definition::init(cx); mouse_context_menu::init(cx); @@ -5949,15 +5956,19 @@ impl Editor { cx.notify(); } - self.hide_scrollbar_task = Some(cx.spawn_weak(|this, mut cx| async move { - Timer::after(SCROLLBAR_SHOW_INTERVAL).await; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - this.show_scrollbars = false; - cx.notify(); - }); - } - })); + if cx.default_global::().0 { + self.hide_scrollbar_task = Some(cx.spawn_weak(|this, mut cx| async move { + Timer::after(SCROLLBAR_SHOW_INTERVAL).await; + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.show_scrollbars = false; + cx.notify(); + }); + } + })); + } else { + self.hide_scrollbar_task = None; + } } fn on_buffer_changed(&mut self, _: ModelHandle, cx: &mut ViewContext) { diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 8997bde527..70d6f1e7d1 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -63,6 +63,7 @@ pub trait Platform: Send + Sync { fn delete_credentials(&self, url: &str) -> Result<()>; fn set_cursor_style(&self, style: CursorStyle); + fn should_auto_hide_scrollbars(&self) -> bool; fn local_timezone(&self) -> UtcOffset; diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 628ddde13c..619ba0f3bf 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -699,6 +699,16 @@ impl platform::Platform for MacPlatform { } } + fn should_auto_hide_scrollbars(&self) -> bool { + #[allow(non_upper_case_globals)] + const NSScrollerStyleOverlay: NSInteger = 1; + + unsafe { + let style: NSInteger = msg_send![class!(NSScroller), preferredScrollerStyle]; + style == NSScrollerStyleOverlay + } + } + fn local_timezone(&self) -> UtcOffset { unsafe { let local_timezone: id = msg_send![class!(NSTimeZone), localTimeZone]; diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 58ef1ffaf2..0dbe011d7b 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -177,6 +177,10 @@ impl super::Platform for Platform { *self.cursor.lock() = style; } + fn should_auto_hide_scrollbars(&self) -> bool { + false + } + fn local_timezone(&self) -> UtcOffset { UtcOffset::UTC } From 67a32de7d427dc48d7a87115022694a1ed34e1ee Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 09:26:19 -0700 Subject: [PATCH 6/9] Hide the scrollbar track, not just the thumb --- crates/editor/src/element.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 66dbe50864..04c67311de 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -931,13 +931,13 @@ impl EditorElement { let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); - cx.scene.push_quad(Quad { - bounds: track_bounds, - border: style.track.border, - background: style.track.background_color, - ..Default::default() - }); if layout.show_scrollbars { + cx.scene.push_quad(Quad { + bounds: track_bounds, + border: style.track.border, + background: style.track.background_color, + ..Default::default() + }); cx.scene.push_quad(Quad { bounds: thumb_bounds, border: style.thumb.border, From d93e75bf5f119bbb6cc0c6ed08e1ae42398e4b16 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 09:26:31 -0700 Subject: [PATCH 7/9] Make scrollbars a little bit narrower --- styles/src/styleTree/editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index a31b3d8654..334931a981 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -172,7 +172,7 @@ export default function editor(theme: Theme) { }, }, scrollbar: { - width: 14, + width: 12, track: { border: { left: true, From f4306d977f87bfe918524a18ade3e04469d0fc74 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 09:28:17 -0700 Subject: [PATCH 8/9] Refresh scrollbar auto-hide setting when opening a new editor --- crates/editor/src/editor.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2b1d99a24c..8df8a29360 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -330,10 +330,6 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_async_action(Editor::confirm_rename); cx.add_async_action(Editor::find_all_references); - cx.set_global(ScrollbarAutoHide( - cx.platform().should_auto_hide_scrollbars(), - )); - hover_popover::init(cx); link_go_to_definition::init(cx); mouse_context_menu::init(cx); @@ -1076,6 +1072,11 @@ impl Editor { let editor_created_event = EditorCreated(cx.handle()); cx.emit_global(editor_created_event); + if mode == EditorMode::Full { + let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars(); + cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars)); + } + this.report_event("open editor", cx); this } From acc85ad03c229ab8e3a6886adad81bfc2c76d4bb Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 13:18:33 -0700 Subject: [PATCH 9/9] Impose a minimum height on the scrollbar --- crates/editor/src/element.rs | 22 ++++++++++++++++++++-- crates/theme/src/theme.rs | 1 + styles/src/styleTree/editor.ts | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 04c67311de..5a8ea0d820 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -916,6 +916,9 @@ impl EditorElement { let view = self.view.clone(); let style = &self.style.theme.scrollbar; + let min_thumb_height = + style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size); + let top = bounds.min_y(); let bottom = bounds.max_y(); let right = bounds.max_x(); @@ -925,8 +928,23 @@ impl EditorElement { let max_row = layout.max_row + ((row_range.end - row_range.start) as u32); let scrollbar_start = row_range.start as f32 / max_row as f32; let scrollbar_end = row_range.end as f32 / max_row as f32; - let thumb_top = top + scrollbar_start * height; - let thumb_bottom = top + scrollbar_end * height; + + let mut thumb_top = top + scrollbar_start * height; + let mut thumb_bottom = top + scrollbar_end * height; + let thumb_center = (thumb_top + thumb_bottom) / 2.0; + + if thumb_bottom - thumb_top < min_thumb_height { + thumb_top = thumb_center - min_thumb_height / 2.0; + thumb_bottom = thumb_center + min_thumb_height / 2.0; + if thumb_top < top { + thumb_top = top; + thumb_bottom = top + min_thumb_height; + } + if thumb_bottom > bottom { + thumb_bottom = bottom; + thumb_top = bottom - min_thumb_height; + } + } let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 7bd7913644..e1a1817b5c 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -518,6 +518,7 @@ pub struct Scrollbar { pub track: ContainerStyle, pub thumb: ContainerStyle, pub width: f32, + pub min_height_factor: f32, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 334931a981..6a333930db 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -173,6 +173,7 @@ export default function editor(theme: Theme) { }, scrollbar: { width: 12, + minHeightFactor: 1.0, track: { border: { left: true,