зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1607049 - Move LengthPercentage to its own file. r=jwatt
I'm (sadly) about to make it a bit more complicated to pack it better. So we may as well do this so it is easier to reason about navigate. I also reordered things a bit, and removed some From<> implementations and such. Differential Revision: https://phabricator.services.mozilla.com/D58701 --HG-- rename : servo/components/style/values/computed/length.rs => servo/components/style/values/computed/length_percentage.rs extra : moz-landing-system : lando
This commit is contained in:
Родитель
d10341c0bc
Коммит
d4c2489000
|
@ -5,7 +5,7 @@
|
||||||
//! Animation implementation for various length-related types.
|
//! Animation implementation for various length-related types.
|
||||||
|
|
||||||
use super::{Animate, Procedure};
|
use super::{Animate, Procedure};
|
||||||
use crate::values::computed::length::{LengthPercentage, CalcLengthPercentage};
|
use crate::values::computed::length::LengthPercentage;
|
||||||
use crate::values::computed::Percentage;
|
use crate::values::computed::Percentage;
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
|
/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
|
||||||
|
@ -29,6 +29,6 @@ impl Animate for LengthPercentage {
|
||||||
|
|
||||||
// Gets clamped as needed after the animation, so no need to specify any
|
// Gets clamped as needed after the animation, so no need to specify any
|
||||||
// particular AllowedNumericType.
|
// particular AllowedNumericType.
|
||||||
Ok(CalcLengthPercentage::new(length, percentage).to_length_percentge())
|
Ok(LengthPercentage::new_calc(length, percentage))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,28 +4,26 @@
|
||||||
|
|
||||||
//! `<length>` computed values, and related ones.
|
//! `<length>` computed values, and related ones.
|
||||||
|
|
||||||
use super::{Context, Number, Percentage, ToComputedValue};
|
use super::{Context, Number, ToComputedValue};
|
||||||
use crate::values::animated::ToAnimatedValue;
|
use crate::values::animated::ToAnimatedValue;
|
||||||
use crate::values::computed::NonNegativeNumber;
|
use crate::values::computed::NonNegativeNumber;
|
||||||
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
||||||
use crate::values::generics::length as generics;
|
use crate::values::generics::length as generics;
|
||||||
use crate::values::generics::length::{
|
use crate::values::generics::length::{
|
||||||
GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
|
GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
|
||||||
};
|
};
|
||||||
use crate::values::generics::NonNegative;
|
use crate::values::generics::NonNegative;
|
||||||
use crate::values::specified::length::ViewportPercentageLength;
|
use crate::values::specified::length::{AbsoluteLength, FontBaseSize};
|
||||||
use crate::values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
|
|
||||||
use crate::values::{specified, CSSFloat};
|
use crate::values::{specified, CSSFloat};
|
||||||
use crate::Zero;
|
use crate::Zero;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
|
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
|
||||||
use style_traits::values::specified::AllowedNumericType;
|
|
||||||
use style_traits::{CSSPixel, CssWriter, ToCss};
|
use style_traits::{CSSPixel, CssWriter, ToCss};
|
||||||
|
|
||||||
pub use super::image::Image;
|
pub use super::image::Image;
|
||||||
pub use crate::values::specified::url::UrlOrNone;
|
pub use crate::values::specified::url::UrlOrNone;
|
||||||
pub use crate::values::specified::{Angle, BorderStyle, Time};
|
pub use crate::values::specified::{Angle, BorderStyle, Time};
|
||||||
|
pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
|
||||||
|
|
||||||
impl ToComputedValue for specified::NoCalcLength {
|
impl ToComputedValue for specified::NoCalcLength {
|
||||||
type ComputedValue = Length;
|
type ComputedValue = Length;
|
||||||
|
@ -69,502 +67,6 @@ impl ToComputedValue for specified::Length {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `<length-percentage>` value. This can be either a `<length>`, a
|
|
||||||
/// `<percentage>`, or a combination of both via `calc()`.
|
|
||||||
///
|
|
||||||
/// cbindgen:private-default-tagged-enum-constructor=false
|
|
||||||
/// cbindgen:derive-mut-casts=true
|
|
||||||
///
|
|
||||||
/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum LengthPercentage {
|
|
||||||
Length(Length),
|
|
||||||
Percentage(Percentage),
|
|
||||||
Calc(Box<CalcLengthPercentage>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The representation of a calc() function.
|
|
||||||
#[derive(
|
|
||||||
Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue,
|
|
||||||
)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CalcLengthPercentage {
|
|
||||||
length: Length,
|
|
||||||
|
|
||||||
percentage: Percentage,
|
|
||||||
|
|
||||||
#[animation(constant)]
|
|
||||||
clamping_mode: AllowedNumericType,
|
|
||||||
|
|
||||||
/// Whether we specified a percentage or not.
|
|
||||||
#[animation(constant)]
|
|
||||||
pub has_percentage: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(emilio): We don't compare `clamping_mode` since we want to preserve the
|
|
||||||
// invariant that `from_computed_value(length).to_computed_value(..) == length`.
|
|
||||||
//
|
|
||||||
// Right now for e.g. a non-negative length, we set clamping_mode to `All`
|
|
||||||
// unconditionally for non-calc values, and to `NonNegative` for calc.
|
|
||||||
//
|
|
||||||
// If we determine that it's sound, from_computed_value() can generate an
|
|
||||||
// absolute length, which then would get `All` as the clamping mode.
|
|
||||||
//
|
|
||||||
// We may want to just eagerly-detect whether we can clamp in
|
|
||||||
// `LengthPercentage::new` and switch to `AllowedNumericType::NonNegative` then,
|
|
||||||
// maybe.
|
|
||||||
impl PartialEq for CalcLengthPercentage {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.length == other.length &&
|
|
||||||
self.percentage == other.percentage &&
|
|
||||||
self.has_percentage == other.has_percentage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComputeSquaredDistance for LengthPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
||||||
// FIXME(nox): This looks incorrect to me, to add a distance between lengths
|
|
||||||
// with a distance between percentages.
|
|
||||||
Ok(self
|
|
||||||
.unclamped_length()
|
|
||||||
.compute_squared_distance(&other.unclamped_length())? +
|
|
||||||
self.percentage()
|
|
||||||
.compute_squared_distance(&other.percentage())?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for LengthPercentage {
|
|
||||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
|
||||||
where
|
|
||||||
W: Write,
|
|
||||||
{
|
|
||||||
specified::LengthPercentage::from_computed_value(self).to_css(dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl specified::CalcLengthPercentage {
|
|
||||||
/// Compute the value, zooming any absolute units by the zoom function.
|
|
||||||
fn to_computed_value_with_zoom<F>(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
zoom_fn: F,
|
|
||||||
base_size: FontBaseSize,
|
|
||||||
) -> CalcLengthPercentage
|
|
||||||
where
|
|
||||||
F: Fn(Length) -> Length,
|
|
||||||
{
|
|
||||||
use std::f32;
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CalcLengthPercentage::with_clamping_mode(
|
|
||||||
Length::new(length.min(f32::MAX).max(f32::MIN)),
|
|
||||||
self.percentage,
|
|
||||||
self.clamping_mode,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute font-size or line-height taking into account text-zoom if necessary.
|
|
||||||
pub fn to_computed_value_zoomed(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
base_size: FontBaseSize,
|
|
||||||
) -> CalcLengthPercentage {
|
|
||||||
self.to_computed_value_with_zoom(
|
|
||||||
context,
|
|
||||||
|abs| context.maybe_zoom_text(abs.into()),
|
|
||||||
base_size,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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(());
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.absolute {
|
|
||||||
Some(abs) => Ok(abs.to_px()),
|
|
||||||
None => {
|
|
||||||
debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self);
|
|
||||||
Err(())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> CalcLengthPercentage {
|
|
||||||
// normal properties don't zoom, and compute em units against the current style's font-size
|
|
||||||
self.to_computed_value_with_zoom(context, |abs| abs, FontBaseSize::CurrentStyle)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_computed_value(computed: &CalcLengthPercentage) -> Self {
|
|
||||||
specified::CalcLengthPercentage {
|
|
||||||
clamping_mode: computed.clamping_mode,
|
|
||||||
absolute: Some(AbsoluteLength::from_computed_value(&computed.length)),
|
|
||||||
percentage: computed.specified_percentage(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LengthPercentage {
|
|
||||||
/// 1px length value for SVG defaults
|
|
||||||
#[inline]
|
|
||||||
pub fn one() -> Self {
|
|
||||||
Self::Length(Length::new(1.))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a length value.
|
|
||||||
#[inline]
|
|
||||||
pub fn new_length(l: Length) -> Self {
|
|
||||||
Self::Length(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a percentage value.
|
|
||||||
#[inline]
|
|
||||||
pub fn new_percent(p: Percentage) -> Self {
|
|
||||||
Self::Percentage(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a `calc()` value.
|
|
||||||
#[inline]
|
|
||||||
pub fn new_calc(l: Length, percentage: Percentage) -> Self {
|
|
||||||
CalcLengthPercentage::new(l, Some(percentage)).to_length_percentge()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the computed value is absolute 0 or 0%.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_definitely_zero(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Self::Length(l) => l.px() == 0.0,
|
|
||||||
Self::Percentage(p) => p.0 == 0.0,
|
|
||||||
Self::Calc(ref c) => c.is_definitely_zero(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `<length>` component of this `calc()`, unclamped.
|
|
||||||
#[inline]
|
|
||||||
pub fn unclamped_length(&self) -> Length {
|
|
||||||
match *self {
|
|
||||||
Self::Length(l) => l,
|
|
||||||
Self::Percentage(..) => Zero::zero(),
|
|
||||||
Self::Calc(ref 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 {
|
|
||||||
Self::Length(l) => l,
|
|
||||||
Self::Percentage(..) => Zero::zero(),
|
|
||||||
Self::Calc(ref 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 {
|
|
||||||
Self::Length(..) => 0.,
|
|
||||||
Self::Percentage(p) => p.0,
|
|
||||||
Self::Calc(ref c) => c.percentage.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `<length>` component of this `calc()`, clamped.
|
|
||||||
#[inline]
|
|
||||||
pub fn as_percentage(&self) -> Option<Percentage> {
|
|
||||||
match *self {
|
|
||||||
Self::Length(..) => None,
|
|
||||||
Self::Percentage(p) => Some(p),
|
|
||||||
Self::Calc(ref c) => c.as_percentage(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolves the percentage.
|
|
||||||
#[inline]
|
|
||||||
pub fn resolve(&self, basis: Length) -> Length {
|
|
||||||
match *self {
|
|
||||||
Self::Length(l) => l,
|
|
||||||
Self::Percentage(p) => Length::new(basis.0 * p.0),
|
|
||||||
Self::Calc(ref c) => c.resolve(basis),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolves the percentage. Just an alias of resolve().
|
|
||||||
#[inline]
|
|
||||||
pub fn percentage_relative_to(&self, basis: Length) -> Length {
|
|
||||||
self.resolve(basis)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return whether there's any percentage in this value.
|
|
||||||
#[inline]
|
|
||||||
pub fn has_percentage(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Self::Length(..) => false,
|
|
||||||
Self::Percentage(..) => true,
|
|
||||||
Self::Calc(ref c) => c.has_percentage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the specified percentage if any.
|
|
||||||
#[inline]
|
|
||||||
pub fn specified_percentage(&self) -> Option<Percentage> {
|
|
||||||
match *self {
|
|
||||||
Self::Length(..) => None,
|
|
||||||
Self::Percentage(p) => Some(p),
|
|
||||||
Self::Calc(ref c) => c.specified_percentage(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the used value.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_used_value(&self, containing_length: Au) -> Au {
|
|
||||||
Au::from(self.to_pixel_length(containing_length))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the used value as CSSPixelLength.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_pixel_length(&self, containing_length: Au) -> Length {
|
|
||||||
self.resolve(containing_length.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the computed value into used value.
|
|
||||||
#[inline]
|
|
||||||
fn maybe_to_used_value(&self, container_len: Option<Length>) -> Option<Au> {
|
|
||||||
self.maybe_percentage_relative_to(container_len).map(Au::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If there are special rules for computing percentages in a value (e.g.
|
|
||||||
/// 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?));
|
|
||||||
}
|
|
||||||
Some(self.length())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the clamped non-negative values.
|
|
||||||
#[inline]
|
|
||||||
pub fn clamp_to_non_negative(self) -> Self {
|
|
||||||
match self {
|
|
||||||
Self::Length(l) => Self::Length(l.clamp_to_non_negative()),
|
|
||||||
Self::Percentage(p) => Self::Percentage(p.clamp_to_non_negative()),
|
|
||||||
Self::Calc(c) => c.clamp_to_non_negative().to_length_percentge(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CalcLengthPercentage {
|
|
||||||
/// Returns a new `LengthPercentage`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(length: Length, percentage: Option<Percentage>) -> Self {
|
|
||||||
Self::with_clamping_mode(length, percentage, AllowedNumericType::All)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts this to a `LengthPercentage`, simplifying if possible.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_length_percentge(self) -> LengthPercentage {
|
|
||||||
if !self.has_percentage {
|
|
||||||
return LengthPercentage::Length(self.length_component())
|
|
||||||
}
|
|
||||||
if self.length.is_zero() {
|
|
||||||
return LengthPercentage::Percentage(Percentage(self.clamping_mode.clamp(self.percentage.0)));
|
|
||||||
}
|
|
||||||
LengthPercentage::Calc(Box::new(self))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn specified_percentage(&self) -> Option<Percentage> {
|
|
||||||
if self.has_percentage {
|
|
||||||
Some(self.percentage)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new `LengthPercentage` with a specific clamping mode.
|
|
||||||
#[inline]
|
|
||||||
fn with_clamping_mode(
|
|
||||||
length: Length,
|
|
||||||
percentage: Option<Percentage>,
|
|
||||||
clamping_mode: AllowedNumericType,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
clamping_mode,
|
|
||||||
length,
|
|
||||||
percentage: percentage.unwrap_or_default(),
|
|
||||||
has_percentage: percentage.is_some(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the length component of this `calc()`
|
|
||||||
#[inline]
|
|
||||||
fn length_component(&self) -> CSSPixelLength {
|
|
||||||
Length::new(self.clamping_mode.clamp(self.length.px()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the percentage component if this could be represented as a
|
|
||||||
/// non-calc percentage.
|
|
||||||
fn as_percentage(&self) -> Option<Percentage> {
|
|
||||||
if !self.has_percentage || self.length.px() != 0. {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Percentage(self.clamping_mode.clamp(self.percentage.0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolves the percentage.
|
|
||||||
#[inline]
|
|
||||||
pub fn resolve(&self, basis: Length) -> Length {
|
|
||||||
let length = self.length.0 + basis.0 * self.percentage.0;
|
|
||||||
Length::new(self.clamping_mode.clamp(length))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolves the percentage.
|
|
||||||
#[inline]
|
|
||||||
pub fn percentage_relative_to(&self, basis: Length) -> Length {
|
|
||||||
self.resolve(basis)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the length, without clamping.
|
|
||||||
#[inline]
|
|
||||||
pub fn unclamped_length(&self) -> Length {
|
|
||||||
self.length
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the computed value is absolute 0 or 0%.
|
|
||||||
#[inline]
|
|
||||||
fn is_definitely_zero(&self) -> bool {
|
|
||||||
self.length.px() == 0.0 && self.percentage.0 == 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the clamped non-negative values.
|
|
||||||
#[inline]
|
|
||||||
fn clamp_to_non_negative(self) -> Self {
|
|
||||||
if self.has_percentage {
|
|
||||||
// If we can eagerly clamp the percentage then just do that.
|
|
||||||
if self.length.is_zero() {
|
|
||||||
return Self::with_clamping_mode(
|
|
||||||
Length::zero(),
|
|
||||||
Some(self.percentage.clamp_to_non_negative()),
|
|
||||||
AllowedNumericType::NonNegative,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Self::with_clamping_mode(self.length, Some(self.percentage), AllowedNumericType::NonNegative);
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::with_clamping_mode(
|
|
||||||
self.length.clamp_to_non_negative(),
|
|
||||||
None,
|
|
||||||
AllowedNumericType::NonNegative,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for specified::LengthPercentage {
|
|
||||||
type ComputedValue = LengthPercentage;
|
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> LengthPercentage {
|
|
||||||
match *self {
|
|
||||||
specified::LengthPercentage::Length(ref value) => {
|
|
||||||
LengthPercentage::Length(value.to_computed_value(context))
|
|
||||||
},
|
|
||||||
specified::LengthPercentage::Percentage(value) => {
|
|
||||||
LengthPercentage::Percentage(value)
|
|
||||||
},
|
|
||||||
specified::LengthPercentage::Calc(ref calc) => {
|
|
||||||
(**calc).to_computed_value(context).to_length_percentge()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_computed_value(computed: &LengthPercentage) -> Self {
|
|
||||||
match *computed {
|
|
||||||
LengthPercentage::Length(ref l) => {
|
|
||||||
specified::LengthPercentage::Length(ToComputedValue::from_computed_value(l))
|
|
||||||
}
|
|
||||||
LengthPercentage::Percentage(p) => {
|
|
||||||
specified::LengthPercentage::Percentage(p)
|
|
||||||
}
|
|
||||||
LengthPercentage::Calc(ref c) => {
|
|
||||||
if let Some(p) = c.as_percentage() {
|
|
||||||
return specified::LengthPercentage::Percentage(p)
|
|
||||||
}
|
|
||||||
if !c.has_percentage {
|
|
||||||
return specified::LengthPercentage::Length(ToComputedValue::from_computed_value(&c.length_component()))
|
|
||||||
}
|
|
||||||
specified::LengthPercentage::Calc(Box::new(specified::CalcLengthPercentage::from_computed_value(c)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Zero for LengthPercentage {
|
|
||||||
fn zero() -> Self {
|
|
||||||
LengthPercentage::Length(Length::zero())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_zero(&self) -> bool {
|
|
||||||
self.is_definitely_zero()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Some boilerplate to share between negative and non-negative
|
/// Some boilerplate to share between negative and non-negative
|
||||||
/// length-percentage or auto.
|
/// length-percentage or auto.
|
||||||
macro_rules! computed_length_percentage_or_auto {
|
macro_rules! computed_length_percentage_or_auto {
|
||||||
|
@ -640,70 +142,6 @@ impl NonNegativeLengthPercentageOrAuto {
|
||||||
computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
|
computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper of LengthPercentage, whose value must be >= 0.
|
|
||||||
pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
|
|
||||||
|
|
||||||
impl ToAnimatedValue for NonNegativeLengthPercentage {
|
|
||||||
type AnimatedValue = LengthPercentage;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_value(self) -> Self::AnimatedValue {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
|
||||||
NonNegative(animated.clamp_to_non_negative())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NonNegativeLength> for NonNegativeLengthPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn from(length: NonNegativeLength) -> Self {
|
|
||||||
NonNegative(LengthPercentage::new_length(length.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LengthPercentage> for NonNegativeLengthPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn from(lp: LengthPercentage) -> Self {
|
|
||||||
NonNegative(lp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(emilio): This is a really generic impl which is only needed to implement
|
|
||||||
// Animated and co for Spacing<>. Get rid of this, probably?
|
|
||||||
impl From<Au> for LengthPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn from(length: Au) -> Self {
|
|
||||||
LengthPercentage::new_length(length.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NonNegativeLengthPercentage {
|
|
||||||
/// Returns true if the computed value is absolute 0 or 0%.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_definitely_zero(&self) -> bool {
|
|
||||||
self.0.is_definitely_zero()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the used value.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_used_value(&self, containing_length: Au) -> Au {
|
|
||||||
let resolved = self.0.to_used_value(containing_length);
|
|
||||||
std::cmp::max(resolved, Au(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the computed value into used value.
|
|
||||||
#[inline]
|
|
||||||
pub fn maybe_to_used_value(&self, containing_length: Option<Au>) -> Option<Au> {
|
|
||||||
let resolved = self
|
|
||||||
.0
|
|
||||||
.maybe_to_used_value(containing_length.map(|v| v.into()))?;
|
|
||||||
Some(std::cmp::max(resolved, Au(0)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
impl MaxSize {
|
impl MaxSize {
|
||||||
/// Convert the computed value into used value.
|
/// Convert the computed value into used value.
|
||||||
|
|
|
@ -0,0 +1,559 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
//! `<length-percentage>` computed values, and related ones.
|
||||||
|
|
||||||
|
use super::{Context, Length, Percentage, ToComputedValue};
|
||||||
|
use crate::values::animated::ToAnimatedValue;
|
||||||
|
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
|
use crate::values::generics::NonNegative;
|
||||||
|
use crate::values::specified::length::FontBaseSize;
|
||||||
|
use crate::values::{specified, CSSFloat};
|
||||||
|
use crate::Zero;
|
||||||
|
use app_units::Au;
|
||||||
|
use std::fmt::{self, Write};
|
||||||
|
use style_traits::values::specified::AllowedNumericType;
|
||||||
|
use style_traits::{CssWriter, ToCss};
|
||||||
|
|
||||||
|
/// A `<length-percentage>` value. This can be either a `<length>`, a
|
||||||
|
/// `<percentage>`, or a combination of both via `calc()`.
|
||||||
|
///
|
||||||
|
/// cbindgen:private-default-tagged-enum-constructor=false
|
||||||
|
/// cbindgen:derive-mut-casts=true
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum LengthPercentage {
|
||||||
|
Length(Length),
|
||||||
|
Percentage(Percentage),
|
||||||
|
Calc(Box<CalcLengthPercentage>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LengthPercentage {
|
||||||
|
/// 1px length value for SVG defaults
|
||||||
|
#[inline]
|
||||||
|
pub fn one() -> Self {
|
||||||
|
Self::new_length(Length::new(1.))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a length value.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_length(l: Length) -> Self {
|
||||||
|
Self::Length(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a percentage value.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_percent(p: Percentage) -> Self {
|
||||||
|
Self::Percentage(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a `calc()` value.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_calc(l: Length, percentage: Option<Percentage>) -> Self {
|
||||||
|
CalcLengthPercentage::new(l, percentage).to_length_percentge()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the computed value is absolute 0 or 0%.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_definitely_zero(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Self::Length(l) => l.px() == 0.0,
|
||||||
|
Self::Percentage(p) => p.0 == 0.0,
|
||||||
|
Self::Calc(ref c) => c.is_definitely_zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `<length>` component of this `calc()`, unclamped.
|
||||||
|
#[inline]
|
||||||
|
pub fn unclamped_length(&self) -> Length {
|
||||||
|
match *self {
|
||||||
|
Self::Length(l) => l,
|
||||||
|
Self::Percentage(..) => Zero::zero(),
|
||||||
|
Self::Calc(ref 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 {
|
||||||
|
Self::Length(l) => l,
|
||||||
|
Self::Percentage(..) => Zero::zero(),
|
||||||
|
Self::Calc(ref 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 {
|
||||||
|
Self::Length(..) => 0.,
|
||||||
|
Self::Percentage(p) => p.0,
|
||||||
|
Self::Calc(ref c) => c.percentage.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `<length>` component of this `calc()`, clamped.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_percentage(&self) -> Option<Percentage> {
|
||||||
|
match *self {
|
||||||
|
Self::Length(..) => None,
|
||||||
|
Self::Percentage(p) => Some(p),
|
||||||
|
Self::Calc(ref c) => c.as_percentage(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the percentage.
|
||||||
|
#[inline]
|
||||||
|
pub fn resolve(&self, basis: Length) -> Length {
|
||||||
|
match *self {
|
||||||
|
Self::Length(l) => l,
|
||||||
|
Self::Percentage(p) => Length::new(basis.px() * p.0),
|
||||||
|
Self::Calc(ref c) => c.resolve(basis),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the percentage. Just an alias of resolve().
|
||||||
|
#[inline]
|
||||||
|
pub fn percentage_relative_to(&self, basis: Length) -> Length {
|
||||||
|
self.resolve(basis)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return whether there's any percentage in this value.
|
||||||
|
#[inline]
|
||||||
|
pub fn has_percentage(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Self::Length(..) => false,
|
||||||
|
Self::Percentage(..) => true,
|
||||||
|
Self::Calc(ref c) => c.has_percentage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the specified percentage if any.
|
||||||
|
#[inline]
|
||||||
|
pub fn specified_percentage(&self) -> Option<Percentage> {
|
||||||
|
match *self {
|
||||||
|
Self::Length(..) => None,
|
||||||
|
Self::Percentage(p) => Some(p),
|
||||||
|
Self::Calc(ref c) => c.specified_percentage(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the used value.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_used_value(&self, containing_length: Au) -> Au {
|
||||||
|
Au::from(self.to_pixel_length(containing_length))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the used value as CSSPixelLength.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_pixel_length(&self, containing_length: Au) -> Length {
|
||||||
|
self.resolve(containing_length.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the computed value into used value.
|
||||||
|
#[inline]
|
||||||
|
fn maybe_to_used_value(&self, container_len: Option<Length>) -> Option<Au> {
|
||||||
|
self.maybe_percentage_relative_to(container_len).map(Au::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If there are special rules for computing percentages in a value (e.g.
|
||||||
|
/// 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?));
|
||||||
|
}
|
||||||
|
Some(self.length())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the clamped non-negative values.
|
||||||
|
#[inline]
|
||||||
|
pub fn clamp_to_non_negative(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Length(l) => Self::Length(l.clamp_to_non_negative()),
|
||||||
|
Self::Percentage(p) => Self::Percentage(p.clamp_to_non_negative()),
|
||||||
|
Self::Calc(c) => c.clamp_to_non_negative().to_length_percentge(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToComputedValue for specified::LengthPercentage {
|
||||||
|
type ComputedValue = LengthPercentage;
|
||||||
|
|
||||||
|
fn to_computed_value(&self, context: &Context) -> LengthPercentage {
|
||||||
|
match *self {
|
||||||
|
specified::LengthPercentage::Length(ref value) => {
|
||||||
|
LengthPercentage::Length(value.to_computed_value(context))
|
||||||
|
},
|
||||||
|
specified::LengthPercentage::Percentage(value) => {
|
||||||
|
LengthPercentage::Percentage(value)
|
||||||
|
},
|
||||||
|
specified::LengthPercentage::Calc(ref calc) => {
|
||||||
|
(**calc).to_computed_value(context).to_length_percentge()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_computed_value(computed: &LengthPercentage) -> Self {
|
||||||
|
match *computed {
|
||||||
|
LengthPercentage::Length(ref l) => {
|
||||||
|
specified::LengthPercentage::Length(ToComputedValue::from_computed_value(l))
|
||||||
|
}
|
||||||
|
LengthPercentage::Percentage(p) => {
|
||||||
|
specified::LengthPercentage::Percentage(p)
|
||||||
|
}
|
||||||
|
LengthPercentage::Calc(ref c) => {
|
||||||
|
if let Some(p) = c.as_percentage() {
|
||||||
|
return specified::LengthPercentage::Percentage(p)
|
||||||
|
}
|
||||||
|
if !c.has_percentage {
|
||||||
|
return specified::LengthPercentage::Length(ToComputedValue::from_computed_value(&c.length_component()))
|
||||||
|
}
|
||||||
|
specified::LengthPercentage::Calc(Box::new(specified::CalcLengthPercentage::from_computed_value(c)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComputeSquaredDistance for LengthPercentage {
|
||||||
|
#[inline]
|
||||||
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||||
|
// FIXME(nox): This looks incorrect to me, to add a distance between lengths
|
||||||
|
// with a distance between percentages.
|
||||||
|
Ok(self
|
||||||
|
.unclamped_length()
|
||||||
|
.compute_squared_distance(&other.unclamped_length())? +
|
||||||
|
self.percentage()
|
||||||
|
.compute_squared_distance(&other.percentage())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for LengthPercentage {
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
specified::LengthPercentage::from_computed_value(self).to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zero for LengthPercentage {
|
||||||
|
fn zero() -> Self {
|
||||||
|
LengthPercentage::Length(Length::zero())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.is_definitely_zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The representation of a calc() function.
|
||||||
|
#[derive(
|
||||||
|
Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue,
|
||||||
|
)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CalcLengthPercentage {
|
||||||
|
length: Length,
|
||||||
|
|
||||||
|
percentage: Percentage,
|
||||||
|
|
||||||
|
#[animation(constant)]
|
||||||
|
clamping_mode: AllowedNumericType,
|
||||||
|
|
||||||
|
/// Whether we specified a percentage or not.
|
||||||
|
#[animation(constant)]
|
||||||
|
pub has_percentage: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CalcLengthPercentage {
|
||||||
|
/// Returns a new `LengthPercentage`.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(length: Length, percentage: Option<Percentage>) -> Self {
|
||||||
|
Self::with_clamping_mode(length, percentage, AllowedNumericType::All)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts this to a `LengthPercentage`, simplifying if possible.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_length_percentge(self) -> LengthPercentage {
|
||||||
|
if !self.has_percentage {
|
||||||
|
return LengthPercentage::Length(self.length_component())
|
||||||
|
}
|
||||||
|
if self.length.is_zero() {
|
||||||
|
return LengthPercentage::Percentage(Percentage(self.clamping_mode.clamp(self.percentage.0)));
|
||||||
|
}
|
||||||
|
LengthPercentage::Calc(Box::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn specified_percentage(&self) -> Option<Percentage> {
|
||||||
|
if self.has_percentage {
|
||||||
|
Some(self.percentage)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new `LengthPercentage` with a specific clamping mode.
|
||||||
|
#[inline]
|
||||||
|
fn with_clamping_mode(
|
||||||
|
length: Length,
|
||||||
|
percentage: Option<Percentage>,
|
||||||
|
clamping_mode: AllowedNumericType,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
clamping_mode,
|
||||||
|
length,
|
||||||
|
percentage: percentage.unwrap_or_default(),
|
||||||
|
has_percentage: percentage.is_some(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the length component of this `calc()`, clamped.
|
||||||
|
#[inline]
|
||||||
|
pub fn length_component(&self) -> Length {
|
||||||
|
Length::new(self.clamping_mode.clamp(self.length.px()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the percentage component if this could be represented as a
|
||||||
|
/// non-calc percentage.
|
||||||
|
fn as_percentage(&self) -> Option<Percentage> {
|
||||||
|
if !self.has_percentage || self.length.px() != 0. {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Percentage(self.clamping_mode.clamp(self.percentage.0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the percentage.
|
||||||
|
#[inline]
|
||||||
|
pub fn percentage_relative_to(&self, basis: Length) -> Length {
|
||||||
|
self.resolve(basis)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the length, without clamping.
|
||||||
|
#[inline]
|
||||||
|
pub fn unclamped_length(&self) -> Length {
|
||||||
|
self.length
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the computed value is absolute 0 or 0%.
|
||||||
|
#[inline]
|
||||||
|
fn is_definitely_zero(&self) -> bool {
|
||||||
|
self.length.px() == 0.0 && self.percentage.0 == 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the clamped non-negative values.
|
||||||
|
#[inline]
|
||||||
|
fn clamp_to_non_negative(self) -> Self {
|
||||||
|
if self.has_percentage {
|
||||||
|
// If we can eagerly clamp the percentage then just do that.
|
||||||
|
if self.length.is_zero() {
|
||||||
|
return Self::with_clamping_mode(
|
||||||
|
Length::zero(),
|
||||||
|
Some(self.percentage.clamp_to_non_negative()),
|
||||||
|
AllowedNumericType::NonNegative,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Self::with_clamping_mode(self.length, Some(self.percentage), AllowedNumericType::NonNegative);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::with_clamping_mode(
|
||||||
|
self.length.clamp_to_non_negative(),
|
||||||
|
None,
|
||||||
|
AllowedNumericType::NonNegative,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(emilio): We don't compare `clamping_mode` since we want to preserve the
|
||||||
|
// invariant that `from_computed_value(length).to_computed_value(..) == length`.
|
||||||
|
//
|
||||||
|
// Right now for e.g. a non-negative length, we set clamping_mode to `All`
|
||||||
|
// unconditionally for non-calc values, and to `NonNegative` for calc.
|
||||||
|
//
|
||||||
|
// If we determine that it's sound, from_computed_value() can generate an
|
||||||
|
// absolute length, which then would get `All` as the clamping mode.
|
||||||
|
//
|
||||||
|
// We may want to just eagerly-detect whether we can clamp in
|
||||||
|
// `LengthPercentage::new` and switch to `AllowedNumericType::NonNegative` then,
|
||||||
|
// maybe.
|
||||||
|
impl PartialEq for CalcLengthPercentage {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.length == other.length &&
|
||||||
|
self.percentage == other.percentage &&
|
||||||
|
self.has_percentage == other.has_percentage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl specified::CalcLengthPercentage {
|
||||||
|
/// Compute the value, zooming any absolute units by the zoom function.
|
||||||
|
fn to_computed_value_with_zoom<F>(
|
||||||
|
&self,
|
||||||
|
context: &Context,
|
||||||
|
zoom_fn: F,
|
||||||
|
base_size: FontBaseSize,
|
||||||
|
) -> CalcLengthPercentage
|
||||||
|
where
|
||||||
|
F: Fn(Length) -> Length,
|
||||||
|
{
|
||||||
|
use std::f32;
|
||||||
|
use crate::values::specified::length::{ViewportPercentageLength, FontRelativeLength};
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CalcLengthPercentage::with_clamping_mode(
|
||||||
|
Length::new(length.min(f32::MAX).max(f32::MIN)),
|
||||||
|
self.percentage,
|
||||||
|
self.clamping_mode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute font-size or line-height taking into account text-zoom if necessary.
|
||||||
|
pub fn to_computed_value_zoomed(
|
||||||
|
&self,
|
||||||
|
context: &Context,
|
||||||
|
base_size: FontBaseSize,
|
||||||
|
) -> CalcLengthPercentage {
|
||||||
|
self.to_computed_value_with_zoom(
|
||||||
|
context,
|
||||||
|
|abs| context.maybe_zoom_text(abs.into()),
|
||||||
|
base_size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(());
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.absolute {
|
||||||
|
Some(abs) => Ok(abs.to_px()),
|
||||||
|
None => {
|
||||||
|
debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self);
|
||||||
|
Err(())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the calc using the current font-size (and without text-zoom).
|
||||||
|
pub fn to_computed_value(&self, context: &Context) -> CalcLengthPercentage {
|
||||||
|
self.to_computed_value_with_zoom(context, |abs| abs, FontBaseSize::CurrentStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_computed_value(computed: &CalcLengthPercentage) -> Self {
|
||||||
|
use crate::values::specified::length::AbsoluteLength;
|
||||||
|
|
||||||
|
specified::CalcLengthPercentage {
|
||||||
|
clamping_mode: computed.clamping_mode,
|
||||||
|
absolute: Some(AbsoluteLength::from_computed_value(&computed.length)),
|
||||||
|
percentage: computed.specified_percentage(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper of LengthPercentage, whose value must be >= 0.
|
||||||
|
pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
|
||||||
|
|
||||||
|
impl ToAnimatedValue for NonNegativeLengthPercentage {
|
||||||
|
type AnimatedValue = LengthPercentage;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_value(self) -> Self::AnimatedValue {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||||||
|
NonNegative(animated.clamp_to_non_negative())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NonNegativeLengthPercentage {
|
||||||
|
/// Returns true if the computed value is absolute 0 or 0%.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_definitely_zero(&self) -> bool {
|
||||||
|
self.0.is_definitely_zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the used value.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_used_value(&self, containing_length: Au) -> Au {
|
||||||
|
let resolved = self.0.to_used_value(containing_length);
|
||||||
|
std::cmp::max(resolved, Au(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the computed value into used value.
|
||||||
|
#[inline]
|
||||||
|
pub fn maybe_to_used_value(&self, containing_length: Option<Au>) -> Option<Au> {
|
||||||
|
let resolved = self
|
||||||
|
.0
|
||||||
|
.maybe_to_used_value(containing_length.map(|v| v.into()))?;
|
||||||
|
Some(std::cmp::max(resolved, Au(0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -108,6 +108,7 @@ pub mod flex;
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
pub mod length;
|
pub mod length;
|
||||||
|
pub mod length_percentage;
|
||||||
pub mod list;
|
pub mod list;
|
||||||
pub mod motion;
|
pub mod motion;
|
||||||
pub mod outline;
|
pub mod outline;
|
||||||
|
|
|
@ -297,7 +297,7 @@ impl<S: Side> ToComputedValue for PositionComponent<S> {
|
||||||
let p = Percentage(1. - length.percentage());
|
let p = Percentage(1. - length.percentage());
|
||||||
let l = -length.unclamped_length();
|
let l = -length.unclamped_length();
|
||||||
// We represent `<end-side> <length>` as `calc(100% - <length>)`.
|
// We represent `<end-side> <length>` as `calc(100% - <length>)`.
|
||||||
ComputedLengthPercentage::new_calc(l, p)
|
ComputedLengthPercentage::new_calc(l, Some(p))
|
||||||
},
|
},
|
||||||
PositionComponent::Side(_, Some(ref length)) |
|
PositionComponent::Side(_, Some(ref length)) |
|
||||||
PositionComponent::Length(ref length) => length.to_computed_value(context),
|
PositionComponent::Length(ref length) => length.to_computed_value(context),
|
||||||
|
|
Загрузка…
Ссылка в новой задаче