您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
456 行
18 KiB
456 行
18 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.UIWidgets.animation;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.gestures;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.scheduler;
|
|
using Unity.UIWidgets.ui;
|
|
using UnityEngine;
|
|
using AsyncCallback = Unity.UIWidgets.foundation.AsyncCallback;
|
|
|
|
namespace Unity.UIWidgets.rendering {
|
|
|
|
public class OverScrollHeaderStretchConfiguration {
|
|
public OverScrollHeaderStretchConfiguration(
|
|
float stretchTriggerOffset = 100.0f,
|
|
AsyncCallback onStretchTrigger = null
|
|
) {
|
|
this.stretchTriggerOffset = stretchTriggerOffset;
|
|
this.onStretchTrigger = onStretchTrigger;
|
|
}
|
|
|
|
|
|
public readonly float stretchTriggerOffset;
|
|
public readonly AsyncCallback onStretchTrigger;
|
|
}
|
|
public abstract class RenderSliverPersistentHeader : RenderObjectWithChildMixinRenderSliver<RenderBox> {
|
|
public RenderSliverPersistentHeader(
|
|
RenderBox child = null,
|
|
OverScrollHeaderStretchConfiguration stretchConfiguration = null
|
|
) {
|
|
this.child = child;
|
|
this.stretchConfiguration = stretchConfiguration;
|
|
}
|
|
|
|
float _lastStretchOffset;
|
|
public virtual float? maxExtent { get; }
|
|
|
|
public virtual float? minExtent { get; }
|
|
|
|
public float childExtent {
|
|
get {
|
|
if (child == null) {
|
|
return 0.0f;
|
|
}
|
|
|
|
D.assert(child.hasSize);
|
|
switch (constraints.axis) {
|
|
case Axis.vertical:
|
|
return child.size.height;
|
|
case Axis.horizontal:
|
|
return child.size.width;
|
|
default:
|
|
throw new Exception("Unknown axis: " + constraints.axis);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool _needsUpdateChild = true;
|
|
float _lastShrinkOffset = 0.0f;
|
|
bool _lastOverlapsContent = false;
|
|
public OverScrollHeaderStretchConfiguration stretchConfiguration;
|
|
|
|
protected virtual void updateChild(float shrinkOffset, bool overlapsContent) {
|
|
}
|
|
|
|
public override void markNeedsLayout() {
|
|
_needsUpdateChild = true;
|
|
base.markNeedsLayout();
|
|
}
|
|
|
|
protected void layoutChild(float scrollOffset, float maxExtent, bool overlapsContent = false) {
|
|
float shrinkOffset = Mathf.Min(scrollOffset, maxExtent);
|
|
if (_needsUpdateChild || _lastShrinkOffset != shrinkOffset ||
|
|
_lastOverlapsContent != overlapsContent) {
|
|
invokeLayoutCallback<SliverConstraints>((SliverConstraints constraints) => {
|
|
D.assert(constraints == this.constraints);
|
|
updateChild(shrinkOffset, overlapsContent);
|
|
});
|
|
_lastShrinkOffset = shrinkOffset;
|
|
_lastOverlapsContent = overlapsContent;
|
|
_needsUpdateChild = false;
|
|
}
|
|
|
|
D.assert(minExtent != null);
|
|
D.assert(() => {
|
|
if (minExtent <= maxExtent) {
|
|
return true;
|
|
}
|
|
|
|
throw new UIWidgetsError(new List<DiagnosticsNode>{
|
|
new ErrorSummary($"The maxExtent for this {GetType()} is less than its minExtent."),
|
|
new FloatProperty("The specified maxExtent was", maxExtent),
|
|
new FloatProperty("The specified minExtent was", minExtent),
|
|
});
|
|
});
|
|
float stretchOffset = 0.0f;
|
|
if (stretchConfiguration != null && childMainAxisPosition(child) == 0.0)
|
|
stretchOffset += constraints.overlap.abs();
|
|
child?.layout(
|
|
constraints.asBoxConstraints(
|
|
maxExtent: Mathf.Max(minExtent?? 0.0f, maxExtent - shrinkOffset) + stretchOffset
|
|
),
|
|
parentUsesSize: true
|
|
);
|
|
if (stretchConfiguration != null &&
|
|
stretchConfiguration.onStretchTrigger != null &&
|
|
stretchOffset >= stretchConfiguration.stretchTriggerOffset &&
|
|
_lastStretchOffset <= stretchConfiguration.stretchTriggerOffset) {
|
|
stretchConfiguration.onStretchTrigger();
|
|
}
|
|
_lastStretchOffset = stretchOffset;
|
|
}
|
|
|
|
public override float? childMainAxisPosition(RenderObject child) {
|
|
return base.childMainAxisPosition(this.child);
|
|
}
|
|
|
|
protected override bool hitTestChildren(SliverHitTestResult result, float mainAxisPosition = 0.0f, float crossAxisPosition = 0.0f) {
|
|
D.assert(geometry.hitTestExtent > 0.0f);
|
|
if (child != null) {
|
|
return RenderSliverHelpers.hitTestBoxChild(this, new BoxHitTestResult(result), child, mainAxisPosition: mainAxisPosition,
|
|
crossAxisPosition: crossAxisPosition);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override void applyPaintTransform(RenderObject child, Matrix4 transform) {
|
|
D.assert(child != null);
|
|
D.assert(child == this.child);
|
|
RenderSliverHelpers.applyPaintTransformForBoxChild(this, this.child, transform);
|
|
}
|
|
|
|
public override void paint(PaintingContext context, Offset offset) {
|
|
if (child != null && geometry.visible) {
|
|
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection(constraints.axisDirection,
|
|
constraints.growthDirection)) {
|
|
case AxisDirection.up:
|
|
offset += new Offset(0.0f,
|
|
geometry.paintExtent - childMainAxisPosition(child)?? 0.0f - childExtent);
|
|
break;
|
|
case AxisDirection.down:
|
|
offset += new Offset(0.0f, childMainAxisPosition(child) ?? 0.0f);
|
|
break;
|
|
case AxisDirection.left:
|
|
offset += new Offset(
|
|
geometry.paintExtent - childMainAxisPosition(child) ?? 0.0f - childExtent,
|
|
0.0f);
|
|
break;
|
|
case AxisDirection.right:
|
|
offset += new Offset(childMainAxisPosition(child) ?? 0.0f, 0.0f);
|
|
break;
|
|
}
|
|
|
|
context.paintChild(child, offset);
|
|
}
|
|
}
|
|
|
|
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
base.debugFillProperties(properties);
|
|
properties.add(FloatProperty.lazy("maxExtent", () => maxExtent));
|
|
properties.add(FloatProperty.lazy("child position", () => childMainAxisPosition(child)));
|
|
}
|
|
}
|
|
|
|
public abstract class RenderSliverScrollingPersistentHeader : RenderSliverPersistentHeader {
|
|
public RenderSliverScrollingPersistentHeader(
|
|
RenderBox child = null,
|
|
OverScrollHeaderStretchConfiguration stretchConfiguration = null
|
|
) : base(child: child,
|
|
stretchConfiguration: stretchConfiguration) {
|
|
}
|
|
|
|
float _childPosition;
|
|
|
|
protected float updateGeometry() {
|
|
float? stretchOffset = 0.0f;
|
|
if (stretchConfiguration != null && _childPosition == 0.0) {
|
|
stretchOffset += constraints.overlap.abs();
|
|
}
|
|
float? maxExtent = this.maxExtent;
|
|
float? paintExtent = maxExtent - constraints.scrollOffset;
|
|
geometry = new SliverGeometry(
|
|
scrollExtent: maxExtent ?? 0.0f,
|
|
paintOrigin: Mathf.Min(constraints.overlap, 0.0f),
|
|
paintExtent: paintExtent?.clamp(0.0f, constraints.remainingPaintExtent) ?? 0.0f,
|
|
maxPaintExtent: maxExtent + stretchOffset ?? 0.0f,
|
|
hasVisualOverflow: true// Conservatively say we do have overflow to avoid complexity.
|
|
);
|
|
return stretchOffset > 0 ? 0.0f :Mathf.Min(0.0f, paintExtent?? 0.0f - childExtent );
|
|
}
|
|
protected override void performLayout() {
|
|
float? maxExtent = this.maxExtent;
|
|
layoutChild(constraints.scrollOffset, maxExtent ?? 0.0f);
|
|
float? paintExtent = maxExtent - constraints.scrollOffset;
|
|
geometry = new SliverGeometry(
|
|
scrollExtent: maxExtent ?? 0.0f,
|
|
paintOrigin: Mathf.Min(constraints.overlap, 0.0f),
|
|
paintExtent: paintExtent?.clamp(0.0f, constraints.remainingPaintExtent) ?? 0.0f,
|
|
maxPaintExtent: maxExtent ?? 0.0f,
|
|
hasVisualOverflow: true
|
|
);
|
|
_childPosition = updateGeometry();
|
|
}
|
|
|
|
public override float? childMainAxisPosition(RenderObject child) {
|
|
D.assert(child == this.child);
|
|
return _childPosition;
|
|
}
|
|
}
|
|
|
|
public abstract class RenderSliverPinnedPersistentHeader : RenderSliverPersistentHeader {
|
|
public RenderSliverPinnedPersistentHeader(
|
|
RenderBox child = null,
|
|
OverScrollHeaderStretchConfiguration stretchConfiguration = null
|
|
) : base(child: child,
|
|
stretchConfiguration: stretchConfiguration) {
|
|
}
|
|
|
|
protected override void performLayout() {
|
|
SliverConstraints constraints = this.constraints;
|
|
float? maxExtent = this.maxExtent;
|
|
bool overlapsContent = constraints.overlap > 0.0f;
|
|
layoutChild(constraints.scrollOffset, maxExtent ?? 0.0f, overlapsContent: overlapsContent);
|
|
float effectiveRemainingPaintExtent = Mathf.Max(0, constraints.remainingPaintExtent - constraints.overlap);
|
|
float? layoutExtent = (maxExtent - constraints.scrollOffset)?.clamp(0.0f, effectiveRemainingPaintExtent);
|
|
float stretchOffset = stretchConfiguration != null ?
|
|
constraints.overlap.abs() :
|
|
0.0f;
|
|
geometry = new SliverGeometry(
|
|
scrollExtent: maxExtent ?? 0.0f,
|
|
paintOrigin: constraints.overlap,
|
|
paintExtent: Mathf.Min(childExtent, effectiveRemainingPaintExtent),
|
|
layoutExtent: layoutExtent,
|
|
maxPaintExtent: (maxExtent ?? 0.0f) + stretchOffset,
|
|
maxScrollObstructionExtent: minExtent ?? 0.0f,
|
|
cacheExtent: layoutExtent > 0.0f ? -constraints.cacheOrigin + layoutExtent : layoutExtent,
|
|
hasVisualOverflow: true
|
|
);
|
|
}
|
|
|
|
public override float? childMainAxisPosition(RenderObject child) {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
public class FloatingHeaderSnapConfiguration {
|
|
public FloatingHeaderSnapConfiguration(
|
|
TickerProvider vsync,
|
|
Curve curve = null,
|
|
TimeSpan? duration = null
|
|
) {
|
|
D.assert(vsync != null);
|
|
D.assert(curve != null);
|
|
D.assert(duration != null);
|
|
this.vsync = vsync;
|
|
this.curve = curve ?? Curves.ease;
|
|
this.duration = duration ?? new TimeSpan(0, 0, 0, 0, 300);
|
|
}
|
|
|
|
public readonly TickerProvider vsync;
|
|
|
|
public readonly Curve curve;
|
|
|
|
public readonly TimeSpan duration;
|
|
}
|
|
|
|
|
|
public abstract class RenderSliverFloatingPersistentHeader : RenderSliverPersistentHeader {
|
|
public RenderSliverFloatingPersistentHeader(
|
|
RenderBox child = null,
|
|
FloatingHeaderSnapConfiguration snapConfiguration = null,
|
|
OverScrollHeaderStretchConfiguration stretchConfiguration = null
|
|
) : base(
|
|
child: child,
|
|
stretchConfiguration: stretchConfiguration) {
|
|
_snapConfiguration = snapConfiguration;
|
|
}
|
|
|
|
AnimationController _controller;
|
|
Animation<float> _animation;
|
|
protected float _lastActualScrollOffset;
|
|
protected float _effectiveScrollOffset;
|
|
|
|
float _childPosition;
|
|
|
|
public override void detach() {
|
|
_controller?.dispose();
|
|
_controller = null;
|
|
base.detach();
|
|
}
|
|
|
|
public FloatingHeaderSnapConfiguration snapConfiguration {
|
|
get { return _snapConfiguration; }
|
|
set {
|
|
if (value == _snapConfiguration) {
|
|
return;
|
|
}
|
|
|
|
if (value == null) {
|
|
_controller?.dispose();
|
|
_controller = null;
|
|
}
|
|
else {
|
|
if (_snapConfiguration != null && value.vsync != _snapConfiguration.vsync) {
|
|
_controller?.resync(value.vsync);
|
|
}
|
|
}
|
|
|
|
_snapConfiguration = value;
|
|
}
|
|
}
|
|
|
|
public FloatingHeaderSnapConfiguration _snapConfiguration;
|
|
|
|
protected virtual float updateGeometry() {
|
|
float stretchOffset = 0.0f;
|
|
if (stretchConfiguration != null && _childPosition == 0.0) {
|
|
stretchOffset += constraints.overlap.abs();
|
|
}
|
|
float? maxExtent = this.maxExtent;
|
|
float? paintExtent = maxExtent - _effectiveScrollOffset;
|
|
float? layoutExtent = maxExtent - constraints.scrollOffset;
|
|
geometry = new SliverGeometry(
|
|
scrollExtent: maxExtent ?? 0.0f,
|
|
paintOrigin: Mathf.Min(constraints.overlap, 0.0f),
|
|
paintExtent: paintExtent?.clamp(0.0f, constraints.remainingPaintExtent) ?? 0.0f,
|
|
layoutExtent: layoutExtent?.clamp(0.0f, constraints.remainingPaintExtent),
|
|
maxPaintExtent: (maxExtent ?? 0.0f) + stretchOffset,
|
|
hasVisualOverflow: true
|
|
);
|
|
return stretchOffset > 0 ? 0.0f : Mathf.Min(0.0f, paintExtent??0.0f - childExtent);
|
|
}
|
|
|
|
public void maybeStartSnapAnimation(ScrollDirection direction) {
|
|
if (snapConfiguration == null) {
|
|
return;
|
|
}
|
|
|
|
if (direction == ScrollDirection.forward && _effectiveScrollOffset <= 0.0f) {
|
|
return;
|
|
}
|
|
|
|
if (direction == ScrollDirection.reverse && _effectiveScrollOffset >= maxExtent) {
|
|
return;
|
|
}
|
|
|
|
TickerProvider vsync = snapConfiguration.vsync;
|
|
TimeSpan duration = snapConfiguration.duration;
|
|
_controller = _controller ?? new AnimationController(vsync: vsync, duration: duration);
|
|
_controller.addListener(() => {
|
|
if (_effectiveScrollOffset == _animation.value) {
|
|
return;
|
|
}
|
|
|
|
_effectiveScrollOffset = _animation.value;
|
|
markNeedsLayout();
|
|
});
|
|
|
|
_animation = _controller.drive(
|
|
new FloatTween(
|
|
begin: _effectiveScrollOffset,
|
|
end: direction == ScrollDirection.forward ? 0.0f : maxExtent ?? 0.0f
|
|
).chain(new CurveTween(
|
|
curve: snapConfiguration.curve
|
|
))
|
|
);
|
|
|
|
_controller.forward(from: 0.0f);
|
|
}
|
|
|
|
public void maybeStopSnapAnimation(ScrollDirection direction) {
|
|
_controller?.stop();
|
|
}
|
|
|
|
protected override void performLayout() {
|
|
SliverConstraints constraints = this.constraints;
|
|
float? maxExtent = this.maxExtent;
|
|
if (((constraints.scrollOffset < _lastActualScrollOffset) ||
|
|
(_effectiveScrollOffset < maxExtent))) {
|
|
float delta = _lastActualScrollOffset - constraints.scrollOffset;
|
|
bool allowFloatingExpansion = constraints.userScrollDirection == ScrollDirection.forward;
|
|
if (allowFloatingExpansion) {
|
|
if (_effectiveScrollOffset > maxExtent) {
|
|
_effectiveScrollOffset = maxExtent ?? 0.0f;
|
|
}
|
|
}
|
|
else {
|
|
if (delta > 0.0f) {
|
|
delta = 0.0f;
|
|
}
|
|
}
|
|
|
|
_effectiveScrollOffset =
|
|
(_effectiveScrollOffset - delta).clamp(0.0f, constraints.scrollOffset);
|
|
}
|
|
else {
|
|
_effectiveScrollOffset = constraints.scrollOffset;
|
|
}
|
|
bool overlapsContent = _effectiveScrollOffset < constraints.scrollOffset;
|
|
layoutChild(
|
|
_effectiveScrollOffset,
|
|
maxExtent ?? 0.0f,
|
|
overlapsContent: overlapsContent
|
|
);
|
|
_childPosition = updateGeometry();
|
|
_lastActualScrollOffset = constraints.scrollOffset;
|
|
}
|
|
|
|
public override float? childMainAxisPosition(RenderObject child) {
|
|
D.assert(child == this.child);
|
|
return _childPosition;
|
|
}
|
|
|
|
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
base.debugFillProperties(properties);
|
|
properties.add(new FloatProperty("effective scroll offset", _effectiveScrollOffset));
|
|
}
|
|
}
|
|
|
|
public abstract class RenderSliverFloatingPinnedPersistentHeader : RenderSliverFloatingPersistentHeader {
|
|
public RenderSliverFloatingPinnedPersistentHeader(
|
|
RenderBox child = null,
|
|
FloatingHeaderSnapConfiguration snapConfiguration = null,
|
|
OverScrollHeaderStretchConfiguration stretchConfiguration = null
|
|
) : base(child: child,
|
|
snapConfiguration: snapConfiguration,
|
|
stretchConfiguration: stretchConfiguration) {
|
|
}
|
|
|
|
protected override float updateGeometry() {
|
|
float? minExtent = this.minExtent;
|
|
float? minAllowedExtent = constraints.remainingPaintExtent > minExtent
|
|
? minExtent
|
|
: constraints.remainingPaintExtent;
|
|
float? maxExtent = this.maxExtent;
|
|
float? paintExtent = maxExtent - _effectiveScrollOffset;
|
|
float? clampedPaintExtent =
|
|
paintExtent?.clamp(minAllowedExtent ?? 0.0f, constraints.remainingPaintExtent);
|
|
float? layoutExtent = maxExtent - constraints.scrollOffset;
|
|
float? stretchOffset = stretchConfiguration != null ?
|
|
constraints.overlap.abs() :
|
|
0.0f;
|
|
geometry = new SliverGeometry(
|
|
scrollExtent: maxExtent ?? 0.0f,
|
|
paintExtent: clampedPaintExtent ?? 0.0f,
|
|
layoutExtent: layoutExtent?.clamp(0.0f, clampedPaintExtent ?? 0.0f),
|
|
maxPaintExtent: (maxExtent ?? 0.0f) + (stretchOffset ?? 0.0f),
|
|
maxScrollObstructionExtent: maxExtent ?? 0.0f,
|
|
hasVisualOverflow: true
|
|
);
|
|
return 0.0f;
|
|
}
|
|
}
|
|
}
|