mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
First pass at implementing the Flex Layout Algorithm
https://drafts.csswg.org/css-flexbox/#layout-algorithm
This commit is contained in:
parent
080f5bb763
commit
01905923db
6 changed files with 1381 additions and 19 deletions
270
components/layout_2020/flexbox/geom.rs
Normal file
270
components/layout_2020/flexbox/geom.rs
Normal file
|
@ -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 }
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -5,9 +5,9 @@
|
|||
use crate::cell::ArcRefCell;
|
||||
use crate::formatting_contexts::IndependentFormattingContext;
|
||||
use crate::positioned::AbsolutelyPositionedBox;
|
||||
use crate::sizing::ContentSizes;
|
||||
|
||||
mod construct;
|
||||
mod geom;
|
||||
mod layout;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
|
@ -20,9 +20,3 @@ pub(crate) enum FlexLevelBox {
|
|||
FlexItem(IndependentFormattingContext),
|
||||
OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
|
||||
}
|
||||
|
||||
impl FlexContainer {
|
||||
pub fn inline_content_sizes(&self) -> ContentSizes {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,6 +134,15 @@ impl IndependentFormattingContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn inline_content_sizes(&mut self, layout_context: &LayoutContext) -> ContentSizes {
|
||||
match self {
|
||||
Self::NonReplaced(inner) => inner
|
||||
.contents
|
||||
.inline_content_sizes(layout_context, inner.style.writing_mode),
|
||||
Self::Replaced(inner) => inner.contents.inline_content_sizes(&inner.style),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn outer_inline_content_sizes(
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
|
@ -159,7 +168,9 @@ impl IndependentFormattingContext {
|
|||
let contents = &non_replaced.contents;
|
||||
sizing::outer_inline_and_percentages(&style, containing_block_writing_mode, || {
|
||||
content_sizes
|
||||
.get_or_insert_with(|| contents.inline_content_sizes(layout_context, style.writing_mode))
|
||||
.get_or_insert_with(|| {
|
||||
contents.inline_content_sizes(layout_context, style.writing_mode)
|
||||
})
|
||||
.clone()
|
||||
})
|
||||
},
|
||||
|
|
|
@ -81,6 +81,20 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Sub<&'_ flow_relative::Vec2<T>> for &'_ flow_relative::Vec2<T>
|
||||
where
|
||||
T: Sub<Output = T> + Copy,
|
||||
{
|
||||
type Output = flow_relative::Vec2<T>;
|
||||
|
||||
fn sub(self, other: &'_ flow_relative::Vec2<T>) -> Self::Output {
|
||||
flow_relative::Vec2 {
|
||||
inline: self.inline - other.inline,
|
||||
block: self.block - other.block,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AddAssign<&'_ flow_relative::Vec2<T>> for flow_relative::Vec2<T>
|
||||
where
|
||||
T: AddAssign<T> + Copy,
|
||||
|
|
|
@ -307,6 +307,12 @@ impl ToCss for CSSPixelLength {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::iter::Sum for CSSPixelLength {
|
||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||
iter.fold(Length::zero(), Add::add)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for CSSPixelLength {
|
||||
type Output = Self;
|
||||
|
||||
|
@ -323,6 +329,15 @@ impl AddAssign for CSSPixelLength {
|
|||
}
|
||||
}
|
||||
|
||||
impl Div for CSSPixelLength {
|
||||
type Output = CSSFloat;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: Self) -> CSSFloat {
|
||||
self.px() / other.px()
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<CSSFloat> for CSSPixelLength {
|
||||
type Output = Self;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue