OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/views/controls/combobox/combobox.h" | 5 #include "ui/views/controls/combobox/combobox.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 | 8 |
9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
11 #include "ui/base/models/combobox_model.h" | 11 #include "ui/base/models/combobox_model.h" |
12 #include "ui/events/event.h" | 12 #include "ui/events/event.h" |
13 #include "ui/events/keycodes/keyboard_codes.h" | 13 #include "ui/events/keycodes/keyboard_codes.h" |
14 #include "ui/views/controls/combobox/combobox_listener.h" | 14 #include "ui/views/controls/combobox/combobox_listener.h" |
| 15 #include "ui/views/controls/menu/menu_runner.h" |
| 16 #include "ui/views/controls/menu/menu_runner_handler.h" |
15 #include "ui/views/ime/mock_input_method.h" | 17 #include "ui/views/ime/mock_input_method.h" |
| 18 #include "ui/views/test/menu_runner_test_api.h" |
16 #include "ui/views/test/views_test_base.h" | 19 #include "ui/views/test/views_test_base.h" |
17 #include "ui/views/widget/widget.h" | 20 #include "ui/views/widget/widget.h" |
18 | 21 |
19 namespace views { | 22 namespace views { |
20 | 23 |
21 namespace { | 24 namespace { |
22 | 25 |
| 26 // An dummy implementation of MenuRunnerHandler to check if the dropdown menu is |
| 27 // shown or not. |
| 28 class TestMenuRunnerHandler : public MenuRunnerHandler { |
| 29 public: |
| 30 TestMenuRunnerHandler() |
| 31 : executed_(false) {} |
| 32 |
| 33 bool executed() const { return executed_; } |
| 34 |
| 35 virtual MenuRunner::RunResult RunMenuAt(Widget* parent, |
| 36 MenuButton* button, |
| 37 const gfx::Rect& bounds, |
| 38 MenuItemView::AnchorPosition anchor, |
| 39 ui::MenuSourceType source_type, |
| 40 int32 types) OVERRIDE { |
| 41 executed_ = true; |
| 42 return MenuRunner::NORMAL_EXIT; |
| 43 } |
| 44 |
| 45 private: |
| 46 bool executed_; |
| 47 |
| 48 DISALLOW_COPY_AND_ASSIGN(TestMenuRunnerHandler); |
| 49 }; |
| 50 |
23 // A wrapper of Combobox to intercept the result of OnKeyPressed() and | 51 // A wrapper of Combobox to intercept the result of OnKeyPressed() and |
24 // OnKeyReleased() methods. | 52 // OnKeyReleased() methods. |
25 class TestCombobox : public Combobox { | 53 class TestCombobox : public Combobox { |
26 public: | 54 public: |
27 explicit TestCombobox(ui::ComboboxModel* model) | 55 explicit TestCombobox(ui::ComboboxModel* model) |
28 : Combobox(model), | 56 : Combobox(model), |
29 key_handled_(false), | 57 key_handled_(false), |
30 key_received_(false) { | 58 key_received_(false) { |
31 } | 59 } |
32 | 60 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 } | 127 } |
100 | 128 |
101 bool deleted() const { return deleted_; } | 129 bool deleted() const { return deleted_; } |
102 | 130 |
103 private: | 131 private: |
104 bool deleted_; | 132 bool deleted_; |
105 | 133 |
106 DISALLOW_COPY_AND_ASSIGN(EvilListener); | 134 DISALLOW_COPY_AND_ASSIGN(EvilListener); |
107 }; | 135 }; |
108 | 136 |
| 137 class TestComboboxListener : public views::ComboboxListener { |
| 138 public: |
| 139 TestComboboxListener() |
| 140 : on_selected_index_changed_called_(false), |
| 141 on_combobox_text_button_clicked_called_(false) { |
| 142 } |
| 143 virtual ~TestComboboxListener() {} |
| 144 |
| 145 virtual void OnSelectedIndexChanged(views::Combobox* combobox) OVERRIDE { |
| 146 on_selected_index_changed_called_ = true; |
| 147 } |
| 148 |
| 149 virtual void OnComboboxTextButtonClicked(views::Combobox* combobox) OVERRIDE { |
| 150 on_combobox_text_button_clicked_called_ = true; |
| 151 } |
| 152 |
| 153 bool on_selected_index_changed_called() const { |
| 154 return on_selected_index_changed_called_; |
| 155 } |
| 156 |
| 157 bool on_combobox_text_button_clicked_called() const { |
| 158 return on_combobox_text_button_clicked_called_; |
| 159 } |
| 160 |
| 161 private: |
| 162 bool on_selected_index_changed_called_; |
| 163 bool on_combobox_text_button_clicked_called_; |
| 164 |
| 165 private: |
| 166 DISALLOW_COPY_AND_ASSIGN(TestComboboxListener); |
| 167 }; |
| 168 |
109 } // namespace | 169 } // namespace |
110 | 170 |
111 class ComboboxTest : public ViewsTestBase { | 171 class ComboboxTest : public ViewsTestBase { |
112 public: | 172 public: |
113 ComboboxTest() : widget_(NULL), combobox_(NULL), input_method_(NULL) {} | 173 ComboboxTest() : widget_(NULL), combobox_(NULL), input_method_(NULL) {} |
114 | 174 |
115 virtual void TearDown() OVERRIDE { | 175 virtual void TearDown() OVERRIDE { |
116 if (widget_) | 176 if (widget_) |
117 widget_->Close(); | 177 widget_->Close(); |
118 ViewsTestBase::TearDown(); | 178 ViewsTestBase::TearDown(); |
119 } | 179 } |
120 | 180 |
121 void InitCombobox() { | 181 void InitCombobox() { |
122 model_.reset(new TestComboboxModel()); | 182 model_.reset(new TestComboboxModel()); |
123 | 183 |
124 ASSERT_FALSE(combobox_); | 184 ASSERT_FALSE(combobox_); |
125 combobox_ = new TestCombobox(model_.get()); | 185 combobox_ = new TestCombobox(model_.get()); |
126 combobox_->set_id(1); | 186 combobox_->set_id(1); |
127 | 187 |
128 widget_ = new Widget; | 188 widget_ = new Widget; |
129 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); | 189 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); |
130 params.bounds = gfx::Rect(100, 100, 100, 100); | 190 params.bounds = gfx::Rect(200, 200, 200, 200); |
131 widget_->Init(params); | 191 widget_->Init(params); |
132 View* container = new View(); | 192 View* container = new View(); |
133 widget_->SetContentsView(container); | 193 widget_->SetContentsView(container); |
134 container->AddChildView(combobox_); | 194 container->AddChildView(combobox_); |
135 | 195 |
136 input_method_ = new MockInputMethod(); | 196 input_method_ = new MockInputMethod(); |
137 widget_->ReplaceInputMethod(input_method_); | 197 widget_->ReplaceInputMethod(input_method_); |
138 | 198 |
139 // Assumes the Widget is always focused. | 199 // Assumes the Widget is always focused. |
140 input_method_->OnFocus(); | 200 input_method_->OnFocus(); |
141 | 201 |
142 combobox_->RequestFocus(); | 202 combobox_->RequestFocus(); |
| 203 combobox_->SizeToPreferredSize(); |
143 } | 204 } |
144 | 205 |
145 protected: | 206 protected: |
146 void SendKeyEvent(ui::KeyboardCode key_code) { | 207 void SendKeyEvent(ui::KeyboardCode key_code) { |
147 ui::KeyEvent event(ui::ET_KEY_PRESSED, key_code, 0, false); | 208 SendKeyEventWithType(key_code, ui::ET_KEY_PRESSED); |
| 209 } |
| 210 |
| 211 void SendKeyEventWithType(ui::KeyboardCode key_code, ui::EventType type) { |
| 212 ui::KeyEvent event(type, key_code, 0, false); |
148 input_method_->DispatchKeyEvent(event); | 213 input_method_->DispatchKeyEvent(event); |
149 } | 214 } |
150 | 215 |
151 View* GetFocusedView() { | 216 View* GetFocusedView() { |
152 return widget_->GetFocusManager()->GetFocusedView(); | 217 return widget_->GetFocusManager()->GetFocusedView(); |
153 } | 218 } |
154 | 219 |
| 220 void PerformClick(const gfx::Point& point) { |
| 221 ui::MouseEvent pressed_event = ui::MouseEvent(ui::ET_MOUSE_PRESSED, point, |
| 222 point, |
| 223 ui::EF_LEFT_MOUSE_BUTTON); |
| 224 widget_->OnMouseEvent(&pressed_event); |
| 225 ui::MouseEvent released_event = ui::MouseEvent(ui::ET_MOUSE_RELEASED, point, |
| 226 point, |
| 227 ui::EF_LEFT_MOUSE_BUTTON); |
| 228 widget_->OnMouseEvent(&released_event); |
| 229 } |
| 230 |
155 // We need widget to populate wrapper class. | 231 // We need widget to populate wrapper class. |
156 Widget* widget_; | 232 Widget* widget_; |
157 | 233 |
158 // |combobox_| will be allocated InitCombobox() and then owned by |widget_|. | 234 // |combobox_| will be allocated InitCombobox() and then owned by |widget_|. |
159 TestCombobox* combobox_; | 235 TestCombobox* combobox_; |
160 | 236 |
161 // Combobox does not take ownership of the model, hence it needs to be scoped. | 237 // Combobox does not take ownership of the model, hence it needs to be scoped. |
162 scoped_ptr<TestComboboxModel> model_; | 238 scoped_ptr<TestComboboxModel> model_; |
163 | 239 |
164 // For testing input method related behaviors. | 240 // For testing input method related behaviors. |
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
363 | 439 |
364 TEST_F(ComboboxTest, ListenerHandlesDelete) { | 440 TEST_F(ComboboxTest, ListenerHandlesDelete) { |
365 TestComboboxModel model; | 441 TestComboboxModel model; |
366 TestCombobox* combobox = new TestCombobox(&model); // Deleted on change. | 442 TestCombobox* combobox = new TestCombobox(&model); // Deleted on change. |
367 EvilListener evil_listener; | 443 EvilListener evil_listener; |
368 combobox->set_listener(&evil_listener); | 444 combobox->set_listener(&evil_listener); |
369 ASSERT_NO_FATAL_FAILURE(combobox->ExecuteCommand(2)); | 445 ASSERT_NO_FATAL_FAILURE(combobox->ExecuteCommand(2)); |
370 EXPECT_TRUE(evil_listener.deleted()); | 446 EXPECT_TRUE(evil_listener.deleted()); |
371 } | 447 } |
372 | 448 |
| 449 TEST_F(ComboboxTest, Click) { |
| 450 InitCombobox(); |
| 451 |
| 452 TestComboboxListener listener; |
| 453 combobox_->set_listener(&listener); |
| 454 |
| 455 combobox_->Layout(); |
| 456 |
| 457 // Click the left side. The menu is shown. |
| 458 TestMenuRunnerHandler* test_menu_runner_handler = new TestMenuRunnerHandler(); |
| 459 scoped_ptr<MenuRunnerHandler> menu_runner_handler(test_menu_runner_handler); |
| 460 test::MenuRunnerTestAPI test_api( |
| 461 combobox_->dropdown_list_menu_runner_.get()); |
| 462 test_api.SetMenuRunnerHandler(menu_runner_handler.Pass()); |
| 463 PerformClick(gfx::Point(combobox_->x() + 1, |
| 464 combobox_->y() + combobox_->height() / 2)); |
| 465 EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); |
| 466 EXPECT_TRUE(test_menu_runner_handler->executed()); |
| 467 } |
| 468 |
| 469 TEST_F(ComboboxTest, NotifyOnClickWithReturnKey) { |
| 470 InitCombobox(); |
| 471 |
| 472 TestComboboxListener listener; |
| 473 combobox_->set_listener(&listener); |
| 474 |
| 475 // With STYLE_SHOW_DROP_DOWN_ON_CLICK, the click event is ignored. |
| 476 SendKeyEvent(ui::VKEY_RETURN); |
| 477 EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); |
| 478 |
| 479 // With STYLE_NOTIFY_ON_CLICK, the click event is notified. |
| 480 combobox_->SetStyle(Combobox::STYLE_NOTIFY_ON_CLICK); |
| 481 SendKeyEvent(ui::VKEY_RETURN); |
| 482 EXPECT_TRUE(listener.on_combobox_text_button_clicked_called()); |
| 483 } |
| 484 |
| 485 TEST_F(ComboboxTest, NotifyOnClickWithSpaceKey) { |
| 486 InitCombobox(); |
| 487 |
| 488 TestComboboxListener listener; |
| 489 combobox_->set_listener(&listener); |
| 490 |
| 491 // With STYLE_SHOW_DROP_DOWN_ON_CLICK, the click event is ignored. |
| 492 SendKeyEvent(ui::VKEY_SPACE); |
| 493 EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); |
| 494 SendKeyEventWithType(ui::VKEY_SPACE, ui::ET_KEY_RELEASED); |
| 495 EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); |
| 496 |
| 497 // With STYLE_NOTIFY_ON_CLICK, the click event is notified after releasing. |
| 498 combobox_->SetStyle(Combobox::STYLE_NOTIFY_ON_CLICK); |
| 499 SendKeyEvent(ui::VKEY_SPACE); |
| 500 EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); |
| 501 SendKeyEventWithType(ui::VKEY_SPACE, ui::ET_KEY_RELEASED); |
| 502 EXPECT_TRUE(listener.on_combobox_text_button_clicked_called()); |
| 503 } |
| 504 |
| 505 TEST_F(ComboboxTest, NotifyOnClickWithMouse) { |
| 506 InitCombobox(); |
| 507 |
| 508 TestComboboxListener listener; |
| 509 combobox_->set_listener(&listener); |
| 510 |
| 511 combobox_->SetStyle(Combobox::STYLE_NOTIFY_ON_CLICK); |
| 512 combobox_->Layout(); |
| 513 |
| 514 // Click the right side (arrow button). The menu is shown. |
| 515 TestMenuRunnerHandler* test_menu_runner_handler = new TestMenuRunnerHandler(); |
| 516 scoped_ptr<MenuRunnerHandler> menu_runner_handler(test_menu_runner_handler); |
| 517 scoped_ptr<test::MenuRunnerTestAPI> test_api( |
| 518 new test::MenuRunnerTestAPI(combobox_->dropdown_list_menu_runner_.get())); |
| 519 test_api->SetMenuRunnerHandler(menu_runner_handler.Pass()); |
| 520 |
| 521 PerformClick(gfx::Point(combobox_->x() + combobox_->width() - 1, |
| 522 combobox_->y() + combobox_->height() / 2)); |
| 523 EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); |
| 524 EXPECT_TRUE(test_menu_runner_handler->executed()); |
| 525 |
| 526 // Click the left side (text button). The click event is notified. |
| 527 test_menu_runner_handler = new TestMenuRunnerHandler(); |
| 528 menu_runner_handler.reset(test_menu_runner_handler); |
| 529 test_api.reset( |
| 530 new test::MenuRunnerTestAPI(combobox_->dropdown_list_menu_runner_.get())); |
| 531 test_api->SetMenuRunnerHandler(menu_runner_handler.Pass()); |
| 532 PerformClick(gfx::Point(combobox_->x() + 1, |
| 533 combobox_->y() + combobox_->height() / 2)); |
| 534 EXPECT_TRUE(listener.on_combobox_text_button_clicked_called()); |
| 535 EXPECT_FALSE(test_menu_runner_handler->executed()); |
| 536 } |
| 537 |
373 } // namespace views | 538 } // namespace views |
OLD | NEW |