diff options
| author | alex-ds13 <145657253+alex-ds13@users.noreply.github.com> | 2025-11-25 18:43:51 +0000 |
|---|---|---|
| committer | Polesznyák Márk <contact@pml68.dev> | 2026-02-05 14:36:32 +0100 |
| commit | 28d41d436313152ace3cfb68b4679affb307ef8f (patch) | |
| tree | e9209dc4d52d266e65a4ee9817960af278069cf2 /src/text | |
| parent | fix: outbounds selecting when inside a scrollable (diff) | |
| download | iced_selection-28d41d436313152ace3cfb68b4679affb307ef8f.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.
Diffstat (limited to '')
| -rw-r--r-- | src/text.rs | 38 | ||||
| -rw-r--r-- | src/text/rich.rs | 38 |
2 files changed, 48 insertions, 28 deletions
diff --git a/src/text.rs b/src/text.rs index ffd5ea2..9c132ad 100644 --- a/src/text.rs +++ b/src/text.rs @@ -830,7 +830,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 @@ -843,21 +843,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 060ab26..22be56d 100644 --- a/src/text/rich.rs +++ b/src/text/rich.rs @@ -977,7 +977,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 @@ -990,21 +990,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) } |
