[go: up one dir, main page]

[Merge to M-96] Desks: Fix the height of desks bar view in zero state

This CL sets the height of desks bar view to zero state height when it's
at zero state. Before this, the height of desks bar view is the same in
zero state and expanded state. This sometimes lead to some weird
behaviors, for example, snapping window vertically in overview mode.

(cherry picked from commit 533fc2438c690193c7f7dc3ec14cabd163968e55)

Test: manual, added a test
Change-Id: I65cc8c9b2e6bb548473348ab708616e51a73e301
Fixed: 1266871, 1262793
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3269608
Commit-Queue: Connie Xu <conniekxu@chromium.org>
Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
Reviewed-by: Min Chen <minch@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#941919}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3283638
Cr-Commit-Position: refs/branch-heads/4664@{#1077}
Cr-Branched-From: 24dc4ee75e01a29d390d43c9c264372a169273a7-refs/heads/main@{#929512}
diff --git a/ash/wm/desks/desk_mini_view_animations.cc b/ash/wm/desks/desk_mini_view_animations.cc
index bf5801a..50773ac 100644
--- a/ash/wm/desks/desk_mini_view_animations.cc
+++ b/ash/wm/desks/desk_mini_view_animations.cc
@@ -10,7 +10,6 @@
 #include "ash/wm/desks/desk_mini_view.h"
 #include "ash/wm/desks/desks_bar_view.h"
 #include "ash/wm/desks/expanded_desks_bar_button.h"
-#include "ash/wm/desks/zero_state_button.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_session.h"
 #include "base/containers/contains.h"
@@ -26,8 +25,6 @@
 
 constexpr gfx::Transform kEndTransform;
 
-constexpr base::TimeDelta kBarBackgroundDuration = base::Milliseconds(200);
-
 constexpr base::TimeDelta kExistingMiniViewsAnimationDuration =
     base::Milliseconds(250);
 
@@ -120,16 +117,14 @@
   RemovedMiniViewAnimation(DeskMiniView* removed_mini_view,
                            DesksBarView* bar_view,
                            const bool to_zero_state)
-      : removed_mini_view_(removed_mini_view),
-        bar_view_(bar_view),
-        to_zero_state_(to_zero_state) {
+      : removed_mini_view_(removed_mini_view) {
     ui::Layer* layer = removed_mini_view_->layer();
     ui::ScopedLayerAnimationSettings settings{layer->GetAnimator()};
     InitScopedAnimationSettings(&settings, kRemovedMiniViewsFadeOutDuration);
     settings.AddObserver(this);
 
-    if (to_zero_state_) {
-      DCHECK(bar_view_);
+    if (to_zero_state) {
+      DCHECK(bar_view);
       layer->SetTransform(GetScaleTransformForView(
           removed_mini_view, bar_view->bounds().CenterPoint().x()));
     } else {
@@ -144,12 +139,6 @@
   ~RemovedMiniViewAnimation() override {
     DCHECK(removed_mini_view_->parent());
     removed_mini_view_->parent()->RemoveChildViewT(removed_mini_view_);
-    if (to_zero_state_) {
-      DCHECK(bar_view_);
-      // Layout the desks bar to make sure the buttons visibilities and button's
-      // text can be updated correctly while going back to zero state.
-      bar_view_->Layout();
-    }
   }
 
   // ui::ImplicitAnimationObserver:
@@ -157,8 +146,69 @@
 
  private:
   DeskMiniView* removed_mini_view_;
-  DesksBarView* bar_view_;
-  const bool to_zero_state_;
+};
+
+// A self-deleting object that performs bounds changes animation for the desks
+// bar while it switches between zero state and expanded state.
+// `is_bounds_animation_on_going_` will be used to help hold Layout calls during
+// the animation. Since Layout is expensive and will be called lots of times
+// during the bounds changes animation without doing this. The object itself
+// will be deleted when the animation is complete.
+class DesksBarBoundsAnimation : public ui::ImplicitAnimationObserver {
+ public:
+  DesksBarBoundsAnimation(DesksBarView* bar_view, bool to_zero_state)
+      : bar_view_(bar_view) {
+    auto* desks_widget = bar_view_->GetWidget();
+    const gfx::Rect current_widget_bounds =
+        desks_widget->GetWindowBoundsInScreen();
+    gfx::Rect target_widget_bounds = current_widget_bounds;
+
+    // When `to_zero_state` is false, desks bar is switching from zero to
+    // expanded state.
+    if (to_zero_state) {
+      target_widget_bounds.set_height(DesksBarView::kZeroStateBarHeight);
+      bar_view_->set_is_bounds_animation_on_going(true);
+    } else {
+      // While switching desks bar from zero state to expanded state, setting
+      // its bounds to its bounds at expanded state directly without animation,
+      // which will trigger Layout and make sure the contents of
+      // desks bar(e.g, desk mini view, new desk button) are at the correct
+      // positions before the animation. And set `is_bounds_animation_on_going_`
+      // to be true, which will help hold Layout until the animation is done.
+      // Then set the bounds of the desks bar back to its bounds at zero state
+      // to start the bounds change animation. See more details at
+      // `is_bounds_animation_on_going_`.
+      target_widget_bounds.set_height(bar_view_->GetExpandedBarHeight(
+          desks_widget->GetNativeWindow()->GetRootWindow()));
+      desks_widget->SetBounds(target_widget_bounds);
+      bar_view_->set_is_bounds_animation_on_going(true);
+      desks_widget->SetBounds(current_widget_bounds);
+    }
+
+    ui::ScopedLayerAnimationSettings settings{
+        desks_widget->GetLayer()->GetAnimator()};
+    InitScopedAnimationSettings(&settings, kZeroStateAnimationDuration);
+    settings.AddObserver(this);
+    desks_widget->SetBounds(target_widget_bounds);
+  }
+
+  DesksBarBoundsAnimation(const DesksBarBoundsAnimation&) = delete;
+  DesksBarBoundsAnimation& operator=(const DesksBarBoundsAnimation&) = delete;
+
+  ~DesksBarBoundsAnimation() override {
+    DCHECK(bar_view_);
+    bar_view_->set_is_bounds_animation_on_going(false);
+    // Layout the desks bar to make sure the buttons visibility will be updated
+    // on desks bar state changes. Also make sure the button's text will be
+    // updated correctly while going back to zero state.
+    bar_view_->Layout();
+  }
+
+  // ui::ImplicitAnimationObserver:
+  void OnImplicitAnimationsCompleted() override { delete this; }
+
+ private:
+  DesksBarView* const bar_view_;
 };
 
 }  // namespace
@@ -166,22 +216,7 @@
 void PerformNewDeskMiniViewAnimation(
     DesksBarView* bar_view,
     const std::vector<DeskMiniView*>& new_mini_views,
-    int shift_x,
-    bool first_time_mini_views) {
-  if (first_time_mini_views) {
-    ui::Layer* layer = bar_view->background_view()->layer();
-    ui::ScopedLayerAnimationSettings settings{layer->GetAnimator()};
-    InitScopedAnimationSettings(&settings, kBarBackgroundDuration);
-    layer->SetOpacity(1);
-
-    // Expect that that bar background is translated off the screen when it's
-    // the first time we're adding mini_views.
-    DCHECK(!layer->GetTargetTransform().IsIdentity());
-
-    layer->SetTransform(gfx::Transform());
-    PositionWindowsInOverview();
-  }
-
+    int shift_x) {
   gfx::Transform begin_transform;
   begin_transform.Translate(shift_x, 0);
 
@@ -234,11 +269,7 @@
 }
 
 void PerformZeroStateToExpandedStateMiniViewAnimation(DesksBarView* bar_view) {
-  ui::Layer* layer = bar_view->background_view()->layer();
-  ui::ScopedLayerAnimationSettings settings{layer->GetAnimator()};
-  InitScopedAnimationSettings(&settings, kZeroStateAnimationDuration);
-  layer->SetTransform(kEndTransform);
-
+  new DesksBarBoundsAnimation(bar_view, /*to_zero_state=*/false);
   const int bar_x_center = bar_view->bounds().CenterPoint().x();
   for (auto* mini_view : bar_view->mini_views())
     ScaleUpAndFadeInView(mini_view, bar_x_center);
@@ -253,19 +284,11 @@
     std::vector<DeskMiniView*> removed_mini_views) {
   for (auto* mini_view : removed_mini_views)
     new RemovedMiniViewAnimation(mini_view, bar_view, /*to_zero_state=*/true);
-
+  new DesksBarBoundsAnimation(bar_view, /*to_zero_state=*/true);
   const gfx::Rect bounds = bar_view->bounds();
   ScaleDownAndFadeOutView(bar_view->expanded_state_new_desk_button(),
                           bounds.CenterPoint().x());
 
-  ui::Layer* layer = bar_view->background_view()->layer();
-  ui::ScopedLayerAnimationSettings settings{layer->GetAnimator()};
-  InitScopedAnimationSettings(&settings, kZeroStateAnimationDuration);
-
-  gfx::Transform transform;
-  transform.Translate(0,
-                      -(bounds.height() - DesksBarView::kZeroStateBarHeight));
-  layer->SetTransform(transform);
   PositionWindowsInOverview();
 }
 
diff --git a/ash/wm/desks/desk_mini_view_animations.h b/ash/wm/desks/desk_mini_view_animations.h
index 105eec29..a6ad07f 100644
--- a/ash/wm/desks/desk_mini_view_animations.h
+++ b/ash/wm/desks/desk_mini_view_animations.h
@@ -27,14 +27,10 @@
 // - It assumes that the new mini_views have already been created, and all
 //   mini_views (new and existing) have already been laid out in their final
 //   positions.
-// - If this is the first time mini_views are being created in the desk bar
-//   view, it will also animate the bar background from top to buttom, and shift
-//   the windows in the overview grid down.
 void PerformNewDeskMiniViewAnimation(
     DesksBarView* bar_view,
     const std::vector<DeskMiniView*>& new_mini_views,
-    int shift_x,
-    bool first_time_mini_views);
+    int shift_x);
 
 // Performs the mini_view removal animation. It is in charge of removing the
 // |removed_mini_view| from the views hierarchy and deleting it.
@@ -58,8 +54,8 @@
 
 // Performs the animation of switching from zero state desks bar to expanded
 // state desks bar. It scales up and fades in the current mini views and the
-// ExpandedDesksBarButton. Also animates the bar's background view from the
-// zero state bar's height to the expanded bar's height.
+// ExpandedDesksBarButton. Also animates the desks bar view from the zero state
+// bar's height to the expanded bar's height.
 void PerformZeroStateToExpandedStateMiniViewAnimation(DesksBarView* bar_view);
 
 // Performs the animation of switching from expanded state desks bar to zero
@@ -67,13 +63,12 @@
 // is remaining. It scales down and fades out the |removed_mini_views| and the
 // ExpandedDesksBarButton. |removed_mini_views| will be removed from the
 // views hierarchy. But the ExpandedDesksBarButton will be kept and set to
-// invisible. It will also animate the bar's background view from the expanded
-// bar's height to zero state bar's height.
+// invisible. It will also animate the desks bar view from the expanded bar's
+// height to zero state bar's height.
 //
 // * Notes:
-// - It assumes the background view, |removed_mini_views| and the
-//   ExpandedDesksBarButton are still laid out at their previous positions
-//   before the bar state transition.
+// - It assumes |removed_mini_views| and the ExpandedDesksBarButton are still
+//   laid out at their previous positions before the bar state transition.
 // - Layout will be done once the animation is completed.
 void PerformExpandedStateToZeroStateMiniViewAnimation(
     DesksBarView* bar_view,
diff --git a/ash/wm/desks/desks_bar_view.cc b/ash/wm/desks/desks_bar_view.cc
index aa2ec698..a02ce43 100644
--- a/ash/wm/desks/desks_bar_view.cc
+++ b/ash/wm/desks/desks_bar_view.cc
@@ -42,6 +42,7 @@
 #include "ui/events/devices/input_device.h"
 #include "ui/events/event_observer.h"
 #include "ui/events/types/event_type.h"
+#include "ui/views/background.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/event_monitor.h"
 #include "ui/views/widget/unique_widget_ptr.h"
@@ -243,14 +244,6 @@
                                  kZeroStateY),
                       desks_templates_button_size));
       }
-
-      // Keep the background view's translation updated while the height of bar
-      // changes. E.g, it could happens while zooming in/out the bar. Note, the
-      // background view is only translated while in zero state.
-      gfx::Transform transform;
-      transform.Translate(
-          0, -(scroll_bounds.height() - DesksBarView::kZeroStateBarHeight));
-      bar_view_->background_view()->layer()->SetTransform(transform);
       return;
     }
 
@@ -313,15 +306,13 @@
 // DesksBarView:
 
 DesksBarView::DesksBarView(OverviewGrid* overview_grid)
-    : background_view_(new views::View), overview_grid_(overview_grid) {
+    : overview_grid_(overview_grid) {
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 
-  background_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-  background_view_->layer()->SetFillsBoundsOpaquely(false);
-
-  AddChildView(background_view_);
-
+  SetBackground(
+      views::CreateSolidBackground(AshColorProvider::Get()->GetShieldLayerColor(
+          AshColorProvider::ShieldLayerType::kShield80)));
   scroll_view_ = AddChildView(std::make_unique<views::ScrollView>());
   scroll_view_->SetPaintToLayer();
   scroll_view_->layer()->SetFillsBoundsOpaquely(false);
@@ -409,7 +400,10 @@
 }
 
 // static
-int DesksBarView::GetBarHeightForWidth(aura::Window* root) {
+constexpr int DesksBarView::kZeroStateBarHeight;
+
+// static
+int DesksBarView::GetExpandedBarHeight(aura::Window* root) {
   return DeskPreviewView::GetHeight(root) + kNonPreviewAllocatedHeight;
 }
 
@@ -685,7 +679,9 @@
 }
 
 void DesksBarView::Layout() {
-  background_view()->SetBoundsRect(bounds());
+  if (is_bounds_animation_on_going_)
+    return;
+
   // Scroll buttons are kept |kScrollViewMinimumHorizontalPadding| away from
   // the edge of the scroll view. So the horizontal padding of the scroll view
   // is set to guarantee enough space for the scroll buttons.
@@ -746,10 +742,10 @@
 
 void DesksBarView::OnThemeChanged() {
   views::View::OnThemeChanged();
-  DCHECK_EQ(ui::LAYER_SOLID_COLOR, background_view_->layer()->type());
-  background_view_->layer()->SetColor(
+  background()->SetNativeControlColor(
       AshColorProvider::Get()->GetShieldLayerColor(
           AshColorProvider::ShieldLayerType::kShield80));
+  SchedulePaint();
 }
 
 void DesksBarView::OnDeskAdded(const Desk* desk) {
@@ -880,7 +876,6 @@
   // This should not be called when a desk is removed.
   DCHECK_LE(mini_views_.size(), desks.size());
 
-  const bool first_time_mini_views = mini_views_.empty();
   const int begin_x = GetFirstMiniViewXOffset();
   std::vector<DeskMiniView*> new_mini_views;
 
@@ -921,20 +916,19 @@
     should_name_nudge_ = false;
   }
 
-  Layout();
-
   if (expanding_bar_view) {
     UpdateDeskButtonsVisibility();
     PerformZeroStateToExpandedStateMiniViewAnimation(this);
     return;
   }
 
+  Layout();
+
   if (initializing_bar_view)
     return;
 
   PerformNewDeskMiniViewAnimation(this, new_mini_views,
-                                  begin_x - GetFirstMiniViewXOffset(),
-                                  first_time_mini_views);
+                                  begin_x - GetFirstMiniViewXOffset());
 }
 
 void DesksBarView::ScrollToShowMiniViewIfNecessary(
diff --git a/ash/wm/desks/desks_bar_view.h b/ash/wm/desks/desks_bar_view.h
index b55a634..cc6da28 100644
--- a/ash/wm/desks/desks_bar_view.h
+++ b/ash/wm/desks/desks_bar_view.h
@@ -44,8 +44,9 @@
 
   static constexpr int kZeroStateBarHeight = 40;
 
-  // Returns the height of the desks bar that exists on |root|.
-  static int GetBarHeightForWidth(aura::Window* root);
+  // Returns the height of the expanded desks bar that exists on `root`. The
+  // height of zero state desks bar is `kZeroStateBarHeight`.
+  static int GetExpandedBarHeight(aura::Window* root);
 
   // Creates and returns the widget that contains the DeskBarView in overview
   // mode. The returned widget has no content view yet, and hasn't been shown
@@ -54,7 +55,9 @@
       aura::Window* root,
       const gfx::Rect& bounds);
 
-  views::View* background_view() const { return background_view_; }
+  void set_is_bounds_animation_on_going(bool value) {
+    is_bounds_animation_on_going_ = value;
+  }
 
   ZeroStateDefaultDeskButton* zero_state_default_desk_button() const {
     return zero_state_default_desk_button_;
@@ -236,10 +239,6 @@
 
   void OnDesksTemplatesButtonPressed();
 
-  // A view that shows a dark gary transparent background that can be animated
-  // when the very first mini_views are created.
-  views::View* background_view_;
-
   // The views representing desks mini_views. They're owned by views hierarchy.
   std::vector<DeskMiniView*> mini_views_;
 
@@ -271,6 +270,13 @@
   // reset.
   bool should_name_nudge_ = false;
 
+  // True if the `DesksBarBoundsAnimation` is started and hasn't finished yet.
+  // It will be used to hold `Layout` until the bounds animation is completed.
+  // `Layout` is expensive and will be called on bounds changes, which means it
+  // will be called lots of times during the bounds changes animation. This is
+  // done to eliminate the unnecessary `Layout` calls during the animation.
+  bool is_bounds_animation_on_going_ = false;
+
   ZeroStateDefaultDeskButton* zero_state_default_desk_button_ = nullptr;
   ZeroStateIconButton* zero_state_new_desk_button_ = nullptr;
   ExpandedDesksBarButton* expanded_state_new_desk_button_ = nullptr;
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 39ade1f..73cdfe3 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -1638,51 +1638,6 @@
   EXPECT_TRUE(DoesActiveDeskContainWindow(window.get()));
 }
 
-TEST_F(DesksTest, DragWindowAtZeroStateToExpandDesksBarView) {
-  auto* controller = DesksController::Get();
-  auto win1 = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
-
-  ASSERT_EQ(1u, controller->desks().size());
-  auto* overview_controller = Shell::Get()->overview_controller();
-  EnterOverview();
-
-  ASSERT_TRUE(overview_controller->InOverviewSession());
-  auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
-  const auto* desks_bar_view = overview_grid->desks_bar_view();
-  ASSERT_TRUE(desks_bar_view);
-
-  // Since we only have one desk, there should be 0 desk mini view and the zero
-  // state default desk button and new desk button should be visible.
-  ASSERT_EQ(0u, desks_bar_view->mini_views().size());
-  auto* zero_state_default_desk_button =
-      desks_bar_view->zero_state_default_desk_button();
-  auto* zero_state_new_desk_button =
-      desks_bar_view->zero_state_new_desk_button();
-  auto* expanded_state_new_desks_button =
-      desks_bar_view->expanded_state_new_desk_button();
-  EXPECT_TRUE(zero_state_default_desk_button->GetVisible());
-  EXPECT_TRUE(zero_state_new_desk_button->GetVisible());
-  EXPECT_FALSE(expanded_state_new_desks_button->GetVisible());
-
-  auto* event_generator = GetEventGenerator();
-
-  // Start dragging the overview item for |win1| without dropping it. This will
-  // lead |desks_bar_view| changing from zero state to expanded state.
-  DragItemToPoint(overview_grid->GetOverviewItemContaining(win1.get()),
-                  zero_state_new_desk_button->GetBoundsInScreen().CenterPoint(),
-                  event_generator, /*by_touch_gestures=*/false, /*drop=*/false);
-  EXPECT_FALSE(zero_state_default_desk_button->GetVisible());
-  EXPECT_FALSE(zero_state_new_desk_button->GetVisible());
-  EXPECT_TRUE(desks_bar_view->expanded_state_new_desk_button()->GetVisible());
-
-  // Now release the drop. This action should not end overview and
-  // |desks_bar_view| should be still at expanded state with one desk mini view.
-  event_generator->ReleaseLeftButton();
-  EXPECT_TRUE(overview_controller->InOverviewSession());
-  EXPECT_TRUE(expanded_state_new_desks_button->GetVisible());
-  EXPECT_EQ(1u, desks_bar_view->mini_views().size());
-}
-
 TEST_F(DesksTest, MruWindowTracker) {
   // Create two desks with two windows in each.
   auto win0 = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index b148f3b..bc2bd5f 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -29,6 +29,7 @@
 #include "ash/wm/desks/desk_mini_view.h"
 #include "ash/wm/desks/desk_name_view.h"
 #include "ash/wm/desks/desks_bar_view.h"
+#include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/desks/expanded_desks_bar_button.h"
 #include "ash/wm/desks/templates/desks_templates_grid_view.h"
@@ -1427,8 +1428,7 @@
     return bounds_;
 
   gfx::Rect effective_bounds = bounds_;
-  effective_bounds.Inset(0, DesksBarView::GetBarHeightForWidth(root_window_), 0,
-                         0);
+  effective_bounds.Inset(0, GetDesksBarHeight(), 0, 0);
   return effective_bounds;
 }
 
@@ -2045,8 +2045,8 @@
 
 gfx::Rect OverviewGrid::GetDesksWidgetBounds() const {
   gfx::Rect desks_widget_screen_bounds = bounds_;
-  desks_widget_screen_bounds.set_height(
-      DesksBarView::GetBarHeightForWidth(root_window_));
+  desks_widget_screen_bounds.set_height(GetDesksBarHeight());
+
   // Shift the widget down to make room for the splitview indicator guidance
   // when it's shown at the top of the screen and no other windows are snapped.
   if (split_view_drag_indicators_ &&
@@ -2077,4 +2077,15 @@
       windows_to_throttle);
 }
 
+int OverviewGrid::GetDesksBarHeight() const {
+  const bool should_show_zero_state_desks_bar =
+      desks_bar_view_ ? desks_bar_view_->IsZeroState()
+                      : !IsShowingDesksTemplatesGrid() &&
+                            DesksController::Get()->GetNumberOfDesks() == 1;
+
+  return should_show_zero_state_desks_bar
+             ? DesksBarView::kZeroStateBarHeight
+             : DesksBarView::GetExpandedBarHeight(root_window_);
+}
+
 }  // namespace ash
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h
index 17f346e..fe3ad77 100644
--- a/ash/wm/overview/overview_grid.h
+++ b/ash/wm/overview/overview_grid.h
@@ -461,6 +461,9 @@
   // Called back when the button to save a desk template is pressed.
   void OnCreateDesksTemplatesButtonPressed();
 
+  // Returns the height of `desks_bar_view_`.
+  int GetDesksBarHeight() const;
+
   // Root window the grid is in.
   aura::Window* root_window_;
 
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index 25008bb..d3e2e17 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -300,8 +300,9 @@
   original_scaled_size_ = item_->target_bounds().size();
   auto* overview_grid = item_->overview_grid();
   // We need to transform desks bar view to expanded state if it's at zero state
-  // when dragging starts.
-  overview_grid->MaybeExpandDesksBarView();
+  // when dragging starts and if `kDragWindowToNewDesk` is enabled.
+  if (features::IsDragWindowToNewDeskEnabled())
+    overview_grid->MaybeExpandDesksBarView();
   overview_grid->AddDropTargetForDraggingFromThisGrid(item_);
 
   if (should_allow_split_view_) {
diff --git a/ash/wm/overview/overview_window_drag_controller_unittest.cc b/ash/wm/overview/overview_window_drag_controller_unittest.cc
index 90eebb4..3c60ec2 100644
--- a/ash/wm/overview/overview_window_drag_controller_unittest.cc
+++ b/ash/wm/overview/overview_window_drag_controller_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/wm/desks/desk_mini_view.h"
 #include "ash/wm/desks/desks_bar_view.h"
 #include "ash/wm/desks/desks_util.h"
+#include "ash/wm/desks/zero_state_button.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_grid.h"
@@ -98,7 +99,83 @@
 
 }  // namespace
 
-using OverviewWindowDragControllerTest = AshTestBase;
+class OverviewWindowDragControllerTest : public AshTestBase {
+ public:
+  OverviewWindowDragControllerTest() = default;
+
+  OverviewWindowDragControllerTest(const OverviewWindowDragControllerTest&) =
+      delete;
+  OverviewWindowDragControllerTest& operator=(
+      const OverviewWindowDragControllerTest&) = delete;
+
+  ~OverviewWindowDragControllerTest() override = default;
+
+  OverviewController* overview_controller() {
+    return Shell::Get()->overview_controller();
+  }
+
+  SplitViewController* split_view_controller() {
+    return SplitViewController::Get(Shell::GetPrimaryRootWindow());
+  }
+
+  OverviewSession* overview_session() {
+    DCHECK(overview_controller()->InOverviewSession());
+    return overview_controller()->overview_session();
+  }
+
+  OverviewWindowDragController* drag_controller() {
+    return overview_session()->window_drag_controller();
+  }
+
+  SplitViewDragIndicators* drag_indicators() {
+    return overview_session()->grid_list()[0]->split_view_drag_indicators();
+  }
+
+  OverviewGrid* overview_grid() {
+    return overview_session()->GetGridWithRootWindow(
+        Shell::GetPrimaryRootWindow());
+  }
+
+  const views::Widget* desks_bar_widget() {
+    DCHECK(overview_grid()->desks_bar_view());
+    return overview_grid()->desks_bar_view()->GetWidget();
+  }
+
+  OverviewItem* GetOverviewItemForWindow(aura::Window* window) {
+    return overview_session()->GetOverviewItemForWindow(window);
+  }
+
+  int GetExpectedDesksBarShiftAmount() {
+    return drag_indicators()->GetLeftHighlightViewBounds().bottom() +
+           kHighlightScreenEdgePaddingDp;
+  }
+
+  void StartDraggingAndValidateDesksBarShifted(aura::Window* window) {
+    // Enter overview mode, and start dragging the window. Validate that the
+    // desks bar widget is shifted down to make room for the indicators.
+    EnterOverview();
+    EXPECT_TRUE(overview_controller()->InOverviewSession());
+    auto* overview_item = GetOverviewItemForWindow(window);
+    ASSERT_TRUE(overview_item);
+    StartDraggingItemBy(overview_item, 30, 200, /*by_touch_gestures=*/false,
+                        GetEventGenerator());
+    ASSERT_TRUE(drag_controller());
+    EXPECT_EQ(OverviewWindowDragController::DragBehavior::kNormalDrag,
+              drag_controller()->current_drag_behavior());
+    ASSERT_TRUE(drag_indicators());
+    EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
+              drag_indicators()->current_window_dragging_state());
+    // Note that it's ok to use screen bounds here since we only have a single
+    // primary display.
+    EXPECT_EQ(GetExpectedDesksBarShiftAmount(),
+              desks_bar_widget()->GetWindowBoundsInScreen().y());
+  }
+
+  int GetDesksBarViewExpandedStateHeight(const DesksBarView* desks_bar_view) {
+    return desks_bar_view->GetExpandedBarHeight(
+        desks_bar_view->GetWidget()->GetNativeWindow()->GetRootWindow());
+  }
+};
 
 TEST_F(OverviewWindowDragControllerTest, NoDragToCloseUsingMouse) {
   auto window = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
@@ -217,9 +294,58 @@
             drag_controller->current_drag_behavior());
 }
 
+TEST_F(OverviewWindowDragControllerTest, DragWindowInPortraitModeWithOneDesk) {
+  // Update the display to make it portrait mode.
+  UpdateDisplay("768x1366");
+  auto window = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
+
+  wm::ActivateWindow(window.get());
+  EXPECT_EQ(window.get(), window_util::GetActiveWindow());
+
+  StartDraggingAndValidateDesksBarShifted(window.get());
+  const auto* desks_bar_view = overview_grid()->desks_bar_view();
+  ASSERT_TRUE(desks_bar_view);
+  // Check the height of the desks bar view. It should have height
+  // `kZeroStateBarHeight` while dragging `window`.
+  EXPECT_EQ(DesksBarView::kZeroStateBarHeight,
+            desks_bar_view->bounds().height());
+
+  // Now drop `window`. Check the height of the desks bar view. It should still
+  // be `kZeroStateBarHeight`.
+  auto* event_generator = GetEventGenerator();
+  event_generator->ReleaseLeftButton();
+  EXPECT_EQ(DesksBarView::kZeroStateBarHeight,
+            desks_bar_view->bounds().height());
+
+  // Click on the zero state new desk button to create a new desk. This
+  // shouldn't end overview mode. The desks bar view should be transformed to
+  // the expanded state.
+  const gfx::Point new_desk_button_center =
+      desks_bar_view->zero_state_new_desk_button()
+          ->GetView()
+          ->GetBoundsInScreen()
+          .CenterPoint();
+  EXPECT_TRUE(overview_controller()->InOverviewSession());
+  event_generator->MoveMouseTo(new_desk_button_center);
+  event_generator->ClickLeftButton();
+  EXPECT_EQ(GetDesksBarViewExpandedStateHeight(desks_bar_view),
+            desks_bar_view->bounds().height());
+
+  // Now remove the newly created desk. This shouldn't end overview mode. The
+  // desks bar view should be transformed to the zero state.
+  auto* controller = Shell::Get()->desks_controller();
+  controller->RemoveDesk(controller->desks().back().get(),
+                         DesksCreationRemovalSource::kButton);
+  EXPECT_TRUE(overview_controller()->InOverviewSession());
+  EXPECT_TRUE(desks_bar_view->IsZeroState());
+  EXPECT_EQ(DesksBarView::kZeroStateBarHeight,
+            desks_bar_view->bounds().height());
+}
+
 // Tests the behavior of dragging a window in portrait tablet mode with virtual
 // desks enabled.
-class OverviewWindowDragControllerDesksPortraitTabletTest : public AshTestBase {
+class OverviewWindowDragControllerDesksPortraitTabletTest
+    : public OverviewWindowDragControllerTest {
  public:
   OverviewWindowDragControllerDesksPortraitTabletTest() = default;
 
@@ -230,40 +356,9 @@
 
   ~OverviewWindowDragControllerDesksPortraitTabletTest() override = default;
 
-  OverviewController* overview_controller() {
-    return Shell::Get()->overview_controller();
-  }
-
-  SplitViewController* split_view_controller() {
-    return SplitViewController::Get(Shell::GetPrimaryRootWindow());
-  }
-
-  OverviewSession* overview_session() {
-    DCHECK(overview_controller()->InOverviewSession());
-    return overview_controller()->overview_session();
-  }
-
-  OverviewWindowDragController* drag_controller() {
-    return overview_session()->window_drag_controller();
-  }
-
-  SplitViewDragIndicators* drag_indicators() {
-    return overview_session()->grid_list()[0]->split_view_drag_indicators();
-  }
-
-  OverviewGrid* overview_grid() {
-    return overview_session()->GetGridWithRootWindow(
-        Shell::GetPrimaryRootWindow());
-  }
-
-  const views::Widget* desks_bar_widget() {
-    DCHECK(overview_grid()->desks_bar_view());
-    return overview_grid()->desks_bar_view()->GetWidget();
-  }
-
-  // AshTestBase:
+  // OverviewWindowDragControllerTest:
   void SetUp() override {
-    AshTestBase::SetUp();
+    OverviewWindowDragControllerTest::SetUp();
 
     // Setup a portrait internal display in tablet mode.
     UpdateDisplay("800x700");
@@ -288,36 +383,6 @@
     desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
     ASSERT_EQ(2u, desks_controller->desks().size());
   }
-
-  OverviewItem* GetOverviewItemForWindow(aura::Window* window) {
-    return overview_session()->GetOverviewItemForWindow(window);
-  }
-
-  int GetExpectedDesksBarShiftAmount() {
-    return drag_indicators()->GetLeftHighlightViewBounds().bottom() +
-           kHighlightScreenEdgePaddingDp;
-  }
-
-  void StartDraggingAndValidateDesksBarShifted(aura::Window* window) {
-    // Enter overview mode, and start dragging the window. Validate that the
-    // desks bar widget is shifted down to make room for the indicators.
-    EnterOverview();
-    EXPECT_TRUE(overview_controller()->InOverviewSession());
-    auto* overview_item = GetOverviewItemForWindow(window);
-    ASSERT_TRUE(overview_item);
-    StartDraggingItemBy(overview_item, 30, 200, /*by_touch_gestures=*/false,
-                        GetEventGenerator());
-    ASSERT_TRUE(drag_controller());
-    EXPECT_EQ(OverviewWindowDragController::DragBehavior::kNormalDrag,
-              drag_controller()->current_drag_behavior());
-    ASSERT_TRUE(drag_indicators());
-    EXPECT_EQ(SplitViewDragIndicators::WindowDraggingState::kFromOverview,
-              drag_indicators()->current_window_dragging_state());
-    // Note that it's ok to use screen bounds here since we only have a single
-    // primary display.
-    EXPECT_EQ(GetExpectedDesksBarShiftAmount(),
-              desks_bar_widget()->GetWindowBoundsInScreen().y());
-  }
 };
 
 TEST_F(OverviewWindowDragControllerDesksPortraitTabletTest,