您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
193 行
6.6 KiB
193 行
6.6 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using RSG;
|
|
using Unity.UIWidgets.animation;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.ui;
|
|
|
|
namespace Unity.UIWidgets.widgets {
|
|
public class ScrollController : ChangeNotifier {
|
|
public ScrollController(
|
|
double initialScrollOffset = 0.0,
|
|
bool keepScrollOffset = true,
|
|
string debugLabel = null
|
|
) {
|
|
this._initialScrollOffset = initialScrollOffset;
|
|
this.keepScrollOffset = keepScrollOffset;
|
|
this.debugLabel = debugLabel;
|
|
}
|
|
|
|
public virtual double initialScrollOffset {
|
|
get { return this._initialScrollOffset; }
|
|
}
|
|
|
|
readonly double _initialScrollOffset;
|
|
|
|
public readonly bool keepScrollOffset;
|
|
|
|
public readonly string debugLabel;
|
|
|
|
public ICollection<ScrollPosition> positions {
|
|
get { return this._positions; }
|
|
}
|
|
|
|
readonly List<ScrollPosition> _positions = new List<ScrollPosition>();
|
|
|
|
public bool hasClients {
|
|
get { return this._positions.isNotEmpty(); }
|
|
}
|
|
|
|
public ScrollPosition position {
|
|
get {
|
|
D.assert(this._positions.isNotEmpty(), "ScrollController not attached to any scroll views.");
|
|
D.assert(this._positions.Count == 1, "ScrollController attached to multiple scroll views.");
|
|
return this._positions.Single();
|
|
}
|
|
}
|
|
|
|
public double offset {
|
|
get { return this.position.pixels; }
|
|
}
|
|
|
|
|
|
public IPromise animateTo(double to,
|
|
TimeSpan duration,
|
|
Curve curve
|
|
) {
|
|
D.assert(this._positions.isNotEmpty(), "ScrollController not attached to any scroll views.");
|
|
List<IPromise> animations = Enumerable.Repeat((IPromise) null, this._positions.Count).ToList();
|
|
for (int i = 0; i < this._positions.Count; i += 1) {
|
|
animations[i] = this._positions[i].animateTo(to, duration: duration, curve: curve);
|
|
}
|
|
|
|
return Promise.All(animations);
|
|
}
|
|
|
|
public void jumpTo(double value) {
|
|
D.assert(this._positions.isNotEmpty(), "ScrollController not attached to any scroll views.");
|
|
foreach (ScrollPosition position in new List<ScrollPosition>(this._positions)) {
|
|
position.jumpTo(value);
|
|
}
|
|
}
|
|
|
|
public virtual void attach(ScrollPosition position) {
|
|
D.assert(!this._positions.Contains(position));
|
|
this._positions.Add(position);
|
|
position.addListener(this.notifyListeners);
|
|
}
|
|
|
|
public virtual void detach(ScrollPosition position) {
|
|
D.assert(this._positions.Contains(position));
|
|
position.removeListener(this.notifyListeners);
|
|
this._positions.Remove(position);
|
|
}
|
|
|
|
public override void dispose() {
|
|
foreach (ScrollPosition position in this._positions) {
|
|
position.removeListener(this.notifyListeners);
|
|
}
|
|
|
|
base.dispose();
|
|
}
|
|
|
|
public virtual ScrollPosition createScrollPosition(
|
|
ScrollPhysics physics,
|
|
ScrollContext context,
|
|
ScrollPosition oldPosition
|
|
) {
|
|
return new ScrollPositionWithSingleContext(
|
|
physics: physics,
|
|
context: context,
|
|
initialPixels: this.initialScrollOffset,
|
|
keepScrollOffset: this.keepScrollOffset,
|
|
oldPosition: oldPosition,
|
|
debugLabel: this.debugLabel
|
|
);
|
|
}
|
|
|
|
public override string ToString() {
|
|
List<string> description = new List<string>();
|
|
this.debugFillDescription(description);
|
|
return $"{Diagnostics.describeIdentity(this)}({string.Join(", ", description.ToArray())})";
|
|
}
|
|
|
|
protected virtual void debugFillDescription(List<string> description) {
|
|
if (this.debugLabel != null) {
|
|
description.Add(this.debugLabel);
|
|
}
|
|
|
|
if (this.initialScrollOffset != 0.0) {
|
|
description.Add($"initialScrollOffset: {this.initialScrollOffset:F1}, ");
|
|
}
|
|
|
|
if (this._positions.isEmpty()) {
|
|
description.Add("no clients");
|
|
}
|
|
else if (this._positions.Count == 1) {
|
|
description.Add($"one client, offset {this.offset:F1}");
|
|
}
|
|
else {
|
|
description.Add(this._positions.Count + " clients");
|
|
}
|
|
}
|
|
}
|
|
|
|
public class TrackingScrollController : ScrollController {
|
|
public TrackingScrollController(
|
|
double initialScrollOffset = 0.0,
|
|
bool keepScrollOffset = true,
|
|
string debugLabel = null
|
|
) : base(initialScrollOffset: initialScrollOffset,
|
|
keepScrollOffset: keepScrollOffset,
|
|
debugLabel: debugLabel) {
|
|
}
|
|
|
|
readonly Dictionary<ScrollPosition, VoidCallback> _positionToListener =
|
|
new Dictionary<ScrollPosition, VoidCallback>();
|
|
|
|
ScrollPosition _lastUpdated;
|
|
double? _lastUpdatedOffset;
|
|
|
|
public ScrollPosition mostRecentlyUpdatedPosition {
|
|
get { return this._lastUpdated; }
|
|
}
|
|
|
|
public override double initialScrollOffset {
|
|
get { return this._lastUpdatedOffset ?? base.initialScrollOffset; }
|
|
}
|
|
|
|
public override void attach(ScrollPosition position) {
|
|
base.attach(position);
|
|
D.assert(!this._positionToListener.ContainsKey(position));
|
|
this._positionToListener[position] = () => {
|
|
this._lastUpdated = position;
|
|
this._lastUpdatedOffset = position.pixels;
|
|
};
|
|
position.addListener(this._positionToListener[position]);
|
|
}
|
|
|
|
public override void detach(ScrollPosition position) {
|
|
base.detach(position);
|
|
D.assert(this._positionToListener.ContainsKey(position));
|
|
position.removeListener(this._positionToListener[position]);
|
|
this._positionToListener.Remove(position);
|
|
if (this._lastUpdated == position) {
|
|
this._lastUpdated = null;
|
|
}
|
|
|
|
if (this._positionToListener.isEmpty()) {
|
|
this._lastUpdatedOffset = null;
|
|
}
|
|
}
|
|
|
|
public override void dispose() {
|
|
foreach (ScrollPosition position in this.positions) {
|
|
D.assert(this._positionToListener.ContainsKey(position));
|
|
position.removeListener(this._positionToListener[position]);
|
|
}
|
|
|
|
base.dispose();
|
|
}
|
|
}
|
|
}
|