Calculate an overshoot when mousing to build columnar selections
This commit is contained in:
parent
2b9db911c7
commit
d19d3bbe45
6 changed files with 66 additions and 22 deletions
|
@ -69,10 +69,13 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
let snapshot = self.snapshot(cx.app);
|
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 {
|
if shift && alt {
|
||||||
cx.dispatch_action(Select(SelectPhase::BeginColumnar { position }));
|
cx.dispatch_action(Select(SelectPhase::BeginColumnar {
|
||||||
|
position,
|
||||||
|
overshoot,
|
||||||
|
}));
|
||||||
} else if shift {
|
} else if shift {
|
||||||
cx.dispatch_action(Select(SelectPhase::Extend {
|
cx.dispatch_action(Select(SelectPhase::Extend {
|
||||||
position,
|
position,
|
||||||
|
@ -138,10 +141,11 @@ impl EditorElement {
|
||||||
let font_cache = cx.font_cache.clone();
|
let font_cache = cx.font_cache.clone();
|
||||||
let text_layout_cache = cx.text_layout_cache.clone();
|
let text_layout_cache = cx.text_layout_cache.clone();
|
||||||
let snapshot = self.snapshot(cx.app);
|
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 {
|
cx.dispatch_action(Select(SelectPhase::Update {
|
||||||
position,
|
position,
|
||||||
|
overshoot,
|
||||||
scroll_position: (snapshot.scroll_position() + scroll_delta).clamp(
|
scroll_position: (snapshot.scroll_position() + scroll_delta).clamp(
|
||||||
Vector2F::zero(),
|
Vector2F::zero(),
|
||||||
layout.scroll_max(&font_cache, &text_layout_cache),
|
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_width = size.x() - gutter_width;
|
||||||
let text_offset = vec2f(-style.text.descent(cx.font_cache), 0.);
|
let text_offset = vec2f(-style.text.descent(cx.font_cache), 0.);
|
||||||
let em_width = style.text.em_width(cx.font_cache);
|
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 overscroll = vec2f(em_width, 0.);
|
||||||
let wrap_width = text_width - text_offset.x() - overscroll.x() - em_width;
|
let wrap_width = text_width - text_offset.x() - overscroll.x() - em_width;
|
||||||
let snapshot = self.update_view(cx.app, |view, cx| {
|
let snapshot = self.update_view(cx.app, |view, cx| {
|
||||||
|
@ -786,6 +791,7 @@ impl Element for EditorElement {
|
||||||
block_layouts,
|
block_layouts,
|
||||||
line_height,
|
line_height,
|
||||||
em_width,
|
em_width,
|
||||||
|
em_advance,
|
||||||
selections,
|
selections,
|
||||||
max_visible_line_width,
|
max_visible_line_width,
|
||||||
};
|
};
|
||||||
|
@ -914,6 +920,7 @@ pub struct LayoutState {
|
||||||
block_layouts: Vec<(Range<u32>, BlockStyle)>,
|
block_layouts: Vec<(Range<u32>, BlockStyle)>,
|
||||||
line_height: f32,
|
line_height: f32,
|
||||||
em_width: f32,
|
em_width: f32,
|
||||||
|
em_advance: f32,
|
||||||
selections: HashMap<ReplicaId, Vec<Range<DisplayPoint>>>,
|
selections: HashMap<ReplicaId, Vec<Range<DisplayPoint>>>,
|
||||||
overscroll: Vector2F,
|
overscroll: Vector2F,
|
||||||
text_offset: Vector2F,
|
text_offset: Vector2F,
|
||||||
|
@ -982,7 +989,7 @@ impl PaintState {
|
||||||
snapshot: &Snapshot,
|
snapshot: &Snapshot,
|
||||||
layout: &LayoutState,
|
layout: &LayoutState,
|
||||||
position: Vector2F,
|
position: Vector2F,
|
||||||
) -> DisplayPoint {
|
) -> (DisplayPoint, u32) {
|
||||||
let scroll_position = snapshot.scroll_position();
|
let scroll_position = snapshot.scroll_position();
|
||||||
let position = position - self.text_bounds.origin();
|
let position = position - self.text_bounds.origin();
|
||||||
let y = position.y().max(0.0).min(layout.size.y());
|
let y = position.y().max(0.0).min(layout.size.y());
|
||||||
|
@ -994,12 +1001,13 @@ impl PaintState {
|
||||||
let column = if x >= 0.0 {
|
let column = if x >= 0.0 {
|
||||||
line.index_for_x(x)
|
line.index_for_x(x)
|
||||||
.map(|ix| ix as u32)
|
.map(|ix| ix as u32)
|
||||||
.unwrap_or(snapshot.line_len(row))
|
.unwrap_or_else(|| snapshot.line_len(row))
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
let overshoot = (0f32.max(x - line.width()) / layout.em_advance) as u32;
|
||||||
|
|
||||||
DisplayPoint::new(row, column)
|
(DisplayPoint::new(row, column), overshoot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,7 @@ pub enum SelectPhase {
|
||||||
},
|
},
|
||||||
BeginColumnar {
|
BeginColumnar {
|
||||||
position: DisplayPoint,
|
position: DisplayPoint,
|
||||||
|
overshoot: u32,
|
||||||
},
|
},
|
||||||
Extend {
|
Extend {
|
||||||
position: DisplayPoint,
|
position: DisplayPoint,
|
||||||
|
@ -291,6 +292,7 @@ pub enum SelectPhase {
|
||||||
},
|
},
|
||||||
Update {
|
Update {
|
||||||
position: DisplayPoint,
|
position: DisplayPoint,
|
||||||
|
overshoot: u32,
|
||||||
scroll_position: Vector2F,
|
scroll_position: Vector2F,
|
||||||
},
|
},
|
||||||
End,
|
End,
|
||||||
|
@ -661,15 +663,19 @@ impl Editor {
|
||||||
add,
|
add,
|
||||||
click_count,
|
click_count,
|
||||||
} => self.begin_selection(*position, *add, *click_count, cx),
|
} => 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 {
|
SelectPhase::Extend {
|
||||||
position,
|
position,
|
||||||
click_count,
|
click_count,
|
||||||
} => self.extend_selection(*position, *click_count, cx),
|
} => self.extend_selection(*position, *click_count, cx),
|
||||||
SelectPhase::Update {
|
SelectPhase::Update {
|
||||||
position,
|
position,
|
||||||
|
overshoot,
|
||||||
scroll_position,
|
scroll_position,
|
||||||
} => self.update_selection(*position, *scroll_position, cx),
|
} => self.update_selection(*position, *overshoot, *scroll_position, cx),
|
||||||
SelectPhase::End => self.end_selection(cx),
|
SelectPhase::End => self.end_selection(cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -780,7 +786,12 @@ impl Editor {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_columnar_selection(&mut self, position: DisplayPoint, cx: &mut ViewContext<Self>) {
|
fn begin_columnar_selection(
|
||||||
|
&mut self,
|
||||||
|
position: DisplayPoint,
|
||||||
|
overshoot: u32,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
if !self.focused {
|
if !self.focused {
|
||||||
cx.focus_self();
|
cx.focus_self();
|
||||||
cx.emit(Event::Activate);
|
cx.emit(Event::Activate);
|
||||||
|
@ -795,6 +806,7 @@ impl Editor {
|
||||||
self.select_columns(
|
self.select_columns(
|
||||||
tail.to_display_point(&display_map),
|
tail.to_display_point(&display_map),
|
||||||
position,
|
position,
|
||||||
|
overshoot,
|
||||||
&display_map,
|
&display_map,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -803,16 +815,15 @@ impl Editor {
|
||||||
fn update_selection(
|
fn update_selection(
|
||||||
&mut self,
|
&mut self,
|
||||||
position: DisplayPoint,
|
position: DisplayPoint,
|
||||||
|
overshoot: u32,
|
||||||
scroll_position: Vector2F,
|
scroll_position: Vector2F,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
log::info!("update selection");
|
|
||||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
|
||||||
if let Some(tail) = self.columnar_selection_tail.as_ref() {
|
if let Some(tail) = self.columnar_selection_tail.as_ref() {
|
||||||
let tail = tail.to_display_point(&display_map);
|
let tail = tail.to_display_point(&display_map);
|
||||||
log::info!("columnar tail {:?}", tail);
|
self.select_columns(tail, position, overshoot, &display_map, cx);
|
||||||
self.select_columns(tail, position, &display_map, cx);
|
|
||||||
} else if let Some(PendingSelection { selection, mode }) = self.pending_selection.as_mut() {
|
} else if let Some(PendingSelection { selection, mode }) = self.pending_selection.as_mut() {
|
||||||
let buffer = self.buffer.read(cx);
|
let buffer = self.buffer.read(cx);
|
||||||
let head;
|
let head;
|
||||||
|
@ -904,13 +915,14 @@ impl Editor {
|
||||||
&mut self,
|
&mut self,
|
||||||
tail: DisplayPoint,
|
tail: DisplayPoint,
|
||||||
head: DisplayPoint,
|
head: DisplayPoint,
|
||||||
|
overshoot: u32,
|
||||||
display_map: &DisplayMapSnapshot,
|
display_map: &DisplayMapSnapshot,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
let start_row = cmp::min(tail.row(), head.row());
|
let start_row = cmp::min(tail.row(), head.row());
|
||||||
let end_row = cmp::max(tail.row(), head.row());
|
let end_row = cmp::max(tail.row(), head.row());
|
||||||
let start_column = cmp::min(tail.column(), head.column());
|
let start_column = cmp::min(tail.column(), head.column() + overshoot);
|
||||||
let end_column = cmp::max(tail.column(), head.column());
|
let end_column = cmp::max(tail.column(), head.column() + overshoot);
|
||||||
|
|
||||||
let selections = (start_row..=end_row)
|
let selections = (start_row..=end_row)
|
||||||
.filter_map(|row| {
|
.filter_map(|row| {
|
||||||
|
@ -3554,7 +3566,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
editor.update(cx, |view, cx| {
|
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!(
|
assert_eq!(
|
||||||
|
@ -3563,7 +3575,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
editor.update(cx, |view, cx| {
|
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!(
|
assert_eq!(
|
||||||
|
@ -3573,7 +3585,7 @@ mod tests {
|
||||||
|
|
||||||
editor.update(cx, |view, cx| {
|
editor.update(cx, |view, cx| {
|
||||||
view.end_selection(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!(
|
assert_eq!(
|
||||||
|
@ -3583,7 +3595,7 @@ mod tests {
|
||||||
|
|
||||||
editor.update(cx, |view, cx| {
|
editor.update(cx, |view, cx| {
|
||||||
view.begin_selection(DisplayPoint::new(3, 3), true, 1, 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!(
|
assert_eq!(
|
||||||
|
@ -3619,7 +3631,7 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
view.update(cx, |view, cx| {
|
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!(
|
assert_eq!(
|
||||||
view.selection_ranges(cx),
|
view.selection_ranges(cx),
|
||||||
[DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
|
[DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
|
||||||
|
@ -3628,7 +3640,7 @@ mod tests {
|
||||||
|
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
view.cancel(&Cancel, 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!(
|
assert_eq!(
|
||||||
view.selection_ranges(cx),
|
view.selection_ranges(cx),
|
||||||
[DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
|
[DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
|
||||||
|
@ -3644,11 +3656,11 @@ mod tests {
|
||||||
|
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
view.begin_selection(DisplayPoint::new(3, 4), false, 1, 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.end_selection(cx);
|
||||||
|
|
||||||
view.begin_selection(DisplayPoint::new(0, 1), true, 1, 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);
|
view.end_selection(cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.selection_ranges(cx),
|
view.selection_ranges(cx),
|
||||||
|
|
|
@ -157,6 +157,17 @@ impl FontCache {
|
||||||
bounds.width() * self.em_scale(font_id, font_size)
|
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 {
|
pub fn line_height(&self, font_id: FontId, font_size: f32) -> f32 {
|
||||||
let height = self.metric(font_id, |m| m.bounding_box.height());
|
let height = self.metric(font_id, |m| m.bounding_box.height());
|
||||||
(height * self.em_scale(font_id, font_size)).ceil()
|
(height * self.em_scale(font_id, font_size)).ceil()
|
||||||
|
|
|
@ -151,6 +151,10 @@ impl TextStyle {
|
||||||
font_cache.em_width(self.font_id, self.font_size)
|
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 {
|
pub fn descent(&self, font_cache: &FontCache) -> f32 {
|
||||||
font_cache.metric(self.font_id, |m| m.descent) * self.em_scale(font_cache)
|
font_cache.metric(self.font_id, |m| m.descent) * self.em_scale(font_cache)
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,6 +148,7 @@ pub trait FontSystem: Send + Sync {
|
||||||
) -> anyhow::Result<FontId>;
|
) -> anyhow::Result<FontId>;
|
||||||
fn font_metrics(&self, font_id: FontId) -> FontMetrics;
|
fn font_metrics(&self, font_id: FontId) -> FontMetrics;
|
||||||
fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<RectF>;
|
fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<RectF>;
|
||||||
|
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Vector2F>;
|
||||||
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
|
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
|
||||||
fn rasterize_glyph(
|
fn rasterize_glyph(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -69,6 +69,10 @@ impl platform::FontSystem for FontSystem {
|
||||||
self.0.read().typographic_bounds(font_id, glyph_id)
|
self.0.read().typographic_bounds(font_id, glyph_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Vector2F> {
|
||||||
|
self.0.read().advance(font_id, glyph_id)
|
||||||
|
}
|
||||||
|
|
||||||
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
|
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
|
||||||
self.0.read().glyph_for_char(font_id, ch)
|
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)?)
|
Ok(self.fonts[font_id.0].typographic_bounds(glyph_id)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Vector2F> {
|
||||||
|
Ok(self.fonts[font_id.0].advance(glyph_id)?)
|
||||||
|
}
|
||||||
|
|
||||||
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
|
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
|
||||||
self.fonts[font_id.0].glyph_for_char(ch)
|
self.fonts[font_id.0].glyph_for_char(ch)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue