1use 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}