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