egui/widgets/text_edit/
state.rs

1use std::sync::Arc;
2
3use crate::mutex::Mutex;
4
5use crate::{
6    text_selection::{CCursorRange, CursorRange, TextCursorState},
7    Context, Galley, Id,
8};
9
10pub type TextEditUndoer = crate::util::undoer::Undoer<(CCursorRange, String)>;
11
12/// The text edit state stored between frames.
13///
14/// Attention: You also need to `store` the updated state.
15/// ```
16/// # egui::__run_test_ui(|ui| {
17/// # let mut text = String::new();
18/// use egui::text::{CCursor, CCursorRange};
19///
20/// let mut output = egui::TextEdit::singleline(&mut text).show(ui);
21///
22/// // Create a new selection range
23/// let min = CCursor::new(0);
24/// let max = CCursor::new(0);
25/// let new_range = CCursorRange::two(min, max);
26///
27/// // Update the state
28/// output.state.cursor.set_char_range(Some(new_range));
29/// // Store the updated state
30/// output.state.store(ui.ctx(), output.response.id);
31/// # });
32/// ```
33#[derive(Clone, Default)]
34#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
35#[cfg_attr(feature = "serde", serde(default))]
36pub struct TextEditState {
37    /// Controls the text selection.
38    pub cursor: TextCursorState,
39
40    /// Wrapped in Arc for cheaper clones.
41    #[cfg_attr(feature = "serde", serde(skip))]
42    pub(crate) undoer: Arc<Mutex<TextEditUndoer>>,
43
44    // If IME candidate window is shown on this text edit.
45    #[cfg_attr(feature = "serde", serde(skip))]
46    pub(crate) ime_enabled: bool,
47
48    // cursor range for IME candidate.
49    #[cfg_attr(feature = "serde", serde(skip))]
50    pub(crate) ime_cursor_range: CursorRange,
51
52    // Visual offset when editing singleline text bigger than the width.
53    #[cfg_attr(feature = "serde", serde(skip))]
54    pub(crate) singleline_offset: f32,
55
56    /// When did the user last press a key or click on the `TextEdit`.
57    /// Used to pause the cursor animation when typing.
58    #[cfg_attr(feature = "serde", serde(skip))]
59    pub(crate) last_interaction_time: f64,
60}
61
62impl TextEditState {
63    pub fn load(ctx: &Context, id: Id) -> Option<Self> {
64        ctx.data_mut(|d| d.get_persisted(id))
65    }
66
67    pub fn store(self, ctx: &Context, id: Id) {
68        ctx.data_mut(|d| d.insert_persisted(id, self));
69    }
70
71    /// The currently selected range of characters.
72    #[deprecated = "Use `self.cursor.char_range` instead"]
73    pub fn ccursor_range(&self) -> Option<CCursorRange> {
74        self.cursor.char_range()
75    }
76
77    /// Sets the currently selected range of characters.
78    #[deprecated = "Use `self.cursor.set_char_range` instead"]
79    pub fn set_ccursor_range(&mut self, ccursor_range: Option<CCursorRange>) {
80        self.cursor.set_char_range(ccursor_range);
81    }
82
83    #[deprecated = "Use `self.cursor.set_range` instead"]
84    pub fn set_cursor_range(&mut self, cursor_range: Option<CursorRange>) {
85        self.cursor.set_range(cursor_range);
86    }
87
88    pub fn undoer(&self) -> TextEditUndoer {
89        self.undoer.lock().clone()
90    }
91
92    #[allow(clippy::needless_pass_by_ref_mut)] // Intentionally hide interiority of mutability
93    pub fn set_undoer(&mut self, undoer: TextEditUndoer) {
94        *self.undoer.lock() = undoer;
95    }
96
97    pub fn clear_undoer(&mut self) {
98        self.set_undoer(TextEditUndoer::default());
99    }
100
101    #[deprecated = "Use `self.cursor.range` instead"]
102    pub fn cursor_range(&self, galley: &Galley) -> Option<CursorRange> {
103        self.cursor.range(galley)
104    }
105}