Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(327)

Side by Side Diff: ui/views/controls/menu/menu_controller_unittest.cc

Issue 1138523006: Enable keyboard accelerators while a menu is open (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Working solution (Even on Windows) Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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/menu/menu_controller.h" 5 #include "ui/views/controls/menu/menu_controller.h"
6 6
7 #include "base/run_loop.h"
8 #include "base/strings/utf_string_conversions.h" 7 #include "base/strings/utf_string_conversions.h"
9 #include "ui/aura/scoped_window_targeter.h" 8 #include "ui/aura/scoped_window_targeter.h"
10 #include "ui/aura/window.h" 9 #include "ui/aura/window.h"
11 #include "ui/events/event_handler.h" 10 #include "ui/events/event_handler.h"
12 #include "ui/events/null_event_targeter.h" 11 #include "ui/events/null_event_targeter.h"
13 #include "ui/events/platform/platform_event_source.h" 12 #include "ui/events/test/event_generator.h"
14 #include "ui/views/controls/menu/menu_item_view.h" 13 #include "ui/views/controls/menu/menu_item_view.h"
15 #include "ui/views/controls/menu/submenu_view.h" 14 #include "ui/views/controls/menu/submenu_view.h"
16 #include "ui/views/test/views_test_base.h" 15 #include "ui/views/test/views_test_base.h"
17 #include "ui/wm/public/dispatcher_client.h"
18 16
19 #if defined(OS_WIN) 17 #if defined(USE_X11)
20 #include "base/message_loop/message_pump_dispatcher.h"
21 #elif defined(USE_X11)
22 #include <X11/Xlib.h> 18 #include <X11/Xlib.h>
23 #undef Bool 19 #undef Bool
24 #undef None 20 #undef None
25 #include "ui/events/devices/x11/device_data_manager_x11.h" 21 #include "ui/events/devices/x11/device_data_manager_x11.h"
26 #include "ui/events/test/events_test_utils_x11.h" 22 #include "ui/events/test/events_test_utils_x11.h"
27 #elif defined(USE_OZONE)
28 #include "ui/events/event.h"
29 #endif 23 #endif
30 24
31 namespace views { 25 namespace views {
32 26
33 namespace { 27 namespace {
34 28
35 class TestMenuItemView : public MenuItemView { 29 class TestMenuItemView : public MenuItemView {
36 public: 30 public:
37 TestMenuItemView() : MenuItemView(nullptr) {} 31 TestMenuItemView() : MenuItemView(nullptr) {}
38 ~TestMenuItemView() override {} 32 ~TestMenuItemView() override {}
39 33
40 private: 34 private:
41 DISALLOW_COPY_AND_ASSIGN(TestMenuItemView); 35 DISALLOW_COPY_AND_ASSIGN(TestMenuItemView);
42 }; 36 };
43 37
44 class TestPlatformEventSource : public ui::PlatformEventSource {
45 public:
46 TestPlatformEventSource() {
47 #if defined(USE_X11)
48 ui::DeviceDataManagerX11::CreateInstance();
49 #endif
50 }
51 ~TestPlatformEventSource() override {}
52
53 uint32_t Dispatch(const ui::PlatformEvent& event) {
54 return DispatchEvent(event);
55 }
56
57 private:
58 DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource);
59 };
60
61 class TestDispatcherClient : public aura::client::DispatcherClient {
62 public:
63 TestDispatcherClient() : dispatcher_(nullptr) {}
64 ~TestDispatcherClient() override {}
65
66 base::MessagePumpDispatcher* dispatcher() {
67 return dispatcher_;
68 }
69
70 // aura::client::DispatcherClient:
71 void PrepareNestedLoopClosures(base::MessagePumpDispatcher* dispatcher,
72 base::Closure* run_closure,
73 base::Closure* quit_closure) override {
74 scoped_ptr<base::RunLoop> run_loop(new base::RunLoop());
75 *quit_closure = run_loop->QuitClosure();
76 *run_closure = base::Bind(&TestDispatcherClient::RunNestedDispatcher,
77 base::Unretained(this),
78 base::Passed(&run_loop),
79 dispatcher);
80 }
81
82 private:
83 void RunNestedDispatcher(scoped_ptr<base::RunLoop> run_loop,
84 base::MessagePumpDispatcher* dispatcher) {
85 base::AutoReset<base::MessagePumpDispatcher*> reset_dispatcher(&dispatcher_,
86 dispatcher);
87 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
88 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
89 run_loop->Run();
90 }
91
92 base::MessagePumpDispatcher* dispatcher_;
93
94 DISALLOW_COPY_AND_ASSIGN(TestDispatcherClient);
95 };
96
97 } // namespace
98
99 class MenuControllerTest : public ViewsTestBase {
100 public:
101 MenuControllerTest() : controller_(nullptr) {}
102 ~MenuControllerTest() override { ResetMenuController(); }
103
104 // Dispatches |count| number of items, each in a separate iteration of the
105 // message-loop, by posting a task.
106 void Step3_DispatchEvents(int count) {
107 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
108 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
109 controller_->exit_type_ = MenuController::EXIT_ALL;
110
111 DispatchEvent();
112 if (count) {
113 base::MessageLoop::current()->PostTask(
114 FROM_HERE,
115 base::Bind(&MenuControllerTest::Step3_DispatchEvents,
116 base::Unretained(this),
117 count - 1));
118 } else {
119 EXPECT_TRUE(run_loop_->running());
120 run_loop_->Quit();
121 }
122 }
123
124 // Runs a nested message-loop that does not involve the menu itself.
125 void Step2_RunNestedLoop() {
126 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
127 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
128 base::MessageLoop::current()->PostTask(
129 FROM_HERE,
130 base::Bind(&MenuControllerTest::Step3_DispatchEvents,
131 base::Unretained(this),
132 3));
133 run_loop_.reset(new base::RunLoop());
134 run_loop_->Run();
135 }
136
137 void Step1_RunMenu() {
138 base::MessageLoop::current()->PostTask(
139 FROM_HERE,
140 base::Bind(&MenuControllerTest::Step2_RunNestedLoop,
141 base::Unretained(this)));
142 scoped_ptr<Widget> owner(CreateOwnerWidget());
143 RunMenu(owner.get());
144 }
145
146 scoped_ptr<Widget> CreateOwnerWidget() {
147 scoped_ptr<Widget> widget(new Widget);
148 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
149 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
150 widget->Init(params);
151 widget->Show();
152
153 aura::client::SetDispatcherClient(
154 widget->GetNativeWindow()->GetRootWindow(), &dispatcher_client_);
155 return widget.Pass();
156 }
157
158 const MenuItemView* pending_state_item() const {
159 return controller_->pending_state_.item;
160 }
161
162 void SetPendingStateItem(MenuItemView* item) {
163 controller_->pending_state_.item = item;
164 }
165
166 void ResetSelection() {
167 controller_->SetSelection(nullptr,
168 MenuController::SELECTION_EXIT |
169 MenuController::SELECTION_UPDATE_IMMEDIATELY);
170 }
171
172 void IncrementSelection(int delta) {
173 controller_->IncrementSelection(delta);
174 }
175
176 MenuItemView* FindFirstSelectableMenuItem(MenuItemView* parent) {
177 return controller_->FindFirstSelectableMenuItem(parent);
178 }
179
180 MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent,
181 int index,
182 int delta) {
183 return controller_->FindNextSelectableMenuItem(parent, index, delta);
184 }
185 void SetupMenu(views::Widget* owner, views::MenuItemView* item) {
186 ResetMenuController();
187 controller_ = new MenuController(nullptr, true, nullptr);
188 controller_->owner_ = owner;
189 controller_->showing_ = true;
190 controller_->SetSelection(item,
191 MenuController::SELECTION_UPDATE_IMMEDIATELY);
192 }
193
194 void RunMenu(views::Widget* owner) {
195 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView);
196 SetupMenu(owner, menu_item.get());
197 controller_->RunMessageLoop(false);
198 }
199
200 #if defined(USE_X11)
201 void DispatchEscapeAndExpect(MenuController::ExitType exit_type) {
202 ui::ScopedXI2Event key_event;
203 key_event.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0);
204 event_source_.Dispatch(key_event);
205 EXPECT_EQ(exit_type, controller_->exit_type());
206 controller_->exit_type_ = MenuController::EXIT_ALL;
207 DispatchEvent();
208 }
209
210 void DispatchTouch(int evtype, int id) {
211 ui::ScopedXI2Event touch_event;
212 std::vector<ui::Valuator> valuators;
213 touch_event.InitTouchEvent(1, evtype, id, gfx::Point(10, 10), valuators);
214 event_source_.Dispatch(touch_event);
215 DispatchEvent();
216 }
217 #endif
218
219 void DispatchEvent() {
220 #if defined(USE_X11)
221 XEvent xevent;
222 memset(&xevent, 0, sizeof(xevent));
223 event_source_.Dispatch(&xevent);
224 #elif defined(OS_WIN)
225 MSG msg;
226 memset(&msg, 0, sizeof(MSG));
227 dispatcher_client_.dispatcher()->Dispatch(msg);
228 #elif defined(USE_OZONE)
229 ui::KeyEvent event(' ', ui::VKEY_SPACE, ui::EF_NONE);
230 event_source_.Dispatch(&event);
231 #else
232 #error Unsupported platform
233 #endif
234 }
235
236 private:
237 void ResetMenuController() {
238 if (controller_) {
239 // These properties are faked by RunMenu for the purposes of testing and
240 // need to be undone before we call the destructor.
241 controller_->owner_ = nullptr;
242 controller_->showing_ = false;
243 delete controller_;
244 controller_ = nullptr;
245 }
246 }
247
248 // A weak pointer to the MenuController owned by this class.
249 MenuController* controller_;
250 scoped_ptr<base::RunLoop> run_loop_;
251 TestPlatformEventSource event_source_;
252 TestDispatcherClient dispatcher_client_;
253
254 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest);
255 };
256
257 TEST_F(MenuControllerTest, Basic) {
258 base::MessageLoop::ScopedNestableTaskAllower allow_nested(
259 base::MessageLoop::current());
260 message_loop()->PostTask(
261 FROM_HERE,
262 base::Bind(&MenuControllerTest::Step1_RunMenu, base::Unretained(this)));
263 }
264
265 #if defined(OS_LINUX) && defined(USE_X11)
266 // Tests that an event targeter which blocks events will be honored by the menu
267 // event dispatcher.
268 TEST_F(MenuControllerTest, EventTargeter) {
269 {
270 // Verify that the menu handles the escape key under normal circumstances.
271 scoped_ptr<Widget> owner(CreateOwnerWidget());
272 message_loop()->PostTask(
273 FROM_HERE,
274 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect,
275 base::Unretained(this),
276 MenuController::EXIT_OUTERMOST));
277 RunMenu(owner.get());
278 }
279
280 {
281 // With the NULL targeter instantiated and assigned we expect the menu to
282 // not handle the key event.
283 scoped_ptr<Widget> owner(CreateOwnerWidget());
284 aura::ScopedWindowTargeter scoped_targeter(
285 owner->GetNativeWindow()->GetRootWindow(),
286 scoped_ptr<ui::EventTargeter>(new ui::NullEventTargeter));
287 message_loop()->PostTask(
288 FROM_HERE,
289 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect,
290 base::Unretained(this),
291 MenuController::EXIT_NONE));
292 RunMenu(owner.get());
293 }
294 }
295 #endif
296
297 #if defined(USE_X11)
298
299 class TestEventHandler : public ui::EventHandler { 38 class TestEventHandler : public ui::EventHandler {
300 public: 39 public:
301 TestEventHandler() : outstanding_touches_(0) {} 40 TestEventHandler() : outstanding_touches_(0) {}
302 41
303 void OnTouchEvent(ui::TouchEvent* event) override { 42 void OnTouchEvent(ui::TouchEvent* event) override {
304 switch(event->type()) { 43 switch(event->type()) {
305 case ui::ET_TOUCH_PRESSED: 44 case ui::ET_TOUCH_PRESSED:
306 outstanding_touches_++; 45 outstanding_touches_++;
307 break; 46 break;
308 case ui::ET_TOUCH_RELEASED: 47 case ui::ET_TOUCH_RELEASED:
309 case ui::ET_TOUCH_CANCELLED: 48 case ui::ET_TOUCH_CANCELLED:
310 outstanding_touches_--; 49 outstanding_touches_--;
311 break; 50 break;
312 default: 51 default:
313 break; 52 break;
314 } 53 }
315 } 54 }
316 55
317 int outstanding_touches() const { return outstanding_touches_; } 56 int outstanding_touches() const { return outstanding_touches_; }
318 57
319 private: 58 private:
320 int outstanding_touches_; 59 int outstanding_touches_;
321 }; 60 };
322 61
62 } // namespace
63
64 class MenuControllerTest : public ViewsTestBase {
65 public:
66 MenuControllerTest()
67 : owner_(),
68 event_generator_(),
69 menu_item_(),
oshima 2015/06/12 19:59:18 you can skip these
afakhry 2015/06/12 23:50:46 I know but I always like to be explicit .. however
70 menu_controller_(nullptr) {
71 }
72
73 ~MenuControllerTest() override {}
74
75 void SetUp() override {
76 ViewsTestBase::SetUp();
77 Init();
78 }
79
80 void TearDown() override {
81 if (menu_controller_) {
82 menu_controller_->owner_ = nullptr;
83 menu_controller_->showing_ = false;
oshima 2015/06/12 19:59:18 do you need these?
afakhry 2015/06/12 23:50:46 Yes the destructor will crash on a DCHECK() if |sh
84 delete menu_controller_;
85 menu_controller_ = nullptr;
oshima 2015/06/12 19:59:18 any reason why we shouldn't use scoped_ptr for thi
afakhry 2015/06/12 23:50:46 Done. Using a scoped_ptr with a specialized Delete
86 }
87
88 ViewsTestBase::TearDown();
89 }
90
91 void ReleaseTouchId(int id) {
92 event_generator_->ReleaseTouchId(id);
93 }
94
95 void PressKey(ui::KeyboardCode key_code) {
96 event_generator_->PressKey(key_code, 0);
97 }
98
99 void TestEventTargeter() {
100 // With the NULL targeter instantiated and assigned we expect the menu to
oshima 2015/06/12 19:59:18 nullptr
afakhry 2015/06/12 23:50:46 Actually NULL here refers to the ui::NullEventTarg
101 // not handle the key event.
102 scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter(
103 new aura::ScopedWindowTargeter(
104 owner()->GetNativeWindow()->GetRootWindow(),
105 scoped_ptr<ui::EventTargeter>(new ui::NullEventTargeter)));
106 event_generator_->PressKey(ui::VKEY_ESCAPE, 0);
107 EXPECT_EQ(MenuController::EXIT_NONE, menu_exit_type());
108
109 // Now remove the targeter an expect to exit the menu normally.
110 scoped_targeter.reset();
111 event_generator_->PressKey(ui::VKEY_ESCAPE, 0);
112 EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type());
113 }
114
115 protected:
116 void SetPendingStateItem(MenuItemView* item) {
117 menu_controller_->pending_state_.item = item;
118 }
119
120 void ResetSelection() {
121 menu_controller_->SetSelection(
122 nullptr,
123 MenuController::SELECTION_EXIT |
124 MenuController::SELECTION_UPDATE_IMMEDIATELY);
125 }
126
127 void IncrementSelection(int delta) {
128 menu_controller_->IncrementSelection(delta);
129 }
130
131 MenuItemView* FindFirstSelectableMenuItem(MenuItemView* parent) {
132 return menu_controller_->FindFirstSelectableMenuItem(parent);
133 }
134
135 MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent,
136 int index,
137 int delta) {
138
139 return menu_controller_->FindNextSelectableMenuItem(parent, index, delta);
140 }
141
142 void RunMenu() {
143 menu_controller_->RunMessageLoop(false);
144 }
145
146 Widget* owner() { return owner_.get(); }
147 ui::test::EventGenerator* event_generator() { return event_generator_.get(); }
148 TestMenuItemView* menu_item() { return menu_item_.get(); }
149 MenuController* menu_controller() { return menu_controller_; }
150 const MenuItemView* pending_state_item() const {
151 return menu_controller_->pending_state_.item;
152 }
153 const MenuController::ExitType menu_exit_type() const {
154 return menu_controller_->exit_type_;
155 }
156
157 private:
158 void Init() {
159 owner_.reset(new Widget);
160 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
161 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
162 owner_->Init(params);
163 event_generator_.reset(new ui::test::EventGenerator(
164 owner_->GetNativeWindow()->GetRootWindow(), owner_->GetNativeWindow()));
165 owner_->Show();
166
167 SetupMenuItem();
168
169 SetupMenuController();
170 }
171
172 void SetupMenuItem() {
173 menu_item_.reset(new TestMenuItemView);
174 menu_item_->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One"));
175 menu_item_->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two"));
176 menu_item_->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three"));
177 menu_item_->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four"));
178 }
179
180 void SetupMenuController() {
181 menu_controller_ = new MenuController(nullptr, true, nullptr);
182 menu_controller_->owner_ = owner_.get();
183 menu_controller_->showing_ = true;
184 menu_controller_->SetSelection(
185 menu_item_.get(), MenuController::SELECTION_UPDATE_IMMEDIATELY);
186 }
187
188 scoped_ptr<Widget> owner_;
189 scoped_ptr<ui::test::EventGenerator> event_generator_;
190 scoped_ptr<TestMenuItemView> menu_item_;
191 MenuController* menu_controller_;
192
193 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest);
194 };
195
196 // Tests that an event targeter which blocks events will be honored by the menu
197 // event dispatcher.
198 TEST_F(MenuControllerTest, EventTargeter) {
199 base::MessageLoopForUI::current()->PostTask(
200 FROM_HERE,
201 base::Bind(&MenuControllerTest::TestEventTargeter,
202 base::Unretained(this)));
203 RunMenu();
204 }
205
206 #if defined(USE_X11)
207
323 // Tests that touch event ids are released correctly. See 208 // Tests that touch event ids are released correctly. See
324 // crbug.com/439051 for details. When the ids aren't managed 209 // crbug.com/439051 for details. When the ids aren't managed
325 // correctly, we get stuck down touches. 210 // correctly, we get stuck down touches.
326 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { 211 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) {
327 scoped_ptr<Widget> owner(CreateOwnerWidget());
328 TestEventHandler test_event_handler; 212 TestEventHandler test_event_handler;
329 owner->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( 213 owner()->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler(
330 &test_event_handler); 214 &test_event_handler);
331 215
332 std::vector<int> devices; 216 std::vector<int> devices;
333 devices.push_back(1); 217 devices.push_back(1);
334 ui::SetUpTouchDevicesForTest(devices); 218 ui::SetUpTouchDevicesForTest(devices);
335 219
336 DispatchTouch(XI_TouchBegin, 0); 220 event_generator()->PressTouchId(0);
337 DispatchTouch(XI_TouchBegin, 1); 221 event_generator()->PressTouchId(1);
338 DispatchTouch(XI_TouchEnd, 0); 222 event_generator()->ReleaseTouchId(0);
339 223
340 message_loop()->PostTask(FROM_HERE, 224 base::MessageLoopForUI::current()->PostTask(
341 base::Bind(&MenuControllerTest::DispatchTouch,
342 base::Unretained(this), XI_TouchEnd, 1));
343
344 message_loop()->PostTask(
345 FROM_HERE, 225 FROM_HERE,
346 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect, 226 base::Bind(&MenuControllerTest::ReleaseTouchId,
347 base::Unretained(this), MenuController::EXIT_OUTERMOST)); 227 base::Unretained(this),
348 228 1));
349 RunMenu(owner.get()); 229
230 base::MessageLoopForUI::current()->PostTask(
231 FROM_HERE,
232 base::Bind(&MenuControllerTest::PressKey,
233 base::Unretained(this),
234 ui::VKEY_ESCAPE));
235
236 RunMenu();
237
238 EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type());
350 EXPECT_EQ(0, test_event_handler.outstanding_touches()); 239 EXPECT_EQ(0, test_event_handler.outstanding_touches());
351 240
352 owner->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler( 241 owner()->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler(
353 &test_event_handler); 242 &test_event_handler);
354 } 243 }
355 #endif // defined(USE_X11) 244
245 #endif // defined(USE_X11)
356 246
357 TEST_F(MenuControllerTest, FirstSelectedItem) { 247 TEST_F(MenuControllerTest, FirstSelectedItem) {
358 scoped_ptr<Widget> owner(CreateOwnerWidget()); 248 // disabling all items but "Two".
359 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); 249 menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false);
360 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); 250 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false);
361 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); 251 menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false);
362 // Disabling the item "One" gets it skipped when a menu is first opened. 252
363 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); 253 // "Two" should be the first selectable item.
364 254 MenuItemView* first_selectable = FindFirstSelectableMenuItem(menu_item());
365 SetupMenu(owner.get(), menu_item.get());
366
367 // First selectable item should be item "Two".
368 MenuItemView* first_selectable = FindFirstSelectableMenuItem(menu_item.get());
369 EXPECT_EQ(2, first_selectable->GetCommand()); 255 EXPECT_EQ(2, first_selectable->GetCommand());
370 256
371 // There should be no next or previous selectable item since there is only a 257 // There should be no next or previous selectable item since there is only a
372 // single enabled item in the menu. 258 // single enabled item in the menu.
373 SetPendingStateItem(first_selectable); 259 SetPendingStateItem(first_selectable);
374 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1, 1)); 260 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item(), 1, 1));
375 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1, -1)); 261 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item(), 1, -1));
376 262
377 // Clear references in menu controller to the menu item that is going away. 263 // Clear references in menu controller to the menu item that is going away.
378 ResetSelection(); 264 ResetSelection();
379 } 265 }
380 266
381 TEST_F(MenuControllerTest, NextSelectedItem) { 267 TEST_F(MenuControllerTest, NextSelectedItem) {
382 scoped_ptr<Widget> owner(CreateOwnerWidget());
383 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView);
384 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One"));
385 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two"));
386 menu_item->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three"));
387 menu_item->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four"));
388 // Disabling the item "Three" gets it skipped when using keyboard to navigate. 268 // Disabling the item "Three" gets it skipped when using keyboard to navigate.
389 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); 269 menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false);
390
391 SetupMenu(owner.get(), menu_item.get());
392 270
393 // Fake initial hot selection. 271 // Fake initial hot selection.
394 SetPendingStateItem(menu_item->GetSubmenu()->GetMenuItemAt(0)); 272 SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0));
395 EXPECT_EQ(1, pending_state_item()->GetCommand()); 273 EXPECT_EQ(1, pending_state_item()->GetCommand());
396 274
397 // Move down in the menu. 275 // Move down in the menu.
398 // Select next item. 276 // Select next item.
399 IncrementSelection(1); 277 IncrementSelection(1);
400 EXPECT_EQ(2, pending_state_item()->GetCommand()); 278 EXPECT_EQ(2, pending_state_item()->GetCommand());
401 279
402 // Skip disabled item. 280 // Skip disabled item.
403 IncrementSelection(1); 281 IncrementSelection(1);
404 EXPECT_EQ(4, pending_state_item()->GetCommand()); 282 EXPECT_EQ(4, pending_state_item()->GetCommand());
(...skipping 13 matching lines...) Expand all
418 296
419 // Select previous item. 297 // Select previous item.
420 IncrementSelection(-1); 298 IncrementSelection(-1);
421 EXPECT_EQ(1, pending_state_item()->GetCommand()); 299 EXPECT_EQ(1, pending_state_item()->GetCommand());
422 300
423 // Clear references in menu controller to the menu item that is going away. 301 // Clear references in menu controller to the menu item that is going away.
424 ResetSelection(); 302 ResetSelection();
425 } 303 }
426 304
427 } // namespace views 305 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698