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