mirror of
https://github.com/servo/servo.git
synced 2025-07-30 18:50:36 +01:00
This change removes the `effective_writing_mode` concept and tries to properly implement right-to-left layout support for all non-inline writing modes. In general, what needs to happen is that rectangles need to be converted to physical rectangles using the containing block. A right-to-left rectangle's inline start is on the right physical side of the containing block. Likewise a positive inline offset in right-to-left text is a negative physical one. The implementation here is pretty good for most layout modes, but floats are still a bit in process. Currently, floats are processed in the logical layout of the block container, but there still might be issues with float interaction with mixed RTL and LTR. While this does move us closer to supporting vertical writing modes, this is still unsupported. New failures: - Vertical writing mode not supported: - `/css/CSS2/floats/floats-placement-vertical-001b.xht` - `/css/CSS2/floats/floats-placement-vertical-001c.xht` - Absolutes inlines should avoid floats (#33323) - `/css/css-position/position-absolute-dynamic-static-position-floats-004.html` - No support for grid - `/css/css-align/self-alignment/self-align-safe-unsafe-grid-003.html` - `/css/css-position/static-position/inline-level-absolute-in-block-level-context-009.html` - `/css/css-position/static-position/inline-level-absolute-in-block-level-context-010.html` - Cannot reproduce these locally on any platform. Very mysterious: - `/css/css-tables/row-group-margin-border-padding.html` - `/css/css-tables/row-margin-border-padding.html` - Exposes bugs we have related to hanging whitespace in preserved whitespace inlines: - `/css/css-text/white-space/trailing-space-and-text-alignment-rtl-003.html` - `/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-023.html` Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Rakhi Sharma <atbrakhi@igalia.com>
706 lines
22 KiB
Rust
706 lines
22 KiB
Rust
/* 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 std::convert::From;
|
||
use std::fmt;
|
||
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
|
||
|
||
use app_units::Au;
|
||
use serde::Serialize;
|
||
use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode};
|
||
use style::values::computed::{CSSPixelLength, Length, LengthPercentage};
|
||
use style::values::generics::length::GenericLengthPercentageOrAuto as AutoOr;
|
||
use style::Zero;
|
||
use style_traits::CSSPixel;
|
||
|
||
use crate::ContainingBlock;
|
||
|
||
pub type PhysicalPoint<U> = euclid::Point2D<U, CSSPixel>;
|
||
pub type PhysicalSize<U> = euclid::Size2D<U, CSSPixel>;
|
||
pub type PhysicalVec<U> = euclid::Vector2D<U, CSSPixel>;
|
||
pub type PhysicalRect<U> = euclid::Rect<U, CSSPixel>;
|
||
pub type PhysicalSides<U> = euclid::SideOffsets2D<U, CSSPixel>;
|
||
pub type AuOrAuto = AutoOr<Au>;
|
||
pub type LengthOrAuto = AutoOr<Length>;
|
||
pub type LengthPercentageOrAuto<'a> = AutoOr<&'a LengthPercentage>;
|
||
|
||
#[derive(Clone, Copy, PartialEq, Serialize)]
|
||
pub struct LogicalVec2<T> {
|
||
pub inline: T,
|
||
pub block: T,
|
||
}
|
||
|
||
#[derive(Clone, Copy, Serialize)]
|
||
pub struct LogicalRect<T> {
|
||
pub start_corner: LogicalVec2<T>,
|
||
pub size: LogicalVec2<T>,
|
||
}
|
||
|
||
#[derive(Clone, Copy, Debug, Serialize)]
|
||
pub struct LogicalSides<T> {
|
||
pub inline_start: T,
|
||
pub inline_end: T,
|
||
pub block_start: T,
|
||
pub block_end: T,
|
||
}
|
||
|
||
impl<T: fmt::Debug> fmt::Debug for LogicalVec2<T> {
|
||
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<T: Clone> LogicalVec2<T> {
|
||
pub fn from_physical_size(physical_size: &PhysicalSize<T>, mode: WritingMode) -> Self {
|
||
// https://drafts.csswg.org/css-writing-modes/#logical-to-physical
|
||
let (i, b) = if mode.is_horizontal() {
|
||
(&physical_size.width, &physical_size.height)
|
||
} else {
|
||
(&physical_size.height, &physical_size.width)
|
||
};
|
||
LogicalVec2 {
|
||
inline: i.clone(),
|
||
block: b.clone(),
|
||
}
|
||
}
|
||
|
||
pub fn map<U>(&self, f: impl Fn(&T) -> U) -> LogicalVec2<U> {
|
||
LogicalVec2 {
|
||
inline: f(&self.inline),
|
||
block: f(&self.block),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<T: Add<Output = T> + Copy> Add<LogicalVec2<T>> for LogicalVec2<T> {
|
||
type Output = LogicalVec2<T>;
|
||
fn add(self, other: Self) -> Self::Output {
|
||
LogicalVec2 {
|
||
inline: self.inline + other.inline,
|
||
block: self.block + other.block,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<T: Sub<Output = T> + Copy> Sub<LogicalVec2<T>> for LogicalVec2<T> {
|
||
type Output = LogicalVec2<T>;
|
||
fn sub(self, other: Self) -> Self::Output {
|
||
LogicalVec2 {
|
||
inline: self.inline - other.inline,
|
||
block: self.block - other.block,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<T: AddAssign<T> + Copy> AddAssign<LogicalVec2<T>> for LogicalVec2<T> {
|
||
fn add_assign(&mut self, other: LogicalVec2<T>) {
|
||
self.inline += other.inline;
|
||
self.block += other.block;
|
||
}
|
||
}
|
||
|
||
impl<T: SubAssign<T> + Copy> SubAssign<LogicalVec2<T>> for LogicalVec2<T> {
|
||
fn sub_assign(&mut self, other: LogicalVec2<T>) {
|
||
self.inline -= other.inline;
|
||
self.block -= other.block;
|
||
}
|
||
}
|
||
|
||
impl<T: Neg<Output = T> + Copy> Neg for LogicalVec2<T> {
|
||
type Output = LogicalVec2<T>;
|
||
fn neg(self) -> Self::Output {
|
||
Self {
|
||
inline: -self.inline,
|
||
block: -self.block,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<T: Zero> LogicalVec2<T> {
|
||
pub fn zero() -> Self {
|
||
Self {
|
||
inline: T::zero(),
|
||
block: T::zero(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<T: Clone> LogicalVec2<AutoOr<T>> {
|
||
pub fn auto_is(&self, f: impl Fn() -> T) -> LogicalVec2<T> {
|
||
self.map(|t| t.auto_is(&f))
|
||
}
|
||
}
|
||
|
||
impl LogicalVec2<LengthPercentageOrAuto<'_>> {
|
||
pub(crate) fn percentages_relative_to(
|
||
&self,
|
||
containing_block: &ContainingBlock,
|
||
) -> LogicalVec2<AuOrAuto> {
|
||
LogicalVec2 {
|
||
inline: self
|
||
.inline
|
||
.map(|value| value.to_used_value(containing_block.inline_size)),
|
||
block: {
|
||
let containing_block_block_size =
|
||
containing_block.block_size.non_auto().map(Into::into);
|
||
self.block
|
||
.non_auto()
|
||
.and_then(|value| value.maybe_to_used_value(containing_block_block_size))
|
||
.map(|value| AuOrAuto::LengthPercentage(value))
|
||
.unwrap_or(AuOrAuto::Auto)
|
||
},
|
||
}
|
||
}
|
||
}
|
||
|
||
impl LogicalVec2<LengthPercentageOrAuto<'_>> {
|
||
pub(crate) fn percentages_relative_to_basis(
|
||
&self,
|
||
basis: &LogicalVec2<Au>,
|
||
) -> LogicalVec2<AuOrAuto> {
|
||
LogicalVec2 {
|
||
inline: self.inline.map(|value| value.to_used_value(basis.inline)),
|
||
block: self.block.map(|value| value.to_used_value(basis.block)),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl LogicalVec2<LengthPercentageOrAuto<'_>> {
|
||
pub(crate) fn maybe_percentages_relative_to_basis(
|
||
&self,
|
||
basis: &LogicalVec2<Option<Au>>,
|
||
) -> LogicalVec2<AuOrAuto> {
|
||
let basis = basis.map(|value| value.map(Into::into));
|
||
LogicalVec2 {
|
||
inline: self
|
||
.inline
|
||
.non_auto()
|
||
.and_then(|value| value.maybe_to_used_value(basis.inline))
|
||
.map(|value| AuOrAuto::LengthPercentage(value))
|
||
.unwrap_or(AuOrAuto::Auto),
|
||
block: self
|
||
.block
|
||
.non_auto()
|
||
.and_then(|value| value.maybe_to_used_value(basis.block))
|
||
.map(|value| AuOrAuto::LengthPercentage(value))
|
||
.unwrap_or(AuOrAuto::Auto),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl LogicalVec2<Option<&'_ LengthPercentage>> {
|
||
pub(crate) fn percentages_relative_to(
|
||
&self,
|
||
containing_block: &ContainingBlock,
|
||
) -> LogicalVec2<Option<Au>> {
|
||
LogicalVec2 {
|
||
inline: self
|
||
.inline
|
||
.map(|lp| lp.to_used_value(containing_block.inline_size)),
|
||
block: self.block.and_then(|lp| {
|
||
lp.maybe_to_used_value(containing_block.block_size.map(Into::into).non_auto())
|
||
}),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl LogicalVec2<Option<&'_ LengthPercentage>> {
|
||
pub(crate) fn maybe_percentages_relative_to_basis(
|
||
&self,
|
||
basis: &LogicalVec2<Option<Au>>,
|
||
) -> LogicalVec2<Option<Au>> {
|
||
LogicalVec2 {
|
||
inline: self
|
||
.inline
|
||
.and_then(|v| v.maybe_to_used_value(basis.inline.map(Into::into))),
|
||
block: self
|
||
.block
|
||
.and_then(|v| v.maybe_to_used_value(basis.block.map(Into::into))),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<T: Zero> LogicalRect<T> {
|
||
pub fn zero() -> Self {
|
||
Self {
|
||
start_corner: LogicalVec2::zero(),
|
||
size: LogicalVec2::zero(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl fmt::Debug for LogicalRect<Au> {
|
||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
write!(
|
||
f,
|
||
"Rect(i{}×b{} @ (i{},b{}))",
|
||
self.size.inline.to_px(),
|
||
self.size.block.to_px(),
|
||
self.start_corner.inline.to_px(),
|
||
self.start_corner.block.to_px(),
|
||
)
|
||
}
|
||
}
|
||
|
||
impl<T: Clone> LogicalVec2<T> {
|
||
pub fn to_physical_size(&self, mode: WritingMode) -> PhysicalSize<T> {
|
||
// https://drafts.csswg.org/css-writing-modes/#logical-to-physical
|
||
let (x, y) = if mode.is_horizontal() {
|
||
(&self.inline, &self.block)
|
||
} else {
|
||
(&self.block, &self.inline)
|
||
};
|
||
PhysicalSize::new(x.clone(), y.clone())
|
||
}
|
||
}
|
||
|
||
impl<T: Copy + Neg<Output = T>> LogicalVec2<T> {
|
||
pub fn to_physical_vector(&self, mode: WritingMode) -> PhysicalVec<T> {
|
||
if mode.is_horizontal() {
|
||
if mode.is_bidi_ltr() {
|
||
PhysicalVec::new(self.inline, self.block)
|
||
} else {
|
||
PhysicalVec::new(-self.inline, self.block)
|
||
}
|
||
} else {
|
||
if mode.is_inline_tb() {
|
||
PhysicalVec::new(self.block, self.inline)
|
||
} else {
|
||
PhysicalVec::new(-self.block, self.inline)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
impl LogicalVec2<Au> {
|
||
#[inline]
|
||
pub fn to_physical_point(
|
||
&self,
|
||
containing_block: Option<&ContainingBlock>,
|
||
) -> PhysicalPoint<Au> {
|
||
let mode = containing_block.map_or_else(WritingMode::horizontal_tb, |containing_block| {
|
||
containing_block.style.writing_mode
|
||
});
|
||
if mode.is_vertical() {
|
||
// TODO: Bottom-to-top writing modes are not supported yet.
|
||
PhysicalPoint::new(self.block, self.inline)
|
||
} else {
|
||
let y = self.block;
|
||
let x = match containing_block {
|
||
Some(containing_block) if !mode.is_bidi_ltr() => {
|
||
containing_block.inline_size - self.inline
|
||
},
|
||
_ => self.inline,
|
||
};
|
||
PhysicalPoint::new(x, y)
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<T: Clone> LogicalSides<T> {
|
||
pub fn from_physical(sides: &PhysicalSides<T>, mode: WritingMode) -> Self {
|
||
// https://drafts.csswg.org/css-writing-modes/#logical-to-physical
|
||
let block_flow = mode.block_flow_direction();
|
||
let (bs, be) = match mode.block_flow_direction() {
|
||
BlockFlowDirection::TopToBottom => (&sides.top, &sides.bottom),
|
||
BlockFlowDirection::RightToLeft => (&sides.right, &sides.left),
|
||
BlockFlowDirection::LeftToRight => (&sides.left, &sides.right),
|
||
};
|
||
use BlockFlowDirection::TopToBottom;
|
||
let (is, ie) = match (block_flow, mode.inline_base_direction()) {
|
||
(TopToBottom, InlineBaseDirection::LeftToRight) => (&sides.left, &sides.right),
|
||
(TopToBottom, InlineBaseDirection::RightToLeft) => (&sides.right, &sides.left),
|
||
(_, InlineBaseDirection::LeftToRight) => (&sides.top, &sides.bottom),
|
||
(_, InlineBaseDirection::RightToLeft) => (&sides.bottom, &sides.top),
|
||
};
|
||
LogicalSides {
|
||
inline_start: is.clone(),
|
||
inline_end: ie.clone(),
|
||
block_start: bs.clone(),
|
||
block_end: be.clone(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<T> LogicalSides<T> {
|
||
pub fn map<U>(&self, f: impl Fn(&T) -> U) -> LogicalSides<U> {
|
||
LogicalSides {
|
||
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<U>(
|
||
&self,
|
||
inline_f: impl Fn(&T) -> U,
|
||
block_f: impl Fn(&T) -> U,
|
||
) -> LogicalSides<U> {
|
||
LogicalSides {
|
||
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 sum(&self) -> LogicalVec2<T::Output>
|
||
where
|
||
T: Add + Copy,
|
||
{
|
||
LogicalVec2 {
|
||
inline: self.inline_sum(),
|
||
block: self.block_sum(),
|
||
}
|
||
}
|
||
|
||
pub fn to_physical(&self, mode: WritingMode) -> PhysicalSides<T>
|
||
where
|
||
T: Clone,
|
||
{
|
||
let top;
|
||
let right;
|
||
let bottom;
|
||
let left;
|
||
if mode.is_vertical() {
|
||
if mode.is_vertical_lr() {
|
||
left = self.block_start.clone();
|
||
right = self.block_end.clone();
|
||
} else {
|
||
right = self.block_start.clone();
|
||
left = self.block_end.clone();
|
||
}
|
||
|
||
if mode.is_inline_tb() {
|
||
top = self.inline_start.clone();
|
||
bottom = self.inline_end.clone();
|
||
} else {
|
||
bottom = self.inline_start.clone();
|
||
top = self.inline_end.clone();
|
||
}
|
||
} else {
|
||
top = self.block_start.clone();
|
||
bottom = self.block_end.clone();
|
||
if mode.is_bidi_ltr() {
|
||
left = self.inline_start.clone();
|
||
right = self.inline_end.clone();
|
||
} else {
|
||
right = self.inline_start.clone();
|
||
left = self.inline_end.clone();
|
||
}
|
||
}
|
||
PhysicalSides::new(top, right, bottom, left)
|
||
}
|
||
}
|
||
|
||
impl<T: Copy> LogicalSides<T> {
|
||
pub fn start_offset(&self) -> LogicalVec2<T> {
|
||
LogicalVec2 {
|
||
inline: self.inline_start,
|
||
block: self.block_start,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl LogicalSides<&'_ LengthPercentage> {
|
||
pub fn percentages_relative_to(&self, basis: Au) -> LogicalSides<Au> {
|
||
self.map(|value| value.to_used_value(basis))
|
||
}
|
||
}
|
||
|
||
impl LogicalSides<LengthPercentageOrAuto<'_>> {
|
||
pub fn percentages_relative_to(&self, basis: Au) -> LogicalSides<AuOrAuto> {
|
||
self.map(|value| value.map(|value| value.to_used_value(basis)))
|
||
}
|
||
}
|
||
|
||
impl<T: Clone> LogicalSides<AutoOr<T>> {
|
||
pub fn auto_is(&self, f: impl Fn() -> T) -> LogicalSides<T> {
|
||
self.map(|s| s.auto_is(&f))
|
||
}
|
||
}
|
||
|
||
impl<T: Add<Output = T> + Copy> Add<LogicalSides<T>> for LogicalSides<T> {
|
||
type Output = LogicalSides<T>;
|
||
|
||
fn add(self, other: Self) -> Self::Output {
|
||
LogicalSides {
|
||
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<T: Sub<Output = T> + Copy> Sub<LogicalSides<T>> for LogicalSides<T> {
|
||
type Output = LogicalSides<T>;
|
||
|
||
fn sub(self, other: Self) -> Self::Output {
|
||
LogicalSides {
|
||
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<T: Neg<Output = T> + Copy> Neg for LogicalSides<T> {
|
||
type Output = LogicalSides<T>;
|
||
fn neg(self) -> Self::Output {
|
||
Self {
|
||
inline_start: -self.inline_start,
|
||
inline_end: -self.inline_end,
|
||
block_start: -self.block_start,
|
||
block_end: -self.block_end,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<T: Zero> LogicalSides<T> {
|
||
pub(crate) fn zero() -> LogicalSides<T> {
|
||
Self {
|
||
inline_start: T::zero(),
|
||
inline_end: T::zero(),
|
||
block_start: T::zero(),
|
||
block_end: T::zero(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl From<LogicalSides<CSSPixelLength>> for LogicalSides<Au> {
|
||
fn from(value: LogicalSides<CSSPixelLength>) -> Self {
|
||
Self {
|
||
inline_start: value.inline_start.into(),
|
||
inline_end: value.inline_end.into(),
|
||
block_start: value.block_start.into(),
|
||
block_end: value.block_end.into(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl From<LogicalSides<Au>> for LogicalSides<CSSPixelLength> {
|
||
fn from(value: LogicalSides<Au>) -> Self {
|
||
Self {
|
||
inline_start: value.inline_start.into(),
|
||
inline_end: value.inline_end.into(),
|
||
block_start: value.block_start.into(),
|
||
block_end: value.block_end.into(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<T> LogicalRect<T> {
|
||
pub fn max_inline_position(&self) -> T
|
||
where
|
||
T: Add<Output = T> + Copy,
|
||
{
|
||
self.start_corner.inline + self.size.inline
|
||
}
|
||
|
||
pub fn max_block_position(&self) -> T
|
||
where
|
||
T: Add<Output = T> + Copy,
|
||
{
|
||
self.start_corner.block + self.size.block
|
||
}
|
||
|
||
pub fn inflate(&self, sides: &LogicalSides<T>) -> Self
|
||
where
|
||
T: Add<Output = T> + Copy,
|
||
T: Sub<Output = T> + Copy,
|
||
{
|
||
Self {
|
||
start_corner: LogicalVec2 {
|
||
inline: self.start_corner.inline - sides.inline_start,
|
||
block: self.start_corner.block - sides.block_start,
|
||
},
|
||
size: LogicalVec2 {
|
||
inline: self.size.inline + sides.inline_sum(),
|
||
block: self.size.block + sides.block_sum(),
|
||
},
|
||
}
|
||
}
|
||
|
||
pub fn deflate(&self, sides: &LogicalSides<T>) -> Self
|
||
where
|
||
T: Add<Output = T> + Copy,
|
||
T: Sub<Output = T> + Copy,
|
||
{
|
||
LogicalRect {
|
||
start_corner: LogicalVec2 {
|
||
inline: self.start_corner.inline + sides.inline_start,
|
||
block: self.start_corner.block + sides.block_start,
|
||
},
|
||
size: LogicalVec2 {
|
||
inline: self.size.inline - sides.inline_sum(),
|
||
block: self.size.block - sides.block_sum(),
|
||
},
|
||
}
|
||
}
|
||
}
|
||
|
||
impl LogicalRect<Au> {
|
||
pub fn to_physical<'a>(
|
||
&self,
|
||
containing_block: Option<&ContainingBlock<'a>>,
|
||
) -> PhysicalRect<Au> {
|
||
let mode = containing_block.map_or_else(WritingMode::horizontal_tb, |containing_block| {
|
||
containing_block.style.writing_mode
|
||
});
|
||
let (x, y, width, height) = if mode.is_vertical() {
|
||
// TODO: Bottom-to-top writing modes are not supported.
|
||
(
|
||
self.start_corner.block,
|
||
self.start_corner.inline,
|
||
self.size.block,
|
||
self.size.inline,
|
||
)
|
||
} else {
|
||
let y = self.start_corner.block;
|
||
let x = match containing_block {
|
||
Some(containing_block) if !mode.is_bidi_ltr() => {
|
||
containing_block.inline_size - self.max_inline_position()
|
||
},
|
||
_ => self.start_corner.inline,
|
||
};
|
||
(x, y, self.size.inline, self.size.block)
|
||
};
|
||
|
||
PhysicalRect::new(PhysicalPoint::new(x, y), PhysicalSize::new(width, height))
|
||
}
|
||
}
|
||
|
||
impl From<LogicalVec2<CSSPixelLength>> for LogicalVec2<Au> {
|
||
fn from(value: LogicalVec2<CSSPixelLength>) -> Self {
|
||
LogicalVec2 {
|
||
inline: value.inline.into(),
|
||
block: value.block.into(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl From<LogicalVec2<Au>> for LogicalVec2<CSSPixelLength> {
|
||
fn from(value: LogicalVec2<Au>) -> Self {
|
||
LogicalVec2 {
|
||
inline: value.inline.into(),
|
||
block: value.block.into(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl From<LogicalRect<Au>> for LogicalRect<CSSPixelLength> {
|
||
fn from(value: LogicalRect<Au>) -> Self {
|
||
LogicalRect {
|
||
start_corner: value.start_corner.into(),
|
||
size: value.size.into(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl From<LogicalRect<CSSPixelLength>> for LogicalRect<Au> {
|
||
fn from(value: LogicalRect<CSSPixelLength>) -> Self {
|
||
LogicalRect {
|
||
start_corner: value.start_corner.into(),
|
||
size: value.size.into(),
|
||
}
|
||
}
|
||
}
|
||
|
||
pub(crate) trait ToLogical<Unit, LogicalType> {
|
||
fn to_logical(&self, writing_mode: WritingMode) -> LogicalType;
|
||
}
|
||
|
||
impl<Unit: Copy> ToLogical<Unit, LogicalVec2<Unit>> for PhysicalSize<Unit> {
|
||
fn to_logical(&self, writing_mode: WritingMode) -> LogicalVec2<Unit> {
|
||
LogicalVec2::from_physical_size(self, writing_mode)
|
||
}
|
||
}
|
||
|
||
impl<Unit: Copy> ToLogical<Unit, LogicalSides<Unit>> for PhysicalSides<Unit> {
|
||
fn to_logical(&self, writing_mode: WritingMode) -> LogicalSides<Unit> {
|
||
LogicalSides::from_physical(self, writing_mode)
|
||
}
|
||
}
|
||
|
||
pub(crate) trait ToLogicalWithContainingBlock<LogicalType> {
|
||
fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalType;
|
||
}
|
||
|
||
impl ToLogicalWithContainingBlock<LogicalVec2<Au>> for PhysicalPoint<Au> {
|
||
fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalVec2<Au> {
|
||
let writing_mode = containing_block.style.writing_mode;
|
||
// TODO: Bottom-to-top and right-to-left vertical writing modes are not supported yet.
|
||
if writing_mode.is_vertical() {
|
||
LogicalVec2 {
|
||
inline: self.y,
|
||
block: self.x,
|
||
}
|
||
} else {
|
||
LogicalVec2 {
|
||
inline: if writing_mode.is_bidi_ltr() {
|
||
self.x
|
||
} else {
|
||
containing_block.inline_size - self.x
|
||
},
|
||
block: self.y,
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
impl ToLogicalWithContainingBlock<LogicalRect<Au>> for PhysicalRect<Au> {
|
||
fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalRect<Au> {
|
||
let inline_start;
|
||
let block_start;
|
||
let inline;
|
||
let block;
|
||
|
||
let writing_mode = containing_block.style.writing_mode;
|
||
if writing_mode.is_vertical() {
|
||
// TODO: Bottom-to-top and right-to-left vertical writing modes are not supported yet.
|
||
inline = self.size.height;
|
||
block = self.size.width;
|
||
block_start = self.origin.x;
|
||
inline_start = self.origin.y;
|
||
} else {
|
||
inline = self.size.width;
|
||
block = self.size.height;
|
||
block_start = self.origin.y;
|
||
if writing_mode.is_bidi_ltr() {
|
||
inline_start = self.origin.x;
|
||
} else {
|
||
inline_start = containing_block.inline_size - (self.origin.x + self.size.width);
|
||
}
|
||
}
|
||
LogicalRect {
|
||
start_corner: LogicalVec2 {
|
||
inline: inline_start,
|
||
block: block_start,
|
||
},
|
||
size: LogicalVec2 { inline, block },
|
||
}
|
||
}
|
||
}
|