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