您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
1458 行
50 KiB
1458 行
50 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.UIWidgets.animation;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.gestures;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.ui;
|
|
using UnityEngine;
|
|
using Canvas = Unity.UIWidgets.ui.Canvas;
|
|
using Rect = Unity.UIWidgets.ui.Rect;
|
|
|
|
namespace Unity.UIWidgets.rendering {
|
|
public class ParentData {
|
|
public virtual void detach() {
|
|
}
|
|
|
|
public override string ToString() {
|
|
return "<none>";
|
|
}
|
|
}
|
|
|
|
public delegate void PaintingContextCallback(PaintingContext context, Offset offset);
|
|
|
|
public class PaintingContext : ClipContext {
|
|
PaintingContext(
|
|
ContainerLayer containerLayer = null,
|
|
Rect estimatedBounds = null
|
|
) {
|
|
D.assert(containerLayer != null);
|
|
D.assert(estimatedBounds != null);
|
|
this._containerLayer = containerLayer;
|
|
this.estimatedBounds = estimatedBounds;
|
|
}
|
|
|
|
readonly ContainerLayer _containerLayer;
|
|
|
|
public readonly Rect estimatedBounds;
|
|
|
|
public static void repaintCompositedChild(RenderObject child, bool debugAlsoPaintedParent = false) {
|
|
D.assert(child._needsPaint);
|
|
|
|
_repaintCompositedChild(
|
|
child,
|
|
debugAlsoPaintedParent: debugAlsoPaintedParent
|
|
);
|
|
}
|
|
|
|
static void _repaintCompositedChild(
|
|
RenderObject child,
|
|
bool debugAlsoPaintedParent = false,
|
|
PaintingContext childContext = null
|
|
) {
|
|
D.assert(child.isRepaintBoundary);
|
|
D.assert(() => {
|
|
child.debugRegisterRepaintBoundaryPaint(
|
|
includedParent: debugAlsoPaintedParent,
|
|
includedChild: true
|
|
);
|
|
return true;
|
|
});
|
|
if (child._layer == null) {
|
|
D.assert(debugAlsoPaintedParent);
|
|
child._layer = new OffsetLayer();
|
|
}
|
|
else {
|
|
D.assert(debugAlsoPaintedParent || child._layer.attached);
|
|
child._layer.removeAllChildren();
|
|
}
|
|
|
|
D.assert(() => {
|
|
child._layer.debugCreator = child.debugCreator ?? child.GetType().ToString();
|
|
return true;
|
|
});
|
|
childContext = childContext ?? new PaintingContext(child._layer, child.paintBounds);
|
|
child._paintWithContext(childContext, Offset.zero);
|
|
childContext.stopRecordingIfNeeded();
|
|
}
|
|
|
|
public static void debugInstrumentRepaintCompositedChild(
|
|
RenderObject child,
|
|
bool debugAlsoPaintedParent = false,
|
|
PaintingContext customContext = null
|
|
) {
|
|
D.assert(() => {
|
|
_repaintCompositedChild(
|
|
child,
|
|
debugAlsoPaintedParent: debugAlsoPaintedParent,
|
|
childContext: customContext
|
|
);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
public void paintChild(RenderObject child, Offset offset) {
|
|
if (child.isRepaintBoundary) {
|
|
this.stopRecordingIfNeeded();
|
|
this._compositeChild(child, offset);
|
|
}
|
|
else {
|
|
child._paintWithContext(this, offset);
|
|
}
|
|
}
|
|
|
|
void _compositeChild(RenderObject child, Offset offset) {
|
|
D.assert(!this._isRecording);
|
|
D.assert(child.isRepaintBoundary);
|
|
D.assert(this._canvas == null || this._canvas.getSaveCount() == 1);
|
|
|
|
if (child._needsPaint) {
|
|
repaintCompositedChild(child, debugAlsoPaintedParent: true);
|
|
}
|
|
else {
|
|
D.assert(child._layer != null);
|
|
D.assert(() => {
|
|
child.debugRegisterRepaintBoundaryPaint(
|
|
includedParent: true,
|
|
includedChild: false
|
|
);
|
|
child._layer.debugCreator = child.debugCreator ?? child;
|
|
return true;
|
|
});
|
|
}
|
|
|
|
child._layer.offset = offset;
|
|
this.appendLayer(child._layer);
|
|
}
|
|
|
|
protected virtual void appendLayer(Layer layer) {
|
|
D.assert(!this._isRecording);
|
|
|
|
layer.remove();
|
|
this._containerLayer.append(layer);
|
|
}
|
|
|
|
bool _isRecording {
|
|
get {
|
|
bool hasCanvas = this._canvas != null;
|
|
D.assert(() => {
|
|
if (hasCanvas) {
|
|
D.assert(this._currentLayer != null);
|
|
D.assert(this._recorder != null);
|
|
D.assert(this._canvas != null);
|
|
}
|
|
else {
|
|
D.assert(this._currentLayer == null);
|
|
D.assert(this._recorder == null);
|
|
D.assert(this._canvas == null);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
return hasCanvas;
|
|
}
|
|
}
|
|
|
|
PictureLayer _currentLayer;
|
|
PictureRecorder _recorder;
|
|
Canvas _canvas;
|
|
|
|
public override Canvas canvas {
|
|
get {
|
|
if (this._canvas == null) {
|
|
this._startRecording();
|
|
}
|
|
|
|
return this._canvas;
|
|
}
|
|
}
|
|
|
|
void _startRecording() {
|
|
D.assert(!this._isRecording);
|
|
|
|
this._currentLayer = new PictureLayer(this.estimatedBounds);
|
|
this._recorder = new PictureRecorder();
|
|
this._canvas = new RecorderCanvas(this._recorder);
|
|
this._containerLayer.append(this._currentLayer);
|
|
}
|
|
|
|
protected virtual void stopRecordingIfNeeded() {
|
|
if (!this._isRecording) {
|
|
return;
|
|
}
|
|
|
|
D.assert(() => {
|
|
if (D.debugRepaintRainbowEnabled) {
|
|
var paint = new Paint {
|
|
style = PaintingStyle.stroke,
|
|
strokeWidth = 6.0f,
|
|
color = D.debugCurrentRepaintColor.toColor()
|
|
};
|
|
this.canvas.drawRect(this.estimatedBounds.deflate(3.0f), paint);
|
|
}
|
|
|
|
if (D.debugPaintLayerBordersEnabled) {
|
|
Paint paint = new Paint {
|
|
style = PaintingStyle.stroke,
|
|
strokeWidth = 1.0f,
|
|
color = new ui.Color(0xFFFF9800),
|
|
};
|
|
this.canvas.drawRect(this.estimatedBounds, paint);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
this._currentLayer.picture = this._recorder.endRecording();
|
|
this._currentLayer = null;
|
|
this._recorder = null;
|
|
this._canvas = null;
|
|
}
|
|
|
|
public void setIsComplexHint() {
|
|
if (this._currentLayer != null) {
|
|
this._currentLayer.isComplexHint = true;
|
|
}
|
|
}
|
|
|
|
public void setWillChangeHint() {
|
|
if (this._currentLayer != null) {
|
|
this._currentLayer.willChangeHint = true;
|
|
}
|
|
}
|
|
|
|
public void addLayer(Layer layer) {
|
|
this.stopRecordingIfNeeded();
|
|
this.appendLayer(layer);
|
|
}
|
|
|
|
public void pushLayer(ContainerLayer childLayer, PaintingContextCallback painter, Offset offset,
|
|
Rect childPaintBounds = null) {
|
|
D.assert(!childLayer.attached);
|
|
D.assert(childLayer.parent == null);
|
|
D.assert(painter != null);
|
|
|
|
this.stopRecordingIfNeeded();
|
|
this.appendLayer(childLayer);
|
|
|
|
var childContext = this.createChildContext(childLayer, childPaintBounds ?? this.estimatedBounds);
|
|
painter(childContext, offset);
|
|
childContext.stopRecordingIfNeeded();
|
|
}
|
|
|
|
protected PaintingContext createChildContext(ContainerLayer childLayer, Rect bounds) {
|
|
return new PaintingContext(childLayer, bounds);
|
|
}
|
|
|
|
public void pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter,
|
|
Clip clipBehavior = Clip.hardEdge) {
|
|
Rect offsetClipRect = clipRect.shift(offset);
|
|
if (needsCompositing) {
|
|
this.pushLayer(new ClipRectLayer(offsetClipRect, clipBehavior: clipBehavior),
|
|
painter, offset, childPaintBounds: offsetClipRect);
|
|
}
|
|
else {
|
|
this.clipRectAndPaint(offsetClipRect, clipBehavior, offsetClipRect, () => painter(this, offset));
|
|
}
|
|
}
|
|
|
|
public void pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, RRect clipRRect,
|
|
PaintingContextCallback painter, Clip clipBehavior = Clip.antiAlias) {
|
|
Rect offsetBounds = bounds.shift(offset);
|
|
RRect offsetClipRRect = clipRRect.shift(offset);
|
|
if (needsCompositing) {
|
|
this.pushLayer(new ClipRRectLayer(offsetClipRRect, clipBehavior: clipBehavior),
|
|
painter, offset, childPaintBounds: offsetBounds);
|
|
}
|
|
else {
|
|
this.clipRRectAndPaint(offsetClipRRect, clipBehavior, offsetBounds, () => painter(this, offset));
|
|
}
|
|
}
|
|
|
|
public void pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath,
|
|
PaintingContextCallback painter, Clip clipBehavior = Clip.antiAlias) {
|
|
Rect offsetBounds = bounds.shift(offset);
|
|
Path offsetClipPath = clipPath.shift(offset);
|
|
if (needsCompositing) {
|
|
this.pushLayer(new ClipPathLayer(clipPath: offsetClipPath, clipBehavior: clipBehavior), painter, offset,
|
|
childPaintBounds: offsetBounds);
|
|
}
|
|
else {
|
|
this.clipPathAndPaint(offsetClipPath, clipBehavior, offsetBounds, () => painter(this, offset));
|
|
}
|
|
}
|
|
|
|
public void pushTransform(bool needsCompositing, Offset offset, Matrix3 transform,
|
|
PaintingContextCallback painter) {
|
|
Matrix3 effectiveTransform;
|
|
if (offset == null || offset == Offset.zero) {
|
|
effectiveTransform = transform;
|
|
} else {
|
|
effectiveTransform = Matrix3.makeTrans(offset.dx, offset.dy);
|
|
effectiveTransform.preConcat(transform);
|
|
effectiveTransform.preTranslate(-offset.dx, -offset.dy);
|
|
}
|
|
|
|
if (needsCompositing) {
|
|
var inverse = Matrix3.I();
|
|
var invertible = effectiveTransform.invert(inverse);
|
|
|
|
// it could just be "scale == 0", ignore the assertion.
|
|
// D.assert(invertible);
|
|
|
|
this.pushLayer(
|
|
new TransformLayer(effectiveTransform),
|
|
painter,
|
|
offset,
|
|
childPaintBounds: inverse.mapRect(this.estimatedBounds)
|
|
);
|
|
}
|
|
else {
|
|
this.canvas.save();
|
|
this.canvas.concat(effectiveTransform);
|
|
painter(this, offset);
|
|
this.canvas.restore();
|
|
}
|
|
}
|
|
|
|
public void pushOpacity(Offset offset, int alpha, PaintingContextCallback painter) {
|
|
this.pushLayer(new OpacityLayer(alpha: alpha), painter, offset);
|
|
}
|
|
|
|
public override string ToString() {
|
|
return
|
|
$"{this.GetType()}#{this.GetHashCode()}(layer: {this._containerLayer}, canvas bounds: {this.estimatedBounds}";
|
|
}
|
|
}
|
|
|
|
public abstract class Constraints {
|
|
public abstract bool isTight { get; }
|
|
|
|
public abstract bool isNormalized { get; }
|
|
|
|
public virtual bool debugAssertIsValid(
|
|
bool isAppliedConstraint = false,
|
|
InformationCollector informationCollector = null
|
|
) {
|
|
D.assert(this.isNormalized);
|
|
return this.isNormalized;
|
|
}
|
|
}
|
|
|
|
public delegate void RenderObjectVisitor(RenderObject child);
|
|
|
|
public delegate void LayoutCallback<T>(T constraints) where T : Constraints;
|
|
|
|
public class PipelineOwner {
|
|
public PipelineOwner(
|
|
VoidCallback onNeedVisualUpdate = null) {
|
|
this.onNeedVisualUpdate = onNeedVisualUpdate;
|
|
}
|
|
|
|
public readonly VoidCallback onNeedVisualUpdate;
|
|
|
|
public void requestVisualUpdate() {
|
|
if (this.onNeedVisualUpdate != null) {
|
|
this.onNeedVisualUpdate();
|
|
}
|
|
}
|
|
|
|
public AbstractNodeMixinDiagnosticableTree rootNode {
|
|
get { return this._rootNode; }
|
|
set {
|
|
if (this._rootNode == value) {
|
|
return;
|
|
}
|
|
|
|
if (this._rootNode != null) {
|
|
this._rootNode.detach();
|
|
}
|
|
|
|
this._rootNode = value;
|
|
if (this._rootNode != null) {
|
|
this._rootNode.attach(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
AbstractNodeMixinDiagnosticableTree _rootNode;
|
|
|
|
internal List<RenderObject> _nodesNeedingLayout = new List<RenderObject>();
|
|
|
|
public bool debugDoingLayout {
|
|
get { return this._debugDoingLayout; }
|
|
}
|
|
|
|
internal bool _debugDoingLayout = false;
|
|
|
|
public void flushLayout() {
|
|
D.assert(() => {
|
|
this._debugDoingLayout = true;
|
|
return true;
|
|
});
|
|
|
|
try {
|
|
while (this._nodesNeedingLayout.isNotEmpty()) {
|
|
var dirtyNodes = this._nodesNeedingLayout;
|
|
this._nodesNeedingLayout = new List<RenderObject>();
|
|
dirtyNodes.Sort((a, b) => a.depth - b.depth);
|
|
foreach (var node in dirtyNodes) {
|
|
if (node._needsLayout && node.owner == this) {
|
|
node._layoutWithoutResize();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
D.assert(() => {
|
|
this._debugDoingLayout = false;
|
|
return true;
|
|
});
|
|
}
|
|
}
|
|
|
|
internal bool _debugAllowMutationsToDirtySubtrees = false;
|
|
|
|
internal void _enableMutationsToDirtySubtrees(VoidCallback callback) {
|
|
D.assert(this._debugDoingLayout);
|
|
bool oldState = false;
|
|
D.assert(() => {
|
|
oldState = this._debugAllowMutationsToDirtySubtrees;
|
|
this._debugAllowMutationsToDirtySubtrees = true;
|
|
return true;
|
|
});
|
|
try {
|
|
callback();
|
|
}
|
|
finally {
|
|
D.assert(() => {
|
|
this._debugAllowMutationsToDirtySubtrees = oldState;
|
|
return true;
|
|
});
|
|
}
|
|
}
|
|
|
|
internal List<RenderObject> _nodesNeedingCompositingBitsUpdate = new List<RenderObject>();
|
|
|
|
public void flushCompositingBits() {
|
|
this._nodesNeedingCompositingBitsUpdate.Sort((a, b) => a.depth - b.depth);
|
|
foreach (RenderObject node in this._nodesNeedingCompositingBitsUpdate) {
|
|
if (node._needsCompositingBitsUpdate && node.owner == this) {
|
|
node._updateCompositingBits();
|
|
}
|
|
}
|
|
|
|
this._nodesNeedingCompositingBitsUpdate.Clear();
|
|
}
|
|
|
|
internal List<RenderObject> _nodesNeedingPaint = new List<RenderObject>();
|
|
|
|
public bool debugDoingPaint {
|
|
get { return this._debugDoingPaint; }
|
|
}
|
|
|
|
internal bool _debugDoingPaint = false;
|
|
|
|
public void flushPaint() {
|
|
D.assert(() => {
|
|
this._debugDoingPaint = true;
|
|
return true;
|
|
});
|
|
|
|
try {
|
|
var dirtyNodes = this._nodesNeedingPaint;
|
|
this._nodesNeedingPaint = new List<RenderObject>();
|
|
dirtyNodes.Sort((a, b) => a.depth - b.depth);
|
|
foreach (var node in dirtyNodes) {
|
|
D.assert(node._layer != null);
|
|
if (node._needsPaint && node.owner == this) {
|
|
if (node._layer.attached) {
|
|
PaintingContext.repaintCompositedChild(node);
|
|
}
|
|
else {
|
|
node._skippedPaintingOnLayer();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
D.assert(() => {
|
|
this._debugDoingPaint = false;
|
|
return true;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
public abstract class RenderObject : AbstractNodeMixinDiagnosticableTree, HitTestTarget {
|
|
protected RenderObject() {
|
|
this._needsCompositing = this.isRepaintBoundary || this.alwaysNeedsCompositing;
|
|
}
|
|
|
|
public ParentData parentData;
|
|
|
|
public virtual void setupParentData(RenderObject child) {
|
|
D.assert(this._debugCanPerformMutations);
|
|
|
|
if (!(child.parentData is ParentData)) {
|
|
child.parentData = new ParentData();
|
|
}
|
|
}
|
|
|
|
protected override void adoptChild(AbstractNodeMixinDiagnosticableTree childNode) {
|
|
var child = (RenderObject) childNode;
|
|
|
|
D.assert(this._debugCanPerformMutations);
|
|
D.assert(child != null);
|
|
this.setupParentData(child);
|
|
this.markNeedsLayout();
|
|
this.markNeedsCompositingBitsUpdate();
|
|
base.adoptChild(child);
|
|
}
|
|
|
|
protected override void dropChild(AbstractNodeMixinDiagnosticableTree childNode) {
|
|
var child = (RenderObject) childNode;
|
|
|
|
D.assert(this._debugCanPerformMutations);
|
|
D.assert(child != null);
|
|
D.assert(child.parentData != null);
|
|
child._cleanRelayoutBoundary();
|
|
child.parentData.detach();
|
|
child.parentData = null;
|
|
base.dropChild(child);
|
|
this.markNeedsLayout();
|
|
this.markNeedsCompositingBitsUpdate();
|
|
}
|
|
|
|
public virtual void visitChildren(RenderObjectVisitor visitor) {
|
|
}
|
|
|
|
public object debugCreator;
|
|
|
|
void _debugReportException(string method, Exception exception) {
|
|
UIWidgetsError.reportError(new UIWidgetsErrorDetailsForRendering(
|
|
exception: exception,
|
|
library: "rendering library",
|
|
context: "during " + method,
|
|
renderObject: this,
|
|
informationCollector: information => {
|
|
information.AppendLine(
|
|
"The following RenderObject was being processed when the exception was fired:");
|
|
information.AppendLine(" " + this.toStringShallow(joiner: "\n "));
|
|
var descendants = new List<string>();
|
|
const int maxDepth = 5;
|
|
int depth = 0;
|
|
const int maxLines = 25;
|
|
int lines = 0;
|
|
RenderObjectVisitor visitor = null;
|
|
visitor = new RenderObjectVisitor((RenderObject child) => {
|
|
if (lines < maxLines) {
|
|
depth += 1;
|
|
descendants.Add(new string(' ', 2 * depth) + child);
|
|
if (depth < maxDepth) {
|
|
child.visitChildren(visitor);
|
|
}
|
|
|
|
depth -= 1;
|
|
}
|
|
else if (lines == maxLines) {
|
|
descendants.Add(" ...(descendants list truncated after " + lines + " lines)");
|
|
}
|
|
|
|
lines += 1;
|
|
});
|
|
this.visitChildren(visitor);
|
|
if (lines > 1) {
|
|
information.AppendLine(
|
|
"This RenderObject had the following descendants (showing up to depth " +
|
|
maxDepth + "):");
|
|
}
|
|
else if (descendants.Count == 1) {
|
|
information.AppendLine("This RenderObject had the following child:");
|
|
}
|
|
else {
|
|
information.AppendLine("This RenderObject has no descendants.");
|
|
}
|
|
|
|
information.Append(string.Join("\n", descendants.ToArray()));
|
|
}
|
|
));
|
|
}
|
|
|
|
public bool debugDoingThisResize {
|
|
get { return this._debugDoingThisResize; }
|
|
}
|
|
|
|
bool _debugDoingThisResize = false;
|
|
|
|
public bool debugDoingThisLayout {
|
|
get { return this._debugDoingThisLayout; }
|
|
}
|
|
|
|
bool _debugDoingThisLayout = false;
|
|
|
|
public static RenderObject debugActiveLayout {
|
|
get { return _debugActiveLayout; }
|
|
}
|
|
|
|
static RenderObject _debugActiveLayout;
|
|
|
|
public bool debugCanParentUseSize {
|
|
get { return this._debugCanParentUseSize; }
|
|
}
|
|
|
|
bool _debugCanParentUseSize;
|
|
|
|
bool _debugMutationsLocked = false;
|
|
|
|
bool _debugCanPerformMutations {
|
|
get {
|
|
bool result = true;
|
|
D.assert(() => {
|
|
RenderObject node = this;
|
|
while (true) {
|
|
if (node._doingThisLayoutWithCallback) {
|
|
result = true;
|
|
break;
|
|
}
|
|
|
|
if (this.owner != null && this.owner._debugAllowMutationsToDirtySubtrees && node._needsLayout) {
|
|
result = true;
|
|
break;
|
|
}
|
|
|
|
if (node._debugMutationsLocked) {
|
|
result = false;
|
|
break;
|
|
}
|
|
|
|
if (!(node.parent is RenderObject)) {
|
|
result = true;
|
|
break;
|
|
}
|
|
|
|
node = (RenderObject) node.parent;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public new PipelineOwner owner {
|
|
get { return (PipelineOwner) base.owner; }
|
|
}
|
|
|
|
public override void attach(object ownerObject) {
|
|
var owner = (PipelineOwner) ownerObject;
|
|
|
|
base.attach(owner);
|
|
if (this._needsLayout && this._relayoutBoundary != null) {
|
|
this._needsLayout = false;
|
|
this.markNeedsLayout();
|
|
}
|
|
|
|
if (this._needsCompositingBitsUpdate) {
|
|
this._needsCompositingBitsUpdate = false;
|
|
this.markNeedsCompositingBitsUpdate();
|
|
}
|
|
|
|
if (this._needsPaint && this._layer != null) {
|
|
this._needsPaint = false;
|
|
this.markNeedsPaint();
|
|
}
|
|
}
|
|
|
|
public bool debugNeedsLayout {
|
|
get {
|
|
bool result = false;
|
|
D.assert(() => {
|
|
result = this._needsLayout;
|
|
return true;
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
internal bool _needsLayout = true;
|
|
|
|
public RenderObject _relayoutBoundary;
|
|
bool _doingThisLayoutWithCallback = false;
|
|
|
|
public Constraints constraints {
|
|
get { return this._constraints; }
|
|
}
|
|
|
|
Constraints _constraints;
|
|
|
|
protected abstract void debugAssertDoesMeetConstraints();
|
|
|
|
internal static bool debugCheckingIntrinsics = false;
|
|
|
|
bool _debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout() {
|
|
if (this._relayoutBoundary == null) {
|
|
return true;
|
|
}
|
|
|
|
RenderObject node = this;
|
|
while (node != this._relayoutBoundary) {
|
|
D.assert(node._relayoutBoundary == this._relayoutBoundary);
|
|
D.assert(node.parent != null);
|
|
node = (RenderObject) node.parent;
|
|
if ((!node._needsLayout) && (!node._debugDoingThisLayout)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
D.assert(node._relayoutBoundary == node);
|
|
return true;
|
|
}
|
|
|
|
public virtual void markNeedsLayout() {
|
|
D.assert(this._debugCanPerformMutations);
|
|
if (this._needsLayout) {
|
|
D.assert(this._debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout());
|
|
return;
|
|
}
|
|
|
|
D.assert(this._relayoutBoundary != null);
|
|
if (this._relayoutBoundary != this) {
|
|
this.markParentNeedsLayout();
|
|
}
|
|
else {
|
|
this._needsLayout = true;
|
|
if (this.owner != null) {
|
|
D.assert(() => {
|
|
if (D.debugPrintMarkNeedsLayoutStacks) {
|
|
Debug.Log("markNeedsLayout() called for " + this);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
this.owner._nodesNeedingLayout.Add(this);
|
|
this.owner.requestVisualUpdate();
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void markParentNeedsLayout() {
|
|
this._needsLayout = true;
|
|
|
|
RenderObject parent = (RenderObject) this.parent;
|
|
if (!this._doingThisLayoutWithCallback) {
|
|
parent.markNeedsLayout();
|
|
}
|
|
else {
|
|
D.assert(parent._debugDoingThisLayout);
|
|
}
|
|
}
|
|
|
|
public void markNeedsLayoutForSizedByParentChange() {
|
|
this.markNeedsLayout();
|
|
this.markParentNeedsLayout();
|
|
}
|
|
|
|
void _cleanRelayoutBoundary() {
|
|
if (this._relayoutBoundary != this) {
|
|
this._relayoutBoundary = null;
|
|
this._needsLayout = true;
|
|
this.visitChildren(child => { child._cleanRelayoutBoundary(); });
|
|
}
|
|
}
|
|
|
|
public void scheduleInitialLayout() {
|
|
D.assert(this.attached);
|
|
D.assert(!(this.parent is RenderObject));
|
|
D.assert(!this.owner._debugDoingLayout);
|
|
D.assert(this._relayoutBoundary == null);
|
|
|
|
this._relayoutBoundary = this;
|
|
D.assert(() => {
|
|
this._debugCanParentUseSize = false;
|
|
return true;
|
|
});
|
|
this.owner._nodesNeedingLayout.Add(this);
|
|
}
|
|
|
|
internal void _layoutWithoutResize() {
|
|
D.assert(this._relayoutBoundary == this);
|
|
RenderObject debugPreviousActiveLayout = null;
|
|
D.assert(!this._debugMutationsLocked);
|
|
D.assert(!this._doingThisLayoutWithCallback);
|
|
D.assert(() => {
|
|
this._debugMutationsLocked = true;
|
|
this._debugDoingThisLayout = true;
|
|
debugPreviousActiveLayout = _debugActiveLayout;
|
|
_debugActiveLayout = this;
|
|
if (D.debugPrintLayouts) {
|
|
Debug.Log("Laying out (without resize) " + this);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
try {
|
|
this.performLayout();
|
|
}
|
|
catch (Exception ex) {
|
|
this._debugReportException("performLayout", ex);
|
|
}
|
|
|
|
D.assert(() => {
|
|
_debugActiveLayout = debugPreviousActiveLayout;
|
|
this._debugDoingThisLayout = false;
|
|
this._debugMutationsLocked = false;
|
|
return true;
|
|
});
|
|
|
|
this._needsLayout = false;
|
|
this.markNeedsPaint();
|
|
}
|
|
|
|
public void layout(Constraints constraints, bool parentUsesSize = false) {
|
|
D.assert(constraints != null);
|
|
D.assert(constraints.debugAssertIsValid(
|
|
isAppliedConstraint: true,
|
|
informationCollector: information => {
|
|
// final List<String> stack = StackTrace.current.toString().split('\n');
|
|
// int targetFrame;
|
|
// final Pattern layoutFramePattern = 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(
|
|
// 'These invalid constraints were provided to $runtimeType\'s layout() '
|
|
// 'function by the following function, which probably computed the '
|
|
// 'invalid constraints in question:'
|
|
// );
|
|
// final Pattern targetFramePattern = RegExp(r'^#[0-9]+ +(.+)$');
|
|
// final Match targetFrameMatch = targetFramePattern.matchAsPrefix(stack[targetFrame]);
|
|
// if (targetFrameMatch != null && targetFrameMatch.groupCount > 0) {
|
|
// information.writeln(' ${targetFrameMatch.group(1)}');
|
|
// } else {
|
|
// information.writeln(stack[targetFrame]);
|
|
// }
|
|
// }
|
|
}));
|
|
D.assert(!this._debugDoingThisResize);
|
|
D.assert(!this._debugDoingThisLayout);
|
|
|
|
RenderObject relayoutBoundary;
|
|
if (!parentUsesSize || this.sizedByParent || constraints.isTight || !(this.parent is RenderObject)) {
|
|
relayoutBoundary = this;
|
|
}
|
|
else {
|
|
RenderObject parent = (RenderObject) this.parent;
|
|
relayoutBoundary = parent._relayoutBoundary;
|
|
}
|
|
|
|
D.assert(() => {
|
|
this._debugCanParentUseSize = parentUsesSize;
|
|
return true;
|
|
});
|
|
|
|
if (!this._needsLayout && Equals(constraints, this._constraints) &&
|
|
relayoutBoundary == this._relayoutBoundary) {
|
|
D.assert(() => {
|
|
this._debugDoingThisResize = this.sizedByParent;
|
|
this._debugDoingThisLayout = !this.sizedByParent;
|
|
RenderObject debugPreviousActiveLayout1 = _debugActiveLayout;
|
|
_debugActiveLayout = this;
|
|
this.debugResetSize();
|
|
_debugActiveLayout = debugPreviousActiveLayout1;
|
|
this._debugDoingThisLayout = false;
|
|
this._debugDoingThisResize = false;
|
|
return true;
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
this._constraints = constraints;
|
|
this._relayoutBoundary = relayoutBoundary;
|
|
|
|
D.assert(!this._debugMutationsLocked);
|
|
D.assert(!this._doingThisLayoutWithCallback);
|
|
D.assert(() => {
|
|
this._debugMutationsLocked = true;
|
|
if (D.debugPrintLayouts) {
|
|
Debug.Log("Laying out (" + (this.sizedByParent ? "with separate resize" : "with resize allowed") +
|
|
") " + this);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
if (this.sizedByParent) {
|
|
D.assert(() => {
|
|
this._debugDoingThisResize = true;
|
|
return true;
|
|
});
|
|
|
|
try {
|
|
this.performResize();
|
|
D.assert(() => {
|
|
this.debugAssertDoesMeetConstraints();
|
|
return true;
|
|
});
|
|
}
|
|
catch (Exception ex) {
|
|
this._debugReportException("performResize", ex);
|
|
}
|
|
|
|
D.assert(() => {
|
|
this._debugDoingThisResize = false;
|
|
return true;
|
|
});
|
|
}
|
|
|
|
RenderObject debugPreviousActiveLayout = null;
|
|
D.assert(() => {
|
|
this._debugDoingThisLayout = true;
|
|
debugPreviousActiveLayout = _debugActiveLayout;
|
|
_debugActiveLayout = this;
|
|
return true;
|
|
});
|
|
|
|
try {
|
|
this.performLayout();
|
|
D.assert(() => {
|
|
this.debugAssertDoesMeetConstraints();
|
|
return true;
|
|
});
|
|
}
|
|
catch (Exception ex) {
|
|
this._debugReportException("performLayout", ex);
|
|
}
|
|
|
|
D.assert(() => {
|
|
_debugActiveLayout = debugPreviousActiveLayout;
|
|
this._debugDoingThisLayout = false;
|
|
this._debugMutationsLocked = false;
|
|
return true;
|
|
});
|
|
|
|
this._needsLayout = false;
|
|
this.markNeedsPaint();
|
|
}
|
|
|
|
protected virtual void debugResetSize() {
|
|
}
|
|
|
|
protected virtual bool sizedByParent {
|
|
get { return false; }
|
|
}
|
|
|
|
protected abstract void performResize();
|
|
|
|
protected abstract void performLayout();
|
|
|
|
protected void invokeLayoutCallback<T>(LayoutCallback<T> callback) where T : Constraints {
|
|
D.assert(this._debugMutationsLocked);
|
|
D.assert(this._debugDoingThisLayout);
|
|
D.assert(!this._doingThisLayoutWithCallback);
|
|
|
|
this._doingThisLayoutWithCallback = true;
|
|
try {
|
|
this.owner._enableMutationsToDirtySubtrees(() => { callback((T) this.constraints); });
|
|
}
|
|
finally {
|
|
this._doingThisLayoutWithCallback = false;
|
|
}
|
|
}
|
|
|
|
public bool debugDoingThisPaint {
|
|
get { return this._debugDoingThisPaint; }
|
|
}
|
|
|
|
bool _debugDoingThisPaint = false;
|
|
|
|
public static RenderObject debugActivePaint {
|
|
get { return _debugActivePaint; }
|
|
}
|
|
|
|
static RenderObject _debugActivePaint;
|
|
|
|
public virtual bool isRepaintBoundary {
|
|
get { return false; }
|
|
}
|
|
|
|
public virtual void debugRegisterRepaintBoundaryPaint(bool includedParent = true, bool includedChild = false) {
|
|
}
|
|
|
|
protected virtual bool alwaysNeedsCompositing {
|
|
get { return false; }
|
|
}
|
|
|
|
internal OffsetLayer _layer;
|
|
|
|
public OffsetLayer layer {
|
|
get {
|
|
D.assert(this.isRepaintBoundary,
|
|
() => "You can only access RenderObject.layer for render objects that are repaint boundaries.");
|
|
D.assert(!this._needsPaint);
|
|
|
|
return this._layer;
|
|
}
|
|
}
|
|
|
|
public OffsetLayer debugLayer {
|
|
get {
|
|
OffsetLayer result = null;
|
|
D.assert(() => {
|
|
result = this._layer;
|
|
return true;
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
internal bool _needsCompositingBitsUpdate = false;
|
|
|
|
public void markNeedsCompositingBitsUpdate() {
|
|
if (this._needsCompositingBitsUpdate) {
|
|
return;
|
|
}
|
|
|
|
this._needsCompositingBitsUpdate = true;
|
|
|
|
if (this.parent is RenderObject) {
|
|
var parent = (RenderObject) this.parent;
|
|
if (parent._needsCompositingBitsUpdate) {
|
|
return;
|
|
}
|
|
|
|
if (!this.isRepaintBoundary && !parent.isRepaintBoundary) {
|
|
parent.markNeedsCompositingBitsUpdate();
|
|
return;
|
|
}
|
|
}
|
|
|
|
D.assert(() => {
|
|
var parent = this.parent;
|
|
if (parent is RenderObject) {
|
|
return ((RenderObject) parent)._needsCompositing;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
if (this.owner != null) {
|
|
this.owner._nodesNeedingCompositingBitsUpdate.Add(this);
|
|
}
|
|
}
|
|
|
|
bool _needsCompositing;
|
|
|
|
public bool needsCompositing {
|
|
get {
|
|
D.assert(!this._needsCompositingBitsUpdate);
|
|
return this._needsCompositing;
|
|
}
|
|
}
|
|
|
|
public void _updateCompositingBits() {
|
|
if (!this._needsCompositingBitsUpdate) {
|
|
return;
|
|
}
|
|
|
|
bool oldNeedsCompositing = this._needsCompositing;
|
|
this._needsCompositing = false;
|
|
this.visitChildren(child => {
|
|
child._updateCompositingBits();
|
|
if (child.needsCompositing) {
|
|
this._needsCompositing = true;
|
|
}
|
|
});
|
|
|
|
if (this.isRepaintBoundary || this.alwaysNeedsCompositing) {
|
|
this._needsCompositing = true;
|
|
}
|
|
|
|
if (oldNeedsCompositing != this._needsCompositing) {
|
|
this.markNeedsPaint();
|
|
}
|
|
|
|
this._needsCompositingBitsUpdate = false;
|
|
}
|
|
|
|
bool debugNeedsPaint {
|
|
get {
|
|
bool result = false;
|
|
D.assert(() => {
|
|
result = this._needsPaint;
|
|
return true;
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
internal bool _needsPaint = true;
|
|
|
|
public void markNeedsPaint() {
|
|
D.assert(this.owner == null || !this.owner.debugDoingPaint);
|
|
|
|
if (this._needsPaint) {
|
|
return;
|
|
}
|
|
|
|
this._needsPaint = true;
|
|
if (this.isRepaintBoundary) {
|
|
D.assert(() => {
|
|
if (D.debugPrintMarkNeedsPaintStacks) {
|
|
Debug.Log("markNeedsPaint() called for " + this);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
D.assert(this._layer != null);
|
|
|
|
if (this.owner != null) {
|
|
this.owner._nodesNeedingPaint.Add(this);
|
|
this.owner.requestVisualUpdate();
|
|
}
|
|
}
|
|
else if (this.parent is RenderObject) {
|
|
D.assert(this._layer == null);
|
|
var parent = (RenderObject) this.parent;
|
|
parent.markNeedsPaint();
|
|
}
|
|
else {
|
|
D.assert(() => {
|
|
if (D.debugPrintMarkNeedsPaintStacks) {
|
|
Debug.Log("markNeedsPaint() called for " + this + " (root of render tree)");
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
if (this.owner != null) {
|
|
this.owner.requestVisualUpdate();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void _skippedPaintingOnLayer() {
|
|
D.assert(this.attached);
|
|
D.assert(this.isRepaintBoundary);
|
|
D.assert(this._needsPaint);
|
|
D.assert(this._layer != null);
|
|
D.assert(!this._layer.attached);
|
|
|
|
var ancestor = this.parent;
|
|
while (ancestor is RenderObject) {
|
|
var node = (RenderObject) ancestor;
|
|
if (node.isRepaintBoundary) {
|
|
if (node._layer == null) {
|
|
break;
|
|
}
|
|
|
|
if (node._layer.attached) {
|
|
break;
|
|
}
|
|
|
|
node._needsPaint = true;
|
|
}
|
|
|
|
ancestor = ancestor.parent;
|
|
}
|
|
}
|
|
|
|
public void scheduleInitialPaint(ContainerLayer rootLayer) {
|
|
D.assert(rootLayer.attached);
|
|
D.assert(this.attached);
|
|
D.assert(!(this.parent is RenderObject));
|
|
D.assert(!this.owner._debugDoingPaint);
|
|
D.assert(this.isRepaintBoundary);
|
|
D.assert(this._layer == null);
|
|
|
|
this._layer = (OffsetLayer) rootLayer;
|
|
D.assert(this._needsPaint);
|
|
this.owner._nodesNeedingPaint.Add(this);
|
|
}
|
|
|
|
public void replaceRootLayer(OffsetLayer rootLayer) {
|
|
D.assert(rootLayer.attached);
|
|
D.assert(this.attached);
|
|
D.assert(!(this.parent is RenderObject));
|
|
D.assert(!this.owner._debugDoingPaint);
|
|
D.assert(this.isRepaintBoundary);
|
|
D.assert(this._layer != null);
|
|
|
|
|
|
this._layer.detach();
|
|
this._layer = rootLayer;
|
|
this.markNeedsPaint();
|
|
}
|
|
|
|
internal void _paintWithContext(PaintingContext context, Offset offset) {
|
|
D.assert(() => {
|
|
if (this._debugDoingThisPaint) {
|
|
throw new UIWidgetsError(
|
|
"Tried to paint a RenderObject reentrantly.\n" +
|
|
"The following RenderObject was already being painted when it was " +
|
|
"painted again:\n" +
|
|
" " + this.toStringShallow(joiner: "\n ") + "\n" +
|
|
"Since this typically indicates an infinite recursion, it is " +
|
|
"disallowed."
|
|
);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
if (this._needsLayout) {
|
|
return;
|
|
}
|
|
|
|
D.assert(() => {
|
|
if (this._needsCompositingBitsUpdate) {
|
|
throw new UIWidgetsError(
|
|
"Tried to paint a RenderObject before its compositing bits were " +
|
|
"updated.\n" +
|
|
"The following RenderObject was marked as having dirty compositing " +
|
|
"bits at the time that it was painted:\n" +
|
|
" " + this.toStringShallow(joiner: "\n ") + "\n" +
|
|
"A RenderObject that still has dirty compositing bits cannot be " +
|
|
"painted because this indicates that the tree has not yet been " +
|
|
"properly configured for creating the layer tree.\n" +
|
|
"This usually indicates an error in the Flutter framework itself."
|
|
);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
RenderObject debugLastActivePaint = null;
|
|
D.assert(() => {
|
|
this._debugDoingThisPaint = true;
|
|
debugLastActivePaint = _debugActivePaint;
|
|
_debugActivePaint = this;
|
|
D.assert(!this.isRepaintBoundary || this._layer != null);
|
|
return true;
|
|
});
|
|
|
|
this._needsPaint = false;
|
|
try {
|
|
this.paint(context, offset);
|
|
D.assert(!this._needsLayout);
|
|
D.assert(!this._needsPaint);
|
|
}
|
|
catch (Exception ex) {
|
|
this._debugReportException("paint", ex);
|
|
}
|
|
|
|
D.assert(() => {
|
|
this.debugPaint(context, offset);
|
|
_debugActivePaint = debugLastActivePaint;
|
|
this._debugDoingThisPaint = false;
|
|
return true;
|
|
});
|
|
}
|
|
|
|
public abstract Rect paintBounds { get; }
|
|
|
|
public virtual void debugPaint(PaintingContext context, Offset offset) {
|
|
}
|
|
|
|
public virtual void paint(PaintingContext context, Offset offset) {
|
|
}
|
|
|
|
public virtual void applyPaintTransform(RenderObject child, Matrix3 transform) {
|
|
D.assert(child.parent == this);
|
|
}
|
|
|
|
public Matrix3 getTransformTo(RenderObject ancestor) {
|
|
D.assert(this.attached);
|
|
|
|
if (ancestor == null) {
|
|
var rootNode = this.owner.rootNode;
|
|
if (rootNode is RenderObject) {
|
|
ancestor = (RenderObject) rootNode;
|
|
}
|
|
}
|
|
|
|
var renderers = new List<RenderObject>();
|
|
for (RenderObject renderer = this; renderer != ancestor; renderer = (RenderObject) renderer.parent) {
|
|
D.assert(renderer != null);
|
|
renderers.Add(renderer);
|
|
}
|
|
|
|
var transform = Matrix3.I();
|
|
for (int index = renderers.Count - 1; index > 0; index -= 1) {
|
|
renderers[index].applyPaintTransform(renderers[index - 1], transform);
|
|
}
|
|
|
|
return transform;
|
|
}
|
|
|
|
public virtual Rect describeApproximatePaintClip(RenderObject child) {
|
|
return null;
|
|
}
|
|
|
|
public abstract Rect semanticBounds { get; }
|
|
|
|
public virtual void handleEvent(PointerEvent evt, HitTestEntry entry) {
|
|
}
|
|
|
|
public override string toStringShort() {
|
|
string header = Diagnostics.describeIdentity(this);
|
|
if (this._relayoutBoundary != null && this._relayoutBoundary != this) {
|
|
int count = 1;
|
|
RenderObject target = (RenderObject) this.parent;
|
|
while (target != null && target != this._relayoutBoundary) {
|
|
target = (RenderObject) target.parent;
|
|
count += 1;
|
|
}
|
|
|
|
header += " relayoutBoundary=up" + count;
|
|
}
|
|
|
|
if (this._needsLayout) {
|
|
header += " NEEDS-LAYOUT";
|
|
}
|
|
|
|
if (this._needsPaint) {
|
|
header += " NEEDS-PAINT";
|
|
}
|
|
|
|
if (!this.attached) {
|
|
header += " DETACHED";
|
|
}
|
|
|
|
return header;
|
|
}
|
|
|
|
public override string toString(DiagnosticLevel minLevel = DiagnosticLevel.debug) {
|
|
return this.toStringShort();
|
|
}
|
|
|
|
public override string toStringDeep(
|
|
string prefixLineOne = "",
|
|
string prefixOtherLines = "",
|
|
DiagnosticLevel minLevel = DiagnosticLevel.debug
|
|
) {
|
|
RenderObject debugPreviousActiveLayout = null;
|
|
D.assert(() => {
|
|
debugPreviousActiveLayout = _debugActiveLayout;
|
|
_debugActiveLayout = null;
|
|
return true;
|
|
});
|
|
string result = base.toStringDeep(
|
|
prefixLineOne: prefixLineOne,
|
|
prefixOtherLines: prefixOtherLines,
|
|
minLevel: minLevel
|
|
);
|
|
D.assert(() => {
|
|
_debugActiveLayout = debugPreviousActiveLayout;
|
|
return true;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
public override string toStringShallow(
|
|
string joiner = ", ",
|
|
DiagnosticLevel minLevel = DiagnosticLevel.debug
|
|
) {
|
|
RenderObject debugPreviousActiveLayout = null;
|
|
D.assert(() => {
|
|
debugPreviousActiveLayout = _debugActiveLayout;
|
|
_debugActiveLayout = null;
|
|
return true;
|
|
});
|
|
string result = base.toStringShallow(joiner: joiner, minLevel: minLevel);
|
|
D.assert(() => {
|
|
_debugActiveLayout = debugPreviousActiveLayout;
|
|
return true;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
properties.add(new DiagnosticsProperty<object>(
|
|
"creator", this.debugCreator, defaultValue: Diagnostics.kNullDefaultValue,
|
|
level: DiagnosticLevel.debug));
|
|
properties.add(new DiagnosticsProperty<ParentData>("parentData", this.parentData,
|
|
tooltip: this._debugCanParentUseSize ? "can use size" : null, missingIfNull: true));
|
|
properties.add(new DiagnosticsProperty<Constraints>("constraints", this.constraints, missingIfNull: true));
|
|
properties.add(new DiagnosticsProperty<OffsetLayer>("layer", this._layer,
|
|
defaultValue: Diagnostics.kNullDefaultValue));
|
|
}
|
|
|
|
public virtual void showOnScreen(
|
|
RenderObject descendant = null,
|
|
Rect rect = null,
|
|
TimeSpan? duration = null,
|
|
Curve curve = null
|
|
) {
|
|
duration = duration ?? TimeSpan.Zero;
|
|
curve = curve ?? Curves.ease;
|
|
|
|
if (this.parent is RenderObject) {
|
|
RenderObject renderParent = (RenderObject) this.parent;
|
|
renderParent.showOnScreen(
|
|
descendant: descendant ?? this,
|
|
rect: rect,
|
|
duration: duration,
|
|
curve: curve
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
public interface RenderObjectWithChildMixin {
|
|
bool debugValidateChild(RenderObject child);
|
|
RenderObject child { get; set; }
|
|
}
|
|
|
|
public interface RenderObjectWithChildMixin<ChildType> : RenderObjectWithChildMixin
|
|
where ChildType : RenderObject {
|
|
new ChildType child { get; set; }
|
|
}
|
|
|
|
public interface ContainerParentDataMixin<ChildType> where ChildType : RenderObject {
|
|
ChildType previousSibling { get; set; }
|
|
ChildType nextSibling { get; set; }
|
|
}
|
|
|
|
public interface ContainerRenderObjectMixin {
|
|
int childCount { get; }
|
|
bool debugValidateChild(RenderObject child);
|
|
void insert(RenderObject child, RenderObject after = null);
|
|
void remove(RenderObject child);
|
|
void move(RenderObject child, RenderObject after = null);
|
|
RenderObject firstChild { get; }
|
|
RenderObject lastChild { get; }
|
|
RenderObject childBefore(RenderObject child);
|
|
RenderObject childAfter(RenderObject child);
|
|
}
|
|
|
|
public class UIWidgetsErrorDetailsForRendering : UIWidgetsErrorDetails {
|
|
public UIWidgetsErrorDetailsForRendering(
|
|
Exception exception = null,
|
|
string library = null,
|
|
string context = null,
|
|
RenderObject renderObject = null,
|
|
InformationCollector informationCollector = null,
|
|
bool silent = false
|
|
) : base(
|
|
exception: exception,
|
|
library: library,
|
|
context: context,
|
|
informationCollector: informationCollector,
|
|
silent: silent
|
|
) {
|
|
this.renderObject = renderObject;
|
|
}
|
|
|
|
public readonly RenderObject renderObject;
|
|
}
|
|
}
|