Commit 2051703c authored by mpcomplete's avatar mpcomplete

Merge pull request #463 from mpcomplete/popup.menu

Push PopupMenu's animation code into its build function and use trans…
parents fd68741f d89fb146
......@@ -15,15 +15,15 @@ abstract class AnimatedVariable {
String toString();
}
abstract class CurvedVariable implements AnimatedVariable {
CurvedVariable({this.interval, this.reverseInterval, this.curve, this.reverseCurve});
class AnimationTiming {
AnimationTiming({this.interval, this.reverseInterval, this.curve, this.reverseCurve});
Interval interval;
Interval reverseInterval;
Curve curve;
Curve reverseCurve;
double _transform(double t, Direction direction) {
double transform(double t, Direction direction) {
Interval interval = _getInterval(direction);
if (interval != null)
t = interval.transform(t);
......@@ -48,7 +48,7 @@ abstract class CurvedVariable implements AnimatedVariable {
}
}
class AnimatedValue<T extends dynamic> extends CurvedVariable {
class AnimatedValue<T extends dynamic> extends AnimationTiming implements AnimatedVariable {
AnimatedValue(this.begin, { this.end, Interval interval, Curve curve, Curve reverseCurve })
: super(interval: interval, curve: curve, reverseCurve: reverseCurve) {
value = begin;
......@@ -62,7 +62,7 @@ class AnimatedValue<T extends dynamic> extends CurvedVariable {
void setProgress(double t, Direction direction) {
if (end != null) {
t = _transform(t, direction);
t = transform(t, direction);
value = (t == 1.0) ? end : lerp(t);
}
}
......@@ -70,14 +70,14 @@ class AnimatedValue<T extends dynamic> extends CurvedVariable {
String toString() => 'AnimatedValue(begin=$begin, end=$end, value=$value)';
}
class AnimatedList extends CurvedVariable {
class AnimatedList extends AnimationTiming implements AnimatedVariable {
List<AnimatedVariable> variables;
AnimatedList(this.variables, { Interval interval, Curve curve, Curve reverseCurve })
: super(interval: interval, curve: curve, reverseCurve: reverseCurve);
void setProgress(double t, Direction direction) {
double adjustedTime = _transform(t, direction);
double adjustedTime = transform(t, direction);
for (AnimatedVariable variable in variables)
variable.setProgress(adjustedTime, direction);
}
......
......@@ -46,6 +46,8 @@ class AnimationPerformance {
Direction _curveDirection;
Direction get curveDirection => _curveDirection;
AnimationTiming timing;
// If non-null, animate with this force instead of a tween animation.
Force attachedForce;
......@@ -67,6 +69,10 @@ class AnimationPerformance {
_checkStatusChanged();
}
double get curvedProgress {
return timing != null ? timing.transform(progress, curveDirection) : progress;
}
bool get isDismissed => status == AnimationStatus.dismissed;
bool get isCompleted => status == AnimationStatus.completed;
bool get isAnimating => timeline.isAnimating;
......@@ -82,7 +88,7 @@ class AnimationPerformance {
}
void updateVariable(AnimatedVariable variable) {
variable.setProgress(progress, curveDirection);
variable.setProgress(curvedProgress, curveDirection);
}
Future play([Direction direction = Direction.forward]) {
......@@ -165,7 +171,7 @@ class AnimationPerformance {
void _tick(double t) {
_updateCurveDirection();
if (variable != null)
variable.setProgress(t, curveDirection);
variable.setProgress(curvedProgress, curveDirection);
_notifyListeners();
_checkStatusChanged();
}
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'dart:async';
import 'dart:sky' as sky;
import 'package:sky/animation/animated_value.dart';
......@@ -10,11 +10,11 @@ import 'package:sky/animation/animation_performance.dart';
import 'package:sky/painting/box_painter.dart';
import 'package:sky/theme/colors.dart';
import 'package:sky/theme/shadows.dart';
import 'package:sky/widgets/animated_component.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/navigator.dart';
import 'package:sky/widgets/popup_menu_item.dart';
import 'package:sky/widgets/scrollable.dart';
import 'package:sky/widgets/transitions.dart';
export 'package:sky/animation/animation_performance.dart' show AnimationStatus;
......@@ -29,7 +29,7 @@ const double _kMenuVerticalPadding = 8.0;
typedef void PopupMenuStatusChangedCallback(AnimationStatus status);
class PopupMenu extends AnimatedComponent {
class PopupMenu extends StatefulComponent {
PopupMenu({
Key key,
......@@ -46,63 +46,42 @@ class PopupMenu extends AnimatedComponent {
int level;
Navigator navigator;
AnimatedValue<double> _opacity;
AnimatedValue<double> _width;
AnimatedValue<double> _height;
List<AnimatedValue<double>> _itemOpacities;
AnimationPerformance _performance;
void initState() {
_performance = new AnimationPerformance()
..duration = _kMenuDuration
..addStatusListener(_onStatusChanged);
_updateAnimationVariables();
watch(_performance);
..duration = _kMenuDuration;
_performance.timing = new AnimationTiming()
..reverseInterval = new Interval(0.0, _kMenuCloseIntervalEnd);
_updateBoxPainter();
if (showing)
_open();
}
void syncFields(PopupMenu source) {
if (showing != source.showing) {
showing = source.showing;
if (showing)
if (!showing && source.showing)
_open();
else
_close();
}
showing = source.showing;
onStatusChanged = source.onStatusChanged;
if (level != source.level) {
level = source.level;
_updateBoxPainter();
}
if (items.length != source.items.length)
_updateAnimationVariables();
items = source.items;
navigator = source.navigator;
super.syncFields(source);
}
void _updateAnimationVariables() {
double unit = 1.0 / (items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade.
_opacity = new AnimatedValue<double>(0.0, end: 1.0);
_width = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit));
_height = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit * items.length));
_itemOpacities = new List<AnimatedValue<double>>();
for (int i = 0; i < items.length; ++i) {
double start = (i + 1) * unit;
double end = (start + 1.5 * unit).clamp(0.0, 1.0);
_itemOpacities.add(new AnimatedValue<double>(
0.0, end: 1.0, interval: new Interval(start, end)));
void _open() {
if (navigator != null) {
scheduleMicrotask(() {
navigator.pushState(this, (_) => _close());
});
}
List<AnimatedVariable> variables = new List<AnimatedVariable>()
..add(_opacity)
..add(_width)
..add(_height)
..addAll(_itemOpacities);
AnimatedList list = new AnimatedList(variables)
..reverseInterval = new Interval(0.0, _kMenuCloseIntervalEnd);
_performance.variable = list;
}
void _close() {
_performance.reverse();
}
void _updateBoxPainter() {
......@@ -112,44 +91,49 @@ class PopupMenu extends AnimatedComponent {
boxShadow: shadows[level]));
}
void _onStatusChanged(AnimationStatus status) {
if (status == AnimationStatus.dismissed &&
navigator != null &&
void _onDismissed() {
if (navigator != null &&
navigator.currentRoute is RouteState &&
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
navigator.pop();
if (onStatusChanged != null)
onStatusChanged(status);
}
void _open() {
_performance.play();
if (navigator != null)
navigator.pushState(this, (_) => _close());
}
void _close() {
_performance.reverse();
onStatusChanged(AnimationStatus.dismissed);
}
BoxPainter _painter;
Widget build() {
int i = 0;
List<Widget> children = new List.from(items.map((Widget item) {
return new Opacity(opacity: _itemOpacities[i++].value, child: item);
}));
double unit = 1.0 / (items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade.
List<Widget> children = [];
for (int i = 0; i < items.length; ++i) {
double start = (i + 1) * unit;
double end = (start + 1.5 * unit).clamp(0.0, 1.0);
children.add(new FadeTransition(
direction: showing ? Direction.forward : Direction.reverse,
performance: _performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(start, end)),
child: items[i]));
}
return new Opacity(
opacity: math.min(1.0, _opacity.value * 3.0),
final width = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit));
final height = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit * items.length));
return new FadeTransition(
direction: showing ? Direction.forward : Direction.reverse,
performance: _performance,
onDismissed: _onDismissed,
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, 1.0 / 3.0)),
child: new Container(
margin: new EdgeDims.all(_kMenuMargin),
child: new CustomPaint(
child: new BuilderTransition(
direction: showing ? Direction.forward : Direction.reverse,
performance: _performance,
variables: [width, height],
builder: () {
return new CustomPaint(
callback: (sky.Canvas canvas, Size size) {
double width = _width.value * size.width;
double height = _height.value * size.height;
_painter.paint(canvas, new Rect.fromLTWH(size.width - width, 0.0, width, height));
double widthValue = width.value * size.width;
double heightValue = height.value * size.height;
_painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue));
},
child: new ConstrainedBox(
constraints: new BoxConstraints(
......@@ -169,6 +153,8 @@ class PopupMenu extends AnimatedComponent {
)
)
)
);
}
)
)
);
......
......@@ -92,9 +92,9 @@ class SlideTransition extends TransitionBase {
AnimatedValue<Point> position;
void syncFields(SlideTransition updated) {
position = updated.position;
super.syncFields(updated);
void syncFields(SlideTransition source) {
position = source.position;
super.syncFields(source);
}
Widget build() {
......@@ -125,9 +125,9 @@ class FadeTransition extends TransitionBase {
AnimatedValue<double> opacity;
void syncFields(FadeTransition updated) {
opacity = updated.opacity;
super.syncFields(updated);
void syncFields(FadeTransition source) {
opacity = source.opacity;
super.syncFields(source);
}
Widget build() {
......@@ -156,9 +156,9 @@ class ColorTransition extends TransitionBase {
AnimatedColorValue color;
void syncFields(ColorTransition updated) {
color = updated.color;
super.syncFields(updated);
void syncFields(ColorTransition source) {
color = source.color;
super.syncFields(source);
}
Widget build() {
......@@ -192,10 +192,10 @@ class SquashTransition extends TransitionBase {
AnimatedValue<double> width;
AnimatedValue<double> height;
void syncFields(SquashTransition updated) {
width = updated.width;
height = updated.height;
super.syncFields(updated);
void syncFields(SquashTransition source) {
width = source.width;
height = source.height;
super.syncFields(source);
}
Widget build() {
......@@ -206,3 +206,40 @@ class SquashTransition extends TransitionBase {
return new SizedBox(width: _maybe(width), height: _maybe(height), child: child);
}
}
typedef Widget BuilderFunction();
class BuilderTransition extends TransitionBase {
BuilderTransition({
Key key,
this.variables,
this.builder,
Duration duration,
AnimationPerformance performance,
Direction direction,
Function onDismissed,
Function onCompleted,
Widget child
}) : super(key: key,
duration: duration,
performance: performance,
direction: direction,
onDismissed: onDismissed,
onCompleted: onCompleted,
child: child);
List<AnimatedValue> variables;
BuilderFunction builder;
void syncFields(BuilderTransition source) {
variables = source.variables;
builder = source.builder;
super.syncFields(source);
}
Widget build() {
for (int i = 0; i < variables.length; ++i)
performance.updateVariable(variables[i]);
return builder();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment