Chromium Code Reviews| 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..14a113604aaf56f925617948b94d597a33ed22d9 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,83 +34,158 @@ 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()); |
| +} |
| + |
| 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() { |
| } |
| 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 || (browser_ && browser != browser_) || |
| + (highlighted_tab_ != -1 && |
| + highlighted_tab_ >= browser->tab_strip_model()->count())) { |
| + FinishScrub(false); |
| return; |
| } |
| + event->StopPropagation(); |
| + float x_offset = event->x_offset(); |
| BrowserView* browser_view = |
| BrowserView::GetBrowserViewForNativeWindow( |
| browser->window()->GetNativeWindow()); |
| TabStrip* tab_strip = browser_view->tabstrip(); |
| - 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; |
| + } |
| + } 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(), |
| + swipe_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; |
| + |
| + 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)); |
| + } |
| } |
| - event->StopPropagation(); |
| + if (highlighted_tab_ != new_index) { |
|
sky
2013/01/28 19:25:29
Merge with if on 152 (since they're the same).
DaveMoore
2013/01/29 01:11:41
Done.
|
| + 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; |
| } |
| Browser* TabScrubber::GetActiveBrowser() { |
| @@ -129,15 +199,42 @@ Browser* TabScrubber::GetActiveBrowser() { |
| return browser; |
| } |
| - |
| -void TabScrubber::StopScrubbing() { |
| - if (!scrubbing_) |
| - return; |
| - |
| - registrar_.Remove( |
| - this, |
| - chrome::NOTIFICATION_BROWSER_CLOSING, |
| - content::Source<Browser>(browser_)); |
| +void TabScrubber::FinishScrub(bool activate) { |
| + if (activate_timer_.IsRunning()) |
|
sky
2013/01/28 19:25:29
Its safe to always invoke Stop.
DaveMoore
2013/01/29 01:11:41
Done.
|
| + activate_timer_.Stop(); |
| + |
| + if (browser_) { |
| + BrowserView* browser_view = |
| + BrowserView::GetBrowserViewForNativeWindow( |
| + browser_->window()->GetNativeWindow()); |
| + if (activate && highlighted_tab_ != -1) { |
| + Tab* tab = browser_view->tabstrip()->tab_at(highlighted_tab_); |
| + tab->hover_controller()->HideImmediately(); |
| + browser_->tab_strip_model()->ActivateTabAt(highlighted_tab_, true); |
| + } |
| + 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() { |
| + if (cancel_immersive_reveal_timer_.IsRunning()) |
| + 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; |
| } |