From 0dcf63d06e17aa64caa64d26392d3187f9b730ea Mon Sep 17 00:00:00 2001 From: Polesznyák Márk Date: Sun, 26 Oct 2025 20:22:20 +0100 Subject: feat: clean up debugging code, add multi-line fix to `Rich` --- src/text/rich.rs | 146 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 121 insertions(+), 25 deletions(-) (limited to 'src/text') diff --git a/src/text/rich.rs b/src/text/rich.rs index 93cefa2..3268160 100644 --- a/src/text/rich.rs +++ b/src/text/rich.rs @@ -204,20 +204,119 @@ 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(); - [start_position, end_position] + ( + 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 (_, glyph, _) = glyphs + .find(|(run_idx, glyph, text)| { + 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; + + if Some(*run_idx) != last_run_index + && graphemes_seen < index + { + real_index = last_grapheme_count; + last_run_graphemes = last_grapheme_count; + } + } else if Some(*run_idx) != last_run_index + && graphemes_seen < index + { + real_index = 0; + last_run_graphemes = 0; + } + + last_run_index = Some(*run_idx); + + graphemes_seen >= index + }) + .or_else(|| glyphs.last())?; + + real_index -= graphemes_seen.saturating_sub(index); + real_index = + real_index.saturating_sub(last_run_index? - first_run_index?); + + last_run_graphemes = last_run_graphemes + .saturating_sub(last_run_index? - first_run_index?); + + let advance = if last_run_index? - first_run_index? <= 1 { + 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) + - glyph.w * (last_run_index? - first_run_index?) as f32 + } + } else { + -(glyph.w + * (1.0 + + last_run_graphemes.saturating_sub(real_index) as f32 + / last_grapheme_count.max(1) as f32)) + }; + + Some(( + last_run_index?, + Point::new( + glyph.x + glyph.x_offset * glyph.font_size + advance, + glyph.y - glyph.y_offset * glyph.font_size, + ), + )) } } @@ -390,9 +489,9 @@ where if !state.selection.is_empty() { let bounds = layout.bounds(); - let [start, end] = state - .selection_end_points() - .map(|pos| pos + 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 .line_height @@ -402,16 +501,9 @@ where .0; let baseline_y = bounds.y - + ((start.y - bounds.y) / 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; - // - // Temporary solution - let rows = - state.selection.end.line - state.selection.start.line + 1; + + (((start.y - bounds.y) * 10.0).ceil() / 10.0 / line_height) + .floor() + * line_height; for row in 0..rows { let (x, width) = if row == 0 { @@ -524,11 +616,11 @@ where if state.keyboard_modifiers.shift() { state.selection.change_selection(new_end); - state.dragging = Some(Dragging::Grapheme); - } else if state.span_pressed.is_none() { + } else { state.selection.select_range(new_end, new_end); - state.dragging = Some(Dragging::Grapheme); } + + state.dragging = Some(Dragging::Grapheme); } mouse::click::Kind::Double => { state.selection.select_word( @@ -536,13 +628,17 @@ where index, &state.paragraph, ); - state.dragging = None; + state.dragging = Some(Dragging::Word); } mouse::click::Kind::Triple => { state.selection.select_line(line, &state.paragraph); state.dragging = Some(Dragging::Line); } } + + state.last_click = Some(click); + + shell.capture_event(); } else { state.selection = Selection::default(); } -- cgit v1.2.3