Workaround for xkbcommon dead keys.
[chromium-blink-merge.git] / ui / views / focus / focus_traversal_unittest.cc
blobcd24bd505a4632d9f61e19eb9267d97f892a0e5c
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 int GetItemCount() const override { return 10; }
93 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 FocusTraversable* GetPaneFocusTraversable() override {
112 if (focus_search_)
113 return this;
114 else
115 return NULL;
118 // Overridden from FocusTraversable:
119 views::FocusSearch* GetFocusSearch() override { return focus_search_; }
120 FocusTraversable* GetFocusTraversableParent() override { return NULL; }
121 View* GetFocusTraversableParentView() override { return NULL; }
123 private:
124 FocusSearch* focus_search_;
127 // BorderView is a view containing a native window with its own view hierarchy.
128 // It is interesting to test focus traversal from a view hierarchy to an inner
129 // view hierarchy.
130 class BorderView : public NativeViewHost {
131 public:
132 explicit BorderView(View* child) : child_(child), widget_(NULL) {
133 DCHECK(child);
134 SetFocusable(false);
137 ~BorderView() override {}
139 virtual internal::RootView* GetContentsRootView() {
140 return static_cast<internal::RootView*>(widget_->GetRootView());
143 FocusTraversable* GetFocusTraversable() override {
144 return static_cast<internal::RootView*>(widget_->GetRootView());
147 void ViewHierarchyChanged(
148 const ViewHierarchyChangedDetails& details) override {
149 NativeViewHost::ViewHierarchyChanged(details);
151 if (details.child == this && details.is_add) {
152 if (!widget_) {
153 widget_ = new Widget;
154 Widget::InitParams params(Widget::InitParams::TYPE_CONTROL);
155 params.parent = details.parent->GetWidget()->GetNativeView();
156 widget_->Init(params);
157 widget_->SetFocusTraversableParentView(this);
158 widget_->SetContentsView(child_);
161 // We have been added to a view hierarchy, attach the native view.
162 Attach(widget_->GetNativeView());
163 // Also update the FocusTraversable parent so the focus traversal works.
164 static_cast<internal::RootView*>(widget_->GetRootView())->
165 SetFocusTraversableParent(GetWidget()->GetFocusTraversable());
169 private:
170 View* child_;
171 Widget* widget_;
173 DISALLOW_COPY_AND_ASSIGN(BorderView);
176 } // namespace
178 class FocusTraversalTest : public FocusManagerTest {
179 public:
180 ~FocusTraversalTest() override;
182 void InitContentView() override;
184 protected:
185 FocusTraversalTest();
187 View* FindViewByID(int id) {
188 View* view = GetContentsView()->GetViewByID(id);
189 if (view)
190 return view;
191 if (style_tab_)
192 view = style_tab_->GetSelectedTab()->GetViewByID(id);
193 if (view)
194 return view;
195 view = search_border_view_->GetContentsRootView()->GetViewByID(id);
196 if (view)
197 return view;
198 return NULL;
201 protected:
202 TabbedPane* style_tab_;
203 BorderView* search_border_view_;
204 DummyComboboxModel combobox_model_;
205 PaneView* left_container_;
206 PaneView* right_container_;
208 DISALLOW_COPY_AND_ASSIGN(FocusTraversalTest);
211 FocusTraversalTest::FocusTraversalTest()
212 : style_tab_(NULL),
213 search_border_view_(NULL) {
216 FocusTraversalTest::~FocusTraversalTest() {
219 void FocusTraversalTest::InitContentView() {
220 // Create a complicated view hierarchy with lots of control types for
221 // use by all of the focus traversal tests.
223 // Class name, ID, and asterisk next to focusable views:
225 // View
226 // Checkbox * kTopCheckBoxID
227 // PaneView kLeftContainerID
228 // Label kAppleLabelID
229 // Textfield * kAppleTextfieldID
230 // Label kOrangeLabelID
231 // Textfield * kOrangeTextfieldID
232 // Label kBananaLabelID
233 // Textfield * kBananaTextfieldID
234 // Label kKiwiLabelID
235 // Textfield * kKiwiTextfieldID
236 // NativeButton * kFruitButtonID
237 // Checkbox * kFruitCheckBoxID
238 // Combobox * kComboboxID
239 // PaneView kRightContainerID
240 // RadioButton * kAsparagusButtonID
241 // RadioButton * kBroccoliButtonID
242 // RadioButton * kCauliflowerButtonID
243 // View kInnerContainerID
244 // ScrollView kScrollViewID
245 // View
246 // Link * kRosettaLinkID
247 // Link * kStupeurEtTremblementLinkID
248 // Link * kDinerGameLinkID
249 // Link * kRidiculeLinkID
250 // Link * kClosetLinkID
251 // Link * kVisitingLinkID
252 // Link * kAmelieLinkID
253 // Link * kJoyeuxNoelLinkID
254 // Link * kCampingLinkID
255 // Link * kBriceDeNiceLinkID
256 // Link * kTaxiLinkID
257 // Link * kAsterixLinkID
258 // NativeButton * kOKButtonID
259 // NativeButton * kCancelButtonID
260 // NativeButton * kHelpButtonID
261 // TabbedPane * kStyleContainerID
262 // View
263 // Checkbox * kBoldCheckBoxID
264 // Checkbox * kItalicCheckBoxID
265 // Checkbox * kUnderlinedCheckBoxID
266 // Link * kStyleHelpLinkID
267 // Textfield * kStyleTextEditID
268 // Other
269 // BorderView kSearchContainerID
270 // View
271 // Textfield * kSearchTextfieldID
272 // NativeButton * kSearchButtonID
273 // Link * kHelpLinkID
274 // View * kThumbnailContainerID
275 // NativeButton * kThumbnailStarID
276 // NativeButton * kThumbnailSuperStarID
278 GetContentsView()->set_background(
279 Background::CreateSolidBackground(SK_ColorWHITE));
281 Checkbox* cb = new Checkbox(ASCIIToUTF16("This is a checkbox"));
282 GetContentsView()->AddChildView(cb);
283 // In this fast paced world, who really has time for non hard-coded layout?
284 cb->SetBounds(10, 10, 200, 20);
285 cb->set_id(kTopCheckBoxID);
287 left_container_ = new PaneView();
288 left_container_->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK));
289 left_container_->set_background(
290 Background::CreateSolidBackground(240, 240, 240));
291 left_container_->set_id(kLeftContainerID);
292 GetContentsView()->AddChildView(left_container_);
293 left_container_->SetBounds(10, 35, 250, 200);
295 int label_x = 5;
296 int label_width = 50;
297 int label_height = 15;
298 int text_field_width = 150;
299 int y = 10;
300 int gap_between_labels = 10;
302 Label* label = new Label(ASCIIToUTF16("Apple:"));
303 label->set_id(kAppleLabelID);
304 left_container_->AddChildView(label);
305 label->SetBounds(label_x, y, label_width, label_height);
307 Textfield* text_field = new Textfield();
308 text_field->set_id(kAppleTextfieldID);
309 left_container_->AddChildView(text_field);
310 text_field->SetBounds(label_x + label_width + 5, y,
311 text_field_width, label_height);
313 y += label_height + gap_between_labels;
315 label = new Label(ASCIIToUTF16("Orange:"));
316 label->set_id(kOrangeLabelID);
317 left_container_->AddChildView(label);
318 label->SetBounds(label_x, y, label_width, label_height);
320 text_field = new Textfield();
321 text_field->set_id(kOrangeTextfieldID);
322 left_container_->AddChildView(text_field);
323 text_field->SetBounds(label_x + label_width + 5, y,
324 text_field_width, label_height);
326 y += label_height + gap_between_labels;
328 label = new Label(ASCIIToUTF16("Banana:"));
329 label->set_id(kBananaLabelID);
330 left_container_->AddChildView(label);
331 label->SetBounds(label_x, y, label_width, label_height);
333 text_field = new Textfield();
334 text_field->set_id(kBananaTextfieldID);
335 left_container_->AddChildView(text_field);
336 text_field->SetBounds(label_x + label_width + 5, y,
337 text_field_width, label_height);
339 y += label_height + gap_between_labels;
341 label = new Label(ASCIIToUTF16("Kiwi:"));
342 label->set_id(kKiwiLabelID);
343 left_container_->AddChildView(label);
344 label->SetBounds(label_x, y, label_width, label_height);
346 text_field = new Textfield();
347 text_field->set_id(kKiwiTextfieldID);
348 left_container_->AddChildView(text_field);
349 text_field->SetBounds(label_x + label_width + 5, y,
350 text_field_width, label_height);
352 y += label_height + gap_between_labels;
354 LabelButton* button = new LabelButton(NULL, ASCIIToUTF16("Click me"));
355 button->SetStyle(Button::STYLE_BUTTON);
356 button->SetBounds(label_x, y + 10, 80, 30);
357 button->set_id(kFruitButtonID);
358 left_container_->AddChildView(button);
359 y += 40;
361 cb = new Checkbox(ASCIIToUTF16("This is another check box"));
362 cb->SetBounds(label_x + label_width + 5, y, 180, 20);
363 cb->set_id(kFruitCheckBoxID);
364 left_container_->AddChildView(cb);
365 y += 20;
367 Combobox* combobox = new Combobox(&combobox_model_);
368 combobox->SetBounds(label_x + label_width + 5, y, 150, 30);
369 combobox->set_id(kComboboxID);
370 left_container_->AddChildView(combobox);
372 right_container_ = new PaneView();
373 right_container_->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK));
374 right_container_->set_background(
375 Background::CreateSolidBackground(240, 240, 240));
376 right_container_->set_id(kRightContainerID);
377 GetContentsView()->AddChildView(right_container_);
378 right_container_->SetBounds(270, 35, 300, 200);
380 y = 10;
381 int radio_button_height = 18;
382 int gap_between_radio_buttons = 10;
383 RadioButton* radio_button = new RadioButton(ASCIIToUTF16("Asparagus"), 1);
384 radio_button->set_id(kAsparagusButtonID);
385 right_container_->AddChildView(radio_button);
386 radio_button->SetBounds(5, y, 70, radio_button_height);
387 radio_button->SetGroup(1);
388 y += radio_button_height + gap_between_radio_buttons;
389 radio_button = new RadioButton(ASCIIToUTF16("Broccoli"), 1);
390 radio_button->set_id(kBroccoliButtonID);
391 right_container_->AddChildView(radio_button);
392 radio_button->SetBounds(5, y, 70, radio_button_height);
393 radio_button->SetGroup(1);
394 RadioButton* radio_button_to_check = radio_button;
395 y += radio_button_height + gap_between_radio_buttons;
396 radio_button = new RadioButton(ASCIIToUTF16("Cauliflower"), 1);
397 radio_button->set_id(kCauliflowerButtonID);
398 right_container_->AddChildView(radio_button);
399 radio_button->SetBounds(5, y, 70, radio_button_height);
400 radio_button->SetGroup(1);
401 y += radio_button_height + gap_between_radio_buttons;
403 View* inner_container = new View();
404 inner_container->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK));
405 inner_container->set_background(
406 Background::CreateSolidBackground(230, 230, 230));
407 inner_container->set_id(kInnerContainerID);
408 right_container_->AddChildView(inner_container);
409 inner_container->SetBounds(100, 10, 150, 180);
411 ScrollView* scroll_view = new ScrollView();
412 scroll_view->set_id(kScrollViewID);
413 inner_container->AddChildView(scroll_view);
414 scroll_view->SetBounds(1, 1, 148, 178);
416 View* scroll_content = new View();
417 scroll_content->SetBounds(0, 0, 200, 200);
418 scroll_content->set_background(
419 Background::CreateSolidBackground(200, 200, 200));
420 scroll_view->SetContents(scroll_content);
422 static const char* const kTitles[] = {
423 "Rosetta", "Stupeur et tremblement", "The diner game",
424 "Ridicule", "Le placard", "Les Visiteurs", "Amelie",
425 "Joyeux Noel", "Camping", "Brice de Nice",
426 "Taxi", "Asterix"
429 static const int kIDs[] = {
430 kRosettaLinkID, kStupeurEtTremblementLinkID, kDinerGameLinkID,
431 kRidiculeLinkID, kClosetLinkID, kVisitingLinkID, kAmelieLinkID,
432 kJoyeuxNoelLinkID, kCampingLinkID, kBriceDeNiceLinkID,
433 kTaxiLinkID, kAsterixLinkID
436 DCHECK(arraysize(kTitles) == arraysize(kIDs));
438 y = 5;
439 for (size_t i = 0; i < arraysize(kTitles); ++i) {
440 Link* link = new Link(ASCIIToUTF16(kTitles[i]));
441 link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
442 link->set_id(kIDs[i]);
443 scroll_content->AddChildView(link);
444 link->SetBounds(5, y, 300, 15);
445 y += 15;
448 y = 250;
449 int width = 60;
450 button = new LabelButton(NULL, ASCIIToUTF16("OK"));
451 button->SetStyle(Button::STYLE_BUTTON);
452 button->set_id(kOKButtonID);
453 button->SetIsDefault(true);
455 GetContentsView()->AddChildView(button);
456 button->SetBounds(150, y, width, 30);
458 button = new LabelButton(NULL, ASCIIToUTF16("Cancel"));
459 button->SetStyle(Button::STYLE_BUTTON);
460 button->set_id(kCancelButtonID);
461 GetContentsView()->AddChildView(button);
462 button->SetBounds(220, y, width, 30);
464 button = new LabelButton(NULL, ASCIIToUTF16("Help"));
465 button->SetStyle(Button::STYLE_BUTTON);
466 button->set_id(kHelpButtonID);
467 GetContentsView()->AddChildView(button);
468 button->SetBounds(290, y, width, 30);
470 y += 40;
472 View* contents = NULL;
473 Link* link = NULL;
475 // Left bottom box with style checkboxes.
476 contents = new View();
477 contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE));
478 cb = new Checkbox(ASCIIToUTF16("Bold"));
479 contents->AddChildView(cb);
480 cb->SetBounds(10, 10, 50, 20);
481 cb->set_id(kBoldCheckBoxID);
483 cb = new Checkbox(ASCIIToUTF16("Italic"));
484 contents->AddChildView(cb);
485 cb->SetBounds(70, 10, 50, 20);
486 cb->set_id(kItalicCheckBoxID);
488 cb = new Checkbox(ASCIIToUTF16("Underlined"));
489 contents->AddChildView(cb);
490 cb->SetBounds(130, 10, 70, 20);
491 cb->set_id(kUnderlinedCheckBoxID);
493 link = new Link(ASCIIToUTF16("Help"));
494 contents->AddChildView(link);
495 link->SetBounds(10, 35, 70, 10);
496 link->set_id(kStyleHelpLinkID);
498 text_field = new Textfield();
499 contents->AddChildView(text_field);
500 text_field->SetBounds(10, 50, 100, 20);
501 text_field->set_id(kStyleTextEditID);
503 style_tab_ = new TabbedPane();
504 style_tab_->set_id(kStyleContainerID);
505 GetContentsView()->AddChildView(style_tab_);
506 style_tab_->SetBounds(10, y, 210, 100);
507 style_tab_->AddTab(ASCIIToUTF16("Style"), contents);
508 style_tab_->AddTab(ASCIIToUTF16("Other"), new View());
510 // Right bottom box with search.
511 contents = new View();
512 contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE));
513 text_field = new Textfield();
514 contents->AddChildView(text_field);
515 text_field->SetBounds(10, 10, 100, 20);
516 text_field->set_id(kSearchTextfieldID);
518 button = new LabelButton(NULL, ASCIIToUTF16("Search"));
519 button->SetStyle(Button::STYLE_BUTTON);
520 contents->AddChildView(button);
521 button->SetBounds(112, 5, 60, 30);
522 button->set_id(kSearchButtonID);
524 link = new Link(ASCIIToUTF16("Help"));
525 link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
526 link->set_id(kHelpLinkID);
527 contents->AddChildView(link);
528 link->SetBounds(175, 10, 30, 20);
530 search_border_view_ = new BorderView(contents);
531 search_border_view_->set_id(kSearchContainerID);
533 GetContentsView()->AddChildView(search_border_view_);
534 search_border_view_->SetBounds(300, y, 240, 50);
536 y += 60;
538 contents = new View();
539 contents->SetFocusable(true);
540 contents->set_background(Background::CreateSolidBackground(SK_ColorBLUE));
541 contents->set_id(kThumbnailContainerID);
542 button = new LabelButton(NULL, ASCIIToUTF16("Star"));
543 button->SetStyle(Button::STYLE_BUTTON);
544 contents->AddChildView(button);
545 button->SetBounds(5, 5, 50, 30);
546 button->set_id(kThumbnailStarID);
547 button = new LabelButton(NULL, ASCIIToUTF16("SuperStar"));
548 button->SetStyle(Button::STYLE_BUTTON);
549 contents->AddChildView(button);
550 button->SetBounds(60, 5, 100, 30);
551 button->set_id(kThumbnailSuperStarID);
553 GetContentsView()->AddChildView(contents);
554 contents->SetBounds(250, y, 200, 50);
555 // We can only call RadioButton::SetChecked() on the radio-button is part of
556 // the view hierarchy.
557 radio_button_to_check->SetChecked(true);
560 TEST_F(FocusTraversalTest, NormalTraversal) {
561 const int kTraversalIDs[] = { kTopCheckBoxID, kAppleTextfieldID,
562 kOrangeTextfieldID, kBananaTextfieldID, kKiwiTextfieldID,
563 kFruitButtonID, kFruitCheckBoxID, kComboboxID, kBroccoliButtonID,
564 kRosettaLinkID, kStupeurEtTremblementLinkID,
565 kDinerGameLinkID, kRidiculeLinkID, kClosetLinkID, kVisitingLinkID,
566 kAmelieLinkID, kJoyeuxNoelLinkID, kCampingLinkID, kBriceDeNiceLinkID,
567 kTaxiLinkID, kAsterixLinkID, kOKButtonID, kCancelButtonID, kHelpButtonID,
568 kStyleContainerID, kBoldCheckBoxID, kItalicCheckBoxID,
569 kUnderlinedCheckBoxID, kStyleHelpLinkID, kStyleTextEditID,
570 kSearchTextfieldID, kSearchButtonID, kHelpLinkID,
571 kThumbnailContainerID, kThumbnailStarID, kThumbnailSuperStarID };
573 // Let's traverse the whole focus hierarchy (several times, to make sure it
574 // loops OK).
575 GetFocusManager()->ClearFocus();
576 for (int i = 0; i < 3; ++i) {
577 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
578 GetFocusManager()->AdvanceFocus(false);
579 View* focused_view = GetFocusManager()->GetFocusedView();
580 EXPECT_TRUE(focused_view != NULL);
581 if (focused_view)
582 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
586 // Let's traverse in reverse order.
587 GetFocusManager()->ClearFocus();
588 for (int i = 0; i < 3; ++i) {
589 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
590 GetFocusManager()->AdvanceFocus(true);
591 View* focused_view = GetFocusManager()->GetFocusedView();
592 EXPECT_TRUE(focused_view != NULL);
593 if (focused_view)
594 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
599 TEST_F(FocusTraversalTest, TraversalWithNonEnabledViews) {
600 const int kDisabledIDs[] = {
601 kBananaTextfieldID, kFruitCheckBoxID, kComboboxID, kAsparagusButtonID,
602 kCauliflowerButtonID, kClosetLinkID, kVisitingLinkID, kBriceDeNiceLinkID,
603 kTaxiLinkID, kAsterixLinkID, kHelpButtonID, kBoldCheckBoxID,
604 kSearchTextfieldID, kHelpLinkID };
606 const int kTraversalIDs[] = { kTopCheckBoxID, kAppleTextfieldID,
607 kOrangeTextfieldID, kKiwiTextfieldID, kFruitButtonID, kBroccoliButtonID,
608 kRosettaLinkID, kStupeurEtTremblementLinkID, kDinerGameLinkID,
609 kRidiculeLinkID, kAmelieLinkID, kJoyeuxNoelLinkID, kCampingLinkID,
610 kOKButtonID, kCancelButtonID, kStyleContainerID, kItalicCheckBoxID,
611 kUnderlinedCheckBoxID, kStyleHelpLinkID, kStyleTextEditID,
612 kSearchButtonID, kThumbnailContainerID, kThumbnailStarID,
613 kThumbnailSuperStarID };
615 // Let's disable some views.
616 for (size_t i = 0; i < arraysize(kDisabledIDs); i++) {
617 View* v = FindViewByID(kDisabledIDs[i]);
618 ASSERT_TRUE(v != NULL);
619 v->SetEnabled(false);
622 View* focused_view;
623 // Let's do one traversal (several times, to make sure it loops ok).
624 GetFocusManager()->ClearFocus();
625 for (int i = 0; i < 3; ++i) {
626 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
627 GetFocusManager()->AdvanceFocus(false);
628 focused_view = GetFocusManager()->GetFocusedView();
629 EXPECT_TRUE(focused_view != NULL);
630 if (focused_view)
631 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
635 // Same thing in reverse.
636 GetFocusManager()->ClearFocus();
637 for (int i = 0; i < 3; ++i) {
638 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
639 GetFocusManager()->AdvanceFocus(true);
640 focused_view = GetFocusManager()->GetFocusedView();
641 EXPECT_TRUE(focused_view != NULL);
642 if (focused_view)
643 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
648 TEST_F(FocusTraversalTest, TraversalWithInvisibleViews) {
649 const int kInvisibleIDs[] = { kTopCheckBoxID, kOKButtonID,
650 kThumbnailContainerID };
652 const int kTraversalIDs[] = { kAppleTextfieldID, kOrangeTextfieldID,
653 kBananaTextfieldID, kKiwiTextfieldID, kFruitButtonID, kFruitCheckBoxID,
654 kComboboxID, kBroccoliButtonID, kRosettaLinkID,
655 kStupeurEtTremblementLinkID, kDinerGameLinkID, kRidiculeLinkID,
656 kClosetLinkID, kVisitingLinkID, kAmelieLinkID, kJoyeuxNoelLinkID,
657 kCampingLinkID, kBriceDeNiceLinkID, kTaxiLinkID, kAsterixLinkID,
658 kCancelButtonID, kHelpButtonID, kStyleContainerID, kBoldCheckBoxID,
659 kItalicCheckBoxID, kUnderlinedCheckBoxID, kStyleHelpLinkID,
660 kStyleTextEditID, kSearchTextfieldID, kSearchButtonID, kHelpLinkID };
663 // Let's make some views invisible.
664 for (size_t i = 0; i < arraysize(kInvisibleIDs); i++) {
665 View* v = FindViewByID(kInvisibleIDs[i]);
666 ASSERT_TRUE(v != NULL);
667 v->SetVisible(false);
670 View* focused_view;
671 // Let's do one traversal (several times, to make sure it loops ok).
672 GetFocusManager()->ClearFocus();
673 for (int i = 0; i < 3; ++i) {
674 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
675 GetFocusManager()->AdvanceFocus(false);
676 focused_view = GetFocusManager()->GetFocusedView();
677 EXPECT_TRUE(focused_view != NULL);
678 if (focused_view)
679 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
683 // Same thing in reverse.
684 GetFocusManager()->ClearFocus();
685 for (int i = 0; i < 3; ++i) {
686 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
687 GetFocusManager()->AdvanceFocus(true);
688 focused_view = GetFocusManager()->GetFocusedView();
689 EXPECT_TRUE(focused_view != NULL);
690 if (focused_view)
691 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
696 TEST_F(FocusTraversalTest, PaneTraversal) {
697 // Tests trapping the traversal within a pane - useful for full
698 // keyboard accessibility for toolbars.
700 // First test the left container.
701 const int kLeftTraversalIDs[] = {
702 kAppleTextfieldID,
703 kOrangeTextfieldID, kBananaTextfieldID, kKiwiTextfieldID,
704 kFruitButtonID, kFruitCheckBoxID, kComboboxID };
706 FocusSearch focus_search_left(left_container_, true, false);
707 left_container_->EnablePaneFocus(&focus_search_left);
708 FindViewByID(kComboboxID)->RequestFocus();
710 // Traverse the focus hierarchy within the pane several times.
711 for (int i = 0; i < 3; ++i) {
712 for (size_t j = 0; j < arraysize(kLeftTraversalIDs); j++) {
713 GetFocusManager()->AdvanceFocus(false);
714 View* focused_view = GetFocusManager()->GetFocusedView();
715 EXPECT_TRUE(focused_view != NULL);
716 if (focused_view)
717 EXPECT_EQ(kLeftTraversalIDs[j], focused_view->id());
721 // Traverse in reverse order.
722 FindViewByID(kAppleTextfieldID)->RequestFocus();
723 for (int i = 0; i < 3; ++i) {
724 for (int j = arraysize(kLeftTraversalIDs) - 1; j >= 0; --j) {
725 GetFocusManager()->AdvanceFocus(true);
726 View* focused_view = GetFocusManager()->GetFocusedView();
727 EXPECT_TRUE(focused_view != NULL);
728 if (focused_view)
729 EXPECT_EQ(kLeftTraversalIDs[j], focused_view->id());
733 // Now test the right container, but this time with accessibility mode.
734 // Make some links not focusable, but mark one of them as
735 // "accessibility focusable", so it should show up in the traversal.
736 const int kRightTraversalIDs[] = {
737 kBroccoliButtonID, kDinerGameLinkID, kRidiculeLinkID,
738 kClosetLinkID, kVisitingLinkID, kAmelieLinkID, kJoyeuxNoelLinkID,
739 kCampingLinkID, kBriceDeNiceLinkID, kTaxiLinkID, kAsterixLinkID };
741 FocusSearch focus_search_right(right_container_, true, true);
742 right_container_->EnablePaneFocus(&focus_search_right);
743 FindViewByID(kRosettaLinkID)->SetFocusable(false);
744 FindViewByID(kStupeurEtTremblementLinkID)->SetFocusable(false);
745 FindViewByID(kDinerGameLinkID)->SetAccessibilityFocusable(true);
746 FindViewByID(kDinerGameLinkID)->SetFocusable(false);
747 FindViewByID(kAsterixLinkID)->RequestFocus();
749 // Traverse the focus hierarchy within the pane several times.
750 for (int i = 0; i < 3; ++i) {
751 for (size_t j = 0; j < arraysize(kRightTraversalIDs); j++) {
752 GetFocusManager()->AdvanceFocus(false);
753 View* focused_view = GetFocusManager()->GetFocusedView();
754 EXPECT_TRUE(focused_view != NULL);
755 if (focused_view)
756 EXPECT_EQ(kRightTraversalIDs[j], focused_view->id());
760 // Traverse in reverse order.
761 FindViewByID(kBroccoliButtonID)->RequestFocus();
762 for (int i = 0; i < 3; ++i) {
763 for (int j = arraysize(kRightTraversalIDs) - 1; j >= 0; --j) {
764 GetFocusManager()->AdvanceFocus(true);
765 View* focused_view = GetFocusManager()->GetFocusedView();
766 EXPECT_TRUE(focused_view != NULL);
767 if (focused_view)
768 EXPECT_EQ(kRightTraversalIDs[j], focused_view->id());
773 } // namespace views