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

Side by Side Diff: chrome/browser/ui/cocoa/wrench_menu/wrench_menu_controller.mm

Issue 1419993010: chrome: Rename WrenchMenuController to AppMenuController. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rewrap comments Created 5 years, 1 month 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
(Empty)
1 // Copyright (c) 2011 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 #import "chrome/browser/ui/cocoa/wrench_menu/wrench_menu_controller.h"
6
7 #include "base/basictypes.h"
8 #include "base/mac/bundle_locations.h"
9 #include "base/scoped_observer.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "chrome/app/chrome_command_ids.h"
13 #import "chrome/browser/app_controller_mac.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #import "chrome/browser/ui/cocoa/accelerators_cocoa.h"
18 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
19 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h"
20 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
21 #import "chrome/browser/ui/cocoa/encoding_menu_controller_delegate_mac.h"
22 #import "chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h"
23 #import "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
24 #import "chrome/browser/ui/cocoa/l10n_util.h"
25 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
26 #import "chrome/browser/ui/cocoa/wrench_menu/menu_tracked_root_view.h"
27 #import "chrome/browser/ui/cocoa/wrench_menu/recent_tabs_menu_model_delegate.h"
28 #include "chrome/browser/ui/toolbar/app_menu_model.h"
29 #include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
30 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
31 #include "chrome/browser/ui/toolbar/toolbar_actions_bar_observer.h"
32 #include "chrome/grit/generated_resources.h"
33 #include "components/ui/zoom/zoom_event_manager.h"
34 #include "content/public/browser/user_metrics.h"
35 #include "ui/base/l10n/l10n_util.h"
36 #include "ui/base/models/menu_model.h"
37 #include "ui/gfx/geometry/size.h"
38
39 namespace {
40 // Padding amounts on the left/right of a custom menu item (like the browser
41 // actions overflow container).
42 const int kLeftPadding = 16;
43 const int kRightPadding = 10;
44
45 // In *very* extreme cases, it's possible that there are so many overflowed
46 // actions, we won't be able to show them all. Cap the height so that the
47 // overflow won't make the menu larger than the height of the screen.
48 // Note: With this height, we can show 104 actions. Less than 0.0002% of our
49 // users will be affected.
50 const int kMaxOverflowContainerHeight = 416;
51 }
52
53 namespace wrench_menu_controller {
54 const CGFloat kWrenchBubblePointOffsetY = 6;
55 }
56
57 using base::UserMetricsAction;
58
59 @interface WrenchMenuController (Private)
60 - (void)createModel;
61 - (void)adjustPositioning;
62 - (void)performCommandDispatch:(NSNumber*)tag;
63 - (NSButton*)zoomDisplay;
64 - (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item;
65 - (void)removeAllItems:(NSMenu*)menu;
66 - (NSMenu*)recentTabsSubmenu;
67 - (RecentTabsSubMenuModel*)recentTabsMenuModel;
68 - (int)maxWidthForMenuModel:(ui::MenuModel*)model
69 modelIndex:(int)modelIndex;
70 @end
71
72 namespace WrenchMenuControllerInternal {
73
74 // A C++ delegate that handles the accelerators in the wrench menu.
75 class AcceleratorDelegate : public ui::AcceleratorProvider {
76 public:
77 bool GetAcceleratorForCommandId(int command_id,
78 ui::Accelerator* out_accelerator) override {
79 AcceleratorsCocoa* keymap = AcceleratorsCocoa::GetInstance();
80 const ui::Accelerator* accelerator =
81 keymap->GetAcceleratorForCommand(command_id);
82 if (!accelerator)
83 return false;
84 *out_accelerator = *accelerator;
85 return true;
86 }
87 };
88
89 class ZoomLevelObserver {
90 public:
91 ZoomLevelObserver(WrenchMenuController* controller,
92 ui_zoom::ZoomEventManager* manager)
93 : controller_(controller) {
94 subscription_ = manager->AddZoomLevelChangedCallback(
95 base::Bind(&ZoomLevelObserver::OnZoomLevelChanged,
96 base::Unretained(this)));
97 }
98
99 ~ZoomLevelObserver() {}
100
101 private:
102 void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change) {
103 AppMenuModel* appMenuModel = [controller_ appMenuModel];
104 appMenuModel->UpdateZoomControls();
105 const base::string16 level =
106 appMenuModel->GetLabelForCommandId(IDC_ZOOM_PERCENT_DISPLAY);
107 [[controller_ zoomDisplay] setTitle:SysUTF16ToNSString(level)];
108 }
109
110 scoped_ptr<content::HostZoomMap::Subscription> subscription_;
111
112 WrenchMenuController* controller_; // Weak; owns this.
113
114 DISALLOW_COPY_AND_ASSIGN(ZoomLevelObserver);
115 };
116
117 class ToolbarActionsBarObserverHelper : public ToolbarActionsBarObserver {
118 public:
119 ToolbarActionsBarObserverHelper(WrenchMenuController* controller,
120 ToolbarActionsBar* toolbar_actions_bar)
121 : controller_(controller),
122 scoped_observer_(this) {
123 scoped_observer_.Add(toolbar_actions_bar);
124 }
125 ~ToolbarActionsBarObserverHelper() override {}
126
127 private:
128 // ToolbarActionsBarObserver:
129 void OnToolbarActionsBarDestroyed() override {
130 scoped_observer_.RemoveAll();
131 }
132 void OnToolbarActionsBarDidStartResize() override {
133 [controller_ updateBrowserActionsSubmenu];
134 }
135
136 WrenchMenuController* controller_;
137 ScopedObserver<ToolbarActionsBar, ToolbarActionsBarObserver> scoped_observer_;
138
139 DISALLOW_COPY_AND_ASSIGN(ToolbarActionsBarObserverHelper);
140 };
141
142 } // namespace WrenchMenuControllerInternal
143
144 @implementation WrenchMenuController
145
146 - (id)initWithBrowser:(Browser*)browser {
147 if ((self = [super init])) {
148 browser_ = browser;
149 acceleratorDelegate_.reset(
150 new WrenchMenuControllerInternal::AcceleratorDelegate());
151 [self createModel];
152 }
153 return self;
154 }
155
156 - (void)dealloc {
157 [self browserWillBeDestroyed];
158 [super dealloc];
159 }
160
161 - (void)browserWillBeDestroyed {
162 // This method indicates imminent destruction. Destroy owned objects that hold
163 // a weak Browser*, or pass this call onto reference counted objects.
164 recentTabsMenuModelDelegate_.reset();
165 [self setModel:nullptr];
166 appMenuModel_.reset();
167 buttonViewController_.reset();
168
169 // The observers should most likely already be destroyed (since they're reset
170 // in -menuDidClose:), but sometimes shutdown can be funny, so make sure to
171 // not leave any dangling observers.
172 zoom_level_observer_.reset();
173 toolbar_actions_bar_observer_.reset();
174
175 [browserActionsController_ browserWillBeDestroyed];
176
177 browser_ = nullptr;
178 }
179
180 - (void)addItemToMenu:(NSMenu*)menu
181 atIndex:(NSInteger)index
182 fromModel:(ui::MenuModel*)model {
183 // Non-button item types should be built as normal items, with the exception
184 // of the extensions overflow menu.
185 int command_id = model->GetCommandIdAt(index);
186 if (model->GetTypeAt(index) != ui::MenuModel::TYPE_BUTTON_ITEM &&
187 command_id != IDC_EXTENSIONS_OVERFLOW_MENU) {
188 [super addItemToMenu:menu
189 atIndex:index
190 fromModel:model];
191 return;
192 }
193
194 // Handle the special-cased menu items.
195 base::scoped_nsobject<NSMenuItem> customItem(
196 [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]);
197 MenuTrackedRootView* view = nil;
198 switch (command_id) {
199 case IDC_EXTENSIONS_OVERFLOW_MENU: {
200 browserActionsMenuItem_ = customItem.get();
201 view = [buttonViewController_ toolbarActionsOverflowItem];
202 BrowserActionsContainerView* containerView =
203 [buttonViewController_ overflowActionsContainerView];
204
205 // The overflow browser actions container can't function properly without
206 // a main counterpart, so if the browser window hasn't initialized, abort.
207 // (This is fine because we re-populate the wrench menu each time before
208 // we show it.)
209 if (!browser_->window())
210 break;
211
212 BrowserActionsController* mainController =
213 [[[BrowserWindowController browserWindowControllerForWindow:browser_->
214 window()->GetNativeWindow()] toolbarController]
215 browserActionsController];
216 toolbar_actions_bar_observer_.reset(
217 new WrenchMenuControllerInternal::ToolbarActionsBarObserverHelper(
218 self, [mainController toolbarActionsBar]));
219 browserActionsController_.reset(
220 [[BrowserActionsController alloc]
221 initWithBrowser:browser_
222 containerView:containerView
223 mainController:mainController]);
224 break;
225 }
226 case IDC_EDIT_MENU:
227 view = [buttonViewController_ editItem];
228 break;
229 case IDC_ZOOM_MENU:
230 view = [buttonViewController_ zoomItem];
231 break;
232 default:
233 NOTREACHED();
234 break;
235 }
236 DCHECK(view);
237 [customItem setView:view];
238 [view setMenuItem:customItem];
239 [self adjustPositioning];
240 [menu insertItem:customItem.get() atIndex:index];
241 }
242
243 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
244 const BOOL enabled = [super validateUserInterfaceItem:item];
245
246 NSMenuItem* menuItem = (id)item;
247 ui::MenuModel* model =
248 static_cast<ui::MenuModel*>(
249 [[menuItem representedObject] pointerValue]);
250
251 // The section headers in the recent tabs submenu should be bold and black if
252 // a font list is specified for the items (bold is already applied in the
253 // |MenuController| as the font list returned by |GetLabelFontListAt| is
254 // bold).
255 if (model && model == [self recentTabsMenuModel]) {
256 if (model->GetLabelFontListAt([item tag])) {
257 DCHECK([menuItem attributedTitle]);
258 base::scoped_nsobject<NSMutableAttributedString> title(
259 [[NSMutableAttributedString alloc]
260 initWithAttributedString:[menuItem attributedTitle]]);
261 [title addAttribute:NSForegroundColorAttributeName
262 value:[NSColor blackColor]
263 range:NSMakeRange(0, [title length])];
264 [menuItem setAttributedTitle:title.get()];
265 } else {
266 // Not a section header. Add a tooltip with the title and the URL.
267 std::string url;
268 base::string16 title;
269 if ([self recentTabsMenuModel]->GetURLAndTitleForItemAtIndex(
270 [item tag], &url, &title)) {
271 [menuItem setToolTip:
272 cocoa_l10n_util::TooltipForURLAndTitle(
273 base::SysUTF8ToNSString(url), base::SysUTF16ToNSString(title))];
274 }
275 }
276 }
277
278 return enabled;
279 }
280
281 - (NSMenu*)bookmarkSubMenu {
282 NSString* title = l10n_util::GetNSStringWithFixup(IDS_BOOKMARKS_MENU);
283 return [[[self menu] itemWithTitle:title] submenu];
284 }
285
286 - (void)updateBookmarkSubMenu {
287 NSMenu* bookmarkMenu = [self bookmarkSubMenu];
288 DCHECK(bookmarkMenu);
289
290 bookmarkMenuBridge_.reset(new BookmarkMenuBridge(
291 [self appMenuModel]->browser()->profile(), bookmarkMenu));
292 }
293
294 - (void)updateBrowserActionsSubmenu {
295 MenuTrackedRootView* view =
296 [buttonViewController_ toolbarActionsOverflowItem];
297 BrowserActionsContainerView* containerView =
298 [buttonViewController_ overflowActionsContainerView];
299
300 // Find the preferred container size for the menu width.
301 int menuWidth = [[self menu] size].width;
302 int maxContainerWidth = menuWidth - kLeftPadding - kRightPadding;
303 // Don't let the menu change sizes on us. (We lift this restriction every time
304 // the menu updates, so if something changes, this won't leave us with an
305 // awkward size.)
306 [[self menu] setMinimumWidth:menuWidth];
307 gfx::Size preferredContainerSize =
308 [browserActionsController_ sizeForOverflowWidth:maxContainerWidth];
309
310 // Set the origins and preferred size for the container.
311 // View hierarchy is as follows (from parent > child):
312 // |view| > |anonymous view| > containerView. We have to set the origin
313 // and size of each for it display properly.
314 // The parent views each have a size of the full width of the menu, so we can
315 // properly position the container.
316 NSSize parentSize = NSMakeSize(menuWidth,
317 std::min(preferredContainerSize.height(),
318 kMaxOverflowContainerHeight));
319 [view setFrameSize:parentSize];
320 [[containerView superview] setFrameSize:parentSize];
321
322 // The container view gets its preferred size.
323 [containerView setFrameSize:NSMakeSize(preferredContainerSize.width(),
324 preferredContainerSize.height())];
325 [browserActionsController_ update];
326
327 [view setFrameOrigin:NSZeroPoint];
328 [[containerView superview] setFrameOrigin:NSZeroPoint];
329 [containerView setFrameOrigin:NSMakePoint(kLeftPadding, 0)];
330 }
331
332 - (void)menuWillOpen:(NSMenu*)menu {
333 [super menuWillOpen:menu];
334
335 zoom_level_observer_.reset(
336 new WrenchMenuControllerInternal::ZoomLevelObserver(
337 self,
338 ui_zoom::ZoomEventManager::GetForBrowserContext(
339 browser_->profile())));
340 NSString* title = base::SysUTF16ToNSString(
341 [self appMenuModel]->GetLabelForCommandId(IDC_ZOOM_PERCENT_DISPLAY));
342 [[[buttonViewController_ zoomItem] viewWithTag:IDC_ZOOM_PERCENT_DISPLAY]
343 setTitle:title];
344 content::RecordAction(UserMetricsAction("ShowAppMenu"));
345
346 NSImage* icon = [self appMenuModel]->browser()->window()->IsFullscreen()
347 ? [NSImage imageNamed:NSImageNameExitFullScreenTemplate]
348 : [NSImage imageNamed:NSImageNameEnterFullScreenTemplate];
349 [[buttonViewController_ zoomFullScreen] setImage:icon];
350 }
351
352 - (void)menuDidClose:(NSMenu*)menu {
353 [super menuDidClose:menu];
354 // We don't need to observe changes to zoom or toolbar size when the menu is
355 // closed, since we instantiate it with the proper value and recreate the menu
356 // on each show. (We do this in -menuNeedsUpdate:, which is called when the
357 // menu is about to be displayed at the start of a tracking session.)
358 zoom_level_observer_.reset();
359 toolbar_actions_bar_observer_.reset();
360 }
361
362 - (void)menuNeedsUpdate:(NSMenu*)menu {
363 // First empty out the menu and create a new model.
364 [self removeAllItems:menu];
365 [self createModel];
366 [menu setMinimumWidth:0];
367
368 // Create a new menu, which cannot be swapped because the tracking is about to
369 // start, so simply copy the items.
370 NSMenu* newMenu = [self menuFromModel:model_];
371 NSArray* itemArray = [newMenu itemArray];
372 [self removeAllItems:newMenu];
373 for (NSMenuItem* item in itemArray) {
374 [menu addItem:item];
375 }
376
377 [self updateRecentTabsSubmenu];
378 [self updateBookmarkSubMenu];
379 [self updateBrowserActionsSubmenu];
380 }
381
382 // Used to dispatch commands from the Wrench menu. The custom items within the
383 // menu cannot be hooked up directly to First Responder because the window in
384 // which the controls reside is not the BrowserWindowController, but a
385 // NSCarbonMenuWindow; this screws up the typical |-commandDispatch:| system.
386 - (IBAction)dispatchWrenchMenuCommand:(id)sender {
387 NSInteger tag = [sender tag];
388 if (sender == [buttonViewController_ zoomPlus] ||
389 sender == [buttonViewController_ zoomMinus]) {
390 // Do a direct dispatch rather than scheduling on the outermost run loop,
391 // which would not get hit until after the menu had closed.
392 [self performCommandDispatch:[NSNumber numberWithInt:tag]];
393
394 // The zoom buttons should not close the menu if opened sticky.
395 if ([sender respondsToSelector:@selector(isTracking)] &&
396 [sender performSelector:@selector(isTracking)]) {
397 [menu_ cancelTracking];
398 }
399 } else {
400 // The custom views within the Wrench menu are abnormal and keep the menu
401 // open after a target-action. Close the menu manually.
402 [menu_ cancelTracking];
403
404 // Executing certain commands from the nested run loop of the menu can lead
405 // to wonky behavior (e.g. http://crbug.com/49716). To avoid this, schedule
406 // the dispatch on the outermost run loop.
407 [self performSelector:@selector(performCommandDispatch:)
408 withObject:[NSNumber numberWithInt:tag]
409 afterDelay:0.0];
410 }
411 }
412
413 // Used to perform the actual dispatch on the outermost runloop.
414 - (void)performCommandDispatch:(NSNumber*)tag {
415 [self appMenuModel]->ExecuteCommand([tag intValue], 0);
416 }
417
418 - (AppMenuModel*)appMenuModel {
419 // Don't use |appMenuModel_| so that a test can override the generic one.
420 return static_cast<AppMenuModel*>(model_);
421 }
422
423 - (void)updateRecentTabsSubmenu {
424 ui::MenuModel* model = [self recentTabsMenuModel];
425 if (model) {
426 recentTabsMenuModelDelegate_.reset(
427 new RecentTabsMenuModelDelegate(model, [self recentTabsSubmenu]));
428 }
429 }
430
431 - (BrowserActionsController*)browserActionsController {
432 return browserActionsController_.get();
433 }
434
435 - (void)createModel {
436 DCHECK(browser_);
437 recentTabsMenuModelDelegate_.reset();
438 appMenuModel_.reset(new AppMenuModel(acceleratorDelegate_.get(), browser_));
439 [self setModel:appMenuModel_.get()];
440
441 buttonViewController_.reset(
442 [[WrenchMenuButtonViewController alloc] initWithController:self]);
443 [buttonViewController_ view];
444
445 // See comment in containerSuperviewFrameChanged:.
446 NSView* containerSuperview =
447 [[buttonViewController_ overflowActionsContainerView] superview];
448 [containerSuperview setPostsFrameChangedNotifications:YES];
449 }
450
451 // Fit the localized strings into the Cut/Copy/Paste control, then resize the
452 // whole menu item accordingly.
453 - (void)adjustPositioning {
454 const CGFloat kButtonPadding = 12;
455 CGFloat delta = 0;
456
457 // Go through the three buttons from right-to-left, adjusting the size to fit
458 // the localized strings while keeping them all aligned on their horizontal
459 // edges.
460 NSButton* views[] = {
461 [buttonViewController_ editPaste],
462 [buttonViewController_ editCopy],
463 [buttonViewController_ editCut]
464 };
465 for (size_t i = 0; i < arraysize(views); ++i) {
466 NSButton* button = views[i];
467 CGFloat originalWidth = NSWidth([button frame]);
468
469 // Do not let |-sizeToFit| change the height of the button.
470 NSSize size = [button frame].size;
471 [button sizeToFit];
472 size.width = [button frame].size.width + kButtonPadding;
473 [button setFrameSize:size];
474
475 CGFloat newWidth = size.width;
476 delta += newWidth - originalWidth;
477
478 NSRect frame = [button frame];
479 frame.origin.x -= delta;
480 [button setFrame:frame];
481 }
482
483 // Resize the menu item by the total amound the buttons changed so that the
484 // spacing between the buttons and the title remains the same.
485 NSRect itemFrame = [[buttonViewController_ editItem] frame];
486 itemFrame.size.width += delta;
487 [[buttonViewController_ editItem] setFrame:itemFrame];
488
489 // Also resize the superview of the buttons, which is an NSView used to slide
490 // when the item title is too big and GTM resizes it.
491 NSRect parentFrame = [[[buttonViewController_ editCut] superview] frame];
492 parentFrame.size.width += delta;
493 parentFrame.origin.x -= delta;
494 [[[buttonViewController_ editCut] superview] setFrame:parentFrame];
495 }
496
497 - (NSButton*)zoomDisplay {
498 return [buttonViewController_ zoomDisplay];
499 }
500
501 - (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item {
502 if (browserActionsController_.get()) {
503 [browserActionsController_ setFocusedInOverflow:
504 (item == browserActionsMenuItem_)];
505 }
506 }
507
508 // -[NSMenu removeAllItems] is only available on 10.6+.
509 - (void)removeAllItems:(NSMenu*)menu {
510 while ([menu numberOfItems]) {
511 [menu removeItemAtIndex:0];
512 }
513 }
514
515 - (NSMenu*)recentTabsSubmenu {
516 NSString* title = l10n_util::GetNSStringWithFixup(IDS_RECENT_TABS_MENU);
517 return [[[self menu] itemWithTitle:title] submenu];
518 }
519
520 // The recent tabs menu model is recognized by the existence of either the
521 // kRecentlyClosedHeaderCommandId or the kDisabledRecentlyClosedHeaderCommandId.
522 - (RecentTabsSubMenuModel*)recentTabsMenuModel {
523 int index = 0;
524 // Start searching at the wrench menu model level, |model| will be updated
525 // only if the command we're looking for is found in one of the [sub]menus.
526 ui::MenuModel* model = [self appMenuModel];
527 if (ui::MenuModel::GetModelAndIndexForCommandId(
528 RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId, &model,
529 &index)) {
530 return static_cast<RecentTabsSubMenuModel*>(model);
531 }
532 if (ui::MenuModel::GetModelAndIndexForCommandId(
533 RecentTabsSubMenuModel::kDisabledRecentlyClosedHeaderCommandId,
534 &model, &index)) {
535 return static_cast<RecentTabsSubMenuModel*>(model);
536 }
537 return NULL;
538 }
539
540 // This overrdies the parent class to return a custom width for recent tabs
541 // menu.
542 - (int)maxWidthForMenuModel:(ui::MenuModel*)model
543 modelIndex:(int)modelIndex {
544 RecentTabsSubMenuModel* recentTabsMenuModel = [self recentTabsMenuModel];
545 if (recentTabsMenuModel && recentTabsMenuModel == model) {
546 return recentTabsMenuModel->GetMaxWidthForItemAtIndex(modelIndex);
547 }
548 return -1;
549 }
550
551 @end // @implementation WrenchMenuController
552
553 ////////////////////////////////////////////////////////////////////////////////
554
555 @interface WrenchMenuButtonViewController ()
556 - (void)containerSuperviewFrameChanged:(NSNotification*)notification;
557 @end
558
559 @implementation WrenchMenuButtonViewController
560
561 @synthesize editItem = editItem_;
562 @synthesize editCut = editCut_;
563 @synthesize editCopy = editCopy_;
564 @synthesize editPaste = editPaste_;
565 @synthesize zoomItem = zoomItem_;
566 @synthesize zoomPlus = zoomPlus_;
567 @synthesize zoomDisplay = zoomDisplay_;
568 @synthesize zoomMinus = zoomMinus_;
569 @synthesize zoomFullScreen = zoomFullScreen_;
570 @synthesize toolbarActionsOverflowItem = toolbarActionsOverflowItem_;
571 @synthesize overflowActionsContainerView = overflowActionsContainerView_;
572
573 - (id)initWithController:(WrenchMenuController*)controller {
574 if ((self = [super initWithNibName:@"WrenchMenu"
575 bundle:base::mac::FrameworkBundle()])) {
576 propertyReleaser_.Init(self, [WrenchMenuButtonViewController class]);
577 controller_ = controller;
578 [[NSNotificationCenter defaultCenter]
579 addObserver:self
580 selector:@selector(containerSuperviewFrameChanged:)
581 name:NSViewFrameDidChangeNotification
582 object:[overflowActionsContainerView_ superview]];
583 }
584 return self;
585 }
586
587 - (void)dealloc {
588 [[NSNotificationCenter defaultCenter] removeObserver:self];
589 [super dealloc];
590 }
591
592 - (IBAction)dispatchWrenchMenuCommand:(id)sender {
593 [controller_ dispatchWrenchMenuCommand:sender];
594 }
595
596 - (void)containerSuperviewFrameChanged:(NSNotification*)notification {
597 // AppKit menus were probably never designed with a view like the browser
598 // actions container in mind, and, as a result, we come across a few oddities.
599 // One of these is that the container's superview will, on some versions of
600 // OSX, change frame position sometime after the the menu begins tracking
601 // (and thus, after all our ability to adjust it normally). Throw in the
602 // towel, and simply don't let the frame move from where it's supposed to be.
603 // TODO(devlin): Yet another Cocoa hack. It'd be good to find a workaround,
604 // but unlikely unless we replace the Cocoa menu implementation.
605 NSView* containerSuperview = [overflowActionsContainerView_ superview];
606 if (NSMinX([containerSuperview frame]) != 0)
607 [containerSuperview setFrameOrigin:NSZeroPoint];
608 }
609
610 @end // @implementation WrenchMenuButtonViewController
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698