aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPolesznyák Márk <contact@pml68.dev>2025-10-25 02:30:58 +0200
committerPolesznyák Márk <contact@pml68.dev>2025-10-25 02:30:58 +0200
commitec65fc409a642f4d5199f64ac24fcbd20ccb61dd (patch)
tree3d7a0e02ea63f976f0bdb3b0c2811407fa58b77f
parentfix: triple-click + drag edge cases (diff)
downloadiced_selection-ec65fc409a642f4d5199f64ac24fcbd20ccb61dd.tar.gz
fix(wip): multi line text selection box drawing (works for non-wrapped)
-rw-r--r--src/text.rs148
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 {