[development] updated editor event handling with respect to imgui (#6114)
Fixes #4366 - Added support for sending text events from the Qt event mapper - Fixed several issues found while running the Profiler gem in debug - Added ability to track AZ-consumed events in the Qt event mapper - Minor clean up to input handling in ImGuiManager - Select ImGui mouse events can now be properly consumed during AZ-input processing in the editor viewport - Added tests for the Qt event mapper Signed-off-by: AMZN-ScottR 24445312+AMZN-ScottR@users.noreply.github.com
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
#include <AzFramework/Input/Buses/Notifications/InputChannelNotificationBus.h>
|
||||
#include <AzFramework/Input/Buses/Notifications/InputTextNotificationBus.h>
|
||||
#include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
|
||||
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
|
||||
|
||||
@@ -24,6 +25,30 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
static bool HandleTextEvent(QEvent::Type eventType, Qt::Key key, QString keyText, bool isAutoRepeat)
|
||||
{
|
||||
bool textConsumed = false;
|
||||
|
||||
if (key == Qt::Key_Backspace)
|
||||
{
|
||||
keyText = "\b";
|
||||
}
|
||||
|
||||
if (!keyText.isEmpty())
|
||||
{
|
||||
// key events are first sent as shortcuts, if accepted they are then re-sent as traditional key
|
||||
// down events. dispatching the key event as text during a shortcut (and auto-repeat press)
|
||||
// ensures all printable keys a fair chance at being consumed before processing elsewhere
|
||||
if (eventType == QEvent::Type::ShortcutOverride || (eventType == QEvent::Type::KeyPress && isAutoRepeat))
|
||||
{
|
||||
AzFramework::InputTextNotificationBus::Broadcast(
|
||||
&AzFramework::InputTextNotifications::OnInputTextEvent, AZStd::string(keyText.toUtf8().data()), textConsumed);
|
||||
}
|
||||
}
|
||||
|
||||
return textConsumed;
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::InitializeKeyMappings()
|
||||
{
|
||||
// This assumes modifier keys (ctrl/shift/alt) map to the left control/shift/alt keys as Qt provides no way to disambiguate
|
||||
@@ -194,6 +219,7 @@ namespace AzToolsFramework
|
||||
|
||||
// Install a global event filter to ensure we don't miss mouse and key release events.
|
||||
QApplication::instance()->installEventFilter(this);
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
bool QtEventToAzInputMapper::HandlesInputEvent(const AzFramework::InputChannel& channel) const
|
||||
@@ -317,6 +343,19 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::s32 QtEventToAzInputMapper::GetPriority() const
|
||||
{
|
||||
return AzFramework::InputChannelEventListener::GetPriorityLast();
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::OnInputChannelEvent(const AzFramework::InputChannel& inputChannel, bool& hasBeenConsumed)
|
||||
{
|
||||
if (m_enabled && hasBeenConsumed)
|
||||
{
|
||||
m_lastConsumedInputChannelIdCrc32 = inputChannel.GetInputChannelId().GetNameCrc32();
|
||||
}
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::NotifyUpdateChannelIfNotIdle(const AzFramework::InputChannel* channel, QEvent* event)
|
||||
{
|
||||
if (channel->GetState() != AzFramework::InputChannel::State::Idle)
|
||||
@@ -357,6 +396,9 @@ namespace AzToolsFramework
|
||||
|
||||
if (buttonChannel)
|
||||
{
|
||||
// reset the consumed event cache so the chain of calls from UpdateState below can properly update it, if necessary
|
||||
m_lastConsumedInputChannelIdCrc32 = 0;
|
||||
|
||||
if (mouseEvent->type() != QEvent::Type::MouseButtonRelease)
|
||||
{
|
||||
buttonChannel->UpdateState(true);
|
||||
@@ -366,7 +408,16 @@ namespace AzToolsFramework
|
||||
buttonChannel->UpdateState(false);
|
||||
}
|
||||
|
||||
NotifyUpdateChannelIfNotIdle(buttonChannel, mouseEvent);
|
||||
if (m_lastConsumedInputChannelIdCrc32 == buttonChannel->GetInputChannelId().GetNameCrc32())
|
||||
{
|
||||
// a standard az-input handler consumed the event so mark it as such
|
||||
mouseEvent->accept();
|
||||
}
|
||||
else
|
||||
{
|
||||
// only notify if not consumed elsewhere
|
||||
NotifyUpdateChannelIfNotIdle(buttonChannel, mouseEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,16 +459,24 @@ namespace AzToolsFramework
|
||||
|
||||
void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent)
|
||||
{
|
||||
// Ignore key repeat events, they're unrelated to actual physical button presses.
|
||||
const Qt::Key key = static_cast<Qt::Key>(keyEvent->key());
|
||||
const QEvent::Type eventType = keyEvent->type();
|
||||
|
||||
// special handling for text events in edit mode
|
||||
if (HandleTextEvent(eventType, key, keyEvent->text(), keyEvent->isAutoRepeat()))
|
||||
{
|
||||
keyEvent->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore key repeat events for non-text, they're unrelated to actual physical button presses.
|
||||
if (keyEvent->isAutoRepeat())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const Qt::Key key = static_cast<Qt::Key>(keyEvent->key());
|
||||
|
||||
// For ShortcutEvent, only continue processing if we're in the HighPriorityKeys set.
|
||||
if (keyEvent->type() != QEvent::Type::ShortcutOverride || m_highPriorityKeys.find(key) != m_highPriorityKeys.end())
|
||||
if (eventType != QEvent::Type::ShortcutOverride || m_highPriorityKeys.find(key) != m_highPriorityKeys.end())
|
||||
{
|
||||
if (auto keyIt = m_keyMappings.find(key); keyIt != m_keyMappings.end())
|
||||
{
|
||||
@@ -425,7 +484,7 @@ namespace AzToolsFramework
|
||||
|
||||
if (keyChannel)
|
||||
{
|
||||
if (keyEvent->type() == QEvent::Type::KeyPress || keyEvent->type() == QEvent::Type::ShortcutOverride)
|
||||
if (eventType == QEvent::Type::KeyPress || eventType == QEvent::Type::ShortcutOverride)
|
||||
{
|
||||
keyChannel->UpdateState(true);
|
||||
}
|
||||
@@ -451,8 +510,22 @@ namespace AzToolsFramework
|
||||
{
|
||||
wheelAngle = angleDelta.y();
|
||||
}
|
||||
|
||||
// reset the consumed event cache so the chain of calls from ProcessRawInputEvent below can properly update it, if necessary
|
||||
m_lastConsumedInputChannelIdCrc32 = 0;
|
||||
|
||||
cursorZChannel->ProcessRawInputEvent(aznumeric_cast<float>(wheelAngle));
|
||||
NotifyUpdateChannelIfNotIdle(cursorZChannel, wheelEvent);
|
||||
|
||||
if (m_lastConsumedInputChannelIdCrc32 == cursorZChannel->GetInputChannelId().GetNameCrc32())
|
||||
{
|
||||
// a standard az-input handler consumed the event so mark it as such
|
||||
wheelEvent->accept();
|
||||
}
|
||||
else
|
||||
{
|
||||
// only notify if not consumed elsewhere
|
||||
NotifyUpdateChannelIfNotIdle(cursorZChannel, wheelEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::ClearInputChannels(QEvent* event)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <AzFramework/Input/Channels/InputChannelDigitalWithSharedPosition2D.h>
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
|
||||
#include <AzFramework/Input/Events/InputChannelEventListener.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
#include <QEvent>
|
||||
@@ -34,7 +34,9 @@ namespace AzToolsFramework
|
||||
{
|
||||
//! Maps events from the Qt input system to synthetic InputChannels in AzFramework
|
||||
//! that can be used by AzFramework::ViewportControllers.
|
||||
class QtEventToAzInputMapper final : public QObject
|
||||
class QtEventToAzInputMapper final
|
||||
: public QObject
|
||||
, public AzFramework::InputChannelNotificationBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -69,6 +71,11 @@ namespace AzToolsFramework
|
||||
//! \param event The underlying Qt event that triggered this change, if applicable.
|
||||
void InputChannelUpdated(const AzFramework::InputChannel* channel, QEvent* event);
|
||||
|
||||
protected:
|
||||
// AzFramework::InputChannelNotificationBus overrides ...
|
||||
AZ::s32 GetPriority() const override;
|
||||
void OnInputChannelEvent(const AzFramework::InputChannel& inputChannel, bool& hasBeenConsumed) override;
|
||||
|
||||
private:
|
||||
// Gets an input channel of the specified type by ID.
|
||||
template<class TInputChannel>
|
||||
@@ -161,6 +168,8 @@ namespace AzToolsFramework
|
||||
AZStd::unordered_set<Qt::Key> m_highPriorityKeys;
|
||||
// A lookup table for AZ input channel ID -> physical input channel on our mouse or keyboard device.
|
||||
AZStd::unordered_map<AzFramework::InputChannelId, AzFramework::InputChannel*> m_channels;
|
||||
// The crc32 of the last consumed input event's channel id.
|
||||
AZ::Crc32 m_lastConsumedInputChannelIdCrc32 = 0;
|
||||
// Where the mouse cursor was at the last cursor event.
|
||||
QPoint m_previousGlobalCursorPosition;
|
||||
// The source widget to map events from, used to calculate the relative mouse position within the widget bounds.
|
||||
|
||||
+34
@@ -56,6 +56,40 @@ namespace UnitTest
|
||||
QApplication::sendEvent(widget, &mouseMoveEvent);
|
||||
}
|
||||
|
||||
void MouseScroll(QWidget* widget, QPoint localEventPosition, QPoint wheelDelta,
|
||||
Qt::MouseButtons mouseButtons, Qt::KeyboardModifiers keyboardModifiers)
|
||||
{
|
||||
const QPoint globalEventPos = widget->mapToGlobal(localEventPosition);
|
||||
const QPoint zero = QPoint();
|
||||
|
||||
QWheelEvent wheelEventBegin(globalEventPos, zero, zero, wheelDelta, mouseButtons, keyboardModifiers, Qt::ScrollBegin, false);
|
||||
QApplication::sendEvent(widget, &wheelEventBegin);
|
||||
|
||||
QWheelEvent wheelEventUpdate(globalEventPos, zero, zero, wheelDelta, mouseButtons, keyboardModifiers, Qt::ScrollUpdate, false);
|
||||
QApplication::sendEvent(widget, &wheelEventUpdate);
|
||||
|
||||
QWheelEvent wheelEventEnd(globalEventPos, zero, zero, zero, mouseButtons, keyboardModifiers, Qt::ScrollEnd, false);
|
||||
QApplication::sendEvent(widget, &wheelEventEnd);
|
||||
}
|
||||
|
||||
AZStd::string QtKeyToAzString(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
QKeySequence keySequence = QKeySequence(key);
|
||||
QString keyText = keySequence.toString();
|
||||
|
||||
// QKeySequence seems to uppercase alpha keys regardless of shift-modifier
|
||||
if (modifiers == Qt::NoModifier && keyText.isUpper())
|
||||
{
|
||||
keyText = keyText.toLower();
|
||||
}
|
||||
else if (modifiers != Qt::ShiftModifier)
|
||||
{
|
||||
keyText = QString();
|
||||
}
|
||||
|
||||
return AZStd::string(keyText.toUtf8().data());
|
||||
}
|
||||
|
||||
bool TestWidget::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
AZ_UNUSED(watched);
|
||||
|
||||
+14
@@ -77,6 +77,20 @@ namespace UnitTest
|
||||
/// @param mouseButton The button to be held during the move.
|
||||
void MouseMove(QWidget* widget, const QPoint& initialPosition, const QPoint& mouseDelta, Qt::MouseButton mouseButton = Qt::NoButton);
|
||||
|
||||
/// Performs a full series (begin, update, end) of mouse wheel events on the provided widget.
|
||||
/// @param widget The widget to perform the mouse wheel events on.
|
||||
/// @param localEventPosition The position of the mouse relative to the widget (will be remapped to a global position internally).
|
||||
/// @param wheelDelta How far to move the mouse (note: mouseDelta may be zero and the mouse will only be moved to initialPosition).
|
||||
/// @param mouseButtons Optional mouse buttons to include during the wheel events, defaults to Qt::NoButton
|
||||
/// @param keyboardModifiers Optional keyboard modifiers to include during the wheel events, defaults to Qt::NoModifier
|
||||
void MouseScroll(QWidget* widget, QPoint localEventPosition, QPoint wheelDelta,
|
||||
Qt::MouseButtons mouseButtons = Qt::NoButton, Qt::KeyboardModifiers keyboardModifiers = Qt::NoModifier);
|
||||
|
||||
/// Convert a Qt::Key + optional modifiers to the printable text of the key sequence
|
||||
/// @param key The widget to perform the mouse wheel event on.
|
||||
/// @param modifiers Optional keyboard modifiers to include during the wheel events, defaults to Qt::NoModifier
|
||||
AZStd::string QtKeyToAzString(Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
|
||||
|
||||
/// Test widget to store QActions generated by EditorTransformComponentSelection.
|
||||
class TestWidget : public QWidget
|
||||
{
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Input/QtEventToAzInputMapper.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
|
||||
#include <AzFramework/Input/Events/InputTextEventListener.h>
|
||||
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
static bool IsMouseButton(const AzFramework::InputChannelId& inputChannelId)
|
||||
{
|
||||
const auto& buttons = AzFramework::InputDeviceMouse::Button::All;
|
||||
const auto& it = AZStd::find(buttons.cbegin(), buttons.cend(), inputChannelId);
|
||||
return it != buttons.cend();
|
||||
}
|
||||
|
||||
class QtEventToAzInputMapperFixture
|
||||
: public AllocatorsTestFixture
|
||||
, public AzFramework::InputChannelNotificationBus::Handler
|
||||
, public AzFramework::InputTextNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
static inline constexpr QSize WidgetSize = QSize(1920, 1080);
|
||||
static inline constexpr int TestDeviceIdSeed = 4321;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
AllocatorsTestFixture::SetUp();
|
||||
|
||||
m_rootWidget = AZStd::make_unique<QWidget>();
|
||||
m_rootWidget->setFixedSize(WidgetSize);
|
||||
m_rootWidget->move(0, 0);
|
||||
|
||||
m_inputChannelMapper = AZStd::make_unique<AzToolsFramework::QtEventToAzInputMapper>(m_rootWidget.get(), TestDeviceIdSeed);
|
||||
|
||||
// listen for events signaled from QtEventToAzInputMapper and forward to the controller list
|
||||
QObject::connect(m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(),
|
||||
[this]([[maybe_unused]] const AzFramework::InputChannel* inputChannel, QEvent* event)
|
||||
{
|
||||
const QEvent::Type eventType = event->type();
|
||||
|
||||
if (eventType == QEvent::Type::MouseButtonPress ||
|
||||
eventType == QEvent::Type::MouseButtonRelease ||
|
||||
eventType == QEvent::Type::MouseButtonDblClick)
|
||||
{
|
||||
m_signalEvents.push_back(QtEventInfo(static_cast<QMouseEvent*>(event)));
|
||||
event->accept();
|
||||
}
|
||||
else if (eventType == QEvent::Type::Wheel)
|
||||
{
|
||||
m_signalEvents.push_back(QtEventInfo(static_cast<QWheelEvent*>(event)));
|
||||
event->accept();
|
||||
}
|
||||
else if (eventType == QEvent::Type::KeyPress ||
|
||||
eventType == QEvent::Type::KeyRelease ||
|
||||
eventType == QEvent::Type::ShortcutOverride)
|
||||
{
|
||||
m_signalEvents.push_back(QtEventInfo(static_cast<QKeyEvent*>(event)));
|
||||
event->accept();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_inputChannelMapper.reset();
|
||||
|
||||
m_rootWidget.reset();
|
||||
|
||||
AllocatorsTestFixture::TearDown();
|
||||
}
|
||||
|
||||
void OnInputChannelEvent(const AzFramework::InputChannel& inputChannel, bool& hasBeenConsumed) override
|
||||
{
|
||||
AZ_Assert(hasBeenConsumed == false, "Unexpected input event consumed elsewhere during QtEventToAzInputMapper tests");
|
||||
|
||||
const AzFramework::InputChannelId& inputChannelId = inputChannel.GetInputChannelId();
|
||||
const AzFramework::InputDeviceId& inputDeviceId = inputChannel.GetInputDevice().GetInputDeviceId();
|
||||
|
||||
if (AzFramework::InputDeviceMouse::IsMouseDevice(inputDeviceId))
|
||||
{
|
||||
if (IsMouseButton(inputChannelId))
|
||||
{
|
||||
m_azChannelEvents.push_back(AzEventInfo(inputChannel));
|
||||
hasBeenConsumed = m_captureAzEvents;
|
||||
}
|
||||
else if (inputChannelId == AzFramework::InputDeviceMouse::Movement::Z)
|
||||
{
|
||||
m_azChannelEvents.push_back(AzEventInfo(inputChannel));
|
||||
hasBeenConsumed = m_captureAzEvents;
|
||||
}
|
||||
}
|
||||
else if (AzFramework::InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
|
||||
{
|
||||
m_azChannelEvents.push_back(AzEventInfo(inputChannel));
|
||||
hasBeenConsumed = m_captureAzEvents;
|
||||
}
|
||||
}
|
||||
|
||||
void OnInputTextEvent(const AZStd::string& textUtf8, bool& hasBeenConsumed) override
|
||||
{
|
||||
AZ_Assert(hasBeenConsumed == false, "Unexpected text event consumed elsewhere during QtEventToAzInputMapper tests");
|
||||
|
||||
m_azTextEvents.push_back(textUtf8);
|
||||
hasBeenConsumed = m_captureTextEvents;
|
||||
}
|
||||
|
||||
// simple structure for caching minimal QtEvent data necessary for testing
|
||||
struct QtEventInfo
|
||||
{
|
||||
explicit QtEventInfo(QMouseEvent* mouseEvent)
|
||||
: m_eventType(mouseEvent->type())
|
||||
, m_button(mouseEvent->button())
|
||||
{
|
||||
}
|
||||
|
||||
explicit QtEventInfo(QWheelEvent* mouseWheelEvent)
|
||||
: m_eventType(mouseWheelEvent->type())
|
||||
, m_scrollPhase(mouseWheelEvent->phase())
|
||||
{
|
||||
}
|
||||
|
||||
explicit QtEventInfo(QKeyEvent* keyEvent)
|
||||
: m_eventType(keyEvent->type())
|
||||
, m_key(keyEvent->key())
|
||||
{
|
||||
}
|
||||
|
||||
QEvent::Type m_eventType{ QEvent::None };
|
||||
Qt::MouseButton m_button{ Qt::NoButton };
|
||||
Qt::ScrollPhase m_scrollPhase{ Qt::NoScrollPhase };
|
||||
int m_key{ 0 };
|
||||
};
|
||||
|
||||
// simple structure for caching minimal AzInput event data necessary for testing
|
||||
struct AzEventInfo
|
||||
{
|
||||
AzEventInfo() = delete;
|
||||
explicit AzEventInfo(const AzFramework::InputChannel& inputChannel)
|
||||
: m_inputChannelId(inputChannel.GetInputChannelId())
|
||||
, m_isActive(inputChannel.IsActive())
|
||||
{
|
||||
}
|
||||
|
||||
AzFramework::InputChannelId m_inputChannelId;
|
||||
bool m_isActive;
|
||||
};
|
||||
|
||||
|
||||
AZStd::unique_ptr<QWidget> m_rootWidget;
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::QtEventToAzInputMapper> m_inputChannelMapper;
|
||||
|
||||
AZStd::vector<QtEventInfo> m_signalEvents;
|
||||
AZStd::vector<AzEventInfo> m_azChannelEvents;
|
||||
AZStd::vector<AZStd::string> m_azTextEvents;
|
||||
|
||||
bool m_captureAzEvents{ false };
|
||||
bool m_captureTextEvents{ false };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Qt event forwarding through the internal signal handler test
|
||||
TEST_F(QtEventToAzInputMapperFixture, MouseWheel_NoAzHandlers_ReceivedThreeSignalAndZeroAzChannelEvents)
|
||||
{
|
||||
// setup
|
||||
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
|
||||
const QPoint scrollDelta = QPoint(10, 10);
|
||||
|
||||
MouseScroll(m_rootWidget.get(), mouseEventPos, scrollDelta);
|
||||
|
||||
// qt validation
|
||||
ASSERT_EQ(m_signalEvents.size(), 3);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::Wheel);
|
||||
EXPECT_EQ(m_signalEvents[0].m_scrollPhase, Qt::ScrollBegin);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::Wheel);
|
||||
EXPECT_EQ(m_signalEvents[1].m_scrollPhase, Qt::ScrollUpdate);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[2].m_eventType, QEvent::Type::Wheel);
|
||||
EXPECT_EQ(m_signalEvents[2].m_scrollPhase, Qt::ScrollEnd);
|
||||
|
||||
// az validation
|
||||
EXPECT_EQ(m_azChannelEvents.size(), 0);
|
||||
}
|
||||
|
||||
// Qt event to AzInput event conversion test
|
||||
TEST_F(QtEventToAzInputMapperFixture, MouseWheel_AzHandlerNotCaptured_ReceivedThreeSignalAndThreeAzChannelEvents)
|
||||
{
|
||||
// setup
|
||||
const AzFramework::InputChannelId mouseWheelId = AzFramework::InputDeviceMouse::Movement::Z;
|
||||
const char* mouseWheelChannelName = mouseWheelId.GetName();
|
||||
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
|
||||
m_captureAzEvents = false;
|
||||
|
||||
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
|
||||
const QPoint scrollDelta = QPoint(10, 10);
|
||||
|
||||
MouseScroll(m_rootWidget.get(), mouseEventPos, scrollDelta);
|
||||
|
||||
// qt validation
|
||||
ASSERT_EQ(m_signalEvents.size(), 3);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::Wheel);
|
||||
EXPECT_EQ(m_signalEvents[0].m_scrollPhase, Qt::ScrollBegin);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::Wheel);
|
||||
EXPECT_EQ(m_signalEvents[1].m_scrollPhase, Qt::ScrollUpdate);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[2].m_eventType, QEvent::Type::Wheel);
|
||||
EXPECT_EQ(m_signalEvents[2].m_scrollPhase, Qt::ScrollEnd);
|
||||
|
||||
// az validation
|
||||
ASSERT_EQ(m_azChannelEvents.size(), 3);
|
||||
|
||||
EXPECT_STREQ(m_azChannelEvents[0].m_inputChannelId.GetName(), mouseWheelChannelName);
|
||||
EXPECT_STREQ(m_azChannelEvents[1].m_inputChannelId.GetName(), mouseWheelChannelName);
|
||||
EXPECT_STREQ(m_azChannelEvents[2].m_inputChannelId.GetName(), mouseWheelChannelName);
|
||||
|
||||
// cleanup
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
// AzInput event handler consumption test
|
||||
TEST_F(QtEventToAzInputMapperFixture, MouseWheel_AzHandlerCaptured_ReceivedZeroSignalAndThreeAzChannelEvents)
|
||||
{
|
||||
// setup
|
||||
const AzFramework::InputChannelId mouseWheelId = AzFramework::InputDeviceMouse::Movement::Z;
|
||||
const char* mouseWheelChannelName = mouseWheelId.GetName();
|
||||
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
|
||||
m_captureAzEvents = true;
|
||||
|
||||
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
|
||||
const QPoint scrollDelta = QPoint(10, 10);
|
||||
|
||||
MouseScroll(m_rootWidget.get(), mouseEventPos, scrollDelta);
|
||||
|
||||
// qt validation
|
||||
EXPECT_EQ(m_signalEvents.size(), 0);
|
||||
|
||||
// az validation
|
||||
ASSERT_EQ(m_azChannelEvents.size(), 3);
|
||||
|
||||
EXPECT_STREQ(m_azChannelEvents[0].m_inputChannelId.GetName(), mouseWheelChannelName);
|
||||
EXPECT_STREQ(m_azChannelEvents[1].m_inputChannelId.GetName(), mouseWheelChannelName);
|
||||
EXPECT_STREQ(m_azChannelEvents[2].m_inputChannelId.GetName(), mouseWheelChannelName);
|
||||
|
||||
// cleanup
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct MouseButtonIdsParam
|
||||
{
|
||||
Qt::MouseButton m_qt;
|
||||
AzFramework::InputChannelId m_az;
|
||||
};
|
||||
|
||||
class MouseButtonParamQtEventToAzInputMapperFixture
|
||||
: public QtEventToAzInputMapperFixture
|
||||
, public ::testing::WithParamInterface<MouseButtonIdsParam>
|
||||
{
|
||||
};
|
||||
|
||||
// Qt event forwarding through the internal signal handler test
|
||||
TEST_P(MouseButtonParamQtEventToAzInputMapperFixture, MouseClick_NoAzHandlers_ReceivedTwoSignalAndZeroAzChannelEvents)
|
||||
{
|
||||
// setup
|
||||
const MouseButtonIdsParam mouseButtonIds = GetParam();
|
||||
|
||||
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
|
||||
QTest::mouseClick(m_rootWidget.get(), mouseButtonIds.m_qt, Qt::NoModifier, mouseEventPos);
|
||||
|
||||
// qt validation
|
||||
ASSERT_EQ(m_signalEvents.size(), 2);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::MouseButtonPress);
|
||||
EXPECT_EQ(m_signalEvents[0].m_button, mouseButtonIds.m_qt);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::MouseButtonRelease);
|
||||
EXPECT_EQ(m_signalEvents[1].m_button, mouseButtonIds.m_qt);
|
||||
|
||||
// az validation
|
||||
EXPECT_EQ(m_azChannelEvents.size(), 0);
|
||||
}
|
||||
|
||||
// Qt event to AzInput event conversion test
|
||||
TEST_P(MouseButtonParamQtEventToAzInputMapperFixture, MouseClick_AzHandlerNotCaptured_ReceivedTwoSignalAndTwoAzChannelEvents)
|
||||
{
|
||||
// setup
|
||||
const MouseButtonIdsParam mouseButtonIds = GetParam();
|
||||
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
|
||||
m_captureAzEvents = false;
|
||||
|
||||
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
|
||||
QTest::mouseClick(m_rootWidget.get(), mouseButtonIds.m_qt, Qt::NoModifier, mouseEventPos);
|
||||
|
||||
// qt validation
|
||||
ASSERT_EQ(m_signalEvents.size(), 2);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::MouseButtonPress);
|
||||
EXPECT_EQ(m_signalEvents[0].m_button, mouseButtonIds.m_qt);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::MouseButtonRelease);
|
||||
EXPECT_EQ(m_signalEvents[1].m_button, mouseButtonIds.m_qt);
|
||||
|
||||
// az validation
|
||||
ASSERT_EQ(m_azChannelEvents.size(), 2);
|
||||
|
||||
EXPECT_STREQ(m_azChannelEvents[0].m_inputChannelId.GetName(), mouseButtonIds.m_az.GetName());
|
||||
EXPECT_TRUE(m_azChannelEvents[0].m_isActive);
|
||||
|
||||
EXPECT_STREQ(m_azChannelEvents[1].m_inputChannelId.GetName(), mouseButtonIds.m_az.GetName());
|
||||
EXPECT_FALSE(m_azChannelEvents[1].m_isActive);
|
||||
|
||||
// cleanup
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
// AzInput event handler consumption test
|
||||
TEST_P(MouseButtonParamQtEventToAzInputMapperFixture, MouseClick_AzHandlerCaptured_ReceivedZeroSignalAndTwoAzChannelEvents)
|
||||
{
|
||||
// setup
|
||||
const MouseButtonIdsParam mouseButtonIds = GetParam();
|
||||
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
|
||||
m_captureAzEvents = true;
|
||||
|
||||
const QPoint mouseEventPos = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
|
||||
QTest::mouseClick(m_rootWidget.get(), mouseButtonIds.m_qt, Qt::NoModifier, mouseEventPos);
|
||||
|
||||
// qt validation
|
||||
EXPECT_EQ(m_signalEvents.size(), 0);
|
||||
|
||||
// az validation
|
||||
ASSERT_EQ(m_azChannelEvents.size(), 2);
|
||||
|
||||
EXPECT_STREQ(m_azChannelEvents[0].m_inputChannelId.GetName(), mouseButtonIds.m_az.GetName());
|
||||
EXPECT_TRUE(m_azChannelEvents[0].m_isActive);
|
||||
|
||||
EXPECT_STREQ(m_azChannelEvents[1].m_inputChannelId.GetName(), mouseButtonIds.m_az.GetName());
|
||||
EXPECT_FALSE(m_azChannelEvents[1].m_isActive);
|
||||
|
||||
// cleanup
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(All, MouseButtonParamQtEventToAzInputMapperFixture,
|
||||
testing::Values(
|
||||
MouseButtonIdsParam{ Qt::MouseButton::LeftButton, AzFramework::InputDeviceMouse::Button::Left },
|
||||
MouseButtonIdsParam{ Qt::MouseButton::RightButton, AzFramework::InputDeviceMouse::Button::Right },
|
||||
MouseButtonIdsParam{ Qt::MouseButton::MiddleButton, AzFramework::InputDeviceMouse::Button::Middle }
|
||||
),
|
||||
[](const ::testing::TestParamInfo<MouseButtonIdsParam>& info)
|
||||
{
|
||||
return info.param.m_az.GetName();
|
||||
}
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct KeyEventIdsParam
|
||||
{
|
||||
Qt::Key m_qt;
|
||||
AzFramework::InputChannelId m_az;
|
||||
};
|
||||
|
||||
class PrintableKeyEventParamQtEventToAzInputMapperFixture
|
||||
: public QtEventToAzInputMapperFixture
|
||||
, public ::testing::WithParamInterface<KeyEventIdsParam>
|
||||
{
|
||||
};
|
||||
|
||||
// Qt event forwarding through the internal signal handler test
|
||||
TEST_P(PrintableKeyEventParamQtEventToAzInputMapperFixture, KeyClick_NoAzHandlers_ReceivedTwoSignalAndZeroAzEvents)
|
||||
{
|
||||
// setup
|
||||
const KeyEventIdsParam keyEventIds = GetParam();
|
||||
const Qt::KeyboardModifiers modifiers = Qt::NoModifier;
|
||||
|
||||
QTest::keyClick(m_rootWidget.get(), keyEventIds.m_qt, modifiers);
|
||||
|
||||
// qt validation
|
||||
ASSERT_EQ(m_signalEvents.size(), 2);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::KeyPress);
|
||||
EXPECT_EQ(m_signalEvents[0].m_key, keyEventIds.m_qt);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::KeyRelease);
|
||||
EXPECT_EQ(m_signalEvents[1].m_key, keyEventIds.m_qt);
|
||||
|
||||
// az validation
|
||||
EXPECT_EQ(m_azChannelEvents.size(), 0);
|
||||
EXPECT_EQ(m_azTextEvents.size(), 0);
|
||||
}
|
||||
|
||||
// Qt event to AzInput event conversion test
|
||||
TEST_P(PrintableKeyEventParamQtEventToAzInputMapperFixture, KeyClick_AzHandlersNotCaptured_ReceivedTwoSignalAndThreeAzEvents)
|
||||
{
|
||||
// setup
|
||||
const KeyEventIdsParam keyEventIds = GetParam();
|
||||
const Qt::KeyboardModifiers modifiers = Qt::NoModifier;
|
||||
|
||||
AZStd::string keyAsText = QtKeyToAzString(keyEventIds.m_qt, modifiers);
|
||||
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusConnect();
|
||||
m_captureAzEvents = false;
|
||||
|
||||
AzFramework::InputTextNotificationBus::Handler::BusConnect();
|
||||
m_captureAzEvents = false;
|
||||
|
||||
QTest::keyClick(m_rootWidget.get(), keyEventIds.m_qt, modifiers);
|
||||
|
||||
// qt validation
|
||||
ASSERT_EQ(m_signalEvents.size(), 2);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[0].m_eventType, QEvent::Type::KeyPress);
|
||||
EXPECT_EQ(m_signalEvents[0].m_key, keyEventIds.m_qt);
|
||||
|
||||
EXPECT_EQ(m_signalEvents[1].m_eventType, QEvent::Type::KeyRelease);
|
||||
EXPECT_EQ(m_signalEvents[1].m_key, keyEventIds.m_qt);
|
||||
|
||||
// az validation
|
||||
ASSERT_EQ(m_azTextEvents.size(), 1);
|
||||
|
||||
EXPECT_STREQ(m_azTextEvents[0].c_str(), keyAsText.c_str());
|
||||
|
||||
ASSERT_EQ(m_azChannelEvents.size(), 2);
|
||||
|
||||
EXPECT_STREQ(m_azChannelEvents[0].m_inputChannelId.GetName(), keyEventIds.m_az.GetName());
|
||||
EXPECT_TRUE(m_azChannelEvents[0].m_isActive);
|
||||
|
||||
EXPECT_STREQ(m_azChannelEvents[1].m_inputChannelId.GetName(), keyEventIds.m_az.GetName());
|
||||
EXPECT_FALSE(m_azChannelEvents[1].m_isActive);
|
||||
|
||||
// cleanup
|
||||
AzFramework::InputTextNotificationBus::Handler::BusDisconnect();
|
||||
AzFramework::InputChannelNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(All, PrintableKeyEventParamQtEventToAzInputMapperFixture,
|
||||
testing::Values(
|
||||
KeyEventIdsParam{ Qt::Key_0, AzFramework::InputDeviceKeyboard::Key::Alphanumeric0 },
|
||||
KeyEventIdsParam{ Qt::Key_1, AzFramework::InputDeviceKeyboard::Key::Alphanumeric1 },
|
||||
KeyEventIdsParam{ Qt::Key_2, AzFramework::InputDeviceKeyboard::Key::Alphanumeric2 },
|
||||
KeyEventIdsParam{ Qt::Key_3, AzFramework::InputDeviceKeyboard::Key::Alphanumeric3 },
|
||||
KeyEventIdsParam{ Qt::Key_4, AzFramework::InputDeviceKeyboard::Key::Alphanumeric4 },
|
||||
KeyEventIdsParam{ Qt::Key_5, AzFramework::InputDeviceKeyboard::Key::Alphanumeric5 },
|
||||
KeyEventIdsParam{ Qt::Key_6, AzFramework::InputDeviceKeyboard::Key::Alphanumeric6 },
|
||||
KeyEventIdsParam{ Qt::Key_7, AzFramework::InputDeviceKeyboard::Key::Alphanumeric7 },
|
||||
KeyEventIdsParam{ Qt::Key_8, AzFramework::InputDeviceKeyboard::Key::Alphanumeric8 },
|
||||
KeyEventIdsParam{ Qt::Key_9, AzFramework::InputDeviceKeyboard::Key::Alphanumeric9 },
|
||||
|
||||
KeyEventIdsParam{ Qt::Key_A, AzFramework::InputDeviceKeyboard::Key::AlphanumericA },
|
||||
KeyEventIdsParam{ Qt::Key_B, AzFramework::InputDeviceKeyboard::Key::AlphanumericB },
|
||||
KeyEventIdsParam{ Qt::Key_C, AzFramework::InputDeviceKeyboard::Key::AlphanumericC },
|
||||
KeyEventIdsParam{ Qt::Key_D, AzFramework::InputDeviceKeyboard::Key::AlphanumericD },
|
||||
KeyEventIdsParam{ Qt::Key_E, AzFramework::InputDeviceKeyboard::Key::AlphanumericE },
|
||||
KeyEventIdsParam{ Qt::Key_F, AzFramework::InputDeviceKeyboard::Key::AlphanumericF },
|
||||
KeyEventIdsParam{ Qt::Key_G, AzFramework::InputDeviceKeyboard::Key::AlphanumericG },
|
||||
KeyEventIdsParam{ Qt::Key_H, AzFramework::InputDeviceKeyboard::Key::AlphanumericH },
|
||||
KeyEventIdsParam{ Qt::Key_I, AzFramework::InputDeviceKeyboard::Key::AlphanumericI },
|
||||
KeyEventIdsParam{ Qt::Key_J, AzFramework::InputDeviceKeyboard::Key::AlphanumericJ },
|
||||
KeyEventIdsParam{ Qt::Key_K, AzFramework::InputDeviceKeyboard::Key::AlphanumericK },
|
||||
KeyEventIdsParam{ Qt::Key_L, AzFramework::InputDeviceKeyboard::Key::AlphanumericL },
|
||||
KeyEventIdsParam{ Qt::Key_M, AzFramework::InputDeviceKeyboard::Key::AlphanumericM },
|
||||
KeyEventIdsParam{ Qt::Key_N, AzFramework::InputDeviceKeyboard::Key::AlphanumericN },
|
||||
KeyEventIdsParam{ Qt::Key_O, AzFramework::InputDeviceKeyboard::Key::AlphanumericO },
|
||||
KeyEventIdsParam{ Qt::Key_P, AzFramework::InputDeviceKeyboard::Key::AlphanumericP },
|
||||
KeyEventIdsParam{ Qt::Key_Q, AzFramework::InputDeviceKeyboard::Key::AlphanumericQ },
|
||||
KeyEventIdsParam{ Qt::Key_R, AzFramework::InputDeviceKeyboard::Key::AlphanumericR },
|
||||
KeyEventIdsParam{ Qt::Key_S, AzFramework::InputDeviceKeyboard::Key::AlphanumericS },
|
||||
KeyEventIdsParam{ Qt::Key_T, AzFramework::InputDeviceKeyboard::Key::AlphanumericT },
|
||||
KeyEventIdsParam{ Qt::Key_U, AzFramework::InputDeviceKeyboard::Key::AlphanumericU },
|
||||
KeyEventIdsParam{ Qt::Key_V, AzFramework::InputDeviceKeyboard::Key::AlphanumericV },
|
||||
KeyEventIdsParam{ Qt::Key_W, AzFramework::InputDeviceKeyboard::Key::AlphanumericW },
|
||||
KeyEventIdsParam{ Qt::Key_X, AzFramework::InputDeviceKeyboard::Key::AlphanumericX },
|
||||
KeyEventIdsParam{ Qt::Key_Y, AzFramework::InputDeviceKeyboard::Key::AlphanumericY },
|
||||
KeyEventIdsParam{ Qt::Key_Z, AzFramework::InputDeviceKeyboard::Key::AlphanumericZ },
|
||||
|
||||
// these may need to be special cased due to the printable text conversion
|
||||
//KeyEventIdsParam{ Qt::Key_Space, AzFramework::InputDeviceKeyboard::Key::EditSpace },
|
||||
//KeyEventIdsParam{ Qt::Key_Tab, AzFramework::InputDeviceKeyboard::Key::EditTab },
|
||||
|
||||
KeyEventIdsParam{ Qt::Key_Apostrophe, AzFramework::InputDeviceKeyboard::Key::PunctuationApostrophe },
|
||||
KeyEventIdsParam{ Qt::Key_Backslash, AzFramework::InputDeviceKeyboard::Key::PunctuationBackslash },
|
||||
KeyEventIdsParam{ Qt::Key_BracketLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketL },
|
||||
KeyEventIdsParam{ Qt::Key_BracketRight, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketR },
|
||||
KeyEventIdsParam{ Qt::Key_Comma, AzFramework::InputDeviceKeyboard::Key::PunctuationComma },
|
||||
KeyEventIdsParam{ Qt::Key_Equal, AzFramework::InputDeviceKeyboard::Key::PunctuationEquals },
|
||||
KeyEventIdsParam{ Qt::Key_hyphen, AzFramework::InputDeviceKeyboard::Key::PunctuationHyphen },
|
||||
KeyEventIdsParam{ Qt::Key_Period, AzFramework::InputDeviceKeyboard::Key::PunctuationPeriod },
|
||||
KeyEventIdsParam{ Qt::Key_Semicolon, AzFramework::InputDeviceKeyboard::Key::PunctuationSemicolon },
|
||||
KeyEventIdsParam{ Qt::Key_Slash, AzFramework::InputDeviceKeyboard::Key::PunctuationSlash },
|
||||
KeyEventIdsParam{ Qt::Key_QuoteLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationTilde }
|
||||
),
|
||||
[](const ::testing::TestParamInfo<KeyEventIdsParam>& info)
|
||||
{
|
||||
return info.param.m_az.GetName();
|
||||
}
|
||||
);
|
||||
} // namespace UnitTest
|
||||
@@ -48,6 +48,7 @@ set(FILES
|
||||
FocusMode/EditorFocusModeSelectionTests.cpp
|
||||
FocusMode/EditorFocusModeTests.cpp
|
||||
GenericComponentWrapperTest.cpp
|
||||
Input/QtEventToAzInputMapperTests.cpp
|
||||
InstanceDataHierarchy.cpp
|
||||
IntegerPrimtitiveTestConfig.h
|
||||
LogLines.cpp
|
||||
|
||||
Reference in New Issue
Block a user