1 // Copyright (c) 2012 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.
5 #import "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #import "chrome/browser/ui/cocoa/extensions/browser_action_button.h"
14 #import "chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h"
15 #import "chrome/browser/ui/cocoa/extensions/extension_popup_controller.h"
16 #import "chrome/browser/ui/cocoa/image_button_cell.h"
17 #import "chrome/browser/ui/cocoa/menu_button.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
20 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
21 #include "chrome/browser/ui/toolbar/toolbar_actions_bar_delegate.h"
22 #include "grit/theme_resources.h"
23 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h"
25 NSString* const kBrowserActionVisibilityChangedNotification =
26 @"BrowserActionVisibilityChangedNotification";
30 const CGFloat kAnimationDuration = 0.2;
32 const CGFloat kChevronWidth = 18;
34 // Since the container is the maximum height of the toolbar, we have
35 // to move the buttons up by this amount in order to have them look
36 // vertically centered within the toolbar.
37 const CGFloat kBrowserActionOriginYOffset = 5.0;
39 // How far to inset from the bottom of the view to get the top border
40 // of the popup 2px below the bottom of the Omnibox.
41 const CGFloat kBrowserActionBubbleYOffset = 3.0;
45 @interface BrowserActionsController(Private)
47 // Creates and adds a view for the given |action| at |index|.
48 - (void)addViewForAction:(ToolbarActionViewController*)action
49 withIndex:(NSUInteger)index;
51 // Removes the view for the given |action| from the ccontainer.
52 - (void)removeViewForAction:(ToolbarActionViewController*)action;
54 // Removes views for all actions.
55 - (void)removeAllViews;
57 // Redraws the BrowserActionsContainerView and updates the button order to match
58 // the order in the ToolbarActionsBar.
61 // Resizes the container to the specified |width|, optionally animating.
62 - (void)resizeContainerToWidth:(CGFloat)width
63 shouldAnimate:(BOOL)animate;
65 // Sets the container to be either hidden or visible based on whether there are
66 // any actions to show.
67 // Returns whether the container is visible.
68 - (BOOL)updateContainerVisibility;
70 // During container resizing, buttons become more transparent as they are pushed
71 // off the screen. This method updates each button's opacity determined by the
72 // position of the button.
73 - (void)updateButtonOpacity;
75 // Returns the existing button associated with the given id; nil if it cannot be
77 - (BrowserActionButton*)buttonForId:(const std::string&)id;
79 // Notification handlers for events registered by the class.
81 // Updates each button's opacity, the cursor rects and chevron position.
82 - (void)containerFrameChanged:(NSNotification*)notification;
84 // Hides the chevron and unhides every hidden button so that dragging the
85 // container out smoothly shows the Browser Action buttons.
86 - (void)containerDragStart:(NSNotification*)notification;
88 // Sends a notification for the toolbar to reposition surrounding UI elements.
89 - (void)containerDragging:(NSNotification*)notification;
91 // Determines which buttons need to be hidden based on the new size, hides them
92 // and updates the chevron overflow menu. Also fires a notification to let the
93 // toolbar know that the drag has finished.
94 - (void)containerDragFinished:(NSNotification*)notification;
96 // Sends a notification for the toolbar to determine whether the container can
97 // translate with a delta on x-axis.
98 - (void)containerWillTranslateOnX:(NSNotification*)notification;
100 // Adjusts the position of the surrounding action buttons depending on where the
101 // button is within the container.
102 - (void)actionButtonDragging:(NSNotification*)notification;
104 // Updates the position of the Browser Actions within the container. This fires
105 // when _any_ Browser Action button is done dragging to keep all open windows in
107 - (void)actionButtonDragFinished:(NSNotification*)notification;
109 // Moves the given button both visually and within the toolbar model to the
111 - (void)moveButton:(BrowserActionButton*)button
112 toIndex:(NSUInteger)index
113 animate:(BOOL)animate;
115 // Handles clicks for BrowserActionButtons.
116 - (BOOL)browserActionClicked:(BrowserActionButton*)button;
118 // The reason |frame| is specified in these chevron functions is because the
119 // container may be animating and the end frame of the animation should be
120 // passed instead of the current frame (which may be off and cause the chevron
121 // to jump at the end of its animation).
123 // Shows the overflow chevron button depending on whether there are any hidden
124 // extensions within the frame given.
125 - (void)showChevronIfNecessaryInFrame:(NSRect)frame animate:(BOOL)animate;
127 // Moves the chevron to its correct position within |frame|.
128 - (void)updateChevronPositionInFrame:(NSRect)frame;
130 // Shows or hides the chevron, animating as specified by |animate|.
131 - (void)setChevronHidden:(BOOL)hidden
132 inFrame:(NSRect)frame
133 animate:(BOOL)animate;
135 // Handles when a menu item within the chevron overflow menu is selected.
136 - (void)chevronItemSelected:(id)menuItem;
138 // Updates the container's grippy cursor based on the number of hidden buttons.
139 - (void)updateGrippyCursors;
141 - (ToolbarActionsBar*)toolbarActionsBar;
147 // A bridge between the ToolbarActionsBar and the BrowserActionsController.
148 class ToolbarActionsBarBridge : public ToolbarActionsBarDelegate {
150 explicit ToolbarActionsBarBridge(BrowserActionsController* controller);
151 ~ToolbarActionsBarBridge() override;
154 // ToolbarActionsBarDelegate:
155 void AddViewForAction(ToolbarActionViewController* action,
156 size_t index) override;
157 void RemoveViewForAction(ToolbarActionViewController* action) override;
158 void RemoveAllViews() override;
159 void Redraw(bool order_changed) override;
160 void ResizeAndAnimate(gfx::Tween::Type tween_type,
162 bool suppress_chevron) override;
163 void SetChevronVisibility(bool chevron_visible) override;
164 int GetWidth() const override;
165 bool IsAnimating() const override;
166 void StopAnimating() override;
167 int GetChevronWidth() const override;
168 bool IsPopupRunning() const override;
170 // The owning BrowserActionsController; weak.
171 BrowserActionsController* controller_;
173 DISALLOW_COPY_AND_ASSIGN(ToolbarActionsBarBridge);
176 ToolbarActionsBarBridge::ToolbarActionsBarBridge(
177 BrowserActionsController* controller)
178 : controller_(controller) {
181 ToolbarActionsBarBridge::~ToolbarActionsBarBridge() {
184 void ToolbarActionsBarBridge::AddViewForAction(
185 ToolbarActionViewController* action,
187 [controller_ addViewForAction:action
191 void ToolbarActionsBarBridge::RemoveViewForAction(
192 ToolbarActionViewController* action) {
193 [controller_ removeViewForAction:action];
196 void ToolbarActionsBarBridge::RemoveAllViews() {
197 [controller_ removeAllViews];
200 void ToolbarActionsBarBridge::Redraw(bool order_changed) {
201 [controller_ redraw];
204 void ToolbarActionsBarBridge::ResizeAndAnimate(gfx::Tween::Type tween_type,
206 bool suppress_chevron) {
207 [controller_ resizeContainerToWidth:target_width
208 shouldAnimate:![controller_ toolbarActionsBar]->
209 suppress_animation()];
212 void ToolbarActionsBarBridge::SetChevronVisibility(bool chevron_visible) {
213 [controller_ setChevronHidden:!chevron_visible
214 inFrame:[[controller_ containerView] frame]
218 int ToolbarActionsBarBridge::GetWidth() const {
219 return NSWidth([[controller_ containerView] frame]);
222 bool ToolbarActionsBarBridge::IsAnimating() const {
226 void ToolbarActionsBarBridge::StopAnimating() {
229 int ToolbarActionsBarBridge::GetChevronWidth() const {
230 return kChevronWidth;
233 bool ToolbarActionsBarBridge::IsPopupRunning() const {
234 return [ExtensionPopupController popup] != nil;
239 @implementation BrowserActionsController
241 @synthesize containerView = containerView_;
244 #pragma mark Public Methods
246 - (id)initWithBrowser:(Browser*)browser
247 containerView:(BrowserActionsContainerView*)container {
248 DCHECK(browser && container);
250 if ((self = [super init])) {
252 profile_ = browser->profile();
254 toolbarActionsBarBridge_.reset(new ToolbarActionsBarBridge(self));
255 toolbarActionsBar_.reset(
256 new ToolbarActionsBar(toolbarActionsBarBridge_.get(), browser_, false));
258 containerView_ = container;
259 [containerView_ setPostsFrameChangedNotifications:YES];
260 [[NSNotificationCenter defaultCenter]
262 selector:@selector(containerFrameChanged:)
263 name:NSViewFrameDidChangeNotification
264 object:containerView_];
265 [[NSNotificationCenter defaultCenter]
267 selector:@selector(containerDragStart:)
268 name:kBrowserActionGrippyDragStartedNotification
269 object:containerView_];
270 [[NSNotificationCenter defaultCenter]
272 selector:@selector(containerDragging:)
273 name:kBrowserActionGrippyDraggingNotification
274 object:containerView_];
275 [[NSNotificationCenter defaultCenter]
277 selector:@selector(containerDragFinished:)
278 name:kBrowserActionGrippyDragFinishedNotification
279 object:containerView_];
280 [[NSNotificationCenter defaultCenter]
282 selector:@selector(containerWillTranslateOnX:)
283 name:kBrowserActionGrippyWillDragNotification
284 object:containerView_];
285 // Listen for a finished drag from any button to make sure each open window
287 [[NSNotificationCenter defaultCenter]
289 selector:@selector(actionButtonDragFinished:)
290 name:kBrowserActionButtonDragEndNotification
293 suppressChevron_ = NO;
294 chevronAnimation_.reset([[NSViewAnimation alloc] init]);
295 [chevronAnimation_ gtm_setDuration:kAnimationDuration
296 eventMask:NSLeftMouseUpMask];
297 [chevronAnimation_ setAnimationBlockingMode:NSAnimationNonblocking];
299 hiddenButtons_.reset([[NSMutableArray alloc] init]);
300 buttons_.reset([[NSMutableArray alloc] init]);
301 toolbarActionsBar_->CreateActions();
302 if ([buttons_ count] != 0)
303 [self resizeContainerAndAnimate:NO];
304 [self showChevronIfNecessaryInFrame:[containerView_ frame] animate:NO];
305 [self updateGrippyCursors];
306 [container setResizable:YES];
313 // Explicitly destroy the ToolbarActionsBar so all buttons get removed with a
314 // valid BrowserActionsController, and so we can verify state before
316 toolbarActionsBar_->DeleteActions();
317 toolbarActionsBar_.reset();
318 DCHECK_EQ(0u, [buttons_ count]);
319 [[NSNotificationCenter defaultCenter] removeObserver:self];
324 for (BrowserActionButton* button in buttons_.get())
325 [button updateState];
328 - (NSUInteger)buttonCount {
329 return [buttons_ count];
332 - (NSUInteger)visibleButtonCount {
333 return [self buttonCount] - [hiddenButtons_ count];
336 - (void)resizeContainerAndAnimate:(BOOL)animate {
337 [self resizeContainerToWidth:toolbarActionsBar_->GetPreferredSize().width()
338 shouldAnimate:animate];
341 - (CGFloat)savedWidth {
342 return toolbarActionsBar_->GetPreferredSize().width();
345 - (NSPoint)popupPointForId:(const std::string&)id {
346 NSButton* button = [self buttonForId:id];
350 if ([hiddenButtons_ containsObject:button])
351 button = chevronMenuButton_.get();
353 // Anchor point just above the center of the bottom.
354 const NSRect bounds = [button bounds];
355 DCHECK([button isFlipped]);
356 NSPoint anchor = NSMakePoint(NSMidX(bounds),
357 NSMaxY(bounds) - kBrowserActionBubbleYOffset);
358 return [button convertPoint:anchor toView:nil];
361 - (BOOL)chevronIsHidden {
362 if (!chevronMenuButton_.get())
365 if (![chevronAnimation_ isAnimating])
366 return [chevronMenuButton_ isHidden];
368 DCHECK([[chevronAnimation_ viewAnimations] count] > 0);
370 // The chevron is animating in or out. Determine which one and have the return
371 // value reflect where the animation is headed.
372 NSString* effect = [[[chevronAnimation_ viewAnimations] objectAtIndex:0]
373 valueForKey:NSViewAnimationEffectKey];
374 if (effect == NSViewAnimationFadeInEffect) {
376 } else if (effect == NSViewAnimationFadeOutEffect) {
384 - (content::WebContents*)currentWebContents {
385 return browser_->tab_strip_model()->GetActiveWebContents();
389 #pragma mark NSMenuDelegate
391 - (void)menuNeedsUpdate:(NSMenu*)menu {
392 [menu removeAllItems];
394 // See menu_button.h for documentation on why this is needed.
395 [menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
397 for (BrowserActionButton* button in hiddenButtons_.get()) {
399 base::SysUTF16ToNSString([button viewController]->GetActionName());
401 [menu addItemWithTitle:name
402 action:@selector(chevronItemSelected:)
404 [item setRepresentedObject:button];
405 [item setImage:[button compositedImage]];
406 [item setTarget:self];
407 [item setEnabled:[button isEnabled]];
412 #pragma mark Private Methods
414 - (void)addViewForAction:(ToolbarActionViewController*)action
415 withIndex:(NSUInteger)index {
416 NSRect buttonFrame = NSMakeRect(0.0,
417 kBrowserActionOriginYOffset,
418 ToolbarActionsBar::IconWidth(false),
419 ToolbarActionsBar::IconHeight());
420 BrowserActionButton* newButton =
421 [[[BrowserActionButton alloc]
422 initWithFrame:buttonFrame
423 viewController:action
424 controller:self] autorelease];
425 [newButton setTarget:self];
426 [newButton setAction:@selector(browserActionClicked:)];
427 [buttons_ insertObject:newButton atIndex:index];
430 [[NSNotificationCenter defaultCenter]
432 selector:@selector(actionButtonDragging:)
433 name:kBrowserActionButtonDraggingNotification
438 if (![self updateContainerVisibility])
439 return; // Container is hidden; no need to update.
441 std::vector<ToolbarActionViewController*> toolbar_actions =
442 toolbarActionsBar_->toolbar_actions();
443 for (NSUInteger i = 0; i < [buttons_ count]; ++i) {
444 if ([[buttons_ objectAtIndex:i] viewController] != toolbar_actions[i]) {
446 while (toolbar_actions[i] != [[buttons_ objectAtIndex:j] viewController])
448 [buttons_ exchangeObjectAtIndex:i withObjectAtIndex: j];
452 BOOL animate = !toolbarActionsBar_->suppress_animation();
453 [self showChevronIfNecessaryInFrame:[containerView_ frame] animate:animate];
454 for (NSUInteger i = 0; i < [buttons_ count]; ++i) {
455 if (![[buttons_ objectAtIndex:i] isBeingDragged])
456 [self moveButton:[buttons_ objectAtIndex:i]
462 - (void)removeViewForAction:(ToolbarActionViewController*)action {
463 BrowserActionButton* button = [self buttonForId:action->GetId()];
465 [button removeFromSuperview];
466 // It may or may not be hidden, but it won't matter to NSMutableArray either
468 [hiddenButtons_ removeObject:button];
472 [buttons_ removeObject:button];
475 - (void)removeAllViews {
476 for (BrowserActionButton* button in buttons_.get()) {
477 [button removeFromSuperview];
479 [hiddenButtons_ removeObject:button];
481 [buttons_ removeAllObjects];
484 - (void)resizeContainerToWidth:(CGFloat)width
485 shouldAnimate:(BOOL)animate {
486 [self updateContainerVisibility];
487 [containerView_ setMaxWidth:
488 toolbarActionsBar_->IconCountToWidth([self buttonCount])];
489 [containerView_ resizeToWidth:width
491 NSRect frame = animate ? [containerView_ animationEndFrame] :
492 [containerView_ frame];
494 [self showChevronIfNecessaryInFrame:frame animate:animate];
496 [containerView_ setNeedsDisplay:YES];
499 [[NSNotificationCenter defaultCenter]
500 postNotificationName:kBrowserActionVisibilityChangedNotification
506 - (BOOL)updateContainerVisibility {
507 BOOL hidden = [buttons_ count] == 0;
508 if ([containerView_ isHidden] != hidden)
509 [containerView_ setHidden:hidden];
513 - (void)updateButtonOpacity {
514 for (BrowserActionButton* button in buttons_.get()) {
515 NSRect buttonFrame = [button frame];
516 if (NSContainsRect([containerView_ bounds], buttonFrame)) {
517 if ([button alphaValue] != 1.0)
518 [button setAlphaValue:1.0];
522 CGFloat intersectionWidth =
523 NSWidth(NSIntersectionRect([containerView_ bounds], buttonFrame));
524 CGFloat alpha = std::max(static_cast<CGFloat>(0.0),
525 intersectionWidth / NSWidth(buttonFrame));
526 [button setAlphaValue:alpha];
527 [button setNeedsDisplay:YES];
531 - (BrowserActionButton*)buttonForId:(const std::string&)id {
532 for (BrowserActionButton* button in buttons_.get()) {
533 if ([button viewController]->GetId() == id)
539 - (void)containerFrameChanged:(NSNotification*)notification {
540 [self updateButtonOpacity];
541 [[containerView_ window] invalidateCursorRectsForView:containerView_];
542 [self updateChevronPositionInFrame:[containerView_ frame]];
545 - (void)containerDragStart:(NSNotification*)notification {
546 [self setChevronHidden:YES inFrame:[containerView_ frame] animate:YES];
547 while([hiddenButtons_ count] > 0) {
548 BrowserActionButton* button = [hiddenButtons_ objectAtIndex:0];
549 [button setAlphaValue:1.0];
550 [containerView_ addSubview:button];
551 [hiddenButtons_ removeObjectAtIndex:0];
555 - (void)containerDragging:(NSNotification*)notification {
556 [[NSNotificationCenter defaultCenter]
557 postNotificationName:kBrowserActionGrippyDraggingNotification
561 - (void)containerDragFinished:(NSNotification*)notification {
562 for (BrowserActionButton* button in buttons_.get()) {
563 NSRect buttonFrame = [button frame];
564 if (NSContainsRect([containerView_ bounds], buttonFrame))
567 CGFloat intersectionWidth =
568 NSWidth(NSIntersectionRect([containerView_ bounds], buttonFrame));
569 // Pad the threshold by 5 pixels in order to have the buttons hide more
571 if (([containerView_ grippyPinned] && intersectionWidth > 0) ||
572 (intersectionWidth <= (NSWidth(buttonFrame) / 2) + 5.0)) {
573 [button setAlphaValue:0.0];
574 [button removeFromSuperview];
575 [hiddenButtons_ addObject:button];
578 [self updateGrippyCursors];
580 toolbarActionsBar_->OnResizeComplete(
581 toolbarActionsBar_->IconCountToWidth([self visibleButtonCount]));
583 [[NSNotificationCenter defaultCenter]
584 postNotificationName:kBrowserActionGrippyDragFinishedNotification
588 - (void)containerWillTranslateOnX:(NSNotification*)notification {
589 [[NSNotificationCenter defaultCenter]
590 postNotificationName:kBrowserActionGrippyWillDragNotification
592 userInfo:notification.userInfo];
595 - (void)actionButtonDragging:(NSNotification*)notification {
596 suppressChevron_ = YES;
597 if (![self chevronIsHidden])
598 [self setChevronHidden:YES inFrame:[containerView_ frame] animate:YES];
600 // Determine what index the dragged button should lie in, alter the model and
601 // reposition the buttons.
602 CGFloat dragThreshold = ToolbarActionsBar::IconWidth(false) / 2;
603 BrowserActionButton* draggedButton = [notification object];
604 NSRect draggedButtonFrame = [draggedButton frame];
606 NSUInteger index = 0;
607 std::vector<ToolbarActionViewController*> toolbar_actions =
608 toolbarActionsBar_->toolbar_actions();
609 for (ToolbarActionViewController* action : toolbar_actions) {
610 BrowserActionButton* button = [self buttonForId:(action->GetId())];
611 CGFloat intersectionWidth =
612 NSWidth(NSIntersectionRect(draggedButtonFrame, [button frame]));
614 if (intersectionWidth > dragThreshold && button != draggedButton &&
615 ![button isAnimating] && index < [self visibleButtonCount]) {
616 toolbarActionsBar_->OnDragDrop(
617 [buttons_ indexOfObject:draggedButton],
619 ToolbarActionsBar::DRAG_TO_SAME);
626 - (void)actionButtonDragFinished:(NSNotification*)notification {
627 suppressChevron_ = NO;
631 - (void)moveButton:(BrowserActionButton*)button
632 toIndex:(NSUInteger)index
633 animate:(BOOL)animate {
634 const ToolbarActionsBar::PlatformSettings& platformSettings =
635 toolbarActionsBar_->platform_settings();
636 CGFloat xOffset = platformSettings.left_padding +
637 (index * ToolbarActionsBar::IconWidth(true));
638 NSRect buttonFrame = [button frame];
639 buttonFrame.origin.x = xOffset;
640 [button setFrame:buttonFrame animate:animate];
642 if (index < toolbarActionsBar_->GetIconCount()) {
643 // Make sure the button is within the visible container.
644 if ([button superview] != containerView_) {
645 [containerView_ addSubview:button];
646 [button setAlphaValue:1.0];
647 [hiddenButtons_ removeObjectIdenticalTo:button];
649 } else if (![hiddenButtons_ containsObject:button]) {
650 [hiddenButtons_ addObject:button];
651 [button removeFromSuperview];
652 [button setAlphaValue:0.0];
656 - (BOOL)browserActionClicked:(BrowserActionButton*)button
657 shouldGrant:(BOOL)shouldGrant {
658 return [button viewController]->ExecuteAction(shouldGrant);
661 - (BOOL)browserActionClicked:(BrowserActionButton*)button {
662 return [self browserActionClicked:button
666 - (void)showChevronIfNecessaryInFrame:(NSRect)frame animate:(BOOL)animate {
667 bool hidden = suppressChevron_ ||
668 toolbarActionsBar_->GetIconCount() == [self buttonCount];
669 [self setChevronHidden:hidden
674 - (void)updateChevronPositionInFrame:(NSRect)frame {
675 CGFloat xPos = NSWidth(frame) - kChevronWidth;
676 NSRect buttonFrame = NSMakeRect(xPos,
677 kBrowserActionOriginYOffset,
679 ToolbarActionsBar::IconHeight());
680 [chevronMenuButton_ setFrame:buttonFrame];
683 - (void)setChevronHidden:(BOOL)hidden
684 inFrame:(NSRect)frame
685 animate:(BOOL)animate {
686 if (hidden == [self chevronIsHidden])
689 if (!chevronMenuButton_.get()) {
690 chevronMenuButton_.reset([[MenuButton alloc] init]);
691 [chevronMenuButton_ setOpenMenuOnClick:YES];
692 [chevronMenuButton_ setBordered:NO];
693 [chevronMenuButton_ setShowsBorderOnlyWhileMouseInside:YES];
695 [[chevronMenuButton_ cell] setImageID:IDR_BROWSER_ACTIONS_OVERFLOW
696 forButtonState:image_button_cell::kDefaultState];
697 [[chevronMenuButton_ cell] setImageID:IDR_BROWSER_ACTIONS_OVERFLOW_H
698 forButtonState:image_button_cell::kHoverState];
699 [[chevronMenuButton_ cell] setImageID:IDR_BROWSER_ACTIONS_OVERFLOW_P
700 forButtonState:image_button_cell::kPressedState];
702 overflowMenu_.reset([[NSMenu alloc] initWithTitle:@""]);
703 [overflowMenu_ setAutoenablesItems:NO];
704 [overflowMenu_ setDelegate:self];
705 [chevronMenuButton_ setAttachedMenu:overflowMenu_];
707 [containerView_ addSubview:chevronMenuButton_];
710 [self updateChevronPositionInFrame:frame];
712 // Stop any running animation.
713 [chevronAnimation_ stopAnimation];
716 [chevronMenuButton_ setHidden:hidden];
720 NSDictionary* animationDictionary;
722 animationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
723 chevronMenuButton_.get(), NSViewAnimationTargetKey,
724 NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey,
727 [chevronMenuButton_ setHidden:NO];
728 animationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
729 chevronMenuButton_.get(), NSViewAnimationTargetKey,
730 NSViewAnimationFadeInEffect, NSViewAnimationEffectKey,
733 [chevronAnimation_ setViewAnimations:
734 [NSArray arrayWithObject:animationDictionary]];
735 [chevronAnimation_ startAnimation];
738 - (void)chevronItemSelected:(id)menuItem {
739 [self browserActionClicked:[menuItem representedObject]];
742 - (void)updateGrippyCursors {
743 [containerView_ setCanDragLeft:[hiddenButtons_ count] > 0];
744 [containerView_ setCanDragRight:[self visibleButtonCount] > 0];
745 [[containerView_ window] invalidateCursorRectsForView:containerView_];
748 - (ToolbarActionsBar*)toolbarActionsBar {
749 return toolbarActionsBar_.get();
753 #pragma mark Testing Methods
755 - (BrowserActionButton*)buttonWithIndex:(NSUInteger)index {
756 const std::vector<ToolbarActionViewController*>& toolbar_actions =
757 toolbarActionsBar_->toolbar_actions();
758 if (index < toolbar_actions.size())
759 return [self buttonForId:toolbar_actions[index]->GetId()];