GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
src/widget/editor.rs (7.2K)
1 #[derive(Debug, Clone, Default)]
2 pub struct TextEditorState {
3 pub buffer: String,
4 pub cursor_idx: usize,
5 pub select_anchor: Option<usize>,
6 pub all_selected: bool,
7 }
8
9 impl TextEditorState {
10 pub fn new(text: String) -> Self {
11 let len = text.chars().count();
12 Self {
13 buffer: text,
14 cursor_idx: len,
15 select_anchor: None,
16 all_selected: false,
17 }
18 }
19
20 pub fn select_all(&mut self) {
21 let len = self.buffer.chars().count();
22 self.select_anchor = Some(0);
23 self.cursor_idx = len;
24 self.all_selected = len > 0;
25 }
26
27 pub fn clear_selection(&mut self) {
28 self.select_anchor = None;
29 self.all_selected = false;
30 }
31
32 pub fn clamp_bounds(&mut self) {
33 let len = self.buffer.chars().count();
34 self.cursor_idx = self.cursor_idx.min(len);
35 if let Some(anchor) = self.select_anchor {
36 self.select_anchor = Some(anchor.min(len));
37 }
38 }
39
40 pub fn selected_range(&self) -> Option<(usize, usize)> {
41 let len = self.buffer.chars().count();
42 let anchor = self.select_anchor?.min(len);
43 let cursor = self.cursor_idx.min(len);
44 if anchor == cursor {
45 None
46 } else {
47 Some((anchor.min(cursor), anchor.max(cursor)))
48 }
49 }
50
51 pub fn selected_text(&self) -> Option<String> {
52 let (start, end) = self.selected_range()?;
53 let chars: Vec<char> = self.buffer.chars().collect();
54 Some(chars[start..end].iter().collect())
55 }
56
57 pub fn insert_text(&mut self, text: &str) {
58 self.clamp_bounds();
59 if self.all_selected {
60 self.buffer.clear();
61 self.cursor_idx = 0;
62 self.clear_selection();
63 }
64
65 let start = self.select_anchor.unwrap_or(self.cursor_idx).min(self.cursor_idx);
66 let end = self.select_anchor.unwrap_or(self.cursor_idx).max(self.cursor_idx);
67 let chars: Vec<char> = self.buffer.chars().collect();
68 let mut new_buf = String::new();
69 for i in 0..start {
70 new_buf.push(chars[i]);
71 }
72 let inserted_count = text.chars().count();
73 new_buf.push_str(text);
74 for i in end..chars.len() {
75 new_buf.push(chars[i]);
76 }
77 self.buffer = new_buf;
78 self.cursor_idx = start + inserted_count;
79 self.clear_selection();
80 }
81
82 pub fn delete_backwards(&mut self) -> bool {
83 self.clamp_bounds();
84 if self.all_selected {
85 self.buffer.clear();
86 self.cursor_idx = 0;
87 self.clear_selection();
88 return true;
89 }
90 let start = self.select_anchor.unwrap_or(self.cursor_idx).min(self.cursor_idx);
91 let end = self.select_anchor.unwrap_or(self.cursor_idx).max(self.cursor_idx);
92 if start != end {
93 let chars: Vec<char> = self.buffer.chars().collect();
94 let mut new_buf = String::new();
95 for i in 0..start {
96 new_buf.push(chars[i]);
97 }
98 for i in end..chars.len() {
99 new_buf.push(chars[i]);
100 }
101 self.buffer = new_buf;
102 self.cursor_idx = start;
103 self.clear_selection();
104 return true;
105 }
106 if self.cursor_idx > 0 {
107 let mut chars: Vec<char> = self.buffer.chars().collect();
108 chars.remove(self.cursor_idx - 1);
109 self.buffer = chars.into_iter().collect();
110 self.cursor_idx -= 1;
111 self.clear_selection();
112 return true;
113 }
114 false
115 }
116
117 pub fn delete_forwards(&mut self) -> bool {
118 self.clamp_bounds();
119 if self.all_selected {
120 self.buffer.clear();
121 self.cursor_idx = 0;
122 self.clear_selection();
123 return true;
124 }
125 let start = self.select_anchor.unwrap_or(self.cursor_idx).min(self.cursor_idx);
126 let end = self.select_anchor.unwrap_or(self.cursor_idx).max(self.cursor_idx);
127 if start != end {
128 let chars: Vec<char> = self.buffer.chars().collect();
129 let mut new_buf = String::new();
130 for i in 0..start {
131 new_buf.push(chars[i]);
132 }
133 for i in end..chars.len() {
134 new_buf.push(chars[i]);
135 }
136 self.buffer = new_buf;
137 self.cursor_idx = start;
138 self.clear_selection();
139 return true;
140 }
141 let char_count = self.buffer.chars().count();
142 if self.cursor_idx < char_count {
143 let mut chars: Vec<char> = self.buffer.chars().collect();
144 chars.remove(self.cursor_idx);
145 self.buffer = chars.into_iter().collect();
146 self.clear_selection();
147 return true;
148 }
149 false
150 }
151
152 pub fn move_cursor_left(&mut self, select: bool) -> bool {
153 self.clamp_bounds();
154 if select {
155 if self.select_anchor.is_none() {
156 self.select_anchor = Some(self.cursor_idx);
157 }
158 if self.cursor_idx > 0 {
159 self.cursor_idx -= 1;
160 true
161 } else {
162 false
163 }
164 } else {
165 if let Some(anchor) = self.select_anchor {
166 self.cursor_idx = anchor.min(self.cursor_idx);
167 self.clear_selection();
168 true
169 } else if self.cursor_idx > 0 {
170 self.cursor_idx -= 1;
171 true
172 } else {
173 false
174 }
175 }
176 }
177
178 pub fn move_cursor_right(&mut self, select: bool) -> bool {
179 self.clamp_bounds();
180 let char_count = self.buffer.chars().count();
181 if select {
182 if self.select_anchor.is_none() {
183 self.select_anchor = Some(self.cursor_idx);
184 }
185 if self.cursor_idx < char_count {
186 self.cursor_idx += 1;
187 true
188 } else {
189 false
190 }
191 } else {
192 if let Some(anchor) = self.select_anchor {
193 self.cursor_idx = anchor.max(self.cursor_idx);
194 self.clear_selection();
195 true
196 } else if self.cursor_idx < char_count {
197 self.cursor_idx += 1;
198 true
199 } else {
200 false
201 }
202 }
203 }
204
205 pub fn move_cursor_to_start(&mut self, select: bool) -> bool {
206 self.clamp_bounds();
207 if select {
208 if self.select_anchor.is_none() {
209 self.select_anchor = Some(self.cursor_idx);
210 }
211 } else {
212 self.clear_selection();
213 }
214 let changed = self.cursor_idx != 0;
215 self.cursor_idx = 0;
216 changed
217 }
218
219 pub fn move_cursor_to_end(&mut self, select: bool) -> bool {
220 self.clamp_bounds();
221 let char_count = self.buffer.chars().count();
222 if select {
223 if self.select_anchor.is_none() {
224 self.select_anchor = Some(self.cursor_idx);
225 }
226 } else {
227 self.clear_selection();
228 }
229 let changed = self.cursor_idx != char_count;
230 self.cursor_idx = char_count;
231 changed
232 }
233 }