Sam Davis OmekaraThis is done barring EdgeIntersection and Get Edge for flex cross gaps
Done
Missing some test addition to TestExpectations but wanted to open this up for review. PTAL 😊
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
LayoutUnit border_scrollbar_padding =
is_column_
? container_builder_->BorderScrollbarPadding().block_end
: container_builder_->BorderScrollbarPadding().inline_end;
content_main_end_ =
is_column_
? container_builder_->InitialBorderBoxSize().block_size -
border_scrollbar_padding
: container_builder_->InlineSize() - border_scrollbar_padding;
I'm ok with keeping the flex changes in this CL, but let's call out in the CL description why we made these changes i.e.:
// Computes the end offset for a flex cross gap at `cross_gap_index`. The end
// offset is either:
// - The container's content end, or
// - The offset of the main gap where this cross gap ends (tracked by
// `main_gap_running_index_`)
Thank you for detailed comments. I wish more of Blink had this.
// Gap locations are used for painting gap decorations.
We should link to readme here too.
// 1. Cross gaps that appear before the main gap
// 2. Cross gaps that appear after the main gap
would be good to link to the readme here, for future readers of this code, since this part can be confusing.
// For a flex cross gap:
// - There are exactly two intersections:
// 1. The gap's start offset
// 2. Its computed end offset (either a main gap or the container's
// content-end edge)
ditto here.
bool HasCrossGapsBefore() const {
return range_of_cross_gaps_before_.IsValid();
}
bool HasCrossGapsAfter() const {
return range_of_cross_gaps_after_.IsValid();
}
wtf_size_t GetCrossGapBeforeStart() const {
return range_of_cross_gaps_before_.Start();
}
wtf_size_t GetCrossGapBeforeEnd() const {
return range_of_cross_gaps_before_.End();
}
wtf_size_t GetCrossGapAfterStart() const {
return range_of_cross_gaps_after_.Start();
}
wtf_size_t GetCrossGapAfterEnd() const {
return range_of_cross_gaps_after_.End();
}
void IncrementRangeOfCrossGapsBefore(wtf_size_t cross_gap_index) {
range_of_cross_gaps_before_.Increment(cross_gap_index);
}
void IncrementRangeOfCrossGapsAfter(wtf_size_t cross_gap_index) {
range_of_cross_gaps_after_.Increment(cross_gap_index);
}
void SetRangeOfCrossGapsBefore(const CrossGapRange& range) {
range_of_cross_gaps_before_ = range;
}
void SetRangeOfCrossGapsAfter(const CrossGapRange& range) {
range_of_cross_gaps_after_ = range;
}
Call out in the description why we made these changes to the way with which we interface with the ranges.
<!DOCTYPE html>
call out the reason for these changes in the CL description too.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
void GapPainter::Paint(GridTrackSizingDirection track_direction,
nit: I feel like this function currently is doing too much (i feel like our previous version did too) and can be split to make it cleaner.
Foe example, we could have a separate function that handles returning the `center`, another for creating the `LogicalRect`, and maybe even the while loop itself could be split out into something like `ComputeGapSegments`
This is awesome, can't wait to see it land!
mutable wtf_size_t main_gap_running_index_ = kNotFound;
Can you make `ComputeEndOffsetForFlexCrossGap` non-const vs making this mutable?
GridTrackSizingDirection main_direction_ = kForRows;
This is for all display types with gaps, right? Consider creating a new enum that uses inline/block for axis direction vs rows/columns. This will make it very obvious later when you assign inline vs block later (in `GapPainter::Paint`).
I realize Gap Decorations uses the concept of rows/columns already for non-grid display types, so this is also fine to keep as-is.
intersections.push_back(offset);
`intersections.push_back(cross_gap.GetGapStartOffset().inline_offset);`
// TODO(samomekarajr): Can Do Merge two sorted lists here instead.
This will also avoid the copy below - you can merge straight into `intersections`, right? (might be worth adding to the TODO).
for (const auto& cross_gap : all_cross_gaps) {
```
// Copy merged and sorted values into `intersections`.
```
LayoutUnit offset = main_gap.GetGapStartOffset();
intersections.push_back(offset);
You can combine these two lines:
```
intersections.push_back(main_gap.GetGapStartOffset());
```
main_gap_running_index_ += 1;
Nit: `++main_gap_running_index_;`
CHECK(main_gap_running_index_ < GetMainGaps().size());
`CHECK_LT(main_gap_running_index_, GetMainGaps().size());`
(also do this above when you index into `GetMainGaps()`)
You can also store a reference to `GetMainGaps()` in a variable since you access it a few times here.
CHECK(!is_main_gap);
This can probably be a `DCHECK` - you only want to `CHECK` if something unsafe or catastrophic would happen anyways
GetCrossGaps()[gap_index].GetEdgeIntersectionState();
Always DCHECK or DCHECK before doing an array index like this `DCHECK_LT(gap_index, GetCrossGaps().size());`
BoxSide box_side;
if (style.IsHorizontalWritingMode()) {
if (style.IsLeftToRightDirection()) {
box_side = direction == kForColumns ? BoxSide::kLeft : BoxSide::kTop;
} else {
box_side = direction == kForColumns ? BoxSide::kRight : BoxSide::kBottom;
}
} else {
// Vertical Writing Mode.
const auto writing_direction = style.GetWritingDirection();
if (writing_direction.InlineEnd() == PhysicalDirection::kDown) {
// Top to Bottom.
box_side = direction == kForColumns ? BoxSide::kTop : BoxSide::kLeft;
} else {
// Bottom to Top.
box_side = direction == kForColumns ? BoxSide::kBottom : BoxSide::kRight;
}
}
return box_side;
Can this be avoided by using `PhysicalBoxSides` and `ToLogical`?
if (is_main) {
center = gap_geometry.GetMainGaps()[gap_index].GetGapStartOffset();
} else {
const LogicalOffset cross_gap_offset =
gap_geometry.GetCrossGaps()[gap_index].GetGapStartOffset();
center = track_direction == kForColumns ? cross_gap_offset.inline_offset
: cross_gap_offset.block_offset;
}
This might be cleaner to move the logic into `GapGeometry`, something like `GetStartOffset(wtf_size_t index, bool is_main_direction, GridTrackSizingDirection track_direction);`
Then this whole block reduces down to:
`LayoutUnit center = gap_geometry.GetStartOffset(index, is_main, track_direction);`
Vector<LayoutUnit> intersections;
gap_geometry.PopulateIntersectionListForGap(intersections, track_direction,
gap_index);
Since `intersections` is just an out-parameter, you can just return it:
```
Vector<LayoutUnit> intersections = gap_geometry.PopulateIntersectionListForGap(intersections, track_direction,
gap_index);
```
The compiler will automatically call `move` so it's not copying the vectors. See https://shaharmike.com/cpp/rvo/.
end = intersections.size() - 1;
I would store this in a variable, something like:
`const wtf_size_t last_index = intersections.size() - 1;`
LayoutUnit start_width =
gap_geometry.IsEdgeIntersection(gap_index, start,
intersections.size(), is_main)
? LayoutUnit()
: cross_gutter_width;
LayoutUnit end_width = gap_geometry.IsEdgeIntersection(
gap_index, end, intersections.size(), is_main)
? LayoutUnit()
: cross_gutter_width;
Make sure to `const` all of the locals that don't need to change
LayoutUnit(start_width / 2.0f) - start_outset;
I think you can just do: `(start_width / 2) - start_outset` and don't need to wrap the intermediate value in a `LayoutUnit`
if (track_direction == kForColumns) {
// For columns, paint a vertical strip at the center of the gap.
inline_start = center - (rule_thickness / 2);
inline_size = rule_thickness;
// Compute the block positions using the computed offsets.
block_start = intersections[start] + decoration_start_offset;
block_size = intersections[end] - block_start - decoration_end_offset;
} else {
// For rows, paint a horizontal strip at the center of the gap.
block_start = center - (rule_thickness / 2);
block_size = rule_thickness;
// Compute the inline positions using the computed offsets.
inline_start = intersections[start] + decoration_start_offset;
inline_size = intersections[end] - inline_start - decoration_end_offset;
}
You can reduce a lot of duplication here with the ternary, e.g.
```
(track_direction == kForColumn) ? inline_start : block_start = center - (rule_thickness / 2);
```
(I think that'll work)
start = end;
Add a newline before this so it's more obvious how the iteration advances.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
LayoutUnit border_scrollbar_padding =
is_column_
? container_builder_->BorderScrollbarPadding().block_end
: container_builder_->BorderScrollbarPadding().inline_end;
content_main_end_ =
is_column_
? container_builder_->InitialBorderBoxSize().block_size -
border_scrollbar_padding
: container_builder_->InlineSize() - border_scrollbar_padding;
I'm ok with keeping the flex changes in this CL, but let's call out in the CL description why we made these changes i.e.:
- Previously we were not setting the ContentStart/End offsets in cases where we only had one item in a flex line.
- The offset for the main gap does not need to be adjusted to inline/block depending on column, it will always be the offset of where the flex line ends + gap. (relevant test was adjusted as such.)
Done
// Computes the end offset for a flex cross gap at `cross_gap_index`. The end
// offset is either:
// - The container's content end, or
// - The offset of the main gap where this cross gap ends (tracked by
// `main_gap_running_index_`)
Thank you for detailed comments. I wish more of Blink had this.
Done
// Gap locations are used for painting gap decorations.
We should link to readme here too.
Done
mutable wtf_size_t main_gap_running_index_ = kNotFound;
Can you make `ComputeEndOffsetForFlexCrossGap` non-const vs making this mutable?
The main issue is that GapGeo is typically created during Layout and then used during Paint. The method ComputeEndOffsetForFlexCrossGap, which is called during Paint, is one of several methods that need to be const because gap geometry is const at Paint time. This means we can't use non-const methods on a const gap geometry, so it needed to be mutable.
GridTrackSizingDirection main_direction_ = kForRows;
This is for all display types with gaps, right? Consider creating a new enum that uses inline/block for axis direction vs rows/columns. This will make it very obvious later when you assign inline vs block later (in `GapPainter::Paint`).
I realize Gap Decorations uses the concept of rows/columns already for non-grid display types, so this is also fine to keep as-is.
Okay, if I get this right, you're of the opinion that we don't rely on the grid rows/cols concept and use inline vs block? Added this as a TODO to look into more. Let me know if this sufficient.
intersections.push_back(offset);
Sam Davis Omekara`intersections.push_back(cross_gap.GetGapStartOffset().inline_offset);`
Done
// 1. Cross gaps that appear before the main gap
// 2. Cross gaps that appear after the main gap
would be good to link to the readme here, for future readers of this code, since this part can be confusing.
Done
// TODO(samomekarajr): Can Do Merge two sorted lists here instead.
This will also avoid the copy below - you can merge straight into `intersections`, right? (might be worth adding to the TODO).
Done
for (const auto& cross_gap : all_cross_gaps) {
```
// Copy merged and sorted values into `intersections`.
```
Done
LayoutUnit offset = main_gap.GetGapStartOffset();
intersections.push_back(offset);
You can combine these two lines:
```
intersections.push_back(main_gap.GetGapStartOffset());
```
Done
// For a flex cross gap:
// - There are exactly two intersections:
// 1. The gap's start offset
// 2. Its computed end offset (either a main gap or the container's
// content-end edge)
Sam Davis Omekaraditto here.
Done
main_gap_running_index_ += 1;
Sam Davis OmekaraNit: `++main_gap_running_index_;`
Done
CHECK(main_gap_running_index_ < GetMainGaps().size());
`CHECK_LT(main_gap_running_index_, GetMainGaps().size());`
(also do this above when you index into `GetMainGaps()`)
You can also store a reference to `GetMainGaps()` in a variable since you access it a few times here.
Done
CHECK(!is_main_gap);
This can probably be a `DCHECK` - you only want to `CHECK` if something unsafe or catastrophic would happen anyways
Done
GetCrossGaps()[gap_index].GetEdgeIntersectionState();
Always DCHECK or DCHECK before doing an array index like this `DCHECK_LT(gap_index, GetCrossGaps().size());`
I actually used to do things like this but in one review, Kevin had mentioned it's redundant since [] operator in blink Vector already CHECKs this before accessing memory.
bool HasCrossGapsBefore() const {
return range_of_cross_gaps_before_.IsValid();
}
bool HasCrossGapsAfter() const {
return range_of_cross_gaps_after_.IsValid();
}
wtf_size_t GetCrossGapBeforeStart() const {
return range_of_cross_gaps_before_.Start();
}
wtf_size_t GetCrossGapBeforeEnd() const {
return range_of_cross_gaps_before_.End();
}
wtf_size_t GetCrossGapAfterStart() const {
return range_of_cross_gaps_after_.Start();
}
wtf_size_t GetCrossGapAfterEnd() const {
return range_of_cross_gaps_after_.End();
}
void IncrementRangeOfCrossGapsBefore(wtf_size_t cross_gap_index) {
range_of_cross_gaps_before_.Increment(cross_gap_index);
}
void IncrementRangeOfCrossGapsAfter(wtf_size_t cross_gap_index) {
range_of_cross_gaps_after_.Increment(cross_gap_index);
}
void SetRangeOfCrossGapsBefore(const CrossGapRange& range) {
range_of_cross_gaps_before_ = range;
}
void SetRangeOfCrossGapsAfter(const CrossGapRange& range) {
range_of_cross_gaps_after_ = range;
}
Call out in the description why we made these changes to the way with which we interface with the ranges.
Done
BoxSide box_side;
if (style.IsHorizontalWritingMode()) {
if (style.IsLeftToRightDirection()) {
box_side = direction == kForColumns ? BoxSide::kLeft : BoxSide::kTop;
} else {
box_side = direction == kForColumns ? BoxSide::kRight : BoxSide::kBottom;
}
} else {
// Vertical Writing Mode.
const auto writing_direction = style.GetWritingDirection();
if (writing_direction.InlineEnd() == PhysicalDirection::kDown) {
// Top to Bottom.
box_side = direction == kForColumns ? BoxSide::kTop : BoxSide::kLeft;
} else {
// Bottom to Top.
box_side = direction == kForColumns ? BoxSide::kBottom : BoxSide::kRight;
}
}
return box_side;
Can this be avoided by using `PhysicalBoxSides` and `ToLogical`?
I'm not completely follow this 100%, but I checked PhysicalBoxSides and it covers all four sides. However, for the paint Op, we need just one side, which is BoxSide. Other similar methods that use this Paint Op also create a BoxSide.
void GapPainter::Paint(GridTrackSizingDirection track_direction,
nit: I feel like this function currently is doing too much (i feel like our previous version did too) and can be split to make it cleaner.
Foe example, we could have a separate function that handles returning the `center`, another for creating the `LogicalRect`, and maybe even the while loop itself could be split out into something like `ComputeGapSegments`
Hmm, I see your point. but I'm weary of refactoring now that I haven't introduced the rule breaking code. I'll add this as a TODO and see how best to approach this refactoring to make function more modular.
if (is_main) {
center = gap_geometry.GetMainGaps()[gap_index].GetGapStartOffset();
} else {
const LogicalOffset cross_gap_offset =
gap_geometry.GetCrossGaps()[gap_index].GetGapStartOffset();
center = track_direction == kForColumns ? cross_gap_offset.inline_offset
: cross_gap_offset.block_offset;
}
This might be cleaner to move the logic into `GapGeometry`, something like `GetStartOffset(wtf_size_t index, bool is_main_direction, GridTrackSizingDirection track_direction);`
Then this whole block reduces down to:
`LayoutUnit center = gap_geometry.GetStartOffset(index, is_main, track_direction);`
Done. You're so right. Thanks!!
Vector<LayoutUnit> intersections;
gap_geometry.PopulateIntersectionListForGap(intersections, track_direction,
gap_index);
Since `intersections` is just an out-parameter, you can just return it:
```
Vector<LayoutUnit> intersections = gap_geometry.PopulateIntersectionListForGap(intersections, track_direction,
gap_index);
```The compiler will automatically call `move` so it's not copying the vectors. See https://shaharmike.com/cpp/rvo/.
Okay, if that's the case I'll rename functions to something like: `GenerateIntersection**`
end = intersections.size() - 1;
I would store this in a variable, something like:
`const wtf_size_t last_index = intersections.size() - 1;`
Done
LayoutUnit start_width =
gap_geometry.IsEdgeIntersection(gap_index, start,
intersections.size(), is_main)
? LayoutUnit()
: cross_gutter_width;
LayoutUnit end_width = gap_geometry.IsEdgeIntersection(
gap_index, end, intersections.size(), is_main)
? LayoutUnit()
: cross_gutter_width;
Make sure to `const` all of the locals that don't need to change
Done
LayoutUnit(start_width / 2.0f) - start_outset;
I think you can just do: `(start_width / 2) - start_outset` and don't need to wrap the intermediate value in a `LayoutUnit`
Actually tried this so many times but it doesn't work, because:
"no viable conversion from 'float' to 'const LayoutUnit' "
if (track_direction == kForColumns) {
// For columns, paint a vertical strip at the center of the gap.
inline_start = center - (rule_thickness / 2);
inline_size = rule_thickness;
// Compute the block positions using the computed offsets.
block_start = intersections[start] + decoration_start_offset;
block_size = intersections[end] - block_start - decoration_end_offset;
} else {
// For rows, paint a horizontal strip at the center of the gap.
block_start = center - (rule_thickness / 2);
block_size = rule_thickness;
// Compute the inline positions using the computed offsets.
inline_start = intersections[start] + decoration_start_offset;
inline_size = intersections[end] - inline_start - decoration_end_offset;
}
You can reduce a lot of duplication here with the ternary, e.g.
```
(track_direction == kForColumn) ? inline_start : block_start = center - (rule_thickness / 2);
```(I think that'll work)
I chose explicit branching here since it makes things clearer, rather than combining everything into one line with a ternary operator. If you agree, I'd prefer to keep it this way :)
start = end;
Add a newline before this so it's more obvious how the iteration advances.
Done
call out the reason for these changes in the CL description too.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Getting very close. Also, do you know why it added so many people to CC?
mutable wtf_size_t main_gap_running_index_ = kNotFound;
Sam Davis OmekaraCan you make `ComputeEndOffsetForFlexCrossGap` non-const vs making this mutable?
The main issue is that GapGeo is typically created during Layout and then used during Paint. The method ComputeEndOffsetForFlexCrossGap, which is called during Paint, is one of several methods that need to be const because gap geometry is const at Paint time. This means we can't use non-const methods on a const gap geometry, so it needed to be mutable.
Typically you want to limit use of `mutable`, but this seems like a reasonable scenario to have a side effect. Can you add a comment here indicating why it needs to be mutable?
GridTrackSizingDirection main_direction_ = kForRows;
Sam Davis OmekaraThis is for all display types with gaps, right? Consider creating a new enum that uses inline/block for axis direction vs rows/columns. This will make it very obvious later when you assign inline vs block later (in `GapPainter::Paint`).
I realize Gap Decorations uses the concept of rows/columns already for non-grid display types, so this is also fine to keep as-is.
Okay, if I get this right, you're of the opinion that we don't rely on the grid rows/cols concept and use inline vs block? Added this as a TODO to look into more. Let me know if this sufficient.
That works for me
GetCrossGaps()[gap_index].GetEdgeIntersectionState();
Sam Davis OmekaraAlways DCHECK or DCHECK before doing an array index like this `DCHECK_LT(gap_index, GetCrossGaps().size());`
I actually used to do things like this but in one review, Kevin had mentioned it's redundant since [] operator in blink Vector already CHECKs this before accessing memory.
Nice, I wasn't aware of that
BoxSide box_side;
if (style.IsHorizontalWritingMode()) {
if (style.IsLeftToRightDirection()) {
box_side = direction == kForColumns ? BoxSide::kLeft : BoxSide::kTop;
} else {
box_side = direction == kForColumns ? BoxSide::kRight : BoxSide::kBottom;
}
} else {
// Vertical Writing Mode.
const auto writing_direction = style.GetWritingDirection();
if (writing_direction.InlineEnd() == PhysicalDirection::kDown) {
// Top to Bottom.
box_side = direction == kForColumns ? BoxSide::kTop : BoxSide::kLeft;
} else {
// Bottom to Top.
box_side = direction == kForColumns ? BoxSide::kBottom : BoxSide::kRight;
}
}
return box_side;
Sam Davis OmekaraCan this be avoided by using `PhysicalBoxSides` and `ToLogical`?
I'm not completely follow this 100%, but I checked PhysicalBoxSides and it covers all four sides. However, for the paint Op, we need just one side, which is BoxSide. Other similar methods that use this Paint Op also create a BoxSide.
I think you can do something like this:
```
PhysicalBoxSides sides;
sides.left = (direction == kForColumns);
sides.top = (direction == kForColumns);
LogicalBoxSides logical_sizes = sides.ToLogical(
WritingDirectionMode(style.GetWritingMode(), style.GetWritingDirection()))
return (direction == kForColumns) ? logical_sizes.left : logical_sizes.top;
```
...where you just set top and left and let the converter handle the writing mode/direction. If this is too complicated what you have is fine. I might have mixed something up in this example too.
LayoutUnit(start_width / 2.0f - start_outset);
Do you need the `LayoutUnit` constructor here? Algebra on `LayoutUnit` should just work for the most part. You might need to change the `2.0f` to just `2`.
if (track_direction == kForColumns) {
// For columns, paint a vertical strip at the center of the gap.
inline_start = center - (rule_thickness / 2);
inline_size = rule_thickness;
// Compute the block positions using the computed offsets.
block_start = intersections[start] + decoration_start_offset;
block_size = intersections[end] - block_start - decoration_end_offset;
} else {
// For rows, paint a horizontal strip at the center of the gap.
block_start = center - (rule_thickness / 2);
block_size = rule_thickness;
// Compute the inline positions using the computed offsets.
inline_start = intersections[start] + decoration_start_offset;
inline_size = intersections[end] - inline_start - decoration_end_offset;
}
Sam Davis OmekaraYou can reduce a lot of duplication here with the ternary, e.g.
```
(track_direction == kForColumn) ? inline_start : block_start = center - (rule_thickness / 2);
```(I think that'll work)
I chose explicit branching here since it makes things clearer, rather than combining everything into one line with a ternary operator. If you agree, I'd prefer to keep it this way :)
This is kind of a lot of duplication. Another option is to use `std::swap`. e.g.
```
// For columns, paint a vertical strip at the center of the gap.
inline_start = center - (rule_thickness / 2);
inline_size = rule_thickness;
// Compute the block positions using the computed offsets.
block_start = intersections[start] + decoration_start_offset;
block_size = intersections[end] - block_start - decoration_end_offset;
// For rows, swap inline to block.
if(track_direction == kForRows) {
std::swap(inline_start, block_start);
std::swap(inline_end, block_end);
}
```
Getting very close. Also, do you know why it added so many people to CC?
I think I didn't sync properly one time + I had this as a dependent change of some other branch that kept being out of sync with main. So when I uploaded it showed the diff between this and the current main and added a whole bunch of people to the CC.
mutable wtf_size_t main_gap_running_index_ = kNotFound;
Sam Davis OmekaraCan you make `ComputeEndOffsetForFlexCrossGap` non-const vs making this mutable?
Kurt Catti-SchmidtThe main issue is that GapGeo is typically created during Layout and then used during Paint. The method ComputeEndOffsetForFlexCrossGap, which is called during Paint, is one of several methods that need to be const because gap geometry is const at Paint time. This means we can't use non-const methods on a const gap geometry, so it needed to be mutable.
Typically you want to limit use of `mutable`, but this seems like a reasonable scenario to have a side effect. Can you add a comment here indicating why it needs to be mutable?
Done
BoxSide box_side;
if (style.IsHorizontalWritingMode()) {
if (style.IsLeftToRightDirection()) {
box_side = direction == kForColumns ? BoxSide::kLeft : BoxSide::kTop;
} else {
box_side = direction == kForColumns ? BoxSide::kRight : BoxSide::kBottom;
}
} else {
// Vertical Writing Mode.
const auto writing_direction = style.GetWritingDirection();
if (writing_direction.InlineEnd() == PhysicalDirection::kDown) {
// Top to Bottom.
box_side = direction == kForColumns ? BoxSide::kTop : BoxSide::kLeft;
} else {
// Bottom to Top.
box_side = direction == kForColumns ? BoxSide::kBottom : BoxSide::kRight;
}
}
return box_side;
Sam Davis OmekaraCan this be avoided by using `PhysicalBoxSides` and `ToLogical`?
Kurt Catti-SchmidtI'm not completely follow this 100%, but I checked PhysicalBoxSides and it covers all four sides. However, for the paint Op, we need just one side, which is BoxSide. Other similar methods that use this Paint Op also create a BoxSide.
I think you can do something like this:
```
PhysicalBoxSides sides;
sides.left = (direction == kForColumns);
sides.top = (direction == kForColumns);
LogicalBoxSides logical_sizes = sides.ToLogical(
WritingDirectionMode(style.GetWritingMode(), style.GetWritingDirection()))
return (direction == kForColumns) ? logical_sizes.left : logical_sizes.top;
```...where you just set top and left and let the converter handle the writing mode/direction. If this is too complicated what you have is fine. I might have mixed something up in this example too.
Started this but added as a TODO because I got cold feet over seeing the PhysidalBoxSide initializes all four sides to true initially. Not sure what the implication would be here... Added as a TODO to explore. Thanks for the suggestion.
LayoutUnit center =
Sam Davis Omekara`const LayoutUnit`
Done
Do you need the `LayoutUnit` constructor here? Algebra on `LayoutUnit` should just work for the most part. You might need to change the `2.0f` to just `2`.
Done
Opted for shared names as discussed offline. Let me know if this is sufficient.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Code-Review | +1 |
if (track_direction == kForColumns) {
// For columns, paint a vertical strip at the center of the gap.
inline_start = center - (rule_thickness / 2);
inline_size = rule_thickness;
// Compute the block positions using the computed offsets.
block_start = intersections[start] + decoration_start_offset;
block_size = intersections[end] - block_start - decoration_end_offset;
} else {
// For rows, paint a horizontal strip at the center of the gap.
block_start = center - (rule_thickness / 2);
block_size = rule_thickness;
// Compute the inline positions using the computed offsets.
inline_start = intersections[start] + decoration_start_offset;
inline_size = intersections[end] - inline_start - decoration_end_offset;
}
Sam Davis OmekaraYou can reduce a lot of duplication here with the ternary, e.g.
```
(track_direction == kForColumn) ? inline_start : block_start = center - (rule_thickness / 2);
```(I think that'll work)
Kurt Catti-SchmidtI chose explicit branching here since it makes things clearer, rather than combining everything into one line with a ternary operator. If you agree, I'd prefer to keep it this way :)
Sam Davis OmekaraThis is kind of a lot of duplication. Another option is to use `std::swap`. e.g.
```
// For columns, paint a vertical strip at the center of the gap.
inline_start = center - (rule_thickness / 2);
inline_size = rule_thickness;
// Compute the block positions using the computed offsets.
block_start = intersections[start] + decoration_start_offset;
block_size = intersections[end] - block_start - decoration_end_offset;// For rows, swap inline to block.
if(track_direction == kForRows) {
std::swap(inline_start, block_start);
std::swap(inline_end, block_end);
}```
Opted for shared names as discussed offline. Let me know if this is sufficient.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
gwsq error while processing this CL: Error looking up group chromeos-commercial-r...@google.com: Streaming RPC error: status 2 error: ??? to (unknown) : DEADLINE_EXCEEDED goa-auth: RPC::DEADLINE_EXCEEDED: LocalIAM check returned an error: goa-auth: RPC::DEADLINE_EXCEEDED: LocalIAM check returned an error:
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Code-Review | +1 |
intersection_index == intersection_count - 1;
You should `DHECK_GT(intersection_count, 0);` to validate that it doesn't underflow.
You can also store `intersection_count - 1` in a variable, since it's repeated a few times.
LayoutUnit cross_gutter_width = track_direction == kForRows
? gap_geometry.GetInlineGapSize()
: gap_geometry.GetBlockGapSize();
Nit: some of these variables in this method can be `const`. This, `gap_count`, and maybe more.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
You should `DHECK_GT(intersection_count, 0);` to validate that it doesn't underflow.
You can also store `intersection_count - 1` in a variable, since it's repeated a few times.
Done
LayoutUnit cross_gutter_width = track_direction == kForRows
? gap_geometry.GetInlineGapSize()
: gap_geometry.GetBlockGapSize();
Nit: some of these variables in this method can be `const`. This, `gap_count`, and maybe more.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Vector<LayoutUnit> intersections;
just realized this, but I think we should `ReserveInitialCapacity(main_gaps_.size() + 2)` since at most we'll have that number of cross intersections? I guess for flex we know its just two, so we could instead call .reserve() inside each of the cases.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
// ToLogical to avoid manual conversion logic.
Might be worth to do this now, e.g.
or LogicalToPhysical
just realized this, but I think we should `ReserveInitialCapacity(main_gaps_.size() + 2)` since at most we'll have that number of cross intersections? I guess for flex we know its just two, so we could instead call .reserve() inside each of the cases.
Done
Might be worth to do this now, e.g.
or LogicalToPhysical
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
change.
nit: might be worth noting the test failures. Were they all related to this?
if (num_lines_ > 1 && !content_main_end_.has_value()) {
Will this not be guaranteed if `is_first_item` is true? (i.e. could this be a CHECK instead?)
main_gaps_[flex_line_index].SetGapStartOffset(cross_intersection_offset);
Kind of confused why we populate main gaps with the cross intersection. Should this variable be renamed? If not, might be worth a comment to explain
// this index as part of its calculation. Making this mutable allows us to
// maintain necessary state without breaking const-correctness for the overall
// GapGeometry object.
mutable wtf_size_t main_gap_running_index_ = kNotFound;
Would it make sense for paint to hold this instead and pass it in as an input/output param wherever needed?
// Paint, but ComputeEndOffsetForFlexCrossGap (called at paint time) updates
ComputeEndOffsetForFlexCrossGap()
// - The container's content end, or
// - The offset of the main gap where this cross gap ends (tracked by
// `main_gap_running_index_`)
Might be worth a note on when it is each of these
Vector<LayoutUnit> GenerateIntersectionListForMain(
GridTrackSizingDirection direction,
wtf_size_t gap_index) const;
// Generates intersections for a cross gap. For grid containers, this
// includes the container content edges and every main gap offset. For flex
// containers, it includes the cross-gap start offset and its computed end
// offset.
Vector<LayoutUnit> GenerateIntersectionListForCross(
Can these two be made private?
// Generates intersection offsets for a main gap at `gap_index`. This list
Same here and below "Returns a list of..."
// This methods generates `intersections` with the ordered intersection
I'm guessing this is a var within the method. If so, I'd remove this and say something like "returns a Vector of offsets" instead
LayoutUnit GetGapCenterOffset(GridTrackSizingDirection direction,
nit: might be worth a brief comment for this one, too
bool IsDirectionMain(GridTrackSizingDirection direction) const {
nit: `IsMainDirection`
LayoutUnit GapGeometry::GetGapCenterOffset(GridTrackSizingDirection direction,
If it is the start, it won't be center, right? Should this be GetGapIntersectionOffset()? Not a strong preference though either way
} else {
nit: else isn't needed in this case
i.e.
```
if (IsDirectionMain(direction)) {
return GenerateIntersectionListForMain(direction, gap_index);
}
return GenerateIntersectionListForCross(direction, gap_index);
```
Vector<LayoutUnit> GapGeometry::GenerateIntersectionListForMain(
Instead of building up a vector, would it make sense to have an iterator instead?
I guess maybe not because of the complexity of flexbox?
Vector<LayoutUnit> GapGeometry::GenerateIntersectionListForMain(
GenerateMainIntersectionList
same for Cross
CHECK(GetMainDirection() == kForRows);
CHECK_EQ
CrossGaps all_cross_gaps;
nit: could just be cross_gaps
// advance main_gap_running_index_ to the next main gap.
missing ``
// * kBoth: Both first and last intersections are edges.
// * kStart: Only the first intersection is an edge.
// * kEnd: Only the last intersection is an edge.
Can we not just use these for all cases rather than diverging?
return range_of_cross_gaps_before_.Start();
Should this CHECK(HasCrossGapsBefore());
Same with other relevant methods
class GapPainter {
Might be worth an high level overall comment for this class (even if just with a link to the spec or something)
class GapPainter {
nit: Would it make sense for this to be GapDecorationPainter instead to avoid confusion with just painting a normal gap (which I guess isn't really painted at all, but just an offset)
BoxSide BoxSideFromGridDirection(const ComputedStyle& style,
This may make sense in one of the grid utils files instead
wtf_size_t end = start;
Why do we set this to start instead of directly to `last_intersection_index`?
Also I don't see end or last_intersection_index ever changing. Do we need both of these vars, or am I just overlooking something?
if (start >= end) {
Is this ever going to happen because of the `while (start < last_intersection_index) {`?
const bool is_column_gap = (track_direction == kForColumns);
I'd move this up higher so we don't need to recompute it throughout the loop (and there are some places we can use this higher up, as well)
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
nit: might be worth noting the test failures. Were they all related to this?
Yes, they are. Added to the CL desc
if (num_lines_ > 1 && !content_main_end_.has_value()) {
Will this not be guaranteed if `is_first_item` is true? (i.e. could this be a CHECK instead?)
Was going to be on board but I think this code path hits for every first item in every line so the extra condition ensures we only set it once.
cc: @javi...@microsoft.com is this assumption right?
main_gaps_[flex_line_index].SetGapStartOffset(cross_intersection_offset);
Kind of confused why we populate main gaps with the cross intersection. Should this variable be renamed? If not, might be worth a comment to explain
Great catch. Wrong naming, renaming to gap_offset.
// this index as part of its calculation. Making this mutable allows us to
// maintain necessary state without breaking const-correctness for the overall
// GapGeometry object.
mutable wtf_size_t main_gap_running_index_ = kNotFound;
Would it make sense for paint to hold this instead and pass it in as an input/output param wherever needed?
I think that might work and make sense, but might need some restructuring. Are you okay landing as is while I explore that in a follow up change or you think it's worth pursuing now.
// Paint, but ComputeEndOffsetForFlexCrossGap (called at paint time) updates
Sam Davis OmekaraComputeEndOffsetForFlexCrossGap()
Done
// - The container's content end, or
// - The offset of the main gap where this cross gap ends (tracked by
// `main_gap_running_index_`)
Might be worth a note on when it is each of these
Done
Vector<LayoutUnit> GenerateIntersectionListForMain(
GridTrackSizingDirection direction,
wtf_size_t gap_index) const;
// Generates intersections for a cross gap. For grid containers, this
// includes the container content edges and every main gap offset. For flex
// containers, it includes the cross-gap start offset and its computed end
// offset.
Vector<LayoutUnit> GenerateIntersectionListForCross(
Can these two be made private?
All three can. Yes!
// Generates intersection offsets for a main gap at `gap_index`. This list
Same here and below "Returns a list of..."
Done
// This methods generates `intersections` with the ordered intersection
I'm guessing this is a var within the method. If so, I'd remove this and say something like "returns a Vector of offsets" instead
Done
LayoutUnit GetGapCenterOffset(GridTrackSizingDirection direction,
nit: might be worth a brief comment for this one, too
Ditto, I created the
bool IsDirectionMain(GridTrackSizingDirection direction) const {
Sam Davis Omekaranit: `IsMainDirection`
Done
LayoutUnit GapGeometry::GetGapCenterOffset(GridTrackSizingDirection direction,
If it is the start, it won't be center, right? Should this be GetGapIntersectionOffset()? Not a strong preference though either way
Yeahh that method and naming is so confusing. I'm creating a separate CL[1] that removes start from these offset's naming, I think this can be renamed to GetGapOffset with that landing.
[1]: https://chromium-review.googlesource.com/c/chromium/src/+/6915478
nit: else isn't needed in this case
i.e.
```
if (IsDirectionMain(direction)) {
return GenerateIntersectionListForMain(direction, gap_index);
}
return GenerateIntersectionListForCross(direction, gap_index);
```
Done
Vector<LayoutUnit> GapGeometry::GenerateIntersectionListForMain(
Sam Davis OmekaraGenerateMainIntersectionList
same for Cross
Done
Vector<LayoutUnit> GapGeometry::GenerateIntersectionListForMain(
Instead of building up a vector, would it make sense to have an iterator instead?
I guess maybe not because of the complexity of flexbox?
I had explored this earlier a bit, but I think the iterator route imo was becoming a tad too complicated for something that could easily be represented as a list. More than happy to add as a TODO and explore that further later.
CHECK(GetMainDirection() == kForRows);
Sam Davis OmekaraCHECK_EQ
Done
nit: could just be cross_gaps
Done
// advance main_gap_running_index_ to the next main gap.
Sam Davis Omekaramissing ``
Done
// * kBoth: Both first and last intersections are edges.
// * kStart: Only the first intersection is an edge.
// * kEnd: Only the last intersection is an edge.
Can we not just use these for all cases rather than diverging?
No we can't use this for all cases because I for all main gaps the first regardless of edge state the first and last intersections are always edge intersections.
If your suggestion is to mark all main gaps kBoth(because technically they are) then I think we can use this for all cases. But rn, the EdgeGap state is only for cross gaps.
Should this CHECK(HasCrossGapsBefore());
Same with other relevant methods
Done
Might be worth an high level overall comment for this class (even if just with a link to the spec or something)
Done
nit: Would it make sense for this to be GapDecorationPainter instead to avoid confusion with just painting a normal gap (which I guess isn't really painted at all, but just an offset)
Done
BoxSide BoxSideFromGridDirection(const ComputedStyle& style,
This may make sense in one of the grid utils files instead
Hmm, grid or maybe CSS Gap Decorations Utils?
wtf_size_t end = start;
Why do we set this to start instead of directly to `last_intersection_index`?
Also I don't see end or last_intersection_index ever changing. Do we need both of these vars, or am I just overlooking something?
I think the additional context here is that the code line below won't live for long (i.e. after the next CL). It was just a placeholder to allow us paint from the first intersection to the last one.
In the child CL we call the Adjust method that properly assigns start and end.
if (start >= end) {
Is this ever going to happen because of the `while (start < last_intersection_index) {`?
Similar to the above comment, I think the Adjust method in the next CL could see us land in a scenario where start>= end. Didn't work on this bit of logic recently but I vaguely remember this being an edge case in the way we adjust the start/end pair.
const bool is_column_gap = (track_direction == kForColumns);
I'd move this up higher so we don't need to recompute it throughout the loop (and there are some places we can use this higher up, as well)
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
LayoutUnit GetGapCenterOffset(GridTrackSizingDirection direction,
nit: might be worth a brief comment for this one, too
Ditto, I created the
Forgot to finish this comment lol. Added comment.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Code-Review | +1 |
if (num_lines_ > 1 && !content_main_end_.has_value()) {
Sam Davis OmekaraWill this not be guaranteed if `is_first_item` is true? (i.e. could this be a CHECK instead?)
Was going to be on board but I think this code path hits for every first item in every line so the extra condition ensures we only set it once.
cc: @javi...@microsoft.com is this assumption right?
Ah ok gotcha, if that's the case, then makes sense
// this index as part of its calculation. Making this mutable allows us to
// maintain necessary state without breaking const-correctness for the overall
// GapGeometry object.
mutable wtf_size_t main_gap_running_index_ = kNotFound;
Sam Davis OmekaraWould it make sense for paint to hold this instead and pass it in as an input/output param wherever needed?
I think that might work and make sense, but might need some restructuring. Are you okay landing as is while I explore that in a follow up change or you think it's worth pursuing now.
Yeah a TODO works for me
LayoutUnit GapGeometry::GetGapCenterOffset(GridTrackSizingDirection direction,
If it is the start, it won't be center, right? Should this be GetGapIntersectionOffset()? Not a strong preference though either way
Yeahh that method and naming is so confusing. I'm creating a separate CL[1] that removes start from these offset's naming, I think this can be renamed to GetGapOffset with that landing.
[1]: https://chromium-review.googlesource.com/c/chromium/src/+/6915478
Acknowledged
Vector<LayoutUnit> GapGeometry::GenerateIntersectionListForMain(
Instead of building up a vector, would it make sense to have an iterator instead?
I guess maybe not because of the complexity of flexbox?
I had explored this earlier a bit, but I think the iterator route imo was becoming a tad too complicated for something that could easily be represented as a list. More than happy to add as a TODO and explore that further later.
Yeah I think given the code for flexbox, this seems fine for now
// * kBoth: Both first and last intersections are edges.
// * kStart: Only the first intersection is an edge.
// * kEnd: Only the last intersection is an edge.
Sam Davis OmekaraCan we not just use these for all cases rather than diverging?
No we can't use this for all cases because I for all main gaps the first regardless of edge state the first and last intersections are always edge intersections.
If your suggestion is to mark all main gaps kBoth(because technically they are) then I think we can use this for all cases. But rn, the EdgeGap state is only for cross gaps.
Yeah my thinking was to mark the EdgeGap state for all cases, that way we only have one way of determining that state (I think that may make this code a bit simpler here).
Can be done in a follow up though
GapDataList<StyleColor> rule_colors = track_direction == kForColumns
These spots also could make use of is_column_gap if we move that up a bit higher
rule_color, style, /*is_column_rule=*/is_column_gap);
nit: this is no longer needed
wtf_size_t end = start;
Sam Davis OmekaraWhy do we set this to start instead of directly to `last_intersection_index`?
Also I don't see end or last_intersection_index ever changing. Do we need both of these vars, or am I just overlooking something?
I think the additional context here is that the code line below won't live for long (i.e. after the next CL). It was just a placeholder to allow us paint from the first intersection to the last one.
In the child CL we call the Adjust method that properly assigns start and end.
Acknowledged
if (start >= end) {
Sam Davis OmekaraIs this ever going to happen because of the `while (start < last_intersection_index) {`?
Similar to the above comment, I think the Adjust method in the next CL could see us land in a scenario where start>= end. Didn't work on this bit of logic recently but I vaguely remember this being an edge case in the way we adjust the start/end pair.
if (num_lines_ > 1 && !content_main_end_.has_value()) {
Sam Davis OmekaraWill this not be guaranteed if `is_first_item` is true? (i.e. could this be a CHECK instead?)
Alison MaherWas going to be on board but I think this code path hits for every first item in every line so the extra condition ensures we only set it once.
cc: @javi...@microsoft.com is this assumption right?
Ah ok gotcha, if that's the case, then makes sense
Done
// * kBoth: Both first and last intersections are edges.
// * kStart: Only the first intersection is an edge.
// * kEnd: Only the last intersection is an edge.
Sam Davis OmekaraCan we not just use these for all cases rather than diverging?
Alison MaherNo we can't use this for all cases because I for all main gaps the first regardless of edge state the first and last intersections are always edge intersections.
If your suggestion is to mark all main gaps kBoth(because technically they are) then I think we can use this for all cases. But rn, the EdgeGap state is only for cross gaps.
Yeah my thinking was to mark the EdgeGap state for all cases, that way we only have one way of determining that state (I think that may make this code a bit simpler here).
Can be done in a follow up though
Added a TODO, will work on the follow up in coming days.
GapDataList<StyleColor> rule_colors = track_direction == kForColumns
These spots also could make use of is_column_gap if we move that up a bit higher
Done
nit: this is no longer needed
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Exportable changes to web-platform-tests were detected in this CL and a pull request in the upstream repo has been made: https://github.com/web-platform-tests/wpt/pull/54697.
When this CL lands, the bot will automatically merge the PR on GitHub if the required GitHub checks pass; otherwise, ecosystem-infra@ team will triage the failures and may contact you.
WPT Export docs:
https://chromium.googlesource.com/chromium/src/+/main/docs/testing/web_platform_tests.md#Automatic-export-process
46 is the latest approved patch-set.
The change was submitted with unreviewed changes in the following files:
```
The name of the file: third_party/blink/renderer/core/paint/gap_decorations_painter.cc
Insertions: 11, Deletions: 15.
The diff is too large to show. Please review the diff.
```
```
The name of the file: third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
Insertions: 1, Deletions: 1.
The diff is too large to show. Please review the diff.
```
```
The name of the file: third_party/blink/renderer/core/layout/gap/gap_geometry.cc
Insertions: 18, Deletions: 13.
The diff is too large to show. Please review the diff.
```
[Gap Decorations]: Major paint logic for Optimized GapGeometry
This CL implements the core paint logic for the optimized approach to
gap geometry. The main changes are in the "population" methods in
`gap_geometry.h` and the addition of the new `GapPainter` class in
`gap_painter.h`.
Gap decorations are painted relative to intersections within a gap, as a
result this change generates these intersection points at paint time
rather than storing precomputed intersections from layout. The general
principle for generating intersection points is:
content-start → intersections between main × cross gaps → content-end
However, the way we compute the middle intersections depends on the
container type. For flex cross gaps, there are only two intersections
(start and end), so the pattern doesn’t fully apply. This distinction
requires calculating the end offset for flex cross gaps dynamically.
The code is exercised through existing web tests in the
virtual/css-gap-decorations-optimized/external/wpt/css/css-gaps/ folder
as all changes are behind the CSSGapDecorationOptimized feature flag.
Note: This CL does not yet handle rule-break behavior as a result tests
that are exercising that logic are marked as failing in this CL.
Currently, decorations are painted from the start to the end of a gap
without interruption. Rule-break handling will be addressed in an
upcoming change.
Other changes made in this CL are:
* For flex:
- Previously, ContentStart and ContentEnd offsets were not set when a flex line contained only a single item. This has been corrected.
- The offset for the main gap no longer needs to be adjusted based on flex direction. It now consistently represents the position where the flex line ends plus the gap. (Relevant test updated accordingly.)
* For `MainGap` and `CrossGapRange`:
- Introduced getters and setters for `CrossGapRange`, improving ease of use during paint time.
* For grid tests:
- Updated test references to use standard div elements instead of relying on column rules from multi-column layout.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
The WPT PR for this CL has been merged upstream! https://github.com/web-platform-tests/wpt/pull/54697
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |