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. 4 5 #include "ui/views/controls/combobox/combobox.h" 6 7 #include <set> 8 9 #include "base/basictypes.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "ui/base/ime/text_input_client.h" 12 #include "ui/base/models/combobox_model.h" 13 #include "ui/events/event.h" 14 #include "ui/events/event_constants.h" 15 #include "ui/events/keycodes/keyboard_codes.h" 16 #include "ui/views/controls/combobox/combobox_listener.h" 17 #include "ui/views/controls/menu/menu_runner.h" 18 #include "ui/views/controls/menu/menu_runner_handler.h" 19 #include "ui/views/ime/mock_input_method.h" 20 #include "ui/views/test/menu_runner_test_api.h" 21 #include "ui/views/test/views_test_base.h" 22 #include "ui/views/widget/widget.h" 23 24 using base::ASCIIToUTF16; 25 26 namespace views { 27 28 namespace { 29 30 // An dummy implementation of MenuRunnerHandler to check if the dropdown menu is 31 // shown or not. 32 class TestMenuRunnerHandler : public MenuRunnerHandler { 33 public: 34 TestMenuRunnerHandler() : executed_(false) {} 35 36 bool executed() const { return executed_; } 37 38 virtual MenuRunner::RunResult RunMenuAt(Widget* parent, 39 MenuButton* button, 40 const gfx::Rect& bounds, 41 MenuAnchorPosition anchor, 42 ui::MenuSourceType source_type, 43 int32 types) OVERRIDE { 44 executed_ = true; 45 return MenuRunner::NORMAL_EXIT; 46 } 47 48 private: 49 bool executed_; 50 51 DISALLOW_COPY_AND_ASSIGN(TestMenuRunnerHandler); 52 }; 53 54 // A wrapper of Combobox to intercept the result of OnKeyPressed() and 55 // OnKeyReleased() methods. 56 class TestCombobox : public Combobox { 57 public: 58 explicit TestCombobox(ui::ComboboxModel* model) 59 : Combobox(model), 60 key_handled_(false), 61 key_received_(false) {} 62 63 virtual bool OnKeyPressed(const ui::KeyEvent& e) OVERRIDE { 64 key_received_ = true; 65 key_handled_ = Combobox::OnKeyPressed(e); 66 return key_handled_; 67 } 68 69 virtual bool OnKeyReleased(const ui::KeyEvent& e) OVERRIDE { 70 key_received_ = true; 71 key_handled_ = Combobox::OnKeyReleased(e); 72 return key_handled_; 73 } 74 75 bool key_handled() const { return key_handled_; } 76 bool key_received() const { return key_received_; } 77 78 void clear() { 79 key_received_ = key_handled_ = false; 80 } 81 82 private: 83 bool key_handled_; 84 bool key_received_; 85 86 DISALLOW_COPY_AND_ASSIGN(TestCombobox); 87 }; 88 89 // A concrete class is needed to test the combobox. 90 class TestComboboxModel : public ui::ComboboxModel { 91 public: 92 TestComboboxModel() {} 93 virtual ~TestComboboxModel() {} 94 95 static const int kItemCount = 10; 96 97 // ui::ComboboxModel: 98 virtual int GetItemCount() const OVERRIDE { 99 return kItemCount; 100 } 101 virtual base::string16 GetItemAt(int index) OVERRIDE { 102 if (IsItemSeparatorAt(index)) { 103 NOTREACHED(); 104 return ASCIIToUTF16("SEPARATOR"); 105 } 106 return ASCIIToUTF16(index % 2 == 0 ? "PEANUT BUTTER" : "JELLY"); 107 } 108 virtual bool IsItemSeparatorAt(int index) OVERRIDE { 109 return separators_.find(index) != separators_.end(); 110 } 111 112 virtual int GetDefaultIndex() const OVERRIDE { 113 // Return the first index that is not a separator. 114 for (int index = 0; index < kItemCount; ++index) { 115 if (separators_.find(index) == separators_.end()) 116 return index; 117 } 118 NOTREACHED(); 119 return 0; 120 } 121 122 void SetSeparators(const std::set<int>& separators) { 123 separators_ = separators; 124 } 125 126 private: 127 std::set<int> separators_; 128 129 DISALLOW_COPY_AND_ASSIGN(TestComboboxModel); 130 }; 131 132 // A combobox model which refers to a vector. 133 class VectorComboboxModel : public ui::ComboboxModel { 134 public: 135 explicit VectorComboboxModel(std::vector<std::string>* values) 136 : values_(values) {} 137 virtual ~VectorComboboxModel() {} 138 139 // ui::ComboboxModel: 140 virtual int GetItemCount() const OVERRIDE { 141 return (int)values_->size(); 142 } 143 virtual base::string16 GetItemAt(int index) OVERRIDE { 144 return ASCIIToUTF16(values_->at(index)); 145 } 146 virtual bool IsItemSeparatorAt(int index) OVERRIDE { 147 return false; 148 } 149 150 private: 151 std::vector<std::string>* values_; 152 }; 153 154 class EvilListener : public ComboboxListener { 155 public: 156 EvilListener() : deleted_(false) {} 157 virtual ~EvilListener() {}; 158 159 // ComboboxListener: 160 virtual void OnPerformAction(Combobox* combobox) OVERRIDE { 161 delete combobox; 162 deleted_ = true; 163 } 164 165 bool deleted() const { return deleted_; } 166 167 private: 168 bool deleted_; 169 170 DISALLOW_COPY_AND_ASSIGN(EvilListener); 171 }; 172 173 class TestComboboxListener : public views::ComboboxListener { 174 public: 175 TestComboboxListener() : perform_action_index_(-1), actions_performed_(0) {} 176 virtual ~TestComboboxListener() {} 177 178 virtual void OnPerformAction(views::Combobox* combobox) OVERRIDE { 179 perform_action_index_ = combobox->selected_index(); 180 actions_performed_++; 181 } 182 183 int perform_action_index() const { 184 return perform_action_index_; 185 } 186 187 bool on_perform_action_called() const { 188 return actions_performed_ > 0; 189 } 190 191 int actions_performed() const { 192 return actions_performed_; 193 } 194 195 private: 196 int perform_action_index_; 197 int actions_performed_; 198 199 private: 200 DISALLOW_COPY_AND_ASSIGN(TestComboboxListener); 201 }; 202 203 } // namespace 204 205 class ComboboxTest : public ViewsTestBase { 206 public: 207 ComboboxTest() : widget_(NULL), combobox_(NULL) {} 208 209 virtual void TearDown() OVERRIDE { 210 if (widget_) 211 widget_->Close(); 212 ViewsTestBase::TearDown(); 213 } 214 215 void InitCombobox(const std::set<int>* separators) { 216 model_.reset(new TestComboboxModel()); 217 218 if (separators) 219 model_->SetSeparators(*separators); 220 221 ASSERT_FALSE(combobox_); 222 combobox_ = new TestCombobox(model_.get()); 223 combobox_->set_id(1); 224 225 widget_ = new Widget; 226 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 227 params.bounds = gfx::Rect(200, 200, 200, 200); 228 widget_->Init(params); 229 View* container = new View(); 230 widget_->SetContentsView(container); 231 container->AddChildView(combobox_); 232 233 widget_->ReplaceInputMethod(new MockInputMethod); 234 235 // Assumes the Widget is always focused. 236 widget_->GetInputMethod()->OnFocus(); 237 238 combobox_->RequestFocus(); 239 combobox_->SizeToPreferredSize(); 240 } 241 242 protected: 243 void SendKeyEvent(ui::KeyboardCode key_code) { 244 SendKeyEventWithType(key_code, ui::ET_KEY_PRESSED); 245 } 246 247 void SendKeyEventWithType(ui::KeyboardCode key_code, ui::EventType type) { 248 ui::KeyEvent event(type, key_code, ui::EF_NONE); 249 widget_->GetInputMethod()->DispatchKeyEvent(event); 250 } 251 252 View* GetFocusedView() { 253 return widget_->GetFocusManager()->GetFocusedView(); 254 } 255 256 void PerformClick(const gfx::Point& point) { 257 ui::MouseEvent pressed_event = ui::MouseEvent(ui::ET_MOUSE_PRESSED, point, 258 point, 259 ui::EF_LEFT_MOUSE_BUTTON, 260 ui::EF_LEFT_MOUSE_BUTTON); 261 widget_->OnMouseEvent(&pressed_event); 262 ui::MouseEvent released_event = ui::MouseEvent(ui::ET_MOUSE_RELEASED, point, 263 point, 264 ui::EF_LEFT_MOUSE_BUTTON, 265 ui::EF_LEFT_MOUSE_BUTTON); 266 widget_->OnMouseEvent(&released_event); 267 } 268 269 // We need widget to populate wrapper class. 270 Widget* widget_; 271 272 // |combobox_| will be allocated InitCombobox() and then owned by |widget_|. 273 TestCombobox* combobox_; 274 275 // Combobox does not take ownership of the model, hence it needs to be scoped. 276 scoped_ptr<TestComboboxModel> model_; 277 }; 278 279 TEST_F(ComboboxTest, KeyTest) { 280 InitCombobox(NULL); 281 SendKeyEvent(ui::VKEY_END); 282 EXPECT_EQ(combobox_->selected_index() + 1, model_->GetItemCount()); 283 SendKeyEvent(ui::VKEY_HOME); 284 EXPECT_EQ(combobox_->selected_index(), 0); 285 SendKeyEvent(ui::VKEY_DOWN); 286 SendKeyEvent(ui::VKEY_DOWN); 287 EXPECT_EQ(combobox_->selected_index(), 2); 288 SendKeyEvent(ui::VKEY_RIGHT); 289 EXPECT_EQ(combobox_->selected_index(), 2); 290 SendKeyEvent(ui::VKEY_LEFT); 291 EXPECT_EQ(combobox_->selected_index(), 2); 292 SendKeyEvent(ui::VKEY_UP); 293 EXPECT_EQ(combobox_->selected_index(), 1); 294 SendKeyEvent(ui::VKEY_PRIOR); 295 EXPECT_EQ(combobox_->selected_index(), 0); 296 SendKeyEvent(ui::VKEY_NEXT); 297 EXPECT_EQ(combobox_->selected_index(), model_->GetItemCount() - 1); 298 } 299 300 // Check that if a combobox is disabled before it has a native wrapper, then the 301 // native wrapper inherits the disabled state when it gets created. 302 TEST_F(ComboboxTest, DisabilityTest) { 303 model_.reset(new TestComboboxModel()); 304 305 ASSERT_FALSE(combobox_); 306 combobox_ = new TestCombobox(model_.get()); 307 combobox_->SetEnabled(false); 308 309 widget_ = new Widget; 310 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); 311 params.bounds = gfx::Rect(100, 100, 100, 100); 312 widget_->Init(params); 313 View* container = new View(); 314 widget_->SetContentsView(container); 315 container->AddChildView(combobox_); 316 EXPECT_FALSE(combobox_->enabled()); 317 } 318 319 // Verifies that we don't select a separator line in combobox when navigating 320 // through keyboard. 321 TEST_F(ComboboxTest, SkipSeparatorSimple) { 322 std::set<int> separators; 323 separators.insert(2); 324 InitCombobox(&separators); 325 EXPECT_EQ(0, combobox_->selected_index()); 326 SendKeyEvent(ui::VKEY_DOWN); 327 EXPECT_EQ(1, combobox_->selected_index()); 328 SendKeyEvent(ui::VKEY_DOWN); 329 EXPECT_EQ(3, combobox_->selected_index()); 330 SendKeyEvent(ui::VKEY_UP); 331 EXPECT_EQ(1, combobox_->selected_index()); 332 SendKeyEvent(ui::VKEY_HOME); 333 EXPECT_EQ(0, combobox_->selected_index()); 334 SendKeyEvent(ui::VKEY_PRIOR); 335 EXPECT_EQ(0, combobox_->selected_index()); 336 SendKeyEvent(ui::VKEY_END); 337 EXPECT_EQ(9, combobox_->selected_index()); 338 } 339 340 // Verifies that we never select the separator that is in the beginning of the 341 // combobox list when navigating through keyboard. 342 TEST_F(ComboboxTest, SkipSeparatorBeginning) { 343 std::set<int> separators; 344 separators.insert(0); 345 InitCombobox(&separators); 346 EXPECT_EQ(1, combobox_->selected_index()); 347 SendKeyEvent(ui::VKEY_DOWN); 348 EXPECT_EQ(2, combobox_->selected_index()); 349 SendKeyEvent(ui::VKEY_DOWN); 350 EXPECT_EQ(3, combobox_->selected_index()); 351 SendKeyEvent(ui::VKEY_UP); 352 EXPECT_EQ(2, combobox_->selected_index()); 353 SendKeyEvent(ui::VKEY_HOME); 354 EXPECT_EQ(1, combobox_->selected_index()); 355 SendKeyEvent(ui::VKEY_PRIOR); 356 EXPECT_EQ(1, combobox_->selected_index()); 357 SendKeyEvent(ui::VKEY_END); 358 EXPECT_EQ(9, combobox_->selected_index()); 359 } 360 361 // Verifies that we never select the separator that is in the end of the 362 // combobox list when navigating through keyboard. 363 TEST_F(ComboboxTest, SkipSeparatorEnd) { 364 std::set<int> separators; 365 separators.insert(TestComboboxModel::kItemCount - 1); 366 InitCombobox(&separators); 367 combobox_->SetSelectedIndex(8); 368 SendKeyEvent(ui::VKEY_DOWN); 369 EXPECT_EQ(8, combobox_->selected_index()); 370 SendKeyEvent(ui::VKEY_UP); 371 EXPECT_EQ(7, combobox_->selected_index()); 372 SendKeyEvent(ui::VKEY_END); 373 EXPECT_EQ(8, combobox_->selected_index()); 374 } 375 376 // Verifies that we never select any of the adjacent separators (multiple 377 // consecutive) that appear in the beginning of the combobox list when 378 // navigating through keyboard. 379 TEST_F(ComboboxTest, SkipMultipleSeparatorsAtBeginning) { 380 std::set<int> separators; 381 separators.insert(0); 382 separators.insert(1); 383 separators.insert(2); 384 InitCombobox(&separators); 385 EXPECT_EQ(3, combobox_->selected_index()); 386 SendKeyEvent(ui::VKEY_DOWN); 387 EXPECT_EQ(4, combobox_->selected_index()); 388 SendKeyEvent(ui::VKEY_UP); 389 EXPECT_EQ(3, combobox_->selected_index()); 390 SendKeyEvent(ui::VKEY_NEXT); 391 EXPECT_EQ(9, combobox_->selected_index()); 392 SendKeyEvent(ui::VKEY_HOME); 393 EXPECT_EQ(3, combobox_->selected_index()); 394 SendKeyEvent(ui::VKEY_END); 395 EXPECT_EQ(9, combobox_->selected_index()); 396 SendKeyEvent(ui::VKEY_PRIOR); 397 EXPECT_EQ(3, combobox_->selected_index()); 398 } 399 400 // Verifies that we never select any of the adjacent separators (multiple 401 // consecutive) that appear in the middle of the combobox list when navigating 402 // through keyboard. 403 TEST_F(ComboboxTest, SkipMultipleAdjacentSeparatorsAtMiddle) { 404 std::set<int> separators; 405 separators.insert(4); 406 separators.insert(5); 407 separators.insert(6); 408 InitCombobox(&separators); 409 combobox_->SetSelectedIndex(3); 410 SendKeyEvent(ui::VKEY_DOWN); 411 EXPECT_EQ(7, combobox_->selected_index()); 412 SendKeyEvent(ui::VKEY_UP); 413 EXPECT_EQ(3, combobox_->selected_index()); 414 } 415 416 // Verifies that we never select any of the adjacent separators (multiple 417 // consecutive) that appear in the end of the combobox list when navigating 418 // through keyboard. 419 TEST_F(ComboboxTest, SkipMultipleSeparatorsAtEnd) { 420 std::set<int> separators; 421 separators.insert(7); 422 separators.insert(8); 423 separators.insert(9); 424 InitCombobox(&separators); 425 combobox_->SetSelectedIndex(6); 426 SendKeyEvent(ui::VKEY_DOWN); 427 EXPECT_EQ(6, combobox_->selected_index()); 428 SendKeyEvent(ui::VKEY_UP); 429 EXPECT_EQ(5, combobox_->selected_index()); 430 SendKeyEvent(ui::VKEY_HOME); 431 EXPECT_EQ(0, combobox_->selected_index()); 432 SendKeyEvent(ui::VKEY_NEXT); 433 EXPECT_EQ(6, combobox_->selected_index()); 434 SendKeyEvent(ui::VKEY_PRIOR); 435 EXPECT_EQ(0, combobox_->selected_index()); 436 SendKeyEvent(ui::VKEY_END); 437 EXPECT_EQ(6, combobox_->selected_index()); 438 } 439 440 TEST_F(ComboboxTest, GetTextForRowTest) { 441 std::set<int> separators; 442 separators.insert(0); 443 separators.insert(1); 444 separators.insert(9); 445 InitCombobox(&separators); 446 for (int i = 0; i < combobox_->GetRowCount(); ++i) { 447 if (separators.count(i) != 0) { 448 EXPECT_TRUE(combobox_->GetTextForRow(i).empty()) << i; 449 } else { 450 EXPECT_EQ(ASCIIToUTF16(i % 2 == 0 ? "PEANUT BUTTER" : "JELLY"), 451 combobox_->GetTextForRow(i)) << i; 452 } 453 } 454 } 455 456 // Verifies selecting the first matching value (and returning whether found). 457 TEST_F(ComboboxTest, SelectValue) { 458 InitCombobox(NULL); 459 ASSERT_EQ(model_->GetDefaultIndex(), combobox_->selected_index()); 460 EXPECT_TRUE(combobox_->SelectValue(ASCIIToUTF16("PEANUT BUTTER"))); 461 EXPECT_EQ(0, combobox_->selected_index()); 462 EXPECT_TRUE(combobox_->SelectValue(ASCIIToUTF16("JELLY"))); 463 EXPECT_EQ(1, combobox_->selected_index()); 464 EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("BANANAS"))); 465 EXPECT_EQ(1, combobox_->selected_index()); 466 467 // With the action style, the selected index is always 0. 468 combobox_->SetStyle(Combobox::STYLE_ACTION); 469 EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("PEANUT BUTTER"))); 470 EXPECT_EQ(0, combobox_->selected_index()); 471 EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("JELLY"))); 472 EXPECT_EQ(0, combobox_->selected_index()); 473 EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("BANANAS"))); 474 EXPECT_EQ(0, combobox_->selected_index()); 475 } 476 477 TEST_F(ComboboxTest, SelectIndexActionStyle) { 478 InitCombobox(NULL); 479 480 // With the action style, the selected index is always 0. 481 combobox_->SetStyle(Combobox::STYLE_ACTION); 482 combobox_->SetSelectedIndex(1); 483 EXPECT_EQ(0, combobox_->selected_index()); 484 combobox_->SetSelectedIndex(2); 485 EXPECT_EQ(0, combobox_->selected_index()); 486 combobox_->SetSelectedIndex(3); 487 EXPECT_EQ(0, combobox_->selected_index()); 488 } 489 490 TEST_F(ComboboxTest, ListenerHandlesDelete) { 491 TestComboboxModel model; 492 493 // |combobox| will be deleted on change. 494 TestCombobox* combobox = new TestCombobox(&model); 495 scoped_ptr<EvilListener> evil_listener(new EvilListener()); 496 combobox->set_listener(evil_listener.get()); 497 ASSERT_NO_FATAL_FAILURE(combobox->ExecuteCommand(2)); 498 EXPECT_TRUE(evil_listener->deleted()); 499 500 // With STYLE_ACTION 501 // |combobox| will be deleted on change. 502 combobox = new TestCombobox(&model); 503 evil_listener.reset(new EvilListener()); 504 combobox->set_listener(evil_listener.get()); 505 combobox->SetStyle(Combobox::STYLE_ACTION); 506 ASSERT_NO_FATAL_FAILURE(combobox->ExecuteCommand(2)); 507 EXPECT_TRUE(evil_listener->deleted()); 508 } 509 510 TEST_F(ComboboxTest, Click) { 511 InitCombobox(NULL); 512 513 TestComboboxListener listener; 514 combobox_->set_listener(&listener); 515 516 combobox_->Layout(); 517 518 // Click the left side. The menu is shown. 519 TestMenuRunnerHandler* test_menu_runner_handler = new TestMenuRunnerHandler(); 520 scoped_ptr<MenuRunnerHandler> menu_runner_handler(test_menu_runner_handler); 521 test::MenuRunnerTestAPI test_api( 522 combobox_->dropdown_list_menu_runner_.get()); 523 test_api.SetMenuRunnerHandler(menu_runner_handler.Pass()); 524 PerformClick(gfx::Point(combobox_->x() + 1, 525 combobox_->y() + combobox_->height() / 2)); 526 EXPECT_FALSE(listener.on_perform_action_called()); 527 EXPECT_TRUE(test_menu_runner_handler->executed()); 528 } 529 530 TEST_F(ComboboxTest, ClickButDisabled) { 531 InitCombobox(NULL); 532 533 TestComboboxListener listener; 534 combobox_->set_listener(&listener); 535 536 combobox_->Layout(); 537 combobox_->SetEnabled(false); 538 539 // Click the left side, but nothing happens since the combobox is disabled. 540 TestMenuRunnerHandler* test_menu_runner_handler = new TestMenuRunnerHandler(); 541 scoped_ptr<MenuRunnerHandler> menu_runner_handler(test_menu_runner_handler); 542 test::MenuRunnerTestAPI test_api( 543 combobox_->dropdown_list_menu_runner_.get()); 544 test_api.SetMenuRunnerHandler(menu_runner_handler.Pass()); 545 PerformClick(gfx::Point(combobox_->x() + 1, 546 combobox_->y() + combobox_->height() / 2)); 547 EXPECT_FALSE(listener.on_perform_action_called()); 548 EXPECT_FALSE(test_menu_runner_handler->executed()); 549 } 550 551 TEST_F(ComboboxTest, NotifyOnClickWithReturnKey) { 552 InitCombobox(NULL); 553 554 TestComboboxListener listener; 555 combobox_->set_listener(&listener); 556 557 // With STYLE_NORMAL, the click event is ignored. 558 SendKeyEvent(ui::VKEY_RETURN); 559 EXPECT_FALSE(listener.on_perform_action_called()); 560 561 // With STYLE_ACTION, the click event is notified. 562 combobox_->SetStyle(Combobox::STYLE_ACTION); 563 SendKeyEvent(ui::VKEY_RETURN); 564 EXPECT_TRUE(listener.on_perform_action_called()); 565 EXPECT_EQ(0, listener.perform_action_index()); 566 } 567 568 TEST_F(ComboboxTest, NotifyOnClickWithSpaceKey) { 569 InitCombobox(NULL); 570 571 TestComboboxListener listener; 572 combobox_->set_listener(&listener); 573 574 // With STYLE_NORMAL, the click event is ignored. 575 SendKeyEvent(ui::VKEY_SPACE); 576 EXPECT_FALSE(listener.on_perform_action_called()); 577 SendKeyEventWithType(ui::VKEY_SPACE, ui::ET_KEY_RELEASED); 578 EXPECT_FALSE(listener.on_perform_action_called()); 579 580 // With STYLE_ACTION, the click event is notified after releasing. 581 combobox_->SetStyle(Combobox::STYLE_ACTION); 582 SendKeyEvent(ui::VKEY_SPACE); 583 EXPECT_FALSE(listener.on_perform_action_called()); 584 SendKeyEventWithType(ui::VKEY_SPACE, ui::ET_KEY_RELEASED); 585 EXPECT_TRUE(listener.on_perform_action_called()); 586 EXPECT_EQ(0, listener.perform_action_index()); 587 } 588 589 TEST_F(ComboboxTest, NotifyOnClickWithMouse) { 590 InitCombobox(NULL); 591 592 TestComboboxListener listener; 593 combobox_->set_listener(&listener); 594 595 combobox_->SetStyle(Combobox::STYLE_ACTION); 596 combobox_->Layout(); 597 598 // Click the right side (arrow button). The menu is shown. 599 TestMenuRunnerHandler* test_menu_runner_handler = new TestMenuRunnerHandler(); 600 scoped_ptr<MenuRunnerHandler> menu_runner_handler(test_menu_runner_handler); 601 scoped_ptr<test::MenuRunnerTestAPI> test_api( 602 new test::MenuRunnerTestAPI(combobox_->dropdown_list_menu_runner_.get())); 603 test_api->SetMenuRunnerHandler(menu_runner_handler.Pass()); 604 605 PerformClick(gfx::Point(combobox_->x() + combobox_->width() - 1, 606 combobox_->y() + combobox_->height() / 2)); 607 EXPECT_FALSE(listener.on_perform_action_called()); 608 EXPECT_TRUE(test_menu_runner_handler->executed()); 609 610 // Click the left side (text button). The click event is notified. 611 test_menu_runner_handler = new TestMenuRunnerHandler(); 612 menu_runner_handler.reset(test_menu_runner_handler); 613 test_api.reset( 614 new test::MenuRunnerTestAPI(combobox_->dropdown_list_menu_runner_.get())); 615 test_api->SetMenuRunnerHandler(menu_runner_handler.Pass()); 616 PerformClick(gfx::Point(combobox_->x() + 1, 617 combobox_->y() + combobox_->height() / 2)); 618 EXPECT_TRUE(listener.on_perform_action_called()); 619 EXPECT_FALSE(test_menu_runner_handler->executed()); 620 EXPECT_EQ(0, listener.perform_action_index()); 621 } 622 623 TEST_F(ComboboxTest, ConsumingPressKeyEvents) { 624 InitCombobox(NULL); 625 626 EXPECT_FALSE(combobox_->OnKeyPressed( 627 ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE))); 628 EXPECT_FALSE(combobox_->OnKeyPressed( 629 ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, ui::EF_NONE))); 630 631 // When the combobox's style is STYLE_ACTION, pressing events of a space key 632 // or an enter key will be consumed. 633 combobox_->SetStyle(Combobox::STYLE_ACTION); 634 EXPECT_TRUE(combobox_->OnKeyPressed( 635 ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE))); 636 EXPECT_TRUE(combobox_->OnKeyPressed( 637 ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, ui::EF_NONE))); 638 } 639 640 TEST_F(ComboboxTest, ContentWidth) { 641 std::vector<std::string> values; 642 VectorComboboxModel model(&values); 643 TestCombobox combobox(&model); 644 645 std::string long_item = "this is the long item"; 646 std::string short_item = "s"; 647 648 values.resize(1); 649 values[0] = long_item; 650 combobox.ModelChanged(); 651 652 const int long_item_width = combobox.content_size_.width(); 653 654 values[0] = short_item; 655 combobox.ModelChanged(); 656 657 const int short_item_width = combobox.content_size_.width(); 658 659 values.resize(2); 660 values[0] = short_item; 661 values[1] = long_item; 662 combobox.ModelChanged(); 663 664 // When the style is STYLE_NORMAL, the width will fit with the longest item. 665 combobox.SetStyle(Combobox::STYLE_NORMAL); 666 EXPECT_EQ(long_item_width, combobox.content_size_.width()); 667 668 // When the style is STYLE_ACTION, the width will fit with the first items' 669 // width. 670 combobox.SetStyle(Combobox::STYLE_ACTION); 671 EXPECT_EQ(short_item_width, combobox.content_size_.width()); 672 } 673 674 TEST_F(ComboboxTest, TypingPrefixNotifiesListener) { 675 InitCombobox(NULL); 676 677 TestComboboxListener listener; 678 combobox_->set_listener(&listener); 679 680 // Type the first character of the second menu item ("JELLY"). 681 combobox_->GetTextInputClient()->InsertChar('J', ui::EF_NONE); 682 EXPECT_EQ(1, listener.actions_performed()); 683 EXPECT_EQ(1, listener.perform_action_index()); 684 685 // Type the second character of "JELLY", item shouldn't change and 686 // OnPerformAction() shouldn't be re-called. 687 combobox_->GetTextInputClient()->InsertChar('E', ui::EF_NONE); 688 EXPECT_EQ(1, listener.actions_performed()); 689 EXPECT_EQ(1, listener.perform_action_index()); 690 691 // Clears the typed text. 692 combobox_->OnBlur(); 693 694 // Type the first character of "PEANUT BUTTER", which should change the 695 // selected index and perform an action. 696 combobox_->GetTextInputClient()->InsertChar('P', ui::EF_NONE); 697 EXPECT_EQ(2, listener.actions_performed()); 698 EXPECT_EQ(2, listener.perform_action_index()); 699 } 700 701 } // namespace views 702