diff options
| author | Polesznyák Márk <contact@pml68.dev> | 2025-10-25 02:30:58 +0200 |
|---|---|---|
| committer | Polesznyák Márk <contact@pml68.dev> | 2025-10-25 02:30:58 +0200 |
| commit | ec65fc409a642f4d5199f64ac24fcbd20ccb61dd (patch) | |
| tree | 3d7a0e02ea63f976f0bdb3b0c2811407fa58b77f | |
| parent | fix: triple-click + drag edge cases (diff) | |
| download | iced_selection-ec65fc409a642f4d5199f64ac24fcbd20ccb61dd.tar.gz | |
fix(wip): multi line text selection box drawing (works for non-wrapped)
| -rw-r--r-- | src/text.rs | 148 |
1 files changed, 132 insertions, 16 deletions
diff --git a/src/text.rs b/src/text.rs index 484456a..82e9c2c 100644 --- a/src/text.rs +++ b/src/text.rs @@ -188,20 +188,133 @@ impl State { )) } - fn selection_end_points(&self) -> [Point; 2] { + fn selection_end_points(&self) -> (usize, Point, Point) { let Selection { start, end, .. } = self.selection; - let start_position = self - .paragraph + let (start_row, start_position) = self .grapheme_position(start.line, start.index) - .unwrap_or(Point::ORIGIN); + .unwrap_or_default(); - let end_position = self - .paragraph + let (end_row, end_position) = self .grapheme_position(end.line, end.index) - .unwrap_or(Point::ORIGIN); + .unwrap_or_default(); + + dbg!(( + end_row.saturating_sub(start_row) + 1, + start_position, + end_position, + )) + } + + fn grapheme_position( + &self, + line: usize, + index: usize, + ) -> Option<(usize, Point)> { + use unicode_segmentation::UnicodeSegmentation; + + let mut first_run_index = None; + let mut last_run_index = None; + let mut last_start = None; + let mut last_grapheme_count = 0; + let mut last_run_graphemes = 0; + let mut real_index = 0; + let mut graphemes_seen = 0; + + let mut glyphs = self + .paragraph + .buffer() + .layout_runs() + .enumerate() + .filter(|(_, run)| run.line_i == line) + .flat_map(|(run_idx, run)| { + let line_top = run.line_top; + + if first_run_index.is_none() { + first_run_index = Some(run_idx); + } + + run.glyphs.iter().map(move |glyph| { + let mut glyph = glyph.clone(); + glyph.y += line_top; + (run_idx, glyph, run.text) + }) + }); + + let (run_idx, glyph, _) = glyphs + .find(|(run_idx, glyph, text)| { + let new_run_index = if Some(*run_idx) != last_run_index { + last_run_graphemes = 0; + Some(*run_idx) + } else { + last_run_index + }; + + if Some(glyph.start) != last_start { + last_grapheme_count = + text[glyph.start..glyph.end].graphemes(false).count(); + last_start = Some(glyph.start); + graphemes_seen += last_grapheme_count; + last_run_graphemes += last_grapheme_count; + real_index += last_grapheme_count; - [start_position, end_position] + if new_run_index != last_run_index && graphemes_seen < index + { + real_index = last_grapheme_count; + } + } else if new_run_index != last_run_index + && graphemes_seen < index + { + real_index = 0; + } + + last_run_index = new_run_index; + + graphemes_seen >= index + }) + .or_else(|| glyphs.last())?; + + real_index -= graphemes_seen.saturating_sub(index); + real_index = + dbg!(real_index.saturating_sub(last_run_index? - first_run_index?)); + + last_run_graphemes = last_run_graphemes + .saturating_sub(last_run_index? - first_run_index? - 1); + + let advance = if real_index == 0 { + 0.0 + } else { + glyph.w + * (1.0 + - last_run_graphemes.saturating_sub(real_index) as f32 + / last_grapheme_count.max(1) as f32) + }; + + // println!( + // "text: total: {} index: {}\nreal: total: {} index: {}\nlast_seen: {}\n\n", + // graphemes_seen, + // index, + // last_run_graphemes, + // real_index, + // last_grapheme_count + // ); + + // println!( + // "{}, {}: {}, {} (index {})", + // line, + // index, + // glyph.x + glyph.x_offset * glyph.font_size + advance, + // glyph.y - glyph.y_offset * glyph.font_size, + // run_idx + // ); + + Some(( + last_run_index?, + Point::new( + glyph.x + glyph.x_offset * glyph.font_size + advance, + glyph.y - glyph.y_offset * glyph.font_size, + ), + )) } fn update(&mut self, text: text::Text<&str, Font>) { @@ -522,9 +635,9 @@ where if !state.selection.is_empty() { let bounds = layout.bounds(); - let [start, end] = state - .selection_end_points() - .map(|pos| pos + core::Vector::new(bounds.x, bounds.y)); + let (rows, mut start, mut end) = state.selection_end_points(); + start = start + core::Vector::new(bounds.x, bounds.y); + end = end + core::Vector::new(bounds.x, bounds.y); let line_height = self .format @@ -535,16 +648,19 @@ where .0; let baseline_y = bounds.y - + ((start.y - bounds.y) / line_height).floor() * line_height; + + (((start.y - bounds.y) * 10.0).ceil() / 10.0 / line_height) + .floor() + * line_height; // The correct code, uncomment when glyphs report a correct `y` value. // - // let height = end.y - baseline_y - 0.5; - // let rows = (height / line_height).ceil() as usize; + //let height = dbg!(end.y) - baseline_y - 0.5; + //let rows = + // usize::max(dbg!((height / line_height).ceil() as usize), 1); // // Temporary solution - let rows = - state.selection.end.line - state.selection.start.line + 1; + //let rows = + // state.selection.end.line - state.selection.start.line + 1; for row in 0..rows { let (x, width) = if row == 0 { |
