OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/panels/panel_drag_controller.h" | 5 #include "chrome/browser/ui/panels/panel_drag_controller.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "chrome/browser/ui/panels/detached_panel_collection.h" | 8 #include "chrome/browser/ui/panels/detached_panel_collection.h" |
9 #include "chrome/browser/ui/panels/detached_panel_drag_handler.h" | 9 #include "chrome/browser/ui/panels/detached_panel_drag_handler.h" |
10 #include "chrome/browser/ui/panels/docked_panel_collection.h" | 10 #include "chrome/browser/ui/panels/docked_panel_collection.h" |
11 #include "chrome/browser/ui/panels/docked_panel_drag_handler.h" | 11 #include "chrome/browser/ui/panels/docked_panel_drag_handler.h" |
12 #include "chrome/browser/ui/panels/panel.h" | 12 #include "chrome/browser/ui/panels/panel.h" |
13 #include "chrome/browser/ui/panels/panel_collection.h" | |
14 #include "chrome/browser/ui/panels/panel_manager.h" | 13 #include "chrome/browser/ui/panels/panel_manager.h" |
15 #include "chrome/browser/ui/panels/stacked_panel_collection.h" | 14 #include "chrome/browser/ui/panels/stacked_panel_collection.h" |
16 #include "chrome/browser/ui/panels/stacked_panel_drag_handler.h" | 15 #include "chrome/browser/ui/panels/stacked_panel_drag_handler.h" |
17 | 16 |
18 namespace { | 17 namespace { |
19 | 18 |
20 // The minimum distance that the docked panel gets dragged up in order to | 19 // The minimum distance that the docked panel gets dragged up in order to |
21 // make it free-floating. | 20 // make it free-floating. |
22 const int kDetachDockedPanelThreshold = 100; | 21 const int kDetachDockedPanelThreshold = 100; |
23 | 22 |
24 // Indicates how close the bottom of the detached panel is to the bottom of | 23 // Indicates how close the bottom of the detached panel is to the bottom of |
25 // the docked area such that the detached panel becomes docked. | 24 // the docked area such that the detached panel becomes docked. |
26 const int kDockDetachedPanelThreshold = 30; | 25 const int kDockDetachedPanelThreshold = 30; |
27 | 26 |
| 27 // The minimum distance and overlap (in pixels) between two panels such that |
| 28 // they can be stacked/snapped together. |
| 29 const int kGluePanelsDistanceThreshold = 15; |
| 30 const int kGluePanelsOverlapThreshold = 10; |
| 31 |
| 32 int GetHorizontalOverlap(const gfx::Rect& bounds1, const gfx::Rect& bounds2) { |
| 33 // Check for no overlap. |
| 34 if (bounds1.right() <= bounds2.x() || bounds1.x() >= bounds2.right()) |
| 35 return 0; |
| 36 |
| 37 // Check for complete overlap. |
| 38 if (bounds2.x() <= bounds1.x() && bounds1.right() <= bounds2.right()) |
| 39 return bounds1.width(); |
| 40 |
| 41 if (bounds1.x() <= bounds2.x() && bounds2.right() <= bounds1.right()) |
| 42 return bounds2.width(); |
| 43 |
| 44 // Compute the overlap part. |
| 45 return (bounds1.x() < bounds2.x()) ? (bounds1.right() - bounds2.x()) |
| 46 : (bounds2.right() - bounds1.x()); |
| 47 } |
| 48 |
| 49 int GetVerticalOverlap(const gfx::Rect& bounds1, const gfx::Rect& bounds2) { |
| 50 // Check for no overlap. |
| 51 if (bounds1.bottom() <= bounds2.y() || bounds1.y() >= bounds2.bottom()) |
| 52 return 0; |
| 53 |
| 54 // Check for complete overlap. |
| 55 if (bounds2.y() <= bounds1.y() && bounds1.bottom() <= bounds2.bottom()) |
| 56 return bounds1.height(); |
| 57 |
| 58 if (bounds1.y() <= bounds2.y() && bounds2.bottom() <= bounds1.bottom()) |
| 59 return bounds2.height(); |
| 60 |
| 61 // Compute the overlap part. |
| 62 return (bounds1.y() < bounds2.y()) ? (bounds1.bottom() - bounds2.y()) |
| 63 : (bounds2.bottom() - bounds1.y()); |
| 64 } |
| 65 |
| 66 // Return the vertical distance between the bottom edge of |top_bounds| and |
| 67 // the top edge of |bottom_bounds|. |
| 68 int GetVerticalDistance(const gfx::Rect& top_bounds, |
| 69 const gfx::Rect& bottom_bounds) { |
| 70 return abs(bottom_bounds.y() - top_bounds.bottom()); |
| 71 } |
| 72 |
| 73 // Return the vertical distance between the right edge of |left_bounds| and |
| 74 // the left edge of |right_bounds|. |
| 75 int GetHorizontalDistance(const gfx::Rect& left_bounds, |
| 76 const gfx::Rect& right_bounds) { |
| 77 return abs(right_bounds.x() - left_bounds.right()); |
| 78 } |
| 79 |
| 80 void SetPreviewModeForPanelAndBelow(Panel* panel, bool in_preview) { |
| 81 StackedPanelCollection* stack = panel->stack(); |
| 82 if (stack) { |
| 83 bool panel_found = false; |
| 84 for (StackedPanelCollection::Panels::const_iterator iter = |
| 85 stack->panels().begin(); |
| 86 iter != stack->panels().end(); ++iter) { |
| 87 Panel* current_panel = *iter; |
| 88 if (!panel_found && current_panel != panel) |
| 89 continue; |
| 90 panel_found = true; |
| 91 if (in_preview != current_panel->in_preview_mode()) |
| 92 current_panel->SetPreviewMode(in_preview); |
| 93 } |
| 94 } else { |
| 95 panel->SetPreviewMode(in_preview); |
| 96 } |
| 97 } |
| 98 |
28 } // namespace | 99 } // namespace |
29 | 100 |
30 // static | 101 // static |
31 int PanelDragController::GetDetachDockedPanelThresholdForTesting() { | 102 int PanelDragController::GetDetachDockedPanelThresholdForTesting() { |
32 return kDetachDockedPanelThreshold; | 103 return kDetachDockedPanelThreshold; |
33 } | 104 } |
34 | 105 |
35 // static | 106 // static |
36 int PanelDragController::GetDockDetachedPanelThresholdForTesting() { | 107 int PanelDragController::GetDockDetachedPanelThresholdForTesting() { |
37 return kDockDetachedPanelThreshold; | 108 return kDockDetachedPanelThreshold; |
38 } | 109 } |
39 | 110 |
| 111 // static |
| 112 int PanelDragController::GetGluePanelDistanceThresholdForTesting() { |
| 113 return kGluePanelsDistanceThreshold; |
| 114 } |
| 115 |
| 116 // static |
| 117 int PanelDragController::GetGluePanelOverlapThresholdForTesting() { |
| 118 return kGluePanelsOverlapThreshold; |
| 119 } |
| 120 |
40 PanelDragController::PanelDragController(PanelManager* panel_manager) | 121 PanelDragController::PanelDragController(PanelManager* panel_manager) |
41 : panel_manager_(panel_manager), | 122 : panel_manager_(panel_manager), |
| 123 panel_stacking_enabled_(PanelManager::IsPanelStackingEnabled()), |
42 dragging_panel_(NULL), | 124 dragging_panel_(NULL), |
43 dragging_panel_original_collection_(NULL) { | 125 dragging_panel_original_collection_(NULL) { |
44 } | 126 } |
45 | 127 |
46 PanelDragController::~PanelDragController() { | 128 PanelDragController::~PanelDragController() { |
47 } | 129 } |
48 | 130 |
49 void PanelDragController::StartDragging(Panel* panel, | 131 void PanelDragController::StartDragging(Panel* panel, |
50 const gfx::Point& mouse_location) { | 132 const gfx::Point& mouse_location) { |
51 DCHECK(!dragging_panel_); | 133 DCHECK(!dragging_panel_); |
52 | 134 |
53 offset_from_mouse_location_on_drag_start_ = | 135 offset_from_mouse_location_on_drag_start_ = |
54 mouse_location - panel->GetBounds().origin(); | 136 mouse_location - panel->GetBounds().origin(); |
55 | 137 |
56 dragging_panel_ = panel; | 138 dragging_panel_ = panel; |
57 dragging_panel_->SetPreviewMode(true); | 139 SetPreviewModeForPanelAndBelow(dragging_panel_, true); |
58 | 140 |
59 // Keep track of original collection and placement for the case that the drag | 141 // Keep track of original collection and placement for the case that the drag |
60 // is cancelled. | 142 // is cancelled. |
61 dragging_panel_original_collection_ = dragging_panel_->collection(); | 143 dragging_panel_original_collection_ = dragging_panel_->collection(); |
62 dragging_panel_original_collection_->SavePanelPlacement(dragging_panel_); | 144 dragging_panel_original_collection_->SavePanelPlacement(dragging_panel_); |
63 } | 145 } |
64 | 146 |
65 void PanelDragController::Drag(const gfx::Point& mouse_location) { | 147 void PanelDragController::Drag(const gfx::Point& mouse_location) { |
66 if (!dragging_panel_) | 148 if (!dragging_panel_) |
67 return; | 149 return; |
68 | 150 |
69 gfx::Point target_position = GetPanelPositionForMouseLocation(mouse_location); | 151 gfx::Point target_position = GetPanelPositionForMouseLocation(mouse_location); |
70 | 152 |
71 // Check if the dragging panel can be detached or docked. | 153 if (panel_stacking_enabled_) { |
72 if (TryDetach(target_position)) | 154 // Check if the dragging panel can be moved out the stack. Note that this |
73 return; | 155 // has to be done first and we should continue processing it for the case |
74 if (TryDock(target_position)) | 156 // that the drag also triggers stacking and docking. |
75 return; | 157 // Note that the panel can only be unstacked from top or bottom. So if |
| 158 // unstacking from top succeeds, there is no need to check for unstacking |
| 159 // from bottom. |
| 160 if (!TryUnstackFromTop(target_position)) |
| 161 TryUnstackFromBottom(target_position); |
| 162 |
| 163 // Check if the dragging panel can stack with other panel or stack. |
| 164 TryStack(target_position); |
| 165 } |
| 166 |
| 167 // Check if the dragging panel can be docked. |
| 168 TryDock(target_position); |
| 169 |
| 170 // Check if the dragging panel can be detached. |
| 171 TryDetach(target_position); |
| 172 |
| 173 // Check if the dragging panel can snap to other panel. |
| 174 if (panel_stacking_enabled_) |
| 175 TrySnap(&target_position); |
76 | 176 |
77 // At last, handle the drag via its collection's specific handler. | 177 // At last, handle the drag via its collection's specific handler. |
78 switch (dragging_panel_->collection()->type()) { | 178 switch (dragging_panel_->collection()->type()) { |
79 case PanelCollection::DOCKED: | 179 case PanelCollection::DOCKED: |
80 DockedPanelDragHandler::HandleDrag(dragging_panel_, target_position); | 180 DockedPanelDragHandler::HandleDrag(dragging_panel_, target_position); |
81 break; | 181 break; |
82 case PanelCollection::DETACHED: | 182 case PanelCollection::DETACHED: |
83 DetachedPanelDragHandler::HandleDrag(dragging_panel_, target_position); | 183 DetachedPanelDragHandler::HandleDrag(dragging_panel_, target_position); |
84 break; | 184 break; |
85 case PanelCollection::STACKED: | 185 case PanelCollection::STACKED: |
86 StackedPanelDragHandler::HandleDrag(dragging_panel_, target_position); | 186 StackedPanelDragHandler::HandleDrag( |
| 187 dragging_panel_, |
| 188 target_position, |
| 189 dragging_panel_->collection() == dragging_panel_original_collection_); |
87 break; | 190 break; |
88 default: | 191 default: |
89 NOTREACHED(); | 192 NOTREACHED(); |
90 break; | 193 break; |
91 } | 194 } |
92 } | 195 } |
93 | 196 |
94 void PanelDragController::EndDragging(bool cancelled) { | 197 void PanelDragController::EndDragging(bool cancelled) { |
95 if (!dragging_panel_) | 198 if (!dragging_panel_) |
96 return; | 199 return; |
97 | 200 |
98 PanelCollection* current_collection = dragging_panel_->collection(); | 201 PanelCollection* current_collection = dragging_panel_->collection(); |
99 if (cancelled) { | 202 if (cancelled) { |
100 // Restore the dragging panel to its original collection if needed. | 203 // Restore the dragging panel to its original collection if needed. |
101 // Note that the bounds of dragging panel is updated later by calling | 204 // Note that the bounds of dragging panel is updated later by calling |
102 // RestorePanelToSavedPlacement. | 205 // RestorePanelToSavedPlacement. |
103 if (current_collection != dragging_panel_original_collection_) { | 206 if (current_collection != dragging_panel_original_collection_) { |
104 PanelCollection::PositioningMask positioning_mask = | 207 PanelCollection::PositioningMask positioning_mask = |
105 static_cast<PanelCollection::PositioningMask>( | 208 static_cast<PanelCollection::PositioningMask>( |
106 PanelCollection::DEFAULT_POSITION | | 209 PanelCollection::DEFAULT_POSITION | |
107 PanelCollection::DO_NOT_UPDATE_BOUNDS); | 210 PanelCollection::DO_NOT_UPDATE_BOUNDS); |
108 panel_manager_->MovePanelToCollection( | 211 MovePanelAndBelowToCollection(dragging_panel_, |
109 dragging_panel_, | 212 dragging_panel_original_collection_, |
110 dragging_panel_original_collection_, | 213 positioning_mask); |
111 positioning_mask); | |
112 } | 214 } |
113 | 215 |
114 // End the preview mode. | 216 // End the preview mode. |
115 dragging_panel_->SetPreviewMode(false); | 217 SetPreviewModeForPanelAndBelow(dragging_panel_, false); |
116 | 218 |
117 // Restore the dragging panel to its original placement. | 219 // Restore the dragging panel to its original placement. |
118 dragging_panel_original_collection_->RestorePanelToSavedPlacement(); | 220 dragging_panel_original_collection_->RestorePanelToSavedPlacement(); |
119 } else { | 221 } else { |
120 // The saved placement is no longer needed. | 222 // The saved placement is no longer needed. |
121 dragging_panel_original_collection_->DiscardSavedPanelPlacement(); | 223 dragging_panel_original_collection_->DiscardSavedPanelPlacement(); |
122 | 224 |
| 225 // Finalizing the drag is only needed for the stack. |
| 226 if (current_collection->type() == PanelCollection::STACKED) |
| 227 StackedPanelDragHandler::FinalizeDrag(dragging_panel_); |
| 228 |
123 // End the preview mode. | 229 // End the preview mode. |
124 dragging_panel_->SetPreviewMode(false); | 230 SetPreviewModeForPanelAndBelow(dragging_panel_, false); |
125 | 231 |
126 // This could cause the panel to be moved to its finalized position. | 232 // This could cause the panel to be moved to its finalized position. |
127 current_collection->RefreshLayout(); | 233 current_collection->RefreshLayout(); |
128 } | 234 } |
129 | 235 |
| 236 // If the origianl collection is a stack and it becomes empty, remove it. |
| 237 if (dragging_panel_original_collection_->type() == PanelCollection::STACKED) { |
| 238 StackedPanelCollection* original_stack = |
| 239 static_cast<StackedPanelCollection*>( |
| 240 dragging_panel_original_collection_); |
| 241 if (original_stack->num_panels() == 0) |
| 242 panel_manager_->RemoveStack(original_stack); |
| 243 } |
| 244 |
130 dragging_panel_ = NULL; | 245 dragging_panel_ = NULL; |
131 } | 246 } |
132 | 247 |
133 void PanelDragController::OnPanelClosed(Panel* panel) { | 248 void PanelDragController::OnPanelClosed(Panel* panel) { |
134 // Abort the drag only if the panel being closed is currently being dragged. | 249 // Abort the drag only if the panel being closed is currently being dragged. |
135 if (dragging_panel_ != panel) | 250 if (dragging_panel_ != panel) |
136 return; | 251 return; |
137 | 252 |
138 dragging_panel_original_collection_->DiscardSavedPanelPlacement(); | 253 dragging_panel_original_collection_->DiscardSavedPanelPlacement(); |
139 dragging_panel_original_collection_ = NULL; | 254 dragging_panel_original_collection_ = NULL; |
(...skipping 14 matching lines...) Expand all Loading... |
154 int display_area_top_position = panel_manager_->display_area().y(); | 269 int display_area_top_position = panel_manager_->display_area().y(); |
155 if (panel_manager_->display_settings_provider()-> | 270 if (panel_manager_->display_settings_provider()-> |
156 GetPrimaryScreenArea().Contains(mouse_location) && | 271 GetPrimaryScreenArea().Contains(mouse_location) && |
157 target_position.y() < display_area_top_position) { | 272 target_position.y() < display_area_top_position) { |
158 target_position.set_y(display_area_top_position); | 273 target_position.set_y(display_area_top_position); |
159 } | 274 } |
160 | 275 |
161 return target_position; | 276 return target_position; |
162 } | 277 } |
163 | 278 |
164 bool PanelDragController::TryDetach(const gfx::Point& target_position) { | 279 void PanelDragController::TryDetach(const gfx::Point& target_position) { |
165 // It has to come from the docked collection. | 280 // It has to come from the docked collection. |
166 if (dragging_panel_->collection()->type() != PanelCollection::DOCKED) | 281 if (dragging_panel_->collection()->type() != PanelCollection::DOCKED) |
167 return false; | 282 return; |
168 | 283 |
169 // The minimized docked panel is not allowed to detach. | 284 // The minimized docked panel is not allowed to detach. |
170 if (dragging_panel_->IsMinimized()) | 285 if (dragging_panel_->IsMinimized()) |
171 return false; | 286 return; |
172 | 287 |
173 // Panels in the detached collection are always at their full size. | 288 // Panels in the detached collection are always at their full size. |
174 gfx::Rect target_panel_bounds(target_position, | 289 gfx::Rect target_bounds(target_position, |
175 dragging_panel_->full_size()); | 290 dragging_panel_->full_size()); |
176 | 291 |
177 // The panel should be dragged up high enough to pass certain threshold. | 292 // The panel should be dragged up high enough to pass certain threshold. |
178 if (panel_manager_->docked_collection()->display_area().bottom() - | 293 if (panel_manager_->docked_collection()->display_area().bottom() - |
179 target_panel_bounds.bottom() < kDetachDockedPanelThreshold) | 294 target_bounds.bottom() < kDetachDockedPanelThreshold) |
180 return false; | 295 return; |
181 | 296 |
182 // Apply new panel bounds. | 297 // Apply new panel bounds. |
183 dragging_panel_->SetPanelBoundsInstantly(target_panel_bounds); | 298 dragging_panel_->SetPanelBoundsInstantly(target_bounds); |
184 | 299 |
185 // Move the panel to new collection. | 300 // Move the panel to new collection. |
186 panel_manager_->MovePanelToCollection(dragging_panel_, | 301 panel_manager_->MovePanelToCollection(dragging_panel_, |
187 panel_manager_->detached_collection(), | 302 panel_manager_->detached_collection(), |
188 PanelCollection::KNOWN_POSITION); | 303 PanelCollection::KNOWN_POSITION); |
189 | |
190 return true; | |
191 } | 304 } |
192 | 305 |
193 bool PanelDragController::TryDock(const gfx::Point& target_position) { | 306 void PanelDragController::TryDock(const gfx::Point& target_position) { |
194 // It has to come from the detached collection. | 307 // It has to come from the detached collection. |
195 if (dragging_panel_->collection()->type() != PanelCollection::DETACHED) | 308 if (dragging_panel_->collection()->type() != PanelCollection::DETACHED) |
196 return false; | 309 return; |
197 | 310 |
198 gfx::Rect target_panel_bounds(target_position, | 311 gfx::Rect target_bounds(target_position, |
199 dragging_panel_->GetBounds().size()); | 312 dragging_panel_->GetBounds().size()); |
200 | 313 |
201 // If the target panel bounds is outside the main display area where the | 314 // If the target panel bounds is outside the main display area where the |
202 // docked collection resides, as in the multi-monitor scenario, we want it to | 315 // docked collection resides, as in the multi-monitor scenario, we want it to |
203 // be still free-floating. | 316 // be still free-floating. |
204 gfx::Rect display_area = panel_manager_->display_settings_provider()-> | 317 gfx::Rect display_area = panel_manager_->display_settings_provider()-> |
205 GetDisplayArea(); | 318 GetDisplayArea(); |
206 if (!display_area.Intersects(target_panel_bounds)) | 319 if (!display_area.Intersects(target_bounds)) |
207 return false; | 320 return; |
208 | 321 |
209 // The bottom of the panel should come very close to or fall below the bottom | 322 // The bottom of the panel should come very close to or fall below the bottom |
210 // of the docked area. | 323 // of the docked area. |
211 if (panel_manager_->docked_collection()->display_area().bottom() - | 324 if (panel_manager_->docked_collection()->display_area().bottom() - |
212 target_panel_bounds.bottom() > kDockDetachedPanelThreshold) | 325 target_bounds.bottom() > kDockDetachedPanelThreshold) |
213 return false; | 326 return; |
214 | 327 |
215 // Apply new panel bounds. | 328 // Apply new panel bounds. |
216 dragging_panel_->SetPanelBoundsInstantly(target_panel_bounds); | 329 dragging_panel_->SetPanelBoundsInstantly(target_bounds); |
217 | 330 |
218 // Move the panel to new collection. | 331 // Move the panel to new collection. |
219 panel_manager_->MovePanelToCollection(dragging_panel_, | 332 panel_manager_->MovePanelToCollection(dragging_panel_, |
220 panel_manager_->docked_collection(), | 333 panel_manager_->docked_collection(), |
221 PanelCollection::KNOWN_POSITION); | 334 PanelCollection::KNOWN_POSITION); |
| 335 } |
| 336 |
| 337 void PanelDragController::TryStack(const gfx::Point& target_position) { |
| 338 gfx::Rect target_bounds; |
| 339 GlueEdge target_edge; |
| 340 Panel* target_panel = FindPanelToGlue(target_position, |
| 341 STACK, |
| 342 &target_bounds, |
| 343 &target_edge); |
| 344 if (!target_panel) |
| 345 return; |
| 346 |
| 347 StackedPanelCollection* dragging_stack = dragging_panel_->stack(); |
| 348 |
| 349 // Move the panel (and all the panels below if in a stack) to the new |
| 350 // position. |
| 351 gfx::Vector2d delta = |
| 352 target_bounds.origin() - dragging_panel_->GetBounds().origin(); |
| 353 if (dragging_stack) |
| 354 dragging_stack->MoveAllDraggingPanelsInstantly(delta); |
| 355 else |
| 356 dragging_panel_->MoveByInstantly(delta); |
| 357 |
| 358 // If the panel to stack with is not in a stack, create it now. |
| 359 StackedPanelCollection* target_stack = target_panel->stack(); |
| 360 if (!target_stack) { |
| 361 target_stack = panel_manager_->CreateStack(); |
| 362 panel_manager_->MovePanelToCollection(target_panel, |
| 363 target_stack, |
| 364 PanelCollection::DEFAULT_POSITION); |
| 365 } |
| 366 |
| 367 // Move the panel to new collection. |
| 368 // Note that we don't want to refresh the layout now because when we add |
| 369 // a panel to top of other panel, we don't want the bottom panel to change |
| 370 // its width to be same as top panel now. |
| 371 PanelCollection::PositioningMask positioning_mask = |
| 372 static_cast<PanelCollection::PositioningMask>( |
| 373 PanelCollection::NO_LAYOUT_REFRESH | |
| 374 (target_edge == TOP_EDGE ? PanelCollection::TOP_POSITION |
| 375 : PanelCollection::DEFAULT_POSITION)); |
| 376 MovePanelAndBelowToCollection(dragging_panel_, |
| 377 target_stack, |
| 378 positioning_mask); |
| 379 } |
| 380 |
| 381 // Check if a panel or a set of stacked panels (being dragged together from a |
| 382 // stack) can be dragged away from the panel below such that the former panel(s) |
| 383 // are not in the same stack as the latter panel. |
| 384 bool PanelDragController::TryUnstackFromTop(const gfx::Point& target_position) { |
| 385 // It has to be stacked. |
| 386 StackedPanelCollection* dragging_stack = dragging_panel_->stack(); |
| 387 if (!dragging_stack) |
| 388 return false; |
| 389 |
| 390 // Unstacking from top only happens when a panel/stack stacks to the top of |
| 391 // another panel and then moves away while the drag is still in progress. |
| 392 if (dragging_panel_ != dragging_stack->top_panel() || |
| 393 dragging_stack == dragging_panel_original_collection_) |
| 394 return false; |
| 395 |
| 396 // Count the number of panels that might need to unstack. |
| 397 Panel* last_panel_to_unstack = NULL; |
| 398 Panel* panel_below_last_panel_to_unstack = NULL; |
| 399 int num_panels_to_unstack = 0; |
| 400 for (StackedPanelCollection::Panels::const_iterator iter = |
| 401 dragging_stack->panels().begin(); |
| 402 iter != dragging_stack->panels().end(); ++iter) { |
| 403 if (!(*iter)->in_preview_mode()) { |
| 404 panel_below_last_panel_to_unstack = *iter; |
| 405 break; |
| 406 } |
| 407 num_panels_to_unstack++; |
| 408 last_panel_to_unstack = *iter; |
| 409 } |
| 410 DCHECK_GE(num_panels_to_unstack, 1); |
| 411 if (num_panels_to_unstack == dragging_stack->num_panels()) |
| 412 return false; |
| 413 |
| 414 gfx::Vector2d delta = target_position - dragging_panel_->GetBounds().origin(); |
| 415 |
| 416 // The last panel to unstack should be dragged far enough from its below |
| 417 // panel. |
| 418 gfx::Rect target_bounds = last_panel_to_unstack->GetBounds(); |
| 419 target_bounds.Offset(delta); |
| 420 gfx::Rect below_panel_bounds = panel_below_last_panel_to_unstack->GetBounds(); |
| 421 if (GetVerticalDistance(target_bounds, below_panel_bounds) < |
| 422 kGluePanelsDistanceThreshold && |
| 423 GetHorizontalOverlap(target_bounds, below_panel_bounds) > |
| 424 kGluePanelsOverlapThreshold) { |
| 425 return false; |
| 426 } |
| 427 |
| 428 // Move the panel (and all the panels below if in a stack) to the new |
| 429 // position. |
| 430 for (StackedPanelCollection::Panels::const_iterator iter = |
| 431 dragging_stack->panels().begin(); |
| 432 iter != dragging_stack->panels().end(); ++iter) { |
| 433 if (!(*iter)->in_preview_mode()) |
| 434 break; |
| 435 (*iter)->MoveByInstantly(delta); |
| 436 } |
| 437 |
| 438 int num_panels_in_stack = dragging_stack->num_panels(); |
| 439 DCHECK_GE(num_panels_in_stack, 2); |
| 440 |
| 441 // When a panel is removed from its stack, we always make it detached. If it |
| 442 // indeed should go to the docked collection, the subsequent TryDock will then |
| 443 // move it from the detached collection to the docked collection. |
| 444 DetachedPanelCollection* detached_collection = |
| 445 panel_manager_->detached_collection(); |
| 446 |
| 447 // If there're only 2 panels in the stack, both panels should move out of the |
| 448 // stack and the stack should be removed. |
| 449 if (num_panels_in_stack == 2) { |
| 450 DCHECK_EQ(1, num_panels_to_unstack); |
| 451 MovePanelAndBelowToCollection(dragging_panel_, |
| 452 detached_collection, |
| 453 PanelCollection::KNOWN_POSITION); |
| 454 return true; |
| 455 } |
| 456 |
| 457 DCHECK_GE(num_panels_in_stack, 3); |
| 458 |
| 459 // If only one panel (top panel) needs to unstack, move it out of the stack. |
| 460 if (num_panels_to_unstack == 1) { |
| 461 panel_manager_->MovePanelToCollection(dragging_panel_, |
| 462 panel_manager_->detached_collection(), |
| 463 PanelCollection::KNOWN_POSITION); |
| 464 return true; |
| 465 } |
| 466 |
| 467 // If all the panels except the bottom panel need to unstack, simply move |
| 468 // bottom panel out of the stack. |
| 469 if (num_panels_in_stack - num_panels_to_unstack == 1) { |
| 470 panel_manager_->MovePanelToCollection(dragging_stack->bottom_panel(), |
| 471 panel_manager_->detached_collection(), |
| 472 PanelCollection::KNOWN_POSITION); |
| 473 return true; |
| 474 } |
| 475 |
| 476 // Otherwise, move all unstacked panels to a new stack. |
| 477 // Note that all the panels to move should be copied to a local list first |
| 478 // because the stack collection will be modified during the move. |
| 479 std::list<Panel*> panels_to_move; |
| 480 for (StackedPanelCollection::Panels::const_iterator iter = |
| 481 dragging_stack->panels().begin(); |
| 482 iter != dragging_stack->panels().end(); ++iter) { |
| 483 Panel* panel = *iter; |
| 484 if (!panel->in_preview_mode()) |
| 485 break; |
| 486 panels_to_move.push_back(panel); |
| 487 } |
| 488 StackedPanelCollection* new_stack = panel_manager_->CreateStack(); |
| 489 for (std::list<Panel*>::const_iterator iter = panels_to_move.begin(); |
| 490 iter != panels_to_move.end(); ++iter) { |
| 491 panel_manager_->MovePanelToCollection(*iter, |
| 492 new_stack, |
| 493 PanelCollection::KNOWN_POSITION); |
| 494 } |
222 | 495 |
223 return true; | 496 return true; |
224 } | 497 } |
| 498 |
| 499 // Check if a panel or a set of stacked panels (being dragged together from a |
| 500 // stack) can be dragged away from the panel above such that the former panel(s) |
| 501 // are not in the same stack as the latter panel. |
| 502 bool PanelDragController::TryUnstackFromBottom( |
| 503 const gfx::Point& target_position) { |
| 504 // It has to be stacked. |
| 505 StackedPanelCollection* dragging_stack = dragging_panel_->stack(); |
| 506 if (!dragging_stack) |
| 507 return false; |
| 508 |
| 509 // It cannot be the top panel. |
| 510 if (dragging_panel_ == dragging_stack->top_panel()) |
| 511 return false; |
| 512 |
| 513 DCHECK_GT(dragging_stack->num_panels(), 1); |
| 514 |
| 515 gfx::Rect target_bounds(target_position, dragging_panel_->GetBounds().size()); |
| 516 |
| 517 // The panel should be dragged far enough from its above panel. |
| 518 Panel* above_panel = dragging_stack->GetPanelAbove(dragging_panel_); |
| 519 DCHECK(above_panel); |
| 520 gfx::Rect above_panel_bounds = above_panel->GetBounds(); |
| 521 if (GetVerticalDistance(above_panel_bounds, target_bounds) < |
| 522 kGluePanelsDistanceThreshold && |
| 523 GetHorizontalOverlap(above_panel_bounds, target_bounds) > |
| 524 kGluePanelsOverlapThreshold) { |
| 525 return false; |
| 526 } |
| 527 |
| 528 // Move the panel (and all the panels below if in a stack) to the new |
| 529 // position. |
| 530 gfx::Vector2d delta = target_position - dragging_panel_->GetBounds().origin(); |
| 531 if (dragging_stack) |
| 532 dragging_stack->MoveAllDraggingPanelsInstantly(delta); |
| 533 else |
| 534 dragging_panel_->MoveByInstantly(delta); |
| 535 |
| 536 // If there're only 2 panels in the stack, both panels should move out the |
| 537 // stack and the stack should be removed. |
| 538 DetachedPanelCollection* detached_collection = |
| 539 panel_manager_->detached_collection(); |
| 540 if (dragging_stack->num_panels() == 2) { |
| 541 MovePanelAndBelowToCollection(dragging_stack->top_panel(), |
| 542 detached_collection, |
| 543 PanelCollection::KNOWN_POSITION); |
| 544 return true; |
| 545 } |
| 546 |
| 547 // There're at least 3 panels. |
| 548 DCHECK_GE(dragging_stack->num_panels(), 3); |
| 549 |
| 550 // If the dragging panel is bottom panel, move it out of the stack. |
| 551 if (dragging_panel_ == dragging_stack->bottom_panel()) { |
| 552 panel_manager_->MovePanelToCollection(dragging_panel_, |
| 553 detached_collection, |
| 554 PanelCollection::KNOWN_POSITION); |
| 555 return true; |
| 556 } |
| 557 |
| 558 // If the dragging panel is the one below the top panel, move top panel |
| 559 // out of the stack. |
| 560 if (dragging_stack->GetPanelAbove(dragging_panel_) == |
| 561 dragging_stack->top_panel()) { |
| 562 panel_manager_->MovePanelToCollection(dragging_stack->top_panel(), |
| 563 detached_collection, |
| 564 PanelCollection::KNOWN_POSITION); |
| 565 return true; |
| 566 } |
| 567 |
| 568 // There're at least 4 panels. |
| 569 DCHECK_GE(dragging_stack->num_panels(), 4); |
| 570 |
| 571 // We can split them into 2 stacks by moving the dragging panel and all panels |
| 572 // below to a new stack while keeping all panels above in the same stack. |
| 573 StackedPanelCollection* new_stack = panel_manager_->CreateStack(); |
| 574 MovePanelAndBelowToCollection(dragging_panel_, |
| 575 new_stack, |
| 576 PanelCollection::KNOWN_POSITION); |
| 577 |
| 578 return true; |
| 579 } |
| 580 |
| 581 void PanelDragController::TrySnap(gfx::Point* target_position) { |
| 582 // Snapping does not apply to docked panels. |
| 583 if (dragging_panel_->collection()->type() == PanelCollection::DOCKED) |
| 584 return; |
| 585 |
| 586 gfx::Rect target_bounds; |
| 587 GlueEdge target_edge; |
| 588 Panel* target_panel = FindPanelToGlue(*target_position, |
| 589 SNAP, |
| 590 &target_bounds, |
| 591 &target_edge); |
| 592 if (!target_panel) |
| 593 return; |
| 594 |
| 595 *target_position = target_bounds.origin(); |
| 596 } |
| 597 |
| 598 Panel* PanelDragController::FindPanelToGlue( |
| 599 const gfx::Point& potential_position, |
| 600 GlueAction action, |
| 601 gfx::Rect* target_bounds, |
| 602 GlueEdge* target_edge) const { |
| 603 int best_distance = kint32max; |
| 604 Panel* best_matching_panel = NULL; |
| 605 |
| 606 // Compute the potential bounds for the dragging panel. |
| 607 gfx::Rect current_dragging_bounds = dragging_panel_->GetBounds(); |
| 608 gfx::Rect potential_dragging_bounds(potential_position, |
| 609 current_dragging_bounds.size()); |
| 610 |
| 611 // Compute the potential bounds for the bottom panel if the dragging panel is |
| 612 // in a stack. If not, it is same as |potential_dragging_bounds|. |
| 613 // This is used to determine if the dragging panel or the bottom panel can |
| 614 // stack to the top of other panel. |
| 615 gfx::Rect current_bottom_bounds; |
| 616 gfx::Rect potential_bottom_bounds; |
| 617 StackedPanelCollection* dragging_stack = dragging_panel_->stack(); |
| 618 if (dragging_stack && dragging_panel_ != dragging_stack->bottom_panel()) { |
| 619 gfx::Vector2d delta = potential_position - current_dragging_bounds.origin(); |
| 620 current_bottom_bounds = dragging_stack->bottom_panel()->GetBounds(); |
| 621 potential_bottom_bounds = current_bottom_bounds; |
| 622 potential_bottom_bounds.Offset(delta); |
| 623 } else { |
| 624 current_bottom_bounds = current_dragging_bounds; |
| 625 potential_bottom_bounds = potential_dragging_bounds; |
| 626 } |
| 627 |
| 628 // Go through all non-docked panels. |
| 629 std::vector<Panel*> panels = panel_manager_->GetDetachedAndStackedPanels(); |
| 630 for (std::vector<Panel*>::const_iterator iter = panels.begin(); |
| 631 iter != panels.end(); ++iter) { |
| 632 Panel* panel = *iter; |
| 633 if (dragging_panel_ == panel) |
| 634 continue; |
| 635 if (dragging_panel_->collection()->type() == PanelCollection::STACKED && |
| 636 dragging_panel_->collection() == panel->collection()) |
| 637 continue; |
| 638 gfx::Rect panel_bounds = panel->GetBounds(); |
| 639 int distance; |
| 640 int overlap; |
| 641 |
| 642 if (action == SNAP) { |
| 643 overlap = GetVerticalOverlap(potential_dragging_bounds, panel_bounds); |
| 644 if (overlap > kGluePanelsOverlapThreshold) { |
| 645 // Can |dragging_panel_| snap to left edge of |panel|? |
| 646 distance = GetHorizontalDistance(potential_dragging_bounds, |
| 647 panel_bounds); |
| 648 if (distance < kGluePanelsDistanceThreshold && |
| 649 distance < best_distance) { |
| 650 best_distance = distance; |
| 651 best_matching_panel = panel; |
| 652 *target_edge = LEFT_EDGE; |
| 653 *target_bounds = potential_dragging_bounds; |
| 654 target_bounds->set_x(panel_bounds.x() - target_bounds->width()); |
| 655 } |
| 656 |
| 657 // Can |dragging_panel_| snap to right edge of |panel|? |
| 658 distance = GetHorizontalDistance(panel_bounds, |
| 659 potential_dragging_bounds); |
| 660 if (distance < kGluePanelsDistanceThreshold && |
| 661 distance < best_distance) { |
| 662 best_distance = distance; |
| 663 best_matching_panel = panel; |
| 664 *target_edge = RIGHT_EDGE; |
| 665 *target_bounds = potential_dragging_bounds; |
| 666 target_bounds->set_x(panel_bounds.right()); |
| 667 } |
| 668 } |
| 669 } else { |
| 670 DCHECK_EQ(STACK, action); |
| 671 |
| 672 // Can |dragging_panel_| or the bottom panel in |dragging_panel_|'s stack |
| 673 // stack to top edge of |panel|? |
| 674 distance = GetVerticalDistance(potential_bottom_bounds, panel_bounds); |
| 675 overlap = GetHorizontalOverlap(panel_bounds, potential_bottom_bounds); |
| 676 if (distance < kGluePanelsDistanceThreshold && |
| 677 overlap > kGluePanelsOverlapThreshold && |
| 678 distance < best_distance) { |
| 679 best_distance = distance; |
| 680 best_matching_panel = panel; |
| 681 *target_edge = TOP_EDGE; |
| 682 target_bounds->SetRect( |
| 683 potential_dragging_bounds.x(), |
| 684 current_dragging_bounds.y() + panel_bounds.y() - |
| 685 current_bottom_bounds.height() - current_bottom_bounds.y(), |
| 686 potential_dragging_bounds.width(), |
| 687 potential_dragging_bounds.height()); |
| 688 } |
| 689 |
| 690 // Can |dragging_panel_| stack to bottom edge of |panel|? |
| 691 distance = GetVerticalDistance(panel_bounds, potential_dragging_bounds); |
| 692 overlap = GetHorizontalOverlap(panel_bounds, potential_dragging_bounds); |
| 693 if (distance < kGluePanelsDistanceThreshold && |
| 694 overlap > kGluePanelsOverlapThreshold && |
| 695 distance < best_distance) { |
| 696 best_distance = distance; |
| 697 best_matching_panel = panel; |
| 698 *target_edge = BOTTOM_EDGE; |
| 699 target_bounds->SetRect(potential_dragging_bounds.x(), |
| 700 panel_bounds.bottom(), |
| 701 potential_dragging_bounds.width(), |
| 702 potential_dragging_bounds.height()); |
| 703 } |
| 704 } |
| 705 } |
| 706 |
| 707 return best_matching_panel; |
| 708 } |
| 709 |
| 710 void PanelDragController::MovePanelAndBelowToCollection( |
| 711 Panel* panel, |
| 712 PanelCollection* target_collection, |
| 713 PanelCollection::PositioningMask positioning_mask) const { |
| 714 StackedPanelCollection* stack = panel->stack(); |
| 715 if (!stack) { |
| 716 panel_manager_->MovePanelToCollection(panel, |
| 717 target_collection, |
| 718 positioning_mask); |
| 719 return; |
| 720 } |
| 721 |
| 722 // Note that all the panels to move should be copied to a local list first |
| 723 // because the stack collection will be modified during the move. |
| 724 std::list<Panel*> panels_to_move; |
| 725 StackedPanelCollection::Panels::const_iterator iter = stack->panels().begin(); |
| 726 for (; iter != stack->panels().end(); ++iter) |
| 727 if ((*iter) == panel) |
| 728 break; |
| 729 for (; iter != stack->panels().end(); ++iter) { |
| 730 // Note that if the panels are going to be inserted from the top, we need |
| 731 // to reverse the order when copying to the local list. |
| 732 if (positioning_mask & PanelCollection::TOP_POSITION) |
| 733 panels_to_move.push_front(*iter); |
| 734 else |
| 735 panels_to_move.push_back(*iter); |
| 736 } |
| 737 for (std::list<Panel*>::const_iterator panel_iter = panels_to_move.begin(); |
| 738 panel_iter != panels_to_move.end(); ++panel_iter) { |
| 739 panel_manager_->MovePanelToCollection(*panel_iter, |
| 740 target_collection, |
| 741 positioning_mask); |
| 742 } |
| 743 |
| 744 // If the stack becomes empty or has only one panel left, no need to keep |
| 745 // the stack. |
| 746 if (stack && stack->num_panels() <= 1) { |
| 747 if (stack->num_panels() == 1) { |
| 748 panel_manager_->MovePanelToCollection( |
| 749 stack->top_panel(), |
| 750 panel_manager_->detached_collection(), |
| 751 PanelCollection::KNOWN_POSITION); |
| 752 } |
| 753 // Note that if the stack is the original collection, do not remove it now. |
| 754 // This is because the original collection contains the information to |
| 755 // restore the dragging panel to the right place when the drag is cancelled. |
| 756 if (stack != dragging_panel_original_collection_) |
| 757 panel_manager_->RemoveStack(stack); |
| 758 } |
| 759 } |
OLD | NEW |