Move Value
to its own module
This commit is contained in:
parent
6b89dd7db9
commit
28382a47d3
@ -4,9 +4,12 @@
|
|||||||
//!
|
//!
|
||||||
//! [`TextInput`]: struct.TextInput.html
|
//! [`TextInput`]: struct.TextInput.html
|
||||||
//! [`State`]: struct.State.html
|
//! [`State`]: struct.State.html
|
||||||
|
mod value;
|
||||||
|
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
|
|
||||||
pub use cursor::Cursor;
|
pub use cursor::Cursor;
|
||||||
|
pub use value::Value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
input::{
|
input::{
|
||||||
@ -19,7 +22,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use std::u32;
|
use std::u32;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
|
||||||
|
|
||||||
/// A field that can be filled with text.
|
/// A field that can be filled with text.
|
||||||
///
|
///
|
||||||
@ -362,12 +364,12 @@ where
|
|||||||
self.state.cursor.move_left(&self.value);
|
self.state.cursor.move_left(&self.value);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if self.state.cursor.start(&self.value) > 0 {
|
let start = self.state.cursor.start(&self.value);
|
||||||
|
|
||||||
|
if start > 0 {
|
||||||
self.state.cursor.move_left(&self.value);
|
self.state.cursor.move_left(&self.value);
|
||||||
|
|
||||||
let _ = self.value.remove(
|
let _ = self.value.remove(start - 1);
|
||||||
self.state.cursor.start(&self.value),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -383,7 +385,7 @@ where
|
|||||||
None => {
|
None => {
|
||||||
let end = self.state.cursor.end(&self.value);
|
let end = self.state.cursor.end(&self.value);
|
||||||
|
|
||||||
if end > 0 {
|
if end < self.value.len() {
|
||||||
let _ = self.value.remove(end);
|
let _ = self.value.remove(end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -691,139 +693,6 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The value of a [`TextInput`].
|
|
||||||
///
|
|
||||||
/// [`TextInput`]: struct.TextInput.html
|
|
||||||
// TODO: Reduce allocations, cache results (?)
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Value {
|
|
||||||
graphemes: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Reduce allocations
|
// TODO: Reduce allocations
|
||||||
fn find_cursor_position<Renderer: self::Renderer>(
|
fn find_cursor_position<Renderer: self::Renderer>(
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
|
134
native/src/widget/text_input/value.rs
Normal file
134
native/src/widget/text_input/value.rs
Normal file
@ -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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user