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 #[cfg(feature = "accesskit")]
194 Event::AccessKitActionRequest(accesskit::ActionRequest {
195 action: accesskit::Action::SetTextSelection,
196 target_node,
197 target_tree,
198 data: Some(accesskit::ActionData::SetTextSelection(selection)),
199 }) => {
200 if _widget_id.accesskit_id() == *target_node
201 && *target_tree == accesskit::TreeId::ROOT
202 {
203 let primary =
204 ccursor_from_accesskit_text_position(_widget_id, galley, &selection.focus);
205 let secondary =
206 ccursor_from_accesskit_text_position(_widget_id, galley, &selection.anchor);
207 if let (Some(primary), Some(secondary)) = (primary, secondary) {
208 *self = Self {
209 primary,
210 secondary,
211 h_pos: None,
212 };
213 return true;
214 }
215 }
216 false
217 }
218
219 _ => false,
220 }
221 }
222}
223
224#[cfg(feature = "accesskit")]
227fn ccursor_from_accesskit_text_position(
228 id: Id,
229 galley: &Galley,
230 position: &accesskit::TextPosition,
231) -> Option<CCursor> {
232 let mut total_length = 0usize;
233 for (i, row) in galley.rows.iter().enumerate() {
234 let row_id = id.with(i);
235 if row_id.accesskit_id() == position.node {
236 return Some(CCursor {
237 index: total_length + position.character_index,
238 prefer_next_row: !(position.character_index == row.glyphs.len()
239 && !row.ends_with_newline
240 && (i + 1) < galley.rows.len()),
241 });
242 }
243 total_length += row.glyphs.len() + (row.ends_with_newline as usize);
244 }
245 None
246}
247
248fn move_single_cursor(
252 os: OperatingSystem,
253 cursor: &mut CCursor,
254 h_pos: &mut Option<f32>,
255 galley: &Galley,
256 key: Key,
257 modifiers: &Modifiers,
258) {
259 let (new_cursor, new_h_pos) =
260 if os == OperatingSystem::Mac && modifiers.ctrl && !modifiers.shift {
261 match key {
262 Key::A => (galley.cursor_begin_of_row(cursor), None),
263 Key::E => (galley.cursor_end_of_row(cursor), None),
264 Key::P => galley.cursor_up_one_row(cursor, *h_pos),
265 Key::N => galley.cursor_down_one_row(cursor, *h_pos),
266 Key::B => (galley.cursor_left_one_character(cursor), None),
267 Key::F => (galley.cursor_right_one_character(cursor), None),
268 _ => return,
269 }
270 } else {
271 match key {
272 Key::ArrowLeft => {
273 if modifiers.alt || modifiers.ctrl {
274 (ccursor_previous_word(galley, *cursor), None)
276 } else if modifiers.mac_cmd {
277 (galley.cursor_begin_of_row(cursor), None)
278 } else {
279 (galley.cursor_left_one_character(cursor), None)
280 }
281 }
282 Key::ArrowRight => {
283 if modifiers.alt || modifiers.ctrl {
284 (ccursor_next_word(galley, *cursor), None)
286 } else if modifiers.mac_cmd {
287 (galley.cursor_end_of_row(cursor), None)
288 } else {
289 (galley.cursor_right_one_character(cursor), None)
290 }
291 }
292 Key::ArrowUp => {
293 if modifiers.command {
294 (galley.begin(), None)
296 } else {
297 galley.cursor_up_one_row(cursor, *h_pos)
298 }
299 }
300 Key::ArrowDown => {
301 if modifiers.command {
302 (galley.end(), None)
304 } else {
305 galley.cursor_down_one_row(cursor, *h_pos)
306 }
307 }
308
309 Key::Home => {
310 if modifiers.ctrl {
311 (galley.begin(), None)
313 } else {
314 (galley.cursor_begin_of_row(cursor), None)
315 }
316 }
317 Key::End => {
318 if modifiers.ctrl {
319 (galley.end(), None)
321 } else {
322 (galley.cursor_end_of_row(cursor), None)
323 }
324 }
325
326 _ => unreachable!(),
327 }
328 };
329
330 *cursor = new_cursor;
331 *h_pos = new_h_pos;
332}