diff --git a/src/buffer.rs b/src/buffer.rs index a84919b..298fe28 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -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); } diff --git a/src/edit/editor.rs b/src/edit/editor.rs index 2ddf9fc..60e93e1 100644 --- a/src/edit/editor.rs +++ b/src/edit/editor.rs @@ -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;