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/linux/app_list_linux.h"
6
7#include "ui/app_list/app_list_switches.h"
8#include "ui/app_list/views/app_list_view.h"
9#include "ui/gfx/screen.h"
10#include "ui/views/linux_ui/linux_ui.h"
11#include "ui/views/widget/widget.h"
12
13// static
14AppListPositioner::ScreenEdge AppListLinux::ShelfLocationInDisplay(
15    const gfx::Display& display) {
16  // On Linux, it is difficult to find the shelf (due to the large variety of
17  // desktop environments). The shelf can usually be found on the edge where the
18  // display edge and work area do not match up, but there can be more than one
19  // such edge. The shelf is assumed to be on the side of the screen with the
20  // largest delta between the display edge and the work area edge. Ties are
21  // broken in the order: top, left, right, bottom.
22  const gfx::Rect work_area = display.work_area();
23  const gfx::Rect display_bounds = display.bounds();
24
25  int winning_margin = 0;
26  AppListPositioner::ScreenEdge winning_edge =
27      AppListPositioner::SCREEN_EDGE_UNKNOWN;
28
29  if (work_area.y() - display_bounds.y() > winning_margin) {
30    winning_margin = work_area.y() - display_bounds.y();
31    winning_edge = AppListPositioner::SCREEN_EDGE_TOP;
32  }
33
34  if (work_area.x() - display_bounds.x() > winning_margin) {
35    winning_margin = work_area.x() - display_bounds.x();
36    winning_edge = AppListPositioner::SCREEN_EDGE_LEFT;
37  }
38
39  if (display_bounds.right() - work_area.right() > winning_margin) {
40    winning_margin = display_bounds.right() - work_area.right();
41    winning_edge = AppListPositioner::SCREEN_EDGE_RIGHT;
42  }
43
44  if (display_bounds.bottom() - work_area.bottom() > winning_margin) {
45    winning_margin = display_bounds.bottom() - work_area.bottom();
46    winning_edge = AppListPositioner::SCREEN_EDGE_BOTTOM;
47  }
48
49  return winning_edge;
50}
51
52// static
53gfx::Point AppListLinux::FindAnchorPoint(const gfx::Size& view_size,
54                                         const gfx::Display& display,
55                                         const gfx::Point& cursor,
56                                         AppListPositioner::ScreenEdge edge,
57                                         bool center_window) {
58  AppListPositioner positioner(display, view_size, 0);
59
60  // Special case for app list in the center of the screen.
61  if (center_window)
62    return positioner.GetAnchorPointForScreenCenter();
63
64  gfx::Point anchor;
65  // Snap to the shelf edge. If the cursor is greater than the window
66  // width/height away, anchor to the corner. Otherwise, anchor to the cursor
67  // position.
68  if (edge == AppListPositioner::SCREEN_EDGE_UNKNOWN) {
69    // If we can't find the shelf, snap to the top left.
70    return positioner.GetAnchorPointForScreenCorner(
71        AppListPositioner::SCREEN_CORNER_TOP_LEFT);
72  }
73
74  int snap_distance = edge == AppListPositioner::SCREEN_EDGE_BOTTOM ||
75                              edge == AppListPositioner::SCREEN_EDGE_TOP
76                          ? view_size.height()
77                          : view_size.width();
78  if (positioner.GetCursorDistanceFromShelf(edge, cursor) > snap_distance)
79    return positioner.GetAnchorPointForShelfCorner(edge);
80
81  return positioner.GetAnchorPointForShelfCursor(edge, cursor);
82}
83
84// static
85void AppListLinux::MoveNearCursor(app_list::AppListView* view) {
86  gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
87  gfx::Screen* screen =
88      gfx::Screen::GetScreenFor(view->GetWidget()->GetNativeView());
89  gfx::Display display = screen->GetDisplayNearestPoint(cursor);
90
91  view->SetBubbleArrow(views::BubbleBorder::FLOAT);
92
93  AppListPositioner::ScreenEdge edge;
94#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
95  // In the Unity desktop environment, special case SCREEN_EDGE_LEFT. It is
96  // always on the left side in Unity, but ShelfLocationInDisplay will not
97  // detect this if the shelf is hidden.
98  // TODO(mgiuca): Apply this special case in Gnome Shell also. The same logic
99  // applies, but we currently have no way to detect whether Gnome Shell is
100  // running.
101  views::LinuxUI* ui = views::LinuxUI::instance();
102  if (ui && ui->UnityIsRunning())
103    edge = AppListPositioner::SCREEN_EDGE_LEFT;
104  else
105#endif
106    edge = ShelfLocationInDisplay(display);
107  view->SetAnchorPoint(FindAnchorPoint(view->GetPreferredSize(),
108                                       display,
109                                       cursor,
110                                       edge,
111                                       view->ShouldCenterWindow()));
112}
113