From 65eb218d3d7ba52b2869a586a1480eeb3c8f84e4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 Nov 2019 13:47:20 +0100 Subject: Move widgets from `core` to `native` and `web` Also made fields private and improved `Renderer` traits. --- native/src/widget/text_input.rs | 229 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 223 insertions(+), 6 deletions(-) (limited to 'native/src/widget/text_input.rs') 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, + on_change: Box Message>, + on_submit: Option, +} + +impl<'a, Message> TextInput<'a, Message> { + /// Creates a new [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn new( + 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 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::>().hash(state); @@ -139,12 +231,15 @@ where pub trait Renderer: crate::Renderer + Sized { fn default_size(&self) -> u16; - fn draw( + 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); + +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); + } +} -- cgit From a7dba612f03e58d7bd9527499d893987986b347c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Nov 2019 19:36:57 +0100 Subject: Write docs for `iced` and `iced_native` --- native/src/widget/text_input.rs | 60 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 65306504..c3876b1d 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -10,7 +10,26 @@ use crate::{ Widget, }; -/// A widget that can be filled with text by using a keyboard. +/// A field that can be filled with text. +/// +/// # Example +/// ``` +/// # use iced_native::{text_input, TextInput}; +/// # +/// 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, +/// ); +/// ``` pub struct TextInput<'a, Message> { state: &'a mut State, placeholder: String, @@ -26,7 +45,14 @@ pub struct TextInput<'a, 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( state: &'a mut State, placeholder: &str, @@ -232,9 +258,32 @@ 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; + /// 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, bounds: Rectangle, @@ -289,6 +338,9 @@ impl State { } } + /// Returns whether the [`TextInput`] is currently focused or not. + /// + /// [`TextInput`]: struct.TextInput.html pub fn is_focused(&self) -> bool { self.is_focused } @@ -303,7 +355,7 @@ impl State { /// Moves the cursor of a [`TextInput`] to the right. /// /// [`TextInput`]: struct.TextInput.html - pub fn move_cursor_right(&mut self, value: &Value) { + pub(crate) fn move_cursor_right(&mut self, value: &Value) { let current = self.cursor_position(value); if current < value.len() { @@ -314,7 +366,7 @@ impl State { /// Moves the cursor of a [`TextInput`] to the left. /// /// [`TextInput`]: struct.TextInput.html - pub fn move_cursor_left(&mut self, value: &Value) { + pub(crate) fn move_cursor_left(&mut self, value: &Value) { let current = self.cursor_position(value); if current > 0 { @@ -325,7 +377,7 @@ impl State { /// Moves the cursor of a [`TextInput`] to the end. /// /// [`TextInput`]: struct.TextInput.html - pub fn move_cursor_to_end(&mut self, value: &Value) { + pub(crate) fn move_cursor_to_end(&mut self, value: &Value) { self.cursor_position = value.len(); } } -- cgit From d136b7ce648cde0dcdcc5388d8cb82b3e7e0fc58 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Nov 2019 21:16:40 +0100 Subject: Uncomment missing debug implementations rule --- native/src/widget/text_input.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index c3876b1d..fbf144e3 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -30,6 +30,7 @@ use crate::{ /// Message::TextInputChanged, /// ); /// ``` +#[allow(missing_debug_implementations)] pub struct TextInput<'a, Message> { state: &'a mut State, placeholder: String, -- cgit From f943764a292837cbf262a4f29dc0021d808852d6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 24 Nov 2019 10:44:55 +0100 Subject: Fix `iced_native` widget examples --- native/src/widget/text_input.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index fbf144e3..6e0857ae 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -28,7 +28,8 @@ use crate::{ /// "This is the placeholder...", /// value, /// Message::TextInputChanged, -/// ); +/// ) +/// .padding(10); /// ``` #[allow(missing_debug_implementations)] pub struct TextInput<'a, Message> { -- cgit From 47196c9007d12d3b3e0036ffabe3bf6d14ff4523 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 24 Nov 2019 10:48:29 +0100 Subject: Display widget images in documentation --- native/src/widget/text_input.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 6e0857ae..f97ed424 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -16,6 +16,7 @@ use crate::{ /// ``` /// # use iced_native::{text_input, TextInput}; /// # +/// #[derive(Debug, Clone)] /// enum Message { /// TextInputChanged(String), /// } @@ -31,6 +32,7 @@ use crate::{ /// ) /// .padding(10); /// ``` +/// ![Text input drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true) #[allow(missing_debug_implementations)] pub struct TextInput<'a, Message> { state: &'a mut State, -- cgit