浏览代码
Merge branch 'master' into 'master'
Merge branch 'master' into 'master'
Master See merge request upm-packages/ui-widgets/com.unity.uiwidgets!17/main
Shenhua Gu
6 年前
当前提交
8014ae96
共有 33 个文件被更改,包括 3900 次插入 和 49 次删除
-
9Runtime/animation/tween.cs
-
38Runtime/engine/WidgetCanvas.cs
-
16Runtime/foundation/basic_types.cs
-
2Runtime/gestures/binding.cs
-
6Runtime/promise/Promise.cs
-
299Runtime/rendering/object.mixin.gen.cs
-
2Runtime/rendering/object.mixin.njk
-
275Runtime/rendering/proxy_box.cs
-
6Runtime/rendering/proxy_box.mixin.gen.cs
-
6Runtime/rendering/proxy_box.mixin.njk
-
223Runtime/widgets/app.cs
-
87Runtime/widgets/basic.cs
-
5Runtime/widgets/binding.cs
-
2Runtime/widgets/focus_manager.cs
-
6Runtime/widgets/page_storage.cs
-
9Runtime/widgets/widget_inspector.cs
-
7Samples/UIWidgetSample/TextInputCanvas.cs
-
46Runtime/widgets/modal_barrier.cs
-
11Runtime/widgets/modal_barrier.cs.meta
-
786Runtime/widgets/navigator.cs
-
11Runtime/widgets/navigator.cs.meta
-
397Runtime/widgets/overlay.cs
-
11Runtime/widgets/overlay.cs.meta
-
88Runtime/widgets/pages.cs
-
11Runtime/widgets/pages.cs.meta
-
773Runtime/widgets/routes.cs
-
11Runtime/widgets/routes.cs.meta
-
131Runtime/widgets/transitions.cs
-
11Runtime/widgets/transitions.cs.meta
-
502Samples/UIWidgetSample/Navigation.unity
-
7Samples/UIWidgetSample/Navigation.unity.meta
-
144Samples/UIWidgetSample/NavigationCanvas.cs
-
11Samples/UIWidgetSample/NavigationCanvas.cs.meta
|
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public class ModalBarrier : StatelessWidget { |
|||
public readonly Color color; |
|||
public readonly bool dismissible; |
|||
|
|||
public ModalBarrier(Key key = null, Color color = null, bool dismissible = true) : base(key) { |
|||
this.color = color; |
|||
this.dismissible = dismissible; |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new GestureDetector( |
|||
onTapDown: details => { |
|||
if (this.dismissible) |
|||
Navigator.maybePop(context); |
|||
}, |
|||
behavior: HitTestBehavior.opaque, |
|||
child: new ConstrainedBox( |
|||
constraints: BoxConstraints.expand(), |
|||
child: this.color == null ? null : new DecoratedBox(decoration: new BoxDecoration(this.color)) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
public class AnimatedModalBarrier : AnimatedWidget { |
|||
public readonly bool dismissible; |
|||
|
|||
public AnimatedModalBarrier(Key key = null, Animation<Color> color = null, |
|||
bool dismissible = true) : base(key, color) { |
|||
this.dismissible = dismissible; |
|||
} |
|||
|
|||
public Animation<Color> color => (Animation<Color>) this.listenable; |
|||
|
|||
protected internal override Widget build(BuildContext context) { |
|||
return new ModalBarrier(color: this.color?.value, dismissible: this.dismissible); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 57f0a2412eaef4c9496cca93496c44e4 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using RSG; |
|||
using RSG.Promises; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.scheduler; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public delegate Route RouteFactory(RouteSettings settings); |
|||
|
|||
public delegate bool RoutePredicate(Route route); |
|||
|
|||
public delegate IPromise<bool> WillPopCallback(); |
|||
|
|||
public enum RoutePopDisposition { |
|||
pop, |
|||
doNotPop, |
|||
bubble |
|||
} |
|||
|
|||
public abstract class Route { |
|||
public readonly RouteSettings settings; |
|||
|
|||
internal NavigatorState _navigator; |
|||
|
|||
public Route(RouteSettings settings = null) { |
|||
this.settings = settings ?? new RouteSettings(); |
|||
} |
|||
|
|||
public NavigatorState navigator => this._navigator; |
|||
|
|||
public virtual List<OverlayEntry> overlayEntries => new List<OverlayEntry>(); |
|||
|
|||
public virtual bool willHandlePopInternally => false; |
|||
|
|||
public object currentResult => default; |
|||
|
|||
public Promise<object> popped { get; } = new Promise<object>(); |
|||
|
|||
public bool isCurrent => this._navigator != null && this._navigator._history.last() == this; |
|||
|
|||
public bool isFirst => this._navigator != null && this._navigator._history.first() == this; |
|||
|
|||
public bool isActive => this._navigator != null && this._navigator._history.Contains(this); |
|||
|
|||
protected internal virtual void install(OverlayEntry insertionPoint) { |
|||
} |
|||
|
|||
protected internal virtual TickerFuture didPush() { |
|||
return TickerFutureImpl.complete(); |
|||
} |
|||
|
|||
protected internal virtual void didReplace(Route oldRoute) { |
|||
} |
|||
|
|||
public virtual IPromise<RoutePopDisposition> willPop() { |
|||
return Promise<RoutePopDisposition>.Resolved(this.isFirst |
|||
? RoutePopDisposition.bubble |
|||
: RoutePopDisposition.pop); |
|||
} |
|||
|
|||
protected internal virtual bool didPop(object result) { |
|||
this.didComplete(result); |
|||
return true; |
|||
} |
|||
|
|||
protected internal virtual void didComplete(object result) { |
|||
this.popped.Resolve(result); |
|||
} |
|||
|
|||
protected internal virtual void didPopNext(Route nextRoute) { |
|||
} |
|||
|
|||
protected internal virtual void didChangeNext(Route nextRoute) { |
|||
} |
|||
|
|||
protected internal virtual void didChangePrevious(Route previousRoute) { |
|||
} |
|||
|
|||
protected internal virtual void changedInternalState() { |
|||
} |
|||
|
|||
protected internal virtual void changedExternalState() { |
|||
} |
|||
|
|||
protected internal virtual void dispose() { |
|||
this._navigator = null; |
|||
} |
|||
} |
|||
|
|||
public class RouteSettings { |
|||
public readonly bool isInitialRoute; |
|||
|
|||
public readonly string name; |
|||
|
|||
public RouteSettings(string name = null, bool isInitialRoute = false) { |
|||
this.name = name; |
|||
this.isInitialRoute = isInitialRoute; |
|||
} |
|||
|
|||
RouteSettings copyWith(string name = null, bool? isInitialRoute = null) { |
|||
return new RouteSettings( |
|||
name ?? this.name, |
|||
isInitialRoute ?? this.isInitialRoute |
|||
); |
|||
} |
|||
|
|||
|
|||
public override string ToString() { |
|||
return $"\"{this.name}\""; |
|||
} |
|||
} |
|||
|
|||
public class NavigatorObserver { |
|||
internal NavigatorState _navigator; |
|||
|
|||
public NavigatorState navigator => this._navigator; |
|||
|
|||
public virtual void didPush(Route route, Route previousRoute) { |
|||
} |
|||
|
|||
public virtual void didPop(Route route, Route previousRoute) { |
|||
} |
|||
|
|||
public virtual void didRemove(Route route, Route previousRoute) { |
|||
} |
|||
|
|||
public virtual void didReplace(Route newRoute = null, Route oldRoute = null) { |
|||
} |
|||
|
|||
public virtual void didStartUserGesture(Route route, Route previousRoute) { |
|||
} |
|||
|
|||
public virtual void didStopUserGesture() { |
|||
} |
|||
} |
|||
|
|||
public class Navigator : StatefulWidget { |
|||
/// The default name for the [initialRoute].
|
|||
///
|
|||
/// See also:
|
|||
///
|
|||
/// * [dart:ui.Window.defaultRouteName], which reflects the route that the
|
|||
/// application was started with.
|
|||
public static string defaultRouteName = "/"; |
|||
|
|||
public readonly string initialRoute; |
|||
|
|||
public readonly List<NavigatorObserver> observers; |
|||
|
|||
public readonly RouteFactory onGenerateRoute; |
|||
|
|||
public readonly RouteFactory onUnknownRoute; |
|||
|
|||
public Navigator(Key key = null, string initialRoute = null, |
|||
RouteFactory onGenerateRoute = null, RouteFactory onUnknownRoute = null, |
|||
List<NavigatorObserver> observers = null) : base(key) { |
|||
D.assert(onGenerateRoute != null); |
|||
this.initialRoute = initialRoute; |
|||
this.onUnknownRoute = onUnknownRoute; |
|||
this.onGenerateRoute = onGenerateRoute; |
|||
this.observers = observers ?? new List<NavigatorObserver>(); |
|||
} |
|||
|
|||
public static IPromise<object> pushName(BuildContext context, string routeName) { |
|||
return of(context).pushNamed(routeName); |
|||
} |
|||
|
|||
public static IPromise<object> pushReplacementNamed(BuildContext context, string routeName, |
|||
object result = null) { |
|||
return of(context).pushReplacementNamed(routeName, result); |
|||
} |
|||
|
|||
public static IPromise<object> popAndPushNamed(BuildContext context, string routeName, object result = null) { |
|||
return of(context).popAndPushNamed(routeName, result); |
|||
} |
|||
|
|||
public static IPromise<object> pushNamedAndRemoveUntil(BuildContext context, string newRouteName, |
|||
RoutePredicate predicate) { |
|||
return of(context).pushNamedAndRemoveUntil(newRouteName, predicate); |
|||
} |
|||
|
|||
public static IPromise<object> push(BuildContext context, Route route) { |
|||
return of(context).push(route); |
|||
} |
|||
|
|||
public static IPromise<object> pushReplacement(BuildContext context, Route newRoute, object result = null) { |
|||
return of(context).pushReplacement(newRoute, result); |
|||
} |
|||
|
|||
public static IPromise<object> pushAndRemoveUntil(BuildContext context, Route newRoute, |
|||
RoutePredicate predicate) { |
|||
return of(context).pushAndRemoveUntil(newRoute, predicate); |
|||
} |
|||
|
|||
public static void replace(BuildContext context, Route oldRoute, Route newRoute) { |
|||
of(context).replace(oldRoute, newRoute); |
|||
} |
|||
|
|||
public static void replaceRouteBelow(BuildContext context, Route anchorRoute = null, Route newRoute = null) { |
|||
of(context).replaceRouteBelow(anchorRoute, newRoute); |
|||
} |
|||
|
|||
|
|||
public static IPromise<bool> maybePop(BuildContext context, object result = null) { |
|||
return of(context).maybePop(result); |
|||
} |
|||
|
|||
public static bool pop(BuildContext context, object result = null) { |
|||
return of(context).pop(result); |
|||
} |
|||
|
|||
|
|||
public static void popUntil(BuildContext context, RoutePredicate predicate) { |
|||
of(context).popUntil(predicate); |
|||
} |
|||
|
|||
public static void removeRoute(BuildContext context, Route route) { |
|||
of(context).removeRoute(route); |
|||
} |
|||
|
|||
|
|||
static void removeRouteBelow(BuildContext context, Route anchorRoute) { |
|||
of(context).removeRouteBelow(anchorRoute); |
|||
} |
|||
|
|||
public static NavigatorState of( |
|||
BuildContext context, |
|||
bool rootNavigator = false, |
|||
bool nullOk = false |
|||
) { |
|||
var navigator = rootNavigator |
|||
? (NavigatorState) context.rootAncestorStateOfType(new TypeMatcher<NavigatorState>()) |
|||
: (NavigatorState) context.ancestorStateOfType(new TypeMatcher<NavigatorState>()); |
|||
D.assert(() => { |
|||
if (navigator == null && !nullOk) |
|||
throw new UIWidgetsError( |
|||
"Navigator operation requested with a context that does not include a Navigator.\n" + |
|||
"The context used to push or pop routes from the Navigator must be that of a " + |
|||
"widget that is a descendant of a Navigator widget." |
|||
); |
|||
|
|||
return true; |
|||
}); |
|||
return navigator; |
|||
} |
|||
|
|||
public override State createState() { |
|||
return new NavigatorState(); |
|||
} |
|||
} |
|||
|
|||
public class NavigatorState : TickerProviderStateMixin<Navigator> { |
|||
internal readonly List<Route> _history = new List<Route>(); |
|||
readonly GlobalKey<OverlayState> _overlayKey = new LabeledGlobalKey<OverlayState>(); |
|||
readonly HashSet<Route> _poppedRoutes = new HashSet<Route>(); |
|||
public readonly FocusScopeNode focusScopeNode = new FocusScopeNode(); |
|||
|
|||
readonly HashSet<int> _activePointers = new HashSet<int>(); |
|||
|
|||
bool _debugLocked; |
|||
readonly List<OverlayEntry> _initialOverlayEntries = new List<OverlayEntry>(); |
|||
|
|||
int _userGesturesInProgress; |
|||
|
|||
public OverlayState overlay => this._overlayKey.currentState; |
|||
|
|||
OverlayEntry _currentOverlayEntry { |
|||
get { |
|||
var route = this._history.FindLast(r => r.overlayEntries.isNotEmpty()); |
|||
return route?.overlayEntries.last(); |
|||
} |
|||
} |
|||
|
|||
public bool userGestureInProgress => this._userGesturesInProgress > 0; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
foreach (var observer in this.widget.observers) { |
|||
D.assert(observer.navigator == null); |
|||
observer._navigator = this; |
|||
} |
|||
|
|||
var initialRouteName = this.widget.initialRoute ?? Navigator.defaultRouteName; |
|||
if (initialRouteName.StartsWith("/") && initialRouteName.Length > 1) { |
|||
initialRouteName = initialRouteName.Substring(1); |
|||
D.assert(Navigator.defaultRouteName == "/"); |
|||
var plannedInitialRouteNames = new List<string> { |
|||
Navigator.defaultRouteName |
|||
}; |
|||
var plannedInitialRoutes = new List<Route> { |
|||
this._routeNamed(Navigator.defaultRouteName, true) |
|||
}; |
|||
|
|||
var routeParts = initialRouteName.Split('/'); |
|||
if (initialRouteName.isNotEmpty()) { |
|||
var routeName = ""; |
|||
foreach (var part in routeParts) { |
|||
routeName += $"/{part}"; |
|||
plannedInitialRouteNames.Add(routeName); |
|||
plannedInitialRoutes.Add(this._routeNamed(routeName, true)); |
|||
} |
|||
} |
|||
|
|||
if (plannedInitialRoutes.Contains(null)) { |
|||
D.assert(() => { |
|||
UIWidgetsError.reportError(new UIWidgetsErrorDetails( |
|||
new Exception( |
|||
"Could not navigate to initial route.\n" + |
|||
$"The requested route name was: \"{initialRouteName}\n" +
|
|||
"The following routes were therefore attempted:\n" + |
|||
$" * {string.Join("\n * ", plannedInitialRouteNames)}\n" + |
|||
"This resulted in the following objects:\n" + |
|||
$" * {string.Join("\n * ", plannedInitialRoutes)}\n" + |
|||
"One or more of those objects was null, and therefore the initial route specified will be " + |
|||
$"ignored and \"{Navigator.defaultRouteName}\" will be used instead."))); |
|||
return true; |
|||
}); |
|||
this.push(this._routeNamed(Navigator.defaultRouteName)); |
|||
} |
|||
else { |
|||
plannedInitialRoutes.Each(route => { this.push(route); }); |
|||
} |
|||
} |
|||
else { |
|||
Route route = null; |
|||
if (initialRouteName != Navigator.defaultRouteName) route = this._routeNamed(initialRouteName, true); |
|||
|
|||
route = route ?? this._routeNamed(Navigator.defaultRouteName); |
|||
this.push(route); |
|||
} |
|||
|
|||
foreach (var route in this._history) this._initialOverlayEntries.AddRange(route.overlayEntries); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget oldWidget) { |
|||
base.didUpdateWidget(oldWidget); |
|||
if (((Navigator) oldWidget).observers != this.widget.observers) { |
|||
foreach (var observer in ((Navigator) oldWidget).observers) observer._navigator = null; |
|||
|
|||
foreach (var observer in this.widget.observers) { |
|||
D.assert(observer.navigator == null); |
|||
observer._navigator = this; |
|||
} |
|||
} |
|||
|
|||
foreach (var route in this._history) route.changedExternalState(); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
D.assert(!this._debugLocked); |
|||
D.assert(() => { |
|||
this._debugLocked = true; |
|||
return true; |
|||
}); |
|||
foreach (var observer in this.widget.observers) observer._navigator = null; |
|||
|
|||
var doomed = this._poppedRoutes.ToList(); |
|||
doomed.AddRange(this._history); |
|||
foreach (var route in doomed) route.dispose(); |
|||
|
|||
this._poppedRoutes.Clear(); |
|||
this._history.Clear(); |
|||
this.focusScopeNode.detach(); |
|||
base.dispose(); |
|||
|
|||
D.assert(() => { |
|||
this._debugLocked = false; |
|||
return true; |
|||
}); |
|||
} |
|||
|
|||
Route _routeNamed(string name, bool allowNull = false) { |
|||
D.assert(!this._debugLocked); |
|||
D.assert(name != null); |
|||
var settings = new RouteSettings(name, this._history.isEmpty()); |
|||
var route = this.widget.onGenerateRoute(settings); |
|||
if (route == null && !allowNull) { |
|||
D.assert(() => { |
|||
if (this.widget.onUnknownRoute == null) |
|||
throw new UIWidgetsError( |
|||
"If a Navigator has no onUnknownRoute, then its onGenerateRoute must never return null.\n" + |
|||
$"When trying to build the route \"{name}\", onGenerateRoute returned null, but there was no " + |
|||
"onUnknownRoute callback specified.\n" + |
|||
"The Navigator was:\n" + |
|||
$" {this}"); |
|||
|
|||
return true; |
|||
}); |
|||
|
|||
route = this.widget.onUnknownRoute(settings); |
|||
D.assert(() => { |
|||
if (route == null) |
|||
throw new UIWidgetsError( |
|||
"A Navigator\'s onUnknownRoute returned null.\n" + |
|||
$"When trying to build the route \"{name}\", both onGenerateRoute and onUnknownRoute returned " + |
|||
"null. The onUnknownRoute callback should never return null.\n" + |
|||
"The Navigator was:\n" + |
|||
$" {this}" |
|||
); |
|||
|
|||
return true; |
|||
}); |
|||
} |
|||
|
|||
return route; |
|||
} |
|||
|
|||
public Promise<object> pushNamed(string routeName) { |
|||
return this.push(this._routeNamed(routeName)); |
|||
} |
|||
|
|||
public Promise<object> pushReplacementNamed(string routeName, object result = null) { |
|||
return this.pushReplacement(this._routeNamed(routeName), result); |
|||
} |
|||
|
|||
public Promise<object> popAndPushNamed(string routeName, object result = null) { |
|||
this.pop(result); |
|||
return this.pushNamed(routeName); |
|||
} |
|||
|
|||
public Promise<object> pushNamedAndRemoveUntil(string newRouteName, RoutePredicate predicate) { |
|||
return this.pushAndRemoveUntil(this._routeNamed(newRouteName), predicate); |
|||
} |
|||
|
|||
public Promise<object> push(Route route) { |
|||
D.assert(!this._debugLocked); |
|||
D.assert(() => { |
|||
this._debugLocked = true; |
|||
return true; |
|||
}); |
|||
D.assert(route != null); |
|||
D.assert(route._navigator == null); |
|||
var oldRoute = this._history.isNotEmpty() ? this._history.last() : null; |
|||
route._navigator = this; |
|||
route.install(this._currentOverlayEntry); |
|||
this._history.Add(route); |
|||
route.didPush(); |
|||
route.didChangeNext(null); |
|||
if (oldRoute != null) { |
|||
oldRoute.didChangeNext(route); |
|||
route.didChangePrevious(oldRoute); |
|||
} |
|||
|
|||
foreach (var observer in this.widget.observers) observer.didPush(route, oldRoute); |
|||
|
|||
D.assert(() => { |
|||
this._debugLocked = false; |
|||
return true; |
|||
}); |
|||
this._afterNavigation(); |
|||
return route.popped; |
|||
} |
|||
|
|||
void _afterNavigation() { |
|||
} |
|||
|
|||
public Promise<object> pushReplacement(Route newRoute, object result = null) { |
|||
D.assert(!this._debugLocked); |
|||
D.assert(() => { |
|||
this._debugLocked = true; |
|||
return true; |
|||
}); |
|||
var oldRoute = this._history.last(); |
|||
D.assert(oldRoute != null && oldRoute._navigator == this); |
|||
D.assert(oldRoute.overlayEntries.isNotEmpty()); |
|||
D.assert(newRoute._navigator == null); |
|||
D.assert(newRoute.overlayEntries.isEmpty()); |
|||
var index = this._history.Count - 1; |
|||
D.assert(index >= 0); |
|||
D.assert(this._history.IndexOf(oldRoute) == index); |
|||
newRoute._navigator = this; |
|||
newRoute.install(this._currentOverlayEntry); |
|||
this._history[index] = newRoute; |
|||
newRoute.didPush().whenCompleteOrCancel(() => { |
|||
// The old route's exit is not animated. We're assuming that the
|
|||
// new route completely obscures the old one.
|
|||
if (this.mounted) { |
|||
oldRoute.didComplete(result ?? oldRoute.currentResult); |
|||
oldRoute.dispose(); |
|||
} |
|||
}); |
|||
newRoute.didChangeNext(null); |
|||
if (index > 0) { |
|||
this._history[index - 1].didChangeNext(newRoute); |
|||
newRoute.didChangePrevious(this._history[index - 1]); |
|||
} |
|||
|
|||
foreach (var observer in this.widget.observers) observer.didReplace(newRoute, oldRoute); |
|||
|
|||
D.assert(() => { |
|||
this._debugLocked = false; |
|||
return true; |
|||
}); |
|||
this._afterNavigation(); |
|||
return newRoute.popped; |
|||
} |
|||
|
|||
public Promise<object> pushAndRemoveUntil(Route newRoute, RoutePredicate predicate) { |
|||
D.assert(!this._debugLocked); |
|||
D.assert(() => { |
|||
this._debugLocked = true; |
|||
return true; |
|||
}); |
|||
var removedRoutes = new List<Route>(); |
|||
while (this._history.isNotEmpty() && !predicate(this._history.last())) { |
|||
var removedRoute = this._history.last(); |
|||
this._history.RemoveAt(this._history.Count - 1); |
|||
D.assert(removedRoute != null && removedRoute._navigator == this); |
|||
D.assert(removedRoute.overlayEntries.isNotEmpty()); |
|||
removedRoutes.Add(removedRoute); |
|||
} |
|||
|
|||
D.assert(newRoute._navigator == null); |
|||
D.assert(newRoute.overlayEntries.isEmpty()); |
|||
var oldRoute = this._history.isNotEmpty() ? this._history.last() : null; |
|||
newRoute._navigator = this; |
|||
newRoute.install(this._currentOverlayEntry); |
|||
this._history.Add(newRoute); |
|||
newRoute.didPush().whenCompleteOrCancel(() => { |
|||
if (this.mounted) |
|||
foreach (var route in removedRoutes) |
|||
route.dispose(); |
|||
}); |
|||
newRoute.didChangeNext(null); |
|||
if (oldRoute != null) oldRoute.didChangeNext(newRoute); |
|||
|
|||
foreach (var observer in this.widget.observers) { |
|||
observer.didPush(newRoute, oldRoute); |
|||
foreach (var removedRoute in removedRoutes) observer.didRemove(removedRoute, oldRoute); |
|||
} |
|||
|
|||
D.assert(() => { |
|||
this._debugLocked = false; |
|||
return true; |
|||
}); |
|||
this._afterNavigation(); |
|||
return newRoute.popped; |
|||
} |
|||
|
|||
public void replace(Route oldRoute = null, Route newRoute = null) { |
|||
D.assert(!this._debugLocked); |
|||
D.assert(oldRoute != null); |
|||
D.assert(newRoute != null); |
|||
if (oldRoute == newRoute |
|||
) // ignore: unrelated_type_equality_checks, https://github.com/dart-lang/sdk/issues/32522
|
|||
return; |
|||
D.assert(() => { |
|||
this._debugLocked = true; |
|||
return true; |
|||
}); |
|||
D.assert(oldRoute._navigator == this); |
|||
D.assert(newRoute._navigator == null); |
|||
D.assert(oldRoute.overlayEntries.isNotEmpty()); |
|||
D.assert(newRoute.overlayEntries.isEmpty()); |
|||
D.assert(!this.overlay.debugIsVisible(oldRoute.overlayEntries.last())); |
|||
var index = this._history.IndexOf(oldRoute); |
|||
D.assert(index >= 0); |
|||
newRoute._navigator = this; |
|||
newRoute.install(oldRoute.overlayEntries.last()); |
|||
this._history[index] = newRoute; |
|||
newRoute.didReplace(oldRoute); |
|||
if (index + 1 < this._history.Capacity) { |
|||
newRoute.didChangeNext(this._history[index + 1]); |
|||
this._history[index + 1].didChangePrevious(newRoute); |
|||
} |
|||
else { |
|||
newRoute.didChangeNext(null); |
|||
} |
|||
|
|||
if (index > 0) { |
|||
this._history[index - 1].didChangeNext(newRoute); |
|||
newRoute.didChangePrevious(this._history[index - 1]); |
|||
} |
|||
|
|||
foreach (var observer in this.widget.observers) observer.didReplace(newRoute, oldRoute); |
|||
|
|||
oldRoute.dispose(); |
|||
D.assert(() => { |
|||
this._debugLocked = false; |
|||
return true; |
|||
}); |
|||
} |
|||
|
|||
public void replaceRouteBelow(Route anchorRoute = null, Route newRoute = null) { |
|||
D.assert(anchorRoute != null); |
|||
D.assert(anchorRoute._navigator == this); |
|||
D.assert(this._history.IndexOf(anchorRoute) > 0); |
|||
this.replace(this._history[this._history.IndexOf(anchorRoute) - 1], newRoute); |
|||
} |
|||
|
|||
public bool canPop() { |
|||
D.assert(this._history.isNotEmpty); |
|||
return this._history.Count > 1 || this._history[0].willHandlePopInternally; |
|||
} |
|||
|
|||
public IPromise<bool> maybePop(object result = null) { |
|||
var route = this._history.last(); |
|||
D.assert(route._navigator == this); |
|||
return route.willPop().Then(disposition => { |
|||
if (disposition != RoutePopDisposition.bubble && this.mounted) { |
|||
if (disposition == RoutePopDisposition.pop) this.pop(result); |
|||
return Promise<bool>.Resolved(true); |
|||
} |
|||
|
|||
return Promise<bool>.Resolved(false); |
|||
}); |
|||
} |
|||
|
|||
public bool pop(object result = null) { |
|||
D.assert(!this._debugLocked); |
|||
D.assert(() => { |
|||
this._debugLocked = true; |
|||
return true; |
|||
}); |
|||
var route = this._history.last(); |
|||
D.assert(route._navigator == this); |
|||
var debugPredictedWouldPop = false; |
|||
D.assert(() => { |
|||
debugPredictedWouldPop = !route.willHandlePopInternally; |
|||
return true; |
|||
}); |
|||
if (route.didPop(result ?? route.currentResult)) { |
|||
D.assert(debugPredictedWouldPop); |
|||
if (this._history.Count > 1) { |
|||
this._history.removeLast(); |
|||
// If route._navigator is null, the route called finalizeRoute from
|
|||
// didPop, which means the route has already been disposed and doesn't
|
|||
// need to be added to _poppedRoutes for later disposal.
|
|||
if (route._navigator != null) this._poppedRoutes.Add(route); |
|||
this._history.last().didPopNext(route); |
|||
foreach (var observer in this.widget.observers) observer.didPop(route, this._history.last()); |
|||
} |
|||
else { |
|||
D.assert(() => { |
|||
this._debugLocked = false; |
|||
return true; |
|||
}); |
|||
return false; |
|||
} |
|||
} |
|||
else { |
|||
D.assert(!debugPredictedWouldPop); |
|||
} |
|||
|
|||
D.assert(() => { |
|||
this._debugLocked = false; |
|||
return true; |
|||
}); |
|||
this._afterNavigation(); |
|||
return true; |
|||
} |
|||
|
|||
public void popUntil(RoutePredicate predicate) { |
|||
while (!predicate(this._history.last())) this.pop(); |
|||
} |
|||
|
|||
public void removeRoute(Route route) { |
|||
D.assert(route != null); |
|||
D.assert(!this._debugLocked); |
|||
D.assert(() => { |
|||
this._debugLocked = true; |
|||
return true; |
|||
}); |
|||
D.assert(route._navigator == this); |
|||
var index = this._history.IndexOf(route); |
|||
D.assert(index != -1); |
|||
var previousRoute = index > 0 ? this._history[index - 1] : null; |
|||
var nextRoute = index + 1 < this._history.Count ? this._history[index + 1] : null; |
|||
this._history.RemoveAt(index); |
|||
previousRoute?.didChangeNext(nextRoute); |
|||
nextRoute?.didChangePrevious(previousRoute); |
|||
foreach (var observer in this.widget.observers) observer.didRemove(route, previousRoute); |
|||
|
|||
route.dispose(); |
|||
D.assert(() => { |
|||
this._debugLocked = false; |
|||
return true; |
|||
}); |
|||
this._afterNavigation(); |
|||
} |
|||
|
|||
public void removeRouteBelow(Route anchorRoute) { |
|||
D.assert(!this._debugLocked); |
|||
D.assert(() => { |
|||
this._debugLocked = true; |
|||
return true; |
|||
}); |
|||
D.assert(anchorRoute._navigator == this); |
|||
var index = this._history.IndexOf(anchorRoute) - 1; |
|||
D.assert(index >= 0); |
|||
var targetRoute = this._history[index]; |
|||
D.assert(targetRoute._navigator == this); |
|||
D.assert(targetRoute.overlayEntries.isEmpty() || |
|||
!this.overlay.debugIsVisible(targetRoute.overlayEntries.last())); |
|||
this._history.RemoveAt(index); |
|||
var nextRoute = index < this._history.Count ? this._history[index] : null; |
|||
var previousRoute = index > 0 ? this._history[index - 1] : null; |
|||
if (previousRoute != null) |
|||
previousRoute.didChangeNext(nextRoute); |
|||
if (nextRoute != null) |
|||
nextRoute.didChangePrevious(previousRoute); |
|||
targetRoute.dispose(); |
|||
D.assert(() => { |
|||
this._debugLocked = false; |
|||
return true; |
|||
}); |
|||
} |
|||
|
|||
public void finalizeRoute(Route route) { |
|||
this._poppedRoutes.Remove(route); |
|||
route.dispose(); |
|||
} |
|||
|
|||
public void didStartUserGesture() { |
|||
this._userGesturesInProgress += 1; |
|||
if (this._userGesturesInProgress == 1) { |
|||
var route = this._history.last(); |
|||
var previousRoute = !route.willHandlePopInternally && this._history.Count > 1 |
|||
? this._history[this._history.Count - 2] |
|||
: null; |
|||
// Don't operate the _history list since the gesture may be cancelled.
|
|||
// In case of a back swipe, the gesture controller will call .pop() itself.
|
|||
foreach (var observer in this.widget.observers) observer.didStartUserGesture(route, previousRoute); |
|||
} |
|||
} |
|||
|
|||
public void didStopUserGesture() { |
|||
D.assert(this._userGesturesInProgress > 0); |
|||
this._userGesturesInProgress -= 1; |
|||
if (this._userGesturesInProgress == 0) |
|||
foreach (var observer in this.widget.observers) |
|||
observer.didStopUserGesture(); |
|||
} |
|||
|
|||
|
|||
void _handlePointerDown(PointerDownEvent evt) { |
|||
this._activePointers.Add(evt.pointer); |
|||
} |
|||
|
|||
void _handlePointerUpOrCancel(PointerEvent evt) { |
|||
this._activePointers.Remove(evt.pointer); |
|||
} |
|||
|
|||
void _cancelActivePointers() { |
|||
// TODO flutter issue https://github.com/flutter/flutter/issues/4770
|
|||
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle) { |
|||
// If we're between frames (SchedulerPhase.idle) then absorb any
|
|||
// subsequent pointers from this frame. The absorbing flag will be
|
|||
// reset in the next frame, see build().
|
|||
var absorber = (RenderAbsorbPointer) this._overlayKey.currentContext? |
|||
.ancestorRenderObjectOfType(new TypeMatcher<RenderAbsorbPointer>()); |
|||
this.setState(() => { |
|||
if (absorber != null) absorber.absorbing = true; |
|||
}); |
|||
} |
|||
|
|||
foreach (var activePointer in this._activePointers) WidgetsBinding.instance.cancelPointer(activePointer); |
|||
} |
|||
|
|||
|
|||
public override Widget build(BuildContext context) { |
|||
D.assert(!this._debugLocked); |
|||
D.assert(this._history.isNotEmpty()); |
|||
return new Listener( |
|||
onPointerDown: this._handlePointerDown, |
|||
onPointerUp: this._handlePointerUpOrCancel, |
|||
onPointerCancel: this._handlePointerUpOrCancel, |
|||
child: new AbsorbPointer( |
|||
absorbing: false, // it's mutated directly by _cancelActivePointers above
|
|||
child: new FocusScope( |
|||
this.focusScopeNode, |
|||
autofocus: true, |
|||
child: new Overlay( |
|||
this._overlayKey, |
|||
this._initialOverlayEntries |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 78638000dd54446508ed86d5936a4493 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.scheduler; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public class OverlayEntry { |
|||
public OverlayEntry(WidgetBuilder builder = null, bool opaque = false, bool maintainState = false) { |
|||
D.assert(builder != null); |
|||
this._opaque = opaque; |
|||
this._maintainState = maintainState; |
|||
this.builder = builder; |
|||
} |
|||
|
|||
public readonly WidgetBuilder builder; |
|||
|
|||
bool _opaque; |
|||
|
|||
public bool opaque { |
|||
get => this._opaque; |
|||
set { |
|||
if (this._opaque == value) return; |
|||
|
|||
this._opaque = value; |
|||
D.assert(this._overlay != null); |
|||
this._overlay._didChangeEntryOpacity(); |
|||
} |
|||
} |
|||
|
|||
bool _maintainState; |
|||
|
|||
public bool maintainState { |
|||
get => this._maintainState; |
|||
set { |
|||
if (this._maintainState == value) return; |
|||
|
|||
this._maintainState = value; |
|||
D.assert(this._overlay != null); |
|||
this._overlay._didChangeEntryOpacity(); |
|||
} |
|||
} |
|||
|
|||
internal OverlayState _overlay; |
|||
|
|||
internal readonly GlobalKey<_OverlayEntryState> _key = new LabeledGlobalKey<_OverlayEntryState>(); |
|||
|
|||
public void remove() { |
|||
D.assert(this._overlay != null); |
|||
OverlayState overlay = this._overlay; |
|||
this._overlay = null; |
|||
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) |
|||
SchedulerBinding.instance.addPostFrameCallback((duration) => { overlay._remove(this); }); |
|||
else |
|||
overlay._remove(this); |
|||
} |
|||
|
|||
public void markNeedsBuild() { |
|||
this._key.currentState?._markNeedsBuild(); |
|||
} |
|||
|
|||
public override string ToString() { |
|||
return $"{Diagnostics.describeIdentity(this)}(opaque: {this.opaque}; maintainState: {this.maintainState})"; |
|||
} |
|||
} |
|||
|
|||
|
|||
internal class _OverlayEntry : StatefulWidget { |
|||
internal _OverlayEntry(OverlayEntry entry) : base(key: entry._key) { |
|||
D.assert(entry != null); |
|||
this.entry = entry; |
|||
} |
|||
|
|||
public readonly OverlayEntry entry; |
|||
|
|||
public override State createState() { |
|||
return new _OverlayEntryState(); |
|||
} |
|||
} |
|||
|
|||
internal class _OverlayEntryState : State<_OverlayEntry> { |
|||
public override Widget build(BuildContext context) { |
|||
return this.widget.entry.builder(context); |
|||
} |
|||
|
|||
internal void _markNeedsBuild() { |
|||
this.setState(() => { |
|||
/* the state that changed is in the builder */ |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public class Overlay : StatefulWidget { |
|||
public Overlay(Key key = null, List<OverlayEntry> initialEntries = null) : base(key) { |
|||
D.assert(initialEntries != null); |
|||
this.initialEntries = initialEntries; |
|||
} |
|||
|
|||
public readonly List<OverlayEntry> initialEntries; |
|||
|
|||
public static OverlayState of(BuildContext context, Widget debugRequiredFor = null) { |
|||
OverlayState result = (OverlayState) context.ancestorStateOfType(new TypeMatcher<OverlayState>()); |
|||
D.assert(() => { |
|||
if (debugRequiredFor != null && result == null) { |
|||
var additional = context.widget != debugRequiredFor |
|||
? $"\nThe context from which that widget was searching for an overlay was:\n {context}" |
|||
: ""; |
|||
throw new UIWidgetsError( |
|||
"No Overlay widget found.\n" + |
|||
$"{debugRequiredFor.GetType()} widgets require an Overlay widget ancestor for correct operation.\n" + |
|||
"The most common way to add an Overlay to an application is to include a MaterialApp or Navigator widget in the runApp() call.\n" + |
|||
"The specific widget that failed to find an overlay was:\n" + |
|||
$" {debugRequiredFor}" + |
|||
$"{additional}" |
|||
); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
public override State createState() { |
|||
return new OverlayState(); |
|||
} |
|||
} |
|||
|
|||
public class OverlayState : TickerProviderStateMixin<Overlay> { |
|||
readonly List<OverlayEntry> _entries = new List<OverlayEntry>(); |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this.insertAll(this.widget.initialEntries); |
|||
} |
|||
|
|||
public void insert(OverlayEntry entry, OverlayEntry above = null) { |
|||
D.assert(entry._overlay == null); |
|||
D.assert(above == null || (above._overlay == this && this._entries.Contains(above))); |
|||
entry._overlay = this; |
|||
this.setState(() => { |
|||
int index = above == null ? this._entries.Count : this._entries.IndexOf(above) + 1; |
|||
this._entries.Insert(index, entry); |
|||
}); |
|||
} |
|||
|
|||
public void insertAll(ICollection<OverlayEntry> entries, OverlayEntry above = null) { |
|||
D.assert(above == null || (above._overlay == this && this._entries.Contains(above))); |
|||
if (entries.isEmpty()) |
|||
return; |
|||
foreach (OverlayEntry entry in entries) { |
|||
D.assert(entry._overlay == null); |
|||
entry._overlay = this; |
|||
} |
|||
|
|||
this.setState(() => { |
|||
int index = above == null ? this._entries.Count : this._entries.IndexOf(above) + 1; |
|||
this._entries.InsertRange(index, entries); |
|||
}); |
|||
} |
|||
|
|||
internal void _remove(OverlayEntry entry) { |
|||
if (this.mounted) { |
|||
this._entries.Remove(entry); |
|||
this.setState(() => { |
|||
/* entry was removed */ |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public bool debugIsVisible(OverlayEntry entry) { |
|||
bool result = false; |
|||
D.assert(this._entries.Contains(entry)); |
|||
D.assert(() => { |
|||
for (int i = this._entries.Count - 1; i > 0; i -= 1) { |
|||
// todo why not including 0?
|
|||
OverlayEntry candidate = this._entries[i]; |
|||
if (candidate == entry) { |
|||
result = true; |
|||
break; |
|||
} |
|||
|
|||
if (candidate.opaque) |
|||
break; |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
internal void _didChangeEntryOpacity() { |
|||
this.setState(() => { }); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
var onstageChildren = new List<Widget>(); |
|||
var offstageChildren = new List<Widget>(); |
|||
var onstage = true; |
|||
for (var i = this._entries.Count - 1; i >= 0; i -= 1) { |
|||
var entry = this._entries[i]; |
|||
if (onstage) { |
|||
onstageChildren.Add(new _OverlayEntry(entry)); |
|||
if (entry.opaque) onstage = false; |
|||
} |
|||
else if (entry.maintainState) { |
|||
offstageChildren.Add(new TickerMode(enabled: false, child: new _OverlayEntry(entry))); |
|||
} |
|||
} |
|||
|
|||
onstageChildren.Reverse(); |
|||
return new _Theatre( |
|||
onstage: new Stack( |
|||
fit: StackFit.expand, |
|||
children: onstageChildren |
|||
), |
|||
offstage: offstageChildren |
|||
); |
|||
} |
|||
} |
|||
|
|||
internal class _Theatre : RenderObjectWidget { |
|||
internal _Theatre(Stack onstage = null, List<Widget> offstage = null) { |
|||
D.assert(offstage != null); |
|||
D.assert(!offstage.Any((child) => child == null)); |
|||
this.onstage = onstage; |
|||
this.offstage = offstage; |
|||
} |
|||
|
|||
public readonly Stack onstage; |
|||
|
|||
public readonly List<Widget> offstage; |
|||
|
|||
public override Element createElement() { |
|||
return new _TheatreElement(this); |
|||
} |
|||
|
|||
public override RenderObject createRenderObject(BuildContext context) { |
|||
return new _RenderTheatre(); |
|||
} |
|||
} |
|||
|
|||
internal class _TheatreElement : RenderObjectElement { |
|||
public _TheatreElement(RenderObjectWidget widget) : base(widget) { |
|||
D.assert(!WidgetsD.debugChildrenHaveDuplicateKeys(widget, ((_Theatre) widget).offstage)); |
|||
} |
|||
|
|||
public new _Theatre widget => (_Theatre) base.widget; |
|||
|
|||
public new _RenderTheatre renderObject => (_RenderTheatre) base.renderObject; |
|||
|
|||
Element _onstage; |
|||
static readonly object _onstageSlot = new object(); |
|||
|
|||
List<Element> _offstage; |
|||
readonly HashSet<Element> _forgottenOffstageChildren = new HashSet<Element>(); |
|||
|
|||
|
|||
protected override void insertChildRenderObject(RenderObject child, object slot) { |
|||
D.assert(this.renderObject.debugValidateChild(child)); |
|||
if (slot == _onstageSlot) { |
|||
D.assert(child is RenderStack); |
|||
this.renderObject.child = (RenderStack) child; |
|||
} |
|||
else { |
|||
D.assert(slot == null || slot is Element); |
|||
this.renderObject.insert((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject); |
|||
} |
|||
} |
|||
|
|||
protected override void moveChildRenderObject(RenderObject child, object slot) { |
|||
if (slot == _onstageSlot) { |
|||
this.renderObject.remove((RenderBox) child); |
|||
D.assert(child is RenderStack); |
|||
this.renderObject.child = (RenderStack) child; |
|||
} |
|||
else { |
|||
D.assert(slot == null || slot is Element); |
|||
if (this.renderObject.child == child) { |
|||
this.renderObject.child = null; |
|||
this.renderObject.insert((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject); |
|||
} |
|||
else { |
|||
this.renderObject.move((RenderBox) child, after: (RenderBox) ((Element) slot)?.renderObject); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override void removeChildRenderObject(RenderObject child) { |
|||
if (this.renderObject.child == child) |
|||
this.renderObject.child = null; |
|||
else |
|||
this.renderObject.remove((RenderBox) child); |
|||
} |
|||
|
|||
public override void visitChildren(ElementVisitor visitor) { |
|||
if (this._onstage != null) |
|||
visitor(this._onstage); |
|||
foreach (var child in this._offstage) |
|||
if (!this._forgottenOffstageChildren.Contains(child)) |
|||
visitor(child); |
|||
} |
|||
|
|||
public override void debugVisitOnstageChildren(ElementVisitor visitor) { |
|||
if (this._onstage != null) |
|||
visitor(this._onstage); |
|||
} |
|||
|
|||
|
|||
protected override void forgetChild(Element child) { |
|||
if (child == this._onstage) { |
|||
this._onstage = null; |
|||
} |
|||
else { |
|||
D.assert(this._offstage.Contains(child)); |
|||
D.assert(!this._forgottenOffstageChildren.Contains(child)); |
|||
this._forgottenOffstageChildren.Add(child); |
|||
} |
|||
} |
|||
|
|||
public override void mount(Element parent, object newSlot) { |
|||
base.mount(parent, newSlot); |
|||
this._onstage = this.updateChild(this._onstage, this.widget.onstage, _onstageSlot); |
|||
this._offstage = new List<Element>(this.widget.offstage.Count); |
|||
Element previousChild = null; |
|||
for (int i = 0; i < this._offstage.Count; i += 1) { |
|||
var newChild = this.inflateWidget(this.widget.offstage[i], previousChild); |
|||
this._offstage[i] = newChild; |
|||
previousChild = newChild; |
|||
} |
|||
} |
|||
|
|||
public override void update(Widget newWidget) { |
|||
base.update(newWidget); |
|||
D.assert(Equals(this.widget, newWidget)); |
|||
this._onstage = this.updateChild(this._onstage, this.widget.onstage, _onstageSlot); |
|||
this._offstage = this.updateChildren(this._offstage, this.widget.offstage, |
|||
forgottenChildren: this._forgottenOffstageChildren); |
|||
this._forgottenOffstageChildren.Clear(); |
|||
} |
|||
} |
|||
|
|||
internal class _RenderTheatre : |
|||
ContainerRenderObjectMixinRenderProxyBoxMixinRenderObjectWithChildMixinRenderBoxRenderStack< |
|||
RenderBox, StackParentData> { |
|||
public override void setupParentData(RenderObject child) { |
|||
if (!(child.parentData is StackParentData)) |
|||
child.parentData = new StackParentData(); |
|||
} |
|||
|
|||
public override void redepthChildren() { |
|||
if (this.child != null) this.redepthChild(this.child); |
|||
base.redepthChildren(); |
|||
} |
|||
|
|||
public override void visitChildren(RenderObjectVisitor visitor) { |
|||
if (this.child != null) |
|||
visitor(this.child); |
|||
base.visitChildren(visitor); |
|||
} |
|||
|
|||
public override List<DiagnosticsNode> debugDescribeChildren() { |
|||
var children = new List<DiagnosticsNode>(); |
|||
|
|||
if (this.child != null) |
|||
children.Add(this.child.toDiagnosticsNode(name: "onstage")); |
|||
|
|||
if (this.firstChild != null) { |
|||
var child = this.firstChild; |
|||
|
|||
int count = 1; |
|||
while (true) { |
|||
children.Add( |
|||
child.toDiagnosticsNode( |
|||
name: $"offstage {count}", |
|||
style: DiagnosticsTreeStyle.offstage |
|||
) |
|||
); |
|||
if (child == this.lastChild) |
|||
break; |
|||
var childParentData = (StackParentData) child.parentData; |
|||
child = childParentData.nextSibling; |
|||
count += 1; |
|||
} |
|||
} |
|||
else { |
|||
children.Add( |
|||
DiagnosticsNode.message( |
|||
"no offstage children", |
|||
style: DiagnosticsTreeStyle.offstage |
|||
) |
|||
); |
|||
} |
|||
|
|||
return children; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b92d0bc4e5fb643c1beee12417aec777 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public abstract class PageRoute : ModalRoute { |
|||
public readonly bool fullscreenDialog; |
|||
|
|||
public PageRoute(RouteSettings settings, bool fullscreenDialog = false) : base(settings) { |
|||
this.fullscreenDialog = fullscreenDialog; |
|||
} |
|||
|
|||
public override bool opaque => false; |
|||
|
|||
public override bool barrierDismissible => false; |
|||
|
|||
public override bool canTransitionTo(TransitionRoute nextRoute) { |
|||
return nextRoute is PageRoute; |
|||
} |
|||
|
|||
public override bool canTransitionFrom(TransitionRoute previousRoute) { |
|||
return previousRoute is PageRoute; |
|||
} |
|||
|
|||
public override AnimationController createAnimationController() { |
|||
var controller = base.createAnimationController(); |
|||
if (this.settings.isInitialRoute) |
|||
controller.setValue(1.0); |
|||
return controller; |
|||
} |
|||
} |
|||
|
|||
public class PageRouteBuilder : PageRoute { |
|||
public readonly RoutePageBuilder pageBuilder; |
|||
|
|||
public readonly RouteTransitionsBuilder transitionsBuilder; |
|||
|
|||
public PageRouteBuilder( |
|||
RouteSettings settings = null, |
|||
RoutePageBuilder pageBuilder = null, |
|||
RouteTransitionsBuilder transitionsBuilder = null, |
|||
TimeSpan? transitionDuration = null, |
|||
bool opaque = true, |
|||
bool barrierDismissible = false, |
|||
Color barrierColor = null, |
|||
string barrierLabel = null, |
|||
bool maintainState = true |
|||
) : base(settings) { |
|||
D.assert(pageBuilder != null); |
|||
this.opaque = opaque; |
|||
this.pageBuilder = pageBuilder; |
|||
this.transitionsBuilder = transitionsBuilder ?? this._defaultTransitionsBuilder; |
|||
this.transitionDuration = transitionDuration ?? TimeSpan.FromMilliseconds(300); |
|||
this.barrierColor = barrierColor; |
|||
this.maintainState = maintainState; |
|||
this.barrierLabel = barrierLabel; |
|||
this.barrierDismissible = barrierDismissible; |
|||
} |
|||
|
|||
public override TimeSpan transitionDuration { get; } |
|||
|
|||
public override bool opaque { get; } |
|||
|
|||
public override bool barrierDismissible { get; } |
|||
|
|||
public override Color barrierColor { get; } |
|||
|
|||
public override string barrierLabel { get; } |
|||
|
|||
public override bool maintainState { get; } |
|||
|
|||
Widget _defaultTransitionsBuilder(BuildContext context, Animation<double> |
|||
animation, Animation<double> secondaryAnimation, Widget child) { |
|||
return child; |
|||
} |
|||
|
|||
public override Widget buildPage(BuildContext context, Animation<double> animation, |
|||
Animation<double> secondaryAnimation) { |
|||
return this.pageBuilder(context, animation, secondaryAnimation); |
|||
} |
|||
|
|||
public override Widget buildTransitions(BuildContext context, Animation<double> animation, |
|||
Animation<double> secondaryAnimation, Widget child) { |
|||
return this.transitionsBuilder(context, animation, secondaryAnimation, child); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b42abffbabbf94d6c92274cfbc74eec0 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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 => this._overlayEntries; |
|||
|
|||
protected virtual bool finishedWhenPopped => 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 => this._transitionCompleter; |
|||
|
|||
internal readonly Promise<object> _transitionCompleter = new Promise<object>(); |
|||
|
|||
public virtual TimeSpan transitionDuration { get; } |
|||
|
|||
public virtual bool opaque { get; } |
|||
|
|||
|
|||
protected override bool finishedWhenPopped => this.controller.status == AnimationStatus.dismissed; |
|||
|
|||
public virtual Animation<double> animation => this._animation; |
|||
|
|||
internal Animation<double> _animation; |
|||
|
|||
public AnimationController controller => 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<double> 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: |
|||
D.assert(!this.overlayEntries.first().opaque); |
|||
// 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<double> secondaryAnimation => 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<double> 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 => $"{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 => this._localHistory != null && this._localHistory.isNotEmpty(); |
|||
public Route route => 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<double> animation, |
|||
Animation<double> secondaryAnimation); |
|||
|
|||
public virtual Widget buildTransitions( |
|||
BuildContext context, |
|||
Animation<double> animation, |
|||
Animation<double> 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 string barrierLabel { get; } |
|||
|
|||
public virtual bool maintainState { get; } |
|||
|
|||
public bool offstage { |
|||
get => 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 => this._subtreeKey.currentContext; |
|||
|
|||
public override Animation<double> animation => this._animationProxy; |
|||
ProxyAnimation _animationProxy; |
|||
|
|||
public override Animation<double> secondaryAnimation => 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 => 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 => !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<double> _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})"; |
|||
} |
|||
} |
|||
|
|||
internal abstract class PopupRoute : ModalRoute { |
|||
protected PopupRoute( |
|||
RouteSettings settings = null |
|||
) : base(settings: settings) { |
|||
} |
|||
|
|||
public override bool opaque => false; |
|||
|
|||
public override bool maintainState => 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(); |
|||
} |
|||
|
|||
internal class _DialogRoute : PopupRoute { |
|||
internal _DialogRoute(RoutePageBuilder pageBuilder = null, bool barrierDismissible = true, |
|||
string barrierLabel = null, |
|||
Color barrierColor = null, |
|||
TimeSpan? transitionDuration = null, |
|||
RouteTransitionsBuilder transitionBuilder = null, |
|||
RouteSettings setting = null) : base(settings: setting) { |
|||
this._pageBuilder = pageBuilder; |
|||
this.barrierDismissible = barrierDismissible; |
|||
this.barrierLabel = barrierLabel; |
|||
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 string barrierLabel { get; } |
|||
|
|||
public override Color barrierColor { get; } |
|||
|
|||
public override TimeSpan transitionDuration { get; } |
|||
|
|||
readonly RouteTransitionsBuilder _transitionBuilder; |
|||
|
|||
public override Widget buildPage(BuildContext context, Animation<double> animation, |
|||
Animation<double> secondaryAnimation) { |
|||
return this._pageBuilder(context, animation, secondaryAnimation); |
|||
} |
|||
|
|||
public override Widget buildTransitions(BuildContext context, Animation<double> animation, |
|||
Animation<double> 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 Promise<object> showGeneralDialog( |
|||
BuildContext context = null, |
|||
RoutePageBuilder pageBuilder = null, |
|||
bool barrierDismissible = false, |
|||
string barrierLabel = null, |
|||
Color barrierColor = null, |
|||
TimeSpan? transitionDuration = null, |
|||
RouteTransitionsBuilder transitionBuilder = null |
|||
) { |
|||
D.assert(pageBuilder != null); |
|||
D.assert(!barrierDismissible || barrierLabel != null); |
|||
return Navigator.of(context, rootNavigator: true).push(new _DialogRoute( |
|||
pageBuilder: pageBuilder, |
|||
barrierDismissible: barrierDismissible, |
|||
barrierLabel: barrierLabel, |
|||
barrierColor: barrierColor, |
|||
transitionDuration: transitionDuration, |
|||
transitionBuilder: transitionBuilder |
|||
)); |
|||
} |
|||
} |
|||
|
|||
public delegate Widget RoutePageBuilder(BuildContext context, Animation<double> animation, |
|||
Animation<double> secondaryAnimation); |
|||
|
|||
public delegate Widget RouteTransitionsBuilder(BuildContext context, Animation<double> animation, |
|||
Animation<double> secondaryAnimation, Widget child); |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 3b5bef8a47dcb49babf3a770d5a3420d |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public abstract class AnimatedWidget : StatefulWidget { |
|||
public readonly Listenable listenable; |
|||
|
|||
protected AnimatedWidget(Key key = null, Listenable listenable = null) : base(key) { |
|||
D.assert(listenable != null); |
|||
this.listenable = listenable; |
|||
} |
|||
|
|||
protected internal abstract Widget build(BuildContext context); |
|||
|
|||
public override State createState() { |
|||
return new _AnimatedState(); |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
properties.add(new DiagnosticsProperty<Listenable>("animation", this.listenable)); |
|||
} |
|||
} |
|||
|
|||
public class _AnimatedState : State<AnimatedWidget> { |
|||
public override void initState() { |
|||
base.initState(); |
|||
this.widget.listenable.addListener(this._handleChange); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget oldWidget) { |
|||
base.didUpdateWidget(oldWidget); |
|||
if (this.widget.listenable != ((AnimatedWidget) oldWidget).listenable) { |
|||
((AnimatedWidget) oldWidget).listenable.removeListener(this._handleChange); |
|||
this.widget.listenable.addListener(this._handleChange); |
|||
} |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this.widget.listenable.removeListener(this._handleChange); |
|||
base.dispose(); |
|||
} |
|||
|
|||
void _handleChange() { |
|||
this.setState(() => { |
|||
// The listenable's state is our build state, and it changed already.
|
|||
}); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return this.widget.build(context); |
|||
} |
|||
} |
|||
|
|||
public class SlideTransition : AnimatedWidget { |
|||
public SlideTransition(Key key = null, |
|||
Animation<Offset> position = null, |
|||
bool transformHitTests = true, |
|||
TextDirection? textDirection = null, |
|||
Widget child = null) : base(key: key, listenable: position) { |
|||
D.assert(position != null); |
|||
this.transformHitTests = transformHitTests; |
|||
this.textDirection = textDirection; |
|||
this.child = child; |
|||
} |
|||
|
|||
public Animation<Offset> position => (Animation<Offset>) this.listenable; |
|||
|
|||
public readonly TextDirection? textDirection; |
|||
|
|||
public readonly bool transformHitTests; |
|||
|
|||
public readonly Widget child; |
|||
|
|||
protected internal override Widget build(BuildContext context) { |
|||
var offset = this.position.value; |
|||
if (this.textDirection == TextDirection.rtl) |
|||
offset = new Offset(-offset.dx, offset.dy); |
|||
return new FractionalTranslation( |
|||
translation: offset, |
|||
transformHitTests: this.transformHitTests, |
|||
child: this.child |
|||
); |
|||
} |
|||
} |
|||
|
|||
public class FadeTransition : SingleChildRenderObjectWidget { |
|||
public FadeTransition(Key key = null, Animation<double> opacity = null, |
|||
Widget child = null) : base(key: key, child: child) { |
|||
this.opacity = opacity; |
|||
} |
|||
|
|||
public readonly Animation<double> opacity; |
|||
|
|||
public override RenderObject createRenderObject(BuildContext context) { |
|||
return new RenderAnimatedOpacity( |
|||
opacity: this.opacity |
|||
); |
|||
} |
|||
|
|||
public override void updateRenderObject(BuildContext context, RenderObject renderObject) { |
|||
((RenderAnimatedOpacity) renderObject).opacity = this.opacity; |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
properties.add(new DiagnosticsProperty<Animation<double>>("opacity", this.opacity)); |
|||
} |
|||
} |
|||
|
|||
|
|||
public class AnimatedBuilder : AnimatedWidget { |
|||
public readonly TransitionBuilder builder; |
|||
|
|||
public readonly Widget child; |
|||
|
|||
public AnimatedBuilder(Key key = null, Listenable animation = null, TransitionBuilder builder = null, |
|||
Widget child = null) : |
|||
base(key, animation) { |
|||
D.assert(builder != null); |
|||
this.builder = builder; |
|||
this.child = child; |
|||
} |
|||
|
|||
protected internal override Widget build(BuildContext context) { |
|||
return this.builder(context, this.child); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 3df6a337dc40c448881ec2d0454362f5 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
%YAML 1.1 |
|||
%TAG !u! tag:unity3d.com,2011: |
|||
--- !u!29 &1 |
|||
OcclusionCullingSettings: |
|||
m_ObjectHideFlags: 0 |
|||
serializedVersion: 2 |
|||
m_OcclusionBakeSettings: |
|||
smallestOccluder: 5 |
|||
smallestHole: 0.25 |
|||
backfaceThreshold: 100 |
|||
m_SceneGUID: 00000000000000000000000000000000 |
|||
m_OcclusionCullingData: {fileID: 0} |
|||
--- !u!104 &2 |
|||
RenderSettings: |
|||
m_ObjectHideFlags: 0 |
|||
serializedVersion: 9 |
|||
m_Fog: 0 |
|||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} |
|||
m_FogMode: 3 |
|||
m_FogDensity: 0.01 |
|||
m_LinearFogStart: 0 |
|||
m_LinearFogEnd: 300 |
|||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} |
|||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} |
|||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} |
|||
m_AmbientIntensity: 1 |
|||
m_AmbientMode: 0 |
|||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} |
|||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} |
|||
m_HaloStrength: 0.5 |
|||
m_FlareStrength: 1 |
|||
m_FlareFadeSpeed: 3 |
|||
m_HaloTexture: {fileID: 0} |
|||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} |
|||
m_DefaultReflectionMode: 0 |
|||
m_DefaultReflectionResolution: 128 |
|||
m_ReflectionBounces: 1 |
|||
m_ReflectionIntensity: 1 |
|||
m_CustomReflection: {fileID: 0} |
|||
m_Sun: {fileID: 0} |
|||
m_IndirectSpecularColor: {r: 0.44657892, g: 0.4964127, b: 0.5748172, a: 1} |
|||
m_UseRadianceAmbientProbe: 0 |
|||
--- !u!157 &3 |
|||
LightmapSettings: |
|||
m_ObjectHideFlags: 0 |
|||
serializedVersion: 11 |
|||
m_GIWorkflowMode: 0 |
|||
m_GISettings: |
|||
serializedVersion: 2 |
|||
m_BounceScale: 1 |
|||
m_IndirectOutputScale: 1 |
|||
m_AlbedoBoost: 1 |
|||
m_EnvironmentLightingMode: 0 |
|||
m_EnableBakedLightmaps: 1 |
|||
m_EnableRealtimeLightmaps: 1 |
|||
m_LightmapEditorSettings: |
|||
serializedVersion: 10 |
|||
m_Resolution: 2 |
|||
m_BakeResolution: 40 |
|||
m_AtlasSize: 1024 |
|||
m_AO: 0 |
|||
m_AOMaxDistance: 1 |
|||
m_CompAOExponent: 1 |
|||
m_CompAOExponentDirect: 0 |
|||
m_Padding: 2 |
|||
m_LightmapParameters: {fileID: 0} |
|||
m_LightmapsBakeMode: 1 |
|||
m_TextureCompression: 1 |
|||
m_FinalGather: 0 |
|||
m_FinalGatherFiltering: 1 |
|||
m_FinalGatherRayCount: 256 |
|||
m_ReflectionCompression: 2 |
|||
m_MixedBakeMode: 2 |
|||
m_BakeBackend: 1 |
|||
m_PVRSampling: 1 |
|||
m_PVRDirectSampleCount: 32 |
|||
m_PVRSampleCount: 500 |
|||
m_PVRBounces: 2 |
|||
m_PVRFilterTypeDirect: 0 |
|||
m_PVRFilterTypeIndirect: 0 |
|||
m_PVRFilterTypeAO: 0 |
|||
m_PVRFilteringMode: 1 |
|||
m_PVRCulling: 1 |
|||
m_PVRFilteringGaussRadiusDirect: 1 |
|||
m_PVRFilteringGaussRadiusIndirect: 5 |
|||
m_PVRFilteringGaussRadiusAO: 2 |
|||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5 |
|||
m_PVRFilteringAtrousPositionSigmaIndirect: 2 |
|||
m_PVRFilteringAtrousPositionSigmaAO: 1 |
|||
m_ShowResolutionOverlay: 1 |
|||
m_LightingDataAsset: {fileID: 0} |
|||
m_UseShadowmask: 1 |
|||
--- !u!196 &4 |
|||
NavMeshSettings: |
|||
serializedVersion: 2 |
|||
m_ObjectHideFlags: 0 |
|||
m_BuildSettings: |
|||
serializedVersion: 2 |
|||
agentTypeID: 0 |
|||
agentRadius: 0.5 |
|||
agentHeight: 2 |
|||
agentSlope: 45 |
|||
agentClimb: 0.4 |
|||
ledgeDropHeight: 0 |
|||
maxJumpAcrossDistance: 0 |
|||
minRegionArea: 2 |
|||
manualCellSize: 0 |
|||
cellSize: 0.16666667 |
|||
manualTileSize: 0 |
|||
tileSize: 256 |
|||
accuratePlacement: 0 |
|||
debug: |
|||
m_Flags: 0 |
|||
m_NavMeshData: {fileID: 0} |
|||
--- !u!1 &4286172 |
|||
GameObject: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
serializedVersion: 6 |
|||
m_Component: |
|||
- component: {fileID: 4286176} |
|||
- component: {fileID: 4286175} |
|||
- component: {fileID: 4286174} |
|||
- component: {fileID: 4286173} |
|||
m_Layer: 5 |
|||
m_Name: Canvas |
|||
m_TagString: Untagged |
|||
m_Icon: {fileID: 0} |
|||
m_NavMeshLayer: 0 |
|||
m_StaticEditorFlags: 0 |
|||
m_IsActive: 1 |
|||
--- !u!114 &4286173 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 4286172} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3} |
|||
m_Name: |
|||
m_EditorClassIdentifier: |
|||
m_IgnoreReversedGraphics: 1 |
|||
m_BlockingObjects: 0 |
|||
m_BlockingMask: |
|||
serializedVersion: 2 |
|||
m_Bits: 4294967295 |
|||
--- !u!114 &4286174 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 4286172} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3} |
|||
m_Name: |
|||
m_EditorClassIdentifier: |
|||
m_UiScaleMode: 0 |
|||
m_ReferencePixelsPerUnit: 100 |
|||
m_ScaleFactor: 1 |
|||
m_ReferenceResolution: {x: 800, y: 600} |
|||
m_ScreenMatchMode: 0 |
|||
m_MatchWidthOrHeight: 0 |
|||
m_PhysicalUnit: 3 |
|||
m_FallbackScreenDPI: 96 |
|||
m_DefaultSpriteDPI: 96 |
|||
m_DynamicPixelsPerUnit: 1 |
|||
--- !u!223 &4286175 |
|||
Canvas: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 4286172} |
|||
m_Enabled: 1 |
|||
serializedVersion: 3 |
|||
m_RenderMode: 0 |
|||
m_Camera: {fileID: 0} |
|||
m_PlaneDistance: 100 |
|||
m_PixelPerfect: 0 |
|||
m_ReceivesEvents: 1 |
|||
m_OverrideSorting: 0 |
|||
m_OverridePixelPerfect: 0 |
|||
m_SortingBucketNormalizedSize: 0 |
|||
m_AdditionalShaderChannelsFlag: 0 |
|||
m_SortingLayerID: 0 |
|||
m_SortingOrder: 0 |
|||
m_TargetDisplay: 0 |
|||
--- !u!224 &4286176 |
|||
RectTransform: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 4286172} |
|||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} |
|||
m_LocalPosition: {x: 0, y: 0, z: 0} |
|||
m_LocalScale: {x: 0, y: 0, z: 0} |
|||
m_Children: |
|||
- {fileID: 927824195} |
|||
m_Father: {fileID: 0} |
|||
m_RootOrder: 2 |
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} |
|||
m_AnchorMin: {x: 0, y: 0} |
|||
m_AnchorMax: {x: 0, y: 0} |
|||
m_AnchoredPosition: {x: 0, y: 0} |
|||
m_SizeDelta: {x: 0, y: 0} |
|||
m_Pivot: {x: 0, y: 0} |
|||
--- !u!1 &26611619 |
|||
GameObject: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
serializedVersion: 6 |
|||
m_Component: |
|||
- component: {fileID: 26611622} |
|||
- component: {fileID: 26611621} |
|||
- component: {fileID: 26611620} |
|||
m_Layer: 0 |
|||
m_Name: Main Camera |
|||
m_TagString: MainCamera |
|||
m_Icon: {fileID: 0} |
|||
m_NavMeshLayer: 0 |
|||
m_StaticEditorFlags: 0 |
|||
m_IsActive: 1 |
|||
--- !u!81 &26611620 |
|||
AudioListener: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 26611619} |
|||
m_Enabled: 1 |
|||
--- !u!20 &26611621 |
|||
Camera: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 26611619} |
|||
m_Enabled: 1 |
|||
serializedVersion: 2 |
|||
m_ClearFlags: 1 |
|||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} |
|||
m_projectionMatrixMode: 1 |
|||
m_SensorSize: {x: 36, y: 24} |
|||
m_LensShift: {x: 0, y: 0} |
|||
m_GateFitMode: 2 |
|||
m_FocalLength: 50 |
|||
m_NormalizedViewPortRect: |
|||
serializedVersion: 2 |
|||
x: 0 |
|||
y: 0 |
|||
width: 1 |
|||
height: 1 |
|||
near clip plane: 0.3 |
|||
far clip plane: 1000 |
|||
field of view: 60 |
|||
orthographic: 0 |
|||
orthographic size: 5 |
|||
m_Depth: -1 |
|||
m_CullingMask: |
|||
serializedVersion: 2 |
|||
m_Bits: 4294967295 |
|||
m_RenderingPath: -1 |
|||
m_TargetTexture: {fileID: 0} |
|||
m_TargetDisplay: 0 |
|||
m_TargetEye: 3 |
|||
m_HDR: 1 |
|||
m_AllowMSAA: 1 |
|||
m_AllowDynamicResolution: 0 |
|||
m_ForceIntoRT: 0 |
|||
m_OcclusionCulling: 1 |
|||
m_StereoConvergence: 10 |
|||
m_StereoSeparation: 0.022 |
|||
--- !u!4 &26611622 |
|||
Transform: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 26611619} |
|||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} |
|||
m_LocalPosition: {x: 0, y: 1, z: -10} |
|||
m_LocalScale: {x: 1, y: 1, z: 1} |
|||
m_Children: [] |
|||
m_Father: {fileID: 0} |
|||
m_RootOrder: 0 |
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} |
|||
--- !u!1 &897547349 |
|||
GameObject: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
serializedVersion: 6 |
|||
m_Component: |
|||
- component: {fileID: 897547351} |
|||
- component: {fileID: 897547350} |
|||
m_Layer: 0 |
|||
m_Name: Directional Light |
|||
m_TagString: Untagged |
|||
m_Icon: {fileID: 0} |
|||
m_NavMeshLayer: 0 |
|||
m_StaticEditorFlags: 0 |
|||
m_IsActive: 1 |
|||
--- !u!108 &897547350 |
|||
Light: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 897547349} |
|||
m_Enabled: 1 |
|||
serializedVersion: 8 |
|||
m_Type: 1 |
|||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} |
|||
m_Intensity: 1 |
|||
m_Range: 10 |
|||
m_SpotAngle: 30 |
|||
m_CookieSize: 10 |
|||
m_Shadows: |
|||
m_Type: 2 |
|||
m_Resolution: -1 |
|||
m_CustomResolution: -1 |
|||
m_Strength: 1 |
|||
m_Bias: 0.05 |
|||
m_NormalBias: 0.4 |
|||
m_NearPlane: 0.2 |
|||
m_Cookie: {fileID: 0} |
|||
m_DrawHalo: 0 |
|||
m_Flare: {fileID: 0} |
|||
m_RenderMode: 0 |
|||
m_CullingMask: |
|||
serializedVersion: 2 |
|||
m_Bits: 4294967295 |
|||
m_Lightmapping: 4 |
|||
m_LightShadowCasterMode: 0 |
|||
m_AreaSize: {x: 1, y: 1} |
|||
m_BounceIntensity: 1 |
|||
m_ColorTemperature: 6570 |
|||
m_UseColorTemperature: 0 |
|||
m_ShadowRadius: 0 |
|||
m_ShadowAngle: 0 |
|||
--- !u!4 &897547351 |
|||
Transform: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 897547349} |
|||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} |
|||
m_LocalPosition: {x: 0, y: 3, z: 0} |
|||
m_LocalScale: {x: 1, y: 1, z: 1} |
|||
m_Children: [] |
|||
m_Father: {fileID: 0} |
|||
m_RootOrder: 1 |
|||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} |
|||
--- !u!1 &927824194 |
|||
GameObject: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
serializedVersion: 6 |
|||
m_Component: |
|||
- component: {fileID: 927824195} |
|||
- component: {fileID: 927824197} |
|||
- component: {fileID: 927824196} |
|||
m_Layer: 5 |
|||
m_Name: Navigation |
|||
m_TagString: Untagged |
|||
m_Icon: {fileID: 0} |
|||
m_NavMeshLayer: 0 |
|||
m_StaticEditorFlags: 0 |
|||
m_IsActive: 1 |
|||
--- !u!224 &927824195 |
|||
RectTransform: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 927824194} |
|||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} |
|||
m_LocalPosition: {x: 0, y: 0, z: 0} |
|||
m_LocalScale: {x: 1, y: 1, z: 1} |
|||
m_Children: [] |
|||
m_Father: {fileID: 4286176} |
|||
m_RootOrder: 0 |
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} |
|||
m_AnchorMin: {x: 0, y: 0} |
|||
m_AnchorMax: {x: 1, y: 1} |
|||
m_AnchoredPosition: {x: 0, y: 0} |
|||
m_SizeDelta: {x: 0, y: 0} |
|||
m_Pivot: {x: 0.5, y: 0.5} |
|||
--- !u!114 &927824196 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 927824194} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 11500000, guid: 020016dfbbaef41b496f4e5be17d098c, type: 3} |
|||
m_Name: |
|||
m_EditorClassIdentifier: |
|||
m_Material: {fileID: 0} |
|||
m_Color: {r: 1, g: 1, b: 1, a: 1} |
|||
m_RaycastTarget: 1 |
|||
m_OnCullStateChanged: |
|||
m_PersistentCalls: |
|||
m_Calls: [] |
|||
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, |
|||
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null |
|||
m_Texture: {fileID: 0} |
|||
m_UVRect: |
|||
serializedVersion: 2 |
|||
x: 0 |
|||
y: 0 |
|||
width: 1 |
|||
height: 1 |
|||
--- !u!222 &927824197 |
|||
CanvasRenderer: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 927824194} |
|||
m_CullTransparentMesh: 0 |
|||
--- !u!1 &1838283857 |
|||
GameObject: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
serializedVersion: 6 |
|||
m_Component: |
|||
- component: {fileID: 1838283860} |
|||
- component: {fileID: 1838283859} |
|||
- component: {fileID: 1838283858} |
|||
m_Layer: 0 |
|||
m_Name: EventSystem |
|||
m_TagString: Untagged |
|||
m_Icon: {fileID: 0} |
|||
m_NavMeshLayer: 0 |
|||
m_StaticEditorFlags: 0 |
|||
m_IsActive: 1 |
|||
--- !u!114 &1838283858 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 1838283857} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3} |
|||
m_Name: |
|||
m_EditorClassIdentifier: |
|||
m_HorizontalAxis: Horizontal |
|||
m_VerticalAxis: Vertical |
|||
m_SubmitButton: Submit |
|||
m_CancelButton: Cancel |
|||
m_InputActionsPerSecond: 10 |
|||
m_RepeatDelay: 0.5 |
|||
m_ForceModuleActive: 0 |
|||
--- !u!114 &1838283859 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 1838283857} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3} |
|||
m_Name: |
|||
m_EditorClassIdentifier: |
|||
m_FirstSelected: {fileID: 0} |
|||
m_sendNavigationEvents: 1 |
|||
m_DragThreshold: 10 |
|||
--- !u!4 &1838283860 |
|||
Transform: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 1838283857} |
|||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} |
|||
m_LocalPosition: {x: 0, y: 0, z: 0} |
|||
m_LocalScale: {x: 1, y: 1, z: 1} |
|||
m_Children: [] |
|||
m_Father: {fileID: 0} |
|||
m_RootOrder: 3 |
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} |
|
|||
fileFormatVersion: 2 |
|||
guid: 48300882e985d484880e16569661a93c |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.engine; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
|
|||
namespace UIWidgetsSample { |
|||
|
|||
public class NavigationCanvas : WidgetCanvas { |
|||
|
|||
protected override string initialRoute => "/"; |
|||
|
|||
protected override Dictionary<string, WidgetBuilder> routes => new Dictionary<string, WidgetBuilder> { |
|||
{"/", (context) => new HomeScreen()}, |
|||
{"/detail", (context) => new DetailScreen()} |
|||
}; |
|||
|
|||
protected override TextStyle textStyle => new TextStyle(fontSize: 24); |
|||
|
|||
protected override PageRouteFactory pageRouteBuilder => (RouteSettings settings, WidgetBuilder builder) => |
|||
new PageRouteBuilder( |
|||
settings: settings, |
|||
pageBuilder: (BuildContext context, Unity.UIWidgets.animation.Animation<double> animation, |
|||
Unity.UIWidgets.animation.Animation<double> secondaryAnimation) => builder(context), |
|||
transitionsBuilder: (BuildContext context, Animation<double> |
|||
animation, Animation<double> secondaryAnimation, Widget child) => new _FadeUpwardsPageTransition( |
|||
routeAnimation: animation, |
|||
child: child |
|||
) |
|||
); |
|||
} |
|||
|
|||
class HomeScreen : StatelessWidget { |
|||
public override Widget build(BuildContext context) { |
|||
return new Container( |
|||
color: new Color(0xFF888888), |
|||
child: new Center(child: new CustomButton(onPressed: () => { |
|||
Navigator.pushName(context, "/detail"); |
|||
}, child: new Text("Go to Detail")) |
|||
)); |
|||
|
|||
} |
|||
} |
|||
|
|||
class DetailScreen : StatelessWidget { |
|||
public override Widget build(BuildContext context) { |
|||
return new Container( |
|||
color: new Color(0xFF1389FD), |
|||
|
|||
child: new Center( |
|||
child: new Column( |
|||
children: new List<Widget>() { |
|||
new CustomButton(onPressed: () => { |
|||
Navigator.pop(context); |
|||
}, child: new Text("Back")), |
|||
new CustomButton(onPressed: () => { |
|||
_Dialog.showDialog(context, builder: (BuildContext c) => new Dialog()); |
|||
}, child: new Text("Show Dialog")) |
|||
} |
|||
) |
|||
)); |
|||
} |
|||
} |
|||
|
|||
class Dialog : StatelessWidget { |
|||
public override Widget build(BuildContext context) { |
|||
return new Center(child:new Container( |
|||
color: new Color(0xFFFF0000), |
|||
width: 100, |
|||
height: 80, |
|||
child: new Center( |
|||
child: new Text("Hello Dialog") |
|||
))); |
|||
} |
|||
} |
|||
|
|||
class _FadeUpwardsPageTransition : StatelessWidget { |
|||
|
|||
internal _FadeUpwardsPageTransition( |
|||
Key key = null, |
|||
Animation<double> routeAnimation = null, // The route's linear 0.0 - 1.0 animation.
|
|||
Widget child = null |
|||
) :base(key: key) { |
|||
this._positionAnimation = _bottomUpTween.chain(_fastOutSlowInTween).animate(routeAnimation); |
|||
this._opacityAnimation = _easeInTween.animate(routeAnimation); |
|||
this.child = child; |
|||
} |
|||
|
|||
static Tween<Offset> _bottomUpTween = new OffsetTween( |
|||
begin: new Offset(0.0, 0.25), |
|||
end: Offset.zero |
|||
); |
|||
|
|||
static Animatable<double> _fastOutSlowInTween = new CurveTween(curve: Curves.fastOutSlowIn); |
|||
static Animatable<double> _easeInTween = new CurveTween(curve: Curves.easeIn); |
|||
|
|||
readonly Animation<Offset> _positionAnimation; |
|||
readonly Animation<double> _opacityAnimation; |
|||
public readonly Widget child; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new SlideTransition( |
|||
position: this._positionAnimation, |
|||
child: new FadeTransition( |
|||
opacity: this._opacityAnimation, |
|||
child: this.child |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
static class _Dialog { |
|||
public static void showDialog(BuildContext context, |
|||
bool barrierDismissible = true, WidgetBuilder builder = null) { |
|||
DialogUtils.showGeneralDialog( |
|||
context: context, |
|||
pageBuilder: (BuildContext buildContext, Animation<double> animation, |
|||
Animation<double> secondaryAnimation) => { |
|||
return builder(buildContext); |
|||
}, |
|||
barrierDismissible: barrierDismissible, |
|||
barrierLabel: "", |
|||
barrierColor: new Color(0x8A000000), |
|||
transitionDuration: TimeSpan.FromMilliseconds(150), |
|||
transitionBuilder: _buildMaterialDialogTransitions |
|||
); |
|||
} |
|||
|
|||
static Widget _buildMaterialDialogTransitions(BuildContext context, |
|||
Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { |
|||
return new FadeTransition( |
|||
opacity: new CurvedAnimation( |
|||
parent: animation, |
|||
curve: Curves.easeOut |
|||
), |
|||
child: child |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 020016dfbbaef41b496f4e5be17d098c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue