172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/status_bubble_views.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/i18n/rtl.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/themes/theme_service.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "googleurl/src/gurl.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/theme_resources.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/net_util.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkPaint.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkPath.h"
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkRect.h"
213f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/animation_delegate.h"
223f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/linear_animation.h"
2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h"
2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/text/text_elider.h"
2572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas_skia.h"
2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/point.h"
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/label.h"
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/scrollbar/native_scroll_bar.h"
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/screen.h"
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/root_view.h"
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget.h"
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/window/window.h"
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing views::Widget;
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The alpha and color of the bubble's shadow.
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const SkColor kShadowColor = SkColorSetARGB(30, 0, 0, 0);
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The roundedness of the edges of our bubble.
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kBubbleCornerRadius = 4;
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// How close the mouse can get to the infobubble before it starts sliding
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// off-screen.
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kMousePadding = 20;
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The horizontal offset of the text within the status bubble, not including the
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// outer shadow ring.
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kTextPositionX = 3;
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The minimum horizontal space between the (right) end of the text and the edge
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// of the status bubble, not including the outer shadow ring.
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kTextHorizPadding = 1;
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Delays before we start hiding or showing the bubble after we receive a
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// show or hide request.
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kShowDelay = 80;
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kHideDelay = 250;
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// How long each fade should last for.
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kShowFadeDurationMS = 120;
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kHideFadeDurationMS = 200;
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kFramerate = 25;
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// How long each expansion step should take.
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kMinExpansionStepDurationMS = 20;
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kMaxExpansionStepDurationMS = 150;
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// View -----------------------------------------------------------------------
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// StatusView manages the display of the bubble, applying text changes and
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// fading in or out the bubble as required.
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass StatusBubbleViews::StatusView : public views::Label,
723f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                      public ui::LinearAnimation,
733f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                      public ui::AnimationDelegate {
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StatusView(StatusBubble* status_bubble, views::Widget* popup,
7672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen             ui::ThemeProvider* theme_provider)
773f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      : ALLOW_THIS_IN_INITIALIZER_LIST(ui::LinearAnimation(kFramerate, this)),
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        stage_(BUBBLE_HIDDEN),
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        style_(STYLE_STANDARD),
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        ALLOW_THIS_IN_INITIALIZER_LIST(timer_factory_(this)),
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        status_bubble_(status_bubble),
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        popup_(popup),
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        opacity_start_(0),
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        opacity_end_(0),
85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        theme_service_(theme_provider) {
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Font font(rb.GetFont(ResourceBundle::BaseFont));
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetFont(font);
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~StatusView() {
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Stop();
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CancelTimer();
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The bubble can be in one of many stages:
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  enum BubbleStage {
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BUBBLE_HIDDEN,         // Entirely BUBBLE_HIDDEN.
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BUBBLE_HIDING_FADE,    // In a fade-out transition.
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BUBBLE_HIDING_TIMER,   // Waiting before a fade-out.
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BUBBLE_SHOWING_TIMER,  // Waiting before a fade-in.
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BUBBLE_SHOWING_FADE,   // In a fade-in transition.
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BUBBLE_SHOWN           // Fully visible.
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  enum BubbleStyle {
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    STYLE_BOTTOM,
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    STYLE_FLOATING,
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    STYLE_STANDARD,
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    STYLE_STANDARD_RIGHT
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Set the bubble text to a certain value, hides the bubble if text is
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // an empty string.  Trigger animation sequence to display if
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |should_animate_open|.
1163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  void SetText(const string16& text, bool should_animate_open);
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BubbleStage GetState() const { return stage_; }
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void SetStyle(BubbleStyle style);
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BubbleStyle GetStyle() const { return style_; }
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Show the bubble instantly.
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Show();
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Hide the bubble instantly.
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Hide();
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Resets any timers we have. Typically called when the user moves a
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // mouse.
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void ResetTimer();
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  class InitialTimer;
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Manage the timers that control the delay before a fade begins or ends.
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void StartTimer(int time);
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void OnTimer();
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void CancelTimer();
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void RestartTimer(int delay);
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Manage the fades and starting and stopping the animations correctly.
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void StartFade(double start, double end, int duration);
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void StartHiding();
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void StartShowing();
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Animation functions.
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double GetCurrentOpacity();
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void SetOpacity(double opacity);
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void AnimateToState(double state);
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void AnimationEnded(const Animation* animation);
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
154dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  virtual void OnPaint(gfx::Canvas* canvas);
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BubbleStage stage_;
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BubbleStyle style_;
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ScopedRunnableMethodFactory<StatusBubbleViews::StatusView> timer_factory_;
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Manager, owns us.
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StatusBubble* status_bubble_;
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Handle to the widget that contains us.
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::Widget* popup_;
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The currently-displayed text.
1683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  string16 text_;
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Start and end opacities for the current transition - note that as a
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // fade-in can easily turn into a fade out, opacity_start_ is sometimes
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // a value between 0 and 1.
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double opacity_start_;
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double opacity_end_;
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Holds the theme provider of the frame that created us.
177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ui::ThemeProvider* theme_service_;
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid StatusBubbleViews::StatusView::SetText(const string16& text,
1813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                            bool should_animate_open) {
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (text.empty()) {
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // The string was empty.
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartHiding();
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We want to show the string.
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    text_ = text;
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (should_animate_open)
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      StartShowing();
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SchedulePaint();
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::Show() {
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Stop();
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CancelTimer();
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetOpacity(1.0);
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  popup_->Show();
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  stage_ = BUBBLE_SHOWN;
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PaintNow();
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::Hide() {
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Stop();
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CancelTimer();
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetOpacity(0.0);
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  text_.clear();
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  popup_->Hide();
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  stage_ = BUBBLE_HIDDEN;
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::StartTimer(int time) {
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!timer_factory_.empty())
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    timer_factory_.RevokeAll();
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MessageLoop::current()->PostDelayedTask(FROM_HERE,
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      timer_factory_.NewRunnableMethod(&StatusBubbleViews::StatusView::OnTimer),
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      time);
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::OnTimer() {
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (stage_ == BUBBLE_HIDING_TIMER) {
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stage_ = BUBBLE_HIDING_FADE;
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartFade(1.0, 0.0, kHideFadeDurationMS);
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (stage_ == BUBBLE_SHOWING_TIMER) {
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stage_ = BUBBLE_SHOWING_FADE;
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartFade(0.0, 1.0, kShowFadeDurationMS);
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::CancelTimer() {
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!timer_factory_.empty())
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    timer_factory_.RevokeAll();
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::RestartTimer(int delay) {
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CancelTimer();
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StartTimer(delay);
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::ResetTimer() {
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (stage_ == BUBBLE_SHOWING_TIMER) {
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We hadn't yet begun showing anything when we received a new request
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // for something to show, so we start from scratch.
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    RestartTimer(kShowDelay);
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::StartFade(double start,
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                              double end,
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                              int duration) {
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  opacity_start_ = start;
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  opacity_end_ = end;
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This will also reset the currently-occurring animation.
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetDuration(duration);
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Start();
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::StartHiding() {
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (stage_ == BUBBLE_SHOWN) {
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stage_ = BUBBLE_HIDING_TIMER;
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartTimer(kHideDelay);
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (stage_ == BUBBLE_SHOWING_TIMER) {
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stage_ = BUBBLE_HIDDEN;
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CancelTimer();
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (stage_ == BUBBLE_SHOWING_FADE) {
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stage_ = BUBBLE_HIDING_FADE;
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Figure out where we are in the current fade.
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double current_opacity = GetCurrentOpacity();
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Start a fade in the opposite direction.
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartFade(current_opacity, 0.0,
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              static_cast<int>(kHideFadeDurationMS * current_opacity));
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::StartShowing() {
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (stage_ == BUBBLE_HIDDEN) {
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    popup_->Show();
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stage_ = BUBBLE_SHOWING_TIMER;
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartTimer(kShowDelay);
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (stage_ == BUBBLE_HIDING_TIMER) {
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stage_ = BUBBLE_SHOWN;
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CancelTimer();
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (stage_ == BUBBLE_HIDING_FADE) {
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We're partway through a fade.
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stage_ = BUBBLE_SHOWING_FADE;
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Figure out where we are in the current fade.
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double current_opacity = GetCurrentOpacity();
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Start a fade in the opposite direction.
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartFade(current_opacity, 1.0,
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              static_cast<int>(kShowFadeDurationMS * current_opacity));
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (stage_ == BUBBLE_SHOWING_TIMER) {
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We hadn't yet begun showing anything when we received a new request
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // for something to show, so we start from scratch.
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ResetTimer();
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Animation functions.
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochdouble StatusBubbleViews::StatusView::GetCurrentOpacity() {
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return opacity_start_ + (opacity_end_ - opacity_start_) *
3073f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen         ui::LinearAnimation::GetCurrentValue();
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::SetOpacity(double opacity) {
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  popup_->SetOpacity(static_cast<unsigned char>(opacity * 255));
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SchedulePaint();
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::AnimateToState(double state) {
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetOpacity(GetCurrentOpacity());
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::AnimationEnded(
3203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    const ui::Animation* animation) {
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetOpacity(opacity_end_);
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (stage_ == BUBBLE_HIDING_FADE) {
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stage_ = BUBBLE_HIDDEN;
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    popup_->Hide();
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (stage_ == BUBBLE_SHOWING_FADE) {
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    stage_ = BUBBLE_SHOWN;
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusView::SetStyle(BubbleStyle style) {
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (style_ != style) {
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    style_ = style;
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SchedulePaint();
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
338dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid StatusBubbleViews::StatusView::OnPaint(gfx::Canvas* canvas) {
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkPaint paint;
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  paint.setStyle(SkPaint::kFill_Style);
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  paint.setFlags(SkPaint::kAntiAlias_Flag);
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkColor toolbar_color =
343ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      theme_service_->GetColor(ThemeService::COLOR_TOOLBAR);
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  paint.setColor(toolbar_color);
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
346dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  gfx::Rect popup_bounds = popup_->GetWindowScreenBounds();
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Figure out how to round the bubble's four corners.
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkScalar rad[8];
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Top Edges - if the bubble is in its bottom position (sticking downwards),
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // then we square the top edges. Otherwise, we square the edges based on the
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // position of the bubble within the window (the bubble is positioned in the
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // southeast corner in RTL and in the southwest corner in LTR).
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (style_ == STYLE_BOTTOM) {
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Top Left corner.
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[0] = 0;
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[1] = 0;
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Top Right corner.
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[2] = 0;
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[3] = 0;
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (base::i18n::IsRTL() != (style_ == STYLE_STANDARD_RIGHT)) {
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // The text is RtL or the bubble is on the right side (but not both).
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Top Left corner.
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      rad[0] = SkIntToScalar(kBubbleCornerRadius);
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      rad[1] = SkIntToScalar(kBubbleCornerRadius);
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Top Right corner.
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      rad[2] = 0;
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      rad[3] = 0;
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Top Left corner.
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      rad[0] = 0;
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      rad[1] = 0;
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Top Right corner.
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      rad[2] = SkIntToScalar(kBubbleCornerRadius);
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      rad[3] = SkIntToScalar(kBubbleCornerRadius);
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Bottom edges - square these off if the bubble is in its standard position
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // (sticking upward).
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (style_ == STYLE_STANDARD || style_ == STYLE_STANDARD_RIGHT) {
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Bottom Right Corner.
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[4] = 0;
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[5] = 0;
391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Bottom Left Corner.
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[6] = 0;
394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[7] = 0;
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Bottom Right Corner.
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[4] = SkIntToScalar(kBubbleCornerRadius);
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[5] = SkIntToScalar(kBubbleCornerRadius);
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Bottom Left Corner.
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[6] = SkIntToScalar(kBubbleCornerRadius);
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    rad[7] = SkIntToScalar(kBubbleCornerRadius);
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Draw the bubble's shadow.
406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int width = popup_bounds.width();
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int height = popup_bounds.height();
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkRect rect;
409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  rect.set(0, 0,
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch           SkIntToScalar(width),
411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch           SkIntToScalar(height));
412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkPath shadow_path;
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  shadow_path.addRoundRect(rect, rad, SkPath::kCW_Direction);
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkPaint shadow_paint;
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  shadow_paint.setFlags(SkPaint::kAntiAlias_Flag);
416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  shadow_paint.setColor(kShadowColor);
417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  canvas->AsCanvasSkia()->drawPath(shadow_path, shadow_paint);
418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Draw the bubble.
420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  rect.set(SkIntToScalar(kShadowThickness),
421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch           SkIntToScalar(kShadowThickness),
422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch           SkIntToScalar(width - kShadowThickness),
423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch           SkIntToScalar(height - kShadowThickness));
424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkPath path;
425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  path.addRoundRect(rect, rad, SkPath::kCW_Direction);
426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  canvas->AsCanvasSkia()->drawPath(path, paint);
427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Draw highlight text and then the text body. In order to make sure the text
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // is aligned to the right on RTL UIs, we mirror the text bounds if the
430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // locale is RTL.
4313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int text_width = std::min(
4323f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      views::Label::font().GetStringWidth(text_),
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      width - (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding);
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int text_height = height - (kShadowThickness * 2);
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect body_bounds(kShadowThickness + kTextPositionX,
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        kShadowThickness,
437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        std::max(0, text_width),
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        std::max(0, text_height));
43972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  body_bounds.set_x(GetMirroredXForRect(body_bounds));
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkColor text_color =
441ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      theme_service_->GetColor(ThemeService::COLOR_TAB_TEXT);
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // DrawStringInt doesn't handle alpha, so we'll do the blending ourselves.
444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  text_color = SkColorSetARGB(
445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      SkColorGetA(text_color),
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (SkColorGetR(text_color) + SkColorGetR(toolbar_color)) / 2,
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (SkColorGetG(text_color) + SkColorGetR(toolbar_color)) / 2,
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (SkColorGetB(text_color) + SkColorGetR(toolbar_color)) / 2);
44972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  canvas->DrawStringInt(text_,
450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        views::Label::font(),
451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        text_color,
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        body_bounds.x(),
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        body_bounds.y(),
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        body_bounds.width(),
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        body_bounds.height());
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// StatusViewExpander ---------------------------------------------------------
459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Manages the expansion and contraction of the status bubble as it accommodates
460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// URLs too long to fit in the standard bubble. Changes are passed through the
461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// StatusView to paint.
4623f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenclass StatusBubbleViews::StatusViewExpander : public ui::LinearAnimation,
4633f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                              public ui::AnimationDelegate {
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StatusViewExpander(StatusBubbleViews* status_bubble,
466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                     StatusView* status_view)
4673f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      : ALLOW_THIS_IN_INITIALIZER_LIST(ui::LinearAnimation(kFramerate, this)),
468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        status_bubble_(status_bubble),
469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        status_view_(status_view),
470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        expansion_start_(0),
471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        expansion_end_(0) {
472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Manage the expansion of the bubble.
475ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void StartExpansion(const string16& expanded_text,
476ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                      int current_width,
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                      int expansion_end);
478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Set width of fully expanded bubble.
480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void SetExpandedWidth(int expanded_width);
481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Animation functions.
484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int GetCurrentBubbleWidth();
485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void SetBubbleWidth(int width);
486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void AnimateToState(double state);
4873f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  void AnimationEnded(const ui::Animation* animation);
488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Manager that owns us.
490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StatusBubbleViews* status_bubble_;
491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Change the bounds and text of this view.
493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StatusView* status_view_;
494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Text elided (if needed) to fit maximum status bar width.
4963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  string16 expanded_text_;
497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Widths at expansion start and end.
499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int expansion_start_;
500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int expansion_end_;
501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusViewExpander::AnimateToState(double state) {
504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetBubbleWidth(GetCurrentBubbleWidth());
505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusViewExpander::AnimationEnded(
5083f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    const ui::Animation* animation) {
509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetBubbleWidth(expansion_end_);
510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  status_view_->SetText(expanded_text_, false);
511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusViewExpander::StartExpansion(
514ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const string16& expanded_text,
515ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    int expansion_start,
516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int expansion_end) {
517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  expanded_text_ = expanded_text;
518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  expansion_start_ = expansion_start;
519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  expansion_end_ = expansion_end;
520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int min_duration = std::max(kMinExpansionStepDurationMS,
521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<int>(kMaxExpansionStepDurationMS *
522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          (expansion_end - expansion_start) / 100.0));
523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetDuration(std::min(kMaxExpansionStepDurationMS, min_duration));
524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Start();
525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint StatusBubbleViews::StatusViewExpander::GetCurrentBubbleWidth() {
528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return static_cast<int>(expansion_start_ +
5293f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      (expansion_end_ - expansion_start_) *
5303f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen          ui::LinearAnimation::GetCurrentValue());
531c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::StatusViewExpander::SetBubbleWidth(int width) {
534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  status_bubble_->SetBubbleWidth(width);
535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  status_view_->SchedulePaint();
536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// StatusBubble ---------------------------------------------------------------
539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int StatusBubbleViews::kShadowThickness = 1;
541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5423345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickStatusBubbleViews::StatusBubbleViews(views::View* base_view)
543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : offset_(0),
544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      popup_(NULL),
545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      opacity_(0),
5463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      base_view_(base_view),
547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      view_(NULL),
548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      download_shelf_is_visible_(false),
549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      is_expanded_(false),
550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ALLOW_THIS_IN_INITIALIZER_LIST(expand_timer_factory_(this)) {
551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  expand_view_.reset();
552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
554c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochStatusBubbleViews::~StatusBubbleViews() {
555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CancelExpandTimer();
556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (popup_.get())
557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    popup_->CloseNow();
558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::Init() {
561c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!popup_.get()) {
562ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    Widget::CreateParams params(Widget::CreateParams::TYPE_POPUP);
563ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    params.transparent = true;
564ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    params.accept_events = false;
565ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    params.delete_on_destroy = false;
566ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    popup_.reset(Widget::CreateWidget(params));
5673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    views::Widget* frame = base_view_->GetWidget();
568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!view_)
5693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      view_ = new StatusView(this, popup_.get(), frame->GetThemeProvider());
570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!expand_view_.get())
571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      expand_view_.reset(new StatusViewExpander(this, view_));
572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    popup_->SetOpacity(0x00);
5733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    popup_->Init(frame->GetNativeView(), gfx::Rect());
574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    popup_->SetContentsView(view_);
575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Reposition();
576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    popup_->Show();
577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::Reposition() {
581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (popup_.get()) {
582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Point top_left;
5833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    views::View::ConvertPointToScreen(base_view_, &top_left);
584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    popup_->SetBounds(gfx::Rect(top_left.x() + position_.x(),
586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                top_left.y() + position_.y(),
587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                size_.width(), size_.height()));
588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Size StatusBubbleViews::GetPreferredSize() {
592c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return gfx::Size(0, ResourceBundle::GetSharedInstance().GetFont(
5933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      ResourceBundle::BaseFont).GetHeight() + kTotalVerticalPadding);
594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::SetBounds(int x, int y, int w, int h) {
5973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  original_position_.SetPoint(x, y);
59872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  position_.SetPoint(base_view_->GetMirroredXWithWidthInView(x, w), y);
599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_.SetSize(w, h);
600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Reposition();
601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid StatusBubbleViews::SetStatus(const string16& status_text) {
604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (size_.IsEmpty())
605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;  // We have no bounds, don't attempt to show the popup.
606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (status_text_ == status_text && !status_text.empty())
608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!IsFrameVisible())
611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;  // Don't show anything if the parent isn't visible.
612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Init();
614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  status_text_ = status_text;
615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!status_text_.empty()) {
616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    view_->SetText(status_text, true);
617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    view_->Show();
618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (!url_text_.empty()) {
619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    view_->SetText(url_text_, true);
620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
6213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    view_->SetText(string16(), true);
622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid StatusBubbleViews::SetURL(const GURL& url, const string16& languages) {
626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  languages_ = languages;
627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  url_ = url;
628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (size_.IsEmpty())
629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;  // We have no bounds, don't attempt to show the popup.
630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Init();
632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
633c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we want to clear a displayed URL but there is a status still to
634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // display, display that status instead.
635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (url.is_empty() && !status_text_.empty()) {
6363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    url_text_ = string16();
637c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (IsFrameVisible())
638c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      view_->SetText(status_text_, true);
639c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
640c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
641c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
642c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Reset expansion state only when bubble is completely hidden.
643c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (view_->GetState() == StatusView::BUBBLE_HIDDEN) {
644c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    is_expanded_ = false;
645c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetBubbleWidth(GetStandardStatusBubbleWidth());
646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
647c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
648c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Set Elided Text corresponding to the GURL object.
649dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  gfx::Rect popup_bounds = popup_->GetWindowScreenBounds();
650c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int text_width = static_cast<int>(popup_bounds.width() -
651c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1);
65272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  url_text_ = ui::ElideUrl(url, view_->Label::font(),
653dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      text_width, UTF16ToUTF8(languages));
654c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  std::wstring original_url_text =
6563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      UTF16ToWideHack(net::FormatUrl(url, UTF16ToUTF8(languages)));
657c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
658c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // An URL is always treated as a left-to-right string. On right-to-left UIs
659c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // we need to explicitly mark the URL as LTR to make sure it is displayed
660c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // correctly.
6613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  url_text_ = base::i18n::GetDisplayStringInLTRDirectionality(url_text_);
662c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
663c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (IsFrameVisible()) {
664c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    view_->SetText(url_text_, true);
665c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
666c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CancelExpandTimer();
667c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
668c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If bubble is already in expanded state, shift to adjust to new text
669c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // size (shrinking or expanding). Otherwise delay.
670c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (is_expanded_ && !url.is_empty())
671c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ExpandBubble();
672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else if (original_url_text.length() > url_text_.length())
673c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      MessageLoop::current()->PostDelayedTask(FROM_HERE,
674c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          expand_timer_factory_.NewRunnableMethod(
675c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          &StatusBubbleViews::ExpandBubble), kExpandHoverDelay);
676c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
677c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
678c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
679c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::Hide() {
6803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  status_text_ = string16();
6813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  url_text_ = string16();
682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (view_)
683c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    view_->Hide();
684c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
685c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
686c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::MouseMoved(const gfx::Point& location,
687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   bool left_content) {
688c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (left_content)
689c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (view_) {
692c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    view_->ResetTimer();
693c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
694c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (view_->GetState() != StatusView::BUBBLE_HIDDEN &&
695c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        view_->GetState() != StatusView::BUBBLE_HIDING_FADE &&
696c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        view_->GetState() != StatusView::BUBBLE_HIDING_TIMER) {
697c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      AvoidMouse(location);
698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
699c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
700c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
701c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::UpdateDownloadShelfVisibility(bool visible) {
703c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  download_shelf_is_visible_ = visible;
704c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
705c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
706c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::AvoidMouse(const gfx::Point& location) {
707c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Get the position of the frame.
708c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point top_left;
7093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  views::View::ConvertPointToScreen(base_view_, &top_left);
7103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Border included.
71172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  int window_width = base_view_->GetLocalBounds().width();
712c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
713c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Get the cursor position relative to the popup.
714c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point relative_location = location;
715c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (base::i18n::IsRTL()) {
716c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int top_right_x = top_left.x() + window_width;
717c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    relative_location.set_x(top_right_x - relative_location.x());
718c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
719c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    relative_location.set_x(
720c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        relative_location.x() - (top_left.x() + position_.x()));
721c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
722c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  relative_location.set_y(
723c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      relative_location.y() - (top_left.y() + position_.y()));
724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the mouse is in a position where we think it would move the
726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // status bubble, figure out where and how the bubble should be moved.
727c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (relative_location.y() > -kMousePadding &&
728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      relative_location.x() < size_.width() + kMousePadding) {
729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int offset = kMousePadding + relative_location.y();
730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Make the movement non-linear.
732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    offset = offset * offset / kMousePadding;
733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
734c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // When the mouse is entering from the right, we want the offset to be
735c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // scaled by how horizontally far away the cursor is from the bubble.
736c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (relative_location.x() > size_.width()) {
737c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      offset = static_cast<int>(static_cast<float>(offset) * (
738c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<float>(kMousePadding -
739c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              (relative_location.x() - size_.width())) /
740c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<float>(kMousePadding)));
741c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
742c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
743c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Cap the offset and change the visual presentation of the bubble
744c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // depending on where it ends up (so that rounded corners square off
745c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // and mate to the edges of the tab content).
746c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (offset >= size_.height() - kShadowThickness * 2) {
747c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      offset = size_.height() - kShadowThickness * 2;
748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      view_->SetStyle(StatusView::STYLE_BOTTOM);
749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if (offset > kBubbleCornerRadius / 2 - kShadowThickness) {
750c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      view_->SetStyle(StatusView::STYLE_FLOATING);
751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
752c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      view_->SetStyle(StatusView::STYLE_STANDARD);
753c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
754c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
755c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Check if the bubble sticks out from the monitor or will obscure
756c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // download shelf.
7573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    gfx::NativeView widget = base_view_->GetWidget()->GetNativeView();
758c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect monitor_rect =
759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        views::Screen::GetMonitorWorkAreaNearestWindow(widget);
760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const int bubble_bottom_y = top_left.y() + position_.y() + size_.height();
761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
762c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (bubble_bottom_y + offset > monitor_rect.height() ||
763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        (download_shelf_is_visible_ &&
764c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch         (view_->GetStyle() == StatusView::STYLE_FLOATING ||
765c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          view_->GetStyle() == StatusView::STYLE_BOTTOM))) {
766c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // The offset is still too large. Move the bubble to the right and reset
767c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Y offset_ to zero.
768c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      view_->SetStyle(StatusView::STYLE_STANDARD_RIGHT);
769c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      offset_ = 0;
770c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
771c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Subtract border width + bubble width.
772c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      int right_position_x = window_width - (position_.x() + size_.width());
773c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      popup_->SetBounds(gfx::Rect(top_left.x() + right_position_x,
774c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  top_left.y() + position_.y(),
775c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  size_.width(), size_.height()));
776c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
777c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      offset_ = offset;
778c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      popup_->SetBounds(gfx::Rect(top_left.x() + position_.x(),
779c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  top_left.y() + position_.y() + offset_,
780c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  size_.width(), size_.height()));
781c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
782c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (offset_ != 0 ||
783c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      view_->GetStyle() == StatusView::STYLE_STANDARD_RIGHT) {
784c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    offset_ = 0;
785c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    view_->SetStyle(StatusView::STYLE_STANDARD);
786c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    popup_->SetBounds(gfx::Rect(top_left.x() + position_.x(),
787c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                top_left.y() + position_.y(),
788c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                size_.width(), size_.height()));
789c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
790c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
791c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
792c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StatusBubbleViews::IsFrameVisible() {
7933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  views::Widget* frame = base_view_->GetWidget();
7943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!frame->IsVisible())
795c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
796c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  views::Window* window = frame->GetWindow();
798c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return !window || !window->IsMinimized();
799c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
800c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
801c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::ExpandBubble() {
802c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Elide URL to maximum possible size, then check actual length (it may
803c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // still be too long to fit) before expanding bubble.
804dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  gfx::Rect popup_bounds = popup_->GetWindowScreenBounds();
805c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int max_status_bubble_width = GetMaxStatusBubbleWidth();
80672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  url_text_ = ui::ElideUrl(url_, view_->Label::font(),
807dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      max_status_bubble_width, UTF16ToUTF8(languages_));
8083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int expanded_bubble_width =std::max(GetStandardStatusBubbleWidth(),
8093f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      std::min(view_->Label::font().GetStringWidth(url_text_) +
8103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                   (kShadowThickness * 2) + kTextPositionX +
8113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                   kTextHorizPadding + 1,
8123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick               max_status_bubble_width));
813c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  is_expanded_ = true;
814c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  expand_view_->StartExpansion(url_text_, popup_bounds.width(),
815c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               expanded_bubble_width);
816c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
817c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
818c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint StatusBubbleViews::GetStandardStatusBubbleWidth() {
8193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return base_view_->bounds().width() / 3;
820c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
821c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
822c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint StatusBubbleViews::GetMaxStatusBubbleWidth() {
8233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return static_cast<int>(std::max(0, base_view_->bounds().width() -
8243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1 -
8253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      views::NativeScrollBar::GetVerticalScrollBarWidth()));
826c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
827c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
828c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::SetBubbleWidth(int width) {
829c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_.set_width(width);
8303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  SetBounds(original_position_.x(), original_position_.y(),
8313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick            size_.width(), size_.height());
832c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
833c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
834c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleViews::CancelExpandTimer() {
835c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!expand_timer_factory_.empty())
836c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    expand_timer_factory_.RevokeAll();
837c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
838