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 "chrome/browser/ui/views/app_list/win/activation_tracker_win.h" 6 7 #include "base/time/time.h" 8 #include "ui/app_list/views/app_list_view.h" 9 #include "ui/views/widget/widget.h" 10 11 namespace { 12 13 const wchar_t kJumpListClassName[] = L"DV2ControlHost"; 14 const wchar_t kTrayClassName[] = L"Shell_TrayWnd"; 15 const int kFocusCheckIntervalMS = 250; 16 17 } // namespace 18 19 ActivationTrackerWin::ActivationTrackerWin( 20 app_list::AppListView* view, 21 const base::Closure& on_should_dismiss) 22 : view_(view), 23 on_should_dismiss_(on_should_dismiss), 24 reactivate_on_next_focus_loss_(false), 25 taskbar_has_focus_(false) { 26 view_->AddObserver(this); 27 } 28 29 ActivationTrackerWin::~ActivationTrackerWin() { 30 view_->RemoveObserver(this); 31 timer_.Stop(); 32 } 33 34 void ActivationTrackerWin::OnActivationChanged(views::Widget* /*widget*/, 35 bool active) { 36 if (active) { 37 timer_.Stop(); 38 return; 39 } 40 41 taskbar_has_focus_ = false; 42 timer_.Start(FROM_HERE, 43 base::TimeDelta::FromMilliseconds(kFocusCheckIntervalMS), this, 44 &ActivationTrackerWin::MaybeDismissAppList); 45 } 46 47 void ActivationTrackerWin::OnViewHidden() { 48 timer_.Stop(); 49 } 50 51 void ActivationTrackerWin::MaybeDismissAppList() { 52 if (!ShouldDismissAppList()) 53 return; 54 55 if (reactivate_on_next_focus_loss_) { 56 // Instead of dismissing the app launcher, re-activate it. 57 reactivate_on_next_focus_loss_ = false; 58 view_->GetWidget()->Activate(); 59 return; 60 } 61 62 on_should_dismiss_.Run(); 63 } 64 65 bool ActivationTrackerWin::ShouldDismissAppList() { 66 // The app launcher should be hidden when it loses focus, except for the cases 67 // necessary to allow the launcher to be pinned or closed via the taskbar 68 // context menu. This will return true to dismiss the app launcher unless one 69 // of the following conditions are met: 70 // - the app launcher is focused, or 71 // - the taskbar's jump list is focused, or 72 // - the taskbar is focused with the right mouse button pressed. 73 74 // Remember if the taskbar had focus without the right mouse button being 75 // down. 76 bool taskbar_had_focus = taskbar_has_focus_; 77 taskbar_has_focus_ = false; 78 79 // First get the taskbar and jump lists windows (the jump list is the 80 // context menu which the taskbar uses). 81 HWND jump_list_hwnd = FindWindow(kJumpListClassName, NULL); 82 HWND taskbar_hwnd = FindWindow(kTrayClassName, NULL); 83 84 // First work out if the left or right button is currently down. 85 int swapped = GetSystemMetrics(SM_SWAPBUTTON); 86 int left_button = swapped ? VK_RBUTTON : VK_LBUTTON; 87 bool left_button_down = GetAsyncKeyState(left_button) < 0; 88 int right_button = swapped ? VK_LBUTTON : VK_RBUTTON; 89 bool right_button_down = GetAsyncKeyState(right_button) < 0; 90 91 // Now get the window that currently has focus. 92 HWND focused_hwnd = GetForegroundWindow(); 93 if (!focused_hwnd) { 94 // Sometimes the focused window is NULL. This can happen when the focus is 95 // changing due to a mouse button press. Dismiss the launcher if and only if 96 // no button is being pressed. 97 return !right_button_down && !left_button_down; 98 } 99 100 while (focused_hwnd) { 101 // If the focused window is the right click menu (called a jump list) or 102 // the app list, don't hide the launcher. 103 if (focused_hwnd == jump_list_hwnd || focused_hwnd == view_->GetHWND()) 104 return false; 105 106 if (focused_hwnd == taskbar_hwnd) { 107 // If the focused window is the taskbar, and the right button is down, 108 // don't hide the launcher as the user might be bringing up the menu. 109 if (right_button_down) 110 return false; 111 112 // There is a short period between the right mouse button being down 113 // and the menu gaining focus, where the taskbar has focus and no button 114 // is down. If the taskbar is observed in this state one time, the 115 // launcher is not dismissed. If it happens for two consecutive timer 116 // ticks, it is dismissed. 117 if (!taskbar_had_focus) { 118 taskbar_has_focus_ = true; 119 return false; 120 } 121 return true; 122 } 123 focused_hwnd = GetParent(focused_hwnd); 124 } 125 126 // If we get here, the focused window is not the taskbar, its context menu, or 127 // the app list. 128 return true; 129 } 130