提交 b96da3b2 编写于 作者: H Hixie

Rationalise hit testing in the new RenderNode world

- makes the event logic not involve a boolean return value (since we ignored it anyway)
- splits the event handling logic into two steps, hit testing and event dispatch
- introduces an App class on the Dart side to factor out the interaction with the C++ side
- ports sector-layout and simple_render_tree to the new App infrastructure
- port simple_render_tree to the new event handling logic
- implement hit testing for the sector-layout demo

R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/1143343004
上级 0991da0d
......@@ -11,7 +11,7 @@ class Event;
class EventCallback {
public:
virtual ~EventCallback() { }
virtual bool handleEvent(Event* event) = 0;
virtual void handleEvent(Event* event) = 0;
};
}
......
......@@ -3,5 +3,5 @@
// found in the LICENSE file.
callback interface EventCallback {
boolean handleEvent(Event event);
void handleEvent(Event event);
};
......@@ -53,9 +53,10 @@ void View::setDisplayMetrics(const SkyDisplayMetrics& metrics)
m_displayMetrics = metrics;
}
bool View::handleInputEvent(PassRefPtr<Event> event)
void View::handleInputEvent(PassRefPtr<Event> event)
{
return m_eventCallback && m_eventCallback->handleEvent(event.get());
if (m_eventCallback)
m_eventCallback->handleEvent(event.get());
}
void View::beginFrame(base::TimeTicks frameTime)
......
......@@ -36,7 +36,7 @@ public:
void scheduleFrame();
void setDisplayMetrics(const SkyDisplayMetrics& metrics);
bool handleInputEvent(PassRefPtr<Event> event);
void handleInputEvent(PassRefPtr<Event> event);
void beginFrame(base::TimeTicks frameTime);
private:
......
......@@ -68,30 +68,23 @@ skia::RefPtr<SkPicture> SkyView::Paint() {
return skia::RefPtr<SkPicture>();
}
bool SkyView::HandleInputEvent(const WebInputEvent& inputEvent) {
void SkyView::HandleInputEvent(const WebInputEvent& inputEvent) {
TRACE_EVENT0("input", "SkyView::HandleInputEvent");
if (WebInputEvent::isPointerEventType(inputEvent.type)) {
const WebPointerEvent& event = static_cast<const WebPointerEvent&>(inputEvent);
return data_->view_->handleInputEvent(PointerEvent::create(event));
}
if (WebInputEvent::isGestureEventType(inputEvent.type)) {
data_->view_->handleInputEvent(PointerEvent::create(event));
} else if (WebInputEvent::isGestureEventType(inputEvent.type)) {
const WebGestureEvent& event = static_cast<const WebGestureEvent&>(inputEvent);
return data_->view_->handleInputEvent(GestureEvent::create(event));
}
if (WebInputEvent::isKeyboardEventType(inputEvent.type)) {
data_->view_->handleInputEvent(GestureEvent::create(event));
} else if (WebInputEvent::isKeyboardEventType(inputEvent.type)) {
const WebKeyboardEvent& event = static_cast<const WebKeyboardEvent&>(inputEvent);
return data_->view_->handleInputEvent(KeyboardEvent::create(event));
}
if (WebInputEvent::isWheelEventType(inputEvent.type)) {
data_->view_->handleInputEvent(KeyboardEvent::create(event));
} else if (WebInputEvent::isWheelEventType(inputEvent.type)) {
const WebWheelEvent& event = static_cast<const WebWheelEvent&>(inputEvent);
return data_->view_->handleInputEvent(WheelEvent::create(event));
data_->view_->handleInputEvent(WheelEvent::create(event));
}
return false;
}
void SkyView::ScheduleFrame() {
......
......@@ -34,7 +34,7 @@ class SkyView {
void Load(const WebURL& url, mojo::URLResponsePtr response = nullptr);
skia::RefPtr<SkPicture> Paint();
bool HandleInputEvent(const WebInputEvent& event);
void HandleInputEvent(const WebInputEvent& event);
private:
explicit SkyView(SkyViewClient* client);
......
......@@ -4,6 +4,7 @@
import 'dart:math' as math;
import 'dart:sky' as sky;
import 'package:sky/framework/app.dart';
import 'package:sky/framework/layout2.dart';
const double kTwoPi = 2 * math.PI;
......@@ -77,6 +78,17 @@ abstract class RenderSector extends RenderNode {
layoutDone();
}
bool hitTest(HitTestResult result, { double radius, double theta }) {
assert(parentData is SectorParentData);
if (radius < parentData.radius || radius >= parentData.radius + deltaRadius ||
theta < parentData.theta || theta >= parentData.theta + deltaTheta)
return false;
hitTestChildren(result, radius: radius, theta: theta);
result.add(this);
return true;
}
void hitTestChildren(HitTestResult result, { double radius, double theta }) { }
double deltaRadius;
double deltaTheta;
}
......@@ -119,7 +131,21 @@ class RenderDecoratedSector extends RenderSector {
class SectorChildListParentData extends SectorParentData with ContainerParentDataMixin<RenderSector> { }
class RenderSectorRing extends RenderDecoratedSector with ContainerRenderNodeMixin<RenderSector, SectorChildListParentData> {
class RenderSectorWithChildren extends RenderDecoratedSector with ContainerRenderNodeMixin<RenderSector, SectorChildListParentData> {
RenderSectorWithChildren(BoxDecoration decoration) : super(decoration);
void hitTestChildren(HitTestResult result, { double radius, double theta }) {
RenderSector child = lastChild;
while (child != null) {
assert(child.parentData is SectorChildListParentData);
if (child.hitTest(result, radius: radius, theta: theta))
return;
child = child.parentData.previousSibling;
}
}
}
class RenderSectorRing extends RenderSectorWithChildren {
// lays out RenderSector children in a ring
RenderSectorRing({
......@@ -229,8 +255,6 @@ class RenderSectorRing extends RenderDecoratedSector with ContainerRenderNodeMix
deltaTheta = innerTheta;
}
// TODO(ianh): hit testing et al is pending on adam's patch
// paint origin is 0,0 of our circle
// each sector then knows how to paint itself at its location
void paint(RenderNodeDisplayList canvas) {
......@@ -246,7 +270,7 @@ class RenderSectorRing extends RenderDecoratedSector with ContainerRenderNodeMix
}
class RenderSectorSlice extends RenderDecoratedSector with ContainerRenderNodeMixin<RenderSector, SectorChildListParentData> {
class RenderSectorSlice extends RenderSectorWithChildren {
// lays out RenderSector children in a stack
RenderSectorSlice({
......@@ -351,8 +375,6 @@ class RenderSectorSlice extends RenderDecoratedSector with ContainerRenderNodeMi
deltaRadius = childRadius - this.parentData.radius;
}
// TODO(ianh): hit testing et al is pending on adam's patch
// paint origin is 0,0 of our circle
// each sector then knows how to paint itself at its location
void paint(RenderNodeDisplayList canvas) {
......@@ -436,14 +458,32 @@ class RenderBoxToRenderSectorAdapter extends RenderBox {
double width;
double height;
// TODO(ianh): hit testing et al is pending on adam's patch
// paint origin is 0,0 of our circle
void paint(RenderNodeDisplayList canvas) {
super.paint(canvas);
if (child != null)
canvas.paintChild(child, width/2.0, height/2.0);
}
bool hitTest(HitTestResult result, { double x, double y }) {
if (child == null)
return false;
// translate to our origin
x -= width/2.0;
y -= height/2.0;
// convert to radius/theta
double radius = math.sqrt(x*x+y*y);
double theta = (math.atan2(x, -y) - math.PI/2.0) % kTwoPi;
if (radius < innerRadius)
return false;
if (radius >= innerRadius + child.deltaRadius)
return false;
if (theta > child.deltaTheta)
return false;
child.hitTest(result, radius: radius, theta: theta);
result.add(this);
return true;
}
}
......@@ -451,10 +491,12 @@ class RenderSolidColor extends RenderDecoratedSector {
RenderSolidColor(int backgroundColor, {
this.desiredDeltaRadius: double.INFINITY,
this.desiredDeltaTheta: kTwoPi
}) : super(new BoxDecoration(backgroundColor: backgroundColor));
}) : this.backgroundColor = backgroundColor,
super(new BoxDecoration(backgroundColor: backgroundColor));
double desiredDeltaRadius;
double desiredDeltaTheta;
final int backgroundColor;
SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) {
return new SectorDimensions.withConstraints(constraints, deltaTheta: 1.0); // 1.0 radians
......@@ -465,39 +507,28 @@ class RenderSolidColor extends RenderDecoratedSector {
deltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta);
layoutDone();
}
}
RenderView renderView;
void beginFrame(double timeStamp) {
RenderNode.flushLayout();
renderView.paintFrame();
void handlePointer(sky.PointerEvent event) {
if (event.type == 'pointerdown')
setBoxDecoration(new BoxDecoration(backgroundColor: 0xFFFF0000));
else if (event.type == 'pointerup')
setBoxDecoration(new BoxDecoration(backgroundColor: backgroundColor));
}
}
bool handleEvent(sky.Event event) {
if (event is! sky.PointerEvent)
return false;
return renderView.handlePointer(event, x: event.x, y: event.y);
}
AppView app;
void main() {
print("test...");
sky.view.setEventCallback(handleEvent);
sky.view.setBeginFrameCallback(beginFrame);
var rootCircle = new RenderSectorRing(padding: 10.0);
rootCircle.add(new RenderSolidColor(0xFF00FFFF, desiredDeltaTheta: kTwoPi * 0.25));
rootCircle.add(new RenderSolidColor(0xFF0000FF, desiredDeltaTheta: kTwoPi * 0.3));
var stack = new RenderSectorSlice(padding: 10.0);
var rootCircle = new RenderSectorRing(padding: 20.0);
rootCircle.add(new RenderSolidColor(0xFF00FFFF, desiredDeltaTheta: kTwoPi * 0.15));
rootCircle.add(new RenderSolidColor(0xFF0000FF, desiredDeltaTheta: kTwoPi * 0.4));
var stack = new RenderSectorSlice(padding: 2.0);
stack.add(new RenderSolidColor(0xFFFFFF00, desiredDeltaRadius: 20.0));
stack.add(new RenderSolidColor(0xFFFF9000, desiredDeltaRadius: 20.0));
stack.add(new RenderSolidColor(0xFF00FF00, desiredDeltaRadius: 20.0));
stack.add(new RenderSolidColor(0xFF00FF00));
rootCircle.add(stack);
var root = new RenderBoxToRenderSectorAdapter(innerRadius: 50.0, child: rootCircle);
renderView = new RenderView(root: root);
renderView.layout(newWidth: sky.view.width, newHeight: sky.view.height);
sky.view.scheduleFrame();
app = new AppView(root);
}
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math';
import 'dart:sky';
import 'package:sky/framework/app.dart';
import 'package:sky/framework/layout2.dart';
class RenderSolidColor extends RenderDecoratedBox {
......@@ -28,39 +28,17 @@ class RenderSolidColor extends RenderDecoratedBox {
layoutDone();
}
bool handlePointer(PointerEvent event, { double x: 0.0, double y: 0.0 }) {
if (event.type == 'pointerdown') {
void handlePointer(PointerEvent event) {
if (event.type == 'pointerdown')
setBoxDecoration(new BoxDecoration(backgroundColor: 0xFFFF0000));
return true;
}
if (event.type == 'pointerup') {
else if (event.type == 'pointerup')
setBoxDecoration(new BoxDecoration(backgroundColor: backgroundColor));
return true;
}
return false;
}
}
RenderView renderView;
void beginFrame(double timeStamp) {
RenderNode.flushLayout();
renderView.paintFrame();
}
bool handleEvent(Event event) {
if (event is! PointerEvent)
return false;
return renderView.handlePointer(event, x: event.x, y: event.y);
}
AppView app;
void main() {
view.setEventCallback(handleEvent);
view.setBeginFrameCallback(beginFrame);
var root = new RenderFlex(
direction: FlexDirection.Vertical,
decoration: new BoxDecoration(backgroundColor: 0xFF000000));
......@@ -98,8 +76,6 @@ void main() {
root.add(row);
row.parentData.flex = 3;
renderView = new RenderView(root: root);
renderView.layout(newWidth: view.width, newHeight: view.height);
app = new AppView(root);
view.scheduleFrame();
}
......@@ -4,6 +4,7 @@
import 'dart:math';
import 'dart:sky';
import 'package:sky/framework/app.dart';
import 'package:sky/framework/layout2.dart';
// Material design colors. :p
......@@ -40,7 +41,7 @@ class RenderTouchDemo extends RenderBox {
RenderTouchDemo();
bool handlePointer(PointerEvent event, { double x: 0.0, double y: 0.0 }) {
void handlePointer(PointerEvent event) {
switch (event.type) {
case 'pointerdown':
int color = colors[event.pointer.remainder(colors.length)];
......@@ -57,7 +58,6 @@ class RenderTouchDemo extends RenderBox {
break;
}
markNeedsPaint();
return true;
}
void paint(RenderNodeDisplayList canvas) {
......@@ -67,25 +67,8 @@ class RenderTouchDemo extends RenderBox {
}
}
RenderView renderView;
void beginFrame(double timeStamp) {
RenderNode.flushLayout();
renderView.paintFrame();
}
bool handleEvent(Event event) {
if (event is! PointerEvent)
return false;
return renderView.handlePointer(event, x: event.x, y: event.y);
}
AppView app;
void main() {
view.setEventCallback(handleEvent);
view.setBeginFrameCallback(beginFrame);
renderView = new RenderView(root: new RenderTouchDemo());
renderView.layout(newWidth: view.width, newHeight: view.height);
view.scheduleFrame();
app = new AppView(new RenderTouchDemo());
}
......@@ -18,6 +18,7 @@ dart_pkg("sdk") {
"lib/framework/animation/generators.dart",
"lib/framework/animation/mechanics.dart",
"lib/framework/animation/scroll_behavior.dart",
"lib/framework/app.dart",
"lib/framework/components/animated_component.dart",
"lib/framework/components/button.dart",
"lib/framework/components/button_base.dart",
......
// 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 'layout2.dart';
import 'dart:sky' as sky;
class AppView {
RenderView _renderView;
void _beginFrame(double timeStamp) {
RenderNode.flushLayout();
_renderView.paintFrame();
}
void _handleEvent(sky.Event event) {
if (event is! sky.PointerEvent)
return;
HitTestResult result = new HitTestResult();
_renderView.hitTest(result, x: event.x, y: event.y);
result.path.reversed.forEach((RenderNode node) {
node.handlePointer(event);
});
}
AppView(RenderBox root) {
sky.view.setEventCallback(_handleEvent);
sky.view.setBeginFrameCallback(_beginFrame);
_renderView = new RenderView(root: root);
_renderView.layout(newWidth: sky.view.width, newHeight: sky.view.height);
sky.view.scheduleFrame();
}
}
// 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.
library layout;
// This version of layout.dart is an update to the other one, this one using new APIs.
......@@ -217,15 +221,6 @@ abstract class RenderNode extends AbstractNode {
}) { }
// HIT TESTING
bool handlePointer(sky.PointerEvent event, { double x: 0.0, double y: 0.0 }) {
// override this if you have children, to hand it to the appropriate child
// override this if you want to do anything with the pointer event
return false;
}
// PAINTING
static bool _debugDoingPaint = false;
......@@ -238,8 +233,39 @@ abstract class RenderNode extends AbstractNode {
}
void paint(RenderNodeDisplayList canvas) { }
// HIT TESTING
void handlePointer(sky.PointerEvent event) {
// override this if you have a client, to hand it to the client
// override this if you want to do anything with the pointer event
}
// RenderNode subclasses are expected to have a method like the
// following (with the signature being whatever passes for coordinates
// for this particular class):
// bool hitTest(HitTestResult result, { double x, double y }) {
// // If (x,y) is not inside this node, then return false.
// // Otherwise:
// // For each child that intersects x,y, in z-order starting from the top,
// // call hitTest() for that child, passing it /result/, and the coordinates
// // converted to the child's coordinate origin, and stop at the first child
// // that returns true.
// // Then, add yourself to /result/, and return true.
// }
// You must not add yourself to /result/ if you return false.
}
class HitTestResult {
final List<RenderNode> path = new List<RenderNode>();
RenderNode get result => path.first;
void add(RenderNode node) {
path.add(node);
}
}
// GENERIC MIXIN FOR RENDER NODES THAT TAKE A LIST OF CHILDREN
......@@ -465,6 +491,15 @@ abstract class RenderBox extends RenderNode {
layoutDone();
}
bool hitTest(HitTestResult result, { double x, double y }) {
if (x < 0.0 || x >= width || y < 0.0 || y >= height)
return false;
hitTestChildren(result, x: x, y: y);
result.add(this);
return true;
}
void hitTestChildren(HitTestResult result, { double x, double y }) { }
double width;
double height;
}
......@@ -564,10 +599,14 @@ class RenderView extends RenderNode {
assert(false); // nobody tells the screen to rotate, the whole rotate() dance is started from our layout()
}
bool handlePointer(sky.PointerEvent event, { double x: 0.0, double y: 0.0 }) {
if (x < 0.0 || x >= root.width || y < 0.0 || y >= root.height)
bool hitTest(HitTestResult result, { double x, double y }) {
assert(root != null);
if (x < 0.0 || x >= width || y < 0.0 || y >= height)
return false;
return root.handlePointer(event, x: x, y: y);
if (x >= 0.0 && x < root.width && y >= 0.0 && y < root.height)
root.hitTest(result, x: x, y: y);
result.add(this);
return true;
}
void paint(RenderNodeDisplayList canvas) {
......@@ -586,20 +625,19 @@ class RenderView extends RenderNode {
// DEFAULT BEHAVIORS FOR RENDERBOX CONTAINERS
abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, ParentDataType extends ContainerParentDataMixin<ChildType>> implements ContainerRenderNodeMixin<ChildType, ParentDataType> {
bool defaultHandlePointer(sky.PointerEvent event, double x, double y) {
void defaultHitTestChildren(HitTestResult result, { double x, double y }) {
// the x, y parameters have the top left of the node's box as the origin
ChildType child = lastChild;
while (child != null) {
assert(child.parentData is BoxParentData);
if ((x >= child.parentData.x) && (x < child.parentData.x + child.width) &&
(y >= child.parentData.y) && (y < child.parentData.y + child.height)) {
if (child.handlePointer(event, x: x-child.parentData.x, y: y-child.parentData.y))
return true;
break;
if (child.hitTest(result, x: x-child.parentData.x, y: y-child.parentData.y))
break;
}
child = child.parentData.previousSibling;
}
return false;
}
void defaultPaint(RenderNodeDisplayList canvas) {
......@@ -714,8 +752,8 @@ class RenderBlock extends RenderDecoratedBox with ContainerRenderNodeMixin<Rende
layoutDone();
}
bool handlePointer(sky.PointerEvent event, { double x: 0.0, double y: 0.0 }) {
return defaultHandlePointer(event, x, y) || super.handlePointer(event, x: x, y: y);
void hitTestChildren(HitTestResult result, { double x, double y }) {
defaultHitTestChildren(result, x: x, y: y);
}
void paint(RenderNodeDisplayList canvas) {
......@@ -846,8 +884,8 @@ class RenderFlex extends RenderDecoratedBox with ContainerRenderNodeMixin<Render
layoutDone();
}
bool handlePointer(sky.PointerEvent event, { double x: 0.0, double y: 0.0 }) {
return defaultHandlePointer(event, x, y) || super.handlePointer(event, x: x, y: y);
void hitTestChildren(HitTestResult result, { double x, double y }) {
defaultHitTestChildren(result, x: x, y: y);
}
void paint(RenderNodeDisplayList canvas) {
......@@ -902,21 +940,16 @@ class ScaffoldBox extends RenderBox {
layoutDone();
}
bool handlePointer(sky.PointerEvent event, { double x: 0.0, double y: 0.0 }) {
void hitTestChildren(HitTestResult result, { double x, double y }) {
if ((drawer != null) && (x < drawer.width)) {
if (drawer.handlePointer(event, x: x, y: y))
return true;
drawer.hitTest(result, x: x, y: y);
} else if ((toolbar != null) && (y < toolbar.height)) {
if (toolbar.handlePointer(event, x: x, y: y))
return true;
toolbar.hitTest(result, x: x, y: y);
} else if ((statusbar != null) && (y > (statusbar.parentData as BoxParentData).y)) {
if (statusbar.handlePointer(event, x: x, y: y-(statusbar.parentData as BoxParentData).y))
return true;
statusbar.hitTest(result, x: x, y: y-(statusbar.parentData as BoxParentData).y);
} else {
if (body.handlePointer(event, x: x, y: y-(body.parentData as BoxParentData).y))
return true;
body.hitTest(result, x: x, y: y-(body.parentData as BoxParentData).y);
}
return super.handlePointer(event, x: x, y: y);
}
void paint(RenderNodeDisplayList canvas) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册