Fix multi-byte handling hit testing

Return a byte offset within the string for the hit test in TextInput.
This commit is contained in:
Simon Hausmann 2020-09-22 18:43:00 +02:00
parent adfadd12fa
commit 49edd69a3a
3 changed files with 16 additions and 15 deletions

View file

@ -27,17 +27,17 @@ impl Font {
text_metrics.width() as _ text_metrics.width() as _
} }
pub fn text_index_for_x_position(&self, text: &str, x: f32) -> usize { pub fn text_offset_for_x_position(&self, text: &str, x: f32) -> usize {
// This is pretty cruel ... // This is pretty cruel ...
let mut last_width = 0.; let mut last_width = 0.;
for index in 1..text.len() { for offset in text.char_indices().map(|(offset, _)| offset) {
let new_width = self.text_width(&text[0..index]); let new_width = self.text_width(&text[0..offset]);
if new_width > last_width { if new_width > last_width {
let advance = new_width - last_width; let advance = new_width - last_width;
if last_width + advance / 2. >= x { if last_width + advance / 2. >= x {
return index; return offset;
} }
last_width = new_width; last_width = new_width;

View file

@ -48,18 +48,19 @@ impl Font {
.fold(0., |width, glyph| width + glyph.advance) .fold(0., |width, glyph| width + glyph.advance)
} }
pub fn text_index_for_x_position(&self, text: &str, x: f32) -> usize { pub fn text_offset_for_x_position<'a>(&self, text: &'a str, x: f32) -> usize {
let mut index = 0; let mut char_offset_it = text.char_indices().map(|(offset, _)| offset);
let mut current_x = 0.; let mut current_x = 0.;
let mut current_offset = 0;
// This assumes a 1:1 mapping between glyphs and characters right now -- this is wrong. // This assumes a 1:1 mapping between glyphs and characters right now -- this is wrong.
for (_, glyph_id) in self.string_to_glyphs(text) { for (_, glyph_id) in self.string_to_glyphs(text) {
let metrics = self.glyph_metrics(glyph_id); let metrics = self.glyph_metrics(glyph_id);
if current_x + metrics.advance / 2. >= x { if current_x + metrics.advance / 2. >= x {
return index; return current_offset;
} }
index += 1; current_offset = char_offset_it.next().unwrap();
current_x += metrics.advance; current_x += metrics.advance;
} }

View file

@ -835,8 +835,8 @@ pub struct TextInput {
pub y: Property<f32>, pub y: Property<f32>,
pub width: Property<f32>, pub width: Property<f32>,
pub height: Property<f32>, pub height: Property<f32>,
pub cursor_position: Property<i32>, pub cursor_position: Property<i32>, // byte offset,
pub anchor_position: Property<i32>, pub anchor_position: Property<i32>, // byte offset
pub text_cursor_width: Property<f32>, pub text_cursor_width: Property<f32>,
pub cursor_visible: Property<bool>, pub cursor_visible: Property<bool>,
pub accepted: Signal<()>, pub accepted: Signal<()>,
@ -952,16 +952,16 @@ impl Item for TextInput {
InputEventResult::GrabMouse InputEventResult::GrabMouse
}; };
let clicked_index = TextInput::with_font(self, window, |font| { let clicked_offset = TextInput::with_font(self, window, |font| {
let text = Self::FIELD_OFFSETS.text.apply_pin(self).get(); let text = Self::FIELD_OFFSETS.text.apply_pin(self).get();
font.text_index_for_x_position(&text, event.pos.x) font.text_offset_for_x_position(&text, event.pos.x)
}) as i32; }) as i32;
if matches!(event.what, MouseEventType::MousePressed) { if matches!(event.what, MouseEventType::MousePressed) {
self.as_ref().pressed.set(true); self.as_ref().pressed.set(true);
self.as_ref().show_cursor(window); self.as_ref().show_cursor(window);
self.as_ref().anchor_position.set(clicked_index); self.as_ref().anchor_position.set(clicked_offset);
self.as_ref().cursor_position.set(clicked_index); self.as_ref().cursor_position.set(clicked_offset);
} }
match event.what { match event.what {
@ -969,7 +969,7 @@ impl Item for TextInput {
self.as_ref().pressed.set(false); self.as_ref().pressed.set(false);
} }
MouseEventType::MouseMoved if self.as_ref().pressed.get() => { MouseEventType::MouseMoved if self.as_ref().pressed.get() => {
self.as_ref().cursor_position.set(clicked_index); self.as_ref().cursor_position.set(clicked_offset);
} }
_ => {} _ => {}
} }