diff options
author | Volker Hilsheimer <[email protected]> | 2020-11-05 18:11:56 +0100 |
---|---|---|
committer | Volker Hilsheimer <[email protected]> | 2020-11-07 06:24:28 +0100 |
commit | bdec189ecbc2cabbfa803a571b49533f190f053d (patch) | |
tree | a061307e207cfbdb989e463c9535208a822fab78 /src/gui/kernel/qeventpoint.cpp | |
parent | 1c6d6cbb62c5e93cbcad2d740c3b0ed01095618c (diff) |
Move QEventPoint and QPointingDeviceUniqueId out of qevent
qevent.h/cpp are huge already, no need for more classes. Move QEventPoint
into new qeventpoint.h/cpp files, and QPointingDeviceUniqueId into
qpointingdevice.cpp; the class is already declared in qpointingdevice.h.
Move the documentation of QEventPoint APIs next to the implementation,
and document all APIs as properties. Add Q_PROPERTY macro where missing.
QEventPoint::device needs a workaround of qdoc due to the type being a
pointer-to-const; qdoc doesn't know how to tie a \property to it, but
documents it correctly.
While at it, move the logging category declarations to the header
matching the .cpp file where they are defined.
Change-Id: I096e609edbb760b5686d577e7fe47eea0807904e
Reviewed-by: Shawn Rutledge <[email protected]>
Diffstat (limited to 'src/gui/kernel/qeventpoint.cpp')
-rw-r--r-- | src/gui/kernel/qeventpoint.cpp | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/src/gui/kernel/qeventpoint.cpp b/src/gui/kernel/qeventpoint.cpp new file mode 100644 index 00000000000..e8a366885fe --- /dev/null +++ b/src/gui/kernel/qeventpoint.cpp @@ -0,0 +1,664 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui 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 "qeventpoint.h" +#include "private/qeventpoint_p.h" +#include "private/qpointingdevice_p.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcPointerVel, "qt.pointer.velocity") +Q_LOGGING_CATEGORY(lcEPDetach, "qt.pointer.eventpoint.detach") + +/*! \class QEventPoint + \brief The QEventPoint class provides information about a point in a QPointerEvent. + \since 6.0 + \inmodule QtGui +*/ + +/*! + \enum QEventPoint::State + + Specifies the state of this event point. + + \value Unknown + Unknown state. + + \value Stationary + The event point did not move. + + \value Pressed + The touch point or button is pressed. + + \value Updated + The event point was updated. + + \value Released + The touch point or button was released. +*/ + +/*! + \internal + Constructs an invalid event point with the given \a id and the \a device + from which it originated. + + This acts as a default constructor in usages like QMap<int, QEventPoint>, + as in qgraphicsscene_p.h. +*/ +QEventPoint::QEventPoint(int id, const QPointingDevice *device) + : d(new QEventPointPrivate(id, device)) {} + +/*! + Constructs an event point with the given \a pointId, \a state, + \a scenePosition and \a globalPosition. +*/ +QEventPoint::QEventPoint(int pointId, State state, const QPointF &scenePosition, const QPointF &globalPosition) + : d(new QEventPointPrivate(pointId, state, scenePosition, globalPosition)) {} + +/*! + Constructs an event point by making a shallow copy of \a other. +*/ +QEventPoint::QEventPoint(const QEventPoint &other) + : d(other.d) +{ + if (d) + d->refCount++; +} + +/*! + Assigns \a other to this event point and returns a reference to this + event point. +*/ +QEventPoint &QEventPoint::operator=(const QEventPoint &other) +{ + if (other.d) + other.d->refCount++; + if (d && !(--d->refCount)) + delete d; + d = other.d; + return *this; +} + +/*! + \fn QEventPoint::QEventPoint(QEventPoint &&other) noexcept + + Constructs an event point by moving \a other. +*/ + +/*! + \fn QEventPoint &QEventPoint::operator=(QEventPoint &&other) noexcept + + Move-assigns \a other to this event point instance. +*/ + +/*! + Returns \c true if this event point is equal to \a other, otherwise + return \c false. +*/ +bool QEventPoint::operator==(const QEventPoint &other) const noexcept +{ + if (d == other.d) + return true; + if (!d || !other.d) + return false; + return *d == *other.d; +} + +/*! + \fn bool QEventPoint::operator!=(const QEventPoint &other) const noexcept + + Returns \c true if this event point is not equal to \a other, otherwise + return \c false. +*/ + +/*! + Destroys the event point. +*/ +QEventPoint::~QEventPoint() +{ + if (d && !(--d->refCount)) + delete d; +} + +/*! \fn QPointF QEventPoint::pos() const + \obsolete + Deprecated since Qt 6.0. Use position() instead. + + Returns the position of this point, relative to the widget + or item that received the event. +*/ + +/*! + \property QEventPoint::position + the position of this point + + The position is relative to the widget or item that received the event. +*/ +QPointF QEventPoint::position() const +{ return d->pos; } + +/*! + \property QEventPoint::pressPosition + the position at which this point was pressed + + The position is relative to the widget or item that received the event. + + \sa position +*/ +QPointF QEventPoint::pressPosition() const +{ return d->globalPressPos - d->globalPos + d->pos; } + +/*! + \property QEventPoint::grabPosition + the position at which this point was grabbed + + The position is relative to the widget or item that received the event. + + \sa position +*/ +QPointF QEventPoint::grabPosition() const +{ return d->globalGrabPos - d->globalPos + d->pos; } + +/*! + \property QEventPoint::lastPosition + the position of this point from the previous press or move event + + The position is relative to the widget or item that received the event. + + \sa position, pressPosition +*/ +QPointF QEventPoint::lastPosition() const +{ return d->globalLastPos - d->globalPos + d->pos; } + +/*! + \property QEventPoint::scenePosition + the scene position of this point + + The scene position is the position relative to QQuickWindow if handled in QQuickItem::event(), + in QGraphicsScene coordinates if handled by an override of QGraphicsItem::touchEvent(), + or the window position in widget applications. + + \sa scenePressPosition, position, globalPosition +*/ +QPointF QEventPoint::scenePosition() const +{ return d->scenePos; } + +/*! + \property QEventPoint::scenePressPosition + the scene position at which this point was pressed + + The scene position is the position relative to QQuickWindow if handled in QQuickItem::event(), + in QGraphicsScene coordinates if handled by an override of QGraphicsItem::touchEvent(), + or the window position in widget applications. + + \sa scenePosition, pressPosition, globalPressPosition +*/ +QPointF QEventPoint::scenePressPosition() const +{ return d->globalPressPos - d->globalPos + d->scenePos; } + +/*! + \property QEventPoint::sceneGrabPosition + the scene position at which this point was grabbed + + The scene position is the position relative to QQuickWindow if handled in QQuickItem::event(), + in QGraphicsScene coordinates if handled by an override of QGraphicsItem::touchEvent(), + or the window position in widget applications. + + \sa scenePosition, grabPosition, globalGrabPosition +*/ +QPointF QEventPoint::sceneGrabPosition() const +{ return d->globalGrabPos - d->globalPos + d->scenePos; } + +/*! + \property QEventPoint::sceneLastPosition + the scene position of this point from the previous press or move event + + The scene position is the position relative to QQuickWindow if handled in QQuickItem::event(), + in QGraphicsScene coordinates if handled by an override of QGraphicsItem::touchEvent(), + or the window position in widget applications. + + \sa scenePosition, scenePressPosition +*/ +QPointF QEventPoint::sceneLastPosition() const +{ return d->globalLastPos - d->globalPos + d->scenePos; } + +/*! + \property QEventPoint::globalPosition + the global position of this point + + The global position is relative to the screen or virtual desktop. + + \sa globalPressPosition, position, scenePosition +*/ +QPointF QEventPoint::globalPosition() const +{ return d->globalPos; } + +/*! + \property QEventPoint::globalPressPosition + the global position at which this point was pressed + + The global position is relative to the screen or virtual desktop. + + \sa globalPosition, pressPosition, scenePressPosition +*/ +QPointF QEventPoint::globalPressPosition() const +{ return d->globalPressPos; } + +/*! + \property QEventPoint::globalGrabPosition + the global position at which this point was grabbed + + The global position is relative to the screen or virtual desktop. + + \sa globalPosition, grabPosition, sceneGrabPosition +*/ +QPointF QEventPoint::globalGrabPosition() const +{ return d->globalGrabPos; } + +/*! + \property QEventPoint::globalLastPosition + the global position of this point from the previous press or move event + + The global position is relative to the screen or virtual desktop. + + \sa globalPosition, lastPosition, sceneLastPosition +*/ +QPointF QEventPoint::globalLastPosition() const +{ return d->globalLastPos; } + +/*! + \property QEventPoint::velocity + a velocity vector, in units of pixels per second, in the coordinate + system of the screen or desktop. + + \note If the device's capabilities include QInputDevice::Velocity, it means + velocity comes from the operating system (perhaps the touch hardware or + driver provides it). But usually the \c Velocity capability is not set, + indicating that the velocity is calculated by Qt, using a simple Kalman + filter to provide a smoothed average velocity rather than an instantaneous + value. Effectively it tells how fast and in what direction the user has + been dragging this point over the last few events, with the most recent + event having the strongest influence. + + \sa QInputDevice::capabilities(), QInputEvent::device() +*/ +QVector2D QEventPoint::velocity() const +{ return d->velocity; } + +/*! + \property QEventPoint::state + the current state of the event point. +*/ +QEventPoint::State QEventPoint::state() const +{ return d->state; } + +/*! + \property QEventPoint::device + the pointing device from which this event point originates +*/ +const QPointingDevice *QEventPoint::device() const +{ return d->device; } + +/*! + \property QEventPoint::id + the ID number of this event point + + \note Do not assume that ID numbers start at zero or that they are + sequential. Such an assumption is often false due to the way + the underlying drivers work. +*/ +int QEventPoint::id() const +{ return d->pointId; } + +/*! + \property QEventPoint::uniqueId + the unique ID of this point or token, if any + + It is often invalid (see \l {QPointingDeviceUniqueId::isValid()} {isValid()}), + because touchscreens cannot uniquely identify fingers. + + When it comes from a QTabletEvent, it identifies the serial number of the + stylus in use. + + It may identify a specific token (fiducial object) when the TUIO driver is + in use with a touchscreen that supports them. +*/ +QPointingDeviceUniqueId QEventPoint::uniqueId() const +{ return d->uniqueId; } + +/*! + \property QEventPoint::timestamp + the most recent time at which this point was included in a QPointerEvent + + \sa QPointerEvent::timestamp() +*/ +ulong QEventPoint::timestamp() const +{ return d->timestamp; } + +/*! + \property QEventPoint::lastTimestamp + the time from the previous QPointerEvent that contained this point + + \sa globalLastPosition +*/ +ulong QEventPoint::lastTimestamp() const +{ return d->lastTimestamp; } + +/*! + \property QEventPoint::pressTimestamp + the most recent time at which this point was pressed + + \sa timestamp +*/ +ulong QEventPoint::pressTimestamp() const +{ return d->pressTimestamp; } + +/*! + \property QEventPoint::timeHeld + the duration, in milliseconds, since this point was pressed and not released + + \sa pressTimestamp, timestamp +*/ +qreal QEventPoint::timeHeld() const +{ return (d->timestamp - d->pressTimestamp) / qreal(1000); } + +/*! + \property QEventPoint::pressure + the pressure of this point + + The return value is in the range \c 0.0 to \c 1.0. +*/ +qreal QEventPoint::pressure() const +{ return d->pressure; } + +/*! + \property QEventPoint::rotation + the angular orientation of this point + + The return value is in degrees, where zero (the default) indicates the finger, + token or stylus is pointing upwards, a negative angle means it's rotated to the + left, and a positive angle means it's rotated to the right. + Most touchscreens do not detect rotation, so zero is the most common value. +*/ +qreal QEventPoint::rotation() const +{ return d->rotation; } + +/*! + \property QEventPoint::ellipseDiameters + the width and height of the bounding ellipse of the touch point + + The return value is in logical pixels. Most touchscreens do not detect the + shape of the contact point, and no mice or tablet devices can detect it, + so a null size is the most common value. On some touchscreens the diameters + may be nonzero and always equal (the ellipse is approximated as a circle). +*/ +QSizeF QEventPoint::ellipseDiameters() const +{ return d->ellipseDiameters; } + +/*! + \property QEventPoint::accepted + the accepted state of the event point + + In widget-based applications, this property is not used, as it's only meaningful + for a widget to accept or reject a complete QInputEvent. + + In Qt Quick however, it's normal for an Item or Event Handler to accept + only the individual points in a QTouchEvent that are actually participating + in a gesture, while other points can be delivered to other items or + handlers. For the sake of consistency, that applies to any QPointerEvent; + and delivery is done only when all points in a QPointerEvent have been + accepted. + + \sa QEvent::accepted +*/ +void QEventPoint::setAccepted(bool accepted) +{ + d->accept = accepted; +} + +bool QEventPoint::isAccepted() const +{ return d->accept; } + + +/*! + \obsolete + \fn QPointF QEventPoint::normalizedPos() const + + Deprecated since Qt 6.0. Use normalizedPosition() instead. +*/ + +/*! + Returns the normalized position of this point. + + The coordinates are calculated by transforming globalPosition() into the + space of QInputDevice::availableVirtualGeometry(), i.e. \c (0, 0) is the + top-left corner and \c (1, 1) is the bottom-right corner. + + \sa globalPosition +*/ +QPointF QEventPoint::normalizedPosition() const +{ + auto geom = d->device->availableVirtualGeometry(); + if (geom.isNull()) + return QPointF(); + return (globalPosition() - geom.topLeft()) / geom.width(); +} + +/*! + \obsolete + Deprecated since Qt 6.0. Use globalPressPosition() instead. + + Returns the normalized press position of this point. +*/ +QPointF QEventPoint::startNormalizedPos() const +{ + auto geom = d->device->availableVirtualGeometry(); + if (geom.isNull()) + return QPointF(); + return (globalPressPosition() - geom.topLeft()) / geom.width(); +} + +/*! + \obsolete + Deprecated since Qt 6.0. Use globalLastPosition() instead. + + Returns the normalized position of this point from the previous press or + move event. + + The coordinates are normalized to QInputDevice::availableVirtualGeometry(), + i.e. \c (0, 0) is the top-left corner and \c (1, 1) is the bottom-right corner. + + \sa normalizedPos(), startNormalizedPos() +*/ +QPointF QEventPoint::lastNormalizedPos() const +{ + auto geom = d->device->availableVirtualGeometry(); + if (geom.isNull()) + return QPointF(); + return (globalLastPosition() - geom.topLeft()) / geom.width(); +} + + +/*! \internal + This class is explicitly shared, which means if you construct an event and + then the point(s) that it holds are modified before the event is delivered, + the event will be seen to hold the modified points. The workaround is that + any code which modifies an eventpoint that could already be included in an + event, or code that wants to save an eventpoint for later, has + responsibility to detach before calling any setters, so as to hold and + modify an independent copy. (The independent copy can then be used in a + subsequent event.) If detaching is unnecessary, because refCount shows that + there is only one QEventPoint referring to the QEventPointPrivate instance, + this function does nothing. +*/ +void QMutableEventPoint::detach() +{ + if (d->refCount == 1) + return; // no need: there is only one QEventPoint using it + qCDebug(lcEPDetach) << "detaching: refCount" << d->refCount << this; + auto old = d; + d = new QEventPointPrivate(*d); + d->refCount = 1; + --old->refCount; +} + +/*! \internal + Update current state from the given \a other point, assuming that this + instance contains state from the previous event and \a other contains new + values that came in from a device. + + That is: global position and other valuators will be updated, but + the following properties will not be updated: + + \list + \li properties that are not likely to be set after a fresh touchpoint + has been received from a device + \li properties that should be persistent between events (such as grabbers) + \endlist +*/ +void QMutableEventPoint::updateFrom(const QEventPoint &other) +{ + detach(); + setPressure(other.pressure()); + + switch (other.state()) { + case QEventPoint::State::Pressed: + setGlobalPressPosition(other.globalPosition()); + setGlobalLastPosition(other.globalPosition()); + if (pressure() < 0) + setPressure(1); + break; + + case QEventPoint::State::Released: + if (globalPosition() != other.globalPosition()) + setGlobalLastPosition(globalPosition()); + setPressure(0); + break; + + default: // update or stationary + if (globalPosition() != other.globalPosition()) + setGlobalLastPosition(globalPosition()); + if (pressure() < 0) + setPressure(1); + break; + } + + setState(other.state()); + setPosition(other.position()); + setScenePosition(other.scenePosition()); + setGlobalPosition(other.globalPosition()); + setEllipseDiameters(other.ellipseDiameters()); + setRotation(other.rotation()); + setVelocity(other.velocity()); +} + +/*! \internal + Set the timestamp from the event that updated this point's positions, + and calculate a new value for velocity(). + + The velocity calculation is done here because none of the QPointerEvent + subclass constructors take the timestamp directly, and because + QGuiApplication traditionally constructs an event first and then sets its + timestamp (see for example QGuiApplicationPrivate::processMouseEvent()). + + This function looks up the corresponding instance in QPointingDevicePrivate::activePoints, + and assumes that its timestamp() still holds the previous time when this point + was updated, its velocity() holds this point's last-known velocity, and + its globalPosition() and globalLastPosition() hold this point's current + and previous positions, respectively. We assume timestamps are in milliseconds. + + The velocity calculation is skipped if the platform has promised to + provide velocities already by setting the QInputDevice::Velocity capability. +*/ +void QMutableEventPoint::setTimestamp(const ulong t) +{ + // On mouse press, if the mouse has moved from its last-known location, + // QGuiApplicationPrivate::processMouseEvent() sends first a mouse move and + // then a press. Both events will get the same timestamp. So we need to set + // the press timestamp and position even when the timestamp isn't advancing, + // but skip setting lastTimestamp and velocity because those need a time delta. + if (state() == QEventPoint::State::Pressed) { + d->pressTimestamp = t; + d->globalPressPos = d->globalPos; + } + if (d->timestamp == t) + return; + detach(); + if (device()) { + // get the persistent instance out of QPointingDevicePrivate::activePoints + // (which sometimes might be the same as this instance) + QEventPointPrivate *pd = QPointingDevicePrivate::get( + const_cast<QPointingDevice *>(d->device))->pointById(id())->eventPoint.d; + if (t > pd->timestamp) { + pd->lastTimestamp = pd->timestamp; + pd->timestamp = t; + if (state() == QEventPoint::State::Pressed) + pd->pressTimestamp = t; + if (pd->lastTimestamp > 0 && !device()->capabilities().testFlag(QInputDevice::Capability::Velocity)) { + // calculate instantaneous velocity according to time and distance moved since the previous point + QVector2D newVelocity = QVector2D(pd->globalPos - pd->globalLastPos) / (t - pd->lastTimestamp) * 1000; + // VERY simple kalman filter: does a weighted average + // where the older velocities get less and less significant + static const float KalmanGain = 0.7f; + pd->velocity = newVelocity * KalmanGain + pd->velocity * (1.0f - KalmanGain); + qCDebug(lcPointerVel) << "velocity" << newVelocity << "filtered" << pd->velocity << + "based on movement" << pd->globalLastPos << "->" << pd->globalPos << + "over time" << pd->lastTimestamp << "->" << pd->timestamp; + } + if (d != pd) { + d->lastTimestamp = pd->lastTimestamp; + d->velocity = pd->velocity; + } + } + } + d->timestamp = t; +} + +/*! + \fn void QMutableEventPoint::setPosition(const QPointF &pos) + \internal + + Sets the localized position. + Often events need to be localized before delivery to specific widgets or + items. This can be done directly, or in a copy (for which we have a copy + constructor), depending on whether the original point needs to be retained. + Usually it's calculated by mapping scenePosition() to the target anyway. +*/ + +QT_END_NAMESPACE |