Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(332)

Unified Diff: services/ui/ws/drag_controller.cc

Issue 2266603002: mus: Implement interwindow drag and drop (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: sky comments Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: services/ui/ws/drag_controller.cc
diff --git a/services/ui/ws/drag_controller.cc b/services/ui/ws/drag_controller.cc
new file mode 100644
index 0000000000000000000000000000000000000000..cbd650cc598866bdc264fd291b7f4df8b7f8f23a
--- /dev/null
+++ b/services/ui/ws/drag_controller.cc
@@ -0,0 +1,290 @@
+// Copyright 2016 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 "services/ui/ws/drag_controller.h"
+
+#include "base/logging.h"
+#include "services/ui/ws/drag_source.h"
+#include "services/ui/ws/drag_target_connection.h"
+#include "services/ui/ws/event_dispatcher.h"
+#include "services/ui/ws/server_window.h"
+
+namespace ui {
+namespace ws {
+
+struct DragController::Operation {
+ OperationType type;
+ uint32_t event_flags;
+ gfx::Point screen_position;
+};
+
+DragController::DragController(
+ DragSource* source,
+ ServerWindow* source_window,
+ DragTargetConnection* source_connection,
+ int32_t drag_pointer,
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data,
+ uint32_t drag_operations)
+ : source_(source),
+ drag_operations_(drag_operations),
+ drag_pointer_id_(drag_pointer),
+ source_window_(source_window),
+ source_connection_(source_connection),
+ mime_data_(std::move(mime_data)),
+ weak_factory_(this) {
+ source_window_->AddObserver(this);
+}
+
+DragController::~DragController() {
+ // Only remove observer one time.
+ std::set<ServerWindow*> observed_windows;
+ if (source_window_)
+ observed_windows.insert(source_window_);
+ if (current_target_window_)
+ observed_windows.insert(current_target_window_);
+ for (auto& w : window_operations_)
sky 2016/09/13 18:15:31 optional: 'w' is mildly misleading as I would tend
+ observed_windows.insert(w.first);
+
+ for (ServerWindow* w : observed_windows)
+ w->RemoveObserver(this);
+}
+
+void DragController::Cancel() {
+ MessageDragCompleted(false);
+ // |this| may be deleted now.
+}
+
+bool DragController::DispatchPointerEvent(const ui::PointerEvent& event,
+ ServerWindow* current_target) {
+ uint32_t event_flags =
+ event.flags() &
+ (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN);
+ gfx::Point screen_position = event.location();
+
+ if (waiting_for_final_drop_response_) {
+ // If we're waiting on a target window to respond to the final drag drop
+ // call, don't process any more pointer events.
+ return false;
+ }
+
+ if (event.pointer_id() != drag_pointer_id_)
+ return false;
+
+ // If |current_target| doesn't accept drags, walk its hierarchy up until we
+ // find one that does (or set to nullptr at the top of the tree).
+ while (current_target && !current_target->can_accept_drops())
+ current_target = current_target->parent();
+
+ if (current_target && current_target == current_target_window_ &&
+ event.type() != ET_POINTER_UP) {
+ // We're continuing a drag inside the bounds of
+ // |current_target_window_|. Send a continue message.
+ auto& window_ops = window_operations_[current_target];
+ if (window_ops.size() > 1 &&
+ window_ops.back().type == OperationType::OVER) {
+ // If we have a queued DragOver which isn't in progress, then just
+ // modify it's data.
+ auto& back = window_ops.back();
+ back.event_flags = event_flags;
+ back.screen_position = screen_position;
+ } else {
+ QueueOperation(current_target, OperationType::OVER, event_flags,
+ screen_position);
+ }
+ } else if (current_target != current_target_window_) {
+ if (current_target_window_) {
+ auto& window_ops = window_operations_[current_target_window_];
+ if (window_ops.size() > 1 &&
+ window_ops.back().type == OperationType::OVER) {
+ // If we have queued DragOver events, we want to replace them.
+ auto& back = window_ops.back();
+ back.type = OperationType::LEAVE;
+ back.event_flags = event_flags;
+ back.screen_position = screen_position;
+ } else {
+ QueueOperation(current_target_window_, OperationType::LEAVE,
+ event_flags, screen_position);
+ }
+ }
+
+ if (current_target)
+ QueueOperation(current_target, OperationType::ENTER, event_flags,
+ screen_position);
+
+ SetCurrentTargetWindow(current_target);
+ }
+
+ if (event.type() == ET_POINTER_UP) {
+ if (current_target) {
+ QueueOperation(current_target, OperationType::DROP, event_flags,
+ screen_position);
+ waiting_for_final_drop_response_ = true;
+ } else {
+ // The pointer was released over no window or a window that doesn't
+ // accept drags.
+ MessageDragCompleted(false);
+ }
+ }
+
+ return true;
+}
+
+void DragController::OnWillDestroyDragTargetConnection(
+ DragTargetConnection* connection) {
+ called_on_drag_mime_types_.erase(connection);
+}
+
+void DragController::MessageDragCompleted(bool success) {
+ for (DragTargetConnection* connection : called_on_drag_mime_types_)
+ connection->PerformOnDragFinish();
+ called_on_drag_mime_types_.clear();
+
+ source_->OnDragCompleted(success);
+ // |this| may be deleted now.
+}
+
+size_t DragController::GetSizeOfQueueForWindow(ServerWindow* window) {
+ auto it = window_operations_.find(window);
+ if (it == window_operations_.end())
+ return 0u;
+ return it->second.size();
+}
+
+void DragController::SetCurrentTargetWindow(ServerWindow* current_target) {
+ // Only add or remove observers when the window isn't in |window_operations_|.
+ if (current_target_window_ && current_target_window_ != source_window_ &&
+ !base::ContainsKey(window_operations_, current_target_window_)) {
+ current_target_window_->RemoveObserver(this);
+ }
+
+ current_target_window_ = current_target;
+
+ if (current_target_window_ && current_target_window_ != source_window_ &&
+ !base::ContainsKey(window_operations_, current_target_window_)) {
+ current_target_window_->AddObserver(this);
+ }
+}
+
+void DragController::QueueOperation(ServerWindow* window,
+ OperationType type,
+ uint32_t event_flags,
+ gfx::Point screen_position) {
+ // If this window doesn't have the mime data, send it.
+ DragTargetConnection* connection = source_->GetDragTargetWithRoot(window);
+ if (connection != source_connection_ &&
+ !base::ContainsKey(called_on_drag_mime_types_, connection)) {
+ connection->PerformOnDragMimeTypes(mime_data_.Clone());
+ called_on_drag_mime_types_.insert(connection);
+ }
+
+ auto& window_ops = window_operations_[window];
+ if (window_ops.empty() && window != source_window_ &&
+ window != current_target_window_) {
+ window->AddObserver(this);
+ }
+ window_ops.push_back({type, event_flags, screen_position});
+ if (window_ops.size() == 1) {
+ // The first item in the queue is the item we're waiting for a response
+ // on. If we're the only item, send it.
+ DispatchFrontOfWindowQueue(window, &window_ops);
+ }
+}
+
+void DragController::RemoveQueueFront(ServerWindow* window) {
+ auto& window_ops = window_operations_[window];
+ if (window_ops.size() == 1 && window != source_window_ &&
+ window != current_target_window_) {
+ // This is the last event in queue; stop watching the queue.
sky 2016/09/13 18:15:31 If you do this, and the window is destroyed OnWind
Elliot Glaysher 2016/09/14 22:19:16 I'm confused. If we're here, we're removing the l
sky 2016/09/14 22:52:02 Isn't the reference still held in the map? e.g. wi
Elliot Glaysher 2016/09/15 17:12:40 This (and related) chunks have been removed. This
+ window->RemoveObserver(this);
+ }
+
+ DCHECK(!window_ops.empty());
+ window_ops.pop_front();
+
+ if (!window_ops.empty())
+ DispatchFrontOfWindowQueue(window, &window_ops);
+}
+
+void DragController::DispatchFrontOfWindowQueue(ServerWindow* target,
+ std::deque<Operation>* queue) {
+ DragTargetConnection* connection = source_->GetDragTargetWithRoot(target);
+
+ DCHECK(!queue->empty());
+ const Operation& op = queue->front();
+ switch (op.type) {
+ case OperationType::ENTER: {
+ connection->PerformOnDragEnter(
+ target, op.event_flags, op.screen_position, drag_operations_,
+ base::Bind(&DragController::OnDragStatusCompleted,
+ weak_factory_.GetWeakPtr(), target->id()));
+ break;
+ }
+ case OperationType::OVER: {
+ connection->PerformOnDragOver(
+ target, op.event_flags, op.screen_position, drag_operations_,
+ base::Bind(&DragController::OnDragStatusCompleted,
+ weak_factory_.GetWeakPtr(), target->id()));
+ break;
+ }
+ case OperationType::LEAVE: {
+ connection->PerformOnDragLeave(target);
+ RemoveQueueFront(target);
+ break;
+ }
+ case OperationType::DROP: {
+ connection->PerformOnCompleteDrop(
+ target, op.event_flags, op.screen_position, drag_operations_,
+ base::Bind(&DragController::OnDragDropCompleted,
+ weak_factory_.GetWeakPtr(), target->id()));
+ break;
+ }
+ }
+}
+
+void DragController::OnDragStatusCompleted(const WindowId& id,
+ uint32_t bitmask) {
+ ServerWindow* window = source_->GetWindowById(id);
+ if (!window) {
+ // The window has been deleted and its queue is empty.
+ return;
+ }
+
+ // We must remove the completed item.
+ RemoveQueueFront(window);
+
+ // TODO(erg): |bitmask| is the allowed drag actions at the mouse location. We
+ // should use this data to change the cursor.
+}
+
+void DragController::OnDragDropCompleted(const WindowId& id, uint32_t bitmask) {
+ ServerWindow* window = source_->GetWindowById(id);
+ if (!window) {
+ // The window has been deleted after we sent the drop message. It's really
+ // hard to recover from this so just signal to the source that our drag
+ // failed.
+ MessageDragCompleted(false);
+ return;
+ }
+
+ // TODO(erg): What happens if there are events behind the drag drop!?
+ RemoveQueueFront(window);
+ MessageDragCompleted(bitmask != 0u);
+}
+
+void DragController::OnWindowDestroying(ServerWindow* window) {
+ if (current_target_window_ == window)
+ SetCurrentTargetWindow(nullptr);
+
+ window_operations_.erase(window);
+
+ if (source_window_ == window) {
+ source_window_->RemoveObserver(this);
+ source_window_ = nullptr;
+ // Our source window is being deleted, fail the drag.
+ MessageDragCompleted(false);
+ }
+}
+
+} // namespace ws
+} // namespace ui

Powered by Google App Engine
This is Rietveld 408576698