aboutsummaryrefslogtreecommitdiff
path: root/src/text/rich.rs
diff options
context:
space:
mode:
authorPolesznyák Márk <contact@pml68.dev>2025-10-26 20:22:20 +0100
committerPolesznyák Márk <contact@pml68.dev>2025-10-26 20:22:20 +0100
commit0dcf63d06e17aa64caa64d26392d3187f9b730ea (patch)
tree1800e235cb33167fedc270742c16eef2ffb0c926 /src/text/rich.rs
parentfix(wip): multi line text selection box drawing (works for non-wrapped) (diff)
downloadiced_selection-0dcf63d06e17aa64caa64d26392d3187f9b730ea.tar.gz
feat: clean up debugging code, add multi-line fix to `Rich`
Diffstat (limited to '')
-rw-r--r--src/text/rich.rs146
1 files changed, 121 insertions, 25 deletions
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<Link> State<Link> {
))
}
- 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();
}