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

Unified Diff: sky/sdk/lib/framework/rendering/render_node.dart

Issue 1161003002: Split layout2.dart into several files (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 7 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
Index: sky/sdk/lib/framework/rendering/render_node.dart
diff --git a/sky/sdk/lib/framework/rendering/render_node.dart b/sky/sdk/lib/framework/rendering/render_node.dart
new file mode 100644
index 0000000000000000000000000000000000000000..29863a84ffac30f6160b87342fb8b636d466e716
--- /dev/null
+++ b/sky/sdk/lib/framework/rendering/render_node.dart
@@ -0,0 +1,390 @@
+// 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;
+
+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;
+ }
+}
« no previous file with comments | « sky/sdk/lib/framework/rendering/render_flex.dart ('k') | sky/sdk/lib/framework/rendering/render_paragraph.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698