OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h" | 5 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h" |
6 | 6 |
7 #include "ash/wm/window_state.h" | 7 #include <algorithm> |
| 8 |
8 #include "ash/wm/window_util.h" | 9 #include "ash/wm/window_util.h" |
9 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h" | |
10 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_v2app.h" | |
11 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" | 10 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" |
12 #include "chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h" | |
13 #include "chrome/browser/ui/ash/launcher/launcher_context_menu.h" | |
14 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h" | |
15 #include "components/favicon/content/content_favicon_driver.h" | |
16 #include "content/public/browser/web_contents.h" | |
17 #include "extensions/browser/app_window/app_window.h" | |
18 #include "extensions/browser/app_window/native_app_window.h" | |
19 #include "skia/ext/image_operations.h" | |
20 #include "ui/aura/client/aura_constants.h" | 11 #include "ui/aura/client/aura_constants.h" |
21 #include "ui/aura/window.h" | 12 #include "ui/aura/window.h" |
22 #include "ui/events/event.h" | 13 #include "ui/base/base_window.h" |
23 #include "ui/gfx/image/image_skia.h" | |
24 #include "ui/wm/core/window_animations.h" | 14 #include "ui/wm/core/window_animations.h" |
25 | 15 |
26 using extensions::AppWindow; | |
27 | |
28 namespace { | |
29 | |
30 // Size of the icon in the shelf launcher in display-independent pixels. | |
31 const int kAppListIconSize = 24; | |
32 | |
33 // This will return a slightly smaller icon than the app icon to be used in | |
34 // the application list menu. | |
35 gfx::Image GetAppListIcon(AppWindow* app_window) { | |
36 // TODO(skuhne): We instead might want to use LoadImages in | |
37 // AppWindow::UpdateExtensionAppIcon() to let the extension give us | |
38 // pre-defined icons in the launcher and the launcher list sizes. Since there | |
39 // is no mock yet, doing this now seems a bit premature and we scale for the | |
40 // time being. | |
41 if (app_window->app_icon().IsEmpty()) | |
42 return gfx::Image(); | |
43 | |
44 SkBitmap bmp = | |
45 skia::ImageOperations::Resize(*app_window->app_icon().ToSkBitmap(), | |
46 skia::ImageOperations::RESIZE_BEST, | |
47 kAppListIconSize, | |
48 kAppListIconSize); | |
49 return gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bmp)); | |
50 } | |
51 | |
52 // Functor for std::find_if used in AppLauncherItemController. | |
53 class AppWindowHasWindow { | |
54 public: | |
55 explicit AppWindowHasWindow(aura::Window* window) : window_(window) {} | |
56 | |
57 bool operator()(AppWindow* app_window) const { | |
58 return app_window->GetNativeWindow() == window_; | |
59 } | |
60 | |
61 private: | |
62 const aura::Window* window_; | |
63 }; | |
64 | |
65 } // namespace | |
66 | |
67 AppWindowLauncherItemController::AppWindowLauncherItemController( | 16 AppWindowLauncherItemController::AppWindowLauncherItemController( |
68 Type type, | 17 Type type, |
69 const std::string& app_shelf_id, | 18 const std::string& app_shelf_id, |
70 const std::string& app_id, | 19 const std::string& app_id, |
71 ChromeLauncherController* controller) | 20 ChromeLauncherController* controller) |
72 : LauncherItemController(type, app_id, controller), | 21 : LauncherItemController(type, app_id, controller), |
73 last_active_app_window_(NULL), | |
74 app_shelf_id_(app_shelf_id), | 22 app_shelf_id_(app_shelf_id), |
75 observed_windows_(this) {} | 23 observed_windows_(this) {} |
76 | 24 |
77 AppWindowLauncherItemController::~AppWindowLauncherItemController() {} | 25 AppWindowLauncherItemController::~AppWindowLauncherItemController() {} |
78 | 26 |
79 void AppWindowLauncherItemController::AddAppWindow( | 27 void AppWindowLauncherItemController::AddWindow(ui::BaseWindow* window) { |
80 AppWindow* app_window, | 28 windows_.push_front(window); |
81 ash::ShelfItemStatus status) { | 29 observed_windows_.Add(window->GetNativeWindow()); |
82 if (app_window->window_type_is_panel() && type() != TYPE_APP_PANEL) | |
83 LOG(ERROR) << "AppWindow of type Panel added to non-panel launcher item"; | |
84 app_windows_.push_front(app_window); | |
85 observed_windows_.Add(app_window->GetNativeWindow()); | |
86 } | 30 } |
87 | 31 |
88 void AppWindowLauncherItemController::RemoveAppWindowForWindow( | 32 AppWindowLauncherItemController::WindowList::iterator |
| 33 AppWindowLauncherItemController::GetFromNativeWindow(aura::Window* window) { |
| 34 return std::find_if(windows_.begin(), windows_.end(), |
| 35 [window](ui::BaseWindow* base_window) { |
| 36 return base_window->GetNativeWindow() == window; |
| 37 }); |
| 38 } |
| 39 |
| 40 void AppWindowLauncherItemController::RemoveWindowForNativeWindow( |
89 aura::Window* window) { | 41 aura::Window* window) { |
90 AppWindowList::iterator iter = std::find_if( | 42 auto iter = GetFromNativeWindow(window); |
91 app_windows_.begin(), app_windows_.end(), AppWindowHasWindow(window)); | 43 if (iter != windows_.end()) { |
92 if (iter != app_windows_.end()) { | 44 if (*iter == last_active_window_) |
93 if (*iter == last_active_app_window_) | 45 last_active_window_ = nullptr; |
94 last_active_app_window_ = NULL; | 46 OnWindowRemoved(*iter); |
95 app_windows_.erase(iter); | 47 windows_.erase(iter); |
96 } | 48 } |
97 observed_windows_.Remove(window); | 49 observed_windows_.Remove(window); |
98 } | 50 } |
99 | 51 |
100 void AppWindowLauncherItemController::SetActiveWindow(aura::Window* window) { | 52 void AppWindowLauncherItemController::SetActiveWindow(aura::Window* window) { |
101 AppWindowList::iterator iter = std::find_if( | 53 auto iter = GetFromNativeWindow(window); |
102 app_windows_.begin(), app_windows_.end(), AppWindowHasWindow(window)); | 54 if (iter != windows_.end()) |
103 if (iter != app_windows_.end()) | 55 last_active_window_ = *iter; |
104 last_active_app_window_ = *iter; | |
105 } | 56 } |
106 | 57 |
107 bool AppWindowLauncherItemController::IsOpen() const { | 58 bool AppWindowLauncherItemController::IsOpen() const { |
108 return !app_windows_.empty(); | 59 return !windows_.empty(); |
109 } | 60 } |
110 | 61 |
111 bool AppWindowLauncherItemController::IsVisible() const { | 62 bool AppWindowLauncherItemController::IsVisible() const { |
112 // Return true if any windows are visible. | 63 // Return true if any windows are visible. |
113 for (AppWindowList::const_iterator iter = app_windows_.begin(); | 64 for (const auto& window : windows_) { |
114 iter != app_windows_.end(); | 65 if (window->GetNativeWindow()->IsVisible()) |
115 ++iter) { | |
116 if ((*iter)->GetNativeWindow()->IsVisible()) | |
117 return true; | 66 return true; |
118 } | 67 } |
119 return false; | 68 return false; |
120 } | 69 } |
121 | 70 |
122 void AppWindowLauncherItemController::Launch(ash::LaunchSource source, | 71 void AppWindowLauncherItemController::Launch(ash::LaunchSource source, |
123 int event_flags) { | 72 int event_flags) { |
124 launcher_controller()->LaunchApp(app_id(), source, ui::EF_NONE); | 73 launcher_controller()->LaunchApp(app_id(), source, ui::EF_NONE); |
125 } | 74 } |
126 | 75 |
127 ash::ShelfItemDelegate::PerformedAction | 76 ash::ShelfItemDelegate::PerformedAction |
128 AppWindowLauncherItemController::Activate(ash::LaunchSource source) { | 77 AppWindowLauncherItemController::Activate(ash::LaunchSource source) { |
129 DCHECK(!app_windows_.empty()); | 78 DCHECK(!windows_.empty()); |
130 AppWindow* window_to_activate = | 79 ui::BaseWindow* window_to_activate = |
131 last_active_app_window_ ? last_active_app_window_ : app_windows_.back(); | 80 last_active_window_ ? last_active_window_ : windows_.back(); |
132 window_to_activate->GetBaseWindow()->Activate(); | 81 window_to_activate->Activate(); |
133 return kExistingWindowActivated; | 82 return kExistingWindowActivated; |
134 } | 83 } |
135 | 84 |
136 void AppWindowLauncherItemController::Close() { | 85 void AppWindowLauncherItemController::Close() { |
137 // Note: Closing windows may affect the contents of app_windows_. | 86 // Note: Closing windows may affect the contents of app_windows_. |
138 AppWindowList windows_to_close = app_windows_; | 87 WindowList windows_to_close = windows_; |
139 for (AppWindowList::iterator iter = windows_to_close.begin(); | 88 for (const auto& window : windows_) |
140 iter != windows_to_close.end(); | 89 window->Close(); |
141 ++iter) { | |
142 (*iter)->GetBaseWindow()->Close(); | |
143 } | |
144 } | 90 } |
145 | 91 |
146 void AppWindowLauncherItemController::ActivateIndexedApp(size_t index) { | 92 void AppWindowLauncherItemController::ActivateIndexedApp(size_t index) { |
147 if (index >= app_windows_.size()) | 93 if (index >= windows_.size()) |
148 return; | 94 return; |
149 AppWindowList::iterator it = app_windows_.begin(); | 95 auto it = windows_.begin(); |
150 std::advance(it, index); | 96 std::advance(it, index); |
151 ShowAndActivateOrMinimize(*it); | 97 ShowAndActivateOrMinimize(*it); |
152 } | 98 } |
153 | 99 |
154 ChromeLauncherAppMenuItems AppWindowLauncherItemController::GetApplicationList( | 100 ChromeLauncherAppMenuItems AppWindowLauncherItemController::GetApplicationList( |
155 int event_flags) { | 101 int event_flags) { |
156 ChromeLauncherAppMenuItems items; | 102 ChromeLauncherAppMenuItems items; |
157 items.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL, false)); | 103 items.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL, false)); |
158 int index = 0; | |
159 for (AppWindowList::iterator iter = app_windows_.begin(); | |
160 iter != app_windows_.end(); | |
161 ++iter) { | |
162 AppWindow* app_window = *iter; | |
163 | |
164 // If the app's web contents provides a favicon, use it. Otherwise, use a | |
165 // scaled down app icon. | |
166 favicon::FaviconDriver* favicon_driver = | |
167 favicon::ContentFaviconDriver::FromWebContents( | |
168 app_window->web_contents()); | |
169 gfx::Image result = favicon_driver->GetFavicon(); | |
170 if (result.IsEmpty()) | |
171 result = GetAppListIcon(app_window); | |
172 | |
173 items.push_back(new ChromeLauncherAppMenuItemV2App( | |
174 app_window->GetTitle(), | |
175 &result, // Will be copied | |
176 app_id(), | |
177 launcher_controller(), | |
178 index, | |
179 index == 0 /* has_leading_separator */)); | |
180 ++index; | |
181 } | |
182 return items; | 104 return items; |
183 } | 105 } |
184 | 106 |
185 ash::ShelfItemDelegate::PerformedAction | 107 ash::ShelfItemDelegate::PerformedAction |
186 AppWindowLauncherItemController::ItemSelected(const ui::Event& event) { | 108 AppWindowLauncherItemController::ItemSelected(const ui::Event& event) { |
187 if (app_windows_.empty()) | 109 if (windows_.empty()) |
188 return kNoAction; | 110 return kNoAction; |
189 if (type() == TYPE_APP_PANEL) { | 111 |
190 DCHECK_EQ(app_windows_.size(), 1u); | 112 DCHECK_EQ(TYPE_APP, type()); |
191 AppWindow* panel = app_windows_.front(); | 113 ui::BaseWindow* window_to_show = |
192 aura::Window* panel_window = panel->GetNativeWindow(); | 114 last_active_window_ ? last_active_window_ : windows_.front(); |
193 // If the panel is attached on another display, move it to the current | 115 // If the event was triggered by a keystroke, we try to advance to the next |
194 // display and activate it. | 116 // item if the window we are trying to activate is already active. |
195 if (ash::wm::GetWindowState(panel_window)->panel_attached() && | 117 if (windows_.size() >= 1 && window_to_show->IsActive() && |
196 ash::wm::MoveWindowToEventRoot(panel_window, event)) { | 118 event.type() == ui::ET_KEY_RELEASED) { |
197 if (!panel->GetBaseWindow()->IsActive()) | 119 return ActivateOrAdvanceToNextAppWindow(window_to_show); |
198 return ShowAndActivateOrMinimize(panel); | |
199 } else { | |
200 return ShowAndActivateOrMinimize(panel); | |
201 } | |
202 } else { | 120 } else { |
203 AppWindow* window_to_show = last_active_app_window_ | 121 return ShowAndActivateOrMinimize(window_to_show); |
204 ? last_active_app_window_ | |
205 : app_windows_.front(); | |
206 // If the event was triggered by a keystroke, we try to advance to the next | |
207 // item if the window we are trying to activate is already active. | |
208 if (app_windows_.size() >= 1 && | |
209 window_to_show->GetBaseWindow()->IsActive() && | |
210 event.type() == ui::ET_KEY_RELEASED) { | |
211 return ActivateOrAdvanceToNextAppWindow(window_to_show); | |
212 } else { | |
213 return ShowAndActivateOrMinimize(window_to_show); | |
214 } | |
215 } | 122 } |
216 return kNoAction; | |
217 } | 123 } |
218 | 124 |
219 base::string16 AppWindowLauncherItemController::GetTitle() { | 125 base::string16 AppWindowLauncherItemController::GetTitle() { |
220 // For panels return the title of the contents if set. | |
221 // Otherwise return the title of the app. | |
222 if (type() == TYPE_APP_PANEL && !app_windows_.empty()) { | |
223 AppWindow* app_window = app_windows_.front(); | |
224 if (app_window->web_contents()) { | |
225 base::string16 title = app_window->web_contents()->GetTitle(); | |
226 if (!title.empty()) | |
227 return title; | |
228 } | |
229 } | |
230 return GetAppTitle(); | 126 return GetAppTitle(); |
231 } | 127 } |
232 | 128 |
233 ash::ShelfMenuModel* AppWindowLauncherItemController::CreateApplicationMenu( | |
234 int event_flags) { | |
235 return new LauncherApplicationMenuItemModel(GetApplicationList(event_flags)); | |
236 } | |
237 | |
238 bool AppWindowLauncherItemController::IsDraggable() { | 129 bool AppWindowLauncherItemController::IsDraggable() { |
239 if (type() == TYPE_APP_PANEL) | 130 DCHECK_EQ(TYPE_APP, type()); |
240 return true; | |
241 return CanPin(); | 131 return CanPin(); |
242 } | 132 } |
243 | 133 |
244 bool AppWindowLauncherItemController::CanPin() const { | 134 bool AppWindowLauncherItemController::CanPin() const { |
245 return launcher_controller()->CanPin(app_id()); | 135 return launcher_controller()->CanPin(app_id()); |
246 } | 136 } |
247 | 137 |
248 bool AppWindowLauncherItemController::ShouldShowTooltip() { | 138 bool AppWindowLauncherItemController::ShouldShowTooltip() { |
249 if (type() == TYPE_APP_PANEL && IsVisible()) | 139 DCHECK_EQ(TYPE_APP, type()); |
250 return false; | |
251 return true; | 140 return true; |
252 } | 141 } |
253 | 142 |
254 void AppWindowLauncherItemController::OnWindowPropertyChanged( | 143 void AppWindowLauncherItemController::OnWindowPropertyChanged( |
255 aura::Window* window, | 144 aura::Window* window, |
256 const void* key, | 145 const void* key, |
257 intptr_t old) { | 146 intptr_t old) { |
258 if (key == aura::client::kDrawAttentionKey) { | 147 if (key == aura::client::kDrawAttentionKey) { |
259 ash::ShelfItemStatus status; | 148 ash::ShelfItemStatus status; |
260 if (ash::wm::IsActiveWindow(window)) { | 149 if (ash::wm::IsActiveWindow(window)) { |
261 status = ash::STATUS_ACTIVE; | 150 status = ash::STATUS_ACTIVE; |
262 } else if (window->GetProperty(aura::client::kDrawAttentionKey)) { | 151 } else if (window->GetProperty(aura::client::kDrawAttentionKey)) { |
263 status = ash::STATUS_ATTENTION; | 152 status = ash::STATUS_ATTENTION; |
264 } else { | 153 } else { |
265 status = ash::STATUS_RUNNING; | 154 status = ash::STATUS_RUNNING; |
266 } | 155 } |
267 launcher_controller()->SetItemStatus(shelf_id(), status); | 156 launcher_controller()->SetItemStatus(shelf_id(), status); |
268 } | 157 } |
269 } | 158 } |
270 | 159 |
271 ash::ShelfItemDelegate::PerformedAction | 160 ash::ShelfItemDelegate::PerformedAction |
272 AppWindowLauncherItemController::ShowAndActivateOrMinimize( | 161 AppWindowLauncherItemController::ShowAndActivateOrMinimize( |
273 AppWindow* app_window) { | 162 ui::BaseWindow* app_window) { |
274 // Either show or minimize windows when shown from the launcher. | 163 // Either show or minimize windows when shown from the launcher. |
275 return launcher_controller()->ActivateWindowOrMinimizeIfActive( | 164 return launcher_controller()->ActivateWindowOrMinimizeIfActive( |
276 app_window->GetBaseWindow(), GetApplicationList(0).size() == 2); | 165 app_window, GetApplicationList(0).size() == 2); |
277 } | 166 } |
278 | 167 |
279 ash::ShelfItemDelegate::PerformedAction | 168 ash::ShelfItemDelegate::PerformedAction |
280 AppWindowLauncherItemController::ActivateOrAdvanceToNextAppWindow( | 169 AppWindowLauncherItemController::ActivateOrAdvanceToNextAppWindow( |
281 AppWindow* window_to_show) { | 170 ui::BaseWindow* window_to_show) { |
282 AppWindowList::iterator i( | 171 WindowList::iterator i( |
283 std::find(app_windows_.begin(), app_windows_.end(), window_to_show)); | 172 std::find(windows_.begin(), windows_.end(), window_to_show)); |
284 if (i != app_windows_.end()) { | 173 if (i != windows_.end()) { |
285 if (++i != app_windows_.end()) | 174 if (++i != windows_.end()) |
286 window_to_show = *i; | 175 window_to_show = *i; |
287 else | 176 else |
288 window_to_show = app_windows_.front(); | 177 window_to_show = windows_.front(); |
289 } | 178 } |
290 if (window_to_show->GetBaseWindow()->IsActive()) { | 179 if (window_to_show->IsActive()) { |
291 // Coming here, only a single window is active. For keyboard activations | 180 // Coming here, only a single window is active. For keyboard activations |
292 // the window gets animated. | 181 // the window gets animated. |
293 AnimateWindow(window_to_show->GetNativeWindow(), | 182 AnimateWindow(window_to_show->GetNativeWindow(), |
294 wm::WINDOW_ANIMATION_TYPE_BOUNCE); | 183 wm::WINDOW_ANIMATION_TYPE_BOUNCE); |
295 } else { | 184 } else { |
296 return ShowAndActivateOrMinimize(window_to_show); | 185 return ShowAndActivateOrMinimize(window_to_show); |
297 } | 186 } |
298 return kNoAction; | 187 return kNoAction; |
299 } | 188 } |
OLD | NEW |