1use std::{marker::PhantomData, mem, ptr};
4
5use glib::{SList, translate::*};
6
7use crate::{AttrType, Attribute, FontDescription, Language, ffi};
8
9#[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 unsafe {
178 Self {
179 ptr: ptr::NonNull::new_unchecked(ptr),
180 list: PhantomData,
181 }
182 }
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 #[test]
189 fn attr_iterator() {
190 let default_lang = crate::Language::default();
191 let attributes = crate::AttrList::new();
192 attributes.insert(crate::AttrColor::new_foreground(0x2000, 0x2000, 0x2000));
193 attributes.insert(crate::AttrSize::new(10 * crate::SCALE));
194 attributes.insert(crate::AttrLanguage::new(&default_lang));
195 let iter = attributes.iterator();
196 {
197 let mut iter = iter.clone();
198 loop {
199 let (desc, lang, attrs) = iter.font();
200 if !attrs.is_empty() {
201 assert_eq!(desc.size(), 10 * crate::SCALE);
202 assert_eq!(lang.map(|l| l.to_string()), Some(default_lang.to_string()));
203 }
204 for attr in attrs {
205 attr.downcast_ref::<crate::AttrColor>().unwrap();
206 }
207 if !iter.next_style_change() {
208 break;
209 }
210 }
211 }
212 let mut max = 0;
213 for (i, mut attrs) in iter.into_iter().enumerate() {
214 if i == 0 {
215 attrs
216 .pop_front()
217 .unwrap()
218 .downcast_ref::<crate::AttrColor>()
219 .unwrap();
220 attrs
221 .pop_front()
222 .unwrap()
223 .downcast_ref::<crate::AttrSize>()
224 .unwrap();
225 attrs
226 .pop_front()
227 .unwrap()
228 .downcast_ref::<crate::AttrLanguage>()
229 .unwrap();
230 assert!(attrs.is_empty());
231 }
232 max = i + 1;
233 }
234 assert!(max > 0);
236 }
237}