| Index: chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm
|
| ===================================================================
|
| --- chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm (revision 99418)
|
| +++ chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm (working copy)
|
| @@ -5,6 +5,7 @@
|
| #import "chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h"
|
|
|
| #include "base/mac/mac_util.h"
|
| +#include "base/memory/scoped_nsobject.h"
|
| #include "base/string_util.h"
|
| #include "base/sys_string_conversions.h"
|
| #include "base/utf_string_conversions.h"
|
| @@ -15,11 +16,21 @@
|
| #include "chrome/common/extensions/extension.h"
|
| #include "grit/generated_resources.h"
|
| #include "skia/ext/skia_utils_mac.h"
|
| +#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
|
| #include "ui/base/l10n/l10n_util.h"
|
| #include "ui/base/l10n/l10n_util_mac.h"
|
|
|
| +@interface ExtensionInstallDialogController ()
|
| +- (bool)isInlineInstall;
|
| +- (void)appendRatingStar:(const SkBitmap*)skiaImage;
|
| +@end
|
| +
|
| namespace {
|
|
|
| +// Padding above the warnings separator, we must also subtract this when hiding
|
| +// it.
|
| +const CGFloat kWarningsSeparatorPadding = 14;
|
| +
|
| // Maximum height we will adjust controls to when trying to accomodate their
|
| // contents.
|
| const CGFloat kMaxControlHeight = 400;
|
| @@ -45,17 +56,26 @@
|
| [control setFrameOrigin:origin];
|
| }
|
|
|
| +void AppendRatingStarsShim(const SkBitmap* skiaImage, void* data) {
|
| + ExtensionInstallDialogController* controller =
|
| + static_cast<ExtensionInstallDialogController*>(data);
|
| + [controller appendRatingStar:skiaImage];
|
| }
|
|
|
| +}
|
| +
|
| @implementation ExtensionInstallDialogController
|
|
|
| @synthesize iconView = iconView_;
|
| @synthesize titleField = titleField_;
|
| @synthesize subtitleField = subtitleField_;
|
| @synthesize warningsField = warningsField_;
|
| -@synthesize warningsBox= warningsBox_;
|
| @synthesize cancelButton = cancelButton_;
|
| @synthesize okButton = okButton_;
|
| +@synthesize warningsSeparator = warningsSeparator_;
|
| +@synthesize ratingStars = ratingStars_;
|
| +@synthesize ratingCountField = ratingCountField_;
|
| +@synthesize userCountField = userCountField_;
|
|
|
| - (id)initWithParentWindow:(NSWindow*)window
|
| profile:(Profile*)profile
|
| @@ -65,10 +85,15 @@
|
| prompt:(const ExtensionInstallUI::Prompt&)prompt {
|
| NSString* nibpath = nil;
|
|
|
| - // We use a different XIB in the case of no permission warnings, that is a
|
| - // little bit more nicely laid out.
|
| - if (prompt.GetPermissionCount() == 0) {
|
| + // We use a different XIB in the case of inline installs or no permission
|
| + // warnings, that respectively show webstore ratings data and are a more
|
| + // nicely laid out.
|
| + if (prompt.type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT) {
|
| nibpath = [base::mac::MainAppBundle()
|
| + pathForResource:@"ExtensionInstallPromptInline"
|
| + ofType:@"nib"];
|
| + } else if (prompt.GetPermissionCount() == 0) {
|
| + nibpath = [base::mac::MainAppBundle()
|
| pathForResource:@"ExtensionInstallPromptNoWarnings"
|
| ofType:@"nib"];
|
| } else {
|
| @@ -82,32 +107,8 @@
|
| profile_ = profile;
|
| icon_ = *icon;
|
| delegate_ = delegate;
|
| -
|
| - title_.reset([base::SysUTF16ToNSString(
|
| - prompt.GetHeading(extension->name())) retain]);
|
| - subtitle_.reset([base::SysUTF16ToNSString(
|
| - prompt.GetPermissionsHeader()) retain]);
|
| - button_.reset([base::SysUTF16ToNSString(
|
| - prompt.GetAcceptButtonLabel()) retain]);
|
| - NSString* cancel_button_label = prompt.HasAbortButtonLabel() ?
|
| - base::SysUTF16ToNSString(prompt.GetAbortButtonLabel()) :
|
| - l10n_util::GetNSString(IDS_CANCEL);
|
| - cancel_button_.reset([cancel_button_label retain]);
|
| -
|
| - // We display the permission warnings as a simple text string, separated by
|
| - // newlines.
|
| - if (prompt.GetPermissionCount()) {
|
| - string16 joined_warnings;
|
| - for (size_t i = 0; i < prompt.GetPermissionCount(); ++i) {
|
| - if (i > 0)
|
| - joined_warnings += UTF8ToUTF16("\n\n");
|
| -
|
| - joined_warnings += prompt.GetPermission(i);
|
| - }
|
| -
|
| - warnings_.reset(
|
| - [base::SysUTF16ToNSString(joined_warnings) retain]);
|
| - }
|
| + extension_ = extension;
|
| + prompt_.reset(new ExtensionInstallUI::Prompt(prompt));
|
| }
|
| return self;
|
| }
|
| @@ -120,8 +121,18 @@
|
| contextInfo:nil];
|
| }
|
|
|
| +- (IBAction)storeLinkClicked:(id)sender {
|
| + GURL store_url(
|
| + extension_urls::GetWebstoreItemDetailURLPrefix() + extension_->id());
|
| + BrowserList::GetLastActiveWithProfile(profile_)->
|
| + OpenURL(store_url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
|
| +
|
| + delegate_->InstallUIAbort(/*user_initiated=*/true);
|
| + [NSApp endSheet:[self window]];
|
| +}
|
| +
|
| - (IBAction)cancel:(id)sender {
|
| - delegate_->InstallUIAbort(true);
|
| + delegate_->InstallUIAbort(/*user_initiated=*/true);
|
| [NSApp endSheet:[self window]];
|
| }
|
|
|
| @@ -131,15 +142,30 @@
|
| }
|
|
|
| - (void)awakeFromNib {
|
| - [titleField_ setStringValue:title_.get()];
|
| - [subtitleField_ setStringValue:subtitle_.get()];
|
| - [okButton_ setTitle:button_.get()];
|
| - [cancelButton_ setTitle:cancel_button_.get()];
|
| + // Make sure we're the window's delegate as set in the nib.
|
| + DCHECK_EQ(self, static_cast<ExtensionInstallDialogController*>(
|
| + [[self window] delegate]));
|
|
|
| + // Set control labels.
|
| + [titleField_ setStringValue:base::SysUTF16ToNSString(
|
| + prompt_->GetHeading(extension_->name()))];
|
| + [okButton_ setTitle:base::SysUTF16ToNSString(
|
| + prompt_->GetAcceptButtonLabel())];
|
| + [cancelButton_ setTitle:prompt_->HasAbortButtonLabel() ?
|
| + base::SysUTF16ToNSString(prompt_->GetAbortButtonLabel()) :
|
| + l10n_util::GetNSString(IDS_CANCEL)];
|
| + if ([self isInlineInstall]) {
|
| + prompt_->AppendRatingStars(AppendRatingStarsShim, self);
|
| + [ratingCountField_ setStringValue:base::SysUTF16ToNSString(
|
| + prompt_->GetRatingCount())];
|
| + [userCountField_ setStringValue:base::SysUTF16ToNSString(
|
| + prompt_->GetUserCount())];
|
| + }
|
| +
|
| NSImage* image = gfx::SkBitmapToNSImage(icon_);
|
| [iconView_ setImage:image];
|
|
|
| - // Reisze |titleField_| to fit title
|
| + // Resize |titleField_| to fit the title.
|
| CGFloat originalTitleWidth = [titleField_ frame].size.width;
|
| [titleField_ sizeToFit];
|
| CGFloat newTitleWidth = [titleField_ frame].size.width;
|
| @@ -149,47 +175,81 @@
|
| [[self window] setFrame:frame display:NO];
|
| }
|
|
|
| - // Make sure we're the window's delegate as set in the nib.
|
| - DCHECK_EQ(self, static_cast<ExtensionInstallDialogController*>(
|
| - [[self window] delegate]));
|
| + // Resize |okButton_| and |cancelButton_| to fit the button labels, but keep
|
| + // them right-aligned.
|
| + NSSize buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:okButton_];
|
| + if (buttonDelta.width) {
|
| + [okButton_ setFrame:NSOffsetRect([okButton_ frame], -buttonDelta.width, 0)];
|
| + [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame],
|
| + -buttonDelta.width, 0)];
|
| + }
|
| + buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:cancelButton_];
|
| + if (buttonDelta.width) {
|
| + [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame],
|
| + -buttonDelta.width, 0)];
|
| + }
|
|
|
| + CGFloat totalOffset = 0.0;
|
| // If there are any warnings, then we have to do some special layout.
|
| - if ([warnings_.get() length] > 0) {
|
| - [warningsField_ setStringValue:warnings_.get()];
|
| + if (prompt_->GetPermissionCount() > 0) {
|
| + [subtitleField_ setStringValue:base::SysUTF16ToNSString(
|
| + prompt_->GetPermissionsHeader())];
|
|
|
| + // We display the permission warnings as a simple text string, separated by
|
| + // newlines.
|
| + NSMutableString* joinedWarnings = [NSMutableString string];
|
| + for (size_t i = 0; i < prompt_->GetPermissionCount(); ++i) {
|
| + if (i > 0)
|
| + [joinedWarnings appendString:@"\n"];
|
| + [joinedWarnings appendString:base::SysUTF16ToNSString(
|
| + prompt_->GetPermission(i))];
|
| + }
|
| + [warningsField_ setStringValue:joinedWarnings];
|
| +
|
| // The dialog is laid out in the NIB exactly how we want it assuming that
|
| // each label fits on one line. However, for each label, we want to allow
|
| // wrapping onto multiple lines. So we accumulate an offset by measuring how
|
| - // big each label wants to be, and comparing it to how bit it actually is.
|
| + // big each label wants to be, and comparing it to how big it actually is.
|
| // Then we shift each label down and resize by the appropriate amount, then
|
| // finally resize the window.
|
| - CGFloat totalOffset = 0.0;
|
|
|
| - // Text fields.
|
| - totalOffset += AdjustControlHeightToFitContent(titleField_);
|
| - OffsetControlVertically(titleField_, -totalOffset);
|
| + // Additionally, in the store version of the dialog the icon extends past
|
| + // the one-line version of the permission field. Therefore when increasing
|
| + // the window size for multi-line permissions we don't have to add the full
|
| + // offset, only the part that extends past the icon.
|
| + CGFloat warningsGrowthSlack = 0;
|
| + if ([warningsField_ frame].origin.y > [iconView_ frame].origin.y) {
|
| + warningsGrowthSlack =
|
| + [warningsField_ frame].origin.y - [iconView_ frame].origin.y;
|
| + }
|
|
|
| totalOffset += AdjustControlHeightToFitContent(subtitleField_);
|
| OffsetControlVertically(subtitleField_, -totalOffset);
|
|
|
| - CGFloat warningsOffset = AdjustControlHeightToFitContent(warningsField_);
|
| - OffsetControlVertically(warningsField_, -warningsOffset);
|
| - totalOffset += warningsOffset;
|
| + totalOffset += AdjustControlHeightToFitContent(warningsField_);
|
| + OffsetControlVertically(warningsField_, -totalOffset);
|
| + totalOffset = MAX(totalOffset - warningsGrowthSlack, 0);
|
| + } else if ([self isInlineInstall]) {
|
| + // Inline installs that don't have a permissions section need to hide
|
| + // controls related to that and shrink the window by the space they take
|
| + // up.
|
| + NSRect hiddenRect = NSUnionRect([warningsSeparator_ frame],
|
| + [subtitleField_ frame]);
|
| + hiddenRect = NSUnionRect(hiddenRect, [warningsField_ frame]);
|
| + [warningsSeparator_ setHidden:YES];
|
| + [subtitleField_ setHidden:YES];
|
| + [warningsField_ setHidden:YES];
|
| + totalOffset -= hiddenRect.size.height + kWarningsSeparatorPadding;
|
| + }
|
|
|
| - NSRect warningsBoxRect = [warningsBox_ frame];
|
| - warningsBoxRect.origin.y -= totalOffset;
|
| - warningsBoxRect.size.height += warningsOffset;
|
| - [warningsBox_ setFrame:warningsBoxRect];
|
| -
|
| - // buttons are positioned automatically in the XIB.
|
| -
|
| - // Finally, adjust the window size.
|
| + // If necessary, adjust the window size.
|
| + if (totalOffset) {
|
| NSRect currentRect = [[self window] frame];
|
| [[self window] setFrame:NSMakeRect(currentRect.origin.x,
|
| currentRect.origin.y - totalOffset,
|
| currentRect.size.width,
|
| currentRect.size.height + totalOffset)
|
| - display:NO];
|
| + display:NO];
|
| }
|
| }
|
|
|
| @@ -203,6 +263,28 @@
|
| [self autorelease];
|
| }
|
|
|
| +- (bool)isInlineInstall {
|
| + return prompt_->type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT;
|
| +}
|
| +
|
| +- (void)appendRatingStar:(const SkBitmap*)skiaImage {
|
| + NSImage* image = gfx::SkBitmapToNSImageWithColorSpace(
|
| + *skiaImage, base::mac::GetSystemColorSpace());
|
| + NSRect frame = NSMakeRect(0, 0, skiaImage->width(), skiaImage->height());
|
| + scoped_nsobject<NSImageView> view([[NSImageView alloc] initWithFrame:frame]);
|
| + [view setImage:image];
|
| +
|
| + // Add this star after all the other ones
|
| + CGFloat maxStarRight = 0;
|
| + if ([[ratingStars_ subviews] count]) {
|
| + maxStarRight = NSMaxX([[[ratingStars_ subviews] lastObject] frame]);
|
| + }
|
| + NSRect starBounds = NSMakeRect(maxStarRight, 0,
|
| + skiaImage->width(), skiaImage->height());
|
| + [view setFrame:starBounds];
|
| + [ratingStars_ addSubview:view];
|
| +}
|
| +
|
| @end // ExtensionInstallDialogController
|
|
|
| void ShowExtensionInstallDialog(
|
| @@ -234,5 +316,6 @@
|
| icon:icon
|
| prompt:prompt];
|
|
|
| + // TODO(mihaip): Switch this to be tab-modal (http://crbug.com/95455)
|
| [controller runAsModalSheet];
|
| }
|
|
|