您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
504 行
19 KiB
504 行
19 KiB
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.gestures;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.ui;
|
|
|
|
namespace Unity.UIWidgets.rendering {
|
|
public interface IKeepAliveParentDataMixin : IParentData {
|
|
bool keptAlive { get; }
|
|
|
|
bool keepAlive { get; set; }
|
|
}
|
|
|
|
public class KeepAliveParentDataMixin : ParentData, IKeepAliveParentDataMixin {
|
|
bool _keepAlive = false;
|
|
|
|
public bool keepAlive {
|
|
get { return _keepAlive; }
|
|
set { _keepAlive = value; }
|
|
}
|
|
|
|
public bool keptAlive { get; }
|
|
}
|
|
|
|
public interface RenderSliverBoxChildManager {
|
|
void createChild(int index, RenderBox after = null);
|
|
|
|
void removeChild(RenderBox child);
|
|
|
|
float estimateMaxScrollOffset(
|
|
SliverConstraints constraints,
|
|
int firstIndex = 0,
|
|
int lastIndex = 0,
|
|
float leadingScrollOffset = 0,
|
|
float trailingScrollOffset = 0);
|
|
|
|
int? childCount { get; }
|
|
|
|
void didAdoptChild(RenderBox child);
|
|
|
|
void setDidUnderflow(bool value);
|
|
|
|
void didStartLayout();
|
|
|
|
void didFinishLayout();
|
|
|
|
bool debugAssertChildListLocked();
|
|
}
|
|
|
|
public class SliverMultiBoxAdaptorParentData : ContainerParentDataMixinSliverLogicalParentData<RenderBox>, IKeepAliveParentDataMixin {
|
|
public int index;
|
|
|
|
bool _keepAlive = false;
|
|
|
|
public bool keepAlive {
|
|
get { return _keepAlive; }
|
|
set { _keepAlive = value; }
|
|
}
|
|
|
|
public bool keptAlive {
|
|
get { return _keptAlive; }
|
|
}
|
|
|
|
internal bool _keptAlive = false;
|
|
|
|
public override string ToString() {
|
|
return $"index={index}; {(keepAlive ? "keeyAlive; " : "")}{base.ToString()}";
|
|
}
|
|
}
|
|
|
|
public abstract class RenderSliverMultiBoxAdaptor
|
|
: ContainerRenderObjectMixinRenderSliver<RenderBox, SliverMultiBoxAdaptorParentData> {
|
|
public RenderSliverMultiBoxAdaptor(
|
|
RenderSliverBoxChildManager childManager = null
|
|
) {
|
|
D.assert(childManager != null);
|
|
_childManager = childManager;
|
|
D.assert(()=> {
|
|
_debugDanglingKeepAlives = new List<RenderBox>();
|
|
return true;
|
|
});
|
|
}
|
|
|
|
public override void setupParentData(RenderObject child) {
|
|
if (!(child.parentData is SliverMultiBoxAdaptorParentData)) {
|
|
child.parentData = new SliverMultiBoxAdaptorParentData();
|
|
}
|
|
}
|
|
|
|
protected RenderSliverBoxChildManager childManager {
|
|
get { return _childManager; }
|
|
}
|
|
readonly RenderSliverBoxChildManager _childManager;
|
|
|
|
readonly Dictionary<int, RenderBox> _keepAliveBucket = new Dictionary<int, RenderBox>();
|
|
List<RenderBox> _debugDanglingKeepAlives;
|
|
|
|
public bool debugChildIntegrityEnabled {
|
|
get { return _debugChildIntegrityEnabled;}
|
|
set {
|
|
D.assert(value != null);
|
|
D.assert(() =>{
|
|
_debugChildIntegrityEnabled = value;
|
|
return _debugVerifyChildOrder() &&
|
|
(!_debugChildIntegrityEnabled || _debugDanglingKeepAlives.isEmpty());
|
|
});
|
|
}
|
|
}
|
|
public void setDebugChildIntegrityEnabled(bool enabled) {
|
|
D.assert(enabled != null);
|
|
D.assert(() => {
|
|
_debugChildIntegrityEnabled = enabled;
|
|
return _debugVerifyChildOrder() &&
|
|
(!_debugChildIntegrityEnabled || _debugDanglingKeepAlives.isEmpty());
|
|
});
|
|
}
|
|
bool _debugChildIntegrityEnabled = true;
|
|
|
|
protected override void adoptChild(AbstractNodeMixinDiagnosticableTree childNode) {
|
|
base.adoptChild(childNode);
|
|
var child = (RenderBox) childNode;
|
|
SliverMultiBoxAdaptorParentData childParentData = (SliverMultiBoxAdaptorParentData) child.parentData;
|
|
if (!childParentData._keptAlive) {
|
|
childManager.didAdoptChild(child);
|
|
}
|
|
}
|
|
|
|
bool _debugAssertChildListLocked() {
|
|
return childManager.debugAssertChildListLocked();
|
|
}
|
|
|
|
bool _debugVerifyChildOrder(){
|
|
if (_debugChildIntegrityEnabled) {
|
|
RenderBox child = firstChild;
|
|
int index;
|
|
while (child != null) {
|
|
index = indexOf(child);
|
|
child = childAfter(child);
|
|
D.assert(child == null || indexOf(child) > index);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public override void insert(RenderBox child, RenderBox after = null) {
|
|
D.assert(!_keepAliveBucket.ContainsValue(value: child));
|
|
base.insert(child, after: after);
|
|
D.assert(firstChild != null);
|
|
D.assert(_debugVerifyChildOrder());
|
|
}
|
|
public new void move(RenderBox child, RenderBox after = null) {
|
|
SliverMultiBoxAdaptorParentData childParentData = child.parentData as SliverMultiBoxAdaptorParentData;
|
|
if (!childParentData.keptAlive) {
|
|
base.move(child, after: after);
|
|
childManager.didAdoptChild(child);
|
|
markNeedsLayout();
|
|
} else {
|
|
if (_keepAliveBucket[childParentData.index] == child) {
|
|
_keepAliveBucket.Remove(childParentData.index);
|
|
}
|
|
D.assert(()=> {
|
|
_debugDanglingKeepAlives.Remove(child);
|
|
return true;
|
|
});
|
|
childManager.didAdoptChild(child);
|
|
D.assert(()=> {
|
|
if (_keepAliveBucket.ContainsKey(childParentData.index))
|
|
_debugDanglingKeepAlives.Add(_keepAliveBucket[childParentData.index]);
|
|
return true;
|
|
});
|
|
_keepAliveBucket[childParentData.index] = child;
|
|
}
|
|
}
|
|
public override void remove(RenderBox child) {
|
|
SliverMultiBoxAdaptorParentData childParentData = (SliverMultiBoxAdaptorParentData) child.parentData;
|
|
if (!childParentData._keptAlive) {
|
|
base.remove(child);
|
|
return;
|
|
}
|
|
D.assert(_keepAliveBucket[childParentData.index] == child);
|
|
D.assert(()=> {
|
|
_debugDanglingKeepAlives.Remove(child);
|
|
return true;
|
|
});
|
|
_keepAliveBucket.Remove(childParentData.index);
|
|
dropChild(child);
|
|
}
|
|
public override void removeAll() {
|
|
base.removeAll();
|
|
|
|
foreach (var child in _keepAliveBucket.Values) {
|
|
dropChild(child);
|
|
}
|
|
|
|
_keepAliveBucket.Clear();
|
|
}
|
|
|
|
void _createOrObtainChild(int index, RenderBox after = null) {
|
|
invokeLayoutCallback<SliverConstraints>(constraints => {
|
|
D.assert(constraints == this.constraints);
|
|
if (_keepAliveBucket.ContainsKey(index)) {
|
|
RenderBox child = _keepAliveBucket[index];
|
|
_keepAliveBucket.Remove(index);
|
|
SliverMultiBoxAdaptorParentData childParentData = (SliverMultiBoxAdaptorParentData) child.parentData;
|
|
D.assert(childParentData._keptAlive);
|
|
dropChild(child);
|
|
child.parentData = childParentData;
|
|
insert(child, after: after);
|
|
childParentData._keptAlive = false;
|
|
}
|
|
else {
|
|
_childManager.createChild(index, after: after);
|
|
}
|
|
});
|
|
}
|
|
|
|
public void _destroyOrCacheChild(RenderBox child) {
|
|
SliverMultiBoxAdaptorParentData childParentData = (SliverMultiBoxAdaptorParentData) child.parentData;
|
|
if (childParentData.keepAlive) {
|
|
D.assert(!childParentData._keptAlive);
|
|
remove(child);
|
|
_keepAliveBucket[childParentData.index] = child;
|
|
child.parentData = childParentData;
|
|
base.adoptChild(child);
|
|
childParentData._keptAlive = true;
|
|
}
|
|
else {
|
|
D.assert(child.parent == this);
|
|
_childManager.removeChild(child);
|
|
D.assert(child.parent == null);
|
|
}
|
|
}
|
|
|
|
public override void attach(object owner) {
|
|
base.attach(owner);
|
|
foreach (RenderBox child in _keepAliveBucket.Values) {
|
|
child.attach(owner);
|
|
}
|
|
}
|
|
|
|
public override void detach() {
|
|
base.detach();
|
|
foreach (RenderBox child in _keepAliveBucket.Values) {
|
|
child.detach();
|
|
}
|
|
}
|
|
|
|
public override void redepthChildren() {
|
|
base.redepthChildren();
|
|
foreach (var child in _keepAliveBucket.Values) {
|
|
redepthChild(child);
|
|
}
|
|
}
|
|
|
|
public override void visitChildren(RenderObjectVisitor visitor) {
|
|
base.visitChildren(visitor);
|
|
|
|
foreach (var child in _keepAliveBucket.Values) {
|
|
visitor(child);
|
|
}
|
|
}
|
|
|
|
protected bool addInitialChild(int index = 0, float layoutOffset = 0.0f) {
|
|
D.assert(_debugAssertChildListLocked());
|
|
D.assert(firstChild == null);
|
|
_createOrObtainChild(index, after: null);
|
|
if (firstChild != null) {
|
|
D.assert(firstChild == lastChild);
|
|
D.assert(indexOf(firstChild) == index);
|
|
SliverMultiBoxAdaptorParentData firstChildParentData = (SliverMultiBoxAdaptorParentData) firstChild.parentData;
|
|
firstChildParentData.layoutOffset = layoutOffset;
|
|
return true;
|
|
}
|
|
childManager.setDidUnderflow(true);
|
|
return false;
|
|
}
|
|
|
|
protected RenderBox insertAndLayoutLeadingChild(
|
|
BoxConstraints childConstraints,
|
|
bool parentUsesSize = false) {
|
|
|
|
D.assert(_debugAssertChildListLocked());
|
|
int index = indexOf(firstChild) - 1;
|
|
_createOrObtainChild(index, after: null);
|
|
if (indexOf(firstChild) == index) {
|
|
firstChild.layout(childConstraints, parentUsesSize: parentUsesSize);
|
|
return firstChild;
|
|
}
|
|
childManager.setDidUnderflow(true);
|
|
return null;
|
|
}
|
|
|
|
protected RenderBox insertAndLayoutChild(
|
|
BoxConstraints childConstraints,
|
|
RenderBox after = null,
|
|
bool parentUsesSize = false
|
|
) {
|
|
D.assert(_debugAssertChildListLocked());
|
|
D.assert(after != null);
|
|
|
|
int index = indexOf(after) + 1;
|
|
_createOrObtainChild(index, after: after);
|
|
RenderBox child = childAfter(after);
|
|
if (child != null && indexOf(child) == index) {
|
|
child.layout(childConstraints, parentUsesSize: parentUsesSize);
|
|
return child;
|
|
}
|
|
|
|
childManager.setDidUnderflow(true);
|
|
return null;
|
|
}
|
|
|
|
protected void collectGarbage(int leadingGarbage, int trailingGarbage) {
|
|
D.assert(_debugAssertChildListLocked());
|
|
D.assert(childCount >= leadingGarbage + trailingGarbage);
|
|
|
|
invokeLayoutCallback<SliverConstraints>(constraints => {
|
|
while (leadingGarbage > 0) {
|
|
_destroyOrCacheChild(firstChild);
|
|
leadingGarbage -= 1;
|
|
}
|
|
|
|
while (trailingGarbage > 0) {
|
|
_destroyOrCacheChild(lastChild);
|
|
trailingGarbage -= 1;
|
|
}
|
|
|
|
_keepAliveBucket.Values.Where(child => {
|
|
SliverMultiBoxAdaptorParentData childParentData = (SliverMultiBoxAdaptorParentData) child.parentData;
|
|
return !childParentData.keepAlive;
|
|
}).ToList().ForEach(_childManager.removeChild);
|
|
|
|
D.assert(_keepAliveBucket.Values.Where(child => {
|
|
SliverMultiBoxAdaptorParentData childParentData = (SliverMultiBoxAdaptorParentData) child.parentData;
|
|
return !childParentData.keepAlive;
|
|
}).ToList().isEmpty());
|
|
});
|
|
}
|
|
|
|
public int indexOf(RenderBox child) {
|
|
D.assert(child != null);
|
|
SliverMultiBoxAdaptorParentData childParentData = (SliverMultiBoxAdaptorParentData) child.parentData;
|
|
D.assert(childParentData.index != null);
|
|
return childParentData.index;
|
|
}
|
|
|
|
protected float paintExtentOf(RenderBox child) {
|
|
D.assert(child != null);
|
|
D.assert(child.hasSize);
|
|
|
|
switch (constraints.axis) {
|
|
case Axis.horizontal:
|
|
return child.size.width;
|
|
case Axis.vertical:
|
|
return child.size.height;
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
protected override bool hitTestChildren(
|
|
SliverHitTestResult result,
|
|
float mainAxisPosition = 0.0f,
|
|
float crossAxisPosition = 0.0f) {
|
|
RenderBox child = lastChild;
|
|
BoxHitTestResult boxResult = new BoxHitTestResult(result);
|
|
|
|
while (child != null) {
|
|
if (this.hitTestBoxChild(boxResult, child, mainAxisPosition: mainAxisPosition,
|
|
crossAxisPosition: crossAxisPosition)) {
|
|
return true;
|
|
}
|
|
|
|
child = childBefore(child);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override float? childMainAxisPosition(RenderObject child) {
|
|
return childScrollOffset(child) - constraints.scrollOffset;
|
|
}
|
|
|
|
public override float? childScrollOffset(RenderObject child) {
|
|
D.assert(child != null);
|
|
D.assert(child.parent == this);
|
|
SliverMultiBoxAdaptorParentData childParentData = (SliverMultiBoxAdaptorParentData) child.parentData;
|
|
return (float)childParentData.layoutOffset;
|
|
}
|
|
|
|
public override void applyPaintTransform(RenderObject child, Matrix4 transform) {
|
|
this.applyPaintTransformForBoxChild((RenderBox) child, transform);
|
|
}
|
|
|
|
public override void paint(PaintingContext context, Offset offset) {
|
|
if (firstChild == null) {
|
|
return;
|
|
}
|
|
|
|
Offset mainAxisUnit = null, crossAxisUnit = null, originOffset = null;
|
|
bool addExtent = false;
|
|
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection(constraints.axisDirection,
|
|
constraints.growthDirection)) {
|
|
case AxisDirection.up:
|
|
mainAxisUnit = new Offset(0.0f, -1.0f);
|
|
crossAxisUnit = new Offset(1.0f, 0.0f);
|
|
originOffset = offset + new Offset(0.0f, geometry.paintExtent);
|
|
addExtent = true;
|
|
break;
|
|
case AxisDirection.right:
|
|
mainAxisUnit = new Offset(1.0f, 0.0f);
|
|
crossAxisUnit = new Offset(0.0f, 1.0f);
|
|
originOffset = offset;
|
|
addExtent = false;
|
|
break;
|
|
case AxisDirection.down:
|
|
mainAxisUnit = new Offset(0.0f, 1.0f);
|
|
crossAxisUnit = new Offset(1.0f, 0.0f);
|
|
originOffset = offset;
|
|
addExtent = false;
|
|
break;
|
|
case AxisDirection.left:
|
|
mainAxisUnit = new Offset(-1.0f, 0.0f);
|
|
crossAxisUnit = new Offset(0.0f, 1.0f);
|
|
originOffset = offset + new Offset(geometry.paintExtent, 0.0f);
|
|
addExtent = true;
|
|
break;
|
|
}
|
|
|
|
RenderBox child = firstChild;
|
|
while (child != null) {
|
|
float mainAxisDelta = childMainAxisPosition(child) ?? 0.0f;
|
|
float crossAxisDelta = childCrossAxisPosition(child) ?? 0.0f;
|
|
Offset childOffset = new Offset(
|
|
originOffset.dx + mainAxisUnit.dx * mainAxisDelta + crossAxisUnit.dx * crossAxisDelta,
|
|
originOffset.dy + mainAxisUnit.dy * mainAxisDelta + crossAxisUnit.dy * crossAxisDelta
|
|
);
|
|
if (addExtent) {
|
|
childOffset += mainAxisUnit * paintExtentOf(child);
|
|
}
|
|
|
|
if (mainAxisDelta < constraints.remainingPaintExtent &&
|
|
mainAxisDelta + paintExtentOf(child) > 0) {
|
|
context.paintChild(child, childOffset);
|
|
}
|
|
|
|
child = childAfter(child);
|
|
}
|
|
}
|
|
|
|
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
base.debugFillProperties(properties);
|
|
properties.add(DiagnosticsNode.message(firstChild != null
|
|
? "currently live children: " + indexOf(firstChild) + " to " + indexOf(lastChild)
|
|
: "no children current live"));
|
|
}
|
|
|
|
public bool debugAssertChildListIsNonEmptyAndContiguous() {
|
|
D.assert(() => {
|
|
D.assert(firstChild != null);
|
|
int index = indexOf(firstChild);
|
|
RenderBox child = childAfter(firstChild);
|
|
while (child != null) {
|
|
index += 1;
|
|
D.assert(indexOf(child) == index);
|
|
child = childAfter(child);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
return true;
|
|
}
|
|
|
|
public override List<DiagnosticsNode> debugDescribeChildren() {
|
|
var children = new List<DiagnosticsNode>();
|
|
if (firstChild != null) {
|
|
RenderBox child = firstChild;
|
|
while (true) {
|
|
SliverMultiBoxAdaptorParentData childParentData = (SliverMultiBoxAdaptorParentData) child.parentData;
|
|
children.Add(child.toDiagnosticsNode(name: "child with index " + childParentData.index));
|
|
if (child == lastChild) {
|
|
break;
|
|
}
|
|
|
|
child = childParentData.nextSibling;
|
|
}
|
|
}
|
|
|
|
if (_keepAliveBucket.isNotEmpty()) {
|
|
List<int> indices = _keepAliveBucket.Keys.ToList();
|
|
indices.Sort();
|
|
foreach (int index in indices) {
|
|
children.Add(_keepAliveBucket[index].toDiagnosticsNode(
|
|
name: "child with index " + index + " (kept alive but not laid out)",
|
|
style: DiagnosticsTreeStyle.offstage
|
|
));
|
|
}
|
|
}
|
|
|
|
return children;
|
|
}
|
|
}
|
|
}
|