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

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

Powered by Google App Engine
This is Rietveld 408576698