/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Quick Extras module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qquickmousethief_p.h" #include #include QQuickMouseThief::QQuickMouseThief(QObject *parent) : QObject(parent), mItem(0), mReceivedPressEvent(false), mAcceptCurrentEvent(false) { } bool QQuickMouseThief::receivedPressEvent() const { return mReceivedPressEvent; } void QQuickMouseThief::setReceivedPressEvent(bool receivedPressEvent) { if (receivedPressEvent != mReceivedPressEvent) { mReceivedPressEvent = receivedPressEvent; emit receivedPressEventChanged(); } } void QQuickMouseThief::grabMouse(QQuickItem *item) { if (item) { mItem = item; // Handle the case where someone makes the PieMenu an orphan. E.g.: // property PieMenu pieMenu: PieMenu {} // They have to set the parent explicitly if they want that to work. // This can also be null if the menu is visible when constructed. if (mItem->window()) { // We install an event filter so that we can track release events. // This is because the MouseArea that we steal the mouse from stores the // pressed flag (before item is visible) and we don't, so we don't know // that it's a release. mItem->grabMouse(); mItem->window()->installEventFilter(this); } else { // The menu was visible when constructed (visible: true), so install the // event filter later on. connect(mItem, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(itemWindowChanged(QQuickWindow*))); } } } void QQuickMouseThief::ungrabMouse() { if (mItem) { // This can be null if someone does the following: // PieMenu { Component.onCompleted: { visible = true; visible = false; } if (mItem->window()) { if (mItem->window()->mouseGrabberItem() == mItem) { mItem->ungrabMouse(); } mItem->window()->removeEventFilter(this); } mItem = 0; } } void QQuickMouseThief::acceptCurrentEvent() { mAcceptCurrentEvent = true; } void QQuickMouseThief::itemWindowChanged(QQuickWindow *window) { // The window will be null when the application is closing. if (window) { // This can be null if someone does the following: // PieMenu { Component.onCompleted: { visible = true; visible = false; } if (mItem) { mItem->grabMouse(); window->installEventFilter(this); } } } bool QQuickMouseThief::eventFilter(QObject *, QEvent *event) { if (!mItem) return false; // The PieMenu QML code dictates which events are accepted by // setting this property when it responds to our signals. mAcceptCurrentEvent = false; if (event->type() == QEvent::MouseButtonRelease) { const QPointF mouseWindowPos = static_cast(event)->windowPos(); emitReleased(mouseWindowPos); // Even if the release should close the menu, we should emit // clicked to be consistent since we have to do it ourselves. bool releaseAccepted = mAcceptCurrentEvent; mAcceptCurrentEvent = false; emitClicked(mouseWindowPos); if (!mAcceptCurrentEvent) { // We might not have accepted click, but we may have accepted release. mAcceptCurrentEvent = releaseAccepted; } } else if (event->type() == QEvent::MouseButtonPress) { emitPressed(static_cast(event)->windowPos()); } else if (event->type() == QEvent::TouchEnd) { // The finger(s) were lifted off the screen. We don't get this as a release event since // we're doing our own custom handling, so we handle it here. QTouchEvent *touchEvent = static_cast(event); const QList points = touchEvent->touchPoints(); // We only care about one finger. If there's more than one, ignore them. if (!points.isEmpty()) { const QPointF pos = points.first().pos(); emitReleased(pos); // Even if the release should close the menu, we should emit // clicked to be consistent since we have to do it ourselves. bool releaseAccepted = mAcceptCurrentEvent; mAcceptCurrentEvent = false; emitClicked(pos); if (!mAcceptCurrentEvent) { // We might not have accepted click, but we may have accepted release. mAcceptCurrentEvent = releaseAccepted; } } } else if (event->type() == QEvent::TouchBegin) { QTouchEvent *touchEvent = static_cast(event); const QList points = touchEvent->touchPoints(); if (!points.isEmpty()) { emitPressed(points.first().pos()); } } else if (event->type() == QEvent::TouchUpdate) { QTouchEvent *touchEvent = static_cast(event); const QList points = touchEvent->touchPoints(); if (!points.isEmpty()) { const QPointF mappedPos = mItem->mapFromScene(points.first().pos()); emit touchUpdate(mappedPos.x(), mappedPos.y()); } } return mAcceptCurrentEvent; } void QQuickMouseThief::emitPressed(const QPointF &pos) { setReceivedPressEvent(true); // PieMenu can't detect presses outside its own MouseArea, // so we need to provide these as well. QPointF mappedPos = mItem->mapFromScene(pos); emit pressed(mappedPos.x(), mappedPos.y()); } void QQuickMouseThief::emitReleased(const QPointF &pos) { QPointF mappedPos = mItem->mapFromScene(pos); emit released(mappedPos.x(), mappedPos.y()); } void QQuickMouseThief::emitClicked(const QPointF &pos) { // mItem can be destroyed if the slots connected to released() caused it to be hidden. // That's fine for us, since PieMenu doesn't need to handle anything if released() // is emitted when the triggerMode is PieMenu.TriggerOnRelease. if (mItem) { QPointF mappedPos = mItem->mapFromScene(pos); // Since we are creating the release events, we must also handle clicks. emit clicked(mappedPos.x(), mappedPos.y()); } }