pango/layout.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use glib::translate::*;
4
5use crate::{LayoutLine, LayoutRun, ffi};
6
7// rustdoc-stripper-ignore-next
8/// The result of [`LayoutLine::x_to_index`].
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub struct HitPosition {
11 index: i32,
12 trailing: i32,
13 is_inside: bool,
14}
15
16impl HitPosition {
17 // rustdoc-stripper-ignore-next
18 /// The UTF-8 byte offset of the grapheme closest to the position.
19 ///
20 /// This position is relative to the start of the [`Layout`]'s text.
21 ///
22 /// [`Layout`]: crate::Layout
23 pub fn index(self) -> i32 {
24 self.index
25 }
26
27 // rustdoc-stripper-ignore-next
28 /// The codepoint within the grapheme of the position.
29 ///
30 /// This will always be either `0`, or the number of `char`s (*not bytes!*)
31 /// in the grapheme. This represents whether the user clicked near the start
32 /// of the grapheme or near the end; this is important for things like
33 /// resolving cursor positions.
34 pub fn trailing(self) -> i32 {
35 self.trailing
36 }
37
38 // rustdoc-stripper-ignore-next
39 /// Whether or not the position was within the bounds of the line.
40 ///
41 /// If this is `false`, then `index` and `trailing` will always resolve
42 /// to either the very first or the very last position in the line; this
43 /// behaviour is dependent on the line's resolved writing direction.
44 pub fn is_inside(self) -> bool {
45 self.is_inside
46 }
47}
48
49impl LayoutLine {
50 // rustdoc-stripper-ignore-next
51 /// The byte index of the start of this line into the text used to create
52 /// the source [`Layout`].
53 ///
54 /// [`Layout`]: crate::Layout
55 // rustdoc-stripper-ignore-next-stop
56 /// Returns the start index of the line, as byte index
57 /// into the text of the layout.
58 ///
59 /// # Returns
60 ///
61 /// the start index of the line
62 #[cfg(not(feature = "v1_50"))]
63 #[cfg_attr(docsrs, doc(cfg(not(feature = "v1_50"))))]
64 pub fn start_index(&self) -> i32 {
65 unsafe { (*self.as_ptr()).start_index }
66 }
67
68 // rustdoc-stripper-ignore-next
69 /// The length of this line's text, in bytes.
70 // rustdoc-stripper-ignore-next-stop
71 /// Returns the length of the line, in bytes.
72 ///
73 /// # Returns
74 ///
75 /// the length of the line
76 #[cfg(not(feature = "v1_50"))]
77 #[cfg_attr(docsrs, doc(cfg(not(feature = "v1_50"))))]
78 pub fn length(&self) -> i32 {
79 unsafe { (*self.as_ptr()).length }
80 }
81
82 #[doc(alias = "pango_layout_line_runs")]
83 pub fn runs(&self) -> Vec<LayoutRun> {
84 unsafe { FromGlibPtrContainer::from_glib_none((*self.as_ptr()).runs) }
85 }
86 /// Converts from x offset to the byte index of the corresponding character
87 /// within the text of the layout.
88 ///
89 /// If @x_pos is outside the line, @index_ and @trailing will point to the very
90 /// first or very last position in the line. This determination is based on the
91 /// resolved direction of the paragraph; for example, if the resolved direction
92 /// is right-to-left, then an X position to the right of the line (after it)
93 /// results in 0 being stored in @index_ and @trailing. An X position to the
94 /// left of the line results in @index_ pointing to the (logical) last grapheme
95 /// in the line and @trailing being set to the number of characters in that
96 /// grapheme. The reverse is true for a left-to-right line.
97 /// ## `x_pos`
98 /// the X offset (in Pango units) from the left edge of the line.
99 ///
100 /// # Returns
101 ///
102 /// [`false`] if @x_pos was outside the line, [`true`] if inside
103 ///
104 /// ## `index_`
105 /// location to store calculated byte index for the grapheme
106 /// in which the user clicked
107 ///
108 /// ## `trailing`
109 /// location to store an integer indicating where in the
110 /// grapheme the user clicked. It will either be zero, or the number of
111 /// characters in the grapheme. 0 represents the leading edge of the grapheme.
112 #[doc(alias = "pango_layout_line_x_to_index")]
113 pub fn x_to_index(&self, x_pos: i32) -> HitPosition {
114 let mut index = 0;
115 let mut trailing = 0;
116
117 let is_inside = unsafe {
118 from_glib(ffi::pango_layout_line_x_to_index(
119 self.to_glib_none().0,
120 x_pos,
121 &mut index,
122 &mut trailing,
123 ))
124 };
125
126 HitPosition {
127 index,
128 trailing,
129 is_inside,
130 }
131 }
132
133 /// Gets a list of visual ranges corresponding to a given logical range.
134 ///
135 /// This list is not necessarily minimal - there may be consecutive
136 /// ranges which are adjacent. The ranges will be sorted from left to
137 /// right. The ranges are with respect to the left edge of the entire
138 /// layout, not with respect to the line.
139 /// ## `start_index`
140 /// Start byte index of the logical range. If this value
141 /// is less than the start index for the line, then the first range
142 /// will extend all the way to the leading edge of the layout. Otherwise,
143 /// it will start at the leading edge of the first character.
144 /// ## `end_index`
145 /// Ending byte index of the logical range. If this value is
146 /// greater than the end index for the line, then the last range will
147 /// extend all the way to the trailing edge of the layout. Otherwise,
148 /// it will end at the trailing edge of the last character.
149 ///
150 /// # Returns
151 ///
152 ///
153 /// ## `ranges`
154 /// location to
155 /// store a pointer to an array of ranges. The array will be of length
156 /// `2*n_ranges`, with each range starting at `(*ranges)[2*n]` and of
157 /// width `(*ranges)[2*n + 1] - (*ranges)[2*n]`. This array must be freed
158 /// with g_free(). The coordinates are relative to the layout and are in
159 /// Pango units.
160 #[doc(alias = "pango_layout_line_get_x_ranges")]
161 #[doc(alias = "get_x_ranges")]
162 pub fn x_ranges(&self, start_index: i32, end_index: i32) -> Vec<i32> {
163 unsafe {
164 let mut ranges = std::ptr::null_mut();
165 let mut n_ranges = std::mem::MaybeUninit::uninit();
166 ffi::pango_layout_line_get_x_ranges(
167 self.to_glib_none().0,
168 start_index,
169 end_index,
170 &mut ranges,
171 n_ranges.as_mut_ptr(),
172 );
173 FromGlibContainer::from_glib_full_num(ranges, 2 * n_ranges.assume_init() as usize)
174 }
175 }
176}