Skip to main content

gtk4/subclass/
accessible_text.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Traits intended for implementing the [`AccessibleText`] interface.
5
6use crate::{
7    AccessibleText, AccessibleTextGranularity, AccessibleTextRange, ffi, subclass::prelude::*,
8};
9use glib::object::Cast;
10use glib::{GString, translate::*};
11
12pub trait AccessibleTextImpl: WidgetImpl {
13    #[doc(alias = "get_attributes")]
14    fn attributes(&self, offset: u32) -> Vec<(AccessibleTextRange, GString, GString)> {
15        self.parent_attributes(offset)
16    }
17
18    #[doc(alias = "get_caret_position")]
19    fn caret_position(&self) -> u32 {
20        self.parent_caret_position()
21    }
22
23    #[doc(alias = "get_contents")]
24    fn contents(&self, start: u32, end: u32) -> Option<glib::Bytes> {
25        self.parent_contents(start, end)
26    }
27
28    #[doc(alias = "get_contents_at")]
29    fn contents_at(
30        &self,
31        offset: u32,
32        granularity: crate::AccessibleTextGranularity,
33    ) -> Option<(u32, u32, glib::Bytes)> {
34        self.parent_contents_at(offset, granularity)
35    }
36
37    #[doc(alias = "get_default_attributes")]
38    fn default_attributes(&self) -> Vec<(GString, GString)> {
39        self.parent_default_attributes()
40    }
41
42    #[cfg(feature = "v4_16")]
43    #[cfg_attr(docsrs, doc(cfg(feature = "v4_16")))]
44    #[doc(alias = "get_extents")]
45    fn extents(&self, start: u32, end: u32) -> Option<graphene::Rect> {
46        self.parent_extents(start, end)
47    }
48
49    #[cfg(feature = "v4_16")]
50    #[cfg_attr(docsrs, doc(cfg(feature = "v4_16")))]
51    #[doc(alias = "get_offset")]
52    fn offset(&self, point: &graphene::Point) -> Option<u32> {
53        self.parent_offset(point)
54    }
55
56    #[doc(alias = "get_selection")]
57    fn selection(&self) -> Vec<AccessibleTextRange> {
58        self.parent_selection()
59    }
60
61    #[cfg(feature = "v4_22")]
62    #[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
63    fn set_caret_position(&self, position: u32) -> bool {
64        self.parent_set_caret_position(position)
65    }
66
67    #[cfg(feature = "v4_22")]
68    #[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
69    fn set_selection(&self, selection: usize, range: AccessibleTextRange) -> bool {
70        self.parent_set_selection(selection, range)
71    }
72}
73
74pub trait AccessibleTextImplExt: AccessibleTextImpl {
75    fn parent_attributes(&self, offset: u32) -> Vec<(AccessibleTextRange, GString, GString)> {
76        unsafe {
77            let type_data = Self::type_data();
78            let parent_iface = type_data.as_ref().parent_interface::<AccessibleText>()
79                as *const ffi::GtkAccessibleTextInterface;
80
81            let func = (*parent_iface)
82                .get_attributes
83                .expect("no parent \"get_attributes\" implementation");
84
85            let mut n_ranges = std::mem::MaybeUninit::uninit();
86            let mut ranges = std::ptr::null_mut();
87            let mut attribute_names = std::ptr::null_mut();
88            let mut attribute_values = std::ptr::null_mut();
89
90            let is_set: bool = from_glib(func(
91                self.obj()
92                    .unsafe_cast_ref::<AccessibleText>()
93                    .to_glib_none()
94                    .0,
95                offset,
96                n_ranges.as_mut_ptr(),
97                &mut ranges,
98                &mut attribute_names,
99                &mut attribute_values,
100            ));
101
102            if !is_set
103                || n_ranges.assume_init() == 0
104                || ranges.is_null()
105                || attribute_names.is_null()
106                || attribute_values.is_null()
107            {
108                Vec::new()
109            } else {
110                let mut names = glib::StrV::from_glib_full(attribute_names).into_iter();
111                let mut values = glib::StrV::from_glib_full(attribute_values).into_iter();
112
113                glib::Slice::from_glib_container_num(ranges, n_ranges.assume_init())
114                    .into_iter()
115                    .flat_map(|range| match (names.next(), values.next()) {
116                        (Some(name), Some(value)) => Some((range, name, value)),
117                        _ => None,
118                    })
119                    .collect()
120            }
121        }
122    }
123
124    fn parent_caret_position(&self) -> u32 {
125        unsafe {
126            let type_data = Self::type_data();
127            let parent_iface = type_data.as_ref().parent_interface::<AccessibleText>()
128                as *const ffi::GtkAccessibleTextInterface;
129
130            let func = (*parent_iface)
131                .get_caret_position
132                .expect("no parent \"get_caret_position\" implementation");
133
134            func(
135                self.obj()
136                    .unsafe_cast_ref::<AccessibleText>()
137                    .to_glib_none()
138                    .0,
139            )
140        }
141    }
142
143    fn parent_contents(&self, start: u32, end: u32) -> Option<glib::Bytes> {
144        unsafe {
145            let type_data = Self::type_data();
146            let parent_iface = type_data.as_ref().parent_interface::<AccessibleText>()
147                as *const ffi::GtkAccessibleTextInterface;
148
149            let func = (*parent_iface).get_contents?;
150
151            from_glib_full(func(
152                self.obj()
153                    .unsafe_cast_ref::<AccessibleText>()
154                    .to_glib_none()
155                    .0,
156                start,
157                end,
158            ))
159        }
160    }
161
162    fn parent_contents_at(
163        &self,
164        offset: u32,
165        granularity: crate::AccessibleTextGranularity,
166    ) -> Option<(u32, u32, glib::Bytes)> {
167        unsafe {
168            let type_data = Self::type_data();
169            let parent_iface = type_data.as_ref().parent_interface::<AccessibleText>()
170                as *const ffi::GtkAccessibleTextInterface;
171
172            let func = (*parent_iface).get_contents_at?;
173
174            let mut start = std::mem::MaybeUninit::uninit();
175            let mut end = std::mem::MaybeUninit::uninit();
176
177            let bytes = func(
178                self.obj()
179                    .unsafe_cast_ref::<AccessibleText>()
180                    .to_glib_none()
181                    .0,
182                offset,
183                granularity.into_glib(),
184                start.as_mut_ptr(),
185                end.as_mut_ptr(),
186            );
187
188            if !bytes.is_null() {
189                Some((
190                    start.assume_init(),
191                    end.assume_init(),
192                    from_glib_full(bytes),
193                ))
194            } else {
195                None
196            }
197        }
198    }
199
200    fn parent_default_attributes(&self) -> Vec<(GString, GString)> {
201        unsafe {
202            let type_data = Self::type_data();
203            let parent_iface = type_data.as_ref().parent_interface::<AccessibleText>()
204                as *const ffi::GtkAccessibleTextInterface;
205
206            let func = (*parent_iface)
207                .get_default_attributes
208                .expect("no parent \"get_default_attributes\" implementation");
209
210            let mut attribute_names = std::ptr::null_mut();
211            let mut attribute_values = std::ptr::null_mut();
212
213            func(
214                self.obj()
215                    .unsafe_cast_ref::<AccessibleText>()
216                    .to_glib_none()
217                    .0,
218                &mut attribute_names,
219                &mut attribute_values,
220            );
221
222            if attribute_names.is_null() || attribute_values.is_null() {
223                Vec::new()
224            } else {
225                glib::StrV::from_glib_full(attribute_names)
226                    .into_iter()
227                    .zip(glib::StrV::from_glib_full(attribute_values))
228                    .collect()
229            }
230        }
231    }
232
233    #[cfg(feature = "v4_16")]
234    #[cfg_attr(docsrs, doc(cfg(feature = "v4_16")))]
235    fn parent_extents(&self, start: u32, end: u32) -> Option<graphene::Rect> {
236        unsafe {
237            let type_data = Self::type_data();
238            let parent_iface = type_data.as_ref().parent_interface::<AccessibleText>()
239                as *const ffi::GtkAccessibleTextInterface;
240
241            let func = (*parent_iface)
242                .get_extents
243                .expect("no parent \"get_extents\" implementation");
244
245            let mut extents = std::mem::MaybeUninit::uninit();
246
247            let filled = from_glib(func(
248                self.obj()
249                    .unsafe_cast_ref::<AccessibleText>()
250                    .to_glib_none()
251                    .0,
252                start,
253                end,
254                extents.as_mut_ptr(),
255            ));
256
257            if filled {
258                Some(graphene::Rect::unsafe_from(extents.assume_init()))
259            } else {
260                None
261            }
262        }
263    }
264
265    #[cfg(feature = "v4_16")]
266    #[cfg_attr(docsrs, doc(cfg(feature = "v4_16")))]
267    fn parent_offset(&self, point: &graphene::Point) -> Option<u32> {
268        unsafe {
269            let type_data = Self::type_data();
270            let parent_iface = type_data.as_ref().parent_interface::<AccessibleText>()
271                as *const ffi::GtkAccessibleTextInterface;
272
273            let func = (*parent_iface)
274                .get_offset
275                .expect("no parent \"get_offset\" implementation");
276
277            let mut offset = std::mem::MaybeUninit::uninit();
278
279            let offset_set = from_glib(func(
280                self.obj()
281                    .unsafe_cast_ref::<AccessibleText>()
282                    .to_glib_none()
283                    .0,
284                point.to_glib_none().0,
285                offset.as_mut_ptr(),
286            ));
287
288            if offset_set {
289                Some(offset.assume_init())
290            } else {
291                None
292            }
293        }
294    }
295
296    fn parent_selection(&self) -> Vec<AccessibleTextRange> {
297        unsafe {
298            let type_data = Self::type_data();
299            let parent_iface = type_data.as_ref().parent_interface::<AccessibleText>()
300                as *const ffi::GtkAccessibleTextInterface;
301
302            let func = (*parent_iface)
303                .get_selection
304                .expect("no parent \"get_selection\" implementation");
305
306            let mut n_ranges = std::mem::MaybeUninit::uninit();
307            let mut ranges = std::ptr::null_mut();
308
309            let valid = from_glib(func(
310                self.obj()
311                    .unsafe_cast_ref::<AccessibleText>()
312                    .to_glib_none()
313                    .0,
314                n_ranges.as_mut_ptr(),
315                &mut ranges,
316            ));
317
318            if valid {
319                let n = n_ranges.assume_init();
320                AccessibleTextRange::from_glib_container_num_as_vec(ranges, n)
321            } else {
322                Vec::new()
323            }
324        }
325    }
326
327    #[cfg(feature = "v4_22")]
328    #[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
329    fn parent_set_caret_position(&self, position: u32) -> bool {
330        unsafe {
331            let type_data = Self::type_data();
332            let parent_iface = type_data.as_ref().parent_interface::<AccessibleText>()
333                as *const ffi::GtkAccessibleTextInterface;
334
335            let func = (*parent_iface)
336                .set_caret_position
337                .expect("no parent \"set_caret_position\" implementation");
338
339            from_glib(func(
340                self.obj()
341                    .unsafe_cast_ref::<AccessibleText>()
342                    .to_glib_none()
343                    .0,
344                position,
345            ))
346        }
347    }
348
349    #[cfg(feature = "v4_22")]
350    #[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
351    fn parent_set_selection(&self, selection: usize, range: AccessibleTextRange) -> bool {
352        unsafe {
353            let type_data = Self::type_data();
354            let parent_iface = type_data.as_ref().parent_interface::<AccessibleText>()
355                as *const ffi::GtkAccessibleTextInterface;
356
357            let func = (*parent_iface)
358                .set_selection
359                .expect("no parent \"set_selection\" implementation");
360
361            from_glib(func(
362                self.obj()
363                    .unsafe_cast_ref::<AccessibleText>()
364                    .to_glib_none()
365                    .0,
366                selection,
367                mut_override(range.to_glib_none().0),
368            ))
369        }
370    }
371}
372
373impl<T: AccessibleTextImpl> AccessibleTextImplExt for T {}
374
375unsafe impl<T: AccessibleTextImpl> IsImplementable<T> for AccessibleText {
376    fn interface_init(iface: &mut glib::Interface<Self>) {
377        let iface = iface.as_mut();
378
379        iface.get_contents = Some(accessible_text_get_contents::<T>);
380        iface.get_contents_at = Some(accessible_text_get_contents_at::<T>);
381        iface.get_caret_position = Some(accessible_text_get_caret_position::<T>);
382        iface.get_selection = Some(accessible_text_get_selection::<T>);
383        iface.get_attributes = Some(accessible_text_get_attributes::<T>);
384        iface.get_default_attributes = Some(accessible_text_get_default_attributes::<T>);
385
386        #[cfg(feature = "v4_16")]
387        {
388            iface.get_extents = Some(accessible_text_get_extents::<T>);
389            iface.get_offset = Some(accessible_text_get_offset::<T>);
390        }
391
392        #[cfg(feature = "v4_22")]
393        {
394            iface.set_caret_position = Some(accessible_text_set_caret_position::<T>);
395            iface.set_selection = Some(accessible_text_set_selection::<T>);
396        }
397    }
398}
399
400unsafe extern "C" fn accessible_text_get_contents<T: AccessibleTextImpl>(
401    accessible_text: *mut ffi::GtkAccessibleText,
402    start: u32,
403    end: u32,
404) -> *mut glib::ffi::GBytes {
405    unsafe {
406        let instance = &*(accessible_text as *mut T::Instance);
407        let imp = instance.imp();
408
409        let contents = imp.contents(start, end);
410        contents.into_glib_ptr()
411    }
412}
413
414unsafe extern "C" fn accessible_text_get_contents_at<T: AccessibleTextImpl>(
415    accessible_text: *mut ffi::GtkAccessibleText,
416    offset: libc::c_uint,
417    granularity: ffi::GtkAccessibleTextGranularity,
418    start: *mut libc::c_uint,
419    end: *mut libc::c_uint,
420) -> *mut glib::ffi::GBytes {
421    unsafe {
422        let instance = &*(accessible_text as *mut T::Instance);
423        let imp = instance.imp();
424
425        match imp.contents_at(offset, AccessibleTextGranularity::from_glib(granularity)) {
426            Some((r_start, r_end, bytes)) => {
427                if !start.is_null() {
428                    *start = r_start;
429                }
430                if !end.is_null() {
431                    *end = r_end;
432                }
433
434                bytes.into_glib_ptr()
435            }
436            _ => std::ptr::null_mut(),
437        }
438    }
439}
440
441unsafe extern "C" fn accessible_text_get_caret_position<T: AccessibleTextImpl>(
442    accessible_text: *mut ffi::GtkAccessibleText,
443) -> u32 {
444    unsafe {
445        let instance = &*(accessible_text as *mut T::Instance);
446        let imp = instance.imp();
447
448        imp.caret_position()
449    }
450}
451
452unsafe extern "C" fn accessible_text_get_selection<T: AccessibleTextImpl>(
453    accessible_text: *mut ffi::GtkAccessibleText,
454    n_ranges: *mut libc::size_t,
455    ranges: *mut *mut ffi::GtkAccessibleTextRange,
456) -> glib::ffi::gboolean {
457    unsafe {
458        let instance = &*(accessible_text as *mut T::Instance);
459        let imp = instance.imp();
460
461        let r_ranges = imp.selection();
462        let n: usize = r_ranges.len();
463        *n_ranges = n;
464
465        if n == 0 {
466            false
467        } else {
468            *ranges = r_ranges.to_glib_container().0;
469
470            true
471        }
472        .into_glib()
473    }
474}
475
476unsafe extern "C" fn accessible_text_get_attributes<T: AccessibleTextImpl>(
477    accessible_text: *mut ffi::GtkAccessibleText,
478    offset: u32,
479    n_ranges: *mut libc::size_t,
480    ranges: *mut *mut ffi::GtkAccessibleTextRange,
481    attribute_names: *mut *mut *mut libc::c_char,
482    attribute_values: *mut *mut *mut libc::c_char,
483) -> glib::ffi::gboolean {
484    unsafe {
485        let instance = &*(accessible_text as *mut T::Instance);
486        let imp = instance.imp();
487
488        let attrs = imp.attributes(offset);
489        let n: usize = attrs.len();
490        *n_ranges = n;
491
492        if n == 0 {
493            *attribute_names = std::ptr::null_mut();
494            *attribute_values = std::ptr::null_mut();
495
496            false
497        } else {
498            let mut c_ranges = glib::Slice::with_capacity(attrs.len());
499            let mut c_names = glib::StrV::with_capacity(attrs.len());
500            let mut c_values = glib::StrV::with_capacity(attrs.len());
501
502            for (range, name, value) in attrs {
503                c_ranges.push(range);
504                c_names.push(name);
505                c_values.push(value);
506            }
507
508            *ranges = c_ranges.to_glib_container().0;
509            *attribute_names = c_names.into_glib_ptr();
510            *attribute_values = c_values.into_glib_ptr();
511
512            true
513        }
514        .into_glib()
515    }
516}
517
518unsafe extern "C" fn accessible_text_get_default_attributes<T: AccessibleTextImpl>(
519    accessible_text: *mut ffi::GtkAccessibleText,
520    attribute_names: *mut *mut *mut libc::c_char,
521    attribute_values: *mut *mut *mut libc::c_char,
522) {
523    unsafe {
524        let instance = &*(accessible_text as *mut T::Instance);
525        let imp = instance.imp();
526
527        let attrs = imp.default_attributes();
528
529        if attrs.is_empty() {
530            *attribute_names = std::ptr::null_mut();
531            *attribute_values = std::ptr::null_mut();
532        } else {
533            let mut c_names = glib::StrV::with_capacity(attrs.len());
534            let mut c_values = glib::StrV::with_capacity(attrs.len());
535
536            for (name, value) in attrs {
537                c_names.push(name);
538                c_values.push(value);
539            }
540
541            *attribute_names = c_names.into_glib_ptr();
542            *attribute_values = c_values.into_glib_ptr();
543        }
544    }
545}
546
547#[cfg(feature = "v4_16")]
548#[cfg_attr(docsrs, doc(cfg(feature = "v4_16")))]
549unsafe extern "C" fn accessible_text_get_extents<T: AccessibleTextImpl>(
550    accessible_text: *mut ffi::GtkAccessibleText,
551    start: u32,
552    end: u32,
553    extents: *mut graphene::ffi::graphene_rect_t,
554) -> glib::ffi::gboolean {
555    unsafe {
556        let instance = &*(accessible_text as *mut T::Instance);
557        let imp = instance.imp();
558
559        let rect = imp.extents(start, end);
560
561        if let Some(rect) = rect {
562            *extents = *rect.as_ptr();
563
564            true
565        } else {
566            false
567        }
568        .into_glib()
569    }
570}
571
572#[cfg(feature = "v4_16")]
573unsafe extern "C" fn accessible_text_get_offset<T: AccessibleTextImpl>(
574    accessible_text: *mut ffi::GtkAccessibleText,
575    point: *const graphene::ffi::graphene_point_t,
576    offset: *mut libc::c_uint,
577) -> glib::ffi::gboolean {
578    unsafe {
579        let instance = &*(accessible_text as *mut T::Instance);
580        let imp = instance.imp();
581
582        let pos = imp.offset(&from_glib_borrow(point));
583
584        if let Some(pos) = pos {
585            if !offset.is_null() {
586                *offset = pos;
587            }
588            true
589        } else {
590            false
591        }
592        .into_glib()
593    }
594}
595
596#[cfg(feature = "v4_22")]
597unsafe extern "C" fn accessible_text_set_caret_position<T: AccessibleTextImpl>(
598    accessible_text: *mut ffi::GtkAccessibleText,
599    position: u32,
600) -> glib::ffi::gboolean {
601    unsafe {
602        let instance = &*(accessible_text as *mut T::Instance);
603        let imp = instance.imp();
604
605        imp.set_caret_position(position).into_glib()
606    }
607}
608
609#[cfg(feature = "v4_22")]
610unsafe extern "C" fn accessible_text_set_selection<T: AccessibleTextImpl>(
611    accessible_text: *mut ffi::GtkAccessibleText,
612    selection: usize,
613    range: *mut ffi::GtkAccessibleTextRange,
614) -> glib::ffi::gboolean {
615    unsafe {
616        let instance = &*(accessible_text as *mut T::Instance);
617        let imp = instance.imp();
618
619        imp.set_selection(selection, from_glib_none(range))
620            .into_glib()
621    }
622}
623
624#[cfg(test)]
625mod test {
626    use crate as gtk4;
627    use crate::prelude::*;
628    use crate::subclass::prelude::*;
629
630    mod imp {
631        use super::*;
632
633        #[derive(Default)]
634        pub struct TestTextView {}
635
636        #[glib::object_subclass]
637        impl ObjectSubclass for TestTextView {
638            const NAME: &'static str = "TestTextView";
639            type Type = super::TestTextView;
640            type ParentType = crate::TextView;
641            type Interfaces = (crate::AccessibleText,);
642        }
643
644        impl ObjectImpl for TestTextView {}
645        impl WidgetImpl for TestTextView {}
646        impl AccessibleTextImpl for TestTextView {
647            fn attributes(
648                &self,
649                offset: u32,
650            ) -> Vec<(
651                crate::accessible_text_range::AccessibleTextRange,
652                glib::GString,
653                glib::GString,
654            )> {
655                self.parent_attributes(offset)
656            }
657
658            fn caret_position(&self) -> u32 {
659                self.parent_caret_position()
660            }
661
662            fn contents(&self, start: u32, end: u32) -> Option<glib::Bytes> {
663                self.parent_contents(start, end)
664            }
665
666            fn contents_at(
667                &self,
668                offset: u32,
669                granularity: crate::AccessibleTextGranularity,
670            ) -> Option<(u32, u32, glib::Bytes)> {
671                self.parent_contents_at(offset, granularity)
672            }
673
674            fn default_attributes(&self) -> Vec<(glib::GString, glib::GString)> {
675                self.parent_default_attributes()
676            }
677
678            fn selection(&self) -> Vec<crate::accessible_text_range::AccessibleTextRange> {
679                self.parent_selection()
680            }
681
682            #[cfg(feature = "v4_16")]
683            fn extents(&self, start: u32, end: u32) -> Option<graphene::Rect> {
684                self.parent_extents(start, end)
685            }
686
687            #[cfg(feature = "v4_16")]
688            fn offset(&self, point: &graphene::Point) -> Option<u32> {
689                self.parent_offset(point)
690            }
691        }
692
693        impl TextViewImpl for TestTextView {}
694        impl TestTextView {}
695    }
696
697    glib::wrapper! {
698        pub struct TestTextView(ObjectSubclass<imp::TestTextView>)
699        @extends crate::Widget, crate::TextView,
700        @implements crate::Accessible, crate::AccessibleText, crate::Buildable, crate::ConstraintTarget, crate::Scrollable;
701    }
702
703    impl TestTextView {}
704
705    #[crate::test]
706    fn test_accessible_text_iface() {
707        let text: TestTextView = glib::Object::new();
708        let mut iter = text.buffer().iter_at_offset(0);
709        text.buffer()
710            .insert_markup(&mut iter, "<b>Lorem Ipsum</b> dolor <i>sit.</i> amnet");
711
712        let (range, _, value) = text
713            .imp()
714            .attributes(0)
715            .into_iter()
716            .find(|(_, name, _)| name == "weight")
717            .unwrap();
718
719        assert_eq!(range.start(), 0);
720        assert_eq!(range.length(), "Lorem Ipsum".len());
721        assert_eq!(value, "700");
722
723        assert_eq!(
724            text.imp().caret_position(),
725            "Lorem Ipsum dolor sit. amnet".len() as u32
726        );
727        let pos = "Lorem Ipsum ".len();
728        let iter = text.buffer().iter_at_offset(pos as i32);
729        text.buffer().place_cursor(&iter);
730        assert_eq!(text.imp().caret_position(), pos as u32);
731
732        assert_eq!(
733            std::str::from_utf8(
734                &text
735                    .imp()
736                    .contents_at(pos as u32, crate::AccessibleTextGranularity::Character)
737                    .unwrap()
738                    .2
739            )
740            .unwrap(),
741            "d"
742        );
743        assert_eq!(
744            std::str::from_utf8(
745                &text
746                    .imp()
747                    .contents_at(pos as u32, crate::AccessibleTextGranularity::Word)
748                    .unwrap()
749                    .2
750            )
751            .unwrap(),
752            "dolor "
753        );
754        assert_eq!(
755            std::str::from_utf8(
756                &text
757                    .imp()
758                    .contents_at(pos as u32, crate::AccessibleTextGranularity::Line)
759                    .unwrap()
760                    .2
761            )
762            .unwrap(),
763            "Lorem Ipsum dolor sit. amnet"
764        );
765
766        assert_eq!(
767            "Lorem Ipsum\0",
768            std::str::from_utf8(&text.imp().contents(0, 11).unwrap()).unwrap()
769        );
770
771        assert!(
772            text.imp()
773                .default_attributes()
774                .iter()
775                .any(|(name, value)| name == "editable" && value == "true")
776        );
777        text.buffer().select_range(
778            &text.buffer().iter_at_offset(0),
779            &text.buffer().iter_at_offset(10),
780        );
781        let selected_range = text.imp().selection()[0];
782        assert_eq!(selected_range.start(), 0);
783        assert_eq!(selected_range.length(), 10);
784
785        #[cfg(feature = "v4_16")]
786        {
787            let _extents = text.imp().extents(0, 20);
788            let _offset = text.imp().offset(&graphene::Point::new(10.0, 10.0));
789        }
790    }
791}