Chromium Code Reviews| Index: chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller.mm |
| diff --git a/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller.mm b/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..276f724425bb9a8ccf5689903b80a9cd51b8d9c1 |
| --- /dev/null |
| +++ b/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller.mm |
| @@ -0,0 +1,290 @@ |
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| +// 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/media_picker/desktop_media_picker_controller.h" |
| + |
| +#include "base/bind.h" |
| +#import "base/mac/bundle_locations.h" |
| +#include "base/strings/sys_string_conversions.h" |
| +#import "chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "grit/generated_resources.h" |
| +#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
| +#import "ui/base/cocoa/flipped_view.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| +#include "ui/gfx/image/image_skia_util_mac.h" |
| + |
| +namespace { |
| + |
| +const int kInitialContentWidth = 620; |
| +const int kMinimumContentWidth = 500; |
| +const int kMinimumContentHeight = 390; |
| +const int kThumbnailWidth = 150; |
| +const int kThumbnailHeight = 150; |
| +const int kFramePadding = 20; |
| +const int kControlSpacing = 10; |
| + |
| +} // namespace |
| + |
| +@interface DesktopMediaPickerController (Private) |
| + |
| +// Populate the window with controls and views. |
| +- (void)initializeContentsWithAppName:(const string16&)appName; |
| + |
| +// Create a |NSTextField| with label traits given |width|. Frame height is |
| +// automatically adjusted to fit. |
| +- (NSTextField*)createTextFieldWithText:(NSString*)text |
| + frameWidth:(CGFloat)width; |
| + |
| +// Create a button with |title|, with size adjusted to fit. |
| +- (NSButton*)createButtonWithTitle:(NSString*)title; |
| + |
| +// Report result by invoking |doneCallback_|. The callback is invoked only on |
| +// the first call to |reportResult:|. Subsequent calls will be no-ops. |
| +- (void)reportResult:(content::DesktopMediaID)sourceID; |
| + |
| +// Action handlers |
|
Robert Sesek
2013/09/06 20:08:21
nit: full-stop
dcaiafa
2013/09/06 20:50:51
Done.
|
| +- (void)okPressed:(id)sender; |
| +- (void)cancelPressed:(id)sender; |
| + |
| +@end |
| + |
| +@implementation DesktopMediaPickerController |
| + |
| +- (id)initWithModel:(scoped_ptr<DesktopMediaPickerModel>)model |
| + callback:(const DesktopMediaPicker::DoneCallback&)callback |
| + appName:(const string16&)appName { |
| + // Create the dialog's window using an arbitrary size. The size will be |
| + // adjusted when the contents are initialized. |
| + const NSUInteger kStyleMask = |
| + NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask; |
| + base::scoped_nsobject<NSWindow> window( |
| + [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 1, 1) |
|
Robert Sesek
2013/09/06 20:08:21
Use ui::kWindowSizeDeterminedLater
dcaiafa
2013/09/06 20:50:51
Done.
|
| + styleMask:kStyleMask |
| + backing:NSBackingStoreBuffered |
| + defer:NO]); |
| + |
| + if ((self = [super initWithWindow:window])) { |
| + [window setDelegate:self]; |
| + [self initializeContentsWithAppName:appName]; |
| + model_ = model.Pass(); |
| + doneCallback_ = callback; |
| + items_.reset([[NSMutableArray alloc] init]); |
| + bridge_.reset(new DesktopMediaPickerBridge(self)); |
| + } |
| + return self; |
| +} |
| + |
| +- (void)initializeContentsWithAppName:(const string16&)appName { |
| + // Use flipped coordinates to facilitate manual layout. |
| + const CGFloat kPaddedWidth = kInitialContentWidth - (kFramePadding * 2); |
| + base::scoped_nsobject<FlippedView> content( |
| + [[FlippedView alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)]); |
|
Robert Sesek
2013/09/06 20:08:21
You can just use NSZeroRect for this, and below.
dcaiafa
2013/09/06 20:50:51
Done.
|
| + [[self window] setContentView:content]; |
| + NSPoint origin = NSMakePoint(kFramePadding, kFramePadding); |
| + |
| + // Set the dialog's title. |
| + NSString* titleText = l10n_util::GetNSStringF( |
| + IDS_DESKTOP_MEDIA_PICKER_TITLE, appName); |
| + [[self window] setTitle:titleText]; |
| + |
| + // Set the dialog's description. |
| + NSString* descriptionText = l10n_util::GetNSStringF( |
| + IDS_DESKTOP_MEDIA_PICKER_TEXT, appName); |
| + NSTextField* description = [self createTextFieldWithText:descriptionText |
| + frameWidth:kPaddedWidth]; |
| + [description setFrameOrigin:origin]; |
| + [content addSubview:description]; |
| + origin.y += NSHeight([description frame]) + kControlSpacing; |
| + |
| + // Create the image browser. |
| + base::scoped_nsobject<IKImageBrowserView> imageBrowser( |
| + [[NSClassFromString(@"IKImageBrowserView") alloc] |
|
Robert Sesek
2013/09/06 20:08:21
At this point, it may make sense to pull in Quartz
|
| + initWithFrame:NSMakeRect(0, 0, 1, 1)]); |
| + NSUInteger cellStyle = IKCellsStyleShadowed | IKCellsStyleTitled; |
| + [imageBrowser setDelegate:self]; |
| + [imageBrowser setDataSource:self]; |
| + [imageBrowser setCellsStyleMask:cellStyle]; |
| + [imageBrowser setCellSize:NSMakeSize(kThumbnailWidth, kThumbnailHeight)]; |
| + sourceBrowser_ = imageBrowser; |
| + |
| + // Create a scroll view to host the image browser. |
| + NSRect imageBrowserScrollFrame = NSMakeRect( |
| + origin.x, origin.y, kPaddedWidth, 350); |
| + base::scoped_nsobject<NSScrollView> imageBrowserScroll( |
| + [[NSScrollView alloc] initWithFrame:imageBrowserScrollFrame]); |
| + [imageBrowserScroll setHasVerticalScroller:YES]; |
| + [imageBrowserScroll setDocumentView:imageBrowser]; |
| + [imageBrowserScroll setBorderType:NSBezelBorder]; |
| + [imageBrowserScroll setAutoresizingMask: |
| + NSViewWidthSizable | NSViewHeightSizable]; |
| + [content addSubview:imageBrowserScroll]; |
| + origin.y += NSHeight(imageBrowserScrollFrame) + kControlSpacing; |
| + |
| + // Create the cancel button. |
| + NSString* cancelTitle = l10n_util::GetNSString(IDS_CANCEL); |
|
Robert Sesek
2013/09/06 20:08:21
This and |okTitle| can just be inlined into the me
dcaiafa
2013/09/06 20:50:51
Done.
|
| + cancelButton_ = [self createButtonWithTitle:cancelTitle]; |
| + origin.x = kPaddedWidth - NSWidth([cancelButton_ frame]); |
| + [cancelButton_ setFrameOrigin:origin]; |
| + [cancelButton_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; |
| + [cancelButton_ setTarget:self]; |
| + [cancelButton_ setAction:@selector(cancelPressed:)]; |
| + [content addSubview:cancelButton_]; |
| + |
| + // Create the OK button. |
| + NSString* okTitle = l10n_util::GetNSString(IDS_OK); |
| + okButton_ = [self createButtonWithTitle:okTitle]; |
| + origin.x -= kControlSpacing + NSWidth([okButton_ frame]); |
| + [okButton_ setEnabled:NO]; |
| + [okButton_ setFrameOrigin:origin]; |
| + [okButton_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; |
| + [okButton_ setTarget:self]; |
| + [okButton_ setAction:@selector(okPressed:)]; |
| + [content addSubview:okButton_]; |
| + origin.y += NSHeight([okButton_ frame]) + kFramePadding; |
| + |
| + // Resize window to fit. |
| + [[[self window] contentView] setAutoresizesSubviews:NO]; |
| + [[self window] setContentSize:NSMakeSize(kInitialContentWidth, origin.y)]; |
| + [[self window] setContentMinSize: |
| + NSMakeSize(kMinimumContentWidth, kMinimumContentHeight)]; |
| + [[[self window] contentView] setAutoresizesSubviews:YES]; |
| +} |
| + |
| +- (void)showWindow:(id)sender { |
| + // Signal the model to start sending thumbnails. |bridge_| is used as the |
| + // observer, and will forward notifications to this object. |
| + model_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight)); |
| + model_->StartUpdating(bridge_.get()); |
| + |
| + [self.window center]; |
| + [super showWindow:sender]; |
| +} |
| + |
| +- (void)reportResult:(content::DesktopMediaID)sourceID { |
| + if (doneCallback_.is_null()) { |
| + return; |
| + } |
| + |
| + // Notify the |callback_| asynchronously because it may release the |
| + // controller. |
| + content::BrowserThread::PostTask( |
| + content::BrowserThread::UI, FROM_HERE, |
| + base::Bind(doneCallback_, sourceID)); |
| + doneCallback_.Reset(); |
| +} |
| + |
| +- (void)okPressed:(id)sender { |
| + NSIndexSet* indexes = [sourceBrowser_ selectionIndexes]; |
| + NSUInteger selectedIndex = [indexes firstIndex]; |
| + DesktopMediaPickerItem* item = |
| + [items_ objectAtIndex:selectedIndex]; |
| + [self reportResult:[item sourceID]]; |
| + [self close]; |
| +} |
| + |
| +- (void)cancelPressed:(id)sender { |
| + [self reportResult:content::DesktopMediaID()]; |
| + [self close]; |
| +} |
| + |
| +- (NSTextField*)createTextFieldWithText:(NSString*)text |
| + frameWidth:(CGFloat)width { |
| + NSRect frame = NSMakeRect(0, 0, width, 1); |
| + base::scoped_nsobject<NSTextField> textField( |
| + [[NSTextField alloc] initWithFrame:frame]); |
| + [textField setEditable:NO]; |
| + [textField setSelectable:YES]; |
| + [textField setDrawsBackground:NO]; |
| + [textField setBezeled:NO]; |
| + [textField setStringValue:text]; |
| + [textField setFont:[NSFont systemFontOfSize:13]]; |
| + [textField setAutoresizingMask:NSViewWidthSizable]; |
| + [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField: textField]; |
|
Robert Sesek
2013/09/06 20:08:21
nit: no space after :
dcaiafa
2013/09/06 20:50:51
Done.
|
| + return textField.autorelease(); |
| +} |
| + |
| +- (NSButton*)createButtonWithTitle:(NSString*)title { |
| + base::scoped_nsobject<NSButton> button( |
| + [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)]); |
| + [button setButtonType:NSMomentaryPushInButton]; |
| + [button setBezelStyle:NSRoundedBezelStyle]; |
| + [button setTitle:title]; |
| + [GTMUILocalizerAndLayoutTweaker sizeToFitView:button]; |
| + return button.autorelease(); |
| +} |
| + |
| +#pragma mark NSWindowDelegate |
| + |
| +- (void)windowWillClose:(NSNotification*)notification { |
| + // Report the result if it hasn't been reported yet. |reportResult:| ensures |
| + // that the result is only reported once. |
| + [self reportResult:content::DesktopMediaID()]; |
| +} |
| + |
| +#pragma mark IKImageBrowserDataSource |
| + |
| +- (NSUInteger)numberOfItemsInImageBrowser:(IKImageBrowserView*)browser { |
| + return [items_ count]; |
| +} |
| + |
| +- (id)imageBrowser:(IKImageBrowserView *)browser |
| + itemAtIndex:(NSUInteger)index { |
| + return [items_ objectAtIndex:index]; |
| +} |
| + |
| +#pragma mark IKImageBrowserDelegate |
| + |
| +- (void)imageBrowser:(IKImageBrowserView *)browser |
| + cellWasDoubleClickedAtIndex:(NSUInteger)index { |
| + DesktopMediaPickerItem* item = [items_ objectAtIndex:index]; |
| + [self reportResult:[item sourceID]]; |
| + [self close]; |
| +} |
| + |
| +- (void)imageBrowserSelectionDidChange:(IKImageBrowserView*) aBrowser { |
| + // Enable or disable the OK button based on whether we have a selection. |
| + [okButton_ setEnabled:([[sourceBrowser_ selectionIndexes] count] > 0)]; |
| +} |
| + |
| +#pragma mark DesktopMediaPickerObserver |
| + |
| +- (void)sourceAddedAtIndex:(int)index { |
| + const DesktopMediaPickerModel::Source& source = model_->source(index); |
| + NSString* imageTitle = base::SysUTF16ToNSString(source.name); |
| + base::scoped_nsobject<DesktopMediaPickerItem> item( |
| + [[DesktopMediaPickerItem alloc] initWithSourceId:source.id |
| + imageUID:++lastImageUID_ |
| + imageTitle:imageTitle]); |
| + [items_ insertObject:item atIndex:index]; |
| + [sourceBrowser_ reloadData]; |
| +} |
| + |
| +- (void)sourceRemovedAtIndex:(int)index { |
| + if ([[sourceBrowser_ selectionIndexes] containsIndex:index]) { |
| + // Selected item was removed. Clear selection. |
| + [sourceBrowser_ setSelectionIndexes:[NSIndexSet indexSet] |
| + byExtendingSelection:FALSE]; |
| + } |
| + [items_ removeObjectAtIndex:index]; |
| + [sourceBrowser_ reloadData]; |
| +} |
| + |
| +- (void)sourceNameChangedAtIndex:(int)index { |
| + DesktopMediaPickerItem* item = [items_ objectAtIndex:index]; |
| + const DesktopMediaPickerModel::Source& source = model_->source(index); |
| + [item setImageTitle:base::SysUTF16ToNSString(source.name)]; |
| + [sourceBrowser_ reloadData]; |
| +} |
| + |
| +- (void)sourceThumbnailChangedAtIndex:(int)index { |
| + const DesktopMediaPickerModel::Source& source = model_->source(index); |
| + NSImage* image = gfx::NSImageFromImageSkia(source.thumbnail); |
| + |
| + DesktopMediaPickerItem* item = [items_ objectAtIndex:index]; |
| + [item setImageRepresentation:image]; |
| + [sourceBrowser_ reloadData]; |
| +} |
| + |
| +@end // @interface DesktopMediaPickerController |