diff options
author | mirandac@google.com <mirandac@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-03 17:23:00 +0000 |
---|---|---|
committer | mirandac@google.com <mirandac@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-03 17:23:00 +0000 |
commit | 9deebf20b712a0b1701a3d508a4a85528e05486e (patch) | |
tree | 156e1ffdef4cc8b88f120de31e541c960afa4837 /chrome/browser | |
parent | b62f11e7644f25e88b5aff178b566f562dea9445 (diff) | |
download | chromium_src-9deebf20b712a0b1701a3d508a4a85528e05486e.zip chromium_src-9deebf20b712a0b1701a3d508a4a85528e05486e.tar.gz chromium_src-9deebf20b712a0b1701a3d508a4a85528e05486e.tar.bz2 |
Change hover time to 1600 ms, and resize more quickly for smaller width change.
BUG= 1455
TEST= hover over link which is too long for status bubble. bubble should expand.
Review URL: http://codereview.chromium.org/149474
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46235 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/cocoa/status_bubble_mac.h | 1 | ||||
-rw-r--r-- | chrome/browser/cocoa/status_bubble_mac.mm | 4 | ||||
-rw-r--r-- | chrome/browser/gtk/status_bubble_gtk.cc | 4 | ||||
-rw-r--r-- | chrome/browser/gtk/status_bubble_gtk.h | 2 | ||||
-rw-r--r-- | chrome/browser/status_bubble.h | 3 | ||||
-rw-r--r-- | chrome/browser/views/status_bubble_views.cc | 181 | ||||
-rw-r--r-- | chrome/browser/views/status_bubble_views.h | 38 |
7 files changed, 224 insertions, 9 deletions
diff --git a/chrome/browser/cocoa/status_bubble_mac.h b/chrome/browser/cocoa/status_bubble_mac.h index e1dadb9..d01aee2 100644 --- a/chrome/browser/cocoa/status_bubble_mac.h +++ b/chrome/browser/cocoa/status_bubble_mac.h @@ -38,6 +38,7 @@ class StatusBubbleMac : public StatusBubble { virtual void Hide(); virtual void MouseMoved(const gfx::Point& location, bool left_content); virtual void UpdateDownloadShelfVisibility(bool visible); + virtual void SetBubbleWidth(int width); // Mac-specific method: Update the size and position of the status bubble to // match the parent window. Safe to call even when the status bubble does not diff --git a/chrome/browser/cocoa/status_bubble_mac.mm b/chrome/browser/cocoa/status_bubble_mac.mm index 7906243..62b0ae0 100644 --- a/chrome/browser/cocoa/status_bubble_mac.mm +++ b/chrome/browser/cocoa/status_bubble_mac.mm @@ -292,6 +292,10 @@ void StatusBubbleMac::MouseMoved( void StatusBubbleMac::UpdateDownloadShelfVisibility(bool visible) { } +void StatusBubbleMac::SetBubbleWidth(int width) { + NOTIMPLEMENTED(); +} + void StatusBubbleMac::Create() { if (window_) return; diff --git a/chrome/browser/gtk/status_bubble_gtk.cc b/chrome/browser/gtk/status_bubble_gtk.cc index fa7742b3..960797f 100644 --- a/chrome/browser/gtk/status_bubble_gtk.cc +++ b/chrome/browser/gtk/status_bubble_gtk.cc @@ -210,6 +210,10 @@ void StatusBubbleGtk::UpdateDownloadShelfVisibility(bool visible) { download_shelf_is_visible_ = visible; } +void StatusBubbleGtk::SetBubbleWidth(int width) { + NOTIMPLEMENTED(); +} + void StatusBubbleGtk::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { diff --git a/chrome/browser/gtk/status_bubble_gtk.h b/chrome/browser/gtk/status_bubble_gtk.h index c15bcd1..c648b8e 100644 --- a/chrome/browser/gtk/status_bubble_gtk.h +++ b/chrome/browser/gtk/status_bubble_gtk.h @@ -45,6 +45,8 @@ class StatusBubbleGtk : public StatusBubble, // the download shelf, when it is visible. virtual void UpdateDownloadShelfVisibility(bool visible); + virtual void SetBubbleWidth(int width); + // Overridden from NotificationObserver: void Observe(NotificationType type, const NotificationSource& source, diff --git a/chrome/browser/status_bubble.h b/chrome/browser/status_bubble.h index 81379d0..43851be 100644 --- a/chrome/browser/status_bubble.h +++ b/chrome/browser/status_bubble.h @@ -49,6 +49,9 @@ class StatusBubble { // This is used by to ensure that the status bubble does not obscure // the download shelf, when it is visible. virtual void UpdateDownloadShelfVisibility(bool visible) = 0; + + // Allow StatusView animation to set width of StatusBubble. + virtual void SetBubbleWidth(int width) = 0; }; #endif // CHROME_BROWSER_STATUS_BUBBLE_H_ diff --git a/chrome/browser/views/status_bubble_views.cc b/chrome/browser/views/status_bubble_views.cc index a76c6e2..f020ef4 100644 --- a/chrome/browser/views/status_bubble_views.cc +++ b/chrome/browser/views/status_bubble_views.cc @@ -24,6 +24,7 @@ #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkRect.h" #include "views/controls/label.h" +#include "views/controls/scrollbar/native_scroll_bar.h" #include "views/screen.h" #include "views/widget/root_view.h" #include "views/widget/widget.h" @@ -59,6 +60,10 @@ static const int kShowFadeDurationMS = 120; static const int kHideFadeDurationMS = 200; static const int kFramerate = 25; +// How long each expansion step should take. +static const int kMinExpansionStepDurationMS = 20; +static const int kMaxExpansionStepDurationMS = 150; + // View ----------------------------------------------------------------------- // StatusView manages the display of the bubble, applying text changes and // fading in or out the bubble as required. @@ -105,9 +110,13 @@ class StatusBubbleViews::StatusView : public views::Label, }; // Set the bubble text to a certain value, hides the bubble if text is - // an empty string. + // an empty string. Do not trigger animation sequence. void SetText(const std::wstring& text); + // Set the bubble text to a certain value, or hide the bubble if text is + // an empty string. Start animation sequence. + void SetTextAndAnimate(const std::wstring& text); + BubbleStage GetState() const { return stage_; } void SetStyle(BubbleStyle style); @@ -171,6 +180,12 @@ class StatusBubbleViews::StatusView : public views::Label, }; void StatusBubbleViews::StatusView::SetText(const std::wstring& text) { + text_ = text; + SchedulePaint(); +} + +void StatusBubbleViews::StatusView::SetTextAndAnimate( + const std::wstring& text) { if (text.empty()) { // The string was empty. StartHiding(); @@ -446,6 +461,83 @@ void StatusBubbleViews::StatusView::Paint(gfx::Canvas* canvas) { body_bounds.height()); } +// StatusViewExpander --------------------------------------------------------- +// Manages the expansion and contraction of the status bubble as it accommodates +// URLs too long to fit in the standard bubble. Changes are passed through the +// StatusView to paint. +class StatusBubbleViews::StatusViewExpander : public Animation, + public AnimationDelegate { + public: + StatusViewExpander(StatusBubble* status_bubble, + StatusView* status_view) + : ALLOW_THIS_IN_INITIALIZER_LIST(Animation(kFramerate, this)), + status_bubble_(status_bubble), + status_view_(status_view), + expansion_start_(0), + expansion_end_(0) { + } + + // Manage the expansion of the bubble. + void StartExpansion(std::wstring expanded_text, int current_width, + int expansion_end); + + // Set width of fully expanded bubble. + void SetExpandedWidth(int expanded_width); + + private: + // Animation functions. + int GetCurrentBubbleWidth(); + void SetBubbleWidth(int width); + void AnimateToState(double state); + void AnimationEnded(const Animation* animation); + + // Manager that owns us. + StatusBubble* status_bubble_; + + // Change the bounds and text of this view. + StatusView* status_view_; + + // Text elided (if needed) to fit maximum status bar width. + std::wstring expanded_text_; + + // Widths at expansion start and end. + int expansion_start_; + int expansion_end_; +}; + +void StatusBubbleViews::StatusViewExpander::AnimateToState(double state) { + SetBubbleWidth(GetCurrentBubbleWidth()); +} + +void StatusBubbleViews::StatusViewExpander::AnimationEnded( + const Animation* animation) { + SetBubbleWidth(expansion_end_); + status_view_->SetText(expanded_text_); +} + +void StatusBubbleViews::StatusViewExpander::StartExpansion( + std::wstring expanded_text, int expansion_start, + int expansion_end) { + expanded_text_ = expanded_text; + expansion_start_ = expansion_start; + expansion_end_ = expansion_end; + int min_duration = std::max(kMinExpansionStepDurationMS, + static_cast<int>(kMaxExpansionStepDurationMS * + (expansion_end - expansion_start) / 100.0)); + SetDuration(std::min(kMaxExpansionStepDurationMS, min_duration)); + Start(); +} + +int StatusBubbleViews::StatusViewExpander::GetCurrentBubbleWidth() { + return static_cast<int>(expansion_start_ + + (expansion_end_ - expansion_start_) * Animation::GetCurrentValue()); +} + +void StatusBubbleViews::StatusViewExpander::SetBubbleWidth(int width) { + status_bubble_->SetBubbleWidth(width); + status_view_->SchedulePaint(); +} + // StatusBubble --------------------------------------------------------------- const int StatusBubbleViews::kShadowThickness = 1; @@ -456,10 +548,14 @@ StatusBubbleViews::StatusBubbleViews(views::Widget* frame) opacity_(0), frame_(frame), view_(NULL), - download_shelf_is_visible_(false) { + download_shelf_is_visible_(false), + is_expanded_(false), + ALLOW_THIS_IN_INITIALIZER_LIST(expand_timer_factory_(this)) { + expand_view_.reset(); } StatusBubbleViews::~StatusBubbleViews() { + CancelExpandTimer(); if (popup_.get()) popup_->CloseNow(); } @@ -471,6 +567,8 @@ void StatusBubbleViews::Init() { Widget::NotDeleteOnDestroy)); if (!view_) view_ = new StatusView(this, popup_.get(), frame_->GetThemeProvider()); + if (!expand_view_.get()) + expand_view_.reset(new StatusViewExpander(this, view_)); popup_->SetOpacity(0x00); popup_->Init(frame_->GetNativeView(), gfx::Rect()); popup_->SetContentsView(view_); @@ -524,16 +622,18 @@ void StatusBubbleViews::SetStatus(const std::wstring& status_text) { Init(); status_text_ = status_text; if (!status_text_.empty()) { - view_->SetText(status_text); + view_->SetTextAndAnimate(status_text); view_->Show(); } else if (!url_text_.empty()) { - view_->SetText(url_text_); + view_->SetTextAndAnimate(url_text_); } else { - view_->SetText(std::wstring()); + view_->SetTextAndAnimate(std::wstring()); } } void StatusBubbleViews::SetURL(const GURL& url, const std::wstring& languages) { + languages_ = languages; + url_ = url; if (size_.IsEmpty()) return; // We have no bounds, don't attempt to show the popup. @@ -544,17 +644,25 @@ void StatusBubbleViews::SetURL(const GURL& url, const std::wstring& languages) { if (url.is_empty() && !status_text_.empty()) { url_text_ = std::wstring(); if (IsFrameVisible()) - view_->SetText(status_text_); + view_->SetTextAndAnimate(status_text_); return; } + // Reset expansion state only when bubble is completely hidden. + if (view_->GetState() == StatusView::BUBBLE_HIDDEN) { + is_expanded_ = false; + SetBubbleWidth(GetStandardStatusBubbleWidth()); + } + // Set Elided Text corresponding to the GURL object. gfx::Rect popup_bounds; popup_->GetBounds(&popup_bounds, true); int text_width = static_cast<int>(popup_bounds.width() - (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1); url_text_ = gfx::ElideUrl(url, view_->Label::font(), text_width, - languages); + languages); + + std::wstring original_url_text = net::FormatUrl(url, languages); // An URL is always treated as a left-to-right string. On right-to-left UIs // we need to explicitly mark the URL as LTR to make sure it is displayed @@ -562,8 +670,20 @@ void StatusBubbleViews::SetURL(const GURL& url, const std::wstring& languages) { if (base::i18n::IsRTL() && !url_text_.empty()) base::i18n::WrapStringWithLTRFormatting(&url_text_); - if (IsFrameVisible()) - view_->SetText(url_text_); + if (IsFrameVisible()) { + view_->SetTextAndAnimate(url_text_); + + CancelExpandTimer(); + + // If bubble is already in expanded state, shift to adjust to new text + // size (shrinking or expanding). Otherwise delay. + if (is_expanded_ && !url.is_empty()) + ExpandBubble(); + else if (original_url_text.length() > url_text_.length()) + MessageLoop::current()->PostDelayedTask(FROM_HERE, + expand_timer_factory_.NewRunnableMethod( + &StatusBubbleViews::ExpandBubble), kExpandHoverDelay); + } } void StatusBubbleViews::Hide() { @@ -686,3 +806,46 @@ bool StatusBubbleViews::IsFrameVisible() { views::Window* window = frame_->GetWindow(); return !window || !window->IsMinimized(); } + +void StatusBubbleViews::ExpandBubble() { + // Elide URL to maximum possible size, then check actual length (it may + // still be too long to fit) before expanding bubble. + gfx::Rect popup_bounds; + popup_->GetBounds(&popup_bounds, true); + int max_status_bubble_width = GetMaxStatusBubbleWidth(); + url_text_ = gfx::ElideUrl(url_, view_->Label::font(), + max_status_bubble_width, languages_); + int expanded_bubble_width = + std::max(GetStandardStatusBubbleWidth(), + std::min(view_->Label::font().GetStringWidth(url_text_) + + (kShadowThickness * 2) + kTextPositionX + + kTextHorizPadding + 1, + max_status_bubble_width)); + is_expanded_ = true; + expand_view_->StartExpansion(url_text_, popup_bounds.width(), + expanded_bubble_width); +} + +int StatusBubbleViews::GetStandardStatusBubbleWidth() { + gfx::Rect frame_bounds; + frame_->GetBounds(&frame_bounds, false); + return frame_bounds.width() / 3; +} + +int StatusBubbleViews::GetMaxStatusBubbleWidth() { + gfx::Rect frame_bounds; + frame_->GetBounds(&frame_bounds, false); + return static_cast<int>(frame_bounds.width() - (kShadowThickness * 2) - + kTextPositionX - kTextHorizPadding - 1 - + views::NativeScrollBar::GetVerticalScrollBarWidth()); +} + +void StatusBubbleViews::SetBubbleWidth(int width) { + size_.set_width(width); + SetBounds(position_.x(), position_.y(), size_.width(), size_.height()); +} + +void StatusBubbleViews::CancelExpandTimer() { + if (!expand_timer_factory_.empty()) + expand_timer_factory_.RevokeAll(); +} diff --git a/chrome/browser/views/status_bubble_views.h b/chrome/browser/views/status_bubble_views.h index 7952ea9..4ff9544 100644 --- a/chrome/browser/views/status_bubble_views.h +++ b/chrome/browser/views/status_bubble_views.h @@ -7,7 +7,9 @@ #include "base/logging.h" #include "base/scoped_ptr.h" +#include "base/task.h" #include "chrome/browser/status_bubble.h" +#include "googleurl/src/gurl.h" #include "gfx/rect.h" class GURL; @@ -29,6 +31,9 @@ class StatusBubbleViews : public StatusBubble { // The combined vertical padding above and below the text. static const int kTotalVerticalPadding = 7; + // On hover, expand status bubble to fit long URL after this delay. + static const int kExpandHoverDelay = 1600; + explicit StatusBubbleViews(views::Widget* frame); ~StatusBubbleViews(); @@ -43,6 +48,9 @@ class StatusBubbleViews : public StatusBubble { // Set the bounds of the bubble relative to the browser window. void SetBounds(int x, int y, int w, int h); + // Set bubble to new width. + void SetBubbleWidth(int width); + // Overridden from StatusBubble: virtual void SetStatus(const std::wstring& status); virtual void SetURL(const GURL& url, const std::wstring& languages); @@ -52,6 +60,7 @@ class StatusBubbleViews : public StatusBubble { private: class StatusView; + class StatusViewExpander; // Initializes the popup and view. void Init(); @@ -63,12 +72,31 @@ class StatusBubbleViews : public StatusBubble { // Returns true if the frame_ is visible and not minimized. bool IsFrameVisible(); + // Expand bubble size to accommodate a long URL. + void ExpandBubble(); + + // Cancel all waiting expansion animations in the timer. + void CancelExpandTimer(); + + // Get the standard width for a status bubble in the current frame size. + int GetStandardStatusBubbleWidth(); + + // Get the maximum possible width for a status bubble in the current frame + // size. + int GetMaxStatusBubbleWidth(); + // The status text we want to display when there are no URLs to display. std::wstring status_text_; // The url we want to display when there is no status text to display. std::wstring url_text_; + // The original, non-elided URL. + GURL url_; + + // Used to elide the original URL again when we expand it. + std::wstring languages_; + // Position relative to the parent window. gfx::Point position_; gfx::Size size_; @@ -84,9 +112,19 @@ class StatusBubbleViews : public StatusBubble { views::Widget* frame_; StatusView* view_; + // Manages the expansion of a status bubble to fit a long URL. + scoped_ptr<StatusViewExpander> expand_view_; + // If the download shelf is visible, do not obscure it. bool download_shelf_is_visible_; + // If the bubble has already been expanded, and encounters a new URL, + // change size immediately, with no hover. + bool is_expanded_; + + // Times expansion of status bubble when URL is too long for standard width. + ScopedRunnableMethodFactory<StatusBubbleViews> expand_timer_factory_; + DISALLOW_COPY_AND_ASSIGN(StatusBubbleViews); }; |