1use std::convert::From;
6use std::fmt;
7use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
8
9use app_units::Au;
10use malloc_size_of_derive::MallocSizeOf;
11use style::Zero;
12use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode};
13use style::values::computed::{CSSPixelLength, LengthPercentage};
14use style::values::generics::length::GenericLengthPercentageOrAuto as AutoOr;
15use style_traits::CSSPixel;
16
17use crate::ContainingBlock;
18use crate::sizing::Size;
19
20pub type PhysicalPoint<U> = euclid::Point2D<U, CSSPixel>;
21pub type PhysicalSize<U> = euclid::Size2D<U, CSSPixel>;
22pub type PhysicalVec<U> = euclid::Vector2D<U, CSSPixel>;
23pub type PhysicalRect<U> = euclid::Rect<U, CSSPixel>;
24pub type PhysicalSides<U> = euclid::SideOffsets2D<U, CSSPixel>;
25pub type AuOrAuto = AutoOr<Au>;
26pub type LengthPercentageOrAuto<'a> = AutoOr<&'a LengthPercentage>;
27
28#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
29pub struct LogicalVec2<T> {
30 pub inline: T,
31 pub block: T,
32}
33
34#[derive(Clone, Copy)]
35pub struct LogicalRect<T> {
36 pub start_corner: LogicalVec2<T>,
37 pub size: LogicalVec2<T>,
38}
39
40#[derive(Clone, Copy, Debug, Default)]
41pub struct LogicalSides<T> {
42 pub inline_start: T,
43 pub inline_end: T,
44 pub block_start: T,
45 pub block_end: T,
46}
47
48#[derive(Clone, Copy, Debug)]
49pub(crate) struct LogicalSides1D<T> {
50 pub start: T,
51 pub end: T,
52}
53
54impl<T> LogicalSides1D<T> {
55 pub(crate) fn map<U>(&self, f: impl Fn(&T) -> U) -> LogicalSides1D<U> {
56 LogicalSides1D {
57 start: f(&self.start),
58 end: f(&self.end),
59 }
60 }
61}
62
63impl LogicalSides1D<AutoOr<&LengthPercentage>> {
64 pub(crate) fn percentages_relative_to(&self, basis: Au) -> LogicalSides1D<AutoOr<Au>> {
65 self.map(|value| value.map(|length_percentage| length_percentage.to_used_value(basis)))
66 }
67}
68
69impl<T: fmt::Debug> fmt::Debug for LogicalVec2<T> {
70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 f.write_str("Vec2 { i: ")?;
73 self.inline.fmt(f)?;
74 f.write_str(", b: ")?;
75 self.block.fmt(f)?;
76 f.write_str(" }")
77 }
78}
79
80impl<T: Default> Default for LogicalVec2<T> {
81 fn default() -> Self {
82 Self {
83 inline: T::default(),
84 block: T::default(),
85 }
86 }
87}
88
89impl<T: Copy> From<T> for LogicalVec2<T> {
90 fn from(value: T) -> Self {
91 Self {
92 inline: value,
93 block: value,
94 }
95 }
96}
97
98impl<T> LogicalVec2<T> {
99 pub fn map_inline_and_block_axes<U>(
100 &self,
101 inline_f: impl FnOnce(&T) -> U,
102 block_f: impl FnOnce(&T) -> U,
103 ) -> LogicalVec2<U> {
104 LogicalVec2 {
105 inline: inline_f(&self.inline),
106 block: block_f(&self.block),
107 }
108 }
109}
110
111impl<T: Clone> LogicalVec2<Size<T>> {
112 pub fn map_inline_and_block_sizes<U>(
113 &self,
114 inline_f: impl FnOnce(T) -> U,
115 block_f: impl FnOnce(T) -> U,
116 ) -> LogicalVec2<Size<U>> {
117 self.map_inline_and_block_axes(|size| size.map(inline_f), |size| size.map(block_f))
118 }
119}
120
121impl<T: Clone> LogicalVec2<T> {
122 pub fn from_physical_size(physical_size: &PhysicalSize<T>, mode: WritingMode) -> Self {
123 let (i, b) = if mode.is_horizontal() {
125 (&physical_size.width, &physical_size.height)
126 } else {
127 (&physical_size.height, &physical_size.width)
128 };
129 LogicalVec2 {
130 inline: i.clone(),
131 block: b.clone(),
132 }
133 }
134
135 pub fn map<U>(&self, f: impl Fn(&T) -> U) -> LogicalVec2<U> {
136 LogicalVec2 {
137 inline: f(&self.inline),
138 block: f(&self.block),
139 }
140 }
141
142 pub(crate) fn map_with<U, V>(
143 &self,
144 other: &LogicalVec2<U>,
145 f: impl Fn(&T, &U) -> V,
146 ) -> LogicalVec2<V> {
147 LogicalVec2 {
148 inline: f(&self.inline, &other.inline),
149 block: f(&self.block, &other.block),
150 }
151 }
152}
153
154impl<T: Add<Output = T> + Copy> Add<LogicalVec2<T>> for LogicalVec2<T> {
155 type Output = LogicalVec2<T>;
156 fn add(self, other: Self) -> Self::Output {
157 LogicalVec2 {
158 inline: self.inline + other.inline,
159 block: self.block + other.block,
160 }
161 }
162}
163
164impl<T: Sub<Output = T> + Copy> Sub<LogicalVec2<T>> for LogicalVec2<T> {
165 type Output = LogicalVec2<T>;
166 fn sub(self, other: Self) -> Self::Output {
167 LogicalVec2 {
168 inline: self.inline - other.inline,
169 block: self.block - other.block,
170 }
171 }
172}
173
174impl<T: AddAssign<T> + Copy> AddAssign<LogicalVec2<T>> for LogicalVec2<T> {
175 fn add_assign(&mut self, other: LogicalVec2<T>) {
176 self.inline += other.inline;
177 self.block += other.block;
178 }
179}
180
181impl<T: SubAssign<T> + Copy> SubAssign<LogicalVec2<T>> for LogicalVec2<T> {
182 fn sub_assign(&mut self, other: LogicalVec2<T>) {
183 self.inline -= other.inline;
184 self.block -= other.block;
185 }
186}
187
188impl<T: Neg<Output = T> + Copy> Neg for LogicalVec2<T> {
189 type Output = LogicalVec2<T>;
190 fn neg(self) -> Self::Output {
191 Self {
192 inline: -self.inline,
193 block: -self.block,
194 }
195 }
196}
197
198impl<T: Zero> LogicalVec2<T> {
199 pub fn zero() -> Self {
200 Self {
201 inline: T::zero(),
202 block: T::zero(),
203 }
204 }
205}
206
207impl<T: Clone> LogicalVec2<AutoOr<T>> {
208 pub fn auto_is(&self, f: impl Fn() -> T) -> LogicalVec2<T> {
209 self.map(|t| t.auto_is(&f))
210 }
211}
212
213impl<T: Zero> LogicalRect<T> {
214 pub fn zero() -> Self {
215 Self {
216 start_corner: LogicalVec2::zero(),
217 size: LogicalVec2::zero(),
218 }
219 }
220}
221
222impl fmt::Debug for LogicalRect<Au> {
223 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224 write!(
225 f,
226 "Rect(i{}×b{} @ (i{},b{}))",
227 self.size.inline.to_f32_px(),
228 self.size.block.to_f32_px(),
229 self.start_corner.inline.to_f32_px(),
230 self.start_corner.block.to_f32_px(),
231 )
232 }
233}
234
235impl<T: Clone> LogicalVec2<T> {
236 pub fn to_physical_size(&self, mode: WritingMode) -> PhysicalSize<T> {
237 let (x, y) = if mode.is_horizontal() {
239 (&self.inline, &self.block)
240 } else {
241 (&self.block, &self.inline)
242 };
243 PhysicalSize::new(x.clone(), y.clone())
244 }
245}
246
247impl<T: Copy + Neg<Output = T>> LogicalVec2<T> {
248 pub fn to_physical_vector(&self, mode: WritingMode) -> PhysicalVec<T> {
249 if mode.is_horizontal() {
250 if mode.is_bidi_ltr() {
251 PhysicalVec::new(self.inline, self.block)
252 } else {
253 PhysicalVec::new(-self.inline, self.block)
254 }
255 } else if mode.is_inline_tb() {
256 PhysicalVec::new(self.block, self.inline)
257 } else {
258 PhysicalVec::new(-self.block, self.inline)
259 }
260 }
261}
262
263impl<T: Clone> LogicalSides<T> {
264 pub fn from_physical(sides: &PhysicalSides<T>, mode: WritingMode) -> Self {
265 let block_flow = mode.block_flow_direction();
267 let (bs, be) = match mode.block_flow_direction() {
268 BlockFlowDirection::TopToBottom => (&sides.top, &sides.bottom),
269 BlockFlowDirection::RightToLeft => (&sides.right, &sides.left),
270 BlockFlowDirection::LeftToRight => (&sides.left, &sides.right),
271 };
272 use BlockFlowDirection::TopToBottom;
273 let (is, ie) = match (block_flow, mode.inline_base_direction()) {
274 (TopToBottom, InlineBaseDirection::LeftToRight) => (&sides.left, &sides.right),
275 (TopToBottom, InlineBaseDirection::RightToLeft) => (&sides.right, &sides.left),
276 (_, InlineBaseDirection::LeftToRight) => (&sides.top, &sides.bottom),
277 (_, InlineBaseDirection::RightToLeft) => (&sides.bottom, &sides.top),
278 };
279 LogicalSides {
280 inline_start: is.clone(),
281 inline_end: ie.clone(),
282 block_start: bs.clone(),
283 block_end: be.clone(),
284 }
285 }
286}
287
288impl<T> LogicalSides<T> {
289 pub fn map<U>(&self, f: impl Fn(&T) -> U) -> LogicalSides<U> {
290 LogicalSides {
291 inline_start: f(&self.inline_start),
292 inline_end: f(&self.inline_end),
293 block_start: f(&self.block_start),
294 block_end: f(&self.block_end),
295 }
296 }
297
298 pub fn map_inline_and_block_axes<U>(
299 &self,
300 inline_f: impl Fn(&T) -> U,
301 block_f: impl Fn(&T) -> U,
302 ) -> LogicalSides<U> {
303 LogicalSides {
304 inline_start: inline_f(&self.inline_start),
305 inline_end: inline_f(&self.inline_end),
306 block_start: block_f(&self.block_start),
307 block_end: block_f(&self.block_end),
308 }
309 }
310
311 pub fn inline_sum(&self) -> T::Output
312 where
313 T: Add + Copy,
314 {
315 self.inline_start + self.inline_end
316 }
317
318 pub fn block_sum(&self) -> T::Output
319 where
320 T: Add + Copy,
321 {
322 self.block_start + self.block_end
323 }
324
325 pub fn sum(&self) -> LogicalVec2<T::Output>
326 where
327 T: Add + Copy,
328 {
329 LogicalVec2 {
330 inline: self.inline_sum(),
331 block: self.block_sum(),
332 }
333 }
334
335 pub fn to_physical(&self, mode: WritingMode) -> PhysicalSides<T>
336 where
337 T: Clone,
338 {
339 let top;
340 let right;
341 let bottom;
342 let left;
343 if mode.is_vertical() {
344 if mode.is_vertical_lr() {
345 left = self.block_start.clone();
346 right = self.block_end.clone();
347 } else {
348 right = self.block_start.clone();
349 left = self.block_end.clone();
350 }
351
352 if mode.is_inline_tb() {
353 top = self.inline_start.clone();
354 bottom = self.inline_end.clone();
355 } else {
356 bottom = self.inline_start.clone();
357 top = self.inline_end.clone();
358 }
359 } else {
360 top = self.block_start.clone();
361 bottom = self.block_end.clone();
362 if mode.is_bidi_ltr() {
363 left = self.inline_start.clone();
364 right = self.inline_end.clone();
365 } else {
366 right = self.inline_start.clone();
367 left = self.inline_end.clone();
368 }
369 }
370 PhysicalSides::new(top, right, bottom, left)
371 }
372}
373
374impl<T: Copy> LogicalSides<T> {
375 pub fn start_offset(&self) -> LogicalVec2<T> {
376 LogicalVec2 {
377 inline: self.inline_start,
378 block: self.block_start,
379 }
380 }
381
382 #[inline]
383 pub(crate) fn inline_sides(&self) -> LogicalSides1D<T> {
384 LogicalSides1D::new(self.inline_start, self.inline_end)
385 }
386
387 #[inline]
388 pub(crate) fn block_sides(&self) -> LogicalSides1D<T> {
389 LogicalSides1D::new(self.block_start, self.block_end)
390 }
391}
392
393impl LogicalSides<LengthPercentage> {
394 pub fn percentages_relative_to(&self, basis: Au) -> LogicalSides<Au> {
395 self.map(|value| value.to_used_value(basis))
396 }
397}
398
399impl LogicalSides<LengthPercentageOrAuto<'_>> {
400 pub fn percentages_relative_to(&self, basis: Au) -> LogicalSides<AuOrAuto> {
401 self.map(|value| value.map(|value| value.to_used_value(basis)))
402 }
403}
404
405impl<T: Clone> LogicalSides<AutoOr<T>> {
406 pub fn auto_is(&self, f: impl Fn() -> T) -> LogicalSides<T> {
407 self.map(|s| s.auto_is(&f))
408 }
409}
410
411impl<T: Add<Output = T> + Copy> Add<LogicalSides<T>> for LogicalSides<T> {
412 type Output = LogicalSides<T>;
413
414 fn add(self, other: Self) -> Self::Output {
415 LogicalSides {
416 inline_start: self.inline_start + other.inline_start,
417 inline_end: self.inline_end + other.inline_end,
418 block_start: self.block_start + other.block_start,
419 block_end: self.block_end + other.block_end,
420 }
421 }
422}
423
424impl<T: Sub<Output = T> + Copy> Sub<LogicalSides<T>> for LogicalSides<T> {
425 type Output = LogicalSides<T>;
426
427 fn sub(self, other: Self) -> Self::Output {
428 LogicalSides {
429 inline_start: self.inline_start - other.inline_start,
430 inline_end: self.inline_end - other.inline_end,
431 block_start: self.block_start - other.block_start,
432 block_end: self.block_end - other.block_end,
433 }
434 }
435}
436
437impl<T: Neg<Output = T> + Copy> Neg for LogicalSides<T> {
438 type Output = LogicalSides<T>;
439 fn neg(self) -> Self::Output {
440 Self {
441 inline_start: -self.inline_start,
442 inline_end: -self.inline_end,
443 block_start: -self.block_start,
444 block_end: -self.block_end,
445 }
446 }
447}
448
449impl<T: Zero> LogicalSides<T> {
450 pub(crate) fn zero() -> LogicalSides<T> {
451 Self {
452 inline_start: T::zero(),
453 inline_end: T::zero(),
454 block_start: T::zero(),
455 block_end: T::zero(),
456 }
457 }
458}
459
460impl From<LogicalSides<CSSPixelLength>> for LogicalSides<Au> {
461 fn from(value: LogicalSides<CSSPixelLength>) -> Self {
462 Self {
463 inline_start: value.inline_start.into(),
464 inline_end: value.inline_end.into(),
465 block_start: value.block_start.into(),
466 block_end: value.block_end.into(),
467 }
468 }
469}
470
471impl From<LogicalSides<Au>> for LogicalSides<CSSPixelLength> {
472 fn from(value: LogicalSides<Au>) -> Self {
473 Self {
474 inline_start: value.inline_start.into(),
475 inline_end: value.inline_end.into(),
476 block_start: value.block_start.into(),
477 block_end: value.block_end.into(),
478 }
479 }
480}
481
482impl<T> LogicalSides1D<T> {
483 #[inline]
484 pub(crate) fn new(start: T, end: T) -> Self {
485 Self { start, end }
486 }
487}
488
489impl<T> LogicalSides1D<AutoOr<T>> {
490 #[inline]
491 pub(crate) fn either_specified(&self) -> bool {
492 !self.start.is_auto() || !self.end.is_auto()
493 }
494
495 #[inline]
496 pub(crate) fn either_auto(&self) -> bool {
497 self.start.is_auto() || self.end.is_auto()
498 }
499}
500
501impl<T> LogicalRect<T> {
502 pub fn max_inline_position(&self) -> T
503 where
504 T: Add<Output = T> + Copy,
505 {
506 self.start_corner.inline + self.size.inline
507 }
508
509 pub fn max_block_position(&self) -> T
510 where
511 T: Add<Output = T> + Copy,
512 {
513 self.start_corner.block + self.size.block
514 }
515
516 pub fn inflate(&self, sides: &LogicalSides<T>) -> Self
517 where
518 T: Add<Output = T> + Copy,
519 T: Sub<Output = T> + Copy,
520 {
521 Self {
522 start_corner: LogicalVec2 {
523 inline: self.start_corner.inline - sides.inline_start,
524 block: self.start_corner.block - sides.block_start,
525 },
526 size: LogicalVec2 {
527 inline: self.size.inline + sides.inline_sum(),
528 block: self.size.block + sides.block_sum(),
529 },
530 }
531 }
532
533 pub fn deflate(&self, sides: &LogicalSides<T>) -> Self
534 where
535 T: Add<Output = T> + Copy,
536 T: Sub<Output = T> + Copy,
537 {
538 LogicalRect {
539 start_corner: LogicalVec2 {
540 inline: self.start_corner.inline + sides.inline_start,
541 block: self.start_corner.block + sides.block_start,
542 },
543 size: LogicalVec2 {
544 inline: self.size.inline - sides.inline_sum(),
545 block: self.size.block - sides.block_sum(),
546 },
547 }
548 }
549}
550
551impl LogicalRect<Au> {
552 pub(crate) fn as_physical(
553 &self,
554 containing_block: Option<&ContainingBlock<'_>>,
555 ) -> PhysicalRect<Au> {
556 let mode = containing_block.map_or_else(WritingMode::horizontal_tb, |containing_block| {
557 containing_block.style.writing_mode
558 });
559 let (x, y, width, height) = if mode.is_vertical() {
560 (
562 self.start_corner.block,
563 self.start_corner.inline,
564 self.size.block,
565 self.size.inline,
566 )
567 } else {
568 let y = self.start_corner.block;
569 let x = match containing_block {
570 Some(containing_block) if !mode.is_bidi_ltr() => {
571 containing_block.size.inline - self.max_inline_position()
572 },
573 _ => self.start_corner.inline,
574 };
575 (x, y, self.size.inline, self.size.block)
576 };
577
578 PhysicalRect::new(PhysicalPoint::new(x, y), PhysicalSize::new(width, height))
579 }
580}
581
582impl From<LogicalVec2<CSSPixelLength>> for LogicalVec2<Au> {
583 fn from(value: LogicalVec2<CSSPixelLength>) -> Self {
584 LogicalVec2 {
585 inline: value.inline.into(),
586 block: value.block.into(),
587 }
588 }
589}
590
591impl From<LogicalVec2<Au>> for LogicalVec2<CSSPixelLength> {
592 fn from(value: LogicalVec2<Au>) -> Self {
593 LogicalVec2 {
594 inline: value.inline.into(),
595 block: value.block.into(),
596 }
597 }
598}
599
600impl From<LogicalRect<Au>> for LogicalRect<CSSPixelLength> {
601 fn from(value: LogicalRect<Au>) -> Self {
602 LogicalRect {
603 start_corner: value.start_corner.into(),
604 size: value.size.into(),
605 }
606 }
607}
608
609impl From<LogicalRect<CSSPixelLength>> for LogicalRect<Au> {
610 fn from(value: LogicalRect<CSSPixelLength>) -> Self {
611 LogicalRect {
612 start_corner: value.start_corner.into(),
613 size: value.size.into(),
614 }
615 }
616}
617
618pub(crate) trait ToLogical<Unit, LogicalType> {
619 fn to_logical(&self, writing_mode: WritingMode) -> LogicalType;
620}
621
622impl<Unit: Copy> ToLogical<Unit, LogicalVec2<Unit>> for PhysicalSize<Unit> {
623 fn to_logical(&self, writing_mode: WritingMode) -> LogicalVec2<Unit> {
624 LogicalVec2::from_physical_size(self, writing_mode)
625 }
626}
627
628impl<Unit: Copy> ToLogical<Unit, LogicalSides<Unit>> for PhysicalSides<Unit> {
629 fn to_logical(&self, writing_mode: WritingMode) -> LogicalSides<Unit> {
630 LogicalSides::from_physical(self, writing_mode)
631 }
632}
633
634pub(crate) trait ToLogicalWithContainingBlock<LogicalType> {
635 fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalType;
636}
637
638impl ToLogicalWithContainingBlock<LogicalVec2<Au>> for PhysicalPoint<Au> {
639 fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalVec2<Au> {
640 let writing_mode = containing_block.style.writing_mode;
641 if writing_mode.is_vertical() {
643 LogicalVec2 {
644 inline: self.y,
645 block: self.x,
646 }
647 } else {
648 LogicalVec2 {
649 inline: if writing_mode.is_bidi_ltr() {
650 self.x
651 } else {
652 containing_block.size.inline - self.x
653 },
654 block: self.y,
655 }
656 }
657 }
658}
659
660impl ToLogicalWithContainingBlock<LogicalRect<Au>> for PhysicalRect<Au> {
661 fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalRect<Au> {
662 let inline_start;
663 let block_start;
664 let inline;
665 let block;
666
667 let writing_mode = containing_block.style.writing_mode;
668 if writing_mode.is_vertical() {
669 inline = self.size.height;
671 block = self.size.width;
672 block_start = self.origin.x;
673 inline_start = self.origin.y;
674 } else {
675 inline = self.size.width;
676 block = self.size.height;
677 block_start = self.origin.y;
678 if writing_mode.is_bidi_ltr() {
679 inline_start = self.origin.x;
680 } else {
681 inline_start = containing_block.size.inline - (self.origin.x + self.size.width);
682 }
683 }
684 LogicalRect {
685 start_corner: LogicalVec2 {
686 inline: inline_start,
687 block: block_start,
688 },
689 size: LogicalVec2 { inline, block },
690 }
691 }
692}