You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

100 lines
3.5 KiB

/*
SPDX-FileCopyrightText: 2020 David Hurka <david.hurka@mailbox.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "cursorwraphelper.h"
#include <QCursor>
#include <QGuiApplication>
#include <QRect>
#include <QScreen>
QPointer<QScreen> CursorWrapHelper::s_lastScreen;
QPoint CursorWrapHelper::s_lastCursorPosition;
QPoint CursorWrapHelper::s_lastWrapOperation;
QPoint CursorWrapHelper::wrapCursor(QPoint eventPosition, Qt::Edges edges)
{
QScreen *screen = getScreen();
if (!screen) {
return QPoint();
}
// Step 1: Generate wrap operations.
// Assuming screen->geometry() is larger than 10x10.
const QRect screenGeometry = screen->geometry();
const QPoint screenCursorPos = QCursor::pos(screen);
if (edges & Qt::LeftEdge && screenCursorPos.x() < screenGeometry.left() + 4) {
QCursor::setPos(screen, screenCursorPos + QPoint(screenGeometry.width() - 10, 0));
s_lastWrapOperation.setX(screenGeometry.width() - 10);
} else if (edges & Qt::RightEdge && screenCursorPos.x() > screenGeometry.right() - 4) {
QCursor::setPos(screen, screenCursorPos + QPoint(-screenGeometry.width() + 10, 0));
s_lastWrapOperation.setX(-screenGeometry.width() + 10);
}
if (edges & Qt::TopEdge && screenCursorPos.y() < screenGeometry.top() + 4) {
QCursor::setPos(screen, screenCursorPos + QPoint(0, screenGeometry.height() - 10));
s_lastWrapOperation.setY(screenGeometry.height() - 10);
} else if (edges & Qt::BottomEdge && screenCursorPos.y() > screenGeometry.bottom() - 4) {
QCursor::setPos(screen, screenCursorPos + QPoint(0, -screenGeometry.height() + 10));
s_lastWrapOperation.setY(-screenGeometry.height() + 10);
}
// Step 2: Catch wrap movements.
// We observe the cursor movement since the last call of wrapCursor().
// If the cursor moves in the same magnitude as the last wrap operation,
// we return the value of this wrap operation with appropriate sign.
const QPoint cursorMovement = eventPosition - s_lastCursorPosition;
s_lastCursorPosition = eventPosition;
QPoint ret_wrapDistance;
qreal horizontalMagnitude = qAbs(qreal(s_lastWrapOperation.x()) / qreal(cursorMovement.x()));
int horizontalSign = cursorMovement.x() > 0 ? 1 : -1;
if (0.5 < horizontalMagnitude && horizontalMagnitude < 2.0) {
ret_wrapDistance.setX(qAbs(s_lastWrapOperation.x()) * horizontalSign);
}
qreal verticalMagnitude = qAbs(qreal(s_lastWrapOperation.y()) / qreal(cursorMovement.y()));
int verticalSign = cursorMovement.y() > 0 ? 1 : -1;
if (0.5 < verticalMagnitude && verticalMagnitude < 2.0) {
ret_wrapDistance.setY(qAbs(s_lastWrapOperation.y()) * verticalSign);
}
return ret_wrapDistance;
}
void CursorWrapHelper::startDrag()
{
s_lastWrapOperation.setX(0);
s_lastWrapOperation.setY(0);
}
QScreen *CursorWrapHelper::getScreen()
{
const QPoint cursorPos = QCursor::pos();
if (s_lastScreen && s_lastScreen->geometry().contains(cursorPos)) {
return s_lastScreen;
}
const QList<QScreen *> screens = QGuiApplication::screens();
for (QScreen *screen : screens) {
if (screen->geometry().contains(cursorPos)) {
s_lastScreen = screen;
return screen;
}
}
// Corner case: cursor already pushed against an edge.
for (QScreen *screen : screens) {
if (screen->geometry().adjusted(-5, -5, 5, 5).contains(cursorPos)) {
s_lastScreen = screen;
return screen;
}
}
return nullptr;
}