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 |