use super::ROBOTO_FLEX_FAMILY;
use frust::TypeScale;
use frust::authoring::text::{FontFamily, FontWeight, GenericSlot, LineHeight, TextStyle};
fn material_family() -> FontFamily {
FontFamily::stack_with_generic([ROBOTO_FLEX_FAMILY], GenericSlot::SansSerif)
}
type TypeToken = (f32, f32, f32, FontWeight);
const DISPLAY_LARGE: TypeToken = (57.0, 64.0, -0.25, FontWeight::REGULAR);
const DISPLAY_MEDIUM: TypeToken = (45.0, 52.0, 0.0, FontWeight::REGULAR);
const DISPLAY_SMALL: TypeToken = (36.0, 44.0, 0.0, FontWeight::REGULAR);
const HEADLINE_LARGE: TypeToken = (32.0, 40.0, 0.0, FontWeight::REGULAR);
const HEADLINE_MEDIUM: TypeToken = (28.0, 36.0, 0.0, FontWeight::REGULAR);
const HEADLINE_SMALL: TypeToken = (24.0, 32.0, 0.0, FontWeight::REGULAR);
const TITLE_LARGE: TypeToken = (22.0, 28.0, 0.0, FontWeight::REGULAR);
const TITLE_MEDIUM: TypeToken = (16.0, 24.0, 0.15, FontWeight::MEDIUM);
const TITLE_SMALL: TypeToken = (14.0, 20.0, 0.1, FontWeight::MEDIUM);
const BODY_LARGE: TypeToken = (16.0, 24.0, 0.5, FontWeight::REGULAR);
const BODY_MEDIUM: TypeToken = (14.0, 20.0, 0.25, FontWeight::REGULAR);
const BODY_SMALL: TypeToken = (12.0, 16.0, 0.4, FontWeight::REGULAR);
const LABEL_LARGE: TypeToken = (14.0, 20.0, 0.1, FontWeight::MEDIUM);
const LABEL_MEDIUM: TypeToken = (12.0, 16.0, 0.5, FontWeight::MEDIUM);
const LABEL_SMALL: TypeToken = (11.0, 16.0, 0.5, FontWeight::MEDIUM);
const DISPLAY_LARGE_EMPHASIZED: TypeToken = (57.0, 64.0, 0.0, FontWeight::MEDIUM);
const DISPLAY_MEDIUM_EMPHASIZED: TypeToken = (45.0, 52.0, 0.0, FontWeight::MEDIUM);
const DISPLAY_SMALL_EMPHASIZED: TypeToken = (36.0, 44.0, 0.0, FontWeight::MEDIUM);
const HEADLINE_LARGE_EMPHASIZED: TypeToken = (32.0, 40.0, 0.0, FontWeight::MEDIUM);
const HEADLINE_MEDIUM_EMPHASIZED: TypeToken = (28.0, 36.0, 0.0, FontWeight::MEDIUM);
const HEADLINE_SMALL_EMPHASIZED: TypeToken = (24.0, 32.0, 0.0, FontWeight::MEDIUM);
const TITLE_LARGE_EMPHASIZED: TypeToken = (22.0, 28.0, 0.0, FontWeight::MEDIUM);
const TITLE_MEDIUM_EMPHASIZED: TypeToken = (16.0, 24.0, 0.15, FontWeight::BOLD);
const TITLE_SMALL_EMPHASIZED: TypeToken = (14.0, 20.0, 0.1, FontWeight::BOLD);
const BODY_LARGE_EMPHASIZED: TypeToken = (16.0, 24.0, 0.15, FontWeight::MEDIUM);
const BODY_MEDIUM_EMPHASIZED: TypeToken = (14.0, 20.0, 0.25, FontWeight::MEDIUM);
const BODY_SMALL_EMPHASIZED: TypeToken = (12.0, 16.0, 0.4, FontWeight::MEDIUM);
const LABEL_LARGE_EMPHASIZED: TypeToken = (14.0, 20.0, 0.1, FontWeight::BOLD);
const LABEL_MEDIUM_EMPHASIZED: TypeToken = (12.0, 16.0, 0.5, FontWeight::BOLD);
const LABEL_SMALL_EMPHASIZED: TypeToken = (11.0, 16.0, 0.5, FontWeight::BOLD);
fn apply_type(base: &TextStyle, token: TypeToken) -> TextStyle {
let (size, line_height_px, letter_spacing, weight) = token;
TextStyle {
family: material_family(),
size,
weight,
letter_spacing,
line_height: LineHeight::Absolute(line_height_px),
..base.clone()
}
}
pub fn type_scale(base: &TextStyle) -> TypeScale {
TypeScale {
display_large: apply_type(base, DISPLAY_LARGE),
display_medium: apply_type(base, DISPLAY_MEDIUM),
display_small: apply_type(base, DISPLAY_SMALL),
headline_large: apply_type(base, HEADLINE_LARGE),
headline_medium: apply_type(base, HEADLINE_MEDIUM),
headline_small: apply_type(base, HEADLINE_SMALL),
title_large: apply_type(base, TITLE_LARGE),
title_medium: apply_type(base, TITLE_MEDIUM),
title_small: apply_type(base, TITLE_SMALL),
body_large: apply_type(base, BODY_LARGE),
body_medium: apply_type(base, BODY_MEDIUM),
body_small: apply_type(base, BODY_SMALL),
label_large: apply_type(base, LABEL_LARGE),
label_medium: apply_type(base, LABEL_MEDIUM),
label_small: apply_type(base, LABEL_SMALL),
display_large_emphasized: apply_type(base, DISPLAY_LARGE_EMPHASIZED),
display_medium_emphasized: apply_type(base, DISPLAY_MEDIUM_EMPHASIZED),
display_small_emphasized: apply_type(base, DISPLAY_SMALL_EMPHASIZED),
headline_large_emphasized: apply_type(base, HEADLINE_LARGE_EMPHASIZED),
headline_medium_emphasized: apply_type(base, HEADLINE_MEDIUM_EMPHASIZED),
headline_small_emphasized: apply_type(base, HEADLINE_SMALL_EMPHASIZED),
title_large_emphasized: apply_type(base, TITLE_LARGE_EMPHASIZED),
title_medium_emphasized: apply_type(base, TITLE_MEDIUM_EMPHASIZED),
title_small_emphasized: apply_type(base, TITLE_SMALL_EMPHASIZED),
body_large_emphasized: apply_type(base, BODY_LARGE_EMPHASIZED),
body_medium_emphasized: apply_type(base, BODY_MEDIUM_EMPHASIZED),
body_small_emphasized: apply_type(base, BODY_SMALL_EMPHASIZED),
label_large_emphasized: apply_type(base, LABEL_LARGE_EMPHASIZED),
label_medium_emphasized: apply_type(base, LABEL_MEDIUM_EMPHASIZED),
label_small_emphasized: apply_type(base, LABEL_SMALL_EMPHASIZED),
}
}
#[cfg(test)]
mod tests {
use peniko::Color;
use super::*;
#[test]
fn type_scale_display_large_matches_table() {
let scale = type_scale(&TextStyle::new(16.0, Color::BLACK));
assert_eq!(scale.display_large.size, 57.0);
assert_eq!(scale.display_large.line_height, LineHeight::Absolute(64.0));
assert_eq!(scale.display_large.letter_spacing, -0.25);
assert_eq!(scale.display_large.weight, FontWeight::REGULAR);
}
#[test]
fn type_scale_preserves_base_style_and_color_but_overrides_family() {
let base = TextStyle {
family: FontFamily::named("Roboto"),
..TextStyle::new(16.0, Color::from_rgb8(1, 2, 3))
};
let scale = type_scale(&base);
assert_ne!(scale.body_large.family, base.family);
assert_eq!(scale.body_large.family, material_family());
assert_eq!(scale.body_large.color, base.color);
assert_eq!(scale.body_large.style, base.style);
}
#[test]
fn type_scale_every_role_resolves_to_roboto_flex() {
let scale = type_scale(&TextStyle::default());
let expected = material_family();
let roles: [&TextStyle; 30] = [
&scale.display_large,
&scale.display_medium,
&scale.display_small,
&scale.headline_large,
&scale.headline_medium,
&scale.headline_small,
&scale.title_large,
&scale.title_medium,
&scale.title_small,
&scale.body_large,
&scale.body_medium,
&scale.body_small,
&scale.label_large,
&scale.label_medium,
&scale.label_small,
&scale.display_large_emphasized,
&scale.display_medium_emphasized,
&scale.display_small_emphasized,
&scale.headline_large_emphasized,
&scale.headline_medium_emphasized,
&scale.headline_small_emphasized,
&scale.title_large_emphasized,
&scale.title_medium_emphasized,
&scale.title_small_emphasized,
&scale.body_large_emphasized,
&scale.body_medium_emphasized,
&scale.body_small_emphasized,
&scale.label_large_emphasized,
&scale.label_medium_emphasized,
&scale.label_small_emphasized,
];
for role in roles {
assert_eq!(role.family, expected);
}
}
#[test]
fn type_scale_every_emphasized_role_differs_in_weight_from_its_base() {
let scale = type_scale(&TextStyle::new(16.0, Color::BLACK));
let pairs: [(&TextStyle, &TextStyle); 15] = [
(&scale.display_large, &scale.display_large_emphasized),
(&scale.display_medium, &scale.display_medium_emphasized),
(&scale.display_small, &scale.display_small_emphasized),
(&scale.headline_large, &scale.headline_large_emphasized),
(&scale.headline_medium, &scale.headline_medium_emphasized),
(&scale.headline_small, &scale.headline_small_emphasized),
(&scale.title_large, &scale.title_large_emphasized),
(&scale.title_medium, &scale.title_medium_emphasized),
(&scale.title_small, &scale.title_small_emphasized),
(&scale.body_large, &scale.body_large_emphasized),
(&scale.body_medium, &scale.body_medium_emphasized),
(&scale.body_small, &scale.body_small_emphasized),
(&scale.label_large, &scale.label_large_emphasized),
(&scale.label_medium, &scale.label_medium_emphasized),
(&scale.label_small, &scale.label_small_emphasized),
];
for (b, emphasized) in pairs {
assert_ne!(
b.weight, emphasized.weight,
"expected emphasized weight to differ from base weight"
);
}
}
#[test]
fn type_scale_every_role_matches_its_literal_metric_table() {
let scale = type_scale(&TextStyle::new(16.0, Color::BLACK));
let table: [(&str, &TextStyle, f32, f32, f32, FontWeight); 30] = [
(
"display_large",
&scale.display_large,
57.0,
64.0,
-0.25,
FontWeight::REGULAR,
),
(
"display_medium",
&scale.display_medium,
45.0,
52.0,
0.0,
FontWeight::REGULAR,
),
(
"display_small",
&scale.display_small,
36.0,
44.0,
0.0,
FontWeight::REGULAR,
),
(
"headline_large",
&scale.headline_large,
32.0,
40.0,
0.0,
FontWeight::REGULAR,
),
(
"headline_medium",
&scale.headline_medium,
28.0,
36.0,
0.0,
FontWeight::REGULAR,
),
(
"headline_small",
&scale.headline_small,
24.0,
32.0,
0.0,
FontWeight::REGULAR,
),
(
"title_large",
&scale.title_large,
22.0,
28.0,
0.0,
FontWeight::REGULAR,
),
(
"title_medium",
&scale.title_medium,
16.0,
24.0,
0.15,
FontWeight::MEDIUM,
),
(
"title_small",
&scale.title_small,
14.0,
20.0,
0.1,
FontWeight::MEDIUM,
),
(
"body_large",
&scale.body_large,
16.0,
24.0,
0.5,
FontWeight::REGULAR,
),
(
"body_medium",
&scale.body_medium,
14.0,
20.0,
0.25,
FontWeight::REGULAR,
),
(
"body_small",
&scale.body_small,
12.0,
16.0,
0.4,
FontWeight::REGULAR,
),
(
"label_large",
&scale.label_large,
14.0,
20.0,
0.1,
FontWeight::MEDIUM,
),
(
"label_medium",
&scale.label_medium,
12.0,
16.0,
0.5,
FontWeight::MEDIUM,
),
(
"label_small",
&scale.label_small,
11.0,
16.0,
0.5,
FontWeight::MEDIUM,
),
(
"display_large_emphasized",
&scale.display_large_emphasized,
57.0,
64.0,
0.0,
FontWeight::MEDIUM,
),
(
"display_medium_emphasized",
&scale.display_medium_emphasized,
45.0,
52.0,
0.0,
FontWeight::MEDIUM,
),
(
"display_small_emphasized",
&scale.display_small_emphasized,
36.0,
44.0,
0.0,
FontWeight::MEDIUM,
),
(
"headline_large_emphasized",
&scale.headline_large_emphasized,
32.0,
40.0,
0.0,
FontWeight::MEDIUM,
),
(
"headline_medium_emphasized",
&scale.headline_medium_emphasized,
28.0,
36.0,
0.0,
FontWeight::MEDIUM,
),
(
"headline_small_emphasized",
&scale.headline_small_emphasized,
24.0,
32.0,
0.0,
FontWeight::MEDIUM,
),
(
"title_large_emphasized",
&scale.title_large_emphasized,
22.0,
28.0,
0.0,
FontWeight::MEDIUM,
),
(
"title_medium_emphasized",
&scale.title_medium_emphasized,
16.0,
24.0,
0.15,
FontWeight::BOLD,
),
(
"title_small_emphasized",
&scale.title_small_emphasized,
14.0,
20.0,
0.1,
FontWeight::BOLD,
),
(
"body_large_emphasized",
&scale.body_large_emphasized,
16.0,
24.0,
0.15,
FontWeight::MEDIUM,
),
(
"body_medium_emphasized",
&scale.body_medium_emphasized,
14.0,
20.0,
0.25,
FontWeight::MEDIUM,
),
(
"body_small_emphasized",
&scale.body_small_emphasized,
12.0,
16.0,
0.4,
FontWeight::MEDIUM,
),
(
"label_large_emphasized",
&scale.label_large_emphasized,
14.0,
20.0,
0.1,
FontWeight::BOLD,
),
(
"label_medium_emphasized",
&scale.label_medium_emphasized,
12.0,
16.0,
0.5,
FontWeight::BOLD,
),
(
"label_small_emphasized",
&scale.label_small_emphasized,
11.0,
16.0,
0.5,
FontWeight::BOLD,
),
];
for (name, style, size, line_height_px, letter_spacing, weight) in table {
assert_eq!(style.size, size, "{name} size");
assert_eq!(
style.line_height,
LineHeight::Absolute(line_height_px),
"{name} line_height"
);
assert_eq!(
style.letter_spacing, letter_spacing,
"{name} letter_spacing"
);
assert_eq!(style.weight, weight, "{name} weight");
}
}
}