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 |