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