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 }