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