aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralex-ds13 <145657253+alex-ds13@users.noreply.github.com>2025-11-25 18:43:51 +0000
committerPolesznyák Márk <contact@pml68.dev>2025-12-29 23:23:41 +0100
commit99ee469c86c8c187d874253ad8f0e3274cf97943 (patch)
tree30e27d835a2e942bfa1aa9365d121bec3084b704
parentfix: outbounds selecting when inside a scrollable (diff)
downloadiced_selection-99ee469c86c8c187d874253ad8f0e3274cf97943.tar.gz
fix: select graphemes with multi codepoints properly
- We continue to use the graphemes index for the selection ends, however when calculating the selection rectangles we now check if the visual line had any glyph with `start` and `end` index bigger than 1 before or within the range and update the range accordingly. This way all the code that checks for word boundaries when selecting by words still functions properly.
-rw-r--r--src/text.rs38
-rw-r--r--src/text/rich.rs38
2 files changed, 48 insertions, 28 deletions
diff --git a/src/text.rs b/src/text.rs
index ac51395..143a78d 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -831,7 +831,7 @@ fn highlight_line(
.map(|glyph| glyph.end)
.unwrap_or(0);
- let range = start.max(from)..end.min(to);
+ let mut range = start.max(from)..end.min(to);
let x_offset = visual_line
.glyphs
@@ -844,21 +844,31 @@ fn highlight_line(
} else if range.start == start && range.end == end {
(x_offset, visual_line.w)
} else {
- let first_glyph = visual_line
- .glyphs
- .iter()
- .position(|glyph| range.start <= glyph.start)
- .unwrap_or(0);
-
- let mut glyphs = visual_line.glyphs.iter();
+ let mut x = 0.0;
+ let mut width = 0.0;
+ for glyph in &visual_line.glyphs {
+ let glyph_count = glyph.start.abs_diff(glyph.end);
+
+ // Check for multi codepoint glyphs before or within the range
+ if glyph_count > 1 {
+ if range.start > glyph.start {
+ range.start += glyph_count - 1;
+ range.end += glyph_count - 1;
+ } else if range.end > glyph.start {
+ range.end += glyph_count - 1;
+ }
+ }
- let x: f32 =
- glyphs.by_ref().take(first_glyph).map(|glyph| glyph.w).sum();
+ if range.start > glyph.start {
+ x += glyph.w;
+ }
- let width: f32 = glyphs
- .take_while(|glyph| range.end > glyph.start)
- .map(|glyph| glyph.w)
- .sum();
+ if range.start <= glyph.start && range.end > glyph.start {
+ width += glyph.w;
+ } else if range.end <= glyph.start {
+ break;
+ }
+ }
(x_offset + x, width)
}
diff --git a/src/text/rich.rs b/src/text/rich.rs
index 9f53387..597fa26 100644
--- a/src/text/rich.rs
+++ b/src/text/rich.rs
@@ -983,7 +983,7 @@ fn highlight_line(
.map(|glyph| glyph.end)
.unwrap_or(0);
- let range = start.max(from)..end.min(to);
+ let mut range = start.max(from)..end.min(to);
let x_offset = visual_line
.glyphs
@@ -996,21 +996,31 @@ fn highlight_line(
} else if range.start == start && range.end == end {
(x_offset, visual_line.w)
} else {
- let first_glyph = visual_line
- .glyphs
- .iter()
- .position(|glyph| range.start <= glyph.start)
- .unwrap_or(0);
-
- let mut glyphs = visual_line.glyphs.iter();
+ let mut x = 0.0;
+ let mut width = 0.0;
+ for glyph in &visual_line.glyphs {
+ let glyph_count = glyph.start.abs_diff(glyph.end);
+
+ // Check for multi codepoint glyphs before or within the range
+ if glyph_count > 1 {
+ if range.start > glyph.start {
+ range.start += glyph_count - 1;
+ range.end += glyph_count - 1;
+ } else if range.end > glyph.start {
+ range.end += glyph_count - 1;
+ }
+ }
- let x: f32 =
- glyphs.by_ref().take(first_glyph).map(|glyph| glyph.w).sum();
+ if range.start > glyph.start {
+ x += glyph.w;
+ }
- let width: f32 = glyphs
- .take_while(|glyph| range.end > glyph.start)
- .map(|glyph| glyph.w)
- .sum();
+ if range.start <= glyph.start && range.end > glyph.start {
+ width += glyph.w;
+ } else if range.end <= glyph.start {
+ break;
+ }
+ }
(x_offset + x, width)
}