Chromium Code Reviews| 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 #import "ui/base/cocoa/menu_controller.h" | |
| 12 #include "ui/base/models/simple_menu_model.h" | 13 #include "ui/base/models/simple_menu_model.h" |
| 13 #include "ui/events/event_utils.h" | 14 #include "ui/events/event_utils.h" |
| 14 #include "ui/views/test/views_test_base.h" | 15 #include "ui/views/test/views_test_base.h" |
| 15 | 16 |
| 17 // A helper class to handle menu open notifications. | |
| 18 @interface MenuOpenWatcher : NSObject { | |
| 19 dispatch_block_t openCallback_; | |
|
tapted
2016/03/24 02:10:36
nit: @private
| |
| 20 } | |
| 21 | |
| 22 // Method to handle menu open notification. | |
| 23 - (void)menuWillOpen:(NSNotification*)notification; | |
| 24 | |
| 25 // Block to be invoked on menu open notification. Weak. Clients must ensure that | |
| 26 // it remains in a valid state. | |
| 27 @property(nonatomic, assign) dispatch_block_t openCallback; | |
| 28 | |
| 29 @end | |
| 30 | |
| 31 @implementation MenuOpenWatcher | |
| 32 | |
| 33 @synthesize openCallback = openCallback_; | |
| 34 | |
| 35 - (id)init { | |
| 36 if (self = [super init]) { | |
|
tapted
2016/03/24 02:10:36
nit: extra parens around assignment used as condit
| |
| 37 [[NSNotificationCenter defaultCenter] | |
| 38 addObserver:self | |
| 39 selector:@selector(menuWillOpen:) | |
| 40 name:kMenuControllerMenuWillOpenNotification | |
| 41 object:nil]; | |
| 42 } | |
| 43 return self; | |
| 44 } | |
| 45 | |
| 46 - (void)dealloc { | |
| 47 [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
| 48 [super dealloc]; | |
| 49 } | |
| 50 | |
| 51 - (void)menuWillOpen:(NSNotification*)notification { | |
| 52 if (openCallback_) | |
| 53 openCallback_(); | |
| 54 } | |
| 55 | |
| 56 @end | |
| 57 | |
| 16 namespace views { | 58 namespace views { |
| 17 namespace test { | 59 namespace test { |
| 18 namespace { | 60 namespace { |
| 19 | 61 |
| 20 class TestModel : public ui::SimpleMenuModel { | 62 class TestModel : public ui::SimpleMenuModel { |
|
tapted
2016/03/24 02:10:36
Can we override MenuModel::MenuWillShow? Here, or
karandeepb
2016/04/06 05:52:48
Done. Though the code looks slightly uglier now si
| |
| 21 public: | 63 public: |
| 22 TestModel() : ui::SimpleMenuModel(&delegate_), delegate_(this) {} | 64 TestModel() : ui::SimpleMenuModel(&delegate_), delegate_(this) {} |
| 23 | 65 |
| 24 void set_checked_command(int command) { checked_command_ = command; } | 66 void set_checked_command(int command) { checked_command_ = command; } |
| 25 | 67 |
| 26 private: | 68 private: |
| 27 class Delegate : public ui::SimpleMenuModel::Delegate { | 69 class Delegate : public ui::SimpleMenuModel::Delegate { |
| 28 public: | 70 public: |
| 29 explicit Delegate(TestModel* model) : model_(model) {} | 71 explicit Delegate(TestModel* model) : model_(model) {} |
| 30 bool IsCommandIdChecked(int command_id) const override { | 72 bool IsCommandIdChecked(int command_id) const override { |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 70 menu_->AddCheckItem(0, base::ASCIIToUTF16("Menu Item")); | 112 menu_->AddCheckItem(0, base::ASCIIToUTF16("Menu Item")); |
| 71 | 113 |
| 72 parent_ = new views::Widget(); | 114 parent_ = new views::Widget(); |
| 73 parent_->Init(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS)); | 115 parent_->Init(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS)); |
| 74 parent_->SetBounds( | 116 parent_->SetBounds( |
| 75 gfx::Rect(kWindowOffset, kWindowOffset, kWindowWidth, kWindowHeight)); | 117 gfx::Rect(kWindowOffset, kWindowOffset, kWindowWidth, kWindowHeight)); |
| 76 parent_->Show(); | 118 parent_->Show(); |
| 77 | 119 |
| 78 runner_ = new internal::MenuRunnerImplCocoa(menu_.get()); | 120 runner_ = new internal::MenuRunnerImplCocoa(menu_.get()); |
| 79 EXPECT_FALSE(runner_->IsRunning()); | 121 EXPECT_FALSE(runner_->IsRunning()); |
| 122 menu_watcher_.reset([[MenuOpenWatcher alloc] init]); | |
| 80 } | 123 } |
| 81 | 124 |
| 82 void TearDown() override { | 125 void TearDown() override { |
| 83 if (runner_) { | 126 if (runner_) { |
| 84 runner_->Release(); | 127 runner_->Release(); |
| 85 runner_ = NULL; | 128 runner_ = NULL; |
| 86 } | 129 } |
| 87 | 130 |
| 88 parent_->CloseNow(); | 131 parent_->CloseNow(); |
| 89 ViewsTestBase::TearDown(); | 132 ViewsTestBase::TearDown(); |
| 90 } | 133 } |
| 91 | 134 |
| 92 // Runs the menu after scheduling |block| on the run loop. | 135 // Runs the menu after scheduling |block| on the run loop. |
| 93 MenuRunner::RunResult RunMenu(dispatch_block_t block) { | 136 MenuRunner::RunResult RunMenu(dispatch_block_t block) { |
| 94 CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopCommonModes, ^{ | 137 [menu_watcher_ setOpenCallback:^{ |
| 95 EXPECT_TRUE(runner_->IsRunning()); | 138 EXPECT_TRUE(runner_->IsRunning()); |
| 96 block(); | 139 block(); |
| 97 }); | 140 }]; |
| 98 return runner_->RunMenuAt(parent_, NULL, gfx::Rect(), MENU_ANCHOR_TOPLEFT, | 141 MenuRunner::RunResult result = |
| 99 MenuRunner::CONTEXT_MENU); | 142 runner_->RunMenuAt(parent_, NULL, gfx::Rect(), MENU_ANCHOR_TOPLEFT, |
|
tapted
2016/03/24 02:10:36
nit (while you're here) NULL -> nullptr
karandeepb
2016/04/06 05:52:48
Done.
| |
| 143 MenuRunner::CONTEXT_MENU); | |
| 144 [menu_watcher_ setOpenCallback:nil]; | |
| 145 return result; | |
| 100 } | 146 } |
| 101 | 147 |
| 102 // Runs then cancels a combobox menu and captures the frame of the anchoring | 148 // Runs then cancels a combobox menu and captures the frame of the anchoring |
| 103 // view. | 149 // view. |
| 104 MenuRunner::RunResult RunMenuAt(const gfx::Rect& anchor) { | 150 MenuRunner::RunResult RunMenuAt(const gfx::Rect& anchor) { |
| 105 last_anchor_frame_ = NSZeroRect; | 151 last_anchor_frame_ = NSZeroRect; |
| 106 | 152 |
| 107 // Should be one child (the compositor layer) before showing, and it should | 153 // 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. | 154 // go up by one (the anchor view) while the menu is shown. |
| 109 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); | 155 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); |
| 110 CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopCommonModes, ^{ | 156 |
| 157 [menu_watcher_ setOpenCallback:^{ | |
| 111 NSArray* subviews = [parent_->GetNativeView() subviews]; | 158 NSArray* subviews = [parent_->GetNativeView() subviews]; |
| 112 EXPECT_EQ(2u, [subviews count]); | 159 EXPECT_EQ(2u, [subviews count]); |
| 113 last_anchor_frame_ = [[subviews objectAtIndex:1] frame]; | 160 last_anchor_frame_ = [[subviews objectAtIndex:1] frame]; |
| 114 runner_->Cancel(); | 161 runner_->Cancel(); |
| 115 }); | 162 }]; |
| 163 | |
| 116 MenuRunner::RunResult result = runner_->RunMenuAt( | 164 MenuRunner::RunResult result = runner_->RunMenuAt( |
| 117 parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT, MenuRunner::COMBOBOX); | 165 parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT, MenuRunner::COMBOBOX); |
| 118 | 166 |
| 119 // Ensure the anchor view is removed. | 167 // Ensure the anchor view is removed. |
| 120 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); | 168 EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); |
| 169 [menu_watcher_ setOpenCallback:nil]; | |
| 121 return result; | 170 return result; |
| 122 } | 171 } |
| 123 | 172 |
| 124 protected: | 173 protected: |
| 125 scoped_ptr<TestModel> menu_; | 174 scoped_ptr<TestModel> menu_; |
| 175 base::scoped_nsobject<MenuOpenWatcher> menu_watcher_; | |
| 126 internal::MenuRunnerImplCocoa* runner_ = nullptr; | 176 internal::MenuRunnerImplCocoa* runner_ = nullptr; |
| 127 views::Widget* parent_ = nullptr; | 177 views::Widget* parent_ = nullptr; |
| 128 NSRect last_anchor_frame_ = NSZeroRect; | 178 NSRect last_anchor_frame_ = NSZeroRect; |
| 129 | 179 |
| 130 private: | 180 private: |
| 131 DISALLOW_COPY_AND_ASSIGN(MenuRunnerCocoaTest); | 181 DISALLOW_COPY_AND_ASSIGN(MenuRunnerCocoaTest); |
| 132 }; | 182 }; |
| 133 | 183 |
| 134 TEST_F(MenuRunnerCocoaTest, RunMenuAndCancel) { | 184 TEST_F(MenuRunnerCocoaTest, RunMenuAndCancel) { |
| 135 base::TimeDelta min_time = ui::EventTimeForNow(); | 185 base::TimeDelta min_time = ui::EventTimeForNow(); |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 217 // In RTL, Cocoa messes up the positioning unless the anchor rectangle is | 267 // 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 | 268 // offset to the right of the view. The offset for the checkmark is also |
| 219 // skipped, to give a better match to native behavior. | 269 // skipped, to give a better match to native behavior. |
| 220 base::i18n::SetICUDefaultLocale("he"); | 270 base::i18n::SetICUDefaultLocale("he"); |
| 221 RunMenuAt(anchor_rect); | 271 RunMenuAt(anchor_rect); |
| 222 EXPECT_EQ(combobox_rect.right(), last_anchor_frame_.origin.x); | 272 EXPECT_EQ(combobox_rect.right(), last_anchor_frame_.origin.x); |
| 223 } | 273 } |
| 224 | 274 |
| 225 } // namespace test | 275 } // namespace test |
| 226 } // namespace views | 276 } // namespace views |
| OLD | NEW |