1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquickdroparea_p.h" |
5 | #include "qquickdrag_p.h" |
6 | #include "qquickitem_p.h" |
7 | |
8 | #include <private/qv4arraybuffer_p.h> |
9 | |
10 | #include <QtCore/qregularexpression.h> |
11 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | QQuickDropAreaDrag::QQuickDropAreaDrag(QQuickDropAreaPrivate *d, QObject *parent) |
15 | : QObject(parent) |
16 | , d(d) |
17 | { |
18 | } |
19 | |
20 | QQuickDropAreaDrag::~QQuickDropAreaDrag() |
21 | { |
22 | } |
23 | |
24 | class QQuickDropAreaPrivate : public QQuickItemPrivate |
25 | { |
26 | Q_DECLARE_PUBLIC(QQuickDropArea) |
27 | |
28 | public: |
29 | QQuickDropAreaPrivate(); |
30 | ~QQuickDropAreaPrivate(); |
31 | |
32 | bool hasMatchingKey(const QStringList &keys) const; |
33 | |
34 | QStringList getKeys(const QMimeData *mimeData) const; |
35 | |
36 | QStringList keys; |
37 | QRegularExpression keyRegExp; |
38 | QPointF dragPosition; |
39 | QQuickDropAreaDrag *drag; |
40 | QPointer<QObject> source; |
41 | bool containsDrag; |
42 | }; |
43 | |
44 | QQuickDropAreaPrivate::QQuickDropAreaPrivate() |
45 | : drag(nullptr) |
46 | , containsDrag(false) |
47 | { |
48 | } |
49 | |
50 | QQuickDropAreaPrivate::~QQuickDropAreaPrivate() |
51 | { |
52 | delete drag; |
53 | } |
54 | |
55 | /*! |
56 | \qmltype DropArea |
57 | \instantiates QQuickDropArea |
58 | \inherits Item |
59 | \inqmlmodule QtQuick |
60 | \ingroup qtquick-input |
61 | \brief For specifying drag and drop handling in an area. |
62 | |
63 | A DropArea is an invisible item which receives events when other items are |
64 | dragged over it. |
65 | |
66 | The \l Drag attached property can be used to notify the DropArea when an Item is |
67 | dragged over it. |
68 | |
69 | The \l keys property can be used to filter drag events which don't include |
70 | a matching key. |
71 | |
72 | The \l drag.source property is communicated to the source of a drag event as |
73 | the recipient of a drop on the drag target. |
74 | |
75 | \sa {Qt Quick Examples - Drag and Drop} |
76 | */ |
77 | |
78 | QQuickDropArea::QQuickDropArea(QQuickItem *parent) |
79 | : QQuickItem(*new QQuickDropAreaPrivate, parent) |
80 | { |
81 | setFlags(ItemAcceptsDrops); |
82 | } |
83 | |
84 | QQuickDropArea::~QQuickDropArea() |
85 | { |
86 | } |
87 | |
88 | /*! |
89 | \qmlproperty bool QtQuick::DropArea::containsDrag |
90 | |
91 | This property identifies whether the DropArea currently contains any |
92 | dragged items. |
93 | */ |
94 | |
95 | bool QQuickDropArea::containsDrag() const |
96 | { |
97 | Q_D(const QQuickDropArea); |
98 | return d->containsDrag; |
99 | } |
100 | |
101 | /*! |
102 | \qmlproperty stringlist QtQuick::DropArea::keys |
103 | |
104 | This property holds a list of drag keys a DropArea will accept. |
105 | |
106 | If no keys are listed the DropArea will accept events from any drag source, |
107 | otherwise the drag source must have at least one compatible key. |
108 | |
109 | \sa QtQuick::Drag::keys |
110 | */ |
111 | |
112 | QStringList QQuickDropArea::keys() const |
113 | { |
114 | Q_D(const QQuickDropArea); |
115 | return d->keys; |
116 | } |
117 | |
118 | void QQuickDropArea::setKeys(const QStringList &keys) |
119 | { |
120 | Q_D(QQuickDropArea); |
121 | if (d->keys != keys) { |
122 | d->keys = keys; |
123 | |
124 | if (keys.isEmpty()) { |
125 | d->keyRegExp = QRegularExpression(); |
126 | } else { |
127 | QString pattern = QLatin1Char('(') + QRegularExpression::escape(str: keys.first()); |
128 | for (int i = 1; i < keys.size(); ++i) |
129 | pattern += QLatin1Char('|') + QRegularExpression::escape(str: keys.at(i)); |
130 | pattern += QLatin1Char(')'); |
131 | d->keyRegExp = QRegularExpression( |
132 | QRegularExpression::anchoredPattern(expression: pattern.replace(before: QLatin1String("\\*" ), |
133 | after: QLatin1String(".+" )))); |
134 | } |
135 | emit keysChanged(); |
136 | } |
137 | } |
138 | |
139 | QQuickDropAreaDrag *QQuickDropArea::drag() |
140 | { |
141 | Q_D(QQuickDropArea); |
142 | if (!d->drag) |
143 | d->drag = new QQuickDropAreaDrag(d); |
144 | return d->drag; |
145 | } |
146 | |
147 | /*! |
148 | \qmlproperty QtObject QtQuick::DropArea::drag.source |
149 | |
150 | This property holds the source of a drag. |
151 | */ |
152 | |
153 | QObject *QQuickDropAreaDrag::source() const |
154 | { |
155 | return d->source; |
156 | } |
157 | |
158 | /*! |
159 | \qmlpropertygroup QtQuick::DropArea::drag |
160 | \qmlproperty qreal QtQuick::DropArea::drag.x |
161 | \qmlproperty qreal QtQuick::DropArea::drag.y |
162 | |
163 | These properties hold the coordinates of the last drag event. |
164 | */ |
165 | |
166 | qreal QQuickDropAreaDrag::x() const |
167 | { |
168 | return d->dragPosition.x(); |
169 | } |
170 | |
171 | qreal QQuickDropAreaDrag::y() const |
172 | { |
173 | return d->dragPosition.y(); |
174 | } |
175 | |
176 | /*! |
177 | \qmlsignal QtQuick::DropArea::positionChanged(DragEvent drag) |
178 | |
179 | This signal is emitted when the position of a \a drag has changed. |
180 | */ |
181 | |
182 | void QQuickDropArea::dragMoveEvent(QDragMoveEvent *event) |
183 | { |
184 | Q_D(QQuickDropArea); |
185 | if (!d->containsDrag) |
186 | return; |
187 | |
188 | d->dragPosition = event->position().toPoint(); |
189 | if (d->drag) |
190 | emit d->drag->positionChanged(); |
191 | |
192 | event->accept(); |
193 | QQuickDragEvent dragTargetEvent(d, event); |
194 | emit positionChanged(drag: &dragTargetEvent); |
195 | } |
196 | |
197 | bool QQuickDropAreaPrivate::hasMatchingKey(const QStringList &keys) const |
198 | { |
199 | if (keyRegExp.pattern().isEmpty()) |
200 | return true; |
201 | |
202 | for (const QString &key : keys) { |
203 | if (key.contains(re: keyRegExp)) |
204 | return true; |
205 | } |
206 | return false; |
207 | } |
208 | |
209 | QStringList QQuickDropAreaPrivate::getKeys(const QMimeData *mimeData) const |
210 | { |
211 | if (const QQuickDragMimeData *dragMime = qobject_cast<const QQuickDragMimeData *>(object: mimeData)) |
212 | return dragMime->keys(); |
213 | return mimeData->formats(); |
214 | } |
215 | |
216 | /*! |
217 | \qmlsignal QtQuick::DropArea::entered(DragEvent drag) |
218 | |
219 | This signal is emitted when a \a drag enters the bounds of a DropArea. |
220 | */ |
221 | |
222 | void QQuickDropArea::dragEnterEvent(QDragEnterEvent *event) |
223 | { |
224 | Q_D(QQuickDropArea); |
225 | const QMimeData *mimeData = event->mimeData(); |
226 | if (!d->effectiveEnable || d->containsDrag || !mimeData || !d->hasMatchingKey(keys: d->getKeys(mimeData))) |
227 | return; |
228 | |
229 | const QQuickDragMimeData *dragMime = qobject_cast<const QQuickDragMimeData *>(object: mimeData); |
230 | auto dragSource = dragMime ? dragMime->source() : event->source(); |
231 | |
232 | // if the source of the drag is an ancestor of the drop area, then dragging |
233 | // also drags the drop area; see QTBUG-64128 |
234 | if (QQuickItem *dragSourceItem = qobject_cast<QQuickItem *>(o: dragSource)) { |
235 | if (dragSourceItem->isAncestorOf(child: this)) |
236 | return; |
237 | } |
238 | |
239 | d->dragPosition = event->position().toPoint(); |
240 | |
241 | event->accept(); |
242 | |
243 | QQuickDragEvent dragTargetEvent(d, event); |
244 | emit entered(drag: &dragTargetEvent); |
245 | if (!event->isAccepted()) |
246 | return; |
247 | |
248 | d->containsDrag = true; |
249 | d->source = dragSource; |
250 | d->dragPosition = event->position().toPoint(); |
251 | if (d->drag) { |
252 | emit d->drag->positionChanged(); |
253 | emit d->drag->sourceChanged(); |
254 | } |
255 | emit containsDragChanged(); |
256 | } |
257 | |
258 | /*! |
259 | \qmlsignal QtQuick::DropArea::exited() |
260 | |
261 | This signal is emitted when a drag exits the bounds of a DropArea. |
262 | */ |
263 | |
264 | void QQuickDropArea::dragLeaveEvent(QDragLeaveEvent *) |
265 | { |
266 | Q_D(QQuickDropArea); |
267 | if (!d->containsDrag) |
268 | return; |
269 | |
270 | emit exited(); |
271 | |
272 | d->containsDrag = false; |
273 | d->source = nullptr; |
274 | emit containsDragChanged(); |
275 | if (d->drag) |
276 | emit d->drag->sourceChanged(); |
277 | } |
278 | |
279 | /*! |
280 | \qmlsignal QtQuick::DropArea::dropped(DragEvent drop) |
281 | |
282 | This signal is emitted when a \a drop event occurs within the bounds of |
283 | a DropArea. |
284 | */ |
285 | |
286 | void QQuickDropArea::dropEvent(QDropEvent *event) |
287 | { |
288 | Q_D(QQuickDropArea); |
289 | if (!d->containsDrag) |
290 | return; |
291 | |
292 | QQuickDragEvent dragTargetEvent(d, event); |
293 | emit dropped(drop: &dragTargetEvent); |
294 | |
295 | d->containsDrag = false; |
296 | d->source = nullptr; |
297 | emit containsDragChanged(); |
298 | if (d->drag) |
299 | emit d->drag->sourceChanged(); |
300 | } |
301 | |
302 | /*! |
303 | \qmltype DragEvent |
304 | \instantiates QQuickDragEvent |
305 | \inqmlmodule QtQuick |
306 | \ingroup qtquick-input-events |
307 | \brief Provides information about a drag event. |
308 | |
309 | The position of the drag event can be obtained from the \l x and \l y |
310 | properties, and the \l keys property identifies the drag keys of the event |
311 | \l {drag.source}{source}. |
312 | |
313 | The existence of specific drag types can be determined using the \l hasColor, |
314 | \l hasHtml, \l hasText, and \l hasUrls properties. |
315 | |
316 | The list of all supplied formats can be determined using the \l formats property. |
317 | |
318 | Specific drag types can be obtained using the \l colorData, \l html, \l text, |
319 | and \l urls properties. |
320 | |
321 | A string version of any available mimeType can be obtained using \l getDataAsString. |
322 | */ |
323 | |
324 | /*! |
325 | \qmlproperty real QtQuick::DragEvent::x |
326 | |
327 | This property holds the x coordinate of a drag event. |
328 | */ |
329 | |
330 | /*! |
331 | \qmlproperty real QtQuick::DragEvent::y |
332 | |
333 | This property holds the y coordinate of a drag event. |
334 | */ |
335 | |
336 | /*! |
337 | \qmlproperty QtObject QtQuick::DragEvent::drag.source |
338 | |
339 | This property holds the source of a drag event. |
340 | */ |
341 | |
342 | /*! |
343 | \qmlproperty stringlist QtQuick::DragEvent::keys |
344 | |
345 | This property holds a list of keys identifying the data type or source of a |
346 | drag event. |
347 | */ |
348 | |
349 | /*! |
350 | \qmlproperty enumeration QtQuick::DragEvent::action |
351 | |
352 | This property holds the action that the \l {drag.source}{source} is to perform on an accepted drop. |
353 | |
354 | The drop action may be one of: |
355 | |
356 | \value Qt.CopyAction Copy the data to the target. |
357 | \value Qt.MoveAction Move the data from the source to the target. |
358 | \value Qt.LinkAction Create a link from the source to the target. |
359 | \value Qt.IgnoreAction Ignore the action (do nothing with the data). |
360 | */ |
361 | |
362 | /*! |
363 | \qmlproperty flags QtQuick::DragEvent::supportedActions |
364 | |
365 | This property holds the set of \l {action}{actions} supported by the |
366 | drag source. |
367 | */ |
368 | |
369 | /*! |
370 | \qmlproperty flags QtQuick::DragEvent::proposedAction |
371 | \since 5.2 |
372 | |
373 | This property holds the set of \l {action}{actions} proposed by the |
374 | drag source. |
375 | */ |
376 | |
377 | /*! |
378 | \qmlproperty bool QtQuick::DragEvent::accepted |
379 | |
380 | This property holds whether the drag event was accepted by a handler. |
381 | |
382 | The default value is true. |
383 | */ |
384 | |
385 | /*! |
386 | \qmlmethod QtQuick::DragEvent::accept() |
387 | \qmlmethod QtQuick::DragEvent::accept(enumeration action) |
388 | |
389 | Accepts the drag event. |
390 | |
391 | If an \a action is specified it will overwrite the value of the \l action property. |
392 | */ |
393 | |
394 | /*! |
395 | \qmlmethod QtQuick::DragEvent::acceptProposedAction() |
396 | \since 5.2 |
397 | |
398 | Accepts the drag event with the \l proposedAction. |
399 | */ |
400 | |
401 | /*! |
402 | \qmlproperty bool QtQuick::DragEvent::hasColor |
403 | \since 5.2 |
404 | |
405 | This property holds whether the drag event contains a color item. |
406 | */ |
407 | |
408 | /*! |
409 | \qmlproperty bool QtQuick::DragEvent::hasHtml |
410 | \since 5.2 |
411 | |
412 | This property holds whether the drag event contains a html item. |
413 | */ |
414 | |
415 | /*! |
416 | \qmlproperty bool QtQuick::DragEvent::hasText |
417 | \since 5.2 |
418 | |
419 | This property holds whether the drag event contains a text item. |
420 | */ |
421 | |
422 | /*! |
423 | \qmlproperty bool QtQuick::DragEvent::hasUrls |
424 | \since 5.2 |
425 | |
426 | This property holds whether the drag event contains one or more url items. |
427 | */ |
428 | |
429 | /*! |
430 | \qmlproperty color QtQuick::DragEvent::colorData |
431 | \since 5.2 |
432 | |
433 | This property holds color data, if any. |
434 | */ |
435 | |
436 | /*! |
437 | \qmlproperty string QtQuick::DragEvent::html |
438 | \since 5.2 |
439 | |
440 | This property holds html data, if any. |
441 | */ |
442 | |
443 | /*! |
444 | \qmlproperty string QtQuick::DragEvent::text |
445 | \since 5.2 |
446 | |
447 | This property holds text data, if any. |
448 | */ |
449 | |
450 | /*! |
451 | \qmlproperty urllist QtQuick::DragEvent::urls |
452 | \since 5.2 |
453 | |
454 | This property holds a list of urls, if any. |
455 | */ |
456 | |
457 | /*! |
458 | \qmlproperty stringlist QtQuick::DragEvent::formats |
459 | \since 5.2 |
460 | |
461 | This property holds a list of mime type formats contained in the drag data. |
462 | */ |
463 | |
464 | /*! |
465 | \qmlmethod string QtQuick::DragEvent::getDataAsString(string format) |
466 | \since 5.2 |
467 | |
468 | Returns the data for the given \a format converted to a string. \a format should be one contained in the \l formats property. |
469 | */ |
470 | |
471 | /*! |
472 | \qmlmethod string QtQuick::DragEvent::getDataAsArrayBuffer(string format) |
473 | \since 5.5 |
474 | |
475 | Returns the data for the given \a format into an ArrayBuffer, which can |
476 | easily be translated into a QByteArray. \a format should be one contained in the \l formats property. |
477 | */ |
478 | |
479 | QObject *QQuickDragEvent::source() const |
480 | { |
481 | if (const QQuickDragMimeData *dragMime = qobject_cast<const QQuickDragMimeData *>(object: event->mimeData())) |
482 | return dragMime->source(); |
483 | else |
484 | return event->source(); |
485 | } |
486 | |
487 | QStringList QQuickDragEvent::keys() const |
488 | { |
489 | return d->getKeys(mimeData: event->mimeData()); |
490 | } |
491 | |
492 | bool QQuickDragEvent::hasColor() const |
493 | { |
494 | return event->mimeData()->hasColor(); |
495 | } |
496 | |
497 | bool QQuickDragEvent::hasHtml() const |
498 | { |
499 | return event->mimeData()->hasHtml(); |
500 | } |
501 | |
502 | bool QQuickDragEvent::hasText() const |
503 | { |
504 | return event->mimeData()->hasText(); |
505 | } |
506 | |
507 | bool QQuickDragEvent::hasUrls() const |
508 | { |
509 | return event->mimeData()->hasUrls(); |
510 | } |
511 | |
512 | QVariant QQuickDragEvent::colorData() const |
513 | { |
514 | return event->mimeData()->colorData(); |
515 | } |
516 | |
517 | QString QQuickDragEvent::html() const |
518 | { |
519 | return event->mimeData()->html(); |
520 | } |
521 | |
522 | QString QQuickDragEvent::text() const |
523 | { |
524 | return event->mimeData()->text(); |
525 | } |
526 | |
527 | QList<QUrl> QQuickDragEvent::urls() const |
528 | { |
529 | return event->mimeData()->urls(); |
530 | } |
531 | |
532 | QStringList QQuickDragEvent::formats() const |
533 | { |
534 | return event->mimeData()->formats(); |
535 | } |
536 | |
537 | QString QQuickDragEvent::getDataAsString(const QString &format) const |
538 | { |
539 | return QString::fromUtf8(ba: event->mimeData()->data(mimetype: format)); |
540 | } |
541 | |
542 | QByteArray QQuickDragEvent::getDataAsArrayBuffer(const QString &format) const |
543 | { |
544 | return event->mimeData()->data(mimetype: format); |
545 | } |
546 | |
547 | void QQuickDragEvent::acceptProposedAction() |
548 | { |
549 | event->acceptProposedAction(); |
550 | } |
551 | |
552 | void QQuickDragEvent::accept() |
553 | { |
554 | Qt::DropAction action = event->dropAction(); |
555 | event->setDropAction(action); |
556 | event->accept(); |
557 | } |
558 | |
559 | void QQuickDragEvent::accept(Qt::DropAction action) |
560 | { |
561 | // get action from arguments. |
562 | event->setDropAction(action); |
563 | event->accept(); |
564 | } |
565 | |
566 | |
567 | QT_END_NAMESPACE |
568 | |
569 | #include "moc_qquickdroparea_p.cpp" |
570 | |