Commit d35d580c authored by Adam Barth's avatar Adam Barth

Merge pull request #780 from abarth/mimic_improvements

Mimic should track Mimicable more completely
parents 9bc23d72 7077b1b3
......@@ -63,6 +63,13 @@ class ExampleApp extends App {
);
}
Rect _targetRect;
void _handleOverlaySizeChanged(Size size) {
setState(() {
_targetRect = Point.origin & size;
});
}
Widget build() {
List<Widget> cards = new List<Widget>();
for (int i = 0; i < _data.length; ++i) {
......@@ -88,10 +95,14 @@ class ExampleApp extends App {
}
)
),
body: new MimicOverlay(
overlay: _overlay,
duration: const Duration(milliseconds: 500),
children: [ new Block(cards) ]
body: new SizeObserver(
callback: _handleOverlaySizeChanged,
child: new MimicOverlay(
overlay: _overlay,
duration: const Duration(milliseconds: 5000),
targetRect: _targetRect,
children: [ new Block(cards) ]
)
)
)
)
......
......@@ -4,73 +4,105 @@
import 'package:sky/widgets/basic.dart';
typedef MimicCallback(Rect globalBounds);
class Mimic extends StatefulComponent {
Mimic({
abstract class GlobalKeyWatcher extends StatefulComponent {
GlobalKeyWatcher({
Key key,
this.original,
this.callback
}) : super(key: key);
this.watchedKey
});
GlobalKey watchedKey;
void syncConstructorArguments(GlobalKeyWatcher source) {
if (source != source.watchedKey) {
_removeListeners();
watchedKey = source.watchedKey;
_addListeners();
}
}
void didMount() {
super.didMount();
_addListeners();
}
GlobalKey original;
MimicCallback callback;
void didUnmount() {
super.didUnmount();
_removeListeners();
}
void didSyncWatchedKey(GlobalKey key, Widget widget) {
assert(key == watchedKey);
}
void initState() {
_requestToStartMimic();
void didRemoveWatchedKey(GlobalKey key) {
assert(key == watchedKey);
}
void _addListeners() {
GlobalKey.registerSyncListener(watchedKey, didSyncWatchedKey);
GlobalKey.registerRemoveListener(watchedKey, didRemoveWatchedKey);
}
void _removeListeners() {
GlobalKey.unregisterSyncListener(watchedKey, didSyncWatchedKey);
GlobalKey.unregisterRemoveListener(watchedKey, didRemoveWatchedKey);
}
}
typedef MimicReadyCallback();
class Mimic extends GlobalKeyWatcher {
Mimic({
Key key,
GlobalKey original,
this.onMimicReady
}) : super(key: key, watchedKey: original);
MimicReadyCallback onMimicReady;
void syncConstructorArguments(Mimic source) {
callback = source.callback;
if (original != source.original) {
_stopMimic();
original = source.original;
_requestToStartMimic();
}
onMimicReady = source.onMimicReady;
super.syncConstructorArguments(source);
}
Mimicable _mimicable;
void didMount() {
super.didMount();
// TODO(abarth): Why is didMount being called without a call to didUnmount?
if (_mimicable == null)
_requestToStartMimic();
_setMimicable(GlobalKey.getWidget(watchedKey));
}
void didUnmount() {
super.didUnmount();
_stopMimic();
if (_mimicable != null) {
_mimicable.stopMimic();
_mimicable = null;
}
}
Mimicable _mimicable;
bool _mimicking = false;
void didSyncWatchedKey(GlobalKey key, Widget widget) {
super.didSyncWatchedKey(key, widget);
_setMimicable(widget);
}
void _requestToStartMimic() {
assert(_mimicable == null);
assert(!_mimicking);
if (original == null)
return;
_mimicable = GlobalKey.getWidget(original) as Mimicable;
assert(_mimicable != null);
_mimicable._requestToStartMimic(this);
void didRemoveWatchedKey(GlobalKey key) {
super.didRemoveWatchedKey(key);
_setMimicable(null);
}
void _startMimic(GlobalKey key, Rect globalBounds) {
assert(key == original);
void _setMimicable(widget) {
if (_mimicable == null && widget != null)
widget.startMimic();
setState(() {
_mimicking = true;
_mimicable = widget;
});
callback(globalBounds);
}
void _stopMimic() {
if (_mimicable != null)
_mimicable._didStopMimic(this);
_mimicable = null;
_mimicking = false;
if (onMimicReady != null && _mimicable != null && _mimicable._didBuildPlaceholder)
onMimicReady();
}
Widget build() {
if (!_mimicking || !_mimicable.mounted)
if (_mimicable == null || !_mimicable._didBuildPlaceholder)
return new Container();
return _mimicable.child;
}
......@@ -84,29 +116,30 @@ class Mimicable extends StatefulComponent {
Size _size;
Size get size => _size;
Mimic _mimic;
bool _didStartMimic = false;
void syncConstructorArguments(Mimicable source) {
child = source.child;
}
void _requestToStartMimic(Mimic mimic) {
assert(mounted);
if (_mimic != null)
return;
bool _didBuildPlaceholder = false;
Rect get globalBounds {
if (_size == null)
return null;
return localToGlobal(Point.origin) & _size;
}
bool _mimicRequested = false;
void startMimic() {
assert(!_mimicRequested);
setState(() {
_mimic = mimic;
_didStartMimic = false;
_mimicRequested = true;
});
}
void _didStopMimic(Mimic mimic) {
assert(_mimic != null);
assert(mimic == _mimic);
void stopMimic() {
assert(_mimicRequested);
setState(() {
_mimic = null;
_didStartMimic = false;
_mimicRequested = false;
});
}
......@@ -116,19 +149,12 @@ class Mimicable extends StatefulComponent {
});
}
void _startMimicIfNeeded() {
if (_didStartMimic)
return;
assert(_mimic != null);
Point globalPosition = localToGlobal(Point.origin);
_mimic._startMimic(key, globalPosition & _size);
_didStartMimic = true;
}
Widget build() {
if (_mimic != null) {
_startMimicIfNeeded();
return new ConstrainedBox(constraints: new BoxConstraints.tight(_size));
_didBuildPlaceholder = _mimicRequested && _size != null;
if (_didBuildPlaceholder) {
return new ConstrainedBox(
constraints: new BoxConstraints.tight(_size)
);
}
return new SizeObserver(
callback: _handleSizeChanged,
......
......@@ -56,6 +56,7 @@ class MimicOverlay extends AnimatedComponent {
_expandPerformance = new AnimationPerformance()
..duration = duration
..addVariable(_mimicBounds)
..addListener(_handleAnimationTick)
..addStatusListener(_handleAnimationStatusChanged);
watch(_expandPerformance);
}
......@@ -72,22 +73,38 @@ class MimicOverlay extends AnimatedComponent {
}
}
void _handleMimicCallback(Rect globalBounds) {
void _handleAnimationTick() {
if (_activeOverlay == null)
return;
_updateMimicBounds();
}
void _updateMimicBounds() {
Mimicable mimicable = GlobalKey.getWidget(_activeOverlay) as Mimicable;
Rect globalBounds = mimicable.globalBounds;
if (globalBounds == null)
return;
Rect localBounds = globalToLocal(globalBounds.topLeft) & globalBounds.size;
if (localBounds == _mimicBounds.begin)
return;
setState(() {
// TODO(abarth): We need to convert global bounds into local coordinates.
_mimicBounds.begin =
globalToLocal(globalBounds.topLeft) & globalBounds.size;
_mimicBounds.value = _mimicBounds.begin;
_mimicBounds.begin = localBounds;
if (_expandPerformance.isDismissed)
_mimicBounds.value = _mimicBounds.begin;
});
_expandPerformance.forward();
}
void _handleMimicReady() {
_updateMimicBounds();
if (_expandPerformance.isDismissed)
_expandPerformance.forward();
}
Widget build() {
List<Widget> layers = new List<Widget>();
if (children != null) {
if (children != null)
layers.addAll(children);
}
if (_activeOverlay != null) {
layers.add(
......@@ -98,7 +115,7 @@ class MimicOverlay extends AnimatedComponent {
width: _mimicBounds.value.width,
height: _mimicBounds.value.height,
child: new Mimic(
callback: _handleMimicCallback,
onMimicReady: _handleMimicReady,
original: _activeOverlay
)
)
......
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