blob: 5c04b61a6d4ed1a36ea6077df6b5609a9c494bc6 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'box.dart';
import 'object.dart';
class MultiChildLayoutParentData extends ContainerBoxParentDataMixin<RenderBox> {
/// An object representing the identity of this child.
Object id;
void merge(MultiChildLayoutParentData other) {
if (other.id != null)
id = other.id;
super.merge(other);
}
String toString() => '${super.toString()}; id=$id';
}
/// A delegate that controls the layout of multiple children.
abstract class MultiChildLayoutDelegate {
Map<Object, RenderBox> _idToChild;
Set<RenderBox> _debugChildrenNeedingLayout;
/// Returns the size of this object given the incomming constraints.
/// The size cannot reflect the instrinsic sizes of the children.
/// If this layout has a fixed width or height the returned size
/// can reflect that.
Size getSize(BoxConstraints constraints) => constraints.biggest;
/// True if a non-null LayoutChild was provided for the specified id.
bool isChild(Object childId) => _idToChild[childId] != null;
/// Ask the child to update its layout within the limits specified by
/// the constraints parameter. The child's size is returned.
Size layoutChild(Object childId, BoxConstraints constraints) {
final RenderBox child = _idToChild[childId];
assert(child != null);
assert(() {
'A MultiChildLayoutDelegate cannot layout the same child more than once.';
return _debugChildrenNeedingLayout.remove(child);
});
child.layout(constraints, parentUsesSize: true);
return child.size;
}
/// Specify the child's origin relative to this origin.
void positionChild(Object childId, Point position) {
final RenderBox child = _idToChild[childId];
assert(child != null);
final MultiChildLayoutParentData childParentData = child.parentData;
childParentData.position = position;
}
void _callPerformLayout(Size size, BoxConstraints constraints, RenderBox firstChild) {
final Map<Object, RenderBox> previousIdToChild = _idToChild;
Set<RenderBox> debugPreviousChildrenNeedingLayout;
assert(() {
debugPreviousChildrenNeedingLayout = _debugChildrenNeedingLayout;
_debugChildrenNeedingLayout = new Set<RenderBox>();
return true;
});
try {
_idToChild = new Map<Object, RenderBox>();
RenderBox child = firstChild;
while (child != null) {
final MultiChildLayoutParentData childParentData = child.parentData;
assert(childParentData.id != null);
_idToChild[childParentData.id] = child;
assert(() {
_debugChildrenNeedingLayout.add(child);
return true;
});
child = childParentData.nextSibling;
}
performLayout(size, constraints);
assert(() {
'A MultiChildLayoutDelegate needs to call layoutChild on every child.';
return _debugChildrenNeedingLayout.isEmpty;
});
} finally {
_idToChild = previousIdToChild;
assert(() {
_debugChildrenNeedingLayout = debugPreviousChildrenNeedingLayout;
return true;
});
}
}
/// Layout and position all children given this widget's size and the specified
/// constraints. This method must apply layoutChild() to each child. It should
/// specify the final position of each child with positionChild().
void performLayout(Size size, BoxConstraints constraints);
}
/// Defers the layout of multiple children to a delegate.
///
/// The delegate can determine the layout constraints for each child and can
/// decide where to position each child. The delegate can also determine the
/// size of the parent, but the size of the parent cannot depend on the sizes of
/// the children.
class RenderCustomMultiChildLayoutBox extends RenderBox
with ContainerRenderObjectMixin<RenderBox, MultiChildLayoutParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, MultiChildLayoutParentData> {
RenderCustomMultiChildLayoutBox({
List<RenderBox> children,
MultiChildLayoutDelegate delegate
}) : _delegate = delegate {
assert(delegate != null);
addAll(children);
}
void setupParentData(RenderBox child) {
if (child.parentData is! MultiChildLayoutParentData)
child.parentData = new MultiChildLayoutParentData();
}
/// The delegate that controls the layout of the children.
MultiChildLayoutDelegate get delegate => _delegate;
MultiChildLayoutDelegate _delegate;
void set delegate (MultiChildLayoutDelegate newDelegate) {
assert(newDelegate != null);
if (_delegate == newDelegate)
return;
_delegate = newDelegate;
markNeedsLayout();
}
Size _getSize(BoxConstraints constraints) {
return constraints.constrain(_delegate.getSize(constraints));
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
return _getSize(constraints).width;
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
return _getSize(constraints).width;
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
return _getSize(constraints).height;
}
double getMaxIntrinsicHeight(BoxConstraints constraints) {
return _getSize(constraints).height;
}
bool get sizedByParent => true;
void performResize() {
size = _getSize(constraints);
}
void performLayout() {
delegate._callPerformLayout(size, constraints, firstChild);
}
void paint(PaintingContext context, Offset offset) {
defaultPaint(context, offset);
}
bool hitTestChildren(HitTestResult result, { Point position }) {
return defaultHitTestChildren(result, position: position);
}
}