diff options
| author | alex-ds13 <145657253+alex-ds13@users.noreply.github.com> | 2025-11-26 00:16:59 +0000 |
|---|---|---|
| committer | Polesznyák Márk <contact@pml68.dev> | 2025-12-29 23:23:41 +0100 |
| commit | a6c4ffae8f973b54897b60a1c9dd1329e5abd332 (patch) | |
| tree | ba91aeeb97be03ad184bb31d6e8fe37e1587ca86 | |
| parent | fix: select graphemes with multi codepoints properly (diff) | |
| download | iced_selection-a6c4ffae8f973b54897b60a1c9dd1329e5abd332.tar.gz | |
fix: graphemes with multi codepoint on multiple visual lines
- The logic for graphemes with multiple codepoints was working when they
were on a single visual line, however on multiple visual lines the
lines after the one that contained such grapheme weren't taking that
into consideration.
| -rw-r--r-- | src/text.rs | 124 | ||||
| -rw-r--r-- | src/text/rich.rs | 126 |
2 files changed, 157 insertions, 93 deletions
diff --git a/src/text.rs b/src/text.rs index 143a78d..851e77b 100644 --- a/src/text.rs +++ b/src/text.rs @@ -819,60 +819,92 @@ fn highlight_line( ) -> impl Iterator<Item = (f32, f32)> + '_ { let layout = line.layout_opt().map(Vec::as_slice).unwrap_or_default(); - layout.iter().map(move |visual_line| { - let start = visual_line - .glyphs - .first() - .map(|glyph| glyph.start) - .unwrap_or(0); - let end = visual_line - .glyphs - .last() - .map(|glyph| glyph.end) - .unwrap_or(0); - - let mut range = start.max(from)..end.min(to); - - let x_offset = visual_line - .glyphs - .first() - .map(|glyph| glyph.x) - .unwrap_or_default(); + // Check for multi codepoint glyphs for each previous visual line + let mut previous_diff = 0; + let previous_lines_diff = line + .layout_opt() + .map(Vec::as_slice) + .unwrap_or_default() + .iter() + .enumerate() + .map(move |(line_nr, visual_line)| { + if line_nr == 0 { + let current_diff = previous_diff + + visual_line + .glyphs + .iter() + .fold(0, |d, g| d + g.start.abs_diff(g.end) - 1); + previous_diff = current_diff; + 0 + } else { + let current_diff = previous_diff + + visual_line + .glyphs + .iter() + .fold(0, |d, g| d + g.start.abs_diff(g.end) - 1); + let previous_diff_temp = previous_diff; + previous_diff = current_diff; + previous_diff_temp + } + }); - if range.is_empty() { - (x_offset, 0.0) - } else if range.start == start && range.end == end { - (x_offset, visual_line.w) - } else { - 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); + layout.iter().zip(previous_lines_diff).map( + move |(visual_line, previous_lines_diff)| { + let start = visual_line + .glyphs + .first() + .map(|glyph| glyph.start) + .unwrap_or(0); + let end = visual_line + .glyphs + .last() + .map(|glyph| glyph.end) + .unwrap_or(0); + + let to = to + previous_lines_diff; + let mut range = start.max(from)..end.min(to); + + let x_offset = visual_line + .glyphs + .first() + .map(|glyph| glyph.x) + .unwrap_or_default(); + + if range.is_empty() { + (x_offset, 0.0) + } else if range.start == start && range.end == end { + (x_offset, visual_line.w) + } else { + 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; + } + } - // 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; + x += glyph.w; } - } - if range.start > glyph.start { - x += glyph.w; + if range.start <= glyph.start && range.end > glyph.start { + width += glyph.w; + } else if range.end <= glyph.start { + break; + } } - if range.start <= glyph.start && range.end > glyph.start { - width += glyph.w; - } else if range.end <= glyph.start { - break; - } + (x_offset + x, width) } - - (x_offset + x, width) - } - }) + }, + ) } fn visual_lines_offset(line: usize, buffer: &cosmic_text::Buffer) -> i32 { diff --git a/src/text/rich.rs b/src/text/rich.rs index 597fa26..865f64a 100644 --- a/src/text/rich.rs +++ b/src/text/rich.rs @@ -971,60 +971,92 @@ fn highlight_line( ) -> impl Iterator<Item = (f32, f32)> + '_ { let layout = line.layout_opt().map(Vec::as_slice).unwrap_or_default(); - layout.iter().map(move |visual_line| { - let start = visual_line - .glyphs - .first() - .map(|glyph| glyph.start) - .unwrap_or(0); - let end = visual_line - .glyphs - .last() - .map(|glyph| glyph.end) - .unwrap_or(0); - - let mut range = start.max(from)..end.min(to); - - let x_offset = visual_line - .glyphs - .first() - .map(|glyph| glyph.x) - .unwrap_or_default(); - - if range.is_empty() { - (x_offset, 0.0) - } else if range.start == start && range.end == end { - (x_offset, visual_line.w) - } else { - 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 for each previous visual line + let mut previous_diff = 0; + let previous_lines_diff = line + .layout_opt() + .map(Vec::as_slice) + .unwrap_or_default() + .iter() + .enumerate() + .map(move |(line_nr, visual_line)| { + if line_nr == 0 { + let current_diff = previous_diff + + visual_line + .glyphs + .iter() + .fold(0, |d, g| d + g.start.abs_diff(g.end) - 1); + previous_diff = current_diff; + 0 + } else { + let current_diff = previous_diff + + visual_line + .glyphs + .iter() + .fold(0, |d, g| d + g.start.abs_diff(g.end) - 1); + let previous_diff_temp = previous_diff; + previous_diff = current_diff; + previous_diff_temp + } + }); + + layout.iter().zip(previous_lines_diff).map( + move |(visual_line, previous_lines_diff)| { + let start = visual_line + .glyphs + .first() + .map(|glyph| glyph.start) + .unwrap_or(0); + let end = visual_line + .glyphs + .last() + .map(|glyph| glyph.end) + .unwrap_or(0); + + let to = to + previous_lines_diff; + let mut range = start.max(from)..end.min(to); + + let x_offset = visual_line + .glyphs + .first() + .map(|glyph| glyph.x) + .unwrap_or_default(); + + if range.is_empty() { + (x_offset, 0.0) + } else if range.start == start && range.end == end { + (x_offset, visual_line.w) + } else { + 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; + } + } - // 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; + x += glyph.w; } - } - if range.start > glyph.start { - x += glyph.w; + if range.start <= glyph.start && range.end > glyph.start { + width += glyph.w; + } else if range.end <= glyph.start { + break; + } } - if range.start <= glyph.start && range.end > glyph.start { - width += glyph.w; - } else if range.end <= glyph.start { - break; - } + (x_offset + x, width) } - - (x_offset + x, width) - } - }) + }, + ) } fn visual_lines_offset(line: usize, buffer: &cosmic_text::Buffer) -> i32 { |
