OLD | NEW |
| (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/compact_layout_manager.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "ash/shell.h" | |
10 #include "ash/shell_delegate.h" | |
11 #include "ash/shell_window_ids.h" | |
12 #include "ash/wm/window_util.h" | |
13 #include "ui/aura/client/aura_constants.h" | |
14 #include "ui/aura/window.h" | |
15 #include "ui/gfx/compositor/layer.h" | |
16 #include "ui/gfx/compositor/layer_animation_sequence.h" | |
17 #include "ui/gfx/compositor/scoped_layer_animation_settings.h" | |
18 #include "ui/gfx/screen.h" | |
19 #include "ui/views/widget/widget.h" | |
20 | |
21 namespace ash { | |
22 namespace internal { | |
23 | |
24 namespace { | |
25 | |
26 typedef std::vector<aura::Window*> WindowList; | |
27 typedef std::vector<aura::Window*>::const_iterator WindowListConstIter; | |
28 | |
29 // Convenience method to get the layer of this container. | |
30 ui::Layer* GetDefaultContainerLayer() { | |
31 return Shell::GetInstance()->GetContainer( | |
32 internal::kShellWindowId_DefaultContainer)->layer(); | |
33 } | |
34 | |
35 // Whether it is a window that should be animated on entrance. | |
36 bool ShouldAnimateOnEntrance(aura::Window* window) { | |
37 return window && | |
38 window->type() == aura::client::WINDOW_TYPE_NORMAL && | |
39 wm::IsWindowMaximized(window); | |
40 } | |
41 | |
42 // Adjust layer bounds to grow or shrink in |delta_width|. | |
43 void AdjustContainerLayerWidth(int delta_width) { | |
44 gfx::Rect bounds(GetDefaultContainerLayer()->bounds()); | |
45 bounds.set_width(bounds.width() + delta_width); | |
46 GetDefaultContainerLayer()->SetBounds(bounds); | |
47 } | |
48 | |
49 } // namespace | |
50 | |
51 ///////////////////////////////////////////////////////////////////////////// | |
52 // CompactLayoutManager, public: | |
53 | |
54 CompactLayoutManager::CompactLayoutManager() | |
55 : status_area_widget_(NULL), | |
56 current_window_(NULL) { | |
57 } | |
58 | |
59 CompactLayoutManager::~CompactLayoutManager() { | |
60 } | |
61 | |
62 ///////////////////////////////////////////////////////////////////////////// | |
63 // CompactLayoutManager, LayoutManager overrides: | |
64 | |
65 void CompactLayoutManager::OnWindowAddedToLayout(aura::Window* child) { | |
66 // Windows added to this container does not need extra animation. | |
67 if (child->type() == aura::client::WINDOW_TYPE_NORMAL) | |
68 child->SetProperty(aura::client::kAnimationsDisabledKey, true); | |
69 BaseLayoutManager::OnWindowAddedToLayout(child); | |
70 UpdateStatusAreaVisibility(); | |
71 if (windows().size() > 1 && | |
72 child->type() == aura::client::WINDOW_TYPE_NORMAL) { | |
73 // The first window is already contained in the current layer, | |
74 // add subsequent windows to layer bounds calculation. | |
75 AdjustContainerLayerWidth(child->bounds().width()); | |
76 } | |
77 } | |
78 | |
79 void CompactLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) { | |
80 BaseLayoutManager::OnWillRemoveWindowFromLayout(child); | |
81 UpdateStatusAreaVisibility(); | |
82 if (windows().size() > 1 && ShouldAnimateOnEntrance(child)) | |
83 AdjustContainerLayerWidth(-child->bounds().width()); | |
84 | |
85 if (child == current_window_) { | |
86 LayoutWindows(current_window_); | |
87 SwitchToReplacementWindow(); | |
88 } | |
89 // Allow window to be animated by others. | |
90 if (child->type() == aura::client::WINDOW_TYPE_NORMAL) | |
91 child->SetProperty(aura::client::kAnimationsDisabledKey, false); | |
92 } | |
93 | |
94 void CompactLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child, | |
95 bool visible) { | |
96 BaseLayoutManager::OnChildWindowVisibilityChanged(child, visible); | |
97 UpdateStatusAreaVisibility(); | |
98 if (ShouldAnimateOnEntrance(child)) { | |
99 LayoutWindows(visible ? NULL : child); | |
100 if (visible) { | |
101 current_window_ = child; | |
102 AnimateSlideTo(child->bounds().x()); | |
103 } else if (child == current_window_) { | |
104 SwitchToReplacementWindow(); | |
105 } | |
106 } | |
107 } | |
108 | |
109 void CompactLayoutManager::SetChildBounds(aura::Window* child, | |
110 const gfx::Rect& requested_bounds) { | |
111 gfx::Rect child_bounds(requested_bounds); | |
112 // Avoid a janky resize on startup by ensuring the initial bounds fill the | |
113 // screen. | |
114 if (wm::IsWindowMaximized(child)) | |
115 child_bounds = gfx::Screen::GetMonitorWorkAreaNearestWindow(child); | |
116 else if (wm::IsWindowFullscreen(child)) | |
117 child_bounds = gfx::Screen::GetMonitorAreaNearestWindow(child); | |
118 else if (current_window_) { | |
119 // All other windows should be offset by the current viewport. | |
120 int offset_x = current_window_->bounds().x(); | |
121 child_bounds.Offset(offset_x, 0); | |
122 } | |
123 SetChildBoundsDirect(child, child_bounds); | |
124 } | |
125 | |
126 ///////////////////////////////////////////////////////////////////////////// | |
127 // CompactLayoutManager, aura::WindowObserver overrides: | |
128 | |
129 void CompactLayoutManager::OnWindowPropertyChanged(aura::Window* window, | |
130 const void* key, | |
131 intptr_t old) { | |
132 BaseLayoutManager::OnWindowPropertyChanged(window, key, old); | |
133 if (key == aura::client::kShowStateKey) | |
134 UpdateStatusAreaVisibility(); | |
135 } | |
136 | |
137 void CompactLayoutManager::OnWindowStackingChanged(aura::Window* window) { | |
138 if (!current_window_ || ShouldAnimateOnEntrance(window)) { | |
139 if (current_window_ != window) { | |
140 LayoutWindows(current_window_); | |
141 current_window_ = window; | |
142 } else { | |
143 // Same window as |current_window_|, and already animating. | |
144 if (GetDefaultContainerLayer()->GetAnimator()->is_animating()) | |
145 return; | |
146 } | |
147 // Animate to |window| when there is a stacking change. | |
148 AnimateSlideTo(window->bounds().x()); | |
149 } | |
150 } | |
151 | |
152 ///////////////////////////////////////////////////////////////////////////// | |
153 // CompactLayoutManager, AnimationDelegate overrides: | |
154 | |
155 void CompactLayoutManager::OnImplicitAnimationsCompleted() { | |
156 if (!GetDefaultContainerLayer()->GetAnimator()->is_animating()) | |
157 HideWindows(); | |
158 } | |
159 | |
160 ////////////////////////////////////////////////////////////////////////////// | |
161 // CompactLayoutManager, private: | |
162 | |
163 void CompactLayoutManager::UpdateStatusAreaVisibility() { | |
164 if (!status_area_widget_) | |
165 return; | |
166 // Full screen windows should hide the status area widget. | |
167 bool has_fullscreen = wm::HasFullscreenWindow(windows()); | |
168 bool widget_visible = status_area_widget_->IsVisible(); | |
169 if (has_fullscreen && widget_visible) | |
170 status_area_widget_->Hide(); | |
171 else if (!has_fullscreen && !widget_visible) | |
172 status_area_widget_->Show(); | |
173 } | |
174 | |
175 void CompactLayoutManager::AnimateSlideTo(int offset_x) { | |
176 GetDefaultContainerLayer()->GetAnimator()->RemoveObserver(this); | |
177 ui::ScopedLayerAnimationSettings settings( | |
178 GetDefaultContainerLayer()->GetAnimator()); | |
179 settings.AddObserver(this); | |
180 ui::Transform transform; | |
181 transform.ConcatTranslate(-offset_x, 0); | |
182 GetDefaultContainerLayer()->SetTransform(transform); // Will be animated! | |
183 } | |
184 | |
185 void CompactLayoutManager::LayoutWindows(aura::Window* skip) { | |
186 ShellDelegate* shell_delegate = ash::Shell::GetInstance()->delegate(); | |
187 const WindowList& windows_list = shell_delegate->GetCycleWindowList( | |
188 ShellDelegate::SOURCE_KEYBOARD, | |
189 ShellDelegate::ORDER_LINEAR); | |
190 int new_x = 0; | |
191 for (WindowListConstIter const_it = windows_list.begin(); | |
192 const_it != windows_list.end(); | |
193 ++const_it) { | |
194 if (*const_it != skip) { | |
195 gfx::Rect new_bounds((*const_it)->bounds()); | |
196 new_bounds.set_x(new_x); | |
197 SetChildBoundsDirect(*const_it, new_bounds); | |
198 (*const_it)->layer()->SetVisible(true); | |
199 new_x += (*const_it)->bounds().width(); | |
200 } | |
201 } | |
202 } | |
203 | |
204 void CompactLayoutManager::HideWindows() { | |
205 // If we do not know which one is the current window, or if the current | |
206 // window is not visible, do not attempt to hide the windows. | |
207 if (current_window_ == NULL) | |
208 return; | |
209 // Current window should be visible, if not it is an error and we shouldn't | |
210 // proceed. | |
211 if (!current_window_->layer()->visible()) | |
212 NOTREACHED() << "Current window is invisible"; | |
213 | |
214 ShellDelegate* shell_delegate = ash::Shell::GetInstance()->delegate(); | |
215 const WindowList& windows_list = shell_delegate->GetCycleWindowList( | |
216 ShellDelegate::SOURCE_KEYBOARD, | |
217 ShellDelegate::ORDER_LINEAR); | |
218 for (WindowListConstIter const_it = windows_list.begin(); | |
219 const_it != windows_list.end(); | |
220 ++const_it) { | |
221 if (*const_it != current_window_) | |
222 (*const_it)->layer()->SetVisible(false); | |
223 } | |
224 } | |
225 | |
226 aura::Window* CompactLayoutManager::FindReplacementWindow( | |
227 aura::Window* window) { | |
228 ShellDelegate* shell_delegate = ash::Shell::GetInstance()->delegate(); | |
229 const WindowList& windows_list = shell_delegate->GetCycleWindowList( | |
230 ShellDelegate::SOURCE_KEYBOARD, | |
231 ShellDelegate::ORDER_LINEAR); | |
232 WindowListConstIter const_it = std::find(windows_list.begin(), | |
233 windows_list.end(), | |
234 window); | |
235 if (windows_list.size() > 1 && const_it != windows_list.end()) { | |
236 do { | |
237 ++const_it; | |
238 if (const_it == windows_list.end()) | |
239 const_it = windows_list.begin(); | |
240 } while (*const_it != window && !(*const_it)->IsVisible()); | |
241 if (*const_it != window) | |
242 return *const_it; | |
243 } | |
244 return NULL; | |
245 } | |
246 | |
247 void CompactLayoutManager::SwitchToReplacementWindow() { | |
248 current_window_ = FindReplacementWindow(current_window_); | |
249 if (current_window_) { | |
250 wm::ActivateWindow(current_window_); | |
251 AnimateSlideTo(current_window_->bounds().x()); | |
252 } | |
253 } | |
254 | |
255 } // namespace internal | |
256 } // namespace ash | |
OLD | NEW |