Home | History | Annotate | Download | only in ash
      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.
      4 
      5 #include "chrome/browser/ui/views/ash/tab_scrubber.h"
      6 
      7 #include "ash/shell.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"
     25 
     26 namespace {
     27 const int64 kActivationDelayMS = 200;
     28 }
     29 
     30 // static
     31 TabScrubber* TabScrubber::GetInstance() {
     32   static TabScrubber* instance = NULL;
     33   if (!instance)
     34     instance = new TabScrubber();
     35   return instance;
     36 }
     37 
     38 // static
     39 gfx::Point TabScrubber::GetStartPoint(
     40     TabStrip* tab_strip,
     41     int index,
     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());
     49 }
     50 
     51 bool TabScrubber::IsActivationPending() {
     52   return activate_timer_.IsRunning();
     53 }
     54 
     55 TabScrubber::TabScrubber()
     56     : scrubbing_(false),
     57       browser_(NULL),
     58       swipe_x_(-1),
     59       swipe_y_(-1),
     60       swipe_direction_(LEFT),
     61       highlighted_tab_(-1),
     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);
     67   registrar_.Add(
     68       this,
     69       chrome::NOTIFICATION_BROWSER_CLOSED,
     70       content::NotificationService::AllSources());
     71 }
     72 
     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();
     77 }
     78 
     79 void TabScrubber::OnScrollEvent(ui::ScrollEvent* event) {
     80   if (event->type() == ui::ET_SCROLL_FLING_CANCEL ||
     81       event->type() == ui::ET_SCROLL_FLING_START) {
     82     FinishScrub(true);
     83     immersive_reveal_lock_.reset();
     84     return;
     85   }
     86 
     87   if (event->finger_count() != 3)
     88     return;
     89 
     90   Browser* browser = GetActiveBrowser();
     91   if (!browser || (scrubbing_ && browser_ && browser != browser_) ||
     92       (highlighted_tab_ != -1 &&
     93           highlighted_tab_ >= browser->tab_strip_model()->count())) {
     94     FinishScrub(false);
     95     return;
     96   }
     97 
     98   BrowserView* browser_view =
     99       BrowserView::GetBrowserViewForNativeWindow(
    100           browser->window()->GetNativeWindow());
    101   TabStrip* tab_strip = browser_view->tabstrip();
    102 
    103   if (tab_strip->IsAnimating()) {
    104     FinishScrub(false);
    105     return;
    106   }
    107 
    108   // We are handling the event.
    109   event->StopPropagation();
    110 
    111   float x_offset = event->x_offset();
    112   int last_tab_index = highlighted_tab_ == -1 ?
    113       browser->tab_strip_model()->active_index() : highlighted_tab_;
    114   if (!scrubbing_) {
    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(),
    119                       swipe_direction_);
    120     browser_ = browser;
    121     scrubbing_ = true;
    122 
    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));
    130     }
    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(),
    138                         direction);
    139       swipe_x_ = start_point.x();
    140       swipe_y_ = start_point.y();
    141       swipe_direction_ = direction;
    142     }
    143   }
    144 
    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;
    154 
    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);
    159   if (!new_tab)
    160     return;
    161 
    162   int new_index = tab_strip->GetModelIndexOfTab(new_tab);
    163   if (highlighted_tab_ == -1 &&
    164       new_index == browser->tab_strip_model()->active_index())
    165     return;
    166 
    167   if (new_index != highlighted_tab_) {
    168     if (activate_timer_.IsRunning()) {
    169       activate_timer_.Reset();
    170     } else {
    171       int delay = use_default_activation_delay_ ?
    172           ui::GestureConfiguration::tab_scrub_activation_delay_in_ms() :
    173           activation_delay_;
    174       if (delay >= 0) {
    175         activate_timer_.Start(FROM_HERE,
    176                               base::TimeDelta::FromMilliseconds(delay),
    177                               base::Bind(&TabScrubber::FinishScrub,
    178                                          weak_ptr_factory_.GetWeakPtr(),
    179                                          true));
    180       }
    181     }
    182     if (highlighted_tab_ != -1) {
    183       Tab* tab = tab_strip->tab_at(highlighted_tab_);
    184       tab->hover_controller()->HideImmediately();
    185     }
    186     if (new_index == browser->tab_strip_model()->active_index()) {
    187       highlighted_tab_ = -1;
    188     } else {
    189       highlighted_tab_ = new_index;
    190       new_tab->hover_controller()->Show(views::GlowHoverController::PRONOUNCED);
    191     }
    192   }
    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);
    197   }
    198 }
    199 
    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();
    205     swipe_x_ = -1;
    206     swipe_y_ = -1;
    207     scrubbing_ = false;
    208     highlighted_tab_ = -1;
    209     browser_ = NULL;
    210   }
    211 }
    212 
    213 void TabScrubber::TabStripAddedTabAt(TabStrip* tab_strip, int index) {
    214   if (highlighted_tab_ == -1)
    215     return;
    216 
    217   if (index < highlighted_tab_)
    218     ++highlighted_tab_;
    219 }
    220 
    221 void TabScrubber::TabStripMovedTab(TabStrip* tab_strip,
    222                                    int from_index,
    223                                    int to_index) {
    224   if (highlighted_tab_ == -1)
    225     return;
    226 
    227   if (from_index == highlighted_tab_)
    228     highlighted_tab_ = to_index;
    229   else if (from_index < highlighted_tab_&& highlighted_tab_<= to_index)
    230     --highlighted_tab_;
    231   else if (from_index > highlighted_tab_ && highlighted_tab_ >= to_index)
    232     ++highlighted_tab_;
    233 }
    234 
    235 void TabScrubber::TabStripRemovedTabAt(TabStrip* tab_strip, int index) {
    236   if (highlighted_tab_ == -1)
    237     return;
    238   if (index == highlighted_tab_) {
    239     FinishScrub(false);
    240     return;
    241   }
    242   if (index < highlighted_tab_)
    243     --highlighted_tab_;
    244 }
    245 
    246 void TabScrubber::TabStripDeleted(TabStrip* tab_strip) {
    247   if (highlighted_tab_ == -1)
    248     return;
    249 }
    250 
    251 Browser* TabScrubber::GetActiveBrowser() {
    252   aura::Window* active_window = ash::wm::GetActiveWindow();
    253   if (!active_window)
    254     return NULL;
    255 
    256   Browser* browser = chrome::FindBrowserWithWindow(active_window);
    257   if (!browser || browser->type() != Browser::TYPE_TABBED)
    258     return NULL;
    259 
    260   return browser;
    261 }
    262 
    263 void TabScrubber::FinishScrub(bool activate) {
    264   activate_timer_.Stop();
    265 
    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();
    274       int distance =
    275           std::abs(
    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);
    279     }
    280     tab_strip->RemoveObserver(this);
    281   }
    282   swipe_x_ = -1;
    283   swipe_y_ = -1;
    284   scrubbing_ = false;
    285   highlighted_tab_ = -1;
    286 }
    287