KDeclarative

DeclarativeDragArea.cpp
1 /*
2  SPDX-FileCopyrightText: 2010 BetterInbox <[email protected]>
3  SPDX-FileContributor: Gregory Schlomoff <[email protected]>
4 
5  SPDX-License-Identifier: MIT
6 */
7 
8 #include "DeclarativeDragArea.h"
9 #include "DeclarativeMimeData.h"
10 
11 #include <QDrag>
12 #include <QIcon>
13 #include <QGuiApplication>
14 #include <QMimeData>
15 #include <QMouseEvent>
16 #include <QPainter>
17 #include <QQuickItemGrabResult>
18 #include <QQuickWindow>
19 #include <QStyleHints>
20 
21 #include <QDebug>
22 
27 DeclarativeDragArea::DeclarativeDragArea(QQuickItem *parent)
28  : QQuickItem(parent),
29  m_delegate(nullptr),
30  m_source(parent),
31  m_target(nullptr),
32  m_enabled(true),
33  m_draggingJustStarted(false),
34  m_dragActive(false),
35  m_supportedActions(Qt::MoveAction),
36  m_defaultAction(Qt::MoveAction),
37  m_data(new DeclarativeMimeData()), // m_data is owned by us, and we shouldn't pass it to Qt directly as it will automatically delete it after the drag and drop.
38  m_pressAndHoldTimerId(0)
39 {
40  m_startDragDistance = QGuiApplication::styleHints()->startDragDistance();
41  setAcceptedMouseButtons(Qt::LeftButton);
42 // setFiltersChildEvents(true);
43  setFlag(ItemAcceptsDrops, m_enabled);
44  setFiltersChildMouseEvents(true);
45 }
46 
47 DeclarativeDragArea::~DeclarativeDragArea()
48 {
49  if (m_data) {
50  delete m_data;
51  }
52 }
53 
58 QQuickItem* DeclarativeDragArea::delegate() const
59 {
60  return m_delegate;
61 }
62 
63 void DeclarativeDragArea::setDelegate(QQuickItem *delegate)
64 {
65  if (m_delegate != delegate) {
66  //qDebug() << " ______________________________________________ " << delegate;
67  m_delegate = delegate;
68  Q_EMIT delegateChanged();
69  }
70 }
71 void DeclarativeDragArea::resetDelegate()
72 {
73  setDelegate(nullptr);
74 }
75 
80 QQuickItem* DeclarativeDragArea::source() const
81 {
82  return m_source;
83 }
84 
85 void DeclarativeDragArea::setSource(QQuickItem* source)
86 {
87  if (m_source != source) {
88  m_source = source;
89  Q_EMIT sourceChanged();
90  }
91 }
92 
93 void DeclarativeDragArea::resetSource()
94 {
95  setSource(nullptr);
96 }
97 
98 bool DeclarativeDragArea::dragActive() const
99 {
100  return m_dragActive;
101 }
102 
103 // target
104 QQuickItem* DeclarativeDragArea::target() const
105 {
106  //TODO: implement me
107  return nullptr;
108 }
109 
110 // data
111 DeclarativeMimeData* DeclarativeDragArea::mimeData() const
112 {
113  return m_data;
114 }
115 
116 // startDragDistance
117 int DeclarativeDragArea::startDragDistance() const
118 {
119  return m_startDragDistance;
120 }
121 
122 void DeclarativeDragArea::setStartDragDistance(int distance)
123 {
124  if (distance == m_startDragDistance) {
125  return;
126  }
127 
128  m_startDragDistance = distance;
129  Q_EMIT startDragDistanceChanged();
130 }
131 
132 // delegateImage
133 QVariant DeclarativeDragArea::delegateImage() const
134 {
135  return m_delegateImage;
136 }
137 
138 void DeclarativeDragArea::setDelegateImage(const QVariant &image)
139 {
140  if (image.canConvert<QImage>() && image.value<QImage>() == m_delegateImage) {
141  return;
142  }
143 
144  if (image.canConvert<QImage>()) {
145  m_delegateImage = image.value<QImage>();
146  } else if (image.canConvert<QString>()) {
147  m_delegateImage = QIcon::fromTheme(image.toString()).pixmap(QSize(48, 48)).toImage();
148  } else {
149  m_delegateImage = image.value<QIcon>().pixmap(QSize(48, 48)).toImage();
150  }
151 
152  Q_EMIT delegateImageChanged();
153 }
154 
155 // enabled
156 bool DeclarativeDragArea::isEnabled() const
157 {
158  return m_enabled;
159 }
160 void DeclarativeDragArea::setEnabled(bool enabled)
161 {
162  if (enabled != m_enabled) {
163  m_enabled = enabled;
164  Q_EMIT enabledChanged();
165  }
166 }
167 
168 // supported actions
169 Qt::DropActions DeclarativeDragArea::supportedActions() const
170 {
171  return m_supportedActions;
172 }
173 void DeclarativeDragArea::setSupportedActions(Qt::DropActions actions)
174 {
175  if (actions != m_supportedActions) {
176  m_supportedActions = actions;
177  Q_EMIT supportedActionsChanged();
178  }
179 }
180 
181 // default action
182 Qt::DropAction DeclarativeDragArea::defaultAction() const
183 {
184  return m_defaultAction;
185 }
186 void DeclarativeDragArea::setDefaultAction(Qt::DropAction action)
187 {
188  if (action != m_defaultAction) {
189  m_defaultAction = action;
190  Q_EMIT defaultActionChanged();
191  }
192 }
193 
194 void DeclarativeDragArea::mousePressEvent(QMouseEvent* event)
195 {
196  m_pressAndHoldTimerId = startTimer(QGuiApplication::styleHints()->mousePressAndHoldInterval());
197  m_buttonDownPos = event->screenPos();
198  m_draggingJustStarted = true;
199  setKeepMouseGrab(true);
200 }
201 
202 void DeclarativeDragArea::mouseReleaseEvent(QMouseEvent* event)
203 {
204  Q_UNUSED(event);
205  killTimer(m_pressAndHoldTimerId);
206  m_pressAndHoldTimerId = 0;
207  m_draggingJustStarted = false;
208  setKeepMouseGrab(false);
209  ungrabMouse();
210 }
211 
212 void DeclarativeDragArea::timerEvent(QTimerEvent *event)
213 {
214  if (event->timerId() == m_pressAndHoldTimerId && m_draggingJustStarted && m_enabled) {
215  // Grab delegate before starting drag
216  if (m_delegate) {
217  // Another grab is already in progress
218  if (m_grabResult) {
219  return;
220  }
221  m_grabResult = m_delegate->grabToImage();
222  if (m_grabResult) {
223  connect(m_grabResult.data(), &QQuickItemGrabResult::ready, this, [this]() {
224  startDrag(m_grabResult->image());
225  m_grabResult.reset();
226  });
227  return;
228  }
229  }
230 
231  // No delegate or grab failed, start drag immediately
232  startDrag(m_delegateImage);
233  }
234 }
235 
236 void DeclarativeDragArea::mouseMoveEvent(QMouseEvent *event)
237 {
238  if ( !m_enabled
239  || QLineF(event->screenPos(), m_buttonDownPos).length()
240  < m_startDragDistance) {
241  return;
242  }
243 
244  //don't start drags on move for touch events, they'll be handled only by press and hold
245  //reset timer if moved more than m_startDragDistance
246  if (event->source() == Qt::MouseEventSynthesizedByQt) {
247  killTimer(m_pressAndHoldTimerId);
248  m_pressAndHoldTimerId = 0;
249  return;
250  }
251 
252  if (m_draggingJustStarted) {
253  // Grab delegate before starting drag
254  if (m_delegate) {
255  // Another grab is already in progress
256  if (m_grabResult) {
257  return;
258  }
259  m_grabResult = m_delegate->grabToImage();
260  if (m_grabResult) {
261  connect(m_grabResult.data(), &QQuickItemGrabResult::ready, this, [this]() {
262  startDrag(m_grabResult->image());
263  m_grabResult.reset();
264  });
265  return;
266  }
267  }
268 
269  // No delegate or grab failed, start drag immediately
270  startDrag(m_delegateImage);
271  }
272 }
273 
274 bool DeclarativeDragArea::childMouseEventFilter(QQuickItem *item, QEvent *event)
275 {
276  if (!isEnabled()) {
277  return false;
278  }
279 
280  switch (event->type()) {
282  QMouseEvent *me = static_cast<QMouseEvent *>(event);
283  //qDebug() << "press in dragarea";
284  mousePressEvent(me);
285  break;
286  }
287  case QEvent::MouseMove: {
288  QMouseEvent *me = static_cast<QMouseEvent *>(event);
289  //qDebug() << "move in dragarea";
290  mouseMoveEvent(me);
291  break;
292  }
294  QMouseEvent *me = static_cast<QMouseEvent *>(event);
295  //qDebug() << "release in dragarea";
296  mouseReleaseEvent(me);
297  break;
298  }
299  default:
300  break;
301  }
302 
303  return QQuickItem::childMouseEventFilter(item, event);
304 }
305 
306 void DeclarativeDragArea::startDrag(const QImage &image)
307 {
308  grabMouse();
309  m_draggingJustStarted = false;
310 
311  QDrag *drag = new QDrag(parent());
312  DeclarativeMimeData *dataCopy = new DeclarativeMimeData(m_data); //Qt will take ownership of this copy and delete it.
313  drag->setMimeData(dataCopy);
314 
315  const qreal devicePixelRatio = window() ? window()->devicePixelRatio() : 1;
316  const int imageSize = 48 * devicePixelRatio;
317 
318  if (!image.isNull()) {
319  drag->setPixmap(QPixmap::fromImage(image));
320  } else if (mimeData()->hasImage()) {
321  const QImage im = qvariant_cast<QImage>(mimeData()->imageData());
322  drag->setPixmap(QPixmap::fromImage(im));
323  } else if (mimeData()->hasColor()) {
324  QPixmap px(imageSize, imageSize);
325  px.fill(mimeData()->color());
326  drag->setPixmap(px);
327  } else {
328  // Icons otherwise
329  QStringList icons;
330  if (mimeData()->hasText()) {
331  icons << QStringLiteral("text-plain");
332  }
333  if (mimeData()->hasHtml()) {
334  icons << QStringLiteral("text-html");
335  }
336  if (mimeData()->hasUrls()) {
337  for (int i = 0; i < std::min(4, mimeData()->urls().size()); ++i) {
338  icons << QStringLiteral("text-html");
339  }
340  }
341  if (!icons.isEmpty()) {
342  QPixmap pm(imageSize * icons.count(), imageSize);
343  pm.fill(Qt::transparent);
344  QPainter p(&pm);
345  int i = 0;
346  for (const QString &ic : qAsConst(icons)) {
347  p.drawPixmap(QPoint(i * imageSize, 0), QIcon::fromTheme(ic).pixmap(imageSize));
348  i++;
349  }
350  p.end();
351  drag->setPixmap(pm);
352  }
353  }
354 
355  //drag->setHotSpot(QPoint(drag->pixmap().width()/2, drag->pixmap().height()/2)); // TODO: Make a property for that
356  //setCursor(Qt::OpenHandCursor); //TODO? Make a property for the cursor
357 
358  m_dragActive = true;
359  Q_EMIT dragActiveChanged();
360  Q_EMIT dragStarted();
361 
362  Qt::DropAction action = drag->exec(m_supportedActions, m_defaultAction);
363  setKeepMouseGrab(false);
364 
365  m_dragActive = false;
366  Q_EMIT dragActiveChanged();
367  Q_EMIT drop(action);
368 
369  ungrabMouse();
370 }
bool canConvert(int targetTypeId) const const
MouseButtonPress
Qt::MouseEventSource source() const const
QEvent::Type type() const const
int size() const const
KJOBWIDGETS_EXPORT QWidget * window(KJob *job)
void fill(const QColor &color)
void setMimeData(QMimeData *data)
int devicePixelRatio() const const
void setPixmap(const QPixmap &pixmap)
LeftButton
T value() const const
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
bool isNull() const const
virtual bool event(QEvent *e)
Qt::DropAction exec(Qt::DropActions supportedActions)
int count(const T &value) const const
QStyleHints * styleHints()
const QPointF & screenPos() const const
bool isEmpty() const const
virtual bool childMouseEventFilter(QQuickItem *item, QEvent *event)
int startTimer(int interval, Qt::TimerType timerType)
MouseEventSynthesizedByQt
int timerId() const const
QIcon fromTheme(const QString &name)
KIOWIDGETS_EXPORT DropJob * drop(const QDropEvent *dropEvent, const QUrl &destUrl, JobFlags flags=DefaultFlags)
transparent
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
MoveAction
QString toString() const const
void killTimer(int id)
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Jan 25 2021 22:44:28 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.