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(
286        #[animation(field_bound)]
287        #[distance(field_bound)]
288        Box<GenericAnchorFunction<P, LP>>,
289    ),
290    /// Inset defined by the size of the anchor element.
291    ///
292    /// <https://drafts.csswg.org/css-anchor-position-1/#anchor-pos>
293    AnchorSizeFunction(
294        #[animation(field_bound)]
295        #[distance(field_bound)]
296        Box<GenericAnchorSizeFunction<LP>>,
297    ),
298    /// A `<length-percentage>` value, guaranteed to contain `calc()`,
299    /// which then is guaranteed to contain `anchor()` or `anchor-size()`.
300    AnchorContainingCalcFunction(LP),
301}
302
303impl<P, LP> SpecifiedValueInfo for GenericInset<P, LP>
304where
305    LP: SpecifiedValueInfo,
306{
307    fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
308        LP::collect_completion_keywords(f);
309        f(&["auto"]);
310        if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
311            f(&["anchor", "anchor-size"]);
312        }
313    }
314}
315
316impl<P, LP> GenericInset<P, LP> {
317    /// `auto` value.
318    #[inline]
319    pub fn auto() -> Self {
320        Self::Auto
321    }
322
323    /// Return true if it is 'auto'.
324    #[inline]
325    #[cfg(feature = "servo")]
326    pub fn is_auto(&self) -> bool {
327        matches!(self, Self::Auto)
328    }
329}
330
331pub use self::GenericInset as Inset;
332
333/// Anchor function used by inset properties. This resolves
334/// to length at computed time.
335///
336/// https://drafts.csswg.org/css-anchor-position-1/#funcdef-anchor
337#[derive(
338    Animate,
339    Clone,
340    ComputeSquaredDistance,
341    Debug,
342    MallocSizeOf,
343    PartialEq,
344    SpecifiedValueInfo,
345    ToShmem,
346    ToAnimatedValue,
347    ToAnimatedZero,
348    ToComputedValue,
349    ToResolvedValue,
350    Serialize,
351    Deserialize,
352)]
353#[repr(C)]
354pub struct GenericAnchorFunction<Percentage, LengthPercentage> {
355    /// Anchor name of the element to anchor to.
356    /// If omitted, selects the implicit anchor element.
357    #[animation(constant)]
358    pub target_element: DashedIdent,
359    /// Where relative to the target anchor element to position
360    /// the anchored element to.
361    pub side: GenericAnchorSide<Percentage>,
362    /// Value to use in case the anchor function is invalid.
363    pub fallback: Optional<LengthPercentage>,
364}
365
366impl<Percentage, LengthPercentage> ToCss for GenericAnchorFunction<Percentage, LengthPercentage>
367where
368    Percentage: ToCss,
369    LengthPercentage: ToCss,
370{
371    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result
372    where
373        W: Write,
374    {
375        dest.write_str("anchor(")?;
376        if !self.target_element.is_empty() {
377            self.target_element.to_css(dest)?;
378            dest.write_str(" ")?;
379        }
380        self.side.to_css(dest)?;
381        if let Some(f) = self.fallback.as_ref() {
382            // This comma isn't really `derive()`-able, unfortunately.
383            dest.write_str(", ")?;
384            f.to_css(dest)?;
385        }
386        dest.write_str(")")
387    }
388}
389
390impl<Percentage, LengthPercentage> GenericAnchorFunction<Percentage, LengthPercentage> {
391    /// Is the anchor valid for given property?
392    pub fn valid_for(
393        &self,
394        side: PhysicalSide,
395        position_property: PositionProperty,
396    ) -> bool {
397        position_property.is_absolutely_positioned() && self.side.valid_for(side)
398    }
399}
400
401/// Keyword values for the anchor positioning function.
402#[derive(
403    Animate,
404    Clone,
405    ComputeSquaredDistance,
406    Copy,
407    Debug,
408    MallocSizeOf,
409    PartialEq,
410    SpecifiedValueInfo,
411    ToCss,
412    ToShmem,
413    Parse,
414    ToAnimatedValue,
415    ToAnimatedZero,
416    ToComputedValue,
417    ToResolvedValue,
418    Serialize,
419    Deserialize,
420)]
421#[repr(u8)]
422pub enum AnchorSideKeyword {
423    /// Inside relative (i.e. Same side) to the inset property it's used in.
424    Inside,
425    /// Same as above, but outside (i.e. Opposite side).
426    Outside,
427    /// Top of the anchor element.
428    Top,
429    /// Left of the anchor element.
430    Left,
431    /// Right of the anchor element.
432    Right,
433    /// Bottom of the anchor element.
434    Bottom,
435    /// Refers to the start side of the anchor element for the same axis of the inset
436    /// property it's used in, resolved against the positioned element's containing
437    /// block's writing mode.
438    Start,
439    /// Same as above, but for the end side.
440    End,
441    /// Same as `start`, resolved against the positioned element's writing mode.
442    SelfStart,
443    /// Same as above, but for the end side.
444    SelfEnd,
445    /// Halfway between `start` and `end` sides.
446    Center,
447}
448
449impl AnchorSideKeyword {
450    fn valid_for(&self, side: PhysicalSide) -> bool {
451        match self {
452            Self::Left | Self::Right => matches!(side, PhysicalSide::Left | PhysicalSide::Right),
453            Self::Top | Self::Bottom => matches!(side, PhysicalSide::Top | PhysicalSide::Bottom),
454            Self::Inside |
455            Self::Outside |
456            Self::Start |
457            Self::End |
458            Self::SelfStart |
459            Self::SelfEnd |
460            Self::Center => true,
461        }
462    }
463}
464
465/// Anchor side for the anchor positioning function.
466#[derive(
467    Animate,
468    Clone,
469    ComputeSquaredDistance,
470    Copy,
471    Debug,
472    MallocSizeOf,
473    PartialEq,
474    Parse,
475    SpecifiedValueInfo,
476    ToCss,
477    ToShmem,
478    ToAnimatedValue,
479    ToAnimatedZero,
480    ToComputedValue,
481    ToResolvedValue,
482    Serialize,
483    Deserialize,
484)]
485#[repr(C)]
486pub enum GenericAnchorSide<P> {
487    /// A keyword value for the anchor side.
488    Keyword(AnchorSideKeyword),
489    /// Percentage value between the `start` and `end` sides.
490    Percentage(P),
491}
492
493impl<P> GenericAnchorSide<P> {
494    /// Is this anchor side valid for a given side?
495    pub fn valid_for(&self, side: PhysicalSide) -> bool {
496        match self {
497            Self::Keyword(k) => k.valid_for(side),
498            Self::Percentage(_) => true,
499        }
500    }
501}