OLD | NEW |
| (Empty) |
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 "ash/common/wm/overview/scoped_transform_overview_window.h" | |
6 | |
7 #include <algorithm> | |
8 #include <vector> | |
9 | |
10 #include "ash/common/wm/overview/scoped_overview_animation_settings.h" | |
11 #include "ash/common/wm/overview/scoped_overview_animation_settings_factory.h" | |
12 #include "ash/common/wm/overview/window_selector_item.h" | |
13 #include "ash/common/wm/window_state.h" | |
14 #include "ash/common/wm_shell.h" | |
15 #include "ash/common/wm_window.h" | |
16 #include "ash/root_window_controller.h" | |
17 #include "base/macros.h" | |
18 #include "base/memory/ptr_util.h" | |
19 #include "base/single_thread_task_runner.h" | |
20 #include "base/threading/thread_task_runner_handle.h" | |
21 #include "ui/aura/client/aura_constants.h" | |
22 #include "ui/compositor/layer.h" | |
23 #include "ui/gfx/geometry/rect.h" | |
24 #include "ui/gfx/geometry/safe_integer_conversions.h" | |
25 #include "ui/gfx/transform_util.h" | |
26 #include "ui/views/widget/widget.h" | |
27 | |
28 using WmWindows = std::vector<ash::WmWindow*>; | |
29 | |
30 namespace ash { | |
31 | |
32 namespace { | |
33 | |
34 // When set to true by tests makes closing the widget synchronous. | |
35 bool immediate_close_for_tests = false; | |
36 | |
37 // Delay closing window to allow it to shrink and fade out. | |
38 const int kCloseWindowDelayInMilliseconds = 150; | |
39 | |
40 WmWindow* GetTransientRoot(WmWindow* window) { | |
41 while (window && window->GetTransientParent()) | |
42 window = window->GetTransientParent(); | |
43 return window; | |
44 } | |
45 | |
46 std::unique_ptr<ScopedOverviewAnimationSettings> | |
47 CreateScopedOverviewAnimationSettings(OverviewAnimationType animation_type, | |
48 WmWindow* window) { | |
49 return ScopedOverviewAnimationSettingsFactory::Get() | |
50 ->CreateOverviewAnimationSettings(animation_type, window); | |
51 } | |
52 | |
53 // An iterator class that traverses a WmWindow and all of its transient | |
54 // descendants. | |
55 class TransientDescendantIterator { | |
56 public: | |
57 // Creates an empty iterator. | |
58 TransientDescendantIterator(); | |
59 | |
60 // Copy constructor required for iterator purposes. | |
61 TransientDescendantIterator(const TransientDescendantIterator& other) = | |
62 default; | |
63 | |
64 // Iterates over |root_window| and all of its transient descendants. | |
65 // Note |root_window| must not have a transient parent. | |
66 explicit TransientDescendantIterator(WmWindow* root_window); | |
67 | |
68 // Prefix increment operator. This assumes there are more items (i.e. | |
69 // *this != TransientDescendantIterator()). | |
70 const TransientDescendantIterator& operator++(); | |
71 | |
72 // Comparison for STL-based loops. | |
73 bool operator!=(const TransientDescendantIterator& other) const; | |
74 | |
75 // Dereference operator for STL-compatible iterators. | |
76 WmWindow* operator*() const; | |
77 | |
78 private: | |
79 // Explicit assignment operator defined because an explicit copy constructor | |
80 // is needed and therefore the DISALLOW_COPY_AND_ASSIGN macro cannot be used. | |
81 TransientDescendantIterator& operator=( | |
82 const TransientDescendantIterator& other) = default; | |
83 | |
84 // The current window that |this| refers to. A null |current_window_| denotes | |
85 // an empty iterator and is used as the last possible value in the traversal. | |
86 WmWindow* current_window_; | |
87 }; | |
88 | |
89 // Provides a virtual container implementing begin() and end() for a sequence of | |
90 // TransientDescendantIterators. This can be used in range-based for loops. | |
91 class TransientDescendantIteratorRange { | |
92 public: | |
93 explicit TransientDescendantIteratorRange( | |
94 const TransientDescendantIterator& begin); | |
95 | |
96 // Copy constructor required for iterator purposes. | |
97 TransientDescendantIteratorRange( | |
98 const TransientDescendantIteratorRange& other) = default; | |
99 | |
100 const TransientDescendantIterator& begin() const { return begin_; } | |
101 const TransientDescendantIterator& end() const { return end_; } | |
102 | |
103 private: | |
104 // Explicit assignment operator defined because an explicit copy constructor | |
105 // is needed and therefore the DISALLOW_COPY_AND_ASSIGN macro cannot be used. | |
106 TransientDescendantIteratorRange& operator=( | |
107 const TransientDescendantIteratorRange& other) = default; | |
108 | |
109 TransientDescendantIterator begin_; | |
110 TransientDescendantIterator end_; | |
111 }; | |
112 | |
113 TransientDescendantIterator::TransientDescendantIterator() | |
114 : current_window_(nullptr) {} | |
115 | |
116 TransientDescendantIterator::TransientDescendantIterator(WmWindow* root_window) | |
117 : current_window_(root_window) { | |
118 DCHECK(!root_window->GetTransientParent()); | |
119 } | |
120 | |
121 // Performs a pre-order traversal of the transient descendants. | |
122 const TransientDescendantIterator& TransientDescendantIterator::operator++() { | |
123 DCHECK(current_window_); | |
124 | |
125 const WmWindows transient_children = current_window_->GetTransientChildren(); | |
126 | |
127 if (!transient_children.empty()) { | |
128 current_window_ = transient_children.front(); | |
129 } else { | |
130 while (current_window_) { | |
131 WmWindow* parent = current_window_->GetTransientParent(); | |
132 if (!parent) { | |
133 current_window_ = nullptr; | |
134 break; | |
135 } | |
136 const WmWindows transient_siblings = parent->GetTransientChildren(); | |
137 auto iter = std::find(transient_siblings.begin(), | |
138 transient_siblings.end(), current_window_); | |
139 ++iter; | |
140 if (iter != transient_siblings.end()) { | |
141 current_window_ = *iter; | |
142 break; | |
143 } | |
144 current_window_ = current_window_->GetTransientParent(); | |
145 } | |
146 } | |
147 return *this; | |
148 } | |
149 | |
150 bool TransientDescendantIterator::operator!=( | |
151 const TransientDescendantIterator& other) const { | |
152 return current_window_ != other.current_window_; | |
153 } | |
154 | |
155 WmWindow* TransientDescendantIterator::operator*() const { | |
156 return current_window_; | |
157 } | |
158 | |
159 TransientDescendantIteratorRange::TransientDescendantIteratorRange( | |
160 const TransientDescendantIterator& begin) | |
161 : begin_(begin) {} | |
162 | |
163 TransientDescendantIteratorRange GetTransientTreeIterator(WmWindow* window) { | |
164 return TransientDescendantIteratorRange( | |
165 TransientDescendantIterator(GetTransientRoot(window))); | |
166 } | |
167 | |
168 } // namespace | |
169 | |
170 ScopedTransformOverviewWindow::ScopedTransformOverviewWindow(WmWindow* window) | |
171 : window_(window), | |
172 determined_original_window_shape_(false), | |
173 ignored_by_shelf_(window->GetWindowState()->ignored_by_shelf()), | |
174 overview_started_(false), | |
175 original_transform_(window->GetTargetTransform()), | |
176 original_opacity_(window->GetTargetOpacity()), | |
177 weak_ptr_factory_(this) {} | |
178 | |
179 ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() {} | |
180 | |
181 void ScopedTransformOverviewWindow::RestoreWindow() { | |
182 ShowHeader(); | |
183 if (minimized_widget_) { | |
184 // TODO(oshima): Use unminimize animation instead of hiding animation. | |
185 minimized_widget_->CloseNow(); | |
186 minimized_widget_.reset(); | |
187 return; | |
188 } | |
189 ScopedAnimationSettings animation_settings_list; | |
190 BeginScopedAnimation(OverviewAnimationType::OVERVIEW_ANIMATION_RESTORE_WINDOW, | |
191 &animation_settings_list); | |
192 SetTransform(window()->GetRootWindow(), original_transform_); | |
193 std::unique_ptr<ScopedOverviewAnimationSettings> animation_settings = | |
194 CreateScopedOverviewAnimationSettings( | |
195 OverviewAnimationType::OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS, | |
196 window_); | |
197 window_->GetWindowState()->set_ignored_by_shelf(ignored_by_shelf_); | |
198 SetOpacity(original_opacity_); | |
199 } | |
200 | |
201 void ScopedTransformOverviewWindow::BeginScopedAnimation( | |
202 OverviewAnimationType animation_type, | |
203 ScopedAnimationSettings* animation_settings) { | |
204 for (auto* window : GetTransientTreeIterator(GetOverviewWindow())) { | |
205 animation_settings->push_back( | |
206 CreateScopedOverviewAnimationSettings(animation_type, window)); | |
207 } | |
208 } | |
209 | |
210 bool ScopedTransformOverviewWindow::Contains(const WmWindow* target) const { | |
211 for (auto* window : GetTransientTreeIterator(window_)) { | |
212 if (window->Contains(target)) | |
213 return true; | |
214 } | |
215 WmWindow* mirror = GetOverviewWindowForMinimizedState(); | |
216 return mirror && mirror->Contains(target); | |
217 } | |
218 | |
219 gfx::Rect ScopedTransformOverviewWindow::GetTargetBoundsInScreen() const { | |
220 gfx::Rect bounds; | |
221 WmWindow* overview_window = GetOverviewWindow(); | |
222 for (auto* window : GetTransientTreeIterator(overview_window)) { | |
223 // Ignore other window types when computing bounding box of window | |
224 // selector target item. | |
225 if (window != overview_window && | |
226 window->GetType() != ui::wm::WINDOW_TYPE_NORMAL && | |
227 window->GetType() != ui::wm::WINDOW_TYPE_PANEL) { | |
228 continue; | |
229 } | |
230 bounds.Union( | |
231 window->GetParent()->ConvertRectToScreen(window->GetTargetBounds())); | |
232 } | |
233 return bounds; | |
234 } | |
235 | |
236 gfx::Rect ScopedTransformOverviewWindow::GetTransformedBounds() const { | |
237 const int top_inset = GetTopInset(); | |
238 gfx::Rect bounds; | |
239 WmWindow* overview_window = GetOverviewWindow(); | |
240 for (auto* window : GetTransientTreeIterator(overview_window)) { | |
241 // Ignore other window types when computing bounding box of window | |
242 // selector target item. | |
243 if (window != overview_window && | |
244 (window->GetType() != ui::wm::WINDOW_TYPE_NORMAL && | |
245 window->GetType() != ui::wm::WINDOW_TYPE_PANEL)) { | |
246 continue; | |
247 } | |
248 gfx::RectF window_bounds(window->GetTargetBounds()); | |
249 gfx::Transform new_transform = | |
250 TransformAboutPivot(gfx::Point(window_bounds.x(), window_bounds.y()), | |
251 window->GetTargetTransform()); | |
252 new_transform.TransformRect(&window_bounds); | |
253 | |
254 // The preview title is shown above the preview window. Hide the window | |
255 // header for apps or browser windows with no tabs (web apps) to avoid | |
256 // showing both the window header and the preview title. | |
257 if (top_inset > 0) { | |
258 gfx::RectF header_bounds(window_bounds); | |
259 header_bounds.set_height(top_inset); | |
260 new_transform.TransformRect(&header_bounds); | |
261 window_bounds.Inset(0, gfx::ToCeiledInt(header_bounds.height()), 0, 0); | |
262 } | |
263 bounds.Union(window->GetParent()->ConvertRectToScreen( | |
264 ToEnclosingRect(window_bounds))); | |
265 } | |
266 return bounds; | |
267 } | |
268 | |
269 SkColor ScopedTransformOverviewWindow::GetTopColor() const { | |
270 for (auto* window : GetTransientTreeIterator(window_)) { | |
271 // If there are regular windows in the transient ancestor tree, all those | |
272 // windows are shown in the same overview item and the header is not masked. | |
273 if (window != window_ && (window->GetType() == ui::wm::WINDOW_TYPE_NORMAL || | |
274 window->GetType() == ui::wm::WINDOW_TYPE_PANEL)) { | |
275 return SK_ColorTRANSPARENT; | |
276 } | |
277 } | |
278 return window_->aura_window()->GetProperty(aura::client::kTopViewColor); | |
279 } | |
280 | |
281 int ScopedTransformOverviewWindow::GetTopInset() const { | |
282 // Mirror window doesn't have insets. | |
283 if (minimized_widget_) | |
284 return 0; | |
285 for (auto* window : GetTransientTreeIterator(window_)) { | |
286 // If there are regular windows in the transient ancestor tree, all those | |
287 // windows are shown in the same overview item and the header is not masked. | |
288 if (window != window_ && (window->GetType() == ui::wm::WINDOW_TYPE_NORMAL || | |
289 window->GetType() == ui::wm::WINDOW_TYPE_PANEL)) { | |
290 return 0; | |
291 } | |
292 } | |
293 return window_->aura_window()->GetProperty(aura::client::kTopViewInset); | |
294 } | |
295 | |
296 void ScopedTransformOverviewWindow::OnWindowDestroyed() { | |
297 window_ = nullptr; | |
298 } | |
299 | |
300 float ScopedTransformOverviewWindow::GetItemScale(const gfx::Size& source, | |
301 const gfx::Size& target, | |
302 int top_view_inset, | |
303 int title_height) { | |
304 return std::min(2.0f, static_cast<float>((target.height() - title_height)) / | |
305 (source.height() - top_view_inset)); | |
306 } | |
307 | |
308 gfx::Rect ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio( | |
309 const gfx::Rect& rect, | |
310 const gfx::Rect& bounds, | |
311 int top_view_inset, | |
312 int title_height) { | |
313 DCHECK(!rect.IsEmpty()); | |
314 DCHECK_LE(top_view_inset, rect.height()); | |
315 const float scale = | |
316 GetItemScale(rect.size(), bounds.size(), top_view_inset, title_height); | |
317 const int horizontal_offset = gfx::ToFlooredInt( | |
318 0.5 * (bounds.width() - gfx::ToFlooredInt(scale * rect.width()))); | |
319 const int width = bounds.width() - 2 * horizontal_offset; | |
320 const int vertical_offset = | |
321 title_height - gfx::ToCeiledInt(scale * top_view_inset); | |
322 const int height = std::min(gfx::ToCeiledInt(scale * rect.height()), | |
323 bounds.height() - vertical_offset); | |
324 return gfx::Rect(bounds.x() + horizontal_offset, bounds.y() + vertical_offset, | |
325 width, height); | |
326 } | |
327 | |
328 gfx::Transform ScopedTransformOverviewWindow::GetTransformForRect( | |
329 const gfx::Rect& src_rect, | |
330 const gfx::Rect& dst_rect) { | |
331 DCHECK(!src_rect.IsEmpty()); | |
332 gfx::Transform transform; | |
333 transform.Translate(dst_rect.x() - src_rect.x(), dst_rect.y() - src_rect.y()); | |
334 transform.Scale(static_cast<float>(dst_rect.width()) / src_rect.width(), | |
335 static_cast<float>(dst_rect.height()) / src_rect.height()); | |
336 return transform; | |
337 } | |
338 | |
339 void ScopedTransformOverviewWindow::SetTransform( | |
340 WmWindow* root_window, | |
341 const gfx::Transform& transform) { | |
342 DCHECK(overview_started_); | |
343 | |
344 if (&transform != &original_transform_ && | |
345 !determined_original_window_shape_) { | |
346 determined_original_window_shape_ = true; | |
347 SkRegion* window_shape = window()->GetLayer()->alpha_shape(); | |
348 if (!original_window_shape_ && window_shape) | |
349 original_window_shape_.reset(new SkRegion(*window_shape)); | |
350 } | |
351 | |
352 gfx::Point target_origin(GetTargetBoundsInScreen().origin()); | |
353 for (auto* window : GetTransientTreeIterator(GetOverviewWindow())) { | |
354 WmWindow* parent_window = window->GetParent(); | |
355 gfx::Point original_origin = | |
356 parent_window->ConvertRectToScreen(window->GetTargetBounds()).origin(); | |
357 gfx::Transform new_transform = | |
358 TransformAboutPivot(gfx::Point(target_origin.x() - original_origin.x(), | |
359 target_origin.y() - original_origin.y()), | |
360 transform); | |
361 window->SetTransform(new_transform); | |
362 } | |
363 } | |
364 | |
365 void ScopedTransformOverviewWindow::SetOpacity(float opacity) { | |
366 for (auto* window : GetTransientTreeIterator(GetOverviewWindow())) { | |
367 window->SetOpacity(opacity); | |
368 } | |
369 } | |
370 | |
371 void ScopedTransformOverviewWindow::HideHeader() { | |
372 // Mirrored Window does not have a header. | |
373 if (minimized_widget_) | |
374 return; | |
375 gfx::Rect bounds(GetTargetBoundsInScreen().size()); | |
376 const int inset = GetTopInset(); | |
377 if (inset > 0) { | |
378 // Use alpha shape to hide the window header. | |
379 bounds.Inset(0, inset, 0, 0); | |
380 std::unique_ptr<SkRegion> region(new SkRegion); | |
381 region->setRect(RectToSkIRect(bounds)); | |
382 if (original_window_shape_) | |
383 region->op(*original_window_shape_, SkRegion::kIntersect_Op); | |
384 WmWindow* window = GetOverviewWindow(); | |
385 window->GetLayer()->SetAlphaShape(std::move(region)); | |
386 window->SetMasksToBounds(true); | |
387 } | |
388 } | |
389 | |
390 void ScopedTransformOverviewWindow::ShowHeader() { | |
391 ui::Layer* layer = window()->GetLayer(); | |
392 if (original_window_shape_) { | |
393 layer->SetAlphaShape( | |
394 base::MakeUnique<SkRegion>(*original_window_shape_.get())); | |
395 } else { | |
396 layer->SetAlphaShape(nullptr); | |
397 } | |
398 window()->SetMasksToBounds(false); | |
399 } | |
400 | |
401 void ScopedTransformOverviewWindow::UpdateMirrorWindowForMinimizedState() { | |
402 // TODO(oshima): Disable animation. | |
403 if (window_->GetShowState() == ui::SHOW_STATE_MINIMIZED) { | |
404 if (!minimized_widget_) | |
405 CreateMirrorWindowForMinimizedState(); | |
406 } else { | |
407 minimized_widget_->CloseNow(); | |
408 minimized_widget_.reset(); | |
409 } | |
410 } | |
411 | |
412 void ScopedTransformOverviewWindow::Close() { | |
413 if (immediate_close_for_tests) { | |
414 CloseWidget(); | |
415 return; | |
416 } | |
417 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
418 FROM_HERE, base::Bind(&ScopedTransformOverviewWindow::CloseWidget, | |
419 weak_ptr_factory_.GetWeakPtr()), | |
420 base::TimeDelta::FromMilliseconds(kCloseWindowDelayInMilliseconds)); | |
421 } | |
422 | |
423 void ScopedTransformOverviewWindow::PrepareForOverview() { | |
424 DCHECK(!overview_started_); | |
425 overview_started_ = true; | |
426 window_->GetWindowState()->set_ignored_by_shelf(true); | |
427 if (window_->GetShowState() == ui::SHOW_STATE_MINIMIZED) | |
428 CreateMirrorWindowForMinimizedState(); | |
429 } | |
430 | |
431 void ScopedTransformOverviewWindow::CloseWidget() { | |
432 WmWindow* parent_window = GetTransientRoot(window_); | |
433 if (parent_window) | |
434 parent_window->CloseWidget(); | |
435 } | |
436 | |
437 // static | |
438 void ScopedTransformOverviewWindow::SetImmediateCloseForTests() { | |
439 immediate_close_for_tests = true; | |
440 } | |
441 | |
442 WmWindow* ScopedTransformOverviewWindow::GetOverviewWindow() const { | |
443 if (minimized_widget_) | |
444 return GetOverviewWindowForMinimizedState(); | |
445 return window_; | |
446 } | |
447 | |
448 void ScopedTransformOverviewWindow::EnsureVisible() { | |
449 original_opacity_ = 1.f; | |
450 } | |
451 | |
452 void ScopedTransformOverviewWindow::OnGestureEvent(ui::GestureEvent* event) { | |
453 if (event->type() == ui::ET_GESTURE_TAP) { | |
454 EnsureVisible(); | |
455 window_->Show(); | |
456 window_->Activate(); | |
457 } | |
458 } | |
459 | |
460 void ScopedTransformOverviewWindow::OnMouseEvent(ui::MouseEvent* event) { | |
461 if (event->type() == ui::ET_MOUSE_PRESSED && event->IsOnlyLeftMouseButton()) { | |
462 EnsureVisible(); | |
463 window_->Show(); | |
464 window_->Activate(); | |
465 } | |
466 } | |
467 | |
468 WmWindow* ScopedTransformOverviewWindow::GetOverviewWindowForMinimizedState() | |
469 const { | |
470 return minimized_widget_ ? WmWindow::Get(minimized_widget_->GetNativeWindow()) | |
471 : nullptr; | |
472 } | |
473 | |
474 void ScopedTransformOverviewWindow::CreateMirrorWindowForMinimizedState() { | |
475 DCHECK(!minimized_widget_.get()); | |
476 views::Widget::InitParams params; | |
477 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; | |
478 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
479 params.visible_on_all_workspaces = true; | |
480 params.name = "OverviewModeMinimized"; | |
481 params.activatable = views::Widget::InitParams::Activatable::ACTIVATABLE_NO; | |
482 params.accept_events = true; | |
483 minimized_widget_.reset(new views::Widget); | |
484 window_->GetRootWindow() | |
485 ->GetRootWindowController() | |
486 ->ConfigureWidgetInitParamsForContainer( | |
487 minimized_widget_.get(), window_->GetParent()->GetShellWindowId(), | |
488 ¶ms); | |
489 minimized_widget_->set_focus_on_creation(false); | |
490 minimized_widget_->Init(params); | |
491 | |
492 views::View* mirror_view = window_->CreateViewWithRecreatedLayers().release(); | |
493 mirror_view->SetVisible(true); | |
494 mirror_view->SetTargetHandler(this); | |
495 minimized_widget_->SetContentsView(mirror_view); | |
496 gfx::Rect bounds(window_->GetBoundsInScreen()); | |
497 gfx::Size preferred = mirror_view->GetPreferredSize(); | |
498 // In unit tests, the content view can have empty size. | |
499 if (!preferred.IsEmpty()) { | |
500 int inset = bounds.height() - preferred.height(); | |
501 bounds.Inset(0, 0, 0, inset); | |
502 } | |
503 minimized_widget_->SetBounds(bounds); | |
504 minimized_widget_->Show(); | |
505 } | |
506 | |
507 } // namespace ash | |
OLD | NEW |