Chromium Code Reviews| Index: chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm |
| diff --git a/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm b/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm |
| index f68a15e51410d9eab97fb3c0cf067861a4d5c7f3..1e4368f070959de3c2b73b985b24ca1f717623af 100644 |
| --- a/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm |
| +++ b/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm |
| @@ -3,81 +3,25 @@ |
| // found in the LICENSE file. |
| #include "base/logging.h" |
| -#include "base/mac/bundle_locations.h" |
| #include "base/mac/mac_util.h" |
| -#include "chrome/browser/chrome_notification_types.h" |
| +#include "base/message_loop/message_loop.h" |
| #include "chrome/browser/infobars/confirm_infobar_delegate.h" |
| #include "chrome/browser/infobars/infobar.h" |
| +#include "chrome/browser/infobars/infobar_container.h" |
| #include "chrome/browser/infobars/infobar_service.h" |
| -#import "chrome/browser/ui/cocoa/animatable_view.h" |
| #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
| +#import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h" |
| +#import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h" |
| #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h" |
| #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h" |
| +#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" |
| #import "chrome/browser/ui/cocoa/view_id_util.h" |
| -#include "content/public/browser/notification_details.h" |
| -#include "content/public/browser/notification_source.h" |
| -#include "skia/ext/skia_utils_mac.h" |
| - |
| -// C++ class that receives INFOBAR_ADDED and INFOBAR_REMOVED |
| -// notifications and proxies them back to |controller|. |
| -class InfoBarNotificationObserver : public content::NotificationObserver { |
| - public: |
| - InfoBarNotificationObserver(InfoBarContainerController* controller) |
| - : controller_(controller) { |
| - } |
| - |
| - private: |
| - // NotificationObserver implementation |
| - virtual void Observe( |
| - int type, |
| - const content::NotificationSource& source, |
| - const content::NotificationDetails& details) OVERRIDE { |
| - InfoBarService* infobar_service = |
| - content::Source<InfoBarService>(source).ptr(); |
| - switch (type) { |
| - case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED: |
| - [controller_ addInfoBar:content::Details<InfoBarAddedDetails>(details)-> |
| - CreateInfoBar(infobar_service) |
| - animate:YES]; |
| - break; |
| - |
| - case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED: { |
| - InfoBarRemovedDetails* removed_details = |
| - content::Details<InfoBarRemovedDetails>(details).ptr(); |
| - [controller_ |
| - closeInfoBarsForDelegate:removed_details->first |
| - animate:(removed_details->second ? YES : NO)]; |
| - break; |
| - } |
| - |
| - case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED: { |
| - InfoBarReplacedDetails* replaced_details = |
| - content::Details<InfoBarReplacedDetails>(details).ptr(); |
| - [controller_ closeInfoBarsForDelegate:replaced_details->first |
| - animate:NO]; |
| - [controller_ addInfoBar:replaced_details->second-> |
| - CreateInfoBar(infobar_service) |
| - animate:NO]; |
| - break; |
| - } |
| - |
| - default: |
| - NOTREACHED(); // we don't ask for anything else! |
| - break; |
| - } |
| - |
| - [controller_ positionInfoBarsAndRedraw]; |
| - } |
| - |
| - InfoBarContainerController* controller_; // weak, owns us. |
| -}; |
| - |
| - |
| -@interface InfoBarContainerController (PrivateMethods) |
| -// Returns the desired height of the container view, computed by |
| -// adding together the heights of all its subviews. |
| -- (CGFloat)desiredHeight; |
| +@interface InfoBarContainerController() |
|
Robert Sesek
2013/08/26 14:06:56
nit: space before (
sail
2013/08/26 18:32:23
Done.
|
| +// Removes |controller| from the list of controllers in this container and |
| +// removes its view from the view hierarchy. This method is safe to call while |
| +// |controller| is still on the call stack. |
| +- (void)removeController:(InfoBarController*)controller; |
| @end |
| @@ -87,50 +31,27 @@ class InfoBarNotificationObserver : public content::NotificationObserver { |
| - (id)initWithResizeDelegate:(id<ViewResizer>)resizeDelegate { |
| DCHECK(resizeDelegate); |
| - if ((self = [super initWithNibName:@"InfoBarContainer" |
| - bundle:base::mac::FrameworkBundle()])) { |
| - resizeDelegate_ = resizeDelegate; |
| - infoBarObserver_.reset(new InfoBarNotificationObserver(self)); |
| + if ((self = [super init])) { |
|
Robert Sesek
2013/08/26 14:06:56
Don't you need to call through -initWithNibName:bu
sail
2013/08/26 18:32:23
Done.
|
| + base::scoped_nsobject<NSView> view( |
| + [[NSView alloc] initWithFrame:NSZeroRect]); |
| + [view setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin]; |
| + view_id_util::SetID(view, VIEW_ID_INFO_BAR_CONTAINER); |
| + [self setView:view]; |
| - // NSMutableArray needs an initial capacity, and we rarely ever see |
| - // more than two infobars at a time, so that seems like a good choice. |
| - infobarControllers_.reset([[NSMutableArray alloc] initWithCapacity:2]); |
| - closingInfoBars_.reset([[NSMutableSet alloc] initWithCapacity:2]); |
| + resizeDelegate_ = resizeDelegate; |
| + containerCocoa_.reset(new InfoBarContainerCocoa(self)); |
| + infobarControllers_.reset([[NSMutableArray alloc] init]); |
| } |
| return self; |
| } |
| - (void)dealloc { |
| + containerCocoa_.reset(); |
|
Robert Sesek
2013/08/26 14:06:56
Why does this need to be done explicitly?
sail
2013/08/26 18:32:23
Done.
Documented.
|
| DCHECK_EQ([infobarControllers_ count], 0U); |
| - DCHECK_EQ([closingInfoBars_ count], 0U); |
| view_id_util::UnsetID([self view]); |
| [super dealloc]; |
| } |
| -- (void)awakeFromNib { |
| - // The info bar container view is an ordinary NSView object, so we set its |
| - // ViewID here. |
| - view_id_util::SetID([self view], VIEW_ID_INFO_BAR_CONTAINER); |
| -} |
| - |
| -- (void)willRemoveController:(InfoBarController*)controller { |
| - [closingInfoBars_ addObject:controller]; |
| -} |
| - |
| -- (void)removeController:(InfoBarController*)controller { |
| - if (![infobarControllers_ containsObject:controller]) |
| - return; |
| - |
| - // This code can be executed while InfoBarController is still on the stack, so |
| - // we retain and autorelease the controller to prevent it from being |
| - // dealloc'ed too early. |
| - [[controller retain] autorelease]; |
| - [[controller view] removeFromSuperview]; |
| - [infobarControllers_ removeObject:controller]; |
| - [closingInfoBars_ removeObject:controller]; |
| - [self positionInfoBarsAndRedraw]; |
| -} |
| - |
| - (BrowserWindowController*)browserWindowController { |
| id controller = [[[self view] window] windowController]; |
| if (![controller isKindOfClass:[BrowserWindowController class]]) |
| @@ -138,30 +59,19 @@ class InfoBarNotificationObserver : public content::NotificationObserver { |
| return controller; |
| } |
| -- (void)changeWebContents:(content::WebContents*)contents { |
| - registrar_.RemoveAll(); |
| - [self removeAllInfoBars]; |
| - |
| - currentWebContents_ = contents; |
| - if (currentWebContents_) { |
| - InfoBarService* infobarService = |
| - InfoBarService::FromWebContents(currentWebContents_); |
| - for (size_t i = 0; i < infobarService->infobar_count(); ++i) { |
| - InfoBar* infobar = |
| - infobarService->infobar_at(i)->CreateInfoBar(infobarService); |
| - [self addInfoBar:infobar animate:NO]; |
| - } |
| +- (CGFloat)infobarArrowX { |
| + LocationBarViewMac* locationBar = |
| + [[self browserWindowController] locationBarBridge]; |
| + return locationBar->GetPageInfoBubblePoint().x; |
| +} |
| - content::Source<InfoBarService> source(infobarService); |
| - registrar_.Add(infoBarObserver_.get(), |
| - chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, source); |
| - registrar_.Add(infoBarObserver_.get(), |
| - chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, source); |
| - registrar_.Add(infoBarObserver_.get(), |
| - chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED, source); |
| +- (void)changeWebContents:(content::WebContents*)contents { |
| + if (contents) { |
| + containerCocoa_->ChangeInfoBarService( |
| + InfoBarService::FromWebContents(contents)); |
| + } else { |
| + containerCocoa_->ChangeInfoBarService(NULL); |
| } |
| - |
| - [self positionInfoBarsAndRedraw]; |
| } |
| - (void)tabDetachedWithContents:(content::WebContents*)contents { |
| @@ -169,99 +79,37 @@ class InfoBarNotificationObserver : public content::NotificationObserver { |
| [self changeWebContents:NULL]; |
| } |
| -- (NSUInteger)infobarCount { |
| - return [infobarControllers_ count] - [closingInfoBars_ count]; |
| -} |
| - |
| - (CGFloat)overlappingTipHeight { |
| - return [self infobarCount] ? infobars::kTipHeight : 0; |
| -} |
| - |
| -- (void)resizeView:(NSView*)view newHeight:(CGFloat)height { |
| - NSRect frame = [view frame]; |
| - frame.size.height = height; |
| - [view setFrame:frame]; |
| - [self positionInfoBarsAndRedraw]; |
| -} |
| - |
| -- (void)setAnimationInProgress:(BOOL)inProgress { |
| - if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)]) |
| - [resizeDelegate_ setAnimationInProgress:inProgress]; |
| -} |
| - |
| -- (void)setShouldSuppressTopInfoBarTip:(BOOL)flag { |
| - if (shouldSuppressTopInfoBarTip_ == flag) |
| - return; |
| - shouldSuppressTopInfoBarTip_ = flag; |
| - [self positionInfoBarsAndRedraw]; |
| -} |
| - |
| -@end |
| - |
| -@implementation InfoBarContainerController (PrivateMethods) |
| - |
| -- (CGFloat)desiredHeight { |
| - CGFloat height = 0; |
| - |
| - // Take out the height of the tip from the total size of the infobar so that |
| - // the tip overlaps the preceding infobar when there is more than one infobar. |
| - for (InfoBarController* controller in infobarControllers_.get()) |
| - height += NSHeight([[controller view] frame]) - infobars::kTipHeight; |
| - |
| - // If there are any infobars, add a little extra room for the tip of the first |
| - // infobar. |
| - if (height) |
| - height += infobars::kTipHeight; |
| - |
| - return height; |
| + return containerCocoa_->GetVerticalOverlap(NULL); |
| } |
| -- (void)addInfoBar:(InfoBar*)infobar animate:(BOOL)animate { |
| +- (void)addInfoBar:(InfoBarCocoa*)infobar |
| + position:(NSUInteger)position { |
| InfoBarController* controller = infobar->controller(); |
| [controller setContainerController:self]; |
| - [[controller animatableView] setResizeDelegate:self]; |
| - [[self view] addSubview:[controller view]]; |
| - [infobarControllers_ addObject:[controller autorelease]]; |
| + [infobarControllers_ insertObject:controller atIndex:position]; |
| - if (animate) |
| - [controller animateOpen]; |
| - else |
| - [controller open]; |
| - |
| - delete infobar; |
| + NSView* relativeView = nil; |
| + if (position > 0) |
| + relativeView = [[infobarControllers_ objectAtIndex:position - 1] view]; |
| + [[self view] addSubview:[controller view] |
| + positioned:NSWindowAbove |
| + relativeTo:relativeView]; |
| } |
| -- (void)closeInfoBarsForDelegate:(InfoBarDelegate*)delegate |
| - animate:(BOOL)animate { |
| - for (InfoBarController* controller in |
| - [NSArray arrayWithArray:infobarControllers_.get()]) { |
| - if ([controller delegate] == delegate) { |
| - [controller infobarWillClose]; |
| - if (animate) |
| - [controller animateClosed]; |
| - else |
| - [controller close]; |
| - } |
| - } |
| +- (void)removeInfoBar:(InfoBarCocoa*)infobar { |
| + [infobar->controller() infobarWillClose]; |
| + [self removeController:infobar->controller()]; |
| + base::MessageLoop::current()->DeleteSoon(FROM_HERE, infobar); |
| } |
| -- (void)removeAllInfoBars { |
| - // stopAnimation can remove the infobar from infobarControllers_ if it was in |
| - // the midst of closing, so copy the array so mutations won't cause problems. |
| - for (InfoBarController* controller in |
| - [NSArray arrayWithArray:infobarControllers_.get()]) { |
| - [[controller animatableView] stopAnimation]; |
| - // This code can be executed while InfoBarController is still on the stack, |
| - // so we retain and autorelease the controller to prevent it from being |
| - // dealloc'ed too early. |
| - [[controller retain] autorelease]; |
| - [[controller view] removeFromSuperview]; |
| +- (void)positionInfoBarsAndRedraw:(BOOL)isAnimating { |
| + if (isAnimating_ != isAnimating) { |
| + isAnimating_ = isAnimating; |
| + if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)]) |
| + [resizeDelegate_ setAnimationInProgress:isAnimating_]; |
| } |
| - [infobarControllers_ removeAllObjects]; |
| - [closingInfoBars_ removeAllObjects]; |
| -} |
| -- (void)positionInfoBarsAndRedraw { |
| NSRect containerBounds = [[self view] bounds]; |
| int minY = 0; |
| @@ -271,20 +119,47 @@ class InfoBarNotificationObserver : public content::NotificationObserver { |
| // the others below. |
| for (InfoBarController* controller in |
| [infobarControllers_ reverseObjectEnumerator]) { |
| - NSView* view = [controller view]; |
| - NSRect frame = [view frame]; |
| + NSRect frame; |
| frame.origin.x = NSMinX(containerBounds); |
| frame.origin.y = minY; |
| frame.size.width = NSWidth(containerBounds); |
| - [view setFrame:frame]; |
| + frame.size.height = [controller infobar]->total_height(); |
| + [[controller view] setFrame:frame]; |
| - minY += NSHeight(frame) - infobars::kTipHeight; |
| + minY += NSHeight(frame) - [controller infobar]->arrow_height(); |
| + [controller layoutArrow]; |
| + } |
| + |
| + int totalHeight = 0; |
| + int overlap = containerCocoa_->GetVerticalOverlap(&totalHeight); |
| - BOOL isTop = [controller isEqual:[infobarControllers_ objectAtIndex:0]]; |
| - [controller setHasTip:!shouldSuppressTopInfoBarTip_ || !isTop]; |
| + if (NSHeight([[self view] frame]) != totalHeight) { |
| + [resizeDelegate_ resizeView:[self view] newHeight:totalHeight]; |
| + } else if (oldOverlappingTipHeight_ != overlap) { |
| + // If the infobar overlap changed but the height didn't change then |
| + // explicitly ask for a layout. |
| + oldOverlappingTipHeight_ = overlap; |
| + [[self browserWindowController] layoutInfoBar]; |
| } |
| +} |
| + |
| +- (void)setShouldSuppressTopInfoBarTip:(BOOL)flag { |
| + if (shouldSuppressTopInfoBarTip_ == flag) |
| + return; |
| + shouldSuppressTopInfoBarTip_ = flag; |
| + [self positionInfoBarsAndRedraw:isAnimating_]; |
| +} |
| - [resizeDelegate_ resizeView:[self view] newHeight:[self desiredHeight]]; |
| +- (void)removeController:(InfoBarController*)controller { |
| + if (![infobarControllers_ containsObject:controller]) |
| + return; |
| + |
| + // This code can be executed while InfoBarController is still on the stack, so |
| + // we retain and autorelease the controller to prevent it from being |
| + // dealloc'ed too early. |
| + [[controller retain] autorelease]; |
| + [[controller view] removeFromSuperview]; |
| + [infobarControllers_ removeObject:controller]; |
| } |
| @end |