skia_safe/modules/paragraph/
text_style.rs

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