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.
5 #include "chrome/browser/ui/views/app_list/win/activation_tracker_win.h"
7 #include "base/time/time.h"
11 static const wchar_t kTrayClassName[] = L"Shell_TrayWnd";
15 ActivationTrackerWin::ActivationTrackerWin(
16 app_list::AppListView* view,
17 const base::Closure& on_should_dismiss)
19 on_should_dismiss_(on_should_dismiss),
20 regain_next_lost_focus_(false),
21 preserving_focus_for_taskbar_menu_(false) {
22 view_->AddObserver(this);
25 ActivationTrackerWin::~ActivationTrackerWin() {
26 view_->RemoveObserver(this);
30 void ActivationTrackerWin::OnActivationChanged(
31 views::Widget* widget, bool active) {
32 const int kFocusCheckIntervalMS = 250;
38 preserving_focus_for_taskbar_menu_ = false;
39 timer_.Start(FROM_HERE,
40 base::TimeDelta::FromMilliseconds(kFocusCheckIntervalMS), this,
41 &ActivationTrackerWin::CheckTaskbarOrViewHasFocus);
44 void ActivationTrackerWin::OnViewHidden() {
48 void ActivationTrackerWin::CheckTaskbarOrViewHasFocus() {
49 // Remember if the taskbar had focus without the right mouse button being
51 bool was_preserving_focus = preserving_focus_for_taskbar_menu_;
52 preserving_focus_for_taskbar_menu_ = false;
54 // First get the taskbar and jump lists windows (the jump list is the
55 // context menu which the taskbar uses).
56 HWND jump_list_hwnd = FindWindow(L"DV2ControlHost", NULL);
57 HWND taskbar_hwnd = FindWindow(kTrayClassName, NULL);
59 // This code is designed to hide the app launcher when it loses focus,
60 // except for the cases necessary to allow the launcher to be pinned or
61 // closed via the taskbar context menu.
62 // First work out if the left or right button is currently down.
63 int swapped = GetSystemMetrics(SM_SWAPBUTTON);
64 int left_button = swapped ? VK_RBUTTON : VK_LBUTTON;
65 bool left_button_down = GetAsyncKeyState(left_button) < 0;
66 int right_button = swapped ? VK_LBUTTON : VK_RBUTTON;
67 bool right_button_down = GetAsyncKeyState(right_button) < 0;
69 // Now get the window that currently has focus.
70 HWND focused_hwnd = GetForegroundWindow();
72 // Sometimes the focused window is NULL. This can happen when the focus is
73 // changing due to a mouse button press. If the button is still being
74 // pressed the launcher should not be hidden.
75 if (right_button_down || left_button_down)
78 // If the focused window is NULL, and the mouse button is not being
79 // pressed, then the launcher no longer has focus.
80 on_should_dismiss_.Run();
84 while (focused_hwnd) {
85 // If the focused window is the right click menu (called a jump list) or
86 // the app list, don't hide the launcher.
87 if (focused_hwnd == jump_list_hwnd ||
88 focused_hwnd == view_->GetHWND()) {
92 if (focused_hwnd == taskbar_hwnd) {
93 // If the focused window is the taskbar, and the right button is down,
94 // don't hide the launcher as the user might be bringing up the menu.
95 if (right_button_down)
98 // There is a short period between the right mouse button being down
99 // and the menu gaining focus, where the taskbar has focus and no button
100 // is down. If the taskbar is observed in this state once the launcher
101 // is not dismissed. If it happens twice in a row it is dismissed.
102 if (!was_preserving_focus) {
103 preserving_focus_for_taskbar_menu_ = true;
109 focused_hwnd = GetParent(focused_hwnd);
112 if (regain_next_lost_focus_) {
113 regain_next_lost_focus_ = false;
114 view_->GetWidget()->Activate();
118 // If we get here, the focused window is not the taskbar, it's context menu,
120 on_should_dismiss_.Run();