Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1145)

Unified Diff: sky/sdk/lib/rendering/box.dart

Issue 1219113003: Make popup menus line up to their baseline per the Material spec. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sky/sdk/lib/rendering/block.dart ('k') | sky/sdk/lib/rendering/flex.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sky/sdk/lib/rendering/box.dart
diff --git a/sky/sdk/lib/rendering/box.dart b/sky/sdk/lib/rendering/box.dart
index 515f58c9f43be093f415609a5759963c1bfda79a..08189169a0fe197401633cbeb9014e19c14012fb 100644
--- a/sky/sdk/lib/rendering/box.dart
+++ b/sky/sdk/lib/rendering/box.dart
@@ -291,28 +291,62 @@ abstract class RenderBox extends RenderObject {
return constraints.constrainHeight(0.0);
}
- // getDistanceToBaseline() should return the distance from the
+
+ Map<TextBaseline, double> _cachedBaselines;
+ RenderObject _baselineSubtreeRoot;
+ // getDistanceToBaseline() returns the distance from the
// y-coordinate of the position of the box to the y-coordinate of
// the first given baseline in the box's contents. This is used by
// certain layout models to align adjacent boxes on a common
// baseline, regardless of padding, font size differences, etc. If
- // there is no baseline, then it should return the distance from the
+ // there is no baseline, then it returns the distance from the
// y-coordinate of the position of the box to the y-coordinate of
- // the bottom of the box, i.e., the height of the box.
- // Only call this after layout has been performed.
+ // the bottom of the box, i.e., the height of the box. Only call
+ // this after layout has been performed. You are only allowed to
+ // call this from the parent of this node, and only during
+ // performLayout().
double getDistanceToBaseline(TextBaseline baseline) {
- assert(!needsLayout);
- double result = getDistanceToActualBaseline(baseline);
+ RenderObject client = parent;
+ assert(client == RenderObject.debugActiveLayout);
+ assert(client.debugDoingThisLayout);
+ if (_baselineSubtreeRoot != null) {
+ client = _baselineSubtreeRoot;
+ assert(client is! RenderBox || (client as RenderBox)._baselineSubtreeRoot == null); // TODO(ianh): Remove cast once the analyzer is cleverer
+ } else {
+ if (client is RenderBox && client._baselineSubtreeRoot != null)
+ client = (client as RenderBox)._baselineSubtreeRoot; // TODO(ianh): Remove cast once the analyzer is cleverer
+ }
+ double result = getDistanceToActualBaseline(baseline, client);
if (result == null)
return size.height;
return result;
}
- // getDistanceToActualBaseline() should return the distance from the
- // y-coordinate of the position of the box to the y-coordinate of
- // the first given baseline in the box's contents, if any, or null
- // otherwise.
- double getDistanceToActualBaseline(TextBaseline baseline) {
+ // getDistanceToActualBaseline() must only be called from
+ // getDistanceToBaseline() and computeDistanceToActualBaseline().
+ // Do not call it directly from outside those two methods.
+ double getDistanceToActualBaseline(TextBaseline baseline, RenderObject client) {
assert(!needsLayout);
+ assert(client != this);
+ _baselineSubtreeRoot = client;
+ if (_cachedBaselines == null)
+ _cachedBaselines = new Map<TextBaseline, double>();
+ if (!_cachedBaselines.containsKey(baseline))
+ _cachedBaselines[baseline] = computeDistanceToActualBaseline(baseline, client);
abarth-chromium 2015/07/01 17:29:53 _cachedBaselines.putIfAbsent(...)
+ return _cachedBaselines[baseline];
+ }
+ // computeDistanceToActualBaseline() should return the distance from
+ // the y-coordinate of the position of the box to the y-coordinate
+ // of the first given baseline in the box's contents, if any, or
+ // null otherwise. This is the method that you should override in
+ // subclasses. If you defer to a child, you should call the child's
+ // getDistanceToActualBaseline(), passing it the same client. This
+ // method should not be called directly. Use getDistanceToBaseline()
+ // if you need to know the baseline of a child from performLayout().
+ // If you need the baseline during paint, cache it during
+ // performLayout(). Use getDistanceToActualBaseline() if you are
+ // implementing computeDistanceToActualBaseline() and need to defer
+ // to a child.
+ double computeDistanceToActualBaseline(TextBaseline baseline, RenderObject client) {
return null;
}
@@ -326,6 +360,52 @@ abstract class RenderBox extends RenderObject {
print("${this.runtimeType} does not meet its constraints. Constraints: $constraints, size: $_size");
return result;
}
+
+ void cleanParentDependencies() {
+ _baselineSubtreeRoot = null;
+ if (_cachedBaselines != null)
+ _cachedBaselines.clear();
+ super.cleanParentDependencies();
+ }
+ bool debugAncestorsAlreadyClearedBaselineCache() {
+ if (_baselineSubtreeRoot == null)
+ return true; // we haven't yet been asked for a baseline even once, so there's nothing for us to do
+ RenderBox node = this;
+ while (node != _baselineSubtreeRoot) {
+ assert(node._baselineSubtreeRoot == _baselineSubtreeRoot);
+ if (node._cachedBaselines != null && node._cachedBaselines.isNotEmpty)
+ return false;
+ assert(node.parent != null);
+ if (node is! RenderBox) {
+ assert(node is RenderObject);
+ break;
+ }
+ node = node.parent as RenderBox;
+ }
+ if (node is RenderBox) {
+ assert(node._baselineSubtreeRoot == null);
+ if (node._cachedBaselines != null && node._cachedBaselines.isNotEmpty)
+ return false;
+ }
+ return true;
+ }
+ void markNeedsLayout() {
+ assert(!RenderObject.debugDoingLayout);
+ assert(!RenderObject.debugDoingPaint);
+ if (_cachedBaselines != null && _cachedBaselines.isNotEmpty) {
+ _cachedBaselines.clear();
+ if (_baselineSubtreeRoot != null) {
+ assert(_baselineSubtreeRoot != this);
+ final parent = this.parent; // TODO(ianh): Remove this once the analyzer is cleverer
+ assert(parent is RenderObject);
+ parent.markNeedsLayout();
+ assert(parent == this.parent); // TODO(ianh): Remove this once the analyzer is cleverer
+ }
+ } else {
+ assert(debugAncestorsAlreadyClearedBaselineCache());
+ }
+ super.markNeedsLayout();
+ }
void performResize() {
// default behaviour for subclasses that have sizedByParent = true
size = constraints.constrain(Size.zero);
@@ -410,10 +490,10 @@ class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox
return super.getMaxIntrinsicHeight(constraints);
}
- double getDistanceToActualBaseline(TextBaseline baseline) {
+ double computeDistanceToActualBaseline(TextBaseline baseline, RenderObject client) {
if (child != null)
- return child.getDistanceToActualBaseline(baseline);
- return super.getDistanceToActualBaseline(baseline);
+ return child.getDistanceToActualBaseline(baseline, client);
+ return super.computeDistanceToActualBaseline(baseline, client);
}
void performLayout() {
@@ -494,6 +574,15 @@ class RenderConstrainedBox extends RenderProxyBox {
}
class RenderShrinkWrapWidth extends RenderProxyBox {
+
+ // This class will attempt to size its child to the child's maximum
+ // intrinsic width, snapped to a multiple of the stepWidth, if one
+ // is provided, and given the provided constraints; and will then
+ // adopt the child's resulting dimensions.
+
+ // Note: laying out this class is relatively expensive. Avoid using
+ // it where possible.
+
RenderShrinkWrapWidth({
double stepWidth,
double stepHeight,
@@ -525,16 +614,15 @@ class RenderShrinkWrapWidth extends RenderProxyBox {
}
BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
+ if (constraints.hasTightWidth)
+ return constraints;
double width = child.getMaxIntrinsicWidth(constraints);
assert(width == constraints.constrainWidth(width));
- return constraints.applyWidth(width);
+ return constraints.applyWidth(applyStep(width, _stepWidth));
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
- if (child == null)
- return constraints.constrainWidth(0.0);
- double childResult = child.getMinIntrinsicWidth(constraints);
- return constraints.constrainWidth(applyStep(childResult, _stepWidth));
+ return getMaxIntrinsicWidth(constraints);
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
@@ -560,12 +648,18 @@ class RenderShrinkWrapWidth extends RenderProxyBox {
void performLayout() {
if (child != null) {
- child.layout(_getInnerConstraints(constraints), parentUsesSize: true);
- size = new Size(applyStep(child.size.width, _stepWidth), applyStep(child.size.height, _stepHeight));
+ BoxConstraints childConstraints = _getInnerConstraints(constraints);
+ if (_stepHeight != null)
+ childConstraints.applyHeight(getMaxIntrinsicHeight(childConstraints));
+ child.layout(childConstraints, parentUsesSize: true);
+ size = child.size;
} else {
performResize();
}
}
+
+ String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}stepWidth: ${stepWidth}\n${prefix}stepHeight: ${stepHeight}\n';
+
}
class RenderOpacity extends RenderProxyBox {
@@ -719,25 +813,49 @@ abstract class RenderShiftedBox extends RenderBox with RenderObjectWithChildMixi
this.child = child;
}
- void paint(PaintingCanvas canvas, Offset offset) {
+ double getMinIntrinsicWidth(BoxConstraints constraints) {
if (child != null)
- canvas.paintChild(child, child.parentData.position + offset);
+ return child.getMinIntrinsicWidth(constraints);
+ return super.getMinIntrinsicWidth(constraints);
+ }
+
+ double getMaxIntrinsicWidth(BoxConstraints constraints) {
+ if (child != null)
+ return child.getMaxIntrinsicWidth(constraints);
+ return super.getMaxIntrinsicWidth(constraints);
+ }
+
+ double getMinIntrinsicHeight(BoxConstraints constraints) {
+ if (child != null)
+ return child.getMinIntrinsicHeight(constraints);
+ return super.getMinIntrinsicHeight(constraints);
+ }
+
+ double getMaxIntrinsicHeight(BoxConstraints constraints) {
+ if (child != null)
+ return child.getMaxIntrinsicHeight(constraints);
+ return super.getMaxIntrinsicHeight(constraints);
}
- double getDistanceToActualBaseline(TextBaseline baseline) {
+ double computeDistanceToActualBaseline(TextBaseline baseline, RenderObject client) {
double result;
if (child != null) {
assert(!needsLayout);
- result = child.getDistanceToActualBaseline(baseline);
+ result = child.getDistanceToActualBaseline(baseline, client);
assert(child.parentData is BoxParentData);
if (result != null)
result += child.parentData.position.y;
} else {
- result = super.getDistanceToActualBaseline(baseline);
+ result = super.computeDistanceToActualBaseline(baseline, client);
}
return result;
}
+ void paint(PaintingCanvas canvas, Offset offset) {
+ if (child != null)
+ canvas.paintChild(child, child.parentData.position + offset);
+ }
+
void hitTestChildren(HitTestResult result, { Point position }) {
if (child != null) {
assert(child.parentData is BoxParentData);
@@ -820,6 +938,12 @@ class RenderPadding extends RenderShiftedBox {
class RenderPositionedBox extends RenderShiftedBox {
+ // This box aligns a child box within itself. It's only useful for
+ // children that don't always size to fit their parent. For example,
+ // to align a box at the bottom right, you would pass this box a
+ // tight constraint that is bigger than the child's natural size,
+ // with horizontal and vertical set to 1.0.
+
RenderPositionedBox({
RenderBox child,
double horizontal: 0.5,
@@ -851,28 +975,52 @@ class RenderPositionedBox extends RenderShiftedBox {
markNeedsLayout();
}
- double getMinIntrinsicWidth(BoxConstraints constraints) {
- if (child != null)
- return child.getMinIntrinsicWidth(constraints);
- return super.getMinIntrinsicWidth(constraints);
+ void performLayout() {
+ if (child != null) {
+ child.layout(constraints.loosen(), parentUsesSize: true);
+ size = constraints.constrain(child.size);
+ assert(child.parentData is BoxParentData);
+ Offset delta = size - child.size;
+ child.parentData.position = (delta.scale(horizontal, vertical)).toPoint();
+ } else {
+ performResize();
+ }
}
- double getMaxIntrinsicWidth(BoxConstraints constraints) {
- if (child != null)
- return child.getMaxIntrinsicWidth(constraints);
- return super.getMaxIntrinsicWidth(constraints);
+ String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}horizontal: ${horizontal}\n${prefix}vertical: ${vertical}\n';
+}
+
+class RenderBaseline extends RenderShiftedBox {
+
+ RenderBaseline({
+ RenderBox child,
+ double baseline,
+ TextBaseline baselineType
+ }) : _baseline = baseline,
+ _baselineType = baselineType,
+ super(child) {
+ assert(baseline != null);
+ assert(baselineType != null);
}
- double getMinIntrinsicHeight(BoxConstraints constraints) {
- if (child != null)
- return child.getMinIntrinsicHeight(constraints);
- return super.getMinIntrinsicHeight(constraints);
+ double _baseline;
+ double get baseline => _baseline;
+ void set baseline (double value) {
+ assert(value != null);
+ if (_baseline == value)
+ return;
+ _baseline = value;
+ markNeedsLayout();
}
- double getMaxIntrinsicHeight(BoxConstraints constraints) {
- if (child != null)
- return child.getMaxIntrinsicHeight(constraints);
- return super.getMaxIntrinsicHeight(constraints);
+ TextBaseline _baselineType;
+ TextBaseline get baselineType => _baselineType;
+ void set baselineType (TextBaseline value) {
+ assert(value != null);
+ if (_baselineType == value)
+ return;
+ _baselineType = value;
+ markNeedsLayout();
}
void performLayout() {
@@ -880,14 +1028,14 @@ class RenderPositionedBox extends RenderShiftedBox {
child.layout(constraints.loosen(), parentUsesSize: true);
size = constraints.constrain(child.size);
assert(child.parentData is BoxParentData);
- Offset delta = size - child.size;
- child.parentData.position = (delta.scale(horizontal, vertical)).toPoint();
+ double delta = baseline - child.getDistanceToBaseline(baselineType);
+ child.parentData.position = new Point(0.0, delta);
} else {
performResize();
}
}
- String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}horizontal: ${horizontal}\n${prefix}vertical: ${vertical}\n';
+ String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}baseline: ${baseline}\nbaselineType: ${baselineType}';
}
class RenderImage extends RenderBox {
@@ -1255,12 +1403,12 @@ abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare
// This class, by convention, doesn't override any members of the superclass.
// It only provides helper functions that subclasses can call.
- double defaultGetDistanceToFirstActualBaseline(TextBaseline baseline) {
+ double defaultComputeDistanceToFirstActualBaseline(TextBaseline baseline, RenderObject client) {
assert(!needsLayout);
RenderBox child = firstChild;
while (child != null) {
assert(child.parentData is ParentDataType);
- double result = child.getDistanceToActualBaseline(baseline);
+ double result = child.getDistanceToActualBaseline(baseline, client);
if (result != null)
return result + child.parentData.position.y;
child = child.parentData.nextSibling;
@@ -1268,13 +1416,13 @@ abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare
return null;
}
- double defaultGetDistanceToHighestActualBaseline(TextBaseline baseline) {
+ double defaultComputeDistanceToHighestActualBaseline(TextBaseline baseline, RenderObject client) {
assert(!needsLayout);
double result;
RenderBox child = firstChild;
while (child != null) {
assert(child.parentData is ParentDataType);
- double candidate = child.getDistanceToActualBaseline(baseline);
+ double candidate = child.getDistanceToActualBaseline(baseline, client);
if (candidate != null) {
candidate += child.parentData.position.x;
if (result != null)
« no previous file with comments | « sky/sdk/lib/rendering/block.dart ('k') | sky/sdk/lib/rendering/flex.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698