pango/
attr_iterator.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{marker::PhantomData, mem, ptr};
4
5use glib::{translate::*, SList};
6
7use crate::{ffi, AttrType, Attribute, FontDescription, Language};
8
9/// A [`AttrIterator`][crate::AttrIterator] is used to iterate through a [`AttrList`][crate::AttrList].
10///
11/// A new iterator is created with [`AttrList::iterator()`][crate::AttrList::iterator()].
12/// Once the iterator is created, it can be advanced through the style
13/// changes in the text using [`next_style_change()`][Self::next_style_change()]. At each
14/// style change, the range of the current style segment and the attributes
15/// currently in effect can be queried.
16#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct AttrIterator<'list> {
18    ptr: ptr::NonNull<ffi::PangoAttrIterator>,
19    list: PhantomData<&'list crate::AttrList>,
20}
21
22impl Clone for AttrIterator<'_> {
23    #[inline]
24    fn clone(&self) -> Self {
25        let ptr = unsafe {
26            ptr::NonNull::new_unchecked(ffi::pango_attr_iterator_copy(self.ptr.as_ptr()))
27        };
28        Self {
29            ptr,
30            list: PhantomData,
31        }
32    }
33}
34
35impl Drop for AttrIterator<'_> {
36    #[inline]
37    fn drop(&mut self) {
38        unsafe {
39            ffi::pango_attr_iterator_destroy(self.ptr.as_ptr());
40        }
41    }
42}
43
44#[cfg(feature = "v1_44")]
45#[cfg_attr(docsrs, doc(cfg(feature = "v1_44")))]
46impl glib::prelude::StaticType for AttrIterator<'_> {
47    #[inline]
48    fn static_type() -> glib::Type {
49        unsafe { from_glib(ffi::pango_attr_iterator_get_type()) }
50    }
51}
52
53impl AttrIterator<'_> {
54    #[doc(alias = "pango_attr_iterator_get")]
55    pub fn get(&self, type_: AttrType) -> Option<Attribute> {
56        unsafe {
57            from_glib_none(ffi::pango_attr_iterator_get(
58                mut_override(self.to_glib_none().0),
59                type_.into_glib(),
60            ))
61        }
62    }
63
64    #[doc(alias = "pango_attr_iterator_get_attrs")]
65    #[doc(alias = "get_attrs")]
66    pub fn attrs(&self) -> SList<Attribute> {
67        unsafe {
68            FromGlibPtrContainer::from_glib_full(ffi::pango_attr_iterator_get_attrs(mut_override(
69                self.to_glib_none().0,
70            )))
71        }
72    }
73
74    #[doc(alias = "pango_attr_iterator_next")]
75    pub fn next_style_change(&mut self) -> bool {
76        unsafe { from_glib(ffi::pango_attr_iterator_next(self.to_glib_none_mut().0)) }
77    }
78
79    #[doc(alias = "pango_attr_iterator_range")]
80    pub fn range(&self) -> (i32, i32) {
81        unsafe {
82            let mut start = mem::MaybeUninit::uninit();
83            let mut end = mem::MaybeUninit::uninit();
84            ffi::pango_attr_iterator_range(
85                mut_override(self.to_glib_none().0),
86                start.as_mut_ptr(),
87                end.as_mut_ptr(),
88            );
89            let start = start.assume_init();
90            let end = end.assume_init();
91            (start, end)
92        }
93    }
94    #[doc(alias = "pango_attr_iterator_get_font")]
95    #[doc(alias = "get_font")]
96    pub fn font(&self) -> (FontDescription, Option<Language>, SList<Attribute>) {
97        unsafe {
98            let desc = FontDescription::new();
99            let mut language = mem::MaybeUninit::uninit();
100            let mut extra_attrs = mem::MaybeUninit::uninit();
101
102            ffi::pango_attr_iterator_get_font(
103                mut_override(self.to_glib_none().0),
104                mut_override(desc.to_glib_none().0),
105                language.as_mut_ptr(),
106                extra_attrs.as_mut_ptr(),
107            );
108
109            (
110                desc,
111                from_glib_full(language.assume_init()),
112                FromGlibPtrContainer::from_glib_full(extra_attrs.assume_init()),
113            )
114        }
115    }
116}
117
118impl<'list> IntoIterator for AttrIterator<'list> {
119    type Item = SList<Attribute>;
120    type IntoIter = AttrIntoIter<'list>;
121    #[inline]
122    fn into_iter(self) -> Self::IntoIter {
123        AttrIntoIter(Some(self))
124    }
125}
126
127#[derive(Clone, Debug)]
128#[repr(transparent)]
129pub struct AttrIntoIter<'list>(Option<AttrIterator<'list>>);
130
131impl Iterator for AttrIntoIter<'_> {
132    type Item = SList<Attribute>;
133    #[inline]
134    fn next(&mut self) -> Option<Self::Item> {
135        if let Some(iter) = &mut self.0 {
136            let attrs = iter.attrs();
137            if !iter.next_style_change() {
138                self.0 = None;
139            }
140            Some(attrs)
141        } else {
142            None
143        }
144    }
145}
146
147impl std::iter::FusedIterator for AttrIntoIter<'_> {}
148
149#[doc(hidden)]
150impl<'a, 'list> ToGlibPtr<'a, *const ffi::PangoAttrIterator> for AttrIterator<'list>
151where
152    'list: 'a,
153{
154    type Storage = PhantomData<&'a Self>;
155    #[inline]
156    fn to_glib_none(&'a self) -> Stash<'a, *const ffi::PangoAttrIterator, Self> {
157        Stash(self.ptr.as_ptr() as *const _, PhantomData)
158    }
159}
160
161#[doc(hidden)]
162impl<'a, 'list> ToGlibPtrMut<'a, *mut ffi::PangoAttrIterator> for AttrIterator<'list>
163where
164    'list: 'a,
165{
166    type Storage = PhantomData<&'a mut Self>;
167    #[inline]
168    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::PangoAttrIterator, Self> {
169        StashMut(self.ptr.as_ptr(), PhantomData)
170    }
171}
172
173#[doc(hidden)]
174impl FromGlibPtrFull<*mut ffi::PangoAttrIterator> for AttrIterator<'_> {
175    #[inline]
176    unsafe fn from_glib_full(ptr: *mut ffi::PangoAttrIterator) -> Self {
177        Self {
178            ptr: ptr::NonNull::new_unchecked(ptr),
179            list: PhantomData,
180        }
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    #[test]
187    fn attr_iterator() {
188        let default_lang = crate::Language::default();
189        let attributes = crate::AttrList::new();
190        attributes.insert(crate::AttrColor::new_foreground(0x2000, 0x2000, 0x2000));
191        attributes.insert(crate::AttrSize::new(10 * crate::SCALE));
192        attributes.insert(crate::AttrLanguage::new(&default_lang));
193        let iter = attributes.iterator();
194        {
195            let mut iter = iter.clone();
196            loop {
197                let (desc, lang, attrs) = iter.font();
198                if !attrs.is_empty() {
199                    assert_eq!(desc.size(), 10 * crate::SCALE);
200                    assert_eq!(lang.map(|l| l.to_string()), Some(default_lang.to_string()));
201                }
202                for attr in attrs {
203                    attr.downcast_ref::<crate::AttrColor>().unwrap();
204                }
205                if !iter.next_style_change() {
206                    break;
207                }
208            }
209        }
210        let mut max = 0;
211        for (i, mut attrs) in iter.into_iter().enumerate() {
212            if i == 0 {
213                attrs
214                    .pop_front()
215                    .unwrap()
216                    .downcast_ref::<crate::AttrColor>()
217                    .unwrap();
218                attrs
219                    .pop_front()
220                    .unwrap()
221                    .downcast_ref::<crate::AttrSize>()
222                    .unwrap();
223                attrs
224                    .pop_front()
225                    .unwrap()
226                    .downcast_ref::<crate::AttrLanguage>()
227                    .unwrap();
228                assert!(attrs.is_empty());
229            }
230            max = i + 1;
231        }
232        // ensure the list was iterated
233        assert!(max > 0);
234    }
235}