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