From 3580f9129101d1e8debbe5d9a216c436b70a2773 Mon Sep 17 00:00:00 2001 From: Daniel Robertson Date: Mon, 4 Apr 2016 21:37:49 -0400 Subject: [PATCH] Implement flexbox reordering Add style property for order and implement reordering by this property in flex flow. Based on previous work by @zentner-kyle. --- components/layout/flex.rs | 116 ++++++++++-------- components/layout/flow_list.rs | 6 + .../dom/webidls/CSSStyleDeclaration.webidl | 1 + components/style/properties.mako.rs | 22 ++++ ...lexbox_computedstyle_order-inherit.htm.ini | 3 - ...lexbox_computedstyle_order-integer.htm.ini | 3 - ...lexbox_computedstyle_order-invalid.htm.ini | 3 - ...exbox_computedstyle_order-negative.htm.ini | 3 - .../html/flexbox_computedstyle_order.htm.ini | 3 - .../html/order_value.htm.ini | 5 - 10 files changed, 96 insertions(+), 69 deletions(-) delete mode 100644 tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-inherit.htm.ini delete mode 100644 tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-integer.htm.ini delete mode 100644 tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-invalid.htm.ini delete mode 100644 tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-negative.htm.ini delete mode 100644 tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order.htm.ini delete mode 100644 tests/wpt/metadata-css/css-flexbox-1_dev/html/order_value.htm.ini diff --git a/components/layout/flex.rs b/components/layout/flex.rs index a506370c17f6..eed7ec8aae3e 100644 --- a/components/layout/flex.rs +++ b/components/layout/flex.rs @@ -13,9 +13,9 @@ use display_list_builder::{DisplayListBuildState, FlexFlowDisplayListBuilding}; use euclid::Point2D; use floats::FloatKind; use flow; -use flow::INLINE_POSITION_IS_STATIC; -use flow::IS_ABSOLUTELY_POSITIONED; use flow::{Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow}; +use flow::{INLINE_POSITION_IS_STATIC, IS_ABSOLUTELY_POSITIONED}; +use flow_ref::{self, FlowRef}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; use gfx::display_list::{StackingContext, StackingContextId}; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; @@ -73,6 +73,19 @@ enum Mode { Block } +#[derive(Debug)] +struct FlexItem { + pub flow: FlowRef, +} + +impl FlexItem { + fn new(flow: FlowRef) -> FlexItem { + FlexItem { + flow: flow + } + } +} + /// A block with the CSS `display` property equal to `flex`. #[derive(Debug)] pub struct FlexFlow { @@ -84,23 +97,9 @@ pub struct FlexFlow { /// The available main axis size available_main_size: AxisSize, /// The available cross axis size - available_cross_size: AxisSize -} - -// 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, - } + available_cross_size: AxisSize, + /// List of flex-items that belong to this flex-container + items: Vec } impl FlexFlow { @@ -117,7 +116,8 @@ impl FlexFlow { block_flow: BlockFlow::from_fragment(fragment, flotation), main_mode: main_mode, available_main_size: AxisSize::Infinite, - available_cross_size: AxisSize::Infinite + available_cross_size: AxisSize::Infinite, + items: Vec::new() } } @@ -132,11 +132,15 @@ impl FlexFlow { let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes(); if !fixed_width { - for kid in self.block_flow.base.child_iter_mut() { - let is_absolutely_positioned = - flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED); + for kid in &mut self.items { + let base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow)); + let is_absolutely_positioned = base.flags.contains(IS_ABSOLUTELY_POSITIONED); if !is_absolutely_positioned { - computation.union_nonbreaking_inline(&flex_item_inline_sizes(kid)); + let flex_item_inline_sizes = IntrinsicISizes { + minimum_inline_size: base.intrinsic_inline_sizes.minimum_inline_size, + preferred_inline_size: base.intrinsic_inline_sizes.preferred_inline_size, + }; + computation.union_nonbreaking_inline(&flex_item_inline_sizes); } } } @@ -154,18 +158,17 @@ impl FlexFlow { let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes(); if !fixed_width { - for kid in self.block_flow.base.child_iter_mut() { - let is_absolutely_positioned = - flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED); - let child_base = flow::mut_base(kid); + for kid in &mut self.items { + let base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow)); + let is_absolutely_positioned = base.flags.contains(IS_ABSOLUTELY_POSITIONED); 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); + 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); + base.intrinsic_inline_sizes.preferred_inline_size); } } } @@ -195,21 +198,15 @@ impl FlexFlow { AxisSize::MinMax(ref constraint) => constraint.clamp(content_inline_size), AxisSize::Infinite => content_inline_size }; - let mut iterator = self.block_flow.base.child_iter_mut().enumerate().peekable(); - while let Some((_, kid)) = iterator.next() { + for kid in &mut self.items { { - let kid_base = flow::mut_base(kid); + let kid_base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow)); kid_base.block_container_explicit_block_size = container_block_size; - } - - // The inline-start margin edge of the child flow is at our inline-start content edge, - // and its inline-size is our content inline-size. - let kid_mode = flow::base(kid).writing_mode; - { - let kid_base = flow::mut_base(kid); if kid_base.flags.contains(INLINE_POSITION_IS_STATIC) { + // The inline-start margin edge of the child flow is at our inline-start content edge, + // and its inline-size is our content inline-size. kid_base.position.start.i = - if kid_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() { + if kid_base.writing_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() { inline_start_content_edge } else { // The kid's inline 'start' is at the parent's 'end' @@ -218,6 +215,7 @@ impl FlexFlow { } kid_base.block_container_inline_size = container_inline_size; kid_base.block_container_writing_mode = containing_block_mode; + kid_base.position.start.i = inline_start_content_edge; } } } @@ -254,19 +252,25 @@ impl FlexFlow { let block_container_explicit_block_size = self.block_flow.base.block_container_explicit_block_size; let mut inline_child_start = inline_start_content_edge; - for kid in self.block_flow.base.child_iter_mut() { - let kid_base = flow::mut_base(kid); + for kid in &mut self.items { + let base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow)); - kid_base.block_container_inline_size = even_content_inline_size; - kid_base.block_container_writing_mode = container_mode; - kid_base.block_container_explicit_block_size = block_container_explicit_block_size; - kid_base.position.start.i = inline_child_start; + base.block_container_inline_size = even_content_inline_size; + base.block_container_writing_mode = container_mode; + base.block_container_explicit_block_size = block_container_explicit_block_size; + base.position.start.i = inline_child_start; inline_child_start = inline_child_start + even_content_inline_size; } } // TODO(zentner): This function should actually flex elements! fn block_mode_assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { + let mut cur_b = self.block_flow.fragment.border_padding.block_start; + for kid in &mut self.items { + let base = flow::mut_base(flow_ref::deref_mut(&mut kid.flow)); + base.position.start.b = cur_b; + cur_b = cur_b + base.position.size.block; + } self.block_flow.assign_block_size(layout_context) } @@ -335,6 +339,10 @@ impl Flow for FlexFlow { FlowClass::Flex } + fn as_block(&self) -> &BlockFlow { + &self.block_flow + } + fn as_mut_block(&mut self) -> &mut BlockFlow { &mut self.block_flow } @@ -352,8 +360,18 @@ impl Flow for FlexFlow { // 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. + + let mut items = self.block_flow.base.children.iter_flow_ref_mut().map(|flow| { + FlexItem::new(flow.clone()) + }).collect::>(); + + items.sort_by(|item1, item2| { + item1.flow.as_block().fragment.style.get_position().order.cmp( + &item2.flow.as_block().fragment.style.get_position().order + ) + }); + + self.items = items; match self.main_mode { Mode::Inline => self.inline_mode_bubble_inline_sizes(), diff --git a/components/layout/flow_list.rs b/components/layout/flow_list.rs index 37044008f7f2..96f4d5ba7ef2 100644 --- a/components/layout/flow_list.rs +++ b/components/layout/flow_list.rs @@ -64,6 +64,12 @@ impl FlowList { } } + /// Provide a forward iterator with FlowRef items + #[inline] + pub fn iter_flow_ref_mut<'a>(&'a mut self) -> linked_list::IterMut<'a, FlowRef> { + self.flows.iter_mut() + } + /// O(1) #[inline] pub fn is_empty(&self) -> bool { diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index dba69a515b01..10cf77ccc834 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -310,4 +310,5 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flexDirection; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flex-direction; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString order; }; diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index f3c2f512cf84..58f68d12637e 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -4906,6 +4906,28 @@ pub mod longhands { // Flex container properties ${single_keyword("flex-direction", "row row-reverse column column-reverse", experimental=True)} + + // https://drafts.csswg.org/css-flexbox/#propdef-order + <%self:longhand name="order"> + use values::computed::ComputedValueAsSpecified; + + impl ComputedValueAsSpecified for SpecifiedValue {} + + pub type SpecifiedValue = computed_value::T; + + pub mod computed_value { + pub type T = i32; + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + 0 + } + + fn parse(_context: &ParserContext, input: &mut Parser) -> Result { + specified::parse_integer(input) + } + } diff --git a/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-inherit.htm.ini b/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-inherit.htm.ini deleted file mode 100644 index 7f4ce8588059..000000000000 --- a/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-inherit.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[flexbox_computedstyle_order-inherit.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-integer.htm.ini b/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-integer.htm.ini deleted file mode 100644 index 271332df7f32..000000000000 --- a/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-integer.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[flexbox_computedstyle_order-integer.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-invalid.htm.ini b/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-invalid.htm.ini deleted file mode 100644 index 85c6209e7785..000000000000 --- a/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-invalid.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[flexbox_computedstyle_order-invalid.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-negative.htm.ini b/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-negative.htm.ini deleted file mode 100644 index 708b6da31ee0..000000000000 --- a/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order-negative.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[flexbox_computedstyle_order-negative.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order.htm.ini b/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order.htm.ini deleted file mode 100644 index 5ed40787983c..000000000000 --- a/tests/wpt/metadata-css/css-flexbox-1_dev/html/flexbox_computedstyle_order.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[flexbox_computedstyle_order.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css-flexbox-1_dev/html/order_value.htm.ini b/tests/wpt/metadata-css/css-flexbox-1_dev/html/order_value.htm.ini deleted file mode 100644 index 7366f308aee5..000000000000 --- a/tests/wpt/metadata-css/css-flexbox-1_dev/html/order_value.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[order_value.htm] - type: testharness - [CSS Flexible Box Test: order_check] - expected: FAIL -