servo: Merge #2790 - Add geometry primitives in flow-relative space (from SimonSapin:abstract-geometry)

Source-Repo: https://github.com/servo/servo
Source-Revision: 5c15c4d11f7bab18e68d27fbd4f8277f278b3612
This commit is contained in:
Simon Sapin 2014-07-09 22:04:59 +01:00
Родитель 7b1d26e343
Коммит d119aea2bf
3 изменённых файлов: 995 добавлений и 0 удалений

Просмотреть файл

@ -1035,6 +1035,17 @@ pub mod longhands {
${single_keyword("table-layout", "auto fixed")} ${single_keyword("table-layout", "auto fixed")}
// CSS 2.1, Section 18 - User interface // CSS 2.1, Section 18 - User interface
// CSS Writing Modes Level 3
// http://dev.w3.org/csswg/css-writing-modes/
${switch_to_style_struct("InheritedBox")}
${single_keyword("writing-mode", "horizontal-tb vertical-rl vertical-lr")}
// FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support)
// FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented
${single_keyword("text-orientation", "sideways sideways-left sideways-right")}
} }
@ -1565,6 +1576,9 @@ impl PropertyDeclaration {
pub mod style_structs { pub mod style_structs {
use super::longhands; use super::longhands;
use super::computed_values;
use servo_util::logical_geometry::WritingMode;
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
#[deriving(PartialEq, Clone)] #[deriving(PartialEq, Clone)]
pub struct ${style_struct.name} { pub struct ${style_struct.name} {
@ -1573,6 +1587,42 @@ pub mod style_structs {
% endfor % endfor
} }
% endfor % endfor
impl InheritedBox {
/// Return a WritingMode bitflags from the relevant CSS properties.
pub fn get_writing_mode(&self) -> WritingMode {
use servo_util::logical_geometry;
let mut flags = WritingMode::empty();
match self.direction {
computed_values::direction::ltr => {},
computed_values::direction::rtl => {
flags.insert(logical_geometry::FlagRTL);
},
}
match self.writing_mode {
computed_values::writing_mode::horizontal_tb => {},
computed_values::writing_mode::vertical_rl => {
flags.insert(logical_geometry::FlagVertical);
},
computed_values::writing_mode::vertical_lr => {
flags.insert(logical_geometry::FlagVertical);
flags.insert(logical_geometry::FlagVerticalLR);
},
}
match self.text_orientation {
computed_values::text_orientation::sideways_right => {},
computed_values::text_orientation::sideways_left => {
flags.insert(logical_geometry::FlagSidewaysLeft);
},
computed_values::text_orientation::sideways => {
if flags.intersects(logical_geometry::FlagVerticalLR) {
flags.insert(logical_geometry::FlagSidewaysLeft);
}
},
}
flags
}
}
} }
#[deriving(Clone)] #[deriving(Clone)]

Просмотреть файл

@ -0,0 +1,944 @@
/* 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 http://mozilla.org/MPL/2.0/. */
/// Geometry in flow-relative space.
use geom::{Size2D, Point2D, SideOffsets2D, Rect};
use std::fmt::{Show, Formatter, FormatError};
use std::num::Zero;
bitflags!(
flags WritingMode: u8 {
static FlagRTL = 1 << 0,
static FlagVertical = 1 << 1,
static FlagVerticalLR = 1 << 2,
static FlagSidewaysLeft = 1 << 3
}
)
impl WritingMode {
#[inline]
pub fn is_vertical(&self) -> bool {
self.intersects(FlagVertical)
}
/// Asuming .is_vertical(), does the block direction go left to right?
#[inline]
pub fn is_vertical_lr(&self) -> bool {
self.intersects(FlagVerticalLR)
}
/// Asuming .is_vertical(), does the inline direction go top to bottom?
#[inline]
pub fn is_inline_tb(&self) -> bool {
!(self.intersects(FlagSidewaysLeft) ^ self.intersects(FlagRTL))
}
#[inline]
pub fn is_bidi_ltr(&self) -> bool {
!self.intersects(FlagRTL)
}
}
impl Show for WritingMode {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
if self.is_vertical() {
try!(write!(formatter, "V"));
if self.is_vertical_lr() {
try!(write!(formatter, " LR"));
} else {
try!(write!(formatter, " RL"));
}
if self.intersects(FlagSidewaysLeft) {
try!(write!(formatter, " SidewaysL"));
}
} else {
try!(write!(formatter, "H"));
}
if self.is_bidi_ltr() {
write!(formatter, " LTR")
} else {
write!(formatter, " RTL")
}
}
}
/// Wherever logical geometry is used, the writing mode is known based on context:
/// every method takes a `mode` parameter.
/// However, this context is easy to get wrong.
/// In debug builds only, logical geometry objects store their writing mode
/// (in addition to taking it as a parameter to methods) and check it.
/// In non-debug builds, make this storage zero-size and the checks no-ops.
#[cfg(ndebug)]
#[deriving(PartialEq, Eq, Clone)]
struct DebugWritingMode;
#[cfg(not(ndebug))]
#[deriving(PartialEq, Eq, Clone)]
struct DebugWritingMode {
mode: WritingMode
}
#[cfg(ndebug)]
impl DebugWritingMode {
#[inline]
fn check(&self, _other: WritingMode) {}
#[inline]
fn check_debug(&self, _other: DebugWritingMode) {}
#[inline]
fn new(_mode: WritingMode) -> DebugWritingMode {
DebugWritingMode
}
}
#[cfg(not(ndebug))]
impl DebugWritingMode {
#[inline]
fn check(&self, other: WritingMode) {
assert!(self.mode == other)
}
#[inline]
fn check_debug(&self, other: DebugWritingMode) {
assert!(self.mode == other.mode)
}
#[inline]
fn new(mode: WritingMode) -> DebugWritingMode {
DebugWritingMode { mode: mode }
}
}
impl Show for DebugWritingMode {
#[cfg(ndebug)]
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
write!(formatter, "?")
}
#[cfg(not(ndebug))]
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
self.mode.fmt(formatter)
}
}
/// A 2D size in flow-relative dimensions
#[deriving(PartialEq, Eq, Clone)]
pub struct LogicalSize<T> {
pub isize: T, // inline-size (a.k.a. logical width)
pub bsize: T, // block-size (a.k.a. logical height)
debug_writing_mode: DebugWritingMode,
}
impl<T: Show> Show for LogicalSize<T> {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
write!(formatter, "LogicalSize[{}, {}, {}]",
self.debug_writing_mode, self.isize, self.bsize)
}
}
// Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
impl<T: Zero> LogicalSize<T> {
#[inline]
pub fn zero(mode: WritingMode) -> LogicalSize<T> {
LogicalSize {
isize: Zero::zero(),
bsize: Zero::zero(),
debug_writing_mode: DebugWritingMode::new(mode),
}
}
#[inline]
pub fn is_zero(&self) -> bool {
self.isize.is_zero() && self.bsize.is_zero()
}
}
impl<T: Copy> LogicalSize<T> {
#[inline]
pub fn new(mode: WritingMode, isize: T, bsize: T) -> LogicalSize<T> {
LogicalSize {
isize: isize,
bsize: bsize,
debug_writing_mode: DebugWritingMode::new(mode),
}
}
#[inline]
pub fn from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T> {
if mode.is_vertical() {
LogicalSize::new(mode, size.height, size.width)
} else {
LogicalSize::new(mode, size.width, size.height)
}
}
#[inline]
pub fn width(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
self.bsize
} else {
self.isize
}
}
#[inline]
pub fn set_width(&mut self, mode: WritingMode, width: T) {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
self.bsize = width
} else {
self.isize = width
}
}
#[inline]
pub fn height(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
self.isize
} else {
self.bsize
}
}
#[inline]
pub fn set_height(&mut self, mode: WritingMode, height: T) {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
self.isize = height
} else {
self.bsize = height
}
}
#[inline]
pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
Size2D { width: self.bsize, height: self.isize }
} else {
Size2D { width: self.isize, height: self.bsize }
}
}
#[inline]
pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T> {
if mode_from == mode_to {
self.debug_writing_mode.check(mode_from);
*self
} else {
LogicalSize::from_physical(mode_to, self.to_physical(mode_from))
}
}
}
impl<T: Add<T, T>> Add<LogicalSize<T>, LogicalSize<T>> for LogicalSize<T> {
#[inline]
fn add(&self, other: &LogicalSize<T>) -> LogicalSize<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalSize {
debug_writing_mode: self.debug_writing_mode,
isize: self.isize + other.isize,
bsize: self.bsize + other.bsize,
}
}
}
impl<T: Sub<T, T>> Sub<LogicalSize<T>, LogicalSize<T>> for LogicalSize<T> {
#[inline]
fn sub(&self, other: &LogicalSize<T>) -> LogicalSize<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalSize {
debug_writing_mode: self.debug_writing_mode,
isize: self.isize - other.isize,
bsize: self.bsize - other.bsize,
}
}
}
/// A 2D point in flow-relative dimensions
#[deriving(PartialEq, Eq, Clone)]
pub struct LogicalPoint<T> {
pub i: T, /// inline-axis coordinate
pub b: T, /// block-axis coordinate
debug_writing_mode: DebugWritingMode,
}
impl<T: Show> Show for LogicalPoint<T> {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
write!(formatter, "LogicalPoint[{}, {}, {}]",
self.debug_writing_mode, self.i, self.b)
}
}
// Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
impl<T: Zero> LogicalPoint<T> {
#[inline]
pub fn zero(mode: WritingMode) -> LogicalPoint<T> {
LogicalPoint {
i: Zero::zero(),
b: Zero::zero(),
debug_writing_mode: DebugWritingMode::new(mode),
}
}
#[inline]
pub fn is_zero(&self) -> bool {
self.i.is_zero() && self.b.is_zero()
}
}
impl<T: Copy> LogicalPoint<T> {
#[inline]
pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T> {
LogicalPoint {
i: i,
b: b,
debug_writing_mode: DebugWritingMode::new(mode),
}
}
}
impl<T: Copy + Sub<T, T>> LogicalPoint<T> {
#[inline]
pub fn from_physical(mode: WritingMode, point: Point2D<T>, container_size: Size2D<T>)
-> LogicalPoint<T> {
if mode.is_vertical() {
LogicalPoint {
i: if mode.is_inline_tb() { point.y } else { container_size.height - point.y },
b: if mode.is_vertical_lr() { point.x } else { container_size.width - point.x },
debug_writing_mode: DebugWritingMode::new(mode),
}
} else {
LogicalPoint {
i: if mode.is_bidi_ltr() { point.x } else { container_size.width - point.x },
b: point.y,
debug_writing_mode: DebugWritingMode::new(mode),
}
}
}
#[inline]
pub fn x(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
if mode.is_vertical_lr() { self.b } else { container_size.width - self.b }
} else {
if mode.is_bidi_ltr() { self.i } else { container_size.width - self.i }
}
}
#[inline]
pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>) {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
self.b = if mode.is_vertical_lr() { x } else { container_size.width - x }
} else {
self.i = if mode.is_bidi_ltr() { x } else { container_size.width - x }
}
}
#[inline]
pub fn y(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
if mode.is_inline_tb() { self.i } else { container_size.height - self.i }
} else {
self.b
}
}
#[inline]
pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>) {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
self.i = if mode.is_inline_tb() { y } else { container_size.height - y }
} else {
self.b = y
}
}
#[inline]
pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
Point2D {
x: if mode.is_vertical_lr() { self.b } else { container_size.width - self.b },
y: if mode.is_inline_tb() { self.i } else { container_size.height - self.i }
}
} else {
Point2D {
x: if mode.is_bidi_ltr() { self.i } else { container_size.width - self.i },
y: self.b
}
}
}
#[inline]
pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode, container_size: Size2D<T>)
-> LogicalPoint<T> {
if mode_from == mode_to {
self.debug_writing_mode.check(mode_from);
*self
} else {
LogicalPoint::from_physical(
mode_to, self.to_physical(mode_from, container_size), container_size)
}
}
}
impl<T: Add<T,T>> Add<LogicalSize<T>, LogicalPoint<T>> for LogicalPoint<T> {
#[inline]
fn add(&self, other: &LogicalSize<T>) -> LogicalPoint<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalPoint {
debug_writing_mode: self.debug_writing_mode,
i: self.i + other.isize,
b: self.b + other.bsize,
}
}
}
impl<T: Sub<T,T>> Sub<LogicalSize<T>, LogicalPoint<T>> for LogicalPoint<T> {
#[inline]
fn sub(&self, other: &LogicalSize<T>) -> LogicalPoint<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalPoint {
debug_writing_mode: self.debug_writing_mode,
i: self.i - other.isize,
b: self.b - other.bsize,
}
}
}
/// A "margin" in flow-relative dimensions
/// Represents the four sides of the margins, borders, or padding of a CSS box,
/// or a combination of those.
/// A positive "margin" can be added to a rectangle to obtain a bigger rectangle.
#[deriving(PartialEq, Eq, Clone)]
pub struct LogicalMargin<T> {
pub bstart: T,
pub iend: T,
pub bend: T,
pub istart: T,
debug_writing_mode: DebugWritingMode,
}
impl<T: Show> Show for LogicalMargin<T> {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
write!(formatter, "LogicalMargin[{}, bstart: {}, iend: {}, bend: {}, istart: {}]",
self.debug_writing_mode, self.bstart, self.iend, self.bend, self.istart)
}
}
impl<T: Zero> LogicalMargin<T> {
#[inline]
pub fn zero(mode: WritingMode) -> LogicalMargin<T> {
LogicalMargin {
bstart: Zero::zero(),
iend: Zero::zero(),
bend: Zero::zero(),
istart: Zero::zero(),
debug_writing_mode: DebugWritingMode::new(mode),
}
}
#[inline]
pub fn is_zero(&self) -> bool {
self.bstart.is_zero() &&
self.iend.is_zero() &&
self.bend.is_zero() &&
self.istart.is_zero()
}
}
impl<T: Copy> LogicalMargin<T> {
#[inline]
pub fn new(mode: WritingMode, bstart: T, iend: T, bend: T, istart: T) -> LogicalMargin<T> {
LogicalMargin {
bstart: bstart,
iend: iend,
bend: bend,
istart: istart,
debug_writing_mode: DebugWritingMode::new(mode),
}
}
#[inline]
pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> {
let bstart;
let iend;
let bend;
let istart;
if mode.is_vertical() {
if mode.is_vertical_lr() {
bstart = offsets.left;
bend = offsets.right;
} else {
bstart = offsets.right;
bend = offsets.left;
}
if mode.is_inline_tb() {
istart = offsets.top;
iend = offsets.bottom;
} else {
istart = offsets.bottom;
iend = offsets.top;
}
} else {
bstart = offsets.top;
bend = offsets.bottom;
if mode.is_bidi_ltr() {
istart = offsets.left;
iend = offsets.right;
} else {
istart = offsets.right;
iend = offsets.left;
}
}
LogicalMargin::new(mode, bstart, iend, bend, istart)
}
#[inline]
pub fn top(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
if mode.is_inline_tb() { self.istart } else { self.iend }
} else {
self.bstart
}
}
#[inline]
pub fn set_top(&mut self, mode: WritingMode, top: T) {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
if mode.is_inline_tb() { self.istart = top } else { self.iend = top }
} else {
self.bstart = top
}
}
#[inline]
pub fn right(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
if mode.is_vertical_lr() { self.bend } else { self.bstart }
} else {
if mode.is_bidi_ltr() { self.iend } else { self.istart }
}
}
#[inline]
pub fn set_right(&mut self, mode: WritingMode, right: T) {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
if mode.is_vertical_lr() { self.bend = right } else { self.bstart = right }
} else {
if mode.is_bidi_ltr() { self.iend = right } else { self.istart = right }
}
}
#[inline]
pub fn bottom(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
if mode.is_inline_tb() { self.iend } else { self.istart }
} else {
self.bend
}
}
#[inline]
pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
if mode.is_inline_tb() { self.iend = bottom } else { self.istart = bottom }
} else {
self.bend = bottom
}
}
#[inline]
pub fn left(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
if mode.is_vertical_lr() { self.bstart } else { self.bend }
} else {
if mode.is_bidi_ltr() { self.istart } else { self.iend }
}
}
#[inline]
pub fn set_left(&mut self, mode: WritingMode, left: T) {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
if mode.is_vertical_lr() { self.bstart = left } else { self.bend = left }
} else {
if mode.is_bidi_ltr() { self.istart = left } else { self.iend = left }
}
}
#[inline]
pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> {
self.debug_writing_mode.check(mode);
let top;
let right;
let bottom;
let left;
if mode.is_vertical() {
if mode.is_vertical_lr() {
left = self.bstart;
right = self.bend;
} else {
right = self.bstart;
left = self.bend;
}
if mode.is_inline_tb() {
top = self.istart;
bottom = self.iend;
} else {
bottom = self.istart;
top = self.iend;
}
} else {
top = self.bstart;
bottom = self.bend;
if mode.is_bidi_ltr() {
left = self.istart;
right = self.iend;
} else {
right = self.istart;
left = self.iend;
}
}
SideOffsets2D::new(top, right, bottom, left)
}
#[inline]
pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> {
if mode_from == mode_to {
self.debug_writing_mode.check(mode_from);
*self
} else {
LogicalMargin::from_physical(mode_to, self.to_physical(mode_from))
}
}
}
impl<T: Add<T, T>> LogicalMargin<T> {
#[inline]
pub fn istart_end(&self) -> T {
self.istart + self.iend
}
#[inline]
pub fn bstart_end(&self) -> T {
self.bstart + self.bend
}
#[inline]
pub fn top_bottom(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
self.istart_end()
} else {
self.bstart_end()
}
}
#[inline]
pub fn left_right(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode);
if mode.is_vertical() {
self.bstart_end()
} else {
self.istart_end()
}
}
}
impl<T: Add<T, T>> Add<LogicalMargin<T>, LogicalMargin<T>> for LogicalMargin<T> {
#[inline]
fn add(&self, other: &LogicalMargin<T>) -> LogicalMargin<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalMargin {
debug_writing_mode: self.debug_writing_mode,
bstart: self.bstart + other.bstart,
iend: self.iend + other.iend,
bend: self.bend + other.bend,
istart: self.istart + other.istart,
}
}
}
impl<T: Sub<T, T>> Sub<LogicalMargin<T>, LogicalMargin<T>> for LogicalMargin<T> {
#[inline]
fn sub(&self, other: &LogicalMargin<T>) -> LogicalMargin<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalMargin {
debug_writing_mode: self.debug_writing_mode,
bstart: self.bstart - other.bstart,
iend: self.iend - other.iend,
bend: self.bend - other.bend,
istart: self.istart - other.istart,
}
}
}
/// A rectangle in flow-relative dimensions
#[deriving(PartialEq, Eq, Clone)]
pub struct LogicalRect<T> {
pub start: LogicalPoint<T>,
pub size: LogicalSize<T>,
debug_writing_mode: DebugWritingMode,
}
impl<T: Show> Show for LogicalRect<T> {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
write!(formatter, "LogicalRect[{}, istart: {}, bstart: {}, isize: {}, bsize: {}]",
self.debug_writing_mode, self.start.i, self.start.b,
self.size.isize, self.size.bsize)
}
}
impl<T: Zero> LogicalRect<T> {
#[inline]
pub fn zero(mode: WritingMode) -> LogicalRect<T> {
LogicalRect {
start: LogicalPoint::zero(mode),
size: LogicalSize::zero(mode),
debug_writing_mode: DebugWritingMode::new(mode),
}
}
#[inline]
pub fn is_zero(&self) -> bool {
self.start.is_zero() && self.size.is_zero()
}
}
impl<T: Copy> LogicalRect<T> {
#[inline]
pub fn new(mode: WritingMode, istart: T, bstart: T, isize: T, bsize: T) -> LogicalRect<T> {
LogicalRect {
start: LogicalPoint::new(mode, istart, bstart),
size: LogicalSize::new(mode, isize, bsize),
debug_writing_mode: DebugWritingMode::new(mode),
}
}
}
impl<T: Copy + Add<T, T> + Sub<T, T>> LogicalRect<T> {
#[inline]
pub fn from_physical(mode: WritingMode, rect: Rect<T>, container_size: Size2D<T>)
-> LogicalRect<T> {
let istart;
let bstart;
let isize;
let bsize;
if mode.is_vertical() {
isize = rect.size.height;
bsize = rect.size.width;
if mode.is_vertical_lr() {
bstart = rect.origin.x;
} else {
bstart = container_size.width - (rect.origin.x + rect.size.width);
}
if mode.is_inline_tb() {
istart = rect.origin.y;
} else {
istart = container_size.height - (rect.origin.y + rect.size.height);
}
} else {
isize = rect.size.width;
bsize = rect.size.height;
bstart = rect.origin.y;
if mode.is_bidi_ltr() {
istart = rect.origin.x;
} else {
istart = container_size.width - (rect.origin.x + rect.size.width);
}
}
LogicalRect {
start: LogicalPoint::new(mode, istart, bstart),
size: LogicalSize::new(mode, isize, bsize),
debug_writing_mode: DebugWritingMode::new(mode),
}
}
#[inline]
pub fn iend(&self) -> T {
self.start.i + self.size.isize
}
#[inline]
pub fn bend(&self) -> T {
self.start.b + self.size.bsize
}
#[inline]
pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> {
self.debug_writing_mode.check(mode);
let x;
let y;
let width;
let height;
if mode.is_vertical() {
width = self.size.bsize;
height = self.size.isize;
if mode.is_vertical_lr() {
x = self.start.b;
} else {
x = container_size.width - self.bend();
}
if mode.is_inline_tb() {
y = self.start.i;
} else {
y = container_size.height - self.iend();
}
} else {
width = self.size.isize;
height = self.size.bsize;
y = self.start.b;
if mode.is_bidi_ltr() {
x = self.start.i;
} else {
x = container_size.width - self.iend();
}
}
Rect {
origin: Point2D { x: x, y: y },
size: Size2D { width: width, height: height },
}
}
#[inline]
pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode, container_size: Size2D<T>)
-> LogicalRect<T> {
if mode_from == mode_to {
self.debug_writing_mode.check(mode_from);
*self
} else {
LogicalRect::from_physical(
mode_to, self.to_physical(mode_from, container_size), container_size)
}
}
}
impl<T: Add<T, T> + Sub<T, T>> Add<LogicalMargin<T>, LogicalRect<T>> for LogicalRect<T> {
#[inline]
fn add(&self, other: &LogicalMargin<T>) -> LogicalRect<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalRect {
start: LogicalPoint {
// Growing a rectangle on the start side means pushing its
// start point on the negative direction.
i: self.start.i - other.istart,
b: self.start.b - other.bstart,
debug_writing_mode: self.debug_writing_mode,
},
size: LogicalSize {
isize: self.size.isize + other.istart_end(),
bsize: self.size.bsize + other.bstart_end(),
debug_writing_mode: self.debug_writing_mode,
},
debug_writing_mode: self.debug_writing_mode,
}
}
}
impl<T: Add<T, T> + Sub<T, T>> Sub<LogicalMargin<T>, LogicalRect<T>> for LogicalRect<T> {
#[inline]
fn sub(&self, other: &LogicalMargin<T>) -> LogicalRect<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalRect {
start: LogicalPoint {
// Shrinking a rectangle on the start side means pushing its
// start point on the positive direction.
i: self.start.i + other.istart,
b: self.start.b + other.bstart,
debug_writing_mode: self.debug_writing_mode,
},
size: LogicalSize {
isize: self.size.isize - other.istart_end(),
bsize: self.size.bsize - other.bstart_end(),
debug_writing_mode: self.debug_writing_mode,
},
debug_writing_mode: self.debug_writing_mode,
}
}
}
#[cfg(test)]
fn modes() -> [WritingMode, ..10] {
[
WritingMode::empty(),
FlagVertical,
FlagVertical | FlagVerticalLR,
FlagVertical | FlagVerticalLR | FlagSidewaysLeft,
FlagVertical | FlagSidewaysLeft,
FlagRTL,
FlagVertical | FlagRTL,
FlagVertical | FlagVerticalLR | FlagRTL,
FlagVertical | FlagVerticalLR | FlagSidewaysLeft | FlagRTL,
FlagVertical | FlagSidewaysLeft | FlagRTL,
]
}
#[test]
fn test_size_round_trip() {
let physical = Size2D(1, 2);
for &mode in modes().iter() {
let logical = LogicalSize::from_physical(mode, physical);
assert!(logical.to_physical(mode) == physical);
assert!(logical.width(mode) == 1);
assert!(logical.height(mode) == 2);
}
}
#[test]
fn test_point_round_trip() {
let physical = Point2D(1, 2);
let container = Size2D(100, 200);
for &mode in modes().iter() {
let logical = LogicalPoint::from_physical(mode, physical, container);
assert!(logical.to_physical(mode, container) == physical);
assert!(logical.x(mode, container) == 1);
assert!(logical.y(mode, container) == 2);
}
}
#[test]
fn test_margin_round_trip() {
let physical = SideOffsets2D::new(1, 2, 3, 4);
for &mode in modes().iter() {
let logical = LogicalMargin::from_physical(mode, physical);
assert!(logical.to_physical(mode) == physical);
assert!(logical.top(mode) == 1);
assert!(logical.right(mode) == 2);
assert!(logical.bottom(mode) == 3);
assert!(logical.left(mode) == 4);
}
}
#[test]
fn test_rect_round_trip() {
let physical = Rect(Point2D(1, 2), Size2D(3, 4));
let container = Size2D(100, 200);
for &mode in modes().iter() {
let logical = LogicalRect::from_physical(mode, physical, container);
assert!(logical.to_physical(mode, container) == physical);
}
}

Просмотреть файл

@ -32,6 +32,7 @@ extern crate std_url = "url";
pub mod cache; pub mod cache;
pub mod debug_utils; pub mod debug_utils;
pub mod geometry; pub mod geometry;
pub mod logical_geometry;
pub mod memory; pub mod memory;
pub mod namespace; pub mod namespace;
pub mod opts; pub mod opts;