diff options
author | mazda@chromium.org <mazda@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-26 11:59:04 +0000 |
---|---|---|
committer | mazda@chromium.org <mazda@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-26 11:59:04 +0000 |
commit | 0518d0dde990e5382c94bde61e10cd3f3bbf7c27 (patch) | |
tree | 7bbe180593b3f2456ae4c4c842a1575a3e3b7cf1 /ash/drag_drop | |
parent | 7f219e315a3b6e6b262f75313f52b555632d9317 (diff) | |
download | chromium_src-0518d0dde990e5382c94bde61e10cd3f3bbf7c27.zip chromium_src-0518d0dde990e5382c94bde61e10cd3f3bbf7c27.tar.gz chromium_src-0518d0dde990e5382c94bde61e10cd3f3bbf7c27.tar.bz2 |
Support Drag and Drop across displays.
This CL introduces DragDropTracker, which
- Makes a dummy window capture events while dragging an item, and
- Dispatches mouse events to the window at the pointer location in DragDropController::PreHandleMouseEvent.
BUG=136817
Review URL: https://chromiumcodereview.appspot.com/10855159
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153401 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash/drag_drop')
-rw-r--r-- | ash/drag_drop/drag_drop_controller.cc | 43 | ||||
-rw-r--r-- | ash/drag_drop/drag_drop_controller.h | 7 | ||||
-rw-r--r-- | ash/drag_drop/drag_drop_controller_unittest.cc | 4 | ||||
-rw-r--r-- | ash/drag_drop/drag_drop_tracker.cc | 68 | ||||
-rw-r--r-- | ash/drag_drop/drag_drop_tracker.h | 51 | ||||
-rw-r--r-- | ash/drag_drop/drag_drop_tracker_unittest.cc | 212 | ||||
-rw-r--r-- | ash/drag_drop/drag_image_view.cc | 2 |
7 files changed, 366 insertions, 21 deletions
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc index c273bf0..5d44085 100644 --- a/ash/drag_drop/drag_drop_controller.cc +++ b/ash/drag_drop/drag_drop_controller.cc @@ -4,6 +4,7 @@ #include "ash/drag_drop/drag_drop_controller.h" +#include "ash/drag_drop/drag_drop_tracker.h" #include "ash/drag_drop/drag_image_view.h" #include "ash/shell.h" #include "ash/wm/coordinate_conversion.h" @@ -44,7 +45,6 @@ DragDropController::DragDropController() drag_data_(NULL), drag_operation_(0), drag_window_(NULL), - drag_drop_in_progress_(false), should_block_during_drag_drop_(true) { Shell::GetInstance()->AddEnvEventFilter(this); } @@ -57,16 +57,13 @@ DragDropController::~DragDropController() { } int DragDropController::StartDragAndDrop(const ui::OSExchangeData& data, + aura::RootWindow* root_window, const gfx::Point& root_location, int operation) { - DCHECK(!drag_drop_in_progress_); - // TODO(oshima): Add CaptureClient client API. - aura::Window* capture_window = - aura::client::GetCaptureWindow(Shell::GetPrimaryRootWindow()); - if (capture_window) - capture_window->ReleaseCapture(); - drag_drop_in_progress_ = true; + DCHECK(!IsDragDropInProgress()); + drag_cursor_ = ui::kCursorPointer; + drag_drop_tracker_.reset(new DragDropTracker(root_window)); drag_data_ = &data; drag_operation_ = operation; @@ -197,7 +194,7 @@ void DragDropController::DragCancel() { } bool DragDropController::IsDragDropInProgress() { - return drag_drop_in_progress_; + return !!drag_drop_tracker_.get(); } gfx::NativeCursor DragDropController::GetDragCursor() { @@ -206,7 +203,7 @@ gfx::NativeCursor DragDropController::GetDragCursor() { bool DragDropController::PreHandleKeyEvent(aura::Window* target, ui::KeyEvent* event) { - if (drag_drop_in_progress_ && event->key_code() == ui::VKEY_ESCAPE) { + if (IsDragDropInProgress() && event->key_code() == ui::VKEY_ESCAPE) { DragCancel(); return true; } @@ -215,17 +212,23 @@ bool DragDropController::PreHandleKeyEvent(aura::Window* target, bool DragDropController::PreHandleMouseEvent(aura::Window* target, ui::MouseEvent* event) { - if (!drag_drop_in_progress_) + if (!IsDragDropInProgress()) return false; - switch (event->type()) { + aura::Window* translated_target = drag_drop_tracker_->GetTarget(*event); + if (!translated_target) { + DragCancel(); + return true; + } + scoped_ptr<ui::MouseEvent> translated_event( + drag_drop_tracker_->ConvertMouseEvent(translated_target, *event)); + switch (translated_event->type()) { case ui::ET_MOUSE_DRAGGED: - DragUpdate(target, *event); + DragUpdate(translated_target, *translated_event.get()); break; case ui::ET_MOUSE_RELEASED: - Drop(target, *event); + Drop(translated_target, *translated_event.get()); break; default: - // We could reach here if the user drops outside the root window. // We could also reach here because RootWindow may sometimes generate a // bunch of fake mouse events // (aura::RootWindow::PostMouseMoveEventAfterWindowChange). @@ -238,7 +241,9 @@ ui::TouchStatus DragDropController::PreHandleTouchEvent( aura::Window* target, ui::TouchEvent* event) { // TODO(sad): Also check for the touch-id. - if (!drag_drop_in_progress_) + // TODO(varunjain): Add code for supporting drag-and-drop across displays + // (http://crbug.com/114755). + if (!IsDragDropInProgress()) return ui::TOUCH_STATUS_UNKNOWN; switch (event->type()) { case ui::ET_TOUCH_MOVED: @@ -277,7 +282,7 @@ void DragDropController::OnImplicitAnimationsCompleted() { // 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 (!drag_drop_in_progress_) + if (!IsDragDropInProgress()) drag_image_.reset(); } @@ -301,7 +306,9 @@ void DragDropController::Cleanup() { drag_window_->RemoveObserver(this); drag_window_ = NULL; drag_data_ = NULL; - drag_drop_in_progress_ = false; + // Cleanup can be called again while deleting DragDropTracker, so use Pass + // instead of reset to avoid double free. + drag_drop_tracker_.Pass(); } } // namespace internal diff --git a/ash/drag_drop/drag_drop_controller.h b/ash/drag_drop/drag_drop_controller.h index 468ba58..dabcbc1 100644 --- a/ash/drag_drop/drag_drop_controller.h +++ b/ash/drag_drop/drag_drop_controller.h @@ -16,6 +16,7 @@ #include "ui/gfx/point.h" namespace aura { +class RootWindow; class Window; } @@ -31,6 +32,7 @@ class DragDropControllerTest; namespace internal { +class DragDropTracker; class DragImageView; class ASH_EXPORT DragDropController @@ -48,6 +50,7 @@ class ASH_EXPORT DragDropController // Overridden from aura::client::DragDropClient: virtual int StartDragAndDrop(const ui::OSExchangeData& data, + aura::RootWindow* root_window, const gfx::Point& root_location, int operation) OVERRIDE; virtual void DragUpdate(aura::Window* target, @@ -95,8 +98,6 @@ class ASH_EXPORT DragDropController aura::Window* drag_window_; gfx::Point drag_start_location_; - bool drag_drop_in_progress_; - // Indicates whether the caller should be blocked on a drag/drop session. // Only be used for tests. bool should_block_during_drag_drop_; @@ -104,6 +105,8 @@ class ASH_EXPORT DragDropController // Closure for quitting nested message loop. base::Closure quit_closure_; + scoped_ptr<ash::internal::DragDropTracker> drag_drop_tracker_; + 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 ca6e368..7740186 100644 --- a/ash/drag_drop/drag_drop_controller_unittest.cc +++ b/ash/drag_drop/drag_drop_controller_unittest.cc @@ -127,11 +127,13 @@ class TestDragDropController : public internal::DragDropController { private: int StartDragAndDrop(const ui::OSExchangeData& data, + aura::RootWindow* root_window, const gfx::Point& location, int operation) OVERRIDE { drag_start_received_ = true; data.GetString(&drag_string_); - return DragDropController::StartDragAndDrop(data, location, operation); + return DragDropController::StartDragAndDrop( + data, root_window, location, operation); } void DragUpdate(aura::Window* target, diff --git a/ash/drag_drop/drag_drop_tracker.cc b/ash/drag_drop/drag_drop_tracker.cc new file mode 100644 index 0000000..0a04d105 --- /dev/null +++ b/ash/drag_drop/drag_drop_tracker.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/drag_drop/drag_drop_tracker.h" + +#include "ash/shell.h" +#include "ash/shell_window_ids.h" +#include "ash/wm/coordinate_conversion.h" +#include "ui/aura/root_window.h" +#include "ui/base/event.h" +#include "ui/gfx/screen.h" + +namespace { + +// Creates a window for capturing drag events. +aura::Window* CreateCaptureWindow(aura::RootWindow* root_window) { + aura::Window* window = new aura::Window(NULL); + window->SetType(aura::client::WINDOW_TYPE_NORMAL); + window->Init(ui::LAYER_NOT_DRAWN); + window->SetParent(NULL); + window->SetBoundsInScreen(root_window->GetBoundsInScreen(), + gfx::Screen::GetDisplayNearestWindow(root_window)); + window->Show(); + return window; +} + +} // namespace + +namespace ash { +namespace internal { + +DragDropTracker::DragDropTracker(aura::RootWindow* root_window) + : capture_window_(CreateCaptureWindow(root_window)) { + capture_window_->SetCapture(); +} + +DragDropTracker::~DragDropTracker() { + capture_window_->ReleaseCapture(); +} + +aura::Window* DragDropTracker::GetTarget(const ui::LocatedEvent& event) { + std::pair<aura::RootWindow*, gfx::Point> pair = + ash::wm::GetRootWindowRelativeToWindow(capture_window_.get(), + event.location()); + return pair.first->GetEventHandlerForPoint(pair.second); +} + +ui::MouseEvent* DragDropTracker::ConvertMouseEvent( + aura::Window* target, + const ui::MouseEvent& event) { + DCHECK(capture_window_.get()); + std::pair<aura::RootWindow*, gfx::Point> location_pair = + ash::wm::GetRootWindowRelativeToWindow(capture_window_.get(), + event.location()); + aura::Window::ConvertPointToTarget(location_pair.first, target, + &location_pair.second); + std::pair<aura::RootWindow*, gfx::Point> root_location_pair = + ash::wm::GetRootWindowRelativeToWindow(capture_window_->GetRootWindow(), + event.root_location()); + return new ui::MouseEvent(event.type(), + location_pair.second, + root_location_pair.second, + event.flags()); +} + +} // namespace internal +} // namespace ash diff --git a/ash/drag_drop/drag_drop_tracker.h b/ash/drag_drop/drag_drop_tracker.h new file mode 100644 index 0000000..fa56ed6 --- /dev/null +++ b/ash/drag_drop/drag_drop_tracker.h @@ -0,0 +1,51 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_DRAG_DROP_DRAG_DROP_TRACKER_H_ +#define ASH_DRAG_DROP_DRAG_DROP_TRACKER_H_ + +#include "ash/ash_export.h" +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "ui/base/event.h" + +namespace aura { +class RootWindow; +class Window; +} + +namespace ash { +namespace internal { + +// Provides functions for handling drag events inside and outside the root +// window where drag is started. This internally sets up a capture window for +// tracking drag events outside the root window where drag is initiated. +// ash/wm/coordinate_conversion.h is used internally and only X11 environment +// is supported for now. +class ASH_EXPORT DragDropTracker { + public: + explicit DragDropTracker(aura::RootWindow* root_window); + ~DragDropTracker(); + + // Gets the target located at |event| in the coordinates of the root window + // given to the constructor. + aura::Window* GetTarget(const ui::LocatedEvent& event); + + // Converts the locations of |event| in the coordinates of the root window + // given to the constructor to the ones in |target|'s coordinates. + // Caller takes ownership of the returned object. + ui::MouseEvent* ConvertMouseEvent(aura::Window* target, + const ui::MouseEvent& event); + + private: + // A window for capturing drag events while dragging. + scoped_ptr<aura::Window> capture_window_; + + DISALLOW_COPY_AND_ASSIGN(DragDropTracker); +}; + +} // namespace internal +} // namespace ash + +#endif // ASH_DRAG_DROP_DRAG_DROP_TRACKER_H_ diff --git a/ash/drag_drop/drag_drop_tracker_unittest.cc b/ash/drag_drop/drag_drop_tracker_unittest.cc new file mode 100644 index 0000000..ddc75ba --- /dev/null +++ b/ash/drag_drop/drag_drop_tracker_unittest.cc @@ -0,0 +1,212 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/drag_drop/drag_drop_tracker.h" + +#include "ash/shell.h" +#include "ash/shell_window_ids.h" +#include "ash/test/ash_test_base.h" +#include "base/memory/scoped_ptr.h" +#include "ui/aura/root_window.h" +#include "ui/aura/test/test_windows.h" +#include "ui/aura/window.h" + +namespace ash { +namespace test { + +class DragDropTrackerTest : public test::AshTestBase { + public: + virtual void SetUp() OVERRIDE { + AshTestBase::SetUp(); + UpdateDisplay("0+0-200x200,0+201-200x200"); + } + + static aura::Window* CreateTestWindow(const gfx::Rect& bounds, + aura::Window* parent) { + static int window_id = 0; + return aura::test::CreateTestWindowWithDelegate( + new aura::test::TestWindowDelegate, + window_id++, + bounds, + parent); + } + + static aura::Window* GetTarget(aura::RootWindow* root_window, + const gfx::Point& location) { + scoped_ptr<internal::DragDropTracker> tracker( + new internal::DragDropTracker(root_window)); + ui::MouseEvent e(ui::ET_MOUSE_DRAGGED, + location, + location, + ui::EF_NONE); + aura::Window* target = tracker->GetTarget(e); + return target; + } + + static ui::MouseEvent* ConvertMouseEvent(aura::RootWindow* root_window, + aura::Window* target, + const ui::MouseEvent& event) { + scoped_ptr<internal::DragDropTracker> tracker( + new internal::DragDropTracker(root_window)); + ui::MouseEvent* converted = tracker->ConvertMouseEvent(target, event); + return converted; + } +}; + +// TODO(mazda): Remove this once ash/wm/coordinate_conversion.h supports +// non-X11 platforms. +#if defined(USE_X11) +#define MAYBE_GetTarget GetTarget +#else +#define MAYBE_GetTarget DISABLED_GetTarget +#endif + +TEST_F(DragDropTrackerTest, MAYBE_GetTarget) { + Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); + EXPECT_EQ(2U, root_windows.size()); + + aura::Window* default_container0 = + ash::Shell::GetContainer(root_windows[0], + ash::internal::kShellWindowId_DefaultContainer); + EXPECT_TRUE(NULL != default_container0); + + scoped_ptr<aura::Window> window0( + CreateTestWindow(gfx::Rect(0, 0, 100, 100), default_container0)); + window0->Show(); + + aura::Window* default_container1 = + ash::Shell::GetContainer(root_windows[1], + ash::internal::kShellWindowId_DefaultContainer); + EXPECT_TRUE(NULL != default_container1); + + scoped_ptr<aura::Window> window1( + CreateTestWindow(gfx::Rect(100, 100, 100, 100), default_container1)); + window1->Show(); + + // Start tracking from the RootWindow0 and check the point on RootWindow0 that + // |window0| covers. + EXPECT_EQ(window0.get(), GetTarget(root_windows[0], gfx::Point(50, 50))); + + // Start tracking from the RootWindow0 and check the point on RootWindow0 that + // neither |window0| nor |window1| covers. + EXPECT_NE(window0.get(), GetTarget(root_windows[0], gfx::Point(150, 150))); + EXPECT_NE(window1.get(), GetTarget(root_windows[0], gfx::Point(150, 150))); + + // Start tracking from the RootWindow0 and check the point on RootWindow1 that + // |window1| covers. + EXPECT_EQ(window1.get(), GetTarget(root_windows[0], gfx::Point(150, 350))); + + // Start tracking from the RootWindow0 and check the point on RootWindow1 that + // neither |window0| nor |window1| covers. + EXPECT_NE(window0.get(), GetTarget(root_windows[0], gfx::Point(50, 250))); + EXPECT_NE(window1.get(), GetTarget(root_windows[0], gfx::Point(50, 250))); + + // Start tracking from the RootWindow1 and check the point on RootWindow0 that + // |window0| covers. + EXPECT_EQ(window0.get(), GetTarget(root_windows[1], gfx::Point(50, -150))); + + // Start tracking from the RootWindow1 and check the point on RootWindow0 that + // neither |window0| nor |window1| covers. + EXPECT_NE(window0.get(), GetTarget(root_windows[1], gfx::Point(150, -50))); + EXPECT_NE(window1.get(), GetTarget(root_windows[1], gfx::Point(150, -50))); + + // Start tracking from the RootWindow1 and check the point on RootWindow1 that + // |window1| covers. + EXPECT_EQ(window1.get(), GetTarget(root_windows[1], gfx::Point(150, 150))); + + // Start tracking from the RootWindow1 and check the point on RootWindow1 that + // neither |window0| nor |window1| covers. + EXPECT_NE(window0.get(), GetTarget(root_windows[1], gfx::Point(50, 50))); + EXPECT_NE(window1.get(), GetTarget(root_windows[1], gfx::Point(50, 50))); +} + +// TODO(mazda): Remove this once ash/wm/coordinate_conversion.h supports +// non-X11 platforms. +#if defined(USE_X11) +#define MAYBE_ConvertMouseEvent ConvertMouseEvent +#else +#define MAYBE_ConvertMouseEvent DISABLED_ConvertMouseEvent +#endif + +TEST_F(DragDropTrackerTest, MAYBE_ConvertMouseEvent) { + Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); + EXPECT_EQ(2U, root_windows.size()); + + aura::Window* default_container0 = + ash::Shell::GetContainer(root_windows[0], + ash::internal::kShellWindowId_DefaultContainer); + EXPECT_TRUE(NULL != default_container0); + + scoped_ptr<aura::Window> window0( + CreateTestWindow(gfx::Rect(0, 0, 100, 100), default_container0)); + window0->Show(); + + aura::Window* default_container1 = + ash::Shell::GetContainer(root_windows[1], + ash::internal::kShellWindowId_DefaultContainer); + EXPECT_TRUE(NULL != default_container1); + + scoped_ptr<aura::Window> window1( + CreateTestWindow(gfx::Rect(100, 100, 100, 100), default_container1)); + window1->Show(); + + // Start tracking from the RootWindow0 and converts the mouse event into + // |window0|'s coodinates. + ui::MouseEvent original00(ui::ET_MOUSE_DRAGGED, + gfx::Point(50, 50), + gfx::Point(50, 50), + ui::EF_NONE); + scoped_ptr<ui::MouseEvent> converted00(ConvertMouseEvent(root_windows[0], + window0.get(), + original00)); + EXPECT_EQ(original00.type(), converted00->type()); + EXPECT_EQ(gfx::Point(50, 50), converted00->location()); + EXPECT_EQ(gfx::Point(50, 50), converted00->root_location()); + EXPECT_EQ(original00.flags(), converted00->flags()); + + // Start tracking from the RootWindow0 and converts the mouse event into + // |window1|'s coodinates. + ui::MouseEvent original01(ui::ET_MOUSE_DRAGGED, + gfx::Point(150, 350), + gfx::Point(150, 350), + ui::EF_NONE); + scoped_ptr<ui::MouseEvent> converted01(ConvertMouseEvent(root_windows[0], + window1.get(), + original01)); + EXPECT_EQ(original01.type(), converted01->type()); + EXPECT_EQ(gfx::Point(50, 50), converted01->location()); + EXPECT_EQ(gfx::Point(150, 150), converted01->root_location()); + EXPECT_EQ(original01.flags(), converted01->flags()); + + // Start tracking from the RootWindow1 and converts the mouse event into + // |window0|'s coodinates. + ui::MouseEvent original10(ui::ET_MOUSE_DRAGGED, + gfx::Point(50, -150), + gfx::Point(50, -150), + ui::EF_NONE); + scoped_ptr<ui::MouseEvent> converted10(ConvertMouseEvent(root_windows[1], + window0.get(), + original10)); + EXPECT_EQ(original10.type(), converted10->type()); + EXPECT_EQ(gfx::Point(50, 50), converted10->location()); + EXPECT_EQ(gfx::Point(50, 50), converted10->root_location()); + EXPECT_EQ(original10.flags(), converted10->flags()); + + // Start tracking from the RootWindow1 and converts the mouse event into + // |window1|'s coodinates. + ui::MouseEvent original11(ui::ET_MOUSE_DRAGGED, + gfx::Point(150, 150), + gfx::Point(150, 150), + ui::EF_NONE); + scoped_ptr<ui::MouseEvent> converted11(ConvertMouseEvent(root_windows[1], + window1.get(), + original11)); + EXPECT_EQ(original11.type(), converted11->type()); + EXPECT_EQ(gfx::Point(50, 50), converted11->location()); + EXPECT_EQ(gfx::Point(150, 150), converted11->root_location()); + EXPECT_EQ(original11.flags(), converted11->flags()); +} + +} // namespace test +} // namespace aura diff --git a/ash/drag_drop/drag_image_view.cc b/ash/drag_drop/drag_image_view.cc index 7a138f3..b6a32f8 100644 --- a/ash/drag_drop/drag_image_view.cc +++ b/ash/drag_drop/drag_image_view.cc @@ -5,6 +5,7 @@ #include "ash/drag_drop/drag_image_view.h" #include "ash/wm/shadow_types.h" +#include "ui/aura/window.h" #include "ui/views/widget/widget.h" namespace ash { @@ -23,6 +24,7 @@ Widget* CreateDragWidget() { params.transparent = true; drag_widget->Init(params); drag_widget->SetOpacity(0xFF); + drag_widget->GetNativeWindow()->set_owned_by_parent(false); SetShadowType(drag_widget->GetNativeView(), SHADOW_TYPE_NONE); return drag_widget; } |