All Classes Functions Properties
PressedOutsideNotifier.cpp
/*
* Copyright (C) 2013 Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PressedOutsideNotifier.h"
#include <QMouseEvent>
PressedOutsideNotifier::PressedOutsideNotifier(QQuickItem *parent)
: QQuickItem(parent)
{
connect(this, &QQuickItem::enabledChanged,
this, &PressedOutsideNotifier::setupOrTearDownEventFiltering);
m_signalEmissionTimer.setSingleShot(true);
m_signalEmissionTimer.setInterval(0); // times out on the next iteration of the event loop
connect(&m_signalEmissionTimer, &QTimer::timeout,
this, &PressedOutsideNotifier::pressedOutside);
}
bool PressedOutsideNotifier::eventFilter(QObject *watched, QEvent *event)
{
Q_UNUSED(watched);
Q_ASSERT(watched == m_filteredWindow);
// We are already going to emit pressedOutside() anyway, thus no need
// for new checks.
// This case takes place when a QTouchEvent comes in and isn't handled by any item,
// causing QQuickWindow to synthesize a QMouseEvent out of it, which would
// be filtered by us as well and count as a second press, which is wrong.
if (m_signalEmissionTimer.isActive()) {
return false;
}
switch (event->type()) {
case QEvent::MouseButtonPress: {
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
QPointF p = mapFromScene(mouseEvent->windowPos());
if (!contains(p)) {
m_signalEmissionTimer.start();
}
break;
}
case QEvent::TouchBegin:
processFilteredTouchBegin(static_cast<QTouchEvent*>(event));
default:
break;
}
// let the event be handled further
return false;
}
void PressedOutsideNotifier::itemChange(ItemChange change, const ItemChangeData &value)
{
if (change == QQuickItem::ItemSceneChange) {
setupOrTearDownEventFiltering();
}
QQuickItem::itemChange(change, value);
}
void PressedOutsideNotifier::setupOrTearDownEventFiltering()
{
if (isEnabled() && window()) {
setupEventFiltering();
} else if (m_filteredWindow) {
tearDownEventFiltering();
}
}
void PressedOutsideNotifier::setupEventFiltering()
{
QQuickWindow *currentWindow = window();
Q_ASSERT(currentWindow != nullptr);
if (currentWindow == m_filteredWindow)
return;
if (m_filteredWindow) {
m_filteredWindow->removeEventFilter(this);
}
currentWindow->installEventFilter(this);
m_filteredWindow = currentWindow;
}
void PressedOutsideNotifier::tearDownEventFiltering()
{
m_filteredWindow->removeEventFilter(this);
m_filteredWindow.clear();
}
void PressedOutsideNotifier::processFilteredTouchBegin(QTouchEvent *event)
{
const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
for (int i = 0; i < touchPoints.count(); ++i) {
const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
if (touchPoint.state() == Qt::TouchPointPressed) {
QPointF p = mapFromScene(touchPoint.pos());
if (!contains(p)) {
m_signalEmissionTimer.start();
return;
}
}
}
}