From c6c8cabdaf03f90b1739be828cf140d8ddb92e65 Mon Sep 17 00:00:00 2001 From: FabianLars Date: Mon, 24 Feb 2020 18:03:42 +0100 Subject: moved cursor into own file moved click tracking as a new State struct to input::mouse made cursor field of text_input state private brought back cursor type(Index, Selection) representation with a state enum cleaned out some stuff (but not enough/all) TODO: Documentation (sigh) TODO: Editor struct TODO: some (hopefully) small improvements here and there --- native/src/widget/text_input/cursor.rs | 176 +++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 native/src/widget/text_input/cursor.rs (limited to 'native/src/widget/text_input') diff --git a/native/src/widget/text_input/cursor.rs b/native/src/widget/text_input/cursor.rs new file mode 100644 index 00000000..bbf5448b --- /dev/null +++ b/native/src/widget/text_input/cursor.rs @@ -0,0 +1,176 @@ +use crate::widget::text_input::Value; + +#[derive(Debug, Copy, Clone)] +enum State { + Index(usize), + Selection { start: usize, end: usize }, +} + +#[derive(Debug, Copy, Clone)] +pub struct Cursor { + state: State, +} + +impl Default for Cursor { + fn default() -> Self { + Cursor { + state: State::Index(0), + } + } +} + +impl Cursor { + /* index move methods */ + pub fn move_to(&mut self, position: usize) { + self.state = State::Index(position); + } + + pub fn move_right(&mut self, value: &Value) { + self.move_right_by_amount(value, 1) + } + + pub fn move_right_by_words(&mut self, value: &Value) { + self.move_to(value.next_end_of_word(self.right())) + } + + pub fn move_right_by_amount(&mut self, value: &Value, amount: usize) { + match self.state { + State::Index(index) => { + self.move_to(index.saturating_add(amount).min(value.len())) + } + State::Selection { .. } => self.move_to(self.right()), + } + } + + pub fn move_left(&mut self) { + match self.state { + State::Index(index) if index > 0 => self.move_to(index - 1), + State::Selection { .. } => self.move_to(self.left()), + _ => self.move_to(0), + } + } + + pub fn move_left_by_words(&mut self, value: &Value) { + self.move_to(value.previous_start_of_word(self.right())); + } + /* end of index move methods */ + + /* expand/shrink selection */ + // TODO: (whole section): Return State::Cursor if start == end after operation + pub fn select_range(&mut self, start: usize, end: usize) { + self.state = State::Selection { start, end }; + } + + pub fn select_left(&mut self) { + match self.state { + State::Index(index) if index > 0 => { + self.select_range(index, index - 1) + } + State::Selection { start, end } if end > 0 => { + self.select_range(start, end - 1) + } + _ => (), + } + } + + pub fn select_right(&mut self, value: &Value) { + match self.state { + State::Index(index) if index < value.len() => { + self.select_range(index, index + 1) + } + State::Selection { start, end } if end < value.len() => { + self.select_range(start, end + 1) + } + _ => (), + } + } + + pub fn select_left_by_words(&mut self, value: &Value) { + match self.state { + State::Index(index) => { + self.select_range(index, value.previous_start_of_word(index)) + } + State::Selection { start, end } => { + self.select_range(start, value.previous_start_of_word(end)) + } + } + } + + pub fn select_right_by_words(&mut self, value: &Value) { + match self.state { + State::Index(index) => { + self.select_range(index, value.next_end_of_word(index)) + } + State::Selection { start, end } => { + self.select_range(start, value.next_end_of_word(end)) + } + } + } + + pub fn select_all(&mut self, value: &Value) { + self.select_range(0, value.len()); + } + /* end of selection section */ + + /* helpers */ + // get start position of selection (can be left OR right boundary of selection) or index + pub(crate) fn start(&self) -> usize { + match self.state { + State::Index(index) => index, + State::Selection { start, .. } => start, + } + } + + // get end position of selection (can be left OR right boundary of selection) or index + pub fn end(&self) -> usize { + match self.state { + State::Index(index) => index, + State::Selection { end, .. } => end, + } + } + + // get left boundary of selection or index + pub fn left(&self) -> usize { + match self.state { + State::Index(index) => index, + State::Selection { start, end } => start.min(end), + } + } + + // get right boundary of selection or index + pub fn right(&self) -> usize { + match self.state { + State::Index(index) => index, + State::Selection { start, end } => start.max(end), + } + } + + pub fn draw_position(&self, value: &Value) -> usize { + self.cursor_position(value) + } + + pub fn cursor_position(&self, value: &Value) -> usize { + match self.state { + State::Index(index) => index.min(value.len()), + State::Selection { end, .. } => end.min(value.len()), + } + } + + // returns Option of left and right border of selection + // a second method return start and end may be useful (see below) + pub fn selection_position(&self) -> Option<(usize, usize)> { + match self.state { + State::Selection { start, end } => { + Some((start.min(end), start.max(end))) + } + _ => None, + } + } + + /* pub fn selection_position(&self) -> Option<(usize, usize)> { + match self.state { + State::Selection { start, end } => Some((start, end)), + _ => None, + } + } */ +} -- cgit From 0d8d236be65abf98a09a13b2e22b677f3d95f195 Mon Sep 17 00:00:00 2001 From: FabianLars Date: Tue, 25 Feb 2020 17:03:52 +0100 Subject: More selection actions: (Ctrl +) Shift + Left/Right, Shift + Home/End --- native/src/widget/text_input/cursor.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'native/src/widget/text_input') diff --git a/native/src/widget/text_input/cursor.rs b/native/src/widget/text_input/cursor.rs index bbf5448b..307040eb 100644 --- a/native/src/widget/text_input/cursor.rs +++ b/native/src/widget/text_input/cursor.rs @@ -58,7 +58,11 @@ impl Cursor { /* expand/shrink selection */ // TODO: (whole section): Return State::Cursor if start == end after operation pub fn select_range(&mut self, start: usize, end: usize) { - self.state = State::Selection { start, end }; + if start != end { + self.state = State::Selection { start, end }; + } else { + self.state = State::Index(start); + } } pub fn select_left(&mut self) { -- cgit From 1ad83889be32b1bdb746e751aa620fad85b864b0 Mon Sep 17 00:00:00 2001 From: FabianLars Date: Wed, 26 Feb 2020 21:58:34 +0100 Subject: really small cleanup --- native/src/widget/text_input/cursor.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'native/src/widget/text_input') diff --git a/native/src/widget/text_input/cursor.rs b/native/src/widget/text_input/cursor.rs index 307040eb..92fd2029 100644 --- a/native/src/widget/text_input/cursor.rs +++ b/native/src/widget/text_input/cursor.rs @@ -56,7 +56,6 @@ impl Cursor { /* end of index move methods */ /* expand/shrink selection */ - // TODO: (whole section): Return State::Cursor if start == end after operation pub fn select_range(&mut self, start: usize, end: usize) { if start != end { self.state = State::Selection { start, end }; @@ -149,10 +148,6 @@ impl Cursor { } } - pub fn draw_position(&self, value: &Value) -> usize { - self.cursor_position(value) - } - pub fn cursor_position(&self, value: &Value) -> usize { match self.state { State::Index(index) => index.min(value.len()), @@ -161,7 +156,7 @@ impl Cursor { } // returns Option of left and right border of selection - // a second method return start and end may be useful (see below) + // a second method that returns start and end may be useful (see below) pub fn selection_position(&self) -> Option<(usize, usize)> { match self.state { State::Selection { start, end } => { -- cgit From 6b89dd7db9715dd46738677e13ca9d9bd12f9636 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 24 Mar 2020 20:23:31 +0100 Subject: Improve `text_input::cursor` API --- native/src/widget/text_input/cursor.rs | 98 ++++++++++++++++------------------ 1 file changed, 47 insertions(+), 51 deletions(-) (limited to 'native/src/widget/text_input') diff --git a/native/src/widget/text_input/cursor.rs b/native/src/widget/text_input/cursor.rs index 92fd2029..d8938777 100644 --- a/native/src/widget/text_input/cursor.rs +++ b/native/src/widget/text_input/cursor.rs @@ -1,7 +1,8 @@ +//! Track the cursor of a text input. use crate::widget::text_input::Value; #[derive(Debug, Copy, Clone)] -enum State { +pub enum State { Index(usize), Selection { start: usize, end: usize }, } @@ -20,7 +21,6 @@ impl Default for Cursor { } impl Cursor { - /* index move methods */ pub fn move_to(&mut self, position: usize) { self.state = State::Index(position); } @@ -30,42 +30,40 @@ impl Cursor { } pub fn move_right_by_words(&mut self, value: &Value) { - self.move_to(value.next_end_of_word(self.right())) + self.move_to(value.next_end_of_word(self.right(value))) } pub fn move_right_by_amount(&mut self, value: &Value, amount: usize) { - match self.state { + match self.state(value) { State::Index(index) => { self.move_to(index.saturating_add(amount).min(value.len())) } - State::Selection { .. } => self.move_to(self.right()), + State::Selection { start, end } => self.move_to(end.max(start)), } } - pub fn move_left(&mut self) { - match self.state { + pub fn move_left(&mut self, value: &Value) { + match self.state(value) { State::Index(index) if index > 0 => self.move_to(index - 1), - State::Selection { .. } => self.move_to(self.left()), + State::Selection { start, end } => self.move_to(start.min(end)), _ => self.move_to(0), } } pub fn move_left_by_words(&mut self, value: &Value) { - self.move_to(value.previous_start_of_word(self.right())); + self.move_to(value.previous_start_of_word(self.left(value))); } - /* end of index move methods */ - /* expand/shrink selection */ pub fn select_range(&mut self, start: usize, end: usize) { - if start != end { - self.state = State::Selection { start, end }; - } else { + if start == end { self.state = State::Index(start); + } else { + self.state = State::Selection { start, end }; } } - pub fn select_left(&mut self) { - match self.state { + pub fn select_left(&mut self, value: &Value) { + match self.state(value) { State::Index(index) if index > 0 => { self.select_range(index, index - 1) } @@ -77,7 +75,7 @@ impl Cursor { } pub fn select_right(&mut self, value: &Value) { - match self.state { + match self.state(value) { State::Index(index) if index < value.len() => { self.select_range(index, index + 1) } @@ -89,7 +87,7 @@ impl Cursor { } pub fn select_left_by_words(&mut self, value: &Value) { - match self.state { + match self.state(value) { State::Index(index) => { self.select_range(index, value.previous_start_of_word(index)) } @@ -100,7 +98,7 @@ impl Cursor { } pub fn select_right_by_words(&mut self, value: &Value) { - match self.state { + match self.state(value) { State::Index(index) => { self.select_range(index, value.next_end_of_word(index)) } @@ -113,51 +111,56 @@ impl Cursor { pub fn select_all(&mut self, value: &Value) { self.select_range(0, value.len()); } - /* end of selection section */ - /* helpers */ - // get start position of selection (can be left OR right boundary of selection) or index - pub(crate) fn start(&self) -> usize { + pub fn state(&self, value: &Value) -> State { match self.state { + State::Index(index) => State::Index(index.min(value.len())), + State::Selection { start, end } => { + let start = start.min(value.len()); + let end = end.min(value.len()); + + if start == end { + State::Index(start) + } else { + State::Selection { start, end } + } + } + } + } + + pub fn start(&self, value: &Value) -> usize { + let start = match self.state { State::Index(index) => index, State::Selection { start, .. } => start, - } + }; + + start.min(value.len()) } - // get end position of selection (can be left OR right boundary of selection) or index - pub fn end(&self) -> usize { - match self.state { + pub fn end(&self, value: &Value) -> usize { + let end = match self.state { State::Index(index) => index, State::Selection { end, .. } => end, - } + }; + + end.min(value.len()) } - // get left boundary of selection or index - pub fn left(&self) -> usize { - match self.state { + fn left(&self, value: &Value) -> usize { + match self.state(value) { State::Index(index) => index, State::Selection { start, end } => start.min(end), } } - // get right boundary of selection or index - pub fn right(&self) -> usize { - match self.state { + fn right(&self, value: &Value) -> usize { + match self.state(value) { State::Index(index) => index, State::Selection { start, end } => start.max(end), } } - pub fn cursor_position(&self, value: &Value) -> usize { - match self.state { - State::Index(index) => index.min(value.len()), - State::Selection { end, .. } => end.min(value.len()), - } - } - - // returns Option of left and right border of selection - // a second method that returns start and end may be useful (see below) - pub fn selection_position(&self) -> Option<(usize, usize)> { + pub fn selection(&self) -> Option<(usize, usize)> { match self.state { State::Selection { start, end } => { Some((start.min(end), start.max(end))) @@ -165,11 +168,4 @@ impl Cursor { _ => None, } } - - /* pub fn selection_position(&self) -> Option<(usize, usize)> { - match self.state { - State::Selection { start, end } => Some((start, end)), - _ => None, - } - } */ } -- cgit From 28382a47d3abd4f79064b610f1a2eca478a08595 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 24 Mar 2020 20:36:33 +0100 Subject: Move `Value` to its own module --- native/src/widget/text_input/value.rs | 134 ++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 native/src/widget/text_input/value.rs (limited to 'native/src/widget/text_input') diff --git a/native/src/widget/text_input/value.rs b/native/src/widget/text_input/value.rs new file mode 100644 index 00000000..1e9ba45b --- /dev/null +++ b/native/src/widget/text_input/value.rs @@ -0,0 +1,134 @@ +use unicode_segmentation::UnicodeSegmentation; + +/// The value of a [`TextInput`]. +/// +/// [`TextInput`]: struct.TextInput.html +// TODO: Reduce allocations, cache results (?) +#[derive(Debug, Clone)] +pub struct Value { + graphemes: Vec, +} + +impl Value { + /// Creates a new [`Value`] from a string slice. + /// + /// [`Value`]: struct.Value.html + pub fn new(string: &str) -> Self { + let graphemes = UnicodeSegmentation::graphemes(string, true) + .map(String::from) + .collect(); + + Self { graphemes } + } + + /// Returns the total amount of graphemes in the [`Value`]. + /// + /// [`Value`]: struct.Value.html + pub fn len(&self) -> usize { + self.graphemes.len() + } + + /// Returns the position of the previous start of a word from the given + /// grapheme `index`. + /// + /// [`Value`]: struct.Value.html + pub fn previous_start_of_word(&self, index: usize) -> usize { + let previous_string = + &self.graphemes[..index.min(self.graphemes.len())].concat(); + + UnicodeSegmentation::split_word_bound_indices(&previous_string as &str) + .filter(|(_, word)| !word.trim_start().is_empty()) + .next_back() + .map(|(i, previous_word)| { + index + - UnicodeSegmentation::graphemes(previous_word, true) + .count() + - UnicodeSegmentation::graphemes( + &previous_string[i + previous_word.len()..] as &str, + true, + ) + .count() + }) + .unwrap_or(0) + } + + /// Returns the position of the next end of a word from the given grapheme + /// `index`. + /// + /// [`Value`]: struct.Value.html + pub fn next_end_of_word(&self, index: usize) -> usize { + let next_string = &self.graphemes[index..].concat(); + + UnicodeSegmentation::split_word_bound_indices(&next_string as &str) + .filter(|(_, word)| !word.trim_start().is_empty()) + .next() + .map(|(i, next_word)| { + index + + UnicodeSegmentation::graphemes(next_word, true).count() + + UnicodeSegmentation::graphemes( + &next_string[..i] as &str, + true, + ) + .count() + }) + .unwrap_or(self.len()) + } + + /// Returns a new [`Value`] containing the graphemes until the given + /// `index`. + /// + /// [`Value`]: struct.Value.html + pub fn until(&self, index: usize) -> Self { + let graphemes = self.graphemes[..index.min(self.len())].to_vec(); + + Self { graphemes } + } + + /// Converts the [`Value`] into a `String`. + /// + /// [`Value`]: struct.Value.html + pub fn to_string(&self) -> String { + self.graphemes.concat() + } + + /// Inserts a new `char` at the given grapheme `index`. + pub fn insert(&mut self, index: usize, c: char) { + self.graphemes.insert(index, c.to_string()); + + self.graphemes = + UnicodeSegmentation::graphemes(&self.to_string() as &str, true) + .map(String::from) + .collect(); + } + + /// Inserts a bunch of graphemes at the given grapheme `index`. + pub fn insert_many(&mut self, index: usize, mut value: Value) { + let _ = self + .graphemes + .splice(index..index, value.graphemes.drain(..)); + } + + /// Removes the grapheme at the given `index`. + /// + /// [`Value`]: struct.Value.html + pub fn remove(&mut self, index: usize) { + let _ = self.graphemes.remove(index); + } + + /// Removes the graphemes from `start` to `end`. + pub fn remove_many(&mut self, start: usize, end: usize) { + let _ = self.graphemes.splice(start..end, std::iter::empty()); + } + + /// Returns a new [`Value`] with all its graphemes replaced with the + /// dot ('•') character. + /// + /// [`Value`]: struct.Value.html + pub fn secure(&self) -> Self { + Self { + graphemes: std::iter::repeat(String::from("•")) + .take(self.graphemes.len()) + .collect(), + } + } +} -- cgit From 6c47a40730938fb59aa7fb738b460dd37f756766 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 24 Mar 2020 20:51:22 +0100 Subject: Create `text_input::Editor` to hold editing logic --- native/src/widget/text_input/editor.rs | 80 ++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 native/src/widget/text_input/editor.rs (limited to 'native/src/widget/text_input') diff --git a/native/src/widget/text_input/editor.rs b/native/src/widget/text_input/editor.rs new file mode 100644 index 00000000..de235d52 --- /dev/null +++ b/native/src/widget/text_input/editor.rs @@ -0,0 +1,80 @@ +use crate::text_input::{Cursor, Value}; + +pub struct Editor<'a> { + value: &'a mut Value, + cursor: &'a mut Cursor, +} + +impl<'a> Editor<'a> { + pub fn new(value: &'a mut Value, cursor: &'a mut Cursor) -> Editor<'a> { + Editor { value, cursor } + } + + pub fn contents(&self) -> String { + self.value.to_string() + } + + pub fn insert(&mut self, character: char) { + match self.cursor.selection() { + Some((left, right)) => { + self.value.remove_many(left, right); + self.cursor.move_left(&self.value); + } + _ => (), + } + + self.value.insert(self.cursor.end(&self.value), character); + self.cursor.move_right(&self.value); + } + + pub fn paste(&mut self, content: Value) { + let length = content.len(); + + match self.cursor.selection() { + Some((left, right)) => { + self.value.remove_many(left, right); + self.cursor.move_left(&self.value); + } + _ => (), + } + + self.value + .insert_many(self.cursor.end(&self.value), content); + + self.cursor.move_right_by_amount(&self.value, length); + } + + pub fn backspace(&mut self) { + match self.cursor.selection() { + Some((start, end)) => { + self.value.remove_many(start, end); + self.cursor.move_left(&self.value); + } + None => { + let start = self.cursor.start(&self.value); + + if start > 0 { + self.cursor.move_left(&self.value); + + let _ = self.value.remove(start - 1); + } + } + } + } + + pub fn delete(&mut self) { + match self.cursor.selection() { + Some((start, end)) => { + self.value.remove_many(start, end); + self.cursor.move_left(&self.value); + } + None => { + let end = self.cursor.end(&self.value); + + if end < self.value.len() { + let _ = self.value.remove(end); + } + } + } + } +} -- cgit From 735d9f049c1dc3872f2699c87817a3c5e3d3c034 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 24 Mar 2020 20:57:03 +0100 Subject: Fix edge case in `Editor::backspace` --- native/src/widget/text_input/editor.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'native/src/widget/text_input') diff --git a/native/src/widget/text_input/editor.rs b/native/src/widget/text_input/editor.rs index de235d52..30025016 100644 --- a/native/src/widget/text_input/editor.rs +++ b/native/src/widget/text_input/editor.rs @@ -47,8 +47,8 @@ impl<'a> Editor<'a> { pub fn backspace(&mut self) { match self.cursor.selection() { Some((start, end)) => { - self.value.remove_many(start, end); self.cursor.move_left(&self.value); + self.value.remove_many(start, end); } None => { let start = self.cursor.start(&self.value); @@ -64,9 +64,8 @@ impl<'a> Editor<'a> { pub fn delete(&mut self) { match self.cursor.selection() { - Some((start, end)) => { - self.value.remove_many(start, end); - self.cursor.move_left(&self.value); + Some(_) => { + self.backspace(); } None => { let end = self.cursor.end(&self.value); -- cgit From 857d65c1ce276e7c03157f5d271a44cd6a047a1f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 24 Mar 2020 21:43:28 +0100 Subject: Write documentation for `text_input::Cursor` --- native/src/widget/text_input/cursor.rs | 108 +++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 44 deletions(-) (limited to 'native/src/widget/text_input') diff --git a/native/src/widget/text_input/cursor.rs b/native/src/widget/text_input/cursor.rs index d8938777..16e7a01b 100644 --- a/native/src/widget/text_input/cursor.rs +++ b/native/src/widget/text_input/cursor.rs @@ -1,15 +1,27 @@ //! Track the cursor of a text input. use crate::widget::text_input::Value; +/// The cursor of a text input. #[derive(Debug, Copy, Clone)] -pub enum State { - Index(usize), - Selection { start: usize, end: usize }, +pub struct Cursor { + state: State, } +/// The state of a [`Cursor`]. +/// +/// [`Cursor`]: struct.Cursor.html #[derive(Debug, Copy, Clone)] -pub struct Cursor { - state: State, +pub enum State { + /// Cursor without a selection + Index(usize), + + /// Cursor selecting a range of text + Selection { + /// The start of the selection + start: usize, + /// The end of the selection + end: usize, + }, } impl Default for Cursor { @@ -21,19 +33,43 @@ impl Default for Cursor { } impl Cursor { - pub fn move_to(&mut self, position: usize) { + /// Returns the [`State`] of the [`Cursor`]. + /// + /// [`State`]: struct.State.html + /// [`Cursor`]: struct.Cursor.html + pub fn state(&self, value: &Value) -> State { + match self.state { + State::Index(index) => State::Index(index.min(value.len())), + State::Selection { start, end } => { + let start = start.min(value.len()); + let end = end.min(value.len()); + + if start == end { + State::Index(start) + } else { + State::Selection { start, end } + } + } + } + } + + pub(crate) fn move_to(&mut self, position: usize) { self.state = State::Index(position); } - pub fn move_right(&mut self, value: &Value) { + pub(crate) fn move_right(&mut self, value: &Value) { self.move_right_by_amount(value, 1) } - pub fn move_right_by_words(&mut self, value: &Value) { + pub(crate) fn move_right_by_words(&mut self, value: &Value) { self.move_to(value.next_end_of_word(self.right(value))) } - pub fn move_right_by_amount(&mut self, value: &Value, amount: usize) { + pub(crate) fn move_right_by_amount( + &mut self, + value: &Value, + amount: usize, + ) { match self.state(value) { State::Index(index) => { self.move_to(index.saturating_add(amount).min(value.len())) @@ -42,7 +78,7 @@ impl Cursor { } } - pub fn move_left(&mut self, value: &Value) { + pub(crate) fn move_left(&mut self, value: &Value) { match self.state(value) { State::Index(index) if index > 0 => self.move_to(index - 1), State::Selection { start, end } => self.move_to(start.min(end)), @@ -50,11 +86,11 @@ impl Cursor { } } - pub fn move_left_by_words(&mut self, value: &Value) { + pub(crate) fn move_left_by_words(&mut self, value: &Value) { self.move_to(value.previous_start_of_word(self.left(value))); } - pub fn select_range(&mut self, start: usize, end: usize) { + pub(crate) fn select_range(&mut self, start: usize, end: usize) { if start == end { self.state = State::Index(start); } else { @@ -62,7 +98,7 @@ impl Cursor { } } - pub fn select_left(&mut self, value: &Value) { + pub(crate) fn select_left(&mut self, value: &Value) { match self.state(value) { State::Index(index) if index > 0 => { self.select_range(index, index - 1) @@ -74,7 +110,7 @@ impl Cursor { } } - pub fn select_right(&mut self, value: &Value) { + pub(crate) fn select_right(&mut self, value: &Value) { match self.state(value) { State::Index(index) if index < value.len() => { self.select_range(index, index + 1) @@ -86,7 +122,7 @@ impl Cursor { } } - pub fn select_left_by_words(&mut self, value: &Value) { + pub(crate) fn select_left_by_words(&mut self, value: &Value) { match self.state(value) { State::Index(index) => { self.select_range(index, value.previous_start_of_word(index)) @@ -97,7 +133,7 @@ impl Cursor { } } - pub fn select_right_by_words(&mut self, value: &Value) { + pub(crate) fn select_right_by_words(&mut self, value: &Value) { match self.state(value) { State::Index(index) => { self.select_range(index, value.next_end_of_word(index)) @@ -108,27 +144,11 @@ impl Cursor { } } - pub fn select_all(&mut self, value: &Value) { + pub(crate) fn select_all(&mut self, value: &Value) { self.select_range(0, value.len()); } - pub fn state(&self, value: &Value) -> State { - match self.state { - State::Index(index) => State::Index(index.min(value.len())), - State::Selection { start, end } => { - let start = start.min(value.len()); - let end = end.min(value.len()); - - if start == end { - State::Index(start) - } else { - State::Selection { start, end } - } - } - } - } - - pub fn start(&self, value: &Value) -> usize { + pub(crate) fn start(&self, value: &Value) -> usize { let start = match self.state { State::Index(index) => index, State::Selection { start, .. } => start, @@ -137,7 +157,7 @@ impl Cursor { start.min(value.len()) } - pub fn end(&self, value: &Value) -> usize { + pub(crate) fn end(&self, value: &Value) -> usize { let end = match self.state { State::Index(index) => index, State::Selection { end, .. } => end, @@ -146,6 +166,15 @@ impl Cursor { end.min(value.len()) } + pub(crate) fn selection(&self) -> Option<(usize, usize)> { + match self.state { + State::Selection { start, end } => { + Some((start.min(end), start.max(end))) + } + _ => None, + } + } + fn left(&self, value: &Value) -> usize { match self.state(value) { State::Index(index) => index, @@ -159,13 +188,4 @@ impl Cursor { State::Selection { start, end } => start.max(end), } } - - pub fn selection(&self) -> Option<(usize, usize)> { - match self.state { - State::Selection { start, end } => { - Some((start.min(end), start.max(end))) - } - _ => None, - } - } } -- cgit From cb32326fe6a57c3b4989741d88998fcafbf8e1ef Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 25 Mar 2020 14:03:15 +0100 Subject: Fix edge cases when inserting text in `Editor` --- native/src/widget/text_input/editor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'native/src/widget/text_input') diff --git a/native/src/widget/text_input/editor.rs b/native/src/widget/text_input/editor.rs index 30025016..37093a70 100644 --- a/native/src/widget/text_input/editor.rs +++ b/native/src/widget/text_input/editor.rs @@ -17,8 +17,8 @@ impl<'a> Editor<'a> { pub fn insert(&mut self, character: char) { match self.cursor.selection() { Some((left, right)) => { - self.value.remove_many(left, right); self.cursor.move_left(&self.value); + self.value.remove_many(left, right); } _ => (), } @@ -32,8 +32,8 @@ impl<'a> Editor<'a> { match self.cursor.selection() { Some((left, right)) => { - self.value.remove_many(left, right); self.cursor.move_left(&self.value); + self.value.remove_many(left, right); } _ => (), } -- cgit From bc10ca501ba012dbd379ade93e27bc012c08c2f1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 25 Mar 2020 14:07:32 +0100 Subject: Remove unnecessary borrows in `Editor` --- native/src/widget/text_input/editor.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'native/src/widget/text_input') diff --git a/native/src/widget/text_input/editor.rs b/native/src/widget/text_input/editor.rs index 37093a70..71c4f292 100644 --- a/native/src/widget/text_input/editor.rs +++ b/native/src/widget/text_input/editor.rs @@ -17,14 +17,14 @@ impl<'a> Editor<'a> { pub fn insert(&mut self, character: char) { match self.cursor.selection() { Some((left, right)) => { - self.cursor.move_left(&self.value); + self.cursor.move_left(self.value); self.value.remove_many(left, right); } _ => (), } - self.value.insert(self.cursor.end(&self.value), character); - self.cursor.move_right(&self.value); + self.value.insert(self.cursor.end(self.value), character); + self.cursor.move_right(self.value); } pub fn paste(&mut self, content: Value) { @@ -32,29 +32,28 @@ impl<'a> Editor<'a> { match self.cursor.selection() { Some((left, right)) => { - self.cursor.move_left(&self.value); + self.cursor.move_left(self.value); self.value.remove_many(left, right); } _ => (), } - self.value - .insert_many(self.cursor.end(&self.value), content); + self.value.insert_many(self.cursor.end(self.value), content); - self.cursor.move_right_by_amount(&self.value, length); + self.cursor.move_right_by_amount(self.value, length); } pub fn backspace(&mut self) { match self.cursor.selection() { Some((start, end)) => { - self.cursor.move_left(&self.value); + self.cursor.move_left(self.value); self.value.remove_many(start, end); } None => { - let start = self.cursor.start(&self.value); + let start = self.cursor.start(self.value); if start > 0 { - self.cursor.move_left(&self.value); + self.cursor.move_left(self.value); let _ = self.value.remove(start - 1); } @@ -68,7 +67,7 @@ impl<'a> Editor<'a> { self.backspace(); } None => { - let end = self.cursor.end(&self.value); + let end = self.cursor.end(self.value); if end < self.value.len() { let _ = self.value.remove(end); -- cgit