diff options
author | 2020-03-24 20:23:31 +0100 | |
---|---|---|
committer | 2020-03-24 20:23:31 +0100 | |
commit | 6b89dd7db9715dd46738677e13ca9d9bd12f9636 (patch) | |
tree | a37fbdece73f8b4ab348cc8ac9e1eb1fc0040d6e | |
parent | 763f64b653e69b46715dcd8f7918ed769639098c (diff) | |
download | iced-6b89dd7db9715dd46738677e13ca9d9bd12f9636.tar.gz iced-6b89dd7db9715dd46738677e13ca9d9bd12f9636.tar.bz2 iced-6b89dd7db9715dd46738677e13ca9d9bd12f9636.zip |
Improve `text_input::cursor` API
-rw-r--r-- | native/src/lib.rs | 2 | ||||
-rw-r--r-- | native/src/widget/text_input.rs | 78 | ||||
-rw-r--r-- | native/src/widget/text_input/cursor.rs | 98 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/text_input.rs | 121 |
4 files changed, 164 insertions, 135 deletions
diff --git a/native/src/lib.rs b/native/src/lib.rs index d17dd918..5c6ab3ee 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -34,7 +34,7 @@ //! [`window::Renderer`]: window/trait.Renderer.html //! [`UserInterface`]: struct.UserInterface.html //! [renderer]: renderer/index.html -#![deny(missing_docs)] +//#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(unsafe_code)] diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index c379f0d1..f5ca16e1 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -4,17 +4,18 @@ //! //! [`TextInput`]: struct.TextInput.html //! [`State`]: struct.State.html -mod cursor; +pub mod cursor; + +pub use cursor::Cursor; + use crate::{ input::{ keyboard, mouse::{self, click}, ButtonState, }, - layout, - widget::text_input::cursor::Cursor, - Clipboard, Element, Event, Font, Hasher, Layout, Length, Point, Rectangle, - Size, Widget, + layout, Clipboard, Element, Event, Font, Hasher, Layout, Length, Point, + Rectangle, Size, Widget, }; use std::u32; @@ -261,7 +262,8 @@ where if self.is_secure { self.state.cursor.select_all(&self.value); } else { - let end = self.state.cursor.end(); + let end = self.state.cursor.end(&self.value); + self.state.cursor.select_range( self.value.previous_start_of_word(end), self.value.next_end_of_word(end), @@ -307,7 +309,7 @@ where self.font, ); - let pos = find_cursor_position( + let position = find_cursor_position( renderer, target + offset, &value, @@ -317,9 +319,10 @@ where self.font, ); - self.state - .cursor - .select_range(self.state.cursor.start(), pos); + self.state.cursor.select_range( + self.state.cursor.start(&value), + position, + ); } } } @@ -328,15 +331,15 @@ where && self.state.is_pasting.is_none() && !c.is_control() => { - match self.state.cursor.selection_position() { + match self.state.cursor.selection() { Some((left, right)) => { self.value.remove_many(left, right); - self.state.cursor.move_left(); + self.state.cursor.move_left(&self.value); } _ => (), } - self.value - .insert(self.state.cursor.end().min(self.value.len()), c); + + self.value.insert(self.state.cursor.end(&self.value), c); self.state.cursor.move_right(&self.value); let message = (self.on_change)(self.value.to_string()); @@ -353,19 +356,18 @@ where } } keyboard::KeyCode::Backspace => { - match self.state.cursor.selection_position() { + match self.state.cursor.selection() { Some((start, end)) => { self.value.remove_many(start, end); - self.state.cursor.move_left(); + self.state.cursor.move_left(&self.value); } None => { - if self.state.cursor.start().min(self.value.len()) - > 0 - { - self.state.cursor.move_left(); - let _ = self - .value - .remove(self.state.cursor.start()); + if self.state.cursor.start(&self.value) > 0 { + self.state.cursor.move_left(&self.value); + + let _ = self.value.remove( + self.state.cursor.start(&self.value), + ); } } } @@ -373,15 +375,16 @@ where messages.push(message); } keyboard::KeyCode::Delete => { - match self.state.cursor.selection_position() { + match self.state.cursor.selection() { Some((start, end)) => { self.value.remove_many(start, end); - self.state.cursor.move_left(); + self.state.cursor.move_left(&self.value); } None => { - if self.state.cursor.end() < self.value.len() { - let _ = - self.value.remove(self.state.cursor.end()); + let end = self.state.cursor.end(&self.value); + + if end > 0 { + let _ = self.value.remove(end); } } } @@ -398,9 +401,9 @@ where self.state.cursor.move_left_by_words(&self.value); } } else if modifiers.shift { - self.state.cursor.select_left() + self.state.cursor.select_left(&self.value) } else { - self.state.cursor.move_left(); + self.state.cursor.move_left(&self.value); } } keyboard::KeyCode::Right => { @@ -422,9 +425,10 @@ where } keyboard::KeyCode::Home => { if modifiers.shift { - self.state - .cursor - .select_range(self.state.cursor.start(), 0); + self.state.cursor.select_range( + self.state.cursor.start(&self.value), + 0, + ); } else { self.state.cursor.move_to(0); } @@ -432,7 +436,7 @@ where keyboard::KeyCode::End => { if modifiers.shift { self.state.cursor.select_range( - self.state.cursor.start(), + self.state.cursor.start(&self.value), self.value.len(), ); } else { @@ -456,16 +460,16 @@ where } }; - match self.state.cursor.selection_position() { + match self.state.cursor.selection() { Some((left, right)) => { self.value.remove_many(left, right); - self.state.cursor.move_left(); + self.state.cursor.move_left(&self.value); } _ => (), } self.value.insert_many( - self.state.cursor.end().min(self.value.len()), + self.state.cursor.end(&self.value), content.clone(), ); 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, - } - } */ } diff --git a/wgpu/src/renderer/widget/text_input.rs b/wgpu/src/renderer/widget/text_input.rs index fa108d68..74f30be1 100644 --- a/wgpu/src/renderer/widget/text_input.rs +++ b/wgpu/src/renderer/widget/text_input.rs @@ -1,8 +1,9 @@ use crate::{text_input::StyleSheet, Primitive, Renderer}; use iced_native::{ - text_input, Background, Color, Font, HorizontalAlignment, MouseCursor, - Point, Rectangle, Size, Vector, VerticalAlignment, + text_input::{self, cursor}, + Background, Color, Font, HorizontalAlignment, MouseCursor, Point, + Rectangle, Size, Vector, VerticalAlignment, }; use std::f32; @@ -41,12 +42,19 @@ impl text_input::Renderer for Renderer { font: Font, ) -> f32 { if state.is_focused() { + let cursor = state.cursor(); + + let focus_position = match cursor.state(value) { + cursor::State::Index(i) => i, + cursor::State::Selection { end, .. } => end, + }; + let (_, offset) = measure_cursor_and_scroll_offset( self, text_bounds, value, size, - state.cursor().cursor_position(value), + focus_position, font, ); @@ -111,70 +119,91 @@ impl text_input::Renderer for Renderer { }; let (contents_primitive, offset) = if state.is_focused() { - let (text_value_width, offset) = measure_cursor_and_scroll_offset( - self, - text_bounds, - value, - size, - state.cursor().cursor_position(value), - font, - ); + let cursor = state.cursor(); + + let (cursor_primitive, offset) = match cursor.state(value) { + cursor::State::Index(position) => { + let (text_value_width, offset) = + measure_cursor_and_scroll_offset( + self, + text_bounds, + value, + size, + position, + font, + ); + + ( + Primitive::Quad { + bounds: Rectangle { + x: text_bounds.x + text_value_width, + y: text_bounds.y, + width: 1.0, + height: text_bounds.height, + }, + background: Background::Color( + style_sheet.value_color(), + ), + border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, + }, + offset, + ) + } + cursor::State::Selection { start, end } => { + let left = start.min(end); + let right = end.max(start); - let selection = match state.cursor().selection_position() { - None => Primitive::None, - Some(_) => { - let (cursor_left_offset, _) = + let (left_position, left_offset) = measure_cursor_and_scroll_offset( self, text_bounds, value, size, - state.cursor().left(), + left, font, ); - let (cursor_right_offset, _) = + + let (right_position, right_offset) = measure_cursor_and_scroll_offset( self, text_bounds, value, size, - state.cursor().right(), + right, font, ); - let width = cursor_right_offset - cursor_left_offset; - Primitive::Quad { - bounds: Rectangle { - x: text_bounds.x + cursor_left_offset, - y: text_bounds.y, - width, - height: text_bounds.height, + + let width = right_position - left_position; + + ( + Primitive::Quad { + bounds: Rectangle { + x: text_bounds.x + left_position, + y: text_bounds.y, + width, + height: text_bounds.height, + }, + background: Background::Color( + style_sheet.selection_color(), + ), + border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, + }, + if end == right { + right_offset + } else { + left_offset }, - background: Background::Color( - style_sheet.selection_color(), - ), - border_radius: 0, - border_width: 0, - border_color: Color::TRANSPARENT, - } + ) } }; - let cursor = Primitive::Quad { - bounds: Rectangle { - x: text_bounds.x + text_value_width, - y: text_bounds.y, - width: 1.0, - height: text_bounds.height, - }, - background: Background::Color(style_sheet.value_color()), - border_radius: 0, - border_width: 0, - border_color: Color::TRANSPARENT, - }; - ( Primitive::Group { - primitives: vec![selection, text_value, cursor], + primitives: vec![cursor_primitive, text_value], }, Vector::new(offset as u32, 0), ) |