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 #include "cursorwraphelper.h"
0008 
0009 #include <QCursor>
0010 #include <QGuiApplication>
0011 #include <QRect>
0012 #include <QScreen>
0013 
0014 QPointer<QScreen> CursorWrapHelper::s_lastScreen;
0015 QPoint CursorWrapHelper::s_lastCursorPosition;
0016 QPoint CursorWrapHelper::s_lastWrapOperation;
0017 
0018 QPoint CursorWrapHelper::wrapCursor(QPoint eventPosition, Qt::Edges edges)
0019 {
0020     QScreen *screen = getScreen();
0021     if (!screen) {
0022         return QPoint();
0023     }
0024 
0025     // Step 1: Generate wrap operations.
0026     // Assuming screen->geometry() is larger than 10x10.
0027     const QRect screenGeometry = screen->geometry();
0028     const QPoint screenCursorPos = QCursor::pos(screen);
0029 
0030     if (edges & Qt::LeftEdge && screenCursorPos.x() < screenGeometry.left() + 4) {
0031         QCursor::setPos(screen, screenCursorPos + QPoint(screenGeometry.width() - 10, 0));
0032         s_lastWrapOperation.setX(screenGeometry.width() - 10);
0033     } else if (edges & Qt::RightEdge && screenCursorPos.x() > screenGeometry.right() - 4) {
0034         QCursor::setPos(screen, screenCursorPos + QPoint(-screenGeometry.width() + 10, 0));
0035         s_lastWrapOperation.setX(-screenGeometry.width() + 10);
0036     }
0037 
0038     if (edges & Qt::TopEdge && screenCursorPos.y() < screenGeometry.top() + 4) {
0039         QCursor::setPos(screen, screenCursorPos + QPoint(0, screenGeometry.height() - 10));
0040         s_lastWrapOperation.setY(screenGeometry.height() - 10);
0041     } else if (edges & Qt::BottomEdge && screenCursorPos.y() > screenGeometry.bottom() - 4) {
0042         QCursor::setPos(screen, screenCursorPos + QPoint(0, -screenGeometry.height() + 10));
0043         s_lastWrapOperation.setY(-screenGeometry.height() + 10);
0044     }
0045 
0046     // Step 2: Catch wrap movements.
0047     // We observe the cursor movement since the last call of wrapCursor().
0048     // If the cursor moves in the same magnitude as the last wrap operation,
0049     // we return the value of this wrap operation with appropriate sign.
0050     const QPoint cursorMovement = eventPosition - s_lastCursorPosition;
0051     s_lastCursorPosition = eventPosition;
0052 
0053     QPoint ret_wrapDistance;
0054 
0055     qreal horizontalMagnitude = qAbs(qreal(s_lastWrapOperation.x()) / qreal(cursorMovement.x()));
0056     int horizontalSign = cursorMovement.x() > 0 ? 1 : -1;
0057     if (0.5 < horizontalMagnitude && horizontalMagnitude < 2.0) {
0058         ret_wrapDistance.setX(qAbs(s_lastWrapOperation.x()) * horizontalSign);
0059     }
0060 
0061     qreal verticalMagnitude = qAbs(qreal(s_lastWrapOperation.y()) / qreal(cursorMovement.y()));
0062     int verticalSign = cursorMovement.y() > 0 ? 1 : -1;
0063     if (0.5 < verticalMagnitude && verticalMagnitude < 2.0) {
0064         ret_wrapDistance.setY(qAbs(s_lastWrapOperation.y()) * verticalSign);
0065     }
0066 
0067     return ret_wrapDistance;
0068 }
0069 
0070 void CursorWrapHelper::startDrag()
0071 {
0072     s_lastWrapOperation.setX(0);
0073     s_lastWrapOperation.setY(0);
0074 }
0075 
0076 QScreen *CursorWrapHelper::getScreen()
0077 {
0078     const QPoint cursorPos = QCursor::pos();
0079 
0080     if (s_lastScreen && s_lastScreen->geometry().contains(cursorPos)) {
0081         return s_lastScreen;
0082     }
0083 
0084     const QList<QScreen *> screens = QGuiApplication::screens();
0085     for (QScreen *screen : screens) {
0086         if (screen->geometry().contains(cursorPos)) {
0087             s_lastScreen = screen;
0088             return screen;
0089         }
0090     }
0091     // Corner case: cursor already pushed against an edge.
0092     for (QScreen *screen : screens) {
0093         if (screen->geometry().adjusted(-5, -5, 5, 5).contains(cursorPos)) {
0094             s_lastScreen = screen;
0095             return screen;
0096         }
0097     }
0098 
0099     return nullptr;
0100 }