diff options
author | groby@chromium.org <groby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-28 21:19:16 +0000 |
---|---|---|
committer | groby@chromium.org <groby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-28 21:19:16 +0000 |
commit | a75c27e72d1853ec4532d697b7a7a00d3d98caab (patch) | |
tree | 9da27cb4a5fcb21321071e2987e00a7dd7a676cc | |
parent | c644b3dfca350eb962f3a08dced7ec9c6f4338b0 (diff) | |
download | chromium_src-a75c27e72d1853ec4532d697b7a7a00d3d98caab.zip chromium_src-a75c27e72d1853ec4532d697b7a7a00d3d98caab.tar.gz chromium_src-a75c27e72d1853ec4532d697b7a7a00d3d98caab.tar.bz2 |
Unify suggestions/service list.
R=thakis@chromium.org
BUG=139026
Review URL: https://chromiumcodereview.appspot.com/10882008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153736 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/generated_resources.grd | 3 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/web_intent_sheet_controller.h | 5 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/web_intent_sheet_controller.mm | 468 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/web_intent_sheet_controller_unittest.mm | 150 |
4 files changed, 349 insertions, 277 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index b11c910..a6a2a7801 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -16999,6 +16999,9 @@ Battery full <message name="IDS_INTENT_PICKER_WAIT_FOR_CWS" desc="Prompt indicating to the user that Chrome is currently waiting for results from the Chrome Web Store"> Waiting for Chrome Web Store </message> + <message name="IDS_INTENT_PICKER_SELECT_INTENT" desc="Button text that, when clicked, allows the user to select the associated intent"> + Select + </message> <!-- Web Intents common actions --> <message name="IDS_WEB_INTENTS_ACTION_SHARE" desc="Question asking the user to pick a service for the 'share' action."> diff --git a/chrome/browser/ui/cocoa/web_intent_sheet_controller.h b/chrome/browser/ui/cocoa/web_intent_sheet_controller.h index b7d2eea..2e47a8a 100644 --- a/chrome/browser/ui/cocoa/web_intent_sheet_controller.h +++ b/chrome/browser/ui/cocoa/web_intent_sheet_controller.h @@ -13,7 +13,7 @@ class WebIntentPickerCocoa; class WebIntentPickerModel; -@class SuggestionView; +@class IntentView; // Controller for intent picker constrained dialog. This dialog pops up // whenever a web page invokes ActivateIntent and lets the user choose which @@ -30,9 +30,8 @@ class WebIntentPickerModel; WebIntentPickerModel* model_; scoped_nsobject<NSTextField> actionTextField_; - scoped_nsobject<SuggestionView> suggestionView_; + scoped_nsobject<IntentView> intentView_; scoped_nsobject<NSButton> closeButton_; - scoped_nsobject<NSMutableArray> intentButtons_; scoped_nsobject<NSView> flipView_; scoped_nsobject<NSTextField> inlineDispositionTitleField_; } diff --git a/chrome/browser/ui/cocoa/web_intent_sheet_controller.mm b/chrome/browser/ui/cocoa/web_intent_sheet_controller.mm index e173769..b284567 100644 --- a/chrome/browser/ui/cocoa/web_intent_sheet_controller.mm +++ b/chrome/browser/ui/cocoa/web_intent_sheet_controller.mm @@ -53,6 +53,9 @@ const CGFloat kHeaderFontSize = 14.5; const CGFloat kTextWidth = WebIntentPicker::kWindowWidth - (WebIntentPicker::kContentAreaBorder * 2.0 + kCloseButtonSize); +// Maximum number of intents (suggested and installed) displayed. +const int kMaxIntentRows = 4; + // Sets properties on the given |field| to act as title or description labels. void ConfigureTextFieldAsLabel(NSTextField* field) { [field setEditable:NO]; @@ -253,14 +256,15 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { } @end -// NSView for a single row in the suggestions view. -@interface SingleSuggestionView : NSView { +// NSView for a single row in the intents view. +@interface IntentRowView : NSView { @private scoped_nsobject<NSProgressIndicator> throbber_; scoped_nsobject<NSButton> cwsButton_; scoped_nsobject<RatingsView> ratingsWidget_; scoped_nsobject<NSButton> installButton_; scoped_nsobject<DimmableImageView> iconView_; + scoped_nsobject<NSTextField> label_; } - (id)initWithExtension: @@ -273,104 +277,12 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { - (NSInteger)tag; @end -@implementation SingleSuggestionView -- (id)initWithExtension: - (const WebIntentPickerModel::SuggestedExtension*)extension - withIndex:(size_t)index - forController:(WebIntentPickerSheetController*)controller { - const CGFloat kMaxHeight = 34.0; - const CGFloat kTitleX = 20.0; - const CGFloat kMinAddButtonHeight = 24.0; - const CGFloat kAddButtonX = 245; - const CGFloat kAddButtonWidth = 128.0; - - // Build the main view - NSRect contentFrame = NSMakeRect( - 0, 0, WebIntentPicker::kWindowWidth, kMaxHeight); - if (self = [super initWithFrame:contentFrame]) { - NSMutableArray* subviews = [NSMutableArray array]; - - // Add the extension icon. - NSImage* icon = extension->icon.ToNSImage(); - NSRect imageFrame = NSZeroRect; - - iconView_.reset( - [[DimmableImageView alloc] initWithFrame:imageFrame]); - [iconView_ setImage:icon]; - [iconView_ setImageFrameStyle:NSImageFrameNone]; - [iconView_ setEnabled:YES]; - - imageFrame.size = [icon size]; - imageFrame.size.height = std::min(NSHeight(imageFrame), kMaxHeight); - imageFrame.origin.y += (kMaxHeight - NSHeight(imageFrame)) / 2.0; - [iconView_ setFrame:imageFrame]; - [subviews addObject:iconView_]; - - // Add the extension title. - NSRect frame = NSMakeRect(kTitleX, 0, 0, 0); - - const string16 elidedTitle = ui::ElideText( - extension->title, gfx::Font(), WebIntentPicker::kTitleLinkMaxWidth, - ui::ELIDE_AT_END); - NSString* string = base::SysUTF16ToNSString(elidedTitle); - cwsButton_.reset(CreateHyperlinkButton(string, frame)); - [cwsButton_ setAlignment:NSLeftTextAlignment]; - [cwsButton_ setTarget:controller]; - [cwsButton_ setAction:@selector(openExtensionLink:)]; - [cwsButton_ setTag:index]; - [cwsButton_ sizeToFit]; - - frame = [cwsButton_ frame]; - frame.size.height = std::min([[cwsButton_ cell] cellSize].height, - kMaxHeight); - frame.origin.y = (kMaxHeight - NSHeight(frame)) / 2.0; - [cwsButton_ setFrame:frame]; - [subviews addObject:cwsButton_]; - - // Add the star rating - CGFloat offsetX = frame.origin.x + NSWidth(frame) + - WebIntentPicker::kContentAreaBorder; - ratingsWidget_.reset( - [SingleSuggestionView - createStarWidgetWithRating:extension->average_rating]); - frame = [ratingsWidget_ frame]; - frame.origin.y += (kMaxHeight - NSHeight(frame)) / 2.0; - frame.origin.x = offsetX; - [ratingsWidget_ setFrame: frame]; - [subviews addObject:ratingsWidget_]; - - // Add an "add to chromium" button. - frame = NSMakeRect(kAddButtonX, 0, kAddButtonWidth, 0); - installButton_.reset([[NSButton alloc] initWithFrame:frame]); - [installButton_ setAlignment:NSLeftTextAlignment]; - [installButton_ setButtonType:NSMomentaryPushInButton]; - [installButton_ setBezelStyle:NSRegularSquareBezelStyle]; - string = l10n_util::GetNSStringWithFixup( - IDS_INTENT_PICKER_INSTALL_EXTENSION); - [installButton_ setTitle:string]; - frame.size.height = std::min(kMinAddButtonHeight, kMaxHeight); - frame.origin.y += (kMaxHeight - NSHeight(frame)) / 2.0; - [installButton_ setFrame:frame]; - - [installButton_ setTarget:controller]; - [installButton_ setAction:@selector(installExtension:)]; - [installButton_ setTag:index]; - [subviews addObject:installButton_]; - - // Keep a throbber handy. - frame.origin.x += (NSWidth(frame) - 16) / 2; - frame.origin.y += (NSHeight(frame) - 16) /2; - frame.size = NSMakeSize(16, 16); - throbber_.reset( - [[NSProgressIndicator alloc] initWithFrame:frame]); - [throbber_ setHidden:YES]; - [throbber_ setStyle:NSProgressIndicatorSpinningStyle]; - [subviews addObject:throbber_]; - - [self setSubviews:subviews]; - } - return self; -} +@implementation IntentRowView +const CGFloat kMaxHeight = 34.0; +const CGFloat kTitleX = 20.0; +const CGFloat kMinAddButtonHeight = 28.0; +const CGFloat kAddButtonX = 245; +const CGFloat kAddButtonWidth = 128.0; + (RatingsView*)createStarWidgetWithRating:(CGFloat)rating { const int kStarSpacing = 1; // Spacing between stars in pixels. @@ -401,6 +313,147 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { return widget; } + +- (void)setIcon:(NSImage*)icon { + NSRect imageFrame = NSZeroRect; + + iconView_.reset( + [[DimmableImageView alloc] initWithFrame:imageFrame]); + [iconView_ setImage:icon]; + [iconView_ setImageFrameStyle:NSImageFrameNone]; + [iconView_ setEnabled:YES]; + + imageFrame.size = [icon size]; + imageFrame.size.height = std::min(NSHeight(imageFrame), kMaxHeight); + imageFrame.origin.y += (kMaxHeight - NSHeight(imageFrame)) / 2.0; + [iconView_ setFrame:imageFrame]; +} + +- (void)setActionButton:(NSString*)title + withSelector:(SEL)selector + forController:(WebIntentPickerSheetController*)controller { + + NSRect frame = NSMakeRect(kAddButtonX, 0, kAddButtonWidth, 0); + installButton_.reset([[NSButton alloc] initWithFrame:frame]); + [installButton_ setAlignment:NSCenterTextAlignment]; + [installButton_ setButtonType:NSMomentaryPushInButton]; + [installButton_ setBezelStyle:NSRegularSquareBezelStyle]; + [installButton_ setTitle:title]; + frame.size.height = std::min(kMinAddButtonHeight, kMaxHeight); + frame.origin.y += (kMaxHeight - NSHeight(frame)) / 2.0; + [installButton_ setFrame:frame]; + + [installButton_ setTarget:controller]; + [installButton_ setAction:selector]; +} + +- (id)init { + // Build the main view. + NSRect contentFrame = NSMakeRect( + 0, 0, WebIntentPicker::kWindowWidth, kMaxHeight); + if (self = [super initWithFrame:contentFrame]) { + NSMutableArray* subviews = [NSMutableArray array]; + if (iconView_) [subviews addObject:iconView_]; + if (label_) [subviews addObject:label_]; + if (cwsButton_) [subviews addObject:cwsButton_]; + if (ratingsWidget_) [subviews addObject:ratingsWidget_]; + if (installButton_) [subviews addObject:installButton_]; + if (throbber_) [subviews addObject:throbber_]; + [self setSubviews:subviews]; + } + return self; +} + +- (id)initWithExtension: + (const WebIntentPickerModel::SuggestedExtension*)extension + withIndex:(size_t)index + forController:(WebIntentPickerSheetController*)controller { + // Add the extension icon. + [self setIcon:extension->icon.ToNSImage()]; + + // Add the extension title. + NSRect frame = NSMakeRect(kTitleX, 0, 0, 0); + const string16 elidedTitle = ui::ElideText( + extension->title, gfx::Font(), WebIntentPicker::kTitleLinkMaxWidth, + ui::ELIDE_AT_END); + NSString* string = base::SysUTF16ToNSString(elidedTitle); + cwsButton_.reset(CreateHyperlinkButton(string, frame)); + [cwsButton_ setAlignment:NSCenterTextAlignment]; + [cwsButton_ setTarget:controller]; + [cwsButton_ setAction:@selector(openExtensionLink:)]; + [cwsButton_ setTag:index]; + [cwsButton_ sizeToFit]; + + frame = [cwsButton_ frame]; + frame.size.height = std::min([[cwsButton_ cell] cellSize].height, + kMaxHeight); + frame.origin.y = (kMaxHeight - NSHeight(frame)) / 2.0; + [cwsButton_ setFrame:frame]; + + // Add the star rating + CGFloat offsetX = frame.origin.x + NSWidth(frame) + + WebIntentPicker::kContentAreaBorder; + ratingsWidget_.reset( + [IntentRowView + createStarWidgetWithRating:extension->average_rating]); + frame = [ratingsWidget_ frame]; + frame.origin.y += (kMaxHeight - NSHeight(frame)) / 2.0; + frame.origin.x = offsetX; + [ratingsWidget_ setFrame: frame]; + + // Add an "add to chromium" button. + string = l10n_util::GetNSStringWithFixup( + IDS_INTENT_PICKER_INSTALL_EXTENSION); + [self setActionButton:string + withSelector:@selector(installExtension:) + forController:controller]; + [installButton_ setTag:index]; + + // Keep a throbber handy. + frame = [installButton_ frame]; + frame.origin.x += (NSWidth(frame) - 16) / 2; + frame.origin.y += (NSHeight(frame) - 16) /2; + frame.size = NSMakeSize(16, 16); + throbber_.reset([[NSProgressIndicator alloc] initWithFrame:frame]); + [throbber_ setHidden:YES]; + [throbber_ setStyle:NSProgressIndicatorSpinningStyle]; + + return [self init]; +} + +- (id)initWithService: + (const WebIntentPickerModel::InstalledService*)service + withIndex:(size_t)index + forController:(WebIntentPickerSheetController*)controller { + + // Add the extension icon. + [self setIcon:service->favicon.ToNSImage()]; + + // Add the extension title. + NSRect frame = NSMakeRect( + kTitleX, 0, WebIntentPicker::kTitleLinkMaxWidth, 10); + + const string16 elidedTitle = ui::ElideText( + service->title, gfx::Font(), + WebIntentPicker::kTitleLinkMaxWidth, ui::ELIDE_AT_END); + NSString* string = base::SysUTF16ToNSString(elidedTitle); + + label_.reset([[NSTextField alloc] initWithFrame:frame]); + ConfigureTextFieldAsLabel(label_); + [label_ setStringValue:string]; + frame.size.height += + [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField: + label_]; + frame.origin.y = (kMaxHeight - NSHeight(frame)) / 2.0; + [label_ setFrame:frame]; + + [self setActionButton:@"Select" + withSelector:@selector(invokeService:) + forController:controller]; + [installButton_ setTag:index]; + return [self init]; +} + - (NSInteger)tag { return [installButton_ tag]; } @@ -410,7 +463,7 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { [throbber_ setHidden:NO]; [throbber_ startAnimation:self]; [iconView_ setEnabled:YES]; - [ratingsWidget_ setEnabled:YES]; + [ratingsWidget_ setEnabled:NO]; } - (void)setEnabled:(BOOL) enabled { @@ -421,18 +474,30 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { } - (void)stopThrobber { - [throbber_ setHidden:YES]; - [throbber_ stopAnimation:self]; + if (throbber_.get()) { + [throbber_ setHidden:YES]; + [throbber_ stopAnimation:self]; + } [installButton_ setHidden:NO]; } +- (void)adjustButtonSize:(CGFloat)newWidth { + CGFloat increase = std::max(kAddButtonWidth, newWidth) - kAddButtonWidth; + NSRect frame = [self frame]; + frame.size.width += increase; + [self setFrame:frame]; + + frame = [installButton_ frame]; + frame.size.width += increase; + [installButton_ setFrame:frame]; +} + @end -@interface SuggestionView : NSView { +@interface IntentView : NSView { @private // Used to forward button clicks. Weak reference. WebIntentPickerSheetController* controller_; - scoped_nsobject<NSTextField> suggestionLabel_; } - (id)initWithModel:(WebIntentPickerModel*)model @@ -441,70 +506,80 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { - (void)stopThrobber; @end -@implementation SuggestionView +@implementation IntentView - (id)initWithModel:(WebIntentPickerModel*)model forController:(WebIntentPickerSheetController*)controller { - size_t count = model->GetSuggestedExtensionCount(); - if (count == 0) - return nil; + if (self = [super initWithFrame:NSZeroRect]) { + const CGFloat kYMargin = 16.0; + int availableSpots = kMaxIntentRows; - NSMutableArray* subviews = [NSMutableArray array]; - - NSRect textFrame = NSMakeRect(0, 0, - kTextWidth, 1); - suggestionLabel_.reset([[NSTextField alloc] initWithFrame:textFrame]); - ConfigureTextFieldAsLabel(suggestionLabel_); - - CGFloat offset = 0.0; - for (size_t i = count; i > 0; --i) { - const WebIntentPickerModel::SuggestedExtension& ext = - model->GetSuggestedExtensionAt(i - 1); - scoped_nsobject<NSView> suggestView( - [[SingleSuggestionView alloc] initWithExtension:&ext - withIndex:i-1 - forController:controller]); - offset += [self addStackedView:suggestView.get() - toSubviews:subviews - atOffset:offset]; - } - - [self updateSuggestionLabelForModel:model]; - offset += [self addStackedView:suggestionLabel_ - toSubviews:subviews - atOffset:offset]; - - NSRect contentFrame = NSMakeRect(WebIntentPicker::kContentAreaBorder, 0, - WebIntentPicker::kWindowWidth, offset); - if(self = [super initWithFrame:contentFrame]) - [self setSubviews: subviews]; - - controller_ = controller; - return self; -} + CGFloat offset = kYMargin; + NSMutableArray* subviews = [NSMutableArray array]; + NSMutableArray* rows = [NSMutableArray array]; + + for (size_t i = 0; i < model->GetInstalledServiceCount() && availableSpots; + ++i, --availableSpots) { + const WebIntentPickerModel::InstalledService& svc = + model->GetInstalledServiceAt(i); + scoped_nsobject<NSView> suggestView( + [[IntentRowView alloc] initWithService:&svc + withIndex:i + forController:controller]); + + [rows addObject:suggestView]; + } -- (void)updateSuggestionLabelForModel:(WebIntentPickerModel*)model { - DCHECK(suggestionLabel_.get()); - string16 labelText = model->GetSuggestionsLinkText(); - if (!model->GetInstalledServiceCount()) - labelText.clear(); + // Special case for the empty view. + size_t count = model->GetSuggestedExtensionCount(); + if (count == 0 && availableSpots == kMaxIntentRows) + return nil; + + for (size_t i = count; i > 0 && availableSpots; --i, --availableSpots) { + const WebIntentPickerModel::SuggestedExtension& ext = + model->GetSuggestedExtensionAt(i - 1); + scoped_nsobject<NSView> suggestView( + [[IntentRowView alloc] initWithExtension:&ext + withIndex:i-1 + forController:controller]); + [rows addObject:suggestView]; + } - if (labelText.empty()) { - [suggestionLabel_ setHidden:TRUE]; - } else { - NSRect textFrame = [suggestionLabel_ frame]; + // Determine optimal width for buttons given localized texts. + scoped_nsobject<NSButton> sizeHelper( + [[NSButton alloc] initWithFrame:NSZeroRect]); + [sizeHelper setAlignment:NSCenterTextAlignment]; + [sizeHelper setButtonType:NSMomentaryPushInButton]; + [sizeHelper setBezelStyle:NSRegularSquareBezelStyle]; + + [sizeHelper setTitle: + l10n_util::GetNSStringWithFixup(IDS_INTENT_PICKER_INSTALL_EXTENSION)]; + [sizeHelper sizeToFit]; + CGFloat buttonWidth = NSWidth([sizeHelper frame]); + + [sizeHelper setTitle: + l10n_util::GetNSStringWithFixup(IDS_INTENT_PICKER_SELECT_INTENT)]; + [sizeHelper sizeToFit]; + buttonWidth = std::max(buttonWidth, NSWidth([sizeHelper frame])); + + for (IntentRowView* row in [rows reverseObjectEnumerator]) { + [row adjustButtonSize:buttonWidth]; + offset += [self addStackedView:row + toSubviews:subviews + atOffset:offset]; + } - [suggestionLabel_ setHidden:FALSE]; - [suggestionLabel_ setStringValue:base::SysUTF16ToNSString(labelText)]; - textFrame.size.height += - [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField: - suggestionLabel_]; - [suggestionLabel_ setFrame: textFrame]; + [self setSubviews:subviews]; + NSRect contentFrame = NSMakeRect(WebIntentPicker::kContentAreaBorder, 0, + WebIntentPicker::kWindowWidth, offset); + [self setFrame:contentFrame]; + controller_ = controller; } + return self; } - (void)startThrobberForRow:(NSInteger)index { - for (SingleSuggestionView* row in [self subviews]) { - if ([row isMemberOfClass:[SingleSuggestionView class]]) { + for (IntentRowView* row in [self subviews]) { + if ([row isMemberOfClass:[IntentRowView class]]) { [row setEnabled:NO]; if ([row tag] == index) { [row startThrobber]; @@ -514,8 +589,8 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { } - (void)stopThrobber { - for (SingleSuggestionView* row in [self subviews]) { - if ([row isMemberOfClass:[SingleSuggestionView class]]) { + for (IntentRowView* row in [self subviews]) { + if ([row isMemberOfClass:[IntentRowView class]]) { [row stopThrobber]; [row setEnabled:YES]; } @@ -526,7 +601,6 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { [controller_ installExtension:sender]; } - - (CGFloat)addStackedView:(NSView*)view toSubviews:(NSMutableArray*)subviews atOffset:(CGFloat)offset { @@ -560,7 +634,6 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { picker_ = picker; if (picker) model_ = picker->model(); - intentButtons_.reset([[NSMutableArray alloc] init]); inlineDispositionTitleField_.reset([[NSTextField alloc] init]); ConfigureTextFieldAsLabel(inlineDispositionTitleField_); @@ -682,19 +755,12 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { const WebIntentPickerModel::SuggestedExtension& extension = model_->GetSuggestedExtensionAt([sender tag]); if (picker_) { - [suggestionView_ startThrobberForRow:[sender tag]]; + [intentView_ startThrobberForRow:[sender tag]]; [closeButton_ setEnabled:NO]; - [self setIntentButtonsEnabled:NO]; picker_->OnExtensionInstallRequested(UTF16ToUTF8(extension.id)); } } -- (void)setIntentButtonsEnabled:(BOOL)enabled { - for (NSButton* button in intentButtons_.get()) { - [button setEnabled:enabled]; - } -} - - (CGFloat)addStackedView:(NSView*)view toSubviews:(NSMutableArray*)subviews atOffset:(CGFloat)offset { @@ -830,42 +896,6 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { return NSHeight(textFrame); } -// Add a single button for a specific service -- (CGFloat)addServiceButton:(NSString*)title - withImage:(NSImage*)image - index:(NSUInteger)index - toSubviews:(NSMutableArray*)subviews - atOffset:(CGFloat)offset { - // Buttons are displayed centered. - CGFloat offsetX = (WebIntentPicker::kWindowWidth - kServiceButtonWidth) / 2.0; - NSRect frame = NSMakeRect(offsetX, offset, kServiceButtonWidth, 45); - scoped_nsobject<NSButton> button([[NSButton alloc] initWithFrame:frame]); - - if (image) { - [button setImage:image]; - [button setImagePosition:NSImageLeft]; - } - [button setAlignment:NSLeftTextAlignment]; - [button setButtonType:NSMomentaryPushInButton]; - [button setBezelStyle:NSRegularSquareBezelStyle]; - [button setTarget:self]; - [button setTitle:title]; - [button setTag:index]; - [button setAction:@selector(invokeService:)]; - [subviews addObject:button]; - [intentButtons_ addObject:button]; - - // Call size-to-fit to fixup size. - [GTMUILocalizerAndLayoutTweaker sizeToFitView:button.get()]; - - // But make sure we're limited to a fixed size. - frame = [button frame]; - frame.size.width = kServiceButtonWidth; - [button setFrame:frame]; - - return NSHeight([button frame]); -} - - (NSView*)createEmptyView { NSMutableArray* subviews = [NSMutableArray array]; @@ -946,24 +976,9 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { offset += [self addHeaderToSubviews:subviews atOffset:offset]; if (model) { - [intentButtons_ removeAllObjects]; - - for (NSUInteger i = 0; i < model->GetInstalledServiceCount(); ++i) { - const WebIntentPickerModel::InstalledService& service = - model->GetInstalledServiceAt(i); - offset += [self addServiceButton:base::SysUTF16ToNSString(service.title) - withImage:service.favicon.ToNSImage() - index:i - toSubviews:subviews - atOffset:offset]; - } - - if (model->GetInstalledServiceCount()) - offset += kVerticalSpacing; - - suggestionView_.reset( - [[SuggestionView alloc] initWithModel:model forController:self]); - offset += [self addStackedView:suggestionView_ + intentView_.reset( + [[IntentView alloc] initWithModel:model forController:self]); + offset += [self addStackedView:intentView_ toSubviews:subviews atOffset:offset]; } @@ -1016,8 +1031,7 @@ NSButton* CreateHyperlinkButton(NSString* title, const NSRect& frame) { - (void)stopThrobber { [closeButton_ setEnabled:YES]; - [self setIntentButtonsEnabled:YES]; - [suggestionView_ stopThrobber]; + [intentView_ stopThrobber]; } - (void)closeSheet { diff --git a/chrome/browser/ui/cocoa/web_intent_sheet_controller_unittest.mm b/chrome/browser/ui/cocoa/web_intent_sheet_controller_unittest.mm index 70337ac..d11f6b4 100644 --- a/chrome/browser/ui/cocoa/web_intent_sheet_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/web_intent_sheet_controller_unittest.mm @@ -53,18 +53,19 @@ class WebIntentPickerSheetControllerTest : public CocoaTest { NSArray* views = [[flip_views objectAtIndex:0] subviews]; - // 3 + |row_count| subviews - header text, close button, - // |row_count| buttons, and a CWS link. - ASSERT_EQ(3U + row_count, [views count]); + // 4 subviews - header view, intents list, CWS link, close button. + // intents list is not added if there are no rows. + NSUInteger view_offset = row_count ? 1U : 0U; + ASSERT_EQ(3U + view_offset, [views count]); - const NSUInteger kFirstButton = 1; ASSERT_TRUE([[views objectAtIndex:0] isKindOfClass:[NSView class]]); CheckHeader([views objectAtIndex:0]); - for(NSUInteger i = 0; i < row_count; ++i) { - ASSERT_TRUE([[views objectAtIndex:kFirstButton + i] isKindOfClass: - [NSButton class]]); + if (view_offset) { + ASSERT_TRUE([[views objectAtIndex:1] isKindOfClass:[NSView class]]); } - ASSERT_TRUE([[views lastObject] isKindOfClass: + ASSERT_TRUE([[views objectAtIndex:1 + view_offset] isKindOfClass: + [NSButton class]]); + ASSERT_TRUE([[views objectAtIndex:2 + view_offset] isKindOfClass: [HoverCloseButton class]]); // Verify the close button @@ -73,22 +74,47 @@ class WebIntentPickerSheetControllerTest : public CocoaTest { // Verify the Chrome Web Store button. NSButton* button = static_cast<NSButton*>( - [views objectAtIndex:kFirstButton + row_count]); - ASSERT_TRUE([button isKindOfClass:[NSButton class]]); + [views objectAtIndex:1 + view_offset]); EXPECT_TRUE([[button cell] isKindOfClass:[HyperlinkButtonCell class]]); CheckButton(button, @selector(showChromeWebStore:)); + } - // Verify buttons pointing to services. - for(NSUInteger i = 0; i < row_count; ++i) { - NSButton* button = [views objectAtIndex:kFirstButton + i]; - CheckServiceButton(button, i); - } + void CheckSuggestionView(NSView* item_view, NSUInteger suggestion_index) { + // 5 subobjects - Icon, title, star rating, add button, and throbber. + ASSERT_EQ(5U, [[item_view subviews] count]); + + // Verify title button is hooked up properly + ASSERT_TRUE([[[item_view subviews] objectAtIndex:1] + isKindOfClass:[NSButton class]]); + NSButton* title_button = [[item_view subviews] objectAtIndex:1]; + CheckButton(title_button, @selector(openExtensionLink:)); + + // Verify "Add to Chromium" button is hooked up properly + ASSERT_TRUE([[[item_view subviews] objectAtIndex:3] + isKindOfClass:[NSButton class]]); + NSButton* add_button = [[item_view subviews] objectAtIndex:3]; + CheckButton(add_button, @selector(installExtension:)); + EXPECT_EQ(NSInteger(suggestion_index), [add_button tag]); + + // Verify we have a throbber. + ASSERT_TRUE([[[item_view subviews] objectAtIndex:4] + isKindOfClass:[NSProgressIndicator class]]); } - // Checks that a service button is hooked up correctly. - void CheckServiceButton(NSButton* button, NSUInteger service_index) { - CheckButton(button, @selector(invokeService:)); - EXPECT_EQ(NSInteger(service_index), [button tag]); + void CheckServiceView(NSView* item_view, NSUInteger service_index) { + // 3 subobjects - Icon, title, select button. + ASSERT_EQ(3U, [[item_view subviews] count]); + + // Verify title is a text field. + ASSERT_TRUE([[[item_view subviews] objectAtIndex:1] + isKindOfClass:[NSTextField class]]); + + // Verify "Select" button is hooked up properly. + ASSERT_TRUE([[[item_view subviews] objectAtIndex:2] + isKindOfClass:[NSButton class]]); + NSButton* select_button = [[item_view subviews] objectAtIndex:2]; + CheckButton(select_button, @selector(invokeService:)); + EXPECT_EQ(NSInteger(service_index), [select_button tag]); } // Checks that a button is hooked up correctly. @@ -110,7 +136,7 @@ TEST_F(WebIntentPickerSheetControllerTest, NoRows) { CheckWindow(/*row_count=*/0); } -TEST_F(WebIntentPickerSheetControllerTest, PopulatedRows) { +TEST_F(WebIntentPickerSheetControllerTest, IntentRows) { WebIntentPickerModel model; model.AddInstalledService(string16(), GURL("http://example.org/intent.html"), webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW); @@ -120,9 +146,24 @@ TEST_F(WebIntentPickerSheetControllerTest, PopulatedRows) { [controller_ performLayoutWithModel:&model]; CheckWindow(/*row_count=*/2); + + NSArray* flip_views = [[window_ contentView] subviews]; + NSArray* main_views = [[flip_views objectAtIndex:0] subviews]; + + // 2nd object should be the suggestion view, 3rd one is close button. + ASSERT_TRUE([main_views count] > 2); + ASSERT_TRUE([[main_views objectAtIndex:1] isKindOfClass:[NSView class]]); + NSView* intent_view = [main_views objectAtIndex:1]; + + // 2 subviews - the two installed services item. Tags are assigned reverse. + ASSERT_EQ(2U, [[intent_view subviews] count]); + NSView* item_view = [[intent_view subviews] objectAtIndex:0]; + CheckServiceView(item_view, 1); + item_view = [[intent_view subviews] objectAtIndex:1]; + CheckServiceView(item_view, 0); } -TEST_F(WebIntentPickerSheetControllerTest, SuggestionView) { +TEST_F(WebIntentPickerSheetControllerTest, SuggestionRow) { WebIntentPickerModel model; std::vector<WebIntentPickerModel::SuggestedExtension> suggestions; suggestions.push_back(WebIntentPickerModel::SuggestedExtension( @@ -130,6 +171,8 @@ TEST_F(WebIntentPickerSheetControllerTest, SuggestionView) { model.AddSuggestedExtensions(suggestions); [controller_ performLayoutWithModel:&model]; + CheckWindow(/*row_count=*/1); + // Get subviews. NSArray* flip_views = [[window_ contentView] subviews]; NSArray* main_views = [[flip_views objectAtIndex:0] subviews]; @@ -137,34 +180,47 @@ TEST_F(WebIntentPickerSheetControllerTest, SuggestionView) { // 2nd object should be the suggestion view, 3rd one is close button. ASSERT_TRUE([main_views count] > 2); ASSERT_TRUE([[main_views objectAtIndex:1] isKindOfClass:[NSView class]]); - NSView* suggest_view = [main_views objectAtIndex:1]; + NSView* intent_view = [main_views objectAtIndex:1]; - // There are two subviews - label & suggested items. - ASSERT_EQ(2U, [[suggest_view subviews] count]); - ASSERT_TRUE([[[suggest_view subviews] objectAtIndex:1] - isKindOfClass:[NSTextField class]]); - ASSERT_TRUE([[[suggest_view subviews] objectAtIndex:0] + // One subview - the suggested item. + ASSERT_EQ(1U, [[intent_view subviews] count]); + ASSERT_TRUE([[[intent_view subviews] objectAtIndex:0] isKindOfClass:[NSView class]]); - NSView* item_view = [[suggest_view subviews] objectAtIndex:0]; - - // 5 subobjects - Icon, title, star rating, add button, and throbber. - ASSERT_EQ(5U, [[item_view subviews] count]); - - // Verify title button is hooked up properly - ASSERT_TRUE([[[item_view subviews] objectAtIndex:1] - isKindOfClass:[NSButton class]]); - NSButton* title_button = [[item_view subviews] objectAtIndex:1]; - CheckButton(title_button, @selector(openExtensionLink:)); - - // Verify "Add to Chromium" button is hooked up properly - ASSERT_TRUE([[[item_view subviews] objectAtIndex:3] - isKindOfClass:[NSButton class]]); - NSButton* add_button = [[item_view subviews] objectAtIndex:3]; - CheckButton(add_button, @selector(installExtension:)); - - // Verify we have a throbber. - ASSERT_TRUE([[[item_view subviews] objectAtIndex:4] - isKindOfClass:[NSProgressIndicator class]]); + NSView* item_view = [[intent_view subviews] objectAtIndex:0]; + CheckSuggestionView(item_view, 0); +} + +TEST_F(WebIntentPickerSheetControllerTest, MixedIntentView) { + WebIntentPickerModel model; + std::vector<WebIntentPickerModel::SuggestedExtension> suggestions; + suggestions.push_back(WebIntentPickerModel::SuggestedExtension( + string16(), string16(), 2.5)); + model.AddSuggestedExtensions(suggestions); + model.AddInstalledService(string16(), GURL("http://example.org/intent.html"), + webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW); + model.AddInstalledService(string16(), GURL("http://example.com/intent.html"), + webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW); + + [controller_ performLayoutWithModel:&model]; + + CheckWindow(/*row_count=*/3); + + NSArray* flip_views = [[window_ contentView] subviews]; + NSArray* main_views = [[flip_views objectAtIndex:0] subviews]; + + // 2nd object should be the suggestion view, 3rd one is close button. + ASSERT_TRUE([main_views count] > 2); + ASSERT_TRUE([[main_views objectAtIndex:1] isKindOfClass:[NSView class]]); + NSView* intent_view = [main_views objectAtIndex:1]; + + // 3 subviews - 2 installed services, 1 suggestion. + ASSERT_EQ(3U, [[intent_view subviews] count]); + NSView* item_view = [[intent_view subviews] objectAtIndex:0]; + CheckSuggestionView(item_view, 0); + item_view = [[intent_view subviews] objectAtIndex:1]; + CheckServiceView(item_view, 1); + item_view = [[intent_view subviews] objectAtIndex:2]; + CheckServiceView(item_view, 0); } TEST_F(WebIntentPickerSheetControllerTest, EmptyView) { |