use mcu_hct::Hct;
use mcu_palettes::TonalPalette;
use mcu_utils::math::clamp_double;
use crate::{
ColorSpecDelegate, ContrastCurve, DeltaConstraint, DynamicColor, DynamicScheme, Platform,
ToneDeltaPair, TonePolarity, Variant,
};
fn t_max_c(
palette: &TonalPalette,
lower_bound: f64,
upper_bound: f64,
chroma_multiplier: f64,
) -> f64 {
let answer = find_best_tone_for_chroma(
palette.hue(),
palette.chroma() * chroma_multiplier,
100.0,
true,
);
clamp_double(lower_bound, upper_bound, answer)
}
fn t_min_c(palette: &TonalPalette, lower_bound: f64, upper_bound: f64) -> f64 {
let answer = find_best_tone_for_chroma(palette.hue(), palette.chroma(), 0.0, false);
clamp_double(lower_bound, upper_bound, answer)
}
fn find_best_tone_for_chroma(
hue: f64,
chroma: f64,
mut tone: f64,
by_decreasing_tone: bool,
) -> f64 {
let mut answer = tone;
let mut best_candidate = Hct::from(hue, chroma, answer);
while best_candidate.chroma() < chroma {
if tone < 0.0 || tone > 100.0 {
break;
}
tone += if by_decreasing_tone { -1.0 } else { 1.0 };
let new_candidate = Hct::from(hue, chroma, tone);
if best_candidate.chroma() < new_candidate.chroma() {
best_candidate = new_candidate;
answer = tone;
}
}
answer
}
fn get_curve(default_contrast: f64) -> ContrastCurve {
if (default_contrast - 1.5).abs() < 0.01 {
ContrastCurve::new(1.5, 1.5, 3.0, 5.5)
} else if (default_contrast - 3.0).abs() < 0.01 {
ContrastCurve::new(3.0, 3.0, 4.5, 7.0)
} else if (default_contrast - 4.5).abs() < 0.01 {
ContrastCurve::new(4.5, 4.5, 7.0, 11.0)
} else if (default_contrast - 6.0).abs() < 0.01 {
ContrastCurve::new(6.0, 6.0, 7.0, 11.0)
} else if (default_contrast - 7.0).abs() < 0.01 {
ContrastCurve::new(7.0, 7.0, 11.0, 21.0)
} else if (default_contrast - 9.0).abs() < 0.01 {
ContrastCurve::new(9.0, 9.0, 11.0, 21.0)
} else if (default_contrast - 11.0).abs() < 0.01 {
ContrastCurve::new(11.0, 11.0, 21.0, 21.0)
} else if (default_contrast - 21.0).abs() < 0.01 {
ContrastCurve::new(21.0, 21.0, 21.0, 21.0)
} else {
ContrastCurve::new(default_contrast, default_contrast, 7.0, 21.0)
}
}
#[derive(Clone, Copy)]
pub struct ColorSpecDelegateImpl2025;
impl ColorSpecDelegateImpl2025 {
fn highest_surface(&self, scheme: &DynamicScheme) -> DynamicColor {
if scheme.is_dark {
self.surface_bright()
} else {
self.surface_dim()
}
}
}
impl ColorSpecDelegate for ColorSpecDelegateImpl2025 {
fn primary_palette_key_color(&self) -> DynamicColor {
DynamicColor::from_palette(
"primary_palette_key_color",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(|s: &DynamicScheme| s.primary_palette.key_color().tone()),
false,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn secondary_palette_key_color(&self) -> DynamicColor {
DynamicColor::from_palette(
"secondary_palette_key_color",
|s: &DynamicScheme| s.secondary_palette.clone(),
Some(|s: &DynamicScheme| s.secondary_palette.key_color().tone()),
false,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn tertiary_palette_key_color(&self) -> DynamicColor {
DynamicColor::from_palette(
"tertiary_palette_key_color",
|s: &DynamicScheme| s.tertiary_palette.clone(),
Some(|s: &DynamicScheme| s.tertiary_palette.key_color().tone()),
false,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn neutral_palette_key_color(&self) -> DynamicColor {
DynamicColor::from_palette(
"neutral_palette_key_color",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| s.neutral_palette.key_color().tone()),
false,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn neutral_variant_palette_key_color(&self) -> DynamicColor {
DynamicColor::from_palette(
"neutral_variant_palette_key_color",
|s: &DynamicScheme| s.neutral_variant_palette.clone(),
Some(|s: &DynamicScheme| s.neutral_variant_palette.key_color().tone()),
false,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn error_palette_key_color(&self) -> DynamicColor {
DynamicColor::from_palette(
"error_palette_key_color",
|s: &DynamicScheme| s.error_palette.clone(),
Some(|s: &DynamicScheme| s.error_palette.key_color().tone()),
false,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn background(&self) -> DynamicColor {
DynamicColor::from_palette(
"background",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 6.0 } else { 98.0 }),
true,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn on_background(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_background",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Watch {
100.0
} else {
delegate_ref.on_surface().tone.as_ref()(s)
}
}),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.background())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(9.0))),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn surface(&self) -> DynamicColor {
DynamicColor::from_palette(
"surface",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
if s.is_dark {
4.0
} else if Hct::is_yellow(s.neutral_palette.hue()) {
99.0
} else if s.variant == Variant::Vibrant {
97.0
} else {
98.0
}
} else {
0.0
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn surface_dim(&self) -> DynamicColor {
DynamicColor::from_palette(
"surface_dim",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| {
if s.is_dark {
#[cfg(feature = "upstream-surface-dim-tone")]
{
4.0
}
#[cfg(not(feature = "upstream-surface-dim-tone"))]
{
2.0
}
} else if Hct::is_yellow(s.neutral_palette.hue()) {
90.0
} else if s.variant == Variant::Vibrant {
85.0
} else {
87.0
}
}),
true,
Some(|s: &DynamicScheme| {
if !s.is_dark {
match s.variant {
Variant::Neutral => 2.5,
Variant::TonalSpot => 1.7,
Variant::Expressive => {
if Hct::is_yellow(s.neutral_palette.hue()) {
2.7
} else {
1.75
}
}
Variant::Vibrant => 1.36,
_ => 1.0,
}
} else {
1.0
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn surface_bright(&self) -> DynamicColor {
DynamicColor::from_palette(
"surface_bright",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| {
if s.is_dark {
18.0
} else if Hct::is_yellow(s.neutral_palette.hue()) {
99.0
} else if s.variant == Variant::Vibrant {
97.0
} else {
98.0
}
}),
true,
Some(|s: &DynamicScheme| {
if s.is_dark {
match s.variant {
Variant::Neutral => 2.5,
Variant::TonalSpot => 1.7,
Variant::Expressive => {
if Hct::is_yellow(s.neutral_palette.hue()) {
2.7
} else {
1.75
}
}
Variant::Vibrant => 1.36,
_ => 1.0,
}
} else {
1.0
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn surface_container_lowest(&self) -> DynamicColor {
DynamicColor::from_palette(
"surface_container_lowest",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 0.0 } else { 100.0 }),
true,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn surface_container_low(&self) -> DynamicColor {
DynamicColor::from_palette(
"surface_container_low",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
if s.is_dark {
6.0
} else if Hct::is_yellow(s.neutral_palette.hue()) {
98.0
} else if s.variant == Variant::Vibrant {
95.0
} else {
96.0
}
} else {
15.0
}
}),
true,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
match s.variant {
Variant::Neutral => 1.3,
Variant::TonalSpot => 1.25,
Variant::Expressive => {
if Hct::is_yellow(s.neutral_palette.hue()) {
1.3
} else {
1.15
}
}
Variant::Vibrant => 1.08,
_ => 1.0,
}
} else {
1.0
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn surface_container(&self) -> DynamicColor {
DynamicColor::from_palette(
"surface_container",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
if s.is_dark {
9.0
} else if Hct::is_yellow(s.neutral_palette.hue()) {
96.0
} else if s.variant == Variant::Vibrant {
92.0
} else {
94.0
}
} else {
20.0
}
}),
true,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
match s.variant {
Variant::Neutral => 1.6,
Variant::TonalSpot => 1.4,
Variant::Expressive => {
if Hct::is_yellow(s.neutral_palette.hue()) {
1.6
} else {
1.3
}
}
Variant::Vibrant => 1.15,
_ => 1.0,
}
} else {
1.0
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn surface_container_high(&self) -> DynamicColor {
DynamicColor::from_palette(
"surface_container_high",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
if s.is_dark {
12.0
} else if Hct::is_yellow(s.neutral_palette.hue()) {
94.0
} else if s.variant == Variant::Vibrant {
90.0
} else {
92.0
}
} else {
25.0
}
}),
true,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
match s.variant {
Variant::Neutral => 1.9,
Variant::TonalSpot => 1.5,
Variant::Expressive => {
if Hct::is_yellow(s.neutral_palette.hue()) {
1.95
} else {
1.45
}
}
Variant::Vibrant => 1.22,
_ => 1.0,
}
} else {
1.0
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn surface_container_highest(&self) -> DynamicColor {
DynamicColor::from_palette(
"surface_container_highest",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| {
if s.is_dark {
15.0
} else if Hct::is_yellow(s.neutral_palette.hue()) {
92.0
} else if s.variant == Variant::Vibrant {
88.0
} else {
90.0
}
}),
true,
Some(|s: &DynamicScheme| match s.variant {
Variant::Neutral => 2.2,
Variant::TonalSpot => 1.7,
Variant::Expressive => {
if Hct::is_yellow(s.neutral_palette.hue()) {
2.3
} else {
1.6
}
}
Variant::Vibrant => 1.29,
_ => 1.0,
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn on_surface(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_surface",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| {
if s.variant == Variant::Vibrant {
t_max_c(&s.neutral_palette, 0.0, 100.0, 1.1)
} else {
if s.is_dark {
90.0
} else {
10.0
}
}
}),
false,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
match s.variant {
Variant::Neutral => 2.2,
Variant::TonalSpot => 1.7,
Variant::Expressive => {
if Hct::is_yellow(s.neutral_palette.hue()) {
if s.is_dark {
3.0
} else {
2.3
}
} else {
1.6
}
}
_ => 1.0,
}
} else {
1.0
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
Some(delegate_ref.surface_container_high())
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.is_dark && s.platform == Platform::Phone {
Some(get_curve(11.0))
} else {
Some(get_curve(9.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn surface_variant(&self) -> DynamicColor {
self.surface_container_highest()
}
fn on_surface_variant(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_surface_variant",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 80.0 } else { 30.0 }),
false,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
match s.variant {
Variant::Neutral => 2.2,
Variant::TonalSpot => 1.7,
Variant::Expressive => {
if Hct::is_yellow(s.neutral_palette.hue()) {
if s.is_dark {
3.0
} else {
2.3
}
} else {
1.6
}
}
_ => 1.0,
}
} else {
1.0
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
Some(delegate_ref.surface_container_high())
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
if s.is_dark {
Some(get_curve(6.0))
} else {
Some(get_curve(4.5))
}
} else {
Some(get_curve(7.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn inverse_surface(&self) -> DynamicColor {
DynamicColor::from_palette(
"inverse_surface",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 98.0 } else { 4.0 }),
true,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn inverse_on_surface(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"inverse_on_surface",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 90.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.inverse_surface())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(7.0))),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn outline(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"outline",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 60.0 } else { 50.0 }),
false,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
match s.variant {
Variant::Neutral => 2.2,
Variant::TonalSpot => 1.7,
Variant::Expressive => {
if Hct::is_yellow(s.neutral_palette.hue()) {
if s.is_dark {
3.0
} else {
2.3
}
} else {
1.6
}
}
_ => 1.0,
}
} else {
1.0
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
Some(delegate_ref.surface_container_high())
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(3.0))
} else {
Some(get_curve(4.5))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn outline_variant(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"outline_variant",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 30.0 } else { 80.0 }),
false,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
match s.variant {
Variant::Neutral => 2.2,
Variant::TonalSpot => 1.7,
Variant::Expressive => {
if Hct::is_yellow(s.neutral_palette.hue()) {
if s.is_dark {
3.0
} else {
2.3
}
} else {
1.6
}
}
_ => 1.0,
}
} else {
1.0
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
Some(delegate_ref.surface_container_high())
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(1.5))
} else {
Some(get_curve(3.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn shadow(&self) -> DynamicColor {
DynamicColor::from_palette(
"shadow",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|_s: &DynamicScheme| 0.0),
false,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn scrim(&self) -> DynamicColor {
DynamicColor::from_palette(
"scrim",
|s: &DynamicScheme| s.neutral_palette.clone(),
Some(|_s: &DynamicScheme| 0.0),
false,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn surface_tint(&self) -> DynamicColor {
self.primary()
}
fn primary(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"primary",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(|s: &DynamicScheme| match s.variant {
Variant::Neutral => {
if s.platform == Platform::Phone {
if s.is_dark {
80.0
} else {
40.0
}
} else {
90.0
}
}
Variant::TonalSpot => {
if s.platform == Platform::Phone {
if s.is_dark {
80.0
} else {
t_max_c(&s.primary_palette, 0.0, 100.0, 1.0)
}
} else {
t_max_c(&s.primary_palette, 0.0, 90.0, 1.0)
}
}
Variant::Expressive => {
if s.platform == Platform::Phone {
let upper = if Hct::is_yellow(s.primary_palette.hue()) {
25.0
} else if Hct::is_cyan(s.primary_palette.hue()) {
88.0
} else {
98.0
};
t_max_c(&s.primary_palette, 0.0, upper, 1.0)
} else {
t_max_c(&s.primary_palette, 0.0, 100.0, 1.0)
}
}
Variant::Vibrant => {
if s.platform == Platform::Phone {
let upper = if Hct::is_cyan(s.primary_palette.hue()) {
88.0
} else {
98.0
};
t_max_c(&s.primary_palette, 0.0, upper, 1.0)
} else {
t_max_c(&s.primary_palette, 0.0, 100.0, 1.0)
}
}
_ => {
if s.is_dark {
80.0
} else {
40.0
}
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
Some(delegate_ref.surface_container_high())
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(4.5))
} else {
Some(get_curve(7.0))
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.primary_container(),
ColorSpecDelegateImpl2025.primary(),
5.0,
TonePolarity::Lighter,
true,
DeltaConstraint::Farther,
))
} else {
None
}
}),
)
}
fn primary_dim(&self) -> Option<DynamicColor> {
let delegate_ref = ColorSpecDelegateImpl2025;
Some(DynamicColor::from_palette(
"primary_dim",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(|s: &DynamicScheme| match s.variant {
Variant::Neutral => 85.0,
Variant::TonalSpot => t_max_c(&s.primary_palette, 0.0, 90.0, 1.0),
_ => t_max_c(&s.primary_palette, 0.0, 100.0, 1.0),
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.surface_container_high())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
Some(move |_s: &DynamicScheme| {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.primary_dim().unwrap(),
ColorSpecDelegateImpl2025.primary(),
5.0,
TonePolarity::Darker,
true,
DeltaConstraint::Farther,
))
}),
))
}
fn on_primary(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_primary",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 100.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.primary())
} else {
delegate_ref.primary_dim()
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(6.0))
} else {
Some(get_curve(7.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn primary_container(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"primary_container",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Watch {
30.0
} else {
match s.variant {
Variant::Neutral => {
if s.is_dark {
30.0
} else {
90.0
}
}
Variant::TonalSpot => {
if s.is_dark {
t_min_c(&s.primary_palette, 35.0, 93.0)
} else {
t_max_c(&s.primary_palette, 0.0, 90.0, 1.0)
}
}
Variant::Expressive => {
if s.is_dark {
t_max_c(&s.primary_palette, 30.0, 93.0, 1.0)
} else {
let upper = if Hct::is_cyan(s.primary_palette.hue()) {
88.0
} else {
90.0
};
t_max_c(&s.primary_palette, 78.0, upper, 1.0)
}
}
Variant::Vibrant => {
if s.is_dark {
t_min_c(&s.primary_palette, 66.0, 93.0)
} else {
let upper = if Hct::is_cyan(s.primary_palette.hue()) {
88.0
} else {
93.0
};
t_max_c(&s.primary_palette, 66.0, upper, 1.0)
}
}
_ => {
if s.is_dark {
30.0
} else {
90.0
}
}
}
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
None
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone && s.contrast_level > 0.0 {
Some(get_curve(1.5))
} else {
None
}
}),
Some(move |s: &DynamicScheme| {
if s.platform != Platform::Phone {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.primary_container(),
ColorSpecDelegateImpl2025.primary_dim().unwrap(),
10.0,
TonePolarity::Darker,
true,
DeltaConstraint::Farther,
))
} else {
None
}
}),
)
}
fn on_primary_container(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_primary_container",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 90.0 } else { 10.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.primary_container())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(6.0))
} else {
Some(get_curve(7.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn inverse_primary(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"inverse_primary",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(|s: &DynamicScheme| t_max_c(&s.primary_palette, 0.0, 100.0, 1.0)),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.inverse_surface())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(6.0))
} else {
Some(get_curve(7.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn secondary(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"secondary",
|s: &DynamicScheme| s.secondary_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Watch {
if s.variant == Variant::Neutral {
90.0
} else {
t_max_c(&s.secondary_palette, 0.0, 90.0, 1.0)
}
} else {
match s.variant {
Variant::Neutral => {
if s.is_dark {
t_min_c(&s.secondary_palette, 0.0, 98.0)
} else {
t_max_c(&s.secondary_palette, 0.0, 100.0, 1.0)
}
}
Variant::Vibrant => {
let upper = if s.is_dark { 90.0 } else { 98.0 };
t_max_c(&s.secondary_palette, 0.0, upper, 1.0)
}
_ => {
if s.is_dark {
80.0
} else {
t_max_c(&s.secondary_palette, 0.0, 100.0, 1.0)
}
}
}
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
Some(delegate_ref.surface_container_high())
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(4.5))
} else {
Some(get_curve(7.0))
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.secondary_container(),
ColorSpecDelegateImpl2025.secondary(),
5.0,
TonePolarity::Lighter,
true,
DeltaConstraint::Farther,
))
} else {
None
}
}),
)
}
fn secondary_dim(&self) -> Option<DynamicColor> {
let delegate_ref = ColorSpecDelegateImpl2025;
Some(DynamicColor::from_palette(
"secondary_dim",
|s: &DynamicScheme| s.secondary_palette.clone(),
Some(|s: &DynamicScheme| {
if s.variant == Variant::Neutral {
85.0
} else {
t_max_c(&s.secondary_palette, 0.0, 90.0, 1.0)
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.surface_container_high())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
Some(move |_s: &DynamicScheme| {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.secondary_dim().unwrap(),
ColorSpecDelegateImpl2025.secondary(),
5.0,
TonePolarity::Darker,
true,
DeltaConstraint::Farther,
))
}),
))
}
fn on_secondary(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_secondary",
|s: &DynamicScheme| s.secondary_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 100.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.secondary())
} else {
delegate_ref.secondary_dim()
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(6.0))
} else {
Some(get_curve(7.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn secondary_container(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"secondary_container",
|s: &DynamicScheme| s.secondary_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Watch {
30.0
} else {
match s.variant {
Variant::Vibrant => {
if s.is_dark {
t_min_c(&s.secondary_palette, 30.0, 40.0)
} else {
t_max_c(&s.secondary_palette, 84.0, 90.0, 1.0)
}
}
Variant::Expressive => {
if s.is_dark {
15.0
} else {
t_max_c(&s.secondary_palette, 90.0, 95.0, 1.0)
}
}
_ => {
if s.is_dark {
25.0
} else {
90.0
}
}
}
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
None
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone && s.contrast_level > 0.0 {
Some(get_curve(1.5))
} else {
None
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Watch {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.secondary_container(),
ColorSpecDelegateImpl2025.secondary_dim().unwrap(),
10.0,
TonePolarity::Darker,
true,
DeltaConstraint::Farther,
))
} else {
None
}
}),
)
}
fn on_secondary_container(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_secondary_container",
|s: &DynamicScheme| s.secondary_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 90.0 } else { 10.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.secondary_container())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(6.0))
} else {
Some(get_curve(7.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn tertiary(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"tertiary",
|s: &DynamicScheme| s.tertiary_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Watch {
if s.variant == Variant::TonalSpot {
t_max_c(&s.tertiary_palette, 0.0, 90.0, 1.0)
} else {
t_max_c(&s.tertiary_palette, 0.0, 100.0, 1.0)
}
} else {
match s.variant {
Variant::Expressive | Variant::Vibrant => {
let upper = if Hct::is_cyan(s.tertiary_palette.hue()) {
88.0
} else if s.is_dark {
98.0
} else {
100.0
};
t_max_c(&s.tertiary_palette, 0.0, upper, 1.0)
}
_ => {
if s.is_dark {
t_max_c(&s.tertiary_palette, 0.0, 98.0, 1.0)
} else {
t_max_c(&s.tertiary_palette, 0.0, 100.0, 1.0)
}
}
}
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
Some(delegate_ref.surface_container_high())
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(4.5))
} else {
Some(get_curve(7.0))
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.tertiary_container(),
ColorSpecDelegateImpl2025.tertiary(),
5.0,
TonePolarity::Lighter,
true,
DeltaConstraint::Farther,
))
} else {
None
}
}),
)
}
fn tertiary_dim(&self) -> Option<DynamicColor> {
let delegate_ref = ColorSpecDelegateImpl2025;
Some(DynamicColor::from_palette(
"tertiary_dim",
|s: &DynamicScheme| s.tertiary_palette.clone(),
Some(|s: &DynamicScheme| {
if s.variant == Variant::TonalSpot {
t_max_c(&s.tertiary_palette, 0.0, 90.0, 1.0)
} else {
t_max_c(&s.tertiary_palette, 0.0, 100.0, 1.0)
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.surface_container_high())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
Some(move |_s: &DynamicScheme| {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.tertiary_dim().unwrap(),
ColorSpecDelegateImpl2025.tertiary(),
5.0,
TonePolarity::Darker,
true,
DeltaConstraint::Farther,
))
}),
))
}
fn on_tertiary(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_tertiary",
|s: &DynamicScheme| s.tertiary_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 100.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.tertiary())
} else {
delegate_ref.tertiary_dim()
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(6.0))
} else {
Some(get_curve(7.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn tertiary_container(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"tertiary_container",
|s: &DynamicScheme| s.tertiary_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Watch {
if s.variant == Variant::TonalSpot {
t_max_c(&s.tertiary_palette, 0.0, 90.0, 1.0)
} else {
t_max_c(&s.tertiary_palette, 0.0, 100.0, 1.0)
}
} else {
match s.variant {
Variant::Neutral => {
if s.is_dark {
t_max_c(&s.tertiary_palette, 0.0, 93.0, 1.0)
} else {
t_max_c(&s.tertiary_palette, 0.0, 96.0, 1.0)
}
}
Variant::TonalSpot => {
let upper = if s.is_dark { 93.0 } else { 100.0 };
t_max_c(&s.tertiary_palette, 0.0, upper, 1.0)
}
Variant::Expressive => {
let upper = if Hct::is_cyan(s.tertiary_palette.hue()) {
88.0
} else if s.is_dark {
93.0
} else {
100.0
};
t_max_c(&s.tertiary_palette, 75.0, upper, 1.0)
}
Variant::Vibrant => {
if s.is_dark {
t_max_c(&s.tertiary_palette, 0.0, 93.0, 1.0)
} else {
t_max_c(&s.tertiary_palette, 72.0, 100.0, 1.0)
}
}
_ => {
if s.is_dark {
30.0
} else {
90.0
}
}
}
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
None
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone && s.contrast_level > 0.0 {
Some(get_curve(1.5))
} else {
None
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Watch {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.tertiary_container(),
ColorSpecDelegateImpl2025.tertiary_dim().unwrap(),
10.0,
TonePolarity::Darker,
true,
DeltaConstraint::Farther,
))
} else {
None
}
}),
)
}
fn on_tertiary_container(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_tertiary_container",
|s: &DynamicScheme| s.tertiary_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 90.0 } else { 10.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.tertiary_container())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(6.0))
} else {
Some(get_curve(7.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn error(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"error",
|s: &DynamicScheme| s.error_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
if s.is_dark {
t_min_c(&s.error_palette, 0.0, 98.0)
} else {
t_max_c(&s.error_palette, 0.0, 100.0, 1.0)
}
} else {
t_min_c(&s.error_palette, 0.0, 100.0)
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
Some(delegate_ref.surface_container_high())
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(4.5))
} else {
Some(get_curve(7.0))
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.error_container(),
ColorSpecDelegateImpl2025.error(),
5.0,
TonePolarity::Lighter,
true,
DeltaConstraint::Farther,
))
} else {
None
}
}),
)
}
fn error_dim(&self) -> Option<DynamicColor> {
let delegate_ref = ColorSpecDelegateImpl2025;
Some(DynamicColor::from_palette(
"error_dim",
|s: &DynamicScheme| s.error_palette.clone(),
Some(|s: &DynamicScheme| t_min_c(&s.error_palette, 0.0, 100.0)),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.surface_container_high())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
Some(move |_s: &DynamicScheme| {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.error_dim().unwrap(),
ColorSpecDelegateImpl2025.error(),
5.0,
TonePolarity::Darker,
true,
DeltaConstraint::Farther,
))
}),
))
}
fn on_error(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_error",
|s: &DynamicScheme| s.error_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 100.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.error())
} else {
delegate_ref.error_dim()
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(6.0))
} else {
Some(get_curve(7.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn error_container(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"error_container",
|s: &DynamicScheme| s.error_palette.clone(),
Some(|s: &DynamicScheme| {
if s.platform == Platform::Watch {
30.0
} else if s.is_dark {
t_min_c(&s.error_palette, 30.0, 93.0)
} else {
t_max_c(&s.error_palette, 0.0, 90.0, 1.0)
}
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
None
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone && s.contrast_level > 0.0 {
Some(get_curve(1.5))
} else {
None
}
}),
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Watch {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.error_container(),
ColorSpecDelegateImpl2025.error_dim().unwrap(),
10.0,
TonePolarity::Darker,
true,
DeltaConstraint::Farther,
))
} else {
None
}
}),
)
}
fn on_error_container(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_error_container",
|s: &DynamicScheme| s.error_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 90.0 } else { 10.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.error_container())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(get_curve(4.5))
} else {
Some(get_curve(7.0))
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn primary_fixed(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"primary_fixed",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(move |s: &DynamicScheme| {
let light_scheme = DynamicScheme {
is_dark: false,
contrast_level: 0.0,
..s.clone()
};
delegate_ref.primary_container().tone.as_ref()(&light_scheme)
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
None
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone && s.contrast_level > 0.0 {
Some(get_curve(1.5))
} else {
None
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn primary_fixed_dim(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"primary_fixed_dim",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(move |s: &DynamicScheme| delegate_ref.primary_fixed().tone.as_ref()(s)),
true,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
Some(move |_s: &DynamicScheme| {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.primary_fixed_dim(),
ColorSpecDelegateImpl2025.primary_fixed(),
5.0,
TonePolarity::Darker,
true,
DeltaConstraint::Exact,
))
}),
)
}
fn on_primary_fixed(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_primary_fixed",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 10.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.primary_fixed_dim())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(7.0))),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn on_primary_fixed_variant(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_primary_fixed_variant",
|s: &DynamicScheme| s.primary_palette.clone(),
Some(|s: &DynamicScheme| if s.is_dark { 30.0 } else { 30.0 }),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.primary_fixed_dim())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn secondary_fixed(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"secondary_fixed",
|s: &DynamicScheme| s.secondary_palette.clone(),
Some(move |s: &DynamicScheme| {
let light_scheme = DynamicScheme {
is_dark: false,
contrast_level: 0.0,
..s.clone()
};
delegate_ref.secondary_container().tone.as_ref()(&light_scheme)
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
None
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone && s.contrast_level > 0.0 {
Some(get_curve(1.5))
} else {
None
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn secondary_fixed_dim(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"secondary_fixed_dim",
|s: &DynamicScheme| s.secondary_palette.clone(),
Some(move |s: &DynamicScheme| delegate_ref.secondary_fixed().tone.as_ref()(s)),
true,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
Some(move |_s: &DynamicScheme| {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.secondary_fixed_dim(),
ColorSpecDelegateImpl2025.secondary_fixed(),
5.0,
TonePolarity::Darker,
true,
DeltaConstraint::Exact,
))
}),
)
}
fn on_secondary_fixed(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_secondary_fixed",
|s: &DynamicScheme| s.secondary_palette.clone(),
Some(|_s: &DynamicScheme| 10.0),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.secondary_fixed_dim())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(7.0))),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn on_secondary_fixed_variant(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_secondary_fixed_variant",
|s: &DynamicScheme| s.secondary_palette.clone(),
Some(|_s: &DynamicScheme| 30.0),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.secondary_fixed_dim())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn tertiary_fixed(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"tertiary_fixed",
|s: &DynamicScheme| s.tertiary_palette.clone(),
Some(move |s: &DynamicScheme| {
let light_scheme = DynamicScheme {
is_dark: false,
contrast_level: 0.0,
..s.clone()
};
delegate_ref.tertiary_container().tone.as_ref()(&light_scheme)
}),
true,
None::<fn(&DynamicScheme) -> f64>,
Some(move |s: &DynamicScheme| {
if s.platform == Platform::Phone {
Some(delegate_ref.highest_surface(s))
} else {
None
}
}),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|s: &DynamicScheme| {
if s.platform == Platform::Phone && s.contrast_level > 0.0 {
Some(get_curve(1.5))
} else {
None
}
}),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn tertiary_fixed_dim(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"tertiary_fixed_dim",
|s: &DynamicScheme| s.tertiary_palette.clone(),
Some(move |s: &DynamicScheme| delegate_ref.tertiary_fixed().tone.as_ref()(s)),
true,
None::<fn(&DynamicScheme) -> f64>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
Some(move |_s: &DynamicScheme| {
Some(ToneDeltaPair::new(
ColorSpecDelegateImpl2025.tertiary_fixed_dim(),
ColorSpecDelegateImpl2025.tertiary_fixed(),
5.0,
TonePolarity::Darker,
true,
DeltaConstraint::Exact,
))
}),
)
}
fn on_tertiary_fixed(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_tertiary_fixed",
|s: &DynamicScheme| s.tertiary_palette.clone(),
Some(|_s: &DynamicScheme| 10.0),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.tertiary_fixed_dim())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(7.0))),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
fn on_tertiary_fixed_variant(&self) -> DynamicColor {
let delegate_ref = ColorSpecDelegateImpl2025;
DynamicColor::from_palette(
"on_tertiary_fixed_variant",
|s: &DynamicScheme| s.tertiary_palette.clone(),
Some(|_s: &DynamicScheme| 30.0),
false,
None::<fn(&DynamicScheme) -> f64>,
Some(move |_s: &DynamicScheme| Some(delegate_ref.tertiary_fixed_dim())),
None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use mcu_hct::Hct;
use mcu_palettes::TonalPalette;
fn create_test_scheme(is_dark: bool, variant: Variant) -> DynamicScheme {
let source = Hct::from(200.0, 40.0, 50.0);
DynamicScheme {
source_color_hct: source,
source_color_argb: source.to_int(),
variant,
contrast_level: 0.0,
is_dark,
platform: Platform::Phone,
spec_version: crate::SpecVersion::Spec2025,
primary_palette: TonalPalette::from_hue_and_chroma(200.0, 40.0),
secondary_palette: TonalPalette::from_hue_and_chroma(220.0, 30.0),
tertiary_palette: TonalPalette::from_hue_and_chroma(180.0, 35.0),
neutral_palette: TonalPalette::from_hue_and_chroma(200.0, 8.0),
neutral_variant_palette: TonalPalette::from_hue_and_chroma(200.0, 12.0),
error_palette: TonalPalette::from_hue_and_chroma(25.0, 84.0),
}
}
#[test]
fn test_all_methods_implemented() {
let delegate = ColorSpecDelegateImpl2025;
let scheme = create_test_scheme(false, Variant::TonalSpot);
assert!(delegate.primary().get_argb(&scheme) != 0);
assert!(delegate.secondary().get_argb(&scheme) != 0);
assert!(delegate.tertiary().get_argb(&scheme) != 0);
assert!(delegate.error().get_argb(&scheme) != 0);
assert!(delegate.surface().get_argb(&scheme) != 0);
}
#[test]
fn test_dim_variants_return_some() {
let delegate = ColorSpecDelegateImpl2025;
assert!(delegate.primary_dim().is_some());
assert!(delegate.secondary_dim().is_some());
assert!(delegate.tertiary_dim().is_some());
assert!(delegate.error_dim().is_some());
}
#[test]
fn test_dim_variants_resolve_colors() {
let delegate = ColorSpecDelegateImpl2025;
let scheme = create_test_scheme(false, Variant::TonalSpot);
if let Some(primary_dim) = delegate.primary_dim() {
let argb = primary_dim.get_argb(&scheme);
assert_ne!(argb, 0, "primary_dim should resolve to valid color");
}
if let Some(secondary_dim) = delegate.secondary_dim() {
let argb = secondary_dim.get_argb(&scheme);
assert_ne!(argb, 0, "secondary_dim should resolve to valid color");
}
if let Some(tertiary_dim) = delegate.tertiary_dim() {
let argb = tertiary_dim.get_argb(&scheme);
assert_ne!(argb, 0, "tertiary_dim should resolve to valid color");
}
if let Some(error_dim) = delegate.error_dim() {
let argb = error_dim.get_argb(&scheme);
assert_ne!(argb, 0, "error_dim should resolve to valid color");
}
}
fn create_watch_scheme(is_dark: bool, variant: Variant) -> DynamicScheme {
let source = Hct::from(200.0, 40.0, 50.0);
DynamicScheme {
source_color_hct: source,
source_color_argb: source.to_int(),
variant,
contrast_level: 0.0,
is_dark,
platform: Platform::Watch,
spec_version: crate::SpecVersion::Spec2025,
primary_palette: TonalPalette::from_hue_and_chroma(200.0, 40.0),
secondary_palette: TonalPalette::from_hue_and_chroma(220.0, 30.0),
tertiary_palette: TonalPalette::from_hue_and_chroma(180.0, 35.0),
neutral_palette: TonalPalette::from_hue_and_chroma(200.0, 8.0),
neutral_variant_palette: TonalPalette::from_hue_and_chroma(200.0, 12.0),
error_palette: TonalPalette::from_hue_and_chroma(25.0, 84.0),
}
}
#[test]
fn test_dim_variants_resolve_colors_watch_platform() {
let delegate = ColorSpecDelegateImpl2025;
let scheme = create_watch_scheme(false, Variant::TonalSpot);
let on_primary = delegate.on_primary();
let argb = on_primary.get_argb(&scheme);
assert_ne!(argb, 0, "on_primary should resolve to valid color on Watch");
let primary_container = delegate.primary_container();
let argb = primary_container.get_argb(&scheme);
assert_ne!(argb, 0, "primary_container should resolve to valid color on Watch");
if let Some(primary_dim) = delegate.primary_dim() {
let argb = primary_dim.get_argb(&scheme);
assert_ne!(argb, 0, "primary_dim should resolve to valid color on Watch");
}
}
#[test]
fn test_key_colors_resolve_correctly() {
let delegate = ColorSpecDelegateImpl2025;
let light_scheme = create_test_scheme(false, Variant::TonalSpot);
let dark_scheme = create_test_scheme(true, Variant::TonalSpot);
let primary_light = delegate.primary().get_argb(&light_scheme);
let surface_light = delegate.surface().get_argb(&light_scheme);
assert_ne!(primary_light, 0);
assert_ne!(surface_light, 0);
assert_ne!(primary_light, surface_light);
let primary_dark = delegate.primary().get_argb(&dark_scheme);
let surface_dark = delegate.surface().get_argb(&dark_scheme);
assert_ne!(primary_dark, 0);
assert_ne!(surface_dark, 0);
assert_ne!(primary_dark, surface_dark);
assert_ne!(primary_light, primary_dark);
}
#[test]
fn test_surface_colors_differ_by_elevation() {
let delegate = ColorSpecDelegateImpl2025;
let scheme = create_test_scheme(false, Variant::TonalSpot);
let container_lowest = delegate.surface_container_lowest().get_argb(&scheme);
let container_low = delegate.surface_container_low().get_argb(&scheme);
let container = delegate.surface_container().get_argb(&scheme);
let container_high = delegate.surface_container_high().get_argb(&scheme);
let container_highest = delegate.surface_container_highest().get_argb(&scheme);
assert_ne!(container_lowest, container_low);
assert_ne!(container_low, container);
assert_ne!(container, container_high);
assert_ne!(container_high, container_highest);
}
#[test]
fn test_variants_produce_different_results() {
let delegate = ColorSpecDelegateImpl2025;
let neutral = create_test_scheme(false, Variant::Neutral);
let tonal_spot = create_test_scheme(false, Variant::TonalSpot);
let vibrant = create_test_scheme(false, Variant::Vibrant);
let expressive = create_test_scheme(false, Variant::Expressive);
let primary_neutral = delegate.primary().get_argb(&neutral);
let primary_tonal_spot = delegate.primary().get_argb(&tonal_spot);
let primary_vibrant = delegate.primary().get_argb(&vibrant);
let primary_expressive = delegate.primary().get_argb(&expressive);
assert_ne!(primary_neutral, primary_tonal_spot);
assert!(primary_vibrant != 0);
assert!(primary_expressive != 0);
}
}