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/drag_controller.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "services/ui/ws/drag_source.h" |
| 9 #include "services/ui/ws/drag_target_connection.h" |
| 10 #include "services/ui/ws/event_dispatcher.h" |
| 11 #include "services/ui/ws/server_window.h" |
| 12 |
| 13 namespace ui { |
| 14 namespace ws { |
| 15 |
| 16 struct DragController::Operation { |
| 17 OperationType type; |
| 18 uint32_t event_flags; |
| 19 gfx::Point screen_position; |
| 20 }; |
| 21 |
| 22 struct DragController::WindowState { |
| 23 // Set to true once we've observed the ServerWindow* that is the key to this |
| 24 // instance in |window_state_|. |
| 25 bool observed; |
| 26 |
| 27 // If we're waiting for a response, this is the type of message. TYPE_NONE |
| 28 // means there's no outstanding |
| 29 OperationType waiting_on_reply; |
| 30 |
| 31 // The operation that we'll send off if |waiting_on_reply| isn't TYPE_NONE. |
| 32 Operation queued_operation; |
| 33 }; |
| 34 |
| 35 DragController::DragController( |
| 36 DragSource* source, |
| 37 ServerWindow* source_window, |
| 38 DragTargetConnection* source_connection, |
| 39 int32_t drag_pointer, |
| 40 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data, |
| 41 uint32_t drag_operations) |
| 42 : source_(source), |
| 43 drag_operations_(drag_operations), |
| 44 drag_pointer_id_(drag_pointer), |
| 45 source_window_(source_window), |
| 46 source_connection_(source_connection), |
| 47 mime_data_(std::move(mime_data)), |
| 48 weak_factory_(this) { |
| 49 EnsureWindowObserved(source_window_); |
| 50 } |
| 51 |
| 52 DragController::~DragController() { |
| 53 for (auto& pair : window_state_) { |
| 54 if (pair.second.observed) |
| 55 pair.first->RemoveObserver(this); |
| 56 } |
| 57 } |
| 58 |
| 59 void DragController::Cancel() { |
| 60 MessageDragCompleted(false); |
| 61 // |this| may be deleted now. |
| 62 } |
| 63 |
| 64 bool DragController::DispatchPointerEvent(const ui::PointerEvent& event, |
| 65 ServerWindow* current_target) { |
| 66 uint32_t event_flags = |
| 67 event.flags() & |
| 68 (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN); |
| 69 gfx::Point screen_position = event.location(); |
| 70 |
| 71 if (waiting_for_final_drop_response_) { |
| 72 // If we're waiting on a target window to respond to the final drag drop |
| 73 // call, don't process any more pointer events. |
| 74 return false; |
| 75 } |
| 76 |
| 77 if (event.pointer_id() != drag_pointer_id_) |
| 78 return false; |
| 79 |
| 80 // If |current_target| doesn't accept drags, walk its hierarchy up until we |
| 81 // find one that does (or set to nullptr at the top of the tree). |
| 82 while (current_target && !current_target->can_accept_drops()) |
| 83 current_target = current_target->parent(); |
| 84 |
| 85 if (current_target) { |
| 86 // If we're non-null, we're about to use |current_target| in some |
| 87 // way. Ensure that we receive notifications that this window has gone |
| 88 // away. |
| 89 EnsureWindowObserved(current_target); |
| 90 } |
| 91 |
| 92 if (current_target && current_target == current_target_window_ && |
| 93 event.type() != ET_POINTER_UP) { |
| 94 QueueOperation(current_target, OperationType::OVER, event_flags, |
| 95 screen_position); |
| 96 } else if (current_target != current_target_window_) { |
| 97 if (current_target_window_) { |
| 98 QueueOperation(current_target_window_, OperationType::LEAVE, event_flags, |
| 99 screen_position); |
| 100 } |
| 101 |
| 102 if (current_target) { |
| 103 // TODO(erg): If we have a queued LEAVE operation, does this turn into a |
| 104 // noop? |
| 105 QueueOperation(current_target, OperationType::ENTER, event_flags, |
| 106 screen_position); |
| 107 } |
| 108 |
| 109 SetCurrentTargetWindow(current_target); |
| 110 } |
| 111 |
| 112 if (event.type() == ET_POINTER_UP) { |
| 113 if (current_target) { |
| 114 QueueOperation(current_target, OperationType::DROP, event_flags, |
| 115 screen_position); |
| 116 waiting_for_final_drop_response_ = true; |
| 117 } else { |
| 118 // The pointer was released over no window or a window that doesn't |
| 119 // accept drags. |
| 120 MessageDragCompleted(false); |
| 121 } |
| 122 } |
| 123 |
| 124 return true; |
| 125 } |
| 126 |
| 127 void DragController::OnWillDestroyDragTargetConnection( |
| 128 DragTargetConnection* connection) { |
| 129 called_on_drag_mime_types_.erase(connection); |
| 130 } |
| 131 |
| 132 void DragController::MessageDragCompleted(bool success) { |
| 133 for (DragTargetConnection* connection : called_on_drag_mime_types_) |
| 134 connection->PerformOnDragDropDone(); |
| 135 called_on_drag_mime_types_.clear(); |
| 136 |
| 137 source_->OnDragCompleted(success); |
| 138 // |this| may be deleted now. |
| 139 } |
| 140 |
| 141 size_t DragController::GetSizeOfQueueForWindow(ServerWindow* window) { |
| 142 auto it = window_state_.find(window); |
| 143 if (it == window_state_.end()) |
| 144 return 0u; |
| 145 if (it->second.waiting_on_reply == OperationType::NONE) |
| 146 return 0u; |
| 147 if (it->second.queued_operation.type == OperationType::NONE) |
| 148 return 1u; |
| 149 return 2u; |
| 150 } |
| 151 |
| 152 void DragController::SetCurrentTargetWindow(ServerWindow* current_target) { |
| 153 current_target_window_ = current_target; |
| 154 } |
| 155 |
| 156 void DragController::EnsureWindowObserved(ServerWindow* window) { |
| 157 if (!window) |
| 158 return; |
| 159 |
| 160 WindowState& state = window_state_[window]; |
| 161 if (!state.observed) { |
| 162 state.observed = true; |
| 163 window->AddObserver(this); |
| 164 } |
| 165 } |
| 166 |
| 167 void DragController::QueueOperation(ServerWindow* window, |
| 168 OperationType type, |
| 169 uint32_t event_flags, |
| 170 gfx::Point screen_position) { |
| 171 // If this window doesn't have the mime data, send it. |
| 172 DragTargetConnection* connection = source_->GetDragTargetForWindow(window); |
| 173 if (connection != source_connection_ && |
| 174 !base::ContainsKey(called_on_drag_mime_types_, connection)) { |
| 175 connection->PerformOnDragDropStart(mime_data_.Clone()); |
| 176 called_on_drag_mime_types_.insert(connection); |
| 177 } |
| 178 |
| 179 WindowState& state = window_state_[window]; |
| 180 // Set the queued operation to the incoming. |
| 181 state.queued_operation = {type, event_flags, screen_position}; |
| 182 |
| 183 if (state.waiting_on_reply == OperationType::NONE) { |
| 184 // Send the operation immediately. |
| 185 DispatchOperation(window, &state); |
| 186 } |
| 187 } |
| 188 |
| 189 void DragController::DispatchOperation(ServerWindow* target, |
| 190 WindowState* state) { |
| 191 DragTargetConnection* connection = source_->GetDragTargetForWindow(target); |
| 192 |
| 193 DCHECK_EQ(OperationType::NONE, state->waiting_on_reply); |
| 194 Operation& op = state->queued_operation; |
| 195 switch (op.type) { |
| 196 case OperationType::NONE: { |
| 197 // NONE case to silence the compiler. |
| 198 NOTREACHED(); |
| 199 break; |
| 200 } |
| 201 case OperationType::ENTER: { |
| 202 connection->PerformOnDragEnter( |
| 203 target, op.event_flags, op.screen_position, drag_operations_, |
| 204 base::Bind(&DragController::OnDragStatusCompleted, |
| 205 weak_factory_.GetWeakPtr(), target->id())); |
| 206 state->waiting_on_reply = OperationType::ENTER; |
| 207 break; |
| 208 } |
| 209 case OperationType::OVER: { |
| 210 connection->PerformOnDragOver( |
| 211 target, op.event_flags, op.screen_position, drag_operations_, |
| 212 base::Bind(&DragController::OnDragStatusCompleted, |
| 213 weak_factory_.GetWeakPtr(), target->id())); |
| 214 state->waiting_on_reply = OperationType::OVER; |
| 215 break; |
| 216 } |
| 217 case OperationType::LEAVE: { |
| 218 connection->PerformOnDragLeave(target); |
| 219 state->waiting_on_reply = OperationType::NONE; |
| 220 break; |
| 221 } |
| 222 case OperationType::DROP: { |
| 223 connection->PerformOnCompleteDrop( |
| 224 target, op.event_flags, op.screen_position, drag_operations_, |
| 225 base::Bind(&DragController::OnDragDropCompleted, |
| 226 weak_factory_.GetWeakPtr(), target->id())); |
| 227 state->waiting_on_reply = OperationType::DROP; |
| 228 break; |
| 229 } |
| 230 } |
| 231 |
| 232 state->queued_operation = {OperationType::NONE, 0, gfx::Point()}; |
| 233 } |
| 234 |
| 235 void DragController::OnRespondToOperation(ServerWindow* window) { |
| 236 WindowState& state = window_state_[window]; |
| 237 DCHECK_NE(OperationType::NONE, state.waiting_on_reply); |
| 238 state.waiting_on_reply = OperationType::NONE; |
| 239 if (state.queued_operation.type != OperationType::NONE) |
| 240 DispatchOperation(window, &state); |
| 241 } |
| 242 |
| 243 void DragController::OnDragStatusCompleted(const WindowId& id, |
| 244 uint32_t bitmask) { |
| 245 ServerWindow* window = source_->GetWindowById(id); |
| 246 if (!window) { |
| 247 // The window has been deleted and its queue is empty. |
| 248 return; |
| 249 } |
| 250 |
| 251 // We must remove the completed item. |
| 252 OnRespondToOperation(window); |
| 253 |
| 254 // TODO(erg): |bitmask| is the allowed drag actions at the mouse location. We |
| 255 // should use this data to change the cursor. |
| 256 } |
| 257 |
| 258 void DragController::OnDragDropCompleted(const WindowId& id, uint32_t bitmask) { |
| 259 ServerWindow* window = source_->GetWindowById(id); |
| 260 if (!window) { |
| 261 // The window has been deleted after we sent the drop message. It's really |
| 262 // hard to recover from this so just signal to the source that our drag |
| 263 // failed. |
| 264 MessageDragCompleted(false); |
| 265 return; |
| 266 } |
| 267 |
| 268 OnRespondToOperation(window); |
| 269 MessageDragCompleted(bitmask != 0u); |
| 270 } |
| 271 |
| 272 void DragController::OnWindowDestroying(ServerWindow* window) { |
| 273 auto it = window_state_.find(window); |
| 274 if (it != window_state_.end()) { |
| 275 window->RemoveObserver(this); |
| 276 window_state_.erase(it); |
| 277 } |
| 278 |
| 279 if (current_target_window_ == window) |
| 280 current_target_window_ = nullptr; |
| 281 |
| 282 if (source_window_ == window) { |
| 283 source_window_ = nullptr; |
| 284 // Our source window is being deleted, fail the drag. |
| 285 MessageDragCompleted(false); |
| 286 } |
| 287 } |
| 288 |
| 289 } // namespace ws |
| 290 } // namespace ui |
OLD | NEW |