Index: chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm |
diff --git a/chrome/browser/ui/cocoa/extensions/extension_toolbar_icon_surfacing_bubble_mac.mm b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm |
similarity index 48% |
rename from chrome/browser/ui/cocoa/extensions/extension_toolbar_icon_surfacing_bubble_mac.mm |
rename to chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm |
index 0151fe8e24131d162c641a4f06d14f30eed1df97..bbbc29eee2b7b1654ed3997fb5fda5968b0e4425 100644 |
--- a/chrome/browser/ui/cocoa/extensions/extension_toolbar_icon_surfacing_bubble_mac.mm |
+++ b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm |
@@ -2,54 +2,75 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#import "chrome/browser/ui/cocoa/extensions/extension_toolbar_icon_surfacing_bubble_mac.h" |
+#import "chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.h" |
#include "base/mac/foundation_util.h" |
+#include "base/strings/sys_string_conversions.h" |
#import "chrome/browser/ui/cocoa/info_bubble_view.h" |
#import "chrome/browser/ui/cocoa/info_bubble_window.h" |
#include "chrome/browser/ui/toolbar/toolbar_actions_bar_bubble_delegate.h" |
-#include "grit/chromium_strings.h" |
-#include "grit/generated_resources.h" |
#include "skia/ext/skia_utils_mac.h" |
#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
#include "third_party/skia/include/core/SkColor.h" |
+#import "ui/base/cocoa/controls/hyperlink_button_cell.h" |
#import "ui/base/cocoa/hover_button.h" |
#import "ui/base/cocoa/window_size_constants.h" |
-#include "ui/base/l10n/l10n_util.h" |
#include "ui/native_theme/native_theme.h" |
-@interface ExtensionToolbarIconSurfacingBubbleMac () |
+@class ExtensionMessageBubbleButton; |
+ |
+@interface ToolbarActionsBarBubbleMac () |
// Handles the notification that the window will close. |
- (void)windowWillClose:(NSNotification*)notification; |
// Creates and returns an NSAttributed string with the specified size and |
// alignment. |
-- (NSAttributedString*)attributedStringWithString:(int)stringId |
+- (NSAttributedString*)attributedStringWithString:(const base::string16&)string |
fontSize:(CGFloat)fontSize |
alignment:(NSTextAlignment)alignment; |
-// Creates an NSTextField with the given string, and adds it to the window. |
-- (NSTextField*)addTextFieldWithString:(NSAttributedString*)attributedString; |
+// Creates an NSTextField with the given string, size, and alignment, and adds |
+// it to the window. |
+- (NSTextField*)addTextFieldWithString:(const base::string16&)string |
+ fontSize:(CGFloat)fontSize |
+ alignment:(NSTextAlignment)alignment; |
+ |
+// Creates an ExtensionMessagebubbleButton the given string id, and adds it to |
+// the window. |
+- (ExtensionMessageBubbleButton*) addButtonWithString: |
+ (const base::string16&)string |
+ isPrimary:(BOOL)isPrimary; |
// Initializes the bubble's content. |
- (void)layout; |
-// Handles the "ok" button being clicked. |
+// Handles a button being clicked. |
- (void)onButtonClicked:(id)sender; |
@end |
-@interface ExtensionToolbarIconSurfacingBubbleButton : HoverButton |
+@interface ExtensionMessageBubbleButton : HoverButton { |
+ BOOL isPrimary_; |
+} |
+ |
// Draws with a blue background and white text. |
- (void)drawRect:(NSRect)rect; |
+ |
+@property(nonatomic) BOOL isPrimary; |
+ |
@end |
-@implementation ExtensionToolbarIconSurfacingBubbleMac |
+@implementation ToolbarActionsBarBubbleMac |
+ |
+@synthesize actionButton = actionButton_; |
+@synthesize dismissButton = dismissButton_; |
+@synthesize learnMoreButton = learnMoreButton_; |
- (id)initWithParentWindow:(NSWindow*)parentWindow |
anchorPoint:(NSPoint)anchorPoint |
- delegate:(ToolbarActionsBarBubbleDelegate*)delegate { |
+ delegate:(scoped_ptr<ToolbarActionsBarBubbleDelegate>) |
+ delegate { |
base::scoped_nsobject<InfoBubbleWindow> window( |
[[InfoBubbleWindow alloc] |
initWithContentRect:ui::kWindowSizeDeterminedLater |
@@ -59,9 +80,9 @@ |
if ((self = [super initWithWindow:window |
parentWindow:parentWindow |
anchoredAt:anchorPoint])) { |
- delegate_ = delegate; |
acknowledged_ = NO; |
[window setCanBecomeKeyWindow:NO]; |
+ delegate_ = delegate.Pass(); |
ui::NativeTheme* nativeTheme = ui::NativeTheme::instance(); |
[[self bubble] setAlignment:info_bubble::kAlignRightEdgeToAnchorEdge]; |
@@ -71,27 +92,30 @@ |
ui::NativeTheme::kColorId_DialogBackground))]; |
[self layout]; |
- |
- delegate_->OnToolbarActionsBarBubbleShown(); |
} |
return self; |
} |
+- (IBAction)showWindow:(id)sender { |
+ delegate_->OnBubbleShown(); |
+ [super showWindow:sender]; |
+} |
+ |
// Private ///////////////////////////////////////////////////////////////////// |
- (void)windowWillClose:(NSNotification*)notification { |
if (!acknowledged_) { |
- delegate_->OnToolbarActionsBarBubbleClosed( |
- ToolbarActionsBarBubbleDelegate::DISMISSED); |
+ delegate_->OnBubbleClosed( |
+ ToolbarActionsBarBubbleDelegate::CLOSE_DISMISS); |
acknowledged_ = YES; |
} |
[super windowWillClose:notification]; |
} |
-- (NSAttributedString*)attributedStringWithString:(int)stringId |
+- (NSAttributedString*)attributedStringWithString:(const base::string16&)string |
fontSize:(CGFloat)fontSize |
alignment:(NSTextAlignment)alignment { |
- NSString* string = l10n_util::GetNSString(stringId); |
+ NSString* cocoaString = base::SysUTF16ToNSString(string); |
base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( |
[[NSMutableParagraphStyle alloc] init]); |
[paragraphStyle setAlignment:alignment]; |
@@ -101,11 +125,18 @@ |
[NSColor colorWithCalibratedWhite:0.2 alpha:1.0], |
NSParagraphStyleAttributeName : paragraphStyle.get() |
}; |
- return [[[NSAttributedString alloc] initWithString:string |
+ return [[[NSAttributedString alloc] initWithString:cocoaString |
attributes:attributes] autorelease]; |
} |
-- (NSTextField*)addTextFieldWithString:(NSAttributedString*)attributedString { |
+- (NSTextField*)addTextFieldWithString:(const base::string16&)string |
+ fontSize:(CGFloat)fontSize |
+ alignment:(NSTextAlignment)alignment { |
+ NSAttributedString* attributedString = |
+ [self attributedStringWithString:string |
+ fontSize:fontSize |
+ alignment:alignment]; |
+ |
base::scoped_nsobject<NSTextField> textField( |
[[NSTextField alloc] initWithFrame:NSZeroRect]); |
[textField setEditable:NO]; |
@@ -113,62 +144,117 @@ |
[textField setDrawsBackground:NO]; |
[textField setAttributedStringValue:attributedString]; |
[[[self window] contentView] addSubview:textField]; |
+ [textField sizeToFit]; |
return textField.autorelease(); |
} |
+- (ExtensionMessageBubbleButton*)addButtonWithString: |
+ (const base::string16&)string |
+ isPrimary:(BOOL)isPrimary { |
+ ExtensionMessageBubbleButton* button = |
+ [[ExtensionMessageBubbleButton alloc] initWithFrame:NSZeroRect]; |
+ NSAttributedString* buttonString = |
+ [self attributedStringWithString:string |
+ fontSize:13.0 |
+ alignment:NSCenterTextAlignment]; |
+ [button setAttributedTitle:buttonString]; |
+ [button setIsPrimary:isPrimary]; |
+ [[button cell] setBordered:NO]; |
+ [button setTarget:self]; |
+ [button setAction:@selector(onButtonClicked:)]; |
+ [[[self window] contentView] addSubview:button]; |
+ [button sizeToFit]; |
+ return button; |
+} |
+ |
- (void)layout { |
// We first construct the different pieces of the bubble (the heading, the |
// content, and the button), and size them appropriately. |
- NSAttributedString* headingString = |
- [self attributedStringWithString:IDS_EXTENSION_TOOLBAR_BUBBLE_HEADING |
- fontSize:13.0 |
- alignment:NSLeftTextAlignment]; |
- NSTextField* heading = [self addTextFieldWithString:headingString]; |
- [heading sizeToFit]; |
+ NSTextField* heading = |
+ [self addTextFieldWithString:delegate_->GetHeadingText() |
+ fontSize:13.0 |
+ alignment:NSLeftTextAlignment]; |
NSSize headingSize = [heading frame].size; |
- NSAttributedString* contentString = |
- [self attributedStringWithString:IDS_EXTENSION_TOOLBAR_BUBBLE_CONTENT |
- fontSize:12.0 |
- alignment:NSLeftTextAlignment]; |
- NSTextField* content = [self addTextFieldWithString:contentString]; |
- [content setFrame:NSMakeRect(0, 0, headingSize.width, 0)]; |
+ NSTextField* content = |
+ [self addTextFieldWithString:delegate_->GetBodyText() |
+ fontSize:12.0 |
+ alignment:NSLeftTextAlignment]; |
+ CGFloat newWidth = headingSize.width + 50; |
+ [content setFrame:NSMakeRect(0, 0, newWidth, 0)]; |
// The content should have the same (max) width as the heading, which means |
// the text will most likely wrap. |
- NSSize contentSize = NSMakeSize(headingSize.width, |
+ NSSize contentSize = NSMakeSize(newWidth, |
[GTMUILocalizerAndLayoutTweaker |
sizeToFitFixedWidthTextField:content]); |
- NSButton* button = [[ExtensionToolbarIconSurfacingBubbleButton alloc] |
- initWithFrame:NSZeroRect]; |
- NSAttributedString* buttonString = |
- [self attributedStringWithString:IDS_EXTENSION_TOOLBAR_BUBBLE_OK |
- fontSize:13.0 |
- alignment:NSCenterTextAlignment]; |
- [button setAttributedTitle:buttonString]; |
- [[button cell] setBordered:NO]; |
- [button setTarget:self]; |
- [button setAction:@selector(onButtonClicked:)]; |
- [[[self window] contentView] addSubview:button]; |
- [button sizeToFit]; |
- // The button's size will only account for the text by default, so pad it a |
+ base::string16 learnMore = delegate_->GetLearnMoreButtonText(); |
+ NSSize learnMoreSize = NSZeroSize; |
+ if (!learnMore.empty()) { // The "learn more" link is optional. |
+ NSAttributedString* learnMoreString = |
+ [self attributedStringWithString:learnMore |
+ fontSize:13.0 |
+ alignment:NSLeftTextAlignment]; |
+ learnMoreButton_ = |
+ [[HyperlinkButtonCell buttonWithString:learnMoreString.string] retain]; |
+ [learnMoreButton_ setTarget:self]; |
+ [learnMoreButton_ setAction:@selector(onButtonClicked:)]; |
+ [[[self window] contentView] addSubview:learnMoreButton_]; |
+ [learnMoreButton_ sizeToFit]; |
+ learnMoreSize = NSMakeSize(NSWidth([learnMoreButton_ frame]), |
+ NSHeight([learnMoreButton_ frame])); |
+ } |
+ |
+ // The buttons' sizes will only account for the text by default, so pad them a |
// bit to make it look good. |
- NSSize buttonSize = NSMakeSize(NSWidth([button frame]) + 40.0, |
- NSHeight([button frame]) + 20.0); |
+ const CGFloat kButtonHorizontalPadding = 40.0; |
+ const CGFloat kButtonVerticalPadding = 20.0; |
+ |
+ base::string16 cancelStr = delegate_->GetDismissButtonText(); |
+ NSSize dismissButtonSize = NSZeroSize; |
+ if (!cancelStr.empty()) { // A cancel/dismiss button is optional. |
+ dismissButton_ = [self addButtonWithString:cancelStr |
+ isPrimary:NO]; |
+ dismissButtonSize = |
+ NSMakeSize(NSWidth([dismissButton_ frame]) + kButtonHorizontalPadding, |
+ NSHeight([dismissButton_ frame]) + kButtonVerticalPadding); |
+ } |
+ |
+ actionButton_ = [self addButtonWithString:delegate_->GetActionButtonText() |
+ isPrimary:YES]; |
+ NSSize actionButtonSize = |
+ NSMakeSize(NSWidth([actionButton_ frame]) + kButtonHorizontalPadding, |
+ NSHeight([actionButton_ frame]) + kButtonVerticalPadding); |
const CGFloat kHorizontalPadding = 15.0; |
const CGFloat kVerticalPadding = 10.0; |
// Next, we set frame for all the different pieces of the bubble, from bottom |
// to top. |
- CGFloat windowWidth = headingSize.width + kHorizontalPadding * 2; |
+ CGFloat windowWidth = newWidth + kHorizontalPadding * 2; |
CGFloat currentHeight = 0; |
- [button setFrame:NSMakeRect(windowWidth - buttonSize.width, |
- currentHeight, |
- buttonSize.width, |
- buttonSize.height)]; |
- currentHeight += buttonSize.height + kVerticalPadding; |
+ [actionButton_ setFrame:NSMakeRect(windowWidth - actionButtonSize.width, |
+ currentHeight, |
+ actionButtonSize.width, |
+ actionButtonSize.height)]; |
+ if (dismissButton_) { |
+ [dismissButton_ setFrame:NSMakeRect( |
+ windowWidth - actionButtonSize.width - dismissButtonSize.width, |
+ currentHeight, |
+ dismissButtonSize.width, |
+ dismissButtonSize.height)]; |
+ } |
+ currentHeight += actionButtonSize.height + kVerticalPadding; |
+ |
+ if (learnMoreButton_) { |
+ [learnMoreButton_ setFrame:NSMakeRect(kHorizontalPadding, |
+ currentHeight, |
+ learnMoreSize.width, |
+ learnMoreSize.height)]; |
+ currentHeight += learnMoreSize.height + kVerticalPadding; |
+ } |
+ |
[content setFrame:NSMakeRect(kHorizontalPadding, |
currentHeight, |
contentSize.width, |
@@ -194,22 +280,41 @@ |
} |
- (void)onButtonClicked:(id)sender { |
- if (!acknowledged_) { |
- delegate_->OnToolbarActionsBarBubbleClosed( |
- ToolbarActionsBarBubbleDelegate::ACKNOWLEDGED); |
- acknowledged_ = YES; |
- [self close]; |
+ if (acknowledged_) |
+ return; |
+ ToolbarActionsBarBubbleDelegate::CloseAction action = |
+ ToolbarActionsBarBubbleDelegate::CLOSE_EXECUTE; |
+ if (learnMoreButton_ && sender == learnMoreButton_) { |
+ action = ToolbarActionsBarBubbleDelegate::CLOSE_LEARN_MORE; |
+ } else if (dismissButton_ && sender == dismissButton_) { |
+ action = ToolbarActionsBarBubbleDelegate::CLOSE_DISMISS; |
+ } else { |
+ DCHECK_EQ(sender, actionButton_); |
+ action = ToolbarActionsBarBubbleDelegate::CLOSE_EXECUTE; |
} |
+ acknowledged_ = YES; |
+ delegate_->OnBubbleClosed(action); |
+ [self close]; |
} |
@end |
-@implementation ExtensionToolbarIconSurfacingBubbleButton |
+@implementation ExtensionMessageBubbleButton |
+ |
+@synthesize isPrimary = isPrimary_; |
- (void)drawRect:(NSRect)rect { |
- SkColor buttonColor = SkColorSetRGB(66, 133, 244); |
- SkColor textColor = [self hoverState] == kHoverStateNone ? |
- SkColorSetARGB(230, 255, 255, 255) : SK_ColorWHITE; |
+ SkColor buttonColor; |
+ SkColor textColor; |
+ if (isPrimary_) { |
+ buttonColor = SkColorSetRGB(66, 133, 244); |
+ textColor = [self hoverState] == kHoverStateNone ? |
+ SkColorSetARGB(220, 255, 255, 255) : SK_ColorWHITE; |
+ } else { |
+ buttonColor = SK_ColorTRANSPARENT; |
+ textColor = [self hoverState] == kHoverStateNone ? |
+ SkColorSetARGB(220, 0, 0, 0) : SK_ColorBLACK; |
+ } |
NSRect bounds = [self bounds]; |
NSAttributedString* title = [self attributedTitle]; |
@@ -224,6 +329,10 @@ |
value:selectedTitleColor |
range:NSMakeRange(0, [title length])]; |
+ if (!isPrimary_) { |
+ [[NSColor darkGrayColor] setStroke]; |
+ [NSBezierPath strokeRect:bounds]; |
+ } |
[[self cell] drawTitle:selectedTitle.get() |
withFrame:bounds |
inView:self]; |