Yuncong Zhang
6 年前
当前提交
9813cb26
共有 54 个文件被更改,包括 3031 次插入 和 107 次删除
-
17README.md
-
2Runtime/async/timer.cs
-
26Runtime/editor/editor_utils.cs
-
21Runtime/editor/editor_window.cs
-
93Runtime/engine/DisplayMetrics.cs
-
48Runtime/engine/UIWidgetsPanel.cs
-
4Runtime/foundation/basic_types.cs
-
2Runtime/foundation/node.cs
-
4Runtime/foundation/node.mixin.gen.cs
-
2Runtime/foundation/node.mixin.njk
-
2Runtime/gestures/multidrag.cs
-
3Runtime/material/input_decorator.cs
-
5Runtime/ui/painting/canvas_impl.cs
-
15Runtime/widgets/app.cs
-
56Runtime/widgets/basic.cs
-
6Runtime/widgets/navigator.cs
-
329Runtime/widgets/scroll_view.cs
-
20Samples/ReduxSample/CounterApp/CounterAppSample.cs
-
4Samples/ReduxSample/ObjectFinder/FinderGameObject.cs
-
73Samples/ReduxSample/ObjectFinder/ObjectFinderApp.cs
-
95Samples/ReduxSample/ObjectFinder/Reducer.cs
-
6Samples/ReduxSample/ObjectFinder/StoreProvider.cs
-
2Samples/UIWidgetsGallery/demo/shrine/shrine_order.cs
-
2Runtime/redux/redux_thunk.cs.meta
-
34Runtime/Plugins/platform/ios/UIWidgetsViewController.h
-
26Runtime/Plugins/platform/ios/UIWidgetsViewController.h.meta
-
108Runtime/Plugins/platform/ios/UIWidgetsViewController.mm
-
36Runtime/Plugins/platform/ios/UIWidgetsViewController.mm.meta
-
387Runtime/material/refresh_indicator.cs
-
3Runtime/material/refresh_indicator.cs.meta
-
397Runtime/material/reorderable_list.cs
-
3Runtime/material/reorderable_list.cs.meta
-
348Runtime/material/user_accounts_drawer_header.cs
-
3Runtime/material/user_accounts_drawer_header.cs.meta
-
8Runtime/redux.meta
-
520Runtime/widgets/overscroll_indicator.cs
-
3Runtime/widgets/overscroll_indicator.cs.meta
-
63Samples/UIWidgetSample/HttpRequestSample.cs
-
11Samples/UIWidgetSample/HttpRequestSample.cs.meta
-
19Runtime/redux/redux_logging.cs
-
32Runtime/redux/redux_thunk.cs
-
81Runtime/redux/store.cs
-
171Runtime/redux/widget_redux.cs
-
24Samples/ReduxSample/ObjectFinder/Middleware.cs
-
8Samples/ReduxSample/redux.meta
-
8Samples/ReduxSample/redux_logging.meta
-
8scripts/node_modules.meta
-
0/Runtime/redux/redux_thunk.cs.meta
-
0/Runtime/redux/store.cs.meta
-
0/Runtime/redux/widget_redux.cs.meta
-
0/Runtime/redux/redux_logging.cs.meta
|
|||
#ifndef PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSVIEWCONTROLLER_H_ |
|||
#define PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSVIEWCONTROLLER_H_ |
|||
|
|||
#import <UIKit/UIKit.h> |
|||
#include "UIWidgetsTextInputDelegate.h" |
|||
|
|||
|
|||
struct viewPadding |
|||
{ |
|||
float top; |
|||
float bottom; |
|||
float left; |
|||
float right; |
|||
}; |
|||
|
|||
struct viewMetrics |
|||
{ |
|||
float insets_top; |
|||
float insets_bottom; |
|||
float insets_left; |
|||
float insets_right; |
|||
|
|||
float padding_top; |
|||
float padding_bottom; |
|||
float padding_left; |
|||
float padding_right; |
|||
}; |
|||
|
|||
@interface UIWidgetsViewController : NSObject |
|||
@property viewPadding padding; |
|||
@property viewPadding viewInsets; |
|||
@end |
|||
|
|||
#endif // PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSVIEWCONTROLLER_H_ |
|
|||
fileFormatVersion: 2 |
|||
guid: a797fb0a2ddb84d4e8ac7f6da5d70819 |
|||
PluginImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
iconMap: {} |
|||
executionOrder: {} |
|||
defineConstraints: [] |
|||
isPreloaded: 0 |
|||
isOverridable: 1 |
|||
isExplicitlyReferenced: 0 |
|||
platformData: |
|||
- first: |
|||
Any: |
|||
second: |
|||
enabled: 1 |
|||
settings: {} |
|||
- first: |
|||
Editor: Editor |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
DefaultValueInitialized: true |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#include "UIWidgetsViewController.h" |
|||
#include "UIWidgetsMessageManager.h" |
|||
#include <Foundation/Foundation.h> |
|||
#include <UIKit/UIKit.h> |
|||
|
|||
@implementation UIWidgetsViewController { |
|||
} |
|||
|
|||
@synthesize viewInsets; |
|||
@synthesize padding; |
|||
|
|||
- (instancetype)init { |
|||
self = [super init]; |
|||
if (self) { |
|||
viewInsets.bottom = 0; |
|||
viewInsets.top = 0; |
|||
viewInsets.left = 0; |
|||
viewInsets.right = 0; |
|||
|
|||
CGFloat scale = [[UIScreen mainScreen] scale]; |
|||
if (@available(iOS 11, *)) { |
|||
padding.bottom = [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom * scale; |
|||
padding.top = [UIApplication sharedApplication].keyWindow.safeAreaInsets.top * scale; |
|||
padding.left = [UIApplication sharedApplication].keyWindow.safeAreaInsets.left * scale; |
|||
padding.right = [UIApplication sharedApplication].keyWindow.safeAreaInsets.right * scale; |
|||
} else { |
|||
CGRect statusFrame = [UIApplication sharedApplication].statusBarFrame; |
|||
padding.bottom = 0; |
|||
padding.top = statusFrame.size.height * scale; |
|||
padding.left = 0; |
|||
padding.right = 0; |
|||
} |
|||
|
|||
NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
|||
[center addObserver:self |
|||
selector:@selector(keyboardWillChangeFrame:) |
|||
name:UIKeyboardWillChangeFrameNotification |
|||
object:nil]; |
|||
[center addObserver:self |
|||
selector:@selector(keyboardWillBeHidden:) |
|||
name:UIKeyboardWillHideNotification |
|||
object:nil]; |
|||
} |
|||
|
|||
return self; |
|||
} |
|||
|
|||
-(void)keyboardWillBeHidden:(NSNotification*)notification { |
|||
viewInsets.bottom = 0; |
|||
CGFloat scale = [UIScreen mainScreen].scale; |
|||
if (@available(iOS 11, *)) { |
|||
CGFloat cur_padding = [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom * scale; |
|||
padding.bottom = cur_padding; |
|||
} else { |
|||
CGRect statusFrame = [UIApplication sharedApplication].statusBarFrame; |
|||
CGFloat cur_padding = statusFrame.size.height * scale; |
|||
padding.top = cur_padding; |
|||
} |
|||
|
|||
UIWidgetsMethodMessage(@"ViewportMatricsChanged", @"UIWidgetViewController.keyboardHide", @[]); |
|||
} |
|||
|
|||
-(void)keyboardWillChangeFrame:(NSNotification*)notification { |
|||
NSDictionary* info = [notification userInfo]; |
|||
CGFloat bottom = CGRectGetHeight([[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]); |
|||
CGFloat scale = [UIScreen mainScreen].scale; |
|||
|
|||
viewInsets.bottom = bottom * scale; |
|||
padding.bottom = 0; |
|||
|
|||
UIWidgetsMethodMessage(@"ViewportMatricsChanged", @"UIWidgetViewController.keyboardHide", @[]); |
|||
} |
|||
|
|||
-(void)tryLaunch { |
|||
|
|||
} |
|||
|
|||
+ (instancetype)sharedInstance { |
|||
static UIWidgetsViewController *sharedInstance = nil; |
|||
static dispatch_once_t onceToken; |
|||
|
|||
dispatch_once(&onceToken, ^{ |
|||
sharedInstance = [[UIWidgetsViewController alloc] init]; |
|||
}); |
|||
return sharedInstance; |
|||
} |
|||
|
|||
@end |
|||
|
|||
extern "C" |
|||
{ |
|||
viewMetrics IOSGetViewportPadding() |
|||
{ |
|||
viewMetrics metrics; |
|||
viewPadding insets = [[UIWidgetsViewController sharedInstance] viewInsets]; |
|||
viewPadding padding = [[UIWidgetsViewController sharedInstance] padding]; |
|||
metrics.insets_bottom = insets.bottom; |
|||
metrics.insets_top = insets.top; |
|||
metrics.insets_left = insets.left; |
|||
metrics.insets_right = insets.right; |
|||
metrics.padding_bottom = padding.bottom; |
|||
metrics.padding_top = padding.top; |
|||
metrics.padding_left = padding.left; |
|||
metrics.padding_right = padding.right; |
|||
|
|||
return metrics; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: fc3d8a9d616f147929ae9b8898fa01bf |
|||
PluginImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
iconMap: {} |
|||
executionOrder: {} |
|||
defineConstraints: [] |
|||
isPreloaded: 0 |
|||
isOverridable: 1 |
|||
isExplicitlyReferenced: 0 |
|||
platformData: |
|||
- first: |
|||
Any: |
|||
second: |
|||
enabled: 0 |
|||
settings: {} |
|||
- first: |
|||
Editor: Editor |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
DefaultValueInitialized: true |
|||
- first: |
|||
iPhone: iOS |
|||
second: |
|||
enabled: 1 |
|||
settings: {} |
|||
- first: |
|||
tvOS: tvOS |
|||
second: |
|||
enabled: 1 |
|||
settings: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using RSG; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
class RefreshIndicatorUtils { |
|||
public const float _kDragContainerExtentPercentage = 0.25f; |
|||
|
|||
public const float _kDragSizeFactorLimit = 1.5f; |
|||
|
|||
public static readonly TimeSpan _kIndicatorSnapDuration = new TimeSpan(0, 0, 0, 0, 150); |
|||
|
|||
public static readonly TimeSpan _kIndicatorScaleDuration = new TimeSpan(0, 0, 0, 0, 200); |
|||
} |
|||
|
|||
public delegate Promise RefreshCallback(); |
|||
|
|||
enum _RefreshIndicatorMode { |
|||
drag, // Pointer is down.
|
|||
armed, // Dragged far enough that an up event will run the onRefresh callback.
|
|||
snap, // Animating to the indicator"s final "displacement".
|
|||
refresh, // Running the refresh callback.
|
|||
done, // Animating the indicator"s fade-out after refreshing.
|
|||
canceled, // Animating the indicator"s fade-out after not arming.
|
|||
} |
|||
|
|||
public class RefreshIndicator : StatefulWidget { |
|||
public RefreshIndicator( |
|||
Key key = null, |
|||
Widget child = null, |
|||
float displacement = 40.0f, |
|||
RefreshCallback onRefresh = null, |
|||
Color color = null, |
|||
Color backgroundColor = null, |
|||
ScrollNotificationPredicate notificationPredicate = null |
|||
) : base(key: key) { |
|||
D.assert(child != null); |
|||
D.assert(onRefresh != null); |
|||
this.child = child; |
|||
this.displacement = displacement; |
|||
this.onRefresh = onRefresh; |
|||
this.color = color; |
|||
this.backgroundColor = backgroundColor; |
|||
this.notificationPredicate = notificationPredicate ?? ScrollNotification.defaultScrollNotificationPredicate; |
|||
} |
|||
|
|||
public readonly Widget child; |
|||
|
|||
public readonly float displacement; |
|||
|
|||
public readonly RefreshCallback onRefresh; |
|||
|
|||
public readonly Color color; |
|||
|
|||
public readonly Color backgroundColor; |
|||
|
|||
public readonly ScrollNotificationPredicate notificationPredicate; |
|||
|
|||
public override State createState() { |
|||
return new RefreshIndicatorState(); |
|||
} |
|||
} |
|||
|
|||
public class RefreshIndicatorState : TickerProviderStateMixin<RefreshIndicator> { |
|||
AnimationController _positionController; |
|||
AnimationController _scaleController; |
|||
Animation<float> _positionFactor; |
|||
Animation<float> _scaleFactor; |
|||
Animation<float> _value; |
|||
Animation<Color> _valueColor; |
|||
|
|||
_RefreshIndicatorMode? _mode; |
|||
Promise _pendingRefreshFuture; |
|||
bool? _isIndicatorAtTop; |
|||
float? _dragOffset; |
|||
|
|||
static readonly Animatable<float> _threeQuarterTween = new FloatTween(begin: 0.0f, end: 0.75f); |
|||
|
|||
static readonly Animatable<float> _kDragSizeFactorLimitTween = |
|||
new FloatTween(begin: 0.0f, end: RefreshIndicatorUtils._kDragSizeFactorLimit); |
|||
|
|||
static readonly Animatable<float> _oneToZeroTween = new FloatTween(begin: 1.0f, end: 0.0f); |
|||
|
|||
public RefreshIndicatorState() { |
|||
} |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this._positionController = new AnimationController(vsync: this); |
|||
this._positionFactor = this._positionController.drive(_kDragSizeFactorLimitTween); |
|||
this._value = |
|||
this._positionController |
|||
.drive(_threeQuarterTween); // The "value" of the circular progress indicator during a drag.
|
|||
|
|||
this._scaleController = new AnimationController(vsync: this); |
|||
this._scaleFactor = this._scaleController.drive(_oneToZeroTween); |
|||
} |
|||
|
|||
public override void didChangeDependencies() { |
|||
ThemeData theme = Theme.of(this.context); |
|||
this._valueColor = this._positionController.drive( |
|||
new ColorTween( |
|||
begin: (this.widget.color ?? theme.accentColor).withOpacity(0.0f), |
|||
end: (this.widget.color ?? theme.accentColor).withOpacity(1.0f) |
|||
).chain(new CurveTween( |
|||
curve: new Interval(0.0f, 1.0f / RefreshIndicatorUtils._kDragSizeFactorLimit) |
|||
)) |
|||
); |
|||
base.didChangeDependencies(); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this._positionController.dispose(); |
|||
this._scaleController.dispose(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
bool _handleScrollNotification(ScrollNotification notification) { |
|||
if (!this.widget.notificationPredicate(notification)) { |
|||
return false; |
|||
} |
|||
|
|||
if (notification is ScrollStartNotification && notification.metrics.extentBefore() == 0.0f && |
|||
this._mode == null && this._start(notification.metrics.axisDirection)) { |
|||
this.setState(() => { this._mode = _RefreshIndicatorMode.drag; }); |
|||
return false; |
|||
} |
|||
|
|||
bool? indicatorAtTopNow = null; |
|||
switch (notification.metrics.axisDirection) { |
|||
case AxisDirection.down: |
|||
indicatorAtTopNow = true; |
|||
break; |
|||
case AxisDirection.up: |
|||
indicatorAtTopNow = false; |
|||
break; |
|||
case AxisDirection.left: |
|||
case AxisDirection.right: |
|||
indicatorAtTopNow = null; |
|||
break; |
|||
} |
|||
|
|||
if (indicatorAtTopNow != this._isIndicatorAtTop) { |
|||
if (this._mode == _RefreshIndicatorMode.drag || this._mode == _RefreshIndicatorMode.armed) { |
|||
this._dismiss(_RefreshIndicatorMode.canceled); |
|||
} |
|||
} |
|||
else if (notification is ScrollUpdateNotification) { |
|||
if (this._mode == _RefreshIndicatorMode.drag || this._mode == _RefreshIndicatorMode.armed) { |
|||
if (notification.metrics.extentBefore() > 0.0f) { |
|||
this._dismiss(_RefreshIndicatorMode.canceled); |
|||
} |
|||
else { |
|||
this._dragOffset -= (notification as ScrollUpdateNotification).scrollDelta; |
|||
this._checkDragOffset(notification.metrics.viewportDimension); |
|||
} |
|||
} |
|||
|
|||
if (this._mode == _RefreshIndicatorMode.armed && |
|||
(notification as ScrollUpdateNotification).dragDetails == null) { |
|||
this._show(); |
|||
} |
|||
} |
|||
else if (notification is OverscrollNotification) { |
|||
if (this._mode == _RefreshIndicatorMode.drag || this._mode == _RefreshIndicatorMode.armed) { |
|||
this._dragOffset -= (notification as OverscrollNotification).overscroll / 2.0f; |
|||
this._checkDragOffset(notification.metrics.viewportDimension); |
|||
} |
|||
} |
|||
else if (notification is ScrollEndNotification) { |
|||
switch (this._mode) { |
|||
case _RefreshIndicatorMode.armed: |
|||
this._show(); |
|||
break; |
|||
case _RefreshIndicatorMode.drag: |
|||
this._dismiss(_RefreshIndicatorMode.canceled); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
bool _handleGlowNotification(OverscrollIndicatorNotification notification) { |
|||
if (notification.depth != 0 || !notification.leading) { |
|||
return false; |
|||
} |
|||
|
|||
if (this._mode == _RefreshIndicatorMode.drag) { |
|||
notification.disallowGlow(); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
bool _start(AxisDirection direction) { |
|||
D.assert(this._mode == null); |
|||
D.assert(this._isIndicatorAtTop == null); |
|||
D.assert(this._dragOffset == null); |
|||
switch (direction) { |
|||
case AxisDirection.down: |
|||
this._isIndicatorAtTop = true; |
|||
break; |
|||
case AxisDirection.up: |
|||
this._isIndicatorAtTop = false; |
|||
break; |
|||
case AxisDirection.left: |
|||
case AxisDirection.right: |
|||
this._isIndicatorAtTop = null; |
|||
return false; |
|||
} |
|||
|
|||
this._dragOffset = 0.0f; |
|||
this._scaleController.setValue(0.0f); |
|||
this._positionController.setValue(0.0f); |
|||
return true; |
|||
} |
|||
|
|||
void _checkDragOffset(float containerExtent) { |
|||
D.assert(this._mode == _RefreshIndicatorMode.drag || this._mode == _RefreshIndicatorMode.armed); |
|||
float? newValue = this._dragOffset / |
|||
(containerExtent * RefreshIndicatorUtils._kDragContainerExtentPercentage); |
|||
if (this._mode == _RefreshIndicatorMode.armed) { |
|||
newValue = Mathf.Max(newValue ?? 0.0f, 1.0f / RefreshIndicatorUtils._kDragSizeFactorLimit); |
|||
} |
|||
|
|||
this._positionController.setValue(newValue?.clamp(0.0f, 1.0f) ?? 0.0f); // this triggers various rebuilds
|
|||
if (this._mode == _RefreshIndicatorMode.drag && this._valueColor.value.alpha == 0xFF) { |
|||
this._mode = _RefreshIndicatorMode.armed; |
|||
} |
|||
} |
|||
|
|||
IPromise _dismiss(_RefreshIndicatorMode newMode) { |
|||
D.assert(newMode == _RefreshIndicatorMode.canceled || newMode == _RefreshIndicatorMode.done); |
|||
this.setState(() => { this._mode = newMode; }); |
|||
switch (this._mode) { |
|||
case _RefreshIndicatorMode.done: |
|||
return this._scaleController |
|||
.animateTo(1.0f, duration: RefreshIndicatorUtils._kIndicatorScaleDuration).Then(() => { |
|||
if (this.mounted && this._mode == newMode) { |
|||
this._dragOffset = null; |
|||
this._isIndicatorAtTop = null; |
|||
this.setState(() => { this._mode = null; }); |
|||
} |
|||
}); |
|||
case _RefreshIndicatorMode.canceled: |
|||
return this._positionController |
|||
.animateTo(0.0f, duration: RefreshIndicatorUtils._kIndicatorScaleDuration).Then(() => { |
|||
if (this.mounted && this._mode == newMode) { |
|||
this._dragOffset = null; |
|||
this._isIndicatorAtTop = null; |
|||
this.setState(() => { this._mode = null; }); |
|||
} |
|||
}); |
|||
default: |
|||
throw new Exception("Unknown refresh indicator mode: " + this._mode); |
|||
} |
|||
} |
|||
|
|||
void _show() { |
|||
D.assert(this._mode != _RefreshIndicatorMode.refresh); |
|||
D.assert(this._mode != _RefreshIndicatorMode.snap); |
|||
Promise completer = new Promise(); |
|||
this._pendingRefreshFuture = completer; |
|||
this._mode = _RefreshIndicatorMode.snap; |
|||
this._positionController |
|||
.animateTo(1.0f / RefreshIndicatorUtils._kDragSizeFactorLimit, |
|||
duration: RefreshIndicatorUtils._kIndicatorSnapDuration) |
|||
.Then(() => { |
|||
if (this.mounted && this._mode == _RefreshIndicatorMode.snap) { |
|||
D.assert(this.widget.onRefresh != null); |
|||
this.setState(() => { this._mode = _RefreshIndicatorMode.refresh; }); |
|||
|
|||
Promise refreshResult = this.widget.onRefresh(); |
|||
D.assert(() => { |
|||
if (refreshResult == null) { |
|||
UIWidgetsError.reportError(new UIWidgetsErrorDetails( |
|||
exception: new UIWidgetsError( |
|||
"The onRefresh callback returned null.\n" + |
|||
"The RefreshIndicator onRefresh callback must return a Promise." |
|||
), |
|||
context: "when calling onRefresh", |
|||
library: "material library" |
|||
)); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
if (refreshResult == null) { |
|||
return; |
|||
} |
|||
|
|||
refreshResult.Then(() => { |
|||
if (this.mounted && this._mode == _RefreshIndicatorMode.refresh) { |
|||
completer.Resolve(); |
|||
this._dismiss(_RefreshIndicatorMode.done); |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
Promise show(bool atTop = true) { |
|||
if (this._mode != _RefreshIndicatorMode.refresh && this._mode != _RefreshIndicatorMode.snap) { |
|||
if (this._mode == null) { |
|||
this._start(atTop ? AxisDirection.down : AxisDirection.up); |
|||
} |
|||
|
|||
this._show(); |
|||
} |
|||
|
|||
return this._pendingRefreshFuture; |
|||
} |
|||
|
|||
GlobalKey _key = GlobalKey.key(); |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
D.assert(MaterialD.debugCheckHasMaterialLocalizations(context)); |
|||
Widget child = new NotificationListener<ScrollNotification>( |
|||
key: this._key, |
|||
onNotification: this._handleScrollNotification, |
|||
child: new NotificationListener<OverscrollIndicatorNotification>( |
|||
onNotification: this._handleGlowNotification, |
|||
child: this.widget.child |
|||
) |
|||
); |
|||
if (this._mode == null) { |
|||
D.assert(this._dragOffset == null); |
|||
D.assert(this._isIndicatorAtTop == null); |
|||
return child; |
|||
} |
|||
|
|||
D.assert(this._dragOffset != null); |
|||
D.assert(this._isIndicatorAtTop != null); |
|||
|
|||
bool showIndeterminateIndicator = |
|||
this._mode == _RefreshIndicatorMode.refresh || this._mode == _RefreshIndicatorMode.done; |
|||
|
|||
return new Stack( |
|||
children: new List<Widget> { |
|||
child, |
|||
new Positioned( |
|||
top: this._isIndicatorAtTop == true ? 0.0f : (float?) null, |
|||
bottom: this._isIndicatorAtTop != true ? 0.0f : (float?) null, |
|||
left: 0.0f, |
|||
right: 0.0f, |
|||
child: new SizeTransition( |
|||
axisAlignment: this._isIndicatorAtTop == true ? 1.0f : -1.0f, |
|||
sizeFactor: this._positionFactor, // this is what brings it down
|
|||
child: new Container( |
|||
padding: this._isIndicatorAtTop == true |
|||
? EdgeInsets.only(top: this.widget.displacement) |
|||
: EdgeInsets.only(bottom: this.widget.displacement), |
|||
alignment: this._isIndicatorAtTop == true |
|||
? Alignment.topCenter |
|||
: Alignment.bottomCenter, |
|||
child: new ScaleTransition( |
|||
scale: this._scaleFactor, |
|||
child: new AnimatedBuilder( |
|||
animation: this._positionController, |
|||
builder: (BuildContext _context, Widget _child) => { |
|||
return new RefreshProgressIndicator( |
|||
value: showIndeterminateIndicator ? (float?) null : this._value.value, |
|||
valueColor: this._valueColor, |
|||
backgroundColor: this.widget.backgroundColor |
|||
); |
|||
} |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
} |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 74dd879101fe4541a5b42619cf4f309c |
|||
timeCreated: 1554344854 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public delegate void ReorderCallback(int oldIndex, int newIndex); |
|||
|
|||
public class ReorderableListView : StatefulWidget { |
|||
public ReorderableListView( |
|||
Widget header = null, |
|||
List<Widget> children = null, |
|||
ReorderCallback onReorder = null, |
|||
Axis scrollDirection = Axis.vertical, |
|||
EdgeInsets padding = null |
|||
) { |
|||
D.assert(onReorder != null); |
|||
D.assert(children != null); |
|||
D.assert( |
|||
children.All((Widget w) => w.key != null), |
|||
"All children of this widget must have a key." |
|||
); |
|||
this.header = header; |
|||
this.children = children; |
|||
this.scrollDirection = scrollDirection; |
|||
this.padding = padding; |
|||
this.onReorder = onReorder; |
|||
} |
|||
|
|||
public readonly Widget header; |
|||
|
|||
public readonly List<Widget> children; |
|||
|
|||
public readonly Axis scrollDirection; |
|||
|
|||
public readonly EdgeInsets padding; |
|||
|
|||
public readonly ReorderCallback onReorder; |
|||
|
|||
public override State createState() { |
|||
return new _ReorderableListViewState(); |
|||
} |
|||
} |
|||
|
|||
class _ReorderableListViewState : State<ReorderableListView> { |
|||
GlobalKey _overlayKey = GlobalKey.key(debugLabel: "$ReorderableListView overlay key"); |
|||
|
|||
OverlayEntry _listOverlayEntry; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this._listOverlayEntry = new OverlayEntry( |
|||
opaque: true, |
|||
builder: (BuildContext context) => { |
|||
return new _ReorderableListContent( |
|||
header: this.widget.header, |
|||
children: this.widget.children, |
|||
scrollDirection: this.widget.scrollDirection, |
|||
onReorder: this.widget.onReorder, |
|||
padding: this.widget.padding |
|||
); |
|||
} |
|||
); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new Overlay( |
|||
key: this._overlayKey, |
|||
initialEntries: new List<OverlayEntry> { |
|||
this._listOverlayEntry |
|||
}); |
|||
} |
|||
} |
|||
|
|||
class _ReorderableListContent : StatefulWidget { |
|||
public _ReorderableListContent( |
|||
Widget header, |
|||
List<Widget> children, |
|||
Axis scrollDirection, |
|||
EdgeInsets padding, |
|||
ReorderCallback onReorder |
|||
) { |
|||
this.header = header; |
|||
this.children = children; |
|||
this.scrollDirection = scrollDirection; |
|||
this.padding = padding; |
|||
this.onReorder = onReorder; |
|||
} |
|||
|
|||
public readonly Widget header; |
|||
public readonly List<Widget> children; |
|||
public readonly Axis scrollDirection; |
|||
public readonly EdgeInsets padding; |
|||
public readonly ReorderCallback onReorder; |
|||
|
|||
public override State createState() { |
|||
return new _ReorderableListContentState(); |
|||
} |
|||
} |
|||
|
|||
class _ReorderableListContentState : TickerProviderStateMixin<_ReorderableListContent> { |
|||
const float _defaultDropAreaExtent = 100.0f; |
|||
|
|||
const float _dropAreaMargin = 8.0f; |
|||
|
|||
readonly TimeSpan _reorderAnimationDuration = new TimeSpan(0, 0, 0, 0, 200); |
|||
|
|||
readonly TimeSpan _scrollAnimationDuration = new TimeSpan(0, 0, 0, 0, 200); |
|||
|
|||
ScrollController _scrollController; |
|||
|
|||
AnimationController _entranceController; |
|||
|
|||
AnimationController _ghostController; |
|||
|
|||
Key _dragging; |
|||
|
|||
Size _draggingFeedbackSize; |
|||
|
|||
int _dragStartIndex = 0; |
|||
|
|||
int _ghostIndex = 0; |
|||
|
|||
int _currentIndex = 0; |
|||
|
|||
int _nextIndex = 0; |
|||
|
|||
bool _scrolling = false; |
|||
|
|||
float _dropAreaExtent { |
|||
get { |
|||
if (this._draggingFeedbackSize == null) { |
|||
return _defaultDropAreaExtent; |
|||
} |
|||
|
|||
float dropAreaWithoutMargin; |
|||
switch (this.widget.scrollDirection) { |
|||
case Axis.horizontal: |
|||
dropAreaWithoutMargin = this._draggingFeedbackSize.width; |
|||
break; |
|||
case Axis.vertical: |
|||
default: |
|||
dropAreaWithoutMargin = this._draggingFeedbackSize.height; |
|||
break; |
|||
} |
|||
|
|||
return dropAreaWithoutMargin + _dropAreaMargin; |
|||
} |
|||
} |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this._entranceController = new AnimationController(vsync: this, duration: this._reorderAnimationDuration); |
|||
this._ghostController = new AnimationController(vsync: this, duration: this._reorderAnimationDuration); |
|||
this._entranceController.addStatusListener(this._onEntranceStatusChanged); |
|||
} |
|||
|
|||
public override void didChangeDependencies() { |
|||
this._scrollController = PrimaryScrollController.of(this.context) ?? new ScrollController(); |
|||
base.didChangeDependencies(); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this._entranceController.dispose(); |
|||
this._ghostController.dispose(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
void _requestAnimationToNextIndex() { |
|||
if (this._entranceController.isCompleted) { |
|||
this._ghostIndex = this._currentIndex; |
|||
if (this._nextIndex == this._currentIndex) { |
|||
return; |
|||
} |
|||
|
|||
this._currentIndex = this._nextIndex; |
|||
this._ghostController.reverse(from: 1.0f); |
|||
this._entranceController.forward(from: 0.0f); |
|||
} |
|||
} |
|||
|
|||
void _onEntranceStatusChanged(AnimationStatus status) { |
|||
if (status == AnimationStatus.completed) { |
|||
this.setState(() => { this._requestAnimationToNextIndex(); }); |
|||
} |
|||
} |
|||
|
|||
void _scrollTo(BuildContext context) { |
|||
if (this._scrolling) { |
|||
return; |
|||
} |
|||
|
|||
RenderObject contextObject = context.findRenderObject(); |
|||
RenderAbstractViewport viewport = RenderViewportUtils.of(contextObject); |
|||
D.assert(viewport != null); |
|||
float margin = this._dropAreaExtent; |
|||
float scrollOffset = this._scrollController.offset; |
|||
float topOffset = Mathf.Max(this._scrollController.position.minScrollExtent, |
|||
viewport.getOffsetToReveal(contextObject, 0.0f).offset - margin |
|||
); |
|||
float bottomOffset = Mathf.Min(this._scrollController.position.maxScrollExtent, |
|||
viewport.getOffsetToReveal(contextObject, 1.0f).offset + margin |
|||
); |
|||
bool onScreen = scrollOffset <= topOffset && scrollOffset >= bottomOffset; |
|||
|
|||
if (!onScreen) { |
|||
this._scrolling = true; |
|||
this._scrollController.position.animateTo( |
|||
scrollOffset < bottomOffset ? bottomOffset : topOffset, |
|||
duration: this._scrollAnimationDuration, |
|||
curve: Curves.easeInOut |
|||
).Then(() => { this.setState(() => { this._scrolling = false; }); }); |
|||
} |
|||
} |
|||
|
|||
Widget _buildContainerForScrollDirection(List<Widget> children = null) { |
|||
switch (this.widget.scrollDirection) { |
|||
case Axis.horizontal: |
|||
return new Row(children: children); |
|||
case Axis.vertical: |
|||
default: |
|||
return new Column(children: children); |
|||
} |
|||
} |
|||
|
|||
Widget _wrap(Widget toWrap, int index, BoxConstraints constraints) { |
|||
D.assert(toWrap.key != null); |
|||
GlobalObjectKey<State<_ReorderableListContent>> keyIndexGlobalKey = new GlobalObjectKey<State<_ReorderableListContent>>(toWrap.key); |
|||
|
|||
void onDragStarted() { |
|||
this.setState(() => { |
|||
this._dragging = toWrap.key; |
|||
this._dragStartIndex = index; |
|||
this._ghostIndex = index; |
|||
this._currentIndex = index; |
|||
this._entranceController.setValue(1.0f); |
|||
this._draggingFeedbackSize = keyIndexGlobalKey.currentContext.size; |
|||
}); |
|||
} |
|||
|
|||
void reorder(int startIndex, int endIndex) { |
|||
this.setState(() => { |
|||
if (startIndex != endIndex) { |
|||
this.widget.onReorder(startIndex, endIndex); |
|||
} |
|||
|
|||
this._ghostController.reverse(from: 0.1f); |
|||
this._entranceController.reverse(from: 0.1f); |
|||
this._dragging = null; |
|||
}); |
|||
} |
|||
|
|||
void onDragEnded() { |
|||
reorder(this._dragStartIndex, this._currentIndex); |
|||
} |
|||
|
|||
|
|||
Widget wrapWithKeyedSubtree() { |
|||
return new KeyedSubtree( |
|||
key: keyIndexGlobalKey, |
|||
child: toWrap |
|||
); |
|||
} |
|||
|
|||
Widget buildDragTarget(BuildContext context, List<Key> acceptedCandidates, List<Key> rejectedCandidates) { |
|||
Widget toWrapWithKeyedSubtree = wrapWithKeyedSubtree(); |
|||
Widget child = new LongPressDraggable<Key>( |
|||
maxSimultaneousDrags: 1, |
|||
axis: this.widget.scrollDirection, |
|||
data: toWrap.key, |
|||
feedback: new Container( |
|||
alignment: Alignment.topLeft, |
|||
// These constraints will limit the cross axis of the drawn widget.
|
|||
constraints: constraints, |
|||
child: new Material( |
|||
elevation: 6.0f, |
|||
child: toWrapWithKeyedSubtree |
|||
) |
|||
), |
|||
child: this._dragging == toWrap.key ? new SizedBox() : toWrapWithKeyedSubtree, |
|||
childWhenDragging: new SizedBox(), |
|||
dragAnchor: DragAnchor.child, |
|||
onDragStarted: onDragStarted, |
|||
onDragCompleted: onDragEnded, |
|||
onDraggableCanceled: (Velocity velocity, Offset offset) => { onDragEnded(); } |
|||
); |
|||
|
|||
if (index >= this.widget.children.Count) { |
|||
child = toWrap; |
|||
} |
|||
|
|||
Widget spacing; |
|||
switch (this.widget.scrollDirection) { |
|||
case Axis.horizontal: |
|||
spacing = new SizedBox(width: this._dropAreaExtent); |
|||
break; |
|||
case Axis.vertical: |
|||
default: |
|||
spacing = new SizedBox(height: this._dropAreaExtent); |
|||
break; |
|||
} |
|||
|
|||
if (this._currentIndex == index) { |
|||
return this._buildContainerForScrollDirection(children: new List<Widget> { |
|||
new SizeTransition( |
|||
sizeFactor: this._entranceController, |
|||
axis: this.widget.scrollDirection, |
|||
child: spacing |
|||
), |
|||
child |
|||
}); |
|||
} |
|||
|
|||
if (this._ghostIndex == index) { |
|||
return this._buildContainerForScrollDirection(children: new List<Widget> { |
|||
new SizeTransition( |
|||
sizeFactor: this._ghostController, |
|||
axis: this.widget.scrollDirection, |
|||
child: spacing |
|||
), |
|||
child |
|||
}); |
|||
} |
|||
|
|||
return child; |
|||
} |
|||
|
|||
return new Builder(builder: (BuildContext context) => { |
|||
return new DragTarget<Key>( |
|||
builder: buildDragTarget, |
|||
onWillAccept: (Key toAccept) => { |
|||
this.setState(() => { |
|||
this._nextIndex = index; |
|||
this._requestAnimationToNextIndex(); |
|||
}); |
|||
this._scrollTo(context); |
|||
return this._dragging == toAccept && toAccept != toWrap.key; |
|||
}, |
|||
onAccept: (Key accepted) => { }, |
|||
onLeave: (Key leaving) => { } |
|||
); |
|||
}); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
D.assert(MaterialD.debugCheckHasMaterialLocalizations(context)); |
|||
return new LayoutBuilder(builder: (BuildContext _, BoxConstraints constraints) => { |
|||
List<Widget> wrappedChildren = new List<Widget> { }; |
|||
if (this.widget.header != null) { |
|||
wrappedChildren.Add(this.widget.header); |
|||
} |
|||
|
|||
for (int i = 0; i < this.widget.children.Count; i += 1) { |
|||
wrappedChildren.Add(this._wrap(this.widget.children[i], i, constraints)); |
|||
} |
|||
|
|||
Key endWidgetKey = Key.key("DraggableList - End Widget"); |
|||
Widget finalDropArea; |
|||
switch (this.widget.scrollDirection) { |
|||
case Axis.horizontal: |
|||
finalDropArea = new SizedBox( |
|||
key: endWidgetKey, |
|||
width: _defaultDropAreaExtent, |
|||
height: constraints.maxHeight |
|||
); |
|||
break; |
|||
case Axis.vertical: |
|||
default: |
|||
finalDropArea = new SizedBox( |
|||
key: endWidgetKey, |
|||
height: _defaultDropAreaExtent, |
|||
width: constraints.maxWidth |
|||
); |
|||
break; |
|||
} |
|||
|
|||
wrappedChildren.Add(this._wrap( |
|||
finalDropArea, this.widget.children.Count, |
|||
constraints) |
|||
); |
|||
return new SingleChildScrollView( |
|||
scrollDirection: this.widget.scrollDirection, |
|||
child: this._buildContainerForScrollDirection(children: wrappedChildren), |
|||
padding: this.widget.padding, |
|||
controller: this._scrollController |
|||
); |
|||
}); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 730da7185538491389639e2cc7099a21 |
|||
timeCreated: 1554301017 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
using Transform = Unity.UIWidgets.widgets.Transform; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
class UserAccountsDrawerHeaderUtils { |
|||
public const float _kAccountDetailsHeight = 56.0f; |
|||
} |
|||
|
|||
class _AccountPictures : StatelessWidget { |
|||
public _AccountPictures( |
|||
Key key = null, |
|||
Widget currentAccountPicture = null, |
|||
List<Widget> otherAccountsPictures = null |
|||
) : base(key: key) { |
|||
this.currentAccountPicture = currentAccountPicture; |
|||
this.otherAccountsPictures = otherAccountsPictures; |
|||
} |
|||
|
|||
public readonly Widget currentAccountPicture; |
|||
public readonly List<Widget> otherAccountsPictures; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new Stack( |
|||
children: new List<Widget> { |
|||
new Positioned( |
|||
top: 0.0f, |
|||
right: 0.0f, |
|||
child: new Row( |
|||
children: (this.otherAccountsPictures ?? new List<Widget> { }) |
|||
.GetRange(0, Math.Min(3, this.otherAccountsPictures?.Count ?? 0)) |
|||
.Select<Widget, Widget>( |
|||
(Widget picture) => { |
|||
return new Padding( |
|||
padding: EdgeInsets.only(left: 8.0f), |
|||
child: new Container( |
|||
padding: EdgeInsets.only(left: 8.0f, bottom: 8.0f), |
|||
width: 48.0f, |
|||
height: 48.0f, |
|||
child: picture |
|||
) |
|||
); |
|||
}).ToList() |
|||
) |
|||
), |
|||
new Positioned( |
|||
top: 0.0f, |
|||
child: new SizedBox( |
|||
width: 72.0f, |
|||
height: 72.0f, |
|||
child: this.currentAccountPicture |
|||
) |
|||
) |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _AccountDetails : StatefulWidget { |
|||
public _AccountDetails( |
|||
Key key = null, |
|||
Widget accountName = null, |
|||
Widget accountEmail = null, |
|||
VoidCallback onTap = null, |
|||
bool? isOpen = null |
|||
) : base(key: key) { |
|||
D.assert(accountName != null); |
|||
D.assert(accountEmail != null); |
|||
this.accountName = accountName; |
|||
this.accountEmail = accountEmail; |
|||
this.onTap = onTap; |
|||
this.isOpen = isOpen; |
|||
} |
|||
|
|||
public readonly Widget accountName; |
|||
public readonly Widget accountEmail; |
|||
public readonly VoidCallback onTap; |
|||
public readonly bool? isOpen; |
|||
|
|||
public override State createState() { |
|||
return new _AccountDetailsState(); |
|||
} |
|||
} |
|||
|
|||
class _AccountDetailsState : SingleTickerProviderStateMixin<_AccountDetails> { |
|||
Animation<float> _animation; |
|||
AnimationController _controller; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this._controller = new AnimationController( |
|||
value: this.widget.isOpen == true ? 1.0f : 0.0f, |
|||
duration: new TimeSpan(0, 0, 0, 0, 200), |
|||
vsync: this |
|||
); |
|||
this._animation = new CurvedAnimation( |
|||
parent: this._controller, |
|||
curve: Curves.fastOutSlowIn, |
|||
reverseCurve: Curves.fastOutSlowIn.flipped |
|||
); |
|||
this._animation.addListener(() => this.setState(() => { })); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this._controller.dispose(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget _oldWidget) { |
|||
base.didUpdateWidget(_oldWidget); |
|||
_AccountDetails oldWidget = _oldWidget as _AccountDetails; |
|||
if (this._animation.status == AnimationStatus.dismissed || |
|||
this._animation.status == AnimationStatus.reverse) { |
|||
this._controller.forward(); |
|||
} |
|||
else { |
|||
this._controller.reverse(); |
|||
} |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
D.assert(WidgetsD.debugCheckHasDirectionality(context)); |
|||
D.assert(MaterialD.debugCheckHasMaterialLocalizations(context)); |
|||
D.assert(MaterialD.debugCheckHasMaterialLocalizations(context)); |
|||
|
|||
ThemeData theme = Theme.of(context); |
|||
List<Widget> children = new List<Widget> { }; |
|||
|
|||
if (this.widget.accountName != null) { |
|||
Widget accountNameLine = new LayoutId( |
|||
id: _AccountDetailsLayout.accountName, |
|||
child: new Padding( |
|||
padding: EdgeInsets.symmetric(vertical: 2.0f), |
|||
child: new DefaultTextStyle( |
|||
style: theme.primaryTextTheme.body2, |
|||
overflow: TextOverflow.ellipsis, |
|||
child: this.widget.accountName |
|||
) |
|||
) |
|||
); |
|||
children.Add(accountNameLine); |
|||
} |
|||
|
|||
if (this.widget.accountEmail != null) { |
|||
Widget accountEmailLine = new LayoutId( |
|||
id: _AccountDetailsLayout.accountEmail, |
|||
child: new Padding( |
|||
padding: EdgeInsets.symmetric(vertical: 2.0f), |
|||
child: new DefaultTextStyle( |
|||
style: theme.primaryTextTheme.body1, |
|||
overflow: TextOverflow.ellipsis, |
|||
child: this.widget.accountEmail |
|||
) |
|||
) |
|||
); |
|||
children.Add(accountEmailLine); |
|||
} |
|||
|
|||
if (this.widget.onTap != null) { |
|||
MaterialLocalizations localizations = MaterialLocalizations.of(context); |
|||
Widget dropDownIcon = new LayoutId( |
|||
id: _AccountDetailsLayout.dropdownIcon, |
|||
child: new SizedBox( |
|||
height: UserAccountsDrawerHeaderUtils._kAccountDetailsHeight, |
|||
width: UserAccountsDrawerHeaderUtils._kAccountDetailsHeight, |
|||
child: new Center( |
|||
child: Transform.rotate( |
|||
degree: this._animation.value * Mathf.PI, |
|||
child: new Icon( |
|||
Icons.arrow_drop_down, |
|||
color: Colors.white |
|||
) |
|||
) |
|||
) |
|||
) |
|||
); |
|||
children.Add(dropDownIcon); |
|||
} |
|||
|
|||
Widget accountDetails = new CustomMultiChildLayout( |
|||
layoutDelegate: new _AccountDetailsLayout(), |
|||
children: children |
|||
); |
|||
|
|||
if (this.widget.onTap != null) { |
|||
accountDetails = new InkWell( |
|||
onTap: this.widget.onTap == null ? (GestureTapCallback) null : () => { this.widget.onTap(); }, |
|||
child: accountDetails |
|||
); |
|||
} |
|||
|
|||
return new SizedBox( |
|||
height: UserAccountsDrawerHeaderUtils._kAccountDetailsHeight, |
|||
child: accountDetails |
|||
); |
|||
} |
|||
} |
|||
|
|||
|
|||
class _AccountDetailsLayout : MultiChildLayoutDelegate { |
|||
public _AccountDetailsLayout() { |
|||
} |
|||
|
|||
public const string accountName = "accountName"; |
|||
public const string accountEmail = "accountEmail"; |
|||
public const string dropdownIcon = "dropdownIcon"; |
|||
|
|||
public override void performLayout(Size size) { |
|||
Size iconSize = null; |
|||
if (this.hasChild(dropdownIcon)) { |
|||
iconSize = this.layoutChild(dropdownIcon, BoxConstraints.loose(size)); |
|||
this.positionChild(dropdownIcon, this._offsetForIcon(size, iconSize)); |
|||
} |
|||
|
|||
string bottomLine = this.hasChild(accountEmail) |
|||
? accountEmail |
|||
: (this.hasChild(accountName) ? accountName : null); |
|||
|
|||
if (bottomLine != null) { |
|||
Size constraintSize = iconSize == null ? size : size - new Offset(iconSize.width, 0.0f); |
|||
iconSize = iconSize ?? new Size(UserAccountsDrawerHeaderUtils._kAccountDetailsHeight, |
|||
UserAccountsDrawerHeaderUtils._kAccountDetailsHeight); |
|||
|
|||
Size bottomLineSize = this.layoutChild(bottomLine, BoxConstraints.loose(constraintSize)); |
|||
Offset bottomLineOffset = this._offsetForBottomLine(size, iconSize, bottomLineSize); |
|||
this.positionChild(bottomLine, bottomLineOffset); |
|||
|
|||
if (bottomLine == accountEmail && this.hasChild(accountName)) { |
|||
Size nameSize = this.layoutChild(accountName, BoxConstraints.loose(constraintSize)); |
|||
this.positionChild(accountName, this._offsetForName(size, nameSize, bottomLineOffset)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) { |
|||
return true; |
|||
} |
|||
|
|||
Offset _offsetForIcon(Size size, Size iconSize) { |
|||
return new Offset(size.width - iconSize.width, size.height - iconSize.height); |
|||
} |
|||
|
|||
Offset _offsetForBottomLine(Size size, Size iconSize, Size bottomLineSize) { |
|||
float y = size.height - 0.5f * iconSize.height - 0.5f * bottomLineSize.height; |
|||
return new Offset(0.0f, y); |
|||
} |
|||
|
|||
Offset _offsetForName(Size size, Size nameSize, Offset bottomLineOffset) { |
|||
float y = bottomLineOffset.dy - nameSize.height; |
|||
return new Offset(0.0f, y); |
|||
} |
|||
} |
|||
|
|||
public class UserAccountsDrawerHeader : StatefulWidget { |
|||
public UserAccountsDrawerHeader( |
|||
Key key = null, |
|||
Decoration decoration = null, |
|||
EdgeInsets margin = null, |
|||
Widget currentAccountPicture = null, |
|||
List<Widget> otherAccountsPictures = null, |
|||
Widget accountName = null, |
|||
Widget accountEmail = null, |
|||
VoidCallback onDetailsPressed = null |
|||
) : base(key: key) { |
|||
D.assert(accountName != null); |
|||
D.assert(accountEmail != null); |
|||
this.decoration = decoration; |
|||
this.margin = margin ?? EdgeInsets.only(bottom: 8.0f); |
|||
this.currentAccountPicture = currentAccountPicture; |
|||
this.otherAccountsPictures = otherAccountsPictures; |
|||
this.accountName = accountName; |
|||
this.accountEmail = accountEmail; |
|||
this.onDetailsPressed = onDetailsPressed; |
|||
} |
|||
|
|||
public readonly Decoration decoration; |
|||
|
|||
public readonly EdgeInsets margin; |
|||
|
|||
public readonly Widget currentAccountPicture; |
|||
|
|||
public readonly List<Widget> otherAccountsPictures; |
|||
|
|||
public readonly Widget accountName; |
|||
|
|||
public readonly Widget accountEmail; |
|||
|
|||
public readonly VoidCallback onDetailsPressed; |
|||
|
|||
public override State createState() { |
|||
return new _UserAccountsDrawerHeaderState(); |
|||
} |
|||
} |
|||
|
|||
class _UserAccountsDrawerHeaderState : State<UserAccountsDrawerHeader> { |
|||
bool _isOpen = false; |
|||
|
|||
void _handleDetailsPressed() { |
|||
this.setState(() => { this._isOpen = !this._isOpen; }); |
|||
this.widget.onDetailsPressed(); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
D.assert(MaterialD.debugCheckHasMaterial(context)); |
|||
D.assert(MaterialD.debugCheckHasMaterialLocalizations(context)); |
|||
return new DrawerHeader( |
|||
decoration: this.widget.decoration ?? new BoxDecoration( |
|||
color: Theme.of(context).primaryColor |
|||
), |
|||
margin: this.widget.margin, |
|||
padding: EdgeInsets.only(top: 16.0f, left: 16.0f), |
|||
child: new SafeArea( |
|||
bottom: false, |
|||
child: new Column( |
|||
crossAxisAlignment: CrossAxisAlignment.stretch, |
|||
children: new List<Widget> { |
|||
new Expanded( |
|||
child: new Padding( |
|||
padding: EdgeInsets.only(right: 16.0f), |
|||
child: new _AccountPictures( |
|||
currentAccountPicture: this.widget.currentAccountPicture, |
|||
otherAccountsPictures: this.widget.otherAccountsPictures |
|||
) |
|||
) |
|||
), |
|||
new _AccountDetails( |
|||
accountName: this.widget.accountName, |
|||
accountEmail: this.widget.accountEmail, |
|||
isOpen: this._isOpen, |
|||
onTap: this.widget.onDetailsPressed == null |
|||
? (VoidCallback) null |
|||
: () => { this._handleDetailsPressed(); }) |
|||
} |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: fb34d1a7ff484d05b8074a10730d51a5 |
|||
timeCreated: 1554352270 |
|
|||
fileFormatVersion: 2 |
|||
guid: 3a7bb5e4fb043482e8bf223c2d3fa76c |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.async; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.physics; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.scheduler; |
|||
using Unity.UIWidgets.ui; |
|||
using UnityEngine; |
|||
using Canvas = Unity.UIWidgets.ui.Canvas; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
using Rect = Unity.UIWidgets.ui.Rect; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public class GlowingOverscrollIndicator : StatefulWidget { |
|||
public GlowingOverscrollIndicator( |
|||
Key key = null, |
|||
bool showLeading = true, |
|||
bool showTrailing = true, |
|||
AxisDirection axisDirection = AxisDirection.up, |
|||
Color color = null, |
|||
ScrollNotificationPredicate notificationPredicate = null, |
|||
Widget child = null |
|||
) : base(key: key) { |
|||
D.assert(color != null); |
|||
this.showLeading = showLeading; |
|||
this.showTrailing = showTrailing; |
|||
this.axisDirection = axisDirection; |
|||
this.child = child; |
|||
this.color = color; |
|||
this.notificationPredicate = notificationPredicate ?? ScrollNotification.defaultScrollNotificationPredicate; |
|||
} |
|||
|
|||
public readonly bool showLeading; |
|||
|
|||
public readonly bool showTrailing; |
|||
|
|||
public readonly AxisDirection axisDirection; |
|||
|
|||
public Axis axis { |
|||
get { return AxisUtils.axisDirectionToAxis(this.axisDirection); } |
|||
} |
|||
|
|||
public readonly Color color; |
|||
|
|||
public readonly ScrollNotificationPredicate notificationPredicate; |
|||
|
|||
public readonly Widget child; |
|||
|
|||
public override State createState() { |
|||
return new _GlowingOverscrollIndicatorState(); |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
properties.add(new EnumProperty<AxisDirection>("axisDirection", this.axisDirection)); |
|||
string showDescription; |
|||
if (this.showLeading && this.showTrailing) { |
|||
showDescription = "both sides"; |
|||
} |
|||
else if (this.showLeading) { |
|||
showDescription = "leading side only"; |
|||
} |
|||
else if (this.showTrailing) { |
|||
showDescription = "trailing side only"; |
|||
} |
|||
else { |
|||
showDescription = "neither side (!)"; |
|||
} |
|||
|
|||
properties.add(new MessageProperty("show", showDescription)); |
|||
properties.add(new DiagnosticsProperty<Color>("color", this.color, showName: false)); |
|||
} |
|||
} |
|||
|
|||
class _GlowingOverscrollIndicatorState : TickerProviderStateMixin<GlowingOverscrollIndicator> { |
|||
_GlowController _leadingController; |
|||
_GlowController _trailingController; |
|||
Listenable _leadingAndTrailingListener; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this._leadingController = |
|||
new _GlowController(vsync: this, color: this.widget.color, axis: this.widget.axis); |
|||
this._trailingController = |
|||
new _GlowController(vsync: this, color: this.widget.color, axis: this.widget.axis); |
|||
this._leadingAndTrailingListener = ListenableUtils.merge(new List<Listenable> |
|||
{this._leadingController, this._trailingController}); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget _oldWidget) { |
|||
base.didUpdateWidget(_oldWidget); |
|||
GlowingOverscrollIndicator oldWidget = _oldWidget as GlowingOverscrollIndicator; |
|||
if (oldWidget.color != this.widget.color || oldWidget.axis != this.widget.axis) { |
|||
this._leadingController.color = this.widget.color; |
|||
this._leadingController.axis = this.widget.axis; |
|||
this._trailingController.color = this.widget.color; |
|||
this._trailingController.axis = this.widget.axis; |
|||
} |
|||
} |
|||
|
|||
Type _lastNotificationType; |
|||
Dictionary<bool, bool> _accepted = new Dictionary<bool, bool> {{false, true}, {true, true}}; |
|||
|
|||
bool _handleScrollNotification(ScrollNotification notification) { |
|||
if (!this.widget.notificationPredicate(notification)) { |
|||
return false; |
|||
} |
|||
|
|||
if (notification is OverscrollNotification) { |
|||
_GlowController controller; |
|||
OverscrollNotification _notification = notification as OverscrollNotification; |
|||
if (_notification.overscroll < 0.0f) { |
|||
controller = this._leadingController; |
|||
} |
|||
else if (_notification.overscroll > 0.0f) { |
|||
controller = this._trailingController; |
|||
} |
|||
else { |
|||
throw new Exception("overscroll is 0.0f!"); |
|||
} |
|||
|
|||
bool isLeading = controller == this._leadingController; |
|||
if (this._lastNotificationType != typeof(OverscrollNotification)) { |
|||
OverscrollIndicatorNotification confirmationNotification = |
|||
new OverscrollIndicatorNotification(leading: isLeading); |
|||
confirmationNotification.dispatch(this.context); |
|||
this._accepted[isLeading] = confirmationNotification._accepted; |
|||
} |
|||
|
|||
D.assert(controller != null); |
|||
D.assert(_notification.metrics.axis() == this.widget.axis); |
|||
if (this._accepted[isLeading]) { |
|||
if (_notification.velocity != 0.0f) { |
|||
D.assert(_notification.dragDetails == null); |
|||
controller.absorbImpact(_notification.velocity.abs()); |
|||
} |
|||
else { |
|||
D.assert(_notification.overscroll != 0.0f); |
|||
if (_notification.dragDetails != null) { |
|||
D.assert(_notification.dragDetails.globalPosition != null); |
|||
RenderBox renderer = (RenderBox) _notification.context.findRenderObject(); |
|||
D.assert(renderer != null); |
|||
D.assert(renderer.hasSize); |
|||
Size size = renderer.size; |
|||
Offset position = renderer.globalToLocal(_notification.dragDetails.globalPosition); |
|||
switch (_notification.metrics.axis()) { |
|||
case Axis.horizontal: |
|||
controller.pull(_notification.overscroll.abs(), size.width, |
|||
position.dy.clamp(0.0f, size.height), size.height); |
|||
break; |
|||
case Axis.vertical: |
|||
controller.pull(_notification.overscroll.abs(), size.height, |
|||
position.dx.clamp(0.0f, size.width), size.width); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else if (notification is ScrollEndNotification || notification is ScrollUpdateNotification) { |
|||
if ((notification as ScrollEndNotification).dragDetails != null) { |
|||
this._leadingController.scrollEnd(); |
|||
this._trailingController.scrollEnd(); |
|||
} |
|||
} |
|||
|
|||
this._lastNotificationType = notification.GetType(); |
|||
return false; |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this._leadingController.dispose(); |
|||
this._trailingController.dispose(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new NotificationListener<ScrollNotification>( |
|||
onNotification: this._handleScrollNotification, |
|||
child: new RepaintBoundary( |
|||
child: new CustomPaint( |
|||
foregroundPainter: new _GlowingOverscrollIndicatorPainter( |
|||
leadingController: this.widget.showLeading ? this._leadingController : null, |
|||
trailingController: this.widget.showTrailing ? this._trailingController : null, |
|||
axisDirection: this.widget.axisDirection, |
|||
repaint: this._leadingAndTrailingListener |
|||
), |
|||
child: new RepaintBoundary( |
|||
child: this.widget.child |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
|
|||
enum _GlowState { |
|||
idle, |
|||
absorb, |
|||
pull, |
|||
recede |
|||
} |
|||
|
|||
class _GlowController : ChangeNotifier { |
|||
public _GlowController( |
|||
TickerProvider vsync, |
|||
Color color, |
|||
Axis axis |
|||
) { |
|||
D.assert(vsync != null); |
|||
D.assert(color != null); |
|||
this._color = color; |
|||
this._axis = axis; |
|||
this._glowController = new AnimationController(vsync: vsync); |
|||
this._glowController.addStatusListener(this._changePhase); |
|||
Animation<float> decelerator = new CurvedAnimation( |
|||
parent: this._glowController, |
|||
curve: Curves.decelerate |
|||
); |
|||
decelerator.addListener(this.notifyListeners); |
|||
this._glowOpacity = decelerator.drive(this._glowOpacityTween); |
|||
this._glowSize = decelerator.drive(this._glowSizeTween); |
|||
this._displacementTicker = vsync.createTicker(this._tickDisplacement); |
|||
} |
|||
|
|||
_GlowState _state = _GlowState.idle; |
|||
AnimationController _glowController; |
|||
Timer _pullRecedeTimer; |
|||
|
|||
FloatTween _glowOpacityTween = new FloatTween(begin: 0.0f, end: 0.0f); |
|||
Animation<float> _glowOpacity; |
|||
FloatTween _glowSizeTween = new FloatTween(begin: 0.0f, end: 0.0f); |
|||
Animation<float> _glowSize; |
|||
|
|||
Ticker _displacementTicker; |
|||
TimeSpan? _displacementTickerLastElapsed; |
|||
float _displacementTarget = 0.5f; |
|||
float _displacement = 0.5f; |
|||
|
|||
float _pullDistance = 0.0f; |
|||
|
|||
public Color color { |
|||
get { return this._color; } |
|||
set { |
|||
D.assert(this.color != null); |
|||
if (this.color == value) { |
|||
return; |
|||
} |
|||
|
|||
this._color = value; |
|||
this.notifyListeners(); |
|||
} |
|||
} |
|||
|
|||
Color _color; |
|||
|
|||
public Axis axis { |
|||
get { return this._axis; } |
|||
set { |
|||
if (this.axis == value) { |
|||
return; |
|||
} |
|||
|
|||
this._axis = value; |
|||
this.notifyListeners(); |
|||
} |
|||
} |
|||
|
|||
Axis _axis; |
|||
|
|||
readonly TimeSpan _recedeTime = new TimeSpan(0, 0, 0, 0, 600); |
|||
readonly TimeSpan _pullTime = new TimeSpan(0, 0, 0, 0, 167); |
|||
readonly TimeSpan _pullHoldTime = new TimeSpan(0, 0, 0, 0, 167); |
|||
readonly TimeSpan _pullDecayTime = new TimeSpan(0, 0, 0, 0, 2000); |
|||
static readonly TimeSpan _crossAxisHalfTime = new TimeSpan(0, 0, 0, 0, (1000.0f / 60.0f).round()); |
|||
|
|||
const float _maxOpacity = 0.5f; |
|||
const float _pullOpacityGlowFactor = 0.8f; |
|||
const float _velocityGlowFactor = 0.00006f; |
|||
const float _sqrt3 = 1.73205080757f; // Mathf.Sqrt(3)
|
|||
const float _widthToHeightFactor = (3.0f / 4.0f) * (2.0f - _sqrt3); |
|||
|
|||
const float _minVelocity = 100.0f; // logical pixels per second
|
|||
const float _maxVelocity = 10000.0f; // logical pixels per second
|
|||
|
|||
public override void dispose() { |
|||
this._glowController.dispose(); |
|||
this._displacementTicker.dispose(); |
|||
this._pullRecedeTimer?.cancel(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
public void absorbImpact(float velocity) { |
|||
D.assert(velocity >= 0.0f); |
|||
this._pullRecedeTimer?.cancel(); |
|||
this._pullRecedeTimer = null; |
|||
velocity = velocity.clamp(_minVelocity, _maxVelocity); |
|||
this._glowOpacityTween.begin = this._state == _GlowState.idle ? 0.3f : this._glowOpacity.value; |
|||
this._glowOpacityTween.end = |
|||
(velocity * _velocityGlowFactor).clamp(this._glowOpacityTween.begin, _maxOpacity); |
|||
this._glowSizeTween.begin = this._glowSize.value; |
|||
this._glowSizeTween.end = Mathf.Min(0.025f + 7.5e-7f * velocity * velocity, 1.0f); |
|||
this._glowController.duration = new TimeSpan(0, 0, 0, 0, (0.15f + velocity * 0.02f).round()); |
|||
this._glowController.forward(from: 0.0f); |
|||
this._displacement = 0.5f; |
|||
this._state = _GlowState.absorb; |
|||
} |
|||
|
|||
public void pull(float overscroll, float extent, float crossAxisOffset, float crossExtent) { |
|||
this._pullRecedeTimer?.cancel(); |
|||
this._pullDistance += |
|||
overscroll / 200.0f; // This factor is magic. Not clear why we need it to match Android.
|
|||
this._glowOpacityTween.begin = this._glowOpacity.value; |
|||
this._glowOpacityTween.end = |
|||
Mathf.Min(this._glowOpacity.value + overscroll / extent * _pullOpacityGlowFactor, _maxOpacity); |
|||
float height = Mathf.Min(extent, crossExtent * _widthToHeightFactor); |
|||
this._glowSizeTween.begin = this._glowSize.value; |
|||
this._glowSizeTween.end = Mathf.Max(1.0f - 1.0f / (0.7f * Mathf.Sqrt(this._pullDistance * height)), |
|||
this._glowSize.value); |
|||
this._displacementTarget = crossAxisOffset / crossExtent; |
|||
if (this._displacementTarget != this._displacement) { |
|||
if (!this._displacementTicker.isTicking) { |
|||
D.assert(this._displacementTickerLastElapsed == null); |
|||
this._displacementTicker.start(); |
|||
} |
|||
} |
|||
else { |
|||
this._displacementTicker.stop(); |
|||
this._displacementTickerLastElapsed = null; |
|||
} |
|||
|
|||
this._glowController.duration = this._pullTime; |
|||
if (this._state != _GlowState.pull) { |
|||
this._glowController.forward(from: 0.0f); |
|||
this._state = _GlowState.pull; |
|||
} |
|||
else { |
|||
if (!this._glowController.isAnimating) { |
|||
D.assert(this._glowController.value == 1.0f); |
|||
this.notifyListeners(); |
|||
} |
|||
} |
|||
|
|||
this._pullRecedeTimer = |
|||
new TimerProvider.TimerImpl(this._pullHoldTime, () => this._recede(this._pullDecayTime)); |
|||
} |
|||
|
|||
public void scrollEnd() { |
|||
if (this._state == _GlowState.pull) { |
|||
this._recede(this._recedeTime); |
|||
} |
|||
} |
|||
|
|||
void _changePhase(AnimationStatus status) { |
|||
if (status != AnimationStatus.completed) { |
|||
return; |
|||
} |
|||
|
|||
switch (this._state) { |
|||
case _GlowState.absorb: |
|||
this._recede(this._recedeTime); |
|||
break; |
|||
case _GlowState.recede: |
|||
this._state = _GlowState.idle; |
|||
this._pullDistance = 0.0f; |
|||
break; |
|||
case _GlowState.pull: |
|||
case _GlowState.idle: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
void _recede(TimeSpan duration) { |
|||
if (this._state == _GlowState.recede || this._state == _GlowState.idle) { |
|||
return; |
|||
} |
|||
|
|||
this._pullRecedeTimer?.cancel(); |
|||
this._pullRecedeTimer = null; |
|||
this._glowOpacityTween.begin = this._glowOpacity.value; |
|||
this._glowOpacityTween.end = 0.0f; |
|||
this._glowSizeTween.begin = this._glowSize.value; |
|||
this._glowSizeTween.end = 0.0f; |
|||
this._glowController.duration = duration; |
|||
this._glowController.forward(from: 0.0f); |
|||
this._state = _GlowState.recede; |
|||
} |
|||
|
|||
void _tickDisplacement(TimeSpan elapsed) { |
|||
if (this._displacementTickerLastElapsed != null) { |
|||
float? t = elapsed.Milliseconds - this._displacementTickerLastElapsed?.Milliseconds; |
|||
this._displacement = this._displacementTarget - (this._displacementTarget - this._displacement) * |
|||
Mathf.Pow(2.0f, (-t ?? 0.0f) / _crossAxisHalfTime.Milliseconds); |
|||
this.notifyListeners(); |
|||
} |
|||
|
|||
if (PhysicsUtils.nearEqual(this._displacementTarget, this._displacement, |
|||
Tolerance.defaultTolerance.distance)) { |
|||
this._displacementTicker.stop(); |
|||
this._displacementTickerLastElapsed = null; |
|||
} |
|||
else { |
|||
this._displacementTickerLastElapsed = elapsed; |
|||
} |
|||
} |
|||
|
|||
public void paint(Canvas canvas, Size size) { |
|||
if (this._glowOpacity.value == 0.0f) { |
|||
return; |
|||
} |
|||
|
|||
float baseGlowScale = size.width > size.height ? size.height / size.width : 1.0f; |
|||
float radius = size.width * 3.0f / 2.0f; |
|||
float height = Mathf.Min(size.height, size.width * _widthToHeightFactor); |
|||
float scaleY = this._glowSize.value * baseGlowScale; |
|||
Rect rect = Rect.fromLTWH(0.0f, 0.0f, size.width, height); |
|||
Offset center = new Offset((size.width / 2.0f) * (0.5f + this._displacement), height - radius); |
|||
Paint paint = new Paint(); |
|||
paint.color = this.color.withOpacity(this._glowOpacity.value); |
|||
canvas.save(); |
|||
canvas.scale(1.0f, scaleY); |
|||
canvas.clipRect(rect); |
|||
canvas.drawCircle(center, radius, paint); |
|||
canvas.restore(); |
|||
} |
|||
} |
|||
|
|||
class _GlowingOverscrollIndicatorPainter : AbstractCustomPainter { |
|||
public _GlowingOverscrollIndicatorPainter( |
|||
_GlowController leadingController, |
|||
_GlowController trailingController, |
|||
AxisDirection axisDirection, |
|||
Listenable repaint |
|||
) : base( |
|||
repaint: repaint |
|||
) { |
|||
this.leadingController = leadingController; |
|||
this.trailingController = trailingController; |
|||
this.axisDirection = axisDirection; |
|||
} |
|||
|
|||
public readonly _GlowController leadingController; |
|||
|
|||
public readonly _GlowController trailingController; |
|||
|
|||
public readonly AxisDirection axisDirection; |
|||
|
|||
const float piOver2 = Mathf.PI / 2.0f; |
|||
|
|||
void _paintSide(Canvas canvas, Size size, _GlowController controller, AxisDirection axisDirection, |
|||
GrowthDirection growthDirection) { |
|||
if (controller == null) { |
|||
return; |
|||
} |
|||
|
|||
switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection(axisDirection, growthDirection)) { |
|||
case AxisDirection.up: |
|||
controller.paint(canvas, size); |
|||
break; |
|||
case AxisDirection.down: |
|||
canvas.save(); |
|||
canvas.translate(0.0f, size.height); |
|||
canvas.scale(1.0f, -1.0f); |
|||
controller.paint(canvas, size); |
|||
canvas.restore(); |
|||
break; |
|||
case AxisDirection.left: |
|||
canvas.save(); |
|||
canvas.rotate(piOver2); |
|||
canvas.scale(1.0f, -1.0f); |
|||
controller.paint(canvas, new Size(size.height, size.width)); |
|||
canvas.restore(); |
|||
break; |
|||
case AxisDirection.right: |
|||
canvas.save(); |
|||
canvas.translate(size.width, 0.0f); |
|||
canvas.rotate(piOver2); |
|||
controller.paint(canvas, new Size(size.height, size.width)); |
|||
canvas.restore(); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
public override void paint(Canvas canvas, Size size) { |
|||
this._paintSide(canvas, size, this.leadingController, this.axisDirection, GrowthDirection.reverse); |
|||
this._paintSide(canvas, size, this.trailingController, this.axisDirection, GrowthDirection.forward); |
|||
} |
|||
|
|||
public override bool shouldRepaint(CustomPainter _oldDelegate) { |
|||
_GlowingOverscrollIndicatorPainter oldDelegate = _oldDelegate as _GlowingOverscrollIndicatorPainter; |
|||
return oldDelegate.leadingController != this.leadingController |
|||
|| oldDelegate.trailingController != this.trailingController; |
|||
} |
|||
} |
|||
|
|||
public class OverscrollIndicatorNotification : ViewportNotificationMixinNotification { |
|||
public OverscrollIndicatorNotification( |
|||
bool leading |
|||
) { |
|||
this.leading = leading; |
|||
} |
|||
|
|||
public readonly bool leading; |
|||
|
|||
internal bool _accepted = true; |
|||
|
|||
public void disallowGlow() { |
|||
this._accepted = false; |
|||
} |
|||
|
|||
protected override void debugFillDescription(List<string> description) { |
|||
base.debugFillDescription(description); |
|||
description.Add($"side: {(this.leading ? "leading edge" : "trailing edge")}"); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b87c58140c4b442e82a27b804f8502c7 |
|||
timeCreated: 1554348404 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.engine; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.material; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
using UnityEngine.Networking; |
|||
|
|||
public class HttpRequestSample : UIWidgetsPanel |
|||
{ |
|||
protected override Widget createWidget() { |
|||
return new MaterialApp( |
|||
title: "Http Request Sample", |
|||
home: new Scaffold( |
|||
body:new AsyncRequestWidget(this.gameObject) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
public class AsyncRequestWidget : StatefulWidget { |
|||
|
|||
public readonly GameObject gameObjOfUIWidgetsPanel; |
|||
|
|||
public AsyncRequestWidget(GameObject gameObjOfUiWidgetsPanel, Key key = null) : base(key) { |
|||
this.gameObjOfUIWidgetsPanel = gameObjOfUiWidgetsPanel; |
|||
} |
|||
|
|||
public override State createState() { |
|||
return new _AsyncRequestWidgetState(); |
|||
} |
|||
} |
|||
|
|||
[Serializable] |
|||
public class TimeData { |
|||
public long currentFileTime; |
|||
} |
|||
|
|||
class _AsyncRequestWidgetState : State<AsyncRequestWidget> { |
|||
|
|||
long _fileTime; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
|
|||
return new Column( |
|||
children: new List<Widget>() { |
|||
new FlatButton(child: new Text("Click To Get Time"), onPressed: () => { |
|||
UnityWebRequest www = UnityWebRequest.Get("http://worldclockapi.com/api/json/est/now"); |
|||
var asyncOperation = www.SendWebRequest(); |
|||
asyncOperation.completed += operation => { |
|||
var timeData = JsonUtility.FromJson<TimeData>(www.downloadHandler.text); |
|||
using(WindowProvider.of(this.widget.gameObjOfUIWidgetsPanel).getScope()) |
|||
{ |
|||
this.setState(() => { this._fileTime = timeData.currentFileTime; }); |
|||
} |
|||
|
|||
}; |
|||
}), |
|||
new Text($"current file time: {this._fileTime}") |
|||
}); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: cfa3e1cd78bb74aef90a7a0289dfc23c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.Redux { |
|||
public static class ReduxLogging { |
|||
public static Middleware<State> create<State>() { |
|||
return (store) => (next) => new DispatcherImpl((action) => { |
|||
var previousState = store.getState(); |
|||
var previousStateDump = JsonUtility.ToJson(previousState); |
|||
var result = next.dispatch(action); |
|||
var afterState = store.getState(); |
|||
var afterStateDump = JsonUtility.ToJson(afterState); |
|||
Debug.LogFormat("Action name={0} data={1}", action.ToString(), JsonUtility.ToJson(action)); |
|||
Debug.LogFormat("previousState=\n{0}", previousStateDump); |
|||
Debug.LogFormat("afterState=\n{0}", afterStateDump); |
|||
return result; |
|||
}); |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
|
|||
namespace Unity.UIWidgets.Redux { |
|||
public static class ReduxThunk { |
|||
public static Middleware<State> create<State>() { |
|||
return (store) => (next) => new DispatcherImpl((action) => { |
|||
var thunkAction = action as ThunkAction<State>; |
|||
if (thunkAction != null && thunkAction.action != null) { |
|||
return thunkAction.action(store.dispatcher, store.getState); |
|||
} |
|||
|
|||
return next.dispatch(action); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public sealed class ThunkAction<State> { |
|||
public readonly Func<Dispatcher, Func<State>, object> action; |
|||
public readonly string displayName; |
|||
|
|||
public ThunkAction( |
|||
Func<Dispatcher, Func<State>, object> action = null, |
|||
string displayName = null) { |
|||
this.action = action; |
|||
this.displayName = displayName ?? ""; |
|||
} |
|||
|
|||
public override string ToString() { |
|||
return "ThunkAction(" + this.displayName + ")"; |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Linq; |
|||
|
|||
namespace Unity.UIWidgets { |
|||
public interface Dispatcher { |
|||
T dispatch<T>(object action); |
|||
|
|||
object dispatch(object action); |
|||
} |
|||
|
|||
public class DispatcherImpl : Dispatcher { |
|||
readonly Func<object, object> _impl; |
|||
|
|||
public DispatcherImpl(Func<object, object> impl) { |
|||
this._impl = impl; |
|||
} |
|||
|
|||
public T dispatch<T>(object action) { |
|||
if (this._impl == null) { |
|||
return default; |
|||
} |
|||
|
|||
return (T) this._impl(action); |
|||
} |
|||
|
|||
public object dispatch(object action) { |
|||
if (this._impl == null) { |
|||
return default; |
|||
} |
|||
|
|||
return this._impl(action); |
|||
} |
|||
} |
|||
|
|||
public delegate State Reducer<State>(State previousState, object action); |
|||
|
|||
public delegate Func<Dispatcher, Dispatcher> Middleware<State>(Store<State> store); |
|||
|
|||
public delegate void StateChangedHandler<State>(State action); |
|||
|
|||
public class Store<State> { |
|||
public StateChangedHandler<State> stateChanged; |
|||
|
|||
readonly Dispatcher _dispatcher; |
|||
readonly Reducer<State> _reducer; |
|||
State _state; |
|||
|
|||
public Store( |
|||
Reducer<State> reducer, |
|||
State initialState = default, |
|||
params Middleware<State>[] middleware) { |
|||
this._reducer = reducer; |
|||
this._dispatcher = this._applyMiddleware(middleware); |
|||
this._state = initialState; |
|||
} |
|||
|
|||
public Dispatcher dispatcher { |
|||
get { return this._dispatcher; } |
|||
} |
|||
|
|||
public State getState() { |
|||
return this._state; |
|||
} |
|||
|
|||
Dispatcher _applyMiddleware(params Middleware<State>[] middleware) { |
|||
return middleware.Reverse().Aggregate<Middleware<State>, Dispatcher>( |
|||
new DispatcherImpl(this._innerDispatch), |
|||
(current, middlewareItem) => middlewareItem(this)(current)); |
|||
} |
|||
|
|||
object _innerDispatch(object action) { |
|||
this._state = this._reducer(this._state, action); |
|||
|
|||
if (this.stateChanged != null) { |
|||
this.stateChanged(this._state); |
|||
} |
|||
|
|||
return action; |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace Unity.UIWidgets.Redux { |
|||
public class StoreProvider<State> : InheritedWidget { |
|||
readonly Store<State> _store; |
|||
|
|||
public StoreProvider( |
|||
Store<State> store = null, |
|||
Widget child = null, |
|||
Key key = null) : base(key: key, child: child) { |
|||
D.assert(store != null); |
|||
D.assert(child != null); |
|||
this._store = store; |
|||
} |
|||
|
|||
public static Store<State> of(BuildContext context) { |
|||
var type = _typeOf<StoreProvider<State>>(); |
|||
StoreProvider<State> provider = context.inheritFromWidgetOfExactType(type) as StoreProvider<State>; |
|||
if (provider == null) { |
|||
throw new UIWidgetsError("StoreProvider is missing"); |
|||
} |
|||
|
|||
return provider._store; |
|||
} |
|||
|
|||
static Type _typeOf<T>() { |
|||
return typeof(T); |
|||
} |
|||
|
|||
public override bool updateShouldNotify(InheritedWidget old) { |
|||
return !Equals(this._store, ((StoreProvider<State>) old)._store); |
|||
} |
|||
} |
|||
|
|||
public delegate Widget ViewModelBuilder<ViewModel>(BuildContext context, ViewModel viewModel, Dispatcher dispatcher); |
|||
|
|||
public delegate ViewModel StoreConverter<State, ViewModel>(State state); |
|||
|
|||
public delegate bool ShouldRebuildCallback<ViewModel>(ViewModel previous, ViewModel current); |
|||
|
|||
public class StoreConnector<State, ViewModel> : StatelessWidget { |
|||
public readonly ViewModelBuilder<ViewModel> builder; |
|||
|
|||
public readonly StoreConverter<State, ViewModel> converter; |
|||
|
|||
public readonly ShouldRebuildCallback<ViewModel> shouldRebuild; |
|||
|
|||
public readonly bool pure; |
|||
|
|||
public StoreConnector( |
|||
ViewModelBuilder<ViewModel> builder = null, |
|||
StoreConverter<State, ViewModel> converter = null, |
|||
bool pure = false, |
|||
ShouldRebuildCallback<ViewModel> shouldRebuild = null, |
|||
Key key = null) : base(key) { |
|||
D.assert(builder != null); |
|||
D.assert(converter != null); |
|||
this.pure = pure; |
|||
this.builder = builder; |
|||
this.converter = converter; |
|||
this.shouldRebuild = shouldRebuild; |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new _StoreListener<State, ViewModel>( |
|||
store: StoreProvider<State>.of(context), |
|||
builder: this.builder, |
|||
converter: this.converter, |
|||
pure: this.pure, |
|||
shouldRebuild: this.shouldRebuild |
|||
); |
|||
} |
|||
} |
|||
|
|||
public class _StoreListener<State, ViewModel> : StatefulWidget { |
|||
public readonly ViewModelBuilder<ViewModel> builder; |
|||
|
|||
public readonly StoreConverter<State, ViewModel> converter; |
|||
|
|||
public readonly Store<State> store; |
|||
|
|||
public readonly ShouldRebuildCallback<ViewModel> shouldRebuild; |
|||
|
|||
public readonly bool pure; |
|||
|
|||
public _StoreListener( |
|||
ViewModelBuilder<ViewModel> builder = null, |
|||
StoreConverter<State, ViewModel> converter = null, |
|||
Store<State> store = null, |
|||
bool pure = false, |
|||
ShouldRebuildCallback<ViewModel> shouldRebuild = null, |
|||
Key key = null) : base(key) { |
|||
D.assert(builder != null); |
|||
D.assert(converter != null); |
|||
D.assert(store != null); |
|||
this.store = store; |
|||
this.builder = builder; |
|||
this.converter = converter; |
|||
this.pure = pure; |
|||
this.shouldRebuild = shouldRebuild; |
|||
} |
|||
|
|||
public override widgets.State createState() { |
|||
return new _StoreListenerState<State, ViewModel>(); |
|||
} |
|||
} |
|||
|
|||
class _StoreListenerState<State, ViewModel> : State<_StoreListener<State, ViewModel>> { |
|||
ViewModel latestValue; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this._init(); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this.widget.store.stateChanged -= this._handleStateChanged; |
|||
base.dispose(); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget oldWidget) { |
|||
var oldStore = ((_StoreListener<State, ViewModel>) oldWidget).store; |
|||
if (this.widget.store != oldStore) { |
|||
oldStore.stateChanged -= this._handleStateChanged; |
|||
this._init(); |
|||
} |
|||
|
|||
base.didUpdateWidget(oldWidget); |
|||
} |
|||
|
|||
void _init() { |
|||
this.widget.store.stateChanged += this._handleStateChanged; |
|||
this.latestValue = this.widget.converter(this.widget.store.getState()); |
|||
} |
|||
|
|||
void _handleStateChanged(State state) { |
|||
if (Window.hasInstance) { |
|||
this._innerStateChanged(state); |
|||
} |
|||
else { |
|||
using (WindowProvider.of(this.context).getScope()) { |
|||
this._innerStateChanged(state); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void _innerStateChanged(State state) { |
|||
var preValue = this.latestValue; |
|||
this.latestValue = this.widget.converter(this.widget.store.getState()); |
|||
if (this.widget.shouldRebuild != null) { |
|||
if (!this.widget.shouldRebuild(preValue, this.latestValue)) { |
|||
return; |
|||
} |
|||
} |
|||
else if (this.widget.pure) { |
|||
if (Equals(preValue, this.latestValue)) { |
|||
return; |
|||
} |
|||
} |
|||
|
|||
this.setState(); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return this.widget.builder(context, this.latestValue, this.widget.store.dispatcher); |
|||
} |
|||
} |
|||
} |
|
|||
using System.Linq; |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.Sample.Redux.ObjectFinder { |
|||
public class GameFinderMiddleware { |
|||
public static Middleware<FinderAppState> Create() { |
|||
return (store) => (next) => (action) => { |
|||
if (action is SearchAction) { |
|||
var searchAction = (SearchAction) action; |
|||
var objects = Object.FindObjectsOfType(typeof(FinderGameObject)).Where((obj) => { |
|||
return searchAction.keyword == "" || |
|||
obj.name.ToUpper().Contains(searchAction.keyword.ToUpper()); |
|||
}).Select(obj => new GameObjectInfo {id = obj.GetInstanceID(), name = obj.name}).ToList(); |
|||
|
|||
var result = next(action); |
|||
store.Dispatch(new SearchResultAction() {keyword = searchAction.keyword, results = objects}); |
|||
return result; |
|||
} |
|||
|
|||
return next(action); |
|||
}; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b6da4bbe4d8914b81a78b7d08ad4c7a4 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 5efe762e4ce804533b403e22eedb2e3b |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 7d78b565ea82c414a9c59aa6ccd9bd2c |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue