1use epaint::{Galley, 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<usize> {
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 #[inline]
101 #[deprecated = "Use `self.sorted_cursors` instead."]
102 pub fn sorted(&self) -> [CCursor; 2] {
103 self.sorted_cursors()
104 }
105
106 pub fn slice_str<'s>(&self, text: &'s str) -> &'s str {
107 let [min, max] = self.sorted_cursors();
108 slice_char_range(text, min.index..max.index)
109 }
110
111 pub fn on_key_press(
115 &mut self,
116 os: OperatingSystem,
117 galley: &Galley,
118 modifiers: &Modifiers,
119 key: Key,
120 ) -> bool {
121 match key {
122 Key::A if modifiers.command => {
123 *self = Self::select_all(galley);
124 true
125 }
126
127 Key::ArrowLeft | Key::ArrowRight if modifiers.is_none() && !self.is_empty() => {
128 if key == Key::ArrowLeft {
129 *self = Self::one(self.sorted_cursors()[0]);
130 } else {
131 *self = Self::one(self.sorted_cursors()[1]);
132 }
133 true
134 }
135
136 Key::ArrowLeft
137 | Key::ArrowRight
138 | Key::ArrowUp
139 | Key::ArrowDown
140 | Key::Home
141 | Key::End => {
142 move_single_cursor(
143 os,
144 &mut self.primary,
145 &mut self.h_pos,
146 galley,
147 key,
148 modifiers,
149 );
150 if !modifiers.shift {
151 self.secondary = self.primary;
152 }
153 true
154 }
155
156 Key::P | Key::N | Key::B | Key::F | Key::A | Key::E
157 if os == OperatingSystem::Mac && modifiers.ctrl && !modifiers.shift =>
158 {
159 move_single_cursor(
160 os,
161 &mut self.primary,
162 &mut self.h_pos,
163 galley,
164 key,
165 modifiers,
166 );
167 self.secondary = self.primary;
168 true
169 }
170
171 _ => false,
172 }
173 }
174
175 pub fn on_event(
179 &mut self,
180 os: OperatingSystem,
181 event: &Event,
182 galley: &Galley,
183 _widget_id: Id,
184 ) -> bool {
185 match event {
186 Event::Key {
187 modifiers,
188 key,
189 pressed: true,
190 ..
191 } => self.on_key_press(os, galley, modifiers, *key),
192
193 Event::AccessKitActionRequest(accesskit::ActionRequest {
194 action: accesskit::Action::SetTextSelection,
195 target_node,
196 target_tree,
197 data: Some(accesskit::ActionData::SetTextSelection(selection)),
198 }) => {
199 if _widget_id.accesskit_id() == *target_node
200 && *target_tree == accesskit::TreeId::ROOT
201 {
202 let primary =
203 ccursor_from_accesskit_text_position(_widget_id, galley, &selection.focus);
204 let secondary =
205 ccursor_from_accesskit_text_position(_widget_id, galley, &selection.anchor);
206 if let (Some(primary), Some(secondary)) = (primary, secondary) {
207 *self = Self {
208 primary,
209 secondary,
210 h_pos: None,
211 };
212 return true;
213 }
214 }
215 false
216 }
217
218 _ => false,
219 }
220 }
221}
222
223fn ccursor_from_accesskit_text_position(
226 id: Id,
227 galley: &Galley,
228 position: &accesskit::TextPosition,
229) -> Option<CCursor> {
230 use super::accesskit_text::MAX_CHARS_PER_TEXT_RUN;
231
232 let mut total_length = 0usize;
233 for (i, row) in galley.rows.iter().enumerate() {
234 let row_chars = row.glyphs.len() + (row.ends_with_newline as usize);
235 let num_chunks = if row_chars == 0 {
236 1
237 } else {
238 row_chars.div_ceil(MAX_CHARS_PER_TEXT_RUN)
239 };
240
241 for chunk_idx in 0..num_chunks {
242 let run_id = id.with(i).with(chunk_idx);
243 if run_id.accesskit_id() == position.node {
244 let column = chunk_idx * MAX_CHARS_PER_TEXT_RUN + position.character_index;
245 return Some(CCursor {
246 index: total_length + column,
247 prefer_next_row: !(column == row.glyphs.len()
248 && !row.ends_with_newline
249 && (i + 1) < galley.rows.len()),
250 });
251 }
252 }
253
254 total_length += row_chars;
255 }
256 None
257}
258
259fn move_single_cursor(
263 os: OperatingSystem,
264 cursor: &mut CCursor,
265 h_pos: &mut Option<f32>,
266 galley: &Galley,
267 key: Key,
268 modifiers: &Modifiers,
269) {
270 let (new_cursor, new_h_pos) =
271 if os == OperatingSystem::Mac && modifiers.ctrl && !modifiers.shift {
272 match key {
273 Key::A => (galley.cursor_begin_of_row(cursor), None),
274 Key::E => (galley.cursor_end_of_row(cursor), None),
275 Key::P => galley.cursor_up_one_row(cursor, *h_pos),
276 Key::N => galley.cursor_down_one_row(cursor, *h_pos),
277 Key::B => (galley.cursor_left_one_character(cursor), None),
278 Key::F => (galley.cursor_right_one_character(cursor), None),
279 _ => return,
280 }
281 } else {
282 match key {
283 Key::ArrowLeft => {
284 if modifiers.alt || modifiers.ctrl {
285 (ccursor_previous_word(galley, *cursor), None)
287 } else if modifiers.mac_cmd {
288 (galley.cursor_begin_of_row(cursor), None)
289 } else {
290 (galley.cursor_left_one_character(cursor), None)
291 }
292 }
293 Key::ArrowRight => {
294 if modifiers.alt || modifiers.ctrl {
295 (ccursor_next_word(galley, *cursor), None)
297 } else if modifiers.mac_cmd {
298 (galley.cursor_end_of_row(cursor), None)
299 } else {
300 (galley.cursor_right_one_character(cursor), None)
301 }
302 }
303 Key::ArrowUp => {
304 if modifiers.command {
305 (galley.begin(), None)
307 } else {
308 galley.cursor_up_one_row(cursor, *h_pos)
309 }
310 }
311 Key::ArrowDown => {
312 if modifiers.command {
313 (galley.end(), None)
315 } else {
316 galley.cursor_down_one_row(cursor, *h_pos)
317 }
318 }
319
320 Key::Home => {
321 if modifiers.ctrl {
322 (galley.begin(), None)
324 } else {
325 (galley.cursor_begin_of_row(cursor), None)
326 }
327 }
328 Key::End => {
329 if modifiers.ctrl {
330 (galley.end(), None)
332 } else {
333 (galley.cursor_end_of_row(cursor), None)
334 }
335 }
336
337 _ => unreachable!(),
338 }
339 };
340
341 *cursor = new_cursor;
342 *h_pos = new_h_pos;
343}