waterui_text/
font.rs

1use core::fmt::Debug;
2
3use nami::{Computed, Signal, impl_constant};
4use waterui_core::{
5    Environment,
6    resolve::{self, AnyResolvable, Resolvable},
7};
8
9/// Font configuration for text rendering.
10///
11/// This struct defines all the visual properties that can be applied to text,
12/// including size, styling, and decorations.
13#[derive(Debug, Clone)]
14#[non_exhaustive]
15pub struct Font(AnyResolvable<ResolvedFont>);
16
17impl Default for Font {
18    fn default() -> Self {
19        Self::new(Body)
20    }
21}
22
23/// A resolved font with specific size and weight.
24#[derive(Debug, Clone)]
25#[non_exhaustive]
26pub struct ResolvedFont {
27    /// Font size in points.
28    pub size: f32,
29    /// Font weight.
30    pub weight: FontWeight,
31}
32
33impl ResolvedFont {
34    /// Creates a new resolved font with the given size and weight.
35    #[must_use]
36    pub const fn new(size: f32, weight: FontWeight) -> Self {
37        Self { size, weight }
38    }
39}
40
41/// Font weight enumeration.
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
43pub enum FontWeight {
44    /// Thin weight (100).
45    Thin,
46    /// Ultra-light weight (200).
47    UltraLight,
48    /// Light weight (300).
49    Light,
50    /// Normal weight (400).
51    #[default]
52    Normal,
53    /// Medium weight (500).
54    Medium,
55    /// Semi-bold weight (600).
56    SemiBold,
57    /// Bold weight (700).
58    Bold,
59    /// Ultra-bold weight (800).
60    UltraBold,
61    /// Black weight (900).
62    Black,
63}
64
65impl_constant!(Font, ResolvedFont, FontWeight);
66
67impl Font {
68    /// Creates a new font from a resolvable value.
69    pub fn new(font: impl Resolvable<Resolved = ResolvedFont> + 'static) -> Self {
70        Self(AnyResolvable::new(font))
71    }
72
73    /// Sets the font weight.
74    #[must_use]
75    pub fn weight(self, weight: FontWeight) -> Self {
76        Self::new(resolve::Map::new(self.0, move |font| ResolvedFont {
77            size: font.size,
78            weight,
79        }))
80    }
81
82    /// Sets the font size in points.
83    #[must_use]
84    pub fn size(self, size: f32) -> Self {
85        Self::new(resolve::Map::new(self.0, move |font| ResolvedFont {
86            size,
87            weight: font.weight,
88        }))
89    }
90
91    /// Sets the font to bold weight.
92    /// Equal to calling `font.weight(FontWeight::Bold)`.
93    #[must_use]
94    pub fn bold(self) -> Self {
95        self.weight(FontWeight::Bold)
96    }
97
98    /// Resolves the font in the given environment.
99    #[must_use]
100    pub fn resolve(&self, env: &Environment) -> Computed<ResolvedFont> {
101        self.0.resolve(env)
102    }
103}
104
105macro_rules! impl_font {
106    ($name:ident,$default_size:expr,$default_weight:expr, $doc:expr) => {
107        #[doc = $doc]
108        #[derive(Debug, Clone, Copy)]
109        pub struct $name;
110
111        impl Resolvable for $name {
112            type Resolved = ResolvedFont;
113            fn resolve(&self, env: &Environment) -> impl Signal<Output = Self::Resolved> {
114                env.query::<Self, Computed<Self::Resolved>>()
115                    .cloned()
116                    .unwrap_or_else(|| {
117                        Computed::constant(ResolvedFont::new($default_size, $default_weight))
118                    })
119            }
120        }
121
122        impl From<$name> for Font {
123            fn from(value: $name) -> Self {
124                Self::new(value)
125            }
126        }
127
128        impl_constant!($name);
129    };
130}
131impl_font!(Body, 16.0, FontWeight::Normal, "Body font style.");
132impl_font!(Title, 24.0, FontWeight::SemiBold, "Title font style.");
133impl_font!(Headline, 32.0, FontWeight::Bold, "Headline font style.");
134impl_font!(
135    Subheadline,
136    20.0,
137    FontWeight::SemiBold,
138    "Subheadline font style."
139);
140impl_font!(Caption, 12.0, FontWeight::Normal, "Caption font style.");
141impl_font!(Footnote, 10.0, FontWeight::Light, "Footnote font style.");