app_list_win.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/app_list_win.h"
6
7#include "chrome/browser/profiles/profile.h"
8#include "ui/app_list/views/app_list_view.h"
9#include "ui/gfx/screen.h"
10#include "ui/views/widget/widget.h"
11
12namespace {
13
14static const wchar_t kTrayClassName[] = L"Shell_TrayWnd";
15
16// Utility methods for showing the app list.
17// Attempts to find the bounds of the Windows taskbar. Returns true on success.
18// |rect| is in screen coordinates. If the taskbar is in autohide mode and is
19// not visible, |rect| will be outside the current monitor's bounds, except for
20// one pixel of overlap where the edge of the taskbar is shown.
21bool GetTaskbarRect(gfx::Rect* rect) {
22  HWND taskbar_hwnd = FindWindow(kTrayClassName, NULL);
23  if (!taskbar_hwnd)
24    return false;
25
26  RECT win_rect;
27  if (!GetWindowRect(taskbar_hwnd, &win_rect))
28    return false;
29
30  *rect = gfx::Rect(win_rect);
31  return true;
32}
33
34gfx::Point FindReferencePoint(const gfx::Display& display,
35                              const gfx::Point& cursor) {
36  const int kSnapDistance = 50;
37
38  // If we can't find the taskbar, snap to the bottom left.
39  // If the display size is the same as the work area, and does not contain the
40  // taskbar, either the taskbar is hidden or on another monitor, so just snap
41  // to the bottom left.
42  gfx::Rect taskbar_rect;
43  if (!GetTaskbarRect(&taskbar_rect) ||
44      (display.work_area() == display.bounds() &&
45          !display.work_area().Contains(taskbar_rect))) {
46    return display.work_area().bottom_left();
47  }
48
49  // Snap to the taskbar edge. If the cursor is greater than kSnapDistance away,
50  // also move to the left (for horizontal taskbars) or top (for vertical).
51  const gfx::Rect& screen_rect = display.bounds();
52  // First handle taskbar on bottom.
53  // Note on Windows 8 the work area won't include split windows on the left or
54  // right, and neither will |taskbar_rect|.
55  if (taskbar_rect.width() == display.work_area().width()) {
56    if (taskbar_rect.bottom() == screen_rect.bottom()) {
57      if (taskbar_rect.y() - cursor.y() > kSnapDistance)
58        return gfx::Point(screen_rect.x(), taskbar_rect.y());
59
60      return gfx::Point(cursor.x(), taskbar_rect.y());
61    }
62
63    // Now try on the top.
64    if (cursor.y() - taskbar_rect.bottom() > kSnapDistance)
65      return gfx::Point(screen_rect.x(), taskbar_rect.bottom());
66
67    return gfx::Point(cursor.x(), taskbar_rect.bottom());
68  }
69
70  // Now try the left.
71  if (taskbar_rect.x() == screen_rect.x()) {
72    if (cursor.x() - taskbar_rect.right() > kSnapDistance)
73      return gfx::Point(taskbar_rect.right(), screen_rect.y());
74
75    return gfx::Point(taskbar_rect.right(), cursor.y());
76  }
77
78  // Finally, try the right.
79  if (taskbar_rect.x() - cursor.x() > kSnapDistance)
80    return gfx::Point(taskbar_rect.x(), screen_rect.y());
81
82  return gfx::Point(taskbar_rect.x(), cursor.y());
83}
84
85gfx::Point FindAnchorPoint(
86    const gfx::Size view_size,
87    const gfx::Display& display,
88    const gfx::Point& cursor) {
89  const int kSnapOffset = 3;
90
91  gfx::Rect bounds_rect(display.work_area());
92  // Always subtract the taskbar area since work_area() will not subtract it
93  // if the taskbar is set to auto-hide, and the app list should never overlap
94  // the taskbar.
95  gfx::Rect taskbar_rect;
96  if (GetTaskbarRect(&taskbar_rect))
97    bounds_rect.Subtract(taskbar_rect);
98
99  bounds_rect.Inset(view_size.width() / 2 + kSnapOffset,
100                    view_size.height() / 2 + kSnapOffset);
101
102  gfx::Point anchor = FindReferencePoint(display, cursor);
103  anchor.SetToMax(bounds_rect.origin());
104  anchor.SetToMin(bounds_rect.bottom_right());
105  return anchor;
106}
107
108}  // namespace
109
110AppListWin::AppListWin(app_list::AppListView* view,
111                       const base::Closure& on_should_dismiss)
112  : view_(view),
113    activation_tracker_(view, on_should_dismiss),
114    window_icon_updated_(false) {
115}
116
117AppListWin::~AppListWin() {
118}
119
120void AppListWin::Show() {
121  view_->GetWidget()->Show();
122  if (!window_icon_updated_) {
123    view_->GetWidget()->GetTopLevelWidget()->UpdateWindowIcon();
124    window_icon_updated_ = true;
125  }
126  view_->GetWidget()->Activate();
127}
128
129void AppListWin::Hide() {
130  view_->GetWidget()->Hide();
131  activation_tracker_.OnViewHidden();
132}
133
134void AppListWin::MoveNearCursor() {
135  gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
136  gfx::Screen* screen =
137      gfx::Screen::GetScreenFor(view_->GetWidget()->GetNativeView());
138  gfx::Display display = screen->GetDisplayNearestPoint(cursor);
139
140  view_->SetBubbleArrow(views::BubbleBorder::FLOAT);
141  view_->SetAnchorPoint(FindAnchorPoint(
142      view_->GetPreferredSize(), display, cursor));
143}
144
145bool AppListWin::IsVisible() {
146  return view_->GetWidget()->IsVisible();
147}
148
149void AppListWin::Prerender() {
150  view_->Prerender();
151}
152
153void AppListWin::RegainNextLostFocus() {
154  activation_tracker_.RegainNextLostFocus();
155}
156
157gfx::NativeWindow AppListWin::GetWindow() {
158  return view_->GetWidget()->GetNativeWindow();
159}
160
161void AppListWin::SetProfile(Profile* profile) {
162  view_->SetProfileByPath(profile->GetPath());
163}
164