File indexing completed on 2024-04-28 15:51:42

0001 /*
0002     SPDX-FileCopyrightText: 2020 David Hurka <david.hurka@mailbox.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #ifndef CURSORWRAPHELPER_H
0008 #define CURSORWRAPHELPER_H
0009 
0010 #include <QPoint>
0011 #include <QPointer>
0012 
0013 class QScreen;
0014 
0015 /**
0016  * Wrap the cursor around screen edges.
0017  *
0018  * Problem: When setting the cursor position,
0019  * the actual wrap operation may happen later or not at all.
0020  * Your application needs to observe the actual wrap operation,
0021  * and calculate movement offsets based on this operation.
0022  *
0023  * This class provides this functionality in a single static function.
0024  *
0025  * Example:
0026  * \code
0027  * MyWidget::mousePressEvent(QMouseEvent *me)
0028  * {
0029  *     CursorWrapHelper::startDrag();
0030  *     m_lastCursorPos = me->pos();
0031  * }
0032  *
0033  * MyWidget::mouseMoveEvent(QMouseEvent *me)
0034  * {
0035  *     cursorMovement = me->pos() - m_lastCursorPos;
0036  *     cursorMovement -= CursorWrapHelper::wrapCursor(me->pos(), Qt::TopEdge | Qt::BottomEdge);
0037  *
0038  *     ...
0039  *     processMovement(cursorMovement);
0040  *     ...
0041  * }
0042  * \endcode
0043  */
0044 class CursorWrapHelper
0045 {
0046 public:
0047     /**
0048      * Wrap the QCursor around specified screen edges.
0049      *
0050      * Wrapping is performed using QCursor::pos().
0051      * You have to provide a cursor position, because QCursor::pos() is realtime,
0052      * which means it can not be used to calculate the resulting offset for you.
0053      * If you implement mousePressEvent() and mouseMoveEvent(),
0054      * you can simply pass event->pos().
0055      * @p eventPosition may have a constant offset.
0056      *
0057      * @param eventPosition The cursor position you are currently working with.
0058      * @param edges At which edges to wrap. (E. g. top -> bottom: use Qt::TopEdge)
0059      * @returns The actual distance the cursor was moved.
0060      */
0061     static QPoint wrapCursor(QPoint eventPosition, Qt::Edges edges);
0062 
0063     /**
0064      * Call this to avoid computing a wrap distance when a drag starts.
0065      *
0066      * This should be called every time you get e. g. a mousePressEvent().
0067      */
0068     static void startDrag();
0069 
0070 protected:
0071     /** Returns the screen under the cursor */
0072     static QScreen *getScreen();
0073     /** Remember screen to speed up screen search */
0074     static QPointer<QScreen> s_lastScreen;
0075 
0076     /**
0077      * Actual wrapping of the cursor may happen later.
0078      * By comparing the magnitude of cursor movements to the last wrap operation,
0079      * we can catch the moment when wrapping actually happens,
0080      * and return the wrapping offset at that time.
0081      *
0082      * Vertical wrapping and horizontal wrapping may happen with little delay,
0083      * so they are handled strictly separately.
0084      */
0085     static QPoint s_lastCursorPosition;
0086     static QPoint s_lastWrapOperation;
0087 
0088     /**
0089      * If the user releases the mouse while it is being wrapped,
0090      * we don’t want the wrap to be subtracted from the next drag operation.
0091      * This timestamp allows to check whether the user possibly started a new drag.
0092      */
0093     static QPoint s_lastTimeStamp;
0094 };
0095 
0096 #endif // CURSORWRAPHELPER_H