Commit 50c2f883 authored by Adam Barth's avatar Adam Barth

Don't hardcode the list of events types in fn

This CL changes how events work in fn. Previously, event listeners were passed
in as constructor arguments. Now Nodes hold an |events| object, which contains
all the event registrations. When a Component renders, all its |events| are
copied onto the Node it produces. When an Element syncs, it walks its |events|
and adds them as event listeners on the underlying sky.Element.

The net result of this change is increased flexibility in how events are
registered. Now components don't need to enumerate all the possible events that
they support. Instead, the parent component can listen for whatever events it
likes.

Also, I've cleaned up the association between DrawerAnimation and Drawer. Now
the constructor for Drawer accepts an |animation| object and wires up its
internal event handlers itself instead of requiring the constructor to do all
the wiring.

R=rafaelw@chromium.org

Review URL: https://codereview.chromium.org/975863003
parent 3f347184
...@@ -103,6 +103,8 @@ abstract class Component extends Node { ...@@ -103,6 +103,8 @@ abstract class Component extends Node {
_currentlyRendering = null; _currentlyRendering = null;
_currentOrder = lastOrder; _currentOrder = lastOrder;
_rendered.events.addAll(events);
_dirty = false; _dirty = false;
// TODO(rafaelw): This prevents components from returning different node // TODO(rafaelw): This prevents components from returning different node
......
// 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.
part of fn;
class EventHandler {
final String type;
final sky.EventListener listener;
EventHandler(this.type, this.listener);
}
class EventMap {
final List<EventHandler> _handlers = new List<EventHandler>();
void listen(String type, sky.EventListener listener) {
assert(listener != null);
_handlers.add(new EventHandler(type, listener));
}
void addAll(EventMap events) {
_handlers.addAll(events._handlers);
}
}
...@@ -10,6 +10,7 @@ import 'dart:sky' as sky; ...@@ -10,6 +10,7 @@ import 'dart:sky' as sky;
import 'reflect.dart' as reflect; import 'reflect.dart' as reflect;
part 'component.dart'; part 'component.dart';
part 'event.dart';
part 'node.dart'; part 'node.dart';
part 'style.dart'; part 'style.dart';
......
...@@ -18,6 +18,10 @@ abstract class Node { ...@@ -18,6 +18,10 @@ abstract class Node {
String _key = null; String _key = null;
sky.Node _root = null; sky.Node _root = null;
// TODO(abarth): Both Elements and Components have |events| but |Text|
// doesn't. Should we add a common base class to contain |events|?
final EventMap events = new EventMap();
Node({ Object key }) { Node({ Object key }) {
_key = key == null ? "$runtimeType" : "$runtimeType-$key"; _key = key == null ? "$runtimeType" : "$runtimeType-$key";
} }
...@@ -65,19 +69,6 @@ abstract class Element extends Node { ...@@ -65,19 +69,6 @@ abstract class Element extends Node {
String inlineStyle; String inlineStyle;
sky.EventListener onClick;
sky.EventListener onFlingCancel;
sky.EventListener onFlingStart;
sky.EventListener onGestureTap;
sky.EventListener onPointerCancel;
sky.EventListener onPointerDown;
sky.EventListener onPointerMove;
sky.EventListener onPointerUp;
sky.EventListener onScrollEnd;
sky.EventListener onScrollStart;
sky.EventListener onScrollUpdate;
sky.EventListener onWheel;
List<Node> _children = null; List<Node> _children = null;
String _className = ''; String _className = '';
...@@ -86,21 +77,7 @@ abstract class Element extends Node { ...@@ -86,21 +77,7 @@ abstract class Element extends Node {
List<Node> children, List<Node> children,
Style style, Style style,
this.inlineStyle, this.inlineStyle
// Events
this.onClick,
this.onFlingCancel,
this.onFlingStart,
this.onGestureTap,
this.onPointerCancel,
this.onPointerDown,
this.onPointerMove,
this.onPointerUp,
this.onScrollEnd,
this.onScrollStart,
this.onScrollUpdate,
this.onWheel
}) : super(key:key) { }) : super(key:key) {
_className = style == null ? '': style._className; _className = style == null ? '': style._className;
...@@ -135,34 +112,48 @@ abstract class Element extends Node { ...@@ -135,34 +112,48 @@ abstract class Element extends Node {
} }
} }
void _syncEvent(String eventName, sky.EventListener listener, void _syncEvents([Element old]) {
sky.EventListener oldListener) { List<EventHandler> newHandlers = events._handlers;
sky.Element root = _root as sky.Element; int newStartIndex = 0;
if (listener == oldListener) int newEndIndex = newHandlers.length;
return;
List<EventHandler> oldHandlers = old.events._handlers;
if (oldListener != null) { int oldStartIndex = 0;
root.removeEventListener(eventName, oldListener); int oldEndIndex = oldHandlers.length;
// Skip over leading handlers that match.
while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) {
EventHandler newHander = newHandlers[newStartIndex];
EventHandler oldHandler = oldHandlers[oldStartIndex];
if (newHander.type != oldHandler.type
|| newHander.listener != oldHandler.listener)
break;
++newStartIndex;
++oldStartIndex;
} }
if (listener != null) { // Skip over trailing handlers that match.
root.addEventListener(eventName, listener); while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) {
EventHandler newHander = newHandlers[newEndIndex - 1];
EventHandler oldHandler = oldHandlers[oldEndIndex - 1];
if (newHander.type != oldHandler.type
|| newHander.listener != oldHandler.listener)
break;
--newEndIndex;
--oldEndIndex;
} }
sky.Element root = _root as sky.Element;
for (int i = oldStartIndex; i < oldEndIndex; ++i) {
EventHandler oldHandler = oldHandlers[i];
root.removeEventListener(oldHandler.type, oldHandler.listener);
} }
void _syncEvents([Element old]) { for (int i = newStartIndex; i < newEndIndex; ++i) {
_syncEvent('click', onClick, old.onClick); EventHandler newHander = newHandlers[i];
_syncEvent('gestureflingcancel', onFlingCancel, old.onFlingCancel); root.addEventListener(newHander.type, newHander.listener);
_syncEvent('gestureflingstart', onFlingStart, old.onFlingStart); }
_syncEvent('gesturescrollend', onScrollEnd, old.onScrollEnd);
_syncEvent('gesturescrollstart', onScrollStart, old.onScrollStart);
_syncEvent('gesturescrollupdate', onScrollUpdate, old.onScrollUpdate);
_syncEvent('gesturetap', onGestureTap, old.onGestureTap);
_syncEvent('pointercancel', onPointerCancel, old.onPointerCancel);
_syncEvent('pointerdown', onPointerDown, old.onPointerDown);
_syncEvent('pointermove', onPointerMove, old.onPointerMove);
_syncEvent('pointerup', onPointerUp, old.onPointerUp);
_syncEvent('wheel', onWheel, old.onWheel);
} }
void _syncNode([Element old]) { void _syncNode([Element old]) {
...@@ -356,36 +347,12 @@ class Container extends Element { ...@@ -356,36 +347,12 @@ class Container extends Element {
Object key, Object key,
List<Node> children, List<Node> children,
Style style, Style style,
String inlineStyle, String inlineStyle
sky.EventListener onClick,
sky.EventListener onFlingCancel,
sky.EventListener onFlingStart,
sky.EventListener onGestureTap,
sky.EventListener onPointerCancel,
sky.EventListener onPointerDown,
sky.EventListener onPointerMove,
sky.EventListener onPointerUp,
sky.EventListener onScrollEnd,
sky.EventListener onScrollStart,
sky.EventListener onScrollUpdate,
sky.EventListener onWheel
}) : super( }) : super(
key: key, key: key,
children: children, children: children,
style: style, style: style,
inlineStyle: inlineStyle, inlineStyle: inlineStyle
onClick: onClick,
onFlingCancel: onFlingCancel,
onFlingStart: onFlingStart,
onGestureTap: onGestureTap,
onPointerCancel: onPointerCancel,
onPointerDown: onPointerDown,
onPointerMove: onPointerMove,
onPointerUp: onPointerUp,
onScrollEnd: onScrollEnd,
onScrollStart: onScrollStart,
onScrollUpdate: onScrollUpdate,
onWheel: onWheel
); );
} }
...@@ -405,18 +372,6 @@ class Image extends Element { ...@@ -405,18 +372,6 @@ class Image extends Element {
List<Node> children, List<Node> children,
Style style, Style style,
String inlineStyle, String inlineStyle,
sky.EventListener onClick,
sky.EventListener onFlingCancel,
sky.EventListener onFlingStart,
sky.EventListener onGestureTap,
sky.EventListener onPointerCancel,
sky.EventListener onPointerDown,
sky.EventListener onPointerMove,
sky.EventListener onPointerUp,
sky.EventListener onScrollEnd,
sky.EventListener onScrollStart,
sky.EventListener onScrollUpdate,
sky.EventListener onWheel,
this.width, this.width,
this.height, this.height,
this.src this.src
...@@ -424,19 +379,7 @@ class Image extends Element { ...@@ -424,19 +379,7 @@ class Image extends Element {
key: key, key: key,
children: children, children: children,
style: style, style: style,
inlineStyle: inlineStyle, inlineStyle: inlineStyle
onClick: onClick,
onFlingCancel: onFlingCancel,
onFlingStart: onFlingStart,
onGestureTap: onGestureTap,
onPointerCancel: onPointerCancel,
onPointerDown: onPointerDown,
onPointerMove: onPointerMove,
onPointerUp: onPointerUp,
onScrollEnd: onScrollEnd,
onScrollStart: onScrollStart,
onScrollUpdate: onScrollUpdate,
onWheel: onWheel
); );
void _syncNode([Element old]) { void _syncNode([Element old]) {
...@@ -470,18 +413,6 @@ class Anchor extends Element { ...@@ -470,18 +413,6 @@ class Anchor extends Element {
List<Node> children, List<Node> children,
Style style, Style style,
String inlineStyle, String inlineStyle,
sky.EventListener onClick,
sky.EventListener onFlingCancel,
sky.EventListener onFlingStart,
sky.EventListener onGestureTap,
sky.EventListener onPointerCancel,
sky.EventListener onPointerDown,
sky.EventListener onPointerMove,
sky.EventListener onPointerUp,
sky.EventListener onScrollEnd,
sky.EventListener onScrollStart,
sky.EventListener onScrollUpdate,
sky.EventListener onWheel,
this.width, this.width,
this.height, this.height,
this.href this.href
...@@ -489,19 +420,7 @@ class Anchor extends Element { ...@@ -489,19 +420,7 @@ class Anchor extends Element {
key: key, key: key,
children: children, children: children,
style: style, style: style,
inlineStyle: inlineStyle, inlineStyle: inlineStyle
onClick: onClick,
onFlingCancel: onFlingCancel,
onFlingStart: onFlingStart,
onGestureTap: onGestureTap,
onPointerCancel: onPointerCancel,
onPointerDown: onPointerDown,
onPointerMove: onPointerMove,
onPointerUp: onPointerUp,
onScrollEnd: onScrollEnd,
onScrollStart: onScrollStart,
onScrollUpdate: onScrollUpdate,
onWheel: onWheel
); );
void _syncNode([Element old]) { void _syncNode([Element old]) {
......
...@@ -26,18 +26,13 @@ class Button extends ButtonBase { ...@@ -26,18 +26,13 @@ class Button extends ButtonBase {
); );
Node content; Node content;
sky.EventListener onClick;
Button({ Object key, this.content, this.onClick }) : super(key: key); Button({ Object key, this.content }) : super(key: key);
Node render() { Node render() {
return new Container( return new Container(
key: 'Button', key: 'Button',
style: _highlight ? _highlightStyle : _style, style: _highlight ? _highlightStyle : _style,
onClick: onClick,
onPointerDown: _handlePointerDown,
onPointerUp: _handlePointerUp,
onPointerCancel: _handlePointerCancel,
children: [super.render(), content] children: [super.render(), content]
); );
} }
......
...@@ -4,7 +4,11 @@ abstract class ButtonBase extends MaterialComponent { ...@@ -4,7 +4,11 @@ abstract class ButtonBase extends MaterialComponent {
bool _highlight = false; bool _highlight = false;
ButtonBase({ Object key }) : super(key: key); ButtonBase({ Object key }) : super(key: key) {
events.listen('pointerdown', _handlePointerDown);
events.listen('pointerup', _handlePointerUp);
events.listen('pointercancel', _handlePointerCancel);
}
void _handlePointerDown(_) { void _handlePointerDown(_) {
setState(() { setState(() {
......
...@@ -57,10 +57,6 @@ class Checkbox extends ButtonBase { ...@@ -57,10 +57,6 @@ class Checkbox extends ButtonBase {
Node render() { Node render() {
return new Container( return new Container(
style: _style, style: _style,
onClick: _handleClick,
onPointerDown: _handlePointerDown,
onPointerUp: _handlePointerUp,
onPointerCancel: _handlePointerCancel,
children: [ children: [
super.render(), super.render(),
new Container( new Container(
...@@ -72,7 +68,7 @@ class Checkbox extends ButtonBase { ...@@ -72,7 +68,7 @@ class Checkbox extends ButtonBase {
] ]
) )
] ]
); )..events.listen('click', _handleClick);
} }
void _handleClick(sky.Event e) { void _handleClick(sky.Event e) {
......
...@@ -127,24 +127,12 @@ class Drawer extends Component { ...@@ -127,24 +127,12 @@ class Drawer extends Component {
bottom: 0;''' bottom: 0;'''
); );
Stream<double> onPositionChanged; DrawerAnimation animation;
sky.EventListener handleMaskFling;
sky.EventListener handleMaskTap;
sky.EventListener handlePointerCancel;
sky.EventListener handlePointerDown;
sky.EventListener handlePointerMove;
sky.EventListener handlePointerUp;
List<Node> children; List<Node> children;
Drawer({ Drawer({
Object key, Object key,
this.onPositionChanged, this.animation,
this.handleMaskFling,
this.handleMaskTap,
this.handlePointerCancel,
this.handlePointerDown,
this.handlePointerMove,
this.handlePointerUp,
this.children this.children
}) : super(key: key); }) : super(key: key);
...@@ -157,7 +145,7 @@ class Drawer extends Component { ...@@ -157,7 +145,7 @@ class Drawer extends Component {
return; return;
_listening = true; _listening = true;
onPositionChanged.listen((position) { animation.onPositionChanged.listen((position) {
setState(() { setState(() {
_position = position; _position = position;
}); });
...@@ -172,29 +160,28 @@ class Drawer extends Component { ...@@ -172,29 +160,28 @@ class Drawer extends Component {
String maskInlineStyle = 'opacity: ${(_position / _kWidth + 1) * 0.25}'; String maskInlineStyle = 'opacity: ${(_position / _kWidth + 1) * 0.25}';
String contentInlineStyle = 'transform: translateX(${_position}px)'; String contentInlineStyle = 'transform: translateX(${_position}px)';
return new Container( Container mask = new Container(
style: _style,
inlineStyle: inlineStyle,
onPointerDown: handlePointerDown,
onPointerMove: handlePointerMove,
onPointerUp: handlePointerUp,
onPointerCancel: handlePointerCancel,
children: [
new Container(
key: 'Mask', key: 'Mask',
style: _maskStyle, style: _maskStyle,
inlineStyle: maskInlineStyle, inlineStyle: maskInlineStyle
onGestureTap: handleMaskTap, )..events.listen('gesturetap', animation.handleMaskTap)
onFlingStart: handleMaskFling ..events.listen('gestureflingstart', animation.handleFlingStart);
),
new Container( Container content = new Container(
key: 'Content', key: 'Content',
style: _contentStyle, style: _contentStyle,
inlineStyle: contentInlineStyle, inlineStyle: contentInlineStyle,
children: children children: children
)
]
); );
return new Container(
style: _style,
inlineStyle: inlineStyle,
children: [ mask, content ]
)..events.listen('pointerdown', animation.handlePointerDown)
..events.listen('pointermove', animation.handlePointerMove)
..events.listen('pointerup', animation.handlePointerUp)
..events.listen('pointercancel', animation.handlePointerCancel);
} }
} }
...@@ -50,10 +50,6 @@ abstract class FixedHeightScrollable extends Component { ...@@ -50,10 +50,6 @@ abstract class FixedHeightScrollable extends Component {
return new Container( return new Container(
style: _style, style: _style,
onFlingStart: _handleFlingStart,
onFlingCancel: _handleFlingCancel,
onScrollUpdate: _handleScrollUpdate,
onWheel: _handleWheel,
children: [ children: [
new Container( new Container(
style: _scrollAreaStyle, style: _scrollAreaStyle,
...@@ -61,7 +57,11 @@ abstract class FixedHeightScrollable extends Component { ...@@ -61,7 +57,11 @@ abstract class FixedHeightScrollable extends Component {
children: items children: items
) )
] ]
); )
..events.listen('gestureflingstart', _handleFlingStart)
..events.listen('gestureflingcancel', _handleFlingCancel)
..events.listen('gesturescrollupdate', _handleScrollUpdate)
..events.listen('wheel', _handleWheel);
} }
void willUnmount() { void willUnmount() {
......
...@@ -7,14 +7,12 @@ class Icon extends Component { ...@@ -7,14 +7,12 @@ class Icon extends Component {
Style style; Style style;
int size; int size;
String type; String type;
sky.EventListener onClick;
Icon({ Icon({
String key, String key,
this.style, this.style,
this.size, this.size,
this.type: '', this.type: ''
this.onClick
}) : super(key: key); }) : super(key: key);
Node render() { Node render() {
...@@ -28,7 +26,6 @@ class Icon extends Component { ...@@ -28,7 +26,6 @@ class Icon extends Component {
return new Image( return new Image(
style: style, style: style,
onClick: onClick,
width: size, width: size,
height: size, height: size,
src: '${kAssetBase}/${category}/2x_web/ic_${subtype}_${size}dp.png' src: '${kAssetBase}/${category}/2x_web/ic_${subtype}_${size}dp.png'
......
...@@ -26,12 +26,11 @@ abstract class MaterialComponent extends Component { ...@@ -26,12 +26,11 @@ abstract class MaterialComponent extends Component {
return new Container( return new Container(
style: _style, style: _style,
onScrollStart: _cancelSplashes,
onWheel: _cancelSplashes,
onPointerDown: _startSplash,
children: children, children: children,
key: _splashesKey key: _splashesKey
); )..events.listen('gesturescrollstart', _cancelSplashes)
..events.listen('wheel', _cancelSplashes)
..events.listen('pointerdown', _startSplash);
} }
sky.ClientRect _getBoundingRect() => getRoot().getBoundingClientRect(); sky.ClientRect _getBoundingRect() => getRoot().getBoundingClientRect();
......
...@@ -33,18 +33,11 @@ class MenuItem extends ButtonBase { ...@@ -33,18 +33,11 @@ class MenuItem extends ButtonBase {
List<Node> children; List<Node> children;
String icon; String icon;
sky.EventListener onClick; MenuItem({ Object key, this.icon, this.children }) : super(key: key);
MenuItem({ Object key, this.icon, this.children, this.onClick }) : super(key: key) {
}
Node render() { Node render() {
return new Container( return new Container(
style: _highlight ? _highlightStyle : _style, style: _highlight ? _highlightStyle : _style,
onClick: onClick,
onPointerDown: _handlePointerDown,
onPointerUp: _handlePointerUp,
onPointerCancel: _handlePointerCancel,
children: [ children: [
super.render(), super.render(),
new Icon( new Icon(
......
...@@ -48,16 +48,12 @@ class Radio extends ButtonBase { ...@@ -48,16 +48,12 @@ class Radio extends ButtonBase {
Node render() { Node render() {
return new Container( return new Container(
style: _highlight ? _highlightStyle : _style, style: _highlight ? _highlightStyle : _style,
onClick: _handleClick,
onPointerDown: _handlePointerDown,
onPointerUp: _handlePointerUp,
onPointerCancel: _handlePointerCancel,
children: value == groupValue ? children: value == groupValue ?
[super.render(), new Container( style : _dotStyle )] : [super.render()] [super.render(), new Container( style : _dotStyle )] : [super.render()]
); )..events.listen('click', _handleClick);
} }
void _handleClick(sky.Event e) { void _handleClick(_) {
onChanged(value); onChanged(value);
} }
} }
...@@ -37,13 +37,7 @@ class StocksApp extends App { ...@@ -37,13 +37,7 @@ class StocksApp extends App {
Node render() { Node render() {
var drawer = new Drawer( var drawer = new Drawer(
onPositionChanged: _drawerAnimation.onPositionChanged, animation: _drawerAnimation,
handleMaskFling: _drawerAnimation.handleFlingStart,
handleMaskTap: _drawerAnimation.handleMaskTap,
handlePointerCancel: _drawerAnimation.handlePointerCancel,
handlePointerDown: _drawerAnimation.handlePointerDown,
handlePointerMove: _drawerAnimation.handlePointerMove,
handlePointerUp: _drawerAnimation.handlePointerUp,
children: [ children: [
new DrawerHeader( new DrawerHeader(
children: [new Text('Stocks')] children: [new Text('Stocks')]
...@@ -76,9 +70,9 @@ class StocksApp extends App { ...@@ -76,9 +70,9 @@ class StocksApp extends App {
var toolbar = new Toolbar( var toolbar = new Toolbar(
children: [ children: [
new Icon(key: 'menu', style: _iconStyle, new Icon(key: 'menu', style: _iconStyle,
onClick: _drawerAnimation.toggle,
size: 24, size: 24,
type: 'navigation/menu_white'), type: 'navigation/menu_white')
..events.listen('click', _drawerAnimation.toggle),
new Container( new Container(
style: _titleStyle, style: _titleStyle,
children: [new Text('I am a stocks app')] children: [new Text('I am a stocks app')]
......
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