KDeclarative

mouseeventlistener.cpp
1 /*
2  SPDX-FileCopyrightText: 2011 Marco Martin <[email protected]>
3  SPDX-FileCopyrightText: 2013 Sebastian K├╝gler <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "mouseeventlistener.h"
9 
10 #include <QGuiApplication>
11 #include <QStyleHints>
12 #include <QEvent>
13 #include <QMouseEvent>
14 #include <QTimer>
15 #include <QQuickWindow>
16 #include <QScreen>
17 #include <QDebug>
18 
19 MouseEventListener::MouseEventListener(QQuickItem *parent)
20  : QQuickItem(parent),
21  m_pressed(false),
22  m_pressAndHoldEvent(nullptr),
23  m_lastEvent(nullptr),
24  m_containsMouse(false),
25  m_acceptedButtons(Qt::LeftButton)
26 {
27  m_pressAndHoldTimer = new QTimer(this);
28  m_pressAndHoldTimer->setSingleShot(true);
29  connect(m_pressAndHoldTimer, SIGNAL(timeout()),
30  this, SLOT(handlePressAndHold()));
31  qmlRegisterAnonymousType<KDeclarativeMouseEvent>("org.kde.kquickcontrolsaddons", 1);
32  qmlRegisterAnonymousType<KDeclarativeWheelEvent>("org.kde.kquickcontrolsaddons", 1);
33 
34  setFiltersChildMouseEvents(true);
36 }
37 
38 MouseEventListener::~MouseEventListener()
39 {
40 }
41 
42 Qt::MouseButtons MouseEventListener::acceptedButtons() const
43 {
44  return m_acceptedButtons;
45 }
46 
47 Qt::CursorShape MouseEventListener::cursorShape() const
48 {
49  return cursor().shape();
50 }
51 
52 void MouseEventListener::setCursorShape(Qt::CursorShape shape)
53 {
54  if (cursor().shape() == shape) {
55  return;
56  }
57 
58  setCursor(shape);
59 
60  Q_EMIT cursorShapeChanged();
61 }
62 
63 void MouseEventListener::setAcceptedButtons(Qt::MouseButtons buttons)
64 {
65  if (buttons == m_acceptedButtons) {
66  return;
67  }
68 
69  m_acceptedButtons = buttons;
70  Q_EMIT acceptedButtonsChanged();
71 }
72 
73 void MouseEventListener::setHoverEnabled(bool enable)
74 {
75  if (enable == acceptHoverEvents()) {
76  return;
77  }
78 
79  setAcceptHoverEvents(enable);
80  Q_EMIT hoverEnabledChanged(enable);
81 }
82 
83 bool MouseEventListener::hoverEnabled() const
84 {
85  return acceptHoverEvents();
86 }
87 
88 bool MouseEventListener::isPressed() const
89 {
90  return m_pressed;
91 }
92 
93 void MouseEventListener::hoverEnterEvent(QHoverEvent *event)
94 {
95  Q_UNUSED(event);
96 
97  m_containsMouse = true;
98  Q_EMIT containsMouseChanged(true);
99 }
100 
101 void MouseEventListener::hoverLeaveEvent(QHoverEvent *event)
102 {
103  Q_UNUSED(event);
104 
105  m_containsMouse = false;
106  Q_EMIT containsMouseChanged(false);
107 }
108 
109 void MouseEventListener::hoverMoveEvent(QHoverEvent * event)
110 {
111  if (m_lastEvent == event) {
112  return;
113  }
114 
115  QQuickWindow *w = window();
116  QPoint screenPos;
117  if (w) {
118  screenPos = w->mapToGlobal(event->pos());
119  }
120 
121  KDeclarativeMouseEvent dme(event->pos().x(), event->pos().y(), screenPos.x(), screenPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), nullptr, Qt::MouseEventNotSynthesized);
122  Q_EMIT positionChanged(&dme);
123 }
124 
125 bool MouseEventListener::containsMouse() const
126 {
127  return m_containsMouse;
128 }
129 
130 void MouseEventListener::mousePressEvent(QMouseEvent *me)
131 {
132  if (m_lastEvent == me || !(me->buttons() & m_acceptedButtons)) {
133  me->setAccepted(false);
134  return;
135  }
136 
137  //FIXME: when a popup window is visible: a click anywhere hides it: but the old qquickitem will continue to think it's under the mouse
138  //doesn't seem to be any good way to properly reset this.
139  //this msolution will still caused a missed click after the popup is gone, but gets the situation unblocked.
140  QPoint viewPosition;
141  if (window()) {
142  viewPosition = window()->position();
143  }
144 
145  if (!QRectF(mapToScene(QPoint(0, 0)) + viewPosition, QSizeF(width(), height())).contains(me->screenPos())) {
146  me->ignore();
147  return;
148  }
149  m_buttonDownPos = me->screenPos();
150 
151  KDeclarativeMouseEvent dme(me->pos().x(), me->pos().y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos()), me->source());
152  if (!m_pressAndHoldEvent) {
153  m_pressAndHoldEvent = new KDeclarativeMouseEvent(me->pos().x(), me->pos().y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos()), me->source());
154  }
155 
156  m_pressed = true;
157  Q_EMIT pressed(&dme);
158  Q_EMIT pressedChanged();
159 
160  if (dme.isAccepted()) {
161  me->setAccepted(true);
162  return;
163  }
164 
165  m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval());
166 }
167 
168 void MouseEventListener::mouseMoveEvent(QMouseEvent *me)
169 {
170  if (m_lastEvent == me || !(me->buttons() & m_acceptedButtons)) {
171  me->setAccepted(false);
172  return;
173  }
174 
175  if (QPointF(me->screenPos() - m_buttonDownPos).manhattanLength() > QGuiApplication::styleHints()->startDragDistance() && m_pressAndHoldTimer->isActive()) {
176  m_pressAndHoldTimer->stop();
177  }
178 
179  KDeclarativeMouseEvent dme(me->pos().x(), me->pos().y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos()), me->source());
180  Q_EMIT positionChanged(&dme);
181 
182  if (dme.isAccepted()) {
183  me->setAccepted(true);
184  }
185 }
186 
187 void MouseEventListener::mouseReleaseEvent(QMouseEvent *me)
188 {
189  if (m_lastEvent == me) {
190  me->setAccepted(false);
191  return;
192  }
193 
194  KDeclarativeMouseEvent dme(me->pos().x(), me->pos().y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos()), me->source());
195  m_pressed = false;
196  Q_EMIT released(&dme);
197  Q_EMIT pressedChanged();
198 
199  if (boundingRect().contains(me->pos()) && m_pressAndHoldTimer->isActive()) {
200  Q_EMIT clicked(&dme);
201  m_pressAndHoldTimer->stop();
202  }
203 
204  if (dme.isAccepted()) {
205  me->setAccepted(true);
206  }
207 }
208 
209 void MouseEventListener::wheelEvent(QWheelEvent *we)
210 {
211  if (m_lastEvent == we) {
212  return;
213  }
214 
215  KDeclarativeWheelEvent dwe(we->position().toPoint(), we->globalPosition().toPoint(), we->angleDelta(), we->buttons(), we->modifiers(), Qt::Vertical /* HACK, deprecated, remove */);
216  Q_EMIT wheelMoved(&dwe);
217 }
218 
219 void MouseEventListener::handlePressAndHold()
220 {
221  if (m_pressed) {
222  Q_EMIT pressAndHold(m_pressAndHoldEvent);
223 
224  delete m_pressAndHoldEvent;
225  m_pressAndHoldEvent = nullptr;
226  }
227 }
228 
229 bool MouseEventListener::childMouseEventFilter(QQuickItem *item, QEvent *event)
230 {
231  if (!isEnabled()) {
232  return false;
233  }
234 
235  //don't filter other mouseeventlisteners
236  if (qobject_cast<MouseEventListener *>(item)) {
237  return false;
238  }
239 
240  switch (event->type()) {
242  m_lastEvent = event;
243  QMouseEvent *me = static_cast<QMouseEvent *>(event);
244 
245  if (!(me->buttons() & m_acceptedButtons)) {
246  break;
247  }
248 
249  //the parent will receive events in its own coordinates
250  const QPointF myPos = mapFromScene(me->windowPos());
251 
252  KDeclarativeMouseEvent dme(myPos.x(), myPos.y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos()), me->source());
253  delete m_pressAndHoldEvent;
254  m_pressAndHoldEvent = new KDeclarativeMouseEvent(myPos.x(), myPos.y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos()), me->source());
255 
256  //qDebug() << "pressed in sceneEventFilter";
257  m_buttonDownPos = me->screenPos();
258  m_pressed = true;
259  Q_EMIT pressed(&dme);
260  Q_EMIT pressedChanged();
261 
262  if (dme.isAccepted()) {
263  return true;
264  }
265 
266  m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval());
267 
268  break;
269  }
270  case QEvent::HoverMove: {
271  if (!acceptHoverEvents()) {
272  break;
273  }
274  m_lastEvent = event;
275  QHoverEvent *he = static_cast<QHoverEvent *>(event);
276  const QPointF myPos = item->mapToItem(this, he->pos());
277 
278  QQuickWindow *w = window();
279  QPoint screenPos;
280  if (w) {
281  screenPos = w->mapToGlobal(myPos.toPoint());
282  }
283 
284  KDeclarativeMouseEvent dme(myPos.x(), myPos.y(), screenPos.x(), screenPos.y(), Qt::NoButton, Qt::NoButton, he->modifiers(), nullptr, Qt::MouseEventNotSynthesized);
285  //qDebug() << "positionChanged..." << dme.x() << dme.y();
286  Q_EMIT positionChanged(&dme);
287 
288  if (dme.isAccepted()) {
289  return true;
290  }
291  break;
292  }
293  case QEvent::MouseMove: {
294  m_lastEvent = event;
295  QMouseEvent *me = static_cast<QMouseEvent *>(event);
296  if (!(me->buttons() & m_acceptedButtons)) {
297  break;
298  }
299 
300  const QPointF myPos = mapFromScene(me->windowPos());
301  KDeclarativeMouseEvent dme(myPos.x(), myPos.y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos()), me->source());
302  //qDebug() << "positionChanged..." << dme.x() << dme.y();
303 
304  //stop the pressandhold if mouse moved enough
305  if (QPointF(me->screenPos() - m_buttonDownPos).manhattanLength() > QGuiApplication::styleHints()->startDragDistance() && m_pressAndHoldTimer->isActive()) {
306  m_pressAndHoldTimer->stop();
307 
308  //if the mouse moves and we are waiting to emit a press and hold event, update the co-ordinates
309  //as there is no update function, delete the old event and create a new one
310  } else if (m_pressAndHoldEvent) {
311  delete m_pressAndHoldEvent;
312  m_pressAndHoldEvent = new KDeclarativeMouseEvent(myPos.x(), myPos.y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos()), me->source());
313  }
314  Q_EMIT positionChanged(&dme);
315 
316  if (dme.isAccepted()) {
317  return true;
318  }
319  break;
320  }
322  m_lastEvent = event;
323  QMouseEvent *me = static_cast<QMouseEvent *>(event);
324 
325  const QPointF myPos = mapFromScene(me->windowPos());
326  KDeclarativeMouseEvent dme(myPos.x(), myPos.y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos()), me->source());
327  m_pressed = false;
328 
329  Q_EMIT released(&dme);
330  Q_EMIT pressedChanged();
331 
332  if (QPointF(me->screenPos() - m_buttonDownPos).manhattanLength() <= QGuiApplication::styleHints()->startDragDistance() && m_pressAndHoldTimer->isActive()) {
333  Q_EMIT clicked(&dme);
334  m_pressAndHoldTimer->stop();
335  }
336 
337  if (dme.isAccepted()) {
338  return true;
339  }
340  break;
341  }
342  case QEvent::UngrabMouse: {
343  m_lastEvent = event;
344  handleUngrab();
345  break;
346  }
347  case QEvent::Wheel: {
348  m_lastEvent = event;
349  QWheelEvent *we = static_cast<QWheelEvent *>(event);
350  KDeclarativeWheelEvent dwe(we->position().toPoint(), we->globalPosition().toPoint(), we->angleDelta(), we->buttons(), we->modifiers(), Qt::Vertical /* HACK, deprecated, remove */);
351  Q_EMIT wheelMoved(&dwe);
352  break;
353  }
354  default:
355  break;
356  }
357 
358  return QQuickItem::childMouseEventFilter(item, event);
359 // return false;
360 }
361 
362 QScreen* MouseEventListener::screenForGlobalPos(const QPoint& globalPos)
363 {
364  const auto screens = QGuiApplication::screens();
365  for (QScreen *screen : screens) {
366  if (screen->geometry().contains(globalPos)) {
367  return screen;
368  }
369  }
370  return nullptr;
371 }
372 
373 void MouseEventListener::mouseUngrabEvent()
374 {
375  handleUngrab();
376 
378 }
379 
380 void MouseEventListener::touchUngrabEvent()
381 {
382  handleUngrab();
383 
385 }
386 
387 void MouseEventListener::handleUngrab()
388 {
389  if (m_pressed) {
390  m_pressAndHoldTimer->stop();
391 
392  m_pressed = false;
393  Q_EMIT pressedChanged();
394 
395  Q_EMIT canceled();
396  }
397 }
MouseButtonPress
Qt::MouseEventSource source() const const
QEvent::Type type() const const
QPointF mapToItem(const QQuickItem *item, const QPointF &point) const const
KJOBWIDGETS_EXPORT QWidget * window(KJob *job)
Qt::MouseButtons buttons() const const
QPointF position() const const
QPoint mapToGlobal(const QPoint &pos) const const
LeftButton
Qt::MouseButtons buttons() const const
int x() const const
int y() const const
virtual void mouseUngrabEvent()
virtual bool event(QEvent *e)
QStyleHints * styleHints()
qreal x() const const
qreal y() const const
QPoint globalPos() const const
void setAccepted(bool accepted)
void ignore()
const QPointF & screenPos() const const
Qt::MouseButton button() const const
CursorShape
const QPointF & windowPos() const const
Qt::KeyboardModifiers modifiers() const const
virtual bool childMouseEventFilter(QQuickItem *item, QEvent *event)
QList< QScreen * > screens()
This item spies on mouse events from all child objects including child MouseAreas regardless of wheth...
QPoint angleDelta() const const
QPoint toPoint() const const
QPoint pos() const const
MouseEventNotSynthesized
QPointF globalPosition() const const
Vertical
virtual void touchUngrabEvent()
QPoint pos() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
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.