KDeclarative

mouseeventlistener.cpp
1/*
2 SPDX-FileCopyrightText: 2011 Marco Martin <notmart@gmail.com>
3 SPDX-FileCopyrightText: 2013 Sebastian Kügler <sebas@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "mouseeventlistener.h"
9
10#include <QDebug>
11#include <QEvent>
12#include <QGuiApplication>
13#include <QMouseEvent>
14#include <QQuickWindow>
15#include <QScreen>
16#include <QStyleHints>
17#include <QTimer>
18
19MouseEventListener::MouseEventListener(QQuickItem *parent)
20 : QQuickItem(parent)
21 , m_pressed(false)
22 , m_pressAndHoldEvent(nullptr)
23 , m_lastEvent(nullptr)
24 , m_acceptedButtons(Qt::LeftButton)
25{
26 m_pressAndHoldTimer = new QTimer(this);
27 m_pressAndHoldTimer->setSingleShot(true);
28 connect(m_pressAndHoldTimer, &QTimer::timeout, this, &MouseEventListener::handlePressAndHold);
29 setFiltersChildMouseEvents(true);
31}
32
33MouseEventListener::~MouseEventListener()
34{
35}
36
37Qt::MouseButtons MouseEventListener::acceptedButtons() const
38{
39 return m_acceptedButtons;
40}
41
42Qt::CursorShape MouseEventListener::cursorShape() const
43{
44 return cursor().shape();
45}
46
47void MouseEventListener::setCursorShape(Qt::CursorShape shape)
48{
49 if (cursor().shape() == shape) {
50 return;
51 }
52
53 setCursor(shape);
54
55 Q_EMIT cursorShapeChanged();
56}
57
58void MouseEventListener::setAcceptedButtons(Qt::MouseButtons buttons)
59{
60 if (buttons == m_acceptedButtons) {
61 return;
62 }
63
64 m_acceptedButtons = buttons;
65 Q_EMIT acceptedButtonsChanged();
66}
67
68void MouseEventListener::setHoverEnabled(bool enable)
69{
70 if (enable == acceptHoverEvents()) {
71 return;
72 }
73
75 Q_EMIT hoverEnabledChanged(enable);
76}
77
78bool MouseEventListener::hoverEnabled() const
79{
80 return acceptHoverEvents();
81}
82
83bool MouseEventListener::isPressed() const
84{
85 return m_pressed;
86}
87
88void MouseEventListener::hoverEnterEvent(QHoverEvent *event)
89{
90 Q_UNUSED(event);
91
92 m_containsMouse = true;
93 if (!m_childContainsMouse) {
94 Q_EMIT containsMouseChanged(true);
95 }
96}
97
98void MouseEventListener::hoverLeaveEvent(QHoverEvent *event)
99{
100 Q_UNUSED(event);
101
102 m_containsMouse = false;
103 if (!m_childContainsMouse) {
104 Q_EMIT containsMouseChanged(false);
105 }
106}
107
108void MouseEventListener::hoverMoveEvent(QHoverEvent *event)
109{
110 if (m_lastEvent == event) {
111 return;
112 }
113
114 QQuickWindow *w = window();
115 QPoint screenPos;
116 if (w) {
117 screenPos = w->mapToGlobal(event->position()).toPoint();
118 }
119
120 KDeclarativeMouseEvent dme(event->position().x(),
121 event->position().y(),
122 screenPos.x(),
123 screenPos.y(),
126 event->modifiers(),
127 nullptr,
129 Q_EMIT positionChanged(&dme);
130}
131
132bool MouseEventListener::containsMouse() const
133{
134 return m_containsMouse || m_childContainsMouse;
135}
136
137void MouseEventListener::mousePressEvent(QMouseEvent *me)
138{
139 if (m_lastEvent == me || !(me->buttons() & m_acceptedButtons)) {
140 me->setAccepted(false);
141 return;
142 }
143
144 // 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
145 // doesn't seem to be any good way to properly reset this.
146 // this msolution will still caused a missed click after the popup is gone, but gets the situation unblocked.
147 QPoint viewPosition;
148 if (window()) {
149 viewPosition = window()->position();
150 }
151
152 if (!QRectF(mapToScene(QPoint(0, 0)) + viewPosition, QSizeF(width(), height())).contains(me->globalPosition())) {
153 me->ignore();
154 return;
155 }
156 m_buttonDownPos = me->globalPosition();
157
158 KDeclarativeMouseEvent dme(me->pos().x(),
159 me->pos().y(),
160 me->globalPosition().x(),
161 me->globalPosition().y(),
162 me->button(),
163 me->buttons(),
164 me->modifiers(),
165 screenForGlobalPos(me->globalPosition()),
166 me->source());
167 if (!m_pressAndHoldEvent) {
168 m_pressAndHoldEvent = new KDeclarativeMouseEvent(me->pos().x(),
169 me->pos().y(),
170 me->globalPosition().x(),
171 me->globalPosition().y(),
172 me->button(),
173 me->buttons(),
174 me->modifiers(),
175 screenForGlobalPos(me->globalPosition()),
176 me->source());
177 }
178
179 m_pressed = true;
180 Q_EMIT pressed(&dme);
181 Q_EMIT pressedChanged();
182
183 if (dme.isAccepted()) {
184 me->setAccepted(true);
185 return;
186 }
187
188 m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval());
189}
190
191void MouseEventListener::mouseMoveEvent(QMouseEvent *me)
192{
193 if (m_lastEvent == me || !(me->buttons() & m_acceptedButtons)) {
194 me->setAccepted(false);
195 return;
196 }
197
199 && m_pressAndHoldTimer->isActive()) {
200 m_pressAndHoldTimer->stop();
201 }
202
203 KDeclarativeMouseEvent dme(me->pos().x(),
204 me->pos().y(),
205 me->globalPosition().x(),
206 me->globalPosition().y(),
207 me->button(),
208 me->buttons(),
209 me->modifiers(),
210 screenForGlobalPos(me->globalPosition()),
211 me->source());
212 Q_EMIT positionChanged(&dme);
213
214 if (dme.isAccepted()) {
215 me->setAccepted(true);
216 }
217}
218
219void MouseEventListener::mouseReleaseEvent(QMouseEvent *me)
220{
221 if (m_lastEvent == me) {
222 me->setAccepted(false);
223 return;
224 }
225
226 KDeclarativeMouseEvent dme(me->pos().x(),
227 me->pos().y(),
228 me->globalPosition().x(),
229 me->globalPosition().y(),
230 me->button(),
231 me->buttons(),
232 me->modifiers(),
233 screenForGlobalPos(me->globalPosition()),
234 me->source());
235 m_pressed = false;
236 Q_EMIT released(&dme);
237 Q_EMIT pressedChanged();
238
239 if (boundingRect().contains(me->pos()) && m_pressAndHoldTimer->isActive()) {
240 Q_EMIT clicked(&dme);
241 m_pressAndHoldTimer->stop();
242 }
243
244 if (dme.isAccepted()) {
245 me->setAccepted(true);
246 }
247}
248
249void MouseEventListener::wheelEvent(QWheelEvent *we)
250{
251 if (m_lastEvent == we) {
252 return;
253 }
254
255 KDeclarativeWheelEvent dwe(we->position().toPoint(),
256 we->globalPosition().toPoint(),
257 we->angleDelta(),
258 we->buttons(),
259 we->modifiers(),
260 Qt::Vertical /* HACK, deprecated, remove */);
261 Q_EMIT wheelMoved(&dwe);
262}
263
264void MouseEventListener::handlePressAndHold()
265{
266 if (m_pressed) {
267 Q_EMIT pressAndHold(m_pressAndHoldEvent);
268
269 delete m_pressAndHoldEvent;
270 m_pressAndHoldEvent = nullptr;
271 }
272}
273
274bool MouseEventListener::childMouseEventFilter(QQuickItem *item, QEvent *event)
275{
276 if (!isEnabled()) {
277 return false;
278 }
279
280 // don't filter other mouseeventlisteners
281 if (qobject_cast<MouseEventListener *>(item)) {
282 return false;
283 }
284
285 switch (event->type()) {
287 m_lastEvent = event;
288 QMouseEvent *me = static_cast<QMouseEvent *>(event);
289
290 if (!(me->buttons() & m_acceptedButtons)) {
291 break;
292 }
293
294 // the parent will receive events in its own coordinates
295 const QPointF myPos = mapFromScene(me->scenePosition());
296
297 KDeclarativeMouseEvent dme(myPos.x(),
298 myPos.y(),
299 me->globalPosition().x(),
300 me->globalPosition().y(),
301 me->button(),
302 me->buttons(),
303 me->modifiers(),
304 screenForGlobalPos(me->globalPosition()),
305 me->source());
306 delete m_pressAndHoldEvent;
307 m_pressAndHoldEvent = new KDeclarativeMouseEvent(myPos.x(),
308 myPos.y(),
309 me->globalPosition().x(),
310 me->globalPosition().y(),
311 me->button(),
312 me->buttons(),
313 me->modifiers(),
314 screenForGlobalPos(me->globalPosition()),
315 me->source());
316
317 // qDebug() << "pressed in sceneEventFilter";
318 m_buttonDownPos = me->globalPosition();
319 m_pressed = true;
320 Q_EMIT pressed(&dme);
321 Q_EMIT pressedChanged();
322
323 if (dme.isAccepted()) {
324 return true;
325 }
326
327 m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval());
328
329 break;
330 }
331 case QEvent::HoverMove: {
332 if (!acceptHoverEvents()) {
333 break;
334 }
335 m_lastEvent = event;
336 QHoverEvent *he = static_cast<QHoverEvent *>(event);
337 const QPointF myPos = item->mapToItem(this, he->position());
338
339 QQuickWindow *w = window();
340 QPoint screenPos;
341 if (w) {
342 screenPos = w->mapToGlobal(myPos.toPoint());
343 }
344
346 dme(myPos.x(), myPos.y(), screenPos.x(), screenPos.y(), Qt::NoButton, Qt::NoButton, he->modifiers(), nullptr, Qt::MouseEventNotSynthesized);
347 // qDebug() << "positionChanged..." << dme.x() << dme.y();
348 Q_EMIT positionChanged(&dme);
349
350 if (dme.isAccepted()) {
351 return true;
352 }
353 break;
354 }
355 case QEvent::MouseMove: {
356 m_lastEvent = event;
357 QMouseEvent *me = static_cast<QMouseEvent *>(event);
358 if (!(me->buttons() & m_acceptedButtons)) {
359 break;
360 }
361
362 const QPointF myPos = mapFromScene(me->scenePosition());
363 KDeclarativeMouseEvent dme(myPos.x(),
364 myPos.y(),
365 me->globalPosition().x(),
366 me->globalPosition().y(),
367 me->button(),
368 me->buttons(),
369 me->modifiers(),
370 screenForGlobalPos(me->globalPosition()),
371 me->source());
372 // qDebug() << "positionChanged..." << dme.x() << dme.y();
373
374 // stop the pressandhold if mouse moved enough
376 && m_pressAndHoldTimer->isActive()) {
377 m_pressAndHoldTimer->stop();
378
379 // if the mouse moves and we are waiting to emit a press and hold event, update the coordinates
380 // as there is no update function, delete the old event and create a new one
381 } else if (m_pressAndHoldEvent) {
382 delete m_pressAndHoldEvent;
383 m_pressAndHoldEvent = new KDeclarativeMouseEvent(myPos.x(),
384 myPos.y(),
385 me->globalPosition().x(),
386 me->globalPosition().y(),
387 me->button(),
388 me->buttons(),
389 me->modifiers(),
390 screenForGlobalPos(me->globalPosition()),
391 me->source());
392 }
393 Q_EMIT positionChanged(&dme);
394
395 if (dme.isAccepted()) {
396 return true;
397 }
398 break;
399 }
401 m_lastEvent = event;
402 QMouseEvent *me = static_cast<QMouseEvent *>(event);
403
404 const QPointF myPos = mapFromScene(me->scenePosition());
405 KDeclarativeMouseEvent dme(myPos.x(),
406 myPos.y(),
407 me->globalPosition().x(),
408 me->globalPosition().y(),
409 me->button(),
410 me->buttons(),
411 me->modifiers(),
412 screenForGlobalPos(me->globalPosition()),
413 me->source());
414 m_pressed = false;
415
416 Q_EMIT released(&dme);
417 Q_EMIT pressedChanged();
418
420 && m_pressAndHoldTimer->isActive()) {
421 Q_EMIT clicked(&dme);
422 m_pressAndHoldTimer->stop();
423 }
424
425 if (dme.isAccepted()) {
426 return true;
427 }
428 break;
429 }
430 case QEvent::UngrabMouse: {
431 m_lastEvent = event;
432 handleUngrab();
433 break;
434 }
435 case QEvent::Wheel: {
436 m_lastEvent = event;
437 QWheelEvent *we = static_cast<QWheelEvent *>(event);
438 KDeclarativeWheelEvent dwe(we->position().toPoint(),
439 we->globalPosition().toPoint(),
440 we->angleDelta(),
441 we->buttons(),
442 we->modifiers(),
443 Qt::Vertical /* HACK, deprecated, remove */);
444 Q_EMIT wheelMoved(&dwe);
445 break;
446 }
447 case QEvent::HoverEnter: {
448 m_childContainsMouse = true;
449 if (!m_containsMouse) {
450 Q_EMIT containsMouseChanged(true);
451 }
452 break;
453 }
454 case QEvent::HoverLeave: {
455 m_childContainsMouse = false;
456 if (!m_containsMouse) {
457 Q_EMIT containsMouseChanged(false);
458 }
459 break;
460 }
461 default:
462 break;
463 }
464
466 // return false;
467}
468
469QScreen *MouseEventListener::screenForGlobalPos(const QPointF &globalPos)
470{
471 const auto screens = QGuiApplication::screens();
472 for (QScreen *screen : screens) {
473 if (screen->geometry().contains(globalPos.toPoint())) {
474 return screen;
475 }
476 }
477 return nullptr;
478}
479
480void MouseEventListener::mouseUngrabEvent()
481{
482 handleUngrab();
483
485}
486
487void MouseEventListener::touchUngrabEvent()
488{
489 handleUngrab();
490
492}
493
494void MouseEventListener::handleUngrab()
495{
496 if (m_pressed) {
497 m_pressAndHoldTimer->stop();
498
499 m_pressed = false;
500 Q_EMIT pressedChanged();
501
502 Q_EMIT canceled();
503 }
504}
505
506#include "moc_mouseeventlistener.cpp"
This item spies on mouse events from all child objects including child MouseAreas regardless of wheth...
Qt::CursorShape shape() const const
MouseButtonPress
void ignore()
QList< QScreen * > screens()
QStyleHints * styleHints()
Qt::KeyboardModifiers modifiers() const const
QPoint pos() const const
Qt::MouseEventSource source() const const
Q_EMITQ_EMIT
int x() const const
int y() const const
virtual void setAccepted(bool accepted) override
qreal manhattanLength() const const
QPoint toPoint() const const
qreal x() const const
qreal y() const const
bool acceptHoverEvents() const const
virtual QRectF boundingRect() const const
virtual bool childMouseEventFilter(QQuickItem *item, QEvent *event)
virtual bool contains(const QPointF &point) const const
QCursor cursor() const const
bool isEnabled() const const
virtual bool event(QEvent *ev) override
QPointF mapFromScene(const QPointF &point) const const
QPointF mapToItem(const QQuickItem *item, const QPointF &point) const const
QPointF mapToScene(const QPointF &point) const const
virtual void mouseUngrabEvent()
void setAcceptHoverEvents(bool enabled)
void setCursor(const QCursor &cursor)
virtual void touchUngrabEvent()
QQuickWindow * window() const const
Qt::MouseButton button() const const
Qt::MouseButtons buttons() const const
QPointF globalPosition() const const
QPointF position() const const
QPointF scenePosition() const const
CursorShape
LeftButton
MouseEventNotSynthesized
Vertical
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isActive() const const
void start()
void stop()
void timeout()
QPoint angleDelta() const const
QPoint mapToGlobal(const QPoint &pos) const const
QPoint position() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:59 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.