From c1fc242a9a1f12f1d16415798a9b9818bea8be5c Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 31 Aug 2021 11:55:32 +0200 Subject: [PATCH] Move the text_input_byte_offset_for_position from the FontMetrics to the Window In preparation of having mutiple-lines TextInput, we will need to give more data to this function so it no longer belong in FontMetrics Also remove the unused FontMetric::line_height() --- sixtyfps_runtime/corelib/graphics.rs | 7 --- sixtyfps_runtime/corelib/items/text.rs | 11 +---- sixtyfps_runtime/corelib/window.rs | 10 +++++ .../rendering_backends/gl/fonts.rs | 17 ------- .../rendering_backends/gl/graphics_window.rs | 34 ++++++++++++++ .../rendering_backends/qt/qt_window.rs | 44 +++++++++---------- .../rendering_backends/testing/lib.rs | 18 ++++---- 7 files changed, 77 insertions(+), 64 deletions(-) diff --git a/sixtyfps_runtime/corelib/graphics.rs b/sixtyfps_runtime/corelib/graphics.rs index 3c7ba89e9..b58fc4ca0 100644 --- a/sixtyfps_runtime/corelib/graphics.rs +++ b/sixtyfps_runtime/corelib/graphics.rs @@ -156,13 +156,6 @@ pub trait FontMetrics { /// Returns the size of the given string in logical pixels. /// When set, `max_width` means that one need to wrap the text so it does not go further than that fn text_size(&self, text: &str, max_width: Option) -> Size; - /// Returns the height of a line of text. - fn line_height(&self) -> f32; - /// Returns the (UTF-8) byte offset in the given text that refers to the character that contributed to - /// the glyph cluster that's visually nearest to the given x coordinate. This is used for hit-testing, - /// for example when receiving a mouse click into a text field. Then this function returns the "cursor" - /// position. - fn text_offset_for_x_position(&self, text: &str, x: f32) -> usize; } #[cfg(feature = "ffi")] diff --git a/sixtyfps_runtime/corelib/items/text.rs b/sixtyfps_runtime/corelib/items/text.rs index 2481be902..559ff1550 100644 --- a/sixtyfps_runtime/corelib/items/text.rs +++ b/sixtyfps_runtime/corelib/items/text.rs @@ -302,16 +302,9 @@ impl Item for TextInput { if !self.enabled() { return InputEventResult::EventIgnored; } - - let text = self.text(); - let font_metrics = window.font_metrics( - &self.cached_rendering_data, - &|| self.unresolved_font_request(), - Self::FIELD_OFFSETS.text.apply_pin(self), - ); match event { MouseEvent::MousePressed { pos } => { - let clicked_offset = font_metrics.text_offset_for_x_position(&text, pos.x) as i32; + let clicked_offset = window.text_input_byte_offset_for_position(self, pos) as i32; self.as_ref().pressed.set(true); self.as_ref().anchor_position.set(clicked_offset); self.as_ref().cursor_position.set(clicked_offset); @@ -325,7 +318,7 @@ impl Item for TextInput { MouseEvent::MouseMoved { pos } => { if self.as_ref().pressed.get() { let clicked_offset = - font_metrics.text_offset_for_x_position(&text, pos.x) as i32; + window.text_input_byte_offset_for_position(self, pos) as i32; self.as_ref().cursor_position.set(clicked_offset); } } diff --git a/sixtyfps_runtime/corelib/window.rs b/sixtyfps_runtime/corelib/window.rs index 97124afe9..055633ad7 100644 --- a/sixtyfps_runtime/corelib/window.rs +++ b/sixtyfps_runtime/corelib/window.rs @@ -66,6 +66,16 @@ pub trait PlatformWindow { reference_text: Pin<&crate::properties::Property>, ) -> Box; + /// Returns the (UTF-8) byte offset in the text property that refers to the character that contributed to + /// the glyph cluster that's visually nearest to the given coordinate. This is used for hit-testing, + /// for example when receiving a mouse click into a text field. Then this function returns the "cursor" + /// position. + fn text_input_byte_offset_for_position( + &self, + text_input: Pin<&crate::items::TextInput>, + pos: Point, + ) -> usize; + /// Return self as any so the backend can upcast fn as_any(&self) -> &dyn core::any::Any; } diff --git a/sixtyfps_runtime/rendering_backends/gl/fonts.rs b/sixtyfps_runtime/rendering_backends/gl/fonts.rs index cc9d0f868..73fb98ed6 100644 --- a/sixtyfps_runtime/rendering_backends/gl/fonts.rs +++ b/sixtyfps_runtime/rendering_backends/gl/fonts.rs @@ -348,23 +348,6 @@ impl FontMetricsTrait for FontMetrics { max_width.map(|x| x * self.scale_factor), ) / self.scale_factor } - - fn line_height(&self) -> f32 { - self.font.height() - } - - fn text_offset_for_x_position(&self, text: &str, x: f32) -> usize { - let x = x * self.scale_factor; - let metrics = self.font.measure(self.letter_spacing.unwrap_or_default(), text); - let mut current_x = 0.; - for glyph in metrics.glyphs { - if current_x + glyph.advance_x / 2. >= x { - return glyph.byte_index; - } - current_x += glyph.advance_x; - } - text.len() - } } pub struct FontCache { diff --git a/sixtyfps_runtime/rendering_backends/gl/graphics_window.rs b/sixtyfps_runtime/rendering_backends/gl/graphics_window.rs index ea751a334..7b7082790 100644 --- a/sixtyfps_runtime/rendering_backends/gl/graphics_window.rs +++ b/sixtyfps_runtime/rendering_backends/gl/graphics_window.rs @@ -556,6 +556,40 @@ impl PlatformWindow for GraphicsWindow { )) } + fn text_input_byte_offset_for_position( + &self, + text_input: Pin<&sixtyfps_corelib::items::TextInput>, + pos: Point, + ) -> usize { + let scale_factor = self.scale_factor(); + let cache_data = text_input + .cached_rendering_data + .get_or_update(&self.graphics_cache, || { + Some(crate::ItemGraphicsCacheEntry::Font(crate::fonts::FONT_CACHE.with(|cache| { + cache.borrow_mut().font( + text_input + .unresolved_font_request() + .merge(&self.default_font_properties.as_ref().get()), + scale_factor, + &text_input.text(), + ) + }))) + }) + .unwrap(); + let font = cache_data.as_font(); + let x = pos.x * scale_factor; + let text = text_input.text(); + let metrics = font.measure(text_input.letter_spacing(), &text); + let mut current_x = 0.; + for glyph in metrics.glyphs { + if current_x + glyph.advance_x / 2. >= x { + return glyph.byte_index; + } + current_x += glyph.advance_x; + } + text.len() + } + fn as_any(&self) -> &dyn std::any::Any { self } diff --git a/sixtyfps_runtime/rendering_backends/qt/qt_window.rs b/sixtyfps_runtime/rendering_backends/qt/qt_window.rs index 4b7c2ecbc..990e0395c 100644 --- a/sixtyfps_runtime/rendering_backends/qt/qt_window.rs +++ b/sixtyfps_runtime/rendering_backends/qt/qt_window.rs @@ -1316,6 +1316,28 @@ impl PlatformWindow for QtWindow { Box::new(get_font(unresolved_font_request_getter().merge(&self.default_font_properties()))) } + fn text_input_byte_offset_for_position( + &self, + text_input: Pin<&sixtyfps_corelib::items::TextInput>, + pos: Point, + ) -> usize { + let font: QFont = + get_font(text_input.unresolved_font_request().merge(&self.default_font_properties())); + let string = qttypes::QString::from(text_input.text().as_str()); + let x: f32 = pos.x; + cpp! { unsafe [font as "QFont", string as "QString", x as "float"] -> usize as "long long" { + QTextLayout layout(string, font); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + if (layout.lineCount() == 0) + return 0; + auto cur = layout.lineAt(0).xToCursor(x); + // convert to an utf8 pos; + return QStringView(string).left(cur).toUtf8().size(); + }} + } + fn as_any(&self) -> &dyn std::any::Any { self } @@ -1369,28 +1391,6 @@ impl sixtyfps_corelib::graphics::FontMetrics for QFont { }}; sixtyfps_corelib::graphics::Size::new(size.width as _, size.height as _) } - - fn line_height(&self) -> f32 { - cpp! { unsafe [self as "const QFont*"] - -> f32 as "float"{ - return QFontMetricsF(*self).height(); - }} - } - - fn text_offset_for_x_position(&self, text: &str, x: f32) -> usize { - let string = qttypes::QString::from(text); - cpp! { unsafe [self as "const QFont*", string as "QString", x as "float"] -> usize as "long long" { - QTextLayout layout(string, *self); - layout.beginLayout(); - layout.createLine(); - layout.endLayout(); - if (layout.lineCount() == 0) - return 0; - auto cur = layout.lineAt(0).xToCursor(x); - // convert to an utf8 pos; - return QStringView(string).left(cur).toUtf8().size(); - }} - } } thread_local! { diff --git a/sixtyfps_runtime/rendering_backends/testing/lib.rs b/sixtyfps_runtime/rendering_backends/testing/lib.rs index c90069d15..7f8426259 100644 --- a/sixtyfps_runtime/rendering_backends/testing/lib.rs +++ b/sixtyfps_runtime/rendering_backends/testing/lib.rs @@ -18,7 +18,7 @@ You should use the `sixtyfps` crate instead. use image::GenericImageView; use sixtyfps_corelib::component::ComponentRc; -use sixtyfps_corelib::graphics::{FontMetrics, Image, Size}; +use sixtyfps_corelib::graphics::{FontMetrics, Image, Point, Size}; use sixtyfps_corelib::slice::Slice; use sixtyfps_corelib::window::{PlatformWindow, Window}; use sixtyfps_corelib::{ImageInner, Property}; @@ -149,6 +149,14 @@ impl PlatformWindow for TestingWindow { Box::new(TestingFontMetrics::default()) } + fn text_input_byte_offset_for_position( + &self, + _text_input: Pin<&sixtyfps_corelib::items::TextInput>, + _pos: Point, + ) -> usize { + 0 + } + fn as_any(&self) -> &dyn std::any::Any { self } @@ -161,14 +169,6 @@ impl FontMetrics for TestingFontMetrics { fn text_size(&self, text: &str, _max_width: Option) -> Size { Size::new(text.len() as f32 * 10., 10.) } - - fn line_height(&self) -> f32 { - 10. - } - - fn text_offset_for_x_position(&self, _text: &str, _x: f32) -> usize { - 0 - } } /// Initialize the testing backend.