OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "services/ui/ws/current_drag_operation.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "services/ui/ws/current_drag_operation_delegate.h" |
| 9 #include "services/ui/ws/event_dispatcher.h" |
| 10 #include "services/ui/ws/server_window.h" |
| 11 #include "services/ui/ws/window_server.h" |
| 12 #include "services/ui/ws/window_tree.h" |
| 13 |
| 14 namespace ui { |
| 15 namespace ws { |
| 16 |
| 17 enum DragEventType { TYPE_ENTER, TYPE_OVER, TYPE_LEAVE, TYPE_DROP }; |
| 18 |
| 19 struct CurrentDragOperation::Operation { |
| 20 DragEventType type; |
| 21 uint32_t key_state; |
| 22 gfx::Point position; |
| 23 }; |
| 24 |
| 25 // TODO(erg): We need to know when WindowTree gets destroyed, or otherwise put |
| 26 // in another layer to communicate back that. This is threaded from the a |
| 27 // WindowTree to a WMS to the EventDispatcher to here. |
| 28 |
| 29 CurrentDragOperation::CurrentDragOperation( |
| 30 CurrentDragOperationDelegate* delegate, |
| 31 uint32_t change_id, |
| 32 WindowTree* source_window_tree, |
| 33 ServerWindow* source_window, |
| 34 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data, |
| 35 uint32_t drag_operations) |
| 36 : delegate_(delegate), |
| 37 change_id_(change_id), |
| 38 drag_operations_(drag_operations), |
| 39 source_window_tree_(source_window_tree), |
| 40 window_server_(source_window_tree->window_server()), |
| 41 source_window_(source_window), |
| 42 mime_data_(std::move(mime_data)), |
| 43 weak_factory_(this) { |
| 44 source_window_->AddObserver(this); |
| 45 } |
| 46 |
| 47 CurrentDragOperation::~CurrentDragOperation() { |
| 48 source_window_->RemoveObserver(this); |
| 49 } |
| 50 |
| 51 void CurrentDragOperation::DispatchLocatedEvent( |
| 52 const ui::LocatedEvent& event, |
| 53 ServerWindow* current_target, |
| 54 const ClientSpecificId& client_id) { |
| 55 uint32_t key_state = event.flags(); |
| 56 gfx::Point position = event.location(); |
| 57 |
| 58 if (waiting_for_final_drop_response_) { |
| 59 // If we're waiting on a target window to respond to the final drag drop |
| 60 // call, don't process any more mouse events. |
| 61 return; |
| 62 } |
| 63 |
| 64 if (current_target && !current_target->can_accept_drags()) { |
| 65 // If the current window over point doesn't accept drops, than don't send |
| 66 // it events. |
| 67 current_target = nullptr; |
| 68 } |
| 69 |
| 70 if (event.type() == ET_POINTER_UP) { |
| 71 if (current_target) { |
| 72 auto& window_ops = current_window_state_[current_target]; |
| 73 window_ops.push_back({TYPE_DROP, key_state, position}); |
| 74 if (window_ops.size() == 1) |
| 75 DispatchFrontOfWindowQueue(current_target, &window_ops); |
| 76 } else { |
| 77 // The pointer was released over no window or a window that doesn't |
| 78 // accept drags. |
| 79 MessageDragCompleted(false); |
| 80 } |
| 81 |
| 82 return; |
| 83 } |
| 84 |
| 85 if (current_target && current_target == current_target_window_) { |
| 86 // We're continuing a drag inside the bounds of |
| 87 // |current_target_window_|. Send a continue message. |
| 88 |
| 89 auto& window_ops = current_window_state_[current_target]; |
| 90 if (window_ops.empty()) { |
| 91 // We have no window operations so send it immediately. |
| 92 window_ops.push_back({TYPE_OVER, key_state, position}); |
| 93 DispatchFrontOfWindowQueue(current_target, &window_ops); |
| 94 } else if (window_ops.size() > 1 && window_ops.back().type == TYPE_OVER) { |
| 95 // If we have a queued DragOver which isn't in progress, then just |
| 96 // modify it's data. |
| 97 auto& back = window_ops.back(); |
| 98 back.key_state = key_state; |
| 99 back.position = position; |
| 100 } else { |
| 101 // In all other cases, queue. |
| 102 window_ops.push_back({TYPE_OVER, key_state, position}); |
| 103 } |
| 104 } else if (current_target != current_target_window_) { |
| 105 if (current_target_window_) { |
| 106 // TODO(erg): There's probably more clever eliding that we can do by |
| 107 // pulling off DragOver events? |
| 108 auto& window_ops = current_window_state_[current_target_window_]; |
| 109 window_ops.push_back({TYPE_LEAVE, key_state, position}); |
| 110 if (window_ops.size() == 1) |
| 111 DispatchFrontOfWindowQueue(current_target_window_, &window_ops); |
| 112 } |
| 113 |
| 114 if (current_target) { |
| 115 auto& window_ops = current_window_state_[current_target]; |
| 116 window_ops.push_back({TYPE_ENTER, key_state, position}); |
| 117 if (window_ops.size() == 1) |
| 118 DispatchFrontOfWindowQueue(current_target, &window_ops); |
| 119 } |
| 120 |
| 121 current_target_window_ = current_target; |
| 122 } |
| 123 } |
| 124 |
| 125 void CurrentDragOperation::MessageDragCompleted(bool success) { |
| 126 // Message our original caller that this drag is completed. |
| 127 source_window_tree_->OnChangeCompleted(change_id_, success); |
| 128 |
| 129 // Deletes the CurrentDragOperation. |
| 130 delegate_->OnDragOver(false); |
| 131 } |
| 132 |
| 133 void CurrentDragOperation::DispatchFrontOfWindowQueue( |
| 134 ServerWindow* target, |
| 135 std::deque<Operation>* queue) { |
| 136 WindowTree* tree = window_server_->GetTreeWithRoot(target); |
| 137 |
| 138 DCHECK(!queue->empty()); |
| 139 const Operation& op = queue->front(); |
| 140 switch (op.type) { |
| 141 case TYPE_ENTER: { |
| 142 tree->PerformOnDragEnter( |
| 143 target, mime_data_.Clone(), op.key_state, op.position, |
| 144 drag_operations_, |
| 145 base::Bind(&CurrentDragOperation::OnDragStatusCompleted, |
| 146 weak_factory_.GetWeakPtr(), target->id())); |
| 147 break; |
| 148 } |
| 149 case TYPE_OVER: { |
| 150 tree->PerformOnDragOver( |
| 151 target, op.key_state, op.position, drag_operations_, |
| 152 base::Bind(&CurrentDragOperation::OnDragStatusCompleted, |
| 153 weak_factory_.GetWeakPtr(), target->id())); |
| 154 break; |
| 155 } |
| 156 case TYPE_LEAVE: { |
| 157 tree->PerformOnDragLeave(target); |
| 158 queue->pop_front(); |
| 159 if (!queue->empty()) |
| 160 DispatchFrontOfWindowQueue(target, queue); |
| 161 break; |
| 162 } |
| 163 case TYPE_DROP: { |
| 164 tree->PerformOnDragDrop( |
| 165 target, op.key_state, op.position, drag_operations_, |
| 166 base::Bind(&CurrentDragOperation::OnDragDropCompleted, |
| 167 weak_factory_.GetWeakPtr(), target->id())); |
| 168 break; |
| 169 } |
| 170 } |
| 171 } |
| 172 |
| 173 void CurrentDragOperation::OnWindowDestroying(ServerWindow* window) { |
| 174 current_window_state_.erase(window); |
| 175 |
| 176 if (current_target_window_ == window) |
| 177 current_target_window_ = nullptr; |
| 178 } |
| 179 |
| 180 void CurrentDragOperation::OnDragStatusCompleted(const WindowId& id, |
| 181 uint32_t bitmask) { |
| 182 ServerWindow* window = window_server_->GetWindow(id); |
| 183 DCHECK(window); |
| 184 |
| 185 // We must remove the completed item. |
| 186 auto& window_ops = current_window_state_[window]; |
| 187 DCHECK(!window_ops.empty()); |
| 188 window_ops.pop_front(); |
| 189 |
| 190 if (!window_ops.empty()) { |
| 191 // There are queued events here. We should dispatch them. |
| 192 DispatchFrontOfWindowQueue(window, &window_ops); |
| 193 } |
| 194 |
| 195 // TODO(erg): |bitmask| is the allowed drag actions at the mouse location. We |
| 196 // should use this data to change the cursor. |
| 197 } |
| 198 |
| 199 void CurrentDragOperation::OnDragDropCompleted(const WindowId& id, |
| 200 uint32_t bitmask) { |
| 201 ServerWindow* window = window_server_->GetWindow(id); |
| 202 DCHECK(window); |
| 203 |
| 204 // We must remove the completed item. |
| 205 auto& window_ops = current_window_state_[window]; |
| 206 DCHECK(!window_ops.empty()); |
| 207 DCHECK_EQ(TYPE_DROP, window_ops.front().type); |
| 208 window_ops.pop_front(); |
| 209 DCHECK(window_ops.empty()); |
| 210 |
| 211 MessageDragCompleted(bitmask != 0u); |
| 212 } |
| 213 |
| 214 } // namespace ws |
| 215 } // namespace ui |
OLD | NEW |