siyao
3 年前
当前提交
73931109
共有 10 个文件被更改,包括 2081 次插入 和 0 次删除
-
644AwesomeUIWidgets/Assets/Packages/SGrid/render/sliver_staggered_grid.cs
-
3AwesomeUIWidgets/Assets/Packages/SGrid/render/sliver_staggered_grid.cs.meta
-
427AwesomeUIWidgets/Assets/Packages/SGrid/render/sliver_variable_size_box_adaptor.cs
-
3AwesomeUIWidgets/Assets/Packages/SGrid/render/sliver_variable_size_box_adaptor.cs.meta
-
1001AwesomeUIWidgets/Assets/Packages/SGrid/widget/grid.cs
-
3AwesomeUIWidgets/Assets/Packages/SGrid/widget/grid.cs.meta
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
public static class GridUtil |
|||
{ |
|||
delegate StaggeredTile? IndexedStaggeredTileBuilder(int index); |
|||
|
|||
public delegate int? SemanticIndexCallback(Widget widget, int localIndex); |
|||
|
|||
internal static int? _kDefaultSemanticIndexCallback(Widget _, int localIndex) => localIndex; |
|||
|
|||
// public delegate int? ChildIndexGetter(Key key);
|
|||
//
|
|||
// public delegate Widget WidgetBuilder(BuildContext context);
|
|||
//
|
|||
// public delegate Widget IndexedWidgetBuilder(BuildContext context, int index);
|
|||
//
|
|||
// public delegate Widget NullableIndexedWidgetBuilder(BuildContext context, int index);
|
|||
//
|
|||
// public delegate Widget TransitionBuilder(BuildContext context, Widget child);
|
|||
//
|
|||
// public delegate Widget ControlsWidgetBuilder(BuildContext context, VoidCallback onStepContinue = null, VoidCallback onStepCancel = null);
|
|||
//
|
|||
// internal static Widget _createErrorWidget(Exception exception) {
|
|||
// UIWidgetsErrorDetails details = new UIWidgetsErrorDetails(
|
|||
// exception: exception,
|
|||
// library: "widgets library",
|
|||
// context: new ErrorDescription("building")
|
|||
// );
|
|||
// UIWidgetsError.reportError(details);
|
|||
// return ErrorWidget.builder(details);
|
|||
} |
|||
|
|||
} |
|||
|
|||
public class StaggeredGridConfiguration { |
|||
public StaggeredGridConfiguration( |
|||
this.crossAxisCount, |
|||
this.staggeredTileBuilder, |
|||
this.cellExtent, |
|||
this.mainAxisSpacing, |
|||
this.crossAxisSpacing, |
|||
this.reverseCrossAxis, |
|||
this.staggeredTileCount, |
|||
this.mainAxisOffsetsCacheSize = 3 |
|||
) : assert(crossAxisCount > 0), |
|||
assert(cellExtent >= 0), |
|||
assert(mainAxisSpacing >= 0), |
|||
assert(crossAxisSpacing >= 0), |
|||
assert(mainAxisOffsetsCacheSize > 0), |
|||
cellStride = cellExtent + crossAxisSpacing; |
|||
|
|||
readonly int crossAxisCount; |
|||
|
|||
readonly float cellExtent; |
|||
|
|||
readonly float mainAxisSpacing; |
|||
|
|||
readonly float crossAxisSpacing; |
|||
|
|||
readonly IndexedStaggeredTileBuilder staggeredTileBuilder; |
|||
|
|||
readonly int? staggeredTileCount; |
|||
|
|||
readonly bool reverseCrossAxis; |
|||
|
|||
readonly float cellStride; |
|||
|
|||
readonly int mainAxisOffsetsCacheSize; |
|||
|
|||
List<float> generateMainAxisOffsets() => |
|||
List.generate(crossAxisCount, (i) => 0.0); |
|||
|
|||
StaggeredTile? getStaggeredTile(int index) { |
|||
StaggeredTile? tile; |
|||
if (staggeredTileCount == null || index < staggeredTileCount!) { |
|||
// There is maybe a tile for this index.
|
|||
tile = _normalizeStaggeredTile(staggeredTileBuilder(index)); |
|||
} |
|||
return tile; |
|||
} |
|||
|
|||
float _getStaggeredTileMainAxisExtent(StaggeredTile tile) { |
|||
return tile.mainAxisExtent ?? |
|||
(tile.mainAxisCellCount! * cellExtent) + |
|||
(tile.mainAxisCellCount! - 1) * mainAxisSpacing; |
|||
} |
|||
|
|||
StaggeredTile? _normalizeStaggeredTile(StaggeredTile? staggeredTile) { |
|||
if (staggeredTile == null) { |
|||
return null; |
|||
} else { |
|||
readonly crossAxisCellCount = |
|||
staggeredTile.crossAxisCellCount.clamp(0, crossAxisCount).toInt(); |
|||
if (staggeredTile.fitContent) { |
|||
return StaggeredTile.fit(crossAxisCellCount); |
|||
} else { |
|||
return StaggeredTile.extent( |
|||
crossAxisCellCount, _getStaggeredTileMainAxisExtent(staggeredTile)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
class _Block { |
|||
const _Block(this.index, this.crossAxisCount, this.minOffset, this.maxOffset); |
|||
|
|||
final int index; |
|||
final int crossAxisCount; |
|||
final float minOffset; |
|||
final float maxOffset; |
|||
} |
|||
|
|||
const float _epsilon = 0.0001; |
|||
|
|||
bool _nearEqual(float d1, float d2) { |
|||
return (d1 - d2).abs() < _epsilon; |
|||
} |
|||
|
|||
@immutable |
|||
class SliverStaggeredGridGeometry { |
|||
const SliverStaggeredGridGeometry({ |
|||
this.scrollOffset, |
|||
this.crossAxisOffset, |
|||
this.mainAxisExtent, |
|||
this.crossAxisExtent, |
|||
this.crossAxisCellCount, |
|||
this.blockIndex, |
|||
}); |
|||
|
|||
final float scrollOffset; |
|||
|
|||
final float crossAxisOffset; |
|||
|
|||
final float? mainAxisExtent; |
|||
|
|||
final float crossAxisExtent; |
|||
|
|||
final int crossAxisCellCount; |
|||
|
|||
final int blockIndex; |
|||
|
|||
bool get hasTrailingScrollOffset => mainAxisExtent != null; |
|||
|
|||
float get trailingScrollOffset => scrollOffset + (mainAxisExtent ?? 0); |
|||
|
|||
SliverStaggeredGridGeometry copyWith({ |
|||
float? scrollOffset, |
|||
float? crossAxisOffset, |
|||
float? mainAxisExtent, |
|||
float? crossAxisExtent, |
|||
int? crossAxisCellCount, |
|||
int? blockIndex, |
|||
}) { |
|||
return SliverStaggeredGridGeometry( |
|||
scrollOffset: scrollOffset ?? this.scrollOffset, |
|||
crossAxisOffset: crossAxisOffset ?? this.crossAxisOffset, |
|||
mainAxisExtent: mainAxisExtent ?? this.mainAxisExtent, |
|||
crossAxisExtent: crossAxisExtent ?? this.crossAxisExtent, |
|||
crossAxisCellCount: crossAxisCellCount ?? this.crossAxisCellCount, |
|||
blockIndex: blockIndex ?? this.blockIndex, |
|||
); |
|||
} |
|||
|
|||
BoxConstraints getBoxConstraints(SliverConstraints constraints) { |
|||
return constraints.asBoxConstraints( |
|||
minExtent: mainAxisExtent ?? 0.0, |
|||
maxExtent: mainAxisExtent ?? float.infinity, |
|||
crossAxisExtent: crossAxisExtent, |
|||
); |
|||
} |
|||
|
|||
@override |
|||
String toString() { |
|||
return 'SliverStaggeredGridGeometry(' |
|||
'scrollOffset: $scrollOffset, ' |
|||
'crossAxisOffset: $crossAxisOffset, ' |
|||
'mainAxisExtent: $mainAxisExtent, ' |
|||
'crossAxisExtent: $crossAxisExtent, ' |
|||
'crossAxisCellCount: $crossAxisCellCount, ' |
|||
'startIndex: $blockIndex)'; |
|||
} |
|||
} |
|||
|
|||
class RenderSliverStaggeredGrid extends RenderSliverVariableSizeBoxAdaptor { |
|||
RenderSliverStaggeredGrid({ |
|||
RenderSliverVariableSizeBoxChildManager childManager, |
|||
SliverStaggeredGridDelegate gridDelegate, |
|||
}) : _gridDelegate = gridDelegate, |
|||
_pageSizeToViewportOffsets = |
|||
HashMap<float, SplayTreeMap<int, _ViewportOffsets?>>(), |
|||
super(childManager: childManager); |
|||
|
|||
@override |
|||
void setupParentData(RenderObject child) { |
|||
if (child.parentData is! SliverVariableSizeBoxAdaptorParentData) { |
|||
final data = SliverVariableSizeBoxAdaptorParentData(); |
|||
|
|||
// By default we will keep it true.
|
|||
//data.keepAlive = true;
|
|||
child.parentData = data; |
|||
} |
|||
} |
|||
|
|||
SliverStaggeredGridDelegate get gridDelegate => _gridDelegate; |
|||
SliverStaggeredGridDelegate _gridDelegate; |
|||
set gridDelegate(SliverStaggeredGridDelegate value) { |
|||
if (_gridDelegate == value) { |
|||
return; |
|||
} |
|||
if (value.runtimeType != _gridDelegate.runtimeType || |
|||
value.shouldRelayout(_gridDelegate)) { |
|||
markNeedsLayout(); |
|||
} |
|||
_gridDelegate = value; |
|||
} |
|||
|
|||
final HashMap<float, SplayTreeMap<int, _ViewportOffsets?>> |
|||
_pageSizeToViewportOffsets; |
|||
|
|||
@override |
|||
void performLayout() { |
|||
childManager.didStartLayout(); |
|||
childManager.setDidUnderflow(false); |
|||
|
|||
final float scrollOffset = |
|||
constraints.scrollOffset + constraints.cacheOrigin; |
|||
assert(scrollOffset >= 0.0); |
|||
final float remainingExtent = constraints.remainingCacheExtent; |
|||
assert(remainingExtent >= 0.0); |
|||
final float targetEndScrollOffset = scrollOffset + remainingExtent; |
|||
|
|||
bool reachedEnd = false; |
|||
float trailingScrollOffset = 0; |
|||
float leadingScrollOffset = float.infinity; |
|||
bool visible = false; |
|||
int firstIndex = 0; |
|||
int lastIndex = 0; |
|||
|
|||
final configuration = _gridDelegate.getConfiguration(constraints); |
|||
|
|||
final pageSize = configuration.mainAxisOffsetsCacheSize * |
|||
constraints.viewportMainAxisExtent; |
|||
if (pageSize == 0.0) { |
|||
geometry = SliverGeometry.zero; |
|||
childManager.didFinishLayout(); |
|||
return; |
|||
} |
|||
final pageIndex = scrollOffset ~/ pageSize; |
|||
assert(pageIndex >= 0); |
|||
|
|||
// If the viewport is resized, we keep the in memory the old offsets caches. (Useful if only the orientation changes multiple times).
|
|||
final viewportOffsets = _pageSizeToViewportOffsets.putIfAbsent( |
|||
pageSize, () => SplayTreeMap<int, _ViewportOffsets?>()); |
|||
|
|||
_ViewportOffsets? viewportOffset; |
|||
if (viewportOffsets.isEmpty) { |
|||
viewportOffset = |
|||
_ViewportOffsets(configuration.generateMainAxisOffsets(), pageSize); |
|||
viewportOffsets[0] = viewportOffset; |
|||
} else { |
|||
final smallestKey = viewportOffsets.lastKeyBefore(pageIndex + 1); |
|||
viewportOffset = viewportOffsets[smallestKey!]; |
|||
} |
|||
|
|||
// A staggered grid always have to layout the child from the zero-index based one to the last visible.
|
|||
final mainAxisOffsets = viewportOffset!.mainAxisOffsets.toList(); |
|||
final visibleIndices = HashSet<int>(); |
|||
|
|||
// Iterate through all children while they can be visible.
|
|||
for (var index = viewportOffset.firstChildIndex; |
|||
mainAxisOffsets.any((o) => o <= targetEndScrollOffset); |
|||
index++) { |
|||
SliverStaggeredGridGeometry? geometry = |
|||
getSliverStaggeredGeometry(index, configuration, mainAxisOffsets); |
|||
if (geometry == null) { |
|||
// There are either no children, or we are past the end of all our children.
|
|||
reachedEnd = true; |
|||
break; |
|||
} |
|||
|
|||
final bool hasTrailingScrollOffset = geometry.hasTrailingScrollOffset; |
|||
RenderBox? child; |
|||
if (!hasTrailingScrollOffset) { |
|||
// Layout the child to compute its tailingScrollOffset.
|
|||
final constraints = |
|||
BoxConstraints.tightFor(width: geometry.crossAxisExtent); |
|||
child = addAndLayoutChild(index, constraints, parentUsesSize: true); |
|||
geometry = geometry.copyWith(mainAxisExtent: paintExtentOf(child!)); |
|||
} |
|||
|
|||
if (!visible && |
|||
targetEndScrollOffset >= geometry.scrollOffset && |
|||
scrollOffset <= geometry.trailingScrollOffset) { |
|||
visible = true; |
|||
leadingScrollOffset = geometry.scrollOffset; |
|||
firstIndex = index; |
|||
} |
|||
|
|||
if (visible && hasTrailingScrollOffset) { |
|||
child = |
|||
addAndLayoutChild(index, geometry.getBoxConstraints(constraints)); |
|||
} |
|||
|
|||
if (child != null) { |
|||
final childParentData = |
|||
child.parentData! as SliverVariableSizeBoxAdaptorParentData; |
|||
childParentData.layoutOffset = geometry.scrollOffset; |
|||
childParentData.crossAxisOffset = geometry.crossAxisOffset; |
|||
assert(childParentData.index == index); |
|||
} |
|||
|
|||
if (visible && indices.contains(index)) { |
|||
visibleIndices.add(index); |
|||
} |
|||
|
|||
if (geometry.trailingScrollOffset >= |
|||
viewportOffset!.trailingScrollOffset) { |
|||
final nextPageIndex = viewportOffset.pageIndex + 1; |
|||
final nextViewportOffset = _ViewportOffsets(mainAxisOffsets, |
|||
(nextPageIndex + 1) * pageSize, nextPageIndex, index); |
|||
viewportOffsets[nextPageIndex] = nextViewportOffset; |
|||
viewportOffset = nextViewportOffset; |
|||
} |
|||
|
|||
final float endOffset = |
|||
geometry.trailingScrollOffset + configuration.mainAxisSpacing; |
|||
for (var i = 0; i < geometry.crossAxisCellCount; i++) { |
|||
mainAxisOffsets[i + geometry.blockIndex] = endOffset; |
|||
} |
|||
|
|||
trailingScrollOffset = mainAxisOffsets.reduce(math.max); |
|||
lastIndex = index; |
|||
} |
|||
|
|||
collectGarbage(visibleIndices); |
|||
|
|||
if (!visible) { |
|||
if (scrollOffset > viewportOffset!.trailingScrollOffset) { |
|||
// We are outside the bounds, we have to correct the scroll.
|
|||
final viewportOffsetScrollOffset = pageSize * viewportOffset.pageIndex; |
|||
final correction = viewportOffsetScrollOffset - scrollOffset; |
|||
geometry = SliverGeometry( |
|||
scrollOffsetCorrection: correction, |
|||
); |
|||
} else { |
|||
geometry = SliverGeometry.zero; |
|||
childManager.didFinishLayout(); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
float estimatedMaxScrollOffset; |
|||
if (reachedEnd) { |
|||
estimatedMaxScrollOffset = trailingScrollOffset; |
|||
} else { |
|||
estimatedMaxScrollOffset = childManager.estimateMaxScrollOffset( |
|||
constraints, |
|||
firstIndex: firstIndex, |
|||
lastIndex: lastIndex, |
|||
leadingScrollOffset: leadingScrollOffset, |
|||
trailingScrollOffset: trailingScrollOffset, |
|||
); |
|||
assert(estimatedMaxScrollOffset >= |
|||
trailingScrollOffset - leadingScrollOffset); |
|||
} |
|||
|
|||
final float paintExtent = calculatePaintOffset( |
|||
constraints, |
|||
from: leadingScrollOffset, |
|||
to: trailingScrollOffset, |
|||
); |
|||
final float cacheExtent = calculateCacheOffset( |
|||
constraints, |
|||
from: leadingScrollOffset, |
|||
to: trailingScrollOffset, |
|||
); |
|||
|
|||
geometry = SliverGeometry( |
|||
scrollExtent: estimatedMaxScrollOffset, |
|||
paintExtent: paintExtent, |
|||
cacheExtent: cacheExtent, |
|||
maxPaintExtent: estimatedMaxScrollOffset, |
|||
// Conservative to avoid flickering away the clip during scroll.
|
|||
hasVisualOverflow: trailingScrollOffset > targetEndScrollOffset || |
|||
constraints.scrollOffset > 0.0, |
|||
); |
|||
|
|||
// We may have started the layout while scrolled to the end, which would not
|
|||
// expose a child.
|
|||
if (estimatedMaxScrollOffset == trailingScrollOffset) { |
|||
childManager.setDidUnderflow(true); |
|||
} |
|||
childManager.didFinishLayout(); |
|||
} |
|||
|
|||
static SliverStaggeredGridGeometry? getSliverStaggeredGeometry(int index, |
|||
StaggeredGridConfiguration configuration, List<float> offsets) { |
|||
final tile = configuration.getStaggeredTile(index); |
|||
if (tile == null) { |
|||
return null; |
|||
} |
|||
|
|||
final block = _findFirstAvailableBlockWithCrossAxisCount( |
|||
tile.crossAxisCellCount, offsets); |
|||
|
|||
final scrollOffset = block.minOffset; |
|||
var blockIndex = block.index; |
|||
if (configuration.reverseCrossAxis) { |
|||
blockIndex = |
|||
configuration.crossAxisCount - tile.crossAxisCellCount - blockIndex; |
|||
} |
|||
final crossAxisOffset = blockIndex * configuration.cellStride; |
|||
final geometry = SliverStaggeredGridGeometry( |
|||
scrollOffset: scrollOffset, |
|||
crossAxisOffset: crossAxisOffset, |
|||
mainAxisExtent: tile.mainAxisExtent, |
|||
crossAxisExtent: configuration.cellStride * tile.crossAxisCellCount - |
|||
configuration.crossAxisSpacing, |
|||
crossAxisCellCount: tile.crossAxisCellCount, |
|||
blockIndex: block.index, |
|||
); |
|||
return geometry; |
|||
} |
|||
|
|||
static _Block _findFirstAvailableBlockWithCrossAxisCount( |
|||
int crossAxisCount, List<float> offsets) { |
|||
return _findFirstAvailableBlockWithCrossAxisCountAndOffsets( |
|||
crossAxisCount, List.from(offsets)); |
|||
} |
|||
|
|||
static _Block _findFirstAvailableBlockWithCrossAxisCountAndOffsets( |
|||
int crossAxisCount, List<float> offsets) { |
|||
final block = _findFirstAvailableBlock(offsets); |
|||
if (block.crossAxisCount < crossAxisCount) { |
|||
// Not enough space for the specified cross axis count.
|
|||
// We have to fill this block and try again.
|
|||
for (var i = 0; i < block.crossAxisCount; ++i) { |
|||
offsets[i + block.index] = block.maxOffset; |
|||
} |
|||
return _findFirstAvailableBlockWithCrossAxisCountAndOffsets( |
|||
crossAxisCount, offsets); |
|||
} else { |
|||
return block; |
|||
} |
|||
} |
|||
|
|||
static _Block _findFirstAvailableBlock(List<float> offsets) { |
|||
int index = 0; |
|||
float minBlockOffset = float.infinity; |
|||
float maxBlockOffset = float.infinity; |
|||
int crossAxisCount = 1; |
|||
bool contiguous = false; |
|||
|
|||
// We have to use the _nearEqual function because of floating-point arithmetic.
|
|||
// Ex: 0.1 + 0.2 = 0.30000000000000004 and not 0.3.
|
|||
|
|||
for (var i = index; i < offsets.length; ++i) { |
|||
final offset = offsets[i]; |
|||
if (offset < minBlockOffset && !_nearEqual(offset, minBlockOffset)) { |
|||
index = i; |
|||
maxBlockOffset = minBlockOffset; |
|||
minBlockOffset = offset; |
|||
crossAxisCount = 1; |
|||
contiguous = true; |
|||
} else if (_nearEqual(offset, minBlockOffset) && contiguous) { |
|||
crossAxisCount++; |
|||
} else if (offset < maxBlockOffset && |
|||
offset > minBlockOffset && |
|||
!_nearEqual(offset, minBlockOffset)) { |
|||
contiguous = false; |
|||
maxBlockOffset = offset; |
|||
} else { |
|||
contiguous = false; |
|||
} |
|||
} |
|||
|
|||
return _Block(index, crossAxisCount, minBlockOffset, maxBlockOffset); |
|||
} |
|||
} |
|||
|
|||
class _ViewportOffsets { |
|||
_ViewportOffsets( |
|||
List<float> mainAxisOffsets, |
|||
this.trailingScrollOffset, [ |
|||
this.pageIndex = 0, |
|||
this.firstChildIndex = 0, |
|||
]) : mainAxisOffsets = mainAxisOffsets.toList(); |
|||
|
|||
final int pageIndex; |
|||
|
|||
final int firstChildIndex; |
|||
|
|||
final float trailingScrollOffset; |
|||
|
|||
final List<float> mainAxisOffsets; |
|||
|
|||
@override |
|||
String toString() => |
|||
'[$pageIndex-$trailingScrollOffset] ($firstChildIndex, $mainAxisOffsets)'; |
|||
} |
|||
|
|||
public abstract class SliverStaggeredGridDelegate { |
|||
const SliverStaggeredGridDelegate({ |
|||
this.staggeredTileBuilder, |
|||
this.mainAxisSpacing = 0, |
|||
this.crossAxisSpacing = 0, |
|||
this.staggeredTileCount, |
|||
}) : assert(mainAxisSpacing >= 0), |
|||
assert(crossAxisSpacing >= 0); |
|||
|
|||
final float mainAxisSpacing; |
|||
|
|||
final float crossAxisSpacing; |
|||
|
|||
final IndexedStaggeredTileBuilder staggeredTileBuilder; |
|||
|
|||
final int? staggeredTileCount; |
|||
|
|||
bool _debugAssertIsValid() { |
|||
assert(mainAxisSpacing >= 0); |
|||
assert(crossAxisSpacing >= 0); |
|||
return true; |
|||
} |
|||
|
|||
StaggeredGridConfiguration getConfiguration(SliverConstraints constraints); |
|||
|
|||
bool shouldRelayout(SliverStaggeredGridDelegate oldDelegate) { |
|||
return oldDelegate.mainAxisSpacing != mainAxisSpacing || |
|||
oldDelegate.crossAxisSpacing != crossAxisSpacing || |
|||
oldDelegate.staggeredTileCount != staggeredTileCount || |
|||
oldDelegate.staggeredTileBuilder != staggeredTileBuilder; |
|||
} |
|||
} |
|||
|
|||
class SliverStaggeredGridDelegateWithFixedCrossAxisCount |
|||
extends SliverStaggeredGridDelegate { |
|||
const SliverStaggeredGridDelegateWithFixedCrossAxisCount({ |
|||
this.crossAxisCount, |
|||
IndexedStaggeredTileBuilder staggeredTileBuilder, |
|||
float mainAxisSpacing = 0, |
|||
float crossAxisSpacing = 0, |
|||
int? staggeredTileCount, |
|||
}) : assert(crossAxisCount > 0), |
|||
super( |
|||
staggeredTileBuilder: staggeredTileBuilder, |
|||
mainAxisSpacing: mainAxisSpacing, |
|||
crossAxisSpacing: crossAxisSpacing, |
|||
staggeredTileCount: staggeredTileCount, |
|||
); |
|||
|
|||
final int crossAxisCount; |
|||
|
|||
@override |
|||
bool _debugAssertIsValid() { |
|||
assert(crossAxisCount > 0); |
|||
return super._debugAssertIsValid(); |
|||
} |
|||
|
|||
@override |
|||
StaggeredGridConfiguration getConfiguration(SliverConstraints constraints) { |
|||
assert(_debugAssertIsValid()); |
|||
final float usableCrossAxisExtent = |
|||
constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1); |
|||
final float cellExtent = usableCrossAxisExtent / crossAxisCount; |
|||
return StaggeredGridConfiguration( |
|||
crossAxisCount: crossAxisCount, |
|||
staggeredTileBuilder: staggeredTileBuilder, |
|||
staggeredTileCount: staggeredTileCount, |
|||
cellExtent: cellExtent, |
|||
mainAxisSpacing: mainAxisSpacing, |
|||
crossAxisSpacing: crossAxisSpacing, |
|||
reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection), |
|||
); |
|||
} |
|||
|
|||
@override |
|||
bool shouldRelayout( |
|||
covariant SliverStaggeredGridDelegateWithFixedCrossAxisCount |
|||
oldDelegate) { |
|||
return oldDelegate.crossAxisCount != crossAxisCount || |
|||
super.shouldRelayout(oldDelegate); |
|||
} |
|||
} |
|||
|
|||
class SliverStaggeredGridDelegateWithMaxCrossAxisExtent |
|||
extends SliverStaggeredGridDelegate { |
|||
const SliverStaggeredGridDelegateWithMaxCrossAxisExtent({ |
|||
this.maxCrossAxisExtent, |
|||
IndexedStaggeredTileBuilder staggeredTileBuilder, |
|||
float mainAxisSpacing = 0, |
|||
float crossAxisSpacing = 0, |
|||
int? staggeredTileCount, |
|||
}) : assert(maxCrossAxisExtent > 0), |
|||
super( |
|||
staggeredTileBuilder: staggeredTileBuilder, |
|||
mainAxisSpacing: mainAxisSpacing, |
|||
crossAxisSpacing: crossAxisSpacing, |
|||
staggeredTileCount: staggeredTileCount, |
|||
); |
|||
|
|||
final float maxCrossAxisExtent; |
|||
|
|||
@override |
|||
bool _debugAssertIsValid() { |
|||
assert(maxCrossAxisExtent >= 0); |
|||
return super._debugAssertIsValid(); |
|||
} |
|||
|
|||
@override |
|||
StaggeredGridConfiguration getConfiguration(SliverConstraints constraints) { |
|||
assert(_debugAssertIsValid()); |
|||
final int crossAxisCount = |
|||
((constraints.crossAxisExtent + crossAxisSpacing) / |
|||
(maxCrossAxisExtent + crossAxisSpacing)) |
|||
.ceil(); |
|||
|
|||
final float usableCrossAxisExtent = |
|||
constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1); |
|||
|
|||
final float cellExtent = usableCrossAxisExtent / crossAxisCount; |
|||
return StaggeredGridConfiguration( |
|||
crossAxisCount: crossAxisCount, |
|||
staggeredTileBuilder: staggeredTileBuilder, |
|||
staggeredTileCount: staggeredTileCount, |
|||
cellExtent: cellExtent, |
|||
mainAxisSpacing: mainAxisSpacing, |
|||
crossAxisSpacing: crossAxisSpacing, |
|||
reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection), |
|||
); |
|||
} |
|||
|
|||
@override |
|||
bool shouldRelayout( |
|||
covariant SliverStaggeredGridDelegateWithMaxCrossAxisExtent oldDelegate) { |
|||
return oldDelegate.maxCrossAxisExtent != maxCrossAxisExtent || |
|||
super.shouldRelayout(oldDelegate); |
|||
} |
|||
} |
|||
en |
|
|||
fileFormatVersion: 2 |
|||
guid: cf193666e135441881359df6531ecc41 |
|||
timeCreated: 1626750920 |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
public interface RenderSliverVariableSizeBoxChildManager |
|||
{ |
|||
void createChild(int index); |
|||
|
|||
void removeChild(RenderBox child); |
|||
|
|||
float estimateMaxScrollOffset( |
|||
SliverConstraints constraints, |
|||
int? firstIndex = null, |
|||
int? lastIndex = null, |
|||
float? leadingScrollOffset = null, |
|||
float? trailingScrollOffset = null |
|||
); |
|||
|
|||
int childCount { get; } |
|||
|
|||
void didAdoptChild(RenderBox child); |
|||
|
|||
// ignore: avoid_positional_boolean_parameters
|
|||
void setDidUnderflow(bool value); |
|||
|
|||
void didStartLayout(); |
|||
|
|||
void didFinishLayout(); |
|||
|
|||
bool debugAssertChildListLocked(); |
|||
} |
|||
|
|||
public class SliverVariableSizeBoxAdaptorParentData : SliverMultiBoxAdaptorParentData |
|||
{ |
|||
public float crossAxisOffset; |
|||
|
|||
internal bool _keptAlive = false; |
|||
|
|||
public override string ToString() => $"crossAxisOffset={crossAxisOffset}; {base.ToString()}"; |
|||
} |
|||
|
|||
public abstract class RenderSliverVariableSizeBoxAdaptor : TileContainerRenderObjectMixinRenderSliver<RenderBox, |
|||
SliverVariableSizeBoxAdaptorParentData> |
|||
{ |
|||
public RenderSliverVariableSizeBoxAdaptor(RenderSliverVariableSizeBoxChildManager childManager) |
|||
{ |
|||
_childManager = childManager; |
|||
} |
|||
|
|||
public override void setupParentData(RenderObject child) |
|||
{ |
|||
if (!(child.parentData is SliverVariableSizeBoxAdaptorParentData)) |
|||
{ |
|||
child.parentData = new SliverVariableSizeBoxAdaptorParentData(); |
|||
} |
|||
} |
|||
|
|||
protected RenderSliverVariableSizeBoxChildManager childManager |
|||
{ |
|||
get => _childManager; |
|||
} |
|||
|
|||
public readonly RenderSliverVariableSizeBoxChildManager _childManager; |
|||
|
|||
public readonly Dictionary<int, RenderBox> _keepAliveBucket = new Dictionary<int, RenderBox>(); |
|||
|
|||
protected override void adoptChild(AbstractNodeMixinDiagnosticableTree childNode) |
|||
{ |
|||
var child = childNode as RenderObject; |
|||
base.adoptChild(child); |
|||
var childParentData = |
|||
child?.parentData as SliverVariableSizeBoxAdaptorParentData; |
|||
if (childParentData?.keepAlive != null && (bool) !childParentData?.keepAlive) |
|||
{ |
|||
childManager.didAdoptChild(child as RenderBox); |
|||
} |
|||
} |
|||
|
|||
bool _debugAssertChildListLocked() => |
|||
childManager.debugAssertChildListLocked(); |
|||
|
|||
public override void remove(int index) |
|||
{ |
|||
RenderBox child = this[index]; |
|||
|
|||
// if child is null, it means this element was cached - drop the cached element
|
|||
if (child == null) |
|||
{ |
|||
RenderBox cachedChild = _keepAliveBucket[index]; |
|||
if (cachedChild != null) |
|||
{ |
|||
dropChild(cachedChild); |
|||
_keepAliveBucket.Remove(index); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
var childParentData = |
|||
child.parentData as SliverVariableSizeBoxAdaptorParentData; |
|||
if (childParentData?._keptAlive != null && (bool) !childParentData?._keptAlive) |
|||
{ |
|||
base.remove(index); |
|||
return; |
|||
} |
|||
|
|||
D.assert(childParentData != null && |
|||
(childParentData.index != null && (_keepAliveBucket[childParentData.index] == child))); |
|||
_keepAliveBucket.Remove(childParentData.index); |
|||
dropChild(child); |
|||
} |
|||
|
|||
public override void removeAll() |
|||
{ |
|||
base.removeAll(); |
|||
_keepAliveBucket.Values.ToList().ForEach(dropChild); |
|||
_keepAliveBucket.Clear(); |
|||
} |
|||
|
|||
void _createOrObtainChild(int index) |
|||
{ |
|||
invokeLayoutCallback<SliverConstraints>((SliverConstraints constraints) => |
|||
{ |
|||
D.assert(constraints == this.constraints); |
|||
if (_keepAliveBucket.ContainsKey(index)) |
|||
{ |
|||
RenderBox child = _keepAliveBucket[index]; |
|||
_keepAliveBucket.Remove(index); |
|||
var childParentData = |
|||
child.parentData as SliverVariableSizeBoxAdaptorParentData; |
|||
D.assert(childParentData._keptAlive); |
|||
dropChild(child); |
|||
child.parentData = childParentData; |
|||
this[index] = child; |
|||
childParentData._keptAlive = false; |
|||
} |
|||
else |
|||
{ |
|||
_childManager.createChild(index); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
void _destroyOrCacheChild(int index) |
|||
{ |
|||
RenderBox child = this[index]; |
|||
var childParentData = |
|||
child.parentData as SliverVariableSizeBoxAdaptorParentData; |
|||
if (childParentData.keepAlive) |
|||
{ |
|||
D.assert(!childParentData._keptAlive); |
|||
remove(index); |
|||
_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 o) |
|||
{ |
|||
base.attach(owner); |
|||
if (o is PipelineOwner) |
|||
{ |
|||
_keepAliveBucket.Values.ToList().ForEach((child) => child.attach(owner)); |
|||
} |
|||
} |
|||
|
|||
public override void detach() |
|||
{ |
|||
base.detach(); |
|||
_keepAliveBucket.Values.ToList().ForEach((child) => child.detach()); |
|||
} |
|||
|
|||
public override void redepthChildren() |
|||
{ |
|||
base.redepthChildren(); |
|||
_keepAliveBucket.Values.ToList().ForEach(redepthChild); |
|||
} |
|||
|
|||
public override void visitChildren(RenderObjectVisitor visitor) |
|||
{ |
|||
base.visitChildren(visitor); |
|||
_keepAliveBucket.Values.ToList().ForEach(a => visitor(a)); |
|||
} |
|||
|
|||
public bool addChild(int index) |
|||
{ |
|||
D.assert(_debugAssertChildListLocked()); |
|||
_createOrObtainChild(index); |
|||
var child = this[index]; |
|||
if (child != null) |
|||
{ |
|||
D.assert(indexOf(child) == index); |
|||
return true; |
|||
} |
|||
|
|||
childManager.setDidUnderflow(true); |
|||
return false; |
|||
} |
|||
|
|||
public RenderBox addAndLayoutChild( |
|||
int index, |
|||
BoxConstraints childConstraints, |
|||
bool parentUsesSize = false |
|||
) |
|||
{ |
|||
D.assert(_debugAssertChildListLocked()); |
|||
_createOrObtainChild(index); |
|||
var child = this[index]; |
|||
if (child != null) |
|||
{ |
|||
D.assert(indexOf(child) == index); |
|||
child.layout(childConstraints, parentUsesSize: parentUsesSize); |
|||
return child; |
|||
} |
|||
|
|||
childManager.setDidUnderflow(true); |
|||
return null; |
|||
} |
|||
|
|||
protected void collectGarbage(HashSet<int> visibleIndices) |
|||
{ |
|||
D.assert(_debugAssertChildListLocked()); |
|||
D.assert(childCount >= visibleIndices.Count); |
|||
invokeLayoutCallback<SliverConstraints>((SliverConstraints constraints) => |
|||
{ |
|||
// We destroy only those which are not visible.
|
|||
indices.Except(visibleIndices).ToList().ForEach(_destroyOrCacheChild); |
|||
|
|||
// Ask the child manager to remove the children that are no longer being
|
|||
// kept alive. (This should cause _keepAliveBucket to change, so we have
|
|||
// to prepare our list ahead of time.)
|
|||
_keepAliveBucket.Values |
|||
.Where((RenderBox child) => |
|||
{ |
|||
var childParentData = |
|||
child.parentData as SliverVariableSizeBoxAdaptorParentData; |
|||
return childParentData != null && !childParentData.keepAlive; |
|||
}) |
|||
.ToList() |
|||
.ForEach(_childManager.removeChild); |
|||
D.assert(!_keepAliveBucket.Values.Where((RenderBox child) => |
|||
{ |
|||
return child.parentData is SliverVariableSizeBoxAdaptorParentData childParentData && |
|||
!childParentData.keepAlive; |
|||
}).Any()); |
|||
}); |
|||
} |
|||
|
|||
public int indexOf(RenderBox child) |
|||
{ |
|||
var childParentData = |
|||
child.parentData as SliverVariableSizeBoxAdaptorParentData; |
|||
D.assert(childParentData?.index != null); |
|||
return childParentData.index; |
|||
} |
|||
|
|||
protected float paintExtentOf(RenderBox child) |
|||
{ |
|||
D.assert(child.hasSize); |
|||
switch (constraints.axis) |
|||
{ |
|||
case Axis.horizontal: |
|||
return child.size.width; |
|||
case Axis.vertical: |
|||
return child.size.height; |
|||
default: |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
protected override bool hitTestChildren(SliverHitTestResult result, |
|||
float mainAxisPosition, float crossAxisPosition) |
|||
{ |
|||
foreach (var child in children) |
|||
{ |
|||
if (this.hitTestBoxChild( |
|||
new BoxHitTestResult(result), |
|||
child, |
|||
mainAxisPosition: mainAxisPosition, |
|||
crossAxisPosition: crossAxisPosition) |
|||
) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public override float? childMainAxisPosition(RenderObject child) |
|||
{ |
|||
return childScrollOffset(child) - constraints.scrollOffset; |
|||
} |
|||
|
|||
public override float? childCrossAxisPosition(RenderObject child) |
|||
{ |
|||
var childParentData = |
|||
child.parentData as SliverVariableSizeBoxAdaptorParentData; |
|||
return childParentData.crossAxisOffset; |
|||
} |
|||
|
|||
public override float? childScrollOffset(RenderObject child) |
|||
{ |
|||
D.assert(child.parent == this); |
|||
var childParentData = |
|||
child.parentData as SliverVariableSizeBoxAdaptorParentData; |
|||
D.assert(childParentData != null && childParentData.layoutOffset != null); |
|||
return childParentData.layoutOffset; |
|||
} |
|||
|
|||
public override void applyPaintTransform(RenderObject child, Matrix4 transform) |
|||
{ |
|||
this.applyPaintTransformForBoxChild(child as RenderBox, transform); |
|||
} |
|||
|
|||
public override void paint(PaintingContext context, Offset offset) |
|||
{ |
|||
if (childCount == 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
// offset is to the top-left corner, regardless of our axis direction.
|
|||
// originOffset gives us the delta from the real origin to the origin in the axis direction.
|
|||
Offset mainAxisUnit = new Offset(0, 0); |
|||
Offset crossAxisUnit = new Offset(0, 0); |
|||
Offset originOffset = new Offset(0, 0); |
|||
bool? addExtent = null; |
|||
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection( |
|||
constraints.axisDirection, constraints.growthDirection)) |
|||
{ |
|||
case AxisDirection.up: |
|||
mainAxisUnit = new Offset (0, -1); |
|||
crossAxisUnit = new Offset (1, 0); |
|||
originOffset = offset + new Offset(0, geometry.paintExtent); |
|||
addExtent = true; |
|||
break; |
|||
case AxisDirection.right: |
|||
mainAxisUnit = new Offset (1, 0); |
|||
crossAxisUnit = new Offset (0, 1); |
|||
originOffset = offset; |
|||
addExtent = false; |
|||
break; |
|||
case AxisDirection.down: |
|||
mainAxisUnit = new Offset (0, 1); |
|||
crossAxisUnit = new Offset (1, 0); |
|||
originOffset = offset; |
|||
addExtent = false; |
|||
break; |
|||
case AxisDirection.left: |
|||
mainAxisUnit = new Offset (-1, 0); |
|||
crossAxisUnit = new Offset (0, 1); |
|||
originOffset = offset + new Offset(geometry.paintExtent, 0); |
|||
addExtent = true; |
|||
break; |
|||
} |
|||
|
|||
foreach (var child in children) { |
|||
float? mainAxisDelta = childMainAxisPosition(child); |
|||
float? crossAxisDelta = childCrossAxisPosition(child); |
|||
Offset childOffset = new Offset( |
|||
(float) (originOffset.dx + |
|||
mainAxisUnit.dx * mainAxisDelta + |
|||
crossAxisUnit.dx * crossAxisDelta), |
|||
(float) (originOffset.dy + |
|||
mainAxisUnit.dy * mainAxisDelta + |
|||
crossAxisUnit.dy * crossAxisDelta) |
|||
); |
|||
if (addExtent.Value) |
|||
{ |
|||
childOffset += mainAxisUnit * paintExtentOf(child); |
|||
} |
|||
|
|||
context.paintChild(child, childOffset); |
|||
} |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) |
|||
{ |
|||
base.debugFillProperties(properties); |
|||
properties.add(DiagnosticsNode.message(childCount > 0 |
|||
? $"currently live children: ${string.Join(", ", indices)}" |
|||
: "no children current live")); |
|||
} |
|||
|
|||
public override List<DiagnosticsNode> debugDescribeChildren() |
|||
{ |
|||
List<DiagnosticsNode> |
|||
childList = new List<DiagnosticsNode>(); |
|||
if (childCount > 0) |
|||
{ |
|||
foreach (var child in |
|||
children) { |
|||
var childParentData = |
|||
child.parentData as SliverVariableSizeBoxAdaptorParentData; |
|||
childList.Add(child.toDiagnosticsNode( |
|||
name: $"child with index {childParentData.index}")); |
|||
} |
|||
} |
|||
|
|||
if (_keepAliveBucket.isNotEmpty()) |
|||
{ |
|||
List<int> indices = _keepAliveBucket.Keys.ToList(); |
|||
indices.Sort(); |
|||
foreach (var index in indices) { |
|||
childList.Add(_keepAliveBucket[index].toDiagnosticsNode( |
|||
name: $"child with index {index} (kept alive offstage)", |
|||
style: DiagnosticsTreeStyle.offstage |
|||
)); |
|||
} |
|||
} |
|||
|
|||
return childList; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 274a2fe33c69454ea4202770796e123d |
|||
timeCreated: 1626667380 |
1001
AwesomeUIWidgets/Assets/Packages/SGrid/widget/grid.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 9363f8d6ad684d27b22851acf4c50f7e |
|||
timeCreated: 1626751663 |
撰写
预览
正在加载...
取消
保存
Reference in new issue