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 3 // found in the LICENSE file. 4 5 #include "ui/views/controls/button/menu_button.h" 6 7 #include "base/memory/scoped_ptr.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "ui/events/test/event_generator.h" 10 #include "ui/views/controls/button/menu_button_listener.h" 11 #include "ui/views/test/views_test_base.h" 12 13 using base::ASCIIToUTF16; 14 15 namespace views { 16 17 class MenuButtonTest : public ViewsTestBase { 18 public: 19 MenuButtonTest() : widget_(NULL), button_(NULL) {} 20 virtual ~MenuButtonTest() {} 21 22 virtual void TearDown() OVERRIDE { 23 if (widget_ && !widget_->IsClosed()) 24 widget_->Close(); 25 26 ViewsTestBase::TearDown(); 27 } 28 29 Widget* widget() { return widget_; } 30 MenuButton* button() { return button_; } 31 32 protected: 33 // Creates a MenuButton with no button listener. 34 void CreateMenuButtonWithNoListener() { 35 CreateMenuButton(NULL, NULL); 36 } 37 38 // Creates a MenuButton with a ButtonListener. In this case, the MenuButton 39 // acts like a regular button. 40 void CreateMenuButtonWithButtonListener(ButtonListener* button_listener) { 41 CreateMenuButton(button_listener, NULL); 42 } 43 44 // Creates a MenuButton with a MenuButtonListener. In this case, when the 45 // MenuButton is pushed, it notifies the MenuButtonListener to open a 46 // drop-down menu. 47 void CreateMenuButtonWithMenuButtonListener( 48 MenuButtonListener* menu_button_listener) { 49 CreateMenuButton(NULL, menu_button_listener); 50 } 51 52 private: 53 void CreateMenuButton(ButtonListener* button_listener, 54 MenuButtonListener* menu_button_listener) { 55 CreateWidget(); 56 57 const base::string16 label(ASCIIToUTF16("button")); 58 button_ = 59 new MenuButton(button_listener, label, menu_button_listener, false); 60 button_->SetBoundsRect(gfx::Rect(0, 0, 200, 20)); 61 widget_->SetContentsView(button_); 62 63 widget_->Show(); 64 } 65 66 void CreateWidget() { 67 DCHECK(!widget_); 68 69 widget_ = new Widget; 70 Widget::InitParams params = 71 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 72 params.bounds = gfx::Rect(0, 0, 200, 200); 73 widget_->Init(params); 74 } 75 76 Widget* widget_; 77 MenuButton* button_; 78 }; 79 80 class TestButtonListener : public ButtonListener { 81 public: 82 TestButtonListener() 83 : last_sender_(NULL), 84 last_sender_state_(Button::STATE_NORMAL), 85 last_event_type_(ui::ET_UNKNOWN) {} 86 virtual ~TestButtonListener() {} 87 88 virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE { 89 last_sender_ = sender; 90 CustomButton* custom_button = CustomButton::AsCustomButton(sender); 91 DCHECK(custom_button); 92 last_sender_state_ = custom_button->state(); 93 last_event_type_ = event.type(); 94 } 95 96 Button* last_sender() { return last_sender_; } 97 Button::ButtonState last_sender_state() { return last_sender_state_; } 98 ui::EventType last_event_type() { return last_event_type_; } 99 100 private: 101 Button* last_sender_; 102 Button::ButtonState last_sender_state_; 103 ui::EventType last_event_type_; 104 105 DISALLOW_COPY_AND_ASSIGN(TestButtonListener); 106 }; 107 108 class TestMenuButtonListener : public MenuButtonListener { 109 public: 110 TestMenuButtonListener() {} 111 virtual ~TestMenuButtonListener() {} 112 113 virtual void OnMenuButtonClicked(View* source, 114 const gfx::Point& /*point*/) OVERRIDE { 115 last_source_ = source; 116 CustomButton* custom_button = CustomButton::AsCustomButton(source); 117 DCHECK(custom_button); 118 last_source_state_ = custom_button->state(); 119 } 120 121 View* last_source() { return last_source_; } 122 Button::ButtonState last_source_state() { return last_source_state_; } 123 124 private: 125 View* last_source_; 126 Button::ButtonState last_source_state_; 127 }; 128 129 // Tests if the listener is notified correctly, when a mouse click happens on a 130 // MenuButton that has a regular ButtonListener. 131 TEST_F(MenuButtonTest, ActivateNonDropDownOnMouseClick) { 132 scoped_ptr<TestButtonListener> button_listener(new TestButtonListener); 133 CreateMenuButtonWithButtonListener(button_listener.get()); 134 135 ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); 136 137 generator.set_current_location(gfx::Point(10, 10)); 138 generator.ClickLeftButton(); 139 140 // Check that MenuButton has notified the listener on mouse-released event, 141 // while it was in hovered state. 142 EXPECT_EQ(button(), button_listener->last_sender()); 143 EXPECT_EQ(ui::ET_MOUSE_RELEASED, button_listener->last_event_type()); 144 EXPECT_EQ(Button::STATE_HOVERED, button_listener->last_sender_state()); 145 } 146 147 // Tests if the listener is notified correctly when a gesture tap happens on a 148 // MenuButton that has a regular ButtonListener. 149 TEST_F(MenuButtonTest, ActivateNonDropDownOnGestureTap) { 150 scoped_ptr<TestButtonListener> button_listener(new TestButtonListener); 151 CreateMenuButtonWithButtonListener(button_listener.get()); 152 153 ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); 154 generator.GestureTapAt(gfx::Point(10, 10)); 155 156 // Check that MenuButton has notified the listener on gesture tap event, while 157 // it was in hovered state. 158 EXPECT_EQ(button(), button_listener->last_sender()); 159 EXPECT_EQ(ui::ET_GESTURE_TAP, button_listener->last_event_type()); 160 EXPECT_EQ(Button::STATE_HOVERED, button_listener->last_sender_state()); 161 } 162 163 // Tests if the listener is notified correctly when a mouse click happens on a 164 // MenuButton that has a MenuButtonListener. 165 TEST_F(MenuButtonTest, ActivateDropDownOnMouseClick) { 166 scoped_ptr<TestMenuButtonListener> menu_button_listener( 167 new TestMenuButtonListener); 168 CreateMenuButtonWithMenuButtonListener(menu_button_listener.get()); 169 170 ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); 171 172 generator.set_current_location(gfx::Point(10, 10)); 173 generator.ClickLeftButton(); 174 175 // Check that MenuButton has notified the listener, while it was in pressed 176 // state. 177 EXPECT_EQ(button(), menu_button_listener->last_source()); 178 EXPECT_EQ(Button::STATE_PRESSED, menu_button_listener->last_source_state()); 179 } 180 181 // Tests if the listener is notified correctly when a gesture tap happens on a 182 // MenuButton that has a MenuButtonListener. 183 TEST_F(MenuButtonTest, ActivateDropDownOnGestureTap) { 184 scoped_ptr<TestMenuButtonListener> menu_button_listener( 185 new TestMenuButtonListener); 186 CreateMenuButtonWithMenuButtonListener(menu_button_listener.get()); 187 188 ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); 189 generator.GestureTapAt(gfx::Point(10, 10)); 190 191 // Check that MenuButton has notified the listener, while it was in pressed 192 // state. 193 EXPECT_EQ(button(), menu_button_listener->last_source()); 194 EXPECT_EQ(Button::STATE_PRESSED, menu_button_listener->last_source_state()); 195 } 196 197 // Test that the MenuButton stays pressed while there are any PressedLocks. 198 TEST_F(MenuButtonTest, MenuButtonPressedLock) { 199 CreateMenuButtonWithNoListener(); 200 201 // Move the mouse over the button; the button should be in a hovered state. 202 ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); 203 generator.MoveMouseTo(gfx::Point(10, 10)); 204 EXPECT_EQ(Button::STATE_HOVERED, button()->state()); 205 206 // Introduce a PressedLock, which should make the button pressed. 207 scoped_ptr<MenuButton::PressedLock> pressed_lock1( 208 new MenuButton::PressedLock(button())); 209 EXPECT_EQ(Button::STATE_PRESSED, button()->state()); 210 211 // Even if we move the mouse outside of the button, it should remain pressed. 212 generator.MoveMouseTo(gfx::Point(300, 10)); 213 EXPECT_EQ(Button::STATE_PRESSED, button()->state()); 214 215 // Creating a new lock should obviously keep the button pressed. 216 scoped_ptr<MenuButton::PressedLock> pressed_lock2( 217 new MenuButton::PressedLock(button())); 218 EXPECT_EQ(Button::STATE_PRESSED, button()->state()); 219 220 // The button should remain pressed while any locks are active. 221 pressed_lock1.reset(); 222 EXPECT_EQ(Button::STATE_PRESSED, button()->state()); 223 224 // Reseting the final lock should return the button's state to normal... 225 pressed_lock2.reset(); 226 EXPECT_EQ(Button::STATE_NORMAL, button()->state()); 227 228 // ...And it should respond to mouse movement again. 229 generator.MoveMouseTo(gfx::Point(10, 10)); 230 EXPECT_EQ(Button::STATE_HOVERED, button()->state()); 231 } 232 233 } // namespace views 234