Index: sky/sdk/lib/framework/layout2.dart |
diff --git a/sky/sdk/lib/framework/layout2.dart b/sky/sdk/lib/framework/layout2.dart |
deleted file mode 100644 |
index c644c317484495b9f278c117caf047c206a390dd..0000000000000000000000000000000000000000 |
--- a/sky/sdk/lib/framework/layout2.dart |
+++ /dev/null |
@@ -1,993 +0,0 @@ |
-// 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 'node.dart'; |
-import 'dart:sky' as sky; |
- |
-// ABSTRACT LAYOUT |
- |
-class ParentData { |
- void detach() { |
- detachSiblings(); |
- } |
- void detachSiblings() { } // workaround for lack of inter-class mixins in Dart |
- void merge(ParentData other) { |
- // override this in subclasses to merge in data from other into this |
- assert(other.runtimeType == this.runtimeType); |
- } |
-} |
- |
-const kLayoutDirections = 4; |
- |
-double clamp({double min: 0.0, double value: 0.0, double max: double.INFINITY}) { |
- assert(min != null); |
- assert(value != null); |
- assert(max != null); |
- |
- if (value > max) |
- value = max; |
- if (value < min) |
- value = min; |
- return value; |
-} |
- |
-class RenderNodeDisplayList extends sky.PictureRecorder { |
- RenderNodeDisplayList(double width, double height) : super(width, height); |
- void paintChild(RenderNode child, sky.Point position) { |
- save(); |
- translate(position.x, position.y); |
- child.paint(this); |
- restore(); |
- } |
-} |
- |
-abstract class RenderNode extends AbstractNode { |
- |
- // LAYOUT |
- |
- // parentData is only for use by the RenderNode that actually lays this |
- // node out, and any other nodes who happen to know exactly what |
- // kind of node that is. |
- ParentData parentData; |
- void setParentData(RenderNode child) { |
- // override this to setup .parentData correctly for your class |
- if (child.parentData is! ParentData) |
- child.parentData = new ParentData(); |
- } |
- |
- void adoptChild(RenderNode child) { // only for use by subclasses |
- // call this whenever you decide a node is a child |
- assert(child != null); |
- setParentData(child); |
- super.adoptChild(child); |
- } |
- void dropChild(RenderNode child) { // only for use by subclasses |
- assert(child != null); |
- assert(child.parentData != null); |
- child.parentData.detach(); |
- super.dropChild(child); |
- } |
- |
- static List<RenderNode> _nodesNeedingLayout = new List<RenderNode>(); |
- static bool _debugDoingLayout = false; |
- bool _needsLayout = true; |
- bool get needsLayout => _needsLayout; |
- RenderNode _relayoutSubtreeRoot; |
- dynamic _constraints; |
- dynamic get constraints => _constraints; |
- bool debugAncestorsAlreadyMarkedNeedsLayout() { |
- if (_relayoutSubtreeRoot == null) |
- return true; // we haven't yet done layout even once, so there's nothing for us to do |
- RenderNode node = this; |
- while (node != _relayoutSubtreeRoot) { |
- assert(node._relayoutSubtreeRoot == _relayoutSubtreeRoot); |
- assert(node.parent != null); |
- node = node.parent as RenderNode; |
- if (!node._needsLayout) |
- return false; |
- } |
- assert(node._relayoutSubtreeRoot == node); |
- return true; |
- } |
- void markNeedsLayout() { |
- assert(!_debugDoingLayout); |
- assert(!_debugDoingPaint); |
- if (_needsLayout) { |
- assert(debugAncestorsAlreadyMarkedNeedsLayout()); |
- return; |
- } |
- _needsLayout = true; |
- assert(_relayoutSubtreeRoot != null); |
- if (_relayoutSubtreeRoot != this) { |
- assert(parent is RenderNode); |
- parent.markNeedsLayout(); |
- } else { |
- _nodesNeedingLayout.add(this); |
- } |
- } |
- static void flushLayout() { |
- _debugDoingLayout = true; |
- List<RenderNode> dirtyNodes = _nodesNeedingLayout; |
- _nodesNeedingLayout = new List<RenderNode>(); |
- dirtyNodes..sort((a, b) => a.depth - b.depth)..forEach((node) { |
- if (node._needsLayout && node.attached) |
- node._doLayout(); |
- }); |
- _debugDoingLayout = false; |
- } |
- void _doLayout() { |
- try { |
- assert(_relayoutSubtreeRoot == this); |
- performLayout(); |
- } catch (e, stack) { |
- print('Exception raised during layout of ${this}: ${e}'); |
- print(stack); |
- return; |
- } |
- _needsLayout = false; |
- } |
- void layout(dynamic constraints, { bool parentUsesSize: false }) { |
- RenderNode relayoutSubtreeRoot; |
- if (!parentUsesSize || sizedByParent || parent is! RenderNode) |
- relayoutSubtreeRoot = this; |
- else |
- relayoutSubtreeRoot = parent._relayoutSubtreeRoot; |
- if (!needsLayout && constraints == _constraints && relayoutSubtreeRoot == _relayoutSubtreeRoot) |
- return; |
- _constraints = constraints; |
- _relayoutSubtreeRoot = relayoutSubtreeRoot; |
- if (sizedByParent) |
- performResize(); |
- performLayout(); |
- _needsLayout = false; |
- markNeedsPaint(); |
- } |
- bool get sizedByParent => false; // return true if the constraints are the only input to the sizing algorithm (in particular, child nodes have no impact) |
- void performResize(); // set the local dimensions, using only the constraints (only called if sizedByParent is true) |
- void performLayout(); |
- // Override this to perform relayout without your parent's |
- // involvement. |
- // |
- // This is called during layout. If sizedByParent is true, then |
- // performLayout() should not change your dimensions, only do that |
- // in performResize(). If sizedByParent is false, then set both |
- // your dimensions and do your children's layout here. |
- // |
- // When calling layout() on your children, pass in |
- // "parentUsesSize: true" if your size or layout is dependent on |
- // your child's size. |
- |
- // when the parent has rotated (e.g. when the screen has been turned |
- // 90 degrees), immediately prior to layout() being called for the |
- // new dimensions, rotate() is called with the old and new angles. |
- // The next time paint() is called, the coordinate space will have |
- // been rotated N quarter-turns clockwise, where: |
- // N = newAngle-oldAngle |
- // ...but the rendering is expected to remain the same, pixel for |
- // pixel, on the output device. Then, the layout() method or |
- // equivalent will be invoked. |
- |
- void rotate({ |
- int oldAngle, // 0..3 |
- int newAngle, // 0..3 |
- Duration time |
- }) { } |
- |
- |
- // PAINTING |
- |
- static bool _debugDoingPaint = false; |
- void markNeedsPaint() { |
- assert(!_debugDoingPaint); |
- // TODO(abarth): It's very redundant to call this for every node in the |
- // render tree during layout. We should instead compute a summary bit and |
- // call it once at the end of layout. |
- sky.view.scheduleFrame(); |
- } |
- void paint(RenderNodeDisplayList canvas) { } |
- |
- |
- // HIT TESTING |
- |
- void handlePointer(sky.PointerEvent event) { |
- // override this if you have a client, to hand it to the client |
- // override this if you want to do anything with the pointer event |
- } |
- |
- // RenderNode subclasses are expected to have a method like the |
- // following (with the signature being whatever passes for coordinates |
- // for this particular class): |
- // bool hitTest(HitTestResult result, { sky.Point position }) { |
- // // If (x,y) is not inside this node, then return false. (You |
- // // can assume that the given coordinate is inside your |
- // // dimensions. You only need to check this if you're an |
- // // irregular shape, e.g. if you have a hole.) |
- // // Otherwise: |
- // // For each child that intersects x,y, in z-order starting from the top, |
- // // call hitTest() for that child, passing it /result/, and the coordinates |
- // // converted to the child's coordinate origin, and stop at the first child |
- // // that returns true. |
- // // Then, add yourself to /result/, and return true. |
- // } |
- // You must not add yourself to /result/ if you return false. |
- |
-} |
- |
-class HitTestResult { |
- final List<RenderNode> path = new List<RenderNode>(); |
- |
- RenderNode get result => path.first; |
- |
- void add(RenderNode node) { |
- path.add(node); |
- } |
-} |
- |
- |
-// GENERIC MIXIN FOR RENDER NODES WITH ONE CHILD |
- |
-abstract class RenderNodeWithChildMixin<ChildType extends RenderNode> { |
- ChildType _child; |
- ChildType get child => _child; |
- void set child (ChildType value) { |
- if (_child != null) |
- dropChild(_child); |
- _child = value; |
- if (_child != null) |
- adoptChild(_child); |
- markNeedsLayout(); |
- } |
-} |
- |
- |
-// GENERIC MIXIN FOR RENDER NODES WITH A LIST OF CHILDREN |
- |
-abstract class ContainerParentDataMixin<ChildType extends RenderNode> { |
- ChildType previousSibling; |
- ChildType nextSibling; |
- void detachSiblings() { |
- if (previousSibling != null) { |
- assert(previousSibling.parentData is ContainerParentDataMixin<ChildType>); |
- assert(previousSibling != this); |
- assert(previousSibling.parentData.nextSibling == this); |
- previousSibling.parentData.nextSibling = nextSibling; |
- } |
- if (nextSibling != null) { |
- assert(nextSibling.parentData is ContainerParentDataMixin<ChildType>); |
- assert(nextSibling != this); |
- assert(nextSibling.parentData.previousSibling == this); |
- nextSibling.parentData.previousSibling = previousSibling; |
- } |
- previousSibling = null; |
- nextSibling = null; |
- } |
-} |
- |
-abstract class ContainerRenderNodeMixin<ChildType extends RenderNode, ParentDataType extends ContainerParentDataMixin<ChildType>> implements RenderNode { |
- // abstract class that has only InlineNode children |
- |
- bool _debugUltimatePreviousSiblingOf(ChildType child, { ChildType equals }) { |
- assert(child.parentData is ParentDataType); |
- while (child.parentData.previousSibling != null) { |
- assert(child.parentData.previousSibling != child); |
- child = child.parentData.previousSibling; |
- assert(child.parentData is ParentDataType); |
- } |
- return child == equals; |
- } |
- bool _debugUltimateNextSiblingOf(ChildType child, { ChildType equals }) { |
- assert(child.parentData is ParentDataType); |
- while (child.parentData.nextSibling != null) { |
- assert(child.parentData.nextSibling != child); |
- child = child.parentData.nextSibling; |
- assert(child.parentData is ParentDataType); |
- } |
- return child == equals; |
- } |
- |
- ChildType _firstChild; |
- ChildType _lastChild; |
- void add(ChildType child, { ChildType before }) { |
- assert(child != this); |
- assert(before != this); |
- assert(child != before); |
- assert(child != _firstChild); |
- assert(child != _lastChild); |
- adoptChild(child); |
- assert(child.parentData is ParentDataType); |
- assert(child.parentData.nextSibling == null); |
- assert(child.parentData.previousSibling == null); |
- if (before == null) { |
- // append at the end (_lastChild) |
- child.parentData.previousSibling = _lastChild; |
- if (_lastChild != null) { |
- assert(_lastChild.parentData is ParentDataType); |
- _lastChild.parentData.nextSibling = child; |
- } |
- _lastChild = child; |
- if (_firstChild == null) |
- _firstChild = child; |
- } else { |
- assert(_firstChild != null); |
- assert(_lastChild != null); |
- assert(_debugUltimatePreviousSiblingOf(before, equals: _firstChild)); |
- assert(_debugUltimateNextSiblingOf(before, equals: _lastChild)); |
- assert(before.parentData is ParentDataType); |
- if (before.parentData.previousSibling == null) { |
- // insert at the start (_firstChild); we'll end up with two or more children |
- assert(before == _firstChild); |
- child.parentData.nextSibling = before; |
- before.parentData.previousSibling = child; |
- _firstChild = child; |
- } else { |
- // insert in the middle; we'll end up with three or more children |
- // set up links from child to siblings |
- child.parentData.previousSibling = before.parentData.previousSibling; |
- child.parentData.nextSibling = before; |
- // set up links from siblings to child |
- assert(child.parentData.previousSibling.parentData is ParentDataType); |
- assert(child.parentData.nextSibling.parentData is ParentDataType); |
- child.parentData.previousSibling.parentData.nextSibling = child; |
- child.parentData.nextSibling.parentData.previousSibling = child; |
- assert(before.parentData.previousSibling == child); |
- } |
- } |
- markNeedsLayout(); |
- } |
- void remove(ChildType child) { |
- assert(child.parentData is ParentDataType); |
- assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild)); |
- assert(_debugUltimateNextSiblingOf(child, equals: _lastChild)); |
- if (child.parentData.previousSibling == null) { |
- assert(_firstChild == child); |
- _firstChild = child.parentData.nextSibling; |
- } else { |
- assert(child.parentData.previousSibling.parentData is ParentDataType); |
- child.parentData.previousSibling.parentData.nextSibling = child.parentData.nextSibling; |
- } |
- if (child.parentData.nextSibling == null) { |
- assert(_lastChild == child); |
- _lastChild = child.parentData.previousSibling; |
- } else { |
- assert(child.parentData.nextSibling.parentData is ParentDataType); |
- child.parentData.nextSibling.parentData.previousSibling = child.parentData.previousSibling; |
- } |
- child.parentData.previousSibling = null; |
- child.parentData.nextSibling = null; |
- dropChild(child); |
- markNeedsLayout(); |
- } |
- void redepthChildren() { |
- ChildType child = _firstChild; |
- while (child != null) { |
- redepthChild(child); |
- assert(child.parentData is ParentDataType); |
- child = child.parentData.nextSibling; |
- } |
- } |
- void attachChildren() { |
- ChildType child = _firstChild; |
- while (child != null) { |
- child.attach(); |
- assert(child.parentData is ParentDataType); |
- child = child.parentData.nextSibling; |
- } |
- } |
- void detachChildren() { |
- ChildType child = _firstChild; |
- while (child != null) { |
- child.detach(); |
- assert(child.parentData is ParentDataType); |
- child = child.parentData.nextSibling; |
- } |
- } |
- |
- ChildType get firstChild => _firstChild; |
- ChildType get lastChild => _lastChild; |
- ChildType childAfter(ChildType child) { |
- assert(child.parentData is ParentDataType); |
- return child.parentData.nextSibling; |
- } |
- |
-} |
- |
- |
-// GENERIC BOX RENDERING |
-// Anything that has a concept of x, y, width, height is going to derive from this |
- |
-class EdgeDims { |
- // used for e.g. padding |
- const EdgeDims(this.top, this.right, this.bottom, this.left); |
- final double top; |
- final double right; |
- final double bottom; |
- final double left; |
- operator ==(EdgeDims other) => (top == other.top) || |
- (right == other.right) || |
- (bottom == other.bottom) || |
- (left == other.left); |
-} |
- |
-class BoxConstraints { |
- const BoxConstraints({ |
- this.minWidth: 0.0, |
- this.maxWidth: double.INFINITY, |
- this.minHeight: 0.0, |
- this.maxHeight: double.INFINITY}); |
- |
- BoxConstraints.tight(sky.Size size) |
- : minWidth = size.width, |
- maxWidth = size.width, |
- minHeight = size.height, |
- maxHeight = size.height; |
- |
- BoxConstraints deflate(EdgeDims edges) { |
- assert(edges != null); |
- return new BoxConstraints( |
- minWidth: minWidth, |
- maxWidth: maxWidth - (edges.left + edges.right), |
- minHeight: minHeight, |
- maxHeight: maxHeight - (edges.top + edges.bottom) |
- ); |
- } |
- |
- final double minWidth; |
- final double maxWidth; |
- final double minHeight; |
- final double maxHeight; |
- |
- double constrainWidth(double width) { |
- return clamp(min: minWidth, max: maxWidth, value: width); |
- } |
- |
- double constrainHeight(double height) { |
- return clamp(min: minHeight, max: maxHeight, value: height); |
- } |
- |
- sky.Size constrain(sky.Size size) { |
- return new sky.Size(constrainWidth(size.width), constrainHeight(size.height)); |
- } |
- |
- bool get isInfinite => maxWidth >= double.INFINITY || maxHeight >= double.INFINITY; |
-} |
- |
-class BoxParentData extends ParentData { |
- sky.Point position = new sky.Point(0.0, 0.0); |
-} |
- |
-abstract class RenderBox extends RenderNode { |
- |
- void setParentData(RenderNode child) { |
- if (child.parentData is! BoxParentData) |
- child.parentData = new BoxParentData(); |
- } |
- |
- // override this to report what dimensions you would have if you |
- // were laid out with the given constraints this can walk the tree |
- // if it must, but it should be as cheap as possible; just get the |
- // dimensions and nothing else (e.g. don't calculate hypothetical |
- // child positions if they're not needed to determine dimensions) |
- sky.Size getIntrinsicDimensions(BoxConstraints constraints) { |
- return constraints.constrain(new sky.Size(0.0, 0.0)); |
- } |
- |
- BoxConstraints get constraints => super.constraints as BoxConstraints; |
- void performResize() { |
- // default behaviour for subclasses that have sizedByParent = true |
- size = constraints.constrain(new sky.Size(0.0, 0.0)); |
- assert(size.height < double.INFINITY); |
- assert(size.width < double.INFINITY); |
- } |
- void performLayout() { |
- // descendants have to either override performLayout() to set both |
- // width and height and lay out children, or, set sizedByParent to |
- // true so that performResize()'s logic above does its thing. |
- assert(sizedByParent); |
- } |
- |
- bool hitTest(HitTestResult result, { sky.Point position }) { |
- hitTestChildren(result, position: position); |
- result.add(this); |
- return true; |
- } |
- void hitTestChildren(HitTestResult result, { sky.Point position }) { } |
- |
- sky.Size size = new sky.Size(0.0, 0.0); |
-} |
- |
-abstract class RenderProxyBox extends RenderBox with RenderNodeWithChildMixin<RenderBox> { |
- RenderProxyBox(RenderBox child) { |
- this.child = child; |
- } |
- |
- sky.Size getIntrinsicDimensions(BoxConstraints constraints) { |
- if (child != null) |
- return child.getIntrinsicDimensions(constraints); |
- return super.getIntrinsicDimensions(constraints); |
- } |
- |
- void performLayout() { |
- if (child != null) { |
- child.layout(constraints, parentUsesSize: true); |
- size = child.size; |
- } else { |
- performResize(); |
- } |
- } |
- |
- void hitTestChildren(HitTestResult result, { sky.Point position }) { |
- if (child != null) |
- child.hitTest(result, position: position); |
- else |
- super.hitTestChildren(result, position: position); |
- } |
- |
- void paint(RenderNodeDisplayList canvas) { |
- if (child != null) |
- child.paint(canvas); |
- } |
-} |
- |
-class RenderSizedBox extends RenderProxyBox { |
- final sky.Size desiredSize; |
- |
- RenderSizedBox({ |
- RenderBox child, |
- this.desiredSize: const sky.Size.infinite() |
- }) : super(child); |
- |
- sky.Size getIntrinsicDimensions(BoxConstraints constraints) { |
- return constraints.constrain(desiredSize); |
- } |
- |
- void performLayout() { |
- size = constraints.constrain(desiredSize); |
- child.layout(new BoxConstraints.tight(size)); |
- } |
-} |
- |
-class RenderPadding extends RenderBox with RenderNodeWithChildMixin<RenderBox> { |
- |
- RenderPadding(EdgeDims padding, RenderBox child) { |
- assert(padding != null); |
- this.padding = padding; |
- this.child = child; |
- } |
- |
- EdgeDims _padding; |
- EdgeDims get padding => _padding; |
- void set padding (EdgeDims value) { |
- assert(value != null); |
- if (_padding != value) { |
- _padding = value; |
- markNeedsLayout(); |
- } |
- } |
- |
- sky.Size getIntrinsicDimensions(BoxConstraints constraints) { |
- assert(padding != null); |
- constraints = constraints.deflate(padding); |
- if (child == null) |
- return super.getIntrinsicDimensions(constraints); |
- return child.getIntrinsicDimensions(constraints); |
- } |
- |
- void performLayout() { |
- assert(padding != null); |
- BoxConstraints innerConstraints = constraints.deflate(padding); |
- if (child == null) { |
- size = innerConstraints.constrain( |
- new sky.Size(padding.left + padding.right, padding.top + padding.bottom)); |
- return; |
- } |
- child.layout(innerConstraints, parentUsesSize: true); |
- assert(child.parentData is BoxParentData); |
- child.parentData.position = new sky.Point(padding.left, padding.top); |
- size = constraints.constrain(new sky.Size(padding.left + child.size.width + padding.right, |
- padding.top + child.size.height + padding.bottom)); |
- } |
- |
- void paint(RenderNodeDisplayList canvas) { |
- if (child != null) |
- canvas.paintChild(child, child.parentData.position); |
- } |
- |
- void hitTestChildren(HitTestResult result, { sky.Point position }) { |
- if (child != null) { |
- assert(child.parentData is BoxParentData); |
- sky.Rect childBounds = new sky.Rect.fromPointAndSize(child.parentData.position, child.size); |
- if (childBounds.contains(position)) { |
- child.hitTest(result, position: new sky.Point(position.x - child.parentData.position.x, |
- position.y - child.parentData.position.y)); |
- } |
- } |
- } |
- |
-} |
- |
-// This must be immutable, because we won't notice when it changes |
-class BoxDecoration { |
- const BoxDecoration({ |
- this.backgroundColor |
- }); |
- |
- final int backgroundColor; |
-} |
- |
-class RenderDecoratedBox extends RenderProxyBox { |
- |
- RenderDecoratedBox({ |
- BoxDecoration decoration, |
- RenderBox child |
- }) : _decoration = decoration, super(child); |
- |
- BoxDecoration _decoration; |
- BoxDecoration get decoration => _decoration; |
- void set decoration (BoxDecoration value) { |
- if (value == _decoration) |
- return; |
- _decoration = value; |
- markNeedsPaint(); |
- } |
- |
- void paint(RenderNodeDisplayList canvas) { |
- assert(size.width != null); |
- assert(size.height != null); |
- |
- if (_decoration == null) |
- return; |
- |
- if (_decoration.backgroundColor != null) { |
- sky.Paint paint = new sky.Paint()..color = _decoration.backgroundColor; |
- canvas.drawRect(new sky.Rect.fromLTRB(0.0, 0.0, size.width, size.height), paint); |
- } |
- super.paint(canvas); |
- } |
- |
-} |
- |
- |
-// RENDER VIEW LAYOUT MANAGER |
- |
-class ViewConstraints { |
- |
- const ViewConstraints({ |
- this.width: 0.0, this.height: 0.0, this.orientation: null |
- }); |
- |
- final double width; |
- final double height; |
- final int orientation; |
- |
-} |
- |
-class RenderView extends RenderNode with RenderNodeWithChildMixin<RenderBox> { |
- |
- RenderView({ |
- RenderBox child, |
- this.timeForRotation: const Duration(microseconds: 83333) |
- }) { |
- this.child = child; |
- } |
- |
- sky.Size _size = new sky.Size(0.0, 0.0); |
- double get width => _size.width; |
- double get height => _size.height; |
- |
- int _orientation; // 0..3 |
- int get orientation => _orientation; |
- Duration timeForRotation; |
- |
- ViewConstraints get constraints => super.constraints as ViewConstraints; |
- bool get sizedByParent => true; |
- void performResize() { |
- if (constraints.orientation != _orientation) { |
- if (_orientation != null && child != null) |
- child.rotate(oldAngle: _orientation, newAngle: constraints.orientation, time: timeForRotation); |
- _orientation = constraints.orientation; |
- } |
- _size = new sky.Size(constraints.width, constraints.height); |
- assert(_size.height < double.INFINITY); |
- assert(_size.width < double.INFINITY); |
- } |
- void performLayout() { |
- if (child != null) { |
- child.layout(new BoxConstraints.tight(_size)); |
- assert(child.size.width == width); |
- assert(child.size.height == height); |
- } |
- } |
- |
- void rotate({ int oldAngle, int newAngle, Duration time }) { |
- assert(false); // nobody tells the screen to rotate, the whole rotate() dance is started from our performResize() |
- } |
- |
- bool hitTest(HitTestResult result, { sky.Point position }) { |
- if (child != null) { |
- sky.Rect childBounds = new sky.Rect.fromSize(child.size); |
- if (childBounds.contains(position)) |
- child.hitTest(result, position: position); |
- } |
- result.add(this); |
- return true; |
- } |
- |
- void paint(RenderNodeDisplayList canvas) { |
- if (child != null) |
- canvas.paintChild(child, new sky.Point(0.0, 0.0)); |
- } |
- |
- void paintFrame() { |
- RenderNode._debugDoingPaint = true; |
- var canvas = new RenderNodeDisplayList(sky.view.width, sky.view.height); |
- paint(canvas); |
- sky.view.picture = canvas.endRecording(); |
- RenderNode._debugDoingPaint = false; |
- } |
- |
-} |
- |
-// DEFAULT BEHAVIORS FOR RENDERBOX CONTAINERS |
-abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, ParentDataType extends ContainerParentDataMixin<ChildType>> implements ContainerRenderNodeMixin<ChildType, ParentDataType> { |
- |
- void defaultHitTestChildren(HitTestResult result, { sky.Point position }) { |
- // the x, y parameters have the top left of the node's box as the origin |
- ChildType child = lastChild; |
- while (child != null) { |
- assert(child.parentData is BoxParentData); |
- sky.Rect childBounds = new sky.Rect.fromPointAndSize(child.parentData.position, child.size); |
- if (childBounds.contains(position)) { |
- if (child.hitTest(result, position: new sky.Point(position.x - child.parentData.position.x, |
- position.y - child.parentData.position.y))) |
- break; |
- } |
- child = child.parentData.previousSibling; |
- } |
- } |
- |
- void defaultPaint(RenderNodeDisplayList canvas) { |
- RenderBox child = firstChild; |
- while (child != null) { |
- assert(child.parentData is BoxParentData); |
- canvas.paintChild(child, child.parentData.position); |
- child = child.parentData.nextSibling; |
- } |
- } |
-} |
- |
-// BLOCK LAYOUT MANAGER |
- |
-class BlockParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> { } |
- |
-class RenderBlock extends RenderBox with ContainerRenderNodeMixin<RenderBox, BlockParentData>, |
- RenderBoxContainerDefaultsMixin<RenderBox, BlockParentData> { |
- // lays out RenderBox children in a vertical stack |
- // uses the maximum width provided by the parent |
- // sizes itself to the height of its child stack |
- |
- void setParentData(RenderBox child) { |
- if (child.parentData is! BlockParentData) |
- child.parentData = new BlockParentData(); |
- } |
- |
- // override this to report what dimensions you would have if you |
- // were laid out with the given constraints this can walk the tree |
- // if it must, but it should be as cheap as possible; just get the |
- // dimensions and nothing else (e.g. don't calculate hypothetical |
- // child positions if they're not needed to determine dimensions) |
- sky.Size getIntrinsicDimensions(BoxConstraints constraints) { |
- double height = 0.0; |
- double width = constraints.constrainWidth(constraints.maxWidth); |
- assert(width < double.INFINITY); |
- RenderBox child = firstChild; |
- BoxConstraints innerConstraints = new BoxConstraints(minWidth: width, |
- maxWidth: width); |
- while (child != null) { |
- height += child.getIntrinsicDimensions(innerConstraints).height; |
- assert(child.parentData is BlockParentData); |
- child = child.parentData.nextSibling; |
- } |
- |
- return new sky.Size(width, constraints.constrainHeight(height)); |
- } |
- |
- void performLayout() { |
- assert(constraints is BoxConstraints); |
- double width = constraints.constrainWidth(constraints.maxWidth); |
- double y = 0.0; |
- RenderBox child = firstChild; |
- while (child != null) { |
- child.layout(new BoxConstraints(minWidth: width, maxWidth: width), parentUsesSize: true); |
- assert(child.parentData is BlockParentData); |
- child.parentData.position = new sky.Point(0.0, y); |
- y += child.size.height; |
- child = child.parentData.nextSibling; |
- } |
- size = new sky.Size(width, constraints.constrainHeight(y)); |
- assert(size.width < double.INFINITY); |
- assert(size.height < double.INFINITY); |
- } |
- |
- void hitTestChildren(HitTestResult result, { sky.Point position }) { |
- defaultHitTestChildren(result, position: position); |
- } |
- |
- void paint(RenderNodeDisplayList canvas) { |
- defaultPaint(canvas); |
- } |
- |
-} |
- |
-// FLEXBOX LAYOUT MANAGER |
- |
-class FlexBoxParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> { |
- int flex; |
- void merge(FlexBoxParentData other) { |
- if (other.flex != null) |
- flex = other.flex; |
- super.merge(other); |
- } |
-} |
- |
-enum FlexDirection { Horizontal, Vertical } |
- |
-class RenderFlex extends RenderBox with ContainerRenderNodeMixin<RenderBox, FlexBoxParentData>, |
- RenderBoxContainerDefaultsMixin<RenderBox, BlockParentData> { |
- // lays out RenderBox children using flexible layout |
- |
- RenderFlex({ |
- FlexDirection direction: FlexDirection.Horizontal |
- }) : _direction = direction; |
- |
- FlexDirection _direction; |
- FlexDirection get direction => _direction; |
- void set direction (FlexDirection value) { |
- if (_direction != value) { |
- _direction = value; |
- markNeedsLayout(); |
- } |
- } |
- |
- void setParentData(RenderBox child) { |
- if (child.parentData is! FlexBoxParentData) |
- child.parentData = new FlexBoxParentData(); |
- } |
- |
- bool get sizedByParent => true; |
- void performResize() { |
- size = _constraints.constrain(new sky.Size(_constraints.maxWidth, _constraints.maxHeight)); |
- assert(size.height < double.INFINITY); |
- assert(size.width < double.INFINITY); |
- } |
- |
- int _getFlex(RenderBox child) { |
- assert(child.parentData is FlexBoxParentData); |
- return child.parentData.flex != null ? child.parentData.flex : 0; |
- } |
- |
- void performLayout() { |
- // Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexible Lengths |
- // Steps 1-3. Determine used flex factor, size inflexible items, calculate free space |
- int totalFlex = 0; |
- assert(constraints != null); |
- double freeSpace = (_direction == FlexDirection.Horizontal) ? constraints.maxWidth : constraints.maxHeight; |
- RenderBox child = firstChild; |
- while (child != null) { |
- int flex = _getFlex(child); |
- if (flex > 0) { |
- totalFlex += child.parentData.flex; |
- } else { |
- BoxConstraints innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight, |
- maxWidth: constraints.maxWidth); |
- child.layout(innerConstraints, parentUsesSize: true); |
- freeSpace -= (_direction == FlexDirection.Horizontal) ? child.size.width : child.size.height; |
- } |
- child = child.parentData.nextSibling; |
- } |
- |
- // Steps 4-5. Distribute remaining space to flexible children. |
- double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; |
- double usedSpace = 0.0; |
- child = firstChild; |
- while (child != null) { |
- int flex = _getFlex(child); |
- if (flex > 0) { |
- double spaceForChild = spacePerFlex * flex; |
- BoxConstraints innerConstraints; |
- switch (_direction) { |
- case FlexDirection.Horizontal: |
- innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight, |
- minWidth: spaceForChild, |
- maxWidth: spaceForChild); |
- break; |
- case FlexDirection.Vertical: |
- innerConstraints = new BoxConstraints(minHeight: spaceForChild, |
- maxHeight: spaceForChild, |
- maxWidth: constraints.maxWidth); |
- break; |
- } |
- child.layout(innerConstraints, parentUsesSize: true); |
- } |
- |
- // For now, center the flex items in the cross direction |
- switch (_direction) { |
- case FlexDirection.Horizontal: |
- child.parentData.position = new sky.Point(usedSpace, size.height / 2.0 - child.size.height / 2.0); |
- usedSpace += child.size.width; |
- break; |
- case FlexDirection.Vertical: |
- child.parentData.position = new sky.Point(size.width / 2.0 - child.size.width / 2.0, usedSpace); |
- usedSpace += child.size.height; |
- break; |
- } |
- child = child.parentData.nextSibling; |
- } |
- } |
- |
- void hitTestChildren(HitTestResult result, { sky.Point position }) { |
- defaultHitTestChildren(result, position: position); |
- } |
- |
- void paint(RenderNodeDisplayList canvas) { |
- defaultPaint(canvas); |
- } |
-} |
- |
-class RenderInline extends RenderNode { |
- String data; |
- |
- RenderInline(this.data); |
-} |
- |
-class RenderParagraph extends RenderBox { |
- |
- RenderParagraph({ |
- String text, |
- int color |
- }) : _color = color { |
- _layoutRoot.rootElement = _document.createElement('p'); |
- this.text = text; |
- } |
- |
- final sky.Document _document = new sky.Document(); |
- final sky.LayoutRoot _layoutRoot = new sky.LayoutRoot(); |
- |
- String get text => (_layoutRoot.rootElement.firstChild as sky.Text).data; |
- void set text (String value) { |
- _layoutRoot.rootElement.setChild(_document.createText(value)); |
- markNeedsLayout(); |
- } |
- |
- int _color = 0xFF000000; |
- int get color => _color; |
- void set color (int value) { |
- if (_color != value) { |
- _color = value; |
- markNeedsPaint(); |
- } |
- } |
- |
- sky.Size getIntrinsicDimensions(BoxConstraints constraints) { |
- assert(false); |
- return null; |
- // we don't currently support this for RenderParagraph |
- } |
- |
- void performLayout() { |
- _layoutRoot.maxWidth = constraints.maxWidth; |
- _layoutRoot.minWidth = constraints.minWidth; |
- _layoutRoot.minHeight = constraints.minHeight; |
- _layoutRoot.maxHeight = constraints.maxHeight; |
- _layoutRoot.layout(); |
- size = constraints.constrain(new sky.Size(_layoutRoot.rootElement.width, _layoutRoot.rootElement.height)); |
- } |
- |
- void paint(RenderNodeDisplayList canvas) { |
- // _layoutRoot.rootElement.style['color'] = 'rgba(' + ...color... + ')'; |
- _layoutRoot.paint(canvas); |
- } |
- |
- // we should probably expose a way to do precise (inter-glpyh) hit testing |
- |
-} |