blob: 42740168ef51d6caf839d7208e80ed9f13e52ca2 [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 'dart:math' as math;
import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/object.dart';
class BlockParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> { }
abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin<RenderBox, BlockParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, BlockParentData> {
// lays out RenderBox children in a vertical stack
// uses the maximum width provided by the parent
RenderBlockBase({
List<RenderBox> children
}) {
addAll(children);
}
void setupParentData(RenderBox child) {
if (child.parentData is! BlockParentData)
child.parentData = new BlockParentData();
}
double _childrenHeight;
double get childrenHeight => _childrenHeight;
void markNeedsLayout() {
_childrenHeight = null;
super.markNeedsLayout();
}
void performLayout() {
assert(constraints is BoxConstraints);
double width = constraints.constrainWidth(constraints.maxWidth);
BoxConstraints innerConstraints = new BoxConstraints.tightFor(width: width);
double y = 0.0;
RenderBox child = firstChild;
while (child != null) {
child.layout(innerConstraints, parentUsesSize: true);
assert(child.parentData is BlockParentData);
child.parentData.position = new Point(0.0, y);
y += child.size.height;
child = child.parentData.nextSibling;
}
_childrenHeight = y;
}
}
class RenderBlock extends RenderBlockBase {
// sizes itself to the height of its child stack
RenderBlock({ List<RenderBox> children }) : super(children: children);
double getMinIntrinsicWidth(BoxConstraints constraints) {
double width = 0.0;
BoxConstraints innerConstraints = constraints.widthConstraints();
RenderBox child = firstChild;
while (child != null) {
width = math.max(width, child.getMinIntrinsicWidth(innerConstraints));
assert(child.parentData is BlockParentData);
child = child.parentData.nextSibling;
}
return width;
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
double width = 0.0;
BoxConstraints innerConstraints = constraints.widthConstraints();
RenderBox child = firstChild;
while (child != null) {
width = math.max(width, child.getMaxIntrinsicWidth(innerConstraints));
assert(child.parentData is BlockParentData);
child = child.parentData.nextSibling;
}
return width;
}
double _getIntrinsicHeight(BoxConstraints constraints) {
double height = 0.0;
double width = constraints.constrainWidth(constraints.maxWidth);
BoxConstraints innerConstraints = new BoxConstraints.tightFor(width: width);
RenderBox child = firstChild;
while (child != null) {
double childHeight = child.getMinIntrinsicHeight(innerConstraints);
assert(childHeight == child.getMaxIntrinsicHeight(innerConstraints));
height += childHeight;
assert(child.parentData is BlockParentData);
child = child.parentData.nextSibling;
}
return height;
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
return _getIntrinsicHeight(constraints);
}
double getMaxIntrinsicHeight(BoxConstraints constraints) {
return _getIntrinsicHeight(constraints);
}
double computeDistanceToActualBaseline(TextBaseline baseline) {
return defaultComputeDistanceToFirstActualBaseline(baseline);
}
void performLayout() {
assert(constraints.maxHeight >= double.INFINITY);
super.performLayout();
size = constraints.constrain(new Size(constraints.maxWidth, childrenHeight));
assert(!size.isInfinite);
}
void paint(PaintingCanvas canvas, Offset offset) {
defaultPaint(canvas, offset);
}
void hitTestChildren(HitTestResult result, { Point position }) {
defaultHitTestChildren(result, position: position);
}
}
class RenderBlockViewport extends RenderBlockBase {
// sizes itself to the given constraints
// at the start of layout, calls callback
RenderBlockViewport({
LayoutCallback callback,
List<RenderBox> children,
double startOffset: 0.0
}) : _callback = callback, _startOffset = startOffset, super(children: children);
bool _inCallback = false;
LayoutCallback _callback;
LayoutCallback get callback => _callback;
void set callback(LayoutCallback value) {
assert(!_inCallback);
if (value == _callback)
return;
_callback = value;
markNeedsLayout();
}
// you can set this from within the callback if necessary
double _startOffset;
double get startOffset => _startOffset;
void set startOffset(double value) {
if (value == _startOffset)
return;
_startOffset = value;
if (!_inCallback)
markNeedsPaint();
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
return constraints.constrainWidth();
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
return constraints.constrainWidth();
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
return constraints.constrainHeight();
}
double getMaxIntrinsicHeight(BoxConstraints constraints) {
return constraints.constrainHeight();
}
// We don't override computeDistanceToActualBaseline(), because we
// want the default behaviour (returning null). Otherwise, as you
// scroll the RenderBlockViewport, it would shift in its parent if
// the parent was baseline-aligned, which makes no sense.
bool get sizedByParent => true;
void performResize() {
size = constraints.biggest;
assert(!size.isInfinite);
}
bool get debugDoesLayoutWithCallback => true;
void performLayout() {
assert(constraints.maxHeight < double.INFINITY);
if (_callback != null) {
try {
_inCallback = true;
invokeLayoutCallback(_callback);
} finally {
_inCallback = false;
}
}
super.performLayout();
}
void paint(PaintingCanvas canvas, Offset offset) {
canvas.save();
canvas.clipRect(offset & size);
defaultPaint(canvas, offset.translate(0.0, startOffset));
canvas.restore();
}
void hitTestChildren(HitTestResult result, { Point position }) {
defaultHitTestChildren(result, position: position + new Offset(0.0, -startOffset));
}
}