1use epaint::{Galley, text::CharIndex, text::cursor::CCursor};
2
3use crate::{Event, Id, Key, Modifiers, os::OperatingSystem};
4
5use super::text_cursor_state::{ccursor_next_word, ccursor_previous_word, slice_char_range};
6
7#[derive(Clone, Copy, Debug, Default, PartialEq)]
11#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
12pub struct CCursorRange {
13 pub primary: CCursor,
17
18 pub secondary: CCursor,
21
22 pub h_pos: Option<f32>,
24}
25
26impl CCursorRange {
27 #[inline]
29 pub fn one(ccursor: CCursor) -> Self {
30 Self {
31 primary: ccursor,
32 secondary: ccursor,
33 h_pos: None,
34 }
35 }
36
37 #[inline]
38 pub fn two(min: impl Into<CCursor>, max: impl Into<CCursor>) -> Self {
39 Self {
40 primary: max.into(),
41 secondary: min.into(),
42 h_pos: None,
43 }
44 }
45
46 pub fn select_all(galley: &Galley) -> Self {
48 Self::two(galley.begin(), galley.end())
49 }
50
51 pub fn as_sorted_char_range(&self) -> std::ops::Range<CharIndex> {
53 let [start, end] = self.sorted_cursors();
54 std::ops::Range {
55 start: start.index,
56 end: end.index,
57 }
58 }
59
60 #[inline]
62 pub fn is_empty(&self) -> bool {
63 self.primary == self.secondary
64 }
65
66 pub fn contains(&self, other: Self) -> bool {
68 let [self_min, self_max] = self.sorted_cursors();
69 let [other_min, other_max] = other.sorted_cursors();
70 self_min.index <= other_min.index && other_max.index <= self_max.index
71 }
72
73 pub fn single(&self) -> Option<CCursor> {
76 if self.is_empty() {
77 Some(self.primary)
78 } else {
79 None
80 }
81 }
82
83 #[inline]
84 pub fn is_sorted(&self) -> bool {
85 let p = self.primary;
86 let s = self.secondary;
87 (p.index, p.prefer_next_row) <= (s.index, s.prefer_next_row)
88 }
89
90 #[inline]
92 pub fn sorted_cursors(&self) -> [CCursor; 2] {
93 if self.is_sorted() {
94 [self.primary, self.secondary]
95 } else {
96 [self.secondary, self.primary]
97 }
98 }
99
100 pub fn slice_str<'s>(&self, text: &'s str) -> &'s str {
101 let [min, max] = self.sorted_cursors();
102 slice_char_range(text, min.index..max.index)
103 }
104
105 pub fn on_key_press(
109 &mut self,
110 os: OperatingSystem,
111 galley: &Galley,
112 modifiers: &Modifiers,
113 key: Key,
114 ) -> bool {
115 match key {
116 Key::A if modifiers.command => {
117 *self = Self::select_all(galley);
118 true
119 }
120
121 Key::ArrowLeft | Key::ArrowRight if modifiers.is_none() && !self.is_empty() => {
122 if key == Key::ArrowLeft {
123 *self = Self::one(self.sorted_cursors()[0]);
124 } else {
125 *self = Self::one(self.sorted_cursors()[1]);
126 }
127 true
128 }
129
130 Key::ArrowLeft
131 | Key::ArrowRight
132 | Key::ArrowUp
133 | Key::ArrowDown
134 | Key::Home
135 | Key::End => {
136 move_single_cursor(
137 os,
138 &mut self.primary,
139 &mut self.h_pos,
140 galley,
141 key,
142 modifiers,
143 );
144 if !modifiers.shift {
145 self.secondary = self.primary;
146 }
147 true
148 }
149
150 Key::P | Key::N | Key::B | Key::F | Key::A | Key::E
151 if os == OperatingSystem::Mac && modifiers.ctrl && !modifiers.shift =>
152 {
153 move_single_cursor(
154 os,
155 &mut self.primary,
156 &mut self.h_pos,
157 galley,
158 key,
159 modifiers,
160 );
161 self.secondary = self.primary;
162 true
163 }
164
165 _ => false,
166 }
167 }
168
169 pub fn on_event(
173 &mut self,
174 os: OperatingSystem,
175 event: &Event,
176 galley: &Galley,
177 _widget_id: Id,
178 ) -> bool {
179 match event {
180 Event::Key {
181 modifiers,
182 key,
183 pressed: true,
184 ..
185 } => self.on_key_press(os, galley, modifiers, *key),
186
187 Event::AccessKitActionRequest(accesskit::ActionRequest {
188 action: accesskit::Action::SetTextSelection,
189 target_node,
190 target_tree,
191 data: Some(accesskit::ActionData::SetTextSelection(selection)),
192 }) => {
193 if _widget_id.accesskit_id() == *target_node
194 && *target_tree == accesskit::TreeId::ROOT
195 {
196 let primary =
197 ccursor_from_accesskit_text_position(_widget_id, galley, &selection.focus);
198 let secondary =
199 ccursor_from_accesskit_text_position(_widget_id, galley, &selection.anchor);
200 if let (Some(primary), Some(secondary)) = (primary, secondary) {
201 *self = Self {
202 primary,
203 secondary,
204 h_pos: None,
205 };
206 return true;
207 }
208 }
209 false
210 }
211
212 _ => false,
213 }
214 }
215}
216
217fn ccursor_from_accesskit_text_position(
220 id: Id,
221 galley: &Galley,
222 position: &accesskit::TextPosition,
223) -> Option<CCursor> {
224 use super::accesskit_text::MAX_CHARS_PER_TEXT_RUN;
225
226 let mut total_length = 0usize;
227 for (i, row) in galley.rows.iter().enumerate() {
228 let row_chars = row.glyphs.len() + (row.ends_with_newline as usize);
229 let num_chunks = if row_chars == 0 {
230 1
231 } else {
232 row_chars.div_ceil(MAX_CHARS_PER_TEXT_RUN)
233 };
234
235 for chunk_idx in 0..num_chunks {
236 let run_id = id.with(i).with(chunk_idx);
237 if run_id.accesskit_id() == position.node {
238 let column = chunk_idx * MAX_CHARS_PER_TEXT_RUN + position.character_index;
239 return Some(CCursor {
240 index: CharIndex(total_length + column),
241 prefer_next_row: !(column == row.glyphs.len()
242 && !row.ends_with_newline
243 && (i + 1) < galley.rows.len()),
244 });
245 }
246 }
247
248 total_length += row_chars;
249 }
250 None
251}
252
253fn move_single_cursor(
257 os: OperatingSystem,
258 cursor: &mut CCursor,
259 h_pos: &mut Option<f32>,
260 galley: &Galley,
261 key: Key,
262 modifiers: &Modifiers,
263) {
264 let (new_cursor, new_h_pos) =
265 if os == OperatingSystem::Mac && modifiers.ctrl && !modifiers.shift {
266 match key {
267 Key::A => (galley.cursor_begin_of_row(cursor), None),
268 Key::E => (galley.cursor_end_of_row(cursor), None),
269 Key::P => galley.cursor_up_one_row(cursor, *h_pos),
270 Key::N => galley.cursor_down_one_row(cursor, *h_pos),
271 Key::B => (galley.cursor_left_one_character(cursor), None),
272 Key::F => (galley.cursor_right_one_character(cursor), None),
273 _ => return,
274 }
275 } else {
276 match key {
277 Key::ArrowLeft => {
278 if modifiers.alt || modifiers.ctrl {
279 (ccursor_previous_word(galley, *cursor), None)
281 } else if modifiers.mac_cmd {
282 (galley.cursor_begin_of_row(cursor), None)
283 } else {
284 (galley.cursor_left_one_character(cursor), None)
285 }
286 }
287 Key::ArrowRight => {
288 if modifiers.alt || modifiers.ctrl {
289 (ccursor_next_word(galley, *cursor), None)
291 } else if modifiers.mac_cmd {
292 (galley.cursor_end_of_row(cursor), None)
293 } else {
294 (galley.cursor_right_one_character(cursor), None)
295 }
296 }
297 Key::ArrowUp => {
298 if modifiers.command {
299 (galley.begin(), None)
301 } else {
302 galley.cursor_up_one_row(cursor, *h_pos)
303 }
304 }
305 Key::ArrowDown => {
306 if modifiers.command {
307 (galley.end(), None)
309 } else {
310 galley.cursor_down_one_row(cursor, *h_pos)
311 }
312 }
313
314 Key::Home => {
315 if modifiers.ctrl {
316 (galley.begin(), None)
318 } else {
319 (galley.cursor_begin_of_row(cursor), None)
320 }
321 }
322 Key::End => {
323 if modifiers.ctrl {
324 (galley.end(), None)
326 } else {
327 (galley.cursor_end_of_row(cursor), None)
328 }
329 }
330
331 _ => unreachable!(),
332 }
333 };
334
335 *cursor = new_cursor;
336 *h_pos = new_h_pos;
337}