From 97abc0daddfec90442965af7abfe39df746a780d Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 19 Mar 2015 11:17:48 -0700 Subject: [PATCH] Improve Material ink effects 1) Factors InkWell out of Material so that components can use an ink well without needing the shadow/level machinery. 2) Makes the ink effect move at a different velocity once the tap has actually occurred, converging with the spec. We don't have the right speeds yet, but at least we're approaching the right shape. 3) To support (2), added a primaryPointer attribute to GestureEvents to let authors coorelate gesturetapdown events with later gesturetap events. 4) To support (2), modernized SplashAnimation to used AnimatedValue and friends. 5) Added more constants to view-configuration.dart that match Android. I've also removed the cancelling of the ink effect on scroll. The proper way to do that is to notice that someone in the event chain is listening for scrollstart and delay the beginning of the ink effect for some period of time. R=eseidel@chromium.org Review URL: https://codereview.chromium.org/1019023003 --- engine/core/events/GestureEvent.cpp | 2 + engine/core/events/GestureEvent.h | 3 + engine/core/events/GestureEvent.idl | 1 + engine/public/platform/WebInputEvent.h | 1 + examples/stocks-fn/lib/stock_row.dart | 4 +- framework/components/ink_splash.dart | 70 ++++++++++----- framework/components/ink_well.dart | 85 +++++++++++++++++++ framework/components/material.dart | 73 ++-------------- framework/components/menu_item.dart | 4 +- framework/components/popup_menu_item.dart | 4 +- framework/components/radio.dart | 3 +- framework/fn.dart | 5 ++ framework/theme/view-configuration.dart | 28 ++++-- services/viewport/input_event.mojom | 1 + .../domokit/sky/shell/GestureProvider.java | 6 +- shell/ui/input_event_converter.cc | 1 + 16 files changed, 185 insertions(+), 106 deletions(-) create mode 100644 framework/components/ink_well.dart diff --git a/engine/core/events/GestureEvent.cpp b/engine/core/events/GestureEvent.cpp index cd56f26e135..bdcb538546f 100644 --- a/engine/core/events/GestureEvent.cpp +++ b/engine/core/events/GestureEvent.cpp @@ -67,6 +67,7 @@ GestureEvent::GestureEvent() GestureEvent::GestureEvent(const WebGestureEvent& event) : Event(stringForType(event.type), true, true) + , m_primaryPointer(event.primaryPointer) , m_x(event.x) , m_y(event.y) , m_dx(0) @@ -90,6 +91,7 @@ GestureEvent::GestureEvent(const WebGestureEvent& event) GestureEvent::GestureEvent(const AtomicString& type, const GestureEventInit& initializer) : Event(type, initializer) + , m_primaryPointer(initializer.primaryPointer) , m_x(initializer.x) , m_y(initializer.y) , m_dx(initializer.dx) diff --git a/engine/core/events/GestureEvent.h b/engine/core/events/GestureEvent.h index f67b1893658..e6c46c539c3 100644 --- a/engine/core/events/GestureEvent.h +++ b/engine/core/events/GestureEvent.h @@ -11,6 +11,7 @@ namespace blink { struct GestureEventInit : public EventInit { + int primaryPointer = 0; double x = 0; double y = 0; double dx = 0; @@ -38,6 +39,7 @@ public: ~GestureEvent() override; const AtomicString& interfaceName() const override; + int primaryPointer() const { return m_primaryPointer; } float x() const { return m_x; } float y() const { return m_y; } float dx() const { return m_dx; } @@ -50,6 +52,7 @@ private: explicit GestureEvent(const WebGestureEvent&); GestureEvent(const AtomicString& type, const GestureEventInit&); + int m_primaryPointer; float m_x; float m_y; float m_dx; diff --git a/engine/core/events/GestureEvent.idl b/engine/core/events/GestureEvent.idl index 37b9ed5ec1b..5d2f3060937 100644 --- a/engine/core/events/GestureEvent.idl +++ b/engine/core/events/GestureEvent.idl @@ -5,6 +5,7 @@ [ EventConstructor, ] interface GestureEvent : Event { + [InitializedByEventConstructor] readonly attribute long primaryPointer; [InitializedByEventConstructor] readonly attribute double x; [InitializedByEventConstructor] readonly attribute double y; [InitializedByEventConstructor] readonly attribute double dx; diff --git a/engine/public/platform/WebInputEvent.h b/engine/public/platform/WebInputEvent.h index c1e66cb276d..b63becb1ef8 100644 --- a/engine/public/platform/WebInputEvent.h +++ b/engine/public/platform/WebInputEvent.h @@ -268,6 +268,7 @@ public: class WebGestureEvent : public WebInputEvent { public: + int primaryPointer = 0; float x = 0; float y = 0; diff --git a/examples/stocks-fn/lib/stock_row.dart b/examples/stocks-fn/lib/stock_row.dart index 6f75dee5db7..94abba7adbe 100644 --- a/examples/stocks-fn/lib/stock_row.dart +++ b/examples/stocks-fn/lib/stock_row.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:sky/framework/components/material.dart'; +import 'package:sky/framework/components/ink_well.dart'; import 'package:sky/framework/fn.dart'; import 'package:sky/framework/theme/typography.dart' as typography; import 'stock_arrow.dart'; @@ -68,7 +68,7 @@ class StockRow extends Component { ) ]; - return new Material( + return new InkWell( style: _style, children: children ); diff --git a/framework/components/ink_splash.dart b/framework/components/ink_splash.dart index c768cb6fccc..928dcdbc2ac 100644 --- a/framework/components/ink_splash.dart +++ b/framework/components/ink_splash.dart @@ -2,45 +2,69 @@ // 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 '../animation/generators.dart'; import '../fn.dart'; +import '../theme/view-configuration.dart' as config; import 'dart:async'; +import 'dart:math' as math; import 'dart:sky' as sky; -const double _kSplashSize = 400.0; -const double _kSplashDuration = 500.0; +const double _kSplashConfirmedDuration = 350.0; +const double _kSplashUnconfirmedDuration = config.kDefaultLongPressTimeout; -class SplashAnimation { - AnimationGenerator _animation; +double _getSplashTargetSize(sky.ClientRect rect, double x, double y) { + return 2.0 * math.max(math.max(x - rect.left, rect.right - x), + math.max(y - rect.top, rect.bottom - y)); +} + +class SplashController { + final int pointer; + Stream get onStyleChanged => _styleStream; + + final AnimatedValue _size = new AnimatedValue(0.0); double _offsetX; double _offsetY; + double _targetSize; + Stream _styleStream; - Stream _styleChanged; + void start() { + _size.animateTo(_targetSize, _kSplashUnconfirmedDuration, curve: easeOut); + } - Stream get onStyleChanged => _styleChanged; + void confirm() { + double fractionRemaining = (_targetSize - _size.value) / _targetSize; + double duration = fractionRemaining * _kSplashConfirmedDuration; + if (duration <= 0.0) + return; + _size.animateTo(_targetSize, duration, curve: easeOut); + } - void cancel() => _animation.cancel(); + void cancel() { + _size.stop(); + } - SplashAnimation(sky.ClientRect rect, double x, double y, - { Function onDone }) + SplashController(sky.ClientRect rect, double x, double y, + { this.pointer, Function onDone }) : _offsetX = x - rect.left, - _offsetY = y - rect.top { + _offsetY = y - rect.top, + _targetSize = _getSplashTargetSize(rect, x, y) { - _animation = new AnimationGenerator( - duration:_kSplashDuration, - end: _kSplashSize, - curve: easeOut, - onDone: onDone); + _styleStream = _size.onValueChanged.map((p) { + if (p == _targetSize) { + onDone(); + } + return ''' + top: ${_offsetY - p/2}px; + left: ${_offsetX - p/2}px; + width: ${p}px; + height: ${p}px; + border-radius: ${p}px; + opacity: ${1.0 - (p / _targetSize)};'''; + }); - _styleChanged = _animation.onTick.map((p) => ''' - top: ${_offsetY - p/2}px; - left: ${_offsetX - p/2}px; - width: ${p}px; - height: ${p}px; - border-radius: ${p}px; - opacity: ${1.0 - (p / _kSplashSize)}; - '''); + start(); } } diff --git a/framework/components/ink_well.dart b/framework/components/ink_well.dart new file mode 100644 index 00000000000..601e3c75102 --- /dev/null +++ b/framework/components/ink_well.dart @@ -0,0 +1,85 @@ +// 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 '../fn.dart'; +import 'dart:collection'; +import 'dart:sky' as sky; +import 'ink_splash.dart'; + +class InkWell extends Component { + LinkedHashSet _splashes; + + Style style; + String inlineStyle; + List children; + + InkWell({ Object key, this.style, this.inlineStyle, this.children }) + : super(key: key); + + Node build() { + List childrenIncludingSplashes = []; + + if (_splashes != null) { + childrenIncludingSplashes.addAll( + _splashes.map((s) => new InkSplash(s.onStyleChanged))); + } + + if (children != null) + childrenIncludingSplashes.addAll(children); + + return new EventTarget( + new Container( + style: style, + inlineStyle: inlineStyle, + children: childrenIncludingSplashes), + onGestureTapDown: _startSplash, + onGestureTap: _confirmSplash + ); + } + + sky.ClientRect _getBoundingRect() => (getRoot() as sky.Element).getBoundingClientRect(); + + void _startSplash(sky.GestureEvent event) { + setState(() { + if (_splashes == null) + _splashes = new LinkedHashSet(); + var splash; + splash = new SplashController(_getBoundingRect(), event.x, event.y, + pointer: event.primaryPointer, + onDone: () { _splashDone(splash); }); + _splashes.add(splash); + }); + } + + void _confirmSplash(sky.GestureEvent event) { + if (_splashes == null) + return; + _splashes.where((splash) => splash.pointer == event.primaryPointer) + .forEach((splash) { splash.confirm(); }); + } + + void _cancelSplashes(sky.Event event) { + if (_splashes == null) + return; + setState(() { + var splashes = _splashes; + _splashes = null; + splashes.forEach((s) { s.cancel(); }); + }); + } + + void didUnmount() { + _cancelSplashes(null); + } + + void _splashDone(SplashController splash) { + if (_splashes == null) + return; + setState(() { + _splashes.remove(splash); + if (_splashes.length == 0) + _splashes = null; + }); + } +} diff --git a/framework/components/material.dart b/framework/components/material.dart index 12ee60724a5..b335785eae7 100644 --- a/framework/components/material.dart +++ b/framework/components/material.dart @@ -4,9 +4,7 @@ import '../fn.dart'; import '../theme/shadows.dart'; -import 'dart:collection'; -import 'dart:sky' as sky; -import 'ink_splash.dart'; +import 'ink_well.dart'; class Material extends Component { static final List