summaryrefslogtreecommitdiffstats
path: root/src/gui/kernel/qeventpoint.cpp
diff options
context:
space:
mode:
authorVolker Hilsheimer <[email protected]>2020-11-05 18:11:56 +0100
committerVolker Hilsheimer <[email protected]>2020-11-07 06:24:28 +0100
commitbdec189ecbc2cabbfa803a571b49533f190f053d (patch)
treea061307e207cfbdb989e463c9535208a822fab78 /src/gui/kernel/qeventpoint.cpp
parent1c6d6cbb62c5e93cbcad2d740c3b0ed01095618c (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.cpp664
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