Index: chrome/browser/ui/views/ash/tab_scrubber.cc |
diff --git a/chrome/browser/ui/views/ash/tab_scrubber.cc b/chrome/browser/ui/views/ash/tab_scrubber.cc |
index 04258140fb3552325a1d1d6f24c55bf5c60dd4dc..68f6c148aeba7d0c4e496bb5c7673547306946d1 100644 |
--- a/chrome/browser/ui/views/ash/tab_scrubber.cc |
+++ b/chrome/browser/ui/views/ash/tab_scrubber.cc |
@@ -10,25 +10,20 @@ |
#include "chrome/browser/ui/browser_finder.h" |
#include "chrome/browser/ui/tabs/tab_strip_model.h" |
#include "chrome/browser/ui/views/frame/browser_view.h" |
+#include "chrome/browser/ui/views/immersive_mode_controller.h" |
#include "chrome/browser/ui/views/tabs/tab.h" |
#include "chrome/browser/ui/views/tabs/tab_strip.h" |
#include "chrome/common/chrome_notification_types.h" |
+#include "content/public/browser/notification_service.h" |
#include "content/public/browser/notification_source.h" |
#include "ui/aura/window.h" |
#include "ui/base/events/event.h" |
#include "ui/base/events/event_utils.h" |
+#include "ui/views/controls/glow_hover_controller.h" |
namespace { |
-Tab* GetTabAt(TabStrip* tab_strip, gfx::Point point) { |
- for (int i = 0; i < tab_strip->tab_count(); ++i) { |
- Tab* tab = tab_strip->tab_at(i); |
- if (tab_strip->tab_at(i)->bounds().Contains(point)) |
- return tab; |
- } |
- return NULL; |
-} |
- |
-const int kInitialTabOffset = 10; |
+const int64 kActivationDelayMS = 200; |
+const int64 kCancelImmersiveRevelDelayMS = 200; |
} |
// static |
@@ -39,32 +34,64 @@ TabScrubber* TabScrubber::GetInstance() { |
return instance; |
} |
+// static |
+gfx::Point TabScrubber::GetStartPoint( |
+ TabStrip* tab_strip, |
+ int index, |
+ TabScrubber::Direction direction) { |
+ int initial_tab_offset = Tab::GetMiniWidth() / 2; |
+ gfx::Rect tab_bounds = tab_strip->tab_at(index)->bounds(); |
+ float x = direction == LEFT ? |
+ tab_bounds.x() + initial_tab_offset : |
+ tab_bounds.right() - initial_tab_offset; |
+ return gfx::Point(x, tab_bounds.CenterPoint().y()); |
+} |
+ |
+bool TabScrubber::IsActivationPending() { |
+ return activate_timer_.IsRunning(); |
+} |
+ |
TabScrubber::TabScrubber() |
: scrubbing_(false), |
browser_(NULL), |
- scroll_x_(-1), |
- scroll_y_(-1) { |
+ swipe_x_(-1), |
+ swipe_y_(-1), |
+ swipe_direction_(LEFT), |
+ highlighted_tab_(-1), |
+ activate_timer_(true, false), |
+ activation_delay_(base::TimeDelta::FromMilliseconds(kActivationDelayMS)), |
+ should_cancel_immersive_reveal_(false), |
+ cancel_immersive_reveal_timer_(true, false), |
+ weak_ptr_factory_(this) { |
ash::Shell::GetInstance()->AddPreTargetHandler(this); |
+ registrar_.Add( |
+ this, |
+ chrome::NOTIFICATION_BROWSER_CLOSING, |
+ content::NotificationService::AllSources()); |
} |
TabScrubber::~TabScrubber() { |
+ // Note: The weak_ptr_factory_ should invalidate its weak pointers before |
+ // any other members are destroyed. |
+ weak_ptr_factory_.InvalidateWeakPtrs(); |
} |
void TabScrubber::OnScrollEvent(ui::ScrollEvent* event) { |
- if (event->type() == ui::ET_SCROLL_FLING_CANCEL) { |
- if (scrubbing_) |
- StopScrubbing(); |
+ if (event->type() == ui::ET_SCROLL_FLING_CANCEL || |
+ event->type() == ui::ET_SCROLL_FLING_START) { |
+ FinishScrub(true); |
+ CancelImmersiveReveal(); |
return; |
} |
- if (event->finger_count() != 3 || |
- event->type() != ui::ET_SCROLL) |
+ if (event->finger_count() != 3) |
return; |
Browser* browser = GetActiveBrowser(); |
- if (!browser || (browser_ && browser != browser_)) { |
- if (scrubbing_) |
- StopScrubbing(); |
+ if (!browser || (scrubbing_ && browser_ && browser != browser_) || |
+ (highlighted_tab_ != -1 && |
+ highlighted_tab_ >= browser->tab_strip_model()->count())) { |
+ FinishScrub(false); |
return; |
} |
@@ -73,49 +100,142 @@ void TabScrubber::OnScrollEvent(ui::ScrollEvent* event) { |
browser->window()->GetNativeWindow()); |
TabStrip* tab_strip = browser_view->tabstrip(); |
- float x_offset = -event->x_offset(); |
+ if (tab_strip->IsAnimating()) { |
+ FinishScrub(false); |
+ return; |
+ } |
+ |
+ // We are handling the event. |
+ event->StopPropagation(); |
+ |
+ float x_offset = event->x_offset(); |
+ if (!ui::IsNaturalScrollEnabled()) |
+ x_offset = -x_offset; |
+ int last_tab_index = highlighted_tab_ == -1 ? |
+ browser->tab_strip_model()->active_index() : highlighted_tab_; |
if (!scrubbing_) { |
- scrubbing_ = true; |
+ swipe_direction_ = (x_offset < 0) ? LEFT : RIGHT; |
+ const gfx::Point start_point = |
+ GetStartPoint(tab_strip, |
+ browser->tab_strip_model()->active_index(), |
+ swipe_direction_); |
browser_ = browser; |
- Tab* initial_tab = |
- tab_strip->tab_at(browser_->tab_strip_model()->active_index()); |
- scroll_x_ = initial_tab->x(); |
- scroll_x_ += (x_offset < 0) ? |
- kInitialTabOffset : initial_tab->width() - kInitialTabOffset; |
- scroll_y_ = initial_tab->height() / 2; |
- registrar_.Add( |
- this, |
- chrome::NOTIFICATION_BROWSER_CLOSING, |
- content::Source<Browser>(browser_)); |
+ scrubbing_ = true; |
+ |
+ swipe_x_ = start_point.x(); |
+ swipe_y_ = start_point.y(); |
+ ImmersiveModeController* immersive_controller = |
+ browser_view->immersive_mode_controller(); |
+ CancelImmersiveReveal(); |
+ if (immersive_controller->enabled() && |
+ !immersive_controller->IsRevealed()) { |
+ immersive_controller->MaybeStartReveal(); |
+ should_cancel_immersive_reveal_ = true; |
+ } |
+ tab_strip->AddObserver(this); |
+ } else if (highlighted_tab_ == -1) { |
+ Direction direction = (x_offset < 0) ? LEFT : RIGHT; |
+ if (direction != swipe_direction_) { |
+ const gfx::Point start_point = |
+ GetStartPoint(tab_strip, |
+ browser->tab_strip_model()->active_index(), |
+ direction); |
+ swipe_x_ = start_point.x(); |
+ swipe_y_ = start_point.y(); |
+ swipe_direction_ = direction; |
+ } |
} |
- if (ui::IsNaturalScrollEnabled()) |
- scroll_x_ += event->x_offset(); |
- else |
- scroll_x_ -= event->x_offset(); |
+ swipe_x_ += x_offset; |
Tab* first_tab = tab_strip->tab_at(0); |
+ int first_tab_center = first_tab->bounds().CenterPoint().x(); |
Tab* last_tab = tab_strip->tab_at(tab_strip->tab_count() - 1); |
- if (scroll_x_ < first_tab->x()) |
- scroll_x_ = first_tab->x(); |
- if (scroll_x_ > last_tab->bounds().right()) |
- scroll_x_ = last_tab->bounds().right(); |
- |
- gfx::Point tab_point(scroll_x_, scroll_y_); |
- Tab* new_tab = GetTabAt(tab_strip, tab_point); |
- if (new_tab && !new_tab->IsActive()) { |
- int new_index = tab_strip->GetModelIndexOfTab(new_tab); |
- browser->tab_strip_model()->ActivateTabAt(new_index, true); |
- } |
+ int last_tab_tab_center = last_tab->bounds().CenterPoint().x(); |
+ if (swipe_x_ < first_tab_center) |
+ swipe_x_ = first_tab_center; |
+ if (swipe_x_ > last_tab_tab_center) |
+ swipe_x_ = last_tab_tab_center; |
- event->StopPropagation(); |
+ Tab* initial_tab = tab_strip->tab_at(last_tab_index); |
+ gfx::Point tab_point(swipe_x_, swipe_y_); |
+ views::View::ConvertPointToTarget(tab_strip, initial_tab, &tab_point); |
+ Tab* new_tab = tab_strip->GetTabAt(initial_tab, tab_point); |
+ if (!new_tab) |
+ return; |
+ |
+ int new_index = tab_strip->GetModelIndexOfTab(new_tab); |
+ if (new_index != highlighted_tab_) { |
+ if (activate_timer_.IsRunning()) { |
+ activate_timer_.Reset(); |
+ } else { |
+ activate_timer_.Start(FROM_HERE, |
+ activation_delay_, |
+ base::Bind(&TabScrubber::FinishScrub, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ true)); |
+ } |
+ if (highlighted_tab_ != -1) { |
+ Tab* tab = tab_strip->tab_at(highlighted_tab_); |
+ tab->hover_controller()->HideImmediately(); |
+ } |
+ if (new_index == browser->tab_strip_model()->active_index()) { |
+ highlighted_tab_ = -1; |
+ } else { |
+ highlighted_tab_ = new_index; |
+ new_tab->hover_controller()->Show(views::GlowHoverController::PRONOUNCED); |
+ } |
+ } |
+ if (highlighted_tab_ != -1) { |
+ gfx::Point hover_point(swipe_x_, swipe_y_); |
+ views::View::ConvertPointToTarget(tab_strip, new_tab, &hover_point); |
+ new_tab->hover_controller()->SetLocation(hover_point); |
+ } |
} |
void TabScrubber::Observe(int type, |
const content::NotificationSource& source, |
const content::NotificationDetails& details) { |
- DCHECK(type == chrome::NOTIFICATION_BROWSER_CLOSING && |
- content::Source<Browser>(source).ptr() == browser_); |
- StopScrubbing(); |
+ if (content::Source<Browser>(source).ptr() == browser_) |
+ FinishScrub(false); |
+ browser_ = NULL; |
+} |
+ |
+void TabScrubber::TabStripAddedTabAt(TabStrip* tab_strip, int index) { |
+ if (highlighted_tab_ == -1) |
+ return; |
+ |
+ if (index < highlighted_tab_) |
+ ++highlighted_tab_; |
+} |
+ |
+void TabScrubber::TabStripMovedTab(TabStrip* tab_strip, |
+ int from_index, |
+ int to_index) { |
+ if (highlighted_tab_ == -1) |
+ return; |
+ |
+ if (from_index == highlighted_tab_) |
+ highlighted_tab_ = to_index; |
+ else if (from_index < highlighted_tab_&& highlighted_tab_<= to_index) |
+ --highlighted_tab_; |
+ else if (from_index > highlighted_tab_ && highlighted_tab_ >= to_index) |
+ ++highlighted_tab_; |
+} |
+ |
+void TabScrubber::TabStripRemovedTabAt(TabStrip* tab_strip, int index) { |
+ if (highlighted_tab_ == -1) |
+ return; |
+ if (index == highlighted_tab_) { |
+ FinishScrub(false); |
+ return; |
+ } |
+ if (index < highlighted_tab_) |
+ --highlighted_tab_; |
+} |
+ |
+void TabScrubber::TabStripDeleted(TabStrip* tab_strip) { |
+ if (highlighted_tab_ == -1) |
+ return; |
} |
Browser* TabScrubber::GetActiveBrowser() { |
@@ -130,14 +250,42 @@ Browser* TabScrubber::GetActiveBrowser() { |
return browser; |
} |
-void TabScrubber::StopScrubbing() { |
- if (!scrubbing_) |
- return; |
+void TabScrubber::FinishScrub(bool activate) { |
+ activate_timer_.Stop(); |
- registrar_.Remove( |
- this, |
- chrome::NOTIFICATION_BROWSER_CLOSING, |
- content::Source<Browser>(browser_)); |
+ if (browser_) { |
+ BrowserView* browser_view = |
+ BrowserView::GetBrowserViewForNativeWindow( |
+ browser_->window()->GetNativeWindow()); |
+ TabStrip* tab_strip = browser_view->tabstrip(); |
+ if (activate && highlighted_tab_ != -1) { |
+ Tab* tab = tab_strip->tab_at(highlighted_tab_); |
+ tab->hover_controller()->HideImmediately(); |
+ browser_->tab_strip_model()->ActivateTabAt(highlighted_tab_, true); |
+ } |
+ tab_strip->RemoveObserver(this); |
+ if (!cancel_immersive_reveal_timer_.IsRunning() && |
+ should_cancel_immersive_reveal_) { |
+ cancel_immersive_reveal_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kCancelImmersiveRevelDelayMS), |
+ base:: Bind(&TabScrubber::CancelImmersiveReveal, |
+ weak_ptr_factory_.GetWeakPtr())); |
+ } |
+ } |
+ swipe_x_ = -1; |
+ swipe_y_ = -1; |
scrubbing_ = false; |
- browser_ = NULL; |
+ highlighted_tab_ = -1; |
+} |
+ |
+void TabScrubber::CancelImmersiveReveal() { |
+ cancel_immersive_reveal_timer_.Stop(); |
+ if (browser_ && should_cancel_immersive_reveal_) { |
+ BrowserView* browser_view = |
+ BrowserView::GetBrowserViewForNativeWindow( |
+ browser_->window()->GetNativeWindow()); |
+ browser_view->immersive_mode_controller()->CancelReveal(); |
+ } |
+ should_cancel_immersive_reveal_ = false; |
} |