blob: b1d5a52c85fe8c41da296360c09110d4f2568242 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/app_list/win/activation_tracker_win.h"
#include "base/time/time.h"
namespace {
static const wchar_t kTrayClassName[] = L"Shell_TrayWnd";
} // namespace
ActivationTrackerWin::ActivationTrackerWin(
app_list::AppListView* view,
const base::Closure& on_should_dismiss)
: view_(view),
on_should_dismiss_(on_should_dismiss),
regain_next_lost_focus_(false),
preserving_focus_for_taskbar_menu_(false) {
view_->AddObserver(this);
}
ActivationTrackerWin::~ActivationTrackerWin() {
view_->RemoveObserver(this);
timer_.Stop();
}
void ActivationTrackerWin::OnActivationChanged(
views::Widget* widget, bool active) {
const int kFocusCheckIntervalMS = 250;
if (active) {
timer_.Stop();
return;
}
preserving_focus_for_taskbar_menu_ = false;
timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(kFocusCheckIntervalMS), this,
&ActivationTrackerWin::CheckTaskbarOrViewHasFocus);
}
void ActivationTrackerWin::OnViewHidden() {
timer_.Stop();
}
void ActivationTrackerWin::CheckTaskbarOrViewHasFocus() {
// Remember if the taskbar had focus without the right mouse button being
// down.
bool was_preserving_focus = preserving_focus_for_taskbar_menu_;
preserving_focus_for_taskbar_menu_ = false;
// First get the taskbar and jump lists windows (the jump list is the
// context menu which the taskbar uses).
HWND jump_list_hwnd = FindWindow(L"DV2ControlHost", NULL);
HWND taskbar_hwnd = FindWindow(kTrayClassName, NULL);
// This code is designed to hide the app launcher when it loses focus,
// except for the cases necessary to allow the launcher to be pinned or
// closed via the taskbar context menu.
// First work out if the left or right button is currently down.
int swapped = GetSystemMetrics(SM_SWAPBUTTON);
int left_button = swapped ? VK_RBUTTON : VK_LBUTTON;
bool left_button_down = GetAsyncKeyState(left_button) < 0;
int right_button = swapped ? VK_LBUTTON : VK_RBUTTON;
bool right_button_down = GetAsyncKeyState(right_button) < 0;
// Now get the window that currently has focus.
HWND focused_hwnd = GetForegroundWindow();
if (!focused_hwnd) {
// Sometimes the focused window is NULL. This can happen when the focus is
// changing due to a mouse button press. If the button is still being
// pressed the launcher should not be hidden.
if (right_button_down || left_button_down)
return;
// If the focused window is NULL, and the mouse button is not being
// pressed, then the launcher no longer has focus.
on_should_dismiss_.Run();
return;
}
while (focused_hwnd) {
// If the focused window is the right click menu (called a jump list) or
// the app list, don't hide the launcher.
if (focused_hwnd == jump_list_hwnd ||
focused_hwnd == view_->GetHWND()) {
return;
}
if (focused_hwnd == taskbar_hwnd) {
// If the focused window is the taskbar, and the right button is down,
// don't hide the launcher as the user might be bringing up the menu.
if (right_button_down)
return;
// There is a short period between the right mouse button being down
// and the menu gaining focus, where the taskbar has focus and no button
// is down. If the taskbar is observed in this state once the launcher
// is not dismissed. If it happens twice in a row it is dismissed.
if (!was_preserving_focus) {
preserving_focus_for_taskbar_menu_ = true;
return;
}
break;
}
focused_hwnd = GetParent(focused_hwnd);
}
if (regain_next_lost_focus_) {
regain_next_lost_focus_ = false;
view_->GetWidget()->Activate();
return;
}
// If we get here, the focused window is not the taskbar, it's context menu,
// or the app list.
on_should_dismiss_.Run();
}