🐛 Fix slash char problems for embedded editor

This commit is contained in:
Alejandro Alonso 2026-03-10 08:07:51 +01:00
parent 052417cd10
commit 7c46a3f1e0
3 changed files with 59 additions and 6 deletions

View File

@ -123,6 +123,9 @@ fn calculate_cursor_rect(
.map(|span| span.text.chars().count())
.sum();
let layout_char_pos = para.logical_to_layout_offset(char_pos);
let layout_para_count = para.logical_to_layout_offset(para_char_count);
let (cursor_x, cursor_height) = if para_char_count == 0 {
// Empty paragraph - use default height
(0.0, laid_out_para.height())
@ -139,7 +142,7 @@ fn calculate_cursor_rect(
}
} else if char_pos >= para_char_count {
let rects = laid_out_para.get_rects_for_range(
para_char_count.saturating_sub(1)..para_char_count,
layout_para_count.saturating_sub(1)..layout_para_count,
RectHeightStyle::Max,
RectWidthStyle::Tight,
);
@ -150,7 +153,7 @@ fn calculate_cursor_rect(
}
} else {
let rects = laid_out_para.get_rects_for_range(
char_pos..char_pos + 1,
layout_char_pos..layout_char_pos + 1,
RectHeightStyle::Max,
RectWidthStyle::Tight,
);
@ -221,9 +224,11 @@ fn calculate_selection_rects(
};
if range_start < range_end {
let layout_range_start = para.logical_to_layout_offset(range_start);
let layout_range_end = para.logical_to_layout_offset(range_end);
use skia_safe::textlayout::{RectHeightStyle, RectWidthStyle};
let text_boxes = laid_out_para.get_rects_for_range(
range_start..range_end,
layout_range_start..layout_range_end,
RectHeightStyle::Max,
RectWidthStyle::Tight,
);

View File

@ -522,10 +522,12 @@ impl TextContent {
}
}
let layout_offset = position_with_affinity.position as usize;
let logical_offset = paragraph.layout_to_logical_offset(layout_offset);
return Some(TextPositionWithAffinity::new(
position_with_affinity,
paragraph_index,
position_with_affinity.position as usize,
logical_offset,
));
}
}
@ -996,6 +998,49 @@ impl Paragraph {
.iter_mut()
.for_each(|l| l.scale_content(value));
}
/// Convert a logical character offset (based on original span text)
/// to a layout offset (as used by Skia). `apply_text_transform` inserts
/// a zero-width space after each '/', so the layout text is longer.
pub fn logical_to_layout_offset(&self, logical_offset: usize) -> usize {
let mut pos = 0;
let mut extra = 0;
for span in self.children() {
for ch in span.text.chars() {
if pos >= logical_offset {
return logical_offset + extra;
}
if ch == '/' {
extra += 1;
}
pos += 1;
}
}
logical_offset + extra
}
/// Convert a layout character offset (from Skia, which includes
/// zero-width spaces after '/') back to a logical offset.
pub fn layout_to_logical_offset(&self, layout_offset: usize) -> usize {
let mut logical_pos = 0;
let mut layout_pos = 0;
for span in self.children() {
for ch in span.text.chars() {
if layout_pos >= layout_offset {
return logical_pos;
}
logical_pos += 1;
layout_pos += 1;
if ch == '/' {
layout_pos += 1;
if layout_pos > layout_offset {
return logical_pos;
}
}
}
}
logical_pos
}
}
#[derive(Debug, PartialEq, Clone)]

View File

@ -851,7 +851,8 @@ fn get_cursor_rect(
let mut y_offset = valign_offset;
for (idx, laid_out_para) in layout_paragraphs.iter().enumerate() {
if idx == cursor.paragraph {
let char_pos = cursor.offset;
let para = &paragraphs[idx];
let char_pos = para.logical_to_layout_offset(cursor.offset);
use skia_safe::textlayout::{RectHeightStyle, RectWidthStyle};
let rects = laid_out_para.get_rects_for_range(
@ -938,9 +939,11 @@ fn get_selection_rects(
};
if range_start < range_end {
let layout_range_start = para.logical_to_layout_offset(range_start);
let layout_range_end = para.logical_to_layout_offset(range_end);
use skia_safe::textlayout::{RectHeightStyle, RectWidthStyle};
let text_boxes = laid_out_para.get_rects_for_range(
range_start..range_end,
layout_range_start..layout_range_end,
RectHeightStyle::Tight,
RectWidthStyle::Tight,
);