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