Home | History | Annotate | Download | only in win
      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