Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(236)

Side by Side Diff: ash/wm/workspace/workspace_manager2.cc

Issue 10830365: Initial crack at new workspace behavior. Each workspace now has its (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ash/wm/workspace/workspace_manager2.h ('k') | ash/wm/workspace/workspace_manager2_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "ash/wm/workspace/workspace_manager2.h"
6
7 #include <algorithm>
8 #include <functional>
9
10 #include "ash/screen_ash.h"
11 #include "ash/shell.h"
12 #include "ash/shell_window_ids.h"
13 #include "ash/wm/always_on_top_controller.h"
14 #include "ash/wm/base_layout_manager.h"
15 #include "ash/wm/property_util.h"
16 #include "ash/wm/shelf_layout_manager.h"
17 #include "ash/wm/window_animations.h"
18 #include "ash/wm/window_properties.h"
19 #include "ash/wm/window_util.h"
20 #include "ash/wm/workspace/workspace2.h"
21 #include "base/auto_reset.h"
22 #include "base/logging.h"
23 #include "base/stl_util.h"
24 #include "base/stringprintf.h"
25 #include "ui/aura/client/aura_constants.h"
26 #include "ui/aura/env.h"
27 #include "ui/aura/root_window.h"
28 #include "ui/aura/window.h"
29 #include "ui/aura/window_property.h"
30 #include "ui/base/ui_base_types.h"
31 #include "ui/compositor/layer.h"
32 #include "ui/compositor/layer_animator.h"
33 #include "ui/compositor/scoped_layer_animation_settings.h"
34 #include "ui/gfx/screen.h"
35 #include "ui/gfx/transform.h"
36
37 DECLARE_WINDOW_PROPERTY_TYPE(ash::internal::Workspace2*);
38
39 using aura::Window;
40
41 namespace ash {
42 namespace internal {
43
44 DEFINE_WINDOW_PROPERTY_KEY(Workspace2*, kWorkspaceKey, NULL);
45
46 namespace {
47
48 // Changes the parent of |window| and all its transient children to
49 // |new_parent|. If |stack_beneach| is non-NULL all the windows are stacked
50 // beneath it.
51 void ReparentWindow(Window* window,
52 Window* new_parent,
53 Window* stack_beneath) {
54 window->SetParent(new_parent);
55 if (stack_beneath)
56 new_parent->StackChildBelow(window, stack_beneath);
57 for (size_t i = 0; i < window->transient_children().size(); ++i)
58 ReparentWindow(window->transient_children()[i], new_parent, stack_beneath);
59 }
60
61 // Workspace -------------------------------------------------------------------
62
63 // LayoutManager installed on the parent window of all the Workspace windows (eg
64 // |WorkspaceManager2::contents_view_|).
65 class WorkspaceManagerLayoutManager2 : public BaseLayoutManager {
66 public:
67 WorkspaceManagerLayoutManager2(Window* window)
68 : BaseLayoutManager(window->GetRootWindow()),
69 window_(window) {
70 }
71 virtual ~WorkspaceManagerLayoutManager2() {}
72
73 // Overridden from BaseWorkspaceLayoutManager:
74 virtual void OnWindowResized() OVERRIDE {
75 for (size_t i = 0; i < window_->children().size(); ++i)
76 window_->children()[i]->SetBounds(gfx::Rect(window_->bounds().size()));
77 }
78 virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {
79 // Only workspaces should be added as children.
80 DCHECK_EQ(kShellWindowId_WorkspaceContainer, child->id());
81 child->SetBounds(gfx::Rect(window_->bounds().size()));
82 }
83
84 private:
85 Window* window_;
86
87 DISALLOW_COPY_AND_ASSIGN(WorkspaceManagerLayoutManager2);
88 };
89
90 } // namespace
91
92 // WorkspaceLayoutManager ------------------------------------------------------
93
94 // LayoutManager installed on the window each workspace contains.
95 class WorkspaceManager2::WorkspaceLayoutManager : public BaseLayoutManager {
96 public:
97 WorkspaceLayoutManager(aura::RootWindow* root_window, Workspace2* workspace)
98 : BaseLayoutManager(root_window),
99 workspace_(workspace) {
100 }
101 virtual ~WorkspaceLayoutManager() {
102 }
103
104 // Overridden from BaseWorkspaceLayoutManager:
105 virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {
106 BaseLayoutManager::OnWindowAddedToLayout(child);
107 child->SetProperty(kWorkspaceKey, workspace_);
108 workspace_manager()->OnWindowAddedToWorkspace(workspace_, child);
109 }
110
111 virtual void OnWillRemoveWindowFromLayout(Window* child) OVERRIDE {
112 BaseLayoutManager::OnWillRemoveWindowFromLayout(child);
113 child->ClearProperty(kWorkspaceKey);
114 }
115
116 virtual void OnWindowRemovedFromLayout(Window* child) OVERRIDE {
117 BaseLayoutManager::OnWindowRemovedFromLayout(child);
118 workspace_manager()->OnWindowRemovedFromWorkspace(workspace_, child);
119 }
120
121 virtual void OnChildWindowVisibilityChanged(Window* child,
122 bool visibile) OVERRIDE {
123 BaseLayoutManager::OnChildWindowVisibilityChanged(child, visibile);
124 workspace_manager()->OnWorkspaceChildWindowVisibilityChanged(workspace_,
125 child);
126 }
127
128 virtual void SetChildBounds(Window* child,
129 const gfx::Rect& requested_bounds) OVERRIDE {
130 BaseLayoutManager::SetChildBounds(child, requested_bounds);
131 workspace_manager()->OnWorkspaceWindowChildBoundsChanged(workspace_, child);
132 }
133
134
135 // Overriden from WindowObserver:
136 virtual void OnWindowPropertyChanged(Window* window,
137 const void* key,
138 intptr_t old) {
139 BaseLayoutManager::OnWindowPropertyChanged(window, key, old);
140
141 if (key == aura::client::kAlwaysOnTopKey &&
142 window->GetProperty(aura::client::kAlwaysOnTopKey)) {
143 internal::AlwaysOnTopController* controller =
144 window->GetRootWindow()->GetProperty(
145 internal::kAlwaysOnTopControllerKey);
146 controller->GetContainer(window)->AddChild(window);
147 }
148 }
149
150 protected:
151 // Overriden from WindowObserver:
152 virtual void ShowStateChanged(Window* window,
153 ui::WindowShowState last_show_state) OVERRIDE {
154 // NOTE: we can't use BaseLayoutManager::ShowStateChanged() as we need to
155 // forward to WorkspaceManager before the window is hidden.
156 if (wm::IsWindowMinimized(window)) {
157 // Save the previous show state so that we can correctly restore it.
158 window->SetProperty(internal::kRestoreShowStateKey, last_show_state);
159 SetWindowVisibilityAnimationType(
160 window, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
161
162 workspace_manager()->OnWorkspaceWindowShowStateChanged(
163 workspace_, window, last_show_state);
164
165 // Hide the window.
166 window->Hide();
167
168 // Activate another window.
169 if (wm::IsActiveWindow(window))
170 wm::DeactivateWindow(window);
171 } else {
172 if ((window->TargetVisibility() ||
173 last_show_state == ui::SHOW_STATE_MINIMIZED) &&
174 !window->layer()->visible()) {
175 // The layer may be hidden if the window was previously minimized. Make
176 // sure it's visible.
177 window->Show();
178 }
179 workspace_manager()->OnWorkspaceWindowShowStateChanged(
180 workspace_, window, last_show_state);
181 }
182 }
183
184 private:
185 WorkspaceManager2* workspace_manager() {
186 return workspace_->workspace_manager();
187 }
188
189 Workspace2* workspace_;
190
191 DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManager);
192 };
193
194 // WorkspaceManager2 -----------------------------------------------------------
195
196 WorkspaceManager2::WorkspaceManager2(Window* contents_view)
197 : contents_view_(contents_view),
198 active_workspace_(NULL),
199 grid_size_(0),
200 shelf_(NULL),
201 in_move_(false) {
202 // Clobber any existing event filter.
203 contents_view->SetEventFilter(NULL);
204 // |contents_view| takes ownership of WorkspaceManagerLayoutManager2.
205 contents_view->SetLayoutManager(
206 new WorkspaceManagerLayoutManager2(contents_view));
207 active_workspace_ = CreateWorkspace(false);
208 workspaces_.push_back(active_workspace_);
209 }
210
211 WorkspaceManager2::~WorkspaceManager2() {
212 // Release the windows, they'll be destroyed when |contents_view_| is
213 // destroyed.
214 std::for_each(workspaces_.begin(), workspaces_.end(),
215 std::mem_fun(&Workspace2::ReleaseWindow));
216 std::for_each(pending_workspaces_.begin(), pending_workspaces_.end(),
217 std::mem_fun(&Workspace2::ReleaseWindow));
218 STLDeleteElements(&workspaces_);
219 STLDeleteElements(&pending_workspaces_);
220 }
221
222 // static
223 bool WorkspaceManager2::IsMaximized(Window* window) {
224 return wm::IsWindowFullscreen(window) || wm::IsWindowMaximized(window);
225 }
226
227 // static
228 bool WorkspaceManager2::WillRestoreMaximized(aura::Window* window) {
229 if (!wm::IsWindowMinimized(window))
230 return false;
231
232 ui::WindowShowState restore_state =
233 window->GetProperty(internal::kRestoreShowStateKey);
234 return restore_state == ui::SHOW_STATE_MAXIMIZED ||
235 restore_state == ui::SHOW_STATE_FULLSCREEN;
236
237 }
238
239 bool WorkspaceManager2::IsInMaximizedMode() const {
240 return active_workspace_ && active_workspace_->is_maximized();
241 }
242
243 void WorkspaceManager2::SetGridSize(int size) {
244 grid_size_ = size;
245 std::for_each(workspaces_.begin(), workspaces_.end(),
246 std::bind2nd(std::mem_fun(&Workspace2::SetGridSize),
247 grid_size_));
248 std::for_each(pending_workspaces_.begin(), pending_workspaces_.end(),
249 std::bind2nd(std::mem_fun(&Workspace2::SetGridSize),
250 grid_size_));
251 }
252
253 int WorkspaceManager2::GetGridSize() const {
254 return grid_size_;
255 }
256
257 WorkspaceWindowState WorkspaceManager2::GetWindowState() const {
258 if (!shelf_)
259 return WORKSPACE_WINDOW_STATE_DEFAULT;
260
261 gfx::Rect shelf_bounds(shelf_->GetIdealBounds());
262 const Window::Windows& windows(active_workspace_->window()->children());
263 bool window_overlaps_launcher = false;
264 bool has_maximized_window = false;
265 for (Window::Windows::const_iterator i = windows.begin();
266 i != windows.end(); ++i) {
267 ui::Layer* layer = (*i)->layer();
268 if (!layer->GetTargetVisibility() || layer->GetTargetOpacity() == 0.0f)
269 continue;
270 if (wm::IsWindowMaximized(*i)) {
271 // An untracked window may still be fullscreen so we keep iterating when
272 // we hit a maximized window.
273 has_maximized_window = true;
274 } else if (wm::IsWindowFullscreen(*i)) {
275 return WORKSPACE_WINDOW_STATE_FULL_SCREEN;
276 }
277 if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds))
278 window_overlaps_launcher = true;
279 }
280 if (has_maximized_window)
281 return WORKSPACE_WINDOW_STATE_MAXIMIZED;
282
283 return window_overlaps_launcher ?
284 WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF :
285 WORKSPACE_WINDOW_STATE_DEFAULT;
286 }
287
288 void WorkspaceManager2::SetShelf(ShelfLayoutManager* shelf) {
289 shelf_ = shelf;
290 }
291
292 void WorkspaceManager2::SetActiveWorkspaceByWindow(Window* window) {
293 Workspace2* workspace = FindBy(window);
294 if (!workspace)
295 return;
296
297 if (workspace != active_workspace_) {
298 // If the window persists across all workspaces, move it to the current
299 // workspace.
300 if (GetPersistsAcrossAllWorkspaces(window) && !IsMaximized(window))
301 ReparentWindow(window, active_workspace_->window(), NULL);
302 else
303 SetActiveWorkspace(workspace);
304 }
305 UpdateShelfVisibility();
306 }
307
308 Window* WorkspaceManager2::GetParentForNewWindow(Window* window) {
309 if (window->transient_parent()) {
310 DCHECK(contents_view_->Contains(window->transient_parent()));
311 DCHECK(!IsMaximized(window));
312 return window->transient_parent()->parent();
313 }
314
315 if (IsMaximized(window)) {
316 // Wait for the window to be made active before showing the workspace.
317 Workspace2* workspace = CreateWorkspace(false);
318 pending_workspaces_.insert(workspace);
319 return workspace->window();
320 }
321
322 if (!GetTrackedByWorkspace(window) || GetPersistsAcrossAllWorkspaces(window))
323 return active_workspace_->window();
324
325 return desktop_workspace()->window();
326 }
327
328 void WorkspaceManager2::UpdateShelfVisibility() {
329 if (shelf_)
330 shelf_->UpdateVisibilityState();
331 }
332
333 Workspace2* WorkspaceManager2::FindBy(Window* window) const {
334 while (window) {
335 Workspace2* workspace = window->GetProperty(kWorkspaceKey);
336 if (workspace)
337 return workspace;
338 window = window->transient_parent();
339 }
340 return NULL;
341 }
342
343 void WorkspaceManager2::SetActiveWorkspace(Workspace2* workspace) {
344 DCHECK(workspace);
345 if (active_workspace_ == workspace)
346 return;
347
348 // TODO: sort out animations.
349
350 pending_workspaces_.erase(workspace);
351
352 // Adjust the z-order. No need to adjust the z-order for the desktop since
353 // it always stays at the bottom.
354 if (workspace != desktop_workspace()) {
355 if (FindWorkspace(workspace) == workspaces_.end()) {
356 contents_view_->StackChildAbove(workspace->window(),
357 workspaces_.back()->window());
358 workspaces_.push_back(workspace);
359 }
360 }
361
362 active_workspace_ = workspace;
363
364 for (size_t i = 0; i < workspaces_.size(); ++i) {
365 if (workspaces_[i] == active_workspace_)
366 workspaces_[i]->window()->Show();
367 else
368 workspaces_[i]->window()->Hide();
369 }
370 }
371
372 WorkspaceManager2::Workspaces::iterator
373 WorkspaceManager2::FindWorkspace(Workspace2* workspace) {
374 return std::find(workspaces_.begin(), workspaces_.end(), workspace);
375 }
376
377 Workspace2* WorkspaceManager2::CreateWorkspace(bool maximized) {
378 Workspace2* workspace = new Workspace2(this, contents_view_, maximized);
379 workspace->SetGridSize(grid_size_);
380 workspace->window()->SetLayoutManager(
381 new WorkspaceLayoutManager(contents_view_->GetRootWindow(), workspace));
382 return workspace;
383 }
384
385 void WorkspaceManager2::MoveWorkspaceToPendingOrDelete(
386 Workspace2* workspace,
387 Window* stack_beneath) {
388 // We're all ready moving windows.
389 if (in_move_)
390 return;
391
392 DCHECK_NE(desktop_workspace(), workspace);
393
394 if (workspace == active_workspace_)
395 SelectNextWorkspace();
396
397 AutoReset<bool> setter(&in_move_, true);
398
399 // Move all non-maximized/fullscreen windows to the desktop.
400 {
401 // Build the list of windows to move. Exclude maximized/fullscreen and
402 // windows with transient parents.
403 Window::Windows to_move;
404 Window* w_window = workspace->window();
405 for (size_t i = 0; i < w_window->children().size(); ++i) {
406 Window* child = w_window->children()[i];
407 if (!child->transient_parent() && !IsMaximized(child) &&
408 !WillRestoreMaximized(child)) {
409 to_move.push_back(child);
410 }
411 }
412 // Move the windows, but make sure the window is still a child of |w_window|
413 // before moving (moving may cascade and cause other windows to move).
414 for (size_t i = 0; i < to_move.size(); ++i) {
415 if (std::find(w_window->children().begin(), w_window->children().end(),
416 to_move[i]) != w_window->children().end()) {
417 ReparentWindow(to_move[i], desktop_workspace()->window(),
418 stack_beneath);
419 }
420 }
421 }
422
423 {
424 Workspaces::iterator workspace_i(FindWorkspace(workspace));
425 if (workspace_i != workspaces_.end())
426 workspaces_.erase(workspace_i);
427 }
428
429 if (workspace->window()->children().empty()) {
430 pending_workspaces_.erase(workspace);
431 delete workspace->ReleaseWindow();
432 delete workspace;
433 } else {
434 pending_workspaces_.insert(workspace);
435 }
436 }
437
438 void WorkspaceManager2::SelectNextWorkspace() {
439 DCHECK_NE(active_workspace_, desktop_workspace());
440
441 Workspaces::const_iterator workspace_i(FindWorkspace(active_workspace_));
442 Workspaces::const_iterator next_workspace_i(workspace_i + 1);
443 if (next_workspace_i != workspaces_.end())
444 SetActiveWorkspace(*next_workspace_i);
445 else
446 SetActiveWorkspace(*(workspace_i - 1));
447 UpdateShelfVisibility();
448 }
449
450 void WorkspaceManager2::OnWindowAddedToWorkspace(Workspace2* workspace,
451 Window* child) {
452 // Do nothing (other than updating shelf visibility) as the right parent was
453 // chosen by way of GetParentForNewWindow() or we explicitly moved the window
454 // to the workspace.
455 if (workspace == active_workspace_)
456 UpdateShelfVisibility();
457 }
458
459 void WorkspaceManager2::OnWindowRemovedFromWorkspace(Workspace2* workspace,
460 Window* child) {
461 if (workspace->ShouldMoveToPending())
462 MoveWorkspaceToPendingOrDelete(workspace, NULL);
463 }
464
465 void WorkspaceManager2::OnWorkspaceChildWindowVisibilityChanged(
466 Workspace2* workspace,
467 Window* child) {
468 if (workspace->ShouldMoveToPending())
469 MoveWorkspaceToPendingOrDelete(workspace, NULL);
470 }
471
472 void WorkspaceManager2::OnWorkspaceWindowChildBoundsChanged(
473 Workspace2* workspace,
474 Window* child) {
475 }
476
477 void WorkspaceManager2::OnWorkspaceWindowShowStateChanged(
478 Workspace2* workspace,
479 Window* child,
480 ui::WindowShowState last_show_state) {
481 if (wm::IsWindowMinimized(child)) {
482 if (workspace->ShouldMoveToPending())
483 MoveWorkspaceToPendingOrDelete(workspace, NULL);
484 } else {
485 // Here's the cases that need to be handled:
486 // . More than one maximized window: move newly maximized window into
487 // own workspace.
488 // . One maximized window and not in a maximized workspace: move window
489 // into own workspace.
490 // . No maximized window and not in desktop: move to desktop and further
491 // any existing windows are stacked beneath |child|.
492 const bool is_active = wm::IsActiveWindow(child);
493 Workspace2* new_workspace = NULL;
494 const int max_count = workspace->GetNumMaximizedWindows();
495 if (max_count == 0) {
496 if (workspace != desktop_workspace()) {
497 {
498 AutoReset<bool> setter(&in_move_, true);
499 ReparentWindow(child, desktop_workspace()->window(), NULL);
500 }
501 MoveWorkspaceToPendingOrDelete(workspace, child);
502 new_workspace = desktop_workspace();
503 }
504 } else if (max_count == 1) {
505 if (workspace == desktop_workspace()) {
506 new_workspace = CreateWorkspace(true);
507 pending_workspaces_.insert(new_workspace);
508 ReparentWindow(child, new_workspace->window(), NULL);
509 }
510 } else {
511 new_workspace = CreateWorkspace(true);
512 pending_workspaces_.insert(new_workspace);
513 ReparentWindow(child, new_workspace->window(), NULL);
514 }
515 if (is_active && new_workspace)
516 SetActiveWorkspace(new_workspace);
517 }
518 UpdateShelfVisibility();
519 }
520
521 } // namespace internal
522 } // namespace ash
OLDNEW
« no previous file with comments | « ash/wm/workspace/workspace_manager2.h ('k') | ash/wm/workspace/workspace_manager2_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698