15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/status_bubble_views.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/rtl.h"
119ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/themes/theme_properties.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/theme_resources.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_util.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkPaint.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkPath.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkRect.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/theme_provider.h"
23d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/animation_delegate.h"
24d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/linear_animation.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/point.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/screen.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/skia_util.h"
2958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "ui/gfx/text_elider.h"
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/native_theme/native_theme.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/label.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/scrollbar/native_scroll_bar.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/root_view.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/gurl.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#if defined(USE_AURA)
38a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "ui/aura/window.h"
39a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#endif
40a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(USE_ASH)
4268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "ash/wm/window_state.h"
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The alpha and color of the bubble's shadow.
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const SkColor kShadowColor = SkColorSetARGB(30, 0, 0, 0);
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The roundedness of the edges of our bubble.
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kBubbleCornerRadius = 4;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// How close the mouse can get to the infobubble before it starts sliding
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// off-screen.
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMousePadding = 20;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The horizontal offset of the text within the status bubble, not including the
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// outer shadow ring.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kTextPositionX = 3;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The minimum horizontal space between the (right) end of the text and the edge
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// of the status bubble, not including the outer shadow ring.
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kTextHorizPadding = 1;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Delays before we start hiding or showing the bubble after we receive a
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// show or hide request.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kShowDelay = 80;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kHideDelay = 250;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// How long each fade should last for.
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kShowFadeDurationMS = 120;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kHideFadeDurationMS = 200;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kFramerate = 25;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// How long each expansion step should take.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMinExpansionStepDurationMS = 20;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMaxExpansionStepDurationMS = 150;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// View -----------------------------------------------------------------------
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusView manages the display of the bubble, applying text changes and
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// fading in or out the bubble as required.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class StatusBubbleViews::StatusView : public views::Label,
81d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                      public gfx::LinearAnimation,
82d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                      public gfx::AnimationDelegate {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StatusView(StatusBubble* status_bubble,
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             views::Widget* popup,
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             ui::ThemeProvider* theme_provider)
87d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      : gfx::LinearAnimation(kFramerate, this),
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        stage_(BUBBLE_HIDDEN),
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        style_(STYLE_STANDARD),
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        timer_factory_(this),
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        status_bubble_(status_bubble),
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        popup_(popup),
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        opacity_start_(0),
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        opacity_end_(0),
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        theme_service_(theme_provider) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetFont(rb.GetFont(ui::ResourceBundle::BaseFont));
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~StatusView() {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Remove ourself as a delegate so that we don't get notified when
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // animations end as a result of destruction.
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_delegate(NULL);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Stop();
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CancelTimer();
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The bubble can be in one of many stages:
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  enum BubbleStage {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_HIDDEN,         // Entirely BUBBLE_HIDDEN.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_HIDING_FADE,    // In a fade-out transition.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_HIDING_TIMER,   // Waiting before a fade-out.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_SHOWING_TIMER,  // Waiting before a fade-in.
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_SHOWING_FADE,   // In a fade-in transition.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_SHOWN           // Fully visible.
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  enum BubbleStyle {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    STYLE_BOTTOM,
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    STYLE_FLOATING,
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    STYLE_STANDARD,
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    STYLE_STANDARD_RIGHT
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the bubble text to a certain value, hides the bubble if text is
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // an empty string.  Trigger animation sequence to display if
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |should_animate_open|.
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void SetText(const base::string16& text, bool should_animate_open);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BubbleStage GetState() const { return stage_; }
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetStyle(BubbleStyle style);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BubbleStyle GetStyle() const { return style_; }
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Show the bubble instantly.
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Show();
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Hide the bubble instantly.
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Hide();
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Resets any timers we have. Typically called when the user moves a
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mouse.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ResetTimer();
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  class InitialTimer;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Manage the timers that control the delay before a fade begins or ends.
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StartTimer(base::TimeDelta time);
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void OnTimer();
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void CancelTimer();
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void RestartTimer(base::TimeDelta delay);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Manage the fades and starting and stopping the animations correctly.
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StartFade(double start, double end, int duration);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StartHiding();
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StartShowing();
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Animation functions.
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double GetCurrentOpacity();
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetOpacity(double opacity);
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void AnimateToState(double state) OVERRIDE;
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void AnimationEnded(const Animation* animation) OVERRIDE;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BubbleStage stage_;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BubbleStyle style_;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::WeakPtrFactory<StatusBubbleViews::StatusView> timer_factory_;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Manager, owns us.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StatusBubble* status_bubble_;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Handle to the widget that contains us.
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget* popup_;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The currently-displayed text.
180a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 text_;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start and end opacities for the current transition - note that as a
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // fade-in can easily turn into a fade out, opacity_start_ is sometimes
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // a value between 0 and 1.
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double opacity_start_;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double opacity_end_;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Holds the theme provider of the frame that created us.
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::ThemeProvider* theme_service_;
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
192a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void StatusBubbleViews::StatusView::SetText(const base::string16& text,
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            bool should_animate_open) {
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (text.empty()) {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The string was empty.
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartHiding();
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We want to show the string.
199f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (text != text_) {
200f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      text_ = text;
201f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      SchedulePaint();
202f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (should_animate_open)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      StartShowing();
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::Show() {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Stop();
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CancelTimer();
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetOpacity(1.0);
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  popup_->Show();
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  stage_ = BUBBLE_SHOWN;
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::Hide() {
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Stop();
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CancelTimer();
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetOpacity(0.0);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  text_.clear();
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  popup_->Hide();
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  stage_ = BUBBLE_HIDDEN;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::StartTimer(base::TimeDelta time) {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (timer_factory_.HasWeakPtrs())
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    timer_factory_.InvalidateWeakPtrs();
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
229b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  base::MessageLoop::current()->PostDelayedTask(
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&StatusBubbleViews::StatusView::OnTimer,
232b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                 timer_factory_.GetWeakPtr()),
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      time);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::OnTimer() {
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stage_ == BUBBLE_HIDING_TIMER) {
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stage_ = BUBBLE_HIDING_FADE;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartFade(1.0, 0.0, kHideFadeDurationMS);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (stage_ == BUBBLE_SHOWING_TIMER) {
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stage_ = BUBBLE_SHOWING_FADE;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartFade(0.0, 1.0, kShowFadeDurationMS);
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::CancelTimer() {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (timer_factory_.HasWeakPtrs())
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    timer_factory_.InvalidateWeakPtrs();
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::RestartTimer(base::TimeDelta delay) {
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CancelTimer();
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StartTimer(delay);
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::ResetTimer() {
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stage_ == BUBBLE_SHOWING_TIMER) {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We hadn't yet begun showing anything when we received a new request
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for something to show, so we start from scratch.
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RestartTimer(base::TimeDelta::FromMilliseconds(kShowDelay));
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::StartFade(double start,
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              double end,
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              int duration) {
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  opacity_start_ = start;
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  opacity_end_ = end;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This will also reset the currently-occurring animation.
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetDuration(duration);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Start();
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::StartHiding() {
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stage_ == BUBBLE_SHOWN) {
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stage_ = BUBBLE_HIDING_TIMER;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartTimer(base::TimeDelta::FromMilliseconds(kHideDelay));
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (stage_ == BUBBLE_SHOWING_TIMER) {
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stage_ = BUBBLE_HIDDEN;
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->Hide();
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CancelTimer();
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (stage_ == BUBBLE_SHOWING_FADE) {
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stage_ = BUBBLE_HIDING_FADE;
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Figure out where we are in the current fade.
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double current_opacity = GetCurrentOpacity();
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Start a fade in the opposite direction.
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartFade(current_opacity, 0.0,
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              static_cast<int>(kHideFadeDurationMS * current_opacity));
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::StartShowing() {
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stage_ == BUBBLE_HIDDEN) {
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->Show();
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stage_ = BUBBLE_SHOWING_TIMER;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartTimer(base::TimeDelta::FromMilliseconds(kShowDelay));
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (stage_ == BUBBLE_HIDING_TIMER) {
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stage_ = BUBBLE_SHOWN;
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CancelTimer();
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (stage_ == BUBBLE_HIDING_FADE) {
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We're partway through a fade.
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stage_ = BUBBLE_SHOWING_FADE;
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Figure out where we are in the current fade.
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double current_opacity = GetCurrentOpacity();
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Start a fade in the opposite direction.
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartFade(current_opacity, 1.0,
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              static_cast<int>(kShowFadeDurationMS * current_opacity));
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (stage_ == BUBBLE_SHOWING_TIMER) {
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We hadn't yet begun showing anything when we received a new request
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for something to show, so we start from scratch.
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ResetTimer();
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Animation functions.
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)double StatusBubbleViews::StatusView::GetCurrentOpacity() {
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return opacity_start_ + (opacity_end_ - opacity_start_) *
322d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)         gfx::LinearAnimation::GetCurrentValue();
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::SetOpacity(double opacity) {
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  popup_->SetOpacity(static_cast<unsigned char>(opacity * 255));
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::AnimateToState(double state) {
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetOpacity(GetCurrentOpacity());
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::AnimationEnded(
334d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const gfx::Animation* animation) {
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetOpacity(opacity_end_);
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stage_ == BUBBLE_HIDING_FADE) {
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stage_ = BUBBLE_HIDDEN;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->Hide();
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (stage_ == BUBBLE_SHOWING_FADE) {
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stage_ = BUBBLE_SHOWN;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::SetStyle(BubbleStyle style) {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (style_ != style) {
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    style_ = style;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SchedulePaint();
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::OnPaint(gfx::Canvas* canvas) {
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPaint paint;
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setStyle(SkPaint::kFill_Style);
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setAntiAlias(true);
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkColor toolbar_color = theme_service_->GetColor(
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ThemeProperties::COLOR_TOOLBAR);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setColor(toolbar_color);
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect popup_bounds = popup_->GetWindowBoundsInScreen();
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Figure out how to round the bubble's four corners.
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkScalar rad[8];
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Top Edges - if the bubble is in its bottom position (sticking downwards),
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // then we square the top edges. Otherwise, we square the edges based on the
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // position of the bubble within the window (the bubble is positioned in the
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // southeast corner in RTL and in the southwest corner in LTR).
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (style_ == STYLE_BOTTOM) {
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Top Left corner.
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[0] = 0;
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[1] = 0;
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Top Right corner.
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[2] = 0;
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[3] = 0;
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (base::i18n::IsRTL() != (style_ == STYLE_STANDARD_RIGHT)) {
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The text is RtL or the bubble is on the right side (but not both).
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Top Left corner.
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rad[0] = SkIntToScalar(kBubbleCornerRadius);
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rad[1] = SkIntToScalar(kBubbleCornerRadius);
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Top Right corner.
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rad[2] = 0;
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rad[3] = 0;
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Top Left corner.
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rad[0] = 0;
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rad[1] = 0;
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Top Right corner.
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rad[2] = SkIntToScalar(kBubbleCornerRadius);
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rad[3] = SkIntToScalar(kBubbleCornerRadius);
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Bottom edges - square these off if the bubble is in its standard position
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (sticking upward).
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (style_ == STYLE_STANDARD || style_ == STYLE_STANDARD_RIGHT) {
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Bottom Right Corner.
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[4] = 0;
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[5] = 0;
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Bottom Left Corner.
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[6] = 0;
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[7] = 0;
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Bottom Right Corner.
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[4] = SkIntToScalar(kBubbleCornerRadius);
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[5] = SkIntToScalar(kBubbleCornerRadius);
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Bottom Left Corner.
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[6] = SkIntToScalar(kBubbleCornerRadius);
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rad[7] = SkIntToScalar(kBubbleCornerRadius);
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Draw the bubble's shadow.
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int width = popup_bounds.width();
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int height = popup_bounds.height();
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkRect rect(gfx::RectToSkRect(gfx::Rect(popup_bounds.size())));
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPath shadow_path;
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shadow_path.addRoundRect(rect, rad, SkPath::kCW_Direction);
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPaint shadow_paint;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shadow_paint.setAntiAlias(true);
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shadow_paint.setColor(kShadowColor);
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas->DrawPath(shadow_path, shadow_paint);
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Draw the bubble.
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rect.set(SkIntToScalar(kShadowThickness),
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           SkIntToScalar(kShadowThickness),
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           SkIntToScalar(width - kShadowThickness),
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           SkIntToScalar(height - kShadowThickness));
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPath path;
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  path.addRoundRect(rect, rad, SkPath::kCW_Direction);
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas->DrawPath(path, paint);
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Draw highlight text and then the text body. In order to make sure the text
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // is aligned to the right on RTL UIs, we mirror the text bounds if the
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // locale is RTL.
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int text_width = std::min(
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      views::Label::font().GetStringWidth(text_),
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      width - (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding);
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int text_height = height - (kShadowThickness * 2);
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect body_bounds(kShadowThickness + kTextPositionX,
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        kShadowThickness,
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        std::max(0, text_width),
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        std::max(0, text_height));
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  body_bounds.set_x(GetMirroredXForRect(body_bounds));
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkColor text_color =
452f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      theme_service_->GetColor(ThemeProperties::COLOR_STATUS_BAR_TEXT);
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas->DrawStringInt(text_,
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        views::Label::font(),
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        text_color,
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        body_bounds.x(),
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        body_bounds.y(),
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        body_bounds.width(),
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        body_bounds.height());
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusViewExpander ---------------------------------------------------------
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Manages the expansion and contraction of the status bubble as it accommodates
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// URLs too long to fit in the standard bubble. Changes are passed through the
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusView to paint.
466d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)class StatusBubbleViews::StatusViewExpander : public gfx::LinearAnimation,
467d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                              public gfx::AnimationDelegate {
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StatusViewExpander(StatusBubbleViews* status_bubble,
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     StatusView* status_view)
471d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      : gfx::LinearAnimation(kFramerate, this),
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        status_bubble_(status_bubble),
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        status_view_(status_view),
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        expansion_start_(0),
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        expansion_end_(0) {
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Manage the expansion of the bubble.
479a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void StartExpansion(const base::string16& expanded_text,
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      int current_width,
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      int expansion_end);
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set width of fully expanded bubble.
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetExpandedWidth(int expanded_width);
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Animation functions.
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int GetCurrentBubbleWidth();
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetBubbleWidth(int width);
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void AnimateToState(double state) OVERRIDE;
491d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Manager that owns us.
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StatusBubbleViews* status_bubble_;
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Change the bounds and text of this view.
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StatusView* status_view_;
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Text elided (if needed) to fit maximum status bar width.
500a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 expanded_text_;
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Widths at expansion start and end.
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int expansion_start_;
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int expansion_end_;
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusViewExpander::AnimateToState(double state) {
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetBubbleWidth(GetCurrentBubbleWidth());
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusViewExpander::AnimationEnded(
512d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const gfx::Animation* animation) {
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetBubbleWidth(expansion_end_);
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_view_->SetText(expanded_text_, false);
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusViewExpander::StartExpansion(
518a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16& expanded_text,
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int expansion_start,
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int expansion_end) {
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expanded_text_ = expanded_text;
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expansion_start_ = expansion_start;
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expansion_end_ = expansion_end;
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int min_duration = std::max(kMinExpansionStepDurationMS,
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(kMaxExpansionStepDurationMS *
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (expansion_end - expansion_start) / 100.0));
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetDuration(std::min(kMaxExpansionStepDurationMS, min_duration));
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Start();
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int StatusBubbleViews::StatusViewExpander::GetCurrentBubbleWidth() {
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<int>(expansion_start_ +
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (expansion_end_ - expansion_start_) *
534d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          gfx::LinearAnimation::GetCurrentValue());
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusViewExpander::SetBubbleWidth(int width) {
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_bubble_->SetBubbleWidth(width);
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_view_->SchedulePaint();
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusBubble ---------------------------------------------------------------
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int StatusBubbleViews::kShadowThickness = 1;
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)StatusBubbleViews::StatusBubbleViews(views::View* base_view)
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : contains_mouse_(false),
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset_(0),
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      opacity_(0),
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base_view_(base_view),
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_(NULL),
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      download_shelf_is_visible_(false),
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_expanded_(false),
554c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      expand_timer_factory_(this) {
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expand_view_.reset();
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)StatusBubbleViews::~StatusBubbleViews() {
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CancelExpandTimer();
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_.get())
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->CloseNow();
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::Init() {
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!popup_.get()) {
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_.reset(new views::Widget);
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::Widget* frame = base_view_->GetWidget();
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!view_)
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_ = new StatusView(this, popup_.get(), frame->GetThemeProvider());
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!expand_view_.get())
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expand_view_.reset(new StatusViewExpander(this, view_));
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
573eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params.accept_events = false;
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
5762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    params.parent = frame->GetNativeView();
5772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    params.context = frame->GetNativeView();
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->Init(params);
579a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#if defined(USE_AURA)
580a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    popup_->GetNativeView()->SetName("StatusBubbleViews");
581a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#endif
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We do our own animation and don't want any from the system.
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->SetVisibilityChangedAnimationsEnabled(false);
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->SetOpacity(0x00);
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->SetContentsView(view_);
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(USE_ASH)
58768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    ash::wm::GetWindowState(popup_->GetNativeWindow())->
58858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        set_ignored_by_shelf(true);
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Reposition();
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::Reposition() {
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_.get()) {
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Point top_left;
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::View::ConvertPointToScreen(base_view_, &top_left);
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->SetBounds(gfx::Rect(top_left.x() + position_.x(),
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                top_left.y() + position_.y(),
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                size_.width(), size_.height()));
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Size StatusBubbleViews::GetPreferredSize() {
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::Size(0, ui::ResourceBundle::GetSharedInstance().GetFont(
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ui::ResourceBundle::BaseFont).GetHeight() + kTotalVerticalPadding);
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::SetBounds(int x, int y, int w, int h) {
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  original_position_.SetPoint(x, y);
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  position_.SetPoint(base_view_->GetMirroredXWithWidthInView(x, w), y);
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_.SetSize(w, h);
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Reposition();
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_.get() && contains_mouse_)
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AvoidMouse(last_mouse_moved_location_);
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
619a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void StatusBubbleViews::SetStatus(const base::string16& status_text) {
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (size_.IsEmpty())
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // We have no bounds, don't attempt to show the popup.
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status_text_ == status_text && !status_text.empty())
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsFrameVisible())
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Don't show anything if the parent isn't visible.
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Init();
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_text_ = status_text;
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_text_.empty()) {
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->SetText(status_text, true);
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->Show();
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (!url_text_.empty()) {
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->SetText(url_text_, true);
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
637d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    view_->SetText(base::string16(), true);
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::SetURL(const GURL& url, const std::string& languages) {
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_ = url;
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  languages_ = languages;
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (size_.IsEmpty())
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // We have no bounds, don't attempt to show the popup.
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Init();
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we want to clear a displayed URL but there is a status still to
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // display, display that status instead.
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (url.is_empty() && !status_text_.empty()) {
652a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    url_text_ = base::string16();
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (IsFrameVisible())
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->SetText(status_text_, true);
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Reset expansion state only when bubble is completely hidden.
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (view_->GetState() == StatusView::BUBBLE_HIDDEN) {
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is_expanded_ = false;
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetBubbleWidth(GetStandardStatusBubbleWidth());
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set Elided Text corresponding to the GURL object.
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect popup_bounds = popup_->GetWindowBoundsInScreen();
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int text_width = static_cast<int>(popup_bounds.width() -
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1);
668f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  url_text_ = gfx::ElideUrl(url, view_->Label::font_list(), text_width,
669f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                            languages);
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // An URL is always treated as a left-to-right string. On right-to-left UIs
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we need to explicitly mark the URL as LTR to make sure it is displayed
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // correctly.
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_text_ = base::i18n::GetDisplayStringInLTRDirectionality(url_text_);
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsFrameVisible()) {
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->SetText(url_text_, true);
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CancelExpandTimer();
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If bubble is already in expanded state, shift to adjust to new text
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // size (shrinking or expanding). Otherwise delay.
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (is_expanded_ && !url.is_empty()) {
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ExpandBubble();
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (net::FormatUrl(url, languages).length() > url_text_.length()) {
686b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      base::MessageLoop::current()->PostDelayedTask(
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          FROM_HERE,
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::Bind(&StatusBubbleViews::ExpandBubble,
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     expand_timer_factory_.GetWeakPtr()),
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::TimeDelta::FromMilliseconds(kExpandHoverDelay));
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::Hide() {
696a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  status_text_ = base::string16();
697a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  url_text_ = base::string16();
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (view_)
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->Hide();
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::MouseMoved(const gfx::Point& location,
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   bool left_content) {
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  contains_mouse_ = !left_content;
7057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (left_content) {
7067dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    Reposition();
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
7087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_mouse_moved_location_ = location;
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (view_) {
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->ResetTimer();
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (view_->GetState() != StatusView::BUBBLE_HIDDEN &&
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        view_->GetState() != StatusView::BUBBLE_HIDING_FADE &&
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        view_->GetState() != StatusView::BUBBLE_HIDING_TIMER) {
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AvoidMouse(location);
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::UpdateDownloadShelfVisibility(bool visible) {
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  download_shelf_is_visible_ = visible;
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::AvoidMouse(const gfx::Point& location) {
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the position of the frame.
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point top_left;
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View::ConvertPointToScreen(base_view_, &top_left);
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Border included.
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int window_width = base_view_->GetLocalBounds().width();
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the cursor position relative to the popup.
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point relative_location = location;
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::i18n::IsRTL()) {
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int top_right_x = top_left.x() + window_width;
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    relative_location.set_x(top_right_x - relative_location.x());
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    relative_location.set_x(
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        relative_location.x() - (top_left.x() + position_.x()));
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  relative_location.set_y(
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      relative_location.y() - (top_left.y() + position_.y()));
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the mouse is in a position where we think it would move the
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // status bubble, figure out where and how the bubble should be moved.
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (relative_location.y() > -kMousePadding &&
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      relative_location.x() < size_.width() + kMousePadding) {
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int offset = kMousePadding + relative_location.y();
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Make the movement non-linear.
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    offset = offset * offset / kMousePadding;
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // When the mouse is entering from the right, we want the offset to be
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // scaled by how horizontally far away the cursor is from the bubble.
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (relative_location.x() > size_.width()) {
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset = static_cast<int>(static_cast<float>(offset) * (
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          static_cast<float>(kMousePadding -
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              (relative_location.x() - size_.width())) /
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          static_cast<float>(kMousePadding)));
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Cap the offset and change the visual presentation of the bubble
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // depending on where it ends up (so that rounded corners square off
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // and mate to the edges of the tab content).
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (offset >= size_.height() - kShadowThickness * 2) {
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset = size_.height() - kShadowThickness * 2;
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->SetStyle(StatusView::STYLE_BOTTOM);
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (offset > kBubbleCornerRadius / 2 - kShadowThickness) {
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->SetStyle(StatusView::STYLE_FLOATING);
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->SetStyle(StatusView::STYLE_STANDARD);
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Check if the bubble sticks out from the monitor or will obscure
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // download shelf.
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::NativeView window = base_view_->GetWidget()->GetNativeView();
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect monitor_rect = gfx::Screen::GetScreenFor(window)->
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetDisplayNearestWindow(window).work_area();
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int bubble_bottom_y = top_left.y() + position_.y() + size_.height();
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (bubble_bottom_y + offset > monitor_rect.height() ||
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (download_shelf_is_visible_ &&
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (view_->GetStyle() == StatusView::STYLE_FLOATING ||
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          view_->GetStyle() == StatusView::STYLE_BOTTOM))) {
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The offset is still too large. Move the bubble to the right and reset
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Y offset_ to zero.
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->SetStyle(StatusView::STYLE_STANDARD_RIGHT);
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset_ = 0;
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Subtract border width + bubble width.
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int right_position_x = window_width - (position_.x() + size_.width());
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      popup_->SetBounds(gfx::Rect(top_left.x() + right_position_x,
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  top_left.y() + position_.y(),
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  size_.width(), size_.height()));
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset_ = offset;
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      popup_->SetBounds(gfx::Rect(top_left.x() + position_.x(),
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  top_left.y() + position_.y() + offset_,
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  size_.width(), size_.height()));
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (offset_ != 0 ||
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->GetStyle() == StatusView::STYLE_STANDARD_RIGHT) {
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    offset_ = 0;
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->SetStyle(StatusView::STYLE_STANDARD);
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->SetBounds(gfx::Rect(top_left.x() + position_.x(),
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                top_left.y() + position_.y(),
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                size_.width(), size_.height()));
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool StatusBubbleViews::IsFrameVisible() {
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget* frame = base_view_->GetWidget();
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!frame->IsVisible())
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget* window = frame->GetTopLevelWidget();
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !window || !window->IsMinimized();
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::ExpandBubble() {
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Elide URL to maximum possible size, then check actual length (it may
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // still be too long to fit) before expanding bubble.
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect popup_bounds = popup_->GetWindowBoundsInScreen();
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int max_status_bubble_width = GetMaxStatusBubbleWidth();
826f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  url_text_ = gfx::ElideUrl(url_, view_->Label::font_list(),
827f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                            max_status_bubble_width, languages_);
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int expanded_bubble_width =std::max(GetStandardStatusBubbleWidth(),
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::min(view_->Label::font().GetStringWidth(url_text_) +
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   (kShadowThickness * 2) + kTextPositionX +
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   kTextHorizPadding + 1,
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               max_status_bubble_width));
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_expanded_ = true;
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expand_view_->StartExpansion(url_text_, popup_bounds.width(),
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               expanded_bubble_width);
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int StatusBubbleViews::GetStandardStatusBubbleWidth() {
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return base_view_->bounds().width() / 3;
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int StatusBubbleViews::GetMaxStatusBubbleWidth() {
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const ui::NativeTheme* theme = base_view_->GetNativeTheme();
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<int>(std::max(0, base_view_->bounds().width() -
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1 -
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      views::NativeScrollBar::GetVerticalScrollBarWidth(theme)));
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::SetBubbleWidth(int width) {
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_.set_width(width);
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetBounds(original_position_.x(), original_position_.y(),
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            size_.width(), size_.height());
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::CancelExpandTimer() {
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (expand_timer_factory_.HasWeakPtrs())
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expand_timer_factory_.InvalidateWeakPtrs();
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
859