14e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 24e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 34e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// found in the LICENSE file. 44e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 54e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/ui/views/app_list/win/activation_tracker_win.h" 64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/time/time.h" 8010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "chrome/browser/ui/app_list/app_list_shower_views.h" 9010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "chrome/browser/ui/views/app_list/win/app_list_service_win.h" 10a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "ui/app_list/views/app_list_view.h" 11a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "ui/views/widget/widget.h" 124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace { 144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const wchar_t kJumpListClassName[] = L"DV2ControlHost"; 16a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const wchar_t kTrayClassName[] = L"Shell_TrayWnd"; 17a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const int kFocusCheckIntervalMS = 250; 184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} // namespace 204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 21010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)ActivationTrackerWin::ActivationTrackerWin(AppListServiceWin* service) 22010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) : service_(service), 23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) taskbar_has_focus_(false) { 24010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) service_->shower().app_list()->AddObserver(this); 254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)ActivationTrackerWin::~ActivationTrackerWin() { 28010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) DCHECK(service_->shower().app_list()); 29010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) service_->shower().app_list()->RemoveObserver(this); 304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) timer_.Stop(); 314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 33a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void ActivationTrackerWin::OnActivationChanged(views::Widget* /*widget*/, 34a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) bool active) { 354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (active) { 364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) timer_.Stop(); 374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return; 384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) taskbar_has_focus_ = false; 414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) timer_.Start(FROM_HERE, 424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) base::TimeDelta::FromMilliseconds(kFocusCheckIntervalMS), this, 43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) &ActivationTrackerWin::MaybeDismissAppList); 444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void ActivationTrackerWin::OnViewHidden() { 474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) timer_.Stop(); 484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ActivationTrackerWin::MaybeDismissAppList() { 51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!ShouldDismissAppList()) 52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return; 53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 54010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) service_->DismissAppList(); 55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool ActivationTrackerWin::ShouldDismissAppList() { 58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // The app launcher should be hidden when it loses focus, except for the cases 59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // necessary to allow the launcher to be pinned or closed via the taskbar 60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // context menu. This will return true to dismiss the app launcher unless one 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // of the following conditions are met: 62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // - the app launcher is focused, or 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // - the taskbar's jump list is focused, or 64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // - the taskbar is focused with the right mouse button pressed. 65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Remember if the taskbar had focus without the right mouse button being 674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // down. 68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) bool taskbar_had_focus = taskbar_has_focus_; 69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) taskbar_has_focus_ = false; 704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // First get the taskbar and jump lists windows (the jump list is the 724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // context menu which the taskbar uses). 73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) HWND jump_list_hwnd = FindWindow(kJumpListClassName, NULL); 744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) HWND taskbar_hwnd = FindWindow(kTrayClassName, NULL); 754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // First work out if the left or right button is currently down. 774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) int swapped = GetSystemMetrics(SM_SWAPBUTTON); 784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) int left_button = swapped ? VK_RBUTTON : VK_LBUTTON; 794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) bool left_button_down = GetAsyncKeyState(left_button) < 0; 804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) int right_button = swapped ? VK_LBUTTON : VK_RBUTTON; 814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) bool right_button_down = GetAsyncKeyState(right_button) < 0; 824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Now get the window that currently has focus. 844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) HWND focused_hwnd = GetForegroundWindow(); 854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (!focused_hwnd) { 864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Sometimes the focused window is NULL. This can happen when the focus is 87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // changing due to a mouse button press. Dismiss the launcher if and only if 88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // no button is being pressed. 89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return !right_button_down && !left_button_down; 904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) while (focused_hwnd) { 934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // If the focused window is the right click menu (called a jump list) or 944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // the app list, don't hide the launcher. 95010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) HWND app_list_hwnd = service_->shower().app_list()->GetHWND(); 96010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if (focused_hwnd == jump_list_hwnd || focused_hwnd == app_list_hwnd) 97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (focused_hwnd == taskbar_hwnd) { 1004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // If the focused window is the taskbar, and the right button is down, 1014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // don't hide the launcher as the user might be bringing up the menu. 1024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (right_button_down) 103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 1044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // There is a short period between the right mouse button being down 1064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // and the menu gaining focus, where the taskbar has focus and no button 107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // is down. If the taskbar is observed in this state one time, the 108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // launcher is not dismissed. If it happens for two consecutive timer 109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // ticks, it is dismissed. 110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!taskbar_had_focus) { 111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) taskbar_has_focus_ = true; 112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 1134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return true; 1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 1164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) focused_hwnd = GetParent(focused_hwnd); 1174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 1184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // If we get here, the focused window is not the taskbar, its context menu, or 120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // the app list. 121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return true; 1224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 123