use imageInfo instead of (deprecated) getDevice and config()
[chromium-blink-merge.git] / ui / views / view_targeter_unittest.cc
blob54a28b13a3235e51cd69ccb6f51783c99f05018e
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_view_targeter.h"
11 #include "ui/views/test/views_test_base.h"
12 #include "ui/views/widget/root_view.h"
14 namespace views {
16 // A class used to define a triangular-shaped hit test mask on a View.
17 class TestMaskedViewTargeter : public MaskedViewTargeter {
18 public:
19 explicit TestMaskedViewTargeter(View* masked_view)
20 : MaskedViewTargeter(masked_view) {}
21 virtual ~TestMaskedViewTargeter() {}
23 private:
24 virtual bool GetHitTestMask(const View* view,
25 gfx::Path* mask) const OVERRIDE {
26 SkScalar w = SkIntToScalar(view->width());
27 SkScalar h = SkIntToScalar(view->height());
29 // Create a triangular mask within the bounds of |view|.
30 mask->moveTo(w / 2, 0);
31 mask->lineTo(w, h);
32 mask->lineTo(0, h);
33 mask->close();
35 return true;
38 DISALLOW_COPY_AND_ASSIGN(TestMaskedViewTargeter);
41 // A derived class of View used for testing purposes.
42 class TestingView : public View {
43 public:
44 TestingView() : can_process_events_within_subtree_(true) {}
45 virtual ~TestingView() {}
47 // Reset all test state.
48 void Reset() { can_process_events_within_subtree_ = true; }
50 void set_can_process_events_within_subtree(bool can_process) {
51 can_process_events_within_subtree_ = can_process;
54 // View:
55 virtual bool CanProcessEventsWithinSubtree() const OVERRIDE {
56 return can_process_events_within_subtree_;
59 private:
60 // Value to return from CanProcessEventsWithinSubtree().
61 bool can_process_events_within_subtree_;
63 DISALLOW_COPY_AND_ASSIGN(TestingView);
66 namespace test {
68 typedef ViewsTestBase ViewTargeterTest;
70 // Verifies that the the functions ViewTargeter::FindTargetForEvent()
71 // and ViewTargeter::FindNextBestTarget() are implemented correctly
72 // for key events.
73 TEST_F(ViewTargeterTest, ViewTargeterForKeyEvents) {
74 Widget widget;
75 Widget::InitParams init_params =
76 CreateParams(Widget::InitParams::TYPE_POPUP);
77 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
78 widget.Init(init_params);
80 View* content = new View;
81 View* child = new View;
82 View* grandchild = new View;
84 widget.SetContentsView(content);
85 content->AddChildView(child);
86 child->AddChildView(grandchild);
88 grandchild->SetFocusable(true);
89 grandchild->RequestFocus();
91 ui::EventTargeter* targeter = new ViewTargeter();
92 internal::RootView* root_view =
93 static_cast<internal::RootView*>(widget.GetRootView());
94 root_view->SetEventTargeter(make_scoped_ptr(targeter));
96 ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, true);
98 // The focused view should be the initial target of the event.
99 ui::EventTarget* current_target = targeter->FindTargetForEvent(root_view,
100 &key_event);
101 EXPECT_EQ(grandchild, static_cast<View*>(current_target));
103 // Verify that FindNextBestTarget() will return the parent view of the
104 // argument (and NULL if the argument has no parent view).
105 current_target = targeter->FindNextBestTarget(grandchild, &key_event);
106 EXPECT_EQ(child, static_cast<View*>(current_target));
107 current_target = targeter->FindNextBestTarget(child, &key_event);
108 EXPECT_EQ(content, static_cast<View*>(current_target));
109 current_target = targeter->FindNextBestTarget(content, &key_event);
110 EXPECT_EQ(widget.GetRootView(), static_cast<View*>(current_target));
111 current_target = targeter->FindNextBestTarget(widget.GetRootView(),
112 &key_event);
113 EXPECT_EQ(NULL, static_cast<View*>(current_target));
116 // Verifies that the the functions ViewTargeter::FindTargetForEvent()
117 // and ViewTargeter::FindNextBestTarget() are implemented correctly
118 // for scroll events.
119 TEST_F(ViewTargeterTest, ViewTargeterForScrollEvents) {
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 init_params.bounds = gfx::Rect(0, 0, 200, 200);
125 widget.Init(init_params);
127 // The coordinates used for SetBounds() are in the parent coordinate space.
128 View* content = new View;
129 content->SetBounds(0, 0, 100, 100);
130 View* child = new View;
131 child->SetBounds(50, 50, 20, 20);
132 View* grandchild = new View;
133 grandchild->SetBounds(0, 0, 5, 5);
135 widget.SetContentsView(content);
136 content->AddChildView(child);
137 child->AddChildView(grandchild);
139 ui::EventTargeter* targeter = new ViewTargeter();
140 internal::RootView* root_view =
141 static_cast<internal::RootView*>(widget.GetRootView());
142 root_view->SetEventTargeter(make_scoped_ptr(targeter));
144 // The event falls within the bounds of |child| and |content| but not
145 // |grandchild|, so |child| should be the initial target for the event.
146 ui::ScrollEvent scroll(ui::ET_SCROLL,
147 gfx::Point(60, 60),
148 ui::EventTimeForNow(),
150 0, 3,
151 0, 3,
153 ui::EventTarget* current_target = targeter->FindTargetForEvent(root_view,
154 &scroll);
155 EXPECT_EQ(child, static_cast<View*>(current_target));
157 // Verify that FindNextBestTarget() will return the parent view of the
158 // argument (and NULL if the argument has no parent view).
159 current_target = targeter->FindNextBestTarget(child, &scroll);
160 EXPECT_EQ(content, static_cast<View*>(current_target));
161 current_target = targeter->FindNextBestTarget(content, &scroll);
162 EXPECT_EQ(widget.GetRootView(), static_cast<View*>(current_target));
163 current_target = targeter->FindNextBestTarget(widget.GetRootView(),
164 &scroll);
165 EXPECT_EQ(NULL, static_cast<View*>(current_target));
167 // The event falls outside of the original specified bounds of |content|,
168 // |child|, and |grandchild|. But since |content| is the contents view,
169 // and contents views are resized to fill the entire area of the root
170 // view, the event's initial target should still be |content|.
171 scroll = ui::ScrollEvent(ui::ET_SCROLL,
172 gfx::Point(150, 150),
173 ui::EventTimeForNow(),
175 0, 3,
176 0, 3,
178 current_target = targeter->FindTargetForEvent(root_view, &scroll);
179 EXPECT_EQ(content, static_cast<View*>(current_target));
182 // Tests the basic functionality of the method
183 // ViewTargeter::SubtreeShouldBeExploredForEvent().
184 TEST_F(ViewTargeterTest, SubtreeShouldBeExploredForEvent) {
185 Widget widget;
186 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
187 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
188 params.bounds = gfx::Rect(0, 0, 650, 650);
189 widget.Init(params);
191 ui::EventTargeter* targeter = new ViewTargeter();
192 internal::RootView* root_view =
193 static_cast<internal::RootView*>(widget.GetRootView());
194 root_view->SetEventTargeter(make_scoped_ptr(targeter));
196 // The coordinates used for SetBounds() are in the parent coordinate space.
197 View v1, v2, v3;
198 v1.SetBounds(0, 0, 300, 300);
199 v2.SetBounds(100, 100, 100, 100);
200 v3.SetBounds(0, 0, 10, 10);
201 v3.SetVisible(false);
202 root_view->AddChildView(&v1);
203 v1.AddChildView(&v2);
204 v2.AddChildView(&v3);
206 // Note that the coordinates used below are in |v1|'s coordinate space,
207 // and that SubtreeShouldBeExploredForEvent() expects the event location
208 // to be in the coordinate space of the target's parent. |v1| and
209 // its parent share a common coordinate space.
211 // Event located within |v1| only.
212 gfx::Point point(10, 10);
213 ui::MouseEvent event(ui::ET_MOUSE_PRESSED, point, point,
214 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
215 EXPECT_TRUE(targeter->SubtreeShouldBeExploredForEvent(&v1, event));
216 EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v2, event));
217 v1.ConvertEventToTarget(&v2, &event);
218 EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v3, event));
220 // Event located within |v1| and |v2| only.
221 event.set_location(gfx::Point(150, 150));
222 EXPECT_TRUE(targeter->SubtreeShouldBeExploredForEvent(&v1, event));
223 EXPECT_TRUE(targeter->SubtreeShouldBeExploredForEvent(&v2, event));
224 v1.ConvertEventToTarget(&v2, &event);
225 EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v3, event));
227 // Event located within |v1|, |v2|, and |v3|. Note that |v3| is not
228 // visible, so it cannot handle the event.
229 event.set_location(gfx::Point(105, 105));
230 EXPECT_TRUE(targeter->SubtreeShouldBeExploredForEvent(&v1, event));
231 EXPECT_TRUE(targeter->SubtreeShouldBeExploredForEvent(&v2, event));
232 v1.ConvertEventToTarget(&v2, &event);
233 EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v3, event));
235 // Event located outside the bounds of all views.
236 event.set_location(gfx::Point(400, 400));
237 EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v1, event));
238 EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v2, event));
239 v1.ConvertEventToTarget(&v2, &event);
240 EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v3, event));
242 // TODO(tdanderson): Move the hit-testing unit tests out of view_unittest
243 // and into here. See crbug.com/355425.
246 // Tests that FindTargetForEvent() returns the correct target when some
247 // views in the view tree return false when CanProcessEventsWithinSubtree()
248 // is called on them.
249 TEST_F(ViewTargeterTest, CanProcessEventsWithinSubtree) {
250 Widget widget;
251 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
252 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
253 params.bounds = gfx::Rect(0, 0, 650, 650);
254 widget.Init(params);
256 ui::EventTargeter* targeter = new ViewTargeter();
257 internal::RootView* root_view =
258 static_cast<internal::RootView*>(widget.GetRootView());
259 root_view->SetEventTargeter(make_scoped_ptr(targeter));
261 // The coordinates used for SetBounds() are in the parent coordinate space.
262 TestingView v1, v2, v3;
263 v1.SetBounds(0, 0, 300, 300);
264 v2.SetBounds(100, 100, 100, 100);
265 v3.SetBounds(0, 0, 10, 10);
266 root_view->AddChildView(&v1);
267 v1.AddChildView(&v2);
268 v2.AddChildView(&v3);
270 // Note that the coordinates used below are in the coordinate space of
271 // the root view.
273 // Define |scroll| to be (105, 105) (in the coordinate space of the root
274 // view). This is located within all of |v1|, |v2|, and |v3|.
275 gfx::Point scroll_point(105, 105);
276 ui::ScrollEvent scroll(
277 ui::ET_SCROLL, scroll_point, ui::EventTimeForNow(), 0, 0, 3, 0, 3, 2);
279 // If CanProcessEventsWithinSubtree() returns true for each view,
280 // |scroll| should be targeted at the deepest view in the hierarchy,
281 // which is |v3|.
282 ui::EventTarget* current_target =
283 targeter->FindTargetForEvent(root_view, &scroll);
284 EXPECT_EQ(&v3, current_target);
286 // If CanProcessEventsWithinSubtree() returns |false| when called
287 // on |v3|, then |v3| cannot be the target of |scroll| (this should
288 // instead be |v2|). Note we need to reset the location of |scroll|
289 // because it may have been mutated by the previous call to
290 // FindTargetForEvent().
291 scroll.set_location(scroll_point);
292 v3.set_can_process_events_within_subtree(false);
293 current_target = targeter->FindTargetForEvent(root_view, &scroll);
294 EXPECT_EQ(&v2, current_target);
296 // If CanProcessEventsWithinSubtree() returns |false| when called
297 // on |v2|, then neither |v2| nor |v3| can be the target of |scroll|
298 // (this should instead be |v1|).
299 scroll.set_location(scroll_point);
300 v3.Reset();
301 v2.set_can_process_events_within_subtree(false);
302 current_target = targeter->FindTargetForEvent(root_view, &scroll);
303 EXPECT_EQ(&v1, current_target);
305 // If CanProcessEventsWithinSubtree() returns |false| when called
306 // on |v1|, then none of |v1|, |v2| or |v3| can be the target of |scroll|
307 // (this should instead be the root view itself).
308 scroll.set_location(scroll_point);
309 v2.Reset();
310 v1.set_can_process_events_within_subtree(false);
311 current_target = targeter->FindTargetForEvent(root_view, &scroll);
312 EXPECT_EQ(root_view, current_target);
314 // TODO(tdanderson): We should also test that targeting works correctly
315 // with gestures. See crbug.com/375822.
318 // Tests that FindTargetForEvent() returns the correct target when some
319 // views in the view tree have a MaskedViewTargeter installed, i.e.,
320 // they have a custom-shaped hit test mask.
321 TEST_F(ViewTargeterTest, MaskedViewTargeter) {
322 Widget widget;
323 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
324 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
325 params.bounds = gfx::Rect(0, 0, 650, 650);
326 widget.Init(params);
328 ui::EventTargeter* targeter = new ViewTargeter();
329 internal::RootView* root_view =
330 static_cast<internal::RootView*>(widget.GetRootView());
331 root_view->SetEventTargeter(make_scoped_ptr(targeter));
333 // The coordinates used for SetBounds() are in the parent coordinate space.
334 View masked_view, unmasked_view, masked_child;
335 masked_view.SetBounds(0, 0, 200, 200);
336 unmasked_view.SetBounds(300, 0, 300, 300);
337 masked_child.SetBounds(0, 0, 100, 100);
338 root_view->AddChildView(&masked_view);
339 root_view->AddChildView(&unmasked_view);
340 unmasked_view.AddChildView(&masked_child);
342 // Install event targeters of type TestMaskedViewTargeter on the two masked
343 // views to define their hit test masks.
344 ui::EventTargeter* masked_targeter = new TestMaskedViewTargeter(&masked_view);
345 masked_view.SetEventTargeter(make_scoped_ptr(masked_targeter));
346 masked_targeter = new TestMaskedViewTargeter(&masked_child);
347 masked_child.SetEventTargeter(make_scoped_ptr(masked_targeter));
349 // Note that the coordinates used below are in the coordinate space of
350 // the root view.
352 // Event located within the hit test mask of |masked_view|.
353 ui::ScrollEvent scroll(ui::ET_SCROLL,
354 gfx::Point(100, 190),
355 ui::EventTimeForNow(),
362 ui::EventTarget* current_target =
363 targeter->FindTargetForEvent(root_view, &scroll);
364 EXPECT_EQ(&masked_view, static_cast<View*>(current_target));
366 // Event located outside the hit test mask of |masked_view|.
367 scroll.set_location(gfx::Point(10, 10));
368 current_target = targeter->FindTargetForEvent(root_view, &scroll);
369 EXPECT_EQ(root_view, static_cast<View*>(current_target));
371 // Event located within the hit test mask of |masked_child|.
372 scroll.set_location(gfx::Point(350, 3));
373 current_target = targeter->FindTargetForEvent(root_view, &scroll);
374 EXPECT_EQ(&masked_child, static_cast<View*>(current_target));
376 // Event located within the hit test mask of |masked_child|.
377 scroll.set_location(gfx::Point(300, 12));
378 current_target = targeter->FindTargetForEvent(root_view, &scroll);
379 EXPECT_EQ(&unmasked_view, static_cast<View*>(current_target));
381 // TODO(tdanderson): We should also test that targeting of masked views
382 // works correctly with gestures. See crbug.com/375822.
385 } // namespace test
386 } // namespace views