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

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 c92f822810c79951746fe29761addbde9debeeab..a0e58ff05416eb4aa6e4e44013f5ce56bf716b1f 100644
--- a/sky/sdk/lib/rendering/box.dart
+++ b/sky/sdk/lib/rendering/box.dart
@@ -291,28 +291,63 @@ abstract class RenderBox extends RenderObject {
return constraints.constrainHeight(0.0);
}
- // getDistanceToBaseline() should return the distance from the
+ Map<TextBaseline, double> _cachedBaselines;
+ bool _ancestorUsesBaseline = false;
+ static bool _debugDoingBaseline = false;
+ static bool _debugSetDoingBaseline(bool value) {
+ _debugDoingBaseline = value;
+ return true;
+ }
+ // 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
+ // that parent's performLayout().
double getDistanceToBaseline(TextBaseline baseline) {
assert(!needsLayout);
+ assert(!_debugDoingBaseline);
+ final parent = this.parent; // TODO(ianh): Remove this once the analyzer is cleverer
+ assert(parent is RenderObject);
+ assert(parent == RenderObject.debugActiveLayout);
+ assert(parent.debugDoingThisLayout);
+ assert(_debugSetDoingBaseline(true));
double result = getDistanceToActualBaseline(baseline);
+ assert(_debugSetDoingBaseline(false));
+ assert(parent == this.parent); // TODO(ianh): Remove this once the analyzer is cleverer
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.
+ // getDistanceToActualBaseline() must only be called from
+ // getDistanceToBaseline() and computeDistanceToActualBaseline(). Do
+ // not call it directly from outside those two methods. It just
+ // calls computeDistanceToActualBaseline() and caches the result.
double getDistanceToActualBaseline(TextBaseline baseline) {
- assert(!needsLayout);
+ assert(_debugDoingBaseline);
+ _ancestorUsesBaseline = true;
+ if (_cachedBaselines == null)
+ _cachedBaselines = new Map<TextBaseline, double>();
+ _cachedBaselines.putIfAbsent(baseline, () => computeDistanceToActualBaseline(baseline));
+ 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. This method (computeDistanceToActualBaseline())
+ // 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) {
+ assert(_debugDoingBaseline);
return null;
}
@@ -326,6 +361,26 @@ abstract class RenderBox extends RenderObject {
print("${this.runtimeType} does not meet its constraints. Constraints: $constraints, size: $_size");
return result;
}
+
+ void markNeedsLayout() {
+ if (_cachedBaselines != null && _cachedBaselines.isNotEmpty) {
+ // if we have cached data, then someone must have used our data
+ assert(_ancestorUsesBaseline);
+ 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
+ // Now that they're dirty, we can forget that they used the
+ // baseline. If they use it again, then we'll set the bit
+ // again, and if we get dirty again, we'll notify them again.
+ _ancestorUsesBaseline = false;
+ _cachedBaselines.clear();
+ } else {
+ // if we've never cached any data, then nobody can have used it
+ assert(!_ancestorUsesBaseline);
+ }
+ super.markNeedsLayout();
+ }
void performResize() {
// default behaviour for subclasses that have sizedByParent = true
size = constraints.constrain(Size.zero);
@@ -410,10 +465,10 @@ class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox
return super.getMaxIntrinsicHeight(constraints);
}
- double getDistanceToActualBaseline(TextBaseline baseline) {
+ double computeDistanceToActualBaseline(TextBaseline baseline) {
if (child != null)
return child.getDistanceToActualBaseline(baseline);
- return super.getDistanceToActualBaseline(baseline);
+ return super.computeDistanceToActualBaseline(baseline);
}
void performLayout() {
@@ -494,6 +549,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 +589,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 +623,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,12 +788,31 @@ 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 getDistanceToActualBaseline(TextBaseline baseline) {
+ 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 computeDistanceToActualBaseline(TextBaseline baseline) {
double result;
if (child != null) {
assert(!needsLayout);
@@ -733,11 +821,16 @@ abstract class RenderShiftedBox extends RenderBox with RenderObjectWithChildMixi
if (result != null)
result += child.parentData.position.y;
} else {
- result = super.getDistanceToActualBaseline(baseline);
+ result = super.computeDistanceToActualBaseline(baseline);
}
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 +913,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 +950,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 +1003,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,7 +1378,7 @@ 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) {
assert(!needsLayout);
RenderBox child = firstChild;
while (child != null) {
@@ -1268,7 +1391,7 @@ abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare
return null;
}
- double defaultGetDistanceToHighestActualBaseline(TextBaseline baseline) {
+ double defaultComputeDistanceToHighestActualBaseline(TextBaseline baseline) {
assert(!needsLayout);
double result;
RenderBox child = firstChild;
« 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