Make Viewport input events fully Qt based (#2142)
* Make Viewport input events fully Qt based. This should fix an issue with Qt touch event -> mouse event translation in the Editor, and may also fix issues with the Mac Editor and remote desktop (though, lacking the requisite hardware, I can test precisely none of these things personally). See https://github.com/o3de/o3de/issues/1889 - Adapted LegacyViewportCameraController to use Movement::X & Y (mostly for testing purposes, it's on the slate for being removed soon) - Moved cursor capture logic from RenderViewportWidget into QtEventToAzInputManager so that it can make sure it generates correct movement deltas - Removed ViewportMouseCursorRequests::PreviousViewportCursorScreenPosition to have our viewport controllers use our dedicated Movement::X and Y channels instead, which will work in the launcher Signed-off-by: nvsickle <nvsickle@amazon.com> * Address review feedback Signed-off-by: nvsickle <nvsickle@amazon.com> * Fix Linux build Signed-off-by: nvsickle <nvsickle@amazon.com>
This commit is contained in:
committed by
GitHub
parent
11327b59ea
commit
867ae23664
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <AzFramework/Input/Buses/Notifications/InputChannelNotificationBus.h>
|
||||
#include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
|
||||
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCursor>
|
||||
@@ -187,12 +188,6 @@ namespace AzToolsFramework
|
||||
|
||||
bool QtEventToAzInputMapper::HandlesInputEvent(const AzFramework::InputChannel& channel) const
|
||||
{
|
||||
const AzFramework::InputChannelId& channelId = channel.GetInputChannelId();
|
||||
if (channelId == AzFramework::InputDeviceMouse::Movement::X || channelId == AzFramework::InputDeviceMouse::Movement::Y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We map keyboard and mouse events from Qt, so flag all events coming from those devices
|
||||
// as handled by our synthetic event system.
|
||||
const AzFramework::InputDeviceId& deviceId = channel.GetInputDevice().GetInputDeviceId();
|
||||
@@ -210,6 +205,22 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::SetCursorCaptureEnabled(bool enabled)
|
||||
{
|
||||
if (m_capturingCursor != enabled)
|
||||
{
|
||||
m_capturingCursor = enabled;
|
||||
if (m_capturingCursor)
|
||||
{
|
||||
qApp->setOverrideCursor(Qt::BlankCursor);
|
||||
}
|
||||
else
|
||||
{
|
||||
qApp->restoreOverrideCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event)
|
||||
{
|
||||
// Abort if processing isn't enabled.
|
||||
@@ -284,13 +295,25 @@ namespace AzToolsFramework
|
||||
{
|
||||
auto systemCursorChannel =
|
||||
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::SystemCursorPosition);
|
||||
auto movementXChannel =
|
||||
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::Movement::X);
|
||||
auto movementYChannel =
|
||||
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::Movement::Y);
|
||||
auto mouseWheelChannel =
|
||||
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::Movement::Z);
|
||||
|
||||
systemCursorChannel->ProcessRawInputEvent(m_cursorPosition->m_normalizedPositionDelta.GetLength());
|
||||
// Generate movement events based on the pixel delta divided by the DPI scaling factor, to calculate a rough approximation
|
||||
// of cursor movement velocity.
|
||||
movementXChannel->ProcessRawInputEvent(
|
||||
m_cursorPosition->m_normalizedPositionDelta.GetX() * aznumeric_cast<float>(m_sourceWidget->width()) / m_sourceWidget->devicePixelRatioF());
|
||||
movementYChannel->ProcessRawInputEvent(
|
||||
m_cursorPosition->m_normalizedPositionDelta.GetY() * aznumeric_cast<float>(m_sourceWidget->height()) / m_sourceWidget->devicePixelRatioF());
|
||||
mouseWheelChannel->ProcessRawInputEvent(0.f);
|
||||
|
||||
NotifyUpdateChannelIfNotIdle(systemCursorChannel, nullptr);
|
||||
NotifyUpdateChannelIfNotIdle(movementXChannel, nullptr);
|
||||
NotifyUpdateChannelIfNotIdle(movementYChannel, nullptr);
|
||||
NotifyUpdateChannelIfNotIdle(mouseWheelChannel, nullptr);
|
||||
}
|
||||
|
||||
@@ -318,16 +341,42 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Vector2 QtEventToAzInputMapper::WidgetPositionToNormalizedPosition(QPoint position)
|
||||
{
|
||||
const float normalizedX = aznumeric_cast<float>(position.x()) / aznumeric_cast<float>(m_sourceWidget->width());
|
||||
const float normalizedY = aznumeric_cast<float>(position.y()) / aznumeric_cast<float>(m_sourceWidget->height());
|
||||
return AZ::Vector2{normalizedX, normalizedY};
|
||||
}
|
||||
|
||||
QPoint QtEventToAzInputMapper::NormalizedPositionToWidgetPosition(AZ::Vector2 normalizedPosition)
|
||||
{
|
||||
const int denormalizedX = aznumeric_cast<int>(normalizedPosition.GetX() * m_sourceWidget->width());
|
||||
const int denormalizedY = aznumeric_cast<int>(normalizedPosition.GetY() * m_sourceWidget->height());
|
||||
return QPoint{denormalizedX, denormalizedY};
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::HandleMouseMoveEvent(QMouseEvent* mouseEvent)
|
||||
{
|
||||
AZ::Vector2 lastCursorPosition = m_cursorPosition->m_normalizedPosition;
|
||||
|
||||
const QPoint mousePos = mouseEvent->pos();
|
||||
const float normalizedX = aznumeric_cast<float>(mousePos.x()) / aznumeric_cast<float>(m_sourceWidget->width());
|
||||
const float normalizedY = aznumeric_cast<float>(mousePos.y()) / aznumeric_cast<float>(m_sourceWidget->height());
|
||||
const AZ::Vector2 normalizedPosition(normalizedX, normalizedY);
|
||||
const AZ::Vector2 normalizedPosition = WidgetPositionToNormalizedPosition(mousePos);
|
||||
m_cursorPosition->m_normalizedPositionDelta = normalizedPosition - m_cursorPosition->m_normalizedPosition;
|
||||
m_cursorPosition->m_normalizedPosition = normalizedPosition;
|
||||
ProcessPendingMouseEvents();
|
||||
m_mouseChannelsNeedUpdate = true;
|
||||
|
||||
if (m_capturingCursor)
|
||||
{
|
||||
// Reset our cursor position to the previous point.
|
||||
QPoint targetScreenPosition = m_sourceWidget->mapToGlobal(NormalizedPositionToWidgetPosition(lastCursorPosition));
|
||||
AzQtComponents::SetCursorPos(targetScreenPosition);
|
||||
|
||||
// Even though we just set the cursor position, there are edge cases such as remote desktop that will leave
|
||||
// the cursor position unchanged. For safety, we re-cache our last cursor position for delta generation.
|
||||
QPoint actualWidgetPosition = m_sourceWidget->mapFromGlobal(QCursor::pos());
|
||||
m_cursorPosition->m_normalizedPosition = WidgetPositionToNormalizedPosition(actualWidgetPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent)
|
||||
|
||||
@@ -47,6 +47,12 @@ namespace AzToolsFramework
|
||||
//! Sets whether or not this input mapper should be updating its input channels from Qt events.
|
||||
void SetEnabled(bool enabled);
|
||||
|
||||
//! 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.
|
||||
void SetCursorCaptureEnabled(bool enabled);
|
||||
|
||||
// QObject overrides...
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
@@ -106,6 +112,11 @@ namespace AzToolsFramework
|
||||
// Processes any pending mouse movement events, this allows mouse movement channels to close themselves.
|
||||
void ProcessPendingMouseEvents();
|
||||
|
||||
// Converts a point in logical source widget space [0..m_sourceWidget->size()] to normalized [0..1] space.
|
||||
AZ::Vector2 WidgetPositionToNormalizedPosition(QPoint position);
|
||||
// Converts a point in normalized [0..1] space to logical source widget space [0..m_sourceWidget->size()].
|
||||
QPoint NormalizedPositionToWidgetPosition(AZ::Vector2 normalizedPosition);
|
||||
|
||||
// Handle mouse click events.
|
||||
void HandleMouseButtonEvent(QMouseEvent* mouseEvent);
|
||||
// Handle mouse move events.
|
||||
@@ -144,6 +155,8 @@ namespace AzToolsFramework
|
||||
bool m_mouseChannelsNeedUpdate = false;
|
||||
// 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;
|
||||
|
||||
// Our viewport-specific AZ devices. We control their internal input channel states.
|
||||
AZStd::unique_ptr<EditorQtMouseDevice> m_mouseDevice;
|
||||
|
||||
@@ -276,11 +276,6 @@ namespace AzToolsFramework
|
||||
virtual void EndCursorCapture() = 0;
|
||||
//! Gets the most recent recorded cursor position in the viewport in screen space coordinates.
|
||||
virtual AzFramework::ScreenPoint ViewportCursorScreenPosition() = 0;
|
||||
//! Gets the cursor position recorded prior to the most recent cursor position.
|
||||
//! Note: The cursor may be captured by the viewport, in which case this may not correspond to the last result
|
||||
//! from ViewportCursorScreenPosition. This method will always return the correct position to generate a mouse
|
||||
//! position delta.
|
||||
virtual AZStd::optional<AzFramework::ScreenPoint> PreviousViewportCursorScreenPosition() = 0;
|
||||
//! Is mouse over viewport.
|
||||
virtual bool IsMouseOver() const = 0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user