Skip to main content

skia_safe/modules/paragraph/
text_style.rs

1use super::{FontArguments, FontFamilies, TextBaseline, TextShadow};
2use crate::{
3    Color, FontHinting, FontMetrics, FontStyle, Paint, Typeface, font,
4    interop::{self, AsStr, FromStrs, SetStr},
5    prelude::*,
6    scalar,
7    textlayout::{EMPTY_INDEX, EMPTY_RANGE, RangeExtensions},
8};
9use skia_bindings as sb;
10use std::{fmt, ops::Range};
11
12bitflags! {
13    /// Multiple decorations can be applied at once. Ex: Underline and overline is
14    /// (0x1 | 0x2)
15    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16    pub struct TextDecoration: u32 {
17        const NO_DECORATION = sb::skia_textlayout_TextDecoration::kNoDecoration as _;
18        const UNDERLINE = sb::skia_textlayout_TextDecoration::kUnderline as _;
19        const OVERLINE = sb::skia_textlayout_TextDecoration::kOverline as _;
20        const LINE_THROUGH = sb::skia_textlayout_TextDecoration::kLineThrough as _;
21    }
22}
23
24pub const ALL_TEXT_DECORATIONS: TextDecoration = TextDecoration::ALL;
25
26impl Default for TextDecoration {
27    fn default() -> Self {
28        TextDecoration::NO_DECORATION
29    }
30}
31
32impl TextDecoration {
33    pub const ALL: TextDecoration = TextDecoration::all();
34}
35
36pub use sb::skia_textlayout_TextDecorationStyle as TextDecorationStyle;
37#[test]
38fn text_decoration_style_naming() {
39    let _ = TextDecorationStyle::Solid;
40}
41
42pub use sb::skia_textlayout_TextDecorationMode as TextDecorationMode;
43#[test]
44fn text_decoration_mode_naming() {
45    let _ = TextDecorationMode::Gaps;
46}
47
48pub use sb::skia_textlayout_StyleType as StyleType;
49#[test]
50fn style_type_member_naming() {
51    let _ = StyleType::Foreground;
52    let _ = StyleType::LetterSpacing;
53}
54
55#[repr(C)]
56#[derive(Copy, Clone, PartialEq, Debug)]
57pub struct Decoration {
58    pub ty: TextDecoration,
59    pub mode: TextDecorationMode,
60    pub color: Color,
61    pub style: TextDecorationStyle,
62    pub thickness_multiplier: scalar,
63}
64
65impl Default for Decoration {
66    fn default() -> Self {
67        Self {
68            ty: TextDecoration::default(),
69            mode: TextDecorationMode::default(),
70            color: Color::TRANSPARENT,
71            style: TextDecorationStyle::default(),
72            thickness_multiplier: 1.0,
73        }
74    }
75}
76
77native_transmutable!(sb::skia_textlayout_Decoration, Decoration);
78
79/// Where to vertically align the placeholder relative to the surrounding text.
80#[repr(i32)]
81#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)]
82pub enum PlaceholderAlignment {
83    /// Match the baseline of the placeholder with the baseline.
84    #[default]
85    Baseline,
86
87    /// Align the bottom edge of the placeholder with the baseline such that the
88    /// placeholder sits on top of the baseline.
89    AboveBaseline,
90
91    /// Align the top edge of the placeholder with the baseline specified in
92    /// such that the placeholder hangs below the baseline.
93    BelowBaseline,
94
95    /// Align the top edge of the placeholder with the top edge of the font.
96    /// When the placeholder is very tall, the extra space will hang from
97    /// the top and extend through the bottom of the line.
98    Top,
99
100    /// Align the bottom edge of the placeholder with the top edge of the font.
101    /// When the placeholder is very tall, the extra space will rise from
102    /// the bottom and extend through the top of the line.
103    Bottom,
104
105    /// Align the middle of the placeholder with the middle of the text. When the
106    /// placeholder is very tall, the extra space will grow equally from
107    /// the top and bottom of the line.
108    Middle,
109}
110native_transmutable!(
111    sb::skia_textlayout_PlaceholderAlignment,
112    PlaceholderAlignment
113);
114
115pub type FontFeature = Handle<sb::skia_textlayout_FontFeature>;
116unsafe_send_sync!(FontFeature);
117
118impl NativeDrop for sb::skia_textlayout_FontFeature {
119    fn drop(&mut self) {
120        unsafe { sb::C_FontFeature_destruct(self) }
121    }
122}
123
124impl NativeClone for sb::skia_textlayout_FontFeature {
125    fn clone(&self) -> Self {
126        construct(|ts| unsafe { sb::C_FontFeature_CopyConstruct(ts, self) })
127    }
128}
129
130impl PartialEq for FontFeature {
131    fn eq(&self, other: &Self) -> bool {
132        self.name() == other.name() && self.value() == other.value()
133    }
134}
135
136impl fmt::Debug for FontFeature {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.debug_tuple("FontFeature")
139            .field(&self.name())
140            .field(&self.value())
141            .finish()
142    }
143}
144
145impl FontFeature {
146    pub fn name(&self) -> &str {
147        self.native().fName.as_str()
148    }
149
150    pub fn value(&self) -> i32 {
151        self.native().fValue
152    }
153}
154
155#[repr(C)]
156#[derive(Clone, Default, Debug)]
157pub struct PlaceholderStyle {
158    pub width: scalar,
159    pub height: scalar,
160    pub alignment: PlaceholderAlignment,
161    pub baseline: TextBaseline,
162    /// Distance from the top edge of the rect to the baseline position. This
163    /// baseline will be aligned against the alphabetic baseline of the surrounding
164    /// text.
165    ///
166    /// Positive values drop the baseline lower (positions the rect higher) and
167    /// small or negative values will cause the rect to be positioned underneath
168    /// the line. When baseline == height, the bottom edge of the rect will rest on
169    /// the alphabetic baseline.
170    pub baseline_offset: scalar,
171}
172
173native_transmutable!(sb::skia_textlayout_PlaceholderStyle, PlaceholderStyle);
174
175impl PartialEq for PlaceholderStyle {
176    fn eq(&self, other: &Self) -> bool {
177        unsafe { self.native().equals(other.native()) }
178    }
179}
180
181impl PlaceholderStyle {
182    pub fn new(
183        width: scalar,
184        height: scalar,
185        alignment: PlaceholderAlignment,
186        baseline: TextBaseline,
187        offset: scalar,
188    ) -> Self {
189        Self {
190            width,
191            height,
192            alignment,
193            baseline,
194            baseline_offset: offset,
195        }
196    }
197}
198
199pub type TextStyle = Handle<sb::skia_textlayout_TextStyle>;
200unsafe_send_sync!(TextStyle);
201
202impl NativeDrop for sb::skia_textlayout_TextStyle {
203    fn drop(&mut self) {
204        unsafe { sb::C_TextStyle_destruct(self) }
205    }
206}
207
208impl NativeClone for sb::skia_textlayout_TextStyle {
209    fn clone(&self) -> Self {
210        construct(|ts| unsafe { sb::C_TextStyle_CopyConstruct(ts, self) })
211    }
212}
213
214impl NativePartialEq for sb::skia_textlayout_TextStyle {
215    fn eq(&self, rhs: &Self) -> bool {
216        unsafe { self.equals(rhs) }
217    }
218}
219
220impl Default for TextStyle {
221    fn default() -> Self {
222        Self::new()
223    }
224}
225
226impl fmt::Debug for TextStyle {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        f.debug_struct("TextStyle")
229            .field("color", &self.color())
230            .field("has_foreground", &self.has_foreground())
231            .field("foreground", &self.foreground())
232            .field("has_background", &self.has_background())
233            .field("background", &self.background())
234            .field("decoration", &self.decoration())
235            .field("font_style", &self.font_style())
236            .field("shadows", &self.shadows())
237            .field("font_features", &self.font_features())
238            .field("font_size", &self.font_size())
239            .field("font_families", &self.font_families())
240            .field("baseline_shift", &self.baseline_shift())
241            .field("height", &self.height())
242            .field("height_override", &self.height_override())
243            .field("half_leading", &self.half_leading())
244            .field("letter_spacing", &self.letter_spacing())
245            .field("word_spacing", &self.word_spacing())
246            .field("typeface", &self.typeface())
247            .field("locale", &self.locale())
248            .field("text_baseline", &self.text_baseline())
249            .field("is_placeholder", &self.is_placeholder())
250            .field("font_edging", &self.font_edging())
251            .field("subpixel", &self.subpixel())
252            .field("font_hinting", &self.font_hinting())
253            .finish()
254    }
255}
256
257impl TextStyle {
258    pub fn new() -> Self {
259        TextStyle::construct(|ts| unsafe { sb::C_TextStyle_Construct(ts) })
260    }
261
262    #[deprecated(since = "0.51.0", note = "Use clone_for_placeholder")]
263    #[must_use]
264    pub fn to_placeholder(&self) -> Self {
265        self.clone_for_placeholder()
266    }
267
268    #[must_use]
269    pub fn clone_for_placeholder(&self) -> Self {
270        Self::construct(|ts| unsafe { sb::C_TextStyle_cloneForPlaceholder(self.native(), ts) })
271    }
272
273    pub fn equals(&self, other: &TextStyle) -> bool {
274        *self == *other
275    }
276
277    pub fn equals_by_fonts(&self, that: &TextStyle) -> bool {
278        unsafe { self.native().equalsByFonts(that.native()) }
279    }
280
281    pub fn match_one_attribute(&self, style_type: StyleType, other: &TextStyle) -> bool {
282        unsafe { self.native().matchOneAttribute(style_type, other.native()) }
283    }
284
285    pub fn color(&self) -> Color {
286        Color::from_native_c(self.native().fColor)
287    }
288
289    pub fn set_color(&mut self, color: impl Into<Color>) -> &mut Self {
290        self.native_mut().fColor = color.into().into_native();
291        self
292    }
293
294    pub fn has_foreground(&self) -> bool {
295        self.native().fHasForeground
296    }
297
298    pub fn foreground(&self) -> Paint {
299        Paint::construct(|p| unsafe { sb::C_TextStyle_getForeground(self.native(), p) })
300    }
301
302    pub fn set_foreground_paint(&mut self, paint: &Paint) -> &mut Self {
303        unsafe { sb::C_TextStyle_setForegroundPaint(self.native_mut(), paint.native()) };
304        self
305    }
306
307    #[deprecated(since = "0.64.0", note = "use set_foreground_paint()")]
308    pub fn set_foreground_color(&mut self, paint: &Paint) -> &mut Self {
309        self.set_foreground_paint(paint)
310    }
311
312    pub fn clear_foreground_color(&mut self) -> &mut Self {
313        self.native_mut().fHasForeground = false;
314        self
315    }
316
317    pub fn has_background(&self) -> bool {
318        self.native().fHasBackground
319    }
320
321    pub fn background(&self) -> Paint {
322        Paint::construct(|p| unsafe { sb::C_TextStyle_getBackground(self.native(), p) })
323    }
324
325    pub fn set_background_paint(&mut self, paint: &Paint) -> &mut Self {
326        unsafe { sb::C_TextStyle_setBackgroundPaint(self.native_mut(), paint.native()) };
327        self
328    }
329
330    #[deprecated(since = "0.64.0", note = "use set_background_paint()")]
331    pub fn set_background_color(&mut self, paint: &Paint) -> &mut Self {
332        self.set_background_paint(paint)
333    }
334
335    pub fn clear_background_color(&mut self) -> &mut Self {
336        self.native_mut().fHasBackground = false;
337        self
338    }
339
340    pub fn decoration(&self) -> &Decoration {
341        Decoration::from_native_ref(&self.native().fDecoration)
342    }
343
344    pub fn decoration_type(&self) -> TextDecoration {
345        self.decoration().ty
346    }
347
348    pub fn decoration_mode(&self) -> TextDecorationMode {
349        self.decoration().mode
350    }
351
352    pub fn decoration_color(&self) -> Color {
353        self.decoration().color
354    }
355
356    pub fn decoration_style(&self) -> TextDecorationStyle {
357        self.decoration().style
358    }
359
360    pub fn decoration_thickness_multiplier(&self) -> scalar {
361        self.decoration().thickness_multiplier
362    }
363
364    pub fn set_decoration(&mut self, decoration: &Decoration) {
365        *self.decoration_mut_internal() = *decoration;
366    }
367
368    pub fn set_decoration_type(&mut self, decoration: TextDecoration) {
369        self.decoration_mut_internal().ty = decoration;
370    }
371
372    pub fn set_decoration_mode(&mut self, mode: TextDecorationMode) {
373        self.decoration_mut_internal().mode = mode;
374    }
375
376    pub fn set_decoration_style(&mut self, style: TextDecorationStyle) {
377        self.decoration_mut_internal().style = style;
378    }
379
380    pub fn set_decoration_color(&mut self, color: impl Into<Color>) {
381        self.decoration_mut_internal().color = color.into();
382    }
383
384    pub fn set_decoration_thickness_multiplier(&mut self, multiplier: scalar) {
385        self.decoration_mut_internal().thickness_multiplier = multiplier;
386    }
387
388    #[deprecated(since = "0.63.1", note = "use set_decoration()")]
389    pub fn decoration_mut(&mut self) -> &mut Decoration {
390        self.decoration_mut_internal()
391    }
392
393    fn decoration_mut_internal(&mut self) -> &mut Decoration {
394        Decoration::from_native_ref_mut(&mut self.native_mut().fDecoration)
395    }
396
397    pub fn font_style(&self) -> FontStyle {
398        FontStyle::from_native_c(self.native().fFontStyle)
399    }
400
401    pub fn set_font_style(&mut self, font_style: FontStyle) -> &mut Self {
402        self.native_mut().fFontStyle = font_style.into_native();
403        self
404    }
405
406    pub fn shadows(&self) -> &[TextShadow] {
407        unsafe {
408            let mut count = 0;
409            let ptr = sb::C_TextStyle_getShadows(&self.native().fTextShadows, &mut count);
410            safer::from_raw_parts(TextShadow::from_native_ptr(ptr), count)
411        }
412    }
413
414    pub fn add_shadow(&mut self, shadow: TextShadow) -> &mut Self {
415        unsafe { sb::C_TextStyle_addShadow(self.native_mut(), shadow.native()) }
416        self
417    }
418
419    pub fn reset_shadows(&mut self) -> &mut Self {
420        unsafe { sb::C_TextStyle_resetShadows(self.native_mut()) }
421        self
422    }
423
424    pub fn font_features(&self) -> &[FontFeature] {
425        unsafe {
426            let mut count = 0;
427            let ptr = sb::C_TextStyle_getFontFeatures(&self.native().fFontFeatures, &mut count);
428            safer::from_raw_parts(FontFeature::from_native_ptr(ptr), count)
429        }
430    }
431
432    pub fn add_font_feature(&mut self, font_feature: impl AsRef<str>, value: i32) {
433        let font_feature = interop::String::from_str(font_feature);
434        unsafe { sb::C_TextStyle_addFontFeature(self.native_mut(), font_feature.native(), value) }
435    }
436
437    pub fn reset_font_features(&mut self) {
438        unsafe { sb::C_TextStyle_resetFontFeatures(self.native_mut()) }
439    }
440
441    pub fn font_arguments(&self) -> Option<&FontArguments> {
442        unsafe { sb::C_TextStyle_getFontArguments(self.native()) }
443            .into_non_null()
444            .map(|ptr| FontArguments::from_native_ref(unsafe { ptr.as_ref() }))
445    }
446
447    /// The contents of the [`crate::FontArguments`] will be copied into the [`TextStyle`].
448    pub fn set_font_arguments<'fa>(
449        &mut self,
450        arguments: impl Into<Option<&'fa crate::FontArguments<'fa, 'fa>>>,
451    ) {
452        unsafe {
453            sb::C_TextStyle_setFontArguments(
454                self.native_mut(),
455                arguments.into().native_ptr_or_null(),
456            )
457        }
458    }
459
460    pub fn font_size(&self) -> scalar {
461        self.native().fFontSize
462    }
463
464    pub fn set_font_size(&mut self, size: scalar) -> &mut Self {
465        self.native_mut().fFontSize = size;
466        self
467    }
468
469    pub fn font_families(&self) -> FontFamilies {
470        unsafe {
471            let mut count = 0;
472            let ptr = sb::C_TextStyle_getFontFamilies(self.native(), &mut count);
473            FontFamilies(safer::from_raw_parts(ptr, count))
474        }
475    }
476
477    pub fn set_font_families(&mut self, families: &[impl AsRef<str>]) -> &mut Self {
478        let families: Vec<interop::String> = FromStrs::from_strs(families);
479        let families = families.native();
480        unsafe {
481            sb::C_TextStyle_setFontFamilies(self.native_mut(), families.as_ptr(), families.len())
482        }
483        self
484    }
485
486    pub fn baseline_shift(&self) -> scalar {
487        self.native().fBaselineShift
488    }
489
490    pub fn set_baseline_shift(&mut self, baseline_shift: scalar) -> &mut Self {
491        self.native_mut().fBaselineShift = baseline_shift;
492        self
493    }
494
495    pub fn set_height(&mut self, height: scalar) -> &mut Self {
496        self.native_mut().fHeight = height;
497        self
498    }
499
500    pub fn height(&self) -> scalar {
501        let n = self.native();
502        if n.fHeightOverride { n.fHeight } else { 0.0 }
503    }
504
505    pub fn set_height_override(&mut self, height_override: bool) -> &mut Self {
506        self.native_mut().fHeightOverride = height_override;
507        self
508    }
509
510    pub fn height_override(&self) -> bool {
511        self.native().fHeightOverride
512    }
513
514    pub fn set_half_leading(&mut self, half_leading: bool) -> &mut Self {
515        self.native_mut().fHalfLeading = half_leading;
516        self
517    }
518
519    pub fn half_leading(&self) -> bool {
520        self.native().fHalfLeading
521    }
522
523    pub fn set_letter_spacing(&mut self, letter_spacing: scalar) -> &mut Self {
524        self.native_mut().fLetterSpacing = letter_spacing;
525        self
526    }
527
528    pub fn letter_spacing(&self) -> scalar {
529        self.native().fLetterSpacing
530    }
531
532    pub fn set_word_spacing(&mut self, word_spacing: scalar) -> &mut Self {
533        self.native_mut().fWordSpacing = word_spacing;
534        self
535    }
536
537    pub fn word_spacing(&self) -> scalar {
538        self.native().fWordSpacing
539    }
540
541    pub fn typeface(&self) -> Option<Typeface> {
542        Typeface::from_unshared_ptr(self.native().fTypeface.fPtr)
543    }
544
545    pub fn set_typeface(&mut self, typeface: impl Into<Option<Typeface>>) -> &mut Self {
546        unsafe {
547            sb::C_TextStyle_setTypeface(self.native_mut(), typeface.into().into_ptr_or_null())
548        }
549        self
550    }
551
552    pub fn locale(&self) -> &str {
553        self.native().fLocale.as_str()
554    }
555
556    pub fn set_locale(&mut self, locale: impl AsRef<str>) -> &mut Self {
557        self.native_mut().fLocale.set_str(locale);
558        self
559    }
560
561    pub fn text_baseline(&self) -> TextBaseline {
562        self.native().fTextBaseline
563    }
564
565    pub fn set_text_baseline(&mut self, baseline: TextBaseline) -> &mut Self {
566        self.native_mut().fTextBaseline = baseline;
567        self
568    }
569
570    pub fn font_metrics(&self) -> FontMetrics {
571        FontMetrics::construct(|fm| unsafe { self.native().getFontMetrics(fm) })
572    }
573
574    pub fn is_placeholder(&self) -> bool {
575        self.native().fIsPlaceholder
576    }
577
578    pub fn set_placeholder(&mut self) -> &mut Self {
579        self.native_mut().fIsPlaceholder = true;
580        self
581    }
582
583    pub fn set_font_edging(&mut self, edging: font::Edging) -> &mut Self {
584        self.native_mut().fEdging = edging;
585        self
586    }
587
588    pub fn font_edging(&self) -> font::Edging {
589        self.native().fEdging
590    }
591
592    pub fn set_subpixel(&mut self, subpixel: bool) -> &mut Self {
593        self.native_mut().fSubpixel = subpixel;
594        self
595    }
596
597    pub fn subpixel(&self) -> bool {
598        self.native().fSubpixel
599    }
600
601    pub fn set_font_hinting(&mut self, hinting: FontHinting) -> &mut Self {
602        self.native_mut().fHinting = hinting;
603        self
604    }
605
606    pub fn font_hinting(&self) -> FontHinting {
607        self.native().fHinting
608    }
609}
610
611pub type TextIndex = usize;
612pub type TextRange = Range<usize>;
613pub const EMPTY_TEXT: TextRange = EMPTY_RANGE;
614
615#[repr(C)]
616#[derive(Clone, PartialEq, Debug)]
617pub struct Block {
618    pub range: TextRange,
619    pub style: TextStyle,
620}
621
622native_transmutable!(sb::skia_textlayout_Block, Block);
623
624impl Default for Block {
625    fn default() -> Self {
626        Self {
627            range: EMPTY_RANGE,
628            style: Default::default(),
629        }
630    }
631}
632
633impl Block {
634    pub fn new(text_range: TextRange, style: TextStyle) -> Self {
635        Self {
636            range: text_range,
637            style,
638        }
639    }
640
641    pub fn add(&mut self, tail: TextRange) -> &mut Self {
642        debug_assert!(self.range.end == tail.start);
643        self.range = self.range.start..self.range.start + self.range.width() + tail.width();
644        self
645    }
646}
647
648pub type BlockIndex = usize;
649pub type BlockRange = Range<usize>;
650
651pub const EMPTY_BLOCK: usize = EMPTY_INDEX;
652pub const EMPTY_BLOCKS: Range<usize> = EMPTY_RANGE;
653
654#[repr(C)]
655#[derive(Clone, PartialEq, Debug)]
656pub struct Placeholder {
657    pub range: TextRange,
658    pub style: PlaceholderStyle,
659    pub text_style: TextStyle,
660    pub blocks_before: BlockRange,
661    pub text_before: TextRange,
662}
663
664native_transmutable!(sb::skia_textlayout_Placeholder, Placeholder);
665
666impl Default for Placeholder {
667    fn default() -> Self {
668        #[allow(clippy::reversed_empty_ranges)]
669        Self {
670            range: EMPTY_RANGE,
671            style: Default::default(),
672            text_style: Default::default(),
673            blocks_before: 0..0,
674            text_before: 0..0,
675        }
676    }
677}
678
679impl Placeholder {
680    pub fn new(
681        range: Range<usize>,
682        style: PlaceholderStyle,
683        text_style: TextStyle,
684        blocks_before: BlockRange,
685        text_before: TextRange,
686    ) -> Self {
687        Self {
688            range,
689            style,
690            text_style,
691            blocks_before,
692            text_before,
693        }
694    }
695}
696
697#[cfg(test)]
698mod tests {
699    use super::*;
700
701    #[test]
702    fn getting_setting_comparing_font_arguments() {
703        let mut ts = TextStyle::new();
704        let mut fa = crate::FontArguments::default();
705        fa.set_collection_index(100);
706        ts.set_font_arguments(&fa);
707        let tl_fa: FontArguments = fa.into();
708        let fa = ts.font_arguments().unwrap();
709        assert_eq!(tl_fa, *fa);
710        let default_fa: FontArguments = crate::FontArguments::default().into();
711        assert_ne!(default_fa, *fa);
712    }
713}