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)
9f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "ash/wm/window_state.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/rtl.h"
129ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/themes/theme_properties.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/ui/elide_url.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/SkRect.h"
205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "ui/aura/window.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/theme_provider.h"
22d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/animation_delegate.h"
23d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/linear_animation.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h"
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/font_list.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/point.h"
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/rect.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/screen.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/skia_util.h"
3058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "ui/gfx/text_elider.h"
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/text_utils.h"
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/native_theme/native_theme.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/scrollbar/native_scroll_bar.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/root_view.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/gurl.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The alpha and color of the bubble's shadow.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const SkColor kShadowColor = SkColorSetARGB(30, 0, 0, 0);
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The roundedness of the edges of our bubble.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kBubbleCornerRadius = 4;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// How close the mouse can get to the infobubble before it starts sliding
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// off-screen.
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMousePadding = 20;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The horizontal offset of the text within the status bubble, not including the
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// outer shadow ring.
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kTextPositionX = 3;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The minimum horizontal space between the (right) end of the text and the edge
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// of the status bubble, not including the outer shadow ring.
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kTextHorizPadding = 1;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Delays before we start hiding or showing the bubble after we receive a
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// show or hide request.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kShowDelay = 80;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kHideDelay = 250;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// How long each fade should last for.
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kShowFadeDurationMS = 120;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kHideFadeDurationMS = 200;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kFramerate = 25;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// How long each expansion step should take.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMinExpansionStepDurationMS = 20;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMaxExpansionStepDurationMS = 150;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// StatusBubbleViews::StatusViewAnimation --------------------------------------
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class StatusBubbleViews::StatusViewAnimation : public gfx::LinearAnimation,
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                               public gfx::AnimationDelegate {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  StatusViewAnimation(StatusView* status_view,
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      double opacity_start,
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      double opacity_end);
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual ~StatusViewAnimation();
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  double GetCurrentOpacity();
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private:
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // gfx::LinearAnimation:
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual void AnimateToState(double state) OVERRIDE;
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // gfx::AnimationDelegate:
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual void AnimationEnded(const Animation* animation) OVERRIDE;
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  StatusView* status_view_;
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Start and end opacities for the current transition - note that as a
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // fade-in can easily turn into a fade out, opacity_start_ is sometimes
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // a value between 0 and 1.
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  double opacity_start_;
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  double opacity_end_;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(StatusViewAnimation);
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// StatusBubbleViews::StatusView -----------------------------------------------
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// StatusView manages the display of the bubble, applying text changes and
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// fading in or out the bubble as required.
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class StatusBubbleViews::StatusView : public views::View {
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public:
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The bubble can be in one of many states:
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  enum BubbleState {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_HIDDEN,         // Entirely BUBBLE_HIDDEN.
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_HIDING_FADE,    // In a fade-out transition.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_HIDING_TIMER,   // Waiting before a fade-out.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_SHOWING_TIMER,  // Waiting before a fade-in.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_SHOWING_FADE,   // In a fade-in transition.
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BUBBLE_SHOWN           // Fully visible.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  enum BubbleStyle {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    STYLE_BOTTOM,
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    STYLE_FLOATING,
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    STYLE_STANDARD,
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    STYLE_STANDARD_RIGHT
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1240529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  StatusView(views::Widget* popup,
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)             ui::ThemeProvider* theme_provider);
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual ~StatusView();
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the bubble text to a certain value, hides the bubble if text is
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // an empty string.  Trigger animation sequence to display if
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |should_animate_open|.
131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void SetText(const base::string16& text, bool should_animate_open);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  BubbleState state() const { return state_; }
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  BubbleStyle style() const { return style_; }
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetStyle(BubbleStyle style);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Show the bubble instantly.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Show();
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Hide the bubble instantly.
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Hide();
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Resets any timers we have. Typically called when the user moves a
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mouse.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ResetTimer();
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // This call backs the StatusView in order to fade the bubble in and out.
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void SetOpacity(double opacity);
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Depending on the state of the bubble this will either hide the popup or
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // not.
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void OnAnimationEnded();
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  class InitialTimer;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Manage the timers that control the delay before a fade begins or ends.
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StartTimer(base::TimeDelta time);
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void OnTimer();
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void CancelTimer();
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void RestartTimer(base::TimeDelta delay);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Manage the fades and starting and stopping the animations correctly.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StartFade(double start, double end, int duration);
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StartHiding();
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StartShowing();
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // views::View:
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  BubbleState state_;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BubbleStyle style_;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::WeakPtrFactory<StatusBubbleViews::StatusView> timer_factory_;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<StatusViewAnimation> animation_;
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Handle to the widget that contains us.
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget* popup_;
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The currently-displayed text.
182a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 text_;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Holds the theme provider of the frame that created us.
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::ThemeProvider* theme_service_;
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(StatusView);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1900529e5d033099cbfc42635f6f6183833b09dff6eBen MurdochStatusBubbleViews::StatusView::StatusView(views::Widget* popup,
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                          ui::ThemeProvider* theme_provider)
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : state_(BUBBLE_HIDDEN),
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      style_(STYLE_STANDARD),
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      timer_factory_(this),
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      animation_(new StatusViewAnimation(this, 0, 0)),
1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      popup_(popup),
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      theme_service_(theme_provider) {
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)StatusBubbleViews::StatusView::~StatusView() {
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  animation_->Stop();
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CancelTimer();
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
205a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void StatusBubbleViews::StatusView::SetText(const base::string16& text,
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            bool should_animate_open) {
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (text.empty()) {
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The string was empty.
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartHiding();
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We want to show the string.
212f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (text != text_) {
213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      text_ = text;
214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      SchedulePaint();
215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (should_animate_open)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      StartShowing();
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::Show() {
2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  animation_->Stop();
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CancelTimer();
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetOpacity(1.0);
2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  popup_->ShowInactive();
2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  state_ = BUBBLE_SHOWN;
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::Hide() {
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  animation_->Stop();
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CancelTimer();
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetOpacity(0.0);
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  text_.clear();
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  popup_->Hide();
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  state_ = BUBBLE_HIDDEN;
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::StartTimer(base::TimeDelta time) {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (timer_factory_.HasWeakPtrs())
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    timer_factory_.InvalidateWeakPtrs();
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
242b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  base::MessageLoop::current()->PostDelayedTask(
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&StatusBubbleViews::StatusView::OnTimer,
245b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                 timer_factory_.GetWeakPtr()),
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      time);
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::OnTimer() {
2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (state_ == BUBBLE_HIDING_TIMER) {
2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    state_ = BUBBLE_HIDING_FADE;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartFade(1.0, 0.0, kHideFadeDurationMS);
2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (state_ == BUBBLE_SHOWING_TIMER) {
2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    state_ = BUBBLE_SHOWING_FADE;
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartFade(0.0, 1.0, kShowFadeDurationMS);
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::CancelTimer() {
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (timer_factory_.HasWeakPtrs())
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    timer_factory_.InvalidateWeakPtrs();
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::RestartTimer(base::TimeDelta delay) {
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CancelTimer();
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StartTimer(delay);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::ResetTimer() {
2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (state_ == BUBBLE_SHOWING_TIMER) {
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We hadn't yet begun showing anything when we received a new request
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for something to show, so we start from scratch.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RestartTimer(base::TimeDelta::FromMilliseconds(kShowDelay));
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::StartFade(double start,
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              double end,
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              int duration) {
2805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  animation_.reset(new StatusViewAnimation(this, start, end));
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This will also reset the currently-occurring animation.
2835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  animation_->SetDuration(duration);
2845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  animation_->Start();
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::StartHiding() {
2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (state_ == BUBBLE_SHOWN) {
2895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    state_ = BUBBLE_HIDING_TIMER;
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartTimer(base::TimeDelta::FromMilliseconds(kHideDelay));
2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (state_ == BUBBLE_SHOWING_TIMER) {
2925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    state_ = BUBBLE_HIDDEN;
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->Hide();
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CancelTimer();
2955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (state_ == BUBBLE_SHOWING_FADE) {
2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    state_ = BUBBLE_HIDING_FADE;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Figure out where we are in the current fade.
2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    double current_opacity = animation_->GetCurrentOpacity();
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Start a fade in the opposite direction.
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartFade(current_opacity, 0.0,
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              static_cast<int>(kHideFadeDurationMS * current_opacity));
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::StartShowing() {
3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (state_ == BUBBLE_HIDDEN) {
3085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    popup_->ShowInactive();
3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    state_ = BUBBLE_SHOWING_TIMER;
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartTimer(base::TimeDelta::FromMilliseconds(kShowDelay));
3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (state_ == BUBBLE_HIDING_TIMER) {
3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    state_ = BUBBLE_SHOWN;
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CancelTimer();
3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (state_ == BUBBLE_HIDING_FADE) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We're partway through a fade.
3165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    state_ = BUBBLE_SHOWING_FADE;
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Figure out where we are in the current fade.
3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    double current_opacity = animation_->GetCurrentOpacity();
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Start a fade in the opposite direction.
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartFade(current_opacity, 1.0,
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              static_cast<int>(kShowFadeDurationMS * current_opacity));
3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (state_ == BUBBLE_SHOWING_TIMER) {
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We hadn't yet begun showing anything when we received a new request
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for something to show, so we start from scratch.
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ResetTimer();
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::SetOpacity(double opacity) {
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  popup_->SetOpacity(static_cast<unsigned char>(opacity * 255));
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::SetStyle(BubbleStyle style) {
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (style_ != style) {
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    style_ = style;
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SchedulePaint();
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void StatusBubbleViews::StatusView::OnAnimationEnded() {
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (state_ == BUBBLE_HIDING_FADE) {
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    state_ = BUBBLE_HIDDEN;
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    popup_->Hide();
3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (state_ == BUBBLE_SHOWING_FADE) {
3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    state_ = BUBBLE_SHOWN;
3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusView::OnPaint(gfx::Canvas* canvas) {
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPaint paint;
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setStyle(SkPaint::kFill_Style);
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setAntiAlias(true);
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkColor toolbar_color = theme_service_->GetColor(
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ThemeProperties::COLOR_TOOLBAR);
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  paint.setColor(toolbar_color);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect popup_bounds = popup_->GetWindowBoundsInScreen();
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  SkScalar rad[8] = {};
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Top Edges - if the bubble is in its bottom position (sticking downwards),
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // then we square the top edges. Otherwise, we square the edges based on the
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // position of the bubble within the window (the bubble is positioned in the
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // southeast corner in RTL and in the southwest corner in LTR).
3671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (style_ != STYLE_BOTTOM) {
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (base::i18n::IsRTL() != (style_ == STYLE_STANDARD_RIGHT)) {
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The text is RtL or the bubble is on the right side (but not both).
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Top Left corner.
3721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rad[0] = kBubbleCornerRadius;
3731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rad[1] = kBubbleCornerRadius;
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Top Right corner.
3761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rad[2] = kBubbleCornerRadius;
3771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rad[3] = kBubbleCornerRadius;
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Bottom edges - Keep these squared off if the bubble is in its standard
3821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // position (sticking upward).
3831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (style_ != STYLE_STANDARD && style_ != STYLE_STANDARD_RIGHT) {
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Bottom Right Corner.
3851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    rad[4] = kBubbleCornerRadius;
3861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    rad[5] = kBubbleCornerRadius;
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Bottom Left Corner.
3891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    rad[6] = kBubbleCornerRadius;
3901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    rad[7] = kBubbleCornerRadius;
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Draw the bubble's shadow.
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int width = popup_bounds.width();
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int height = popup_bounds.height();
3961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  gfx::Rect rect(gfx::Rect(popup_bounds.size()));
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPaint shadow_paint;
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shadow_paint.setAntiAlias(true);
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shadow_paint.setColor(kShadowColor);
4001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  SkRRect rrect;
4021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  rrect.setRectRadii(RectToSkRect(rect), (const SkVector*)rad);
4031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  canvas->sk_canvas()->drawRRect(rrect, paint);
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Draw the bubble.
4061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  rect.SetRect(SkIntToScalar(kShadowThickness),
4071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci               SkIntToScalar(kShadowThickness),
4081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci               SkIntToScalar(width),
4091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci               SkIntToScalar(height));
4101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  rrect.setRectRadii(RectToSkRect(rect), (const SkVector*)rad);
4111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  canvas->sk_canvas()->drawRRect(rrect, paint);
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Draw highlight text and then the text body. In order to make sure the text
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // is aligned to the right on RTL UIs, we mirror the text bounds if the
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // locale is RTL.
4165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const gfx::FontList font_list;
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int text_width = std::min(
4185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      gfx::GetStringWidth(text_, font_list),
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      width - (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding);
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int text_height = height - (kShadowThickness * 2);
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect body_bounds(kShadowThickness + kTextPositionX,
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        kShadowThickness,
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        std::max(0, text_width),
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        std::max(0, text_height));
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  body_bounds.set_x(GetMirroredXForRect(body_bounds));
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkColor text_color =
427f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      theme_service_->GetColor(ThemeProperties::COLOR_STATUS_BAR_TEXT);
4285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  canvas->DrawStringRect(text_, font_list, text_color, body_bounds);
4295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// StatusBubbleViews::StatusViewAnimation --------------------------------------
4335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)StatusBubbleViews::StatusViewAnimation::StatusViewAnimation(
4355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    StatusView* status_view,
4365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    double opacity_start,
4375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    double opacity_end)
4385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : gfx::LinearAnimation(kFramerate, this),
4395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      status_view_(status_view),
4405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      opacity_start_(opacity_start),
4415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      opacity_end_(opacity_end) {
4425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)StatusBubbleViews::StatusViewAnimation::~StatusViewAnimation() {
4455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Remove ourself as a delegate so that we don't get notified when
4465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // animations end as a result of destruction.
4475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  set_delegate(NULL);
4485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)double StatusBubbleViews::StatusViewAnimation::GetCurrentOpacity() {
4515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return opacity_start_ + (opacity_end_ - opacity_start_) *
4525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      gfx::LinearAnimation::GetCurrentValue();
4535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void StatusBubbleViews::StatusViewAnimation::AnimateToState(double state) {
4565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  status_view_->SetOpacity(GetCurrentOpacity());
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void StatusBubbleViews::StatusViewAnimation::AnimationEnded(
4605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const gfx::Animation* animation) {
4615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  status_view_->SetOpacity(opacity_end_);
4625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  status_view_->OnAnimationEnded();
4635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// StatusBubbleViews::StatusViewExpander ---------------------------------------
4665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Manages the expansion and contraction of the status bubble as it accommodates
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// URLs too long to fit in the standard bubble. Changes are passed through the
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusView to paint.
470d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)class StatusBubbleViews::StatusViewExpander : public gfx::LinearAnimation,
471d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                              public gfx::AnimationDelegate {
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StatusViewExpander(StatusBubbleViews* status_bubble,
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     StatusView* status_view)
475d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      : gfx::LinearAnimation(kFramerate, this),
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        status_bubble_(status_bubble),
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        status_view_(status_view),
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        expansion_start_(0),
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        expansion_end_(0) {
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Manage the expansion of the bubble.
483a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void StartExpansion(const base::string16& expanded_text,
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      int current_width,
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      int expansion_end);
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set width of fully expanded bubble.
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetExpandedWidth(int expanded_width);
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Animation functions.
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int GetCurrentBubbleWidth();
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetBubbleWidth(int width);
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void AnimateToState(double state) OVERRIDE;
495d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Manager that owns us.
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StatusBubbleViews* status_bubble_;
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Change the bounds and text of this view.
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StatusView* status_view_;
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Text elided (if needed) to fit maximum status bar width.
504a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 expanded_text_;
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Widths at expansion start and end.
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int expansion_start_;
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int expansion_end_;
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusViewExpander::AnimateToState(double state) {
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetBubbleWidth(GetCurrentBubbleWidth());
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusViewExpander::AnimationEnded(
516d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const gfx::Animation* animation) {
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetBubbleWidth(expansion_end_);
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_view_->SetText(expanded_text_, false);
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusViewExpander::StartExpansion(
522a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16& expanded_text,
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int expansion_start,
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int expansion_end) {
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expanded_text_ = expanded_text;
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expansion_start_ = expansion_start;
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expansion_end_ = expansion_end;
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int min_duration = std::max(kMinExpansionStepDurationMS,
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(kMaxExpansionStepDurationMS *
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (expansion_end - expansion_start) / 100.0));
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetDuration(std::min(kMaxExpansionStepDurationMS, min_duration));
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Start();
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int StatusBubbleViews::StatusViewExpander::GetCurrentBubbleWidth() {
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<int>(expansion_start_ +
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (expansion_end_ - expansion_start_) *
538d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          gfx::LinearAnimation::GetCurrentValue());
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::StatusViewExpander::SetBubbleWidth(int width) {
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_bubble_->SetBubbleWidth(width);
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_view_->SchedulePaint();
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// StatusBubbleViews -----------------------------------------------------------
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int StatusBubbleViews::kShadowThickness = 1;
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)StatusBubbleViews::StatusBubbleViews(views::View* base_view)
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : contains_mouse_(false),
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset_(0),
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base_view_(base_view),
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_(NULL),
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      download_shelf_is_visible_(false),
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_expanded_(false),
558c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      expand_timer_factory_(this) {
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expand_view_.reset();
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)StatusBubbleViews::~StatusBubbleViews() {
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CancelExpandTimer();
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_.get())
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->CloseNow();
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::Init() {
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!popup_.get()) {
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_.reset(new views::Widget);
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::Widget* frame = base_view_->GetWidget();
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!view_)
5730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      view_ = new StatusView(popup_.get(), frame->GetThemeProvider());
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!expand_view_.get())
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expand_view_.reset(new StatusViewExpander(this, view_));
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
577eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params.accept_events = false;
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
5802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    params.parent = frame->GetNativeView();
581116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    params.context = frame->GetNativeWindow();
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->Init(params);
583a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    popup_->GetNativeView()->SetName("StatusBubbleViews");
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We do our own animation and don't want any from the system.
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->SetVisibilityChangedAnimationsEnabled(false);
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->SetOpacity(0x00);
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->SetContentsView(view_);
58868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    ash::wm::GetWindowState(popup_->GetNativeWindow())->
58958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        set_ignored_by_shelf(true);
5905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    RepositionPopup();
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::Reposition() {
5955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // In restored mode, the client area has a client edge between it and the
5965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // frame.
5975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int overlap = kShadowThickness;
5985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int height = GetPreferredSize().height();
5995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int base_view_height = base_view()->bounds().height();
6005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  gfx::Point origin(-overlap, base_view_height - height + overlap);
6015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SetBounds(origin.x(), origin.y(), base_view()->bounds().width() / 3, height);
6025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void StatusBubbleViews::RepositionPopup() {
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_.get()) {
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Point top_left;
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::View::ConvertPointToScreen(base_view_, &top_left);
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->SetBounds(gfx::Rect(top_left.x() + position_.x(),
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                top_left.y() + position_.y(),
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                size_.width(), size_.height()));
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Size StatusBubbleViews::GetPreferredSize() {
6165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return gfx::Size(0, gfx::FontList().GetHeight() + kTotalVerticalPadding);
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::SetBounds(int x, int y, int w, int h) {
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  original_position_.SetPoint(x, y);
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  position_.SetPoint(base_view_->GetMirroredXWithWidthInView(x, w), y);
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_.SetSize(w, h);
6235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  RepositionPopup();
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_.get() && contains_mouse_)
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AvoidMouse(last_mouse_moved_location_);
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
628a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void StatusBubbleViews::SetStatus(const base::string16& status_text) {
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (size_.IsEmpty())
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // We have no bounds, don't attempt to show the popup.
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status_text_ == status_text && !status_text.empty())
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsFrameVisible())
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Don't show anything if the parent isn't visible.
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Init();
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_text_ = status_text;
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_text_.empty()) {
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->SetText(status_text, true);
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->Show();
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (!url_text_.empty()) {
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->SetText(url_text_, true);
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
6465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    view_->SetText(base::string16(), true);
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::SetURL(const GURL& url, const std::string& languages) {
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_ = url;
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  languages_ = languages;
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (size_.IsEmpty())
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // We have no bounds, don't attempt to show the popup.
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Init();
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we want to clear a displayed URL but there is a status still to
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // display, display that status instead.
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (url.is_empty() && !status_text_.empty()) {
661a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    url_text_ = base::string16();
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (IsFrameVisible())
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->SetText(status_text_, true);
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Reset expansion state only when bubble is completely hidden.
6685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (view_->state() == StatusView::BUBBLE_HIDDEN) {
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is_expanded_ = false;
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetBubbleWidth(GetStandardStatusBubbleWidth());
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set Elided Text corresponding to the GURL object.
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect popup_bounds = popup_->GetWindowBoundsInScreen();
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int text_width = static_cast<int>(popup_bounds.width() -
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1);
6775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  url_text_ = ElideUrl(url, gfx::FontList(), text_width, languages);
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // An URL is always treated as a left-to-right string. On right-to-left UIs
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we need to explicitly mark the URL as LTR to make sure it is displayed
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // correctly.
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_text_ = base::i18n::GetDisplayStringInLTRDirectionality(url_text_);
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsFrameVisible()) {
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->SetText(url_text_, true);
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CancelExpandTimer();
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If bubble is already in expanded state, shift to adjust to new text
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // size (shrinking or expanding). Otherwise delay.
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (is_expanded_ && !url.is_empty()) {
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ExpandBubble();
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (net::FormatUrl(url, languages).length() > url_text_.length()) {
694b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      base::MessageLoop::current()->PostDelayedTask(
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          FROM_HERE,
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::Bind(&StatusBubbleViews::ExpandBubble,
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     expand_timer_factory_.GetWeakPtr()),
6985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          base::TimeDelta::FromMilliseconds(kExpandHoverDelayMS));
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::Hide() {
704a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  status_text_ = base::string16();
705a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  url_text_ = base::string16();
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (view_)
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->Hide();
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::MouseMoved(const gfx::Point& location,
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   bool left_content) {
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  contains_mouse_ = !left_content;
7137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (left_content) {
7145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    RepositionPopup();
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
7167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_mouse_moved_location_ = location;
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (view_) {
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->ResetTimer();
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (view_->state() != StatusView::BUBBLE_HIDDEN &&
7235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        view_->state() != StatusView::BUBBLE_HIDING_FADE &&
7245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        view_->state() != StatusView::BUBBLE_HIDING_TIMER) {
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AvoidMouse(location);
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::UpdateDownloadShelfVisibility(bool visible) {
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  download_shelf_is_visible_ = visible;
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::AvoidMouse(const gfx::Point& location) {
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the position of the frame.
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point top_left;
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View::ConvertPointToScreen(base_view_, &top_left);
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Border included.
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int window_width = base_view_->GetLocalBounds().width();
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the cursor position relative to the popup.
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point relative_location = location;
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::i18n::IsRTL()) {
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int top_right_x = top_left.x() + window_width;
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    relative_location.set_x(top_right_x - relative_location.x());
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    relative_location.set_x(
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        relative_location.x() - (top_left.x() + position_.x()));
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  relative_location.set_y(
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      relative_location.y() - (top_left.y() + position_.y()));
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the mouse is in a position where we think it would move the
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // status bubble, figure out where and how the bubble should be moved.
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (relative_location.y() > -kMousePadding &&
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      relative_location.x() < size_.width() + kMousePadding) {
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int offset = kMousePadding + relative_location.y();
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Make the movement non-linear.
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    offset = offset * offset / kMousePadding;
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // When the mouse is entering from the right, we want the offset to be
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // scaled by how horizontally far away the cursor is from the bubble.
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (relative_location.x() > size_.width()) {
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset = static_cast<int>(static_cast<float>(offset) * (
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          static_cast<float>(kMousePadding -
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              (relative_location.x() - size_.width())) /
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          static_cast<float>(kMousePadding)));
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Cap the offset and change the visual presentation of the bubble
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // depending on where it ends up (so that rounded corners square off
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // and mate to the edges of the tab content).
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (offset >= size_.height() - kShadowThickness * 2) {
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset = size_.height() - kShadowThickness * 2;
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->SetStyle(StatusView::STYLE_BOTTOM);
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (offset > kBubbleCornerRadius / 2 - kShadowThickness) {
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->SetStyle(StatusView::STYLE_FLOATING);
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->SetStyle(StatusView::STYLE_STANDARD);
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Check if the bubble sticks out from the monitor or will obscure
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // download shelf.
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::NativeView window = base_view_->GetWidget()->GetNativeView();
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect monitor_rect = gfx::Screen::GetScreenFor(window)->
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetDisplayNearestWindow(window).work_area();
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int bubble_bottom_y = top_left.y() + position_.y() + size_.height();
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (bubble_bottom_y + offset > monitor_rect.height() ||
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (download_shelf_is_visible_ &&
7925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)         (view_->style() == StatusView::STYLE_FLOATING ||
7935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          view_->style() == StatusView::STYLE_BOTTOM))) {
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The offset is still too large. Move the bubble to the right and reset
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Y offset_ to zero.
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view_->SetStyle(StatusView::STYLE_STANDARD_RIGHT);
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset_ = 0;
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Subtract border width + bubble width.
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int right_position_x = window_width - (position_.x() + size_.width());
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      popup_->SetBounds(gfx::Rect(top_left.x() + right_position_x,
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  top_left.y() + position_.y(),
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  size_.width(), size_.height()));
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset_ = offset;
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      popup_->SetBounds(gfx::Rect(top_left.x() + position_.x(),
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  top_left.y() + position_.y() + offset_,
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  size_.width(), size_.height()));
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (offset_ != 0 ||
8115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      view_->style() == StatusView::STYLE_STANDARD_RIGHT) {
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    offset_ = 0;
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view_->SetStyle(StatusView::STYLE_STANDARD);
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->SetBounds(gfx::Rect(top_left.x() + position_.x(),
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                top_left.y() + position_.y(),
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                size_.width(), size_.height()));
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool StatusBubbleViews::IsFrameVisible() {
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget* frame = base_view_->GetWidget();
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!frame->IsVisible())
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget* window = frame->GetTopLevelWidget();
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !window || !window->IsMinimized();
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool StatusBubbleViews::IsFrameMaximized() {
8305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  views::Widget* frame = base_view_->GetWidget();
8315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  views::Widget* window = frame->GetTopLevelWidget();
8325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return window && window->IsMaximized();
8335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
8345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::ExpandBubble() {
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Elide URL to maximum possible size, then check actual length (it may
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // still be too long to fit) before expanding bubble.
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect popup_bounds = popup_->GetWindowBoundsInScreen();
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int max_status_bubble_width = GetMaxStatusBubbleWidth();
8405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const gfx::FontList font_list;
8415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  url_text_ = ElideUrl(url_, font_list, max_status_bubble_width, languages_);
8425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int expanded_bubble_width =
8435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      std::max(GetStandardStatusBubbleWidth(),
8445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)               std::min(gfx::GetStringWidth(url_text_, font_list) +
8455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            (kShadowThickness * 2) + kTextPositionX +
8465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            kTextHorizPadding + 1,
8475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        max_status_bubble_width));
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_expanded_ = true;
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expand_view_->StartExpansion(url_text_, popup_bounds.width(),
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               expanded_bubble_width);
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int StatusBubbleViews::GetStandardStatusBubbleWidth() {
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return base_view_->bounds().width() / 3;
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int StatusBubbleViews::GetMaxStatusBubbleWidth() {
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const ui::NativeTheme* theme = base_view_->GetNativeTheme();
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<int>(std::max(0, base_view_->bounds().width() -
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (kShadowThickness * 2) - kTextPositionX - kTextHorizPadding - 1 -
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      views::NativeScrollBar::GetVerticalScrollBarWidth(theme)));
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::SetBubbleWidth(int width) {
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_.set_width(width);
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetBounds(original_position_.x(), original_position_.y(),
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            size_.width(), size_.height());
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusBubbleViews::CancelExpandTimer() {
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (expand_timer_factory_.HasWeakPtrs())
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expand_timer_factory_.InvalidateWeakPtrs();
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
874