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.
5 #include "chrome/browser/ui/views/ash/tab_scrubber.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 "content/public/browser/notification_service.h"
18 #include "content/public/browser/notification_source.h"
19 #include "ui/aura/window.h"
20 #include "ui/events/event.h"
21 #include "ui/events/event_utils.h"
22 #include "ui/events/gesture_detection/gesture_configuration.h"
23 #include "ui/views/controls/glow_hover_controller.h"
26 const int64 kActivationDelayMS
= 200;
30 TabScrubber
* TabScrubber::GetInstance() {
31 static TabScrubber
* instance
= NULL
;
33 instance
= new TabScrubber();
38 gfx::Point
TabScrubber::GetStartPoint(
41 TabScrubber::Direction direction
) {
42 int initial_tab_offset
= Tab::GetMiniWidth() / 2;
43 gfx::Rect tab_bounds
= tab_strip
->tab_at(index
)->bounds();
44 float x
= direction
== LEFT
?
45 tab_bounds
.x() + initial_tab_offset
:
46 tab_bounds
.right() - initial_tab_offset
;
47 return gfx::Point(x
, tab_bounds
.CenterPoint().y());
50 bool TabScrubber::IsActivationPending() {
51 return activate_timer_
.IsRunning();
54 TabScrubber::TabScrubber()
59 swipe_direction_(LEFT
),
61 activate_timer_(true, false),
62 activation_delay_(kActivationDelayMS
),
63 use_default_activation_delay_(true),
64 weak_ptr_factory_(this) {
65 ash::Shell::GetInstance()->AddPreTargetHandler(this);
68 chrome::NOTIFICATION_BROWSER_CLOSED
,
69 content::NotificationService::AllSources());
72 TabScrubber::~TabScrubber() {
73 // Note: The weak_ptr_factory_ should invalidate its weak pointers before
74 // any other members are destroyed.
75 weak_ptr_factory_
.InvalidateWeakPtrs();
78 void TabScrubber::OnScrollEvent(ui::ScrollEvent
* event
) {
79 if (event
->type() == ui::ET_SCROLL_FLING_CANCEL
||
80 event
->type() == ui::ET_SCROLL_FLING_START
) {
82 immersive_reveal_lock_
.reset();
86 if (event
->finger_count() != 3)
89 Browser
* browser
= GetActiveBrowser();
90 if (!browser
|| (scrubbing_
&& browser_
&& browser
!= browser_
) ||
91 (highlighted_tab_
!= -1 &&
92 highlighted_tab_
>= browser
->tab_strip_model()->count())) {
97 BrowserView
* browser_view
=
98 BrowserView::GetBrowserViewForNativeWindow(
99 browser
->window()->GetNativeWindow());
100 TabStrip
* tab_strip
= browser_view
->tabstrip();
102 if (tab_strip
->IsAnimating()) {
107 // We are handling the event.
108 event
->StopPropagation();
110 float x_offset
= event
->x_offset();
111 int last_tab_index
= highlighted_tab_
== -1 ?
112 browser
->tab_strip_model()->active_index() : highlighted_tab_
;
114 swipe_direction_
= (x_offset
< 0) ? LEFT
: RIGHT
;
115 const gfx::Point start_point
=
116 GetStartPoint(tab_strip
,
117 browser
->tab_strip_model()->active_index(),
122 swipe_x_
= start_point
.x();
123 swipe_y_
= start_point
.y();
124 ImmersiveModeController
* immersive_controller
=
125 browser_view
->immersive_mode_controller();
126 if (immersive_controller
->IsEnabled()) {
127 immersive_reveal_lock_
.reset(immersive_controller
->GetRevealedLock(
128 ImmersiveModeController::ANIMATE_REVEAL_YES
));
130 tab_strip
->AddObserver(this);
131 } else if (highlighted_tab_
== -1) {
132 Direction direction
= (x_offset
< 0) ? LEFT
: RIGHT
;
133 if (direction
!= swipe_direction_
) {
134 const gfx::Point start_point
=
135 GetStartPoint(tab_strip
,
136 browser
->tab_strip_model()->active_index(),
138 swipe_x_
= start_point
.x();
139 swipe_y_
= start_point
.y();
140 swipe_direction_
= direction
;
144 swipe_x_
+= x_offset
;
145 Tab
* first_tab
= tab_strip
->tab_at(0);
146 int first_tab_center
= first_tab
->bounds().CenterPoint().x();
147 Tab
* last_tab
= tab_strip
->tab_at(tab_strip
->tab_count() - 1);
148 int last_tab_tab_center
= last_tab
->bounds().CenterPoint().x();
149 if (swipe_x_
< first_tab_center
)
150 swipe_x_
= first_tab_center
;
151 if (swipe_x_
> last_tab_tab_center
)
152 swipe_x_
= last_tab_tab_center
;
154 Tab
* initial_tab
= tab_strip
->tab_at(last_tab_index
);
155 gfx::Point
tab_point(swipe_x_
, swipe_y_
);
156 views::View::ConvertPointToTarget(tab_strip
, initial_tab
, &tab_point
);
157 Tab
* new_tab
= tab_strip
->GetTabAt(initial_tab
, tab_point
);
161 int new_index
= tab_strip
->GetModelIndexOfTab(new_tab
);
162 if (highlighted_tab_
== -1 &&
163 new_index
== browser
->tab_strip_model()->active_index())
166 if (new_index
!= highlighted_tab_
) {
167 if (activate_timer_
.IsRunning()) {
168 activate_timer_
.Reset();
170 int delay
= use_default_activation_delay_
171 ? ui::GestureConfiguration::GetInstance()
172 ->tab_scrub_activation_delay_in_ms()
175 activate_timer_
.Start(FROM_HERE
,
176 base::TimeDelta::FromMilliseconds(delay
),
177 base::Bind(&TabScrubber::FinishScrub
,
178 weak_ptr_factory_
.GetWeakPtr(),
182 if (highlighted_tab_
!= -1) {
183 Tab
* tab
= tab_strip
->tab_at(highlighted_tab_
);
184 tab
->hover_controller()->HideImmediately();
186 if (new_index
== browser
->tab_strip_model()->active_index()) {
187 highlighted_tab_
= -1;
189 highlighted_tab_
= new_index
;
190 new_tab
->hover_controller()->Show(views::GlowHoverController::PRONOUNCED
);
193 if (highlighted_tab_
!= -1) {
194 gfx::Point
hover_point(swipe_x_
, swipe_y_
);
195 views::View::ConvertPointToTarget(tab_strip
, new_tab
, &hover_point
);
196 new_tab
->hover_controller()->SetLocation(hover_point
);
200 void TabScrubber::Observe(int type
,
201 const content::NotificationSource
& source
,
202 const content::NotificationDetails
& details
) {
203 if (content::Source
<Browser
>(source
).ptr() == browser_
) {
204 activate_timer_
.Stop();
208 highlighted_tab_
= -1;
213 void TabScrubber::TabStripAddedTabAt(TabStrip
* tab_strip
, int index
) {
214 if (highlighted_tab_
== -1)
217 if (index
< highlighted_tab_
)
221 void TabScrubber::TabStripMovedTab(TabStrip
* tab_strip
,
224 if (highlighted_tab_
== -1)
227 if (from_index
== highlighted_tab_
)
228 highlighted_tab_
= to_index
;
229 else if (from_index
< highlighted_tab_
&& highlighted_tab_
<= to_index
)
231 else if (from_index
> highlighted_tab_
&& highlighted_tab_
>= to_index
)
235 void TabScrubber::TabStripRemovedTabAt(TabStrip
* tab_strip
, int index
) {
236 if (highlighted_tab_
== -1)
238 if (index
== highlighted_tab_
) {
242 if (index
< highlighted_tab_
)
246 void TabScrubber::TabStripDeleted(TabStrip
* tab_strip
) {
247 if (highlighted_tab_
== -1)
251 Browser
* TabScrubber::GetActiveBrowser() {
252 aura::Window
* active_window
= ash::wm::GetActiveWindow();
256 Browser
* browser
= chrome::FindBrowserWithWindow(active_window
);
257 if (!browser
|| browser
->type() != Browser::TYPE_TABBED
)
263 void TabScrubber::FinishScrub(bool activate
) {
264 activate_timer_
.Stop();
266 if (browser_
&& browser_
->window()) {
267 BrowserView
* browser_view
=
268 BrowserView::GetBrowserViewForNativeWindow(
269 browser_
->window()->GetNativeWindow());
270 TabStrip
* tab_strip
= browser_view
->tabstrip();
271 if (activate
&& highlighted_tab_
!= -1) {
272 Tab
* tab
= tab_strip
->tab_at(highlighted_tab_
);
273 tab
->hover_controller()->HideImmediately();
276 highlighted_tab_
- browser_
->tab_strip_model()->active_index());
277 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.ScrubDistance", distance
, 0, 20, 21);
278 browser_
->tab_strip_model()->ActivateTabAt(highlighted_tab_
, true);
280 tab_strip
->RemoveObserver(this);
285 highlighted_tab_
= -1;