Merge pull request #5155 from pollend/feat/add-cursor-wrap-mode-viewport

feat: add cursor wrapped mode
This commit is contained in:
Chris Galvan
2022-01-24 11:43:18 -06:00
committed by GitHub
5 changed files with 283 additions and 23 deletions
@@ -241,25 +241,34 @@ namespace AzToolsFramework
}
}
void QtEventToAzInputMapper::SetCursorCaptureEnabled(bool enabled)
void QtEventToAzInputMapper::SetCursorMode(AzToolsFramework::CursorInputMode mode)
{
if (m_capturingCursor != enabled)
if (mode != m_cursorMode)
{
m_capturingCursor = enabled;
if (m_capturingCursor)
m_cursorMode = mode;
switch (m_cursorMode)
{
m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::ConstrainedAndHidden);
case CursorInputMode::CursorModeCaptured:
qApp->setOverrideCursor(Qt::BlankCursor);
}
else
{
m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible);
m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::ConstrainedAndHidden);
break;
case CursorInputMode::CursorModeWrapped:
qApp->restoreOverrideCursor();
m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible);
break;
case CursorInputMode::CursorModeNone:
qApp->restoreOverrideCursor();
m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible);
break;
}
}
}
void QtEventToAzInputMapper::SetCursorCaptureEnabled(bool enabled)
{
SetCursorMode(enabled ? CursorInputMode::CursorModeCaptured : CursorInputMode::CursorModeNone);
}
bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event)
{
// Abort if processing isn't enabled.
@@ -436,25 +445,80 @@ namespace AzToolsFramework
return QPoint{ denormalizedX, denormalizedY };
}
void WrapCursorX(const QRect& rect, QPoint& point)
{
if (point.x() < rect.left())
{
point.setX(rect.right() - 1);
}
else if (point.x() > rect.right())
{
point.setX(rect.left() + 1);
}
}
void WrapCursorY(const QRect& rect, QPoint& point)
{
if (point.y() < rect.top())
{
point.setY(rect.bottom() - 1);
}
else if (point.y() > rect.bottom())
{
point.setY(rect.top() + 1);
}
}
void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& globalCursorPosition)
{
const QPoint cursorDelta = globalCursorPosition - m_previousGlobalCursorPosition;
QScreen* screen = m_sourceWidget->screen();
const QRect widgetRect(m_sourceWidget->mapToGlobal(QPoint(0, 0)), m_sourceWidget->size());
m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition =
WidgetPositionToNormalizedPosition(m_sourceWidget->mapFromGlobal(globalCursorPosition));
m_mouseDevice->m_cursorPositionData2D->m_normalizedPositionDelta = WidgetPositionToNormalizedPosition(cursorDelta);
ProcessPendingMouseEvents(cursorDelta);
if (m_capturingCursor)
switch (m_cursorMode)
{
// Reset our cursor position to the previous point
case CursorInputMode::CursorModeCaptured:
AzQtComponents::SetCursorPos(m_previousGlobalCursorPosition);
}
else
{
break;
case CursorInputMode::CursorModeWrappedX:
case CursorInputMode::CursorModeWrappedY:
case CursorInputMode::CursorModeWrapped:
{
QPoint screenPos(globalCursorPosition);
switch (m_cursorMode)
{
case CursorInputMode::CursorModeWrappedX:
WrapCursorX(widgetRect, screenPos);
break;
case CursorInputMode::CursorModeWrappedY:
WrapCursorY(widgetRect, screenPos);
break;
case CursorInputMode::CursorModeWrapped:
WrapCursorX(widgetRect, screenPos);
WrapCursorY(widgetRect, screenPos);
break;
default:
// this should never happen
AZ_Assert(false, "Invalid Curosr Mode: %i.", m_cursorMode);
break;
}
QCursor::setPos(screen, screenPos);
const QPoint screenDelta = globalCursorPosition - screenPos;
m_previousGlobalCursorPosition = globalCursorPosition - screenDelta;
}
break;
case CursorInputMode::CursorModeNone:
m_previousGlobalCursorPosition = globalCursorPosition;
break;
default:
AZ_Assert(false, "Invalid Curosr Mode: %i.", m_cursorMode);
break;
}
ProcessPendingMouseEvents(cursorDelta);
}
void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent)
@@ -32,6 +32,17 @@ class QWheelEvent;
namespace AzToolsFramework
{
enum class CursorInputMode {
CursorModeNone,
CursorModeCaptured, //!< Sets whether or not the cursor should be constrained to the source widget and invisible.
//!< Internally, this will reset the cursor position after each move event to ensure movement
//!< events don't allow the cursor to escape. This can be used for typical camera controls
//!< like a dolly or rotation, where mouse movement is important but cursor location is not.
CursorModeWrapped, //!< Flags whether the curser is going to wrap around the soruce widget.
CursorModeWrappedX, //!< Flags whether the curser is going to wrap around the soruce widget only on the left and right side.
CursorModeWrappedY //!< Flags whether the curser is going to wrap around the soruce widget only on the top and bottom side.
};
//! Maps events from the Qt input system to synthetic InputChannels in AzFramework
//! that can be used by AzFramework::ViewportControllers.
class QtEventToAzInputMapper final
@@ -56,8 +67,12 @@ namespace AzToolsFramework
//! Internally, this will reset the cursor position after each move event to ensure movement
//! events don't allow the cursor to escape. This can be used for typical camera controls
//! like a dolly or rotation, where mouse movement is important but cursor location is not.
//! @deprecated Use #SetCursorMode()
void SetCursorCaptureEnabled(bool enabled);
//! Set the cursor mode.
void SetCursorMode(AzToolsFramework::CursorInputMode mode);
void SetOverrideCursor(ViewportInteraction::CursorStyleOverride cursorStyleOverride);
void ClearOverrideCursor();
@@ -176,8 +191,8 @@ namespace AzToolsFramework
QWidget* m_sourceWidget;
// Flags whether or not Qt events should currently be processed.
bool m_enabled = true;
// Flags whether or not the cursor is being constrained to the source widget (for invisible mouse movement).
bool m_capturingCursor = false;
// Controls the cursor behavior.
AzToolsFramework::CursorInputMode m_cursorMode = AzToolsFramework::CursorInputMode::CursorModeNone;
// Flags whether the cursor has been overridden.
bool m_overrideCursor = false;