iced_widget/text_input/
value.rs

1use unicode_segmentation::UnicodeSegmentation;
2
3/// The value of a [`TextInput`].
4///
5/// [`TextInput`]: super::TextInput
6// TODO: Reduce allocations, cache results (?)
7#[derive(Debug, Clone)]
8pub struct Value {
9    graphemes: Vec<String>,
10}
11
12impl Value {
13    /// Creates a new [`Value`] from a string slice.
14    pub fn new(string: &str) -> Self {
15        let graphemes = UnicodeSegmentation::graphemes(string, true)
16            .map(String::from)
17            .collect();
18
19        Self { graphemes }
20    }
21
22    /// Returns whether the [`Value`] is empty or not.
23    ///
24    /// A [`Value`] is empty when it contains no graphemes.
25    pub fn is_empty(&self) -> bool {
26        self.len() == 0
27    }
28
29    /// Returns the total amount of graphemes in the [`Value`].
30    pub fn len(&self) -> usize {
31        self.graphemes.len()
32    }
33
34    /// Returns the position of the previous start of a word from the given
35    /// grapheme `index`.
36    pub fn previous_start_of_word(&self, index: usize) -> usize {
37        let previous_string =
38            &self.graphemes[..index.min(self.graphemes.len())].concat();
39
40        UnicodeSegmentation::split_word_bound_indices(previous_string as &str)
41            .filter(|(_, word)| !word.trim_start().is_empty())
42            .next_back()
43            .map(|(i, previous_word)| {
44                index
45                    - UnicodeSegmentation::graphemes(previous_word, true)
46                        .count()
47                    - UnicodeSegmentation::graphemes(
48                        &previous_string[i + previous_word.len()..] as &str,
49                        true,
50                    )
51                    .count()
52            })
53            .unwrap_or(0)
54    }
55
56    /// Returns the position of the next end of a word from the given grapheme
57    /// `index`.
58    pub fn next_end_of_word(&self, index: usize) -> usize {
59        let next_string = &self.graphemes[index..].concat();
60
61        UnicodeSegmentation::split_word_bound_indices(next_string as &str)
62            .find(|(_, word)| !word.trim_start().is_empty())
63            .map(|(i, next_word)| {
64                index
65                    + UnicodeSegmentation::graphemes(next_word, true).count()
66                    + UnicodeSegmentation::graphemes(
67                        &next_string[..i] as &str,
68                        true,
69                    )
70                    .count()
71            })
72            .unwrap_or(self.len())
73    }
74
75    /// Returns a new [`Value`] containing the graphemes from `start` until the
76    /// given `end`.
77    pub fn select(&self, start: usize, end: usize) -> Self {
78        let graphemes =
79            self.graphemes[start.min(self.len())..end.min(self.len())].to_vec();
80
81        Self { graphemes }
82    }
83
84    /// Returns a new [`Value`] containing the graphemes until the given
85    /// `index`.
86    pub fn until(&self, index: usize) -> Self {
87        let graphemes = self.graphemes[..index.min(self.len())].to_vec();
88
89        Self { graphemes }
90    }
91
92    /// Inserts a new `char` at the given grapheme `index`.
93    pub fn insert(&mut self, index: usize, c: char) {
94        self.graphemes.insert(index, c.to_string());
95
96        self.graphemes =
97            UnicodeSegmentation::graphemes(&self.to_string() as &str, true)
98                .map(String::from)
99                .collect();
100    }
101
102    /// Inserts a bunch of graphemes at the given grapheme `index`.
103    pub fn insert_many(&mut self, index: usize, mut value: Value) {
104        let _ = self
105            .graphemes
106            .splice(index..index, value.graphemes.drain(..));
107    }
108
109    /// Removes the grapheme at the given `index`.
110    pub fn remove(&mut self, index: usize) {
111        let _ = self.graphemes.remove(index);
112    }
113
114    /// Removes the graphemes from `start` to `end`.
115    pub fn remove_many(&mut self, start: usize, end: usize) {
116        let _ = self.graphemes.splice(start..end, std::iter::empty());
117    }
118
119    /// Returns a new [`Value`] with all its graphemes replaced with the
120    /// dot ('•') character.
121    pub fn secure(&self) -> Self {
122        Self {
123            graphemes: std::iter::repeat(String::from("•"))
124                .take(self.graphemes.len())
125                .collect(),
126        }
127    }
128}
129
130impl std::fmt::Display for Value {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        f.write_str(&self.graphemes.concat())
133    }
134}