pango/
glyph_item_iter.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{marker::PhantomData, mem};
4
5use glib::{prelude::*, translate::*, GStr, GString};
6
7use crate::{ffi, GlyphItem};
8
9/// A [`GlyphItemIter`][crate::GlyphItemIter] is an iterator over the clusters in a
10/// [`GlyphItem`][crate::GlyphItem].
11///
12/// The *forward direction* of the iterator is the logical direction of text.
13/// That is, with increasing @start_index and @start_char values. If @glyph_item
14/// is right-to-left (that is, if `glyph_item->item->analysis.level` is odd),
15/// then @start_glyph decreases as the iterator moves forward.  Moreover,
16/// in right-to-left cases, @start_glyph is greater than @end_glyph.
17///
18/// An iterator should be initialized using either
19/// pango_glyph_item_iter_init_start() or
20/// pango_glyph_item_iter_init_end(), for forward and backward iteration
21/// respectively, and walked over using any desired mixture of
22/// pango_glyph_item_iter_next_cluster() and
23/// pango_glyph_item_iter_prev_cluster().
24///
25/// A common idiom for doing a forward iteration over the clusters is:
26///
27/// ```text
28/// PangoGlyphItemIter cluster_iter;
29/// gboolean have_cluster;
30///
31/// for (have_cluster = pango_glyph_item_iter_init_start (&cluster_iter,
32///                                                       glyph_item, text);
33///      have_cluster;
34///      have_cluster = pango_glyph_item_iter_next_cluster (&cluster_iter))
35/// {
36///   ...
37/// }
38/// ```
39///
40/// Note that @text is the start of the text for layout, which is then
41/// indexed by `glyph_item->item->offset` to get to the text of @glyph_item.
42/// The @start_index and @end_index values can directly index into @text. The
43/// @start_glyph, @end_glyph, @start_char, and @end_char values however are
44/// zero-based for the @glyph_item.  For each cluster, the item pointed at by
45/// the start variables is included in the cluster while the one pointed at by
46/// end variables is not.
47///
48/// None of the members of a [`GlyphItemIter`][crate::GlyphItemIter] should be modified manually.
49#[derive(Clone, Debug)]
50pub struct GlyphItemIter<'item> {
51    inner: ffi::PangoGlyphItemIter,
52    text: GString,
53    item: PhantomData<&'item GlyphItem>,
54}
55
56impl StaticType for GlyphItemIter<'_> {
57    #[inline]
58    fn static_type() -> glib::Type {
59        unsafe { from_glib(ffi::pango_glyph_item_iter_get_type()) }
60    }
61}
62
63impl<'item> GlyphItemIter<'item> {
64    #[doc(alias = "pango_glyph_item_iter_init_start")]
65    pub fn new_start(glyph_item: &'item GlyphItem, text: &str) -> Result<Self, glib::BoolError> {
66        unsafe {
67            let mut iter = mem::MaybeUninit::zeroed();
68            let text = GString::from(text);
69            let res: bool = from_glib(ffi::pango_glyph_item_iter_init_start(
70                iter.as_mut_ptr(),
71                mut_override(glyph_item.to_glib_none().0),
72                text.as_ptr(),
73            ));
74
75            if res {
76                Ok(Self {
77                    inner: iter.assume_init(),
78                    text,
79                    item: PhantomData,
80                })
81            } else {
82                Err(glib::bool_error!("Failed to create glyph item iter"))
83            }
84        }
85    }
86
87    #[doc(alias = "pango_glyph_item_iter_init_end")]
88    pub fn new_end(glyph_item: &'item GlyphItem, text: &str) -> Result<Self, glib::BoolError> {
89        unsafe {
90            let mut iter = mem::MaybeUninit::zeroed();
91            let text = GString::from(text);
92            let res: bool = from_glib(ffi::pango_glyph_item_iter_init_end(
93                iter.as_mut_ptr(),
94                mut_override(glyph_item.to_glib_none().0),
95                text.as_ptr(),
96            ));
97
98            if res {
99                Ok(Self {
100                    inner: iter.assume_init(),
101                    text,
102                    item: PhantomData,
103                })
104            } else {
105                Err(glib::bool_error!("Failed to create glyph item iter"))
106            }
107        }
108    }
109
110    #[doc(alias = "pango_glyph_item_iter_next_cluster")]
111    pub fn next_cluster(&mut self) -> bool {
112        unsafe {
113            from_glib(ffi::pango_glyph_item_iter_next_cluster(
114                self.to_glib_none_mut().0,
115            ))
116        }
117    }
118
119    #[doc(alias = "pango_glyph_item_iter_prev_cluster")]
120    pub fn prev_cluster(&mut self) -> bool {
121        unsafe {
122            from_glib(ffi::pango_glyph_item_iter_prev_cluster(
123                self.to_glib_none_mut().0,
124            ))
125        }
126    }
127
128    #[inline]
129    pub fn glyph_item(&self) -> &'item GlyphItem {
130        unsafe { &*(&self.inner.glyph_item as *const _ as *const GlyphItem) }
131    }
132    #[inline]
133    pub fn text(&self) -> &GStr {
134        self.text.as_gstr()
135    }
136    #[inline]
137    pub fn start_glyph(&self) -> i32 {
138        self.inner.start_glyph
139    }
140    #[inline]
141    pub fn start_index(&self) -> i32 {
142        self.inner.start_index
143    }
144    #[inline]
145    pub fn start_char(&self) -> i32 {
146        self.inner.start_char
147    }
148    #[inline]
149    pub fn end_glyph(&self) -> i32 {
150        self.inner.end_glyph
151    }
152    #[inline]
153    pub fn end_index(&self) -> i32 {
154        self.inner.end_index
155    }
156    #[inline]
157    pub fn end_char(&self) -> i32 {
158        self.inner.end_char
159    }
160}
161
162impl<'item> IntoIterator for GlyphItemIter<'item> {
163    type Item = (i32, i32, i32, i32, i32, i32);
164    type IntoIter = GlyphItemIntoIter<'item>;
165    #[inline]
166    fn into_iter(self) -> Self::IntoIter {
167        GlyphItemIntoIter(Some(self))
168    }
169}
170
171#[derive(Clone, Debug)]
172#[repr(transparent)]
173pub struct GlyphItemIntoIter<'item>(Option<GlyphItemIter<'item>>);
174
175impl Iterator for GlyphItemIntoIter<'_> {
176    type Item = (i32, i32, i32, i32, i32, i32);
177    fn next(&mut self) -> Option<Self::Item> {
178        if let Some(iter) = &mut self.0 {
179            let values = (
180                iter.start_glyph(),
181                iter.start_index(),
182                iter.start_char(),
183                iter.end_glyph(),
184                iter.end_index(),
185                iter.end_char(),
186            );
187            if !iter.next_cluster() {
188                self.0 = None;
189            }
190            Some(values)
191        } else {
192            None
193        }
194    }
195}
196
197impl std::iter::FusedIterator for GlyphItemIntoIter<'_> {}
198
199#[doc(hidden)]
200impl<'a, 'item> ToGlibPtr<'a, *const ffi::PangoGlyphItemIter> for GlyphItemIter<'item>
201where
202    'item: 'a,
203{
204    type Storage = PhantomData<&'a Self>;
205    #[inline]
206    fn to_glib_none(&'a self) -> Stash<'a, *const ffi::PangoGlyphItemIter, Self> {
207        Stash(&self.inner, PhantomData)
208    }
209}
210
211#[doc(hidden)]
212impl<'a, 'item> ToGlibPtrMut<'a, *mut ffi::PangoGlyphItemIter> for GlyphItemIter<'item>
213where
214    'item: 'a,
215{
216    type Storage = PhantomData<&'a mut Self>;
217    #[inline]
218    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::PangoGlyphItemIter, Self> {
219        StashMut(&mut self.inner, PhantomData)
220    }
221}