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