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