summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorgroby@chromium.org <groby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-20 21:39:25 +0000
committergroby@chromium.org <groby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-20 21:39:25 +0000
commit89457c907830db941455f4838ce4a6a3e3a968a7 (patch)
treec96ac5bbbc7efd72a6cf062240e106e73820202a /chrome
parent7242ab5738122da6cc21aa469a418de9bce00b48 (diff)
downloadchromium_src-89457c907830db941455f4838ce4a6a3e3a968a7.zip
chromium_src-89457c907830db941455f4838ce4a6a3e3a968a7.tar.gz
chromium_src-89457c907830db941455f4838ce4a6a3e3a968a7.tar.bz2
[WebIntents] intent suggestions for OSX
BUG=113476 TEST=none Review URL: http://codereview.chromium.org/9700083 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@127796 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/ui/cocoa/web_intent_picker_cocoa.mm3
-rw-r--r--chrome/browser/ui/cocoa/web_intent_sheet_controller.h3
-rw-r--r--chrome/browser/ui/cocoa/web_intent_sheet_controller.mm201
-rw-r--r--chrome/browser/ui/cocoa/web_intent_sheet_controller_unittest.mm38
4 files changed, 230 insertions, 15 deletions
diff --git a/chrome/browser/ui/cocoa/web_intent_picker_cocoa.mm b/chrome/browser/ui/cocoa/web_intent_picker_cocoa.mm
index d7a6cbd..a92862c 100644
--- a/chrome/browser/ui/cocoa/web_intent_picker_cocoa.mm
+++ b/chrome/browser/ui/cocoa/web_intent_picker_cocoa.mm
@@ -149,7 +149,8 @@ void WebIntentPickerCocoa::OnFaviconChanged(WebIntentPickerModel* model,
void WebIntentPickerCocoa::OnExtensionIconChanged(
WebIntentPickerModel* model,
const string16& extension_id) {
- // TODO(binji): implement.
+ // We don't handle individual icon changes - just redo the whole model.
+ PerformLayout();
}
void WebIntentPickerCocoa::OnInlineDisposition(WebIntentPickerModel* model,
diff --git a/chrome/browser/ui/cocoa/web_intent_sheet_controller.h b/chrome/browser/ui/cocoa/web_intent_sheet_controller.h
index 5820f08..04678f5 100644
--- a/chrome/browser/ui/cocoa/web_intent_sheet_controller.h
+++ b/chrome/browser/ui/cocoa/web_intent_sheet_controller.h
@@ -25,6 +25,9 @@ class WebIntentPickerModel;
// Inline disposition tab contents. Weak reference.
TabContentsWrapper* contents_;
+
+ // The intent picker data to be rendered. Weak reference.
+ WebIntentPickerModel* model_;
}
// Initialize the constrained dialog, and connect to picker.
diff --git a/chrome/browser/ui/cocoa/web_intent_sheet_controller.mm b/chrome/browser/ui/cocoa/web_intent_sheet_controller.mm
index 29547b4..bb66962 100644
--- a/chrome/browser/ui/cocoa/web_intent_sheet_controller.mm
+++ b/chrome/browser/ui/cocoa/web_intent_sheet_controller.mm
@@ -6,6 +6,7 @@
#include "base/memory/scoped_nsobject.h"
#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
#include "chrome/browser/ui/browser_list.h"
#import "chrome/browser/ui/cocoa/hover_close_button.h"
#import "chrome/browser/ui/cocoa/hyperlink_button_cell.h"
@@ -18,6 +19,7 @@
#include "content/public/browser/web_contents_view.h"
#include "content/public/browser/web_contents.h"
#include "grit/generated_resources.h"
+#include "grit/google_chrome_strings.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
#include "grit/ui_resources.h"
@@ -34,7 +36,7 @@ namespace {
// The width of the window, in view coordinates. The height will be
// determined by the content.
-const CGFloat kWindowWidth = 380;
+const CGFloat kWindowWidth = 400;
// The width of a service button, in view coordinates.
const CGFloat kServiceButtonWidth = 300;
@@ -144,6 +146,24 @@ const CGFloat kTextWidth = kWindowWidth - (kImageSize + kImageSpacing +
picker_->OnServiceChosen([sender tag]);
}
+- (IBAction)openExtensionLink:(id)sender {
+ const WebIntentPickerModel::SuggestedExtension& extension =
+ model_->GetSuggestedExtensionAt([sender tag]);
+
+ GURL extension_url(extension_urls::GetWebstoreItemDetailURLPrefix() +
+ UTF16ToUTF8(extension.id));
+ Browser* browser = BrowserList::GetLastActive();
+ browser::NavigateParams params(browser,
+ extension_url,
+ content::PAGE_TRANSITION_AUTO_BOOKMARK);
+ params.disposition = NEW_FOREGROUND_TAB;
+ browser::Navigate(&params);
+}
+
+- (IBAction)installExtension:(id)sender {
+ // TODO(groby):one-click install
+}
+
// Sets proprties on the given |field| to act as the title or description labels
// in the bubble.
- (void)configureTextFieldAsLabel:(NSTextField*)field {
@@ -153,20 +173,140 @@ const CGFloat kTextWidth = kWindowWidth - (kImageSize + kImageSpacing +
[field setBezeled:NO];
}
+- (CGFloat)addStackedView:(NSView*)view
+ toSubviews:(NSMutableArray*)subviews
+ atOffset:(CGFloat)offset {
+ if (view == nil)
+ return 0.0;
+
+ NSPoint frameOrigin = [view frame].origin;
+ frameOrigin.y = offset;
+ [view setFrameOrigin:frameOrigin];
+ [subviews addObject:view];
+
+ return NSHeight([view frame]);
+}
+
+- (NSButton*)newHyperlinkButton:(NSString*)title withFrame:(NSRect)frame {
+ NSButton* button = [[NSButton alloc] initWithFrame:frame];
+ scoped_nsobject<HyperlinkButtonCell> cell(
+ [[HyperlinkButtonCell alloc] initTextCell:title]);
+ [cell setControlSize:NSSmallControlSize];
+ [button setCell:cell.get()];
+ [button setButtonType:NSMomentaryPushInButton];
+ [button setBezelStyle:NSRegularSquareBezelStyle];
+
+ return button;
+}
+
+- (NSView*)newViewForSuggestedExtension:
+ (const WebIntentPickerModel::SuggestedExtension*)extension
+ withIndex:(size_t)index {
+ const CGFloat kMaxHeight = 34.0;
+ const CGFloat kTitleX = 20.0;
+ const CGFloat kTitleWidth = 120.0;
+ const CGFloat kStarX = 150.0;
+ const CGFloat kMinAddButtonHeight = 24.0;
+ const CGFloat kAddButtonX = 245;
+ const CGFloat kAddButtonWidth = 128.0;
+
+ NSMutableArray* subviews = [NSMutableArray array];
+
+ // Add the extension icon.
+ NSImage* icon = extension->icon.ToNSImage();
+ NSRect imageFrame = NSZeroRect;
+
+ scoped_nsobject<NSImageView> imageView(
+ [[NSImageView alloc] initWithFrame:imageFrame]);
+ [imageView setImage:icon];
+ [imageView setImageFrameStyle:NSImageFrameNone];
+
+ imageFrame.size = [icon size];
+ imageFrame.size.height = std::min(NSHeight(imageFrame), kMaxHeight);
+ imageFrame.origin.y += (kMaxHeight - NSHeight(imageFrame)) / 2.0;
+ [imageView setFrame:imageFrame];
+ [subviews addObject:imageView.get()];
+
+ // Add the extension title.
+ NSRect frame = NSMakeRect(kTitleX, 0, kTitleWidth, 0.0);
+
+ NSString* string = base::SysUTF16ToNSString(extension->title);
+ scoped_nsobject<NSButton> button(
+ [self newHyperlinkButton:string withFrame:frame]);
+ [button setTarget:self];
+ [button setAction:@selector(openExtensionLink:)];
+ [button setTag:index];
+ frame.size.height = std::min([[button cell] cellSize].height, kMaxHeight);
+ frame.origin.y += (kMaxHeight - NSHeight(frame)) / 2.0;
+ [button setFrame:frame];
+ [subviews addObject:button.get()];
+
+ // Add the star rating
+ [self addStarWidgetWithRating:extension->average_rating
+ toSubviews:subviews
+ atPoint:NSMakePoint(kStarX,0)];
+
+ // Add an "add to chromium" button.
+ frame = NSMakeRect(kAddButtonX, 0, kAddButtonWidth, 0);
+ scoped_nsobject<NSButton> addButton([[NSButton alloc] initWithFrame:frame]);
+ [addButton setAlignment:NSLeftTextAlignment];
+ [addButton setButtonType:NSMomentaryPushInButton];
+ [addButton setBezelStyle:NSRegularSquareBezelStyle];
+ string = l10n_util::GetNSStringWithFixup(IDS_INTENT_PICKER_INSTALL_EXTENSION);
+ [addButton setTitle:string];
+ frame.size.height = std::min(kMinAddButtonHeight, kMaxHeight);
+ frame.origin.y += (kMaxHeight - NSHeight(frame)) / 2.0;
+ [addButton setFrame:frame];
+
+ [addButton setTarget:self];
+ [addButton setAction:@selector(invokeService:)];
+ [addButton setTag:index];
+ [subviews addObject:addButton.get()];
+
+ // Build the main view
+ NSRect contentFrame = NSMakeRect(0, 0, kWindowWidth, kMaxHeight);
+ NSView* view = [[NSView alloc] initWithFrame:contentFrame];
+
+ [view setSubviews: subviews];
+ return view;
+}
+
+- (NSView*)newViewForSuggestions:(WebIntentPickerModel*)model {
+ const CGFloat kYMargin = 16.0;
+ size_t count = model->GetSuggestedExtensionCount();
+ if (count == 0)
+ return nil;
+
+ NSMutableArray* subviews = [NSMutableArray array];
+
+ CGFloat offset = kYMargin;
+ for (size_t i = count; i > 0; --i) {
+ const WebIntentPickerModel::SuggestedExtension& ext =
+ model->GetSuggestedExtensionAt(i - 1);
+ scoped_nsobject<NSView> suggestView(
+ [self newViewForSuggestedExtension:&ext withIndex:i - 1]);
+ offset += [self addStackedView:suggestView.get()
+ toSubviews:subviews
+ atOffset:offset];
+ }
+ offset += kYMargin;
+
+ NSRect contentFrame = NSMakeRect(kFramePadding, 0, kWindowWidth, offset);
+ NSView* view = [[NSView alloc] initWithFrame:contentFrame];
+ [view setSubviews: subviews];
+
+ return view;
+}
+
// Adds a link to the Chrome Web Store, to obtain further intent handlers.
// Returns the y position delta for the next offset.
- (CGFloat)addCwsButtonToSubviews:(NSMutableArray*)subviews
atOffset:(CGFloat)offset {
NSRect frame = NSMakeRect(kFramePadding, offset, 100, 10);
- scoped_nsobject<NSButton> button([[NSButton alloc] initWithFrame:frame]);
NSString* string =
l10n_util::GetNSStringWithFixup(IDS_FIND_MORE_INTENT_HANDLER_MESSAGE);
- scoped_nsobject<HyperlinkButtonCell> cell(
- [[HyperlinkButtonCell alloc] initTextCell:string]);
- [cell setControlSize:NSSmallControlSize];
- [button setCell:cell.get()];
- [button setButtonType:NSMomentaryPushInButton];
- [button setBezelStyle:NSRegularSquareBezelStyle];
+ scoped_nsobject<NSButton> button(
+ [self newHyperlinkButton:string withFrame:frame]);
[button setTarget:self];
[button setAction:@selector(showChromeWebStore:)];
[subviews addObject:button.get()];
@@ -204,18 +344,19 @@ const CGFloat kTextWidth = kWindowWidth - (kImageSize + kImageSpacing +
[GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:
textField];
- NSRect buttonFrame = NSMakeRect(kFramePadding+kImageSize+kTextWidth,
- offset, kCloseButtonSize, kCloseButtonSize);
+ NSRect buttonFrame = NSMakeRect(
+ kFramePadding + kImageSize + kTextWidth, offset,
+ kCloseButtonSize, kCloseButtonSize);
scoped_nsobject<HoverCloseButton> closeButton(
[[HoverCloseButton alloc] initWithFrame:buttonFrame]);
[closeButton setTarget:self];
[closeButton setAction:@selector(cancelSelection:)];
// Adjust view height to fit elements, center-align elements.
- CGFloat maxHeight = std::max(buttonFrame.size.height,
- std::max(imageFrame.size.height,textFrame.size.height));
- textFrame.origin.y += (maxHeight - textFrame.size.height) / 2;
- imageFrame.origin.y += (maxHeight - imageFrame.size.height) / 2;
+ CGFloat maxHeight = std::max(NSHeight(buttonFrame),
+ std::max(NSHeight(imageFrame), NSHeight(textFrame)));
+ textFrame.origin.y += (maxHeight - NSHeight(textFrame)) / 2;
+ imageFrame.origin.y += (maxHeight - NSHeight(imageFrame)) / 2;
[textField setFrame:textFrame];
[imageView setFrame:imageFrame];
@@ -278,8 +419,34 @@ const CGFloat kTextWidth = kWindowWidth - (kImageSize + kImageSpacing +
return NSHeight([button frame]);
}
+- (NSPoint)addStarWidgetWithRating:(CGFloat)rating
+ toSubviews:(NSMutableArray*)subviews
+ atPoint:(NSPoint)position {
+ const int kStarSpacing = 1; // Spacing between stars in pixels.
+ const CGFloat kStarSize = 16.0; // Size of the star in pixels.
+
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ NSRect imageFrame = NSMakeRect(position.x, position.y, kStarSize, kStarSize);
+
+ for (int i = 0; i < 5; ++i) {
+ NSImage* nsImage = rb.GetNativeImageNamed(
+ WebIntentPicker::GetNthStarImageIdFromCWSRating(rating, i));
+
+ scoped_nsobject<NSImageView> imageView(
+ [[NSImageView alloc] initWithFrame:imageFrame]);
+ [imageView setImage:nsImage];
+ [imageView setImageFrameStyle:NSImageFrameNone];
+ [imageView setFrame:imageFrame];
+ [subviews addObject:imageView.get()];
+ imageFrame.origin.x += kStarSize + kStarSpacing;
+ }
+ position.x = imageFrame.origin.x;
+ return position;
+}
+
// Layout the contents of the picker bubble.
- (void)performLayoutWithModel:(WebIntentPickerModel*)model {
+ model_ = model;
// |offset| is the Y position that should be drawn at next.
CGFloat offset = kFramePadding;
@@ -300,6 +467,12 @@ const CGFloat kTextWidth = kWindowWidth - (kImageSize + kImageSpacing +
toSubviews:subviews
atOffset:offset];
}
+
+ scoped_nsobject<NSView> suggestionView(
+ [self newViewForSuggestions:model]);
+ offset += [self addStackedView:suggestionView.get()
+ toSubviews:subviews
+ atOffset:offset];
}
offset += [self addCwsButtonToSubviews:subviews atOffset:offset];
}
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 8e0a13b..c173462 100644
--- a/chrome/browser/ui/cocoa/web_intent_sheet_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/web_intent_sheet_controller_unittest.mm
@@ -193,3 +193,41 @@ TEST_F(WebIntentPickerSheetControllerTest, DontCancelAfterServiceInvokation) {
ignore_result(picker_.release()); // Closing |picker_| will destruct it.
}
+
+TEST_F(WebIntentPickerSheetControllerTest, SuggestionView) {
+ CreateBubble();
+
+ WebIntentPickerModel model;
+
+ model.AddSuggestedExtension(string16(), string16(), 2.5);
+ [controller_ performLayoutWithModel:&model];
+
+ // Get subviews.
+ NSArray* flip_views = [[window_ contentView] subviews];
+ NSArray* main_views = [[flip_views objectAtIndex:0] subviews];
+
+ // 4th object should be the suggestion view.
+ ASSERT_TRUE([main_views count] > 3);
+ ASSERT_TRUE([[main_views objectAtIndex:3] isKindOfClass:[NSView class]]);
+ NSView* suggest_view = [main_views objectAtIndex:3];
+
+ // There is exactly one subview, which contains the suggested item.
+ ASSERT_EQ(1U, [[suggest_view subviews] count]);
+ ASSERT_TRUE([[[suggest_view subviews] objectAtIndex:0]
+ isKindOfClass:[NSView class]]);
+ NSView* item_view = [[suggest_view subviews] objectAtIndex:0];
+
+ // 8 subobject - Icon, title, star rating (5 objects), add button.
+ ASSERT_EQ(8U, [[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:));
+
+ ASSERT_TRUE([[[item_view subviews] objectAtIndex:7]
+ isKindOfClass:[NSButton class]]);
+ NSButton* add_button = [[item_view subviews] objectAtIndex:7];
+ CheckButton(add_button, @selector(invokeService:));
+}