Commit 6ea86b6a authored by Hixie's avatar Hixie

fn3: Listener

In this new world, Listener is just a wrapper around a node in the
render tree that hooks directly into the event handling logic.
parent b3ebd440
......@@ -852,6 +852,36 @@ class AssetImage extends StatelessComponent {
// EVENT HANDLING
class Listener extends OneChildRenderObjectWidget {
Listener({
Key key,
Widget child,
this.onPointerDown,
this.onPointerMove,
this.onPointerUp,
this.onPointerCancel
}): super(key: key, child: child);
final PointerEventListener onPointerDown;
final PointerEventListener onPointerMove;
final PointerEventListener onPointerUp;
final PointerEventListener onPointerCancel;
RenderPointerListener createRenderObject() => new RenderPointerListener(
onPointerDown: onPointerDown,
onPointerMove: onPointerMove,
onPointerUp: onPointerUp,
onPointerCancel: onPointerCancel
);
void updateRenderObject(RenderPointerListener renderObject, Listener oldWidget) {
renderObject.onPointerDown = onPointerDown;
renderObject.onPointerMove = onPointerMove;
renderObject.onPointerUp = onPointerUp;
renderObject.onPointerCancel = onPointerCancel;
}
}
class IgnorePointer extends OneChildRenderObjectWidget {
IgnorePointer({ Key key, Widget child, this.ignoring: true })
: super(key: key, child: child);
......
......@@ -814,12 +814,41 @@ class RenderCustomPaint extends RenderProxyBox {
}
}
/// Is invisible during hit testing
typedef void PointerEventListener(sky.PointerEvent e);
/// Invokes the callbacks in response to pointer events.
class RenderPointerListener extends RenderProxyBox {
RenderPointerListener({
this.onPointerDown,
this.onPointerMove,
this.onPointerUp,
this.onPointerCancel,
RenderBox child
}) : super(child);
PointerEventListener onPointerDown;
PointerEventListener onPointerMove;
PointerEventListener onPointerUp;
PointerEventListener onPointerCancel;
void handleEvent(sky.Event event, HitTestEntry entry) {
if (onPointerDown != null && event.type == 'pointerdown')
return onPointerDown(event);
if (onPointerMove != null && event.type == 'pointermove')
return onPointerMove(event);
if (onPointerUp != null && event.type == 'pointerup')
return onPointerUp(event);
if (onPointerCancel != null && event.type == 'pointercancel')
return onPointerCancel(event);
}
}
/// Is invisible during hit testing.
///
/// When [ignoring] is true, this render object is invisible to hit testing. It
/// still consumes space during layout and paints its child as usual. It just
/// cannot be the target of located events because it returns false from
/// [hitTest].
/// When [ignoring] is true, this render object (and its subtree) is invisible
/// to hit testing. It still consumes space during layout and paints its child
/// as usual. It just cannot be the target of located events because it returns
/// false from [hitTest].
class RenderIgnorePointer extends RenderProxyBox {
RenderIgnorePointer({ RenderBox child, bool ignoring: true }) : super(child);
......
import 'package:sky/src/fn3.dart';
import 'package:test/test.dart';
import 'widget_tester.dart';
void main() {
test('Events bubble up the tree', () {
WidgetTester tester = new WidgetTester();
List<String> log = new List<String>();
tester.pumpFrame(
new Listener(
onPointerDown: (_) {
log.add('top');
},
child: new Listener(
onPointerDown: (_) {
log.add('middle');
},
child: new DecoratedBox(
decoration: const BoxDecoration(),
child: new Listener(
onPointerDown: (_) {
log.add('bottom');
},
child: new Text('X')
)
)
)
)
);
tester.tap(tester.findText('X'));
expect(log, equals([
'bottom',
'middle',
'top',
]));
});
}
import 'dart:sky' as sky;
import 'package:sky/rendering.dart';
import 'package:sky/src/fn3.dart';
import '../engine/mock_events.dart';
class RootComponent extends StatefulComponent {
RootComponentState createState() => new RootComponentState(this);
}
......@@ -20,6 +25,16 @@ class RootComponentState extends ComponentState<RootComponent> {
class WidgetTester {
void pumpFrame(Widget widget) {
runApp(widget);
WidgetFlutterBinding.instance.beginFrame(0.0); // TODO(ianh): https://github.com/flutter/engine/issues/1084
}
void pumpFrameWithoutChange() {
WidgetFlutterBinding.instance.beginFrame(0.0); // TODO(ianh): https://github.com/flutter/engine/issues/1084
}
void walkElements(ElementVisitor visitor) {
void walk(Element element) {
visitor(element);
......@@ -44,13 +59,60 @@ class WidgetTester {
return findElement((Element element) => element.widget.key == key);
}
void pumpFrame(Widget widget) {
runApp(widget);
WidgetFlutterBinding.instance.beginFrame(0.0); // TODO(ianh): https://github.com/flutter/engine/issues/1084
Element findText(String text) {
return findElement((Element element) {
return element.widget is Text && element.widget.data == text;
});
}
void pumpFrameWithoutChange() {
WidgetFlutterBinding.instance.beginFrame(0.0); // TODO(ianh): https://github.com/flutter/engine/issues/1084
Point getCenter(Element element) {
return _getElementPoint(element, (Size size) => size.center(Point.origin));
}
Point getTopLeft(Element element) {
return _getElementPoint(element, (_) => Point.origin);
}
Point getTopRight(Element element) {
return _getElementPoint(element, (Size size) => size.topRight(Point.origin));
}
Point getBottomLeft(Element element) {
return _getElementPoint(element, (Size size) => size.bottomLeft(Point.origin));
}
Point getBottomRight(Element element) {
return _getElementPoint(element, (Size size) => size.bottomRight(Point.origin));
}
Point _getElementPoint(Element element, Function sizeToPoint) {
assert(element != null);
RenderBox box = element.renderObject as RenderBox;
assert(box != null);
return box.localToGlobal(sizeToPoint(box.size));
}
void tap(Element element, { int pointer: 1 }) {
tapAt(getCenter(element), pointer: pointer);
}
void tapAt(Point location, { int pointer: 1 }) {
HitTestResult result = _hitTest(location);
TestPointer p = new TestPointer(pointer);
_dispatchEvent(p.down(location), result);
_dispatchEvent(p.up(), result);
}
void dispatchEvent(sky.Event event, Point location) {
_dispatchEvent(event, _hitTest(location));
}
HitTestResult _hitTest(Point location) => WidgetFlutterBinding.instance.hitTest(location);
void _dispatchEvent(sky.Event event, HitTestResult result) {
WidgetFlutterBinding.instance.dispatchEvent(event, result);
}
}
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