1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/cocoa/screen_capture_notification_ui_cocoa.h" 6 7#import <Cocoa/Cocoa.h> 8 9#include "base/compiler_specific.h" 10#include "base/i18n/rtl.h" 11#include "base/mac/mac_util.h" 12#include "base/mac/scoped_nsobject.h" 13#include "base/strings/string_util.h" 14#include "base/strings/sys_string_conversions.h" 15#include "chrome/grit/generated_resources.h" 16#include "grit/theme_resources.h" 17#include "skia/ext/skia_utils_mac.h" 18#import "ui/base/cocoa/controls/blue_label_button.h" 19#import "ui/base/cocoa/controls/hyperlink_button_cell.h" 20#include "ui/base/cocoa/window_size_constants.h" 21#include "ui/base/l10n/l10n_util.h" 22#include "ui/base/resource/resource_bundle.h" 23#include "ui/gfx/font_list.h" 24#include "ui/gfx/image/image_skia.h" 25#include "ui/gfx/image/image_skia_util_mac.h" 26#include "ui/gfx/text_elider.h" 27#include "ui/native_theme/native_theme.h" 28 29const CGFloat kMinimumWidth = 460; 30const CGFloat kMaximumWidth = 1000; 31const CGFloat kHorizontalMargin = 10; 32const CGFloat kPaddingVertical = 5; 33const CGFloat kPaddingHorizontal = 10; 34const CGFloat kWindowCornerRadius = 2; 35const CGFloat kWindowAlphaValue = 0.85; 36 37@interface ScreenCaptureNotificationController() 38- (void)hide; 39- (void)populateWithText:(const base::string16&)text; 40@end 41 42@interface ScreenCaptureNotificationView : NSView 43@end 44 45@interface WindowGripView : NSImageView 46- (WindowGripView*)init; 47@end 48 49 50ScreenCaptureNotificationUICocoa::ScreenCaptureNotificationUICocoa( 51 const base::string16& text) 52 : text_(text) { 53} 54 55ScreenCaptureNotificationUICocoa::~ScreenCaptureNotificationUICocoa() {} 56 57gfx::NativeViewId ScreenCaptureNotificationUICocoa::OnStarted( 58 const base::Closure& stop_callback) { 59 DCHECK(!stop_callback.is_null()); 60 DCHECK(!windowController_); 61 62 windowController_.reset([[ScreenCaptureNotificationController alloc] 63 initWithCallback:stop_callback 64 text:text_]); 65 [windowController_ showWindow:nil]; 66 return [[windowController_ window] windowNumber]; 67} 68 69scoped_ptr<ScreenCaptureNotificationUI> ScreenCaptureNotificationUI::Create( 70 const base::string16& text) { 71 return scoped_ptr<ScreenCaptureNotificationUI>( 72 new ScreenCaptureNotificationUICocoa(text)); 73} 74 75@implementation ScreenCaptureNotificationController 76- (id)initWithCallback:(const base::Closure&)stop_callback 77 text:(const base::string16&)text { 78 base::scoped_nsobject<NSWindow> window( 79 [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater 80 styleMask:NSBorderlessWindowMask 81 backing:NSBackingStoreBuffered 82 defer:NO]); 83 [window setReleasedWhenClosed:NO]; 84 [window setAlphaValue:kWindowAlphaValue]; 85 [window setBackgroundColor:[NSColor clearColor]]; 86 [window setOpaque:NO]; 87 [window setHasShadow:YES]; 88 [window setLevel:NSStatusWindowLevel]; 89 [window setMovableByWindowBackground:YES]; 90 [window setDelegate:self]; 91 92 self = [super initWithWindow:window]; 93 if (self) { 94 stop_callback_ = stop_callback; 95 [self populateWithText:text]; 96 97 // Center the window at the bottom of the screen, above the dock (if 98 // present). 99 NSRect desktopRect = [[NSScreen mainScreen] visibleFrame]; 100 NSRect contentRect = [[window contentView] frame]; 101 NSRect windowRect = 102 NSMakeRect((NSWidth(desktopRect) - NSWidth(contentRect)) / 2, 103 NSMinY(desktopRect), 104 NSWidth(contentRect), 105 NSHeight(contentRect)); 106 [window setFrame:windowRect display:YES]; 107 } 108 return self; 109} 110 111- (void)dealloc { 112 [stopButton_ setTarget:nil]; 113 [minimizeButton_ setTarget:nil]; 114 [super dealloc]; 115} 116 117- (void)stopSharing:(id)sender { 118 if (!stop_callback_.is_null()) { 119 base::Closure callback = stop_callback_; 120 stop_callback_.Reset(); 121 callback.Run(); // Deletes |self|. 122 } 123} 124 125- (void)minimize:(id)sender { 126 [[self window] miniaturize:sender]; 127} 128 129- (void)hide { 130 stop_callback_.Reset(); 131 [self close]; 132} 133 134- (void)populateWithText:(const base::string16&)text { 135 base::scoped_nsobject<ScreenCaptureNotificationView> content( 136 [[ScreenCaptureNotificationView alloc] 137 initWithFrame:ui::kWindowSizeDeterminedLater]); 138 [[self window] setContentView:content]; 139 140 // Create button. 141 stopButton_.reset([[BlueLabelButton alloc] initWithFrame:NSZeroRect]); 142 [stopButton_ setTitle:l10n_util::GetNSString( 143 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP)]; 144 [stopButton_ setTarget:self]; 145 [stopButton_ setAction:@selector(stopSharing:)]; 146 [stopButton_ sizeToFit]; 147 [content addSubview:stopButton_]; 148 149 base::scoped_nsobject<HyperlinkButtonCell> cell( 150 [[HyperlinkButtonCell alloc] 151 initTextCell:l10n_util::GetNSString( 152 IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON)]); 153 [cell setShouldUnderline:NO]; 154 155 minimizeButton_.reset([[NSButton alloc] initWithFrame:NSZeroRect]); 156 [minimizeButton_ setCell:cell.get()]; 157 [minimizeButton_ sizeToFit]; 158 [minimizeButton_ setTarget:self]; 159 [minimizeButton_ setAction:@selector(minimize:)]; 160 [content addSubview:minimizeButton_]; 161 162 CGFloat buttonsWidth = NSWidth([stopButton_ frame]) + kHorizontalMargin + 163 NSWidth([minimizeButton_ frame]); 164 CGFloat totalHeight = 165 kPaddingVertical + NSHeight([stopButton_ frame]) + kPaddingVertical; 166 167 // Create grip icon. 168 base::scoped_nsobject<WindowGripView> gripView([[WindowGripView alloc] init]); 169 [content addSubview:gripView]; 170 CGFloat gripWidth = NSWidth([gripView frame]); 171 CGFloat gripHeight = NSHeight([gripView frame]); 172 [gripView setFrameOrigin:NSMakePoint(kPaddingHorizontal, 173 (totalHeight - gripHeight) / 2)]; 174 175 // Create text label. 176 int maximumWidth = 177 std::min(kMaximumWidth, NSWidth([[NSScreen mainScreen] visibleFrame])); 178 int maxLabelWidth = maximumWidth - kPaddingHorizontal * 2 - 179 kHorizontalMargin * 2 - gripWidth - buttonsWidth; 180 gfx::FontList font_list; 181 base::string16 elidedText = 182 ElideText(text, font_list, maxLabelWidth, gfx::ELIDE_MIDDLE); 183 NSString* statusText = base::SysUTF16ToNSString(elidedText); 184 base::scoped_nsobject<NSTextField> statusTextField( 185 [[NSTextField alloc] initWithFrame:ui::kWindowSizeDeterminedLater]); 186 [statusTextField setEditable:NO]; 187 [statusTextField setSelectable:NO]; 188 [statusTextField setDrawsBackground:NO]; 189 [statusTextField setBezeled:NO]; 190 [statusTextField setStringValue:statusText]; 191 [statusTextField setFont:font_list.GetPrimaryFont().GetNativeFont()]; 192 [statusTextField sizeToFit]; 193 [statusTextField setFrameOrigin:NSMakePoint( 194 kPaddingHorizontal + kHorizontalMargin + gripWidth, 195 (totalHeight - NSHeight([statusTextField frame])) / 2)]; 196 [content addSubview:statusTextField]; 197 198 // Resize content view to fit controls. 199 CGFloat minimumLableWidth = kMinimumWidth - kPaddingHorizontal * 2 - 200 kHorizontalMargin * 2 - gripWidth - buttonsWidth; 201 CGFloat lableWidth = 202 std::max(NSWidth([statusTextField frame]), minimumLableWidth); 203 CGFloat totalWidth = kPaddingHorizontal * 2 + kHorizontalMargin * 2 + 204 gripWidth + lableWidth + buttonsWidth; 205 [content setFrame:NSMakeRect(0, 0, totalWidth, totalHeight)]; 206 207 // Move the buttons to the right place. 208 NSPoint buttonOrigin = NSMakePoint( 209 totalWidth - kPaddingHorizontal - buttonsWidth, kPaddingVertical); 210 [stopButton_ setFrameOrigin:buttonOrigin]; 211 212 [minimizeButton_ setFrameOrigin:NSMakePoint( 213 totalWidth - kPaddingHorizontal - NSWidth([minimizeButton_ frame]), 214 (totalHeight - NSHeight([minimizeButton_ frame])) / 2)]; 215 216 if (base::i18n::IsRTL()) { 217 [stopButton_ 218 setFrameOrigin:NSMakePoint(totalWidth - NSMaxX([stopButton_ frame]), 219 NSMinY([stopButton_ frame]))]; 220 [minimizeButton_ 221 setFrameOrigin:NSMakePoint(totalWidth - NSMaxX([minimizeButton_ frame]), 222 NSMinY([minimizeButton_ frame]))]; 223 [statusTextField 224 setFrameOrigin:NSMakePoint(totalWidth - NSMaxX([statusTextField frame]), 225 NSMinY([statusTextField frame]))]; 226 [gripView setFrameOrigin:NSMakePoint(totalWidth - NSMaxX([gripView frame]), 227 NSMinY([gripView frame]))]; 228 } 229} 230 231- (void)windowWillClose:(NSNotification*)notification { 232 [self stopSharing:nil]; 233} 234 235@end 236 237@implementation ScreenCaptureNotificationView 238 239- (void)drawRect:(NSRect)dirtyRect { 240 [gfx::SkColorToSRGBNSColor(ui::NativeTheme::instance()->GetSystemColor( 241 ui::NativeTheme::kColorId_DialogBackground)) set]; 242 [[NSBezierPath bezierPathWithRoundedRect:[self bounds] 243 xRadius:kWindowCornerRadius 244 yRadius:kWindowCornerRadius] fill]; 245} 246 247@end 248 249@implementation WindowGripView 250- (WindowGripView*)init { 251 gfx::Image gripImage = 252 ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( 253 IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP, 254 ui::ResourceBundle::RTL_DISABLED); 255 self = [super 256 initWithFrame:NSMakeRect(0, 0, gripImage.Width(), gripImage.Height())]; 257 [self setImage:gripImage.ToNSImage()]; 258 return self; 259} 260 261- (BOOL)mouseDownCanMoveWindow { 262 return YES; 263} 264@end 265