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 //! Geometry in flow-relative space. 6 7 use crate::properties::style_structs; 8 use euclid::default::{Point2D, Rect, SideOffsets2D, Size2D}; 9 use euclid::num::Zero; 10 use std::cmp::{max, min}; 11 use std::fmt::{self, Debug, Error, Formatter}; 12 use std::ops::{Add, Sub}; 13 use unicode_bidi as bidi; 14 15 pub enum BlockFlowDirection { 16 TopToBottom, 17 RightToLeft, 18 LeftToRight, 19 } 20 21 pub enum InlineBaseDirection { 22 LeftToRight, 23 RightToLeft, 24 } 25 26 // TODO: improve the readability of the WritingMode serialization, refer to the Debug:fmt() 27 bitflags!( 28 #[cfg_attr(feature = "servo", derive(MallocSizeOf, Serialize))] 29 #[repr(C)] 30 pub struct WritingMode: u8 { 31 /// A vertical writing mode; writing-mode is vertical-rl, 32 /// vertical-lr, sideways-lr, or sideways-rl. 33 const VERTICAL = 1 << 0; 34 /// The inline flow direction is reversed against the physical 35 /// direction (i.e. right-to-left or bottom-to-top); writing-mode is 36 /// sideways-lr or direction is rtl (but not both). 37 /// 38 /// (This bit can be derived from the others, but we store it for 39 /// convenience.) 40 const INLINE_REVERSED = 1 << 1; 41 /// A vertical writing mode whose block progression direction is left- 42 /// to-right; writing-mode is vertical-lr or sideways-lr. 43 /// 44 /// Never set without VERTICAL. 45 const VERTICAL_LR = 1 << 2; 46 /// The line-over/line-under sides are inverted with respect to the 47 /// block-start/block-end edge; writing-mode is vertical-lr. 48 /// 49 /// Never set without VERTICAL and VERTICAL_LR. 50 const LINE_INVERTED = 1 << 3; 51 /// direction is rtl. 52 const RTL = 1 << 4; 53 /// All text within a vertical writing mode is displayed sideways 54 /// and runs top-to-bottom or bottom-to-top; set in these cases: 55 /// 56 /// * writing-mode: sideways-rl; 57 /// * writing-mode: sideways-lr; 58 /// 59 /// Never set without VERTICAL. 60 const VERTICAL_SIDEWAYS = 1 << 5; 61 /// Similar to VERTICAL_SIDEWAYS, but is set via text-orientation; 62 /// set in these cases: 63 /// 64 /// * writing-mode: vertical-rl; text-orientation: sideways; 65 /// * writing-mode: vertical-lr; text-orientation: sideways; 66 /// 67 /// Never set without VERTICAL. 68 const TEXT_SIDEWAYS = 1 << 6; 69 /// Horizontal text within a vertical writing mode is displayed with each 70 /// glyph upright; set in these cases: 71 /// 72 /// * writing-mode: vertical-rl; text-orientation: upright; 73 /// * writing-mode: vertical-lr: text-orientation: upright; 74 /// 75 /// Never set without VERTICAL. 76 const UPRIGHT = 1 << 7; 77 } 78 ); 79 80 impl WritingMode { 81 /// Return a WritingMode bitflags from the relevant CSS properties. new(inheritedbox_style: &style_structs::InheritedBox) -> Self82 pub fn new(inheritedbox_style: &style_structs::InheritedBox) -> Self { 83 use crate::properties::longhands::direction::computed_value::T as Direction; 84 use crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode; 85 86 let mut flags = WritingMode::empty(); 87 88 let direction = inheritedbox_style.clone_direction(); 89 let writing_mode = inheritedbox_style.clone_writing_mode(); 90 91 match direction { 92 Direction::Ltr => {}, 93 Direction::Rtl => { 94 flags.insert(WritingMode::RTL); 95 }, 96 } 97 98 match writing_mode { 99 SpecifiedWritingMode::HorizontalTb => { 100 if direction == Direction::Rtl { 101 flags.insert(WritingMode::INLINE_REVERSED); 102 } 103 }, 104 SpecifiedWritingMode::VerticalRl => { 105 flags.insert(WritingMode::VERTICAL); 106 if direction == Direction::Rtl { 107 flags.insert(WritingMode::INLINE_REVERSED); 108 } 109 }, 110 SpecifiedWritingMode::VerticalLr => { 111 flags.insert(WritingMode::VERTICAL); 112 flags.insert(WritingMode::VERTICAL_LR); 113 flags.insert(WritingMode::LINE_INVERTED); 114 if direction == Direction::Rtl { 115 flags.insert(WritingMode::INLINE_REVERSED); 116 } 117 }, 118 #[cfg(feature = "gecko")] 119 SpecifiedWritingMode::SidewaysRl => { 120 flags.insert(WritingMode::VERTICAL); 121 flags.insert(WritingMode::VERTICAL_SIDEWAYS); 122 if direction == Direction::Rtl { 123 flags.insert(WritingMode::INLINE_REVERSED); 124 } 125 }, 126 #[cfg(feature = "gecko")] 127 SpecifiedWritingMode::SidewaysLr => { 128 flags.insert(WritingMode::VERTICAL); 129 flags.insert(WritingMode::VERTICAL_LR); 130 flags.insert(WritingMode::VERTICAL_SIDEWAYS); 131 if direction == Direction::Ltr { 132 flags.insert(WritingMode::INLINE_REVERSED); 133 } 134 }, 135 } 136 137 #[cfg(feature = "gecko")] 138 { 139 use crate::properties::longhands::text_orientation::computed_value::T as TextOrientation; 140 141 // text-orientation only has an effect for vertical-rl and 142 // vertical-lr values of writing-mode. 143 match writing_mode { 144 SpecifiedWritingMode::VerticalRl | SpecifiedWritingMode::VerticalLr => { 145 match inheritedbox_style.clone_text_orientation() { 146 TextOrientation::Mixed => {}, 147 TextOrientation::Upright => { 148 flags.insert(WritingMode::UPRIGHT); 149 150 // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-upright: 151 // 152 // > This value causes the used value of direction 153 // > to be ltr, and for the purposes of bidi 154 // > reordering, causes all characters to be treated 155 // > as strong LTR. 156 flags.remove(WritingMode::RTL); 157 flags.remove(WritingMode::INLINE_REVERSED); 158 }, 159 TextOrientation::Sideways => { 160 flags.insert(WritingMode::TEXT_SIDEWAYS); 161 }, 162 } 163 }, 164 _ => {}, 165 } 166 } 167 168 flags 169 } 170 171 #[inline] is_vertical(&self) -> bool172 pub fn is_vertical(&self) -> bool { 173 self.intersects(WritingMode::VERTICAL) 174 } 175 176 #[inline] is_horizontal(&self) -> bool177 pub fn is_horizontal(&self) -> bool { 178 !self.is_vertical() 179 } 180 181 /// Assuming .is_vertical(), does the block direction go left to right? 182 #[inline] is_vertical_lr(&self) -> bool183 pub fn is_vertical_lr(&self) -> bool { 184 self.intersects(WritingMode::VERTICAL_LR) 185 } 186 187 /// Assuming .is_vertical(), does the inline direction go top to bottom? 188 #[inline] is_inline_tb(&self) -> bool189 pub fn is_inline_tb(&self) -> bool { 190 // https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical 191 !self.intersects(WritingMode::INLINE_REVERSED) 192 } 193 194 #[inline] is_bidi_ltr(&self) -> bool195 pub fn is_bidi_ltr(&self) -> bool { 196 !self.intersects(WritingMode::RTL) 197 } 198 199 #[inline] is_sideways(&self) -> bool200 pub fn is_sideways(&self) -> bool { 201 self.intersects(WritingMode::VERTICAL_SIDEWAYS | WritingMode::TEXT_SIDEWAYS) 202 } 203 204 #[inline] is_upright(&self) -> bool205 pub fn is_upright(&self) -> bool { 206 self.intersects(WritingMode::UPRIGHT) 207 } 208 209 /// https://drafts.csswg.org/css-writing-modes/#logical-to-physical 210 /// 211 /// | Return | line-left is… | line-right is… | 212 /// |---------|---------------|----------------| 213 /// | `true` | inline-start | inline-end | 214 /// | `false` | inline-end | inline-start | 215 #[inline] line_left_is_inline_start(&self) -> bool216 pub fn line_left_is_inline_start(&self) -> bool { 217 // https://drafts.csswg.org/css-writing-modes/#inline-start 218 // “For boxes with a used direction value of ltr, this means the line-left side. 219 // For boxes with a used direction value of rtl, this means the line-right side.” 220 self.is_bidi_ltr() 221 } 222 223 #[inline] inline_start_physical_side(&self) -> PhysicalSide224 pub fn inline_start_physical_side(&self) -> PhysicalSide { 225 match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) { 226 (false, _, true) => PhysicalSide::Left, 227 (false, _, false) => PhysicalSide::Right, 228 (true, true, _) => PhysicalSide::Top, 229 (true, false, _) => PhysicalSide::Bottom, 230 } 231 } 232 233 #[inline] inline_end_physical_side(&self) -> PhysicalSide234 pub fn inline_end_physical_side(&self) -> PhysicalSide { 235 match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) { 236 (false, _, true) => PhysicalSide::Right, 237 (false, _, false) => PhysicalSide::Left, 238 (true, true, _) => PhysicalSide::Bottom, 239 (true, false, _) => PhysicalSide::Top, 240 } 241 } 242 243 #[inline] block_start_physical_side(&self) -> PhysicalSide244 pub fn block_start_physical_side(&self) -> PhysicalSide { 245 match (self.is_vertical(), self.is_vertical_lr()) { 246 (false, _) => PhysicalSide::Top, 247 (true, true) => PhysicalSide::Left, 248 (true, false) => PhysicalSide::Right, 249 } 250 } 251 252 #[inline] block_end_physical_side(&self) -> PhysicalSide253 pub fn block_end_physical_side(&self) -> PhysicalSide { 254 match (self.is_vertical(), self.is_vertical_lr()) { 255 (false, _) => PhysicalSide::Bottom, 256 (true, true) => PhysicalSide::Right, 257 (true, false) => PhysicalSide::Left, 258 } 259 } 260 261 #[inline] physical_sides_to_corner( block_side: PhysicalSide, inline_side: PhysicalSide, ) -> PhysicalCorner262 fn physical_sides_to_corner( 263 block_side: PhysicalSide, 264 inline_side: PhysicalSide, 265 ) -> PhysicalCorner { 266 match (block_side, inline_side) { 267 (PhysicalSide::Top, PhysicalSide::Left) | (PhysicalSide::Left, PhysicalSide::Top) => { 268 PhysicalCorner::TopLeft 269 }, 270 (PhysicalSide::Top, PhysicalSide::Right) | (PhysicalSide::Right, PhysicalSide::Top) => { 271 PhysicalCorner::TopRight 272 }, 273 (PhysicalSide::Bottom, PhysicalSide::Right) | 274 (PhysicalSide::Right, PhysicalSide::Bottom) => PhysicalCorner::BottomRight, 275 (PhysicalSide::Bottom, PhysicalSide::Left) | 276 (PhysicalSide::Left, PhysicalSide::Bottom) => PhysicalCorner::BottomLeft, 277 _ => unreachable!("block and inline sides must be orthogonal"), 278 } 279 } 280 281 #[inline] start_start_physical_corner(&self) -> PhysicalCorner282 pub fn start_start_physical_corner(&self) -> PhysicalCorner { 283 WritingMode::physical_sides_to_corner( 284 self.block_start_physical_side(), 285 self.inline_start_physical_side(), 286 ) 287 } 288 289 #[inline] start_end_physical_corner(&self) -> PhysicalCorner290 pub fn start_end_physical_corner(&self) -> PhysicalCorner { 291 WritingMode::physical_sides_to_corner( 292 self.block_start_physical_side(), 293 self.inline_end_physical_side(), 294 ) 295 } 296 297 #[inline] end_start_physical_corner(&self) -> PhysicalCorner298 pub fn end_start_physical_corner(&self) -> PhysicalCorner { 299 WritingMode::physical_sides_to_corner( 300 self.block_end_physical_side(), 301 self.inline_start_physical_side(), 302 ) 303 } 304 305 #[inline] end_end_physical_corner(&self) -> PhysicalCorner306 pub fn end_end_physical_corner(&self) -> PhysicalCorner { 307 WritingMode::physical_sides_to_corner( 308 self.block_end_physical_side(), 309 self.inline_end_physical_side(), 310 ) 311 } 312 313 #[inline] block_flow_direction(&self) -> BlockFlowDirection314 pub fn block_flow_direction(&self) -> BlockFlowDirection { 315 match (self.is_vertical(), self.is_vertical_lr()) { 316 (false, _) => BlockFlowDirection::TopToBottom, 317 (true, true) => BlockFlowDirection::LeftToRight, 318 (true, false) => BlockFlowDirection::RightToLeft, 319 } 320 } 321 322 #[inline] inline_base_direction(&self) -> InlineBaseDirection323 pub fn inline_base_direction(&self) -> InlineBaseDirection { 324 if self.intersects(WritingMode::RTL) { 325 InlineBaseDirection::RightToLeft 326 } else { 327 InlineBaseDirection::LeftToRight 328 } 329 } 330 331 #[inline] 332 /// The default bidirectional embedding level for this writing mode. 333 /// 334 /// Returns bidi level 0 if the mode is LTR, or 1 otherwise. to_bidi_level(&self) -> bidi::Level335 pub fn to_bidi_level(&self) -> bidi::Level { 336 if self.is_bidi_ltr() { 337 bidi::Level::ltr() 338 } else { 339 bidi::Level::rtl() 340 } 341 } 342 } 343 344 impl fmt::Display for WritingMode { fmt(&self, formatter: &mut Formatter) -> Result<(), Error>345 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { 346 if self.is_vertical() { 347 write!(formatter, "V")?; 348 if self.is_vertical_lr() { 349 write!(formatter, " LR")?; 350 } else { 351 write!(formatter, " RL")?; 352 } 353 if self.is_sideways() { 354 write!(formatter, " Sideways")?; 355 } 356 if self.intersects(WritingMode::LINE_INVERTED) { 357 write!(formatter, " Inverted")?; 358 } 359 } else { 360 write!(formatter, "H")?; 361 } 362 if self.is_bidi_ltr() { 363 write!(formatter, " LTR") 364 } else { 365 write!(formatter, " RTL") 366 } 367 } 368 } 369 370 /// Wherever logical geometry is used, the writing mode is known based on context: 371 /// every method takes a `mode` parameter. 372 /// However, this context is easy to get wrong. 373 /// In debug builds only, logical geometry objects store their writing mode 374 /// (in addition to taking it as a parameter to methods) and check it. 375 /// In non-debug builds, make this storage zero-size and the checks no-ops. 376 #[cfg(not(debug_assertions))] 377 #[derive(Clone, Copy, Eq, PartialEq)] 378 #[cfg_attr(feature = "servo", derive(Serialize))] 379 struct DebugWritingMode; 380 381 #[cfg(debug_assertions)] 382 #[derive(Clone, Copy, Eq, PartialEq)] 383 #[cfg_attr(feature = "servo", derive(Serialize))] 384 struct DebugWritingMode { 385 mode: WritingMode, 386 } 387 388 #[cfg(not(debug_assertions))] 389 impl DebugWritingMode { 390 #[inline] check(&self, _other: WritingMode)391 fn check(&self, _other: WritingMode) {} 392 393 #[inline] check_debug(&self, _other: DebugWritingMode)394 fn check_debug(&self, _other: DebugWritingMode) {} 395 396 #[inline] new(_mode: WritingMode) -> DebugWritingMode397 fn new(_mode: WritingMode) -> DebugWritingMode { 398 DebugWritingMode 399 } 400 } 401 402 #[cfg(debug_assertions)] 403 impl DebugWritingMode { 404 #[inline] check(&self, other: WritingMode)405 fn check(&self, other: WritingMode) { 406 assert_eq!(self.mode, other) 407 } 408 409 #[inline] check_debug(&self, other: DebugWritingMode)410 fn check_debug(&self, other: DebugWritingMode) { 411 assert_eq!(self.mode, other.mode) 412 } 413 414 #[inline] new(mode: WritingMode) -> DebugWritingMode415 fn new(mode: WritingMode) -> DebugWritingMode { 416 DebugWritingMode { mode: mode } 417 } 418 } 419 420 impl Debug for DebugWritingMode { 421 #[cfg(not(debug_assertions))] fmt(&self, formatter: &mut Formatter) -> Result<(), Error>422 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { 423 write!(formatter, "?") 424 } 425 426 #[cfg(debug_assertions)] fmt(&self, formatter: &mut Formatter) -> Result<(), Error>427 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { 428 write!(formatter, "{}", self.mode) 429 } 430 } 431 432 // Used to specify the logical direction. 433 #[derive(Clone, Copy, Debug, PartialEq)] 434 #[cfg_attr(feature = "servo", derive(Serialize))] 435 pub enum Direction { 436 Inline, 437 Block, 438 } 439 440 /// A 2D size in flow-relative dimensions 441 #[derive(Clone, Copy, Eq, PartialEq)] 442 #[cfg_attr(feature = "servo", derive(Serialize))] 443 pub struct LogicalSize<T> { 444 pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure 445 pub block: T, // block-size, a.k.a. logical height, a.k.a. extent 446 debug_writing_mode: DebugWritingMode, 447 } 448 449 impl<T: Debug> Debug for LogicalSize<T> { fmt(&self, formatter: &mut Formatter) -> Result<(), Error>450 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { 451 write!( 452 formatter, 453 "LogicalSize({:?}, i{:?}×b{:?})", 454 self.debug_writing_mode, self.inline, self.block 455 ) 456 } 457 } 458 459 // Can not implement the Zero trait: its zero() method does not have the `mode` parameter. 460 impl<T: Zero> LogicalSize<T> { 461 #[inline] zero(mode: WritingMode) -> LogicalSize<T>462 pub fn zero(mode: WritingMode) -> LogicalSize<T> { 463 LogicalSize { 464 inline: Zero::zero(), 465 block: Zero::zero(), 466 debug_writing_mode: DebugWritingMode::new(mode), 467 } 468 } 469 } 470 471 impl<T> LogicalSize<T> { 472 #[inline] new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T>473 pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T> { 474 LogicalSize { 475 inline: inline, 476 block: block, 477 debug_writing_mode: DebugWritingMode::new(mode), 478 } 479 } 480 481 #[inline] from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T>482 pub fn from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T> { 483 if mode.is_vertical() { 484 LogicalSize::new(mode, size.height, size.width) 485 } else { 486 LogicalSize::new(mode, size.width, size.height) 487 } 488 } 489 } 490 491 impl<T: Copy> LogicalSize<T> { 492 #[inline] width(&self, mode: WritingMode) -> T493 pub fn width(&self, mode: WritingMode) -> T { 494 self.debug_writing_mode.check(mode); 495 if mode.is_vertical() { 496 self.block 497 } else { 498 self.inline 499 } 500 } 501 502 #[inline] set_width(&mut self, mode: WritingMode, width: T)503 pub fn set_width(&mut self, mode: WritingMode, width: T) { 504 self.debug_writing_mode.check(mode); 505 if mode.is_vertical() { 506 self.block = width 507 } else { 508 self.inline = width 509 } 510 } 511 512 #[inline] height(&self, mode: WritingMode) -> T513 pub fn height(&self, mode: WritingMode) -> T { 514 self.debug_writing_mode.check(mode); 515 if mode.is_vertical() { 516 self.inline 517 } else { 518 self.block 519 } 520 } 521 522 #[inline] set_height(&mut self, mode: WritingMode, height: T)523 pub fn set_height(&mut self, mode: WritingMode, height: T) { 524 self.debug_writing_mode.check(mode); 525 if mode.is_vertical() { 526 self.inline = height 527 } else { 528 self.block = height 529 } 530 } 531 532 #[inline] to_physical(&self, mode: WritingMode) -> Size2D<T>533 pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> { 534 self.debug_writing_mode.check(mode); 535 if mode.is_vertical() { 536 Size2D::new(self.block, self.inline) 537 } else { 538 Size2D::new(self.inline, self.block) 539 } 540 } 541 542 #[inline] convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T>543 pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T> { 544 if mode_from == mode_to { 545 self.debug_writing_mode.check(mode_from); 546 *self 547 } else { 548 LogicalSize::from_physical(mode_to, self.to_physical(mode_from)) 549 } 550 } 551 } 552 553 impl<T: Add<T, Output = T>> Add for LogicalSize<T> { 554 type Output = LogicalSize<T>; 555 556 #[inline] add(self, other: LogicalSize<T>) -> LogicalSize<T>557 fn add(self, other: LogicalSize<T>) -> LogicalSize<T> { 558 self.debug_writing_mode 559 .check_debug(other.debug_writing_mode); 560 LogicalSize { 561 debug_writing_mode: self.debug_writing_mode, 562 inline: self.inline + other.inline, 563 block: self.block + other.block, 564 } 565 } 566 } 567 568 impl<T: Sub<T, Output = T>> Sub for LogicalSize<T> { 569 type Output = LogicalSize<T>; 570 571 #[inline] sub(self, other: LogicalSize<T>) -> LogicalSize<T>572 fn sub(self, other: LogicalSize<T>) -> LogicalSize<T> { 573 self.debug_writing_mode 574 .check_debug(other.debug_writing_mode); 575 LogicalSize { 576 debug_writing_mode: self.debug_writing_mode, 577 inline: self.inline - other.inline, 578 block: self.block - other.block, 579 } 580 } 581 } 582 583 /// A 2D point in flow-relative dimensions 584 #[derive(Clone, Copy, Eq, PartialEq)] 585 #[cfg_attr(feature = "servo", derive(Serialize))] 586 pub struct LogicalPoint<T> { 587 /// inline-axis coordinate 588 pub i: T, 589 /// block-axis coordinate 590 pub b: T, 591 debug_writing_mode: DebugWritingMode, 592 } 593 594 impl<T: Debug> Debug for LogicalPoint<T> { fmt(&self, formatter: &mut Formatter) -> Result<(), Error>595 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { 596 write!( 597 formatter, 598 "LogicalPoint({:?} (i{:?}, b{:?}))", 599 self.debug_writing_mode, self.i, self.b 600 ) 601 } 602 } 603 604 // Can not implement the Zero trait: its zero() method does not have the `mode` parameter. 605 impl<T: Zero> LogicalPoint<T> { 606 #[inline] zero(mode: WritingMode) -> LogicalPoint<T>607 pub fn zero(mode: WritingMode) -> LogicalPoint<T> { 608 LogicalPoint { 609 i: Zero::zero(), 610 b: Zero::zero(), 611 debug_writing_mode: DebugWritingMode::new(mode), 612 } 613 } 614 } 615 616 impl<T: Copy> LogicalPoint<T> { 617 #[inline] new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T>618 pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T> { 619 LogicalPoint { 620 i: i, 621 b: b, 622 debug_writing_mode: DebugWritingMode::new(mode), 623 } 624 } 625 } 626 627 impl<T: Copy + Sub<T, Output = T>> LogicalPoint<T> { 628 #[inline] from_physical( mode: WritingMode, point: Point2D<T>, container_size: Size2D<T>, ) -> LogicalPoint<T>629 pub fn from_physical( 630 mode: WritingMode, 631 point: Point2D<T>, 632 container_size: Size2D<T>, 633 ) -> LogicalPoint<T> { 634 if mode.is_vertical() { 635 LogicalPoint { 636 i: if mode.is_inline_tb() { 637 point.y 638 } else { 639 container_size.height - point.y 640 }, 641 b: if mode.is_vertical_lr() { 642 point.x 643 } else { 644 container_size.width - point.x 645 }, 646 debug_writing_mode: DebugWritingMode::new(mode), 647 } 648 } else { 649 LogicalPoint { 650 i: if mode.is_bidi_ltr() { 651 point.x 652 } else { 653 container_size.width - point.x 654 }, 655 b: point.y, 656 debug_writing_mode: DebugWritingMode::new(mode), 657 } 658 } 659 } 660 661 #[inline] x(&self, mode: WritingMode, container_size: Size2D<T>) -> T662 pub fn x(&self, mode: WritingMode, container_size: Size2D<T>) -> T { 663 self.debug_writing_mode.check(mode); 664 if mode.is_vertical() { 665 if mode.is_vertical_lr() { 666 self.b 667 } else { 668 container_size.width - self.b 669 } 670 } else { 671 if mode.is_bidi_ltr() { 672 self.i 673 } else { 674 container_size.width - self.i 675 } 676 } 677 } 678 679 #[inline] set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>)680 pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>) { 681 self.debug_writing_mode.check(mode); 682 if mode.is_vertical() { 683 self.b = if mode.is_vertical_lr() { 684 x 685 } else { 686 container_size.width - x 687 } 688 } else { 689 self.i = if mode.is_bidi_ltr() { 690 x 691 } else { 692 container_size.width - x 693 } 694 } 695 } 696 697 #[inline] y(&self, mode: WritingMode, container_size: Size2D<T>) -> T698 pub fn y(&self, mode: WritingMode, container_size: Size2D<T>) -> T { 699 self.debug_writing_mode.check(mode); 700 if mode.is_vertical() { 701 if mode.is_inline_tb() { 702 self.i 703 } else { 704 container_size.height - self.i 705 } 706 } else { 707 self.b 708 } 709 } 710 711 #[inline] set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>)712 pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>) { 713 self.debug_writing_mode.check(mode); 714 if mode.is_vertical() { 715 self.i = if mode.is_inline_tb() { 716 y 717 } else { 718 container_size.height - y 719 } 720 } else { 721 self.b = y 722 } 723 } 724 725 #[inline] to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T>726 pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> { 727 self.debug_writing_mode.check(mode); 728 if mode.is_vertical() { 729 Point2D::new( 730 if mode.is_vertical_lr() { 731 self.b 732 } else { 733 container_size.width - self.b 734 }, 735 if mode.is_inline_tb() { 736 self.i 737 } else { 738 container_size.height - self.i 739 }, 740 ) 741 } else { 742 Point2D::new( 743 if mode.is_bidi_ltr() { 744 self.i 745 } else { 746 container_size.width - self.i 747 }, 748 self.b, 749 ) 750 } 751 } 752 753 #[inline] convert( &self, mode_from: WritingMode, mode_to: WritingMode, container_size: Size2D<T>, ) -> LogicalPoint<T>754 pub fn convert( 755 &self, 756 mode_from: WritingMode, 757 mode_to: WritingMode, 758 container_size: Size2D<T>, 759 ) -> LogicalPoint<T> { 760 if mode_from == mode_to { 761 self.debug_writing_mode.check(mode_from); 762 *self 763 } else { 764 LogicalPoint::from_physical( 765 mode_to, 766 self.to_physical(mode_from, container_size), 767 container_size, 768 ) 769 } 770 } 771 } 772 773 impl<T: Copy + Add<T, Output = T>> LogicalPoint<T> { 774 /// This doesn’t really makes sense, 775 /// but happens when dealing with multiple origins. 776 #[inline] add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T>777 pub fn add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T> { 778 self.debug_writing_mode 779 .check_debug(other.debug_writing_mode); 780 LogicalPoint { 781 debug_writing_mode: self.debug_writing_mode, 782 i: self.i + other.i, 783 b: self.b + other.b, 784 } 785 } 786 } 787 788 impl<T: Copy + Add<T, Output = T>> Add<LogicalSize<T>> for LogicalPoint<T> { 789 type Output = LogicalPoint<T>; 790 791 #[inline] add(self, other: LogicalSize<T>) -> LogicalPoint<T>792 fn add(self, other: LogicalSize<T>) -> LogicalPoint<T> { 793 self.debug_writing_mode 794 .check_debug(other.debug_writing_mode); 795 LogicalPoint { 796 debug_writing_mode: self.debug_writing_mode, 797 i: self.i + other.inline, 798 b: self.b + other.block, 799 } 800 } 801 } 802 803 impl<T: Copy + Sub<T, Output = T>> Sub<LogicalSize<T>> for LogicalPoint<T> { 804 type Output = LogicalPoint<T>; 805 806 #[inline] sub(self, other: LogicalSize<T>) -> LogicalPoint<T>807 fn sub(self, other: LogicalSize<T>) -> LogicalPoint<T> { 808 self.debug_writing_mode 809 .check_debug(other.debug_writing_mode); 810 LogicalPoint { 811 debug_writing_mode: self.debug_writing_mode, 812 i: self.i - other.inline, 813 b: self.b - other.block, 814 } 815 } 816 } 817 818 /// A "margin" in flow-relative dimensions 819 /// Represents the four sides of the margins, borders, or padding of a CSS box, 820 /// or a combination of those. 821 /// A positive "margin" can be added to a rectangle to obtain a bigger rectangle. 822 #[derive(Clone, Copy, Eq, PartialEq)] 823 #[cfg_attr(feature = "servo", derive(Serialize))] 824 pub struct LogicalMargin<T> { 825 pub block_start: T, 826 pub inline_end: T, 827 pub block_end: T, 828 pub inline_start: T, 829 debug_writing_mode: DebugWritingMode, 830 } 831 832 impl<T: Debug> Debug for LogicalMargin<T> { fmt(&self, formatter: &mut Formatter) -> Result<(), Error>833 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { 834 let writing_mode_string = if cfg!(debug_assertions) { 835 format!("{:?}, ", self.debug_writing_mode) 836 } else { 837 "".to_owned() 838 }; 839 840 write!( 841 formatter, 842 "LogicalMargin({}i:{:?}..{:?} b:{:?}..{:?})", 843 writing_mode_string, 844 self.inline_start, 845 self.inline_end, 846 self.block_start, 847 self.block_end 848 ) 849 } 850 } 851 852 impl<T: Zero> LogicalMargin<T> { 853 #[inline] zero(mode: WritingMode) -> LogicalMargin<T>854 pub fn zero(mode: WritingMode) -> LogicalMargin<T> { 855 LogicalMargin { 856 block_start: Zero::zero(), 857 inline_end: Zero::zero(), 858 block_end: Zero::zero(), 859 inline_start: Zero::zero(), 860 debug_writing_mode: DebugWritingMode::new(mode), 861 } 862 } 863 } 864 865 impl<T> LogicalMargin<T> { 866 #[inline] new( mode: WritingMode, block_start: T, inline_end: T, block_end: T, inline_start: T, ) -> LogicalMargin<T>867 pub fn new( 868 mode: WritingMode, 869 block_start: T, 870 inline_end: T, 871 block_end: T, 872 inline_start: T, 873 ) -> LogicalMargin<T> { 874 LogicalMargin { 875 block_start: block_start, 876 inline_end: inline_end, 877 block_end: block_end, 878 inline_start: inline_start, 879 debug_writing_mode: DebugWritingMode::new(mode), 880 } 881 } 882 883 #[inline] from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T>884 pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> { 885 let block_start; 886 let inline_end; 887 let block_end; 888 let inline_start; 889 if mode.is_vertical() { 890 if mode.is_vertical_lr() { 891 block_start = offsets.left; 892 block_end = offsets.right; 893 } else { 894 block_start = offsets.right; 895 block_end = offsets.left; 896 } 897 if mode.is_inline_tb() { 898 inline_start = offsets.top; 899 inline_end = offsets.bottom; 900 } else { 901 inline_start = offsets.bottom; 902 inline_end = offsets.top; 903 } 904 } else { 905 block_start = offsets.top; 906 block_end = offsets.bottom; 907 if mode.is_bidi_ltr() { 908 inline_start = offsets.left; 909 inline_end = offsets.right; 910 } else { 911 inline_start = offsets.right; 912 inline_end = offsets.left; 913 } 914 } 915 LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start) 916 } 917 } 918 919 impl<T: Copy> LogicalMargin<T> { 920 #[inline] new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T>921 pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T> { 922 LogicalMargin::new(mode, value, value, value, value) 923 } 924 925 #[inline] top(&self, mode: WritingMode) -> T926 pub fn top(&self, mode: WritingMode) -> T { 927 self.debug_writing_mode.check(mode); 928 if mode.is_vertical() { 929 if mode.is_inline_tb() { 930 self.inline_start 931 } else { 932 self.inline_end 933 } 934 } else { 935 self.block_start 936 } 937 } 938 939 #[inline] set_top(&mut self, mode: WritingMode, top: T)940 pub fn set_top(&mut self, mode: WritingMode, top: T) { 941 self.debug_writing_mode.check(mode); 942 if mode.is_vertical() { 943 if mode.is_inline_tb() { 944 self.inline_start = top 945 } else { 946 self.inline_end = top 947 } 948 } else { 949 self.block_start = top 950 } 951 } 952 953 #[inline] right(&self, mode: WritingMode) -> T954 pub fn right(&self, mode: WritingMode) -> T { 955 self.debug_writing_mode.check(mode); 956 if mode.is_vertical() { 957 if mode.is_vertical_lr() { 958 self.block_end 959 } else { 960 self.block_start 961 } 962 } else { 963 if mode.is_bidi_ltr() { 964 self.inline_end 965 } else { 966 self.inline_start 967 } 968 } 969 } 970 971 #[inline] set_right(&mut self, mode: WritingMode, right: T)972 pub fn set_right(&mut self, mode: WritingMode, right: T) { 973 self.debug_writing_mode.check(mode); 974 if mode.is_vertical() { 975 if mode.is_vertical_lr() { 976 self.block_end = right 977 } else { 978 self.block_start = right 979 } 980 } else { 981 if mode.is_bidi_ltr() { 982 self.inline_end = right 983 } else { 984 self.inline_start = right 985 } 986 } 987 } 988 989 #[inline] bottom(&self, mode: WritingMode) -> T990 pub fn bottom(&self, mode: WritingMode) -> T { 991 self.debug_writing_mode.check(mode); 992 if mode.is_vertical() { 993 if mode.is_inline_tb() { 994 self.inline_end 995 } else { 996 self.inline_start 997 } 998 } else { 999 self.block_end 1000 } 1001 } 1002 1003 #[inline] set_bottom(&mut self, mode: WritingMode, bottom: T)1004 pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) { 1005 self.debug_writing_mode.check(mode); 1006 if mode.is_vertical() { 1007 if mode.is_inline_tb() { 1008 self.inline_end = bottom 1009 } else { 1010 self.inline_start = bottom 1011 } 1012 } else { 1013 self.block_end = bottom 1014 } 1015 } 1016 1017 #[inline] left(&self, mode: WritingMode) -> T1018 pub fn left(&self, mode: WritingMode) -> T { 1019 self.debug_writing_mode.check(mode); 1020 if mode.is_vertical() { 1021 if mode.is_vertical_lr() { 1022 self.block_start 1023 } else { 1024 self.block_end 1025 } 1026 } else { 1027 if mode.is_bidi_ltr() { 1028 self.inline_start 1029 } else { 1030 self.inline_end 1031 } 1032 } 1033 } 1034 1035 #[inline] set_left(&mut self, mode: WritingMode, left: T)1036 pub fn set_left(&mut self, mode: WritingMode, left: T) { 1037 self.debug_writing_mode.check(mode); 1038 if mode.is_vertical() { 1039 if mode.is_vertical_lr() { 1040 self.block_start = left 1041 } else { 1042 self.block_end = left 1043 } 1044 } else { 1045 if mode.is_bidi_ltr() { 1046 self.inline_start = left 1047 } else { 1048 self.inline_end = left 1049 } 1050 } 1051 } 1052 1053 #[inline] to_physical(&self, mode: WritingMode) -> SideOffsets2D<T>1054 pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> { 1055 self.debug_writing_mode.check(mode); 1056 let top; 1057 let right; 1058 let bottom; 1059 let left; 1060 if mode.is_vertical() { 1061 if mode.is_vertical_lr() { 1062 left = self.block_start; 1063 right = self.block_end; 1064 } else { 1065 right = self.block_start; 1066 left = self.block_end; 1067 } 1068 if mode.is_inline_tb() { 1069 top = self.inline_start; 1070 bottom = self.inline_end; 1071 } else { 1072 bottom = self.inline_start; 1073 top = self.inline_end; 1074 } 1075 } else { 1076 top = self.block_start; 1077 bottom = self.block_end; 1078 if mode.is_bidi_ltr() { 1079 left = self.inline_start; 1080 right = self.inline_end; 1081 } else { 1082 right = self.inline_start; 1083 left = self.inline_end; 1084 } 1085 } 1086 SideOffsets2D::new(top, right, bottom, left) 1087 } 1088 1089 #[inline] convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T>1090 pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> { 1091 if mode_from == mode_to { 1092 self.debug_writing_mode.check(mode_from); 1093 *self 1094 } else { 1095 LogicalMargin::from_physical(mode_to, self.to_physical(mode_from)) 1096 } 1097 } 1098 } 1099 1100 impl<T: PartialEq + Zero> LogicalMargin<T> { 1101 #[inline] is_zero(&self) -> bool1102 pub fn is_zero(&self) -> bool { 1103 self.block_start == Zero::zero() && 1104 self.inline_end == Zero::zero() && 1105 self.block_end == Zero::zero() && 1106 self.inline_start == Zero::zero() 1107 } 1108 } 1109 1110 impl<T: Copy + Add<T, Output = T>> LogicalMargin<T> { 1111 #[inline] inline_start_end(&self) -> T1112 pub fn inline_start_end(&self) -> T { 1113 self.inline_start + self.inline_end 1114 } 1115 1116 #[inline] block_start_end(&self) -> T1117 pub fn block_start_end(&self) -> T { 1118 self.block_start + self.block_end 1119 } 1120 1121 #[inline] start_end(&self, direction: Direction) -> T1122 pub fn start_end(&self, direction: Direction) -> T { 1123 match direction { 1124 Direction::Inline => self.inline_start + self.inline_end, 1125 Direction::Block => self.block_start + self.block_end, 1126 } 1127 } 1128 1129 #[inline] top_bottom(&self, mode: WritingMode) -> T1130 pub fn top_bottom(&self, mode: WritingMode) -> T { 1131 self.debug_writing_mode.check(mode); 1132 if mode.is_vertical() { 1133 self.inline_start_end() 1134 } else { 1135 self.block_start_end() 1136 } 1137 } 1138 1139 #[inline] left_right(&self, mode: WritingMode) -> T1140 pub fn left_right(&self, mode: WritingMode) -> T { 1141 self.debug_writing_mode.check(mode); 1142 if mode.is_vertical() { 1143 self.block_start_end() 1144 } else { 1145 self.inline_start_end() 1146 } 1147 } 1148 } 1149 1150 impl<T: Add<T, Output = T>> Add for LogicalMargin<T> { 1151 type Output = LogicalMargin<T>; 1152 1153 #[inline] add(self, other: LogicalMargin<T>) -> LogicalMargin<T>1154 fn add(self, other: LogicalMargin<T>) -> LogicalMargin<T> { 1155 self.debug_writing_mode 1156 .check_debug(other.debug_writing_mode); 1157 LogicalMargin { 1158 debug_writing_mode: self.debug_writing_mode, 1159 block_start: self.block_start + other.block_start, 1160 inline_end: self.inline_end + other.inline_end, 1161 block_end: self.block_end + other.block_end, 1162 inline_start: self.inline_start + other.inline_start, 1163 } 1164 } 1165 } 1166 1167 impl<T: Sub<T, Output = T>> Sub for LogicalMargin<T> { 1168 type Output = LogicalMargin<T>; 1169 1170 #[inline] sub(self, other: LogicalMargin<T>) -> LogicalMargin<T>1171 fn sub(self, other: LogicalMargin<T>) -> LogicalMargin<T> { 1172 self.debug_writing_mode 1173 .check_debug(other.debug_writing_mode); 1174 LogicalMargin { 1175 debug_writing_mode: self.debug_writing_mode, 1176 block_start: self.block_start - other.block_start, 1177 inline_end: self.inline_end - other.inline_end, 1178 block_end: self.block_end - other.block_end, 1179 inline_start: self.inline_start - other.inline_start, 1180 } 1181 } 1182 } 1183 1184 /// A rectangle in flow-relative dimensions 1185 #[derive(Clone, Copy, Eq, PartialEq)] 1186 #[cfg_attr(feature = "servo", derive(Serialize))] 1187 pub struct LogicalRect<T> { 1188 pub start: LogicalPoint<T>, 1189 pub size: LogicalSize<T>, 1190 debug_writing_mode: DebugWritingMode, 1191 } 1192 1193 impl<T: Debug> Debug for LogicalRect<T> { fmt(&self, formatter: &mut Formatter) -> Result<(), Error>1194 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { 1195 let writing_mode_string = if cfg!(debug_assertions) { 1196 format!("{:?}, ", self.debug_writing_mode) 1197 } else { 1198 "".to_owned() 1199 }; 1200 1201 write!( 1202 formatter, 1203 "LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))", 1204 writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b 1205 ) 1206 } 1207 } 1208 1209 impl<T: Zero> LogicalRect<T> { 1210 #[inline] zero(mode: WritingMode) -> LogicalRect<T>1211 pub fn zero(mode: WritingMode) -> LogicalRect<T> { 1212 LogicalRect { 1213 start: LogicalPoint::zero(mode), 1214 size: LogicalSize::zero(mode), 1215 debug_writing_mode: DebugWritingMode::new(mode), 1216 } 1217 } 1218 } 1219 1220 impl<T: Copy> LogicalRect<T> { 1221 #[inline] new( mode: WritingMode, inline_start: T, block_start: T, inline: T, block: T, ) -> LogicalRect<T>1222 pub fn new( 1223 mode: WritingMode, 1224 inline_start: T, 1225 block_start: T, 1226 inline: T, 1227 block: T, 1228 ) -> LogicalRect<T> { 1229 LogicalRect { 1230 start: LogicalPoint::new(mode, inline_start, block_start), 1231 size: LogicalSize::new(mode, inline, block), 1232 debug_writing_mode: DebugWritingMode::new(mode), 1233 } 1234 } 1235 1236 #[inline] from_point_size( mode: WritingMode, start: LogicalPoint<T>, size: LogicalSize<T>, ) -> LogicalRect<T>1237 pub fn from_point_size( 1238 mode: WritingMode, 1239 start: LogicalPoint<T>, 1240 size: LogicalSize<T>, 1241 ) -> LogicalRect<T> { 1242 start.debug_writing_mode.check(mode); 1243 size.debug_writing_mode.check(mode); 1244 LogicalRect { 1245 start: start, 1246 size: size, 1247 debug_writing_mode: DebugWritingMode::new(mode), 1248 } 1249 } 1250 } 1251 1252 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> { 1253 #[inline] from_physical( mode: WritingMode, rect: Rect<T>, container_size: Size2D<T>, ) -> LogicalRect<T>1254 pub fn from_physical( 1255 mode: WritingMode, 1256 rect: Rect<T>, 1257 container_size: Size2D<T>, 1258 ) -> LogicalRect<T> { 1259 let inline_start; 1260 let block_start; 1261 let inline; 1262 let block; 1263 if mode.is_vertical() { 1264 inline = rect.size.height; 1265 block = rect.size.width; 1266 if mode.is_vertical_lr() { 1267 block_start = rect.origin.x; 1268 } else { 1269 block_start = container_size.width - (rect.origin.x + rect.size.width); 1270 } 1271 if mode.is_inline_tb() { 1272 inline_start = rect.origin.y; 1273 } else { 1274 inline_start = container_size.height - (rect.origin.y + rect.size.height); 1275 } 1276 } else { 1277 inline = rect.size.width; 1278 block = rect.size.height; 1279 block_start = rect.origin.y; 1280 if mode.is_bidi_ltr() { 1281 inline_start = rect.origin.x; 1282 } else { 1283 inline_start = container_size.width - (rect.origin.x + rect.size.width); 1284 } 1285 } 1286 LogicalRect { 1287 start: LogicalPoint::new(mode, inline_start, block_start), 1288 size: LogicalSize::new(mode, inline, block), 1289 debug_writing_mode: DebugWritingMode::new(mode), 1290 } 1291 } 1292 1293 #[inline] inline_end(&self) -> T1294 pub fn inline_end(&self) -> T { 1295 self.start.i + self.size.inline 1296 } 1297 1298 #[inline] block_end(&self) -> T1299 pub fn block_end(&self) -> T { 1300 self.start.b + self.size.block 1301 } 1302 1303 #[inline] to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T>1304 pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> { 1305 self.debug_writing_mode.check(mode); 1306 let x; 1307 let y; 1308 let width; 1309 let height; 1310 if mode.is_vertical() { 1311 width = self.size.block; 1312 height = self.size.inline; 1313 if mode.is_vertical_lr() { 1314 x = self.start.b; 1315 } else { 1316 x = container_size.width - self.block_end(); 1317 } 1318 if mode.is_inline_tb() { 1319 y = self.start.i; 1320 } else { 1321 y = container_size.height - self.inline_end(); 1322 } 1323 } else { 1324 width = self.size.inline; 1325 height = self.size.block; 1326 y = self.start.b; 1327 if mode.is_bidi_ltr() { 1328 x = self.start.i; 1329 } else { 1330 x = container_size.width - self.inline_end(); 1331 } 1332 } 1333 Rect { 1334 origin: Point2D::new(x, y), 1335 size: Size2D::new(width, height), 1336 } 1337 } 1338 1339 #[inline] convert( &self, mode_from: WritingMode, mode_to: WritingMode, container_size: Size2D<T>, ) -> LogicalRect<T>1340 pub fn convert( 1341 &self, 1342 mode_from: WritingMode, 1343 mode_to: WritingMode, 1344 container_size: Size2D<T>, 1345 ) -> LogicalRect<T> { 1346 if mode_from == mode_to { 1347 self.debug_writing_mode.check(mode_from); 1348 *self 1349 } else { 1350 LogicalRect::from_physical( 1351 mode_to, 1352 self.to_physical(mode_from, container_size), 1353 container_size, 1354 ) 1355 } 1356 } 1357 translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T>1358 pub fn translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T> { 1359 LogicalRect { 1360 start: self.start + offset, 1361 ..*self 1362 } 1363 } 1364 translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T>1365 pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> { 1366 LogicalRect { 1367 start: self.start + 1368 LogicalSize { 1369 inline: offset.i, 1370 block: offset.b, 1371 debug_writing_mode: offset.debug_writing_mode, 1372 }, 1373 size: self.size, 1374 debug_writing_mode: self.debug_writing_mode, 1375 } 1376 } 1377 } 1378 1379 impl<T: Copy + Ord + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> { 1380 #[inline] union(&self, other: &LogicalRect<T>) -> LogicalRect<T>1381 pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> { 1382 self.debug_writing_mode 1383 .check_debug(other.debug_writing_mode); 1384 1385 let inline_start = min(self.start.i, other.start.i); 1386 let block_start = min(self.start.b, other.start.b); 1387 LogicalRect { 1388 start: LogicalPoint { 1389 i: inline_start, 1390 b: block_start, 1391 debug_writing_mode: self.debug_writing_mode, 1392 }, 1393 size: LogicalSize { 1394 inline: max(self.inline_end(), other.inline_end()) - inline_start, 1395 block: max(self.block_end(), other.block_end()) - block_start, 1396 debug_writing_mode: self.debug_writing_mode, 1397 }, 1398 debug_writing_mode: self.debug_writing_mode, 1399 } 1400 } 1401 } 1402 1403 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Add<LogicalMargin<T>> for LogicalRect<T> { 1404 type Output = LogicalRect<T>; 1405 1406 #[inline] add(self, other: LogicalMargin<T>) -> LogicalRect<T>1407 fn add(self, other: LogicalMargin<T>) -> LogicalRect<T> { 1408 self.debug_writing_mode 1409 .check_debug(other.debug_writing_mode); 1410 LogicalRect { 1411 start: LogicalPoint { 1412 // Growing a rectangle on the start side means pushing its 1413 // start point on the negative direction. 1414 i: self.start.i - other.inline_start, 1415 b: self.start.b - other.block_start, 1416 debug_writing_mode: self.debug_writing_mode, 1417 }, 1418 size: LogicalSize { 1419 inline: self.size.inline + other.inline_start_end(), 1420 block: self.size.block + other.block_start_end(), 1421 debug_writing_mode: self.debug_writing_mode, 1422 }, 1423 debug_writing_mode: self.debug_writing_mode, 1424 } 1425 } 1426 } 1427 1428 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Sub<LogicalMargin<T>> for LogicalRect<T> { 1429 type Output = LogicalRect<T>; 1430 1431 #[inline] sub(self, other: LogicalMargin<T>) -> LogicalRect<T>1432 fn sub(self, other: LogicalMargin<T>) -> LogicalRect<T> { 1433 self.debug_writing_mode 1434 .check_debug(other.debug_writing_mode); 1435 LogicalRect { 1436 start: LogicalPoint { 1437 // Shrinking a rectangle on the start side means pushing its 1438 // start point on the positive direction. 1439 i: self.start.i + other.inline_start, 1440 b: self.start.b + other.block_start, 1441 debug_writing_mode: self.debug_writing_mode, 1442 }, 1443 size: LogicalSize { 1444 inline: self.size.inline - other.inline_start_end(), 1445 block: self.size.block - other.block_start_end(), 1446 debug_writing_mode: self.debug_writing_mode, 1447 }, 1448 debug_writing_mode: self.debug_writing_mode, 1449 } 1450 } 1451 } 1452 1453 #[derive(Clone, Copy, Debug, PartialEq)] 1454 pub enum PhysicalSide { 1455 Top, 1456 Right, 1457 Bottom, 1458 Left, 1459 } 1460 1461 #[derive(Clone, Copy, Debug, PartialEq)] 1462 pub enum PhysicalCorner { 1463 TopLeft, 1464 TopRight, 1465 BottomRight, 1466 BottomLeft, 1467 } 1468