Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ash / wm / dock / docked_window_resizer.cc
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/dock/docked_window_resizer.h"
6
7 #include "ash/display/display_controller.h"
8 #include "ash/root_window_controller.h"
9 #include "ash/screen_util.h"
10 #include "ash/shelf/shelf.h"
11 #include "ash/shelf/shelf_types.h"
12 #include "ash/shelf/shelf_widget.h"
13 #include "ash/shell.h"
14 #include "ash/shell_window_ids.h"
15 #include "ash/wm/coordinate_conversion.h"
16 #include "ash/wm/dock/docked_window_layout_manager.h"
17 #include "ash/wm/window_state.h"
18 #include "ash/wm/window_util.h"
19 #include "ash/wm/workspace/magnetism_matcher.h"
20 #include "ash/wm/workspace/workspace_window_resizer.h"
21 #include "base/command_line.h"
22 #include "base/memory/weak_ptr.h"
23 #include "ui/aura/client/aura_constants.h"
24 #include "ui/aura/client/window_tree_client.h"
25 #include "ui/aura/env.h"
26 #include "ui/aura/window.h"
27 #include "ui/aura/window_delegate.h"
28 #include "ui/aura/window_event_dispatcher.h"
29 #include "ui/base/hit_test.h"
30 #include "ui/base/ui_base_types.h"
31 #include "ui/gfx/screen.h"
32 #include "ui/views/widget/widget.h"
33
34 namespace ash {
35 namespace {
36
37 DockedWindowLayoutManager* GetDockedLayoutManagerAtPoint(
38     const gfx::Point& point) {
39   gfx::Display display = ScreenUtil::FindDisplayContainingPoint(point);
40   if (!display.is_valid())
41     return NULL;
42   aura::Window* root = Shell::GetInstance()->display_controller()->
43       GetRootWindowForDisplayId(display.id());
44   aura::Window* dock_container = Shell::GetContainer(
45       root, kShellWindowId_DockedContainer);
46   return static_cast<DockedWindowLayoutManager*>(
47       dock_container->layout_manager());
48 }
49
50 }  // namespace
51
52 DockedWindowResizer::~DockedWindowResizer() {
53 }
54
55 // static
56 DockedWindowResizer*
57 DockedWindowResizer::Create(WindowResizer* next_window_resizer,
58                             wm::WindowState* window_state) {
59   return new DockedWindowResizer(next_window_resizer, window_state);
60 }
61
62 void DockedWindowResizer::Drag(const gfx::Point& location, int event_flags) {
63   last_location_ = location;
64   wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_);
65   if (!did_move_or_resize_) {
66     did_move_or_resize_ = true;
67     StartedDragging();
68   }
69   gfx::Point offset;
70   gfx::Rect bounds(CalculateBoundsForDrag(location));
71   MaybeSnapToEdge(bounds, &offset);
72   gfx::Point modified_location(location);
73   modified_location.Offset(offset.x(), offset.y());
74
75   base::WeakPtr<DockedWindowResizer> resizer(weak_ptr_factory_.GetWeakPtr());
76   next_window_resizer_->Drag(modified_location, event_flags);
77   if (!resizer)
78     return;
79
80   DockedWindowLayoutManager* new_dock_layout =
81       GetDockedLayoutManagerAtPoint(last_location_);
82   if (new_dock_layout && new_dock_layout != dock_layout_) {
83     // The window is being dragged to a new display. If the previous
84     // container is the current parent of the window it will be informed of
85     // the end of drag when the window is reparented, otherwise let the
86     // previous container know the drag is complete. If we told the
87     // window's parent that the drag was complete it would begin
88     // positioning the window.
89     if (is_docked_ && dock_layout_->is_dragged_window_docked())
90       dock_layout_->UndockDraggedWindow();
91     if (dock_layout_ != initial_dock_layout_)
92       dock_layout_->FinishDragging(
93           DOCKED_ACTION_NONE,
94           details().source == aura::client::WINDOW_MOVE_SOURCE_MOUSE ?
95               DOCKED_ACTION_SOURCE_MOUSE : DOCKED_ACTION_SOURCE_TOUCH);
96     is_docked_ = false;
97     dock_layout_ = new_dock_layout;
98     // The window's initial layout manager already knows that the drag is
99     // in progress for this window.
100     if (new_dock_layout != initial_dock_layout_)
101       new_dock_layout->StartDragging(GetTarget());
102   }
103   // Window could get docked by the WorkspaceWindowResizer, update the state.
104   is_docked_ = dock_layout_->is_dragged_window_docked();
105   // Whenever a window is dragged out of the dock it will be auto-sized
106   // in the dock if it gets docked again.
107   if (!is_docked_)
108     was_bounds_changed_by_user_ = false;
109 }
110
111 void DockedWindowResizer::CompleteDrag() {
112   // The root window can change when dragging into a different screen.
113   next_window_resizer_->CompleteDrag();
114   FinishedDragging(aura::client::MOVE_SUCCESSFUL);
115 }
116
117 void DockedWindowResizer::RevertDrag() {
118   next_window_resizer_->RevertDrag();
119   // Restore docked state to what it was before the drag if necessary.
120   if (is_docked_ != was_docked_) {
121     is_docked_ = was_docked_;
122     if (is_docked_)
123       dock_layout_->DockDraggedWindow(GetTarget());
124     else
125       dock_layout_->UndockDraggedWindow();
126   }
127   FinishedDragging(aura::client::MOVE_CANCELED);
128 }
129
130 DockedWindowResizer::DockedWindowResizer(WindowResizer* next_window_resizer,
131                                          wm::WindowState* window_state)
132     : WindowResizer(window_state),
133       next_window_resizer_(next_window_resizer),
134       dock_layout_(NULL),
135       initial_dock_layout_(NULL),
136       did_move_or_resize_(false),
137       was_docked_(false),
138       is_docked_(false),
139       was_bounds_changed_by_user_(window_state->bounds_changed_by_user()),
140       weak_ptr_factory_(this) {
141   DCHECK(details().is_resizable);
142   aura::Window* dock_container = Shell::GetContainer(
143       GetTarget()->GetRootWindow(),
144       kShellWindowId_DockedContainer);
145   dock_layout_ = static_cast<DockedWindowLayoutManager*>(
146       dock_container->layout_manager());
147   initial_dock_layout_ = dock_layout_;
148   was_docked_ = GetTarget()->parent() == dock_container;
149   is_docked_ = was_docked_;
150 }
151
152 void DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds,
153                                           gfx::Point* offset) {
154   // Windows only snap magnetically when they were previously docked.
155   if (!was_docked_)
156     return;
157   DockedAlignment dock_alignment = dock_layout_->CalculateAlignment();
158   gfx::Rect dock_bounds = ScreenUtil::ConvertRectFromScreen(
159       GetTarget()->parent(),
160       dock_layout_->dock_container()->GetBoundsInScreen());
161
162   // Short-range magnetism when retaining docked state. Same constant as in
163   // MagnetismMatcher is used for consistency.
164   const int kSnapToDockDistance = MagnetismMatcher::kMagneticDistance;
165
166   if (dock_alignment == DOCKED_ALIGNMENT_LEFT ||
167       dock_alignment == DOCKED_ALIGNMENT_NONE) {
168     const int distance = bounds.x() - dock_bounds.x();
169     if (distance < kSnapToDockDistance && distance > 0) {
170       offset->set_x(-distance);
171       return;
172     }
173   }
174   if (dock_alignment == DOCKED_ALIGNMENT_RIGHT ||
175       dock_alignment == DOCKED_ALIGNMENT_NONE) {
176     const int distance = dock_bounds.right() - bounds.right();
177     if (distance < kSnapToDockDistance && distance > 0)
178       offset->set_x(distance);
179   }
180 }
181
182 void DockedWindowResizer::StartedDragging() {
183   // During resizing the window width is preserved by DockedwindowLayoutManager.
184   if (is_docked_ &&
185       (details().bounds_change & WindowResizer::kBoundsChange_Resizes)) {
186     window_state_->set_bounds_changed_by_user(true);
187   }
188
189   // Tell the dock layout manager that we are dragging this window.
190   // At this point we are not yet animating the window as it may not be
191   // inside the docked area.
192   dock_layout_->StartDragging(GetTarget());
193   // Reparent workspace windows during the drag to elevate them above workspace.
194   // Other windows for which the DockedWindowResizer is instantiated include
195   // panels and windows that are already docked. Those do not need reparenting.
196   if (GetTarget()->type() != ui::wm::WINDOW_TYPE_PANEL &&
197       GetTarget()->parent()->id() == kShellWindowId_DefaultContainer) {
198     // Reparent the window into the docked windows container in order to get it
199     // on top of other docked windows.
200     aura::Window* docked_container = Shell::GetContainer(
201         GetTarget()->GetRootWindow(),
202         kShellWindowId_DockedContainer);
203     wm::ReparentChildWithTransientChildren(GetTarget(),
204                                            GetTarget()->parent(),
205                                            docked_container);
206   }
207   if (is_docked_)
208     dock_layout_->DockDraggedWindow(GetTarget());
209 }
210
211 void DockedWindowResizer::FinishedDragging(
212     aura::client::WindowMoveResult move_result) {
213   if (!did_move_or_resize_)
214     return;
215   did_move_or_resize_ = false;
216   aura::Window* window = GetTarget();
217   const bool is_attached_panel = window->type() == ui::wm::WINDOW_TYPE_PANEL &&
218                                  window_state_->panel_attached();
219   const bool is_resized =
220       (details().bounds_change & WindowResizer::kBoundsChange_Resizes) != 0;
221
222   // Undock the window if it is not in the normal or minimized state type. This
223   // happens if a user snaps or maximizes a window using a keyboard shortcut
224   // while it is being dragged.
225   if (!window_state_->IsMinimized() && !window_state_->IsNormalStateType())
226     is_docked_ = false;
227
228   // When drag is completed the dragged docked window is resized to the bounds
229   // calculated by the layout manager that conform to other docked windows.
230   if (!is_attached_panel && is_docked_ && !is_resized) {
231     gfx::Rect bounds = ScreenUtil::ConvertRectFromScreen(
232         window->parent(), dock_layout_->dragged_bounds());
233     if (!bounds.IsEmpty() && bounds.width() != window->bounds().width()) {
234       window->SetBounds(bounds);
235     }
236   }
237   // If a window has restore bounds, update the restore origin and width but not
238   // the height (since the height is auto-calculated for the docked windows).
239   if (is_resized && is_docked_ && window_state_->HasRestoreBounds()) {
240     gfx::Rect restore_bounds = window->GetBoundsInScreen();
241     restore_bounds.set_height(
242         window_state_->GetRestoreBoundsInScreen().height());
243     window_state_->SetRestoreBoundsInScreen(restore_bounds);
244   }
245
246   // Check if the window needs to be docked or returned to workspace.
247   DockedAction action = MaybeReparentWindowOnDragCompletion(is_resized,
248                                                             is_attached_panel);
249   dock_layout_->FinishDragging(
250       move_result == aura::client::MOVE_CANCELED ? DOCKED_ACTION_NONE : action,
251       details().source == aura::client::WINDOW_MOVE_SOURCE_MOUSE ?
252           DOCKED_ACTION_SOURCE_MOUSE : DOCKED_ACTION_SOURCE_TOUCH);
253
254   // If we started the drag in one root window and moved into another root
255   // but then canceled the drag we may need to inform the original layout
256   // manager that the drag is finished.
257   if (initial_dock_layout_ != dock_layout_)
258     initial_dock_layout_->FinishDragging(
259         DOCKED_ACTION_NONE,
260         details().source == aura::client::WINDOW_MOVE_SOURCE_MOUSE ?
261             DOCKED_ACTION_SOURCE_MOUSE : DOCKED_ACTION_SOURCE_TOUCH);
262   is_docked_ = false;
263 }
264
265 DockedAction DockedWindowResizer::MaybeReparentWindowOnDragCompletion(
266     bool is_resized, bool is_attached_panel) {
267   aura::Window* window = GetTarget();
268
269   // Check if the window needs to be docked or returned to workspace.
270   DockedAction action = DOCKED_ACTION_NONE;
271   aura::Window* dock_container = Shell::GetContainer(
272       window->GetRootWindow(),
273       kShellWindowId_DockedContainer);
274   if ((is_resized || !is_attached_panel) &&
275       is_docked_ != (window->parent() == dock_container)) {
276     if (is_docked_) {
277       wm::ReparentChildWithTransientChildren(window,
278                                              window->parent(),
279                                              dock_container);
280       action = DOCKED_ACTION_DOCK;
281     } else if (window->parent()->id() == kShellWindowId_DockedContainer) {
282       // Reparent the window back to workspace.
283       // We need to be careful to give ParentWindowWithContext a location in
284       // the right root window (matching the logic in DragWindowResizer) based
285       // on which root window a mouse pointer is in. We want to undock into the
286       // right screen near the edge of a multiscreen setup (based on where the
287       // mouse is).
288       gfx::Rect near_last_location(last_location_, gfx::Size());
289       // Reparenting will cause Relayout and possible dock shrinking.
290       aura::Window* previous_parent = window->parent();
291       aura::client::ParentWindowWithContext(window, window, near_last_location);
292       if (window->parent() != previous_parent) {
293         wm::ReparentTransientChildrenOfChild(window,
294                                              previous_parent,
295                                              window->parent());
296       }
297       action = was_docked_ ? DOCKED_ACTION_UNDOCK : DOCKED_ACTION_NONE;
298     }
299   } else {
300     // Docked state was not changed but still need to record a UMA action.
301     if (is_resized && is_docked_ && was_docked_)
302       action = DOCKED_ACTION_RESIZE;
303     else if (is_docked_ && was_docked_)
304       action = DOCKED_ACTION_REORDER;
305     else if (is_docked_ && !was_docked_)
306       action = DOCKED_ACTION_DOCK;
307     else
308       action = DOCKED_ACTION_NONE;
309   }
310   // When a window is newly docked it is auto-sized by docked layout adjusting
311   // to other windows. If it is just dragged (but not resized) while being
312   // docked it is auto-sized unless it has been resized while being docked
313   // before.
314   if (is_docked_) {
315     wm::GetWindowState(window)->set_bounds_changed_by_user(
316         was_docked_ && (is_resized || was_bounds_changed_by_user_));
317   }
318   return action;
319 }
320
321 }  // namespace ash