use super::*;
fn parse_color_space(s: Option<&str>) -> InterpolationColorSpace {
match s {
Some("oklch") => InterpolationColorSpace::Oklcha,
Some("oklchLong") => InterpolationColorSpace::OklchaLong,
Some("srgb") => InterpolationColorSpace::Srgba,
Some("linearRgb") => InterpolationColorSpace::LinearRgba,
Some("hsl") => InterpolationColorSpace::Hsla,
Some("hslLong") => InterpolationColorSpace::HslaLong,
Some("hsv") => InterpolationColorSpace::Hsva,
Some("hsvLong") => InterpolationColorSpace::HsvaLong,
_ => InterpolationColorSpace::Oklaba,
}
}
fn parse_position(s: Option<&str>) -> UiPosition {
match s {
Some("top") => UiPosition::TOP,
Some("bottom") => UiPosition::BOTTOM,
Some("left") => UiPosition::LEFT,
Some("right") => UiPosition::RIGHT,
Some("topLeft") => UiPosition::TOP_LEFT,
Some("topRight") => UiPosition::TOP_RIGHT,
Some("bottomLeft") => UiPosition::BOTTOM_LEFT,
Some("bottomRight") => UiPosition::BOTTOM_RIGHT,
_ => UiPosition::CENTER,
}
}
fn parse_radial_shape(shape: Option<&RadialShapeSpec>) -> RadialGradientShape {
fn radius(a: &Animatable<Length>) -> Val {
length_to_val(a.value().or_else(|| a.seed()).copied().unwrap_or_default())
}
match shape {
Some(RadialShapeSpec::Circle { circle }) => RadialGradientShape::Circle(radius(circle)),
Some(RadialShapeSpec::Ellipse { ellipse }) => {
RadialGradientShape::Ellipse(radius(&ellipse[0]), radius(&ellipse[1]))
}
Some(RadialShapeSpec::Keyword(k)) => match k.as_str() {
"closestSide" => RadialGradientShape::ClosestSide,
"farthestSide" => RadialGradientShape::FarthestSide,
"farthestCorner" => RadialGradientShape::FarthestCorner,
_ => RadialGradientShape::ClosestCorner,
},
None => RadialGradientShape::ClosestCorner,
}
}
fn stop_color(color: &Animatable<String>) -> Color {
match color {
Animatable::Static(s) => parse_color(s),
a => a.seed().map(|s| parse_color(s)).unwrap_or(Color::NONE),
}
}
fn color_stop(stop: &GradientStop, opacity: Option<f32>) -> ColorStop {
ColorStop {
color: apply_opacity(stop_color(&stop.color), opacity),
point: stop
.position
.static_or_seed()
.map(length_to_val)
.unwrap_or(Val::Auto),
hint: stop.hint.static_or_seed().unwrap_or(0.5),
}
}
fn angular_stop(stop: &AngularStop, opacity: Option<f32>) -> AngularColorStop {
AngularColorStop {
color: apply_opacity(stop_color(&stop.color), opacity),
angle: stop.angle.static_or_seed().map(Angle::radians),
hint: stop.hint.static_or_seed().unwrap_or(0.5),
}
}
fn build_linear(spec: &LinearGradientSpec, opacity: Option<f32>) -> Gradient {
LinearGradient::new(
spec.angle
.static_or_seed()
.map(Angle::radians)
.unwrap_or(0.0),
spec.stops.iter().map(|s| color_stop(s, opacity)).collect(),
)
.in_color_space(parse_color_space(spec.color_space.as_deref()))
.into()
}
fn build_radial(spec: &RadialGradientSpec, opacity: Option<f32>) -> Gradient {
RadialGradient::new(
parse_position(spec.position.as_deref()),
parse_radial_shape(spec.shape.as_ref()),
spec.stops.iter().map(|s| color_stop(s, opacity)).collect(),
)
.in_color_space(parse_color_space(spec.color_space.as_deref()))
.into()
}
fn build_conic(spec: &ConicGradientSpec, opacity: Option<f32>) -> Gradient {
ConicGradient::new(
parse_position(spec.position.as_deref()),
spec.stops
.iter()
.map(|s| angular_stop(s, opacity))
.collect(),
)
.with_start(
spec.start
.static_or_seed()
.map(Angle::radians)
.unwrap_or(0.0),
)
.in_color_space(parse_color_space(spec.color_space.as_deref()))
.into()
}
fn build_gradient(spec: &GradientSpec, opacity: Option<f32>) -> Gradient {
match spec {
GradientSpec::Linear(l) => build_linear(l, opacity),
GradientSpec::Radial(r) => build_radial(r, opacity),
GradientSpec::Conic(c) => build_conic(c, opacity),
}
}
pub fn build_gradients(list: &GradientList, opacity: Option<f32>) -> Vec<Gradient> {
match list {
GradientList::One(g) => vec![build_gradient(g, opacity)],
GradientList::Many(gs) => gs.iter().map(|g| build_gradient(g, opacity)).collect(),
}
}
#[derive(Component, Debug, Clone, Default, PartialEq)]
pub struct GradientTargets {
pub background: Option<Vec<Gradient>>,
pub border: Option<Vec<Gradient>>,
pub opacity: Option<f32>,
}
pub fn fold_gradients(list: &[Gradient], opacity: Option<f32>) -> Vec<Gradient> {
let Some(o) = opacity else {
return list.to_vec();
};
list.iter()
.map(|g| {
let mut g = g.clone();
match &mut g {
Gradient::Linear(l) => {
for s in &mut l.stops {
s.color = apply_opacity(s.color, Some(o));
}
}
Gradient::Radial(r) => {
for s in &mut r.stops {
s.color = apply_opacity(s.color, Some(o));
}
}
Gradient::Conic(c) => {
for s in &mut c.stops {
s.color = apply_opacity(s.color, Some(o));
}
}
}
g
})
.collect()
}