diff options
author | 2023-02-24 20:52:10 +0100 | |
---|---|---|
committer | 2023-02-24 20:52:10 +0100 | |
commit | 368cadd25a8b57ee5c41e45d1abe8d1dfb194c69 (patch) | |
tree | 191cb7cc7807a5fe513b3d485b2fda21d3bf0bde /native | |
parent | 573d27eb52bbfacf1b06983b4282f00eb5265bdc (diff) | |
parent | 8059c40142d601588e01c152ce1bb72a1295dde8 (diff) | |
download | iced-368cadd25a8b57ee5c41e45d1abe8d1dfb194c69.tar.gz iced-368cadd25a8b57ee5c41e45d1abe8d1dfb194c69.tar.bz2 iced-368cadd25a8b57ee5c41e45d1abe8d1dfb194c69.zip |
Merge pull request #1697 from iced-rs/text-glyphon
Text shaping, font fallback, and `iced_wgpu` overhaul
Diffstat (limited to '')
-rw-r--r-- | native/src/command/action.rs | 16 | ||||
-rw-r--r-- | native/src/font.rs | 19 | ||||
-rw-r--r-- | native/src/lib.rs | 6 | ||||
-rw-r--r-- | native/src/overlay/menu.rs | 23 | ||||
-rw-r--r-- | native/src/program.rs | 3 | ||||
-rw-r--r-- | native/src/renderer/null.rs | 12 | ||||
-rw-r--r-- | native/src/text.rs | 23 | ||||
-rw-r--r-- | native/src/widget/checkbox.rs | 16 | ||||
-rw-r--r-- | native/src/widget/pick_list.rs | 45 | ||||
-rw-r--r-- | native/src/widget/radio.rs | 10 | ||||
-rw-r--r-- | native/src/widget/text.rs | 22 | ||||
-rw-r--r-- | native/src/widget/text_input.rs | 57 | ||||
-rw-r--r-- | native/src/widget/toggler.rs | 12 |
13 files changed, 161 insertions, 103 deletions
diff --git a/native/src/command/action.rs b/native/src/command/action.rs index a51b8c21..d1589c05 100644 --- a/native/src/command/action.rs +++ b/native/src/command/action.rs @@ -1,10 +1,12 @@ use crate::clipboard; +use crate::font; use crate::system; use crate::widget; use crate::window; use iced_futures::MaybeSend; +use std::borrow::Cow; use std::fmt; /// An action that a [`Command`] can perform. @@ -27,6 +29,15 @@ pub enum Action<T> { /// Run a widget action. Widget(widget::Action<T>), + + /// Load a font from its bytes. + LoadFont { + /// The bytes of the font to load. + bytes: Cow<'static, [u8]>, + + /// The message to produce when the font has been loaded. + tagger: Box<dyn Fn(Result<(), font::Error>) -> T>, + }, } impl<T> Action<T> { @@ -49,6 +60,10 @@ impl<T> Action<T> { Self::Window(window) => Action::Window(window.map(f)), Self::System(system) => Action::System(system.map(f)), Self::Widget(widget) => Action::Widget(widget.map(f)), + Self::LoadFont { bytes, tagger } => Action::LoadFont { + bytes, + tagger: Box::new(move |result| f(tagger(result))), + }, } } } @@ -63,6 +78,7 @@ impl<T> fmt::Debug for Action<T> { Self::Window(action) => write!(f, "Action::Window({action:?})"), Self::System(action) => write!(f, "Action::System({action:?})"), Self::Widget(_action) => write!(f, "Action::Widget"), + Self::LoadFont { .. } => write!(f, "Action::LoadFont"), } } } diff --git a/native/src/font.rs b/native/src/font.rs new file mode 100644 index 00000000..15359694 --- /dev/null +++ b/native/src/font.rs @@ -0,0 +1,19 @@ +//! Load and use fonts. +pub use iced_core::font::*; + +use crate::command::{self, Command}; +use std::borrow::Cow; + +/// An error while loading a font. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error {} + +/// Load a font from its bytes. +pub fn load( + bytes: impl Into<Cow<'static, [u8]>>, +) -> Command<Result<(), Error>> { + Command::single(command::Action::LoadFont { + bytes: bytes.into(), + tagger: Box::new(std::convert::identity), + }) +} diff --git a/native/src/lib.rs b/native/src/lib.rs index ebdc8490..27b6fc0d 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -47,6 +47,7 @@ pub mod clipboard; pub mod command; pub mod event; +pub mod font; pub mod image; pub mod keyboard; pub mod layout; @@ -80,8 +81,8 @@ mod debug; pub use iced_core::alignment; pub use iced_core::time; pub use iced_core::{ - color, Alignment, Background, Color, ContentFit, Font, Length, Padding, - Pixels, Point, Rectangle, Size, Vector, + color, Alignment, Background, Color, ContentFit, Length, Padding, Pixels, + Point, Rectangle, Size, Vector, }; pub use iced_futures::{executor, futures}; pub use iced_style::application; @@ -95,6 +96,7 @@ pub use command::Command; pub use debug::Debug; pub use element::Element; pub use event::Event; +pub use font::Font; pub use hasher::Hasher; pub use layout::Layout; pub use overlay::Overlay; diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index 50f741ef..bd58a122 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -31,7 +31,7 @@ where width: f32, padding: Padding, text_size: Option<f32>, - font: Renderer::Font, + font: Option<Renderer::Font>, style: <Renderer::Theme as StyleSheet>::Style, } @@ -58,7 +58,7 @@ where width: 0.0, padding: Padding::ZERO, text_size: None, - font: Default::default(), + font: None, style: Default::default(), } } @@ -82,8 +82,8 @@ where } /// Sets the font of the [`Menu`]. - pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; + pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self { + self.font = Some(font.into()); self } @@ -311,7 +311,7 @@ where last_selection: &'a mut Option<T>, padding: Padding, text_size: Option<f32>, - font: Renderer::Font, + font: Option<Renderer::Font>, style: <Renderer::Theme as StyleSheet>::Style, } @@ -344,7 +344,7 @@ where let size = { let intrinsic = Size::new( 0.0, - (text_size + self.padding.vertical()) + (text_size * 1.2 + self.padding.vertical()) * self.options.len() as f32, ); @@ -386,7 +386,7 @@ where *self.hovered_option = Some( ((cursor_position.y - bounds.y) - / (text_size + self.padding.vertical())) + / (text_size * 1.2 + self.padding.vertical())) as usize, ); } @@ -401,7 +401,7 @@ where *self.hovered_option = Some( ((cursor_position.y - bounds.y) - / (text_size + self.padding.vertical())) + / (text_size * 1.2 + self.padding.vertical())) as usize, ); @@ -450,7 +450,8 @@ where let text_size = self.text_size.unwrap_or_else(|| renderer.default_size()); - let option_height = (text_size + self.padding.vertical()) as usize; + let option_height = + (text_size * 1.2 + self.padding.vertical()) as usize; let offset = viewport.y - bounds.y; let start = (offset / option_height as f32) as usize; @@ -467,7 +468,7 @@ where x: bounds.x, y: bounds.y + (option_height * i) as f32, width: bounds.width, - height: text_size + self.padding.vertical(), + height: text_size * 1.2 + self.padding.vertical(), }; if is_selected { @@ -491,7 +492,7 @@ where ..bounds }, size: text_size, - font: self.font.clone(), + font: self.font.unwrap_or_else(|| renderer.default_font()), color: if is_selected { appearance.selected_text_color } else { diff --git a/native/src/program.rs b/native/src/program.rs index c71c237f..25cab332 100644 --- a/native/src/program.rs +++ b/native/src/program.rs @@ -1,4 +1,5 @@ //! Build interactive programs using The Elm Architecture. +use crate::text; use crate::{Command, Element, Renderer}; mod state; @@ -8,7 +9,7 @@ pub use state::State; /// The core of a user interface application following The Elm Architecture. pub trait Program: Sized { /// The graphics backend to use to draw the [`Program`]. - type Renderer: Renderer; + type Renderer: Renderer + text::Renderer; /// The type of __messages__ your [`Program`] will produce. type Message: std::fmt::Debug + Send; diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 9376d540..150ee786 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -2,6 +2,8 @@ use crate::renderer::{self, Renderer}; use crate::text::{self, Text}; use crate::{Background, Font, Point, Rectangle, Size, Theme, Vector}; +use std::borrow::Cow; + /// A renderer that does nothing. /// /// It can be useful if you are writing tests! @@ -40,14 +42,20 @@ impl Renderer for Null { impl text::Renderer for Null { type Font = Font; - const ICON_FONT: Font = Font::Default; + const ICON_FONT: Font = Font::SansSerif; const CHECKMARK_ICON: char = '0'; const ARROW_DOWN_ICON: char = '0'; + fn default_font(&self) -> Self::Font { + Font::SansSerif + } + fn default_size(&self) -> f32 { - 20.0 + 16.0 } + fn load_font(&mut self, _font: Cow<'static, [u8]>) {} + fn measure( &self, _content: &str, diff --git a/native/src/text.rs b/native/src/text.rs index 55c3cfd3..4c72abc3 100644 --- a/native/src/text.rs +++ b/native/src/text.rs @@ -1,6 +1,8 @@ //! Draw and interact with text. use crate::alignment; -use crate::{Color, Point, Rectangle, Size, Vector}; +use crate::{Color, Point, Rectangle, Size}; + +use std::borrow::Cow; /// A paragraph. #[derive(Debug, Clone, Copy)] @@ -32,10 +34,6 @@ pub struct Text<'a, Font> { pub enum Hit { /// The point was within the bounds of the returned character index. CharOffset(usize), - /// The provided point was not within the bounds of a glyph. The index - /// of the character with the closest centeroid position is returned, - /// as well as its delta. - NearestCharOffset(usize, Vector), } impl Hit { @@ -43,13 +41,6 @@ impl Hit { pub fn cursor(self) -> usize { match self { Self::CharOffset(i) => i, - Self::NearestCharOffset(i, delta) => { - if delta.x > f32::EPSILON { - i + 1 - } else { - i - } - } } } } @@ -57,7 +48,7 @@ impl Hit { /// A renderer capable of measuring and drawing [`Text`]. pub trait Renderer: crate::Renderer { /// The font type used. - type Font: Default + Clone; + type Font: Copy; /// The icon font of the backend. const ICON_FONT: Self::Font; @@ -72,6 +63,9 @@ pub trait Renderer: crate::Renderer { /// [`ICON_FONT`]: Self::ICON_FONT const ARROW_DOWN_ICON: char; + /// Returns the default [`Self::Font`]. + fn default_font(&self) -> Self::Font; + /// Returns the default size of [`Text`]. fn default_size(&self) -> f32; @@ -109,6 +103,9 @@ pub trait Renderer: crate::Renderer { nearest_only: bool, ) -> Option<Hit>; + /// Loads a [`Self::Font`] from its bytes. + fn load_font(&mut self, font: Cow<'static, [u8]>); + /// Draws the given [`Text`]. fn fill_text(&mut self, text: Text<'_, Self::Font>); } diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 9b69e574..cd8b9c6b 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -55,7 +55,7 @@ where size: f32, spacing: f32, text_size: Option<f32>, - font: Renderer::Font, + font: Option<Renderer::Font>, icon: Icon<Renderer::Font>, style: <Renderer::Theme as StyleSheet>::Style, } @@ -91,7 +91,7 @@ where size: Self::DEFAULT_SIZE, spacing: Self::DEFAULT_SPACING, text_size: None, - font: Renderer::Font::default(), + font: None, icon: Icon { font: Renderer::ICON_FONT, code_point: Renderer::CHECKMARK_ICON, @@ -128,8 +128,8 @@ where /// Sets the [`Font`] of the text of the [`Checkbox`]. /// /// [`Font`]: crate::text::Renderer::Font - pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; + pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self { + self.font = Some(font.into()); self } @@ -175,7 +175,7 @@ where .push(Row::new().width(self.size).height(self.size)) .push( Text::new(&self.label) - .font(self.font.clone()) + .font(self.font.unwrap_or_else(|| renderer.default_font())) .width(self.width) .size( self.text_size @@ -267,12 +267,12 @@ where code_point, size, } = &self.icon; - let size = size.map(f32::from).unwrap_or(bounds.height * 0.7); + let size = size.unwrap_or(bounds.height * 0.7); if self.is_checked { renderer.fill_text(text::Text { content: &code_point.to_string(), - font: font.clone(), + font: *font, size, bounds: Rectangle { x: bounds.center_x(), @@ -295,7 +295,7 @@ where label_layout, &self.label, self.text_size, - self.font.clone(), + self.font, widget::text::Appearance { color: custom_style.text_color, }, diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index 17528db4..8ff82f3b 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -35,7 +35,7 @@ where width: Length, padding: Padding, text_size: Option<f32>, - font: Renderer::Font, + font: Option<Renderer::Font>, handle: Handle<Renderer::Font>, style: <Renderer::Theme as StyleSheet>::Style, } @@ -70,7 +70,7 @@ where width: Length::Shrink, padding: Self::DEFAULT_PADDING, text_size: None, - font: Default::default(), + font: None, handle: Default::default(), style: Default::default(), } @@ -101,8 +101,8 @@ where } /// Sets the font of the [`PickList`]. - pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; + pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self { + self.font = Some(font.into()); self } @@ -163,7 +163,7 @@ where self.width, self.padding, self.text_size, - &self.font, + self.font, self.placeholder.as_deref(), &self.options, ) @@ -212,6 +212,7 @@ where cursor_position: Point, _viewport: &Rectangle, ) { + let font = self.font.unwrap_or_else(|| renderer.default_font()); draw( renderer, theme, @@ -219,7 +220,7 @@ where cursor_position, self.padding, self.text_size, - &self.font, + font, self.placeholder.as_deref(), self.selected.as_ref(), &self.handle, @@ -232,7 +233,7 @@ where &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, - _renderer: &Renderer, + renderer: &Renderer, ) -> Option<overlay::Element<'b, Message, Renderer>> { let state = tree.state.downcast_mut::<State<T>>(); @@ -241,7 +242,7 @@ where state, self.padding, self.text_size, - self.font.clone(), + self.font.unwrap_or_else(|| renderer.default_font()), &self.options, self.style.clone(), ) @@ -343,7 +344,7 @@ pub fn layout<Renderer, T>( width: Length, padding: Padding, text_size: Option<f32>, - font: &Renderer::Font, + font: Option<Renderer::Font>, placeholder: Option<&str>, options: &[T], ) -> layout::Node @@ -362,7 +363,7 @@ where let (width, _) = renderer.measure( label, text_size, - font.clone(), + font.unwrap_or_else(|| renderer.default_font()), Size::new(f32::INFINITY, f32::INFINITY), ); @@ -384,7 +385,7 @@ where let size = { let intrinsic = - Size::new(max_width + text_size + padding.left, text_size); + Size::new(max_width + text_size + padding.left, text_size * 1.2); limits.resolve(intrinsic).pad(padding) }; @@ -560,7 +561,7 @@ pub fn draw<'a, T, Renderer>( cursor_position: Point, padding: Padding, text_size: Option<f32>, - font: &Renderer::Font, + font: Renderer::Font, placeholder: Option<&str>, selected: Option<&T>, handle: &Handle<Renderer::Font>, @@ -599,12 +600,12 @@ pub fn draw<'a, T, Renderer>( font, code_point, size, - }) => Some((font.clone(), *code_point, *size)), + }) => Some((*font, *code_point, *size)), Handle::Dynamic { open, closed } => { if state().is_open { - Some((open.font.clone(), open.code_point, open.size)) + Some((open.font, open.code_point, open.size)) } else { - Some((closed.font.clone(), closed.code_point, closed.size)) + Some((closed.font, closed.code_point, closed.size)) } } Handle::None => None, @@ -620,12 +621,12 @@ pub fn draw<'a, T, Renderer>( color: style.handle_color, bounds: Rectangle { x: bounds.x + bounds.width - padding.horizontal(), - y: bounds.center_y() - size / 2.0, - height: size, + y: bounds.center_y(), + height: size * 1.2, ..bounds }, horizontal_alignment: alignment::Horizontal::Right, - vertical_alignment: alignment::Vertical::Top, + vertical_alignment: alignment::Vertical::Center, }); } @@ -637,7 +638,7 @@ pub fn draw<'a, T, Renderer>( renderer.fill_text(Text { content: label, size: text_size, - font: font.clone(), + font, color: if is_selected { style.text_color } else { @@ -645,12 +646,12 @@ pub fn draw<'a, T, Renderer>( }, bounds: Rectangle { x: bounds.x + padding.left, - y: bounds.center_y() - text_size / 2.0, + y: bounds.center_y(), width: bounds.width - padding.horizontal(), - height: text_size, + height: text_size * 1.2, }, horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Top, + vertical_alignment: alignment::Vertical::Center, }); } } diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 9daddfbc..5f60eaef 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -53,7 +53,7 @@ where size: f32, spacing: f32, text_size: Option<f32>, - font: Renderer::Font, + font: Option<Renderer::Font>, style: <Renderer::Theme as StyleSheet>::Style, } @@ -95,7 +95,7 @@ where size: Self::DEFAULT_SIZE, spacing: Self::DEFAULT_SPACING, //15 text_size: None, - font: Default::default(), + font: None, style: Default::default(), } } @@ -125,8 +125,8 @@ where } /// Sets the text font of the [`Radio`] button. - pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; + pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self { + self.font = Some(font.into()); self } @@ -275,7 +275,7 @@ where label_layout, &self.label, self.text_size, - self.font.clone(), + self.font, widget::text::Appearance { color: custom_style.text_color, }, diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index 3fee48f2..aede754a 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -37,7 +37,7 @@ where height: Length, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, - font: Renderer::Font, + font: Option<Renderer::Font>, style: <Renderer::Theme as StyleSheet>::Style, } @@ -51,7 +51,7 @@ where Text { content: content.into(), size: None, - font: Default::default(), + font: None, width: Length::Shrink, height: Length::Shrink, horizontal_alignment: alignment::Horizontal::Left, @@ -70,7 +70,7 @@ where /// /// [`Font`]: crate::text::Renderer::Font pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self { - self.font = font.into(); + self.font = Some(font.into()); self } @@ -138,8 +138,12 @@ where let bounds = limits.max(); - let (width, height) = - renderer.measure(&self.content, size, self.font.clone(), bounds); + let (width, height) = renderer.measure( + &self.content, + size, + self.font.unwrap_or_else(|| renderer.default_font()), + bounds, + ); let size = limits.resolve(Size::new(width, height)); @@ -162,7 +166,7 @@ where layout, &self.content, self.size, - self.font.clone(), + self.font, theme.appearance(self.style), self.horizontal_alignment, self.vertical_alignment, @@ -186,7 +190,7 @@ pub fn draw<Renderer>( layout: Layout<'_>, content: &str, size: Option<f32>, - font: Renderer::Font, + font: Option<Renderer::Font>, appearance: Appearance, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, @@ -212,7 +216,7 @@ pub fn draw<Renderer>( size: size.unwrap_or_else(|| renderer.default_size()), bounds: Rectangle { x, y, ..bounds }, color: appearance.color.unwrap_or(style.text_color), - font, + font: font.unwrap_or_else(|| renderer.default_font()), horizontal_alignment, vertical_alignment, }); @@ -242,7 +246,7 @@ where height: self.height, horizontal_alignment: self.horizontal_alignment, vertical_alignment: self.vertical_alignment, - font: self.font.clone(), + font: self.font, style: self.style, } } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index ee0473ea..65a9bd3b 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -61,7 +61,7 @@ where placeholder: String, value: Value, is_secure: bool, - font: Renderer::Font, + font: Option<Renderer::Font>, width: Length, padding: Padding, size: Option<f32>, @@ -92,7 +92,7 @@ where placeholder: String::from(placeholder), value: Value::new(value), is_secure: false, - font: Default::default(), + font: None, width: Length::Fill, padding: Padding::new(5.0), size: None, @@ -129,7 +129,7 @@ where /// /// [`Font`]: text::Renderer::Font pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; + self.font = Some(font); self } /// Sets the width of the [`TextInput`]. @@ -188,7 +188,7 @@ where value.unwrap_or(&self.value), &self.placeholder, self.size, - &self.font, + self.font, self.is_secure, &self.style, ) @@ -258,7 +258,7 @@ where shell, &mut self.value, self.size, - &self.font, + self.font, self.is_secure, self.on_change.as_ref(), self.on_paste.as_deref(), @@ -286,7 +286,7 @@ where &self.value, &self.placeholder, self.size, - &self.font, + self.font, self.is_secure, &self.style, ) @@ -385,9 +385,8 @@ where Renderer: text::Renderer, { 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); + let limits = limits.width(width).pad(padding).height(text_size * 1.2); let mut text = layout::Node::new(limits.resolve(Size::ZERO)); text.move_to(Point::new(padding.left, padding.top)); @@ -406,7 +405,7 @@ pub fn update<'a, Message, Renderer>( shell: &mut Shell<'_, Message>, value: &mut Value, size: Option<f32>, - font: &Renderer::Font, + font: Option<Renderer::Font>, is_secure: bool, on_change: &dyn Fn(String) -> Message, on_paste: Option<&dyn Fn(String) -> Message>, @@ -455,7 +454,7 @@ where find_cursor_position( renderer, text_layout.bounds(), - font.clone(), + font, size, &value, state, @@ -483,7 +482,7 @@ where let position = find_cursor_position( renderer, text_layout.bounds(), - font.clone(), + font, size, value, state, @@ -532,7 +531,7 @@ where let position = find_cursor_position( renderer, text_layout.bounds(), - font.clone(), + font, size, &value, state, @@ -812,7 +811,7 @@ pub fn draw<Renderer>( value: &Value, placeholder: &str, size: Option<f32>, - font: &Renderer::Font, + font: Option<Renderer::Font>, is_secure: bool, style: &<Renderer::Theme as StyleSheet>::Style, ) where @@ -846,6 +845,7 @@ pub fn draw<Renderer>( ); let text = value.to_string(); + let font = font.unwrap_or_else(|| renderer.default_font()); let size = size.unwrap_or_else(|| renderer.default_size()); let (cursor, offset) = if let Some(focus) = &state.is_focused { @@ -858,7 +858,7 @@ pub fn draw<Renderer>( value, size, position, - font.clone(), + font, ); let is_cursor_visible = ((focus.now - focus.updated_at) @@ -899,7 +899,7 @@ pub fn draw<Renderer>( value, size, left, - font.clone(), + font, ); let (right_position, right_offset) = @@ -909,7 +909,7 @@ pub fn draw<Renderer>( value, size, right, - font.clone(), + font, ); let width = right_position - left_position; @@ -944,7 +944,7 @@ pub fn draw<Renderer>( let text_width = renderer.measure_width( if text.is_empty() { placeholder } else { &text }, size, - font.clone(), + font, ); let render = |renderer: &mut Renderer| { @@ -959,7 +959,7 @@ pub fn draw<Renderer>( } else { theme.value_color(style) }, - font: font.clone(), + font, bounds: Rectangle { y: text_bounds.center_y(), width: f32::INFINITY, @@ -1180,7 +1180,7 @@ where fn find_cursor_position<Renderer>( renderer: &Renderer, text_bounds: Rectangle, - font: Renderer::Font, + font: Option<Renderer::Font>, size: Option<f32>, value: &Value, state: &State, @@ -1189,21 +1189,30 @@ fn find_cursor_position<Renderer>( where Renderer: text::Renderer, { + let font = font.unwrap_or_else(|| renderer.default_font()); let size = size.unwrap_or_else(|| renderer.default_size()); - let offset = - offset(renderer, text_bounds, font.clone(), size, value, state); + let offset = offset(renderer, text_bounds, font, size, value, state); + let value = value.to_string(); - renderer + let char_offset = renderer .hit_test( - &value.to_string(), + &value, size, font, Size::INFINITY, Point::new(x + offset, text_bounds.height / 2.0), true, ) - .map(text::Hit::cursor) + .map(text::Hit::cursor)?; + + Some( + unicode_segmentation::UnicodeSegmentation::graphemes( + &value[..char_offset], + true, + ) + .count(), + ) } const CURSOR_BLINK_INTERVAL_MILLIS: u128 = 500; diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index a434af65..d9c80ebe 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -42,7 +42,7 @@ where text_size: Option<f32>, text_alignment: alignment::Horizontal, spacing: f32, - font: Renderer::Font, + font: Option<Renderer::Font>, style: <Renderer::Theme as StyleSheet>::Style, } @@ -79,7 +79,7 @@ where text_size: None, text_alignment: alignment::Horizontal::Left, spacing: 0.0, - font: Renderer::Font::default(), + font: None, style: Default::default(), } } @@ -117,8 +117,8 @@ where /// Sets the [`Font`] of the text of the [`Toggler`] /// /// [`Font`]: crate::text::Renderer::Font - pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; + pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self { + self.font = Some(font.into()); self } @@ -160,7 +160,7 @@ where row = row.push( Text::new(label) .horizontal_alignment(self.text_alignment) - .font(self.font.clone()) + .font(self.font.unwrap_or_else(|| renderer.default_font())) .width(self.width) .size( self.text_size @@ -243,7 +243,7 @@ where label_layout, label, self.text_size, - self.font.clone(), + self.font, Default::default(), self.text_alignment, alignment::Vertical::Center, |