accesskit_consumer/
text.rs

1// Copyright 2022 The AccessKit Authors. All rights reserved.
2// Licensed under the Apache License, Version 2.0 (found in
3// the LICENSE-APACHE file) or the MIT license (found in
4// the LICENSE-MIT file), at your option.
5
6use 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        // TODO: support variable text formatting (part of rich text)
206        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        // TODO: support variable text formatting (part of rich text)
359        self.document_end()
360    }
361
362    pub fn forward_to_format_end(&self) -> Self {
363        // TODO: support variable text formatting (part of rich text)
364        self.document_end()
365    }
366
367    pub fn backward_to_format_start(&self) -> Self {
368        // TODO: support variable text formatting (part of rich text)
369        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        // If the range is degenerate, we don't want to normalize it.
551        // This is important e.g. when getting the bounding rectangle
552        // of the caret range when the caret is at the end of a wrapped line.
553        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    /// Returns the range's transformed bounding boxes relative to the tree's
624    /// container (e.g. window).
625    ///
626    /// If the return value is empty, it means that the source tree doesn't
627    /// provide enough information to calculate bounding boxes. Otherwise,
628    /// there will always be at least one box, even if it's zero-width,
629    /// as it is for a degenerate range.
630    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                    // Note: The following directions assume that the rectangle,
693                    // in the node's coordinate space, is y-down. TBD: Will we
694                    // ever encounter a case where this isn't true?
695                    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        // We use `>=` here because if the two endpoints are equivalent
743        // but with a different bias, we want to normalize the bias.
744        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        // We use `>=` here because if the two endpoints are equivalent
754        // but with a different bias, we want to normalize the bias.
755        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    // We know the node has a bounding rectangle because it was returned
833    // by a hit test.
834    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            // Note: The following directions assume that the rectangle,
859            // in the node's coordinate space, is y-down. TBD: Will we
860            // ever encounter a case where this isn't true?
861            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    /// Returns the nearest text position to the given point
1042    /// in this node's coordinate space.
1043    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        // The following tests can assume that the point is not within
1059        // any text run.
1060
1061        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                        // Note: The following directions assume that the rectangle,
1084                        // in the root node's coordinate space, is y-down. TBD: Will we
1085                        // ever encounter a case where this isn't true?
1086                        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    // This is based on an actual tree produced by egui.
1226    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                    // The non-breaking space in the following text
1268                    // is in an arbitrary spot; its only purpose
1269                    // is to test conversion between UTF-8 and UTF-16
1270                    // indices.
1271                    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                    // Use an arbitrary emoji consisting of two code points
1369                    // (combining characters), each of which encodes to two
1370                    // UTF-16 code units, to fully test conversion between
1371                    // UTF-8, UTF-16, and AccessKit character indices.
1372                    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}