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