mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
style: Make CalcNode the specified representation of <length> and <length-percentage> values.
This is the meat of the patch. There are a couple improvements done in a couple later patches which should hopefully be straight-forward. Differential Revision: https://phabricator.services.mozilla.com/D63397
This commit is contained in:
parent
426edbd991
commit
7e8dbd0896
9 changed files with 320 additions and 385 deletions
|
@ -1,39 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Animation implementation for various length-related types.
|
||||
|
||||
use super::{Animate, Procedure};
|
||||
use crate::values::computed::length::LengthPercentage;
|
||||
use crate::values::computed::Percentage;
|
||||
use style_traits::values::specified::AllowedNumericType;
|
||||
|
||||
/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
|
||||
impl Animate for LengthPercentage {
|
||||
#[inline]
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
let animate_percentage_half = |this: Option<Percentage>, other: Option<Percentage>| {
|
||||
if this.is_none() && other.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
let this = this.unwrap_or_default();
|
||||
let other = other.unwrap_or_default();
|
||||
Ok(Some(this.animate(&other, procedure)?))
|
||||
};
|
||||
|
||||
let length = self
|
||||
.unclamped_length()
|
||||
.animate(&other.unclamped_length(), procedure)?;
|
||||
let percentage =
|
||||
animate_percentage_half(self.specified_percentage(), other.specified_percentage())?;
|
||||
|
||||
// Gets clamped as needed after the animation if needed, so no need to
|
||||
// specify any particular AllowedNumericType.
|
||||
Ok(LengthPercentage::new_calc(
|
||||
length,
|
||||
percentage,
|
||||
AllowedNumericType::All,
|
||||
))
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@ pub mod color;
|
|||
pub mod effects;
|
||||
mod font;
|
||||
mod grid;
|
||||
mod length;
|
||||
mod svg;
|
||||
pub mod transform;
|
||||
|
||||
|
@ -454,6 +453,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> ToAnimatedZero for Box<[T]>
|
||||
where
|
||||
T: ToAnimatedZero,
|
||||
{
|
||||
#[inline]
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
self.iter().map(|v| v.to_animated_zero()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToAnimatedZero for crate::OwnedSlice<T>
|
||||
where
|
||||
T: ToAnimatedZero,
|
||||
|
|
|
@ -17,7 +17,7 @@ use crate::values::{specified, CSSFloat};
|
|||
use crate::Zero;
|
||||
use app_units::Au;
|
||||
use std::fmt::{self, Write};
|
||||
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
|
||||
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub};
|
||||
use style_traits::{CSSPixel, CssWriter, ToCss};
|
||||
|
||||
pub use super::image::Image;
|
||||
|
@ -331,6 +331,13 @@ impl Div<CSSFloat> for CSSPixelLength {
|
|||
}
|
||||
}
|
||||
|
||||
impl MulAssign<CSSFloat> for CSSPixelLength {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: CSSFloat) {
|
||||
self.0 *= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<CSSFloat> for CSSPixelLength {
|
||||
type Output = Self;
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@
|
|||
//! our expectations.
|
||||
|
||||
use super::{Context, Length, Percentage, ToComputedValue};
|
||||
use crate::values::animated::{ToAnimatedValue, ToAnimatedZero};
|
||||
use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
||||
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||
use crate::values::generics::NonNegative;
|
||||
use crate::values::generics::{calc, NonNegative};
|
||||
use crate::values::specified::length::FontBaseSize;
|
||||
use crate::values::{specified, CSSFloat};
|
||||
use crate::Zero;
|
||||
|
@ -35,6 +35,7 @@ use app_units::Au;
|
|||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Write};
|
||||
use std::borrow::Cow;
|
||||
use style_traits::values::specified::AllowedNumericType;
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
|
||||
|
@ -162,7 +163,7 @@ impl MallocSizeOf for LengthPercentage {
|
|||
}
|
||||
|
||||
/// An unpacked `<length-percentage>` that borrows the `calc()` variant.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, ToCss)]
|
||||
enum Unpacked<'a> {
|
||||
Calc(&'a CalcLengthPercentage),
|
||||
Length(Length),
|
||||
|
@ -185,6 +186,14 @@ impl LengthPercentage {
|
|||
Self::new_length(Length::new(1.))
|
||||
}
|
||||
|
||||
fn to_calc_node(&self) -> Cow<CalcNode> {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(l) => Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l))),
|
||||
Unpacked::Percentage(p) => Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(p))),
|
||||
Unpacked::Calc(p) => Cow::Borrowed(&p.node),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a length value.
|
||||
#[inline]
|
||||
pub fn new_length(length: Length) -> Self {
|
||||
|
@ -211,25 +220,48 @@ impl LengthPercentage {
|
|||
percent
|
||||
}
|
||||
|
||||
/// Given a `LengthPercentage` value `v`, construct the value representing
|
||||
/// `calc(100% - v)`.
|
||||
pub fn hundred_percent_minus(v: Self, clamping_mode: AllowedNumericType) -> Self {
|
||||
// TODO: This could in theory take ownership of the calc node in `v` if
|
||||
// possible instead of cloning.
|
||||
let mut node = v.to_calc_node().into_owned();
|
||||
node.negate();
|
||||
|
||||
let new_node = CalcNode::Sum(vec![
|
||||
CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(Percentage::hundred())),
|
||||
node,
|
||||
].into());
|
||||
|
||||
Self::new_calc(new_node, clamping_mode)
|
||||
}
|
||||
|
||||
/// Constructs a `calc()` value.
|
||||
#[inline]
|
||||
pub fn new_calc(
|
||||
length: Length,
|
||||
percentage: Option<Percentage>,
|
||||
mut node: CalcNode,
|
||||
clamping_mode: AllowedNumericType,
|
||||
) -> Self {
|
||||
let percentage = match percentage {
|
||||
Some(p) => p,
|
||||
None => return Self::new_length(Length::new(clamping_mode.clamp(length.px()))),
|
||||
};
|
||||
if length.is_zero() {
|
||||
return Self::new_percent(Percentage(clamping_mode.clamp(percentage.0)));
|
||||
node.simplify_and_sort_children();
|
||||
|
||||
match node {
|
||||
CalcNode::Leaf(l) => {
|
||||
return match l {
|
||||
CalcLengthPercentageLeaf::Length(l) => {
|
||||
Self::new_length(Length::new(clamping_mode.clamp(l.px())))
|
||||
},
|
||||
CalcLengthPercentageLeaf::Percentage(p) => {
|
||||
Self::new_percent(Percentage(clamping_mode.clamp(p.0)))
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
|
||||
clamping_mode,
|
||||
node,
|
||||
}))
|
||||
}
|
||||
}
|
||||
Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
|
||||
length,
|
||||
percentage,
|
||||
clamping_mode,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Private version of new_calc() that constructs a calc() variant without
|
||||
|
@ -313,57 +345,7 @@ impl LengthPercentage {
|
|||
match self.unpack() {
|
||||
Unpacked::Length(l) => l.px() == 0.0,
|
||||
Unpacked::Percentage(p) => p.0 == 0.0,
|
||||
Unpacked::Calc(ref c) => {
|
||||
debug_assert_ne!(
|
||||
c.length.px(),
|
||||
0.0,
|
||||
"Should've been simplified to a percentage"
|
||||
);
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `<length>` component of this `calc()`, unclamped.
|
||||
#[inline]
|
||||
pub fn unclamped_length(&self) -> Length {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(l) => l,
|
||||
Unpacked::Percentage(..) => Zero::zero(),
|
||||
Unpacked::Calc(c) => c.unclamped_length(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns this `calc()` as a `<length>`.
|
||||
///
|
||||
/// Panics in debug mode if a percentage is present in the expression.
|
||||
#[inline]
|
||||
fn length(&self) -> Length {
|
||||
debug_assert!(!self.has_percentage());
|
||||
self.length_component()
|
||||
}
|
||||
|
||||
/// Returns the `<length>` component of this `calc()`, clamped.
|
||||
#[inline]
|
||||
pub fn length_component(&self) -> Length {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(l) => l,
|
||||
Unpacked::Percentage(..) => Zero::zero(),
|
||||
Unpacked::Calc(c) => c.length_component(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `<percentage>` component of this `calc()`, unclamped, as a
|
||||
/// float.
|
||||
///
|
||||
/// FIXME: This are very different semantics from length(), we should
|
||||
/// probably rename this.
|
||||
#[inline]
|
||||
pub fn percentage(&self) -> CSSFloat {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(..) => 0.,
|
||||
Unpacked::Percentage(p) => p.0,
|
||||
Unpacked::Calc(c) => c.percentage.0,
|
||||
Unpacked::Calc(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -407,25 +389,8 @@ impl LengthPercentage {
|
|||
#[inline]
|
||||
pub fn to_percentage(&self) -> Option<Percentage> {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(..) => None,
|
||||
Unpacked::Percentage(p) => Some(p),
|
||||
Unpacked::Calc(ref c) => {
|
||||
debug_assert!(!c.length.is_zero());
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the specified percentage if any.
|
||||
#[inline]
|
||||
pub fn specified_percentage(&self) -> Option<Percentage> {
|
||||
match self.unpack() {
|
||||
Unpacked::Length(..) => None,
|
||||
Unpacked::Percentage(p) => Some(p),
|
||||
Unpacked::Calc(ref c) => {
|
||||
debug_assert!(self.has_percentage());
|
||||
Some(c.percentage)
|
||||
},
|
||||
Unpacked::Length(..) | Unpacked::Calc(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,10 +417,10 @@ impl LengthPercentage {
|
|||
/// the height property), they apply whenever a calc() expression contains
|
||||
/// percentages.
|
||||
pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> {
|
||||
if self.has_percentage() {
|
||||
return Some(self.resolve(container_len?));
|
||||
if let Unpacked::Length(l) = self.unpack() {
|
||||
return Some(l);
|
||||
}
|
||||
Some(self.length())
|
||||
Some(self.resolve(container_len?))
|
||||
}
|
||||
|
||||
/// Returns the clamped non-negative values.
|
||||
|
@ -549,7 +514,7 @@ impl ToCss for LengthPercentage {
|
|||
where
|
||||
W: Write,
|
||||
{
|
||||
specified::LengthPercentage::from_computed_value(self).to_css(dest)
|
||||
self.unpack().to_css(dest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -584,46 +549,135 @@ impl<'de> Deserialize<'de> for LengthPercentage {
|
|||
}
|
||||
}
|
||||
|
||||
/// The leaves of a `<length-percentage>` calc expression.
|
||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToCss, ToResolvedValue)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum CalcLengthPercentageLeaf {
|
||||
Length(Length),
|
||||
Percentage(Percentage),
|
||||
}
|
||||
|
||||
impl CalcLengthPercentageLeaf {
|
||||
fn is_zero_length(&self) -> bool {
|
||||
match *self {
|
||||
Self::Length(ref l) => l.is_zero(),
|
||||
Self::Percentage(..) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for CalcLengthPercentageLeaf {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
use self::CalcLengthPercentageLeaf::*;
|
||||
|
||||
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||
return None;
|
||||
}
|
||||
|
||||
match (self, other) {
|
||||
(&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
|
||||
(&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
|
||||
_ => {
|
||||
match *self {
|
||||
Length(..) | Percentage(..) => {},
|
||||
}
|
||||
unsafe { debug_unreachable!("Forgot a branch?"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
|
||||
fn is_negative(&self) -> bool {
|
||||
match *self {
|
||||
Self::Length(ref l) => l.px() < 0.,
|
||||
Self::Percentage(ref p) => p.0 < 0.,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
|
||||
use self::CalcLengthPercentageLeaf::*;
|
||||
|
||||
// 0px plus anything else is equal to the right hand side.
|
||||
if self.is_zero_length() {
|
||||
*self = other.clone();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if other.is_zero_length() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match (self, other) {
|
||||
(&mut Length(ref mut one), &Length(ref other)) => {
|
||||
*one += *other;
|
||||
},
|
||||
(&mut Percentage(ref mut one), &Percentage(ref other)) => {
|
||||
one.0 += other.0;
|
||||
},
|
||||
_ => return Err(()),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mul_by(&mut self, scalar: f32) {
|
||||
match *self {
|
||||
Self::Length(ref mut l) => *l = *l * scalar,
|
||||
Self::Percentage(ref mut p) => p.0 *= scalar,
|
||||
}
|
||||
}
|
||||
|
||||
fn simplify(&mut self) {}
|
||||
|
||||
fn sort_key(&self) -> calc::SortKey {
|
||||
match *self {
|
||||
Self::Length(..) => calc::SortKey::Px,
|
||||
Self::Percentage(..) => calc::SortKey::Percentage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The computed version of a calc() node for `<length-percentage>` values.
|
||||
pub type CalcNode = calc::GenericCalcNode<CalcLengthPercentageLeaf>;
|
||||
|
||||
/// The representation of a calc() function with mixed lengths and percentages.
|
||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue)]
|
||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue, ToCss)]
|
||||
#[repr(C)]
|
||||
pub struct CalcLengthPercentage {
|
||||
length: Length,
|
||||
|
||||
percentage: Percentage,
|
||||
|
||||
#[animation(constant)]
|
||||
#[css(skip)]
|
||||
clamping_mode: AllowedNumericType,
|
||||
node: CalcNode,
|
||||
}
|
||||
|
||||
impl CalcLengthPercentage {
|
||||
/// Returns the length component of this `calc()`, clamped.
|
||||
#[inline]
|
||||
fn length_component(&self) -> Length {
|
||||
Length::new(self.clamping_mode.clamp(self.length.px()))
|
||||
}
|
||||
|
||||
/// Resolves the percentage.
|
||||
#[inline]
|
||||
pub fn resolve(&self, basis: Length) -> Length {
|
||||
let length = self.length.px() + basis.px() * self.percentage.0;
|
||||
Length::new(self.clamping_mode.clamp(length))
|
||||
fn resolve(&self, basis: Length) -> Length {
|
||||
// TODO: This could be faster (without the extra allocations),
|
||||
// potentially.
|
||||
let mut resolved = self.node.map_leafs(|l| {
|
||||
match l {
|
||||
CalcLengthPercentageLeaf::Length(..) => l.clone(),
|
||||
CalcLengthPercentageLeaf::Percentage(ref p) => {
|
||||
CalcLengthPercentageLeaf::Length(Length::new(basis.px() * p.0))
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
resolved.simplify_and_sort_children();
|
||||
|
||||
match resolved {
|
||||
CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l)) => l,
|
||||
other => unreachable!("Didn't manage to resolve <length-percentage>: {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the length, without clamping.
|
||||
#[inline]
|
||||
fn unclamped_length(&self) -> Length {
|
||||
self.length
|
||||
}
|
||||
|
||||
/// Returns the clamped non-negative values.
|
||||
#[inline]
|
||||
fn clamp_to_non_negative(&self) -> LengthPercentage {
|
||||
LengthPercentage::new_calc(
|
||||
self.length,
|
||||
Some(self.percentage),
|
||||
AllowedNumericType::NonNegative,
|
||||
)
|
||||
let mut new = self.clone();
|
||||
new.clamping_mode = AllowedNumericType::NonNegative;
|
||||
LengthPercentage::new_calc_unchecked(Box::new(new))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -641,7 +695,7 @@ impl CalcLengthPercentage {
|
|||
// maybe.
|
||||
impl PartialEq for CalcLengthPercentage {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.length == other.length && self.percentage == other.percentage
|
||||
self.node == other.node
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -656,43 +710,30 @@ impl specified::CalcLengthPercentage {
|
|||
where
|
||||
F: Fn(Length) -> Length,
|
||||
{
|
||||
use crate::values::specified::length::{FontRelativeLength, ViewportPercentageLength};
|
||||
use std::f32;
|
||||
use crate::values::specified::calc::Leaf;
|
||||
use crate::values::specified::length::NoCalcLength;
|
||||
|
||||
let mut length = 0.;
|
||||
|
||||
if let Some(absolute) = self.absolute {
|
||||
length += zoom_fn(absolute.to_computed_value(context)).px();
|
||||
}
|
||||
|
||||
for val in &[
|
||||
self.vw.map(ViewportPercentageLength::Vw),
|
||||
self.vh.map(ViewportPercentageLength::Vh),
|
||||
self.vmin.map(ViewportPercentageLength::Vmin),
|
||||
self.vmax.map(ViewportPercentageLength::Vmax),
|
||||
] {
|
||||
if let Some(val) = *val {
|
||||
let viewport_size = context.viewport_size_for_viewport_unit_resolution();
|
||||
length += val.to_computed_value(viewport_size).px();
|
||||
let node = self.node.map_leaves(|leaf| {
|
||||
match *leaf {
|
||||
Leaf::Percentage(p) => CalcLengthPercentageLeaf::Percentage(Percentage(p)),
|
||||
Leaf::Length(l) => {
|
||||
CalcLengthPercentageLeaf::Length(match l {
|
||||
NoCalcLength::Absolute(ref abs) => {
|
||||
zoom_fn(abs.to_computed_value(context))
|
||||
},
|
||||
NoCalcLength::FontRelative(ref fr) => {
|
||||
fr.to_computed_value(context, base_size)
|
||||
},
|
||||
other => other.to_computed_value(context),
|
||||
})
|
||||
},
|
||||
Leaf::Number(..) |
|
||||
Leaf::Angle(..) |
|
||||
Leaf::Time(..) => unreachable!("Shouldn't have parsed"),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for val in &[
|
||||
self.ch.map(FontRelativeLength::Ch),
|
||||
self.em.map(FontRelativeLength::Em),
|
||||
self.ex.map(FontRelativeLength::Ex),
|
||||
self.rem.map(FontRelativeLength::Rem),
|
||||
] {
|
||||
if let Some(val) = *val {
|
||||
length += val.to_computed_value(context, base_size).px();
|
||||
}
|
||||
}
|
||||
|
||||
LengthPercentage::new_calc(
|
||||
Length::new(length.min(f32::MAX).max(f32::MIN)),
|
||||
self.percentage,
|
||||
self.clamping_mode,
|
||||
)
|
||||
LengthPercentage::new_calc(node, self.clamping_mode)
|
||||
}
|
||||
|
||||
/// Compute font-size or line-height taking into account text-zoom if necessary.
|
||||
|
@ -711,25 +752,14 @@ impl specified::CalcLengthPercentage {
|
|||
/// Compute the value into pixel length as CSSFloat without context,
|
||||
/// so it returns Err(()) if there is any non-absolute unit.
|
||||
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
|
||||
if self.vw.is_some() ||
|
||||
self.vh.is_some() ||
|
||||
self.vmin.is_some() ||
|
||||
self.vmax.is_some() ||
|
||||
self.em.is_some() ||
|
||||
self.ex.is_some() ||
|
||||
self.ch.is_some() ||
|
||||
self.rem.is_some() ||
|
||||
self.percentage.is_some()
|
||||
{
|
||||
return Err(());
|
||||
}
|
||||
use crate::values::specified::calc::Leaf;
|
||||
use crate::values::specified::length::NoCalcLength;
|
||||
|
||||
match self.absolute {
|
||||
Some(abs) => Ok(abs.to_px()),
|
||||
None => {
|
||||
debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self);
|
||||
Err(())
|
||||
},
|
||||
// Simplification should've turned this into an absolute length,
|
||||
// otherwise it wouldn't have been able to.
|
||||
match self.node {
|
||||
calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -740,17 +770,48 @@ impl specified::CalcLengthPercentage {
|
|||
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &CalcLengthPercentage) -> Self {
|
||||
use crate::values::specified::length::AbsoluteLength;
|
||||
use crate::values::specified::calc::Leaf;
|
||||
use crate::values::specified::length::NoCalcLength;
|
||||
|
||||
specified::CalcLengthPercentage {
|
||||
clamping_mode: computed.clamping_mode,
|
||||
absolute: Some(AbsoluteLength::from_computed_value(&computed.length)),
|
||||
percentage: Some(computed.percentage),
|
||||
..Default::default()
|
||||
node: computed.node.map_leaves(|l| {
|
||||
match l {
|
||||
CalcLengthPercentageLeaf::Length(ref l) => Leaf::Length(NoCalcLength::from_px(l.px())),
|
||||
CalcLengthPercentageLeaf::Percentage(ref p) => Leaf::Percentage(p.0),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
||||
/// https://drafts.csswg.org/css-values-4/#combine-math
|
||||
/// https://drafts.csswg.org/css-values-4/#combine-mixed
|
||||
impl Animate for LengthPercentage {
|
||||
#[inline]
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
Ok(match (self.unpack(), other.unpack()) {
|
||||
(Unpacked::Length(one), Unpacked::Length(other)) => {
|
||||
Self::new_length(one.animate(&other, procedure)?)
|
||||
},
|
||||
(Unpacked::Percentage(one), Unpacked::Percentage(other)) => {
|
||||
Self::new_percent(one.animate(&other, procedure)?)
|
||||
},
|
||||
_ => {
|
||||
let mut one = self.to_calc_node().into_owned();
|
||||
let mut other = other.to_calc_node().into_owned();
|
||||
let (l, r) = procedure.weights();
|
||||
|
||||
one.mul_by(l as f32);
|
||||
other.mul_by(r as f32);
|
||||
|
||||
Self::new_calc(CalcNode::Sum(vec![one, other].into()), AllowedNumericType::All)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper of LengthPercentage, whose value must be >= 0.
|
||||
pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::{cmp, mem};
|
|||
use smallvec::SmallVec;
|
||||
|
||||
/// Whether we're a `min` or `max` function.
|
||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue, ToShmem)]
|
||||
#[repr(u8)]
|
||||
pub enum MinMaxOp {
|
||||
/// `min()`
|
||||
|
@ -44,24 +44,27 @@ pub enum SortKey {
|
|||
}
|
||||
|
||||
/// A generic node in a calc expression.
|
||||
///
|
||||
/// FIXME: This would be much more elegant if we used `Self` in the types below,
|
||||
/// but we can't because of https://github.com/serde-rs/serde/issues/1565.
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue, ToShmem)]
|
||||
pub enum GenericCalcNode<L> {
|
||||
/// A leaf node.
|
||||
Leaf(L),
|
||||
/// A sum node, representing `a + b + c` where a, b, and c are the
|
||||
/// arguments.
|
||||
Sum(Box<[Self]>),
|
||||
Sum(Box<[GenericCalcNode<L>]>),
|
||||
/// A `min` or `max` function.
|
||||
MinMax(Box<[Self]>, MinMaxOp),
|
||||
MinMax(Box<[GenericCalcNode<L>]>, MinMaxOp),
|
||||
/// A `clamp()` function.
|
||||
Clamp {
|
||||
/// The minimum value.
|
||||
min: Box<Self>,
|
||||
min: Box<GenericCalcNode<L>>,
|
||||
/// The central value.
|
||||
center: Box<Self>,
|
||||
center: Box<GenericCalcNode<L>>,
|
||||
/// The maximum value.
|
||||
max: Box<Self>,
|
||||
max: Box<GenericCalcNode<L>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -111,6 +114,46 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convert this `CalcNode` into a `CalcNode` with a different leaf kind.
|
||||
pub fn map_leaves<O, F>(&self, mut map: F) -> CalcNode<O>
|
||||
where
|
||||
O: CalcNodeLeaf,
|
||||
F: FnMut(&L) -> O,
|
||||
{
|
||||
self.map_leaves_internal(&mut map)
|
||||
}
|
||||
|
||||
fn map_leaves_internal<O, F>(&self, map: &mut F) -> CalcNode<O>
|
||||
where
|
||||
O: CalcNodeLeaf,
|
||||
F: FnMut(&L) -> O,
|
||||
{
|
||||
fn map_children<L, O, F>(
|
||||
children: &[CalcNode<L>],
|
||||
map: &mut F,
|
||||
) -> Box<[CalcNode<O>]>
|
||||
where
|
||||
L: CalcNodeLeaf,
|
||||
O: CalcNodeLeaf,
|
||||
F: FnMut(&L) -> O,
|
||||
{
|
||||
children.iter().map(|c| c.map_leaves_internal(map)).collect()
|
||||
}
|
||||
|
||||
match *self {
|
||||
Self::Leaf(ref l) => CalcNode::Leaf(map(l)),
|
||||
Self::Sum(ref c) => CalcNode::Sum(map_children(c, map)),
|
||||
Self::MinMax(ref c, op) => CalcNode::MinMax(map_children(c, map), op),
|
||||
Self::Clamp { ref min, ref center, ref max } => {
|
||||
let min = Box::new(min.map_leaves_internal(map));
|
||||
let center = Box::new(center.map_leaves_internal(map));
|
||||
let max = Box::new(max.map_leaves_internal(map));
|
||||
CalcNode::Clamp { min, center, max }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_negative_leaf(&self) -> bool {
|
||||
match *self {
|
||||
Self::Leaf(ref l) => l.is_negative(),
|
||||
|
|
|
@ -354,7 +354,7 @@ impl ToAbsoluteLength for SpecifiedLengthPercentage {
|
|||
match *self {
|
||||
Length(len) => len.to_computed_pixel_length_without_context(),
|
||||
Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
|
||||
_ => Err(()),
|
||||
Percentage(..) => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
//! [calc]: https://drafts.csswg.org/css-values/#calc-notation
|
||||
|
||||
use crate::parser::ParserContext;
|
||||
use crate::values::computed;
|
||||
use crate::values::generics::calc as generic;
|
||||
use crate::values::generics::calc::{MinMaxOp, SortKey};
|
||||
use crate::values::specified::length::ViewportPercentageLength;
|
||||
|
@ -35,7 +34,7 @@ pub enum MathFunction {
|
|||
}
|
||||
|
||||
/// A leaf node inside a `Calc` expression's AST.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
||||
pub enum Leaf {
|
||||
/// `<length>`
|
||||
Length(NoCalcLength),
|
||||
|
@ -89,87 +88,12 @@ enum CalcUnit {
|
|||
/// relative lengths, and to_computed_pixel_length_without_context() handles
|
||||
/// this case. Therefore, if you want to add a new field, please make sure this
|
||||
/// function work properly.
|
||||
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct CalcLengthPercentage {
|
||||
#[css(skip)]
|
||||
pub clamping_mode: AllowedNumericType,
|
||||
pub absolute: Option<AbsoluteLength>,
|
||||
pub vw: Option<CSSFloat>,
|
||||
pub vh: Option<CSSFloat>,
|
||||
pub vmin: Option<CSSFloat>,
|
||||
pub vmax: Option<CSSFloat>,
|
||||
pub em: Option<CSSFloat>,
|
||||
pub ex: Option<CSSFloat>,
|
||||
pub ch: Option<CSSFloat>,
|
||||
pub rem: Option<CSSFloat>,
|
||||
pub percentage: Option<computed::Percentage>,
|
||||
}
|
||||
|
||||
impl ToCss for CalcLengthPercentage {
|
||||
/// <https://drafts.csswg.org/css-values/#calc-serialize>
|
||||
///
|
||||
/// FIXME(emilio): Should this simplify away zeros?
|
||||
#[allow(unused_assignments)]
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
use num_traits::Zero;
|
||||
|
||||
let mut first_value = true;
|
||||
macro_rules! first_value_check {
|
||||
($val:expr) => {
|
||||
if !first_value {
|
||||
dest.write_str(if $val < Zero::zero() { " - " } else { " + " })?;
|
||||
} else if $val < Zero::zero() {
|
||||
dest.write_str("-")?;
|
||||
}
|
||||
first_value = false;
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! serialize {
|
||||
( $( $val:ident ),* ) => {
|
||||
$(
|
||||
if let Some(val) = self.$val {
|
||||
first_value_check!(val);
|
||||
val.abs().to_css(dest)?;
|
||||
dest.write_str(stringify!($val))?;
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! serialize_abs {
|
||||
( $( $val:ident ),+ ) => {
|
||||
$(
|
||||
if let Some(AbsoluteLength::$val(v)) = self.absolute {
|
||||
first_value_check!(v);
|
||||
AbsoluteLength::$val(v.abs()).to_css(dest)?;
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
dest.write_str("calc(")?;
|
||||
|
||||
// NOTE(emilio): Percentages first because of web-compat problems, see:
|
||||
// https://github.com/w3c/csswg-drafts/issues/1731
|
||||
if let Some(val) = self.percentage {
|
||||
first_value_check!(val.0);
|
||||
val.abs().to_css(dest)?;
|
||||
}
|
||||
|
||||
// NOTE(emilio): The order here it's very intentional, and alphabetic
|
||||
// per the spec linked above.
|
||||
serialize!(ch);
|
||||
serialize_abs!(Cm);
|
||||
serialize!(em, ex);
|
||||
serialize_abs!(In, Mm, Pc, Pt, Px, Q);
|
||||
serialize!(rem, vh, vmax, vmin, vw);
|
||||
|
||||
dest.write_str(")")
|
||||
}
|
||||
pub node: CalcNode,
|
||||
}
|
||||
|
||||
impl SpecifiedValueInfo for CalcLengthPercentage {}
|
||||
|
@ -591,84 +515,15 @@ impl CalcNode {
|
|||
|
||||
/// Tries to simplify this expression into a `<length>` or `<percentage`>
|
||||
/// value.
|
||||
fn to_length_or_percentage(
|
||||
&mut self,
|
||||
fn into_length_or_percentage(
|
||||
mut self,
|
||||
clamping_mode: AllowedNumericType,
|
||||
) -> Result<CalcLengthPercentage, ()> {
|
||||
let mut ret = CalcLengthPercentage {
|
||||
clamping_mode,
|
||||
..Default::default()
|
||||
};
|
||||
self.simplify_and_sort_children();
|
||||
self.add_length_or_percentage_to(&mut ret, 1.0)?;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Puts this `<length>` or `<percentage>` into `ret`, or error.
|
||||
///
|
||||
/// `factor` is the sign or multiplicative factor to account for the sign
|
||||
/// (this allows adding and substracting into the return value).
|
||||
fn add_length_or_percentage_to(
|
||||
&self,
|
||||
ret: &mut CalcLengthPercentage,
|
||||
factor: CSSFloat,
|
||||
) -> Result<(), ()> {
|
||||
match *self {
|
||||
CalcNode::Leaf(Leaf::Percentage(pct)) => {
|
||||
ret.percentage = Some(computed::Percentage(
|
||||
ret.percentage.map_or(0., |p| p.0) + pct * factor,
|
||||
));
|
||||
},
|
||||
CalcNode::Leaf(Leaf::Length(ref l)) => match *l {
|
||||
NoCalcLength::Absolute(abs) => {
|
||||
ret.absolute = Some(match ret.absolute {
|
||||
Some(value) => value + abs * factor,
|
||||
None => abs * factor,
|
||||
});
|
||||
},
|
||||
NoCalcLength::FontRelative(rel) => match rel {
|
||||
FontRelativeLength::Em(em) => {
|
||||
ret.em = Some(ret.em.unwrap_or(0.) + em * factor);
|
||||
},
|
||||
FontRelativeLength::Ex(ex) => {
|
||||
ret.ex = Some(ret.ex.unwrap_or(0.) + ex * factor);
|
||||
},
|
||||
FontRelativeLength::Ch(ch) => {
|
||||
ret.ch = Some(ret.ch.unwrap_or(0.) + ch * factor);
|
||||
},
|
||||
FontRelativeLength::Rem(rem) => {
|
||||
ret.rem = Some(ret.rem.unwrap_or(0.) + rem * factor);
|
||||
},
|
||||
},
|
||||
NoCalcLength::ViewportPercentage(rel) => match rel {
|
||||
ViewportPercentageLength::Vh(vh) => {
|
||||
ret.vh = Some(ret.vh.unwrap_or(0.) + vh * factor)
|
||||
},
|
||||
ViewportPercentageLength::Vw(vw) => {
|
||||
ret.vw = Some(ret.vw.unwrap_or(0.) + vw * factor)
|
||||
},
|
||||
ViewportPercentageLength::Vmax(vmax) => {
|
||||
ret.vmax = Some(ret.vmax.unwrap_or(0.) + vmax * factor)
|
||||
},
|
||||
ViewportPercentageLength::Vmin(vmin) => {
|
||||
ret.vmin = Some(ret.vmin.unwrap_or(0.) + vmin * factor)
|
||||
},
|
||||
},
|
||||
NoCalcLength::ServoCharacterWidth(..) => unreachable!(),
|
||||
},
|
||||
CalcNode::Sum(ref children) => {
|
||||
for child in &**children {
|
||||
child.add_length_or_percentage_to(ret, factor)?;
|
||||
}
|
||||
},
|
||||
CalcNode::MinMax(..) | CalcNode::Clamp { .. } => {
|
||||
// FIXME(emilio): Implement min/max/clamp for length-percentage.
|
||||
return Err(());
|
||||
},
|
||||
CalcNode::Leaf(..) => return Err(()),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(CalcLengthPercentage {
|
||||
clamping_mode,
|
||||
node: self,
|
||||
})
|
||||
}
|
||||
|
||||
/// Tries to simplify this expression into a `<time>` value.
|
||||
|
@ -742,7 +597,7 @@ impl CalcNode {
|
|||
function: MathFunction,
|
||||
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
||||
Self::parse(context, input, function, CalcUnit::LengthPercentage)?
|
||||
.to_length_or_percentage(clamping_mode)
|
||||
.into_length_or_percentage(clamping_mode)
|
||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
|
@ -765,7 +620,7 @@ impl CalcNode {
|
|||
function: MathFunction,
|
||||
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
||||
Self::parse(context, input, function, CalcUnit::Length)?
|
||||
.to_length_or_percentage(clamping_mode)
|
||||
.into_length_or_percentage(clamping_mode)
|
||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::values::generics::length::{
|
|||
GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
|
||||
};
|
||||
use crate::values::generics::NonNegative;
|
||||
use crate::values::specified::calc::CalcNode;
|
||||
use crate::values::specified::calc::{self, CalcNode};
|
||||
use crate::values::specified::NonNegativeNumber;
|
||||
use crate::values::CSSFloat;
|
||||
use crate::Zero;
|
||||
|
@ -952,9 +952,10 @@ impl From<Percentage> for LengthPercentage {
|
|||
#[inline]
|
||||
fn from(pc: Percentage) -> Self {
|
||||
if pc.is_calc() {
|
||||
// FIXME(emilio): Hard-coding the clamping mode is suspect.
|
||||
LengthPercentage::Calc(Box::new(CalcLengthPercentage {
|
||||
percentage: Some(computed::Percentage(pc.get())),
|
||||
..Default::default()
|
||||
clamping_mode: AllowedNumericType::All,
|
||||
node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
|
||||
}))
|
||||
} else {
|
||||
LengthPercentage::Percentage(computed::Percentage(pc.get()))
|
||||
|
|
|
@ -295,10 +295,8 @@ impl<S: Side> ToComputedValue for PositionComponent<S> {
|
|||
},
|
||||
PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {
|
||||
let length = length.to_computed_value(context);
|
||||
let p = Percentage(1. - length.percentage());
|
||||
let l = -length.unclamped_length();
|
||||
// We represent `<end-side> <length>` as `calc(100% - <length>)`.
|
||||
ComputedLengthPercentage::new_calc(l, Some(p), AllowedNumericType::All)
|
||||
ComputedLengthPercentage::hundred_percent_minus(length, AllowedNumericType::All)
|
||||
},
|
||||
PositionComponent::Side(_, Some(ref length)) |
|
||||
PositionComponent::Length(ref length) => length.to_computed_value(context),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue