summaryrefslogtreecommitdiffstats
path: root/ash/drag_drop
diff options
context:
space:
mode:
authorvarunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-28 12:11:49 +0000
committervarunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-28 12:11:49 +0000
commitffa4e448cf15245002c286ea781717426aa3eff4 (patch)
treeca251556ea039f07dfc5a4bb96817aaaac8c656a /ash/drag_drop
parent186b661f7383039cecfb392643349eb0c5fce2a8 (diff)
downloadchromium_src-ffa4e448cf15245002c286ea781717426aa3eff4.zip
chromium_src-ffa4e448cf15245002c286ea781717426aa3eff4.tar.gz
chromium_src-ffa4e448cf15245002c286ea781717426aa3eff4.tar.bz2
aura: Enable touch initiated drag and drop.
Touch drag/drop in aura follows the following rules: 1. Initiate drag on long press gesture. Give the user a visual cue by showing a scaled up drag image. 2. Do not initiate dnd if there is no drag image. 3. If, after initiating a drag, the user lifts up their finger without moving, cancel the drag and show a context menu (this is implemented using the long tap gesture). BUG=114755 Review URL: https://chromiumcodereview.appspot.com/11368131 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@169901 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash/drag_drop')
-rw-r--r--ash/drag_drop/drag_drop_controller.cc274
-rw-r--r--ash/drag_drop/drag_drop_controller.h32
-rw-r--r--ash/drag_drop/drag_drop_controller_unittest.cc154
-rw-r--r--ash/drag_drop/drag_drop_tracker.cc9
-rw-r--r--ash/drag_drop/drag_drop_tracker.h11
-rw-r--r--ash/drag_drop/drag_drop_tracker_unittest.cc24
-rw-r--r--ash/drag_drop/drag_image_view.cc17
-rw-r--r--ash/drag_drop/drag_image_view.h4
8 files changed, 450 insertions, 75 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);
diff --git a/ash/drag_drop/drag_drop_controller.h b/ash/drag_drop/drag_drop_controller.h
index bdeb06f..9c6375f 100644
--- a/ash/drag_drop/drag_drop_controller.h
+++ b/ash/drag_drop/drag_drop_controller.h
@@ -7,13 +7,14 @@
#include "ash/ash_export.h"
#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
#include "ui/aura/client/drag_drop_client.h"
#include "ui/aura/window_observer.h"
#include "ui/base/animation/animation_delegate.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/events/event_constants.h"
#include "ui/base/events/event_handler.h"
-#include "ui/gfx/point.h"
+#include "ui/gfx/rect.h"
namespace aura {
class RootWindow;
@@ -67,10 +68,15 @@ class ASH_EXPORT DragDropController
virtual ui::EventResult OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
virtual ui::EventResult OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
virtual ui::EventResult OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
+ virtual ui::EventResult OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
// Overridden from aura::WindowObserver.
virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+ protected:
+ // Actual implementation of |DragCancel()|. protected for testing.
+ virtual void DoDragCancel(int drag_cancel_animation_duration_ms);
+
private:
friend class ash::test::DragDropControllerTest;
@@ -80,7 +86,10 @@ class ASH_EXPORT DragDropController
virtual void AnimationCanceled(const ui::Animation* animation) OVERRIDE;
// Helper method to start drag widget flying back animation.
- void StartCanceledAnimation();
+ void StartCanceledAnimation(int animation_duration_ms);
+
+ // Helper method to forward |pending_log_tap_| event to |drag_source_window_|.
+ void ForwardPendingLongTap();
// Helper method to reset everything.
void Cleanup();
@@ -92,13 +101,16 @@ class ASH_EXPORT DragDropController
// Window that is currently under the drag cursor.
aura::Window* drag_window_;
- // The location where drag is started in screen coordinate.
- gfx::Point drag_start_location_;
- // The location where drag is canceled in screen coordinate.
- gfx::Point drag_cancel_location_;
+
+ // Starting and final bounds for the drag image for the drag cancel animation.
+ gfx::Rect drag_image_initial_bounds_for_cancel_animation_;
+ gfx::Rect drag_image_final_bounds_for_cancel_animation_;
scoped_ptr<ui::LinearAnimation> cancel_animation_;
+ // Window that started the drag.
+ aura::Window* drag_source_window_;
+
// Indicates whether the caller should be blocked on a drag/drop session.
// Only be used for tests.
bool should_block_during_drag_drop_;
@@ -108,6 +120,14 @@ class ASH_EXPORT DragDropController
scoped_ptr<ash::internal::DragDropTracker> drag_drop_tracker_;
+ ui::DragDropTypes::DragEventSource current_drag_event_source_;
+
+ // Holds a synthetic long tap event to be sent to the |drag_source_window_|.
+ // See comment in OnGestureEvent() on why we need this.
+ scoped_ptr<ui::GestureEvent> pending_long_tap_;
+
+ base::WeakPtrFactory<DragDropController> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(DragDropController);
};
diff --git a/ash/drag_drop/drag_drop_controller_unittest.cc b/ash/drag_drop/drag_drop_controller_unittest.cc
index 4474207..45c06c0 100644
--- a/ash/drag_drop/drag_drop_controller_unittest.cc
+++ b/ash/drag_drop/drag_drop_controller_unittest.cc
@@ -7,6 +7,7 @@
#include "ash/drag_drop/drag_image_view.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
+#include "base/command_line.h"
#include "base/location.h"
#include "base/utf_string_conversions.h"
#include "ui/aura/root_window.h"
@@ -16,7 +17,11 @@
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/dragdrop/os_exchange_data_provider_aura.h"
#include "ui/base/events/event.h"
+#include "ui/base/gestures/gesture_types.h"
+#include "ui/base/ui_base_switches.h"
+#include "ui/gfx/image/image_skia_rep.h"
#include "ui/views/test/test_views_delegate.h"
#include "ui/views/view.h"
#include "ui/views/views_delegate.h"
@@ -42,6 +47,7 @@ class DragTestView : public views::View {
num_drag_updates_ = 0;
num_drops_ = 0;
drag_done_received_ = false;
+ long_tap_received_ = false;
}
int VerticalDragThreshold() {
@@ -57,6 +63,7 @@ class DragTestView : public views::View {
int num_drag_updates_;
int num_drops_;
bool drag_done_received_;
+ bool long_tap_received_;
private:
// View overrides:
@@ -66,12 +73,24 @@ class DragTestView : public views::View {
void WriteDragData(const gfx::Point& p, OSExchangeData* data) OVERRIDE {
data->SetString(UTF8ToUTF16("I am being dragged"));
+ ui::OSExchangeDataProviderAura& provider =
+ static_cast<ui::OSExchangeDataProviderAura&>(data->provider());
+ gfx::ImageSkiaRep* image = new gfx::ImageSkiaRep(
+ gfx::Size(10, 20), ui::SCALE_FACTOR_100P);
+ gfx::ImageSkia* image_skia = new gfx::ImageSkia(*image);
+ provider.set_drag_image(*image_skia);
}
bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
return true;
}
+ ui::EventResult OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
+ if (event->type() == ui::ET_GESTURE_LONG_TAP)
+ long_tap_received_ = true;
+ return ui::ER_UNHANDLED;
+ }
+
bool GetDropFormats(int* formats,
std::set<OSExchangeData::CustomFormat>* custom_formats) {
*formats = ui::OSExchangeData::STRING;
@@ -149,6 +168,11 @@ class TestDragDropController : public internal::DragDropController {
drag_canceled_ = true;
}
+ void DoDragCancel(int animation_duration_ms) OVERRIDE {
+ DragDropController::DoDragCancel(animation_duration_ms);
+ drag_canceled_ = true;
+ }
+
bool drag_start_received_;
int num_drag_updates_;
bool drop_received_;
@@ -216,6 +240,13 @@ void AddViewToWidgetAndResize(views::Widget* widget, views::View* view) {
widget->SetBounds(contents_view_bounds);
}
+void DispatchGesture(ui::EventType gesture_type, gfx::Point location) {
+ ui::GestureEvent gesture_event(gesture_type, location.x(), location.y(), 0,
+ base::Time::NowFromSystemTime() - base::Time(),
+ ui::GestureEventDetails(gesture_type, 0, 0), 1);
+ Shell::GetPrimaryRootWindow()->DispatchGestureEvent(&gesture_event);
+}
+
} // namespace
class DragDropControllerTest : public AshTestBase {
@@ -246,6 +277,15 @@ class DragDropControllerTest : public AshTestBase {
return drag_drop_controller_->drag_window_;
}
+ aura::Window* GetDragSourceWindow() {
+ return drag_drop_controller_->drag_source_window_;
+ }
+
+ void SetDragSourceWindow(aura::Window* drag_source_window) {
+ drag_drop_controller_->drag_source_window_ = drag_source_window;
+ drag_source_window->AddObserver(drag_drop_controller_.get());
+ }
+
aura::Window* GetDragImageWindow() {
return drag_drop_controller_->drag_image_.get() ?
drag_drop_controller_->drag_image_->GetWidget()->GetNativeWindow() :
@@ -689,6 +729,116 @@ TEST_F(DragDropControllerTest, PressingEscapeCancelsDragDrop) {
EXPECT_TRUE(drag_view->drag_done_received_);
}
+TEST_F(DragDropControllerTest, TouchDragDropInMultipleWindows) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableTouchDragDrop);
+ scoped_ptr<views::Widget> widget1(CreateNewWidget());
+ DragTestView* drag_view1 = new DragTestView;
+ AddViewToWidgetAndResize(widget1.get(), drag_view1);
+ scoped_ptr<views::Widget> widget2(CreateNewWidget());
+ DragTestView* drag_view2 = new DragTestView;
+ AddViewToWidgetAndResize(widget2.get(), drag_view2);
+ gfx::Rect widget1_bounds = widget1->GetClientAreaBoundsInScreen();
+ gfx::Rect widget2_bounds = widget2->GetClientAreaBoundsInScreen();
+ widget2->SetBounds(gfx::Rect(widget1_bounds.width(), 0,
+ widget2_bounds.width(), widget2_bounds.height()));
+
+ ui::OSExchangeData data;
+ data.SetString(UTF8ToUTF16("I am being dragged"));
+
+ aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
+ widget1->GetNativeView());
+ generator.PressTouch();
+ gfx::Point point = gfx::Rect(drag_view1->bounds()).CenterPoint();
+ DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point);
+ // Because we are not doing a blocking drag and drop, the original
+ // OSDragExchangeData object is lost as soon as we return from the drag
+ // initiation in DragDropController::StartDragAndDrop(). Hence we set the
+ // drag_data_ to a fake drag data object that we created.
+ UpdateDragData(&data);
+ gfx::Point gesture_location = point;
+ int num_drags = drag_view1->width();
+ for (int i = 0; i < num_drags; ++i) {
+ gesture_location.Offset(1, 0);
+ DispatchGesture(ui::ET_GESTURE_SCROLL_UPDATE, gesture_location);
+
+ // Execute any scheduled draws to process deferred mouse events.
+ RunAllPendingInMessageLoop();
+ }
+
+ DispatchGesture(ui::ET_GESTURE_SCROLL_END, gesture_location);
+
+ EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
+ EXPECT_EQ(num_drags, drag_drop_controller_->num_drag_updates_);
+ EXPECT_TRUE(drag_drop_controller_->drop_received_);
+ EXPECT_EQ(UTF8ToUTF16("I am being dragged"),
+ drag_drop_controller_->drag_string_);
+
+ EXPECT_EQ(1, drag_view1->num_drag_enters_);
+ int num_expected_updates = drag_view1->bounds().width() -
+ drag_view1->bounds().CenterPoint().x() - 1;
+ EXPECT_EQ(num_expected_updates, drag_view1->num_drag_updates_);
+ EXPECT_EQ(0, drag_view1->num_drops_);
+ EXPECT_EQ(1, drag_view1->num_drag_exits_);
+ EXPECT_TRUE(drag_view1->drag_done_received_);
+
+ EXPECT_EQ(1, drag_view2->num_drag_enters_);
+ num_expected_updates = num_drags - num_expected_updates;
+ EXPECT_EQ(num_expected_updates, drag_view2->num_drag_updates_);
+ EXPECT_EQ(1, drag_view2->num_drops_);
+ EXPECT_EQ(0, drag_view2->num_drag_exits_);
+ EXPECT_FALSE(drag_view2->drag_done_received_);
+}
+
+TEST_F(DragDropControllerTest, TouchDragDropCancelsOnLongTap) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableTouchDragDrop);
+ scoped_ptr<views::Widget> widget(CreateNewWidget());
+ DragTestView* drag_view = new DragTestView;
+ AddViewToWidgetAndResize(widget.get(), drag_view);
+ aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
+ widget->GetNativeView());
+
+ generator.PressTouch();
+ gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint();
+ DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point);
+ DispatchGesture(ui::ET_GESTURE_LONG_TAP, point);
+
+ EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
+ EXPECT_TRUE(drag_drop_controller_->drag_canceled_);
+ EXPECT_EQ(0, drag_drop_controller_->num_drag_updates_);
+ EXPECT_FALSE(drag_drop_controller_->drop_received_);
+ EXPECT_EQ(UTF8ToUTF16("I am being dragged"),
+ drag_drop_controller_->drag_string_);
+ EXPECT_EQ(0, drag_view->num_drag_enters_);
+ EXPECT_EQ(0, drag_view->num_drops_);
+ EXPECT_EQ(0, drag_view->num_drag_exits_);
+ EXPECT_TRUE(drag_view->drag_done_received_);
+}
+
+TEST_F(DragDropControllerTest, TouchDragDropLongTapGestureIsForwarded) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableTouchDragDrop);
+ scoped_ptr<views::Widget> widget(CreateNewWidget());
+ DragTestView* drag_view = new DragTestView;
+ AddViewToWidgetAndResize(widget.get(), drag_view);
+ aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
+ widget->GetNativeView());
+
+ generator.PressTouch();
+ gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint();
+ DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point);
+
+ // Since we are not running inside a nested loop, the |drag_source_window_|
+ // will get destroyed immediately. Hence we reassign it.
+ EXPECT_EQ(NULL, GetDragSourceWindow());
+ SetDragSourceWindow(widget->GetNativeView());
+ EXPECT_FALSE(drag_view->long_tap_received_);
+ DispatchGesture(ui::ET_GESTURE_LONG_TAP, point);
+ EndCancelAnimation();
+ EXPECT_TRUE(drag_view->long_tap_received_);
+}
+
namespace {
class DragImageWindowObserver : public aura::WindowObserver {
@@ -790,6 +940,10 @@ TEST_F(DragDropControllerTest, DragCancelAcrossDisplays) {
EXPECT_EQ("405,405", observer.window_location_on_destroying().ToString());
}
+ for (Shell::RootWindowList::iterator iter = root_windows.begin();
+ iter != root_windows.end(); ++iter) {
+ aura::client::SetDragDropClient(*iter, NULL);
+ }
}
} // namespace test
diff --git a/ash/drag_drop/drag_drop_tracker.cc b/ash/drag_drop/drag_drop_tracker.cc
index f0bb206..2258755 100644
--- a/ash/drag_drop/drag_drop_tracker.cc
+++ b/ash/drag_drop/drag_drop_tracker.cc
@@ -30,13 +30,16 @@ namespace internal {
DragDropTracker::DragDropTracker()
: capture_window_(CreateCaptureWindow()) {
- capture_window_->SetCapture();
}
DragDropTracker::~DragDropTracker() {
capture_window_->ReleaseCapture();
}
+void DragDropTracker::TakeCapture() {
+ capture_window_->SetCapture();
+}
+
aura::Window* DragDropTracker::GetTarget(const ui::LocatedEvent& event) {
DCHECK(capture_window_.get());
gfx::Point location_in_screen = event.location();
@@ -49,9 +52,9 @@ aura::Window* DragDropTracker::GetTarget(const ui::LocatedEvent& event) {
return root_window_at_point->GetEventHandlerForPoint(location_in_root);
}
-ui::MouseEvent* DragDropTracker::ConvertMouseEvent(
+ui::LocatedEvent* DragDropTracker::ConvertEvent(
aura::Window* target,
- const ui::MouseEvent& event) {
+ const ui::LocatedEvent& event) {
DCHECK(capture_window_.get());
gfx::Point target_location = event.location();
aura::Window::ConvertPointToTarget(capture_window_.get(), target,
diff --git a/ash/drag_drop/drag_drop_tracker.h b/ash/drag_drop/drag_drop_tracker.h
index 56b6309..6493b06 100644
--- a/ash/drag_drop/drag_drop_tracker.h
+++ b/ash/drag_drop/drag_drop_tracker.h
@@ -27,6 +27,13 @@ class ASH_EXPORT DragDropTracker {
DragDropTracker();
~DragDropTracker();
+ aura::Window* capture_window() { return capture_window_.get(); }
+
+ // Tells our |capture_window_| to take capture. This is not done right at
+ // creation to give the caller a chance to perform any operations needed
+ // before the capture is transfered.
+ void TakeCapture();
+
// Gets the target located at |event| in the coordinates of the active root
// window.
aura::Window* GetTarget(const ui::LocatedEvent& event);
@@ -34,8 +41,8 @@ class ASH_EXPORT DragDropTracker {
// Converts the locations of |event| in the coordinates of the active root
// window to the ones in |target|'s coordinates.
// Caller takes ownership of the returned object.
- ui::MouseEvent* ConvertMouseEvent(aura::Window* target,
- const ui::MouseEvent& event);
+ ui::LocatedEvent* ConvertEvent(aura::Window* target,
+ const ui::LocatedEvent& event);
private:
// A window for capturing drag events while dragging.
diff --git a/ash/drag_drop/drag_drop_tracker_unittest.cc b/ash/drag_drop/drag_drop_tracker_unittest.cc
index dfefe82..279e17e 100644
--- a/ash/drag_drop/drag_drop_tracker_unittest.cc
+++ b/ash/drag_drop/drag_drop_tracker_unittest.cc
@@ -43,11 +43,11 @@ class DragDropTrackerTest : public test::AshTestBase {
return target;
}
- static ui::MouseEvent* ConvertMouseEvent(aura::Window* target,
+ static ui::LocatedEvent* ConvertEvent(aura::Window* target,
const ui::MouseEvent& event) {
scoped_ptr<internal::DragDropTracker> tracker(
new internal::DragDropTracker);
- ui::MouseEvent* converted = tracker->ConvertMouseEvent(target, event);
+ ui::LocatedEvent* converted = tracker->ConvertEvent(target, event);
return converted;
}
};
@@ -122,12 +122,12 @@ TEST_F(DragDropTrackerTest, MAYBE_GetTarget) {
// TODO(mazda): Remove this once ash/wm/coordinate_conversion.h supports
// non-X11 platforms.
#if defined(USE_X11)
-#define MAYBE_ConvertMouseEvent ConvertMouseEvent
+#define MAYBE_ConvertEvent ConvertEvent
#else
-#define MAYBE_ConvertMouseEvent DISABLED_ConvertMouseEvent
+#define MAYBE_ConvertEvent DISABLED_ConvertEvent
#endif
-TEST_F(DragDropTrackerTest, MAYBE_ConvertMouseEvent) {
+TEST_F(DragDropTrackerTest, MAYBE_ConvertEvent) {
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
EXPECT_EQ(2U, root_windows.size());
@@ -150,8 +150,8 @@ TEST_F(DragDropTrackerTest, MAYBE_ConvertMouseEvent) {
gfx::Point(50, 50),
gfx::Point(50, 50),
ui::EF_NONE);
- scoped_ptr<ui::MouseEvent> converted00(ConvertMouseEvent(window0.get(),
- original00));
+ scoped_ptr<ui::LocatedEvent> converted00(ConvertEvent(window0.get(),
+ original00));
EXPECT_EQ(original00.type(), converted00->type());
EXPECT_EQ("50,50", converted00->location().ToString());
EXPECT_EQ("50,50", converted00->root_location().ToString());
@@ -163,8 +163,8 @@ TEST_F(DragDropTrackerTest, MAYBE_ConvertMouseEvent) {
gfx::Point(350, 150),
gfx::Point(350, 150),
ui::EF_NONE);
- scoped_ptr<ui::MouseEvent> converted01(ConvertMouseEvent(window1.get(),
- original01));
+ scoped_ptr<ui::LocatedEvent> converted01(ConvertEvent(window1.get(),
+ original01));
EXPECT_EQ(original01.type(), converted01->type());
EXPECT_EQ("50,50", converted01->location().ToString());
EXPECT_EQ("150,150", converted01->root_location().ToString());
@@ -179,8 +179,8 @@ TEST_F(DragDropTrackerTest, MAYBE_ConvertMouseEvent) {
gfx::Point(-150, 50),
gfx::Point(-150, 50),
ui::EF_NONE);
- scoped_ptr<ui::MouseEvent> converted10(ConvertMouseEvent(window0.get(),
- original10));
+ scoped_ptr<ui::LocatedEvent> converted10(ConvertEvent(window0.get(),
+ original10));
EXPECT_EQ(original10.type(), converted10->type());
EXPECT_EQ("50,50", converted10->location().ToString());
EXPECT_EQ("50,50", converted10->root_location().ToString());
@@ -192,7 +192,7 @@ TEST_F(DragDropTrackerTest, MAYBE_ConvertMouseEvent) {
gfx::Point(150, 150),
gfx::Point(150, 150),
ui::EF_NONE);
- scoped_ptr<ui::MouseEvent> converted11(ConvertMouseEvent(window1.get(),
+ scoped_ptr<ui::LocatedEvent> converted11(ConvertEvent(window1.get(),
original11));
EXPECT_EQ(original11.type(), converted11->type());
EXPECT_EQ("50,50", converted11->location().ToString());
diff --git a/ash/drag_drop/drag_image_view.cc b/ash/drag_drop/drag_image_view.cc
index 6490d6f..fb28b9d 100644
--- a/ash/drag_drop/drag_image_view.cc
+++ b/ash/drag_drop/drag_image_view.cc
@@ -4,7 +4,9 @@
#include "ash/drag_drop/drag_image_view.h"
+#include "skia/ext/image_operations.h"
#include "ui/aura/window.h"
+#include "ui/gfx/canvas.h"
#include "ui/views/corewm/shadow_types.h"
#include "ui/views/widget/widget.h"
@@ -45,10 +47,11 @@ DragImageView::~DragImageView() {
void DragImageView::SetBoundsInScreen(const gfx::Rect& bounds) {
widget_->SetBounds(bounds);
+ widget_size_ = bounds.size();
}
void DragImageView::SetScreenPosition(const gfx::Point& position) {
- widget_->SetBounds(gfx::Rect(position, GetPreferredSize()));
+ widget_->SetBounds(gfx::Rect(position, widget_size_));
}
void DragImageView::SetWidgetVisible(bool visible) {
@@ -60,5 +63,17 @@ void DragImageView::SetWidgetVisible(bool visible) {
}
}
+void DragImageView::OnPaint(gfx::Canvas* canvas) {
+ if (GetImage().size() == widget_size_) {
+ canvas->DrawImageInt(GetImage(), 0, 0);
+ } else {
+ SkBitmap scaled = skia::ImageOperations::Resize(
+ *GetImage().bitmap(), skia::ImageOperations::RESIZE_LANCZOS3,
+ widget_size_.width(), widget_size_.height());
+ SkPaint paint;
+ canvas->sk_canvas()->drawBitmap(scaled, 0, 0, &paint);
+ }
+}
+
} // namespace internal
} // namespace ash
diff --git a/ash/drag_drop/drag_image_view.h b/ash/drag_drop/drag_image_view.h
index 63f718c..d8db83c 100644
--- a/ash/drag_drop/drag_image_view.h
+++ b/ash/drag_drop/drag_image_view.h
@@ -32,7 +32,11 @@ class DragImageView : public views::ImageView {
void SetWidgetVisible(bool visible);
private:
+ // Overridden from views::ImageView.
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
scoped_ptr<views::Widget> widget_;
+ gfx::Size widget_size_;
DISALLOW_COPY_AND_ASSIGN(DragImageView);
};