Commit 5aba54d9 authored by Ian Kilpatrick's avatar Ian Kilpatrick Committed by Commit Bot

[css-layout-api] Allow developers to position fragments.

This implements the LayoutFragment.inlineOffset and
LayoutFragment.blockOffset attributes.

The tests added simply re-create the reference by setting these two
attributes in different text directions and writing modes.

Change-Id: I1865403ca12e3b174738ee93320eae5ba16ac292
Bug: 726125
Reviewed-on: https://chromium-review.googlesource.com/971832
Commit-Queue: Ian Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Reviewed-by: default avatarMorten Stenshorne <mstensho@chromium.org>
Cr-Commit-Position: refs/heads/master@{#546018}
parent 585c7af0
<!DOCTYPE html>
<html class=reftest-wait>
<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
<link rel="match" href="position-fragment-ref.html">
<meta name="assert" content="This test checks that child fragments get positioned correctly." />
<style>
.test {
background: red;
width: 100px;
height: 100px;
}
.test {
writing-mode: horizontal-tb;
direction: ltr;
}
.child-1 {
background: rebeccapurple;
width: 10px;
height: 20px;
--inline-offset: 5;
--block-offset: 25;
}
.child-2 {
writing-mode: vertical-rl;
background: rebeccapurple;
width: 15px;
height: 25px;
--inline-offset: 50;
--block-offset: 60;
}
@supports (display: layout(test)) {
.test {
background: green;
display: layout(test);
}
}
</style>
<script src="/common/reftest-wait.js"></script>
<script src="/common/worklet-reftest.js"></script>
<div class="test">
<div class="child-1"></div>
<div class="child-2"></div>
</div>
<script>
importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
</script>
<!DOCTYPE html>
<html class=reftest-wait>
<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
<link rel="match" href="position-fragment-ref.html">
<meta name="assert" content="This test checks that child fragments get positioned correctly." />
<style>
.test {
background: red;
width: 100px;
height: 100px;
}
.test {
writing-mode: horizontal-tb;
direction: rtl;
}
.child-1 {
background: rebeccapurple;
width: 10px;
height: 20px;
--inline-offset: 85;
--block-offset: 25;
}
.child-2 {
writing-mode: vertical-rl;
background: rebeccapurple;
width: 15px;
height: 25px;
--inline-offset: 35;
--block-offset: 60;
}
@supports (display: layout(test)) {
.test {
background: green;
display: layout(test);
}
}
</style>
<script src="/common/reftest-wait.js"></script>
<script src="/common/worklet-reftest.js"></script>
<div class="test">
<div class="child-1"></div>
<div class="child-2"></div>
</div>
<script>
importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
</script>
<!DOCTYPE html>
<style>
.result {
position: relative;
background: green;
width: 100px;
height: 100px;
}
.result-child-1 {
background: rebeccapurple;
width: 10px;
height: 20px;
position: absolute;
top: 25px;
left: 5px;
}
.result-child-2 {
background: rebeccapurple;
width: 15px;
height: 25px;
position: absolute;
top: 60px;
left: 50px;
}
</style>
<div class="result">
<div class="result-child-1"></div>
<div class="result-child-2"></div>
</div>
<!DOCTYPE html>
<html class=reftest-wait>
<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
<link rel="match" href="position-fragment-ref.html">
<meta name="assert" content="This test checks that child fragments get positioned correctly." />
<style>
.test {
background: red;
width: 100px;
height: 100px;
}
.test {
writing-mode: vertical-lr;
direction: ltr;
}
.child-1 {
background: rebeccapurple;
width: 10px;
height: 20px;
--inline-offset: 25;
--block-offset: 5;
}
.child-2 {
writing-mode: vertical-rl;
background: rebeccapurple;
width: 15px;
height: 25px;
--inline-offset: 60;
--block-offset: 50;
}
@supports (display: layout(test)) {
.test {
background: green;
display: layout(test);
}
}
</style>
<script src="/common/reftest-wait.js"></script>
<script src="/common/worklet-reftest.js"></script>
<div class="test">
<div class="child-1"></div>
<div class="child-2"></div>
</div>
<script>
importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
</script>
<!DOCTYPE html>
<html class=reftest-wait>
<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
<link rel="match" href="position-fragment-ref.html">
<meta name="assert" content="This test checks that child fragments get positioned correctly." />
<style>
.test {
background: red;
width: 100px;
height: 100px;
}
.test {
writing-mode: vertical-lr;
direction: rtl;
}
.child-1 {
background: rebeccapurple;
width: 10px;
height: 20px;
--inline-offset: 55;
--block-offset: 5;
}
.child-2 {
writing-mode: vertical-rl;
background: rebeccapurple;
width: 15px;
height: 25px;
--inline-offset: 15;
--block-offset: 50;
}
@supports (display: layout(test)) {
.test {
background: green;
display: layout(test);
}
}
</style>
<script src="/common/reftest-wait.js"></script>
<script src="/common/worklet-reftest.js"></script>
<div class="test">
<div class="child-1"></div>
<div class="child-2"></div>
</div>
<script>
importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
</script>
<!DOCTYPE html>
<html class=reftest-wait>
<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
<link rel="match" href="position-fragment-ref.html">
<meta name="assert" content="This test checks that child fragments get positioned correctly." />
<style>
.test {
background: red;
width: 100px;
height: 100px;
}
.test {
writing-mode: vertical-rl;
direction: ltr;
}
.child-1 {
background: rebeccapurple;
width: 10px;
height: 20px;
--inline-offset: 25;
--block-offset: 85;
}
.child-2 {
writing-mode: vertical-rl;
background: rebeccapurple;
width: 15px;
height: 25px;
--inline-offset: 60;
--block-offset: 35;
}
@supports (display: layout(test)) {
.test {
background: green;
display: layout(test);
}
}
</style>
<script src="/common/reftest-wait.js"></script>
<script src="/common/worklet-reftest.js"></script>
<div class="test">
<div class="child-1"></div>
<div class="child-2"></div>
</div>
<script>
importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
</script>
<!DOCTYPE html>
<html class=reftest-wait>
<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
<link rel="match" href="position-fragment-ref.html">
<meta name="assert" content="This test checks that child fragments get positioned correctly." />
<style>
.test {
background: red;
width: 100px;
height: 100px;
}
.test {
writing-mode: vertical-rl;
direction: rtl;
}
.child-1 {
background: rebeccapurple;
width: 10px;
height: 20px;
--inline-offset: 55;
--block-offset: 85;
}
.child-2 {
writing-mode: vertical-rl;
background: rebeccapurple;
width: 15px;
height: 25px;
--inline-offset: 15;
--block-offset: 35;
}
@supports (display: layout(test)) {
.test {
background: green;
display: layout(test);
}
}
</style>
<script src="/common/reftest-wait.js"></script>
<script src="/common/worklet-reftest.js"></script>
<div class="test">
<div class="child-1"></div>
<div class="child-2"></div>
</div>
<script>
importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
</script>
registerLayout('test', class {
static get childInputProperties() {
return [
'--inline-offset',
'--block-offset',
];
}
*intrinsicSizes() {}
*layout(children, edges, constraints, styleMap) {
const childFragments = yield children.map((child) => {
return child.layoutNextFragment({});
});
for (let i = 0; i < children.length; i++) {
childFragments[i].inlineOffset = parseInt(children[i].styleMap.get('--inline-offset').toString());
childFragments[i].blockOffset = parseInt(children[i].styleMap.get('--block-offset').toString());
}
return {autoBlockSize: 0, childFragments};
}
});
......@@ -37,6 +37,12 @@ class CustomLayoutFragment : public ScriptWrappable {
double inlineSize() const { return inline_size_; }
double blockSize() const { return block_size_; }
double inlineOffset() const { return inline_offset_; }
double blockOffset() const { return block_offset_; }
void setInlineOffset(double inline_offset) { inline_offset_ = inline_offset; }
void setBlockOffset(double block_offset) { block_offset_ = block_offset; }
LayoutBox* GetLayoutBox() const;
bool IsValid() const;
......@@ -59,6 +65,10 @@ class CustomLayoutFragment : public ScriptWrappable {
// The inline and block size on this object should never change.
const double inline_size_;
const double block_size_;
// The offset is relative to our parent, and in the parent's writing mode.
double inline_offset_ = 0;
double block_offset_ = 0;
};
} // namespace blink
......
......@@ -43,12 +43,12 @@ CustomLayoutFragment* CustomLayoutFragmentRequest::PerformLayout() {
if (options_.hasFixedInlineSize()) {
if (is_parallel_writing_mode) {
box->SetOverrideLogicalContentWidth(
(LayoutUnit(options_.fixedInlineSize()) -
(LayoutUnit::FromDoubleRound(options_.fixedInlineSize()) -
box->BorderAndPaddingLogicalWidth())
.ClampNegativeToZero());
} else {
box->SetOverrideLogicalContentHeight(
(LayoutUnit(options_.fixedInlineSize()) -
(LayoutUnit::FromDoubleRound(options_.fixedInlineSize()) -
box->BorderAndPaddingLogicalHeight())
.ClampNegativeToZero());
}
......@@ -57,12 +57,12 @@ CustomLayoutFragment* CustomLayoutFragmentRequest::PerformLayout() {
if (options_.hasFixedBlockSize()) {
if (is_parallel_writing_mode) {
box->SetOverrideLogicalContentHeight(
(LayoutUnit(options_.fixedBlockSize()) -
(LayoutUnit::FromDoubleRound(options_.fixedBlockSize()) -
box->BorderAndPaddingLogicalHeight())
.ClampNegativeToZero());
} else {
box->SetOverrideLogicalContentWidth(
(LayoutUnit(options_.fixedBlockSize()) -
(LayoutUnit::FromDoubleRound(options_.fixedBlockSize()) -
box->BorderAndPaddingLogicalWidth())
.ClampNegativeToZero());
}
......
......@@ -170,6 +170,25 @@ bool LayoutCustom::PerformLayout(bool relayout_children,
// TODO(ikilpatrick): At this stage we may need to perform a re-layout on
// the given child. (The LayoutFragment may have been produced from a
// different LayoutFragmentRequest).
// Update the position of the child.
bool is_parallel_writing_mode = IsParallelWritingMode(
StyleRef().GetWritingMode(), child->StyleRef().GetWritingMode());
LayoutUnit inline_size = is_parallel_writing_mode
? child->LogicalWidth()
: child->LogicalHeight();
LayoutUnit inline_offset =
LayoutUnit::FromDoubleRound(fragment->inlineOffset());
LayoutUnit block_offset =
LayoutUnit::FromDoubleRound(fragment->blockOffset());
LayoutUnit logical_left =
StyleRef().IsLeftToRightDirection()
? inline_offset
: LogicalWidth() - inline_size - inline_offset;
child->SetLocationAndUpdateOverflowControlsIfNeeded(
IsHorizontalWritingMode() ? LayoutPoint(logical_left, block_offset)
: LayoutPoint(block_offset, logical_left));
}
// Currently we only support
......@@ -182,7 +201,8 @@ bool LayoutCustom::PerformLayout(bool relayout_children,
return false;
}
SetLogicalHeight(LayoutUnit(fragment_result_options.autoBlockSize()));
SetLogicalHeight(
LayoutUnit::FromDoubleRound(fragment_result_options.autoBlockSize()));
LayoutUnit old_client_after_edge = ClientLogicalBottom();
UpdateLogicalHeight();
......
......@@ -13,8 +13,8 @@ interface LayoutFragment {
readonly attribute double inlineSize;
readonly attribute double blockSize;
// attribute double inlineOffset;
// attribute double blockOffset;
attribute double inlineOffset;
attribute double blockOffset;
// readonly attribute any data;
};
......@@ -101,6 +101,12 @@ class LayoutUnit {
return v;
}
static LayoutUnit FromDoubleRound(double value) {
LayoutUnit v;
v.value_ = base::saturated_cast<int>(round(value * kFixedPointDenominator));
return v;
}
int ToInt() const { return value_ / kFixedPointDenominator; }
float ToFloat() const {
return static_cast<float>(value_) / kFixedPointDenominator;
......
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