1use crate::properties::style_structs;
8use euclid::default::{Point2D, Rect, SideOffsets2D, Size2D};
9use euclid::num::Zero;
10use std::cmp::{max, min};
11use std::fmt::{self, Debug, Error, Formatter};
12use std::ops::{Add, Sub};
13
14pub enum BlockFlowDirection {
15 TopToBottom,
16 RightToLeft,
17 LeftToRight,
18}
19
20pub enum InlineBaseDirection {
21 LeftToRight,
22 RightToLeft,
23}
24
25#[allow(missing_docs)]
29#[derive(
30 Clone,
31 Copy,
32 Debug,
33 Eq,
34 FromPrimitive,
35 MallocSizeOf,
36 Parse,
37 PartialEq,
38 SpecifiedValueInfo,
39 ToComputedValue,
40 ToCss,
41 ToResolvedValue,
42 ToShmem,
43)]
44#[repr(u8)]
45pub enum WritingModeProperty {
46 #[parse(aliases = "lr,lr-tb,rl,rl-tb")]
47 HorizontalTb,
48 #[parse(aliases = "tb,tb-rl")]
49 VerticalRl,
50 VerticalLr,
51 #[cfg(feature = "gecko")]
52 SidewaysRl,
53 #[cfg(feature = "gecko")]
54 SidewaysLr,
55}
56
57#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Serialize)]
59#[repr(C)]
60pub struct WritingMode(u8);
61bitflags!(
62 impl WritingMode: u8 {
63 const VERTICAL = 1 << 0;
66 const INLINE_REVERSED = 1 << 1;
73 const VERTICAL_LR = 1 << 2;
78 const LINE_INVERTED = 1 << 3;
83 const RTL = 1 << 4;
85 const VERTICAL_SIDEWAYS = 1 << 5;
93 const TEXT_SIDEWAYS = 1 << 6;
101 const UPRIGHT = 1 << 7;
109 const WRITING_MODE_HORIZONTAL_TB = 0;
113 const WRITING_MODE_VERTICAL_RL = WritingMode::VERTICAL.bits();
115 const WRITING_MODE_VERTICAL_LR = WritingMode::VERTICAL.bits() |
117 WritingMode::VERTICAL_LR.bits() |
118 WritingMode::LINE_INVERTED.bits();
119 const WRITING_MODE_SIDEWAYS_RL = WritingMode::VERTICAL.bits() |
121 WritingMode::VERTICAL_SIDEWAYS.bits();
122 const WRITING_MODE_SIDEWAYS_LR = WritingMode::VERTICAL.bits() |
124 WritingMode::VERTICAL_LR.bits() |
125 WritingMode::VERTICAL_SIDEWAYS.bits();
126 }
127);
128
129impl WritingMode {
130 pub fn new(inheritedbox_style: &style_structs::InheritedBox) -> Self {
132 use crate::properties::longhands::direction::computed_value::T as Direction;
133
134 let mut flags = WritingMode::empty();
135
136 let direction = inheritedbox_style.clone_direction();
137 let writing_mode = inheritedbox_style.clone_writing_mode();
138
139 match direction {
140 Direction::Ltr => {},
141 Direction::Rtl => {
142 flags.insert(WritingMode::RTL);
143 },
144 }
145
146 match writing_mode {
147 WritingModeProperty::HorizontalTb => {
148 if direction == Direction::Rtl {
149 flags.insert(WritingMode::INLINE_REVERSED);
150 }
151 },
152 WritingModeProperty::VerticalRl => {
153 flags.insert(WritingMode::WRITING_MODE_VERTICAL_RL);
154 if direction == Direction::Rtl {
155 flags.insert(WritingMode::INLINE_REVERSED);
156 }
157 },
158 WritingModeProperty::VerticalLr => {
159 flags.insert(WritingMode::WRITING_MODE_VERTICAL_LR);
160 if direction == Direction::Rtl {
161 flags.insert(WritingMode::INLINE_REVERSED);
162 }
163 },
164 #[cfg(feature = "gecko")]
165 WritingModeProperty::SidewaysRl => {
166 flags.insert(WritingMode::WRITING_MODE_SIDEWAYS_RL);
167 if direction == Direction::Rtl {
168 flags.insert(WritingMode::INLINE_REVERSED);
169 }
170 },
171 #[cfg(feature = "gecko")]
172 WritingModeProperty::SidewaysLr => {
173 flags.insert(WritingMode::WRITING_MODE_SIDEWAYS_LR);
174 if direction == Direction::Ltr {
175 flags.insert(WritingMode::INLINE_REVERSED);
176 }
177 },
178 }
179
180 #[cfg(feature = "gecko")]
181 {
182 use crate::properties::longhands::text_orientation::computed_value::T as TextOrientation;
183
184 match writing_mode {
187 WritingModeProperty::VerticalRl | WritingModeProperty::VerticalLr => {
188 match inheritedbox_style.clone_text_orientation() {
189 TextOrientation::Mixed => {},
190 TextOrientation::Upright => {
191 flags.insert(WritingMode::UPRIGHT);
192
193 flags.remove(WritingMode::RTL);
200 flags.remove(WritingMode::INLINE_REVERSED);
201 },
202 TextOrientation::Sideways => {
203 flags.insert(WritingMode::TEXT_SIDEWAYS);
204 },
205 }
206 },
207 _ => {},
208 }
209 }
210
211 flags
212 }
213
214 pub fn horizontal_tb() -> Self {
216 Self::empty()
217 }
218
219 #[inline]
220 pub fn is_vertical(&self) -> bool {
221 self.intersects(WritingMode::VERTICAL)
222 }
223
224 #[inline]
225 pub fn is_horizontal(&self) -> bool {
226 !self.is_vertical()
227 }
228
229 #[inline]
231 pub fn is_vertical_lr(&self) -> bool {
232 self.intersects(WritingMode::VERTICAL_LR)
233 }
234
235 #[inline]
237 pub fn is_inline_tb(&self) -> bool {
238 !self.intersects(WritingMode::INLINE_REVERSED)
240 }
241
242 #[inline]
243 pub fn is_bidi_ltr(&self) -> bool {
244 !self.intersects(WritingMode::RTL)
245 }
246
247 #[inline]
248 pub fn is_sideways(&self) -> bool {
249 self.intersects(WritingMode::VERTICAL_SIDEWAYS | WritingMode::TEXT_SIDEWAYS)
250 }
251
252 #[inline]
253 pub fn is_upright(&self) -> bool {
254 self.intersects(WritingMode::UPRIGHT)
255 }
256
257 #[inline]
264 pub fn line_left_is_inline_start(&self) -> bool {
265 self.is_bidi_ltr()
269 }
270
271 #[inline]
272 pub fn inline_start_physical_side(&self) -> PhysicalSide {
273 match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
274 (false, _, true) => PhysicalSide::Left,
275 (false, _, false) => PhysicalSide::Right,
276 (true, true, _) => PhysicalSide::Top,
277 (true, false, _) => PhysicalSide::Bottom,
278 }
279 }
280
281 #[inline]
282 pub fn inline_end_physical_side(&self) -> PhysicalSide {
283 match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
284 (false, _, true) => PhysicalSide::Right,
285 (false, _, false) => PhysicalSide::Left,
286 (true, true, _) => PhysicalSide::Bottom,
287 (true, false, _) => PhysicalSide::Top,
288 }
289 }
290
291 #[inline]
292 pub fn block_start_physical_side(&self) -> PhysicalSide {
293 match (self.is_vertical(), self.is_vertical_lr()) {
294 (false, _) => PhysicalSide::Top,
295 (true, true) => PhysicalSide::Left,
296 (true, false) => PhysicalSide::Right,
297 }
298 }
299
300 #[inline]
301 pub fn block_end_physical_side(&self) -> PhysicalSide {
302 match (self.is_vertical(), self.is_vertical_lr()) {
303 (false, _) => PhysicalSide::Bottom,
304 (true, true) => PhysicalSide::Right,
305 (true, false) => PhysicalSide::Left,
306 }
307 }
308
309 #[inline]
310 pub fn start_start_physical_corner(&self) -> PhysicalCorner {
311 PhysicalCorner::from_sides(
312 self.block_start_physical_side(),
313 self.inline_start_physical_side(),
314 )
315 }
316
317 #[inline]
318 pub fn start_end_physical_corner(&self) -> PhysicalCorner {
319 PhysicalCorner::from_sides(
320 self.block_start_physical_side(),
321 self.inline_end_physical_side(),
322 )
323 }
324
325 #[inline]
326 pub fn end_start_physical_corner(&self) -> PhysicalCorner {
327 PhysicalCorner::from_sides(
328 self.block_end_physical_side(),
329 self.inline_start_physical_side(),
330 )
331 }
332
333 #[inline]
334 pub fn end_end_physical_corner(&self) -> PhysicalCorner {
335 PhysicalCorner::from_sides(
336 self.block_end_physical_side(),
337 self.inline_end_physical_side(),
338 )
339 }
340
341 #[inline]
342 pub fn block_flow_direction(&self) -> BlockFlowDirection {
343 match (self.is_vertical(), self.is_vertical_lr()) {
344 (false, _) => BlockFlowDirection::TopToBottom,
345 (true, true) => BlockFlowDirection::LeftToRight,
346 (true, false) => BlockFlowDirection::RightToLeft,
347 }
348 }
349
350 #[inline]
351 pub fn inline_base_direction(&self) -> InlineBaseDirection {
352 if self.intersects(WritingMode::RTL) {
353 InlineBaseDirection::RightToLeft
354 } else {
355 InlineBaseDirection::LeftToRight
356 }
357 }
358
359 #[inline]
360 pub fn is_text_vertical(&self) -> bool {
362 self.is_vertical() && !self.is_sideways()
363 }
364}
365
366impl fmt::Display for WritingMode {
367 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
368 if self.is_vertical() {
369 write!(formatter, "V")?;
370 if self.is_vertical_lr() {
371 write!(formatter, " LR")?;
372 } else {
373 write!(formatter, " RL")?;
374 }
375 if self.is_sideways() {
376 write!(formatter, " Sideways")?;
377 }
378 if self.intersects(WritingMode::LINE_INVERTED) {
379 write!(formatter, " Inverted")?;
380 }
381 } else {
382 write!(formatter, "H")?;
383 }
384 if self.is_bidi_ltr() {
385 write!(formatter, " LTR")
386 } else {
387 write!(formatter, " RTL")
388 }
389 }
390}
391
392#[cfg(not(debug_assertions))]
399#[derive(Clone, Copy, Eq, PartialEq)]
400#[cfg_attr(feature = "servo", derive(Serialize))]
401struct DebugWritingMode;
402
403#[cfg(debug_assertions)]
404#[derive(Clone, Copy, Eq, PartialEq)]
405#[cfg_attr(feature = "servo", derive(Serialize))]
406struct DebugWritingMode {
407 mode: WritingMode,
408}
409
410#[cfg(not(debug_assertions))]
411impl DebugWritingMode {
412 #[inline]
413 fn check(&self, _other: WritingMode) {}
414
415 #[inline]
416 fn check_debug(&self, _other: DebugWritingMode) {}
417
418 #[inline]
419 fn new(_mode: WritingMode) -> DebugWritingMode {
420 DebugWritingMode
421 }
422}
423
424#[cfg(debug_assertions)]
425impl DebugWritingMode {
426 #[inline]
427 fn check(&self, other: WritingMode) {
428 assert_eq!(self.mode, other)
429 }
430
431 #[inline]
432 fn check_debug(&self, other: DebugWritingMode) {
433 assert_eq!(self.mode, other.mode)
434 }
435
436 #[inline]
437 fn new(mode: WritingMode) -> DebugWritingMode {
438 DebugWritingMode { mode }
439 }
440}
441
442impl Debug for DebugWritingMode {
443 #[cfg(not(debug_assertions))]
444 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
445 write!(formatter, "?")
446 }
447
448 #[cfg(debug_assertions)]
449 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
450 write!(formatter, "{}", self.mode)
451 }
452}
453
454#[derive(Clone, Copy, Debug, PartialEq)]
456#[cfg_attr(feature = "servo", derive(Serialize))]
457pub enum Direction {
458 Inline,
459 Block,
460}
461
462#[derive(Clone, Copy, Eq, PartialEq)]
464#[cfg_attr(feature = "servo", derive(Serialize))]
465pub struct LogicalSize<T> {
466 pub inline: T, pub block: T, debug_writing_mode: DebugWritingMode,
469}
470
471impl<T: Debug> Debug for LogicalSize<T> {
472 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
473 write!(
474 formatter,
475 "LogicalSize({:?}, i{:?}×b{:?})",
476 self.debug_writing_mode, self.inline, self.block
477 )
478 }
479}
480
481impl<T: Zero> LogicalSize<T> {
483 #[inline]
484 pub fn zero(mode: WritingMode) -> LogicalSize<T> {
485 LogicalSize {
486 inline: Zero::zero(),
487 block: Zero::zero(),
488 debug_writing_mode: DebugWritingMode::new(mode),
489 }
490 }
491}
492
493impl<T> LogicalSize<T> {
494 #[inline]
495 pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T> {
496 LogicalSize {
497 inline: inline,
498 block: block,
499 debug_writing_mode: DebugWritingMode::new(mode),
500 }
501 }
502
503 #[inline]
504 pub fn from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T> {
505 if mode.is_vertical() {
506 LogicalSize::new(mode, size.height, size.width)
507 } else {
508 LogicalSize::new(mode, size.width, size.height)
509 }
510 }
511}
512
513impl<T: Clone> LogicalSize<T> {
514 #[inline]
515 pub fn width(&self, mode: WritingMode) -> T {
516 self.debug_writing_mode.check(mode);
517 if mode.is_vertical() {
518 self.block.clone()
519 } else {
520 self.inline.clone()
521 }
522 }
523
524 #[inline]
525 pub fn set_width(&mut self, mode: WritingMode, width: T) {
526 self.debug_writing_mode.check(mode);
527 if mode.is_vertical() {
528 self.block = width
529 } else {
530 self.inline = width
531 }
532 }
533
534 #[inline]
535 pub fn height(&self, mode: WritingMode) -> T {
536 self.debug_writing_mode.check(mode);
537 if mode.is_vertical() {
538 self.inline.clone()
539 } else {
540 self.block.clone()
541 }
542 }
543
544 #[inline]
545 pub fn set_height(&mut self, mode: WritingMode, height: T) {
546 self.debug_writing_mode.check(mode);
547 if mode.is_vertical() {
548 self.inline = height
549 } else {
550 self.block = height
551 }
552 }
553
554 #[inline]
555 pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> {
556 self.debug_writing_mode.check(mode);
557 if mode.is_vertical() {
558 Size2D::new(self.block.clone(), self.inline.clone())
559 } else {
560 Size2D::new(self.inline.clone(), self.block.clone())
561 }
562 }
563
564 #[inline]
565 pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T> {
566 if mode_from == mode_to {
567 self.debug_writing_mode.check(mode_from);
568 self.clone()
569 } else {
570 LogicalSize::from_physical(mode_to, self.to_physical(mode_from))
571 }
572 }
573}
574
575impl<T: Add<T, Output = T>> Add for LogicalSize<T> {
576 type Output = LogicalSize<T>;
577
578 #[inline]
579 fn add(self, other: LogicalSize<T>) -> LogicalSize<T> {
580 self.debug_writing_mode
581 .check_debug(other.debug_writing_mode);
582 LogicalSize {
583 debug_writing_mode: self.debug_writing_mode,
584 inline: self.inline + other.inline,
585 block: self.block + other.block,
586 }
587 }
588}
589
590impl<T: Sub<T, Output = T>> Sub for LogicalSize<T> {
591 type Output = LogicalSize<T>;
592
593 #[inline]
594 fn sub(self, other: LogicalSize<T>) -> LogicalSize<T> {
595 self.debug_writing_mode
596 .check_debug(other.debug_writing_mode);
597 LogicalSize {
598 debug_writing_mode: self.debug_writing_mode,
599 inline: self.inline - other.inline,
600 block: self.block - other.block,
601 }
602 }
603}
604
605#[derive(Clone, Copy, Eq, PartialEq)]
607#[cfg_attr(feature = "servo", derive(Serialize))]
608pub struct LogicalPoint<T> {
609 pub i: T,
611 pub b: T,
613 debug_writing_mode: DebugWritingMode,
614}
615
616impl<T: Debug> Debug for LogicalPoint<T> {
617 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
618 write!(
619 formatter,
620 "LogicalPoint({:?} (i{:?}, b{:?}))",
621 self.debug_writing_mode, self.i, self.b
622 )
623 }
624}
625
626impl<T: Zero> LogicalPoint<T> {
628 #[inline]
629 pub fn zero(mode: WritingMode) -> LogicalPoint<T> {
630 LogicalPoint {
631 i: Zero::zero(),
632 b: Zero::zero(),
633 debug_writing_mode: DebugWritingMode::new(mode),
634 }
635 }
636}
637
638impl<T: Copy> LogicalPoint<T> {
639 #[inline]
640 pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T> {
641 LogicalPoint {
642 i: i,
643 b: b,
644 debug_writing_mode: DebugWritingMode::new(mode),
645 }
646 }
647}
648
649impl<T: Copy + Sub<T, Output = T>> LogicalPoint<T> {
650 #[inline]
651 pub fn from_physical(
652 mode: WritingMode,
653 point: Point2D<T>,
654 container_size: Size2D<T>,
655 ) -> LogicalPoint<T> {
656 if mode.is_vertical() {
657 LogicalPoint {
658 i: if mode.is_inline_tb() {
659 point.y
660 } else {
661 container_size.height - point.y
662 },
663 b: if mode.is_vertical_lr() {
664 point.x
665 } else {
666 container_size.width - point.x
667 },
668 debug_writing_mode: DebugWritingMode::new(mode),
669 }
670 } else {
671 LogicalPoint {
672 i: if mode.is_bidi_ltr() {
673 point.x
674 } else {
675 container_size.width - point.x
676 },
677 b: point.y,
678 debug_writing_mode: DebugWritingMode::new(mode),
679 }
680 }
681 }
682
683 #[inline]
684 pub fn x(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
685 self.debug_writing_mode.check(mode);
686 if mode.is_vertical() {
687 if mode.is_vertical_lr() {
688 self.b
689 } else {
690 container_size.width - self.b
691 }
692 } else {
693 if mode.is_bidi_ltr() {
694 self.i
695 } else {
696 container_size.width - self.i
697 }
698 }
699 }
700
701 #[inline]
702 pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>) {
703 self.debug_writing_mode.check(mode);
704 if mode.is_vertical() {
705 self.b = if mode.is_vertical_lr() {
706 x
707 } else {
708 container_size.width - x
709 }
710 } else {
711 self.i = if mode.is_bidi_ltr() {
712 x
713 } else {
714 container_size.width - x
715 }
716 }
717 }
718
719 #[inline]
720 pub fn y(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
721 self.debug_writing_mode.check(mode);
722 if mode.is_vertical() {
723 if mode.is_inline_tb() {
724 self.i
725 } else {
726 container_size.height - self.i
727 }
728 } else {
729 self.b
730 }
731 }
732
733 #[inline]
734 pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>) {
735 self.debug_writing_mode.check(mode);
736 if mode.is_vertical() {
737 self.i = if mode.is_inline_tb() {
738 y
739 } else {
740 container_size.height - y
741 }
742 } else {
743 self.b = y
744 }
745 }
746
747 #[inline]
748 pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> {
749 self.debug_writing_mode.check(mode);
750 if mode.is_vertical() {
751 Point2D::new(
752 if mode.is_vertical_lr() {
753 self.b
754 } else {
755 container_size.width - self.b
756 },
757 if mode.is_inline_tb() {
758 self.i
759 } else {
760 container_size.height - self.i
761 },
762 )
763 } else {
764 Point2D::new(
765 if mode.is_bidi_ltr() {
766 self.i
767 } else {
768 container_size.width - self.i
769 },
770 self.b,
771 )
772 }
773 }
774
775 #[inline]
776 pub fn convert(
777 &self,
778 mode_from: WritingMode,
779 mode_to: WritingMode,
780 container_size: Size2D<T>,
781 ) -> LogicalPoint<T> {
782 if mode_from == mode_to {
783 self.debug_writing_mode.check(mode_from);
784 *self
785 } else {
786 LogicalPoint::from_physical(
787 mode_to,
788 self.to_physical(mode_from, container_size),
789 container_size,
790 )
791 }
792 }
793}
794
795impl<T: Copy + Add<T, Output = T>> LogicalPoint<T> {
796 #[inline]
799 pub fn add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T> {
800 self.debug_writing_mode
801 .check_debug(other.debug_writing_mode);
802 LogicalPoint {
803 debug_writing_mode: self.debug_writing_mode,
804 i: self.i + other.i,
805 b: self.b + other.b,
806 }
807 }
808}
809
810impl<T: Copy + Add<T, Output = T>> Add<LogicalSize<T>> for LogicalPoint<T> {
811 type Output = LogicalPoint<T>;
812
813 #[inline]
814 fn add(self, other: LogicalSize<T>) -> LogicalPoint<T> {
815 self.debug_writing_mode
816 .check_debug(other.debug_writing_mode);
817 LogicalPoint {
818 debug_writing_mode: self.debug_writing_mode,
819 i: self.i + other.inline,
820 b: self.b + other.block,
821 }
822 }
823}
824
825impl<T: Copy + Sub<T, Output = T>> Sub<LogicalSize<T>> for LogicalPoint<T> {
826 type Output = LogicalPoint<T>;
827
828 #[inline]
829 fn sub(self, other: LogicalSize<T>) -> LogicalPoint<T> {
830 self.debug_writing_mode
831 .check_debug(other.debug_writing_mode);
832 LogicalPoint {
833 debug_writing_mode: self.debug_writing_mode,
834 i: self.i - other.inline,
835 b: self.b - other.block,
836 }
837 }
838}
839
840#[derive(Clone, Copy, Eq, PartialEq)]
845#[cfg_attr(feature = "servo", derive(Serialize))]
846pub struct LogicalMargin<T> {
847 pub block_start: T,
848 pub inline_end: T,
849 pub block_end: T,
850 pub inline_start: T,
851 debug_writing_mode: DebugWritingMode,
852}
853
854impl<T: Debug> Debug for LogicalMargin<T> {
855 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
856 let writing_mode_string = if cfg!(debug_assertions) {
857 format!("{:?}, ", self.debug_writing_mode)
858 } else {
859 "".to_owned()
860 };
861
862 write!(
863 formatter,
864 "LogicalMargin({}i:{:?}..{:?} b:{:?}..{:?})",
865 writing_mode_string,
866 self.inline_start,
867 self.inline_end,
868 self.block_start,
869 self.block_end
870 )
871 }
872}
873
874impl<T: Zero> LogicalMargin<T> {
875 #[inline]
876 pub fn zero(mode: WritingMode) -> LogicalMargin<T> {
877 LogicalMargin {
878 block_start: Zero::zero(),
879 inline_end: Zero::zero(),
880 block_end: Zero::zero(),
881 inline_start: Zero::zero(),
882 debug_writing_mode: DebugWritingMode::new(mode),
883 }
884 }
885}
886
887impl<T> LogicalMargin<T> {
888 #[inline]
889 pub fn new(
890 mode: WritingMode,
891 block_start: T,
892 inline_end: T,
893 block_end: T,
894 inline_start: T,
895 ) -> LogicalMargin<T> {
896 LogicalMargin {
897 block_start,
898 inline_end,
899 block_end,
900 inline_start,
901 debug_writing_mode: DebugWritingMode::new(mode),
902 }
903 }
904
905 #[inline]
906 pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> {
907 let block_start;
908 let inline_end;
909 let block_end;
910 let inline_start;
911 if mode.is_vertical() {
912 if mode.is_vertical_lr() {
913 block_start = offsets.left;
914 block_end = offsets.right;
915 } else {
916 block_start = offsets.right;
917 block_end = offsets.left;
918 }
919 if mode.is_inline_tb() {
920 inline_start = offsets.top;
921 inline_end = offsets.bottom;
922 } else {
923 inline_start = offsets.bottom;
924 inline_end = offsets.top;
925 }
926 } else {
927 block_start = offsets.top;
928 block_end = offsets.bottom;
929 if mode.is_bidi_ltr() {
930 inline_start = offsets.left;
931 inline_end = offsets.right;
932 } else {
933 inline_start = offsets.right;
934 inline_end = offsets.left;
935 }
936 }
937 LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start)
938 }
939}
940
941impl<T: Clone> LogicalMargin<T> {
942 #[inline]
943 pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T> {
944 LogicalMargin::new(mode, value.clone(), value.clone(), value.clone(), value)
945 }
946
947 #[inline]
948 pub fn top(&self, mode: WritingMode) -> T {
949 self.debug_writing_mode.check(mode);
950 if mode.is_vertical() {
951 if mode.is_inline_tb() {
952 self.inline_start.clone()
953 } else {
954 self.inline_end.clone()
955 }
956 } else {
957 self.block_start.clone()
958 }
959 }
960
961 #[inline]
962 pub fn set_top(&mut self, mode: WritingMode, top: T) {
963 self.debug_writing_mode.check(mode);
964 if mode.is_vertical() {
965 if mode.is_inline_tb() {
966 self.inline_start = top
967 } else {
968 self.inline_end = top
969 }
970 } else {
971 self.block_start = top
972 }
973 }
974
975 #[inline]
976 pub fn right(&self, mode: WritingMode) -> T {
977 self.debug_writing_mode.check(mode);
978 if mode.is_vertical() {
979 if mode.is_vertical_lr() {
980 self.block_end.clone()
981 } else {
982 self.block_start.clone()
983 }
984 } else {
985 if mode.is_bidi_ltr() {
986 self.inline_end.clone()
987 } else {
988 self.inline_start.clone()
989 }
990 }
991 }
992
993 #[inline]
994 pub fn set_right(&mut self, mode: WritingMode, right: T) {
995 self.debug_writing_mode.check(mode);
996 if mode.is_vertical() {
997 if mode.is_vertical_lr() {
998 self.block_end = right
999 } else {
1000 self.block_start = right
1001 }
1002 } else {
1003 if mode.is_bidi_ltr() {
1004 self.inline_end = right
1005 } else {
1006 self.inline_start = right
1007 }
1008 }
1009 }
1010
1011 #[inline]
1012 pub fn bottom(&self, mode: WritingMode) -> T {
1013 self.debug_writing_mode.check(mode);
1014 if mode.is_vertical() {
1015 if mode.is_inline_tb() {
1016 self.inline_end.clone()
1017 } else {
1018 self.inline_start.clone()
1019 }
1020 } else {
1021 self.block_end.clone()
1022 }
1023 }
1024
1025 #[inline]
1026 pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {
1027 self.debug_writing_mode.check(mode);
1028 if mode.is_vertical() {
1029 if mode.is_inline_tb() {
1030 self.inline_end = bottom
1031 } else {
1032 self.inline_start = bottom
1033 }
1034 } else {
1035 self.block_end = bottom
1036 }
1037 }
1038
1039 #[inline]
1040 pub fn left(&self, mode: WritingMode) -> T {
1041 self.debug_writing_mode.check(mode);
1042 if mode.is_vertical() {
1043 if mode.is_vertical_lr() {
1044 self.block_start.clone()
1045 } else {
1046 self.block_end.clone()
1047 }
1048 } else {
1049 if mode.is_bidi_ltr() {
1050 self.inline_start.clone()
1051 } else {
1052 self.inline_end.clone()
1053 }
1054 }
1055 }
1056
1057 #[inline]
1058 pub fn set_left(&mut self, mode: WritingMode, left: T) {
1059 self.debug_writing_mode.check(mode);
1060 if mode.is_vertical() {
1061 if mode.is_vertical_lr() {
1062 self.block_start = left
1063 } else {
1064 self.block_end = left
1065 }
1066 } else {
1067 if mode.is_bidi_ltr() {
1068 self.inline_start = left
1069 } else {
1070 self.inline_end = left
1071 }
1072 }
1073 }
1074
1075 #[inline]
1076 pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> {
1077 self.debug_writing_mode.check(mode);
1078 let top;
1079 let right;
1080 let bottom;
1081 let left;
1082 if mode.is_vertical() {
1083 if mode.is_vertical_lr() {
1084 left = self.block_start.clone();
1085 right = self.block_end.clone();
1086 } else {
1087 right = self.block_start.clone();
1088 left = self.block_end.clone();
1089 }
1090 if mode.is_inline_tb() {
1091 top = self.inline_start.clone();
1092 bottom = self.inline_end.clone();
1093 } else {
1094 bottom = self.inline_start.clone();
1095 top = self.inline_end.clone();
1096 }
1097 } else {
1098 top = self.block_start.clone();
1099 bottom = self.block_end.clone();
1100 if mode.is_bidi_ltr() {
1101 left = self.inline_start.clone();
1102 right = self.inline_end.clone();
1103 } else {
1104 right = self.inline_start.clone();
1105 left = self.inline_end.clone();
1106 }
1107 }
1108 SideOffsets2D::new(top, right, bottom, left)
1109 }
1110
1111 #[inline]
1112 pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> {
1113 if mode_from == mode_to {
1114 self.debug_writing_mode.check(mode_from);
1115 self.clone()
1116 } else {
1117 LogicalMargin::from_physical(mode_to, self.to_physical(mode_from))
1118 }
1119 }
1120}
1121
1122impl<T: PartialEq + Zero> LogicalMargin<T> {
1123 #[inline]
1124 pub fn is_zero(&self) -> bool {
1125 self.block_start == Zero::zero()
1126 && self.inline_end == Zero::zero()
1127 && self.block_end == Zero::zero()
1128 && self.inline_start == Zero::zero()
1129 }
1130}
1131
1132impl<T: Copy + Add<T, Output = T>> LogicalMargin<T> {
1133 #[inline]
1134 pub fn inline_start_end(&self) -> T {
1135 self.inline_start + self.inline_end
1136 }
1137
1138 #[inline]
1139 pub fn block_start_end(&self) -> T {
1140 self.block_start + self.block_end
1141 }
1142
1143 #[inline]
1144 pub fn start_end(&self, direction: Direction) -> T {
1145 match direction {
1146 Direction::Inline => self.inline_start + self.inline_end,
1147 Direction::Block => self.block_start + self.block_end,
1148 }
1149 }
1150
1151 #[inline]
1152 pub fn top_bottom(&self, mode: WritingMode) -> T {
1153 self.debug_writing_mode.check(mode);
1154 if mode.is_vertical() {
1155 self.inline_start_end()
1156 } else {
1157 self.block_start_end()
1158 }
1159 }
1160
1161 #[inline]
1162 pub fn left_right(&self, mode: WritingMode) -> T {
1163 self.debug_writing_mode.check(mode);
1164 if mode.is_vertical() {
1165 self.block_start_end()
1166 } else {
1167 self.inline_start_end()
1168 }
1169 }
1170}
1171
1172impl<T: Add<T, Output = T>> Add for LogicalMargin<T> {
1173 type Output = LogicalMargin<T>;
1174
1175 #[inline]
1176 fn add(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1177 self.debug_writing_mode
1178 .check_debug(other.debug_writing_mode);
1179 LogicalMargin {
1180 debug_writing_mode: self.debug_writing_mode,
1181 block_start: self.block_start + other.block_start,
1182 inline_end: self.inline_end + other.inline_end,
1183 block_end: self.block_end + other.block_end,
1184 inline_start: self.inline_start + other.inline_start,
1185 }
1186 }
1187}
1188
1189impl<T: Sub<T, Output = T>> Sub for LogicalMargin<T> {
1190 type Output = LogicalMargin<T>;
1191
1192 #[inline]
1193 fn sub(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1194 self.debug_writing_mode
1195 .check_debug(other.debug_writing_mode);
1196 LogicalMargin {
1197 debug_writing_mode: self.debug_writing_mode,
1198 block_start: self.block_start - other.block_start,
1199 inline_end: self.inline_end - other.inline_end,
1200 block_end: self.block_end - other.block_end,
1201 inline_start: self.inline_start - other.inline_start,
1202 }
1203 }
1204}
1205
1206#[derive(Clone, Copy, Eq, PartialEq)]
1208#[cfg_attr(feature = "servo", derive(Serialize))]
1209pub struct LogicalRect<T> {
1210 pub start: LogicalPoint<T>,
1211 pub size: LogicalSize<T>,
1212 debug_writing_mode: DebugWritingMode,
1213}
1214
1215impl<T: Debug> Debug for LogicalRect<T> {
1216 fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
1217 let writing_mode_string = if cfg!(debug_assertions) {
1218 format!("{:?}, ", self.debug_writing_mode)
1219 } else {
1220 "".to_owned()
1221 };
1222
1223 write!(
1224 formatter,
1225 "LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))",
1226 writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b
1227 )
1228 }
1229}
1230
1231impl<T: Zero> LogicalRect<T> {
1232 #[inline]
1233 pub fn zero(mode: WritingMode) -> LogicalRect<T> {
1234 LogicalRect {
1235 start: LogicalPoint::zero(mode),
1236 size: LogicalSize::zero(mode),
1237 debug_writing_mode: DebugWritingMode::new(mode),
1238 }
1239 }
1240}
1241
1242impl<T: Copy> LogicalRect<T> {
1243 #[inline]
1244 pub fn new(
1245 mode: WritingMode,
1246 inline_start: T,
1247 block_start: T,
1248 inline: T,
1249 block: T,
1250 ) -> LogicalRect<T> {
1251 LogicalRect {
1252 start: LogicalPoint::new(mode, inline_start, block_start),
1253 size: LogicalSize::new(mode, inline, block),
1254 debug_writing_mode: DebugWritingMode::new(mode),
1255 }
1256 }
1257
1258 #[inline]
1259 pub fn from_point_size(
1260 mode: WritingMode,
1261 start: LogicalPoint<T>,
1262 size: LogicalSize<T>,
1263 ) -> LogicalRect<T> {
1264 start.debug_writing_mode.check(mode);
1265 size.debug_writing_mode.check(mode);
1266 LogicalRect {
1267 start: start,
1268 size: size,
1269 debug_writing_mode: DebugWritingMode::new(mode),
1270 }
1271 }
1272}
1273
1274impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1275 #[inline]
1276 pub fn from_physical(
1277 mode: WritingMode,
1278 rect: Rect<T>,
1279 container_size: Size2D<T>,
1280 ) -> LogicalRect<T> {
1281 let inline_start;
1282 let block_start;
1283 let inline;
1284 let block;
1285 if mode.is_vertical() {
1286 inline = rect.size.height;
1287 block = rect.size.width;
1288 if mode.is_vertical_lr() {
1289 block_start = rect.origin.x;
1290 } else {
1291 block_start = container_size.width - (rect.origin.x + rect.size.width);
1292 }
1293 if mode.is_inline_tb() {
1294 inline_start = rect.origin.y;
1295 } else {
1296 inline_start = container_size.height - (rect.origin.y + rect.size.height);
1297 }
1298 } else {
1299 inline = rect.size.width;
1300 block = rect.size.height;
1301 block_start = rect.origin.y;
1302 if mode.is_bidi_ltr() {
1303 inline_start = rect.origin.x;
1304 } else {
1305 inline_start = container_size.width - (rect.origin.x + rect.size.width);
1306 }
1307 }
1308 LogicalRect {
1309 start: LogicalPoint::new(mode, inline_start, block_start),
1310 size: LogicalSize::new(mode, inline, block),
1311 debug_writing_mode: DebugWritingMode::new(mode),
1312 }
1313 }
1314
1315 #[inline]
1316 pub fn inline_end(&self) -> T {
1317 self.start.i + self.size.inline
1318 }
1319
1320 #[inline]
1321 pub fn block_end(&self) -> T {
1322 self.start.b + self.size.block
1323 }
1324
1325 #[inline]
1326 pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> {
1327 self.debug_writing_mode.check(mode);
1328 let x;
1329 let y;
1330 let width;
1331 let height;
1332 if mode.is_vertical() {
1333 width = self.size.block;
1334 height = self.size.inline;
1335 if mode.is_vertical_lr() {
1336 x = self.start.b;
1337 } else {
1338 x = container_size.width - self.block_end();
1339 }
1340 if mode.is_inline_tb() {
1341 y = self.start.i;
1342 } else {
1343 y = container_size.height - self.inline_end();
1344 }
1345 } else {
1346 width = self.size.inline;
1347 height = self.size.block;
1348 y = self.start.b;
1349 if mode.is_bidi_ltr() {
1350 x = self.start.i;
1351 } else {
1352 x = container_size.width - self.inline_end();
1353 }
1354 }
1355 Rect {
1356 origin: Point2D::new(x, y),
1357 size: Size2D::new(width, height),
1358 }
1359 }
1360
1361 #[inline]
1362 pub fn convert(
1363 &self,
1364 mode_from: WritingMode,
1365 mode_to: WritingMode,
1366 container_size: Size2D<T>,
1367 ) -> LogicalRect<T> {
1368 if mode_from == mode_to {
1369 self.debug_writing_mode.check(mode_from);
1370 *self
1371 } else {
1372 LogicalRect::from_physical(
1373 mode_to,
1374 self.to_physical(mode_from, container_size),
1375 container_size,
1376 )
1377 }
1378 }
1379
1380 pub fn translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T> {
1381 LogicalRect {
1382 start: self.start + offset,
1383 ..*self
1384 }
1385 }
1386
1387 pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> {
1388 LogicalRect {
1389 start: self.start
1390 + LogicalSize {
1391 inline: offset.i,
1392 block: offset.b,
1393 debug_writing_mode: offset.debug_writing_mode,
1394 },
1395 size: self.size,
1396 debug_writing_mode: self.debug_writing_mode,
1397 }
1398 }
1399}
1400
1401impl<T: Copy + Ord + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1402 #[inline]
1403 pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> {
1404 self.debug_writing_mode
1405 .check_debug(other.debug_writing_mode);
1406
1407 let inline_start = min(self.start.i, other.start.i);
1408 let block_start = min(self.start.b, other.start.b);
1409 LogicalRect {
1410 start: LogicalPoint {
1411 i: inline_start,
1412 b: block_start,
1413 debug_writing_mode: self.debug_writing_mode,
1414 },
1415 size: LogicalSize {
1416 inline: max(self.inline_end(), other.inline_end()) - inline_start,
1417 block: max(self.block_end(), other.block_end()) - block_start,
1418 debug_writing_mode: self.debug_writing_mode,
1419 },
1420 debug_writing_mode: self.debug_writing_mode,
1421 }
1422 }
1423}
1424
1425impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Add<LogicalMargin<T>> for LogicalRect<T> {
1426 type Output = LogicalRect<T>;
1427
1428 #[inline]
1429 fn add(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1430 self.debug_writing_mode
1431 .check_debug(other.debug_writing_mode);
1432 LogicalRect {
1433 start: LogicalPoint {
1434 i: self.start.i - other.inline_start,
1437 b: self.start.b - other.block_start,
1438 debug_writing_mode: self.debug_writing_mode,
1439 },
1440 size: LogicalSize {
1441 inline: self.size.inline + other.inline_start_end(),
1442 block: self.size.block + other.block_start_end(),
1443 debug_writing_mode: self.debug_writing_mode,
1444 },
1445 debug_writing_mode: self.debug_writing_mode,
1446 }
1447 }
1448}
1449
1450impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Sub<LogicalMargin<T>> for LogicalRect<T> {
1451 type Output = LogicalRect<T>;
1452
1453 #[inline]
1454 fn sub(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1455 self.debug_writing_mode
1456 .check_debug(other.debug_writing_mode);
1457 LogicalRect {
1458 start: LogicalPoint {
1459 i: self.start.i + other.inline_start,
1462 b: self.start.b + other.block_start,
1463 debug_writing_mode: self.debug_writing_mode,
1464 },
1465 size: LogicalSize {
1466 inline: self.size.inline - other.inline_start_end(),
1467 block: self.size.block - other.block_start_end(),
1468 debug_writing_mode: self.debug_writing_mode,
1469 },
1470 debug_writing_mode: self.debug_writing_mode,
1471 }
1472 }
1473}
1474
1475#[derive(Clone, Copy, Debug, PartialEq)]
1476#[repr(u8)]
1477pub enum LogicalAxis {
1478 Block = 0,
1479 Inline,
1480}
1481
1482impl LogicalAxis {
1483 #[inline]
1484 pub fn to_physical(self, wm: WritingMode) -> PhysicalAxis {
1485 if wm.is_horizontal() == (self == Self::Inline) {
1486 PhysicalAxis::Horizontal
1487 } else {
1488 PhysicalAxis::Vertical
1489 }
1490 }
1491}
1492
1493#[derive(Clone, Copy, Debug, PartialEq)]
1494#[repr(u8)]
1495pub enum LogicalSide {
1496 BlockStart = 0,
1497 BlockEnd,
1498 InlineStart,
1499 InlineEnd,
1500}
1501
1502impl LogicalSide {
1503 fn is_block(self) -> bool {
1504 matches!(self, Self::BlockStart | Self::BlockEnd)
1505 }
1506
1507 #[inline]
1508 pub fn to_physical(self, wm: WritingMode) -> PhysicalSide {
1509 static BLOCK_MAPPING: [[PhysicalSide; 2]; 4] = [
1511 [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Bottom, PhysicalSide::Top], [PhysicalSide::Left, PhysicalSide::Right], ];
1516
1517 if self.is_block() {
1518 let vertical = wm.is_vertical();
1519 let lr = wm.is_vertical_lr();
1520 let index = (vertical as usize) | ((lr as usize) << 1);
1521 return BLOCK_MAPPING[index][self as usize];
1522 }
1523
1524 let edge = self as usize - 2;
1526 static INLINE_MAPPING: [[PhysicalSide; 2]; 16] = [
1539 [PhysicalSide::Left, PhysicalSide::Right], [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Bottom, PhysicalSide::Top], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Left, PhysicalSide::Right], [PhysicalSide::Bottom, PhysicalSide::Top], [PhysicalSide::Left, PhysicalSide::Right], [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Bottom, PhysicalSide::Top], [PhysicalSide::Left, PhysicalSide::Right], [PhysicalSide::Top, PhysicalSide::Bottom], [PhysicalSide::Right, PhysicalSide::Left], [PhysicalSide::Bottom, PhysicalSide::Top], ];
1556
1557 debug_assert!(
1558 WritingMode::VERTICAL.bits() == 0x01
1559 && WritingMode::INLINE_REVERSED.bits() == 0x02
1560 && WritingMode::VERTICAL_LR.bits() == 0x04
1561 && WritingMode::LINE_INVERTED.bits() == 0x08
1562 );
1563 let index = (wm.bits() & 0xF) as usize;
1564 INLINE_MAPPING[index][edge]
1565 }
1566}
1567
1568#[derive(Clone, Copy, Debug, PartialEq)]
1569#[repr(u8)]
1570pub enum LogicalCorner {
1571 StartStart = 0,
1572 StartEnd,
1573 EndStart,
1574 EndEnd,
1575}
1576
1577impl LogicalCorner {
1578 #[inline]
1579 pub fn to_physical(self, wm: WritingMode) -> PhysicalCorner {
1580 static CORNER_TO_SIDES: [[LogicalSide; 2]; 4] = [
1581 [LogicalSide::BlockStart, LogicalSide::InlineStart],
1582 [LogicalSide::BlockStart, LogicalSide::InlineEnd],
1583 [LogicalSide::BlockEnd, LogicalSide::InlineStart],
1584 [LogicalSide::BlockEnd, LogicalSide::InlineEnd],
1585 ];
1586
1587 let [block, inline] = CORNER_TO_SIDES[self as usize];
1588 let block = block.to_physical(wm);
1589 let inline = inline.to_physical(wm);
1590 PhysicalCorner::from_sides(block, inline)
1591 }
1592}
1593
1594#[derive(Clone, Copy, Debug, PartialEq)]
1595#[repr(u8)]
1596pub enum PhysicalAxis {
1597 Vertical = 0,
1598 Horizontal,
1599}
1600
1601#[derive(Clone, Copy, Debug, PartialEq)]
1602#[repr(u8)]
1603pub enum PhysicalSide {
1604 Top = 0,
1605 Right,
1606 Bottom,
1607 Left,
1608}
1609
1610impl PhysicalSide {
1611 fn orthogonal_to(self, other: Self) -> bool {
1612 matches!(self, Self::Top | Self::Bottom) != matches!(other, Self::Top | Self::Bottom)
1613 }
1614}
1615
1616#[derive(Clone, Copy, Debug, PartialEq)]
1617#[repr(u8)]
1618pub enum PhysicalCorner {
1619 TopLeft = 0,
1620 TopRight,
1621 BottomRight,
1622 BottomLeft,
1623}
1624
1625impl PhysicalCorner {
1626 fn from_sides(a: PhysicalSide, b: PhysicalSide) -> Self {
1627 debug_assert!(a.orthogonal_to(b), "Sides should be orthogonal");
1628 const IMPOSSIBLE: PhysicalCorner = PhysicalCorner::TopLeft;
1631 static SIDES_TO_CORNER: [[PhysicalCorner; 4]; 4] = [
1632 [
1633 IMPOSSIBLE,
1634 PhysicalCorner::TopRight,
1635 IMPOSSIBLE,
1636 PhysicalCorner::TopLeft,
1637 ],
1638 [
1639 PhysicalCorner::TopRight,
1640 IMPOSSIBLE,
1641 PhysicalCorner::BottomRight,
1642 IMPOSSIBLE,
1643 ],
1644 [
1645 IMPOSSIBLE,
1646 PhysicalCorner::BottomRight,
1647 IMPOSSIBLE,
1648 PhysicalCorner::BottomLeft,
1649 ],
1650 [
1651 PhysicalCorner::TopLeft,
1652 IMPOSSIBLE,
1653 PhysicalCorner::BottomLeft,
1654 IMPOSSIBLE,
1655 ],
1656 ];
1657 SIDES_TO_CORNER[a as usize][b as usize]
1658 }
1659}