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