This source file includes following definitions.
- GetHorizontalOverlap
- GetVerticalOverlap
- GetVerticalDistance
- GetHorizontalDistance
- SetPreviewModeForPanelAndBelow
- GetDetachDockedPanelThresholdForTesting
- GetDockDetachedPanelThresholdForTesting
- GetGluePanelDistanceThresholdForTesting
- GetGluePanelOverlapThresholdForTesting
- GetSnapPanelToScreenEdgeThresholdForTesting
- dragging_panel_original_collection_
- StartDragging
- EndDragging
- OnPanelClosed
- GetPanelPositionForMouseLocation
- TryDetach
- TryDock
- TryStack
- TryUnstackFromTop
- TryUnstackFromBottom
- TrySnap
- FindPanelToGlue
- MovePanelAndBelowToCollection
#include "chrome/browser/ui/panels/panel_drag_controller.h"
#include "base/logging.h"
#include "chrome/browser/ui/panels/detached_panel_collection.h"
#include "chrome/browser/ui/panels/detached_panel_drag_handler.h"
#include "chrome/browser/ui/panels/docked_panel_collection.h"
#include "chrome/browser/ui/panels/docked_panel_drag_handler.h"
#include "chrome/browser/ui/panels/panel.h"
#include "chrome/browser/ui/panels/panel_manager.h"
#include "chrome/browser/ui/panels/stacked_panel_collection.h"
#include "chrome/browser/ui/panels/stacked_panel_drag_handler.h"
namespace {
const int kDetachDockedPanelThreshold = 100;
const int kDockDetachedPanelThreshold = 30;
const int kGluePanelsDistanceThreshold = 15;
const int kGluePanelsOverlapThreshold = 10;
const int kSnapPanelToScreenEdgeThreshold = 25;
int GetHorizontalOverlap(const gfx::Rect& bounds1, const gfx::Rect& bounds2) {
if (bounds1.right() <= bounds2.x() || bounds1.x() >= bounds2.right())
return 0;
if (bounds2.x() <= bounds1.x() && bounds1.right() <= bounds2.right())
return bounds1.width();
if (bounds1.x() <= bounds2.x() && bounds2.right() <= bounds1.right())
return bounds2.width();
return (bounds1.x() < bounds2.x()) ? (bounds1.right() - bounds2.x())
: (bounds2.right() - bounds1.x());
}
int GetVerticalOverlap(const gfx::Rect& bounds1, const gfx::Rect& bounds2) {
if (bounds1.bottom() <= bounds2.y() || bounds1.y() >= bounds2.bottom())
return 0;
if (bounds2.y() <= bounds1.y() && bounds1.bottom() <= bounds2.bottom())
return bounds1.height();
if (bounds1.y() <= bounds2.y() && bounds2.bottom() <= bounds1.bottom())
return bounds2.height();
return (bounds1.y() < bounds2.y()) ? (bounds1.bottom() - bounds2.y())
: (bounds2.bottom() - bounds1.y());
}
int GetVerticalDistance(const gfx::Rect& top_bounds,
const gfx::Rect& bottom_bounds) {
return abs(bottom_bounds.y() - top_bounds.bottom());
}
int GetHorizontalDistance(const gfx::Rect& left_bounds,
const gfx::Rect& right_bounds) {
return abs(right_bounds.x() - left_bounds.right());
}
void SetPreviewModeForPanelAndBelow(Panel* panel, bool in_preview) {
StackedPanelCollection* stack = panel->stack();
if (stack) {
bool panel_found = false;
for (StackedPanelCollection::Panels::const_iterator iter =
stack->panels().begin();
iter != stack->panels().end(); ++iter) {
Panel* current_panel = *iter;
if (!panel_found && current_panel != panel)
continue;
panel_found = true;
if (in_preview != current_panel->in_preview_mode())
current_panel->SetPreviewMode(in_preview);
}
} else {
panel->SetPreviewMode(in_preview);
}
}
}
int PanelDragController::GetDetachDockedPanelThresholdForTesting() {
return kDetachDockedPanelThreshold;
}
int PanelDragController::GetDockDetachedPanelThresholdForTesting() {
return kDockDetachedPanelThreshold;
}
int PanelDragController::GetGluePanelDistanceThresholdForTesting() {
return kGluePanelsDistanceThreshold;
}
int PanelDragController::GetGluePanelOverlapThresholdForTesting() {
return kGluePanelsOverlapThreshold;
}
int PanelDragController::GetSnapPanelToScreenEdgeThresholdForTesting() {
return kSnapPanelToScreenEdgeThreshold;
}
PanelDragController::PanelDragController(PanelManager* panel_manager)
: panel_manager_(panel_manager),
panel_stacking_enabled_(PanelManager::IsPanelStackingEnabled()),
dragging_panel_(NULL),
dragging_panel_original_collection_(NULL) {
}
PanelDragController::~PanelDragController() {
}
void PanelDragController::StartDragging(Panel* panel,
const gfx::Point& mouse_location) {
DCHECK(!dragging_panel_);
offset_from_mouse_location_on_drag_start_ =
mouse_location - panel->GetBounds().origin();
dragging_panel_ = panel;
SetPreviewModeForPanelAndBelow(dragging_panel_, true);
dragging_panel_original_collection_ = dragging_panel_->collection();
dragging_panel_original_collection_->SavePanelPlacement(dragging_panel_);
}
void PanelDragController::Drag(const gfx::Point& mouse_location) {
if (!dragging_panel_)
return;
gfx::Point target_position = GetPanelPositionForMouseLocation(mouse_location);
if (panel_stacking_enabled_) {
if (!TryUnstackFromTop(target_position))
TryUnstackFromBottom(target_position);
TryStack(target_position);
}
TryDock(target_position);
TryDetach(target_position);
if (panel_stacking_enabled_)
TrySnap(&target_position);
switch (dragging_panel_->collection()->type()) {
case PanelCollection::DOCKED:
DockedPanelDragHandler::HandleDrag(dragging_panel_, target_position);
break;
case PanelCollection::DETACHED:
DetachedPanelDragHandler::HandleDrag(dragging_panel_, target_position);
break;
case PanelCollection::STACKED:
StackedPanelDragHandler::HandleDrag(
dragging_panel_,
target_position,
dragging_panel_->collection() == dragging_panel_original_collection_);
break;
default:
NOTREACHED();
break;
}
}
void PanelDragController::EndDragging(bool cancelled) {
if (!dragging_panel_)
return;
PanelCollection* current_collection = dragging_panel_->collection();
if (cancelled) {
if (current_collection != dragging_panel_original_collection_) {
PanelCollection::PositioningMask positioning_mask =
static_cast<PanelCollection::PositioningMask>(
PanelCollection::DEFAULT_POSITION |
PanelCollection::DO_NOT_UPDATE_BOUNDS);
MovePanelAndBelowToCollection(dragging_panel_,
dragging_panel_original_collection_,
positioning_mask);
}
SetPreviewModeForPanelAndBelow(dragging_panel_, false);
dragging_panel_original_collection_->RestorePanelToSavedPlacement();
} else {
dragging_panel_original_collection_->DiscardSavedPanelPlacement();
if (current_collection->type() == PanelCollection::STACKED)
StackedPanelDragHandler::FinalizeDrag(dragging_panel_);
SetPreviewModeForPanelAndBelow(dragging_panel_, false);
current_collection->RefreshLayout();
if (current_collection != panel_manager_->detached_collection())
panel_manager_->detached_collection()->RefreshLayout();
}
if (dragging_panel_original_collection_->type() == PanelCollection::STACKED) {
StackedPanelCollection* original_stack =
static_cast<StackedPanelCollection*>(
dragging_panel_original_collection_);
if (original_stack->num_panels() == 0)
panel_manager_->RemoveStack(original_stack);
}
dragging_panel_ = NULL;
}
void PanelDragController::OnPanelClosed(Panel* panel) {
if (dragging_panel_ != panel)
return;
dragging_panel_original_collection_->DiscardSavedPanelPlacement();
dragging_panel_original_collection_ = NULL;
dragging_panel_ = NULL;
}
gfx::Point PanelDragController::GetPanelPositionForMouseLocation(
const gfx::Point& mouse_location) const {
gfx::Point target_position =
mouse_location - offset_from_mouse_location_on_drag_start_;
gfx::Rect target_bounds(target_position, dragging_panel_->GetBounds().size());
gfx::Rect display_area = panel_manager_->display_settings_provider()->
GetDisplayAreaMatching(target_bounds);
gfx::Rect work_area = panel_manager_->display_settings_provider()->
GetWorkAreaMatching(target_bounds);
if (display_area.Contains(mouse_location) &&
target_position.y() < work_area.y()) {
target_position.set_y(work_area.y());
}
return target_position;
}
void PanelDragController::TryDetach(const gfx::Point& target_position) {
if (dragging_panel_->collection()->type() != PanelCollection::DOCKED)
return;
if (dragging_panel_->IsMinimized())
return;
gfx::Rect target_bounds(target_position, dragging_panel_->full_size());
gfx::Rect target_work_area = panel_manager_->display_settings_provider()->
GetWorkAreaMatching(target_bounds);
gfx::Rect dock_work_area = panel_manager_->docked_collection()->work_area();
if (target_work_area.Contains(dock_work_area) &&
dock_work_area.bottom() - target_bounds.bottom() <
kDetachDockedPanelThreshold) {
return;
}
dragging_panel_->SetPanelBoundsInstantly(target_bounds);
panel_manager_->MovePanelToCollection(dragging_panel_,
panel_manager_->detached_collection(),
PanelCollection::KNOWN_POSITION);
}
void PanelDragController::TryDock(const gfx::Point& target_position) {
if (dragging_panel_->collection()->type() != PanelCollection::DETACHED)
return;
gfx::Rect target_bounds(target_position, dragging_panel_->GetBounds().size());
gfx::Rect target_work_area = panel_manager_->display_settings_provider()->
GetWorkAreaMatching(target_bounds);
gfx::Rect dock_work_area = panel_manager_->docked_collection()->work_area();
if (!target_work_area.Contains(dock_work_area) ||
dock_work_area.bottom() - target_bounds.bottom() >
kDockDetachedPanelThreshold) {
return;
}
dragging_panel_->SetPanelBoundsInstantly(target_bounds);
panel_manager_->MovePanelToCollection(dragging_panel_,
panel_manager_->docked_collection(),
PanelCollection::KNOWN_POSITION);
}
void PanelDragController::TryStack(const gfx::Point& target_position) {
gfx::Rect target_bounds;
GlueEdge target_edge;
Panel* target_panel = FindPanelToGlue(target_position,
STACK,
&target_bounds,
&target_edge);
if (!target_panel)
return;
StackedPanelCollection* dragging_stack = dragging_panel_->stack();
gfx::Vector2d delta =
target_bounds.origin() - dragging_panel_->GetBounds().origin();
if (dragging_stack)
dragging_stack->MoveAllDraggingPanelsInstantly(delta);
else
dragging_panel_->MoveByInstantly(delta);
StackedPanelCollection* target_stack = target_panel->stack();
if (!target_stack) {
target_stack = panel_manager_->CreateStack();
panel_manager_->MovePanelToCollection(target_panel,
target_stack,
PanelCollection::DEFAULT_POSITION);
}
PanelCollection::PositioningMask positioning_mask =
static_cast<PanelCollection::PositioningMask>(
PanelCollection::NO_LAYOUT_REFRESH |
(target_edge == TOP_EDGE ? PanelCollection::TOP_POSITION
: PanelCollection::DEFAULT_POSITION));
MovePanelAndBelowToCollection(dragging_panel_,
target_stack,
positioning_mask);
}
bool PanelDragController::TryUnstackFromTop(const gfx::Point& target_position) {
StackedPanelCollection* dragging_stack = dragging_panel_->stack();
if (!dragging_stack)
return false;
if (dragging_panel_ != dragging_stack->top_panel() ||
dragging_stack == dragging_panel_original_collection_)
return false;
Panel* last_panel_to_unstack = NULL;
Panel* panel_below_last_panel_to_unstack = NULL;
int num_panels_to_unstack = 0;
for (StackedPanelCollection::Panels::const_iterator iter =
dragging_stack->panels().begin();
iter != dragging_stack->panels().end(); ++iter) {
if (!(*iter)->in_preview_mode()) {
panel_below_last_panel_to_unstack = *iter;
break;
}
num_panels_to_unstack++;
last_panel_to_unstack = *iter;
}
DCHECK_GE(num_panels_to_unstack, 1);
if (num_panels_to_unstack == dragging_stack->num_panels())
return false;
gfx::Vector2d delta = target_position - dragging_panel_->GetBounds().origin();
gfx::Rect target_bounds = last_panel_to_unstack->GetBounds();
target_bounds.Offset(delta);
gfx::Rect below_panel_bounds = panel_below_last_panel_to_unstack->GetBounds();
if (GetVerticalDistance(target_bounds, below_panel_bounds) <
kGluePanelsDistanceThreshold &&
GetHorizontalOverlap(target_bounds, below_panel_bounds) >
kGluePanelsOverlapThreshold) {
return false;
}
int num_panels_in_stack = dragging_stack->num_panels();
DCHECK_GE(num_panels_in_stack, 2);
DetachedPanelCollection* detached_collection =
panel_manager_->detached_collection();
if (num_panels_in_stack == 2) {
DCHECK_EQ(1, num_panels_to_unstack);
MovePanelAndBelowToCollection(dragging_panel_,
detached_collection,
PanelCollection::KNOWN_POSITION);
dragging_panel_->MoveByInstantly(delta);
return true;
}
DCHECK_GE(num_panels_in_stack, 3);
if (num_panels_to_unstack == 1) {
panel_manager_->MovePanelToCollection(dragging_panel_,
detached_collection,
PanelCollection::KNOWN_POSITION);
dragging_panel_->MoveByInstantly(delta);
return true;
}
if (num_panels_in_stack - num_panels_to_unstack == 1) {
panel_manager_->MovePanelToCollection(dragging_stack->bottom_panel(),
detached_collection,
PanelCollection::KNOWN_POSITION);
dragging_panel_->stack()->MoveAllDraggingPanelsInstantly(delta);
return true;
}
std::list<Panel*> panels_to_move;
for (StackedPanelCollection::Panels::const_iterator iter =
dragging_stack->panels().begin();
iter != dragging_stack->panels().end(); ++iter) {
Panel* panel = *iter;
if (!panel->in_preview_mode())
break;
panels_to_move.push_back(panel);
}
StackedPanelCollection* new_stack = panel_manager_->CreateStack();
for (std::list<Panel*>::const_iterator iter = panels_to_move.begin();
iter != panels_to_move.end(); ++iter) {
panel_manager_->MovePanelToCollection(*iter,
new_stack,
PanelCollection::KNOWN_POSITION);
}
dragging_panel_->stack()->MoveAllDraggingPanelsInstantly(delta);
return true;
}
bool PanelDragController::TryUnstackFromBottom(
const gfx::Point& target_position) {
StackedPanelCollection* dragging_stack = dragging_panel_->stack();
if (!dragging_stack)
return false;
if (dragging_panel_ == dragging_stack->top_panel())
return false;
DCHECK_GT(dragging_stack->num_panels(), 1);
gfx::Rect target_bounds(target_position, dragging_panel_->GetBounds().size());
Panel* above_panel = dragging_stack->GetPanelAbove(dragging_panel_);
DCHECK(above_panel);
gfx::Rect above_panel_bounds = above_panel->GetBounds();
if (GetVerticalDistance(above_panel_bounds, target_bounds) <
kGluePanelsDistanceThreshold &&
GetHorizontalOverlap(above_panel_bounds, target_bounds) >
kGluePanelsOverlapThreshold) {
return false;
}
gfx::Vector2d delta = target_position - dragging_panel_->GetBounds().origin();
DetachedPanelCollection* detached_collection =
panel_manager_->detached_collection();
if (dragging_stack->num_panels() == 2) {
MovePanelAndBelowToCollection(dragging_stack->top_panel(),
detached_collection,
PanelCollection::KNOWN_POSITION);
dragging_panel_->MoveByInstantly(delta);
return true;
}
DCHECK_GE(dragging_stack->num_panels(), 3);
if (dragging_panel_ == dragging_stack->bottom_panel()) {
panel_manager_->MovePanelToCollection(dragging_panel_,
detached_collection,
PanelCollection::KNOWN_POSITION);
dragging_panel_->MoveByInstantly(delta);
return true;
}
if (dragging_stack->GetPanelAbove(dragging_panel_) ==
dragging_stack->top_panel()) {
panel_manager_->MovePanelToCollection(dragging_stack->top_panel(),
detached_collection,
PanelCollection::KNOWN_POSITION);
dragging_panel_->stack()->MoveAllDraggingPanelsInstantly(delta);
return true;
}
DCHECK_GE(dragging_stack->num_panels(), 4);
StackedPanelCollection* new_stack = panel_manager_->CreateStack();
MovePanelAndBelowToCollection(dragging_panel_,
new_stack,
PanelCollection::KNOWN_POSITION);
dragging_panel_->stack()->MoveAllDraggingPanelsInstantly(delta);
return true;
}
void PanelDragController::TrySnap(gfx::Point* target_position) {
if (dragging_panel_->collection()->type() == PanelCollection::DOCKED)
return;
gfx::Rect target_bounds;
GlueEdge target_edge;
Panel* target_panel = FindPanelToGlue(*target_position,
SNAP,
&target_bounds,
&target_edge);
if (target_panel) {
*target_position = target_bounds.origin();
return;
}
target_bounds.set_origin(*target_position);
target_bounds.set_size(dragging_panel_->GetBounds().size());
gfx::Rect work_area = panel_manager_->display_settings_provider()->
GetWorkAreaMatching(target_bounds);
if (abs(target_position->x() - work_area.x()) <
kSnapPanelToScreenEdgeThreshold) {
target_position->set_x(work_area.x());
} else {
int width = dragging_panel_->GetBounds().width();
if (abs(work_area.right() - target_position->x() - width) <
kSnapPanelToScreenEdgeThreshold)
target_position->set_x(work_area.right() - width);
}
if (abs(target_position->y() - work_area.y()) <
kSnapPanelToScreenEdgeThreshold) {
target_position->set_y(work_area.y());
} else {
int height;
StackedPanelCollection* stack = dragging_panel_->stack();
if (stack) {
height = stack->bottom_panel()->GetBounds().bottom() -
dragging_panel_->GetBounds().y();
} else {
height = dragging_panel_->GetBounds().height();
}
if (abs(work_area.bottom() - target_position->y() - height) <
kSnapPanelToScreenEdgeThreshold)
target_position->set_y(work_area.bottom() - height);
}
}
Panel* PanelDragController::FindPanelToGlue(
const gfx::Point& potential_position,
GlueAction action,
gfx::Rect* target_bounds,
GlueEdge* target_edge) const {
int best_distance = kint32max;
Panel* best_matching_panel = NULL;
gfx::Rect current_dragging_bounds = dragging_panel_->GetBounds();
gfx::Rect potential_dragging_bounds(potential_position,
current_dragging_bounds.size());
gfx::Rect current_bottom_bounds;
gfx::Rect potential_bottom_bounds;
StackedPanelCollection* dragging_stack = dragging_panel_->stack();
if (dragging_stack && dragging_panel_ != dragging_stack->bottom_panel()) {
gfx::Vector2d delta = potential_position - current_dragging_bounds.origin();
current_bottom_bounds = dragging_stack->bottom_panel()->GetBounds();
potential_bottom_bounds = current_bottom_bounds;
potential_bottom_bounds.Offset(delta);
} else {
current_bottom_bounds = current_dragging_bounds;
potential_bottom_bounds = potential_dragging_bounds;
}
std::vector<Panel*> panels = panel_manager_->GetDetachedAndStackedPanels();
for (std::vector<Panel*>::const_iterator iter = panels.begin();
iter != panels.end(); ++iter) {
Panel* panel = *iter;
if (dragging_panel_ == panel)
continue;
if (dragging_panel_->collection()->type() == PanelCollection::STACKED &&
dragging_panel_->collection() == panel->collection())
continue;
gfx::Rect panel_bounds = panel->GetBounds();
int distance;
int overlap;
if (action == SNAP) {
overlap = GetVerticalOverlap(potential_dragging_bounds, panel_bounds);
if (overlap > kGluePanelsOverlapThreshold) {
distance = GetHorizontalDistance(potential_dragging_bounds,
panel_bounds);
if (distance < kGluePanelsDistanceThreshold &&
distance < best_distance) {
best_distance = distance;
best_matching_panel = panel;
*target_edge = LEFT_EDGE;
*target_bounds = potential_dragging_bounds;
target_bounds->set_x(panel_bounds.x() - target_bounds->width());
}
distance = GetHorizontalDistance(panel_bounds,
potential_dragging_bounds);
if (distance < kGluePanelsDistanceThreshold &&
distance < best_distance) {
best_distance = distance;
best_matching_panel = panel;
*target_edge = RIGHT_EDGE;
*target_bounds = potential_dragging_bounds;
target_bounds->set_x(panel_bounds.right());
}
}
} else {
DCHECK_EQ(STACK, action);
StackedPanelCollection* stack = panel->stack();
distance = GetVerticalDistance(potential_bottom_bounds, panel_bounds);
overlap = GetHorizontalOverlap(panel_bounds, potential_bottom_bounds);
if ((!stack || panel == stack->top_panel()) &&
distance < kGluePanelsDistanceThreshold &&
overlap > kGluePanelsOverlapThreshold &&
distance < best_distance) {
best_distance = distance;
best_matching_panel = panel;
*target_edge = TOP_EDGE;
target_bounds->SetRect(
potential_dragging_bounds.x(),
current_dragging_bounds.y() + panel_bounds.y() -
current_bottom_bounds.height() - current_bottom_bounds.y(),
potential_dragging_bounds.width(),
potential_dragging_bounds.height());
}
distance = GetVerticalDistance(panel_bounds, potential_dragging_bounds);
overlap = GetHorizontalOverlap(panel_bounds, potential_dragging_bounds);
if ((!stack || panel == stack->bottom_panel()) &&
distance < kGluePanelsDistanceThreshold &&
overlap > kGluePanelsOverlapThreshold &&
distance < best_distance) {
best_distance = distance;
best_matching_panel = panel;
*target_edge = BOTTOM_EDGE;
target_bounds->SetRect(potential_dragging_bounds.x(),
panel_bounds.bottom(),
potential_dragging_bounds.width(),
potential_dragging_bounds.height());
}
}
}
return best_matching_panel;
}
void PanelDragController::MovePanelAndBelowToCollection(
Panel* panel,
PanelCollection* target_collection,
PanelCollection::PositioningMask positioning_mask) const {
StackedPanelCollection* stack = panel->stack();
if (!stack) {
panel_manager_->MovePanelToCollection(panel,
target_collection,
positioning_mask);
return;
}
std::list<Panel*> panels_to_move;
StackedPanelCollection::Panels::const_iterator iter = stack->panels().begin();
for (; iter != stack->panels().end(); ++iter)
if ((*iter) == panel)
break;
for (; iter != stack->panels().end(); ++iter) {
if (positioning_mask & PanelCollection::TOP_POSITION)
panels_to_move.push_front(*iter);
else
panels_to_move.push_back(*iter);
}
for (std::list<Panel*>::const_iterator panel_iter = panels_to_move.begin();
panel_iter != panels_to_move.end(); ++panel_iter) {
panel_manager_->MovePanelToCollection(*panel_iter,
target_collection,
positioning_mask);
}
if (stack && stack->num_panels() <= 1) {
if (stack->num_panels() == 1) {
panel_manager_->MovePanelToCollection(
stack->top_panel(),
panel_manager_->detached_collection(),
PanelCollection::KNOWN_POSITION);
}
if (stack != dragging_panel_original_collection_)
panel_manager_->RemoveStack(stack);
}
}