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/. */
5 //! Geometry in flow-relative space.
7 use crate::properties::style_structs;
8 use euclid::default::{Point2D, Rect, SideOffsets2D, Size2D};
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;
15 pub enum BlockFlowDirection {
21 pub enum InlineBaseDirection {
26 // TODO: improve the readability of the WritingMode serialization, refer to the Debug:fmt()
27 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Serialize)]
29 pub struct WritingMode(u8);
31 impl WritingMode: u8 {
32 /// A vertical writing mode; writing-mode is vertical-rl,
33 /// vertical-lr, sideways-lr, or sideways-rl.
34 const VERTICAL = 1 << 0;
35 /// The inline flow direction is reversed against the physical
36 /// direction (i.e. right-to-left or bottom-to-top); writing-mode is
37 /// sideways-lr or direction is rtl (but not both).
39 /// (This bit can be derived from the others, but we store it for
41 const INLINE_REVERSED = 1 << 1;
42 /// A vertical writing mode whose block progression direction is left-
43 /// to-right; writing-mode is vertical-lr or sideways-lr.
45 /// Never set without VERTICAL.
46 const VERTICAL_LR = 1 << 2;
47 /// The line-over/line-under sides are inverted with respect to the
48 /// block-start/block-end edge; writing-mode is vertical-lr.
50 /// Never set without VERTICAL and VERTICAL_LR.
51 const LINE_INVERTED = 1 << 3;
54 /// All text within a vertical writing mode is displayed sideways
55 /// and runs top-to-bottom or bottom-to-top; set in these cases:
57 /// * writing-mode: sideways-rl;
58 /// * writing-mode: sideways-lr;
60 /// Never set without VERTICAL.
61 const VERTICAL_SIDEWAYS = 1 << 5;
62 /// Similar to VERTICAL_SIDEWAYS, but is set via text-orientation;
63 /// set in these cases:
65 /// * writing-mode: vertical-rl; text-orientation: sideways;
66 /// * writing-mode: vertical-lr; text-orientation: sideways;
68 /// Never set without VERTICAL.
69 const TEXT_SIDEWAYS = 1 << 6;
70 /// Horizontal text within a vertical writing mode is displayed with each
71 /// glyph upright; set in these cases:
73 /// * writing-mode: vertical-rl; text-orientation: upright;
74 /// * writing-mode: vertical-lr: text-orientation: upright;
76 /// Never set without VERTICAL.
77 const UPRIGHT = 1 << 7;
82 /// Return a WritingMode bitflags from the relevant CSS properties.
83 pub fn new(inheritedbox_style: &style_structs::InheritedBox) -> Self {
84 use crate::properties::longhands::direction::computed_value::T as Direction;
85 use crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode;
87 let mut flags = WritingMode::empty();
89 let direction = inheritedbox_style.clone_direction();
90 let writing_mode = inheritedbox_style.clone_writing_mode();
95 flags.insert(WritingMode::RTL);
100 SpecifiedWritingMode::HorizontalTb => {
101 if direction == Direction::Rtl {
102 flags.insert(WritingMode::INLINE_REVERSED);
105 SpecifiedWritingMode::VerticalRl => {
106 flags.insert(WritingMode::VERTICAL);
107 if direction == Direction::Rtl {
108 flags.insert(WritingMode::INLINE_REVERSED);
111 SpecifiedWritingMode::VerticalLr => {
112 flags.insert(WritingMode::VERTICAL);
113 flags.insert(WritingMode::VERTICAL_LR);
114 flags.insert(WritingMode::LINE_INVERTED);
115 if direction == Direction::Rtl {
116 flags.insert(WritingMode::INLINE_REVERSED);
119 #[cfg(feature = "gecko")]
120 SpecifiedWritingMode::SidewaysRl => {
121 flags.insert(WritingMode::VERTICAL);
122 flags.insert(WritingMode::VERTICAL_SIDEWAYS);
123 if direction == Direction::Rtl {
124 flags.insert(WritingMode::INLINE_REVERSED);
127 #[cfg(feature = "gecko")]
128 SpecifiedWritingMode::SidewaysLr => {
129 flags.insert(WritingMode::VERTICAL);
130 flags.insert(WritingMode::VERTICAL_LR);
131 flags.insert(WritingMode::VERTICAL_SIDEWAYS);
132 if direction == Direction::Ltr {
133 flags.insert(WritingMode::INLINE_REVERSED);
138 #[cfg(feature = "gecko")]
140 use crate::properties::longhands::text_orientation::computed_value::T as TextOrientation;
142 // text-orientation only has an effect for vertical-rl and
143 // vertical-lr values of writing-mode.
145 SpecifiedWritingMode::VerticalRl | SpecifiedWritingMode::VerticalLr => {
146 match inheritedbox_style.clone_text_orientation() {
147 TextOrientation::Mixed => {},
148 TextOrientation::Upright => {
149 flags.insert(WritingMode::UPRIGHT);
151 // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-upright:
153 // > This value causes the used value of direction
154 // > to be ltr, and for the purposes of bidi
155 // > reordering, causes all characters to be treated
157 flags.remove(WritingMode::RTL);
158 flags.remove(WritingMode::INLINE_REVERSED);
160 TextOrientation::Sideways => {
161 flags.insert(WritingMode::TEXT_SIDEWAYS);
172 /// Returns the `horizontal-tb` value.
173 pub fn horizontal_tb() -> Self {
178 pub fn is_vertical(&self) -> bool {
179 self.intersects(WritingMode::VERTICAL)
183 pub fn is_horizontal(&self) -> bool {
187 /// Assuming .is_vertical(), does the block direction go left to right?
189 pub fn is_vertical_lr(&self) -> bool {
190 self.intersects(WritingMode::VERTICAL_LR)
193 /// Assuming .is_vertical(), does the inline direction go top to bottom?
195 pub fn is_inline_tb(&self) -> bool {
196 // https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical
197 !self.intersects(WritingMode::INLINE_REVERSED)
201 pub fn is_bidi_ltr(&self) -> bool {
202 !self.intersects(WritingMode::RTL)
206 pub fn is_sideways(&self) -> bool {
207 self.intersects(WritingMode::VERTICAL_SIDEWAYS | WritingMode::TEXT_SIDEWAYS)
211 pub fn is_upright(&self) -> bool {
212 self.intersects(WritingMode::UPRIGHT)
215 /// https://drafts.csswg.org/css-writing-modes/#logical-to-physical
217 /// | Return | line-left is… | line-right is… |
218 /// |---------|---------------|----------------|
219 /// | `true` | inline-start | inline-end |
220 /// | `false` | inline-end | inline-start |
222 pub fn line_left_is_inline_start(&self) -> bool {
223 // https://drafts.csswg.org/css-writing-modes/#inline-start
224 // “For boxes with a used direction value of ltr, this means the line-left side.
225 // For boxes with a used direction value of rtl, this means the line-right side.”
230 pub fn inline_start_physical_side(&self) -> PhysicalSide {
231 match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
232 (false, _, true) => PhysicalSide::Left,
233 (false, _, false) => PhysicalSide::Right,
234 (true, true, _) => PhysicalSide::Top,
235 (true, false, _) => PhysicalSide::Bottom,
240 pub fn inline_end_physical_side(&self) -> PhysicalSide {
241 match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
242 (false, _, true) => PhysicalSide::Right,
243 (false, _, false) => PhysicalSide::Left,
244 (true, true, _) => PhysicalSide::Bottom,
245 (true, false, _) => PhysicalSide::Top,
250 pub fn block_start_physical_side(&self) -> PhysicalSide {
251 match (self.is_vertical(), self.is_vertical_lr()) {
252 (false, _) => PhysicalSide::Top,
253 (true, true) => PhysicalSide::Left,
254 (true, false) => PhysicalSide::Right,
259 pub fn block_end_physical_side(&self) -> PhysicalSide {
260 match (self.is_vertical(), self.is_vertical_lr()) {
261 (false, _) => PhysicalSide::Bottom,
262 (true, true) => PhysicalSide::Right,
263 (true, false) => PhysicalSide::Left,
268 fn physical_sides_to_corner(
269 block_side: PhysicalSide,
270 inline_side: PhysicalSide,
271 ) -> PhysicalCorner {
272 match (block_side, inline_side) {
273 (PhysicalSide::Top, PhysicalSide::Left) | (PhysicalSide::Left, PhysicalSide::Top) => {
274 PhysicalCorner::TopLeft
276 (PhysicalSide::Top, PhysicalSide::Right) | (PhysicalSide::Right, PhysicalSide::Top) => {
277 PhysicalCorner::TopRight
279 (PhysicalSide::Bottom, PhysicalSide::Right) |
280 (PhysicalSide::Right, PhysicalSide::Bottom) => PhysicalCorner::BottomRight,
281 (PhysicalSide::Bottom, PhysicalSide::Left) |
282 (PhysicalSide::Left, PhysicalSide::Bottom) => PhysicalCorner::BottomLeft,
283 _ => unreachable!("block and inline sides must be orthogonal"),
288 pub fn start_start_physical_corner(&self) -> PhysicalCorner {
289 WritingMode::physical_sides_to_corner(
290 self.block_start_physical_side(),
291 self.inline_start_physical_side(),
296 pub fn start_end_physical_corner(&self) -> PhysicalCorner {
297 WritingMode::physical_sides_to_corner(
298 self.block_start_physical_side(),
299 self.inline_end_physical_side(),
304 pub fn end_start_physical_corner(&self) -> PhysicalCorner {
305 WritingMode::physical_sides_to_corner(
306 self.block_end_physical_side(),
307 self.inline_start_physical_side(),
312 pub fn end_end_physical_corner(&self) -> PhysicalCorner {
313 WritingMode::physical_sides_to_corner(
314 self.block_end_physical_side(),
315 self.inline_end_physical_side(),
320 pub fn block_flow_direction(&self) -> BlockFlowDirection {
321 match (self.is_vertical(), self.is_vertical_lr()) {
322 (false, _) => BlockFlowDirection::TopToBottom,
323 (true, true) => BlockFlowDirection::LeftToRight,
324 (true, false) => BlockFlowDirection::RightToLeft,
329 pub fn inline_base_direction(&self) -> InlineBaseDirection {
330 if self.intersects(WritingMode::RTL) {
331 InlineBaseDirection::RightToLeft
333 InlineBaseDirection::LeftToRight
338 /// The default bidirectional embedding level for this writing mode.
340 /// Returns bidi level 0 if the mode is LTR, or 1 otherwise.
341 pub fn to_bidi_level(&self) -> bidi::Level {
342 if self.is_bidi_ltr() {
350 impl fmt::Display for WritingMode {
351 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
352 if self.is_vertical() {
353 write!(formatter, "V")?;
354 if self.is_vertical_lr() {
355 write!(formatter, " LR")?;
357 write!(formatter, " RL")?;
359 if self.is_sideways() {
360 write!(formatter, " Sideways")?;
362 if self.intersects(WritingMode::LINE_INVERTED) {
363 write!(formatter, " Inverted")?;
366 write!(formatter, "H")?;
368 if self.is_bidi_ltr() {
369 write!(formatter, " LTR")
371 write!(formatter, " RTL")
376 /// Wherever logical geometry is used, the writing mode is known based on context:
377 /// every method takes a `mode` parameter.
378 /// However, this context is easy to get wrong.
379 /// In debug builds only, logical geometry objects store their writing mode
380 /// (in addition to taking it as a parameter to methods) and check it.
381 /// In non-debug builds, make this storage zero-size and the checks no-ops.
382 #[cfg(not(debug_assertions))]
383 #[derive(Clone, Copy, Eq, PartialEq)]
384 #[cfg_attr(feature = "servo", derive(Serialize))]
385 struct DebugWritingMode;
387 #[cfg(debug_assertions)]
388 #[derive(Clone, Copy, Eq, PartialEq)]
389 #[cfg_attr(feature = "servo", derive(Serialize))]
390 struct DebugWritingMode {
394 #[cfg(not(debug_assertions))]
395 impl DebugWritingMode {
397 fn check(&self, _other: WritingMode) {}
400 fn check_debug(&self, _other: DebugWritingMode) {}
403 fn new(_mode: WritingMode) -> DebugWritingMode {
408 #[cfg(debug_assertions)]
409 impl DebugWritingMode {
411 fn check(&self, other: WritingMode) {
412 assert_eq!(self.mode, other)
416 fn check_debug(&self, other: DebugWritingMode) {
417 assert_eq!(self.mode, other.mode)
421 fn new(mode: WritingMode) -> DebugWritingMode {
422 DebugWritingMode { mode }
426 impl Debug for DebugWritingMode {
427 #[cfg(not(debug_assertions))]
428 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
429 write!(formatter, "?")
432 #[cfg(debug_assertions)]
433 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
434 write!(formatter, "{}", self.mode)
438 // Used to specify the logical direction.
439 #[derive(Clone, Copy, Debug, PartialEq)]
440 #[cfg_attr(feature = "servo", derive(Serialize))]
446 /// A 2D size in flow-relative dimensions
447 #[derive(Clone, Copy, Eq, PartialEq)]
448 #[cfg_attr(feature = "servo", derive(Serialize))]
449 pub struct LogicalSize<T> {
450 pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure
451 pub block: T, // block-size, a.k.a. logical height, a.k.a. extent
452 debug_writing_mode: DebugWritingMode,
455 impl<T: Debug> Debug for LogicalSize<T> {
456 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
459 "LogicalSize({:?}, i{:?}×b{:?})",
460 self.debug_writing_mode, self.inline, self.block
465 // Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
466 impl<T: Zero> LogicalSize<T> {
468 pub fn zero(mode: WritingMode) -> LogicalSize<T> {
470 inline: Zero::zero(),
472 debug_writing_mode: DebugWritingMode::new(mode),
477 impl<T> LogicalSize<T> {
479 pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T> {
483 debug_writing_mode: DebugWritingMode::new(mode),
488 pub fn from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T> {
489 if mode.is_vertical() {
490 LogicalSize::new(mode, size.height, size.width)
492 LogicalSize::new(mode, size.width, size.height)
497 impl<T: Copy> LogicalSize<T> {
499 pub fn width(&self, mode: WritingMode) -> T {
500 self.debug_writing_mode.check(mode);
501 if mode.is_vertical() {
509 pub fn set_width(&mut self, mode: WritingMode, width: T) {
510 self.debug_writing_mode.check(mode);
511 if mode.is_vertical() {
519 pub fn height(&self, mode: WritingMode) -> T {
520 self.debug_writing_mode.check(mode);
521 if mode.is_vertical() {
529 pub fn set_height(&mut self, mode: WritingMode, height: T) {
530 self.debug_writing_mode.check(mode);
531 if mode.is_vertical() {
539 pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> {
540 self.debug_writing_mode.check(mode);
541 if mode.is_vertical() {
542 Size2D::new(self.block, self.inline)
544 Size2D::new(self.inline, self.block)
549 pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T> {
550 if mode_from == mode_to {
551 self.debug_writing_mode.check(mode_from);
554 LogicalSize::from_physical(mode_to, self.to_physical(mode_from))
559 impl<T: Add<T, Output = T>> Add for LogicalSize<T> {
560 type Output = LogicalSize<T>;
563 fn add(self, other: LogicalSize<T>) -> LogicalSize<T> {
564 self.debug_writing_mode
565 .check_debug(other.debug_writing_mode);
567 debug_writing_mode: self.debug_writing_mode,
568 inline: self.inline + other.inline,
569 block: self.block + other.block,
574 impl<T: Sub<T, Output = T>> Sub for LogicalSize<T> {
575 type Output = LogicalSize<T>;
578 fn sub(self, other: LogicalSize<T>) -> LogicalSize<T> {
579 self.debug_writing_mode
580 .check_debug(other.debug_writing_mode);
582 debug_writing_mode: self.debug_writing_mode,
583 inline: self.inline - other.inline,
584 block: self.block - other.block,
589 /// A 2D point in flow-relative dimensions
590 #[derive(Clone, Copy, Eq, PartialEq)]
591 #[cfg_attr(feature = "servo", derive(Serialize))]
592 pub struct LogicalPoint<T> {
593 /// inline-axis coordinate
595 /// block-axis coordinate
597 debug_writing_mode: DebugWritingMode,
600 impl<T: Debug> Debug for LogicalPoint<T> {
601 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
604 "LogicalPoint({:?} (i{:?}, b{:?}))",
605 self.debug_writing_mode, self.i, self.b
610 // Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
611 impl<T: Zero> LogicalPoint<T> {
613 pub fn zero(mode: WritingMode) -> LogicalPoint<T> {
617 debug_writing_mode: DebugWritingMode::new(mode),
622 impl<T: Copy> LogicalPoint<T> {
624 pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T> {
628 debug_writing_mode: DebugWritingMode::new(mode),
633 impl<T: Copy + Sub<T, Output = T>> LogicalPoint<T> {
635 pub fn from_physical(
638 container_size: Size2D<T>,
639 ) -> LogicalPoint<T> {
640 if mode.is_vertical() {
642 i: if mode.is_inline_tb() {
645 container_size.height - point.y
647 b: if mode.is_vertical_lr() {
650 container_size.width - point.x
652 debug_writing_mode: DebugWritingMode::new(mode),
656 i: if mode.is_bidi_ltr() {
659 container_size.width - point.x
662 debug_writing_mode: DebugWritingMode::new(mode),
668 pub fn x(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
669 self.debug_writing_mode.check(mode);
670 if mode.is_vertical() {
671 if mode.is_vertical_lr() {
674 container_size.width - self.b
677 if mode.is_bidi_ltr() {
680 container_size.width - self.i
686 pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>) {
687 self.debug_writing_mode.check(mode);
688 if mode.is_vertical() {
689 self.b = if mode.is_vertical_lr() {
692 container_size.width - x
695 self.i = if mode.is_bidi_ltr() {
698 container_size.width - x
704 pub fn y(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
705 self.debug_writing_mode.check(mode);
706 if mode.is_vertical() {
707 if mode.is_inline_tb() {
710 container_size.height - self.i
718 pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>) {
719 self.debug_writing_mode.check(mode);
720 if mode.is_vertical() {
721 self.i = if mode.is_inline_tb() {
724 container_size.height - y
732 pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> {
733 self.debug_writing_mode.check(mode);
734 if mode.is_vertical() {
736 if mode.is_vertical_lr() {
739 container_size.width - self.b
741 if mode.is_inline_tb() {
744 container_size.height - self.i
749 if mode.is_bidi_ltr() {
752 container_size.width - self.i
762 mode_from: WritingMode,
763 mode_to: WritingMode,
764 container_size: Size2D<T>,
765 ) -> LogicalPoint<T> {
766 if mode_from == mode_to {
767 self.debug_writing_mode.check(mode_from);
770 LogicalPoint::from_physical(
772 self.to_physical(mode_from, container_size),
779 impl<T: Copy + Add<T, Output = T>> LogicalPoint<T> {
780 /// This doesn’t really makes sense,
781 /// but happens when dealing with multiple origins.
783 pub fn add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T> {
784 self.debug_writing_mode
785 .check_debug(other.debug_writing_mode);
787 debug_writing_mode: self.debug_writing_mode,
794 impl<T: Copy + Add<T, Output = T>> Add<LogicalSize<T>> for LogicalPoint<T> {
795 type Output = LogicalPoint<T>;
798 fn add(self, other: LogicalSize<T>) -> LogicalPoint<T> {
799 self.debug_writing_mode
800 .check_debug(other.debug_writing_mode);
802 debug_writing_mode: self.debug_writing_mode,
803 i: self.i + other.inline,
804 b: self.b + other.block,
809 impl<T: Copy + Sub<T, Output = T>> Sub<LogicalSize<T>> for LogicalPoint<T> {
810 type Output = LogicalPoint<T>;
813 fn sub(self, other: LogicalSize<T>) -> LogicalPoint<T> {
814 self.debug_writing_mode
815 .check_debug(other.debug_writing_mode);
817 debug_writing_mode: self.debug_writing_mode,
818 i: self.i - other.inline,
819 b: self.b - other.block,
824 /// A "margin" in flow-relative dimensions
825 /// Represents the four sides of the margins, borders, or padding of a CSS box,
826 /// or a combination of those.
827 /// A positive "margin" can be added to a rectangle to obtain a bigger rectangle.
828 #[derive(Clone, Copy, Eq, PartialEq)]
829 #[cfg_attr(feature = "servo", derive(Serialize))]
830 pub struct LogicalMargin<T> {
835 debug_writing_mode: DebugWritingMode,
838 impl<T: Debug> Debug for LogicalMargin<T> {
839 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
840 let writing_mode_string = if cfg!(debug_assertions) {
841 format!("{:?}, ", self.debug_writing_mode)
848 "LogicalMargin({}i:{:?}..{:?} b:{:?}..{:?})",
858 impl<T: Zero> LogicalMargin<T> {
860 pub fn zero(mode: WritingMode) -> LogicalMargin<T> {
862 block_start: Zero::zero(),
863 inline_end: Zero::zero(),
864 block_end: Zero::zero(),
865 inline_start: Zero::zero(),
866 debug_writing_mode: DebugWritingMode::new(mode),
871 impl<T> LogicalMargin<T> {
879 ) -> LogicalMargin<T> {
885 debug_writing_mode: DebugWritingMode::new(mode),
890 pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> {
895 if mode.is_vertical() {
896 if mode.is_vertical_lr() {
897 block_start = offsets.left;
898 block_end = offsets.right;
900 block_start = offsets.right;
901 block_end = offsets.left;
903 if mode.is_inline_tb() {
904 inline_start = offsets.top;
905 inline_end = offsets.bottom;
907 inline_start = offsets.bottom;
908 inline_end = offsets.top;
911 block_start = offsets.top;
912 block_end = offsets.bottom;
913 if mode.is_bidi_ltr() {
914 inline_start = offsets.left;
915 inline_end = offsets.right;
917 inline_start = offsets.right;
918 inline_end = offsets.left;
921 LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start)
925 impl<T: Copy> LogicalMargin<T> {
927 pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T> {
928 LogicalMargin::new(mode, value, value, value, value)
932 pub fn top(&self, mode: WritingMode) -> T {
933 self.debug_writing_mode.check(mode);
934 if mode.is_vertical() {
935 if mode.is_inline_tb() {
946 pub fn set_top(&mut self, mode: WritingMode, top: T) {
947 self.debug_writing_mode.check(mode);
948 if mode.is_vertical() {
949 if mode.is_inline_tb() {
950 self.inline_start = top
952 self.inline_end = top
955 self.block_start = top
960 pub fn right(&self, mode: WritingMode) -> T {
961 self.debug_writing_mode.check(mode);
962 if mode.is_vertical() {
963 if mode.is_vertical_lr() {
969 if mode.is_bidi_ltr() {
978 pub fn set_right(&mut self, mode: WritingMode, right: T) {
979 self.debug_writing_mode.check(mode);
980 if mode.is_vertical() {
981 if mode.is_vertical_lr() {
982 self.block_end = right
984 self.block_start = right
987 if mode.is_bidi_ltr() {
988 self.inline_end = right
990 self.inline_start = right
996 pub fn bottom(&self, mode: WritingMode) -> T {
997 self.debug_writing_mode.check(mode);
998 if mode.is_vertical() {
999 if mode.is_inline_tb() {
1010 pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {
1011 self.debug_writing_mode.check(mode);
1012 if mode.is_vertical() {
1013 if mode.is_inline_tb() {
1014 self.inline_end = bottom
1016 self.inline_start = bottom
1019 self.block_end = bottom
1024 pub fn left(&self, mode: WritingMode) -> T {
1025 self.debug_writing_mode.check(mode);
1026 if mode.is_vertical() {
1027 if mode.is_vertical_lr() {
1033 if mode.is_bidi_ltr() {
1042 pub fn set_left(&mut self, mode: WritingMode, left: T) {
1043 self.debug_writing_mode.check(mode);
1044 if mode.is_vertical() {
1045 if mode.is_vertical_lr() {
1046 self.block_start = left
1048 self.block_end = left
1051 if mode.is_bidi_ltr() {
1052 self.inline_start = left
1054 self.inline_end = left
1060 pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> {
1061 self.debug_writing_mode.check(mode);
1066 if mode.is_vertical() {
1067 if mode.is_vertical_lr() {
1068 left = self.block_start;
1069 right = self.block_end;
1071 right = self.block_start;
1072 left = self.block_end;
1074 if mode.is_inline_tb() {
1075 top = self.inline_start;
1076 bottom = self.inline_end;
1078 bottom = self.inline_start;
1079 top = self.inline_end;
1082 top = self.block_start;
1083 bottom = self.block_end;
1084 if mode.is_bidi_ltr() {
1085 left = self.inline_start;
1086 right = self.inline_end;
1088 right = self.inline_start;
1089 left = self.inline_end;
1092 SideOffsets2D::new(top, right, bottom, left)
1096 pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> {
1097 if mode_from == mode_to {
1098 self.debug_writing_mode.check(mode_from);
1101 LogicalMargin::from_physical(mode_to, self.to_physical(mode_from))
1106 impl<T: PartialEq + Zero> LogicalMargin<T> {
1108 pub fn is_zero(&self) -> bool {
1109 self.block_start == Zero::zero() &&
1110 self.inline_end == Zero::zero() &&
1111 self.block_end == Zero::zero() &&
1112 self.inline_start == Zero::zero()
1116 impl<T: Copy + Add<T, Output = T>> LogicalMargin<T> {
1118 pub fn inline_start_end(&self) -> T {
1119 self.inline_start + self.inline_end
1123 pub fn block_start_end(&self) -> T {
1124 self.block_start + self.block_end
1128 pub fn start_end(&self, direction: Direction) -> T {
1130 Direction::Inline => self.inline_start + self.inline_end,
1131 Direction::Block => self.block_start + self.block_end,
1136 pub fn top_bottom(&self, mode: WritingMode) -> T {
1137 self.debug_writing_mode.check(mode);
1138 if mode.is_vertical() {
1139 self.inline_start_end()
1141 self.block_start_end()
1146 pub fn left_right(&self, mode: WritingMode) -> T {
1147 self.debug_writing_mode.check(mode);
1148 if mode.is_vertical() {
1149 self.block_start_end()
1151 self.inline_start_end()
1156 impl<T: Add<T, Output = T>> Add for LogicalMargin<T> {
1157 type Output = LogicalMargin<T>;
1160 fn add(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1161 self.debug_writing_mode
1162 .check_debug(other.debug_writing_mode);
1164 debug_writing_mode: self.debug_writing_mode,
1165 block_start: self.block_start + other.block_start,
1166 inline_end: self.inline_end + other.inline_end,
1167 block_end: self.block_end + other.block_end,
1168 inline_start: self.inline_start + other.inline_start,
1173 impl<T: Sub<T, Output = T>> Sub for LogicalMargin<T> {
1174 type Output = LogicalMargin<T>;
1177 fn sub(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1178 self.debug_writing_mode
1179 .check_debug(other.debug_writing_mode);
1181 debug_writing_mode: self.debug_writing_mode,
1182 block_start: self.block_start - other.block_start,
1183 inline_end: self.inline_end - other.inline_end,
1184 block_end: self.block_end - other.block_end,
1185 inline_start: self.inline_start - other.inline_start,
1190 /// A rectangle in flow-relative dimensions
1191 #[derive(Clone, Copy, Eq, PartialEq)]
1192 #[cfg_attr(feature = "servo", derive(Serialize))]
1193 pub struct LogicalRect<T> {
1194 pub start: LogicalPoint<T>,
1195 pub size: LogicalSize<T>,
1196 debug_writing_mode: DebugWritingMode,
1199 impl<T: Debug> Debug for LogicalRect<T> {
1200 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
1201 let writing_mode_string = if cfg!(debug_assertions) {
1202 format!("{:?}, ", self.debug_writing_mode)
1209 "LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))",
1210 writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b
1215 impl<T: Zero> LogicalRect<T> {
1217 pub fn zero(mode: WritingMode) -> LogicalRect<T> {
1219 start: LogicalPoint::zero(mode),
1220 size: LogicalSize::zero(mode),
1221 debug_writing_mode: DebugWritingMode::new(mode),
1226 impl<T: Copy> LogicalRect<T> {
1234 ) -> LogicalRect<T> {
1236 start: LogicalPoint::new(mode, inline_start, block_start),
1237 size: LogicalSize::new(mode, inline, block),
1238 debug_writing_mode: DebugWritingMode::new(mode),
1243 pub fn from_point_size(
1245 start: LogicalPoint<T>,
1246 size: LogicalSize<T>,
1247 ) -> LogicalRect<T> {
1248 start.debug_writing_mode.check(mode);
1249 size.debug_writing_mode.check(mode);
1253 debug_writing_mode: DebugWritingMode::new(mode),
1258 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1260 pub fn from_physical(
1263 container_size: Size2D<T>,
1264 ) -> LogicalRect<T> {
1269 if mode.is_vertical() {
1270 inline = rect.size.height;
1271 block = rect.size.width;
1272 if mode.is_vertical_lr() {
1273 block_start = rect.origin.x;
1275 block_start = container_size.width - (rect.origin.x + rect.size.width);
1277 if mode.is_inline_tb() {
1278 inline_start = rect.origin.y;
1280 inline_start = container_size.height - (rect.origin.y + rect.size.height);
1283 inline = rect.size.width;
1284 block = rect.size.height;
1285 block_start = rect.origin.y;
1286 if mode.is_bidi_ltr() {
1287 inline_start = rect.origin.x;
1289 inline_start = container_size.width - (rect.origin.x + rect.size.width);
1293 start: LogicalPoint::new(mode, inline_start, block_start),
1294 size: LogicalSize::new(mode, inline, block),
1295 debug_writing_mode: DebugWritingMode::new(mode),
1300 pub fn inline_end(&self) -> T {
1301 self.start.i + self.size.inline
1305 pub fn block_end(&self) -> T {
1306 self.start.b + self.size.block
1310 pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> {
1311 self.debug_writing_mode.check(mode);
1316 if mode.is_vertical() {
1317 width = self.size.block;
1318 height = self.size.inline;
1319 if mode.is_vertical_lr() {
1322 x = container_size.width - self.block_end();
1324 if mode.is_inline_tb() {
1327 y = container_size.height - self.inline_end();
1330 width = self.size.inline;
1331 height = self.size.block;
1333 if mode.is_bidi_ltr() {
1336 x = container_size.width - self.inline_end();
1340 origin: Point2D::new(x, y),
1341 size: Size2D::new(width, height),
1348 mode_from: WritingMode,
1349 mode_to: WritingMode,
1350 container_size: Size2D<T>,
1351 ) -> LogicalRect<T> {
1352 if mode_from == mode_to {
1353 self.debug_writing_mode.check(mode_from);
1356 LogicalRect::from_physical(
1358 self.to_physical(mode_from, container_size),
1364 pub fn translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T> {
1366 start: self.start + offset,
1371 pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> {
1377 debug_writing_mode: offset.debug_writing_mode,
1380 debug_writing_mode: self.debug_writing_mode,
1385 impl<T: Copy + Ord + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1387 pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> {
1388 self.debug_writing_mode
1389 .check_debug(other.debug_writing_mode);
1391 let inline_start = min(self.start.i, other.start.i);
1392 let block_start = min(self.start.b, other.start.b);
1394 start: LogicalPoint {
1397 debug_writing_mode: self.debug_writing_mode,
1400 inline: max(self.inline_end(), other.inline_end()) - inline_start,
1401 block: max(self.block_end(), other.block_end()) - block_start,
1402 debug_writing_mode: self.debug_writing_mode,
1404 debug_writing_mode: self.debug_writing_mode,
1409 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Add<LogicalMargin<T>> for LogicalRect<T> {
1410 type Output = LogicalRect<T>;
1413 fn add(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1414 self.debug_writing_mode
1415 .check_debug(other.debug_writing_mode);
1417 start: LogicalPoint {
1418 // Growing a rectangle on the start side means pushing its
1419 // start point on the negative direction.
1420 i: self.start.i - other.inline_start,
1421 b: self.start.b - other.block_start,
1422 debug_writing_mode: self.debug_writing_mode,
1425 inline: self.size.inline + other.inline_start_end(),
1426 block: self.size.block + other.block_start_end(),
1427 debug_writing_mode: self.debug_writing_mode,
1429 debug_writing_mode: self.debug_writing_mode,
1434 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Sub<LogicalMargin<T>> for LogicalRect<T> {
1435 type Output = LogicalRect<T>;
1438 fn sub(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1439 self.debug_writing_mode
1440 .check_debug(other.debug_writing_mode);
1442 start: LogicalPoint {
1443 // Shrinking a rectangle on the start side means pushing its
1444 // start point on the positive direction.
1445 i: self.start.i + other.inline_start,
1446 b: self.start.b + other.block_start,
1447 debug_writing_mode: self.debug_writing_mode,
1450 inline: self.size.inline - other.inline_start_end(),
1451 block: self.size.block - other.block_start_end(),
1452 debug_writing_mode: self.debug_writing_mode,
1454 debug_writing_mode: self.debug_writing_mode,
1459 #[derive(Clone, Copy, Debug, PartialEq)]
1460 pub enum PhysicalSide {
1467 #[derive(Clone, Copy, Debug, PartialEq)]
1468 pub enum PhysicalCorner {