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

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: Clear the implicit pointer drags before start. 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..d37a66c6a1ba4d83c9d59b7b779f8f234e6a3ed3
--- /dev/null
+++ b/services/ui/ws/drag_controller.cc
@@ -0,0 +1,253 @@
+// 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 {
+ EventType type;
+ uint32_t key_state;
+ gfx::Point screen_position;
+};
+
+DragController::DragController(
+ DragSource* source,
+ ServerWindow* window,
+ 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_(window),
+ mime_data_(std::move(mime_data)),
+ weak_factory_(this) {
+ source_window_->AddObserver(this);
+}
+
+DragController::~DragController() {
+ if (source_window_)
+ source_window_->RemoveObserver(this);
+}
+
+void DragController::Cancel() {
+ MessageDragCompleted(false);
+ // |this| may be invalid now.
sky 2016/09/08 23:37:20 invalid->deleted?
+}
+
+bool DragController::DispatchPointerEvent(const ui::PointerEvent& event,
+ ServerWindow* current_target) {
+ uint32_t key_state = event.flags();
sky 2016/09/08 23:37:20 Naming this key_state, when it's event_flags is mi
+ 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 == EventType::OVER) {
+ // If we have a queued DragOver which isn't in progress, then just
+ // modify it's data.
+ auto& back = window_ops.back();
+ back.key_state = key_state;
+ back.screen_position = screen_position;
+ } else {
+ QueueEvent(current_target, EventType::OVER, key_state, 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 == EventType::OVER) {
+ // If we have queued DragOver events, we want to replace them.
+ auto& back = window_ops.back();
+ back.type = EventType::LEAVE;
+ back.key_state = key_state;
+ back.screen_position = screen_position;
+ } else {
+ QueueEvent(current_target_window_, EventType::LEAVE, key_state,
+ screen_position);
+ }
+ }
+
+ if (current_target)
+ QueueEvent(current_target, EventType::ENTER, key_state, screen_position);
+
+ current_target_window_ = current_target;
+ }
+
+ if (event.type() == ET_POINTER_UP) {
+ if (current_target) {
+ QueueEvent(current_target, EventType::DROP, key_state, 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::MessageDragCompleted(bool success) {
+ for (ServerWindow* window : called_on_drag_start_) {
+ DragTargetConnection* connection = source_->GetDragTargetWithRoot(window);
+ if (connection)
+ connection->PerformOnDragFinish(window);
+ }
+ called_on_drag_start_.clear();
+
+ source_->OnDragCompleted(success);
+ // |this| may be invalid 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::QueueEvent(ServerWindow* window,
+ EventType type,
+ uint32_t key_state,
+ gfx::Point screen_position) {
+ // If this window doesn't have the mime data, send it a PerformOnDragStart()
+ // message.
+ if (window != source_window_ &&
+ !base::ContainsKey(called_on_drag_start_, window)) {
+ DragTargetConnection* connection = source_->GetDragTargetWithRoot(window);
+ connection->PerformOnDragStart(window, mime_data_.Clone());
+ called_on_drag_start_.insert(window);
+ }
+
+ auto& window_ops = window_operations_[window];
+ if (window_ops.empty() && window != source_window_)
+ window->AddObserver(this);
+ window_ops.push_back({type, key_state, 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_) {
+ // This is the last event in queue; stop watching the queue.
+ 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 EventType::ENTER: {
+ connection->PerformOnDragEnter(
+ target, op.key_state, op.screen_position, drag_operations_,
+ base::Bind(&DragController::OnDragStatusCompleted,
+ weak_factory_.GetWeakPtr(), target->id()));
+ break;
+ }
+ case EventType::OVER: {
+ connection->PerformOnDragOver(
+ target, op.key_state, op.screen_position, drag_operations_,
+ base::Bind(&DragController::OnDragStatusCompleted,
+ weak_factory_.GetWeakPtr(), target->id()));
+ break;
+ }
+ case EventType::LEAVE: {
+ connection->PerformOnDragLeave(target);
+ RemoveQueueFront(target);
+ break;
+ }
+ case EventType::DROP: {
+ connection->PerformOnDragDrop(
+ target, op.key_state, 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)
sky 2016/09/08 23:37:20 You never add an observer for current_target_windo
+ current_target_window_ = nullptr;
+
+ window_operations_.erase(window);
+ called_on_drag_start_.erase(window);
+
+ if (source_window_ == window) {
+ source_window_ = nullptr;
sky 2016/09/08 23:37:20 RemoveObserver?
Elliot Glaysher 2016/09/13 00:14:19 Did this and also made sure we were unsubscribing
+ // Our source window is being deleted, fail the drag.
+ MessageDragCompleted(false);
+ }
+}
+
+} // namespace ws
+} // namespace ui

Powered by Google App Engine
This is Rietveld 408576698