Keyboard input, text input elements

This commit is contained in:
2026-03-21 01:17:07 -04:00
parent 423df4ae1f
commit 84077b718f
13 changed files with 1451 additions and 52 deletions

View File

@@ -228,6 +228,18 @@ impl PreparedText {
rects
}
pub fn caret_rect(&self, offset: usize, width: f32) -> Option<Rect> {
let width = width.max(0.0);
let line = self.line_for_offset(offset)?;
let x = self.caret_x_for_line_offset(line, offset);
Some(Rect::new(
x,
line.rect.origin.y,
width,
line.rect.size.height,
))
}
pub fn apply_selected_text_color(&mut self, start: usize, end: usize) {
let Some(selected_color) = self.selection_style.text_color else {
return;
@@ -260,6 +272,18 @@ impl PreparedText {
Some(last)
}
fn line_for_offset(&self, offset: usize) -> Option<&PreparedTextLine> {
let offset = offset.min(self.text.len());
let mut lines = self.lines.iter();
let first = lines.next()?;
for line in std::iter::once(first).chain(lines) {
if offset <= line.text_end {
return Some(line);
}
}
Some(self.lines.last().unwrap_or(first))
}
fn byte_offset_for_line_position(&self, line: &PreparedTextLine, x: f32) -> usize {
if line.glyph_start == line.glyph_end {
return line.text_start;
@@ -285,6 +309,33 @@ impl PreparedText {
line.text_end
}
fn caret_x_for_line_offset(&self, line: &PreparedTextLine, offset: usize) -> f32 {
if line.glyph_start == line.glyph_end {
return line.rect.origin.x;
}
let offset = offset.min(self.text.len());
let line_glyphs = &self.glyphs[line.glyph_start..line.glyph_end];
let first_glyph = &line_glyphs[0];
if offset <= first_glyph.text_start {
return first_glyph.position.x;
}
for glyph in line_glyphs {
if offset <= glyph.text_start {
return glyph.position.x;
}
if offset <= glyph.text_end {
return glyph.position.x + glyph.advance.max(0.0);
}
}
line_glyphs
.last()
.map(|glyph| glyph.position.x + glyph.advance.max(0.0))
.unwrap_or(line.rect.origin.x)
}
}
#[derive(Clone, Debug, PartialEq)]
@@ -409,4 +460,28 @@ mod tests {
assert_eq!(text.glyphs[1].color, Color::rgb(0x11, 0x12, 0x1A));
assert_eq!(text.glyphs[2].color, Color::rgb(0x11, 0x12, 0x1A));
}
#[test]
fn prepared_text_caret_rect_tracks_cluster_boundaries() {
let text = PreparedText::monospace(
"abcd",
Point::new(10.0, 20.0),
16.0,
8.0,
Color::rgb(0xFF, 0xFF, 0xFF),
);
assert_eq!(
text.caret_rect(0, 2.0),
Some(Rect::new(10.0, 20.0, 2.0, 16.0))
);
assert_eq!(
text.caret_rect(2, 2.0),
Some(Rect::new(26.0, 20.0, 2.0, 16.0))
);
assert_eq!(
text.caret_rect(4, 2.0),
Some(Rect::new(42.0, 20.0, 2.0, 16.0))
);
}
}