QPointerUniqueId: make fit for release

- Declare as Q_MOVABLE_TYPE
- Prevent QList<QPointerUniqueId> from being instantiated
  (use QVector instead)
- Add equality relational operators
- Add qHash() overload
- Replace non-default ctor with named ctor.
- Add Q_DECL_NOTHROW.
- Add Q_DECL_CONSTEXPR.
- Rename numeric() -> numericId().
- Update docs.

The extension vector for this class calls for additional
properties to be added later, but these are not user-
settable. It thus suffices to rely on the only data
member, a qint64, which can be reinterpreted to an index
into an array or hash with actual objects. This allows
to make the class a Trivial Type (ie. no overhead over
an int) while still supporting later extension. Cf.
QSslEllipticCurve as another example of such a class.

The extension has to maintain the following invariants,
encoded into user code by way of being used in inline
functions:

- m_numericId == -1 <=> !isValid()

This is trivial to support. An extension could not and
still cannot reinterpret the qint64 member as a d-pointer,
but a d-pointer is only necessary for user-settable
properties where updating a central private data structure
would cause too much contention.

Add a test.

Since this type is used in other modules, keep the existing
functions, but mark them as deprecated with the expectation
that these compat functions be removed before 5.8.0 final.

Task-number: QTBUG-54616
Change-Id: Ia3ede0ecaeeef4cd3ffa94a72b1050bd409713a5
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
This commit is contained in:
Marc Mutz 2016-09-08 10:10:12 +02:00 committed by Jani Heikkinen
parent f4a33e345e
commit 048447346b
3 changed files with 142 additions and 15 deletions

View File

@ -44,6 +44,7 @@
#include "qpa/qplatformdrag.h"
#include "private/qevent_p.h"
#include "qfile.h"
#include "qhashfunctions.h"
#include "qmetaobject.h"
#include "qmimedata.h"
#include "private/qdnd_p.h"
@ -4474,7 +4475,7 @@ int QTouchEvent::TouchPoint::id() const
\since 5.8
Returns the unique ID of this touch point or token, if any.
It is normally invalid (with a \l {QPointerUniqueId::numeric()} {numeric()} value of -1),
It is normally invalid (see \l {QPointerUniqueId::isValid()} {isValid()}),
because touchscreens cannot uniquely identify fingers. But when the
\l {TouchPoint::InfoFlag} {Token} flag is set, it is expected to uniquely
identify a specific token (fiducial object).
@ -4757,7 +4758,7 @@ void QTouchEvent::TouchPoint::setUniqueId(qint64 uid)
{
if (d->ref.load() != 1)
d = d->detach();
d->uniqueId = QPointerUniqueId(uid);
d->uniqueId = QPointerUniqueId::fromNumericId(uid);
}
/*! \internal */
@ -5184,28 +5185,91 @@ Qt::ApplicationState QApplicationStateChangeEvent::applicationState() const
\brief QPointerUniqueId identifies a unique object, such as a tagged token
or stylus, which is used with a pointing device.
QPointerUniqueIds can be compared for equality, and can be used as keys in a QHash.
You get access to the numerical ID via numericId(), if the device supports such IDs.
For future extensions, though, you should not use that function, but compare objects
of this type using the equality operator.
This class is a thin wrapper around an integer ID. You pass it into and out of
functions by value.
This type actively prevents you from holding it in a QList, because doing so would
be very inefficient. Use a QVector instead, which has the same API as QList, but more
efficient storage.
\sa QTouchEvent::TouchPoint
*/
/*!
Constructs a unique pointer ID with a numeric \a id provided by the hardware.
The default is -1, which means an invalid pointer ID.
\fn QPointerUniqueId::QPointerUniqueId()
Constructs an invalid unique pointer ID.
*/
QPointerUniqueId::QPointerUniqueId(qint64 id)
: m_numericId(id)
/*!
Constructs a unique pointer ID from numeric ID \a id.
*/
QPointerUniqueId QPointerUniqueId::fromNumericId(qint64 id)
{
QPointerUniqueId result;
result.m_numericId = id;
return result;
}
/*!
\property QPointerUniqueId::numeric
\fn bool QPointerUniqueId::isValid()
Returns whether this unique pointer ID is valid, that is, it represents an actual
pointer.
*/
/*!
\property QPointerUniqueId::numericId
\brief the numeric unique ID of the token represented by a touchpoint
This is the numeric unique ID if the device provides that type of ID;
If the device provides a numeric ID, isValid() returns true, and this
property provides the numeric ID;
otherwise it is -1.
You should not use the value of this property in portable code, but
instead rely on equality to identify pointers.
\sa isValid()
*/
qint64 QPointerUniqueId::numeric() const
qint64 QPointerUniqueId::numericId() const Q_DECL_NOTHROW
{
return m_numericId;
}
/*!
\relates QPointerUniqueId
\since 5.8
Returns whether the two unique pointer IDs \a lhs and \a rhs identify the same pointer
(\c true) or not (\c false).
*/
bool operator==(QPointerUniqueId lhs, QPointerUniqueId rhs) Q_DECL_NOTHROW
{
return lhs.numericId() == rhs.numericId();
}
/*!
\fn bool operator!=(QPointerUniqueId lhs, QPointerUniqueId rhs)
\relates QPointerUniqueId
\since 5.8
Returns whether the two unique pointer IDs \a lhs and \a rhs identify different pointers
(\c true) or not (\c false).
*/
/*!
\relates QPointerUniqueId
\since 5.8
Returns the hash value for \a key, using \a seed to seed the calculation.
*/
uint qHash(QPointerUniqueId key, uint seed) Q_DECL_NOTHROW
{
return qHash(key.numericId(), seed);
}
QT_END_NAMESPACE

View File

@ -793,21 +793,45 @@ inline bool operator==(QKeyEvent *e, QKeySequence::StandardKey key){return (e ?
inline bool operator==(QKeySequence::StandardKey key, QKeyEvent *e){return (e ? e->matches(key) : false);}
#endif // QT_NO_SHORTCUT
class QPointerUniqueIdPrivate;
class Q_GUI_EXPORT QPointerUniqueId
{
Q_GADGET
Q_PROPERTY(qint64 numeric READ numeric CONSTANT)
// ### kept these to keep other modules compiling. Remove before 5.8.0 final!
#if QT_DEPRECATED_SINCE(5, 8)
Q_PROPERTY(qint64 numeric READ numericId CONSTANT)
#endif
Q_PROPERTY(qint64 numericId READ numericId CONSTANT)
public:
explicit QPointerUniqueId(qint64 id = -1);
Q_ALWAYS_INLINE
Q_DECL_CONSTEXPR QPointerUniqueId() Q_DECL_NOTHROW : m_numericId(-1) {}
// compiler-generated copy/move ctor/assignment operators are ok!
// compiler-generated dtor is ok!
qint64 numeric() const;
static QPointerUniqueId fromNumericId(qint64 id);
Q_ALWAYS_INLINE Q_DECL_CONSTEXPR bool isValid() const Q_DECL_NOTHROW { return m_numericId != -1; }
qint64 numericId() const Q_DECL_NOTHROW;
// ### kept these to keep other modules compiling. Remove before 5.8.0 final!
#if QT_DEPRECATED_SINCE(5, 8)
Q_ALWAYS_INLINE Q_DECL_DEPRECATED qint64 numeric() const { return numericId(); }
Q_ALWAYS_INLINE Q_DECL_DEPRECATED explicit QPointerUniqueId(qint64 id) : m_numericId(id) {}
#endif
private:
// TODO for TUIO 2, or any other type of complex token ID, a d-pointer can replace
// m_numericId without changing the size of this class.
// TODO: for TUIO 2, or any other type of complex token ID, an internal
// array (or hash) can be added to hold additional properties.
// In this case, m_numericId will then turn into an index into that array (or hash).
qint64 m_numericId;
};
Q_DECLARE_TYPEINFO(QPointerUniqueId, Q_MOVABLE_TYPE);
template <> class QList<QPointerUniqueId> {}; // to prevent instantiation: use QVector instead
Q_GUI_EXPORT bool operator==(QPointerUniqueId lhs, QPointerUniqueId rhs) Q_DECL_NOTHROW;
inline bool operator!=(QPointerUniqueId lhs, QPointerUniqueId rhs) Q_DECL_NOTHROW
{ return !operator==(lhs, rhs); }
Q_GUI_EXPORT uint qHash(QPointerUniqueId key, uint seed = 0) Q_DECL_NOTHROW;
class QTouchEventTouchPointPrivate;
class Q_GUI_EXPORT QTouchEvent : public QInputEvent

View File

@ -194,6 +194,7 @@ public:
private slots:
void cleanup();
void qPointerUniqueId();
void touchDisabledByDefault();
void touchEventAcceptedByDefault();
void touchBeginPropagatesWhenIgnored();
@ -224,6 +225,44 @@ void tst_QTouchEvent::cleanup()
QVERIFY(QGuiApplication::topLevelWindows().isEmpty());
}
void tst_QTouchEvent::qPointerUniqueId()
{
QPointerUniqueId id1, id2;
QCOMPARE(id1.numericId(), Q_INT64_C(-1));
QVERIFY(!id1.isValid());
QVERIFY( id1 == id2);
QVERIFY(!(id1 != id2));
QSet<QPointerUniqueId> set; // compile test
set.insert(id1);
set.insert(id2);
QCOMPARE(set.size(), 1);
const auto id3 = QPointerUniqueId::fromNumericId(-1);
QCOMPARE(id3.numericId(), Q_INT64_C(-1));
QVERIFY(!id3.isValid());
QVERIFY( id1 == id3);
QVERIFY(!(id1 != id3));
set.insert(id3);
QCOMPARE(set.size(), 1);
const auto id4 = QPointerUniqueId::fromNumericId(4);
QCOMPARE(id4.numericId(), Q_INT64_C(4));
QVERIFY(id4.isValid());
QVERIFY( id1 != id4);
QVERIFY(!(id1 == id4));
set.insert(id4);
QCOMPARE(set.size(), 2);
}
void tst_QTouchEvent::touchDisabledByDefault()
{
// QWidget