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("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 _childWidgets = new Dictionary(); readonly SplayTree _childElements = new SplayTree(); 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 children = null, List staggeredTiles = null ) { staggeredTiles = staggeredTiles ?? new List(); children = children ?? new List(); 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 children = null, //const [], List staggeredTiles = null // = const [], ) { children = new List(); staggeredTiles = new List(); 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; } }