Skip to content

Commit

Permalink
[anchor-position] Support percentage
Browse files Browse the repository at this point in the history
This patch supports the percentage values in the `anchor()`
function as defined in the spec[1].

[1] https://drafts.csswg.org/css-anchor-1/#anchor-pos

Bug: 1382524
Change-Id: I9bf73f0bf98904289992d7276e165dd199be8c5d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4061808
Auto-Submit: Koji Ishii <kojii@chromium.org>
Reviewed-by: Kent Tamura <tkent@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Reviewed-by: Yoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1076691}
  • Loading branch information
kojiishi authored and Chromium LUCI CQ committed Nov 29, 2022
1 parent bbbd78c commit 722b763
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class CORE_EXPORT CalculationExpressionAnchorQueryNode final
DCHECK_EQ(AnchorSide(), AnchorValue::kPercentage);
return side_percentage_;
}
float AnchorSidePercentageOrZero() const {
DCHECK_EQ(type_, AnchorQueryType::kAnchor);
return AnchorSide() == AnchorValue::kPercentage ? side_percentage_ : 0;
}
AnchorSizeValue AnchorSize() const {
DCHECK_EQ(type_, AnchorQueryType::kAnchorSize);
return value_.anchor_size;
Expand Down
34 changes: 28 additions & 6 deletions third_party/blink/renderer/core/layout/ng/ng_anchor_query.cc
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ void NGLogicalAnchorQuery::SetFromPhysical(
absl::optional<LayoutUnit> NGLogicalAnchorQuery::EvaluateAnchor(
const ScopedCSSName* anchor_name,
AnchorValue anchor_value,
float percentage,
LayoutUnit available_size,
const WritingModeConverter& container_converter,
WritingDirectionMode self_writing_direction,
Expand Down Expand Up @@ -278,6 +279,26 @@ absl::optional<LayoutUnit> NGLogicalAnchorQuery::EvaluateAnchor(
// See |AnchorValue::kLeft|.
value = anchor.Bottom() - offset_to_padding_box.top;
break;
case AnchorValue::kPercentage: {
LayoutUnit size;
if (is_y_axis) {
value = anchor.Y() - offset_to_padding_box.top;
size = anchor.Height();
// The percentage is logical, between the `start` and `end` sides.
// Convert to the physical percentage.
// https://drafts.csswg.org/css-anchor-1/#anchor-pos
if (container_converter.GetWritingDirection().IsFlippedY())
percentage = 100 - percentage;
} else {
value = anchor.X() - offset_to_padding_box.left;
size = anchor.Width();
// Convert the logical percentage to physical. See above.
if (container_converter.GetWritingDirection().IsFlippedX())
percentage = 100 - percentage;
}
value += LayoutUnit::FromFloatRound(size * percentage / 100);
break;
}
case AnchorValue::kStart:
case AnchorValue::kEnd:
case AnchorValue::kSelfStart:
Expand All @@ -287,7 +308,6 @@ absl::optional<LayoutUnit> NGLogicalAnchorQuery::EvaluateAnchor(
NOTREACHED();
return absl::nullopt;
case AnchorValue::kCenter:
case AnchorValue::kPercentage:
// TODO(crbug.com/1382524): Not implemented yet.
NOTREACHED();
return absl::nullopt;
Expand Down Expand Up @@ -361,7 +381,8 @@ absl::optional<LayoutUnit> NGAnchorEvaluatorImpl::Evaluate(
switch (anchor_query.Type()) {
case AnchorQueryType::kAnchor:
return EvaluateAnchor(anchor_query.AnchorName(),
anchor_query.AnchorSide());
anchor_query.AnchorSide(),
anchor_query.AnchorSidePercentageOrZero());
case AnchorQueryType::kAnchorSize:
return EvaluateAnchorSize(anchor_query.AnchorName(),
anchor_query.AnchorSize());
Expand All @@ -370,13 +391,14 @@ absl::optional<LayoutUnit> NGAnchorEvaluatorImpl::Evaluate(

absl::optional<LayoutUnit> NGAnchorEvaluatorImpl::EvaluateAnchor(
const ScopedCSSName* anchor_name,
AnchorValue anchor_value) const {
AnchorValue anchor_value,
float percentage) const {
has_anchor_functions_ = true;
if (const NGLogicalAnchorQuery* anchor_query = AnchorQuery()) {
return anchor_query->EvaluateAnchor(
anchor_name, anchor_value, available_size_, container_converter_,
self_writing_direction_, offset_to_padding_box_, is_y_axis_,
is_right_or_bottom_);
anchor_name, anchor_value, percentage, available_size_,
container_converter_, self_writing_direction_, offset_to_padding_box_,
is_y_axis_, is_right_or_bottom_);
}
return absl::nullopt;
}
Expand Down
4 changes: 3 additions & 1 deletion third_party/blink/renderer/core/layout/ng/ng_anchor_query.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class CORE_EXPORT NGLogicalAnchorQuery
absl::optional<LayoutUnit> EvaluateAnchor(
const ScopedCSSName* anchor_name,
AnchorValue anchor_value,
float percentage,
LayoutUnit available_size,
const WritingModeConverter& container_converter,
WritingDirectionMode self_writing_direction,
Expand Down Expand Up @@ -204,7 +205,8 @@ class CORE_EXPORT NGAnchorEvaluatorImpl : public Length::AnchorEvaluator {
const NGLogicalAnchorQuery* AnchorQuery() const;

absl::optional<LayoutUnit> EvaluateAnchor(const ScopedCSSName* anchor_name,
AnchorValue anchor_value) const;
AnchorValue anchor_value,
float percentage) const;
absl::optional<LayoutUnit> EvaluateAnchorSize(
const ScopedCSSName* anchor_name,
AnchorSizeValue anchor_size_value) const;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<!DOCTYPE html>
<title>The `anchor()` function with percentages</title>
<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos">
<link rel="author" href="mailto:kojii@chromium.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<style>
.relpos {
position: relative;
width: 200px;
outline: 1px solid;
}
.vrl-rtl {
writing-mode: vertical-rl;
direction: rtl;
}
.spacer {
background: yellow;
width: 10px;
height: 10px;
}
#anchor {
anchor-name: --a1;
margin: 20px;
width: 100px;
height: 200px;
background: orange;
}
.target {
position: absolute;
}
</style>
<body onload="checkLayout('.target')">
<div class="relpos">
<div class="spacer"></div>
<div id="anchor"></div>

<div class="target" style="left: anchor(--a1 0%)"
data-offset-x="20"></div>
<div class="target" style="left: anchor(--a1 20%)"
data-offset-x="40"></div>
<div class="target" style="left: anchor(--a1 50%)"
data-offset-x="70"></div>
<div class="target" style="left: anchor(--a1 80%)"
data-offset-x="100"></div>
<div class="target" style="left: anchor(--a1 100%)"
data-offset-x="120"></div>

<div class="target" style="right: anchor(--a1 0%)"
data-offset-x="20"></div>
<div class="target" style="right: anchor(--a1 20%)"
data-offset-x="40"></div>
<div class="target" style="right: anchor(--a1 50%)"
data-offset-x="70"></div>
<div class="target" style="right: anchor(--a1 80%)"
data-offset-x="100"></div>
<div class="target" style="right: anchor(--a1 100%)"
data-offset-x="120"></div>

<div class="target" style="top: anchor(--a1 0%)"
data-offset-y="30"></div>
<div class="target" style="top: anchor(--a1 20%)"
data-offset-y="70"></div>
<div class="target" style="top: anchor(--a1 50%)"
data-offset-y="130"></div>
<div class="target" style="top: anchor(--a1 80%)"
data-offset-y="190"></div>
<div class="target" style="top: anchor(--a1 100%)"
data-offset-y="230"></div>

<div class="target" style="bottom: anchor(--a1 0%)"
data-offset-y="30"></div>
<div class="target" style="bottom: anchor(--a1 20%)"
data-offset-y="70"></div>
<div class="target" style="bottom: anchor(--a1 50%)"
data-offset-y="130"></div>
<div class="target" style="bottom: anchor(--a1 80%)"
data-offset-y="190"></div>
<div class="target" style="bottom: anchor(--a1 100%)"
data-offset-y="230"></div>
</div>

<div class="vrl-rtl relpos">
<div class="spacer"></div>
<div id="anchor"></div>

<div class="target" style="left: anchor(--a1 0%)"
data-offset-x="170"></div>
<div class="target" style="left: anchor(--a1 100%)"
data-offset-x="70"></div>

<div class="target" style="right: anchor(--a1 0%)"
data-offset-x="170"></div>
<div class="target" style="right: anchor(--a1 100%)"
data-offset-x="70"></div>

<div class="target" style="top: anchor(--a1 0%)"
data-offset-y="220"></div>
<div class="target" style="top: anchor(--a1 100%)"
data-offset-y="20"></div>

<div class="target" style="bottom: anchor(--a1 0%)"
data-offset-y="220"></div>
<div class="target" style="bottom: anchor(--a1 100%)"
data-offset-y="20"></div>
</div>
</body>

0 comments on commit 722b763

Please sign in to comment.