| // Copyright 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 <Cocoa/Cocoa.h> |
| |
| #include "chrome/browser/ui/cocoa/screen_capture_notification_ui_cocoa.h" |
| |
| #include "base/compiler_specific.h" |
| #include "base/i18n/rtl.h" |
| #include "base/mac/bundle_locations.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "grit/generated_resources.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| @interface ScreenCaptureNotificationController() |
| - (void)Hide; |
| @end |
| |
| class ScreenCaptureNotificationUICocoa : public ScreenCaptureNotificationUI { |
| public: |
| explicit ScreenCaptureNotificationUICocoa(const string16& text); |
| virtual ~ScreenCaptureNotificationUICocoa(); |
| |
| // ScreenCaptureNotificationUI interface. |
| virtual void OnStarted(const base::Closure& stop_callback) OVERRIDE; |
| |
| private: |
| const string16 text_; |
| ScreenCaptureNotificationController* window_controller_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUICocoa); |
| }; |
| |
| ScreenCaptureNotificationUICocoa::ScreenCaptureNotificationUICocoa( |
| const string16& text) |
| : text_(text), |
| window_controller_(nil) { |
| } |
| |
| ScreenCaptureNotificationUICocoa::~ScreenCaptureNotificationUICocoa() { |
| // ScreenCaptureNotificationController is responsible for releasing itself in |
| // its windowWillClose: method. |
| [window_controller_ Hide]; |
| window_controller_ = nil; |
| } |
| |
| void ScreenCaptureNotificationUICocoa::OnStarted( |
| const base::Closure& stop_callback) { |
| DCHECK(!stop_callback.is_null()); |
| DCHECK(window_controller_ == nil); |
| |
| window_controller_ = |
| [[ScreenCaptureNotificationController alloc] |
| initWithCallback:stop_callback |
| text:text_]; |
| [window_controller_ showWindow:nil]; |
| } |
| |
| scoped_ptr<ScreenCaptureNotificationUI> ScreenCaptureNotificationUI::Create( |
| const string16& text) { |
| return scoped_ptr<ScreenCaptureNotificationUI>( |
| new ScreenCaptureNotificationUICocoa(text)); |
| } |
| |
| @implementation ScreenCaptureNotificationController |
| - (id)initWithCallback:(const base::Closure&)stop_callback |
| text:(const string16&)text { |
| NSString* nibpath = |
| [base::mac::FrameworkBundle() pathForResource:@"ScreenCaptureNotification" |
| ofType:@"nib"]; |
| self = [super initWithWindowNibPath:nibpath owner:self]; |
| if (self) { |
| stop_callback_ = stop_callback; |
| text_ = text; |
| } |
| return self; |
| } |
| |
| - (void)dealloc { |
| [super dealloc]; |
| } |
| |
| - (IBAction)stopSharing:(id)sender { |
| if (!stop_callback_.is_null()) { |
| stop_callback_.Run(); |
| } |
| } |
| |
| - (void)Hide { |
| stop_callback_.Reset(); |
| [self close]; |
| } |
| |
| - (void)windowDidLoad { |
| [statusField_ setStringValue:base::SysUTF16ToNSString(text_)]; |
| |
| string16 button_label = |
| l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP); |
| [stopButton_ setTitle:base::SysUTF16ToNSString(button_label)]; |
| |
| // Resize the window dynamically based on the content. |
| CGFloat oldConnectedWidth = NSWidth([statusField_ bounds]); |
| [statusField_ sizeToFit]; |
| NSRect statusFrame = [statusField_ frame]; |
| CGFloat newConnectedWidth = NSWidth(statusFrame); |
| |
| // Set a max width for the connected to text field. |
| const int kMaximumStatusWidth = 400; |
| if (newConnectedWidth > kMaximumStatusWidth) { |
| newConnectedWidth = kMaximumStatusWidth; |
| statusFrame.size.width = newConnectedWidth; |
| [statusField_ setFrame:statusFrame]; |
| } |
| |
| CGFloat oldstopWidth = NSWidth([stopButton_ bounds]); |
| [stopButton_ sizeToFit]; |
| NSRect stopFrame = [stopButton_ frame]; |
| CGFloat newStopWidth = NSWidth(stopFrame); |
| |
| // Move the stop button appropriately. |
| stopFrame.origin.x += newConnectedWidth - oldConnectedWidth; |
| [stopButton_ setFrame:stopFrame]; |
| |
| // Then resize the window appropriately. |
| NSWindow *window = [self window]; |
| NSRect windowFrame = [window frame]; |
| windowFrame.size.width += (newConnectedWidth - oldConnectedWidth + |
| newStopWidth - oldstopWidth); |
| [window setFrame:windowFrame display:NO]; |
| |
| if (base::i18n::IsRTL()) { |
| // Handle right to left case |
| CGFloat buttonInset = NSWidth(windowFrame) - NSMaxX(stopFrame); |
| CGFloat buttonTextSpacing |
| = NSMinX(stopFrame) - NSMaxX(statusFrame); |
| stopFrame.origin.x = buttonInset; |
| statusFrame.origin.x = NSMaxX(stopFrame) + buttonTextSpacing; |
| [statusField_ setFrame:statusFrame]; |
| [stopButton_ setFrame:stopFrame]; |
| } |
| |
| // Center the window at the bottom of the screen, above the dock (if present). |
| NSRect desktopRect = [[NSScreen mainScreen] visibleFrame]; |
| NSRect windowRect = [[self window] frame]; |
| CGFloat x = (NSWidth(desktopRect) - NSWidth(windowRect)) / 2; |
| CGFloat y = NSMinY(desktopRect); |
| [[self window] setFrameOrigin:NSMakePoint(x, y)]; |
| } |
| |
| - (void)windowWillClose:(NSNotification*)notification { |
| [self stopSharing:self]; |
| [self autorelease]; |
| } |
| |
| @end |
| |
| |
| @implementation ScreenCaptureNotificationWindow |
| - (id)initWithContentRect:(NSRect)contentRect |
| styleMask:(NSUInteger)aStyle |
| backing:(NSBackingStoreType)bufferingType |
| defer:(BOOL)flag { |
| // Pass NSBorderlessWindowMask for the styleMask to remove the title bar. |
| self = [super initWithContentRect:contentRect |
| styleMask:NSBorderlessWindowMask |
| backing:bufferingType |
| defer:flag]; |
| |
| if (self) { |
| // Set window to be clear and non-opaque so we can see through it. |
| [self setBackgroundColor:[NSColor clearColor]]; |
| [self setOpaque:NO]; |
| [self setMovableByWindowBackground:YES]; |
| |
| // Pull the window up to Status Level so that it always displays. |
| [self setLevel:NSStatusWindowLevel]; |
| } |
| return self; |
| } |
| @end |
| |
| @implementation ScreenCaptureNotificationView |
| - (void)drawRect:(NSRect)rect { |
| // All magic numbers taken from screen shots provided by UX. |
| NSRect bounds = NSInsetRect([self bounds], 1, 1); |
| |
| NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:bounds |
| xRadius:5 |
| yRadius:5]; |
| NSColor *gray = [NSColor colorWithCalibratedWhite:0.91 alpha:1.0]; |
| [gray setFill]; |
| [path fill]; |
| [path setLineWidth:4]; |
| NSColor *green = [NSColor colorWithCalibratedRed:0.13 |
| green:0.69 |
| blue:0.11 |
| alpha:1.0]; |
| [green setStroke]; |
| [path stroke]; |
| |
| |
| // Draw drag handle on proper side |
| const CGFloat kHeight = 21.0; |
| const CGFloat kBaseInset = 12.0; |
| const CGFloat kDragHandleWidth = 5.0; |
| |
| NSColor *dark = [NSColor colorWithCalibratedWhite:0.70 alpha:1.0]; |
| NSColor *light = [NSColor colorWithCalibratedWhite:0.97 alpha:1.0]; |
| |
| // Turn off aliasing so it's nice and crisp. |
| NSGraphicsContext *context = [NSGraphicsContext currentContext]; |
| BOOL alias = [context shouldAntialias]; |
| [context setShouldAntialias:NO]; |
| |
| // Handle bidirectional locales properly. |
| CGFloat inset = base::i18n::IsRTL() ? |
| NSMaxX(bounds) - kBaseInset - kDragHandleWidth : kBaseInset; |
| |
| NSPoint top = NSMakePoint(inset, NSMidY(bounds) - kHeight / 2.0); |
| NSPoint bottom = NSMakePoint(inset, top.y + kHeight); |
| |
| path = [NSBezierPath bezierPath]; |
| [path moveToPoint:top]; |
| [path lineToPoint:bottom]; |
| [dark setStroke]; |
| [path stroke]; |
| |
| top.x += 1; |
| bottom.x += 1; |
| path = [NSBezierPath bezierPath]; |
| [path moveToPoint:top]; |
| [path lineToPoint:bottom]; |
| [light setStroke]; |
| [path stroke]; |
| |
| top.x += 2; |
| bottom.x += 2; |
| path = [NSBezierPath bezierPath]; |
| [path moveToPoint:top]; |
| [path lineToPoint:bottom]; |
| [dark setStroke]; |
| [path stroke]; |
| |
| top.x += 1; |
| bottom.x += 1; |
| path = [NSBezierPath bezierPath]; |
| [path moveToPoint:top]; |
| [path lineToPoint:bottom]; |
| [light setStroke]; |
| [path stroke]; |
| |
| [context setShouldAntialias:alias]; |
| } |
| |
| @end |