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