Merge pull request #870 from Hixie/RenderBlockViewport
Generalise RenderBlockViewport so that it can be used by a Widget that knows its children's dimensions.
This commit is contained in:
commit
7a4bdc7f2a
@ -13,6 +13,7 @@ class BlockParentData extends BoxParentData with ContainerParentDataMixin<Render
|
|||||||
enum BlockDirection { horizontal, vertical }
|
enum BlockDirection { horizontal, vertical }
|
||||||
|
|
||||||
typedef double _ChildSizingFunction(RenderBox child, BoxConstraints constraints);
|
typedef double _ChildSizingFunction(RenderBox child, BoxConstraints constraints);
|
||||||
|
typedef double _Constrainer(double value);
|
||||||
|
|
||||||
abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin<RenderBox, BlockParentData>,
|
abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin<RenderBox, BlockParentData>,
|
||||||
RenderBoxContainerDefaultsMixin<RenderBox, BlockParentData> {
|
RenderBoxContainerDefaultsMixin<RenderBox, BlockParentData> {
|
||||||
@ -41,10 +42,10 @@ abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get _isVertical => _direction == BlockDirection.vertical;
|
bool get isVertical => _direction == BlockDirection.vertical;
|
||||||
|
|
||||||
BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
|
BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
|
||||||
if (_isVertical)
|
if (isVertical)
|
||||||
return new BoxConstraints.tightFor(width: constraints.constrainWidth(constraints.maxWidth));
|
return new BoxConstraints.tightFor(width: constraints.constrainWidth(constraints.maxWidth));
|
||||||
return new BoxConstraints.tightFor(height: constraints.constrainHeight(constraints.maxHeight));
|
return new BoxConstraints.tightFor(height: constraints.constrainHeight(constraints.maxHeight));
|
||||||
}
|
}
|
||||||
@ -54,7 +55,7 @@ abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin
|
|||||||
if (child == null)
|
if (child == null)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
BoxParentData parentData = child.parentData;
|
BoxParentData parentData = child.parentData;
|
||||||
return _isVertical ?
|
return isVertical ?
|
||||||
parentData.position.y + child.size.height :
|
parentData.position.y + child.size.height :
|
||||||
parentData.position.x + child.size.width;
|
parentData.position.x + child.size.width;
|
||||||
}
|
}
|
||||||
@ -66,11 +67,11 @@ abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin
|
|||||||
while (child != null) {
|
while (child != null) {
|
||||||
child.layout(innerConstraints, parentUsesSize: true);
|
child.layout(innerConstraints, parentUsesSize: true);
|
||||||
assert(child.parentData is BlockParentData);
|
assert(child.parentData is BlockParentData);
|
||||||
child.parentData.position = _isVertical ? new Point(0.0, position) : new Point(position, 0.0);
|
child.parentData.position = isVertical ? new Point(0.0, position) : new Point(position, 0.0);
|
||||||
position += _isVertical ? child.size.height : child.size.width;
|
position += isVertical ? child.size.height : child.size.width;
|
||||||
child = child.parentData.nextSibling;
|
child = child.parentData.nextSibling;
|
||||||
}
|
}
|
||||||
size = _isVertical ?
|
size = isVertical ?
|
||||||
constraints.constrain(new Size(constraints.maxWidth, _mainAxisExtent)) :
|
constraints.constrain(new Size(constraints.maxWidth, _mainAxisExtent)) :
|
||||||
constraints.constrain(new Size(_mainAxisExtent, constraints.maxHeight));
|
constraints.constrain(new Size(_mainAxisExtent, constraints.maxHeight));
|
||||||
assert(!size.isInfinite);
|
assert(!size.isInfinite);
|
||||||
@ -88,7 +89,7 @@ class RenderBlock extends RenderBlockBase {
|
|||||||
|
|
||||||
double _getIntrinsicCrossAxis(BoxConstraints constraints, _ChildSizingFunction childSize) {
|
double _getIntrinsicCrossAxis(BoxConstraints constraints, _ChildSizingFunction childSize) {
|
||||||
double extent = 0.0;
|
double extent = 0.0;
|
||||||
BoxConstraints innerConstraints = _isVertical ? constraints.widthConstraints() : constraints.heightConstraints();
|
BoxConstraints innerConstraints = isVertical ? constraints.widthConstraints() : constraints.heightConstraints();
|
||||||
RenderBox child = firstChild;
|
RenderBox child = firstChild;
|
||||||
while (child != null) {
|
while (child != null) {
|
||||||
extent = math.max(extent, childSize(child, innerConstraints));
|
extent = math.max(extent, childSize(child, innerConstraints));
|
||||||
@ -103,11 +104,11 @@ class RenderBlock extends RenderBlockBase {
|
|||||||
BoxConstraints innerConstraints = _getInnerConstraints(constraints);
|
BoxConstraints innerConstraints = _getInnerConstraints(constraints);
|
||||||
RenderBox child = firstChild;
|
RenderBox child = firstChild;
|
||||||
while (child != null) {
|
while (child != null) {
|
||||||
double childExtent = _isVertical ?
|
double childExtent = isVertical ?
|
||||||
child.getMinIntrinsicHeight(innerConstraints) :
|
child.getMinIntrinsicHeight(innerConstraints) :
|
||||||
child.getMinIntrinsicWidth(innerConstraints);
|
child.getMinIntrinsicWidth(innerConstraints);
|
||||||
assert(() {
|
assert(() {
|
||||||
if (_isVertical)
|
if (isVertical)
|
||||||
return childExtent == child.getMaxIntrinsicHeight(innerConstraints);
|
return childExtent == child.getMaxIntrinsicHeight(innerConstraints);
|
||||||
return childExtent == child.getMaxIntrinsicWidth(innerConstraints);
|
return childExtent == child.getMaxIntrinsicWidth(innerConstraints);
|
||||||
});
|
});
|
||||||
@ -119,7 +120,7 @@ class RenderBlock extends RenderBlockBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double getMinIntrinsicWidth(BoxConstraints constraints) {
|
double getMinIntrinsicWidth(BoxConstraints constraints) {
|
||||||
if (_isVertical) {
|
if (isVertical) {
|
||||||
return _getIntrinsicCrossAxis(constraints,
|
return _getIntrinsicCrossAxis(constraints,
|
||||||
(c, innerConstraints) => c.getMinIntrinsicWidth(innerConstraints));
|
(c, innerConstraints) => c.getMinIntrinsicWidth(innerConstraints));
|
||||||
}
|
}
|
||||||
@ -127,7 +128,7 @@ class RenderBlock extends RenderBlockBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double getMaxIntrinsicWidth(BoxConstraints constraints) {
|
double getMaxIntrinsicWidth(BoxConstraints constraints) {
|
||||||
if (_isVertical) {
|
if (isVertical) {
|
||||||
return _getIntrinsicCrossAxis(constraints,
|
return _getIntrinsicCrossAxis(constraints,
|
||||||
(c, innerConstraints) => c.getMaxIntrinsicWidth(innerConstraints));
|
(c, innerConstraints) => c.getMaxIntrinsicWidth(innerConstraints));
|
||||||
}
|
}
|
||||||
@ -135,14 +136,14 @@ class RenderBlock extends RenderBlockBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double getMinIntrinsicHeight(BoxConstraints constraints) {
|
double getMinIntrinsicHeight(BoxConstraints constraints) {
|
||||||
if (_isVertical)
|
if (isVertical)
|
||||||
return _getIntrinsicMainAxis(constraints);
|
return _getIntrinsicMainAxis(constraints);
|
||||||
return _getIntrinsicCrossAxis(constraints,
|
return _getIntrinsicCrossAxis(constraints,
|
||||||
(c, innerConstraints) => c.getMinIntrinsicWidth(innerConstraints));
|
(c, innerConstraints) => c.getMinIntrinsicWidth(innerConstraints));
|
||||||
}
|
}
|
||||||
|
|
||||||
double getMaxIntrinsicHeight(BoxConstraints constraints) {
|
double getMaxIntrinsicHeight(BoxConstraints constraints) {
|
||||||
if (_isVertical)
|
if (isVertical)
|
||||||
return _getIntrinsicMainAxis(constraints);
|
return _getIntrinsicMainAxis(constraints);
|
||||||
return _getIntrinsicCrossAxis(constraints,
|
return _getIntrinsicCrossAxis(constraints,
|
||||||
(c, innerConstraints) => c.getMaxIntrinsicWidth(innerConstraints));
|
(c, innerConstraints) => c.getMaxIntrinsicWidth(innerConstraints));
|
||||||
@ -153,7 +154,7 @@ class RenderBlock extends RenderBlockBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void performLayout() {
|
void performLayout() {
|
||||||
assert((_isVertical ? constraints.maxHeight >= double.INFINITY : constraints.maxWidth >= double.INFINITY) &&
|
assert((isVertical ? constraints.maxHeight >= double.INFINITY : constraints.maxWidth >= double.INFINITY) &&
|
||||||
'RenderBlock does not clip or resize its children, so it must be placed in a parent that does not constrain ' +
|
'RenderBlock does not clip or resize its children, so it must be placed in a parent that does not constrain ' +
|
||||||
'the block\'s main direction. You probably want to put the RenderBlock inside a RenderViewport.' is String);
|
'the block\'s main direction. You probably want to put the RenderBlock inside a RenderViewport.' is String);
|
||||||
super.performLayout();
|
super.performLayout();
|
||||||
@ -171,14 +172,32 @@ class RenderBlock extends RenderBlockBase {
|
|||||||
|
|
||||||
class RenderBlockViewport extends RenderBlockBase {
|
class RenderBlockViewport extends RenderBlockBase {
|
||||||
|
|
||||||
|
// This class invokes a callbacks for layout and intrinsic
|
||||||
|
// dimensions. The main callback (constructor argument and property
|
||||||
|
// called "callback") is expected to modify the element's child
|
||||||
|
// list. The regular block layout algorithm is then applied to the
|
||||||
|
// children. The intrinsic dimension callbacks are called to
|
||||||
|
// determine intrinsic dimensions; if no value can be returned, they
|
||||||
|
// should not be set or, if set, should return null.
|
||||||
|
|
||||||
RenderBlockViewport({
|
RenderBlockViewport({
|
||||||
LayoutCallback callback,
|
LayoutCallback callback,
|
||||||
|
DimensionCallback totalExtentCallback,
|
||||||
|
DimensionCallback maxCrossAxisDimensionCallback,
|
||||||
|
DimensionCallback minCrossAxisDimensionCallback,
|
||||||
|
double startOffset: 0.0,
|
||||||
List<RenderBox> children,
|
List<RenderBox> children,
|
||||||
double startOffset: 0.0
|
BlockDirection direction: BlockDirection.vertical
|
||||||
}) : _callback = callback, _startOffset = startOffset, super(children: children);
|
}) : _callback = callback,
|
||||||
|
_totalExtentCallback = totalExtentCallback,
|
||||||
|
_maxCrossAxisDimensionCallback = maxCrossAxisDimensionCallback,
|
||||||
|
_minCrossAxisDimensionCallback = minCrossAxisDimensionCallback,
|
||||||
|
_startOffset = startOffset,
|
||||||
|
super(children: children, direction: direction);
|
||||||
|
|
||||||
bool _inCallback = false;
|
bool _inCallback = false;
|
||||||
|
|
||||||
|
// Called during layout. Mutate the child list appropriately.
|
||||||
LayoutCallback _callback;
|
LayoutCallback _callback;
|
||||||
LayoutCallback get callback => _callback;
|
LayoutCallback get callback => _callback;
|
||||||
void set callback(LayoutCallback value) {
|
void set callback(LayoutCallback value) {
|
||||||
@ -189,6 +208,44 @@ class RenderBlockViewport extends RenderBlockBase {
|
|||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the sum of the extent of all the children that could be included by the callback in one go.
|
||||||
|
// The extent is the dimension in the direction given by the 'direction' property.
|
||||||
|
DimensionCallback _totalExtentCallback;
|
||||||
|
DimensionCallback get totalExtentCallback => _totalExtentCallback;
|
||||||
|
void set totalExtentCallback(DimensionCallback value) {
|
||||||
|
assert(!_inCallback);
|
||||||
|
if (value == _totalExtentCallback)
|
||||||
|
return;
|
||||||
|
_totalExtentCallback = value;
|
||||||
|
markNeedsLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the minimum dimension across all the children that could
|
||||||
|
// be included in one go, in the direction orthogonal to that given
|
||||||
|
// by the 'direction' property.
|
||||||
|
DimensionCallback _minCrossAxisDimensionCallback;
|
||||||
|
DimensionCallback get minCrossAxisDimensionCallback => _minCrossAxisDimensionCallback;
|
||||||
|
void set minCrossAxisDimensionCallback(DimensionCallback value) {
|
||||||
|
assert(!_inCallback);
|
||||||
|
if (value == _minCrossAxisDimensionCallback)
|
||||||
|
return;
|
||||||
|
_minCrossAxisDimensionCallback = value;
|
||||||
|
markNeedsLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the maximum dimension across all the children that could
|
||||||
|
// be included in one go, in the direction orthogonal to that given
|
||||||
|
// by the 'direction' property.
|
||||||
|
DimensionCallback _maxCrossAxisDimensionCallback;
|
||||||
|
DimensionCallback get maxCrossAxisDimensionCallback => _maxCrossAxisDimensionCallback;
|
||||||
|
void set maxCrossAxisDimensionCallback(DimensionCallback value) {
|
||||||
|
assert(!_inCallback);
|
||||||
|
if (value == _maxCrossAxisDimensionCallback)
|
||||||
|
return;
|
||||||
|
_maxCrossAxisDimensionCallback = value;
|
||||||
|
markNeedsLayout();
|
||||||
|
}
|
||||||
|
|
||||||
// you can set this from within the callback if necessary
|
// you can set this from within the callback if necessary
|
||||||
double _startOffset;
|
double _startOffset;
|
||||||
double get startOffset => _startOffset;
|
double get startOffset => _startOffset;
|
||||||
@ -200,30 +257,50 @@ class RenderBlockViewport extends RenderBlockBase {
|
|||||||
markNeedsPaint();
|
markNeedsPaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
double _noIntrinsicDimensions() {
|
double _getIntrinsicDimension(BoxConstraints constraints, DimensionCallback intrinsicCallback, _Constrainer constrainer) {
|
||||||
|
assert(!_inCallback);
|
||||||
|
double result;
|
||||||
|
if (intrinsicCallback == null) {
|
||||||
assert(() {
|
assert(() {
|
||||||
'RenderBlockViewport does not support returning intrinsic dimensions. ' +
|
'RenderBlockViewport does not support returning intrinsic dimensions if the relevant callbacks have not been specified.';
|
||||||
'Calculating the intrinsic dimensions would require walking the entire child list, ' +
|
|
||||||
'which defeats the entire point of having a lazily-built list of children.';
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
return 0.0;
|
return constrainer(0.0);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
_inCallback = true;
|
||||||
|
result = intrinsicCallback(constraints);
|
||||||
|
if (result == null)
|
||||||
|
result = constrainer(0.0);
|
||||||
|
assert(constrainer(result) == result);
|
||||||
|
} finally {
|
||||||
|
_inCallback = false;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
double getMinIntrinsicWidth(BoxConstraints constraints) {
|
double getMinIntrinsicWidth(BoxConstraints constraints) {
|
||||||
return _noIntrinsicDimensions();
|
if (isVertical)
|
||||||
|
return _getIntrinsicDimension(constraints, minCrossAxisDimensionCallback, constraints.constrainWidth);
|
||||||
|
return constraints.constrainWidth(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
double getMaxIntrinsicWidth(BoxConstraints constraints) {
|
double getMaxIntrinsicWidth(BoxConstraints constraints) {
|
||||||
return _noIntrinsicDimensions();
|
if (isVertical)
|
||||||
|
return _getIntrinsicDimension(constraints, maxCrossAxisDimensionCallback, constraints.constrainWidth);
|
||||||
|
return _getIntrinsicDimension(constraints, totalExtentCallback, constraints.constrainWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
double getMinIntrinsicHeight(BoxConstraints constraints) {
|
double getMinIntrinsicHeight(BoxConstraints constraints) {
|
||||||
return _noIntrinsicDimensions();
|
if (!isVertical)
|
||||||
|
return _getIntrinsicDimension(constraints, minCrossAxisDimensionCallback, constraints.constrainHeight);
|
||||||
|
return constraints.constrainHeight(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
double getMaxIntrinsicHeight(BoxConstraints constraints) {
|
double getMaxIntrinsicHeight(BoxConstraints constraints) {
|
||||||
return _noIntrinsicDimensions();
|
if (!isVertical)
|
||||||
|
return _getIntrinsicDimension(constraints, maxCrossAxisDimensionCallback, constraints.constrainHeight);
|
||||||
|
return _getIntrinsicDimension(constraints, totalExtentCallback, constraints.constrainHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't override computeDistanceToActualBaseline(), because we
|
// We don't override computeDistanceToActualBaseline(), because we
|
||||||
|
@ -292,6 +292,7 @@ abstract class Constraints {
|
|||||||
|
|
||||||
typedef void RenderObjectVisitor(RenderObject child);
|
typedef void RenderObjectVisitor(RenderObject child);
|
||||||
typedef void LayoutCallback(Constraints constraints);
|
typedef void LayoutCallback(Constraints constraints);
|
||||||
|
typedef double DimensionCallback(Constraints constraints);
|
||||||
|
|
||||||
abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
||||||
|
|
||||||
|
@ -117,13 +117,29 @@ class MixedViewport extends RenderObjectWrapper {
|
|||||||
assert(renderObject == this.renderObject); // TODO(ianh): Remove this once the analyzer is cleverer
|
assert(renderObject == this.renderObject); // TODO(ianh): Remove this once the analyzer is cleverer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double _noIntrinsicDimensions(BoxConstraints constraints) {
|
||||||
|
assert(() {
|
||||||
|
'MixedViewport does not support returning intrinsic dimensions. ' +
|
||||||
|
'Calculating the intrinsic dimensions would require walking the entire child list, ' +
|
||||||
|
'which defeats the entire point of having a lazily-built list of children.';
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
void didMount() {
|
void didMount() {
|
||||||
renderObject.callback = layout;
|
renderObject.callback = layout;
|
||||||
|
renderObject.totalExtentCallback = _noIntrinsicDimensions;
|
||||||
|
renderObject.maxCrossAxisDimensionCallback = _noIntrinsicDimensions;
|
||||||
|
renderObject.minCrossAxisDimensionCallback = _noIntrinsicDimensions;
|
||||||
super.didMount();
|
super.didMount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void didUnmount() {
|
void didUnmount() {
|
||||||
renderObject.callback = null;
|
renderObject.callback = null;
|
||||||
|
renderObject.totalExtentCallback = null;
|
||||||
|
renderObject.maxCrossAxisDimensionCallback = null;
|
||||||
|
renderObject.minCrossAxisDimensionCallback = null;
|
||||||
super.didUnmount();
|
super.didUnmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user