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

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

Powered by Google App Engine
This is Rietveld 408576698