cosmic/widget/text_input/
value.rs1use unicode_segmentation::UnicodeSegmentation;
6
7#[derive(Default, Debug, Clone, PartialEq)]
12pub struct Value {
13 graphemes: Vec<String>,
14}
15
16impl Value {
17 pub fn new(string: &str) -> Self {
19 let graphemes = UnicodeSegmentation::graphemes(string, true)
20 .map(String::from)
21 .collect();
22
23 Self { graphemes }
24 }
25
26 #[must_use]
30 #[inline]
31 pub fn is_empty(&self) -> bool {
32 self.len() == 0
33 }
34
35 #[must_use]
37 #[inline]
38 pub fn len(&self) -> usize {
39 self.graphemes.len()
40 }
41
42 #[must_use]
45 pub fn previous_start_of_word(&self, index: usize) -> usize {
46 let previous_string = &self.graphemes[..index.min(self.graphemes.len())].concat();
47
48 UnicodeSegmentation::split_word_bound_indices(previous_string as &str)
49 .filter(|(_, word)| !word.trim_start().is_empty())
50 .next_back()
51 .map_or(0, |(i, previous_word)| {
52 index
53 - UnicodeSegmentation::graphemes(previous_word, true).count()
54 - UnicodeSegmentation::graphemes(
55 &previous_string[i + previous_word.len()..] as &str,
56 true,
57 )
58 .count()
59 })
60 }
61
62 #[must_use]
65 pub fn next_end_of_word(&self, index: usize) -> usize {
66 let next_string = &self.graphemes[index..].concat();
67
68 UnicodeSegmentation::split_word_bound_indices(next_string as &str)
69 .find(|(_, word)| !word.trim_start().is_empty())
70 .map_or(self.len(), |(i, next_word)| {
71 index
72 + UnicodeSegmentation::graphemes(next_word, true).count()
73 + UnicodeSegmentation::graphemes(&next_string[..i] as &str, true).count()
74 })
75 }
76
77 #[must_use]
80 #[inline]
81 pub fn select(&self, start: usize, end: usize) -> Self {
82 let graphemes = self.graphemes[start.min(self.len())..end.min(self.len())].to_vec();
83
84 Self { graphemes }
85 }
86
87 #[must_use]
90 #[inline]
91 pub fn until(&self, index: usize) -> Self {
92 let graphemes = self.graphemes[..index.min(self.len())].to_vec();
93
94 Self { graphemes }
95 }
96
97 #[inline]
99 pub fn insert(&mut self, index: usize, c: char) {
100 self.graphemes.insert(index, c.to_string());
101
102 self.graphemes = UnicodeSegmentation::graphemes(&self.to_string() as &str, true)
103 .map(String::from)
104 .collect();
105 }
106
107 #[inline]
109 pub fn insert_many(&mut self, index: usize, mut value: Value) {
110 let _ = self
111 .graphemes
112 .splice(index..index, value.graphemes.drain(..));
113 }
114
115 #[inline]
117 pub fn remove(&mut self, index: usize) {
118 let _ = self.graphemes.remove(index);
119 }
120
121 #[inline]
123 pub fn remove_many(&mut self, start: usize, end: usize) {
124 let _ = self.graphemes.splice(start..end, std::iter::empty());
125 }
126
127 #[must_use]
130 pub fn secure(&self) -> Self {
131 Self {
132 graphemes: std::iter::repeat_n(String::from("•"), self.graphemes.len()).collect(),
133 }
134 }
135
136 #[must_use]
138 pub fn byte_index_at_grapheme(&self, grapheme_index: usize) -> usize {
139 self.graphemes[..grapheme_index.min(self.graphemes.len())]
140 .iter()
141 .map(|g| g.len())
142 .sum()
143 }
144
145 #[must_use]
148 pub fn rfind_char(&self, ch: char) -> Option<usize> {
149 let needle = ch.to_string();
150 self.graphemes.iter().rposition(|g| g == &needle)
151 }
152
153 #[must_use]
155 pub fn grapheme_index_at_byte(&self, byte_index: usize) -> usize {
156 let mut bytes = 0;
157 for (i, g) in self.graphemes.iter().enumerate() {
158 if bytes >= byte_index {
159 return i;
160 }
161 bytes += g.len();
162 }
163
164 self.graphemes.len()
165 }
166}
167
168impl std::fmt::Display for Value {
169 #[inline]
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 f.write_str(&self.graphemes.concat())
172 }
173}