Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / views / focus / focus_traversal_unittest.cc
blobea68b160af791dc2d4530eed6b7687d3f0dd0561
1 // Copyright (c) 2012 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/focus/focus_manager.h"
7 #include "base/run_loop.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "ui/base/models/combobox_model.h"
11 #include "ui/views/background.h"
12 #include "ui/views/border.h"
13 #include "ui/views/controls/button/checkbox.h"
14 #include "ui/views/controls/button/label_button.h"
15 #include "ui/views/controls/button/radio_button.h"
16 #include "ui/views/controls/combobox/combobox.h"
17 #include "ui/views/controls/label.h"
18 #include "ui/views/controls/link.h"
19 #include "ui/views/controls/native/native_view_host.h"
20 #include "ui/views/controls/scroll_view.h"
21 #include "ui/views/controls/tabbed_pane/tabbed_pane.h"
22 #include "ui/views/controls/textfield/textfield.h"
23 #include "ui/views/test/focus_manager_test.h"
24 #include "ui/views/widget/root_view.h"
25 #include "ui/views/widget/widget.h"
27 using base::ASCIIToUTF16;
29 namespace views {
31 namespace {
33 int count = 1;
35 const int kTopCheckBoxID = count++; // 1
36 const int kLeftContainerID = count++;
37 const int kAppleLabelID = count++;
38 const int kAppleTextfieldID = count++;
39 const int kOrangeLabelID = count++; // 5
40 const int kOrangeTextfieldID = count++;
41 const int kBananaLabelID = count++;
42 const int kBananaTextfieldID = count++;
43 const int kKiwiLabelID = count++;
44 const int kKiwiTextfieldID = count++; // 10
45 const int kFruitButtonID = count++;
46 const int kFruitCheckBoxID = count++;
47 const int kComboboxID = count++;
49 const int kRightContainerID = count++;
50 const int kAsparagusButtonID = count++; // 15
51 const int kBroccoliButtonID = count++;
52 const int kCauliflowerButtonID = count++;
54 const int kInnerContainerID = count++;
55 const int kScrollViewID = count++;
56 const int kRosettaLinkID = count++; // 20
57 const int kStupeurEtTremblementLinkID = count++;
58 const int kDinerGameLinkID = count++;
59 const int kRidiculeLinkID = count++;
60 const int kClosetLinkID = count++;
61 const int kVisitingLinkID = count++; // 25
62 const int kAmelieLinkID = count++;
63 const int kJoyeuxNoelLinkID = count++;
64 const int kCampingLinkID = count++;
65 const int kBriceDeNiceLinkID = count++;
66 const int kTaxiLinkID = count++; // 30
67 const int kAsterixLinkID = count++;
69 const int kOKButtonID = count++;
70 const int kCancelButtonID = count++;
71 const int kHelpButtonID = count++;
73 const int kStyleContainerID = count++; // 35
74 const int kBoldCheckBoxID = count++;
75 const int kItalicCheckBoxID = count++;
76 const int kUnderlinedCheckBoxID = count++;
77 const int kStyleHelpLinkID = count++;
78 const int kStyleTextEditID = count++; // 40
80 const int kSearchContainerID = count++;
81 const int kSearchTextfieldID = count++;
82 const int kSearchButtonID = count++;
83 const int kHelpLinkID = count++;
85 const int kThumbnailContainerID = count++; // 45
86 const int kThumbnailStarID = count++;
87 const int kThumbnailSuperStarID = count++;
89 class DummyComboboxModel : public ui::ComboboxModel {
90 public:
91 // Overridden from ui::ComboboxModel:
92 virtual int GetItemCount() const OVERRIDE { return 10; }
93 virtual base::string16 GetItemAt(int index) OVERRIDE {
94 return ASCIIToUTF16("Item ") + base::IntToString16(index);
98 // A View that can act as a pane.
99 class PaneView : public View, public FocusTraversable {
100 public:
101 PaneView() : focus_search_(NULL) {}
103 // If this method is called, this view will use GetPaneFocusTraversable to
104 // have this provided FocusSearch used instead of the default one, allowing
105 // you to trap focus within the pane.
106 void EnablePaneFocus(FocusSearch* focus_search) {
107 focus_search_ = focus_search;
110 // Overridden from View:
111 virtual FocusTraversable* GetPaneFocusTraversable() OVERRIDE {
112 if (focus_search_)
113 return this;
114 else
115 return NULL;
118 // Overridden from FocusTraversable:
119 virtual views::FocusSearch* GetFocusSearch() OVERRIDE {
120 return focus_search_;
122 virtual FocusTraversable* GetFocusTraversableParent() OVERRIDE {
123 return NULL;
125 virtual View* GetFocusTraversableParentView() OVERRIDE {
126 return NULL;
129 private:
130 FocusSearch* focus_search_;
133 // BorderView is a view containing a native window with its own view hierarchy.
134 // It is interesting to test focus traversal from a view hierarchy to an inner
135 // view hierarchy.
136 class BorderView : public NativeViewHost {
137 public:
138 explicit BorderView(View* child) : child_(child), widget_(NULL) {
139 DCHECK(child);
140 SetFocusable(false);
143 virtual ~BorderView() {}
145 virtual internal::RootView* GetContentsRootView() {
146 return static_cast<internal::RootView*>(widget_->GetRootView());
149 virtual FocusTraversable* GetFocusTraversable() OVERRIDE {
150 return static_cast<internal::RootView*>(widget_->GetRootView());
153 virtual void ViewHierarchyChanged(
154 const ViewHierarchyChangedDetails& details) OVERRIDE {
155 NativeViewHost::ViewHierarchyChanged(details);
157 if (details.child == this && details.is_add) {
158 if (!widget_) {
159 widget_ = new Widget;
160 Widget::InitParams params(Widget::InitParams::TYPE_CONTROL);
161 params.parent = details.parent->GetWidget()->GetNativeView();
162 widget_->Init(params);
163 widget_->SetFocusTraversableParentView(this);
164 widget_->SetContentsView(child_);
167 // We have been added to a view hierarchy, attach the native view.
168 Attach(widget_->GetNativeView());
169 // Also update the FocusTraversable parent so the focus traversal works.
170 static_cast<internal::RootView*>(widget_->GetRootView())->
171 SetFocusTraversableParent(GetWidget()->GetFocusTraversable());
175 private:
176 View* child_;
177 Widget* widget_;
179 DISALLOW_COPY_AND_ASSIGN(BorderView);
182 } // namespace
184 class FocusTraversalTest : public FocusManagerTest {
185 public:
186 virtual ~FocusTraversalTest();
188 virtual void InitContentView() OVERRIDE;
190 protected:
191 FocusTraversalTest();
193 View* FindViewByID(int id) {
194 View* view = GetContentsView()->GetViewByID(id);
195 if (view)
196 return view;
197 if (style_tab_)
198 view = style_tab_->GetSelectedTab()->GetViewByID(id);
199 if (view)
200 return view;
201 view = search_border_view_->GetContentsRootView()->GetViewByID(id);
202 if (view)
203 return view;
204 return NULL;
207 protected:
208 TabbedPane* style_tab_;
209 BorderView* search_border_view_;
210 DummyComboboxModel combobox_model_;
211 PaneView* left_container_;
212 PaneView* right_container_;
214 DISALLOW_COPY_AND_ASSIGN(FocusTraversalTest);
217 FocusTraversalTest::FocusTraversalTest()
218 : style_tab_(NULL),
219 search_border_view_(NULL) {
222 FocusTraversalTest::~FocusTraversalTest() {
225 void FocusTraversalTest::InitContentView() {
226 // Create a complicated view hierarchy with lots of control types for
227 // use by all of the focus traversal tests.
229 // Class name, ID, and asterisk next to focusable views:
231 // View
232 // Checkbox * kTopCheckBoxID
233 // PaneView kLeftContainerID
234 // Label kAppleLabelID
235 // Textfield * kAppleTextfieldID
236 // Label kOrangeLabelID
237 // Textfield * kOrangeTextfieldID
238 // Label kBananaLabelID
239 // Textfield * kBananaTextfieldID
240 // Label kKiwiLabelID
241 // Textfield * kKiwiTextfieldID
242 // NativeButton * kFruitButtonID
243 // Checkbox * kFruitCheckBoxID
244 // Combobox * kComboboxID
245 // PaneView kRightContainerID
246 // RadioButton * kAsparagusButtonID
247 // RadioButton * kBroccoliButtonID
248 // RadioButton * kCauliflowerButtonID
249 // View kInnerContainerID
250 // ScrollView kScrollViewID
251 // View
252 // Link * kRosettaLinkID
253 // Link * kStupeurEtTremblementLinkID
254 // Link * kDinerGameLinkID
255 // Link * kRidiculeLinkID
256 // Link * kClosetLinkID
257 // Link * kVisitingLinkID
258 // Link * kAmelieLinkID
259 // Link * kJoyeuxNoelLinkID
260 // Link * kCampingLinkID
261 // Link * kBriceDeNiceLinkID
262 // Link * kTaxiLinkID
263 // Link * kAsterixLinkID
264 // NativeButton * kOKButtonID
265 // NativeButton * kCancelButtonID
266 // NativeButton * kHelpButtonID
267 // TabbedPane * kStyleContainerID
268 // View
269 // Checkbox * kBoldCheckBoxID
270 // Checkbox * kItalicCheckBoxID
271 // Checkbox * kUnderlinedCheckBoxID
272 // Link * kStyleHelpLinkID
273 // Textfield * kStyleTextEditID
274 // Other
275 // BorderView kSearchContainerID
276 // View
277 // Textfield * kSearchTextfieldID
278 // NativeButton * kSearchButtonID
279 // Link * kHelpLinkID
280 // View * kThumbnailContainerID
281 // NativeButton * kThumbnailStarID
282 // NativeButton * kThumbnailSuperStarID
284 GetContentsView()->set_background(
285 Background::CreateSolidBackground(SK_ColorWHITE));
287 Checkbox* cb = new Checkbox(ASCIIToUTF16("This is a checkbox"));
288 GetContentsView()->AddChildView(cb);
289 // In this fast paced world, who really has time for non hard-coded layout?
290 cb->SetBounds(10, 10, 200, 20);
291 cb->set_id(kTopCheckBoxID);
293 left_container_ = new PaneView();
294 left_container_->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK));
295 left_container_->set_background(
296 Background::CreateSolidBackground(240, 240, 240));
297 left_container_->set_id(kLeftContainerID);
298 GetContentsView()->AddChildView(left_container_);
299 left_container_->SetBounds(10, 35, 250, 200);
301 int label_x = 5;
302 int label_width = 50;
303 int label_height = 15;
304 int text_field_width = 150;
305 int y = 10;
306 int gap_between_labels = 10;
308 Label* label = new Label(ASCIIToUTF16("Apple:"));
309 label->set_id(kAppleLabelID);
310 left_container_->AddChildView(label);
311 label->SetBounds(label_x, y, label_width, label_height);
313 Textfield* text_field = new Textfield();
314 text_field->set_id(kAppleTextfieldID);
315 left_container_->AddChildView(text_field);
316 text_field->SetBounds(label_x + label_width + 5, y,
317 text_field_width, label_height);
319 y += label_height + gap_between_labels;
321 label = new Label(ASCIIToUTF16("Orange:"));
322 label->set_id(kOrangeLabelID);
323 left_container_->AddChildView(label);
324 label->SetBounds(label_x, y, label_width, label_height);
326 text_field = new Textfield();
327 text_field->set_id(kOrangeTextfieldID);
328 left_container_->AddChildView(text_field);
329 text_field->SetBounds(label_x + label_width + 5, y,
330 text_field_width, label_height);
332 y += label_height + gap_between_labels;
334 label = new Label(ASCIIToUTF16("Banana:"));
335 label->set_id(kBananaLabelID);
336 left_container_->AddChildView(label);
337 label->SetBounds(label_x, y, label_width, label_height);
339 text_field = new Textfield();
340 text_field->set_id(kBananaTextfieldID);
341 left_container_->AddChildView(text_field);
342 text_field->SetBounds(label_x + label_width + 5, y,
343 text_field_width, label_height);
345 y += label_height + gap_between_labels;
347 label = new Label(ASCIIToUTF16("Kiwi:"));
348 label->set_id(kKiwiLabelID);
349 left_container_->AddChildView(label);
350 label->SetBounds(label_x, y, label_width, label_height);
352 text_field = new Textfield();
353 text_field->set_id(kKiwiTextfieldID);
354 left_container_->AddChildView(text_field);
355 text_field->SetBounds(label_x + label_width + 5, y,
356 text_field_width, label_height);
358 y += label_height + gap_between_labels;
360 LabelButton* button = new LabelButton(NULL, ASCIIToUTF16("Click me"));
361 button->SetStyle(Button::STYLE_BUTTON);
362 button->SetBounds(label_x, y + 10, 80, 30);
363 button->set_id(kFruitButtonID);
364 left_container_->AddChildView(button);
365 y += 40;
367 cb = new Checkbox(ASCIIToUTF16("This is another check box"));
368 cb->SetBounds(label_x + label_width + 5, y, 180, 20);
369 cb->set_id(kFruitCheckBoxID);
370 left_container_->AddChildView(cb);
371 y += 20;
373 Combobox* combobox = new Combobox(&combobox_model_);
374 combobox->SetBounds(label_x + label_width + 5, y, 150, 30);
375 combobox->set_id(kComboboxID);
376 left_container_->AddChildView(combobox);
378 right_container_ = new PaneView();
379 right_container_->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK));
380 right_container_->set_background(
381 Background::CreateSolidBackground(240, 240, 240));
382 right_container_->set_id(kRightContainerID);
383 GetContentsView()->AddChildView(right_container_);
384 right_container_->SetBounds(270, 35, 300, 200);
386 y = 10;
387 int radio_button_height = 18;
388 int gap_between_radio_buttons = 10;
389 RadioButton* radio_button = new RadioButton(ASCIIToUTF16("Asparagus"), 1);
390 radio_button->set_id(kAsparagusButtonID);
391 right_container_->AddChildView(radio_button);
392 radio_button->SetBounds(5, y, 70, radio_button_height);
393 radio_button->SetGroup(1);
394 y += radio_button_height + gap_between_radio_buttons;
395 radio_button = new RadioButton(ASCIIToUTF16("Broccoli"), 1);
396 radio_button->set_id(kBroccoliButtonID);
397 right_container_->AddChildView(radio_button);
398 radio_button->SetBounds(5, y, 70, radio_button_height);
399 radio_button->SetGroup(1);
400 RadioButton* radio_button_to_check = radio_button;
401 y += radio_button_height + gap_between_radio_buttons;
402 radio_button = new RadioButton(ASCIIToUTF16("Cauliflower"), 1);
403 radio_button->set_id(kCauliflowerButtonID);
404 right_container_->AddChildView(radio_button);
405 radio_button->SetBounds(5, y, 70, radio_button_height);
406 radio_button->SetGroup(1);
407 y += radio_button_height + gap_between_radio_buttons;
409 View* inner_container = new View();
410 inner_container->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK));
411 inner_container->set_background(
412 Background::CreateSolidBackground(230, 230, 230));
413 inner_container->set_id(kInnerContainerID);
414 right_container_->AddChildView(inner_container);
415 inner_container->SetBounds(100, 10, 150, 180);
417 ScrollView* scroll_view = new ScrollView();
418 scroll_view->set_id(kScrollViewID);
419 inner_container->AddChildView(scroll_view);
420 scroll_view->SetBounds(1, 1, 148, 178);
422 View* scroll_content = new View();
423 scroll_content->SetBounds(0, 0, 200, 200);
424 scroll_content->set_background(
425 Background::CreateSolidBackground(200, 200, 200));
426 scroll_view->SetContents(scroll_content);
428 static const char* const kTitles[] = {
429 "Rosetta", "Stupeur et tremblement", "The diner game",
430 "Ridicule", "Le placard", "Les Visiteurs", "Amelie",
431 "Joyeux Noel", "Camping", "Brice de Nice",
432 "Taxi", "Asterix"
435 static const int kIDs[] = {
436 kRosettaLinkID, kStupeurEtTremblementLinkID, kDinerGameLinkID,
437 kRidiculeLinkID, kClosetLinkID, kVisitingLinkID, kAmelieLinkID,
438 kJoyeuxNoelLinkID, kCampingLinkID, kBriceDeNiceLinkID,
439 kTaxiLinkID, kAsterixLinkID
442 DCHECK(arraysize(kTitles) == arraysize(kIDs));
444 y = 5;
445 for (size_t i = 0; i < arraysize(kTitles); ++i) {
446 Link* link = new Link(ASCIIToUTF16(kTitles[i]));
447 link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
448 link->set_id(kIDs[i]);
449 scroll_content->AddChildView(link);
450 link->SetBounds(5, y, 300, 15);
451 y += 15;
454 y = 250;
455 int width = 60;
456 button = new LabelButton(NULL, ASCIIToUTF16("OK"));
457 button->SetStyle(Button::STYLE_BUTTON);
458 button->set_id(kOKButtonID);
459 button->SetIsDefault(true);
461 GetContentsView()->AddChildView(button);
462 button->SetBounds(150, y, width, 30);
464 button = new LabelButton(NULL, ASCIIToUTF16("Cancel"));
465 button->SetStyle(Button::STYLE_BUTTON);
466 button->set_id(kCancelButtonID);
467 GetContentsView()->AddChildView(button);
468 button->SetBounds(220, y, width, 30);
470 button = new LabelButton(NULL, ASCIIToUTF16("Help"));
471 button->SetStyle(Button::STYLE_BUTTON);
472 button->set_id(kHelpButtonID);
473 GetContentsView()->AddChildView(button);
474 button->SetBounds(290, y, width, 30);
476 y += 40;
478 View* contents = NULL;
479 Link* link = NULL;
481 // Left bottom box with style checkboxes.
482 contents = new View();
483 contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE));
484 cb = new Checkbox(ASCIIToUTF16("Bold"));
485 contents->AddChildView(cb);
486 cb->SetBounds(10, 10, 50, 20);
487 cb->set_id(kBoldCheckBoxID);
489 cb = new Checkbox(ASCIIToUTF16("Italic"));
490 contents->AddChildView(cb);
491 cb->SetBounds(70, 10, 50, 20);
492 cb->set_id(kItalicCheckBoxID);
494 cb = new Checkbox(ASCIIToUTF16("Underlined"));
495 contents->AddChildView(cb);
496 cb->SetBounds(130, 10, 70, 20);
497 cb->set_id(kUnderlinedCheckBoxID);
499 link = new Link(ASCIIToUTF16("Help"));
500 contents->AddChildView(link);
501 link->SetBounds(10, 35, 70, 10);
502 link->set_id(kStyleHelpLinkID);
504 text_field = new Textfield();
505 contents->AddChildView(text_field);
506 text_field->SetBounds(10, 50, 100, 20);
507 text_field->set_id(kStyleTextEditID);
509 style_tab_ = new TabbedPane();
510 style_tab_->set_id(kStyleContainerID);
511 GetContentsView()->AddChildView(style_tab_);
512 style_tab_->SetBounds(10, y, 210, 100);
513 style_tab_->AddTab(ASCIIToUTF16("Style"), contents);
514 style_tab_->AddTab(ASCIIToUTF16("Other"), new View());
516 // Right bottom box with search.
517 contents = new View();
518 contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE));
519 text_field = new Textfield();
520 contents->AddChildView(text_field);
521 text_field->SetBounds(10, 10, 100, 20);
522 text_field->set_id(kSearchTextfieldID);
524 button = new LabelButton(NULL, ASCIIToUTF16("Search"));
525 button->SetStyle(Button::STYLE_BUTTON);
526 contents->AddChildView(button);
527 button->SetBounds(112, 5, 60, 30);
528 button->set_id(kSearchButtonID);
530 link = new Link(ASCIIToUTF16("Help"));
531 link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
532 link->set_id(kHelpLinkID);
533 contents->AddChildView(link);
534 link->SetBounds(175, 10, 30, 20);
536 search_border_view_ = new BorderView(contents);
537 search_border_view_->set_id(kSearchContainerID);
539 GetContentsView()->AddChildView(search_border_view_);
540 search_border_view_->SetBounds(300, y, 240, 50);
542 y += 60;
544 contents = new View();
545 contents->SetFocusable(true);
546 contents->set_background(Background::CreateSolidBackground(SK_ColorBLUE));
547 contents->set_id(kThumbnailContainerID);
548 button = new LabelButton(NULL, ASCIIToUTF16("Star"));
549 button->SetStyle(Button::STYLE_BUTTON);
550 contents->AddChildView(button);
551 button->SetBounds(5, 5, 50, 30);
552 button->set_id(kThumbnailStarID);
553 button = new LabelButton(NULL, ASCIIToUTF16("SuperStar"));
554 button->SetStyle(Button::STYLE_BUTTON);
555 contents->AddChildView(button);
556 button->SetBounds(60, 5, 100, 30);
557 button->set_id(kThumbnailSuperStarID);
559 GetContentsView()->AddChildView(contents);
560 contents->SetBounds(250, y, 200, 50);
561 // We can only call RadioButton::SetChecked() on the radio-button is part of
562 // the view hierarchy.
563 radio_button_to_check->SetChecked(true);
566 TEST_F(FocusTraversalTest, NormalTraversal) {
567 const int kTraversalIDs[] = { kTopCheckBoxID, kAppleTextfieldID,
568 kOrangeTextfieldID, kBananaTextfieldID, kKiwiTextfieldID,
569 kFruitButtonID, kFruitCheckBoxID, kComboboxID, kBroccoliButtonID,
570 kRosettaLinkID, kStupeurEtTremblementLinkID,
571 kDinerGameLinkID, kRidiculeLinkID, kClosetLinkID, kVisitingLinkID,
572 kAmelieLinkID, kJoyeuxNoelLinkID, kCampingLinkID, kBriceDeNiceLinkID,
573 kTaxiLinkID, kAsterixLinkID, kOKButtonID, kCancelButtonID, kHelpButtonID,
574 kStyleContainerID, kBoldCheckBoxID, kItalicCheckBoxID,
575 kUnderlinedCheckBoxID, kStyleHelpLinkID, kStyleTextEditID,
576 kSearchTextfieldID, kSearchButtonID, kHelpLinkID,
577 kThumbnailContainerID, kThumbnailStarID, kThumbnailSuperStarID };
579 // Let's traverse the whole focus hierarchy (several times, to make sure it
580 // loops OK).
581 GetFocusManager()->ClearFocus();
582 for (int i = 0; i < 3; ++i) {
583 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
584 GetFocusManager()->AdvanceFocus(false);
585 View* focused_view = GetFocusManager()->GetFocusedView();
586 EXPECT_TRUE(focused_view != NULL);
587 if (focused_view)
588 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
592 // Let's traverse in reverse order.
593 GetFocusManager()->ClearFocus();
594 for (int i = 0; i < 3; ++i) {
595 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
596 GetFocusManager()->AdvanceFocus(true);
597 View* focused_view = GetFocusManager()->GetFocusedView();
598 EXPECT_TRUE(focused_view != NULL);
599 if (focused_view)
600 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
605 TEST_F(FocusTraversalTest, TraversalWithNonEnabledViews) {
606 const int kDisabledIDs[] = {
607 kBananaTextfieldID, kFruitCheckBoxID, kComboboxID, kAsparagusButtonID,
608 kCauliflowerButtonID, kClosetLinkID, kVisitingLinkID, kBriceDeNiceLinkID,
609 kTaxiLinkID, kAsterixLinkID, kHelpButtonID, kBoldCheckBoxID,
610 kSearchTextfieldID, kHelpLinkID };
612 const int kTraversalIDs[] = { kTopCheckBoxID, kAppleTextfieldID,
613 kOrangeTextfieldID, kKiwiTextfieldID, kFruitButtonID, kBroccoliButtonID,
614 kRosettaLinkID, kStupeurEtTremblementLinkID, kDinerGameLinkID,
615 kRidiculeLinkID, kAmelieLinkID, kJoyeuxNoelLinkID, kCampingLinkID,
616 kOKButtonID, kCancelButtonID, kStyleContainerID, kItalicCheckBoxID,
617 kUnderlinedCheckBoxID, kStyleHelpLinkID, kStyleTextEditID,
618 kSearchButtonID, kThumbnailContainerID, kThumbnailStarID,
619 kThumbnailSuperStarID };
621 // Let's disable some views.
622 for (size_t i = 0; i < arraysize(kDisabledIDs); i++) {
623 View* v = FindViewByID(kDisabledIDs[i]);
624 ASSERT_TRUE(v != NULL);
625 v->SetEnabled(false);
628 View* focused_view;
629 // Let's do one traversal (several times, to make sure it loops ok).
630 GetFocusManager()->ClearFocus();
631 for (int i = 0; i < 3; ++i) {
632 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
633 GetFocusManager()->AdvanceFocus(false);
634 focused_view = GetFocusManager()->GetFocusedView();
635 EXPECT_TRUE(focused_view != NULL);
636 if (focused_view)
637 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
641 // Same thing in reverse.
642 GetFocusManager()->ClearFocus();
643 for (int i = 0; i < 3; ++i) {
644 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
645 GetFocusManager()->AdvanceFocus(true);
646 focused_view = GetFocusManager()->GetFocusedView();
647 EXPECT_TRUE(focused_view != NULL);
648 if (focused_view)
649 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
654 TEST_F(FocusTraversalTest, TraversalWithInvisibleViews) {
655 const int kInvisibleIDs[] = { kTopCheckBoxID, kOKButtonID,
656 kThumbnailContainerID };
658 const int kTraversalIDs[] = { kAppleTextfieldID, kOrangeTextfieldID,
659 kBananaTextfieldID, kKiwiTextfieldID, kFruitButtonID, kFruitCheckBoxID,
660 kComboboxID, kBroccoliButtonID, kRosettaLinkID,
661 kStupeurEtTremblementLinkID, kDinerGameLinkID, kRidiculeLinkID,
662 kClosetLinkID, kVisitingLinkID, kAmelieLinkID, kJoyeuxNoelLinkID,
663 kCampingLinkID, kBriceDeNiceLinkID, kTaxiLinkID, kAsterixLinkID,
664 kCancelButtonID, kHelpButtonID, kStyleContainerID, kBoldCheckBoxID,
665 kItalicCheckBoxID, kUnderlinedCheckBoxID, kStyleHelpLinkID,
666 kStyleTextEditID, kSearchTextfieldID, kSearchButtonID, kHelpLinkID };
669 // Let's make some views invisible.
670 for (size_t i = 0; i < arraysize(kInvisibleIDs); i++) {
671 View* v = FindViewByID(kInvisibleIDs[i]);
672 ASSERT_TRUE(v != NULL);
673 v->SetVisible(false);
676 View* focused_view;
677 // Let's do one traversal (several times, to make sure it loops ok).
678 GetFocusManager()->ClearFocus();
679 for (int i = 0; i < 3; ++i) {
680 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
681 GetFocusManager()->AdvanceFocus(false);
682 focused_view = GetFocusManager()->GetFocusedView();
683 EXPECT_TRUE(focused_view != NULL);
684 if (focused_view)
685 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
689 // Same thing in reverse.
690 GetFocusManager()->ClearFocus();
691 for (int i = 0; i < 3; ++i) {
692 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
693 GetFocusManager()->AdvanceFocus(true);
694 focused_view = GetFocusManager()->GetFocusedView();
695 EXPECT_TRUE(focused_view != NULL);
696 if (focused_view)
697 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
702 TEST_F(FocusTraversalTest, PaneTraversal) {
703 // Tests trapping the traversal within a pane - useful for full
704 // keyboard accessibility for toolbars.
706 // First test the left container.
707 const int kLeftTraversalIDs[] = {
708 kAppleTextfieldID,
709 kOrangeTextfieldID, kBananaTextfieldID, kKiwiTextfieldID,
710 kFruitButtonID, kFruitCheckBoxID, kComboboxID };
712 FocusSearch focus_search_left(left_container_, true, false);
713 left_container_->EnablePaneFocus(&focus_search_left);
714 FindViewByID(kComboboxID)->RequestFocus();
716 // Traverse the focus hierarchy within the pane several times.
717 for (int i = 0; i < 3; ++i) {
718 for (size_t j = 0; j < arraysize(kLeftTraversalIDs); j++) {
719 GetFocusManager()->AdvanceFocus(false);
720 View* focused_view = GetFocusManager()->GetFocusedView();
721 EXPECT_TRUE(focused_view != NULL);
722 if (focused_view)
723 EXPECT_EQ(kLeftTraversalIDs[j], focused_view->id());
727 // Traverse in reverse order.
728 FindViewByID(kAppleTextfieldID)->RequestFocus();
729 for (int i = 0; i < 3; ++i) {
730 for (int j = arraysize(kLeftTraversalIDs) - 1; j >= 0; --j) {
731 GetFocusManager()->AdvanceFocus(true);
732 View* focused_view = GetFocusManager()->GetFocusedView();
733 EXPECT_TRUE(focused_view != NULL);
734 if (focused_view)
735 EXPECT_EQ(kLeftTraversalIDs[j], focused_view->id());
739 // Now test the right container, but this time with accessibility mode.
740 // Make some links not focusable, but mark one of them as
741 // "accessibility focusable", so it should show up in the traversal.
742 const int kRightTraversalIDs[] = {
743 kBroccoliButtonID, kDinerGameLinkID, kRidiculeLinkID,
744 kClosetLinkID, kVisitingLinkID, kAmelieLinkID, kJoyeuxNoelLinkID,
745 kCampingLinkID, kBriceDeNiceLinkID, kTaxiLinkID, kAsterixLinkID };
747 FocusSearch focus_search_right(right_container_, true, true);
748 right_container_->EnablePaneFocus(&focus_search_right);
749 FindViewByID(kRosettaLinkID)->SetFocusable(false);
750 FindViewByID(kStupeurEtTremblementLinkID)->SetFocusable(false);
751 FindViewByID(kDinerGameLinkID)->SetAccessibilityFocusable(true);
752 FindViewByID(kDinerGameLinkID)->SetFocusable(false);
753 FindViewByID(kAsterixLinkID)->RequestFocus();
755 // Traverse the focus hierarchy within the pane several times.
756 for (int i = 0; i < 3; ++i) {
757 for (size_t j = 0; j < arraysize(kRightTraversalIDs); j++) {
758 GetFocusManager()->AdvanceFocus(false);
759 View* focused_view = GetFocusManager()->GetFocusedView();
760 EXPECT_TRUE(focused_view != NULL);
761 if (focused_view)
762 EXPECT_EQ(kRightTraversalIDs[j], focused_view->id());
766 // Traverse in reverse order.
767 FindViewByID(kBroccoliButtonID)->RequestFocus();
768 for (int i = 0; i < 3; ++i) {
769 for (int j = arraysize(kRightTraversalIDs) - 1; j >= 0; --j) {
770 GetFocusManager()->AdvanceFocus(true);
771 View* focused_view = GetFocusManager()->GetFocusedView();
772 EXPECT_TRUE(focused_view != NULL);
773 if (focused_view)
774 EXPECT_EQ(kRightTraversalIDs[j], focused_view->id());
779 } // namespace views