diff options
author | Kirill Chibisov <contact@kchibisov.com> | 2024-10-15 11:41:08 +0300 |
---|---|---|
committer | Christian Duerr <contact@christianduerr.com> | 2024-10-15 20:12:21 +0000 |
commit | b56a0e86b7681d0ee8ed239844ecfe92bbf87f7b (patch) | |
tree | 77671ca3e0b1bffb2877566a82a2720c8f874db4 | |
parent | 6b3a85606bf04601dbe41140cea29a4abbac2f78 (diff) | |
download | alacritty-b56a0e86b7681d0ee8ed239844ecfe92bbf87f7b.tar.gz alacritty-b56a0e86b7681d0ee8ed239844ecfe92bbf87f7b.zip |
Implement multi-char cursor highlight
Use `end` of the cursor to draw a `HollowBlock` from `start` to `end`.
When cursor covers only a single character, use `Beam` cursor instead
of `HollowBlock`.
Fixes #8238.
Fixes #7849.
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | alacritty/src/display/content.rs | 27 | ||||
-rw-r--r-- | alacritty/src/display/cursor.rs | 4 | ||||
-rw-r--r-- | alacritty/src/display/mod.rs | 44 | ||||
-rw-r--r-- | alacritty/src/event.rs | 7 |
5 files changed, 49 insertions, 35 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c0634f3..15063ca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Notable changes to the `alacritty_terminal` crate are documented in its to the new `general` section - Moved config option `shell` to `terminal.shell` - `ctrl+shift+u` binding to open links to `ctrl+shift+o` to avoid collisions with IMEs +- Use `Beam` cursor for single char cursor inside the IME preview ### Fixed @@ -48,6 +49,7 @@ Notable changes to the `alacritty_terminal` crate are documented in its - Windows app icon now displays properly in old alt+tab on Windows - Alacritty not being properly activated with startup notify - Invalid URL highlights after terminal scrolling +- Hollow block cursor not spanning multiple chars being edited inside the IME preview ## 0.13.2 diff --git a/alacritty/src/display/content.rs b/alacritty/src/display/content.rs index 0e06b3ca..2fbbdec4 100644 --- a/alacritty/src/display/content.rs +++ b/alacritty/src/display/content.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::num::NonZeroU32; use std::ops::Deref; use std::{cmp, mem}; @@ -134,8 +135,13 @@ impl<'a> RenderableContent<'a> { text_color = self.config.colors.primary.background; } + let width = if cell.flags.contains(Flags::WIDE_CHAR) { + NonZeroU32::new(2).unwrap() + } else { + NonZeroU32::new(1).unwrap() + }; RenderableCursor { - is_wide: cell.flags.contains(Flags::WIDE_CHAR), + width, shape: self.cursor_shape, point: self.cursor_point, cursor_color, @@ -396,7 +402,7 @@ pub struct RenderableCursor { shape: CursorShape, cursor_color: Rgb, text_color: Rgb, - is_wide: bool, + width: NonZeroU32, point: Point<usize>, } @@ -405,15 +411,20 @@ impl RenderableCursor { let shape = CursorShape::Hidden; let cursor_color = Rgb::default(); let text_color = Rgb::default(); - let is_wide = false; + let width = NonZeroU32::new(1).unwrap(); let point = Point::default(); - Self { shape, cursor_color, text_color, is_wide, point } + Self { shape, cursor_color, text_color, width, point } } } impl RenderableCursor { - pub fn new(point: Point<usize>, shape: CursorShape, cursor_color: Rgb, is_wide: bool) -> Self { - Self { shape, cursor_color, text_color: cursor_color, is_wide, point } + pub fn new( + point: Point<usize>, + shape: CursorShape, + cursor_color: Rgb, + width: NonZeroU32, + ) -> Self { + Self { shape, cursor_color, text_color: cursor_color, width, point } } pub fn color(&self) -> Rgb { @@ -424,8 +435,8 @@ impl RenderableCursor { self.shape } - pub fn is_wide(&self) -> bool { - self.is_wide + pub fn width(&self) -> NonZeroU32 { + self.width } pub fn point(&self) -> Point<usize> { diff --git a/alacritty/src/display/cursor.rs b/alacritty/src/display/cursor.rs index 65933ccc..b0e2d6c3 100644 --- a/alacritty/src/display/cursor.rs +++ b/alacritty/src/display/cursor.rs @@ -24,9 +24,7 @@ impl IntoRects for RenderableCursor { let thickness = (thickness * width).round().max(1.); - if self.is_wide() { - width *= 2.; - } + width *= self.width().get() as f32; match self.shape() { CursorShape::Beam => beam(x, y, height, thickness, self.color()), diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs index 51553288..a8501da6 100644 --- a/alacritty/src/display/mod.rs +++ b/alacritty/src/display/mod.rs @@ -874,7 +874,9 @@ impl Display { if self.ime.preedit().is_none() { let fg = config.colors.footer_bar_foreground(); let shape = CursorShape::Underline; - let cursor = RenderableCursor::new(Point::new(line, column), shape, fg, false); + let cursor_width = NonZeroU32::new(1).unwrap(); + let cursor = + RenderableCursor::new(Point::new(line, column), shape, fg, cursor_width); rects.extend(cursor.rects(&size_info, config.cursor.thickness())); } @@ -1081,8 +1083,8 @@ impl Display { // Get the visible preedit. let visible_text: String = match (preedit.cursor_byte_offset, preedit.cursor_end_offset) { - (Some(byte_offset), Some(end_offset)) if end_offset > num_cols => StrShortener::new( - &preedit.text[byte_offset..], + (Some(byte_offset), Some(end_offset)) if end_offset.0 > num_cols => StrShortener::new( + &preedit.text[byte_offset.0..], num_cols, ShortenDirection::Right, Some(SHORTENER), @@ -1125,19 +1127,21 @@ impl Display { rects.extend(underline.rects(Flags::UNDERLINE, &metrics, &self.size_info)); let ime_popup_point = match preedit.cursor_end_offset { - Some(cursor_end_offset) if cursor_end_offset != 0 => { - let is_wide = preedit.text[preedit.cursor_byte_offset.unwrap_or_default()..] - .chars() - .next() - .map(|ch| ch.width() == Some(2)) - .unwrap_or_default(); + Some(cursor_end_offset) => { + // Use hollow block when multiple characters are changed at once. + let (shape, width) = if let Some(width) = + NonZeroU32::new((cursor_end_offset.0 - cursor_end_offset.1) as u32) + { + (CursorShape::HollowBlock, width) + } else { + (CursorShape::Beam, NonZeroU32::new(1).unwrap()) + }; let cursor_column = Column( - (end.column.0 as isize - cursor_end_offset as isize + 1).max(0) as usize, + (end.column.0 as isize - cursor_end_offset.0 as isize + 1).max(0) as usize, ); let cursor_point = Point::new(point.line, cursor_column); - let cursor = - RenderableCursor::new(cursor_point, CursorShape::HollowBlock, fg, is_wide); + let cursor = RenderableCursor::new(cursor_point, shape, fg, width); rects.extend(cursor.rects(&self.size_info, config.cursor.thickness())); cursor_point }, @@ -1436,20 +1440,22 @@ pub struct Preedit { /// Byte offset for cursor start into the preedit text. /// /// `None` means that the cursor is invisible. - cursor_byte_offset: Option<usize>, + cursor_byte_offset: Option<(usize, usize)>, - /// The cursor offset from the end of the preedit in char width. - cursor_end_offset: Option<usize>, + /// The cursor offset from the end of the start of the preedit in char width. + cursor_end_offset: Option<(usize, usize)>, } impl Preedit { - pub fn new(text: String, cursor_byte_offset: Option<usize>) -> Self { + pub fn new(text: String, cursor_byte_offset: Option<(usize, usize)>) -> Self { let cursor_end_offset = if let Some(byte_offset) = cursor_byte_offset { // Convert byte offset into char offset. - let cursor_end_offset = - text[byte_offset..].chars().fold(0, |acc, ch| acc + ch.width().unwrap_or(1)); + let start_to_end_offset = + text[byte_offset.0..].chars().fold(0, |acc, ch| acc + ch.width().unwrap_or(1)); + let end_to_end_offset = + text[byte_offset.1..].chars().fold(0, |acc, ch| acc + ch.width().unwrap_or(1)); - Some(cursor_end_offset) + Some((start_to_end_offset, end_to_end_offset)) } else { None }; diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index b4600ae3..46e9433c 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -1843,11 +1843,8 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> { self.ctx.update_cursor_blinking(); }, Ime::Preedit(text, cursor_offset) => { - let preedit = if text.is_empty() { - None - } else { - Some(Preedit::new(text, cursor_offset.map(|offset| offset.0))) - }; + let preedit = + (!text.is_empty()).then(|| Preedit::new(text, cursor_offset)); if self.ctx.display.ime.preedit() != preedit.as_ref() { self.ctx.display.ime.set_preedit(preedit); |