Index: services/ui/ws/current_drag_operation.cc |
diff --git a/services/ui/ws/current_drag_operation.cc b/services/ui/ws/current_drag_operation.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..90daa8a48bb3da431add5e4e70786934245a17e7 |
--- /dev/null |
+++ b/services/ui/ws/current_drag_operation.cc |
@@ -0,0 +1,215 @@ |
+// 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/current_drag_operation.h" |
+ |
+#include "base/logging.h" |
+#include "services/ui/ws/current_drag_operation_delegate.h" |
+#include "services/ui/ws/event_dispatcher.h" |
+#include "services/ui/ws/server_window.h" |
+#include "services/ui/ws/window_server.h" |
+#include "services/ui/ws/window_tree.h" |
+ |
+namespace ui { |
+namespace ws { |
+ |
+enum DragEventType { TYPE_ENTER, TYPE_OVER, TYPE_LEAVE, TYPE_DROP }; |
+ |
+struct CurrentDragOperation::Operation { |
+ DragEventType type; |
+ uint32_t key_state; |
+ gfx::Point position; |
+}; |
+ |
+// TODO(erg): We need to know when WindowTree gets destroyed, or otherwise put |
+// in another layer to communicate back that. This is threaded from the a |
+// WindowTree to a WMS to the EventDispatcher to here. |
+ |
+CurrentDragOperation::CurrentDragOperation( |
+ CurrentDragOperationDelegate* delegate, |
+ uint32_t change_id, |
+ WindowTree* source_window_tree, |
+ ServerWindow* source_window, |
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data, |
+ uint32_t drag_operations) |
+ : delegate_(delegate), |
+ change_id_(change_id), |
+ drag_operations_(drag_operations), |
+ source_window_tree_(source_window_tree), |
+ window_server_(source_window_tree->window_server()), |
+ source_window_(source_window), |
+ mime_data_(std::move(mime_data)), |
+ weak_factory_(this) { |
+ source_window_->AddObserver(this); |
+} |
+ |
+CurrentDragOperation::~CurrentDragOperation() { |
+ source_window_->RemoveObserver(this); |
+} |
+ |
+void CurrentDragOperation::DispatchLocatedEvent( |
+ const ui::LocatedEvent& event, |
+ ServerWindow* current_target, |
+ const ClientSpecificId& client_id) { |
+ uint32_t key_state = event.flags(); |
+ gfx::Point 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 mouse events. |
+ return; |
+ } |
+ |
+ if (current_target && !current_target->can_accept_drags()) { |
+ // If the current window over point doesn't accept drops, than don't send |
+ // it events. |
+ current_target = nullptr; |
+ } |
+ |
+ if (event.type() == ET_POINTER_UP) { |
+ if (current_target) { |
+ auto& window_ops = current_window_state_[current_target]; |
+ window_ops.push_back({TYPE_DROP, key_state, position}); |
+ if (window_ops.size() == 1) |
+ DispatchFrontOfWindowQueue(current_target, &window_ops); |
+ } else { |
+ // The pointer was released over no window or a window that doesn't |
+ // accept drags. |
+ MessageDragCompleted(false); |
+ } |
+ |
+ return; |
+ } |
+ |
+ if (current_target && current_target == current_target_window_) { |
+ // We're continuing a drag inside the bounds of |
+ // |current_target_window_|. Send a continue message. |
+ |
+ auto& window_ops = current_window_state_[current_target]; |
+ if (window_ops.empty()) { |
+ // We have no window operations so send it immediately. |
+ window_ops.push_back({TYPE_OVER, key_state, position}); |
+ DispatchFrontOfWindowQueue(current_target, &window_ops); |
+ } else if (window_ops.size() > 1 && window_ops.back().type == TYPE_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.position = position; |
+ } else { |
+ // In all other cases, queue. |
+ window_ops.push_back({TYPE_OVER, key_state, position}); |
+ } |
+ } else if (current_target != current_target_window_) { |
+ if (current_target_window_) { |
+ // TODO(erg): There's probably more clever eliding that we can do by |
+ // pulling off DragOver events? |
+ auto& window_ops = current_window_state_[current_target_window_]; |
+ window_ops.push_back({TYPE_LEAVE, key_state, position}); |
+ if (window_ops.size() == 1) |
+ DispatchFrontOfWindowQueue(current_target_window_, &window_ops); |
+ } |
+ |
+ if (current_target) { |
+ auto& window_ops = current_window_state_[current_target]; |
+ window_ops.push_back({TYPE_ENTER, key_state, position}); |
+ if (window_ops.size() == 1) |
+ DispatchFrontOfWindowQueue(current_target, &window_ops); |
+ } |
+ |
+ current_target_window_ = current_target; |
+ } |
+} |
+ |
+void CurrentDragOperation::MessageDragCompleted(bool success) { |
+ // Message our original caller that this drag is completed. |
+ source_window_tree_->OnChangeCompleted(change_id_, success); |
+ |
+ // Deletes the CurrentDragOperation. |
+ delegate_->OnDragOver(false); |
+} |
+ |
+void CurrentDragOperation::DispatchFrontOfWindowQueue( |
+ ServerWindow* target, |
+ std::deque<Operation>* queue) { |
+ WindowTree* tree = window_server_->GetTreeWithRoot(target); |
+ |
+ DCHECK(!queue->empty()); |
+ const Operation& op = queue->front(); |
+ switch (op.type) { |
+ case TYPE_ENTER: { |
+ tree->PerformOnDragEnter( |
+ target, mime_data_.Clone(), op.key_state, op.position, |
+ drag_operations_, |
+ base::Bind(&CurrentDragOperation::OnDragStatusCompleted, |
+ weak_factory_.GetWeakPtr(), target->id())); |
+ break; |
+ } |
+ case TYPE_OVER: { |
+ tree->PerformOnDragOver( |
+ target, op.key_state, op.position, drag_operations_, |
+ base::Bind(&CurrentDragOperation::OnDragStatusCompleted, |
+ weak_factory_.GetWeakPtr(), target->id())); |
+ break; |
+ } |
+ case TYPE_LEAVE: { |
+ tree->PerformOnDragLeave(target); |
+ queue->pop_front(); |
+ if (!queue->empty()) |
+ DispatchFrontOfWindowQueue(target, queue); |
+ break; |
+ } |
+ case TYPE_DROP: { |
+ tree->PerformOnDragDrop( |
+ target, op.key_state, op.position, drag_operations_, |
+ base::Bind(&CurrentDragOperation::OnDragDropCompleted, |
+ weak_factory_.GetWeakPtr(), target->id())); |
+ break; |
+ } |
+ } |
+} |
+ |
+void CurrentDragOperation::OnWindowDestroying(ServerWindow* window) { |
+ current_window_state_.erase(window); |
+ |
+ if (current_target_window_ == window) |
+ current_target_window_ = nullptr; |
+} |
+ |
+void CurrentDragOperation::OnDragStatusCompleted(const WindowId& id, |
+ uint32_t bitmask) { |
+ ServerWindow* window = window_server_->GetWindow(id); |
+ DCHECK(window); |
+ |
+ // We must remove the completed item. |
+ auto& window_ops = current_window_state_[window]; |
+ DCHECK(!window_ops.empty()); |
+ window_ops.pop_front(); |
+ |
+ if (!window_ops.empty()) { |
+ // There are queued events here. We should dispatch them. |
+ DispatchFrontOfWindowQueue(window, &window_ops); |
+ } |
+ |
+ // TODO(erg): |bitmask| is the allowed drag actions at the mouse location. We |
+ // should use this data to change the cursor. |
+} |
+ |
+void CurrentDragOperation::OnDragDropCompleted(const WindowId& id, |
+ uint32_t bitmask) { |
+ ServerWindow* window = window_server_->GetWindow(id); |
+ DCHECK(window); |
+ |
+ // We must remove the completed item. |
+ auto& window_ops = current_window_state_[window]; |
+ DCHECK(!window_ops.empty()); |
+ DCHECK_EQ(TYPE_DROP, window_ops.front().type); |
+ window_ops.pop_front(); |
+ DCHECK(window_ops.empty()); |
+ |
+ MessageDragCompleted(bitmask != 0u); |
+} |
+ |
+} // namespace ws |
+} // namespace ui |