Commit f6a05301 authored by Ian Kilpatrick's avatar Ian Kilpatrick Committed by Commit Bot

[GridNG] Implement alignment within grid-areas.

This patch implements alignment within a grid-item's grid-area.
When creating the GridItemData we read of the containers, and
grid-item's style, or determine its alignment axis-edge, and if it
should be stretched, saving this information on the GridItemData.

When placing the grid-item it will use this to adjust the offset.

This doesn't implement baseline alignment which requires a secondary
pass to correctly determine the baseline for the column/row.

The other TODO relates to the constraint-space API, and how grid will
need a slightly new concept in order to do block-axis stretching
easily. By default non-replaced grid-items will stretch to the available
space. A new bit on the space can indicate that we should stretch to
the available space (if possible), this is similar to the shrink-to-fit
bit we have today.

This adds a self-start/self-end test as after implementing this now new
tests passed immediately, and I don't believe we had good test
coverage for inflow content with these keywords.

Bug: 1045599
Change-Id: I8c9cc51381c3d75519e52b32472b58ec96a6a85a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2492176
Commit-Queue: Ian Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: default avatarChristian Biesinger <cbiesinger@chromium.org>
Reviewed-by: default avatarKurt Catti-Schmidt <kschmi@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#821043}
parent e8b4d61b
...@@ -297,8 +297,105 @@ void NGGridLayoutAlgorithm::ConstructAndAppendGridItems() { ...@@ -297,8 +297,105 @@ void NGGridLayoutAlgorithm::ConstructAndAppendGridItems() {
reordered_item_indices_.push_back(i); reordered_item_indices_.push_back(i);
} }
namespace {
using AxisEdge = NGGridLayoutAlgorithm::AxisEdge;
// Given an |item_position| determines the correct |AxisEdge| alignment.
// Additionally will determine if the grid-item should be stretched with the
// |is_stretched| out-parameter.
AxisEdge AxisEdgeFromItemPosition(const ComputedStyle& container_style,
const ComputedStyle& style,
const ItemPosition item_position,
bool is_inline_axis,
bool* is_stretched) {
DCHECK(is_stretched);
*is_stretched = false;
// Auto-margins take precedence over any alignment properties.
if (style.MayHaveMargin()) {
bool start_auto = is_inline_axis
? style.MarginStartUsing(container_style).IsAuto()
: style.MarginBeforeUsing(container_style).IsAuto();
bool end_auto = is_inline_axis
? style.MarginEndUsing(container_style).IsAuto()
: style.MarginAfterUsing(container_style).IsAuto();
if (start_auto && end_auto)
return AxisEdge::kCenter;
else if (start_auto)
return AxisEdge::kEnd;
else if (end_auto)
return AxisEdge::kStart;
}
const auto container_writing_direction =
container_style.GetWritingDirection();
switch (item_position) {
case ItemPosition::kSelfStart:
case ItemPosition::kSelfEnd: {
// In order to determine the correct "self" axis-edge without a
// complicated set of if-branches we use two converters.
// First use the grid-item's writing-direction to convert the logical
// edge into the physical coordinate space.
LogicalToPhysical<AxisEdge> physical(style.GetWritingDirection(),
AxisEdge::kStart, AxisEdge::kEnd,
AxisEdge::kStart, AxisEdge::kEnd);
// Then use the container's writing-direction to convert the physical
// edges, into our logical coordinate space.
PhysicalToLogical<AxisEdge> logical(container_writing_direction,
physical.Top(), physical.Right(),
physical.Bottom(), physical.Left());
if (is_inline_axis) {
return item_position == ItemPosition::kSelfStart ? logical.InlineStart()
: logical.InlineEnd();
}
return item_position == ItemPosition::kSelfStart ? logical.BlockStart()
: logical.BlockEnd();
}
case ItemPosition::kCenter:
return AxisEdge::kCenter;
case ItemPosition::kFlexStart:
case ItemPosition::kStart:
return AxisEdge::kStart;
case ItemPosition::kFlexEnd:
case ItemPosition::kEnd:
return AxisEdge::kEnd;
case ItemPosition::kStretch:
*is_stretched = true;
return AxisEdge::kStart;
case ItemPosition::kBaseline:
case ItemPosition::kLastBaseline:
return AxisEdge::kBaseline;
case ItemPosition::kLeft:
DCHECK(is_inline_axis);
return container_writing_direction.IsLtr() ? AxisEdge::kStart
: AxisEdge::kEnd;
case ItemPosition::kRight:
DCHECK(is_inline_axis);
return container_writing_direction.IsRtl() ? AxisEdge::kStart
: AxisEdge::kEnd;
case ItemPosition::kLegacy:
case ItemPosition::kAuto:
case ItemPosition::kNormal:
NOTREACHED();
break;
}
NOTREACHED();
return AxisEdge::kStart;
}
} // namespace
NGGridLayoutAlgorithm::GridItemData NGGridLayoutAlgorithm::MeasureGridItem( NGGridLayoutAlgorithm::GridItemData NGGridLayoutAlgorithm::MeasureGridItem(
const NGBlockNode node) { const NGBlockNode node) {
const auto& container_style = Style();
// Before we take track sizing into account for column width contributions, // Before we take track sizing into account for column width contributions,
// have all child inline and min/max sizes measured for content-based width // have all child inline and min/max sizes measured for content-based width
// resolution. // resolution.
...@@ -323,6 +420,23 @@ NGGridLayoutAlgorithm::GridItemData NGGridLayoutAlgorithm::MeasureGridItem( ...@@ -323,6 +420,23 @@ NGGridLayoutAlgorithm::GridItemData NGGridLayoutAlgorithm::MeasureGridItem(
constraint_space, node, border_padding_in_child_writing_mode); constraint_space, node, border_padding_in_child_writing_mode);
} }
const ItemPosition normal_behaviour =
node.IsReplaced() ? ItemPosition::kStart : ItemPosition::kStretch;
// Determine the alignment for the grid-item ahead of time (we may need to
// know if it stretches ahead of time to correctly determine any block-axis
// contribution).
grid_item.inline_axis_alignment = AxisEdgeFromItemPosition(
container_style, child_style,
child_style.ResolvedJustifySelf(normal_behaviour, &container_style)
.GetPosition(),
/* is_inline_axis */ true, &grid_item.is_inline_axis_stretched);
grid_item.block_axis_alignment = AxisEdgeFromItemPosition(
container_style, child_style,
child_style.ResolvedAlignSelf(normal_behaviour, &container_style)
.GetPosition(),
/* is_inline_axis */ false, &grid_item.is_block_axis_stretched);
grid_item.margins = grid_item.margins =
ComputeMarginsFor(constraint_space, child_style, ConstraintSpace()); ComputeMarginsFor(constraint_space, child_style, ConstraintSpace());
grid_item.min_max_sizes = grid_item.min_max_sizes =
...@@ -1053,6 +1167,31 @@ void NGGridLayoutAlgorithm::PlaceGridItems() { ...@@ -1053,6 +1167,31 @@ void NGGridLayoutAlgorithm::PlaceGridItems() {
} }
} }
namespace {
// Returns the alignment offset for either the inline or block direction.
LayoutUnit AlignmentOffset(LayoutUnit container_size,
LayoutUnit size,
LayoutUnit margin_start,
LayoutUnit margin_end,
AxisEdge axis_edge) {
switch (axis_edge) {
case AxisEdge::kStart:
return margin_start;
case AxisEdge::kCenter:
return (container_size - size - margin_start - margin_end) / 2;
case AxisEdge::kEnd:
return container_size - margin_end - size;
case AxisEdge::kBaseline:
// TODO(ikilpatrick): Implement baseline alignment.
return margin_start;
}
NOTREACHED();
return LayoutUnit();
}
} // namespace
void NGGridLayoutAlgorithm::PlaceGridItem(const GridItemData& grid_item, void NGGridLayoutAlgorithm::PlaceGridItem(const GridItemData& grid_item,
LogicalOffset offset, LogicalOffset offset,
LogicalSize size) { LogicalSize size) {
...@@ -1064,14 +1203,34 @@ void NGGridLayoutAlgorithm::PlaceGridItem(const GridItemData& grid_item, ...@@ -1064,14 +1203,34 @@ void NGGridLayoutAlgorithm::PlaceGridItem(const GridItemData& grid_item,
builder.SetAvailableSize(size); builder.SetAvailableSize(size);
builder.SetPercentageResolutionSize(size); builder.SetPercentageResolutionSize(size);
builder.SetTextDirection(item_style.Direction()); builder.SetTextDirection(item_style.Direction());
builder.SetIsShrinkToFit(item_style.LogicalWidth().IsAuto());
NGConstraintSpace constraint_space = builder.ToConstraintSpace(); // TODO(ikilpatrick): We need a slightly different constraint space API now.
// Instead of a "shrink-to-fit" bit, we should have a "is-stretched" bit to
// indicate if 'auto' should stretch to fill the available space. This should
// apply to both the inline, and block axis. E.g.
// IsInlineSizeStretched / IsBlockSizeStretched or similar.
builder.SetIsFixedBlockSize(item_style.LogicalHeight().IsAuto() &&
grid_item.is_block_axis_stretched);
builder.SetIsShrinkToFit(item_style.LogicalWidth().IsAuto() &&
!grid_item.is_inline_axis_stretched);
scoped_refptr<const NGLayoutResult> result = scoped_refptr<const NGLayoutResult> result =
grid_item.node.Layout(constraint_space); grid_item.node.Layout(builder.ToConstraintSpace());
container_builder_.AddChild( const auto& physical_fragment = result->PhysicalFragment();
result->PhysicalFragment(),
{offset.inline_offset + grid_item.margins.inline_start, // Apply the grid-item's alignment (if any).
offset.block_offset + grid_item.margins.block_start}); NGFragment fragment(ConstraintSpace().GetWritingDirection(),
physical_fragment);
offset += LogicalOffset(
AlignmentOffset(size.inline_size, fragment.InlineSize(),
grid_item.margins.inline_start,
grid_item.margins.inline_end,
grid_item.inline_axis_alignment),
AlignmentOffset(
size.block_size, fragment.BlockSize(), grid_item.margins.block_start,
grid_item.margins.block_end, grid_item.block_axis_alignment));
container_builder_.AddChild(physical_fragment, offset);
} }
LayoutUnit NGGridLayoutAlgorithm::GridGap( LayoutUnit NGGridLayoutAlgorithm::GridGap(
......
...@@ -32,6 +32,7 @@ class CORE_EXPORT NGGridLayoutAlgorithm ...@@ -32,6 +32,7 @@ class CORE_EXPORT NGGridLayoutAlgorithm
NGBoxFragmentBuilder, NGBoxFragmentBuilder,
NGBlockBreakToken> { NGBlockBreakToken> {
public: public:
enum class AxisEdge { kStart, kCenter, kEnd, kBaseline };
struct GridItemData { struct GridItemData {
explicit GridItemData(const NGBlockNode node); explicit GridItemData(const NGBlockNode node);
...@@ -60,6 +61,12 @@ class CORE_EXPORT NGGridLayoutAlgorithm ...@@ -60,6 +61,12 @@ class CORE_EXPORT NGGridLayoutAlgorithm
wtf_size_t rows_begin_set_index; wtf_size_t rows_begin_set_index;
wtf_size_t rows_end_set_index; wtf_size_t rows_end_set_index;
AxisEdge inline_axis_alignment;
AxisEdge block_axis_alignment;
bool is_inline_axis_stretched;
bool is_block_axis_stretched;
bool is_spanning_flex_track : 1; bool is_spanning_flex_track : 1;
bool is_spanning_intrinsic_track : 1; bool is_spanning_intrinsic_track : 1;
}; };
......
This is a testharness.js-based test.
FAIL .grid 1 assert_equals:
<div class="grid">
<div id="c1" data-expected-width="23" data-expected-height="11"></div>
<div id="c2" data-expected-width="52" data-expected-height="24"></div>
<div id="c3" data-expected-width="83" data-expected-height="41"></div>
<div id="c4" data-expected-width="120" data-expected-height="60"></div>
</div>
width expected 23 but got 784
Harness: the test ran to completion.
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