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
;
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
{
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
{
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
{
118 // Overridden from FocusTraversable:
119 views::FocusSearch
* GetFocusSearch() override
{ return focus_search_
; }
120 FocusTraversable
* GetFocusTraversableParent() override
{ return NULL
; }
121 View
* GetFocusTraversableParentView() override
{ return NULL
; }
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
130 class BorderView
: public NativeViewHost
{
132 explicit BorderView(View
* child
) : child_(child
), widget_(NULL
) {
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
) {
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());
173 DISALLOW_COPY_AND_ASSIGN(BorderView
);
178 class FocusTraversalTest
: public FocusManagerTest
{
180 ~FocusTraversalTest() override
;
182 void InitContentView() override
;
185 FocusTraversalTest();
187 View
* FindViewByID(int id
) {
188 View
* view
= GetContentsView()->GetViewByID(id
);
192 view
= style_tab_
->GetSelectedTab()->GetViewByID(id
);
195 view
= search_border_view_
->GetContentsRootView()->GetViewByID(id
);
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()
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:
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
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
263 // Checkbox * kBoldCheckBoxID
264 // Checkbox * kItalicCheckBoxID
265 // Checkbox * kUnderlinedCheckBoxID
266 // Link * kStyleHelpLinkID
267 // Textfield * kStyleTextEditID
269 // BorderView kSearchContainerID
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);
296 int label_width
= 50;
297 int label_height
= 15;
298 int text_field_width
= 150;
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
);
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
);
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);
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",
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
));
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);
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);
472 View
* contents
= 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);
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
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
);
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
);
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);
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
);
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
);
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);
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
);
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
);
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
[] = {
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
);
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
);
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
);
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
);
768 EXPECT_EQ(kRightTraversalIDs
[j
], focused_view
->id());