Chromium Code Reviews| 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 DragController::DragController( | |
| 23 DragSource* source, | |
| 24 ServerWindow* source_window, | |
| 25 DragTargetConnection* source_connection, | |
| 26 int32_t drag_pointer, | |
| 27 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data, | |
| 28 uint32_t drag_operations) | |
| 29 : source_(source), | |
| 30 drag_operations_(drag_operations), | |
| 31 drag_pointer_id_(drag_pointer), | |
| 32 source_window_(source_window), | |
| 33 source_connection_(source_connection), | |
| 34 mime_data_(std::move(mime_data)), | |
| 35 weak_factory_(this) { | |
| 36 source_window_->AddObserver(this); | |
| 37 } | |
| 38 | |
| 39 DragController::~DragController() { | |
| 40 // Only remove observer one time. | |
| 41 std::set<ServerWindow*> observed_windows; | |
| 42 if (source_window_) | |
| 43 observed_windows.insert(source_window_); | |
| 44 if (current_target_window_) | |
| 45 observed_windows.insert(current_target_window_); | |
| 46 for (auto& w : window_operations_) | |
|
sky
2016/09/13 18:15:31
optional: 'w' is mildly misleading as I would tend
| |
| 47 observed_windows.insert(w.first); | |
| 48 | |
| 49 for (ServerWindow* w : observed_windows) | |
| 50 w->RemoveObserver(this); | |
| 51 } | |
| 52 | |
| 53 void DragController::Cancel() { | |
| 54 MessageDragCompleted(false); | |
| 55 // |this| may be deleted now. | |
| 56 } | |
| 57 | |
| 58 bool DragController::DispatchPointerEvent(const ui::PointerEvent& event, | |
| 59 ServerWindow* current_target) { | |
| 60 uint32_t event_flags = | |
| 61 event.flags() & | |
| 62 (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN); | |
| 63 gfx::Point screen_position = event.location(); | |
| 64 | |
| 65 if (waiting_for_final_drop_response_) { | |
| 66 // If we're waiting on a target window to respond to the final drag drop | |
| 67 // call, don't process any more pointer events. | |
| 68 return false; | |
| 69 } | |
| 70 | |
| 71 if (event.pointer_id() != drag_pointer_id_) | |
| 72 return false; | |
| 73 | |
| 74 // If |current_target| doesn't accept drags, walk its hierarchy up until we | |
| 75 // find one that does (or set to nullptr at the top of the tree). | |
| 76 while (current_target && !current_target->can_accept_drops()) | |
| 77 current_target = current_target->parent(); | |
| 78 | |
| 79 if (current_target && current_target == current_target_window_ && | |
| 80 event.type() != ET_POINTER_UP) { | |
| 81 // We're continuing a drag inside the bounds of | |
| 82 // |current_target_window_|. Send a continue message. | |
| 83 auto& window_ops = window_operations_[current_target]; | |
| 84 if (window_ops.size() > 1 && | |
| 85 window_ops.back().type == OperationType::OVER) { | |
| 86 // If we have a queued DragOver which isn't in progress, then just | |
| 87 // modify it's data. | |
| 88 auto& back = window_ops.back(); | |
| 89 back.event_flags = event_flags; | |
| 90 back.screen_position = screen_position; | |
| 91 } else { | |
| 92 QueueOperation(current_target, OperationType::OVER, event_flags, | |
| 93 screen_position); | |
| 94 } | |
| 95 } else if (current_target != current_target_window_) { | |
| 96 if (current_target_window_) { | |
| 97 auto& window_ops = window_operations_[current_target_window_]; | |
| 98 if (window_ops.size() > 1 && | |
| 99 window_ops.back().type == OperationType::OVER) { | |
| 100 // If we have queued DragOver events, we want to replace them. | |
| 101 auto& back = window_ops.back(); | |
| 102 back.type = OperationType::LEAVE; | |
| 103 back.event_flags = event_flags; | |
| 104 back.screen_position = screen_position; | |
| 105 } else { | |
| 106 QueueOperation(current_target_window_, OperationType::LEAVE, | |
| 107 event_flags, screen_position); | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 if (current_target) | |
| 112 QueueOperation(current_target, OperationType::ENTER, event_flags, | |
| 113 screen_position); | |
| 114 | |
| 115 SetCurrentTargetWindow(current_target); | |
| 116 } | |
| 117 | |
| 118 if (event.type() == ET_POINTER_UP) { | |
| 119 if (current_target) { | |
| 120 QueueOperation(current_target, OperationType::DROP, event_flags, | |
| 121 screen_position); | |
| 122 waiting_for_final_drop_response_ = true; | |
| 123 } else { | |
| 124 // The pointer was released over no window or a window that doesn't | |
| 125 // accept drags. | |
| 126 MessageDragCompleted(false); | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 return true; | |
| 131 } | |
| 132 | |
| 133 void DragController::OnWillDestroyDragTargetConnection( | |
| 134 DragTargetConnection* connection) { | |
| 135 called_on_drag_mime_types_.erase(connection); | |
| 136 } | |
| 137 | |
| 138 void DragController::MessageDragCompleted(bool success) { | |
| 139 for (DragTargetConnection* connection : called_on_drag_mime_types_) | |
| 140 connection->PerformOnDragFinish(); | |
| 141 called_on_drag_mime_types_.clear(); | |
| 142 | |
| 143 source_->OnDragCompleted(success); | |
| 144 // |this| may be deleted now. | |
| 145 } | |
| 146 | |
| 147 size_t DragController::GetSizeOfQueueForWindow(ServerWindow* window) { | |
| 148 auto it = window_operations_.find(window); | |
| 149 if (it == window_operations_.end()) | |
| 150 return 0u; | |
| 151 return it->second.size(); | |
| 152 } | |
| 153 | |
| 154 void DragController::SetCurrentTargetWindow(ServerWindow* current_target) { | |
| 155 // Only add or remove observers when the window isn't in |window_operations_|. | |
| 156 if (current_target_window_ && current_target_window_ != source_window_ && | |
| 157 !base::ContainsKey(window_operations_, current_target_window_)) { | |
| 158 current_target_window_->RemoveObserver(this); | |
| 159 } | |
| 160 | |
| 161 current_target_window_ = current_target; | |
| 162 | |
| 163 if (current_target_window_ && current_target_window_ != source_window_ && | |
| 164 !base::ContainsKey(window_operations_, current_target_window_)) { | |
| 165 current_target_window_->AddObserver(this); | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 void DragController::QueueOperation(ServerWindow* window, | |
| 170 OperationType type, | |
| 171 uint32_t event_flags, | |
| 172 gfx::Point screen_position) { | |
| 173 // If this window doesn't have the mime data, send it. | |
| 174 DragTargetConnection* connection = source_->GetDragTargetWithRoot(window); | |
| 175 if (connection != source_connection_ && | |
| 176 !base::ContainsKey(called_on_drag_mime_types_, connection)) { | |
| 177 connection->PerformOnDragMimeTypes(mime_data_.Clone()); | |
| 178 called_on_drag_mime_types_.insert(connection); | |
| 179 } | |
| 180 | |
| 181 auto& window_ops = window_operations_[window]; | |
| 182 if (window_ops.empty() && window != source_window_ && | |
| 183 window != current_target_window_) { | |
| 184 window->AddObserver(this); | |
| 185 } | |
| 186 window_ops.push_back({type, event_flags, screen_position}); | |
| 187 if (window_ops.size() == 1) { | |
| 188 // The first item in the queue is the item we're waiting for a response | |
| 189 // on. If we're the only item, send it. | |
| 190 DispatchFrontOfWindowQueue(window, &window_ops); | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 void DragController::RemoveQueueFront(ServerWindow* window) { | |
| 195 auto& window_ops = window_operations_[window]; | |
| 196 if (window_ops.size() == 1 && window != source_window_ && | |
| 197 window != current_target_window_) { | |
| 198 // This is the last event in queue; stop watching the queue. | |
|
sky
2016/09/13 18:15:31
If you do this, and the window is destroyed OnWind
Elliot Glaysher
2016/09/14 22:19:16
I'm confused.
If we're here, we're removing the l
sky
2016/09/14 22:52:02
Isn't the reference still held in the map? e.g. wi
Elliot Glaysher
2016/09/15 17:12:40
This (and related) chunks have been removed. This
| |
| 199 window->RemoveObserver(this); | |
| 200 } | |
| 201 | |
| 202 DCHECK(!window_ops.empty()); | |
| 203 window_ops.pop_front(); | |
| 204 | |
| 205 if (!window_ops.empty()) | |
| 206 DispatchFrontOfWindowQueue(window, &window_ops); | |
| 207 } | |
| 208 | |
| 209 void DragController::DispatchFrontOfWindowQueue(ServerWindow* target, | |
| 210 std::deque<Operation>* queue) { | |
| 211 DragTargetConnection* connection = source_->GetDragTargetWithRoot(target); | |
| 212 | |
| 213 DCHECK(!queue->empty()); | |
| 214 const Operation& op = queue->front(); | |
| 215 switch (op.type) { | |
| 216 case OperationType::ENTER: { | |
| 217 connection->PerformOnDragEnter( | |
| 218 target, op.event_flags, op.screen_position, drag_operations_, | |
| 219 base::Bind(&DragController::OnDragStatusCompleted, | |
| 220 weak_factory_.GetWeakPtr(), target->id())); | |
| 221 break; | |
| 222 } | |
| 223 case OperationType::OVER: { | |
| 224 connection->PerformOnDragOver( | |
| 225 target, op.event_flags, op.screen_position, drag_operations_, | |
| 226 base::Bind(&DragController::OnDragStatusCompleted, | |
| 227 weak_factory_.GetWeakPtr(), target->id())); | |
| 228 break; | |
| 229 } | |
| 230 case OperationType::LEAVE: { | |
| 231 connection->PerformOnDragLeave(target); | |
| 232 RemoveQueueFront(target); | |
| 233 break; | |
| 234 } | |
| 235 case OperationType::DROP: { | |
| 236 connection->PerformOnCompleteDrop( | |
| 237 target, op.event_flags, op.screen_position, drag_operations_, | |
| 238 base::Bind(&DragController::OnDragDropCompleted, | |
| 239 weak_factory_.GetWeakPtr(), target->id())); | |
| 240 break; | |
| 241 } | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 void DragController::OnDragStatusCompleted(const WindowId& id, | |
| 246 uint32_t bitmask) { | |
| 247 ServerWindow* window = source_->GetWindowById(id); | |
| 248 if (!window) { | |
| 249 // The window has been deleted and its queue is empty. | |
| 250 return; | |
| 251 } | |
| 252 | |
| 253 // We must remove the completed item. | |
| 254 RemoveQueueFront(window); | |
| 255 | |
| 256 // TODO(erg): |bitmask| is the allowed drag actions at the mouse location. We | |
| 257 // should use this data to change the cursor. | |
| 258 } | |
| 259 | |
| 260 void DragController::OnDragDropCompleted(const WindowId& id, uint32_t bitmask) { | |
| 261 ServerWindow* window = source_->GetWindowById(id); | |
| 262 if (!window) { | |
| 263 // The window has been deleted after we sent the drop message. It's really | |
| 264 // hard to recover from this so just signal to the source that our drag | |
| 265 // failed. | |
| 266 MessageDragCompleted(false); | |
| 267 return; | |
| 268 } | |
| 269 | |
| 270 // TODO(erg): What happens if there are events behind the drag drop!? | |
| 271 RemoveQueueFront(window); | |
| 272 MessageDragCompleted(bitmask != 0u); | |
| 273 } | |
| 274 | |
| 275 void DragController::OnWindowDestroying(ServerWindow* window) { | |
| 276 if (current_target_window_ == window) | |
| 277 SetCurrentTargetWindow(nullptr); | |
| 278 | |
| 279 window_operations_.erase(window); | |
| 280 | |
| 281 if (source_window_ == window) { | |
| 282 source_window_->RemoveObserver(this); | |
| 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 |