1use 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 assert!(max > 0);
227 }
228}