/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::style_ext::{Direction, WritingMode}; use std::fmt; use std::ops::{Add, AddAssign, Sub}; use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto}; use style::Zero; use style_traits::CSSPixel; pub type Point = euclid::Point2D; pub type Size = euclid::Size2D; pub type Rect = euclid::Rect; pub(crate) mod physical { #[derive(Clone)] pub(crate) struct Vec2 { pub x: T, pub y: T, } #[derive(Clone, Debug)] pub(crate) struct Rect { pub top_left: Vec2, pub size: Vec2, } #[derive(Clone, Debug)] pub(crate) struct Sides { pub top: T, pub left: T, pub bottom: T, pub right: T, } } pub(crate) mod flow_relative { #[derive(Clone)] pub(crate) struct Vec2 { pub inline: T, pub block: T, } #[derive(Clone, Debug)] pub(crate) struct Rect { pub start_corner: Vec2, pub size: Vec2, } #[derive(Clone, Debug)] pub(crate) struct Sides { pub inline_start: T, pub inline_end: T, pub block_start: T, pub block_end: T, } } impl fmt::Debug for physical::Vec2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Not using f.debug_struct on purpose here, to keep {:?} output somewhat compact f.write_str("Vec2 { x: ")?; self.x.fmt(f)?; f.write_str(", y: ")?; self.y.fmt(f)?; f.write_str(" }") } } impl fmt::Debug for flow_relative::Vec2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Not using f.debug_struct on purpose here, to keep {:?} output somewhat compact f.write_str("Vec2 { i: ")?; self.inline.fmt(f)?; f.write_str(", b: ")?; self.block.fmt(f)?; f.write_str(" }") } } impl Add<&'_ physical::Vec2> for &'_ physical::Vec2 where T: Add + Copy, { type Output = physical::Vec2; fn add(self, other: &'_ physical::Vec2) -> Self::Output { physical::Vec2 { x: self.x + other.x, y: self.y + other.y, } } } impl physical::Vec2 { pub fn size_to_flow_relative(&self, mode: (WritingMode, Direction)) -> flow_relative::Vec2 { // https://drafts.csswg.org/css-writing-modes/#logical-to-physical let (i, b) = if let (WritingMode::HorizontalTb, _) = mode { (&self.x, &self.y) } else { (&self.y, &self.x) }; flow_relative::Vec2 { inline: i.clone(), block: b.clone(), } } } impl Add<&'_ flow_relative::Vec2> for &'_ flow_relative::Vec2 where T: Add + Copy, { type Output = flow_relative::Vec2; fn add(self, other: &'_ flow_relative::Vec2) -> Self::Output { flow_relative::Vec2 { inline: self.inline + other.inline, block: self.block + other.block, } } } impl AddAssign<&'_ flow_relative::Vec2> for flow_relative::Vec2 where T: AddAssign + Copy, { fn add_assign(&mut self, other: &'_ flow_relative::Vec2) { self.inline += other.inline; self.block += other.block; } } impl flow_relative::Vec2 { pub fn zero() -> Self { Self { inline: Length::zero(), block: Length::zero(), } } } impl flow_relative::Sides { pub fn zero() -> Self { Self { inline_start: Length::zero(), inline_end: Length::zero(), block_start: Length::zero(), block_end: Length::zero(), } } } impl flow_relative::Rect { pub fn zero() -> Self { Self { start_corner: flow_relative::Vec2::zero(), size: flow_relative::Vec2::zero(), } } } impl flow_relative::Vec2 { pub fn size_to_physical(&self, mode: (WritingMode, Direction)) -> physical::Vec2 { // https://drafts.csswg.org/css-writing-modes/#logical-to-physical let (x, y) = if let (WritingMode::HorizontalTb, _) = mode { (&self.inline, &self.block) } else { (&self.block, &self.inline) }; physical::Vec2 { x: x.clone(), y: y.clone(), } } } impl From> for Point { fn from(v: physical::Vec2) -> Self { Point::from_lengths(v.x.into(), v.y.into()) } } impl physical::Sides { pub fn to_flow_relative(&self, mode: (WritingMode, Direction)) -> flow_relative::Sides { use Direction::*; use WritingMode::*; // https://drafts.csswg.org/css-writing-modes/#logical-to-physical let (bs, be) = match mode.0 { HorizontalTb => (&self.top, &self.bottom), VerticalRl => (&self.right, &self.left), VerticalLr => (&self.left, &self.right), }; let (is, ie) = match mode { (HorizontalTb, Ltr) => (&self.left, &self.right), (HorizontalTb, Rtl) => (&self.right, &self.left), (VerticalRl, Ltr) | (VerticalLr, Ltr) => (&self.top, &self.bottom), (VerticalRl, Rtl) | (VerticalLr, Rtl) => (&self.bottom, &self.top), }; flow_relative::Sides { inline_start: is.clone(), inline_end: ie.clone(), block_start: bs.clone(), block_end: be.clone(), } } } impl flow_relative::Sides { pub fn map(&self, f: impl Fn(&T) -> U) -> flow_relative::Sides { flow_relative::Sides { inline_start: f(&self.inline_start), inline_end: f(&self.inline_end), block_start: f(&self.block_start), block_end: f(&self.block_end), } } pub fn map_inline_and_block_axes( &self, inline_f: impl Fn(&T) -> U, block_f: impl Fn(&T) -> U, ) -> flow_relative::Sides { flow_relative::Sides { inline_start: inline_f(&self.inline_start), inline_end: inline_f(&self.inline_end), block_start: block_f(&self.block_start), block_end: block_f(&self.block_end), } } pub fn inline_sum(&self) -> T::Output where T: Add + Copy, { self.inline_start + self.inline_end } pub fn block_sum(&self) -> T::Output where T: Add + Copy, { self.block_start + self.block_end } pub fn start_corner(&self) -> flow_relative::Vec2 where T: Clone, { flow_relative::Vec2 { inline: self.inline_start.clone(), block: self.block_start.clone(), } } } impl flow_relative::Sides { pub fn percentages_relative_to(&self, basis: Length) -> flow_relative::Sides { self.map(|s| s.percentage_relative_to(basis)) } } impl flow_relative::Sides { pub fn percentages_relative_to(&self, basis: Length) -> flow_relative::Sides { self.map(|s| s.percentage_relative_to(basis)) } } impl flow_relative::Sides { pub fn auto_is(&self, f: impl Fn() -> Length) -> flow_relative::Sides { self.map(|s| s.auto_is(&f)) } } impl Add<&'_ flow_relative::Sides> for &'_ flow_relative::Sides where T: Add + Copy, { type Output = flow_relative::Sides; fn add(self, other: &'_ flow_relative::Sides) -> Self::Output { flow_relative::Sides { inline_start: self.inline_start + other.inline_start, inline_end: self.inline_end + other.inline_end, block_start: self.block_start + other.block_start, block_end: self.block_end + other.block_end, } } } impl flow_relative::Rect { pub fn inflate(&self, sides: &flow_relative::Sides) -> Self where T: Add + Copy, T: Sub + Copy, { flow_relative::Rect { start_corner: flow_relative::Vec2 { inline: self.start_corner.inline - sides.inline_start, block: self.start_corner.block - sides.block_start, }, size: flow_relative::Vec2 { inline: self.size.inline + sides.inline_sum(), block: self.size.block + sides.block_sum(), }, } } pub fn to_physical( &self, mode: (WritingMode, Direction), // Will be needed for other writing modes // FIXME: what if the containing block has a different mode? // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows _containing_block: &physical::Rect, ) -> physical::Rect where T: Clone, { // Top-left corner let (tl_x, tl_y) = if let (WritingMode::HorizontalTb, Direction::Ltr) = mode { (&self.start_corner.inline, &self.start_corner.block) } else { unimplemented!() }; physical::Rect { top_left: physical::Vec2 { x: tl_x.clone(), y: tl_y.clone(), }, size: self.size.size_to_physical(mode), } } } impl physical::Rect { pub fn translate(&self, by: &physical::Vec2) -> Self where T: Add + Copy, { physical::Rect { top_left: &self.top_left + by, size: self.size.clone(), } } } impl From> for Rect { fn from(r: physical::Rect) -> Self { Rect { origin: Point::new(r.top_left.x.px(), r.top_left.y.px()), size: Size::new(r.size.x.px(), r.size.y.px()), } } } impl From> for webrender_api::units::LayoutRect { fn from(r: physical::Rect) -> Self { Rect { origin: Point::new(r.top_left.x.px(), r.top_left.y.px()), size: Size::new(r.size.x.px(), r.size.y.px()), } } }