Skip to content

Commit

Permalink
Create FlexFlow's for 'display: flex' nodes.
Browse files Browse the repository at this point in the history
This commit doesn't implement any flexbox behavior at all.
It just constructs FlexFlow's, which act just like the BlockFlow from
which they "inherit."
  • Loading branch information
Kyle Zentner authored and pcwalton committed Aug 21, 2015
1 parent 45b7ee9 commit 0488a36
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 1 deletion.
16 changes: 16 additions & 0 deletions components/layout/construct.rs
Expand Up @@ -16,6 +16,7 @@
use block::BlockFlow;
use context::LayoutContext;
use data::{HAS_NEWLY_CONSTRUCTED_FLOW, LayoutDataWrapper};
use flex::FlexFlow;
use floats::FloatKind;
use flow::{MutableFlowUtils, MutableOwnedFlowUtils};
use flow::{self, AbsoluteDescendants, Flow, ImmutableFlowUtils, IS_ABSOLUTELY_POSITIONED};
Expand Down Expand Up @@ -1277,6 +1278,14 @@ impl<'a> FlowConstructor<'a> {
ConstructionResult::Flow(flow, AbsoluteDescendants::new())
}

/// Builds a flow for a node with 'display: flex'.
fn build_flow_for_flex(&mut self, node: &ThreadSafeLayoutNode, float_kind: Option<FloatKind>)
-> ConstructionResult {
let fragment = self.build_fragment_for_block(node);
let flow = Box::new(FlexFlow::from_fragment(fragment, float_kind));
self.build_flow_for_block_like(FlowRef::new(flow), node)
}

/// Attempts to perform incremental repair to account for recent changes to this node. This
/// can fail and return false, indicating that flows will need to be reconstructed.
///
Expand Down Expand Up @@ -1518,6 +1527,13 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
self.set_flow_construction_result(node, construction_result)
}

// Flex items contribute flex flow construction results.
(display::T::flex, float_value, _) => {
let float_kind = FloatKind::from_property(float_value);
let construction_result = self.build_flow_for_flex(node, float_kind);
self.set_flow_construction_result(node, construction_result)
}

// Block flows that are not floated contribute block flow construction results.
//
// TODO(pcwalton): Make this only trigger for blocks and handle the other `display`
Expand Down
18 changes: 18 additions & 0 deletions components/layout/display_list_builder.rs
Expand Up @@ -13,6 +13,7 @@
use azure::azure_hl::Color;
use block::BlockFlow;
use context::LayoutContext;
use flex::FlexFlow;
use flow::{self, BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED, NEEDS_LAYER};
use flow_ref;
use fragment::{CoordinateSystem, Fragment, IframeFragmentInfo, ImageFragmentInfo};
Expand Down Expand Up @@ -1886,6 +1887,23 @@ impl ListItemFlowDisplayListBuilding for ListItemFlow {
}
}

pub trait FlexFlowDisplayListBuilding {
fn build_display_list_for_flex(&mut self,
display_list: Box<DisplayList>,
layout_context: &LayoutContext);
}

impl FlexFlowDisplayListBuilding for FlexFlow {
fn build_display_list_for_flex(&mut self,
display_list: Box<DisplayList>,
layout_context: &LayoutContext) {
// Draw the rest of the block.
self.as_block().build_display_list_for_block(display_list,
layout_context,
BorderPaintingMode::Separate)
}
}

trait BaseFlowDisplayListBuilding {
fn build_display_items_for_debugging_tint(&self,
display_list: &mut DisplayList,
Expand Down
241 changes: 241 additions & 0 deletions components/layout/flex.rs
@@ -0,0 +1,241 @@
/* 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/. */

//! Layout for elements with a CSS `display` property of `flex`.

#![deny(unsafe_code)]

use block::BlockFlow;
use context::LayoutContext;
use display_list_builder::FlexFlowDisplayListBuilding;
use floats::FloatKind;
use flow;
use flow::{Flow, FlowClass, OpaqueFlow};
use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS};
use flow::IS_ABSOLUTELY_POSITIONED;
use flow::mut_base;
use fragment::{Fragment, FragmentBorderBoxIterator};
use layout_debug;
use model::{IntrinsicISizes};
use style::computed_values::{flex_direction, float};
use style::properties::style_structs;
use style::values::computed::LengthOrPercentageOrAuto;

use euclid::{Point2D, Rect};
use gfx::display_list::DisplayList;
use std::cmp::max;
use std::sync::Arc;
use style::properties::ComputedValues;
use util::geometry::Au;
use util::logical_geometry::LogicalSize;
use util::opts;

// A mode describes which logical axis a flex axis is parallel with.
// The logical axises are inline and block, the flex axises are main and cross.
// When the flex container has flex-direction: column or flex-direction: column-reverse, the main axis
// should be block. Otherwise, it should be inline.
#[derive(Debug)]
enum Mode {
Inline,
Block
}

/// A block with the CSS `display` property equal to `flex`.
#[derive(Debug)]
pub struct FlexFlow {
/// Data common to all block flows.
block_flow: BlockFlow,
/// The logical axis which the main axis will be parallel with.
/// The cross axis will be parallel with the opposite logical axis.
main_mode: Mode,
}

fn flex_style(fragment: &Fragment) -> &style_structs::Flex {
fragment.style.get_flex()
}

// TODO(zentner): This function should use flex-basis.
fn flex_item_inline_sizes(flow: &mut Flow) -> IntrinsicISizes {
let _scope = layout_debug_scope!("flex::flex_item_inline_sizes");
debug!("flex_item_inline_sizes");
let base = flow::mut_base(flow);

debug!("FlexItem intrinsic inline sizes: {:?}, {:?}",
base.intrinsic_inline_sizes.minimum_inline_size,
base.intrinsic_inline_sizes.preferred_inline_size);

IntrinsicISizes {
minimum_inline_size: base.intrinsic_inline_sizes.minimum_inline_size,
preferred_inline_size: base.intrinsic_inline_sizes.preferred_inline_size,
}
}

impl FlexFlow {
pub fn from_fragment(fragment: Fragment,
flotation: Option<FloatKind>)
-> FlexFlow {

let main_mode = match flex_style(&fragment).flex_direction {
flex_direction::T::row_reverse => Mode::Inline,
flex_direction::T::row => Mode::Inline,
flex_direction::T::column_reverse => Mode::Block,
flex_direction::T::column => Mode::Block
};

let this = FlexFlow {
block_flow: BlockFlow::from_fragment(fragment, flotation),
main_mode: main_mode
};

this
}

// TODO(zentner): This function should use flex-basis.
// Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic
// stripped out, and max replaced with union_nonbreaking_inline.
fn inline_mode_bubble_inline_sizes(&mut self) {
let fixed_width = match self.block_flow.fragment.style().get_box().width {
LengthOrPercentageOrAuto::Length(_) => true,
_ => false,
};

let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
if !fixed_width {
for kid in self.block_flow.base.child_iter() {
let is_absolutely_positioned =
flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
if !is_absolutely_positioned {
computation.union_nonbreaking_inline(&flex_item_inline_sizes(kid));
}
}
}
self.block_flow.base.intrinsic_inline_sizes = computation.finish();
}

// TODO(zentner): This function should use flex-basis.
// Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic
// stripped out.
fn block_mode_bubble_inline_sizes(&mut self) {
let fixed_width = match self.block_flow.fragment.style().get_box().width {
LengthOrPercentageOrAuto::Length(_) => true,
_ => false,
};

let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
if !fixed_width {
for kid in self.block_flow.base.child_iter() {
let is_absolutely_positioned =
flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
let child_base = flow::mut_base(kid);
if !is_absolutely_positioned {
computation.content_intrinsic_sizes.minimum_inline_size =
max(computation.content_intrinsic_sizes.minimum_inline_size,
child_base.intrinsic_inline_sizes.minimum_inline_size);

computation.content_intrinsic_sizes.preferred_inline_size =
max(computation.content_intrinsic_sizes.preferred_inline_size,
child_base.intrinsic_inline_sizes.preferred_inline_size);
}
}
}
self.block_flow.base.intrinsic_inline_sizes = computation.finish();
}
}

impl Flow for FlexFlow {
fn class(&self) -> FlowClass {
FlowClass::Flex
}

fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
&mut self.block_flow
}

fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!("flex::bubble_inline_sizes {:x}", self.block_flow.base.debug_id());

// Flexbox Section 9.0: Generate anonymous flex items:
// This part was handled in the flow constructor.

// Flexbox Section 9.1: Re-order the flex items (and any absolutely positioned flex
// container children) according to their order.
// TODO(zentner): We need to re-order the items at some point. However, all the operations
// here ignore order, so we can afford to do it later, if necessary.

// `flex item`s (our children) cannot be floated. Furthermore, they all establish BFC's.
// Therefore, we do not have to handle any floats here.

let mut flags = self.block_flow.base.flags;
flags.remove(HAS_LEFT_FLOATED_DESCENDANTS);
flags.remove(HAS_RIGHT_FLOATED_DESCENDANTS);

match self.main_mode {
Mode::Inline => self.inline_mode_bubble_inline_sizes(),
Mode::Block => self.block_mode_bubble_inline_sizes()
}

// Although our children can't be floated, we can.
match self.block_flow.fragment.style().get_box().float {
float::T::none => {}
float::T::left => flags.insert(HAS_LEFT_FLOATED_DESCENDANTS),
float::T::right => flags.insert(HAS_RIGHT_FLOATED_DESCENDANTS),
}
self.block_flow.base.flags = flags
}

fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
self.block_flow.assign_inline_sizes(layout_context);
}

fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
self.block_flow.assign_block_size(layout_context);
}

fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {
self.block_flow.compute_absolute_position(layout_context)
}

fn place_float_if_applicable<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
self.block_flow.place_float_if_applicable(layout_context)
}

fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
}

fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
}

fn build_display_list(&mut self, layout_context: &LayoutContext) {
self.build_display_list_for_flex(Box::new(DisplayList::new()), layout_context);

if opts::get().validate_display_list_geometry {
self.block_flow.base.validate_display_list_geometry();
}
}

fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
self.block_flow.repair_style(new_style)
}

fn compute_overflow(&self) -> Rect<Au> {
self.block_flow.compute_overflow()
}

fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
self.block_flow.generated_containing_block_size(flow)
}

fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
level: i32,
stacking_context_position: &Point2D<Au>) {
self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position);
}

fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
self.block_flow.mutate_fragments(mutator);
}
}
9 changes: 9 additions & 0 deletions components/layout/flow.rs
Expand Up @@ -517,6 +517,7 @@ pub enum FlowClass {
TableCaption,
TableCell,
Multicol,
Flex,
}

/// A top-down traversal.
Expand Down Expand Up @@ -1173,6 +1174,8 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
FlowClass::Table => !child.is_proper_table_child(),
FlowClass::TableRowGroup => !child.is_table_row(),
FlowClass::TableRow => !child.is_table_cell(),
// FIXME(zentner): According to spec, anonymous flex items are only needed for text.
FlowClass::Flex => child.is_inline_flow(),
_ => false
}
}
Expand Down Expand Up @@ -1210,6 +1213,12 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
let hide = node.style().get_inheritedtable().empty_cells == empty_cells::T::hide;
Arc::new(TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide))
},
FlowClass::Flex => {
let fragment =
Fragment::new_anonymous_from_specific_info(node,
SpecificFragmentInfo::Generic);
box BlockFlow::from_fragment(fragment, None) as Box<Flow>
},
_ => {
panic!("no need to generate a missing child")
}
Expand Down
1 change: 1 addition & 0 deletions components/layout/lib.rs
Expand Up @@ -72,6 +72,7 @@ pub mod construct;
pub mod context;
pub mod data;
pub mod display_list_builder;
pub mod flex;
pub mod floats;
pub mod flow;
pub mod flow_list;
Expand Down
2 changes: 1 addition & 1 deletion components/style/properties.mako.rs
Expand Up @@ -412,7 +412,7 @@ pub mod longhands {
values="inline block inline-block
table inline-table table-row-group table-header-group table-footer-group
table-row table-column-group table-column table-cell table-caption
list-item
list-item flex
none">
use values::computed::Context;

Expand Down

0 comments on commit 0488a36

Please sign in to comment.