Add cursor affinity
This commit is contained in:
parent
e00109d77f
commit
da842ec10d
2 changed files with 66 additions and 33 deletions
|
|
@ -19,12 +19,24 @@ pub struct Cursor {
|
|||
pub line: usize,
|
||||
/// First-byte-index of glyph at cursor (will insert behind this glyph)
|
||||
pub index: usize,
|
||||
/// Whether to associate the cursor with the run before it (false) or the run after it (true)
|
||||
/// if placed at the boundary between two runs
|
||||
pub affinity: bool,
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
/// Create a new cursor
|
||||
pub const fn new(line: usize, index: usize) -> Self {
|
||||
Self { line, index }
|
||||
Self::new_affinity(line, index, false)
|
||||
}
|
||||
|
||||
/// Create a new cursor, specifying the affinity
|
||||
pub const fn new_affinity(line: usize, index: usize, affinity: bool) -> Self {
|
||||
Self {
|
||||
line,
|
||||
index,
|
||||
affinity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +73,7 @@ pub struct LayoutRun<'a> {
|
|||
}
|
||||
|
||||
impl<'a> LayoutRun<'a> {
|
||||
/// Return the pixel span Some((x_start, x_width)) of the highlighted area between cursor_start
|
||||
/// Return the pixel span Some((x_left, x_width)) of the highlighted area between cursor_start
|
||||
/// and cursor_end within this run, or None if the cursor range does not intersect this run.
|
||||
/// This may return widths of zero if cursor_start == cursor_end, if the run is empty, or if the
|
||||
/// region's left start boundary is the same as the cursor's end boundary or vice versa.
|
||||
|
|
@ -71,28 +83,20 @@ impl<'a> LayoutRun<'a> {
|
|||
let rtl_factor = if self.rtl { 1. } else { 0. };
|
||||
let ltr_factor = 1. - rtl_factor;
|
||||
for glyph in self.glyphs.iter() {
|
||||
let cursor = Cursor::new(self.line_i, glyph.start);
|
||||
let cursor = self.cursor_from_glyph_left(glyph);
|
||||
if cursor >= cursor_start && cursor <= cursor_end {
|
||||
if x_start.is_none() {
|
||||
x_start = Some(glyph.x + glyph.w * rtl_factor);
|
||||
}
|
||||
x_end = Some(glyph.x + glyph.w * rtl_factor);
|
||||
}
|
||||
}
|
||||
let cursor = Cursor::new(self.line_i, self.glyphs.last().map_or(0, |glyph| glyph.end));
|
||||
if cursor >= cursor_start && cursor <= cursor_end {
|
||||
if x_start.is_none() {
|
||||
x_start = Some(
|
||||
self.glyphs
|
||||
.last()
|
||||
.map_or(0., |glyph| glyph.x + glyph.w * ltr_factor),
|
||||
);
|
||||
let cursor = self.cursor_from_glyph_right(glyph);
|
||||
if cursor >= cursor_start && cursor <= cursor_end {
|
||||
if x_start.is_none() {
|
||||
x_start = Some(glyph.x + glyph.w * ltr_factor);
|
||||
}
|
||||
x_end = Some(glyph.x + glyph.w * ltr_factor);
|
||||
}
|
||||
x_end = Some(
|
||||
self.glyphs
|
||||
.last()
|
||||
.map_or(0., |glyph| glyph.x + glyph.w * ltr_factor),
|
||||
);
|
||||
}
|
||||
if let Some(x_start) = x_start {
|
||||
let x_end = x_end.expect("end of cursor not found");
|
||||
|
|
@ -106,6 +110,22 @@ impl<'a> LayoutRun<'a> {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor {
|
||||
if self.rtl {
|
||||
Cursor::new_affinity(self.line_i, glyph.end, false)
|
||||
} else {
|
||||
Cursor::new_affinity(self.line_i, glyph.start, true)
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor {
|
||||
if self.rtl {
|
||||
Cursor::new_affinity(self.line_i, glyph.start, true)
|
||||
} else {
|
||||
Cursor::new_affinity(self.line_i, glyph.end, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator of visible text lines, see [`LayoutRun`]
|
||||
|
|
@ -378,18 +398,18 @@ impl<'a> Buffer<'a> {
|
|||
let layout = line.layout_opt().as_ref().expect("layout not found");
|
||||
for (layout_i, layout_line) in layout.iter().enumerate() {
|
||||
for (glyph_i, glyph) in layout_line.glyphs.iter().enumerate() {
|
||||
if cursor.index == glyph.start {
|
||||
let cursor_end = Cursor::new_affinity(cursor.line, glyph.end, false);
|
||||
let cursor_start = Cursor::new_affinity(cursor.line, glyph.start, true);
|
||||
let (cursor_left, cursor_right) = if glyph.level.is_ltr() {
|
||||
(cursor_start, cursor_end)
|
||||
} else {
|
||||
(cursor_end, cursor_start)
|
||||
};
|
||||
if *cursor == cursor_left {
|
||||
return LayoutCursor::new(cursor.line, layout_i, glyph_i);
|
||||
}
|
||||
}
|
||||
match layout_line.glyphs.last() {
|
||||
Some(glyph) => {
|
||||
if cursor.index == glyph.end {
|
||||
return LayoutCursor::new(cursor.line, layout_i, layout_line.glyphs.len());
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return LayoutCursor::new(cursor.line, layout_i, 0);
|
||||
if *cursor == cursor_right {
|
||||
return LayoutCursor::new(cursor.line, layout_i, glyph_i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -537,6 +557,7 @@ impl<'a> Buffer<'a> {
|
|||
} else if y >= line_y - font_size && y < line_y - font_size + line_height {
|
||||
let mut new_cursor_glyph = run.glyphs.len();
|
||||
let mut new_cursor_char = 0;
|
||||
let mut new_cursor_affinity = true;
|
||||
|
||||
let mut first_glyph = true;
|
||||
|
||||
|
|
@ -563,6 +584,7 @@ impl<'a> Buffer<'a> {
|
|||
if right_half != glyph.level.is_rtl() {
|
||||
// If clicking on last half of glyph, move cursor past glyph
|
||||
new_cursor_char += egc.len();
|
||||
new_cursor_affinity = false;
|
||||
}
|
||||
break 'hit;
|
||||
}
|
||||
|
|
@ -573,6 +595,7 @@ impl<'a> Buffer<'a> {
|
|||
if right_half != glyph.level.is_rtl() {
|
||||
// If clicking on last half of glyph, move cursor past glyph
|
||||
new_cursor_char = cluster.len();
|
||||
new_cursor_affinity = false;
|
||||
}
|
||||
break 'hit;
|
||||
}
|
||||
|
|
@ -584,11 +607,13 @@ impl<'a> Buffer<'a> {
|
|||
Some(glyph) => {
|
||||
// Position at glyph
|
||||
new_cursor.index = glyph.start + new_cursor_char;
|
||||
new_cursor.affinity = new_cursor_affinity;
|
||||
}
|
||||
None => {
|
||||
if let Some(glyph) = run.glyphs.last() {
|
||||
// Position at end of line
|
||||
new_cursor.index = glyph.end;
|
||||
new_cursor.affinity = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -599,7 +624,7 @@ impl<'a> Buffer<'a> {
|
|||
} else if runs.peek().is_none() && y > run.line_y {
|
||||
let mut new_cursor = Cursor::new(run.line_i, 0);
|
||||
if let Some(glyph) = run.glyphs.last() {
|
||||
new_cursor.index = glyph.end;
|
||||
new_cursor = run.cursor_from_glyph_right(glyph);
|
||||
}
|
||||
new_cursor_opt = Some(new_cursor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,18 +44,22 @@ impl<'a> Editor<'a> {
|
|||
},
|
||||
};
|
||||
|
||||
let new_index = match layout_line.glyphs.get(cursor.glyph) {
|
||||
Some(glyph) => glyph.start,
|
||||
let (new_index, new_affinity) = match layout_line.glyphs.get(cursor.glyph) {
|
||||
Some(glyph) => (glyph.start, true),
|
||||
None => match layout_line.glyphs.last() {
|
||||
Some(glyph) => glyph.end,
|
||||
Some(glyph) => (glyph.end, false),
|
||||
//TODO: is this correct?
|
||||
None => 0,
|
||||
None => (0, true),
|
||||
},
|
||||
};
|
||||
|
||||
if self.cursor.line != cursor.line || self.cursor.index != new_index {
|
||||
if self.cursor.line != cursor.line
|
||||
|| self.cursor.index != new_index
|
||||
|| self.cursor.affinity != new_affinity
|
||||
{
|
||||
self.cursor.line = cursor.line;
|
||||
self.cursor.index = new_index;
|
||||
self.cursor.affinity = new_affinity;
|
||||
self.buffer.set_redraw(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -290,10 +294,12 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
}
|
||||
|
||||
self.cursor.index = prev_index;
|
||||
self.cursor.affinity = true;
|
||||
self.buffer.set_redraw(true);
|
||||
} else if self.cursor.line > 0 {
|
||||
self.cursor.line -= 1;
|
||||
self.cursor.index = self.buffer.lines[self.cursor.line].text().len();
|
||||
self.cursor.affinity = true;
|
||||
self.buffer.set_redraw(true);
|
||||
}
|
||||
self.cursor_x_opt = None;
|
||||
|
|
@ -304,6 +310,7 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
for (i, c) in line.text().grapheme_indices(true) {
|
||||
if i == self.cursor.index {
|
||||
self.cursor.index += c.len();
|
||||
self.cursor.affinity = false;
|
||||
self.buffer.set_redraw(true);
|
||||
break;
|
||||
}
|
||||
|
|
@ -311,6 +318,7 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
} else if self.cursor.line + 1 < self.buffer.lines.len() {
|
||||
self.cursor.line += 1;
|
||||
self.cursor.index = 0;
|
||||
self.cursor.affinity = false;
|
||||
self.buffer.set_redraw(true);
|
||||
}
|
||||
self.cursor_x_opt = None;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue