您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
520 行
16 KiB
520 行
16 KiB
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Unity.UIWidgets.external;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.rendering;
|
|
using Unity.UIWidgets.widgets;
|
|
|
|
public abstract class SliverVariableSizeBoxAdaptorWidget : SliverWithKeepAliveWidget
|
|
{
|
|
public SliverVariableSizeBoxAdaptorWidget(
|
|
SliverChildDelegate Delegate,
|
|
Key key = null
|
|
) : base(key: key)
|
|
{
|
|
this.Delegate = Delegate;
|
|
}
|
|
|
|
public readonly SliverChildDelegate Delegate;
|
|
|
|
public override Element createElement()
|
|
{
|
|
return new SliverVariableSizeBoxAdaptorElement(this);
|
|
}
|
|
|
|
public abstract override RenderObject createRenderObject(BuildContext context);
|
|
|
|
public float? estimateMaxScrollOffset(
|
|
SliverConstraints constraints,
|
|
int firstIndex,
|
|
int lastIndex,
|
|
float leadingScrollOffset,
|
|
float trailingScrollOffset
|
|
)
|
|
{
|
|
D.assert(lastIndex >= firstIndex);
|
|
return Delegate.estimateMaxScrollOffset(
|
|
firstIndex,
|
|
lastIndex,
|
|
leadingScrollOffset,
|
|
trailingScrollOffset
|
|
);
|
|
}
|
|
|
|
public override void debugFillProperties(DiagnosticPropertiesBuilder properties)
|
|
{
|
|
base.debugFillProperties(properties);
|
|
properties.add(
|
|
new DiagnosticsProperty<SliverChildDelegate>("delegate", Delegate)
|
|
);
|
|
}
|
|
}
|
|
|
|
public class SliverVariableSizeBoxAdaptorElement : RenderObjectElement
|
|
, RenderSliverVariableSizeBoxChildManager
|
|
{
|
|
public SliverVariableSizeBoxAdaptorElement(SliverVariableSizeBoxAdaptorWidget widget)
|
|
: base(widget)
|
|
{
|
|
}
|
|
|
|
public override RenderObjectWidget widget
|
|
{
|
|
get { return base.widget as SliverVariableSizeBoxAdaptorWidget; }
|
|
}
|
|
|
|
public override RenderObject renderObject
|
|
{
|
|
get { return base.renderObject as RenderSliverVariableSizeBoxAdaptor; }
|
|
}
|
|
|
|
public override void update(Widget newWidgetx)
|
|
{
|
|
SliverVariableSizeBoxAdaptorWidget newWidget = newWidgetx as SliverVariableSizeBoxAdaptorWidget;
|
|
SliverVariableSizeBoxAdaptorWidget oldWidget = widget as SliverVariableSizeBoxAdaptorWidget;
|
|
base.update(newWidget);
|
|
SliverChildDelegate newDelegate = newWidget.Delegate;
|
|
SliverChildDelegate oldDelegate = oldWidget.Delegate;
|
|
if (newDelegate != oldDelegate &&
|
|
(newDelegate.GetType() != oldDelegate.GetType() ||
|
|
newDelegate.shouldRebuild(oldDelegate)))
|
|
{
|
|
performRebuild();
|
|
}
|
|
}
|
|
|
|
// We inflate widgets at two different times:
|
|
// 1. When we ourselves are told to rebuild (see performRebuild).
|
|
// 2. When our render object needs a child (see createChild).
|
|
// In both cases, we cache the results of calling into our delegate to get the widget,
|
|
// so that if we do case 2 later, we don't call the builder again.
|
|
// Any time we do case 1, though, we reset the cache.
|
|
|
|
private readonly Dictionary<int, Widget> _childWidgets = new Dictionary<int, Widget>();
|
|
|
|
readonly SplayTree<int, Element> _childElements =
|
|
new SplayTree<int, Element>();
|
|
|
|
protected override void performRebuild()
|
|
{
|
|
_childWidgets.Clear(); // Reset the cache, as described above.
|
|
base.performRebuild();
|
|
D.assert(_currentlyUpdatingChildIndex == null);
|
|
try
|
|
{
|
|
int firstIndex;
|
|
int lastIndex;
|
|
if (_childElements.isEmpty())
|
|
{
|
|
firstIndex = 0;
|
|
lastIndex = 0;
|
|
}
|
|
else if (_didUnderflow)
|
|
{
|
|
firstIndex = _childElements.firstKey();
|
|
lastIndex = _childElements.lastKey() + 1;
|
|
}
|
|
else
|
|
{
|
|
firstIndex = _childElements.firstKey();
|
|
lastIndex = _childElements.lastKey();
|
|
}
|
|
|
|
for (int index = firstIndex; index <= lastIndex; ++index)
|
|
{
|
|
_currentlyUpdatingChildIndex = index;
|
|
Element newChild =
|
|
updateChild(_childElements[index], _build(index), index);
|
|
if (newChild != null)
|
|
{
|
|
_childElements[index] = newChild;
|
|
}
|
|
else
|
|
{
|
|
_childElements.Remove(index);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_currentlyUpdatingChildIndex = null;
|
|
}
|
|
}
|
|
|
|
|
|
Widget _build(int index)
|
|
{
|
|
return _childWidgets.putIfAbsent(
|
|
index, () => ((SliverVariableSizeBoxAdaptorWidget) widget).Delegate.build(this, index));
|
|
}
|
|
|
|
public void createChild(int index)
|
|
{
|
|
D.assert(_currentlyUpdatingChildIndex == null);
|
|
owner.buildScope(this, () =>
|
|
{
|
|
Element newChild;
|
|
try
|
|
{
|
|
_currentlyUpdatingChildIndex = index;
|
|
newChild = updateChild(_childElements.getOrDefault(index, null), _build(index), index);
|
|
}
|
|
finally
|
|
{
|
|
_currentlyUpdatingChildIndex = null;
|
|
}
|
|
|
|
if (newChild != null)
|
|
{
|
|
_childElements[index] = newChild;
|
|
}
|
|
else
|
|
{
|
|
_childElements.Remove(index);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected override Element updateChild(Element child, Widget newWidget, object newSlot)
|
|
{
|
|
Element newChild = base.updateChild(child, newWidget, newSlot);
|
|
var oldParentData = child?.renderObject?.parentData
|
|
as SliverVariableSizeBoxAdaptorParentData;
|
|
var newParentData = newChild?.renderObject?.parentData
|
|
as SliverVariableSizeBoxAdaptorParentData;
|
|
|
|
// set keepAlive to true in order to populate the cache
|
|
if (newParentData != null)
|
|
{
|
|
newParentData.keepAlive = true;
|
|
}
|
|
|
|
// Preserve the old layoutOffset if the renderObject was swapped out.
|
|
if (oldParentData != newParentData &&
|
|
oldParentData != null &&
|
|
newParentData != null)
|
|
{
|
|
newParentData.layoutOffset = oldParentData.layoutOffset;
|
|
}
|
|
|
|
return newChild;
|
|
}
|
|
|
|
public override void forgetChild(Element child)
|
|
{
|
|
D.assert(child.slot != null);
|
|
D.assert(_childElements.ContainsKey((int) child.slot));
|
|
_childElements.Remove((int) child.slot);
|
|
base.forgetChild(child);
|
|
}
|
|
|
|
public void removeChild(RenderBox child)
|
|
{
|
|
int index = ((RenderSliverVariableSizeBoxAdaptor) renderObject).indexOf(child);
|
|
D.assert(_currentlyUpdatingChildIndex == null);
|
|
D.assert(index >= 0);
|
|
owner.buildScope(this, () =>
|
|
{
|
|
D.assert(_childElements.ContainsKey(index));
|
|
try
|
|
{
|
|
_currentlyUpdatingChildIndex = index;
|
|
Element result = updateChild(_childElements[index], null, index);
|
|
D.assert(result == null);
|
|
}
|
|
finally
|
|
{
|
|
_currentlyUpdatingChildIndex = null;
|
|
}
|
|
|
|
_childElements.Remove(index);
|
|
D.assert(!_childElements.ContainsKey(index));
|
|
});
|
|
}
|
|
|
|
float? _extrapolateMaxScrollOffset(
|
|
int? firstIndex,
|
|
int? lastIndex,
|
|
float? leadingScrollOffset,
|
|
float? trailingScrollOffset
|
|
)
|
|
{
|
|
int? childCount = ((SliverVariableSizeBoxAdaptorWidget) widget).Delegate.estimatedChildCount;
|
|
if (childCount == null)
|
|
{
|
|
return float.PositiveInfinity;
|
|
}
|
|
|
|
if (lastIndex == childCount - 1)
|
|
{
|
|
return trailingScrollOffset;
|
|
}
|
|
|
|
int? reifiedCount = lastIndex - firstIndex + 1;
|
|
float? averageExtent =
|
|
(trailingScrollOffset - leadingScrollOffset) / reifiedCount;
|
|
int? remainingCount = childCount - lastIndex - 1;
|
|
return trailingScrollOffset + averageExtent * remainingCount;
|
|
}
|
|
|
|
public float estimateMaxScrollOffset(
|
|
SliverConstraints constraints,
|
|
int? firstIndex = null,
|
|
int? lastIndex = null,
|
|
float? leadingScrollOffset = null,
|
|
float? trailingScrollOffset = null
|
|
)
|
|
{
|
|
return ((SliverVariableSizeBoxAdaptorWidget) widget).estimateMaxScrollOffset(
|
|
constraints,
|
|
firstIndex.Value,
|
|
lastIndex.Value,
|
|
leadingScrollOffset.Value,
|
|
trailingScrollOffset.Value
|
|
) ??
|
|
_extrapolateMaxScrollOffset(
|
|
firstIndex,
|
|
lastIndex,
|
|
leadingScrollOffset,
|
|
trailingScrollOffset
|
|
).Value;
|
|
}
|
|
|
|
public int childCount
|
|
{
|
|
get { return ((SliverVariableSizeBoxAdaptorWidget) widget).Delegate.estimatedChildCount ?? 0; }
|
|
}
|
|
|
|
public void didStartLayout()
|
|
{
|
|
D.assert(debugAssertChildListLocked());
|
|
}
|
|
|
|
public void didFinishLayout()
|
|
{
|
|
D.assert(debugAssertChildListLocked());
|
|
int firstIndex = _childElements.firstKey();
|
|
int lastIndex = _childElements.lastKey();
|
|
((SliverVariableSizeBoxAdaptorWidget) widget).Delegate.didFinishLayout(firstIndex, lastIndex);
|
|
}
|
|
|
|
int? _currentlyUpdatingChildIndex;
|
|
|
|
public bool debugAssertChildListLocked()
|
|
{
|
|
D.assert(_currentlyUpdatingChildIndex == null);
|
|
return true;
|
|
}
|
|
|
|
public void didAdoptChild(RenderBox child)
|
|
{
|
|
D.assert(_currentlyUpdatingChildIndex != null);
|
|
var childParentData =
|
|
child.parentData as SliverVariableSizeBoxAdaptorParentData;
|
|
childParentData.index = _currentlyUpdatingChildIndex.Value;
|
|
}
|
|
|
|
bool _didUnderflow = false;
|
|
|
|
public void setDidUnderflow(bool value)
|
|
{
|
|
_didUnderflow = value;
|
|
}
|
|
|
|
protected override void insertChildRenderObject(RenderObject childObject, object o)
|
|
{
|
|
var child = childObject as RenderBox;
|
|
int slot = (int) o;
|
|
D.assert(_currentlyUpdatingChildIndex == slot);
|
|
D.assert(((RenderSliverVariableSizeBoxAdaptor) renderObject).debugValidateChild(child));
|
|
((RenderSliverVariableSizeBoxAdaptor) renderObject)[_currentlyUpdatingChildIndex.Value] = child;
|
|
D.assert(() =>
|
|
{
|
|
var childParentData =
|
|
child.parentData as SliverVariableSizeBoxAdaptorParentData;
|
|
D.assert(slot == childParentData.index);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
protected override void moveChildRenderObject(RenderObject child, object slot)
|
|
{
|
|
D.assert(false);
|
|
}
|
|
|
|
protected override void removeChildRenderObject(RenderObject child)
|
|
{
|
|
D.assert(_currentlyUpdatingChildIndex != null);
|
|
((RenderSliverVariableSizeBoxAdaptor) renderObject).remove(_currentlyUpdatingChildIndex.Value);
|
|
}
|
|
|
|
public override void visitChildren(ElementVisitor visitor)
|
|
{
|
|
// The toList() is to make a copy so that the underlying list can be modified by
|
|
// the visitor:
|
|
_childElements.Values.ToList().ForEach(v => visitor(v));
|
|
}
|
|
|
|
public override void debugVisitOnstageChildren(ElementVisitor visitor)
|
|
{
|
|
_childElements.Values.Where((Element child) =>
|
|
{
|
|
var parentData =
|
|
child.renderObject.parentData as SliverMultiBoxAdaptorParentData;
|
|
float itemExtent = 0;
|
|
SliverConstraints sliverConstraints = (SliverConstraints) renderObject.constraints;
|
|
switch (sliverConstraints.axis)
|
|
{
|
|
case Axis.horizontal:
|
|
itemExtent = child.renderObject.paintBounds.width;
|
|
break;
|
|
case Axis.vertical:
|
|
itemExtent = child.renderObject.paintBounds.height;
|
|
break;
|
|
}
|
|
|
|
return parentData.layoutOffset <
|
|
sliverConstraints.scrollOffset +
|
|
sliverConstraints.remainingPaintExtent &&
|
|
parentData.layoutOffset + itemExtent >
|
|
sliverConstraints.scrollOffset;
|
|
}).ToList().ForEach(v => visitor(v));
|
|
}
|
|
}
|
|
|
|
public class SliverStaggeredGrid : SliverVariableSizeBoxAdaptorWidget
|
|
{
|
|
public SliverStaggeredGrid(
|
|
SliverChildDelegate Delegate,
|
|
SliverStaggeredGridDelegate gridDelegate,
|
|
Key key = null
|
|
) : base(key: key, Delegate: Delegate)
|
|
{
|
|
this.gridDelegate = gridDelegate;
|
|
}
|
|
|
|
public static SliverStaggeredGrid count(
|
|
int crossAxisCount,
|
|
Key key = null,
|
|
float mainAxisSpacing = 0.0f,
|
|
float crossAxisSpacing = 0.0f,
|
|
List<Widget> children = null,
|
|
List<StaggeredTile> staggeredTiles = null
|
|
)
|
|
{
|
|
staggeredTiles = staggeredTiles ?? new List<StaggeredTile>();
|
|
children = children ?? new List<Widget>();
|
|
var gridDelegate = new SliverStaggeredGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: crossAxisCount,
|
|
mainAxisSpacing: mainAxisSpacing,
|
|
crossAxisSpacing: crossAxisSpacing,
|
|
staggeredTileBuilder: (i) => staggeredTiles[i],
|
|
staggeredTileCount: staggeredTiles.Count
|
|
);
|
|
return new SliverStaggeredGrid(
|
|
Delegate: new SliverChildListDelegate(
|
|
children
|
|
),
|
|
gridDelegate: gridDelegate,
|
|
key: key
|
|
);
|
|
}
|
|
|
|
public static SliverStaggeredGrid countBuilder(
|
|
int crossAxisCount,
|
|
GridUtil.IndexedStaggeredTileBuilder staggeredTileBuilder,
|
|
IndexedWidgetBuilder itemBuilder,
|
|
int itemCount,
|
|
Key key = null,
|
|
float mainAxisSpacing = 0,
|
|
float crossAxisSpacing = 0
|
|
)
|
|
{
|
|
var gridDelegate = new SliverStaggeredGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: crossAxisCount,
|
|
mainAxisSpacing: mainAxisSpacing,
|
|
crossAxisSpacing: crossAxisSpacing,
|
|
staggeredTileBuilder: staggeredTileBuilder,
|
|
staggeredTileCount: itemCount
|
|
);
|
|
|
|
return new SliverStaggeredGrid(
|
|
Delegate: new SliverChildBuilderDelegate(
|
|
itemBuilder,
|
|
childCount: itemCount
|
|
),
|
|
gridDelegate: gridDelegate,
|
|
key: key
|
|
);
|
|
}
|
|
|
|
public static SliverStaggeredGrid extent(
|
|
float maxCrossAxisExtent,
|
|
Key key = null,
|
|
float mainAxisSpacing = 0,
|
|
float crossAxisSpacing = 0,
|
|
List<Widget> children = null, //const <Widget>[],
|
|
List<StaggeredTile> staggeredTiles = null // = const <StaggeredTile>[],
|
|
)
|
|
{
|
|
children = new List<Widget>();
|
|
staggeredTiles = new List<StaggeredTile>();
|
|
var gridDelegate = new SliverStaggeredGridDelegateWithMaxCrossAxisExtent(
|
|
maxCrossAxisExtent: maxCrossAxisExtent,
|
|
mainAxisSpacing: mainAxisSpacing,
|
|
crossAxisSpacing: crossAxisSpacing,
|
|
staggeredTileBuilder: (i) => staggeredTiles[i],
|
|
staggeredTileCount: staggeredTiles.Count
|
|
);
|
|
return new SliverStaggeredGrid(
|
|
key: key,
|
|
Delegate: new SliverChildListDelegate(
|
|
children
|
|
),
|
|
gridDelegate: gridDelegate
|
|
);
|
|
}
|
|
|
|
public static SliverStaggeredGrid extentBuilder(
|
|
float maxCrossAxisExtent,
|
|
GridUtil.IndexedStaggeredTileBuilder staggeredTileBuilder,
|
|
IndexedWidgetBuilder itemBuilder,
|
|
int itemCount,
|
|
Key key = null,
|
|
float mainAxisSpacing = 0,
|
|
float crossAxisSpacing = 0
|
|
)
|
|
{
|
|
var gridDelegate = new SliverStaggeredGridDelegateWithMaxCrossAxisExtent(
|
|
maxCrossAxisExtent: maxCrossAxisExtent,
|
|
mainAxisSpacing: mainAxisSpacing,
|
|
crossAxisSpacing: crossAxisSpacing,
|
|
staggeredTileBuilder: staggeredTileBuilder,
|
|
staggeredTileCount: itemCount
|
|
);
|
|
return new SliverStaggeredGrid(
|
|
key: key,
|
|
Delegate: new SliverChildBuilderDelegate(
|
|
itemBuilder,
|
|
childCount: itemCount
|
|
),
|
|
gridDelegate: gridDelegate
|
|
);
|
|
}
|
|
|
|
public readonly SliverStaggeredGridDelegate gridDelegate;
|
|
|
|
public override RenderObject createRenderObject(BuildContext context)
|
|
{
|
|
var element = context as SliverVariableSizeBoxAdaptorElement;
|
|
return new RenderSliverStaggeredGrid(
|
|
childManager: element, gridDelegate: gridDelegate);
|
|
}
|
|
|
|
public override void updateRenderObject(BuildContext context, RenderObject o)
|
|
{
|
|
var renderObject = o as RenderSliverStaggeredGrid;
|
|
renderObject.gridDelegate = gridDelegate;
|
|
}
|
|
}
|