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/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"
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 int last_tab_index
= highlighted_tab_
== -1 ?
113 browser
->tab_strip_model()->active_index() : highlighted_tab_
;
115 swipe_direction_
= (x_offset
< 0) ? LEFT
: RIGHT
;
116 const gfx::Point start_point
=
117 GetStartPoint(tab_strip
,
118 browser
->tab_strip_model()->active_index(),
123 swipe_x_
= start_point
.x();
124 swipe_y_
= start_point
.y();
125 ImmersiveModeController
* immersive_controller
=
126 browser_view
->immersive_mode_controller();
127 if (immersive_controller
->IsEnabled()) {
128 immersive_reveal_lock_
.reset(immersive_controller
->GetRevealedLock(
129 ImmersiveModeController::ANIMATE_REVEAL_YES
));
131 tab_strip
->AddObserver(this);
132 } else if (highlighted_tab_
== -1) {
133 Direction direction
= (x_offset
< 0) ? LEFT
: RIGHT
;
134 if (direction
!= swipe_direction_
) {
135 const gfx::Point start_point
=
136 GetStartPoint(tab_strip
,
137 browser
->tab_strip_model()->active_index(),
139 swipe_x_
= start_point
.x();
140 swipe_y_
= start_point
.y();
141 swipe_direction_
= direction
;
145 swipe_x_
+= x_offset
;
146 Tab
* first_tab
= tab_strip
->tab_at(0);
147 int first_tab_center
= first_tab
->bounds().CenterPoint().x();
148 Tab
* last_tab
= tab_strip
->tab_at(tab_strip
->tab_count() - 1);
149 int last_tab_tab_center
= last_tab
->bounds().CenterPoint().x();
150 if (swipe_x_
< first_tab_center
)
151 swipe_x_
= first_tab_center
;
152 if (swipe_x_
> last_tab_tab_center
)
153 swipe_x_
= last_tab_tab_center
;
155 Tab
* initial_tab
= tab_strip
->tab_at(last_tab_index
);
156 gfx::Point
tab_point(swipe_x_
, swipe_y_
);
157 views::View::ConvertPointToTarget(tab_strip
, initial_tab
, &tab_point
);
158 Tab
* new_tab
= tab_strip
->GetTabAt(initial_tab
, tab_point
);
162 int new_index
= tab_strip
->GetModelIndexOfTab(new_tab
);
163 if (highlighted_tab_
== -1 &&
164 new_index
== browser
->tab_strip_model()->active_index())
167 if (new_index
!= highlighted_tab_
) {
168 if (activate_timer_
.IsRunning()) {
169 activate_timer_
.Reset();
171 int delay
= use_default_activation_delay_
?
172 ui::GestureConfiguration::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, 20);
278 browser_
->tab_strip_model()->ActivateTabAt(highlighted_tab_
, true);
280 tab_strip
->RemoveObserver(this);
285 highlighted_tab_
= -1;