takumi_css/style/properties/
font_size.rs1use std::fmt;
2
3use cssparser::Parser;
4
5use crate::style::{
6 Animatable, Color, CssSyntaxKind, CssToken, FromCss, Length, MakeComputed, ParseResult,
7 SizingContext, ToCss, declare_enum_from_css_impl,
8};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12#[non_exhaustive]
13pub enum FontSizeKeyword {
14 XXSmall,
16 XSmall,
18 Small,
20 #[default]
22 Medium,
23 Large,
25 XLarge,
27 XXLarge,
29 XXXLarge,
31}
32
33impl FontSizeKeyword {
34 pub const fn to_length(self) -> Length {
36 match self {
37 Self::XXSmall => Length::Rem(0.6),
38 Self::XSmall => Length::Rem(0.75),
39 Self::Small => Length::Rem(8.0 / 9.0),
40 Self::Medium => Length::Rem(1.0),
41 Self::Large => Length::Rem(1.2),
42 Self::XLarge => Length::Rem(1.5),
43 Self::XXLarge => Length::Rem(2.0),
44 Self::XXXLarge => Length::Rem(3.0),
45 }
46 }
47}
48
49declare_enum_from_css_impl!(
50 FontSizeKeyword,
51 "xx-small" => FontSizeKeyword::XXSmall,
52 "x-small" => FontSizeKeyword::XSmall,
53 "small" => FontSizeKeyword::Small,
54 "medium" => FontSizeKeyword::Medium,
55 "large" => FontSizeKeyword::Large,
56 "x-large" => FontSizeKeyword::XLarge,
57 "xx-large" => FontSizeKeyword::XXLarge,
58 "xxx-large" => FontSizeKeyword::XXXLarge,
59);
60
61#[derive(Debug, Clone, Copy, PartialEq)]
63#[non_exhaustive]
64pub enum FontSize {
65 Keyword(FontSizeKeyword),
67 Length(Length),
69}
70
71impl FontSize {
72 pub fn to_px(self, sizing: &SizingContext, inherited_font_size: f32) -> f32 {
73 match self {
74 Self::Keyword(keyword) => keyword.to_length().to_px(sizing, inherited_font_size),
75 Self::Length(length) => length.to_px(sizing, inherited_font_size),
76 }
77 }
78}
79
80impl Default for FontSize {
81 fn default() -> Self {
82 Self::Keyword(FontSizeKeyword::Medium)
83 }
84}
85
86impl From<Length> for FontSize {
87 fn from(value: Length) -> Self {
88 Self::Length(value)
89 }
90}
91
92impl From<FontSizeKeyword> for FontSize {
93 fn from(value: FontSizeKeyword) -> Self {
94 Self::Keyword(value)
95 }
96}
97
98impl<'i> FromCss<'i> for FontSize {
99 fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
100 input
101 .try_parse(FontSizeKeyword::from_css)
102 .map(Self::Keyword)
103 .or_else(|_| Length::from_css(input).map(Self::Length))
104 }
105
106 const VALID_TOKENS: &'static [CssToken] = &[
107 CssToken::Keyword("xx-small"),
108 CssToken::Keyword("x-small"),
109 CssToken::Keyword("small"),
110 CssToken::Keyword("medium"),
111 CssToken::Keyword("large"),
112 CssToken::Keyword("x-large"),
113 CssToken::Keyword("xx-large"),
114 CssToken::Keyword("xxx-large"),
115 CssToken::Syntax(CssSyntaxKind::Length),
116 ];
117}
118
119impl MakeComputed for FontSize {
120 fn make_computed(&mut self, sizing: &SizingContext) {
121 if let Self::Length(length) = self {
122 length.make_computed(sizing);
123 }
124 }
125}
126
127impl Animatable for FontSize {
128 fn interpolate(
129 &mut self,
130 from: &Self,
131 to: &Self,
132 progress: f32,
133 sizing: &SizingContext,
134 current_color: Color,
135 ) {
136 let from_length = match *from {
137 Self::Keyword(keyword) => keyword.to_length(),
138 Self::Length(length) => length,
139 };
140 let to_length = match *to {
141 Self::Keyword(keyword) => keyword.to_length(),
142 Self::Length(length) => length,
143 };
144
145 let mut value = from_length;
146 value.interpolate(&from_length, &to_length, progress, sizing, current_color);
147 *self = Self::Length(value);
148 }
149}
150
151impl ToCss for FontSize {
152 fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
153 match self {
154 Self::Keyword(k) => k.to_css(dest),
155 Self::Length(l) => l.to_css(dest),
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use std::rc::Rc;
163
164 use taffy::Size;
165
166 use super::*;
167 use crate::{
168 style::{CalcArena, SizingContext},
169 viewport::Viewport,
170 };
171
172 #[test]
173 fn defaults_to_medium_keyword() {
174 assert_eq!(
175 FontSize::default(),
176 FontSize::Keyword(FontSizeKeyword::Medium)
177 );
178 }
179
180 #[test]
181 fn resolves_medium_keyword_to_default_font_size() {
182 let sizing = SizingContext {
183 viewport: Viewport::new((1200, 630)),
184 container_size: Size::NONE,
185 font_size: 16.0,
186 root_font_size: None,
187 line_height: 0.0,
188 root_line_height: None,
189 calc_arena: Rc::new(CalcArena::default()),
190 };
191
192 assert_eq!(FontSize::default().to_px(&sizing, sizing.font_size), 16.0);
193 }
194
195 #[test]
196 fn rem_font_size_in_descendant_does_not_double_apply_dpr() {
197 use crate::viewport::DEFAULT_FONT_SIZE;
198
199 let viewport = Viewport::new((1200, 630)).with_device_pixel_ratio(2.0);
200 let root_font_size_device_px = DEFAULT_FONT_SIZE * viewport.device_pixel_ratio;
201 let sizing = SizingContext {
202 viewport,
203 container_size: Size::NONE,
204 font_size: root_font_size_device_px,
205 root_font_size: Some(root_font_size_device_px),
206 line_height: 0.0,
207 root_line_height: None,
208 calc_arena: Rc::new(CalcArena::default()),
209 };
210
211 assert_eq!(
212 FontSize::Length(Length::Rem(0.5)).to_px(&sizing, sizing.font_size),
213 16.0
214 );
215 assert_eq!(
216 FontSize::Length(Length::Rem(1.0)).to_px(&sizing, sizing.font_size),
217 32.0
218 );
219 }
220}