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