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/app_list/app_list_positioner.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "ui/gfx/point.h"
11#include "ui/gfx/rect.h"
12
13AppListPositioner::AppListPositioner(const gfx::Display& display,
14                                     const gfx::Size& window_size,
15                                     int min_distance_from_edge)
16    : display_(display),
17      window_size_(window_size),
18      min_distance_from_edge_(min_distance_from_edge) {}
19
20void AppListPositioner::WorkAreaSubtract(const gfx::Rect& rect) {
21  gfx::Rect work_area = display_.work_area();
22  work_area.Subtract(rect);
23  display_.set_work_area(work_area);
24}
25
26void AppListPositioner::WorkAreaInset(int left,
27                                      int top,
28                                      int right,
29                                      int bottom) {
30  gfx::Rect work_area = display_.work_area();
31  work_area.Inset(left, top, right, bottom);
32  display_.set_work_area(work_area);
33}
34
35gfx::Point AppListPositioner::GetAnchorPointForScreenCenter() const {
36  return display_.bounds().CenterPoint();
37}
38
39gfx::Point AppListPositioner::GetAnchorPointForScreenCorner(
40    ScreenCorner corner) const {
41  const gfx::Rect& screen_rect = display_.bounds();
42  gfx::Point anchor;
43  switch (corner) {
44    case SCREEN_CORNER_TOP_LEFT:
45      anchor = screen_rect.origin();
46      break;
47    case SCREEN_CORNER_TOP_RIGHT:
48      anchor = screen_rect.top_right();
49      break;
50    case SCREEN_CORNER_BOTTOM_LEFT:
51      anchor = screen_rect.bottom_left();
52      break;
53    case SCREEN_CORNER_BOTTOM_RIGHT:
54      anchor = screen_rect.bottom_right();
55      break;
56    default:
57      NOTREACHED();
58      anchor = gfx::Point();
59  }
60  return ClampAnchorPoint(anchor);
61}
62
63gfx::Point AppListPositioner::GetAnchorPointForShelfCorner(
64    ScreenEdge shelf_edge) const {
65  const gfx::Rect& screen_rect = display_.bounds();
66  const gfx::Rect& work_area = display_.work_area();
67  gfx::Point anchor;
68  switch (shelf_edge) {
69    case SCREEN_EDGE_LEFT:
70      anchor = gfx::Point(work_area.x(), screen_rect.y());
71      break;
72    case SCREEN_EDGE_RIGHT:
73      anchor = gfx::Point(work_area.right(), screen_rect.y());
74      break;
75    case SCREEN_EDGE_TOP:
76      anchor = gfx::Point(screen_rect.x(), work_area.y());
77      break;
78    case SCREEN_EDGE_BOTTOM:
79      anchor = gfx::Point(screen_rect.x(), work_area.bottom());
80      break;
81    default:
82      NOTREACHED();
83      anchor = gfx::Point();
84  }
85  return ClampAnchorPoint(anchor);
86}
87
88gfx::Point AppListPositioner::GetAnchorPointForShelfCenter(
89    ScreenEdge shelf_edge) const {
90  const gfx::Rect& work_area = display_.work_area();
91  gfx::Point anchor;
92  switch (shelf_edge) {
93    case SCREEN_EDGE_LEFT:
94      anchor =
95          gfx::Point(work_area.x(), work_area.y() + work_area.height() / 2);
96      break;
97    case SCREEN_EDGE_RIGHT:
98      anchor =
99          gfx::Point(work_area.right(), work_area.y() + work_area.height() / 2);
100      break;
101    case SCREEN_EDGE_TOP:
102      anchor = gfx::Point(work_area.x() + work_area.width() / 2, work_area.y());
103      break;
104    case SCREEN_EDGE_BOTTOM:
105      anchor =
106          gfx::Point(work_area.x() + work_area.width() / 2, work_area.bottom());
107      break;
108    default:
109      NOTREACHED();
110      anchor = gfx::Point();
111  }
112  return ClampAnchorPoint(anchor);
113}
114
115gfx::Point AppListPositioner::GetAnchorPointForShelfCursor(
116    ScreenEdge shelf_edge,
117    const gfx::Point& cursor) const {
118  const gfx::Rect& work_area = display_.work_area();
119  gfx::Point anchor;
120  switch (shelf_edge) {
121    case SCREEN_EDGE_LEFT:
122      anchor = gfx::Point(work_area.x(), cursor.y());
123      break;
124    case SCREEN_EDGE_RIGHT:
125      anchor = gfx::Point(work_area.right(), cursor.y());
126      break;
127    case SCREEN_EDGE_TOP:
128      anchor = gfx::Point(cursor.x(), work_area.y());
129      break;
130    case SCREEN_EDGE_BOTTOM:
131      anchor = gfx::Point(cursor.x(), work_area.bottom());
132      break;
133    default:
134      NOTREACHED();
135      anchor = gfx::Point();
136  }
137  return ClampAnchorPoint(anchor);
138}
139
140AppListPositioner::ScreenEdge AppListPositioner::GetShelfEdge(
141    const gfx::Rect& shelf_rect) const {
142  const gfx::Rect& screen_rect = display_.bounds();
143  const gfx::Rect& work_area = display_.work_area();
144
145  // If we can't find the shelf, return SCREEN_EDGE_UNKNOWN. If the display
146  // size is the same as the work area, and does not contain the shelf, either
147  // the shelf is hidden or on another monitor.
148  if (work_area == screen_rect && !work_area.Contains(shelf_rect))
149    return SCREEN_EDGE_UNKNOWN;
150
151  // Note: On Windows 8 the work area won't include split windows on the left or
152  // right, and neither will |shelf_rect|.
153  if (shelf_rect.x() == work_area.x() &&
154      shelf_rect.width() == work_area.width()) {
155    // Shelf is horizontal.
156    if (shelf_rect.bottom() == screen_rect.bottom())
157      return SCREEN_EDGE_BOTTOM;
158    else if (shelf_rect.y() == screen_rect.y())
159      return SCREEN_EDGE_TOP;
160  } else if (shelf_rect.y() == work_area.y() &&
161             shelf_rect.height() == work_area.height()) {
162    // Shelf is vertical.
163    if (shelf_rect.x() == screen_rect.x())
164      return SCREEN_EDGE_LEFT;
165    else if (shelf_rect.right() == screen_rect.right())
166      return SCREEN_EDGE_RIGHT;
167  }
168
169  return SCREEN_EDGE_UNKNOWN;
170}
171
172int AppListPositioner::GetCursorDistanceFromShelf(
173    ScreenEdge shelf_edge,
174    const gfx::Point& cursor) const {
175  const gfx::Rect& work_area = display_.work_area();
176  switch (shelf_edge) {
177    case SCREEN_EDGE_UNKNOWN:
178      return 0;
179    case SCREEN_EDGE_LEFT:
180      return std::max(0, cursor.x() - work_area.x());
181    case SCREEN_EDGE_RIGHT:
182      return std::max(0, work_area.right() - cursor.x());
183    case SCREEN_EDGE_TOP:
184      return std::max(0, cursor.y() - work_area.y());
185    case SCREEN_EDGE_BOTTOM:
186      return std::max(0, work_area.bottom() - cursor.y());
187    default:
188      NOTREACHED();
189      return 0;
190  }
191}
192
193gfx::Point AppListPositioner::ClampAnchorPoint(gfx::Point anchor) const {
194  gfx::Rect bounds_rect(display_.work_area());
195
196  // Anchor the center of the window in a region that prevents the window
197  // showing outside of the work area.
198  bounds_rect.Inset(window_size_.width() / 2 + min_distance_from_edge_,
199                    window_size_.height() / 2 + min_distance_from_edge_);
200
201  anchor.SetToMax(bounds_rect.origin());
202  anchor.SetToMin(bounds_rect.bottom_right());
203  return anchor;
204}
205