From 8e51a4763f969c205ce3b4c7b7adf58575675b1a Mon Sep 17 00:00:00 2001 From: nvsickle Date: Wed, 16 Jun 2021 12:01:56 -0700 Subject: [PATCH 01/12] Add InputChannel API to disable forwarding events to the underyling device Signed-off-by: nvsickle --- .../Input/Channels/InputChannel.cpp | 20 ++++++++++++++++++- .../AzFramework/Input/Channels/InputChannel.h | 11 ++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.cpp b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.cpp index c274bbf491..81dec1cac7 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.cpp @@ -327,7 +327,7 @@ namespace AzFramework break; } - if (m_state != State::Idle) + if (m_state != State::Idle && m_shouldDispatchEvents) { bool hasBeenConsumed = false; InputChannelNotificationBus::Broadcast(&InputChannelNotifications::OnInputChannelEvent, @@ -350,4 +350,22 @@ namespace AzFramework // Directly return the channel to the 'Idle' state m_state = State::Idle; } + + //////////////////////////////////////////////////////////////////////////////////////////////// + void InputChannel::SetUpdateEventsEnabled(bool enabled) + { + if (m_shouldDispatchEvents != enabled) + { + m_shouldDispatchEvents = enabled; + const InputChannelRequests::BusIdType busId(m_inputChannelId, m_inputDevice.GetInputDeviceId().GetIndex()); + if (enabled) + { + InputChannelRequestBus::Handler::BusConnect(busId); + } + else + { + InputChannelRequestBus::Handler::BusDisconnect(busId); + } + } + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.h b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.h index fc5dc2a7b2..8be719e979 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.h @@ -218,12 +218,23 @@ namespace AzFramework //! \ref AzFramework::InputChannelRequests::ResetState void ResetState() override; + //////////////////////////////////////////////////////////////////////////////////////////// + //! Sets whether or not the channel shall broadcast to the InputChannelNotificationBus when + //! its internal state is updated. + //! This can be disabled to allow for testing or synthetic events that aren't part of the + //! input system. + void SetUpdateEventsEnabled(bool enabled); + private: //////////////////////////////////////////////////////////////////////////////////////////// // Variables const InputChannelId m_inputChannelId; //!< Id of the input channel const InputDevice& m_inputDevice; //!< Input device that owns the input channel State m_state; //!< Current state of the input channel + + //! If true, &InputChannelNotifications::OnInputChannelEvent will be fired when UpdateState + //! is called. + bool m_shouldDispatchEvents = true; }; //////////////////////////////////////////////////////////////////////////////////////////////// From 15248e7c12dab1382d7065a8645830cf7d2421ae Mon Sep 17 00:00:00 2001 From: nvsickle Date: Thu, 17 Jun 2021 15:21:20 -0700 Subject: [PATCH 02/12] Add QtEventToAzInputManager Signed-off-by: nvsickle --- .../Input/QtEventToAzInputManager.cpp | 332 ++++++++++++++++++ .../Input/QtEventToAzInputManager.h | 84 +++++ .../aztoolsframework_files.cmake | 2 + 3 files changed, 418 insertions(+) create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp new file mode 100644 index 0000000000..f842777fb3 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -0,0 +1,332 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace AzToolsFramework +{ + // This asumes modifier keys (ctrl/shift/alt) map to the left control/shift/alt keys as Qt provides no way to disambiguate + // in a platform agnostic manner. This could be expanded later with a PAL mapping from native scan codes from QKeyEvents, if needed. + AZStd::array, 91> QtEventToAzInputMapper::QtKeyMappings = { { + { Qt::Key_0, AzFramework::InputDeviceKeyboard::Key::Alphanumeric0 }, + { Qt::Key_1, AzFramework::InputDeviceKeyboard::Key::Alphanumeric1 }, + { Qt::Key_2, AzFramework::InputDeviceKeyboard::Key::Alphanumeric2 }, + { Qt::Key_3, AzFramework::InputDeviceKeyboard::Key::Alphanumeric3 }, + { Qt::Key_4, AzFramework::InputDeviceKeyboard::Key::Alphanumeric4 }, + { Qt::Key_5, AzFramework::InputDeviceKeyboard::Key::Alphanumeric5 }, + { Qt::Key_6, AzFramework::InputDeviceKeyboard::Key::Alphanumeric6 }, + { Qt::Key_7, AzFramework::InputDeviceKeyboard::Key::Alphanumeric7 }, + { Qt::Key_8, AzFramework::InputDeviceKeyboard::Key::Alphanumeric8 }, + { Qt::Key_9, AzFramework::InputDeviceKeyboard::Key::Alphanumeric9 }, + { Qt::Key_A, AzFramework::InputDeviceKeyboard::Key::AlphanumericA }, + { Qt::Key_B, AzFramework::InputDeviceKeyboard::Key::AlphanumericB }, + { Qt::Key_C, AzFramework::InputDeviceKeyboard::Key::AlphanumericC }, + { Qt::Key_D, AzFramework::InputDeviceKeyboard::Key::AlphanumericD }, + { Qt::Key_E, AzFramework::InputDeviceKeyboard::Key::AlphanumericE }, + { Qt::Key_F, AzFramework::InputDeviceKeyboard::Key::AlphanumericF }, + { Qt::Key_G, AzFramework::InputDeviceKeyboard::Key::AlphanumericG }, + { Qt::Key_H, AzFramework::InputDeviceKeyboard::Key::AlphanumericH }, + { Qt::Key_I, AzFramework::InputDeviceKeyboard::Key::AlphanumericI }, + { Qt::Key_J, AzFramework::InputDeviceKeyboard::Key::AlphanumericJ }, + { Qt::Key_K, AzFramework::InputDeviceKeyboard::Key::AlphanumericK }, + { Qt::Key_L, AzFramework::InputDeviceKeyboard::Key::AlphanumericL }, + { Qt::Key_M, AzFramework::InputDeviceKeyboard::Key::AlphanumericM }, + { Qt::Key_N, AzFramework::InputDeviceKeyboard::Key::AlphanumericN }, + { Qt::Key_O, AzFramework::InputDeviceKeyboard::Key::AlphanumericO }, + { Qt::Key_P, AzFramework::InputDeviceKeyboard::Key::AlphanumericP }, + { Qt::Key_Q, AzFramework::InputDeviceKeyboard::Key::AlphanumericQ }, + { Qt::Key_R, AzFramework::InputDeviceKeyboard::Key::AlphanumericR }, + { Qt::Key_S, AzFramework::InputDeviceKeyboard::Key::AlphanumericS }, + { Qt::Key_T, AzFramework::InputDeviceKeyboard::Key::AlphanumericT }, + { Qt::Key_U, AzFramework::InputDeviceKeyboard::Key::AlphanumericU }, + { Qt::Key_V, AzFramework::InputDeviceKeyboard::Key::AlphanumericV }, + { Qt::Key_W, AzFramework::InputDeviceKeyboard::Key::AlphanumericW }, + { Qt::Key_X, AzFramework::InputDeviceKeyboard::Key::AlphanumericX }, + { Qt::Key_Y, AzFramework::InputDeviceKeyboard::Key::AlphanumericY }, + { Qt::Key_Z, AzFramework::InputDeviceKeyboard::Key::AlphanumericZ }, + { Qt::Key_Backspace, AzFramework::InputDeviceKeyboard::Key::EditBackspace }, + { Qt::Key_CapsLock, AzFramework::InputDeviceKeyboard::Key::EditCapsLock }, + { Qt::Key_Enter, AzFramework::InputDeviceKeyboard::Key::EditEnter }, + { Qt::Key_Space, AzFramework::InputDeviceKeyboard::Key::EditSpace }, + { Qt::Key_Tab, AzFramework::InputDeviceKeyboard::Key::EditTab }, + { Qt::Key_Escape, AzFramework::InputDeviceKeyboard::Key::Escape }, + { Qt::Key_F1, AzFramework::InputDeviceKeyboard::Key::Function01 }, + { Qt::Key_F2, AzFramework::InputDeviceKeyboard::Key::Function02 }, + { Qt::Key_F3, AzFramework::InputDeviceKeyboard::Key::Function03 }, + { Qt::Key_F4, AzFramework::InputDeviceKeyboard::Key::Function04 }, + { Qt::Key_F5, AzFramework::InputDeviceKeyboard::Key::Function05 }, + { Qt::Key_F6, AzFramework::InputDeviceKeyboard::Key::Function06 }, + { Qt::Key_F7, AzFramework::InputDeviceKeyboard::Key::Function07 }, + { Qt::Key_F8, AzFramework::InputDeviceKeyboard::Key::Function08 }, + { Qt::Key_F9, AzFramework::InputDeviceKeyboard::Key::Function09 }, + { Qt::Key_F10, AzFramework::InputDeviceKeyboard::Key::Function10 }, + { Qt::Key_F11, AzFramework::InputDeviceKeyboard::Key::Function11 }, + { Qt::Key_F12, AzFramework::InputDeviceKeyboard::Key::Function12 }, + { Qt::Key_F13, AzFramework::InputDeviceKeyboard::Key::Function13 }, + { Qt::Key_F14, AzFramework::InputDeviceKeyboard::Key::Function14 }, + { Qt::Key_F15, AzFramework::InputDeviceKeyboard::Key::Function15 }, + { Qt::Key_F16, AzFramework::InputDeviceKeyboard::Key::Function16 }, + { Qt::Key_F17, AzFramework::InputDeviceKeyboard::Key::Function17 }, + { Qt::Key_F18, AzFramework::InputDeviceKeyboard::Key::Function18 }, + { Qt::Key_F19, AzFramework::InputDeviceKeyboard::Key::Function19 }, + { Qt::Key_F20, AzFramework::InputDeviceKeyboard::Key::Function20 }, + { Qt::Key_Alt, AzFramework::InputDeviceKeyboard::Key::ModifierAltL }, + { Qt::Key_Control, AzFramework::InputDeviceKeyboard::Key::ModifierCtrlL }, + { Qt::Key_Shift, AzFramework::InputDeviceKeyboard::Key::ModifierShiftL }, + { Qt::Key_Super_L, AzFramework::InputDeviceKeyboard::Key::ModifierSuperL }, + { Qt::Key_Super_R, AzFramework::InputDeviceKeyboard::Key::ModifierSuperR }, + { Qt::Key_Down, AzFramework::InputDeviceKeyboard::Key::NavigationArrowDown }, + { Qt::Key_Left, AzFramework::InputDeviceKeyboard::Key::NavigationArrowLeft }, + { Qt::Key_Right, AzFramework::InputDeviceKeyboard::Key::NavigationArrowRight }, + { Qt::Key_Up, AzFramework::InputDeviceKeyboard::Key::NavigationArrowUp }, + { Qt::Key_Delete, AzFramework::InputDeviceKeyboard::Key::NavigationDelete }, + { Qt::Key_End, AzFramework::InputDeviceKeyboard::Key::NavigationEnd }, + { Qt::Key_Home, AzFramework::InputDeviceKeyboard::Key::NavigationHome }, + { Qt::Key_Insert, AzFramework::InputDeviceKeyboard::Key::NavigationInsert }, + { Qt::Key_PageDown, AzFramework::InputDeviceKeyboard::Key::NavigationPageDown }, + { Qt::Key_PageUp, AzFramework::InputDeviceKeyboard::Key::NavigationPageUp }, + { Qt::Key_Apostrophe, AzFramework::InputDeviceKeyboard::Key::PunctuationApostrophe }, + { Qt::Key_Backslash, AzFramework::InputDeviceKeyboard::Key::PunctuationBackslash }, + { Qt::Key_BracketLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketL }, + { Qt::Key_BracketRight, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketR }, + { Qt::Key_Comma, AzFramework::InputDeviceKeyboard::Key::PunctuationComma }, + { Qt::Key_Equal, AzFramework::InputDeviceKeyboard::Key::PunctuationEquals }, + { Qt::Key_hyphen, AzFramework::InputDeviceKeyboard::Key::PunctuationHyphen }, + { Qt::Key_Period, AzFramework::InputDeviceKeyboard::Key::PunctuationPeriod }, + { Qt::Key_Semicolon, AzFramework::InputDeviceKeyboard::Key::PunctuationSemicolon }, + { Qt::Key_Slash, AzFramework::InputDeviceKeyboard::Key::PunctuationSlash }, + { Qt::Key_QuoteLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationTilde }, + { Qt::Key_Pause, AzFramework::InputDeviceKeyboard::Key::WindowsSystemPause }, + { Qt::Key_Print, AzFramework::InputDeviceKeyboard::Key::WindowsSystemPrint }, + { Qt::Key_ScrollLock, AzFramework::InputDeviceKeyboard::Key::WindowsSystemScrollLock }, + } }; + + AZStd::array, 5> QtEventToAzInputMapper::QtMouseButtonMappings = { { + { Qt::MouseButton::LeftButton, AzFramework::InputDeviceMouse::Button::Left }, + { Qt::MouseButton::RightButton, AzFramework::InputDeviceMouse::Button::Right }, + { Qt::MouseButton::MiddleButton, AzFramework::InputDeviceMouse::Button::Middle }, + { Qt::MouseButton::ExtraButton1, AzFramework::InputDeviceMouse::Button::Other1 }, + { Qt::MouseButton::ExtraButton2, AzFramework::InputDeviceMouse::Button::Other2 }, + } }; + + // Currently this is only set for modifier keys. + // This should only be expanded sparingly, any keys handled here will not be bubbled up to the shortcut system. + // ex: If Key_S was here, the viewport would consume S key presses before the application could process a QAction with a Ctrl+S + // shortcut. + AZStd::array QtEventToAzInputMapper::HighPriorityKeys = { Qt::Key_Alt, Qt::Key_Control, Qt::Key_Shift, Qt::Key_Super_L, + Qt::Key_Super_R }; + + QtEventToAzInputMapper::QtEventToAzInputMapper(QWidget* sourceWidget) + : m_sourceWidget(sourceWidget) + , m_keyboardModifiers(AZStd::make_shared()) + , m_cursorPosition(AZStd::make_shared()) + , m_keyMappings(QtKeyMappings.begin(), QtKeyMappings.end()) + , m_mouseButtonMappings(QtMouseButtonMappings.begin(), QtMouseButtonMappings.end()) + { + AzFramework::InputDeviceRequests::InputDeviceByIdMap deviceMap; + AzFramework::InputDeviceRequestBus::Broadcast(&AzFramework::InputDeviceRequestBus::Events::GetInputDevicesById, deviceMap); + + const AzFramework::InputDevice* keyboardDevice = deviceMap.find(AzFramework::InputDeviceKeyboard::Id)->second; + + // Create synthetic keyboard input channels. + for (auto [_, id] : QtKeyMappings) + { + AZStd::unique_ptr channel = + AZStd::make_unique(id, *keyboardDevice, m_keyboardModifiers); + channel->SetUpdateEventsEnabled(false); + m_channels.emplace(id, AZStd::move(channel)); + } + + const AzFramework::InputDevice* mouseDevice = deviceMap.find(AzFramework::InputDeviceMouse::Id)->second; + + // Create synthetic mouse button input channels. + for (auto [_, id] : QtMouseButtonMappings) + { + AZStd::unique_ptr channel = + AZStd::make_unique(id, *mouseDevice, m_cursorPosition); + channel->SetUpdateEventsEnabled(false); + m_channels.emplace(id, AZStd::move(channel)); + } + + // Create synthetic mouse movement channels. + for (const auto& id : { AzFramework::InputDeviceMouse::Movement::X, AzFramework::InputDeviceMouse::Movement::Y, + AzFramework::InputDeviceMouse::Movement::Z, AzFramework::InputDeviceMouse::SystemCursorPosition }) + { + AZStd::unique_ptr channel = + AZStd::make_unique(id, *mouseDevice, m_cursorPosition); + channel->SetUpdateEventsEnabled(false); + m_channels.emplace(id, AZStd::move(channel)); + } + } + + AZStd::vector QtEventToAzInputMapper::MapQtEventToAzInput(QEvent* event) + { + AZStd::vector mappedChannels; + + // If our focus changes, go ahead and reset all input devices. + if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) + { + for (auto& channelData : m_channels) + { + // If resetting the input device changed the channel state, submit it to the mapped channel list + // for processing. + if (channelData.second->UpdateState(false)) + { + mappedChannels.push_back(channelData.second.get()); + } + } + m_mouseChannelsNeedUpdate = false; + } + + // Update mouse movement channels based on the cursor delta (if 0, ProcessRawInputEvent will change state to Ended). + auto processMouseMoveChannels = [this, &mappedChannels]() + { + auto systemCursorChannel = + GetInputChannel(AzFramework::InputDeviceMouse::SystemCursorPosition); + auto cursorXChannel = + GetInputChannel(AzFramework::InputDeviceMouse::Movement::X); + auto cursorYChannel = + GetInputChannel(AzFramework::InputDeviceMouse::Movement::Y); + auto cursorZChannel = + GetInputChannel(AzFramework::InputDeviceMouse::Movement::Z); + + systemCursorChannel->ProcessRawInputEvent(m_cursorPosition->m_normalizedPositionDelta.GetLength()); + cursorXChannel->ProcessRawInputEvent( + m_cursorPosition->m_normalizedPositionDelta.GetX() * aznumeric_cast(m_sourceWidget->width())); + cursorYChannel->ProcessRawInputEvent( + m_cursorPosition->m_normalizedPositionDelta.GetY() * aznumeric_cast(m_sourceWidget->height())); + cursorZChannel->ProcessRawInputEvent(0.f); + + mappedChannels.push_back(systemCursorChannel); + mappedChannels.push_back(cursorXChannel); + mappedChannels.push_back(cursorYChannel); + mappedChannels.push_back(cursorZChannel); + }; + + // Because there's no "end" to mouse movement and wheel events, we reset mouse movement channels that have been opened + // during the next processed non-mouse event. + if (m_mouseChannelsNeedUpdate && event->type() != QEvent::Type::MouseMove && event->type() != QEvent::Type::Wheel) + { + m_cursorPosition->m_normalizedPositionDelta = AZ::Vector2::CreateZero(); + processMouseMoveChannels(); + m_mouseChannelsNeedUpdate = false; + } + + // Map key events to input channels. + // ShortcutOverride is used in lieu of KeyPress for high priority input channels like Alt + // that need to be accepted and stopped before they bubble up and cause unintended behavior. + if (event->type() == QEvent::Type::KeyPress || event->type() == QEvent::Type::KeyRelease || + event->type() == QEvent::Type::ShortcutOverride) + { + QKeyEvent* keyEvent = static_cast(event); + const Qt::Key key = static_cast(keyEvent->key()); + const auto modifiers = keyEvent->modifiers(); + + // For ShortcutEvent, only continue processing if we're in the HighPriorityKeys set. + if (event->type() != QEvent::Type::ShortcutOverride || + AZStd::find(HighPriorityKeys.begin(), HighPriorityKeys.end(), key) != HighPriorityKeys.end()) + { + if (auto keyIt = m_keyMappings.find(key); keyIt != m_keyMappings.end()) + { + auto keyChannel = GetInputChannel(keyIt->second); + + if (keyChannel) + { + if (event->type() == QEvent::Type::KeyPress || event->type() == QEvent::Type::ShortcutOverride) + { + keyChannel->UpdateState(true); + } + else + { + keyChannel->UpdateState(false); + } + mappedChannels.push_back(keyChannel); + } + } + } + } + // Map mouse events to input channels. + else if (event->type() == QEvent::Type::MouseButtonPress || event->type() == QEvent::Type::MouseButtonRelease) + { + QMouseEvent* mouseEvent = static_cast(event); + const Qt::MouseButton button = mouseEvent->button(); + + if (auto buttonIt = m_mouseButtonMappings.find(button); buttonIt != m_mouseButtonMappings.end()) + { + auto buttonChannel = GetInputChannel(buttonIt->second); + + if (buttonChannel) + { + if (event->type() == QEvent::Type::MouseButtonPress) + { + buttonChannel->UpdateState(true); + } + else + { + buttonChannel->UpdateState(false); + } + mappedChannels.push_back(buttonChannel); + } + } + } + // Map mouse movement to the movement input channels. + // This includes SystemCursorPosition alongside Movement::X and Movement::Y. + else if (event->type() == QEvent::Type::MouseMove) + { + QMouseEvent* mouseEvent = static_cast(event); + const QPoint mousePos = mouseEvent->pos(); + const float normalizedX = aznumeric_cast(mousePos.x()) / aznumeric_cast(m_sourceWidget->width()); + const float normalizedY = aznumeric_cast(mousePos.y()) / aznumeric_cast(m_sourceWidget->height()); + const AZ::Vector2 normalizedPosition(normalizedX, normalizedY); + m_cursorPosition->m_normalizedPositionDelta = normalizedPosition - m_cursorPosition->m_normalizedPosition; + m_cursorPosition->m_normalizedPosition = normalizedPosition; + processMouseMoveChannels(); + m_mouseChannelsNeedUpdate = true; + } + // Map wheel events to the mouse Z movement channel. + else if (event->type() == QEvent::Type::Wheel) + { + QWheelEvent* wheelEvent = static_cast(event); + auto cursorZChannel = + GetInputChannel(AzFramework::InputDeviceMouse::Movement::Z); + const QPoint angleDelta = wheelEvent->angleDelta(); + cursorZChannel->ProcessRawInputEvent(aznumeric_cast(angleDelta.y())); + mappedChannels.push_back(cursorZChannel); + m_mouseChannelsNeedUpdate = true; + } + + return mappedChannels; + } + + bool QtEventToAzInputMapper::HandlesInputEvent(const AzFramework::InputChannel& channel) const + { + // 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(); + return deviceId == AzFramework::InputDeviceMouse::Id || deviceId == AzFramework::InputDeviceKeyboard::Id; + } +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h new file mode 100644 index 0000000000..0bfe7d33eb --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h @@ -0,0 +1,84 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +class QWidget; + +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: + QtEventToAzInputMapper(QWidget* sourceWidget); + ~QtEventToAzInputMapper() = default; + + //! Maps a Qt event to any relevant input channels + //! \returns A vector containing all InputChannels that have changed state. + AZStd::vector MapQtEventToAzInput(QEvent* event); + //! Queries whether a given input channel has a synthetic equivalent mapped + //! by this system. + //! \returns true if the channel is handled by MapQtEventToAzInput. + bool HandlesInputEvent(const AzFramework::InputChannel& channel) const; + + private: + template + TInputChannel* GetInputChannel(const AzFramework::InputChannelId& id) + { + auto channelIt = m_channels.find(id); + if (channelIt != m_channels.end()) + { + return static_cast(channelIt->second.get()); + } + return {}; + } + + // Mapping from Qt::Keys to InputChannelIds. + // Used to populate m_keyMappings. + static AZStd::array, 91> QtKeyMappings; + // Mapping from Qt::MouseButtons to InputChannelIds. + // Used to populate m_mouseButtonMappings. + static AZStd::array, 5> QtMouseButtonMappings; + // A set of high priority keys that need to be processed at the ShortcutOverride level instead of the + // KeyEvent level. This prevents e.g. the main menu bar from processing a press of the "alt" key when the + // viewport consumes the event. + static AZStd::array HighPriorityKeys; + + // The current keyboard modifier state used by our synthetic key input channels. + AZStd::shared_ptr m_keyboardModifiers; + // The current normalized cursor position used by our synthetic system cursor event. + AZStd::shared_ptr m_cursorPosition; + // A lookup table for Qt key -> AZ input channel. + AZStd::unordered_map m_keyMappings; + // A lookup table for Qt mouse button -> AZ input channel. + AZStd::unordered_map m_mouseButtonMappings; + // Our set of synthetic input channels that can be mapped by MapQtEventToAzInput. + // These channels do not broadcast state changes to the actual AZ input devices, as we're bypassing + // that system to integrate with Qt to allow coexistence with the platform native implementation. + AZStd::unordered_map> m_channels; + // The source widget to map events from, used to calculate the relative mouse position within the widget bounds. + QWidget* m_sourceWidget; + // Flags when mouse movement channels have been opened and may need to be closed (as there are no movement ended events). + bool m_mouseChannelsNeedUpdate = false; + }; +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index b5b2c028a5..7fbde67ce4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -724,6 +724,8 @@ set(FILES PythonTerminal/ScriptTermDialog.cpp PythonTerminal/ScriptTermDialog.h PythonTerminal/ScriptTermDialog.ui + Input/QtEventToAzInputManager.h + Input/QtEventToAzInputManager.cpp ) # Prevent the following files from being grouped in UNITY builds From a4a038cb28febf959f8d0528110b4c899b052860 Mon Sep 17 00:00:00 2001 From: nvsickle Date: Thu, 17 Jun 2021 15:35:14 -0700 Subject: [PATCH 03/12] Switch RenderViewportWidget to using Qt events instead of AzFramework input events Signed-off-by: nvsickle --- .../Viewport/RenderViewportWidget.h | 3 + .../Source/Viewport/RenderViewportWidget.cpp | 104 ++++-------------- 2 files changed, 25 insertions(+), 82 deletions(-) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index c649031280..977c3f522c 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -157,5 +158,7 @@ namespace AtomToolsFramework AZStd::optional m_lastCursorPosition; // The viewport settings (e.g. grid snapping, grid size) for this viewport. const AzToolsFramework::ViewportInteraction::ViewportSettings* m_viewportSettings = nullptr; + // Maps our internal Qt events into AzFramework InputChannels for our ViewportControllerList. + AzToolsFramework::QtEventToAzInputMapper m_inputChannelMapper = AzToolsFramework::QtEventToAzInputMapper(this); }; } //namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 529652e1a9..d1702d52df 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -149,86 +149,24 @@ namespace AtomToolsFramework return m_defaultCamera; } - static bool IsMouseButtonEvent(const AzFramework::InputChannel& inputChannel) - { - const auto& mouseButtons = AzFramework::InputDeviceMouse::Button::All; - return AZStd::find(mouseButtons.begin(), mouseButtons.end(), inputChannel.GetInputChannelId()) != mouseButtons.end(); - } - - static bool IsMouseMoveEvent(const AzFramework::InputChannel& inputChannel) - { - return inputChannel.GetInputChannelId() == AzFramework::InputDeviceMouse::SystemCursorPosition; - } - - static bool IsMouseButtonOrWheelEvent(const AzFramework::InputChannel& inputChannel) - { - return IsMouseButtonEvent(inputChannel) || inputChannel.GetInputChannelId() == AzFramework::InputDeviceMouse::Movement::Z; - } - - bool RenderViewportWidget::CanInputGrantFocus(const AzFramework::InputChannel& inputChannel) const - { - // Only take focus from a mouse event if the cursor is currently within the viewport - if (!m_mouseOver) - { - return false; - } - - // Only mouse button down events (clicks) can grant focus - if (inputChannel.GetState() != AzFramework::InputChannel::State::Began) - { - return false; - } - - // Only mouse button events can grant focus - return IsMouseButtonEvent(inputChannel); - } - bool RenderViewportWidget::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) { - bool shouldConsumeEvent = true; - - // Grab keyboard focus if we've been clicked on. - // Qt normally handles this for us, but we're filtering native events before they get - // synthesized into QMouseEvents. - if (!hasFocus() && CanInputGrantFocus(inputChannel)) - { - setFocus(); - } - - // Don't consume new input events if we don't currently have focus. - // We do forward Ended events, as they may be relevant to our current state - // (e.g. a key gets released after we lose focus, it shouldn't remain "stuck"). if (!hasFocus()) { - if (inputChannel.GetState() == AzFramework::InputChannel::State::Ended) - { - // Forward the input ended event to our controllers, but don't prevent other viewports from receiving it. - shouldConsumeEvent = false; - } - else - { - // Not an event we should listen to, abort. - return false; - } + return false; } - // If we receive a mouse button event from outside of our viewport, ignore it even if we have focus. - if (!m_mouseOver - && inputChannel.GetState() == AzFramework::InputChannel::State::Began - && IsMouseButtonOrWheelEvent(inputChannel)) + // Only forward channels that aren't covered by our Qt -> AZ event mapper + if (m_inputChannelMapper.HandlesInputEvent(inputChannel)) { return false; } - // Don't forward system cursor position updates, we'll do that ourselves for in-window movements once the result of - // ViewportCursorScreenPosition is guaranteed to be correct (see mouseMoveEvent). - if (IsMouseMoveEvent(inputChannel)) - { - return false; - } + bool shouldConsumeEvent = true; AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); const bool eventHandled = m_controllerList->HandleInputChannelEvent({GetId(), windowId, inputChannel}); + // If our controllers handled the event and it's one we can safely consume (i.e. it's not an Ended event that other viewports might need), consume it. return eventHandled && shouldConsumeEvent; } @@ -262,7 +200,23 @@ namespace AtomToolsFramework { SendWindowResizeEvent(); } - return QWidget::event(event); + + bool eventHandled = false; + for(AzFramework::InputChannel* mappedInputChannel : m_inputChannelMapper.MapQtEventToAzInput(event)) + { + AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); + if (m_controllerList->HandleInputChannelEvent({GetId(), windowId, *mappedInputChannel})) + { + eventHandled = true; + } + } + + if (eventHandled) + { + event->setAccepted(true); + } + + return QWidget::event(event) || eventHandled; } void RenderViewportWidget::enterEvent([[maybe_unused]] QEvent* event) @@ -279,20 +233,6 @@ namespace AtomToolsFramework { m_mousePosition = event->localPos(); - // Now that we've looked a viewport local mouse position, - // we can go ahead and broadcast the system cursor input event to the controllers. - // This allows any controllers not listening to pure mouse deltas to consistently - // look up the mouse position in viewport screen coordinates. - const AzFramework::InputDevice* mouseInputDevice = nullptr; - if (AzFramework::InputDeviceRequestBus::EventResult( - mouseInputDevice, AzFramework::InputDeviceMouse::Id, &AzFramework::InputDeviceRequests::GetInputDevice); - mouseInputDevice != nullptr) - { - const AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); - AzFramework::InputChannel syntheticInput(AzFramework::InputDeviceMouse::SystemCursorPosition, *mouseInputDevice); - m_controllerList->HandleInputChannelEvent({GetId(), windowId, syntheticInput}); - } - if (m_capturingCursor && m_lastCursorPosition.has_value()) { AzQtComponents::SetCursorPos(m_lastCursorPosition.value()); From f4c96fc9df3d2463c6779dc8db406acd172f8efc Mon Sep 17 00:00:00 2001 From: nvsickle Date: Tue, 22 Jun 2021 19:04:08 -0700 Subject: [PATCH 04/12] -Rework input handling to filter through a global event filter -Attempt to support remote desktop for viewport cursor capture Signed-off-by: nvsickle --- .../Input/QtEventToAzInputManager.cpp | 327 ++++++++++-------- .../Input/QtEventToAzInputManager.h | 39 ++- .../Source/Viewport/RenderViewportWidget.cpp | 35 +- 3 files changed, 239 insertions(+), 162 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index f842777fb3..512922aad8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -141,7 +142,8 @@ namespace AzToolsFramework Qt::Key_Super_R }; QtEventToAzInputMapper::QtEventToAzInputMapper(QWidget* sourceWidget) - : m_sourceWidget(sourceWidget) + : QObject(sourceWidget) + , m_sourceWidget(sourceWidget) , m_keyboardModifiers(AZStd::make_shared()) , m_cursorPosition(AZStd::make_shared()) , m_keyMappings(QtKeyMappings.begin(), QtKeyMappings.end()) @@ -181,145 +183,9 @@ namespace AzToolsFramework channel->SetUpdateEventsEnabled(false); m_channels.emplace(id, AZStd::move(channel)); } - } - AZStd::vector QtEventToAzInputMapper::MapQtEventToAzInput(QEvent* event) - { - AZStd::vector mappedChannels; - - // If our focus changes, go ahead and reset all input devices. - if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) - { - for (auto& channelData : m_channels) - { - // If resetting the input device changed the channel state, submit it to the mapped channel list - // for processing. - if (channelData.second->UpdateState(false)) - { - mappedChannels.push_back(channelData.second.get()); - } - } - m_mouseChannelsNeedUpdate = false; - } - - // Update mouse movement channels based on the cursor delta (if 0, ProcessRawInputEvent will change state to Ended). - auto processMouseMoveChannels = [this, &mappedChannels]() - { - auto systemCursorChannel = - GetInputChannel(AzFramework::InputDeviceMouse::SystemCursorPosition); - auto cursorXChannel = - GetInputChannel(AzFramework::InputDeviceMouse::Movement::X); - auto cursorYChannel = - GetInputChannel(AzFramework::InputDeviceMouse::Movement::Y); - auto cursorZChannel = - GetInputChannel(AzFramework::InputDeviceMouse::Movement::Z); - - systemCursorChannel->ProcessRawInputEvent(m_cursorPosition->m_normalizedPositionDelta.GetLength()); - cursorXChannel->ProcessRawInputEvent( - m_cursorPosition->m_normalizedPositionDelta.GetX() * aznumeric_cast(m_sourceWidget->width())); - cursorYChannel->ProcessRawInputEvent( - m_cursorPosition->m_normalizedPositionDelta.GetY() * aznumeric_cast(m_sourceWidget->height())); - cursorZChannel->ProcessRawInputEvent(0.f); - - mappedChannels.push_back(systemCursorChannel); - mappedChannels.push_back(cursorXChannel); - mappedChannels.push_back(cursorYChannel); - mappedChannels.push_back(cursorZChannel); - }; - - // Because there's no "end" to mouse movement and wheel events, we reset mouse movement channels that have been opened - // during the next processed non-mouse event. - if (m_mouseChannelsNeedUpdate && event->type() != QEvent::Type::MouseMove && event->type() != QEvent::Type::Wheel) - { - m_cursorPosition->m_normalizedPositionDelta = AZ::Vector2::CreateZero(); - processMouseMoveChannels(); - m_mouseChannelsNeedUpdate = false; - } - - // Map key events to input channels. - // ShortcutOverride is used in lieu of KeyPress for high priority input channels like Alt - // that need to be accepted and stopped before they bubble up and cause unintended behavior. - if (event->type() == QEvent::Type::KeyPress || event->type() == QEvent::Type::KeyRelease || - event->type() == QEvent::Type::ShortcutOverride) - { - QKeyEvent* keyEvent = static_cast(event); - const Qt::Key key = static_cast(keyEvent->key()); - const auto modifiers = keyEvent->modifiers(); - - // For ShortcutEvent, only continue processing if we're in the HighPriorityKeys set. - if (event->type() != QEvent::Type::ShortcutOverride || - AZStd::find(HighPriorityKeys.begin(), HighPriorityKeys.end(), key) != HighPriorityKeys.end()) - { - if (auto keyIt = m_keyMappings.find(key); keyIt != m_keyMappings.end()) - { - auto keyChannel = GetInputChannel(keyIt->second); - - if (keyChannel) - { - if (event->type() == QEvent::Type::KeyPress || event->type() == QEvent::Type::ShortcutOverride) - { - keyChannel->UpdateState(true); - } - else - { - keyChannel->UpdateState(false); - } - mappedChannels.push_back(keyChannel); - } - } - } - } - // Map mouse events to input channels. - else if (event->type() == QEvent::Type::MouseButtonPress || event->type() == QEvent::Type::MouseButtonRelease) - { - QMouseEvent* mouseEvent = static_cast(event); - const Qt::MouseButton button = mouseEvent->button(); - - if (auto buttonIt = m_mouseButtonMappings.find(button); buttonIt != m_mouseButtonMappings.end()) - { - auto buttonChannel = GetInputChannel(buttonIt->second); - - if (buttonChannel) - { - if (event->type() == QEvent::Type::MouseButtonPress) - { - buttonChannel->UpdateState(true); - } - else - { - buttonChannel->UpdateState(false); - } - mappedChannels.push_back(buttonChannel); - } - } - } - // Map mouse movement to the movement input channels. - // This includes SystemCursorPosition alongside Movement::X and Movement::Y. - else if (event->type() == QEvent::Type::MouseMove) - { - QMouseEvent* mouseEvent = static_cast(event); - const QPoint mousePos = mouseEvent->pos(); - const float normalizedX = aznumeric_cast(mousePos.x()) / aznumeric_cast(m_sourceWidget->width()); - const float normalizedY = aznumeric_cast(mousePos.y()) / aznumeric_cast(m_sourceWidget->height()); - const AZ::Vector2 normalizedPosition(normalizedX, normalizedY); - m_cursorPosition->m_normalizedPositionDelta = normalizedPosition - m_cursorPosition->m_normalizedPosition; - m_cursorPosition->m_normalizedPosition = normalizedPosition; - processMouseMoveChannels(); - m_mouseChannelsNeedUpdate = true; - } - // Map wheel events to the mouse Z movement channel. - else if (event->type() == QEvent::Type::Wheel) - { - QWheelEvent* wheelEvent = static_cast(event); - auto cursorZChannel = - GetInputChannel(AzFramework::InputDeviceMouse::Movement::Z); - const QPoint angleDelta = wheelEvent->angleDelta(); - cursorZChannel->ProcessRawInputEvent(aznumeric_cast(angleDelta.y())); - mappedChannels.push_back(cursorZChannel); - m_mouseChannelsNeedUpdate = true; - } - - return mappedChannels; + // Install a global event filter to ensure we don't miss mouse and key release events. + QApplication::instance()->installEventFilter(this); } bool QtEventToAzInputMapper::HandlesInputEvent(const AzFramework::InputChannel& channel) const @@ -329,4 +195,187 @@ namespace AzToolsFramework const AzFramework::InputDeviceId& deviceId = channel.GetInputDevice().GetInputDeviceId(); return deviceId == AzFramework::InputDeviceMouse::Id || deviceId == AzFramework::InputDeviceKeyboard::Id; } + + bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event) + { + // Because there's no "end" to mouse movement and wheel events, we reset mouse movement channels that have been opened + // during the next processed non-mouse event. + if (m_mouseChannelsNeedUpdate && event->type() != QEvent::Type::MouseMove && event->type() != QEvent::Type::Wheel) + { + m_cursorPosition->m_normalizedPositionDelta = AZ::Vector2::CreateZero(); + ProcessPendingMouseEvents(); + m_mouseChannelsNeedUpdate = false; + } + + // Only accept mouse & key release events that originate from an object that is not our target widget, + // as we don't want to erroneously intercept user input meant for another component. + if (object != m_sourceWidget && event->type() != QEvent::Type::KeyRelease && event->type() != QEvent::Type::MouseButtonRelease) + { + return false; + } + + // If our focus changes, go ahead and reset all input devices. + if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) + { + HandleFocusChange(event); + } + // Map key events to input channels. + // ShortcutOverride is used in lieu of KeyPress for high priority input channels like Alt + // that need to be accepted and stopped before they bubble up and cause unintended behavior. + else if (event->type() == QEvent::Type::KeyPress || event->type() == QEvent::Type::KeyRelease || + event->type() == QEvent::Type::ShortcutOverride) + { + QKeyEvent* keyEvent = static_cast(event); + HandleKeyEvent(keyEvent); + } + // Map mouse events to input channels. + else if (event->type() == QEvent::Type::MouseButtonPress || event->type() == QEvent::Type::MouseButtonRelease) + { + QMouseEvent* mouseEvent = static_cast(event); + HandleMouseButtonEvent(mouseEvent); + } + // Map mouse movement to the movement input channels. + // This includes SystemCursorPosition alongside Movement::X and Movement::Y. + else if (event->type() == QEvent::Type::MouseMove) + { + QMouseEvent* mouseEvent = static_cast(event); + HandleMouseMoveEvent(mouseEvent); + } + // Map wheel events to the mouse Z movement channel. + else if (event->type() == QEvent::Type::Wheel) + { + QWheelEvent* wheelEvent = static_cast(event); + HandleWheelEvent(wheelEvent); + } + + return false; + } + + void QtEventToAzInputMapper::NotifyUpdateChannelIfNotIdle(const AzFramework::InputChannel* channel, QEvent* event) + { + if (channel->GetState() != AzFramework::InputChannel::State::Idle) + { + emit InputChannelUpdated(channel, event); + } + } + + void QtEventToAzInputMapper::ProcessPendingMouseEvents() + { + auto systemCursorChannel = + GetInputChannel(AzFramework::InputDeviceMouse::SystemCursorPosition); + auto cursorXChannel = + GetInputChannel(AzFramework::InputDeviceMouse::Movement::X); + auto cursorYChannel = + GetInputChannel(AzFramework::InputDeviceMouse::Movement::Y); + auto cursorZChannel = + GetInputChannel(AzFramework::InputDeviceMouse::Movement::Z); + + systemCursorChannel->ProcessRawInputEvent(m_cursorPosition->m_normalizedPositionDelta.GetLength()); + cursorXChannel->ProcessRawInputEvent( + m_cursorPosition->m_normalizedPositionDelta.GetX() * aznumeric_cast(m_sourceWidget->width())); + cursorYChannel->ProcessRawInputEvent( + m_cursorPosition->m_normalizedPositionDelta.GetY() * aznumeric_cast(m_sourceWidget->height())); + cursorZChannel->ProcessRawInputEvent(0.f); + + NotifyUpdateChannelIfNotIdle(systemCursorChannel, nullptr); + NotifyUpdateChannelIfNotIdle(cursorXChannel, nullptr); + NotifyUpdateChannelIfNotIdle(cursorYChannel, nullptr); + NotifyUpdateChannelIfNotIdle(cursorZChannel, nullptr); + } + + void QtEventToAzInputMapper::HandleMouseButtonEvent(QMouseEvent* mouseEvent) + { + const Qt::MouseButton button = mouseEvent->button(); + + if (auto buttonIt = m_mouseButtonMappings.find(button); buttonIt != m_mouseButtonMappings.end()) + { + auto buttonChannel = GetInputChannel(buttonIt->second); + + if (buttonChannel) + { + if (mouseEvent->type() == QEvent::Type::MouseButtonPress) + { + buttonChannel->UpdateState(true); + } + else + { + buttonChannel->UpdateState(false); + } + + NotifyUpdateChannelIfNotIdle(buttonChannel, mouseEvent); + } + } + } + + void QtEventToAzInputMapper::HandleMouseMoveEvent(QMouseEvent* mouseEvent) + { + const QPoint mousePos = mouseEvent->pos(); + const float normalizedX = aznumeric_cast(mousePos.x()) / aznumeric_cast(m_sourceWidget->width()); + const float normalizedY = aznumeric_cast(mousePos.y()) / aznumeric_cast(m_sourceWidget->height()); + const AZ::Vector2 normalizedPosition(normalizedX, normalizedY); + m_cursorPosition->m_normalizedPositionDelta = normalizedPosition - m_cursorPosition->m_normalizedPosition; + m_cursorPosition->m_normalizedPosition = normalizedPosition; + ProcessPendingMouseEvents(); + m_mouseChannelsNeedUpdate = true; + } + + void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent) + { + const Qt::Key key = static_cast(keyEvent->key()); + + // For ShortcutEvent, only continue processing if we're in the HighPriorityKeys set. + if (keyEvent->type() != QEvent::Type::ShortcutOverride || + AZStd::find(HighPriorityKeys.begin(), HighPriorityKeys.end(), key) != HighPriorityKeys.end()) + { + if (auto keyIt = m_keyMappings.find(key); keyIt != m_keyMappings.end()) + { + auto keyChannel = GetInputChannel(keyIt->second); + + if (keyChannel) + { + if (keyEvent->type() == QEvent::Type::KeyPress || keyEvent->type() == QEvent::Type::ShortcutOverride) + { + keyChannel->UpdateState(true); + } + else + { + keyChannel->UpdateState(false); + } + + NotifyUpdateChannelIfNotIdle(keyChannel, keyEvent); + } + } + } + } + + void QtEventToAzInputMapper::HandleWheelEvent(QWheelEvent* wheelEvent) + { + auto cursorZChannel = + GetInputChannel(AzFramework::InputDeviceMouse::Movement::Z); + const QPoint angleDelta = wheelEvent->angleDelta(); + // Check both angles, as the alt modifier can change the wheel direction. + int wheelAngle = angleDelta.x(); + if (wheelAngle == 0) + { + wheelAngle = angleDelta.y(); + } + cursorZChannel->ProcessRawInputEvent(aznumeric_cast(wheelAngle)); + NotifyUpdateChannelIfNotIdle(cursorZChannel, wheelEvent); + m_mouseChannelsNeedUpdate = true; + } + + void QtEventToAzInputMapper::HandleFocusChange(QEvent* event) + { + for (auto& channelData : m_channels) + { + // If resetting the input device changed the channel state, submit it to the mapped channel list + // for processing. + if (channelData.second->IsActive()) + { + channelData.second->UpdateState(false); + NotifyUpdateChannelIfNotIdle(channelData.second.get(), event); + } + } + m_mouseChannelsNeedUpdate = false; + } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h index 0bfe7d33eb..8ad96e3949 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h @@ -12,37 +12,52 @@ #pragma once -#include +#if !defined(Q_MOC_RUN) #include +#include #include +#include #include #include -#include #include +#include +#endif //!defined(Q_MOC_RUN) class QWidget; +class QKeyEvent; +class QMouseEvent; +class QWheelEvent; namespace AzToolsFramework { //! Maps events from the Qt input system to synthetic InputChannels in AzFramework //! that can be used by AzFramework::ViewportControllers. - class QtEventToAzInputMapper final + class QtEventToAzInputMapper final : public QObject { + Q_OBJECT + public: QtEventToAzInputMapper(QWidget* sourceWidget); ~QtEventToAzInputMapper() = default; - //! Maps a Qt event to any relevant input channels - //! \returns A vector containing all InputChannels that have changed state. - AZStd::vector MapQtEventToAzInput(QEvent* event); //! Queries whether a given input channel has a synthetic equivalent mapped //! by this system. //! \returns true if the channel is handled by MapQtEventToAzInput. bool HandlesInputEvent(const AzFramework::InputChannel& channel) const; + // QObject overrides... + bool eventFilter(QObject* object, QEvent* event) override; + + signals: + //! This signal fires whenever the state of the specified input channel changes. + //! This is determined by Qt events dispatched to the source widget. + //! \param channel The AZ input channel that has been updated. + //! \param event The underlying Qt event that triggered this change, if applicable. + void InputChannelUpdated(const AzFramework::InputChannel* channel, QEvent* event); + private: - template + template TInputChannel* GetInputChannel(const AzFramework::InputChannelId& id) { auto channelIt = m_channels.find(id); @@ -53,6 +68,16 @@ namespace AzToolsFramework return {}; } + void NotifyUpdateChannelIfNotIdle(const AzFramework::InputChannel* channel, QEvent* event); + + void ProcessPendingMouseEvents(); + + void HandleMouseButtonEvent(QMouseEvent* mouseEvent); + void HandleMouseMoveEvent(QMouseEvent* mouseEvent); + void HandleKeyEvent(QKeyEvent* keyEvent); + void HandleWheelEvent(QWheelEvent* wheelEvent); + void HandleFocusChange(QEvent* event); + // Mapping from Qt::Keys to InputChannelIds. // Used to populate m_keyMappings. static AZStd::array, 91> QtKeyMappings; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index d1702d52df..8488fb574a 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -38,6 +38,21 @@ namespace AtomToolsFramework setUpdatesEnabled(false); setFocusPolicy(Qt::FocusPolicy::WheelFocus); setMouseTracking(true); + + // Forward input events to our controller list. + QObject::connect(&m_inputChannelMapper, &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, this, + [this](const AzFramework::InputChannel* inputChannel, QEvent* event) + { + AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); + if (m_controllerList->HandleInputChannelEvent({GetId(), windowId, *inputChannel})) + { + // If the controller handled the input event, mark the event as accepted so it doesn't continue to propagate. + if (event) + { + event->setAccepted(true); + } + } + }); } bool RenderViewportWidget::InitializeViewportContext(AzFramework::ViewportId id) @@ -201,22 +216,7 @@ namespace AtomToolsFramework SendWindowResizeEvent(); } - bool eventHandled = false; - for(AzFramework::InputChannel* mappedInputChannel : m_inputChannelMapper.MapQtEventToAzInput(event)) - { - AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); - if (m_controllerList->HandleInputChannelEvent({GetId(), windowId, *mappedInputChannel})) - { - eventHandled = true; - } - } - - if (eventHandled) - { - event->setAccepted(true); - } - - return QWidget::event(event) || eventHandled; + return QWidget::event(event); } void RenderViewportWidget::enterEvent([[maybe_unused]] QEvent* event) @@ -236,6 +236,9 @@ namespace AtomToolsFramework if (m_capturingCursor && m_lastCursorPosition.has_value()) { AzQtComponents::SetCursorPos(m_lastCursorPosition.value()); + // 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. + m_lastCursorPosition = QCursor::pos(); } else { From 042e465622eb0c2bb25dad7da13a31653ad8bbc9 Mon Sep 17 00:00:00 2001 From: nvsickle Date: Tue, 22 Jun 2021 19:06:55 -0700 Subject: [PATCH 05/12] Harden a few pieces of viewport controller logic -Don't eat mouse/keyboard release events in the ViewportManipulatorController -Do a key activity check in the LegacyViewportCameraController instead of checking state (this could be done elsewhere but it seems to be working as-is and is scheduled to go away) -Ignore idle mouse delta updates sent to the modular camera controller Signed-off-by: nvsickle --- .../Editor/LegacyViewportCameraController.cpp | 2 +- Code/Editor/ViewportManipulatorController.cpp | 3 ++- .../AzFramework/Viewport/CameraInput.cpp | 26 +++++++++++-------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Code/Editor/LegacyViewportCameraController.cpp b/Code/Editor/LegacyViewportCameraController.cpp index 0750db8d97..f437196548 100644 --- a/Code/Editor/LegacyViewportCameraController.cpp +++ b/Code/Editor/LegacyViewportCameraController.cpp @@ -404,7 +404,7 @@ bool LegacyViewportCameraControllerInstance::HandleInputChannelEvent(const AzFra } else if (auto key = GetKeyboardKey(event.m_inputChannel); key != Qt::Key_unknown) { - if (state == InputChannel::State::Ended) + if (!event.m_inputChannel.IsActive()) { m_pressedKeys.erase(key); } diff --git a/Code/Editor/ViewportManipulatorController.cpp b/Code/Editor/ViewportManipulatorController.cpp index c7d2885cb2..fb696a61f3 100644 --- a/Code/Editor/ViewportManipulatorController.cpp +++ b/Code/Editor/ViewportManipulatorController.cpp @@ -216,7 +216,8 @@ namespace SandboxEditor interactionHandled, AzToolsFramework::GetEntityContextId(), targetInteractionEvent, mouseInteractionEvent); } - return interactionHandled; + // Only filter button/key press events, not release events + return interactionHandled && event.m_inputChannel.IsActive(); } void ViewportManipulatorControllerInstance::ResetInputChannels() diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index 3658f9cd22..a85d631160 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -747,19 +747,23 @@ namespace AzFramework return button == inputChannelId; }); - if (inputChannelId == InputDeviceMouse::Movement::X) + // Accept active mouse channel updates, inactive movement channels will just have a 0 delta. + if (inputChannel.IsActive()) { - return HorizontalMotionEvent{ aznumeric_cast(inputChannel.GetValue()) }; + if (inputChannelId == InputDeviceMouse::Movement::X) + { + return HorizontalMotionEvent{ aznumeric_cast(inputChannel.GetValue()) }; + } + else if (inputChannelId == InputDeviceMouse::Movement::Y) + { + return VerticalMotionEvent{ aznumeric_cast(inputChannel.GetValue()) }; + } + else if (inputChannelId == InputDeviceMouse::Movement::Z) + { + return ScrollEvent{ inputChannel.GetValue() }; + } } - else if (inputChannelId == InputDeviceMouse::Movement::Y) - { - return VerticalMotionEvent{ aznumeric_cast(inputChannel.GetValue()) }; - } - else if (inputChannelId == InputDeviceMouse::Movement::Z) - { - return ScrollEvent{ inputChannel.GetValue() }; - } - else if (wasMouseButton || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId)) + if (wasMouseButton || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId)) { return DiscreteInputEvent{ inputChannelId, inputChannel.GetState() }; } From 8dc59a13da6b8180aa791b45a1ec403fa784aa77 Mon Sep 17 00:00:00 2001 From: nvsickle Date: Thu, 1 Jul 2021 14:05:51 -0700 Subject: [PATCH 06/12] Revert "Add InputChannel API to disable forwarding events to the underyling device" This reverts commit c4be5021116fd7f4944ac300056031b7d5be25be. Signed-off-by: nvsickle --- .../Input/Channels/InputChannel.cpp | 20 +------------------ .../AzFramework/Input/Channels/InputChannel.h | 11 ---------- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.cpp b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.cpp index 81dec1cac7..c274bbf491 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.cpp @@ -327,7 +327,7 @@ namespace AzFramework break; } - if (m_state != State::Idle && m_shouldDispatchEvents) + if (m_state != State::Idle) { bool hasBeenConsumed = false; InputChannelNotificationBus::Broadcast(&InputChannelNotifications::OnInputChannelEvent, @@ -350,22 +350,4 @@ namespace AzFramework // Directly return the channel to the 'Idle' state m_state = State::Idle; } - - //////////////////////////////////////////////////////////////////////////////////////////////// - void InputChannel::SetUpdateEventsEnabled(bool enabled) - { - if (m_shouldDispatchEvents != enabled) - { - m_shouldDispatchEvents = enabled; - const InputChannelRequests::BusIdType busId(m_inputChannelId, m_inputDevice.GetInputDeviceId().GetIndex()); - if (enabled) - { - InputChannelRequestBus::Handler::BusConnect(busId); - } - else - { - InputChannelRequestBus::Handler::BusDisconnect(busId); - } - } - } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.h b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.h index 8be719e979..fc5dc2a7b2 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannel.h @@ -218,23 +218,12 @@ namespace AzFramework //! \ref AzFramework::InputChannelRequests::ResetState void ResetState() override; - //////////////////////////////////////////////////////////////////////////////////////////// - //! Sets whether or not the channel shall broadcast to the InputChannelNotificationBus when - //! its internal state is updated. - //! This can be disabled to allow for testing or synthetic events that aren't part of the - //! input system. - void SetUpdateEventsEnabled(bool enabled); - private: //////////////////////////////////////////////////////////////////////////////////////////// // Variables const InputChannelId m_inputChannelId; //!< Id of the input channel const InputDevice& m_inputDevice; //!< Input device that owns the input channel State m_state; //!< Current state of the input channel - - //! If true, &InputChannelNotifications::OnInputChannelEvent will be fired when UpdateState - //! is called. - bool m_shouldDispatchEvents = true; }; //////////////////////////////////////////////////////////////////////////////////////////////// From 52ae7433b3e27c5e9becd0f6661de5ea626571cf Mon Sep 17 00:00:00 2001 From: nvsickle Date: Thu, 1 Jul 2021 14:07:04 -0700 Subject: [PATCH 07/12] Use synthetic keyboard and mouse devices instead of synthetic input channels Signed-off-by: nvsickle --- .../Devices/Keyboard/InputDeviceKeyboard.cpp | 4 +- .../Devices/Keyboard/InputDeviceKeyboard.h | 2 +- .../Input/Devices/Mouse/InputDeviceMouse.cpp | 4 +- .../Input/Devices/Mouse/InputDeviceMouse.h | 2 +- .../Input/QtEventToAzInputManager.cpp | 300 +++++++++--------- .../Input/QtEventToAzInputManager.h | 77 ++++- .../Viewport/RenderViewportWidget.h | 3 +- .../Source/Viewport/RenderViewportWidget.cpp | 34 +- 8 files changed, 239 insertions(+), 187 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp index 7625679d69..75346227ed 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp @@ -454,8 +454,8 @@ namespace AzFramework } //////////////////////////////////////////////////////////////////////////////////////////////// - InputDeviceKeyboard::InputDeviceKeyboard() - : InputDevice(Id) + InputDeviceKeyboard::InputDeviceKeyboard(AzFramework::InputDeviceId id) + : InputDevice(id) , m_modifierKeyStates(AZStd::make_shared()) , m_allChannelsById() , m_keyChannelsById() diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h index ea0aa1beec..9f61ba96f7 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h @@ -240,7 +240,7 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////// //! Constructor - InputDeviceKeyboard(); + InputDeviceKeyboard(AzFramework::InputDeviceId id = Id); //////////////////////////////////////////////////////////////////////////////////////////// // Disable copying diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp index a7180d7ec1..5cd940e989 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp @@ -95,8 +95,8 @@ namespace AzFramework } //////////////////////////////////////////////////////////////////////////////////////////////// - InputDeviceMouse::InputDeviceMouse() - : InputDevice(Id) + InputDeviceMouse::InputDeviceMouse(AzFramework::InputDeviceId id) + : InputDevice(id) , m_allChannelsById() , m_buttonChannelsById() , m_movementChannelsById() diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h index 7fc6a7a505..3feb79ac68 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h @@ -111,7 +111,7 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////// //! Constructor - explicit InputDeviceMouse(); + explicit InputDeviceMouse(AzFramework::InputDeviceId id = Id); //////////////////////////////////////////////////////////////////////////////////////////// // Disable copying diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index 512922aad8..e5c565f8f5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -17,9 +17,6 @@ #include #include -#include -#include - #include #include #include @@ -30,159 +27,163 @@ namespace AzToolsFramework { - // This asumes modifier keys (ctrl/shift/alt) map to the left control/shift/alt keys as Qt provides no way to disambiguate - // in a platform agnostic manner. This could be expanded later with a PAL mapping from native scan codes from QKeyEvents, if needed. - AZStd::array, 91> QtEventToAzInputMapper::QtKeyMappings = { { - { Qt::Key_0, AzFramework::InputDeviceKeyboard::Key::Alphanumeric0 }, - { Qt::Key_1, AzFramework::InputDeviceKeyboard::Key::Alphanumeric1 }, - { Qt::Key_2, AzFramework::InputDeviceKeyboard::Key::Alphanumeric2 }, - { Qt::Key_3, AzFramework::InputDeviceKeyboard::Key::Alphanumeric3 }, - { Qt::Key_4, AzFramework::InputDeviceKeyboard::Key::Alphanumeric4 }, - { Qt::Key_5, AzFramework::InputDeviceKeyboard::Key::Alphanumeric5 }, - { Qt::Key_6, AzFramework::InputDeviceKeyboard::Key::Alphanumeric6 }, - { Qt::Key_7, AzFramework::InputDeviceKeyboard::Key::Alphanumeric7 }, - { Qt::Key_8, AzFramework::InputDeviceKeyboard::Key::Alphanumeric8 }, - { Qt::Key_9, AzFramework::InputDeviceKeyboard::Key::Alphanumeric9 }, - { Qt::Key_A, AzFramework::InputDeviceKeyboard::Key::AlphanumericA }, - { Qt::Key_B, AzFramework::InputDeviceKeyboard::Key::AlphanumericB }, - { Qt::Key_C, AzFramework::InputDeviceKeyboard::Key::AlphanumericC }, - { Qt::Key_D, AzFramework::InputDeviceKeyboard::Key::AlphanumericD }, - { Qt::Key_E, AzFramework::InputDeviceKeyboard::Key::AlphanumericE }, - { Qt::Key_F, AzFramework::InputDeviceKeyboard::Key::AlphanumericF }, - { Qt::Key_G, AzFramework::InputDeviceKeyboard::Key::AlphanumericG }, - { Qt::Key_H, AzFramework::InputDeviceKeyboard::Key::AlphanumericH }, - { Qt::Key_I, AzFramework::InputDeviceKeyboard::Key::AlphanumericI }, - { Qt::Key_J, AzFramework::InputDeviceKeyboard::Key::AlphanumericJ }, - { Qt::Key_K, AzFramework::InputDeviceKeyboard::Key::AlphanumericK }, - { Qt::Key_L, AzFramework::InputDeviceKeyboard::Key::AlphanumericL }, - { Qt::Key_M, AzFramework::InputDeviceKeyboard::Key::AlphanumericM }, - { Qt::Key_N, AzFramework::InputDeviceKeyboard::Key::AlphanumericN }, - { Qt::Key_O, AzFramework::InputDeviceKeyboard::Key::AlphanumericO }, - { Qt::Key_P, AzFramework::InputDeviceKeyboard::Key::AlphanumericP }, - { Qt::Key_Q, AzFramework::InputDeviceKeyboard::Key::AlphanumericQ }, - { Qt::Key_R, AzFramework::InputDeviceKeyboard::Key::AlphanumericR }, - { Qt::Key_S, AzFramework::InputDeviceKeyboard::Key::AlphanumericS }, - { Qt::Key_T, AzFramework::InputDeviceKeyboard::Key::AlphanumericT }, - { Qt::Key_U, AzFramework::InputDeviceKeyboard::Key::AlphanumericU }, - { Qt::Key_V, AzFramework::InputDeviceKeyboard::Key::AlphanumericV }, - { Qt::Key_W, AzFramework::InputDeviceKeyboard::Key::AlphanumericW }, - { Qt::Key_X, AzFramework::InputDeviceKeyboard::Key::AlphanumericX }, - { Qt::Key_Y, AzFramework::InputDeviceKeyboard::Key::AlphanumericY }, - { Qt::Key_Z, AzFramework::InputDeviceKeyboard::Key::AlphanumericZ }, - { Qt::Key_Backspace, AzFramework::InputDeviceKeyboard::Key::EditBackspace }, - { Qt::Key_CapsLock, AzFramework::InputDeviceKeyboard::Key::EditCapsLock }, - { Qt::Key_Enter, AzFramework::InputDeviceKeyboard::Key::EditEnter }, - { Qt::Key_Space, AzFramework::InputDeviceKeyboard::Key::EditSpace }, - { Qt::Key_Tab, AzFramework::InputDeviceKeyboard::Key::EditTab }, - { Qt::Key_Escape, AzFramework::InputDeviceKeyboard::Key::Escape }, - { Qt::Key_F1, AzFramework::InputDeviceKeyboard::Key::Function01 }, - { Qt::Key_F2, AzFramework::InputDeviceKeyboard::Key::Function02 }, - { Qt::Key_F3, AzFramework::InputDeviceKeyboard::Key::Function03 }, - { Qt::Key_F4, AzFramework::InputDeviceKeyboard::Key::Function04 }, - { Qt::Key_F5, AzFramework::InputDeviceKeyboard::Key::Function05 }, - { Qt::Key_F6, AzFramework::InputDeviceKeyboard::Key::Function06 }, - { Qt::Key_F7, AzFramework::InputDeviceKeyboard::Key::Function07 }, - { Qt::Key_F8, AzFramework::InputDeviceKeyboard::Key::Function08 }, - { Qt::Key_F9, AzFramework::InputDeviceKeyboard::Key::Function09 }, - { Qt::Key_F10, AzFramework::InputDeviceKeyboard::Key::Function10 }, - { Qt::Key_F11, AzFramework::InputDeviceKeyboard::Key::Function11 }, - { Qt::Key_F12, AzFramework::InputDeviceKeyboard::Key::Function12 }, - { Qt::Key_F13, AzFramework::InputDeviceKeyboard::Key::Function13 }, - { Qt::Key_F14, AzFramework::InputDeviceKeyboard::Key::Function14 }, - { Qt::Key_F15, AzFramework::InputDeviceKeyboard::Key::Function15 }, - { Qt::Key_F16, AzFramework::InputDeviceKeyboard::Key::Function16 }, - { Qt::Key_F17, AzFramework::InputDeviceKeyboard::Key::Function17 }, - { Qt::Key_F18, AzFramework::InputDeviceKeyboard::Key::Function18 }, - { Qt::Key_F19, AzFramework::InputDeviceKeyboard::Key::Function19 }, - { Qt::Key_F20, AzFramework::InputDeviceKeyboard::Key::Function20 }, - { Qt::Key_Alt, AzFramework::InputDeviceKeyboard::Key::ModifierAltL }, - { Qt::Key_Control, AzFramework::InputDeviceKeyboard::Key::ModifierCtrlL }, - { Qt::Key_Shift, AzFramework::InputDeviceKeyboard::Key::ModifierShiftL }, - { Qt::Key_Super_L, AzFramework::InputDeviceKeyboard::Key::ModifierSuperL }, - { Qt::Key_Super_R, AzFramework::InputDeviceKeyboard::Key::ModifierSuperR }, - { Qt::Key_Down, AzFramework::InputDeviceKeyboard::Key::NavigationArrowDown }, - { Qt::Key_Left, AzFramework::InputDeviceKeyboard::Key::NavigationArrowLeft }, - { Qt::Key_Right, AzFramework::InputDeviceKeyboard::Key::NavigationArrowRight }, - { Qt::Key_Up, AzFramework::InputDeviceKeyboard::Key::NavigationArrowUp }, - { Qt::Key_Delete, AzFramework::InputDeviceKeyboard::Key::NavigationDelete }, - { Qt::Key_End, AzFramework::InputDeviceKeyboard::Key::NavigationEnd }, - { Qt::Key_Home, AzFramework::InputDeviceKeyboard::Key::NavigationHome }, - { Qt::Key_Insert, AzFramework::InputDeviceKeyboard::Key::NavigationInsert }, - { Qt::Key_PageDown, AzFramework::InputDeviceKeyboard::Key::NavigationPageDown }, - { Qt::Key_PageUp, AzFramework::InputDeviceKeyboard::Key::NavigationPageUp }, - { Qt::Key_Apostrophe, AzFramework::InputDeviceKeyboard::Key::PunctuationApostrophe }, - { Qt::Key_Backslash, AzFramework::InputDeviceKeyboard::Key::PunctuationBackslash }, - { Qt::Key_BracketLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketL }, - { Qt::Key_BracketRight, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketR }, - { Qt::Key_Comma, AzFramework::InputDeviceKeyboard::Key::PunctuationComma }, - { Qt::Key_Equal, AzFramework::InputDeviceKeyboard::Key::PunctuationEquals }, - { Qt::Key_hyphen, AzFramework::InputDeviceKeyboard::Key::PunctuationHyphen }, - { Qt::Key_Period, AzFramework::InputDeviceKeyboard::Key::PunctuationPeriod }, - { Qt::Key_Semicolon, AzFramework::InputDeviceKeyboard::Key::PunctuationSemicolon }, - { Qt::Key_Slash, AzFramework::InputDeviceKeyboard::Key::PunctuationSlash }, - { Qt::Key_QuoteLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationTilde }, - { Qt::Key_Pause, AzFramework::InputDeviceKeyboard::Key::WindowsSystemPause }, - { Qt::Key_Print, AzFramework::InputDeviceKeyboard::Key::WindowsSystemPrint }, - { Qt::Key_ScrollLock, AzFramework::InputDeviceKeyboard::Key::WindowsSystemScrollLock }, - } }; + void QtEventToAzInputMapper::InitializeKeyMappings() + { + // This asumes modifier keys (ctrl/shift/alt) map to the left control/shift/alt keys as Qt provides no way to disambiguate + // in a platform agnostic manner. This could be expanded later with a PAL mapping from native scan codes from QKeyEvents, if needed. + m_keyMappings = { { + { Qt::Key_0, AzFramework::InputDeviceKeyboard::Key::Alphanumeric0 }, + { Qt::Key_1, AzFramework::InputDeviceKeyboard::Key::Alphanumeric1 }, + { Qt::Key_2, AzFramework::InputDeviceKeyboard::Key::Alphanumeric2 }, + { Qt::Key_3, AzFramework::InputDeviceKeyboard::Key::Alphanumeric3 }, + { Qt::Key_4, AzFramework::InputDeviceKeyboard::Key::Alphanumeric4 }, + { Qt::Key_5, AzFramework::InputDeviceKeyboard::Key::Alphanumeric5 }, + { Qt::Key_6, AzFramework::InputDeviceKeyboard::Key::Alphanumeric6 }, + { Qt::Key_7, AzFramework::InputDeviceKeyboard::Key::Alphanumeric7 }, + { Qt::Key_8, AzFramework::InputDeviceKeyboard::Key::Alphanumeric8 }, + { Qt::Key_9, AzFramework::InputDeviceKeyboard::Key::Alphanumeric9 }, + { Qt::Key_A, AzFramework::InputDeviceKeyboard::Key::AlphanumericA }, + { Qt::Key_B, AzFramework::InputDeviceKeyboard::Key::AlphanumericB }, + { Qt::Key_C, AzFramework::InputDeviceKeyboard::Key::AlphanumericC }, + { Qt::Key_D, AzFramework::InputDeviceKeyboard::Key::AlphanumericD }, + { Qt::Key_E, AzFramework::InputDeviceKeyboard::Key::AlphanumericE }, + { Qt::Key_F, AzFramework::InputDeviceKeyboard::Key::AlphanumericF }, + { Qt::Key_G, AzFramework::InputDeviceKeyboard::Key::AlphanumericG }, + { Qt::Key_H, AzFramework::InputDeviceKeyboard::Key::AlphanumericH }, + { Qt::Key_I, AzFramework::InputDeviceKeyboard::Key::AlphanumericI }, + { Qt::Key_J, AzFramework::InputDeviceKeyboard::Key::AlphanumericJ }, + { Qt::Key_K, AzFramework::InputDeviceKeyboard::Key::AlphanumericK }, + { Qt::Key_L, AzFramework::InputDeviceKeyboard::Key::AlphanumericL }, + { Qt::Key_M, AzFramework::InputDeviceKeyboard::Key::AlphanumericM }, + { Qt::Key_N, AzFramework::InputDeviceKeyboard::Key::AlphanumericN }, + { Qt::Key_O, AzFramework::InputDeviceKeyboard::Key::AlphanumericO }, + { Qt::Key_P, AzFramework::InputDeviceKeyboard::Key::AlphanumericP }, + { Qt::Key_Q, AzFramework::InputDeviceKeyboard::Key::AlphanumericQ }, + { Qt::Key_R, AzFramework::InputDeviceKeyboard::Key::AlphanumericR }, + { Qt::Key_S, AzFramework::InputDeviceKeyboard::Key::AlphanumericS }, + { Qt::Key_T, AzFramework::InputDeviceKeyboard::Key::AlphanumericT }, + { Qt::Key_U, AzFramework::InputDeviceKeyboard::Key::AlphanumericU }, + { Qt::Key_V, AzFramework::InputDeviceKeyboard::Key::AlphanumericV }, + { Qt::Key_W, AzFramework::InputDeviceKeyboard::Key::AlphanumericW }, + { Qt::Key_X, AzFramework::InputDeviceKeyboard::Key::AlphanumericX }, + { Qt::Key_Y, AzFramework::InputDeviceKeyboard::Key::AlphanumericY }, + { Qt::Key_Z, AzFramework::InputDeviceKeyboard::Key::AlphanumericZ }, + { Qt::Key_Backspace, AzFramework::InputDeviceKeyboard::Key::EditBackspace }, + { Qt::Key_CapsLock, AzFramework::InputDeviceKeyboard::Key::EditCapsLock }, + { Qt::Key_Enter, AzFramework::InputDeviceKeyboard::Key::EditEnter }, + { Qt::Key_Space, AzFramework::InputDeviceKeyboard::Key::EditSpace }, + { Qt::Key_Tab, AzFramework::InputDeviceKeyboard::Key::EditTab }, + { Qt::Key_Escape, AzFramework::InputDeviceKeyboard::Key::Escape }, + { Qt::Key_F1, AzFramework::InputDeviceKeyboard::Key::Function01 }, + { Qt::Key_F2, AzFramework::InputDeviceKeyboard::Key::Function02 }, + { Qt::Key_F3, AzFramework::InputDeviceKeyboard::Key::Function03 }, + { Qt::Key_F4, AzFramework::InputDeviceKeyboard::Key::Function04 }, + { Qt::Key_F5, AzFramework::InputDeviceKeyboard::Key::Function05 }, + { Qt::Key_F6, AzFramework::InputDeviceKeyboard::Key::Function06 }, + { Qt::Key_F7, AzFramework::InputDeviceKeyboard::Key::Function07 }, + { Qt::Key_F8, AzFramework::InputDeviceKeyboard::Key::Function08 }, + { Qt::Key_F9, AzFramework::InputDeviceKeyboard::Key::Function09 }, + { Qt::Key_F10, AzFramework::InputDeviceKeyboard::Key::Function10 }, + { Qt::Key_F11, AzFramework::InputDeviceKeyboard::Key::Function11 }, + { Qt::Key_F12, AzFramework::InputDeviceKeyboard::Key::Function12 }, + { Qt::Key_F13, AzFramework::InputDeviceKeyboard::Key::Function13 }, + { Qt::Key_F14, AzFramework::InputDeviceKeyboard::Key::Function14 }, + { Qt::Key_F15, AzFramework::InputDeviceKeyboard::Key::Function15 }, + { Qt::Key_F16, AzFramework::InputDeviceKeyboard::Key::Function16 }, + { Qt::Key_F17, AzFramework::InputDeviceKeyboard::Key::Function17 }, + { Qt::Key_F18, AzFramework::InputDeviceKeyboard::Key::Function18 }, + { Qt::Key_F19, AzFramework::InputDeviceKeyboard::Key::Function19 }, + { Qt::Key_F20, AzFramework::InputDeviceKeyboard::Key::Function20 }, + { Qt::Key_Alt, AzFramework::InputDeviceKeyboard::Key::ModifierAltL }, + { Qt::Key_Control, AzFramework::InputDeviceKeyboard::Key::ModifierCtrlL }, + { Qt::Key_Shift, AzFramework::InputDeviceKeyboard::Key::ModifierShiftL }, + { Qt::Key_Super_L, AzFramework::InputDeviceKeyboard::Key::ModifierSuperL }, + { Qt::Key_Super_R, AzFramework::InputDeviceKeyboard::Key::ModifierSuperR }, + { Qt::Key_Down, AzFramework::InputDeviceKeyboard::Key::NavigationArrowDown }, + { Qt::Key_Left, AzFramework::InputDeviceKeyboard::Key::NavigationArrowLeft }, + { Qt::Key_Right, AzFramework::InputDeviceKeyboard::Key::NavigationArrowRight }, + { Qt::Key_Up, AzFramework::InputDeviceKeyboard::Key::NavigationArrowUp }, + { Qt::Key_Delete, AzFramework::InputDeviceKeyboard::Key::NavigationDelete }, + { Qt::Key_End, AzFramework::InputDeviceKeyboard::Key::NavigationEnd }, + { Qt::Key_Home, AzFramework::InputDeviceKeyboard::Key::NavigationHome }, + { Qt::Key_Insert, AzFramework::InputDeviceKeyboard::Key::NavigationInsert }, + { Qt::Key_PageDown, AzFramework::InputDeviceKeyboard::Key::NavigationPageDown }, + { Qt::Key_PageUp, AzFramework::InputDeviceKeyboard::Key::NavigationPageUp }, + { Qt::Key_Apostrophe, AzFramework::InputDeviceKeyboard::Key::PunctuationApostrophe }, + { Qt::Key_Backslash, AzFramework::InputDeviceKeyboard::Key::PunctuationBackslash }, + { Qt::Key_BracketLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketL }, + { Qt::Key_BracketRight, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketR }, + { Qt::Key_Comma, AzFramework::InputDeviceKeyboard::Key::PunctuationComma }, + { Qt::Key_Equal, AzFramework::InputDeviceKeyboard::Key::PunctuationEquals }, + { Qt::Key_hyphen, AzFramework::InputDeviceKeyboard::Key::PunctuationHyphen }, + { Qt::Key_Period, AzFramework::InputDeviceKeyboard::Key::PunctuationPeriod }, + { Qt::Key_Semicolon, AzFramework::InputDeviceKeyboard::Key::PunctuationSemicolon }, + { Qt::Key_Slash, AzFramework::InputDeviceKeyboard::Key::PunctuationSlash }, + { Qt::Key_QuoteLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationTilde }, + { Qt::Key_Pause, AzFramework::InputDeviceKeyboard::Key::WindowsSystemPause }, + { Qt::Key_Print, AzFramework::InputDeviceKeyboard::Key::WindowsSystemPrint }, + { Qt::Key_ScrollLock, AzFramework::InputDeviceKeyboard::Key::WindowsSystemScrollLock }, + } }; + } - AZStd::array, 5> QtEventToAzInputMapper::QtMouseButtonMappings = { { - { Qt::MouseButton::LeftButton, AzFramework::InputDeviceMouse::Button::Left }, - { Qt::MouseButton::RightButton, AzFramework::InputDeviceMouse::Button::Right }, - { Qt::MouseButton::MiddleButton, AzFramework::InputDeviceMouse::Button::Middle }, - { Qt::MouseButton::ExtraButton1, AzFramework::InputDeviceMouse::Button::Other1 }, - { Qt::MouseButton::ExtraButton2, AzFramework::InputDeviceMouse::Button::Other2 }, - } }; + void QtEventToAzInputMapper::InitializeMouseButtonMappings() + { + m_mouseButtonMappings = { { + { Qt::MouseButton::LeftButton, AzFramework::InputDeviceMouse::Button::Left }, + { Qt::MouseButton::RightButton, AzFramework::InputDeviceMouse::Button::Right }, + { Qt::MouseButton::MiddleButton, AzFramework::InputDeviceMouse::Button::Middle }, + { Qt::MouseButton::ExtraButton1, AzFramework::InputDeviceMouse::Button::Other1 }, + { Qt::MouseButton::ExtraButton2, AzFramework::InputDeviceMouse::Button::Other2 }, + } }; + } // Currently this is only set for modifier keys. // This should only be expanded sparingly, any keys handled here will not be bubbled up to the shortcut system. // ex: If Key_S was here, the viewport would consume S key presses before the application could process a QAction with a Ctrl+S // shortcut. - AZStd::array QtEventToAzInputMapper::HighPriorityKeys = { Qt::Key_Alt, Qt::Key_Control, Qt::Key_Shift, Qt::Key_Super_L, - Qt::Key_Super_R }; + void QtEventToAzInputMapper::InitializeHighPriorityKeys() + { + m_highPriorityKeys = { Qt::Key_Alt, Qt::Key_Control, Qt::Key_Shift, Qt::Key_Super_L, Qt::Key_Super_R }; + } - QtEventToAzInputMapper::QtEventToAzInputMapper(QWidget* sourceWidget) + QtEventToAzInputMapper::EditorQtKeyboardDevice::EditorQtKeyboardDevice(AzFramework::InputDeviceId id) + : AzFramework::InputDeviceKeyboard(id) + { + // Disable all platform native processing in favor of our Qt event handling + SetImplementation(nullptr); + } + + QtEventToAzInputMapper::EditorQtMouseDevice::EditorQtMouseDevice(AzFramework::InputDeviceId id) + : AzFramework::InputDeviceMouse(id) + { + // Disable all platform native processing in favor of our Qt event handling + SetImplementation(nullptr); + } + + QtEventToAzInputMapper::QtEventToAzInputMapper(QWidget* sourceWidget, int syntheticDeviceId) : QObject(sourceWidget) , m_sourceWidget(sourceWidget) , m_keyboardModifiers(AZStd::make_shared()) , m_cursorPosition(AZStd::make_shared()) - , m_keyMappings(QtKeyMappings.begin(), QtKeyMappings.end()) - , m_mouseButtonMappings(QtMouseButtonMappings.begin(), QtMouseButtonMappings.end()) { - AzFramework::InputDeviceRequests::InputDeviceByIdMap deviceMap; - AzFramework::InputDeviceRequestBus::Broadcast(&AzFramework::InputDeviceRequestBus::Events::GetInputDevicesById, deviceMap); + InitializeKeyMappings(); + InitializeMouseButtonMappings(); + InitializeHighPriorityKeys(); - const AzFramework::InputDevice* keyboardDevice = deviceMap.find(AzFramework::InputDeviceKeyboard::Id)->second; + // Add an arbitrary offset to our device index to avoid collision with real physical device index. + // We still have to use the keyboard and mouse device channel names because input channels are only addressed + // by their own name and their device index, so overlapping input channels between devices would conflict. + constexpr AZ::u32 syntheticDeviceOffset = 1000; + const AzFramework::InputDeviceId keyboardDeviceId( + AzFramework::InputDeviceKeyboard::Id.GetName(), syntheticDeviceId + syntheticDeviceOffset); + const AzFramework::InputDeviceId mouseDeviceId( + AzFramework::InputDeviceMouse::Id.GetName(), syntheticDeviceId + syntheticDeviceOffset); - // Create synthetic keyboard input channels. - for (auto [_, id] : QtKeyMappings) - { - AZStd::unique_ptr channel = - AZStd::make_unique(id, *keyboardDevice, m_keyboardModifiers); - channel->SetUpdateEventsEnabled(false); - m_channels.emplace(id, AZStd::move(channel)); - } + m_keyboardDevice = AZStd::make_unique(keyboardDeviceId); + m_mouseDevice = AZStd::make_unique(mouseDeviceId); - const AzFramework::InputDevice* mouseDevice = deviceMap.find(AzFramework::InputDeviceMouse::Id)->second; - - // Create synthetic mouse button input channels. - for (auto [_, id] : QtMouseButtonMappings) - { - AZStd::unique_ptr channel = - AZStd::make_unique(id, *mouseDevice, m_cursorPosition); - channel->SetUpdateEventsEnabled(false); - m_channels.emplace(id, AZStd::move(channel)); - } - - // Create synthetic mouse movement channels. - for (const auto& id : { AzFramework::InputDeviceMouse::Movement::X, AzFramework::InputDeviceMouse::Movement::Y, - AzFramework::InputDeviceMouse::Movement::Z, AzFramework::InputDeviceMouse::SystemCursorPosition }) - { - AZStd::unique_ptr channel = - AZStd::make_unique(id, *mouseDevice, m_cursorPosition); - channel->SetUpdateEventsEnabled(false); - m_channels.emplace(id, AZStd::move(channel)); - } + AddChannels(m_keyboardDevice->m_allChannelsById); + AddChannels(m_mouseDevice->m_allChannelsById); // Install a global event filter to ensure we don't miss mouse and key release events. QApplication::instance()->installEventFilter(this); @@ -193,7 +194,8 @@ namespace AzToolsFramework // 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(); - return deviceId == AzFramework::InputDeviceMouse::Id || deviceId == AzFramework::InputDeviceKeyboard::Id; + return deviceId.GetNameCrc32() == AzFramework::InputDeviceMouse::Id.GetNameCrc32() || + deviceId.GetNameCrc32() == AzFramework::InputDeviceKeyboard::Id.GetNameCrc32(); } bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event) @@ -222,7 +224,8 @@ namespace AzToolsFramework // Map key events to input channels. // ShortcutOverride is used in lieu of KeyPress for high priority input channels like Alt // that need to be accepted and stopped before they bubble up and cause unintended behavior. - else if (event->type() == QEvent::Type::KeyPress || event->type() == QEvent::Type::KeyRelease || + else if ( + event->type() == QEvent::Type::KeyPress || event->type() == QEvent::Type::KeyRelease || event->type() == QEvent::Type::ShortcutOverride) { QKeyEvent* keyEvent = static_cast(event); @@ -321,11 +324,16 @@ namespace AzToolsFramework void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent) { + // Ignore key repeat events, they're unrelated to actual physical button presses. + if (keyEvent->isAutoRepeat()) + { + return; + } + const Qt::Key key = static_cast(keyEvent->key()); // For ShortcutEvent, only continue processing if we're in the HighPriorityKeys set. - if (keyEvent->type() != QEvent::Type::ShortcutOverride || - AZStd::find(HighPriorityKeys.begin(), HighPriorityKeys.end(), key) != HighPriorityKeys.end()) + if (keyEvent->type() != QEvent::Type::ShortcutOverride || m_highPriorityKeys.find(key) != m_highPriorityKeys.end()) { if (auto keyIt = m_keyMappings.find(key); keyIt != m_keyMappings.end()) { @@ -373,7 +381,7 @@ namespace AzToolsFramework if (channelData.second->IsActive()) { channelData.second->UpdateState(false); - NotifyUpdateChannelIfNotIdle(channelData.second.get(), event); + NotifyUpdateChannelIfNotIdle(channelData.second, event); } } m_mouseChannelsNeedUpdate = false; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h index 8ad96e3949..08202f4969 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h @@ -20,9 +20,12 @@ #include #include +#include +#include + #include #include -#endif //!defined(Q_MOC_RUN) +#endif //! defined(Q_MOC_RUN) class QWidget; class QKeyEvent; @@ -38,7 +41,7 @@ namespace AzToolsFramework Q_OBJECT public: - QtEventToAzInputMapper(QWidget* sourceWidget); + QtEventToAzInputMapper(QWidget* sourceWidget, int syntheticDeviceId = 0); ~QtEventToAzInputMapper() = default; //! Queries whether a given input channel has a synthetic equivalent mapped @@ -57,37 +60,71 @@ namespace AzToolsFramework void InputChannelUpdated(const AzFramework::InputChannel* channel, QEvent* event); private: + // Gets an input channel of the specified type by ID. template TInputChannel* GetInputChannel(const AzFramework::InputChannelId& id) { auto channelIt = m_channels.find(id); if (channelIt != m_channels.end()) { - return static_cast(channelIt->second.get()); + return static_cast(channelIt->second); } return {}; } + // Adds channels from the specified channel container to our input channel ID -> input channel lookup table. + // Used for rapid lookup. + template + void AddChannels(const TContainer& container) + { + for (const auto& channelData : container) + { + // Break const as we're taking these input channels from devices we own. + m_channels.emplace(channelData.first, const_cast(channelData.second)); + } + } + + // Our synthetic Keyboard device, does no internal keyboard handling and instead listens to this class for updates. + class EditorQtKeyboardDevice : public AzFramework::InputDeviceKeyboard + { + public: + EditorQtKeyboardDevice(AzFramework::InputDeviceId id); + + friend class QtEventToAzInputMapper; + }; + + // Our synthetic Mouse device, does no internal keyboard handling and instead listens to this class for updates. + class EditorQtMouseDevice : public AzFramework::InputDeviceMouse + { + public: + EditorQtMouseDevice(AzFramework::InputDeviceId id); + + friend class QtEventToAzInputMapper; + }; + + // Emits InputChannelUpdated if channel has transitioned in state (i.e. has gone from active to inactive or vice versa). void NotifyUpdateChannelIfNotIdle(const AzFramework::InputChannel* channel, QEvent* event); + // Processes any pending mouse movement events, this allows mouse movement channels to close themselves. void ProcessPendingMouseEvents(); + // Handle mouse click events. void HandleMouseButtonEvent(QMouseEvent* mouseEvent); + // Handle mouse move events. void HandleMouseMoveEvent(QMouseEvent* mouseEvent); + // Handles key press / release events (or ShortcutOverride events for keys listed in m_highPriorityKeys). void HandleKeyEvent(QKeyEvent* keyEvent); + // Handles mouse wheel events. void HandleWheelEvent(QWheelEvent* wheelEvent); + // Handles focus change events. void HandleFocusChange(QEvent* event); - // Mapping from Qt::Keys to InputChannelIds. - // Used to populate m_keyMappings. - static AZStd::array, 91> QtKeyMappings; - // Mapping from Qt::MouseButtons to InputChannelIds. - // Used to populate m_mouseButtonMappings. - static AZStd::array, 5> QtMouseButtonMappings; - // A set of high priority keys that need to be processed at the ShortcutOverride level instead of the - // KeyEvent level. This prevents e.g. the main menu bar from processing a press of the "alt" key when the - // viewport consumes the event. - static AZStd::array HighPriorityKeys; + // Populates m_keyMappings. + void InitializeKeyMappings(); + // Populates m_mouseButtonMappings. + void InitializeMouseButtonMappings(); + // Populates m_highPriorityKeys. + void InitializeHighPriorityKeys(); // The current keyboard modifier state used by our synthetic key input channels. AZStd::shared_ptr m_keyboardModifiers; @@ -97,13 +134,19 @@ namespace AzToolsFramework AZStd::unordered_map m_keyMappings; // A lookup table for Qt mouse button -> AZ input channel. AZStd::unordered_map m_mouseButtonMappings; - // Our set of synthetic input channels that can be mapped by MapQtEventToAzInput. - // These channels do not broadcast state changes to the actual AZ input devices, as we're bypassing - // that system to integrate with Qt to allow coexistence with the platform native implementation. - AZStd::unordered_map> m_channels; + // A set of high priority keys that need to be processed at the ShortcutOverride level instead of the + // KeyEvent level. This prevents e.g. the main menu bar from processing a press of the "alt" key when the + // viewport consumes the event. + AZStd::unordered_set m_highPriorityKeys; + // A lookup table for AZ input channel ID -> physical input channel on our mouse or keyboard device. + AZStd::unordered_map m_channels; // The source widget to map events from, used to calculate the relative mouse position within the widget bounds. QWidget* m_sourceWidget; // Flags when mouse movement channels have been opened and may need to be closed (as there are no movement ended events). bool m_mouseChannelsNeedUpdate = false; + + // Our viewport-specific AZ devices. We control their internal input channel states. + AZStd::unique_ptr m_mouseDevice; + AZStd::unique_ptr m_keyboardDevice; }; } // namespace AzToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index 977c3f522c..316741cdc5 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -131,7 +131,6 @@ namespace AtomToolsFramework private: void SendWindowResizeEvent(); - bool CanInputGrantFocus(const AzFramework::InputChannel& inputChannel) const; // The underlying ViewportContext, our entry-point to the Atom RPI. AZ::RPI::ViewportContextPtr m_viewportContext; @@ -159,6 +158,6 @@ namespace AtomToolsFramework // The viewport settings (e.g. grid snapping, grid size) for this viewport. const AzToolsFramework::ViewportInteraction::ViewportSettings* m_viewportSettings = nullptr; // Maps our internal Qt events into AzFramework InputChannels for our ViewportControllerList. - AzToolsFramework::QtEventToAzInputMapper m_inputChannelMapper = AzToolsFramework::QtEventToAzInputMapper(this); + AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr; }; } //namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 8488fb574a..86ee8a9c39 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -38,21 +38,6 @@ namespace AtomToolsFramework setUpdatesEnabled(false); setFocusPolicy(Qt::FocusPolicy::WheelFocus); setMouseTracking(true); - - // Forward input events to our controller list. - QObject::connect(&m_inputChannelMapper, &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, this, - [this](const AzFramework::InputChannel* inputChannel, QEvent* event) - { - AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); - if (m_controllerList->HandleInputChannelEvent({GetId(), windowId, *inputChannel})) - { - // If the controller handled the input event, mark the event as accepted so it doesn't continue to propagate. - if (event) - { - event->setAccepted(true); - } - } - }); } bool RenderViewportWidget::InitializeViewportContext(AzFramework::ViewportId id) @@ -96,6 +81,23 @@ namespace AtomToolsFramework AZ::TickBus::Handler::BusConnect(); AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle); + m_inputChannelMapper = new AzToolsFramework::QtEventToAzInputMapper(this, id); + + // Forward input events to our controller list. + QObject::connect(m_inputChannelMapper, &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, this, + [this](const AzFramework::InputChannel* inputChannel, QEvent* event) + { + AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId()); + if (m_controllerList->HandleInputChannelEvent({GetId(), windowId, *inputChannel})) + { + // If the controller handled the input event, mark the event as accepted so it doesn't continue to propagate. + if (event) + { + event->setAccepted(true); + } + } + }); + return true; } @@ -172,7 +174,7 @@ namespace AtomToolsFramework } // Only forward channels that aren't covered by our Qt -> AZ event mapper - if (m_inputChannelMapper.HandlesInputEvent(inputChannel)) + if (!m_inputChannelMapper || m_inputChannelMapper->HandlesInputEvent(inputChannel)) { return false; } From 049ef81fe9c676ae7237dc2bfe538f7d0f3a737d Mon Sep 17 00:00:00 2001 From: nvsickle Date: Thu, 1 Jul 2021 17:55:27 -0700 Subject: [PATCH 08/12] Address some review feedback, fix license Signed-off-by: nvsickle --- .../Input/QtEventToAzInputManager.cpp | 30 +++++++------------ .../Input/QtEventToAzInputManager.h | 9 ++---- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index e5c565f8f5..a90908fd9d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -1,12 +1,7 @@ /* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. + * 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 (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * SPDX-License-Identifier: Apache-2.0 OR MIT * */ @@ -29,8 +24,9 @@ namespace AzToolsFramework { void QtEventToAzInputMapper::InitializeKeyMappings() { - // This asumes modifier keys (ctrl/shift/alt) map to the left control/shift/alt keys as Qt provides no way to disambiguate - // in a platform agnostic manner. This could be expanded later with a PAL mapping from native scan codes from QKeyEvents, if needed. + // This assumes modifier keys (ctrl/shift/alt) map to the left control/shift/alt keys as Qt provides no way to disambiguate + // in a platform agnostic manner. This could be expanded later with a PAL mapping from native scan codes acquired from + // QKeyEvents, if needed. m_keyMappings = { { { Qt::Key_0, AzFramework::InputDeviceKeyboard::Key::Alphanumeric0 }, { Qt::Key_1, AzFramework::InputDeviceKeyboard::Key::Alphanumeric1 }, @@ -191,6 +187,12 @@ 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(); @@ -266,23 +268,13 @@ namespace AzToolsFramework { auto systemCursorChannel = GetInputChannel(AzFramework::InputDeviceMouse::SystemCursorPosition); - auto cursorXChannel = - GetInputChannel(AzFramework::InputDeviceMouse::Movement::X); - auto cursorYChannel = - GetInputChannel(AzFramework::InputDeviceMouse::Movement::Y); auto cursorZChannel = GetInputChannel(AzFramework::InputDeviceMouse::Movement::Z); systemCursorChannel->ProcessRawInputEvent(m_cursorPosition->m_normalizedPositionDelta.GetLength()); - cursorXChannel->ProcessRawInputEvent( - m_cursorPosition->m_normalizedPositionDelta.GetX() * aznumeric_cast(m_sourceWidget->width())); - cursorYChannel->ProcessRawInputEvent( - m_cursorPosition->m_normalizedPositionDelta.GetY() * aznumeric_cast(m_sourceWidget->height())); cursorZChannel->ProcessRawInputEvent(0.f); NotifyUpdateChannelIfNotIdle(systemCursorChannel, nullptr); - NotifyUpdateChannelIfNotIdle(cursorXChannel, nullptr); - NotifyUpdateChannelIfNotIdle(cursorYChannel, nullptr); NotifyUpdateChannelIfNotIdle(cursorZChannel, nullptr); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h index 08202f4969..515e0c3f8a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h @@ -1,12 +1,7 @@ /* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. + * 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 (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * SPDX-License-Identifier: Apache-2.0 OR MIT * */ From 4babf69361d066cd3eb690c389f056b1dac74c3b Mon Sep 17 00:00:00 2001 From: nvsickle Date: Fri, 2 Jul 2021 15:26:16 -0700 Subject: [PATCH 09/12] Address a few more review things Signed-off-by: nvsickle --- Code/Editor/EditorViewportWidget.cpp | 4 ++-- .../AzFramework/Viewport/CameraInput.cpp | 3 ++- .../Input/QtEventToAzInputManager.cpp | 22 ++++++++++++++++--- .../Input/QtEventToAzInputManager.h | 7 +++++- .../Viewport/RenderViewportWidget.h | 6 +++++ .../Source/Viewport/RenderViewportWidget.cpp | 6 +++++ 6 files changed, 41 insertions(+), 7 deletions(-) diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index 1cfc3b6dd0..1b461e1356 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -714,7 +714,7 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) if (m_renderViewport) { - m_renderViewport->GetControllerList()->SetEnabled(false); + m_renderViewport->SetInputProcessingEnabled(false); } } break; @@ -738,7 +738,7 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) if (m_renderViewport) { - m_renderViewport->GetControllerList()->SetEnabled(true); + m_renderViewport->SetInputProcessingEnabled(true); } break; diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index a85d631160..0516cdd591 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -747,7 +747,7 @@ namespace AzFramework return button == inputChannelId; }); - // Accept active mouse channel updates, inactive movement channels will just have a 0 delta. + // accept active mouse channel updates, inactive movement channels will just have a 0 delta if (inputChannel.IsActive()) { if (inputChannelId == InputDeviceMouse::Movement::X) @@ -763,6 +763,7 @@ namespace AzFramework return ScrollEvent{ inputChannel.GetValue() }; } } + if (wasMouseButton || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId)) { return DiscreteInputEvent{ inputChannelId, inputChannel.GetState() }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index a90908fd9d..039c1da8f2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -200,8 +200,24 @@ namespace AzToolsFramework deviceId.GetNameCrc32() == AzFramework::InputDeviceKeyboard::Id.GetNameCrc32(); } + void QtEventToAzInputMapper::SetEnabled(bool enabled) + { + m_enabled = enabled; + if (!enabled) + { + // Send an internal focus change event to reset our input state to fresh if we're disabled. + HandleFocusChange(nullptr); + } + } + bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event) { + // Abort if processing isn't enabled. + if (!m_enabled) + { + return false; + } + // Because there's no "end" to mouse movement and wheel events, we reset mouse movement channels that have been opened // during the next processed non-mouse event. if (m_mouseChannelsNeedUpdate && event->type() != QEvent::Type::MouseMove && event->type() != QEvent::Type::Wheel) @@ -268,14 +284,14 @@ namespace AzToolsFramework { auto systemCursorChannel = GetInputChannel(AzFramework::InputDeviceMouse::SystemCursorPosition); - auto cursorZChannel = + auto mouseWheelChannel = GetInputChannel(AzFramework::InputDeviceMouse::Movement::Z); systemCursorChannel->ProcessRawInputEvent(m_cursorPosition->m_normalizedPositionDelta.GetLength()); - cursorZChannel->ProcessRawInputEvent(0.f); + mouseWheelChannel->ProcessRawInputEvent(0.f); NotifyUpdateChannelIfNotIdle(systemCursorChannel, nullptr); - NotifyUpdateChannelIfNotIdle(cursorZChannel, nullptr); + NotifyUpdateChannelIfNotIdle(mouseWheelChannel, nullptr); } void QtEventToAzInputMapper::HandleMouseButtonEvent(QMouseEvent* mouseEvent) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h index 515e0c3f8a..28ad949e7c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h @@ -44,6 +44,9 @@ namespace AzToolsFramework //! \returns true if the channel is handled by MapQtEventToAzInput. bool HandlesInputEvent(const AzFramework::InputChannel& channel) const; + //! Sets whether or not this input mapper should be updating its input channels from Qt events. + void SetEnabled(bool enabled); + // QObject overrides... bool eventFilter(QObject* object, QEvent* event) override; @@ -64,7 +67,7 @@ namespace AzToolsFramework { return static_cast(channelIt->second); } - return {}; + return nullptr; } // Adds channels from the specified channel container to our input channel ID -> input channel lookup table. @@ -139,6 +142,8 @@ namespace AzToolsFramework QWidget* m_sourceWidget; // Flags when mouse movement channels have been opened and may need to be closed (as there are no movement ended events). bool m_mouseChannelsNeedUpdate = false; + // Flags whether or not Qt events should currently be processed. + bool m_enabled = true; // Our viewport-specific AZ devices. We control their internal input channel states. AZStd::unique_ptr m_mouseDevice; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index 316741cdc5..50378c15bd 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -82,6 +82,12 @@ namespace AtomToolsFramework //! Gets the default camera that's been automatically registered to our ViewportContext. AZ::RPI::ViewPtr GetDefaultCamera(); AZ::RPI::ConstViewPtr GetDefaultCamera() const; + //! Sets whether or not input processing is enabled for this RenderViewportWidget. + //! While input processing is enabled, synthetic input events may appear in OnInputChannelEventFiltered + //! due to internal viewport input mapping via QtEventToAzInputMapper, so it may be desirable to disable + //! camera controller input processing wholesale to avoid competing input messages. + //! Input processing is enabled by default. + void SetInputProcessingEnabled(bool enabled); // AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler ... AzFramework::CameraState GetCameraState() override; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 86ee8a9c39..9fcd93f069 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -299,6 +299,12 @@ namespace AtomToolsFramework } } + void RenderViewportWidget::SetInputProcessingEnabled(bool enabled) + { + m_inputChannelMapper->SetEnabled(enabled); + m_controllerList->SetEnabled(enabled); + } + AzFramework::CameraState RenderViewportWidget::GetCameraState() { AZ::RPI::ViewPtr currentView = m_viewportContext->GetDefaultView(); From fa9a12eac3101b1c58b5ef439321edb08a28b543 Mon Sep 17 00:00:00 2001 From: nvsickle Date: Fri, 2 Jul 2021 17:33:58 -0700 Subject: [PATCH 10/12] Forward double click events as well Signed-off-by: nvsickle --- .../AzToolsFramework/Input/QtEventToAzInputManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index 039c1da8f2..62fad8f640 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -250,7 +250,7 @@ namespace AzToolsFramework HandleKeyEvent(keyEvent); } // Map mouse events to input channels. - else if (event->type() == QEvent::Type::MouseButtonPress || event->type() == QEvent::Type::MouseButtonRelease) + else if (event->type() == QEvent::Type::MouseButtonPress || event->type() == QEvent::Type::MouseButtonRelease || event->type() == QEvent::Type::MouseButtonDblClick) { QMouseEvent* mouseEvent = static_cast(event); HandleMouseButtonEvent(mouseEvent); @@ -304,7 +304,7 @@ namespace AzToolsFramework if (buttonChannel) { - if (mouseEvent->type() == QEvent::Type::MouseButtonPress) + if (mouseEvent->type() != QEvent::Type::MouseButtonRelease) { buttonChannel->UpdateState(true); } From 964b4247f761f386c2df5debe533ae37cc68ead1 Mon Sep 17 00:00:00 2001 From: nvsickle Date: Fri, 2 Jul 2021 17:48:46 -0700 Subject: [PATCH 11/12] Update license header Signed-off-by: nvsickle --- .../AzToolsFramework/Input/QtEventToAzInputManager.cpp | 3 ++- .../AzToolsFramework/Input/QtEventToAzInputManager.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index 62fad8f640..e276d706d1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) Contributors to the Open 3D Engine Project + * 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 * diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h index 28ad949e7c..f08dab59d6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h @@ -1,5 +1,6 @@ /* - * Copyright (c) Contributors to the Open 3D Engine Project + * 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 * From c8fb4537608bf575cc737cc85353de9312895040 Mon Sep 17 00:00:00 2001 From: nvsickle Date: Wed, 7 Jul 2021 14:40:59 -0700 Subject: [PATCH 12/12] Fix copyright header copy/paste issue. Signed-off-by: nvsickle --- .../AzToolsFramework/Input/QtEventToAzInputManager.cpp | 5 ++--- .../AzToolsFramework/Input/QtEventToAzInputManager.h | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp index e276d706d1..eb5034fdd5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp @@ -1,7 +1,6 @@ /* - * 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. - * + * 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 * */ diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h index f08dab59d6..6d95f3a4b0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h @@ -1,7 +1,6 @@ /* - * 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. - * + * 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 * */