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