From 63cd0fd8eb1eebae8de7d5141c846fc4ea55d702 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 30 Oct 2019 03:31:07 +0100 Subject: Draft `TextInput` widget structure Also started a `todos` example to showcase it! --- core/src/widget/text_input.rs | 84 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 core/src/widget/text_input.rs (limited to 'core/src/widget') diff --git a/core/src/widget/text_input.rs b/core/src/widget/text_input.rs new file mode 100644 index 00000000..2f20635f --- /dev/null +++ b/core/src/widget/text_input.rs @@ -0,0 +1,84 @@ +use crate::Length; + +pub struct TextInput<'a, Message> { + pub state: &'a mut State, + pub placeholder: String, + pub value: String, + pub width: Length, + pub max_width: Length, + pub padding: u16, + pub size: Option, + pub on_change: Box Message>, + pub on_submit: Option, +} + +#[derive(Debug, Default)] +pub struct State {} + +impl<'a, Message> TextInput<'a, Message> { + 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: String::from(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 + } + + pub fn size(mut self, size: u16) -> Self { + self.size = Some(size); + self + } + + pub fn on_submit(mut self, message: Message) -> Self { + self.on_submit = Some(message); + self + } +} + +impl<'a, Message> std::fmt::Debug for TextInput<'a, Message> +where + Message: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // TODO: Complete once stabilized + f.debug_struct("TextInput").finish() + } +} -- cgit From fedcab6f4f5ffd3a6dfffe7dd41c58df2e314099 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 30 Oct 2019 05:00:12 +0100 Subject: Handle some `TextInput` events --- core/src/widget/text_input.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'core/src/widget') diff --git a/core/src/widget/text_input.rs b/core/src/widget/text_input.rs index 2f20635f..e0f0744b 100644 --- a/core/src/widget/text_input.rs +++ b/core/src/widget/text_input.rs @@ -13,7 +13,9 @@ pub struct TextInput<'a, Message> { } #[derive(Debug, Default)] -pub struct State {} +pub struct State { + pub is_focused: bool, +} impl<'a, Message> TextInput<'a, Message> { pub fn new( -- cgit From 51a0e99097f9ecb63eeb7f2ea7c38089977eb4d0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 31 Oct 2019 03:50:40 +0100 Subject: Implement cursor movement in `TextInput` --- core/src/widget/text_input.rs | 92 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 7 deletions(-) (limited to 'core/src/widget') diff --git a/core/src/widget/text_input.rs b/core/src/widget/text_input.rs index e0f0744b..95064ab3 100644 --- a/core/src/widget/text_input.rs +++ b/core/src/widget/text_input.rs @@ -1,9 +1,11 @@ use crate::Length; +use std::ops::{Index, RangeTo}; + pub struct TextInput<'a, Message> { pub state: &'a mut State, pub placeholder: String, - pub value: String, + pub value: Value, pub width: Length, pub max_width: Length, pub padding: u16, @@ -12,11 +14,6 @@ pub struct TextInput<'a, Message> { pub on_submit: Option, } -#[derive(Debug, Default)] -pub struct State { - pub is_focused: bool, -} - impl<'a, Message> TextInput<'a, Message> { pub fn new( state: &'a mut State, @@ -30,7 +27,7 @@ impl<'a, Message> TextInput<'a, Message> { Self { state, placeholder: String::from(placeholder), - value: String::from(value), + value: Value::new(value), width: Length::Fill, max_width: Length::Shrink, padding: 0, @@ -84,3 +81,84 @@ where f.debug_struct("TextInput").finish() } } + +#[derive(Debug, Default)] +pub struct State { + pub is_focused: bool, + cursor_position: usize, +} + +impl State { + pub fn new() -> Self { + Self::default() + } + + pub fn focused() -> Self { + Self { + is_focused: true, + ..Self::default() + } + } + + pub fn move_cursor_right(&mut self, value: &Value) { + let current = self.cursor_position(value); + + if current < value.len() { + self.cursor_position = current + 1; + } + } + + pub fn move_cursor_left(&mut self, value: &Value) { + let current = self.cursor_position(value); + + if current > 0 { + self.cursor_position = current - 1; + } + } + + pub fn cursor_position(&self, value: &Value) -> usize { + self.cursor_position.min(value.len()) + } +} + +pub struct Value(Vec); + +impl Value { + pub fn new(string: &str) -> Self { + Self(string.chars().collect()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn until(&self, index: usize) -> Self { + Self(self.0[..index].iter().cloned().collect()) + } + + pub fn to_string(&self) -> String { + let mut string = String::new(); + + for c in self.0.iter() { + string.push(*c); + } + + string + } + + pub fn insert(&mut self, index: usize, c: char) { + self.0.insert(index, c); + } + + pub fn remove(&mut self, index: usize) { + self.0.remove(index); + } +} + +impl Index> for Value { + type Output = [char]; + + fn index(&self, index: RangeTo) -> &[char] { + &self.0[index] + } +} -- cgit From d3cdee1d9bcd9f7fe22eeccce93ab5a26faf18e8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 31 Oct 2019 04:43:29 +0100 Subject: Render `TextInput` cursor inside the clipping area --- core/src/widget/text_input.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'core/src/widget') diff --git a/core/src/widget/text_input.rs b/core/src/widget/text_input.rs index 95064ab3..6c7dff67 100644 --- a/core/src/widget/text_input.rs +++ b/core/src/widget/text_input.rs @@ -121,6 +121,7 @@ impl State { } } +// TODO: Use `unicode-segmentation` pub struct Value(Vec); impl Value { -- cgit From ba470a2b2a334fa961af19ef9412ad2dfb4acbeb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 5 Nov 2019 02:58:42 +0100 Subject: Remove unnecessary code in `Value` --- core/src/widget/text_input.rs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) (limited to 'core/src/widget') diff --git a/core/src/widget/text_input.rs b/core/src/widget/text_input.rs index 6c7dff67..e5d6b70d 100644 --- a/core/src/widget/text_input.rs +++ b/core/src/widget/text_input.rs @@ -1,7 +1,5 @@ use crate::Length; -use std::ops::{Index, RangeTo}; - pub struct TextInput<'a, Message> { pub state: &'a mut State, pub placeholder: String, @@ -93,13 +91,6 @@ impl State { Self::default() } - pub fn focused() -> Self { - Self { - is_focused: true, - ..Self::default() - } - } - pub fn move_cursor_right(&mut self, value: &Value) { let current = self.cursor_position(value); @@ -134,7 +125,7 @@ impl Value { } pub fn until(&self, index: usize) -> Self { - Self(self.0[..index].iter().cloned().collect()) + Self(self.0[..index.min(self.len())].iter().cloned().collect()) } pub fn to_string(&self) -> String { @@ -155,11 +146,3 @@ impl Value { self.0.remove(index); } } - -impl Index> for Value { - type Output = [char]; - - fn index(&self, index: RangeTo) -> &[char] { - &self.0[index] - } -} -- cgit