use crate::color::mix::ColorInterpolationMethod;
use crate::custom_properties;
use crate::values::generics::position::PositionComponent;
use crate::values::generics::Optional;
use crate::values::serialize_atom_identifier;
use crate::Atom;
use crate::Zero;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
#[derive(
Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[repr(C, u8)]
pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {
None,
Url(ImageUrl),
Gradient(Box<G>),
#[cfg(feature = "gecko")]
#[css(function = "-moz-element")]
Element(Atom),
#[cfg(feature = "servo")]
PaintWorklet(PaintWorklet),
CrossFade(Box<GenericCrossFade<Self, Color, Percentage>>),
ImageSet(#[compute(field_bound)] Box<GenericImageSet<Self, Resolution>>),
}
pub use self::GenericImage as Image;
#[derive(
Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue,
)]
#[css(comma, function = "cross-fade")]
#[repr(C)]
pub struct GenericCrossFade<Image, Color, Percentage> {
#[css(iterable)]
pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,
}
#[derive(
Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
)]
#[repr(C)]
pub struct GenericCrossFadeElement<Image, Color, Percentage> {
pub percent: Optional<Percentage>,
pub image: GenericCrossFadeImage<Image, Color>,
}
#[derive(
Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
)]
#[repr(C, u8)]
pub enum GenericCrossFadeImage<I, C> {
Image(I),
Color(C),
}
pub use self::GenericCrossFade as CrossFade;
pub use self::GenericCrossFadeElement as CrossFadeElement;
pub use self::GenericCrossFadeImage as CrossFadeImage;
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
#[css(comma, function = "image-set")]
#[repr(C)]
pub struct GenericImageSet<Image, Resolution> {
#[css(skip)]
pub selected_index: usize,
#[css(iterable)]
pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,
}
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
#[repr(C)]
pub struct GenericImageSetItem<Image, Resolution> {
pub image: Image,
pub resolution: Resolution,
pub mime_type: crate::OwnedStr,
pub has_mime_type: bool,
}
impl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.image.to_css(dest)?;
dest.write_char(' ')?;
self.resolution.to_css(dest)?;
if self.has_mime_type {
dest.write_char(' ')?;
dest.write_str("type(")?;
self.mime_type.to_css(dest)?;
dest.write_char(')')?;
}
Ok(())
}
}
pub use self::GenericImageSet as ImageSet;
pub use self::GenericImageSetItem as ImageSetItem;
#[derive(
Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[repr(C)]
pub struct GradientFlags(u8);
bitflags! {
impl GradientFlags: u8 {
const REPEATING = 1 << 0;
const HAS_DEFAULT_COLOR_INTERPOLATION_METHOD = 1 << 1;
}
}
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
#[repr(C)]
pub enum GenericGradient<
LineDirection,
LengthPercentage,
NonNegativeLength,
NonNegativeLengthPercentage,
Position,
Angle,
AngleOrPercentage,
Color,
> {
Linear {
direction: LineDirection,
color_interpolation_method: ColorInterpolationMethod,
items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
flags: GradientFlags,
compat_mode: GradientCompatMode,
},
Radial {
shape: GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>,
position: Position,
color_interpolation_method: ColorInterpolationMethod,
items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
flags: GradientFlags,
compat_mode: GradientCompatMode,
},
Conic {
angle: Angle,
position: Position,
color_interpolation_method: ColorInterpolationMethod,
items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>,
flags: GradientFlags,
},
}
pub use self::GenericGradient as Gradient;
#[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[repr(u8)]
pub enum GradientCompatMode {
Modern,
WebKit,
Moz,
}
#[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
)]
#[repr(C, u8)]
pub enum GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage> {
Circle(GenericCircle<NonNegativeLength>),
Ellipse(GenericEllipse<NonNegativeLengthPercentage>),
}
pub use self::GenericEndingShape as EndingShape;
#[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[repr(C, u8)]
pub enum GenericCircle<NonNegativeLength> {
Radius(NonNegativeLength),
Extent(ShapeExtent),
}
pub use self::GenericCircle as Circle;
#[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
)]
#[repr(C, u8)]
pub enum GenericEllipse<NonNegativeLengthPercentage> {
Radii(NonNegativeLengthPercentage, NonNegativeLengthPercentage),
Extent(ShapeExtent),
}
pub use self::GenericEllipse as Ellipse;
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ShapeExtent {
ClosestSide,
FarthestSide,
ClosestCorner,
FarthestCorner,
Contain,
Cover,
}
#[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
)]
#[repr(C, u8)]
pub enum GenericGradientItem<Color, T> {
SimpleColorStop(Color),
ComplexColorStop {
color: Color,
position: T,
},
InterpolationHint(T),
}
pub use self::GenericGradientItem as GradientItem;
#[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
)]
pub struct ColorStop<Color, T> {
pub color: Color,
pub position: Option<T>,
}
impl<Color, T> ColorStop<Color, T> {
#[inline]
pub fn into_item(self) -> GradientItem<Color, T> {
match self.position {
Some(position) => GradientItem::ComplexColorStop {
color: self.color,
position,
},
None => GradientItem::SimpleColorStop(self.color),
}
}
}
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
#[derive(Clone, Debug, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
pub struct PaintWorklet {
pub name: Atom,
#[compute(no_field_bound)]
#[resolve(no_field_bound)]
pub arguments: Vec<custom_properties::SpecifiedValue>,
}
impl ::style_traits::SpecifiedValueInfo for PaintWorklet {}
impl ToCss for PaintWorklet {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
dest.write_str("paint(")?;
serialize_atom_identifier(&self.name, dest)?;
for argument in &self.arguments {
dest.write_str(", ")?;
argument.to_css(dest)?;
}
dest.write_char(')')
}
}
impl<G, U, C, P, Resolution> fmt::Debug for Image<G, U, C, P, Resolution>
where
Image<G, U, C, P, Resolution>: ToCss,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_css(&mut CssWriter::new(f))
}
}
impl<G, U, C, P, Resolution> ToCss for Image<G, U, C, P, Resolution>
where
G: ToCss,
U: ToCss,
C: ToCss,
P: ToCss,
Resolution: ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match *self {
Image::None => dest.write_str("none"),
Image::Url(ref url) => url.to_css(dest),
Image::Gradient(ref gradient) => gradient.to_css(dest),
#[cfg(feature = "servo")]
Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
#[cfg(feature = "gecko")]
Image::Element(ref selector) => {
dest.write_str("-moz-element(#")?;
serialize_atom_identifier(selector, dest)?;
dest.write_char(')')
},
Image::ImageSet(ref is) => is.to_css(dest),
Image::CrossFade(ref cf) => cf.to_css(dest),
}
}
}
impl<D, LP, NL, NLP, P, A: Zero, AoP, C> ToCss for Gradient<D, LP, NL, NLP, P, A, AoP, C>
where
D: LineDirection,
LP: ToCss,
NL: ToCss,
NLP: ToCss,
P: PositionComponent + ToCss,
A: ToCss,
AoP: ToCss,
C: ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
let (compat_mode, repeating, has_default_color_interpolation_method) = match *self {
Gradient::Linear {
compat_mode, flags, ..
} |
Gradient::Radial {
compat_mode, flags, ..
} => (
compat_mode,
flags.contains(GradientFlags::REPEATING),
flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
),
Gradient::Conic { flags, .. } => (
GradientCompatMode::Modern,
flags.contains(GradientFlags::REPEATING),
flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
),
};
match compat_mode {
GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
GradientCompatMode::Moz => dest.write_str("-moz-")?,
_ => {},
}
if repeating {
dest.write_str("repeating-")?;
}
match *self {
Gradient::Linear {
ref direction,
ref color_interpolation_method,
ref items,
compat_mode,
..
} => {
dest.write_str("linear-gradient(")?;
let mut skip_comma = true;
if !direction.points_downwards(compat_mode) {
direction.to_css(dest, compat_mode)?;
skip_comma = false;
}
if !has_default_color_interpolation_method {
if !skip_comma {
dest.write_char(' ')?;
}
color_interpolation_method.to_css(dest)?;
skip_comma = false;
}
for item in &**items {
if !skip_comma {
dest.write_str(", ")?;
}
skip_comma = false;
item.to_css(dest)?;
}
},
Gradient::Radial {
ref shape,
ref position,
ref color_interpolation_method,
ref items,
compat_mode,
..
} => {
dest.write_str("radial-gradient(")?;
let omit_shape = match *shape {
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
_ => false,
};
let omit_position = position.is_center();
if compat_mode == GradientCompatMode::Modern {
if !omit_shape {
shape.to_css(dest)?;
if !omit_position {
dest.write_char(' ')?;
}
}
if !omit_position {
dest.write_str("at ")?;
position.to_css(dest)?;
}
} else {
if !omit_position {
position.to_css(dest)?;
if !omit_shape {
dest.write_str(", ")?;
}
}
if !omit_shape {
shape.to_css(dest)?;
}
}
if !has_default_color_interpolation_method {
if !omit_shape || !omit_position {
dest.write_char(' ')?;
}
color_interpolation_method.to_css(dest)?;
}
let mut skip_comma =
omit_shape && omit_position && has_default_color_interpolation_method;
for item in &**items {
if !skip_comma {
dest.write_str(", ")?;
}
skip_comma = false;
item.to_css(dest)?;
}
},
Gradient::Conic {
ref angle,
ref position,
ref color_interpolation_method,
ref items,
..
} => {
dest.write_str("conic-gradient(")?;
let omit_angle = angle.is_zero();
let omit_position = position.is_center();
if !omit_angle {
dest.write_str("from ")?;
angle.to_css(dest)?;
if !omit_position {
dest.write_char(' ')?;
}
}
if !omit_position {
dest.write_str("at ")?;
position.to_css(dest)?;
}
if !has_default_color_interpolation_method {
if !omit_angle || !omit_position {
dest.write_char(' ')?;
}
color_interpolation_method.to_css(dest)?;
}
let mut skip_comma =
omit_angle && omit_position && has_default_color_interpolation_method;
for item in &**items {
if !skip_comma {
dest.write_str(", ")?;
}
skip_comma = false;
item.to_css(dest)?;
}
},
}
dest.write_char(')')
}
}
pub trait LineDirection {
fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool;
fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
where
W: Write;
}
impl<L> ToCss for Circle<L>
where
L: ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match *self {
Circle::Extent(ShapeExtent::FarthestCorner) | Circle::Extent(ShapeExtent::Cover) => {
dest.write_str("circle")
},
Circle::Extent(keyword) => {
dest.write_str("circle ")?;
keyword.to_css(dest)
},
Circle::Radius(ref length) => length.to_css(dest),
}
}
}