From aa63841e2c80ca8130adf41d25e5d731409b92f4 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 21 Aug 2021 10:31:26 -0700 Subject: Implement textual hit testing --- wgpu/src/backend.rs | 23 +++++++++++++- wgpu/src/text.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) (limited to 'wgpu') diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 4f34045b..fe8ed255 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -7,7 +7,9 @@ use iced_graphics::font; use iced_graphics::layer::Layer; use iced_graphics::{Primitive, Viewport}; use iced_native::mouse; -use iced_native::{Font, HorizontalAlignment, Size, VerticalAlignment}; +use iced_native::{ + Font, HitTestResult, HorizontalAlignment, Size, VerticalAlignment, +}; #[cfg(any(feature = "image_rs", feature = "svg"))] use crate::image; @@ -274,6 +276,25 @@ impl backend::Text for Backend { ) -> (f32, f32) { self.text_pipeline.measure(contents, size, font, bounds) } + + fn hit_test( + &self, + contents: &str, + size: f32, + font: Font, + bounds: Size, + point: iced_native::Point, + nearest_only: bool, + ) -> HitTestResult { + self.text_pipeline.hit_test( + contents, + size, + font, + bounds, + point, + nearest_only, + ) + } } #[cfg(feature = "image_rs")] diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 2b5b94c9..f227cb6f 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -1,5 +1,6 @@ use crate::Transformation; use iced_graphics::font; +use iced_native::HitTestResult; use std::{cell::RefCell, collections::HashMap}; use wgpu_glyph::ab_glyph; @@ -117,6 +118,97 @@ impl Pipeline { } } + pub fn hit_test( + &self, + content: &str, + size: f32, + font: iced_native::Font, + bounds: iced_native::Size, + point: iced_native::Point, + nearest_only: bool, + ) -> HitTestResult { + use wgpu_glyph::GlyphCruncher; + + let wgpu_glyph::FontId(font_id) = self.find_font(font); + + let section = wgpu_glyph::Section { + bounds: (bounds.width, bounds.height), + text: vec![wgpu_glyph::Text { + text: content, + scale: size.into(), + font_id: wgpu_glyph::FontId(font_id), + extra: wgpu_glyph::Extra::default(), + }], + ..Default::default() + }; + + let mut mb = self.measure_brush.borrow_mut(); + + // The underlying type is FontArc, so clones are cheap. + use wgpu_glyph::ab_glyph::{Font, ScaleFont}; + let font = mb.fonts()[font_id].clone().into_scaled(size); + + // Implements an iterator over the glyph bounding boxes. + let bounds = mb.glyphs(section).map( + |wgpu_glyph::SectionGlyph { + byte_index, glyph, .. + }| { + ( + *byte_index, + iced_native::Rectangle::new( + iced_native::Point::new( + glyph.position.x - font.h_side_bearing(glyph.id), + glyph.position.y - font.ascent(), + ), + iced_native::Size::new( + font.h_advance(glyph.id), + font.ascent() - font.descent(), + ), + ), + ) + }, + ); + + // Implements computation of the character index based on the byte index + // within the input string. + let char_index = |byte_index| { + let mut b_count = 0; + for (i, utf8_len) in + content.chars().map(|c| c.len_utf8()).enumerate() + { + if byte_index < (b_count + utf8_len) { + return i; + } + b_count += utf8_len; + } + return byte_index; + }; + + if !nearest_only { + for (idx, bounds) in bounds.clone() { + if bounds.contains(point) { + return HitTestResult::CharOffset(char_index(idx)); + } + } + } + + let (idx, nearest) = bounds.fold( + (0usize, iced_native::Point::ORIGIN), + |acc: (usize, iced_native::Point), (idx, bounds)| { + if bounds.center().distance(point) < acc.1.distance(point) { + (idx, bounds.center()) + } else { + acc + } + }, + ); + + HitTestResult::NearestCharOffset( + char_index(idx), + (point - nearest).into(), + ) + } + pub fn trim_measurement_cache(&mut self) { // TODO: We should probably use a `GlyphCalculator` for this. However, // it uses a lifetimed `GlyphCalculatorGuard` with side-effects on drop. -- cgit From 7614127d3641cf3224798c2f0ff07b6ae57d9a53 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 26 Aug 2021 14:41:33 +0700 Subject: Rename `HitTestResult` to `Hit` ... and also move it to a new `text` module in `iced_core` --- wgpu/src/backend.rs | 7 +++---- wgpu/src/text.rs | 14 +++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'wgpu') diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index fe8ed255..b31bf92c 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -2,14 +2,13 @@ use crate::quad; use crate::text; use crate::triangle; use crate::{Settings, Transformation}; + use iced_graphics::backend; use iced_graphics::font; use iced_graphics::layer::Layer; use iced_graphics::{Primitive, Viewport}; use iced_native::mouse; -use iced_native::{ - Font, HitTestResult, HorizontalAlignment, Size, VerticalAlignment, -}; +use iced_native::{Font, HorizontalAlignment, Size, VerticalAlignment}; #[cfg(any(feature = "image_rs", feature = "svg"))] use crate::image; @@ -285,7 +284,7 @@ impl backend::Text for Backend { bounds: Size, point: iced_native::Point, nearest_only: bool, - ) -> HitTestResult { + ) -> text::Hit { self.text_pipeline.hit_test( contents, size, diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index f227cb6f..ee49ee4b 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -1,9 +1,12 @@ use crate::Transformation; + use iced_graphics::font; -use iced_native::HitTestResult; + use std::{cell::RefCell, collections::HashMap}; use wgpu_glyph::ab_glyph; +pub use iced_native::text::Hit; + #[derive(Debug)] pub struct Pipeline { draw_brush: RefCell>, @@ -126,7 +129,7 @@ impl Pipeline { bounds: iced_native::Size, point: iced_native::Point, nearest_only: bool, - ) -> HitTestResult { + ) -> Hit { use wgpu_glyph::GlyphCruncher; let wgpu_glyph::FontId(font_id) = self.find_font(font); @@ -187,7 +190,7 @@ impl Pipeline { if !nearest_only { for (idx, bounds) in bounds.clone() { if bounds.contains(point) { - return HitTestResult::CharOffset(char_index(idx)); + return Hit::CharOffset(char_index(idx)); } } } @@ -203,10 +206,7 @@ impl Pipeline { }, ); - HitTestResult::NearestCharOffset( - char_index(idx), - (point - nearest).into(), - ) + Hit::NearestCharOffset(char_index(idx), (point - nearest).into()) } pub fn trim_measurement_cache(&mut self) { -- cgit