Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(244)

Side by Side Diff: chrome/browser/ui/panels/panel_drag_controller.cc

Issue 11669018: Support dragging panels to stack and snap. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Change per feedback Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698