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