diff options
Diffstat (limited to 'native/src/widget/text_input.rs')
-rw-r--r-- | native/src/widget/text_input.rs | 229 |
1 files changed, 223 insertions, 6 deletions
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 35e10000..bb5bb523 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -1,10 +1,95 @@ +//! Display fields that can be filled with text. +//! +//! A [`TextInput`] has some local [`State`]. +//! +//! [`TextInput`]: struct.TextInput.html +//! [`State`]: struct.State.html use crate::{ input::{keyboard, mouse, ButtonState}, layout, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; -pub use iced_core::{text_input::State, TextInput}; +/// A widget that can be filled with text by using a keyboard. +pub struct TextInput<'a, Message> { + state: &'a mut State, + placeholder: String, + value: Value, + width: Length, + max_width: Length, + padding: u16, + size: Option<u16>, + on_change: Box<dyn Fn(String) -> Message>, + on_submit: Option<Message>, +} + +impl<'a, Message> TextInput<'a, Message> { + /// Creates a new [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn new<F>( + state: &'a mut State, + placeholder: &str, + value: &str, + on_change: F, + ) -> Self + where + F: 'static + Fn(String) -> Message, + { + Self { + state, + placeholder: String::from(placeholder), + value: Value::new(value), + width: Length::Fill, + max_width: Length::Shrink, + padding: 0, + size: None, + on_change: Box::new(on_change), + on_submit: None, + } + } + + /// Sets the width of the [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the maximum width of the [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn max_width(mut self, max_width: Length) -> Self { + self.max_width = max_width; + self + } + + /// Sets the padding of the [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn padding(mut self, units: u16) -> Self { + self.padding = units; + self + } + + /// Sets the text size of the [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn size(mut self, size: u16) -> Self { + self.size = Some(size); + self + } + + /// Sets the message that should be produced when the [`TextInput`] is + /// focused and the enter key is pressed. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn on_submit(mut self, message: Message) -> Self { + self.on_submit = Some(message); + self + } +} impl<'a, Message, Renderer> Widget<Message, Renderer> for TextInput<'a, Message> where @@ -120,12 +205,19 @@ where let bounds = layout.bounds(); let text_bounds = layout.children().next().unwrap().bounds(); - renderer.draw(&self, bounds, text_bounds, cursor_position) + renderer.draw( + bounds, + text_bounds, + cursor_position, + self.size.unwrap_or(renderer.default_size()), + &self.placeholder, + &self.value, + &self.state, + ) } fn hash_layout(&self, state: &mut Hasher) { - use std::any::TypeId; - use std::hash::Hash; + use std::{any::TypeId, hash::Hash}; TypeId::of::<TextInput<'static, ()>>().hash(state); @@ -139,12 +231,15 @@ where pub trait Renderer: crate::Renderer + Sized { fn default_size(&self) -> u16; - fn draw<Message>( + fn draw( &mut self, - text_input: &TextInput<'_, Message>, bounds: Rectangle, text_bounds: Rectangle, cursor_position: Point, + size: u16, + placeholder: &str, + value: &Value, + state: &State, ) -> Self::Output; } @@ -160,3 +255,125 @@ where Element::new(text_input) } } + +/// The state of a [`TextInput`]. +/// +/// [`TextInput`]: struct.TextInput.html +#[derive(Debug, Default, Clone)] +pub struct State { + is_focused: bool, + cursor_position: usize, +} + +impl State { + /// Creates a new [`State`], representing an unfocused [`TextInput`]. + /// + /// [`State`]: struct.State.html + pub fn new() -> Self { + Self::default() + } + + /// Creates a new [`State`], representing a focused [`TextInput`]. + /// + /// [`State`]: struct.State.html + pub fn focused() -> Self { + use std::usize; + + Self { + is_focused: true, + cursor_position: usize::MAX, + } + } + + pub fn is_focused(&self) -> bool { + self.is_focused + } + + /// Returns the cursor position of a [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn cursor_position(&self, value: &Value) -> usize { + self.cursor_position.min(value.len()) + } + + /// Moves the cursor of a [`TextInput`] to the right. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn move_cursor_right(&mut self, value: &Value) { + let current = self.cursor_position(value); + + if current < value.len() { + self.cursor_position = current + 1; + } + } + + /// Moves the cursor of a [`TextInput`] to the left. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn move_cursor_left(&mut self, value: &Value) { + let current = self.cursor_position(value); + + if current > 0 { + self.cursor_position = current - 1; + } + } + + /// Moves the cursor of a [`TextInput`] to the end. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn move_cursor_to_end(&mut self, value: &Value) { + self.cursor_position = value.len(); + } +} + +/// The value of a [`TextInput`]. +/// +/// [`TextInput`]: struct.TextInput.html +// TODO: Use `unicode-segmentation` +#[derive(Debug)] +pub struct Value(Vec<char>); + +impl Value { + /// Creates a new [`Value`] from a string slice. + /// + /// [`Value`]: struct.Value.html + pub fn new(string: &str) -> Self { + Self(string.chars().collect()) + } + + /// Returns the total amount of `char` in the [`Value`]. + /// + /// [`Value`]: struct.Value.html + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns a new [`Value`] containing the `char` until the given `index`. + /// + /// [`Value`]: struct.Value.html + pub fn until(&self, index: usize) -> Self { + Self(self.0[..index.min(self.len())].to_vec()) + } + + /// Converts the [`Value`] into a `String`. + /// + /// [`Value`]: struct.Value.html + pub fn to_string(&self) -> String { + use std::iter::FromIterator; + String::from_iter(self.0.iter()) + } + + /// Inserts a new `char` at the given `index`. + /// + /// [`Value`]: struct.Value.html + pub fn insert(&mut self, index: usize, c: char) { + self.0.insert(index, c); + } + + /// Removes the `char` at the given `index`. + /// + /// [`Value`]: struct.Value.html + pub fn remove(&mut self, index: usize) { + let _ = self.0.remove(index); + } +} |