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