diff options
author | 2023-05-04 13:00:16 +0200 | |
---|---|---|
committer | 2023-05-04 18:39:31 +0200 | |
commit | 9499a8f9e6f9971dedfae563cb133232aa3cebc2 (patch) | |
tree | 54074dd8b1fc17d63ad92d84b6d2b4415ad29df6 | |
parent | 8e8808f0e187ed6671441f5016f07bfcba426452 (diff) | |
download | iced-9499a8f9e6f9971dedfae563cb133232aa3cebc2.tar.gz iced-9499a8f9e6f9971dedfae563cb133232aa3cebc2.tar.bz2 iced-9499a8f9e6f9971dedfae563cb133232aa3cebc2.zip |
Support configurable `LineHeight` in text widgets
-rw-r--r-- | core/src/length.rs | 8 | ||||
-rw-r--r-- | core/src/pixels.rs | 6 | ||||
-rw-r--r-- | core/src/renderer/null.rs | 2 | ||||
-rw-r--r-- | core/src/text.rs | 73 | ||||
-rw-r--r-- | core/src/widget/text.rs | 20 | ||||
-rw-r--r-- | graphics/src/backend.rs | 2 | ||||
-rw-r--r-- | graphics/src/geometry/text.rs | 5 | ||||
-rw-r--r-- | graphics/src/primitive.rs | 4 | ||||
-rw-r--r-- | graphics/src/renderer.rs | 13 | ||||
-rw-r--r-- | renderer/src/backend.rs | 5 | ||||
-rw-r--r-- | tiny_skia/src/backend.rs | 20 | ||||
-rw-r--r-- | tiny_skia/src/geometry.rs | 1 | ||||
-rw-r--r-- | tiny_skia/src/text.rs | 28 | ||||
-rw-r--r-- | wgpu/src/backend.rs | 13 | ||||
-rw-r--r-- | wgpu/src/geometry.rs | 1 | ||||
-rw-r--r-- | wgpu/src/layer.rs | 3 | ||||
-rw-r--r-- | wgpu/src/layer/text.rs | 5 | ||||
-rw-r--r-- | wgpu/src/text.rs | 28 | ||||
-rw-r--r-- | widget/src/checkbox.rs | 14 | ||||
-rw-r--r-- | widget/src/overlay/menu.rs | 46 | ||||
-rw-r--r-- | widget/src/pick_list.rs | 26 | ||||
-rw-r--r-- | widget/src/radio.rs | 13 | ||||
-rw-r--r-- | widget/src/text_input.rs | 30 | ||||
-rw-r--r-- | widget/src/toggler.rs | 13 |
24 files changed, 337 insertions, 42 deletions
diff --git a/core/src/length.rs b/core/src/length.rs index bb925c4b..3adb996e 100644 --- a/core/src/length.rs +++ b/core/src/length.rs @@ -1,3 +1,5 @@ +use crate::Pixels; + /// The strategy used to fill space in a specific dimension. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Length { @@ -36,6 +38,12 @@ impl Length { } } +impl From<Pixels> for Length { + fn from(amount: Pixels) -> Self { + Length::Fixed(f32::from(amount)) + } +} + impl From<f32> for Length { fn from(amount: f32) -> Self { Length::Fixed(amount) diff --git a/core/src/pixels.rs b/core/src/pixels.rs index e42cd9f9..6a9e5c88 100644 --- a/core/src/pixels.rs +++ b/core/src/pixels.rs @@ -20,3 +20,9 @@ impl From<u16> for Pixels { Self(f32::from(amount)) } } + +impl From<Pixels> for f32 { + fn from(pixels: Pixels) -> Self { + pixels.0 + } +} diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 22afb058..f0cc952e 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -60,6 +60,7 @@ impl text::Renderer for Null { &self, _content: &str, _size: f32, + _line_height: text::LineHeight, _font: Font, _bounds: Size, _shaping: text::Shaping, @@ -71,6 +72,7 @@ impl text::Renderer for Null { &self, _contents: &str, _size: f32, + _line_height: text::LineHeight, _font: Self::Font, _bounds: Size, _shaping: text::Shaping, diff --git a/core/src/text.rs b/core/src/text.rs index c59d8fce..1a867be3 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -1,8 +1,9 @@ //! Draw and interact with text. use crate::alignment; -use crate::{Color, Point, Rectangle, Size}; +use crate::{Color, Pixels, Point, Rectangle, Size}; use std::borrow::Cow; +use std::hash::{Hash, Hasher}; /// A paragraph. #[derive(Debug, Clone, Copy)] @@ -13,9 +14,12 @@ pub struct Text<'a, Font> { /// The bounds of the paragraph. pub bounds: Rectangle, - /// The size of the [`Text`]. + /// The size of the [`Text`] in logical pixels. pub size: f32, + /// The line height of the [`Text`]. + pub line_height: LineHeight, + /// The color of the [`Text`]. pub color: Color, @@ -56,6 +60,59 @@ pub enum Shaping { Advanced, } +/// The height of a line of text in a paragraph. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum LineHeight { + /// A factor of the size of the text. + Relative(f32), + + /// An absolute height in logical pixels. + Absolute(Pixels), +} + +impl LineHeight { + /// Returns the [`LineHeight`] in absolute logical pixels. + pub fn to_absolute(self, text_size: Pixels) -> Pixels { + match self { + Self::Relative(factor) => Pixels(factor * text_size.0), + Self::Absolute(pixels) => pixels, + } + } +} + +impl Default for LineHeight { + fn default() -> Self { + Self::Relative(1.2) + } +} + +impl From<f32> for LineHeight { + fn from(factor: f32) -> Self { + Self::Relative(factor) + } +} + +impl From<Pixels> for LineHeight { + fn from(pixels: Pixels) -> Self { + Self::Absolute(pixels) + } +} + +impl Hash for LineHeight { + fn hash<H: Hasher>(&self, state: &mut H) { + match self { + Self::Relative(factor) => { + state.write_u8(0); + factor.to_bits().hash(state); + } + Self::Absolute(pixels) => { + state.write_u8(1); + f32::from(*pixels).to_bits().hash(state); + } + } + } +} + /// The result of hit testing on text. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Hit { @@ -102,6 +159,7 @@ pub trait Renderer: crate::Renderer { &self, content: &str, size: f32, + line_height: LineHeight, font: Self::Font, bounds: Size, shaping: Shaping, @@ -115,8 +173,14 @@ pub trait Renderer: crate::Renderer { font: Self::Font, shaping: Shaping, ) -> f32 { - let (width, _) = - self.measure(content, size, font, Size::INFINITY, shaping); + let (width, _) = self.measure( + content, + size, + LineHeight::Absolute(Pixels(size)), + font, + Size::INFINITY, + shaping, + ); width } @@ -132,6 +196,7 @@ pub trait Renderer: crate::Renderer { &self, contents: &str, size: f32, + line_height: LineHeight, font: Self::Font, bounds: Size, shaping: Shaping, diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index f0392168..2d86e735 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -19,6 +19,7 @@ where { content: Cow<'a, str>, size: Option<f32>, + line_height: text::LineHeight, width: Length, height: Length, horizontal_alignment: alignment::Horizontal, @@ -38,6 +39,7 @@ where Text { content: content.into(), size: None, + line_height: text::LineHeight::default(), font: None, width: Length::Shrink, height: Length::Shrink, @@ -54,6 +56,15 @@ where self } + /// Sets the [`LineHeight`] of the [`Text`]. + pub fn line_height( + mut self, + line_height: impl Into<text::LineHeight>, + ) -> Self { + self.line_height = line_height.into(); + self + } + /// Sets the [`Font`] of the [`Text`]. /// /// [`Font`]: crate::text::Renderer::Font @@ -135,6 +146,7 @@ where let (width, height) = renderer.measure( &self.content, size, + self.line_height, self.font.unwrap_or_else(|| renderer.default_font()), bounds, self.shaping, @@ -161,6 +173,7 @@ where layout, &self.content, self.size, + self.line_height, self.font, theme.appearance(self.style.clone()), self.horizontal_alignment, @@ -186,6 +199,7 @@ pub fn draw<Renderer>( layout: Layout<'_>, content: &str, size: Option<f32>, + line_height: text::LineHeight, font: Option<Renderer::Font>, appearance: Appearance, horizontal_alignment: alignment::Horizontal, @@ -208,9 +222,12 @@ pub fn draw<Renderer>( alignment::Vertical::Bottom => bounds.y + bounds.height, }; + let size = size.unwrap_or_else(|| renderer.default_size()); + renderer.fill_text(crate::Text { content, - size: size.unwrap_or_else(|| renderer.default_size()), + size, + line_height, bounds: Rectangle { x, y, ..bounds }, color: appearance.color.unwrap_or(style.text_color), font: font.unwrap_or_else(|| renderer.default_font()), @@ -240,6 +257,7 @@ where Self { content: self.content.clone(), size: self.size, + line_height: self.line_height, width: self.width, height: self.height, horizontal_alignment: self.horizontal_alignment, diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index 0c2a6d30..ae89da06 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -46,6 +46,7 @@ pub trait Text { &self, contents: &str, size: f32, + line_height: text::LineHeight, font: Font, bounds: Size, shaping: text::Shaping, @@ -62,6 +63,7 @@ pub trait Text { &self, contents: &str, size: f32, + line_height: text::LineHeight, font: Font, bounds: Size, shaping: text::Shaping, diff --git a/graphics/src/geometry/text.rs b/graphics/src/geometry/text.rs index 0f731e74..c584f3cd 100644 --- a/graphics/src/geometry/text.rs +++ b/graphics/src/geometry/text.rs @@ -1,5 +1,5 @@ use crate::core::alignment; -use crate::core::text::Shaping; +use crate::core::text::{LineHeight, Shaping}; use crate::core::{Color, Font, Point}; /// A bunch of text that can be drawn to a canvas @@ -20,6 +20,8 @@ pub struct Text { pub color: Color, /// The size of the text pub size: f32, + /// The line height of the text. + pub line_height: LineHeight, /// The font of the text pub font: Font, /// The horizontal alignment of the text @@ -37,6 +39,7 @@ impl Default for Text { position: Point::ORIGIN, color: Color::BLACK, size: 16.0, + line_height: LineHeight::Relative(1.2), font: Font::default(), horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index db237035..d814c757 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -19,8 +19,10 @@ pub enum Primitive { bounds: Rectangle, /// The color of the text color: Color, - /// The size of the text + /// The size of the text in logical pixels size: f32, + /// The line height of the text + line_height: text::LineHeight, /// The font of the text font: Font, /// The horizontal alignment of the text diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 605286d6..aaf1737a 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -136,17 +136,26 @@ where &self, content: &str, size: f32, + line_height: text::LineHeight, font: Font, bounds: Size, shaping: text::Shaping, ) -> (f32, f32) { - self.backend().measure(content, size, font, bounds, shaping) + self.backend().measure( + content, + size, + line_height, + font, + bounds, + shaping, + ) } fn hit_test( &self, content: &str, size: f32, + line_height: text::LineHeight, font: Font, bounds: Size, shaping: text::Shaping, @@ -156,6 +165,7 @@ where self.backend().hit_test( content, size, + line_height, font, bounds, shaping, @@ -173,6 +183,7 @@ where content: text.content.to_string(), bounds: text.bounds, size: text.size, + line_height: text.line_height, color: text.color, font: text.font, horizontal_alignment: text.horizontal_alignment, diff --git a/renderer/src/backend.rs b/renderer/src/backend.rs index 70b146f4..c1bec6af 100644 --- a/renderer/src/backend.rs +++ b/renderer/src/backend.rs @@ -46,6 +46,7 @@ impl backend::Text for Backend { &self, contents: &str, size: f32, + line_height: text::LineHeight, font: Font, bounds: Size, shaping: text::Shaping, @@ -53,7 +54,7 @@ impl backend::Text for Backend { delegate!( self, backend, - backend.measure(contents, size, font, bounds, shaping) + backend.measure(contents, size, line_height, font, bounds, shaping) ) } @@ -61,6 +62,7 @@ impl backend::Text for Backend { &self, contents: &str, size: f32, + line_height: text::LineHeight, font: Font, bounds: Size, shaping: text::Shaping, @@ -73,6 +75,7 @@ impl backend::Text for Backend { backend.hit_test( contents, size, + line_height, font, bounds, shaping, diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 3ef7e717..99230a2f 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -216,6 +216,7 @@ impl Backend { bounds, color, size, + line_height, font, horizontal_alignment, vertical_alignment, @@ -233,13 +234,15 @@ impl Backend { self.text_pipeline.draw( content, - (*bounds + translation) * scale_factor, + *bounds + translation, *color, - *size * scale_factor, + *size, + *line_height, *font, *horizontal_alignment, *vertical_alignment, *shaping, + scale_factor, pixels, clip_mask, ); @@ -626,18 +629,26 @@ impl backend::Text for Backend { &self, contents: &str, size: f32, + line_height: text::LineHeight, font: Font, bounds: Size, shaping: text::Shaping, ) -> (f32, f32) { - self.text_pipeline - .measure(contents, size, font, bounds, shaping) + self.text_pipeline.measure( + contents, + size, + line_height, + font, + bounds, + shaping, + ) } fn hit_test( &self, contents: &str, size: f32, + line_height: text::LineHeight, font: Font, bounds: Size, shaping: text::Shaping, @@ -647,6 +658,7 @@ impl backend::Text for Backend { self.text_pipeline.hit_test( contents, size, + line_height, font, bounds, shaping, diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 7963fd89..a445b561 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -111,6 +111,7 @@ impl Frame { }, color: text.color, size: text.size, + line_height: text.line_height, font: text.font, horizontal_alignment: text.horizontal_alignment, vertical_alignment: text.vertical_alignment, diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 58079cc0..ba8a4e4b 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -1,7 +1,7 @@ use crate::core::alignment; use crate::core::font::{self, Font}; -use crate::core::text::{Hit, Shaping}; -use crate::core::{Color, Point, Rectangle, Size}; +use crate::core::text::{Hit, LineHeight, Shaping}; +use crate::core::{Color, Pixels, Point, Rectangle, Size}; use rustc_hash::{FxHashMap, FxHashSet}; use std::borrow::Cow; @@ -46,13 +46,21 @@ impl Pipeline { bounds: Rectangle, color: Color, size: f32, + line_height: LineHeight, font: Font, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, shaping: Shaping, + scale_factor: f32, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, ) { + let line_height = + f32::from(line_height.to_absolute(Pixels(size))) * scale_factor; + + let bounds = bounds * scale_factor; + let size = size * scale_factor; + let font_system = self.font_system.get_mut(); let key = Key { bounds: { @@ -64,6 +72,7 @@ impl Pipeline { content, font, size, + line_height, shaping, }; @@ -76,7 +85,7 @@ impl Pipeline { (i + 1, buffer.line_w.max(max)) }); - let total_height = total_lines as f32 * size * 1.2; + let total_height = total_lines as f32 * line_height; let x = match horizontal_alignment { alignment::Horizontal::Left => bounds.x, @@ -130,17 +139,21 @@ impl Pipeline { &self, content: &str, size: f32, + line_height: LineHeight, font: Font, bounds: Size, shaping: Shaping, ) -> (f32, f32) { let mut measurement_cache = self.measurement_cache.borrow_mut(); + let line_height = f32::from(line_height.to_absolute(Pixels(size))); + let (_, paragraph) = measurement_cache.allocate( &mut self.font_system.borrow_mut(), Key { content, size, + line_height, font, bounds, shaping, @@ -154,13 +167,14 @@ impl Pipeline { (i + 1, buffer.line_w.max(max)) }); - (max_width, size * 1.2 * total_lines as f32) + (max_width, line_height * total_lines as f32) } pub fn hit_test( &self, content: &str, size: f32, + line_height: LineHeight, font: Font, bounds: Size, shaping: Shaping, @@ -169,11 +183,14 @@ impl Pipeline { ) -> Option<Hit> { let mut measurement_cache = self.measurement_cache.borrow_mut(); + let line_height = f32::from(line_height.to_absolute(Pixels(size))); + let (_, paragraph) = measurement_cache.allocate( &mut self.font_system.borrow_mut(), Key { content, size, + line_height, font, bounds, shaping, @@ -380,9 +397,11 @@ impl Cache { key.content.hash(&mut hasher); key.size.to_bits().hash(&mut hasher); + key.line_height.to_bits().hash(&mut hasher); key.font.hash(&mut hasher); key.bounds.width.to_bits().hash(&mut hasher); key.bounds.height.to_bits().hash(&mut hasher); + key.shaping.hash(&mut hasher); hasher.finish() }; @@ -432,6 +451,7 @@ impl Cache { struct Key<'a> { content: &'a str, size: f32, + line_height: f32, font: Font, bounds: Size, shaping: Shaping, diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 6b847aff..def80a81 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -352,18 +352,26 @@ impl backend::Text for Backend { &self, contents: &str, size: f32, + line_height: core::text::LineHeight, font: Font, bounds: Size, shaping: core::text::Shaping, ) -> (f32, f32) { - self.text_pipeline - .measure(contents, size, font, bounds, shaping) + self.text_pipeline.measure( + contents, + size, + line_height, + font, + bounds, + shaping, + ) } fn hit_test( &self, contents: &str, size: f32, + line_height: core::text::LineHeight, font: Font, bounds: Size, shaping: core::text::Shaping, @@ -373,6 +381,7 @@ impl backend::Text for Backend { self.text_pipeline.hit_test( contents, size, + line_height, font, bounds, shaping, diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index f6397ab7..8cfed1e5 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -331,6 +331,7 @@ impl Frame { }, color: text.color, size: text.size, + line_height: text.line_height, font: text.font, horizontal_alignment: text.horizontal_alignment, vertical_alignment: text.vertical_alignment, diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index b9fd044e..dcae0648 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -62,6 +62,7 @@ impl<'a> Layer<'a> { ), color: Color::new(0.9, 0.9, 0.9, 1.0), size: 20.0, + line_height: core::text::LineHeight::Relative(1.2), font: Font::MONOSPACE, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, @@ -114,6 +115,7 @@ impl<'a> Layer<'a> { content, bounds, size, + line_height, color, font, horizontal_alignment, @@ -126,6 +128,7 @@ impl<'a> Layer<'a> { content, bounds: *bounds + translation, size: *size, + line_height: *line_height, color: *color, font: *font, horizontal_alignment: *horizontal_alignment, diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs index 665f7188..ba1bdca8 100644 --- a/wgpu/src/layer/text.rs +++ b/wgpu/src/layer/text.rs @@ -14,9 +14,12 @@ pub struct Text<'a> { /// The color of the [`Text`], in __linear RGB_. pub color: Color, - /// The size of the [`Text`]. + /// The size of the [`Text`] in logical pixels. pub size: f32, + /// The line height of the [`Text`]. + pub line_height: text::LineHeight, + /// The font of the [`Text`]. pub font: Font, diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index ad7bdc8d..ff68772d 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -1,7 +1,7 @@ use crate::core::alignment; use crate::core::font::{self, Font}; -use crate::core::text::{Hit, Shaping}; -use crate::core::{Point, Rectangle, Size}; +use crate::core::text::{Hit, LineHeight, Shaping}; +use crate::core::{Pixels, Point, Rectangle, Size}; use crate::layer::Text; use rustc_hash::{FxHashMap, FxHashSet}; @@ -77,6 +77,11 @@ impl Pipeline { Key { content: section.content, size: section.size * scale_factor, + line_height: f32::from( + section + .line_height + .to_absolute(Pixels(section.size)), + ) * scale_factor, font: section.font, bounds: Size { width: (section.bounds.width * scale_factor).ceil(), @@ -114,7 +119,7 @@ impl Pipeline { }); let total_height = - total_lines as f32 * section.size * 1.2 * scale_factor; + total_lines as f32 * buffer.metrics().line_height; let left = match section.horizontal_alignment { alignment::Horizontal::Left => x, @@ -212,17 +217,21 @@ impl Pipeline { &self, content: &str, size: f32, + line_height: LineHeight, font: Font, bounds: Size, shaping: Shaping, ) -> (f32, f32) { let mut measurement_cache = self.measurement_cache.borrow_mut(); + let line_height = f32::from(line_height.to_absolute(Pixels(size))); + let (_, paragraph) = measurement_cache.allocate( &mut self.font_system.borrow_mut(), Key { content, size, + line_height, font, bounds, shaping, @@ -236,13 +245,14 @@ impl Pipeline { (i + 1, buffer.line_w.max(max)) }); - (max_width, size * 1.2 * total_lines as f32) + (max_width, line_height * total_lines as f32) } pub fn hit_test( &self, content: &str, size: f32, + line_height: LineHeight, font: Font, bounds: Size, shaping: Shaping, @@ -251,11 +261,14 @@ impl Pipeline { ) -> Option<Hit> { let mut measurement_cache = self.measurement_cache.borrow_mut(); + let line_height = f32::from(line_height.to_absolute(Pixels(size))); + let (_, paragraph) = measurement_cache.allocate( &mut self.font_system.borrow_mut(), Key { content, size, + line_height, font, bounds, shaping, @@ -353,21 +366,23 @@ impl Cache { key.content.hash(&mut hasher); key.size.to_bits().hash(&mut hasher); + key.line_height.to_bits().hash(&mut hasher); key.font.hash(&mut hasher); key.bounds.width.to_bits().hash(&mut hasher); key.bounds.height.to_bits().hash(&mut hasher); + key.shaping.hash(&mut hasher); hasher.finish() }; if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) { - let metrics = glyphon::Metrics::new(key.size, key.size * 1.2); + let metrics = glyphon::Metrics::new(key.size, key.line_height); let mut buffer = glyphon::Buffer::new(font_system, metrics); buffer.set_size( font_system, key.bounds.width, - key.bounds.height.max(key.size * 1.2), + key.bounds.height.max(key.line_height), ); buffer.set_text( font_system, @@ -399,6 +414,7 @@ impl Cache { struct Key<'a> { content: &'a str, size: f32, + line_height: f32, font: Font, bounds: Size, shaping: Shaping, diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 2a09b8fd..c34fd0bb 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -46,6 +46,7 @@ where size: f32, spacing: f32, text_size: Option<f32>, + text_line_height: text::LineHeight, text_shaping: text::Shaping, font: Option<Renderer::Font>, icon: Icon<Renderer::Font>, @@ -83,6 +84,7 @@ where size: Self::DEFAULT_SIZE, spacing: Self::DEFAULT_SPACING, text_size: None, + text_line_height: text::LineHeight::default(), text_shaping: text::Shaping::Basic, font: None, icon: Icon { @@ -119,6 +121,15 @@ where self } + /// Sets the text [`LineHeight`] of the [`Checkbox`]. + pub fn text_line_height( + mut self, + line_height: impl Into<text::LineHeight>, + ) -> Self { + self.text_line_height = line_height.into(); + self + } + /// Sets the [`text::Shaping`] strategy of the [`Checkbox`]. pub fn text_shaping(mut self, shaping: text::Shaping) -> Self { self.text_shaping = shaping; @@ -181,6 +192,7 @@ where self.text_size .unwrap_or_else(|| renderer.default_size()), ) + .line_height(self.text_line_height) .shaping(self.text_shaping), ) .layout(renderer, limits) @@ -276,6 +288,7 @@ where content: &code_point.to_string(), font: *font, size, + line_height: text::LineHeight::default(), bounds: Rectangle { x: bounds.center_x(), y: bounds.center_y(), @@ -298,6 +311,7 @@ where label_layout, &self.label, self.text_size, + self.text_line_height, self.font, crate::text::Appearance { color: custom_style.text_color, diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index 7de3cbae..dfb6a22a 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<f32>, + text_line_height: text::LineHeight, text_shaping: text::Shaping, font: Option<Renderer::Font>, style: <Renderer::Theme as StyleSheet>::Style, @@ -59,6 +60,7 @@ where width: 0.0, padding: Padding::ZERO, text_size: None, + text_line_height: text::LineHeight::default(), text_shaping: text::Shaping::Basic, font: None, style: Default::default(), @@ -83,6 +85,15 @@ where self } + /// Sets the text [`LineHeight`] of the [`Menu`]. + pub fn text_line_height( + mut self, + line_height: impl Into<text::LineHeight>, + ) -> Self { + self.text_line_height = line_height.into(); + self + } + /// Sets the [`text::Shaping`] strategy of the [`Menu`]. pub fn text_shaping(mut self, shaping: text::Shaping) -> Self { self.text_shaping = shaping; @@ -176,6 +187,7 @@ where padding, font, text_size, + text_line_height, text_shaping, style, } = menu; @@ -186,6 +198,7 @@ where last_selection, font, text_size, + text_line_height, text_shaping, padding, style: style.clone(), @@ -321,6 +334,7 @@ where last_selection: &'a mut Option<T>, padding: Padding, text_size: Option<f32>, + text_line_height: text::LineHeight, text_shaping: text::Shaping, font: Option<Renderer::Font>, style: <Renderer::Theme as StyleSheet>::Style, @@ -352,10 +366,13 @@ where let text_size = self.text_size.unwrap_or_else(|| renderer.default_size()); + let text_line_height = + self.text_line_height.to_absolute(Pixels(text_size)); + let size = { let intrinsic = Size::new( 0.0, - (text_size * 1.2 + self.padding.vertical()) + (f32::from(text_line_height) + self.padding.vertical()) * self.options.len() as f32, ); @@ -395,9 +412,12 @@ where .text_size .unwrap_or_else(|| renderer.default_size()); + let option_height = f32::from( + self.text_line_height.to_absolute(Pixels(text_size)), + ) + self.padding.vertical(); + *self.hovered_option = Some( - ((cursor_position.y - bounds.y) - / (text_size * 1.2 + self.padding.vertical())) + ((cursor_position.y - bounds.y) / option_height) as usize, ); } @@ -410,9 +430,12 @@ where .text_size .unwrap_or_else(|| renderer.default_size()); + let option_height = f32::from( + self.text_line_height.to_absolute(Pixels(text_size)), + ) + self.padding.vertical(); + *self.hovered_option = Some( - ((cursor_position.y - bounds.y) - / (text_size * 1.2 + self.padding.vertical())) + ((cursor_position.y - bounds.y) / option_height) as usize, ); @@ -462,12 +485,12 @@ where let text_size = self.text_size.unwrap_or_else(|| renderer.default_size()); let option_height = - (text_size * 1.2 + self.padding.vertical()) as usize; + f32::from(self.text_line_height.to_absolute(Pixels(text_size))) + + self.padding.vertical(); let offset = viewport.y - bounds.y; - let start = (offset / option_height as f32) as usize; - let end = - ((offset + viewport.height) / option_height as f32).ceil() as usize; + let start = (offset / option_height) as usize; + let end = ((offset + viewport.height) / option_height).ceil() as usize; let visible_options = &self.options[start..end.min(self.options.len())]; @@ -477,9 +500,9 @@ where let bounds = Rectangle { x: bounds.x, - y: bounds.y + (option_height * i) as f32, + y: bounds.y + (option_height * i as f32), width: bounds.width, - height: text_size * 1.2 + self.padding.vertical(), + height: option_height, }; if is_selected { @@ -503,6 +526,7 @@ where ..bounds }, size: text_size, + line_height: self.text_line_height, font: self.font.unwrap_or_else(|| renderer.default_font()), color: if is_selected { appearance.selected_text_color diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index c0cb2946..bc2c9066 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<f32>, + text_line_height: text::LineHeight, text_shaping: text::Shaping, font: Option<Renderer::Font>, handle: Handle<Renderer::Font>, @@ -72,6 +73,7 @@ where width: Length::Shrink, padding: Self::DEFAULT_PADDING, text_size: None, + text_line_height: text::LineHeight::default(), text_shaping: text::Shaping::Basic, font: None, handle: Default::default(), @@ -103,6 +105,15 @@ where self } + /// Sets the text [`LineHeight`] of the [`PickList`]. + pub fn text_line_height( + mut self, + line_height: impl Into<text::LineHeight>, + ) -> Self { + self.text_line_height = line_height.into(); + self + } + /// Sets the [`text::Shaping`] strategy of the [`PickList`]. pub fn text_shaping(mut self, shaping: text::Shaping) -> Self { self.text_shaping = shaping; @@ -172,6 +183,7 @@ where self.width, self.padding, self.text_size, + self.text_line_height, self.text_shaping, self.font, self.placeholder.as_deref(), @@ -230,6 +242,7 @@ where cursor_position, self.padding, self.text_size, + self.text_line_height, self.text_shaping, font, self.placeholder.as_deref(), @@ -358,6 +371,7 @@ pub fn layout<Renderer, T>( width: Length, padding: Padding, text_size: Option<f32>, + text_line_height: text::LineHeight, text_shaping: text::Shaping, font: Option<Renderer::Font>, placeholder: Option<&str>, @@ -375,11 +389,10 @@ where let max_width = match width { Length::Shrink => { let measure = |label: &str| -> f32 { - let (width, _) = renderer.measure( + let width = renderer.measure_width( label, text_size, font.unwrap_or_else(|| renderer.default_font()), - Size::new(f32::INFINITY, f32::INFINITY), text_shaping, ); @@ -400,8 +413,10 @@ where }; let size = { - let intrinsic = - Size::new(max_width + text_size + padding.left, text_size * 1.2); + let intrinsic = Size::new( + max_width + text_size + padding.left, + f32::from(text_line_height.to_absolute(Pixels(text_size))), + ); limits.resolve(intrinsic).pad(padding) }; @@ -579,6 +594,7 @@ pub fn draw<'a, T, Renderer>( cursor_position: Point, padding: Padding, text_size: Option<f32>, + text_line_height: text::LineHeight, text_shaping: text::Shaping, font: Renderer::Font, placeholder: Option<&str>, @@ -645,6 +661,7 @@ pub fn draw<'a, T, Renderer>( renderer.fill_text(Text { content: &code_point.to_string(), size, + line_height: text::LineHeight::default(), font, color: style.handle_color, bounds: Rectangle { @@ -667,6 +684,7 @@ pub fn draw<'a, T, Renderer>( renderer.fill_text(Text { content: label, size: text_size, + line_height: text_line_height, font, color: if is_selected { style.text_color diff --git a/widget/src/radio.rs b/widget/src/radio.rs index f62f4703..9dad1e22 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -81,6 +81,7 @@ where size: f32, spacing: f32, text_size: Option<f32>, + text_line_height: text::LineHeight, text_shaping: text::Shaping, font: Option<Renderer::Font>, style: <Renderer::Theme as StyleSheet>::Style, @@ -124,6 +125,7 @@ where size: Self::DEFAULT_SIZE, spacing: Self::DEFAULT_SPACING, //15 text_size: None, + text_line_height: text::LineHeight::default(), text_shaping: text::Shaping::Basic, font: None, style: Default::default(), @@ -154,6 +156,15 @@ where self } + /// Sets the text [`LineHeight`] of the [`Radio`] button. + pub fn text_line_height( + mut self, + line_height: impl Into<text::LineHeight>, + ) -> Self { + self.text_line_height = line_height.into(); + self + } + /// Sets the [`text::Shaping`] strategy of the [`Radio`] button. pub fn text_shaping(mut self, shaping: text::Shaping) -> Self { self.text_shaping = shaping; @@ -207,6 +218,7 @@ where self.text_size .unwrap_or_else(|| renderer.default_size()), ) + .line_height(self.text_line_height) .shaping(self.text_shaping), ) .layout(renderer, limits) @@ -317,6 +329,7 @@ where label_layout, &self.label, self.text_size, + self.text_line_height, self.font, crate::text::Appearance { color: custom_style.text_color, diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 364ec3cd..bbc07dac 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -68,6 +68,7 @@ where width: Length, padding: Padding, size: Option<f32>, + line_height: text::LineHeight, on_input: Option<Box<dyn Fn(String) -> Message + 'a>>, on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>, on_submit: Option<Message>, @@ -96,6 +97,7 @@ where width: Length::Fill, padding: Padding::new(5.0), size: None, + line_height: text::LineHeight::default(), on_input: None, on_paste: None, on_submit: None, @@ -177,6 +179,15 @@ where self } + /// Sets the [`LineHeight`] of the [`TextInput`]. + pub fn line_height( + mut self, + line_height: impl Into<text::LineHeight>, + ) -> Self { + self.line_height = line_height.into(); + self + } + /// Sets the style of the [`TextInput`]. pub fn style( mut self, @@ -208,6 +219,7 @@ where value.unwrap_or(&self.value), &self.placeholder, self.size, + self.line_height, self.font, self.on_input.is_none(), self.is_secure, @@ -263,6 +275,7 @@ where self.width, self.padding, self.size, + self.line_height, self.icon.as_ref(), ) } @@ -299,6 +312,7 @@ where shell, &mut self.value, self.size, + self.line_height, self.font, self.is_secure, self.on_input.as_deref(), @@ -327,6 +341,7 @@ where &self.value, &self.placeholder, self.size, + self.line_height, self.font, self.on_input.is_none(), self.is_secure, @@ -447,6 +462,7 @@ pub fn layout<Renderer>( width: Length, padding: Padding, size: Option<f32>, + line_height: text::LineHeight, icon: Option<&Icon<Renderer::Font>>, ) -> layout::Node where @@ -454,7 +470,10 @@ where { let text_size = size.unwrap_or_else(|| renderer.default_size()); let padding = padding.fit(Size::ZERO, limits.max()); - let limits = limits.width(width).pad(padding).height(text_size * 1.2); + let limits = limits + .width(width) + .pad(padding) + .height(line_height.to_absolute(Pixels(text_size))); let text_bounds = limits.resolve(Size::ZERO); @@ -515,6 +534,7 @@ pub fn update<'a, Message, Renderer>( shell: &mut Shell<'_, Message>, value: &mut Value, size: Option<f32>, + line_height: text::LineHeight, font: Option<Renderer::Font>, is_secure: bool, on_input: Option<&dyn Fn(String) -> Message>, @@ -567,6 +587,7 @@ where text_layout.bounds(), font, size, + line_height, &value, state, target, @@ -595,6 +616,7 @@ where text_layout.bounds(), font, size, + line_height, value, state, target, @@ -644,6 +666,7 @@ where text_layout.bounds(), font, size, + line_height, &value, state, target, @@ -926,6 +949,7 @@ pub fn draw<Renderer>( value: &Value, placeholder: &str, size: Option<f32>, + line_height: text::LineHeight, font: Option<Renderer::Font>, is_disabled: bool, is_secure: bool, @@ -971,6 +995,7 @@ pub fn draw<Renderer>( renderer.fill_text(Text { content: &icon.code_point.to_string(), size: icon.size.unwrap_or_else(|| renderer.default_size()), + line_height: text::LineHeight::default(), font: icon.font, color: appearance.icon_color, bounds: Rectangle { @@ -1110,6 +1135,7 @@ pub fn draw<Renderer>( ..text_bounds }, size, + line_height, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, shaping: text::Shaping::Advanced, @@ -1336,6 +1362,7 @@ fn find_cursor_position<Renderer>( text_bounds: Rectangle, font: Option<Renderer::Font>, size: Option<f32>, + line_height: text::LineHeight, value: &Value, state: &State, x: f32, @@ -1353,6 +1380,7 @@ where .hit_test( &value, size, + line_height, font, Size::INFINITY, text::Shaping::Advanced, diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index 639bbb3b..b1ba65c9 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -42,6 +42,7 @@ where width: Length, size: f32, text_size: Option<f32>, + text_line_height: text::LineHeight, text_alignment: alignment::Horizontal, text_shaping: text::Shaping, spacing: f32, @@ -80,6 +81,7 @@ where width: Length::Fill, size: Self::DEFAULT_SIZE, text_size: None, + text_line_height: text::LineHeight::default(), text_alignment: alignment::Horizontal::Left, text_shaping: text::Shaping::Basic, spacing: 0.0, @@ -106,6 +108,15 @@ where self } + /// Sets the text [`LineHeight`] of the [`Toggler`]. + pub fn text_line_height( + mut self, + line_height: impl Into<text::LineHeight>, + ) -> Self { + self.text_line_height = line_height.into(); + self + } + /// Sets the horizontal alignment of the text of the [`Toggler`] pub fn text_alignment(mut self, alignment: alignment::Horizontal) -> Self { self.text_alignment = alignment; @@ -176,6 +187,7 @@ where self.text_size .unwrap_or_else(|| renderer.default_size()), ) + .line_height(self.text_line_height) .shaping(self.text_shaping), ); } @@ -254,6 +266,7 @@ where label_layout, label, self.text_size, + self.text_line_height, self.font, Default::default(), self.text_alignment, |