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

837 行
32 KiB

using System;
using System.Collections.Generic;
using RSG;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.widgets {
public abstract class OverlayRoute : Route {
readonly List<OverlayEntry> _overlayEntries = new List<OverlayEntry>();
public OverlayRoute(
RouteSettings settings = null
) : base(settings) {
}
public override List<OverlayEntry> overlayEntries {
get { return this._overlayEntries; }
}
protected virtual bool finishedWhenPopped {
get { return true; }
}
public abstract ICollection<OverlayEntry> createOverlayEntries();
protected internal override void install(OverlayEntry insertionPoint) {
D.assert(this._overlayEntries.isEmpty());
this._overlayEntries.AddRange(this.createOverlayEntries());
this.navigator.overlay?.insertAll(this._overlayEntries, insertionPoint);
base.install(insertionPoint);
}
protected internal override bool didPop(object result) {
var returnValue = base.didPop(result);
D.assert(returnValue);
if (this.finishedWhenPopped) {
this.navigator.finalizeRoute(this);
}
return returnValue;
}
protected internal override void dispose() {
foreach (var entry in this._overlayEntries) {
entry.remove();
}
this._overlayEntries.Clear();
base.dispose();
}
}
public abstract class TransitionRoute : OverlayRoute {
public TransitionRoute(
RouteSettings settings = null
) : base(settings) {
}
public IPromise<object> completed {
get { return this._transitionCompleter; }
}
internal readonly Promise<object> _transitionCompleter = new Promise<object>();
public virtual TimeSpan transitionDuration { get; }
public virtual bool opaque { get; }
protected override bool finishedWhenPopped {
get { return this.controller.status == AnimationStatus.dismissed; }
}
public virtual Animation<float> animation {
get { return this._animation; }
}
internal Animation<float> _animation;
public AnimationController controller {
get { return this._controller; }
}
internal AnimationController _controller;
public virtual AnimationController createAnimationController() {
D.assert(this._transitionCompleter.CurState == PromiseState.Pending,
() => $"Cannot reuse a {this.GetType()} after disposing it.");
TimeSpan duration = this.transitionDuration;
D.assert(duration >= TimeSpan.Zero);
return new AnimationController(
duration: duration,
debugLabel: this.debugLabel,
vsync: this.navigator
);
}
public virtual Animation<float> createAnimation() {
D.assert(this._transitionCompleter.CurState == PromiseState.Pending,
() => $"Cannot reuse a {this.GetType()} after disposing it.");
D.assert(this._controller != null);
return this._controller.view;
}
object _result;
internal void _handleStatusChanged(AnimationStatus status) {
switch (status) {
case AnimationStatus.completed:
if (this.overlayEntries.isNotEmpty()) {
this.overlayEntries.first().opaque = this.opaque;
}
break;
case AnimationStatus.forward:
case AnimationStatus.reverse:
if (this.overlayEntries.isNotEmpty()) {
this.overlayEntries.first().opaque = false;
}
break;
case AnimationStatus.dismissed:
// We might still be the current route if a subclass is controlling the
// the transition and hits the dismissed status. For example, the iOS
// back gesture drives this animation to the dismissed status before
// popping the navigator.
if (!this.isCurrent) {
this.navigator.finalizeRoute(this);
D.assert(this.overlayEntries.isEmpty());
}
break;
}
this.changedInternalState();
}
public virtual Animation<float> secondaryAnimation {
get { return this._secondaryAnimation; }
}
readonly ProxyAnimation _secondaryAnimation = new ProxyAnimation(Animations.kAlwaysDismissedAnimation);
protected internal override void install(OverlayEntry insertionPoint) {
D.assert(!this._transitionCompleter.isCompleted, () => $"Cannot install a {this.GetType()} after disposing it.");
this._controller = this.createAnimationController();
D.assert(this._controller != null, () => $"{this.GetType()}.createAnimationController() returned null.");
this._animation = this.createAnimation();
D.assert(this._animation != null, () => $"{this.GetType()}.createAnimation() returned null.");
base.install(insertionPoint);
}
protected internal override TickerFuture didPush() {
D.assert(this._controller != null,
() => $"{this.GetType()}.didPush called before calling install() or after calling dispose().");
D.assert(!this._transitionCompleter.isCompleted, () => $"Cannot reuse a {this.GetType()} after disposing it.");
this._animation.addStatusListener(this._handleStatusChanged);
return this._controller.forward();
}
protected internal override void didReplace(Route oldRoute) {
D.assert(this._controller != null,
() => $"{this.GetType()}.didReplace called before calling install() or after calling dispose().");
D.assert(!this._transitionCompleter.isCompleted, () => $"Cannot reuse a {this.GetType()} after disposing it.");
if (oldRoute is TransitionRoute route) {
this._controller.setValue(route._controller.value);
}
this._animation.addStatusListener(this._handleStatusChanged);
base.didReplace(oldRoute);
}
protected internal override bool didPop(object result) {
D.assert(this._controller != null,
() => $"{this.GetType()}.didPop called before calling install() or after calling dispose().");
D.assert(!this._transitionCompleter.isCompleted, () => $"Cannot reuse a {this.GetType()} after disposing it.");
this._result = result;
this._controller.reverse();
return base.didPop(result);
}
protected internal override void didPopNext(Route nextRoute) {
D.assert(this._controller != null,
() => $"{this.GetType()}.didPopNext called before calling install() or after calling dispose().");
D.assert(!this._transitionCompleter.isCompleted, () => $"Cannot reuse a {this.GetType()} after disposing it.");
this._updateSecondaryAnimation(nextRoute);
base.didPopNext(nextRoute);
}
protected internal override void didChangeNext(Route nextRoute) {
D.assert(this._controller != null,
() => $"{this.GetType()}.didChangeNext called before calling install() or after calling dispose().");
D.assert(!this._transitionCompleter.isCompleted, () => $"Cannot reuse a {this.GetType()} after disposing it.");
this._updateSecondaryAnimation(nextRoute);
base.didChangeNext(nextRoute);
}
void _updateSecondaryAnimation(Route nextRoute) {
if (nextRoute is TransitionRoute && this.canTransitionTo((TransitionRoute) nextRoute) &&
((TransitionRoute) nextRoute).canTransitionFrom(this)) {
Animation<float> current = this._secondaryAnimation.parent;
if (current != null) {
if (current is TrainHoppingAnimation) {
TrainHoppingAnimation newAnimation = null;
newAnimation = new TrainHoppingAnimation(
((TrainHoppingAnimation) current).currentTrain,
((TransitionRoute) nextRoute)._animation,
onSwitchedTrain: () => {
D.assert(this._secondaryAnimation.parent == newAnimation);
D.assert(newAnimation.currentTrain == ((TransitionRoute) nextRoute)._animation);
this._secondaryAnimation.parent = newAnimation.currentTrain;
newAnimation.dispose();
}
);
this._secondaryAnimation.parent = newAnimation;
((TrainHoppingAnimation) current).dispose();
}
else {
this._secondaryAnimation.parent =
new TrainHoppingAnimation(current, ((TransitionRoute) nextRoute)._animation);
}
}
else {
this._secondaryAnimation.parent = ((TransitionRoute) nextRoute)._animation;
}
}
else {
this._secondaryAnimation.parent = Animations.kAlwaysDismissedAnimation;
}
}
public virtual bool canTransitionTo(TransitionRoute nextRoute) {
return true;
}
public virtual bool canTransitionFrom(TransitionRoute previousRoute) {
return true;
}
protected internal override void dispose() {
D.assert(!this._transitionCompleter.isCompleted, () => $"Cannot dispose a {this.GetType()} twice.");
this._controller?.dispose();
this._transitionCompleter.Resolve(this._result);
base.dispose();
}
public string debugLabel {
get { return $"{this.GetType()}"; }
}
public override string ToString() {
return $"{this.GetType()}(animation: {this._controller}";
}
}
public class LocalHistoryEntry {
public LocalHistoryEntry(VoidCallback onRemove = null) {
this.onRemove = onRemove;
}
public readonly VoidCallback onRemove;
internal LocalHistoryRoute _owner;
public void remove() {
this._owner.removeLocalHistoryEntry(this);
D.assert(this._owner == null);
}
internal void _notifyRemoved() {
this.onRemove?.Invoke();
}
}
public interface LocalHistoryRoute {
void addLocalHistoryEntry(LocalHistoryEntry entry);
void removeLocalHistoryEntry(LocalHistoryEntry entry);
Route route { get; }
}
// todo make it to mixin
public abstract class LocalHistoryRouteTransitionRoute : TransitionRoute, LocalHistoryRoute {
List<LocalHistoryEntry> _localHistory;
protected LocalHistoryRouteTransitionRoute(RouteSettings settings = null) : base(settings: settings) {
}
public void addLocalHistoryEntry(LocalHistoryEntry entry) {
D.assert(entry._owner == null);
entry._owner = this;
this._localHistory = this._localHistory ?? new List<LocalHistoryEntry>();
var wasEmpty = this._localHistory.isEmpty();
this._localHistory.Add(entry);
if (wasEmpty) {
this.changedInternalState();
}
}
public void removeLocalHistoryEntry(LocalHistoryEntry entry) {
D.assert(entry != null);
D.assert(entry._owner == this);
D.assert(this._localHistory.Contains(entry));
this._localHistory.Remove(entry);
entry._owner = null;
entry._notifyRemoved();
if (this._localHistory.isEmpty()) {
this.changedInternalState();
}
}
public override IPromise<RoutePopDisposition> willPop() {
if (this.willHandlePopInternally) {
return Promise<RoutePopDisposition>.Resolved(RoutePopDisposition.pop);
}
return base.willPop();
}
protected internal override bool didPop(object result) {
if (this._localHistory != null && this._localHistory.isNotEmpty()) {
var entry = this._localHistory.removeLast();
D.assert(entry._owner == this);
entry._owner = null;
entry._notifyRemoved();
if (this._localHistory.isEmpty()) {
this.changedInternalState();
}
return false;
}
return base.didPop(result);
}
public override bool willHandlePopInternally {
get { return this._localHistory != null && this._localHistory.isNotEmpty(); }
}
public Route route {
get { return this; }
}
}
public class _ModalScopeStatus : InheritedWidget {
public _ModalScopeStatus(Key key = null, bool isCurrent = false,
bool canPop = false, Route route = null, Widget child = null) : base(key: key, child: child) {
D.assert(route != null);
D.assert(child != null);
this.isCurrent = isCurrent;
this.canPop = canPop;
this.route = route;
}
public readonly bool isCurrent;
public readonly bool canPop;
public readonly Route route;
public override bool updateShouldNotify(InheritedWidget oldWidget) {
return this.isCurrent != ((_ModalScopeStatus) oldWidget).isCurrent ||
this.canPop != ((_ModalScopeStatus) oldWidget).canPop ||
this.route != ((_ModalScopeStatus) oldWidget).route;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder description) {
base.debugFillProperties(description);
description.add(new FlagProperty("isCurrent", value: this.isCurrent, ifTrue: "active",
ifFalse: "inactive"));
description.add(new FlagProperty("canPop", value: this.canPop, ifTrue: "can pop"));
}
}
public class _ModalScope : StatefulWidget {
public _ModalScope(Key key = null, ModalRoute route = null) : base(key) {
this.route = route;
}
public readonly ModalRoute route;
public override State createState() {
return new _ModalScopeState();
}
}
public class _ModalScopeState : State<_ModalScope> {
Widget _page;
Listenable _listenable;
public override void initState() {
base.initState();
var animations = new List<Listenable> { };
if (this.widget.route.animation != null) {
animations.Add(this.widget.route.animation);
}
if (this.widget.route.secondaryAnimation != null) {
animations.Add(this.widget.route.secondaryAnimation);
}
this._listenable = ListenableUtils.merge(animations);
}
public override void didUpdateWidget(StatefulWidget oldWidget) {
base.didUpdateWidget(oldWidget);
D.assert(this.widget.route == ((_ModalScope) oldWidget).route);
}
public override void didChangeDependencies() {
base.didChangeDependencies();
this._page = null;
}
internal void _forceRebuildPage() {
this.setState(() => { this._page = null; });
}
internal void _routeSetState(VoidCallback fn) {
this.setState(fn);
}
public override Widget build(BuildContext context) {
this._page = this._page ?? new RepaintBoundary(
key: this.widget.route._subtreeKey, // immutable
child: new Builder(
builder: (BuildContext _context) => this.widget.route.buildPage(
_context,
this.widget.route.animation,
this.widget.route.secondaryAnimation
))
);
return new _ModalScopeStatus(
route: this.widget.route,
isCurrent: this.widget.route.isCurrent,
canPop: this.widget.route.canPop,
child: new Offstage(
offstage: this.widget.route.offstage,
child: new PageStorage(
bucket: this.widget.route._storageBucket,
child: new FocusScope(
node: this.widget.route.focusScopeNode,
child: new RepaintBoundary(
child: new AnimatedBuilder(
animation: this._listenable, // immutable
builder: (BuildContext _context, Widget child) =>
this.widget.route.buildTransitions(
_context,
this.widget.route.animation,
this.widget.route.secondaryAnimation,
new IgnorePointer(
ignoring: this.widget.route.animation?.status ==
AnimationStatus.reverse,
child: child
)
),
child: this._page
)
)
)
)
)
);
}
}
public abstract class ModalRoute : LocalHistoryRouteTransitionRoute {
protected ModalRoute(RouteSettings settings) : base(settings) {
}
public static Color _kTransparent = new Color(0x00000000);
public static ModalRoute of(BuildContext context) {
_ModalScopeStatus widget =
(_ModalScopeStatus) context.inheritFromWidgetOfExactType(typeof(_ModalScopeStatus));
return (ModalRoute) widget?.route;
}
protected virtual void setState(VoidCallback fn) {
if (this._scopeKey.currentState != null) {
this._scopeKey.currentState._routeSetState(fn);
}
else {
fn();
}
}
public RoutePredicate withName(string name) {
return (Route route) => !route.willHandlePopInternally
&& route is ModalRoute
&& route.settings.name == name;
}
public abstract Widget buildPage(BuildContext context, Animation<float> animation,
Animation<float> secondaryAnimation);
public virtual Widget buildTransitions(
BuildContext context,
Animation<float> animation,
Animation<float> secondaryAnimation,
Widget child
) {
return child;
}
public readonly FocusScopeNode focusScopeNode = new FocusScopeNode();
protected internal override void install(OverlayEntry insertionPoint) {
base.install(insertionPoint);
this._animationProxy = new ProxyAnimation(base.animation);
this._secondaryAnimationProxy = new ProxyAnimation(base.secondaryAnimation);
}
protected internal override TickerFuture didPush() {
this.navigator.focusScopeNode.setFirstFocus(this.focusScopeNode);
return base.didPush();
}
protected internal override void dispose() {
this.focusScopeNode.detach();
base.dispose();
}
public virtual bool barrierDismissible { get; }
public virtual Color barrierColor { get; }
public virtual bool maintainState { get; }
public bool offstage {
get { return this._offstage; }
set {
if (this._offstage == value) {
return;
}
this.setState(() => { this._offstage = value; });
this._animationProxy.parent = this._offstage ? Animations.kAlwaysCompleteAnimation : base.animation;
this._secondaryAnimationProxy.parent =
this._offstage ? Animations.kAlwaysDismissedAnimation : base.secondaryAnimation;
}
}
bool _offstage = false;
public BuildContext subtreeContext {
get { return this._subtreeKey.currentContext; }
}
public override Animation<float> animation {
get { return this._animationProxy; }
}
ProxyAnimation _animationProxy;
public override Animation<float> secondaryAnimation {
get { return this._secondaryAnimationProxy; }
}
ProxyAnimation _secondaryAnimationProxy;
readonly List<WillPopCallback> _willPopCallbacks = new List<WillPopCallback>();
public override IPromise<RoutePopDisposition> willPop() {
_ModalScopeState scope = this._scopeKey.currentState;
D.assert(scope != null);
var callbacks = new List<WillPopCallback>(this._willPopCallbacks);
Promise<RoutePopDisposition> result = new Promise<RoutePopDisposition>();
Action<int> fn = null;
fn = (int index) => {
if (index < callbacks.Count) {
callbacks[index]().Then((pop) => {
if (!pop) {
result.Resolve(RoutePopDisposition.doNotPop);
}
else {
fn(index + 1);
}
});
}
else {
base.willPop().Then((pop) => result.Resolve(pop));
}
};
fn(0);
return result;
}
public void addScopedWillPopCallback(WillPopCallback callback) {
D.assert(this._scopeKey.currentState != null,
() => "Tried to add a willPop callback to a route that is not currently in the tree.");
this._willPopCallbacks.Add(callback);
}
public void removeScopedWillPopCallback(WillPopCallback callback) {
D.assert(this._scopeKey.currentState != null,
() => "Tried to remove a willPop callback from a route that is not currently in the tree.");
this._willPopCallbacks.Remove(callback);
}
protected bool hasScopedWillPopCallback {
get { return this._willPopCallbacks.isNotEmpty(); }
}
protected internal override void didChangePrevious(Route previousRoute) {
base.didChangePrevious(previousRoute);
this.changedInternalState();
}
protected internal override void changedInternalState() {
base.changedInternalState();
this.setState(() => { });
this._modalBarrier.markNeedsBuild();
}
protected internal override void changedExternalState() {
base.changedExternalState();
this._scopeKey.currentState?._forceRebuildPage();
}
public bool canPop {
get { return !this.isFirst || this.willHandlePopInternally; }
}
readonly GlobalKey<_ModalScopeState> _scopeKey = new LabeledGlobalKey<_ModalScopeState>();
internal readonly GlobalKey _subtreeKey = new LabeledGlobalKey<_ModalScopeState>();
internal readonly PageStorageBucket _storageBucket = new PageStorageBucket();
static readonly Animatable<float> _easeCurveTween = new CurveTween(curve: Curves.ease);
OverlayEntry _modalBarrier;
Widget _buildModalBarrier(BuildContext context) {
Widget barrier;
if (this.barrierColor != null && !this.offstage) {
// changedInternalState is called if these update
D.assert(this.barrierColor != _kTransparent);
Animation<Color> color =
new ColorTween(
begin: _kTransparent,
end: this.barrierColor // changedInternalState is called if this updates
).chain(_easeCurveTween).animate(this.animation);
barrier = new AnimatedModalBarrier(
color: color,
dismissible: this.barrierDismissible
);
}
else {
barrier = new ModalBarrier(
dismissible: this.barrierDismissible
);
}
return new IgnorePointer(
ignoring: this.animation.status == AnimationStatus.reverse ||
this.animation.status == AnimationStatus.dismissed,
child: barrier
);
}
Widget _modalScopeCache;
Widget _buildModalScope(BuildContext context) {
return this._modalScopeCache = this._modalScopeCache ?? new _ModalScope(
key: this._scopeKey,
route: this
// _ModalScope calls buildTransitions() and buildChild(), defined above
);
}
public override ICollection<OverlayEntry> createOverlayEntries() {
this._modalBarrier = new OverlayEntry(builder: this._buildModalBarrier);
var content = new OverlayEntry(
builder: this._buildModalScope, maintainState: this.maintainState
);
return new List<OverlayEntry> {this._modalBarrier, content};
}
public override string ToString() {
return $"{this.GetType()}({this.settings}, animation: {this._animation})";
}
}
public abstract class PopupRoute : ModalRoute {
protected PopupRoute(
RouteSettings settings = null
) : base(settings: settings) {
}
public override bool opaque {
get { return false; }
}
public override bool maintainState {
get { return true; }
}
}
public class RouteObserve<R> : NavigatorObserver where R : Route {
readonly Dictionary<R, HashSet<RouteAware>> _listeners = new Dictionary<R, HashSet<RouteAware>>();
public void subscribe(RouteAware routeAware, R route) {
D.assert(routeAware != null);
D.assert(route != null);
HashSet<RouteAware> subscribers = this._listeners.putIfAbsent(route, () => new HashSet<RouteAware>());
if (subscribers.Add(routeAware)) {
routeAware.didPush();
}
}
public void unsubscribe(RouteAware routeAware) {
D.assert(routeAware != null);
foreach (R route in this._listeners.Keys) {
HashSet<RouteAware> subscribers = this._listeners[route];
subscribers?.Remove(routeAware);
}
}
public override void didPop(Route route, Route previousRoute) {
if (route is R && previousRoute is R) {
var previousSubscribers = this._listeners.getOrDefault((R) previousRoute);
if (previousSubscribers != null) {
foreach (RouteAware routeAware in previousSubscribers) {
routeAware.didPopNext();
}
}
var subscribers = this._listeners.getOrDefault((R) route);
if (subscribers != null) {
foreach (RouteAware routeAware in subscribers) {
routeAware.didPop();
}
}
}
}
public override void didPush(Route route, Route previousRoute) {
if (route is R && previousRoute is R) {
var previousSubscribers = this._listeners.getOrDefault((R) previousRoute);
if (previousSubscribers != null) {
foreach (RouteAware routeAware in previousSubscribers) {
routeAware.didPushNext();
}
}
}
}
}
public interface RouteAware {
void didPopNext();
void didPush();
void didPop();
void didPushNext();
}
class _DialogRoute : PopupRoute {
internal _DialogRoute(RoutePageBuilder pageBuilder = null, bool barrierDismissible = true,
Color barrierColor = null,
TimeSpan? transitionDuration = null,
RouteTransitionsBuilder transitionBuilder = null,
RouteSettings setting = null) : base(settings: setting) {
this._pageBuilder = pageBuilder;
this.barrierDismissible = barrierDismissible;
this.barrierColor = barrierColor ?? new Color(0x80000000);
this.transitionDuration = transitionDuration ?? TimeSpan.FromMilliseconds(200);
this._transitionBuilder = transitionBuilder;
}
readonly RoutePageBuilder _pageBuilder;
public override bool barrierDismissible { get; }
public override Color barrierColor { get; }
public override TimeSpan transitionDuration { get; }
readonly RouteTransitionsBuilder _transitionBuilder;
public override Widget buildPage(BuildContext context, Animation<float> animation,
Animation<float> secondaryAnimation) {
return this._pageBuilder(context, animation, secondaryAnimation);
}
public override Widget buildTransitions(BuildContext context, Animation<float> animation,
Animation<float> secondaryAnimation, Widget child) {
if (this._transitionBuilder == null) {
return new FadeTransition(
opacity: new CurvedAnimation(
parent: animation,
curve: Curves.linear
),
child: child);
}
return this._transitionBuilder(context, animation, secondaryAnimation, child);
}
}
public static class DialogUtils {
public static IPromise<object> showGeneralDialog(
BuildContext context = null,
RoutePageBuilder pageBuilder = null,
bool barrierDismissible = false,
Color barrierColor = null,
TimeSpan? transitionDuration = null,
RouteTransitionsBuilder transitionBuilder = null
) {
D.assert(pageBuilder != null);
return Navigator.of(context, rootNavigator: true).push(new _DialogRoute(
pageBuilder: pageBuilder,
barrierDismissible: barrierDismissible,
barrierColor: barrierColor,
transitionDuration: transitionDuration,
transitionBuilder: transitionBuilder
));
}
}
public delegate Widget RoutePageBuilder(BuildContext context, Animation<float> animation,
Animation<float> secondaryAnimation);
public delegate Widget RouteTransitionsBuilder(BuildContext context, Animation<float> animation,
Animation<float> secondaryAnimation, Widget child);
}