mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Expose minContentWidth/maxContentWidth and a callback for computing them.
This exposes the last hooks needed to implement flexbox layout. For now, I didn't worry too much about the exact API we're exposing since this will all change with the upcoming redesign (e.g. https://codereview.chromium.org/1093633002). minContentWidth == the width if the element were to wrap at every wrapping point (not including border/padding) maxContentWidth == the width if the element were to only wrap at hard wrapping points (e.g. \n inside a whitespace: pre). R=eseidel@chromium.org Review URL: https://codereview.chromium.org/1101793003
This commit is contained in:
parent
ec37fed8f9
commit
fc1dff4288
@ -945,6 +945,32 @@ void Element::setHeight(double height)
|
||||
return box->setHeight(height);
|
||||
}
|
||||
|
||||
double Element::minContentWidth() const
|
||||
{
|
||||
if (RenderBox* box = renderBox())
|
||||
return box->minPreferredLogicalWidth();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Element::setMinContentWidth(double width)
|
||||
{
|
||||
if (RenderBox* box = renderBox())
|
||||
return box->setMinPreferredLogicalWidth(width);
|
||||
}
|
||||
|
||||
double Element::maxContentWidth() const
|
||||
{
|
||||
if (RenderBox* box = renderBox())
|
||||
return box->maxPreferredLogicalWidth();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Element::setMaxContentWidth(double width)
|
||||
{
|
||||
if (RenderBox* box = renderBox())
|
||||
return box->setMaxPreferredLogicalWidth(width);
|
||||
}
|
||||
|
||||
void Element::setNeedsLayout()
|
||||
{
|
||||
if (RenderBox* box = renderBox())
|
||||
@ -962,19 +988,26 @@ LayoutCallback* Element::layoutManager() const
|
||||
return m_layoutManager.get();
|
||||
}
|
||||
|
||||
void Element::setLayoutManager(PassOwnPtr<LayoutCallback> callback)
|
||||
LayoutCallback* Element::intrinsicWidthsComputer() const
|
||||
{
|
||||
return m_intrinsicWidthsComputer.get();
|
||||
}
|
||||
|
||||
void Element::setLayoutManager(PassOwnPtr<LayoutCallback> layoutManager,
|
||||
PassOwnPtr<LayoutCallback> intrinsicWidthsComputer)
|
||||
{
|
||||
bool isAlreadyCustomLayout = renderer() && renderer()->isRenderCustomLayout();
|
||||
bool requiresCustomLayout = callback;
|
||||
bool requiresCustomLayout = layoutManager;
|
||||
if (requiresCustomLayout != isAlreadyCustomLayout) {
|
||||
// We don't go through the normal reattach codepaths because
|
||||
// those are all tied to changes to the RenderStyle.
|
||||
markAncestorsWithChildNeedsStyleRecalc();
|
||||
detach();
|
||||
} else if (callback.get() != m_layoutManager) {
|
||||
} else if (layoutManager.get() != m_layoutManager) {
|
||||
setNeedsLayout();
|
||||
}
|
||||
m_layoutManager = callback;
|
||||
m_layoutManager = layoutManager;
|
||||
m_intrinsicWidthsComputer = intrinsicWidthsComputer;
|
||||
}
|
||||
|
||||
void Element::childrenChanged(const ChildrenChange& change)
|
||||
|
||||
@ -215,11 +215,19 @@ public:
|
||||
double height() const;
|
||||
void setHeight(double);
|
||||
|
||||
double minContentWidth() const;
|
||||
void setMinContentWidth(double);
|
||||
|
||||
double maxContentWidth() const;
|
||||
void setMaxContentWidth(double);
|
||||
|
||||
void setNeedsLayout();
|
||||
void layout();
|
||||
|
||||
LayoutCallback* intrinsicWidthsComputer() const;
|
||||
LayoutCallback* layoutManager() const;
|
||||
void setLayoutManager(PassOwnPtr<LayoutCallback>);
|
||||
void setLayoutManager(PassOwnPtr<LayoutCallback> layoutManager,
|
||||
PassOwnPtr<LayoutCallback> intrinsicWidthsComputer);
|
||||
|
||||
RenderStyle* computedStyle();
|
||||
|
||||
@ -370,6 +378,7 @@ private:
|
||||
|
||||
RefPtr<ElementData> m_elementData;
|
||||
OwnPtr<LayoutCallback> m_layoutManager;
|
||||
OwnPtr<LayoutCallback> m_intrinsicWidthsComputer;
|
||||
};
|
||||
|
||||
DEFINE_NODE_TYPE_CASTS(Element, isElementNode());
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
void setNeedsLayout();
|
||||
void layout();
|
||||
void setLayoutManager(LayoutCallback callback);
|
||||
void setLayoutManager(LayoutCallback layout, LayoutCallback computeIntrinsicWidths);
|
||||
|
||||
// TODO(abarth): Move to Node.
|
||||
readonly attribute CSSStyleDeclaration style;
|
||||
@ -48,4 +48,6 @@
|
||||
attribute double y;
|
||||
attribute double width;
|
||||
attribute double height;
|
||||
attribute double minContentWidth; // Intrinsic width if all wrappable points wrap.
|
||||
attribute double maxContentWidth; // Intrinsic width if no wrappable points wrap.
|
||||
};
|
||||
|
||||
@ -388,6 +388,16 @@ LayoutUnit RenderBox::maxPreferredLogicalWidth() const
|
||||
return m_maxPreferredLogicalWidth;
|
||||
}
|
||||
|
||||
void RenderBox::setMinPreferredLogicalWidth(LayoutUnit width)
|
||||
{
|
||||
m_minPreferredLogicalWidth = width;
|
||||
}
|
||||
|
||||
void RenderBox::setMaxPreferredLogicalWidth(LayoutUnit width)
|
||||
{
|
||||
m_maxPreferredLogicalWidth = width;
|
||||
}
|
||||
|
||||
bool RenderBox::hasOverrideHeight() const
|
||||
{
|
||||
return m_rareData && m_rareData->m_overrideLogicalContentHeight != -1;
|
||||
|
||||
@ -301,6 +301,9 @@ public:
|
||||
virtual LayoutUnit minPreferredLogicalWidth() const override;
|
||||
virtual LayoutUnit maxPreferredLogicalWidth() const override;
|
||||
|
||||
void setMinPreferredLogicalWidth(LayoutUnit);
|
||||
void setMaxPreferredLogicalWidth(LayoutUnit);
|
||||
|
||||
// FIXME: We should rename these back to overrideLogicalHeight/Width and have them store
|
||||
// the border-box height/width like the regular height/width accessors on RenderBox.
|
||||
// Right now, these are different than contentHeight/contentWidth because they still
|
||||
|
||||
@ -19,6 +19,13 @@ RenderCustomLayout::~RenderCustomLayout()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderCustomLayout::computePreferredLogicalWidths()
|
||||
{
|
||||
ASSERT(node()->isElementNode());
|
||||
toElement(node())->intrinsicWidthsComputer()->handleEvent();
|
||||
clearPreferredLogicalWidthsDirty();
|
||||
}
|
||||
|
||||
void RenderCustomLayout::layout()
|
||||
{
|
||||
ASSERT(node()->isElementNode());
|
||||
|
||||
@ -12,7 +12,8 @@ namespace blink {
|
||||
class RenderCustomLayout : public RenderBlock {
|
||||
public:
|
||||
explicit RenderCustomLayout(ContainerNode* node);
|
||||
virtual void layout() override;
|
||||
void computePreferredLogicalWidths() final;
|
||||
void layout() final;
|
||||
const char* renderName() const;
|
||||
bool isRenderCustomLayout() const final { return true; }
|
||||
|
||||
|
||||
@ -11,6 +11,6 @@ void main() {
|
||||
// don't need laying out.
|
||||
// This test passes if it doesn't crash and the render tree
|
||||
// has no RenderTexts.
|
||||
document.querySelector('block').setLayoutManager(() => {});
|
||||
document.querySelector('block').setLayoutManager(() {}, () {});
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
CONSOLE: unittest-suite-wait-for-done
|
||||
CONSOLE: PASS: should have the right sizes after layout
|
||||
CONSOLE: PASS: intrinsic sizes should apply
|
||||
CONSOLE:
|
||||
CONSOLE: All 1 tests passed.
|
||||
CONSOLE: All 2 tests passed.
|
||||
CONSOLE: unittest-suite-success
|
||||
DONE
|
||||
|
||||
@ -7,6 +7,12 @@
|
||||
</parent>
|
||||
</root>
|
||||
|
||||
<intrinsic-container>
|
||||
<intrinsic>
|
||||
<intrinsic-child style="width: 10px; height: 10px; background-color: lightblue;" />
|
||||
</intrinsic>
|
||||
</intrinsic-container>
|
||||
|
||||
<script>
|
||||
import "../resources/third_party/unittest/unittest.dart";
|
||||
import "../resources/unit.dart";
|
||||
@ -17,60 +23,60 @@ import 'dart:sky';
|
||||
void main() {
|
||||
initUnit();
|
||||
|
||||
var first = true;
|
||||
|
||||
var parent = document.querySelector('parent');
|
||||
var firstChild = parent.firstElementChild;
|
||||
var secondChild = parent.lastElementChild;
|
||||
var grandChild = document.querySelector('grandchild');
|
||||
|
||||
parent.setLayoutManager(() {
|
||||
if (first) {
|
||||
parent.width = 200.0;
|
||||
} else {
|
||||
parent.width = 150.0;
|
||||
}
|
||||
|
||||
firstChild.width = 100.0;
|
||||
firstChild.layout();
|
||||
firstChild.x = 100.0;
|
||||
firstChild.y = 50.0;
|
||||
firstChild.height = 50.0;
|
||||
|
||||
// The second element correctly gets it's width from it's container.
|
||||
// TODO(ojan): Change the layout method to take in availableWidth
|
||||
// so code doesn't need to mess with setNeedsLayout dirty bits
|
||||
// in the middle of layout and so the parent and child don't need
|
||||
// to coordinate as much about expectations.
|
||||
secondChild.setNeedsLayout();
|
||||
secondChild.layout();
|
||||
|
||||
parent.height = 100.0;
|
||||
});
|
||||
|
||||
void assertNonChangingValues() {
|
||||
expect(parent.offsetHeight, equals(100));
|
||||
expect(parent.offsetTop, equals(0));
|
||||
expect(parent.offsetLeft, equals(0));
|
||||
|
||||
expect(firstChild.offsetWidth, equals(100));
|
||||
expect(firstChild.offsetHeight, equals(50));
|
||||
expect(firstChild.offsetTop, equals(50));
|
||||
expect(firstChild.offsetLeft, equals(100));
|
||||
|
||||
expect(secondChild.offsetHeight, equals(25));
|
||||
expect(secondChild.offsetTop, equals(0));
|
||||
expect(secondChild.offsetLeft, equals(0));
|
||||
|
||||
expect(grandChild.offsetWidth, equals(25));
|
||||
expect(grandChild.offsetHeight, equals(25));
|
||||
expect(secondChild.offsetTop, equals(0));
|
||||
expect(secondChild.offsetLeft, equals(0));
|
||||
};
|
||||
|
||||
test("should have the right sizes after layout", () {
|
||||
Completer completer = new Completer();
|
||||
|
||||
var first = true;
|
||||
|
||||
var parent = document.querySelector('parent');
|
||||
var firstChild = parent.firstElementChild;
|
||||
var secondChild = parent.lastElementChild;
|
||||
var grandChild = document.querySelector('grandchild');
|
||||
|
||||
parent.setLayoutManager(() {
|
||||
if (first) {
|
||||
parent.width = 200.0;
|
||||
} else {
|
||||
parent.width = 150.0;
|
||||
}
|
||||
|
||||
firstChild.width = 100.0;
|
||||
firstChild.layout();
|
||||
firstChild.x = 100.0;
|
||||
firstChild.y = 50.0;
|
||||
firstChild.height = 50.0;
|
||||
|
||||
// The second element correctly gets it's width from it's container.
|
||||
// TODO(ojan): Change the layout method to take in availableWidth
|
||||
// so code doesn't need to mess with setNeedsLayout dirty bits
|
||||
// in the middle of layout and so the parent and child don't need
|
||||
// to coordinate as much about expectations.
|
||||
secondChild.setNeedsLayout();
|
||||
secondChild.layout();
|
||||
|
||||
parent.height = 100.0;
|
||||
}, () {});
|
||||
|
||||
void assertNonChangingValues() {
|
||||
expect(parent.offsetHeight, equals(100));
|
||||
expect(parent.offsetTop, equals(0));
|
||||
expect(parent.offsetLeft, equals(0));
|
||||
|
||||
expect(firstChild.offsetWidth, equals(100));
|
||||
expect(firstChild.offsetHeight, equals(50));
|
||||
expect(firstChild.offsetTop, equals(50));
|
||||
expect(firstChild.offsetLeft, equals(100));
|
||||
|
||||
expect(secondChild.offsetHeight, equals(25));
|
||||
expect(secondChild.offsetTop, equals(0));
|
||||
expect(secondChild.offsetLeft, equals(0));
|
||||
|
||||
expect(grandChild.offsetWidth, equals(25));
|
||||
expect(grandChild.offsetHeight, equals(25));
|
||||
expect(secondChild.offsetTop, equals(0));
|
||||
expect(secondChild.offsetLeft, equals(0));
|
||||
};
|
||||
|
||||
window.requestAnimationFrame((_) {
|
||||
expect(parent.offsetWidth, equals(200));
|
||||
expect(secondChild.offsetWidth, equals(200));
|
||||
@ -86,13 +92,13 @@ void main() {
|
||||
|
||||
parent.setLayoutManager(() {
|
||||
parent.width = 250.0;
|
||||
});
|
||||
}, () {});
|
||||
|
||||
window.requestAnimationFrame((_) {
|
||||
expect(parent.offsetWidth, equals(250));
|
||||
assertNonChangingValues();
|
||||
|
||||
parent.setLayoutManager(null);
|
||||
parent.setLayoutManager(null, null);
|
||||
|
||||
window.requestAnimationFrame((_) {
|
||||
expect(parent.offsetWidth, equals(300));
|
||||
@ -123,5 +129,30 @@ void main() {
|
||||
|
||||
return completer.future;
|
||||
});
|
||||
|
||||
test("intrinsic sizes should apply", () {
|
||||
Completer completer = new Completer();
|
||||
|
||||
var intrinsic = document.querySelector('intrinsic');
|
||||
var intrinsicChild = document.querySelector('intrinsic-child');
|
||||
|
||||
intrinsic.setLayoutManager(() {
|
||||
intrinsicChild.layout();
|
||||
}, () {
|
||||
intrinsic.minContentWidth = intrinsicChild.minContentWidth + 5;
|
||||
intrinsic.maxContentWidth = intrinsicChild.maxContentWidth + 7;
|
||||
});
|
||||
|
||||
window.requestAnimationFrame((_) {
|
||||
var container = document.querySelector('intrinsic-container');
|
||||
container.style['width'] = '-webkit-min-content';
|
||||
expect(container.offsetWidth, equals(15));
|
||||
container.style['width'] = '-webkit-max-content';
|
||||
expect(container.offsetWidth, equals(17));
|
||||
completer.complete();
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user