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