summaryrefslogtreecommitdiffstats
path: root/ash/drag_drop/drag_drop_controller.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ash/drag_drop/drag_drop_controller.cc')
-rw-r--r--ash/drag_drop/drag_drop_controller.cc274
1 files changed, 223 insertions, 51 deletions
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index bde1b36..4e947ba 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -9,6 +9,7 @@
#include "ash/shell.h"
#include "ash/wm/coordinate_conversion.h"
#include "ash/wm/cursor_manager.h"
+#include "base/bind.h"
#include "base/message_loop.h"
#include "base/run_loop.h"
#include "ui/aura/client/capture_client.h"
@@ -22,6 +23,7 @@
#include "ui/base/events/event.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_conversions.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/native_widget_aura.h"
@@ -33,8 +35,41 @@ using aura::RootWindow;
namespace {
// The duration of the drag cancel animation in millisecond.
const int kCancelAnimationDuration = 250;
+const int kTouchCancelAnimationDuration = 20;
// The frame rate of the drag cancel animation in hertz.
const int kCancelAnimationFrameRate = 60;
+
+// For touch initiated dragging, we scale and shift drag image by the following:
+static const float kTouchDragImageScale = 1.2;
+static const int kTouchDragImageVerticalOffset = -25;
+
+// Adjusts the drag image bounds such that the new bounds are scaled by |scale|
+// and translated by the |drag_image_offset| and and additional
+// |vertical_offset|.
+gfx::Rect AdjustDragImageBoundsForScaleAndOffset(
+ const gfx::Rect& drag_image_bounds,
+ int vertical_offset,
+ float scale,
+ gfx::Vector2d* drag_image_offset) {
+ gfx::PointF final_origin = drag_image_bounds.origin();
+ gfx::SizeF final_size = drag_image_bounds.size();
+ final_size.Scale(scale);
+ drag_image_offset->set_x(drag_image_offset->x() * scale);
+ drag_image_offset->set_y(drag_image_offset->y() * scale);
+ float total_x_offset = drag_image_offset->x();
+ float total_y_offset = drag_image_offset->y() - vertical_offset;
+ final_origin.Offset(-total_x_offset, -total_y_offset);
+ return gfx::ToEnclosingRect(gfx::RectF(final_origin, final_size));
+}
+
+void DispatchGestureEndToWindow(aura::Window* window) {
+ if (window && window->delegate()) {
+ ui::GestureEvent gesture_end(ui::ET_GESTURE_END, 0, 0, 0,
+ base::Time::Now() - base::Time::FromDoubleT(0),
+ ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 0);
+ window->delegate()->OnGestureEvent(&gesture_end);
+ }
+}
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -45,7 +80,10 @@ DragDropController::DragDropController()
drag_data_(NULL),
drag_operation_(0),
drag_window_(NULL),
- should_block_during_drag_drop_(true) {
+ drag_source_window_(NULL),
+ should_block_during_drag_drop_(true),
+ current_drag_event_source_(ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE),
+ weak_factory_(this) {
Shell::GetInstance()->AddPreTargetHandler(this);
}
@@ -65,26 +103,60 @@ int DragDropController::StartDragAndDrop(
const gfx::Point& root_location,
int operation,
ui::DragDropTypes::DragEventSource source) {
- DCHECK(!IsDragDropInProgress());
+ if (IsDragDropInProgress())
+ return 0;
- drag_drop_tracker_.reset(new DragDropTracker);
+ const ui::OSExchangeDataProviderAura& provider =
+ static_cast<const ui::OSExchangeDataProviderAura&>(data.provider());
+ // We do not support touch drag/drop without a drag image.
+ if (source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH &&
+ provider.drag_image().size().IsEmpty())
+ return 0;
+
+ current_drag_event_source_ = source;
+ DragDropTracker* tracker = new DragDropTracker;
+ if (source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH) {
+ // We need to transfer the current gesture sequence and the GR's touch event
+ // queue to the |drag_drop_tracker_|'s capture window so that when it takes
+ // capture, it still gets a valid gesture state.
+ root_window->gesture_recognizer()->TransferEventsTo(source_window,
+ tracker->capture_window());
+ // We also send a gesture end to the source window so it can clear state.
+ // TODO(varunjain): Remove this whole block when gesture sequence
+ // transferring is properly done in the GR (http://crbug.com/160558)
+ DispatchGestureEndToWindow(source_window);
+ }
+ tracker->TakeCapture();
+ drag_drop_tracker_.reset(tracker);
+ drag_source_window_ = source_window;
+ if (drag_source_window_)
+ drag_source_window_->AddObserver(this);
+ pending_long_tap_.reset();
drag_data_ = &data;
drag_operation_ = operation;
- const ui::OSExchangeDataProviderAura& provider =
- static_cast<const ui::OSExchangeDataProviderAura&>(data.provider());
+ float drag_image_scale = 1;
+ int drag_image_vertical_offset = 0;
+ if (source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH) {
+ drag_image_scale = kTouchDragImageScale;
+ drag_image_vertical_offset = kTouchDragImageVerticalOffset;
+ }
+ gfx::Point start_location = root_location;
+ ash::wm::ConvertPointToScreen(root_window, &start_location);
+ drag_image_final_bounds_for_cancel_animation_ = gfx::Rect(
+ start_location - provider.drag_image_offset(),
+ provider.drag_image().size());
drag_image_.reset(new DragImageView);
drag_image_->SetImage(provider.drag_image());
drag_image_offset_ = provider.drag_image_offset();
- drag_image_->SetBoundsInScreen(gfx::Rect(
- root_location - drag_image_offset_,
- drag_image_->GetPreferredSize()));
+ gfx::Rect drag_image_bounds(start_location, drag_image_->GetPreferredSize());
+ drag_image_bounds = AdjustDragImageBoundsForScaleAndOffset(drag_image_bounds,
+ drag_image_vertical_offset, drag_image_scale, &drag_image_offset_);
+ drag_image_->SetBoundsInScreen(drag_image_bounds);
drag_image_->SetWidgetVisible(true);
drag_window_ = NULL;
- drag_start_location_ = root_location - drag_image_offset_;
- ash::wm::ConvertPointToScreen(root_window, &drag_start_location_);
// Ends cancel animation if it's in progress.
if (cancel_animation_.get())
@@ -100,6 +172,15 @@ int DragDropController::StartDragAndDrop(
}
#endif // !defined(OS_MACOSX)
+ if (!cancel_animation_.get() || !cancel_animation_->is_animating() ||
+ !pending_long_tap_.get()) {
+ // If drag cancel animation is running, this cleanup is done when the
+ // animation completes.
+ if (drag_source_window_)
+ drag_source_window_->RemoveObserver(this);
+ drag_source_window_ = NULL;
+ }
+
return drag_operation_;
}
@@ -110,10 +191,13 @@ void DragDropController::DragUpdate(aura::Window* target,
if (drag_window_) {
if ((delegate = aura::client::GetDragDropDelegate(drag_window_)))
delegate->OnDragExited();
- drag_window_->RemoveObserver(this);
+ if (drag_window_ != drag_source_window_)
+ drag_window_->RemoveObserver(this);
}
drag_window_ = target;
- drag_window_->AddObserver(this);
+ // We are already an observer of |drag_source_window_| so no need to add.
+ if (drag_window_ != drag_source_window_)
+ drag_window_->AddObserver(this);
if ((delegate = aura::client::GetDragDropDelegate(drag_window_))) {
ui::DropTargetEvent e(*drag_data_,
event.location(),
@@ -169,7 +253,7 @@ void DragDropController::Drop(aura::Window* target,
e.set_flags(event.flags());
drag_operation_ = delegate->OnPerformDrop(e);
if (drag_operation_ == 0)
- StartCanceledAnimation();
+ StartCanceledAnimation(kCancelAnimationDuration);
else
drag_image_.reset();
} else {
@@ -182,21 +266,7 @@ void DragDropController::Drop(aura::Window* target,
}
void DragDropController::DragCancel() {
- ash::Shell::GetInstance()->cursor_manager()->SetCursor(ui::kCursorPointer);
-
- // |drag_window_| can be NULL if we have just started the drag and have not
- // received any DragUpdates, or, if the |drag_window_| gets destroyed during
- // a drag/drop.
- aura::client::DragDropDelegate* delegate = drag_window_?
- aura::client::GetDragDropDelegate(drag_window_) : NULL;
- if (delegate)
- delegate->OnDragExited();
-
- Cleanup();
- drag_operation_ = 0;
- StartCanceledAnimation();
- if (should_block_during_drag_drop_)
- quit_closure_.Run();
+ DoDragCancel(kCancelAnimationDuration);
}
bool DragDropController::IsDragDropInProgress() {
@@ -214,13 +284,19 @@ ui::EventResult DragDropController::OnKeyEvent(ui::KeyEvent* event) {
ui::EventResult DragDropController::OnMouseEvent(ui::MouseEvent* event) {
if (!IsDragDropInProgress())
return ui::ER_UNHANDLED;
+
+ // If current drag session was not started by mouse, dont process this mouse
+ // event, but consume it so it does not interfere with current drag session.
+ if (current_drag_event_source_ != ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE)
+ return ui::ER_CONSUMED;
+
aura::Window* translated_target = drag_drop_tracker_->GetTarget(*event);
if (!translated_target) {
DragCancel();
return ui::ER_CONSUMED;
}
- scoped_ptr<ui::MouseEvent> translated_event(
- drag_drop_tracker_->ConvertMouseEvent(translated_target, *event));
+ scoped_ptr<ui::LocatedEvent> translated_event(
+ drag_drop_tracker_->ConvertEvent(translated_target, *event));
switch (translated_event->type()) {
case ui::ET_MOUSE_DRAGGED:
DragUpdate(translated_target, *translated_event.get());
@@ -238,26 +314,77 @@ ui::EventResult DragDropController::OnMouseEvent(ui::MouseEvent* event) {
}
ui::EventResult DragDropController::OnTouchEvent(ui::TouchEvent* event) {
- // TODO(sad): Also check for the touch-id.
- // TODO(varunjain): Add code for supporting drag-and-drop across displays
- // (http://crbug.com/114755).
- aura::Window* target = static_cast<aura::Window*>(event->target());
if (!IsDragDropInProgress())
return ui::ER_UNHANDLED;
+
+ // If current drag session was not started by touch, dont process this touch
+ // event, but consume it so it does not interfere with current drag session.
+ if (current_drag_event_source_ != ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH)
+ return ui::ER_CONSUMED;
+
switch (event->type()) {
- case ui::ET_TOUCH_MOVED:
- DragUpdate(target, *event);
- break;
- case ui::ET_TOUCH_RELEASED:
- Drop(target, *event);
- break;
case ui::ET_TOUCH_CANCELLED:
DragCancel();
break;
default:
- return ui::ER_UNHANDLED;
+ break;
}
- return ui::ER_CONSUMED;
+ return ui::ER_UNHANDLED;
+}
+
+ui::EventResult DragDropController::OnGestureEvent(ui::GestureEvent* event) {
+ if (!IsDragDropInProgress())
+ return ui::ER_UNHANDLED;
+
+ // If current drag session was not started by touch, dont process this touch
+ // event, but consume it so it does not interfere with current drag session.
+ if (current_drag_event_source_ != ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH)
+ return ui::ER_CONSUMED;
+
+ // Apply kTouchDragImageVerticalOffset to the location.
+ ui::GestureEvent touch_offset_event(*event,
+ static_cast<aura::Window*>(NULL),
+ static_cast<aura::Window*>(NULL));
+ gfx::Point touch_offset_location = touch_offset_event.location();
+ gfx::Point touch_offset_root_location = touch_offset_event.root_location();
+ touch_offset_location.Offset(0, kTouchDragImageVerticalOffset);
+ touch_offset_root_location.Offset(0, kTouchDragImageVerticalOffset);
+ touch_offset_event.set_location(touch_offset_location);
+ touch_offset_event.set_root_location(touch_offset_root_location);
+
+ aura::Window* translated_target =
+ drag_drop_tracker_->GetTarget(touch_offset_event);
+ if (!translated_target) {
+ DragCancel();
+ return ui::ER_HANDLED;
+ }
+ scoped_ptr<ui::LocatedEvent> translated_event(
+ drag_drop_tracker_->ConvertEvent(translated_target, touch_offset_event));
+
+ switch (event->type()) {
+ case ui::ET_GESTURE_SCROLL_UPDATE:
+ DragUpdate(translated_target, *translated_event.get());
+ break;
+ case ui::ET_GESTURE_SCROLL_END:
+ Drop(translated_target, *translated_event.get());
+ break;
+ case ui::ET_SCROLL_FLING_START:
+ case ui::ET_GESTURE_LONG_TAP:
+ // Ideally we would want to just forward this long tap event to the
+ // |drag_source_window_|. However, webkit does not accept events while a
+ // drag drop is still in progress. The drag drop ends only when the nested
+ // message loop ends. Due to this stupidity, we have to defer forwarding
+ // the long tap.
+ pending_long_tap_.reset(
+ new ui::GestureEvent(*event,
+ static_cast<aura::Window*>(drag_drop_tracker_->capture_window()),
+ static_cast<aura::Window*>(drag_source_window_)));
+ DoDragCancel(kTouchCancelAnimationDuration);
+ break;
+ default:
+ break;
+ }
+ return ui::ER_HANDLED;
}
void DragDropController::OnWindowDestroyed(aura::Window* window) {
@@ -265,42 +392,87 @@ void DragDropController::OnWindowDestroyed(aura::Window* window) {
drag_window_->RemoveObserver(this);
drag_window_ = NULL;
}
+ if (drag_source_window_ == window) {
+ drag_source_window_->RemoveObserver(this);
+ drag_source_window_ = NULL;
+ }
}
////////////////////////////////////////////////////////////////////////////////
// DragDropController, private:
void DragDropController::AnimationEnded(const ui::Animation* animation) {
- drag_image_->SetScreenPosition(drag_start_location_);
+ drag_image_->SetScreenPosition(
+ drag_image_final_bounds_for_cancel_animation_.origin());
cancel_animation_.reset();
// By the time we finish animation, another drag/drop session may have
// started. We do not want to destroy the drag image in that case.
if (!IsDragDropInProgress())
drag_image_.reset();
+ if (pending_long_tap_.get()) {
+ // If not in a nested message loop, we can forward the long tap right now.
+ if (!should_block_during_drag_drop_)
+ ForwardPendingLongTap();
+ else {
+ // See comment about this in OnGestureEvent().
+ MessageLoopForUI::current()->PostTask(
+ FROM_HERE, base::Bind(&DragDropController::ForwardPendingLongTap,
+ weak_factory_.GetWeakPtr()));
+ }
+ }
+}
+
+void DragDropController::DoDragCancel(int drag_cancel_animation_duration_ms) {
+ ash::Shell::GetInstance()->cursor_manager()->SetCursor(ui::kCursorPointer);
+
+ // |drag_window_| can be NULL if we have just started the drag and have not
+ // received any DragUpdates, or, if the |drag_window_| gets destroyed during
+ // a drag/drop.
+ aura::client::DragDropDelegate* delegate = drag_window_?
+ aura::client::GetDragDropDelegate(drag_window_) : NULL;
+ if (delegate)
+ delegate->OnDragExited();
+
+ Cleanup();
+ drag_operation_ = 0;
+ StartCanceledAnimation(drag_cancel_animation_duration_ms);
+ if (should_block_during_drag_drop_)
+ quit_closure_.Run();
}
void DragDropController::AnimationProgressed(const ui::Animation* animation) {
- drag_image_->SetScreenPosition(gfx::Point(
- animation->CurrentValueBetween(drag_cancel_location_.x(),
- drag_start_location_.x()),
- animation->CurrentValueBetween(drag_cancel_location_.y(),
- drag_start_location_.y())));
+ gfx::Rect current_bounds = animation->CurrentValueBetween(
+ drag_image_initial_bounds_for_cancel_animation_,
+ drag_image_final_bounds_for_cancel_animation_);
+ drag_image_->SetBoundsInScreen(current_bounds);
}
void DragDropController::AnimationCanceled(const ui::Animation* animation) {
AnimationEnded(animation);
}
-void DragDropController::StartCanceledAnimation() {
+void DragDropController::StartCanceledAnimation(int animation_duration_ms) {
DCHECK(drag_image_.get());
- drag_cancel_location_ = drag_image_->GetBoundsInScreen().origin();
- cancel_animation_.reset(new ui::LinearAnimation(kCancelAnimationDuration,
+ drag_image_initial_bounds_for_cancel_animation_ =
+ drag_image_->GetBoundsInScreen();
+ cancel_animation_.reset(new ui::LinearAnimation(animation_duration_ms,
kCancelAnimationFrameRate,
this));
cancel_animation_->Start();
}
+void DragDropController::ForwardPendingLongTap() {
+ if (drag_source_window_ && drag_source_window_->delegate()) {
+ drag_source_window_->delegate()->OnGestureEvent(pending_long_tap_.get());
+ DispatchGestureEndToWindow(drag_source_window_);
+ }
+ pending_long_tap_.reset();
+ if (drag_source_window_)
+ drag_source_window_->RemoveObserver(this);
+ drag_source_window_ = NULL;
+}
+
void DragDropController::Cleanup() {
if (drag_window_)
drag_window_->RemoveObserver(this);