1use core::fmt::Debug;
2
3use nami::{Computed, Signal, impl_constant};
4use waterui_core::{
5 Environment,
6 resolve::{self, AnyResolvable, Resolvable},
7};
8
9#[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#[derive(Debug, Clone)]
25#[non_exhaustive]
26pub struct ResolvedFont {
27 pub size: f32,
29 pub weight: FontWeight,
31}
32
33impl ResolvedFont {
34 #[must_use]
36 pub const fn new(size: f32, weight: FontWeight) -> Self {
37 Self { size, weight }
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
43pub enum FontWeight {
44 Thin,
46 UltraLight,
48 Light,
50 #[default]
52 Normal,
53 Medium,
55 SemiBold,
57 Bold,
59 UltraBold,
61 Black,
63}
64
65impl_constant!(Font, ResolvedFont, FontWeight);
66
67impl Font {
68 pub fn new(font: impl Resolvable<Resolved = ResolvedFont> + 'static) -> Self {
70 Self(AnyResolvable::new(font))
71 }
72
73 #[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 #[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 #[must_use]
94 pub fn bold(self) -> Self {
95 self.weight(FontWeight::Bold)
96 }
97
98 #[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.");