提交 8a361dde 编写于 作者: J James Robinson

Teach event system about disposition and make 'consumed' disposition terminal

This introduces the notion of event disposition and allows event
targets (widgets and render objects) to consume events that should not
be processed further. This is needed by the Switch component in the
Drawer in the stocks example. The Switch is embedded in a DrawerItem.
The Switch handles the gesture tap event to toggle its state and should
handle pointer events to allow swiping and draw its own radial
reaction. The DrawerItem also handles gesture taps to allow toggling
the switch value when tapping anywhere on the drawer and to draw its
own ink splash. When tapping on the switch, both the switch's render
object and the DrawerItem's listener are in the event dispatch path.
The Switch needs to signal in some fashion that it consumed the event
so the DrawerItem does not also try to toggle the switch's state.
上级 7a220802
......@@ -18,7 +18,7 @@ AssetBundle _initBundle() {
final AssetBundle _bundle = _initBundle();
void launch(String relativeUrl, String bundle) {
EventDisposition launch(String relativeUrl, String bundle) {
// TODO(eseidel): This is a hack to keep non-skyx examples working for now:
Uri productionBase = Uri.parse(
'https://domokit.github.io/example/demo_launcher/lib/main.dart');
......@@ -42,6 +42,7 @@ void launch(String relativeUrl, String bundle) {
}
activity.startActivity(intent);
return EventDisposition.processed;
}
class SkyDemo {
......
......@@ -82,11 +82,12 @@ class FeedFragment extends StatefulComponent {
bool _isShowingSnackBar = false;
void _handleFitnessModeChange(FitnessMode value) {
EventDisposition _handleFitnessModeChange(FitnessMode value) {
setState(() {
_fitnessMode = value;
_drawerShowing = false;
});
return EventDisposition.processed;
}
Drawer buildDrawer() {
......@@ -137,9 +138,10 @@ class FeedFragment extends StatefulComponent {
});
}
void _handleShowSettings() {
EventDisposition _handleShowSettings() {
navigator.pop();
navigator.pushNamed('/settings');
return EventDisposition.processed;
}
// TODO(jackson): We should be localizing
......
......@@ -56,9 +56,10 @@ class MealFragment extends StatefulComponent {
String _description = "";
void _handleSave() {
EventDisposition _handleSave() {
onCreated(new Meal(when: new DateTime.now(), description: _description));
navigator.pop();
return EventDisposition.processed;
}
Widget buildToolBar() {
......
......@@ -60,7 +60,7 @@ class MeasurementFragment extends StatefulComponent {
String _weight = "";
String _errorMessage = null;
void _handleSave() {
EventDisposition _handleSave() {
double parsedWeight;
try {
parsedWeight = double.parse(_weight);
......@@ -68,10 +68,11 @@ class MeasurementFragment extends StatefulComponent {
setState(() {
_errorMessage = "Save failed";
});
return;
return EventDisposition.processed;
}
onCreated(new Measurement(when: new DateTime.now(), weight: parsedWeight));
navigator.pop();
return EventDisposition.processed;
}
Widget buildToolBar() {
......
......@@ -174,9 +174,9 @@ class SpriteBox extends RenderBox {
}
}
void handleEvent(Event event, _SpriteBoxHitTestEntry entry) {
EventDisposition handleEvent(Event event, _SpriteBoxHitTestEntry entry) {
if (!attached)
return;
return EventDisposition.ignored;
if (event is PointerEvent) {
......@@ -225,6 +225,7 @@ class SpriteBox extends RenderBox {
}
}
}
return EventDisposition.ignored;
}
bool hitTest(HitTestResult result, { Point position }) {
......
......@@ -208,10 +208,11 @@ class MineDiggerApp extends App {
);
}
void handleToolbarPointerDown(sky.PointerEvent event) {
EventDisposition handleToolbarPointerDown(sky.PointerEvent event) {
setState(() {
resetGame();
});
return EventDisposition.processed;
}
// User action. The user uncovers the cell which can cause losing the game.
......
......@@ -37,11 +37,15 @@ class RenderSolidColor extends RenderDecoratedBox {
size = constraints.constrain(desiredSize);
}
void handleEvent(sky.Event event, BoxHitTestEntry entry) {
if (event.type == 'pointerdown')
EventDisposition handleEvent(sky.Event event, BoxHitTestEntry entry) {
if (event.type == 'pointerdown') {
decoration = new BoxDecoration(backgroundColor: const sky.Color(0xFFFF0000));
else if (event.type == 'pointerup')
return EventDisposition.processed;
} else if (event.type == 'pointerup') {
decoration = new BoxDecoration(backgroundColor: backgroundColor);
return EventDisposition.processed;
}
return super.handleEvent(event, entry);
}
}
......
......@@ -517,11 +517,15 @@ class RenderSolidColor extends RenderDecoratedSector {
deltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta);
}
void handleEvent(sky.Event event, HitTestEntry entry) {
if (event.type == 'pointerdown')
EventDisposition handleEvent(sky.Event event, HitTestEntry entry) {
if (event.type == 'pointerdown') {
decoration = new BoxDecoration(backgroundColor: const Color(0xFFFF0000));
else if (event.type == 'pointerup')
return EventDisposition.processed;
} else if (event.type == 'pointerup') {
decoration = new BoxDecoration(backgroundColor: backgroundColor);
return EventDisposition.processed;
}
return EventDisposition.ignored;
}
}
......
......@@ -42,10 +42,14 @@ class RenderSolidColorBox extends RenderDecoratedBox {
size = constraints.constrain(desiredSize);
}
void handleEvent(sky.Event event, BoxHitTestEntry entry) {
if (event.type == 'pointerdown')
EventDisposition handleEvent(sky.Event event, BoxHitTestEntry entry) {
if (event.type == 'pointerdown') {
decoration = new BoxDecoration(backgroundColor: const Color(0xFFFF0000));
else if (event.type == 'pointerup')
return EventDisposition.processed;
} else if (event.type == 'pointerup') {
decoration = new BoxDecoration(backgroundColor: backgroundColor);
return EventDisposition.processed;
}
return EventDisposition.ignored;
}
}
......@@ -43,7 +43,7 @@ class RenderTouchDemo extends RenderBox {
RenderTouchDemo();
void handleEvent(sky.Event event, BoxHitTestEntry entry) {
EventDisposition handleEvent(sky.Event event, BoxHitTestEntry entry) {
if (event is sky.PointerEvent) {
switch (event.type) {
case 'pointerdown':
......@@ -60,8 +60,10 @@ class RenderTouchDemo extends RenderBox {
dots[event.pointer].update(event);
break;
}
markNeedsPaint();
return EventDisposition.processed;
}
markNeedsPaint();
return EventDisposition.processed;
}
void performLayout() {
......
......@@ -103,12 +103,13 @@ class StockHome extends StatefulComponent {
});
}
void _handleStockModeChange(StockMode value) {
EventDisposition _handleStockModeChange(StockMode value) {
setState(() {
stockMode = value;
});
if (modeUpdater != null)
modeUpdater(value);
return EventDisposition.processed;
}
Drawer buildDrawer() {
......@@ -156,9 +157,10 @@ class StockHome extends StatefulComponent {
);
}
void _handleShowSettings() {
EventDisposition _handleShowSettings() {
navigator.pop();
navigator.pushNamed('/settings');
return EventDisposition.processed;
}
Widget buildToolBar() {
......
......@@ -39,7 +39,7 @@ class StockSettings extends StatefulComponent {
sendUpdates();
}
void _confirmOptimismChange() {
EventDisposition _confirmOptimismChange() {
switch (optimism) {
case StockMode.optimistic:
_handleOptimismChanged(false);
......@@ -70,6 +70,7 @@ class StockSettings extends StatefulComponent {
}).then(_handleOptimismChanged);
break;
}
return EventDisposition.processed;
}
void sendUpdates() {
......
......@@ -26,17 +26,19 @@ class Key {
final String soundUrl;
MediaPlayerProxy player;
void down() {
EventDisposition down() {
if (player == null)
return;
return EventDisposition.ignored;
player.ptr.seekTo(0);
player.ptr.start();
return EventDisposition.processed;
}
void up() {
EventDisposition up() {
if (player == null)
return;
return EventDisposition.ignored;
player.ptr.pause();
return EventDisposition.processed;
}
}
......
......@@ -71,10 +71,11 @@ HAL: This mission is too important for me to allow you to jeopardize it.''';
);
}
void toggleToTextFunction(_) {
EventDisposition toggleToTextFunction(_) {
setState(() {
toText = (toText == toPlainText) ? toStyledText : toPlainText;
});
return EventDisposition.processed;
}
Widget build() {
......
......@@ -4,8 +4,14 @@
import 'dart:sky' as sky;
enum EventDisposition {
ignored,
processed,
consumed,
}
abstract class HitTestTarget {
void handleEvent(sky.Event event, HitTestEntry entry);
EventDisposition handleEvent(sky.Event event, HitTestEntry entry);
}
class HitTestEntry {
......
......@@ -107,7 +107,7 @@ class Input extends StatefulComponent {
);
}
void focus(_) {
EventDisposition focus(_) {
if (Focus.at(this)) {
assert(_keyboardHandle.attached);
_keyboardHandle.showByRequest();
......@@ -115,6 +115,7 @@ class Input extends StatefulComponent {
Focus.moveTo(this);
// we'll get told to rebuild and we'll take care of the keyboard then
}
return EventDisposition.processed;
}
void didUnmount() {
......
......@@ -7,6 +7,8 @@ import 'dart:math' as math;
import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/object.dart';
export 'package:sky/rendering/object.dart' show EventDisposition;
class FlexBoxParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> {
int flex;
......
......@@ -12,7 +12,7 @@ import 'package:sky/base/node.dart';
import 'package:sky/base/scheduler.dart' as scheduler;
export 'dart:sky' show Point, Offset, Size, Rect, Color, Paint, Path;
export 'package:sky/base/hit_test.dart' show HitTestTarget, HitTestEntry, HitTestResult;
export 'package:sky/base/hit_test.dart' show EventDisposition, HitTestTarget, HitTestEntry, HitTestResult;
class ParentData {
void detach() {
......@@ -415,9 +415,10 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
// EVENTS
void handleEvent(sky.Event event, HitTestEntry entry) {
EventDisposition handleEvent(sky.Event event, HitTestEntry entry) {
// override this if you have a client, to hand it to the client
// override this if you want to do anything with the event
return EventDisposition.ignored;
}
......
......@@ -109,7 +109,7 @@ class SkyBinding {
return state;
}
void _handlePointerEvent(sky.PointerEvent event) {
EventDisposition _handlePointerEvent(sky.PointerEvent event) {
Point position = new Point(event.x, event.y);
PointerState state = _getOrCreateStateForPointer(event, position);
......@@ -123,13 +123,20 @@ class SkyBinding {
event.dy = position.y - state.lastPosition.y;
state.lastPosition = position;
dispatchEvent(event, state.result);
return dispatchEvent(event, state.result);
}
void dispatchEvent(sky.Event event, HitTestResult result) {
EventDisposition dispatchEvent(sky.Event event, HitTestResult result) {
assert(result != null);
for (HitTestEntry entry in result.path.reversed)
entry.target.handleEvent(event, entry);
EventDisposition disposition = EventDisposition.ignored;
for (HitTestEntry entry in result.path.reversed) {
EventDisposition entryDisposition = entry.target.handleEvent(event, entry);
if (entryDisposition == EventDisposition.consumed)
return EventDisposition.consumed;
else if (entryDisposition == EventDisposition.processed)
disposition = EventDisposition.processed;
}
return disposition;
}
String toString() => 'Render Tree:\n${_renderView}';
......
......@@ -19,6 +19,7 @@ import 'package:sky/rendering/stack.dart';
import 'package:sky/widgets/default_text_style.dart';
import 'package:sky/widgets/widget.dart';
export 'package:sky/base/hit_test.dart' show EventDisposition;
export 'package:sky/rendering/box.dart' show BackgroundImage, BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims;
export 'package:sky/rendering/flex.dart' show FlexDirection, FlexJustifyContent, FlexAlignItems;
export 'package:sky/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path;
......
......@@ -14,20 +14,23 @@ abstract class ButtonBase extends StatefulComponent {
highlight = source.highlight;
}
void _handlePointerDown(_) {
EventDisposition _handlePointerDown(_) {
setState(() {
highlight = true;
});
return EventDisposition.processed;
}
void _handlePointerUp(_) {
EventDisposition _handlePointerUp(_) {
setState(() {
highlight = false;
});
return EventDisposition.processed;
}
void _handlePointerCancel(_) {
EventDisposition _handlePointerCancel(_) {
setState(() {
highlight = false;
});
return EventDisposition.processed;
}
Widget build() {
......
......@@ -99,15 +99,16 @@ class Dismissable extends StatefulComponent {
_maybeCallOnResized();
}
void _handlePointerDown(sky.PointerEvent event) {
EventDisposition _handlePointerDown(sky.PointerEvent event) {
_dragUnderway = true;
_dragX = 0.0;
_fadePerformance.progress = 0.0;
return EventDisposition.processed;
}
void _handlePointerMove(sky.PointerEvent event) {
EventDisposition _handlePointerMove(sky.PointerEvent event) {
if (!_isActive)
return;
return EventDisposition.ignored;
double oldDragX = _dragX;
_dragX += event.dx;
......@@ -115,17 +116,19 @@ class Dismissable extends StatefulComponent {
setState(() {}); // Rebuild to update the new drag endpoint.
if (!_fadePerformance.isAnimating)
_fadePerformance.progress = _dragX.abs() / (_size.width * _kDismissCardThreshold);
return EventDisposition.processed;
}
void _handlePointerUpOrCancel(_) {
EventDisposition _handlePointerUpOrCancel(_) {
if (!_isActive)
return;
return EventDisposition.ignored;
_dragUnderway = false;
if (_fadePerformance.isCompleted)
_startResizePerformance();
else if (!_fadePerformance.isAnimating)
_fadePerformance.reverse();
return EventDisposition.processed;
}
bool _isHorizontalFlingGesture(sky.GestureEvent event) {
......@@ -134,15 +137,16 @@ class Dismissable extends StatefulComponent {
return vx - vy > _kMinFlingVelocityDelta && vx > _kMinFlingVelocity;
}
void _handleFlingStart(sky.GestureEvent event) {
EventDisposition _handleFlingStart(sky.GestureEvent event) {
if (!_isActive)
return;
return EventDisposition.ignored;
if (_isHorizontalFlingGesture(event)) {
_dragUnderway = false;
_dragX = event.velocityX.sign;
_fadePerformance.fling(Direction.forward, velocity: event.velocityX.abs() * _kFlingVelocityScale);
}
return EventDisposition.processed;
}
void _handleSizeChanged(Size newSize) {
......
......@@ -139,33 +139,48 @@ class Drawer extends StatefulComponent {
void _settle() { _isMostlyClosed ? _performance.reverse() : _performance.play(); }
void handleMaskTap(_) { _performance.reverse(); }
EventDisposition handleMaskTap(_) {
_performance.reverse();
return EventDisposition.consumed;
}
// TODO(mpcomplete): Figure out how to generalize these handlers on a
// "PannableThingy" interface.
void handlePointerDown(_) { _performance.stop(); }
EventDisposition handlePointerDown(_) {
_performance.stop();
return EventDisposition.processed;
}
void handlePointerMove(sky.PointerEvent event) {
EventDisposition handlePointerMove(sky.PointerEvent event) {
if (_performance.isAnimating)
return;
return EventDisposition.ignored;
_performance.progress += event.dx / _kWidth;
return EventDisposition.processed;
}
void handlePointerUp(_) {
if (!_performance.isAnimating)
EventDisposition handlePointerUp(_) {
if (!_performance.isAnimating) {
_settle();
return EventDisposition.processed;
}
return EventDisposition.ignored;
}
void handlePointerCancel(_) {
if (!_performance.isAnimating)
EventDisposition handlePointerCancel(_) {
if (!_performance.isAnimating) {
_settle();
return EventDisposition.processed;
}
return EventDisposition.ignored;
}
void handleFlingStart(event) {
EventDisposition handleFlingStart(event) {
if (event.velocityX.abs() >= _kMinFlingVelocity) {
_performance.fling(
event.velocityX < 0.0 ? Direction.reverse : Direction.forward,
velocity: event.velocityX.abs() * _kFlingVelocityScale);
return EventDisposition.processed;
}
return EventDisposition.ignored;
}
}
......@@ -14,13 +14,15 @@ import 'package:sky/widgets/ink_well.dart';
import 'package:sky/widgets/theme.dart';
import 'package:sky/widgets/widget.dart';
typedef EventDisposition OnPressedFunction();
class DrawerItem extends ButtonBase {
DrawerItem({ Key key, this.icon, this.children, this.onPressed, this.selected: false })
: super(key: key);
String icon;
List<Widget> children;
Function onPressed;
OnPressedFunction onPressed;
bool selected;
void syncFields(DrawerItem source) {
......@@ -82,7 +84,8 @@ class DrawerItem extends ButtonBase {
return new Listener(
onGestureTap: (_) {
if (onPressed != null)
onPressed();
return onPressed();
return EventDisposition.ignored;
},
child: new Container(
height: 48.0,
......
......@@ -84,17 +84,18 @@ class RenderInkWell extends RenderProxyBox {
final List<InkSplash> _splashes = new List<InkSplash>();
void handleEvent(sky.Event event, BoxHitTestEntry entry) {
EventDisposition handleEvent(sky.Event event, BoxHitTestEntry entry) {
if (event is sky.GestureEvent) {
switch (event.type) {
case 'gesturetapdown':
_startSplash(event.primaryPointer, entry.localPosition);
break;
return EventDisposition.processed;
case 'gesturetap':
_confirmSplash(event.primaryPointer);
break;
return EventDisposition.processed;
}
}
return EventDisposition.ignored;
}
void _startSplash(int pointer, Point position) {
......
......@@ -68,12 +68,13 @@ class Radio extends ButtonBase {
}
)
),
onGestureTap: _handleClick
onGestureTap: _handleTap
);
}
void _handleClick(_) {
EventDisposition _handleTap(_) {
onChanged(value);
return EventDisposition.consumed;
}
}
......@@ -181,20 +181,23 @@ abstract class Scrollable extends StatefulComponent {
scrollTo(value);
}
void _handlePointerDown(_) {
EventDisposition _handlePointerDown(_) {
_stopToEndAnimation();
_stopToOffsetAnimation();
return EventDisposition.processed;
}
void _handleScrollUpdate(sky.GestureEvent event) {
EventDisposition _handleScrollUpdate(sky.GestureEvent event) {
scrollBy(direction == ScrollDirection.horizontal ? event.dx : -event.dy);
return EventDisposition.processed;
}
void _handleFlingStart(sky.GestureEvent event) {
EventDisposition _handleFlingStart(sky.GestureEvent event) {
double eventVelocity = direction == ScrollDirection.horizontal
? -event.velocityX
: -event.velocityY;
_startToEndAnimation(velocity: _velocityForFlingGesture(eventVelocity));
return EventDisposition.processed;
}
void _maybeSettleScrollOffset() {
......@@ -202,13 +205,20 @@ abstract class Scrollable extends StatefulComponent {
settleScrollOffset();
}
void _handlePointerUpOrCancel(_) { _maybeSettleScrollOffset(); }
EventDisposition _handlePointerUpOrCancel(_) {
_maybeSettleScrollOffset();
return EventDisposition.processed;
}
void _handleFlingCancel(sky.GestureEvent event) { _maybeSettleScrollOffset(); }
EventDisposition _handleFlingCancel(sky.GestureEvent event) {
_maybeSettleScrollOffset();
return EventDisposition.processed;
}
void _handleWheel(sky.WheelEvent event) {
EventDisposition _handleWheel(sky.WheelEvent event) {
scrollBy(-event.offsetY);
return EventDisposition.processed;
}
}
......
......@@ -80,9 +80,12 @@ class _RenderSwitch extends RenderConstrainedBox {
..addListener(markNeedsPaint);
}
void handleEvent(sky.Event event, BoxHitTestEntry entry) {
if (event is sky.GestureEvent &&
event.type == 'gesturetap') _onChanged(!_value);
EventDisposition handleEvent(sky.Event event, BoxHitTestEntry entry) {
if (event is sky.GestureEvent && event.type == 'gesturetap') {
_onChanged(!_value);
return EventDisposition.consumed;
}
return EventDisposition.ignored;
}
bool _value;
......
......@@ -475,7 +475,7 @@ class TabBar extends Scrollable {
.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
}
void _handleTap(int tabIndex) {
EventDisposition _handleTap(int tabIndex) {
if (tabIndex != selectedIndex) {
if (_tabWidths != null) {
if (isScrollable)
......@@ -484,7 +484,9 @@ class TabBar extends Scrollable {
}
if (onChanged != null)
onChanged(tabIndex);
return EventDisposition.processed;
}
return EventDisposition.ignored;
}
Widget _toTab(TabLabel label, int tabIndex) {
......
......@@ -57,8 +57,9 @@ abstract class Toggleable extends AnimatedComponent {
super.syncFields(source);
}
void _handleClick(sky.Event e) {
EventDisposition _handleClick(sky.Event e) {
onChanged(!value);
return EventDisposition.consumed;
}
// Override these to draw yourself
......
......@@ -13,6 +13,7 @@ import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/object.dart';
import 'package:sky/rendering/sky_binding.dart';
export 'package:sky/base/hit_test.dart' show EventDisposition;
export 'package:sky/rendering/box.dart' show BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims;
export 'package:sky/rendering/flex.dart' show FlexDirection;
export 'package:sky/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path;
......@@ -446,9 +447,9 @@ abstract class Inherited extends TagNode {
}
typedef void GestureEventListener(sky.GestureEvent e);
typedef void PointerEventListener(sky.PointerEvent e);
typedef void EventListener(sky.Event e);
typedef EventDisposition GestureEventListener(sky.GestureEvent e);
typedef EventDisposition PointerEventListener(sky.PointerEvent e);
typedef EventDisposition EventListener(sky.Event e);
class Listener extends TagNode {
......@@ -529,11 +530,12 @@ class Listener extends TagNode {
return listeners;
}
void _handleEvent(sky.Event e) {
EventDisposition _handleEvent(sky.Event e) {
EventListener listener = listeners[e.type];
if (listener != null) {
listener(e);
return listener(e);
}
return EventDisposition.ignored;
}
}
......@@ -1169,20 +1171,29 @@ class WidgetSkyBinding extends SkyBinding {
assert(SkyBinding.instance is WidgetSkyBinding);
}
void dispatchEvent(sky.Event event, HitTestResult result) {
EventDisposition dispatchEvent(sky.Event event, HitTestResult result) {
assert(SkyBinding.instance == this);
super.dispatchEvent(event, result);
EventDisposition disposition = super.dispatchEvent(event, result);
if (disposition == EventDisposition.consumed)
return EventDisposition.consumed;
for (HitTestEntry entry in result.path.reversed) {
Widget target = RenderObjectWrapper._getMounted(entry.target);
if (target == null)
continue;
RenderObject targetRoot = target.root;
while (target != null && target.root == targetRoot) {
if (target is Listener)
target._handleEvent(event);
if (target is Listener) {
EventDisposition targetDisposition = target._handleEvent(event);
if (targetDisposition == EventDisposition.consumed) {
return targetDisposition;
} else if (targetDisposition == EventDisposition.processed) {
disposition = EventDisposition.processed;
}
}
target = target._parent;
}
}
return disposition;
}
void beginFrame(double timeStamp) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册