| 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
|
|
|