Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(355)

Side by Side Diff: services/ui/ws/drag_controller.cc

Issue 2266603002: mus: Implement interwindow drag and drop (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: sky comments Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698