render character under block cursor

This commit is contained in:
Keith Simmons 2022-03-10 13:16:31 -08:00
parent 0d42c85195
commit eddb089f27
3 changed files with 66 additions and 19 deletions

View file

@ -1023,9 +1023,9 @@ impl Editor {
cx.notify(); cx.notify();
} }
pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape) { pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
self.cursor_shape = cursor_shape; self.cursor_shape = cursor_shape;
// TODO: Do we need to notify? cx.notify();
} }
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F { pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {

View file

@ -16,7 +16,7 @@ use gpui::{
PathBuilder, PathBuilder,
}, },
json::{self, ToJson}, json::{self, ToJson},
text_layout::{self, RunStyle, TextLayoutCache}, text_layout::{self, Line, RunStyle, TextLayoutCache},
AppContext, Axis, Border, Element, ElementBox, Event, EventContext, LayoutContext, AppContext, Axis, Border, Element, ElementBox, Event, EventContext, LayoutContext,
MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle, MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
}; };
@ -347,7 +347,7 @@ impl EditorElement {
let mut cursors = SmallVec::<[Cursor; 32]>::new(); let mut cursors = SmallVec::<[Cursor; 32]>::new();
for (replica_id, selections) in &layout.selections { for (replica_id, selections) in &layout.selections {
let style = style.replica_selection_style(*replica_id); let selection_style = style.replica_selection_style(*replica_id);
let corner_radius = 0.15 * layout.line_height; let corner_radius = 0.15 * layout.line_height;
for selection in selections { for selection in selections {
@ -355,7 +355,7 @@ impl EditorElement {
selection.start..selection.end, selection.start..selection.end,
start_row, start_row,
end_row, end_row,
style.selection, selection_style.selection,
corner_radius, corner_radius,
corner_radius * 2., corner_radius * 2.,
layout, layout,
@ -372,24 +372,49 @@ impl EditorElement {
let cursor_row_layout = let cursor_row_layout =
&layout.line_layouts[(cursor_position.row() - start_row) as usize]; &layout.line_layouts[(cursor_position.row() - start_row) as usize];
let cursor_column = cursor_position.column() as usize; let cursor_column = cursor_position.column() as usize;
let cursor_character_x = cursor_row_layout.x_for_index(cursor_column); let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
let mut character_width = let mut block_width =
cursor_row_layout.x_for_index(cursor_column + 1) - cursor_character_x; cursor_row_layout.x_for_index(cursor_column + 1) - cursor_character_x;
// TODO: Is there a better option here for the character size if block_width == 0.0 {
// at the end of the line? block_width = layout.em_width;
// Default to 1/3 the line height
if character_width == 0.0 {
character_width = layout.line_height / 3.0;
} }
let block_text =
if matches!(self.cursor_shape, CursorShape::Block) {
layout.snapshot.chars_at(cursor_position).next().and_then(
|character| {
let font_id =
cursor_row_layout.font_for_index(cursor_column)?;
let text = character.to_string();
Some(cx.text_layout_cache.layout_str(
&text,
cursor_row_layout.font_size(),
&[(
text.len(),
RunStyle {
font_id,
color: style.background,
underline: None,
},
)],
))
},
)
} else {
None
};
let x = cursor_character_x - scroll_left; let x = cursor_character_x - scroll_left;
let y = cursor_position.row() as f32 * layout.line_height - scroll_top; let y = cursor_position.row() as f32 * layout.line_height - scroll_top;
cursors.push(Cursor { cursors.push(Cursor {
color: style.cursor, color: selection_style.cursor,
character_width, block_width,
origin: content_origin + vec2f(x, y), origin: content_origin + vec2f(x, y),
line_height: layout.line_height, line_height: layout.line_height,
shape: self.cursor_shape, shape: self.cursor_shape,
block_text,
}); });
} }
} }
@ -1182,6 +1207,7 @@ fn layout_line(
while !line.is_char_boundary(len) { while !line.is_char_boundary(len) {
len -= 1; len -= 1;
} }
line.truncate(len); line.truncate(len);
} }
@ -1242,16 +1268,17 @@ pub enum CursorShape {
impl Default for CursorShape { impl Default for CursorShape {
fn default() -> Self { fn default() -> Self {
CursorShape::Bar CursorShape::Block
} }
} }
struct Cursor { struct Cursor {
origin: Vector2F, origin: Vector2F,
character_width: f32, block_width: f32,
line_height: f32, line_height: f32,
color: Color, color: Color,
shape: CursorShape, shape: CursorShape,
block_text: Option<Line>,
} }
impl Cursor { impl Cursor {
@ -1259,11 +1286,11 @@ impl Cursor {
let bounds = match self.shape { let bounds = match self.shape {
CursorShape::Bar => RectF::new(self.origin, vec2f(2.0, self.line_height)), CursorShape::Bar => RectF::new(self.origin, vec2f(2.0, self.line_height)),
CursorShape::Block => { CursorShape::Block => {
RectF::new(self.origin, vec2f(self.character_width, self.line_height)) RectF::new(self.origin, vec2f(self.block_width, self.line_height))
} }
CursorShape::Underscore => RectF::new( CursorShape::Underscore => RectF::new(
self.origin + Vector2F::new(0.0, self.line_height - 2.0), self.origin + Vector2F::new(0.0, self.line_height - 2.0),
vec2f(self.character_width, 2.0), vec2f(self.block_width, 2.0),
), ),
}; };
@ -1273,6 +1300,10 @@ impl Cursor {
border: Border::new(0., Color::black()), border: Border::new(0., Color::black()),
corner_radius: 0., corner_radius: 0.,
}); });
if let Some(block_text) = &self.block_text {
block_text.paint(self.origin, bounds, self.line_height, cx);
}
} }
} }

View file

@ -186,7 +186,7 @@ pub struct Run {
pub glyphs: Vec<Glyph>, pub glyphs: Vec<Glyph>,
} }
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct Glyph { pub struct Glyph {
pub id: GlyphId, pub id: GlyphId,
pub position: Vector2F, pub position: Vector2F,
@ -210,10 +210,14 @@ impl Line {
self.layout.width self.layout.width
} }
pub fn font_size(&self) -> f32 {
self.layout.font_size
}
pub fn x_for_index(&self, index: usize) -> f32 { pub fn x_for_index(&self, index: usize) -> f32 {
for run in &self.layout.runs { for run in &self.layout.runs {
for glyph in &run.glyphs { for glyph in &run.glyphs {
if glyph.index == index { if glyph.index >= index {
return glyph.position.x(); return glyph.position.x();
} }
} }
@ -221,6 +225,18 @@ impl Line {
self.layout.width self.layout.width
} }
pub fn font_for_index(&self, index: usize) -> Option<FontId> {
for run in &self.layout.runs {
for glyph in &run.glyphs {
if glyph.index >= index {
return Some(run.font_id);
}
}
}
None
}
pub fn index_for_x(&self, x: f32) -> Option<usize> { pub fn index_for_x(&self, x: f32) -> Option<usize> {
if x >= self.layout.width { if x >= self.layout.width {
None None