您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

1453 行
54 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using RSG;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.physics;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
namespace Unity.UIWidgets.widgets {
public delegate List<Widget> NestedScrollViewHeaderSliversBuilder(BuildContext context, bool innerBoxIsScrolled);
public class NestedScrollView : StatefulWidget {
public NestedScrollView(
Key key = null,
ScrollController controller = null,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollPhysics physics = null,
NestedScrollViewHeaderSliversBuilder headerSliverBuilder = null,
Widget body = null,
DragStartBehavior dragStartBehavior = DragStartBehavior.start
) : base(key: key) {
D.assert(headerSliverBuilder != null);
D.assert(body != null);
this.controller = controller;
this.scrollDirection = scrollDirection;
this.reverse = reverse;
this.physics = physics;
this.headerSliverBuilder = headerSliverBuilder;
this.body = body;
this.dragStartBehavior = dragStartBehavior;
}
public readonly ScrollController controller;
public readonly Axis scrollDirection;
public readonly bool reverse;
public readonly ScrollPhysics physics;
public readonly NestedScrollViewHeaderSliversBuilder headerSliverBuilder;
public readonly Widget body;
public readonly DragStartBehavior dragStartBehavior;
public static SliverOverlapAbsorberHandle sliverOverlapAbsorberHandleFor(BuildContext context) {
_InheritedNestedScrollView target =
(_InheritedNestedScrollView) context.inheritFromWidgetOfExactType(typeof(_InheritedNestedScrollView));
D.assert(target != null,
() => {
return
"NestedScrollView.sliverOverlapAbsorberHandleFor must be called with a context that contains a NestedScrollView.";
});
return target.state._absorberHandle;
}
internal List<Widget> _buildSlivers(BuildContext context, ScrollController innerController,
bool bodyIsScrolled) {
List<Widget> slivers = new List<Widget> { };
slivers.AddRange(this.headerSliverBuilder(context, bodyIsScrolled));
slivers.Add(new SliverFillRemaining(
child: new PrimaryScrollController(
controller: innerController,
child: this.body
)
));
return slivers;
}
public override State createState() {
return new _NestedScrollViewState();
}
}
class _NestedScrollViewState : State<NestedScrollView> {
internal SliverOverlapAbsorberHandle _absorberHandle = new SliverOverlapAbsorberHandle();
_NestedScrollCoordinator _coordinator;
public override void initState() {
base.initState();
this._coordinator =
new _NestedScrollCoordinator(this, this.widget.controller, this._handleHasScrolledBodyChanged);
}
public override void didChangeDependencies() {
base.didChangeDependencies();
this._coordinator.setParent(this.widget.controller);
}
public override void didUpdateWidget(StatefulWidget _oldWidget) {
NestedScrollView oldWidget = _oldWidget as NestedScrollView;
base.didUpdateWidget(oldWidget);
if (oldWidget.controller != this.widget.controller) {
this._coordinator.setParent(this.widget.controller);
}
}
public override void dispose() {
this._coordinator.dispose();
this._coordinator = null;
base.dispose();
}
bool _lastHasScrolledBody;
void _handleHasScrolledBodyChanged() {
if (!this.mounted) {
return;
}
bool newHasScrolledBody = this._coordinator.hasScrolledBody;
if (this._lastHasScrolledBody != newHasScrolledBody) {
this.setState(() => { });
}
}
public override Widget build(BuildContext context) {
return new _InheritedNestedScrollView(
state: this,
child: new Builder(
builder: (BuildContext _context) => {
this._lastHasScrolledBody = this._coordinator.hasScrolledBody;
return new _NestedScrollViewCustomScrollView(
dragStartBehavior: this.widget.dragStartBehavior,
scrollDirection: this.widget.scrollDirection,
reverse: this.widget.reverse,
physics: this.widget.physics != null
? this.widget.physics.applyTo(new ClampingScrollPhysics())
: new ClampingScrollPhysics(),
controller: this._coordinator._outerController,
slivers: this.widget._buildSlivers(
_context, this._coordinator._innerController, this._lastHasScrolledBody
),
handle: this._absorberHandle
);
}
)
);
}
}
class _NestedScrollViewCustomScrollView : CustomScrollView {
public _NestedScrollViewCustomScrollView(
Axis scrollDirection,
bool reverse,
ScrollPhysics physics,
ScrollController controller,
List<Widget> slivers,
SliverOverlapAbsorberHandle handle,
DragStartBehavior dragStartBehavior = DragStartBehavior.start
) : base(
scrollDirection: scrollDirection,
reverse: reverse,
physics: physics,
controller: controller,
slivers: slivers,
dragStartBehavior: dragStartBehavior
) {
this.handle = handle;
}
public readonly SliverOverlapAbsorberHandle handle;
protected override Widget buildViewport(
BuildContext context,
ViewportOffset offset,
AxisDirection axisDirection,
List<Widget> slivers
) {
D.assert(!this.shrinkWrap);
return new NestedScrollViewViewport(
axisDirection: axisDirection,
offset: offset,
slivers: slivers,
handle: this.handle
);
}
}
class _InheritedNestedScrollView : InheritedWidget {
public _InheritedNestedScrollView(
Key key = null,
_NestedScrollViewState state = null,
Widget child = null
) : base(key: key, child: child) {
D.assert(state != null);
D.assert(child != null);
this.state = state;
}
public readonly _NestedScrollViewState state;
public override bool updateShouldNotify(InheritedWidget _old) {
_InheritedNestedScrollView old = _old as _InheritedNestedScrollView;
return this.state != old.state;
}
}
class _NestedScrollMetrics : FixedScrollMetrics {
public _NestedScrollMetrics(
float minScrollExtent,
float maxScrollExtent,
float pixels,
float viewportDimension,
AxisDirection axisDirection,
float minRange,
float maxRange,
float correctionOffset
) : base(
minScrollExtent: minScrollExtent,
maxScrollExtent: maxScrollExtent,
pixels: pixels,
viewportDimension: viewportDimension,
axisDirection: axisDirection
) {
this.minRange = minRange;
this.maxRange = maxRange;
this.correctionOffset = correctionOffset;
}
public _NestedScrollMetrics copyWith(
float? minScrollExtent = null,
float? maxScrollExtent = null,
float? pixels = null,
float? viewportDimension = null,
AxisDirection? axisDirection = null,
float? minRange = null,
float? maxRange = null,
float? correctionOffset = null
) {
return new _NestedScrollMetrics(
minScrollExtent: minScrollExtent ?? this.minScrollExtent,
maxScrollExtent: maxScrollExtent ?? this.maxScrollExtent,
pixels: pixels ?? this.pixels,
viewportDimension: viewportDimension ?? this.viewportDimension,
axisDirection: axisDirection ?? this.axisDirection,
minRange: minRange ?? this.minRange,
maxRange: maxRange ?? this.maxRange,
correctionOffset: correctionOffset ?? this.correctionOffset
);
}
public readonly float minRange;
public readonly float maxRange;
public readonly float correctionOffset;
}
delegate ScrollActivity _NestedScrollActivityGetter(_NestedScrollPosition position);
class _NestedScrollCoordinator : ScrollActivityDelegate, ScrollHoldController {
public _NestedScrollCoordinator(
_NestedScrollViewState _state,
ScrollController _parent,
VoidCallback _onHasScrolledBodyChanged) {
float initialScrollOffset = _parent?.initialScrollOffset ?? 0.0f;
this._outerController =
new _NestedScrollController(this, initialScrollOffset: initialScrollOffset, debugLabel: "outer");
this._innerController = new _NestedScrollController(this, initialScrollOffset: 0.0f, debugLabel: "inner");
this._state = _state;
this._parent = _parent;
this._onHasScrolledBodyChanged = _onHasScrolledBodyChanged;
}
public readonly _NestedScrollViewState _state;
ScrollController _parent;
public readonly VoidCallback _onHasScrolledBodyChanged;
internal _NestedScrollController _outerController;
internal _NestedScrollController _innerController;
_NestedScrollPosition _outerPosition {
get {
if (!this._outerController.hasClients) {
return null;
}
return this._outerController.nestedPositions.Single();
}
}
IEnumerable<_NestedScrollPosition> _innerPositions {
get { return this._innerController.nestedPositions; }
}
public bool canScrollBody {
get {
_NestedScrollPosition outer = this._outerPosition;
if (outer == null) {
return true;
}
return outer.haveDimensions && outer.extentAfter() == 0.0f;
}
}
public bool hasScrolledBody {
get {
foreach (_NestedScrollPosition position in this._innerPositions) {
if (position.pixels > position.minScrollExtent) {
return true;
}
}
return false;
}
}
public void updateShadow() {
if (this._onHasScrolledBodyChanged != null) {
this._onHasScrolledBodyChanged();
}
}
public ScrollDirection userScrollDirection {
get { return this._userScrollDirection; }
}
ScrollDirection _userScrollDirection = ScrollDirection.idle;
public void updateUserScrollDirection(ScrollDirection value) {
if (this.userScrollDirection == value) {
return;
}
this._userScrollDirection = value;
this._outerPosition.didUpdateScrollDirection(value);
foreach (_NestedScrollPosition position in this._innerPositions) {
position.didUpdateScrollDirection(value);
}
}
ScrollDragController _currentDrag;
public void beginActivity(ScrollActivity newOuterActivity, _NestedScrollActivityGetter innerActivityGetter) {
this._outerPosition.beginActivity(newOuterActivity);
bool scrolling = newOuterActivity.isScrolling;
foreach (_NestedScrollPosition position in this._innerPositions) {
ScrollActivity newInnerActivity = innerActivityGetter(position);
position.beginActivity(newInnerActivity);
scrolling = scrolling && newInnerActivity.isScrolling;
}
this._currentDrag?.dispose();
this._currentDrag = null;
if (!scrolling) {
this.updateUserScrollDirection(ScrollDirection.idle);
}
}
public AxisDirection axisDirection {
get { return this._outerPosition.axisDirection; }
}
static IdleScrollActivity _createIdleScrollActivity(_NestedScrollPosition position) {
return new IdleScrollActivity(position);
}
public void goIdle() {
this.beginActivity(_createIdleScrollActivity(this._outerPosition), _createIdleScrollActivity);
}
public void goBallistic(float velocity) {
this.beginActivity(this.createOuterBallisticScrollActivity(velocity),
(_NestedScrollPosition position) => this.createInnerBallisticScrollActivity(position, velocity)
);
}
public ScrollActivity createOuterBallisticScrollActivity(float velocity) {
_NestedScrollPosition innerPosition = null;
if (velocity != 0.0f) {
foreach (_NestedScrollPosition position in this._innerPositions) {
if (innerPosition != null) {
if (velocity > 0.0f) {
if (innerPosition.pixels < position.pixels) {
continue;
}
}
else {
D.assert(velocity < 0.0f);
if (innerPosition.pixels > position.pixels) {
continue;
}
}
}
innerPosition = position;
}
}
if (innerPosition == null) {
return this._outerPosition.createBallisticScrollActivity(
this._outerPosition.physics.createBallisticSimulation(this._outerPosition, velocity),
mode: _NestedBallisticScrollActivityMode.independent
);
}
_NestedScrollMetrics metrics = this._getMetrics(innerPosition, velocity);
return this._outerPosition.createBallisticScrollActivity(
this._outerPosition.physics.createBallisticSimulation(metrics, velocity),
mode: _NestedBallisticScrollActivityMode.outer,
metrics: metrics
);
}
protected internal ScrollActivity createInnerBallisticScrollActivity(_NestedScrollPosition position,
float velocity) {
return position.createBallisticScrollActivity(
position.physics.createBallisticSimulation(
velocity == 0 ? (ScrollMetrics) position : this._getMetrics(position, velocity),
velocity
),
mode: _NestedBallisticScrollActivityMode.inner
);
}
_NestedScrollMetrics _getMetrics(_NestedScrollPosition innerPosition, float velocity) {
D.assert(innerPosition != null);
float pixels, minRange, maxRange, correctionOffset, extra;
if (innerPosition.pixels == innerPosition.minScrollExtent) {
pixels = this._outerPosition.pixels.clamp(this._outerPosition.minScrollExtent,
this._outerPosition.maxScrollExtent); // TODO(ianh): gracefully handle out-of-range outer positions
minRange = this._outerPosition.minScrollExtent;
maxRange = this._outerPosition.maxScrollExtent;
D.assert(minRange <= maxRange);
correctionOffset = 0.0f;
extra = 0.0f;
}
else {
D.assert(innerPosition.pixels != innerPosition.minScrollExtent);
if (innerPosition.pixels < innerPosition.minScrollExtent) {
pixels = innerPosition.pixels - innerPosition.minScrollExtent + this._outerPosition.minScrollExtent;
}
else {
D.assert(innerPosition.pixels > innerPosition.minScrollExtent);
pixels = innerPosition.pixels - innerPosition.minScrollExtent + this._outerPosition.maxScrollExtent;
}
if ((velocity > 0.0f) && (innerPosition.pixels > innerPosition.minScrollExtent)) {
extra = this._outerPosition.maxScrollExtent - this._outerPosition.pixels;
D.assert(extra >= 0.0f);
minRange = pixels;
maxRange = pixels + extra;
D.assert(minRange <= maxRange);
correctionOffset = this._outerPosition.pixels - pixels;
}
else if ((velocity < 0.0f) && (innerPosition.pixels < innerPosition.minScrollExtent)) {
extra = this._outerPosition.pixels - this._outerPosition.minScrollExtent;
D.assert(extra >= 0.0f);
minRange = pixels - extra;
maxRange = pixels;
D.assert(minRange <= maxRange);
correctionOffset = this._outerPosition.pixels - pixels;
}
else {
if (velocity > 0.0f) {
extra = this._outerPosition.minScrollExtent - this._outerPosition.pixels;
}
else {
D.assert(velocity < 0.0f);
extra = this._outerPosition.pixels -
(this._outerPosition.maxScrollExtent - this._outerPosition.minScrollExtent);
}
D.assert(extra <= 0.0f);
minRange = this._outerPosition.minScrollExtent;
maxRange = this._outerPosition.maxScrollExtent + extra;
D.assert(minRange <= maxRange);
correctionOffset = 0.0f;
}
}
return new _NestedScrollMetrics(
minScrollExtent: this._outerPosition.minScrollExtent,
maxScrollExtent: this._outerPosition.maxScrollExtent + innerPosition.maxScrollExtent -
innerPosition.minScrollExtent + extra,
pixels: pixels,
viewportDimension: this._outerPosition.viewportDimension,
axisDirection: this._outerPosition.axisDirection,
minRange: minRange,
maxRange: maxRange,
correctionOffset: correctionOffset
);
}
public float unnestOffset(float value, _NestedScrollPosition source) {
if (source == this._outerPosition) {
return value.clamp(this._outerPosition.minScrollExtent, this._outerPosition.maxScrollExtent);
}
if (value < source.minScrollExtent) {
return value - source.minScrollExtent + this._outerPosition.minScrollExtent;
}
return value - source.minScrollExtent + this._outerPosition.maxScrollExtent;
}
public float nestOffset(float value, _NestedScrollPosition target) {
if (target == this._outerPosition) {
return value.clamp(this._outerPosition.minScrollExtent, this._outerPosition.maxScrollExtent);
}
if (value < this._outerPosition.minScrollExtent) {
return value - this._outerPosition.minScrollExtent + target.minScrollExtent;
}
if (value > this._outerPosition.maxScrollExtent) {
return value - this._outerPosition.maxScrollExtent + target.minScrollExtent;
}
return target.minScrollExtent;
}
public void updateCanDrag() {
if (!this._outerPosition.haveDimensions) {
return;
}
float maxInnerExtent = 0.0f;
foreach (_NestedScrollPosition position in this._innerPositions) {
if (!position.haveDimensions) {
return;
}
maxInnerExtent = Mathf.Max(maxInnerExtent, position.maxScrollExtent - position.minScrollExtent);
}
this._outerPosition.updateCanDrag(maxInnerExtent);
}
public IPromise animateTo(float to,
TimeSpan duration,
Curve curve
) {
DrivenScrollActivity outerActivity = this._outerPosition.createDrivenScrollActivity(
this.nestOffset(to, this._outerPosition),
duration,
curve
);
List<IPromise> resultFutures = new List<IPromise> {outerActivity.done};
this.beginActivity(
outerActivity,
(_NestedScrollPosition position) => {
DrivenScrollActivity innerActivity = position.createDrivenScrollActivity(
this.nestOffset(to, position),
duration,
curve
);
resultFutures.Add(innerActivity.done);
return innerActivity;
}
);
return Promise.All(resultFutures);
}
public void jumpTo(float to) {
this.goIdle();
this._outerPosition.localJumpTo(this.nestOffset(to, this._outerPosition));
foreach (_NestedScrollPosition position in this._innerPositions) {
position.localJumpTo(this.nestOffset(to, position));
}
this.goBallistic(0.0f);
}
public float setPixels(float newPixels) {
D.assert(false);
return 0.0f;
}
public ScrollHoldController hold(VoidCallback holdCancelCallback) {
this.beginActivity(
new HoldScrollActivity(del: this._outerPosition, onHoldCanceled: holdCancelCallback),
(_NestedScrollPosition position) => new HoldScrollActivity(del: position)
);
return this;
}
public void cancel() {
this.goBallistic(0.0f);
}
public Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) {
ScrollDragController drag = new ScrollDragController(
del: this,
details: details,
onDragCanceled: dragCancelCallback
);
this.beginActivity(
new DragScrollActivity(this._outerPosition, drag),
(_NestedScrollPosition position) => new DragScrollActivity(position, drag)
);
D.assert(this._currentDrag == null);
this._currentDrag = drag;
return drag;
}
public void applyUserOffset(float delta) {
this.updateUserScrollDirection(delta > 0.0f ? ScrollDirection.forward : ScrollDirection.reverse);
D.assert(delta != 0.0f);
if (!this._innerPositions.Any()) {
this._outerPosition.applyFullDragUpdate(delta);
}
else if (delta < 0.0f) {
float innerDelta = this._outerPosition.applyClampedDragUpdate(delta);
if (innerDelta != 0.0f) {
foreach (_NestedScrollPosition position in this._innerPositions) {
position.applyFullDragUpdate(innerDelta);
}
}
}
else {
float outerDelta = 0.0f; // it will go positive if it changes
List<float> overscrolls = new List<float> { };
List<_NestedScrollPosition> innerPositions = this._innerPositions.ToList();
foreach (_NestedScrollPosition position in innerPositions) {
float overscroll = position.applyClampedDragUpdate(delta);
outerDelta = Mathf.Max(outerDelta, overscroll);
overscrolls.Add(overscroll);
}
if (outerDelta != 0.0f) {
outerDelta -= this._outerPosition.applyClampedDragUpdate(outerDelta);
}
for (int i = 0; i < innerPositions.Count; ++i) {
float remainingDelta = overscrolls[i] - outerDelta;
if (remainingDelta > 0.0f) {
innerPositions[i].applyFullDragUpdate(remainingDelta);
}
}
}
}
public void applyUserScrollOffset(float delta) {
// TODO: replace with real implementation
this.applyUserOffset(delta);
}
public void setParent(ScrollController value) {
this._parent = value;
this.updateParent();
}
public void updateParent() {
this._outerPosition?.setParent(this._parent ?? PrimaryScrollController.of(this._state.context));
}
public void dispose() {
this._currentDrag?.dispose();
this._currentDrag = null;
this._outerController.dispose();
this._innerController.dispose();
}
public override string ToString() {
return "$GetType()(outer=$_outerController; inner=$_innerController)";
}
}
class _NestedScrollController : ScrollController {
public _NestedScrollController(
_NestedScrollCoordinator coordinator,
float initialScrollOffset = 0.0f,
string debugLabel = null
) : base(initialScrollOffset: initialScrollOffset, debugLabel: debugLabel) {
this.coordinator = coordinator;
}
public readonly _NestedScrollCoordinator coordinator;
public override ScrollPosition createScrollPosition(
ScrollPhysics physics,
ScrollContext context,
ScrollPosition oldPosition
) {
return new _NestedScrollPosition(
coordinator: this.coordinator,
physics: physics,
context: context,
initialPixels: this.initialScrollOffset,
oldPosition: oldPosition,
debugLabel: this.debugLabel
);
}
public override void attach(ScrollPosition position) {
D.assert(position is _NestedScrollPosition);
base.attach(position);
this.coordinator.updateParent();
this.coordinator.updateCanDrag();
position.addListener(this._scheduleUpdateShadow);
this._scheduleUpdateShadow();
}
public override void detach(ScrollPosition position) {
D.assert(position is _NestedScrollPosition);
position.removeListener(this._scheduleUpdateShadow);
base.detach(position);
this._scheduleUpdateShadow();
}
void _scheduleUpdateShadow() {
SchedulerBinding.instance.addPostFrameCallback(
(TimeSpan timeStamp) => { this.coordinator.updateShadow(); }
);
}
public IEnumerable<_NestedScrollPosition> nestedPositions {
get {
foreach (var scrollPosition in this.positions) {
yield return (_NestedScrollPosition) scrollPosition;
}
}
}
}
class _NestedScrollPosition : ScrollPosition, ScrollActivityDelegate {
public _NestedScrollPosition(
ScrollPhysics physics,
ScrollContext context,
float initialPixels = 0.0f,
ScrollPosition oldPosition = null,
string debugLabel = null,
_NestedScrollCoordinator coordinator = null
) : base(
physics: physics,
context: context,
oldPosition: oldPosition,
debugLabel: debugLabel,
coordinator: coordinator
) {
D.assert(coordinator != null);
if (!this.havePixels) {
this.correctPixels(initialPixels);
}
if (this.activity == null) {
this.goIdle();
}
D.assert(this.activity != null);
this.saveScrollOffset(); // in case we didn't restore but could, so that we don't restore it later
}
public _NestedScrollCoordinator coordinator {
get { return (_NestedScrollCoordinator) this._coordinator; }
}
public TickerProvider vsync {
get { return this.context.vsync; }
}
ScrollController _parent;
public void setParent(ScrollController value) {
this._parent?.detach(this);
this._parent = value;
this._parent?.attach(this);
}
public override AxisDirection axisDirection {
get { return this.context.axisDirection; }
}
protected override void absorb(ScrollPosition other) {
base.absorb(other);
this.activity.updateDelegate(this);
}
protected override void restoreScrollOffset() {
if (this.coordinator.canScrollBody) {
base.restoreScrollOffset();
}
}
public float applyClampedDragUpdate(float delta) {
D.assert(delta != 0.0f);
float min = delta < 0.0f ? -float.PositiveInfinity : Mathf.Min(this.minScrollExtent, this.pixels);
float max = delta > 0.0f ? float.PositiveInfinity : Mathf.Max(this.maxScrollExtent, this.pixels);
float oldPixels = this.pixels;
float newPixels = (this.pixels - delta).clamp(min, max);
float clampedDelta = newPixels - this.pixels;
if (clampedDelta == 0.0f) {
return delta;
}
float overscroll = this.physics.applyBoundaryConditions(this, newPixels);
float actualNewPixels = newPixels - overscroll;
float offset = actualNewPixels - oldPixels;
if (offset != 0.0f) {
this.forcePixels(actualNewPixels);
this.didUpdateScrollPositionBy(offset);
}
return delta + offset;
}
public float applyFullDragUpdate(float delta) {
D.assert(delta != 0.0f);
float oldPixels = this.pixels;
float newPixels = this.pixels - this.physics.applyPhysicsToUserOffset(this, delta);
if (oldPixels == newPixels) {
return 0.0f; // delta must have been so small we dropped it during floating point addition
}
float overscroll = this.physics.applyBoundaryConditions(this, newPixels);
float actualNewPixels = newPixels - overscroll;
if (actualNewPixels != oldPixels) {
this.forcePixels(actualNewPixels);
this.didUpdateScrollPositionBy(actualNewPixels - oldPixels);
}
if (overscroll != 0.0f) {
this.didOverscrollBy(overscroll);
return overscroll;
}
return 0.0f;
}
public override ScrollDirection userScrollDirection {
get { return this.coordinator.userScrollDirection; }
}
public DrivenScrollActivity createDrivenScrollActivity(float to, TimeSpan duration, Curve curve) {
return new DrivenScrollActivity(
this,
from: this.pixels,
to: to,
duration: duration,
curve: curve,
vsync: this.vsync
);
}
public void applyUserOffset(float delta) {
D.assert(false);
}
public void applyUserScrollOffset(float delta) {
// TODO: replace with real implementation
this.applyUserOffset(delta);
}
public void goIdle() {
this.beginActivity(new IdleScrollActivity(this));
}
public void goBallistic(float velocity) {
Simulation simulation = null;
if (velocity != 0.0f || this.outOfRange()) {
simulation = this.physics.createBallisticSimulation(this, velocity);
}
this.beginActivity(this.createBallisticScrollActivity(
simulation,
mode: _NestedBallisticScrollActivityMode.independent
));
}
public ScrollActivity createBallisticScrollActivity(
Simulation simulation = null,
_NestedBallisticScrollActivityMode? mode = null,
_NestedScrollMetrics metrics = null
) {
if (simulation == null) {
return new IdleScrollActivity(this);
}
D.assert(mode != null);
switch (mode) {
case _NestedBallisticScrollActivityMode.outer:
D.assert(metrics != null);
if (metrics.minRange == metrics.maxRange) {
return new IdleScrollActivity(this);
}
return new _NestedOuterBallisticScrollActivity(this.coordinator, this, metrics, simulation,
this.context.vsync);
case _NestedBallisticScrollActivityMode.inner:
return new _NestedInnerBallisticScrollActivity(this.coordinator, this, simulation,
this.context.vsync);
case _NestedBallisticScrollActivityMode.independent:
return new BallisticScrollActivity(this, simulation, this.context.vsync);
}
return null;
}
public override IPromise animateTo(float to,
TimeSpan duration,
Curve curve
) {
return this.coordinator.animateTo(this.coordinator.unnestOffset(to, this), duration: duration,
curve: curve);
}
public override void jumpTo(float value) {
this.coordinator.jumpTo(this.coordinator.unnestOffset(value, this));
}
public void localJumpTo(float value) {
if (this.pixels != value) {
float oldPixels = this.pixels;
this.forcePixels(value);
this.didStartScroll();
this.didUpdateScrollPositionBy(this.pixels - oldPixels);
this.didEndScroll();
}
}
protected override void applyNewDimensions() {
base.applyNewDimensions();
this.coordinator.updateCanDrag();
}
public void updateCanDrag(float totalExtent) {
this.context.setCanDrag(totalExtent > (this.viewportDimension - this.maxScrollExtent) ||
this.minScrollExtent != this.maxScrollExtent);
}
public override ScrollHoldController hold(VoidCallback holdCancelCallback) {
return this.coordinator.hold(holdCancelCallback);
}
public override Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) {
return this.coordinator.drag(details, dragCancelCallback);
}
public override void dispose() {
this._parent?.detach(this);
base.dispose();
}
}
enum _NestedBallisticScrollActivityMode {
outer,
inner,
independent
}
class _NestedInnerBallisticScrollActivity : BallisticScrollActivity {
public _NestedInnerBallisticScrollActivity(
_NestedScrollCoordinator coordinator,
_NestedScrollPosition position,
Simulation simulation,
TickerProvider vsync
) : base(position, simulation, vsync) {
this.coordinator = coordinator;
}
public readonly _NestedScrollCoordinator coordinator;
public new _NestedScrollPosition del {
get { return (_NestedScrollPosition) base.del; }
}
public override void resetActivity() {
this.del.beginActivity(this.coordinator.createInnerBallisticScrollActivity(this.del, this.velocity));
}
public override void applyNewDimensions() {
this.del.beginActivity(this.coordinator.createInnerBallisticScrollActivity(this.del, this.velocity));
}
protected override bool applyMoveTo(float value) {
return base.applyMoveTo(this.coordinator.nestOffset(value, this.del));
}
}
class _NestedOuterBallisticScrollActivity : BallisticScrollActivity {
public _NestedOuterBallisticScrollActivity(
_NestedScrollCoordinator coordinator,
_NestedScrollPosition position,
_NestedScrollMetrics metrics,
Simulation simulation,
TickerProvider vsync
) : base(position, simulation, vsync) {
D.assert(metrics.minRange != metrics.maxRange);
D.assert(metrics.maxRange > metrics.minRange);
this.coordinator = coordinator;
this.metrics = metrics;
}
public readonly _NestedScrollCoordinator coordinator;
public readonly _NestedScrollMetrics metrics;
public new _NestedScrollPosition del {
get { return (_NestedScrollPosition) base.del; }
}
public override void resetActivity() {
this.del.beginActivity(this.coordinator.createOuterBallisticScrollActivity(this.velocity));
}
public override void applyNewDimensions() {
this.del.beginActivity(this.coordinator.createOuterBallisticScrollActivity(this.velocity));
}
protected override bool applyMoveTo(float value) {
bool done = false;
if (this.velocity > 0.0f) {
if (value < this.metrics.minRange) {
return true;
}
if (value > this.metrics.maxRange) {
value = this.metrics.maxRange;
done = true;
}
}
else if (this.velocity < 0.0f) {
if (value > this.metrics.maxRange) {
return true;
}
if (value < this.metrics.minRange) {
value = this.metrics.minRange;
done = true;
}
}
else {
value = value.clamp(this.metrics.minRange, this.metrics.maxRange);
done = true;
}
bool result = base.applyMoveTo(value + this.metrics.correctionOffset);
D.assert(result); // since we tried to pass an in-range value, it shouldn"t ever overflow
return !done;
}
public override string ToString() {
return
$"{this.GetType()}({this.metrics.minRange} .. {this.metrics.maxRange}; correcting by {this.metrics.correctionOffset})";
}
}
public class SliverOverlapAbsorberHandle : ChangeNotifier {
internal int _writers = 0;
public float layoutExtent {
get { return this._layoutExtent; }
}
float _layoutExtent;
public float scrollExtent {
get { return this._scrollExtent; }
}
float _scrollExtent;
internal void _setExtents(float layoutValue, float scrollValue) {
D.assert(this._writers == 1,
() => "Multiple RenderSliverOverlapAbsorbers have been provided the same SliverOverlapAbsorberHandle.");
this._layoutExtent = layoutValue;
this._scrollExtent = scrollValue;
}
internal void _markNeedsLayout() {
this.notifyListeners();
}
public override string ToString() {
string extra = "";
switch (this._writers) {
case 0:
extra = ", orphan";
break;
case 1:
break;
default:
extra = ", $_writers WRITERS ASSIGNED";
break;
}
return $"{this.GetType()}({this.layoutExtent}{extra})";
}
}
public class SliverOverlapAbsorber : SingleChildRenderObjectWidget {
public SliverOverlapAbsorber(
Key key = null,
SliverOverlapAbsorberHandle handle = null,
Widget child = null
) : base(key: key, child: child) {
D.assert(handle != null);
this.handle = handle;
}
public readonly SliverOverlapAbsorberHandle handle;
public override RenderObject createRenderObject(BuildContext context) {
return new RenderSliverOverlapAbsorber(
handle: this.handle
);
}
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
RenderSliverOverlapAbsorber renderObject = _renderObject as RenderSliverOverlapAbsorber;
renderObject.handle = this.handle;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<SliverOverlapAbsorberHandle>("handle", this.handle));
}
}
public class RenderSliverOverlapAbsorber : RenderObjectWithChildMixinRenderSliver<RenderSliver> {
public RenderSliverOverlapAbsorber(
SliverOverlapAbsorberHandle handle,
RenderSliver child = null
) {
D.assert(handle != null);
this._handle = handle;
this.child = child;
}
public SliverOverlapAbsorberHandle handle {
get { return this._handle; }
set {
D.assert(value != null);
if (this.handle == value) {
return;
}
if (this.attached) {
this.handle._writers -= 1;
value._writers += 1;
value._setExtents(this.handle.layoutExtent, this.handle.scrollExtent);
}
this._handle = value;
}
}
SliverOverlapAbsorberHandle _handle;
public override void attach(object owner) {
base.attach(owner);
this.handle._writers += 1;
}
public override void detach() {
this.handle._writers -= 1;
base.detach();
}
protected override void performLayout() {
D.assert(this.handle._writers == 1,
() =>
"A SliverOverlapAbsorberHandle cannot be passed to multiple RenderSliverOverlapAbsorber objects at the same time.");
if (this.child == null) {
this.geometry = new SliverGeometry();
return;
}
this.child.layout(this.constraints, parentUsesSize: true);
SliverGeometry childLayoutGeometry = this.child.geometry;
this.geometry = new SliverGeometry(
scrollExtent: childLayoutGeometry.scrollExtent - childLayoutGeometry.maxScrollObstructionExtent,
paintExtent: childLayoutGeometry.paintExtent,
paintOrigin: childLayoutGeometry.paintOrigin,
layoutExtent: childLayoutGeometry.paintExtent - childLayoutGeometry.maxScrollObstructionExtent,
maxPaintExtent: childLayoutGeometry.maxPaintExtent,
maxScrollObstructionExtent: childLayoutGeometry.maxScrollObstructionExtent,
hitTestExtent: childLayoutGeometry.hitTestExtent,
visible: childLayoutGeometry.visible,
hasVisualOverflow: childLayoutGeometry.hasVisualOverflow,
scrollOffsetCorrection: childLayoutGeometry.scrollOffsetCorrection
);
this.handle._setExtents(childLayoutGeometry.maxScrollObstructionExtent,
childLayoutGeometry.maxScrollObstructionExtent);
}
public override void applyPaintTransform(RenderObject child, Matrix3 transform) {
}
protected override bool hitTestChildren(
HitTestResult result,
float mainAxisPosition = 0,
float crossAxisPosition = 0
) {
if (this.child != null) {
return this.child.hitTest(result, mainAxisPosition: mainAxisPosition,
crossAxisPosition: crossAxisPosition);
}
return false;
}
public override void paint(PaintingContext context, Offset offset) {
if (this.child != null) {
context.paintChild(this.child, offset);
}
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<SliverOverlapAbsorberHandle>("handle", this.handle));
}
}
public class SliverOverlapInjector : SingleChildRenderObjectWidget {
public SliverOverlapInjector(
Key key = null,
SliverOverlapAbsorberHandle handle = null,
Widget child = null
) : base(key: key, child: child) {
D.assert(handle != null);
this.handle = handle;
}
public readonly SliverOverlapAbsorberHandle handle;
public override RenderObject createRenderObject(BuildContext context) {
return new RenderSliverOverlapInjector(
handle: this.handle
);
}
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
RenderSliverOverlapInjector renderObject = _renderObject as RenderSliverOverlapInjector;
renderObject.handle = this.handle;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<SliverOverlapAbsorberHandle>("handle", this.handle));
}
}
public class RenderSliverOverlapInjector : RenderSliver {
public RenderSliverOverlapInjector(
SliverOverlapAbsorberHandle handle
) {
D.assert(handle != null);
this._handle = handle;
}
float _currentLayoutExtent;
float _currentMaxExtent;
public SliverOverlapAbsorberHandle handle {
get { return this._handle; }
set {
D.assert(value != null);
if (this.handle == value) {
return;
}
if (this.attached) {
this.handle.removeListener(this.markNeedsLayout);
}
this._handle = value;
if (this.attached) {
this.handle.addListener(this.markNeedsLayout);
if (this.handle.layoutExtent != this._currentLayoutExtent ||
this.handle.scrollExtent != this._currentMaxExtent) {
this.markNeedsLayout();
}
}
}
}
SliverOverlapAbsorberHandle _handle;
public override void attach(object owner) {
base.attach(owner);
this.handle.addListener(this.markNeedsLayout);
if (this.handle.layoutExtent != this._currentLayoutExtent ||
this.handle.scrollExtent != this._currentMaxExtent) {
this.markNeedsLayout();
}
}
public override void detach() {
this.handle.removeListener(this.markNeedsLayout);
base.detach();
}
protected override void performLayout() {
this._currentLayoutExtent = this.handle.layoutExtent;
this._currentMaxExtent = this.handle.layoutExtent;
float clampedLayoutExtent = Mathf.Min(this._currentLayoutExtent - this.constraints.scrollOffset,
this.constraints.remainingPaintExtent);
this.geometry = new SliverGeometry(
scrollExtent: this._currentLayoutExtent,
paintExtent: Mathf.Max(0.0f, clampedLayoutExtent),
maxPaintExtent: this._currentMaxExtent
);
}
public override void debugPaint(PaintingContext context, Offset offset) {
D.assert(() => {
if (D.debugPaintSizeEnabled) {
Paint paint = new Paint();
paint.color = new Color(0xFFCC9933);
paint.strokeWidth = 3.0f;
paint.style = PaintingStyle.stroke;
Offset start, end, delta;
switch (this.constraints.axis) {
case Axis.vertical:
float x = offset.dx + this.constraints.crossAxisExtent / 2.0f;
start = new Offset(x, offset.dy);
end = new Offset(x, offset.dy + this.geometry.paintExtent);
delta = new Offset(this.constraints.crossAxisExtent / 5.0f, 0.0f);
break;
case Axis.horizontal:
float y = offset.dy + this.constraints.crossAxisExtent / 2.0f;
start = new Offset(offset.dx, y);
end = new Offset(offset.dy + this.geometry.paintExtent, y);
delta = new Offset(0.0f, this.constraints.crossAxisExtent / 5.0f);
break;
default:
throw new Exception("");
}
for (int index = -2; index <= 2; index += 1) {
PaintingUtilities.paintZigZag(context.canvas, paint, start - delta * (float) index,
end - delta * (float) index, 10, 10.0f);
}
}
return true;
});
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<SliverOverlapAbsorberHandle>("handle", this.handle));
}
}
public class NestedScrollViewViewport : Viewport {
public NestedScrollViewViewport(
Key key = null,
AxisDirection axisDirection = AxisDirection.down,
AxisDirection crossAxisDirection = AxisDirection.right,
float anchor = 0.0f,
ViewportOffset offset = null,
Key center = null,
List<Widget> slivers = null,
SliverOverlapAbsorberHandle handle = null
) : base(
key: key,
axisDirection: axisDirection,
crossAxisDirection: crossAxisDirection,
anchor: anchor,
offset: offset,
center: center,
slivers: slivers ?? new List<Widget>()
) {
D.assert(handle != null);
D.assert(this.offset != null);
this.handle = handle;
}
public readonly SliverOverlapAbsorberHandle handle;
public override RenderObject createRenderObject(BuildContext context) {
return new RenderNestedScrollViewViewport(
axisDirection: this.axisDirection,
crossAxisDirection: this.crossAxisDirection ??
getDefaultCrossAxisDirection(context, this.axisDirection),
anchor: this.anchor,
offset: this.offset,
handle: this.handle
);
}
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
RenderNestedScrollViewViewport renderObject = _renderObject as RenderNestedScrollViewViewport;
renderObject.axisDirection = this.axisDirection;
renderObject.crossAxisDirection = this.crossAxisDirection ??
getDefaultCrossAxisDirection(context, this.axisDirection);
if (this.crossAxisDirection == null) {
renderObject.anchor = this.anchor;
}
renderObject.offset = this.offset;
renderObject.handle = this.handle;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<SliverOverlapAbsorberHandle>("handle", this.handle));
}
}
public class RenderNestedScrollViewViewport : RenderViewport {
public RenderNestedScrollViewViewport(
AxisDirection axisDirection = AxisDirection.down,
AxisDirection crossAxisDirection = AxisDirection.right,
ViewportOffset offset = null,
float anchor = 0.0f,
List<RenderSliver> children = null,
RenderSliver center = null,
SliverOverlapAbsorberHandle handle = null
) : base(
axisDirection: axisDirection,
crossAxisDirection: crossAxisDirection,
offset: offset,
anchor: anchor,
children: children,
center: center
) {
D.assert(handle != null);
D.assert(offset != null);
this._handle = handle;
}
public SliverOverlapAbsorberHandle handle {
get { return this._handle; }
set {
D.assert(value != null);
if (this.handle == value) {
return;
}
this._handle = value;
this.handle._markNeedsLayout();
}
}
SliverOverlapAbsorberHandle _handle;
public override void markNeedsLayout() {
this.handle._markNeedsLayout();
base.markNeedsLayout();
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<SliverOverlapAbsorberHandle>("handle", this.handle));
}
}
}