| 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 #import "ui/views/controls/menu/menu_runner_impl_cocoa.h" | 5 #import "ui/views/controls/menu/menu_runner_impl_cocoa.h" |
| 6 | 6 |
| 7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 8 | 8 |
| 9 #include "base/macros.h" | 9 #include "base/macros.h" |
| 10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| 11 #import "testing/gtest_mac.h" | 11 #import "testing/gtest_mac.h" |
| 12 #include "ui/base/models/simple_menu_model.h" | 12 #include "ui/base/models/simple_menu_model.h" |
| 13 #include "ui/events/event_utils.h" | 13 #include "ui/events/event_utils.h" |
| 14 #include "ui/views/test/views_test_base.h" | 14 #include "ui/views/test/views_test_base.h" |
| 15 | 15 |
| 16 namespace views { | 16 namespace views { |
| 17 namespace test { | 17 namespace test { |
| 18 namespace { | 18 namespace { |
| 19 | 19 |
| 20 class TestModel : public ui::SimpleMenuModel { | 20 class TestModel : public ui::SimpleMenuModel { |
| 21 public: | 21 public: |
| 22 TestModel() : ui::SimpleMenuModel(&delegate_), delegate_(this) {} | 22 TestModel() : ui::SimpleMenuModel(&delegate_), delegate_(this) {} |
| 23 | 23 |
| 24 void set_checked_command(int command) { checked_command_ = command; } | 24 void set_checked_command(int command) { checked_command_ = command; } |
| 25 | 25 |
| 26 void set_menu_open_callback(const base::Closure& callback) { |
| 27 menu_open_callback_ = callback; |
| 28 } |
| 29 |
| 26 private: | 30 private: |
| 27 class Delegate : public ui::SimpleMenuModel::Delegate { | 31 class Delegate : public ui::SimpleMenuModel::Delegate { |
| 28 public: | 32 public: |
| 29 explicit Delegate(TestModel* model) : model_(model) {} | 33 explicit Delegate(TestModel* model) : model_(model) {} |
| 30 bool IsCommandIdChecked(int command_id) const override { | 34 bool IsCommandIdChecked(int command_id) const override { |
| 31 return command_id == model_->checked_command_; | 35 return command_id == model_->checked_command_; |
| 32 } | 36 } |
| 33 bool IsCommandIdEnabled(int command_id) const override { return true; } | 37 bool IsCommandIdEnabled(int command_id) const override { return true; } |
| 34 bool GetAcceleratorForCommandId(int command_id, | 38 bool GetAcceleratorForCommandId(int command_id, |
| 35 ui::Accelerator* accelerator) override { | 39 ui::Accelerator* accelerator) override { |
| 36 return false; | 40 return false; |
| 37 } | 41 } |
| 38 void ExecuteCommand(int command_id, int event_flags) override {} | 42 void ExecuteCommand(int command_id, int event_flags) override {} |
| 39 | 43 |
| 44 void MenuWillShow(SimpleMenuModel* source) override { |
| 45 model_->menu_open_callback_.Run(); |
| 46 } |
| 47 |
| 40 private: | 48 private: |
| 41 TestModel* model_; | 49 TestModel* model_; |
| 42 | 50 |
| 43 DISALLOW_COPY_AND_ASSIGN(Delegate); | 51 DISALLOW_COPY_AND_ASSIGN(Delegate); |
| 44 }; | 52 }; |
| 45 | 53 |
| 46 private: | 54 private: |
| 47 int checked_command_ = -1; | 55 int checked_command_ = -1; |
| 48 Delegate delegate_; | 56 Delegate delegate_; |
| 57 base::Closure menu_open_callback_; |
| 49 | 58 |
| 50 DISALLOW_COPY_AND_ASSIGN(TestModel); | 59 DISALLOW_COPY_AND_ASSIGN(TestModel); |
| 51 }; | 60 }; |
| 52 | 61 |
| 53 } // namespace | 62 } // namespace |
| 54 | 63 |
| 55 class MenuRunnerCocoaTest : public ViewsTestBase { | 64 class MenuRunnerCocoaTest : public ViewsTestBase { |
| 56 public: | 65 public: |
| 57 enum { | 66 enum { |
| 58 kWindowHeight = 200, | 67 kWindowHeight = 200, |
| (...skipping 23 matching lines...) Expand all Loading... |
| 82 void TearDown() override { | 91 void TearDown() override { |
| 83 if (runner_) { | 92 if (runner_) { |
| 84 runner_->Release(); | 93 runner_->Release(); |
| 85 runner_ = NULL; | 94 runner_ = NULL; |
| 86 } | 95 } |
| 87 | 96 |
| 88 parent_->CloseNow(); | 97 parent_->CloseNow(); |
| 89 ViewsTestBase::TearDown(); | 98 ViewsTestBase::TearDown(); |
| 90 } | 99 } |
| 91 | 100 |
| 92 // Runs the menu after scheduling |block| on the run loop. | 101 // Runs the menu after registering |callback| as the menu open callback. |
| 93 MenuRunner::RunResult RunMenu(dispatch_block_t block) { | 102 MenuRunner::RunResult RunMenu(const base::Closure& callback) { |
| 94 CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopCommonModes, ^{ | 103 menu_->set_menu_open_callback( |
| 95 EXPECT_TRUE(runner_->IsRunning()); | 104 base::Bind(&MenuRunnerCocoaTest::RunMenuWrapperCallback, |
| 96 block(); | 105 base::Unretained(this), callback)); |
| 97 }); | 106 return runner_->RunMenuAt(parent_, nullptr, gfx::Rect(), |
| 98 return runner_->RunMenuAt(parent_, NULL, gfx::Rect(), MENU_ANCHOR_TOPLEFT, | 107 MENU_ANCHOR_TOPLEFT, MenuRunner::CONTEXT_MENU); |
| 99 MenuRunner::CONTEXT_MENU); | |
| 100 } | 108 } |
| 101 | 109 |
| 102 // Runs then cancels a combobox menu and captures the frame of the anchoring | 110 // Runs then cancels a combobox menu and captures the frame of the anchoring |
| 103 // view. | 111 // view. |
| 104 MenuRunner::RunResult RunMenuAt(const gfx::Rect& anchor) { | 112 MenuRunner::RunResult RunMenuAt(const gfx::Rect& anchor) { |
| 105 last_anchor_frame_ = NSZeroRect; | 113 last_anchor_frame_ = NSZeroRect; |
| 106 | 114 |
| 107 // Should be one child (the compositor layer) before showing, and it should | 115 // Should be one child (the compositor layer) before showing, and it should |
| 108 // go up by one (the anchor view) while the menu is shown. | 116 // go up by one (the anchor view) while the menu is shown. |
| 109 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); | 117 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); |
| 110 CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopCommonModes, ^{ | 118 |
| 111 NSArray* subviews = [parent_->GetNativeView() subviews]; | 119 menu_->set_menu_open_callback(base::Bind( |
| 112 EXPECT_EQ(2u, [subviews count]); | 120 &MenuRunnerCocoaTest::RunMenuAtCallback, base::Unretained(this))); |
| 113 last_anchor_frame_ = [[subviews objectAtIndex:1] frame]; | 121 |
| 114 runner_->Cancel(); | |
| 115 }); | |
| 116 MenuRunner::RunResult result = runner_->RunMenuAt( | 122 MenuRunner::RunResult result = runner_->RunMenuAt( |
| 117 parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT, MenuRunner::COMBOBOX); | 123 parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT, MenuRunner::COMBOBOX); |
| 118 | 124 |
| 119 // Ensure the anchor view is removed. | 125 // Ensure the anchor view is removed. |
| 120 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); | 126 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); |
| 121 return result; | 127 return result; |
| 122 } | 128 } |
| 123 | 129 |
| 130 void MenuCancelCallback() { |
| 131 runner_->Cancel(); |
| 132 EXPECT_FALSE(runner_->IsRunning()); |
| 133 } |
| 134 |
| 135 void MenuDeleteCallback() { |
| 136 runner_->Release(); |
| 137 runner_ = nullptr; |
| 138 } |
| 139 |
| 124 protected: | 140 protected: |
| 125 scoped_ptr<TestModel> menu_; | 141 scoped_ptr<TestModel> menu_; |
| 126 internal::MenuRunnerImplCocoa* runner_ = nullptr; | 142 internal::MenuRunnerImplCocoa* runner_ = nullptr; |
| 127 views::Widget* parent_ = nullptr; | 143 views::Widget* parent_ = nullptr; |
| 128 NSRect last_anchor_frame_ = NSZeroRect; | 144 NSRect last_anchor_frame_ = NSZeroRect; |
| 129 | 145 |
| 130 private: | 146 private: |
| 147 void RunMenuWrapperCallback(const base::Closure& callback) { |
| 148 EXPECT_TRUE(runner_->IsRunning()); |
| 149 callback.Run(); |
| 150 } |
| 151 |
| 152 void RunMenuAtCallback() { |
| 153 NSArray* subviews = [parent_->GetNativeView() subviews]; |
| 154 EXPECT_EQ(2u, [subviews count]); |
| 155 last_anchor_frame_ = [[subviews objectAtIndex:1] frame]; |
| 156 runner_->Cancel(); |
| 157 } |
| 158 |
| 131 DISALLOW_COPY_AND_ASSIGN(MenuRunnerCocoaTest); | 159 DISALLOW_COPY_AND_ASSIGN(MenuRunnerCocoaTest); |
| 132 }; | 160 }; |
| 133 | 161 |
| 134 TEST_F(MenuRunnerCocoaTest, RunMenuAndCancel) { | 162 TEST_F(MenuRunnerCocoaTest, RunMenuAndCancel) { |
| 135 base::TimeDelta min_time = ui::EventTimeForNow(); | 163 base::TimeDelta min_time = ui::EventTimeForNow(); |
| 136 | 164 |
| 137 MenuRunner::RunResult result = RunMenu(^{ | 165 MenuRunner::RunResult result = RunMenu(base::Bind( |
| 138 runner_->Cancel(); | 166 &MenuRunnerCocoaTest::MenuCancelCallback, base::Unretained(this))); |
| 139 EXPECT_FALSE(runner_->IsRunning()); | |
| 140 }); | |
| 141 | 167 |
| 142 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); | 168 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); |
| 143 EXPECT_FALSE(runner_->IsRunning()); | 169 EXPECT_FALSE(runner_->IsRunning()); |
| 144 | 170 |
| 145 EXPECT_GE(runner_->GetClosingEventTime(), min_time); | 171 EXPECT_GE(runner_->GetClosingEventTime(), min_time); |
| 146 EXPECT_LE(runner_->GetClosingEventTime(), ui::EventTimeForNow()); | 172 EXPECT_LE(runner_->GetClosingEventTime(), ui::EventTimeForNow()); |
| 147 | 173 |
| 148 // Cancel again. | 174 // Cancel again. |
| 149 runner_->Cancel(); | 175 runner_->Cancel(); |
| 150 EXPECT_FALSE(runner_->IsRunning()); | 176 EXPECT_FALSE(runner_->IsRunning()); |
| 151 } | 177 } |
| 152 | 178 |
| 153 TEST_F(MenuRunnerCocoaTest, RunMenuAndDelete) { | 179 TEST_F(MenuRunnerCocoaTest, RunMenuAndDelete) { |
| 154 MenuRunner::RunResult result = RunMenu(^{ | 180 MenuRunner::RunResult result = RunMenu(base::Bind( |
| 155 runner_->Release(); | 181 &MenuRunnerCocoaTest::MenuDeleteCallback, base::Unretained(this))); |
| 156 runner_ = NULL; | |
| 157 }); | |
| 158 | |
| 159 EXPECT_EQ(MenuRunner::MENU_DELETED, result); | 182 EXPECT_EQ(MenuRunner::MENU_DELETED, result); |
| 160 } | 183 } |
| 161 | 184 |
| 162 TEST_F(MenuRunnerCocoaTest, RunMenuTwice) { | 185 TEST_F(MenuRunnerCocoaTest, RunMenuTwice) { |
| 163 for (int i = 0; i < 2; ++i) { | 186 for (int i = 0; i < 2; ++i) { |
| 164 MenuRunner::RunResult result = RunMenu(^{ | 187 MenuRunner::RunResult result = RunMenu(base::Bind( |
| 165 runner_->Cancel(); | 188 &MenuRunnerCocoaTest::MenuCancelCallback, base::Unretained(this))); |
| 166 }); | |
| 167 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); | 189 EXPECT_EQ(MenuRunner::NORMAL_EXIT, result); |
| 168 EXPECT_FALSE(runner_->IsRunning()); | 190 EXPECT_FALSE(runner_->IsRunning()); |
| 169 } | 191 } |
| 170 } | 192 } |
| 171 | 193 |
| 172 TEST_F(MenuRunnerCocoaTest, CancelWithoutRunning) { | 194 TEST_F(MenuRunnerCocoaTest, CancelWithoutRunning) { |
| 173 runner_->Cancel(); | 195 runner_->Cancel(); |
| 174 EXPECT_FALSE(runner_->IsRunning()); | 196 EXPECT_FALSE(runner_->IsRunning()); |
| 175 EXPECT_EQ(base::TimeDelta(), runner_->GetClosingEventTime()); | 197 EXPECT_EQ(base::TimeDelta(), runner_->GetClosingEventTime()); |
| 176 } | 198 } |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 217 // In RTL, Cocoa messes up the positioning unless the anchor rectangle is | 239 // In RTL, Cocoa messes up the positioning unless the anchor rectangle is |
| 218 // offset to the right of the view. The offset for the checkmark is also | 240 // offset to the right of the view. The offset for the checkmark is also |
| 219 // skipped, to give a better match to native behavior. | 241 // skipped, to give a better match to native behavior. |
| 220 base::i18n::SetICUDefaultLocale("he"); | 242 base::i18n::SetICUDefaultLocale("he"); |
| 221 RunMenuAt(anchor_rect); | 243 RunMenuAt(anchor_rect); |
| 222 EXPECT_EQ(combobox_rect.right(), last_anchor_frame_.origin.x); | 244 EXPECT_EQ(combobox_rect.right(), last_anchor_frame_.origin.x); |
| 223 } | 245 } |
| 224 | 246 |
| 225 } // namespace test | 247 } // namespace test |
| 226 } // namespace views | 248 } // namespace views |
| OLD | NEW |