Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ash / wm / dock / docked_window_resizer.cc
blob14b58d408fe53eb12eca8c96538a33b537877fe6
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.
5 #include "ash/wm/dock/docked_window_resizer.h"
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/workspace/magnetism_matcher.h"
19 #include "ash/wm/workspace/workspace_window_resizer.h"
20 #include "base/command_line.h"
21 #include "base/memory/weak_ptr.h"
22 #include "ui/aura/client/aura_constants.h"
23 #include "ui/aura/client/window_tree_client.h"
24 #include "ui/aura/env.h"
25 #include "ui/aura/window.h"
26 #include "ui/aura/window_delegate.h"
27 #include "ui/aura/window_event_dispatcher.h"
28 #include "ui/base/hit_test.h"
29 #include "ui/base/ui_base_types.h"
30 #include "ui/gfx/screen.h"
31 #include "ui/views/widget/widget.h"
32 #include "ui/wm/core/coordinate_conversion.h"
34 namespace ash {
35 namespace {
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());
50 } // namespace
52 DockedWindowResizer::~DockedWindowResizer() {
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);
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();
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());
75 base::WeakPtr<DockedWindowResizer> resizer(weak_ptr_factory_.GetWeakPtr());
76 next_window_resizer_->Drag(modified_location, event_flags);
77 if (!resizer)
78 return;
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());
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;
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);
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();
127 FinishedDragging(aura::client::MOVE_CANCELED);
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_;
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());
162 // Short-range magnetism when retaining docked state. Same constant as in
163 // MagnetismMatcher is used for consistency.
164 const int kSnapToDockDistance = MagnetismMatcher::kMagneticDistance;
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;
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);
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);
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);
207 if (is_docked_)
208 dock_layout_->DockDraggedWindow(GetTarget());
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;
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;
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);
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);
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);
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;
265 DockedAction DockedWindowResizer::MaybeReparentWindowOnDragCompletion(
266 bool is_resized, bool is_attached_panel) {
267 aura::Window* window = GetTarget();
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());
297 action = was_docked_ ? DOCKED_ACTION_UNDOCK : DOCKED_ACTION_NONE;
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;
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_));
318 return action;
321 } // namespace ash