Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / ash / wm / workspace / workspace_window_resizer.cc
index 93706c8..3e551f3 100644 (file)
@@ -9,14 +9,12 @@
 #include <utility>
 #include <vector>
 
-#include "ash/ash_switches.h"
 #include "ash/display/display_controller.h"
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
 #include "ash/shell.h"
 #include "ash/shell_window_ids.h"
-#include "ash/wm/coordinate_conversion.h"
 #include "ash/wm/default_window_resizer.h"
 #include "ash/wm/dock/docked_window_layout_manager.h"
 #include "ash/wm/dock/docked_window_resizer.h"
 #include "ash/wm/panels/panel_window_resizer.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
+#include "ash/wm/wm_event.h"
 #include "ash/wm/workspace/phantom_window_controller.h"
-#include "ash/wm/workspace/snap_sizer.h"
+#include "ash/wm/workspace/two_step_edge_cycler.h"
 #include "base/command_line.h"
 #include "base/memory/weak_ptr.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/screen_position_client.h"
-#include "ui/aura/root_window.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
+#include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/hit_test.h"
 #include "ui/compositor/layer.h"
 #include "ui/gfx/screen.h"
 #include "ui/gfx/transform.h"
-#include "ui/views/corewm/window_util.h"
+#include "ui/wm/core/coordinate_conversion.h"
+#include "ui/wm/core/window_util.h"
 #include "ui/wm/public/window_types.h"
 
 namespace ash {
@@ -52,10 +52,13 @@ scoped_ptr<WindowResizer> CreateWindowResizer(
   // No need to return a resizer when the window cannot get resized or when a
   // resizer already exists for this window.
   if ((!window_state->CanResize() && window_component != HTCAPTION) ||
-      window_state->window_resizer()) {
+      window_state->drag_details()) {
     return scoped_ptr<WindowResizer>();
   }
 
+  if (window_component == HTCAPTION && !window_state->can_be_dragged())
+    return scoped_ptr<WindowResizer>();
+
   // TODO(varkha): The chaining of window resizers causes some of the logic
   // to be repeated and the logic flow difficult to control. With some windows
   // classes using reparenting during drag operations it becomes challenging to
@@ -69,68 +72,49 @@ scoped_ptr<WindowResizer> CreateWindowResizer(
   // It may be possible to refactor and eliminate chaining.
   WindowResizer* window_resizer = NULL;
 
-  if (!window_state->IsNormalShowState()) {
-    if (window->parent() &&
-        (window->parent()->id() == internal::kShellWindowId_DefaultContainer ||
-         window->parent()->id() == internal::kShellWindowId_DockedContainer ||
-         window->parent()->id() == internal::kShellWindowId_PanelContainer)) {
-      // Allow dragging maximized windows only when dragged by a tab.
-      if (window_component != HTCAPTION || !window_state->is_dragged())
-        return scoped_ptr<WindowResizer>();
-    } else {
-      return scoped_ptr<WindowResizer>();
-    }
-  }
+  if (!window_state->IsNormalOrSnapped())
+    return scoped_ptr<WindowResizer>();
 
-  if (!window_state->CreateDragDetails(
-      window, point_in_parent, window_component, source)) {
+  int bounds_change = WindowResizer::GetBoundsChangeForWindowComponent(
+      window_component);
+  if (bounds_change == WindowResizer::kBoundsChangeDirection_None)
     return scoped_ptr<WindowResizer>();
-  }
+
+  window_state->CreateDragDetails(window, point_in_parent, window_component,
+      source);
   if (window->parent() &&
-      (window->parent()->id() == internal::kShellWindowId_DefaultContainer ||
-       window->parent()->id() == internal::kShellWindowId_DockedContainer ||
-       window->parent()->id() == internal::kShellWindowId_PanelContainer)) {
-    window_resizer = internal::WorkspaceWindowResizer::Create(
-        window_state,
-        std::vector<aura::Window*>());
+      (window->parent()->id() == kShellWindowId_DefaultContainer ||
+       window->parent()->id() == kShellWindowId_DockedContainer ||
+       window->parent()->id() == kShellWindowId_PanelContainer)) {
+    window_resizer = WorkspaceWindowResizer::Create(
+        window_state, std::vector<aura::Window*>());
   } else {
     window_resizer = DefaultWindowResizer::Create(window_state);
   }
-  if (window_resizer) {
-    window_resizer = internal::DragWindowResizer::Create(window_resizer,
-                                                         window_state);
-  }
-  if (window_resizer && window->type() == ui::wm::WINDOW_TYPE_PANEL)
+  window_resizer = DragWindowResizer::Create(window_resizer, window_state);
+  if (window->type() == ui::wm::WINDOW_TYPE_PANEL)
     window_resizer = PanelWindowResizer::Create(window_resizer, window_state);
-  if (switches::UseDockedWindows() &&
-      window_resizer && window->parent() &&
-      !views::corewm::GetTransientParent(window) &&
-      (window->parent()->id() == internal::kShellWindowId_DefaultContainer ||
-       window->parent()->id() == internal::kShellWindowId_DockedContainer ||
-       window->parent()->id() == internal::kShellWindowId_PanelContainer)) {
-    window_resizer = internal::DockedWindowResizer::Create(window_resizer,
-                                                           window_state);
+  if (window_resizer && window->parent() &&
+      !::wm::GetTransientParent(window) &&
+      (window->parent()->id() == kShellWindowId_DefaultContainer ||
+       window->parent()->id() == kShellWindowId_DockedContainer ||
+       window->parent()->id() == kShellWindowId_PanelContainer)) {
+    window_resizer = DockedWindowResizer::Create(window_resizer, window_state);
   }
-  window_state->drag_details()->window_resizer = window_resizer;
   return make_scoped_ptr<WindowResizer>(window_resizer);
 }
 
-namespace internal {
-
 namespace {
 
 // Snapping distance used instead of WorkspaceWindowResizer::kScreenEdgeInset
 // when resizing a window using touchscreen.
-const int kScreenEdgeInsetForTouchResize = 32;
+const int kScreenEdgeInsetForTouchDrag = 32;
+
+// Current instance for use by the WorkspaceWindowResizerTest.
+WorkspaceWindowResizer* instance = NULL;
 
 // Returns true if the window should stick to the edge.
 bool ShouldStickToEdge(int distance_from_edge, int sticky_size) {
-  if (CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kAshEnableStickyEdges)) {
-    // TODO(varkha): Consider keeping snapping behavior for touch drag.
-    return distance_from_edge < 0 &&
-           distance_from_edge > -sticky_size;
-  }
   return distance_from_edge < sticky_size &&
          distance_from_edge > -sticky_size * 2;
 }
@@ -275,11 +259,9 @@ const int WorkspaceWindowResizer::kMinOnscreenHeight = 32;
 // static
 const int WorkspaceWindowResizer::kScreenEdgeInset = 8;
 
-// static
-const int WorkspaceWindowResizer::kStickyDistancePixels = 64;
-
-// static
-WorkspaceWindowResizer* WorkspaceWindowResizer::instance_ = NULL;
+WorkspaceWindowResizer* WorkspaceWindowResizer::GetInstanceForTest() {
+  return instance;
+}
 
 // Represents the width or height of a window with constraints on its minimum
 // and maximum size. 0 represents a lack of a constraint.
@@ -355,8 +337,8 @@ WorkspaceWindowResizer::~WorkspaceWindowResizer() {
     Shell* shell = Shell::GetInstance();
     shell->cursor_manager()->UnlockCursor();
   }
-  if (instance_ == this)
-    instance_ = NULL;
+  if (instance == this)
+    instance = NULL;
 }
 
 // static
@@ -373,19 +355,15 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location_in_parent,
   int sticky_size;
   if (event_flags & ui::EF_CONTROL_DOWN) {
     sticky_size = 0;
-  } else if (CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kAshEnableStickyEdges)) {
-    sticky_size = kStickyDistancePixels;
   } else if ((details().bounds_change & kBoundsChange_Resizes) &&
       details().source == aura::client::WINDOW_MOVE_SOURCE_TOUCH) {
-    sticky_size = kScreenEdgeInsetForTouchResize;
+    sticky_size = kScreenEdgeInsetForTouchDrag;
   } else {
     sticky_size = kScreenEdgeInset;
   }
   // |bounds| is in |GetTarget()->parent()|'s coordinates.
   gfx::Rect bounds = CalculateBoundsForDrag(location_in_parent);
-  if (window_state()->IsNormalShowState())
-    AdjustBoundsForMainWindow(sticky_size, &bounds);
+  AdjustBoundsForMainWindow(sticky_size, &bounds);
 
   if (bounds != GetTarget()->bounds()) {
     if (!did_move_or_resize_) {
@@ -397,7 +375,7 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location_in_parent,
   }
 
   gfx::Point location_in_screen = location_in_parent;
-  wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen);
+  ::wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen);
 
   aura::Window* root = NULL;
   gfx::Display display =
@@ -427,28 +405,26 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location_in_parent,
   } else {
     snap_type_ = SNAP_NONE;
     snap_phantom_window_controller_.reset();
-    snap_sizer_.reset();
+    edge_cycler_.reset();
     SetDraggedWindowDocked(false);
   }
 }
 
 void WorkspaceWindowResizer::CompleteDrag() {
+  if (!did_move_or_resize_)
+    return;
+
   window_state()->set_bounds_changed_by_user(true);
   snap_phantom_window_controller_.reset();
-  if (!did_move_or_resize_ || details().window_component != HTCAPTION)
+
+  // If the window's state type changed over the course of the drag do not snap
+  // the window. This happens when the user minimizes or maximizes the window
+  // using a keyboard shortcut while dragging it.
+  if (window_state()->GetStateType() != details().initial_state_type)
     return;
 
   bool snapped = false;
-  // When the window is not in the normal show state, we do not snap the window.
-  // This happens when the user minimizes or maximizes the window by keyboard
-  // shortcut while dragging it. If the window is the result of dragging a tab
-  // out of a maximized window, it's already in the normal show state when this
-  // is called, so it does not matter.
-  if (window_state()->IsNormalShowState() &&
-      (GetTarget()->type() != ui::wm::WINDOW_TYPE_PANEL ||
-       !window_state()->panel_attached() ||
-       dock_layout_->is_dragged_window_docked()) &&
-      (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) {
+  if (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT) {
     if (!window_state()->HasRestoreBounds()) {
       gfx::Rect initial_bounds = ScreenUtil::ConvertRectToScreen(
           GetTarget()->parent(), details().initial_bounds_in_parent);
@@ -457,19 +433,47 @@ void WorkspaceWindowResizer::CompleteDrag() {
           initial_bounds :
           details().restore_bounds);
     }
-    DCHECK(snap_sizer_);
-    if (window_state()->CanResize() &&
-        !dock_layout_->is_dragged_window_docked()) {
-      Shell::GetInstance()->metrics()->RecordUserMetricsAction(
+    if (!dock_layout_->is_dragged_window_docked()) {
+      UserMetricsRecorder* metrics = Shell::GetInstance()->metrics();
+      // TODO(oshima): Add event source type to WMEvent and move
+      // metrics recording inside WindowState::OnWMEvent.
+      const wm::WMEvent event(snap_type_ == SNAP_LEFT ?
+                              wm::WM_EVENT_SNAP_LEFT : wm::WM_EVENT_SNAP_RIGHT);
+      window_state()->OnWMEvent(&event);
+      metrics->RecordUserMetricsAction(
           snap_type_ == SNAP_LEFT ?
-              UMA_DRAG_MAXIMIZE_LEFT :
-              UMA_DRAG_MAXIMIZE_RIGHT);
-      snap_sizer_->SnapWindowToTargetBounds();
+          UMA_DRAG_MAXIMIZE_LEFT : UMA_DRAG_MAXIMIZE_RIGHT);
       snapped = true;
     }
   }
-  if (window_state()->IsSnapped() && !snapped)
-    window_state()->Restore();
+
+  if (!snapped) {
+    if (window_state()->IsSnapped()) {
+      // Keep the window snapped if the user resizes the window such that the
+      // window has valid bounds for a snapped window. Always unsnap the window
+      // if the user dragged the window via the caption area because doing this
+      // is slightly less confusing.
+      if (details().window_component == HTCAPTION ||
+          !AreBoundsValidSnappedBounds(window_state()->GetStateType(),
+                                       GetTarget()->bounds())) {
+        // Set the window to WINDOW_STATE_TYPE_NORMAL but keep the
+        // window at the bounds that the user has moved/resized the
+        // window to. ClearRestoreBounds() is used instead of
+        // SaveCurrentBoundsForRestore() because most of the restore
+        // logic is skipped because we are still in the middle of a
+        // drag.  TODO(pkotwicz): Fix this and use
+        // SaveCurrentBoundsForRestore().
+        window_state()->ClearRestoreBounds();
+        window_state()->Restore();
+      }
+    } else if (!dock_layout_->is_dragged_window_docked()) {
+      // The window was not snapped and is not snapped. This is a user
+      // resize/drag and so the current bounds should be maintained, clearing
+      // any prior restore bounds. When the window is docked the restore bound
+      // must be kept so the docked state can be reverted properly.
+      window_state()->ClearRestoreBounds();
+    }
+  }
 }
 
 void WorkspaceWindowResizer::RevertDrag() {
@@ -555,7 +559,7 @@ WorkspaceWindowResizer::WorkspaceWindowResizer(
     total_initial_size_ += initial_size;
     total_available += std::max(min_size, initial_size) - min_size;
   }
-  instance_ = this;
+  instance = this;
 }
 
 void WorkspaceWindowResizer::LayoutAttachedWindows(
@@ -760,7 +764,7 @@ bool WorkspaceWindowResizer::UpdateMagnetismWindow(const gfx::Rect& bounds,
       wm::WindowState* other_state = wm::GetWindowState(*i);
       if (other_state->window() == GetTarget() ||
           !other_state->window()->IsVisible() ||
-          !other_state->IsNormalShowState() ||
+          !other_state->IsNormalOrSnapped() ||
           !other_state->CanResize()) {
         continue;
       }
@@ -779,8 +783,8 @@ void WorkspaceWindowResizer::AdjustBoundsForMainWindow(
     int sticky_size,
     gfx::Rect* bounds) {
   gfx::Point last_mouse_location_in_screen = last_mouse_location_;
-  wm::ConvertPointToScreen(GetTarget()->parent(),
-                           &last_mouse_location_in_screen);
+  ::wm::ConvertPointToScreen(GetTarget()->parent(),
+                             &last_mouse_location_in_screen);
   gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(
       last_mouse_location_in_screen);
   gfx::Rect work_area =
@@ -803,10 +807,9 @@ void WorkspaceWindowResizer::AdjustBoundsForMainWindow(
     if (sticky_size > 0) {
       // Possibly stick to edge except when a mouse pointer is outside the
       // work area.
-      if (!(display.work_area().Contains(last_mouse_location_in_screen) &&
-            StickToWorkAreaOnMove(work_area, sticky_size, bounds))) {
-        MagneticallySnapToOtherWindows(bounds);
-      }
+      if (display.work_area().Contains(last_mouse_location_in_screen))
+        StickToWorkAreaOnMove(work_area, sticky_size, bounds);
+      MagneticallySnapToOtherWindows(bounds);
     }
   } else if (sticky_size > 0) {
     MagneticallySnapResizeToOtherWindows(bounds);
@@ -912,54 +915,55 @@ void WorkspaceWindowResizer::UpdateSnapPhantomWindow(const gfx::Point& location,
   snap_type_ = GetSnapType(location);
   if (snap_type_ == SNAP_NONE || snap_type_ != last_type) {
     snap_phantom_window_controller_.reset();
-    snap_sizer_.reset();
+    edge_cycler_.reset();
     if (snap_type_ == SNAP_NONE) {
       SetDraggedWindowDocked(false);
       return;
     }
   }
-  const bool can_dock = dock_layout_->CanDockWindow(GetTarget(), snap_type_);
-  const bool can_snap = window_state()->CanSnap();
+
+  DCHECK(snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT);
+  DockedAlignment desired_alignment = (snap_type_ == SNAP_LEFT) ?
+      DOCKED_ALIGNMENT_LEFT : DOCKED_ALIGNMENT_RIGHT;
+  const bool can_dock =
+      dock_layout_->CanDockWindow(GetTarget(), desired_alignment) &&
+      dock_layout_->GetAlignmentOfWindow(GetTarget()) != DOCKED_ALIGNMENT_NONE;
+  if (!can_dock) {
+    // If the window cannot be docked, undock the window. This may change the
+    // workspace bounds and hence |snap_type_|.
+    SetDraggedWindowDocked(false);
+    snap_type_ = GetSnapType(location);
+  }
+  const bool can_snap = snap_type_ != SNAP_NONE && window_state()->CanSnap();
   if (!can_snap && !can_dock) {
     snap_type_ = SNAP_NONE;
     snap_phantom_window_controller_.reset();
-    snap_sizer_.reset();
-    SetDraggedWindowDocked(false);
+    edge_cycler_.reset();
     return;
   }
-  SnapSizer::Edge edge = (snap_type_ == SNAP_LEFT) ?
-      SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE;
-  if (!snap_sizer_) {
-    snap_sizer_.reset(new SnapSizer(window_state(),
-                                    location,
-                                    edge,
-                                    internal::SnapSizer::OTHER_INPUT));
-  } else {
-    snap_sizer_->Update(location);
-  }
+  if (!edge_cycler_)
+    edge_cycler_.reset(new TwoStepEdgeCycler(location));
+  else
+    edge_cycler_->OnMove(location);
 
   // Update phantom window with snapped or docked guide bounds.
   // Windows that cannot be snapped or are less wide than kMaxDockWidth can get
   // docked without going through a snapping sequence.
   gfx::Rect phantom_bounds;
-  if (can_snap && (!can_dock || GetTarget()->bounds().width() >
-      DockedWindowLayoutManager::kMaxDockWidth)) {
-    phantom_bounds = snap_sizer_->target_bounds();
-  }
   const bool should_dock = can_dock &&
-      (phantom_bounds.IsEmpty() ||
-       snap_sizer_->end_of_sequence() ||
+      (!can_snap ||
+       GetTarget()->bounds().width() <=
+           DockedWindowLayoutManager::kMaxDockWidth ||
+       edge_cycler_->use_second_mode() ||
        dock_layout_->is_dragged_window_docked());
-  SetDraggedWindowDocked(should_dock);
-  snap_type_ = GetSnapType(location);
-  if (dock_layout_->is_dragged_window_docked()) {
+  if (should_dock) {
+    SetDraggedWindowDocked(true);
     phantom_bounds = ScreenUtil::ConvertRectFromScreen(
         GetTarget()->parent(), dock_layout_->dragged_bounds());
-  }
-
-  if (phantom_bounds.IsEmpty()) {
-    snap_phantom_window_controller_.reset();
-    return;
+  } else {
+    phantom_bounds = (snap_type_ == SNAP_LEFT) ?
+        wm::GetDefaultLeftSnappedWindowBoundsInParent(GetTarget()) :
+        wm::GetDefaultRightSnappedWindowBoundsInParent(GetTarget());
   }
 
   if (!snap_phantom_window_controller_) {
@@ -1001,7 +1005,7 @@ void WorkspaceWindowResizer::RestackWindows() {
   }
 }
 
-SnapType WorkspaceWindowResizer::GetSnapType(
+WorkspaceWindowResizer::SnapType WorkspaceWindowResizer::GetSnapType(
     const gfx::Point& location) const {
   // TODO: this likely only wants total display area, not the area of a single
   // display.
@@ -1012,10 +1016,10 @@ SnapType WorkspaceWindowResizer::GetSnapType(
     gfx::Rect display_bounds(ScreenUtil::GetDisplayBoundsInParent(GetTarget()));
     int inset_left = 0;
     if (area.x() == display_bounds.x())
-      inset_left = kScreenEdgeInsetForTouchResize;
+      inset_left = kScreenEdgeInsetForTouchDrag;
     int inset_right = 0;
     if (area.right() == display_bounds.right())
-      inset_right = kScreenEdgeInsetForTouchResize;
+      inset_right = kScreenEdgeInsetForTouchDrag;
     area.Inset(inset_left, 0, inset_right, 0);
   }
   if (location.x() <= area.x())
@@ -1026,9 +1030,7 @@ SnapType WorkspaceWindowResizer::GetSnapType(
 }
 
 void WorkspaceWindowResizer::SetDraggedWindowDocked(bool should_dock) {
-  if (should_dock &&
-      dock_layout_->GetAlignmentOfWindow(GetTarget()) !=
-          DOCKED_ALIGNMENT_NONE) {
+  if (should_dock) {
     if (!dock_layout_->is_dragged_window_docked()) {
       window_state()->set_bounds_changed_by_user(false);
       dock_layout_->DockDraggedWindow(GetTarget());
@@ -1041,5 +1043,17 @@ void WorkspaceWindowResizer::SetDraggedWindowDocked(bool should_dock) {
   }
 }
 
-}  // namespace internal
+bool WorkspaceWindowResizer::AreBoundsValidSnappedBounds(
+    wm::WindowStateType snapped_type,
+    const gfx::Rect& bounds_in_parent) const {
+  DCHECK(snapped_type == wm::WINDOW_STATE_TYPE_LEFT_SNAPPED ||
+         snapped_type == wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED);
+  gfx::Rect snapped_bounds = ScreenUtil::GetDisplayWorkAreaBoundsInParent(
+      GetTarget());
+  if (snapped_type == wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED)
+    snapped_bounds.set_x(snapped_bounds.right() - bounds_in_parent.width());
+  snapped_bounds.set_width(bounds_in_parent.width());
+  return bounds_in_parent == snapped_bounds;
+}
+
 }  // namespace ash