OLD | NEW |
---|---|
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 Loading... | |
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 |
OLD | NEW |