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/ash_switches.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/property_util.h" 18 #include "ash/wm/window_properties.h" 19 #include "ash/wm/workspace_controller.h" 20 #include "base/command_line.h" 21 #include "ui/aura/client/aura_constants.h" 22 #include "ui/aura/env.h" 23 #include "ui/aura/root_window.h" 24 #include "ui/aura/window.h" 25 #include "ui/aura/window_delegate.h" 26 #include "ui/base/hit_test.h" 27 #include "ui/base/ui_base_types.h" 28 #include "ui/gfx/screen.h" 29 #include "ui/views/widget/widget.h" 30 31 namespace ash { 32 33 namespace { 34 35 // Distance in pixels that the cursor must move past an edge for a window 36 // to move beyond that edge. 37 const int kStickyDistance = 64; 38 39 // How far in pixels we have to drag to undock a window. 40 const int kSnapToDockDistance = 32; 41 42 } // namespace 43 44 DockedWindowResizer::~DockedWindowResizer() { 45 if (destroyed_) 46 *destroyed_ = true; 47 } 48 49 // static 50 DockedWindowResizer* 51 DockedWindowResizer::Create(WindowResizer* next_window_resizer, 52 aura::Window* window, 53 const gfx::Point& location, 54 int window_component, 55 aura::client::WindowMoveSource source) { 56 Details details(window, location, window_component, source); 57 return details.is_resizable ? 58 new DockedWindowResizer(next_window_resizer, details) : NULL; 59 } 60 61 void DockedWindowResizer::Drag(const gfx::Point& location, int event_flags) { 62 last_location_ = location; 63 wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_); 64 bool destroyed = false; 65 if (!did_move_or_resize_) { 66 did_move_or_resize_ = true; 67 StartedDragging(); 68 } 69 gfx::Point offset; 70 gfx::Rect bounds(CalculateBoundsForDrag(details_, location)); 71 bool set_tracked_by_workspace = MaybeSnapToEdge(bounds, &offset); 72 73 // Temporarily clear kWindowTrackedByWorkspaceKey for windows that are snapped 74 // to screen edges e.g. when they are docked. This prevents the windows from 75 // getting snapped to other nearby windows during the drag. 76 bool tracked_by_workspace = GetTrackedByWorkspace(GetTarget()); 77 if (set_tracked_by_workspace) 78 SetTrackedByWorkspace(GetTarget(), false); 79 gfx::Point modified_location(location.x() + offset.x(), 80 location.y() + offset.y()); 81 destroyed_ = &destroyed; 82 next_window_resizer_->Drag(modified_location, event_flags); 83 84 // TODO(varkha): Refactor the way WindowResizer calls into other window 85 // resizers to avoid the awkward pattern here for checking if 86 // next_window_resizer_ destroys the resizer object. 87 if (destroyed) 88 return; 89 destroyed_ = NULL; 90 if (set_tracked_by_workspace) 91 SetTrackedByWorkspace(GetTarget(), tracked_by_workspace); 92 } 93 94 void DockedWindowResizer::CompleteDrag(int event_flags) { 95 // Temporarily clear kWindowTrackedByWorkspaceKey for panels so that they 96 // don't get forced into the workspace that may be shrunken because of docked 97 // windows. 98 bool tracked_by_workspace = GetTrackedByWorkspace(GetTarget()); 99 bool set_tracked_by_workspace = 100 (was_docked_ && GetTarget()->type() == aura::client::WINDOW_TYPE_PANEL); 101 if (set_tracked_by_workspace) 102 SetTrackedByWorkspace(GetTarget(), false); 103 // The root window can change when dragging into a different screen. 104 next_window_resizer_->CompleteDrag(event_flags); 105 if (set_tracked_by_workspace) 106 SetTrackedByWorkspace(GetTarget(), tracked_by_workspace); 107 FinishDragging(); 108 } 109 110 void DockedWindowResizer::RevertDrag() { 111 // Temporarily clear kWindowTrackedByWorkspaceKey for panels so that they 112 // don't get forced into the workspace that may be shrunken because of docked 113 // windows. 114 bool tracked_by_workspace = GetTrackedByWorkspace(GetTarget()); 115 bool set_tracked_by_workspace = 116 (was_docked_ && GetTarget()->type() == aura::client::WINDOW_TYPE_PANEL); 117 if (set_tracked_by_workspace) 118 SetTrackedByWorkspace(GetTarget(), false); 119 next_window_resizer_->RevertDrag(); 120 if (set_tracked_by_workspace) 121 SetTrackedByWorkspace(GetTarget(), tracked_by_workspace); 122 FinishDragging(); 123 } 124 125 aura::Window* DockedWindowResizer::GetTarget() { 126 return next_window_resizer_->GetTarget(); 127 } 128 129 const gfx::Point& DockedWindowResizer::GetInitialLocation() const { 130 return details_.initial_location_in_parent; 131 } 132 133 DockedWindowResizer::DockedWindowResizer(WindowResizer* next_window_resizer, 134 const Details& details) 135 : details_(details), 136 next_window_resizer_(next_window_resizer), 137 dock_layout_(NULL), 138 did_move_or_resize_(false), 139 was_docked_(false), 140 destroyed_(NULL) { 141 DCHECK(details_.is_resizable); 142 aura::Window* dock_container = Shell::GetContainer( 143 details.window->GetRootWindow(), 144 internal::kShellWindowId_DockedContainer); 145 DCHECK(dock_container->id() == internal::kShellWindowId_DockedContainer); 146 dock_layout_ = static_cast<internal::DockedWindowLayoutManager*>( 147 dock_container->layout_manager()); 148 was_docked_ = details.window->parent() == dock_container; 149 } 150 151 bool DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds, 152 gfx::Point* offset) { 153 aura::Window* dock_container = Shell::GetContainer( 154 wm::GetRootWindowAt(last_location_), 155 internal::kShellWindowId_DockedContainer); 156 DCHECK(dock_container->id() == internal::kShellWindowId_DockedContainer); 157 internal::DockedWindowLayoutManager* dock_layout = 158 static_cast<internal::DockedWindowLayoutManager*>( 159 dock_container->layout_manager()); 160 internal::DockedAlignment dock_alignment = dock_layout->CalculateAlignment(); 161 gfx::Rect dock_bounds = ScreenAsh::ConvertRectFromScreen( 162 GetTarget()->parent(), dock_container->GetBoundsInScreen()); 163 // Windows only snap magnetically when they are close to the edge of the 164 // screen and when the cursor is over other docked windows. 165 // When a window being dragged is the last window that was previously 166 // docked it is still allowed to magnetically snap to either side. 167 bool can_snap = was_docked_ || 168 internal::DockedWindowLayoutManager::ShouldWindowDock(GetTarget(), 169 last_location_); 170 if (!can_snap) 171 return false; 172 173 if (dock_alignment == internal::DOCKED_ALIGNMENT_LEFT || 174 (dock_alignment == internal::DOCKED_ALIGNMENT_NONE && was_docked_)) { 175 const int distance = bounds.x() - dock_bounds.x(); 176 if (distance < kSnapToDockDistance && distance > -kStickyDistance) { 177 offset->set_x(-distance); 178 return true; 179 } 180 } 181 if (dock_alignment == internal::DOCKED_ALIGNMENT_RIGHT || 182 (dock_alignment == internal::DOCKED_ALIGNMENT_NONE && was_docked_)) { 183 const int distance = dock_bounds.right() - bounds.right(); 184 if (distance < kSnapToDockDistance && distance > -kStickyDistance) { 185 offset->set_x(distance); 186 return true; 187 } 188 } 189 return false; 190 } 191 192 void DockedWindowResizer::StartedDragging() { 193 // Tell the dock layout manager that we are dragging this window. 194 dock_layout_->StartDragging(GetTarget()); 195 } 196 197 void DockedWindowResizer::FinishDragging() { 198 if (!did_move_or_resize_) 199 return; 200 201 aura::Window* window = GetTarget(); 202 bool should_dock = was_docked_; 203 if ((details_.bounds_change & WindowResizer::kBoundsChange_Repositions) && 204 !(details_.bounds_change & WindowResizer::kBoundsChange_Resizes) && 205 CommandLine::ForCurrentProcess()->HasSwitch( 206 switches::kAshEnableDockedWindows)) { 207 const bool attached_panel = 208 window->type() == aura::client::WINDOW_TYPE_PANEL && 209 window->GetProperty(internal::kPanelAttachedKey); 210 should_dock = !attached_panel && 211 internal::DockedWindowLayoutManager::ShouldWindowDock(window, 212 last_location_); 213 } 214 215 // Check if desired docked state is not same as current. 216 // If not same dock or undock accordingly. 217 if (should_dock != 218 (window->parent()->id() == internal::kShellWindowId_DockedContainer)) { 219 if (should_dock) { 220 aura::Window* dock_container = Shell::GetContainer( 221 window->GetRootWindow(), 222 internal::kShellWindowId_DockedContainer); 223 dock_container->AddChild(window); 224 } else { 225 // Reparent the window back to workspace. 226 // We need to be careful to give SetDefaultParentByRootWindow location in 227 // the right root window (matching the logic in DragWindowResizer) based 228 // on which root window a mouse pointer is in. We want to undock into the 229 // right screen near the edge of a multiscreen setup (based on where the 230 // mouse is). 231 gfx::Rect near_last_location(last_location_, gfx::Size()); 232 // Reparenting will cause Relayout and possible dock shrinking. 233 window->SetDefaultParentByRootWindow(window->GetRootWindow(), 234 near_last_location); 235 } 236 } 237 dock_layout_->FinishDragging(); 238 } 239 240 } // namespace aura 241