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