Commit 20a1c21e authored by Jacques Newman's avatar Jacques Newman Committed by Commit Bot

[GridNG] Auto placement for sparsely placed items.

This change brings the placement algorithm out of
NGGridLayoutAlgorithm and into NGGridAutoPlacement.
NGGridAutoPlacement exists only to encapsulate the
placement logic, and has a short lifetime on the stack.

NGGridAutoPlacement will resolve each GridItemData's
position and will ensure track coverage for that position.

Currently Auto Placement is implemented only for
sparsely packed grids.

Bug: 1045599
Change-Id: I5e1889ad5b4ea12cf3526736f71ffc512ae05bb3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2404110
Commit-Queue: Jacques Newman <janewman@microsoft.com>
Reviewed-by: default avatarKurt Catti-Schmidt <kschmi@microsoft.com>
Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: default avatarChristian Biesinger <cbiesinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810793}
parent 2c667c7c
...@@ -341,6 +341,8 @@ blink_core_sources("layout") { ...@@ -341,6 +341,8 @@ blink_core_sources("layout") {
"ng/grid/ng_grid_child_iterator.h", "ng/grid/ng_grid_child_iterator.h",
"ng/grid/ng_grid_layout_algorithm.cc", "ng/grid/ng_grid_layout_algorithm.cc",
"ng/grid/ng_grid_layout_algorithm.h", "ng/grid/ng_grid_layout_algorithm.h",
"ng/grid/ng_grid_placement.cc",
"ng/grid/ng_grid_placement.h",
"ng/grid/ng_grid_track_collection.cc", "ng/grid/ng_grid_track_collection.cc",
"ng/grid/ng_grid_track_collection.h", "ng/grid/ng_grid_track_collection.h",
"ng/inline/empty_offset_mapping_builder.h", "ng/inline/empty_offset_mapping_builder.h",
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.h" #include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.h"
#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h" #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
...@@ -34,6 +35,18 @@ scoped_refptr<const NGLayoutResult> NGGridLayoutAlgorithm::Layout() { ...@@ -34,6 +35,18 @@ scoped_refptr<const NGLayoutResult> NGGridLayoutAlgorithm::Layout() {
SetSpecifiedTracks(); SetSpecifiedTracks();
DetermineExplicitTrackStarts(); DetermineExplicitTrackStarts();
ConstructAndAppendGridItems(); ConstructAndAppendGridItems();
// TODO(janewman): Split placement into its own GridLayoutAlgorithmState
NGGridPlacement(
automatic_row_repetitions_, automatic_column_repetitions_,
explicit_row_start_, explicit_column_start_,
Style().IsGridAutoFlowAlgorithmSparse()
? NGGridPlacement::PackingBehavior::kSparse
: NGGridPlacement::PackingBehavior::kDense,
AutoFlowDirection(), Style(),
AutoFlowDirection() == kForRows ? column_count_ : row_count_,
block_row_track_collection_, block_column_track_collection_, items_)
.RunAutoPlacementAlgorithm();
block_column_track_collection_.FinalizeRanges(); block_column_track_collection_.FinalizeRanges();
block_row_track_collection_.FinalizeRanges(); block_row_track_collection_.FinalizeRanges();
...@@ -106,6 +119,24 @@ NGGridLayoutAlgorithm::RowTrackCollection() const { ...@@ -106,6 +119,24 @@ NGGridLayoutAlgorithm::RowTrackCollection() const {
NGGridLayoutAlgorithm::GridItemData::GridItemData(const NGBlockNode node) NGGridLayoutAlgorithm::GridItemData::GridItemData(const NGBlockNode node)
: node(node) {} : node(node) {}
NGGridLayoutAlgorithm::AutoPlacementType
NGGridLayoutAlgorithm::GridItemData::AutoPlacement(
GridTrackSizingDirection flow_direction) const {
bool is_major_indefinite = Span(flow_direction).IsIndefinite();
bool is_minor_indefinite =
Span(flow_direction == kForColumns ? kForRows : kForColumns)
.IsIndefinite();
if (is_minor_indefinite && is_major_indefinite)
return NGGridLayoutAlgorithm::AutoPlacementType::kBoth;
else if (is_minor_indefinite)
return NGGridLayoutAlgorithm::AutoPlacementType::kMinor;
else if (is_major_indefinite)
return NGGridLayoutAlgorithm::AutoPlacementType::kMajor;
return NGGridLayoutAlgorithm::AutoPlacementType::kNotNeeded;
}
wtf_size_t NGGridLayoutAlgorithm::GridItemData::StartLine( wtf_size_t NGGridLayoutAlgorithm::GridItemData::StartLine(
GridTrackSizingDirection track_direction) const { GridTrackSizingDirection track_direction) const {
const GridSpan& span = (track_direction == kForColumns) const GridSpan& span = (track_direction == kForColumns)
...@@ -124,6 +155,25 @@ wtf_size_t NGGridLayoutAlgorithm::GridItemData::EndLine( ...@@ -124,6 +155,25 @@ wtf_size_t NGGridLayoutAlgorithm::GridItemData::EndLine(
return span.EndLine(); return span.EndLine();
} }
const GridSpan& NGGridLayoutAlgorithm::GridItemData::Span(
GridTrackSizingDirection direction) const {
return (direction == kForColumns) ? resolved_position.columns
: resolved_position.rows;
}
void NGGridLayoutAlgorithm::GridItemData::SetSpan(
const GridSpan& span,
GridTrackSizingDirection track_direction) {
switch (track_direction) {
case kForColumns:
resolved_position.columns = span;
break;
case kForRows:
resolved_position.rows = span;
break;
}
}
NGGridLayoutAlgorithm::ReorderedGridItems::Iterator::Iterator( NGGridLayoutAlgorithm::ReorderedGridItems::Iterator::Iterator(
Vector<wtf_size_t>::const_iterator current_index, Vector<wtf_size_t>::const_iterator current_index,
Vector<GridItemData>* items) Vector<GridItemData>* items)
...@@ -177,8 +227,6 @@ void NGGridLayoutAlgorithm::ConstructAndAppendGridItems() { ...@@ -177,8 +227,6 @@ void NGGridLayoutAlgorithm::ConstructAndAppendGridItems() {
for (NGBlockNode child = iterator.NextChild(); child; for (NGBlockNode child = iterator.NextChild(); child;
child = iterator.NextChild()) { child = iterator.NextChild()) {
GridItemData grid_item = MeasureGridItem(child); GridItemData grid_item = MeasureGridItem(child);
EnsureTrackCoverageForGridItem(kForColumns, grid_item);
EnsureTrackCoverageForGridItem(kForRows, grid_item);
items_.emplace_back(grid_item); items_.emplace_back(grid_item);
} }
...@@ -261,34 +309,11 @@ void NGGridLayoutAlgorithm::SetSpecifiedTracks() { ...@@ -261,34 +309,11 @@ void NGGridLayoutAlgorithm::SetSpecifiedTracks() {
&grid_style.GridAutoRows().NGTrackList(), automatic_row_repetitions_); &grid_style.GridAutoRows().NGTrackList(), automatic_row_repetitions_);
} }
void NGGridLayoutAlgorithm::EnsureTrackCoverageForGridItem(
GridTrackSizingDirection track_direction,
GridItemData& grid_item) {
GridSpan span = GridPositionsResolver::ResolveGridPositionsFromStyle(
Style(), grid_item.node.Style(), track_direction,
AutoRepeatCountForDirection(track_direction));
// TODO(janewman): indefinite positions should be resolved with the auto
// placement algorithm.
if (span.IsIndefinite())
return;
if (track_direction == kForColumns) {
span.Translate(explicit_column_start_);
grid_item.resolved_position.columns = span;
block_column_track_collection_.EnsureTrackCoverage(span.StartLine(),
span.IntegerSpan());
} else {
span.Translate(explicit_row_start_);
grid_item.resolved_position.rows = span;
block_row_track_collection_.EnsureTrackCoverage(span.StartLine(),
span.IntegerSpan());
}
}
void NGGridLayoutAlgorithm::DetermineExplicitTrackStarts() { void NGGridLayoutAlgorithm::DetermineExplicitTrackStarts() {
DCHECK_EQ(0u, explicit_column_start_); DCHECK_EQ(0u, explicit_column_start_);
DCHECK_EQ(0u, explicit_row_start_); DCHECK_EQ(0u, explicit_row_start_);
DCHECK_EQ(0u, column_count_);
DCHECK_EQ(0u, row_count_);
NGGridChildIterator iterator(Node()); NGGridChildIterator iterator(Node());
for (NGBlockNode child = iterator.NextChild(); child; for (NGBlockNode child = iterator.NextChild(); child;
...@@ -302,10 +327,21 @@ void NGGridLayoutAlgorithm::DetermineExplicitTrackStarts() { ...@@ -302,10 +327,21 @@ void NGGridLayoutAlgorithm::DetermineExplicitTrackStarts() {
if (!column_span.IsIndefinite()) { if (!column_span.IsIndefinite()) {
explicit_column_start_ = std::max<int>( explicit_column_start_ = std::max<int>(
explicit_column_start_, -column_span.UntranslatedStartLine()); explicit_column_start_, -column_span.UntranslatedStartLine());
column_count_ =
std::max<int>(column_count_, column_span.UntranslatedEndLine());
} else {
column_count_ = std::max<int>(
column_count_, GridPositionsResolver::SpanSizeForAutoPlacedItem(
child.Style(), kForColumns));
} }
if (!row_span.IsIndefinite()) { if (!row_span.IsIndefinite()) {
explicit_row_start_ = explicit_row_start_ =
std::max<int>(explicit_row_start_, -row_span.UntranslatedStartLine()); std::max<int>(explicit_row_start_, -row_span.UntranslatedStartLine());
row_count_ = std::max<int>(row_count_, row_span.UntranslatedEndLine());
} else {
row_count_ = std::max<int>(
row_count_, GridPositionsResolver::SpanSizeForAutoPlacedItem(
child.Style(), kForRows));
} }
} }
} }
...@@ -435,6 +471,10 @@ wtf_size_t NGGridLayoutAlgorithm::AutoRepeatCountForDirection( ...@@ -435,6 +471,10 @@ wtf_size_t NGGridLayoutAlgorithm::AutoRepeatCountForDirection(
: automatic_row_repetitions_; : automatic_row_repetitions_;
} }
GridTrackSizingDirection NGGridLayoutAlgorithm::AutoFlowDirection() const {
return Style().IsGridAutoFlowDirectionRow() ? kForRows : kForColumns;
}
void NGGridLayoutAlgorithm::PlaceGridItems() { void NGGridLayoutAlgorithm::PlaceGridItems() {
NGGridChildIterator iterator(Node()); NGGridChildIterator iterator(Node());
LayoutUnit current_inline_offset, current_block_offset; LayoutUnit current_inline_offset, current_block_offset;
......
...@@ -19,6 +19,28 @@ class CORE_EXPORT NGGridLayoutAlgorithm ...@@ -19,6 +19,28 @@ class CORE_EXPORT NGGridLayoutAlgorithm
NGBoxFragmentBuilder, NGBoxFragmentBuilder,
NGBlockBreakToken> { NGBlockBreakToken> {
public: public:
enum class AutoPlacementType { kNotNeeded, kMajor, kMinor, kBoth };
struct GridItemData {
explicit GridItemData(const NGBlockNode node);
AutoPlacementType AutoPlacement(
GridTrackSizingDirection flow_direction) const;
wtf_size_t StartLine(GridTrackSizingDirection direction) const;
wtf_size_t EndLine(GridTrackSizingDirection direction) const;
const GridSpan& Span(GridTrackSizingDirection direction) const;
void SetSpan(const GridSpan& span, GridTrackSizingDirection direction);
const NGBlockNode node;
GridArea resolved_position;
NGBoxStrut margins;
LayoutUnit inline_size;
MinMaxSizes min_max_sizes;
bool is_spanning_flex_track : 1;
bool is_spanning_intrinsic_track : 1;
};
explicit NGGridLayoutAlgorithm(const NGLayoutAlgorithmParams& params); explicit NGGridLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
scoped_refptr<const NGLayoutResult> Layout() override; scoped_refptr<const NGLayoutResult> Layout() override;
...@@ -39,23 +61,6 @@ class CORE_EXPORT NGGridLayoutAlgorithm ...@@ -39,23 +61,6 @@ class CORE_EXPORT NGGridLayoutAlgorithm
kCompletedLayout kCompletedLayout
}; };
struct GridItemData {
explicit GridItemData(const NGBlockNode node);
wtf_size_t StartLine(GridTrackSizingDirection track_direction) const;
wtf_size_t EndLine(GridTrackSizingDirection track_direction) const;
const NGBlockNode node;
GridArea resolved_position;
NGBoxStrut margins;
LayoutUnit inline_size;
MinMaxSizes min_max_sizes;
bool is_spanning_flex_track : 1;
bool is_spanning_intrinsic_track : 1;
};
class ReorderedGridItems { class ReorderedGridItems {
public: public:
class Iterator class Iterator
...@@ -116,6 +121,8 @@ class CORE_EXPORT NGGridLayoutAlgorithm ...@@ -116,6 +121,8 @@ class CORE_EXPORT NGGridLayoutAlgorithm
// Lays out and computes inline and block offsets for grid items. // Lays out and computes inline and block offsets for grid items.
void PlaceGridItems(); void PlaceGridItems();
GridTrackSizingDirection AutoFlowDirection() const;
GridLayoutAlgorithmState state_; GridLayoutAlgorithmState state_;
LogicalSize border_box_size_; LogicalSize border_box_size_;
LogicalSize child_percentage_size_; LogicalSize child_percentage_size_;
...@@ -131,6 +138,8 @@ class CORE_EXPORT NGGridLayoutAlgorithm ...@@ -131,6 +138,8 @@ class CORE_EXPORT NGGridLayoutAlgorithm
wtf_size_t explicit_column_start_ = 0; wtf_size_t explicit_column_start_ = 0;
wtf_size_t explicit_row_start_ = 0; wtf_size_t explicit_row_start_ = 0;
wtf_size_t column_count_ = 0;
wtf_size_t row_count_ = 0;
wtf_size_t automatic_column_repetitions_ = wtf_size_t automatic_column_repetitions_ =
NGGridBlockTrackCollection::kInvalidRangeIndex; NGGridBlockTrackCollection::kInvalidRangeIndex;
......
...@@ -374,12 +374,22 @@ TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmRanges) { ...@@ -374,12 +374,22 @@ TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmRanges) {
NGGridTrackCollectionBase::RangeRepeatIterator row_iterator( NGGridTrackCollectionBase::RangeRepeatIterator row_iterator(
&algorithm.RowTrackCollection(), 0u); &algorithm.RowTrackCollection(), 0u);
EXPECT_RANGE(0u, 1000u, row_iterator); EXPECT_RANGE(0u, 1u, row_iterator);
EXPECT_TRUE(row_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 999u, row_iterator);
EXPECT_FALSE(row_iterator.MoveToNextRange()); EXPECT_FALSE(row_iterator.MoveToNextRange());
NGGridTrackCollectionBase::RangeRepeatIterator column_iterator( NGGridTrackCollectionBase::RangeRepeatIterator column_iterator(
&algorithm.ColumnTrackCollection(), 0u); &algorithm.ColumnTrackCollection(), 0u);
EXPECT_RANGE(0u, 8u, column_iterator); EXPECT_RANGE(0u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(2u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(3u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(4u, 4u, column_iterator);
EXPECT_FALSE(column_iterator.MoveToNextRange()); EXPECT_FALSE(column_iterator.MoveToNextRange());
} }
...@@ -420,7 +430,9 @@ TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmRangesWithAutoRepeater) { ...@@ -420,7 +430,9 @@ TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmRangesWithAutoRepeater) {
NGGridTrackCollectionBase::RangeRepeatIterator row_iterator( NGGridTrackCollectionBase::RangeRepeatIterator row_iterator(
&algorithm.RowTrackCollection(), 0u); &algorithm.RowTrackCollection(), 0u);
EXPECT_RANGE(0u, 20u, row_iterator); EXPECT_RANGE(0u, 1u, row_iterator);
EXPECT_TRUE(row_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 19u, row_iterator);
EXPECT_TRUE(row_iterator.MoveToNextRange()); EXPECT_TRUE(row_iterator.MoveToNextRange());
EXPECT_RANGE(20u, 1u, row_iterator); EXPECT_RANGE(20u, 1u, row_iterator);
...@@ -434,12 +446,16 @@ TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmRangesWithAutoRepeater) { ...@@ -434,12 +446,16 @@ TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmRangesWithAutoRepeater) {
EXPECT_RANGE(0u, 1u, column_iterator); EXPECT_RANGE(0u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange()); EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 3u, column_iterator); EXPECT_RANGE(1u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange()); EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(4u, 1u, column_iterator); EXPECT_RANGE(2u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange()); EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(3u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(4u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(5u, 1u, column_iterator); EXPECT_RANGE(5u, 1u, column_iterator);
EXPECT_FALSE(column_iterator.MoveToNextRange()); EXPECT_FALSE(column_iterator.MoveToNextRange());
} }
...@@ -516,6 +532,200 @@ TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmRangesImplicit) { ...@@ -516,6 +532,200 @@ TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmRangesImplicit) {
EXPECT_FALSE(row_iterator.MoveToNextRange()); EXPECT_FALSE(row_iterator.MoveToNextRange());
} }
TEST_F(NGGridLayoutAlgorithmTest,
NGGridLayoutAlgorithmRangesImplicitAutoColumns) {
if (!RuntimeEnabledFeatures::LayoutNGGridEnabled())
return;
SetBodyInnerHTML(R"HTML(
<style>
#grid1 {
display: grid;
}
#cell1 {
grid-row: 1 / 2;
width: 50px;
}
#cell2 {
grid-row: 1 / 2;
width: 50px;
}
#cell3 {
grid-row: 2 / 3;
width: 50px;
}
#cell4 {
grid-row: 2 / 3;
width: 50px;
}
</style>
<div id="grid1">
<div id="cell1">Cell 1</div>
<div id="cell2">Cell 2</div>
<div id="cell3">Cell 3</div>
<div id="cell4">Cell 4</div>
</div>
)HTML");
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("grid1")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
LogicalSize(LayoutUnit(100), LayoutUnit(100)), false, true);
NGFragmentGeometry fragment_geometry =
CalculateInitialFragmentGeometry(space, node);
NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
EXPECT_EQ(GridItemCount(algorithm), 0U);
SetAutoTrackRepeat(algorithm, 0, 0);
algorithm.Layout();
EXPECT_EQ(GridItemCount(algorithm), 4U);
NGGridTrackCollectionBase::RangeRepeatIterator column_iterator(
&algorithm.ColumnTrackCollection(), 0u);
EXPECT_RANGE(0u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 1u, column_iterator);
EXPECT_FALSE(column_iterator.MoveToNextRange());
NGGridTrackCollectionBase::RangeRepeatIterator row_iterator(
&algorithm.RowTrackCollection(), 0u);
EXPECT_RANGE(0u, 1u, row_iterator);
EXPECT_TRUE(row_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 1u, row_iterator);
EXPECT_FALSE(row_iterator.MoveToNextRange());
}
TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmRangesImplicitAutoRows) {
if (!RuntimeEnabledFeatures::LayoutNGGridEnabled())
return;
SetBodyInnerHTML(R"HTML(
<style>
#grid1 {
display: grid;
}
#cell1 {
grid-column: 1 / 2;
width: 50px;
}
#cell2 {
grid-column: 2 / 3;
width: 50px;
}
#cell3 {
grid-column: 1 / 2;
width: 50px;
}
#cell4 {
grid-column: 2 / 5;
width: 50px;
}
</style>
<div id="grid1">
<div id="cell1">Cell 1</div>
<div id="cell2">Cell 2</div>
<div id="cell3">Cell 3</div>
<div id="cell4">Cell 4</div>
</div>
)HTML");
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("grid1")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
LogicalSize(LayoutUnit(100), LayoutUnit(100)), false, true);
NGFragmentGeometry fragment_geometry =
CalculateInitialFragmentGeometry(space, node);
NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
EXPECT_EQ(GridItemCount(algorithm), 0U);
SetAutoTrackRepeat(algorithm, 0, 0);
algorithm.Layout();
EXPECT_EQ(GridItemCount(algorithm), 4U);
NGGridTrackCollectionBase::RangeRepeatIterator column_iterator(
&algorithm.ColumnTrackCollection(), 0u);
EXPECT_RANGE(0u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(2u, 2u, column_iterator);
EXPECT_FALSE(column_iterator.MoveToNextRange());
NGGridTrackCollectionBase::RangeRepeatIterator row_iterator(
&algorithm.RowTrackCollection(), 0u);
EXPECT_RANGE(0u, 1u, row_iterator);
EXPECT_TRUE(row_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 1u, row_iterator);
EXPECT_FALSE(row_iterator.MoveToNextRange());
}
TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmRangesImplicitMixed) {
if (!RuntimeEnabledFeatures::LayoutNGGridEnabled())
return;
SetBodyInnerHTML(R"HTML(
<style>
#grid1 {
display: grid;
}
#cell1 {
grid-column: 2;
grid-row: 1;
}
</style>
<div id="grid1">
<div id="cell1">Cell 1</div>
<div id="cell2">Cell 2</div>
<div id="cell3">Cell 3</div>
<div id="cell4">Cell 4</div>
<div id="cell4">Cell 5</div>
</div>
)HTML");
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("grid1")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
LogicalSize(LayoutUnit(100), LayoutUnit(100)), false, true);
NGFragmentGeometry fragment_geometry =
CalculateInitialFragmentGeometry(space, node);
NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
EXPECT_EQ(GridItemCount(algorithm), 0U);
SetAutoTrackRepeat(algorithm, 0, 0);
algorithm.Layout();
EXPECT_EQ(GridItemCount(algorithm), 5U);
NGGridTrackCollectionBase::RangeRepeatIterator column_iterator(
&algorithm.ColumnTrackCollection(), 0u);
EXPECT_RANGE(0u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 1u, column_iterator);
EXPECT_FALSE(column_iterator.MoveToNextRange());
NGGridTrackCollectionBase::RangeRepeatIterator row_iterator(
&algorithm.RowTrackCollection(), 0u);
EXPECT_RANGE(0u, 1u, row_iterator);
EXPECT_TRUE(row_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 1u, row_iterator);
EXPECT_TRUE(row_iterator.MoveToNextRange());
EXPECT_RANGE(2u, 1u, row_iterator);
EXPECT_FALSE(row_iterator.MoveToNextRange());
}
TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmGridPositions) { TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmGridPositions) {
if (!RuntimeEnabledFeatures::LayoutNGGridEnabled()) if (!RuntimeEnabledFeatures::LayoutNGGridEnabled())
return; return;
...@@ -563,11 +773,15 @@ TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmGridPositions) { ...@@ -563,11 +773,15 @@ TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmGridPositions) {
NGGridTrackCollectionBase::RangeRepeatIterator column_iterator( NGGridTrackCollectionBase::RangeRepeatIterator column_iterator(
&algorithm.ColumnTrackCollection(), 0u); &algorithm.ColumnTrackCollection(), 0u);
EXPECT_RANGE(0u, 1u, column_iterator); EXPECT_RANGE(0u, 1u, column_iterator);
EXPECT_TRUE(column_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 1u, column_iterator);
EXPECT_FALSE(column_iterator.MoveToNextRange()); EXPECT_FALSE(column_iterator.MoveToNextRange());
NGGridTrackCollectionBase::RangeRepeatIterator row_iterator( NGGridTrackCollectionBase::RangeRepeatIterator row_iterator(
&algorithm.RowTrackCollection(), 0u); &algorithm.RowTrackCollection(), 0u);
EXPECT_RANGE(0u, 3u, row_iterator); EXPECT_RANGE(0u, 1u, row_iterator);
EXPECT_TRUE(row_iterator.MoveToNextRange());
EXPECT_RANGE(1u, 2u, row_iterator);
EXPECT_TRUE(row_iterator.MoveToNextRange()); EXPECT_TRUE(row_iterator.MoveToNextRange());
EXPECT_RANGE(3u, 1u, row_iterator); EXPECT_RANGE(3u, 1u, row_iterator);
EXPECT_TRUE(row_iterator.MoveToNextRange()); EXPECT_TRUE(row_iterator.MoveToNextRange());
...@@ -765,4 +979,64 @@ TEST_F(NGGridLayoutAlgorithmTest, FixedSizePositioning) { ...@@ -765,4 +979,64 @@ TEST_F(NGGridLayoutAlgorithmTest, FixedSizePositioning) {
EXPECT_EQ(expectation, dump); EXPECT_EQ(expectation, dump);
} }
TEST_F(NGGridLayoutAlgorithmTest, FixedSizePositioningAutoRows) {
if (!RuntimeEnabledFeatures::LayoutNGGridEnabled())
return;
LoadAhem();
SetBodyInnerHTML(R"HTML(
<style>
body {
font: 10px/1 Ahem;
}
#grid {
display: grid;
width: 200px;
height: 200px;
grid-auto-columns: 100px;
grid-auto-rows: 100px;
}
.grid_item {
width: 100px;
height: 100px;
background-color: gray;
}
.cell2 {
width: 100px;
height: 100px;
grid-column: 2;
background-color: gray;
}
</style>
<div id="wrapper">
<div id="grid">
<div class="grid_item">1</div>
<div class="cell2">2</div>
<div class="grid_item">3</div>
<div class="grid_item">4</div>
</div>
</div>
)HTML");
String dump = DumpFragmentTree(GetElementById("wrapper"));
String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
offset:unplaced size:1000x200
offset:0,0 size:200x200
offset:0,0 size:100x100
offset:0,0 size:10x10
offset:100,0 size:100x100
offset:0,0 size:10x10
offset:0,100 size:100x100
offset:0,0 size:10x10
offset:100,100 size:100x100
offset:0,0 size:10x10
)DUMP";
EXPECT_EQ(expectation, dump);
}
} // namespace blink } // namespace blink
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file
#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.h"
#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.h"
namespace blink {
NGGridPlacement::NGGridPlacement(
const wtf_size_t row_auto_repeat,
const wtf_size_t column_auto_repeat,
const wtf_size_t row_explicit_start,
const wtf_size_t column_explicit_start,
const PackingBehavior packing_behavior,
const GridTrackSizingDirection major_direction,
const ComputedStyle& grid_style,
wtf_size_t minor_max_end_line,
NGGridBlockTrackCollection& row_collection,
NGGridBlockTrackCollection& column_collection,
Vector<NGGridLayoutAlgorithm::GridItemData>& items)
: row_auto_repeat_(row_auto_repeat),
column_auto_repeat_(column_auto_repeat),
row_explicit_start_(row_explicit_start),
column_explicit_start_(column_explicit_start),
packing_behavior_(packing_behavior),
major_direction_(major_direction),
minor_direction_(major_direction == kForRows ? kForColumns : kForRows),
grid_style_(grid_style),
minor_max_end_line_(minor_max_end_line),
row_collection_(row_collection),
column_collection_(column_collection),
items_(items)
{
placement_cursor_major = ExplicitStart(major_direction_);
placement_cursor_minor = ExplicitStart(minor_direction_);
}
// https://drafts.csswg.org/css-grid/#auto-placement-algo
void NGGridPlacement::RunAutoPlacementAlgorithm() {
// Step 1. Position anything that’s not auto-positioned, if no items need
// auto positioning, then we are done.
if (!PlaceNonAutoGridItems())
return;
// Step 2. Process the items locked to the major axis.
PlaceGridItemsLockedToMajorAxis();
// Step 3. Determine the number of minor tracks in the implicit grid.
starting_minor_line_ = ExplicitStart(minor_direction_);
ending_minor_line_ = std::max(
minor_max_end_line_,
starting_minor_line_ +
BlockCollection(minor_direction_).ExplicitTracks().TotalTrackCount());
// Step 4. Position remaining grid items.
for (NGGridLayoutAlgorithm::GridItemData* grid_item :
items_not_locked_to_major_axis_) {
DCHECK(grid_item);
switch (packing_behavior_) {
case PackingBehavior::kSparse:
if (grid_item->AutoPlacement(major_direction_) ==
NGGridLayoutAlgorithm::AutoPlacementType::kMajor) {
PlaceAutoMajorAxisGridItem(*grid_item);
} else if (grid_item->AutoPlacement(major_direction_) ==
NGGridLayoutAlgorithm::AutoPlacementType::kBoth) {
PlaceAutoBothAxisGridItem(*grid_item);
}
break;
case PackingBehavior::kDense:
// TODO(janewman): implement auto placement for dense packing.
break;
}
}
}
bool NGGridPlacement::PlaceNonAutoGridItems() {
for (NGGridLayoutAlgorithm::GridItemData& grid_item : items_) {
bool has_definite_major_placement =
PlaceGridItem(major_direction_, grid_item);
bool has_definite_minor_placement =
PlaceGridItem(minor_direction_, grid_item);
// If the item has definite positions on both axis then no auto placement is
// needed.
if (has_definite_major_placement && has_definite_minor_placement)
continue;
if (has_definite_major_placement)
items_locked_to_major_axis_.push_back(&grid_item);
else
items_not_locked_to_major_axis_.push_back(&grid_item);
}
return !items_locked_to_major_axis_.IsEmpty() ||
!items_not_locked_to_major_axis_.IsEmpty();
}
void NGGridPlacement::PlaceGridItemsLockedToMajorAxis() {
// Mapping between the major axis tracks (rows or columns) and the last
// auto-placed item's position inserted on that track. This is needed to
// implement "sparse" packing for items locked to a given track.
// See https://drafts.csswg.org/css-grid/#auto-placement-algo
HashMap<wtf_size_t, wtf_size_t> minor_axis_cursors;
for (NGGridLayoutAlgorithm::GridItemData* grid_item :
items_locked_to_major_axis_) {
DCHECK(grid_item);
DCHECK_EQ(grid_item->AutoPlacement(major_direction_),
NGGridLayoutAlgorithm::AutoPlacementType::kMinor);
switch (packing_behavior_) {
case PackingBehavior::kSparse: {
wtf_size_t minor_axis_start;
if (minor_axis_cursors.Contains(grid_item->StartLine(major_direction_) +
1)) {
minor_axis_start =
minor_axis_cursors.at(grid_item->StartLine(major_direction_) + 1);
} else {
minor_axis_start = ExplicitStart(minor_direction_);
}
wtf_size_t minor_span_size =
GridPositionsResolver::SpanSizeForAutoPlacedItem(
grid_item->node.Style(), minor_direction_);
while (DoesItemOverlap(grid_item->StartLine(major_direction_),
grid_item->EndLine(major_direction_),
minor_axis_start,
minor_axis_start + minor_span_size)) {
minor_axis_start++;
}
wtf_size_t minor_axis_end = minor_axis_start + minor_span_size;
minor_axis_cursors.Set(grid_item->StartLine(major_direction_) + 1,
minor_axis_start);
minor_max_end_line_ =
std::max<wtf_size_t>(minor_max_end_line_, minor_axis_end);
// Update placement and ensure track coverage.
UpdatePlacementAndEnsureTrackCoverage(
GridSpan::TranslatedDefiniteGridSpan(minor_axis_start,
minor_axis_end),
minor_direction_, *grid_item);
} break;
case PackingBehavior::kDense:
// TODO(janewman): implement auto placement for dense packing.
break;
}
}
}
void NGGridPlacement::PlaceAutoMajorAxisGridItem(
NGGridLayoutAlgorithm::GridItemData& grid_item) {
wtf_size_t major_span_size = GridPositionsResolver::SpanSizeForAutoPlacedItem(
grid_item.node.Style(), major_direction_);
// Set the minor position of the cursor to the grid item’s minor starting
// line. If this is less than the previous column position of the cursor,
// increment the major position by 1.
if (grid_item.StartLine(minor_direction_) < placement_cursor_minor) {
placement_cursor_major++;
}
placement_cursor_minor = grid_item.StartLine(minor_direction_);
// Increment the cursor’s major position until a value is found where the grid
// item does not overlap any occupied grid cells
while (DoesItemOverlap(placement_cursor_major,
placement_cursor_major + major_span_size,
grid_item.StartLine(minor_direction_),
grid_item.EndLine(minor_direction_))) {
placement_cursor_major++;
}
// Update item and track placement.
UpdatePlacementAndEnsureTrackCoverage(
GridSpan::TranslatedDefiniteGridSpan(
placement_cursor_major, placement_cursor_major + major_span_size),
major_direction_, grid_item);
}
void NGGridPlacement::PlaceAutoBothAxisGridItem(
NGGridLayoutAlgorithm::GridItemData& grid_item) {
wtf_size_t major_span_size = GridPositionsResolver::SpanSizeForAutoPlacedItem(
grid_item.node.Style(), major_direction_);
wtf_size_t minor_span_size = GridPositionsResolver::SpanSizeForAutoPlacedItem(
grid_item.node.Style(), minor_direction_);
// Check to see if there would be overlap if this item was placed at the
// cursor. If overlap exists, increment minor position until no conflict
// exists or the item would overflow the minor axis.
while (DoesItemOverlap(
placement_cursor_major, placement_cursor_major + major_span_size,
placement_cursor_minor, placement_cursor_minor + minor_span_size)) {
placement_cursor_minor++;
if (placement_cursor_minor + minor_span_size > ending_minor_line_) {
// If the cursor overflows the minor axis, increment cursor on the major
// axis and start from the beginning.
placement_cursor_major++;
placement_cursor_minor = starting_minor_line_;
}
}
UpdatePlacementAndEnsureTrackCoverage(
GridSpan::TranslatedDefiniteGridSpan(
placement_cursor_major, placement_cursor_major + major_span_size),
major_direction_, grid_item);
UpdatePlacementAndEnsureTrackCoverage(
GridSpan::TranslatedDefiniteGridSpan(
placement_cursor_minor, placement_cursor_minor + minor_span_size),
minor_direction_, grid_item);
}
bool NGGridPlacement::PlaceGridItem(
GridTrackSizingDirection direction,
NGGridLayoutAlgorithm::NGGridLayoutAlgorithm::GridItemData& grid_item) {
GridSpan span = GridPositionsResolver::ResolveGridPositionsFromStyle(
grid_style_, grid_item.node.Style(), direction, AutoRepeat(direction));
// Indefinite positions are resolved with the auto placement algorithm.
if (span.IsIndefinite())
return false;
span.Translate(ExplicitStart(direction));
UpdatePlacementAndEnsureTrackCoverage(span, direction, grid_item);
return true;
}
void NGGridPlacement::UpdatePlacementAndEnsureTrackCoverage(
GridSpan span,
GridTrackSizingDirection track_direction,
NGGridLayoutAlgorithm::NGGridLayoutAlgorithm::GridItemData& grid_item) {
grid_item.SetSpan(span, track_direction);
BlockCollection(track_direction)
.EnsureTrackCoverage(span.StartLine(), span.IntegerSpan());
}
bool NGGridPlacement::DoesItemOverlap(wtf_size_t major_start,
wtf_size_t major_end,
wtf_size_t minor_start,
wtf_size_t minor_end) const {
DCHECK_LE(major_start, major_end);
DCHECK_LE(minor_start, minor_end);
// TODO(janewman): Implement smarter collision detection, iterating over all
// items is not ideal and has large performance implications.
for (const NGGridLayoutAlgorithm::GridItemData& grid_item : items_) {
if (grid_item.Span(major_direction_).IsIndefinite())
continue;
// Only test against definite positions.
// No collision if both start and end are on the same side of the item.
wtf_size_t item_major_start = grid_item.StartLine(major_direction_);
wtf_size_t item_major_end = grid_item.EndLine(major_direction_);
if (major_end < item_major_start)
continue;
if (major_start >= item_major_end)
continue;
// Do the same for the minor axis.
if (grid_item.Span(minor_direction_).IsIndefinite())
continue;
wtf_size_t item_minor_start = grid_item.StartLine(minor_direction_);
wtf_size_t item_minor_end = grid_item.EndLine(minor_direction_);
if (minor_end < item_minor_start)
continue;
if (minor_start >= item_minor_end)
continue;
return true;
}
return false;
}
wtf_size_t NGGridPlacement::AutoRepeat(GridTrackSizingDirection direction) {
switch (direction) {
case kForRows:
return row_auto_repeat_;
case kForColumns:
return column_auto_repeat_;
}
}
wtf_size_t NGGridPlacement::ExplicitStart(GridTrackSizingDirection direction) {
switch (direction) {
case kForRows:
return row_explicit_start_;
case kForColumns:
return column_explicit_start_;
}
}
NGGridBlockTrackCollection& NGGridPlacement::BlockCollection(
GridTrackSizingDirection direction) {
switch (direction) {
case kForRows:
return row_collection_;
case kForColumns:
return column_collection_;
}
}
} // namespace blink
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_PLACEMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_PLACEMENT_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
// This class encapsulates the Grid Item Placement Algorithm described by
// https://drafts.csswg.org/css-grid/#auto-placement-algo
class CORE_EXPORT NGGridPlacement {
STACK_ALLOCATED();
public:
enum class PackingBehavior { kSparse, kDense };
explicit NGGridPlacement(const wtf_size_t row_auto_repeat,
const wtf_size_t column_auto_repeat,
const wtf_size_t row_explicit_start,
const wtf_size_t column_explicit_start,
const PackingBehavior packing_behavior,
const GridTrackSizingDirection major_direction,
const ComputedStyle& grid_style,
wtf_size_t minor_max_end_line,
NGGridBlockTrackCollection& row_collection,
NGGridBlockTrackCollection& column_collection,
Vector<NGGridLayoutAlgorithm::GridItemData>& items);
void RunAutoPlacementAlgorithm();
private:
// Place non auto-positioned items and determine what items need auto
// placement, if any do, this returns true.
bool PlaceNonAutoGridItems();
// Place items that have a definite position on the major axis but need auto
// placement on the minor axis.
void PlaceGridItemsLockedToMajorAxis();
// Place item that has a definite position on the minor axis but need auto
// placement on the major axis.
void PlaceAutoMajorAxisGridItem(
NGGridLayoutAlgorithm::GridItemData& item_data);
// Place items that need automatic placement on both the major and minor axis.
void PlaceAutoBothAxisGridItem(
NGGridLayoutAlgorithm::GridItemData& item_data);
// Places a grid item if it has a definite position in the given direction,
// returns true if item was able to be positioned, false if item needs auto
// positioning in the given direction.
bool PlaceGridItem(
GridTrackSizingDirection grid_direction,
NGGridLayoutAlgorithm::NGGridLayoutAlgorithm::GridItemData& item_data);
void UpdatePlacementAndEnsureTrackCoverage(
GridSpan span,
GridTrackSizingDirection track_direction,
NGGridLayoutAlgorithm::NGGridLayoutAlgorithm::GridItemData& item_data);
// Returns true if the given placement would overlap with a placed item.
bool DoesItemOverlap(wtf_size_t major_start,
wtf_size_t major_end,
wtf_size_t minor_start,
wtf_size_t minor_end) const;
wtf_size_t AutoRepeat(GridTrackSizingDirection direction);
wtf_size_t ExplicitStart(GridTrackSizingDirection direction);
NGGridBlockTrackCollection& BlockCollection(
GridTrackSizingDirection direction);
const wtf_size_t row_auto_repeat_;
const wtf_size_t column_auto_repeat_;
const wtf_size_t row_explicit_start_;
const wtf_size_t column_explicit_start_;
const PackingBehavior packing_behavior_;
const GridTrackSizingDirection major_direction_;
const GridTrackSizingDirection minor_direction_;
// Used to resolve positions using GridPositionsResolver.
const ComputedStyle& grid_style_;
// Keeps track of the biggest minor end line among items with an explicit
// major line.
wtf_size_t minor_max_end_line_ = 0;
NGGridBlockTrackCollection& row_collection_;
NGGridBlockTrackCollection& column_collection_;
Vector<NGGridLayoutAlgorithm::GridItemData>& items_;
wtf_size_t starting_minor_line_ = 0;
wtf_size_t ending_minor_line_ = 0;
wtf_size_t placement_cursor_major;
wtf_size_t placement_cursor_minor;
// Subset of |items_| containing items that have a definite position on the
// major axis.
Vector<NGGridLayoutAlgorithm::GridItemData*> items_locked_to_major_axis_;
// Subset of |items_| containing items that do not have a definite position on
// the major axis.
Vector<NGGridLayoutAlgorithm::GridItemData*> items_not_locked_to_major_axis_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_PLACEMENT_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment