| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |