1use accesskit::{
7 Node as NodeData, NodeId, Point, Rect, Role, TextAlign, TextDecoration, TextDirection,
8 TextPosition as WeakPosition, TextSelection, VerticalOffset,
9};
10use alloc::{string::String, vec::Vec};
11use core::{cmp::Ordering, fmt, iter::FusedIterator};
12
13use crate::{FilterResult, Node, TreeState};
14
15#[derive(Clone, Copy)]
16pub(crate) struct InnerPosition<'a> {
17 pub(crate) node: Node<'a>,
18 pub(crate) character_index: usize,
19}
20
21impl<'a> InnerPosition<'a> {
22 fn upgrade(tree_state: &'a TreeState, weak: WeakPosition) -> Option<Self> {
23 let node = tree_state.node_by_id(weak.node)?;
24 if node.role() != Role::TextRun {
25 return None;
26 }
27 let character_index = weak.character_index;
28 if character_index > node.data().character_lengths().len() {
29 return None;
30 }
31 Some(Self {
32 node,
33 character_index,
34 })
35 }
36
37 fn clamped_upgrade(tree_state: &'a TreeState, weak: WeakPosition) -> Option<Self> {
38 let node = tree_state.node_by_id(weak.node)?;
39 if node.role() != Role::TextRun {
40 return None;
41 }
42 let character_index = weak
43 .character_index
44 .min(node.data().character_lengths().len());
45 Some(Self {
46 node,
47 character_index,
48 })
49 }
50
51 fn is_word_start(&self) -> bool {
52 let mut total_length = 0usize;
53 for length in self.node.data().word_lengths().iter() {
54 if total_length == self.character_index {
55 return true;
56 }
57 total_length += *length as usize;
58 }
59 false
60 }
61
62 fn is_run_start(&self) -> bool {
63 self.character_index == 0
64 }
65
66 fn is_line_start(&self) -> bool {
67 self.is_run_start() && self.node.data().previous_on_line().is_none()
68 }
69
70 fn is_run_end(&self) -> bool {
71 self.character_index == self.node.data().character_lengths().len()
72 }
73
74 fn is_line_end(&self) -> bool {
75 self.is_run_end() && self.node.data().next_on_line().is_none()
76 }
77
78 fn is_paragraph_end(&self) -> bool {
79 self.is_line_end() && self.node.data().value().unwrap().ends_with('\n')
80 }
81
82 fn is_document_start(&self, root_node: &Node) -> bool {
83 self.is_run_start() && self.node.preceding_text_runs(root_node).next().is_none()
84 }
85
86 fn is_document_end(&self, root_node: &Node) -> bool {
87 self.is_run_end() && self.node.following_text_runs(root_node).next().is_none()
88 }
89
90 fn biased_to_start(&self, root_node: &Node) -> Self {
91 if self.is_run_end() {
92 if let Some(node) = self.node.following_text_runs(root_node).next() {
93 return Self {
94 node,
95 character_index: 0,
96 };
97 }
98 }
99 *self
100 }
101
102 fn biased_to_end(&self, root_node: &Node) -> Self {
103 if self.is_run_start() {
104 if let Some(node) = self.node.preceding_text_runs(root_node).next() {
105 return Self {
106 node,
107 character_index: node.data().character_lengths().len(),
108 };
109 }
110 }
111 *self
112 }
113
114 fn comparable(&self, root_node: &Node) -> (Vec<usize>, usize) {
115 let normalized = self.biased_to_start(root_node);
116 (
117 normalized.node.relative_index_path(root_node.id()),
118 normalized.character_index,
119 )
120 }
121
122 fn previous_word_start(&self) -> Self {
123 let mut total_length_before = 0usize;
124 for length in self.node.data().word_lengths().iter() {
125 let new_total_length = total_length_before + (*length as usize);
126 if new_total_length >= self.character_index {
127 break;
128 }
129 total_length_before = new_total_length;
130 }
131 Self {
132 node: self.node,
133 character_index: total_length_before,
134 }
135 }
136
137 fn word_end(&self) -> Self {
138 let mut total_length = 0usize;
139 for length in self.node.data().word_lengths().iter() {
140 total_length += *length as usize;
141 if total_length > self.character_index {
142 break;
143 }
144 }
145 Self {
146 node: self.node,
147 character_index: total_length,
148 }
149 }
150
151 fn line_start(&self) -> Self {
152 let mut node = self.node;
153 while let Some(id) = node.data().previous_on_line() {
154 node = node.tree_state.node_by_id(id).unwrap();
155 }
156 Self {
157 node,
158 character_index: 0,
159 }
160 }
161
162 fn line_end(&self) -> Self {
163 let mut node = self.node;
164 while let Some(id) = node.data().next_on_line() {
165 node = node.tree_state.node_by_id(id).unwrap();
166 }
167 Self {
168 node,
169 character_index: node.data().character_lengths().len(),
170 }
171 }
172
173 pub(crate) fn downgrade(&self) -> WeakPosition {
174 WeakPosition {
175 node: self.node.id(),
176 character_index: self.character_index,
177 }
178 }
179}
180
181impl PartialEq for InnerPosition<'_> {
182 fn eq(&self, other: &Self) -> bool {
183 self.node.id() == other.node.id() && self.character_index == other.character_index
184 }
185}
186
187impl Eq for InnerPosition<'_> {}
188
189#[derive(Clone, Copy)]
190pub struct Position<'a> {
191 root_node: Node<'a>,
192 pub(crate) inner: InnerPosition<'a>,
193}
194
195impl<'a> Position<'a> {
196 pub fn to_raw(self) -> WeakPosition {
197 self.inner.downgrade()
198 }
199
200 pub fn inner_node(&self) -> &Node<'a> {
201 &self.inner.node
202 }
203
204 pub fn is_format_start(&self) -> bool {
205 self.is_document_start()
207 }
208
209 pub fn is_word_start(&self) -> bool {
210 self.inner.is_word_start()
211 }
212
213 pub fn is_line_start(&self) -> bool {
214 self.inner.is_line_start()
215 }
216
217 pub fn is_line_end(&self) -> bool {
218 self.inner.is_line_end()
219 }
220
221 pub fn is_paragraph_start(&self) -> bool {
222 self.is_document_start()
223 || (self.is_line_start()
224 && self.inner.biased_to_end(&self.root_node).is_paragraph_end())
225 }
226
227 pub fn is_paragraph_end(&self) -> bool {
228 self.is_document_end() || self.inner.is_paragraph_end()
229 }
230
231 pub fn is_paragraph_separator(&self) -> bool {
232 if self.is_document_end() {
233 return false;
234 }
235 let next = self.forward_to_character_end();
236 !next.is_document_end() && next.is_paragraph_end()
237 }
238
239 pub fn is_page_start(&self) -> bool {
240 self.is_document_start()
241 }
242
243 pub fn is_document_start(&self) -> bool {
244 self.inner.is_document_start(&self.root_node)
245 }
246
247 pub fn is_document_end(&self) -> bool {
248 self.inner.is_document_end(&self.root_node)
249 }
250
251 pub fn to_degenerate_range(&self) -> Range<'a> {
252 Range::new(self.root_node, self.inner, self.inner)
253 }
254
255 pub fn to_global_usv_index(&self) -> usize {
256 let mut total_length = 0usize;
257 for node in self.root_node.text_runs() {
258 let node_text = node.data().value().unwrap();
259 if node.id() == self.inner.node.id() {
260 let character_lengths = node.data().character_lengths();
261 let slice_end = character_lengths[..self.inner.character_index]
262 .iter()
263 .copied()
264 .map(usize::from)
265 .sum::<usize>();
266 return total_length + node_text[..slice_end].chars().count();
267 }
268 total_length += node_text.chars().count();
269 }
270 panic!("invalid position")
271 }
272
273 pub fn to_global_utf16_index(&self) -> usize {
274 let mut total_length = 0usize;
275 for node in self.root_node.text_runs() {
276 let node_text = node.data().value().unwrap();
277 if node.id() == self.inner.node.id() {
278 let character_lengths = node.data().character_lengths();
279 let slice_end = character_lengths[..self.inner.character_index]
280 .iter()
281 .copied()
282 .map(usize::from)
283 .sum::<usize>();
284 return total_length
285 + node_text[..slice_end]
286 .chars()
287 .map(char::len_utf16)
288 .sum::<usize>();
289 }
290 total_length += node_text.chars().map(char::len_utf16).sum::<usize>();
291 }
292 panic!("invalid position")
293 }
294
295 pub fn to_line_index(&self) -> usize {
296 let mut pos = *self;
297 if !pos.is_line_start() {
298 pos = pos.backward_to_line_start();
299 }
300 let mut lines_before_current = 0usize;
301 while !pos.is_document_start() {
302 pos = pos.backward_to_line_start();
303 lines_before_current += 1;
304 }
305 lines_before_current
306 }
307
308 pub fn biased_to_start(&self) -> Self {
309 Self {
310 root_node: self.root_node,
311 inner: self.inner.biased_to_start(&self.root_node),
312 }
313 }
314
315 pub fn biased_to_end(&self) -> Self {
316 Self {
317 root_node: self.root_node,
318 inner: self.inner.biased_to_end(&self.root_node),
319 }
320 }
321
322 pub fn forward_to_character_start(&self) -> Self {
323 let pos = self.inner.biased_to_start(&self.root_node);
324 Self {
325 root_node: self.root_node,
326 inner: InnerPosition {
327 node: pos.node,
328 character_index: pos.character_index + 1,
329 }
330 .biased_to_start(&self.root_node),
331 }
332 }
333
334 pub fn forward_to_character_end(&self) -> Self {
335 let pos = self.inner.biased_to_start(&self.root_node);
336 Self {
337 root_node: self.root_node,
338 inner: InnerPosition {
339 node: pos.node,
340 character_index: pos.character_index + 1,
341 },
342 }
343 }
344
345 pub fn backward_to_character_start(&self) -> Self {
346 let pos = self.inner.biased_to_end(&self.root_node);
347 Self {
348 root_node: self.root_node,
349 inner: InnerPosition {
350 node: pos.node,
351 character_index: pos.character_index - 1,
352 }
353 .biased_to_start(&self.root_node),
354 }
355 }
356
357 pub fn forward_to_format_start(&self) -> Self {
358 self.document_end()
360 }
361
362 pub fn forward_to_format_end(&self) -> Self {
363 self.document_end()
365 }
366
367 pub fn backward_to_format_start(&self) -> Self {
368 self.document_start()
370 }
371
372 pub fn forward_to_word_start(&self) -> Self {
373 let pos = self.inner.biased_to_start(&self.root_node);
374 Self {
375 root_node: self.root_node,
376 inner: pos.word_end().biased_to_start(&self.root_node),
377 }
378 }
379
380 pub fn forward_to_word_end(&self) -> Self {
381 let pos = self.inner.biased_to_start(&self.root_node);
382 Self {
383 root_node: self.root_node,
384 inner: pos.word_end(),
385 }
386 }
387
388 pub fn backward_to_word_start(&self) -> Self {
389 let pos = self.inner.biased_to_end(&self.root_node);
390 Self {
391 root_node: self.root_node,
392 inner: pos.previous_word_start().biased_to_start(&self.root_node),
393 }
394 }
395
396 pub fn forward_to_line_start(&self) -> Self {
397 let pos = self.inner.biased_to_start(&self.root_node);
398 Self {
399 root_node: self.root_node,
400 inner: pos.line_end().biased_to_start(&self.root_node),
401 }
402 }
403
404 pub fn forward_to_line_end(&self) -> Self {
405 let pos = self.inner.biased_to_start(&self.root_node);
406 Self {
407 root_node: self.root_node,
408 inner: pos.line_end(),
409 }
410 }
411
412 pub fn backward_to_line_start(&self) -> Self {
413 let pos = self.inner.biased_to_end(&self.root_node);
414 Self {
415 root_node: self.root_node,
416 inner: pos.line_start().biased_to_start(&self.root_node),
417 }
418 }
419
420 pub fn forward_to_paragraph_start(&self) -> Self {
421 let mut current = *self;
422 loop {
423 current = current.forward_to_line_start();
424 if current.is_document_end()
425 || current
426 .inner
427 .biased_to_end(&self.root_node)
428 .is_paragraph_end()
429 {
430 break;
431 }
432 }
433 current
434 }
435
436 pub fn forward_to_paragraph_end(&self) -> Self {
437 let mut current = *self;
438 loop {
439 current = current.forward_to_line_end();
440 if current.is_document_end() || current.inner.is_paragraph_end() {
441 break;
442 }
443 }
444 current
445 }
446
447 pub fn backward_to_paragraph_start(&self) -> Self {
448 let mut current = *self;
449 loop {
450 current = current.backward_to_line_start();
451 if current.is_paragraph_start() {
452 break;
453 }
454 }
455 current
456 }
457
458 pub fn forward_to_page_start(&self) -> Self {
459 self.document_end()
460 }
461
462 pub fn forward_to_page_end(&self) -> Self {
463 self.document_end()
464 }
465
466 pub fn backward_to_page_start(&self) -> Self {
467 self.document_start()
468 }
469
470 pub fn document_end(&self) -> Self {
471 Self {
472 root_node: self.root_node,
473 inner: self.root_node.document_end(),
474 }
475 }
476
477 pub fn document_start(&self) -> Self {
478 Self {
479 root_node: self.root_node,
480 inner: self.root_node.document_start(),
481 }
482 }
483}
484
485impl PartialEq for Position<'_> {
486 fn eq(&self, other: &Self) -> bool {
487 self.root_node.id() == other.root_node.id() && self.inner == other.inner
488 }
489}
490
491impl Eq for Position<'_> {}
492
493impl PartialOrd for Position<'_> {
494 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
495 if self.root_node.id() != other.root_node.id() {
496 return None;
497 }
498 let self_comparable = self.inner.comparable(&self.root_node);
499 let other_comparable = other.inner.comparable(&self.root_node);
500 Some(self_comparable.cmp(&other_comparable))
501 }
502}
503
504pub enum AttributeValue<T> {
505 Single(T),
506 Mixed,
507}
508
509#[derive(Clone, Copy)]
510pub struct Range<'a> {
511 pub(crate) node: Node<'a>,
512 pub(crate) start: InnerPosition<'a>,
513 pub(crate) end: InnerPosition<'a>,
514}
515
516impl<'a> Range<'a> {
517 fn new(node: Node<'a>, mut start: InnerPosition<'a>, mut end: InnerPosition<'a>) -> Self {
518 if start.comparable(&node) > end.comparable(&node) {
519 core::mem::swap(&mut start, &mut end);
520 }
521 Self { node, start, end }
522 }
523
524 pub fn node(&self) -> &Node<'a> {
525 &self.node
526 }
527
528 pub fn start(&self) -> Position<'a> {
529 Position {
530 root_node: self.node,
531 inner: self.start,
532 }
533 }
534
535 pub fn end(&self) -> Position<'a> {
536 Position {
537 root_node: self.node,
538 inner: self.end,
539 }
540 }
541
542 pub fn is_degenerate(&self) -> bool {
543 self.start.comparable(&self.node) == self.end.comparable(&self.node)
544 }
545
546 fn walk<F, T>(&self, mut f: F) -> Option<T>
547 where
548 F: FnMut(&Node) -> Option<T>,
549 {
550 let (start, end) = if self.is_degenerate() {
554 (self.start, self.start)
555 } else {
556 let start = self.start.biased_to_start(&self.node);
557 let end = self.end.biased_to_end(&self.node);
558 (start, end)
559 };
560 if let Some(result) = f(&start.node) {
561 return Some(result);
562 }
563 if start.node.id() == end.node.id() {
564 return None;
565 }
566 for node in start.node.following_text_runs(&self.node) {
567 if let Some(result) = f(&node) {
568 return Some(result);
569 }
570 if node.id() == end.node.id() {
571 break;
572 }
573 }
574 None
575 }
576
577 pub fn text(&self) -> String {
578 let mut result = String::new();
579 self.write_text(&mut result).unwrap();
580 result
581 }
582
583 pub fn write_text<W: fmt::Write>(&self, mut writer: W) -> fmt::Result {
584 if let Some(err) = self.walk(|node| {
585 let character_lengths = node.data().character_lengths();
586 let start_index = if node.id() == self.start.node.id() {
587 self.start.character_index
588 } else {
589 0
590 };
591 let end_index = if node.id() == self.end.node.id() {
592 self.end.character_index
593 } else {
594 character_lengths.len()
595 };
596 let value = node.data().value().unwrap();
597 let s = if start_index == end_index {
598 ""
599 } else if start_index == 0 && end_index == character_lengths.len() {
600 value
601 } else {
602 let slice_start = character_lengths[..start_index]
603 .iter()
604 .copied()
605 .map(usize::from)
606 .sum::<usize>();
607 let slice_end = slice_start
608 + character_lengths[start_index..end_index]
609 .iter()
610 .copied()
611 .map(usize::from)
612 .sum::<usize>();
613 &value[slice_start..slice_end]
614 };
615 writer.write_str(s).err()
616 }) {
617 Err(err)
618 } else {
619 Ok(())
620 }
621 }
622
623 pub fn bounding_boxes(&self) -> Vec<Rect> {
631 let mut result = Vec::new();
632 self.walk(|node| {
633 let mut rect = match node.data().bounds() {
634 Some(rect) => rect,
635 None => {
636 return Some(Vec::new());
637 }
638 };
639 let positions = match node.data().character_positions() {
640 Some(positions) => positions,
641 None => {
642 return Some(Vec::new());
643 }
644 };
645 let widths = match node.data().character_widths() {
646 Some(widths) => widths,
647 None => {
648 return Some(Vec::new());
649 }
650 };
651 let direction = match node.text_direction() {
652 Some(direction) => direction,
653 None => {
654 return Some(Vec::new());
655 }
656 };
657 let character_lengths = node.data().character_lengths();
658 let start_index = if node.id() == self.start.node.id() {
659 self.start.character_index
660 } else {
661 0
662 };
663 let end_index = if node.id() == self.end.node.id() {
664 self.end.character_index
665 } else {
666 character_lengths.len()
667 };
668 if start_index != 0 || end_index != character_lengths.len() {
669 let pixel_start = if start_index < character_lengths.len() {
670 positions[start_index]
671 } else {
672 positions[start_index - 1] + widths[start_index - 1]
673 };
674 let pixel_end = if end_index == start_index {
675 pixel_start
676 } else {
677 positions[end_index - 1] + widths[end_index - 1]
678 };
679 let pixel_start = f64::from(pixel_start);
680 let pixel_end = f64::from(pixel_end);
681 match direction {
682 TextDirection::LeftToRight => {
683 let orig_left = rect.x0;
684 rect.x0 = orig_left + pixel_start;
685 rect.x1 = orig_left + pixel_end;
686 }
687 TextDirection::RightToLeft => {
688 let orig_right = rect.x1;
689 rect.x1 = orig_right - pixel_start;
690 rect.x0 = orig_right - pixel_end;
691 }
692 TextDirection::TopToBottom => {
696 let orig_top = rect.y0;
697 rect.y0 = orig_top + pixel_start;
698 rect.y1 = orig_top + pixel_end;
699 }
700 TextDirection::BottomToTop => {
701 let orig_bottom = rect.y1;
702 rect.y1 = orig_bottom - pixel_start;
703 rect.y0 = orig_bottom - pixel_end;
704 }
705 }
706 }
707 result.push(node.transform().transform_rect_bbox(rect));
708 None
709 })
710 .unwrap_or(result)
711 }
712
713 pub fn attribute<F, T>(&self, f: F) -> AttributeValue<T>
714 where
715 F: Fn(&Node) -> T,
716 T: PartialEq,
717 {
718 let mut value = None;
719 self.walk(|node| {
720 let current = f(node);
721 if let Some(value) = &value {
722 if *value != current {
723 return Some(AttributeValue::Mixed);
724 }
725 } else {
726 value = Some(current);
727 }
728 None
729 })
730 .unwrap_or_else(|| AttributeValue::Single(value.unwrap()))
731 }
732
733 fn fix_start_bias(&mut self) {
734 if !self.is_degenerate() {
735 self.start = self.start.biased_to_start(&self.node);
736 }
737 }
738
739 pub fn set_start(&mut self, pos: Position<'a>) {
740 assert_eq!(pos.root_node.id(), self.node.id());
741 self.start = pos.inner;
742 if self.start.comparable(&self.node) >= self.end.comparable(&self.node) {
745 self.end = self.start;
746 }
747 self.fix_start_bias();
748 }
749
750 pub fn set_end(&mut self, pos: Position<'a>) {
751 assert_eq!(pos.root_node.id(), self.node.id());
752 self.end = pos.inner;
753 if self.start.comparable(&self.node) >= self.end.comparable(&self.node) {
756 self.start = self.end;
757 }
758 self.fix_start_bias();
759 }
760
761 pub fn to_text_selection(&self) -> TextSelection {
762 TextSelection {
763 anchor: self.start.downgrade(),
764 focus: self.end.downgrade(),
765 }
766 }
767
768 pub fn downgrade(&self) -> WeakRange {
769 WeakRange {
770 node_id: self.node.id(),
771 start: self.start.downgrade(),
772 end: self.end.downgrade(),
773 start_comparable: self.start.comparable(&self.node),
774 end_comparable: self.end.comparable(&self.node),
775 }
776 }
777}
778
779impl PartialEq for Range<'_> {
780 fn eq(&self, other: &Self) -> bool {
781 self.node.id() == other.node.id() && self.start == other.start && self.end == other.end
782 }
783}
784
785impl Eq for Range<'_> {}
786
787#[derive(Clone, Debug, PartialEq, Eq)]
788pub struct WeakRange {
789 node_id: NodeId,
790 start: WeakPosition,
791 end: WeakPosition,
792 start_comparable: (Vec<usize>, usize),
793 end_comparable: (Vec<usize>, usize),
794}
795
796impl WeakRange {
797 pub fn node_id(&self) -> NodeId {
798 self.node_id
799 }
800
801 pub fn start_comparable(&self) -> &(Vec<usize>, usize) {
802 &self.start_comparable
803 }
804
805 pub fn end_comparable(&self) -> &(Vec<usize>, usize) {
806 &self.end_comparable
807 }
808
809 pub fn upgrade_node<'a>(&self, tree_state: &'a TreeState) -> Option<Node<'a>> {
810 tree_state
811 .node_by_id(self.node_id)
812 .filter(Node::supports_text_ranges)
813 }
814
815 pub fn upgrade<'a>(&self, tree_state: &'a TreeState) -> Option<Range<'a>> {
816 let node = self.upgrade_node(tree_state)?;
817 let start = InnerPosition::upgrade(tree_state, self.start)?;
818 let end = InnerPosition::upgrade(tree_state, self.end)?;
819 Some(Range { node, start, end })
820 }
821}
822
823fn text_node_filter(root_id: NodeId, node: &Node) -> FilterResult {
824 if node.id() == root_id || node.role() == Role::TextRun {
825 FilterResult::Include
826 } else {
827 FilterResult::ExcludeNode
828 }
829}
830
831fn character_index_at_point(node: &Node, point: Point) -> usize {
832 let rect = node.data().bounds().unwrap();
835 let character_lengths = node.data().character_lengths();
836 let positions = match node.data().character_positions() {
837 Some(positions) => positions,
838 None => {
839 return 0;
840 }
841 };
842 let widths = match node.data().character_widths() {
843 Some(widths) => widths,
844 None => {
845 return 0;
846 }
847 };
848 let direction = match node.text_direction() {
849 Some(direction) => direction,
850 None => {
851 return 0;
852 }
853 };
854 for (i, (position, width)) in positions.iter().zip(widths.iter()).enumerate().rev() {
855 let relative_pos = match direction {
856 TextDirection::LeftToRight => point.x - rect.x0,
857 TextDirection::RightToLeft => rect.x1 - point.x,
858 TextDirection::TopToBottom => point.y - rect.y0,
862 TextDirection::BottomToTop => rect.y1 - point.y,
863 };
864 if relative_pos >= f64::from(*position) && relative_pos < f64::from(*position + *width) {
865 return i;
866 }
867 }
868 character_lengths.len()
869}
870
871macro_rules! inherited_properties {
872 ($(($getter:ident, $type:ty, $setter:ident, $test_value:expr)),+) => {
873 impl Node<'_> {
874 $(pub fn $getter(&self) -> Option<$type> {
875 self.fetch_inherited_property(NodeData::$getter)
876 })*
877 }
878 $(#[cfg(test)]
879 mod $getter {
880 use accesskit::{Node, NodeId, Role, Tree, TreeUpdate};
881 use alloc::vec;
882 #[test]
883 fn directly_set() {
884 let update = TreeUpdate {
885 nodes: vec![
886 (NodeId(0), {
887 let mut node = Node::new(Role::TextInput);
888 node.set_children(vec![NodeId(1)]);
889 node
890 }),
891 (NodeId(1), {
892 let mut node = Node::new(Role::TextRun);
893 node.$setter($test_value);
894 node
895 }),
896 ],
897 tree: Some(Tree::new(NodeId(0))),
898 focus: NodeId(0),
899 };
900 let tree = crate::Tree::new(update, false);
901 assert_eq!(tree.state().node_by_id(NodeId(1)).unwrap().$getter(), Some($test_value));
902 }
903 #[test]
904 fn set_on_parent() {
905 let update = TreeUpdate {
906 nodes: vec![
907 (NodeId(0), {
908 let mut node = Node::new(Role::TextInput);
909 node.set_children(vec![NodeId(1)]);
910 node.$setter($test_value);
911 node
912 }),
913 (NodeId(1), Node::new(Role::TextRun)),
914 ],
915 tree: Some(Tree::new(NodeId(0))),
916 focus: NodeId(0),
917 };
918 let tree = crate::Tree::new(update, false);
919 assert_eq!(tree.state().node_by_id(NodeId(1)).unwrap().$getter(), Some($test_value));
920 }
921 #[test]
922 fn unset() {
923 let update = TreeUpdate {
924 nodes: vec![
925 (NodeId(0), {
926 let mut node = Node::new(Role::TextInput);
927 node.set_children(vec![NodeId(1)]);
928 node
929 }),
930 (NodeId(1), Node::new(Role::TextRun)),
931 ],
932 tree: Some(Tree::new(NodeId(0))),
933 focus: NodeId(0),
934 };
935 let tree = crate::Tree::new(update, false);
936 assert!(tree.state().node_by_id(NodeId(1)).unwrap().$getter().is_none());
937 }
938 })*
939 }
940}
941
942inherited_properties! {
943 (text_direction, TextDirection, set_text_direction, accesskit::TextDirection::RightToLeft),
944 (font_family, &str, set_font_family, "Inconsolata"),
945 (language, &str, set_language, "fr"),
946 (font_size, f64, set_font_size, 24.0),
947 (font_weight, f64, set_font_weight, 700.0),
948 (background_color, u32, set_background_color, 0xff),
949 (foreground_color, u32, set_foreground_color, 0xff00),
950 (overline, TextDecoration, set_overline, accesskit::TextDecoration::Dotted),
951 (strikethrough, TextDecoration, set_strikethrough, accesskit::TextDecoration::Dashed),
952 (underline, TextDecoration, set_underline, accesskit::TextDecoration::Double),
953 (text_align, TextAlign, set_text_align, accesskit::TextAlign::Justify),
954 (vertical_offset, VerticalOffset, set_vertical_offset, accesskit::VerticalOffset::Superscript)
955}
956
957impl<'a> Node<'a> {
958 pub(crate) fn text_runs(
959 &self,
960 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
961 let id = self.id();
962 self.filtered_children(move |node| text_node_filter(id, node))
963 }
964
965 fn following_text_runs(
966 &self,
967 root_node: &Node,
968 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
969 let id = root_node.id();
970 self.following_filtered_siblings(move |node| text_node_filter(id, node))
971 }
972
973 fn preceding_text_runs(
974 &self,
975 root_node: &Node,
976 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
977 let id = root_node.id();
978 self.preceding_filtered_siblings(move |node| text_node_filter(id, node))
979 }
980
981 pub fn supports_text_ranges(&self) -> bool {
982 (self.is_text_input()
983 || matches!(self.role(), Role::Label | Role::Document | Role::Terminal))
984 && self.text_runs().next().is_some()
985 }
986
987 fn document_start(&self) -> InnerPosition<'a> {
988 let node = self.text_runs().next().unwrap();
989 InnerPosition {
990 node,
991 character_index: 0,
992 }
993 }
994
995 fn document_end(&self) -> InnerPosition<'a> {
996 let node = self.text_runs().next_back().unwrap();
997 InnerPosition {
998 node,
999 character_index: node.data().character_lengths().len(),
1000 }
1001 }
1002
1003 pub fn document_range(&self) -> Range<'_> {
1004 let start = self.document_start();
1005 let end = self.document_end();
1006 Range::new(*self, start, end)
1007 }
1008
1009 pub fn has_text_selection(&self) -> bool {
1010 self.data().text_selection().is_some()
1011 }
1012
1013 pub fn text_selection(&self) -> Option<Range<'_>> {
1014 self.data().text_selection().map(|selection| {
1015 let anchor = InnerPosition::clamped_upgrade(self.tree_state, selection.anchor).unwrap();
1016 let focus = InnerPosition::clamped_upgrade(self.tree_state, selection.focus).unwrap();
1017 Range::new(*self, anchor, focus)
1018 })
1019 }
1020
1021 pub fn text_selection_anchor(&self) -> Option<Position<'_>> {
1022 self.data().text_selection().map(|selection| {
1023 let anchor = InnerPosition::clamped_upgrade(self.tree_state, selection.anchor).unwrap();
1024 Position {
1025 root_node: *self,
1026 inner: anchor,
1027 }
1028 })
1029 }
1030
1031 pub fn text_selection_focus(&self) -> Option<Position<'_>> {
1032 self.data().text_selection().map(|selection| {
1033 let focus = InnerPosition::clamped_upgrade(self.tree_state, selection.focus).unwrap();
1034 Position {
1035 root_node: *self,
1036 inner: focus,
1037 }
1038 })
1039 }
1040
1041 pub fn text_position_at_point(&self, point: Point) -> Position<'_> {
1044 let id = self.id();
1045 if let Some((node, point)) = self.hit_test(point, &move |node| text_node_filter(id, node)) {
1046 if node.role() == Role::TextRun {
1047 let pos = InnerPosition {
1048 node,
1049 character_index: character_index_at_point(&node, point),
1050 };
1051 return Position {
1052 root_node: *self,
1053 inner: pos,
1054 };
1055 }
1056 }
1057
1058 if let Some(node) = self.text_runs().next() {
1062 if let Some(rect) = node.bounding_box_in_coordinate_space(self) {
1063 let origin = rect.origin();
1064 if point.x < origin.x || point.y < origin.y {
1065 return Position {
1066 root_node: *self,
1067 inner: self.document_start(),
1068 };
1069 }
1070 }
1071 }
1072
1073 for node in self.text_runs().rev() {
1074 if let Some(rect) = node.bounding_box_in_coordinate_space(self) {
1075 if let Some(direction) = node.text_direction() {
1076 let is_past_end = match direction {
1077 TextDirection::LeftToRight => {
1078 point.y >= rect.y0 && point.y < rect.y1 && point.x >= rect.x1
1079 }
1080 TextDirection::RightToLeft => {
1081 point.y >= rect.y0 && point.y < rect.y1 && point.x < rect.x0
1082 }
1083 TextDirection::TopToBottom => {
1087 point.x >= rect.x0 && point.x < rect.x1 && point.y >= rect.y1
1088 }
1089 TextDirection::BottomToTop => {
1090 point.x >= rect.x0 && point.x < rect.x1 && point.y < rect.y0
1091 }
1092 };
1093 if is_past_end {
1094 return Position {
1095 root_node: *self,
1096 inner: InnerPosition {
1097 node,
1098 character_index: node.data().character_lengths().len(),
1099 },
1100 };
1101 }
1102 }
1103 }
1104 }
1105
1106 Position {
1107 root_node: *self,
1108 inner: self.document_end(),
1109 }
1110 }
1111
1112 pub fn line_range_from_index(&self, line_index: usize) -> Option<Range<'_>> {
1113 let mut pos = self.document_range().start();
1114
1115 if line_index > 0 {
1116 if pos.is_document_end() || pos.forward_to_line_end().is_document_end() {
1117 return None;
1118 }
1119 for _ in 0..line_index {
1120 if pos.is_document_end() {
1121 return None;
1122 }
1123 pos = pos.forward_to_line_start();
1124 }
1125 }
1126
1127 let end = if pos.is_document_end() {
1128 pos
1129 } else {
1130 pos.forward_to_line_end()
1131 };
1132 Some(Range::new(*self, pos.inner, end.inner))
1133 }
1134
1135 pub fn text_position_from_global_usv_index(&self, index: usize) -> Option<Position<'_>> {
1136 let mut total_length = 0usize;
1137 for node in self.text_runs() {
1138 let node_text = node.data().value().unwrap();
1139 let node_text_length = node_text.chars().count();
1140 let new_total_length = total_length + node_text_length;
1141 if index >= total_length && index < new_total_length {
1142 let index = index - total_length;
1143 let mut utf8_length = 0usize;
1144 let mut usv_length = 0usize;
1145 for (character_index, utf8_char_length) in
1146 node.data().character_lengths().iter().enumerate()
1147 {
1148 let new_utf8_length = utf8_length + (*utf8_char_length as usize);
1149 let char_str = &node_text[utf8_length..new_utf8_length];
1150 let usv_char_length = char_str.chars().count();
1151 let new_usv_length = usv_length + usv_char_length;
1152 if index >= usv_length && index < new_usv_length {
1153 return Some(Position {
1154 root_node: *self,
1155 inner: InnerPosition {
1156 node,
1157 character_index,
1158 },
1159 });
1160 }
1161 utf8_length = new_utf8_length;
1162 usv_length = new_usv_length;
1163 }
1164 panic!("index out of range");
1165 }
1166 total_length = new_total_length;
1167 }
1168 if index == total_length {
1169 return Some(Position {
1170 root_node: *self,
1171 inner: self.document_end(),
1172 });
1173 }
1174 None
1175 }
1176
1177 pub fn text_position_from_global_utf16_index(&self, index: usize) -> Option<Position<'_>> {
1178 let mut total_length = 0usize;
1179 for node in self.text_runs() {
1180 let node_text = node.data().value().unwrap();
1181 let node_text_length = node_text.chars().map(char::len_utf16).sum::<usize>();
1182 let new_total_length = total_length + node_text_length;
1183 if index >= total_length && index < new_total_length {
1184 let index = index - total_length;
1185 let mut utf8_length = 0usize;
1186 let mut utf16_length = 0usize;
1187 for (character_index, utf8_char_length) in
1188 node.data().character_lengths().iter().enumerate()
1189 {
1190 let new_utf8_length = utf8_length + (*utf8_char_length as usize);
1191 let char_str = &node_text[utf8_length..new_utf8_length];
1192 let utf16_char_length = char_str.chars().map(char::len_utf16).sum::<usize>();
1193 let new_utf16_length = utf16_length + utf16_char_length;
1194 if index >= utf16_length && index < new_utf16_length {
1195 return Some(Position {
1196 root_node: *self,
1197 inner: InnerPosition {
1198 node,
1199 character_index,
1200 },
1201 });
1202 }
1203 utf8_length = new_utf8_length;
1204 utf16_length = new_utf16_length;
1205 }
1206 panic!("index out of range");
1207 }
1208 total_length = new_total_length;
1209 }
1210 if index == total_length {
1211 return Some(Position {
1212 root_node: *self,
1213 inner: self.document_end(),
1214 });
1215 }
1216 None
1217 }
1218}
1219
1220#[cfg(test)]
1221mod tests {
1222 use accesskit::{NodeId, Point, Rect, TextSelection};
1223 use alloc::vec;
1224
1225 fn main_multiline_tree(selection: Option<TextSelection>) -> crate::Tree {
1227 use accesskit::{Action, Affine, Node, Role, TextDirection, Tree, TreeUpdate};
1228
1229 let update = TreeUpdate {
1230 nodes: vec![
1231 (NodeId(0), {
1232 let mut node = Node::new(Role::Window);
1233 node.set_transform(Affine::scale(1.5));
1234 node.set_children(vec![NodeId(1)]);
1235 node
1236 }),
1237 (NodeId(1), {
1238 let mut node = Node::new(Role::MultilineTextInput);
1239 node.set_bounds(Rect {
1240 x0: 8.0,
1241 y0: 31.666664123535156,
1242 x1: 296.0,
1243 y1: 123.66666412353516,
1244 });
1245 node.set_children(vec![
1246 NodeId(2),
1247 NodeId(3),
1248 NodeId(4),
1249 NodeId(5),
1250 NodeId(6),
1251 NodeId(7),
1252 ]);
1253 node.add_action(Action::Focus);
1254 if let Some(selection) = selection {
1255 node.set_text_selection(selection);
1256 }
1257 node
1258 }),
1259 (NodeId(2), {
1260 let mut node = Node::new(Role::TextRun);
1261 node.set_bounds(Rect {
1262 x0: 12.0,
1263 y0: 33.666664123535156,
1264 x1: 290.9189147949219,
1265 y1: 48.33333206176758,
1266 });
1267 node.set_value("This paragraph is\u{a0}long enough to wrap ");
1272 node.set_text_direction(TextDirection::LeftToRight);
1273 node.set_character_lengths([
1274 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
1275 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1276 ]);
1277 node.set_character_positions([
1278 0.0, 7.3333335, 14.666667, 22.0, 29.333334, 36.666668, 44.0, 51.333332,
1279 58.666668, 66.0, 73.333336, 80.666664, 88.0, 95.333336, 102.666664, 110.0,
1280 117.333336, 124.666664, 132.0, 139.33333, 146.66667, 154.0, 161.33333,
1281 168.66667, 176.0, 183.33333, 190.66667, 198.0, 205.33333, 212.66667, 220.0,
1282 227.33333, 234.66667, 242.0, 249.33333, 256.66666, 264.0, 271.33334,
1283 ]);
1284 node.set_character_widths([
1285 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1286 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1287 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1288 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1289 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1290 ]);
1291 node.set_word_lengths([5, 10, 3, 5, 7, 3, 5]);
1292 node
1293 }),
1294 (NodeId(3), {
1295 let mut node = Node::new(Role::TextRun);
1296 node.set_bounds(Rect {
1297 x0: 12.0,
1298 y0: 48.33333206176758,
1299 x1: 129.5855712890625,
1300 y1: 63.0,
1301 });
1302 node.set_value("to another line.\n");
1303 node.set_text_direction(TextDirection::LeftToRight);
1304 node.set_character_lengths([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
1305 node.set_character_positions([
1306 0.0, 7.3333435, 14.666687, 22.0, 29.333344, 36.666687, 44.0, 51.333344,
1307 58.666687, 66.0, 73.33334, 80.66669, 88.0, 95.33334, 102.66669, 110.0,
1308 117.58557,
1309 ]);
1310 node.set_character_widths([
1311 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1312 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1313 0.0,
1314 ]);
1315 node.set_word_lengths([3, 8, 6]);
1316 node
1317 }),
1318 (NodeId(4), {
1319 let mut node = Node::new(Role::TextRun);
1320 node.set_bounds(Rect {
1321 x0: 12.0,
1322 y0: 63.0,
1323 x1: 144.25222778320313,
1324 y1: 77.66666412353516,
1325 });
1326 node.set_value("Another paragraph.\n");
1327 node.set_text_direction(TextDirection::LeftToRight);
1328 node.set_character_lengths([
1329 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1330 ]);
1331 node.set_character_positions([
1332 0.0, 7.3333335, 14.666667, 22.0, 29.333334, 36.666668, 44.0, 51.333332,
1333 58.666668, 66.0, 73.333336, 80.666664, 88.0, 95.333336, 102.666664, 110.0,
1334 117.333336, 124.666664, 132.25223,
1335 ]);
1336 node.set_character_widths([
1337 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1338 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1339 7.58557, 7.58557, 0.0,
1340 ]);
1341 node.set_word_lengths([8, 11]);
1342 node
1343 }),
1344 (NodeId(5), {
1345 let mut node = Node::new(Role::TextRun);
1346 node.set_bounds(Rect {
1347 x0: 12.0,
1348 y0: 77.66666412353516,
1349 x1: 12.0,
1350 y1: 92.33332824707031,
1351 });
1352 node.set_value("\n");
1353 node.set_text_direction(TextDirection::LeftToRight);
1354 node.set_character_lengths([1]);
1355 node.set_character_positions([0.0]);
1356 node.set_character_widths([0.0]);
1357 node.set_word_lengths([1]);
1358 node
1359 }),
1360 (NodeId(6), {
1361 let mut node = Node::new(Role::TextRun);
1362 node.set_bounds(Rect {
1363 x0: 12.0,
1364 y0: 92.33332824707031,
1365 x1: 158.9188995361328,
1366 y1: 107.0,
1367 });
1368 node.set_value("Last non-blank line\u{1f44d}\u{1f3fb}\n");
1373 node.set_text_direction(TextDirection::LeftToRight);
1374 node.set_character_lengths([
1375 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 1,
1376 ]);
1377 node.set_character_positions([
1378 0.0, 7.3333335, 14.666667, 22.0, 29.333334, 36.666668, 44.0, 51.333332,
1379 58.666668, 66.0, 73.333336, 80.666664, 88.0, 95.333336, 102.666664, 110.0,
1380 117.333336, 124.666664, 132.0, 139.33333, 146.9189,
1381 ]);
1382 node.set_character_widths([
1383 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1384 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1385 7.58557, 7.58557, 7.58557, 7.58557, 0.0,
1386 ]);
1387 node.set_word_lengths([5, 4, 6, 6]);
1388 node
1389 }),
1390 (NodeId(7), {
1391 let mut node = Node::new(Role::TextRun);
1392 node.set_bounds(Rect {
1393 x0: 12.0,
1394 y0: 107.0,
1395 x1: 12.0,
1396 y1: 121.66666412353516,
1397 });
1398 node.set_value("");
1399 node.set_text_direction(TextDirection::LeftToRight);
1400 node.set_character_lengths([]);
1401 node.set_character_positions([]);
1402 node.set_character_widths([]);
1403 node.set_word_lengths([0]);
1404 node
1405 }),
1406 ],
1407 tree: Some(Tree::new(NodeId(0))),
1408 focus: NodeId(1),
1409 };
1410
1411 crate::Tree::new(update, true)
1412 }
1413
1414 fn multiline_end_selection() -> TextSelection {
1415 use accesskit::TextPosition;
1416
1417 TextSelection {
1418 anchor: TextPosition {
1419 node: NodeId(7),
1420 character_index: 0,
1421 },
1422 focus: TextPosition {
1423 node: NodeId(7),
1424 character_index: 0,
1425 },
1426 }
1427 }
1428
1429 fn multiline_past_end_selection() -> TextSelection {
1430 use accesskit::TextPosition;
1431
1432 TextSelection {
1433 anchor: TextPosition {
1434 node: NodeId(7),
1435 character_index: 3,
1436 },
1437 focus: TextPosition {
1438 node: NodeId(7),
1439 character_index: 3,
1440 },
1441 }
1442 }
1443
1444 fn multiline_wrapped_line_end_selection() -> TextSelection {
1445 use accesskit::TextPosition;
1446
1447 TextSelection {
1448 anchor: TextPosition {
1449 node: NodeId(2),
1450 character_index: 38,
1451 },
1452 focus: TextPosition {
1453 node: NodeId(2),
1454 character_index: 38,
1455 },
1456 }
1457 }
1458
1459 fn multiline_first_line_middle_selection() -> TextSelection {
1460 use accesskit::TextPosition;
1461
1462 TextSelection {
1463 anchor: TextPosition {
1464 node: NodeId(2),
1465 character_index: 5,
1466 },
1467 focus: TextPosition {
1468 node: NodeId(2),
1469 character_index: 5,
1470 },
1471 }
1472 }
1473
1474 fn multiline_second_line_middle_selection() -> TextSelection {
1475 use accesskit::TextPosition;
1476
1477 TextSelection {
1478 anchor: TextPosition {
1479 node: NodeId(3),
1480 character_index: 5,
1481 },
1482 focus: TextPosition {
1483 node: NodeId(3),
1484 character_index: 5,
1485 },
1486 }
1487 }
1488
1489 #[test]
1490 fn supports_text_ranges() {
1491 let tree = main_multiline_tree(None);
1492 let state = tree.state();
1493 assert!(!state.node_by_id(NodeId(0)).unwrap().supports_text_ranges());
1494 assert!(state.node_by_id(NodeId(1)).unwrap().supports_text_ranges());
1495 }
1496
1497 #[test]
1498 fn multiline_document_range() {
1499 let tree = main_multiline_tree(None);
1500 let state = tree.state();
1501 let node = state.node_by_id(NodeId(1)).unwrap();
1502 let range = node.document_range();
1503 let start = range.start();
1504 assert!(start.is_word_start());
1505 assert!(start.is_line_start());
1506 assert!(!start.is_line_end());
1507 assert!(start.is_paragraph_start());
1508 assert!(start.is_document_start());
1509 assert!(!start.is_document_end());
1510 let end = range.end();
1511 assert!(start < end);
1512 assert!(end.is_word_start());
1513 assert!(end.is_line_start());
1514 assert!(end.is_line_end());
1515 assert!(end.is_paragraph_start());
1516 assert!(!end.is_document_start());
1517 assert!(end.is_document_end());
1518 assert_eq!(range.text(), "This paragraph is\u{a0}long enough to wrap to another line.\nAnother paragraph.\n\nLast non-blank line\u{1f44d}\u{1f3fb}\n");
1519 assert_eq!(
1520 range.bounding_boxes(),
1521 vec![
1522 Rect {
1523 x0: 18.0,
1524 y0: 50.499996185302734,
1525 x1: 436.3783721923828,
1526 y1: 72.49999809265137
1527 },
1528 Rect {
1529 x0: 18.0,
1530 y0: 72.49999809265137,
1531 x1: 194.37835693359375,
1532 y1: 94.5
1533 },
1534 Rect {
1535 x0: 18.0,
1536 y0: 94.5,
1537 x1: 216.3783416748047,
1538 y1: 116.49999618530273
1539 },
1540 Rect {
1541 x0: 18.0,
1542 y0: 116.49999618530273,
1543 x1: 18.0,
1544 y1: 138.49999237060547
1545 },
1546 Rect {
1547 x0: 18.0,
1548 y0: 138.49999237060547,
1549 x1: 238.37834930419922,
1550 y1: 160.5
1551 }
1552 ]
1553 );
1554 }
1555
1556 #[test]
1557 fn multiline_end_degenerate_range() {
1558 let tree = main_multiline_tree(Some(multiline_end_selection()));
1559 let state = tree.state();
1560 let node = state.node_by_id(NodeId(1)).unwrap();
1561 let range = node.text_selection().unwrap();
1562 assert!(range.is_degenerate());
1563 let pos = range.start();
1564 assert!(pos.is_word_start());
1565 assert!(pos.is_line_start());
1566 assert!(pos.is_line_end());
1567 assert!(pos.is_paragraph_start());
1568 assert!(!pos.is_document_start());
1569 assert!(pos.is_document_end());
1570 assert_eq!(range.text(), "");
1571 assert_eq!(
1572 range.bounding_boxes(),
1573 vec![Rect {
1574 x0: 18.0,
1575 y0: 160.5,
1576 x1: 18.0,
1577 y1: 182.49999618530273,
1578 }]
1579 );
1580 }
1581
1582 #[test]
1583 fn multiline_wrapped_line_end_range() {
1584 let tree = main_multiline_tree(Some(multiline_wrapped_line_end_selection()));
1585 let state = tree.state();
1586 let node = state.node_by_id(NodeId(1)).unwrap();
1587 let range = node.text_selection().unwrap();
1588 assert!(range.is_degenerate());
1589 let pos = range.start();
1590 assert!(!pos.is_word_start());
1591 assert!(!pos.is_line_start());
1592 assert!(pos.is_line_end());
1593 assert!(!pos.is_paragraph_start());
1594 assert!(!pos.is_document_start());
1595 assert!(!pos.is_document_end());
1596 assert_eq!(range.text(), "");
1597 assert_eq!(
1598 range.bounding_boxes(),
1599 vec![Rect {
1600 x0: 436.3783721923828,
1601 y0: 50.499996185302734,
1602 x1: 436.3783721923828,
1603 y1: 72.49999809265137
1604 }]
1605 );
1606 let char_end_pos = pos.forward_to_character_end();
1607 let mut line_start_range = range;
1608 line_start_range.set_end(char_end_pos);
1609 assert!(!line_start_range.is_degenerate());
1610 assert!(line_start_range.start().is_line_start());
1611 assert_eq!(line_start_range.text(), "t");
1612 assert_eq!(
1613 line_start_range.bounding_boxes(),
1614 vec![Rect {
1615 x0: 18.0,
1616 y0: 72.49999809265137,
1617 x1: 29.378354787826538,
1618 y1: 94.5
1619 }]
1620 );
1621 let prev_char_pos = pos.backward_to_character_start();
1622 let mut prev_char_range = range;
1623 prev_char_range.set_start(prev_char_pos);
1624 assert!(!prev_char_range.is_degenerate());
1625 assert!(prev_char_range.end().is_line_end());
1626 assert_eq!(prev_char_range.text(), " ");
1627 assert_eq!(
1628 prev_char_range.bounding_boxes(),
1629 vec![Rect {
1630 x0: 425.00001525878906,
1631 y0: 50.499996185302734,
1632 x1: 436.3783721923828,
1633 y1: 72.49999809265137
1634 }]
1635 );
1636 assert!(prev_char_pos.forward_to_character_end().is_line_end());
1637 assert!(prev_char_pos.forward_to_word_end().is_line_end());
1638 assert!(prev_char_pos.forward_to_line_end().is_line_end());
1639 assert!(prev_char_pos.forward_to_character_start().is_line_start());
1640 assert!(prev_char_pos.forward_to_word_start().is_line_start());
1641 assert!(prev_char_pos.forward_to_line_start().is_line_start());
1642 }
1643
1644 #[test]
1645 fn multiline_find_line_ends_from_middle() {
1646 let tree = main_multiline_tree(Some(multiline_second_line_middle_selection()));
1647 let state = tree.state();
1648 let node = state.node_by_id(NodeId(1)).unwrap();
1649 let mut range = node.text_selection().unwrap();
1650 assert!(range.is_degenerate());
1651 let pos = range.start();
1652 assert!(!pos.is_line_start());
1653 assert!(!pos.is_line_end());
1654 assert!(!pos.is_document_start());
1655 assert!(!pos.is_document_end());
1656 let line_start = pos.backward_to_line_start();
1657 range.set_start(line_start);
1658 let line_end = line_start.forward_to_line_end();
1659 range.set_end(line_end);
1660 assert!(!range.is_degenerate());
1661 assert!(range.start().is_line_start());
1662 assert!(range.end().is_line_end());
1663 assert_eq!(range.text(), "to another line.\n");
1664 assert_eq!(
1665 range.bounding_boxes(),
1666 vec![Rect {
1667 x0: 18.0,
1668 y0: 72.49999809265137,
1669 x1: 194.37835693359375,
1670 y1: 94.5
1671 },]
1672 );
1673 assert!(line_start.forward_to_line_start().is_line_start());
1674 }
1675
1676 #[test]
1677 fn multiline_find_wrapped_line_ends_from_middle() {
1678 let tree = main_multiline_tree(Some(multiline_first_line_middle_selection()));
1679 let state = tree.state();
1680 let node = state.node_by_id(NodeId(1)).unwrap();
1681 let mut range = node.text_selection().unwrap();
1682 assert!(range.is_degenerate());
1683 let pos = range.start();
1684 assert!(!pos.is_line_start());
1685 assert!(!pos.is_line_end());
1686 assert!(!pos.is_document_start());
1687 assert!(!pos.is_document_end());
1688 let line_start = pos.backward_to_line_start();
1689 range.set_start(line_start);
1690 let line_end = line_start.forward_to_line_end();
1691 range.set_end(line_end);
1692 assert!(!range.is_degenerate());
1693 assert!(range.start().is_line_start());
1694 assert!(range.end().is_line_end());
1695 assert_eq!(range.text(), "This paragraph is\u{a0}long enough to wrap ");
1696 assert_eq!(
1697 range.bounding_boxes(),
1698 vec![Rect {
1699 x0: 18.0,
1700 y0: 50.499996185302734,
1701 x1: 436.3783721923828,
1702 y1: 72.49999809265137
1703 }]
1704 );
1705 assert!(line_start.forward_to_line_start().is_line_start());
1706 }
1707
1708 #[test]
1709 fn multiline_find_paragraph_ends_from_middle() {
1710 let tree = main_multiline_tree(Some(multiline_second_line_middle_selection()));
1711 let state = tree.state();
1712 let node = state.node_by_id(NodeId(1)).unwrap();
1713 let mut range = node.text_selection().unwrap();
1714 assert!(range.is_degenerate());
1715 let pos = range.start();
1716 assert!(!pos.is_paragraph_start());
1717 assert!(!pos.is_document_start());
1718 assert!(!pos.is_document_end());
1719 let paragraph_start = pos.backward_to_paragraph_start();
1720 range.set_start(paragraph_start);
1721 let paragraph_end = paragraph_start.forward_to_paragraph_end();
1722 range.set_end(paragraph_end);
1723 assert!(!range.is_degenerate());
1724 assert!(range.start().is_paragraph_start());
1725 assert!(range.end().is_paragraph_end());
1726 assert_eq!(
1727 range.text(),
1728 "This paragraph is\u{a0}long enough to wrap to another line.\n"
1729 );
1730 assert_eq!(
1731 range.bounding_boxes(),
1732 vec![
1733 Rect {
1734 x0: 18.0,
1735 y0: 50.499996185302734,
1736 x1: 436.3783721923828,
1737 y1: 72.49999809265137
1738 },
1739 Rect {
1740 x0: 18.0,
1741 y0: 72.49999809265137,
1742 x1: 194.37835693359375,
1743 y1: 94.5
1744 },
1745 ]
1746 );
1747 assert!(paragraph_start
1748 .forward_to_paragraph_start()
1749 .is_paragraph_start());
1750 }
1751
1752 #[test]
1753 fn multiline_find_word_ends_from_middle() {
1754 let tree = main_multiline_tree(Some(multiline_second_line_middle_selection()));
1755 let state = tree.state();
1756 let node = state.node_by_id(NodeId(1)).unwrap();
1757 let mut range = node.text_selection().unwrap();
1758 assert!(range.is_degenerate());
1759 let pos = range.start();
1760 assert!(!pos.is_word_start());
1761 assert!(!pos.is_document_start());
1762 assert!(!pos.is_document_end());
1763 let word_start = pos.backward_to_word_start();
1764 range.set_start(word_start);
1765 let word_end = word_start.forward_to_word_end();
1766 range.set_end(word_end);
1767 assert!(!range.is_degenerate());
1768 assert_eq!(range.text(), "another ");
1769 assert_eq!(
1770 range.bounding_boxes(),
1771 vec![Rect {
1772 x0: 51.0,
1773 y0: 72.49999809265137,
1774 x1: 139.3783721923828,
1775 y1: 94.5
1776 }]
1777 );
1778 }
1779
1780 #[test]
1781 fn text_position_at_point() {
1782 let tree = main_multiline_tree(None);
1783 let state = tree.state();
1784 let node = state.node_by_id(NodeId(1)).unwrap();
1785
1786 {
1787 let pos = node.text_position_at_point(Point::new(8.0, 31.666664123535156));
1788 assert!(pos.is_document_start());
1789 }
1790
1791 {
1792 let pos = node.text_position_at_point(Point::new(12.0, 33.666664123535156));
1793 assert!(pos.is_document_start());
1794 }
1795
1796 {
1797 let pos = node.text_position_at_point(Point::new(16.0, 40.0));
1798 assert!(pos.is_document_start());
1799 }
1800
1801 {
1802 let pos = node.text_position_at_point(Point::new(144.0, 40.0));
1803 assert!(!pos.is_document_start());
1804 assert!(!pos.is_document_end());
1805 assert!(!pos.is_line_end());
1806 let mut range = pos.to_degenerate_range();
1807 range.set_end(pos.forward_to_character_end());
1808 assert_eq!(range.text(), "l");
1809 }
1810
1811 {
1812 let pos = node.text_position_at_point(Point::new(150.0, 40.0));
1813 assert!(!pos.is_document_start());
1814 assert!(!pos.is_document_end());
1815 assert!(!pos.is_line_end());
1816 let mut range = pos.to_degenerate_range();
1817 range.set_end(pos.forward_to_character_end());
1818 assert_eq!(range.text(), "l");
1819 }
1820
1821 {
1822 let pos = node.text_position_at_point(Point::new(291.0, 40.0));
1823 assert!(!pos.is_document_start());
1824 assert!(!pos.is_document_end());
1825 assert!(pos.is_line_end());
1826 let mut range = pos.to_degenerate_range();
1827 range.set_start(pos.backward_to_word_start());
1828 assert_eq!(range.text(), "wrap ");
1829 }
1830
1831 {
1832 let pos = node.text_position_at_point(Point::new(12.0, 50.0));
1833 assert!(!pos.is_document_start());
1834 assert!(pos.is_line_start());
1835 assert!(!pos.is_paragraph_start());
1836 let mut range = pos.to_degenerate_range();
1837 range.set_end(pos.forward_to_word_end());
1838 assert_eq!(range.text(), "to ");
1839 }
1840
1841 {
1842 let pos = node.text_position_at_point(Point::new(130.0, 50.0));
1843 assert!(!pos.is_document_start());
1844 assert!(!pos.is_document_end());
1845 assert!(pos.is_line_end());
1846 let mut range = pos.to_degenerate_range();
1847 range.set_start(pos.backward_to_word_start());
1848 assert_eq!(range.text(), "line.\n");
1849 }
1850
1851 {
1852 let pos = node.text_position_at_point(Point::new(12.0, 80.0));
1853 assert!(!pos.is_document_start());
1854 assert!(!pos.is_document_end());
1855 assert!(pos.is_line_end());
1856 let mut range = pos.to_degenerate_range();
1857 range.set_start(pos.backward_to_line_start());
1858 assert_eq!(range.text(), "\n");
1859 }
1860
1861 {
1862 let pos = node.text_position_at_point(Point::new(12.0, 120.0));
1863 assert!(pos.is_document_end());
1864 }
1865
1866 {
1867 let pos = node.text_position_at_point(Point::new(250.0, 122.0));
1868 assert!(pos.is_document_end());
1869 }
1870 }
1871
1872 #[test]
1873 fn to_global_usv_index() {
1874 let tree = main_multiline_tree(None);
1875 let state = tree.state();
1876 let node = state.node_by_id(NodeId(1)).unwrap();
1877
1878 {
1879 let range = node.document_range();
1880 assert_eq!(range.start().to_global_usv_index(), 0);
1881 assert_eq!(range.end().to_global_usv_index(), 97);
1882 }
1883
1884 {
1885 let range = node.document_range();
1886 let pos = range.start().forward_to_line_end();
1887 assert_eq!(pos.to_global_usv_index(), 38);
1888 let pos = range.start().forward_to_line_start();
1889 assert_eq!(pos.to_global_usv_index(), 38);
1890 let pos = pos.forward_to_character_start();
1891 assert_eq!(pos.to_global_usv_index(), 39);
1892 let pos = pos.forward_to_line_start();
1893 assert_eq!(pos.to_global_usv_index(), 55);
1894 }
1895 }
1896
1897 #[test]
1898 fn to_global_utf16_index() {
1899 let tree = main_multiline_tree(None);
1900 let state = tree.state();
1901 let node = state.node_by_id(NodeId(1)).unwrap();
1902
1903 {
1904 let range = node.document_range();
1905 assert_eq!(range.start().to_global_utf16_index(), 0);
1906 assert_eq!(range.end().to_global_utf16_index(), 99);
1907 }
1908
1909 {
1910 let range = node.document_range();
1911 let pos = range.start().forward_to_line_end();
1912 assert_eq!(pos.to_global_utf16_index(), 38);
1913 let pos = range.start().forward_to_line_start();
1914 assert_eq!(pos.to_global_utf16_index(), 38);
1915 let pos = pos.forward_to_character_start();
1916 assert_eq!(pos.to_global_utf16_index(), 39);
1917 let pos = pos.forward_to_line_start();
1918 assert_eq!(pos.to_global_utf16_index(), 55);
1919 }
1920 }
1921
1922 #[test]
1923 fn to_line_index() {
1924 let tree = main_multiline_tree(None);
1925 let state = tree.state();
1926 let node = state.node_by_id(NodeId(1)).unwrap();
1927
1928 {
1929 let range = node.document_range();
1930 assert_eq!(range.start().to_line_index(), 0);
1931 assert_eq!(range.end().to_line_index(), 5);
1932 }
1933
1934 {
1935 let range = node.document_range();
1936 let pos = range.start().forward_to_line_end();
1937 assert_eq!(pos.to_line_index(), 0);
1938 let pos = range.start().forward_to_line_start();
1939 assert_eq!(pos.to_line_index(), 1);
1940 let pos = pos.forward_to_character_start();
1941 assert_eq!(pos.to_line_index(), 1);
1942 assert_eq!(pos.forward_to_line_end().to_line_index(), 1);
1943 let pos = pos.forward_to_line_start();
1944 assert_eq!(pos.to_line_index(), 2);
1945 }
1946 }
1947
1948 #[test]
1949 fn line_range_from_index() {
1950 let tree = main_multiline_tree(None);
1951 let state = tree.state();
1952 let node = state.node_by_id(NodeId(1)).unwrap();
1953
1954 {
1955 let range = node.line_range_from_index(0).unwrap();
1956 assert_eq!(range.text(), "This paragraph is\u{a0}long enough to wrap ");
1957 }
1958
1959 {
1960 let range = node.line_range_from_index(1).unwrap();
1961 assert_eq!(range.text(), "to another line.\n");
1962 }
1963
1964 {
1965 let range = node.line_range_from_index(2).unwrap();
1966 assert_eq!(range.text(), "Another paragraph.\n");
1967 }
1968
1969 {
1970 let range = node.line_range_from_index(3).unwrap();
1971 assert_eq!(range.text(), "\n");
1972 }
1973
1974 {
1975 let range = node.line_range_from_index(4).unwrap();
1976 assert_eq!(range.text(), "Last non-blank line\u{1f44d}\u{1f3fb}\n");
1977 }
1978
1979 {
1980 let range = node.line_range_from_index(5).unwrap();
1981 assert_eq!(range.text(), "");
1982 }
1983
1984 assert!(node.line_range_from_index(6).is_none());
1985 }
1986
1987 #[test]
1988 fn text_position_from_global_usv_index() {
1989 let tree = main_multiline_tree(None);
1990 let state = tree.state();
1991 let node = state.node_by_id(NodeId(1)).unwrap();
1992
1993 {
1994 let pos = node.text_position_from_global_usv_index(0).unwrap();
1995 assert!(pos.is_document_start());
1996 }
1997
1998 {
1999 let pos = node.text_position_from_global_usv_index(17).unwrap();
2000 let mut range = pos.to_degenerate_range();
2001 range.set_end(pos.forward_to_character_end());
2002 assert_eq!(range.text(), "\u{a0}");
2003 }
2004
2005 {
2006 let pos = node.text_position_from_global_usv_index(18).unwrap();
2007 let mut range = pos.to_degenerate_range();
2008 range.set_end(pos.forward_to_character_end());
2009 assert_eq!(range.text(), "l");
2010 }
2011
2012 {
2013 let pos = node.text_position_from_global_usv_index(37).unwrap();
2014 let mut range = pos.to_degenerate_range();
2015 range.set_end(pos.forward_to_character_end());
2016 assert_eq!(range.text(), " ");
2017 }
2018
2019 {
2020 let pos = node.text_position_from_global_usv_index(38).unwrap();
2021 assert!(!pos.is_paragraph_start());
2022 assert!(pos.is_line_start());
2023 let mut range = pos.to_degenerate_range();
2024 range.set_end(pos.forward_to_character_end());
2025 assert_eq!(range.text(), "t");
2026 }
2027
2028 {
2029 let pos = node.text_position_from_global_usv_index(54).unwrap();
2030 let mut range = pos.to_degenerate_range();
2031 range.set_end(pos.forward_to_character_end());
2032 assert_eq!(range.text(), "\n");
2033 }
2034
2035 {
2036 let pos = node.text_position_from_global_usv_index(55).unwrap();
2037 assert!(pos.is_paragraph_start());
2038 assert!(pos.is_line_start());
2039 let mut range = pos.to_degenerate_range();
2040 range.set_end(pos.forward_to_character_end());
2041 assert_eq!(range.text(), "A");
2042 }
2043
2044 for i in 94..=95 {
2045 let pos = node.text_position_from_global_usv_index(i).unwrap();
2046 let mut range = pos.to_degenerate_range();
2047 range.set_end(pos.forward_to_character_end());
2048 assert_eq!(range.text(), "\u{1f44d}\u{1f3fb}");
2049 }
2050
2051 {
2052 let pos = node.text_position_from_global_usv_index(96).unwrap();
2053 let mut range = pos.to_degenerate_range();
2054 range.set_end(pos.forward_to_character_end());
2055 assert_eq!(range.text(), "\n");
2056 }
2057
2058 {
2059 let pos = node.text_position_from_global_usv_index(97).unwrap();
2060 assert!(pos.is_document_end());
2061 }
2062
2063 assert!(node.text_position_from_global_usv_index(98).is_none());
2064 }
2065
2066 #[test]
2067 fn text_position_from_global_utf16_index() {
2068 let tree = main_multiline_tree(None);
2069 let state = tree.state();
2070 let node = state.node_by_id(NodeId(1)).unwrap();
2071
2072 {
2073 let pos = node.text_position_from_global_utf16_index(0).unwrap();
2074 assert!(pos.is_document_start());
2075 }
2076
2077 {
2078 let pos = node.text_position_from_global_utf16_index(17).unwrap();
2079 let mut range = pos.to_degenerate_range();
2080 range.set_end(pos.forward_to_character_end());
2081 assert_eq!(range.text(), "\u{a0}");
2082 }
2083
2084 {
2085 let pos = node.text_position_from_global_utf16_index(18).unwrap();
2086 let mut range = pos.to_degenerate_range();
2087 range.set_end(pos.forward_to_character_end());
2088 assert_eq!(range.text(), "l");
2089 }
2090
2091 {
2092 let pos = node.text_position_from_global_utf16_index(37).unwrap();
2093 let mut range = pos.to_degenerate_range();
2094 range.set_end(pos.forward_to_character_end());
2095 assert_eq!(range.text(), " ");
2096 }
2097
2098 {
2099 let pos = node.text_position_from_global_utf16_index(38).unwrap();
2100 assert!(!pos.is_paragraph_start());
2101 assert!(pos.is_line_start());
2102 let mut range = pos.to_degenerate_range();
2103 range.set_end(pos.forward_to_character_end());
2104 assert_eq!(range.text(), "t");
2105 }
2106
2107 {
2108 let pos = node.text_position_from_global_utf16_index(54).unwrap();
2109 let mut range = pos.to_degenerate_range();
2110 range.set_end(pos.forward_to_character_end());
2111 assert_eq!(range.text(), "\n");
2112 }
2113
2114 {
2115 let pos = node.text_position_from_global_utf16_index(55).unwrap();
2116 assert!(pos.is_paragraph_start());
2117 assert!(pos.is_line_start());
2118 let mut range = pos.to_degenerate_range();
2119 range.set_end(pos.forward_to_character_end());
2120 assert_eq!(range.text(), "A");
2121 }
2122
2123 for i in 94..=97 {
2124 let pos = node.text_position_from_global_utf16_index(i).unwrap();
2125 let mut range = pos.to_degenerate_range();
2126 range.set_end(pos.forward_to_character_end());
2127 assert_eq!(range.text(), "\u{1f44d}\u{1f3fb}");
2128 }
2129
2130 {
2131 let pos = node.text_position_from_global_utf16_index(98).unwrap();
2132 let mut range = pos.to_degenerate_range();
2133 range.set_end(pos.forward_to_character_end());
2134 assert_eq!(range.text(), "\n");
2135 }
2136
2137 {
2138 let pos = node.text_position_from_global_utf16_index(99).unwrap();
2139 assert!(pos.is_document_end());
2140 }
2141
2142 assert!(node.text_position_from_global_utf16_index(100).is_none());
2143 }
2144
2145 #[test]
2146 fn multiline_selection_clamping() {
2147 let tree = main_multiline_tree(Some(multiline_past_end_selection()));
2148 let state = tree.state();
2149 let node = state.node_by_id(NodeId(1)).unwrap();
2150 let _ = node.text_selection().unwrap();
2151 }
2152}