object.dart 51.3 KB
Newer Older
1 2 3 4 5 6 7 8
// 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 'dart:sky' as sky;
import 'dart:sky' show Point, Offset, Size, Rect, Color, Paint, Path;

9
import 'package:sky/animation.dart';
Adam Barth's avatar
Adam Barth committed
10 11
import 'package:sky/src/rendering/debug.dart';
import 'package:sky/src/rendering/hit_test.dart';
12
import 'package:sky/src/rendering/layer.dart';
Adam Barth's avatar
Adam Barth committed
13
import 'package:sky/src/rendering/node.dart';
14
import 'package:vector_math/vector_math.dart';
15 16

export 'dart:sky' show Point, Offset, Size, Rect, Color, Paint, Path;
Adam Barth's avatar
Adam Barth committed
17
export 'package:sky/src/rendering/hit_test.dart' show EventDisposition, HitTestTarget, HitTestEntry, HitTestResult;
18

19 20 21 22 23
/// Base class for data associated with a [RenderObject] by its parent
///
/// Some render objects wish to store data on their children, such as their
/// input parameters to the parent's layout algorithm or their position relative
/// to other children.
24 25 26 27 28
class ParentData {
  void detach() {
    detachSiblings();
  }
  void detachSiblings() { } // workaround for lack of inter-class mixins in Dart
29 30

  /// Override this function in subclasses to merge in data from other instance into this instance
31 32 33 34 35 36
  void merge(ParentData other) {
    assert(other.runtimeType == this.runtimeType);
  }
  String toString() => '<none>';
}

37
/// Obsolete class that will be removed eventually
38 39
class PaintingCanvas extends sky.Canvas {
  PaintingCanvas(sky.PictureRecorder recorder, Rect bounds) : super(recorder, bounds);
40
  // TODO(ianh): Just use sky.Canvas everywhere instead
41 42
}

43 44 45 46 47 48 49 50 51 52 53 54
/// A place to paint
///
/// Rather than holding a canvas directly, render objects paint using a painting
/// context. The painting context has a canvas, which receives the
/// individual draw operations, and also has functions for painting child
/// render objects.
///
/// When painting a child render object, the canvas held by the painting context
/// can change because the draw operations issued before and after painting the
/// child might be recorded in separate compositing layers. For this reason, do
/// not hold a reference to the canvas across operations that might paint
/// child render objects.
55
class PaintingContext {
56
  /// Construct a painting context at a given offset with the given bounds
Hixie's avatar
Hixie committed
57 58 59
  PaintingContext.withOffset(Offset offset, Rect paintBounds) {
    _containerLayer = new ContainerLayer(offset: offset);
    _startRecording(paintBounds);
60 61
  }

62
  /// Construct a painting context for paiting into the given layer with the given bounds
Hixie's avatar
Hixie committed
63 64 65 66
  PaintingContext.withLayer(ContainerLayer containerLayer, Rect paintBounds) {
    _containerLayer = containerLayer;
    _startRecording(paintBounds);
  }
67

68
  /// A backdoor for testing that lets the test set a specific canvas
69
  PaintingContext.forTesting(this._canvas);
70

Hixie's avatar
Hixie committed
71
  ContainerLayer _containerLayer;
72
  /// The layer contain all the composting layers that will be used for this context
Hixie's avatar
Hixie committed
73 74 75 76 77
  ContainerLayer get containerLayer => _containerLayer;

  PictureLayer _currentLayer;
  sky.PictureRecorder _recorder;
  PaintingCanvas _canvas;
78 79 80 81 82 83
  /// The canvas on which to paint
  ///
  /// This getter can return a different canvas object after painting child
  /// render objects using this canvas because draw operations before and after
  /// a child might need to be recorded in separate compositing layers.
  PaintingCanvas get canvas => _canvas;
Hixie's avatar
Hixie committed
84 85 86

  void _startRecording(Rect paintBounds) {
    assert(_currentLayer == null);
87
    assert(_recorder == null);
88
    assert(_canvas == null);
Hixie's avatar
Hixie committed
89
    _currentLayer = new PictureLayer(paintBounds: paintBounds);
90
    _recorder = new sky.PictureRecorder();
Hixie's avatar
Hixie committed
91
    _canvas = new PaintingCanvas(_recorder, paintBounds);
92
    _containerLayer.append(_currentLayer);
93 94
  }

95
  /// Stop recording draw operations into the current compositing layer
96
  void endRecording() {
Hixie's avatar
Hixie committed
97
    assert(_currentLayer != null);
98
    assert(_recorder != null);
99
    assert(_canvas != null);
Hixie's avatar
Hixie committed
100 101
    _currentLayer.picture = _recorder.endRecording();
    _currentLayer = null;
102 103 104 105
    _recorder = null;
    _canvas = null;
  }

106
  /// Whether the canvas is in a state that permits drawing the given child
Hixie's avatar
Hixie committed
107
  bool debugCanPaintChild(RenderObject child) {
108 109 110 111
    // You need to use layers if you are applying transforms, clips,
    // or similar, to a child. To do so, use the paintChildWith*()
    // methods below.
    // (commented out for now because we haven't ported everything yet)
Hixie's avatar
Hixie committed
112
    assert(canvas.getSaveCount() == 1 || !child.needsCompositing);
113
    return true;
114
  }
115

116 117 118 119 120 121 122
  /// Paint a child render object at the given position
  ///
  /// If the child needs compositing, a new composited layer will be created
  /// and inserted into the containerLayer. If the child does not require
  /// compositing, the child will be painted into the current canvas.
  ///
  /// Note: After calling this function, the current canvas might change.
Hixie's avatar
Hixie committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
  void paintChild(RenderObject child, Point childPosition) {
    assert(debugCanPaintChild(child));
    final Offset childOffset = childPosition.toOffset();
    if (!child.hasLayer) {
      insertChild(child, childOffset);
    } else {
      compositeChild(child, childOffset: childOffset, parentLayer: _containerLayer);
    }
  }

  // Below we have various variants of the paintChild() method, which
  // do additional work, such as clipping or transforming, at the same
  // time as painting the children.

  // If none of the descendants require compositing, then these don't
  // need to use a new layer, because at no point will any of the
  // children introduce a new layer of their own. In that case, we
  // just use regular canvas commands to do the work.

  // If at least one of the descendants requires compositing, though,
  // we introduce a new layer to do the work, so that when the
  // children are split into a new layer, the work (e.g. clip) is not
  // lost, as it would if we didn't introduce a new layer.

  static final Paint _disableAntialias = new Paint()..isAntiAlias = false;

149 150 151 152 153 154
  /// Paint a child with a rectangular clip
  ///
  /// If the child needs compositing, the clip will be applied by a
  /// compositing layer. Otherwise, the clip will be applied by the canvas.
  ///
  /// Note: clipRect is in the parent's coordinate space
Hixie's avatar
Hixie committed
155 156 157 158 159 160 161 162 163 164
  void paintChildWithClipRect(RenderObject child, Point childPosition, Rect clipRect) {
    assert(debugCanPaintChild(child));
    final Offset childOffset = childPosition.toOffset();
    if (!child.needsCompositing) {
      canvas.save();
      canvas.clipRect(clipRect);
      insertChild(child, childOffset);
      canvas.restore();
    } else {
      ClipRectLayer clipLayer = new ClipRectLayer(offset: childOffset, clipRect: clipRect);
165
      _containerLayer.append(clipLayer);
Hixie's avatar
Hixie committed
166 167 168 169
      compositeChild(child, parentLayer: clipLayer);
    }
  }

170 171 172 173 174 175
  /// Paint a child with a rounded-rectangular clip
  ///
  /// If the child needs compositing, the clip will be applied by a
  /// compositing layer. Otherwise, the clip will be applied by the canvas.
  ///
  /// Note: clipRRect is in the parent's coordinate space
Hixie's avatar
Hixie committed
176 177 178 179 180 181 182 183 184 185
  void paintChildWithClipRRect(RenderObject child, Point childPosition, Rect bounds, sky.RRect clipRRect) {
    assert(debugCanPaintChild(child));
    final Offset childOffset = childPosition.toOffset();
    if (!child.needsCompositing) {
      canvas.saveLayer(bounds, _disableAntialias);
      canvas.clipRRect(clipRRect);
      insertChild(child, childOffset);
      canvas.restore();
    } else {
      ClipRRectLayer clipLayer = new ClipRRectLayer(offset: childOffset, bounds: bounds, clipRRect: clipRRect);
186
      _containerLayer.append(clipLayer);
Hixie's avatar
Hixie committed
187 188 189 190
      compositeChild(child, parentLayer: clipLayer);
    }
  }

191 192 193 194 195 196
  /// Paint a child with a clip path
  ///
  /// If the child needs compositing, the clip will be applied by a
  /// compositing layer. Otherwise, the clip will be applied by the canvas.
  ///
  /// Note: bounds and clipPath are in the parent's coordinate space
Hixie's avatar
Hixie committed
197 198 199 200 201 202 203 204 205
  void paintChildWithClipPath(RenderObject child, Point childPosition, Rect bounds, Path clipPath) {
    assert(debugCanPaintChild(child));
    final Offset childOffset = childPosition.toOffset();
    if (!child.needsCompositing) {
      canvas.saveLayer(bounds, _disableAntialias);
      canvas.clipPath(clipPath);
      canvas.translate(childOffset.dx, childOffset.dy);
      insertChild(child, Offset.zero);
      canvas.restore();
206
    } else {
Hixie's avatar
Hixie committed
207
      ClipPathLayer clipLayer = new ClipPathLayer(offset: childOffset, bounds: bounds, clipPath: clipPath);
208
      _containerLayer.append(clipLayer);
Hixie's avatar
Hixie committed
209
      compositeChild(child, parentLayer: clipLayer);
210 211
    }
  }
212

213 214 215 216
  /// Paint a child with a transform
  ///
  /// If the child needs compositing, the transform will be applied by a
  /// compositing layer. Otherwise, the transform will be applied by the canvas.
Hixie's avatar
Hixie committed
217 218 219 220
  void paintChildWithTransform(RenderObject child, Point childPosition, Matrix4 transform) {
    assert(debugCanPaintChild(child));
    final Offset childOffset = childPosition.toOffset();
    if (!child.needsCompositing) {
221
      canvas.save();
Hixie's avatar
Hixie committed
222 223 224
      canvas.translate(childOffset.dx, childOffset.dy);
      canvas.concat(transform.storage);
      insertChild(child, Offset.zero);
225 226
      canvas.restore();
    } else {
Hixie's avatar
Hixie committed
227
      TransformLayer transformLayer = new TransformLayer(offset: childOffset, transform: transform);
228
      _containerLayer.append(transformLayer);
Hixie's avatar
Hixie committed
229
      compositeChild(child, parentLayer: transformLayer);
230
    }
231
  }
232

Adam Barth's avatar
Adam Barth committed
233 234 235 236 237 238 239
  static Paint _getPaintForAlpha(int alpha) {
    return new Paint()
      ..color = new Color.fromARGB(alpha, 0, 0, 0)
      ..setTransferMode(sky.TransferMode.srcOver)
      ..isAntiAlias = false;
  }

240 241 242 243 244
  /// Paint a child with an opacity
  ///
  /// If the child needs compositing, the blending operation will be applied by
  /// a compositing layer. Otherwise, the blending operation will be applied by
  /// the canvas.
245 246 247 248
  void paintChildWithOpacity(RenderObject child,
                             Point childPosition,
                             Rect bounds,
                             int alpha) {
Hixie's avatar
Hixie committed
249 250 251
    assert(debugCanPaintChild(child));
    final Offset childOffset = childPosition.toOffset();
    if (!child.needsCompositing) {
Adam Barth's avatar
Adam Barth committed
252
      canvas.saveLayer(bounds, _getPaintForAlpha(alpha));
Hixie's avatar
Hixie committed
253 254 255 256
      canvas.translate(childOffset.dx, childOffset.dy);
      insertChild(child, Offset.zero);
      canvas.restore();
    } else {
257 258 259 260
      OpacityLayer paintLayer = new OpacityLayer(
          offset: childOffset,
          bounds: bounds,
          alpha: alpha);
261
      _containerLayer.append(paintLayer);
262 263 264 265
      compositeChild(child, parentLayer: paintLayer);
    }
  }

Adam Barth's avatar
Adam Barth committed
266 267
  static Paint _getPaintForColorFilter(Color color, sky.TransferMode transferMode) {
    return new Paint()
268
      ..colorFilter = new sky.ColorFilter.mode(color, transferMode)
Adam Barth's avatar
Adam Barth committed
269 270 271
      ..isAntiAlias = false;
  }

272 273 274 275 276 277 278 279
  /// Paint a child with a color filter
  ///
  /// The color filter is constructed by combining the given color and the given
  /// transfer mode, as if they were passed to the [ColorFilter.mode] constructor.
  ///
  /// If the child needs compositing, the blending operation will be applied by
  /// a compositing layer. Otherwise, the blending operation will be applied by
  /// the canvas.
280 281 282 283 284 285 286 287
  void paintChildWithColorFilter(RenderObject child,
                                 Point childPosition,
                                 Rect bounds,
                                 Color color,
                                 sky.TransferMode transferMode) {
    assert(debugCanPaintChild(child));
    final Offset childOffset = childPosition.toOffset();
    if (!child.needsCompositing) {
Adam Barth's avatar
Adam Barth committed
288
      canvas.saveLayer(bounds, _getPaintForColorFilter(color, transferMode));
289 290 291 292 293 294 295 296 297
      canvas.translate(childOffset.dx, childOffset.dy);
      insertChild(child, Offset.zero);
      canvas.restore();
    } else {
      ColorFilterLayer paintLayer = new ColorFilterLayer(
          offset: childOffset,
          bounds: bounds,
          color: color,
          transferMode: transferMode);
298
      _containerLayer.append(paintLayer);
Hixie's avatar
Hixie committed
299 300 301 302
      compositeChild(child, parentLayer: paintLayer);
    }
  }

303 304 305 306
  /// Instructs the child to draw itself onto this context at the given offset
  ///
  /// Do not call directly. This function is visible so that it can be
  /// overridden in tests.
Hixie's avatar
Hixie committed
307 308 309 310
  void insertChild(RenderObject child, Offset offset) {
    child._paintWithContext(this, offset);
  }

311 312 313 314
  /// Instructs the child to paint itself into a new composited layer using this context
  ///
  /// Do not call directly. This function is visible so that it can be
  /// overridden in tests.
Hixie's avatar
Hixie committed
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
  void compositeChild(RenderObject child, { Offset childOffset: Offset.zero, ContainerLayer parentLayer }) {
    // This ends the current layer and starts a new layer for the
    // remainder of our rendering. It also creates a new layer for the
    // child, and inserts that layer into the given parentLayer, which
    // must either be our current layer's parent layer, or at least
    // must have our current layer's parent layer as an ancestor.
    final PictureLayer originalLayer = _currentLayer;
    assert(() {
      assert(parentLayer != null);
      assert(originalLayer != null);
      assert(originalLayer.parent != null);
      ContainerLayer ancestor = parentLayer;
      while (ancestor != null && ancestor != originalLayer.parent)
        ancestor = ancestor.parent;
      assert(ancestor == originalLayer.parent);
      assert(originalLayer.parent == _containerLayer);
      return true;
    });

    // End our current layer.
335 336
    endRecording();

Hixie's avatar
Hixie committed
337 338 339 340 341 342 343 344 345 346 347
    // Create a layer for our child, and paint the child into it.
    if (child.needsPaint || !child.hasLayer) {
      PaintingContext newContext = new PaintingContext.withOffset(childOffset, child.paintBounds);
      child._layer = newContext.containerLayer;
      child._paintWithContext(newContext, Offset.zero);
      newContext.endRecording();
    } else {
      assert(child._layer != null);
      child._layer.detach();
      child._layer.offset = childOffset;
    }
348
    parentLayer.append(child._layer);
349

Hixie's avatar
Hixie committed
350 351
    // Start a new layer for anything that remains of our own paint.
    _startRecording(originalLayer.paintBounds);
352
  }
353

354 355
}

356 357 358 359
/// An abstract set of layout constraints
///
/// Concrete layout models (such as box) will create concrete subclasses to
/// communicate layout constraints between parents and children.
360 361
abstract class Constraints {
  const Constraints();
362 363

  /// Whether there is exactly one size possible given these constraints
364 365 366
  bool get isTight;
}

367
typedef void RenderObjectVisitor(RenderObject child);
368
typedef void LayoutCallback(Constraints constraints);
369
typedef double ExtentCallback(Constraints constraints);
370

371 372 373 374
/// An object in the render tree
///
/// Render objects have a reference to their parent but do not commit to a model
/// for their children.
375 376 377 378
abstract class RenderObject extends AbstractNode implements HitTestTarget {

  // LAYOUT

379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
  /// Data for use by the parent render object
  ///
  /// The parent data is used by the render object that lays out this object
  /// (typically this object's parent in the render tree) to store information
  /// relevant to itself and to any other nodes who happen to know exactly what
  /// the data means. The parent data is opaque to the child.
  ///
  /// - The parent data object must inherit from [ParentData] (despite being
  ///   typed as `dynamic`).
  /// - The parent data field must not be directly set, except by calling
  ///   [setupParentData] on the parent node.
  /// - The parent data can be set before the child is added to the parent, by
  ///   calling [setupParentData] on the future parent node.
  /// - The conventions for using the parent data depend on the layout protocol
  ///   used between the parent and child. For example, in box layout, the
  ///   parent data is completely opaque but in sector layout the child is
  ///   permitted to read some fields of the parent data.
396
  dynamic parentData; // TODO(ianh): change the type of this back to ParentData once the analyzer is cleverer
397 398 399 400 401

  /// Override to setup parent data correctly for your children
  ///
  /// You can call this function to set up the parent data for child before the
  /// child is added to the parent's child list.
402 403 404 405 406 407
  void setupParentData(RenderObject child) {
    assert(debugCanPerformMutations);
    if (child.parentData is! ParentData)
      child.parentData = new ParentData();
  }

408 409 410 411 412
  /// Called by subclases when they decide a render object is a child
  ///
  /// Only for use by subclasses when changing their child lists. Calling this
  /// in other cases will lead to an inconsistent tree and probably cause crashes.
  void adoptChild(RenderObject child) {
413 414 415 416 417
    assert(debugCanPerformMutations);
    assert(child != null);
    setupParentData(child);
    super.adoptChild(child);
    markNeedsLayout();
418
    _markNeedsCompositingBitsUpdate();
419
  }
420 421 422 423 424 425

  /// Called by subclases when they decide a render object is no longer a child
  ///
  /// Only for use by subclasses when changing their child lists. Calling this
  /// in other cases will lead to an inconsistent tree and probably cause crashes.
  void dropChild(RenderObject child) {
426 427 428 429 430 431 432
    assert(debugCanPerformMutations);
    assert(child != null);
    assert(child.parentData != null);
    child._cleanRelayoutSubtreeRoot();
    child.parentData.detach();
    super.dropChild(child);
    markNeedsLayout();
433
    _markNeedsCompositingBitsUpdate();
434 435
  }

436 437 438
  /// Calls visitor for each immediate child of this render object
  ///
  /// Override in subclasses with children and call the visitor for each child
439 440
  void visitChildren(RenderObjectVisitor visitor) { }

441
  dynamic debugExceptionContext = '';
442 443 444 445 446 447 448 449 450 451
  void _debugReportException(String method, dynamic exception, StackTrace stack) {
    print('-- EXCEPTION --');
    print('The following exception was raised during ${method}():');
    print('$exception');
    print('Stack trace:');
    print('$stack');
    'The following RenderObject was being processed when the exception was fired (limited to $debugRenderObjectDumpMaxLength lines):\n${this}'
      .split('\n').take(debugRenderObjectDumpMaxLength+1).forEach(print);
    if (debugExceptionContext != '')
      'The RenderObject had the following exception context:\n${debugExceptionContext}'.split('\n').forEach(print);
452 453
  }

454 455 456 457 458 459 460 461 462 463 464 465 466 467
  static bool _debugDoingLayout = false;
  static bool get debugDoingLayout => _debugDoingLayout;
  bool _debugDoingThisResize = false;
  bool get debugDoingThisResize => _debugDoingThisResize;
  bool _debugDoingThisLayout = false;
  bool get debugDoingThisLayout => _debugDoingThisLayout;
  static RenderObject _debugActiveLayout = null;
  static RenderObject get debugActiveLayout => _debugActiveLayout;
  bool _debugMutationsLocked = false;
  bool _debugCanParentUseSize;
  bool get debugCanParentUseSize => _debugCanParentUseSize;
  bool get debugCanPerformMutations {
    RenderObject node = this;
    while (true) {
468
      if (node._doingThisLayoutWithCallback)
469 470 471 472 473 474 475 476 477 478 479
        return true;
      if (node._debugMutationsLocked)
        return false;
      if (node.parent is! RenderObject)
        return true;
      node = node.parent;
    }
  }

  static List<RenderObject> _nodesNeedingLayout = new List<RenderObject>();
  bool _needsLayout = true;
480
  /// Whether this render object's layout information is dirty
481 482
  bool get needsLayout => _needsLayout;
  RenderObject _relayoutSubtreeRoot;
483
  bool _doingThisLayoutWithCallback = false;
484
  Constraints _constraints;
485
  /// The layout constraints most recently supplied by the parent
486
  Constraints get constraints => _constraints;
487 488
  /// Override this function in a subclass to verify that your state matches the constraints object
  bool debugDoesMeetConstraints();
489 490 491 492 493 494 495 496
  bool debugAncestorsAlreadyMarkedNeedsLayout() {
    if (_relayoutSubtreeRoot == null)
      return true; // we haven't yet done layout even once, so there's nothing for us to do
    RenderObject node = this;
    while (node != _relayoutSubtreeRoot) {
      assert(node._relayoutSubtreeRoot == _relayoutSubtreeRoot);
      assert(node.parent != null);
      node = node.parent as RenderObject;
497
      if ((!node._needsLayout) && (!node._debugDoingThisLayout))
498 499 500 501 502
        return false;
    }
    assert(node._relayoutSubtreeRoot == node);
    return true;
  }
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517

  /// Mark this render object's layout information as dirty
  ///
  /// Rather than eagerly updating layout information in response to writes into
  /// this render object, we instead mark the layout information as dirty, which
  /// schedules a visual update. As part of the visual update, the rendering
  /// pipeline will update this render object's layout information.
  ///
  /// This mechanism batches the layout work so that multiple sequential writes
  /// are coalesced, removing redundant computation.
  ///
  /// Causes [needsLayout] to return true for this render object. If the parent
  /// render object indicated that it uses the size of this render object in
  /// computing its layout information, this function will also mark the parent
  /// as needing layout.
518 519 520 521 522 523 524 525 526 527 528
  void markNeedsLayout() {
    assert(debugCanPerformMutations);
    if (_needsLayout) {
      assert(debugAncestorsAlreadyMarkedNeedsLayout());
      return;
    }
    _needsLayout = true;
    assert(_relayoutSubtreeRoot != null);
    if (_relayoutSubtreeRoot != this) {
      final parent = this.parent; // TODO(ianh): Remove this once the analyzer is cleverer
      assert(parent is RenderObject);
529 530 531 532 533
      if (!_doingThisLayoutWithCallback) {
        parent.markNeedsLayout();
      } else {
        assert(parent._debugDoingThisLayout);
      }
534 535 536 537 538 539
      assert(parent == this.parent); // TODO(ianh): Remove this once the analyzer is cleverer
    } else {
      _nodesNeedingLayout.add(this);
      scheduler.ensureVisualUpdate();
    }
  }
540

541 542 543 544
  void _cleanRelayoutSubtreeRoot() {
    if (_relayoutSubtreeRoot != this) {
      _relayoutSubtreeRoot = null;
      _needsLayout = true;
545 546 547
      visitChildren((RenderObject child) {
        child._cleanRelayoutSubtreeRoot();
      });
548 549
    }
  }
550 551 552 553 554 555 556

  /// Bootstrap the rendering pipeline by scheduling the very first layout
  ///
  /// Requires this render object to be attached and that this render object
  /// is the root of the render tree.
  ///
  /// See [RenderView] for an example of how this function is used.
557 558
  void scheduleInitialLayout() {
    assert(attached);
Hixie's avatar
Hixie committed
559
    assert(parent is! RenderObject);
560
    assert(!_debugDoingLayout);
561 562
    assert(_relayoutSubtreeRoot == null);
    _relayoutSubtreeRoot = this;
563 564 565 566
    assert(() {
      _debugCanParentUseSize = false;
      return true;
    });
567 568
    _nodesNeedingLayout.add(this);
  }
569 570 571 572 573 574 575 576

  /// Update the layout information for all dirty render objects
  ///
  /// This function is one of the core stages of the rendering pipeline. Layout
  /// information is cleaned prior to painting so that render objects will
  /// appear on screen in their up-to-date locations.
  ///
  /// See [SkyBinding] for an example of how this function is used.
577 578 579 580
  static void flushLayout() {
    sky.tracing.begin('RenderObject.flushLayout');
    _debugDoingLayout = true;
    try {
581 582 583 584 585 586
      // TODO(ianh): assert that we're not allowing previously dirty nodes to redirty themeselves
      while(_nodesNeedingLayout.isNotEmpty) {
        List<RenderObject> dirtyNodes = _nodesNeedingLayout;
        _nodesNeedingLayout = new List<RenderObject>();
        dirtyNodes..sort((a, b) => a.depth - b.depth)..forEach((node) {
          if (node._needsLayout && node.attached)
587
            node._layoutWithoutResize();
588 589
        });
      }
590 591 592 593 594
    } finally {
      _debugDoingLayout = false;
      sky.tracing.end('RenderObject.flushLayout');
    }
  }
595
  void _layoutWithoutResize() {
596 597 598 599 600 601 602 603 604 605 606 607
    assert(_relayoutSubtreeRoot == this);
    RenderObject debugPreviousActiveLayout;
    assert(!_debugMutationsLocked);
    assert(!_doingThisLayoutWithCallback);
    assert(_debugCanParentUseSize != null);
    assert(() {
      _debugMutationsLocked = true;
      _debugDoingThisLayout = true;
      debugPreviousActiveLayout = _debugActiveLayout;
      _debugActiveLayout = this;
      return true;
    });
608 609
    try {
      performLayout();
610 611
    } catch (e, stack) {
      _debugReportException('performLayout', e, stack);
612
    }
613 614 615 616 617 618
    assert(() {
      _debugActiveLayout = debugPreviousActiveLayout;
      _debugDoingThisLayout = false;
      _debugMutationsLocked = false;
      return true;
    });
619 620 621
    _needsLayout = false;
    markNeedsPaint();
  }
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644

  /// Compute the layout for this render object
  ///
  /// This function is the main entry point for parents to ask their children to
  /// update their layout information. The parent passes a constraints object,
  /// which informs the child as which layouts are permissible. The child is
  /// required to obey the given constraints.
  ///
  /// If the parent reads information computed during the child's layout, the
  /// parent must pass true for parentUsesSize. In that case, the parent will be
  /// marked as needing layout whenever the child is marked as needing layout
  /// because the parent's layout information depends on the child's layout
  /// information. If the parent uses the default value (false) for
  /// parentUsesSize, the child can change its layout information (subject to
  /// the given constraints) without informing the parent.
  ///
  /// Subclasses should not override layout directly. Instead, they should
  /// override performResize and/or performLayout.
  ///
  /// The parent's performLayout method should call the layout of all its
  /// children unconditionally. It is the layout functions's responsibility (as
  /// implemented here) to return early if the child does not need to do any
  /// work to update its layout information.
645 646 647 648 649 650 651 652 653 654 655 656 657
  void layout(Constraints constraints, { bool parentUsesSize: false }) {
    final parent = this.parent; // TODO(ianh): Remove this once the analyzer is cleverer
    RenderObject relayoutSubtreeRoot;
    if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject)
      relayoutSubtreeRoot = this;
    else
      relayoutSubtreeRoot = parent._relayoutSubtreeRoot;
    assert(parent == this.parent); // TODO(ianh): Remove this once the analyzer is cleverer
    if (!needsLayout && constraints == _constraints && relayoutSubtreeRoot == _relayoutSubtreeRoot)
      return;
    _constraints = constraints;
    _relayoutSubtreeRoot = relayoutSubtreeRoot;
    assert(!_debugMutationsLocked);
658
    assert(!_doingThisLayoutWithCallback);
659 660 661 662 663 664 665
    assert(() {
      _debugMutationsLocked = true;
      _debugCanParentUseSize = parentUsesSize;
      return true;
    });
    if (sizedByParent) {
      assert(() { _debugDoingThisResize = true; return true; });
666 667 668 669 670 671
      try {
        performResize();
        assert(debugDoesMeetConstraints());
      } catch (e, stack) {
        _debugReportException('performResize', e, stack);
      }
672 673 674 675 676 677 678 679 680
      assert(() { _debugDoingThisResize = false; return true; });
    }
    RenderObject debugPreviousActiveLayout;
    assert(() {
      _debugDoingThisLayout = true;
      debugPreviousActiveLayout = _debugActiveLayout;
      _debugActiveLayout = this;
      return true;
    });
681 682 683
    try {
      performLayout();
      assert(debugDoesMeetConstraints());
684 685
    } catch (e, stack) {
      _debugReportException('performLayout', e, stack);
686
    }
687 688 689 690 691 692
    assert(() {
      _debugActiveLayout = debugPreviousActiveLayout;
      _debugDoingThisLayout = false;
      _debugMutationsLocked = false;
      return true;
    });
693 694 695 696
    _needsLayout = false;
    markNeedsPaint();
    assert(parent == this.parent); // TODO(ianh): Remove this once the analyzer is cleverer
  }
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737

  /// Whether the constraints are the only input to the sizing algorithm (in
  /// particular, child nodes have no impact)
  ///
  /// Returning false is always correct, but returning true can be more
  /// efficient when computing the size of this render object because we don't
  /// need to recompute the size if the constraints don't change.
  bool get sizedByParent => false;

  /// Updates the render objects size using only the constraints
  ///
  /// Do not call this function directly: call [layout] instead. This function
  /// is called by [layout] when there is actually work to be done by this
  /// render object during layout. The layout constraints provided by your
  /// parent are available via the [constraints] getter.
  ///
  /// Subclasses that set [sizedByParent] to true should override this function
  /// to compute their size.
  ///
  /// Note: This function is called only if [sizedByParent] is true.
  void performResize();

  /// Do the work of computing the layout for this render object
  ///
  /// Do not call this function directly: call [layout] instead. This function
  /// is called by [layout] when there is actually work to be done by this
  /// render object during layout. The layout constraints provided by your
  /// parent are available via the [constraints] getter.
  ///
  /// If [sizedByParent] is true, then this function should not actually change
  /// the dimensions of this render object. Instead, that work should be done by
  /// [performResize]. If [sizedByParent] is false, then this function should
  /// both change the dimensions of this render object and instruct its children
  /// to layout.
  ///
  /// In implementing this function, you must call [layout] on each of your
  /// children, passing true for parentUsesSize if your layout information is
  /// dependent on your child's layout information. Passing true for
  /// parentUsesSize ensures that this render object will undergo layout if the
  /// child undergoes layout. Otherwise, the child can changes its layout
  /// information without informing this render object.
738
  void performLayout();
739 740 741

  /// Allows this render object to mutation its child list during layout and
  /// invokes callback
742 743 744
  void invokeLayoutCallback(LayoutCallback callback) {
    assert(_debugMutationsLocked);
    assert(_debugDoingThisLayout);
745 746 747 748 749 750 751
    assert(!_doingThisLayoutWithCallback);
    _doingThisLayoutWithCallback = true;
    try {
      callback(constraints);
    } finally {
      _doingThisLayoutWithCallback = false;
    }
752 753
  }

754 755 756 757 758 759 760
  /// Rotate this render object (not yet implemented)
  void rotate({
    int oldAngle, // 0..3
    int newAngle, // 0..3
    Duration time
  }) { }

761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
  // 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.


  // PAINTING

  static bool _debugDoingPaint = false;
  static bool get debugDoingPaint => _debugDoingPaint;
  static void set debugDoingPaint(bool value) {
    _debugDoingPaint = value;
  }
  bool _debugDoingThisPaint = false;
  bool get debugDoingThisPaint => _debugDoingThisPaint;
  static RenderObject _debugActivePaint = null;
  static RenderObject get debugActivePaint => _debugActivePaint;

  static List<RenderObject> _nodesNeedingPaint = new List<RenderObject>();

786 787 788 789 790 791 792
  /// Whether this render object paints using a composited layer
  ///
  /// Override this in subclasses to indicate that instances of your class need
  /// to have their own compositing layer. For example, videos should return
  /// true if they use hardware decoders.
  ///
  /// Note: This getter must not change value over the lifetime of this object.
Hixie's avatar
Hixie committed
793
  bool get hasLayer => false;
794

Hixie's avatar
Hixie committed
795
  ContainerLayer _layer;
796 797 798
  /// The compositing layer that this render object uses to paint
  ///
  /// Call only when [hasLayer] is true.
Hixie's avatar
Hixie committed
799 800 801 802
  ContainerLayer get layer {
    assert(hasLayer);
    assert(!_needsPaint);
    return _layer;
803 804
  }

Hixie's avatar
Hixie committed
805
  bool _needsCompositingBitsUpdate = true;
806 807 808 809 810 811 812
  /// Mark the compositing state for this render object as dirty
  ///
  /// When the subtree is mutated, we need to recompute our [needsCompositing]
  /// bit, and our ancestors need to do the same (in case ours changed).
  /// Therefore, [adoptChild] and [dropChild] call
  /// [markNeedsCompositingBitsUpdate].
  void _markNeedsCompositingBitsUpdate() {
Hixie's avatar
Hixie committed
813
    if (_needsCompositingBitsUpdate)
814
      return;
Hixie's avatar
Hixie committed
815 816
    _needsCompositingBitsUpdate = true;
    final AbstractNode parent = this.parent; // TODO(ianh): remove the once the analyzer is cleverer
817
    if (parent is RenderObject)
818
      parent._markNeedsCompositingBitsUpdate();
819
  }
Hixie's avatar
Hixie committed
820
  bool _needsCompositing = false;
821 822 823 824
  /// Whether we or one of our descendants has a compositing layer
  ///
  /// Only legal to call after [flushLayout] and [updateCompositingBits] have
  /// been called.
Hixie's avatar
Hixie committed
825 826 827 828
  bool get needsCompositing {
    assert(!_needsCompositingBitsUpdate); // make sure we don't use this bit when it is dirty
    return _needsCompositing;
  }
829 830 831 832 833

  /// Updates the [needsCompositing] bits
  ///
  /// Called as part of the rendering pipeline after [flushLayout] and before
  /// [flushPaint].
Hixie's avatar
Hixie committed
834 835
  void updateCompositingBits() {
    if (!_needsCompositingBitsUpdate)
836
      return;
Hixie's avatar
Hixie committed
837
    bool didHaveCompositedDescendant = _needsCompositing;
838
    visitChildren((RenderObject child) {
Hixie's avatar
Hixie committed
839 840 841
      child.updateCompositingBits();
      if (child.needsCompositing)
        _needsCompositing = true;
842
    });
Hixie's avatar
Hixie committed
843 844 845 846 847
    if (hasLayer)
      _needsCompositing = true;
    if (didHaveCompositedDescendant != _needsCompositing)
      markNeedsPaint();
    _needsCompositingBitsUpdate = false;
848 849 850
  }

  bool _needsPaint = true;
851
  /// The visual appearance of this render object has changed since it last painted
852
  bool get needsPaint => _needsPaint;
853 854 855 856 857 858 859 860 861 862 863

  /// Mark this render object as having changed its visual appearance
  ///
  /// Rather than eagerly updating this render object's display list
  /// in response to writes, we instead mark the the render object as needing to
  /// paint, which schedules a visual update. As part of the visual update, the
  /// rendering pipeline will give this render object an opportunity to update
  /// its display list.
  ///
  /// This mechanism batches the painting work so that multiple sequential
  /// writes are coalesced, removing redundant computation.
864 865 866 867
  void markNeedsPaint() {
    assert(!debugDoingPaint);
    if (!attached) return; // Don't try painting things that aren't in the hierarchy
    if (_needsPaint) return;
Hixie's avatar
Hixie committed
868 869 870 871
    if (hasLayer) {
      // If we always have our own layer, then we can just repaint
      // ourselves without involving any other nodes.
      assert(_layer != null);
872 873 874
      _needsPaint = true;
      _nodesNeedingPaint.add(this);
      scheduler.ensureVisualUpdate();
Hixie's avatar
Hixie committed
875 876 877 878 879 880 881 882 883 884 885
    } else if (parent is RenderObject) {
      // We don't have our own layer; one of our ancestors will take
      // care of updating the layer we're in and when they do that
      // we'll get our paint() method called.
      assert(_layer == null);
      (parent as RenderObject).markNeedsPaint(); // TODO(ianh): remove the cast once the analyzer is cleverer
    } else {
      // If we're the root of the render tree (probably a RenderView),
      // then we have to paint ourselves, since nobody else can paint
      // us. We don't add ourselves to _nodesNeedingPaint in this
      // case, because the root is always told to paint regardless.
Adam Barth's avatar
Adam Barth committed
886 887
      _needsPaint = true;
      scheduler.ensureVisualUpdate();
888 889
    }
  }
890 891 892 893 894 895 896 897

  /// Update the display lists for all render objects
  ///
  /// This function is one of the core stages of the rendering pipeline.
  /// Painting occurs after layout and before the scene is recomposited so that
  /// scene is composited with up-to-date display lists for every render object.
  ///
  /// See [SkyBinding] for an example of how this function is used.
898 899 900 901 902 903
  static void flushPaint() {
    sky.tracing.begin('RenderObject.flushPaint');
    _debugDoingPaint = true;
    try {
      List<RenderObject> dirtyNodes = _nodesNeedingPaint;
      _nodesNeedingPaint = new List<RenderObject>();
Hixie's avatar
Hixie committed
904 905 906 907
      // Sort the dirty nodes in reverse order (deepest first).
      for (RenderObject node in dirtyNodes..sort((a, b) => b.depth - a.depth)) {
        assert(node._needsPaint);
        if (node.attached)
908
          node._repaint();
909 910 911 912 913 914 915
      };
      assert(_nodesNeedingPaint.length == 0);
    } finally {
      _debugDoingPaint = false;
      sky.tracing.end('RenderObject.flushPaint');
    }
  }
916 917 918 919 920 921 922

  /// Bootstrap the rendering pipeline by scheduling the very first paint
  ///
  /// Requires that this render object is attached, is the root of the render
  /// tree, and has a composited layer.
  ///
  /// See [RenderView] for an example of how this function is used.
923
  void scheduleInitialPaint(ContainerLayer rootLayer) {
Hixie's avatar
Hixie committed
924 925 926 927
    assert(attached);
    assert(parent is! RenderObject);
    assert(!_debugDoingPaint);
    assert(hasLayer);
928
    assert(_layer == null);
929 930 931
    _layer = rootLayer;
    assert(_needsPaint);
    _nodesNeedingPaint.add(this);
Hixie's avatar
Hixie committed
932
  }
933
  void _repaint() {
Hixie's avatar
Hixie committed
934
    assert(hasLayer);
935
    assert(_layer != null);
936 937
    _layer.removeAllChildren();
    PaintingContext context = new PaintingContext.withLayer(_layer, paintBounds);
Hixie's avatar
Hixie committed
938
    _layer = context._containerLayer;
939 940
    _paintWithContext(context, Offset.zero);
    context.endRecording();
941
  }
942
  void _paintWithContext(PaintingContext context, Offset offset) {
943
    assert(!_debugDoingThisPaint);
Hixie's avatar
Hixie committed
944 945
    assert(!_needsLayout);
    assert(!_needsCompositingBitsUpdate);
946 947 948 949 950
    RenderObject debugLastActivePaint;
    assert(() {
      _debugDoingThisPaint = true;
      debugLastActivePaint = _debugActivePaint;
      _debugActivePaint = this;
951
      debugPaint(context, offset);
952
      if (debugPaintBoundsEnabled) {
953 954
        context.canvas.save();
        context.canvas.clipRect(paintBounds.shift(offset));
955
      }
Hixie's avatar
Hixie committed
956
      assert(!hasLayer || _layer != null);
957 958
      return true;
    });
Hixie's avatar
Hixie committed
959
    _needsPaint = false;
960 961 962 963 964 965 966
    try {
      paint(context, offset);
      assert(!_needsLayout); // check that the paint() method didn't mark us dirty again
      assert(!_needsPaint); // check that the paint() method didn't mark us dirty again
    } catch (e, stack) {
      _debugReportException('paint', e, stack);
    }
967 968
    assert(() {
      if (debugPaintBoundsEnabled)
969
        context.canvas.restore();
970 971 972 973 974 975
      _debugActivePaint = debugLastActivePaint;
      _debugDoingThisPaint = false;
      return true;
    });
  }

976 977 978 979 980 981 982 983
  /// The bounds within which this render object will paint
  ///
  /// A render object is permitted to paint outside the region it occupies
  /// during layout but is not permitted to paint outside these paints bounds.
  /// These paint bounds are used to construct memory-efficient composited
  /// layers, which means attempting to paint outside these bounds can attempt
  /// to write to pixels that do not exist in this render object's composited
  /// layer.
984
  Rect get paintBounds;
985 986

  /// Override this function to paint debugging information
987
  void debugPaint(PaintingContext context, Offset offset) { }
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005

  /// Paint this render object into the given context at the given offset
  ///
  /// Subclasses should override this function to provide a visual appearance
  /// for themselves. The render object's local coordinate system is
  /// axis-aligned with the coordinate system of the context's canvas and the
  /// render object's local origin (i.e, x=0 and y=0) is placed at the given
  /// offset in the context's canvas.
  ///
  /// Do not call this function directly. If you wish to paint yourself, call
  /// [markNeedsPaint] instead to schedule a call to this function. If you wish
  /// to paint one of your children, call one of the paint child functions on
  /// the given context, such as [paintChild] or [paintChildWithClipRect].
  ///
  /// When painting one of your children (via a paint child function on the
  /// given context), the current canvas held by the context might change
  /// because draw operations before and after painting children might need to
  /// be recorded on separate compositing layers.
1006
  void paint(PaintingContext context, Offset offset) { }
1007

1008 1009 1010 1011 1012
  /// If this render object applies a transform before painting, apply that
  /// transform to the given matrix
  ///
  /// Used by coordinate conversion functions to translate coordiantes local to
  /// one render object into coordinates local to another render object.
1013 1014
  void applyPaintTransform(Matrix4 transform) { }

1015 1016 1017

  // EVENTS

1018
  /// Override this function to handle events that hit this render object
1019 1020
  EventDisposition handleEvent(sky.Event event, HitTestEntry entry) {
    return EventDisposition.ignored;
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
  }


  // HIT TESTING

  // RenderObject 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, { 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.


  String toString([String prefix = '']) {
    RenderObject debugPreviousActiveLayout = _debugActiveLayout;
    _debugActiveLayout = null;
1047 1048 1049 1050 1051 1052
    String header = toStringName();
    prefix += '  ';
    String result = '${header}\n${debugDescribeSettings(prefix)}${debugDescribeChildren(prefix)}';
    _debugActiveLayout = debugPreviousActiveLayout;
    return result;
  }
1053 1054

  /// Returns a human understandable name
1055
  String toStringName() {
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
    String header = '${runtimeType}';
    if (_relayoutSubtreeRoot != null && _relayoutSubtreeRoot != this) {
      int count = 1;
      RenderObject target = parent;
      while (target != null && target != _relayoutSubtreeRoot) {
        target = target.parent as RenderObject;
        count += 1;
      }
      header += ' relayoutSubtreeRoot=up$count';
    }
    if (_needsLayout)
      header += ' NEEDS-LAYOUT';
    if (!attached)
      header += ' DETACHED';
1070
    return header;
1071
  }
1072

1073 1074 1075 1076 1077
  String debugDescribeSettings(String prefix) => '${prefix}parentData: ${parentData}\n${prefix}constraints: ${constraints}\n';
  String debugDescribeChildren(String prefix) => '';

}

1078
/// Obsolete function that will be removed eventually
1079 1080 1081 1082 1083 1084 1085 1086
double clamp({ double min: 0.0, double value: 0.0, double max: double.INFINITY }) {
  assert(min != null);
  assert(value != null);
  assert(max != null);
  return math.max(min, math.min(max, value));
}


1087 1088 1089
/// Generic mixin for render objects with one child
///
/// Provides a child model for a render object subclass that has a unique child
1090 1091
abstract class RenderObjectWithChildMixin<ChildType extends RenderObject> implements RenderObject {
  ChildType _child;
1092
  /// The render object's unique child
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
  ChildType get child => _child;
  void set child (ChildType value) {
    if (_child != null)
      dropChild(_child);
    _child = value;
    if (_child != null)
      adoptChild(_child);
  }
  void attachChildren() {
    if (_child != null)
      _child.attach();
  }
  void detachChildren() {
    if (_child != null)
      _child.detach();
  }
1109
  void visitChildren(RenderObjectVisitor visitor) {
1110
    if (_child != null)
1111
      visitor(_child);
1112 1113 1114 1115 1116 1117 1118 1119
  }
  String debugDescribeChildren(String prefix) {
    if (child != null)
      return '${prefix}child: ${child.toString(prefix)}';
    return '';
  }
}

1120
/// Parent data to support a doubly-linked list of children
1121
abstract class ContainerParentDataMixin<ChildType extends RenderObject> {
1122
  /// The previous sibling in the parent's child list
1123
  ChildType previousSibling;
1124
  /// The next sibling in the parent's child list
1125
  ChildType nextSibling;
1126 1127

  /// Clear the sibling pointers.
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
  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;
  }
}

1146 1147 1148 1149
/// Generic mixin for render objects with a list of children
///
/// Provides a child model for a render object subclass that has a doubly-linked
/// list of children.
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, ParentDataType extends ContainerParentDataMixin<ChildType>> implements RenderObject {

  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;
  }

  int _childCount = 0;
1172
  /// The number of children
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
  int get childCount => _childCount;

  ChildType _firstChild;
  ChildType _lastChild;
  void _addToChildList(ChildType child, { ChildType before }) {
    assert(child.parentData is ParentDataType);
    assert(child.parentData.nextSibling == null);
    assert(child.parentData.previousSibling == null);
    _childCount += 1;
    assert(_childCount > 0);
    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);
      }
    }
  }
1219 1220 1221
  /// Insert child into this render object's child list before the given child
  ///
  /// To insert a child at the end of the child list, omit the before parameter.
1222 1223 1224 1225 1226 1227 1228 1229 1230
  void add(ChildType child, { ChildType before }) {
    assert(child != this);
    assert(before != this);
    assert(child != before);
    assert(child != _firstChild);
    assert(child != _lastChild);
    adoptChild(child);
    _addToChildList(child, before: before);
  }
1231 1232

  /// Add all the children to the end of this render object's child list
1233
  void addAll(List<ChildType> children) {
1234 1235 1236 1237
    if (children != null)
      for (ChildType child in children)
        add(child);
  }
1238

1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
  void _removeFromChildList(ChildType child) {
    assert(child.parentData is ParentDataType);
    assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild));
    assert(_debugUltimateNextSiblingOf(child, equals: _lastChild));
    assert(_childCount >= 0);
    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;
    _childCount -= 1;
  }
1262 1263 1264 1265

  /// Remove this child from the child list
  ///
  /// Requires the child to be present in the child list.
1266 1267 1268 1269
  void remove(ChildType child) {
    _removeFromChildList(child);
    dropChild(child);
  }
1270 1271 1272 1273

  /// Remove all their children from this render object's child list
  ///
  /// More efficient than removing them individually.
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
  void removeAll() {
    ChildType child = _firstChild;
    while (child != null) {
      assert(child.parentData is ParentDataType);
      ChildType next = child.parentData.nextSibling;
      child.parentData.previousSibling = null;
      child.parentData.nextSibling = null;
      dropChild(child);
      child = next;
    }
    _firstChild = null;
    _lastChild = null;
    _childCount = 0;
  }
1288 1289 1290 1291 1292 1293

  /// Move this child in the child list to be before the given child
  ///
  /// More efficient than removing and re-adding the child. Requires the child
  /// to already be in the child list at some position. Pass null for before to
  /// move the child to the end of the child list.
1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328
  void move(ChildType child, { ChildType before }) {
    assert(child != this);
    assert(before != this);
    assert(child != before);
    assert(child.parent == this);
    assert(child.parentData is ParentDataType);
    if (child.parentData.nextSibling == before)
      return;
    _removeFromChildList(child);
    _addToChildList(child, before: before);
  }
  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;
    }
  }
1329
  void visitChildren(RenderObjectVisitor visitor) {
1330 1331
    ChildType child = _firstChild;
    while (child != null) {
1332
      visitor(child);
1333 1334 1335 1336 1337
      assert(child.parentData is ParentDataType);
      child = child.parentData.nextSibling;
    }
  }

1338
  /// The first child in the child list
1339
  ChildType get firstChild => _firstChild;
1340 1341

  /// The last child in the child list
1342
  ChildType get lastChild => _lastChild;
1343 1344

  /// The next child after the given child in the child list
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
  ChildType childAfter(ChildType child) {
    assert(child.parentData is ParentDataType);
    return child.parentData.nextSibling;
  }

  String debugDescribeChildren(String prefix) {
    String result = '';
    int count = 1;
    ChildType child = _firstChild;
    while (child != null) {
      result += '${prefix}child ${count}: ${child.toString(prefix)}';
      count += 1;
      child = child.parentData.nextSibling;
    }
    return result;
  }
}