您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
458 行
18 KiB
458 行
18 KiB
using System.Collections.Generic;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.rendering;
|
|
using Unity.UIWidgets.ui;
|
|
using UnityEngine;
|
|
|
|
namespace com.unity.uiwidgets.Runtime.rendering {
|
|
public class SliverGridGeometry {
|
|
public SliverGridGeometry(
|
|
float? scrollOffset = null,
|
|
float? crossAxisOffset = null,
|
|
float? mainAxisExtent = null,
|
|
float? crossAxisExtent = null
|
|
) {
|
|
this.scrollOffset = scrollOffset;
|
|
this.crossAxisOffset = crossAxisOffset;
|
|
this.mainAxisExtent = mainAxisExtent;
|
|
this.crossAxisExtent = crossAxisExtent;
|
|
}
|
|
|
|
public readonly float? scrollOffset;
|
|
|
|
public readonly float? crossAxisOffset;
|
|
|
|
public readonly float? mainAxisExtent;
|
|
|
|
public readonly float? crossAxisExtent;
|
|
|
|
public float? trailingScrollOffset {
|
|
get { return scrollOffset + mainAxisExtent; }
|
|
}
|
|
|
|
public BoxConstraints getBoxConstraints(SliverConstraints constraints) {
|
|
return constraints.asBoxConstraints(
|
|
minExtent: mainAxisExtent ?? 0.0f,
|
|
maxExtent: mainAxisExtent ?? 0.0f,
|
|
crossAxisExtent: crossAxisExtent ?? 0.0f
|
|
);
|
|
}
|
|
|
|
public override string ToString() {
|
|
List<string> properties = new List<string>();
|
|
properties.Add("scrollOffset: $scrollOffset");
|
|
properties.Add("crossAxisOffset: $crossAxisOffset");
|
|
properties.Add("mainAxisExtent: $mainAxisExtent");
|
|
properties.Add("crossAxisExtent: $crossAxisExtent");
|
|
return $"SliverGridGeometry({string.Join(", ",properties)})";
|
|
}
|
|
}
|
|
|
|
public abstract class SliverGridLayout {
|
|
public SliverGridLayout() {
|
|
}
|
|
|
|
public abstract int getMinChildIndexForScrollOffset(float scrollOffset);
|
|
|
|
public abstract int getMaxChildIndexForScrollOffset(float scrollOffset);
|
|
|
|
public abstract SliverGridGeometry getGeometryForChildIndex(int index);
|
|
|
|
public abstract float computeMaxScrollOffset(int childCount);
|
|
}
|
|
|
|
public class SliverGridRegularTileLayout : SliverGridLayout {
|
|
public SliverGridRegularTileLayout(
|
|
int? crossAxisCount = null,
|
|
float? mainAxisStride = null,
|
|
float? crossAxisStride = null,
|
|
float? childMainAxisExtent = null,
|
|
float? childCrossAxisExtent = null,
|
|
bool? reverseCrossAxis = null
|
|
) {
|
|
D.assert(crossAxisCount > 0);
|
|
D.assert(mainAxisStride >= 0);
|
|
D.assert(crossAxisStride >= 0);
|
|
D.assert(childMainAxisExtent >= 0);
|
|
D.assert(childCrossAxisExtent >= 0);
|
|
D.assert(reverseCrossAxis != null);
|
|
this.crossAxisCount = crossAxisCount;
|
|
this.mainAxisStride = mainAxisStride;
|
|
this.crossAxisStride = crossAxisStride;
|
|
this.childMainAxisExtent = childMainAxisExtent;
|
|
this.childCrossAxisExtent = childCrossAxisExtent;
|
|
this.reverseCrossAxis = reverseCrossAxis;
|
|
}
|
|
|
|
public readonly int? crossAxisCount;
|
|
|
|
public readonly float? mainAxisStride;
|
|
|
|
public readonly float? crossAxisStride;
|
|
|
|
public readonly float? childMainAxisExtent;
|
|
|
|
public readonly float? childCrossAxisExtent;
|
|
|
|
public readonly bool? reverseCrossAxis;
|
|
|
|
public override int getMinChildIndexForScrollOffset(float scrollOffset) {
|
|
return (mainAxisStride > 0.0f
|
|
? crossAxisCount * ((int) (scrollOffset / mainAxisStride))
|
|
: 0) ?? 0;
|
|
}
|
|
|
|
public override int getMaxChildIndexForScrollOffset(float scrollOffset) {
|
|
if (mainAxisStride > 0.0f) {
|
|
int? mainAxisCount = (scrollOffset / mainAxisStride)?.ceil();
|
|
return Mathf.Max(0, (crossAxisCount * mainAxisCount - 1) ?? 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
float _getOffsetFromStartInCrossAxis(float crossAxisStart) {
|
|
if (reverseCrossAxis == true) {
|
|
return (crossAxisCount * crossAxisStride - crossAxisStart - childCrossAxisExtent
|
|
- (crossAxisStride - childCrossAxisExtent)) ??
|
|
0.0f;
|
|
}
|
|
|
|
return crossAxisStart;
|
|
}
|
|
|
|
public override SliverGridGeometry getGeometryForChildIndex(int index) {
|
|
float? crossAxisStart = (index % crossAxisCount) * crossAxisStride;
|
|
return new SliverGridGeometry(
|
|
scrollOffset: (index / crossAxisCount) * mainAxisStride,
|
|
crossAxisOffset:
|
|
_getOffsetFromStartInCrossAxis(crossAxisStart ?? 0.0f),
|
|
mainAxisExtent:
|
|
childMainAxisExtent,
|
|
crossAxisExtent:
|
|
childCrossAxisExtent
|
|
);
|
|
}
|
|
|
|
public override float computeMaxScrollOffset(int childCount) {
|
|
int? mainAxisCount = ((childCount - 1) / crossAxisCount) + 1;
|
|
float? mainAxisSpacing = mainAxisStride - childMainAxisExtent;
|
|
return (mainAxisStride * mainAxisCount - mainAxisSpacing) ?? 0.0f;
|
|
}
|
|
}
|
|
|
|
public abstract class SliverGridDelegate {
|
|
|
|
protected SliverGridDelegate() { }
|
|
|
|
public abstract SliverGridLayout getLayout(SliverConstraints constraints);
|
|
|
|
public abstract bool shouldRelayout(SliverGridDelegate oldDelegate);
|
|
}
|
|
|
|
public class SliverGridDelegateWithFixedCrossAxisCount : SliverGridDelegate {
|
|
public SliverGridDelegateWithFixedCrossAxisCount(
|
|
int crossAxisCount,
|
|
float mainAxisSpacing = 0.0f,
|
|
float crossAxisSpacing = 0.0f,
|
|
float childAspectRatio = 1.0f
|
|
) {
|
|
D.assert(crossAxisCount > 0);
|
|
D.assert(mainAxisSpacing >= 0);
|
|
D.assert(crossAxisSpacing >= 0);
|
|
D.assert(childAspectRatio > 0);
|
|
this.crossAxisCount = crossAxisCount;
|
|
this.mainAxisSpacing = mainAxisSpacing;
|
|
this.crossAxisSpacing = crossAxisSpacing;
|
|
this.childAspectRatio = childAspectRatio;
|
|
}
|
|
|
|
public readonly int crossAxisCount;
|
|
|
|
public readonly float mainAxisSpacing;
|
|
|
|
public readonly float crossAxisSpacing;
|
|
|
|
public readonly float childAspectRatio;
|
|
|
|
bool _debugAssertIsValid() {
|
|
D.assert(crossAxisCount > 0);
|
|
D.assert(mainAxisSpacing >= 0.0f);
|
|
D.assert(crossAxisSpacing >= 0.0f);
|
|
D.assert(childAspectRatio > 0.0f);
|
|
return true;
|
|
}
|
|
|
|
public override SliverGridLayout getLayout(SliverConstraints constraints) {
|
|
D.assert(_debugAssertIsValid());
|
|
float usableCrossAxisExtent =
|
|
constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
|
|
float childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
|
|
float childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
|
|
return new SliverGridRegularTileLayout(
|
|
crossAxisCount: crossAxisCount,
|
|
mainAxisStride: childMainAxisExtent + mainAxisSpacing,
|
|
crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
|
|
childMainAxisExtent: childMainAxisExtent,
|
|
childCrossAxisExtent: childCrossAxisExtent,
|
|
reverseCrossAxis: AxisUtils.axisDirectionIsReversed(constraints.crossAxisDirection)
|
|
);
|
|
}
|
|
|
|
public override bool shouldRelayout(SliverGridDelegate _oldDelegate) {
|
|
SliverGridDelegateWithFixedCrossAxisCount oldDelegate =
|
|
_oldDelegate as SliverGridDelegateWithFixedCrossAxisCount;
|
|
return oldDelegate.crossAxisCount != crossAxisCount
|
|
|| oldDelegate.mainAxisSpacing != mainAxisSpacing
|
|
|| oldDelegate.crossAxisSpacing != crossAxisSpacing
|
|
|| oldDelegate.childAspectRatio != childAspectRatio;
|
|
}
|
|
}
|
|
|
|
public class SliverGridDelegateWithMaxCrossAxisExtent : SliverGridDelegate {
|
|
public SliverGridDelegateWithMaxCrossAxisExtent(
|
|
float maxCrossAxisExtent,
|
|
float mainAxisSpacing = 0.0f,
|
|
float crossAxisSpacing = 0.0f,
|
|
float childAspectRatio = 1.0f
|
|
) {
|
|
D.assert(maxCrossAxisExtent >= 0);
|
|
D.assert(mainAxisSpacing >= 0);
|
|
D.assert(crossAxisSpacing >= 0);
|
|
D.assert(childAspectRatio > 0);
|
|
this.maxCrossAxisExtent = maxCrossAxisExtent;
|
|
this.mainAxisSpacing = mainAxisSpacing;
|
|
this.crossAxisSpacing = crossAxisSpacing;
|
|
this.childAspectRatio = childAspectRatio;
|
|
}
|
|
|
|
public readonly float maxCrossAxisExtent;
|
|
|
|
public readonly float mainAxisSpacing;
|
|
|
|
public readonly float crossAxisSpacing;
|
|
|
|
public readonly float childAspectRatio;
|
|
|
|
bool _debugAssertIsValid() {
|
|
D.assert(maxCrossAxisExtent > 0.0f);
|
|
D.assert(mainAxisSpacing >= 0.0f);
|
|
D.assert(crossAxisSpacing >= 0.0f);
|
|
D.assert(childAspectRatio > 0.0f);
|
|
return true;
|
|
}
|
|
|
|
public override SliverGridLayout getLayout(SliverConstraints constraints) {
|
|
D.assert(_debugAssertIsValid());
|
|
int crossAxisCount =
|
|
(constraints.crossAxisExtent / (maxCrossAxisExtent + crossAxisSpacing)).ceil();
|
|
float usableCrossAxisExtent = constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
|
|
float childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
|
|
float childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
|
|
return new SliverGridRegularTileLayout(
|
|
crossAxisCount: crossAxisCount,
|
|
mainAxisStride: childMainAxisExtent + mainAxisSpacing,
|
|
crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
|
|
childMainAxisExtent: childMainAxisExtent,
|
|
childCrossAxisExtent: childCrossAxisExtent,
|
|
reverseCrossAxis: AxisUtils.axisDirectionIsReversed(constraints.crossAxisDirection)
|
|
);
|
|
}
|
|
|
|
public override bool shouldRelayout(SliverGridDelegate _oldDelegate) {
|
|
SliverGridDelegateWithMaxCrossAxisExtent oldDelegate =
|
|
_oldDelegate as SliverGridDelegateWithMaxCrossAxisExtent;
|
|
return oldDelegate.maxCrossAxisExtent != maxCrossAxisExtent
|
|
|| oldDelegate.mainAxisSpacing != mainAxisSpacing
|
|
|| oldDelegate.crossAxisSpacing != crossAxisSpacing
|
|
|| oldDelegate.childAspectRatio != childAspectRatio;
|
|
}
|
|
}
|
|
|
|
public class SliverGridParentData : SliverMultiBoxAdaptorParentData {
|
|
public float crossAxisOffset;
|
|
|
|
public override string ToString() {
|
|
return $"crossAxisOffset={crossAxisOffset}; {base.ToString()}";
|
|
}
|
|
}
|
|
|
|
public class RenderSliverGrid : RenderSliverMultiBoxAdaptor {
|
|
public RenderSliverGrid(
|
|
RenderSliverBoxChildManager childManager,
|
|
SliverGridDelegate gridDelegate
|
|
) : base(childManager: childManager) {
|
|
D.assert(gridDelegate != null);
|
|
_gridDelegate = gridDelegate;
|
|
}
|
|
|
|
|
|
public override void setupParentData(RenderObject child) {
|
|
if (!(child.parentData is SliverGridParentData)) {
|
|
child.parentData = new SliverGridParentData();
|
|
}
|
|
}
|
|
|
|
public SliverGridDelegate gridDelegate {
|
|
get { return _gridDelegate; }
|
|
set {
|
|
D.assert(value != null);
|
|
if (_gridDelegate == value) {
|
|
return;
|
|
}
|
|
|
|
if (value.GetType() != _gridDelegate.GetType() ||
|
|
value.shouldRelayout(_gridDelegate)) {
|
|
markNeedsLayout();
|
|
}
|
|
|
|
_gridDelegate = value;
|
|
}
|
|
}
|
|
|
|
SliverGridDelegate _gridDelegate;
|
|
|
|
public override float? childCrossAxisPosition(RenderObject child) {
|
|
SliverGridParentData childParentData = (SliverGridParentData) child.parentData;
|
|
return childParentData.crossAxisOffset;
|
|
}
|
|
|
|
protected override void performLayout() {
|
|
childManager.didStartLayout();
|
|
childManager.setDidUnderflow(false);
|
|
|
|
float scrollOffset = constraints.scrollOffset + constraints.cacheOrigin;
|
|
D.assert(scrollOffset >= 0.0f);
|
|
float remainingExtent = constraints.remainingCacheExtent;
|
|
D.assert(remainingExtent >= 0.0f);
|
|
float targetEndScrollOffset = scrollOffset + remainingExtent;
|
|
|
|
SliverGridLayout layout = _gridDelegate.getLayout(constraints);
|
|
|
|
int firstIndex = layout.getMinChildIndexForScrollOffset(scrollOffset);
|
|
int? targetLastIndex = targetEndScrollOffset.isFinite()
|
|
? (int?) layout.getMaxChildIndexForScrollOffset(targetEndScrollOffset)
|
|
: null;
|
|
|
|
if (firstChild != null) {
|
|
int oldFirstIndex = indexOf(firstChild);
|
|
int oldLastIndex = indexOf(lastChild);
|
|
int leadingGarbage = (firstIndex - oldFirstIndex).clamp(0, childCount);
|
|
int trailingGarbage = targetLastIndex == null
|
|
? 0
|
|
: ((oldLastIndex - targetLastIndex) ?? 0).clamp(0, childCount);
|
|
collectGarbage(leadingGarbage, trailingGarbage);
|
|
}
|
|
else {
|
|
collectGarbage(0, 0);
|
|
}
|
|
|
|
SliverGridGeometry firstChildGridGeometry = layout.getGeometryForChildIndex(firstIndex);
|
|
float? leadingScrollOffset = firstChildGridGeometry.scrollOffset;
|
|
float? trailingScrollOffset = firstChildGridGeometry.trailingScrollOffset;
|
|
|
|
if (firstChild == null) {
|
|
if (!addInitialChild(index: firstIndex,
|
|
layoutOffset: firstChildGridGeometry.scrollOffset ?? 0.0f)) {
|
|
float max = layout.computeMaxScrollOffset(childManager.childCount ?? 0);
|
|
geometry = new SliverGeometry(
|
|
scrollExtent: max,
|
|
maxPaintExtent: max
|
|
);
|
|
childManager.didFinishLayout();
|
|
return;
|
|
}
|
|
}
|
|
|
|
RenderBox trailingChildWithLayout = null;
|
|
|
|
for (int index = indexOf(firstChild) - 1; index >= firstIndex; --index) {
|
|
SliverGridGeometry gridGeometry = layout.getGeometryForChildIndex(index);
|
|
RenderBox child = insertAndLayoutLeadingChild(
|
|
gridGeometry.getBoxConstraints(constraints)
|
|
);
|
|
SliverGridParentData childParentData = child.parentData as SliverGridParentData;
|
|
childParentData.layoutOffset = gridGeometry.scrollOffset ?? 0.0f;
|
|
childParentData.crossAxisOffset = gridGeometry.crossAxisOffset ?? 0.0f;
|
|
D.assert(childParentData.index == index);
|
|
trailingChildWithLayout = trailingChildWithLayout ?? child;
|
|
trailingScrollOffset =
|
|
Mathf.Max(trailingScrollOffset ?? 0.0f, gridGeometry.trailingScrollOffset ?? 0.0f);
|
|
}
|
|
|
|
if (trailingChildWithLayout == null) {
|
|
firstChild.layout(firstChildGridGeometry.getBoxConstraints(constraints));
|
|
SliverGridParentData childParentData = firstChild.parentData as SliverGridParentData;
|
|
childParentData.layoutOffset = firstChildGridGeometry.scrollOffset ?? 0.0f;
|
|
childParentData.crossAxisOffset = firstChildGridGeometry.crossAxisOffset ?? 0.0f;
|
|
trailingChildWithLayout = firstChild;
|
|
}
|
|
|
|
for (int index = indexOf(trailingChildWithLayout) + 1;
|
|
targetLastIndex == null || index <= targetLastIndex;
|
|
++index) {
|
|
SliverGridGeometry gridGeometry = layout.getGeometryForChildIndex(index);
|
|
BoxConstraints childConstraints = gridGeometry.getBoxConstraints(constraints);
|
|
RenderBox child = childAfter(trailingChildWithLayout);
|
|
if (child == null || indexOf(child) != index) {
|
|
child = insertAndLayoutChild(childConstraints, after: trailingChildWithLayout);
|
|
if (child == null) {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
child.layout(childConstraints);
|
|
}
|
|
|
|
trailingChildWithLayout = child;
|
|
SliverGridParentData childParentData = child.parentData as SliverGridParentData;
|
|
childParentData.layoutOffset = gridGeometry.scrollOffset ?? 0.0f;
|
|
childParentData.crossAxisOffset = gridGeometry.crossAxisOffset ?? 0.0f;
|
|
D.assert(childParentData.index == index);
|
|
trailingScrollOffset =
|
|
Mathf.Max(trailingScrollOffset ?? 0.0f, gridGeometry.trailingScrollOffset ?? 0.0f);
|
|
}
|
|
|
|
int lastIndex = indexOf(lastChild);
|
|
|
|
D.assert(childScrollOffset(firstChild) <= scrollOffset);
|
|
D.assert(debugAssertChildListIsNonEmptyAndContiguous());
|
|
D.assert(indexOf(firstChild) == firstIndex);
|
|
D.assert(targetLastIndex == null || lastIndex <= targetLastIndex);
|
|
|
|
float estimatedTotalExtent = childManager.estimateMaxScrollOffset(
|
|
constraints,
|
|
firstIndex: firstIndex,
|
|
lastIndex: lastIndex,
|
|
leadingScrollOffset: leadingScrollOffset ?? 0.0f,
|
|
trailingScrollOffset: trailingScrollOffset ?? 0.0f
|
|
);
|
|
|
|
float paintExtent = calculatePaintOffset(
|
|
constraints,
|
|
from: leadingScrollOffset ?? 0.0f,
|
|
to: trailingScrollOffset ?? 0.0f
|
|
);
|
|
float cacheExtent = calculateCacheOffset(
|
|
constraints,
|
|
from: leadingScrollOffset ?? 0.0f,
|
|
to: trailingScrollOffset ?? 0.0f
|
|
);
|
|
|
|
geometry = new SliverGeometry(
|
|
scrollExtent: estimatedTotalExtent,
|
|
paintExtent: paintExtent,
|
|
maxPaintExtent: estimatedTotalExtent,
|
|
cacheExtent: cacheExtent,
|
|
hasVisualOverflow: true
|
|
);
|
|
|
|
if (estimatedTotalExtent == trailingScrollOffset) {
|
|
childManager.setDidUnderflow(true);
|
|
}
|
|
|
|
childManager.didFinishLayout();
|
|
}
|
|
}
|
|
}
|