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

Side by Side Diff: ash/common/wm/overview/scoped_transform_overview_window.cc

Issue 2734653002: chromeos: Move files in //ash/common to //ash (Closed)
Patch Set: fix a11y tests, fix docs Created 3 years, 9 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
OLDNEW
(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 &params);
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
OLDNEW
« no previous file with comments | « ash/common/wm/overview/scoped_transform_overview_window.h ('k') | ash/common/wm/overview/window_grid.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698