Home | History | Annotate | Download | only in overview
      1 // Copyright 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 "ash/wm/overview/window_selector.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "ash/ash_switches.h"
     10 #include "ash/root_window_controller.h"
     11 #include "ash/shell.h"
     12 #include "ash/wm/mru_window_tracker.h"
     13 #include "ash/wm/overview/window_overview.h"
     14 #include "ash/wm/overview/window_selector_delegate.h"
     15 #include "ash/wm/overview/window_selector_panels.h"
     16 #include "ash/wm/overview/window_selector_window.h"
     17 #include "ash/wm/window_state.h"
     18 #include "base/auto_reset.h"
     19 #include "base/command_line.h"
     20 #include "base/metrics/histogram.h"
     21 #include "base/strings/string_number_conversions.h"
     22 #include "base/timer/timer.h"
     23 #include "ui/aura/client/activation_client.h"
     24 #include "ui/aura/client/focus_client.h"
     25 #include "ui/aura/root_window.h"
     26 #include "ui/aura/window.h"
     27 #include "ui/aura/window_observer.h"
     28 #include "ui/events/event.h"
     29 #include "ui/events/event_handler.h"
     30 
     31 namespace ash {
     32 
     33 namespace {
     34 
     35 // The time from when the user pressed alt+tab while still holding alt before
     36 // overview is engaged.
     37 const int kOverviewDelayOnCycleMilliseconds = 10000;
     38 
     39 // If the delay before overview is less than or equal to this threshold the
     40 // initial monitor is used for multi-display overview, otherwise the monitor
     41 // of the currently selected window is used.
     42 const int kOverviewDelayInitialMonitorThreshold = 100;
     43 
     44 // The maximum amount of time allowed for the delay before overview on cycling.
     45 // If the specified time exceeds this the timer will not be started.
     46 const int kMaxOverviewDelayOnCycleMilliseconds = 10000;
     47 
     48 int GetOverviewDelayOnCycleMilliseconds() {
     49   static int value = -1;
     50   if (value == -1) {
     51     value = kOverviewDelayOnCycleMilliseconds;
     52     if (CommandLine::ForCurrentProcess()->HasSwitch(
     53           switches::kAshOverviewDelayOnAltTab)) {
     54       if (!base::StringToInt(CommandLine::ForCurrentProcess()->
     55             GetSwitchValueASCII(switches::kAshOverviewDelayOnAltTab), &value)) {
     56         LOG(ERROR) << "Expected int value for "
     57                    << switches::kAshOverviewDelayOnAltTab;
     58       }
     59     }
     60   }
     61   return value;
     62 }
     63 
     64 // A comparator for locating a given selectable window.
     65 struct WindowSelectorItemComparator
     66     : public std::unary_function<WindowSelectorItem*, bool> {
     67   explicit WindowSelectorItemComparator(const aura::Window* window)
     68       : window_(window) {
     69   }
     70 
     71   bool operator()(WindowSelectorItem* window) const {
     72     return window->HasSelectableWindow(window_);
     73   }
     74 
     75   const aura::Window* window_;
     76 };
     77 
     78 // A comparator for locating a selectable window given a targeted window.
     79 struct WindowSelectorItemTargetComparator
     80     : public std::unary_function<WindowSelectorItem*, bool> {
     81   explicit WindowSelectorItemTargetComparator(const aura::Window* target_window)
     82       : target(target_window) {
     83   }
     84 
     85   bool operator()(WindowSelectorItem* window) const {
     86     return window->TargetedWindow(target) != NULL;
     87   }
     88 
     89   const aura::Window* target;
     90 };
     91 
     92 // A comparator for locating a selector item for a given root.
     93 struct WindowSelectorItemForRoot
     94     : public std::unary_function<WindowSelectorItem*, bool> {
     95   explicit WindowSelectorItemForRoot(const aura::Window* root)
     96       : root_window(root) {
     97   }
     98 
     99   bool operator()(WindowSelectorItem* item) const {
    100     return item->GetRootWindow() == root_window;
    101   }
    102 
    103   const aura::Window* root_window;
    104 };
    105 
    106 // Filter to watch for the termination of a keyboard gesture to cycle through
    107 // multiple windows.
    108 class WindowSelectorEventFilter : public ui::EventHandler {
    109  public:
    110   WindowSelectorEventFilter(WindowSelector* selector);
    111   virtual ~WindowSelectorEventFilter();
    112 
    113   // Overridden from ui::EventHandler:
    114   virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
    115 
    116  private:
    117   // A weak pointer to the WindowSelector which owns this instance.
    118   WindowSelector* selector_;
    119 
    120   DISALLOW_COPY_AND_ASSIGN(WindowSelectorEventFilter);
    121 };
    122 
    123 // Watch for all keyboard events by filtering the root window.
    124 WindowSelectorEventFilter::WindowSelectorEventFilter(WindowSelector* selector)
    125     : selector_(selector) {
    126   Shell::GetInstance()->AddPreTargetHandler(this);
    127 }
    128 
    129 WindowSelectorEventFilter::~WindowSelectorEventFilter() {
    130   Shell::GetInstance()->RemovePreTargetHandler(this);
    131 }
    132 
    133 void WindowSelectorEventFilter::OnKeyEvent(ui::KeyEvent* event) {
    134   // Views uses VKEY_MENU for both left and right Alt keys.
    135   if (event->key_code() == ui::VKEY_MENU &&
    136       event->type() == ui::ET_KEY_RELEASED) {
    137     selector_->SelectWindow();
    138     // Warning: |this| will be deleted from here on.
    139   }
    140 }
    141 
    142 // Triggers a shelf visibility update on all root window controllers.
    143 void UpdateShelfVisibility() {
    144   Shell::RootWindowControllerList root_window_controllers =
    145       Shell::GetInstance()->GetAllRootWindowControllers();
    146   for (Shell::RootWindowControllerList::iterator iter =
    147           root_window_controllers.begin();
    148        iter != root_window_controllers.end(); ++iter) {
    149     (*iter)->UpdateShelfVisibility();
    150   }
    151 }
    152 
    153 // Returns the window immediately below |window| in the current container.
    154 aura::Window* GetWindowBelow(aura::Window* window) {
    155   aura::Window* parent = window->parent();
    156   if (!parent)
    157     return NULL;
    158   aura::Window* below = NULL;
    159   for (aura::Window::Windows::const_iterator iter = parent->children().begin();
    160        iter != parent->children().end(); ++iter) {
    161     if (*iter == window)
    162       return below;
    163     below = *iter;
    164   }
    165   NOTREACHED();
    166   return NULL;
    167 }
    168 
    169 }  // namespace
    170 
    171 // This class restores and moves a window to the front of the stacking order for
    172 // the duration of the class's scope.
    173 class ScopedShowWindow : public aura::WindowObserver {
    174  public:
    175   ScopedShowWindow();
    176   virtual ~ScopedShowWindow();
    177 
    178   // Show |window| at the top of the stacking order.
    179   void Show(aura::Window* window);
    180 
    181   // Cancel restoring the window on going out of scope.
    182   void CancelRestore();
    183 
    184   aura::Window* window() { return window_; }
    185 
    186   // aura::WindowObserver:
    187   virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
    188 
    189  private:
    190   // The window being shown.
    191   aura::Window* window_;
    192 
    193   // The window immediately below where window_ belongs.
    194   aura::Window* stack_window_above_;
    195 
    196   // If true, minimize window_ on going out of scope.
    197   bool minimized_;
    198 
    199   DISALLOW_COPY_AND_ASSIGN(ScopedShowWindow);
    200 };
    201 
    202 ScopedShowWindow::ScopedShowWindow()
    203     : window_(NULL),
    204       stack_window_above_(NULL),
    205       minimized_(false) {
    206 }
    207 
    208 void ScopedShowWindow::Show(aura::Window* window) {
    209   DCHECK(!window_);
    210   window_ = window;
    211   stack_window_above_ = GetWindowBelow(window);
    212   minimized_ = wm::GetWindowState(window)->IsMinimized();
    213   window_->parent()->AddObserver(this);
    214   window_->Show();
    215   wm::GetWindowState(window_)->Activate();
    216 }
    217 
    218 ScopedShowWindow::~ScopedShowWindow() {
    219   if (window_) {
    220     window_->parent()->RemoveObserver(this);
    221 
    222     // Restore window's stacking position.
    223     if (stack_window_above_)
    224       window_->parent()->StackChildAbove(window_, stack_window_above_);
    225     else
    226       window_->parent()->StackChildAtBottom(window_);
    227 
    228     // Restore minimized state.
    229     if (minimized_)
    230       wm::GetWindowState(window_)->Minimize();
    231   }
    232 }
    233 
    234 void ScopedShowWindow::CancelRestore() {
    235   if (!window_)
    236     return;
    237   window_->parent()->RemoveObserver(this);
    238   window_ = stack_window_above_ = NULL;
    239 }
    240 
    241 void ScopedShowWindow::OnWillRemoveWindow(aura::Window* window) {
    242   if (window == window_) {
    243     CancelRestore();
    244   } else if (window == stack_window_above_) {
    245     // If the window this window was above is removed, use the next window down
    246     // as the restore marker.
    247     stack_window_above_ = GetWindowBelow(stack_window_above_);
    248   }
    249 }
    250 
    251 WindowSelector::WindowSelector(const WindowList& windows,
    252                                WindowSelector::Mode mode,
    253                                WindowSelectorDelegate* delegate)
    254     : mode_(mode),
    255       timer_enabled_(GetOverviewDelayOnCycleMilliseconds() <
    256                          kMaxOverviewDelayOnCycleMilliseconds),
    257       start_overview_timer_(FROM_HERE,
    258           base::TimeDelta::FromMilliseconds(
    259               GetOverviewDelayOnCycleMilliseconds()),
    260           this, &WindowSelector::StartOverview),
    261       delegate_(delegate),
    262       selected_window_(0),
    263       restore_focus_window_(aura::client::GetFocusClient(
    264           Shell::GetPrimaryRootWindow())->GetFocusedWindow()),
    265       ignore_activations_(false) {
    266   DCHECK(delegate_);
    267 
    268   if (restore_focus_window_)
    269     restore_focus_window_->AddObserver(this);
    270 
    271   std::vector<WindowSelectorPanels*> panels_items;
    272   for (size_t i = 0; i < windows.size(); ++i) {
    273     WindowSelectorItem* item = NULL;
    274     if (windows[i] != restore_focus_window_)
    275       windows[i]->AddObserver(this);
    276     observed_windows_.insert(windows[i]);
    277 
    278     if (windows[i]->type() == aura::client::WINDOW_TYPE_PANEL &&
    279         wm::GetWindowState(windows[i])->panel_attached()) {
    280       // Attached panel windows are grouped into a single overview item per
    281       // root window (display).
    282       std::vector<WindowSelectorPanels*>::iterator iter =
    283           std::find_if(panels_items.begin(), panels_items.end(),
    284                        WindowSelectorItemForRoot(windows[i]->GetRootWindow()));
    285       WindowSelectorPanels* panels_item = NULL;
    286       if (iter == panels_items.end()) {
    287         panels_item = new WindowSelectorPanels();
    288         panels_items.push_back(panels_item);
    289         windows_.push_back(panels_item);
    290       } else {
    291         panels_item = *iter;
    292       }
    293       panels_item->AddWindow(windows[i]);
    294       item = panels_item;
    295     } else {
    296       item = new WindowSelectorWindow(windows[i]);
    297       windows_.push_back(item);
    298     }
    299     // Verify that the window has been added to an item in overview.
    300     CHECK(item->TargetedWindow(windows[i]));
    301   }
    302   UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.Items", windows_.size());
    303 
    304   // Observe window activations and switchable containers on all root windows
    305   // for newly created windows during overview.
    306   Shell::GetInstance()->activation_client()->AddObserver(this);
    307   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
    308   for (aura::Window::Windows::const_iterator iter = root_windows.begin();
    309        iter != root_windows.end(); ++iter) {
    310     for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) {
    311       aura::Window* container = Shell::GetContainer(*iter,
    312           kSwitchableWindowContainerIds[i]);
    313       container->AddObserver(this);
    314       observed_windows_.insert(container);
    315     }
    316   }
    317 
    318   if (mode == WindowSelector::CYCLE) {
    319     cycle_start_time_ = base::Time::Now();
    320     event_handler_.reset(new WindowSelectorEventFilter(this));
    321     if (timer_enabled_)
    322       start_overview_timer_.Reset();
    323   } else {
    324     StartOverview();
    325   }
    326 }
    327 
    328 WindowSelector::~WindowSelector() {
    329   ResetFocusRestoreWindow(true);
    330   for (std::set<aura::Window*>::iterator iter = observed_windows_.begin();
    331        iter != observed_windows_.end(); ++iter) {
    332     (*iter)->RemoveObserver(this);
    333   }
    334   Shell::GetInstance()->activation_client()->RemoveObserver(this);
    335   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
    336   window_overview_.reset();
    337   // Clearing the window list resets the ignored_by_shelf flag on the windows.
    338   windows_.clear();
    339   UpdateShelfVisibility();
    340 
    341   if (!cycle_start_time_.is_null()) {
    342     UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.CycleTime",
    343         base::Time::Now() - cycle_start_time_);
    344   }
    345 }
    346 
    347 void WindowSelector::Step(WindowSelector::Direction direction) {
    348   DCHECK(!windows_.empty());
    349   // Upgrade to CYCLE mode if currently in OVERVIEW mode.
    350   if (mode_ != CYCLE) {
    351     event_handler_.reset(new WindowSelectorEventFilter(this));
    352     DCHECK(window_overview_);
    353     // Set the initial selection window to animate to the new selection.
    354     window_overview_->SetSelection(selected_window_);
    355     window_overview_->MoveToSingleRootWindow(
    356         windows_[selected_window_]->GetRootWindow());
    357     mode_ = CYCLE;
    358   }
    359 
    360   selected_window_ = (selected_window_ + windows_.size() +
    361       (direction == WindowSelector::FORWARD ? 1 : -1)) % windows_.size();
    362   if (window_overview_) {
    363     window_overview_->SetSelection(selected_window_);
    364   } else {
    365     base::AutoReset<bool> restoring_focus(&ignore_activations_, true);
    366     showing_window_.reset(new ScopedShowWindow);
    367     showing_window_->Show(windows_[selected_window_]->SelectionWindow());
    368     if (timer_enabled_)
    369       start_overview_timer_.Reset();
    370   }
    371 }
    372 
    373 void WindowSelector::SelectWindow() {
    374   SelectWindow(windows_[selected_window_]->SelectionWindow());
    375 }
    376 
    377 void WindowSelector::SelectWindow(aura::Window* window) {
    378   ResetFocusRestoreWindow(false);
    379   if (showing_window_ && showing_window_->window() == window)
    380     showing_window_->CancelRestore();
    381   ScopedVector<WindowSelectorItem>::iterator iter =
    382       std::find_if(windows_.begin(), windows_.end(),
    383                    WindowSelectorItemTargetComparator(window));
    384   DCHECK(iter != windows_.end());
    385   // The selected window should not be minimized when window selection is
    386   // ended.
    387   (*iter)->RestoreWindowOnExit(window);
    388   delegate_->OnWindowSelected(window);
    389 }
    390 
    391 void WindowSelector::CancelSelection() {
    392   delegate_->OnSelectionCanceled();
    393 }
    394 
    395 void WindowSelector::OnWindowAdded(aura::Window* new_window) {
    396   if (new_window->type() != aura::client::WINDOW_TYPE_NORMAL &&
    397       new_window->type() != aura::client::WINDOW_TYPE_PANEL) {
    398     return;
    399   }
    400 
    401   for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) {
    402     if (new_window->parent()->id() == kSwitchableWindowContainerIds[i] &&
    403         !new_window->transient_parent()) {
    404       // The new window is in one of the switchable containers, abort overview.
    405       CancelSelection();
    406       return;
    407     }
    408   }
    409 }
    410 
    411 void WindowSelector::OnWindowDestroying(aura::Window* window) {
    412   // window is one of a container, the restore_focus_window and/or
    413   // one of the selectable windows in overview.
    414   ScopedVector<WindowSelectorItem>::iterator iter =
    415       std::find_if(windows_.begin(), windows_.end(),
    416                    WindowSelectorItemComparator(window));
    417   window->RemoveObserver(this);
    418   observed_windows_.erase(window);
    419   if (window == restore_focus_window_)
    420     restore_focus_window_ = NULL;
    421   if (iter == windows_.end())
    422     return;
    423 
    424   (*iter)->RemoveWindow(window);
    425   // If there are still windows in this selector entry then the overview is
    426   // still active and the active selection remains the same.
    427   if (!(*iter)->empty())
    428     return;
    429 
    430   size_t deleted_index = iter - windows_.begin();
    431   windows_.erase(iter);
    432   if (windows_.empty()) {
    433     CancelSelection();
    434     return;
    435   }
    436   if (window_overview_)
    437     window_overview_->OnWindowsChanged();
    438   if (mode_ == CYCLE && selected_window_ >= deleted_index) {
    439     if (selected_window_ > deleted_index)
    440       selected_window_--;
    441     selected_window_ = selected_window_ % windows_.size();
    442     if (window_overview_)
    443       window_overview_->SetSelection(selected_window_);
    444   }
    445 }
    446 
    447 void WindowSelector::OnWindowBoundsChanged(aura::Window* window,
    448                                            const gfx::Rect& old_bounds,
    449                                            const gfx::Rect& new_bounds) {
    450   if (!window_overview_)
    451     return;
    452 
    453   ScopedVector<WindowSelectorItem>::iterator iter =
    454       std::find_if(windows_.begin(), windows_.end(),
    455                    WindowSelectorItemTargetComparator(window));
    456   DCHECK(window == restore_focus_window_ || iter != windows_.end());
    457   if (iter == windows_.end())
    458     return;
    459 
    460   // Immediately finish any active bounds animation.
    461   window->layer()->GetAnimator()->StopAnimatingProperty(
    462       ui::LayerAnimationElement::BOUNDS);
    463 
    464   // Recompute the transform for the window.
    465   (*iter)->RecomputeWindowTransforms();
    466 }
    467 
    468 void WindowSelector::OnWindowActivated(aura::Window* gained_active,
    469                                        aura::Window* lost_active) {
    470   if (ignore_activations_ || !gained_active)
    471     return;
    472   // Don't restore focus on exit if a window was just activated.
    473   ResetFocusRestoreWindow(false);
    474   CancelSelection();
    475 }
    476 
    477 void WindowSelector::OnAttemptToReactivateWindow(aura::Window* request_active,
    478                                                  aura::Window* actual_active) {
    479   if (ignore_activations_)
    480     return;
    481   // Don't restore focus on exit if a window was just activated.
    482   ResetFocusRestoreWindow(false);
    483   CancelSelection();
    484 }
    485 
    486 void WindowSelector::StartOverview() {
    487   DCHECK(!window_overview_);
    488   // Remove focus from active window before entering overview.
    489   aura::client::GetFocusClient(
    490       Shell::GetPrimaryRootWindow())->FocusWindow(NULL);
    491 
    492   aura::Window* overview_root = NULL;
    493   if (mode_ == CYCLE) {
    494     overview_root = GetOverviewDelayOnCycleMilliseconds() <=
    495                         kOverviewDelayInitialMonitorThreshold ?
    496                     windows_.front()->GetRootWindow() :
    497                     windows_[selected_window_]->GetRootWindow();
    498   }
    499   window_overview_.reset(new WindowOverview(this, &windows_, overview_root));
    500   if (mode_ == CYCLE)
    501     window_overview_->SetSelection(selected_window_);
    502   UpdateShelfVisibility();
    503 }
    504 
    505 void WindowSelector::ResetFocusRestoreWindow(bool focus) {
    506   if (!restore_focus_window_)
    507     return;
    508   if (focus) {
    509     base::AutoReset<bool> restoring_focus(&ignore_activations_, true);
    510     restore_focus_window_->Focus();
    511   }
    512   // If the window is in the observed_windows_ list it needs to continue to be
    513   // observed.
    514   if (observed_windows_.find(restore_focus_window_) ==
    515           observed_windows_.end()) {
    516     restore_focus_window_->RemoveObserver(this);
    517   }
    518   restore_focus_window_ = NULL;
    519 }
    520 
    521 }  // namespace ash
    522