| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/ui/cocoa/status_bubble_mac.h" | 5 #include "chrome/browser/ui/cocoa/status_bubble_mac.h" |
| 6 | 6 |
| 7 #include <limits> | 7 #include <limits> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 135 changes([NSAnimationContext currentContext]); | 135 changes([NSAnimationContext currentContext]); |
| 136 // At this point, -animationForKey should have been called by CoreAnimation | 136 // At this point, -animationForKey should have been called by CoreAnimation |
| 137 // to set up the animation to run. Verify this. | 137 // to set up the animation to run. Verify this. |
| 138 DCHECK(completionHandler_ == nil); | 138 DCHECK(completionHandler_ == nil); |
| 139 [NSAnimationContext endGrouping]; | 139 [NSAnimationContext endGrouping]; |
| 140 } | 140 } |
| 141 } | 141 } |
| 142 | 142 |
| 143 @end | 143 @end |
| 144 | 144 |
| 145 // Mac implementation of the status bubble. |
| 146 // |
| 147 // Child windows interact with Spaces in interesting ways, so this code has to |
| 148 // follow these rules: |
| 149 // |
| 150 // 1) NSWindows cannot have zero size. At times when the status bubble window |
| 151 // has no specific size (for example, when hidden), its size is set to |
| 152 // ui::kWindowSizeDeterminedLater. |
| 153 // |
| 154 // 2) Child window frames are in the coordinate space of the screen, not of the |
| 155 // parent window. If a child window has its origin at (0, 0), Spaces will |
| 156 // position it in the corner of the screen but group it with the parent |
| 157 // window in Spaces. This causes Chrome windows to have a large (mostly |
| 158 // blank) area in Spaces. To avoid this, child windows always have their |
| 159 // origin set to the lower-left corner of the window. |
| 160 // |
| 161 // 3) Detached child windows may show up as top-level windows in Spaces. To |
| 162 // avoid this, once the status bubble is Attach()ed to the parent, it is |
| 163 // never detached (except in rare cases when reparenting to a fullscreen |
| 164 // window). |
| 165 // |
| 166 // 4) To avoid unnecessary redraws, if a bubble is in the kBubbleHidden state, |
| 167 // its size is always set to ui::kWindowSizeDeterminedLater. The proper |
| 168 // width for the current URL or status text is not calculated until the |
| 169 // bubble leaves the kBubbleHidden state. |
| 170 |
| 145 StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) | 171 StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) |
| 146 : parent_(parent), | 172 : parent_(parent), |
| 147 delegate_(delegate), | 173 delegate_(delegate), |
| 148 window_(nil), | 174 window_(nil), |
| 149 status_text_(nil), | 175 status_text_(nil), |
| 150 url_text_(nil), | 176 url_text_(nil), |
| 151 state_(kBubbleHidden), | 177 state_(kBubbleHidden), |
| 152 immediate_(false), | 178 immediate_(false), |
| 153 is_expanded_(false), | 179 is_expanded_(false), |
| 154 timer_factory_(this), | 180 timer_factory_(this), |
| (...skipping 16 matching lines...) Expand all Loading... |
| 171 | 197 |
| 172 void StatusBubbleMac::SetStatus(const base::string16& status) { | 198 void StatusBubbleMac::SetStatus(const base::string16& status) { |
| 173 SetText(status, false); | 199 SetText(status, false); |
| 174 } | 200 } |
| 175 | 201 |
| 176 void StatusBubbleMac::SetURL(const GURL& url, const std::string& languages) { | 202 void StatusBubbleMac::SetURL(const GURL& url, const std::string& languages) { |
| 177 url_ = url; | 203 url_ = url; |
| 178 languages_ = languages; | 204 languages_ = languages; |
| 179 | 205 |
| 180 CGFloat bubble_width = NSWidth([window_ frame]); | 206 CGFloat bubble_width = NSWidth([window_ frame]); |
| 181 | |
| 182 // Reset frame size when bubble is hidden. | |
| 183 if (state_ == kBubbleHidden) { | 207 if (state_ == kBubbleHidden) { |
| 184 is_expanded_ = false; | 208 DCHECK_EQ(ui::kWindowSizeDeterminedLater.size.width, |
| 185 NSRect frame = [window_ frame]; | 209 [window_ frame].size.width); |
| 186 frame.size = ui::kWindowSizeDeterminedLater.size; | 210 DCHECK_EQ(ui::kWindowSizeDeterminedLater.size.height, |
| 187 [window_ setFrame:frame display:NO]; | 211 [window_ frame].size.height); |
| 188 bubble_width = NSWidth(CalculateWindowFrame(/*expand=*/false)); | 212 bubble_width = NSWidth(CalculateWindowFrame(/*expand=*/false)); |
| 189 } | 213 } |
| 190 | 214 |
| 191 int text_width = static_cast<int>(bubble_width - | 215 int text_width = static_cast<int>(bubble_width - |
| 192 kBubbleViewTextPositionX - | 216 kBubbleViewTextPositionX - |
| 193 kTextPadding); | 217 kTextPadding); |
| 194 | 218 |
| 195 // Scale from view to window coordinates before eliding URL string. | 219 // Scale from view to window coordinates before eliding URL string. |
| 196 NSSize scaled_width = NSMakeSize(text_width, 0); | 220 NSSize scaled_width = NSMakeSize(text_width, 0); |
| 197 scaled_width = [[parent_ contentView] convertSize:scaled_width fromView:nil]; | 221 scaled_width = [[parent_ contentView] convertSize:scaled_width fromView:nil]; |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 444 [window_ orderFront:nil]; | 468 [window_ orderFront:nil]; |
| 445 [parent_ addChildWindow:window_ ordered:NSWindowAbove]; | 469 [parent_ addChildWindow:window_ ordered:NSWindowAbove]; |
| 446 | 470 |
| 447 [[window_ contentView] setThemeProvider:parent_]; | 471 [[window_ contentView] setThemeProvider:parent_]; |
| 448 } | 472 } |
| 449 | 473 |
| 450 void StatusBubbleMac::Detach() { | 474 void StatusBubbleMac::Detach() { |
| 451 DCHECK(is_attached()); | 475 DCHECK(is_attached()); |
| 452 | 476 |
| 453 // Magic setFrame: See http://crbug.com/58506 and http://crrev.com/3564021 . | 477 // Magic setFrame: See http://crbug.com/58506 and http://crrev.com/3564021 . |
| 454 [window_ setFrame:CalculateWindowFrame(/*expand=*/false) display:NO]; | 478 // TODO(rohitrao): Does the frame size actually matter here? Can we always |
| 479 // set it to kWindowSizeDeterminedLater? |
| 480 NSRect frame = [window_ frame]; |
| 481 frame.size = ui::kWindowSizeDeterminedLater.size; |
| 482 if (state_ != kBubbleHidden) { |
| 483 frame = CalculateWindowFrame(/*expand=*/false); |
| 484 } |
| 485 [window_ setFrame:frame display:NO]; |
| 455 [parent_ removeChildWindow:window_]; // See crbug.com/28107 ... | 486 [parent_ removeChildWindow:window_]; // See crbug.com/28107 ... |
| 456 [window_ orderOut:nil]; // ... and crbug.com/29054. | 487 [window_ orderOut:nil]; // ... and crbug.com/29054. |
| 457 | 488 |
| 458 [[window_ contentView] setThemeProvider:nil]; | 489 [[window_ contentView] setThemeProvider:nil]; |
| 459 } | 490 } |
| 460 | 491 |
| 461 void StatusBubbleMac::AnimationDidStop() { | 492 void StatusBubbleMac::AnimationDidStop() { |
| 462 DCHECK([NSThread isMainThread]); | 493 DCHECK([NSThread isMainThread]); |
| 463 DCHECK(state_ == kBubbleShowingFadeIn || state_ == kBubbleHidingFadeOut); | 494 DCHECK(state_ == kBubbleShowingFadeIn || state_ == kBubbleHidingFadeOut); |
| 464 DCHECK(is_attached()); | 495 DCHECK(is_attached()); |
| 465 | 496 |
| 466 if (state_ == kBubbleShowingFadeIn) { | 497 if (state_ == kBubbleShowingFadeIn) { |
| 467 DCHECK_EQ([[window_ animator] alphaValue], kBubbleOpacity); | 498 DCHECK_EQ([[window_ animator] alphaValue], kBubbleOpacity); |
| 468 SetState(kBubbleShown); | 499 SetState(kBubbleShown); |
| 469 } else { | 500 } else { |
| 470 DCHECK_EQ([[window_ animator] alphaValue], 0.0); | 501 DCHECK_EQ([[window_ animator] alphaValue], 0.0); |
| 471 SetState(kBubbleHidden); | 502 SetState(kBubbleHidden); |
| 472 } | 503 } |
| 473 } | 504 } |
| 474 | 505 |
| 475 void StatusBubbleMac::SetState(StatusBubbleState state) { | 506 void StatusBubbleMac::SetState(StatusBubbleState state) { |
| 476 if (state == state_) | 507 if (state == state_) |
| 477 return; | 508 return; |
| 478 | 509 |
| 479 if (state == kBubbleHidden) { | 510 if (state == kBubbleHidden) { |
| 511 is_expanded_ = false; |
| 512 |
| 480 // When hidden (with alpha of 0), make the window have the minimum size, | 513 // When hidden (with alpha of 0), make the window have the minimum size, |
| 481 // while still keeping the same origin. It's important to not set the | 514 // while still keeping the same origin. It's important to not set the |
| 482 // origin to 0,0 as that will cause the window to use more space in | 515 // origin to 0,0 as that will cause the window to use more space in |
| 483 // Expose/Mission Control. See http://crbug.com/81969. | 516 // Expose/Mission Control. See http://crbug.com/81969. |
| 484 // | 517 // |
| 485 // Also, doing it this way instead of detaching the window avoids bugs with | 518 // Also, doing it this way instead of detaching the window avoids bugs with |
| 486 // Spaces and Cmd-`. See http://crbug.com/31821 and http://crbug.com/61629. | 519 // Spaces and Cmd-`. See http://crbug.com/31821 and http://crbug.com/61629. |
| 487 NSRect frame = [window_ frame]; | 520 NSRect frame = [window_ frame]; |
| 488 frame.size = ui::kWindowSizeDeterminedLater.size; | 521 frame.size = ui::kWindowSizeDeterminedLater.size; |
| 489 [window_ setFrame:frame display:YES]; | 522 [window_ setFrame:frame display:YES]; |
| (...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 715 [NSAnimationContext beginGrouping]; | 748 [NSAnimationContext beginGrouping]; |
| 716 [[NSAnimationContext currentContext] setDuration:kExpansionDurationSeconds]; | 749 [[NSAnimationContext currentContext] setDuration:kExpansionDurationSeconds]; |
| 717 [[window_ animator] setFrame:actual_window_frame display:YES]; | 750 [[window_ animator] setFrame:actual_window_frame display:YES]; |
| 718 [NSAnimationContext endGrouping]; | 751 [NSAnimationContext endGrouping]; |
| 719 } | 752 } |
| 720 | 753 |
| 721 void StatusBubbleMac::UpdateSizeAndPosition() { | 754 void StatusBubbleMac::UpdateSizeAndPosition() { |
| 722 if (!window_) | 755 if (!window_) |
| 723 return; | 756 return; |
| 724 | 757 |
| 725 // Hidden bubbles always have size equal to ui::kWindowSizeDeterminedLater. | 758 // There is no need to update the size if the bubble is hidden. |
| 726 if (state_ == kBubbleHidden) { | 759 if (state_ == kBubbleHidden) { |
| 727 NSRect frame = [window_ frame]; | 760 // Verify that hidden bubbles always have size equal to |
| 728 frame.size = ui::kWindowSizeDeterminedLater.size; | 761 // ui::kWindowSizeDeterminedLater. |
| 729 [window_ setFrame:frame display:YES]; | 762 DCHECK_EQ(ui::kWindowSizeDeterminedLater.size.width, |
| 763 [window_ frame].size.width); |
| 764 DCHECK_EQ(ui::kWindowSizeDeterminedLater.size.height, |
| 765 [window_ frame].size.height); |
| 730 return; | 766 return; |
| 731 } | 767 } |
| 732 | 768 |
| 733 SetFrameAvoidingMouse(CalculateWindowFrame(/*expand=*/false), | 769 SetFrameAvoidingMouse(CalculateWindowFrame(/*expand=*/false), |
| 734 GetMouseLocation()); | 770 GetMouseLocation()); |
| 735 } | 771 } |
| 736 | 772 |
| 737 void StatusBubbleMac::SwitchParentWindow(NSWindow* parent) { | 773 void StatusBubbleMac::SwitchParentWindow(NSWindow* parent) { |
| 738 DCHECK(parent); | 774 DCHECK(parent); |
| 739 DCHECK(is_attached()); | 775 DCHECK(is_attached()); |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 788 } | 824 } |
| 789 | 825 |
| 790 // Round the top corners when the bubble is below the parent window. | 826 // Round the top corners when the bubble is below the parent window. |
| 791 if (NSMinY(window_frame) < NSMinY(parent_frame)) { | 827 if (NSMinY(window_frame) < NSMinY(parent_frame)) { |
| 792 corner_flags |= kRoundedTopLeftCorner | kRoundedTopRightCorner; | 828 corner_flags |= kRoundedTopLeftCorner | kRoundedTopRightCorner; |
| 793 } | 829 } |
| 794 } | 830 } |
| 795 | 831 |
| 796 return corner_flags; | 832 return corner_flags; |
| 797 } | 833 } |
| OLD | NEW |