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