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