您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

1443 行
57 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.rendering {
class _DebugSize : Size {
internal _DebugSize(Size source, RenderBox _owner, bool _canBeUsedByParent) :
base(source.width, source.height) {
this._owner = _owner;
this._canBeUsedByParent = _canBeUsedByParent;
}
internal readonly RenderBox _owner;
internal readonly bool _canBeUsedByParent;
}
public class BoxConstraints : Constraints, IEquatable<BoxConstraints> {
public BoxConstraints(
float minWidth = 0.0f,
float maxWidth = float.PositiveInfinity,
float minHeight = 0.0f,
float maxHeight = float.PositiveInfinity) {
this.minWidth = minWidth;
this.maxWidth = maxWidth;
this.minHeight = minHeight;
this.maxHeight = maxHeight;
}
public readonly float minWidth;
public readonly float maxWidth;
public readonly float minHeight;
public readonly float maxHeight;
public static BoxConstraints tight(Size size) {
return new BoxConstraints(
size.width,
size.width,
size.height,
size.height
);
}
public static BoxConstraints tightFor(
float? width = null,
float? height = null
) {
return new BoxConstraints(
width ?? 0.0f,
width ?? float.PositiveInfinity,
height ?? 0.0f,
height ?? float.PositiveInfinity
);
}
public static BoxConstraints tightForFinite(
float width = float.PositiveInfinity,
float height = float.PositiveInfinity
) {
return new BoxConstraints(
!float.IsPositiveInfinity(width) ? width : 0.0f,
!float.IsPositiveInfinity(width) ? width : float.PositiveInfinity,
!float.IsPositiveInfinity(height) ? height : 0.0f,
!float.IsPositiveInfinity(height) ? height : float.PositiveInfinity
);
}
public static BoxConstraints loose(Size size) {
return new BoxConstraints(
minWidth: 0,
maxWidth: size.width,
minHeight: 0,
maxHeight: size.height
);
}
public static BoxConstraints expand(
float? width = null,
float? height = null
) {
return new BoxConstraints(
width ?? float.PositiveInfinity,
width ?? float.PositiveInfinity,
height ?? float.PositiveInfinity,
height ?? float.PositiveInfinity
);
}
public BoxConstraints copyWith(
float? minWidth = null,
float? maxWidth = null,
float? minHeight = null,
float? maxHeight = null
) {
return new BoxConstraints(
minWidth ?? this.minWidth,
maxWidth ?? this.maxWidth,
minHeight ?? this.minHeight,
maxHeight ?? this.maxHeight
);
}
public BoxConstraints deflate(EdgeInsets edges) {
D.assert(edges != null);
D.assert(this.debugAssertIsValid());
float horizontal = edges.horizontal;
float vertical = edges.vertical;
float deflatedMinWidth = Mathf.Max(0.0f, this.minWidth - horizontal);
float deflatedMinHeight = Mathf.Max(0.0f, this.minHeight - vertical);
return new BoxConstraints(
minWidth: deflatedMinWidth,
maxWidth: Mathf.Max(deflatedMinWidth, this.maxWidth - horizontal),
minHeight: deflatedMinHeight,
maxHeight: Mathf.Max(deflatedMinHeight, this.maxHeight - vertical)
);
}
public BoxConstraints loosen() {
D.assert(this.debugAssertIsValid());
return new BoxConstraints(
minWidth: 0.0f,
maxWidth: this.maxWidth,
minHeight: 0.0f,
maxHeight: this.maxHeight
);
}
public BoxConstraints enforce(BoxConstraints constraints) {
return new BoxConstraints(
minWidth: this.minWidth.clamp(constraints.minWidth, constraints.maxWidth),
maxWidth: this.maxWidth.clamp(constraints.minWidth, constraints.maxWidth),
minHeight: this.minHeight.clamp(constraints.minHeight, constraints.maxHeight),
maxHeight: this.maxHeight.clamp(constraints.minHeight, constraints.maxHeight)
);
}
public BoxConstraints tighten(
float? width = null,
float? height = null
) {
return new BoxConstraints(
minWidth: width == null ? this.minWidth : width.Value.clamp(this.minWidth, this.maxWidth),
maxWidth: width == null ? this.maxWidth : width.Value.clamp(this.minWidth, this.maxWidth),
minHeight: height == null ? this.minHeight : height.Value.clamp(this.minHeight, this.maxHeight),
maxHeight: height == null ? this.maxHeight : height.Value.clamp(this.minHeight, this.maxHeight)
);
}
public BoxConstraints flipped {
get {
return new BoxConstraints(
minWidth: this.minHeight,
maxWidth: this.maxHeight,
minHeight: this.minWidth,
maxHeight: this.maxWidth
);
}
}
public BoxConstraints widthConstraints() {
return new BoxConstraints(minWidth: this.minWidth, maxWidth: this.maxWidth);
}
public BoxConstraints heightConstraints() {
return new BoxConstraints(minHeight: this.minHeight, maxHeight: this.maxHeight);
}
public float constrainWidth(float width = float.PositiveInfinity) {
D.assert(this.debugAssertIsValid());
return width.clamp(this.minWidth, this.maxWidth);
}
public float constrainHeight(float height = float.PositiveInfinity) {
D.assert(this.debugAssertIsValid());
return height.clamp(this.minHeight, this.maxHeight);
}
Size _debugPropagateDebugSize(Size size, Size result) {
D.assert(() => {
if (size is _DebugSize) {
result = new _DebugSize(result,
((_DebugSize) size)._owner, ((_DebugSize) size)._canBeUsedByParent);
}
return true;
});
return result;
}
public Size constrain(Size size) {
var result = new Size(this.constrainWidth(size.width), this.constrainHeight(size.height));
D.assert(() => {
result = this._debugPropagateDebugSize(size, result);
return true;
});
return result;
}
public Size constrainDimensions(float width, float height) {
return new Size(this.constrainWidth(width), this.constrainHeight(height));
}
public Size constrainSizeAndAttemptToPreserveAspectRatio(Size size) {
if (this.isTight) {
Size result1 = this.smallest;
D.assert(() => {
result1 = this._debugPropagateDebugSize(size, result1);
return true;
});
return result1;
}
float width = size.width;
float height = size.height;
D.assert(width > 0.0);
D.assert(height > 0.0);
float aspectRatio = width / height;
if (width > this.maxWidth) {
width = this.maxWidth;
height = width / aspectRatio;
}
if (height > this.maxHeight) {
height = this.maxHeight;
width = height * aspectRatio;
}
if (width < this.minWidth) {
width = this.minWidth;
height = width / aspectRatio;
}
if (height < this.minHeight) {
height = this.minHeight;
width = height * aspectRatio;
}
var result = new Size(this.constrainWidth(width), this.constrainHeight(height));
D.assert(() => {
result = this._debugPropagateDebugSize(size, result);
return true;
});
return result;
}
public Size biggest {
get { return new Size(this.constrainWidth(), this.constrainHeight()); }
}
public Size smallest {
get { return new Size(this.constrainWidth(0.0f), this.constrainHeight(0.0f)); }
}
public bool hasTightWidth {
get { return this.minWidth >= this.maxWidth; }
}
public bool hasTightHeight {
get { return this.minHeight >= this.maxHeight; }
}
public override bool isTight {
get { return this.hasTightWidth && this.hasTightHeight; }
}
public bool hasBoundedWidth {
get { return this.maxWidth < float.PositiveInfinity; }
}
public bool hasBoundedHeight {
get { return this.maxHeight < float.PositiveInfinity; }
}
public bool hasInfiniteWidth {
get { return this.minWidth >= float.PositiveInfinity; }
}
public bool hasInfiniteHeight {
get { return this.minHeight >= float.PositiveInfinity; }
}
public bool isSatisfiedBy(Size size) {
D.assert(this.debugAssertIsValid());
return this.minWidth <= size.width && size.width <= this.maxWidth &&
this.minHeight <= size.height && size.height <= this.maxHeight;
}
public static BoxConstraints operator *(BoxConstraints it, float factor) {
return new BoxConstraints(
minWidth: it.minWidth * factor,
maxWidth: it.maxWidth * factor,
minHeight: it.minHeight * factor,
maxHeight: it.maxHeight * factor
);
}
public static BoxConstraints operator /(BoxConstraints it, float factor) {
return new BoxConstraints(
minWidth: it.minWidth / factor,
maxWidth: it.maxWidth / factor,
minHeight: it.minHeight / factor,
maxHeight: it.maxHeight / factor
);
}
public static BoxConstraints operator %(BoxConstraints it, float value) {
return new BoxConstraints(
minWidth: it.minWidth % value,
maxWidth: it.maxWidth % value,
minHeight: it.minHeight % value,
maxHeight: it.maxHeight % value
);
}
public static BoxConstraints lerp(BoxConstraints a, BoxConstraints b, float t) {
if (a == null && b == null) {
return null;
}
if (a == null) {
return b * t;
}
if (b == null) {
return a * (1.0f - t);
}
D.assert(a.debugAssertIsValid());
D.assert(b.debugAssertIsValid());
D.assert(
(a.minWidth.isFinite() && b.minWidth.isFinite()) ||
(a.minWidth == float.PositiveInfinity && b.minWidth == float.PositiveInfinity),
"Cannot interpolate between finite constraints and unbounded constraints.");
D.assert(
(a.maxWidth.isFinite() && b.maxWidth.isFinite()) ||
(a.maxWidth == float.PositiveInfinity && b.maxWidth == float.PositiveInfinity),
"Cannot interpolate between finite constraints and unbounded constraints.");
D.assert(
(a.minHeight.isFinite() && b.minHeight.isFinite()) ||
(a.minHeight == float.PositiveInfinity && b.minHeight == float.PositiveInfinity),
"Cannot interpolate between finite constraints and unbounded constraints.");
D.assert(
(a.maxHeight.isFinite() && b.maxHeight.isFinite()) ||
(a.maxHeight == float.PositiveInfinity && b.maxHeight == float.PositiveInfinity),
"Cannot interpolate between finite constraints and unbounded constraints.");
return new BoxConstraints(
minWidth: a.minWidth.isFinite()
? MathUtils.lerpFloat(a.minWidth, b.minWidth, t)
: float.PositiveInfinity,
maxWidth: a.maxWidth.isFinite()
? MathUtils.lerpFloat(a.maxWidth, b.maxWidth, t)
: float.PositiveInfinity,
minHeight: a.minHeight.isFinite()
? MathUtils.lerpFloat(a.minHeight, b.minHeight, t)
: float.PositiveInfinity,
maxHeight: a.maxHeight.isFinite()
? MathUtils.lerpFloat(a.maxHeight, b.maxHeight, t)
: float.PositiveInfinity
);
}
public override bool isNormalized {
get {
return this.minWidth >= 0.0 &&
this.minWidth <= this.maxWidth &&
this.minHeight >= 0.0 &&
this.minHeight <= this.maxHeight;
}
}
public override bool debugAssertIsValid(
bool isAppliedConstraint = false,
InformationCollector informationCollector = null
) {
D.assert(() => {
var throwError = new Action<string>(message => {
var information = new StringBuilder();
if (informationCollector != null) {
informationCollector(information);
}
throw new UIWidgetsError($"{message}\n{information}The offending constraints were:\n {this}");
});
if (this.minWidth.isNaN() ||
this.maxWidth.isNaN() ||
this.minHeight.isNaN() ||
this.maxHeight.isNaN()) {
var affectedFieldsList = new List<string>();
if (this.minWidth.isNaN()) {
affectedFieldsList.Add("minWidth");
}
if (this.maxWidth.isNaN()) {
affectedFieldsList.Add("maxWidth");
}
if (this.minHeight.isNaN()) {
affectedFieldsList.Add("minHeight");
}
if (this.maxHeight.isNaN()) {
affectedFieldsList.Add("maxHeight");
}
D.assert(affectedFieldsList.isNotEmpty());
if (affectedFieldsList.Count > 1) {
var last = affectedFieldsList.Last();
affectedFieldsList.RemoveAt(affectedFieldsList.Count - 1);
affectedFieldsList.Add("and " + last);
}
string whichFields;
if (affectedFieldsList.Count > 2) {
whichFields = string.Join(", ", affectedFieldsList.ToArray());
}
else if (affectedFieldsList.Count == 2) {
whichFields = string.Join(" ", affectedFieldsList.ToArray());
}
else {
whichFields = affectedFieldsList.Single();
}
throwError("BoxConstraints has NaN values in " + whichFields + ".");
}
if (this.minWidth < 0.0 && this.minHeight < 0.0) {
throwError("BoxConstraints has both a negative minimum width and a negative minimum height.");
}
if (this.minWidth < 0.0) {
throwError("BoxConstraints has a negative minimum width.");
}
if (this.minHeight < 0.0) {
throwError("BoxConstraints has a negative minimum height.");
}
if (this.maxWidth < this.minWidth && this.maxHeight < this.minHeight) {
throwError("BoxConstraints has both width and height constraints non-normalized.");
}
if (this.maxWidth < this.minWidth) {
throwError("BoxConstraints has non-normalized width constraints.");
}
if (this.maxHeight < this.minHeight) {
throwError("BoxConstraints has non-normalized height constraints.");
}
if (isAppliedConstraint) {
if (this.minWidth.isInfinite() && this.minHeight.isInfinite()) {
throwError("BoxConstraints forces an infinite width and infinite height.");
}
if (this.minWidth.isInfinite()) {
throwError("BoxConstraints forces an infinite width.");
}
if (this.minHeight.isInfinite()) {
throwError("BoxConstraints forces an infinite height.");
}
}
D.assert(this.isNormalized);
return true;
});
return this.isNormalized;
}
public BoxConstraints normalize() {
if (this.isNormalized) {
return this;
}
var minWidth = this.minWidth >= 0.0 ? this.minWidth : 0.0f;
var minHeight = this.minHeight >= 0.0 ? this.minHeight : 0.0f;
return new BoxConstraints(
minWidth,
minWidth > this.maxWidth ? minWidth : this.maxWidth,
minHeight,
minHeight > this.maxHeight ? minHeight : this.maxHeight
);
}
public bool Equals(BoxConstraints other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return this.minWidth.Equals(other.minWidth)
&& this.maxWidth.Equals(other.maxWidth)
&& this.minHeight.Equals(other.minHeight)
&& this.maxHeight.Equals(other.maxHeight);
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return this.Equals((BoxConstraints) obj);
}
public override int GetHashCode() {
unchecked {
var hashCode = this.minWidth.GetHashCode();
hashCode = (hashCode * 397) ^ this.maxWidth.GetHashCode();
hashCode = (hashCode * 397) ^ this.minHeight.GetHashCode();
hashCode = (hashCode * 397) ^ this.maxHeight.GetHashCode();
return hashCode;
}
}
public static bool operator ==(BoxConstraints left, BoxConstraints right) {
return Equals(left, right);
}
public static bool operator !=(BoxConstraints left, BoxConstraints right) {
return !Equals(left, right);
}
public override string ToString() {
string annotation = this.isNormalized ? "" : "; NOT NORMALIZED";
if (this.minWidth == float.PositiveInfinity &&
this.minHeight == float.PositiveInfinity) {
return "BoxConstraints(biggest" + annotation + ")";
}
if (this.minWidth == 0 && this.maxWidth == float.PositiveInfinity &&
this.minHeight == 0 && this.maxHeight == float.PositiveInfinity) {
return "BoxConstraints(unconstrained" + annotation + ")";
}
var describe = new Func<float, float, string, string>((min, max, dim) => {
if (min == max) {
return dim + "=" + min.ToString("F1");
}
return min.ToString("F1") + "<=" + dim + "<=" + max.ToString("F1");
});
string width = describe(this.minWidth, this.maxWidth, "w");
string height = describe(this.minHeight, this.maxHeight, "h");
return "BoxConstraints(" + width + ", " + height + annotation + ")";
}
}
public class BoxHitTestEntry : HitTestEntry {
public BoxHitTestEntry(RenderBox target, Offset localPosition)
: base(target) {
D.assert(localPosition != null);
this.localPosition = localPosition;
}
public new RenderBox target {
get { return (RenderBox) base.target; }
}
public readonly Offset localPosition;
public override string ToString() {
return $"{Diagnostics.describeIdentity(this.target)}@{this.localPosition}";
}
}
public class BoxParentData : ParentData {
public Offset offset = Offset.zero;
public override string ToString() {
return "offset=" + this.offset;
}
}
enum _IntrinsicDimension {
minWidth,
maxWidth,
minHeight,
maxHeight
}
class _IntrinsicDimensionsCacheEntry : IEquatable<_IntrinsicDimensionsCacheEntry> {
internal _IntrinsicDimensionsCacheEntry(_IntrinsicDimension dimension, float argument) {
this.dimension = dimension;
this.argument = argument;
}
public readonly _IntrinsicDimension dimension;
public readonly float argument;
public bool Equals(_IntrinsicDimensionsCacheEntry other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return this.dimension == other.dimension && this.argument.Equals(other.argument);
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return this.Equals((_IntrinsicDimensionsCacheEntry) obj);
}
public override int GetHashCode() {
unchecked {
return ((int) this.dimension * 397) ^ this.argument.GetHashCode();
}
}
public static bool operator ==(_IntrinsicDimensionsCacheEntry a, _IntrinsicDimensionsCacheEntry b) {
return Equals(a, b);
}
public static bool operator !=(_IntrinsicDimensionsCacheEntry a, _IntrinsicDimensionsCacheEntry b) {
return !Equals(a, b);
}
}
public abstract class RenderBox : RenderObject {
public override void setupParentData(RenderObject child) {
if (!(child.parentData is BoxParentData)) {
child.parentData = new BoxParentData();
}
}
Dictionary<_IntrinsicDimensionsCacheEntry, float> _cachedIntrinsicDimensions;
float _computeIntrinsicDimension(_IntrinsicDimension dimension, float argument,
Func<float, float> computer) {
D.assert(debugCheckingIntrinsics || !this.debugDoingThisResize);
bool shouldCache = true;
D.assert(() => {
if (debugCheckingIntrinsics) {
shouldCache = false;
}
return true;
});
if (shouldCache) {
this._cachedIntrinsicDimensions =
this._cachedIntrinsicDimensions
?? new Dictionary<_IntrinsicDimensionsCacheEntry, float>();
return this._cachedIntrinsicDimensions.putIfAbsent(
new _IntrinsicDimensionsCacheEntry(dimension, argument),
() => computer(argument));
}
return computer(argument);
}
public float getMinIntrinsicWidth(float height) {
D.assert(() => {
if (height < 0.0) {
throw new UIWidgetsError(
"The height argument to getMinIntrinsicWidth was negative.\n" +
"The argument to getMinIntrinsicWidth must not be negative. " +
"If you perform computations on another height before passing it to " +
"getMinIntrinsicWidth, consider using Mathf.Max() or float.clamp() " +
"to force the value into the valid range."
);
}
return true;
});
return this._computeIntrinsicDimension(_IntrinsicDimension.minWidth, height, this.computeMinIntrinsicWidth);
}
protected virtual float computeMinIntrinsicWidth(float height) {
return 0.0f;
}
public float getMaxIntrinsicWidth(float height) {
D.assert(() => {
if (height < 0.0) {
throw new UIWidgetsError(
"The height argument to getMaxIntrinsicWidth was negative.\n" +
"The argument to getMaxIntrinsicWidth must not be negative. " +
"If you perform computations on another height before passing it to " +
"getMaxIntrinsicWidth, consider using Mathf.Max() or float.clamp() " +
"to force the value into the valid range."
);
}
return true;
});
return this._computeIntrinsicDimension(_IntrinsicDimension.maxWidth, height, this.computeMaxIntrinsicWidth);
}
protected virtual float computeMaxIntrinsicWidth(float height) {
return 0.0f;
}
public float getMinIntrinsicHeight(float width) {
D.assert(() => {
if (width < 0.0) {
throw new UIWidgetsError(
"The width argument to getMinIntrinsicHeight was negative.\n" +
"The argument to getMinIntrinsicHeight must not be negative. " +
"If you perform computations on another width before passing it to " +
"getMinIntrinsicHeight, consider using Mathf.Max() or float.clamp() " +
"to force the value into the valid range."
);
}
return true;
});
return this._computeIntrinsicDimension(_IntrinsicDimension.minHeight, width,
this.computeMinIntrinsicHeight);
}
protected virtual float computeMinIntrinsicHeight(float width) {
return 0.0f;
}
public float getMaxIntrinsicHeight(float width) {
D.assert(() => {
if (width < 0.0) {
throw new UIWidgetsError(
"The width argument to getMaxIntrinsicHeight was negative.\n" +
"The argument to getMaxIntrinsicHeight must not be negative. " +
"If you perform computations on another width before passing it to " +
"getMaxIntrinsicHeight, consider using Mathf.Max() or float.clamp() " +
"to force the value into the valid range."
);
}
return true;
});
return this._computeIntrinsicDimension(_IntrinsicDimension.maxHeight, width,
this.computeMaxIntrinsicHeight);
}
protected virtual float computeMaxIntrinsicHeight(float width) {
return 0.0f;
}
public bool hasSize {
get { return this._size != null; }
}
public Size size {
get {
D.assert(this.hasSize, "RenderBox was not laid out: " + this);
D.assert(() => {
if (this._size is _DebugSize) {
_DebugSize _size = (_DebugSize) this._size;
D.assert(_size._owner == this);
if (debugActiveLayout != null) {
D.assert(this.debugDoingThisResize || this.debugDoingThisLayout ||
(debugActiveLayout == this.parent && _size._canBeUsedByParent));
}
D.assert(_size == this._size);
}
return true;
});
return this._size;
}
set {
D.assert(!(this.debugDoingThisResize && this.debugDoingThisLayout));
D.assert(this.sizedByParent || !this.debugDoingThisResize);
D.assert(() => {
if ((this.sizedByParent && this.debugDoingThisResize) ||
(!this.sizedByParent && this.debugDoingThisLayout)) {
return true;
}
D.assert(!this.debugDoingThisResize);
string contract = "", violation = "", hint = "";
if (this.debugDoingThisLayout) {
D.assert(this.sizedByParent);
violation = "It appears that the size setter was called from performLayout().";
hint = "";
}
else {
violation =
"The size setter was called from outside layout (neither performResize() nor performLayout() were being run for this object).";
if (this.owner != null && this.owner.debugDoingLayout) {
hint =
"Only the object itself can set its size. It is a contract violation for other objects to set it.";
}
}
if (this.sizedByParent) {
contract =
"Because this RenderBox has sizedByParent set to true, it must set its size in performResize().";
}
else {
contract =
"Because this RenderBox has sizedByParent set to false, it must set its size in performLayout().";
}
throw new UIWidgetsError(
"RenderBox size setter called incorrectly.\n" +
violation + "\n" +
hint + "\n" +
contract + "\n" +
"The RenderBox in question is:\n" +
" " + this
);
});
D.assert(() => {
value = this.debugAdoptSize(value);
return true;
});
this._size = value;
D.assert(() => {
this.debugAssertDoesMeetConstraints();
return true;
});
}
}
Size _size;
public Size debugAdoptSize(Size valueRaw) {
Size result = valueRaw;
D.assert(() => {
if (valueRaw is _DebugSize) {
var value = (_DebugSize) valueRaw;
if (value._owner != this) {
if (value._owner.parent != this) {
throw new UIWidgetsError(
"The size property was assigned a size inappropriately.\n" +
"The following render object:\n" +
" " + this + "\n" +
"...was assigned a size obtained from:\n" +
" " + value._owner + "\n" +
"However, this second render object is not, or is no longer, a " +
"child of the first, and it is therefore a violation of the " +
"RenderBox layout protocol to use that size in the layout of the " +
"first render object.\n" +
"If the size was obtained at a time where it was valid to read " +
"the size (because the second render object above was a child " +
"of the first at the time), then it should be adopted using " +
"debugAdoptSize at that time.\n" +
"If the size comes from a grandchild or a render object from an " +
"entirely different part of the render tree, then there is no " +
"way to be notified when the size changes and therefore attempts " +
"to read that size are almost certainly a source of bugs. A different " +
"approach should be used."
);
}
if (!value._canBeUsedByParent) {
throw new UIWidgetsError(
"A child\"s size was used without setting parentUsesSize.\n" +
"The following render object:\n" +
" " + this + "\n" +
"...was assigned a size obtained from its child:\n" +
" " + value._owner + "\n" +
"However, when the child was laid out, the parentUsesSize argument " +
"was not set or set to false. Subsequently this transpired to be " +
"inaccurate: the size was nonetheless used by the parent.\n" +
"It is important to tell the framework if the size will be used or not " +
"as several important performance optimizations can be made if the " +
"size will not be used by the parent."
);
}
}
}
result = new _DebugSize(valueRaw, this, this.debugCanParentUseSize);
return true;
});
return result;
}
public override Rect semanticBounds {
get { return Offset.zero & this.size; }
}
protected override void debugResetSize() {
this.size = this.size;
}
Dictionary<TextBaseline, float?> _cachedBaselines;
static bool _debugDoingBaseline = false;
static bool _debugSetDoingBaseline(bool value) {
_debugDoingBaseline = value;
return true;
}
public float? getDistanceToBaseline(TextBaseline baseline, bool onlyReal = false) {
D.assert(!_debugDoingBaseline,
"Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.");
D.assert(!this.debugNeedsLayout);
D.assert(() => {
RenderObject parent = (RenderObject) this.parent;
if (this.owner.debugDoingLayout) {
return (debugActiveLayout == parent) && parent.debugDoingThisLayout;
}
if (this.owner.debugDoingPaint) {
return ((debugActivePaint == parent) && parent.debugDoingThisPaint) ||
((debugActivePaint == this) && this.debugDoingThisPaint);
}
D.assert(parent == this.parent);
return false;
});
D.assert(_debugSetDoingBaseline(true));
float? result = this.getDistanceToActualBaseline(baseline);
D.assert(_debugSetDoingBaseline(false));
if (result == null && !onlyReal) {
return this.size.height;
}
return result;
}
public virtual float? getDistanceToActualBaseline(TextBaseline baseline) {
D.assert(_debugDoingBaseline,
"Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.");
this._cachedBaselines = this._cachedBaselines ?? new Dictionary<TextBaseline, float?>();
return this._cachedBaselines.putIfAbsent(baseline, () => this.computeDistanceToActualBaseline(baseline));
}
protected virtual float? computeDistanceToActualBaseline(TextBaseline baseline) {
D.assert(_debugDoingBaseline,
"Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.");
return null;
}
public new BoxConstraints constraints {
get { return (BoxConstraints) base.constraints; }
}
protected override void debugAssertDoesMeetConstraints() {
D.assert(this.constraints != null);
D.assert(() => {
if (!this.hasSize) {
D.assert(!this.debugNeedsLayout);
string contract = "";
if (this.sizedByParent) {
contract =
"Because this RenderBox has sizedByParent set to true, it must set its size in performResize().\n";
}
else {
contract =
"Because this RenderBox has sizedByParent set to false, it must set its size in performLayout().\n";
}
throw new UIWidgetsError(
"RenderBox did not set its size during layout.\n" +
contract +
"It appears that this did not happen; layout completed, but the size property is still null.\n" +
"The RenderBox in question is:\n" +
" " + this);
}
if (!this._size.isFinite) {
var information = new StringBuilder();
if (!this.constraints.hasBoundedWidth) {
RenderBox node = this;
while (!node.constraints.hasBoundedWidth && node.parent is RenderBox) {
node = (RenderBox) node.parent;
}
information.AppendLine("The nearest ancestor providing an unbounded width constraint is:");
information.Append(" ");
information.AppendLine(node.toStringShallow(joiner: "\n "));
}
if (!this.constraints.hasBoundedHeight) {
RenderBox node = this;
while (!node.constraints.hasBoundedHeight && node.parent is RenderBox) {
node = (RenderBox) node.parent;
}
information.AppendLine("The nearest ancestor providing an unbounded height constraint is:");
information.Append(" ");
information.AppendLine(node.toStringShallow(joiner: "\n "));
}
throw new UIWidgetsError(
this.GetType() + " object was given an infinite size during layout.\n" +
"This probably means that it is a render object that tries to be " +
"as big as possible, but it was put inside another render object " +
"that allows its children to pick their own size.\n" +
information +
"The constraints that applied to the " + this.GetType() + " were:\n" +
" " + this.constraints + "\n" +
"The exact size it was given was:\n" +
" " + this._size
);
}
if (!this.constraints.isSatisfiedBy(this._size)) {
throw new UIWidgetsError(
this.GetType() + " does not meet its constraints.\n" +
"Constraints: " + this.constraints + "\n" +
"Size: " + this._size + "\n" +
"If you are not writing your own RenderBox subclass, then this is not " +
"your fault."
);
}
if (D.debugCheckIntrinsicSizes) {
D.assert(!debugCheckingIntrinsics);
debugCheckingIntrinsics = true;
var failures = new StringBuilder();
int failureCount = 0;
var testIntrinsic = new Func<Func<float, float>, string, float, float>(
(function, name, constraint) => {
float result = function(constraint);
if (result < 0) {
failures.AppendLine(" * " + name + "(" + constraint + ") returned a negative value: " +
result);
failureCount += 1;
}
if (result.isInfinite()) {
failures.AppendLine(" * " + name + "(" + constraint +
") returned a non-finite value: " + result);
failureCount += 1;
}
return result;
});
var testIntrinsicsForValues =
new Action<Func<float, float>, Func<float, float>, string, float>(
(getMin, getMax, name, constraint) => {
float min = testIntrinsic(getMin, "getMinIntrinsic" + name, constraint);
float max = testIntrinsic(getMax, "getMaxIntrinsic" + name, constraint);
if (min > max) {
failures.AppendLine(
" * getMinIntrinsic" + name + "(" + constraint + ") returned a larger value (" +
min +
") than getMaxIntrinsic" + name + "(" + constraint + ") (" + max + ")");
failureCount += 1;
}
});
testIntrinsicsForValues(this.getMinIntrinsicWidth, this.getMaxIntrinsicWidth, "Width",
float.PositiveInfinity);
testIntrinsicsForValues(this.getMinIntrinsicHeight, this.getMaxIntrinsicHeight, "Height",
float.PositiveInfinity);
if (this.constraints.hasBoundedWidth) {
testIntrinsicsForValues(this.getMinIntrinsicWidth, this.getMaxIntrinsicWidth, "Width",
this.constraints.maxWidth);
}
if (this.constraints.hasBoundedHeight) {
testIntrinsicsForValues(this.getMinIntrinsicHeight, this.getMaxIntrinsicHeight, "Height",
this.constraints.maxHeight);
}
debugCheckingIntrinsics = false;
if (failures.Length > 0) {
D.assert(failureCount > 0);
throw new UIWidgetsError(
"The intrinsic dimension methods of the " + this.GetType() +
" class returned values that violate the intrinsic protocol contract.\n" +
"The following failures was detected:\n" +
failures +
"If you are not writing your own RenderBox subclass, then this is not\n" +
"your fault."
);
}
}
return true;
});
}
public override void markNeedsLayout() {
if (this._cachedBaselines != null && this._cachedBaselines.isNotEmpty() ||
this._cachedIntrinsicDimensions != null && this._cachedIntrinsicDimensions.isNotEmpty()) {
if (this._cachedBaselines != null) {
this._cachedBaselines.Clear();
}
if (this._cachedIntrinsicDimensions != null) {
this._cachedIntrinsicDimensions.Clear();
}
if (this.parent is RenderObject) {
this.markParentNeedsLayout();
return;
}
}
base.markNeedsLayout();
}
protected override void performResize() {
this.size = this.constraints.smallest;
D.assert(this.size.isFinite);
}
protected override void performLayout() {
D.assert(() => {
if (!this.sizedByParent) {
throw new UIWidgetsError(
this.GetType() + " did not implement performLayout().\n" +
"RenderBox subclasses need to either override performLayout() to " +
"set a size and lay out any children, or, set sizedByParent to true " +
"so that performResize() sizes the render object."
);
}
return true;
});
}
public virtual bool hitTest(HitTestResult result, Offset position) {
D.assert(() => {
if (!this.hasSize) {
if (this.debugNeedsLayout) {
throw new UIWidgetsError(
"Cannot hit test a render box that has never been laid out.\n" +
"The hitTest() method was called on this RenderBox:\n" +
" " + this + "\n" +
"Unfortunately, this object\"s geometry is not known at this time, " +
"probably because it has never been laid out. " +
"This means it cannot be accurately hit-tested. If you are trying " +
"to perform a hit test during the layout phase itself, make sure " +
"you only hit test nodes that have completed layout (e.g. the node\"s " +
"children, after their layout() method has been called)."
);
}
throw new UIWidgetsError(
"Cannot hit test a render box with no size.\n" +
"The hitTest() method was called on this RenderBox:\n" +
" " + this + "\n" +
"Although this node is not marked as needing layout, " +
"its size is not set. A RenderBox object must have an " +
"explicit size before it can be hit-tested. Make sure " +
"that the RenderBox in question sets its size during layout."
);
}
return true;
});
if (this._size.contains(position)) {
if (this.hitTestChildren(result, position: position) || this.hitTestSelf(position)) {
result.add(new BoxHitTestEntry(this, position));
return true;
}
}
return false;
}
protected virtual bool hitTestSelf(Offset position) {
return false;
}
protected virtual bool hitTestChildren(HitTestResult result, Offset position = null) {
return false;
}
public override void applyPaintTransform(RenderObject child, Matrix3 transform) {
D.assert(child != null);
D.assert(child.parent == this);
D.assert(() => {
if (!(child.parentData is BoxParentData)) {
throw new UIWidgetsError(
this.GetType() + " does not implement applyPaintTransform.\n" +
"The following " + this.GetType() + " object:\n" +
" " + this.toStringShallow() + "\n" +
"...did not use a BoxParentData class for the parentData field of the following child:\n" +
" " + child.toStringShallow() + "\n" +
"The " + this.GetType() + " class inherits from RenderBox. " +
"The default applyPaintTransform implementation provided by RenderBox assumes that the " +
"children all use BoxParentData objects for their parentData field. " +
"Since " + this.GetType() +
" does not in fact use that ParentData class for its children, it must " +
"provide an implementation of applyPaintTransform that supports the specific ParentData " +
"subclass used by its children (which apparently is " + child.parentData.GetType() + ")."
);
}
return true;
});
var childParentData = (BoxParentData) child.parentData;
var offset = childParentData.offset;
transform.preTranslate(offset.dx, offset.dy);
}
public Offset globalToLocal(Offset point, RenderObject ancestor = null) {
var transform = this.getTransformTo(ancestor);
var inverse = Matrix3.I();
var invertible = transform.invert(inverse);
return invertible ? inverse.mapPoint(point) : Offset.zero;
}
public Offset localToGlobal(Offset point, RenderObject ancestor = null) {
return this.getTransformTo(ancestor).mapPoint(point);
}
public override Rect paintBounds {
get { return Offset.zero & this.size; }
}
int _debugActivePointers = 0;
protected bool debugHandleEvent(PointerEvent evt, HitTestEntry entry) {
D.assert(() => {
if (D.debugPaintPointersEnabled) {
if (evt is PointerDownEvent) {
this._debugActivePointers += 1;
}
else if (evt is PointerUpEvent || evt is PointerCancelEvent) {
this._debugActivePointers -= 1;
}
this.markNeedsPaint();
}
return true;
});
return true;
}
public override void debugPaint(PaintingContext context, Offset offset) {
D.assert(() => {
if (D.debugPaintSizeEnabled) {
this.debugPaintSize(context, offset);
}
if (D.debugPaintBaselinesEnabled) {
this.debugPaintBaselines(context, offset);
}
if (D.debugPaintPointersEnabled) {
this.debugPaintPointers(context, offset);
}
return true;
});
}
protected virtual void debugPaintSize(PaintingContext context, Offset offset) {
D.assert(() => {
var paint = new Paint {
color = new Color(0xFF00FFFF),
strokeWidth = 1,
style = PaintingStyle.stroke,
};
context.canvas.drawRect((offset & this.size).deflate(0.5f), paint);
return true;
});
}
protected virtual void debugPaintBaselines(PaintingContext context, Offset offset) {
D.assert(() => {
Paint paint = new Paint {
style = PaintingStyle.stroke,
strokeWidth = 0.25f
};
Path path;
// ideographic baseline
float? baselineI = this.getDistanceToBaseline(TextBaseline.ideographic, onlyReal: true);
if (baselineI != null) {
paint.color = new Color(0xFFFFD000);
path = new Path();
path.moveTo(offset.dx, offset.dy + baselineI.Value);
path.lineTo(offset.dx + this.size.width, offset.dy + baselineI.Value);
context.canvas.drawPath(path, paint);
}
// alphabetic baseline
float? baselineA = this.getDistanceToBaseline(TextBaseline.alphabetic, onlyReal: true);
if (baselineA != null) {
paint.color = new Color(0xFF00FF00);
path = new Path();
path.moveTo(offset.dx, offset.dy + baselineA.Value);
path.lineTo(offset.dx + this.size.width, offset.dy + baselineA.Value);
context.canvas.drawPath(path, paint);
}
return true;
});
}
protected virtual void debugPaintPointers(PaintingContext context, Offset offset) {
D.assert(() => {
if (this._debugActivePointers > 0) {
var paint = new Paint {
color = new Color(0x00BBBB | ((0x04000000 * this.depth) & 0xFF000000)),
};
context.canvas.drawRect(offset & this.size, paint);
}
return true;
});
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Size>("size", this._size, missingIfNull: true));
}
}
public abstract class ContainerBoxParentData<ChildType> : ContainerParentDataMixinBoxParentData<ChildType>
where ChildType : RenderBox {
}
public abstract class
RenderBoxContainerDefaultsMixin<ChildType, ParentDataType>
: ContainerRenderObjectMixinRenderBox<ChildType, ParentDataType>
where ChildType : RenderBox
where ParentDataType : ContainerParentDataMixinBoxParentData<ChildType> {
public float? defaultComputeDistanceToFirstActualBaseline(TextBaseline baseline) {
D.assert(!this.debugNeedsLayout);
var child = this.firstChild;
while (child != null) {
var childParentData = (ParentDataType) child.parentData;
float? result = child.getDistanceToActualBaseline(baseline);
if (result != null) {
return result.Value + childParentData.offset.dy;
}
child = childParentData.nextSibling;
}
return null;
}
public float? defaultComputeDistanceToHighestActualBaseline(TextBaseline baseline) {
D.assert(!this.debugNeedsLayout);
float? result = null;
var child = this.firstChild;
while (child != null) {
var childParentData = (ParentDataType) child.parentData;
float? candidate = child.getDistanceToActualBaseline(baseline);
if (candidate != null) {
candidate += childParentData.offset.dy;
if (result != null) {
result = Mathf.Min(result.Value, candidate.Value);
}
else {
result = candidate;
}
}
child = childParentData.nextSibling;
}
return result;
}
public bool defaultHitTestChildren(HitTestResult result, Offset position = null) {
ChildType child = this.lastChild;
while (child != null) {
ParentDataType childParentData = (ParentDataType) child.parentData;
if (child.hitTest(result, position: position - childParentData.offset)) {
return true;
}
child = childParentData.previousSibling;
}
return false;
}
public void defaultPaint(PaintingContext context, Offset offset) {
var child = this.firstChild;
while (child != null) {
var childParentData = (ParentDataType) child.parentData;
context.paintChild(child, childParentData.offset + offset);
child = childParentData.nextSibling;
}
}
public List<ChildType> getChildrenAsList() {
var result = new List<ChildType>();
var child = this.firstChild;
while (child != null) {
var childParentData = (ParentDataType) child.parentData;
result.Add(child);
child = childParentData.nextSibling;
}
return result;
}
}
}