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