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
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/panels/panel_drag_controller.h"
7 #include "base/logging.h"
8 #include "chrome/browser/ui/panels/detached_panel_collection.h"
9 #include "chrome/browser/ui/panels/detached_panel_drag_handler.h"
10 #include "chrome/browser/ui/panels/docked_panel_collection.h"
11 #include "chrome/browser/ui/panels/docked_panel_drag_handler.h"
12 #include "chrome/browser/ui/panels/panel.h"
13 #include "chrome/browser/ui/panels/panel_manager.h"
14 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
15 #include "chrome/browser/ui/panels/stacked_panel_drag_handler.h"
19 // The minimum distance that the docked panel gets dragged up in order to
20 // make it free-floating.
21 const int kDetachDockedPanelThreshold
= 100;
23 // Indicates how close the bottom of the detached panel is to the bottom of
24 // the docked area such that the detached panel becomes docked.
25 const int kDockDetachedPanelThreshold
= 30;
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;
32 // The minimum distance between the panel edge and the screen (or work area)
33 // edge such that the panel can snap to the screen (or work area) edge.
34 const int kSnapPanelToScreenEdgeThreshold
= 25;
36 int GetHorizontalOverlap(const gfx::Rect
& bounds1
, const gfx::Rect
& bounds2
) {
37 // Check for no overlap.
38 if (bounds1
.right() <= bounds2
.x() || bounds1
.x() >= bounds2
.right())
41 // Check for complete overlap.
42 if (bounds2
.x() <= bounds1
.x() && bounds1
.right() <= bounds2
.right())
43 return bounds1
.width();
45 if (bounds1
.x() <= bounds2
.x() && bounds2
.right() <= bounds1
.right())
46 return bounds2
.width();
48 // Compute the overlap part.
49 return (bounds1
.x() < bounds2
.x()) ? (bounds1
.right() - bounds2
.x())
50 : (bounds2
.right() - bounds1
.x());
53 int GetVerticalOverlap(const gfx::Rect
& bounds1
, const gfx::Rect
& bounds2
) {
54 // Check for no overlap.
55 if (bounds1
.bottom() <= bounds2
.y() || bounds1
.y() >= bounds2
.bottom())
58 // Check for complete overlap.
59 if (bounds2
.y() <= bounds1
.y() && bounds1
.bottom() <= bounds2
.bottom())
60 return bounds1
.height();
62 if (bounds1
.y() <= bounds2
.y() && bounds2
.bottom() <= bounds1
.bottom())
63 return bounds2
.height();
65 // Compute the overlap part.
66 return (bounds1
.y() < bounds2
.y()) ? (bounds1
.bottom() - bounds2
.y())
67 : (bounds2
.bottom() - bounds1
.y());
70 // Return the vertical distance between the bottom edge of |top_bounds| and
71 // the top edge of |bottom_bounds|.
72 int GetVerticalDistance(const gfx::Rect
& top_bounds
,
73 const gfx::Rect
& bottom_bounds
) {
74 return abs(bottom_bounds
.y() - top_bounds
.bottom());
77 // Return the vertical distance between the right edge of |left_bounds| and
78 // the left edge of |right_bounds|.
79 int GetHorizontalDistance(const gfx::Rect
& left_bounds
,
80 const gfx::Rect
& right_bounds
) {
81 return abs(right_bounds
.x() - left_bounds
.right());
84 void SetPreviewModeForPanelAndBelow(Panel
* panel
, bool in_preview
) {
85 StackedPanelCollection
* stack
= panel
->stack();
87 bool panel_found
= false;
88 for (StackedPanelCollection::Panels::const_iterator iter
=
89 stack
->panels().begin();
90 iter
!= stack
->panels().end(); ++iter
) {
91 Panel
* current_panel
= *iter
;
92 if (!panel_found
&& current_panel
!= panel
)
95 if (in_preview
!= current_panel
->in_preview_mode())
96 current_panel
->SetPreviewMode(in_preview
);
99 panel
->SetPreviewMode(in_preview
);
106 int PanelDragController::GetDetachDockedPanelThresholdForTesting() {
107 return kDetachDockedPanelThreshold
;
111 int PanelDragController::GetDockDetachedPanelThresholdForTesting() {
112 return kDockDetachedPanelThreshold
;
116 int PanelDragController::GetGluePanelDistanceThresholdForTesting() {
117 return kGluePanelsDistanceThreshold
;
121 int PanelDragController::GetGluePanelOverlapThresholdForTesting() {
122 return kGluePanelsOverlapThreshold
;
126 int PanelDragController::GetSnapPanelToScreenEdgeThresholdForTesting() {
127 return kSnapPanelToScreenEdgeThreshold
;
130 PanelDragController::PanelDragController(PanelManager
* panel_manager
)
131 : panel_manager_(panel_manager
),
132 panel_stacking_enabled_(PanelManager::IsPanelStackingEnabled()),
133 dragging_panel_(NULL
),
134 dragging_panel_original_collection_(NULL
) {
137 PanelDragController::~PanelDragController() {
140 void PanelDragController::StartDragging(Panel
* panel
,
141 const gfx::Point
& mouse_location
) {
142 DCHECK(!dragging_panel_
);
144 offset_from_mouse_location_on_drag_start_
=
145 mouse_location
- panel
->GetBounds().origin();
147 dragging_panel_
= panel
;
148 SetPreviewModeForPanelAndBelow(dragging_panel_
, true);
150 // Keep track of original collection and placement for the case that the drag
152 dragging_panel_original_collection_
= dragging_panel_
->collection();
153 dragging_panel_original_collection_
->SavePanelPlacement(dragging_panel_
);
156 void PanelDragController::Drag(const gfx::Point
& mouse_location
) {
157 if (!dragging_panel_
)
160 gfx::Point target_position
= GetPanelPositionForMouseLocation(mouse_location
);
162 if (panel_stacking_enabled_
) {
163 // Check if the dragging panel can be moved out the stack. Note that this
164 // has to be done first and we should continue processing it for the case
165 // that the drag also triggers stacking and docking.
166 // Note that the panel can only be unstacked from top or bottom. So if
167 // unstacking from top succeeds, there is no need to check for unstacking
169 if (!TryUnstackFromTop(target_position
))
170 TryUnstackFromBottom(target_position
);
172 // Check if the dragging panel can stack with other panel or stack.
173 TryStack(target_position
);
176 // Check if the dragging panel can be docked.
177 TryDock(target_position
);
179 // Check if the dragging panel can be detached.
180 TryDetach(target_position
);
182 // Check if the dragging panel can snap to other panel or edge of the working
184 if (panel_stacking_enabled_
)
185 TrySnap(&target_position
);
187 // At last, handle the drag via its collection's specific handler.
188 switch (dragging_panel_
->collection()->type()) {
189 case PanelCollection::DOCKED
:
190 DockedPanelDragHandler::HandleDrag(dragging_panel_
, target_position
);
192 case PanelCollection::DETACHED
:
193 DetachedPanelDragHandler::HandleDrag(dragging_panel_
, target_position
);
195 case PanelCollection::STACKED
:
196 StackedPanelDragHandler::HandleDrag(
199 dragging_panel_
->collection() == dragging_panel_original_collection_
);
207 void PanelDragController::EndDragging(bool cancelled
) {
208 if (!dragging_panel_
)
211 PanelCollection
* current_collection
= dragging_panel_
->collection();
213 // Restore the dragging panel to its original collection if needed.
214 // Note that the bounds of dragging panel is updated later by calling
215 // RestorePanelToSavedPlacement.
216 if (current_collection
!= dragging_panel_original_collection_
) {
217 PanelCollection::PositioningMask positioning_mask
=
218 static_cast<PanelCollection::PositioningMask
>(
219 PanelCollection::DEFAULT_POSITION
|
220 PanelCollection::DO_NOT_UPDATE_BOUNDS
);
221 MovePanelAndBelowToCollection(dragging_panel_
,
222 dragging_panel_original_collection_
,
226 // End the preview mode.
227 SetPreviewModeForPanelAndBelow(dragging_panel_
, false);
229 // Restore the dragging panel to its original placement.
230 dragging_panel_original_collection_
->RestorePanelToSavedPlacement();
232 // The saved placement is no longer needed.
233 dragging_panel_original_collection_
->DiscardSavedPanelPlacement();
235 // Finalizing the drag.
236 if (current_collection
->type() == PanelCollection::STACKED
)
237 StackedPanelDragHandler::FinalizeDrag(dragging_panel_
);
239 // End the preview mode.
240 SetPreviewModeForPanelAndBelow(dragging_panel_
, false);
242 // This could cause the panel to be moved to its finalized position.
243 current_collection
->RefreshLayout();
245 // This could cause the detached panel, that still keeps its minimized state
246 // when it gets detached due to unstacking, to expand. This could occur
247 // when the stack has more than 2 panels and the 2nd top panel is unstacked
248 // from the top panel: the top panel is detached while all other panels
249 // remain in the stack.
250 if (current_collection
!= panel_manager_
->detached_collection())
251 panel_manager_
->detached_collection()->RefreshLayout();
254 // If the origianl collection is a stack and it becomes empty, remove it.
255 if (dragging_panel_original_collection_
->type() == PanelCollection::STACKED
) {
256 StackedPanelCollection
* original_stack
=
257 static_cast<StackedPanelCollection
*>(
258 dragging_panel_original_collection_
);
259 if (original_stack
->num_panels() == 0)
260 panel_manager_
->RemoveStack(original_stack
);
263 dragging_panel_
= NULL
;
266 void PanelDragController::OnPanelClosed(Panel
* panel
) {
267 // Abort the drag only if the panel being closed is currently being dragged.
268 if (dragging_panel_
!= panel
)
271 dragging_panel_original_collection_
->DiscardSavedPanelPlacement();
272 dragging_panel_original_collection_
= NULL
;
273 dragging_panel_
= NULL
;
276 gfx::Point
PanelDragController::GetPanelPositionForMouseLocation(
277 const gfx::Point
& mouse_location
) const {
278 // The target panel position is computed based on the fact that the panel
279 // should follow the mouse movement.
280 gfx::Point target_position
=
281 mouse_location
- offset_from_mouse_location_on_drag_start_
;
282 gfx::Rect
target_bounds(target_position
, dragging_panel_
->GetBounds().size());
284 // Make sure that the panel's titlebar cannot be moved under the taskbar or
285 // OSX menu bar that is aligned to top screen edge.
286 gfx::Rect display_area
= panel_manager_
->display_settings_provider()->
287 GetDisplayAreaMatching(target_bounds
);
288 gfx::Rect work_area
= panel_manager_
->display_settings_provider()->
289 GetWorkAreaMatching(target_bounds
);
290 if (display_area
.Contains(mouse_location
) &&
291 target_position
.y() < work_area
.y()) {
292 target_position
.set_y(work_area
.y());
295 return target_position
;
298 void PanelDragController::TryDetach(const gfx::Point
& target_position
) {
299 // It has to come from the docked collection.
300 if (dragging_panel_
->collection()->type() != PanelCollection::DOCKED
)
303 // The minimized docked panel is not allowed to detach.
304 if (dragging_panel_
->IsMinimized())
307 // Panels in the detached collection are always at their full size.
308 gfx::Rect
target_bounds(target_position
, dragging_panel_
->full_size());
310 // To become detached, the panel should be dragged either out of the main
311 // work area or up high enough to pass certain threshold.
312 gfx::Rect target_work_area
= panel_manager_
->display_settings_provider()->
313 GetWorkAreaMatching(target_bounds
);
314 gfx::Rect dock_work_area
= panel_manager_
->docked_collection()->work_area();
315 if (target_work_area
.Contains(dock_work_area
) &&
316 dock_work_area
.bottom() - target_bounds
.bottom() <
317 kDetachDockedPanelThreshold
) {
321 // Apply new panel bounds.
322 dragging_panel_
->SetPanelBoundsInstantly(target_bounds
);
324 // Move the panel to new collection.
325 panel_manager_
->MovePanelToCollection(dragging_panel_
,
326 panel_manager_
->detached_collection(),
327 PanelCollection::KNOWN_POSITION
);
330 void PanelDragController::TryDock(const gfx::Point
& target_position
) {
331 // It has to come from the detached collection.
332 if (dragging_panel_
->collection()->type() != PanelCollection::DETACHED
)
335 gfx::Rect
target_bounds(target_position
, dragging_panel_
->GetBounds().size());
337 // To become docked, the panel should fall within the main work area and
338 // its bottom should come very close to or fall below the bottom of the main
340 gfx::Rect target_work_area
= panel_manager_
->display_settings_provider()->
341 GetWorkAreaMatching(target_bounds
);
342 gfx::Rect dock_work_area
= panel_manager_
->docked_collection()->work_area();
343 if (!target_work_area
.Contains(dock_work_area
) ||
344 dock_work_area
.bottom() - target_bounds
.bottom() >
345 kDockDetachedPanelThreshold
) {
349 // Apply new panel bounds.
350 dragging_panel_
->SetPanelBoundsInstantly(target_bounds
);
352 // Move the panel to new collection.
353 panel_manager_
->MovePanelToCollection(dragging_panel_
,
354 panel_manager_
->docked_collection(),
355 PanelCollection::KNOWN_POSITION
);
358 void PanelDragController::TryStack(const gfx::Point
& target_position
) {
359 gfx::Rect target_bounds
;
360 GlueEdge target_edge
;
361 Panel
* target_panel
= FindPanelToGlue(target_position
,
368 StackedPanelCollection
* dragging_stack
= dragging_panel_
->stack();
370 // Move the panel (and all the panels below if in a stack) to the new
372 gfx::Vector2d delta
=
373 target_bounds
.origin() - dragging_panel_
->GetBounds().origin();
375 dragging_stack
->MoveAllDraggingPanelsInstantly(delta
);
377 dragging_panel_
->MoveByInstantly(delta
);
379 // If the panel to stack with is not in a stack, create it now.
380 StackedPanelCollection
* target_stack
= target_panel
->stack();
382 target_stack
= panel_manager_
->CreateStack();
383 panel_manager_
->MovePanelToCollection(target_panel
,
385 PanelCollection::DEFAULT_POSITION
);
388 // Move the panel to new collection.
389 // Note that we don't want to refresh the layout now because when we add
390 // a panel to top of other panel, we don't want the bottom panel to change
391 // its width to be same as top panel now.
392 PanelCollection::PositioningMask positioning_mask
=
393 static_cast<PanelCollection::PositioningMask
>(
394 PanelCollection::NO_LAYOUT_REFRESH
|
395 (target_edge
== TOP_EDGE
? PanelCollection::TOP_POSITION
396 : PanelCollection::DEFAULT_POSITION
));
397 MovePanelAndBelowToCollection(dragging_panel_
,
402 // Check if a panel or a set of stacked panels (being dragged together from a
403 // stack) can be dragged away from the panel below such that the former panel(s)
404 // are not in the same stack as the latter panel.
405 bool PanelDragController::TryUnstackFromTop(const gfx::Point
& target_position
) {
406 // It has to be stacked.
407 StackedPanelCollection
* dragging_stack
= dragging_panel_
->stack();
411 // Unstacking from top only happens when a panel/stack stacks to the top of
412 // another panel and then moves away while the drag is still in progress.
413 if (dragging_panel_
!= dragging_stack
->top_panel() ||
414 dragging_stack
== dragging_panel_original_collection_
)
417 // Count the number of panels that might need to unstack.
418 Panel
* last_panel_to_unstack
= NULL
;
419 Panel
* panel_below_last_panel_to_unstack
= NULL
;
420 int num_panels_to_unstack
= 0;
421 for (StackedPanelCollection::Panels::const_iterator iter
=
422 dragging_stack
->panels().begin();
423 iter
!= dragging_stack
->panels().end(); ++iter
) {
424 if (!(*iter
)->in_preview_mode()) {
425 panel_below_last_panel_to_unstack
= *iter
;
428 num_panels_to_unstack
++;
429 last_panel_to_unstack
= *iter
;
431 DCHECK_GE(num_panels_to_unstack
, 1);
432 if (num_panels_to_unstack
== dragging_stack
->num_panels())
435 gfx::Vector2d delta
= target_position
- dragging_panel_
->GetBounds().origin();
437 // The last panel to unstack should be dragged far enough from its below
439 gfx::Rect target_bounds
= last_panel_to_unstack
->GetBounds();
440 target_bounds
.Offset(delta
);
441 gfx::Rect below_panel_bounds
= panel_below_last_panel_to_unstack
->GetBounds();
442 if (GetVerticalDistance(target_bounds
, below_panel_bounds
) <
443 kGluePanelsDistanceThreshold
&&
444 GetHorizontalOverlap(target_bounds
, below_panel_bounds
) >
445 kGluePanelsOverlapThreshold
) {
449 int num_panels_in_stack
= dragging_stack
->num_panels();
450 DCHECK_GE(num_panels_in_stack
, 2);
452 // When a panel is removed from its stack, we always make it detached. If it
453 // indeed should go to the docked collection, the subsequent TryDock will then
454 // move it from the detached collection to the docked collection.
455 DetachedPanelCollection
* detached_collection
=
456 panel_manager_
->detached_collection();
458 // If there're only 2 panels in the stack, both panels should move out of the
459 // stack and the stack should be removed.
460 if (num_panels_in_stack
== 2) {
461 DCHECK_EQ(1, num_panels_to_unstack
);
462 MovePanelAndBelowToCollection(dragging_panel_
,
464 PanelCollection::KNOWN_POSITION
);
465 dragging_panel_
->MoveByInstantly(delta
);
469 DCHECK_GE(num_panels_in_stack
, 3);
471 // If only one panel (top panel) needs to unstack, move it out of the stack.
472 if (num_panels_to_unstack
== 1) {
473 panel_manager_
->MovePanelToCollection(dragging_panel_
,
475 PanelCollection::KNOWN_POSITION
);
476 dragging_panel_
->MoveByInstantly(delta
);
480 // If all the panels except the bottom panel need to unstack, simply move
481 // bottom panel out of the stack.
482 if (num_panels_in_stack
- num_panels_to_unstack
== 1) {
483 panel_manager_
->MovePanelToCollection(dragging_stack
->bottom_panel(),
485 PanelCollection::KNOWN_POSITION
);
486 dragging_panel_
->stack()->MoveAllDraggingPanelsInstantly(delta
);
490 // Otherwise, move all unstacked panels to a new stack.
491 // Note that all the panels to move should be copied to a local list first
492 // because the stack collection will be modified during the move.
493 std::list
<Panel
*> panels_to_move
;
494 for (StackedPanelCollection::Panels::const_iterator iter
=
495 dragging_stack
->panels().begin();
496 iter
!= dragging_stack
->panels().end(); ++iter
) {
497 Panel
* panel
= *iter
;
498 if (!panel
->in_preview_mode())
500 panels_to_move
.push_back(panel
);
502 StackedPanelCollection
* new_stack
= panel_manager_
->CreateStack();
503 for (std::list
<Panel
*>::const_iterator iter
= panels_to_move
.begin();
504 iter
!= panels_to_move
.end(); ++iter
) {
505 panel_manager_
->MovePanelToCollection(*iter
,
507 PanelCollection::KNOWN_POSITION
);
509 dragging_panel_
->stack()->MoveAllDraggingPanelsInstantly(delta
);
514 // Check if a panel or a set of stacked panels (being dragged together from a
515 // stack) can be dragged away from the panel above such that the former panel(s)
516 // are not in the same stack as the latter panel.
517 bool PanelDragController::TryUnstackFromBottom(
518 const gfx::Point
& target_position
) {
519 // It has to be stacked.
520 StackedPanelCollection
* dragging_stack
= dragging_panel_
->stack();
524 // It cannot be the top panel.
525 if (dragging_panel_
== dragging_stack
->top_panel())
528 DCHECK_GT(dragging_stack
->num_panels(), 1);
530 gfx::Rect
target_bounds(target_position
, dragging_panel_
->GetBounds().size());
532 // The panel should be dragged far enough from its above panel.
533 Panel
* above_panel
= dragging_stack
->GetPanelAbove(dragging_panel_
);
535 gfx::Rect above_panel_bounds
= above_panel
->GetBounds();
536 if (GetVerticalDistance(above_panel_bounds
, target_bounds
) <
537 kGluePanelsDistanceThreshold
&&
538 GetHorizontalOverlap(above_panel_bounds
, target_bounds
) >
539 kGluePanelsOverlapThreshold
) {
543 gfx::Vector2d delta
= target_position
- dragging_panel_
->GetBounds().origin();
545 // If there're only 2 panels in the stack, both panels should move out the
546 // stack and the stack should be removed.
547 DetachedPanelCollection
* detached_collection
=
548 panel_manager_
->detached_collection();
549 if (dragging_stack
->num_panels() == 2) {
550 MovePanelAndBelowToCollection(dragging_stack
->top_panel(),
552 PanelCollection::KNOWN_POSITION
);
553 dragging_panel_
->MoveByInstantly(delta
);
557 // There're at least 3 panels.
558 DCHECK_GE(dragging_stack
->num_panels(), 3);
560 // If the dragging panel is bottom panel, move it out of the stack.
561 if (dragging_panel_
== dragging_stack
->bottom_panel()) {
562 panel_manager_
->MovePanelToCollection(dragging_panel_
,
564 PanelCollection::KNOWN_POSITION
);
565 dragging_panel_
->MoveByInstantly(delta
);
569 // If the dragging panel is the one below the top panel, move top panel
571 if (dragging_stack
->GetPanelAbove(dragging_panel_
) ==
572 dragging_stack
->top_panel()) {
573 panel_manager_
->MovePanelToCollection(dragging_stack
->top_panel(),
575 PanelCollection::KNOWN_POSITION
);
576 dragging_panel_
->stack()->MoveAllDraggingPanelsInstantly(delta
);
580 // There're at least 4 panels.
581 DCHECK_GE(dragging_stack
->num_panels(), 4);
583 // We can split them into 2 stacks by moving the dragging panel and all panels
584 // below to a new stack while keeping all panels above in the same stack.
585 StackedPanelCollection
* new_stack
= panel_manager_
->CreateStack();
586 MovePanelAndBelowToCollection(dragging_panel_
,
588 PanelCollection::KNOWN_POSITION
);
589 dragging_panel_
->stack()->MoveAllDraggingPanelsInstantly(delta
);
594 void PanelDragController::TrySnap(gfx::Point
* target_position
) {
595 // Snapping does not apply to docked panels.
596 if (dragging_panel_
->collection()->type() == PanelCollection::DOCKED
)
599 // Check if the panel can snap to other panel.
600 gfx::Rect target_bounds
;
601 GlueEdge target_edge
;
602 Panel
* target_panel
= FindPanelToGlue(*target_position
,
607 *target_position
= target_bounds
.origin();
611 // Check if the panel can snap to the left/right edge of the work area.
612 target_bounds
.set_origin(*target_position
);
613 target_bounds
.set_size(dragging_panel_
->GetBounds().size());
614 gfx::Rect work_area
= panel_manager_
->display_settings_provider()->
615 GetWorkAreaMatching(target_bounds
);
616 if (abs(target_position
->x() - work_area
.x()) <
617 kSnapPanelToScreenEdgeThreshold
) {
618 target_position
->set_x(work_area
.x());
620 int width
= dragging_panel_
->GetBounds().width();
621 if (abs(work_area
.right() - target_position
->x() - width
) <
622 kSnapPanelToScreenEdgeThreshold
)
623 target_position
->set_x(work_area
.right() - width
);
626 // Check if the panel can snap to the top/bottom edge of the work area.
627 if (abs(target_position
->y() - work_area
.y()) <
628 kSnapPanelToScreenEdgeThreshold
) {
629 target_position
->set_y(work_area
.y());
631 // If the panel is in a stack, the height is from the top edge of this panel
632 // to the bottom edge of the last panel in the stack.
634 StackedPanelCollection
* stack
= dragging_panel_
->stack();
636 height
= stack
->bottom_panel()->GetBounds().bottom() -
637 dragging_panel_
->GetBounds().y();
639 height
= dragging_panel_
->GetBounds().height();
641 if (abs(work_area
.bottom() - target_position
->y() - height
) <
642 kSnapPanelToScreenEdgeThreshold
)
643 target_position
->set_y(work_area
.bottom() - height
);
647 Panel
* PanelDragController::FindPanelToGlue(
648 const gfx::Point
& potential_position
,
650 gfx::Rect
* target_bounds
,
651 GlueEdge
* target_edge
) const {
652 int best_distance
= kint32max
;
653 Panel
* best_matching_panel
= NULL
;
655 // Compute the potential bounds for the dragging panel.
656 gfx::Rect current_dragging_bounds
= dragging_panel_
->GetBounds();
657 gfx::Rect
potential_dragging_bounds(potential_position
,
658 current_dragging_bounds
.size());
660 // Compute the potential bounds for the bottom panel if the dragging panel is
661 // in a stack. If not, it is same as |potential_dragging_bounds|.
662 // This is used to determine if the dragging panel or the bottom panel can
663 // stack to the top of other panel.
664 gfx::Rect current_bottom_bounds
;
665 gfx::Rect potential_bottom_bounds
;
666 StackedPanelCollection
* dragging_stack
= dragging_panel_
->stack();
667 if (dragging_stack
&& dragging_panel_
!= dragging_stack
->bottom_panel()) {
668 gfx::Vector2d delta
= potential_position
- current_dragging_bounds
.origin();
669 current_bottom_bounds
= dragging_stack
->bottom_panel()->GetBounds();
670 potential_bottom_bounds
= current_bottom_bounds
;
671 potential_bottom_bounds
.Offset(delta
);
673 current_bottom_bounds
= current_dragging_bounds
;
674 potential_bottom_bounds
= potential_dragging_bounds
;
677 // Go through all non-docked panels.
678 std::vector
<Panel
*> panels
= panel_manager_
->GetDetachedAndStackedPanels();
679 for (std::vector
<Panel
*>::const_iterator iter
= panels
.begin();
680 iter
!= panels
.end(); ++iter
) {
681 Panel
* panel
= *iter
;
682 if (dragging_panel_
== panel
)
684 if (dragging_panel_
->collection()->type() == PanelCollection::STACKED
&&
685 dragging_panel_
->collection() == panel
->collection())
687 gfx::Rect panel_bounds
= panel
->GetBounds();
691 if (action
== SNAP
) {
692 overlap
= GetVerticalOverlap(potential_dragging_bounds
, panel_bounds
);
693 if (overlap
> kGluePanelsOverlapThreshold
) {
694 // Can |dragging_panel_| snap to left edge of |panel|?
695 distance
= GetHorizontalDistance(potential_dragging_bounds
,
697 if (distance
< kGluePanelsDistanceThreshold
&&
698 distance
< best_distance
) {
699 best_distance
= distance
;
700 best_matching_panel
= panel
;
701 *target_edge
= LEFT_EDGE
;
702 *target_bounds
= potential_dragging_bounds
;
703 target_bounds
->set_x(panel_bounds
.x() - target_bounds
->width());
706 // Can |dragging_panel_| snap to right edge of |panel|?
707 distance
= GetHorizontalDistance(panel_bounds
,
708 potential_dragging_bounds
);
709 if (distance
< kGluePanelsDistanceThreshold
&&
710 distance
< best_distance
) {
711 best_distance
= distance
;
712 best_matching_panel
= panel
;
713 *target_edge
= RIGHT_EDGE
;
714 *target_bounds
= potential_dragging_bounds
;
715 target_bounds
->set_x(panel_bounds
.right());
719 DCHECK_EQ(STACK
, action
);
720 StackedPanelCollection
* stack
= panel
->stack();
722 // Can |dragging_panel_| or the bottom panel in |dragging_panel_|'s stack
723 // stack to top edge of |panel|? If |panel| is in a stack and not top
724 // panel, its top edge is interior edge and thus cannot be aligned with.
725 distance
= GetVerticalDistance(potential_bottom_bounds
, panel_bounds
);
726 overlap
= GetHorizontalOverlap(panel_bounds
, potential_bottom_bounds
);
727 if ((!stack
|| panel
== stack
->top_panel()) &&
728 distance
< kGluePanelsDistanceThreshold
&&
729 overlap
> kGluePanelsOverlapThreshold
&&
730 distance
< best_distance
) {
731 best_distance
= distance
;
732 best_matching_panel
= panel
;
733 *target_edge
= TOP_EDGE
;
734 target_bounds
->SetRect(
735 potential_dragging_bounds
.x(),
736 current_dragging_bounds
.y() + panel_bounds
.y() -
737 current_bottom_bounds
.height() - current_bottom_bounds
.y(),
738 potential_dragging_bounds
.width(),
739 potential_dragging_bounds
.height());
742 // Can |dragging_panel_| stack to bottom edge of |panel|? If |panel| is
743 // in a stack and not bottom panel, its bottom edge is interior edge and
744 // thus cannot be aligned with.
745 distance
= GetVerticalDistance(panel_bounds
, potential_dragging_bounds
);
746 overlap
= GetHorizontalOverlap(panel_bounds
, potential_dragging_bounds
);
747 if ((!stack
|| panel
== stack
->bottom_panel()) &&
748 distance
< kGluePanelsDistanceThreshold
&&
749 overlap
> kGluePanelsOverlapThreshold
&&
750 distance
< best_distance
) {
751 best_distance
= distance
;
752 best_matching_panel
= panel
;
753 *target_edge
= BOTTOM_EDGE
;
754 target_bounds
->SetRect(potential_dragging_bounds
.x(),
755 panel_bounds
.bottom(),
756 potential_dragging_bounds
.width(),
757 potential_dragging_bounds
.height());
762 return best_matching_panel
;
765 void PanelDragController::MovePanelAndBelowToCollection(
767 PanelCollection
* target_collection
,
768 PanelCollection::PositioningMask positioning_mask
) const {
769 StackedPanelCollection
* stack
= panel
->stack();
771 panel_manager_
->MovePanelToCollection(panel
,
777 // Note that all the panels to move should be copied to a local list first
778 // because the stack collection will be modified during the move.
779 std::list
<Panel
*> panels_to_move
;
780 StackedPanelCollection::Panels::const_iterator iter
= stack
->panels().begin();
781 for (; iter
!= stack
->panels().end(); ++iter
)
782 if ((*iter
) == panel
)
784 for (; iter
!= stack
->panels().end(); ++iter
) {
785 // Note that if the panels are going to be inserted from the top, we need
786 // to reverse the order when copying to the local list.
787 if (positioning_mask
& PanelCollection::TOP_POSITION
)
788 panels_to_move
.push_front(*iter
);
790 panels_to_move
.push_back(*iter
);
792 for (std::list
<Panel
*>::const_iterator panel_iter
= panels_to_move
.begin();
793 panel_iter
!= panels_to_move
.end(); ++panel_iter
) {
794 panel_manager_
->MovePanelToCollection(*panel_iter
,
799 // If the stack becomes empty or has only one panel left, no need to keep
801 if (stack
&& stack
->num_panels() <= 1) {
802 if (stack
->num_panels() == 1) {
803 panel_manager_
->MovePanelToCollection(
805 panel_manager_
->detached_collection(),
806 PanelCollection::KNOWN_POSITION
);
808 // Note that if the stack is the original collection, do not remove it now.
809 // This is because the original collection contains the information to
810 // restore the dragging panel to the right place when the drag is cancelled.
811 if (stack
!= dragging_panel_original_collection_
)
812 panel_manager_
->RemoveStack(stack
);