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