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 "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/base/events/event.h"
22 #include "ui/base/events/event_utils.h"
23 #include "ui/base/gestures/gesture_configuration.h"
24 #include "ui/views/controls/glow_hover_controller.h"
27 const int64 kActivationDelayMS
= 200;
31 TabScrubber
* TabScrubber::GetInstance() {
32 static TabScrubber
* instance
= NULL
;
34 instance
= new TabScrubber();
39 gfx::Point
TabScrubber::GetStartPoint(
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());
51 bool TabScrubber::IsActivationPending() {
52 return activate_timer_
.IsRunning();
55 TabScrubber::TabScrubber()
60 swipe_direction_(LEFT
),
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);
69 chrome::NOTIFICATION_BROWSER_CLOSED
,
70 content::NotificationService::AllSources());
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();
79 void TabScrubber::OnScrollEvent(ui::ScrollEvent
* event
) {
80 if (event
->type() == ui::ET_SCROLL_FLING_CANCEL
||
81 event
->type() == ui::ET_SCROLL_FLING_START
) {
83 immersive_reveal_lock_
.reset();
87 if (event
->finger_count() != 3)
90 Browser
* browser
= GetActiveBrowser();
91 if (!browser
|| (scrubbing_
&& browser_
&& browser
!= browser_
) ||
92 (highlighted_tab_
!= -1 &&
93 highlighted_tab_
>= browser
->tab_strip_model()->count())) {
98 BrowserView
* browser_view
=
99 BrowserView::GetBrowserViewForNativeWindow(
100 browser
->window()->GetNativeWindow());
101 TabStrip
* tab_strip
= browser_view
->tabstrip();
103 if (tab_strip
->IsAnimating()) {
108 // We are handling the event.
109 event
->StopPropagation();
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_
;
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(),
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
));
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(),
141 swipe_x_
= start_point
.x();
142 swipe_y_
= start_point
.y();
143 swipe_direction_
= direction
;
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
;
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
);
164 int new_index
= tab_strip
->GetModelIndexOfTab(new_tab
);
165 if (highlighted_tab_
== -1 &&
166 new_index
== browser
->tab_strip_model()->active_index())
169 if (new_index
!= highlighted_tab_
) {
170 if (activate_timer_
.IsRunning()) {
171 activate_timer_
.Reset();
173 int delay
= use_default_activation_delay_
?
174 ui::GestureConfiguration::tab_scrub_activation_delay_in_ms() :
177 activate_timer_
.Start(FROM_HERE
,
178 base::TimeDelta::FromMilliseconds(delay
),
179 base::Bind(&TabScrubber::FinishScrub
,
180 weak_ptr_factory_
.GetWeakPtr(),
184 if (highlighted_tab_
!= -1) {
185 Tab
* tab
= tab_strip
->tab_at(highlighted_tab_
);
186 tab
->hover_controller()->HideImmediately();
188 if (new_index
== browser
->tab_strip_model()->active_index()) {
189 highlighted_tab_
= -1;
191 highlighted_tab_
= new_index
;
192 new_tab
->hover_controller()->Show(views::GlowHoverController::PRONOUNCED
);
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
);
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();
210 highlighted_tab_
= -1;
215 void TabScrubber::TabStripAddedTabAt(TabStrip
* tab_strip
, int index
) {
216 if (highlighted_tab_
== -1)
219 if (index
< highlighted_tab_
)
223 void TabScrubber::TabStripMovedTab(TabStrip
* tab_strip
,
226 if (highlighted_tab_
== -1)
229 if (from_index
== highlighted_tab_
)
230 highlighted_tab_
= to_index
;
231 else if (from_index
< highlighted_tab_
&& highlighted_tab_
<= to_index
)
233 else if (from_index
> highlighted_tab_
&& highlighted_tab_
>= to_index
)
237 void TabScrubber::TabStripRemovedTabAt(TabStrip
* tab_strip
, int index
) {
238 if (highlighted_tab_
== -1)
240 if (index
== highlighted_tab_
) {
244 if (index
< highlighted_tab_
)
248 void TabScrubber::TabStripDeleted(TabStrip
* tab_strip
) {
249 if (highlighted_tab_
== -1)
253 Browser
* TabScrubber::GetActiveBrowser() {
254 aura::Window
* active_window
= ash::wm::GetActiveWindow();
258 Browser
* browser
= chrome::FindBrowserWithWindow(active_window
);
259 if (!browser
|| browser
->type() != Browser::TYPE_TABBED
)
265 void TabScrubber::FinishScrub(bool activate
) {
266 activate_timer_
.Stop();
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();
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);
282 tab_strip
->RemoveObserver(this);
287 highlighted_tab_
= -1;