From d19d3bbe4513c5a6466369f1cfe8c5d48bc79fc9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 24 Nov 2021 10:28:32 +0100 Subject: [PATCH] Calculate an overshoot when mousing to build columnar selections --- crates/editor/src/element.rs | 20 ++++++++---- crates/editor/src/lib.rs | 44 +++++++++++++++++---------- crates/gpui/src/font_cache.rs | 11 +++++++ crates/gpui/src/fonts.rs | 4 +++ crates/gpui/src/platform.rs | 1 + crates/gpui/src/platform/mac/fonts.rs | 8 +++++ 6 files changed, 66 insertions(+), 22 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 544889cb75..489f6f0a40 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -69,10 +69,13 @@ impl EditorElement { } let snapshot = self.snapshot(cx.app); - let position = paint.point_for_position(&snapshot, layout, position); + let (position, overshoot) = paint.point_for_position(&snapshot, layout, position); if shift && alt { - cx.dispatch_action(Select(SelectPhase::BeginColumnar { position })); + cx.dispatch_action(Select(SelectPhase::BeginColumnar { + position, + overshoot, + })); } else if shift { cx.dispatch_action(Select(SelectPhase::Extend { position, @@ -138,10 +141,11 @@ impl EditorElement { let font_cache = cx.font_cache.clone(); let text_layout_cache = cx.text_layout_cache.clone(); let snapshot = self.snapshot(cx.app); - let position = paint.point_for_position(&snapshot, layout, position); + let (position, overshoot) = paint.point_for_position(&snapshot, layout, position); cx.dispatch_action(Select(SelectPhase::Update { position, + overshoot, scroll_position: (snapshot.scroll_position() + scroll_delta).clamp( Vector2F::zero(), layout.scroll_max(&font_cache, &text_layout_cache), @@ -687,6 +691,7 @@ impl Element for EditorElement { let text_width = size.x() - gutter_width; let text_offset = vec2f(-style.text.descent(cx.font_cache), 0.); let em_width = style.text.em_width(cx.font_cache); + let em_advance = style.text.em_advance(cx.font_cache); let overscroll = vec2f(em_width, 0.); let wrap_width = text_width - text_offset.x() - overscroll.x() - em_width; let snapshot = self.update_view(cx.app, |view, cx| { @@ -786,6 +791,7 @@ impl Element for EditorElement { block_layouts, line_height, em_width, + em_advance, selections, max_visible_line_width, }; @@ -914,6 +920,7 @@ pub struct LayoutState { block_layouts: Vec<(Range, BlockStyle)>, line_height: f32, em_width: f32, + em_advance: f32, selections: HashMap>>, overscroll: Vector2F, text_offset: Vector2F, @@ -982,7 +989,7 @@ impl PaintState { snapshot: &Snapshot, layout: &LayoutState, position: Vector2F, - ) -> DisplayPoint { + ) -> (DisplayPoint, u32) { let scroll_position = snapshot.scroll_position(); let position = position - self.text_bounds.origin(); let y = position.y().max(0.0).min(layout.size.y()); @@ -994,12 +1001,13 @@ impl PaintState { let column = if x >= 0.0 { line.index_for_x(x) .map(|ix| ix as u32) - .unwrap_or(snapshot.line_len(row)) + .unwrap_or_else(|| snapshot.line_len(row)) } else { 0 }; + let overshoot = (0f32.max(x - line.width()) / layout.em_advance) as u32; - DisplayPoint::new(row, column) + (DisplayPoint::new(row, column), overshoot) } } diff --git a/crates/editor/src/lib.rs b/crates/editor/src/lib.rs index 5b9603dcbd..93024d6c2f 100644 --- a/crates/editor/src/lib.rs +++ b/crates/editor/src/lib.rs @@ -284,6 +284,7 @@ pub enum SelectPhase { }, BeginColumnar { position: DisplayPoint, + overshoot: u32, }, Extend { position: DisplayPoint, @@ -291,6 +292,7 @@ pub enum SelectPhase { }, Update { position: DisplayPoint, + overshoot: u32, scroll_position: Vector2F, }, End, @@ -661,15 +663,19 @@ impl Editor { add, click_count, } => self.begin_selection(*position, *add, *click_count, cx), - SelectPhase::BeginColumnar { position } => self.begin_columnar_selection(*position, cx), + SelectPhase::BeginColumnar { + position, + overshoot, + } => self.begin_columnar_selection(*position, *overshoot, cx), SelectPhase::Extend { position, click_count, } => self.extend_selection(*position, *click_count, cx), SelectPhase::Update { position, + overshoot, scroll_position, - } => self.update_selection(*position, *scroll_position, cx), + } => self.update_selection(*position, *overshoot, *scroll_position, cx), SelectPhase::End => self.end_selection(cx), } } @@ -780,7 +786,12 @@ impl Editor { cx.notify(); } - fn begin_columnar_selection(&mut self, position: DisplayPoint, cx: &mut ViewContext) { + fn begin_columnar_selection( + &mut self, + position: DisplayPoint, + overshoot: u32, + cx: &mut ViewContext, + ) { if !self.focused { cx.focus_self(); cx.emit(Event::Activate); @@ -795,6 +806,7 @@ impl Editor { self.select_columns( tail.to_display_point(&display_map), position, + overshoot, &display_map, cx, ); @@ -803,16 +815,15 @@ impl Editor { fn update_selection( &mut self, position: DisplayPoint, + overshoot: u32, scroll_position: Vector2F, cx: &mut ViewContext, ) { - log::info!("update selection"); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); if let Some(tail) = self.columnar_selection_tail.as_ref() { let tail = tail.to_display_point(&display_map); - log::info!("columnar tail {:?}", tail); - self.select_columns(tail, position, &display_map, cx); + self.select_columns(tail, position, overshoot, &display_map, cx); } else if let Some(PendingSelection { selection, mode }) = self.pending_selection.as_mut() { let buffer = self.buffer.read(cx); let head; @@ -904,13 +915,14 @@ impl Editor { &mut self, tail: DisplayPoint, head: DisplayPoint, + overshoot: u32, display_map: &DisplayMapSnapshot, cx: &mut ViewContext, ) { let start_row = cmp::min(tail.row(), head.row()); let end_row = cmp::max(tail.row(), head.row()); - let start_column = cmp::min(tail.column(), head.column()); - let end_column = cmp::max(tail.column(), head.column()); + let start_column = cmp::min(tail.column(), head.column() + overshoot); + let end_column = cmp::max(tail.column(), head.column() + overshoot); let selections = (start_row..=end_row) .filter_map(|row| { @@ -3554,7 +3566,7 @@ mod tests { ); editor.update(cx, |view, cx| { - view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), cx); + view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx); }); assert_eq!( @@ -3563,7 +3575,7 @@ mod tests { ); editor.update(cx, |view, cx| { - view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), cx); + view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx); }); assert_eq!( @@ -3573,7 +3585,7 @@ mod tests { editor.update(cx, |view, cx| { view.end_selection(cx); - view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), cx); + view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx); }); assert_eq!( @@ -3583,7 +3595,7 @@ mod tests { editor.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx); - view.update_selection(DisplayPoint::new(0, 0), Vector2F::zero(), cx); + view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx); }); assert_eq!( @@ -3619,7 +3631,7 @@ mod tests { }); view.update(cx, |view, cx| { - view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), cx); + view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx); assert_eq!( view.selection_ranges(cx), [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] @@ -3628,7 +3640,7 @@ mod tests { view.update(cx, |view, cx| { view.cancel(&Cancel, cx); - view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), cx); + view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx); assert_eq!( view.selection_ranges(cx), [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] @@ -3644,11 +3656,11 @@ mod tests { view.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx); - view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), cx); + view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx); view.end_selection(cx); view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx); - view.update_selection(DisplayPoint::new(0, 3), Vector2F::zero(), cx); + view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx); view.end_selection(cx); assert_eq!( view.selection_ranges(cx), diff --git a/crates/gpui/src/font_cache.rs b/crates/gpui/src/font_cache.rs index 0509ecd437..8a6ae62ff9 100644 --- a/crates/gpui/src/font_cache.rs +++ b/crates/gpui/src/font_cache.rs @@ -157,6 +157,17 @@ impl FontCache { bounds.width() * self.em_scale(font_id, font_size) } + pub fn em_advance(&self, font_id: FontId, font_size: f32) -> f32 { + let glyph_id; + let advance; + { + let state = self.0.read(); + glyph_id = state.fonts.glyph_for_char(font_id, 'm').unwrap(); + advance = state.fonts.advance(font_id, glyph_id).unwrap(); + } + advance.x() * self.em_scale(font_id, font_size) + } + pub fn line_height(&self, font_id: FontId, font_size: f32) -> f32 { let height = self.metric(font_id, |m| m.bounding_box.height()); (height * self.em_scale(font_id, font_size)).ceil() diff --git a/crates/gpui/src/fonts.rs b/crates/gpui/src/fonts.rs index 3dbfd66034..6509360a62 100644 --- a/crates/gpui/src/fonts.rs +++ b/crates/gpui/src/fonts.rs @@ -151,6 +151,10 @@ impl TextStyle { font_cache.em_width(self.font_id, self.font_size) } + pub fn em_advance(&self, font_cache: &FontCache) -> f32 { + font_cache.em_advance(self.font_id, self.font_size) + } + pub fn descent(&self, font_cache: &FontCache) -> f32 { font_cache.metric(self.font_id, |m| m.descent) * self.em_scale(font_cache) } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 7a55916043..0e1bb3fca9 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -148,6 +148,7 @@ pub trait FontSystem: Send + Sync { ) -> anyhow::Result; fn font_metrics(&self, font_id: FontId) -> FontMetrics; fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result; + fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result; fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option; fn rasterize_glyph( &self, diff --git a/crates/gpui/src/platform/mac/fonts.rs b/crates/gpui/src/platform/mac/fonts.rs index c7f03689ee..2453660abc 100644 --- a/crates/gpui/src/platform/mac/fonts.rs +++ b/crates/gpui/src/platform/mac/fonts.rs @@ -69,6 +69,10 @@ impl platform::FontSystem for FontSystem { self.0.read().typographic_bounds(font_id, glyph_id) } + fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result { + self.0.read().advance(font_id, glyph_id) + } + fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { self.0.read().glyph_for_char(font_id, ch) } @@ -137,6 +141,10 @@ impl FontSystemState { Ok(self.fonts[font_id.0].typographic_bounds(glyph_id)?) } + fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result { + Ok(self.fonts[font_id.0].advance(glyph_id)?) + } + fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { self.fonts[font_id.0].glyph_for_char(ch) }