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