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> | 2025-12-29 23:23:41 +0100 |
| commit | 99ee469c86c8c187d874253ad8f0e3274cf97943 (patch) | |
| tree | 30e27d835a2e942bfa1aa9365d121bec3084b704 | |
| parent | fix: outbounds selecting when inside a scrollable (diff) | |
| download | iced_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.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 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) } |
