mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
layout: Combine layout_2020
and layout_thread_2020
into a crate called layout
(#36613)
Now that legacy layout has been removed, the name `layout_2020` doesn't make much sense any longer, also it's 2025 now for better or worse. The split between the "layout thread" and "layout" also doesn't make as much sense since layout doesn't run on it's own thread. There's a possibility that it will in the future, but that should be something that the user of the crate controls rather than layout iself. This is part of the larger layout interface cleanup and optimization that @Looriool and I are doing. Testing: Covered by existing tests as this is just code movement. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
3ab5b8c447
commit
7787cab521
62 changed files with 58 additions and 122 deletions
272
components/layout/flexbox/geom.rs
Normal file
272
components/layout/flexbox/geom.rs
Normal file
|
@ -0,0 +1,272 @@
|
|||
/* 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 malloc_size_of_derive::MallocSizeOf;
|
||||
use style::properties::longhands::flex_direction::computed_value::T as FlexDirection;
|
||||
|
||||
use crate::geom::{LogicalRect, LogicalSides, LogicalVec2};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub(super) struct FlexRelativeVec2<T> {
|
||||
pub main: T,
|
||||
pub cross: T,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
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, Debug, MallocSizeOf, 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, Debug, MallocSizeOf)]
|
||||
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: LogicalVec2<T>) -> FlexRelativeVec2<T> {
|
||||
let LogicalVec2 { 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>) -> LogicalVec2<T> {
|
||||
let FlexRelativeVec2 { main, cross } = flex_relative;
|
||||
match self {
|
||||
FlexAxis::Row => LogicalVec2 {
|
||||
inline: main,
|
||||
block: cross,
|
||||
},
|
||||
FlexAxis::Column => LogicalVec2 {
|
||||
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: LogicalSides<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>) -> LogicalSides<T> {
|
||||
match self {
|
||||
$(
|
||||
$variant => LogicalSides {
|
||||
$( $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>,
|
||||
) -> LogicalRect<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 = LogicalVec2 {
|
||||
inline: flow_relative_offsets.inline_start,
|
||||
block: flow_relative_offsets.block_start,
|
||||
};
|
||||
let end_corner_position = LogicalVec2 {
|
||||
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;
|
||||
LogicalRect { start_corner, size }
|
||||
}
|
2739
components/layout/flexbox/layout.rs
Normal file
2739
components/layout/flexbox/layout.rs
Normal file
File diff suppressed because it is too large
Load diff
201
components/layout/flexbox/mod.rs
Normal file
201
components/layout/flexbox/mod.rs
Normal file
|
@ -0,0 +1,201 @@
|
|||
/* 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 geom::{FlexAxis, MainStartCrossStart};
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use style::logical_geometry::WritingMode;
|
||||
use style::properties::ComputedValues;
|
||||
use style::properties::longhands::align_items::computed_value::T as AlignItems;
|
||||
use style::properties::longhands::flex_direction::computed_value::T as FlexDirection;
|
||||
use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap;
|
||||
use style::values::computed::{AlignContent, JustifyContent};
|
||||
use style::values::specified::align::AlignFlags;
|
||||
|
||||
use crate::PropagatedBoxTreeData;
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::construct_modern::{ModernContainerBuilder, ModernItemKind};
|
||||
use crate::context::LayoutContext;
|
||||
use crate::dom::{LayoutBox, NodeExt};
|
||||
use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents};
|
||||
use crate::formatting_contexts::IndependentFormattingContext;
|
||||
use crate::fragment_tree::{BaseFragmentInfo, Fragment};
|
||||
use crate::positioned::AbsolutelyPositionedBox;
|
||||
|
||||
mod geom;
|
||||
mod layout;
|
||||
|
||||
/// A structure to hold the configuration of a flex container for use during layout
|
||||
/// and preferred width calculation.
|
||||
#[derive(Clone, Debug, MallocSizeOf)]
|
||||
pub(crate) struct FlexContainerConfig {
|
||||
container_is_single_line: bool,
|
||||
writing_mode: WritingMode,
|
||||
flex_axis: FlexAxis,
|
||||
flex_direction: FlexDirection,
|
||||
flex_direction_is_reversed: bool,
|
||||
flex_wrap: FlexWrap,
|
||||
flex_wrap_is_reversed: bool,
|
||||
main_start_cross_start_sides_are: MainStartCrossStart,
|
||||
align_content: AlignContent,
|
||||
align_items: AlignItems,
|
||||
justify_content: JustifyContent,
|
||||
}
|
||||
|
||||
impl FlexContainerConfig {
|
||||
fn new(container_style: &ComputedValues) -> FlexContainerConfig {
|
||||
let flex_direction = container_style.clone_flex_direction();
|
||||
let flex_axis = FlexAxis::from(flex_direction);
|
||||
let flex_wrap = container_style.get_position().flex_wrap;
|
||||
let container_is_single_line = match flex_wrap {
|
||||
FlexWrap::Nowrap => true,
|
||||
FlexWrap::Wrap | FlexWrap::WrapReverse => false,
|
||||
};
|
||||
let flex_direction_is_reversed = match flex_direction {
|
||||
FlexDirection::Row | FlexDirection::Column => false,
|
||||
FlexDirection::RowReverse | FlexDirection::ColumnReverse => true,
|
||||
};
|
||||
let flex_wrap_reverse = match flex_wrap {
|
||||
FlexWrap::Nowrap | FlexWrap::Wrap => false,
|
||||
FlexWrap::WrapReverse => true,
|
||||
};
|
||||
|
||||
let align_content = container_style.clone_align_content();
|
||||
let align_items = AlignItems(match container_style.clone_align_items().0 {
|
||||
AlignFlags::AUTO | AlignFlags::NORMAL => AlignFlags::STRETCH,
|
||||
align => align,
|
||||
});
|
||||
let justify_content = container_style.clone_justify_content();
|
||||
let main_start_cross_start_sides_are =
|
||||
MainStartCrossStart::from(flex_direction, flex_wrap_reverse);
|
||||
|
||||
FlexContainerConfig {
|
||||
container_is_single_line,
|
||||
writing_mode: container_style.writing_mode,
|
||||
flex_axis,
|
||||
flex_direction,
|
||||
flex_direction_is_reversed,
|
||||
flex_wrap,
|
||||
flex_wrap_is_reversed: flex_wrap_reverse,
|
||||
main_start_cross_start_sides_are,
|
||||
align_content,
|
||||
align_items,
|
||||
justify_content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, MallocSizeOf)]
|
||||
pub(crate) struct FlexContainer {
|
||||
children: Vec<ArcRefCell<FlexLevelBox>>,
|
||||
|
||||
#[conditional_malloc_size_of]
|
||||
style: ServoArc<ComputedValues>,
|
||||
|
||||
/// The configuration of this [`FlexContainer`].
|
||||
config: FlexContainerConfig,
|
||||
}
|
||||
|
||||
impl FlexContainer {
|
||||
pub fn construct<'dom>(
|
||||
context: &LayoutContext,
|
||||
info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
|
||||
contents: NonReplacedContents,
|
||||
propagated_data: PropagatedBoxTreeData,
|
||||
) -> Self {
|
||||
let mut builder =
|
||||
ModernContainerBuilder::new(context, info, propagated_data.union(&info.style));
|
||||
contents.traverse(context, info, &mut builder);
|
||||
let items = builder.finish();
|
||||
|
||||
let children = items
|
||||
.into_iter()
|
||||
.map(|item| {
|
||||
let box_ = match item.kind {
|
||||
ModernItemKind::InFlow => ArcRefCell::new(FlexLevelBox::FlexItem(
|
||||
FlexItemBox::new(item.formatting_context),
|
||||
)),
|
||||
ModernItemKind::OutOfFlow => {
|
||||
let abs_pos_box =
|
||||
ArcRefCell::new(AbsolutelyPositionedBox::new(item.formatting_context));
|
||||
ArcRefCell::new(FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(abs_pos_box))
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(box_slot) = item.box_slot {
|
||||
box_slot.set(LayoutBox::FlexLevel(box_.clone()));
|
||||
}
|
||||
|
||||
box_
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
children,
|
||||
style: info.style.clone(),
|
||||
config: FlexContainerConfig::new(&info.style),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, MallocSizeOf)]
|
||||
pub(crate) enum FlexLevelBox {
|
||||
FlexItem(FlexItemBox),
|
||||
OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
|
||||
}
|
||||
|
||||
impl FlexLevelBox {
|
||||
pub(crate) fn invalidate_cached_fragment(&self) {
|
||||
match self {
|
||||
FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
|
||||
.independent_formatting_context
|
||||
.base
|
||||
.invalidate_cached_fragment(),
|
||||
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
|
||||
.borrow()
|
||||
.context
|
||||
.base
|
||||
.invalidate_cached_fragment(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fragments(&self) -> Vec<Fragment> {
|
||||
match self {
|
||||
FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
|
||||
.independent_formatting_context
|
||||
.base
|
||||
.fragments(),
|
||||
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
||||
positioned_box.borrow().context.base.fragments()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MallocSizeOf)]
|
||||
pub(crate) struct FlexItemBox {
|
||||
independent_formatting_context: IndependentFormattingContext,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for FlexItemBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("FlexItemBox")
|
||||
}
|
||||
}
|
||||
|
||||
impl FlexItemBox {
|
||||
fn new(independent_formatting_context: IndependentFormattingContext) -> Self {
|
||||
Self {
|
||||
independent_formatting_context,
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> &ServoArc<ComputedValues> {
|
||||
self.independent_formatting_context.style()
|
||||
}
|
||||
|
||||
fn base_fragment_info(&self) -> BaseFragmentInfo {
|
||||
self.independent_formatting_context.base_fragment_info()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue