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

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

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

Powered by Google App Engine
This is Rietveld 408576698