#![warn(unused_must_use)]
use crate as css;
use crate::PrintErr;
use crate::Printer;
use crate::css_values::image::Image;
use crate::css_values::length::LengthOrNumber;
use crate::css_values::length::LengthPercentage;
use crate::css_values::position::Position;
use crate::css_values::rect::Rect;
use crate::css_properties::border_radius::BorderRadius;
use crate::css_properties::shape::FillRule;
use crate::css_properties::background::BackgroundRepeat;
use crate::css_properties::background::BackgroundSize;
use crate::css_properties::border_image::BorderImage;
use crate::css_properties::border_image::BorderImageRepeat;
use crate::css_properties::border_image::BorderImageSideWidth;
use crate::css_properties::border_image::BorderImageSlice;
use crate::VendorPrefix;
use crate::generics::{CssEql, DeepClone};
use crate::properties::PropertyId;
use crate::properties::PropertyIdTag;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, css::Parse, css::ToCss)]
pub enum GeometryBox {
#[css(name = "border-box")]
#[default]
BorderBox,
#[css(name = "padding-box")]
PaddingBox,
#[css(name = "content-box")]
ContentBox,
#[css(name = "margin-box")]
MarginBox,
#[css(name = "fill-box")]
FillBox,
#[css(name = "stroke-box")]
StrokeBox,
#[css(name = "view-box")]
ViewBox,
}
impl GeometryBox {
pub fn into_mask_clip(self) -> MaskClip {
MaskClip::GeometryBox(self)
}
}
pub enum BasicShape {
Inset(InsetRect),
Circle(Circle),
Ellipse(Ellipse),
Polygon(Polygon),
}
pub struct InsetRect {
pub rect: Rect<LengthPercentage>,
pub radius: BorderRadius,
}
pub struct Circle {
pub radius: ShapeRadius,
pub position: Position,
}
pub struct Ellipse {
pub radius_x: ShapeRadius,
pub radius_y: ShapeRadius,
pub position: Position,
}
pub struct Polygon {
pub fill_rule: FillRule,
pub points: Vec<Point>,
}
pub enum ShapeRadius {
LengthPercentage(LengthPercentage),
ClosestSide,
FarthestSide,
}
pub struct Point {
pub x: LengthPercentage,
pub y: LengthPercentage,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, css::Parse, css::ToCss)]
pub enum MaskMode {
#[css(name = "luminance")]
Luminance,
#[css(name = "alpha")]
Alpha,
#[css(name = "match-source")]
#[default]
MatchSource,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, css::Parse, css::ToCss)]
pub enum MaskClip {
GeometryBox(GeometryBox),
#[css(name = "no-clip")]
NoClip,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, css::Parse, css::ToCss)]
pub enum MaskComposite {
#[css(name = "add")]
#[default]
Add,
#[css(name = "subtract")]
Subtract,
#[css(name = "intersect")]
Intersect,
#[css(name = "exclude")]
Exclude,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, css::Parse, css::ToCss)]
pub enum MaskType {
#[css(name = "luminance")]
Luminance,
#[css(name = "alpha")]
Alpha,
}
#[cfg_attr(any(), derive(Debug, Clone, PartialEq))]
#[derive(DeepClone, CssEql)]
pub struct Mask {
pub image: Image,
pub position: Position,
pub size: BackgroundSize,
pub repeat: BackgroundRepeat,
pub clip: MaskClip,
pub origin: GeometryBox,
pub composite: MaskComposite,
pub mode: MaskMode,
}
impl Mask {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("image", PropertyIdTag::MaskImage),
("position", PropertyIdTag::MaskPosition),
("size", PropertyIdTag::MaskSize),
("repeat", PropertyIdTag::MaskRepeat),
("clip", PropertyIdTag::MaskClip),
("origin", PropertyIdTag::MaskOrigin),
("composite", PropertyIdTag::MaskComposite),
("mode", PropertyIdTag::MaskMode),
];
pub const VENDOR_PREFIX_MAP: &'static [&'static str] =
&["image", "position", "size", "repeat", "clip", "origin"];
pub fn parse(input: &mut css::Parser) -> css::Result<Self> {
let mut image: Option<Image> = None;
let mut position: Option<Position> = None;
let mut size: Option<BackgroundSize> = None;
let mut repeat: Option<BackgroundRepeat> = None;
let mut clip: Option<MaskClip> = None;
let mut origin: Option<GeometryBox> = None;
let mut composite: Option<MaskComposite> = None;
let mut mode: Option<MaskMode> = None;
loop {
if image.is_none() {
if let Ok(value) = input.try_parse(Image::parse) {
image = Some(value);
continue;
}
}
if position.is_none() {
if let Ok(value) = input.try_parse(Position::parse) {
position = Some(value);
size = input
.try_parse(|i: &mut css::Parser| -> css::Result<BackgroundSize> {
i.expect_delim(b'/')?;
BackgroundSize::parse(i)
})
.ok();
continue;
}
}
if repeat.is_none() {
if let Ok(value) = input.try_parse(BackgroundRepeat::parse) {
repeat = Some(value);
continue;
}
}
if origin.is_none() {
if let Ok(value) = input.try_parse(GeometryBox::parse) {
origin = Some(value);
continue;
}
}
if clip.is_none() {
if let Ok(value) = input.try_parse(MaskClip::parse) {
clip = Some(value);
continue;
}
}
if composite.is_none() {
if let Ok(value) = input.try_parse(MaskComposite::parse) {
composite = Some(value);
continue;
}
}
if mode.is_none() {
if let Ok(value) = input.try_parse(MaskMode::parse) {
mode = Some(value);
continue;
}
}
break;
}
if clip.is_none() {
if let Some(o) = origin {
clip = Some(o.into_mask_clip());
}
}
Ok(Self {
image: image.unwrap_or_default(),
position: position.unwrap_or_default(),
repeat: repeat.unwrap_or_else(BackgroundRepeat::default),
size: size.unwrap_or_else(BackgroundSize::default),
origin: origin.unwrap_or(GeometryBox::BorderBox),
clip: clip.unwrap_or_else(|| GeometryBox::BorderBox.into_mask_clip()),
composite: composite.unwrap_or(MaskComposite::Add),
mode: mode.unwrap_or(MaskMode::MatchSource),
})
}
pub fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
self.image.to_css(dest)?;
if self.position != Position::default() || self.size != BackgroundSize::default() {
dest.write_char(b' ')?;
self.position.to_css(dest)?;
if self.size != BackgroundSize::default() {
dest.delim(b'/', true)?;
self.size.to_css(dest)?;
}
}
if self.repeat != BackgroundRepeat::default() {
dest.write_char(b' ')?;
self.repeat.to_css(dest)?;
}
if self.origin != GeometryBox::BorderBox
|| self.clip != GeometryBox::BorderBox.into_mask_clip()
{
dest.write_char(b' ')?;
self.origin.to_css(dest)?;
if self.clip != self.origin.into_mask_clip() {
dest.write_char(b' ')?;
self.clip.to_css(dest)?;
}
}
if self.composite != MaskComposite::default() {
dest.write_char(b' ')?;
self.composite.to_css(dest)?;
}
if self.mode != MaskMode::default() {
dest.write_char(b' ')?;
self.mode.to_css(dest)?;
}
Ok(())
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, css::Parse, css::ToCss)]
pub enum MaskBorderMode {
#[css(name = "luminance")]
Luminance,
#[css(name = "alpha")]
#[default]
Alpha,
}
#[cfg_attr(any(), derive(Debug, Clone, PartialEq))]
#[derive(DeepClone, CssEql)]
pub struct MaskBorder {
pub source: Image,
pub slice: BorderImageSlice,
pub width: Rect<BorderImageSideWidth>,
pub outset: Rect<LengthOrNumber>,
pub repeat: BorderImageRepeat,
pub mode: MaskBorderMode,
}
impl MaskBorder {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("source", PropertyIdTag::MaskBorderSource),
("slice", PropertyIdTag::MaskBorderSlice),
("width", PropertyIdTag::MaskBorderWidth),
("outset", PropertyIdTag::MaskBorderOutset),
("repeat", PropertyIdTag::MaskBorderRepeat),
("mode", PropertyIdTag::MaskBorderMode),
];
pub fn parse(input: &mut css::Parser) -> css::Result<Self> {
let mut mode: Option<MaskBorderMode> = None;
let border_image = BorderImage::parse_with_callback(input, |p: &mut css::Parser| -> bool {
if mode.is_none() {
if let Ok(value) = p.try_parse(MaskBorderMode::parse) {
mode = Some(value);
return true;
}
}
false
});
if border_image.is_ok() || mode.is_some() {
let bi = border_image.unwrap_or_else(|_| BorderImage::default());
Ok(MaskBorder {
source: bi.source,
slice: bi.slice,
width: bi.width,
outset: bi.outset,
repeat: bi.repeat,
mode: mode.unwrap_or_default(),
})
} else {
Err(input.new_custom_error(css::ParserError::invalid_declaration))
}
}
pub fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
BorderImage::to_css_internal(
&self.source,
&self.slice,
&self.width,
&self.outset,
&self.repeat,
dest,
)?;
if self.mode != MaskBorderMode::default() {
dest.write_char(b' ')?;
self.mode.to_css(dest)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, css::Parse, css::ToCss)]
pub enum WebKitMaskComposite {
#[css(name = "clear")]
Clear,
#[css(name = "copy")]
Copy,
#[css(name = "source-over")]
SourceOver,
#[css(name = "source-in")]
SourceIn,
#[css(name = "source-out")]
SourceOut,
#[css(name = "source-atop")]
SourceAtop,
#[css(name = "destination-over")]
DestinationOver,
#[css(name = "destination-in")]
DestinationIn,
#[css(name = "destination-out")]
DestinationOut,
#[css(name = "destination-atop")]
DestinationAtop,
#[css(name = "xor")]
Xor,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, css::Parse, css::ToCss)]
pub enum WebKitMaskSourceType {
#[css(name = "auto")]
Auto,
#[css(name = "luminance")]
Luminance,
#[css(name = "alpha")]
Alpha,
}
pub fn get_webkit_mask_property(property_id: &PropertyId) -> Option<PropertyId> {
match property_id {
PropertyId::MaskBorderSource => Some(PropertyId::MaskBoxImageSource(VendorPrefix::WEBKIT)),
PropertyId::MaskBorderSlice => Some(PropertyId::MaskBoxImageSlice(VendorPrefix::WEBKIT)),
PropertyId::MaskBorderWidth => Some(PropertyId::MaskBoxImageWidth(VendorPrefix::WEBKIT)),
PropertyId::MaskBorderOutset => Some(PropertyId::MaskBoxImageOutset(VendorPrefix::WEBKIT)),
PropertyId::MaskBorderRepeat => Some(PropertyId::MaskBoxImageRepeat(VendorPrefix::WEBKIT)),
PropertyId::MaskBorder => Some(PropertyId::MaskBoxImage(VendorPrefix::WEBKIT)),
PropertyId::MaskComposite => Some(PropertyId::WebKitMaskComposite),
PropertyId::MaskMode => Some(PropertyId::MaskSourceType(VendorPrefix::WEBKIT)),
_ => None,
}
}