Index: sky/sdk/lib/framework/components2/ink_well.dart |
diff --git a/sky/sdk/lib/framework/components2/ink_well.dart b/sky/sdk/lib/framework/components2/ink_well.dart |
index 05120acddb1ba1a992190e5d6c830b52239a2c00..914ebce536d84621540f6672c85ffc7c6b5cf435 100644 |
--- a/sky/sdk/lib/framework/components2/ink_well.dart |
+++ b/sky/sdk/lib/framework/components2/ink_well.dart |
@@ -2,125 +2,126 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+import '../animation/animated_value.dart'; |
+import '../animation/curves.dart'; |
import '../fn2.dart'; |
+import '../rendering/box.dart'; |
import '../rendering/flex.dart'; |
+import '../rendering/object.dart'; |
+import '../theme/view_configuration.dart' as config; |
+import 'dart:async'; |
import 'dart:collection'; |
+import 'dart:math' as math; |
import 'dart:sky' as sky; |
-// import 'ink_splash.dart'; |
-import 'scrollable.dart'; |
-class InkWell extends Component implements ScrollClient { |
+const int _kSplashInitialOpacity = 0x80; |
+const double _kSplashInitialSize = 0.0; |
+const double _kSplashConfirmedDuration = 350.0; |
+const double _kSplashUnconfirmedDuration = config.kDefaultLongPressTimeout; |
+const double _kSplashInitialDelay = 0.0; // we could delay initially in case the user scrolls |
- InkWell({ Object key, this.children }) : super(key: key); |
+double _getSplashTargetSize(Size bounds, Point position) { |
+ return 2.0 * math.max(math.max(position.x, bounds.width - position.x), |
+ math.max(position.y, bounds.height - position.y)); |
+} |
- // static final Style _containmentStyleHack = new Style(''' |
- // align-items: center; |
- // transform: translateX(0);'''); |
+class InkSplash { |
+ final int pointer; |
+ final Point position; |
+ final RenderInkWell well; |
- // LinkedHashSet<SplashController> _splashes; |
+ double _targetRadius; |
+ AnimatedValue _radius; |
- List<UINode> children; |
+ InkSplash(this.pointer, this.position, this.well) { |
+ _targetRadius = _getSplashTargetSize(well.size, position); |
+ _radius = new AnimatedValue(_kSplashInitialSize, onChange: _handleRadiusChange); |
+ _radius.animateTo(_targetRadius, _kSplashUnconfirmedDuration, |
+ curve: easeOut, initialDelay: _kSplashInitialDelay); |
+ } |
- // InkWell({ Object key, this.inlineStyle, this.children }) |
- // : super(key: key) { |
- // onDidUnmount(() { |
- // _cancelSplashes(null); |
- // }); |
- // } |
+ void confirm() { |
+ double fractionRemaining = (_targetRadius - _radius.value) / _targetRadius; |
+ double duration = fractionRemaining * _kSplashConfirmedDuration; |
+ if (duration <= 0.0) |
+ return; |
+ _radius.animateTo(_targetRadius, duration, curve: easeOut); |
+ } |
- UINode build() { |
- return new FlexContainer( |
- direction: FlexDirection.horizontal, |
- justifyContent: FlexJustifyContent.center, |
- children: children); |
- // List<UINode> childrenIncludingSplashes = []; |
- |
- // if (_splashes != null) { |
- // childrenIncludingSplashes.addAll( |
- // _splashes.map((s) => new InkSplash(s.onStyleChanged))); |
- // } |
- |
- // if (children != null) |
- // childrenIncludingSplashes.addAll(children); |
- |
- // return new EventListenerNode( |
- // new FlexContainer( |
- // direction: FlexDirection.horizontal, |
- // style: _containmentStyleHack, |
- // inlineStyle: inlineStyle, |
- // children: childrenIncludingSplashes), |
- // onGestureTapDown: _startSplash, |
- // onGestureTap: _confirmSplash |
- // ); |
+ void _handleRadiusChange() { |
+ if (_radius.value == _targetRadius) |
+ well._splashes.remove(this); |
+ well.markNeedsPaint(); |
} |
- // void _startSplash(sky.GestureEvent event) { |
- // setState(() { |
- // if (_splashes == null) |
- // _splashes = new LinkedHashSet<SplashController>(); |
- // var splash; |
- // var root = getRoot(); |
- // splash = new SplashController(root.rect, event.x, event.y, |
- // pointer: event.primaryPointer, |
- // onDone: () { _splashDone(splash); }); |
- // _splashes.add(splash); |
- // UINode node = parent; |
- // while (node != null) { |
- // if (node is Scrollable) |
- // node.registerScrollClient(this); |
- // node = node.parent; |
- // } |
- // }); |
- // } |
- |
- bool ancestorScrolled(Scrollable ancestor) { |
- // _abortSplashes(); |
- return false; |
+ void paint(RenderObjectDisplayList canvas) { |
+ int opacity = (_kSplashInitialOpacity * (1.0 - (_radius.value / _targetRadius))).floor(); |
+ sky.Paint paint = new sky.Paint()..color = new sky.Color(opacity << 24); |
+ canvas.drawCircle(position.x, position.y, _radius.value, paint); |
} |
+} |
- // void handleRemoved() { |
- // UINode node = parent; |
- // while (node != null) { |
- // if (node is Scrollable) |
- // node.unregisterScrollClient(this); |
- // node = node.parent; |
- // } |
- // super.handleRemoved(); |
- // } |
- |
- // void _confirmSplash(sky.GestureEvent event) { |
- // if (_splashes == null) |
- // return; |
- // _splashes.where((splash) => splash.pointer == event.primaryPointer) |
- // .forEach((splash) { splash.confirm(); }); |
- // } |
- |
- // void _abortSplashes() { |
- // if (_splashes == null) |
- // return; |
- // setState(() { |
- // _splashes.forEach((s) { s.abort(); }); |
- // }); |
- // } |
- |
- // void _cancelSplashes(sky.Event event) { |
- // if (_splashes == null) |
- // return; |
- // setState(() { |
- // var splashes = _splashes; |
- // _splashes = null; |
- // splashes.forEach((s) { s.cancel(); }); |
- // }); |
- // } |
- |
- // void _splashDone(SplashController splash) { |
- // if (_splashes == null) |
- // return; |
- // setState(() { |
- // _splashes.remove(splash); |
- // if (_splashes.length == 0) |
- // _splashes = null; |
- // }); |
- // } |
+class RenderInkWell extends RenderProxyBox { |
+ final List<InkSplash> _splashes = new List<InkSplash>(); |
+ |
+ RenderInkWell({ RenderBox child }) : super(child); |
+ |
+ void handleEvent(sky.Event event) { |
+ switch (event.type) { |
+ case 'gesturetapdown': |
+ // TODO(abarth): We should position the splash at the location of the tap. |
+ _startSplash(event.primaryPointer, new Point(size.width / 2.0, size.height / 2.0)); |
+ break; |
+ case 'gesturetap': |
+ _confirmSplash(event.primaryPointer); |
+ break; |
+ } |
+ } |
+ |
+ void _startSplash(int pointer, Point position) { |
+ _splashes.add(new InkSplash(pointer, position, this)); |
+ markNeedsPaint(); |
+ } |
+ void _confirmSplash(int pointer) { |
+ _splashes.where((splash) => splash.pointer == pointer) |
+ .forEach((splash) { splash.confirm(); }); |
+ markNeedsPaint(); |
+ } |
+ |
+ void paint(RenderObjectDisplayList canvas) { |
+ if (!_splashes.isEmpty) { |
+ canvas.save(); |
+ canvas.clipRect(new Rect.fromSize(size)); |
+ for (InkSplash splash in _splashes) |
+ splash.paint(canvas); |
+ canvas.restore(); |
+ } |
+ super.paint(canvas); |
+ } |
+} |
+ |
+class InkWellWrapper extends OneChildRenderObjectWrapper { |
+ RenderInkWell root; |
+ |
+ InkWellWrapper({ UINode child, Object key }) |
+ : super(child: child, key: key); |
+ |
+ RenderInkWell createNode() => new RenderInkWell(); |
+} |
+ |
+class InkWell extends Component { |
+ List<UINode> children; |
+ |
+ InkWell({ Object key, this.children }) : super(key: key); |
+ |
+ UINode build() { |
+ return new InkWellWrapper( |
+ child: new FlexContainer( |
+ direction: FlexDirection.horizontal, |
+ justifyContent: FlexJustifyContent.center, |
+ children: children |
+ ) |
+ ); |
+ } |
} |