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 @@ ...@@ -8,10 +8,11 @@
library animation; library animation;
export 'src/animation/animated_value.dart'; export 'src/animation/animated_value.dart';
export 'src/animation/performance.dart';
export 'src/animation/clamped_simulation.dart'; export 'src/animation/clamped_simulation.dart';
export 'src/animation/curves.dart'; export 'src/animation/curves.dart';
export 'src/animation/forces.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/scroll_behavior.dart';
export 'src/animation/simulation_stepper.dart'; export 'src/animation/simulation_stepper.dart';
export 'src/animation/ticker.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'; ...@@ -10,23 +10,7 @@ import 'package:newton/newton.dart';
import 'animated_value.dart'; import 'animated_value.dart';
import 'forces.dart'; import 'forces.dart';
import 'simulation_stepper.dart'; import 'simulation_stepper.dart';
import 'listener_helpers.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);
/// An interface that is implemented by [Performance] that exposes a /// An interface that is implemented by [Performance] that exposes a
/// read-only view of the underlying performance. This is used by classes that /// read-only view of the underlying performance. This is used by classes that
...@@ -89,10 +73,9 @@ class AlwaysCompletePerformance extends PerformanceView { ...@@ -89,10 +73,9 @@ class AlwaysCompletePerformance extends PerformanceView {
} }
const AlwaysCompletePerformance alwaysCompletePerformance = const AlwaysCompletePerformance(); const AlwaysCompletePerformance alwaysCompletePerformance = const AlwaysCompletePerformance();
class ReversePerformance extends PerformanceView { class ReversePerformance extends PerformanceView
ReversePerformance(this.masterPerformance) { with LazyListenerMixin, LocalPerformanceStatusListenersMixin {
masterPerformance.addStatusListener(_statusChangeHandler); ReversePerformance(this.masterPerformance);
}
final PerformanceView masterPerformance; final PerformanceView masterPerformance;
...@@ -101,27 +84,24 @@ class ReversePerformance extends PerformanceView { ...@@ -101,27 +84,24 @@ class ReversePerformance extends PerformanceView {
} }
void addListener(VoidCallback listener) { void addListener(VoidCallback listener) {
didRegisterListener();
masterPerformance.addListener(listener); masterPerformance.addListener(listener);
} }
void removeListener(VoidCallback listener) { void removeListener(VoidCallback listener) {
masterPerformance.removeListener(listener); masterPerformance.removeListener(listener);
didUnregisterListener();
} }
final List<PerformanceStatusListener> _statusListeners = new List<PerformanceStatusListener>(); void didStartListening() {
masterPerformance.addStatusListener(_statusChangeHandler);
void addStatusListener(PerformanceStatusListener listener) {
_statusListeners.add(listener);
} }
void removeStatusListener(PerformanceStatusListener listener) { void didStopListening() {
_statusListeners.remove(listener); masterPerformance.removeStatusListener(_statusChangeHandler);
} }
void _statusChangeHandler(PerformanceStatus status) { void _statusChangeHandler(PerformanceStatus status) {
status = _reverseStatus(status); notifyStatusListeners(_reverseStatus(status));
List<PerformanceStatusListener> localListeners = new List<PerformanceStatusListener>.from(_statusListeners);
for (PerformanceStatusListener listener in localListeners)
listener(status);
} }
PerformanceStatus get status => _reverseStatus(masterPerformance.status); PerformanceStatus get status => _reverseStatus(masterPerformance.status);
...@@ -153,11 +133,16 @@ enum _TrainHoppingMode { minimize, maximize } ...@@ -153,11 +133,16 @@ enum _TrainHoppingMode { minimize, maximize }
/// going in the opposite direction, or because the one overtakes the other), /// going in the opposite direction, or because the one overtakes the other),
/// the performance hops over to proxying the second performance, and the second /// the performance hops over to proxying the second performance, and the second
/// performance becomes the new "first" performance. /// 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 }) { TrainHoppingPerformance(this._currentTrain, this._nextTrain, { this.onSwitchedTrain }) {
assert(_currentTrain != null); assert(_currentTrain != null);
if (_nextTrain != null) { if (_nextTrain != null) {
if (_currentTrain.progress > _nextTrain.progress) { if (_currentTrain.progress > _nextTrain.progress) {
_mode = _TrainHoppingMode.maximize; _mode = _TrainHoppingMode.maximize;
} else { } else {
...@@ -187,37 +172,11 @@ class TrainHoppingPerformance extends PerformanceView { ...@@ -187,37 +172,11 @@ class TrainHoppingPerformance extends PerformanceView {
variable.setProgress(progress, curveDirection); 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; PerformanceStatus _lastStatus;
void _statusChangeHandler(PerformanceStatus status) { void _statusChangeHandler(PerformanceStatus status) {
assert(_currentTrain != null); assert(_currentTrain != null);
if (status != _lastStatus) { if (status != _lastStatus) {
List<PerformanceStatusListener> localListeners = new List<PerformanceStatusListener>.from(_statusListeners); notifyListeners();
for (PerformanceStatusListener listener in localListeners)
listener(status);
_lastStatus = status; _lastStatus = status;
} }
assert(_lastStatus != null); assert(_lastStatus != null);
...@@ -250,9 +209,7 @@ class TrainHoppingPerformance extends PerformanceView { ...@@ -250,9 +209,7 @@ class TrainHoppingPerformance extends PerformanceView {
} }
double newProgress = progress; double newProgress = progress;
if (newProgress != _lastProgress) { if (newProgress != _lastProgress) {
List<VoidCallback> localListeners = new List<VoidCallback>.from(_listeners); notifyListeners();
for (VoidCallback listener in localListeners)
listener();
_lastProgress = newProgress; _lastProgress = newProgress;
} }
assert(_lastProgress != null); assert(_lastProgress != null);
...@@ -276,10 +233,22 @@ class TrainHoppingPerformance extends PerformanceView { ...@@ -276,10 +233,22 @@ class TrainHoppingPerformance extends PerformanceView {
} }
} }
class ProxyPerformance extends PerformanceView { class ProxyPerformance extends PerformanceView
with LazyListenerMixin, LocalPerformanceListenersMixin, LocalPerformanceStatusListenersMixin {
ProxyPerformance([PerformanceView performance]) { 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 get masterPerformance => _masterPerformance;
PerformanceView _masterPerformance; PerformanceView _masterPerformance;
...@@ -287,96 +256,58 @@ class ProxyPerformance extends PerformanceView { ...@@ -287,96 +256,58 @@ class ProxyPerformance extends PerformanceView {
if (value == _masterPerformance) if (value == _masterPerformance)
return; return;
if (_masterPerformance != null) { if (_masterPerformance != null) {
_masterPerformance.removeStatusListener(_statusChangeHandler); _status = _masterPerformance.status;
_masterPerformance.removeListener(_valueChangeHandler); _direction = _masterPerformance.direction;
_curveDirection = _masterPerformance.curveDirection;
_progress = _masterPerformance.progress;
if (isListening)
didStopListening();
} }
_masterPerformance = value; _masterPerformance = value;
if (_masterPerformance != null) { if (_masterPerformance != null) {
_masterPerformance.addListener(_valueChangeHandler); if (isListening)
_masterPerformance.addStatusListener(_statusChangeHandler); didStartListening();
_valueChangeHandler(); if (_progress != _masterPerformance.progress)
_statusChangeHandler(_masterPerformance.status); notifyListeners();
} if (_status != _masterPerformance.status)
} notifyStatusListeners(_masterPerformance.status);
_status = null;
void updateVariable(Animatable variable) { _direction = null;
variable.setProgress(progress, curveDirection); _curveDirection = null;
} _progress = null;
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 didStartListening() {
if (_masterPerformance != null) {
void addStatusListener(PerformanceStatusListener listener) { _masterPerformance.addListener(_valueChangeHandler);
_statusListeners.add(listener); _masterPerformance.addStatusListener(_statusChangeHandler);
} }
void removeStatusListener(PerformanceStatusListener listener) {
_statusListeners.remove(listener);
} }
PerformanceStatus _status = PerformanceStatus.dismissed; void didStopListening() {
AnimationDirection _direction = AnimationDirection.forward; if (_masterPerformance != null) {
AnimationDirection _curveDirection = AnimationDirection.forward; _masterPerformance.removeListener(_valueChangeHandler);
void _statusChangeHandler(PerformanceStatus status) { _masterPerformance.removeStatusListener(_statusChangeHandler);
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);
} }
} }
PerformanceStatus get status => _status;
AnimationDirection get direction => _direction;
AnimationDirection get curveDirection => _curveDirection;
double _progress = 0.0;
void _valueChangeHandler() { void _valueChangeHandler() {
assert(_masterPerformance != null); notifyListeners();
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();
}
} }
double get progress => _progress; void _statusChangeHandler(PerformanceStatus status) {
} notifyStatusListeners(status);
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; void updateVariable(Animatable variable) {
final double max; variable.setProgress(progress, curveDirection);
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; PerformanceStatus get status => _masterPerformance != null ? _masterPerformance.status : _status;
AnimationDirection get direction => _masterPerformance != null ? _masterPerformance.direction : _direction;
bool isDone(double timeInSeconds) => false; 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. /// A timeline that can be reversed and used to update [Animatable]s.
...@@ -387,7 +318,8 @@ class _RepeatingSimulation extends Simulation { ...@@ -387,7 +318,8 @@ class _RepeatingSimulation extends Simulation {
/// may also take direct control of the timeline by manipulating [progress], or /// may also take direct control of the timeline by manipulating [progress], or
/// [fling] the timeline causing a physics-based simulation to take over the /// [fling] the timeline causing a physics-based simulation to take over the
/// progression. /// progression.
class Performance extends PerformanceView { class Performance extends PerformanceView
with EagerListenerMixin, LocalPerformanceListenersMixin, LocalPerformanceStatusListenersMixin {
Performance({ this.duration, double progress, this.debugLabel }) { Performance({ this.duration, double progress, this.debugLabel }) {
_timeline = new SimulationStepper(_tick); _timeline = new SimulationStepper(_tick);
if (progress != null) if (progress != null)
...@@ -464,11 +396,18 @@ class Performance extends PerformanceView { ...@@ -464,11 +396,18 @@ class Performance extends PerformanceView {
return _animateTo(_direction == AnimationDirection.forward ? 1.0 : 0.0); return _animateTo(_direction == AnimationDirection.forward ? 1.0 : 0.0);
} }
/// Stop running this animation /// Stop running this animation.
void stop() { void stop() {
_timeline.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 /// Start running this animation according to the given physical parameters
/// ///
/// Flings the timeline with an optional force (defaults to a critically /// Flings the timeline with an optional force (defaults to a critically
...@@ -487,40 +426,11 @@ class Performance extends PerformanceView { ...@@ -487,40 +426,11 @@ class Performance extends PerformanceView {
return _timeline.animateWith(new _RepeatingSimulation(min, max, period)); 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; PerformanceStatus _lastStatus = PerformanceStatus.dismissed;
void _checkStatusChanged() { void _checkStatusChanged() {
PerformanceStatus currentStatus = status; PerformanceStatus currentStatus = status;
if (currentStatus != _lastStatus) { if (currentStatus != _lastStatus)
List<PerformanceStatusListener> localListeners = new List<PerformanceStatusListener>.from(_statusListeners); notifyStatusListeners(status);
for (PerformanceStatusListener listener in localListeners)
listener(currentStatus);
}
_lastStatus = currentStatus; _lastStatus = currentStatus;
} }
...@@ -545,7 +455,7 @@ class Performance extends PerformanceView { ...@@ -545,7 +455,7 @@ class Performance extends PerformanceView {
} }
void didTick(double t) { void didTick(double t) {
_notifyListeners(); notifyListeners();
_checkStatusChanged(); _checkStatusChanged();
} }
...@@ -570,3 +480,25 @@ class ValuePerformance<T> extends Performance { ...@@ -570,3 +480,25 @@ class ValuePerformance<T> extends Performance {
super.didTick(t); 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 { ...@@ -13,7 +13,9 @@ class TestTransition extends TransitionComponent {
this.childFirstHalf, this.childFirstHalf,
this.childSecondHalf, this.childSecondHalf,
PerformanceView performance PerformanceView performance
}) : super(key: key, performance: performance); }) : super(key: key, performance: performance) {
assert(performance != null);
}
final Widget childFirstHalf; final Widget childFirstHalf;
final Widget childSecondHalf; 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