Skip to main content

skia_safe/modules/paragraph/
paragraph_style.rs

1use std::fmt;
2
3use skia_bindings as sb;
4
5use super::{FontFamilies, TextAlign, TextDirection, TextStyle};
6use crate::{
7    FontStyle,
8    interop::{self, AsStr, FromStrs, SetStr},
9    modules::paragraph::TextHeightBehavior,
10    prelude::*,
11    scalar,
12};
13
14pub type StrutStyle = Handle<sb::skia_textlayout_StrutStyle>;
15unsafe_send_sync!(StrutStyle);
16
17impl NativeDrop for sb::skia_textlayout_StrutStyle {
18    fn drop(&mut self) {
19        unsafe { sb::C_StrutStyle_destruct(self) }
20    }
21}
22
23impl NativeClone for sb::skia_textlayout_StrutStyle {
24    fn clone(&self) -> Self {
25        construct(|ss| unsafe { sb::C_StrutStyle_CopyConstruct(ss, self) })
26    }
27}
28
29impl NativePartialEq for sb::skia_textlayout_StrutStyle {
30    fn eq(&self, rhs: &Self) -> bool {
31        unsafe { sb::C_StrutStyle_equals(self, rhs) }
32    }
33}
34
35impl Default for StrutStyle {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41impl fmt::Debug for StrutStyle {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        f.debug_struct("StrutStyle")
44            .field("font_families", &self.font_families())
45            .field("font_style", &self.font_style())
46            .field("font_size", &self.font_size())
47            .field("height", &self.height())
48            .field("leading", &self.leading())
49            .field("strut_enabled", &self.strut_enabled())
50            .field("force_strut_height", &self.force_strut_height())
51            .field("height_override", &self.height_override())
52            .field("half_leading", &self.half_leading())
53            .finish()
54    }
55}
56
57impl StrutStyle {
58    pub fn new() -> Self {
59        StrutStyle::construct(|ss| unsafe { sb::C_StrutStyle_Construct(ss) })
60    }
61
62    pub fn font_families(&self) -> FontFamilies {
63        unsafe {
64            let mut count = 0;
65            let ptr = sb::C_StrutStyle_getFontFamilies(self.native(), &mut count);
66            FontFamilies(safer::from_raw_parts(ptr, count))
67        }
68    }
69
70    pub fn set_font_families(&mut self, families: &[impl AsRef<str>]) -> &mut Self {
71        let families: Vec<interop::String> = FromStrs::from_strs(families);
72        let families = families.native();
73        unsafe {
74            sb::C_StrutStyle_setFontFamilies(self.native_mut(), families.as_ptr(), families.len());
75        }
76        self
77    }
78
79    pub fn font_style(&self) -> FontStyle {
80        FontStyle::from_native_c(self.native().fFontStyle)
81    }
82
83    pub fn set_font_style(&mut self, font_style: FontStyle) -> &mut Self {
84        self.native_mut().fFontStyle = font_style.into_native();
85        self
86    }
87
88    pub fn font_size(&self) -> scalar {
89        self.native().fFontSize
90    }
91
92    pub fn set_font_size(&mut self, font_size: scalar) -> &mut Self {
93        self.native_mut().fFontSize = font_size;
94        self
95    }
96
97    pub fn set_height(&mut self, height: scalar) -> &mut Self {
98        self.native_mut().fHeight = height;
99        self
100    }
101
102    pub fn height(&self) -> scalar {
103        self.native().fHeight
104    }
105
106    pub fn set_leading(&mut self, leading: scalar) -> &mut Self {
107        self.native_mut().fLeading = leading;
108        self
109    }
110
111    pub fn leading(&self) -> scalar {
112        self.native().fLeading
113    }
114
115    pub fn strut_enabled(&self) -> bool {
116        self.native().fEnabled
117    }
118
119    pub fn set_strut_enabled(&mut self, enabled: bool) -> &mut Self {
120        self.native_mut().fEnabled = enabled;
121        self
122    }
123
124    pub fn force_strut_height(&self) -> bool {
125        self.native().fForceHeight
126    }
127
128    pub fn set_force_strut_height(&mut self, force_height: bool) -> &mut Self {
129        self.native_mut().fForceHeight = force_height;
130        self
131    }
132
133    pub fn height_override(&self) -> bool {
134        self.native().fHeightOverride
135    }
136
137    pub fn set_height_override(&mut self, height_override: bool) -> &mut Self {
138        self.native_mut().fHeightOverride = height_override;
139        self
140    }
141
142    pub fn half_leading(&self) -> bool {
143        self.native().fHalfLeading
144    }
145
146    pub fn set_half_leading(&mut self, half_leading: bool) -> &mut Self {
147        self.native_mut().fHalfLeading = half_leading;
148        self
149    }
150}
151
152// Can't use `Handle<>` here, `std::u16string` maintains an interior pointer.
153pub type ParagraphStyle = RefHandle<sb::skia_textlayout_ParagraphStyle>;
154unsafe_send_sync!(ParagraphStyle);
155
156impl NativeDrop for sb::skia_textlayout_ParagraphStyle {
157    fn drop(&mut self) {
158        unsafe { sb::C_ParagraphStyle_delete(self) }
159    }
160}
161
162impl Clone for ParagraphStyle {
163    fn clone(&self) -> Self {
164        Self::from_ptr(unsafe { sb::C_ParagraphStyle_newCopy(self.native()) }).unwrap()
165    }
166}
167
168impl NativePartialEq for sb::skia_textlayout_ParagraphStyle {
169    fn eq(&self, rhs: &Self) -> bool {
170        unsafe { sb::C_ParagraphStyle_Equals(self, rhs) }
171    }
172}
173
174impl Default for ParagraphStyle {
175    fn default() -> Self {
176        Self::new()
177    }
178}
179
180impl fmt::Debug for ParagraphStyle {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        f.debug_struct("ParagraphStyle")
183            .field("strut_style", &self.strut_style())
184            .field("text_style", &self.text_style())
185            .field("text_direction", &self.text_direction())
186            .field("text_align", &self.text_align())
187            .field("max_lines", &self.max_lines())
188            .field("ellipsis", &self.ellipsis())
189            .field("height", &self.height())
190            .field("text_height_behavior", &self.text_height_behavior())
191            .field("unlimited_lines", &self.unlimited_lines())
192            .field("ellipsized", &self.ellipsized())
193            .field("effective_align", &self.effective_align())
194            .field("hinting_is_on", &self.hinting_is_on())
195            .field("replace_tab_characters", &self.replace_tab_characters())
196            .field("fake_missing_font_styles", &self.fake_missing_font_styles())
197            .field(
198                "letter_spacing_by_css_spec",
199                &self.letter_spacing_by_css_spec(),
200            )
201            .field("render_soft_hyphens", &self.render_soft_hyphens())
202            .finish()
203    }
204}
205
206impl ParagraphStyle {
207    pub fn new() -> Self {
208        Self::from_ptr(unsafe { sb::C_ParagraphStyle_new() }).unwrap()
209    }
210
211    pub fn strut_style(&self) -> &StrutStyle {
212        StrutStyle::from_native_ref(&self.native().fStrutStyle)
213    }
214
215    pub fn set_strut_style(&mut self, strut_style: StrutStyle) -> &mut Self {
216        self.native_mut().fStrutStyle.replace_with(strut_style);
217        self
218    }
219
220    pub fn text_style(&self) -> &TextStyle {
221        TextStyle::from_native_ref(&self.native().fDefaultTextStyle)
222    }
223
224    pub fn set_text_style(&mut self, text_style: &TextStyle) -> &mut Self {
225        // TODO: implement the assignment operator in C.
226        self.native_mut()
227            .fDefaultTextStyle
228            .replace_with(text_style.clone());
229        self
230    }
231
232    pub fn text_direction(&self) -> TextDirection {
233        self.native().fTextDirection
234    }
235
236    pub fn set_text_direction(&mut self, direction: TextDirection) -> &mut Self {
237        self.native_mut().fTextDirection = direction;
238        self
239    }
240
241    pub fn text_align(&self) -> TextAlign {
242        self.native().fTextAlign
243    }
244
245    pub fn set_text_align(&mut self, align: TextAlign) -> &mut Self {
246        self.native_mut().fTextAlign = align;
247        self
248    }
249
250    pub fn max_lines(&self) -> Option<usize> {
251        match self.native().fLinesLimit {
252            std::usize::MAX => None,
253            lines => Some(lines),
254        }
255    }
256
257    pub fn set_max_lines(&mut self, lines: impl Into<Option<usize>>) -> &mut Self {
258        self.native_mut().fLinesLimit = lines.into().unwrap_or(usize::MAX);
259        self
260    }
261
262    // TODO: Support u16 ellipsis, but why? Doesn't SkString support UTF-8?
263
264    pub fn ellipsis(&self) -> &str {
265        self.native().fEllipsis.as_str()
266    }
267
268    pub fn set_ellipsis(&mut self, ellipsis: impl AsRef<str>) -> &mut Self {
269        self.native_mut().fEllipsis.set_str(ellipsis);
270        self
271    }
272
273    pub fn height(&self) -> scalar {
274        self.native().fHeight
275    }
276
277    pub fn set_height(&mut self, height: scalar) -> &mut Self {
278        self.native_mut().fHeight = height;
279        self
280    }
281
282    pub fn text_height_behavior(&self) -> TextHeightBehavior {
283        self.native().fTextHeightBehavior
284    }
285
286    pub fn set_text_height_behavior(&mut self, v: TextHeightBehavior) -> &mut Self {
287        self.native_mut().fTextHeightBehavior = v;
288        self
289    }
290
291    pub fn unlimited_lines(&self) -> bool {
292        self.max_lines().is_none()
293    }
294
295    pub fn ellipsized(&self) -> bool {
296        unsafe { sb::C_ParagraphStyle_ellipsized(self.native()) }
297    }
298
299    pub fn effective_align(&self) -> TextAlign {
300        unsafe { self.native().effective_align() }
301    }
302
303    pub fn hinting_is_on(&self) -> bool {
304        self.native().fHintingIsOn
305    }
306
307    pub fn turn_hinting_off(&mut self) -> &mut Self {
308        self.native_mut().fHintingIsOn = false;
309        self
310    }
311
312    pub fn fake_missing_font_styles(&self) -> bool {
313        self.native().fFakeMissingFontStyles
314    }
315
316    pub fn set_fake_missing_font_styles(&mut self, value: bool) -> &mut Self {
317        self.native_mut().fFakeMissingFontStyles = value;
318        self
319    }
320
321    pub fn replace_tab_characters(&self) -> bool {
322        self.native().fReplaceTabCharacters
323    }
324
325    pub fn set_replace_tab_characters(&mut self, value: bool) -> &mut Self {
326        self.native_mut().fReplaceTabCharacters = value;
327        self
328    }
329
330    pub fn apply_rounding_hack(&self) -> bool {
331        self.native().fApplyRoundingHack
332    }
333
334    pub fn set_apply_rounding_hack(&mut self, value: bool) -> &mut Self {
335        self.native_mut().fApplyRoundingHack = value;
336        self
337    }
338
339    pub fn letter_spacing_by_css_spec(&self) -> bool {
340        self.native().fLetterSpacingByCSSSpec
341    }
342
343    pub fn set_letter_spacing_by_css_spec(&mut self, value: bool) -> &mut Self {
344        self.native_mut().fLetterSpacingByCSSSpec = value;
345        self
346    }
347
348    pub fn render_soft_hyphens(&self) -> bool {
349        self.native().fRenderSoftHyphens
350    }
351
352    pub fn set_render_soft_hyphens(&mut self, value: bool) -> &mut Self {
353        self.native_mut().fRenderSoftHyphens = value;
354        self
355    }
356}
357
358#[cfg(test)]
359mod tests {
360    use super::ParagraphStyle;
361
362    // Regression test for https://github.com/rust-skia/rust-skia/issues/607
363    #[test]
364    fn paragraph_style_supports_equality() {
365        let a = ParagraphStyle::default();
366        let b = ParagraphStyle::default();
367        assert_eq!(a, b)
368    }
369}