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