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/overview_item.h"
6 
7 #include <algorithm>
8 #include <utility>
9 #include <vector>
10 
11 #include "ash/accessibility/accessibility_controller_impl.h"
12 #include "ash/public/cpp/shell_window_ids.h"
13 #include "ash/public/cpp/window_properties.h"
14 #include "ash/scoped_animation_disabler.h"
15 #include "ash/shell.h"
16 #include "ash/strings/grit/ash_strings.h"
17 #include "ash/style/default_color_constants.h"
18 #include "ash/style/default_colors.h"
19 #include "ash/wm/desks/desks_util.h"
20 #include "ash/wm/drag_window_controller.h"
21 #include "ash/wm/overview/delayed_animation_observer_impl.h"
22 #include "ash/wm/overview/overview_constants.h"
23 #include "ash/wm/overview/overview_controller.h"
24 #include "ash/wm/overview/overview_grid.h"
25 #include "ash/wm/overview/overview_grid_event_handler.h"
26 #include "ash/wm/overview/overview_highlight_controller.h"
27 #include "ash/wm/overview/overview_item_view.h"
28 #include "ash/wm/overview/overview_types.h"
29 #include "ash/wm/overview/overview_utils.h"
30 #include "ash/wm/overview/overview_window_drag_controller.h"
31 #include "ash/wm/overview/rounded_label_widget.h"
32 #include "ash/wm/overview/scoped_overview_animation_settings.h"
33 #include "ash/wm/overview/scoped_overview_transform_window.h"
34 #include "ash/wm/splitview/split_view_constants.h"
35 #include "ash/wm/splitview/split_view_utils.h"
36 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
37 #include "ash/wm/window_preview_view.h"
38 #include "ash/wm/window_state.h"
39 #include "ash/wm/window_transient_descendant_iterator.h"
40 #include "ash/wm/window_util.h"
41 #include "ash/wm/wm_event.h"
42 #include "base/auto_reset.h"
43 #include "base/bind.h"
44 #include "base/callback_helpers.h"
45 #include "base/metrics/user_metrics.h"
46 #include "chromeos/ui/base/window_state_type.h"
47 #include "ui/accessibility/ax_enums.mojom.h"
48 #include "ui/aura/client/aura_constants.h"
49 #include "ui/base/l10n/l10n_util.h"
50 #include "ui/compositor/layer_animation_observer.h"
51 #include "ui/compositor/layer_animation_sequence.h"
52 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
53 #include "ui/compositor/scoped_layer_animation_settings.h"
54 #include "ui/compositor_extra/shadow.h"
55 #include "ui/gfx/geometry/size_conversions.h"
56 #include "ui/gfx/transform_util.h"
57 #include "ui/views/controls/button/image_button.h"
58 #include "ui/views/widget/widget.h"
59 #include "ui/wm/core/coordinate_conversion.h"
60 #include "ui/wm/core/shadow_types.h"
61 #include "ui/wm/core/window_util.h"
62 
63 namespace ash {
64 
65 namespace {
66 
67 using ::chromeos::WindowStateType;
68 
69 // Opacity for fading out during closing a window.
70 constexpr float kClosingItemOpacity = 0.8f;
71 
72 // Before closing a window animate both the window and the caption to shrink by
73 // this fraction of size.
74 constexpr float kPreCloseScale = 0.02f;
75 
76 constexpr int kShadowElevation = 16;
77 
78 // The amount of translation an item animates by when it is closed by using
79 // swipe to close.
80 constexpr int kSwipeToCloseCloseTranslationDp = 96;
81 
82 // When an item is being dragged, the bounds are outset horizontally by this
83 // fraction of the width, and vertically by this fraction of the height. The
84 // outset in each dimension is on both sides, for a total of twice this much
85 // change in the size of the item along that dimension.
86 constexpr float kDragWindowScale = 0.05f;
87 
88 // A self-deleting animation observer that runs the given callback when its
89 // associated animation completes. Optionally takes a callback that is run when
90 // the animation starts.
91 class AnimationObserver : public ui::ImplicitAnimationObserver {
92  public:
AnimationObserver(base::OnceClosure on_animation_finished)93   explicit AnimationObserver(base::OnceClosure on_animation_finished)
94       : AnimationObserver(base::NullCallback(),
95                           std::move(on_animation_finished)) {}
96 
AnimationObserver(base::OnceClosure on_animation_started,base::OnceClosure on_animation_finished)97   AnimationObserver(base::OnceClosure on_animation_started,
98                     base::OnceClosure on_animation_finished)
99       : on_animation_started_(std::move(on_animation_started)),
100         on_animation_finished_(std::move(on_animation_finished)) {
101     DCHECK(!on_animation_finished_.is_null());
102   }
103 
104   ~AnimationObserver() override = default;
105 
106   // ui::ImplicitAnimationObserver:
OnLayerAnimationStarted(ui::LayerAnimationSequence * sequence)107   void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override {
108     if (!on_animation_started_.is_null())
109       std::move(on_animation_started_).Run();
110   }
OnImplicitAnimationsCompleted()111   void OnImplicitAnimationsCompleted() override {
112     std::move(on_animation_finished_).Run();
113     delete this;
114   }
115 
116  private:
117   base::OnceClosure on_animation_started_;
118   base::OnceClosure on_animation_finished_;
119 
120   DISALLOW_COPY_AND_ASSIGN(AnimationObserver);
121 };
122 
GetExitOverviewAnimationTypeForMinimizedWindow(OverviewEnterExitType type,bool should_animate_when_exiting)123 OverviewAnimationType GetExitOverviewAnimationTypeForMinimizedWindow(
124     OverviewEnterExitType type,
125     bool should_animate_when_exiting) {
126   // We should never get here when overview mode should exit immediately. The
127   // minimized window's |item_widget_| should be closed and destroyed
128   // immediately.
129   DCHECK_NE(type, OverviewEnterExitType::kImmediateExit);
130 
131   // OverviewEnterExitType can only be set to kWindowMinimized in talbet mode.
132   // Fade out the minimized window without animation if switch from tablet mode
133   // to clamshell mode.
134   if (type == OverviewEnterExitType::kFadeOutExit) {
135     return Shell::Get()->tablet_mode_controller()->InTabletMode()
136                ? OVERVIEW_ANIMATION_EXIT_TO_HOME_LAUNCHER
137                : OVERVIEW_ANIMATION_NONE;
138   }
139   return should_animate_when_exiting
140              ? OVERVIEW_ANIMATION_EXIT_OVERVIEW_MODE_FADE_OUT
141              : OVERVIEW_ANIMATION_RESTORE_WINDOW_ZERO;
142 }
143 
144 // Applies |new_bounds_in_screen| to |widget|, animating and observing the
145 // transform if necessary.
SetWidgetBoundsAndMaybeAnimateTransform(views::Widget * widget,const gfx::Rect & new_bounds_in_screen,OverviewAnimationType animation_type,ui::ImplicitAnimationObserver * observer)146 void SetWidgetBoundsAndMaybeAnimateTransform(
147     views::Widget* widget,
148     const gfx::Rect& new_bounds_in_screen,
149     OverviewAnimationType animation_type,
150     ui::ImplicitAnimationObserver* observer) {
151   aura::Window* window = widget->GetNativeWindow();
152   gfx::RectF previous_bounds = gfx::RectF(window->GetBoundsInScreen());
153   window->SetBoundsInScreen(
154       new_bounds_in_screen,
155       display::Screen::GetScreen()->GetDisplayNearestWindow(window));
156   if (animation_type == OVERVIEW_ANIMATION_NONE ||
157       animation_type == OVERVIEW_ANIMATION_ENTER_FROM_HOME_LAUNCHER ||
158       previous_bounds.IsEmpty()) {
159     window->SetTransform(gfx::Transform());
160 
161     // Make sure that |observer|, which could be a self-deleting object, will
162     // not be leaked.
163     DCHECK(!observer);
164     return;
165   }
166 
167   // For animations, compute the transform needed to place the widget at its
168   // new bounds back to the old bounds, and then apply the idenity
169   // transform. This so the bounds visually line up the concurrent transform
170   // animations. Also transform animations may be more performant.
171   const gfx::RectF current_bounds = gfx::RectF(window->GetBoundsInScreen());
172   window->SetTransform(
173       gfx::TransformBetweenRects(current_bounds, previous_bounds));
174   ScopedOverviewAnimationSettings settings(animation_type, window);
175   if (observer)
176     settings.AddObserver(observer);
177   window->SetTransform(gfx::Transform());
178 }
179 
180 }  // namespace
181 
OverviewItem(aura::Window * window,OverviewSession * overview_session,OverviewGrid * overview_grid)182 OverviewItem::OverviewItem(aura::Window* window,
183                            OverviewSession* overview_session,
184                            OverviewGrid* overview_grid)
185     : root_window_(window->GetRootWindow()),
186       transform_window_(this, window),
187       overview_session_(overview_session),
188       overview_grid_(overview_grid) {
189   CreateItemWidget();
190   window->AddObserver(this);
191   WindowState::Get(window)->AddObserver(this);
192 }
193 
~OverviewItem()194 OverviewItem::~OverviewItem() {
195   aura::Window* window = GetWindow();
196   WindowState::Get(window)->RemoveObserver(this);
197   window->RemoveObserver(this);
198 }
199 
GetWindow()200 aura::Window* OverviewItem::GetWindow() {
201   return transform_window_.window();
202 }
203 
Contains(const aura::Window * target) const204 bool OverviewItem::Contains(const aura::Window* target) const {
205   return transform_window_.Contains(target);
206 }
207 
OnMovingWindowToAnotherDesk()208 void OverviewItem::OnMovingWindowToAnotherDesk() {
209   is_moving_to_another_desk_ = true;
210   // Restore the dragged item window, so that its transform is reset to
211   // identity.
212   RestoreWindow(/*reset_transform=*/true);
213 }
214 
RestoreWindow(bool reset_transform)215 void OverviewItem::RestoreWindow(bool reset_transform) {
216   // TODO(oshima): SplitViewController has its own logic to adjust the
217   // target state in |SplitViewController::OnOverviewModeEnding|.
218   // Unify the mechanism to control it and remove ifs.
219   if (Shell::Get()->tablet_mode_controller()->InTabletMode() &&
220       !SplitViewController::Get(root_window_)->InSplitViewMode() &&
221       reset_transform) {
222     MaximizeIfSnapped(GetWindow());
223   }
224 
225   overview_item_view_->OnOverviewItemWindowRestoring();
226   transform_window_.RestoreWindow(reset_transform);
227 
228   if (transform_window_.IsMinimized()) {
229     const auto enter_exit_type = overview_session_->enter_exit_overview_type();
230 
231     if (is_moving_to_another_desk_ ||
232         enter_exit_type == OverviewEnterExitType::kImmediateExit) {
233       overview_session_->highlight_controller()->OnViewDestroyingOrDisabling(
234           overview_item_view_);
235       ImmediatelyCloseWidgetOnExit(std::move(item_widget_));
236       overview_item_view_ = nullptr;
237       return;
238     }
239 
240     OverviewAnimationType animation_type =
241         GetExitOverviewAnimationTypeForMinimizedWindow(
242             enter_exit_type, should_animate_when_exiting_);
243     FadeOutWidgetFromOverview(std::move(item_widget_), animation_type);
244   }
245 }
246 
EnsureVisible()247 void OverviewItem::EnsureVisible() {
248   transform_window_.EnsureVisible();
249 }
250 
Shutdown()251 void OverviewItem::Shutdown() {
252   item_widget_.reset();
253   overview_item_view_ = nullptr;
254 }
255 
PrepareForOverview()256 void OverviewItem::PrepareForOverview() {
257   transform_window_.PrepareForOverview();
258   prepared_for_overview_ = true;
259 }
260 
GetItemScale(const gfx::Size & size)261 float OverviewItem::GetItemScale(const gfx::Size& size) {
262   gfx::SizeF inset_size(size.width(), size.height());
263   return ScopedOverviewTransformWindow::GetItemScale(
264       GetTargetBoundsInScreen().size(), inset_size,
265       transform_window_.GetTopInset(), kHeaderHeightDp);
266 }
267 
GetTargetBoundsInScreen() const268 gfx::RectF OverviewItem::GetTargetBoundsInScreen() const {
269   return ::ash::GetTargetBoundsInScreen(transform_window_.window());
270 }
271 
GetTransformedBounds() const272 gfx::RectF OverviewItem::GetTransformedBounds() const {
273   return transform_window_.GetTransformedBounds();
274 }
275 
SetBounds(const gfx::RectF & target_bounds,OverviewAnimationType animation_type)276 void OverviewItem::SetBounds(const gfx::RectF& target_bounds,
277                              OverviewAnimationType animation_type) {
278   if (in_bounds_update_ ||
279       !Shell::Get()->overview_controller()->InOverviewSession()) {
280     return;
281   }
282 
283   // Do not animate if the resulting bounds does not change. The original
284   // window may change bounds so we still need to call SetItemBounds to update
285   // the window transform.
286   OverviewAnimationType new_animation_type = animation_type;
287   if (target_bounds == target_bounds_ &&
288       !GetWindow()->layer()->GetAnimator()->is_animating()) {
289     new_animation_type = OVERVIEW_ANIMATION_NONE;
290   }
291 
292   base::AutoReset<bool> auto_reset_in_bounds_update(&in_bounds_update_, true);
293   // If |target_bounds_| is empty, this is the first update. Let
294   // UpdateHeaderLayout know, as we do not want |item_widget_| to be animated
295   // with the window.
296   const bool is_first_update = target_bounds_.IsEmpty();
297   target_bounds_ = target_bounds;
298 
299   // If the window is minimized we can avoid applying transforms on the original
300   // window.
301   if (transform_window_.IsMinimized()) {
302     item_widget_->GetNativeWindow()->layer()->GetAnimator()->StopAnimating();
303 
304     gfx::Rect minimized_bounds = ToStableSizeRoundedRect(target_bounds);
305     OverviewAnimationType minimized_animation_type =
306         is_first_update ? OVERVIEW_ANIMATION_NONE : new_animation_type;
307     SetWidgetBoundsAndMaybeAnimateTransform(
308         item_widget_.get(), minimized_bounds, minimized_animation_type,
309         minimized_animation_type ==
310                 OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_IN_OVERVIEW
311             ? new AnimationObserver{base::BindOnce(
312                                         &OverviewItem::
313                                             OnItemBoundsAnimationStarted,
314                                         weak_ptr_factory_.GetWeakPtr()),
315                                     base::BindOnce(
316                                         &OverviewItem::
317                                             OnItemBoundsAnimationEnded,
318                                         weak_ptr_factory_.GetWeakPtr())}
319             : nullptr);
320 
321     // Minimized windows have a WindowPreviewView which mirrors content from the
322     // window. |target_bounds| may not have a matching aspect ratio to the
323     // actual window (eg. in splitview overview). In this case, the contents
324     // will be squashed to fit the given bounds. To get around this, stretch out
325     // the contents so that it matches |unclipped_size_|, then clip the layer to
326     // match |target_bounds|. This is what is done on non-minimized windows.
327     ui::Layer* preview_layer = overview_item_view_->preview_view()->layer();
328     DCHECK(preview_layer);
329     if (unclipped_size_) {
330       gfx::SizeF target_size(*unclipped_size_);
331       gfx::SizeF preview_size = GetWindowTargetBoundsWithInsets().size();
332       target_size.Enlarge(0, -kHeaderHeightDp);
333 
334       const float x_scale = target_size.width() / preview_size.width();
335       const float y_scale = target_size.height() / preview_size.height();
336       gfx::Transform transform;
337       transform.Scale(x_scale, y_scale);
338       preview_layer->SetTransform(transform);
339 
340       // Transform affects clip rect so scale the clip rect so that the final
341       // size is equal to the untransformed layer.
342       gfx::Size clip_size(preview_layer->size());
343       clip_size =
344           gfx::ScaleToRoundedSize(clip_size, 1.f / x_scale, 1.f / y_scale);
345       preview_layer->SetClipRect(gfx::Rect(clip_size));
346     } else {
347       preview_layer->SetClipRect(gfx::Rect());
348       preview_layer->SetTransform(gfx::Transform());
349     }
350 
351     // On the first update show |item_widget_|. It's created on creation of
352     // |this|, and needs to be shown as soon as its bounds have been determined
353     // as it contains a mirror view of the window in its contents. The header
354     // will be faded in later to match non minimized windows.
355     if (is_first_update) {
356       if (!should_animate_when_entering_) {
357         item_widget_->GetNativeWindow()->layer()->SetOpacity(1.f);
358       } else {
359         if (new_animation_type == OVERVIEW_ANIMATION_SPAWN_ITEM_IN_OVERVIEW) {
360           PerformItemSpawnedAnimation(item_widget_->GetNativeWindow(),
361                                       gfx::Transform{});
362         } else {
363           // If entering from home launcher, use the home specific (fade)
364           // animation.
365           OverviewAnimationType fade_animation =
366               animation_type == OVERVIEW_ANIMATION_ENTER_FROM_HOME_LAUNCHER
367                   ? animation_type
368                   : OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN;
369 
370           FadeInWidgetToOverview(item_widget_.get(), fade_animation,
371                                  /*observe=*/true);
372 
373           // Update the item header visibility immediately if entering from home
374           // launcher.
375           if (new_animation_type ==
376               OVERVIEW_ANIMATION_ENTER_FROM_HOME_LAUNCHER) {
377             overview_item_view_->SetHeaderVisibility(
378                 OverviewItemView::HeaderVisibility::kVisible);
379           }
380         }
381       }
382     }
383   } else {
384     gfx::RectF inset_bounds(target_bounds);
385     SetItemBounds(inset_bounds, new_animation_type, is_first_update);
386     UpdateHeaderLayout(is_first_update ? OVERVIEW_ANIMATION_NONE
387                                        : new_animation_type);
388   }
389 
390   // Shadow is normally set after an animation is finished. In the case of no
391   // animations, manually set the shadow. Shadow relies on both the window
392   // transform and |item_widget_|'s new bounds so set it after SetItemBounds
393   // and UpdateHeaderLayout. Do not apply the shadow for drop target.
394   if (new_animation_type == OVERVIEW_ANIMATION_NONE)
395     UpdateRoundedCornersAndShadow();
396 
397   if (cannot_snap_widget_) {
398     SetWidgetBoundsAndMaybeAnimateTransform(
399         cannot_snap_widget_.get(),
400         cannot_snap_widget_->GetBoundsCenteredIn(
401             ToStableSizeRoundedRect(GetWindowTargetBoundsWithInsets())),
402         new_animation_type, nullptr);
403   }
404 }
405 
SendAccessibleSelectionEvent()406 void OverviewItem::SendAccessibleSelectionEvent() {
407   overview_item_view_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection,
408                                                 true);
409 }
410 
AnimateAndCloseWindow(bool up)411 void OverviewItem::AnimateAndCloseWindow(bool up) {
412   base::RecordAction(base::UserMetricsAction("WindowSelector_SwipeToClose"));
413 
414   animating_to_close_ = true;
415   overview_session_->PositionWindows(/*animate=*/true);
416   overview_item_view_->OnOverviewItemWindowRestoring();
417 
418   int translation_y = kSwipeToCloseCloseTranslationDp * (up ? -1 : 1);
419   gfx::Transform transform;
420   transform.Translate(gfx::Vector2d(0, translation_y));
421 
422   auto animate_window = [this](aura::Window* window,
423                                const gfx::Transform& transform, bool observe) {
424     ScopedOverviewAnimationSettings settings(
425         OVERVIEW_ANIMATION_CLOSE_OVERVIEW_ITEM, window);
426     gfx::Transform original_transform = window->transform();
427     original_transform.ConcatTransform(transform);
428     window->SetTransform(original_transform);
429     if (observe) {
430       settings.AddObserver(new AnimationObserver{
431           base::BindOnce(&OverviewItem::OnWindowCloseAnimationCompleted,
432                          weak_ptr_factory_.GetWeakPtr())});
433     }
434   };
435 
436   AnimateOpacity(0.0, OVERVIEW_ANIMATION_CLOSE_OVERVIEW_ITEM);
437   if (cannot_snap_widget_)
438     animate_window(cannot_snap_widget_->GetNativeWindow(), transform, false);
439   if (!transform_window_.IsMinimized())
440     animate_window(GetWindow(), transform, false);
441   animate_window(item_widget_->GetNativeWindow(), transform, true);
442 }
443 
CloseWindow()444 void OverviewItem::CloseWindow() {
445   SetShadowBounds(base::nullopt);
446 
447   gfx::RectF inset_bounds(target_bounds_);
448   inset_bounds.Inset(target_bounds_.width() * kPreCloseScale,
449                      target_bounds_.height() * kPreCloseScale);
450   // Scale down both the window and label.
451   SetBounds(inset_bounds, OVERVIEW_ANIMATION_CLOSING_OVERVIEW_ITEM);
452   // First animate opacity to an intermediate value concurrently with the
453   // scaling animation.
454   AnimateOpacity(kClosingItemOpacity, OVERVIEW_ANIMATION_CLOSING_OVERVIEW_ITEM);
455 
456   // Fade out the window and the label, effectively hiding them.
457   AnimateOpacity(0.0, OVERVIEW_ANIMATION_CLOSE_OVERVIEW_ITEM);
458 
459   // |transform_window_| will delete |this| by deleting the widget associated
460   // with |this|.
461   transform_window_.Close();
462 }
463 
UpdateCannotSnapWarningVisibility()464 void OverviewItem::UpdateCannotSnapWarningVisibility() {
465   // Windows which can snap will never show this warning. Or if the window is
466   // the drop target window, also do not show this warning.
467   bool visible = true;
468   if (SplitViewController::Get(root_window_)->CanSnapWindow(GetWindow()) ||
469       overview_grid_->IsDropTargetWindow(GetWindow())) {
470     visible = false;
471   } else {
472     const SplitViewController::State state =
473         SplitViewController::Get(root_window_)->state();
474     visible = state == SplitViewController::State::kLeftSnapped ||
475               state == SplitViewController::State::kRightSnapped;
476   }
477 
478   if (!visible && !cannot_snap_widget_)
479     return;
480 
481   if (!cannot_snap_widget_) {
482     RoundedLabelWidget::InitParams params;
483     params.horizontal_padding = kSplitviewLabelHorizontalInsetDp;
484     params.vertical_padding = kSplitviewLabelVerticalInsetDp;
485     params.background_color = DeprecatedGetBaseLayerColor(
486         AshColorProvider::BaseLayerType::kTransparent80,
487         kSplitviewLabelBackgroundColor);
488     params.foreground_color = DeprecatedGetContentLayerColor(
489         AshColorProvider::ContentLayerType::kTextColorPrimary,
490         kSplitviewLabelEnabledColor);
491     params.rounding_dp = kSplitviewLabelRoundRectRadiusDp;
492     params.preferred_height = kSplitviewLabelPreferredHeightDp;
493     params.message_id = IDS_ASH_SPLIT_VIEW_CANNOT_SNAP;
494     params.parent = GetWindow()->parent();
495     params.hide_in_mini_view = true;
496     cannot_snap_widget_ = std::make_unique<RoundedLabelWidget>();
497     cannot_snap_widget_->Init(std::move(params));
498     GetWindow()->parent()->StackChildAbove(
499         cannot_snap_widget_->GetNativeWindow(), GetWindow());
500   }
501 
502   DoSplitviewOpacityAnimation(cannot_snap_widget_->GetNativeWindow()->layer(),
503                               visible
504                                   ? SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_IN
505                                   : SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_OUT);
506   const gfx::Rect bounds =
507       ToStableSizeRoundedRect(GetWindowTargetBoundsWithInsets());
508   cannot_snap_widget_->SetBoundsCenteredIn(bounds, /*animate=*/false);
509 }
510 
HideCannotSnapWarning()511 void OverviewItem::HideCannotSnapWarning() {
512   if (!cannot_snap_widget_)
513     return;
514   DoSplitviewOpacityAnimation(cannot_snap_widget_->GetNativeWindow()->layer(),
515                               SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_OUT);
516 }
517 
OnSelectorItemDragStarted(OverviewItem * item)518 void OverviewItem::OnSelectorItemDragStarted(OverviewItem* item) {
519   is_being_dragged_ = (item == this);
520   overview_item_view_->SetHeaderVisibility(
521       is_being_dragged_
522           ? OverviewItemView::HeaderVisibility::kInvisible
523           : OverviewItemView::HeaderVisibility::kCloseButtonInvisibleOnly);
524 }
525 
OnSelectorItemDragEnded(bool snap)526 void OverviewItem::OnSelectorItemDragEnded(bool snap) {
527   if (snap) {
528     if (!is_being_dragged_)
529       overview_item_view_->HideCloseInstantlyAndThenShowItSlowly();
530   } else {
531     overview_item_view_->SetHeaderVisibility(
532         OverviewItemView::HeaderVisibility::kVisible);
533   }
534   is_being_dragged_ = false;
535 }
536 
SetVisibleDuringWindowDragging(bool visible,bool animate)537 void OverviewItem::SetVisibleDuringWindowDragging(bool visible, bool animate) {
538   aura::Window::Windows windows = GetWindowsForHomeGesture();
539   float new_opacity = visible ? 1.f : 0.f;
540   for (auto* window : windows) {
541     ui::Layer* layer = window->layer();
542     if (layer->GetTargetOpacity() == new_opacity)
543       continue;
544 
545     if (animate) {
546       ScopedOverviewAnimationSettings settings(
547           OVERVIEW_ANIMATION_OPACITY_ON_WINDOW_DRAG, window);
548       layer->SetOpacity(new_opacity);
549     } else {
550       layer->SetOpacity(new_opacity);
551     }
552   }
553 }
554 
GetWindowDimensionsType() const555 OverviewGridWindowFillMode OverviewItem::GetWindowDimensionsType() const {
556   return transform_window_.type();
557 }
558 
UpdateWindowDimensionsType()559 void OverviewItem::UpdateWindowDimensionsType() {
560   transform_window_.UpdateWindowDimensionsType();
561   const bool show_backdrop =
562       GetWindowDimensionsType() != OverviewGridWindowFillMode::kNormal;
563   overview_item_view_->SetBackdropVisibility(show_backdrop);
564 }
565 
GetBoundsOfSelectedItem()566 gfx::Rect OverviewItem::GetBoundsOfSelectedItem() {
567   gfx::RectF original_bounds = target_bounds();
568   ScaleUpSelectedItem(OVERVIEW_ANIMATION_NONE);
569   gfx::RectF selected_bounds = transform_window_.GetTransformedBounds();
570   SetBounds(original_bounds, OVERVIEW_ANIMATION_NONE);
571   return ToStableSizeRoundedRect(selected_bounds);
572 }
573 
ScaleUpSelectedItem(OverviewAnimationType animation_type)574 void OverviewItem::ScaleUpSelectedItem(OverviewAnimationType animation_type) {
575   gfx::RectF scaled_bounds = target_bounds();
576   scaled_bounds.Inset(-scaled_bounds.width() * kDragWindowScale,
577                       -scaled_bounds.height() * kDragWindowScale);
578   if (unclipped_size_) {
579     // If a clipped item is scaled up, we need to recalculate the unclipped
580     // size.
581     const int height = scaled_bounds.height();
582     const int width =
583         overview_grid_->CalculateWidthAndMaybeSetUnclippedBounds(this, height);
584     DCHECK(unclipped_size_);
585     const gfx::SizeF new_size(width, height);
586     scaled_bounds.set_size(new_size);
587     scaled_bounds.ClampToCenteredSize(new_size);
588   }
589   SetBounds(scaled_bounds, animation_type);
590 }
591 
UpdateItemContentViewForMinimizedWindow()592 void OverviewItem::UpdateItemContentViewForMinimizedWindow() {
593   overview_item_view_->RefreshPreviewView();
594 }
595 
IsDragItem()596 bool OverviewItem::IsDragItem() {
597   return overview_session_->GetCurrentDraggedOverviewItem() == this;
598 }
599 
Restack()600 void OverviewItem::Restack() {
601   aura::Window* window = GetWindow();
602   aura::Window* parent_window = window->parent();
603   aura::Window* stacking_target = nullptr;
604   // Stack |window| below the split view window if split view is active.
605   SplitViewController* split_view_controller =
606       SplitViewController::Get(root_window_);
607   if (split_view_controller->InSplitViewMode()) {
608     aura::Window* snapped_window =
609         split_view_controller->GetDefaultSnappedWindow();
610     if (snapped_window->parent() == parent_window)
611       stacking_target = snapped_window;
612   }
613   // Stack |window| below the last window in |overview_grid_| that comes before
614   // |window| and has the same parent.
615   for (const std::unique_ptr<OverviewItem>& overview_item :
616        overview_grid_->window_list()) {
617     // |Restack| is sometimes called when there is a drop target, but is never
618     // used to restack an item that comes after a drop target. In other words,
619     // |overview_grid_| might have a drop target, but we will break out of the
620     // for loop before reaching it.
621     DCHECK(!overview_grid_->IsDropTargetWindow(overview_item->GetWindow()));
622     if (overview_item.get() == this)
623       break;
624     if (overview_item->GetWindow()->parent() == parent_window)
625       stacking_target = overview_item->item_widget()->GetNativeWindow();
626   }
627 
628   if (stacking_target) {
629     DCHECK_EQ(parent_window, stacking_target->parent());
630     parent_window->StackChildBelow(window, stacking_target);
631   }
632   DCHECK_EQ(parent_window, item_widget_->GetNativeWindow()->parent());
633   parent_window->StackChildBelow(item_widget_->GetNativeWindow(), window);
634   if (cannot_snap_widget_) {
635     DCHECK_EQ(parent_window, cannot_snap_widget_->GetNativeWindow()->parent());
636     parent_window->StackChildAbove(cannot_snap_widget_->GetNativeWindow(),
637                                    window);
638   }
639 }
640 
UpdatePhantomsForDragging(bool is_touch_dragging)641 void OverviewItem::UpdatePhantomsForDragging(bool is_touch_dragging) {
642   DCHECK(AreMultiDisplayOverviewAndSplitViewEnabled());
643   DCHECK_GT(Shell::GetAllRootWindows().size(), 1u);
644   if (!phantoms_for_dragging_) {
645     phantoms_for_dragging_ =
646         transform_window_.IsMinimized()
647             ? std::make_unique<DragWindowController>(
648                   item_widget_->GetNativeWindow(), is_touch_dragging,
649                   base::make_optional(shadow_->content_bounds()))
650             : std::make_unique<DragWindowController>(GetWindow(),
651                                                      is_touch_dragging);
652   }
653   phantoms_for_dragging_->Update();
654 }
655 
DestroyPhantomsForDragging()656 void OverviewItem::DestroyPhantomsForDragging() {
657   DCHECK(AreMultiDisplayOverviewAndSplitViewEnabled());
658   phantoms_for_dragging_.reset();
659 }
660 
SetShadowBounds(base::Optional<gfx::RectF> bounds_in_screen)661 void OverviewItem::SetShadowBounds(
662     base::Optional<gfx::RectF> bounds_in_screen) {
663   // Shadow is normally turned off during animations and reapplied when they
664   // are finished. On destruction, |shadow_| is cleaned up before
665   // |transform_window_|, which may call this function, so early exit if
666   // |shadow_| is nullptr.
667   if (!shadow_)
668     return;
669 
670   if (!bounds_in_screen) {
671     shadow_->layer()->SetVisible(false);
672     return;
673   }
674 
675   shadow_->layer()->SetVisible(true);
676   gfx::Rect bounds_in_item =
677       gfx::Rect(item_widget_->GetNativeWindow()->GetTargetBounds().size());
678   bounds_in_item.Inset(0, kHeaderHeightDp, 0, 0);
679   bounds_in_item.ClampToCenteredSize(
680       gfx::ToRoundedSize(bounds_in_screen->size()));
681   shadow_->SetContentBounds(bounds_in_item);
682 }
683 
UpdateRoundedCornersAndShadow()684 void OverviewItem::UpdateRoundedCornersAndShadow() {
685   // Do not show the rounded corners and the shadow if overview is shutting
686   // down or we're currently in entering overview animation. Also don't update
687   // or animate the window's frame header clip under these conditions.
688   OverviewController* overview_controller = Shell::Get()->overview_controller();
689   const bool is_shutting_down =
690       !overview_controller || !overview_controller->InOverviewSession();
691   const bool should_show_rounded_corners =
692       !disable_mask_ && !is_shutting_down &&
693       !overview_controller->IsInStartAnimation();
694 
695   if (transform_window_.IsMinimized()) {
696     overview_item_view_->UpdatePreviewRoundedCorners(
697         should_show_rounded_corners);
698   } else {
699     transform_window_.UpdateRoundedCorners(should_show_rounded_corners);
700   }
701 
702   // In addition, the shadow should be hidden if
703   // 1) this overview item is the drop target window or
704   // 2) this overview item is in animation.
705   const bool should_show_shadow =
706       should_show_rounded_corners &&
707       !overview_grid_->IsDropTargetWindow(GetWindow()) &&
708       !transform_window_.GetOverviewWindow()
709            ->layer()
710            ->GetAnimator()
711            ->is_animating();
712   if (should_show_shadow) {
713     // The shadow should match the size of the transformed window or preview
714     // window if unclipped. If clipped, the shadow should match the size of the
715     // item minus the border and header.
716     const gfx::RectF shadow_bounds = unclipped_size_
717                                          ? GetWindowTargetBoundsWithInsets()
718                                          : GetUnclippedShadowBounds();
719     SetShadowBounds(base::make_optional(shadow_bounds));
720   } else {
721     SetShadowBounds(base::nullopt);
722   }
723 }
724 
OnStartingAnimationComplete()725 void OverviewItem::OnStartingAnimationComplete() {
726   DCHECK(item_widget_);
727   if (transform_window_.IsMinimized()) {
728     // Fade the title in if minimized. The rest of |item_widget_| should
729     // already be shown.
730     overview_item_view_->SetHeaderVisibility(
731         OverviewItemView::HeaderVisibility::kVisible);
732   } else {
733     FadeInWidgetToOverview(item_widget_.get(),
734                            OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN,
735                            /*observe=*/false);
736   }
737   const bool show_backdrop =
738       GetWindowDimensionsType() != OverviewGridWindowFillMode::kNormal;
739   overview_item_view_->SetBackdropVisibility(show_backdrop);
740   UpdateCannotSnapWarningVisibility();
741 }
742 
StopWidgetAnimation()743 void OverviewItem::StopWidgetAnimation() {
744   DCHECK(item_widget_.get());
745   item_widget_->GetNativeWindow()->layer()->GetAnimator()->StopAnimating();
746 }
747 
SetOpacity(float opacity)748 void OverviewItem::SetOpacity(float opacity) {
749   item_widget_->SetOpacity(opacity);
750   transform_window_.SetOpacity(opacity);
751   if (cannot_snap_widget_)
752     cannot_snap_widget_->SetOpacity(opacity);
753 }
754 
GetOpacity()755 float OverviewItem::GetOpacity() {
756   return item_widget_->GetNativeWindow()->layer()->GetTargetOpacity();
757 }
758 
GetExitOverviewAnimationType()759 OverviewAnimationType OverviewItem::GetExitOverviewAnimationType() {
760   if (overview_session_->enter_exit_overview_type() ==
761       OverviewEnterExitType::kImmediateExit) {
762     return OVERVIEW_ANIMATION_NONE;
763   }
764 
765   return should_animate_when_exiting_
766              ? OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_ON_EXIT
767              : OVERVIEW_ANIMATION_NONE;
768 }
769 
GetExitTransformAnimationType()770 OverviewAnimationType OverviewItem::GetExitTransformAnimationType() {
771   if (is_moving_to_another_desk_ ||
772       overview_session_->enter_exit_overview_type() ==
773           OverviewEnterExitType::kImmediateExit) {
774     return OVERVIEW_ANIMATION_NONE;
775   }
776 
777   return should_animate_when_exiting_ ? OVERVIEW_ANIMATION_RESTORE_WINDOW
778                                       : OVERVIEW_ANIMATION_RESTORE_WINDOW_ZERO;
779 }
780 
HandleGestureEventForTabletModeLayout(ui::GestureEvent * event)781 void OverviewItem::HandleGestureEventForTabletModeLayout(
782     ui::GestureEvent* event) {
783   const gfx::PointF location = event->details().bounding_box_f().CenterPoint();
784   switch (event->type()) {
785     case ui::ET_SCROLL_FLING_START:
786       if (IsDragItem()) {
787         HandleFlingStartEvent(location, event->details().velocity_x(),
788                               event->details().velocity_y());
789       } else {
790         overview_grid()->grid_event_handler()->OnGestureEvent(event);
791       }
792       break;
793     case ui::ET_GESTURE_SCROLL_BEGIN:
794       if (std::abs(event->details().scroll_y_hint()) >
795           std::abs(event->details().scroll_x_hint())) {
796         HandlePressEvent(location, /*from_touch_gesture=*/true);
797       } else {
798         overview_grid()->grid_event_handler()->OnGestureEvent(event);
799       }
800       break;
801     case ui::ET_GESTURE_SCROLL_UPDATE:
802       if (IsDragItem())
803         HandleDragEvent(location);
804       else
805         overview_grid()->grid_event_handler()->OnGestureEvent(event);
806       break;
807     case ui::ET_GESTURE_SCROLL_END:
808       if (IsDragItem())
809         HandleReleaseEvent(location);
810       else
811         overview_grid()->grid_event_handler()->OnGestureEvent(event);
812       break;
813     case ui::ET_GESTURE_LONG_PRESS:
814       HandlePressEvent(location, /*from_touch_gesture=*/true);
815       HandleLongPressEvent(location);
816       break;
817     case ui::ET_GESTURE_TAP:
818       overview_session_->SelectWindow(this);
819       break;
820     case ui::ET_GESTURE_END:
821       HandleGestureEndEvent();
822       break;
823     default:
824       overview_grid()->grid_event_handler()->OnGestureEvent(event);
825       break;
826   }
827 }
828 
HandleMouseEvent(const ui::MouseEvent & event)829 void OverviewItem::HandleMouseEvent(const ui::MouseEvent& event) {
830   if (!overview_session_->CanProcessEvent(this, /*from_touch_gesture=*/false))
831     return;
832 
833   const gfx::PointF screen_location = event.target()->GetScreenLocationF(event);
834   switch (event.type()) {
835     case ui::ET_MOUSE_PRESSED:
836       HandlePressEvent(screen_location, /*from_touch_gesture=*/false);
837       break;
838     case ui::ET_MOUSE_RELEASED:
839       HandleReleaseEvent(screen_location);
840       break;
841     case ui::ET_MOUSE_DRAGGED:
842       HandleDragEvent(screen_location);
843       break;
844     default:
845       NOTREACHED();
846       break;
847   }
848 }
849 
HandleGestureEvent(ui::GestureEvent * event)850 void OverviewItem::HandleGestureEvent(ui::GestureEvent* event) {
851   if (!overview_session_->CanProcessEvent(this, /*from_touch_gesture=*/true)) {
852     event->StopPropagation();
853     event->SetHandled();
854     return;
855   }
856 
857   if (ShouldUseTabletModeGridLayout()) {
858     HandleGestureEventForTabletModeLayout(event);
859     return;
860   }
861 
862   const gfx::PointF location = event->details().bounding_box_f().CenterPoint();
863   switch (event->type()) {
864     case ui::ET_GESTURE_TAP_DOWN:
865       HandlePressEvent(location, /*from_touch_gesture=*/true);
866       break;
867     case ui::ET_GESTURE_SCROLL_UPDATE:
868       HandleDragEvent(location);
869       break;
870     case ui::ET_SCROLL_FLING_START:
871       HandleFlingStartEvent(location, event->details().velocity_x(),
872                             event->details().velocity_y());
873       break;
874     case ui::ET_GESTURE_SCROLL_END:
875       HandleReleaseEvent(location);
876       break;
877     case ui::ET_GESTURE_LONG_PRESS:
878       HandleLongPressEvent(location);
879       break;
880     case ui::ET_GESTURE_TAP:
881       HandleTapEvent();
882       break;
883     case ui::ET_GESTURE_END:
884       HandleGestureEndEvent();
885       break;
886     default:
887       break;
888   }
889 }
890 
OnHighlightedViewActivated()891 void OverviewItem::OnHighlightedViewActivated() {
892   overview_session_->OnHighlightedItemActivated(this);
893 }
894 
OnHighlightedViewClosed()895 void OverviewItem::OnHighlightedViewClosed() {
896   overview_session_->OnHighlightedItemClosed(this);
897 }
898 
OnWindowPropertyChanged(aura::Window * window,const void * key,intptr_t old)899 void OverviewItem::OnWindowPropertyChanged(aura::Window* window,
900                                            const void* key,
901                                            intptr_t old) {
902   DCHECK_EQ(GetWindow(), window);
903 
904   if (!prepared_for_overview_)
905     return;
906 
907   if (key != aura::client::kTopViewInset)
908     return;
909 
910   if (window->GetProperty(aura::client::kTopViewInset) !=
911       static_cast<int>(old)) {
912     overview_grid_->PositionWindows(/*animate=*/false);
913   }
914 }
915 
OnWindowBoundsChanged(aura::Window * window,const gfx::Rect & old_bounds,const gfx::Rect & new_bounds,ui::PropertyChangeReason reason)916 void OverviewItem::OnWindowBoundsChanged(aura::Window* window,
917                                          const gfx::Rect& old_bounds,
918                                          const gfx::Rect& new_bounds,
919                                          ui::PropertyChangeReason reason) {
920   DCHECK_EQ(GetWindow(), window);
921 
922   // During preparation, window bounds can change. Ignore bounds change
923   // notifications in this case; we'll reposition soon.
924   if (!prepared_for_overview_)
925     return;
926 
927   // Do not keep the overview bounds if we're shutting down.
928   if (!Shell::Get()->overview_controller()->InOverviewSession())
929     return;
930 
931   // The drop target will get its bounds set as opposed to its transform
932   // set in |SetItemBounds| so do not position windows again when that
933   // particular window has its bounds changed.
934   if (overview_grid_->IsDropTargetWindow(window))
935     return;
936 
937   if (reason == ui::PropertyChangeReason::NOT_FROM_ANIMATION)
938     overview_item_view_->RefreshPreviewView();
939 
940   // Immediately finish any active bounds animation.
941   window->layer()->GetAnimator()->StopAnimatingProperty(
942       ui::LayerAnimationElement::BOUNDS);
943   UpdateWindowDimensionsType();
944   overview_grid_->PositionWindows(/*animate=*/false);
945 }
946 
OnWindowDestroying(aura::Window * window)947 void OverviewItem::OnWindowDestroying(aura::Window* window) {
948   DCHECK_EQ(GetWindow(), window);
949 
950   if (is_being_dragged_) {
951     DCHECK_EQ(this, overview_session_->window_drag_controller()->item());
952     overview_session_->window_drag_controller()->ResetGesture();
953   }
954 
955   // Remove the item from the session which will remove it from the grid in
956   // addition to updating accessibility states. If the session is not available
957   // then remove it from the grid directly.
958   if (overview_session_) {
959     overview_session_->RemoveItem(this, /*item_destroying=*/true,
960                                   /*reposition=*/!animating_to_close_);
961   } else {
962     overview_grid()->RemoveItem(this, /*item_destroying=*/true,
963                                 /*reposition=*/!animating_to_close_);
964   }
965 
966   Shell::Get()
967       ->accessibility_controller()
968       ->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
969           IDS_ASH_OVERVIEW_WINDOW_CLOSING_A11Y_ALERT, window->GetTitle()));
970 }
971 
OnPreWindowStateTypeChange(WindowState * window_state,WindowStateType old_type)972 void OverviewItem::OnPreWindowStateTypeChange(WindowState* window_state,
973                                               WindowStateType old_type) {
974   // If entering overview and PIP happen at the same time, the PIP window is
975   // incorrectly listed in the overview list, which is not allowed.
976   if (window_state->IsPip())
977     overview_session_->RemoveItem(this);
978 }
979 
OnPostWindowStateTypeChange(WindowState * window_state,WindowStateType old_type)980 void OverviewItem::OnPostWindowStateTypeChange(WindowState* window_state,
981                                                WindowStateType old_type) {
982   // During preparation, window state can change, e.g. updating shelf
983   // visibility may show the temporarily hidden (minimized) panels.
984   if (!prepared_for_overview_)
985     return;
986 
987   WindowStateType new_type = window_state->GetStateType();
988   if (chromeos::IsMinimizedWindowStateType(old_type) ==
989       chromeos::IsMinimizedWindowStateType(new_type)) {
990     return;
991   }
992 
993   const bool minimized = transform_window_.IsMinimized();
994   overview_item_view_->SetShowPreview(minimized);
995   if (!minimized)
996     EnsureVisible();
997 
998   // Ensures the item widget is visible. |item_widget_| opacity is set to 0.f
999   // and shown at either |SetBounds| or |OnStartingAnimationComplete| based on
1000   // the minimized state. It's possible the minimized state changes in between
1001   // for ARC apps, so just force show it here.
1002   item_widget_->GetLayer()->SetOpacity(1.f);
1003 
1004   overview_grid_->PositionWindows(/*animate=*/false);
1005 }
1006 
GetShadowBoundsForTesting()1007 gfx::Rect OverviewItem::GetShadowBoundsForTesting() {
1008   if (!shadow_ || !shadow_->layer()->visible())
1009     return gfx::Rect();
1010 
1011   return shadow_->content_bounds();
1012 }
1013 
GetWindowTargetBoundsWithInsets() const1014 gfx::RectF OverviewItem::GetWindowTargetBoundsWithInsets() const {
1015   gfx::RectF window_target_bounds = target_bounds_;
1016   window_target_bounds.Inset(kWindowMargin, kWindowMargin);
1017   window_target_bounds.Inset(0, kHeaderHeightDp, 0, 0);
1018   return window_target_bounds;
1019 }
1020 
GetUnclippedShadowBounds() const1021 gfx::RectF OverviewItem::GetUnclippedShadowBounds() const {
1022   return transform_window_.IsMinimized()
1023              ? gfx::RectF(
1024                    overview_item_view_->preview_view()->GetBoundsInScreen())
1025              : transform_window_.GetTransformedBounds();
1026 }
1027 
OnWindowCloseAnimationCompleted()1028 void OverviewItem::OnWindowCloseAnimationCompleted() {
1029   transform_window_.Close();
1030 }
1031 
OnItemSpawnedAnimationCompleted()1032 void OverviewItem::OnItemSpawnedAnimationCompleted() {
1033   UpdateRoundedCornersAndShadow();
1034   if (should_restack_on_animation_end_) {
1035     Restack();
1036     should_restack_on_animation_end_ = false;
1037   }
1038   OnStartingAnimationComplete();
1039 }
1040 
OnItemBoundsAnimationStarted()1041 void OverviewItem::OnItemBoundsAnimationStarted() {
1042   // Remove the shadow before animating because it may affect animation
1043   // performance. The shadow will be added back once the animation is completed.
1044   // Note that we can't use UpdateRoundedCornersAndShadow() since we don't want
1045   // to update the rounded corners.
1046   SetShadowBounds(base::nullopt);
1047 }
1048 
OnItemBoundsAnimationEnded()1049 void OverviewItem::OnItemBoundsAnimationEnded() {
1050   // Do nothing if overview is shutting down. See crbug.com/1025267 for when it
1051   // might happen.
1052   if (!Shell::Get()->overview_controller()->InOverviewSession())
1053     return;
1054 
1055   UpdateRoundedCornersAndShadow();
1056   if (should_restack_on_animation_end_) {
1057     Restack();
1058     should_restack_on_animation_end_ = false;
1059   }
1060 }
1061 
PerformItemSpawnedAnimation(aura::Window * window,const gfx::Transform & target_transform)1062 void OverviewItem::PerformItemSpawnedAnimation(
1063     aura::Window* window,
1064     const gfx::Transform& target_transform) {
1065   DCHECK(should_use_spawn_animation_);
1066   should_use_spawn_animation_ = false;
1067 
1068   constexpr float kInitialScaler = 0.1f;
1069   constexpr float kTargetScaler = 1.0f;
1070 
1071   // Scale-up |window| and fade it in along with the |cannot_snap_widget_|'s
1072   // window.
1073   gfx::Transform initial_transform = target_transform;
1074   initial_transform.Scale(kInitialScaler, kInitialScaler);
1075   SetTransform(window, initial_transform);
1076   transform_window_.SetOpacity(kInitialScaler);
1077 
1078   ScopedOverviewTransformWindow::ScopedAnimationSettings animation_settings;
1079   for (auto* window_iter :
1080        window_util::GetVisibleTransientTreeIterator(window)) {
1081     auto settings = std::make_unique<ScopedOverviewAnimationSettings>(
1082         OVERVIEW_ANIMATION_SPAWN_ITEM_IN_OVERVIEW, window_iter);
1083     settings->DeferPaint();
1084     animation_settings.push_back(std::move(settings));
1085   }
1086 
1087   if (!animation_settings.empty()) {
1088     animation_settings.front()->AddObserver(new AnimationObserver{
1089         base::BindOnce(&OverviewItem::OnItemSpawnedAnimationCompleted,
1090                        weak_ptr_factory_.GetWeakPtr())});
1091   }
1092   SetTransform(window, target_transform);
1093   transform_window_.SetOpacity(kTargetScaler);
1094 
1095   if (cannot_snap_widget_) {
1096     aura::Window* cannot_snap_window = cannot_snap_widget_->GetNativeWindow();
1097     cannot_snap_window->layer()->SetOpacity(kInitialScaler);
1098     ScopedOverviewAnimationSettings label_animation_settings(
1099         OVERVIEW_ANIMATION_SPAWN_ITEM_IN_OVERVIEW, cannot_snap_window);
1100     cannot_snap_window->layer()->SetOpacity(kTargetScaler);
1101   }
1102 }
1103 
SetItemBounds(const gfx::RectF & target_bounds,OverviewAnimationType animation_type,bool is_first_update)1104 void OverviewItem::SetItemBounds(const gfx::RectF& target_bounds,
1105                                  OverviewAnimationType animation_type,
1106                                  bool is_first_update) {
1107   aura::Window* window = GetWindow();
1108   DCHECK(root_window_ == window->GetRootWindow());
1109   // Do not set transform for drop target, set bounds instead.
1110   if (overview_grid_->IsDropTargetWindow(window)) {
1111     const gfx::Rect drop_target_bounds =
1112         ToStableSizeRoundedRect(GetWindowTargetBoundsWithInsets());
1113     SetWidgetBoundsAndMaybeAnimateTransform(
1114         overview_grid_->drop_target_widget(), drop_target_bounds,
1115         animation_type, /*observer=*/nullptr);
1116     return;
1117   }
1118 
1119   gfx::RectF screen_rect = gfx::RectF(GetTargetBoundsInScreen());
1120 
1121   // Avoid division by zero by ensuring screen bounds is not empty.
1122   gfx::SizeF screen_size(screen_rect.size());
1123   screen_size.SetToMax(gfx::SizeF(1.f, 1.f));
1124   screen_rect.set_size(screen_size);
1125 
1126   const int top_view_inset = transform_window_.GetTopInset();
1127   gfx::RectF transformed_bounds = target_bounds;
1128 
1129   // |target_bounds| are the bounds of the |item_widget|, which include a
1130   // border.
1131   transformed_bounds.Inset(kWindowMargin, kWindowMargin);
1132 
1133   // Update |transformed_bounds| to match the unclipped size of the window, so
1134   // we transform the window to the correct size.
1135   if (unclipped_size_)
1136     transformed_bounds.set_size(gfx::SizeF(*unclipped_size_));
1137 
1138   gfx::RectF overview_item_bounds =
1139       transform_window_.ShrinkRectToFitPreservingAspectRatio(
1140           screen_rect, transformed_bounds, top_view_inset, kHeaderHeightDp);
1141 
1142   const gfx::Transform transform =
1143       gfx::TransformBetweenRects(screen_rect, overview_item_bounds);
1144 
1145   if (is_first_update &&
1146       animation_type == OVERVIEW_ANIMATION_SPAWN_ITEM_IN_OVERVIEW) {
1147     PerformItemSpawnedAnimation(window, transform);
1148     return;
1149   }
1150 
1151   ScopedOverviewTransformWindow::ScopedAnimationSettings animation_settings;
1152   transform_window_.BeginScopedAnimation(animation_type, &animation_settings);
1153   if (animation_type == OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_IN_OVERVIEW &&
1154       !animation_settings.empty()) {
1155     animation_settings.front()->AddObserver(new AnimationObserver{
1156         base::BindOnce(&OverviewItem::OnItemBoundsAnimationStarted,
1157                        weak_ptr_factory_.GetWeakPtr()),
1158         base::BindOnce(&OverviewItem::OnItemBoundsAnimationEnded,
1159                        weak_ptr_factory_.GetWeakPtr())});
1160   }
1161   SetTransform(window, transform);
1162 
1163   using ClippingType = ScopedOverviewTransformWindow::ClippingType;
1164   ScopedOverviewTransformWindow::ClippingData clipping_data{
1165       ClippingType::kCustom, gfx::SizeF()};
1166   if (unclipped_size_)
1167     clipping_data.second = GetWindowTargetBoundsWithInsets().size();
1168   else if (is_first_update)
1169     clipping_data.first = ClippingType::kEnter;
1170   transform_window_.SetClipping(clipping_data);
1171 }
1172 
CreateItemWidget()1173 void OverviewItem::CreateItemWidget() {
1174   views::Widget::InitParams params;
1175   params.type = views::Widget::InitParams::TYPE_POPUP;
1176   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1177   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
1178   params.visible_on_all_workspaces = true;
1179   params.layer_type = ui::LAYER_NOT_DRAWN;
1180   params.name = "OverviewModeLabel";
1181   params.activatable =
1182       views::Widget::InitParams::Activatable::ACTIVATABLE_DEFAULT;
1183   params.accept_events = true;
1184   params.parent = transform_window_.window()->parent();
1185   params.init_properties_container.SetProperty(kHideInDeskMiniViewKey, true);
1186 
1187   item_widget_ = std::make_unique<views::Widget>();
1188   item_widget_->set_focus_on_creation(false);
1189   item_widget_->Init(std::move(params));
1190   aura::Window* widget_window = item_widget_->GetNativeWindow();
1191   widget_window->parent()->StackChildBelow(widget_window, GetWindow());
1192 
1193   shadow_ = std::make_unique<ui::Shadow>();
1194   shadow_->Init(kShadowElevation);
1195   item_widget_->GetLayer()->Add(shadow_->layer());
1196 
1197   overview_item_view_ =
1198       item_widget_->SetContentsView(std::make_unique<OverviewItemView>(
1199           this,
1200           base::BindRepeating(&OverviewItem::CloseButtonPressed,
1201                               base::Unretained(this)),
1202           GetWindow(), transform_window_.IsMinimized()));
1203   item_widget_->Show();
1204   item_widget_->SetOpacity(0.f);
1205   item_widget_->GetLayer()->SetMasksToBounds(false);
1206 }
1207 
UpdateHeaderLayout(OverviewAnimationType animation_type)1208 void OverviewItem::UpdateHeaderLayout(OverviewAnimationType animation_type) {
1209   aura::Window* widget_window = item_widget_->GetNativeWindow();
1210   ScopedOverviewAnimationSettings animation_settings(animation_type,
1211                                                      widget_window);
1212   // Create a start animation observer if this is an enter overview layout
1213   // animation.
1214   if (animation_type == OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_ON_ENTER ||
1215       animation_type == OVERVIEW_ANIMATION_ENTER_FROM_HOME_LAUNCHER) {
1216     auto enter_observer = std::make_unique<EnterAnimationObserver>();
1217     animation_settings.AddObserver(enter_observer.get());
1218     Shell::Get()->overview_controller()->AddEnterAnimationObserver(
1219         std::move(enter_observer));
1220   }
1221 
1222   gfx::RectF item_bounds = target_bounds_;
1223   ::wm::TranslateRectFromScreen(root_window_, &item_bounds);
1224   const gfx::Point origin = gfx::ToRoundedPoint(item_bounds.origin());
1225   item_bounds.set_origin(gfx::PointF());
1226   widget_window->SetBounds(ToStableSizeRoundedRect(item_bounds));
1227 
1228   gfx::Transform label_transform;
1229   label_transform.Translate(origin.x(), origin.y());
1230   widget_window->SetTransform(label_transform);
1231 }
1232 
AnimateOpacity(float opacity,OverviewAnimationType animation_type)1233 void OverviewItem::AnimateOpacity(float opacity,
1234                                   OverviewAnimationType animation_type) {
1235   DCHECK_GE(opacity, 0.f);
1236   DCHECK_LE(opacity, 1.f);
1237   ScopedOverviewTransformWindow::ScopedAnimationSettings animation_settings;
1238   transform_window_.BeginScopedAnimation(animation_type, &animation_settings);
1239   transform_window_.SetOpacity(opacity);
1240 
1241   ScopedOverviewAnimationSettings animation_settings_label(
1242       animation_type, item_widget_->GetNativeWindow());
1243   item_widget_->SetOpacity(opacity);
1244 
1245   if (cannot_snap_widget_) {
1246     aura::Window* cannot_snap_widget_window =
1247         cannot_snap_widget_->GetNativeWindow();
1248     ScopedOverviewAnimationSettings animation_settings_label(
1249         animation_type, cannot_snap_widget_window);
1250     cannot_snap_widget_window->layer()->SetOpacity(opacity);
1251   }
1252 }
1253 
StartDrag()1254 void OverviewItem::StartDrag() {
1255   aura::Window* widget_window = item_widget_->GetNativeWindow();
1256   aura::Window* window = GetWindow();
1257   if (widget_window && widget_window->parent() == window->parent()) {
1258     // TODO(xdai): This might not work if there is an always on top window.
1259     // See crbug.com/733760.
1260     widget_window->parent()->StackChildAtTop(window);
1261     widget_window->parent()->StackChildBelow(widget_window, window);
1262   }
1263 }
1264 
CloseButtonPressed()1265 void OverviewItem::CloseButtonPressed() {
1266   base::RecordAction(
1267       base::UserMetricsAction("WindowSelector_OverviewCloseButton"));
1268   if (Shell::Get()->tablet_mode_controller()->InTabletMode()) {
1269     base::RecordAction(
1270         base::UserMetricsAction("Tablet_WindowCloseFromOverviewButton"));
1271   }
1272   CloseWindow();
1273 }
1274 
HandlePressEvent(const gfx::PointF & location_in_screen,bool from_touch_gesture)1275 void OverviewItem::HandlePressEvent(const gfx::PointF& location_in_screen,
1276                                     bool from_touch_gesture) {
1277   // No need to start the drag again if already in a drag. This can happen if we
1278   // switch fingers midway through a drag.
1279   if (IsDragItem())
1280     return;
1281 
1282   StartDrag();
1283   overview_session_->InitiateDrag(this, location_in_screen,
1284                                   /*is_touch_dragging=*/from_touch_gesture);
1285 }
1286 
HandleReleaseEvent(const gfx::PointF & location_in_screen)1287 void OverviewItem::HandleReleaseEvent(const gfx::PointF& location_in_screen) {
1288   if (!IsDragItem())
1289     return;
1290 
1291   overview_session_->CompleteDrag(this, location_in_screen);
1292 }
1293 
HandleDragEvent(const gfx::PointF & location_in_screen)1294 void OverviewItem::HandleDragEvent(const gfx::PointF& location_in_screen) {
1295   if (!IsDragItem())
1296     return;
1297 
1298   overview_session_->Drag(this, location_in_screen);
1299 }
1300 
HandleLongPressEvent(const gfx::PointF & location_in_screen)1301 void OverviewItem::HandleLongPressEvent(const gfx::PointF& location_in_screen) {
1302   if (!IsDragItem())
1303     return;
1304 
1305   if (ShouldAllowSplitView() || (desks_util::ShouldDesksBarBeCreated() &&
1306                                  overview_grid_->IsDesksBarViewActive())) {
1307     overview_session_->StartNormalDragMode(location_in_screen);
1308   }
1309 }
1310 
HandleFlingStartEvent(const gfx::PointF & location_in_screen,float velocity_x,float velocity_y)1311 void OverviewItem::HandleFlingStartEvent(const gfx::PointF& location_in_screen,
1312                                          float velocity_x,
1313                                          float velocity_y) {
1314   overview_session_->Fling(this, location_in_screen, velocity_x, velocity_y);
1315 }
1316 
HandleTapEvent()1317 void OverviewItem::HandleTapEvent() {
1318   if (!IsDragItem())
1319     return;
1320 
1321   overview_session_->ActivateDraggedWindow();
1322 }
1323 
HandleGestureEndEvent()1324 void OverviewItem::HandleGestureEndEvent() {
1325   if (!IsDragItem())
1326     return;
1327 
1328   // Gesture end events come from a long press getting canceled. Long press
1329   // alters the stacking order, so on gesture end, make sure we restore the
1330   // stacking order on the next reposition.
1331   set_should_restack_on_animation_end(true);
1332   overview_session_->ResetDraggedWindowGesture();
1333 }
1334 
GetWindowsForHomeGesture()1335 aura::Window::Windows OverviewItem::GetWindowsForHomeGesture() {
1336   aura::Window::Windows windows = {item_widget_->GetNativeWindow()};
1337   if (!transform_window_.IsMinimized()) {
1338     for (auto* window : GetTransientTreeIterator(GetWindow()))
1339       windows.push_back(window);
1340   }
1341   if (cannot_snap_widget_)
1342     windows.push_back(cannot_snap_widget_->GetNativeWindow());
1343   return windows;
1344 }
1345 
1346 }  // namespace ash
1347