Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(163)

Side by Side Diff: chrome/browser/ui/cocoa/web_intent_bubble_controller.mm

Issue 9310074: Switch to using WebIntentPickerModel, and bring picker closer to mocks. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed unit tests Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "chrome/browser/ui/cocoa/web_intent_bubble_controller.h" 5 #import "chrome/browser/ui/cocoa/web_intent_bubble_controller.h"
6 6
7 #include "base/memory/scoped_nsobject.h" 7 #include "base/memory/scoped_nsobject.h"
8 #include "base/sys_string_conversions.h"
8 #include "chrome/browser/ui/browser_list.h" 9 #include "chrome/browser/ui/browser_list.h"
9 #import "chrome/browser/ui/cocoa/hyperlink_button_cell.h" 10 #import "chrome/browser/ui/cocoa/hyperlink_button_cell.h"
10 #import "chrome/browser/ui/cocoa/info_bubble_view.h" 11 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
11 #import "chrome/browser/ui/cocoa/info_bubble_window.h" 12 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
12 #include "chrome/browser/ui/cocoa/web_intent_picker_cocoa.h" 13 #include "chrome/browser/ui/cocoa/web_intent_picker_cocoa.h"
13 #include "chrome/browser/ui/intents/web_intent_picker_delegate.h" 14 #include "chrome/browser/ui/intents/web_intent_picker_delegate.h"
15 #include "chrome/browser/ui/intents/web_intent_picker_model.h"
14 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 16 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
15 #include "content/public/browser/web_contents_view.h" 17 #include "content/public/browser/web_contents_view.h"
16 #include "content/public/browser/web_contents.h" 18 #include "content/public/browser/web_contents.h"
17 #include "grit/generated_resources.h" 19 #include "grit/generated_resources.h"
18 #include "grit/locale_settings.h" 20 #include "grit/locale_settings.h"
19 #include "grit/theme_resources.h" 21 #include "grit/theme_resources.h"
20 #include "grit/ui_resources.h" 22 #include "grit/ui_resources.h"
21 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" 23 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
22 #include "ui/base/l10n/l10n_util.h" 24 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/l10n/l10n_util_mac.h" 25 #include "ui/base/l10n/l10n_util_mac.h"
24 #include "ui/base/resource/resource_bundle.h" 26 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/gfx/image/image.h" 27 #include "ui/gfx/image/image.h"
26 28
27 using content::OpenURLParams; 29 using content::OpenURLParams;
28 using content::Referrer; 30 using content::Referrer;
29 31
30 namespace { 32 namespace {
31 33
32 // The width of the window, in view coordinates. The height will be 34 // The width of the window, in view coordinates. The height will be
33 // determined by the content. 35 // determined by the content.
34 const CGFloat kWindowWidth = 380; 36 const CGFloat kWindowWidth = 380;
35 37
38 // The width of a service button, in view coordinates.
39 const CGFloat kServiceButtonWidth = 300;
Nico 2012/02/03 23:27:07 Does this localize?
groby-ooo-7-16 2012/02/06 22:30:19 No, but neither do the strings in that UI for now.
40
36 // Padding along on the X-axis between the window frame and content. 41 // Padding along on the X-axis between the window frame and content.
37 const CGFloat kFramePadding = 10; 42 const CGFloat kFramePadding = 10;
38 43
39 // Spacing in between sections. 44 // Spacing in between sections.
40 const CGFloat kVerticalSpacing = 10; 45 const CGFloat kVerticalSpacing = 10;
41 46
42 // Square size of the image. 47 // Square size of the image.
43 const CGFloat kImageSize = 32; 48 const CGFloat kImageSize = 32;
44 49
45 // Spacing between the image and the text. 50 // Spacing between the image and the text.
46 const CGFloat kImageSpacing = 10; 51 const CGFloat kImageSpacing = 10;
47 52
48 // Spacing between icons. 53 // Spacing between icons.
49 const CGFloat kImagePadding = 6; 54 const CGFloat kImagePadding = 6;
50 55
51 // Width of the text fields. 56 // Width of the text fields.
52 const CGFloat kTextWidth = kWindowWidth - (kImageSize + kImageSpacing + 57 const CGFloat kTextWidth = kWindowWidth - (kImageSize + kImageSpacing +
53 kFramePadding * 2); 58 kFramePadding * 2);
54 59
55 } // namespace 60 } // namespace
56 61
62 // This simple NSView subclass is used as the single subview of the page info
63 // bubble's window's contentView. Drawing is flipped so that layout of the
64 // sections is easier. Apple recommends flipping the coordinate origin when
65 // doing a lot of text layout because it's more natural.
66 @interface WebIntentsContentView : NSView
67 @end
68 @implementation WebIntentsContentView
69 - (BOOL)isFlipped {
70 return YES;
71 }
72 @end
73
57 @implementation WebIntentBubbleController; 74 @implementation WebIntentBubbleController;
58 75
59 - (id)initWithPicker:(WebIntentPickerCocoa*)picker 76 - (id)initWithPicker:(WebIntentPickerCocoa*)picker
60 parentWindow:(NSWindow*)parent 77 parentWindow:(NSWindow*)parent
61 anchoredAt:(NSPoint)point { 78 anchoredAt:(NSPoint)point {
62 // Use an arbitrary height because it will reflect the size of the content. 79 // Use an arbitrary height because it will reflect the size of the content.
63 NSRect contentRect = NSMakeRect(0, 0, kWindowWidth, kVerticalSpacing); 80 NSRect contentRect = NSMakeRect(0, 0, kWindowWidth, kVerticalSpacing);
64 // Create an empty window into which content is placed. 81 // Create an empty window into which content is placed.
65 scoped_nsobject<InfoBubbleWindow> window( 82 scoped_nsobject<InfoBubbleWindow> window(
66 [[InfoBubbleWindow alloc] initWithContentRect:contentRect 83 [[InfoBubbleWindow alloc] initWithContentRect:contentRect
67 styleMask:NSBorderlessWindowMask 84 styleMask:NSBorderlessWindowMask
68 backing:NSBackingStoreBuffered 85 backing:NSBackingStoreBuffered
69 defer:NO]); 86 defer:NO]);
70 if ((self = [super initWithWindow:window.get() 87 if ((self = [super initWithWindow:window.get()
71 parentWindow:parent 88 parentWindow:parent
72 anchoredAt:point])) { 89 anchoredAt:point])) {
73 picker_ = picker; 90 picker_ = picker;
74 iconImages_.reset([[NSPointerArray alloc] initWithOptions:
75 NSPointerFunctionsStrongMemory |
76 NSPointerFunctionsObjectPersonality]);
77 91
78 [[self bubble] setArrowLocation:info_bubble::kTopLeft]; 92 [[self bubble] setArrowLocation:info_bubble::kTopLeft];
79 [self performLayout]; 93 [self performLayoutWithModel:NULL];
80 [self showWindow:nil]; 94 [self showWindow:nil];
81 picker_->set_controller(self); 95 picker_->set_controller(self);
82 } 96 }
83 97
84 return self; 98 return self;
85 } 99 }
86 100
87 - (void)setServiceURLs:(NSArray*)urls {
88 serviceURLs_.reset([urls retain]);
89
90 if ([iconImages_ count] < [serviceURLs_ count])
91 [iconImages_ setCount:[serviceURLs_ count]];
92
93 [self performLayout];
94 }
95
96 - (void)setInlineDispositionTabContents:(TabContentsWrapper*)wrapper { 101 - (void)setInlineDispositionTabContents:(TabContentsWrapper*)wrapper {
97 contents_ = wrapper; 102 contents_ = wrapper;
98 [self performLayout];
99 }
100
101 - (void)replaceImageAtIndex:(size_t)index withImage:(NSImage*)image {
102 if ([iconImages_ count] <= index)
103 [iconImages_ setCount:index + 1];
104
105 [iconImages_ replacePointerAtIndex:index withPointer:image];
106 [self performLayout];
107 } 103 }
108 104
109 // We need to watch for window closing so we can notify up via |picker_|. 105 // We need to watch for window closing so we can notify up via |picker_|.
110 - (void)windowWillClose:(NSNotification*)notification { 106 - (void)windowWillClose:(NSNotification*)notification {
111 if (picker_) { 107 if (picker_) {
112 WebIntentPickerCocoa* temp = picker_; 108 WebIntentPickerCocoa* temp = picker_;
113 picker_ = NULL; // Abandon picker, we are done with it. 109 picker_ = NULL; // Abandon picker, we are done with it.
114 temp->OnCancelled(); 110 temp->OnCancelled();
115 } 111 }
116 [super windowWillClose:notification]; 112 [super windowWillClose:notification];
117 } 113 }
118 114
119 // Pop up a new tab with the Chrome Web Store. 115 // Pop up a new tab with the Chrome Web Store.
120 - (IBAction)showChromeWebStore:(id)sender { 116 - (IBAction)showChromeWebStore:(id)sender {
121 GURL url(l10n_util::GetStringUTF8(IDS_WEBSTORE_URL)); 117 GURL url(l10n_util::GetStringUTF8(IDS_WEBSTORE_URL));
122 Browser* browser = BrowserList::GetLastActive(); 118 Browser* browser = BrowserList::GetLastActive();
123 OpenURLParams params( 119 OpenURLParams params(
124 url, Referrer(), NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK, 120 url, Referrer(), NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK,
125 false); 121 false);
126 browser->OpenURL(params); 122 browser->OpenURL(params);
127 } 123 }
128 124
129 // A picker button has been pressed - invoke corresponding service. 125 // A picker button has been pressed - invoke corresponding service.
130 - (IBAction)invokeService:(id)sender { 126 - (IBAction)invokeService:(id)sender {
131 if (picker_) { 127 if (picker_) {
132 WebIntentPickerCocoa* temp = picker_; 128 WebIntentPickerCocoa* temp = picker_;
133 picker_ = NULL; // Abandon picker, we are done with it. 129 picker_ = NULL; // Abandon picker, we are done with it.
134 temp->OnServiceChosen([[sender selectedCell] tag]); 130 temp->OnServiceChosen([sender tag]);
135 } 131 }
136 } 132 }
137 133
138 // Sets proprties on the given |field| to act as the title or description labels 134 // Sets proprties on the given |field| to act as the title or description labels
139 // in the bubble. 135 // in the bubble.
140 - (void)configureTextFieldAsLabel:(NSTextField*)field { 136 - (void)configureTextFieldAsLabel:(NSTextField*)field {
141 [field setEditable:NO]; 137 [field setEditable:NO];
142 [field setSelectable:YES]; 138 [field setSelectable:YES];
143 [field setDrawsBackground:NO]; 139 [field setDrawsBackground:NO];
144 [field setBezeled:NO]; 140 [field setBezeled:NO];
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
202 else 198 else
203 imageFrame.origin.y += maxHeight / 2; 199 imageFrame.origin.y += maxHeight / 2;
204 [textField setFrame:textFrame]; 200 [textField setFrame:textFrame];
205 [imageView setFrame:imageFrame]; 201 [imageView setFrame:imageFrame];
206 202
207 [subviews addObject:textField.get()]; 203 [subviews addObject:textField.get()];
208 [subviews addObject:imageView.get()]; 204 [subviews addObject:imageView.get()];
209 return NSHeight([imageView frame]); 205 return NSHeight([imageView frame]);
210 } 206 }
211 207
212 // Add all service icons to picker UI.
213 // Returns the y position delta for the next offset.
214 - (CGFloat)addIcons:(NSPointerArray*)icons
215 toSubviews:(NSMutableArray*)subviews
216 atOffset:(CGFloat)offset {
217 CGFloat matrixOffset = kFramePadding + kImageSize + kImagePadding;
218 CGFloat matrixWidth = kWindowWidth - matrixOffset - kFramePadding;
219
220 CGFloat iconWidth = kImageSize + kImagePadding;
221 NSInteger iconsPerRow = matrixWidth / iconWidth;
222 NSInteger numRows = ([icons count] / iconsPerRow) + 1;
223
224 NSRect frame = NSMakeRect(matrixOffset, offset, matrixWidth,
225 (kImageSize + kImagePadding) * numRows);
226
227 scoped_nsobject<NSMatrix> matrix(
228 [[NSMatrix alloc] initWithFrame:frame
229 mode:NSHighlightModeMatrix
230 cellClass:[NSImageCell class]
231 numberOfRows:numRows
232 numberOfColumns:iconsPerRow]);
233
234 [matrix setCellSize:NSMakeSize(kImageSize,kImageSize)];
235 [matrix setIntercellSpacing:NSMakeSize(kImagePadding,kImagePadding)];
236
237 for (NSUInteger i = 0; i < [iconImages_ count]; ++i) {
238 scoped_nsobject<NSButtonCell> cell([[NSButtonCell alloc] init]);
239 NSImage* image = static_cast<NSImage*>([iconImages_ pointerAtIndex:i]);
240 if (!image)
241 continue;
242
243 // Set cell styles so it acts as image button.
244 [cell setBordered:NO];
245 [cell setImage:image];
246 [cell setImagePosition:NSImageOnly];
247 [cell setButtonType:NSMomentaryChangeButton];
248 [cell setTarget:self];
249 [cell setAction:@selector(invokeService:)];
250 [cell setTag:i];
251 [cell setEnabled:YES];
252
253 [matrix putCell:cell atRow:(i / iconsPerRow) column:(i % iconsPerRow)];
254 if (serviceURLs_ && i < [serviceURLs_ count])
255 [matrix setToolTip:[serviceURLs_ objectAtIndex:i] forCell:cell];
256 }
257
258 [subviews addObject:matrix];
259 return NSHeight([matrix frame]);
260 }
261
262 - (CGFloat)addInlineHtmlToSubviews:(NSMutableArray*)subviews 208 - (CGFloat)addInlineHtmlToSubviews:(NSMutableArray*)subviews
263 atOffset:(CGFloat)offset { 209 atOffset:(CGFloat)offset {
264 if (!contents_) 210 if (!contents_)
265 return 0; 211 return 0;
266 212
267 // Determine a good size for the inline disposition window. 213 // Determine a good size for the inline disposition window.
268 gfx::Size tab_size = contents_->web_contents()->GetView()->GetContainerSize(); 214 gfx::Size tab_size = contents_->web_contents()->GetView()->GetContainerSize();
269 CGFloat width = std::max(CGFloat(tab_size.width()/2.0), kWindowWidth); 215 CGFloat width = std::max(CGFloat(tab_size.width()/2.0), kWindowWidth);
270 CGFloat height = std::max(CGFloat(tab_size.height()/2.0), kWindowWidth); 216 CGFloat height = std::max(CGFloat(tab_size.height()/2.0), kWindowWidth);
271 NSRect frame = NSMakeRect(kFramePadding, offset, width, height); 217 NSRect frame = NSMakeRect(kFramePadding, offset, width, height);
272 218
273 [contents_->web_contents()->GetNativeView() setFrame:frame]; 219 [contents_->web_contents()->GetNativeView() setFrame:frame];
274 [subviews addObject:contents_->web_contents()->GetNativeView()]; 220 [subviews addObject:contents_->web_contents()->GetNativeView()];
275 221
276 return NSHeight(frame); 222 return NSHeight(frame);
277 } 223 }
278 224
225 // Add a single button for a specific service
226 -(CGFloat)addServiceButton:(NSString*)title
227 withImage:(NSImage*)image
228 index:(NSUInteger)index
229 toSubviews:(NSMutableArray*)subviews
230 atOffset:(CGFloat)offset {
Nico 2012/02/03 23:27:07 yOffset:? Makes this sound less like an index too.
groby-ooo-7-16 2012/02/06 22:30:19 It would be inconsistent with all other bubble con
231 NSRect frame = NSMakeRect(kFramePadding, offset, kServiceButtonWidth, 45);
232 scoped_nsobject<NSButton> button([[NSButton alloc] initWithFrame:frame]);
233
234 if (image) {
235 [button setImage:image];
236 [button setImagePosition:NSImageLeft];
237 }
238 [button setButtonType:NSMomentaryPushInButton];
239 [button setBezelStyle:NSRegularSquareBezelStyle];
240 [button setTarget:self];
241 [button setTitle:title];
242 [button setTag:index];
243 [button setAction:@selector(invokeService:)];
244 [subviews addObject:button.get()];
245
246 // Call size-to-fit to fixup size.
247 [GTMUILocalizerAndLayoutTweaker sizeToFitView:button.get()];
248
249 // But make sure we're limited to a fixed size.
250 frame = [button frame];
251 frame.size.width = kServiceButtonWidth;
252 [button setFrame:frame];
253
254 return NSHeight([button frame]);
255 }
256
279 // Layout the contents of the picker bubble. 257 // Layout the contents of the picker bubble.
280 - (void)performLayout { 258 - (void)performLayoutWithModel:(WebIntentPickerModel*)model {
281 // |offset| is the Y position that should be drawn at next. 259 // |offset| is the Y position that should be drawn at next.
282 CGFloat offset = kFramePadding + info_bubble::kBubbleArrowHeight; 260 CGFloat offset = kFramePadding + info_bubble::kBubbleArrowHeight;
283 261
284 // Keep the new subviews in an array that gets replaced at the end. 262 // Keep the new subviews in an array that gets replaced at the end.
285 NSMutableArray* subviews = [NSMutableArray array]; 263 NSMutableArray* subviews = [NSMutableArray array];
286 264
287 if (contents_) { 265 if (contents_) {
288 offset += [self addInlineHtmlToSubviews:subviews atOffset:offset]; 266 offset += [self addInlineHtmlToSubviews:subviews atOffset:offset];
289 } else { 267 } else {
268 offset += [self addHeaderToSubviews:subviews atOffset:offset];
269 if (model) {
270 for (NSUInteger i = 0; i < model->GetItemCount(); ++i) {
271 const WebIntentPickerModel::Item& item = model->GetItemAt(i);
272 offset += [self addServiceButton:base::SysUTF16ToNSString(item.title)
273 withImage:item.favicon.ToNSImage()
274 index:i
275 toSubviews:subviews
276 atOffset:offset];
277 }
278 }
290 offset += [self addCwsButtonToSubviews:subviews atOffset:offset]; 279 offset += [self addCwsButtonToSubviews:subviews atOffset:offset];
291 offset += [self addIcons:iconImages_ toSubviews:subviews atOffset:offset];
292 offset += [self addHeaderToSubviews:subviews atOffset:offset];
293 } 280 }
294 281
295 // Add the bottom padding. 282 // Add the bottom padding.
296 offset += kVerticalSpacing; 283 offset += kVerticalSpacing;
297 284
285 // Create the dummy view that uses flipped coordinates.
Nico 2012/02/03 23:27:07 nit: add 1 indent to comment
groby-ooo-7-16 2012/02/06 22:30:19 Done.
286 NSRect contentFrame = NSMakeRect(0, 0, kWindowWidth, offset);
287 scoped_nsobject<WebIntentsContentView> contentView(
288 [[WebIntentsContentView alloc] initWithFrame:contentFrame]);
289 [contentView setSubviews:subviews];
290 [contentView setAutoresizingMask:NSViewMinYMargin];
291
298 // Adjust frame to fit all elements. 292 // Adjust frame to fit all elements.
299 NSRect windowFrame = NSMakeRect(0, 0, kWindowWidth, offset); 293 NSRect windowFrame = NSMakeRect(0, 0, kWindowWidth, offset);
300 windowFrame.size = [[[self window] contentView] convertSize:windowFrame.size 294 windowFrame.size = [[[self window] contentView] convertSize:windowFrame.size
301 toView:nil]; 295 toView:nil];
302 // Adjust the origin by the difference in height. 296 // Adjust the origin by the difference in height.
303 windowFrame.origin = [[self window] frame].origin; 297 windowFrame.origin = [[self window] frame].origin;
304 windowFrame.origin.y -= NSHeight(windowFrame) - 298 windowFrame.origin.y -= NSHeight(windowFrame) -
305 NSHeight([[self window] frame]); 299 NSHeight([[self window] frame]);
306 300
307 [[self window] setFrame:windowFrame display:YES animate:YES]; 301 [[self window] setFrame:windowFrame display:YES animate:YES];
308 302
309 // Replace the window's content. 303 // Replace the window's content.
310 [[[self window] contentView] setSubviews:subviews]; 304 [[[self window] contentView] setSubviews:
305 [NSArray arrayWithObject:contentView]];
311 } 306 }
312 307
313 @end // WebIntentBubbleController 308 @end // WebIntentBubbleController
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698