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