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/wm/overview/scoped_overview_transform_window.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "ash/public/cpp/ash_features.h"
11 #include "ash/public/cpp/window_properties.h"
12 #include "ash/shell.h"
13 #include "ash/wm/overview/delayed_animation_observer_impl.h"
14 #include "ash/wm/overview/overview_constants.h"
15 #include "ash/wm/overview/overview_controller.h"
16 #include "ash/wm/overview/overview_grid.h"
17 #include "ash/wm/overview/overview_item.h"
18 #include "ash/wm/overview/overview_utils.h"
19 #include "ash/wm/overview/scoped_overview_animation_settings.h"
20 #include "ash/wm/splitview/split_view_controller.h"
21 #include "ash/wm/splitview/split_view_utils.h"
22 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
23 #include "ash/wm/window_state.h"
24 #include "ash/wm/window_transient_descendant_iterator.h"
25 #include "ash/wm/window_util.h"
26 #include "base/bind.h"
27 #include "base/macros.h"
28 #include "base/single_thread_task_runner.h"
29 #include "base/threading/thread_task_runner_handle.h"
30 #include "chromeos/ui/base/window_properties.h"
31 #include "ui/aura/client/aura_constants.h"
32 #include "ui/aura/client/transient_window_client.h"
33 #include "ui/aura/scoped_window_event_targeting_blocker.h"
34 #include "ui/aura/window.h"
35 #include "ui/compositor/layer.h"
36 #include "ui/compositor/layer_animation_observer.h"
37 #include "ui/compositor/layer_animator.h"
38 #include "ui/compositor/layer_observer.h"
39 #include "ui/gfx/geometry/insets.h"
40 #include "ui/gfx/geometry/rect_conversions.h"
41 #include "ui/gfx/geometry/vector2d_f.h"
42 #include "ui/gfx/transform_util.h"
43 #include "ui/views/layout/layout_provider.h"
44 #include "ui/views/widget/widget.h"
45 #include "ui/wm/core/coordinate_conversion.h"
46 #include "ui/wm/core/shadow_controller.h"
47 #include "ui/wm/core/window_animations.h"
48 #include "ui/wm/core/window_util.h"
49
50 namespace ash {
51
52 namespace {
53
54 // When set to true by tests makes closing the widget synchronous.
55 bool immediate_close_for_tests = false;
56
57 // Delay closing window to allow it to shrink and fade out.
58 constexpr int kCloseWindowDelayInMilliseconds = 150;
59
60 // Layer animation observer that is attached to a clip animation. Removes the
61 // clip and then self destructs after the animation is finished.
62 class RemoveClipObserver : public ui::ImplicitAnimationObserver,
63 public aura::WindowObserver {
64 public:
RemoveClipObserver(aura::Window * window)65 explicit RemoveClipObserver(aura::Window* window) : window_(window) {
66 auto* animator = window_->layer()->GetAnimator();
67 DCHECK(window_->layer()->GetAnimator()->is_animating());
68
69 const auto original_transition_duration = animator->GetTransitionDuration();
70 // Don't let |settings| overwrite the existing animation's duration.
71 ui::ScopedLayerAnimationSettings settings{animator};
72 settings.SetTransitionDuration(original_transition_duration);
73 settings.AddObserver(this);
74 window_->AddObserver(this);
75 }
76 RemoveClipObserver(const RemoveClipObserver&) = delete;
77 RemoveClipObserver& operator=(const RemoveClipObserver&) = delete;
~RemoveClipObserver()78 ~RemoveClipObserver() override {
79 StopObservingImplicitAnimations();
80 window_->RemoveObserver(this);
81 window_ = nullptr;
82 }
83
84 private:
85 // ui::ImplicitAnimationObserver:
OnImplicitAnimationsCompleted()86 void OnImplicitAnimationsCompleted() override {
87 window_->layer()->SetClipRect(gfx::Rect());
88 delete this;
89 }
90
91 // aura::WindowObserver:
OnWindowDestroying(aura::Window * window)92 void OnWindowDestroying(aura::Window* window) override {
93 DCHECK_EQ(window_, window);
94 delete this;
95 }
96
97 // Guaranteed to be not null for the duration of |this|.
98 aura::Window* window_;
99 };
100
101 // Clips |window| to |clip_rect|. If |clip_rect| is empty and there is an
102 // animation, animate first to a clip the size of |window|, then remove the
103 // clip. Otherwise the clip animation will clip away all the contents while it
104 // animates towards an empty clip rect (but not yet empty) before reshowing it
105 // once the clip rect is really empty. An empty clip rect means a request to
106 // clip nothing.
ClipWindow(aura::Window * window,const gfx::Rect & clip_rect)107 void ClipWindow(aura::Window* window, const gfx::Rect& clip_rect) {
108 DCHECK(window);
109
110 ui::LayerAnimator* animator = window->layer()->GetAnimator();
111 const gfx::Rect target_clip_rect = animator->GetTargetClipRect();
112 if (target_clip_rect == clip_rect)
113 return;
114
115 gfx::Rect new_clip_rect = clip_rect;
116 if (new_clip_rect.IsEmpty() && animator->is_animating()) {
117 // Animate to a clip the size of |window|. Create a self deleting object
118 // which removes the clip when the animation is finished.
119 new_clip_rect = gfx::Rect(window->bounds().size());
120 new RemoveClipObserver(window);
121 }
122
123 window->layer()->SetClipRect(new_clip_rect);
124 }
125
126 } // namespace
127
128 class ScopedOverviewTransformWindow::LayerCachingAndFilteringObserver
129 : public ui::LayerObserver {
130 public:
LayerCachingAndFilteringObserver(ui::Layer * layer)131 explicit LayerCachingAndFilteringObserver(ui::Layer* layer) : layer_(layer) {
132 layer_->AddObserver(this);
133 layer_->AddCacheRenderSurfaceRequest();
134 layer_->AddTrilinearFilteringRequest();
135 }
~LayerCachingAndFilteringObserver()136 ~LayerCachingAndFilteringObserver() override {
137 if (layer_) {
138 layer_->RemoveTrilinearFilteringRequest();
139 layer_->RemoveCacheRenderSurfaceRequest();
140 layer_->RemoveObserver(this);
141 }
142 }
143
144 // ui::LayerObserver overrides:
LayerDestroyed(ui::Layer * layer)145 void LayerDestroyed(ui::Layer* layer) override {
146 layer_->RemoveObserver(this);
147 layer_ = nullptr;
148 }
149
150 private:
151 ui::Layer* layer_;
152
153 DISALLOW_COPY_AND_ASSIGN(LayerCachingAndFilteringObserver);
154 };
155
ScopedOverviewTransformWindow(OverviewItem * overview_item,aura::Window * window)156 ScopedOverviewTransformWindow::ScopedOverviewTransformWindow(
157 OverviewItem* overview_item,
158 aura::Window* window)
159 : overview_item_(overview_item),
160 window_(window),
161 original_opacity_(window->layer()->GetTargetOpacity()),
162 original_clip_rect_(window_->layer()->clip_rect()) {
163 type_ = GetWindowDimensionsType(window->bounds().size());
164
165 std::vector<aura::Window*> transient_children_to_hide;
166 for (auto* transient : GetTransientTreeIterator(window)) {
167 event_targeting_blocker_map_[transient] =
168 std::make_unique<aura::ScopedWindowEventTargetingBlocker>(transient);
169
170 transient->SetProperty(chromeos::kIsShowingInOverviewKey, true);
171
172 // Add this as |aura::WindowObserver| for observing |kHideInOverviewKey|
173 // property changes.
174 window_observer_.Add(transient);
175
176 // Hide transient children which have been specified to be hidden in
177 // overview mode.
178 if (transient != window && transient->GetProperty(kHideInOverviewKey))
179 transient_children_to_hide.push_back(transient);
180 }
181
182 if (!transient_children_to_hide.empty())
183 AddHiddenTransientWindows(std::move(transient_children_to_hide));
184
185 aura::client::GetTransientWindowClient()->AddObserver(this);
186
187 // Tablet mode grid layout has scrolling, so all windows must be stacked under
188 // the current split view window if they share the same parent so that during
189 // scrolls, they get scrolled underneath the split view window. The window
190 // will be returned to its proper z-order on exiting overview if it is
191 // activated.
192 // TODO(sammiequon): This does not handle the case if either the snapped
193 // window or this window is an always on top window.
194 auto* split_view_controller =
195 SplitViewController::Get(Shell::GetPrimaryRootWindow());
196 if (ShouldUseTabletModeGridLayout() &&
197 split_view_controller->InSplitViewMode()) {
198 aura::Window* snapped_window =
199 split_view_controller->GetDefaultSnappedWindow();
200 if (window->parent() == snapped_window->parent()) {
201 // Helper to get the z order of a window in its parent.
202 auto get_z_order = [](aura::Window* window) -> size_t {
203 for (size_t i = 0u; i < window->parent()->children().size(); ++i) {
204 if (window == window->parent()->children()[i])
205 return i;
206 }
207 NOTREACHED();
208 return 0u;
209 };
210
211 if (get_z_order(window_) > get_z_order(snapped_window))
212 window_->parent()->StackChildBelow(window_, snapped_window);
213 }
214 }
215 }
216
~ScopedOverviewTransformWindow()217 ScopedOverviewTransformWindow::~ScopedOverviewTransformWindow() {
218 // Reset clipping in the case RestoreWindow() is not called, such as when
219 // |this| is dragged to another display. This is a no-op if SetClipping() was
220 // called in RestoreWindow().
221 // See crbug.com/1140639.
222 SetClipping({ClippingType::kExit, gfx::SizeF()});
223
224 for (auto* transient : GetTransientTreeIterator(window_)) {
225 transient->ClearProperty(chromeos::kIsShowingInOverviewKey);
226 DCHECK(event_targeting_blocker_map_.contains(transient));
227 event_targeting_blocker_map_.erase(transient);
228 }
229
230 UpdateRoundedCorners(/*show=*/false);
231 aura::client::GetTransientWindowClient()->RemoveObserver(this);
232
233 window_observer_.RemoveAll();
234 }
235
236 // static
GetItemScale(const gfx::SizeF & source,const gfx::SizeF & target,int top_view_inset,int title_height)237 float ScopedOverviewTransformWindow::GetItemScale(const gfx::SizeF& source,
238 const gfx::SizeF& target,
239 int top_view_inset,
240 int title_height) {
241 return std::min(2.0f, (target.height() - title_height) /
242 (source.height() - top_view_inset));
243 }
244
245 // static
246 OverviewGridWindowFillMode
GetWindowDimensionsType(const gfx::Size & size)247 ScopedOverviewTransformWindow::GetWindowDimensionsType(const gfx::Size& size) {
248 if (size.width() > size.height() * kExtremeWindowRatioThreshold)
249 return OverviewGridWindowFillMode::kLetterBoxed;
250
251 if (size.height() > size.width() * kExtremeWindowRatioThreshold)
252 return OverviewGridWindowFillMode::kPillarBoxed;
253
254 return OverviewGridWindowFillMode::kNormal;
255 }
256
RestoreWindow(bool reset_transform)257 void ScopedOverviewTransformWindow::RestoreWindow(bool reset_transform) {
258 // Shadow controller may be null on shutdown.
259 if (Shell::Get()->shadow_controller())
260 Shell::Get()->shadow_controller()->UpdateShadowForWindow(window_);
261
262 if (IsMinimized()) {
263 // Minimized windows may have had their transforms altered by swiping up
264 // from the shelf.
265 SetTransform(window_, gfx::Transform());
266 return;
267 }
268
269 if (reset_transform) {
270 ScopedAnimationSettings animation_settings_list;
271 BeginScopedAnimation(overview_item_->GetExitTransformAnimationType(),
272 &animation_settings_list);
273 for (auto& settings : animation_settings_list) {
274 auto exit_observer = std::make_unique<ExitAnimationObserver>();
275 settings->AddObserver(exit_observer.get());
276 if (window_->layer()->GetAnimator() == settings->GetAnimator())
277 settings->AddObserver(new WindowTransformAnimationObserver(window_));
278 Shell::Get()->overview_controller()->AddExitAnimationObserver(
279 std::move(exit_observer));
280 }
281
282 // Use identity transform directly to reset window's transform when exiting
283 // overview.
284 SetTransform(window_, gfx::Transform());
285 // Add requests to cache render surface and perform trilinear filtering for
286 // the exit animation of overview mode. The requests will be removed when
287 // the exit animation finishes.
288 if (features::IsTrilinearFilteringEnabled()) {
289 for (auto& settings : animation_settings_list) {
290 settings->CacheRenderSurface();
291 settings->TrilinearFiltering();
292 }
293 }
294 }
295
296 ScopedOverviewAnimationSettings animation_settings(
297 overview_item_->GetExitOverviewAnimationType(), window_);
298 SetOpacity(original_opacity_);
299 SetClipping({ClippingType::kExit, gfx::SizeF()});
300 }
301
BeginScopedAnimation(OverviewAnimationType animation_type,ScopedAnimationSettings * animation_settings)302 void ScopedOverviewTransformWindow::BeginScopedAnimation(
303 OverviewAnimationType animation_type,
304 ScopedAnimationSettings* animation_settings) {
305 if (animation_type == OVERVIEW_ANIMATION_NONE)
306 return;
307
308 for (auto* window : window_util::GetVisibleTransientTreeIterator(window_)) {
309 auto settings = std::make_unique<ScopedOverviewAnimationSettings>(
310 animation_type, window);
311 settings->DeferPaint();
312
313 // Create an EnterAnimationObserver if this is an enter overview layout
314 // animation.
315 if (animation_type == OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_ON_ENTER) {
316 auto enter_observer = std::make_unique<EnterAnimationObserver>();
317 settings->AddObserver(enter_observer.get());
318 Shell::Get()->overview_controller()->AddEnterAnimationObserver(
319 std::move(enter_observer));
320 }
321
322 animation_settings->push_back(std::move(settings));
323 }
324 }
325
Contains(const aura::Window * target) const326 bool ScopedOverviewTransformWindow::Contains(const aura::Window* target) const {
327 for (auto* window : GetTransientTreeIterator(window_)) {
328 if (window->Contains(target))
329 return true;
330 }
331
332 if (!IsMinimized())
333 return false;
334
335 // A minimized window's item_widget_ may have already been destroyed.
336 const auto* item_widget = overview_item_->item_widget();
337 if (!item_widget)
338 return false;
339
340 return item_widget->GetNativeWindow()->Contains(target);
341 }
342
GetTransformedBounds() const343 gfx::RectF ScopedOverviewTransformWindow::GetTransformedBounds() const {
344 return window_util::GetTransformedBounds(window_, GetTopInset());
345 }
346
GetTopInset() const347 int ScopedOverviewTransformWindow::GetTopInset() const {
348 // Mirror window doesn't have insets.
349 if (IsMinimized())
350 return 0;
351 for (auto* window : window_util::GetVisibleTransientTreeIterator(window_)) {
352 // If there are regular windows in the transient ancestor tree, all those
353 // windows are shown in the same overview item and the header is not masked.
354 if (window != window_ &&
355 window->type() == aura::client::WINDOW_TYPE_NORMAL) {
356 return 0;
357 }
358 }
359 return window_->GetProperty(aura::client::kTopViewInset);
360 }
361
SetOpacity(float opacity)362 void ScopedOverviewTransformWindow::SetOpacity(float opacity) {
363 for (auto* window :
364 window_util::GetVisibleTransientTreeIterator(GetOverviewWindow()))
365 window->layer()->SetOpacity(opacity);
366 }
367
SetClipping(const ClippingData & clipping_data)368 void ScopedOverviewTransformWindow::SetClipping(
369 const ClippingData& clipping_data) {
370 gfx::SizeF size;
371 switch (clipping_data.first) {
372 case ClippingType::kEnter:
373 size = gfx::SizeF(window_->bounds().size());
374 break;
375 case ClippingType::kExit:
376 ClipWindow(window_, original_clip_rect_);
377 return;
378 case ClippingType::kCustom:
379 size = clipping_data.second;
380 if (size.IsEmpty()) {
381 // Given size is empty so we fallback to the overview clipping, which is
382 // the size of the window. The header will be accounted for below.
383 size = gfx::SizeF(window_->bounds().size());
384 } else {
385 // Transform affects the clip rect, so take that into account.
386 const gfx::Vector2dF scale =
387 window_->layer()->GetTargetTransform().Scale2d();
388 size.Scale(1 / scale.x(), 1 / scale.y());
389 }
390 break;
391 }
392
393 if (size.IsEmpty())
394 return;
395
396 gfx::Rect clip_rect(gfx::ToRoundedSize(size));
397 // We add 1 to the top_inset, because in some cases, the header is not
398 // clipped fully due to what seems to be a rounding error.
399 // TODO(afakhry|sammiequon): Investigate a proper fix for this.
400 const int top_inset = GetTopInset();
401 if (top_inset > 0)
402 clip_rect.Inset(gfx::Insets(top_inset + 1, 0, 0, 0));
403 ClipWindow(window_, clip_rect);
404 }
405
ShrinkRectToFitPreservingAspectRatio(const gfx::RectF & rect,const gfx::RectF & bounds,int top_view_inset,int title_height)406 gfx::RectF ScopedOverviewTransformWindow::ShrinkRectToFitPreservingAspectRatio(
407 const gfx::RectF& rect,
408 const gfx::RectF& bounds,
409 int top_view_inset,
410 int title_height) {
411 DCHECK(!rect.IsEmpty());
412 DCHECK_LE(top_view_inset, rect.height());
413 const float scale =
414 GetItemScale(rect.size(), bounds.size(), top_view_inset, title_height);
415 const float horizontal_offset = 0.5 * (bounds.width() - scale * rect.width());
416 const float width = bounds.width() - 2.f * horizontal_offset;
417 const float vertical_offset = title_height - scale * top_view_inset;
418 const float height =
419 std::min(scale * rect.height(), bounds.height() - vertical_offset);
420 gfx::RectF new_bounds(bounds.x() + horizontal_offset,
421 bounds.y() + vertical_offset, width, height);
422
423 switch (type()) {
424 case OverviewGridWindowFillMode::kLetterBoxed:
425 case OverviewGridWindowFillMode::kPillarBoxed: {
426 // Attempt to scale |rect| to fit |bounds|. Maintain the aspect ratio of
427 // |rect|. Letter boxed windows' width will match |bounds|'s width and
428 // pillar boxed windows' height will match |bounds|'s height.
429 const bool is_pillar = type() == OverviewGridWindowFillMode::kPillarBoxed;
430 const gfx::Rect window_bounds =
431 ::wm::GetTransientRoot(window_)->GetBoundsInScreen();
432 const float window_ratio =
433 float{window_bounds.width()} / window_bounds.height();
434 if (is_pillar) {
435 const float new_x = height * window_ratio;
436 new_bounds.set_width(new_x);
437 } else {
438 const float new_y = bounds.width() / window_ratio;
439 new_bounds = bounds;
440 new_bounds.Inset(0, title_height, 0, 0);
441 new_bounds.ClampToCenteredSize(gfx::SizeF(bounds.width(), new_y));
442 }
443 break;
444 }
445 default:
446 break;
447 }
448
449 // If we do not use whole numbers, there may be some artifacts drawn (i.e.
450 // shadows, notches). This may be an effect of subpixel rendering. It's ok to
451 // round it here since this is the last calculation (we don't have to worry
452 // about roundoff error).
453 return gfx::RectF(gfx::ToRoundedRect(new_bounds));
454 }
455
GetOverviewWindow() const456 aura::Window* ScopedOverviewTransformWindow::GetOverviewWindow() const {
457 if (IsMinimized())
458 return overview_item_->item_widget()->GetNativeWindow();
459 return window_;
460 }
461
Close()462 void ScopedOverviewTransformWindow::Close() {
463 if (immediate_close_for_tests) {
464 CloseWidget();
465 return;
466 }
467
468 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
469 FROM_HERE,
470 base::BindOnce(&ScopedOverviewTransformWindow::CloseWidget,
471 weak_ptr_factory_.GetWeakPtr()),
472 base::TimeDelta::FromMilliseconds(kCloseWindowDelayInMilliseconds));
473 }
474
IsMinimized() const475 bool ScopedOverviewTransformWindow::IsMinimized() const {
476 return WindowState::Get(window_)->IsMinimized();
477 }
478
PrepareForOverview()479 void ScopedOverviewTransformWindow::PrepareForOverview() {
480 Shell::Get()->shadow_controller()->UpdateShadowForWindow(window_);
481
482 // Add requests to cache render surface and perform trilinear filtering. The
483 // requests will be removed in dtor. So the requests will be valid during the
484 // enter animation and the whole time during overview mode. For the exit
485 // animation of overview mode, we need to add those requests again.
486 if (features::IsTrilinearFilteringEnabled()) {
487 for (auto* window :
488 window_util::GetVisibleTransientTreeIterator(GetOverviewWindow())) {
489 cached_and_filtered_layer_observers_.push_back(
490 std::make_unique<LayerCachingAndFilteringObserver>(window->layer()));
491 }
492 }
493 }
494
EnsureVisible()495 void ScopedOverviewTransformWindow::EnsureVisible() {
496 original_opacity_ = 1.f;
497 }
498
UpdateWindowDimensionsType()499 void ScopedOverviewTransformWindow::UpdateWindowDimensionsType() {
500 type_ = GetWindowDimensionsType(window_->bounds().size());
501 }
502
UpdateRoundedCorners(bool show)503 void ScopedOverviewTransformWindow::UpdateRoundedCorners(bool show) {
504 // Hide the corners if minimized, OverviewItemView will handle showing the
505 // rounded corners on the UI.
506 if (IsMinimized())
507 DCHECK(!show);
508
509 ui::Layer* layer = window_->layer();
510 const float scale = layer->transform().Scale2d().x();
511 const int radius =
512 views::LayoutProvider::Get()->GetCornerRadiusMetric(views::EMPHASIS_LOW);
513 const gfx::RoundedCornersF radii(show ? (radius / scale) : 0.0f);
514 layer->SetRoundedCornerRadius(radii);
515 layer->SetIsFastRoundedCorner(true);
516 }
517
OnTransientChildWindowAdded(aura::Window * parent,aura::Window * transient_child)518 void ScopedOverviewTransformWindow::OnTransientChildWindowAdded(
519 aura::Window* parent,
520 aura::Window* transient_child) {
521 if (parent != window_ && !::wm::HasTransientAncestor(parent, window_))
522 return;
523
524 DCHECK(!event_targeting_blocker_map_.contains(transient_child));
525 event_targeting_blocker_map_[transient_child] =
526 std::make_unique<aura::ScopedWindowEventTargetingBlocker>(
527 transient_child);
528 transient_child->SetProperty(chromeos::kIsShowingInOverviewKey, true);
529
530 // Hide transient children which have been specified to be hidden in
531 // overview mode.
532 if (transient_child != window_ &&
533 transient_child->GetProperty(kHideInOverviewKey))
534 AddHiddenTransientWindows({transient_child});
535
536 // Add this as |aura::WindowObserver| for observing |kHideInOverviewKey|
537 // property changes.
538 window_observer_.Add(transient_child);
539 }
540
OnTransientChildWindowRemoved(aura::Window * parent,aura::Window * transient_child)541 void ScopedOverviewTransformWindow::OnTransientChildWindowRemoved(
542 aura::Window* parent,
543 aura::Window* transient_child) {
544 if (parent != window_ && !::wm::HasTransientAncestor(parent, window_))
545 return;
546
547 transient_child->ClearProperty(chromeos::kIsShowingInOverviewKey);
548 DCHECK(event_targeting_blocker_map_.contains(transient_child));
549 event_targeting_blocker_map_.erase(transient_child);
550
551 if (window_observer_.IsObserving(transient_child))
552 window_observer_.Remove(transient_child);
553 }
554
OnWindowPropertyChanged(aura::Window * window,const void * key,intptr_t old)555 void ScopedOverviewTransformWindow::OnWindowPropertyChanged(
556 aura::Window* window,
557 const void* key,
558 intptr_t old) {
559 if (key != kHideInOverviewKey)
560 return;
561
562 const auto current_value = window->GetProperty(kHideInOverviewKey);
563 if (current_value == old)
564 return;
565
566 if (current_value) {
567 AddHiddenTransientWindows({window});
568 } else {
569 hidden_transient_children_->RemoveWindow(window);
570 }
571 }
572
OnWindowBoundsChanged(aura::Window * window,const gfx::Rect & old_bounds,const gfx::Rect & new_bounds,ui::PropertyChangeReason reason)573 void ScopedOverviewTransformWindow::OnWindowBoundsChanged(
574 aura::Window* window,
575 const gfx::Rect& old_bounds,
576 const gfx::Rect& new_bounds,
577 ui::PropertyChangeReason reason) {
578 if (window == window_)
579 return;
580
581 // Transient window is repositioned. The new position within the
582 // overview item needs to be recomputed. No need to recompute if the
583 // transient is invisible. It will get placed properly when it reshows on
584 // overview end.
585 if (!window->IsVisible())
586 return;
587
588 overview_item_->SetBounds(overview_item_->target_bounds(),
589 OVERVIEW_ANIMATION_NONE);
590 }
591
592 // static
SetImmediateCloseForTests(bool immediate)593 void ScopedOverviewTransformWindow::SetImmediateCloseForTests(bool immediate) {
594 immediate_close_for_tests = immediate;
595 }
596
CloseWidget()597 void ScopedOverviewTransformWindow::CloseWidget() {
598 aura::Window* parent_window = ::wm::GetTransientRoot(window_);
599 if (parent_window)
600 window_util::CloseWidgetForWindow(parent_window);
601 }
602
AddHiddenTransientWindows(const std::vector<aura::Window * > & transient_windows)603 void ScopedOverviewTransformWindow::AddHiddenTransientWindows(
604 const std::vector<aura::Window*>& transient_windows) {
605 if (!hidden_transient_children_) {
606 hidden_transient_children_ = std::make_unique<ScopedOverviewHideWindows>(
607 std::move(transient_windows), /*forced_hidden=*/true);
608 } else {
609 for (auto* window : transient_windows)
610 hidden_transient_children_->AddWindow(window);
611 }
612 }
613
614 } // namespace ash
615