diff options
Diffstat (limited to '')
-rw-r--r-- | widget/src/text_input.rs (renamed from native/src/widget/text_input.rs) | 145 |
1 files changed, 98 insertions, 47 deletions
diff --git a/native/src/widget/text_input.rs b/widget/src/text_input.rs index 6113cf94..bbc07dac 100644 --- a/native/src/widget/text_input.rs +++ b/widget/src/text_input.rs @@ -11,31 +11,34 @@ pub use value::Value; use editor::Editor; -use crate::alignment; -use crate::event::{self, Event}; -use crate::keyboard; -use crate::layout; -use crate::mouse::{self, click}; -use crate::renderer; -use crate::text::{self, Text}; -use crate::time::{Duration, Instant}; -use crate::touch; -use crate::widget; -use crate::widget::operation::{self, Operation}; -use crate::widget::tree::{self, Tree}; -use crate::window; -use crate::{ - Clipboard, Color, Command, Element, Layout, Length, Padding, Pixels, Point, +use crate::core::alignment; +use crate::core::event::{self, Event}; +use crate::core::keyboard; +use crate::core::layout; +use crate::core::mouse::{self, click}; +use crate::core::renderer; +use crate::core::text::{self, Text}; +use crate::core::time::{Duration, Instant}; +use crate::core::touch; +use crate::core::widget; +use crate::core::widget::operation::{self, Operation}; +use crate::core::widget::tree::{self, Tree}; +use crate::core::window; +use crate::core::{ + Clipboard, Color, Element, Layout, Length, Padding, Pixels, Point, Rectangle, Shell, Size, Vector, Widget, }; +use crate::runtime::Command; pub use iced_style::text_input::{Appearance, StyleSheet}; /// A field that can be filled with text. /// /// # Example -/// ``` -/// # pub type TextInput<'a, Message> = iced_native::widget::TextInput<'a, Message, iced_native::renderer::Null>; +/// ```no_run +/// # pub type TextInput<'a, Message> = +/// # iced_widget::TextInput<'a, Message, iced_widget::renderer::Renderer<iced_widget::style::Theme>>; +/// # /// #[derive(Debug, Clone)] /// enum Message { /// TextInputChanged(String), @@ -52,7 +55,7 @@ pub use iced_style::text_input::{Appearance, StyleSheet}; /// ``` ///  #[allow(missing_debug_implementations)] -pub struct TextInput<'a, Message, Renderer> +pub struct TextInput<'a, Message, Renderer = crate::Renderer> where Renderer: text::Renderer, Renderer::Theme: StyleSheet, @@ -61,10 +64,11 @@ where placeholder: String, value: Value, is_secure: bool, - font: Renderer::Font, + font: Option<Renderer::Font>, 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>, @@ -89,10 +93,11 @@ 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, + line_height: text::LineHeight::default(), on_input: None, on_paste: None, on_submit: None, @@ -146,7 +151,7 @@ where /// /// [`Font`]: text::Renderer::Font pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; + self.font = Some(font); self } @@ -174,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, @@ -205,7 +219,8 @@ where value.unwrap_or(&self.value), &self.placeholder, self.size, - &self.font, + self.line_height, + self.font, self.on_input.is_none(), self.is_secure, self.icon.as_ref(), @@ -260,6 +275,7 @@ where self.width, self.padding, self.size, + self.line_height, self.icon.as_ref(), ) } @@ -296,7 +312,8 @@ where shell, &mut self.value, self.size, - &self.font, + self.line_height, + self.font, self.is_secure, self.on_input.as_deref(), self.on_paste.as_deref(), @@ -324,7 +341,8 @@ where &self.value, &self.placeholder, self.size, - &self.font, + self.line_height, + self.font, self.on_input.is_none(), self.is_secure, self.icon.as_ref(), @@ -444,15 +462,18 @@ pub fn layout<Renderer>( width: Length, padding: Padding, size: Option<f32>, + line_height: text::LineHeight, icon: Option<&Icon<Renderer::Font>>, ) -> layout::Node 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(line_height.to_absolute(Pixels(text_size))); let text_bounds = limits.resolve(Size::ZERO); @@ -460,7 +481,8 @@ where let icon_width = renderer.measure_width( &icon.code_point.to_string(), icon.size.unwrap_or_else(|| renderer.default_size()), - icon.font.clone(), + icon.font, + text::Shaping::Advanced, ); let mut text_node = layout::Node::new( @@ -512,7 +534,8 @@ pub fn update<'a, Message, Renderer>( shell: &mut Shell<'_, Message>, value: &mut Value, size: Option<f32>, - font: &Renderer::Font, + line_height: text::LineHeight, + font: Option<Renderer::Font>, is_secure: bool, on_input: Option<&dyn Fn(String) -> Message>, on_paste: Option<&dyn Fn(String) -> Message>, @@ -562,8 +585,9 @@ where find_cursor_position( renderer, text_layout.bounds(), - font.clone(), + font, size, + line_height, &value, state, target, @@ -590,8 +614,9 @@ where let position = find_cursor_position( renderer, text_layout.bounds(), - font.clone(), + font, size, + line_height, value, state, target, @@ -639,8 +664,9 @@ where let position = find_cursor_position( renderer, text_layout.bounds(), - font.clone(), + font, size, + line_height, &value, state, target, @@ -923,7 +949,8 @@ pub fn draw<Renderer>( value: &Value, placeholder: &str, size: Option<f32>, - font: &Renderer::Font, + line_height: text::LineHeight, + font: Option<Renderer::Font>, is_disabled: bool, is_secure: bool, icon: Option<&Icon<Renderer::Font>>, @@ -968,7 +995,8 @@ pub fn draw<Renderer>( renderer.fill_text(Text { content: &icon.code_point.to_string(), size: icon.size.unwrap_or_else(|| renderer.default_size()), - font: icon.font.clone(), + line_height: text::LineHeight::default(), + font: icon.font, color: appearance.icon_color, bounds: Rectangle { y: text_bounds.center_y(), @@ -976,10 +1004,12 @@ pub fn draw<Renderer>( }, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, + shaping: text::Shaping::Advanced, }); } 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 { @@ -992,7 +1022,7 @@ pub fn draw<Renderer>( value, size, position, - font.clone(), + font, ); let is_cursor_visible = ((focus.now - focus.updated_at) @@ -1033,7 +1063,7 @@ pub fn draw<Renderer>( value, size, left, - font.clone(), + font, ); let (right_position, right_offset) = @@ -1043,7 +1073,7 @@ pub fn draw<Renderer>( value, size, right, - font.clone(), + font, ); let width = right_position - left_position; @@ -1078,12 +1108,15 @@ pub fn draw<Renderer>( let text_width = renderer.measure_width( if text.is_empty() { placeholder } else { &text }, size, - font.clone(), + font, + text::Shaping::Advanced, ); let render = |renderer: &mut Renderer| { if let Some((cursor, color)) = cursor { renderer.fill_quad(cursor, color); + } else { + renderer.with_translation(Vector::ZERO, |_| {}); } renderer.fill_text(Text { @@ -1095,15 +1128,17 @@ pub fn draw<Renderer>( } else { theme.value_color(style) }, - font: font.clone(), + font, bounds: Rectangle { y: text_bounds.center_y(), width: f32::INFINITY, ..text_bounds }, size, + line_height, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, + shaping: text::Shaping::Advanced, }); }; @@ -1250,7 +1285,7 @@ impl operation::TextInput for State { } mod platform { - use crate::keyboard; + use crate::core::keyboard; pub fn is_jump_modifier_pressed(modifiers: keyboard::Modifiers) -> bool { if cfg!(target_os = "macos") { @@ -1308,8 +1343,12 @@ where { let text_before_cursor = value.until(cursor_index).to_string(); - let text_value_width = - renderer.measure_width(&text_before_cursor, size, font); + 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); @@ -1321,8 +1360,9 @@ where fn find_cursor_position<Renderer>( renderer: &Renderer, text_bounds: Rectangle, - font: Renderer::Font, + font: Option<Renderer::Font>, size: Option<f32>, + line_height: text::LineHeight, value: &Value, state: &State, x: f32, @@ -1330,21 +1370,32 @@ 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, + line_height, font, Size::INFINITY, + text::Shaping::Advanced, 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; |