summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authormirandac@google.com <mirandac@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-03 17:23:00 +0000
committermirandac@google.com <mirandac@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-03 17:23:00 +0000
commit9deebf20b712a0b1701a3d508a4a85528e05486e (patch)
tree156e1ffdef4cc8b88f120de31e541c960afa4837 /chrome/browser
parentb62f11e7644f25e88b5aff178b566f562dea9445 (diff)
downloadchromium_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.h1
-rw-r--r--chrome/browser/cocoa/status_bubble_mac.mm4
-rw-r--r--chrome/browser/gtk/status_bubble_gtk.cc4
-rw-r--r--chrome/browser/gtk/status_bubble_gtk.h2
-rw-r--r--chrome/browser/status_bubble.h3
-rw-r--r--chrome/browser/views/status_bubble_views.cc181
-rw-r--r--chrome/browser/views/status_bubble_views.h38
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);
};