OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/cocoa/status_bubble_mac.h" | 5 #include "chrome/browser/cocoa/status_bubble_mac.h" |
6 | 6 |
| 7 #include <limits> |
| 8 |
7 #include "app/gfx/text_elider.h" | 9 #include "app/gfx/text_elider.h" |
| 10 #include "base/compiler_specific.h" |
| 11 #include "base/message_loop.h" |
8 #include "base/string_util.h" | 12 #include "base/string_util.h" |
9 #include "base/sys_string_conversions.h" | 13 #include "base/sys_string_conversions.h" |
10 #import "chrome/browser/cocoa/bubble_view.h" | 14 #import "chrome/browser/cocoa/bubble_view.h" |
11 #include "googleurl/src/gurl.h" | 15 #include "googleurl/src/gurl.h" |
12 #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h" | 16 #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h" |
13 #import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" | 17 #import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" |
14 #import "third_party/GTM/AppKit/GTMTheme.h" | 18 #import "third_party/GTM/AppKit/GTMTheme.h" |
15 | 19 |
16 namespace { | 20 namespace { |
17 | 21 |
18 const int kWindowHeight = 18; | 22 const int kWindowHeight = 18; |
| 23 |
19 // The width of the bubble in relation to the width of the parent window. | 24 // The width of the bubble in relation to the width of the parent window. |
20 const float kWindowWidthPercent = 1.0f/3.0f; | 25 const double kWindowWidthPercent = 1.0 / 3.0; |
21 | 26 |
22 // How close the mouse can get to the infobubble before it starts sliding | 27 // How close the mouse can get to the infobubble before it starts sliding |
23 // off-screen. | 28 // off-screen. |
24 const int kMousePadding = 20; | 29 const int kMousePadding = 20; |
25 | 30 |
26 const int kTextPadding = 3; | 31 const int kTextPadding = 3; |
27 | 32 |
28 // How long each fade should last for. | 33 // The animation key used for fade-in and fade-out transitions. |
29 const int kShowFadeDuration = 0.120f; | 34 const NSString* kFadeAnimationKey = @"alphaValue"; |
30 const int kHideFadeDuration = 0.200f; | |
31 | 35 |
| 36 // The status bubble's maximum opacity, when fully faded in. |
| 37 const CGFloat kBubbleOpacity = 1.0; |
| 38 |
| 39 // Delay before showing or hiding the bubble after a SetStatus or SetURL call. |
| 40 const int64 kShowDelayMilliseconds = 80; |
| 41 const int64 kHideDelayMilliseconds = 250; |
| 42 |
| 43 // How long each fade should last. |
| 44 const NSTimeInterval kShowFadeInDurationSeconds = 0.120; |
| 45 const NSTimeInterval kHideFadeOutDurationSeconds = 0.200; |
| 46 |
| 47 // The minimum representable time interval. This can be used as the value |
| 48 // passed to +[NSAnimationContext setDuration:] to stop an in-progress |
| 49 // animation as quickly as possible. |
| 50 const NSTimeInterval kMinimumTimeInterval = |
| 51 std::numeric_limits<NSTimeInterval>::min(); |
| 52 |
| 53 } // namespace |
| 54 |
| 55 @interface StatusBubbleAnimationDelegate : NSObject { |
| 56 @private |
| 57 StatusBubbleMac* statusBubble_; // weak; owns us indirectly |
32 } | 58 } |
33 | 59 |
34 // TODO(avi): | 60 - (id)initWithStatusBubble:(StatusBubbleMac*)statusBubble; |
35 // - do display delay | 61 |
| 62 // Invalidates this object so that no further calls will be made to |
| 63 // statusBubble_. This should be called when statusBubble_ is released, to |
| 64 // prevent attempts to call into the released object. |
| 65 - (void)invalidate; |
| 66 |
| 67 // CAAnimation delegate method |
| 68 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished; |
| 69 @end |
| 70 |
| 71 @implementation StatusBubbleAnimationDelegate |
| 72 |
| 73 - (id)initWithStatusBubble:(StatusBubbleMac*)statusBubble { |
| 74 if ((self = [super init])) { |
| 75 statusBubble_ = statusBubble; |
| 76 } |
| 77 |
| 78 return self; |
| 79 } |
| 80 |
| 81 - (void)invalidate { |
| 82 statusBubble_ = NULL; |
| 83 } |
| 84 |
| 85 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished { |
| 86 if (statusBubble_) |
| 87 statusBubble_->AnimationDidStop(animation, finished ? true : false); |
| 88 } |
| 89 |
| 90 @end |
36 | 91 |
37 StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) | 92 StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) |
38 : parent_(parent), | 93 : ALLOW_THIS_IN_INITIALIZER_LIST(timer_factory_(this)), |
| 94 parent_(parent), |
39 delegate_(delegate), | 95 delegate_(delegate), |
40 window_(nil), | 96 window_(nil), |
41 status_text_(nil), | 97 status_text_(nil), |
42 url_text_(nil) { | 98 url_text_(nil), |
| 99 state_(kBubbleHidden), |
| 100 immediate_(false) { |
43 } | 101 } |
44 | 102 |
45 StatusBubbleMac::~StatusBubbleMac() { | 103 StatusBubbleMac::~StatusBubbleMac() { |
46 Hide(); | 104 Hide(); |
| 105 |
| 106 if (window_) { |
| 107 [[[window_ animationForKey:kFadeAnimationKey] delegate] invalidate]; |
| 108 [parent_ removeChildWindow:window_]; |
| 109 [window_ release]; |
| 110 window_ = nil; |
| 111 } |
47 } | 112 } |
48 | 113 |
49 void StatusBubbleMac::SetStatus(const std::wstring& status) { | 114 void StatusBubbleMac::SetStatus(const std::wstring& status) { |
50 Create(); | 115 Create(); |
51 | 116 |
52 NSString* status_ns = base::SysWideToNSString(status); | 117 SetText(status, false); |
53 | |
54 SetStatus(status_ns, false); | |
55 } | 118 } |
56 | 119 |
57 void StatusBubbleMac::SetURL(const GURL& url, const std::wstring& languages) { | 120 void StatusBubbleMac::SetURL(const GURL& url, const std::wstring& languages) { |
58 Create(); | 121 Create(); |
59 | 122 |
60 NSRect frame = [window_ frame]; | 123 NSRect frame = [window_ frame]; |
61 int text_width = static_cast<int>(frame.size.width - | 124 int text_width = static_cast<int>(frame.size.width - |
62 kBubbleViewTextPositionX - | 125 kBubbleViewTextPositionX - |
63 kTextPadding); | 126 kTextPadding); |
64 NSFont* font = [[window_ contentView] font]; | 127 NSFont* font = [[window_ contentView] font]; |
65 gfx::Font font_chr = | 128 gfx::Font font_chr = |
66 gfx::Font::CreateFont(base::SysNSStringToWide([font fontName]), | 129 gfx::Font::CreateFont(base::SysNSStringToWide([font fontName]), |
67 [font pointSize]); | 130 [font pointSize]); |
68 | 131 |
69 std::wstring status = gfx::ElideUrl(url, font_chr, text_width, languages); | 132 std::wstring status = gfx::ElideUrl(url, font_chr, text_width, languages); |
70 NSString* status_ns = base::SysWideToNSString(status); | |
71 | 133 |
72 SetStatus(status_ns, true); | 134 SetText(status, true); |
73 } | 135 } |
74 | 136 |
75 void StatusBubbleMac::SetStatus(NSString* status, bool is_url) { | 137 void StatusBubbleMac::SetText(const std::wstring& text, bool is_url) { |
| 138 // The status bubble allows the status and URL strings to be set |
| 139 // independently. Whichever was set non-empty most recently will be the |
| 140 // value displayed. When both are empty, the status bubble hides. |
| 141 |
| 142 NSString* text_ns = base::SysWideToNSString(text); |
| 143 |
76 NSString** main; | 144 NSString** main; |
77 NSString** backup; | 145 NSString** backup; |
78 | 146 |
79 if (is_url) { | 147 if (is_url) { |
80 main = &url_text_; | 148 main = &url_text_; |
81 backup = &status_text_; | 149 backup = &status_text_; |
82 } else { | 150 } else { |
83 main = &status_text_; | 151 main = &status_text_; |
84 backup = &url_text_; | 152 backup = &url_text_; |
85 } | 153 } |
86 | 154 |
87 if ([status isEqualToString:*main]) | 155 // Don't return from this function early. It's important to make sure that |
88 return; | 156 // all calls to StartShowing and StartHiding are made, so that all delays |
| 157 // are observed properly. Specifically, if the state is currently |
| 158 // kBubbleShowingTimer, the timer will need to be restarted even if |
| 159 // [text_ns isEqualToString:*main] is true. |
89 | 160 |
90 [*main release]; | 161 [*main autorelease]; |
91 *main = [status retain]; | 162 *main = [text_ns retain]; |
92 if ([*main length] > 0) { | 163 |
| 164 bool show = true; |
| 165 if ([*main length] > 0) |
93 [[window_ contentView] setContent:*main]; | 166 [[window_ contentView] setContent:*main]; |
94 } else if ([*backup length] > 0) { | 167 else if ([*backup length] > 0) |
95 [[window_ contentView] setContent:*backup]; | 168 [[window_ contentView] setContent:*backup]; |
96 } else { | 169 else |
97 Hide(); | 170 show = false; |
98 } | |
99 | 171 |
100 FadeIn(); | 172 if (show) |
| 173 StartShowing(); |
| 174 else |
| 175 StartHiding(); |
101 } | 176 } |
102 | 177 |
103 void StatusBubbleMac::Hide() { | 178 void StatusBubbleMac::Hide() { |
104 FadeOut(); | 179 CancelTimer(); |
105 | 180 |
106 if (window_) { | 181 bool fade_out = false; |
107 [parent_ removeChildWindow:window_]; | 182 if (state_ == kBubbleHidingFadeOut || state_ == kBubbleShowingFadeIn) { |
108 [window_ release]; | 183 SetState(kBubbleHidingFadeOut); |
109 window_ = nil; | 184 |
| 185 if (!immediate_) { |
| 186 // An animation is in progress. Cancel it by starting a new animation. |
| 187 // Use kMinimumTimeInterval to set the opacity as rapidly as possible. |
| 188 fade_out = true; |
| 189 [NSAnimationContext beginGrouping]; |
| 190 [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval]; |
| 191 [[window_ animator] setAlphaValue:0.0]; |
| 192 [NSAnimationContext endGrouping]; |
| 193 } |
| 194 } |
| 195 |
| 196 if (!fade_out) { |
| 197 // No animation is in progress, so the opacity can be set directly. |
| 198 [window_ setAlphaValue:0.0]; |
| 199 SetState(kBubbleHidden); |
110 } | 200 } |
111 | 201 |
112 [status_text_ release]; | 202 [status_text_ release]; |
113 status_text_ = nil; | 203 status_text_ = nil; |
114 [url_text_ release]; | 204 [url_text_ release]; |
115 url_text_ = nil; | 205 url_text_ = nil; |
116 } | 206 } |
117 | 207 |
118 void StatusBubbleMac::MouseMoved() { | 208 void StatusBubbleMac::MouseMoved() { |
119 if (!window_) | 209 if (!window_) |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 [[window_ contentView] setCornerFlags: | 248 [[window_ contentView] setCornerFlags: |
159 kRoundedBottomLeftCorner | kRoundedBottomRightCorner]; | 249 kRoundedBottomLeftCorner | kRoundedBottomRightCorner]; |
160 } else if (offset > 0) { | 250 } else if (offset > 0) { |
161 [[window_ contentView] setCornerFlags: | 251 [[window_ contentView] setCornerFlags: |
162 kRoundedTopRightCorner | kRoundedBottomLeftCorner | | 252 kRoundedTopRightCorner | kRoundedBottomLeftCorner | |
163 kRoundedBottomRightCorner]; | 253 kRoundedBottomRightCorner]; |
164 } else { | 254 } else { |
165 [[window_ contentView] setCornerFlags:kRoundedTopRightCorner]; | 255 [[window_ contentView] setCornerFlags:kRoundedTopRightCorner]; |
166 } | 256 } |
167 | 257 |
168 offset_ = offset; | |
169 window_frame.origin.y -= offset; | 258 window_frame.origin.y -= offset; |
170 } else { | 259 } else { |
171 offset_ = 0; | |
172 [[window_ contentView] setCornerFlags:kRoundedTopRightCorner]; | 260 [[window_ contentView] setCornerFlags:kRoundedTopRightCorner]; |
173 } | 261 } |
174 | 262 |
175 [window_ setFrame:window_frame display:YES]; | 263 [window_ setFrame:window_frame display:YES]; |
176 } | 264 } |
177 | 265 |
178 void StatusBubbleMac::UpdateDownloadShelfVisibility(bool visible) { | 266 void StatusBubbleMac::UpdateDownloadShelfVisibility(bool visible) { |
179 } | 267 } |
180 | 268 |
181 void StatusBubbleMac::Create() { | 269 void StatusBubbleMac::Create() { |
(...skipping 12 matching lines...) Expand all Loading... |
194 [window_ setHasShadow:NO]; | 282 [window_ setHasShadow:NO]; |
195 | 283 |
196 // We do not need to worry about the bubble outliving |parent_| because our | 284 // We do not need to worry about the bubble outliving |parent_| because our |
197 // teardown sequence in BWC guarantees that |parent_| outlives the status | 285 // teardown sequence in BWC guarantees that |parent_| outlives the status |
198 // bubble and that the StatusBubble is torn down completely prior to the | 286 // bubble and that the StatusBubble is torn down completely prior to the |
199 // window going away. | 287 // window going away. |
200 scoped_nsobject<BubbleView> view( | 288 scoped_nsobject<BubbleView> view( |
201 [[BubbleView alloc] initWithFrame:NSZeroRect themeProvider:parent_]); | 289 [[BubbleView alloc] initWithFrame:NSZeroRect themeProvider:parent_]); |
202 [window_ setContentView:view]; | 290 [window_ setContentView:view]; |
203 | 291 |
204 [parent_ addChildWindow:window_ ordered:NSWindowAbove]; | 292 [window_ setAlphaValue:0.0]; |
205 | 293 |
206 [window_ setAlphaValue:0.0f]; | 294 // Set a delegate for the fade-in and fade-out transitions to be notified |
| 295 // when fades are complete. The ownership model is for window_ to own |
| 296 // animation_dictionary, which owns animation, which owns |
| 297 // animation_delegate. |
| 298 CAAnimation* animation = [[window_ animationForKey:kFadeAnimationKey] copy]; |
| 299 [animation autorelease]; |
| 300 StatusBubbleAnimationDelegate* animation_delegate = |
| 301 [[StatusBubbleAnimationDelegate alloc] initWithStatusBubble:this]; |
| 302 [animation_delegate autorelease]; |
| 303 [animation setDelegate:animation_delegate]; |
| 304 NSMutableDictionary* animation_dictionary = |
| 305 [NSMutableDictionary dictionaryWithDictionary:[window_ animations]]; |
| 306 [animation_dictionary setObject:animation forKey:kFadeAnimationKey]; |
| 307 [window_ setAnimations:animation_dictionary]; |
207 | 308 |
208 offset_ = 0; | 309 Attach(); |
| 310 |
209 [view setCornerFlags:kRoundedTopRightCorner]; | 311 [view setCornerFlags:kRoundedTopRightCorner]; |
210 MouseMoved(); | 312 MouseMoved(); |
211 } | 313 } |
212 | 314 |
213 void StatusBubbleMac::FadeIn() { | 315 void StatusBubbleMac::Attach() { |
| 316 // If the parent window is offscreen when the child is added, the child will |
| 317 // never be displayed, even when the parent moves on-screen. This method |
| 318 // may be called several times during the process of creating or showing a |
| 319 // status bubble to attach the bubble to its parent window. |
| 320 if (![window_ parentWindow] && [parent_ isVisible]) |
| 321 [parent_ addChildWindow:window_ ordered:NSWindowAbove]; |
| 322 } |
| 323 |
| 324 void StatusBubbleMac::AnimationDidStop(CAAnimation* animation, bool finished) { |
| 325 DCHECK(state_ == kBubbleShowingFadeIn || state_ == kBubbleHidingFadeOut); |
| 326 |
| 327 if (finished) { |
| 328 // Because of the mechanism used to interrupt animations, this is never |
| 329 // actually called with finished set to false. If animations ever become |
| 330 // directly interruptible, the check will ensure that state_ remains |
| 331 // properly synchronized. |
| 332 if (state_ == kBubbleShowingFadeIn) { |
| 333 DCHECK_EQ([[window_ animator] alphaValue], kBubbleOpacity); |
| 334 state_ = kBubbleShown; |
| 335 } else { |
| 336 DCHECK_EQ([[window_ animator] alphaValue], 0.0); |
| 337 state_ = kBubbleHidden; |
| 338 } |
| 339 } |
| 340 } |
| 341 |
| 342 void StatusBubbleMac::SetState(StatusBubbleState state) { |
| 343 if (state == state_) |
| 344 return; |
| 345 |
| 346 if ([delegate_ respondsToSelector:@selector(statusBubbleWillEnterState:)]) |
| 347 [delegate_ statusBubbleWillEnterState:state]; |
| 348 |
| 349 state_ = state; |
| 350 } |
| 351 |
| 352 void StatusBubbleMac::Fade(bool show) { |
| 353 StatusBubbleState fade_state = kBubbleShowingFadeIn; |
| 354 StatusBubbleState target_state = kBubbleShown; |
| 355 NSTimeInterval full_duration = kShowFadeInDurationSeconds; |
| 356 CGFloat opacity = kBubbleOpacity; |
| 357 |
| 358 if (!show) { |
| 359 fade_state = kBubbleHidingFadeOut; |
| 360 target_state = kBubbleHidden; |
| 361 full_duration = kHideFadeOutDurationSeconds; |
| 362 opacity = 0.0; |
| 363 } |
| 364 |
| 365 DCHECK(state_ == fade_state || state_ == target_state); |
| 366 |
| 367 if (state_ == target_state) |
| 368 return; |
| 369 |
| 370 Attach(); |
| 371 |
| 372 if (immediate_) { |
| 373 [window_ setAlphaValue:opacity]; |
| 374 SetState(target_state); |
| 375 return; |
| 376 } |
| 377 |
| 378 // If an incomplete transition has left the opacity somewhere between 0 and |
| 379 // kBubbleOpacity, the fade rate is kept constant by shortening the duration. |
| 380 NSTimeInterval duration = |
| 381 full_duration * |
| 382 fabs(opacity - [[window_ animator] alphaValue]) / kBubbleOpacity; |
| 383 |
| 384 // 0.0 will not cancel an in-progress animation. |
| 385 if (duration == 0.0) |
| 386 duration = kMinimumTimeInterval; |
| 387 |
| 388 // This will cancel an in-progress transition and replace it with this fade. |
214 [NSAnimationContext beginGrouping]; | 389 [NSAnimationContext beginGrouping]; |
215 [[NSAnimationContext currentContext] setDuration:kShowFadeDuration]; | 390 [[NSAnimationContext currentContext] setDuration:duration]; |
216 [[window_ animator] setAlphaValue:1.0f]; | 391 [[window_ animator] setAlphaValue:opacity]; |
217 [NSAnimationContext endGrouping]; | 392 [NSAnimationContext endGrouping]; |
218 } | 393 } |
219 | 394 |
220 void StatusBubbleMac::FadeOut() { | 395 void StatusBubbleMac::StartTimer(int64 delay_ms) { |
221 [NSAnimationContext beginGrouping]; | 396 DCHECK(state_ == kBubbleShowingTimer || state_ == kBubbleHidingTimer); |
222 [[NSAnimationContext currentContext] setDuration:kHideFadeDuration]; | 397 |
223 [[window_ animator] setAlphaValue:0.0f]; | 398 if (immediate_) { |
224 [NSAnimationContext endGrouping]; | 399 TimerFired(); |
| 400 return; |
| 401 } |
| 402 |
| 403 // There can only be one running timer. |
| 404 CancelTimer(); |
| 405 |
| 406 MessageLoop::current()->PostDelayedTask( |
| 407 FROM_HERE, |
| 408 timer_factory_.NewRunnableMethod(&StatusBubbleMac::TimerFired), |
| 409 delay_ms); |
| 410 } |
| 411 |
| 412 void StatusBubbleMac::CancelTimer() { |
| 413 if (!timer_factory_.empty()) |
| 414 timer_factory_.RevokeAll(); |
| 415 } |
| 416 |
| 417 void StatusBubbleMac::TimerFired() { |
| 418 DCHECK(state_ == kBubbleShowingTimer || state_ == kBubbleHidingTimer); |
| 419 |
| 420 if (state_ == kBubbleShowingTimer) { |
| 421 SetState(kBubbleShowingFadeIn); |
| 422 Fade(true); |
| 423 } else { |
| 424 SetState(kBubbleHidingFadeOut); |
| 425 Fade(false); |
| 426 } |
| 427 } |
| 428 |
| 429 void StatusBubbleMac::StartShowing() { |
| 430 Attach(); |
| 431 |
| 432 if (state_ == kBubbleHidden) { |
| 433 // Arrange to begin fading in after a delay. |
| 434 SetState(kBubbleShowingTimer); |
| 435 StartTimer(kShowDelayMilliseconds); |
| 436 } else if (state_ == kBubbleHidingFadeOut) { |
| 437 // Cancel the fade-out in progress and replace it with a fade in. |
| 438 SetState(kBubbleShowingFadeIn); |
| 439 Fade(true); |
| 440 } else if (state_ == kBubbleHidingTimer) { |
| 441 // The bubble was already shown but was waiting to begin fading out. It's |
| 442 // given a stay of execution. |
| 443 SetState(kBubbleShown); |
| 444 CancelTimer(); |
| 445 } else if (state_ == kBubbleShowingTimer) { |
| 446 // The timer was already running but nothing was showing yet. Reaching |
| 447 // this point means that there is a new request to show something. Start |
| 448 // over again by resetting the timer, effectively invalidating the earlier |
| 449 // request. |
| 450 StartTimer(kShowDelayMilliseconds); |
| 451 } |
| 452 |
| 453 // If the state is kBubbleShown or kBubbleShowingFadeIn, leave everything |
| 454 // alone. |
| 455 } |
| 456 |
| 457 void StatusBubbleMac::StartHiding() { |
| 458 if (state_ == kBubbleShown) { |
| 459 // Arrange to begin fading out after a delay. |
| 460 SetState(kBubbleHidingTimer); |
| 461 StartTimer(kHideDelayMilliseconds); |
| 462 } else if (state_ == kBubbleShowingFadeIn) { |
| 463 // Cancel the fade-in in progress and replace it with a fade out. |
| 464 SetState(kBubbleHidingFadeOut); |
| 465 Fade(false); |
| 466 } else if (state_ == kBubbleShowingTimer) { |
| 467 // The bubble was already hidden but was waiting to begin fading in. Too |
| 468 // bad, it won't get the opportunity now. |
| 469 SetState(kBubbleHidden); |
| 470 CancelTimer(); |
| 471 } |
| 472 |
| 473 // If the state is kBubbleHidden, kBubbleHidingFadeOut, or |
| 474 // kBubbleHidingTimer, leave everything alone. The timer is not reset as |
| 475 // with kBubbleShowingTimer in StartShowing() because a subsequent request |
| 476 // to hide something while one is already in flight does not invalidate the |
| 477 // earlier request. |
225 } | 478 } |
226 | 479 |
227 void StatusBubbleMac::UpdateSizeAndPosition() { | 480 void StatusBubbleMac::UpdateSizeAndPosition() { |
228 if (!window_) | 481 if (!window_) |
229 return; | 482 return; |
230 | 483 |
231 [window_ setFrame:CalculateWindowFrame() display:YES]; | 484 [window_ setFrame:CalculateWindowFrame() display:YES]; |
232 } | 485 } |
233 | 486 |
234 NSRect StatusBubbleMac::CalculateWindowFrame() { | 487 NSRect StatusBubbleMac::CalculateWindowFrame() { |
235 DCHECK(parent_); | 488 DCHECK(parent_); |
236 | 489 |
237 NSRect rect = [parent_ frame]; | 490 NSRect rect = [parent_ frame]; |
238 rect.size.height = kWindowHeight; | 491 rect.size.height = kWindowHeight; |
239 rect.size.width = static_cast<int>(kWindowWidthPercent * rect.size.width); | 492 rect.size.width = static_cast<int>(kWindowWidthPercent * rect.size.width); |
240 return rect; | 493 return rect; |
241 } | 494 } |
OLD | NEW |