blob: e49cf0b926eb361d8788f108b56c0fc116d14370 [file] [log] [blame]
// 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