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   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_;
    116   if (!scrubbing_) {
    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(),
    121                       swipe_direction_);
    122     browser_ = browser;
    123     scrubbing_ = true;
    124 
    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));
    132     }
    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(),
    140                         direction);
    141       swipe_x_ = start_point.x();
    142       swipe_y_ = start_point.y();
    143       swipe_direction_ = direction;
    144     }
    145   }
    146 
    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;
    156 
    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);
    161   if (!new_tab)
    162     return;
    163 
    164   int new_index = tab_strip->GetModelIndexOfTab(new_tab);
    165   if (highlighted_tab_ == -1 &&
    166       new_index == browser->tab_strip_model()->active_index())
    167     return;
    168 
    169   if (new_index != highlighted_tab_) {
    170     if (activate_timer_.IsRunning()) {
    171       activate_timer_.Reset();
    172     } else {
    173       int delay = use_default_activation_delay_ ?
    174           ui::GestureConfiguration::tab_scrub_activation_delay_in_ms() :
    175           activation_delay_;
    176       if (delay >= 0) {
    177         activate_timer_.Start(FROM_HERE,
    178                               base::TimeDelta::FromMilliseconds(delay),
    179                               base::Bind(&TabScrubber::FinishScrub,
    180                                          weak_ptr_factory_.GetWeakPtr(),
    181                                          true));
    182       }
    183     }
    184     if (highlighted_tab_ != -1) {
    185       Tab* tab = tab_strip->tab_at(highlighted_tab_);
    186       tab->hover_controller()->HideImmediately();
    187     }
    188     if (new_index == browser->tab_strip_model()->active_index()) {
    189       highlighted_tab_ = -1;
    190     } else {
    191       highlighted_tab_ = new_index;
    192       new_tab->hover_controller()->Show(views::GlowHoverController::PRONOUNCED);
    193     }
    194   }
    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);
    199   }
    200 }
    201 
    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();
    207     swipe_x_ = -1;
    208     swipe_y_ = -1;
    209     scrubbing_ = false;
    210     highlighted_tab_ = -1;
    211     browser_ = NULL;
    212   }
    213 }
    214 
    215 void TabScrubber::TabStripAddedTabAt(TabStrip* tab_strip, int index) {
    216   if (highlighted_tab_ == -1)
    217     return;
    218 
    219   if (index < highlighted_tab_)
    220     ++highlighted_tab_;
    221 }
    222 
    223 void TabScrubber::TabStripMovedTab(TabStrip* tab_strip,
    224                                    int from_index,
    225                                    int to_index) {
    226   if (highlighted_tab_ == -1)
    227     return;
    228 
    229   if (from_index == highlighted_tab_)
    230     highlighted_tab_ = to_index;
    231   else if (from_index < highlighted_tab_&& highlighted_tab_<= to_index)
    232     --highlighted_tab_;
    233   else if (from_index > highlighted_tab_ && highlighted_tab_ >= to_index)
    234     ++highlighted_tab_;
    235 }
    236 
    237 void TabScrubber::TabStripRemovedTabAt(TabStrip* tab_strip, int index) {
    238   if (highlighted_tab_ == -1)
    239     return;
    240   if (index == highlighted_tab_) {
    241     FinishScrub(false);
    242     return;
    243   }
    244   if (index < highlighted_tab_)
    245     --highlighted_tab_;
    246 }
    247 
    248 void TabScrubber::TabStripDeleted(TabStrip* tab_strip) {
    249   if (highlighted_tab_ == -1)
    250     return;
    251 }
    252 
    253 Browser* TabScrubber::GetActiveBrowser() {
    254   aura::Window* active_window = ash::wm::GetActiveWindow();
    255   if (!active_window)
    256     return NULL;
    257 
    258   Browser* browser = chrome::FindBrowserWithWindow(active_window);
    259   if (!browser || browser->type() != Browser::TYPE_TABBED)
    260     return NULL;
    261 
    262   return browser;
    263 }
    264 
    265 void TabScrubber::FinishScrub(bool activate) {
    266   activate_timer_.Stop();
    267 
    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();
    276       int distance =
    277           std::abs(
    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);
    281     }
    282     tab_strip->RemoveObserver(this);
    283   }
    284   swipe_x_ = -1;
    285   swipe_y_ = -1;
    286   scrubbing_ = false;
    287   highlighted_tab_ = -1;
    288 }
    289