Commit d46aff8a authored by Ian Hickson's avatar Ian Hickson

Merge pull request #750 from Hixie/yak3-performance-view-listener-refactor

Refactor PerformanceView listeners.
parents 0d6a564a 4ba074f6
......@@ -8,10 +8,11 @@
library animation;
export 'src/animation/animated_value.dart';
export 'src/animation/performance.dart';
export 'src/animation/clamped_simulation.dart';
export 'src/animation/curves.dart';
export 'src/animation/forces.dart';
export 'src/animation/listener_helpers.dart';
export 'src/animation/performance.dart';
export 'src/animation/scroll_behavior.dart';
export 'src/animation/simulation_stepper.dart';
export 'src/animation/ticker.dart';
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show VoidCallback;
/// The status of an animation
enum PerformanceStatus {
/// The animation is stopped at the beginning
dismissed,
/// The animation is running from beginning to end
forward,
/// The animation is running backwards, from end to beginning
reverse,
/// The animation is stopped at the end
completed,
}
typedef void PerformanceStatusListener(PerformanceStatus status);
abstract class _ListenerMixin {
void didRegisterListener();
void didUnregisterListener();
}
abstract class LazyListenerMixin implements _ListenerMixin {
int _listenerCounter = 0;
void didRegisterListener() {
assert(_listenerCounter >= 0);
if (_listenerCounter == 0)
didStartListening();
_listenerCounter += 1;
}
void didUnregisterListener() {
assert(_listenerCounter >= 1);
_listenerCounter -= 1;
if (_listenerCounter == 0)
didStopListening();
}
void didStartListening();
void didStopListening();
bool get isListening => _listenerCounter > 0;
}
abstract class EagerListenerMixin implements _ListenerMixin {
void didRegisterListener() { }
void didUnregisterListener() { }
/// Release any resources used by this object.
void dispose();
}
abstract class LocalPerformanceListenersMixin extends _ListenerMixin {
final List<VoidCallback> _listeners = <VoidCallback>[];
void addListener(VoidCallback listener) {
didRegisterListener();
_listeners.add(listener);
}
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
didUnregisterListener();
}
void notifyListeners() {
List<VoidCallback> localListeners = new List<VoidCallback>.from(_listeners);
for (VoidCallback listener in localListeners)
listener();
}
}
abstract class LocalPerformanceStatusListenersMixin extends _ListenerMixin {
final List<PerformanceStatusListener> _statusListeners = <PerformanceStatusListener>[];
void addStatusListener(PerformanceStatusListener listener) {
didRegisterListener();
_statusListeners.add(listener);
}
void removeStatusListener(PerformanceStatusListener listener) {
_statusListeners.remove(listener);
didUnregisterListener();
}
void notifyStatusListeners(PerformanceStatus status) {
List<PerformanceStatusListener> localListeners = new List<PerformanceStatusListener>.from(_statusListeners);
for (PerformanceStatusListener listener in localListeners)
listener(status);
}
}
......@@ -10,23 +10,7 @@ import 'package:newton/newton.dart';
import 'animated_value.dart';
import 'forces.dart';
import 'simulation_stepper.dart';
/// The status of an animation
enum PerformanceStatus {
/// The animation is stopped at the beginning
dismissed,
/// The animation is running from beginning to end
forward,
/// The animation is running backwards, from end to beginning
reverse,
/// The animation is stopped at the end
completed,
}
typedef void PerformanceStatusListener(PerformanceStatus status);
import 'listener_helpers.dart';
/// An interface that is implemented by [Performance] that exposes a
/// read-only view of the underlying performance. This is used by classes that
......@@ -89,10 +73,9 @@ class AlwaysCompletePerformance extends PerformanceView {
}
const AlwaysCompletePerformance alwaysCompletePerformance = const AlwaysCompletePerformance();
class ReversePerformance extends PerformanceView {
ReversePerformance(this.masterPerformance) {
masterPerformance.addStatusListener(_statusChangeHandler);
}
class ReversePerformance extends PerformanceView
with LazyListenerMixin, LocalPerformanceStatusListenersMixin {
ReversePerformance(this.masterPerformance);
final PerformanceView masterPerformance;
......@@ -101,27 +84,24 @@ class ReversePerformance extends PerformanceView {
}
void addListener(VoidCallback listener) {
didRegisterListener();
masterPerformance.addListener(listener);
}
void removeListener(VoidCallback listener) {
masterPerformance.removeListener(listener);
didUnregisterListener();
}
final List<PerformanceStatusListener> _statusListeners = new List<PerformanceStatusListener>();
void addStatusListener(PerformanceStatusListener listener) {
_statusListeners.add(listener);
void didStartListening() {
masterPerformance.addStatusListener(_statusChangeHandler);
}
void removeStatusListener(PerformanceStatusListener listener) {
_statusListeners.remove(listener);
void didStopListening() {
masterPerformance.removeStatusListener(_statusChangeHandler);
}
void _statusChangeHandler(PerformanceStatus status) {
status = _reverseStatus(status);
List<PerformanceStatusListener> localListeners = new List<PerformanceStatusListener>.from(_statusListeners);
for (PerformanceStatusListener listener in localListeners)
listener(status);
notifyStatusListeners(_reverseStatus(status));
}
PerformanceStatus get status => _reverseStatus(masterPerformance.status);
......@@ -153,11 +133,16 @@ enum _TrainHoppingMode { minimize, maximize }
/// going in the opposite direction, or because the one overtakes the other),
/// the performance hops over to proxying the second performance, and the second
/// performance becomes the new "first" performance.
class TrainHoppingPerformance extends PerformanceView {
///
/// Since this object must track the two performances even when it has no
/// listeners of its own, instead of shutting down when all its listeners are
/// removed, it exposes a [dispose()] method. Call this method to shut this
/// object down.
class TrainHoppingPerformance extends PerformanceView
with EagerListenerMixin, LocalPerformanceListenersMixin, LocalPerformanceStatusListenersMixin {
TrainHoppingPerformance(this._currentTrain, this._nextTrain, { this.onSwitchedTrain }) {
assert(_currentTrain != null);
if (_nextTrain != null) {
if (_currentTrain.progress > _nextTrain.progress) {
_mode = _TrainHoppingMode.maximize;
} else {
......@@ -187,37 +172,11 @@ class TrainHoppingPerformance extends PerformanceView {
variable.setProgress(progress, curveDirection);
}
final List<VoidCallback> _listeners = new List<VoidCallback>();
void addListener(VoidCallback listener) {
assert(_currentTrain != null);
_listeners.add(listener);
}
void removeListener(VoidCallback listener) {
assert(_currentTrain != null);
_listeners.remove(listener);
}
final List<PerformanceStatusListener> _statusListeners = new List<PerformanceStatusListener>();
void addStatusListener(PerformanceStatusListener listener) {
assert(_currentTrain != null);
_statusListeners.add(listener);
}
void removeStatusListener(PerformanceStatusListener listener) {
assert(_currentTrain != null);
_statusListeners.remove(listener);
}
PerformanceStatus _lastStatus;
void _statusChangeHandler(PerformanceStatus status) {
assert(_currentTrain != null);
if (status != _lastStatus) {
List<PerformanceStatusListener> localListeners = new List<PerformanceStatusListener>.from(_statusListeners);
for (PerformanceStatusListener listener in localListeners)
listener(status);
notifyListeners();
_lastStatus = status;
}
assert(_lastStatus != null);
......@@ -250,9 +209,7 @@ class TrainHoppingPerformance extends PerformanceView {
}
double newProgress = progress;
if (newProgress != _lastProgress) {
List<VoidCallback> localListeners = new List<VoidCallback>.from(_listeners);
for (VoidCallback listener in localListeners)
listener();
notifyListeners();
_lastProgress = newProgress;
}
assert(_lastProgress != null);
......@@ -276,107 +233,81 @@ class TrainHoppingPerformance extends PerformanceView {
}
}
class ProxyPerformance extends PerformanceView {
class ProxyPerformance extends PerformanceView
with LazyListenerMixin, LocalPerformanceListenersMixin, LocalPerformanceStatusListenersMixin {
ProxyPerformance([PerformanceView performance]) {
masterPerformance = performance;
_masterPerformance = performance;
if (_masterPerformance == null) {
_status = PerformanceStatus.dismissed;
_direction = AnimationDirection.forward;
_curveDirection = AnimationDirection.forward;
_progress = 0.0;
}
}
PerformanceStatus _status;
AnimationDirection _direction;
AnimationDirection _curveDirection;
double _progress;
PerformanceView get masterPerformance => _masterPerformance;
PerformanceView _masterPerformance;
void set masterPerformance(PerformanceView value) {
if (value == _masterPerformance)
return;
if (_masterPerformance != null) {
_masterPerformance.removeStatusListener(_statusChangeHandler);
_masterPerformance.removeListener(_valueChangeHandler);
_status = _masterPerformance.status;
_direction = _masterPerformance.direction;
_curveDirection = _masterPerformance.curveDirection;
_progress = _masterPerformance.progress;
if (isListening)
didStopListening();
}
_masterPerformance = value;
if (_masterPerformance != null) {
_masterPerformance.addListener(_valueChangeHandler);
_masterPerformance.addStatusListener(_statusChangeHandler);
_valueChangeHandler();
_statusChangeHandler(_masterPerformance.status);
if (isListening)
didStartListening();
if (_progress != _masterPerformance.progress)
notifyListeners();
if (_status != _masterPerformance.status)
notifyStatusListeners(_masterPerformance.status);
_status = null;
_direction = null;
_curveDirection = null;
_progress = null;
}
}
void updateVariable(Animatable variable) {
variable.setProgress(progress, curveDirection);
}
final List<VoidCallback> _listeners = new List<VoidCallback>();
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
}
final List<PerformanceStatusListener> _statusListeners = new List<PerformanceStatusListener>();
void addStatusListener(PerformanceStatusListener listener) {
_statusListeners.add(listener);
}
void removeStatusListener(PerformanceStatusListener listener) {
_statusListeners.remove(listener);
void didStartListening() {
if (_masterPerformance != null) {
_masterPerformance.addListener(_valueChangeHandler);
_masterPerformance.addStatusListener(_statusChangeHandler);
}
}
PerformanceStatus _status = PerformanceStatus.dismissed;
AnimationDirection _direction = AnimationDirection.forward;
AnimationDirection _curveDirection = AnimationDirection.forward;
void _statusChangeHandler(PerformanceStatus status) {
assert(_masterPerformance != null);
if (status != _status) {
_status = status;
_direction = _masterPerformance.direction;
List<PerformanceStatusListener> localListeners = new List<PerformanceStatusListener>.from(_statusListeners);
for (PerformanceStatusListener listener in localListeners)
listener(status);
void didStopListening() {
if (_masterPerformance != null) {
_masterPerformance.removeListener(_valueChangeHandler);
_masterPerformance.removeStatusListener(_statusChangeHandler);
}
}
PerformanceStatus get status => _status;
AnimationDirection get direction => _direction;
AnimationDirection get curveDirection => _curveDirection;
double _progress = 0.0;
void _valueChangeHandler() {
assert(_masterPerformance != null);
double newProgress = _masterPerformance.progress;
if (newProgress != _progress) {
_progress = newProgress;
_curveDirection = _masterPerformance.curveDirection;
List<VoidCallback> localListeners = new List<VoidCallback>.from(_listeners);
for (VoidCallback listener in localListeners)
listener();
}
notifyListeners();
}
double get progress => _progress;
}
class _RepeatingSimulation extends Simulation {
_RepeatingSimulation(this.min, this.max, Duration period)
: _periodInSeconds = period.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND {
assert(_periodInSeconds > 0.0);
void _statusChangeHandler(PerformanceStatus status) {
notifyStatusListeners(status);
}
final double min;
final double max;
final double _periodInSeconds;
double x(double timeInSeconds) {
assert(timeInSeconds >= 0.0);
final double t = (timeInSeconds / _periodInSeconds) % 1.0;
return lerpDouble(min, max, t);
void updateVariable(Animatable variable) {
variable.setProgress(progress, curveDirection);
}
double dx(double timeInSeconds) => 1.0;
bool isDone(double timeInSeconds) => false;
PerformanceStatus get status => _masterPerformance != null ? _masterPerformance.status : _status;
AnimationDirection get direction => _masterPerformance != null ? _masterPerformance.direction : _direction;
AnimationDirection get curveDirection => _masterPerformance != null ? _masterPerformance.curveDirection : _curveDirection;
double get progress => _masterPerformance != null ? _masterPerformance.progress : _progress;
}
/// A timeline that can be reversed and used to update [Animatable]s.
......@@ -387,7 +318,8 @@ class _RepeatingSimulation extends Simulation {
/// may also take direct control of the timeline by manipulating [progress], or
/// [fling] the timeline causing a physics-based simulation to take over the
/// progression.
class Performance extends PerformanceView {
class Performance extends PerformanceView
with EagerListenerMixin, LocalPerformanceListenersMixin, LocalPerformanceStatusListenersMixin {
Performance({ this.duration, double progress, this.debugLabel }) {
_timeline = new SimulationStepper(_tick);
if (progress != null)
......@@ -464,11 +396,18 @@ class Performance extends PerformanceView {
return _animateTo(_direction == AnimationDirection.forward ? 1.0 : 0.0);
}
/// Stop running this animation
/// Stop running this animation.
void stop() {
_timeline.stop();
}
/// Release any resources used by this object.
///
/// Same as stop().
void dispose() {
stop();
}
/// Start running this animation according to the given physical parameters
///
/// Flings the timeline with an optional force (defaults to a critically
......@@ -487,40 +426,11 @@ class Performance extends PerformanceView {
return _timeline.animateWith(new _RepeatingSimulation(min, max, period));
}
final List<VoidCallback> _listeners = new List<VoidCallback>();
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
}
void _notifyListeners() {
List<VoidCallback> localListeners = new List<VoidCallback>.from(_listeners);
for (VoidCallback listener in localListeners)
listener();
}
final List<PerformanceStatusListener> _statusListeners = new List<PerformanceStatusListener>();
void addStatusListener(PerformanceStatusListener listener) {
_statusListeners.add(listener);
}
void removeStatusListener(PerformanceStatusListener listener) {
_statusListeners.remove(listener);
}
PerformanceStatus _lastStatus = PerformanceStatus.dismissed;
void _checkStatusChanged() {
PerformanceStatus currentStatus = status;
if (currentStatus != _lastStatus) {
List<PerformanceStatusListener> localListeners = new List<PerformanceStatusListener>.from(_statusListeners);
for (PerformanceStatusListener listener in localListeners)
listener(currentStatus);
}
if (currentStatus != _lastStatus)
notifyStatusListeners(status);
_lastStatus = currentStatus;
}
......@@ -545,7 +455,7 @@ class Performance extends PerformanceView {
}
void didTick(double t) {
_notifyListeners();
notifyListeners();
_checkStatusChanged();
}
......@@ -570,3 +480,25 @@ class ValuePerformance<T> extends Performance {
super.didTick(t);
}
}
class _RepeatingSimulation extends Simulation {
_RepeatingSimulation(this.min, this.max, Duration period)
: _periodInSeconds = period.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND {
assert(_periodInSeconds > 0.0);
}
final double min;
final double max;
final double _periodInSeconds;
double x(double timeInSeconds) {
assert(timeInSeconds >= 0.0);
final double t = (timeInSeconds / _periodInSeconds) % 1.0;
return lerpDouble(min, max, t);
}
double dx(double timeInSeconds) => 1.0;
bool isDone(double timeInSeconds) => false;
}
......@@ -13,7 +13,9 @@ class TestTransition extends TransitionComponent {
this.childFirstHalf,
this.childSecondHalf,
PerformanceView performance
}) : super(key: key, performance: performance);
}) : super(key: key, performance: performance) {
assert(performance != null);
}
final Widget childFirstHalf;
final Widget childSecondHalf;
......
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