From 075635df2964d7c31f1fbb452e1161c4f6298eae Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 5 Mar 2015 08:00:24 -0800 Subject: [PATCH] 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 --- examples/fn/lib/component.dart | 2 + examples/fn/lib/event.dart | 25 +++ examples/fn/lib/fn.dart | 1 + examples/fn/lib/node.dart | 173 +++++------------- examples/fn/widgets/button.dart | 7 +- examples/fn/widgets/buttonbase.dart | 6 +- examples/fn/widgets/checkbox.dart | 6 +- examples/fn/widgets/drawer.dart | 59 +++--- .../fn/widgets/fixedheightscrollable.dart | 10 +- examples/fn/widgets/icon.dart | 5 +- examples/fn/widgets/material.dart | 7 +- examples/fn/widgets/menuitem.dart | 9 +- examples/fn/widgets/radio.dart | 8 +- examples/stocks-fn/stocksapp.dart | 12 +- 14 files changed, 119 insertions(+), 211 deletions(-) create mode 100644 examples/fn/lib/event.dart diff --git a/examples/fn/lib/component.dart b/examples/fn/lib/component.dart index 1503e6348..93900235b 100644 --- a/examples/fn/lib/component.dart +++ b/examples/fn/lib/component.dart @@ -103,6 +103,8 @@ abstract class Component extends Node { _currentlyRendering = null; _currentOrder = lastOrder; + _rendered.events.addAll(events); + _dirty = false; // TODO(rafaelw): This prevents components from returning different node diff --git a/examples/fn/lib/event.dart b/examples/fn/lib/event.dart new file mode 100644 index 000000000..5b96d739d --- /dev/null +++ b/examples/fn/lib/event.dart @@ -0,0 +1,25 @@ +// 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 _handlers = new List(); + + void listen(String type, sky.EventListener listener) { + assert(listener != null); + _handlers.add(new EventHandler(type, listener)); + } + + void addAll(EventMap events) { + _handlers.addAll(events._handlers); + } +} diff --git a/examples/fn/lib/fn.dart b/examples/fn/lib/fn.dart index ab67c50bd..a1d6e2ba4 100644 --- a/examples/fn/lib/fn.dart +++ b/examples/fn/lib/fn.dart @@ -10,6 +10,7 @@ import 'dart:sky' as sky; import 'reflect.dart' as reflect; part 'component.dart'; +part 'event.dart'; part 'node.dart'; part 'style.dart'; diff --git a/examples/fn/lib/node.dart b/examples/fn/lib/node.dart index 1339e204e..54853b3e8 100644 --- a/examples/fn/lib/node.dart +++ b/examples/fn/lib/node.dart @@ -18,6 +18,10 @@ abstract class Node { String _key = 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 }) { _key = key == null ? "$runtimeType" : "$runtimeType-$key"; } @@ -65,19 +69,6 @@ abstract class Element extends Node { 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 _children = null; String _className = ''; @@ -86,21 +77,7 @@ abstract class Element extends Node { List children, Style style, - 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 + this.inlineStyle }) : super(key:key) { _className = style == null ? '': style._className; @@ -135,34 +112,48 @@ abstract class Element extends Node { } } - void _syncEvent(String eventName, sky.EventListener listener, - sky.EventListener oldListener) { - sky.Element root = _root as sky.Element; - if (listener == oldListener) - return; + void _syncEvents([Element old]) { + List newHandlers = events._handlers; + int newStartIndex = 0; + int newEndIndex = newHandlers.length; + + List oldHandlers = old.events._handlers; + int oldStartIndex = 0; + 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 (oldListener != null) { - root.removeEventListener(eventName, oldListener); + // Skip over trailing handlers that match. + 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; } - if (listener != null) { - root.addEventListener(eventName, listener); + 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]) { - _syncEvent('click', onClick, old.onClick); - _syncEvent('gestureflingcancel', onFlingCancel, old.onFlingCancel); - _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); + for (int i = newStartIndex; i < newEndIndex; ++i) { + EventHandler newHander = newHandlers[i]; + root.addEventListener(newHander.type, newHander.listener); + } } void _syncNode([Element old]) { @@ -356,36 +347,12 @@ class Container extends Element { Object key, List children, Style style, - 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 + String inlineStyle }) : super( key: key, children: children, style: style, - 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 + inlineStyle: inlineStyle ); } @@ -405,18 +372,6 @@ class Image extends Element { List children, Style style, 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.height, this.src @@ -424,19 +379,7 @@ class Image extends Element { key: key, children: children, style: style, - 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 + inlineStyle: inlineStyle ); void _syncNode([Element old]) { @@ -470,18 +413,6 @@ class Anchor extends Element { List children, Style style, 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.height, this.href @@ -489,19 +420,7 @@ class Anchor extends Element { key: key, children: children, style: style, - 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 + inlineStyle: inlineStyle ); void _syncNode([Element old]) { diff --git a/examples/fn/widgets/button.dart b/examples/fn/widgets/button.dart index ac7f62d62..c7f53cac5 100644 --- a/examples/fn/widgets/button.dart +++ b/examples/fn/widgets/button.dart @@ -26,18 +26,13 @@ class Button extends ButtonBase { ); 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() { return new Container( key: 'Button', style: _highlight ? _highlightStyle : _style, - onClick: onClick, - onPointerDown: _handlePointerDown, - onPointerUp: _handlePointerUp, - onPointerCancel: _handlePointerCancel, children: [super.render(), content] ); } diff --git a/examples/fn/widgets/buttonbase.dart b/examples/fn/widgets/buttonbase.dart index 461b44d49..a69202280 100644 --- a/examples/fn/widgets/buttonbase.dart +++ b/examples/fn/widgets/buttonbase.dart @@ -4,7 +4,11 @@ abstract class ButtonBase extends MaterialComponent { 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(_) { setState(() { diff --git a/examples/fn/widgets/checkbox.dart b/examples/fn/widgets/checkbox.dart index 480264756..2825498d6 100644 --- a/examples/fn/widgets/checkbox.dart +++ b/examples/fn/widgets/checkbox.dart @@ -57,10 +57,6 @@ class Checkbox extends ButtonBase { Node render() { return new Container( style: _style, - onClick: _handleClick, - onPointerDown: _handlePointerDown, - onPointerUp: _handlePointerUp, - onPointerCancel: _handlePointerCancel, children: [ super.render(), new Container( @@ -72,7 +68,7 @@ class Checkbox extends ButtonBase { ] ) ] - ); + )..events.listen('click', _handleClick); } void _handleClick(sky.Event e) { diff --git a/examples/fn/widgets/drawer.dart b/examples/fn/widgets/drawer.dart index 229022011..26edbb2f0 100644 --- a/examples/fn/widgets/drawer.dart +++ b/examples/fn/widgets/drawer.dart @@ -127,24 +127,12 @@ class Drawer extends Component { bottom: 0;''' ); - Stream onPositionChanged; - sky.EventListener handleMaskFling; - sky.EventListener handleMaskTap; - sky.EventListener handlePointerCancel; - sky.EventListener handlePointerDown; - sky.EventListener handlePointerMove; - sky.EventListener handlePointerUp; + DrawerAnimation animation; List children; Drawer({ Object key, - this.onPositionChanged, - this.handleMaskFling, - this.handleMaskTap, - this.handlePointerCancel, - this.handlePointerDown, - this.handlePointerMove, - this.handlePointerUp, + this.animation, this.children }) : super(key: key); @@ -157,7 +145,7 @@ class Drawer extends Component { return; _listening = true; - onPositionChanged.listen((position) { + animation.onPositionChanged.listen((position) { setState(() { _position = position; }); @@ -172,29 +160,28 @@ class Drawer extends Component { String maskInlineStyle = 'opacity: ${(_position / _kWidth + 1) * 0.25}'; String contentInlineStyle = 'transform: translateX(${_position}px)'; + Container mask = new Container( + key: 'Mask', + style: _maskStyle, + inlineStyle: maskInlineStyle + )..events.listen('gesturetap', animation.handleMaskTap) + ..events.listen('gestureflingstart', animation.handleFlingStart); + + Container content = new Container( + key: 'Content', + style: _contentStyle, + inlineStyle: contentInlineStyle, + children: children + ); + return new Container( style: _style, inlineStyle: inlineStyle, - onPointerDown: handlePointerDown, - onPointerMove: handlePointerMove, - onPointerUp: handlePointerUp, - onPointerCancel: handlePointerCancel, - - children: [ - new Container( - key: 'Mask', - style: _maskStyle, - inlineStyle: maskInlineStyle, - onGestureTap: handleMaskTap, - onFlingStart: handleMaskFling - ), - new Container( - key: 'Content', - style: _contentStyle, - inlineStyle: contentInlineStyle, - children: children - ) - ] - ); + children: [ mask, content ] + )..events.listen('pointerdown', animation.handlePointerDown) + ..events.listen('pointermove', animation.handlePointerMove) + ..events.listen('pointerup', animation.handlePointerUp) + ..events.listen('pointercancel', animation.handlePointerCancel); + } } diff --git a/examples/fn/widgets/fixedheightscrollable.dart b/examples/fn/widgets/fixedheightscrollable.dart index ef343e267..3c134aa92 100644 --- a/examples/fn/widgets/fixedheightscrollable.dart +++ b/examples/fn/widgets/fixedheightscrollable.dart @@ -50,10 +50,6 @@ abstract class FixedHeightScrollable extends Component { return new Container( style: _style, - onFlingStart: _handleFlingStart, - onFlingCancel: _handleFlingCancel, - onScrollUpdate: _handleScrollUpdate, - onWheel: _handleWheel, children: [ new Container( style: _scrollAreaStyle, @@ -61,7 +57,11 @@ abstract class FixedHeightScrollable extends Component { children: items ) ] - ); + ) + ..events.listen('gestureflingstart', _handleFlingStart) + ..events.listen('gestureflingcancel', _handleFlingCancel) + ..events.listen('gesturescrollupdate', _handleScrollUpdate) + ..events.listen('wheel', _handleWheel); } void willUnmount() { diff --git a/examples/fn/widgets/icon.dart b/examples/fn/widgets/icon.dart index dded9217d..8f76d8f41 100644 --- a/examples/fn/widgets/icon.dart +++ b/examples/fn/widgets/icon.dart @@ -7,14 +7,12 @@ class Icon extends Component { Style style; int size; String type; - sky.EventListener onClick; Icon({ String key, this.style, this.size, - this.type: '', - this.onClick + this.type: '' }) : super(key: key); Node render() { @@ -28,7 +26,6 @@ class Icon extends Component { return new Image( style: style, - onClick: onClick, width: size, height: size, src: '${kAssetBase}/${category}/2x_web/ic_${subtype}_${size}dp.png' diff --git a/examples/fn/widgets/material.dart b/examples/fn/widgets/material.dart index 0260705a0..2d38a511d 100644 --- a/examples/fn/widgets/material.dart +++ b/examples/fn/widgets/material.dart @@ -26,12 +26,11 @@ abstract class MaterialComponent extends Component { return new Container( style: _style, - onScrollStart: _cancelSplashes, - onWheel: _cancelSplashes, - onPointerDown: _startSplash, children: children, key: _splashesKey - ); + )..events.listen('gesturescrollstart', _cancelSplashes) + ..events.listen('wheel', _cancelSplashes) + ..events.listen('pointerdown', _startSplash); } sky.ClientRect _getBoundingRect() => getRoot().getBoundingClientRect(); diff --git a/examples/fn/widgets/menuitem.dart b/examples/fn/widgets/menuitem.dart index 22cd8f75a..6a7407b1d 100644 --- a/examples/fn/widgets/menuitem.dart +++ b/examples/fn/widgets/menuitem.dart @@ -33,18 +33,11 @@ class MenuItem extends ButtonBase { List children; String icon; - sky.EventListener onClick; - - MenuItem({ Object key, this.icon, this.children, this.onClick }) : super(key: key) { - } + MenuItem({ Object key, this.icon, this.children }) : super(key: key); Node render() { return new Container( style: _highlight ? _highlightStyle : _style, - onClick: onClick, - onPointerDown: _handlePointerDown, - onPointerUp: _handlePointerUp, - onPointerCancel: _handlePointerCancel, children: [ super.render(), new Icon( diff --git a/examples/fn/widgets/radio.dart b/examples/fn/widgets/radio.dart index fe907929e..c5c1b41f8 100644 --- a/examples/fn/widgets/radio.dart +++ b/examples/fn/widgets/radio.dart @@ -48,16 +48,12 @@ class Radio extends ButtonBase { Node render() { return new Container( style: _highlight ? _highlightStyle : _style, - onClick: _handleClick, - onPointerDown: _handlePointerDown, - onPointerUp: _handlePointerUp, - onPointerCancel: _handlePointerCancel, children: value == groupValue ? [super.render(), new Container( style : _dotStyle )] : [super.render()] - ); + )..events.listen('click', _handleClick); } - void _handleClick(sky.Event e) { + void _handleClick(_) { onChanged(value); } } diff --git a/examples/stocks-fn/stocksapp.dart b/examples/stocks-fn/stocksapp.dart index 501383c5d..562e615d1 100644 --- a/examples/stocks-fn/stocksapp.dart +++ b/examples/stocks-fn/stocksapp.dart @@ -37,13 +37,7 @@ class StocksApp extends App { Node render() { var drawer = new Drawer( - onPositionChanged: _drawerAnimation.onPositionChanged, - handleMaskFling: _drawerAnimation.handleFlingStart, - handleMaskTap: _drawerAnimation.handleMaskTap, - handlePointerCancel: _drawerAnimation.handlePointerCancel, - handlePointerDown: _drawerAnimation.handlePointerDown, - handlePointerMove: _drawerAnimation.handlePointerMove, - handlePointerUp: _drawerAnimation.handlePointerUp, + animation: _drawerAnimation, children: [ new DrawerHeader( children: [new Text('Stocks')] @@ -76,9 +70,9 @@ class StocksApp extends App { var toolbar = new Toolbar( children: [ new Icon(key: 'menu', style: _iconStyle, - onClick: _drawerAnimation.toggle, size: 24, - type: 'navigation/menu_white'), + type: 'navigation/menu_white') + ..events.listen('click', _drawerAnimation.toggle), new Container( style: _titleStyle, children: [new Text('I am a stocks app')] -- GitLab