From 3dd5b3f426aeb8c297bb839980476b0c972bb3a1 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 2 Mar 2023 19:42:59 -0800 Subject: [PATCH 1/3] Attempted to initialize code-fold indicators, does not work --- crates/editor/src/display_map.rs | 1 - crates/editor/src/editor.rs | 52 +++++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 229ecc34fe..38d5242321 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -643,7 +643,6 @@ impl DisplaySnapshot { break; } } - let end = end.unwrap_or(max_point); Some(start..end) } else { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index cb9bf4da6c..643dd5b951 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1196,6 +1196,19 @@ impl Editor { if mode == EditorMode::Full { let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars(); cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars)); + + // TODO: this does not work at all + let display_snapshot = this.snapshot(cx).display_snapshot; + let editor_snapshot = this.snapshot(cx); + if buffer.read(cx).is_singleton() { + this.insert_fold_styles( + display_snapshot + .folds_in_range(Anchor::min()..Anchor::max()) + .cloned(), + &editor_snapshot, + cx, + ) + } } this.report_event("open editor", cx); @@ -5865,26 +5878,35 @@ impl Editor { let snapshot = self.snapshot(cx); let anchor_ranges = offset_to_anchors(ranges, &snapshot); - self.change_click_ranges::(cx, |click_ranges| { - for range in anchor_ranges { - if let Err(idx) = click_ranges.binary_search_by(|click_range| { - click_range.cmp(&range, &snapshot.buffer_snapshot) - }) { - click_ranges.insert(idx, range) - } - } - }); - let click_ranges = self.clone_click_ranges::(); - self.highlight_background::( - click_ranges, - |theme| theme.editor.document_highlight_write_background, - cx, - ); + self.insert_fold_styles(anchor_ranges, &snapshot, cx); cx.notify(); } } + fn insert_fold_styles( + &mut self, + anchor_ranges: impl Iterator>, + snapshot: &EditorSnapshot, + cx: &mut ViewContext, + ) { + self.change_click_ranges::(cx, |click_ranges| { + for range in anchor_ranges { + if let Err(idx) = click_ranges.binary_search_by(|click_range| { + click_range.cmp(&range, &snapshot.buffer_snapshot) + }) { + click_ranges.insert(idx, range) + } + } + }); + let click_ranges = self.clone_click_ranges::(); + self.highlight_background::( + click_ranges, + |theme| theme.editor.document_highlight_write_background, + cx, + ); + } + pub fn unfold_ranges( &mut self, ranges: impl IntoIterator>, From 7ca0b38048f270f64b8da90f54b7df4bbf64f8f5 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 3 Mar 2023 09:32:54 -0800 Subject: [PATCH 2/3] Made fold inline styles be driven by the fold map co-authored-by: Max --- crates/editor/src/editor.rs | 165 +---------------------------------- crates/editor/src/element.rs | 54 +++++------- 2 files changed, 23 insertions(+), 196 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 643dd5b951..8a3acef996 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -458,8 +458,6 @@ type CompletionId = usize; type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor; type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option; -type TextClickedCallback = - fn(&MouseClick, &Range, &EditorSnapshot, &mut EventContext); pub struct Editor { handle: WeakViewHandle, @@ -488,7 +486,6 @@ pub struct Editor { highlighted_rows: Option>, #[allow(clippy::type_complexity)] background_highlights: BTreeMap Color, Vec>)>, - clickable_text: BTreeMap>)>, nav_history: Option, context_menu: Option, mouse_context_menu: ViewHandle, @@ -1159,7 +1156,6 @@ impl Editor { placeholder_text: None, highlighted_rows: None, background_highlights: Default::default(), - clickable_text: Default::default(), nav_history: None, context_menu: None, mouse_context_menu: cx.add_view(context_menu::ContextMenu::new), @@ -1196,19 +1192,6 @@ impl Editor { if mode == EditorMode::Full { let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars(); cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars)); - - // TODO: this does not work at all - let display_snapshot = this.snapshot(cx).display_snapshot; - let editor_snapshot = this.snapshot(cx); - if buffer.read(cx).is_singleton() { - this.insert_fold_styles( - display_snapshot - .folds_in_range(Anchor::min()..Anchor::max()) - .cloned(), - &editor_snapshot, - cx, - ) - } } this.report_event("open editor", cx); @@ -5866,47 +5849,16 @@ impl Editor { ) { let mut ranges = ranges.into_iter().peekable(); if ranges.peek().is_some() { - let ranges = ranges.collect_vec(); - - self.display_map - .update(cx, |map, cx| map.fold(ranges.iter().cloned(), cx)); + self.display_map.update(cx, |map, cx| map.fold(ranges, cx)); if auto_scroll { self.request_autoscroll(Autoscroll::fit(), cx); } - let snapshot = self.snapshot(cx); - let anchor_ranges = offset_to_anchors(ranges, &snapshot); - - self.insert_fold_styles(anchor_ranges, &snapshot, cx); - cx.notify(); } } - fn insert_fold_styles( - &mut self, - anchor_ranges: impl Iterator>, - snapshot: &EditorSnapshot, - cx: &mut ViewContext, - ) { - self.change_click_ranges::(cx, |click_ranges| { - for range in anchor_ranges { - if let Err(idx) = click_ranges.binary_search_by(|click_range| { - click_range.cmp(&range, &snapshot.buffer_snapshot) - }) { - click_ranges.insert(idx, range) - } - } - }); - let click_ranges = self.clone_click_ranges::(); - self.highlight_background::( - click_ranges, - |theme| theme.editor.document_highlight_write_background, - cx, - ); - } - pub fn unfold_ranges( &mut self, ranges: impl IntoIterator>, @@ -5916,41 +5868,12 @@ impl Editor { ) { let mut ranges = ranges.into_iter().peekable(); if ranges.peek().is_some() { - let ranges = ranges.collect_vec(); - - self.display_map.update(cx, |map, cx| { - map.unfold(ranges.iter().cloned(), inclusive, cx) - }); + self.display_map + .update(cx, |map, cx| map.unfold(ranges, inclusive, cx)); if auto_scroll { self.request_autoscroll(Autoscroll::fit(), cx); } - let snapshot = self.snapshot(cx); - let anchor_ranges = offset_to_anchors(ranges, &snapshot); - - self.change_click_ranges::(cx, |click_ranges| { - for range in anchor_ranges { - let range_point = range.start.to_point(&snapshot.buffer_snapshot); - // Fold and unfold ranges start at different points in the row. - // But their rows do match, so we can use that to detect sameness. - if let Ok(idx) = click_ranges.binary_search_by(|click_range| { - click_range - .start - .to_point(&snapshot.buffer_snapshot) - .row - .cmp(&range_point.row) - }) { - click_ranges.remove(idx); - } - } - }); - let click_ranges = self.clone_click_ranges::(); - self.highlight_background::( - click_ranges, - |theme| theme.editor.document_highlight_write_background, - cx, - ); - cx.notify(); } } @@ -6076,69 +5999,6 @@ impl Editor { } } - // FIXME: Consolidate the range styling APIs so that this clone isn't nescessary - pub fn clone_click_ranges(&self) -> Vec> { - self.clickable_text - .get(&TypeId::of::()) - .map(|click_range| click_range.1.clone()) - .unwrap_or_default() - } - - pub fn change_click_ranges( - &mut self, - cx: &mut ViewContext, - change: impl FnOnce(&mut Vec>), - ) { - let mut ranges = self - .clickable_text - .remove(&TypeId::of::()) - .map(|click_range| click_range.1) - .unwrap_or_default(); - - change(&mut ranges); - - self.clickable_text - .insert(TypeId::of::(), (T::click_handler, ranges)); - - cx.notify(); - } - - pub fn click_ranges_in_range( - &self, - search_range: Range, - display_snapshot: &DisplaySnapshot, - ) -> Vec<(Range, TextClickedCallback)> { - let mut results = Vec::new(); - let buffer = &display_snapshot.buffer_snapshot; - for (callback, ranges) in self.clickable_text.values() { - let start_ix = match ranges.binary_search_by(|probe| { - let cmp = probe.end.cmp(&search_range.start, buffer); - if cmp.is_gt() { - Ordering::Greater - } else { - Ordering::Less - } - }) { - Ok(i) | Err(i) => i, - }; - for range in &ranges[start_ix..] { - if range.start.cmp(&search_range.end, buffer).is_ge() { - break; - } - let start = range - .start - .to_point(buffer) - .to_display_point(display_snapshot); - let end = range - .end - .to_point(buffer) - .to_display_point(display_snapshot); - results.push((start..end, *callback)) - } - } - results - } - pub fn highlight_rows(&mut self, rows: Option>) { self.highlighted_rows = rows; } @@ -6517,25 +6377,6 @@ fn ending_row(next_selection: &Selection, display_map: &DisplaySnapshot) } } -fn offset_to_anchors< - 'snapshot, - 'iter: 'snapshot, - T: ToOffset, - I: IntoIterator> + 'iter, ->( - ranges: I, - snapshot: &'snapshot EditorSnapshot, -) -> impl Iterator> + 'snapshot { - ranges.into_iter().map(|range| { - snapshot - .buffer_snapshot - .anchor_at(range.start.to_offset(&snapshot.buffer_snapshot), Bias::Left) - ..snapshot - .buffer_snapshot - .anchor_at(range.end.to_offset(&snapshot.buffer_snapshot), Bias::Right) - }) -} - impl EditorSnapshot { pub fn language_at(&self, position: T) -> Option<&Arc> { self.display_snapshot.buffer_snapshot.language_at(position) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 239be52461..fac90f2507 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -14,7 +14,7 @@ use crate::{ }, mouse_context_menu::DeployMouseContextMenu, scroll::actions::Scroll, - EditorStyle, GutterHover, TextClickedCallback, UnfoldAt, + EditorStyle, GutterHover, UnfoldAt, }; use clock::ReplicaId; use collections::{BTreeMap, HashMap}; @@ -30,7 +30,6 @@ use gpui::{ }, json::{self, ToJson}, platform::CursorStyle, - scene::MouseClick, text_layout::{self, Line, RunStyle, TextLayoutCache}, AppContext, Axis, Border, CursorRegion, Element, ElementBox, EventContext, LayoutContext, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent, MouseRegion, MutableAppContext, @@ -116,7 +115,7 @@ impl EditorElement { fn attach_mouse_handlers( view: &WeakViewHandle, position_map: &Arc, - click_ranges: Arc, TextClickedCallback)>>, + fold_ranges: Arc<[Range]>, has_popovers: bool, visible_bounds: RectF, text_bounds: RectF, @@ -220,10 +219,12 @@ impl EditorElement { let point = position_to_display_point(e.position, text_bounds, &position_map); if let Some(point) = point { - for (range, callback) in click_ranges.iter() { + for range in fold_ranges.iter() { // Range -> RangeInclusive if range.contains(&point) || range.end == point { - callback(&e, range, &position_map.snapshot, cx) + cx.dispatch_action(UnfoldAt { + display_row: point.row(), + }) } } } @@ -723,7 +724,7 @@ impl EditorElement { }, }); - for (range, _) in layout.click_ranges.iter() { + for range in layout.fold_ranges.iter() { for bound in range_to_bounds( range, content_origin, @@ -1676,7 +1677,7 @@ impl Element for EditorElement { let mut active_rows = BTreeMap::new(); let mut highlighted_rows = None; let mut highlighted_ranges = Vec::new(); - let mut click_ranges = Vec::new(); + let mut fold_ranges = Vec::new(); let mut show_scrollbars = false; let mut include_root = false; let mut is_singleton = false; @@ -1689,7 +1690,15 @@ impl Element for EditorElement { let theme = cx.global::().theme.as_ref(); highlighted_ranges = view.background_highlights_in_range(start_anchor..end_anchor, &display_map, theme); - click_ranges = view.click_ranges_in_range(start_anchor..end_anchor, &display_map); + + fold_ranges.extend( + snapshot + .folds_in_range(start_anchor..end_anchor) + .map(|anchor| { + anchor.start.to_display_point(&snapshot.display_snapshot) + ..anchor.end.to_display_point(&snapshot.display_snapshot) + }), + ); let mut remote_selections = HashMap::default(); for (replica_id, line_mode, cursor_shape, selection) in display_map @@ -1946,7 +1955,7 @@ impl Element for EditorElement { active_rows, highlighted_rows, highlighted_ranges, - click_ranges: Arc::new(click_ranges), + fold_ranges: fold_ranges.into(), line_number_layouts, display_hunks, blocks, @@ -1978,7 +1987,7 @@ impl Element for EditorElement { Self::attach_mouse_handlers( &self.view, &layout.position_map, - layout.click_ranges.clone(), // No need to clone the vec + layout.fold_ranges.clone(), // No need to clone the vec layout.hover_popovers.is_some(), visible_bounds, text_bounds, @@ -2076,7 +2085,7 @@ pub struct LayoutState { display_hunks: Vec, blocks: Vec, highlighted_ranges: Vec<(Range, Color)>, - click_ranges: Arc, TextClickedCallback)>>, + fold_ranges: Arc<[Range]>, selections: Vec<(ReplicaId, Vec)>, scrollbar_row_range: Range, show_scrollbars: bool, @@ -2383,29 +2392,6 @@ impl HighlightedRange { } } -pub trait ClickRange: 'static { - fn click_handler( - click: &MouseClick, - range: &Range, - snapshot: &EditorSnapshot, - cx: &mut EventContext, - ); -} - -pub enum FoldMarker {} -impl ClickRange for FoldMarker { - fn click_handler( - _click: &MouseClick, - range: &Range, - _snapshot: &EditorSnapshot, - cx: &mut EventContext, - ) { - cx.dispatch_action(UnfoldAt { - display_row: range.start.row(), - }) - } -} - pub fn position_to_display_point( position: Vector2F, text_bounds: RectF, From 606d683f29585ea8ab1760d98666693a4824373b Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 3 Mar 2023 12:26:29 -0800 Subject: [PATCH 3/3] Add interactable fold markers Change fold handlers to be driven by the fold map Switch to a mouse region based implementation for click regions Co-authored-by: Max --- crates/editor/src/display_map.rs | 5 ++ crates/editor/src/display_map/fold_map.rs | 25 ++++++- crates/editor/src/editor.rs | 6 +- crates/editor/src/element.rs | 82 ++++++++++++++++------- crates/gpui/src/app.rs | 8 +-- crates/gpui/src/presenter.rs | 24 ++++++- crates/theme/src/theme.rs | 16 +++-- styles/src/styleTree/editor.ts | 16 +++++ 8 files changed, 140 insertions(+), 42 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 38d5242321..500f3626e3 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -8,6 +8,7 @@ use block_map::{BlockMap, BlockPoint}; use collections::{HashMap, HashSet}; use fold_map::FoldMap; use gpui::{ + color::Color, fonts::{FontId, HighlightStyle}, Entity, ModelContext, ModelHandle, }; @@ -218,6 +219,10 @@ impl DisplayMap { .update(cx, |map, cx| map.set_font(font_id, font_size, cx)) } + pub fn set_fold_ellipses_color(&mut self, color: Color) -> bool { + self.fold_map.set_ellipses_color(color) + } + pub fn set_wrap_width(&self, width: Option, cx: &mut ModelContext) -> bool { self.wrap_map .update(cx, |map, cx| map.set_wrap_width(width, cx)) diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 44de95fe32..6323461987 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -4,7 +4,7 @@ use crate::{ ToOffset, }; use collections::BTreeMap; -use gpui::fonts::HighlightStyle; +use gpui::{color::Color, fonts::HighlightStyle}; use language::{Chunk, Edit, Point, TextSummary}; use parking_lot::Mutex; use std::{ @@ -133,6 +133,7 @@ impl<'a> FoldMapWriter<'a> { folds: self.0.folds.clone(), buffer_snapshot: buffer, version: self.0.version.load(SeqCst), + ellipses_color: self.0.ellipses_color, }; (snapshot, edits) } @@ -182,6 +183,7 @@ impl<'a> FoldMapWriter<'a> { folds: self.0.folds.clone(), buffer_snapshot: buffer, version: self.0.version.load(SeqCst), + ellipses_color: self.0.ellipses_color, }; (snapshot, edits) } @@ -192,6 +194,7 @@ pub struct FoldMap { transforms: Mutex>, folds: SumTree, version: AtomicUsize, + ellipses_color: Option, } impl FoldMap { @@ -209,6 +212,7 @@ impl FoldMap { }, &(), )), + ellipses_color: None, version: Default::default(), }; @@ -217,6 +221,7 @@ impl FoldMap { folds: this.folds.clone(), buffer_snapshot: this.buffer.lock().clone(), version: this.version.load(SeqCst), + ellipses_color: None, }; (this, snapshot) } @@ -233,6 +238,7 @@ impl FoldMap { folds: self.folds.clone(), buffer_snapshot: self.buffer.lock().clone(), version: self.version.load(SeqCst), + ellipses_color: self.ellipses_color, }; (snapshot, edits) } @@ -246,6 +252,15 @@ impl FoldMap { (FoldMapWriter(self), snapshot, edits) } + pub fn set_ellipses_color(&mut self, color: Color) -> bool { + if self.ellipses_color != Some(color) { + self.ellipses_color = Some(color); + true + } else { + false + } + } + fn check_invariants(&self) { if cfg!(test) { assert_eq!( @@ -477,6 +492,7 @@ pub struct FoldSnapshot { folds: SumTree, buffer_snapshot: MultiBufferSnapshot, pub version: usize, + pub ellipses_color: Option, } impl FoldSnapshot { @@ -739,6 +755,7 @@ impl FoldSnapshot { max_output_offset: range.end.0, highlight_endpoints: highlight_endpoints.into_iter().peekable(), active_highlights: Default::default(), + ellipses_color: self.ellipses_color, } } @@ -1029,6 +1046,7 @@ pub struct FoldChunks<'a> { max_output_offset: usize, highlight_endpoints: Peekable>, active_highlights: BTreeMap, HighlightStyle>, + ellipses_color: Option, } impl<'a> Iterator for FoldChunks<'a> { @@ -1058,7 +1076,10 @@ impl<'a> Iterator for FoldChunks<'a> { return Some(Chunk { text: output_text, syntax_highlight_id: None, - highlight_style: None, + highlight_style: self.ellipses_color.map(|color| HighlightStyle { + color: Some(color), + ..Default::default() + }), diagnostic_severity: None, is_unnecessary: false, }); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8a3acef996..3e7a14d2ae 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -39,11 +39,10 @@ use gpui::{ impl_actions, impl_internal_actions, keymap_matcher::KeymapContext, platform::CursorStyle, - scene::MouseClick, serde_json::json, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity, - EventContext, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, - View, ViewContext, ViewHandle, WeakViewHandle, + ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View, + ViewContext, ViewHandle, WeakViewHandle, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HideHover, HoverState}; @@ -6448,6 +6447,7 @@ impl View for Editor { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { let style = self.style(cx); let font_changed = self.display_map.update(cx, |map, cx| { + map.set_fold_ellipses_color(style.folds.ellipses.text_color); map.set_font(style.text.font_id, style.text.font_size, cx) }); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index fac90f2507..f69902e53d 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -50,6 +50,8 @@ use std::{ }; use workspace::item::Item; +enum FoldMarkers {} + struct SelectionLayout { head: DisplayPoint, cursor_shape: CursorShape, @@ -115,7 +117,6 @@ impl EditorElement { fn attach_mouse_handlers( view: &WeakViewHandle, position_map: &Arc, - fold_ranges: Arc<[Range]>, has_popovers: bool, visible_bounds: RectF, text_bounds: RectF, @@ -212,23 +213,6 @@ impl EditorElement { cx.propagate_event() } } - }) - .on_click(MouseButton::Left, { - let position_map = position_map.clone(); - move |e, cx| { - let point = - position_to_display_point(e.position, text_bounds, &position_map); - if let Some(point) = point { - for range in fold_ranges.iter() { - // Range -> RangeInclusive - if range.contains(&point) || range.end == point { - cx.dispatch_action(UnfoldAt { - display_row: point.row(), - }) - } - } - } - } }), ); @@ -724,9 +708,24 @@ impl EditorElement { }, }); - for range in layout.fold_ranges.iter() { + let fold_corner_radius = + self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height; + for (id, range, color) in layout.fold_ranges.iter() { + self.paint_highlighted_range( + range.clone(), + *color, + fold_corner_radius, + fold_corner_radius * 2., + layout, + content_origin, + scroll_top, + scroll_left, + bounds, + cx, + ); + for bound in range_to_bounds( - range, + &range, content_origin, scroll_left, scroll_top, @@ -738,6 +737,16 @@ impl EditorElement { bounds: bound, style: CursorStyle::PointingHand, }); + + let display_row = range.start.row(); + cx.scene.push_mouse_region( + MouseRegion::new::(self.view.id(), *id as usize, bound) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(UnfoldAt { display_row }) + }) + .with_notify_on_hover(true) + .with_notify_on_click(true), + ) } } @@ -757,9 +766,10 @@ impl EditorElement { } let mut cursors = SmallVec::<[Cursor; 32]>::new(); + let corner_radius = 0.15 * layout.position_map.line_height; + for (replica_id, selections) in &layout.selections { let selection_style = style.replica_selection_style(*replica_id); - let corner_radius = 0.15 * layout.position_map.line_height; for selection in selections { self.paint_highlighted_range( @@ -1695,8 +1705,12 @@ impl Element for EditorElement { snapshot .folds_in_range(start_anchor..end_anchor) .map(|anchor| { - anchor.start.to_display_point(&snapshot.display_snapshot) - ..anchor.end.to_display_point(&snapshot.display_snapshot) + let start = anchor.start.to_point(&snapshot.buffer_snapshot); + ( + start.row, + start.to_display_point(&snapshot.display_snapshot) + ..anchor.end.to_display_point(&snapshot), + ) }), ); @@ -1768,6 +1782,21 @@ impl Element for EditorElement { .unwrap_or_default() }); + let fold_ranges: Vec<(BufferRow, Range, Color)> = fold_ranges + .into_iter() + .map(|(id, fold)| { + let color = self + .style + .folds + .ellipses + .background + .style_for(&mut cx.mouse_state::(id as usize), false) + .color; + + (id, fold, color) + }) + .collect(); + let line_number_layouts = self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx); @@ -1955,7 +1984,7 @@ impl Element for EditorElement { active_rows, highlighted_rows, highlighted_ranges, - fold_ranges: fold_ranges.into(), + fold_ranges, line_number_layouts, display_hunks, blocks, @@ -1987,7 +2016,6 @@ impl Element for EditorElement { Self::attach_mouse_handlers( &self.view, &layout.position_map, - layout.fold_ranges.clone(), // No need to clone the vec layout.hover_popovers.is_some(), visible_bounds, text_bounds, @@ -2071,6 +2099,8 @@ impl Element for EditorElement { } } +type BufferRow = u32; + pub struct LayoutState { position_map: Arc, gutter_size: Vector2F, @@ -2085,7 +2115,7 @@ pub struct LayoutState { display_hunks: Vec, blocks: Vec, highlighted_ranges: Vec<(Range, Color)>, - fold_ranges: Arc<[Range]>, + fold_ranges: Vec<(BufferRow, Range, Color)>, selections: Vec<(ReplicaId, Vec)>, scrollbar_row_range: Range, show_scrollbars: bool, diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 35c91f1ff2..31563010b7 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -4159,10 +4159,10 @@ pub struct RenderContext<'a, T: View> { #[derive(Debug, Clone, Default)] pub struct MouseState { - hovered: bool, - clicked: Option, - accessed_hovered: bool, - accessed_clicked: bool, + pub(crate) hovered: bool, + pub(crate) clicked: Option, + pub(crate) accessed_hovered: bool, + pub(crate) accessed_clicked: bool, } impl MouseState { diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index c0785e11f3..5ffed579cf 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -12,9 +12,9 @@ use crate::{ text_layout::TextLayoutCache, Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, Appearance, AssetCache, ElementBox, Entity, FontSystem, ModelHandle, MouseButton, MouseMovedEvent, - MouseRegion, MouseRegionId, ParentId, ReadModel, ReadView, RenderContext, RenderParams, - SceneBuilder, UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle, - WeakViewHandle, + MouseRegion, MouseRegionId, MouseState, ParentId, ReadModel, ReadView, RenderContext, + RenderParams, SceneBuilder, UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, + WeakModelHandle, WeakViewHandle, }; use anyhow::bail; use collections::{HashMap, HashSet}; @@ -603,6 +603,24 @@ pub struct LayoutContext<'a> { } impl<'a> LayoutContext<'a> { + pub fn mouse_state(&self, region_id: usize) -> MouseState { + let view_id = self.view_stack.last().unwrap(); + + let region_id = MouseRegionId::new::(*view_id, region_id); + MouseState { + hovered: self.hovered_region_ids.contains(®ion_id), + clicked: self.clicked_region_ids.as_ref().and_then(|(ids, button)| { + if ids.contains(®ion_id) { + Some(*button) + } else { + None + } + }), + accessed_hovered: false, + accessed_clicked: false, + } + } + fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F { let print_error = |view_id| { format!( diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 06122b9dda..5e8b5dcbcd 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -632,16 +632,22 @@ pub struct FieldEditor { pub selection: SelectionStyle, } +#[derive(Clone, Deserialize, Default)] +pub struct InteractiveColor { + pub color: Color, +} + #[derive(Clone, Deserialize, Default)] pub struct CodeActions { #[serde(default)] - pub indicator: Interactive, + pub indicator: Interactive, pub vertical_scale: f32, } #[derive(Clone, Deserialize, Default)] pub struct Folds { - pub indicator: Interactive, + pub indicator: Interactive, + pub ellipses: FoldEllipses, pub fold_background: Color, pub icon_width: f32, pub folded_icon: String, @@ -649,8 +655,10 @@ pub struct Folds { } #[derive(Clone, Deserialize, Default)] -pub struct Indicator { - pub color: Color, +pub struct FoldEllipses { + pub text_color: Color, + pub background: Interactive, + pub corner_radius_factor: f32, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index c8334d1cc0..799363a349 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -76,6 +76,22 @@ export default function editor(colorScheme: ColorScheme) { color: foreground(layer, "on"), }, }, + ellipses: { + textColor: colorScheme.ramps.neutral(0.71).hex(), + cornerRadiusFactor: 0.15, + background: { + // Copied from hover_popover highlight + color: colorScheme.ramps.neutral(0.5).alpha(0.0).hex(), + + hover: { + color: colorScheme.ramps.neutral(0.5).alpha(0.5).hex(), + }, + + clicked: { + color: colorScheme.ramps.neutral(0.5).alpha(0.7).hex(), + }, + } + }, foldBackground: foreground(layer, "variant"), }, diff: {