diff options
| author | 2021-08-26 14:53:15 +0700 | |
|---|---|---|
| committer | 2021-08-26 14:53:15 +0700 | |
| commit | 6821114cae2e41fd2bc69d6fcaee1e8574ac061d (patch) | |
| tree | 5741859eba63251190eb0a901a72ef4e185349e7 /glow/src | |
| parent | 2d65621a3b680457e689b93c800e74f726ffc175 (diff) | |
| parent | 7614127d3641cf3224798c2f0ff07b6ae57d9a53 (diff) | |
| download | iced-6821114cae2e41fd2bc69d6fcaee1e8574ac061d.tar.gz iced-6821114cae2e41fd2bc69d6fcaee1e8574ac061d.tar.bz2 iced-6821114cae2e41fd2bc69d6fcaee1e8574ac061d.zip | |
Merge pull request #670 from twitchyliquid64/text_backend
Refactor textual hit testing into a `renderer::Backend` method
Diffstat (limited to 'glow/src')
| -rw-r--r-- | glow/src/backend.rs | 20 | ||||
| -rw-r--r-- | glow/src/text.rs | 94 | 
2 files changed, 113 insertions, 1 deletions
| diff --git a/glow/src/backend.rs b/glow/src/backend.rs index 1680fc00..37c0ac9d 100644 --- a/glow/src/backend.rs +++ b/glow/src/backend.rs @@ -2,6 +2,7 @@ use crate::quad;  use crate::text;  use crate::triangle;  use crate::{Settings, Transformation, Viewport}; +  use iced_graphics::backend;  use iced_graphics::font;  use iced_graphics::Layer; @@ -211,6 +212,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, +    ) -> text::Hit { +        self.text_pipeline.hit_test( +            contents, +            size, +            font, +            bounds, +            point, +            nearest_only, +        ) +    }  }  #[cfg(feature = "image")] diff --git a/glow/src/text.rs b/glow/src/text.rs index a4c39dfe..d6915d92 100644 --- a/glow/src/text.rs +++ b/glow/src/text.rs @@ -1,8 +1,12 @@  use crate::Transformation; -use glow_glyph::ab_glyph; +  use iced_graphics::font; + +use glow_glyph::ab_glyph;  use std::{cell::RefCell, collections::HashMap}; +pub use iced_native::text::Hit; +  #[derive(Debug)]  pub struct Pipeline {      draw_brush: RefCell<glow_glyph::GlyphBrush>, @@ -109,6 +113,94 @@ 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, +    ) -> Hit { +        use glow_glyph::GlyphCruncher; + +        let glow_glyph::FontId(font_id) = self.find_font(font); + +        let section = glow_glyph::Section { +            bounds: (bounds.width, bounds.height), +            text: vec![glow_glyph::Text { +                text: content, +                scale: size.into(), +                font_id: glow_glyph::FontId(font_id), +                extra: glow_glyph::Extra::default(), +            }], +            ..Default::default() +        }; + +        let mut mb = self.measure_brush.borrow_mut(); + +        // The underlying type is FontArc, so clones are cheap. +        use 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( +            |glow_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 Hit::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 +                } +            }, +        ); + +        Hit::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. | 
