Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
First pass at implementing the Flex Layout Algorithm
- Loading branch information
1 parent
080f5bb
commit 0190592
Showing
6 changed files
with
1,381 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
/* 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/. */ | ||
|
||
//! https://drafts.csswg.org/css-flexbox/#box-model | ||
|
||
use crate::geom::flow_relative::{Rect, Sides, Vec2}; | ||
use style::properties::longhands::flex_direction::computed_value::T as FlexDirection; | ||
|
||
#[derive(Clone, Copy)] | ||
pub(super) struct FlexRelativeVec2<T> { | ||
pub main: T, | ||
pub cross: T, | ||
} | ||
|
||
#[derive(Clone, Copy)] | ||
pub(super) struct FlexRelativeSides<T> { | ||
pub cross_start: T, | ||
pub main_start: T, | ||
pub cross_end: T, | ||
pub main_end: T, | ||
} | ||
|
||
pub(super) struct FlexRelativeRect<T> { | ||
pub start_corner: FlexRelativeVec2<T>, | ||
pub size: FlexRelativeVec2<T>, | ||
} | ||
|
||
impl<T> std::ops::Add for FlexRelativeVec2<T> | ||
where | ||
T: std::ops::Add, | ||
{ | ||
type Output = FlexRelativeVec2<T::Output>; | ||
fn add(self, rhs: Self) -> Self::Output { | ||
FlexRelativeVec2 { | ||
main: self.main + rhs.main, | ||
cross: self.cross + rhs.cross, | ||
} | ||
} | ||
} | ||
|
||
impl<T> std::ops::Sub for FlexRelativeVec2<T> | ||
where | ||
T: std::ops::Sub, | ||
{ | ||
type Output = FlexRelativeVec2<T::Output>; | ||
fn sub(self, rhs: Self) -> Self::Output { | ||
FlexRelativeVec2 { | ||
main: self.main - rhs.main, | ||
cross: self.cross - rhs.cross, | ||
} | ||
} | ||
} | ||
|
||
impl<T> FlexRelativeSides<T> { | ||
pub fn sum_by_axis(self) -> FlexRelativeVec2<T::Output> | ||
where | ||
T: std::ops::Add, | ||
{ | ||
FlexRelativeVec2 { | ||
main: self.main_start + self.main_end, | ||
cross: self.cross_start + self.cross_end, | ||
} | ||
} | ||
} | ||
|
||
/// One of the two bits set by the `flex-direction` property | ||
/// (The other is "forward" v.s. reverse.) | ||
#[derive(Clone, Copy, PartialEq)] | ||
pub(super) enum FlexAxis { | ||
/// The main axis is the inline axis of the container (not necessarily of flex items!), | ||
/// cross is block. | ||
Row, | ||
/// The main axis is the block axis, cross is inline. | ||
Column, | ||
} | ||
|
||
/// Which flow-relative sides map to the main-start and cross-start sides, respectively. | ||
/// See https://drafts.csswg.org/css-flexbox/#box-model | ||
#[derive(Clone, Copy)] | ||
pub(super) enum MainStartCrossStart { | ||
InlineStartBlockStart, | ||
InlineStartBlockEnd, | ||
BlockStartInlineStart, | ||
BlockStartInlineEnd, | ||
InlineEndBlockStart, | ||
InlineEndBlockEnd, | ||
BlockEndInlineStart, | ||
BlockEndInlineEnd, | ||
} | ||
|
||
impl FlexAxis { | ||
pub fn from(flex_direction: FlexDirection) -> Self { | ||
match flex_direction { | ||
FlexDirection::Row | FlexDirection::RowReverse => FlexAxis::Row, | ||
FlexDirection::Column | FlexDirection::ColumnReverse => FlexAxis::Column, | ||
} | ||
} | ||
|
||
pub fn vec2_to_flex_relative<T>(self, flow_relative: Vec2<T>) -> FlexRelativeVec2<T> { | ||
let Vec2 { inline, block } = flow_relative; | ||
match self { | ||
FlexAxis::Row => FlexRelativeVec2 { | ||
main: inline, | ||
cross: block, | ||
}, | ||
FlexAxis::Column => FlexRelativeVec2 { | ||
main: block, | ||
cross: inline, | ||
}, | ||
} | ||
} | ||
|
||
pub fn vec2_to_flow_relative<T>(self, flex_relative: FlexRelativeVec2<T>) -> Vec2<T> { | ||
let FlexRelativeVec2 { main, cross } = flex_relative; | ||
match self { | ||
FlexAxis::Row => Vec2 { | ||
inline: main, | ||
block: cross, | ||
}, | ||
FlexAxis::Column => Vec2 { | ||
block: main, | ||
inline: cross, | ||
}, | ||
} | ||
} | ||
} | ||
|
||
macro_rules! sides_mapping_methods { | ||
( | ||
$( | ||
$variant: path => { | ||
$( $flex_relative_side: ident <=> $flow_relative_side: ident, )+ | ||
}, | ||
)+ | ||
) => { | ||
pub fn sides_to_flex_relative<T>(self, flow_relative: Sides<T>) -> FlexRelativeSides<T> { | ||
match self { | ||
$( | ||
$variant => FlexRelativeSides { | ||
$( $flex_relative_side: flow_relative.$flow_relative_side, )+ | ||
}, | ||
)+ | ||
} | ||
} | ||
|
||
pub fn sides_to_flow_relative<T>(self, flex_relative: FlexRelativeSides<T>) -> Sides<T> { | ||
match self { | ||
$( | ||
$variant => Sides { | ||
$( $flow_relative_side: flex_relative.$flex_relative_side, )+ | ||
}, | ||
)+ | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl MainStartCrossStart { | ||
pub fn from(flex_direction: FlexDirection, flex_wrap_reverse: bool) -> Self { | ||
match (flex_direction, flex_wrap_reverse) { | ||
// See definition of each keyword in | ||
// https://drafts.csswg.org/css-flexbox/#flex-direction-property and | ||
// https://drafts.csswg.org/css-flexbox/#flex-wrap-property, | ||
// or the tables (though they map to physical rather than flow-relative) at | ||
// https://drafts.csswg.org/css-flexbox/#axis-mapping | ||
(FlexDirection::Row, true) => MainStartCrossStart::InlineStartBlockEnd, | ||
(FlexDirection::Row, false) => MainStartCrossStart::InlineStartBlockStart, | ||
(FlexDirection::Column, true) => MainStartCrossStart::BlockStartInlineEnd, | ||
(FlexDirection::Column, false) => MainStartCrossStart::BlockStartInlineStart, | ||
(FlexDirection::RowReverse, true) => MainStartCrossStart::InlineEndBlockEnd, | ||
(FlexDirection::RowReverse, false) => MainStartCrossStart::InlineEndBlockStart, | ||
(FlexDirection::ColumnReverse, true) => MainStartCrossStart::BlockEndInlineEnd, | ||
(FlexDirection::ColumnReverse, false) => MainStartCrossStart::BlockEndInlineStart, | ||
} | ||
} | ||
|
||
sides_mapping_methods! { | ||
MainStartCrossStart::InlineStartBlockStart => { | ||
main_start <=> inline_start, | ||
cross_start <=> block_start, | ||
main_end <=> inline_end, | ||
cross_end <=> block_end, | ||
}, | ||
MainStartCrossStart::InlineStartBlockEnd => { | ||
main_start <=> inline_start, | ||
cross_start <=> block_end, | ||
main_end <=> inline_end, | ||
cross_end <=> block_start, | ||
}, | ||
MainStartCrossStart::BlockStartInlineStart => { | ||
main_start <=> block_start, | ||
cross_start <=> inline_start, | ||
main_end <=> block_end, | ||
cross_end <=> inline_end, | ||
}, | ||
MainStartCrossStart::BlockStartInlineEnd => { | ||
main_start <=> block_start, | ||
cross_start <=> inline_end, | ||
main_end <=> block_end, | ||
cross_end <=> inline_start, | ||
}, | ||
MainStartCrossStart::InlineEndBlockStart => { | ||
main_start <=> inline_end, | ||
cross_start <=> block_start, | ||
main_end <=> inline_start, | ||
cross_end <=> block_end, | ||
}, | ||
MainStartCrossStart::InlineEndBlockEnd => { | ||
main_start <=> inline_end, | ||
cross_start <=> block_end, | ||
main_end <=> inline_start, | ||
cross_end <=> block_start, | ||
}, | ||
MainStartCrossStart::BlockEndInlineStart => { | ||
main_start <=> block_end, | ||
cross_start <=> inline_start, | ||
main_end <=> block_start, | ||
cross_end <=> inline_end, | ||
}, | ||
MainStartCrossStart::BlockEndInlineEnd => { | ||
main_start <=> block_end, | ||
cross_start <=> inline_end, | ||
main_end <=> block_start, | ||
cross_end <=> inline_start, | ||
}, | ||
} | ||
} | ||
|
||
/// The start corner coordinates in both the input rectangle and output rectangle | ||
/// are relative to some “base rectangle” whose size is passed here. | ||
pub(super) fn rect_to_flow_relative<T>( | ||
flex_axis: FlexAxis, | ||
main_start_cross_start_sides_are: MainStartCrossStart, | ||
base_rect_size: FlexRelativeVec2<T>, | ||
rect: FlexRelativeRect<T>, | ||
) -> Rect<T> | ||
where | ||
T: Copy + std::ops::Add<Output = T> + std::ops::Sub<Output = T>, | ||
{ | ||
// First, convert from (start corner, size) to offsets from the edges of the base rectangle | ||
|
||
let end_corner_position = rect.start_corner + rect.size; | ||
let end_corner_offsets = base_rect_size - end_corner_position; | ||
// No-ops, but hopefully clarifies to human readers: | ||
let start_corner_position = rect.start_corner; | ||
let start_corner_offsets = start_corner_position; | ||
|
||
// Then, convert to flow-relative using methods above | ||
let flow_relative_offsets = | ||
main_start_cross_start_sides_are.sides_to_flow_relative(FlexRelativeSides { | ||
main_start: start_corner_offsets.main, | ||
cross_start: start_corner_offsets.cross, | ||
main_end: end_corner_offsets.main, | ||
cross_end: end_corner_offsets.cross, | ||
}); | ||
let flow_relative_base_rect_size = flex_axis.vec2_to_flow_relative(base_rect_size); | ||
|
||
// Finally, convert back to (start corner, size) | ||
let start_corner = Vec2 { | ||
inline: flow_relative_offsets.inline_start, | ||
block: flow_relative_offsets.block_start, | ||
}; | ||
let end_corner_position = Vec2 { | ||
inline: flow_relative_base_rect_size.inline - flow_relative_offsets.inline_end, | ||
block: flow_relative_base_rect_size.block - flow_relative_offsets.block_end, | ||
}; | ||
let size = &end_corner_position - &start_corner; | ||
Rect { start_corner, size } | ||
} |
Oops, something went wrong.