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