1use azul_core::{
26 dom::DomNodeId,
27 selection::{OptionSelectionRange, SelectionRange},
28 task::Instant,
29 window::CursorPosition,
30};
31use azul_css::AzString;
32
33use crate::managers::selection::ClipboardContent;
34
35pub type ChangesetId = usize;
37
38#[derive(Debug, Clone)]
40#[repr(C)]
41pub struct TextChangeset {
42 pub id: ChangesetId,
44 pub target: DomNodeId,
46 pub operation: TextOperation,
48 pub timestamp: Instant,
50}
51
52#[derive(Debug, Clone)]
54#[repr(C)]
55pub struct TextOpInsertText {
56 pub text: AzString,
57 pub position: CursorPosition,
58 pub new_cursor: CursorPosition,
59}
60
61#[derive(Debug, Clone)]
63#[repr(C)]
64pub struct TextOpDeleteText {
65 pub range: SelectionRange,
66 pub deleted_text: AzString,
67 pub new_cursor: CursorPosition,
68}
69
70#[derive(Debug, Clone)]
72#[repr(C)]
73pub struct TextOpReplaceText {
74 pub range: SelectionRange,
75 pub old_text: AzString,
76 pub new_text: AzString,
77 pub new_cursor: CursorPosition,
78}
79
80#[derive(Copy, Debug, Clone)]
82#[repr(C)]
83pub struct TextOpSetSelection {
84 pub old_range: OptionSelectionRange,
85 pub new_range: SelectionRange,
86}
87
88#[derive(Copy, Debug, Clone)]
90#[repr(C)]
91pub struct TextOpExtendSelection {
92 pub old_range: SelectionRange,
93 pub new_range: SelectionRange,
94 pub direction: SelectionDirection,
95}
96
97#[derive(Copy, Debug, Clone)]
99#[repr(C)]
100pub struct TextOpClearSelection {
101 pub old_range: SelectionRange,
102}
103
104#[derive(Copy, Debug, Clone)]
106#[repr(C)]
107pub struct TextOpMoveCursor {
108 pub old_position: CursorPosition,
109 pub new_position: CursorPosition,
110 pub movement: CursorMovement,
111}
112
113#[derive(Debug, Clone)]
115#[repr(C)]
116pub struct TextOpCopy {
117 pub range: SelectionRange,
118 pub content: ClipboardContent,
119}
120
121#[derive(Debug, Clone)]
123#[repr(C)]
124pub struct TextOpCut {
125 pub range: SelectionRange,
126 pub content: ClipboardContent,
127 pub new_cursor: CursorPosition,
128}
129
130#[derive(Debug, Clone)]
132#[repr(C)]
133pub struct TextOpPaste {
134 pub content: ClipboardContent,
135 pub position: CursorPosition,
136 pub new_cursor: CursorPosition,
137}
138
139#[derive(Copy, Debug, Clone)]
141#[repr(C)]
142pub struct TextOpSelectAll {
143 pub old_range: OptionSelectionRange,
144 pub new_range: SelectionRange,
145}
146
147#[derive(Debug, Clone)]
149#[repr(C, u8)]
150pub enum TextOperation {
151 InsertText(TextOpInsertText),
153 DeleteText(TextOpDeleteText),
155 ReplaceText(TextOpReplaceText),
157 SetSelection(TextOpSetSelection),
159 ExtendSelection(TextOpExtendSelection),
161 ClearSelection(TextOpClearSelection),
163 MoveCursor(TextOpMoveCursor),
165 Copy(TextOpCopy),
167 Cut(TextOpCut),
169 Paste(TextOpPaste),
171 SelectAll(TextOpSelectAll),
173}
174
175pub use azul_core::events::SelectionDirection;
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq)]
180#[repr(C)]
181pub enum CursorMovement {
182 Left,
184 Right,
186 Up,
188 Down,
190 WordLeft,
192 WordRight,
194 LineStart,
196 LineEnd,
198 DocumentStart,
200 DocumentEnd,
202 Absolute,
204}
205
206impl TextChangeset {
207 pub fn new(target: DomNodeId, operation: TextOperation, timestamp: Instant) -> Self {
209 use std::sync::atomic::{AtomicUsize, Ordering};
210 static CHANGESET_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
211
212 Self {
213 id: CHANGESET_ID_COUNTER.fetch_add(1, Ordering::Relaxed),
214 target,
215 operation,
216 timestamp,
217 }
218 }
219
220 #[must_use] pub const fn mutates_text(&self) -> bool {
222 matches!(
223 self.operation,
224 TextOperation::InsertText { .. }
225 | TextOperation::DeleteText { .. }
226 | TextOperation::ReplaceText { .. }
227 | TextOperation::Cut { .. }
228 | TextOperation::Paste { .. }
229 )
230 }
231
232 #[must_use] pub const fn changes_selection(&self) -> bool {
234 matches!(
235 self.operation,
236 TextOperation::SetSelection { .. }
237 | TextOperation::ExtendSelection { .. }
238 | TextOperation::ClearSelection { .. }
239 | TextOperation::MoveCursor { .. }
240 | TextOperation::SelectAll { .. }
241 )
242 }
243
244 #[must_use] pub const fn uses_clipboard(&self) -> bool {
246 matches!(
247 self.operation,
248 TextOperation::Copy { .. } | TextOperation::Cut { .. } | TextOperation::Paste { .. }
249 )
250 }
251
252 #[must_use] pub const fn resulting_cursor_position(&self) -> Option<CursorPosition> {
254 match &self.operation {
255 TextOperation::InsertText(op) => Some(op.new_cursor),
256 TextOperation::DeleteText(op) => Some(op.new_cursor),
257 TextOperation::ReplaceText(op) => Some(op.new_cursor),
258 TextOperation::Cut(op) => Some(op.new_cursor),
259 TextOperation::Paste(op) => Some(op.new_cursor),
260 TextOperation::MoveCursor(op) => Some(op.new_position),
261 _ => None,
262 }
263 }
264
265 #[must_use] pub const fn resulting_selection_range(&self) -> Option<SelectionRange> {
267 match &self.operation {
268 TextOperation::SetSelection(op) => Some(op.new_range),
269 TextOperation::ExtendSelection(op) => Some(op.new_range),
270 TextOperation::SelectAll(op) => Some(op.new_range),
271 _ => None,
272 }
273 }
274}