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 = &self.graphemes[..index.min(self.graphemes.len())].concat();
38
39        UnicodeSegmentation::split_word_bound_indices(previous_string as &str)
40            .rfind(|(_, word)| !word.trim_start().is_empty())
41            .map(|(i, previous_word)| {
42                index
43                    - UnicodeSegmentation::graphemes(previous_word, true).count()
44                    - UnicodeSegmentation::graphemes(
45                        &previous_string[i + previous_word.len()..] as &str,
46                        true,
47                    )
48                    .count()
49            })
50            .unwrap_or(0)
51    }
52
53    /// Returns the position of the next end of a word from the given grapheme
54    /// `index`.
55    pub fn next_end_of_word(&self, index: usize) -> usize {
56        let next_string = &self.graphemes[index..].concat();
57
58        UnicodeSegmentation::split_word_bound_indices(next_string as &str)
59            .find(|(_, word)| !word.trim_start().is_empty())
60            .map(|(i, next_word)| {
61                index
62                    + UnicodeSegmentation::graphemes(next_word, true).count()
63                    + UnicodeSegmentation::graphemes(&next_string[..i] as &str, true).count()
64            })
65            .unwrap_or(self.len())
66    }
67
68    /// Returns a new [`Value`] containing the graphemes from `start` until the
69    /// given `end`.
70    pub fn select(&self, start: usize, end: usize) -> Self {
71        let graphemes = self.graphemes[start.min(self.len())..end.min(self.len())].to_vec();
72
73        Self { graphemes }
74    }
75
76    /// Returns a new [`Value`] containing the graphemes until the given
77    /// `index`.
78    pub fn until(&self, index: usize) -> Self {
79        let graphemes = self.graphemes[..index.min(self.len())].to_vec();
80
81        Self { graphemes }
82    }
83
84    /// Inserts a new `char` at the given grapheme `index`.
85    pub fn insert(&mut self, index: usize, c: char) {
86        self.graphemes.insert(index, c.to_string());
87
88        self.graphemes = UnicodeSegmentation::graphemes(&self.to_string() as &str, true)
89            .map(String::from)
90            .collect();
91    }
92
93    /// Inserts a bunch of graphemes at the given grapheme `index`.
94    pub fn insert_many(&mut self, index: usize, mut value: Value) {
95        let _ = self
96            .graphemes
97            .splice(index..index, value.graphemes.drain(..));
98    }
99
100    /// Removes the grapheme at the given `index`.
101    pub fn remove(&mut self, index: usize) {
102        let _ = self.graphemes.remove(index);
103    }
104
105    /// Removes the graphemes from `start` to `end`.
106    pub fn remove_many(&mut self, start: usize, end: usize) {
107        let _ = self.graphemes.splice(start..end, std::iter::empty());
108    }
109
110    /// Returns a new [`Value`] with all its graphemes replaced with the
111    /// dot ('•') character.
112    pub fn secure(&self) -> Self {
113        Self {
114            graphemes: std::iter::repeat_n(String::from("•"), self.graphemes.len()).collect(),
115        }
116    }
117}
118
119impl std::fmt::Display for Value {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        f.write_str(&self.graphemes.concat())
122    }
123}