1 // Copyright 2013 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/controls/combobox/combobox.h"
9 #include "base/basictypes.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "ui/base/ime/input_method.h"
12 #include "ui/base/ime/text_input_client.h"
13 #include "ui/base/models/combobox_model.h"
14 #include "ui/base/models/menu_model.h"
15 #include "ui/events/event.h"
16 #include "ui/events/event_constants.h"
17 #include "ui/events/event_utils.h"
18 #include "ui/events/keycodes/keyboard_codes.h"
19 #include "ui/views/controls/combobox/combobox_listener.h"
20 #include "ui/views/controls/menu/menu_runner.h"
21 #include "ui/views/controls/menu/menu_runner_handler.h"
22 #include "ui/views/test/menu_runner_test_api.h"
23 #include "ui/views/test/views_test_base.h"
24 #include "ui/views/widget/widget.h"
26 using base::ASCIIToUTF16
;
31 class ComboboxTestApi
{
33 explicit ComboboxTestApi(Combobox
* combobox
) : combobox_(combobox
) {}
35 void PerformActionAt(int index
) { menu_model()->ActivatedAt(index
); }
36 void InstallTestMenuRunner(int* menu_show_count
);
38 gfx::Size
content_size() { return combobox_
->content_size_
; }
39 ui::MenuModel
* menu_model() { return combobox_
->menu_model_adapter_
.get(); }
44 DISALLOW_COPY_AND_ASSIGN(ComboboxTestApi
);
49 using test::ComboboxTestApi
;
53 // An dummy implementation of MenuRunnerHandler to check if the dropdown menu is
55 class TestMenuRunnerHandler
: public MenuRunnerHandler
{
57 explicit TestMenuRunnerHandler(int* show_counter
)
58 : show_counter_(show_counter
) {}
59 MenuRunner::RunResult
RunMenuAt(Widget
* parent
,
61 const gfx::Rect
& bounds
,
62 MenuAnchorPosition anchor
,
63 ui::MenuSourceType source_type
,
64 int32 types
) override
{
66 return MenuRunner::NORMAL_EXIT
;
72 DISALLOW_COPY_AND_ASSIGN(TestMenuRunnerHandler
);
75 // A wrapper of Combobox to intercept the result of OnKeyPressed() and
76 // OnKeyReleased() methods.
77 class TestCombobox
: public Combobox
{
79 explicit TestCombobox(ui::ComboboxModel
* model
)
82 key_received_(false) {}
84 bool OnKeyPressed(const ui::KeyEvent
& e
) override
{
86 key_handled_
= Combobox::OnKeyPressed(e
);
90 bool OnKeyReleased(const ui::KeyEvent
& e
) override
{
92 key_handled_
= Combobox::OnKeyReleased(e
);
96 bool key_handled() const { return key_handled_
; }
97 bool key_received() const { return key_received_
; }
100 key_received_
= key_handled_
= false;
107 DISALLOW_COPY_AND_ASSIGN(TestCombobox
);
110 // A concrete class is needed to test the combobox.
111 class TestComboboxModel
: public ui::ComboboxModel
{
113 TestComboboxModel() {}
114 ~TestComboboxModel() override
{}
116 enum { kItemCount
= 10 };
118 // ui::ComboboxModel:
119 int GetItemCount() const override
{ return item_count_
; }
120 base::string16
GetItemAt(int index
) override
{
121 if (IsItemSeparatorAt(index
)) {
123 return ASCIIToUTF16("SEPARATOR");
125 return ASCIIToUTF16(index
% 2 == 0 ? "PEANUT BUTTER" : "JELLY");
127 bool IsItemSeparatorAt(int index
) override
{
128 return separators_
.find(index
) != separators_
.end();
131 int GetDefaultIndex() const override
{
132 // Return the first index that is not a separator.
133 for (int index
= 0; index
< kItemCount
; ++index
) {
134 if (separators_
.find(index
) == separators_
.end())
141 void SetSeparators(const std::set
<int>& separators
) {
142 separators_
= separators
;
145 void set_item_count(int item_count
) { item_count_
= item_count
; }
148 std::set
<int> separators_
;
149 int item_count_
= kItemCount
;
151 DISALLOW_COPY_AND_ASSIGN(TestComboboxModel
);
154 // A combobox model which refers to a vector.
155 class VectorComboboxModel
: public ui::ComboboxModel
{
157 explicit VectorComboboxModel(std::vector
<std::string
>* values
)
159 ~VectorComboboxModel() override
{}
161 void set_default_index(int default_index
) { default_index_
= default_index
; }
163 // ui::ComboboxModel:
164 int GetItemCount() const override
{ return (int)values_
->size(); }
165 base::string16
GetItemAt(int index
) override
{
166 return ASCIIToUTF16(values_
->at(index
));
168 bool IsItemSeparatorAt(int index
) override
{ return false; }
169 int GetDefaultIndex() const override
{ return default_index_
; }
172 std::vector
<std::string
>* values_
;
173 int default_index_
= 0;
175 DISALLOW_COPY_AND_ASSIGN(VectorComboboxModel
);
178 class EvilListener
: public ComboboxListener
{
180 EvilListener() : deleted_(false) {}
181 ~EvilListener() override
{};
184 void OnPerformAction(Combobox
* combobox
) override
{
189 bool deleted() const { return deleted_
; }
194 DISALLOW_COPY_AND_ASSIGN(EvilListener
);
197 class TestComboboxListener
: public views::ComboboxListener
{
199 TestComboboxListener() : perform_action_index_(-1), actions_performed_(0) {}
200 ~TestComboboxListener() override
{}
202 void OnPerformAction(views::Combobox
* combobox
) override
{
203 perform_action_index_
= combobox
->selected_index();
204 actions_performed_
++;
207 int perform_action_index() const {
208 return perform_action_index_
;
211 bool on_perform_action_called() const {
212 return actions_performed_
> 0;
215 int actions_performed() const {
216 return actions_performed_
;
220 int perform_action_index_
;
221 int actions_performed_
;
224 DISALLOW_COPY_AND_ASSIGN(TestComboboxListener
);
229 void ComboboxTestApi::InstallTestMenuRunner(int* menu_show_count
) {
230 combobox_
->menu_runner_
.reset(
231 new MenuRunner(menu_model(), MenuRunner::COMBOBOX
));
232 test::MenuRunnerTestAPI
test_api(combobox_
->menu_runner_
.get());
233 test_api
.SetMenuRunnerHandler(
234 make_scoped_ptr(new TestMenuRunnerHandler(menu_show_count
)));
237 class ComboboxTest
: public ViewsTestBase
{
239 ComboboxTest() : widget_(NULL
), combobox_(NULL
) {}
241 void TearDown() override
{
244 ViewsTestBase::TearDown();
247 void InitCombobox(const std::set
<int>* separators
) {
248 model_
.reset(new TestComboboxModel());
251 model_
->SetSeparators(*separators
);
253 ASSERT_FALSE(combobox_
);
254 combobox_
= new TestCombobox(model_
.get());
255 test_api_
.reset(new ComboboxTestApi(combobox_
));
256 combobox_
->set_id(1);
258 widget_
= new Widget
;
259 Widget::InitParams params
= CreateParams(Widget::InitParams::TYPE_POPUP
);
260 params
.bounds
= gfx::Rect(200, 200, 200, 200);
261 widget_
->Init(params
);
262 View
* container
= new View();
263 widget_
->SetContentsView(container
);
264 container
->AddChildView(combobox_
);
266 combobox_
->RequestFocus();
267 combobox_
->SizeToPreferredSize();
271 void SendKeyEvent(ui::KeyboardCode key_code
) {
272 SendKeyEventWithType(key_code
, ui::ET_KEY_PRESSED
);
275 void SendKeyEventWithType(ui::KeyboardCode key_code
, ui::EventType type
) {
276 ui::KeyEvent
event(type
, key_code
, ui::EF_NONE
);
277 FocusManager
* focus_manager
= widget_
->GetFocusManager();
278 widget_
->OnKeyEvent(&event
);
279 if (!event
.handled() && focus_manager
)
280 focus_manager
->OnKeyEvent(event
);
283 View
* GetFocusedView() {
284 return widget_
->GetFocusManager()->GetFocusedView();
287 void PerformClick(const gfx::Point
& point
) {
288 ui::MouseEvent pressed_event
= ui::MouseEvent(
289 ui::ET_MOUSE_PRESSED
, point
, point
, ui::EventTimeForNow(),
290 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
291 widget_
->OnMouseEvent(&pressed_event
);
292 ui::MouseEvent released_event
= ui::MouseEvent(
293 ui::ET_MOUSE_RELEASED
, point
, point
, ui::EventTimeForNow(),
294 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
295 widget_
->OnMouseEvent(&released_event
);
298 // We need widget to populate wrapper class.
301 // |combobox_| will be allocated InitCombobox() and then owned by |widget_|.
302 TestCombobox
* combobox_
;
303 scoped_ptr
<ComboboxTestApi
> test_api_
;
305 // Combobox does not take ownership of the model, hence it needs to be scoped.
306 scoped_ptr
<TestComboboxModel
> model_
;
309 DISALLOW_COPY_AND_ASSIGN(ComboboxTest
);
312 TEST_F(ComboboxTest
, KeyTest
) {
314 SendKeyEvent(ui::VKEY_END
);
315 EXPECT_EQ(combobox_
->selected_index() + 1, model_
->GetItemCount());
316 SendKeyEvent(ui::VKEY_HOME
);
317 EXPECT_EQ(combobox_
->selected_index(), 0);
318 SendKeyEvent(ui::VKEY_DOWN
);
319 SendKeyEvent(ui::VKEY_DOWN
);
320 EXPECT_EQ(combobox_
->selected_index(), 2);
321 SendKeyEvent(ui::VKEY_RIGHT
);
322 EXPECT_EQ(combobox_
->selected_index(), 2);
323 SendKeyEvent(ui::VKEY_LEFT
);
324 EXPECT_EQ(combobox_
->selected_index(), 2);
325 SendKeyEvent(ui::VKEY_UP
);
326 EXPECT_EQ(combobox_
->selected_index(), 1);
327 SendKeyEvent(ui::VKEY_PRIOR
);
328 EXPECT_EQ(combobox_
->selected_index(), 0);
329 SendKeyEvent(ui::VKEY_NEXT
);
330 EXPECT_EQ(combobox_
->selected_index(), model_
->GetItemCount() - 1);
333 // Check that if a combobox is disabled before it has a native wrapper, then the
334 // native wrapper inherits the disabled state when it gets created.
335 TEST_F(ComboboxTest
, DisabilityTest
) {
336 model_
.reset(new TestComboboxModel());
338 ASSERT_FALSE(combobox_
);
339 combobox_
= new TestCombobox(model_
.get());
340 combobox_
->SetEnabled(false);
342 widget_
= new Widget
;
343 Widget::InitParams params
= CreateParams(Widget::InitParams::TYPE_POPUP
);
344 params
.bounds
= gfx::Rect(100, 100, 100, 100);
345 widget_
->Init(params
);
346 View
* container
= new View();
347 widget_
->SetContentsView(container
);
348 container
->AddChildView(combobox_
);
349 EXPECT_FALSE(combobox_
->enabled());
352 // Verifies that we don't select a separator line in combobox when navigating
354 TEST_F(ComboboxTest
, SkipSeparatorSimple
) {
355 std::set
<int> separators
;
356 separators
.insert(2);
357 InitCombobox(&separators
);
358 EXPECT_EQ(0, combobox_
->selected_index());
359 SendKeyEvent(ui::VKEY_DOWN
);
360 EXPECT_EQ(1, combobox_
->selected_index());
361 SendKeyEvent(ui::VKEY_DOWN
);
362 EXPECT_EQ(3, combobox_
->selected_index());
363 SendKeyEvent(ui::VKEY_UP
);
364 EXPECT_EQ(1, combobox_
->selected_index());
365 SendKeyEvent(ui::VKEY_HOME
);
366 EXPECT_EQ(0, combobox_
->selected_index());
367 SendKeyEvent(ui::VKEY_PRIOR
);
368 EXPECT_EQ(0, combobox_
->selected_index());
369 SendKeyEvent(ui::VKEY_END
);
370 EXPECT_EQ(9, combobox_
->selected_index());
373 // Verifies that we never select the separator that is in the beginning of the
374 // combobox list when navigating through keyboard.
375 TEST_F(ComboboxTest
, SkipSeparatorBeginning
) {
376 std::set
<int> separators
;
377 separators
.insert(0);
378 InitCombobox(&separators
);
379 EXPECT_EQ(1, combobox_
->selected_index());
380 SendKeyEvent(ui::VKEY_DOWN
);
381 EXPECT_EQ(2, combobox_
->selected_index());
382 SendKeyEvent(ui::VKEY_DOWN
);
383 EXPECT_EQ(3, combobox_
->selected_index());
384 SendKeyEvent(ui::VKEY_UP
);
385 EXPECT_EQ(2, combobox_
->selected_index());
386 SendKeyEvent(ui::VKEY_HOME
);
387 EXPECT_EQ(1, combobox_
->selected_index());
388 SendKeyEvent(ui::VKEY_PRIOR
);
389 EXPECT_EQ(1, combobox_
->selected_index());
390 SendKeyEvent(ui::VKEY_END
);
391 EXPECT_EQ(9, combobox_
->selected_index());
394 // Verifies that we never select the separator that is in the end of the
395 // combobox list when navigating through keyboard.
396 TEST_F(ComboboxTest
, SkipSeparatorEnd
) {
397 std::set
<int> separators
;
398 separators
.insert(TestComboboxModel::kItemCount
- 1);
399 InitCombobox(&separators
);
400 combobox_
->SetSelectedIndex(8);
401 SendKeyEvent(ui::VKEY_DOWN
);
402 EXPECT_EQ(8, combobox_
->selected_index());
403 SendKeyEvent(ui::VKEY_UP
);
404 EXPECT_EQ(7, combobox_
->selected_index());
405 SendKeyEvent(ui::VKEY_END
);
406 EXPECT_EQ(8, combobox_
->selected_index());
409 // Verifies that we never select any of the adjacent separators (multiple
410 // consecutive) that appear in the beginning of the combobox list when
411 // navigating through keyboard.
412 TEST_F(ComboboxTest
, SkipMultipleSeparatorsAtBeginning
) {
413 std::set
<int> separators
;
414 separators
.insert(0);
415 separators
.insert(1);
416 separators
.insert(2);
417 InitCombobox(&separators
);
418 EXPECT_EQ(3, combobox_
->selected_index());
419 SendKeyEvent(ui::VKEY_DOWN
);
420 EXPECT_EQ(4, combobox_
->selected_index());
421 SendKeyEvent(ui::VKEY_UP
);
422 EXPECT_EQ(3, combobox_
->selected_index());
423 SendKeyEvent(ui::VKEY_NEXT
);
424 EXPECT_EQ(9, combobox_
->selected_index());
425 SendKeyEvent(ui::VKEY_HOME
);
426 EXPECT_EQ(3, combobox_
->selected_index());
427 SendKeyEvent(ui::VKEY_END
);
428 EXPECT_EQ(9, combobox_
->selected_index());
429 SendKeyEvent(ui::VKEY_PRIOR
);
430 EXPECT_EQ(3, combobox_
->selected_index());
433 // Verifies that we never select any of the adjacent separators (multiple
434 // consecutive) that appear in the middle of the combobox list when navigating
436 TEST_F(ComboboxTest
, SkipMultipleAdjacentSeparatorsAtMiddle
) {
437 std::set
<int> separators
;
438 separators
.insert(4);
439 separators
.insert(5);
440 separators
.insert(6);
441 InitCombobox(&separators
);
442 combobox_
->SetSelectedIndex(3);
443 SendKeyEvent(ui::VKEY_DOWN
);
444 EXPECT_EQ(7, combobox_
->selected_index());
445 SendKeyEvent(ui::VKEY_UP
);
446 EXPECT_EQ(3, combobox_
->selected_index());
449 // Verifies that we never select any of the adjacent separators (multiple
450 // consecutive) that appear in the end of the combobox list when navigating
452 TEST_F(ComboboxTest
, SkipMultipleSeparatorsAtEnd
) {
453 std::set
<int> separators
;
454 separators
.insert(7);
455 separators
.insert(8);
456 separators
.insert(9);
457 InitCombobox(&separators
);
458 combobox_
->SetSelectedIndex(6);
459 SendKeyEvent(ui::VKEY_DOWN
);
460 EXPECT_EQ(6, combobox_
->selected_index());
461 SendKeyEvent(ui::VKEY_UP
);
462 EXPECT_EQ(5, combobox_
->selected_index());
463 SendKeyEvent(ui::VKEY_HOME
);
464 EXPECT_EQ(0, combobox_
->selected_index());
465 SendKeyEvent(ui::VKEY_NEXT
);
466 EXPECT_EQ(6, combobox_
->selected_index());
467 SendKeyEvent(ui::VKEY_PRIOR
);
468 EXPECT_EQ(0, combobox_
->selected_index());
469 SendKeyEvent(ui::VKEY_END
);
470 EXPECT_EQ(6, combobox_
->selected_index());
473 TEST_F(ComboboxTest
, GetTextForRowTest
) {
474 std::set
<int> separators
;
475 separators
.insert(0);
476 separators
.insert(1);
477 separators
.insert(9);
478 InitCombobox(&separators
);
479 for (int i
= 0; i
< combobox_
->GetRowCount(); ++i
) {
480 if (separators
.count(i
) != 0) {
481 EXPECT_TRUE(combobox_
->GetTextForRow(i
).empty()) << i
;
483 EXPECT_EQ(ASCIIToUTF16(i
% 2 == 0 ? "PEANUT BUTTER" : "JELLY"),
484 combobox_
->GetTextForRow(i
)) << i
;
489 // Verifies selecting the first matching value (and returning whether found).
490 TEST_F(ComboboxTest
, SelectValue
) {
492 ASSERT_EQ(model_
->GetDefaultIndex(), combobox_
->selected_index());
493 EXPECT_TRUE(combobox_
->SelectValue(ASCIIToUTF16("PEANUT BUTTER")));
494 EXPECT_EQ(0, combobox_
->selected_index());
495 EXPECT_TRUE(combobox_
->SelectValue(ASCIIToUTF16("JELLY")));
496 EXPECT_EQ(1, combobox_
->selected_index());
497 EXPECT_FALSE(combobox_
->SelectValue(ASCIIToUTF16("BANANAS")));
498 EXPECT_EQ(1, combobox_
->selected_index());
500 // With the action style, the selected index is always 0.
501 combobox_
->SetStyle(Combobox::STYLE_ACTION
);
502 EXPECT_FALSE(combobox_
->SelectValue(ASCIIToUTF16("PEANUT BUTTER")));
503 EXPECT_EQ(0, combobox_
->selected_index());
504 EXPECT_FALSE(combobox_
->SelectValue(ASCIIToUTF16("JELLY")));
505 EXPECT_EQ(0, combobox_
->selected_index());
506 EXPECT_FALSE(combobox_
->SelectValue(ASCIIToUTF16("BANANAS")));
507 EXPECT_EQ(0, combobox_
->selected_index());
510 TEST_F(ComboboxTest
, SelectIndexActionStyle
) {
513 // With the action style, the selected index is always 0.
514 combobox_
->SetStyle(Combobox::STYLE_ACTION
);
515 combobox_
->SetSelectedIndex(1);
516 EXPECT_EQ(0, combobox_
->selected_index());
517 combobox_
->SetSelectedIndex(2);
518 EXPECT_EQ(0, combobox_
->selected_index());
519 combobox_
->SetSelectedIndex(3);
520 EXPECT_EQ(0, combobox_
->selected_index());
523 TEST_F(ComboboxTest
, ListenerHandlesDelete
) {
524 TestComboboxModel model
;
526 // |combobox| will be deleted on change.
527 TestCombobox
* combobox
= new TestCombobox(&model
);
528 scoped_ptr
<EvilListener
> evil_listener(new EvilListener());
529 combobox
->set_listener(evil_listener
.get());
530 ASSERT_NO_FATAL_FAILURE(ComboboxTestApi(combobox
).PerformActionAt(2));
531 EXPECT_TRUE(evil_listener
->deleted());
534 // |combobox| will be deleted on change.
535 combobox
= new TestCombobox(&model
);
536 evil_listener
.reset(new EvilListener());
537 combobox
->set_listener(evil_listener
.get());
538 combobox
->SetStyle(Combobox::STYLE_ACTION
);
539 ASSERT_NO_FATAL_FAILURE(ComboboxTestApi(combobox
).PerformActionAt(2));
540 EXPECT_TRUE(evil_listener
->deleted());
543 TEST_F(ComboboxTest
, Click
) {
546 TestComboboxListener listener
;
547 combobox_
->set_listener(&listener
);
550 int menu_show_count
= 0;
551 test_api_
->InstallTestMenuRunner(&menu_show_count
);
553 // Click the left side. The menu is shown.
554 EXPECT_EQ(0, menu_show_count
);
555 PerformClick(gfx::Point(combobox_
->x() + 1,
556 combobox_
->y() + combobox_
->height() / 2));
557 EXPECT_FALSE(listener
.on_perform_action_called());
558 EXPECT_EQ(1, menu_show_count
);
561 TEST_F(ComboboxTest
, ClickButDisabled
) {
564 TestComboboxListener listener
;
565 combobox_
->set_listener(&listener
);
568 combobox_
->SetEnabled(false);
570 // Click the left side, but nothing happens since the combobox is disabled.
571 int menu_show_count
= 0;
572 test_api_
->InstallTestMenuRunner(&menu_show_count
);
573 PerformClick(gfx::Point(combobox_
->x() + 1,
574 combobox_
->y() + combobox_
->height() / 2));
575 EXPECT_FALSE(listener
.on_perform_action_called());
576 EXPECT_EQ(0, menu_show_count
);
579 TEST_F(ComboboxTest
, NotifyOnClickWithReturnKey
) {
582 TestComboboxListener listener
;
583 combobox_
->set_listener(&listener
);
585 // With STYLE_NORMAL, the click event is ignored.
586 SendKeyEvent(ui::VKEY_RETURN
);
587 EXPECT_FALSE(listener
.on_perform_action_called());
589 // With STYLE_ACTION, the click event is notified.
590 combobox_
->SetStyle(Combobox::STYLE_ACTION
);
591 SendKeyEvent(ui::VKEY_RETURN
);
592 EXPECT_TRUE(listener
.on_perform_action_called());
593 EXPECT_EQ(0, listener
.perform_action_index());
596 TEST_F(ComboboxTest
, NotifyOnClickWithSpaceKey
) {
599 TestComboboxListener listener
;
600 combobox_
->set_listener(&listener
);
602 // With STYLE_NORMAL, the click event is ignored.
603 SendKeyEvent(ui::VKEY_SPACE
);
604 EXPECT_FALSE(listener
.on_perform_action_called());
605 SendKeyEventWithType(ui::VKEY_SPACE
, ui::ET_KEY_RELEASED
);
606 EXPECT_FALSE(listener
.on_perform_action_called());
608 // With STYLE_ACTION, the click event is notified after releasing.
609 combobox_
->SetStyle(Combobox::STYLE_ACTION
);
610 SendKeyEvent(ui::VKEY_SPACE
);
611 EXPECT_FALSE(listener
.on_perform_action_called());
612 SendKeyEventWithType(ui::VKEY_SPACE
, ui::ET_KEY_RELEASED
);
613 EXPECT_TRUE(listener
.on_perform_action_called());
614 EXPECT_EQ(0, listener
.perform_action_index());
617 TEST_F(ComboboxTest
, NotifyOnClickWithMouse
) {
620 TestComboboxListener listener
;
621 combobox_
->set_listener(&listener
);
623 combobox_
->SetStyle(Combobox::STYLE_ACTION
);
626 // Click the right side (arrow button). The menu is shown.
627 int menu_show_count
= 0;
628 test_api_
->InstallTestMenuRunner(&menu_show_count
);
629 EXPECT_EQ(0, menu_show_count
);
630 PerformClick(gfx::Point(combobox_
->x() + combobox_
->width() - 1,
631 combobox_
->y() + combobox_
->height() / 2));
632 EXPECT_FALSE(listener
.on_perform_action_called());
633 EXPECT_EQ(1, menu_show_count
);
635 // Click the left side (text button). The click event is notified.
636 test_api_
->InstallTestMenuRunner(&menu_show_count
);
637 PerformClick(gfx::Point(combobox_
->x() + 1,
638 combobox_
->y() + combobox_
->height() / 2));
639 EXPECT_TRUE(listener
.on_perform_action_called());
640 EXPECT_EQ(1, menu_show_count
); // Unchanged.
641 EXPECT_EQ(0, listener
.perform_action_index());
644 TEST_F(ComboboxTest
, ConsumingPressKeyEvents
) {
647 EXPECT_FALSE(combobox_
->OnKeyPressed(
648 ui::KeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_RETURN
, ui::EF_NONE
)));
649 EXPECT_FALSE(combobox_
->OnKeyPressed(
650 ui::KeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_SPACE
, ui::EF_NONE
)));
652 // When the combobox's style is STYLE_ACTION, pressing events of a space key
653 // or an enter key will be consumed.
654 combobox_
->SetStyle(Combobox::STYLE_ACTION
);
655 EXPECT_TRUE(combobox_
->OnKeyPressed(
656 ui::KeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_RETURN
, ui::EF_NONE
)));
657 EXPECT_TRUE(combobox_
->OnKeyPressed(
658 ui::KeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_SPACE
, ui::EF_NONE
)));
661 TEST_F(ComboboxTest
, ContentWidth
) {
662 std::vector
<std::string
> values
;
663 VectorComboboxModel
model(&values
);
664 TestCombobox
combobox(&model
);
665 ComboboxTestApi
test_api(&combobox
);
667 std::string long_item
= "this is the long item";
668 std::string short_item
= "s";
671 values
[0] = long_item
;
672 combobox
.ModelChanged();
674 const int long_item_width
= test_api
.content_size().width();
676 values
[0] = short_item
;
677 combobox
.ModelChanged();
679 const int short_item_width
= test_api
.content_size().width();
682 values
[0] = short_item
;
683 values
[1] = long_item
;
684 combobox
.ModelChanged();
686 // When the style is STYLE_NORMAL, the width will fit with the longest item.
687 combobox
.SetStyle(Combobox::STYLE_NORMAL
);
688 EXPECT_EQ(long_item_width
, test_api
.content_size().width());
690 // When the style is STYLE_ACTION, the width will fit with the selected item's
692 combobox
.SetStyle(Combobox::STYLE_ACTION
);
693 EXPECT_EQ(short_item_width
, test_api
.content_size().width());
696 // Test that model updates preserve the selected index, so long as it is in
698 TEST_F(ComboboxTest
, ModelChanged
) {
699 InitCombobox(nullptr);
701 EXPECT_EQ(0, combobox_
->GetSelectedRow());
702 EXPECT_EQ(10, combobox_
->GetRowCount());
704 combobox_
->SetSelectedIndex(4);
705 EXPECT_EQ(4, combobox_
->GetSelectedRow());
707 model_
->set_item_count(5);
708 combobox_
->ModelChanged();
709 EXPECT_EQ(5, combobox_
->GetRowCount());
710 EXPECT_EQ(4, combobox_
->GetSelectedRow()); // Unchanged.
712 model_
->set_item_count(4);
713 combobox_
->ModelChanged();
714 EXPECT_EQ(4, combobox_
->GetRowCount());
715 EXPECT_EQ(0, combobox_
->GetSelectedRow()); // Resets.
717 // Restore a non-zero selection.
718 combobox_
->SetSelectedIndex(2);
719 EXPECT_EQ(2, combobox_
->GetSelectedRow());
721 // Make the selected index a separator.
722 std::set
<int> separators
;
723 separators
.insert(2);
724 model_
->SetSeparators(separators
);
725 combobox_
->ModelChanged();
726 EXPECT_EQ(4, combobox_
->GetRowCount());
727 EXPECT_EQ(0, combobox_
->GetSelectedRow()); // Resets.
729 // Restore a non-zero selection.
730 combobox_
->SetSelectedIndex(1);
731 EXPECT_EQ(1, combobox_
->GetSelectedRow());
733 // Test an empty model.
734 model_
->set_item_count(0);
735 combobox_
->ModelChanged();
736 EXPECT_EQ(0, combobox_
->GetRowCount());
737 EXPECT_EQ(0, combobox_
->GetSelectedRow()); // Resets.
740 TEST_F(ComboboxTest
, TypingPrefixNotifiesListener
) {
743 TestComboboxListener listener
;
744 combobox_
->set_listener(&listener
);
745 ui::TextInputClient
* input_client
=
746 widget_
->GetInputMethod()->GetTextInputClient();
748 // Type the first character of the second menu item ("JELLY").
749 input_client
->InsertChar('J', ui::EF_NONE
);
750 EXPECT_EQ(1, listener
.actions_performed());
751 EXPECT_EQ(1, listener
.perform_action_index());
753 // Type the second character of "JELLY", item shouldn't change and
754 // OnPerformAction() shouldn't be re-called.
755 input_client
->InsertChar('E', ui::EF_NONE
);
756 EXPECT_EQ(1, listener
.actions_performed());
757 EXPECT_EQ(1, listener
.perform_action_index());
759 // Clears the typed text.
761 combobox_
->RequestFocus();
763 // Type the first character of "PEANUT BUTTER", which should change the
764 // selected index and perform an action.
765 input_client
->InsertChar('P', ui::EF_NONE
);
766 EXPECT_EQ(2, listener
.actions_performed());
767 EXPECT_EQ(2, listener
.perform_action_index());
770 // Test properties on the Combobox menu model.
771 TEST_F(ComboboxTest
, MenuModel
) {
772 const int kSeparatorIndex
= 3;
773 std::set
<int> separators
;
774 separators
.insert(kSeparatorIndex
);
775 InitCombobox(&separators
);
777 ui::MenuModel
* menu_model
= test_api_
->menu_model();
779 EXPECT_EQ(TestComboboxModel::kItemCount
, menu_model
->GetItemCount());
780 EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR
,
781 menu_model
->GetTypeAt(kSeparatorIndex
));
783 #if defined(OS_MACOSX)
784 // Comboboxes on Mac should have checkmarks, with the selected item checked,
785 EXPECT_EQ(ui::MenuModel::TYPE_CHECK
, menu_model
->GetTypeAt(0));
786 EXPECT_EQ(ui::MenuModel::TYPE_CHECK
, menu_model
->GetTypeAt(1));
787 EXPECT_TRUE(menu_model
->IsItemCheckedAt(0));
788 EXPECT_FALSE(menu_model
->IsItemCheckedAt(1));
790 combobox_
->SetSelectedIndex(1);
791 EXPECT_FALSE(menu_model
->IsItemCheckedAt(0));
792 EXPECT_TRUE(menu_model
->IsItemCheckedAt(1));
794 EXPECT_EQ(ui::MenuModel::TYPE_COMMAND
, menu_model
->GetTypeAt(0));
795 EXPECT_EQ(ui::MenuModel::TYPE_COMMAND
, menu_model
->GetTypeAt(1));
798 EXPECT_EQ(ASCIIToUTF16("PEANUT BUTTER"), menu_model
->GetLabelAt(0));
799 EXPECT_EQ(ASCIIToUTF16("JELLY"), menu_model
->GetLabelAt(1));
801 // Check that with STYLE_ACTION, the first item (only) is not shown.
802 EXPECT_TRUE(menu_model
->IsVisibleAt(0));
803 combobox_
->SetStyle(Combobox::STYLE_ACTION
);
804 EXPECT_FALSE(menu_model
->IsVisibleAt(0));
805 EXPECT_TRUE(menu_model
->IsVisibleAt(1));