From 33b5a900197e2798a393d6d9a0834039666eddbb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 19 Apr 2023 01:19:56 +0200 Subject: Make basic text shaping the default shaping strategy --- core/src/renderer/null.rs | 2 ++ core/src/text.rs | 22 ++++++++++++++++++++-- core/src/widget/text.rs | 21 +++++++++++++++++++++ graphics/src/backend.rs | 2 ++ graphics/src/geometry/text.rs | 10 ++++++++++ graphics/src/primitive.rs | 9 +++++++++ graphics/src/renderer.rs | 7 ++++++- renderer/src/backend.rs | 11 +++++++++-- tiny_skia/Cargo.toml | 5 ++++- tiny_skia/src/backend.rs | 8 +++++++- tiny_skia/src/geometry.rs | 1 + tiny_skia/src/text.rs | 8 ++++++++ wgpu/Cargo.toml | 2 +- wgpu/src/backend.rs | 6 +++++- wgpu/src/geometry.rs | 1 + wgpu/src/layer.rs | 3 +++ wgpu/src/layer/text.rs | 10 ++++++++++ wgpu/src/text.rs | 7 +++++++ widget/src/checkbox.rs | 2 ++ widget/src/overlay/menu.rs | 1 + widget/src/pick_list.rs | 3 +++ widget/src/radio.rs | 1 + widget/src/text_input.rs | 7 ++++++- widget/src/toggler.rs | 1 + 24 files changed, 140 insertions(+), 10 deletions(-) diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 88c58825..f62a4338 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -62,6 +62,7 @@ impl text::Renderer for Null { _size: f32, _font: Font, _bounds: Size, + _needs_shaping: bool, ) -> (f32, f32) { (0.0, 20.0) } @@ -74,6 +75,7 @@ impl text::Renderer for Null { _bounds: Size, _point: Point, _nearest_only: bool, + _advanced_shape: bool, ) -> Option { None } diff --git a/core/src/text.rs b/core/src/text.rs index 4c72abc3..0111cf31 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -27,6 +27,15 @@ pub struct Text<'a, Font> { /// The vertical alignment of the [`Text`]. pub vertical_alignment: alignment::Vertical, + + /// Whether the [`Text`] needs advanced shaping and font fallback. + /// + /// You will need to enable this flag if the text contains a complex + /// script, the font used needs it, and/or multiple fonts in your system + /// may be needed to display all of the glyphs. + /// + /// Advanced shaping is expensive! You should only enable it when necessary. + pub advanced_shape: bool, } /// The result of hit testing on text. @@ -77,11 +86,19 @@ pub trait Renderer: crate::Renderer { size: f32, font: Self::Font, bounds: Size, + advanced_shape: bool, ) -> (f32, f32); /// Measures the width of the text as if it were laid out in a single line. - fn measure_width(&self, content: &str, size: f32, font: Self::Font) -> f32 { - let (width, _) = self.measure(content, size, font, Size::INFINITY); + fn measure_width( + &self, + content: &str, + size: f32, + font: Self::Font, + advanced_shape: bool, + ) -> f32 { + let (width, _) = + self.measure(content, size, font, Size::INFINITY, advanced_shape); width } @@ -101,6 +118,7 @@ pub trait Renderer: crate::Renderer { bounds: Size, point: Point, nearest_only: bool, + advanced_shape: bool, ) -> Option; /// Loads a [`Self::Font`] from its bytes. diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 3193ba84..2df4556d 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -25,6 +25,7 @@ where vertical_alignment: alignment::Vertical, font: Option, style: ::Style, + advanced_shape: bool, } impl<'a, Renderer> Text<'a, Renderer> @@ -43,6 +44,7 @@ where horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, style: Default::default(), + advanced_shape: false, } } @@ -98,6 +100,20 @@ where self.vertical_alignment = alignment; self } + + /// Enables advanced text shaping and font fallback for the [`Text`]. + /// + /// You will need to enable this if the text contains a complex script, the + /// font used needs it, and/or multiple fonts in your system may be needed + /// to display all of the glyphs. + /// + /// If your text isn't displaying properly, try enabling this! + /// + /// Advanced shaping is expensive! You should only enable it when necessary. + pub fn advanced_shape(mut self) -> Self { + self.advanced_shape = true; + self + } } impl<'a, Message, Renderer> Widget for Text<'a, Renderer> @@ -129,6 +145,7 @@ where size, self.font.unwrap_or_else(|| renderer.default_font()), bounds, + self.advanced_shape, ); let size = limits.resolve(Size::new(width, height)); @@ -156,6 +173,7 @@ where theme.appearance(self.style.clone()), self.horizontal_alignment, self.vertical_alignment, + self.advanced_shape, ); } } @@ -180,6 +198,7 @@ pub fn draw( appearance: Appearance, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, + advanced_shape: bool, ) where Renderer: text::Renderer, { @@ -205,6 +224,7 @@ pub fn draw( font: font.unwrap_or_else(|| renderer.default_font()), horizontal_alignment, vertical_alignment, + advanced_shape, }); } @@ -234,6 +254,7 @@ where vertical_alignment: self.vertical_alignment, font: self.font, style: self.style.clone(), + advanced_shape: self.advanced_shape, } } } diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index dd2888ab..0e107dd5 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -48,6 +48,7 @@ pub trait Text { size: f32, font: Font, bounds: Size, + advanced_shape: bool, ) -> (f32, f32); /// Tests whether the provided point is within the boundaries of [`Text`] @@ -65,6 +66,7 @@ pub trait Text { bounds: Size, point: Point, nearest_only: bool, + advanced_shape: bool, ) -> Option; /// Loads a [`Font`] from its bytes. diff --git a/graphics/src/geometry/text.rs b/graphics/src/geometry/text.rs index 0befd635..f4af9ee4 100644 --- a/graphics/src/geometry/text.rs +++ b/graphics/src/geometry/text.rs @@ -25,6 +25,15 @@ pub struct Text { pub horizontal_alignment: alignment::Horizontal, /// The vertical alignment of the text pub vertical_alignment: alignment::Vertical, + /// Whether the text needs advanced shaping and font fallback. + /// + /// You will need to enable this flag if the text contains a complex + /// script, the font used needs it, and/or multiple fonts in your system + /// may be needed to display all of the glyphs. + /// + /// Advanced shaping is expensive! You should only enable it when + /// necessary. + pub advanced_shape: bool, } impl Default for Text { @@ -37,6 +46,7 @@ impl Default for Text { font: Font::default(), horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, + advanced_shape: false, } } } diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index d6a2c4c4..2d9c221b 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -26,6 +26,15 @@ pub enum Primitive { horizontal_alignment: alignment::Horizontal, /// The vertical alignment of the text vertical_alignment: alignment::Vertical, + /// Whether the text needs advanced shaping and font fallback. + /// + /// You will need to enable this flag if the text contains a complex + /// script, the font used needs it, and/or multiple fonts in your system + /// may be needed to display all of the glyphs. + /// + /// Advanced shaping is expensive! You should only enable it when + /// necessary. + advanced_shape: bool, }, /// A quad primitive Quad { diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 23e594be..558e21dd 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -138,8 +138,10 @@ where size: f32, font: Font, bounds: Size, + needs_shaping: bool, ) -> (f32, f32) { - self.backend().measure(content, size, font, bounds) + self.backend() + .measure(content, size, font, bounds, needs_shaping) } fn hit_test( @@ -150,6 +152,7 @@ where bounds: Size, point: Point, nearest_only: bool, + advanced_shape: bool, ) -> Option { self.backend().hit_test( content, @@ -158,6 +161,7 @@ where bounds, point, nearest_only, + advanced_shape, ) } @@ -174,6 +178,7 @@ where font: text.font, horizontal_alignment: text.horizontal_alignment, vertical_alignment: text.vertical_alignment, + advanced_shape: text.advanced_shape, }); } } diff --git a/renderer/src/backend.rs b/renderer/src/backend.rs index 120dd644..a98e2479 100644 --- a/renderer/src/backend.rs +++ b/renderer/src/backend.rs @@ -48,8 +48,13 @@ impl backend::Text for Backend { size: f32, font: Font, bounds: Size, + advanced_shape: bool, ) -> (f32, f32) { - delegate!(self, backend, backend.measure(contents, size, font, bounds)) + delegate!( + self, + backend, + backend.measure(contents, size, font, bounds, advanced_shape) + ) } fn hit_test( @@ -60,6 +65,7 @@ impl backend::Text for Backend { bounds: Size, position: Point, nearest_only: bool, + advanced_shape: bool, ) -> Option { delegate!( self, @@ -70,7 +76,8 @@ impl backend::Text for Backend { font, bounds, position, - nearest_only + nearest_only, + advanced_shape ) ) } diff --git a/tiny_skia/Cargo.toml b/tiny_skia/Cargo.toml index a3bddc93..32645ac1 100644 --- a/tiny_skia/Cargo.toml +++ b/tiny_skia/Cargo.toml @@ -12,7 +12,6 @@ geometry = ["iced_graphics/geometry"] raw-window-handle = "0.5" softbuffer = "0.2" tiny-skia = "0.9" -cosmic-text = "0.8" bytemuck = "1" rustc-hash = "1.1" kurbo = "0.9" @@ -23,6 +22,10 @@ version = "0.8" path = "../graphics" features = ["tiny-skia"] +[dependencies.cosmic-text] +git = "https://github.com/hecrj/cosmic-text.git" +rev = "ad111a1df10d5da503620f4b841de5d41ebd4e73" + [dependencies.twox-hash] version = "1.6" default-features = false diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 9c69e1d2..0a06f8c9 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -219,6 +219,7 @@ impl Backend { font, horizontal_alignment, vertical_alignment, + advanced_shape, } => { let physical_bounds = (primitive.bounds() + translation) * scale_factor; @@ -238,6 +239,7 @@ impl Backend { *font, *horizontal_alignment, *vertical_alignment, + *advanced_shape, pixels, clip_mask, ); @@ -626,8 +628,10 @@ impl backend::Text for Backend { size: f32, font: Font, bounds: Size, + advanced_shape: bool, ) -> (f32, f32) { - self.text_pipeline.measure(contents, size, font, bounds) + self.text_pipeline + .measure(contents, size, font, bounds, advanced_shape) } fn hit_test( @@ -638,6 +642,7 @@ impl backend::Text for Backend { bounds: Size, point: Point, nearest_only: bool, + advanced_shape: bool, ) -> Option { self.text_pipeline.hit_test( contents, @@ -646,6 +651,7 @@ impl backend::Text for Backend { bounds, point, nearest_only, + advanced_shape, ) } diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 508965ad..7cdac1c8 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -114,6 +114,7 @@ impl Frame { font: text.font, horizontal_alignment: text.horizontal_alignment, vertical_alignment: text.vertical_alignment, + advanced_shape: text.advanced_shape, }); } diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 1246bbd5..603a3e16 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -49,6 +49,7 @@ impl Pipeline { font: Font, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, + advanced_shape: bool, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, ) { @@ -63,6 +64,7 @@ impl Pipeline { content, font, size, + advanced_shape, }; let (_, buffer) = self.render_cache.allocate(font_system, key); @@ -130,6 +132,7 @@ impl Pipeline { size: f32, font: Font, bounds: Size, + advanced_shape: bool, ) -> (f32, f32) { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -140,6 +143,7 @@ impl Pipeline { size, font, bounds, + advanced_shape, }, ); @@ -161,6 +165,7 @@ impl Pipeline { bounds: Size, point: Point, _nearest_only: bool, + advanced_shape: bool, ) -> Option { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -171,6 +176,7 @@ impl Pipeline { size, font, bounds, + advanced_shape, }, ); @@ -390,6 +396,7 @@ impl Cache { .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) .stretch(to_stretch(key.font.stretch)), + !key.advanced_shape, ); let _ = entry.insert(buffer); @@ -420,6 +427,7 @@ struct Key<'a> { size: f32, font: Font, bounds: Size, + advanced_shape: bool, } type KeyHash = u64; diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 9f9bd066..254d32d6 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -44,7 +44,7 @@ path = "../graphics" [dependencies.glyphon] version = "0.2" git = "https://github.com/hecrj/glyphon.git" -rev = "1d26d92b19407c5dabe4625944d4a6babbbf0715" +rev = "446cf0803065b52ba5fb9a30fe0addb6d7b5f9d9" [dependencies.encase] version = "0.3.0" diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 9772781a..d09b2dba 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -354,8 +354,10 @@ impl backend::Text for Backend { size: f32, font: Font, bounds: Size, + advanced_shape: bool, ) -> (f32, f32) { - self.text_pipeline.measure(contents, size, font, bounds) + self.text_pipeline + .measure(contents, size, font, bounds, advanced_shape) } fn hit_test( @@ -366,6 +368,7 @@ impl backend::Text for Backend { bounds: Size, point: Point, nearest_only: bool, + advanced_shape: bool, ) -> Option { self.text_pipeline.hit_test( contents, @@ -374,6 +377,7 @@ impl backend::Text for Backend { bounds, point, nearest_only, + advanced_shape, ) } diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index 59ec31fe..a85875a4 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -334,6 +334,7 @@ impl Frame { font: text.font, horizontal_alignment: text.horizontal_alignment, vertical_alignment: text.vertical_alignment, + advanced_shape: text.advanced_shape, }); } diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index c4723397..7c5b43a3 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -64,6 +64,7 @@ impl<'a> Layer<'a> { font: Font::MONOSPACE, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, + advanced_shape: false, }; overlay.text.push(text); @@ -116,6 +117,7 @@ impl<'a> Layer<'a> { font, horizontal_alignment, vertical_alignment, + advanced_shape, } => { let layer = &mut layers[current_layer]; @@ -127,6 +129,7 @@ impl<'a> Layer<'a> { font: *font, horizontal_alignment: *horizontal_alignment, vertical_alignment: *vertical_alignment, + advanced_shape: *advanced_shape, }); } Primitive::Quad { diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs index fdbdaafb..d36ff273 100644 --- a/wgpu/src/layer/text.rs +++ b/wgpu/src/layer/text.rs @@ -24,4 +24,14 @@ pub struct Text<'a> { /// The vertical alignment of the [`Text`]. pub vertical_alignment: alignment::Vertical, + + /// Whether the text needs advanced shaping and font fallback. + /// + /// You will need to enable this flag if the text contains a complex + /// script, the font used needs it, and/or multiple fonts in your system + /// may be needed to display all of the glyphs. + /// + /// Advanced shaping is expensive! You should only enable it when + /// necessary. + pub advanced_shape: bool, } diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index f01e0b42..f433a5b6 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -83,6 +83,7 @@ impl Pipeline { height: (section.bounds.height * scale_factor) .ceil(), }, + advanced_shape: section.advanced_shape, }, ); @@ -213,6 +214,7 @@ impl Pipeline { size: f32, font: Font, bounds: Size, + advanced_shape: bool, ) -> (f32, f32) { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -223,6 +225,7 @@ impl Pipeline { size, font, bounds, + advanced_shape, }, ); @@ -244,6 +247,7 @@ impl Pipeline { bounds: Size, point: Point, _nearest_only: bool, + advanced_shape: bool, ) -> Option { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -254,6 +258,7 @@ impl Pipeline { size, font, bounds, + advanced_shape, }, ); @@ -364,6 +369,7 @@ impl Cache { .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) .stretch(to_stretch(key.font.stretch)), + !key.advanced_shape, ); let _ = entry.insert(buffer); @@ -388,6 +394,7 @@ struct Key<'a> { size: f32, font: Font, bounds: Size, + advanced_shape: bool, } type KeyHash = u64; diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 6505cfdd..e28f76af 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -273,6 +273,7 @@ where color: custom_style.icon_color, horizontal_alignment: alignment::Horizontal::Center, vertical_alignment: alignment::Vertical::Center, + advanced_shape: true, }); } } @@ -292,6 +293,7 @@ where }, alignment::Horizontal::Left, alignment::Vertical::Center, + false, ); } } diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index c322c8ba..c904730d 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -500,6 +500,7 @@ where }, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, + advanced_shape: false, }); } } diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index cd23cdd2..d44f4cae 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -366,6 +366,7 @@ where text_size, font.unwrap_or_else(|| renderer.default_font()), Size::new(f32::INFINITY, f32::INFINITY), + false, ); width.round() @@ -628,6 +629,7 @@ pub fn draw<'a, T, Renderer>( }, horizontal_alignment: alignment::Horizontal::Right, vertical_alignment: alignment::Vertical::Center, + advanced_shape: false, }); } @@ -653,6 +655,7 @@ pub fn draw<'a, T, Renderer>( }, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, + advanced_shape: false, }); } } diff --git a/widget/src/radio.rs b/widget/src/radio.rs index c3229aed..b685c1a1 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -309,6 +309,7 @@ where }, alignment::Horizontal::Left, alignment::Vertical::Center, + false, ); } } diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 9db382f7..abf858ca 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -463,6 +463,7 @@ where &icon.code_point.to_string(), icon.size.unwrap_or_else(|| renderer.default_size()), icon.font, + true, ); let mut text_node = layout::Node::new( @@ -975,6 +976,7 @@ pub fn draw( bounds: icon_layout.bounds(), horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, + advanced_shape: true, }); } @@ -1079,6 +1081,7 @@ pub fn draw( if text.is_empty() { placeholder } else { &text }, size, font, + true, ); let render = |renderer: &mut Renderer| { @@ -1106,6 +1109,7 @@ pub fn draw( size, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, + advanced_shape: true, }); }; @@ -1311,7 +1315,7 @@ where let text_before_cursor = value.until(cursor_index).to_string(); let text_value_width = - renderer.measure_width(&text_before_cursor, size, font); + renderer.measure_width(&text_before_cursor, size, font, true); let offset = ((text_value_width + 5.0) - text_bounds.width).max(0.0); @@ -1346,6 +1350,7 @@ where Size::INFINITY, Point::new(x + offset, text_bounds.height / 2.0), true, + true, ) .map(text::Hit::cursor)?; diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index 713a9c30..d3033ddb 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -249,6 +249,7 @@ where Default::default(), self.text_alignment, alignment::Vertical::Center, + false, ); } -- cgit From 4bd290afe7d81d9aaf7467b3ce91491f6600261a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 19 Apr 2023 02:00:45 +0200 Subject: Introduce `text::Shaping` enum and replace magic boolean --- core/src/renderer/null.rs | 4 ++-- core/src/text.rs | 30 +++++++++++++++++++++------ core/src/widget/text.rs | 28 +++++++++---------------- examples/todos/src/main.rs | 3 ++- graphics/src/backend.rs | 4 ++-- graphics/src/geometry/text.rs | 18 ++++++----------- graphics/src/primitive.rs | 20 +++++++----------- graphics/src/renderer.rs | 11 +++++----- renderer/src/backend.rs | 8 ++++---- tiny_skia/src/backend.rs | 12 +++++------ tiny_skia/src/geometry.rs | 2 +- tiny_skia/src/text.rs | 18 ++++++++--------- wgpu/fonts/Iced-Icons.ttf | Bin 5108 -> 5108 bytes wgpu/src/backend.rs | 8 ++++---- wgpu/src/geometry.rs | 2 +- wgpu/src/layer.rs | 7 ++++--- wgpu/src/layer/text.rs | 12 +++-------- wgpu/src/text.rs | 16 +++++++-------- widget/src/checkbox.rs | 19 ++++++++++++++--- widget/src/overlay/menu.rs | 13 +++++++++++- widget/src/pick_list.rs | 46 +++++++++++++++++++++++++++++++++--------- widget/src/radio.rs | 22 ++++++++++++++++---- widget/src/text.rs | 1 + widget/src/text_input.rs | 18 ++++++++++------- widget/src/toggler.rs | 13 ++++++++++-- 25 files changed, 203 insertions(+), 132 deletions(-) diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index f62a4338..22afb058 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -62,7 +62,7 @@ impl text::Renderer for Null { _size: f32, _font: Font, _bounds: Size, - _needs_shaping: bool, + _shaping: text::Shaping, ) -> (f32, f32) { (0.0, 20.0) } @@ -73,9 +73,9 @@ impl text::Renderer for Null { _size: f32, _font: Self::Font, _bounds: Size, + _shaping: text::Shaping, _point: Point, _nearest_only: bool, - _advanced_shape: bool, ) -> Option { None } diff --git a/core/src/text.rs b/core/src/text.rs index 0111cf31..c59d8fce 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -28,14 +28,32 @@ pub struct Text<'a, Font> { /// The vertical alignment of the [`Text`]. pub vertical_alignment: alignment::Vertical, - /// Whether the [`Text`] needs advanced shaping and font fallback. + /// The [`Shaping`] strategy of the [`Text`]. + pub shaping: Shaping, +} + +/// The shaping strategy of some text. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum Shaping { + /// No shaping and no font fallback. + /// + /// This shaping strategy is very cheap, but it will not display complex + /// scripts properly nor try to find missing glyphs in your system fonts. + /// + /// You should use this strategy when you have complete control of the text + /// and the font you are displaying in your application. + /// + /// This is the default. + #[default] + Basic, + /// Advanced text shaping and font fallback. /// /// You will need to enable this flag if the text contains a complex /// script, the font used needs it, and/or multiple fonts in your system /// may be needed to display all of the glyphs. /// /// Advanced shaping is expensive! You should only enable it when necessary. - pub advanced_shape: bool, + Advanced, } /// The result of hit testing on text. @@ -86,7 +104,7 @@ pub trait Renderer: crate::Renderer { size: f32, font: Self::Font, bounds: Size, - advanced_shape: bool, + shaping: Shaping, ) -> (f32, f32); /// Measures the width of the text as if it were laid out in a single line. @@ -95,10 +113,10 @@ pub trait Renderer: crate::Renderer { content: &str, size: f32, font: Self::Font, - advanced_shape: bool, + shaping: Shaping, ) -> f32 { let (width, _) = - self.measure(content, size, font, Size::INFINITY, advanced_shape); + self.measure(content, size, font, Size::INFINITY, shaping); width } @@ -116,9 +134,9 @@ pub trait Renderer: crate::Renderer { size: f32, font: Self::Font, bounds: Size, + shaping: Shaping, point: Point, nearest_only: bool, - advanced_shape: bool, ) -> Option; /// Loads a [`Self::Font`] from its bytes. diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 2df4556d..f0392168 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -24,8 +24,8 @@ where horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, font: Option, + shaping: text::Shaping, style: ::Style, - advanced_shape: bool, } impl<'a, Renderer> Text<'a, Renderer> @@ -43,8 +43,8 @@ where height: Length::Shrink, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, + shaping: text::Shaping::Basic, style: Default::default(), - advanced_shape: false, } } @@ -101,17 +101,9 @@ where self } - /// Enables advanced text shaping and font fallback for the [`Text`]. - /// - /// You will need to enable this if the text contains a complex script, the - /// font used needs it, and/or multiple fonts in your system may be needed - /// to display all of the glyphs. - /// - /// If your text isn't displaying properly, try enabling this! - /// - /// Advanced shaping is expensive! You should only enable it when necessary. - pub fn advanced_shape(mut self) -> Self { - self.advanced_shape = true; + /// Sets the [`text::Shaping`] strategy of the [`Text`]. + pub fn shaping(mut self, shaping: text::Shaping) -> Self { + self.shaping = shaping; self } } @@ -145,7 +137,7 @@ where size, self.font.unwrap_or_else(|| renderer.default_font()), bounds, - self.advanced_shape, + self.shaping, ); let size = limits.resolve(Size::new(width, height)); @@ -173,7 +165,7 @@ where theme.appearance(self.style.clone()), self.horizontal_alignment, self.vertical_alignment, - self.advanced_shape, + self.shaping, ); } } @@ -198,7 +190,7 @@ pub fn draw( appearance: Appearance, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, - advanced_shape: bool, + shaping: text::Shaping, ) where Renderer: text::Renderer, { @@ -224,7 +216,7 @@ pub fn draw( font: font.unwrap_or_else(|| renderer.default_font()), horizontal_alignment, vertical_alignment, - advanced_shape, + shaping, }); } @@ -254,7 +246,7 @@ where vertical_alignment: self.vertical_alignment, font: self.font, style: self.style.clone(), - advanced_shape: self.advanced_shape, + shaping: self.shaping, } } } diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 8bc7be09..6ad7b4fb 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -364,7 +364,8 @@ impl Task { self.completed, TaskMessage::Completed, ) - .width(Length::Fill); + .width(Length::Fill) + .text_shaping(text::Shaping::Advanced); row![ checkbox, diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index 0e107dd5..0c2a6d30 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -48,7 +48,7 @@ pub trait Text { size: f32, font: Font, bounds: Size, - advanced_shape: bool, + shaping: text::Shaping, ) -> (f32, f32); /// Tests whether the provided point is within the boundaries of [`Text`] @@ -64,9 +64,9 @@ pub trait Text { size: f32, font: Font, bounds: Size, + shaping: text::Shaping, point: Point, nearest_only: bool, - advanced_shape: bool, ) -> Option; /// Loads a [`Font`] from its bytes. diff --git a/graphics/src/geometry/text.rs b/graphics/src/geometry/text.rs index f4af9ee4..0f731e74 100644 --- a/graphics/src/geometry/text.rs +++ b/graphics/src/geometry/text.rs @@ -1,5 +1,6 @@ -use iced_core::alignment; -use iced_core::{Color, Font, Point}; +use crate::core::alignment; +use crate::core::text::Shaping; +use crate::core::{Color, Font, Point}; /// A bunch of text that can be drawn to a canvas #[derive(Debug, Clone)] @@ -25,15 +26,8 @@ pub struct Text { pub horizontal_alignment: alignment::Horizontal, /// The vertical alignment of the text pub vertical_alignment: alignment::Vertical, - /// Whether the text needs advanced shaping and font fallback. - /// - /// You will need to enable this flag if the text contains a complex - /// script, the font used needs it, and/or multiple fonts in your system - /// may be needed to display all of the glyphs. - /// - /// Advanced shaping is expensive! You should only enable it when - /// necessary. - pub advanced_shape: bool, + /// The shaping strategy of the text. + pub shaping: Shaping, } impl Default for Text { @@ -46,7 +40,7 @@ impl Default for Text { font: Font::default(), horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, - advanced_shape: false, + shaping: Shaping::Basic, } } } diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 2d9c221b..db237035 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -1,7 +1,8 @@ -use iced_core::alignment; -use iced_core::image; -use iced_core::svg; -use iced_core::{Background, Color, Font, Gradient, Rectangle, Size, Vector}; +use crate::core::alignment; +use crate::core::image; +use crate::core::svg; +use crate::core::text; +use crate::core::{Background, Color, Font, Gradient, Rectangle, Size, Vector}; use bytemuck::{Pod, Zeroable}; use std::sync::Arc; @@ -26,15 +27,8 @@ pub enum Primitive { horizontal_alignment: alignment::Horizontal, /// The vertical alignment of the text vertical_alignment: alignment::Vertical, - /// Whether the text needs advanced shaping and font fallback. - /// - /// You will need to enable this flag if the text contains a complex - /// script, the font used needs it, and/or multiple fonts in your system - /// may be needed to display all of the glyphs. - /// - /// Advanced shaping is expensive! You should only enable it when - /// necessary. - advanced_shape: bool, + /// The shaping strategy of the text. + shaping: text::Shaping, }, /// A quad primitive Quad { diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 558e21dd..605286d6 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -138,10 +138,9 @@ where size: f32, font: Font, bounds: Size, - needs_shaping: bool, + shaping: text::Shaping, ) -> (f32, f32) { - self.backend() - .measure(content, size, font, bounds, needs_shaping) + self.backend().measure(content, size, font, bounds, shaping) } fn hit_test( @@ -150,18 +149,18 @@ where size: f32, font: Font, bounds: Size, + shaping: text::Shaping, point: Point, nearest_only: bool, - advanced_shape: bool, ) -> Option { self.backend().hit_test( content, size, font, bounds, + shaping, point, nearest_only, - advanced_shape, ) } @@ -178,7 +177,7 @@ where font: text.font, horizontal_alignment: text.horizontal_alignment, vertical_alignment: text.vertical_alignment, - advanced_shape: text.advanced_shape, + shaping: text.shaping, }); } } diff --git a/renderer/src/backend.rs b/renderer/src/backend.rs index a98e2479..70b146f4 100644 --- a/renderer/src/backend.rs +++ b/renderer/src/backend.rs @@ -48,12 +48,12 @@ impl backend::Text for Backend { size: f32, font: Font, bounds: Size, - advanced_shape: bool, + shaping: text::Shaping, ) -> (f32, f32) { delegate!( self, backend, - backend.measure(contents, size, font, bounds, advanced_shape) + backend.measure(contents, size, font, bounds, shaping) ) } @@ -63,9 +63,9 @@ impl backend::Text for Backend { size: f32, font: Font, bounds: Size, + shaping: text::Shaping, position: Point, nearest_only: bool, - advanced_shape: bool, ) -> Option { delegate!( self, @@ -75,9 +75,9 @@ impl backend::Text for Backend { size, font, bounds, + shaping, position, nearest_only, - advanced_shape ) ) } diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 0a06f8c9..3ef7e717 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -219,7 +219,7 @@ impl Backend { font, horizontal_alignment, vertical_alignment, - advanced_shape, + shaping, } => { let physical_bounds = (primitive.bounds() + translation) * scale_factor; @@ -239,7 +239,7 @@ impl Backend { *font, *horizontal_alignment, *vertical_alignment, - *advanced_shape, + *shaping, pixels, clip_mask, ); @@ -628,10 +628,10 @@ impl backend::Text for Backend { size: f32, font: Font, bounds: Size, - advanced_shape: bool, + shaping: text::Shaping, ) -> (f32, f32) { self.text_pipeline - .measure(contents, size, font, bounds, advanced_shape) + .measure(contents, size, font, bounds, shaping) } fn hit_test( @@ -640,18 +640,18 @@ impl backend::Text for Backend { size: f32, font: Font, bounds: Size, + shaping: text::Shaping, point: Point, nearest_only: bool, - advanced_shape: bool, ) -> Option { self.text_pipeline.hit_test( contents, size, font, bounds, + shaping, point, nearest_only, - advanced_shape, ) } diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 7cdac1c8..7963fd89 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -114,7 +114,7 @@ impl Frame { font: text.font, horizontal_alignment: text.horizontal_alignment, vertical_alignment: text.vertical_alignment, - advanced_shape: text.advanced_shape, + shaping: text.shaping, }); } diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 603a3e16..a63da193 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -1,6 +1,6 @@ use crate::core::alignment; use crate::core::font::{self, Font}; -use crate::core::text::Hit; +use crate::core::text::{Hit, Shaping}; use crate::core::{Color, Point, Rectangle, Size}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -49,7 +49,7 @@ impl Pipeline { font: Font, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, - advanced_shape: bool, + shaping: Shaping, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, ) { @@ -64,7 +64,7 @@ impl Pipeline { content, font, size, - advanced_shape, + shaping, }; let (_, buffer) = self.render_cache.allocate(font_system, key); @@ -132,7 +132,7 @@ impl Pipeline { size: f32, font: Font, bounds: Size, - advanced_shape: bool, + shaping: Shaping, ) -> (f32, f32) { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -143,7 +143,7 @@ impl Pipeline { size, font, bounds, - advanced_shape, + shaping, }, ); @@ -163,9 +163,9 @@ impl Pipeline { size: f32, font: Font, bounds: Size, + shaping: Shaping, point: Point, _nearest_only: bool, - advanced_shape: bool, ) -> Option { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -176,7 +176,7 @@ impl Pipeline { size, font, bounds, - advanced_shape, + shaping, }, ); @@ -396,7 +396,7 @@ impl Cache { .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) .stretch(to_stretch(key.font.stretch)), - !key.advanced_shape, + matches!(key.shaping, Shaping::Basic), ); let _ = entry.insert(buffer); @@ -427,7 +427,7 @@ struct Key<'a> { size: f32, font: Font, bounds: Size, - advanced_shape: bool, + shaping: Shaping, } type KeyHash = u64; diff --git a/wgpu/fonts/Iced-Icons.ttf b/wgpu/fonts/Iced-Icons.ttf index 7112f086..e3273141 100644 Binary files a/wgpu/fonts/Iced-Icons.ttf and b/wgpu/fonts/Iced-Icons.ttf differ diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index d09b2dba..6b847aff 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -354,10 +354,10 @@ impl backend::Text for Backend { size: f32, font: Font, bounds: Size, - advanced_shape: bool, + shaping: core::text::Shaping, ) -> (f32, f32) { self.text_pipeline - .measure(contents, size, font, bounds, advanced_shape) + .measure(contents, size, font, bounds, shaping) } fn hit_test( @@ -366,18 +366,18 @@ impl backend::Text for Backend { size: f32, font: Font, bounds: Size, + shaping: core::text::Shaping, point: Point, nearest_only: bool, - advanced_shape: bool, ) -> Option { self.text_pipeline.hit_test( contents, size, font, bounds, + shaping, point, nearest_only, - advanced_shape, ) } diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index a85875a4..f6397ab7 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -334,7 +334,7 @@ impl Frame { font: text.font, horizontal_alignment: text.horizontal_alignment, vertical_alignment: text.vertical_alignment, - advanced_shape: text.advanced_shape, + shaping: text.shaping, }); } diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 7c5b43a3..b9fd044e 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -10,6 +10,7 @@ pub use mesh::Mesh; pub use quad::Quad; pub use text::Text; +use crate::core; use crate::core::alignment; use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector}; use crate::graphics::{Primitive, Viewport}; @@ -64,7 +65,7 @@ impl<'a> Layer<'a> { font: Font::MONOSPACE, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, - advanced_shape: false, + shaping: core::text::Shaping::Basic, }; overlay.text.push(text); @@ -117,7 +118,7 @@ impl<'a> Layer<'a> { font, horizontal_alignment, vertical_alignment, - advanced_shape, + shaping, } => { let layer = &mut layers[current_layer]; @@ -129,7 +130,7 @@ impl<'a> Layer<'a> { font: *font, horizontal_alignment: *horizontal_alignment, vertical_alignment: *vertical_alignment, - advanced_shape: *advanced_shape, + shaping: *shaping, }); } Primitive::Quad { diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs index d36ff273..665f7188 100644 --- a/wgpu/src/layer/text.rs +++ b/wgpu/src/layer/text.rs @@ -1,4 +1,5 @@ use crate::core::alignment; +use crate::core::text; use crate::core::{Color, Font, Rectangle}; /// A paragraph of text. @@ -25,13 +26,6 @@ pub struct Text<'a> { /// The vertical alignment of the [`Text`]. pub vertical_alignment: alignment::Vertical, - /// Whether the text needs advanced shaping and font fallback. - /// - /// You will need to enable this flag if the text contains a complex - /// script, the font used needs it, and/or multiple fonts in your system - /// may be needed to display all of the glyphs. - /// - /// Advanced shaping is expensive! You should only enable it when - /// necessary. - pub advanced_shape: bool, + /// The shaping strategy of the text. + pub shaping: text::Shaping, } diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index f433a5b6..fc126125 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -1,6 +1,6 @@ use crate::core::alignment; use crate::core::font::{self, Font}; -use crate::core::text::Hit; +use crate::core::text::{Hit, Shaping}; use crate::core::{Point, Rectangle, Size}; use crate::layer::Text; @@ -83,7 +83,7 @@ impl Pipeline { height: (section.bounds.height * scale_factor) .ceil(), }, - advanced_shape: section.advanced_shape, + shaping: section.shaping, }, ); @@ -214,7 +214,7 @@ impl Pipeline { size: f32, font: Font, bounds: Size, - advanced_shape: bool, + shaping: Shaping, ) -> (f32, f32) { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -225,7 +225,7 @@ impl Pipeline { size, font, bounds, - advanced_shape, + shaping, }, ); @@ -245,9 +245,9 @@ impl Pipeline { size: f32, font: Font, bounds: Size, + shaping: Shaping, point: Point, _nearest_only: bool, - advanced_shape: bool, ) -> Option { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -258,7 +258,7 @@ impl Pipeline { size, font, bounds, - advanced_shape, + shaping, }, ); @@ -369,7 +369,7 @@ impl Cache { .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) .stretch(to_stretch(key.font.stretch)), - !key.advanced_shape, + matches!(key.shaping, Shaping::Basic), ); let _ = entry.insert(buffer); @@ -394,7 +394,7 @@ struct Key<'a> { size: f32, font: Font, bounds: Size, - advanced_shape: bool, + shaping: Shaping, } type KeyHash = u64; diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index e28f76af..2a09b8fd 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -46,6 +46,7 @@ where size: f32, spacing: f32, text_size: Option, + text_shaping: text::Shaping, font: Option, icon: Icon, style: ::Style, @@ -82,11 +83,13 @@ where size: Self::DEFAULT_SIZE, spacing: Self::DEFAULT_SPACING, text_size: None, + text_shaping: text::Shaping::Basic, font: None, icon: Icon { font: Renderer::ICON_FONT, code_point: Renderer::CHECKMARK_ICON, size: None, + shaping: text::Shaping::Basic, }, style: Default::default(), } @@ -116,6 +119,12 @@ where self } + /// Sets the [`text::Shaping`] strategy of the [`Checkbox`]. + pub fn text_shaping(mut self, shaping: text::Shaping) -> Self { + self.text_shaping = shaping; + self + } + /// Sets the [`Font`] of the text of the [`Checkbox`]. /// /// [`Font`]: crate::text::Renderer::Font @@ -171,7 +180,8 @@ where .size( self.text_size .unwrap_or_else(|| renderer.default_size()), - ), + ) + .shaping(self.text_shaping), ) .layout(renderer, limits) } @@ -257,6 +267,7 @@ where font, code_point, size, + shaping, } = &self.icon; let size = size.unwrap_or(bounds.height * 0.7); @@ -273,7 +284,7 @@ where color: custom_style.icon_color, horizontal_alignment: alignment::Horizontal::Center, vertical_alignment: alignment::Vertical::Center, - advanced_shape: true, + shaping: *shaping, }); } } @@ -293,7 +304,7 @@ where }, alignment::Horizontal::Left, alignment::Vertical::Center, - false, + self.text_shaping, ); } } @@ -322,4 +333,6 @@ pub struct Icon { pub code_point: char, /// Font size of the content. pub size: Option, + /// The shaping strategy of the icon. + pub shaping: text::Shaping, } diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index c904730d..7de3cbae 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -31,6 +31,7 @@ where width: f32, padding: Padding, text_size: Option, + text_shaping: text::Shaping, font: Option, style: ::Style, } @@ -58,6 +59,7 @@ where width: 0.0, padding: Padding::ZERO, text_size: None, + text_shaping: text::Shaping::Basic, font: None, style: Default::default(), } @@ -81,6 +83,12 @@ where self } + /// Sets the [`text::Shaping`] strategy of the [`Menu`]. + pub fn text_shaping(mut self, shaping: text::Shaping) -> Self { + self.text_shaping = shaping; + self + } + /// Sets the font of the [`Menu`]. pub fn font(mut self, font: impl Into) -> Self { self.font = Some(font.into()); @@ -168,6 +176,7 @@ where padding, font, text_size, + text_shaping, style, } = menu; @@ -177,6 +186,7 @@ where last_selection, font, text_size, + text_shaping, padding, style: style.clone(), })); @@ -311,6 +321,7 @@ where last_selection: &'a mut Option, padding: Padding, text_size: Option, + text_shaping: text::Shaping, font: Option, style: ::Style, } @@ -500,7 +511,7 @@ where }, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, - advanced_shape: false, + shaping: self.text_shaping, }); } } diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index d44f4cae..c0cb2946 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -36,6 +36,7 @@ where width: Length, padding: Padding, text_size: Option, + text_shaping: text::Shaping, font: Option, handle: Handle, style: ::Style, @@ -71,6 +72,7 @@ where width: Length::Shrink, padding: Self::DEFAULT_PADDING, text_size: None, + text_shaping: text::Shaping::Basic, font: None, handle: Default::default(), style: Default::default(), @@ -101,6 +103,12 @@ where self } + /// Sets the [`text::Shaping`] strategy of the [`PickList`]. + pub fn text_shaping(mut self, shaping: text::Shaping) -> Self { + self.text_shaping = shaping; + self + } + /// Sets the font of the [`PickList`]. pub fn font(mut self, font: impl Into) -> Self { self.font = Some(font.into()); @@ -164,6 +172,7 @@ where self.width, self.padding, self.text_size, + self.text_shaping, self.font, self.placeholder.as_deref(), &self.options, @@ -221,6 +230,7 @@ where cursor_position, self.padding, self.text_size, + self.text_shaping, font, self.placeholder.as_deref(), self.selected.as_ref(), @@ -243,6 +253,7 @@ where state, self.padding, self.text_size, + self.text_shaping, self.font.unwrap_or_else(|| renderer.default_font()), &self.options, self.style.clone(), @@ -336,6 +347,8 @@ pub struct Icon { pub code_point: char, /// Font size of the content. pub size: Option, + /// The shaping strategy of the icon. + pub shaping: text::Shaping, } /// Computes the layout of a [`PickList`]. @@ -345,6 +358,7 @@ pub fn layout( width: Length, padding: Padding, text_size: Option, + text_shaping: text::Shaping, font: Option, placeholder: Option<&str>, options: &[T], @@ -366,7 +380,7 @@ where text_size, font.unwrap_or_else(|| renderer.default_font()), Size::new(f32::INFINITY, f32::INFINITY), - false, + text_shaping, ); width.round() @@ -516,6 +530,7 @@ pub fn overlay<'a, T, Message, Renderer>( state: &'a mut State, padding: Padding, text_size: Option, + text_shaping: text::Shaping, font: Renderer::Font, options: &'a [T], style: ::Style, @@ -543,6 +558,7 @@ where .width(bounds.width) .padding(padding) .font(font) + .text_shaping(text_shaping) .style(style); if let Some(text_size) = text_size { @@ -563,6 +579,7 @@ pub fn draw<'a, T, Renderer>( cursor_position: Point, padding: Padding, text_size: Option, + text_shaping: text::Shaping, font: Renderer::Font, placeholder: Option<&str>, selected: Option<&T>, @@ -595,25 +612,34 @@ pub fn draw<'a, T, Renderer>( ); let handle = match handle { - Handle::Arrow { size } => { - Some((Renderer::ICON_FONT, Renderer::ARROW_DOWN_ICON, *size)) - } + Handle::Arrow { size } => Some(( + Renderer::ICON_FONT, + Renderer::ARROW_DOWN_ICON, + *size, + text::Shaping::Basic, + )), Handle::Static(Icon { font, code_point, size, - }) => Some((*font, *code_point, *size)), + shaping, + }) => Some((*font, *code_point, *size, *shaping)), Handle::Dynamic { open, closed } => { if state().is_open { - Some((open.font, open.code_point, open.size)) + Some((open.font, open.code_point, open.size, open.shaping)) } else { - Some((closed.font, closed.code_point, closed.size)) + Some(( + closed.font, + closed.code_point, + closed.size, + closed.shaping, + )) } } Handle::None => None, }; - if let Some((font, code_point, size)) = handle { + if let Some((font, code_point, size, shaping)) = handle { let size = size.unwrap_or_else(|| renderer.default_size()); renderer.fill_text(Text { @@ -629,7 +655,7 @@ pub fn draw<'a, T, Renderer>( }, horizontal_alignment: alignment::Horizontal::Right, vertical_alignment: alignment::Vertical::Center, - advanced_shape: false, + shaping, }); } @@ -655,7 +681,7 @@ pub fn draw<'a, T, Renderer>( }, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, - advanced_shape: false, + shaping: text_shaping, }); } } diff --git a/widget/src/radio.rs b/widget/src/radio.rs index b685c1a1..f62f4703 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -81,6 +81,7 @@ where size: f32, spacing: f32, text_size: Option, + text_shaping: text::Shaping, font: Option, style: ::Style, } @@ -123,6 +124,7 @@ where size: Self::DEFAULT_SIZE, spacing: Self::DEFAULT_SPACING, //15 text_size: None, + text_shaping: text::Shaping::Basic, font: None, style: Default::default(), } @@ -152,6 +154,12 @@ where self } + /// Sets the [`text::Shaping`] strategy of the [`Radio`] button. + pub fn text_shaping(mut self, shaping: text::Shaping) -> Self { + self.text_shaping = shaping; + self + } + /// Sets the text font of the [`Radio`] button. pub fn font(mut self, font: impl Into) -> Self { self.font = Some(font.into()); @@ -192,9 +200,15 @@ where .spacing(self.spacing) .align_items(Alignment::Center) .push(Row::new().width(self.size).height(self.size)) - .push(Text::new(&self.label).width(self.width).size( - self.text_size.unwrap_or_else(|| renderer.default_size()), - )) + .push( + Text::new(&self.label) + .width(self.width) + .size( + self.text_size + .unwrap_or_else(|| renderer.default_size()), + ) + .shaping(self.text_shaping), + ) .layout(renderer, limits) } @@ -309,7 +323,7 @@ where }, alignment::Horizontal::Left, alignment::Vertical::Center, - false, + self.text_shaping, ); } } diff --git a/widget/src/text.rs b/widget/src/text.rs index 04c31edc..50aa1370 100644 --- a/widget/src/text.rs +++ b/widget/src/text.rs @@ -1,3 +1,4 @@ +pub use crate::core::text::Shaping; pub use crate::core::widget::text::*; pub type Text<'a, Renderer = crate::Renderer> = diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index abf858ca..32d0b1f8 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -463,7 +463,7 @@ where &icon.code_point.to_string(), icon.size.unwrap_or_else(|| renderer.default_size()), icon.font, - true, + text::Shaping::Advanced, ); let mut text_node = layout::Node::new( @@ -976,7 +976,7 @@ pub fn draw( bounds: icon_layout.bounds(), horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, - advanced_shape: true, + shaping: text::Shaping::Advanced, }); } @@ -1081,7 +1081,7 @@ pub fn draw( if text.is_empty() { placeholder } else { &text }, size, font, - true, + text::Shaping::Advanced, ); let render = |renderer: &mut Renderer| { @@ -1109,7 +1109,7 @@ pub fn draw( size, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, - advanced_shape: true, + shaping: text::Shaping::Advanced, }); }; @@ -1314,8 +1314,12 @@ where { let text_before_cursor = value.until(cursor_index).to_string(); - let text_value_width = - renderer.measure_width(&text_before_cursor, size, font, true); + let text_value_width = renderer.measure_width( + &text_before_cursor, + size, + font, + text::Shaping::Advanced, + ); let offset = ((text_value_width + 5.0) - text_bounds.width).max(0.0); @@ -1348,9 +1352,9 @@ where size, font, Size::INFINITY, + text::Shaping::Advanced, Point::new(x + offset, text_bounds.height / 2.0), true, - true, ) .map(text::Hit::cursor)?; diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index d3033ddb..639bbb3b 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -43,6 +43,7 @@ where size: f32, text_size: Option, text_alignment: alignment::Horizontal, + text_shaping: text::Shaping, spacing: f32, font: Option, style: ::Style, @@ -80,6 +81,7 @@ where size: Self::DEFAULT_SIZE, text_size: None, text_alignment: alignment::Horizontal::Left, + text_shaping: text::Shaping::Basic, spacing: 0.0, font: None, style: Default::default(), @@ -110,6 +112,12 @@ where self } + /// Sets the [`text::Shaping`] strategy of the [`Toggler`]. + pub fn text_shaping(mut self, shaping: text::Shaping) -> Self { + self.text_shaping = shaping; + self + } + /// Sets the spacing between the [`Toggler`] and the text. pub fn spacing(mut self, spacing: impl Into) -> Self { self.spacing = spacing.into().0; @@ -167,7 +175,8 @@ where .size( self.text_size .unwrap_or_else(|| renderer.default_size()), - ), + ) + .shaping(self.text_shaping), ); } @@ -249,7 +258,7 @@ where Default::default(), self.text_alignment, alignment::Vertical::Center, - false, + self.text_shaping, ); } -- cgit From edf3432bf5176be13437b9fd5d25b890ec9dbe69 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 2 May 2023 00:58:33 +0200 Subject: Update `glyphon` and `cosmic-text` --- examples/checkbox/src/main.rs | 3 ++- tiny_skia/Cargo.toml | 2 +- tiny_skia/src/text.rs | 9 ++++++++- wgpu/Cargo.toml | 2 +- wgpu/src/text.rs | 9 ++++++++- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/examples/checkbox/src/main.rs b/examples/checkbox/src/main.rs index c5f3c134..5852e978 100644 --- a/examples/checkbox/src/main.rs +++ b/examples/checkbox/src/main.rs @@ -1,6 +1,6 @@ use iced::executor; use iced::font::{self, Font}; -use iced::widget::{checkbox, column, container}; +use iced::widget::{checkbox, column, container, text}; use iced::{Application, Command, Element, Length, Settings, Theme}; const ICON_FONT: Font = Font::with_name("icons"); @@ -59,6 +59,7 @@ impl Application for Example { font: ICON_FONT, code_point: '\u{e901}', size: None, + shaping: text::Shaping::Basic, }); let content = column![default_checkbox, custom_checkbox].spacing(22); diff --git a/tiny_skia/Cargo.toml b/tiny_skia/Cargo.toml index 32645ac1..400eee6a 100644 --- a/tiny_skia/Cargo.toml +++ b/tiny_skia/Cargo.toml @@ -24,7 +24,7 @@ features = ["tiny-skia"] [dependencies.cosmic-text] git = "https://github.com/hecrj/cosmic-text.git" -rev = "ad111a1df10d5da503620f4b841de5d41ebd4e73" +rev = "b85d6a4f2376f8a8a7dadc0f8bcb89d4db10a1c9" [dependencies.twox-hash] version = "1.6" diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index a63da193..58079cc0 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -229,6 +229,13 @@ fn to_stretch(stretch: font::Stretch) -> cosmic_text::Stretch { } } +fn to_shaping(shaping: Shaping) -> cosmic_text::Shaping { + match shaping { + Shaping::Basic => cosmic_text::Shaping::Basic, + Shaping::Advanced => cosmic_text::Shaping::Advanced, + } +} + #[derive(Debug, Clone, Default)] struct GlyphCache { entries: FxHashMap< @@ -396,7 +403,7 @@ impl Cache { .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) .stretch(to_stretch(key.font.stretch)), - matches!(key.shaping, Shaping::Basic), + to_shaping(key.shaping), ); let _ = entry.insert(buffer); diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 254d32d6..6934ae49 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -44,7 +44,7 @@ path = "../graphics" [dependencies.glyphon] version = "0.2" git = "https://github.com/hecrj/glyphon.git" -rev = "446cf0803065b52ba5fb9a30fe0addb6d7b5f9d9" +rev = "504aa8a9a1fb42726f02fa244b70119e7ca25933" [dependencies.encase] version = "0.3.0" diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index fc126125..ad7bdc8d 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -311,6 +311,13 @@ fn to_stretch(stretch: font::Stretch) -> glyphon::Stretch { } } +fn to_shaping(shaping: Shaping) -> glyphon::Shaping { + match shaping { + Shaping::Basic => glyphon::Shaping::Basic, + Shaping::Advanced => glyphon::Shaping::Advanced, + } +} + struct Cache { entries: FxHashMap, recently_used: FxHashSet, @@ -369,7 +376,7 @@ impl Cache { .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) .stretch(to_stretch(key.font.stretch)), - matches!(key.shaping, Shaping::Basic), + to_shaping(key.shaping), ); let _ = entry.insert(buffer); -- cgit