1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// found in the LICENSE file.
4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/views/app_list/linux/app_list_linux.h"
6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/app_list/app_list_switches.h"
8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/app_list/views/app_list_view.h"
9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/screen.h"
105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "ui/views/linux_ui/linux_ui.h"
11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/widget/widget.h"
12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
13a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// static
145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuAppListPositioner::ScreenEdge AppListLinux::ShelfLocationInDisplay(
155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    const gfx::Display& display) {
165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // On Linux, it is difficult to find the shelf (due to the large variety of
175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // desktop environments). The shelf can usually be found on the edge where the
185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // display edge and work area do not match up, but there can be more than one
195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // such edge. The shelf is assumed to be on the side of the screen with the
205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // largest delta between the display edge and the work area edge. Ties are
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // broken in the order: top, left, right, bottom.
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  const gfx::Rect work_area = display.work_area();
235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  const gfx::Rect display_bounds = display.bounds();
245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  int winning_margin = 0;
265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  AppListPositioner::ScreenEdge winning_edge =
275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      AppListPositioner::SCREEN_EDGE_UNKNOWN;
285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (work_area.y() - display_bounds.y() > winning_margin) {
305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    winning_margin = work_area.y() - display_bounds.y();
315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    winning_edge = AppListPositioner::SCREEN_EDGE_TOP;
325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (work_area.x() - display_bounds.x() > winning_margin) {
355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    winning_margin = work_area.x() - display_bounds.x();
365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    winning_edge = AppListPositioner::SCREEN_EDGE_LEFT;
375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (display_bounds.right() - work_area.right() > winning_margin) {
405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    winning_margin = display_bounds.right() - work_area.right();
415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    winning_edge = AppListPositioner::SCREEN_EDGE_RIGHT;
425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (display_bounds.bottom() - work_area.bottom() > winning_margin) {
455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    winning_margin = display_bounds.bottom() - work_area.bottom();
465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    winning_edge = AppListPositioner::SCREEN_EDGE_BOTTOM;
475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return winning_edge;
505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// static
53a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)gfx::Point AppListLinux::FindAnchorPoint(const gfx::Size& view_size,
54a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                         const gfx::Display& display,
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                         const gfx::Point& cursor,
56010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                         AppListPositioner::ScreenEdge edge,
57010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                         bool center_window) {
58a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  AppListPositioner positioner(display, view_size, 0);
59a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
60010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // Special case for app list in the center of the screen.
61010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (center_window)
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return positioner.GetAnchorPointForScreenCenter();
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  gfx::Point anchor;
65a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Snap to the shelf edge. If the cursor is greater than the window
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // width/height away, anchor to the corner. Otherwise, anchor to the cursor
67a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // position.
68a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (edge == AppListPositioner::SCREEN_EDGE_UNKNOWN) {
69a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // If we can't find the shelf, snap to the top left.
70a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return positioner.GetAnchorPointForScreenCorner(
71a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        AppListPositioner::SCREEN_CORNER_TOP_LEFT);
72a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  int snap_distance = edge == AppListPositioner::SCREEN_EDGE_BOTTOM ||
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                              edge == AppListPositioner::SCREEN_EDGE_TOP
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                          ? view_size.height()
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                          : view_size.width();
78a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (positioner.GetCursorDistanceFromShelf(edge, cursor) > snap_distance)
79a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return positioner.GetAnchorPointForShelfCorner(edge);
80a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
81a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return positioner.GetAnchorPointForShelfCursor(edge, cursor);
82a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
83a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
84010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// static
85010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void AppListLinux::MoveNearCursor(app_list::AppListView* view) {
86a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
87a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  gfx::Screen* screen =
88010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      gfx::Screen::GetScreenFor(view->GetWidget()->GetNativeView());
89a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  gfx::Display display = screen->GetDisplayNearestPoint(cursor);
90a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
91010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  view->SetBubbleArrow(views::BubbleBorder::FLOAT);
925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  AppListPositioner::ScreenEdge edge;
94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // In the Unity desktop environment, special case SCREEN_EDGE_LEFT. It is
965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // always on the left side in Unity, but ShelfLocationInDisplay will not
975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // detect this if the shelf is hidden.
985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // TODO(mgiuca): Apply this special case in Gnome Shell also. The same logic
995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // applies, but we currently have no way to detect whether Gnome Shell is
1005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // running.
1015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  views::LinuxUI* ui = views::LinuxUI::instance();
1025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (ui && ui->UnityIsRunning())
1035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    edge = AppListPositioner::SCREEN_EDGE_LEFT;
1045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  else
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#endif
1065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    edge = ShelfLocationInDisplay(display);
107010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  view->SetAnchorPoint(FindAnchorPoint(view->GetPreferredSize(),
108010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                       display,
109010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                       cursor,
110010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                       edge,
111010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                       view->ShouldCenterWindow()));
112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
113