mod animation;
mod aspect_ratio;
mod background;
mod background_image;
mod background_position;
mod background_repeat;
mod background_size;
mod blend_mode;
mod border;
mod box_shadow;
mod clip_path;
mod color;
mod conic_gradient;
mod filter;
mod flex;
mod flex_grow;
mod font_family;
mod font_feature_settings;
mod font_size;
mod font_stretch;
mod font_style;
mod font_synthesis;
mod font_variation_settings;
mod font_weight;
mod gradient_utils;
mod grid;
mod length;
mod line_clamp;
mod line_height;
mod linear_gradient;
mod noise_v1;
mod overflow;
mod overflow_wrap;
mod percentage_number;
mod radial_gradient;
mod sides;
mod space_pair;
mod text_decoration;
mod text_overflow;
mod text_shadow;
mod text_stroke;
mod text_wrap;
mod transform;
mod vertical_align;
mod white_space;
mod word_break;
pub use animation::*;
pub use aspect_ratio::*;
pub use background::*;
pub use background_image::*;
pub use background_position::*;
pub use background_repeat::*;
pub use background_size::*;
pub use blend_mode::*;
pub use border::*;
pub use box_shadow::*;
pub use clip_path::*;
pub use color::*;
pub use conic_gradient::*;
pub use filter::*;
pub use flex::*;
pub use flex_grow::*;
pub use font_family::*;
pub use font_feature_settings::*;
pub use font_size::*;
pub use font_stretch::*;
pub use font_style::*;
pub use font_synthesis::*;
pub use font_variation_settings::*;
pub use font_weight::*;
pub(crate) use gradient_utils::{
GradientOverlayTile, compute_overlay_bounds, overlay_gradient_tile_fast_normal_unconstrained,
};
pub use grid::*;
pub use length::*;
pub use line_clamp::*;
pub use line_height::*;
pub use linear_gradient::*;
pub use noise_v1::*;
pub use overflow::*;
pub use overflow_wrap::*;
pub use percentage_number::*;
pub use radial_gradient::*;
pub use sides::*;
pub use space_pair::*;
pub use text_decoration::*;
pub use text_overflow::*;
pub use text_shadow::*;
pub use text_stroke::*;
pub use text_wrap::*;
pub use transform::*;
pub use vertical_align::*;
pub use white_space::*;
pub use word_break::*;
use cssparser::{
ParseError, ParseErrorKind, Parser, ParserInput, SourceLocation, ToCss, Token,
match_ignore_ascii_case,
};
use fast_image_resize::ResizeAlg;
use image::imageops::FilterType;
use parley::Alignment;
use std::borrow::Cow;
use zeno::Join;
use crate::layout::style::tw::TailwindPropertyParser;
use crate::rendering::Sizing;
pub type ParseResult<'i, T> = Result<T, ParseError<'i, Cow<'i, str>>>;
pub enum CssToken {
Keyword(&'static str),
Token(&'static str),
}
impl std::fmt::Display for CssToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CssToken::Keyword(keyword) => write!(f, "'{}'", keyword),
CssToken::Token(token) => write!(f, "<{}>", token),
}
}
}
pub trait FromCss<'i> {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self>
where
Self: Sized;
fn from_str(source: &'i str) -> ParseResult<'i, Self>
where
Self: Sized,
{
let mut input = ParserInput::new(source);
let mut parser = Parser::new(&mut input);
Self::from_css(&mut parser)
}
fn valid_tokens() -> &'static [CssToken];
fn expect_message() -> Cow<'static, str> {
Cow::Owned(format!(
"a value of {}",
merge_enum_values(Self::valid_tokens())
))
}
fn unexpected_token_error(
location: SourceLocation,
token: &Token,
) -> ParseError<'i, Cow<'i, str>> {
#[cfg(feature = "detailed_css_error")]
{
create_unexpected_token_error(location, token, Self::expect_message())
}
#[cfg(not(feature = "detailed_css_error"))]
{
create_unexpected_token_error(location, token)
}
}
}
impl<'i, T: FromCss<'i>> FromCss<'i> for Option<T> {
fn valid_tokens() -> &'static [CssToken] {
T::valid_tokens()
}
fn expect_message() -> Cow<'static, str> {
Cow::Owned(format!("{} or 'none'", T::expect_message()))
}
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
return Ok(None);
}
T::from_css(input).map(Some)
}
}
pub(crate) trait MakeComputed {
fn make_computed(&mut self, _sizing: &Sizing) {}
}
pub(crate) trait Animatable: Sized + Clone {
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
_sizing: &Sizing,
_current_color: Color,
) {
*self = if progress >= 1.0 {
to.clone()
} else {
from.clone()
};
}
fn list_interpolation_strategy() -> ListInterpolationStrategy {
ListInterpolationStrategy::Discrete
}
fn neutral_value_like(_other: &Self) -> Option<Self> {
None
}
fn missing_value() -> Option<Self> {
None
}
}
pub(crate) fn lerp(lhs: f32, rhs: f32, progress: f32) -> f32 {
lhs + (rhs - lhs) * progress
}
pub(crate) enum ListInterpolationStrategy {
Discrete,
RepeatToLcm,
PadToLongestWithNeutral,
}
impl<T: MakeComputed> MakeComputed for Option<T> {
fn make_computed(&mut self, sizing: &Sizing) {
if let Some(value) = self.as_mut() {
value.make_computed(sizing);
}
}
}
impl<T: MakeComputed> MakeComputed for Box<[T]> {
fn make_computed(&mut self, sizing: &Sizing) {
for value in self.iter_mut() {
value.make_computed(sizing);
}
}
}
impl<T: MakeComputed> MakeComputed for Vec<T> {
fn make_computed(&mut self, sizing: &Sizing) {
for value in self.iter_mut() {
value.make_computed(sizing);
}
}
}
pub(crate) fn next_is_comma<'i>(input: &mut Parser<'i, '_>) -> bool {
let state = input.state();
let is_comma = input.expect_comma().is_ok();
input.reset(&state);
is_comma
}
impl<T: Animatable + Clone> Animatable for Option<T> {
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
sizing: &Sizing,
current_color: Color,
) {
*self = match (from, to) {
(Some(from), Some(to)) => {
let mut value = from.clone();
value.interpolate(from, to, progress, sizing, current_color);
Some(value)
}
(Some(from), None) => T::missing_value().map_or_else(
|| {
if progress >= 0.5 {
None
} else {
Some(from.clone())
}
},
|missing| {
let mut value = from.clone();
value.interpolate(from, &missing, progress, sizing, current_color);
Some(value)
},
),
(None, Some(to)) => T::missing_value().map_or_else(
|| {
if progress >= 0.5 {
Some(to.clone())
} else {
None
}
},
|missing| {
let mut value = missing.clone();
value.interpolate(&missing, to, progress, sizing, current_color);
Some(value)
},
),
(None, None) => None,
};
}
}
impl<T: Animatable + Clone> Animatable for Box<[T]> {
fn missing_value() -> Option<Self> {
match T::list_interpolation_strategy() {
ListInterpolationStrategy::Discrete => None,
ListInterpolationStrategy::RepeatToLcm
| ListInterpolationStrategy::PadToLongestWithNeutral => Some(Box::default()),
}
}
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
sizing: &Sizing,
current_color: Color,
) {
*self = interpolate_list(
from,
to,
progress,
sizing,
current_color,
Vec::into_boxed_slice,
)
.unwrap_or_else(|| {
if progress >= 1.0 {
to.clone()
} else {
from.clone()
}
});
}
}
impl<T: Animatable + Clone> Animatable for Vec<T> {
fn missing_value() -> Option<Self> {
match T::list_interpolation_strategy() {
ListInterpolationStrategy::Discrete => None,
ListInterpolationStrategy::RepeatToLcm
| ListInterpolationStrategy::PadToLongestWithNeutral => Some(Vec::new()),
}
}
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
sizing: &Sizing,
current_color: Color,
) {
*self = interpolate_list(from, to, progress, sizing, current_color, |values| values)
.unwrap_or_else(|| {
if progress >= 1.0 {
to.clone()
} else {
from.clone()
}
});
}
}
fn interpolate_list<T: Animatable + Clone, C: AsRef<[T]>, O>(
from: &C,
to: &C,
progress: f32,
sizing: &Sizing,
current_color: Color,
build: impl FnOnce(Vec<T>) -> O,
) -> Option<O> {
let from = from.as_ref();
let to = to.as_ref();
let values = match T::list_interpolation_strategy() {
ListInterpolationStrategy::Discrete => {
if from.len() != to.len() {
return None;
}
interpolate_pairwise_list(from, to, from.len(), progress, sizing, current_color)
}
ListInterpolationStrategy::RepeatToLcm => {
if from.is_empty() || to.is_empty() {
return None;
}
interpolate_pairwise_list(
from,
to,
lcm(from.len(), to.len()),
progress,
sizing,
current_color,
)
}
ListInterpolationStrategy::PadToLongestWithNeutral => {
interpolate_neutral_padded_list(from, to, progress, sizing, current_color)?
}
};
Some(build(values))
}
fn interpolate_pairwise_list<T: Animatable + Clone>(
from: &[T],
to: &[T],
output_len: usize,
progress: f32,
sizing: &Sizing,
current_color: Color,
) -> Vec<T> {
(0..output_len)
.map(|index| {
let from_value = &from[index % from.len()];
let to_value = &to[index % to.len()];
let mut value = from_value.clone();
value.interpolate(from_value, to_value, progress, sizing, current_color);
value
})
.collect()
}
fn interpolate_neutral_padded_list<T: Animatable + Clone>(
from: &[T],
to: &[T],
progress: f32,
sizing: &Sizing,
current_color: Color,
) -> Option<Vec<T>> {
let output_len = from.len().max(to.len());
(0..output_len)
.map(|index| {
let from_value = if index < from.len() {
from.get(index).cloned()
} else {
to.get(index).and_then(T::neutral_value_like)
}?;
let to_value = if index < to.len() {
to.get(index).cloned()
} else {
from.get(index).and_then(T::neutral_value_like)
}?;
let mut value = from_value.clone();
value.interpolate(&from_value, &to_value, progress, sizing, current_color);
Some(value)
})
.collect()
}
fn gcd(lhs: usize, rhs: usize) -> usize {
let mut lhs = lhs;
let mut rhs = rhs;
while rhs != 0 {
let remainder = lhs % rhs;
lhs = rhs;
rhs = remainder;
}
lhs
}
fn lcm(lhs: usize, rhs: usize) -> usize {
lhs / gcd(lhs, rhs) * rhs
}
impl<T: Animatable + Copy> Animatable for SpacePair<T> {
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
sizing: &Sizing,
current_color: Color,
) {
self
.x
.interpolate(&from.x, &to.x, progress, sizing, current_color);
self
.y
.interpolate(&from.y, &to.y, progress, sizing, current_color);
}
}
impl<T: Animatable + Copy> Animatable for Sides<T> {
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
sizing: &Sizing,
current_color: Color,
) {
for (index, value) in self.0.iter_mut().enumerate() {
value.interpolate(
&from.0[index],
&to.0[index],
progress,
sizing,
current_color,
);
}
}
}
fn create_unexpected_token_error<'i>(
location: SourceLocation,
token: &Token,
#[cfg(feature = "detailed_css_error")] expect_message: Cow<'static, str>,
) -> ParseError<'i, Cow<'i, str>> {
#[cfg(feature = "detailed_css_error")]
let message = format!(
"unexpected token: {}, {}.",
token.to_css_string(),
expect_message
);
#[cfg(not(feature = "detailed_css_error"))]
let message = format!("unexpected token: {}.", token.to_css_string());
ParseError {
location,
kind: ParseErrorKind::Custom(Cow::Owned(message)),
}
}
pub(crate) fn merge_enum_values(values: &[CssToken]) -> String {
match values.len() {
0 => String::new(),
1 => values[0].to_string(),
2 => format!("{} or {}", values[0], values[1]),
_ => {
let all_but_last = values[..values.len() - 1]
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", ");
format!("{} or {}", all_but_last, values[values.len() - 1])
}
}
}
macro_rules! impl_from_taffy_enum {
($from_ty:ty, $to_ty:ty, $($variant:ident),*) => {
impl From<$from_ty> for $to_ty {
fn from(value: $from_ty) -> Self {
match value {
$(<$from_ty>::$variant => <$to_ty>::$variant,)*
}
}
}
};
}
macro_rules! declare_enum_from_css_impl {
(
$enum_type:ty,
$($css_value:expr => $variant:expr),* $(,)?
) => {
impl crate::layout::style::MakeComputed for $enum_type {}
impl<'i> crate::layout::style::FromCss<'i> for $enum_type {
fn valid_tokens() -> &'static [crate::layout::style::CssToken] {
&[$(crate::layout::style::CssToken::Keyword($css_value)),*]
}
fn from_css(input: &mut cssparser::Parser<'i, '_>) -> crate::layout::style::ParseResult<'i, Self> {
let location = input.current_source_location();
let token = input.next()?;
let cssparser::Token::Ident(ident) = token else {
return Err(Self::unexpected_token_error(location, &token));
};
cssparser::match_ignore_ascii_case! {&ident,
$(
$css_value => Ok($variant),
)*
_ => Err(Self::unexpected_token_error(location, &token)),
}
}
}
};
}
pub(crate) use declare_enum_from_css_impl;
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum ObjectFit {
#[default]
Fill,
Contain,
Cover,
ScaleDown,
None,
}
declare_enum_from_css_impl!(
ObjectFit,
"fill" => ObjectFit::Fill,
"contain" => ObjectFit::Contain,
"cover" => ObjectFit::Cover,
"scale-down" => ObjectFit::ScaleDown,
"none" => ObjectFit::None
);
impl TailwindPropertyParser for ObjectFit {
fn parse_tw(token: &str) -> Option<Self> {
Self::from_str(token).ok()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum BackgroundClip {
#[default]
BorderBox,
PaddingBox,
ContentBox,
Text,
BorderArea,
}
declare_enum_from_css_impl!(
BackgroundClip,
"border-box" => BackgroundClip::BorderBox,
"padding-box" => BackgroundClip::PaddingBox,
"content-box" => BackgroundClip::ContentBox,
"text" => BackgroundClip::Text,
"border-area" => BackgroundClip::BorderArea
);
impl TailwindPropertyParser for BackgroundClip {
fn parse_tw(token: &str) -> Option<Self> {
match_ignore_ascii_case! {token,
"border" => Some(BackgroundClip::BorderBox),
"padding" => Some(BackgroundClip::PaddingBox),
"content" => Some(BackgroundClip::ContentBox),
"text" => Some(BackgroundClip::Text),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct BorderRadius(pub Sides<SpacePair<LengthDefaultsToZero>>);
impl From<f32> for BorderRadius {
fn from(value: f32) -> Self {
Self(Sides(
[SpacePair::from_pair(Length::Px(value), Length::Px(value)); 4],
))
}
}
impl MakeComputed for BorderRadius {
fn make_computed(&mut self, sizing: &Sizing) {
self.0.make_computed(sizing);
}
}
impl Animatable for BorderRadius {
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
sizing: &Sizing,
current_color: Color,
) {
self
.0
.interpolate(&from.0, &to.0, progress, sizing, current_color);
}
}
impl Animatable for Box<BorderRadius> {
fn interpolate(
&mut self,
from: &Self,
to: &Self,
progress: f32,
sizing: &Sizing,
current_color: Color,
) {
let mut value = **from;
value.interpolate(&**from, to.as_ref(), progress, sizing, current_color);
**self = value;
}
}
impl<'i> FromCss<'i> for Box<BorderRadius> {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
let value = BorderRadius::from_css(input)?;
Ok(Box::new(value))
}
fn expect_message() -> Cow<'static, str> {
BorderRadius::expect_message()
}
fn valid_tokens() -> &'static [CssToken] {
BorderRadius::valid_tokens()
}
}
impl<'i> FromCss<'i> for BorderRadius {
fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
let widths: Sides<LengthDefaultsToZero> = Sides::from_css(input)?;
let heights = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
Sides::from_css(input)?
} else {
widths
};
Ok(BorderRadius(Sides([
SpacePair::from_pair(widths.0[0], heights.0[0]),
SpacePair::from_pair(widths.0[1], heights.0[1]),
SpacePair::from_pair(widths.0[2], heights.0[2]),
SpacePair::from_pair(widths.0[3], heights.0[3]),
])))
}
fn expect_message() -> Cow<'static, str> {
"1 to 4 length values for width, optionally followed by '/' and 1 to 4 length values for height"
.into()
}
fn valid_tokens() -> &'static [CssToken] {
&[CssToken::Token("length")]
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub enum BoxSizing {
ContentBox,
#[default]
BorderBox,
}
declare_enum_from_css_impl!(
BoxSizing,
"content-box" => BoxSizing::ContentBox,
"border-box" => BoxSizing::BorderBox
);
impl_from_taffy_enum!(BoxSizing, taffy::BoxSizing, ContentBox, BorderBox);
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub enum TextAlign {
Left,
Right,
Center,
Justify,
#[default]
Start,
End,
}
declare_enum_from_css_impl!(
TextAlign,
"left" => TextAlign::Left,
"right" => TextAlign::Right,
"center" => TextAlign::Center,
"justify" => TextAlign::Justify,
"start" => TextAlign::Start,
"end" => TextAlign::End
);
impl TailwindPropertyParser for TextAlign {
fn parse_tw(token: &str) -> Option<Self> {
Self::from_str(token).ok()
}
}
impl_from_taffy_enum!(
TextAlign, Alignment, Left, Right, Center, Justify, Start, End
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum Isolation {
Isolate,
#[default]
Auto,
}
declare_enum_from_css_impl!(
Isolation,
"isolate" => Isolation::Isolate,
"auto" => Isolation::Auto
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum Visibility {
#[default]
Visible,
Hidden,
}
declare_enum_from_css_impl!(
Visibility,
"visible" => Visibility::Visible,
"hidden" => Visibility::Hidden
);
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub enum LineJoin {
#[default]
Miter,
Round,
Bevel,
}
declare_enum_from_css_impl!(
LineJoin,
"miter" => LineJoin::Miter,
"round" => LineJoin::Round,
"bevel" => LineJoin::Bevel
);
impl From<LineJoin> for Join {
fn from(value: LineJoin) -> Self {
match value {
LineJoin::Miter => Join::Miter,
LineJoin::Round => Join::Round,
LineJoin::Bevel => Join::Bevel,
}
}
}
impl TailwindPropertyParser for LineJoin {
fn parse_tw(token: &str) -> Option<Self> {
Self::from_str(token).ok()
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub enum Position {
#[default]
Relative,
Absolute,
}
declare_enum_from_css_impl!(
Position,
"relative" => Position::Relative,
"absolute" => Position::Absolute
);
impl_from_taffy_enum!(Position, taffy::Position, Relative, Absolute);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum FlexDirection {
#[default]
Row,
Column,
RowReverse,
ColumnReverse,
}
declare_enum_from_css_impl!(
FlexDirection,
"row" => FlexDirection::Row,
"column" => FlexDirection::Column,
"row-reverse" => FlexDirection::RowReverse,
"column-reverse" => FlexDirection::ColumnReverse
);
impl_from_taffy_enum!(
FlexDirection,
taffy::FlexDirection,
Row,
Column,
RowReverse,
ColumnReverse
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum JustifyContent {
#[default]
Normal,
Start,
End,
FlexStart,
FlexEnd,
Center,
Stretch,
SpaceBetween,
SpaceEvenly,
SpaceAround,
}
declare_enum_from_css_impl!(
JustifyContent,
"normal" => JustifyContent::Normal,
"start" => JustifyContent::Start,
"end" => JustifyContent::End,
"flex-start" => JustifyContent::FlexStart,
"flex-end" => JustifyContent::FlexEnd,
"center" => JustifyContent::Center,
"stretch" => JustifyContent::Stretch,
"space-between" => JustifyContent::SpaceBetween,
"space-around" => JustifyContent::SpaceAround,
"space-evenly" => JustifyContent::SpaceEvenly
);
impl TailwindPropertyParser for JustifyContent {
fn parse_tw(token: &str) -> Option<Self> {
match token {
"between" => Some(JustifyContent::SpaceBetween),
"around" => Some(JustifyContent::SpaceAround),
"evenly" => Some(JustifyContent::SpaceEvenly),
_ => Self::from_str(token).ok(),
}
}
}
impl From<JustifyContent> for Option<taffy::JustifyContent> {
fn from(value: JustifyContent) -> Self {
match value {
JustifyContent::Normal => None,
JustifyContent::Start => Some(taffy::JustifyContent::Start),
JustifyContent::End => Some(taffy::JustifyContent::End),
JustifyContent::FlexStart => Some(taffy::JustifyContent::FlexStart),
JustifyContent::FlexEnd => Some(taffy::JustifyContent::FlexEnd),
JustifyContent::Center => Some(taffy::JustifyContent::Center),
JustifyContent::Stretch => Some(taffy::JustifyContent::Stretch),
JustifyContent::SpaceBetween => Some(taffy::JustifyContent::SpaceBetween),
JustifyContent::SpaceAround => Some(taffy::JustifyContent::SpaceAround),
JustifyContent::SpaceEvenly => Some(taffy::JustifyContent::SpaceEvenly),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum Display {
None,
#[default]
Flex,
InlineFlex,
Grid,
InlineGrid,
Inline,
Block,
InlineBlock,
}
declare_enum_from_css_impl!(
Display,
"none" => Display::None,
"flex" => Display::Flex,
"inline-flex" => Display::InlineFlex,
"grid" => Display::Grid,
"inline-grid" => Display::InlineGrid,
"inline" => Display::Inline,
"block" => Display::Block,
"inline-block" => Display::InlineBlock
);
impl Display {
pub fn is_inline(&self) -> bool {
*self == Display::Inline
}
pub fn is_inline_level(&self) -> bool {
matches!(
self,
Display::Inline | Display::InlineBlock | Display::InlineFlex | Display::InlineGrid
)
}
pub fn should_blockify_children(&self) -> bool {
matches!(
self,
Display::Flex | Display::InlineFlex | Display::Grid | Display::InlineGrid
)
}
pub fn as_blockified(self) -> Self {
match self {
Display::Inline => Display::Block,
Display::InlineBlock => Display::Block,
Display::InlineFlex => Display::Flex,
Display::InlineGrid => Display::Grid,
_ => self,
}
}
pub fn blockify(&mut self) {
*self = self.as_blockified();
}
}
impl From<Display> for taffy::Display {
fn from(value: Display) -> Self {
match value {
Display::Flex => taffy::Display::Flex,
Display::InlineFlex => taffy::Display::Flex,
Display::Grid => taffy::Display::Grid,
Display::InlineGrid => taffy::Display::Grid,
Display::Block => taffy::Display::Block,
Display::InlineBlock => taffy::Display::Block,
Display::None => taffy::Display::None,
Display::Inline => unreachable!("Inline node should not be inserted into taffy context"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum AlignItems {
#[default]
Normal,
Start,
End,
FlexStart,
FlexEnd,
Center,
Baseline,
Stretch,
}
declare_enum_from_css_impl!(
AlignItems,
"normal" => AlignItems::Normal,
"start" => AlignItems::Start,
"end" => AlignItems::End,
"flex-start" => AlignItems::FlexStart,
"flex-end" => AlignItems::FlexEnd,
"center" => AlignItems::Center,
"baseline" => AlignItems::Baseline,
"stretch" => AlignItems::Stretch
);
impl TailwindPropertyParser for AlignItems {
fn parse_tw(token: &str) -> Option<Self> {
Self::from_str(token).ok()
}
}
impl From<AlignItems> for Option<taffy::AlignItems> {
fn from(value: AlignItems) -> Self {
match value {
AlignItems::Normal => None,
AlignItems::Start => Some(taffy::AlignItems::Start),
AlignItems::End => Some(taffy::AlignItems::End),
AlignItems::FlexStart => Some(taffy::AlignItems::FlexStart),
AlignItems::FlexEnd => Some(taffy::AlignItems::FlexEnd),
AlignItems::Center => Some(taffy::AlignItems::Center),
AlignItems::Baseline => Some(taffy::AlignItems::Baseline),
AlignItems::Stretch => Some(taffy::AlignItems::Stretch),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum FlexWrap {
#[default]
NoWrap,
Wrap,
WrapReverse,
}
declare_enum_from_css_impl!(
FlexWrap,
"nowrap" => FlexWrap::NoWrap,
"wrap" => FlexWrap::Wrap,
"wrap-reverse" => FlexWrap::WrapReverse
);
impl_from_taffy_enum!(FlexWrap, taffy::FlexWrap, NoWrap, Wrap, WrapReverse);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum TextTransform {
#[default]
None,
Uppercase,
Lowercase,
Capitalize,
}
declare_enum_from_css_impl!(
TextTransform,
"none" => TextTransform::None,
"uppercase" => TextTransform::Uppercase,
"lowercase" => TextTransform::Lowercase,
"capitalize" => TextTransform::Capitalize
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum TextDecorationSkipInk {
#[default]
Auto,
None,
}
declare_enum_from_css_impl!(
TextDecorationSkipInk,
"auto" => TextDecorationSkipInk::Auto,
"none" => TextDecorationSkipInk::None
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum WhiteSpaceCollapse {
Preserve,
#[default]
Collapse,
PreserveSpaces,
PreserveBreaks,
}
declare_enum_from_css_impl!(
WhiteSpaceCollapse,
"preserve" => WhiteSpaceCollapse::Preserve,
"collapse" => WhiteSpaceCollapse::Collapse,
"preserve-spaces" => WhiteSpaceCollapse::PreserveSpaces,
"preserve-breaks" => WhiteSpaceCollapse::PreserveBreaks,
);
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum ImageScalingAlgorithm {
#[default]
Auto,
Smooth,
Pixelated,
}
declare_enum_from_css_impl!(
ImageScalingAlgorithm,
"auto" => ImageScalingAlgorithm::Auto,
"smooth" => ImageScalingAlgorithm::Smooth,
"pixelated" => ImageScalingAlgorithm::Pixelated
);
#[cfg(feature = "svg")]
impl From<ImageScalingAlgorithm> for resvg::usvg::ImageRendering {
fn from(algorithm: ImageScalingAlgorithm) -> Self {
match algorithm {
ImageScalingAlgorithm::Auto => resvg::usvg::ImageRendering::default(),
ImageScalingAlgorithm::Smooth => resvg::usvg::ImageRendering::Smooth,
ImageScalingAlgorithm::Pixelated => resvg::usvg::ImageRendering::Pixelated,
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub enum BorderStyle {
#[default]
None,
Solid,
}
declare_enum_from_css_impl!(
BorderStyle,
"none" => BorderStyle::None,
"solid" => BorderStyle::Solid,
);
impl TailwindPropertyParser for BorderStyle {
fn parse_tw(token: &str) -> Option<Self> {
Self::from_str(token).ok()
}
}
impl From<ImageScalingAlgorithm> for FilterType {
fn from(algorithm: ImageScalingAlgorithm) -> Self {
match algorithm {
ImageScalingAlgorithm::Auto => FilterType::CatmullRom,
ImageScalingAlgorithm::Smooth => FilterType::Lanczos3,
ImageScalingAlgorithm::Pixelated => FilterType::Nearest,
}
}
}
impl From<ImageScalingAlgorithm> for ResizeAlg {
fn from(algorithm: ImageScalingAlgorithm) -> Self {
match algorithm {
ImageScalingAlgorithm::Auto => {
ResizeAlg::Convolution(fast_image_resize::FilterType::CatmullRom)
}
ImageScalingAlgorithm::Smooth => {
ResizeAlg::Convolution(fast_image_resize::FilterType::Lanczos3)
}
ImageScalingAlgorithm::Pixelated => ResizeAlg::Nearest,
}
}
}