Commit f258eb44 authored by Nick Diego Yamane's avatar Nick Diego Yamane Committed by Commit Bot

exo: extended-drag: Introduce ToplevelWindowDragDelegate

This adds initial support for toplevel window dragging during DND
sessions, which is required by Exo's extended-drag protocol impl. To do
so, this CL adds ash::ToplevelWindowDragDelegate, a brand new interface
which makes ash's DragDropController aware of toplevel window dragging
during DND sessions, so allowing it to forward events of interest to its
instance, if it's set for a particular DND operation.

Context: Wayland Protocol needs to be extended to make it possible to
properly support full Chromium's tab dragging experience. Further
details in the Design document [1].

The actual ToplevelWindowDragDelegate implementation will be added in a
upcoming CL [2].

Bug: 1099418
Test: ash_unittests --gtest_filter=DragDropControllerTest.ToplevelWIndowDragDelegate

[1] https://docs.google.com/document/d/1s6OwTi_WC-pS21WLGQYI39yw2m42ZlVolUXBclljXB4/edit?usp=sharing
[2] https://chromium-review.googlesource.com/c/chromium/src/+/2401280

R=oshima@chromium.org

Change-Id: I8d76eb7bfe9086f3a89d0c1b8762ea39dbea69a6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2483663
Commit-Queue: Nick Yamane <nickdiego@igalia.com>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#821612}
parent 2f18fe8d
......@@ -412,6 +412,7 @@ component("ash") {
"drag_drop/drag_image_view.h",
"drag_drop/tab_drag_drop_delegate.cc",
"drag_drop/tab_drag_drop_delegate.h",
"drag_drop/toplevel_window_drag_delegate.h",
"events/accessibility_event_rewriter.cc",
"events/accessibility_event_rewriter.h",
"events/event_rewriter_controller_impl.cc",
......
......@@ -9,6 +9,7 @@
#include "ash/drag_drop/drag_drop_tracker.h"
#include "ash/drag_drop/drag_image_view.h"
#include "ash/drag_drop/toplevel_window_drag_delegate.h"
#include "ash/shell.h"
#include "base/bind.h"
#include "base/run_loop.h"
......@@ -200,6 +201,11 @@ int DragDropController::StartDragAndDrop(
for (aura::client::DragDropClientObserver& observer : observers_)
observer.OnDragStarted();
if (toplevel_window_drag_delegate_) {
toplevel_window_drag_delegate_->OnToplevelWindowDragStarted(
gfx::PointF(start_location_), source);
}
if (TabDragDropDelegate::IsChromeTabDrag(*drag_data_)) {
DCHECK(!tab_drag_drop_delegate_);
tab_drag_drop_delegate_.emplace(root_window, drag_source_window_,
......@@ -321,6 +327,10 @@ void DragDropController::OnMouseEvent(ui::MouseEvent* event) {
// (aura::RootWindow::PostMouseMoveEventAfterWindowChange).
break;
}
if (toplevel_window_drag_delegate_)
toplevel_window_drag_delegate_->OnToplevelWindowDragEvent(event);
event->StopPropagation();
}
......@@ -535,6 +545,11 @@ void DragDropController::Drop(aura::Window* target,
drag_image_widget_.reset();
}
if (toplevel_window_drag_delegate_) {
drag_operation_ =
toplevel_window_drag_delegate_->OnToplevelWindowDragDropped();
}
Cleanup();
if (should_block_during_drag_drop_)
std::move(quit_closure_).Run();
......@@ -576,6 +591,9 @@ void DragDropController::DoDragCancel(
if (delegate)
delegate->OnDragExited();
if (toplevel_window_drag_delegate_)
toplevel_window_drag_delegate_->OnToplevelWindowDragCancelled();
Cleanup();
drag_operation_ = 0;
StartCanceledAnimation(drag_cancel_animation_duration);
......
......@@ -37,6 +37,7 @@ class LocatedEvent;
namespace ash {
class DragDropTracker;
class DragDropTrackerDelegate;
class ToplevelWindowDragDelegate;
class ASH_EXPORT DragDropController : public aura::client::DragDropClient,
public ui::EventHandler,
......@@ -53,6 +54,10 @@ class ASH_EXPORT DragDropController : public aura::client::DragDropClient,
void set_enabled(bool enabled) { enabled_ = enabled; }
void set_toplevel_window_drag_delegate(ToplevelWindowDragDelegate* delegate) {
toplevel_window_drag_delegate_ = delegate;
}
// Overridden from aura::client::DragDropClient:
int StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data,
aura::Window* root_window,
......@@ -160,6 +165,8 @@ class ASH_EXPORT DragDropController : public aura::client::DragDropClient,
base::ObserverList<aura::client::DragDropClientObserver>::Unchecked
observers_;
ToplevelWindowDragDelegate* toplevel_window_drag_delegate_ = nullptr;
base::WeakPtrFactory<DragDropController> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(DragDropController);
......
......@@ -8,6 +8,7 @@
#include "ash/drag_drop/drag_drop_tracker.h"
#include "ash/drag_drop/drag_image_view.h"
#include "ash/drag_drop/toplevel_window_drag_delegate.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
......@@ -15,6 +16,7 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
......@@ -39,6 +41,9 @@
#include "ui/events/test/event_generator.h"
#include "ui/events/test/events_test_utils.h"
#include "ui/gfx/animation/linear_animation.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
......@@ -299,6 +304,66 @@ void DispatchGesture(ui::EventType gesture_type, gfx::Point location) {
CHECK(!details.dispatcher_destroyed);
}
class TestToplevelWindowDragDelegate : public ToplevelWindowDragDelegate {
public:
enum class State {
kNotInvoked,
kDragStartedInvoked,
kDragDroppedInvoked,
kDragCancelledInvoked,
kDragEventInvoked
};
TestToplevelWindowDragDelegate() = default;
TestToplevelWindowDragDelegate(const TestToplevelWindowDragDelegate&) =
delete;
TestToplevelWindowDragDelegate& operator=(
const TestToplevelWindowDragDelegate&) = delete;
~TestToplevelWindowDragDelegate() override = default;
State state() const { return state_; }
int events_forwarded() const { return events_forwarded_; }
ui::mojom::DragEventSource source() const { return source_; }
base::Optional<gfx::PointF> current_location() const {
return current_location_;
}
// ToplevelWindowDragDelegate:
void OnToplevelWindowDragStarted(const gfx::PointF& start_location,
ui::mojom::DragEventSource source) override {
EXPECT_EQ(State::kNotInvoked, state_);
state_ = State::kDragStartedInvoked;
current_location_.emplace(start_location);
source_ = source;
}
int OnToplevelWindowDragDropped() override {
EXPECT_EQ(State::kDragStartedInvoked, state_);
state_ = State::kDragDroppedInvoked;
return ui::DragDropTypes::DRAG_MOVE;
}
void OnToplevelWindowDragCancelled() override {
EXPECT_EQ(State::kDragStartedInvoked, state_);
state_ = State::kDragCancelledInvoked;
}
void OnToplevelWindowDragEvent(ui::LocatedEvent* event) override {
ASSERT_TRUE(event);
EXPECT_TRUE(current_location_.has_value());
current_location_.emplace(event->root_location_f());
events_forwarded_++;
}
private:
State state_ = State::kNotInvoked;
int events_forwarded_ = 0;
base::Optional<gfx::PointF> current_location_;
ui::mojom::DragEventSource source_;
};
} // namespace
class MockShellDelegate : public TestShellDelegate {
......@@ -1331,4 +1396,78 @@ TEST_F(DragDropControllerTest, DragTabChangesDragOperationToMove) {
EXPECT_EQ(operation, ui::DragDropTypes::DRAG_MOVE);
}
TEST_F(DragDropControllerTest, ToplevelWindowDragDelegate) {
std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), -1,
gfx::Rect(0, 0, 100, 100)));
// Emulate a full drag and drop flow and verify that toplevel window drag
// delegate gets notified about the events as expected.
{
TestToplevelWindowDragDelegate delegate;
drag_drop_controller_->set_toplevel_window_drag_delegate(&delegate);
ui::test::EventGenerator generator(window->GetRootWindow(), window.get());
generator.PressLeftButton();
auto data(std::make_unique<ui::OSExchangeData>());
drag_drop_controller_->StartDragAndDrop(
std::move(data), window->GetRootWindow(), window.get(),
gfx::Point(5, 5), ui::DragDropTypes::DRAG_MOVE,
ui::mojom::DragEventSource::kMouse);
EXPECT_EQ(TestToplevelWindowDragDelegate::State::kDragStartedInvoked,
delegate.state());
EXPECT_EQ(ui::mojom::DragEventSource::kMouse, delegate.source());
EXPECT_TRUE(delegate.current_location().has_value());
EXPECT_EQ(gfx::PointF(5, 5), *delegate.current_location());
EXPECT_EQ(0, delegate.events_forwarded());
generator.MoveMouseBy(1, 1);
generator.MoveMouseBy(1, 1);
generator.MoveMouseBy(1, 1);
generator.MoveMouseBy(1, 1);
generator.ReleaseLeftButton();
EXPECT_EQ(TestToplevelWindowDragDelegate::State::kDragDroppedInvoked,
delegate.state());
EXPECT_TRUE(delegate.current_location().has_value());
EXPECT_EQ(gfx::PointF(54, 54), *delegate.current_location());
EXPECT_EQ(5, delegate.events_forwarded());
}
// Emulate a drag session cancellation and verify the toplevel window drag
// delegate gets notified about the events as expected.
{
TestToplevelWindowDragDelegate delegate;
drag_drop_controller_->set_toplevel_window_drag_delegate(&delegate);
ui::test::EventGenerator generator(window->GetRootWindow(), window.get());
generator.PressLeftButton();
auto data(std::make_unique<ui::OSExchangeData>());
drag_drop_controller_->StartDragAndDrop(
std::move(data), window->GetRootWindow(), window.get(),
gfx::Point(5, 5), ui::DragDropTypes::DRAG_MOVE,
ui::mojom::DragEventSource::kMouse);
EXPECT_EQ(TestToplevelWindowDragDelegate::State::kDragStartedInvoked,
delegate.state());
EXPECT_EQ(ui::mojom::DragEventSource::kMouse, delegate.source());
EXPECT_TRUE(delegate.current_location().has_value());
EXPECT_EQ(gfx::PointF(5, 5), *delegate.current_location());
EXPECT_EQ(0, delegate.events_forwarded());
generator.MoveMouseBy(1, 1);
generator.MoveMouseBy(1, 1);
generator.PressKey(ui::VKEY_ESCAPE, 0);
EXPECT_EQ(TestToplevelWindowDragDelegate::State::kDragCancelledInvoked,
delegate.state());
EXPECT_TRUE(delegate.current_location().has_value());
EXPECT_EQ(gfx::PointF(52, 52), *delegate.current_location());
EXPECT_EQ(2, delegate.events_forwarded());
}
}
} // namespace ash
// Copyright 2020 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_TOPLEVEL_WINDOW_DRAG_DELEGATE_H_
#define ASH_DRAG_DROP_TOPLEVEL_WINDOW_DRAG_DELEGATE_H_
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
namespace gfx {
class PointF;
}
namespace ui {
class LocatedEvent;
}
namespace ash {
// Interface that makes it possible to implement toplevel window drag handling
// during Drag & Drop sessions.
class ToplevelWindowDragDelegate {
public:
virtual void OnToplevelWindowDragStarted(
const gfx::PointF& start_location,
ui::mojom::DragEventSource source) = 0;
virtual int OnToplevelWindowDragDropped() = 0;
virtual void OnToplevelWindowDragCancelled() = 0;
virtual void OnToplevelWindowDragEvent(ui::LocatedEvent* event) = 0;
protected:
virtual ~ToplevelWindowDragDelegate() = default;
};
} // namespace ash
#endif // ASH_DRAG_DROP_TOPLEVEL_WINDOW_DRAG_DELEGATE_H_
......@@ -17,6 +17,7 @@ namespace exo {
class DataSourceDelegate;
class DataSourceObserver;
class ExtendedDragSource;
enum class DndAction;
// Object representing transferred data offered by a client.
......@@ -81,6 +82,11 @@ class DataSource {
bool CanBeDataSourceForCopy(Surface* surface) const;
ExtendedDragSource* extended_drag_source() { return extended_drag_source_; }
void set_extended_drag_source(ExtendedDragSource* extended_drag_source) {
extended_drag_source_ = extended_drag_source;
}
private:
// Reads data from the source. Then |callback| is invoked with read data. If
// Cancelled() is invoked or DataSource is destroyed before completion,
......@@ -107,6 +113,8 @@ class DataSource {
base::flat_set<DndAction> dnd_actions_;
ExtendedDragSource* extended_drag_source_ = nullptr;
base::WeakPtrFactory<DataSource> read_data_weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(DataSource);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment