object.dart 108 KB
Newer Older
1 2 3 4
// 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.

5
import 'dart:developer';
6
import 'dart:ui' as ui show ImageFilter, PictureRecorder;
7

8
import 'package:flutter/foundation.dart';
9
import 'package:flutter/gestures.dart';
Hixie's avatar
Hixie committed
10
import 'package:flutter/painting.dart';
11
import 'package:flutter/scheduler.dart';
12

13
import 'package:vector_math/vector_math_64.dart';
14

15 16 17
import 'debug.dart';
import 'layer.dart';
import 'node.dart';
Hixie's avatar
Hixie committed
18 19
import 'semantics.dart';
import 'binding.dart';
20

21
export 'package:flutter/foundation.dart' show FlutterError, InformationCollector;
Ian Hickson's avatar
Ian Hickson committed
22
export 'package:flutter/gestures.dart' show HitTestEntry, HitTestResult;
23
export 'package:flutter/painting.dart';
24

25
/// Base class for data associated with a [RenderObject] by its parent.
26 27 28 29
///
/// 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.
30
class ParentData {
31
  /// Called when the RenderObject is removed from the tree.
32
  @protected
33
  @mustCallSuper
34
  void detach() { }
35

36
  @override
37 38 39
  String toString() => '<none>';
}

40 41 42 43
/// Signature for painting into a [PaintingContext].
///
/// The `offset` argument is the offset from the origin of the coordinate system
/// of the [PaintingContext.canvas] to the coordinate system of the callee.
44 45
///
/// Used by many of the methods of [PaintingContext].
Adam Barth's avatar
Adam Barth committed
46 47 48
typedef void PaintingContextCallback(PaintingContext context, Offset offset);

/// A place to paint.
49 50 51 52 53 54 55 56 57 58 59
///
/// 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.
60
class PaintingContext {
61 62 63 64
  PaintingContext._(this._containerLayer, this._paintBounds) {
    assert(_containerLayer != null);
    assert(_paintBounds != null);
  }
Adam Barth's avatar
Adam Barth committed
65 66 67 68 69 70 71 72 73

  final ContainerLayer _containerLayer;
  final Rect _paintBounds;

  /// Repaint the given render object.
  ///
  /// The render object must have a composited layer and must be in need of
  /// painting. The render object's layer is re-used, along with any layers in
  /// the subtree that don't need to be repainted.
74
  static void repaintCompositedChild(RenderObject child, { bool debugAlsoPaintedParent: false }) {
75
    assert(child.isRepaintBoundary);
76
    assert(child._needsPaint);
77 78 79 80
    assert(() {
      child.debugRegisterRepaintBoundaryPaint(includedParent: debugAlsoPaintedParent, includedChild: true);
      return true;
    });
81
    child._layer ??= new OffsetLayer();
Adam Barth's avatar
Adam Barth committed
82
    child._layer.removeAllChildren();
83
    assert(() {
84
      child._layer.debugCreator = child.debugCreator ?? child.runtimeType;
85 86
      return true;
    });
Adam Barth's avatar
Adam Barth committed
87 88 89 90 91 92 93 94 95 96 97
    PaintingContext childContext = new PaintingContext._(child._layer, child.paintBounds);
    child._paintWithContext(childContext, Offset.zero);
    childContext._stopRecordingIfNeeded();
  }

  /// Paint a child render object.
  ///
  /// If the child has its own composited layer, the child will be composited
  /// into the layer subtree associated with this painting context. Otherwise,
  /// the child will be painted into the current PictureLayer for this context.
  void paintChild(RenderObject child, Offset offset) {
98 99 100 101 102 103
    assert(() {
      if (debugProfilePaintsEnabled)
        Timeline.startSync('${child.runtimeType}');
      return true;
    });

104
    if (child.isRepaintBoundary) {
Adam Barth's avatar
Adam Barth committed
105 106 107 108 109
      _stopRecordingIfNeeded();
      _compositeChild(child, offset);
    } else {
      child._paintWithContext(this, offset);
    }
110 111 112 113 114 115

    assert(() {
      if (debugProfilePaintsEnabled)
        Timeline.finishSync();
      return true;
    });
116 117
  }

Adam Barth's avatar
Adam Barth committed
118 119
  void _compositeChild(RenderObject child, Offset offset) {
    assert(!_isRecording);
120
    assert(child.isRepaintBoundary);
121
    assert(_canvas == null || _canvas.getSaveCount() == 1);
Adam Barth's avatar
Adam Barth committed
122 123

    // Create a layer for our child, and paint the child into it.
124
    if (child._needsPaint) {
125
      repaintCompositedChild(child, debugAlsoPaintedParent: true);
Adam Barth's avatar
Adam Barth committed
126 127
    } else {
      assert(child._layer != null);
128 129
      assert(() {
        child.debugRegisterRepaintBoundaryPaint(includedParent: true, includedChild: false);
130
        child._layer.debugCreator = child.debugCreator ?? child.runtimeType;
131 132
        return true;
      });
Adam Barth's avatar
Adam Barth committed
133
    }
134 135
    child._layer.offset = offset;
    _appendLayer(child._layer);
Hixie's avatar
Hixie committed
136
  }
137

138
  void _appendLayer(Layer layer) {
Adam Barth's avatar
Adam Barth committed
139
    assert(!_isRecording);
140
    layer.detach();
Adam Barth's avatar
Adam Barth committed
141 142
    _containerLayer.append(layer);
  }
143

Adam Barth's avatar
Adam Barth committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
  bool get _isRecording {
    final bool hasCanvas = (_canvas != null);
    assert(() {
      if (hasCanvas) {
        assert(_currentLayer != null);
        assert(_recorder != null);
        assert(_canvas != null);
      } else {
        assert(_currentLayer == null);
        assert(_recorder == null);
        assert(_canvas == null);
      }
      return true;
    });
    return hasCanvas;
  }
Hixie's avatar
Hixie committed
160

Adam Barth's avatar
Adam Barth committed
161
  // Recording state
Hixie's avatar
Hixie committed
162
  PictureLayer _currentLayer;
163
  ui.PictureRecorder _recorder;
Adam Barth's avatar
Adam Barth committed
164
  Canvas _canvas;
Adam Barth's avatar
Adam Barth committed
165 166 167 168 169 170

  /// The canvas on which to paint.
  ///
  /// The current canvas can change whenever you paint a child using this
  /// context, which means it's fragile to hold a reference to the canvas
  /// returned by this getter.
Adam Barth's avatar
Adam Barth committed
171
  Canvas get canvas {
Adam Barth's avatar
Adam Barth committed
172 173 174 175 176 177 178
    if (_canvas == null)
      _startRecording();
    return _canvas;
  }

  void _startRecording() {
    assert(!_isRecording);
179
    _currentLayer = new PictureLayer();
180
    _recorder = new ui.PictureRecorder();
Adam Barth's avatar
Adam Barth committed
181
    _canvas = new Canvas(_recorder, _paintBounds);
182
    _containerLayer.append(_currentLayer);
183 184
  }

Adam Barth's avatar
Adam Barth committed
185 186 187
  void _stopRecordingIfNeeded() {
    if (!_isRecording)
      return;
188
    assert(() {
189 190 191 192 193 194 195
      if (debugRepaintRainbowEnabled) {
        Paint paint = new Paint()
          ..style = PaintingStyle.stroke
          ..strokeWidth = 6.0
          ..color = debugCurrentRepaintColor.toColor();
        canvas.drawRect(_paintBounds.deflate(3.0), paint);
      }
196 197
      if (debugPaintLayerBordersEnabled) {
        Paint paint = new Paint()
198
          ..style = PaintingStyle.stroke
199 200 201 202 203 204
          ..strokeWidth = 1.0
          ..color = debugPaintLayerBordersColor;
        canvas.drawRect(_paintBounds, paint);
      }
      return true;
    });
Hixie's avatar
Hixie committed
205 206
    _currentLayer.picture = _recorder.endRecording();
    _currentLayer = null;
207 208 209 210
    _recorder = null;
    _canvas = null;
  }

211
  static final Paint _defaultPaint = new Paint();
212

213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
  /// Hints that the painting in the current layer is complex and would benefit
  /// from caching.
  ///
  /// If this hint is not set, the compositor will apply its own heuristics to
  /// decide whether the current layer is complex enough to benefit from
  /// caching.
  void setIsComplexHint() {
    _currentLayer?.isComplexHint = true;
  }

  /// Hints that the painting in the current layer is likely to change next frame.
  ///
  /// This hint tells the compositor not to cache the current layer because the
  /// cache will not be used in the future. If this hint is not set, the
  /// compositor will apply its own heuristics to decide whether the current
  /// layer is likely to be reused in the future.
  void setWillChangeHint() {
    _currentLayer?.willChangeHint = true;
  }

233
  /// Adds a composited layer to the recording.
234
  ///
235 236
  /// After calling this function, the [canvas] property will change to refer to
  /// a new [Canvas] that draws on top of the given layer.
237
  ///
238
  /// A [RenderObject] that uses this function is very likely to require its
239
  /// [RenderObject.alwaysNeedsCompositing] property to return true. That informs
240 241 242
  /// ancestor render objects that this render object will include a composited
  /// layer, which causes them to use composited clips, for example.
  void addLayer(Layer layer) {
243
    _stopRecordingIfNeeded();
244
    _appendLayer(layer);
Adam Barth's avatar
Adam Barth committed
245 246
  }

247
  /// Clip further painting using a rectangle.
Adam Barth's avatar
Adam Barth committed
248
  ///
249 250 251 252 253 254 255 256
  /// * `needsCompositing` is whether the child needs compositing. Typically
  ///   matches the value of [RenderObject.needsCompositing] for the caller.
  /// * `offset` is the offset from the origin of the canvas' coordinate system
  ///   to the origin of the caller's coordinate system.
  /// * `clipRect` is rectangle (in the caller's coodinate system) to use to
  ///   clip the painting done by [painter].
  /// * `painter` is a callback that will paint with the [clipRect] applied. This
  ///   function calls the [painter] synchronously.
Adam Barth's avatar
Adam Barth committed
257
  void pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter) {
258
    final Rect offsetClipRect = clipRect.shift(offset);
Adam Barth's avatar
Adam Barth committed
259 260
    if (needsCompositing) {
      _stopRecordingIfNeeded();
261
      final ClipRectLayer clipLayer = new ClipRectLayer(clipRect: offsetClipRect);
262
      _appendLayer(clipLayer);
263
      final PaintingContext childContext = new PaintingContext._(clipLayer, offsetClipRect);
264
      painter(childContext, offset);
Adam Barth's avatar
Adam Barth committed
265 266
      childContext._stopRecordingIfNeeded();
    } else {
Hixie's avatar
Hixie committed
267
      canvas.save();
268
      canvas.clipRect(offsetClipRect);
Adam Barth's avatar
Adam Barth committed
269
      painter(this, offset);
Hixie's avatar
Hixie committed
270 271 272 273
      canvas.restore();
    }
  }

274
  /// Clip further painting using a rounded rectangle.
275
  ///
276 277 278 279 280 281 282 283 284 285
  /// * `needsCompositing` is whether the child needs compositing. Typically
  ///   matches the value of [RenderObject.needsCompositing] for the caller.
  /// * `offset` is the offset from the origin of the canvas' coordinate system
  ///   to the origin of the caller's coordinate system.
  /// * `bounds` is the region of the canvas (in the caller's coodinate system)
  ///   into which `painter` will paint in.
  /// * `clipRRect` is the rounded-rectangle (in the caller's coodinate system)
  ///   to use to clip the painting done by `painter`.
  /// * `painter` is a callback that will paint with the `clipRRect` applied. This
  ///   function calls the `painter` synchronously.
286
  void pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, RRect clipRRect, PaintingContextCallback painter) {
287 288
    final Rect offsetBounds = bounds.shift(offset);
    final RRect offsetClipRRect = clipRRect.shift(offset);
Adam Barth's avatar
Adam Barth committed
289 290
    if (needsCompositing) {
      _stopRecordingIfNeeded();
291
      final ClipRRectLayer clipLayer = new ClipRRectLayer(clipRRect: offsetClipRRect);
292
      _appendLayer(clipLayer);
293
      final PaintingContext childContext = new PaintingContext._(clipLayer, offsetBounds);
294
      painter(childContext, offset);
Adam Barth's avatar
Adam Barth committed
295 296
      childContext._stopRecordingIfNeeded();
    } else {
297
      canvas.saveLayer(offsetBounds, _defaultPaint);
298
      canvas.clipRRect(offsetClipRRect);
Adam Barth's avatar
Adam Barth committed
299
      painter(this, offset);
Hixie's avatar
Hixie committed
300 301 302 303
      canvas.restore();
    }
  }

304
  /// Clip further painting using a path.
305
  ///
306 307 308 309 310 311 312 313 314 315
  /// * `needsCompositing` is whether the child needs compositing. Typically
  ///   matches the value of [RenderObject.needsCompositing] for the caller.
  /// * `offset` is the offset from the origin of the canvas' coordinate system
  ///   to the origin of the caller's coordinate system.
  /// * `bounds` is the region of the canvas (in the caller's coodinate system)
  ///   into which `painter` will paint in.
  /// * `clipPath` is the path (in the coodinate system of the caller) to use to
  ///   clip the painting done by `painter`.
  /// * `painter` is a callback that will paint with the `clipPath` applied. This
  ///   function calls the `painter` synchronously.
Adam Barth's avatar
Adam Barth committed
316
  void pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath, PaintingContextCallback painter) {
317 318
    final Rect offsetBounds = bounds.shift(offset);
    final Path offsetClipPath = clipPath.shift(offset);
Adam Barth's avatar
Adam Barth committed
319 320
    if (needsCompositing) {
      _stopRecordingIfNeeded();
321
      final ClipPathLayer clipLayer = new ClipPathLayer(clipPath: offsetClipPath);
322
      _appendLayer(clipLayer);
323
      final PaintingContext childContext = new PaintingContext._(clipLayer, offsetBounds);
324
      painter(childContext, offset);
Adam Barth's avatar
Adam Barth committed
325
      childContext._stopRecordingIfNeeded();
326
    } else {
327
      canvas.saveLayer(bounds.shift(offset), _defaultPaint);
Adam Barth's avatar
Adam Barth committed
328 329 330
      canvas.clipPath(clipPath.shift(offset));
      painter(this, offset);
      canvas.restore();
331 332
    }
  }
333

334
  /// Transform further painting using a matrix.
335
  ///
336 337 338 339 340 341 342
  /// * `needsCompositing` is whether the child needs compositing. Typically
  ///   matches the value of [RenderObject.needsCompositing] for the caller.
  /// * `offset` is the offset from the origin of the canvas' coordinate system
  ///   to the origin of the caller's coordinate system.
  /// * `transform` is the matrix to apply to the paiting done by `painter`.
  /// * `painter` is a callback that will paint with the `transform` applied. This
  ///   function calls the `painter` synchronously.
Adam Barth's avatar
Adam Barth committed
343
  void pushTransform(bool needsCompositing, Offset offset, Matrix4 transform, PaintingContextCallback painter) {
344 345
    final Matrix4 effectiveTransform = new Matrix4.translationValues(offset.dx, offset.dy, 0.0)
      ..multiply(transform)..translate(-offset.dx, -offset.dy);
Adam Barth's avatar
Adam Barth committed
346 347
    if (needsCompositing) {
      _stopRecordingIfNeeded();
348
      final TransformLayer transformLayer = new TransformLayer(transform: effectiveTransform);
349
      _appendLayer(transformLayer);
350 351 352
      final Rect transformedPaintBounds = MatrixUtils.inverseTransformRect(_paintBounds, effectiveTransform);
      final PaintingContext childContext = new PaintingContext._(transformLayer, transformedPaintBounds);
      painter(childContext, offset);
Adam Barth's avatar
Adam Barth committed
353 354
      childContext._stopRecordingIfNeeded();
    } else {
355
      canvas.save();
356 357
      canvas.transform(effectiveTransform.storage);
      painter(this, offset);
358 359
      canvas.restore();
    }
360
  }
361

362
  /// Blend further paiting with an alpha value.
Adam Barth's avatar
Adam Barth committed
363
  ///
364 365 366 367 368 369 370
  /// * `offset` is the offset from the origin of the canvas' coordinate system
  ///   to the origin of the caller's coordinate system.
  /// * `alpha` is the alpha value to use when blending the painting done by
  ///   `painter`. An alpha value of 0 means the painting is fully transparent
  ///   and an alpha value of 255 means the painting is fully opaque.
  /// * `painter` is a callback that will paint with the `alpha` applied. This
  ///   function calls the `painter` synchronously.
371 372 373 374 375
  ///
  /// A [RenderObject] that uses this function is very likely to require its
  /// [RenderObject.alwaysNeedsCompositing] property to return true. That informs
  /// ancestor render objects that this render object will include a composited
  /// layer, which causes them to use composited clips, for example.
376 377
  void pushOpacity(Offset offset, int alpha, PaintingContextCallback painter) {
    _stopRecordingIfNeeded();
378
    final OpacityLayer opacityLayer = new OpacityLayer(alpha: alpha);
379
    _appendLayer(opacityLayer);
380
    final PaintingContext childContext = new PaintingContext._(opacityLayer, _paintBounds);
381
    painter(childContext, offset);
382
    childContext._stopRecordingIfNeeded();
383 384
  }

385
  /// Apply a mask derived from a shader to further painting.
386
  ///
387 388 389 390 391 392 393 394 395 396
  /// * `offset` is the offset from the origin of the canvas' coordinate system
  ///   to the origin of the caller's coordinate system.
  /// * `shader` is the shader that will generate the mask. The shader operates
  ///   in the coordinate system of the caller.
  /// * `maskRect` is the region of the canvas (in the coodinate system of the
  ///   caller) in which to apply the mask.
  /// * `transferMode` is the [TransferMode] to use when applying the shader to
  ///   the painting done by `painter`.
  /// * `painter` is a callback that will paint with the mask applied. This
  ///   function calls the `painter` synchronously.
397 398 399 400 401
  ///
  /// A [RenderObject] that uses this function is very likely to require its
  /// [RenderObject.alwaysNeedsCompositing] property to return true. That informs
  /// ancestor render objects that this render object will include a composited
  /// layer, which causes them to use composited clips, for example.
402
  void pushShaderMask(Offset offset, Shader shader, Rect maskRect, TransferMode transferMode, PaintingContextCallback painter) {
403
    _stopRecordingIfNeeded();
404
    final ShaderMaskLayer shaderLayer = new ShaderMaskLayer(
405 406 407 408
      shader: shader,
      maskRect: maskRect,
      transferMode: transferMode
    );
409
    _appendLayer(shaderLayer);
410
    final PaintingContext childContext = new PaintingContext._(shaderLayer, _paintBounds);
411
    painter(childContext, offset);
412
    childContext._stopRecordingIfNeeded();
413
  }
414 415 416 417 418

  /// Push a backdrop filter.
  ///
  /// This function applies a filter to the existing painted content and then
  /// synchronously calls the painter to paint on top of the filtered backdrop.
419 420 421 422 423
  ///
  /// A [RenderObject] that uses this function is very likely to require its
  /// [RenderObject.alwaysNeedsCompositing] property to return true. That informs
  /// ancestor render objects that this render object will include a composited
  /// layer, which causes them to use composited clips, for example.
424
  // TODO(abarth): I don't quite understand how this API is supposed to work.
425 426
  void pushBackdropFilter(Offset offset, ui.ImageFilter filter, PaintingContextCallback painter) {
    _stopRecordingIfNeeded();
427
    final BackdropFilterLayer backdropFilterLayer = new BackdropFilterLayer(filter: filter);
428
    _appendLayer(backdropFilterLayer);
429
    final PaintingContext childContext = new PaintingContext._(backdropFilterLayer, _paintBounds);
430 431 432
    painter(childContext, offset);
    childContext._stopRecordingIfNeeded();
  }
433 434
}

435
/// An abstract set of layout constraints.
436 437 438
///
/// Concrete layout models (such as box) will create concrete subclasses to
/// communicate layout constraints between parents and children.
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
///
/// ## Writing a Constraints subclass
///
/// When creating a new [RenderObject] subclass with a new layout protocol, one
/// will usually need to create a new [Constraints] subclass to express the
/// input to the layout algorithms.
///
/// A [Constraints] subclass should be immutable (all fields final). There are
/// several members to implement, in addition to whatever fields, constructors,
/// and helper methods one may find useful for a particular layout protocol:
///
/// * The [isTight] getter, which should return true if the object represents a
///   case where the [RenderObject] class has no choice for how to lay itself
///   out. For example, [BoxConstraints] returns true for [isTight] when both
///   the minimum and maximum widths and the minimum and maximum heights are
///   equal.
///
/// * The [isNormalized] getter, which should return true if the object
///   represents its data in its canonical form. Sometimes, it is possible for
///   fields to be redundant with each other, such that several different
///   representations have the same implications. For example, a
///   [BoxConstraints] instance with its minimum width greater than its maximum
///   width is equivalent to one where the maximum width is set to that minimum
///   width (`2<w<1` is equivalent to `2<w<2`, since minimum constraints have
///   priority). This getter is used by the default implementation of
///   [debugAssertIsValid].
///
/// * The [debugAssertIsValid] method, which should assert if there's anything
///   wrong with the constraints object. (We use this approach rather than
///   asserting in constructors so that our constructors can be `const` and so
///   that it is possible to create invalid constraints temporarily while
///   building valid ones.) See the implementation of
///   [BoxConstraints.debugAssertIsValid] for an example of the detailed checks
///   that can be made.
///
/// * The [operator ==] and [hashCode] members, so that constraints can be
///   compared for equality. If a render object is given constraints that are
///   equal, then the rendering library will avoid laying the object out again
///   if it is not dirty.
///
/// * The [toString] method, which should describe the constraints so that they
///   appear in a usefully readable form in the output of [debugDumpRenderTree].
481
abstract class Constraints {
482 483
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
484
  const Constraints();
485 486

  /// Whether there is exactly one size possible given these constraints
487
  bool get isTight;
488 489 490

  /// Whether the constraint is expressed in a consistent manner.
  bool get isNormalized;
491

492 493 494 495 496 497 498
  /// Asserts that the constraints are valid.
  ///
  /// This might involve checks more detailed than [isNormalized].
  ///
  /// For example, the [BoxConstraints] subclass verifies that the
  /// constraints are not [NaN].
  ///
499
  /// If the `isAppliedConstraint` argument is true, then even
500 501 502 503 504 505 506 507 508
  /// stricter rules are enforced. This argument is set to true when
  /// checking constraints that are about to be applied to a
  /// [RenderObject] during layout, as opposed to constraints that may
  /// be further affected by other constraints. For example, the
  /// asserts for verifying the validity of
  /// [RenderConstrainedBox.additionalConstraints] do not set this
  /// argument, but the asserts for verifying the argument passed to
  /// the [layout] method do.
  ///
509 510 511 512 513
  /// The `informationCollector` argument takes an optional callback
  /// which is called when an exception is to be thrown. The collected
  /// information is then included in the message after the error
  /// line.
  ///
514
  /// Returns the same as [isNormalized] if asserts are disabled.
515 516 517
  bool debugAssertIsValid({
    bool isAppliedConstraint: false,
    InformationCollector informationCollector
518 519 520 521
  }) {
    assert(isNormalized);
    return isNormalized;
  }
522 523
}

524
/// Signature for a function that is called for each [RenderObject].
525 526
///
/// Used by [RenderObject.visitChildren] and [RenderObject.visitChildrenForSemantics].
527
typedef void RenderObjectVisitor(RenderObject child);
528 529

/// Signature for a function that is called during layout.
530 531
///
/// Used by [RenderObject.invokeLayoutCallback].
532 533
typedef void LayoutCallback(Constraints constraints);

Hixie's avatar
Hixie committed
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
class _SemanticsGeometry {
  _SemanticsGeometry() : transform = new Matrix4.identity();
  _SemanticsGeometry.withClipFrom(_SemanticsGeometry other) {
    clipRect = other?.clipRect;
    transform = new Matrix4.identity();
  }
  _SemanticsGeometry.copy(_SemanticsGeometry other) {
    if (other != null) {
      clipRect = other.clipRect;
      transform = new Matrix4.copy(other.transform);
    } else {
      transform = new Matrix4.identity();
    }
  }
  Rect clipRect;
  Rect _intersectClipRect(Rect other) {
    if (clipRect == null)
      return other;
    if (other == null)
      return clipRect;
    return clipRect.intersect(other);
  }
  Matrix4 transform;
  void applyAncestorChain(List<RenderObject> ancestorChain) {
    for (int index = ancestorChain.length-1; index > 0; index -= 1) {
      RenderObject parent = ancestorChain[index];
      RenderObject child = ancestorChain[index-1];
      clipRect = _intersectClipRect(parent.describeApproximatePaintClip(child));
      if (clipRect != null) {
        if (clipRect.isEmpty) {
          clipRect = Rect.zero;
        } else {
          Matrix4 clipTransform = new Matrix4.identity();
          parent.applyPaintTransform(child, clipTransform);
568
          clipRect = MatrixUtils.inverseTransformRect(clipRect, clipTransform);
Hixie's avatar
Hixie committed
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
        }
      }
      parent.applyPaintTransform(child, transform);
    }
  }
  void updateSemanticsNode({ RenderObject rendering, SemanticsNode semantics, SemanticsNode parentSemantics }) {
    assert(rendering != null);
    assert(semantics != null);
    assert(parentSemantics.wasAffectedByClip != null);
    semantics.transform = transform;
    if (clipRect != null) {
      semantics.rect = clipRect.intersect(rendering.semanticBounds);
      semantics.wasAffectedByClip = true;
    } else {
      semantics.rect = rendering.semanticBounds;
      semantics.wasAffectedByClip = parentSemantics?.wasAffectedByClip ?? false;
    }
  }
}

abstract class _SemanticsFragment {
  _SemanticsFragment({
591
    RenderObject renderObjectOwner,
592
    this.annotator,
Hixie's avatar
Hixie committed
593 594
    List<_SemanticsFragment> children
  }) {
595 596
    assert(renderObjectOwner != null);
    _ancestorChain = <RenderObject>[renderObjectOwner];
Hixie's avatar
Hixie committed
597 598 599 600 601 602 603 604 605 606 607
    assert(() {
      if (children == null)
        return true;
      Set<_SemanticsFragment> seenChildren = new Set<_SemanticsFragment>();
      for (_SemanticsFragment child in children)
        assert(seenChildren.add(child)); // check for duplicate adds
      return true;
    });
    _children = children ?? const <_SemanticsFragment>[];
  }

608
  final SemanticsAnnotator annotator;
609

Hixie's avatar
Hixie committed
610 611 612 613 614
  List<RenderObject> _ancestorChain;
  void addAncestor(RenderObject ancestor) {
    _ancestorChain.add(ancestor);
  }

615
  RenderObject get renderObjectOwner => _ancestorChain.first;
Hixie's avatar
Hixie committed
616 617 618 619 620 621

  List<_SemanticsFragment> _children;

  bool _debugCompiled = false;
  Iterable<SemanticsNode> compile({ _SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics });

622
  @override
Hixie's avatar
Hixie committed
623 624 625 626 627 628 629 630
  String toString() => '$runtimeType($hashCode)';
}

/// Represents a subtree that doesn't need updating, it already has a
/// SemanticsNode and isn't dirty. (We still update the matrix, since
/// that comes from the (dirty) ancestors.)
class _CleanSemanticsFragment extends _SemanticsFragment {
  _CleanSemanticsFragment({
631 632 633
    RenderObject renderObjectOwner
  }) : super(renderObjectOwner: renderObjectOwner) {
    assert(renderObjectOwner._semantics != null);
Hixie's avatar
Hixie committed
634 635
  }

636
  @override
Hixie's avatar
Hixie committed
637 638 639
  Iterable<SemanticsNode> compile({ _SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics }) sync* {
    assert(!_debugCompiled);
    assert(() { _debugCompiled = true; return true; });
640
    SemanticsNode node = renderObjectOwner._semantics;
Hixie's avatar
Hixie committed
641 642 643
    assert(node != null);
    if (geometry != null) {
      geometry.applyAncestorChain(_ancestorChain);
644
      geometry.updateSemanticsNode(rendering: renderObjectOwner, semantics: node, parentSemantics: parentSemantics);
Hixie's avatar
Hixie committed
645 646 647 648 649 650 651 652 653
    } else {
      assert(_ancestorChain.length == 1);
    }
    yield node;
  }
}

abstract class _InterestingSemanticsFragment extends _SemanticsFragment {
  _InterestingSemanticsFragment({
654
    RenderObject renderObjectOwner,
655
    SemanticsAnnotator annotator,
Hixie's avatar
Hixie committed
656
    Iterable<_SemanticsFragment> children
657
  }) : super(renderObjectOwner: renderObjectOwner, annotator: annotator, children: children);
Hixie's avatar
Hixie committed
658 659 660

  bool get haveConcreteNode => true;

661
  @override
Hixie's avatar
Hixie committed
662 663 664 665
  Iterable<SemanticsNode> compile({ _SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics }) sync* {
    assert(!_debugCompiled);
    assert(() { _debugCompiled = true; return true; });
    SemanticsNode node = establishSemanticsNode(geometry, currentSemantics, parentSemantics);
666
    if (annotator != null)
Hixie's avatar
Hixie committed
667 668
      annotator(node);
    for (_SemanticsFragment child in _children) {
669
      assert(child._ancestorChain.last == renderObjectOwner);
Hixie's avatar
Hixie committed
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
      node.addChildren(child.compile(
        geometry: createSemanticsGeometryForChild(geometry),
        currentSemantics: _children.length > 1 ? null : node,
        parentSemantics: node
      ));
    }
    if (haveConcreteNode) {
      node.finalizeChildren();
      yield node;
    }
  }

  SemanticsNode establishSemanticsNode(_SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics);
  _SemanticsGeometry createSemanticsGeometryForChild(_SemanticsGeometry geometry);
}

class _RootSemanticsFragment extends _InterestingSemanticsFragment {
  _RootSemanticsFragment({
688
    RenderObject renderObjectOwner,
689
    SemanticsAnnotator annotator,
Hixie's avatar
Hixie committed
690
    Iterable<_SemanticsFragment> children
691
  }) : super(renderObjectOwner: renderObjectOwner, annotator: annotator, children: children);
Hixie's avatar
Hixie committed
692

693
  @override
Hixie's avatar
Hixie committed
694 695 696 697 698
  SemanticsNode establishSemanticsNode(_SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics) {
    assert(_ancestorChain.length == 1);
    assert(geometry == null);
    assert(currentSemantics == null);
    assert(parentSemantics == null);
699
    renderObjectOwner._semantics ??= new SemanticsNode.root(
700
      handler: renderObjectOwner is SemanticsActionHandler ? renderObjectOwner as dynamic : null,
701
      owner: renderObjectOwner.owner.semanticsOwner
Hixie's avatar
Hixie committed
702
    );
703
    SemanticsNode node = renderObjectOwner._semantics;
Hixie's avatar
Hixie committed
704 705
    assert(MatrixUtils.matrixEquals(node.transform, new Matrix4.identity()));
    assert(!node.wasAffectedByClip);
706
    node.rect = renderObjectOwner.semanticBounds;
Hixie's avatar
Hixie committed
707 708 709
    return node;
  }

710
  @override
Hixie's avatar
Hixie committed
711 712 713 714 715 716 717
  _SemanticsGeometry createSemanticsGeometryForChild(_SemanticsGeometry geometry) {
    return new _SemanticsGeometry();
  }
}

class _ConcreteSemanticsFragment extends _InterestingSemanticsFragment {
  _ConcreteSemanticsFragment({
718
    RenderObject renderObjectOwner,
719
    SemanticsAnnotator annotator,
Hixie's avatar
Hixie committed
720
    Iterable<_SemanticsFragment> children
721
  }) : super(renderObjectOwner: renderObjectOwner, annotator: annotator, children: children);
Hixie's avatar
Hixie committed
722

723
  @override
Hixie's avatar
Hixie committed
724
  SemanticsNode establishSemanticsNode(_SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics) {
725
    renderObjectOwner._semantics ??= new SemanticsNode(
726
      handler: renderObjectOwner is SemanticsActionHandler ? renderObjectOwner as dynamic : null
Hixie's avatar
Hixie committed
727
    );
728
    SemanticsNode node = renderObjectOwner._semantics;
Hixie's avatar
Hixie committed
729 730
    if (geometry != null) {
      geometry.applyAncestorChain(_ancestorChain);
731
      geometry.updateSemanticsNode(rendering: renderObjectOwner, semantics: node, parentSemantics: parentSemantics);
Hixie's avatar
Hixie committed
732 733 734 735 736 737
    } else {
      assert(_ancestorChain.length == 1);
    }
    return node;
  }

738
  @override
Hixie's avatar
Hixie committed
739 740 741 742 743 744 745
  _SemanticsGeometry createSemanticsGeometryForChild(_SemanticsGeometry geometry) {
    return new _SemanticsGeometry.withClipFrom(geometry);
  }
}

class _ImplicitSemanticsFragment extends _InterestingSemanticsFragment {
  _ImplicitSemanticsFragment({
746
    RenderObject renderObjectOwner,
747
    SemanticsAnnotator annotator,
Hixie's avatar
Hixie committed
748
    Iterable<_SemanticsFragment> children
749
  }) : super(renderObjectOwner: renderObjectOwner, annotator: annotator, children: children);
Hixie's avatar
Hixie committed
750

751
  @override
Hixie's avatar
Hixie committed
752 753 754
  bool get haveConcreteNode => _haveConcreteNode;
  bool _haveConcreteNode;

755
  @override
Hixie's avatar
Hixie committed
756 757 758
  SemanticsNode establishSemanticsNode(_SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics) {
    SemanticsNode node;
    assert(_haveConcreteNode == null);
759
    _haveConcreteNode = currentSemantics == null && annotator != null;
Hixie's avatar
Hixie committed
760
    if (haveConcreteNode) {
761
      renderObjectOwner._semantics ??= new SemanticsNode(
762
        handler: renderObjectOwner is SemanticsActionHandler ? renderObjectOwner as dynamic : null
Hixie's avatar
Hixie committed
763
      );
764
      node = renderObjectOwner._semantics;
Hixie's avatar
Hixie committed
765
    } else {
766
      renderObjectOwner._semantics = null;
Hixie's avatar
Hixie committed
767 768 769 770 771
      node = currentSemantics;
    }
    if (geometry != null) {
      geometry.applyAncestorChain(_ancestorChain);
      if (haveConcreteNode)
772
        geometry.updateSemanticsNode(rendering: renderObjectOwner, semantics: node, parentSemantics: parentSemantics);
Hixie's avatar
Hixie committed
773 774 775 776 777 778
    } else {
      assert(_ancestorChain.length == 1);
    }
    return node;
  }

779
  @override
Hixie's avatar
Hixie committed
780 781 782 783 784 785 786 787 788
  _SemanticsGeometry createSemanticsGeometryForChild(_SemanticsGeometry geometry) {
    if (haveConcreteNode)
      return new _SemanticsGeometry.withClipFrom(geometry);
    return new _SemanticsGeometry.copy(geometry);
  }
}

class _ForkingSemanticsFragment extends _SemanticsFragment {
  _ForkingSemanticsFragment({
789
    RenderObject renderObjectOwner,
Hixie's avatar
Hixie committed
790
    Iterable<_SemanticsFragment> children
791
  }) : super(renderObjectOwner: renderObjectOwner, children: children) {
Hixie's avatar
Hixie committed
792 793 794 795
    assert(children != null);
    assert(children.length > 1);
  }

796
  @override
Hixie's avatar
Hixie committed
797 798 799 800 801 802
  Iterable<SemanticsNode> compile({ _SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics }) sync* {
    assert(!_debugCompiled);
    assert(() { _debugCompiled = true; return true; });
    assert(geometry != null);
    geometry.applyAncestorChain(_ancestorChain);
    for (_SemanticsFragment child in _children) {
803
      assert(child._ancestorChain.last == renderObjectOwner);
Hixie's avatar
Hixie committed
804 805 806 807 808 809 810 811 812
      yield* child.compile(
        geometry: new _SemanticsGeometry.copy(geometry),
        currentSemantics: null,
        parentSemantics: parentSemantics
      );
    }
  }
}

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
class SemanticsHandle {
  SemanticsHandle._(this._owner, this.listener) {
    assert(_owner != null);
    if (listener != null)
      _owner.semanticsOwner.addListener(listener);
  }

  PipelineOwner _owner;
  final VoidCallback listener;

  @mustCallSuper
  void dispose() {
    assert(() {
      if (_owner == null) {
        throw new FlutterError(
          'SemanticsHandle has already been disposed.\n'
          'Each SemanticsHandle should be disposed exactly once.'
        );
      }
      return true;
    });
    if (_owner != null) {
      if (listener != null)
        _owner.semanticsOwner.removeListener(listener);
      _owner._didDisposeSemanticsHandle();
      _owner = null;
    }
  }
}

843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
/// The pipeline owner manages the rendering pipeline.
///
/// The pipeline owner provides an interface for driving the rendering pipeline
/// and stores the state about which render objects have requested to be visited
/// in each stage of the pipeline. To flush the pipeline, call the following
/// functions in order:
///
/// 1. [flushLayout] updates any render objects that need to compute their
///    layout. During this phase, the size and position of each render
///    object is calculated. Render objects might dirty their painting or
///    compositing state during this phase.
/// 2. [flushCompositingBits] updates any render objects that have dirty
///    compositing bits. During this phase, each render object learns whether
///    any of its children require compositing. This information is used during
///    the painting phase when selecting how to implement visual effects such as
///    clipping. If a render object has a composited child, its needs to use a
///    [Layer] to create the clip in order for the clip to apply to the
///    composited child (which will be painted into its own [Layer]).
/// 3. [flushPaint] visites any render objects that need to paint. During this
///    phase, render objects get a chance to record painting commands into
///    [PictureLayer]s and construct other composited [Layer]s.
864 865 866
/// 4. Finally, if semantics are enabled, [flushSemantics] will compile the
///    semantics for the render objects. This semantic information is used by
///    assistive technology to improve the accessibility of the render tree.
867 868 869 870 871
///
/// The [RendererBinding] holds the pipeline owner for the render objects that
/// are visible on screen. You can create other pipeline owners to manage
/// off-screen objects, which can flush their pipelines independently of the
/// on-screen render objects.
872
class PipelineOwner {
873 874 875 876 877
  /// Creates a pipeline owner.
  ///
  /// Typically created by the binding (e.g., [RendererBinding]), but can be
  /// created separately from the binding to drive off-screen render objects
  /// through the rendering pipeline.
878 879
  PipelineOwner({
    this.onNeedVisualUpdate,
880 881
    this.onSemanticsOwnerCreated,
    this.onSemanticsOwnerDisposed,
882
  });
883 884 885 886 887 888 889 890

  /// Called when a render object associated with this pipeline owner wishes to
  /// update its visual appearance.
  ///
  /// Typical implementations of this function will schedule a task to flush the
  /// various stages of the pipeline. This function might be called multiple
  /// times in quick succession. Implementations should take care to discard
  /// duplicate calls quickly.
891 892
  final VoidCallback onNeedVisualUpdate;

893
  /// Called whenever this pipeline owner creates as semantics object.
894
  ///
895 896 897
  /// Typical implementations will schedule the creation of the initial
  /// semantics tree.
  final VoidCallback onSemanticsOwnerCreated;
898

899
  /// Called whenever this pipeline owner disposes its semantics owner.
900
  ///
901 902
  /// Typical implementations will tear down the semantics tree.
  final VoidCallback onSemanticsOwnerDisposed;
903

904 905 906 907
  /// Calls [onNeedVisualUpdate] if [onNeedVisualUpdate] is not null.
  ///
  /// Used to notify the pipeline owner that an associated render object wishes
  /// to update its visual appearance.
908 909 910 911
  void requestVisualUpdate() {
    if (onNeedVisualUpdate != null)
      onNeedVisualUpdate();
  }
912

913 914 915 916 917 918 919
  /// The unique object managed by this pipeline that has no parent.
  ///
  /// This object does not have to be a [RenderObject].
  AbstractNode get rootNode => _rootNode;
  AbstractNode _rootNode;
  set rootNode(AbstractNode value) {
    if (_rootNode == value)
920
      return;
921 922 923
    _rootNode?.detach();
    _rootNode = value;
    _rootNode?.attach(this);
924 925
  }

926
  List<RenderObject> _nodesNeedingLayout = <RenderObject>[];
927 928 929 930 931 932

  /// Whether this pipeline is currently in the layout phase.
  ///
  /// Specifically, whether [flushLayout] is currently running.
  ///
  /// Only valid when asserts are enabled.
933
  bool get debugDoingLayout => _debugDoingLayout;
934 935
  bool _debugDoingLayout = false;

936 937 938 939 940 941
  /// 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.
  ///
942
  /// See [RendererBinding] for an example of how this function is used.
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
  void flushLayout() {
    Timeline.startSync('Layout');
    _debugDoingLayout = true;
    try {
      // TODO(ianh): assert that we're not allowing previously dirty nodes to redirty themeselves
      while (_nodesNeedingLayout.isNotEmpty) {
        List<RenderObject> dirtyNodes = _nodesNeedingLayout;
        _nodesNeedingLayout = <RenderObject>[];
        for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
          if (node._needsLayout && node.owner == this)
            node._layoutWithoutResize();
        }
      }
    } finally {
      _debugDoingLayout = false;
      Timeline.finishSync();
    }
  }

962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
  // This flag is used to allow the kinds of mutations performed by GlobalKey
  // reparenting while a LayoutBuilder is being rebuilt and in so doing tries to
  // move a node from another LayoutBuilder subtree that hasn't been updated
  // yet. To set this, call [_enableMutationsToDirtySubtrees], which is called
  // by [RenderObject.invokeLayoutCallback].
  bool _debugAllowMutationsToDirtySubtrees = false;

  // See [RenderObject.invokeLayoutCallback].
  void _enableMutationsToDirtySubtrees(VoidCallback callback) {
    assert(_debugDoingLayout);
    bool oldState = _debugAllowMutationsToDirtySubtrees;
    _debugAllowMutationsToDirtySubtrees = true;
    try {
      callback();
    } finally {
      _debugAllowMutationsToDirtySubtrees = oldState;
    }
  }

981 982 983 984 985 986
  List<RenderObject> _nodesNeedingCompositingBitsUpdate = <RenderObject>[];
  /// Updates the [needsCompositing] bits.
  ///
  /// Called as part of the rendering pipeline after [flushLayout] and before
  /// [flushPaint].
  void flushCompositingBits() {
987
    Timeline.startSync('Compositing bits');
988 989
    _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
    for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
990
      if (node._needsCompositingBitsUpdate && node.owner == this)
991 992 993 994 995 996 997
        node._updateCompositingBits();
    }
    _nodesNeedingCompositingBitsUpdate.clear();
    Timeline.finishSync();
  }

  List<RenderObject> _nodesNeedingPaint = <RenderObject>[];
998 999 1000 1001 1002 1003

  /// Whether this pipeline is currently in the paint phase.
  ///
  /// Specifically, whether [flushPaint] is currently running.
  ///
  /// Only valid when asserts are enabled.
1004
  bool get debugDoingPaint => _debugDoingPaint;
1005 1006
  bool _debugDoingPaint = false;

1007 1008 1009 1010 1011 1012
  /// 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.
  ///
1013
  /// See [RendererBinding] for an example of how this function is used.
1014 1015 1016 1017 1018 1019 1020 1021
  void flushPaint() {
    Timeline.startSync('Paint');
    _debugDoingPaint = true;
    try {
      List<RenderObject> dirtyNodes = _nodesNeedingPaint;
      _nodesNeedingPaint = <RenderObject>[];
      // Sort the dirty nodes in reverse order (deepest first).
      for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
1022
        if (node._needsPaint && node.owner == this)
1023
          PaintingContext.repaintCompositedChild(node);
pq's avatar
pq committed
1024
      }
1025 1026 1027 1028 1029 1030 1031
      assert(_nodesNeedingPaint.length == 0);
    } finally {
      _debugDoingPaint = false;
      Timeline.finishSync();
    }
  }

1032 1033
  /// The object that is managing semantics for this pipeline owner, if any.
  ///
1034 1035 1036 1037 1038
  /// An owner is created by [ensureSemantics]. The owner is valid for as long
  /// there are [SemanticsHandle] returned by [ensureSemantics] that have not
  /// yet be disposed. Once the last handle has been disposed, the
  /// [semanticsOwner] field will revert to null, and the previous owner will be
  /// disposed.
1039 1040 1041
  ///
  /// When [semanticsOwner] is null, the [PipelineOwner] skips all steps
  /// relating to semantics.
1042 1043
  SemanticsOwner get semanticsOwner => _semanticsOwner;
  SemanticsOwner _semanticsOwner;
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066

  int _outstandingSemanticsHandle = 0;

  SemanticsHandle ensureSemantics({ VoidCallback listener }) {
    if (_outstandingSemanticsHandle++ == 0) {
      assert(_semanticsOwner == null);
      _semanticsOwner = new SemanticsOwner();
      if (onSemanticsOwnerCreated != null)
        onSemanticsOwnerCreated();
    }
    return new SemanticsHandle._(this, listener);
  }

  void _didDisposeSemanticsHandle() {
    assert(_semanticsOwner != null);
    if (--_outstandingSemanticsHandle == 0) {
      _semanticsOwner.dispose();
      _semanticsOwner = null;
      if (onSemanticsOwnerDisposed != null)
        onSemanticsOwnerDisposed();
    }
  }

1067
  bool _debugDoingSemantics = false;
1068
  final List<RenderObject> _nodesNeedingSemantics = <RenderObject>[];
1069

1070 1071 1072 1073 1074 1075
  /// Update the semantics for all render objects.
  ///
  /// This function is one of the core stages of the rendering pipeline. The
  /// semantics are compiled after painting and only after
  /// [RenderObject.scheduleInitialSemantics] has been called.
  ///
1076
  /// See [RendererBinding] for an example of how this function is used.
1077
  void flushSemantics() {
1078 1079
    if (_semanticsOwner == null)
      return;
1080
    Timeline.startSync('Semantics');
1081
    assert(_semanticsOwner != null);
1082 1083 1084 1085 1086 1087 1088
    assert(() { _debugDoingSemantics = true; return true; });
    try {
      _nodesNeedingSemantics.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
      for (RenderObject node in _nodesNeedingSemantics) {
        if (node._needsSemanticsUpdate && node.owner == this)
          node._updateSemantics();
      }
1089
      _semanticsOwner.sendSemanticsUpdate();
1090 1091 1092 1093 1094 1095 1096 1097
    } finally {
      _nodesNeedingSemantics.clear();
      assert(() { _debugDoingSemantics = false; return true; });
      Timeline.finishSync();
    }
  }
}

1098 1099 1100
// See _performLayout.
void _doNothing() { }

1101
/// An object in the render tree.
1102
///
1103 1104 1105
/// The [RenderObject] class hierarchy is the core of the rendering
/// library's reason for being.
///
1106 1107 1108 1109
/// [RenderObject]s have a [parent], and have a slot called [parentData] in
/// which the parent [RenderObject] can store child-specific data, for example,
/// the child position. The [RenderObject] class also implements the basic
/// layout and paint protocols.
1110
///
1111 1112 1113 1114 1115 1116 1117 1118
/// The [RenderObject] class, however, does not define a child model (e.g.
/// whether a node has zero, one, or more children). It also doesn't define a
/// coordinate system (e.g. whether children are positioned in cartesian
/// coordinates, in polar coordinates, etc) or a specific layout protocol (e.g.
/// whether the layout is width-in-height-out, or constraint-in-size-out, or
/// whether the parent sets the size and position of the child before or after
/// the child lays out, etc; or indeed whether the children are allowed to read
/// their parent's [parentData] slot).
1119 1120 1121
///
/// The [RenderBox] subclass introduces the opinion that the layout
/// system uses cartesian coordinates.
1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 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
///
/// ## Writing a RenderObject subclass
///
/// In most cases, subclassing [RenderObject] itself is overkill, and
/// [RenderBox] would be a better starting point. However, if a render object
/// doesn't want to use a cartesian coordinate system, then it should indeed
/// inherit from [RenderObject] directly. This allows it to define its own
/// layout protocol by using a new subclass of [Constraints] rather than using
/// [BoxConstraints], and by potentially using an entirely new set of objects
/// and values to represent the result of the output rather than just a [Size].
/// This increased flexibility comes at the cost of not being able to rely on
/// the features of [RenderBox]. For example, [RenderBox] implements an
/// intrinsic sizing protocol that allows you to measure a child without fully
/// laying it out, in such a way that if that child changes size, the parent
/// will be laid out again (to take into account the new dimensions of the
/// child). This is a subtle and bug-prone feature to get right.
///
/// Most aspects of writing a [RenderBox] apply to writing a [RenderObject] as
/// well, and therefore the discussion at [RenderBox] is recommended background
/// reading. The main differences are around layout and hit testing, since those
/// are the aspects that [RenderBox] primarily specializes.
///
/// ### Layout
///
/// A layout protocol begins with a subclass of [Constraints]. See the
/// discussion at [Constraints] for more information on how to write a
/// [Constraints] subclass.
///
/// The [performLayout] method should take the [constraints], and apply them.
/// The output of the layout algorithm is fields set on the object that describe
/// the geometry of the object for the purposes of the parent's layout. For
/// example, with [RenderBox] the output is the [RenderBox.size] field. This
/// output should only be read by the parent if the parent specified
/// `parentUsesSize` as true when calling [layout] on the child.
///
/// Anytime anything changes on a render object that would affect the layout of
/// that object, it should call [markNeedsLayout].
///
/// ### Hit Testing
///
/// Hit testing is even more open-ended than layout. There is no method to
/// override, you are expected to provide one.
///
/// The general behaviour of your hit-testing method should be similar to the
/// behavior described for [RenderBox]. The main difference is that the input
/// need not be a [Point]. You are also allowed to use a different subclass of
/// [HitTestEntry] when adding entries to the [HitTestResult]. When the
/// [handleEvent] method is called, the same object that was added to the
/// [HitTestResult] will be passed in, so it can be used to track information
/// like the precise coordinate of the hit, in whatever coordinate system is
/// used by the new layout protocol.
///
/// ### Adapting from one protocol to another
///
/// In general, the root of a Flutter render object tree is a [RenderView]. This
/// object has a single child, which must be a [RenderBox]. Thus, if you want to
/// have a custom [RenderObject] subclass in the render tree, you have two
/// choices: you either need to replace the [RenderView] itself, or you need to
/// have a [RenderBox] that has your class as its child. (The latter is the much
/// more common case.)
///
/// This [RenderBox] subclass converts from the box protocol to the protocol of
/// your class.
///
/// In particular, this means that for hit testing it overrides
/// [RenderBox.hitTest], and calls whatever method you have in your class for
/// hit testing.
///
/// Similarly, it overrides [performLayout] to create a [Constraints] object
/// appropriate for your class and passes that to the child's [layout] method.
///
/// ### Layout interactions between render objects
///
/// In general, the layout of a render box should only depend on the output of
/// its child's layout, and then only if `parentUsesSize` is set to true in the
/// [layout] call. Furthermore, if it is set to true, the parent must call the
/// child's [layout] if the child is to be rendered, because otherwise the
/// parent will not be notified when the child changes its layout outputs.
///
/// It is possible to set up render object protocols that transfer additional
/// information. For example, in the [RenderBox] protocol you can query your
/// children's intrinsic dimensions and baseline geometry. However, if this is
/// done then it is imperative that the child call [markNeedsLayout] on the
/// parent any time that additional information changes, if the parent used it
/// in the last layout phase. For an example of how to implement this, see the
/// [RenderBox.markNeedsLayout] method. It overrides
/// [RenderObject.markNeedsLayout] so that if a parent has queried the intrinsic
/// or baseline information, it gets marked dirty whenever the child's geometry
/// changes.
1211
abstract class RenderObject extends AbstractNode implements HitTestTarget {
1212
  /// Initializes internal fields for subclasses.
1213
  RenderObject() {
1214
    _needsCompositing = isRepaintBoundary || alwaysNeedsCompositing;
1215
    _performLayout = performLayout;
1216 1217
  }

1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
  /// Cause the entire subtree rooted at the given [RenderObject] to be marked
  /// dirty for layout, paint, etc. This is called by the [RendererBinding] in
  /// response to the `ext.flutter.reassemble` hook, which is used by
  /// development tools when the application code has changed, to cause the
  /// widget tree to pick up any changed implementations.
  ///
  /// This is expensive and should not be called except during development.
  ///
  /// See also:
  ///
  /// * [BindingBase.reassembleApplication].
  void reassemble() {
1230 1231 1232 1233 1234 1235
    _performLayout = performLayout;
    markNeedsLayout();
    markNeedsCompositingBitsUpdate();
    markNeedsPaint();
    markNeedsSemanticsUpdate();
    visitChildren((RenderObject child) {
1236
      child.reassemble();
1237 1238 1239
    });
  }

1240 1241
  // LAYOUT

1242
  /// Data for use by the parent render object.
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
  ///
  /// 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 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.
Hixie's avatar
Hixie committed
1257
  ParentData parentData;
1258

1259
  /// Override to setup parent data correctly for your children.
1260 1261 1262
  ///
  /// You can call this function to set up the parent data for child before the
  /// child is added to the parent's child list.
Adam Barth's avatar
Adam Barth committed
1263
  void setupParentData(@checked RenderObject child) {
1264
    assert(_debugCanPerformMutations);
1265 1266 1267 1268
    if (child.parentData is! ParentData)
      child.parentData = new ParentData();
  }

1269
  /// Called by subclasses when they decide a render object is a child.
1270 1271 1272
  ///
  /// 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.
1273
  @override
1274
  void adoptChild(RenderObject child) {
1275
    assert(_debugCanPerformMutations);
1276 1277 1278 1279
    assert(child != null);
    setupParentData(child);
    super.adoptChild(child);
    markNeedsLayout();
1280
    markNeedsCompositingBitsUpdate();
1281
  }
1282

1283
  /// Called by subclasses when they decide a render object is no longer a child.
1284 1285 1286
  ///
  /// 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.
1287
  @override
1288
  void dropChild(RenderObject child) {
1289
    assert(_debugCanPerformMutations);
1290 1291
    assert(child != null);
    assert(child.parentData != null);
1292
    child._cleanRelayoutBoundary();
1293
    child.parentData.detach();
1294
    child.parentData = null;
1295 1296
    super.dropChild(child);
    markNeedsLayout();
1297
    markNeedsCompositingBitsUpdate();
1298 1299
  }

1300
  /// Calls visitor for each immediate child of this render object.
1301
  ///
1302
  /// Override in subclasses with children and call the visitor for each child.
1303 1304
  void visitChildren(RenderObjectVisitor visitor) { }

1305 1306 1307
  /// The object responsible for creating this render object.
  ///
  /// Used in debug messages.
1308
  dynamic debugCreator;
1309
  void _debugReportException(String method, dynamic exception, StackTrace stack) {
1310 1311 1312 1313 1314 1315 1316
    FlutterError.reportError(new FlutterErrorDetailsForRendering(
      exception: exception,
      stack: stack,
      library: 'rendering library',
      context: 'during $method()',
      renderObject: this,
      informationCollector: (StringBuffer information) {
1317
        information.writeln('The following RenderObject was being processed when the exception was fired:\n  $this');
1318
        if (debugCreator != null)
1319
          information.writeln('This RenderObject had the following creator information:\n  $debugCreator');
1320 1321 1322
        List<String> descendants = <String>[];
        const int maxDepth = 5;
        int depth = 0;
1323
        const int maxLines = 25;
1324 1325 1326 1327
        int lines = 0;
        void visitor(RenderObject child) {
          if (lines < maxLines) {
            depth += 1;
1328
            descendants.add('${"  " * depth}$child');
1329 1330 1331
            if (depth < maxDepth)
              child.visitChildren(visitor);
            depth -= 1;
1332 1333
          } else if (lines == maxLines) {
            descendants.add('  ...(descendants list truncated after $lines lines)');
1334
          }
1335 1336 1337 1338 1339 1340 1341
          lines += 1;
        }
        visitChildren(visitor);
        if (lines > 1) {
          information.writeln('This RenderObject had the following descendants (showing up to depth $maxDepth):');
        } else if (descendants.length == 1) {
          information.writeln('This RenderObject had the following child:');
1342
        } else {
1343
          information.writeln('This RenderObject has no descendants.');
1344
        }
1345
        information.writeAll(descendants, '\n');
1346
      }
1347
    ));
1348 1349
  }

1350 1351 1352 1353
  /// Whether [performResize] for this render object is currently running.
  ///
  /// Only valid when asserts are enabled. In release builds, always returns
  /// false.
1354
  bool get debugDoingThisResize => _debugDoingThisResize;
1355 1356 1357 1358 1359 1360
  bool _debugDoingThisResize = false;

  /// Whether [performLayout] for this render object is currently running.
  ///
  /// Only valid when asserts are enabled. In release builds, always returns
  /// false.
1361
  bool get debugDoingThisLayout => _debugDoingThisLayout;
1362 1363 1364 1365 1366 1367
  bool _debugDoingThisLayout = false;

  /// The render object that is actively computing layout.
  ///
  /// Only valid when asserts are enabled. In release builds, always returns
  /// null.
1368
  static RenderObject get debugActiveLayout => _debugActiveLayout;
1369 1370 1371 1372 1373 1374 1375 1376 1377
  static RenderObject _debugActiveLayout;

  /// Whether the parent render object is permitted to use this render object's
  /// size.
  ///
  /// Determined by the `parentUsesSize` parameter to [layout].
  ///
  /// Only valid when asserts are enabled. In release builds, always returns
  /// null.
1378
  bool get debugCanParentUseSize => _debugCanParentUseSize;
1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
  bool _debugCanParentUseSize;

  bool _debugMutationsLocked = false;

  /// Whether tree mutations are currently permitted.
  ///
  /// Only valid when asserts are enabled. In release builds, always returns
  /// null.
  bool get _debugCanPerformMutations {
    bool result;
    assert(() {
      RenderObject node = this;
      while (true) {
        if (node._doingThisLayoutWithCallback) {
          result = true;
          break;
        }
1396 1397 1398 1399
        if (owner != null && owner._debugAllowMutationsToDirtySubtrees && node._needsLayout) {
          result = true;
          break;
        }
1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
        if (node._debugMutationsLocked) {
          result = false;
          break;
        }
        if (node.parent is! RenderObject) {
          result = true;
          break;
        }
        node = node.parent;
      }
      return true;
    });
    return result;
1413 1414
  }

1415 1416 1417
  @override
  PipelineOwner get owner => super.owner;

1418 1419 1420
  @override
  void attach(PipelineOwner owner) {
    super.attach(owner);
1421 1422
    // If the node was dirtied in some way while unattached, make sure to add
    // it to the appropriate dirty list now that an owner is available
1423
    if (_needsLayout && _relayoutBoundary != null) {
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438
      // Don't enter this block if we've never laid out at all;
      // scheduleInitialLayout() will handle it
      _needsLayout = false;
      markNeedsLayout();
    }
    if (_needsCompositingBitsUpdate) {
      _needsCompositingBitsUpdate = false;
      markNeedsCompositingBitsUpdate();
    }
    if (_needsPaint && _layer != null) {
      // Don't enter this block if we've never painted at all;
      // scheduleInitialPaint() will handle it
      _needsPaint = false;
      markNeedsPaint();
    }
1439
    if (_needsSemanticsUpdate && isSemanticBoundary) {
1440 1441 1442 1443 1444
      // Don't enter this block if we've never updated semantics at all;
      // scheduleInitialSemantics() will handle it
      _needsSemanticsUpdate = false;
      markNeedsSemanticsUpdate();
    }
1445 1446
  }

1447
  /// Whether this render object's layout information is dirty.
1448
  bool get needsLayout => _needsLayout;
1449 1450
  bool _needsLayout = true;

1451
  RenderObject _relayoutBoundary;
1452
  bool _doingThisLayoutWithCallback = false;
1453

1454
  /// The layout constraints most recently supplied by the parent.
1455
  @protected
1456
  Constraints get constraints => _constraints;
1457 1458
  Constraints _constraints;

1459 1460 1461
  /// Verify that the object's constraints are being met. Override
  /// this function in a subclass to verify that your state matches
  /// the constraints object. This function is only called in checked
1462 1463
  /// mode and only when needsLayout is false. If the constraints are
  /// not met, it should assert or throw an exception.
1464
  @protected
1465 1466 1467
  void debugAssertDoesMeetConstraints();

  /// When true, debugAssertDoesMeetConstraints() is currently
1468
  /// executing asserts for verifying the consistent behavior of
1469 1470 1471 1472 1473 1474 1475 1476
  /// intrinsic dimensions methods.
  ///
  /// This should only be set by debugAssertDoesMeetConstraints()
  /// implementations. It is used by tests to selectively ignore
  /// custom layout callbacks. It should not be set outside of
  /// debugAssertDoesMeetConstraints(), and should not be checked in
  /// release mode (where it will always be false).
  static bool debugCheckingIntrinsics = false;
1477
  bool _debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout() {
1478
    if (_relayoutBoundary == null)
1479 1480
      return true; // we haven't yet done layout even once, so there's nothing for us to do
    RenderObject node = this;
1481 1482
    while (node != _relayoutBoundary) {
      assert(node._relayoutBoundary == _relayoutBoundary);
1483
      assert(node.parent != null);
1484
      node = node.parent;
1485
      if ((!node._needsLayout) && (!node._debugDoingThisLayout))
1486 1487
        return false;
    }
1488
    assert(node._relayoutBoundary == node);
1489 1490
    return true;
  }
1491

1492 1493 1494 1495 1496
  /// Mark this render object's layout information as dirty, and either register
  /// this object with its [PipelineOwner], or defer to the parent, depending on
  /// whether this object is a relayout boundary or not respectively.
  ///
  /// ## Background
1497 1498
  ///
  /// Rather than eagerly updating layout information in response to writes into
1499
  /// a render object, we instead mark the layout information as dirty, which
1500
  /// schedules a visual update. As part of the visual update, the rendering
1501
  /// pipeline updates the render object's layout information.
1502 1503 1504 1505
  ///
  /// This mechanism batches the layout work so that multiple sequential writes
  /// are coalesced, removing redundant computation.
  ///
1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525
  /// If a render object's parent indicates that it uses the size of one of its
  /// render object children when computing its layout information, this
  /// function, when called for the child, will also mark the parent as needing
  /// layout. In that case, since both the parent and the child need to have
  /// their layout recomputed, the pipeline owner is only notified about the
  /// parent; when the parent is laid out, it will call the child's [layout]
  /// method and thus the child will be laid out as well.
  ///
  /// Once [markNeedsLayout] has been called on a render object, [needsLayout]
  /// returns true for that render object until just after the pipeline owner
  /// has called [layout] on the render object.
  ///
  /// ## Special cases
  ///
  /// Some subclasses of [RenderObject], notably [RenderBox], have other
  /// situations in which the parent needs to be notified if the child is
  /// dirtied. Such subclasses override markNeedsLayout and either call
  /// `super.markNeedsLayout()`, in the normal case, or call
  /// [markParentNeedsLayout], in the case where the parent neds to be laid out
  /// as well as the child.
1526
  void markNeedsLayout() {
1527
    assert(_debugCanPerformMutations);
1528
    if (_needsLayout) {
1529
      assert(_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout());
1530 1531
      return;
    }
1532 1533
    assert(_relayoutBoundary != null);
    if (_relayoutBoundary != this) {
1534
      markParentNeedsLayout();
1535
    } else {
1536
      _needsLayout = true;
1537
      if (owner != null) {
1538 1539
        assert(() {
          if (debugPrintMarkNeedsLayoutStacks)
1540
            debugPrintStack(label: 'markNeedsLayout() called for $this');
1541 1542
          return true;
        });
1543
        owner._nodesNeedingLayout.add(this);
1544 1545
        owner.requestVisualUpdate();
      }
1546 1547
    }
  }
1548

1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568
  /// Mark this render object's layout information as dirty, and then defer to
  /// the parent.
  ///
  /// This function should only be called from [markNeedsLayout] implementations
  /// of subclasses that introduce more reasons for deferring the handling of
  /// dirty layout to the parent. See [markNeedsLayout] for details.
  ///
  /// Only call this if [parent] is not null.
  @protected
  void markParentNeedsLayout() {
    _needsLayout = true;
    final RenderObject parent = this.parent;
    if (!_doingThisLayoutWithCallback) {
      parent.markNeedsLayout();
    } else {
      assert(parent._debugDoingThisLayout);
    }
    assert(parent == this.parent);
  }

1569 1570 1571
  void _cleanRelayoutBoundary() {
    if (_relayoutBoundary != this) {
      _relayoutBoundary = null;
1572
      _needsLayout = true;
1573
      visitChildren((RenderObject child) {
1574
        child._cleanRelayoutBoundary();
1575
      });
1576 1577
    }
  }
1578

1579
  /// Bootstrap the rendering pipeline by scheduling the very first layout.
1580 1581 1582 1583 1584
  ///
  /// 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.
1585 1586
  void scheduleInitialLayout() {
    assert(attached);
Hixie's avatar
Hixie committed
1587
    assert(parent is! RenderObject);
1588
    assert(!owner._debugDoingLayout);
1589 1590
    assert(_relayoutBoundary == null);
    _relayoutBoundary = this;
1591 1592 1593 1594
    assert(() {
      _debugCanParentUseSize = false;
      return true;
    });
1595
    owner._nodesNeedingLayout.add(this);
1596
  }
1597 1598

  void _layoutWithoutResize() {
1599
    assert(_relayoutBoundary == this);
1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610
    RenderObject debugPreviousActiveLayout;
    assert(!_debugMutationsLocked);
    assert(!_doingThisLayoutWithCallback);
    assert(_debugCanParentUseSize != null);
    assert(() {
      _debugMutationsLocked = true;
      _debugDoingThisLayout = true;
      debugPreviousActiveLayout = _debugActiveLayout;
      _debugActiveLayout = this;
      return true;
    });
1611
    try {
1612
      _performLayout();
Hixie's avatar
Hixie committed
1613
      markNeedsSemanticsUpdate();
1614 1615
    } catch (e, stack) {
      _debugReportException('performLayout', e, stack);
1616
    }
1617 1618 1619 1620 1621 1622
    assert(() {
      _debugActiveLayout = debugPreviousActiveLayout;
      _debugDoingThisLayout = false;
      _debugMutationsLocked = false;
      return true;
    });
1623 1624 1625
    _needsLayout = false;
    markNeedsPaint();
  }
1626

1627
  /// Compute the layout for this render object.
1628
  ///
1629
  /// This method is the main entry point for parents to ask their children to
1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641
  /// 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.
  ///
1642 1643 1644
  /// Subclasses should not override [layout] directly. Instead, they should
  /// override [performResize] and/or [performLayout]. The [layout] method
  /// delegates the actual work to [performResize] and [performLayout].
1645
  ///
1646 1647
  /// The parent's performLayout method should call the [layout] of all its
  /// children unconditionally. It is the [layout] method's responsibility (as
1648 1649
  /// implemented here) to return early if the child does not need to do any
  /// work to update its layout information.
1650
  void layout(Constraints constraints, { bool parentUsesSize: false }) {
1651
    assert(constraints != null);
1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665
    assert(constraints.debugAssertIsValid(
      isAppliedConstraint: true,
      informationCollector: (StringBuffer information) {
        List<String> stack = StackTrace.current.toString().split('\n');
        int targetFrame;
        Pattern layoutFramePattern = new RegExp(r'^#[0-9]+ +RenderObject.layout \(');
        for (int i = 0; i < stack.length; i += 1) {
          if (layoutFramePattern.matchAsPrefix(stack[i]) != null) {
            targetFrame = i + 1;
            break;
          }
        }
        if (targetFrame != null && targetFrame < stack.length) {
          information.writeln(
1666
            'These invalid constraints were provided to $runtimeType\'s layout() '
1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679
            'function by the following function, which probably computed the '
            'invalid constraints in question:'
          );
          Pattern targetFramePattern = new RegExp(r'^#[0-9]+ +(.+)$');
          Match targetFrameMatch = targetFramePattern.matchAsPrefix(stack[targetFrame]);
          if (targetFrameMatch != null && targetFrameMatch.groupCount > 0) {
            information.writeln('  ${targetFrameMatch.group(1)}');
          } else {
            information.writeln(stack[targetFrame]);
          }
        }
      }
    ));
Hixie's avatar
Hixie committed
1680 1681
    assert(!_debugDoingThisResize);
    assert(!_debugDoingThisLayout);
1682
    RenderObject relayoutBoundary;
1683
    if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject) {
1684
      relayoutBoundary = this;
1685 1686
    } else {
      final RenderObject parent = this.parent;
1687
      relayoutBoundary = parent._relayoutBoundary;
1688
    }
Hixie's avatar
Hixie committed
1689
    assert(parent == this.parent);
1690 1691 1692 1693
    assert(() {
      _debugCanParentUseSize = parentUsesSize;
      return true;
    });
1694
    if (!needsLayout && constraints == _constraints && relayoutBoundary == _relayoutBoundary) {
1695 1696 1697
      assert(() {
        // in case parentUsesSize changed since the last invocation, set size
        // to itself, so it has the right internal debug values.
Hixie's avatar
Hixie committed
1698 1699
        _debugDoingThisResize = sizedByParent;
        _debugDoingThisLayout = !sizedByParent;
1700 1701 1702 1703 1704
        RenderObject debugPreviousActiveLayout = _debugActiveLayout;
        _debugActiveLayout = this;
        debugResetSize();
        _debugActiveLayout = debugPreviousActiveLayout;
        _debugDoingThisLayout = false;
Hixie's avatar
Hixie committed
1705
        _debugDoingThisResize = false;
1706 1707
        return true;
      });
1708
      return;
1709
    }
1710
    _constraints = constraints;
1711
    _relayoutBoundary = relayoutBoundary;
1712
    assert(!_debugMutationsLocked);
1713
    assert(!_doingThisLayoutWithCallback);
1714 1715 1716 1717 1718 1719
    assert(() {
      _debugMutationsLocked = true;
      return true;
    });
    if (sizedByParent) {
      assert(() { _debugDoingThisResize = true; return true; });
1720 1721
      try {
        performResize();
1722
        assert(() { debugAssertDoesMeetConstraints(); return true; });
1723 1724 1725
      } catch (e, stack) {
        _debugReportException('performResize', e, stack);
      }
1726 1727 1728 1729 1730 1731 1732 1733 1734
      assert(() { _debugDoingThisResize = false; return true; });
    }
    RenderObject debugPreviousActiveLayout;
    assert(() {
      _debugDoingThisLayout = true;
      debugPreviousActiveLayout = _debugActiveLayout;
      _debugActiveLayout = this;
      return true;
    });
1735
    try {
1736
      _performLayout();
Hixie's avatar
Hixie committed
1737
      markNeedsSemanticsUpdate();
1738
      assert(() { debugAssertDoesMeetConstraints(); return true; });
1739 1740
    } catch (e, stack) {
      _debugReportException('performLayout', e, stack);
1741
    }
1742 1743 1744 1745 1746 1747
    assert(() {
      _debugActiveLayout = debugPreviousActiveLayout;
      _debugDoingThisLayout = false;
      _debugMutationsLocked = false;
      return true;
    });
1748 1749
    _needsLayout = false;
    markNeedsPaint();
Hixie's avatar
Hixie committed
1750
    assert(parent == this.parent);
1751
  }
1752

1753 1754 1755 1756 1757 1758
  /// If a subclass has a "size" (the state controlled by `parentUsesSize`,
  /// whatever it is in the subclass, e.g. the actual `size` property of
  /// [RenderBox]), and the subclass verifies that in checked mode this "size"
  /// property isn't used when [debugCanParentUseSize] isn't set, then that
  /// subclass should override [debugResetSize] to reapply the current values of
  /// [debugCanParentUseSize] to that state.
1759
  @protected
1760 1761
  void debugResetSize() { }

1762
  /// Whether the constraints are the only input to the sizing algorithm (in
1763
  /// particular, child nodes have no impact).
1764 1765 1766 1767
  ///
  /// 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.
1768
  @protected
1769 1770
  bool get sizedByParent => false;

1771
  /// Updates the render objects size using only the constraints.
1772 1773 1774 1775 1776 1777
  ///
  /// 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.
  ///
1778
  /// Subclasses that set [sizedByParent] to true should override this method
1779 1780
  /// to compute their size.
  ///
1781
  /// This function is called only if [sizedByParent] is true.
1782
  @protected
1783 1784
  void performResize();

1785
  /// Do the work of computing the layout for this render object.
1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
  ///
  /// 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.
1804
  @protected
1805
  void performLayout();
1806

1807 1808 1809 1810 1811
  // We cache a closure to performLayout so that the callsite is monomorphic.
  // Initializing this field with _buildNothing helps the compiler prove that
  // this field always holds a closure.
  VoidCallback _performLayout = _doNothing;

1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826
  /// Allows mutations to be made to this object's child list (and any
  /// descendants) as well as to any other dirty nodes in the render tree owned
  /// by the same [PipelineOwner] as this object. The `callback` argument is
  /// invoked synchronously, and the mutations are allowed only during that
  /// callback's execution.
  ///
  /// This exists to allow child lists to be built on-demand during layout (e.g.
  /// based on the object's size), and to enable nodes to be moved around the
  /// tree as this happens (e.g. to handle [GlobalKey] reparenting), while still
  /// ensuring that any particular node is only laid out once per frame.
  ///
  /// Calling this function disables a number of assertions that are intended to
  /// catch likely bugs. As such, using this function is generally discouraged.
  ///
  /// This function can only be called during layout.
1827
  @protected
1828 1829 1830
  void invokeLayoutCallback(LayoutCallback callback) {
    assert(_debugMutationsLocked);
    assert(_debugDoingThisLayout);
1831 1832 1833
    assert(!_doingThisLayoutWithCallback);
    _doingThisLayoutWithCallback = true;
    try {
1834
      owner._enableMutationsToDirtySubtrees(() { callback(constraints); });
1835 1836 1837
    } finally {
      _doingThisLayoutWithCallback = false;
    }
1838 1839
  }

1840
  /// Rotate this render object (not yet implemented).
1841 1842 1843 1844 1845 1846
  void rotate({
    int oldAngle, // 0..3
    int newAngle, // 0..3
    Duration time
  }) { }

1847 1848 1849 1850 1851 1852 1853 1854
  // 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
1855
  // equivalent will be called.
1856 1857 1858 1859


  // PAINTING

1860 1861 1862 1863
  /// Whether [paint] for this render object is currently running.
  ///
  /// Only valid when asserts are enabled. In release builds, always returns
  /// false.
1864
  bool get debugDoingThisPaint => _debugDoingThisPaint;
1865 1866 1867 1868 1869 1870
  bool _debugDoingThisPaint = false;

  /// The render object that is actively painting.
  ///
  /// Only valid when asserts are enabled. In release builds, always returns
  /// null.
1871
  static RenderObject get debugActivePaint => _debugActivePaint;
1872
  static RenderObject _debugActivePaint;
1873

1874
  /// Whether this render object repaints separately from its parent.
1875
  ///
1876 1877 1878 1879 1880 1881 1882 1883
  /// Override this in subclasses to indicate that instances of your class ought
  /// to repaint independently. For example, render objects that repaint
  /// frequently might want to repaint themselves without requiring their parent
  /// to repaint.
  ///
  /// Warning: This getter must not change value over the lifetime of this object.
  bool get isRepaintBoundary => false;

1884 1885 1886 1887 1888 1889 1890
  /// Called, in checked mode, if [isRepaintBoundary] is true, when either the
  /// this render object or its parent attempt to paint.
  ///
  /// This can be used to record metrics about whether the node should actually
  /// be a repaint boundary.
  void debugRegisterRepaintBoundaryPaint({ bool includedParent: true, bool includedChild: false }) { }

1891 1892 1893 1894
  /// Whether this render object always needs compositing.
  ///
  /// Override this in subclasses to indicate that your paint function always
  /// creates at least one composited layer. For example, videos should return
1895 1896
  /// true if they use hardware decoders.
  ///
1897 1898
  /// You must call markNeedsCompositingBitsUpdate() if the value of this
  /// getter changes.
1899
  @protected
1900
  bool get alwaysNeedsCompositing => false;
1901

1902
  OffsetLayer _layer;
1903
  /// The compositing layer that this render object uses to repaint.
1904
  ///
1905
  /// Call only when [isRepaintBoundary] is true.
1906
  OffsetLayer get layer {
1907
    assert(isRepaintBoundary);
Hixie's avatar
Hixie committed
1908 1909
    assert(!_needsPaint);
    return _layer;
1910 1911
  }

1912
  bool _needsCompositingBitsUpdate = false; // set to true when a child is added
1913
  /// Mark the compositing state for this render object as dirty.
1914
  ///
1915 1916 1917 1918 1919 1920 1921 1922 1923 1924
  /// When the subtree is mutated, we need to recompute our
  /// [needsCompositing] bit, and some of our ancestors need to do the
  /// same (in case ours changed in a way that will change theirs). To
  /// this end, [adoptChild] and [dropChild] call this method, and, as
  /// necessary, this method calls the parent's, etc, walking up the
  /// tree to mark all the nodes that need updating.
  ///
  /// This method does not schedule a rendering frame, because since
  /// it cannot be the case that _only_ the compositing bits changed,
  /// something else will have scheduled a frame for us.
1925
  void markNeedsCompositingBitsUpdate() {
Hixie's avatar
Hixie committed
1926
    if (_needsCompositingBitsUpdate)
1927
      return;
Hixie's avatar
Hixie committed
1928
    _needsCompositingBitsUpdate = true;
1929 1930 1931 1932
    if (parent is RenderObject) {
      final RenderObject parent = this.parent;
      if (parent._needsCompositingBitsUpdate)
        return;
1933
      if (!isRepaintBoundary && !parent.isRepaintBoundary) {
1934
        parent.markNeedsCompositingBitsUpdate();
1935 1936 1937 1938 1939 1940 1941 1942 1943 1944
        return;
      }
    }
    assert(() {
      final AbstractNode parent = this.parent;
      if (parent is RenderObject)
        return parent._needsCompositing;
      return true;
    });
    // parent is fine (or there isn't one), but we are dirty
1945 1946
    if (owner != null)
      owner._nodesNeedingCompositingBitsUpdate.add(this);
1947 1948 1949
  }

  bool _needsCompositing; // initialised in the constructor
1950
  /// Whether we or one of our descendants has a compositing layer.
1951
  ///
1952
  /// Only legal to call after [flushLayout] and [flushCompositingBits] have
1953
  /// been called.
Hixie's avatar
Hixie committed
1954 1955 1956 1957
  bool get needsCompositing {
    assert(!_needsCompositingBitsUpdate); // make sure we don't use this bit when it is dirty
    return _needsCompositing;
  }
1958

1959
  void _updateCompositingBits() {
Hixie's avatar
Hixie committed
1960
    if (!_needsCompositingBitsUpdate)
1961
      return;
1962
    bool oldNeedsCompositing = _needsCompositing;
1963
    visitChildren((RenderObject child) {
1964
      child._updateCompositingBits();
Hixie's avatar
Hixie committed
1965 1966
      if (child.needsCompositing)
        _needsCompositing = true;
1967
    });
1968
    if (isRepaintBoundary || alwaysNeedsCompositing)
Hixie's avatar
Hixie committed
1969
      _needsCompositing = true;
1970
    if (oldNeedsCompositing != _needsCompositing)
Hixie's avatar
Hixie committed
1971 1972
      markNeedsPaint();
    _needsCompositingBitsUpdate = false;
1973 1974 1975
  }

  bool _needsPaint = true;
1976

1977
  /// Mark this render object as having changed its visual appearance.
1978 1979 1980 1981 1982 1983 1984 1985 1986
  ///
  /// 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.
1987
  void markNeedsPaint() {
1988
    assert(owner == null || !owner.debugDoingPaint);
1989 1990 1991
    if (_needsPaint)
      return;
    _needsPaint = true;
1992
    if (isRepaintBoundary) {
1993 1994
      assert(() {
        if (debugPrintMarkNeedsPaintStacks)
1995
          debugPrintStack(label: 'markNeedsPaint() called for $this');
1996 1997
        return true;
      });
Hixie's avatar
Hixie committed
1998 1999 2000
      // If we always have our own layer, then we can just repaint
      // ourselves without involving any other nodes.
      assert(_layer != null);
2001
      if (owner != null) {
2002
        owner._nodesNeedingPaint.add(this);
2003 2004
        owner.requestVisualUpdate();
      }
Hixie's avatar
Hixie committed
2005 2006 2007 2008 2009
    } 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);
Hixie's avatar
Hixie committed
2010 2011 2012
      final RenderObject parent = this.parent;
      parent.markNeedsPaint();
      assert(parent == this.parent);
Hixie's avatar
Hixie committed
2013 2014 2015 2016 2017
    } 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.
2018 2019
      if (owner != null)
        owner.requestVisualUpdate();
2020 2021
    }
  }
2022

2023
  /// Bootstrap the rendering pipeline by scheduling the very first paint.
2024 2025 2026 2027 2028
  ///
  /// 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.
2029
  void scheduleInitialPaint(ContainerLayer rootLayer) {
Hixie's avatar
Hixie committed
2030 2031
    assert(attached);
    assert(parent is! RenderObject);
2032
    assert(!owner._debugDoingPaint);
2033
    assert(isRepaintBoundary);
2034
    assert(_layer == null);
2035 2036
    _layer = rootLayer;
    assert(_needsPaint);
2037
    owner._nodesNeedingPaint.add(this);
Hixie's avatar
Hixie committed
2038
  }
2039 2040 2041 2042 2043 2044

  /// Replace the layer. This is only valid for the root of a render
  /// object subtree (whatever object [scheduleInitialPaint] was
  /// called on).
  ///
  /// This might be called if, e.g., the device pixel ratio changed.
2045
  void replaceRootLayer(OffsetLayer rootLayer) {
2046 2047 2048 2049 2050 2051 2052 2053 2054
    assert(attached);
    assert(parent is! RenderObject);
    assert(!owner._debugDoingPaint);
    assert(isRepaintBoundary);
    assert(_layer != null); // use scheduleInitialPaint the first time
    _layer = rootLayer;
    markNeedsPaint();
  }

2055
  void _paintWithContext(PaintingContext context, Offset offset) {
2056
    assert(!_debugDoingThisPaint);
Hixie's avatar
Hixie committed
2057 2058
    assert(!_needsLayout);
    assert(!_needsCompositingBitsUpdate);
2059 2060 2061 2062 2063
    RenderObject debugLastActivePaint;
    assert(() {
      _debugDoingThisPaint = true;
      debugLastActivePaint = _debugActivePaint;
      _debugActivePaint = this;
2064
      assert(!isRepaintBoundary || _layer != null);
2065 2066
      return true;
    });
Hixie's avatar
Hixie committed
2067
    _needsPaint = false;
2068 2069 2070 2071 2072 2073 2074
    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);
    }
2075
    assert(() {
2076
      debugPaint(context, offset);
2077 2078 2079 2080 2081 2082
      _debugActivePaint = debugLastActivePaint;
      _debugDoingThisPaint = false;
      return true;
    });
  }

2083
  /// The bounds within which this render object will paint.
2084 2085 2086 2087 2088 2089 2090
  ///
  /// 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.
2091
  Rect get paintBounds;
2092

2093
  /// Override this method to paint debugging information.
2094
  @protected
2095
  void debugPaint(PaintingContext context, Offset offset) { }
2096

2097
  /// Paint this render object into the given context at the given offset.
2098
  ///
2099
  /// Subclasses should override this method to provide a visual appearance
2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113
  /// 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.
2114
  void paint(PaintingContext context, Offset offset) { }
2115

2116 2117
  /// Applies the transform that would be applied when painting the given child
  /// to the given matrix.
2118
  ///
2119
  /// Used by coordinate conversion functions to translate coordinates local to
2120
  /// one render object into coordinates local to another render object.
Adam Barth's avatar
Adam Barth committed
2121
  void applyPaintTransform(@checked RenderObject child, Matrix4 transform) {
2122 2123
    assert(child.parent == this);
  }
2124

Hixie's avatar
Hixie committed
2125 2126 2127 2128
  /// Returns a rect in this object's coordinate system that describes
  /// the approximate bounding box of the clip rect that would be
  /// applied to the given child during the paint phase, if any.
  ///
2129
  /// Returns `null` if the child would not be clipped.
Hixie's avatar
Hixie committed
2130 2131 2132
  ///
  /// This is used in the semantics phase to avoid including children
  /// that are not physically visible.
Adam Barth's avatar
Adam Barth committed
2133
  Rect describeApproximatePaintClip(@checked RenderObject child) => null;
Hixie's avatar
Hixie committed
2134 2135 2136 2137 2138 2139 2140 2141 2142 2143


  // SEMANTICS

  /// Bootstrap the semantics reporting mechanism by marking this node
  /// as needing a semantics update.
  ///
  /// Requires that this render object is attached, and is the root of
  /// the render tree.
  ///
2144
  /// See [RendererBinding] for an example of how this function is used.
Hixie's avatar
Hixie committed
2145 2146 2147
  void scheduleInitialSemantics() {
    assert(attached);
    assert(parent is! RenderObject);
2148
    assert(!owner._debugDoingSemantics);
Hixie's avatar
Hixie committed
2149 2150
    assert(_semantics == null);
    assert(_needsSemanticsUpdate);
2151
    assert(owner._semanticsOwner != null);
2152
    owner._nodesNeedingSemantics.add(this);
2153
    owner.requestVisualUpdate();
Hixie's avatar
Hixie committed
2154 2155 2156
  }

  /// Whether this RenderObject introduces a new box for accessibility purposes.
2157
  bool get isSemanticBoundary => false;
Hixie's avatar
Hixie committed
2158 2159 2160 2161 2162 2163 2164 2165 2166

  /// The bounding box, in the local coordinate system, of this
  /// object, for accessibility purposes.
  Rect get semanticBounds;

  bool _needsSemanticsUpdate = true;
  bool _needsSemanticsGeometryUpdate = true;
  SemanticsNode _semantics;

2167 2168 2169 2170 2171 2172 2173 2174 2175
  /// The semantics of this render object.
  ///
  /// Exposed only for testing and debugging. To learn about the semantics of
  /// render objects in production, register as a listener using
  /// [SemanticsNode.addListener].
  ///
  /// Only valid when asserts are enabled. In release builds, always returns
  /// null.
  SemanticsNode get debugSemantics {
Hixie's avatar
Hixie committed
2176 2177 2178 2179 2180 2181 2182 2183
    SemanticsNode result;
    assert(() {
      result = _semantics;
      return true;
    });
    return result;
  }

2184
  /// Removes all semantics from this render object and its descendants.
2185 2186 2187
  ///
  /// Should only be called on objects whose [parent] is not a [RenderObject].
  void clearSemantics() {
2188 2189 2190 2191
    _needsSemanticsUpdate = true;
    _needsSemanticsGeometryUpdate = true;
    _semantics = null;
    visitChildren((RenderObject child) {
2192
      child.clearSemantics();
2193 2194 2195
    });
  }

Hixie's avatar
Hixie committed
2196 2197 2198
  /// Mark this node as needing an update to its semantics
  /// description.
  ///
2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216
  /// If the change did not involve a removal or addition of semantics, only the
  /// change of semantics (e.g. isChecked changing from true to false, as
  /// opposed to isChecked changing from being true to not being changed at
  /// all), then you can pass the onlyChanges argument with the value true to
  /// reduce the cost. If semantics are being added or removed, more work needs
  /// to be done to update the semantics tree. If you pass 'onlyChanges: true'
  /// but this node, which previously had a SemanticsNode, no longer has one, or
  /// previously did not set any semantics, but now does, or previously had a
  /// child that returned annotators, but no longer does, or other such
  /// combinations, then you will either assert during the subsequent call to
  /// [PipelineOwner.flushSemantics()] or you will have out-of-date information
  /// in the semantics tree.
  ///
  /// If the geometry might have changed in any way, then again, more work needs
  /// to be done to update the semantics tree (to deal with clips). You can pass
  /// the noGeometry argument to avoid this work in the case where only the
  /// labels or flags changed. If you pass 'noGeometry: true' when the geometry
  /// did change, the semantic tree will be out of date.
Hixie's avatar
Hixie committed
2217
  void markNeedsSemanticsUpdate({ bool onlyChanges: false, bool noGeometry: false }) {
2218
    assert(!attached || !owner._debugDoingSemantics);
2219
    if ((attached && owner._semanticsOwner == null) || (_needsSemanticsUpdate && onlyChanges && (_needsSemanticsGeometryUpdate || noGeometry)))
Hixie's avatar
Hixie committed
2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238
      return;
    if (!noGeometry && (_semantics == null || (_semantics.hasChildren && _semantics.wasAffectedByClip))) {
      // Since the geometry might have changed, we need to make sure to reapply any clips.
      _needsSemanticsGeometryUpdate = true;
    }
    if (onlyChanges) {
      // The shape of the tree didn't change, but the details did.
      // If we have our own SemanticsNode (our _semantics isn't null)
      // then mark ourselves dirty. If we don't then we are using an
      // ancestor's; mark all the nodes up to that one dirty.
      RenderObject node = this;
      while (node._semantics == null && node.parent is RenderObject) {
        if (node._needsSemanticsUpdate)
          return;
        node._needsSemanticsUpdate = true;
        node = node.parent;
      }
      if (!node._needsSemanticsUpdate) {
        node._needsSemanticsUpdate = true;
2239 2240
        if (owner != null)
          owner._nodesNeedingSemantics.add(node);
2241
      }
Hixie's avatar
Hixie committed
2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258
    } else {
      // The shape of the semantics tree around us may have changed.
      // The worst case is that we may have removed a branch of the
      // semantics tree, because when that happens we have to go up
      // and dirty the nearest _semantics-laden ancestor of the
      // affected node to rebuild the tree.
      RenderObject node = this;
      do {
        if (node.parent is! RenderObject)
          break;
        node._needsSemanticsUpdate = true;
        node._semantics?.reset();
        node = node.parent;
      } while (node._semantics == null);
      node._semantics?.reset();
      if (!node._needsSemanticsUpdate) {
        node._needsSemanticsUpdate = true;
2259 2260
        if (owner != null)
          owner._nodesNeedingSemantics.add(node);
2261
      }
Hixie's avatar
Hixie committed
2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280
    }
  }

  void _updateSemantics() {
    try {
      assert(_needsSemanticsUpdate);
      assert(_semantics != null || parent is! RenderObject);
      _SemanticsFragment fragment = _getSemanticsFragment();
      assert(fragment is _InterestingSemanticsFragment);
      SemanticsNode node = fragment.compile(parentSemantics: _semantics?.parent).single;
      assert(node != null);
      assert(node == _semantics);
    } catch (e, stack) {
      _debugReportException('_updateSemantics', e, stack);
    }
  }

  _SemanticsFragment _getSemanticsFragment() {
    // early-exit if we're not dirty and have our own semantics
2281
    if (!_needsSemanticsUpdate && isSemanticBoundary) {
Hixie's avatar
Hixie committed
2282
      assert(_semantics != null);
2283
      return new _CleanSemanticsFragment(renderObjectOwner: this);
Hixie's avatar
Hixie committed
2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303
    }
    List<_SemanticsFragment> children;
    visitChildrenForSemantics((RenderObject child) {
      if (_needsSemanticsGeometryUpdate) {
        // If our geometry changed, make sure the child also does a
        // full update so that any changes to the clip are fully
        // applied.
        child._needsSemanticsUpdate = true;
        child._needsSemanticsGeometryUpdate = true;
      }
      _SemanticsFragment fragment = child._getSemanticsFragment();
      if (fragment != null) {
        fragment.addAncestor(this);
        children ??= <_SemanticsFragment>[];
        assert(!children.contains(fragment));
        children.add(fragment);
      }
    });
    _needsSemanticsUpdate = false;
    _needsSemanticsGeometryUpdate = false;
2304
    SemanticsAnnotator annotator = semanticsAnnotator;
Hixie's avatar
Hixie committed
2305
    if (parent is! RenderObject)
2306 2307 2308 2309 2310
      return new _RootSemanticsFragment(renderObjectOwner: this, annotator: annotator, children: children);
    if (isSemanticBoundary)
      return new _ConcreteSemanticsFragment(renderObjectOwner: this, annotator: annotator, children: children);
    if (annotator != null)
      return new _ImplicitSemanticsFragment(renderObjectOwner: this, annotator: annotator, children: children);
Hixie's avatar
Hixie committed
2311 2312 2313 2314
    _semantics = null;
    if (children == null)
      return null;
    if (children.length > 1)
2315
      return new _ForkingSemanticsFragment(renderObjectOwner: this, children: children);
Hixie's avatar
Hixie committed
2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329
    assert(children.length == 1);
    return children.single;
  }

  /// Called when collecting the semantics of this node. Subclasses
  /// that have children that are not semantically relevant (e.g.
  /// because they are invisible) should skip those children here.
  ///
  /// The default implementation mirrors the behavior of
  /// [visitChildren()] (which is supposed to walk all the children).
  void visitChildrenForSemantics(RenderObjectVisitor visitor) {
    visitChildren(visitor);
  }

2330 2331
  /// Returns a function that will annotate a [SemanticsNode] with the semantics
  /// of this [RenderObject].
Hixie's avatar
Hixie committed
2332
  ///
2333 2334
  /// To annotate a SemanticsNode for this node, return an annotator that
  /// adds the annotations. When the behavior of the annotator would
Hixie's avatar
Hixie committed
2335
  /// change (e.g. the box is now checked rather than unchecked), call
2336
  /// [markNeedsSemanticsUpdate] to indicate to the rendering system
Hixie's avatar
Hixie committed
2337 2338 2339
  /// that the semantics tree needs to be rebuilt.
  ///
  /// To introduce a new SemanticsNode, set hasSemantics to true for
2340
  /// this object. The function returned by this function will be used
Hixie's avatar
Hixie committed
2341 2342 2343 2344
  /// to annotate the SemanticsNode for this object.
  ///
  /// Semantic annotations are persistent. Values set in one pass will
  /// still be set in the next pass. Therefore it is important to
2345
  /// explicitly set fields to false once they are no longer true;
Hixie's avatar
Hixie committed
2346 2347 2348 2349
  /// setting them to true when they are to be enabled, and not
  /// setting them at all when they are not, will mean they remain set
  /// once enabled once and will never get unset.
  ///
2350 2351 2352 2353
  /// If the value return will change from null to non-null (or vice versa), and
  /// [hasSemantics] isn't true, then the associated call to
  /// [markNeedsSemanticsUpdate] must not have `onlyChanges` set, as it is
  /// possible that the node should be entirely removed.
2354
  SemanticsAnnotator get semanticsAnnotator => null;
Hixie's avatar
Hixie committed
2355

2356 2357 2358

  // EVENTS

2359
  /// Override this method to handle pointer events that hit this render object.
2360
  @override
Adam Barth's avatar
Adam Barth committed
2361
  void handleEvent(PointerEvent event, @checked HitTestEntry entry) { }
2362 2363 2364 2365


  // HIT TESTING

2366 2367 2368 2369
  // RenderObject subclasses are expected to have a method like the following
  // (with the signature being whatever passes for coordinates for this
  // particular class):
  //
2370
  // bool hitTest(HitTestResult result, { Point position }) {
2371
  //   // If the given position is not inside this node, then return false.
2372
  //   // Otherwise:
2373 2374 2375 2376
  //   // For each child that intersects the position, 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.
2377 2378
  //   // Then, add yourself to /result/, and return true.
  // }
2379 2380 2381
  //
  // If you add yourself to /result/ and still return false, then that means you
  // will see events but so will objects below you.
2382 2383


2384
  /// Returns a human understandable name.
2385
  @override
2386
  String toString() {
Hixie's avatar
Hixie committed
2387
    String header = '$runtimeType';
2388
    if (_relayoutBoundary != null && _relayoutBoundary != this) {
2389 2390
      int count = 1;
      RenderObject target = parent;
2391
      while (target != null && target != _relayoutBoundary) {
2392
        target = target.parent;
2393 2394
        count += 1;
      }
2395
      header += ' relayoutBoundary=up$count';
2396 2397 2398 2399 2400
    }
    if (_needsLayout)
      header += ' NEEDS-LAYOUT';
    if (!attached)
      header += ' DETACHED';
2401
    return header;
2402
  }
2403

2404 2405 2406
  /// Returns a description of the tree rooted at this node.
  /// If the prefix argument is provided, then every line in the output
  /// will be prefixed by that string.
Hixie's avatar
Hixie committed
2407
  String toStringDeep([String prefixLineOne = '', String prefixOtherLines = '']) {
2408 2409
    RenderObject debugPreviousActiveLayout = _debugActiveLayout;
    _debugActiveLayout = null;
Hixie's avatar
Hixie committed
2410 2411
    String result = '$prefixLineOne$this\n';
    final String childrenDescription = debugDescribeChildren(prefixOtherLines);
2412 2413 2414
    final String descriptionPrefix = childrenDescription != '' ? '$prefixOtherLines \u2502 ' : '$prefixOtherLines   ';
    List<String> description = <String>[];
    debugFillDescription(description);
2415
    result += description
2416
      .expand((String description) => debugWordWrap(description, 65, wrapIndent: '  '))
2417 2418
      .map/*<String>*/((String line) => "$descriptionPrefix$line\n")
      .join();
2419
    if (childrenDescription == '')
Hixie's avatar
Hixie committed
2420 2421
      result += '$prefixOtherLines\n';
    result += childrenDescription;
2422 2423 2424 2425
    _debugActiveLayout = debugPreviousActiveLayout;
    return result;
  }

2426 2427 2428 2429
  /// Returns a one-line detailed description of the render object.
  /// This description is often somewhat long.
  ///
  /// This includes the same information for this RenderObject as given by
2430
  /// [toStringDeep], but does not recurse to any children.
2431 2432 2433 2434 2435
  String toStringShallow() {
    RenderObject debugPreviousActiveLayout = _debugActiveLayout;
    _debugActiveLayout = null;
    StringBuffer result = new StringBuffer();
    result.write('$this; ');
2436 2437 2438
    List<String> description = <String>[];
    debugFillDescription(description);
    result.write(description.join('; '));
2439 2440 2441 2442
    _debugActiveLayout = debugPreviousActiveLayout;
    return result.toString();
  }

2443 2444 2445
  /// Accumulates a list of strings describing the current node's fields, one
  /// field per string. Subclasses should override this to have their
  /// information included in [toStringDeep].
2446
  @protected
2447
  void debugFillDescription(List<String> description) {
2448 2449
    if (debugCreator != null)
      description.add('creator: $debugCreator');
2450 2451
    description.add('parentData: $parentData');
    description.add('constraints: $constraints');
2452
  }
2453 2454 2455

  /// Returns a string describing the current node's descendants. Each line of
  /// the subtree in the output should be indented by the prefix argument.
2456
  @protected
2457 2458 2459 2460
  String debugDescribeChildren(String prefix) => '';

}

2461
/// Generic mixin for render objects with one child.
2462
///
2463
/// Provides a child model for a render object subclass that has a unique child.
2464 2465
abstract class RenderObjectWithChildMixin<ChildType extends RenderObject> implements RenderObject {
  ChildType _child;
2466
  /// The render object's unique child
2467
  ChildType get child => _child;
2468
  set child (ChildType value) {
2469 2470 2471 2472 2473 2474
    if (_child != null)
      dropChild(_child);
    _child = value;
    if (_child != null)
      adoptChild(_child);
  }
2475 2476

  @override
2477 2478
  void attach(PipelineOwner owner) {
    super.attach(owner);
2479
    if (_child != null)
2480
      _child.attach(owner);
2481
  }
2482 2483

  @override
2484 2485
  void detach() {
    super.detach();
2486 2487 2488
    if (_child != null)
      _child.detach();
  }
2489

2490 2491 2492 2493 2494 2495
  @override
  void redepthChildren() {
    if (_child != null)
      redepthChild(_child);
  }

2496
  @override
2497
  void visitChildren(RenderObjectVisitor visitor) {
2498
    if (_child != null)
2499
      visitor(_child);
2500
  }
2501 2502

  @override
2503 2504
  String debugDescribeChildren(String prefix) {
    if (child != null)
2505
      return '$prefix \u2502\n${child.toStringDeep('$prefix \u2514\u2500child: ', '$prefix  ')}';
2506 2507 2508 2509
    return '';
  }
}

2510
/// Parent data to support a doubly-linked list of children.
2511
abstract class ContainerParentDataMixin<ChildType extends RenderObject> implements ParentData {
2512
  /// The previous sibling in the parent's child list.
2513
  ChildType previousSibling;
2514
  /// The next sibling in the parent's child list.
2515
  ChildType nextSibling;
2516 2517

  /// Clear the sibling pointers.
2518
  @override
2519 2520
  void detach() {
    super.detach();
2521
    if (previousSibling != null) {
Hixie's avatar
Hixie committed
2522
      final ContainerParentDataMixin<ChildType> previousSiblingParentData = previousSibling.parentData;
2523
      assert(previousSibling != this);
Hixie's avatar
Hixie committed
2524 2525
      assert(previousSiblingParentData.nextSibling == this);
      previousSiblingParentData.nextSibling = nextSibling;
2526 2527
    }
    if (nextSibling != null) {
Hixie's avatar
Hixie committed
2528
      final ContainerParentDataMixin<ChildType> nextSiblingParentData = nextSibling.parentData;
2529
      assert(nextSibling != this);
Hixie's avatar
Hixie committed
2530 2531
      assert(nextSiblingParentData.previousSibling == this);
      nextSiblingParentData.previousSibling = previousSibling;
2532 2533 2534 2535 2536 2537
    }
    previousSibling = null;
    nextSibling = null;
  }
}

2538
/// Generic mixin for render objects with a list of children.
2539 2540 2541
///
/// Provides a child model for a render object subclass that has a doubly-linked
/// list of children.
2542 2543 2544
abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, ParentDataType extends ContainerParentDataMixin<ChildType>> implements RenderObject {

  bool _debugUltimatePreviousSiblingOf(ChildType child, { ChildType equals }) {
Hixie's avatar
Hixie committed
2545 2546 2547 2548 2549
    ParentDataType childParentData = child.parentData;
    while (childParentData.previousSibling != null) {
      assert(childParentData.previousSibling != child);
      child = childParentData.previousSibling;
      childParentData = child.parentData;
2550 2551 2552 2553
    }
    return child == equals;
  }
  bool _debugUltimateNextSiblingOf(ChildType child, { ChildType equals }) {
Hixie's avatar
Hixie committed
2554 2555 2556 2557 2558
    ParentDataType childParentData = child.parentData;
    while (childParentData.nextSibling != null) {
      assert(childParentData.nextSibling != child);
      child = childParentData.nextSibling;
      childParentData = child.parentData;
2559 2560 2561 2562 2563
    }
    return child == equals;
  }

  int _childCount = 0;
2564
  /// The number of children.
2565 2566 2567 2568
  int get childCount => _childCount;

  ChildType _firstChild;
  ChildType _lastChild;
2569
  void _insertIntoChildList(ChildType child, { ChildType after }) {
Hixie's avatar
Hixie committed
2570 2571 2572
    final ParentDataType childParentData = child.parentData;
    assert(childParentData.nextSibling == null);
    assert(childParentData.previousSibling == null);
2573 2574
    _childCount += 1;
    assert(_childCount > 0);
2575 2576 2577 2578 2579 2580
    if (after == null) {
      // insert at the start (_firstChild)
      childParentData.nextSibling = _firstChild;
      if (_firstChild != null) {
        final ParentDataType _firstChildParentData = _firstChild.parentData;
        _firstChildParentData.previousSibling = child;
2581
      }
2582 2583 2584
      _firstChild = child;
      if (_lastChild == null)
        _lastChild = child;
2585 2586 2587
    } else {
      assert(_firstChild != null);
      assert(_lastChild != null);
2588 2589 2590 2591 2592 2593 2594 2595 2596
      assert(_debugUltimatePreviousSiblingOf(after, equals: _firstChild));
      assert(_debugUltimateNextSiblingOf(after, equals: _lastChild));
      final ParentDataType afterParentData = after.parentData;
      if (afterParentData.nextSibling == null) {
        // insert at the end (_lastChild); we'll end up with two or more children
        assert(after == _lastChild);
        childParentData.previousSibling = after;
        afterParentData.nextSibling = child;
        _lastChild = child;
2597 2598 2599
      } else {
        // insert in the middle; we'll end up with three or more children
        // set up links from child to siblings
2600 2601
        childParentData.nextSibling = afterParentData.nextSibling;
        childParentData.previousSibling = after;
2602
        // set up links from siblings to child
Hixie's avatar
Hixie committed
2603 2604 2605 2606
        final ParentDataType childPreviousSiblingParentData = childParentData.previousSibling.parentData;
        final ParentDataType childNextSiblingParentData = childParentData.nextSibling.parentData;
        childPreviousSiblingParentData.nextSibling = child;
        childNextSiblingParentData.previousSibling = child;
2607
        assert(afterParentData.nextSibling == child);
2608 2609 2610
      }
    }
  }
2611
  /// Insert child into this render object's child list after the given child.
2612 2613 2614
  ///
  /// If `after` is null, then this inserts the child at the start of the list,
  /// and the child becomes the new [firstChild].
2615
  void insert(ChildType child, { ChildType after }) {
2616
    assert(child != this);
2617 2618
    assert(after != this);
    assert(child != after);
2619 2620 2621
    assert(child != _firstChild);
    assert(child != _lastChild);
    adoptChild(child);
2622 2623 2624 2625 2626 2627
    _insertIntoChildList(child, after: after);
  }

  /// Append child to the end of this render object's child list.
  void add(ChildType child) {
    insert(child, after: _lastChild);
2628
  }
2629

2630
  /// Add all the children to the end of this render object's child list.
2631
  void addAll(List<ChildType> children) {
2632 2633 2634 2635
    if (children != null)
      for (ChildType child in children)
        add(child);
  }
2636

2637
  void _removeFromChildList(ChildType child) {
Hixie's avatar
Hixie committed
2638
    final ParentDataType childParentData = child.parentData;
2639 2640 2641
    assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild));
    assert(_debugUltimateNextSiblingOf(child, equals: _lastChild));
    assert(_childCount >= 0);
Hixie's avatar
Hixie committed
2642
    if (childParentData.previousSibling == null) {
2643
      assert(_firstChild == child);
Hixie's avatar
Hixie committed
2644
      _firstChild = childParentData.nextSibling;
2645
    } else {
Hixie's avatar
Hixie committed
2646 2647
      final ParentDataType childPreviousSiblingParentData = childParentData.previousSibling.parentData;
      childPreviousSiblingParentData.nextSibling = childParentData.nextSibling;
2648
    }
Hixie's avatar
Hixie committed
2649
    if (childParentData.nextSibling == null) {
2650
      assert(_lastChild == child);
Hixie's avatar
Hixie committed
2651
      _lastChild = childParentData.previousSibling;
2652
    } else {
Hixie's avatar
Hixie committed
2653 2654
      final ParentDataType childNextSiblingParentData = childParentData.nextSibling.parentData;
      childNextSiblingParentData.previousSibling = childParentData.previousSibling;
2655
    }
Hixie's avatar
Hixie committed
2656 2657
    childParentData.previousSibling = null;
    childParentData.nextSibling = null;
2658 2659
    _childCount -= 1;
  }
2660

2661
  /// Remove this child from the child list.
2662 2663
  ///
  /// Requires the child to be present in the child list.
2664 2665 2666 2667
  void remove(ChildType child) {
    _removeFromChildList(child);
    dropChild(child);
  }
2668

2669
  /// Remove all their children from this render object's child list.
2670 2671
  ///
  /// More efficient than removing them individually.
2672 2673 2674
  void removeAll() {
    ChildType child = _firstChild;
    while (child != null) {
Hixie's avatar
Hixie committed
2675 2676 2677 2678
      final ParentDataType childParentData = child.parentData;
      ChildType next = childParentData.nextSibling;
      childParentData.previousSibling = null;
      childParentData.nextSibling = null;
2679 2680 2681 2682 2683 2684 2685
      dropChild(child);
      child = next;
    }
    _firstChild = null;
    _lastChild = null;
    _childCount = 0;
  }
2686

2687
  /// Move this child in the child list to be before the given child.
2688 2689 2690 2691
  ///
  /// 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.
2692
  void move(ChildType child, { ChildType after }) {
2693
    assert(child != this);
2694 2695
    assert(after != this);
    assert(child != after);
2696
    assert(child.parent == this);
Hixie's avatar
Hixie committed
2697
    final ParentDataType childParentData = child.parentData;
2698
    if (childParentData.previousSibling == after)
2699 2700
      return;
    _removeFromChildList(child);
2701
    _insertIntoChildList(child, after: after);
2702
    markNeedsLayout();
2703
  }
2704

2705
  @override
2706 2707
  void attach(PipelineOwner owner) {
    super.attach(owner);
2708 2709
    ChildType child = _firstChild;
    while (child != null) {
2710
      child.attach(owner);
Hixie's avatar
Hixie committed
2711 2712
      final ParentDataType childParentData = child.parentData;
      child = childParentData.nextSibling;
2713 2714
    }
  }
2715

2716
  @override
2717 2718
  void detach() {
    super.detach();
2719 2720
    ChildType child = _firstChild;
    while (child != null) {
2721
      child.detach();
Hixie's avatar
Hixie committed
2722 2723
      final ParentDataType childParentData = child.parentData;
      child = childParentData.nextSibling;
2724 2725
    }
  }
2726

2727
  @override
2728
  void redepthChildren() {
2729 2730
    ChildType child = _firstChild;
    while (child != null) {
2731
      redepthChild(child);
Hixie's avatar
Hixie committed
2732 2733
      final ParentDataType childParentData = child.parentData;
      child = childParentData.nextSibling;
2734 2735
    }
  }
2736

2737
  @override
2738
  void visitChildren(RenderObjectVisitor visitor) {
2739 2740
    ChildType child = _firstChild;
    while (child != null) {
2741
      visitor(child);
Hixie's avatar
Hixie committed
2742 2743
      final ParentDataType childParentData = child.parentData;
      child = childParentData.nextSibling;
2744 2745 2746
    }
  }

2747
  /// The first child in the child list.
2748
  ChildType get firstChild => _firstChild;
2749

2750
  /// The last child in the child list.
2751
  ChildType get lastChild => _lastChild;
2752

2753 2754 2755 2756 2757 2758 2759 2760
  /// The previous child before the given child in the child list.
  ChildType childBefore(ChildType child) {
    assert(child != null);
    assert(child.parent == this);
    final ParentDataType childParentData = child.parentData;
    return childParentData.previousSibling;
  }

2761
  /// The next child after the given child in the child list.
2762
  ChildType childAfter(ChildType child) {
2763 2764
    assert(child != null);
    assert(child.parent == this);
Hixie's avatar
Hixie committed
2765 2766
    final ParentDataType childParentData = child.parentData;
    return childParentData.nextSibling;
2767 2768
  }

2769
  @override
2770
  String debugDescribeChildren(String prefix) {
2771 2772 2773
    if (firstChild != null) {
      String result = '$prefix \u2502\n';
      ChildType child = firstChild;
Hixie's avatar
Hixie committed
2774
      int count = 1;
2775
      while (child != lastChild) {
Hixie's avatar
Hixie committed
2776 2777 2778 2779 2780 2781
        result += '${child.toStringDeep("$prefix \u251C\u2500child $count: ", "$prefix \u2502")}';
        count += 1;
        final ParentDataType childParentData = child.parentData;
        child = childParentData.nextSibling;
      }
      if (child != null) {
2782
        assert(child == lastChild);
Hixie's avatar
Hixie committed
2783 2784
        result += '${child.toStringDeep("$prefix \u2514\u2500child $count: ", "$prefix  ")}';
      }
2785
      return result;
2786
    }
2787
    return '';
2788 2789
  }
}
2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804

/// Variant of [FlutterErrorDetails] with extra fields for the rendering
/// library.
class FlutterErrorDetailsForRendering extends FlutterErrorDetails {
  /// Creates a [FlutterErrorDetailsForRendering] object with the given
  /// arguments setting the object's properties.
  ///
  /// The rendering library calls this constructor when catching an exception
  /// that will subsequently be reported using [FlutterError.onError].
  const FlutterErrorDetailsForRendering({
    dynamic exception,
    StackTrace stack,
    String library,
    String context,
    this.renderObject,
2805
    InformationCollector informationCollector,
2806
    bool silent: false
2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818
  }) : super(
    exception: exception,
    stack: stack,
    library: library,
    context: context,
    informationCollector: informationCollector,
    silent: silent
  );

  /// The RenderObject that was being processed when the exception was caught.
  final RenderObject renderObject;
}