egui/widgets/text_edit/
text_buffer.rs

1use std::{borrow::Cow, ops::Range};
2
3use epaint::{
4    Galley,
5    text::{TAB_SIZE, cursor::CCursor},
6};
7
8use crate::{
9    text::CCursorRange,
10    text_selection::text_cursor_state::{
11        byte_index_from_char_index, ccursor_next_word, ccursor_previous_word,
12        char_index_from_byte_index, find_line_start, slice_char_range,
13    },
14};
15
16/// Trait constraining what types [`crate::TextEdit`] may use as
17/// an underlying buffer.
18///
19/// Most likely you will use a [`String`] which implements [`TextBuffer`].
20pub trait TextBuffer {
21    /// Can this text be edited?
22    fn is_mutable(&self) -> bool;
23
24    /// Returns this buffer as a `str`.
25    fn as_str(&self) -> &str;
26
27    /// Inserts text `text` into this buffer at character index `char_index`.
28    ///
29    /// # Notes
30    /// `char_index` is a *character index*, not a byte index.
31    ///
32    /// # Return
33    /// Returns how many *characters* were successfully inserted
34    fn insert_text(&mut self, text: &str, char_index: usize) -> usize;
35
36    /// Deletes a range of text `char_range` from this buffer.
37    ///
38    /// # Notes
39    /// `char_range` is a *character range*, not a byte range.
40    fn delete_char_range(&mut self, char_range: Range<usize>);
41
42    /// Reads the given character range.
43    fn char_range(&self, char_range: Range<usize>) -> &str {
44        slice_char_range(self.as_str(), char_range)
45    }
46
47    fn byte_index_from_char_index(&self, char_index: usize) -> usize {
48        byte_index_from_char_index(self.as_str(), char_index)
49    }
50
51    fn char_index_from_byte_index(&self, char_index: usize) -> usize {
52        char_index_from_byte_index(self.as_str(), char_index)
53    }
54
55    /// Clears all characters in this buffer
56    fn clear(&mut self) {
57        self.delete_char_range(0..self.as_str().len());
58    }
59
60    /// Replaces all contents of this string with `text`
61    fn replace_with(&mut self, text: &str) {
62        self.clear();
63        self.insert_text(text, 0);
64    }
65
66    /// Clears all characters in this buffer and returns a string of the contents.
67    fn take(&mut self) -> String {
68        let s = self.as_str().to_owned();
69        self.clear();
70        s
71    }
72
73    fn insert_text_at(&mut self, ccursor: &mut CCursor, text_to_insert: &str, char_limit: usize) {
74        if char_limit < usize::MAX {
75            let mut new_string = text_to_insert;
76            // Avoid subtract with overflow panic
77            let cutoff = char_limit.saturating_sub(self.as_str().chars().count());
78
79            new_string = match new_string.char_indices().nth(cutoff) {
80                None => new_string,
81                Some((idx, _)) => &new_string[..idx],
82            };
83
84            ccursor.index += self.insert_text(new_string, ccursor.index);
85        } else {
86            ccursor.index += self.insert_text(text_to_insert, ccursor.index);
87        }
88    }
89
90    fn decrease_indentation(&mut self, ccursor: &mut CCursor) {
91        let line_start = find_line_start(self.as_str(), *ccursor);
92
93        let remove_len = if self.as_str().chars().nth(line_start.index) == Some('\t') {
94            Some(1)
95        } else if self
96            .as_str()
97            .chars()
98            .skip(line_start.index)
99            .take(TAB_SIZE)
100            .all(|c| c == ' ')
101        {
102            Some(TAB_SIZE)
103        } else {
104            None
105        };
106
107        if let Some(len) = remove_len {
108            self.delete_char_range(line_start.index..(line_start.index + len));
109            if *ccursor != line_start {
110                *ccursor -= len;
111            }
112        }
113    }
114
115    fn delete_selected(&mut self, cursor_range: &CCursorRange) -> CCursor {
116        let [min, max] = cursor_range.sorted_cursors();
117        self.delete_selected_ccursor_range([min, max])
118    }
119
120    fn delete_selected_ccursor_range(&mut self, [min, max]: [CCursor; 2]) -> CCursor {
121        self.delete_char_range(min.index..max.index);
122        CCursor {
123            index: min.index,
124            prefer_next_row: true,
125        }
126    }
127
128    fn delete_previous_char(&mut self, ccursor: CCursor) -> CCursor {
129        if ccursor.index > 0 {
130            let max_ccursor = ccursor;
131            let min_ccursor = max_ccursor - 1;
132            self.delete_selected_ccursor_range([min_ccursor, max_ccursor])
133        } else {
134            ccursor
135        }
136    }
137
138    fn delete_next_char(&mut self, ccursor: CCursor) -> CCursor {
139        self.delete_selected_ccursor_range([ccursor, ccursor + 1])
140    }
141
142    fn delete_previous_word(&mut self, max_ccursor: CCursor) -> CCursor {
143        let min_ccursor = ccursor_previous_word(self.as_str(), max_ccursor);
144        self.delete_selected_ccursor_range([min_ccursor, max_ccursor])
145    }
146
147    fn delete_next_word(&mut self, min_ccursor: CCursor) -> CCursor {
148        let max_ccursor = ccursor_next_word(self.as_str(), min_ccursor);
149        self.delete_selected_ccursor_range([min_ccursor, max_ccursor])
150    }
151
152    fn delete_paragraph_before_cursor(
153        &mut self,
154        galley: &Galley,
155        cursor_range: &CCursorRange,
156    ) -> CCursor {
157        let [min, max] = cursor_range.sorted_cursors();
158        let min = galley.cursor_begin_of_paragraph(&min);
159        if min == max {
160            self.delete_previous_char(min)
161        } else {
162            self.delete_selected(&CCursorRange::two(min, max))
163        }
164    }
165
166    fn delete_paragraph_after_cursor(
167        &mut self,
168        galley: &Galley,
169        cursor_range: &CCursorRange,
170    ) -> CCursor {
171        let [min, max] = cursor_range.sorted_cursors();
172        let max = galley.cursor_end_of_paragraph(&max);
173        if min == max {
174            self.delete_next_char(min)
175        } else {
176            self.delete_selected(&CCursorRange::two(min, max))
177        }
178    }
179
180    /// Returns a unique identifier for the implementing type.
181    ///
182    /// This is useful for downcasting from this trait to the implementing type.
183    /// Here is an example usage:
184    /// ```
185    /// use egui::TextBuffer;
186    /// use std::any::TypeId;
187    ///
188    /// struct ExampleBuffer {}
189    ///
190    /// impl TextBuffer for ExampleBuffer {
191    ///     fn is_mutable(&self) -> bool { unimplemented!() }
192    ///     fn as_str(&self) -> &str { unimplemented!() }
193    ///     fn insert_text(&mut self, text: &str, char_index: usize) -> usize { unimplemented!() }
194    ///     fn delete_char_range(&mut self, char_range: std::ops::Range<usize>) { unimplemented!() }
195    ///
196    ///     // Implement it like the following:
197    ///     fn type_id(&self) -> TypeId {
198    ///         TypeId::of::<Self>()
199    ///     }
200    /// }
201    ///
202    /// // Example downcast:
203    /// pub fn downcast_example(buffer: &dyn TextBuffer) -> Option<&ExampleBuffer> {
204    ///     if buffer.type_id() == TypeId::of::<ExampleBuffer>() {
205    ///         unsafe { Some(&*(buffer as *const dyn TextBuffer as *const ExampleBuffer)) }
206    ///     } else {
207    ///         None
208    ///     }
209    /// }
210    /// ```
211    fn type_id(&self) -> std::any::TypeId;
212}
213
214impl TextBuffer for String {
215    fn is_mutable(&self) -> bool {
216        true
217    }
218
219    fn as_str(&self) -> &str {
220        self.as_ref()
221    }
222
223    fn insert_text(&mut self, text: &str, char_index: usize) -> usize {
224        // Get the byte index from the character index
225        let byte_idx = byte_index_from_char_index(self.as_str(), char_index);
226
227        // Then insert the string
228        self.insert_str(byte_idx, text);
229
230        text.chars().count()
231    }
232
233    fn delete_char_range(&mut self, char_range: Range<usize>) {
234        assert!(
235            char_range.start <= char_range.end,
236            "start must be <= end, but got {char_range:?}"
237        );
238
239        // Get both byte indices
240        let byte_start = byte_index_from_char_index(self.as_str(), char_range.start);
241        let byte_end = byte_index_from_char_index(self.as_str(), char_range.end);
242
243        // Then drain all characters within this range
244        self.drain(byte_start..byte_end);
245    }
246
247    fn clear(&mut self) {
248        self.clear();
249    }
250
251    fn replace_with(&mut self, text: &str) {
252        text.clone_into(self);
253    }
254
255    fn take(&mut self) -> String {
256        std::mem::take(self)
257    }
258
259    fn type_id(&self) -> std::any::TypeId {
260        std::any::TypeId::of::<Self>()
261    }
262}
263
264impl TextBuffer for Cow<'_, str> {
265    fn is_mutable(&self) -> bool {
266        true
267    }
268
269    fn as_str(&self) -> &str {
270        self.as_ref()
271    }
272
273    fn insert_text(&mut self, text: &str, char_index: usize) -> usize {
274        <String as TextBuffer>::insert_text(self.to_mut(), text, char_index)
275    }
276
277    fn delete_char_range(&mut self, char_range: Range<usize>) {
278        <String as TextBuffer>::delete_char_range(self.to_mut(), char_range);
279    }
280
281    fn clear(&mut self) {
282        <String as TextBuffer>::clear(self.to_mut());
283    }
284
285    fn replace_with(&mut self, text: &str) {
286        *self = Cow::Owned(text.to_owned());
287    }
288
289    fn take(&mut self) -> String {
290        std::mem::take(self).into_owned()
291    }
292
293    fn type_id(&self) -> std::any::TypeId {
294        std::any::TypeId::of::<Cow<'_, str>>()
295    }
296}
297
298/// Immutable view of a `&str`!
299impl TextBuffer for &str {
300    fn is_mutable(&self) -> bool {
301        false
302    }
303
304    fn as_str(&self) -> &str {
305        self
306    }
307
308    fn insert_text(&mut self, _text: &str, _ch_idx: usize) -> usize {
309        0
310    }
311
312    fn delete_char_range(&mut self, _ch_range: Range<usize>) {}
313
314    fn type_id(&self) -> std::any::TypeId {
315        std::any::TypeId::of::<&str>()
316    }
317}