OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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. | |
4 | |
5 #include "base/logging.h" | |
6 #include "base/string16.h" | |
7 #include "base/string_number_conversions.h" | |
8 #include "base/utf_string_conversions.h" | |
9 #include "third_party/skia/include/core/SkColor.h" | |
10 #include "ui/base/keycodes/keyboard_codes.h" | |
11 #include "ui/base/models/accelerator.h" | |
12 #include "ui/base/models/combobox_model.h" | |
13 #include "ui/gfx/rect.h" | |
14 #include "ui/views/test/views_test_base.h" | |
15 #include "ui/views/window/non_client_view.h" | |
16 #include "views/background.h" | |
17 #include "views/border.h" | |
18 #include "views/controls/button/checkbox.h" | |
19 #include "views/controls/button/radio_button.h" | |
20 #include "views/controls/combobox/combobox.h" | |
21 #include "views/controls/combobox/native_combobox_wrapper.h" | |
22 #include "views/controls/label.h" | |
23 #include "views/controls/link.h" | |
24 #include "views/controls/native/native_view_host.h" | |
25 #include "views/controls/scroll_view.h" | |
26 #include "views/controls/tabbed_pane/native_tabbed_pane_wrapper.h" | |
27 #include "views/controls/tabbed_pane/tabbed_pane.h" | |
28 #include "views/controls/textfield/textfield.h" | |
29 #include "views/focus/accelerator_handler.h" | |
30 #include "views/focus/focus_manager_factory.h" | |
31 #include "views/widget/root_view.h" | |
32 #include "views/widget/widget.h" | |
33 #include "views/widget/widget_delegate.h" | |
34 | |
35 #if defined(OS_LINUX) | |
36 #include "ui/base/keycodes/keyboard_code_conversion_gtk.h" | |
37 #endif | |
38 | |
39 namespace { | |
40 const int kWindowWidth = 600; | |
41 const int kWindowHeight = 500; | |
42 | |
43 int count = 1; | |
44 | |
45 const int kTopCheckBoxID = count++; // 1 | |
46 const int kLeftContainerID = count++; | |
47 const int kAppleLabelID = count++; | |
48 const int kAppleTextfieldID = count++; | |
49 const int kOrangeLabelID = count++; // 5 | |
50 const int kOrangeTextfieldID = count++; | |
51 const int kBananaLabelID = count++; | |
52 const int kBananaTextfieldID = count++; | |
53 const int kKiwiLabelID = count++; | |
54 const int kKiwiTextfieldID = count++; // 10 | |
55 const int kFruitButtonID = count++; | |
56 const int kFruitCheckBoxID = count++; | |
57 const int kComboboxID = count++; | |
58 | |
59 const int kRightContainerID = count++; | |
60 const int kAsparagusButtonID = count++; // 15 | |
61 const int kBroccoliButtonID = count++; | |
62 const int kCauliflowerButtonID = count++; | |
63 | |
64 const int kInnerContainerID = count++; | |
65 const int kScrollViewID = count++; | |
66 const int kRosettaLinkID = count++; // 20 | |
67 const int kStupeurEtTremblementLinkID = count++; | |
68 const int kDinerGameLinkID = count++; | |
69 const int kRidiculeLinkID = count++; | |
70 const int kClosetLinkID = count++; | |
71 const int kVisitingLinkID = count++; // 25 | |
72 const int kAmelieLinkID = count++; | |
73 const int kJoyeuxNoelLinkID = count++; | |
74 const int kCampingLinkID = count++; | |
75 const int kBriceDeNiceLinkID = count++; | |
76 const int kTaxiLinkID = count++; // 30 | |
77 const int kAsterixLinkID = count++; | |
78 | |
79 const int kOKButtonID = count++; | |
80 const int kCancelButtonID = count++; | |
81 const int kHelpButtonID = count++; | |
82 | |
83 const int kStyleContainerID = count++; // 35 | |
84 const int kBoldCheckBoxID = count++; | |
85 const int kItalicCheckBoxID = count++; | |
86 const int kUnderlinedCheckBoxID = count++; | |
87 const int kStyleHelpLinkID = count++; | |
88 const int kStyleTextEditID = count++; // 40 | |
89 | |
90 const int kSearchContainerID = count++; | |
91 const int kSearchTextfieldID = count++; | |
92 const int kSearchButtonID = count++; | |
93 const int kHelpLinkID = count++; | |
94 | |
95 const int kThumbnailContainerID = count++; // 45 | |
96 const int kThumbnailStarID = count++; | |
97 const int kThumbnailSuperStarID = count++; | |
98 | |
99 } // namespace | |
100 | |
101 namespace views { | |
102 | |
103 class FocusManagerTest : public ViewsTestBase, public WidgetDelegate { | |
104 public: | |
105 FocusManagerTest() | |
106 : window_(NULL), | |
107 content_view_(NULL), | |
108 focus_change_listener_(NULL) { | |
109 } | |
110 | |
111 ~FocusManagerTest() { | |
112 } | |
113 | |
114 virtual void SetUp() OVERRIDE { | |
115 ViewsTestBase::SetUp(); | |
116 window_ = Widget::CreateWindowWithBounds(this, bounds()); | |
117 InitContentView(); | |
118 window_->Show(); | |
119 } | |
120 | |
121 virtual void TearDown() OVERRIDE { | |
122 if (focus_change_listener_) | |
123 GetFocusManager()->RemoveFocusChangeListener(focus_change_listener_); | |
124 window_->Close(); | |
125 | |
126 // Flush the message loop to make application verifiers happy. | |
127 RunPendingMessages(); | |
128 ViewsTestBase::TearDown(); | |
129 } | |
130 | |
131 FocusManager* GetFocusManager() { | |
132 return window_->GetFocusManager(); | |
133 } | |
134 | |
135 void FocusNativeView(gfx::NativeView native_view) { | |
136 #if defined(USE_AURA) | |
137 NOTIMPLEMENTED(); | |
138 #elif defined(OS_WIN) | |
139 ::SendMessage(native_view, WM_SETFOCUS, NULL, NULL); | |
140 #else | |
141 gint return_val; | |
142 GdkEventFocus event; | |
143 event.type = GDK_FOCUS_CHANGE; | |
144 event.window = | |
145 gtk_widget_get_root_window(GTK_WIDGET(window_->GetNativeWindow())); | |
146 event.send_event = TRUE; | |
147 event.in = TRUE; | |
148 gtk_signal_emit_by_name(GTK_OBJECT(native_view), "focus-in-event", | |
149 &event, &return_val); | |
150 #endif | |
151 } | |
152 | |
153 // WidgetDelegate Implementation. | |
154 virtual View* GetContentsView() OVERRIDE { | |
155 if (!content_view_) | |
156 content_view_ = new View(); | |
157 return content_view_; | |
158 } | |
159 virtual Widget* GetWidget() OVERRIDE { | |
160 return content_view_->GetWidget(); | |
161 } | |
162 virtual const Widget* GetWidget() const OVERRIDE { | |
163 return content_view_->GetWidget(); | |
164 } | |
165 | |
166 virtual void InitContentView() { | |
167 } | |
168 | |
169 protected: | |
170 virtual gfx::Rect bounds() { | |
171 return gfx::Rect(0, 0, 500, 500); | |
172 } | |
173 | |
174 // Mocks activating/deactivating the window. | |
175 void SimulateActivateWindow() { | |
176 #if defined(USE_AURA) | |
177 NOTIMPLEMENTED(); | |
178 #elif defined(OS_WIN) | |
179 ::SendMessage(window_->GetNativeWindow(), WM_ACTIVATE, WA_ACTIVE, NULL); | |
180 #else | |
181 gboolean result; | |
182 g_signal_emit_by_name(G_OBJECT(window_->GetNativeWindow()), | |
183 "focus_in_event", 0, &result); | |
184 #endif | |
185 } | |
186 void SimulateDeactivateWindow() { | |
187 #if defined(USE_AURA) | |
188 NOTIMPLEMENTED(); | |
189 #elif defined(OS_WIN) | |
190 ::SendMessage(window_->GetNativeWindow(), WM_ACTIVATE, WA_INACTIVE, NULL); | |
191 #else | |
192 gboolean result; | |
193 g_signal_emit_by_name(G_OBJECT(window_->GetNativeWindow()), | |
194 "focus_out_event", 0, & result); | |
195 #endif | |
196 } | |
197 | |
198 Widget* window_; | |
199 View* content_view_; | |
200 | |
201 void AddFocusChangeListener(FocusChangeListener* listener) { | |
202 ASSERT_FALSE(focus_change_listener_); | |
203 focus_change_listener_ = listener; | |
204 GetFocusManager()->AddFocusChangeListener(listener); | |
205 } | |
206 | |
207 #if defined(USE_AURA) | |
208 void PostKeyDown(ui::KeyboardCode key_code) { | |
209 NOTIMPLEMENTED(); | |
210 } | |
211 | |
212 void PostKeyUp(ui::KeyboardCode key_code) { | |
213 NOTIMPLEMENTED(); | |
214 } | |
215 #elif defined(OS_WIN) | |
216 void PostKeyDown(ui::KeyboardCode key_code) { | |
217 ::PostMessage(window_->GetNativeWindow(), WM_KEYDOWN, key_code, 0); | |
218 } | |
219 | |
220 void PostKeyUp(ui::KeyboardCode key_code) { | |
221 ::PostMessage(window_->GetNativeWindow(), WM_KEYUP, key_code, 0); | |
222 } | |
223 #elif defined(OS_LINUX) | |
224 void PostKeyDown(ui::KeyboardCode key_code) { | |
225 PostKeyEvent(key_code, true); | |
226 } | |
227 | |
228 void PostKeyUp(ui::KeyboardCode key_code) { | |
229 PostKeyEvent(key_code, false); | |
230 } | |
231 | |
232 void PostKeyEvent(ui::KeyboardCode key_code, bool pressed) { | |
233 int keyval = GdkKeyCodeForWindowsKeyCode(key_code, false); | |
234 GdkKeymapKey* keys; | |
235 gint n_keys; | |
236 gdk_keymap_get_entries_for_keyval( | |
237 gdk_keymap_get_default(), | |
238 keyval, | |
239 &keys, | |
240 &n_keys); | |
241 GdkEvent* event = gdk_event_new(pressed ? GDK_KEY_PRESS : GDK_KEY_RELEASE); | |
242 GdkEventKey* key_event = reinterpret_cast<GdkEventKey*>(event); | |
243 int modifier = 0; | |
244 if (pressed) | |
245 key_event->state = modifier | GDK_KEY_PRESS_MASK; | |
246 else | |
247 key_event->state = modifier | GDK_KEY_RELEASE_MASK; | |
248 | |
249 key_event->window = GTK_WIDGET(window_->GetNativeWindow())->window; | |
250 DCHECK(key_event->window != NULL); | |
251 g_object_ref(key_event->window); | |
252 key_event->send_event = true; | |
253 key_event->time = GDK_CURRENT_TIME; | |
254 key_event->keyval = keyval; | |
255 key_event->hardware_keycode = keys[0].keycode; | |
256 key_event->group = keys[0].group; | |
257 | |
258 g_free(keys); | |
259 | |
260 gdk_event_put(event); | |
261 gdk_event_free(event); | |
262 } | |
263 #endif | |
264 | |
265 private: | |
266 FocusChangeListener* focus_change_listener_; | |
267 | |
268 DISALLOW_COPY_AND_ASSIGN(FocusManagerTest); | |
269 }; | |
270 | |
271 // BorderView is a view containing a native window with its own view hierarchy. | |
272 // It is interesting to test focus traversal from a view hierarchy to an inner | |
273 // view hierarchy. | |
274 class BorderView : public NativeViewHost { | |
275 public: | |
276 explicit BorderView(View* child) : child_(child), widget_(NULL) { | |
277 DCHECK(child); | |
278 set_focusable(false); | |
279 } | |
280 | |
281 virtual ~BorderView() {} | |
282 | |
283 virtual internal::RootView* GetContentsRootView() { | |
284 return static_cast<internal::RootView*>(widget_->GetRootView()); | |
285 } | |
286 | |
287 virtual FocusTraversable* GetFocusTraversable() { | |
288 return static_cast<internal::RootView*>(widget_->GetRootView()); | |
289 } | |
290 | |
291 virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child) { | |
292 NativeViewHost::ViewHierarchyChanged(is_add, parent, child); | |
293 | |
294 if (child == this && is_add) { | |
295 if (!widget_) { | |
296 widget_ = new Widget; | |
297 Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); | |
298 #if defined(OS_WIN) | |
299 params.parent = parent->GetWidget()->GetNativeView(); | |
300 #elif defined(TOOLKIT_USES_GTK) | |
301 params.parent = native_view(); | |
302 #else | |
303 NOTREACHED(); | |
304 #endif | |
305 widget_->Init(params); | |
306 widget_->SetFocusTraversableParentView(this); | |
307 widget_->SetContentsView(child_); | |
308 } | |
309 | |
310 // We have been added to a view hierarchy, attach the native view. | |
311 Attach(widget_->GetNativeView()); | |
312 // Also update the FocusTraversable parent so the focus traversal works. | |
313 static_cast<internal::RootView*>(widget_->GetRootView())-> | |
314 SetFocusTraversableParent(GetWidget()->GetFocusTraversable()); | |
315 } | |
316 } | |
317 | |
318 private: | |
319 View* child_; | |
320 Widget* widget_; | |
321 | |
322 DISALLOW_COPY_AND_ASSIGN(BorderView); | |
323 }; | |
324 | |
325 class DummyComboboxModel : public ui::ComboboxModel { | |
326 public: | |
327 virtual int GetItemCount() { return 10; } | |
328 | |
329 virtual string16 GetItemAt(int index) { | |
330 return ASCIIToUTF16("Item ") + base::IntToString16(index); | |
331 } | |
332 }; | |
333 | |
334 // A View that can act as a pane. | |
335 class PaneView : public View, public FocusTraversable { | |
336 public: | |
337 PaneView() : focus_search_(NULL) {} | |
338 | |
339 // If this method is called, this view will use GetPaneFocusTraversable to | |
340 // have this provided FocusSearch used instead of the default one, allowing | |
341 // you to trap focus within the pane. | |
342 void EnablePaneFocus(FocusSearch* focus_search) { | |
343 focus_search_ = focus_search; | |
344 } | |
345 | |
346 // Overridden from views::View: | |
347 virtual FocusTraversable* GetPaneFocusTraversable() { | |
348 if (focus_search_) | |
349 return this; | |
350 else | |
351 return NULL; | |
352 } | |
353 | |
354 // Overridden from views::FocusTraversable: | |
355 virtual views::FocusSearch* GetFocusSearch() { | |
356 return focus_search_; | |
357 } | |
358 virtual FocusTraversable* GetFocusTraversableParent() { | |
359 return NULL; | |
360 } | |
361 virtual View* GetFocusTraversableParentView() { | |
362 return NULL; | |
363 } | |
364 | |
365 private: | |
366 FocusSearch* focus_search_; | |
367 }; | |
368 | |
369 class FocusTraversalTest : public FocusManagerTest { | |
370 public: | |
371 ~FocusTraversalTest(); | |
372 | |
373 virtual void InitContentView(); | |
374 | |
375 protected: | |
376 FocusTraversalTest(); | |
377 | |
378 virtual gfx::Rect bounds() { | |
379 return gfx::Rect(0, 0, 600, 460); | |
380 } | |
381 | |
382 View* FindViewByID(int id) { | |
383 View* view = GetContentsView()->GetViewByID(id); | |
384 if (view) | |
385 return view; | |
386 if (style_tab_) | |
387 view = style_tab_->GetSelectedTab()->GetViewByID(id); | |
388 if (view) | |
389 return view; | |
390 view = search_border_view_->GetContentsRootView()->GetViewByID(id); | |
391 if (view) | |
392 return view; | |
393 return NULL; | |
394 } | |
395 | |
396 protected: | |
397 TabbedPane* style_tab_; | |
398 BorderView* search_border_view_; | |
399 DummyComboboxModel combobox_model_; | |
400 PaneView* left_container_; | |
401 PaneView* right_container_; | |
402 | |
403 DISALLOW_COPY_AND_ASSIGN(FocusTraversalTest); | |
404 }; | |
405 | |
406 //////////////////////////////////////////////////////////////////////////////// | |
407 // FocusTraversalTest | |
408 //////////////////////////////////////////////////////////////////////////////// | |
409 | |
410 FocusTraversalTest::FocusTraversalTest() | |
411 : style_tab_(NULL), | |
412 search_border_view_(NULL) { | |
413 } | |
414 | |
415 FocusTraversalTest::~FocusTraversalTest() { | |
416 } | |
417 | |
418 void FocusTraversalTest::InitContentView() { | |
419 // Create a complicated view hierarchy with lots of control types for | |
420 // use by all of the focus traversal tests. | |
421 // | |
422 // Class name, ID, and asterisk next to focusable views: | |
423 // | |
424 // View | |
425 // Checkbox * kTopCheckBoxID | |
426 // PaneView kLeftContainerID | |
427 // Label kAppleLabelID | |
428 // Textfield * kAppleTextfieldID | |
429 // Label kOrangeLabelID | |
430 // Textfield * kOrangeTextfieldID | |
431 // Label kBananaLabelID | |
432 // Textfield * kBananaTextfieldID | |
433 // Label kKiwiLabelID | |
434 // Textfield * kKiwiTextfieldID | |
435 // NativeButton * kFruitButtonID | |
436 // Checkbox * kFruitCheckBoxID | |
437 // Combobox * kComboboxID | |
438 // PaneView kRightContainerID | |
439 // RadioButton * kAsparagusButtonID | |
440 // RadioButton * kBroccoliButtonID | |
441 // RadioButton * kCauliflowerButtonID | |
442 // View kInnerContainerID | |
443 // ScrollView kScrollViewID | |
444 // View | |
445 // Link * kRosettaLinkID | |
446 // Link * kStupeurEtTremblementLinkID | |
447 // Link * kDinerGameLinkID | |
448 // Link * kRidiculeLinkID | |
449 // Link * kClosetLinkID | |
450 // Link * kVisitingLinkID | |
451 // Link * kAmelieLinkID | |
452 // Link * kJoyeuxNoelLinkID | |
453 // Link * kCampingLinkID | |
454 // Link * kBriceDeNiceLinkID | |
455 // Link * kTaxiLinkID | |
456 // Link * kAsterixLinkID | |
457 // NativeButton * kOKButtonID | |
458 // NativeButton * kCancelButtonID | |
459 // NativeButton * kHelpButtonID | |
460 // TabbedPane * kStyleContainerID | |
461 // View | |
462 // Checkbox * kBoldCheckBoxID | |
463 // Checkbox * kItalicCheckBoxID | |
464 // Checkbox * kUnderlinedCheckBoxID | |
465 // Link * kStyleHelpLinkID | |
466 // Textfield * kStyleTextEditID | |
467 // Other | |
468 // BorderView kSearchContainerID | |
469 // View | |
470 // Textfield * kSearchTextfieldID | |
471 // NativeButton * kSearchButtonID | |
472 // Link * kHelpLinkID | |
473 // View * kThumbnailContainerID | |
474 // NativeButton * kThumbnailStarID | |
475 // NativeButton * kThumbnailSuperStarID | |
476 | |
477 content_view_->set_background( | |
478 Background::CreateSolidBackground(SK_ColorWHITE)); | |
479 | |
480 Checkbox* cb = new Checkbox(ASCIIToUTF16("This is a checkbox")); | |
481 content_view_->AddChildView(cb); | |
482 // In this fast paced world, who really has time for non hard-coded layout? | |
483 cb->SetBounds(10, 10, 200, 20); | |
484 cb->set_id(kTopCheckBoxID); | |
485 | |
486 left_container_ = new PaneView(); | |
487 left_container_->set_border(Border::CreateSolidBorder(1, SK_ColorBLACK)); | |
488 left_container_->set_background( | |
489 Background::CreateSolidBackground(240, 240, 240)); | |
490 left_container_->set_id(kLeftContainerID); | |
491 content_view_->AddChildView(left_container_); | |
492 left_container_->SetBounds(10, 35, 250, 200); | |
493 | |
494 int label_x = 5; | |
495 int label_width = 50; | |
496 int label_height = 15; | |
497 int text_field_width = 150; | |
498 int y = 10; | |
499 int gap_between_labels = 10; | |
500 | |
501 Label* label = new Label(ASCIIToUTF16("Apple:")); | |
502 label->set_id(kAppleLabelID); | |
503 left_container_->AddChildView(label); | |
504 label->SetBounds(label_x, y, label_width, label_height); | |
505 | |
506 Textfield* text_field = new Textfield(); | |
507 text_field->set_id(kAppleTextfieldID); | |
508 left_container_->AddChildView(text_field); | |
509 text_field->SetBounds(label_x + label_width + 5, y, | |
510 text_field_width, label_height); | |
511 | |
512 y += label_height + gap_between_labels; | |
513 | |
514 label = new Label(ASCIIToUTF16("Orange:")); | |
515 label->set_id(kOrangeLabelID); | |
516 left_container_->AddChildView(label); | |
517 label->SetBounds(label_x, y, label_width, label_height); | |
518 | |
519 text_field = new Textfield(); | |
520 text_field->set_id(kOrangeTextfieldID); | |
521 left_container_->AddChildView(text_field); | |
522 text_field->SetBounds(label_x + label_width + 5, y, | |
523 text_field_width, label_height); | |
524 | |
525 y += label_height + gap_between_labels; | |
526 | |
527 label = new Label(ASCIIToUTF16("Banana:")); | |
528 label->set_id(kBananaLabelID); | |
529 left_container_->AddChildView(label); | |
530 label->SetBounds(label_x, y, label_width, label_height); | |
531 | |
532 text_field = new Textfield(); | |
533 text_field->set_id(kBananaTextfieldID); | |
534 left_container_->AddChildView(text_field); | |
535 text_field->SetBounds(label_x + label_width + 5, y, | |
536 text_field_width, label_height); | |
537 | |
538 y += label_height + gap_between_labels; | |
539 | |
540 label = new Label(ASCIIToUTF16("Kiwi:")); | |
541 label->set_id(kKiwiLabelID); | |
542 left_container_->AddChildView(label); | |
543 label->SetBounds(label_x, y, label_width, label_height); | |
544 | |
545 text_field = new Textfield(); | |
546 text_field->set_id(kKiwiTextfieldID); | |
547 left_container_->AddChildView(text_field); | |
548 text_field->SetBounds(label_x + label_width + 5, y, | |
549 text_field_width, label_height); | |
550 | |
551 y += label_height + gap_between_labels; | |
552 | |
553 NativeTextButton* button = new NativeTextButton(NULL, | |
554 ASCIIToUTF16("Click me")); | |
555 button->SetBounds(label_x, y + 10, 80, 30); | |
556 button->set_id(kFruitButtonID); | |
557 left_container_->AddChildView(button); | |
558 y += 40; | |
559 | |
560 cb = new Checkbox(ASCIIToUTF16("This is another check box")); | |
561 cb->SetBounds(label_x + label_width + 5, y, 180, 20); | |
562 cb->set_id(kFruitCheckBoxID); | |
563 left_container_->AddChildView(cb); | |
564 y += 20; | |
565 | |
566 Combobox* combobox = new Combobox(&combobox_model_); | |
567 combobox->SetBounds(label_x + label_width + 5, y, 150, 30); | |
568 combobox->set_id(kComboboxID); | |
569 left_container_->AddChildView(combobox); | |
570 | |
571 right_container_ = new PaneView(); | |
572 right_container_->set_border(Border::CreateSolidBorder(1, SK_ColorBLACK)); | |
573 right_container_->set_background( | |
574 Background::CreateSolidBackground(240, 240, 240)); | |
575 right_container_->set_id(kRightContainerID); | |
576 content_view_->AddChildView(right_container_); | |
577 right_container_->SetBounds(270, 35, 300, 200); | |
578 | |
579 y = 10; | |
580 int radio_button_height = 18; | |
581 int gap_between_radio_buttons = 10; | |
582 RadioButton* radio_button = new RadioButton(ASCIIToUTF16("Asparagus"), 1); | |
583 radio_button->set_id(kAsparagusButtonID); | |
584 right_container_->AddChildView(radio_button); | |
585 radio_button->SetBounds(5, y, 70, radio_button_height); | |
586 radio_button->SetGroup(1); | |
587 y += radio_button_height + gap_between_radio_buttons; | |
588 radio_button = new RadioButton(ASCIIToUTF16("Broccoli"), 1); | |
589 radio_button->set_id(kBroccoliButtonID); | |
590 right_container_->AddChildView(radio_button); | |
591 radio_button->SetBounds(5, y, 70, radio_button_height); | |
592 radio_button->SetGroup(1); | |
593 RadioButton* radio_button_to_check = radio_button; | |
594 y += radio_button_height + gap_between_radio_buttons; | |
595 radio_button = new RadioButton(ASCIIToUTF16("Cauliflower"), 1); | |
596 radio_button->set_id(kCauliflowerButtonID); | |
597 right_container_->AddChildView(radio_button); | |
598 radio_button->SetBounds(5, y, 70, radio_button_height); | |
599 radio_button->SetGroup(1); | |
600 y += radio_button_height + gap_between_radio_buttons; | |
601 | |
602 View* inner_container = new View(); | |
603 inner_container->set_border(Border::CreateSolidBorder(1, SK_ColorBLACK)); | |
604 inner_container->set_background( | |
605 Background::CreateSolidBackground(230, 230, 230)); | |
606 inner_container->set_id(kInnerContainerID); | |
607 right_container_->AddChildView(inner_container); | |
608 inner_container->SetBounds(100, 10, 150, 180); | |
609 | |
610 ScrollView* scroll_view = new ScrollView(); | |
611 scroll_view->set_id(kScrollViewID); | |
612 inner_container->AddChildView(scroll_view); | |
613 scroll_view->SetBounds(1, 1, 148, 178); | |
614 | |
615 View* scroll_content = new View(); | |
616 scroll_content->SetBounds(0, 0, 200, 200); | |
617 scroll_content->set_background( | |
618 Background::CreateSolidBackground(200, 200, 200)); | |
619 scroll_view->SetContents(scroll_content); | |
620 | |
621 static const char* const kTitles[] = { | |
622 "Rosetta", "Stupeur et tremblement", "The diner game", | |
623 "Ridicule", "Le placard", "Les Visiteurs", "Amelie", | |
624 "Joyeux Noel", "Camping", "Brice de Nice", | |
625 "Taxi", "Asterix" | |
626 }; | |
627 | |
628 static const int kIDs[] = { | |
629 kRosettaLinkID, kStupeurEtTremblementLinkID, kDinerGameLinkID, | |
630 kRidiculeLinkID, kClosetLinkID, kVisitingLinkID, kAmelieLinkID, | |
631 kJoyeuxNoelLinkID, kCampingLinkID, kBriceDeNiceLinkID, | |
632 kTaxiLinkID, kAsterixLinkID | |
633 }; | |
634 | |
635 DCHECK(arraysize(kTitles) == arraysize(kIDs)); | |
636 | |
637 y = 5; | |
638 for (size_t i = 0; i < arraysize(kTitles); ++i) { | |
639 Link* link = new Link(ASCIIToUTF16(kTitles[i])); | |
640 link->SetHorizontalAlignment(Label::ALIGN_LEFT); | |
641 link->set_id(kIDs[i]); | |
642 scroll_content->AddChildView(link); | |
643 link->SetBounds(5, y, 300, 15); | |
644 y += 15; | |
645 } | |
646 | |
647 y = 250; | |
648 int width = 60; | |
649 button = new NativeTextButton(NULL, ASCIIToUTF16("OK")); | |
650 button->set_id(kOKButtonID); | |
651 button->SetIsDefault(true); | |
652 | |
653 content_view_->AddChildView(button); | |
654 button->SetBounds(150, y, width, 30); | |
655 | |
656 button = new NativeTextButton(NULL, ASCIIToUTF16("Cancel")); | |
657 button->set_id(kCancelButtonID); | |
658 content_view_->AddChildView(button); | |
659 button->SetBounds(220, y, width, 30); | |
660 | |
661 button = new NativeTextButton(NULL, ASCIIToUTF16("Help")); | |
662 button->set_id(kHelpButtonID); | |
663 content_view_->AddChildView(button); | |
664 button->SetBounds(290, y, width, 30); | |
665 | |
666 y += 40; | |
667 | |
668 // Left bottom box with style checkboxes. | |
669 View* contents = new View(); | |
670 contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE)); | |
671 cb = new Checkbox(ASCIIToUTF16("Bold")); | |
672 contents->AddChildView(cb); | |
673 cb->SetBounds(10, 10, 50, 20); | |
674 cb->set_id(kBoldCheckBoxID); | |
675 | |
676 cb = new Checkbox(ASCIIToUTF16("Italic")); | |
677 contents->AddChildView(cb); | |
678 cb->SetBounds(70, 10, 50, 20); | |
679 cb->set_id(kItalicCheckBoxID); | |
680 | |
681 cb = new Checkbox(ASCIIToUTF16("Underlined")); | |
682 contents->AddChildView(cb); | |
683 cb->SetBounds(130, 10, 70, 20); | |
684 cb->set_id(kUnderlinedCheckBoxID); | |
685 | |
686 Link* link = new Link(ASCIIToUTF16("Help")); | |
687 contents->AddChildView(link); | |
688 link->SetBounds(10, 35, 70, 10); | |
689 link->set_id(kStyleHelpLinkID); | |
690 | |
691 text_field = new Textfield(); | |
692 contents->AddChildView(text_field); | |
693 text_field->SetBounds(10, 50, 100, 20); | |
694 text_field->set_id(kStyleTextEditID); | |
695 | |
696 style_tab_ = new TabbedPane(); | |
697 style_tab_->set_id(kStyleContainerID); | |
698 content_view_->AddChildView(style_tab_); | |
699 style_tab_->SetBounds(10, y, 210, 100); | |
700 style_tab_->AddTab(ASCIIToUTF16("Style"), contents); | |
701 style_tab_->AddTab(ASCIIToUTF16("Other"), new View()); | |
702 | |
703 // Right bottom box with search. | |
704 contents = new View(); | |
705 contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE)); | |
706 text_field = new Textfield(); | |
707 contents->AddChildView(text_field); | |
708 text_field->SetBounds(10, 10, 100, 20); | |
709 text_field->set_id(kSearchTextfieldID); | |
710 | |
711 button = new NativeTextButton(NULL, ASCIIToUTF16("Search")); | |
712 contents->AddChildView(button); | |
713 button->SetBounds(112, 5, 60, 30); | |
714 button->set_id(kSearchButtonID); | |
715 | |
716 link = new Link(ASCIIToUTF16("Help")); | |
717 link->SetHorizontalAlignment(Label::ALIGN_LEFT); | |
718 link->set_id(kHelpLinkID); | |
719 contents->AddChildView(link); | |
720 link->SetBounds(175, 10, 30, 20); | |
721 | |
722 search_border_view_ = new BorderView(contents); | |
723 search_border_view_->set_id(kSearchContainerID); | |
724 | |
725 content_view_->AddChildView(search_border_view_); | |
726 search_border_view_->SetBounds(300, y, 240, 50); | |
727 | |
728 y += 60; | |
729 | |
730 contents = new View(); | |
731 contents->set_focusable(true); | |
732 contents->set_background(Background::CreateSolidBackground(SK_ColorBLUE)); | |
733 contents->set_id(kThumbnailContainerID); | |
734 button = new NativeTextButton(NULL, ASCIIToUTF16("Star")); | |
735 contents->AddChildView(button); | |
736 button->SetBounds(5, 5, 50, 30); | |
737 button->set_id(kThumbnailStarID); | |
738 button = new NativeTextButton(NULL, ASCIIToUTF16("SuperStar")); | |
739 contents->AddChildView(button); | |
740 button->SetBounds(60, 5, 100, 30); | |
741 button->set_id(kThumbnailSuperStarID); | |
742 | |
743 content_view_->AddChildView(contents); | |
744 contents->SetBounds(250, y, 200, 50); | |
745 // We can only call RadioButton::SetChecked() on the radio-button is part of | |
746 // the view hierarchy. | |
747 radio_button_to_check->SetChecked(true); | |
748 } | |
749 | |
750 //////////////////////////////////////////////////////////////////////////////// | |
751 // The tests | |
752 //////////////////////////////////////////////////////////////////////////////// | |
753 | |
754 enum FocusTestEventType { | |
755 ON_FOCUS = 0, | |
756 ON_BLUR | |
757 }; | |
758 | |
759 struct FocusTestEvent { | |
760 FocusTestEvent(FocusTestEventType type, int view_id) | |
761 : type(type), | |
762 view_id(view_id) { | |
763 } | |
764 | |
765 FocusTestEventType type; | |
766 int view_id; | |
767 }; | |
768 | |
769 class SimpleTestView : public View { | |
770 public: | |
771 SimpleTestView(std::vector<FocusTestEvent>* event_list, int view_id) | |
772 : event_list_(event_list) { | |
773 set_focusable(true); | |
774 set_id(view_id); | |
775 } | |
776 | |
777 virtual void OnFocus() { | |
778 event_list_->push_back(FocusTestEvent(ON_FOCUS, id())); | |
779 } | |
780 | |
781 virtual void OnBlur() { | |
782 event_list_->push_back(FocusTestEvent(ON_BLUR, id())); | |
783 } | |
784 | |
785 private: | |
786 std::vector<FocusTestEvent>* event_list_; | |
787 }; | |
788 | |
789 // Tests that the appropriate Focus related methods are called when a View | |
790 // gets/loses focus. | |
791 TEST_F(FocusManagerTest, ViewFocusCallbacks) { | |
792 std::vector<FocusTestEvent> event_list; | |
793 const int kView1ID = 1; | |
794 const int kView2ID = 2; | |
795 | |
796 SimpleTestView* view1 = new SimpleTestView(&event_list, kView1ID); | |
797 SimpleTestView* view2 = new SimpleTestView(&event_list, kView2ID); | |
798 content_view_->AddChildView(view1); | |
799 content_view_->AddChildView(view2); | |
800 | |
801 view1->RequestFocus(); | |
802 ASSERT_EQ(1, static_cast<int>(event_list.size())); | |
803 EXPECT_EQ(ON_FOCUS, event_list[0].type); | |
804 EXPECT_EQ(kView1ID, event_list[0].view_id); | |
805 | |
806 event_list.clear(); | |
807 view2->RequestFocus(); | |
808 ASSERT_EQ(2, static_cast<int>(event_list.size())); | |
809 EXPECT_EQ(ON_BLUR, event_list[0].type); | |
810 EXPECT_EQ(kView1ID, event_list[0].view_id); | |
811 EXPECT_EQ(ON_FOCUS, event_list[1].type); | |
812 EXPECT_EQ(kView2ID, event_list[1].view_id); | |
813 | |
814 event_list.clear(); | |
815 GetFocusManager()->ClearFocus(); | |
816 ASSERT_EQ(1, static_cast<int>(event_list.size())); | |
817 EXPECT_EQ(ON_BLUR, event_list[0].type); | |
818 EXPECT_EQ(kView2ID, event_list[0].view_id); | |
819 } | |
820 | |
821 typedef std::pair<View*, View*> ViewPair; | |
822 class TestFocusChangeListener : public FocusChangeListener { | |
823 public: | |
824 virtual void OnWillChangeFocus(View* focused_before, View* focused_now) { | |
825 focus_changes_.push_back(ViewPair(focused_before, focused_now)); | |
826 } | |
827 virtual void OnDidChangeFocus(View* focused_before, View* focused_now) { | |
828 } | |
829 | |
830 const std::vector<ViewPair>& focus_changes() const { return focus_changes_; } | |
831 void ClearFocusChanges() { focus_changes_.clear(); } | |
832 | |
833 private: | |
834 // A vector of which views lost/gained focus. | |
835 std::vector<ViewPair> focus_changes_; | |
836 }; | |
837 | |
838 TEST_F(FocusManagerTest, FocusChangeListener) { | |
839 View* view1 = new View(); | |
840 view1->set_focusable(true); | |
841 View* view2 = new View(); | |
842 view2->set_focusable(true); | |
843 content_view_->AddChildView(view1); | |
844 content_view_->AddChildView(view2); | |
845 | |
846 TestFocusChangeListener listener; | |
847 AddFocusChangeListener(&listener); | |
848 | |
849 // Visual Studio 2010 has problems converting NULL to the null pointer for | |
850 // std::pair. See http://connect.microsoft.com/VisualStudio/feedback/details/
520043/error-converting-from-null-to-a-pointer-type-in-std-pair | |
851 // It will work if we pass nullptr. | |
852 #if defined(_MSC_VER) && _MSC_VER >= 1600 | |
853 views::View* null_view = nullptr; | |
854 #else | |
855 views::View* null_view = NULL; | |
856 #endif | |
857 | |
858 view1->RequestFocus(); | |
859 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size())); | |
860 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view1)); | |
861 listener.ClearFocusChanges(); | |
862 | |
863 view2->RequestFocus(); | |
864 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size())); | |
865 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(view1, view2)); | |
866 listener.ClearFocusChanges(); | |
867 | |
868 GetFocusManager()->ClearFocus(); | |
869 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size())); | |
870 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(view2, null_view)); | |
871 } | |
872 | |
873 class TestNativeButton : public NativeTextButton { | |
874 public: | |
875 explicit TestNativeButton(const string16& text) | |
876 : NativeTextButton(NULL, text) { | |
877 }; | |
878 virtual gfx::NativeView TestGetNativeControlView() { | |
879 return GetWidget()->GetNativeView(); | |
880 } | |
881 }; | |
882 | |
883 class TestCheckbox : public Checkbox { | |
884 public: | |
885 explicit TestCheckbox(const string16& text) : Checkbox(text) { | |
886 }; | |
887 virtual gfx::NativeView TestGetNativeControlView() { | |
888 return GetWidget()->GetNativeView(); | |
889 } | |
890 }; | |
891 | |
892 class TestRadioButton : public RadioButton { | |
893 public: | |
894 explicit TestRadioButton(const string16& text) | |
895 : RadioButton(text, 1) { | |
896 } | |
897 virtual gfx::NativeView TestGetNativeControlView() { | |
898 return GetWidget()->GetNativeView(); | |
899 } | |
900 }; | |
901 | |
902 class TestTextfield : public Textfield { | |
903 public: | |
904 TestTextfield() { } | |
905 virtual gfx::NativeView TestGetNativeControlView() { | |
906 return native_wrapper_->GetTestingHandle(); | |
907 } | |
908 }; | |
909 | |
910 class TestCombobox : public Combobox, public ui::ComboboxModel { | |
911 public: | |
912 TestCombobox() : Combobox(this) { } | |
913 virtual gfx::NativeView TestGetNativeControlView() { | |
914 return native_wrapper_->GetTestingHandle(); | |
915 } | |
916 virtual int GetItemCount() { | |
917 return 10; | |
918 } | |
919 virtual string16 GetItemAt(int index) { | |
920 return ASCIIToUTF16("Hello combo"); | |
921 } | |
922 }; | |
923 | |
924 class TestTabbedPane : public TabbedPane { | |
925 public: | |
926 TestTabbedPane() { } | |
927 virtual gfx::NativeView TestGetNativeControlView() { | |
928 return native_tabbed_pane_->GetTestingHandle(); | |
929 } | |
930 }; | |
931 | |
932 #if !defined(TOUCH_UI) | |
933 // TODO(oshima): replace TOUCH_UI with PURE_VIEWS | |
934 | |
935 // Tests that NativeControls do set the focus View appropriately on the | |
936 // FocusManager. | |
937 TEST_F(FocusManagerTest, FAILS_FocusNativeControls) { | |
938 TestTextfield* textfield = new TestTextfield(); | |
939 TestTabbedPane* tabbed_pane = new TestTabbedPane(); | |
940 TestTextfield* textfield2 = new TestTextfield(); | |
941 | |
942 content_view_->AddChildView(textfield); | |
943 content_view_->AddChildView(tabbed_pane); | |
944 | |
945 tabbed_pane->AddTab(ASCIIToUTF16("Awesome textfield"), textfield2); | |
946 | |
947 // Simulate the native view getting the native focus (such as by user click). | |
948 FocusNativeView(textfield->TestGetNativeControlView()); | |
949 EXPECT_EQ(textfield, GetFocusManager()->GetFocusedView()); | |
950 | |
951 FocusNativeView(tabbed_pane->TestGetNativeControlView()); | |
952 EXPECT_EQ(tabbed_pane, GetFocusManager()->GetFocusedView()); | |
953 | |
954 FocusNativeView(textfield2->TestGetNativeControlView()); | |
955 EXPECT_EQ(textfield2, GetFocusManager()->GetFocusedView()); | |
956 } | |
957 #endif | |
958 | |
959 // On linux, we don't store/restore focused view because gtk handles | |
960 // this (and pure views will be the same). | |
961 #if defined(OS_WIN) | |
962 | |
963 // Test that when activating/deactivating the top window, the focus is stored/ | |
964 // restored properly. | |
965 TEST_F(FocusManagerTest, FocusStoreRestore) { | |
966 // Simulate an activate, otherwise the deactivate isn't going to do anything. | |
967 SimulateActivateWindow(); | |
968 | |
969 NativeTextButton* button = new NativeTextButton(NULL, | |
970 ASCIIToUTF16("Press me")); | |
971 View* view = new View(); | |
972 view->set_focusable(true); | |
973 | |
974 content_view_->AddChildView(button); | |
975 button->SetBounds(10, 10, 200, 30); | |
976 content_view_->AddChildView(view); | |
977 RunPendingMessages(); | |
978 | |
979 TestFocusChangeListener listener; | |
980 AddFocusChangeListener(&listener); | |
981 | |
982 view->RequestFocus(); | |
983 RunPendingMessages(); | |
984 // MessageLoopForUI::current()->RunWithDispatcher(new AcceleratorHandler()); | |
985 | |
986 // Visual Studio 2010 has problems converting NULL to the null pointer for | |
987 // std::pair. See http://connect.microsoft.com/VisualStudio/feedback/details/
520043/error-converting-from-null-to-a-pointer-type-in-std-pair | |
988 // It will work if we pass nullptr. | |
989 #if defined(_MSC_VER) && _MSC_VER >= 1600 | |
990 views::View* null_view = nullptr; | |
991 #else | |
992 views::View* null_view = NULL; | |
993 #endif | |
994 | |
995 // Deacivate the window, it should store its focus. | |
996 SimulateDeactivateWindow(); | |
997 EXPECT_EQ(NULL, GetFocusManager()->GetFocusedView()); | |
998 ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size())); | |
999 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view)); | |
1000 EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(view, null_view)); | |
1001 listener.ClearFocusChanges(); | |
1002 | |
1003 // Reactivate, focus should come-back to the previously focused view. | |
1004 SimulateActivateWindow(); | |
1005 EXPECT_EQ(view, GetFocusManager()->GetFocusedView()); | |
1006 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size())); | |
1007 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view)); | |
1008 listener.ClearFocusChanges(); | |
1009 | |
1010 // Same test with a NativeControl. | |
1011 button->RequestFocus(); | |
1012 SimulateDeactivateWindow(); | |
1013 EXPECT_EQ(NULL, GetFocusManager()->GetFocusedView()); | |
1014 ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size())); | |
1015 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(view, button)); | |
1016 EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(button, null_view)); | |
1017 listener.ClearFocusChanges(); | |
1018 | |
1019 SimulateActivateWindow(); | |
1020 EXPECT_EQ(button, GetFocusManager()->GetFocusedView()); | |
1021 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size())); | |
1022 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, button)); | |
1023 listener.ClearFocusChanges(); | |
1024 | |
1025 /* | |
1026 // Now test that while the window is inactive we can change the focused view | |
1027 // (we do that in several places). | |
1028 SimulateDeactivateWindow(); | |
1029 // TODO: would have to mock the window being inactive (with a TestWidgetWin | |
1030 // that would return false on IsActive()). | |
1031 GetFocusManager()->SetFocusedView(view); | |
1032 ::SendMessage(window_->GetNativeWindow(), WM_ACTIVATE, WA_ACTIVE, NULL); | |
1033 | |
1034 EXPECT_EQ(view, GetFocusManager()->GetFocusedView()); | |
1035 ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size())); | |
1036 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(button, null_view)); | |
1037 EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(null_view, view)); | |
1038 */ | |
1039 } | |
1040 #endif | |
1041 | |
1042 #if !defined(TOUCH_UI) | |
1043 // TODO(oshima): There is no tabbed pane in pure views. Replace it | |
1044 // with different implementation. | |
1045 | |
1046 TEST_F(FocusManagerTest, ContainsView) { | |
1047 View* view = new View(); | |
1048 scoped_ptr<View> detached_view(new View()); | |
1049 TabbedPane* tabbed_pane = new TabbedPane(); | |
1050 TabbedPane* nested_tabbed_pane = new TabbedPane(); | |
1051 NativeTextButton* tab_button = new NativeTextButton( | |
1052 NULL, ASCIIToUTF16("tab button")); | |
1053 | |
1054 content_view_->AddChildView(view); | |
1055 content_view_->AddChildView(tabbed_pane); | |
1056 // Adding a View inside a TabbedPane to test the case of nested root view. | |
1057 | |
1058 tabbed_pane->AddTab(ASCIIToUTF16("Awesome tab"), nested_tabbed_pane); | |
1059 nested_tabbed_pane->AddTab(ASCIIToUTF16("Awesomer tab"), tab_button); | |
1060 | |
1061 EXPECT_TRUE(GetFocusManager()->ContainsView(view)); | |
1062 EXPECT_TRUE(GetFocusManager()->ContainsView(tabbed_pane)); | |
1063 EXPECT_TRUE(GetFocusManager()->ContainsView(nested_tabbed_pane)); | |
1064 EXPECT_TRUE(GetFocusManager()->ContainsView(tab_button)); | |
1065 EXPECT_FALSE(GetFocusManager()->ContainsView(detached_view.get())); | |
1066 } | |
1067 #endif | |
1068 | |
1069 TEST_F(FocusTraversalTest, NormalTraversal) { | |
1070 const int kTraversalIDs[] = { kTopCheckBoxID, kAppleTextfieldID, | |
1071 kOrangeTextfieldID, kBananaTextfieldID, kKiwiTextfieldID, | |
1072 kFruitButtonID, kFruitCheckBoxID, kComboboxID, kBroccoliButtonID, | |
1073 kRosettaLinkID, kStupeurEtTremblementLinkID, | |
1074 kDinerGameLinkID, kRidiculeLinkID, kClosetLinkID, kVisitingLinkID, | |
1075 kAmelieLinkID, kJoyeuxNoelLinkID, kCampingLinkID, kBriceDeNiceLinkID, | |
1076 kTaxiLinkID, kAsterixLinkID, kOKButtonID, kCancelButtonID, kHelpButtonID, | |
1077 kStyleContainerID, kBoldCheckBoxID, kItalicCheckBoxID, | |
1078 kUnderlinedCheckBoxID, kStyleHelpLinkID, kStyleTextEditID, | |
1079 kSearchTextfieldID, kSearchButtonID, kHelpLinkID, | |
1080 kThumbnailContainerID, kThumbnailStarID, kThumbnailSuperStarID }; | |
1081 | |
1082 // Uncomment the following line if you want to test manually the UI of this | |
1083 // test. | |
1084 // MessageLoopForUI::current()->RunWithDispatcher(new AcceleratorHandler()); | |
1085 | |
1086 // Let's traverse the whole focus hierarchy (several times, to make sure it | |
1087 // loops OK). | |
1088 GetFocusManager()->ClearFocus(); | |
1089 for (int i = 0; i < 3; ++i) { | |
1090 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) { | |
1091 GetFocusManager()->AdvanceFocus(false); | |
1092 View* focused_view = GetFocusManager()->GetFocusedView(); | |
1093 EXPECT_TRUE(focused_view != NULL); | |
1094 if (focused_view) | |
1095 EXPECT_EQ(kTraversalIDs[j], focused_view->id()); | |
1096 } | |
1097 } | |
1098 | |
1099 // Let's traverse in reverse order. | |
1100 GetFocusManager()->ClearFocus(); | |
1101 for (int i = 0; i < 3; ++i) { | |
1102 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) { | |
1103 GetFocusManager()->AdvanceFocus(true); | |
1104 View* focused_view = GetFocusManager()->GetFocusedView(); | |
1105 EXPECT_TRUE(focused_view != NULL); | |
1106 if (focused_view) | |
1107 EXPECT_EQ(kTraversalIDs[j], focused_view->id()); | |
1108 } | |
1109 } | |
1110 } | |
1111 | |
1112 TEST_F(FocusTraversalTest, TraversalWithNonEnabledViews) { | |
1113 const int kDisabledIDs[] = { | |
1114 kBananaTextfieldID, kFruitCheckBoxID, kComboboxID, kAsparagusButtonID, | |
1115 kCauliflowerButtonID, kClosetLinkID, kVisitingLinkID, kBriceDeNiceLinkID, | |
1116 kTaxiLinkID, kAsterixLinkID, kHelpButtonID, kBoldCheckBoxID, | |
1117 kSearchTextfieldID, kHelpLinkID }; | |
1118 | |
1119 const int kTraversalIDs[] = { kTopCheckBoxID, kAppleTextfieldID, | |
1120 kOrangeTextfieldID, kKiwiTextfieldID, kFruitButtonID, kBroccoliButtonID, | |
1121 kRosettaLinkID, kStupeurEtTremblementLinkID, kDinerGameLinkID, | |
1122 kRidiculeLinkID, kAmelieLinkID, kJoyeuxNoelLinkID, kCampingLinkID, | |
1123 kOKButtonID, kCancelButtonID, kStyleContainerID, kItalicCheckBoxID, | |
1124 kUnderlinedCheckBoxID, kStyleHelpLinkID, kStyleTextEditID, | |
1125 kSearchButtonID, kThumbnailContainerID, kThumbnailStarID, | |
1126 kThumbnailSuperStarID }; | |
1127 | |
1128 // Let's disable some views. | |
1129 for (size_t i = 0; i < arraysize(kDisabledIDs); i++) { | |
1130 View* v = FindViewByID(kDisabledIDs[i]); | |
1131 ASSERT_TRUE(v != NULL); | |
1132 v->SetEnabled(false); | |
1133 } | |
1134 | |
1135 // Uncomment the following line if you want to test manually the UI of this | |
1136 // test. | |
1137 // MessageLoopForUI::current()->RunWithDispatcher(new AcceleratorHandler()); | |
1138 | |
1139 View* focused_view; | |
1140 // Let's do one traversal (several times, to make sure it loops ok). | |
1141 GetFocusManager()->ClearFocus(); | |
1142 for (int i = 0; i < 3; ++i) { | |
1143 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) { | |
1144 GetFocusManager()->AdvanceFocus(false); | |
1145 focused_view = GetFocusManager()->GetFocusedView(); | |
1146 EXPECT_TRUE(focused_view != NULL); | |
1147 if (focused_view) | |
1148 EXPECT_EQ(kTraversalIDs[j], focused_view->id()); | |
1149 } | |
1150 } | |
1151 | |
1152 // Same thing in reverse. | |
1153 GetFocusManager()->ClearFocus(); | |
1154 for (int i = 0; i < 3; ++i) { | |
1155 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) { | |
1156 GetFocusManager()->AdvanceFocus(true); | |
1157 focused_view = GetFocusManager()->GetFocusedView(); | |
1158 EXPECT_TRUE(focused_view != NULL); | |
1159 if (focused_view) | |
1160 EXPECT_EQ(kTraversalIDs[j], focused_view->id()); | |
1161 } | |
1162 } | |
1163 } | |
1164 | |
1165 TEST_F(FocusTraversalTest, TraversalWithInvisibleViews) { | |
1166 const int kInvisibleIDs[] = { kTopCheckBoxID, kOKButtonID, | |
1167 kThumbnailContainerID }; | |
1168 | |
1169 const int kTraversalIDs[] = { kAppleTextfieldID, kOrangeTextfieldID, | |
1170 kBananaTextfieldID, kKiwiTextfieldID, kFruitButtonID, kFruitCheckBoxID, | |
1171 kComboboxID, kBroccoliButtonID, kRosettaLinkID, | |
1172 kStupeurEtTremblementLinkID, kDinerGameLinkID, kRidiculeLinkID, | |
1173 kClosetLinkID, kVisitingLinkID, kAmelieLinkID, kJoyeuxNoelLinkID, | |
1174 kCampingLinkID, kBriceDeNiceLinkID, kTaxiLinkID, kAsterixLinkID, | |
1175 kCancelButtonID, kHelpButtonID, kStyleContainerID, kBoldCheckBoxID, | |
1176 kItalicCheckBoxID, kUnderlinedCheckBoxID, kStyleHelpLinkID, | |
1177 kStyleTextEditID, kSearchTextfieldID, kSearchButtonID, kHelpLinkID }; | |
1178 | |
1179 | |
1180 // Let's make some views invisible. | |
1181 for (size_t i = 0; i < arraysize(kInvisibleIDs); i++) { | |
1182 View* v = FindViewByID(kInvisibleIDs[i]); | |
1183 ASSERT_TRUE(v != NULL); | |
1184 v->SetVisible(false); | |
1185 } | |
1186 | |
1187 // Uncomment the following line if you want to test manually the UI of this | |
1188 // test. | |
1189 // MessageLoopForUI::current()->RunWithDispatcher(new AcceleratorHandler()); | |
1190 | |
1191 View* focused_view; | |
1192 // Let's do one traversal (several times, to make sure it loops ok). | |
1193 GetFocusManager()->ClearFocus(); | |
1194 for (int i = 0; i < 3; ++i) { | |
1195 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) { | |
1196 GetFocusManager()->AdvanceFocus(false); | |
1197 focused_view = GetFocusManager()->GetFocusedView(); | |
1198 EXPECT_TRUE(focused_view != NULL); | |
1199 if (focused_view) | |
1200 EXPECT_EQ(kTraversalIDs[j], focused_view->id()); | |
1201 } | |
1202 } | |
1203 | |
1204 // Same thing in reverse. | |
1205 GetFocusManager()->ClearFocus(); | |
1206 for (int i = 0; i < 3; ++i) { | |
1207 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) { | |
1208 GetFocusManager()->AdvanceFocus(true); | |
1209 focused_view = GetFocusManager()->GetFocusedView(); | |
1210 EXPECT_TRUE(focused_view != NULL); | |
1211 if (focused_view) | |
1212 EXPECT_EQ(kTraversalIDs[j], focused_view->id()); | |
1213 } | |
1214 } | |
1215 } | |
1216 | |
1217 TEST_F(FocusTraversalTest, PaneTraversal) { | |
1218 // Tests trapping the traversal within a pane - useful for full | |
1219 // keyboard accessibility for toolbars. | |
1220 | |
1221 // First test the left container. | |
1222 const int kLeftTraversalIDs[] = { | |
1223 kAppleTextfieldID, | |
1224 kOrangeTextfieldID, kBananaTextfieldID, kKiwiTextfieldID, | |
1225 kFruitButtonID, kFruitCheckBoxID, kComboboxID }; | |
1226 | |
1227 FocusSearch focus_search_left(left_container_, true, false); | |
1228 left_container_->EnablePaneFocus(&focus_search_left); | |
1229 FindViewByID(kComboboxID)->RequestFocus(); | |
1230 | |
1231 // Traverse the focus hierarchy within the pane several times. | |
1232 for (int i = 0; i < 3; ++i) { | |
1233 for (size_t j = 0; j < arraysize(kLeftTraversalIDs); j++) { | |
1234 GetFocusManager()->AdvanceFocus(false); | |
1235 View* focused_view = GetFocusManager()->GetFocusedView(); | |
1236 EXPECT_TRUE(focused_view != NULL); | |
1237 if (focused_view) | |
1238 EXPECT_EQ(kLeftTraversalIDs[j], focused_view->id()); | |
1239 } | |
1240 } | |
1241 | |
1242 // Traverse in reverse order. | |
1243 FindViewByID(kAppleTextfieldID)->RequestFocus(); | |
1244 for (int i = 0; i < 3; ++i) { | |
1245 for (int j = arraysize(kLeftTraversalIDs) - 1; j >= 0; --j) { | |
1246 GetFocusManager()->AdvanceFocus(true); | |
1247 View* focused_view = GetFocusManager()->GetFocusedView(); | |
1248 EXPECT_TRUE(focused_view != NULL); | |
1249 if (focused_view) | |
1250 EXPECT_EQ(kLeftTraversalIDs[j], focused_view->id()); | |
1251 } | |
1252 } | |
1253 | |
1254 // Now test the right container, but this time with accessibility mode. | |
1255 // Make some links not focusable, but mark one of them as | |
1256 // "accessibility focusable", so it should show up in the traversal. | |
1257 const int kRightTraversalIDs[] = { | |
1258 kBroccoliButtonID, kDinerGameLinkID, kRidiculeLinkID, | |
1259 kClosetLinkID, kVisitingLinkID, kAmelieLinkID, kJoyeuxNoelLinkID, | |
1260 kCampingLinkID, kBriceDeNiceLinkID, kTaxiLinkID, kAsterixLinkID }; | |
1261 | |
1262 FocusSearch focus_search_right(right_container_, true, true); | |
1263 right_container_->EnablePaneFocus(&focus_search_right); | |
1264 FindViewByID(kRosettaLinkID)->set_focusable(false); | |
1265 FindViewByID(kStupeurEtTremblementLinkID)->set_focusable(false); | |
1266 FindViewByID(kDinerGameLinkID)->set_accessibility_focusable(true); | |
1267 FindViewByID(kDinerGameLinkID)->set_focusable(false); | |
1268 FindViewByID(kAsterixLinkID)->RequestFocus(); | |
1269 | |
1270 // Traverse the focus hierarchy within the pane several times. | |
1271 for (int i = 0; i < 3; ++i) { | |
1272 for (size_t j = 0; j < arraysize(kRightTraversalIDs); j++) { | |
1273 GetFocusManager()->AdvanceFocus(false); | |
1274 View* focused_view = GetFocusManager()->GetFocusedView(); | |
1275 EXPECT_TRUE(focused_view != NULL); | |
1276 if (focused_view) | |
1277 EXPECT_EQ(kRightTraversalIDs[j], focused_view->id()); | |
1278 } | |
1279 } | |
1280 | |
1281 // Traverse in reverse order. | |
1282 FindViewByID(kBroccoliButtonID)->RequestFocus(); | |
1283 for (int i = 0; i < 3; ++i) { | |
1284 for (int j = arraysize(kRightTraversalIDs) - 1; j >= 0; --j) { | |
1285 GetFocusManager()->AdvanceFocus(true); | |
1286 View* focused_view = GetFocusManager()->GetFocusedView(); | |
1287 EXPECT_TRUE(focused_view != NULL); | |
1288 if (focused_view) | |
1289 EXPECT_EQ(kRightTraversalIDs[j], focused_view->id()); | |
1290 } | |
1291 } | |
1292 } | |
1293 | |
1294 // Counts accelerator calls. | |
1295 class TestAcceleratorTarget : public ui::AcceleratorTarget { | |
1296 public: | |
1297 explicit TestAcceleratorTarget(bool process_accelerator) | |
1298 : accelerator_count_(0), process_accelerator_(process_accelerator) {} | |
1299 | |
1300 virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) { | |
1301 ++accelerator_count_; | |
1302 return process_accelerator_; | |
1303 } | |
1304 | |
1305 int accelerator_count() const { return accelerator_count_; } | |
1306 | |
1307 private: | |
1308 int accelerator_count_; // number of times that the accelerator is activated | |
1309 bool process_accelerator_; // return value of AcceleratorPressed | |
1310 | |
1311 DISALLOW_COPY_AND_ASSIGN(TestAcceleratorTarget); | |
1312 }; | |
1313 | |
1314 TEST_F(FocusManagerTest, CallsNormalAcceleratorTarget) { | |
1315 FocusManager* focus_manager = GetFocusManager(); | |
1316 ui::Accelerator return_accelerator(ui::VKEY_RETURN, false, false, false); | |
1317 ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, false, false, false); | |
1318 | |
1319 TestAcceleratorTarget return_target(true); | |
1320 TestAcceleratorTarget escape_target(true); | |
1321 EXPECT_EQ(return_target.accelerator_count(), 0); | |
1322 EXPECT_EQ(escape_target.accelerator_count(), 0); | |
1323 EXPECT_EQ(NULL, | |
1324 focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); | |
1325 EXPECT_EQ(NULL, | |
1326 focus_manager->GetCurrentTargetForAccelerator(escape_accelerator)); | |
1327 | |
1328 // Register targets. | |
1329 focus_manager->RegisterAccelerator(return_accelerator, &return_target); | |
1330 focus_manager->RegisterAccelerator(escape_accelerator, &escape_target); | |
1331 | |
1332 // Checks if the correct target is registered. | |
1333 EXPECT_EQ(&return_target, | |
1334 focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); | |
1335 EXPECT_EQ(&escape_target, | |
1336 focus_manager->GetCurrentTargetForAccelerator(escape_accelerator)); | |
1337 | |
1338 // Hitting the return key. | |
1339 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); | |
1340 EXPECT_EQ(return_target.accelerator_count(), 1); | |
1341 EXPECT_EQ(escape_target.accelerator_count(), 0); | |
1342 | |
1343 // Hitting the escape key. | |
1344 EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator)); | |
1345 EXPECT_EQ(return_target.accelerator_count(), 1); | |
1346 EXPECT_EQ(escape_target.accelerator_count(), 1); | |
1347 | |
1348 // Register another target for the return key. | |
1349 TestAcceleratorTarget return_target2(true); | |
1350 EXPECT_EQ(return_target2.accelerator_count(), 0); | |
1351 focus_manager->RegisterAccelerator(return_accelerator, &return_target2); | |
1352 EXPECT_EQ(&return_target2, | |
1353 focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); | |
1354 | |
1355 // Hitting the return key; return_target2 has the priority. | |
1356 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); | |
1357 EXPECT_EQ(return_target.accelerator_count(), 1); | |
1358 EXPECT_EQ(return_target2.accelerator_count(), 1); | |
1359 | |
1360 // Register a target that does not process the accelerator event. | |
1361 TestAcceleratorTarget return_target3(false); | |
1362 EXPECT_EQ(return_target3.accelerator_count(), 0); | |
1363 focus_manager->RegisterAccelerator(return_accelerator, &return_target3); | |
1364 EXPECT_EQ(&return_target3, | |
1365 focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); | |
1366 | |
1367 // Hitting the return key. | |
1368 // Since the event handler of return_target3 returns false, return_target2 | |
1369 // should be called too. | |
1370 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); | |
1371 EXPECT_EQ(return_target.accelerator_count(), 1); | |
1372 EXPECT_EQ(return_target2.accelerator_count(), 2); | |
1373 EXPECT_EQ(return_target3.accelerator_count(), 1); | |
1374 | |
1375 // Unregister return_target2. | |
1376 focus_manager->UnregisterAccelerator(return_accelerator, &return_target2); | |
1377 EXPECT_EQ(&return_target3, | |
1378 focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); | |
1379 | |
1380 // Hitting the return key. return_target3 and return_target should be called. | |
1381 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); | |
1382 EXPECT_EQ(return_target.accelerator_count(), 2); | |
1383 EXPECT_EQ(return_target2.accelerator_count(), 2); | |
1384 EXPECT_EQ(return_target3.accelerator_count(), 2); | |
1385 | |
1386 // Unregister targets. | |
1387 focus_manager->UnregisterAccelerator(return_accelerator, &return_target); | |
1388 focus_manager->UnregisterAccelerator(return_accelerator, &return_target3); | |
1389 focus_manager->UnregisterAccelerator(escape_accelerator, &escape_target); | |
1390 | |
1391 // Now there is no target registered. | |
1392 EXPECT_EQ(NULL, | |
1393 focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); | |
1394 EXPECT_EQ(NULL, | |
1395 focus_manager->GetCurrentTargetForAccelerator(escape_accelerator)); | |
1396 | |
1397 // Hitting the return key and the escape key. Nothing should happen. | |
1398 EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator)); | |
1399 EXPECT_EQ(return_target.accelerator_count(), 2); | |
1400 EXPECT_EQ(return_target2.accelerator_count(), 2); | |
1401 EXPECT_EQ(return_target3.accelerator_count(), 2); | |
1402 EXPECT_FALSE(focus_manager->ProcessAccelerator(escape_accelerator)); | |
1403 EXPECT_EQ(escape_target.accelerator_count(), 1); | |
1404 } | |
1405 | |
1406 // Unregisters itself when its accelerator is invoked. | |
1407 class SelfUnregisteringAcceleratorTarget : public ui::AcceleratorTarget { | |
1408 public: | |
1409 SelfUnregisteringAcceleratorTarget(ui::Accelerator accelerator, | |
1410 FocusManager* focus_manager) | |
1411 : accelerator_(accelerator), | |
1412 focus_manager_(focus_manager), | |
1413 accelerator_count_(0) { | |
1414 } | |
1415 | |
1416 virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) { | |
1417 ++accelerator_count_; | |
1418 focus_manager_->UnregisterAccelerator(accelerator, this); | |
1419 return true; | |
1420 } | |
1421 | |
1422 int accelerator_count() const { return accelerator_count_; } | |
1423 | |
1424 private: | |
1425 ui::Accelerator accelerator_; | |
1426 FocusManager* focus_manager_; | |
1427 int accelerator_count_; | |
1428 | |
1429 DISALLOW_COPY_AND_ASSIGN(SelfUnregisteringAcceleratorTarget); | |
1430 }; | |
1431 | |
1432 TEST_F(FocusManagerTest, CallsSelfDeletingAcceleratorTarget) { | |
1433 FocusManager* focus_manager = GetFocusManager(); | |
1434 ui::Accelerator return_accelerator(ui::VKEY_RETURN, false, false, false); | |
1435 SelfUnregisteringAcceleratorTarget target(return_accelerator, focus_manager); | |
1436 EXPECT_EQ(target.accelerator_count(), 0); | |
1437 EXPECT_EQ(NULL, | |
1438 focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); | |
1439 | |
1440 // Register the target. | |
1441 focus_manager->RegisterAccelerator(return_accelerator, &target); | |
1442 EXPECT_EQ(&target, | |
1443 focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); | |
1444 | |
1445 // Hitting the return key. The target will be unregistered. | |
1446 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); | |
1447 EXPECT_EQ(target.accelerator_count(), 1); | |
1448 EXPECT_EQ(NULL, | |
1449 focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); | |
1450 | |
1451 // Hitting the return key again; nothing should happen. | |
1452 EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator)); | |
1453 EXPECT_EQ(target.accelerator_count(), 1); | |
1454 } | |
1455 | |
1456 class MessageTrackingView : public View { | |
1457 public: | |
1458 MessageTrackingView() : accelerator_pressed_(false) { | |
1459 } | |
1460 | |
1461 virtual bool OnKeyPressed(const KeyEvent& e) { | |
1462 keys_pressed_.push_back(e.key_code()); | |
1463 return true; | |
1464 } | |
1465 | |
1466 virtual bool OnKeyReleased(const KeyEvent& e) { | |
1467 keys_released_.push_back(e.key_code()); | |
1468 return true; | |
1469 } | |
1470 | |
1471 virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) { | |
1472 accelerator_pressed_ = true; | |
1473 return true; | |
1474 } | |
1475 | |
1476 void Reset() { | |
1477 accelerator_pressed_ = false; | |
1478 keys_pressed_.clear(); | |
1479 keys_released_.clear(); | |
1480 } | |
1481 | |
1482 const std::vector<ui::KeyboardCode>& keys_pressed() const { | |
1483 return keys_pressed_; | |
1484 } | |
1485 | |
1486 const std::vector<ui::KeyboardCode>& keys_released() const { | |
1487 return keys_released_; | |
1488 } | |
1489 | |
1490 bool accelerator_pressed() const { | |
1491 return accelerator_pressed_; | |
1492 } | |
1493 | |
1494 private: | |
1495 bool accelerator_pressed_; | |
1496 std::vector<ui::KeyboardCode> keys_pressed_; | |
1497 std::vector<ui::KeyboardCode> keys_released_; | |
1498 | |
1499 DISALLOW_COPY_AND_ASSIGN(MessageTrackingView); | |
1500 }; | |
1501 | |
1502 #if defined(OS_WIN) | |
1503 // This test is now Windows only. Linux Views port does not handle accelerator | |
1504 // keys in AcceleratorHandler anymore. The logic has been moved into | |
1505 // NativeWidgetGtk::OnKeyEvent(). | |
1506 // Tests that the keyup messages are eaten for accelerators. | |
1507 TEST_F(FocusManagerTest, IgnoreKeyupForAccelerators) { | |
1508 FocusManager* focus_manager = GetFocusManager(); | |
1509 MessageTrackingView* mtv = new MessageTrackingView(); | |
1510 mtv->AddAccelerator(ui::Accelerator(ui::VKEY_0, false, false, false)); | |
1511 mtv->AddAccelerator(ui::Accelerator(ui::VKEY_1, false, false, false)); | |
1512 content_view_->AddChildView(mtv); | |
1513 focus_manager->SetFocusedView(mtv); | |
1514 | |
1515 // First send a non-accelerator key sequence. | |
1516 PostKeyDown(ui::VKEY_9); | |
1517 PostKeyUp(ui::VKEY_9); | |
1518 AcceleratorHandler accelerator_handler; | |
1519 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); | |
1520 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler); | |
1521 // Make sure we get a key-up and key-down. | |
1522 ASSERT_EQ(1U, mtv->keys_pressed().size()); | |
1523 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[0]); | |
1524 ASSERT_EQ(1U, mtv->keys_released().size()); | |
1525 EXPECT_EQ(ui::VKEY_9, mtv->keys_released()[0]); | |
1526 EXPECT_FALSE(mtv->accelerator_pressed()); | |
1527 mtv->Reset(); | |
1528 | |
1529 // Same thing with repeat and more than one key at once. | |
1530 PostKeyDown(ui::VKEY_9); | |
1531 PostKeyDown(ui::VKEY_9); | |
1532 PostKeyDown(ui::VKEY_8); | |
1533 PostKeyDown(ui::VKEY_9); | |
1534 PostKeyDown(ui::VKEY_7); | |
1535 PostKeyUp(ui::VKEY_9); | |
1536 PostKeyUp(ui::VKEY_7); | |
1537 PostKeyUp(ui::VKEY_8); | |
1538 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); | |
1539 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler); | |
1540 // Make sure we get a key-up and key-down. | |
1541 ASSERT_EQ(5U, mtv->keys_pressed().size()); | |
1542 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[0]); | |
1543 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[1]); | |
1544 EXPECT_EQ(ui::VKEY_8, mtv->keys_pressed()[2]); | |
1545 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[3]); | |
1546 EXPECT_EQ(ui::VKEY_7, mtv->keys_pressed()[4]); | |
1547 ASSERT_EQ(3U, mtv->keys_released().size()); | |
1548 EXPECT_EQ(ui::VKEY_9, mtv->keys_released()[0]); | |
1549 EXPECT_EQ(ui::VKEY_7, mtv->keys_released()[1]); | |
1550 EXPECT_EQ(ui::VKEY_8, mtv->keys_released()[2]); | |
1551 EXPECT_FALSE(mtv->accelerator_pressed()); | |
1552 mtv->Reset(); | |
1553 | |
1554 // Now send an accelerator key sequence. | |
1555 PostKeyDown(ui::VKEY_0); | |
1556 PostKeyUp(ui::VKEY_0); | |
1557 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); | |
1558 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler); | |
1559 EXPECT_TRUE(mtv->keys_pressed().empty()); | |
1560 EXPECT_TRUE(mtv->keys_released().empty()); | |
1561 EXPECT_TRUE(mtv->accelerator_pressed()); | |
1562 mtv->Reset(); | |
1563 | |
1564 // Same thing with repeat and more than one key at once. | |
1565 PostKeyDown(ui::VKEY_0); | |
1566 PostKeyDown(ui::VKEY_1); | |
1567 PostKeyDown(ui::VKEY_1); | |
1568 PostKeyDown(ui::VKEY_0); | |
1569 PostKeyDown(ui::VKEY_0); | |
1570 PostKeyUp(ui::VKEY_1); | |
1571 PostKeyUp(ui::VKEY_0); | |
1572 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); | |
1573 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler); | |
1574 EXPECT_TRUE(mtv->keys_pressed().empty()); | |
1575 EXPECT_TRUE(mtv->keys_released().empty()); | |
1576 EXPECT_TRUE(mtv->accelerator_pressed()); | |
1577 mtv->Reset(); | |
1578 } | |
1579 #endif | |
1580 | |
1581 #if defined(OS_WIN) && !defined(USE_AURA) | |
1582 // Test that the focus manager is created successfully for the first view | |
1583 // window parented to a native dialog. | |
1584 TEST_F(FocusManagerTest, CreationForNativeRoot) { | |
1585 // Create a window class. | |
1586 WNDCLASSEX class_ex; | |
1587 memset(&class_ex, 0, sizeof(class_ex)); | |
1588 class_ex.cbSize = sizeof(WNDCLASSEX); | |
1589 class_ex.lpfnWndProc = &DefWindowProc; | |
1590 class_ex.lpszClassName = L"TestWindow"; | |
1591 ATOM atom = RegisterClassEx(&class_ex); | |
1592 ASSERT_TRUE(atom); | |
1593 | |
1594 // Create a native dialog window. | |
1595 HWND hwnd = CreateWindowEx(0, class_ex.lpszClassName, NULL, | |
1596 WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, | |
1597 NULL, NULL, NULL, NULL); | |
1598 ASSERT_TRUE(hwnd); | |
1599 | |
1600 // Create a view window parented to native dialog. | |
1601 scoped_ptr<Widget> widget1(new Widget); | |
1602 Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); | |
1603 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
1604 params.parent = hwnd; | |
1605 params.bounds = gfx::Rect(0, 0, 100, 100); | |
1606 params.top_level = true; // This is top level in views hierarchy. | |
1607 widget1->Init(params); | |
1608 | |
1609 // Get the focus manager directly from the first window. Should exist | |
1610 // because the first window is the root widget. | |
1611 views::FocusManager* focus_manager1 = widget1->GetFocusManager(); | |
1612 EXPECT_TRUE(focus_manager1); | |
1613 | |
1614 // Create another view window parented to the first view window. | |
1615 scoped_ptr<Widget> widget2(new Widget); | |
1616 params.parent = widget1->GetNativeView(); | |
1617 params.top_level = false; // This is child widget. | |
1618 widget2->Init(params); | |
1619 | |
1620 // Access the shared focus manager directly from the second window. | |
1621 views::FocusManager* focus_manager2 = widget2->GetFocusManager(); | |
1622 EXPECT_EQ(focus_manager2, focus_manager1); | |
1623 | |
1624 // Access the shared focus manager indirectly from the first window handle. | |
1625 gfx::NativeWindow native_window = widget1->GetNativeWindow(); | |
1626 views::Widget* widget = | |
1627 views::Widget::GetWidgetForNativeWindow(native_window); | |
1628 EXPECT_EQ(widget->GetFocusManager(), focus_manager1); | |
1629 | |
1630 // Access the shared focus manager indirectly from the second window handle. | |
1631 native_window = widget2->GetNativeWindow(); | |
1632 widget = views::Widget::GetWidgetForNativeWindow(native_window); | |
1633 EXPECT_EQ(widget->GetFocusManager(), focus_manager1); | |
1634 | |
1635 // Access the shared focus manager indirectly from the first view handle. | |
1636 gfx::NativeView native_view = widget1->GetNativeView(); | |
1637 widget = views::Widget::GetTopLevelWidgetForNativeView(native_view); | |
1638 EXPECT_EQ(widget->GetFocusManager(), focus_manager1); | |
1639 | |
1640 // Access the shared focus manager indirectly from the second view handle. | |
1641 native_view = widget2->GetNativeView(); | |
1642 widget = views::Widget::GetTopLevelWidgetForNativeView(native_view); | |
1643 EXPECT_EQ(widget->GetFocusManager(), focus_manager1); | |
1644 | |
1645 DestroyWindow(hwnd); | |
1646 } | |
1647 #endif | |
1648 | |
1649 class FocusManagerDtorTest : public FocusManagerTest { | |
1650 protected: | |
1651 typedef std::vector<std::string> DtorTrackVector; | |
1652 | |
1653 class FocusManagerDtorTracked : public FocusManager { | |
1654 public: | |
1655 FocusManagerDtorTracked(Widget* widget, DtorTrackVector* dtor_tracker) | |
1656 : FocusManager(widget), | |
1657 dtor_tracker_(dtor_tracker) { | |
1658 } | |
1659 | |
1660 virtual ~FocusManagerDtorTracked() { | |
1661 dtor_tracker_->push_back("FocusManagerDtorTracked"); | |
1662 } | |
1663 | |
1664 DtorTrackVector* dtor_tracker_; | |
1665 | |
1666 private: | |
1667 DISALLOW_COPY_AND_ASSIGN(FocusManagerDtorTracked); | |
1668 }; | |
1669 | |
1670 class TestFocusManagerFactory : public FocusManagerFactory { | |
1671 public: | |
1672 explicit TestFocusManagerFactory(DtorTrackVector* dtor_tracker) | |
1673 : dtor_tracker_(dtor_tracker) { | |
1674 } | |
1675 | |
1676 FocusManager* CreateFocusManager(Widget* widget) OVERRIDE { | |
1677 return new FocusManagerDtorTracked(widget, dtor_tracker_); | |
1678 } | |
1679 | |
1680 private: | |
1681 DtorTrackVector* dtor_tracker_; | |
1682 DISALLOW_COPY_AND_ASSIGN(TestFocusManagerFactory); | |
1683 }; | |
1684 | |
1685 class NativeButtonDtorTracked : public NativeTextButton { | |
1686 public: | |
1687 NativeButtonDtorTracked(const string16& text, | |
1688 DtorTrackVector* dtor_tracker) | |
1689 : NativeTextButton(NULL, text), | |
1690 dtor_tracker_(dtor_tracker) { | |
1691 }; | |
1692 virtual ~NativeButtonDtorTracked() { | |
1693 dtor_tracker_->push_back("NativeButtonDtorTracked"); | |
1694 } | |
1695 | |
1696 DtorTrackVector* dtor_tracker_; | |
1697 }; | |
1698 | |
1699 class WindowDtorTracked : public Widget { | |
1700 public: | |
1701 explicit WindowDtorTracked(DtorTrackVector* dtor_tracker) | |
1702 : dtor_tracker_(dtor_tracker) { | |
1703 } | |
1704 | |
1705 virtual ~WindowDtorTracked() { | |
1706 dtor_tracker_->push_back("WindowDtorTracked"); | |
1707 } | |
1708 | |
1709 DtorTrackVector* dtor_tracker_; | |
1710 }; | |
1711 | |
1712 virtual void SetUp() { | |
1713 ViewsTestBase::SetUp(); | |
1714 FocusManagerFactory::Install(new TestFocusManagerFactory(&dtor_tracker_)); | |
1715 // Create WindowDtorTracked that uses FocusManagerDtorTracked. | |
1716 window_ = new WindowDtorTracked(&dtor_tracker_); | |
1717 Widget::InitParams params; | |
1718 params.delegate = this; | |
1719 params.bounds = gfx::Rect(0, 0, 100, 100); | |
1720 window_->Init(params); | |
1721 | |
1722 tracked_focus_manager_ = | |
1723 static_cast<FocusManagerDtorTracked*>(GetFocusManager()); | |
1724 window_->Show(); | |
1725 } | |
1726 | |
1727 virtual void TearDown() { | |
1728 if (window_) { | |
1729 window_->Close(); | |
1730 RunPendingMessages(); | |
1731 } | |
1732 FocusManagerFactory::Install(NULL); | |
1733 ViewsTestBase::TearDown(); | |
1734 } | |
1735 | |
1736 FocusManager* tracked_focus_manager_; | |
1737 DtorTrackVector dtor_tracker_; | |
1738 }; | |
1739 | |
1740 TEST_F(FocusManagerDtorTest, FocusManagerDestructedLast) { | |
1741 // Setup views hierarchy. | |
1742 TabbedPane* tabbed_pane = new TabbedPane(); | |
1743 content_view_->AddChildView(tabbed_pane); | |
1744 | |
1745 NativeButtonDtorTracked* button = new NativeButtonDtorTracked( | |
1746 ASCIIToUTF16("button"), &dtor_tracker_); | |
1747 tabbed_pane->AddTab(ASCIIToUTF16("Awesome tab"), button); | |
1748 | |
1749 // Close the window. | |
1750 window_->Close(); | |
1751 RunPendingMessages(); | |
1752 | |
1753 // Test window, button and focus manager should all be destructed. | |
1754 ASSERT_EQ(3, static_cast<int>(dtor_tracker_.size())); | |
1755 | |
1756 // Focus manager should be the last one to destruct. | |
1757 ASSERT_STREQ("FocusManagerDtorTracked", dtor_tracker_[2].c_str()); | |
1758 | |
1759 // Clear window_ so that we don't try to close it again. | |
1760 window_ = NULL; | |
1761 } | |
1762 | |
1763 } // namespace views | |
OLD | NEW |