diff options
author | mirandac@chromium.org <mirandac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-17 19:29:02 +0000 |
---|---|---|
committer | mirandac@chromium.org <mirandac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-17 19:29:02 +0000 |
commit | 3e5fe0f02d7347d2d913f237ad8f06cb8a9defec (patch) | |
tree | e52fd781d78d17075e486c7e059b2d3145d3bab4 /chrome/browser/cocoa | |
parent | ace802341456e24de9091f5cc7ae9b38525e60e6 (diff) | |
download | chromium_src-3e5fe0f02d7347d2d913f237ad8f06cb8a9defec.zip chromium_src-3e5fe0f02d7347d2d913f237ad8f06cb8a9defec.tar.gz chromium_src-3e5fe0f02d7347d2d913f237ad8f06cb8a9defec.tar.bz2 |
Make status bubble expand on Mac to fit URLs longer than the standard width.
BUG=43193, 55770
TEST=status bubble resizes on Mac to fit URLs longer than standard bubble size.
Review URL: http://codereview.chromium.org/3437002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@59832 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa')
-rw-r--r-- | chrome/browser/cocoa/status_bubble_mac.h | 27 | ||||
-rw-r--r-- | chrome/browser/cocoa/status_bubble_mac.mm | 124 | ||||
-rw-r--r-- | chrome/browser/cocoa/status_bubble_mac_unittest.mm | 50 |
3 files changed, 192 insertions, 9 deletions
diff --git a/chrome/browser/cocoa/status_bubble_mac.h b/chrome/browser/cocoa/status_bubble_mac.h index c1a722f..7ec4b51 100644 --- a/chrome/browser/cocoa/status_bubble_mac.h +++ b/chrome/browser/cocoa/status_bubble_mac.h @@ -14,6 +14,7 @@ #include "base/string16.h" #include "base/task.h" #include "chrome/browser/status_bubble.h" +#include "googleurl/src/gurl.h" class GURL; class StatusBubbleMacTest; @@ -55,6 +56,9 @@ class StatusBubbleMac : public StatusBubble { // delegate, which is an Objective-C object. void AnimationDidStop(CAAnimation* animation, bool finished); + // Expand the bubble to fit a URL too long for the standard bubble size. + void ExpandBubble(); + private: friend class StatusBubbleMacTest; @@ -103,11 +107,18 @@ class StatusBubbleMac : public StatusBubble { void StartShowing(); void StartHiding(); + // Cancel the expansion timer. + void CancelExpandTimer(); + // The timer factory used for show and hide delay timers. ScopedRunnableMethodFactory<StatusBubbleMac> timer_factory_; - // Calculate the appropriate frame for the status bubble window. - NSRect CalculateWindowFrame(); + // The timer factory used for the expansion delay timer. + ScopedRunnableMethodFactory<StatusBubbleMac> expand_timer_factory_; + + // Calculate the appropriate frame for the status bubble window. If + // |expanded_width|, use entire width of parent frame. + NSRect CalculateWindowFrame(bool expanded_width); // The window we attach ourselves to. NSWindow* parent_; // WEAK @@ -133,6 +144,18 @@ class StatusBubbleMac : public StatusBubble { // true for testing. bool immediate_; + // True if the status bubble has been expanded. If the bubble is in the + // expanded state and encounters a new URL, change size immediately, + // with no hover delay. + bool is_expanded_; + + // The original, non-elided URL. + GURL url_; + + // Needs to be passed to ElideURL if the original URL string is wider than + // the standard bubble width. + string16 languages_; + DISALLOW_COPY_AND_ASSIGN(StatusBubbleMac); }; diff --git a/chrome/browser/cocoa/status_bubble_mac.mm b/chrome/browser/cocoa/status_bubble_mac.mm index 98d3e07..4b51d56 100644 --- a/chrome/browser/cocoa/status_bubble_mac.mm +++ b/chrome/browser/cocoa/status_bubble_mac.mm @@ -14,7 +14,7 @@ #include "base/utf_string_conversions.h" #import "chrome/browser/cocoa/bubble_view.h" #include "gfx/point.h" -#include "googleurl/src/gurl.h" +#include "net/base/net_util.h" #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h" #import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" @@ -52,6 +52,9 @@ const NSTimeInterval kHideFadeOutDurationSeconds = 0.200; const NSTimeInterval kMinimumTimeInterval = std::numeric_limits<NSTimeInterval>::min(); +// How quickly the status bubble should expand, in seconds. +const CGFloat kExpansionDuration = 0.125; + } // namespace @interface StatusBubbleAnimationDelegate : NSObject { @@ -93,13 +96,15 @@ const NSTimeInterval kMinimumTimeInterval = StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) : ALLOW_THIS_IN_INITIALIZER_LIST(timer_factory_(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(expand_timer_factory_(this)), parent_(parent), delegate_(delegate), window_(nil), status_text_(nil), url_text_(nil), state_(kBubbleHidden), - immediate_(false) { + immediate_(false), + is_expanded_(false) { } StatusBubbleMac::~StatusBubbleMac() { @@ -120,20 +125,55 @@ void StatusBubbleMac::SetStatus(const string16& status) { } void StatusBubbleMac::SetURL(const GURL& url, const string16& languages) { + url_ = url; + languages_ = languages; + Create(); NSRect frame = [window_ frame]; - int text_width = static_cast<int>(frame.size.width - + + // Reset frame size when bubble is hidden. + if (state_ == kBubbleHidden) { + is_expanded_ = false; + frame.size.width = NSWidth(CalculateWindowFrame(/*expand=*/false)); + [window_ setFrame:frame display:NO]; + } + + int text_width = static_cast<int>(NSWidth(frame) - kBubbleViewTextPositionX - kTextPadding); + + // Scale from view to window coordinates before eliding URL string. + NSSize scaled_width = NSMakeSize(text_width, 0); + scaled_width = [[parent_ contentView] convertSize:scaled_width fromView:nil]; + text_width = static_cast<int>(scaled_width.width); NSFont* font = [[window_ contentView] font]; gfx::Font font_chr(base::SysNSStringToWide([font fontName]), [font pointSize]); + string16 original_url_text = net::FormatUrl(url, UTF16ToUTF8(languages)); string16 status = WideToUTF16(gfx::ElideUrl(url, font_chr, text_width, UTF16ToWideHack(languages))); SetText(status, true); + + // In testing, don't use animation. When ExpandBubble is tested, it is + // called explicitly. + if (immediate_) + return; + else + CancelExpandTimer(); + + // If the bubble has been expanded, the user has already hovered over a link + // to trigger the expanded state. Don't wait to change the bubble in this + // case -- immediately expand or contract to fit the URL. + if (is_expanded_ && !url.is_empty()) { + ExpandBubble(); + } else if (original_url_text.length() > status.length()) { + MessageLoop::current()->PostDelayedTask(FROM_HERE, + expand_timer_factory_.NewRunnableMethod( + &StatusBubbleMac::ExpandBubble), kExpandHoverDelay); + } } void StatusBubbleMac::SetText(const string16& text, bool is_url) { @@ -179,6 +219,8 @@ void StatusBubbleMac::SetText(const string16& text, bool is_url) { void StatusBubbleMac::Hide() { CancelTimer(); + CancelExpandTimer(); + is_expanded_ = false; bool fade_out = false; if (state_ == kBubbleHidingFadeOut || state_ == kBubbleShowingFadeIn) { @@ -201,6 +243,17 @@ void StatusBubbleMac::Hide() { SetState(kBubbleHidden); } + // Stop any width animation and reset the bubble size. + if (!immediate_) { + [NSAnimationContext beginGrouping]; + [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval]; + [[window_ animator] setFrame:CalculateWindowFrame(/*expand=*/false) + display:NO]; + [NSAnimationContext endGrouping]; + } else { + [window_ setFrame:CalculateWindowFrame(/*expand=*/false) display:NO]; + } + [status_text_ release]; status_text_ = nil; [url_text_ release]; @@ -298,7 +351,7 @@ void StatusBubbleMac::Create() { return; // TODO(avi):fix this for RTL - NSRect window_rect = CalculateWindowFrame(); + NSRect window_rect = CalculateWindowFrame(/*expand=*/false); // initWithContentRect has origin in screen coords and size in scaled window // coordinates. window_rect.size = @@ -535,11 +588,61 @@ void StatusBubbleMac::StartHiding() { // earlier request. } +void StatusBubbleMac::CancelExpandTimer() { + DCHECK([NSThread isMainThread]); + expand_timer_factory_.RevokeAll(); +} + +void StatusBubbleMac::ExpandBubble() { + // Calculate the width available for expanded and standard bubbles. + NSRect window_frame = CalculateWindowFrame(/*expand=*/true); + CGFloat max_bubble_width = NSWidth(window_frame); + CGFloat standard_bubble_width = + NSWidth(CalculateWindowFrame(/*expand=*/false)); + + // Generate the URL string that fits in the expanded bubble. + NSFont* font = [[window_ contentView] font]; + gfx::Font font_chr(base::SysNSStringToWide([font fontName]), + [font pointSize]); + string16 expanded_url = WideToUTF16(gfx::ElideUrl(url_, font_chr, + max_bubble_width, UTF16ToWide(languages_))); + + // Scale width from gfx::Font in view coordinates to window coordinates. + int required_width_for_string = + font_chr.GetStringWidth(UTF16ToWide(expanded_url)) + + kTextPadding * 2 + kBubbleViewTextPositionX; + NSSize scaled_width = NSMakeSize(required_width_for_string, 0); + scaled_width = [[parent_ contentView] convertSize:scaled_width toView:nil]; + required_width_for_string = scaled_width.width; + + // The expanded width must be at least as wide as the standard width, but no + // wider than the maximum width for its parent frame. + int expanded_bubble_width = + std::max(standard_bubble_width, + std::min(max_bubble_width, + static_cast<CGFloat>(required_width_for_string))); + + SetText(expanded_url, true); + is_expanded_ = true; + window_frame.size.width = expanded_bubble_width; + + // In testing, don't do any animation. + if (immediate_) { + [window_ setFrame:window_frame display:YES]; + return; + } + + [NSAnimationContext beginGrouping]; + [[NSAnimationContext currentContext] setDuration:kExpansionDuration]; + [[window_ animator] setFrame:window_frame display:YES]; + [NSAnimationContext endGrouping]; +} + void StatusBubbleMac::UpdateSizeAndPosition() { if (!window_) return; - [window_ setFrame:CalculateWindowFrame() display:YES]; + [window_ setFrame:CalculateWindowFrame(/*expand=*/false) display:YES]; } void StatusBubbleMac::SwitchParentWindow(NSWindow* parent) { @@ -560,7 +663,7 @@ void StatusBubbleMac::SwitchParentWindow(NSWindow* parent) { UpdateSizeAndPosition(); } -NSRect StatusBubbleMac::CalculateWindowFrame() { +NSRect StatusBubbleMac::CalculateWindowFrame(bool expanded_width) { DCHECK(parent_); NSSize size = NSMakeSize(0, kWindowHeight); @@ -568,6 +671,13 @@ NSRect StatusBubbleMac::CalculateWindowFrame() { NSRect rect = [parent_ frame]; rect.size.height = size.height; - rect.size.width = static_cast<int>(kWindowWidthPercent * rect.size.width); + + const int kScrollbarWidth = 16; // see BrowserWindowController. + if (expanded_width) { + rect.size.width = NSWidth(rect) - kScrollbarWidth; + } else { + rect.size.width = static_cast<int>(kWindowWidthPercent * NSWidth(rect)); + } + return rect; } diff --git a/chrome/browser/cocoa/status_bubble_mac_unittest.mm b/chrome/browser/cocoa/status_bubble_mac_unittest.mm index d1e2b73..0d87a0c 100644 --- a/chrome/browser/cocoa/status_bubble_mac_unittest.mm +++ b/chrome/browser/cocoa/status_bubble_mac_unittest.mm @@ -482,3 +482,53 @@ TEST_F(StatusBubbleMacTest, MovingWindowUpdatesPosition) { child = GetWindow(); EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin)); } + +TEST_F(StatusBubbleMacTest, ExpandBubble) { + NSWindow* window = test_window(); + ASSERT_TRUE(window); + NSRect window_frame = [window frame]; + window_frame.size.width = 600.0; + [window setFrame:window_frame display:YES]; + + // Check basic expansion + bubble_->SetStatus(UTF8ToUTF16("Showing")); + EXPECT_TRUE(IsVisible()); + bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"), + string16()); + EXPECT_NSEQ(@"battersbox.com/peter_paul_and_\u2026", GetURLText()); + bubble_->ExpandBubble(); + EXPECT_TRUE(IsVisible()); + EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText()); + bubble_->Hide(); + + // Make sure bubble resets after hide. + bubble_->SetStatus(UTF8ToUTF16("Showing")); + bubble_->SetURL(GURL("http://www.snickersnee.com/pioneer_fishstix.html"), + string16()); + EXPECT_NSEQ(@"snickersnee.com/pioneer_fishstix\u2026", GetURLText()); + // ...and that it expands again properly. + bubble_->ExpandBubble(); + EXPECT_NSEQ(@"www.snickersnee.com/pioneer_fishstix.html", GetURLText()); + // ...again, again! + bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"), + string16()); + bubble_->ExpandBubble(); + EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText()); + bubble_->Hide(); + + window_frame = [window frame]; + window_frame.size.width = 300.0; + [window setFrame:window_frame display:YES]; + + // Very long URL's will be cut off even in the expanded state. + bubble_->SetStatus(UTF8ToUTF16("Showing")); + bubble_->SetURL(GURL( + "http://www.diewahrscheinlichlaengstepralinederwelt.com/duuuuplo.html"), + string16()); + EXPECT_NSEQ(@"diewahrscheinli\u2026", GetURLText()); + bubble_->ExpandBubble(); + EXPECT_NSEQ(@"diewahrscheinlichlaengstepralinederwelt.com/duu\u2026", + GetURLText()); +} + + |