| // Copyright 2019 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/layout/flex_item.h" |
| |
| #include "base/memory/ptr_util.h" |
| #include "cobalt/cssom/css_computed_style_data.h" |
| #include "cobalt/cssom/number_value.h" |
| #include "cobalt/layout/container_box.h" |
| #include "cobalt/layout/used_style.h" |
| |
| namespace cobalt { |
| namespace layout { |
| |
| // Class for Flex Items in a container with a horizontal main axis. |
| // Since Cobalt does not support vertical writing modes, this is used for row |
| // flex containers. |
| class MainAxisHorizontalFlexItem : public FlexItem { |
| public: |
| MainAxisHorizontalFlexItem(Box* box, LayoutUnit flex_base_size, |
| LayoutUnit hypothetical_main_size) |
| : FlexItem(box, flex_base_size, hypothetical_main_size) {} |
| |
| ~MainAxisHorizontalFlexItem() override {} |
| |
| LayoutUnit GetContentToMarginMainAxis() override; |
| LayoutUnit GetContentToMarginCrossAxis() override; |
| LayoutUnit GetUsedMinMainAxisSize( |
| const SizeLayoutUnit& containing_block_size) override; |
| LayoutUnit GetUsedMinCrossAxisSize( |
| const SizeLayoutUnit& containing_block_size) override; |
| base::Optional<LayoutUnit> GetUsedMaxMainAxisSizeIfNotNone( |
| const SizeLayoutUnit& containing_block_size) override; |
| base::Optional<LayoutUnit> GetUsedMaxCrossAxisSizeIfNotNone( |
| const SizeLayoutUnit& containing_block_size) override; |
| |
| void DetermineHypotheticalCrossSize( |
| const LayoutParams& layout_params) override; |
| LayoutUnit GetMarginBoxMainSize() override; |
| LayoutUnit GetMarginBoxCrossSize() override; |
| |
| bool CrossSizeIsAuto() override; |
| bool MarginMainStartIsAuto() override; |
| bool MarginMainEndIsAuto() override; |
| bool MarginCrossStartIsAuto() override; |
| bool MarginCrossEndIsAuto() override; |
| |
| void SetCrossSize(LayoutUnit cross_size) override; |
| void SetMainAxisStart(LayoutUnit position) override; |
| void SetCrossAxisStart(LayoutUnit position) override; |
| }; |
| |
| LayoutUnit MainAxisHorizontalFlexItem::GetContentToMarginMainAxis() { |
| return box()->GetContentToMarginHorizontal(); |
| } |
| |
| LayoutUnit MainAxisHorizontalFlexItem::GetContentToMarginCrossAxis() { |
| return box()->GetContentToMarginVertical(); |
| } |
| |
| LayoutUnit MainAxisHorizontalFlexItem::GetUsedMinMainAxisSize( |
| const SizeLayoutUnit& containing_block_size) { |
| return GetUsedMinWidth(box()->computed_style(), containing_block_size, NULL); |
| } |
| |
| LayoutUnit MainAxisHorizontalFlexItem::GetUsedMinCrossAxisSize( |
| const SizeLayoutUnit& containing_block_size) { |
| return GetUsedMinHeight(box()->computed_style(), containing_block_size); |
| } |
| |
| base::Optional<LayoutUnit> |
| MainAxisHorizontalFlexItem::GetUsedMaxMainAxisSizeIfNotNone( |
| const SizeLayoutUnit& containing_block_size) { |
| return GetUsedMaxWidthIfNotNone(box()->computed_style(), |
| containing_block_size, NULL); |
| } |
| |
| base::Optional<LayoutUnit> |
| MainAxisHorizontalFlexItem::GetUsedMaxCrossAxisSizeIfNotNone( |
| const SizeLayoutUnit& containing_block_size) { |
| return GetUsedMaxHeightIfNotNone(box()->computed_style(), |
| containing_block_size); |
| } |
| |
| void MainAxisHorizontalFlexItem::DetermineHypotheticalCrossSize( |
| const LayoutParams& layout_params) { |
| // 5. Set each item's used main size to its target main size. |
| // https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths |
| // Also, algorithm for Flex Layout continued from step 7: |
| // Cross Size Determination: |
| // 7. Determine the hypothetical cross size of each item |
| // By performing layout with the used main size and the available space. |
| // https://www.w3.org/TR/css-flexbox-1/#algo-cross-item |
| LayoutParams child_layout_params(layout_params); |
| child_layout_params.shrink_to_fit_width_forced = false; |
| child_layout_params.freeze_width = true; |
| box()->set_width(target_main_size()); |
| box()->UpdateSize(child_layout_params); |
| } |
| |
| LayoutUnit MainAxisHorizontalFlexItem::GetMarginBoxMainSize() { |
| return box()->GetMarginBoxWidth(); |
| } |
| |
| LayoutUnit MainAxisHorizontalFlexItem::GetMarginBoxCrossSize() { |
| return box()->GetMarginBoxHeight(); |
| } |
| |
| bool MainAxisHorizontalFlexItem::CrossSizeIsAuto() { |
| return box()->computed_style()->height() == cssom::KeywordValue::GetAuto(); |
| } |
| |
| bool MainAxisHorizontalFlexItem::MarginMainStartIsAuto() { |
| return box()->computed_style()->margin_left() == |
| cssom::KeywordValue::GetAuto(); |
| } |
| |
| bool MainAxisHorizontalFlexItem::MarginMainEndIsAuto() { |
| return box()->computed_style()->margin_right() == |
| cssom::KeywordValue::GetAuto(); |
| } |
| |
| bool MainAxisHorizontalFlexItem::MarginCrossStartIsAuto() { |
| return box()->computed_style()->margin_top() == |
| cssom::KeywordValue::GetAuto(); |
| } |
| |
| bool MainAxisHorizontalFlexItem::MarginCrossEndIsAuto() { |
| return box()->computed_style()->margin_bottom() == |
| cssom::KeywordValue::GetAuto(); |
| } |
| |
| void MainAxisHorizontalFlexItem::SetCrossSize(LayoutUnit cross_size) { |
| box()->set_height(cross_size); |
| } |
| |
| void MainAxisHorizontalFlexItem::SetMainAxisStart(LayoutUnit position) { |
| box()->set_left(position); |
| } |
| |
| void MainAxisHorizontalFlexItem::SetCrossAxisStart(LayoutUnit position) { |
| box()->set_top(position); |
| } |
| |
| // Class for Flex Items in a container with a vertical main axis. |
| // Since Cobalt does not support vertical writing modes, this is used for column |
| // flex containers. |
| class MainAxisVerticalFlexItem : public FlexItem { |
| public: |
| MainAxisVerticalFlexItem(Box* box, LayoutUnit flex_base_size, |
| LayoutUnit hypothetical_main_size) |
| : FlexItem(box, flex_base_size, hypothetical_main_size) {} |
| |
| ~MainAxisVerticalFlexItem() override {} |
| |
| LayoutUnit GetContentToMarginMainAxis() override; |
| LayoutUnit GetContentToMarginCrossAxis() override; |
| |
| LayoutUnit GetUsedMinMainAxisSize( |
| const SizeLayoutUnit& containing_block_size) override; |
| LayoutUnit GetUsedMinCrossAxisSize( |
| const SizeLayoutUnit& containing_block_size) override; |
| base::Optional<LayoutUnit> GetUsedMaxMainAxisSizeIfNotNone( |
| const SizeLayoutUnit& containing_block_size) override; |
| base::Optional<LayoutUnit> GetUsedMaxCrossAxisSizeIfNotNone( |
| const SizeLayoutUnit& containing_block_size) override; |
| |
| void DetermineHypotheticalCrossSize( |
| const LayoutParams& layout_params) override; |
| LayoutUnit GetMarginBoxMainSize() override; |
| LayoutUnit GetMarginBoxCrossSize() override; |
| |
| bool CrossSizeIsAuto() override; |
| bool MarginMainStartIsAuto() override; |
| bool MarginMainEndIsAuto() override; |
| bool MarginCrossStartIsAuto() override; |
| bool MarginCrossEndIsAuto() override; |
| |
| void SetCrossSize(LayoutUnit cross_size) override; |
| void SetMainAxisStart(LayoutUnit position) override; |
| void SetCrossAxisStart(LayoutUnit position) override; |
| }; |
| |
| LayoutUnit MainAxisVerticalFlexItem::GetContentToMarginMainAxis() { |
| return box()->GetContentToMarginVertical(); |
| } |
| |
| LayoutUnit MainAxisVerticalFlexItem::GetContentToMarginCrossAxis() { |
| return box()->GetContentToMarginHorizontal(); |
| } |
| |
| LayoutUnit MainAxisVerticalFlexItem::GetUsedMinMainAxisSize( |
| const SizeLayoutUnit& containing_block_size) { |
| return GetUsedMinHeight(box()->computed_style(), containing_block_size); |
| } |
| |
| LayoutUnit MainAxisVerticalFlexItem::GetUsedMinCrossAxisSize( |
| const SizeLayoutUnit& containing_block_size) { |
| return GetUsedMinWidth(box()->computed_style(), containing_block_size, NULL); |
| } |
| |
| base::Optional<LayoutUnit> |
| MainAxisVerticalFlexItem::GetUsedMaxMainAxisSizeIfNotNone( |
| const SizeLayoutUnit& containing_block_size) { |
| return GetUsedMaxHeightIfNotNone(box()->computed_style(), |
| containing_block_size); |
| } |
| |
| base::Optional<LayoutUnit> |
| MainAxisVerticalFlexItem::GetUsedMaxCrossAxisSizeIfNotNone( |
| const SizeLayoutUnit& containing_block_size) { |
| return GetUsedMaxWidthIfNotNone(box()->computed_style(), |
| containing_block_size, NULL); |
| } |
| |
| void MainAxisVerticalFlexItem::DetermineHypotheticalCrossSize( |
| const LayoutParams& layout_params) { |
| // 7. Determine the hypothetical cross size of each item |
| // By performing layout with the used main size and the available space. |
| // https://www.w3.org/TR/css-flexbox-1/#algo-cross-item |
| LayoutParams child_layout_params(layout_params); |
| child_layout_params.shrink_to_fit_width_forced = true; |
| box()->UpdateSize(child_layout_params); |
| box()->set_height(target_main_size()); |
| } |
| |
| LayoutUnit MainAxisVerticalFlexItem::GetMarginBoxMainSize() { |
| return box()->GetMarginBoxHeight(); |
| } |
| |
| LayoutUnit MainAxisVerticalFlexItem::GetMarginBoxCrossSize() { |
| return box()->GetMarginBoxWidth(); |
| } |
| |
| bool MainAxisVerticalFlexItem::CrossSizeIsAuto() { |
| return box()->computed_style()->width() == cssom::KeywordValue::GetAuto(); |
| } |
| |
| bool MainAxisVerticalFlexItem::MarginMainStartIsAuto() { |
| return box()->computed_style()->margin_top() == |
| cssom::KeywordValue::GetAuto(); |
| } |
| |
| bool MainAxisVerticalFlexItem::MarginMainEndIsAuto() { |
| return box()->computed_style()->margin_bottom() == |
| cssom::KeywordValue::GetAuto(); |
| } |
| |
| bool MainAxisVerticalFlexItem::MarginCrossStartIsAuto() { |
| return box()->computed_style()->margin_left() == |
| cssom::KeywordValue::GetAuto(); |
| } |
| |
| bool MainAxisVerticalFlexItem::MarginCrossEndIsAuto() { |
| return box()->computed_style()->margin_right() == |
| cssom::KeywordValue::GetAuto(); |
| } |
| |
| void MainAxisVerticalFlexItem::SetCrossSize(LayoutUnit cross_size) { |
| box()->set_width(cross_size); |
| } |
| |
| void MainAxisVerticalFlexItem::SetMainAxisStart(LayoutUnit position) { |
| box()->set_top(position); |
| } |
| |
| void MainAxisVerticalFlexItem::SetCrossAxisStart(LayoutUnit position) { |
| box()->set_left(position); |
| } |
| |
| FlexItem::FlexItem(Box* box, LayoutUnit flex_base_size, |
| LayoutUnit hypothetical_main_size) |
| : box_(box), |
| flex_base_size_(flex_base_size), |
| hypothetical_main_size_(hypothetical_main_size) {} |
| |
| std::unique_ptr<FlexItem> FlexItem::Create(bool main_direction_is_horizontal, |
| Box* box, LayoutUnit flex_base_size, |
| LayoutUnit hypothetical_main_size) { |
| if (main_direction_is_horizontal) { |
| return base::WrapUnique(new MainAxisHorizontalFlexItem( |
| box, flex_base_size, hypothetical_main_size)); |
| } else { |
| return base::WrapUnique(new MainAxisVerticalFlexItem( |
| box, flex_base_size, hypothetical_main_size)); |
| } |
| } |
| |
| void FlexItem::DetermineFlexFactor(bool flex_factor_is_grow) { |
| auto flex_factor_property = flex_factor_is_grow |
| ? box_->computed_style()->flex_grow() |
| : box_->computed_style()->flex_shrink(); |
| flex_factor_ = base::polymorphic_downcast<const cssom::NumberValue*>( |
| flex_factor_property.get()) |
| ->value(); |
| } |
| |
| const scoped_refptr<cobalt::cssom::PropertyValue>& |
| FlexItem::GetUsedAlignSelfPropertyValue() { |
| DCHECK(box()->parent()); |
| return GetUsedAlignSelf(box()->computed_style(), |
| box()->parent()->computed_style()); |
| } |
| |
| const scoped_refptr<cobalt::cssom::PropertyValue>& |
| FlexItem::GetUsedJustifyContentPropertyValue() { |
| DCHECK(box()->parent()); |
| return box()->parent()->computed_style()->justify_content(); |
| } |
| |
| |
| } // namespace layout |
| } // namespace cobalt |