• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/ui/views/ash/tab_scrubber.h"
6 
7 #include "ash/shell.h"
8 #include "ash/wm/window_util.h"
9 #include "base/metrics/histogram.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_finder.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "chrome/browser/ui/views/frame/browser_view.h"
15 #include "chrome/browser/ui/views/tabs/tab.h"
16 #include "chrome/browser/ui/views/tabs/tab_strip.h"
17 #include "chrome/common/pref_names.h"
18 #include "content/public/browser/notification_service.h"
19 #include "content/public/browser/notification_source.h"
20 #include "ui/aura/window.h"
21 #include "ui/events/event.h"
22 #include "ui/events/event_utils.h"
23 #include "ui/events/gestures/gesture_configuration.h"
24 #include "ui/views/controls/glow_hover_controller.h"
25 
26 namespace {
27 const int64 kActivationDelayMS = 200;
28 }
29 
30 // static
GetInstance()31 TabScrubber* TabScrubber::GetInstance() {
32   static TabScrubber* instance = NULL;
33   if (!instance)
34     instance = new TabScrubber();
35   return instance;
36 }
37 
38 // static
GetStartPoint(TabStrip * tab_strip,int index,TabScrubber::Direction direction)39 gfx::Point TabScrubber::GetStartPoint(
40     TabStrip* tab_strip,
41     int index,
42     TabScrubber::Direction direction) {
43   int initial_tab_offset = Tab::GetMiniWidth() / 2;
44   gfx::Rect tab_bounds = tab_strip->tab_at(index)->bounds();
45   float x = direction == LEFT ?
46       tab_bounds.x() + initial_tab_offset :
47           tab_bounds.right() - initial_tab_offset;
48   return gfx::Point(x, tab_bounds.CenterPoint().y());
49 }
50 
IsActivationPending()51 bool TabScrubber::IsActivationPending() {
52   return activate_timer_.IsRunning();
53 }
54 
TabScrubber()55 TabScrubber::TabScrubber()
56     : scrubbing_(false),
57       browser_(NULL),
58       swipe_x_(-1),
59       swipe_y_(-1),
60       swipe_direction_(LEFT),
61       highlighted_tab_(-1),
62       activate_timer_(true, false),
63       activation_delay_(kActivationDelayMS),
64       use_default_activation_delay_(true),
65       weak_ptr_factory_(this) {
66   ash::Shell::GetInstance()->AddPreTargetHandler(this);
67   registrar_.Add(
68       this,
69       chrome::NOTIFICATION_BROWSER_CLOSED,
70       content::NotificationService::AllSources());
71 }
72 
~TabScrubber()73 TabScrubber::~TabScrubber() {
74   // Note: The weak_ptr_factory_ should invalidate  its weak pointers before
75   // any other members are destroyed.
76   weak_ptr_factory_.InvalidateWeakPtrs();
77 }
78 
OnScrollEvent(ui::ScrollEvent * event)79 void TabScrubber::OnScrollEvent(ui::ScrollEvent* event) {
80   if (event->type() == ui::ET_SCROLL_FLING_CANCEL ||
81       event->type() == ui::ET_SCROLL_FLING_START) {
82     FinishScrub(true);
83     immersive_reveal_lock_.reset();
84     return;
85   }
86 
87   if (event->finger_count() != 3)
88     return;
89 
90   Browser* browser = GetActiveBrowser();
91   if (!browser || (scrubbing_ && browser_ && browser != browser_) ||
92       (highlighted_tab_ != -1 &&
93           highlighted_tab_ >= browser->tab_strip_model()->count())) {
94     FinishScrub(false);
95     return;
96   }
97 
98   BrowserView* browser_view =
99       BrowserView::GetBrowserViewForNativeWindow(
100           browser->window()->GetNativeWindow());
101   TabStrip* tab_strip = browser_view->tabstrip();
102 
103   if (tab_strip->IsAnimating()) {
104     FinishScrub(false);
105     return;
106   }
107 
108   // We are handling the event.
109   event->StopPropagation();
110 
111   float x_offset = event->x_offset();
112   if (!ui::IsNaturalScrollEnabled())
113     x_offset = -x_offset;
114   int last_tab_index = highlighted_tab_ == -1 ?
115       browser->tab_strip_model()->active_index() : highlighted_tab_;
116   if (!scrubbing_) {
117     swipe_direction_ = (x_offset < 0) ? LEFT : RIGHT;
118     const gfx::Point start_point =
119         GetStartPoint(tab_strip,
120                       browser->tab_strip_model()->active_index(),
121                       swipe_direction_);
122     browser_ = browser;
123     scrubbing_ = true;
124 
125     swipe_x_ = start_point.x();
126     swipe_y_ = start_point.y();
127     ImmersiveModeController* immersive_controller =
128         browser_view->immersive_mode_controller();
129     if (immersive_controller->IsEnabled()) {
130       immersive_reveal_lock_.reset(immersive_controller->GetRevealedLock(
131           ImmersiveModeController::ANIMATE_REVEAL_YES));
132     }
133     tab_strip->AddObserver(this);
134   } else if (highlighted_tab_ == -1) {
135     Direction direction = (x_offset < 0) ? LEFT : RIGHT;
136     if (direction != swipe_direction_) {
137       const gfx::Point start_point =
138           GetStartPoint(tab_strip,
139                         browser->tab_strip_model()->active_index(),
140                         direction);
141       swipe_x_ = start_point.x();
142       swipe_y_ = start_point.y();
143       swipe_direction_ = direction;
144     }
145   }
146 
147   swipe_x_ += x_offset;
148   Tab* first_tab = tab_strip->tab_at(0);
149   int first_tab_center = first_tab->bounds().CenterPoint().x();
150   Tab* last_tab = tab_strip->tab_at(tab_strip->tab_count() - 1);
151   int last_tab_tab_center = last_tab->bounds().CenterPoint().x();
152   if (swipe_x_ < first_tab_center)
153     swipe_x_ = first_tab_center;
154   if (swipe_x_ > last_tab_tab_center)
155     swipe_x_ = last_tab_tab_center;
156 
157   Tab* initial_tab = tab_strip->tab_at(last_tab_index);
158   gfx::Point tab_point(swipe_x_, swipe_y_);
159   views::View::ConvertPointToTarget(tab_strip, initial_tab, &tab_point);
160   Tab* new_tab = tab_strip->GetTabAt(initial_tab, tab_point);
161   if (!new_tab)
162     return;
163 
164   int new_index = tab_strip->GetModelIndexOfTab(new_tab);
165   if (highlighted_tab_ == -1 &&
166       new_index == browser->tab_strip_model()->active_index())
167     return;
168 
169   if (new_index != highlighted_tab_) {
170     if (activate_timer_.IsRunning()) {
171       activate_timer_.Reset();
172     } else {
173       int delay = use_default_activation_delay_ ?
174           ui::GestureConfiguration::tab_scrub_activation_delay_in_ms() :
175           activation_delay_;
176       if (delay >= 0) {
177         activate_timer_.Start(FROM_HERE,
178                               base::TimeDelta::FromMilliseconds(delay),
179                               base::Bind(&TabScrubber::FinishScrub,
180                                          weak_ptr_factory_.GetWeakPtr(),
181                                          true));
182       }
183     }
184     if (highlighted_tab_ != -1) {
185       Tab* tab = tab_strip->tab_at(highlighted_tab_);
186       tab->hover_controller()->HideImmediately();
187     }
188     if (new_index == browser->tab_strip_model()->active_index()) {
189       highlighted_tab_ = -1;
190     } else {
191       highlighted_tab_ = new_index;
192       new_tab->hover_controller()->Show(views::GlowHoverController::PRONOUNCED);
193     }
194   }
195   if (highlighted_tab_ != -1) {
196     gfx::Point hover_point(swipe_x_, swipe_y_);
197     views::View::ConvertPointToTarget(tab_strip, new_tab, &hover_point);
198     new_tab->hover_controller()->SetLocation(hover_point);
199   }
200 }
201 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)202 void TabScrubber::Observe(int type,
203                           const content::NotificationSource& source,
204                           const content::NotificationDetails& details) {
205   if (content::Source<Browser>(source).ptr() == browser_) {
206     activate_timer_.Stop();
207     swipe_x_ = -1;
208     swipe_y_ = -1;
209     scrubbing_ = false;
210     highlighted_tab_ = -1;
211     browser_ = NULL;
212   }
213 }
214 
TabStripAddedTabAt(TabStrip * tab_strip,int index)215 void TabScrubber::TabStripAddedTabAt(TabStrip* tab_strip, int index) {
216   if (highlighted_tab_ == -1)
217     return;
218 
219   if (index < highlighted_tab_)
220     ++highlighted_tab_;
221 }
222 
TabStripMovedTab(TabStrip * tab_strip,int from_index,int to_index)223 void TabScrubber::TabStripMovedTab(TabStrip* tab_strip,
224                                    int from_index,
225                                    int to_index) {
226   if (highlighted_tab_ == -1)
227     return;
228 
229   if (from_index == highlighted_tab_)
230     highlighted_tab_ = to_index;
231   else if (from_index < highlighted_tab_&& highlighted_tab_<= to_index)
232     --highlighted_tab_;
233   else if (from_index > highlighted_tab_ && highlighted_tab_ >= to_index)
234     ++highlighted_tab_;
235 }
236 
TabStripRemovedTabAt(TabStrip * tab_strip,int index)237 void TabScrubber::TabStripRemovedTabAt(TabStrip* tab_strip, int index) {
238   if (highlighted_tab_ == -1)
239     return;
240   if (index == highlighted_tab_) {
241     FinishScrub(false);
242     return;
243   }
244   if (index < highlighted_tab_)
245     --highlighted_tab_;
246 }
247 
TabStripDeleted(TabStrip * tab_strip)248 void TabScrubber::TabStripDeleted(TabStrip* tab_strip) {
249   if (highlighted_tab_ == -1)
250     return;
251 }
252 
GetActiveBrowser()253 Browser* TabScrubber::GetActiveBrowser() {
254   aura::Window* active_window = ash::wm::GetActiveWindow();
255   if (!active_window)
256     return NULL;
257 
258   Browser* browser = chrome::FindBrowserWithWindow(active_window);
259   if (!browser || browser->type() != Browser::TYPE_TABBED)
260     return NULL;
261 
262   return browser;
263 }
264 
FinishScrub(bool activate)265 void TabScrubber::FinishScrub(bool activate) {
266   activate_timer_.Stop();
267 
268   if (browser_ && browser_->window()) {
269     BrowserView* browser_view =
270         BrowserView::GetBrowserViewForNativeWindow(
271             browser_->window()->GetNativeWindow());
272     TabStrip* tab_strip = browser_view->tabstrip();
273     if (activate && highlighted_tab_ != -1) {
274       Tab* tab = tab_strip->tab_at(highlighted_tab_);
275       tab->hover_controller()->HideImmediately();
276       int distance =
277           std::abs(
278               highlighted_tab_ - browser_->tab_strip_model()->active_index());
279       UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.ScrubDistance", distance, 0, 20, 20);
280       browser_->tab_strip_model()->ActivateTabAt(highlighted_tab_, true);
281     }
282     tab_strip->RemoveObserver(this);
283   }
284   swipe_x_ = -1;
285   swipe_y_ = -1;
286   scrubbing_ = false;
287   highlighted_tab_ = -1;
288 }
289