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