Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / views / view_targeter_unittest.cc
blob6564a8800a79f8cf0ced54267251c8348e7a57ac
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/views/view_targeter.h"
7 #include "ui/events/event_targeter.h"
8 #include "ui/events/event_utils.h"
9 #include "ui/gfx/path.h"
10 #include "ui/views/masked_targeter_delegate.h"
11 #include "ui/views/test/views_test_base.h"
12 #include "ui/views/view_targeter.h"
13 #include "ui/views/view_targeter_delegate.h"
14 #include "ui/views/widget/root_view.h"
16 namespace views {
18 // A derived class of View used for testing purposes.
19 class TestingView : public View, public ViewTargeterDelegate {
20 public:
21 TestingView() : can_process_events_within_subtree_(true) {}
22 virtual ~TestingView() {}
24 // Reset all test state.
25 void Reset() { can_process_events_within_subtree_ = true; }
27 void set_can_process_events_within_subtree(bool can_process) {
28 can_process_events_within_subtree_ = can_process;
31 // A call-through function to ViewTargeterDelegate::DoesIntersectRect().
32 bool TestDoesIntersectRect(const View* target, const gfx::Rect& rect) const {
33 return DoesIntersectRect(target, rect);
36 // View:
37 virtual bool CanProcessEventsWithinSubtree() const OVERRIDE {
38 return can_process_events_within_subtree_;
41 private:
42 // Value to return from CanProcessEventsWithinSubtree().
43 bool can_process_events_within_subtree_;
45 DISALLOW_COPY_AND_ASSIGN(TestingView);
48 // A derived class of View having a triangular-shaped hit test mask.
49 class TestMaskedView : public View, public MaskedTargeterDelegate {
50 public:
51 TestMaskedView() {}
52 virtual ~TestMaskedView() {}
54 // A call-through function to MaskedTargeterDelegate::DoesIntersectRect().
55 bool TestDoesIntersectRect(const View* target, const gfx::Rect& rect) const {
56 return DoesIntersectRect(target, rect);
59 private:
60 // MaskedTargeterDelegate:
61 virtual bool GetHitTestMask(gfx::Path* mask) const OVERRIDE {
62 DCHECK(mask);
63 SkScalar w = SkIntToScalar(width());
64 SkScalar h = SkIntToScalar(height());
66 // Create a triangular mask within the bounds of this View.
67 mask->moveTo(w / 2, 0);
68 mask->lineTo(w, h);
69 mask->lineTo(0, h);
70 mask->close();
71 return true;
74 DISALLOW_COPY_AND_ASSIGN(TestMaskedView);
77 namespace test {
79 // TODO(tdanderson): Clean up this test suite by moving common code/state into
80 // ViewTargeterTest and overriding SetUp(), TearDown(), etc.
81 // See crbug.com/355680.
82 class ViewTargeterTest : public ViewsTestBase {
83 public:
84 ViewTargeterTest() {}
85 virtual ~ViewTargeterTest() {}
87 void SetGestureHandler(internal::RootView* root_view, View* handler) {
88 root_view->gesture_handler_ = handler;
91 void SetAllowGestureEventRetargeting(internal::RootView* root_view,
92 bool allow) {
93 root_view->allow_gesture_event_retargeting_ = allow;
96 private:
97 DISALLOW_COPY_AND_ASSIGN(ViewTargeterTest);
100 namespace {
102 gfx::Point ConvertPointToView(View* view, const gfx::Point& p) {
103 gfx::Point tmp(p);
104 View::ConvertPointToTarget(view->GetWidget()->GetRootView(), view, &tmp);
105 return tmp;
108 gfx::Rect ConvertRectToView(View* view, const gfx::Rect& r) {
109 gfx::Rect tmp(r);
110 tmp.set_origin(ConvertPointToView(view, r.origin()));
111 return tmp;
114 } // namespace
116 // Verifies that the the functions ViewTargeter::FindTargetForEvent()
117 // and ViewTargeter::FindNextBestTarget() are implemented correctly
118 // for key events.
119 TEST_F(ViewTargeterTest, ViewTargeterForKeyEvents) {
120 Widget widget;
121 Widget::InitParams init_params =
122 CreateParams(Widget::InitParams::TYPE_POPUP);
123 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
124 widget.Init(init_params);
126 View* content = new View;
127 View* child = new View;
128 View* grandchild = new View;
130 widget.SetContentsView(content);
131 content->AddChildView(child);
132 child->AddChildView(grandchild);
134 grandchild->SetFocusable(true);
135 grandchild->RequestFocus();
137 internal::RootView* root_view =
138 static_cast<internal::RootView*>(widget.GetRootView());
139 ui::EventTargeter* targeter = root_view->targeter();
141 ui::KeyEvent key_event('a', ui::VKEY_A, ui::EF_NONE);
143 // The focused view should be the initial target of the event.
144 ui::EventTarget* current_target = targeter->FindTargetForEvent(root_view,
145 &key_event);
146 EXPECT_EQ(grandchild, static_cast<View*>(current_target));
148 // Verify that FindNextBestTarget() will return the parent view of the
149 // argument (and NULL if the argument has no parent view).
150 current_target = targeter->FindNextBestTarget(grandchild, &key_event);
151 EXPECT_EQ(child, static_cast<View*>(current_target));
152 current_target = targeter->FindNextBestTarget(child, &key_event);
153 EXPECT_EQ(content, static_cast<View*>(current_target));
154 current_target = targeter->FindNextBestTarget(content, &key_event);
155 EXPECT_EQ(widget.GetRootView(), static_cast<View*>(current_target));
156 current_target = targeter->FindNextBestTarget(widget.GetRootView(),
157 &key_event);
158 EXPECT_EQ(NULL, static_cast<View*>(current_target));
161 // Verifies that the the functions ViewTargeter::FindTargetForEvent()
162 // and ViewTargeter::FindNextBestTarget() are implemented correctly
163 // for scroll events.
164 TEST_F(ViewTargeterTest, ViewTargeterForScrollEvents) {
165 Widget widget;
166 Widget::InitParams init_params =
167 CreateParams(Widget::InitParams::TYPE_POPUP);
168 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
169 init_params.bounds = gfx::Rect(0, 0, 200, 200);
170 widget.Init(init_params);
172 // The coordinates used for SetBounds() are in the parent coordinate space.
173 View* content = new View;
174 content->SetBounds(0, 0, 100, 100);
175 View* child = new View;
176 child->SetBounds(50, 50, 20, 20);
177 View* grandchild = new View;
178 grandchild->SetBounds(0, 0, 5, 5);
180 widget.SetContentsView(content);
181 content->AddChildView(child);
182 child->AddChildView(grandchild);
184 internal::RootView* root_view =
185 static_cast<internal::RootView*>(widget.GetRootView());
186 ui::EventTargeter* targeter = root_view->targeter();
188 // The event falls within the bounds of |child| and |content| but not
189 // |grandchild|, so |child| should be the initial target for the event.
190 ui::ScrollEvent scroll(ui::ET_SCROLL,
191 gfx::Point(60, 60),
192 ui::EventTimeForNow(),
194 0, 3,
195 0, 3,
197 ui::EventTarget* current_target = targeter->FindTargetForEvent(root_view,
198 &scroll);
199 EXPECT_EQ(child, static_cast<View*>(current_target));
201 // Verify that FindNextBestTarget() will return the parent view of the
202 // argument (and NULL if the argument has no parent view).
203 current_target = targeter->FindNextBestTarget(child, &scroll);
204 EXPECT_EQ(content, static_cast<View*>(current_target));
205 current_target = targeter->FindNextBestTarget(content, &scroll);
206 EXPECT_EQ(widget.GetRootView(), static_cast<View*>(current_target));
207 current_target = targeter->FindNextBestTarget(widget.GetRootView(),
208 &scroll);
209 EXPECT_EQ(NULL, static_cast<View*>(current_target));
211 // The event falls outside of the original specified bounds of |content|,
212 // |child|, and |grandchild|. But since |content| is the contents view,
213 // and contents views are resized to fill the entire area of the root
214 // view, the event's initial target should still be |content|.
215 scroll = ui::ScrollEvent(ui::ET_SCROLL,
216 gfx::Point(150, 150),
217 ui::EventTimeForNow(),
219 0, 3,
220 0, 3,
222 current_target = targeter->FindTargetForEvent(root_view, &scroll);
223 EXPECT_EQ(content, static_cast<View*>(current_target));
226 // Convenience to make constructing a GestureEvent simpler.
227 class GestureEventForTest : public ui::GestureEvent {
228 public:
229 GestureEventForTest(ui::EventType type, int x, int y)
230 : GestureEvent(x,
233 base::TimeDelta(),
234 ui::GestureEventDetails(type, 0.0f, 0.0f)) {}
236 GestureEventForTest(ui::GestureEventDetails details, int x, int y)
237 : GestureEvent(x, y, 0, base::TimeDelta(), details) {}
240 // Verifies that the the functions ViewTargeter::FindTargetForEvent()
241 // and ViewTargeter::FindNextBestTarget() are implemented correctly
242 // for gesture events.
243 TEST_F(ViewTargeterTest, ViewTargeterForGestureEvents) {
244 Widget widget;
245 Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP);
246 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
247 init_params.bounds = gfx::Rect(0, 0, 200, 200);
248 widget.Init(init_params);
250 // The coordinates used for SetBounds() are in the parent coordinate space.
251 View* content = new View;
252 content->SetBounds(0, 0, 100, 100);
253 View* child = new View;
254 child->SetBounds(50, 50, 20, 20);
255 View* grandchild = new View;
256 grandchild->SetBounds(0, 0, 5, 5);
258 widget.SetContentsView(content);
259 content->AddChildView(child);
260 child->AddChildView(grandchild);
262 internal::RootView* root_view =
263 static_cast<internal::RootView*>(widget.GetRootView());
264 ui::EventTargeter* targeter = root_view->targeter();
266 // Define a GESTURE_TAP and a GESTURE_SCROLL_BEGIN.
267 gfx::Rect bounding_box(gfx::Point(46, 46), gfx::Size(8, 8));
268 gfx::Point center_point(bounding_box.CenterPoint());
269 ui::GestureEventDetails details(ui::ET_GESTURE_TAP, 0.0f, 0.0f);
270 details.set_bounding_box(bounding_box);
271 GestureEventForTest tap(details, center_point.x(), center_point.y());
272 details = ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0.0f, 0.0f);
273 details.set_bounding_box(bounding_box);
274 GestureEventForTest scroll_begin(details, center_point.x(), center_point.y());
276 // Assume that the view currently handling gestures has been set as
277 // |grandchild| by a previous gesture event. Thus subsequent gesture events
278 // should be initially targeted to |grandchild|, and re-targeting should
279 // be prohibited for all gesture event types except for GESTURE_SCROLL_BEGIN
280 // (which should be re-targeted to the parent of |grandchild|).
281 SetAllowGestureEventRetargeting(root_view, false);
282 SetGestureHandler(root_view, grandchild);
283 EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &tap));
284 EXPECT_EQ(NULL, targeter->FindNextBestTarget(grandchild, &tap));
285 EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &scroll_begin));
286 EXPECT_EQ(child, targeter->FindNextBestTarget(grandchild, &scroll_begin));
288 // Assume that the view currently handling gestures is still set as
289 // |grandchild|, but this was not done by a previous gesture. Thus we are
290 // in the process of finding the View to which subsequent gestures will be
291 // dispatched, so all gesture events should be re-targeted up the ancestor
292 // chain.
293 SetAllowGestureEventRetargeting(root_view, true);
294 EXPECT_EQ(child, targeter->FindNextBestTarget(grandchild, &tap));
295 EXPECT_EQ(child, targeter->FindNextBestTarget(grandchild, &scroll_begin));
297 // Assume that the default gesture handler was set by the previous gesture,
298 // but that this handler is currently NULL. No gesture events should be
299 // re-targeted in this case (regardless of the view that is passed in to
300 // FindNextBestTarget() as the previous target).
301 SetGestureHandler(root_view, NULL);
302 SetAllowGestureEventRetargeting(root_view, false);
303 EXPECT_EQ(NULL, targeter->FindNextBestTarget(child, &tap));
304 EXPECT_EQ(NULL, targeter->FindNextBestTarget(NULL, &tap));
305 EXPECT_EQ(NULL, targeter->FindNextBestTarget(content, &scroll_begin));
307 // If no default gesture handler is currently set, targeting should be
308 // performed using the location of the gesture event.
309 SetAllowGestureEventRetargeting(root_view, true);
310 EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &tap));
311 EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &scroll_begin));
314 // Tests that the functions ViewTargeterDelegate::DoesIntersectRect()
315 // and MaskedTargeterDelegate::DoesIntersectRect() work as intended when
316 // called on views which are derived from ViewTargeterDelegate.
317 // Also verifies that ViewTargeterDelegate::DoesIntersectRect() can
318 // be called from the ViewTargeter installed on RootView.
319 TEST_F(ViewTargeterTest, DoesIntersectRect) {
320 Widget widget;
321 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
322 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
323 params.bounds = gfx::Rect(0, 0, 650, 650);
324 widget.Init(params);
326 internal::RootView* root_view =
327 static_cast<internal::RootView*>(widget.GetRootView());
328 ViewTargeter* view_targeter = root_view->targeter();
330 // The coordinates used for SetBounds() are in the parent coordinate space.
331 TestingView v2;
332 TestMaskedView v1, v3;
333 v1.SetBounds(0, 0, 200, 200);
334 v2.SetBounds(300, 0, 300, 300);
335 v3.SetBounds(0, 0, 100, 100);
336 root_view->AddChildView(&v1);
337 root_view->AddChildView(&v2);
338 v2.AddChildView(&v3);
340 // The coordinates used below are in the local coordinate space of the
341 // view that is passed in as an argument.
343 // Hit tests against |v1|, which has a hit test mask.
344 EXPECT_TRUE(v1.TestDoesIntersectRect(&v1, gfx::Rect(0, 0, 200, 200)));
345 EXPECT_TRUE(v1.TestDoesIntersectRect(&v1, gfx::Rect(-10, -10, 110, 12)));
346 EXPECT_TRUE(v1.TestDoesIntersectRect(&v1, gfx::Rect(112, 142, 1, 1)));
347 EXPECT_FALSE(v1.TestDoesIntersectRect(&v1, gfx::Rect(0, 0, 20, 20)));
348 EXPECT_FALSE(v1.TestDoesIntersectRect(&v1, gfx::Rect(-10, -10, 90, 12)));
349 EXPECT_FALSE(v1.TestDoesIntersectRect(&v1, gfx::Rect(150, 49, 1, 1)));
351 // Hit tests against |v2|, which does not have a hit test mask.
352 EXPECT_TRUE(v2.TestDoesIntersectRect(&v2, gfx::Rect(0, 0, 200, 200)));
353 EXPECT_TRUE(v2.TestDoesIntersectRect(&v2, gfx::Rect(-10, 250, 60, 60)));
354 EXPECT_TRUE(v2.TestDoesIntersectRect(&v2, gfx::Rect(250, 250, 1, 1)));
355 EXPECT_FALSE(v2.TestDoesIntersectRect(&v2, gfx::Rect(-10, 250, 7, 7)));
356 EXPECT_FALSE(v2.TestDoesIntersectRect(&v2, gfx::Rect(-1, -1, 1, 1)));
358 // Hit tests against |v3|, which has a hit test mask and is a child of |v2|.
359 EXPECT_TRUE(v3.TestDoesIntersectRect(&v3, gfx::Rect(0, 0, 50, 50)));
360 EXPECT_TRUE(v3.TestDoesIntersectRect(&v3, gfx::Rect(90, 90, 1, 1)));
361 EXPECT_FALSE(v3.TestDoesIntersectRect(&v3, gfx::Rect(10, 125, 50, 50)));
362 EXPECT_FALSE(v3.TestDoesIntersectRect(&v3, gfx::Rect(110, 110, 1, 1)));
364 // Verify that hit-testing is performed correctly when using the
365 // call-through function ViewTargeter::DoesIntersectRect().
366 EXPECT_TRUE(view_targeter->DoesIntersectRect(root_view,
367 gfx::Rect(0, 0, 50, 50)));
368 EXPECT_FALSE(view_targeter->DoesIntersectRect(root_view,
369 gfx::Rect(-20, -20, 10, 10)));
372 // Tests that calls made directly on the hit-testing methods in View
373 // (HitTestPoint(), HitTestRect(), etc.) return the correct values.
374 TEST_F(ViewTargeterTest, HitTestCallsOnView) {
375 // The coordinates in this test are in the coordinate space of the root view.
376 Widget* widget = new Widget;
377 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
378 widget->Init(params);
379 View* root_view = widget->GetRootView();
380 root_view->SetBoundsRect(gfx::Rect(0, 0, 500, 500));
382 // |v1| has no hit test mask. No ViewTargeter is installed on |v1|, which
383 // means that View::HitTestRect() will call into the targeter installed on
384 // the root view instead when we hit test against |v1|.
385 gfx::Rect v1_bounds = gfx::Rect(0, 0, 100, 100);
386 TestingView* v1 = new TestingView();
387 v1->SetBoundsRect(v1_bounds);
388 root_view->AddChildView(v1);
390 // |v2| has a triangular hit test mask. Install a ViewTargeter on |v2| which
391 // will be called into by View::HitTestRect().
392 gfx::Rect v2_bounds = gfx::Rect(105, 0, 100, 100);
393 TestMaskedView* v2 = new TestMaskedView();
394 v2->SetBoundsRect(v2_bounds);
395 root_view->AddChildView(v2);
396 ViewTargeter* view_targeter = new ViewTargeter(v2);
397 v2->SetEventTargeter(make_scoped_ptr(view_targeter));
399 gfx::Point v1_centerpoint = v1_bounds.CenterPoint();
400 gfx::Point v2_centerpoint = v2_bounds.CenterPoint();
401 gfx::Point v1_origin = v1_bounds.origin();
402 gfx::Point v2_origin = v2_bounds.origin();
403 gfx::Rect r1(10, 10, 110, 15);
404 gfx::Rect r2(106, 1, 98, 98);
405 gfx::Rect r3(0, 0, 300, 300);
406 gfx::Rect r4(115, 342, 200, 10);
408 // Test calls into View::HitTestPoint().
409 EXPECT_TRUE(v1->HitTestPoint(ConvertPointToView(v1, v1_centerpoint)));
410 EXPECT_TRUE(v2->HitTestPoint(ConvertPointToView(v2, v2_centerpoint)));
412 EXPECT_TRUE(v1->HitTestPoint(ConvertPointToView(v1, v1_origin)));
413 EXPECT_FALSE(v2->HitTestPoint(ConvertPointToView(v2, v2_origin)));
415 // Test calls into View::HitTestRect().
416 EXPECT_TRUE(v1->HitTestRect(ConvertRectToView(v1, r1)));
417 EXPECT_FALSE(v2->HitTestRect(ConvertRectToView(v2, r1)));
419 EXPECT_FALSE(v1->HitTestRect(ConvertRectToView(v1, r2)));
420 EXPECT_TRUE(v2->HitTestRect(ConvertRectToView(v2, r2)));
422 EXPECT_TRUE(v1->HitTestRect(ConvertRectToView(v1, r3)));
423 EXPECT_TRUE(v2->HitTestRect(ConvertRectToView(v2, r3)));
425 EXPECT_FALSE(v1->HitTestRect(ConvertRectToView(v1, r4)));
426 EXPECT_FALSE(v2->HitTestRect(ConvertRectToView(v2, r4)));
428 // Test calls into View::GetEventHandlerForPoint().
429 EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_centerpoint));
430 EXPECT_EQ(v2, root_view->GetEventHandlerForPoint(v2_centerpoint));
432 EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_origin));
433 EXPECT_EQ(root_view, root_view->GetEventHandlerForPoint(v2_origin));
435 // Test calls into View::GetTooltipHandlerForPoint().
436 EXPECT_EQ(v1, root_view->GetTooltipHandlerForPoint(v1_centerpoint));
437 EXPECT_EQ(v2, root_view->GetTooltipHandlerForPoint(v2_centerpoint));
439 EXPECT_EQ(v1, root_view->GetTooltipHandlerForPoint(v1_origin));
440 EXPECT_EQ(root_view, root_view->GetTooltipHandlerForPoint(v2_origin));
442 EXPECT_FALSE(v1->GetTooltipHandlerForPoint(v2_origin));
444 widget->CloseNow();
447 } // namespace test
448 } // namespace views