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 |