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