File indexing completed on 2024-05-19 15:09:24
0001 /* 0002 SPDX-FileCopyrightText: 2011 Marco Martin <notmart@gmail.com> 0003 SPDX-FileCopyrightText: 2013 Sebastian Kügler <sebas@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "mouseeventlistener.h" 0009 0010 #include <QDebug> 0011 #include <QEvent> 0012 #include <QGuiApplication> 0013 #include <QMouseEvent> 0014 #include <QQuickWindow> 0015 #include <QScreen> 0016 #include <QStyleHints> 0017 #include <QTimer> 0018 0019 MouseEventListener::MouseEventListener(QQuickItem *parent) 0020 : QQuickItem(parent) 0021 , m_pressed(false) 0022 , m_pressAndHoldEvent(nullptr) 0023 , m_lastEvent(nullptr) 0024 , m_containsMouse(false) 0025 , m_acceptedButtons(Qt::LeftButton) 0026 { 0027 m_pressAndHoldTimer = new QTimer(this); 0028 m_pressAndHoldTimer->setSingleShot(true); 0029 connect(m_pressAndHoldTimer, &QTimer::timeout, this, &MouseEventListener::handlePressAndHold); 0030 setFiltersChildMouseEvents(true); 0031 setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton | Qt::XButton1 | Qt::XButton2); 0032 } 0033 0034 MouseEventListener::~MouseEventListener() 0035 { 0036 } 0037 0038 Qt::MouseButtons MouseEventListener::acceptedButtons() const 0039 { 0040 return m_acceptedButtons; 0041 } 0042 0043 Qt::CursorShape MouseEventListener::cursorShape() const 0044 { 0045 return cursor().shape(); 0046 } 0047 0048 void MouseEventListener::setCursorShape(Qt::CursorShape shape) 0049 { 0050 if (cursor().shape() == shape) { 0051 return; 0052 } 0053 0054 setCursor(shape); 0055 0056 Q_EMIT cursorShapeChanged(); 0057 } 0058 0059 void MouseEventListener::setAcceptedButtons(Qt::MouseButtons buttons) 0060 { 0061 if (buttons == m_acceptedButtons) { 0062 return; 0063 } 0064 0065 m_acceptedButtons = buttons; 0066 Q_EMIT acceptedButtonsChanged(); 0067 } 0068 0069 void MouseEventListener::setHoverEnabled(bool enable) 0070 { 0071 if (enable == acceptHoverEvents()) { 0072 return; 0073 } 0074 0075 setAcceptHoverEvents(enable); 0076 Q_EMIT hoverEnabledChanged(enable); 0077 } 0078 0079 bool MouseEventListener::hoverEnabled() const 0080 { 0081 return acceptHoverEvents(); 0082 } 0083 0084 bool MouseEventListener::isPressed() const 0085 { 0086 return m_pressed; 0087 } 0088 0089 void MouseEventListener::hoverEnterEvent(QHoverEvent *event) 0090 { 0091 Q_UNUSED(event); 0092 0093 m_containsMouse = true; 0094 Q_EMIT containsMouseChanged(true); 0095 } 0096 0097 void MouseEventListener::hoverLeaveEvent(QHoverEvent *event) 0098 { 0099 Q_UNUSED(event); 0100 0101 m_containsMouse = false; 0102 Q_EMIT containsMouseChanged(false); 0103 } 0104 0105 void MouseEventListener::hoverMoveEvent(QHoverEvent *event) 0106 { 0107 if (m_lastEvent == event) { 0108 return; 0109 } 0110 0111 QQuickWindow *w = window(); 0112 QPoint screenPos; 0113 if (w) { 0114 screenPos = w->mapToGlobal(event->pos()); 0115 } 0116 0117 KDeclarativeMouseEvent dme(event->pos().x(), 0118 event->pos().y(), 0119 screenPos.x(), 0120 screenPos.y(), 0121 Qt::NoButton, 0122 Qt::NoButton, 0123 event->modifiers(), 0124 nullptr, 0125 Qt::MouseEventNotSynthesized); 0126 Q_EMIT positionChanged(&dme); 0127 } 0128 0129 bool MouseEventListener::containsMouse() const 0130 { 0131 return m_containsMouse; 0132 } 0133 0134 void MouseEventListener::mousePressEvent(QMouseEvent *me) 0135 { 0136 if (m_lastEvent == me || !(me->buttons() & m_acceptedButtons)) { 0137 me->setAccepted(false); 0138 return; 0139 } 0140 0141 // 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 0142 // doesn't seem to be any good way to properly reset this. 0143 // this msolution will still caused a missed click after the popup is gone, but gets the situation unblocked. 0144 QPoint viewPosition; 0145 if (window()) { 0146 viewPosition = window()->position(); 0147 } 0148 0149 if (!QRectF(mapToScene(QPoint(0, 0)) + viewPosition, QSizeF(width(), height())).contains(me->screenPos())) { 0150 me->ignore(); 0151 return; 0152 } 0153 m_buttonDownPos = me->screenPos(); 0154 0155 KDeclarativeMouseEvent dme(me->pos().x(), 0156 me->pos().y(), 0157 me->screenPos().x(), 0158 me->screenPos().y(), 0159 me->button(), 0160 me->buttons(), 0161 me->modifiers(), 0162 screenForGlobalPos(me->globalPos()), 0163 me->source()); 0164 if (!m_pressAndHoldEvent) { 0165 m_pressAndHoldEvent = new KDeclarativeMouseEvent(me->pos().x(), 0166 me->pos().y(), 0167 me->screenPos().x(), 0168 me->screenPos().y(), 0169 me->button(), 0170 me->buttons(), 0171 me->modifiers(), 0172 screenForGlobalPos(me->globalPos()), 0173 me->source()); 0174 } 0175 0176 m_pressed = true; 0177 Q_EMIT pressed(&dme); 0178 Q_EMIT pressedChanged(); 0179 0180 if (dme.isAccepted()) { 0181 me->setAccepted(true); 0182 return; 0183 } 0184 0185 m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval()); 0186 } 0187 0188 void MouseEventListener::mouseMoveEvent(QMouseEvent *me) 0189 { 0190 if (m_lastEvent == me || !(me->buttons() & m_acceptedButtons)) { 0191 me->setAccepted(false); 0192 return; 0193 } 0194 0195 if (QPointF(me->screenPos() - m_buttonDownPos).manhattanLength() > QGuiApplication::styleHints()->startDragDistance() && m_pressAndHoldTimer->isActive()) { 0196 m_pressAndHoldTimer->stop(); 0197 } 0198 0199 KDeclarativeMouseEvent dme(me->pos().x(), 0200 me->pos().y(), 0201 me->screenPos().x(), 0202 me->screenPos().y(), 0203 me->button(), 0204 me->buttons(), 0205 me->modifiers(), 0206 screenForGlobalPos(me->globalPos()), 0207 me->source()); 0208 Q_EMIT positionChanged(&dme); 0209 0210 if (dme.isAccepted()) { 0211 me->setAccepted(true); 0212 } 0213 } 0214 0215 void MouseEventListener::mouseReleaseEvent(QMouseEvent *me) 0216 { 0217 if (m_lastEvent == me) { 0218 me->setAccepted(false); 0219 return; 0220 } 0221 0222 KDeclarativeMouseEvent dme(me->pos().x(), 0223 me->pos().y(), 0224 me->screenPos().x(), 0225 me->screenPos().y(), 0226 me->button(), 0227 me->buttons(), 0228 me->modifiers(), 0229 screenForGlobalPos(me->globalPos()), 0230 me->source()); 0231 m_pressed = false; 0232 Q_EMIT released(&dme); 0233 Q_EMIT pressedChanged(); 0234 0235 if (boundingRect().contains(me->pos()) && m_pressAndHoldTimer->isActive()) { 0236 Q_EMIT clicked(&dme); 0237 m_pressAndHoldTimer->stop(); 0238 } 0239 0240 if (dme.isAccepted()) { 0241 me->setAccepted(true); 0242 } 0243 } 0244 0245 void MouseEventListener::wheelEvent(QWheelEvent *we) 0246 { 0247 if (m_lastEvent == we) { 0248 return; 0249 } 0250 0251 KDeclarativeWheelEvent dwe(we->position().toPoint(), 0252 we->globalPosition().toPoint(), 0253 we->angleDelta(), 0254 we->buttons(), 0255 we->modifiers(), 0256 Qt::Vertical /* HACK, deprecated, remove */); 0257 Q_EMIT wheelMoved(&dwe); 0258 } 0259 0260 void MouseEventListener::handlePressAndHold() 0261 { 0262 if (m_pressed) { 0263 Q_EMIT pressAndHold(m_pressAndHoldEvent); 0264 0265 delete m_pressAndHoldEvent; 0266 m_pressAndHoldEvent = nullptr; 0267 } 0268 } 0269 0270 bool MouseEventListener::childMouseEventFilter(QQuickItem *item, QEvent *event) 0271 { 0272 if (!isEnabled()) { 0273 return false; 0274 } 0275 0276 // don't filter other mouseeventlisteners 0277 if (qobject_cast<MouseEventListener *>(item)) { 0278 return false; 0279 } 0280 0281 switch (event->type()) { 0282 case QEvent::MouseButtonPress: { 0283 m_lastEvent = event; 0284 QMouseEvent *me = static_cast<QMouseEvent *>(event); 0285 0286 if (!(me->buttons() & m_acceptedButtons)) { 0287 break; 0288 } 0289 0290 // the parent will receive events in its own coordinates 0291 const QPointF myPos = mapFromScene(me->windowPos()); 0292 0293 KDeclarativeMouseEvent dme(myPos.x(), 0294 myPos.y(), 0295 me->screenPos().x(), 0296 me->screenPos().y(), 0297 me->button(), 0298 me->buttons(), 0299 me->modifiers(), 0300 screenForGlobalPos(me->globalPos()), 0301 me->source()); 0302 delete m_pressAndHoldEvent; 0303 m_pressAndHoldEvent = new KDeclarativeMouseEvent(myPos.x(), 0304 myPos.y(), 0305 me->screenPos().x(), 0306 me->screenPos().y(), 0307 me->button(), 0308 me->buttons(), 0309 me->modifiers(), 0310 screenForGlobalPos(me->globalPos()), 0311 me->source()); 0312 0313 // qDebug() << "pressed in sceneEventFilter"; 0314 m_buttonDownPos = me->screenPos(); 0315 m_pressed = true; 0316 Q_EMIT pressed(&dme); 0317 Q_EMIT pressedChanged(); 0318 0319 if (dme.isAccepted()) { 0320 return true; 0321 } 0322 0323 m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval()); 0324 0325 break; 0326 } 0327 case QEvent::HoverMove: { 0328 if (!acceptHoverEvents()) { 0329 break; 0330 } 0331 m_lastEvent = event; 0332 QHoverEvent *he = static_cast<QHoverEvent *>(event); 0333 const QPointF myPos = item->mapToItem(this, he->pos()); 0334 0335 QQuickWindow *w = window(); 0336 QPoint screenPos; 0337 if (w) { 0338 screenPos = w->mapToGlobal(myPos.toPoint()); 0339 } 0340 0341 KDeclarativeMouseEvent 0342 dme(myPos.x(), myPos.y(), screenPos.x(), screenPos.y(), Qt::NoButton, Qt::NoButton, he->modifiers(), nullptr, Qt::MouseEventNotSynthesized); 0343 // qDebug() << "positionChanged..." << dme.x() << dme.y(); 0344 Q_EMIT positionChanged(&dme); 0345 0346 if (dme.isAccepted()) { 0347 return true; 0348 } 0349 break; 0350 } 0351 case QEvent::MouseMove: { 0352 m_lastEvent = event; 0353 QMouseEvent *me = static_cast<QMouseEvent *>(event); 0354 if (!(me->buttons() & m_acceptedButtons)) { 0355 break; 0356 } 0357 0358 const QPointF myPos = mapFromScene(me->windowPos()); 0359 KDeclarativeMouseEvent dme(myPos.x(), 0360 myPos.y(), 0361 me->screenPos().x(), 0362 me->screenPos().y(), 0363 me->button(), 0364 me->buttons(), 0365 me->modifiers(), 0366 screenForGlobalPos(me->globalPos()), 0367 me->source()); 0368 // qDebug() << "positionChanged..." << dme.x() << dme.y(); 0369 0370 // stop the pressandhold if mouse moved enough 0371 if (QPointF(me->screenPos() - m_buttonDownPos).manhattanLength() > QGuiApplication::styleHints()->startDragDistance() 0372 && m_pressAndHoldTimer->isActive()) { 0373 m_pressAndHoldTimer->stop(); 0374 0375 // if the mouse moves and we are waiting to emit a press and hold event, update the coordinates 0376 // as there is no update function, delete the old event and create a new one 0377 } else if (m_pressAndHoldEvent) { 0378 delete m_pressAndHoldEvent; 0379 m_pressAndHoldEvent = new KDeclarativeMouseEvent(myPos.x(), 0380 myPos.y(), 0381 me->screenPos().x(), 0382 me->screenPos().y(), 0383 me->button(), 0384 me->buttons(), 0385 me->modifiers(), 0386 screenForGlobalPos(me->globalPos()), 0387 me->source()); 0388 } 0389 Q_EMIT positionChanged(&dme); 0390 0391 if (dme.isAccepted()) { 0392 return true; 0393 } 0394 break; 0395 } 0396 case QEvent::MouseButtonRelease: { 0397 m_lastEvent = event; 0398 QMouseEvent *me = static_cast<QMouseEvent *>(event); 0399 0400 const QPointF myPos = mapFromScene(me->windowPos()); 0401 KDeclarativeMouseEvent dme(myPos.x(), 0402 myPos.y(), 0403 me->screenPos().x(), 0404 me->screenPos().y(), 0405 me->button(), 0406 me->buttons(), 0407 me->modifiers(), 0408 screenForGlobalPos(me->globalPos()), 0409 me->source()); 0410 m_pressed = false; 0411 0412 Q_EMIT released(&dme); 0413 Q_EMIT pressedChanged(); 0414 0415 if (QPointF(me->screenPos() - m_buttonDownPos).manhattanLength() <= QGuiApplication::styleHints()->startDragDistance() 0416 && m_pressAndHoldTimer->isActive()) { 0417 Q_EMIT clicked(&dme); 0418 m_pressAndHoldTimer->stop(); 0419 } 0420 0421 if (dme.isAccepted()) { 0422 return true; 0423 } 0424 break; 0425 } 0426 case QEvent::UngrabMouse: { 0427 m_lastEvent = event; 0428 handleUngrab(); 0429 break; 0430 } 0431 case QEvent::Wheel: { 0432 m_lastEvent = event; 0433 QWheelEvent *we = static_cast<QWheelEvent *>(event); 0434 KDeclarativeWheelEvent dwe(we->position().toPoint(), 0435 we->globalPosition().toPoint(), 0436 we->angleDelta(), 0437 we->buttons(), 0438 we->modifiers(), 0439 Qt::Vertical /* HACK, deprecated, remove */); 0440 Q_EMIT wheelMoved(&dwe); 0441 break; 0442 } 0443 default: 0444 break; 0445 } 0446 0447 return QQuickItem::childMouseEventFilter(item, event); 0448 // return false; 0449 } 0450 0451 QScreen *MouseEventListener::screenForGlobalPos(const QPoint &globalPos) 0452 { 0453 const auto screens = QGuiApplication::screens(); 0454 for (QScreen *screen : screens) { 0455 if (screen->geometry().contains(globalPos)) { 0456 return screen; 0457 } 0458 } 0459 return nullptr; 0460 } 0461 0462 void MouseEventListener::mouseUngrabEvent() 0463 { 0464 handleUngrab(); 0465 0466 QQuickItem::mouseUngrabEvent(); 0467 } 0468 0469 void MouseEventListener::touchUngrabEvent() 0470 { 0471 handleUngrab(); 0472 0473 QQuickItem::touchUngrabEvent(); 0474 } 0475 0476 void MouseEventListener::handleUngrab() 0477 { 0478 if (m_pressed) { 0479 m_pressAndHoldTimer->stop(); 0480 0481 m_pressed = false; 0482 Q_EMIT pressedChanged(); 0483 0484 Q_EMIT canceled(); 0485 } 0486 } 0487 0488 #include "moc_mouseeventlistener.cpp"