Commit 76b7f06e authored by tdanderson's avatar tdanderson Committed by Commit bot

Convert ui::GestureEvent coordinates during targeting

In RootView::DispatchGestureEvent(), use Event::Clone()
to copy gesture events prior to processing and perform
coordinate conversion of the events during targeting but
before dispatch (so the location of the gesture event
is in the coordinate space of the target).

In preparation for removing
RootView::DispatchGestureEvent(), also make the
corresponding changes to
EventProcessor::OnEventFromSource(). These changes
are necessary to ensure that located events will
be dispatched with their locations in the correct
coordinate space in all situations (in particular,
when an event is bubbling during an invocation of
nested event-processing).

BUG=411487
TEST=WidgetTest.GestureEventLocationWhileBubbling,
     ViewTargeterTest.GestureEventCoordinateConversion,
     modified ViewTargeterTest.ViewTargeterForGestureEvents

Review URL: https://codereview.chromium.org/542323002

Cr-Commit-Position: refs/heads/master@{#293797}
parent 8a4c06c0
...@@ -14,27 +14,27 @@ EventDispatchDetails EventProcessor::OnEventFromSource(Event* event) { ...@@ -14,27 +14,27 @@ EventDispatchDetails EventProcessor::OnEventFromSource(Event* event) {
CHECK(root); CHECK(root);
EventTargeter* targeter = root->GetEventTargeter(); EventTargeter* targeter = root->GetEventTargeter();
CHECK(targeter); CHECK(targeter);
PrepareEventForDispatch(event); PrepareEventForDispatch(event);
EventTarget* target = targeter->FindTargetForEvent(root, event);
// If the event is in the process of being dispatched or has already been // If |event| is in the process of being dispatched or has already been
// dispatched, then dispatch a copy of the event instead. // dispatched, then dispatch a copy of the event instead.
bool dispatch_original_event = event->phase() == EP_PREDISPATCH;
Event* event_to_dispatch = event;
scoped_ptr<Event> event_copy; scoped_ptr<Event> event_copy;
if (event->phase() != EP_PREDISPATCH) if (!dispatch_original_event) {
event_copy = Event::Clone(*event); event_copy = Event::Clone(*event);
event_to_dispatch = event_copy.get();
}
EventTarget* target = targeter->FindTargetForEvent(root, event_to_dispatch);
while (target) { while (target) {
EventDispatchDetails details; EventDispatchDetails details = DispatchEvent(target, event_to_dispatch);
if (event_copy) {
details = DispatchEvent(target, event_copy.get());
if (event_copy->stopped_propagation()) if (!dispatch_original_event) {
if (event_to_dispatch->stopped_propagation())
event->StopPropagation(); event->StopPropagation();
else if (event_copy->handled()) else if (event_to_dispatch->handled())
event->SetHandled(); event->SetHandled();
} else {
details = DispatchEvent(target, event);
} }
if (details.dispatcher_destroyed || if (details.dispatcher_destroyed ||
...@@ -43,7 +43,7 @@ EventDispatchDetails EventProcessor::OnEventFromSource(Event* event) { ...@@ -43,7 +43,7 @@ EventDispatchDetails EventProcessor::OnEventFromSource(Event* event) {
return details; return details;
} }
target = targeter->FindNextBestTarget(target, event); target = targeter->FindNextBestTarget(target, event_to_dispatch);
} }
return EventDispatchDetails(); return EventDispatchDetails();
......
...@@ -170,7 +170,9 @@ TEST_F(EventProcessorTest, Bounds) { ...@@ -170,7 +170,9 @@ TEST_F(EventProcessorTest, Bounds) {
// ReDispatchEventHandler is used to receive mouse events and forward them // ReDispatchEventHandler is used to receive mouse events and forward them
// to a specified EventProcessor. Verifies that the event has the correct // to a specified EventProcessor. Verifies that the event has the correct
// target and phase both before and after the nested event processing. // target and phase both before and after the nested event processing. Also
// verifies that the location of the event remains the same after it has
// been processed by the second EventProcessor.
class ReDispatchEventHandler : public TestEventHandler { class ReDispatchEventHandler : public TestEventHandler {
public: public:
ReDispatchEventHandler(EventProcessor* processor, EventTarget* target) ReDispatchEventHandler(EventProcessor* processor, EventTarget* target)
...@@ -184,14 +186,16 @@ class ReDispatchEventHandler : public TestEventHandler { ...@@ -184,14 +186,16 @@ class ReDispatchEventHandler : public TestEventHandler {
EXPECT_EQ(expected_target_, event->target()); EXPECT_EQ(expected_target_, event->target());
EXPECT_EQ(EP_TARGET, event->phase()); EXPECT_EQ(EP_TARGET, event->phase());
gfx::Point location(event->location());
EventDispatchDetails details = processor_->OnEventFromSource(event); EventDispatchDetails details = processor_->OnEventFromSource(event);
EXPECT_FALSE(details.dispatcher_destroyed); EXPECT_FALSE(details.dispatcher_destroyed);
EXPECT_FALSE(details.target_destroyed); EXPECT_FALSE(details.target_destroyed);
// The nested event-processing should not have mutated the target or // The nested event-processing should not have mutated the target,
// phase of |event|. // phase, or location of |event|.
EXPECT_EQ(expected_target_, event->target()); EXPECT_EQ(expected_target_, event->target());
EXPECT_EQ(EP_TARGET, event->phase()); EXPECT_EQ(EP_TARGET, event->phase());
EXPECT_EQ(location, event->location());
} }
private: private:
......
...@@ -20,7 +20,11 @@ class EVENTS_EXPORT EventTargeter { ...@@ -20,7 +20,11 @@ class EVENTS_EXPORT EventTargeter {
virtual ~EventTargeter(); virtual ~EventTargeter();
// Returns the target |event| should be dispatched to. If there is no such // Returns the target |event| should be dispatched to. If there is no such
// target, this should return NULL. // target, return NULL. If |event| is a located event, the location of |event|
// is in the coordinate space of |root|. Furthermore, the targeter can mutate
// the event (e.g., by changing the location of the event to be in the
// returned target's coordinate space) so that it can be dispatched to the
// target without any further modification.
virtual EventTarget* FindTargetForEvent(EventTarget* root, virtual EventTarget* FindTargetForEvent(EventTarget* root,
Event* event); Event* event);
...@@ -28,7 +32,9 @@ class EVENTS_EXPORT EventTargeter { ...@@ -28,7 +32,9 @@ class EVENTS_EXPORT EventTargeter {
// etc. of |event| are in |root|'s coordinate system. When finding the target // etc. of |event| are in |root|'s coordinate system. When finding the target
// for the event, the targeter can mutate the |event| (e.g. change the // for the event, the targeter can mutate the |event| (e.g. change the
// coordinate to be in the returned target's coordinate system) so that it can // coordinate to be in the returned target's coordinate system) so that it can
// be dispatched to the target without any farther modification. // be dispatched to the target without any further modification.
// TODO(tdanderson|sadrul): This should not be in the public API of
// EventTargeter.
virtual EventTarget* FindTargetForLocatedEvent(EventTarget* root, virtual EventTarget* FindTargetForLocatedEvent(EventTarget* root,
LocatedEvent* event); LocatedEvent* event);
...@@ -43,6 +49,7 @@ class EVENTS_EXPORT EventTargeter { ...@@ -43,6 +49,7 @@ class EVENTS_EXPORT EventTargeter {
const LocatedEvent& event); const LocatedEvent& event);
// Returns the next best target for |event| as compared to |previous_target|. // Returns the next best target for |event| as compared to |previous_target|.
// |event| is in the local coordinate space of |previous_target|.
// Also mutates |event| so that it can be dispatched to the returned target // Also mutates |event| so that it can be dispatched to the returned target
// (e.g., by changing |event|'s location to be in the returned target's // (e.g., by changing |event|'s location to be in the returned target's
// coordinate space). // coordinate space).
......
...@@ -39,8 +39,12 @@ ui::EventTarget* ViewTargeter::FindTargetForEvent(ui::EventTarget* root, ...@@ -39,8 +39,12 @@ ui::EventTarget* ViewTargeter::FindTargetForEvent(ui::EventTarget* root,
*static_cast<ui::ScrollEvent*>(event)); *static_cast<ui::ScrollEvent*>(event));
} }
if (event->IsGestureEvent()) if (event->IsGestureEvent()) {
return FindTargetForGestureEvent(view, *(event->AsGestureEvent())); ui::GestureEvent* gesture = event->AsGestureEvent();
View* gesture_target = FindTargetForGestureEvent(view, *gesture);
root->ConvertEventToTarget(gesture_target, gesture);
return gesture_target;
}
NOTREACHED() << "ViewTargeter does not yet support this event type."; NOTREACHED() << "ViewTargeter does not yet support this event type.";
return NULL; return NULL;
...@@ -53,8 +57,11 @@ ui::EventTarget* ViewTargeter::FindNextBestTarget( ...@@ -53,8 +57,11 @@ ui::EventTarget* ViewTargeter::FindNextBestTarget(
return NULL; return NULL;
if (event->IsGestureEvent()) { if (event->IsGestureEvent()) {
return FindNextBestTargetForGestureEvent(previous_target, ui::GestureEvent* gesture = event->AsGestureEvent();
*(event->AsGestureEvent())); ui::EventTarget* next_target =
FindNextBestTargetForGestureEvent(previous_target, *gesture);
previous_target->ConvertEventToTarget(next_target, gesture);
return next_target;
} }
return previous_target->GetParentTarget(); return previous_target->GetParentTarget();
......
...@@ -99,15 +99,15 @@ class ViewTargeterTest : public ViewsTestBase { ...@@ -99,15 +99,15 @@ class ViewTargeterTest : public ViewsTestBase {
namespace { namespace {
gfx::Point ConvertPointToView(View* view, const gfx::Point& p) { gfx::Point ConvertPointFromWidgetToView(View* view, const gfx::Point& p) {
gfx::Point tmp(p); gfx::Point tmp(p);
View::ConvertPointToTarget(view->GetWidget()->GetRootView(), view, &tmp); View::ConvertPointToTarget(view->GetWidget()->GetRootView(), view, &tmp);
return tmp; return tmp;
} }
gfx::Rect ConvertRectToView(View* view, const gfx::Rect& r) { gfx::Rect ConvertRectFromWidgetToView(View* view, const gfx::Rect& r) {
gfx::Rect tmp(r); gfx::Rect tmp(r);
tmp.set_origin(ConvertPointToView(view, r.origin())); tmp.set_origin(ConvertPointFromWidgetToView(view, r.origin()));
return tmp; return tmp;
} }
...@@ -292,7 +292,7 @@ TEST_F(ViewTargeterTest, ViewTargeterForGestureEvents) { ...@@ -292,7 +292,7 @@ TEST_F(ViewTargeterTest, ViewTargeterForGestureEvents) {
// GESTURE_END events should be targeted to the existing gesture handler, // GESTURE_END events should be targeted to the existing gesture handler,
// but re-targeting should be prohibited. // but re-targeting should be prohibited.
EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &end)); EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &end));
EXPECT_EQ(NULL, targeter->FindNextBestTarget(grandchild, &tap)); EXPECT_EQ(NULL, targeter->FindNextBestTarget(grandchild, &end));
// Assume that the view currently handling gestures is still set as // Assume that the view currently handling gestures is still set as
// |grandchild|, but this was not done by a previous gesture. Thus we are // |grandchild|, but this was not done by a previous gesture. Thus we are
...@@ -319,6 +319,22 @@ TEST_F(ViewTargeterTest, ViewTargeterForGestureEvents) { ...@@ -319,6 +319,22 @@ TEST_F(ViewTargeterTest, ViewTargeterForGestureEvents) {
EXPECT_EQ(NULL, targeter->FindNextBestTarget(content, &scroll_begin)); EXPECT_EQ(NULL, targeter->FindNextBestTarget(content, &scroll_begin));
EXPECT_EQ(NULL, targeter->FindNextBestTarget(content, &end)); EXPECT_EQ(NULL, targeter->FindNextBestTarget(content, &end));
// Reset the locations of the gesture events to be in the root view
// coordinate space since we are about to call FindTargetForEvent()
// again (calls to FindTargetForEvent() and FindNextBestTarget()
// mutate the location of the gesture events to be in the coordinate
// space of the returned view).
details = ui::GestureEventDetails(ui::ET_GESTURE_TAP, 0.0f, 0.0f);
details.set_bounding_box(bounding_box);
tap = GestureEventForTest(details, center_point.x(), center_point.y());
details = ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0.0f, 0.0f);
details.set_bounding_box(bounding_box);
scroll_begin =
GestureEventForTest(details, center_point.x(), center_point.y());
details = ui::GestureEventDetails(ui::ET_GESTURE_END, 0.0f, 0.0f);
details.set_bounding_box(bounding_box);
end = GestureEventForTest(details, center_point.x(), center_point.y());
// If no default gesture handler is currently set, targeting should be // If no default gesture handler is currently set, targeting should be
// performed using the location of the gesture event for a TAP and a // performed using the location of the gesture event for a TAP and a
// SCROLL_BEGIN. // SCROLL_BEGIN.
...@@ -333,6 +349,98 @@ TEST_F(ViewTargeterTest, ViewTargeterForGestureEvents) { ...@@ -333,6 +349,98 @@ TEST_F(ViewTargeterTest, ViewTargeterForGestureEvents) {
EXPECT_EQ(NULL, targeter->FindNextBestTarget(child, &end)); EXPECT_EQ(NULL, targeter->FindNextBestTarget(child, &end));
} }
// Tests that calls to FindTargetForEvent() and FindNextBestTarget() change
// the location of a gesture event to be in the correct coordinate space.
TEST_F(ViewTargeterTest, GestureEventCoordinateConversion) {
Widget widget;
Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP);
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
init_params.bounds = gfx::Rect(0, 0, 200, 200);
widget.Init(init_params);
// The coordinates used for SetBounds() are in the parent coordinate space.
View* content = new View;
content->SetBounds(0, 0, 100, 100);
View* child = new View;
child->SetBounds(50, 50, 20, 20);
View* grandchild = new View;
grandchild->SetBounds(5, 5, 10, 10);
View* great_grandchild = new View;
great_grandchild->SetBounds(3, 3, 4, 4);
widget.SetContentsView(content);
content->AddChildView(child);
child->AddChildView(grandchild);
grandchild->AddChildView(great_grandchild);
internal::RootView* root_view =
static_cast<internal::RootView*>(widget.GetRootView());
ui::EventTargeter* targeter = root_view->targeter();
// Define a GESTURE_TAP event with a bounding box centered at (60, 60)
// in root view coordinates with width and height of 4.
gfx::Rect bounding_box(gfx::Point(58, 58), gfx::Size(4, 4));
gfx::Point center_point(bounding_box.CenterPoint());
ui::GestureEventDetails details(ui::ET_GESTURE_TAP, 0.0f, 0.0f);
details.set_bounding_box(bounding_box);
GestureEventForTest tap(details, center_point.x(), center_point.y());
// Calculate the location of the gesture in each of the different
// coordinate spaces.
gfx::Point location_in_root(center_point);
EXPECT_EQ(gfx::Point(60, 60), location_in_root);
gfx::Point location_in_great_grandchild(
ConvertPointFromWidgetToView(great_grandchild, location_in_root));
EXPECT_EQ(gfx::Point(2, 2), location_in_great_grandchild);
gfx::Point location_in_grandchild(
ConvertPointFromWidgetToView(grandchild, location_in_root));
EXPECT_EQ(gfx::Point(5, 5), location_in_grandchild);
gfx::Point location_in_child(
ConvertPointFromWidgetToView(child, location_in_root));
EXPECT_EQ(gfx::Point(10, 10), location_in_child);
gfx::Point location_in_content(
ConvertPointFromWidgetToView(content, location_in_root));
EXPECT_EQ(gfx::Point(60, 60), location_in_content);
// Verify the location of |tap| is in screen coordinates.
EXPECT_EQ(gfx::Point(60, 60), tap.location());
// The initial target should be |great_grandchild| and the location of
// the event should be changed into the coordinate space of the target.
EXPECT_EQ(great_grandchild, targeter->FindTargetForEvent(root_view, &tap));
EXPECT_EQ(location_in_great_grandchild, tap.location());
SetGestureHandler(root_view, great_grandchild);
// The next target should be |grandchild| and the location of
// the event should be changed into the coordinate space of the target.
EXPECT_EQ(grandchild, targeter->FindNextBestTarget(great_grandchild, &tap));
EXPECT_EQ(location_in_grandchild, tap.location());
SetGestureHandler(root_view, grandchild);
// The next target should be |child| and the location of
// the event should be changed into the coordinate space of the target.
EXPECT_EQ(child, targeter->FindNextBestTarget(grandchild, &tap));
EXPECT_EQ(location_in_child, tap.location());
SetGestureHandler(root_view, child);
// The next target should be |content| and the location of
// the event should be changed into the coordinate space of the target.
EXPECT_EQ(content, targeter->FindNextBestTarget(child, &tap));
EXPECT_EQ(location_in_content, tap.location());
SetGestureHandler(root_view, content);
// The next target should be |root_view| and the location of
// the event should be changed into the coordinate space of the target.
EXPECT_EQ(widget.GetRootView(), targeter->FindNextBestTarget(content, &tap));
EXPECT_EQ(location_in_root, tap.location());
SetGestureHandler(root_view, widget.GetRootView());
// The next target should be NULL and the location of the event should
// remain unchanged.
EXPECT_EQ(NULL, targeter->FindNextBestTarget(widget.GetRootView(), &tap));
EXPECT_EQ(location_in_root, tap.location());
}
// Tests that the functions ViewTargeterDelegate::DoesIntersectRect() // Tests that the functions ViewTargeterDelegate::DoesIntersectRect()
// and MaskedTargeterDelegate::DoesIntersectRect() work as intended when // and MaskedTargeterDelegate::DoesIntersectRect() work as intended when
// called on views which are derived from ViewTargeterDelegate. // called on views which are derived from ViewTargeterDelegate.
...@@ -428,24 +536,26 @@ TEST_F(ViewTargeterTest, HitTestCallsOnView) { ...@@ -428,24 +536,26 @@ TEST_F(ViewTargeterTest, HitTestCallsOnView) {
gfx::Rect r4(115, 342, 200, 10); gfx::Rect r4(115, 342, 200, 10);
// Test calls into View::HitTestPoint(). // Test calls into View::HitTestPoint().
EXPECT_TRUE(v1->HitTestPoint(ConvertPointToView(v1, v1_centerpoint))); EXPECT_TRUE(
EXPECT_TRUE(v2->HitTestPoint(ConvertPointToView(v2, v2_centerpoint))); v1->HitTestPoint(ConvertPointFromWidgetToView(v1, v1_centerpoint)));
EXPECT_TRUE(
v2->HitTestPoint(ConvertPointFromWidgetToView(v2, v2_centerpoint)));
EXPECT_TRUE(v1->HitTestPoint(ConvertPointToView(v1, v1_origin))); EXPECT_TRUE(v1->HitTestPoint(ConvertPointFromWidgetToView(v1, v1_origin)));
EXPECT_FALSE(v2->HitTestPoint(ConvertPointToView(v2, v2_origin))); EXPECT_FALSE(v2->HitTestPoint(ConvertPointFromWidgetToView(v2, v2_origin)));
// Test calls into View::HitTestRect(). // Test calls into View::HitTestRect().
EXPECT_TRUE(v1->HitTestRect(ConvertRectToView(v1, r1))); EXPECT_TRUE(v1->HitTestRect(ConvertRectFromWidgetToView(v1, r1)));
EXPECT_FALSE(v2->HitTestRect(ConvertRectToView(v2, r1))); EXPECT_FALSE(v2->HitTestRect(ConvertRectFromWidgetToView(v2, r1)));
EXPECT_FALSE(v1->HitTestRect(ConvertRectToView(v1, r2))); EXPECT_FALSE(v1->HitTestRect(ConvertRectFromWidgetToView(v1, r2)));
EXPECT_TRUE(v2->HitTestRect(ConvertRectToView(v2, r2))); EXPECT_TRUE(v2->HitTestRect(ConvertRectFromWidgetToView(v2, r2)));
EXPECT_TRUE(v1->HitTestRect(ConvertRectToView(v1, r3))); EXPECT_TRUE(v1->HitTestRect(ConvertRectFromWidgetToView(v1, r3)));
EXPECT_TRUE(v2->HitTestRect(ConvertRectToView(v2, r3))); EXPECT_TRUE(v2->HitTestRect(ConvertRectFromWidgetToView(v2, r3)));
EXPECT_FALSE(v1->HitTestRect(ConvertRectToView(v1, r4))); EXPECT_FALSE(v1->HitTestRect(ConvertRectFromWidgetToView(v1, r4)));
EXPECT_FALSE(v2->HitTestRect(ConvertRectToView(v2, r4))); EXPECT_FALSE(v2->HitTestRect(ConvertRectFromWidgetToView(v2, r4)));
// Test calls into View::GetEventHandlerForPoint(). // Test calls into View::GetEventHandlerForPoint().
EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_centerpoint)); EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_centerpoint));
......
...@@ -647,19 +647,19 @@ View::DragInfo* RootView::GetDragInfo() { ...@@ -647,19 +647,19 @@ View::DragInfo* RootView::GetDragInfo() {
void RootView::DispatchGestureEvent(ui::GestureEvent* event) { void RootView::DispatchGestureEvent(ui::GestureEvent* event) {
bool gesture_handler_set_before_dispatch = !!gesture_handler_; bool gesture_handler_set_before_dispatch = !!gesture_handler_;
View* target = scoped_ptr<ui::Event> event_copy = ui::Event::Clone(*event);
static_cast<View*>(targeter()->FindTargetForEvent(this, event)); View* target = static_cast<View*>(
targeter()->FindTargetForEvent(this, event_copy.get()));
while (target && target != this) { while (target && target != this) {
// Create and dispatch a copy of |event|. // Create and dispatch a copy of |event|.
ui::GestureEvent event_copy(*event, static_cast<View*>(this), target);
ui::EventDispatchDetails dispatch_details = ui::EventDispatchDetails dispatch_details =
DispatchEvent(target, &event_copy); DispatchEvent(target, event_copy.get());
if (dispatch_details.dispatcher_destroyed) if (dispatch_details.dispatcher_destroyed)
return; return;
if (event_copy.stopped_propagation()) if (event_copy->stopped_propagation())
event->StopPropagation(); event->StopPropagation();
else if (event_copy.handled()) else if (event_copy->handled())
event->SetHandled(); event->SetHandled();
// If the event was handled by the previous dispatch or if the target // If the event was handled by the previous dispatch or if the target
...@@ -669,7 +669,8 @@ void RootView::DispatchGestureEvent(ui::GestureEvent* event) { ...@@ -669,7 +669,8 @@ void RootView::DispatchGestureEvent(ui::GestureEvent* event) {
// The event was not handled by |target|, so continue processing by // The event was not handled by |target|, so continue processing by
// re-targeting the event. // re-targeting the event.
target = static_cast<View*>(targeter()->FindNextBestTarget(target, event)); target = static_cast<View*>(
targeter()->FindNextBestTarget(target, event_copy.get()));
} }
// |event| was not handled, so if |gesture_handler_| was not set by the // |event| was not handled, so if |gesture_handler_| was not set by the
......
...@@ -39,6 +39,19 @@ ...@@ -39,6 +39,19 @@
namespace views { namespace views {
namespace test { namespace test {
namespace {
// TODO(tdanderson): This utility function is used in different unittest
// files. Move to a common location to avoid
// repeated code.
gfx::Point ConvertPointFromWidgetToView(View* view, const gfx::Point& p) {
gfx::Point tmp(p);
View::ConvertPointToTarget(view->GetWidget()->GetRootView(), view, &tmp);
return tmp;
}
} // namespace
// A view that keeps track of the events it receives, optionally consuming them. // A view that keeps track of the events it receives, optionally consuming them.
class EventCountView : public View { class EventCountView : public View {
public: public:
...@@ -2349,6 +2362,97 @@ TEST_F(WidgetTest, ScrollGestureEventDispatch) { ...@@ -2349,6 +2362,97 @@ TEST_F(WidgetTest, ScrollGestureEventDispatch) {
widget->Close(); widget->Close();
} }
// A class used in WidgetTest.GestureEventLocationWhileBubbling to verify
// that when a gesture event bubbles up a View hierarchy, the location
// of a gesture event seen by each View is in the local coordinate space
// of that View.
class GestureLocationView : public EventCountView {
public:
GestureLocationView() {}
virtual ~GestureLocationView() {}
void set_expected_location(gfx::Point expected_location) {
expected_location_ = expected_location;
}
// EventCountView:
virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
EventCountView::OnGestureEvent(event);
// Verify that the location of |event| is in the local coordinate
// space of |this|.
EXPECT_EQ(expected_location_, event->location());
}
private:
// The expected location of a gesture event dispatched to |this|.
gfx::Point expected_location_;
DISALLOW_COPY_AND_ASSIGN(GestureLocationView);
};
// Verifies that the location of a gesture event is always in the local
// coordinate space of the View receiving the event while bubbling.
TEST_F(WidgetTest, GestureEventLocationWhileBubbling) {
Widget* widget = CreateTopLevelNativeWidget();
widget->SetBounds(gfx::Rect(0, 0, 300, 300));
// Define a hierarchy of three views (coordinates shown below are in the
// coordinate space of the root view, but the coordinates used for
// SetBounds() are in their parent coordinate space).
// v1 (50, 50, 150, 150)
// v2 (100, 70, 50, 80)
// v3 (120, 100, 10, 10)
GestureLocationView* v1 = new GestureLocationView();
v1->SetBounds(50, 50, 150, 150);
GestureLocationView* v2 = new GestureLocationView();
v2->SetBounds(50, 20, 50, 80);
GestureLocationView* v3 = new GestureLocationView();
v3->SetBounds(20, 30, 10, 10);
internal::RootView* root_view =
static_cast<internal::RootView*>(widget->GetRootView());
root_view->AddChildView(v1);
v1->AddChildView(v2);
v2->AddChildView(v3);
widget->Show();
// Define a GESTURE_TAP event located at (125, 105) in root view coordinates.
// This event is contained within all of |v1|, |v2|, and |v3|.
gfx::Point location_in_root(125, 105);
GestureEventForTest tap(
ui::ET_GESTURE_TAP, location_in_root.x(), location_in_root.y());
// Calculate the location of the event in the local coordinate spaces
// of each of the views.
gfx::Point location_in_v1(ConvertPointFromWidgetToView(v1, location_in_root));
EXPECT_EQ(gfx::Point(75, 55), location_in_v1);
gfx::Point location_in_v2(ConvertPointFromWidgetToView(v2, location_in_root));
EXPECT_EQ(gfx::Point(25, 35), location_in_v2);
gfx::Point location_in_v3(ConvertPointFromWidgetToView(v3, location_in_root));
EXPECT_EQ(gfx::Point(5, 5), location_in_v3);
// Dispatch the event. When each view receives the event, its location should
// be in the local coordinate space of that view (see the check made by
// GestureLocationView). After dispatch is complete the event's location
// should be in the root coordinate space.
v1->set_expected_location(location_in_v1);
v2->set_expected_location(location_in_v2);
v3->set_expected_location(location_in_v3);
widget->OnGestureEvent(&tap);
EXPECT_EQ(location_in_root, tap.location());
// Verify that each view did in fact see the event.
EventCountView* view1 = v1;
EventCountView* view2 = v2;
EventCountView* view3 = v3;
EXPECT_EQ(1, view1->GetEventCount(ui::ET_GESTURE_TAP));
EXPECT_EQ(1, view2->GetEventCount(ui::ET_GESTURE_TAP));
EXPECT_EQ(1, view3->GetEventCount(ui::ET_GESTURE_TAP));
widget->Close();
}
// Verifies that disabled views are permitted to be set as the default gesture // Verifies that disabled views are permitted to be set as the default gesture
// handler in RootView. Also verifies that gesture events targeted to a disabled // handler in RootView. Also verifies that gesture events targeted to a disabled
// view are not actually dispatched to the view, but are still marked as // view are not actually dispatched to the view, but are still marked as
......
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