Commit 7077b1b3 authored by Adam Barth's avatar Adam Barth

Mimic should track Mimicable more completely

We now have Mimic tracking Mimicable through tree structure changes and while
moving around the screen.

Fixes #751 and #756
parent d7ed623e
......@@ -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