style/values/generics/
position.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Generic types for CSS handling of specified and computed values of
6//! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position)
7
8use std::fmt::Write;
9
10use style_traits::CssWriter;
11use style_traits::SpecifiedValueInfo;
12use style_traits::ToCss;
13
14use crate::logical_geometry::PhysicalSide;
15use crate::values::animated::ToAnimatedZero;
16use crate::values::generics::box_::PositionProperty;
17use crate::values::generics::length::GenericAnchorSizeFunction;
18use crate::values::generics::ratio::Ratio;
19use crate::values::generics::Optional;
20use crate::values::DashedIdent;
21
22/// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position).
23#[derive(
24    Animate,
25    Clone,
26    ComputeSquaredDistance,
27    Copy,
28    Debug,
29    Deserialize,
30    MallocSizeOf,
31    PartialEq,
32    Serialize,
33    SpecifiedValueInfo,
34    ToAnimatedValue,
35    ToAnimatedZero,
36    ToComputedValue,
37    ToResolvedValue,
38    ToShmem,
39    ToTyped,
40)]
41#[repr(C)]
42pub struct GenericPosition<H, V> {
43    /// The horizontal component of position.
44    pub horizontal: H,
45    /// The vertical component of position.
46    pub vertical: V,
47}
48
49impl<H, V> PositionComponent for Position<H, V>
50where
51    H: PositionComponent,
52    V: PositionComponent,
53{
54    #[inline]
55    fn is_center(&self) -> bool {
56        self.horizontal.is_center() && self.vertical.is_center()
57    }
58}
59
60pub use self::GenericPosition as Position;
61
62impl<H, V> Position<H, V> {
63    /// Returns a new position.
64    pub fn new(horizontal: H, vertical: V) -> Self {
65        Self {
66            horizontal,
67            vertical,
68        }
69    }
70}
71
72/// Implements a method that checks if the position is centered.
73pub trait PositionComponent {
74    /// Returns if the position component is 50% or center.
75    /// For pixel lengths, it always returns false.
76    fn is_center(&self) -> bool;
77}
78
79/// A generic type for representing an `Auto | <position>`.
80/// This is used by <offset-anchor> for now.
81/// https://drafts.fxtf.org/motion-1/#offset-anchor-property
82#[derive(
83    Animate,
84    Clone,
85    ComputeSquaredDistance,
86    Copy,
87    Debug,
88    Deserialize,
89    MallocSizeOf,
90    Parse,
91    PartialEq,
92    Serialize,
93    SpecifiedValueInfo,
94    ToAnimatedZero,
95    ToAnimatedValue,
96    ToComputedValue,
97    ToCss,
98    ToResolvedValue,
99    ToShmem,
100    ToTyped,
101)]
102#[repr(C, u8)]
103pub enum GenericPositionOrAuto<Pos> {
104    /// The <position> value.
105    Position(Pos),
106    /// The keyword `auto`.
107    Auto,
108}
109
110pub use self::GenericPositionOrAuto as PositionOrAuto;
111
112impl<Pos> PositionOrAuto<Pos> {
113    /// Return `auto`.
114    #[inline]
115    pub fn auto() -> Self {
116        PositionOrAuto::Auto
117    }
118
119    /// Return true if it is 'auto'.
120    #[inline]
121    pub fn is_auto(&self) -> bool {
122        matches!(self, PositionOrAuto::Auto)
123    }
124}
125
126/// A generic value for the `z-index` property.
127#[derive(
128    Animate,
129    Clone,
130    ComputeSquaredDistance,
131    Copy,
132    Debug,
133    MallocSizeOf,
134    PartialEq,
135    Parse,
136    SpecifiedValueInfo,
137    ToAnimatedValue,
138    ToAnimatedZero,
139    ToComputedValue,
140    ToCss,
141    ToResolvedValue,
142    ToShmem,
143    ToTyped,
144)]
145#[repr(C, u8)]
146pub enum GenericZIndex<I> {
147    /// An integer value.
148    Integer(I),
149    /// The keyword `auto`.
150    Auto,
151}
152
153pub use self::GenericZIndex as ZIndex;
154
155impl<Integer> ZIndex<Integer> {
156    /// Returns `auto`
157    #[inline]
158    pub fn auto() -> Self {
159        ZIndex::Auto
160    }
161
162    /// Returns whether `self` is `auto`.
163    #[inline]
164    pub fn is_auto(self) -> bool {
165        matches!(self, ZIndex::Auto)
166    }
167
168    /// Returns the integer value if it is an integer, or `auto`.
169    #[inline]
170    pub fn integer_or(self, auto: Integer) -> Integer {
171        match self {
172            ZIndex::Integer(n) => n,
173            ZIndex::Auto => auto,
174        }
175    }
176}
177
178/// Ratio or None.
179#[derive(
180    Animate,
181    Clone,
182    ComputeSquaredDistance,
183    Copy,
184    Debug,
185    MallocSizeOf,
186    PartialEq,
187    SpecifiedValueInfo,
188    ToAnimatedValue,
189    ToComputedValue,
190    ToCss,
191    ToResolvedValue,
192    ToShmem,
193)]
194#[repr(C, u8)]
195pub enum PreferredRatio<N> {
196    /// Without specified ratio
197    #[css(skip)]
198    None,
199    /// With specified ratio
200    Ratio(
201        #[animation(field_bound)]
202        #[css(field_bound)]
203        #[distance(field_bound)]
204        Ratio<N>,
205    ),
206}
207
208/// A generic value for the `aspect-ratio` property, the value is `auto || <ratio>`.
209#[derive(
210    Animate,
211    Clone,
212    ComputeSquaredDistance,
213    Copy,
214    Debug,
215    MallocSizeOf,
216    PartialEq,
217    SpecifiedValueInfo,
218    ToAnimatedValue,
219    ToComputedValue,
220    ToCss,
221    ToResolvedValue,
222    ToShmem,
223    ToTyped,
224)]
225#[repr(C)]
226pub struct GenericAspectRatio<N> {
227    /// Specifiy auto or not.
228    #[animation(constant)]
229    #[css(represents_keyword)]
230    pub auto: bool,
231    /// The preferred aspect-ratio value.
232    #[animation(field_bound)]
233    #[css(field_bound)]
234    #[distance(field_bound)]
235    pub ratio: PreferredRatio<N>,
236}
237
238pub use self::GenericAspectRatio as AspectRatio;
239
240impl<N> AspectRatio<N> {
241    /// Returns `auto`
242    #[inline]
243    pub fn auto() -> Self {
244        AspectRatio {
245            auto: true,
246            ratio: PreferredRatio::None,
247        }
248    }
249}
250
251impl<N> ToAnimatedZero for AspectRatio<N> {
252    #[inline]
253    fn to_animated_zero(&self) -> Result<Self, ()> {
254        Err(())
255    }
256}
257
258/// Specified type for `inset` properties, which allows
259/// the use of the `anchor()` function.
260/// Note(dshin): `LengthPercentageOrAuto` is not used here because
261/// having `LengthPercentageOrAuto` and `AnchorFunction` in the enum
262/// pays the price of the discriminator for `LengthPercentage | Auto`
263/// as well as `LengthPercentageOrAuto | AnchorFunction`. This increases
264/// the size of the style struct, which would not be great.
265/// On the other hand, we trade for code duplication, so... :(
266#[derive(
267    Animate,
268    Clone,
269    ComputeSquaredDistance,
270    Debug,
271    MallocSizeOf,
272    PartialEq,
273    ToCss,
274    ToShmem,
275    ToAnimatedValue,
276    ToAnimatedZero,
277    ToComputedValue,
278    ToResolvedValue,
279    ToTyped,
280)]
281#[repr(C)]
282pub enum GenericInset<P, LP> {
283    /// A `<length-percentage>` value.
284    LengthPercentage(LP),
285    /// An `auto` value.
286    Auto,
287    /// Inset defined by the anchor element.
288    ///
289    /// <https://drafts.csswg.org/css-anchor-position-1/#anchor-pos>
290    AnchorFunction(Box<GenericAnchorFunction<P, Self>>),
291    /// Inset defined by the size of the anchor element.
292    ///
293    /// <https://drafts.csswg.org/css-anchor-position-1/#anchor-pos>
294    AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
295    /// A `<length-percentage>` value, guaranteed to contain `calc()`,
296    /// which then is guaranteed to contain `anchor()` or `anchor-size()`.
297    AnchorContainingCalcFunction(LP),
298}
299
300impl<P, LP> SpecifiedValueInfo for GenericInset<P, LP>
301where
302    LP: SpecifiedValueInfo,
303{
304    fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
305        LP::collect_completion_keywords(f);
306        f(&["auto"]);
307        if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
308            f(&["anchor", "anchor-size"]);
309        }
310    }
311}
312
313impl<P, LP> GenericInset<P, LP> {
314    /// `auto` value.
315    #[inline]
316    pub fn auto() -> Self {
317        Self::Auto
318    }
319
320    /// Return true if it is 'auto'.
321    #[inline]
322    #[cfg(feature = "servo")]
323    pub fn is_auto(&self) -> bool {
324        matches!(self, Self::Auto)
325    }
326}
327
328pub use self::GenericInset as Inset;
329
330/// Anchor function used by inset properties. This resolves
331/// to length at computed time.
332///
333/// https://drafts.csswg.org/css-anchor-position-1/#funcdef-anchor
334#[derive(
335    Animate,
336    Clone,
337    ComputeSquaredDistance,
338    Debug,
339    MallocSizeOf,
340    PartialEq,
341    SpecifiedValueInfo,
342    ToShmem,
343    ToAnimatedValue,
344    ToAnimatedZero,
345    ToComputedValue,
346    ToResolvedValue,
347    Serialize,
348    Deserialize,
349)]
350#[repr(C)]
351pub struct GenericAnchorFunction<Percentage, Fallback> {
352    /// Anchor name of the element to anchor to.
353    /// If omitted, selects the implicit anchor element.
354    #[animation(constant)]
355    pub target_element: DashedIdent,
356    /// Where relative to the target anchor element to position
357    /// the anchored element to.
358    pub side: GenericAnchorSide<Percentage>,
359    /// Value to use in case the anchor function is invalid.
360    pub fallback: Optional<Fallback>,
361}
362
363impl<Percentage, Fallback> ToCss for GenericAnchorFunction<Percentage, Fallback>
364where
365    Percentage: ToCss,
366    Fallback: ToCss,
367{
368    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result
369    where
370        W: Write,
371    {
372        dest.write_str("anchor(")?;
373        if !self.target_element.is_empty() {
374            self.target_element.to_css(dest)?;
375            dest.write_str(" ")?;
376        }
377        self.side.to_css(dest)?;
378        if let Some(f) = self.fallback.as_ref() {
379            // This comma isn't really `derive()`-able, unfortunately.
380            dest.write_str(", ")?;
381            f.to_css(dest)?;
382        }
383        dest.write_str(")")
384    }
385}
386
387impl<Percentage, Fallback> GenericAnchorFunction<Percentage, Fallback> {
388    /// Is the anchor valid for given property?
389    pub fn valid_for(&self, side: PhysicalSide, position_property: PositionProperty) -> bool {
390        position_property.is_absolutely_positioned() && self.side.valid_for(side)
391    }
392}
393
394/// Keyword values for the anchor positioning function.
395#[derive(
396    Animate,
397    Clone,
398    ComputeSquaredDistance,
399    Copy,
400    Debug,
401    MallocSizeOf,
402    PartialEq,
403    SpecifiedValueInfo,
404    ToCss,
405    ToShmem,
406    Parse,
407    ToAnimatedValue,
408    ToAnimatedZero,
409    ToComputedValue,
410    ToResolvedValue,
411    Serialize,
412    Deserialize,
413)]
414#[repr(u8)]
415pub enum AnchorSideKeyword {
416    /// Inside relative (i.e. Same side) to the inset property it's used in.
417    Inside,
418    /// Same as above, but outside (i.e. Opposite side).
419    Outside,
420    /// Top of the anchor element.
421    Top,
422    /// Left of the anchor element.
423    Left,
424    /// Right of the anchor element.
425    Right,
426    /// Bottom of the anchor element.
427    Bottom,
428    /// Refers to the start side of the anchor element for the same axis of the inset
429    /// property it's used in, resolved against the positioned element's containing
430    /// block's writing mode.
431    Start,
432    /// Same as above, but for the end side.
433    End,
434    /// Same as `start`, resolved against the positioned element's writing mode.
435    SelfStart,
436    /// Same as above, but for the end side.
437    SelfEnd,
438    /// Halfway between `start` and `end` sides.
439    Center,
440}
441
442impl AnchorSideKeyword {
443    fn valid_for(&self, side: PhysicalSide) -> bool {
444        match self {
445            Self::Left | Self::Right => matches!(side, PhysicalSide::Left | PhysicalSide::Right),
446            Self::Top | Self::Bottom => matches!(side, PhysicalSide::Top | PhysicalSide::Bottom),
447            Self::Inside
448            | Self::Outside
449            | Self::Start
450            | Self::End
451            | Self::SelfStart
452            | Self::SelfEnd
453            | Self::Center => true,
454        }
455    }
456}
457
458/// Anchor side for the anchor positioning function.
459#[derive(
460    Animate,
461    Clone,
462    ComputeSquaredDistance,
463    Copy,
464    Debug,
465    MallocSizeOf,
466    PartialEq,
467    Parse,
468    SpecifiedValueInfo,
469    ToCss,
470    ToShmem,
471    ToAnimatedValue,
472    ToAnimatedZero,
473    ToComputedValue,
474    ToResolvedValue,
475    Serialize,
476    Deserialize,
477)]
478#[repr(C)]
479pub enum GenericAnchorSide<P> {
480    /// A keyword value for the anchor side.
481    Keyword(AnchorSideKeyword),
482    /// Percentage value between the `start` and `end` sides.
483    Percentage(P),
484}
485
486impl<P> GenericAnchorSide<P> {
487    /// Is this anchor side valid for a given side?
488    pub fn valid_for(&self, side: PhysicalSide) -> bool {
489        match self {
490            Self::Keyword(k) => k.valid_for(side),
491            Self::Percentage(_) => true,
492        }
493    }
494}