1 // Copyright (c) 2012 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.
5 #import <Cocoa/Cocoa.h>
7 #import "remoting/host/disconnect_window_mac.h"
10 #include "base/compiler_specific.h"
11 #include "base/i18n/rtl.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "remoting/base/string_resources.h"
16 #include "remoting/host/client_session_control.h"
17 #include "remoting/host/host_window.h"
18 #include "ui/base/l10n/l10n_util_mac.h"
20 @interface DisconnectWindowController()
25 const int kMaximumConnectedNameWidthInPixels = 600;
29 class DisconnectWindowMac : public HostWindow {
31 DisconnectWindowMac();
32 ~DisconnectWindowMac() override;
34 // HostWindow overrides.
35 void Start(const base::WeakPtr<ClientSessionControl>& client_session_control)
39 DisconnectWindowController* window_controller_;
41 DISALLOW_COPY_AND_ASSIGN(DisconnectWindowMac);
44 DisconnectWindowMac::DisconnectWindowMac()
45 : window_controller_(nil) {
48 DisconnectWindowMac::~DisconnectWindowMac() {
49 DCHECK(CalledOnValidThread());
51 // DisconnectWindowController is responsible for releasing itself in its
52 // windowWillClose: method.
53 [window_controller_ Hide];
54 window_controller_ = nil;
57 void DisconnectWindowMac::Start(
58 const base::WeakPtr<ClientSessionControl>& client_session_control) {
59 DCHECK(CalledOnValidThread());
60 DCHECK(client_session_control);
61 DCHECK(window_controller_ == nil);
64 base::Closure disconnect_callback =
65 base::Bind(&ClientSessionControl::DisconnectSession,
66 client_session_control);
67 std::string client_jid = client_session_control->client_jid();
68 std::string username = client_jid.substr(0, client_jid.find('/'));
70 [[DisconnectWindowController alloc] initWithCallback:disconnect_callback
72 [window_controller_ showWindow:nil];
76 scoped_ptr<HostWindow> HostWindow::CreateDisconnectWindow() {
77 return make_scoped_ptr(new DisconnectWindowMac());
80 } // namespace remoting
82 @implementation DisconnectWindowController
83 - (id)initWithCallback:(const base::Closure&)disconnect_callback
84 username:(const std::string&)username {
85 self = [super initWithWindowNibName:@"disconnect_window"];
87 disconnect_callback_ = disconnect_callback;
88 username_ = base::UTF8ToUTF16(username);
97 - (IBAction)stopSharing:(id)sender {
98 if (!disconnect_callback_.is_null()) {
99 disconnect_callback_.Run();
104 return base::i18n::IsRTL();
108 disconnect_callback_.Reset();
112 - (void)windowDidLoad {
113 [connectedToField_ setStringValue:l10n_util::GetNSStringF(IDS_MESSAGE_SHARED,
115 [disconnectButton_ setTitle:l10n_util::GetNSString(IDS_STOP_SHARING_BUTTON)];
117 // Resize the window dynamically based on the content.
118 CGFloat oldConnectedWidth = NSWidth([connectedToField_ bounds]);
119 [connectedToField_ sizeToFit];
120 NSRect connectedToFrame = [connectedToField_ frame];
121 CGFloat newConnectedWidth = NSWidth(connectedToFrame);
123 // Set a max width for the connected to text field.
124 if (newConnectedWidth > kMaximumConnectedNameWidthInPixels) {
125 newConnectedWidth = kMaximumConnectedNameWidthInPixels;
126 connectedToFrame.size.width = newConnectedWidth;
127 [connectedToField_ setFrame:connectedToFrame];
130 CGFloat oldDisconnectWidth = NSWidth([disconnectButton_ bounds]);
131 [disconnectButton_ sizeToFit];
132 NSRect disconnectFrame = [disconnectButton_ frame];
133 CGFloat newDisconnectWidth = NSWidth(disconnectFrame);
135 // Move the disconnect button appropriately.
136 disconnectFrame.origin.x += newConnectedWidth - oldConnectedWidth;
137 [disconnectButton_ setFrame:disconnectFrame];
139 // Then resize the window appropriately
140 NSWindow *window = [self window];
141 NSRect windowFrame = [window frame];
142 windowFrame.size.width += (newConnectedWidth - oldConnectedWidth +
143 newDisconnectWidth - oldDisconnectWidth);
144 [window setFrame:windowFrame display:NO];
147 // Handle right to left case
148 CGFloat buttonInset = NSWidth(windowFrame) - NSMaxX(disconnectFrame);
149 CGFloat buttonTextSpacing
150 = NSMinX(disconnectFrame) - NSMaxX(connectedToFrame);
151 disconnectFrame.origin.x = buttonInset;
152 connectedToFrame.origin.x = NSMaxX(disconnectFrame) + buttonTextSpacing;
153 [connectedToField_ setFrame:connectedToFrame];
154 [disconnectButton_ setFrame:disconnectFrame];
157 // Center the window at the bottom of the screen, above the dock (if present).
158 NSRect desktopRect = [[NSScreen mainScreen] visibleFrame];
159 NSRect windowRect = [[self window] frame];
160 CGFloat x = (NSWidth(desktopRect) - NSWidth(windowRect)) / 2;
161 CGFloat y = NSMinY(desktopRect);
162 [[self window] setFrameOrigin:NSMakePoint(x, y)];
165 - (void)windowWillClose:(NSNotification*)notification {
166 [self stopSharing:self];
173 @interface DisconnectWindow()
177 @implementation DisconnectWindow
179 - (id)initWithContentRect:(NSRect)contentRect
180 styleMask:(NSUInteger)aStyle
181 backing:(NSBackingStoreType)bufferingType
183 // Pass NSBorderlessWindowMask for the styleMask to remove the title bar.
184 self = [super initWithContentRect:contentRect
185 styleMask:NSBorderlessWindowMask
186 backing:bufferingType
190 // Set window to be clear and non-opaque so we can see through it.
191 [self setBackgroundColor:[NSColor clearColor]];
193 [self setMovableByWindowBackground:YES];
195 // Pull the window up to Status Level so that it always displays.
196 [self setLevel:NSStatusWindowLevel];
202 DCHECK([[self windowController] respondsToSelector:@selector(isRToL)]);
203 return [[self windowController] isRToL];
209 @interface DisconnectView()
213 @implementation DisconnectView
216 DCHECK([[self window] isKindOfClass:[DisconnectWindow class]]);
217 return [static_cast<DisconnectWindow*>([self window]) isRToL];
220 - (void)drawRect:(NSRect)rect {
221 // All magic numbers taken from screen shots provided by UX.
222 NSRect bounds = NSInsetRect([self bounds], 1, 1);
224 NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:bounds
227 NSColor *gray = [NSColor colorWithCalibratedWhite:0.91 alpha:1.0];
230 [path setLineWidth:4];
231 NSColor *green = [NSColor colorWithCalibratedRed:0.13
239 // Draw drag handle on proper side
240 const CGFloat kHeight = 21.0;
241 const CGFloat kBaseInset = 12.0;
242 const CGFloat kDragHandleWidth = 5.0;
244 NSColor *dark = [NSColor colorWithCalibratedWhite:0.70 alpha:1.0];
245 NSColor *light = [NSColor colorWithCalibratedWhite:0.97 alpha:1.0];
247 // Turn off aliasing so it's nice and crisp.
248 NSGraphicsContext *context = [NSGraphicsContext currentContext];
249 BOOL alias = [context shouldAntialias];
250 [context setShouldAntialias:NO];
252 // Handle bidirectional locales properly.
253 CGFloat inset = [self isRToL] ? NSMaxX(bounds) - kBaseInset - kDragHandleWidth
256 NSPoint top = NSMakePoint(inset, NSMidY(bounds) - kHeight / 2.0);
257 NSPoint bottom = NSMakePoint(inset, top.y + kHeight);
259 path = [NSBezierPath bezierPath];
260 [path moveToPoint:top];
261 [path lineToPoint:bottom];
267 path = [NSBezierPath bezierPath];
268 [path moveToPoint:top];
269 [path lineToPoint:bottom];
275 path = [NSBezierPath bezierPath];
276 [path moveToPoint:top];
277 [path lineToPoint:bottom];
283 path = [NSBezierPath bezierPath];
284 [path moveToPoint:top];
285 [path lineToPoint:bottom];
289 [context setShouldAntialias:alias];