diff options
author | 2019-11-24 11:34:30 +0100 | |
---|---|---|
committer | 2019-11-24 11:34:30 +0100 | |
commit | 149fd2aa1fa86858c7c1dcec8fd844caa78cec94 (patch) | |
tree | a199cf8d2caaf6aa60e48e93d6dd0688969d43b0 /native/src/widget/text_input.rs | |
parent | 9712b319bb7a32848001b96bd84977430f14b623 (diff) | |
parent | 47196c9007d12d3b3e0036ffabe3bf6d14ff4523 (diff) | |
download | iced-149fd2aa1fa86858c7c1dcec8fd844caa78cec94.tar.gz iced-149fd2aa1fa86858c7c1dcec8fd844caa78cec94.tar.bz2 iced-149fd2aa1fa86858c7c1dcec8fd844caa78cec94.zip |
Merge pull request #65 from hecrj/improvement/docs
Documentation
Diffstat (limited to 'native/src/widget/text_input.rs')
-rw-r--r-- | native/src/widget/text_input.rs | 285 |
1 files changed, 279 insertions, 6 deletions
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index d54cf82c..f97ed424 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -1,10 +1,125 @@ +//! 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 field that can be filled with text. +/// +/// # Example +/// ``` +/// # use iced_native::{text_input, TextInput}; +/// # +/// #[derive(Debug, Clone)] +/// enum Message { +/// TextInputChanged(String), +/// } +/// +/// let mut state = text_input::State::new(); +/// let value = "Some text"; +/// +/// let input = TextInput::new( +/// &mut state, +/// "This is the placeholder...", +/// value, +/// Message::TextInputChanged, +/// ) +/// .padding(10); +/// ``` +///  +#[allow(missing_debug_implementations)] +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`]. + /// + /// It expects: + /// - some [`State`] + /// - a placeholder + /// - the current value + /// - a function that produces a message when the [`TextInput`] changes + /// + /// [`TextInput`]: struct.TextInput.html + /// [`State`]: struct.State.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 @@ -124,12 +239,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); @@ -140,15 +262,41 @@ where } } +/// The renderer of a [`TextInput`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`TextInput`] in your user interface. +/// +/// [`TextInput`]: struct.TextInput.html +/// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + /// Returns the default size of the text of the [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html fn default_size(&self) -> u16; - fn draw<Message>( + /// Draws a [`TextInput`]. + /// + /// It receives: + /// - its bounds of the [`TextInput`] + /// - the bounds of the text (i.e. the current value) + /// - the cursor position + /// - the placeholder to show when the value is empty + /// - the current [`Value`] + /// - the current [`State`] + /// + /// [`TextInput`]: struct.TextInput.html + /// [`Value`]: struct.Value.html + /// [`State`]: struct.State.html + 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; } @@ -164,3 +312,128 @@ 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, + } + } + + /// Returns whether the [`TextInput`] is currently focused or not. + /// + /// [`TextInput`]: struct.TextInput.html + 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(crate) 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(crate) 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(crate) 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); + } +} |