app_list_linux.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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 "base/command_line.h" 8#include "base/location.h" 9#include "base/single_thread_task_runner.h" 10#include "base/thread_task_runner_handle.h" 11#include "chrome/browser/profiles/profile.h" 12#include "chrome/browser/ui/app_list/app_list_positioner.h" 13#include "ui/app_list/app_list_switches.h" 14#include "ui/app_list/views/app_list_view.h" 15#include "ui/gfx/screen.h" 16#include "ui/views/linux_ui/linux_ui.h" 17#include "ui/views/widget/widget.h" 18 19AppListLinux::AppListLinux(app_list::AppListView* view, 20 const base::Closure& on_should_dismiss) 21 : view_(view), 22 window_icon_updated_(false), 23 on_should_dismiss_(on_should_dismiss) { 24 view_->AddObserver(this); 25} 26 27AppListLinux::~AppListLinux() { 28 view_->RemoveObserver(this); 29} 30 31// static 32AppListPositioner::ScreenEdge AppListLinux::ShelfLocationInDisplay( 33 const gfx::Display& display) { 34 // On Linux, it is difficult to find the shelf (due to the large variety of 35 // desktop environments). The shelf can usually be found on the edge where the 36 // display edge and work area do not match up, but there can be more than one 37 // such edge. The shelf is assumed to be on the side of the screen with the 38 // largest delta between the display edge and the work area edge. Ties are 39 // broken in the order: top, left, right, bottom. 40 const gfx::Rect work_area = display.work_area(); 41 const gfx::Rect display_bounds = display.bounds(); 42 43 int winning_margin = 0; 44 AppListPositioner::ScreenEdge winning_edge = 45 AppListPositioner::SCREEN_EDGE_UNKNOWN; 46 47 if (work_area.y() - display_bounds.y() > winning_margin) { 48 winning_margin = work_area.y() - display_bounds.y(); 49 winning_edge = AppListPositioner::SCREEN_EDGE_TOP; 50 } 51 52 if (work_area.x() - display_bounds.x() > winning_margin) { 53 winning_margin = work_area.x() - display_bounds.x(); 54 winning_edge = AppListPositioner::SCREEN_EDGE_LEFT; 55 } 56 57 if (display_bounds.right() - work_area.right() > winning_margin) { 58 winning_margin = display_bounds.right() - work_area.right(); 59 winning_edge = AppListPositioner::SCREEN_EDGE_RIGHT; 60 } 61 62 if (display_bounds.bottom() - work_area.bottom() > winning_margin) { 63 winning_margin = display_bounds.bottom() - work_area.bottom(); 64 winning_edge = AppListPositioner::SCREEN_EDGE_BOTTOM; 65 } 66 67 return winning_edge; 68} 69 70// static 71gfx::Point AppListLinux::FindAnchorPoint(const gfx::Size& view_size, 72 const gfx::Display& display, 73 const gfx::Point& cursor, 74 AppListPositioner::ScreenEdge edge) { 75 AppListPositioner positioner(display, view_size, 0); 76 77 // The experimental app list is placed in the center of the screen. 78 if (app_list::switches::IsExperimentalAppListPositionEnabled()) 79 return positioner.GetAnchorPointForScreenCenter(); 80 81 gfx::Point anchor; 82 // Snap to the shelf edge. If the cursor is greater than the window 83 // width/height away, anchor to the corner. Otherwise, anchor to the cursor 84 // position. 85 if (edge == AppListPositioner::SCREEN_EDGE_UNKNOWN) { 86 // If we can't find the shelf, snap to the top left. 87 return positioner.GetAnchorPointForScreenCorner( 88 AppListPositioner::SCREEN_CORNER_TOP_LEFT); 89 } 90 91 int snap_distance = edge == AppListPositioner::SCREEN_EDGE_BOTTOM || 92 edge == AppListPositioner::SCREEN_EDGE_TOP 93 ? view_size.height() 94 : view_size.width(); 95 if (positioner.GetCursorDistanceFromShelf(edge, cursor) > snap_distance) 96 return positioner.GetAnchorPointForShelfCorner(edge); 97 98 return positioner.GetAnchorPointForShelfCursor(edge, cursor); 99} 100 101void AppListLinux::Show() { 102 view_->GetWidget()->Show(); 103 if (!window_icon_updated_) { 104 view_->GetWidget()->GetTopLevelWidget()->UpdateWindowIcon(); 105 window_icon_updated_ = true; 106 } 107 view_->GetWidget()->Activate(); 108} 109 110void AppListLinux::Hide() { 111 view_->GetWidget()->Hide(); 112} 113 114void AppListLinux::MoveNearCursor() { 115 gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(); 116 gfx::Screen* screen = 117 gfx::Screen::GetScreenFor(view_->GetWidget()->GetNativeView()); 118 gfx::Display display = screen->GetDisplayNearestPoint(cursor); 119 120 view_->SetBubbleArrow(views::BubbleBorder::FLOAT); 121 122 // In the Unity desktop environment, special case SCREEN_EDGE_LEFT. It is 123 // always on the left side in Unity, but ShelfLocationInDisplay will not 124 // detect this if the shelf is hidden. 125 // TODO(mgiuca): Apply this special case in Gnome Shell also. The same logic 126 // applies, but we currently have no way to detect whether Gnome Shell is 127 // running. 128 views::LinuxUI* ui = views::LinuxUI::instance(); 129 AppListPositioner::ScreenEdge edge; 130 if (ui && ui->UnityIsRunning()) 131 edge = AppListPositioner::SCREEN_EDGE_LEFT; 132 else 133 edge = ShelfLocationInDisplay(display); 134 view_->SetAnchorPoint( 135 FindAnchorPoint(view_->GetPreferredSize(), display, cursor, edge)); 136} 137 138bool AppListLinux::IsVisible() { 139 return view_->GetWidget()->IsVisible(); 140} 141 142void AppListLinux::Prerender() { 143 view_->Prerender(); 144} 145 146gfx::NativeWindow AppListLinux::GetWindow() { 147 return view_->GetWidget()->GetNativeWindow(); 148} 149 150void AppListLinux::SetProfile(Profile* profile) { 151 view_->SetProfileByPath(profile->GetPath()); 152} 153 154void AppListLinux::OnActivationChanged( 155 views::Widget* /*widget*/, bool active) { 156 if (active) 157 return; 158 159 // Call |on_should_dismiss_| asynchronously. This must be done asynchronously 160 // or our caller will crash, as it expects the app list to remain alive. 161 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, on_should_dismiss_); 162} 163