diff --git a/src/qmlcontrols/kquickcontrolsaddons/mouseeventlistener.cpp b/src/qmlcontrols/kquickcontrolsaddons/mouseeventlistener.cpp index 3e5f65e..6409639 100644 --- a/src/qmlcontrols/kquickcontrolsaddons/mouseeventlistener.cpp +++ b/src/qmlcontrols/kquickcontrolsaddons/mouseeventlistener.cpp @@ -1,411 +1,411 @@ /* Copyright 2011 Marco Martin Copyright 2013 Sebastian Kügler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mouseeventlistener.h" #include #include #include #include #include #include #include #include MouseEventListener::MouseEventListener(QQuickItem *parent) : QQuickItem(parent), m_pressed(false), m_pressAndHoldEvent(nullptr), m_lastEvent(nullptr), m_containsMouse(false), m_acceptedButtons(Qt::LeftButton) { m_pressAndHoldTimer = new QTimer(this); m_pressAndHoldTimer->setSingleShot(true); connect(m_pressAndHoldTimer, SIGNAL(timeout()), this, SLOT(handlePressAndHold())); qmlRegisterType(); qmlRegisterType(); setFiltersChildMouseEvents(true); setAcceptedMouseButtons(Qt::LeftButton|Qt::RightButton|Qt::MidButton|Qt::XButton1|Qt::XButton2); } MouseEventListener::~MouseEventListener() { } Qt::MouseButtons MouseEventListener::acceptedButtons() const { return m_acceptedButtons; } Qt::CursorShape MouseEventListener::cursorShape() const { return cursor().shape(); } void MouseEventListener::setCursorShape(Qt::CursorShape shape) { if (cursor().shape() == shape) { return; } setCursor(shape); emit cursorShapeChanged(); } void MouseEventListener::setAcceptedButtons(Qt::MouseButtons buttons) { if (buttons == m_acceptedButtons) { return; } m_acceptedButtons = buttons; emit acceptedButtonsChanged(); } void MouseEventListener::setHoverEnabled(bool enable) { if (enable == acceptHoverEvents()) { return; } setAcceptHoverEvents(enable); emit hoverEnabledChanged(enable); } bool MouseEventListener::hoverEnabled() const { return acceptHoverEvents(); } bool MouseEventListener::isPressed() const { return m_pressed; } void MouseEventListener::hoverEnterEvent(QHoverEvent *event) { Q_UNUSED(event); m_containsMouse = true; emit containsMouseChanged(true); } void MouseEventListener::hoverLeaveEvent(QHoverEvent *event) { Q_UNUSED(event); m_containsMouse = false; emit containsMouseChanged(false); } void MouseEventListener::hoverMoveEvent(QHoverEvent * event) { if (m_lastEvent == event) { return; } QQuickWindow *w = window(); QPoint screenPos; if (w) { screenPos = w->mapToGlobal(event->pos()); } KDeclarativeMouseEvent dme(event->pos().x(), event->pos().y(), screenPos.x(), screenPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), nullptr); emit positionChanged(&dme); } bool MouseEventListener::containsMouse() const { return m_containsMouse; } void MouseEventListener::mousePressEvent(QMouseEvent *me) { if (m_lastEvent == me || !(me->buttons() & m_acceptedButtons)) { me->setAccepted(false); return; } //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 //doesn't seem to be any good way to properly reset this. //this msolution will still caused a missed click after the popup is gone, but gets the situation unblocked. QPoint viewPosition; if (window()) { viewPosition = window()->position(); } if (!QRectF(mapToScene(QPoint(0, 0)) + viewPosition, QSizeF(width(), height())).contains(me->screenPos())) { me->ignore(); return; } m_buttonDownPos = me->screenPos(); KDeclarativeMouseEvent dme(me->pos().x(), me->pos().y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos())); if (!m_pressAndHoldEvent) { 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())); } m_pressed = true; emit pressed(&dme); emit pressedChanged(); if (dme.isAccepted()) { me->setAccepted(true); return; } m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval()); } void MouseEventListener::mouseMoveEvent(QMouseEvent *me) { if (m_lastEvent == me || !(me->buttons() & m_acceptedButtons)) { me->setAccepted(false); return; } if (QPointF(me->screenPos() - m_buttonDownPos).manhattanLength() > QGuiApplication::styleHints()->startDragDistance() && m_pressAndHoldTimer->isActive()) { m_pressAndHoldTimer->stop(); } KDeclarativeMouseEvent dme(me->pos().x(), me->pos().y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos())); emit positionChanged(&dme); if (dme.isAccepted()) { me->setAccepted(true); } } void MouseEventListener::mouseReleaseEvent(QMouseEvent *me) { if (m_lastEvent == me) { me->setAccepted(false); return; } KDeclarativeMouseEvent dme(me->pos().x(), me->pos().y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos())); m_pressed = false; emit released(&dme); emit pressedChanged(); if (boundingRect().contains(me->pos()) && m_pressAndHoldTimer->isActive()) { emit clicked(&dme); m_pressAndHoldTimer->stop(); } if (dme.isAccepted()) { me->setAccepted(true); } } void MouseEventListener::wheelEvent(QWheelEvent *we) { if (m_lastEvent == we) { return; } - KDeclarativeWheelEvent dwe(we->pos(), we->globalPos(), we->delta(), we->buttons(), we->modifiers(), we->orientation()); + KDeclarativeWheelEvent dwe(we->pos(), we->globalPos(), we->angleDelta(), we->buttons(), we->modifiers(), we->orientation()); emit wheelMoved(&dwe); } void MouseEventListener::handlePressAndHold() { if (m_pressed) { emit pressAndHold(m_pressAndHoldEvent); delete m_pressAndHoldEvent; m_pressAndHoldEvent = nullptr; } } bool MouseEventListener::childMouseEventFilter(QQuickItem *item, QEvent *event) { if (!isEnabled()) { return false; } //don't filter other mouseeventlisteners if (qobject_cast(item)) { return false; } switch (event->type()) { case QEvent::MouseButtonPress: { m_lastEvent = event; QMouseEvent *me = static_cast(event); if (!(me->buttons() & m_acceptedButtons)) { break; } //the parent will receive events in its own coordinates const QPointF myPos = mapFromScene(me->windowPos()); KDeclarativeMouseEvent dme(myPos.x(), myPos.y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos())); delete m_pressAndHoldEvent; m_pressAndHoldEvent = new KDeclarativeMouseEvent(myPos.x(), myPos.y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos())); //qDebug() << "pressed in sceneEventFilter"; m_buttonDownPos = me->screenPos(); m_pressed = true; emit pressed(&dme); emit pressedChanged(); if (dme.isAccepted()) { return true; } m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval()); break; } case QEvent::HoverMove: { if (!acceptHoverEvents()) { break; } m_lastEvent = event; QHoverEvent *he = static_cast(event); const QPointF myPos = item->mapToItem(this, he->pos()); QQuickWindow *w = window(); QPoint screenPos; if (w) { screenPos = w->mapToGlobal(myPos.toPoint()); } KDeclarativeMouseEvent dme(myPos.x(), myPos.y(), screenPos.x(), screenPos.y(), Qt::NoButton, Qt::NoButton, he->modifiers(), nullptr); //qDebug() << "positionChanged..." << dme.x() << dme.y(); emit positionChanged(&dme); if (dme.isAccepted()) { return true; } break; } case QEvent::MouseMove: { m_lastEvent = event; QMouseEvent *me = static_cast(event); if (!(me->buttons() & m_acceptedButtons)) { break; } const QPointF myPos = mapFromScene(me->windowPos()); KDeclarativeMouseEvent dme(myPos.x(), myPos.y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos())); //qDebug() << "positionChanged..." << dme.x() << dme.y(); //stop the pressandhold if mouse moved enough if (QPointF(me->screenPos() - m_buttonDownPos).manhattanLength() > QGuiApplication::styleHints()->startDragDistance() && m_pressAndHoldTimer->isActive()) { m_pressAndHoldTimer->stop(); //if the mouse moves and we are waiting to emit a press and hold event, update the co-ordinates //as there is no update function, delete the old event and create a new one } else if (m_pressAndHoldEvent) { delete m_pressAndHoldEvent; m_pressAndHoldEvent = new KDeclarativeMouseEvent(myPos.x(), myPos.y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos())); } emit positionChanged(&dme); if (dme.isAccepted()) { return true; } break; } case QEvent::MouseButtonRelease: { m_lastEvent = event; QMouseEvent *me = static_cast(event); const QPointF myPos = mapFromScene(me->windowPos()); KDeclarativeMouseEvent dme(myPos.x(), myPos.y(), me->screenPos().x(), me->screenPos().y(), me->button(), me->buttons(), me->modifiers(), screenForGlobalPos(me->globalPos())); m_pressed = false; emit released(&dme); emit pressedChanged(); if (QPointF(me->screenPos() - m_buttonDownPos).manhattanLength() <= QGuiApplication::styleHints()->startDragDistance() && m_pressAndHoldTimer->isActive()) { emit clicked(&dme); m_pressAndHoldTimer->stop(); } if (dme.isAccepted()) { return true; } break; } case QEvent::UngrabMouse: { m_lastEvent = event; handleUngrab(); break; } case QEvent::Wheel: { m_lastEvent = event; QWheelEvent *we = static_cast(event); - KDeclarativeWheelEvent dwe(we->pos(), we->globalPos(), we->delta(), we->buttons(), we->modifiers(), we->orientation()); + KDeclarativeWheelEvent dwe(we->pos(), we->globalPos(), we->angleDelta(), we->buttons(), we->modifiers(), we->orientation()); emit wheelMoved(&dwe); break; } default: break; } return QQuickItem::childMouseEventFilter(item, event); // return false; } QScreen* MouseEventListener::screenForGlobalPos(const QPoint& globalPos) { const auto screens = QGuiApplication::screens(); for (QScreen *screen : screens) { if (screen->geometry().contains(globalPos)) { return screen; } } return nullptr; } void MouseEventListener::mouseUngrabEvent() { handleUngrab(); QQuickItem::mouseUngrabEvent(); } void MouseEventListener::touchUngrabEvent() { handleUngrab(); QQuickItem::touchUngrabEvent(); } void MouseEventListener::handleUngrab() { if (m_pressed) { m_pressAndHoldTimer->stop(); m_pressed = false; emit pressedChanged(); emit canceled(); } } diff --git a/src/qmlcontrols/kquickcontrolsaddons/mouseeventlistener.h b/src/qmlcontrols/kquickcontrolsaddons/mouseeventlistener.h index 9a4d974..4d58f2e 100644 --- a/src/qmlcontrols/kquickcontrolsaddons/mouseeventlistener.h +++ b/src/qmlcontrols/kquickcontrolsaddons/mouseeventlistener.h @@ -1,236 +1,239 @@ /* Copyright 2011 Marco Martin Copyright 2013 Sebastian Kügler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MOUSEEVENTLISTENER_H #define MOUSEEVENTLISTENER_H #include /** * This item spies on mouse events from all child objects including child MouseAreas regardless * of whether the child MouseArea propagates events. It does not accept the event. * * In addition unlike MouseArea events include the mouse position in global co-ordinates and provides * the screen the mouse is in. */ class KDeclarativeMouseEvent : public QObject { Q_OBJECT Q_PROPERTY(int x READ x) Q_PROPERTY(int y READ y) Q_PROPERTY(int screenX READ screenX) Q_PROPERTY(int screenY READ screenY) Q_PROPERTY(int button READ button) Q_PROPERTY(Qt::MouseButtons buttons READ buttons) Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers) Q_PROPERTY(QScreen* screen READ screen CONSTANT) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted NOTIFY acceptedChanged) public: KDeclarativeMouseEvent(int x, int y, int screenX, int screenY, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QScreen* screen) : m_x(x), m_y(y), m_screenX(screenX), m_screenY(screenY), m_button(button), m_buttons(buttons), m_modifiers(modifiers), m_screen(screen) {} int x() const { return m_x; } int y() const { return m_y; } int screenX() const { return m_screenX; } int screenY() const { return m_screenY; } int button() const { return m_button; } Qt::MouseButtons buttons() const { return m_buttons; } Qt::KeyboardModifiers modifiers() const { return m_modifiers; } QScreen* screen() const { return m_screen; } bool isAccepted() const { return m_accepted; } void setAccepted(bool accepted) { if (m_accepted != accepted) { m_accepted = accepted; emit acceptedChanged(); } } // only for internal usage void setX(int x) { m_x = x; } void setY(int y) { m_y = y; } Q_SIGNALS: void acceptedChanged(); private: int m_x; int m_y; int m_screenX; int m_screenY; Qt::MouseButton m_button; Qt::MouseButtons m_buttons; Qt::KeyboardModifiers m_modifiers; QScreen *m_screen; bool m_accepted = false; }; class KDeclarativeWheelEvent : public QObject { Q_OBJECT Q_PROPERTY(int x READ x CONSTANT) Q_PROPERTY(int y READ y CONSTANT) Q_PROPERTY(int screenX READ screenX CONSTANT) Q_PROPERTY(int screenY READ screenY CONSTANT) - Q_PROPERTY(int delta READ delta CONSTANT) + Q_PROPERTY(int deltaX READ deltaX CONSTANT) + Q_PROPERTY(int deltaY READ deltaY CONSTANT) + Q_PROPERTY(int delta READ deltaY CONSTANT) // deprecated in favor of deltaY. TODO KF6: remove Q_PROPERTY(Qt::MouseButtons buttons READ buttons CONSTANT) Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers CONSTANT) - Q_PROPERTY(Qt::Orientation orientation READ orientation CONSTANT) + Q_PROPERTY(Qt::Orientation orientation READ orientation CONSTANT) // deprecated. TODO KF6: remove public: - KDeclarativeWheelEvent(QPointF pos, QPoint screenPos, int delta, + KDeclarativeWheelEvent(QPointF pos, QPoint screenPos, QPoint angleDelta, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::Orientation orientation) : m_x(pos.x()), m_y(pos.y()), m_screenX(screenPos.x()), m_screenY(screenPos.y()), - m_delta(delta), + m_angleDelta(angleDelta), m_buttons(buttons), m_modifiers(modifiers), m_orientation(orientation) {} int x() const { return m_x; } int y() const { return m_y; } int screenX() const { return m_screenX; } int screenY() const { return m_screenY; } - int delta() const { return m_delta; } + int deltaX() const { return m_angleDelta.x(); } + int deltaY() const { return m_angleDelta.y(); } Qt::MouseButtons buttons() const { return m_buttons; } Qt::KeyboardModifiers modifiers() const { return m_modifiers; } - Qt::Orientation orientation() { return m_orientation; } + Qt::Orientation orientation() { return m_orientation; } // TODO KF6: remove // only for internal usage void setX(int x) { m_x = x; } void setY(int y) { m_y = y; } private: int m_x; int m_y; int m_screenX; int m_screenY; - int m_delta; + QPoint m_angleDelta; Qt::MouseButtons m_buttons; Qt::KeyboardModifiers m_modifiers; Qt::Orientation m_orientation; }; class MouseEventListener : public QQuickItem { Q_OBJECT /** * This property holds whether hover events are handled. * By default hover events are disabled */ Q_PROPERTY(bool hoverEnabled READ hoverEnabled WRITE setHoverEnabled NOTIFY hoverEnabledChanged) /** * True if this MouseEventListener or any of its children contains the mouse cursor: this property will change only when the mouse button is pressed if hoverEnabled is false */ Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged) Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged) /** * This property holds the cursor shape for this mouse area. * Note that on platforms that do not display a mouse cursor this may have no effect. */ Q_PROPERTY(Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape RESET unsetCursor NOTIFY cursorShapeChanged) /** * True if the mouse is pressed in the item or any of its children */ Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged) public: MouseEventListener(QQuickItem *parent=nullptr); ~MouseEventListener() override; bool containsMouse() const; void setHoverEnabled(bool enable); bool hoverEnabled() const; bool isPressed() const; Qt::MouseButtons acceptedButtons() const; void setAcceptedButtons(Qt::MouseButtons buttons); Qt::CursorShape cursorShape() const; void setCursorShape(Qt::CursorShape shape); protected: void hoverEnterEvent(QHoverEvent *event) override; void hoverLeaveEvent(QHoverEvent *event) override; void hoverMoveEvent(QHoverEvent * event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; void mouseUngrabEvent() override; void touchUngrabEvent() override; Q_SIGNALS: void pressed(KDeclarativeMouseEvent *mouse); void positionChanged(KDeclarativeMouseEvent *mouse); void released(KDeclarativeMouseEvent *mouse); void clicked(KDeclarativeMouseEvent *mouse); void pressAndHold(KDeclarativeMouseEvent *mouse); void wheelMoved(KDeclarativeWheelEvent *wheel); void containsMouseChanged(bool containsMouseChanged); void hoverEnabledChanged(bool hoverEnabled); void acceptedButtonsChanged(); void cursorShapeChanged(); void pressedChanged(); void canceled(); private Q_SLOTS: void handlePressAndHold(); void handleUngrab(); private: static QScreen* screenForGlobalPos(const QPoint &globalPos); bool m_pressed; KDeclarativeMouseEvent* m_pressAndHoldEvent; QPointF m_buttonDownPos; //Important: used only for comparison. If you will ever need to access this pointer, make it a QWeakPointer QEvent *m_lastEvent; QTimer *m_pressAndHoldTimer; bool m_containsMouse; Qt::MouseButtons m_acceptedButtons; }; #endif