position.rs (13945B)
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 8 use std::fmt::Write; 9 10 use style_traits::CssWriter; 11 use style_traits::SpecifiedValueInfo; 12 use style_traits::ToCss; 13 14 use crate::derives::*; 15 use crate::logical_geometry::PhysicalSide; 16 use crate::values::animated::ToAnimatedZero; 17 use crate::values::computed::position::TryTacticAdjustment; 18 use crate::values::generics::box_::PositionProperty; 19 use crate::values::generics::length::GenericAnchorSizeFunction; 20 use crate::values::generics::ratio::Ratio; 21 use crate::values::generics::Optional; 22 use 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)] 44 pub 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 51 impl<H, V> PositionComponent for Position<H, V> 52 where 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 62 pub use self::GenericPosition as Position; 63 64 impl<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. 75 pub 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)] 105 pub enum GenericPositionOrAuto<Pos> { 106 /// The <position> value. 107 Position(Pos), 108 /// The keyword `auto`. 109 Auto, 110 } 111 112 pub use self::GenericPositionOrAuto as PositionOrAuto; 113 114 impl<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)] 148 pub enum GenericZIndex<I> { 149 /// An integer value. 150 Integer(I), 151 /// The keyword `auto`. 152 Auto, 153 } 154 155 pub use self::GenericZIndex as ZIndex; 156 157 impl<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)] 197 pub 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)] 228 pub 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 240 pub use self::GenericAspectRatio as AspectRatio; 241 242 impl<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 253 impl<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)] 284 pub 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 302 impl<P, LP> SpecifiedValueInfo for GenericInset<P, LP> 303 where 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 315 impl<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 330 pub 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)] 353 pub 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 365 impl<Percentage, Fallback> ToCss for GenericAnchorFunction<Percentage, Fallback> 366 where 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 389 impl<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)] 417 pub 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 444 impl 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 465 impl 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 498 impl 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)] 535 pub 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 542 impl<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 }