| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:sky' as sky; | 5 import 'dart:sky' as sky; |
| 6 | 6 |
| 7 import '../animation/animation_performance.dart'; | 7 import '../animation/animation_performance.dart'; |
| 8 import '../animation/curves.dart'; | 8 import '../animation/curves.dart'; |
| 9 import '../theme/shadows.dart'; | 9 import '../theme/shadows.dart'; |
| 10 import 'animated_component.dart'; | 10 import 'animated_component.dart'; |
| 11 import 'animated_container.dart'; |
| 11 import 'basic.dart'; | 12 import 'basic.dart'; |
| 12 import 'theme.dart'; | 13 import 'theme.dart'; |
| 13 | 14 |
| 14 // TODO(eseidel): Draw width should vary based on device size: | 15 // TODO(eseidel): Draw width should vary based on device size: |
| 15 // http://www.google.com/design/spec/layout/structure.html#structure-side-nav | 16 // http://www.google.com/design/spec/layout/structure.html#structure-side-nav |
| 16 | 17 |
| 17 // Mobile: | 18 // Mobile: |
| 18 // Width = Screen width − 56 dp | 19 // Width = Screen width − 56 dp |
| 19 // Maximum width: 320dp | 20 // Maximum width: 320dp |
| 20 // Maximum width applies only when using a left nav. When using a right nav, | 21 // Maximum width applies only when using a left nav. When using a right nav, |
| 21 // the panel can cover the full width of the screen. | 22 // the panel can cover the full width of the screen. |
| 22 | 23 |
| 23 // Desktop/Tablet: | 24 // Desktop/Tablet: |
| 24 // Maximum width for a left nav is 400dp. | 25 // Maximum width for a left nav is 400dp. |
| 25 // The right nav can vary depending on content. | 26 // The right nav can vary depending on content. |
| 26 | 27 |
| 27 const double _kWidth = 304.0; | 28 const double _kWidth = 304.0; |
| 28 const double _kMinFlingVelocity = 0.4; | 29 const double _kMinFlingVelocity = 0.4; |
| 29 const int _kBaseSettleDurationMS = 246; | 30 const Duration _kBaseSettleDuration = const Duration(milliseconds: 246); |
| 30 // TODO(mpcomplete): The curve must be linear if we want the drawer to track | 31 // TODO(mpcomplete): The curve must be linear if we want the drawer to track |
| 31 // the user's finger. Odeon remedies this by attaching spring forces to the | 32 // the user's finger. Odeon remedies this by attaching spring forces to the |
| 32 // initial timeline when animating (so it doesn't look linear). | 33 // initial timeline when animating (so it doesn't look linear). |
| 33 const Curve _kAnimationCurve = linear; | 34 const Curve _kAnimationCurve = linear; |
| 34 | 35 |
| 35 typedef void DrawerStatusChangeHandler (bool showing); | 36 typedef void DrawerStatusChangeHandler (bool showing); |
| 36 | 37 |
| 37 class DrawerController { | 38 class DrawerController { |
| 38 DrawerController(this.onStatusChange) { | 39 DrawerController(this.onStatusChange) { |
| 39 performance = new AnimationPerformance() | 40 container = new AnimatedContainer() |
| 40 ..duration = new Duration(milliseconds: _kBaseSettleDurationMS) | 41 ..position = new AnimatedType<Point>( |
| 41 ..variable = position; | 42 new Point(-_kWidth, 0.0), end: Point.origin, curve: _kAnimationCurve); |
| 43 performance = container.createPerformance( |
| 44 [AnimatedContainerSlots.position], |
| 45 duration: _kBaseSettleDuration); |
| 42 performance.timeline.onValueChanged.listen(_checkValue); | 46 performance.timeline.onValueChanged.listen(_checkValue); |
| 43 } | 47 } |
| 44 final DrawerStatusChangeHandler onStatusChange; | 48 final DrawerStatusChangeHandler onStatusChange; |
| 45 | 49 |
| 46 AnimationPerformance performance; | 50 AnimationPerformance performance; |
| 47 final AnimatedPosition position = new AnimatedPosition( | 51 AnimatedContainer container; |
| 48 new Point(-_kWidth, 0.0), Point.origin, curve: _kAnimationCurve); | 52 |
| 53 double get xPosition => container.position.value.x; |
| 49 | 54 |
| 50 bool _oldClosedState = true; | 55 bool _oldClosedState = true; |
| 51 void _checkValue(_) { | 56 void _checkValue(_) { |
| 52 var newClosedState = isClosed; | 57 var newClosedState = isClosed; |
| 53 if (onStatusChange != null && _oldClosedState != newClosedState) { | 58 if (onStatusChange != null && _oldClosedState != newClosedState) { |
| 54 onStatusChange(!newClosedState); | 59 onStatusChange(!newClosedState); |
| 55 _oldClosedState = newClosedState; | 60 _oldClosedState = newClosedState; |
| 56 } | 61 } |
| 57 } | 62 } |
| 58 | 63 |
| 59 bool get isClosed => performance.isDismissed; | 64 bool get isClosed => performance.isDismissed; |
| 60 bool get _isMostlyClosed => position.value.x <= -_kWidth/2; | 65 bool get _isMostlyClosed => xPosition <= -_kWidth/2; |
| 61 | 66 |
| 62 void open() => performance.play(); | 67 void open() => performance.play(); |
| 63 | 68 |
| 64 void close() => performance.reverse(); | 69 void close() => performance.reverse(); |
| 65 | 70 |
| 66 void _settle() => _isMostlyClosed ? close() : open(); | 71 void _settle() => _isMostlyClosed ? close() : open(); |
| 67 | 72 |
| 68 void handleMaskTap(_) => close(); | 73 void handleMaskTap(_) => close(); |
| 69 | 74 |
| 70 // TODO(mpcomplete): Figure out how to generalize these handlers on a | 75 // TODO(mpcomplete): Figure out how to generalize these handlers on a |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 121 Widget build() { | 126 Widget build() { |
| 122 // TODO(mpcomplete): animate as a fade-in. | 127 // TODO(mpcomplete): animate as a fade-in. |
| 123 double scaler = controller.performance.progress; | 128 double scaler = controller.performance.progress; |
| 124 Color maskColor = new Color.fromARGB((0x7F * scaler).floor(), 0, 0, 0); | 129 Color maskColor = new Color.fromARGB((0x7F * scaler).floor(), 0, 0, 0); |
| 125 | 130 |
| 126 var mask = new Listener( | 131 var mask = new Listener( |
| 127 child: new Container(decoration: new BoxDecoration(backgroundColor: maskCo
lor)), | 132 child: new Container(decoration: new BoxDecoration(backgroundColor: maskCo
lor)), |
| 128 onGestureTap: controller.handleMaskTap | 133 onGestureTap: controller.handleMaskTap |
| 129 ); | 134 ); |
| 130 | 135 |
| 131 Widget content = controller.position.build( | 136 Widget content = controller.container.build( |
| 132 new Container( | 137 new Container( |
| 133 decoration: new BoxDecoration( | 138 decoration: new BoxDecoration( |
| 134 backgroundColor: Theme.of(this).canvasColor, | 139 backgroundColor: Theme.of(this).canvasColor, |
| 135 boxShadow: shadows[level]), | 140 boxShadow: shadows[level]), |
| 136 width: _kWidth, | 141 width: _kWidth, |
| 137 child: new Block(children) | 142 child: new Block(children) |
| 138 )); | 143 )); |
| 139 | 144 |
| 140 return new Listener( | 145 return new Listener( |
| 141 child: new Stack([ mask, content ]), | 146 child: new Stack([ mask, content ]), |
| 142 onPointerDown: controller.handlePointerDown, | 147 onPointerDown: controller.handlePointerDown, |
| 143 onPointerMove: controller.handlePointerMove, | 148 onPointerMove: controller.handlePointerMove, |
| 144 onPointerUp: controller.handlePointerUp, | 149 onPointerUp: controller.handlePointerUp, |
| 145 onPointerCancel: controller.handlePointerCancel, | 150 onPointerCancel: controller.handlePointerCancel, |
| 146 onGestureFlingStart: controller.handleFlingStart | 151 onGestureFlingStart: controller.handleFlingStart |
| 147 ); | 152 ); |
| 148 } | 153 } |
| 149 | 154 |
| 150 } | 155 } |
| OLD | NEW |