diff --git a/README.md b/README.md index 2adb47a..a23b063 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Also note that the NetImGui branch is not up to date with any of this fork's cha - Updated Dear ImGui to `v1.91.4`. - Added ImPlot `v0.17+419a8a0f`. - `ImGui::IsKey*` now functional with all known ImGui keys. -- Updated input handling flow to be [standard compliant](https://github.com/ocornut/imgui/issues/4921) with Dear ImGui 1.87 which makes ImGui react better at low FPS. ~~Will add `IMGUI_DISABLE_OBSOLETE_KEYIO` preprocessor once I've ripped out old style input.~~ +- Updated input handling flow to be [standard compliant](https://github.com/ocornut/imgui/issues/4921) with Dear ImGui 1.87 which makes ImGui react better at low FPS. - Allowed `UTexture` for Texture Manager so render targets can also be rendered to quads rather than just being limited to using `UTexture2D` instances. - Added the ability to instruct ImGui context to build custom fonts (like FontAwesome). - Changed Module Type to `Runtime`. diff --git a/Source/ImGui/Private/ImGuiContextProxy.cpp b/Source/ImGui/Private/ImGuiContextProxy.cpp index 523b860..715c292 100644 --- a/Source/ImGui/Private/ImGuiContextProxy.cpp +++ b/Source/ImGui/Private/ImGuiContextProxy.cpp @@ -92,6 +92,7 @@ FImGuiContextProxy::FImGuiContextProxy(const FString& InName, int32 InContextInd // Start initialization. ImGuiIO& IO = ImGui::GetIO(); + InputState.IOFunctions = IO; // Set session data storage. IO.IniFilename = IniFilename.c_str(); @@ -104,7 +105,7 @@ FImGuiContextProxy::FImGuiContextProxy(const FString& InName, int32 InContextInd SetDPIScale(InDPIScale); // Initialize key mapping, so context can correctly interpret input state. - ImGuiInterops::SetUnrealKeyMap(IO); + ImGuiInterops::SetUnrealKeyMap(); // Begin frame to complete context initialization (this is to avoid problems with other systems calling to ImGui // during startup). @@ -200,16 +201,23 @@ void FImGuiContextProxy::Tick(float DeltaSeconds) EndFrame(); } + ImGuiIO& IO = ImGui::GetIO(); + // Update context information (some data need to be collected before starting a new frame while some other data // may need to be collected after). bHasActiveItem = ImGui::IsAnyItemActive(); MouseCursor = ImGuiInterops::ToSlateMouseCursor(ImGui::GetMouseCursor()); + // Update remaining context information. + bWantsMouseCapture = IO.WantCaptureMouse; + + // Set Config and Backend flags in this IO context, as InputState IO is only for function use + ImGuiInterops::SetFlag(IO.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard, InputState.IsKeyboardNavigationEnabled()); + ImGuiInterops::SetFlag(IO.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad, InputState.IsGamepadNavigationEnabled()); + ImGuiInterops::SetFlag(IO.BackendFlags, ImGuiBackendFlags_HasGamepad, InputState.HasGamepad()); + // Begin a new frame and set the context back to a state in which it allows to draw controls. BeginFrame(DeltaSeconds); - - // Update remaining context information. - bWantsMouseCapture = ImGui::GetIO().WantCaptureMouse; } } @@ -220,7 +228,6 @@ void FImGuiContextProxy::BeginFrame(float DeltaTime) ImGuiIO& IO = ImGui::GetIO(); IO.DeltaTime = DeltaTime; - ImGuiInterops::CopyInput(IO, InputState); InputState.ClearUpdateState(); IO.DisplaySize = { (float)DisplaySize.X, (float)DisplaySize.Y }; diff --git a/Source/ImGui/Private/ImGuiInputHandler.cpp b/Source/ImGui/Private/ImGuiInputHandler.cpp index 873d423..1e836e2 100644 --- a/Source/ImGui/Private/ImGuiInputHandler.cpp +++ b/Source/ImGui/Private/ImGuiInputHandler.cpp @@ -9,7 +9,6 @@ #include "ImGuiModuleSettings.h" #include "VersionCompatibility.h" -#include #include #include #include @@ -45,68 +44,56 @@ FReply UImGuiInputHandler::OnKeyDown(const FKeyEvent& KeyEvent) bool bConsume = false; if (InputState->IsGamepadNavigationEnabled()) { - InputState->SetGamepadNavigationKey(KeyEvent, true); + InputState->SetKeyDown(KeyEvent, true); bConsume = !ModuleManager->GetProperties().IsGamepadInputShared(); } return ToReply(bConsume); } - else + + // Ignore console events, so we don't block it from opening. + if (IsConsoleEvent(KeyEvent)) { - // Ignore console events, so we don't block it from opening. - if (IsConsoleEvent(KeyEvent)) - { - return ToReply(false); - } + return ToReply(false); + } #if WITH_EDITOR - // If there is no active ImGui control that would get precedence and this key event is bound to a stop play session - // command, then ignore that event and let the command execute. - if (!HasImGuiActiveItem() && IsStopPlaySessionEvent(KeyEvent)) - { - return ToReply(false); - } + // If there is no active ImGui control that would get precedence and this key event is bound to a stop play session + // command, then ignore that event and let the command execute. + if (!HasImGuiActiveItem() && IsStopPlaySessionEvent(KeyEvent)) + { + return ToReply(false); + } #endif // WITH_EDITOR - const bool bConsume = !ModuleManager->GetProperties().IsKeyboardInputShared(); - - // With shared input we can leave command bindings for DebugExec to handle, otherwise we need to do it here. - if (bConsume && IsToggleInputEvent(KeyEvent)) - { - ModuleManager->GetProperties().ToggleInput(); - } - - InputState->SetKeyDown(KeyEvent, true); - CopyModifierKeys(KeyEvent); - - InputState->KeyDownEvents.Add(KeyEvent.GetKeyCode(), KeyEvent); + const bool bConsume = !ModuleManager->GetProperties().IsKeyboardInputShared(); - return ToReply(bConsume); + // With shared input we can leave command bindings for DebugExec to handle, otherwise we need to do it here. + if (bConsume && IsToggleInputEvent(KeyEvent)) + { + ModuleManager->GetProperties().ToggleInput(); } + + InputState->SetKeyDown(KeyEvent, true); + return ToReply(bConsume); } FReply UImGuiInputHandler::OnKeyUp(const FKeyEvent& KeyEvent) { - InputState->KeyUpEvents.Add(KeyEvent.GetKeyCode(), KeyEvent); - if (KeyEvent.GetKey().IsGamepadKey()) { bool bConsume = false; if (InputState->IsGamepadNavigationEnabled()) { - InputState->SetGamepadNavigationKey(KeyEvent, false); + InputState->SetKeyDown(KeyEvent, false); bConsume = !ModuleManager->GetProperties().IsGamepadInputShared(); } return ToReply(bConsume); } - else - { - InputState->SetKeyDown(KeyEvent, false); - CopyModifierKeys(KeyEvent); - return ToReply(!ModuleManager->GetProperties().IsKeyboardInputShared()); - } + InputState->SetKeyDown(KeyEvent, false); + return ToReply(!ModuleManager->GetProperties().IsKeyboardInputShared()); } FReply UImGuiInputHandler::OnAnalogValueChanged(const FAnalogInputEvent& AnalogInputEvent) @@ -224,7 +211,6 @@ void UImGuiInputHandler::OnGamepadInputDisabled() if (bGamepadInputEnabled) { bGamepadInputEnabled = false; - InputState->ResetGamepadNavigation(); } } @@ -247,14 +233,7 @@ void UImGuiInputHandler::OnMouseInputDisabled() } } -void UImGuiInputHandler::CopyModifierKeys(const FInputEvent& InputEvent) -{ - InputState->SetControlDown(InputEvent.IsControlDown()); - InputState->SetShiftDown(InputEvent.IsShiftDown()); - InputState->SetAltDown(InputEvent.IsAltDown()); -} - -bool UImGuiInputHandler::IsConsoleEvent(const FKeyEvent& KeyEvent) const +bool UImGuiInputHandler::IsConsoleEvent(const FKeyEvent& KeyEvent) { // Checking modifiers is based on console implementation. const bool bModifierDown = KeyEvent.IsControlDown() || KeyEvent.IsShiftDown() || KeyEvent.IsAltDown() || KeyEvent.IsCommandDown(); diff --git a/Source/ImGui/Private/ImGuiInputState.cpp b/Source/ImGui/Private/ImGuiInputState.cpp index e404ea4..a47f8b9 100644 --- a/Source/ImGui/Private/ImGuiInputState.cpp +++ b/Source/ImGui/Private/ImGuiInputState.cpp @@ -14,75 +14,112 @@ FImGuiInputState::FImGuiInputState() void FImGuiInputState::AddCharacter(TCHAR Char) { - InputCharacters.Add(Char); + IOFunctions.AddInputCharacter(ImGuiInterops::CastInputChar(Char)); } -void FImGuiInputState::SetKeyDown(uint32 KeyIndex, bool bIsDown) +void FImGuiInputState::SetKeyDown(const FKeyEvent& KeyEvent, bool bIsDown) { - if (KeyIndex < Utilities::GetArraySize(KeysDown)) - { - if (KeysDown[KeyIndex] != bIsDown) - { - KeysDown[KeyIndex] = bIsDown; - KeysUpdateRange.AddPosition(KeyIndex); - } - } + const FKey& Key = KeyEvent.GetKey(); + SetKeyDown(Key, bIsDown); } -void FImGuiInputState::SetMouseDown(uint32 MouseIndex, bool bIsDown) +void FImGuiInputState::SetKeyDown(const FKey& Key, bool bIsDown) { - if (MouseIndex < Utilities::GetArraySize(MouseButtonsDown)) + const ImGuiKey& ImKey = ImGuiInterops::UnrealToImGuiKey(Key); + IOFunctions.AddKeyEvent(ImKey, bIsDown); + + if (ImKey == ImGuiKey_LeftCtrl || ImKey == ImGuiKey_RightCtrl) + { + bIsControlDown = bIsDown; + } + else if (ImKey == ImGuiKey_LeftShift || ImKey == ImGuiKey_RightShift) + { + bIsShiftDown = bIsDown; + } + else if (ImKey == ImGuiKey_LeftAlt || ImKey == ImGuiKey_RightAlt) + { + bIsAltDown = bIsDown; + } + else if (ImKey == ImGuiKey_LeftSuper || ImKey == ImGuiKey_RightSuper) + { + bIsCommandDown = bIsDown; + } + + const ImGuiKey& ImMod = ImGuiInterops::UnrealToImGuiMod(Key); + if (ImMod != ImGuiKey_None) { - if (MouseButtonsDown[MouseIndex] != bIsDown) - { - MouseButtonsDown[MouseIndex] = bIsDown; - MouseButtonsUpdateRange.AddPosition(MouseIndex); - } + IOFunctions.AddKeyEvent(ImMod, bIsDown); } } -void FImGuiInputState::ClearUpdateState() +void FImGuiInputState::SetMouseDown(const FPointerEvent& MouseEvent, bool bIsDown) { - ClearCharacters(); + const uint32 MouseIndex = ImGuiInterops::GetMouseIndex(MouseEvent); + IOFunctions.AddMouseButtonEvent(MouseIndex, bIsDown); +} - KeyDownEvents.Reset(); - KeyUpEvents.Reset(); +void FImGuiInputState::SetMouseDown(const FKey& MouseButton, bool bIsDown) +{ + const uint32 MouseIndex = ImGuiInterops::GetMouseIndex(MouseButton); + IOFunctions.AddMouseButtonEvent(MouseIndex, bIsDown); +} - KeysUpdateRange.SetEmpty(); - MouseButtonsUpdateRange.SetEmpty(); +void FImGuiInputState::AddMouseWheelDelta(float DeltaValue) +{ + IOFunctions.AddMouseWheelEvent(0, DeltaValue); +} - MouseWheelDelta = 0.f; +void FImGuiInputState::SetMousePosition(const FVector2D& Position) +{ + IOFunctions.AddMousePosEvent(Position.X, Position.Y); + MousePosition = Position; +} - bTouchProcessed = bTouchDown; +void FImGuiInputState::SetMousePointer(bool bInHasMousePointer) +{ + IOFunctions.MouseDrawCursor = bInHasMousePointer; + bHasMousePointer = bInHasMousePointer; } -void FImGuiInputState::ClearCharacters() +void FImGuiInputState::SetTouchDown(bool bIsDown) { - InputCharacters.Empty(); + IOFunctions.AddMouseButtonEvent(0, bIsDown); + bTouchDown = bIsDown; } -void FImGuiInputState::ClearKeys() +void FImGuiInputState::SetTouchPosition(const FVector2D& Position) { - using std::fill; - fill(KeysDown, &KeysDown[Utilities::GetArraySize(KeysDown)], false); + IOFunctions.AddMousePosEvent(Position.X, Position.Y); +} - // Mark the whole array as dirty because potentially each entry could be affected. - KeysUpdateRange.SetFull(); +void FImGuiInputState::SetGamepadNavigationAxis(const FAnalogInputEvent& AnalogInputEvent, float Value) +{ + ImGuiInterops::SetGamepadNavigationAxis(IOFunctions, AnalogInputEvent.GetKey(), Value); } -void FImGuiInputState::ClearMouseButtons() +void FImGuiInputState::SetKeyboardNavigationEnabled(bool bEnabled) { - using std::fill; - fill(MouseButtonsDown, &MouseButtonsDown[Utilities::GetArraySize(MouseButtonsDown)], false); + bKeyboardNavigationEnabled = bEnabled; +} - // Mark the whole array as dirty because potentially each entry could be affected. - MouseButtonsUpdateRange.SetFull(); +void FImGuiInputState::SetGamepadNavigationEnabled(bool bEnabled) +{ + bGamepadNavigationEnabled = bEnabled; +} + +void FImGuiInputState::SetGamepad(bool bInHasGamepad) +{ + bHasGamepad = bInHasGamepad; +} + +void FImGuiInputState::ClearUpdateState() +{ + bTouchProcessed = bTouchDown; } void FImGuiInputState::ClearMouseAnalogue() { MousePosition = FVector2D::ZeroVector; - MouseWheelDelta = 0.f; } void FImGuiInputState::ClearModifierKeys() @@ -90,11 +127,6 @@ void FImGuiInputState::ClearModifierKeys() bIsControlDown = false; bIsShiftDown = false; bIsAltDown = false; -} - -void FImGuiInputState::ClearNavigationInputs() -{ - using std::fill; - fill(NavigationInputs, &NavigationInputs[Utilities::GetArraySize(NavigationInputs)], 0.f); + bIsCommandDown = false; } diff --git a/Source/ImGui/Private/ImGuiInputState.h b/Source/ImGui/Private/ImGuiInputState.h index bcce726..e51c085 100644 --- a/Source/ImGui/Private/ImGuiInputState.h +++ b/Source/ImGui/Private/ImGuiInputState.h @@ -16,84 +16,51 @@ class FImGuiInputState // Characters buffer. using FCharactersBuffer = TArray>; - // Array for mouse button states. - using FMouseButtonsArray = ImGuiInterops::ImGuiTypes::FMouseButtonsArray; - - // Array for key states. - using FKeysArray = ImGuiInterops::ImGuiTypes::FKeysArray; - - // Array for navigation input states. - using FNavInputArray = ImGuiInterops::ImGuiTypes::FNavInputArray; - - // Pair of indices defining range in mouse buttons array. - using FMouseButtonsIndexRange = Utilities::TArrayIndexRange; - - // Pair of indices defining range in keys array. - using FKeysIndexRange = Utilities::TArrayIndexRange; - // Create empty state with whole range instance with the whole update state marked as dirty. FImGuiInputState(); - // Get reference to input characters buffer. - const FCharactersBuffer& GetCharacters() const { return InputCharacters; } - // Add a character to the characters buffer. We can store and send to ImGui up to 16 characters per frame. Any // character beyond that limit will be discarded. // @param Char - Character to add void AddCharacter(TCHAR Char); - // Get reference to the array with key down states. - const FKeysArray& GetKeys() const { return KeysDown; } - - // Get possibly empty range of indices bounding dirty part of the keys array. - const FKeysIndexRange& GetKeysUpdateRange() const { return KeysUpdateRange; } - // Change state of the key in the keys array and expand range bounding dirty part of the array. // @param KeyEvent - Key event representing the key // @param bIsDown - True, if key is down - void SetKeyDown(const FKeyEvent& KeyEvent, bool bIsDown) { SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), bIsDown); } + void SetKeyDown(const FKeyEvent& KeyEvent, bool bIsDown); // Change state of the key in the keys array and expand range bounding dirty part of the array. // @param Key - Keyboard key // @param bIsDown - True, if key is down - void SetKeyDown(const FKey& Key, bool bIsDown) { SetKeyDown(ImGuiInterops::GetKeyIndex(Key), bIsDown); } - - // Get reference to the array with mouse button down states. - const FMouseButtonsArray& GetMouseButtons() const { return MouseButtonsDown; } + void SetKeyDown(const FKey& Key, bool bIsDown); - // Get possibly empty range of indices bounding dirty part of the mouse buttons array. - const FMouseButtonsIndexRange& GetMouseButtonsUpdateRange() const { return MouseButtonsUpdateRange; } - - // Change state of the button in the mouse buttons array and expand range bounding dirty part of the array. + // Change state of the mouse button. // @param MouseEvent - Mouse event representing mouse button // @param bIsDown - True, if button is down - void SetMouseDown(const FPointerEvent& MouseEvent, bool bIsDown) { SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), bIsDown); } + void SetMouseDown(const FPointerEvent& MouseEvent, bool bIsDown); - // Change state of the button in the mouse buttons array and expand range bounding dirty part of the array. + // Change state of the mouse button. // @param MouseButton - Mouse button key // @param bIsDown - True, if button is down - void SetMouseDown(const FKey& MouseButton, bool bIsDown) { SetMouseDown(ImGuiInterops::GetMouseIndex(MouseButton), bIsDown); } - - // Get mouse wheel delta accumulated during the last frame. - float GetMouseWheelDelta() const { return MouseWheelDelta; } + void SetMouseDown(const FKey& MouseButton, bool bIsDown); // Add mouse wheel delta. // @param DeltaValue - Mouse wheel delta to add - void AddMouseWheelDelta(float DeltaValue) { MouseWheelDelta += DeltaValue; } + void AddMouseWheelDelta(float DeltaValue); // Get the mouse position. const FVector2D& GetMousePosition() const { return MousePosition; } // Set the mouse position. // @param Position - Mouse position - void SetMousePosition(const FVector2D& Position) { MousePosition = Position; } + void SetMousePosition(const FVector2D& Position); // Check whether input has active mouse pointer. bool HasMousePointer() const { return bHasMousePointer; } // Set whether input has active mouse pointer. // @param bHasPointer - True, if input has active mouse pointer - void SetMousePointer(bool bInHasMousePointer) { bHasMousePointer = bInHasMousePointer; } + void SetMousePointer(bool bInHasMousePointer); // Check whether touch input is in progress. True, after touch is started until one frame after it has ended. // One frame delay is used to process mouse release in ImGui since touch-down is simulated with mouse-down. @@ -104,132 +71,88 @@ class FImGuiInputState // Set whether touch input is down. // @param bIsDown - True, if touch is down (or started) and false, if touch is up (or ended) - void SetTouchDown(bool bIsDown) { bTouchDown = bIsDown; } + void SetTouchDown(bool bIsDown); // Get the touch position. const FVector2D& GetTouchPosition() const { return TouchPosition; } // Set the touch position. // @param Position - Touch position - void SetTouchPosition(const FVector2D& Position) { TouchPosition = Position; } + void SetTouchPosition(const FVector2D& Position); // Get Control down state. bool IsControlDown() const { return bIsControlDown; } - // Set Control down state. - // @param bIsDown - True, if Control is down - void SetControlDown(bool bIsDown) { bIsControlDown = bIsDown; } - // Get Shift down state. bool IsShiftDown() const { return bIsShiftDown; } - // Set Shift down state. - // @param bIsDown - True, if Shift is down - void SetShiftDown(bool bIsDown) { bIsShiftDown = bIsDown; } - // Get Alt down state. bool IsAltDown() const { return bIsAltDown; } - // Set Alt down state. - // @param bIsDown - True, if Alt is down - void SetAltDown(bool bIsDown) { bIsAltDown = bIsDown; } - - // Get reference to the array with navigation input states. - const FNavInputArray& GetNavigationInputs() const { return NavigationInputs; } - - // Change state of the navigation input associated with this gamepad key. - // @param KeyEvent - Key event with gamepad key input - // @param bIsDown - True, if key is down - void SetGamepadNavigationKey(const FKeyEvent& KeyEvent, bool bIsDown) { ImGuiInterops::SetGamepadNavigationKey(NavigationInputs, KeyEvent.GetKey(), bIsDown); } + // Get Command down state. + bool IsCommandDown() const { return bIsCommandDown; } // Change state of the navigation input associated with this gamepad axis. // @param AnalogInputEvent - Analogue input event with gamepad axis input // @param Value - Analogue value that should be set for this axis - void SetGamepadNavigationAxis(const FAnalogInputEvent& AnalogInputEvent, float Value) { ImGuiInterops::SetGamepadNavigationAxis(NavigationInputs, AnalogInputEvent.GetKey(), Value); } + void SetGamepadNavigationAxis(const FAnalogInputEvent& AnalogInputEvent, float Value); // Check whether keyboard navigation is enabled. bool IsKeyboardNavigationEnabled() const { return bKeyboardNavigationEnabled; } // Set whether keyboard navigation is enabled. // @param bEnabled - True, if navigation is enabled - void SetKeyboardNavigationEnabled(bool bEnabled) { bKeyboardNavigationEnabled = bEnabled; } + void SetKeyboardNavigationEnabled(bool bEnabled); // Check whether gamepad navigation is enabled. bool IsGamepadNavigationEnabled() const { return bGamepadNavigationEnabled; } // Set whether gamepad navigation is enabled. // @param bEnabled - True, if navigation is enabled - void SetGamepadNavigationEnabled(bool bEnabled) { bGamepadNavigationEnabled = bEnabled; } + void SetGamepadNavigationEnabled(bool bEnabled); // Check whether gamepad is attached. bool HasGamepad() const { return bHasGamepad; } // Set whether gamepad is attached. // @param bInHasGamepad - True, if gamepad is attached - void SetGamepad(bool bInHasGamepad) { bHasGamepad = bInHasGamepad; } + void SetGamepad(bool bInHasGamepad); // Reset the whole input state and mark it as dirty. void Reset() { ResetKeyboard(); ResetMouse(); - ResetGamepadNavigation(); } // Reset the keyboard input state and mark it as dirty. void ResetKeyboard() { - ClearCharacters(); - ClearKeys(); ClearModifierKeys(); } // Reset the mouse input state and mark it as dirty. void ResetMouse() { - ClearMouseButtons(); ClearMouseAnalogue(); } - // Reset the gamepad navigation state. - void ResetGamepadNavigation() - { - ClearNavigationInputs(); - } - // Clear part of the state that is meant to be updated in every frame like: accumulators, buffers, navigation data // and information about dirty parts of keys or mouse buttons arrays. void ClearUpdateState(); - TMap KeyDownEvents; - TMap KeyUpEvents; + ImGuiIO IOFunctions; // Only for functional use! private: - - void SetKeyDown(uint32 KeyIndex, bool bIsDown); - void SetMouseDown(uint32 MouseIndex, bool IsDown); - - void ClearCharacters(); - void ClearKeys(); - void ClearMouseButtons(); void ClearMouseAnalogue(); void ClearModifierKeys(); - void ClearNavigationInputs(); FVector2D MousePosition = FVector2D::ZeroVector; FVector2D TouchPosition = FVector2D::ZeroVector; float MouseWheelDelta = 0.f; - FMouseButtonsArray MouseButtonsDown; - FMouseButtonsIndexRange MouseButtonsUpdateRange; - FCharactersBuffer InputCharacters; - FKeysArray KeysDown; - FKeysIndexRange KeysUpdateRange; - - FNavInputArray NavigationInputs; - bool bHasMousePointer = false; bool bTouchDown = false; bool bTouchProcessed = false; @@ -237,6 +160,7 @@ class FImGuiInputState bool bIsControlDown = false; bool bIsShiftDown = false; bool bIsAltDown = false; + bool bIsCommandDown = false; bool bKeyboardNavigationEnabled = false; bool bGamepadNavigationEnabled = false; diff --git a/Source/ImGui/Private/ImGuiInteroperability.cpp b/Source/ImGui/Private/ImGuiInteroperability.cpp index f2f0b23..4433074 100644 --- a/Source/ImGui/Private/ImGuiInteroperability.cpp +++ b/Source/ImGui/Private/ImGuiInteroperability.cpp @@ -2,82 +2,6 @@ #include "ImGuiInteroperability.h" -#include "ImGuiInputState.h" -#include "Utilities/Arrays.h" - - -// If TCHAR is wider than ImWchar, enable or disable validation of input character before conversions. -#define VALIDATE_INPUT_CHARACTERS 1 - -#if VALIDATE_INPUT_CHARACTERS -DEFINE_LOG_CATEGORY_STATIC(LogImGuiInput, Warning, All); -#endif - -namespace -{ - //==================================================================================================== - // Character conversion - //==================================================================================================== - - template* = nullptr> - ImWchar CastInputChar(T Char) - { - return static_cast(Char); - } - - template* = nullptr> - ImWchar CastInputChar(T Char) - { -#if VALIDATE_INPUT_CHARACTERS - // We only need a runtime validation if TCHAR is wider than ImWchar. - // Signed and unsigned integral types with the same size as ImWchar should be safely converted. As long as the - // char value is in that range we can safely use it, otherwise we should log an error to notify about possible - // truncations. - static constexpr auto MinLimit = (std::numeric_limits>::min)(); - static constexpr auto MaxLimit = (std::numeric_limits>::max)(); - UE_CLOG(!(Char >= MinLimit && Char <= MaxLimit), LogImGuiInput, Error, - TEXT("TCHAR value '%c' (%#x) is out of range %d (%#x) to %u (%#x) that can be safely converted to ImWchar. ") - TEXT("If you wish to disable this validation, please set VALIDATE_INPUT_CHARACTERS in ImGuiInputState.cpp to 0."), - Char, Char, MinLimit, MinLimit, MaxLimit, MaxLimit); -#endif - - return static_cast(Char); - } - - //==================================================================================================== - // Copying Utilities - //==================================================================================================== - - // Copy all elements from source to destination array of the same size. - template - void Copy(const TArray& Src, TArray& Dst) - { - using std::copy; - using std::begin; - using std::end; - copy(begin(Src), end(Src), begin(Dst)); - } - - // Copy subrange of source array to destination array of the same size. - template - void Copy(const TArray& Src, TArray& Dst, const Utilities::TArrayIndexRange& Range) - { - using std::copy; - using std::begin; - copy(begin(Src) + Range.GetBegin(), begin(Src) + Range.GetEnd(), begin(Dst) + Range.GetBegin()); - } - - // Copy number of elements from the beginning of source array to the beginning of destination array of the same size. - template - void Copy(const TArray& Src, TArray& Dst, SizeType Count) - { - checkf(Count < Utilities::ArraySize::value, TEXT("Number of copied elements is larger than array size.")); - - using std::copy; - using std::begin; - copy(begin(Src), begin(Src) + Count, begin(Dst)); - } -} namespace ImGuiInterops { @@ -85,17 +9,34 @@ namespace ImGuiInterops // Input Mapping //==================================================================================================== + static TMap UnrealToImGuiMouseMap; static TMap UnrealToImGuiKeyMap; + static TMap UnrealToImGuiModMap; - void SetUnrealKeyMap(ImGuiIO& IO) + void SetUnrealKeyMap() { + UnrealToImGuiMouseMap.Add(EKeys::LeftMouseButton, 0); + UnrealToImGuiMouseMap.Add(EKeys::RightMouseButton, 1); + UnrealToImGuiMouseMap.Add(EKeys::MiddleMouseButton, 2); + UnrealToImGuiMouseMap.Add(EKeys::ThumbMouseButton, 3); + UnrealToImGuiMouseMap.Add(EKeys::ThumbMouseButton2, 4); + + UnrealToImGuiKeyMap.Add(EKeys::LeftControl, ImGuiKey_LeftCtrl); + UnrealToImGuiKeyMap.Add(EKeys::RightControl, ImGuiKey_RightCtrl); + UnrealToImGuiKeyMap.Add(EKeys::LeftShift, ImGuiKey_LeftShift); + UnrealToImGuiKeyMap.Add(EKeys::RightShift, ImGuiKey_RightShift); + UnrealToImGuiKeyMap.Add(EKeys::LeftAlt, ImGuiKey_LeftAlt); + UnrealToImGuiKeyMap.Add(EKeys::RightAlt, ImGuiKey_RightAlt); + UnrealToImGuiKeyMap.Add(EKeys::LeftCommand, ImGuiKey_LeftSuper); + UnrealToImGuiKeyMap.Add(EKeys::RightCommand, ImGuiKey_RightSuper); + UnrealToImGuiKeyMap.Add(EKeys::Tab, ImGuiKey_Tab); - + UnrealToImGuiKeyMap.Add(EKeys::Left, ImGuiKey_LeftArrow); UnrealToImGuiKeyMap.Add(EKeys::Right, ImGuiKey_RightArrow); UnrealToImGuiKeyMap.Add(EKeys::Up, ImGuiKey_UpArrow); UnrealToImGuiKeyMap.Add(EKeys::Down, ImGuiKey_DownArrow); - + UnrealToImGuiKeyMap.Add(EKeys::PageUp, ImGuiKey_PageUp); UnrealToImGuiKeyMap.Add(EKeys::PageDown, ImGuiKey_PageDown); UnrealToImGuiKeyMap.Add(EKeys::Home, ImGuiKey_Home); @@ -106,12 +47,12 @@ namespace ImGuiInterops UnrealToImGuiKeyMap.Add(EKeys::NumLock, ImGuiKey_NumLock); UnrealToImGuiKeyMap.Add(EKeys::ScrollLock, ImGuiKey_ScrollLock); UnrealToImGuiKeyMap.Add(EKeys::Pause, ImGuiKey_Pause); - + UnrealToImGuiKeyMap.Add(EKeys::BackSpace, ImGuiKey_Backspace); UnrealToImGuiKeyMap.Add(EKeys::SpaceBar, ImGuiKey_Space); UnrealToImGuiKeyMap.Add(EKeys::Enter, ImGuiKey_Enter); UnrealToImGuiKeyMap.Add(EKeys::Escape, ImGuiKey_Escape); - + UnrealToImGuiKeyMap.Add(EKeys::A, ImGuiKey_A); UnrealToImGuiKeyMap.Add(EKeys::B, ImGuiKey_B); UnrealToImGuiKeyMap.Add(EKeys::C, ImGuiKey_C); @@ -138,7 +79,7 @@ namespace ImGuiInterops UnrealToImGuiKeyMap.Add(EKeys::X, ImGuiKey_X); UnrealToImGuiKeyMap.Add(EKeys::Y, ImGuiKey_Y); UnrealToImGuiKeyMap.Add(EKeys::Z, ImGuiKey_Z); - + UnrealToImGuiKeyMap.Add(EKeys::F1, ImGuiKey_F1); UnrealToImGuiKeyMap.Add(EKeys::F2, ImGuiKey_F2); UnrealToImGuiKeyMap.Add(EKeys::F3, ImGuiKey_F3); @@ -162,7 +103,7 @@ namespace ImGuiInterops UnrealToImGuiKeyMap.Add(EKeys::Seven, ImGuiKey_7); UnrealToImGuiKeyMap.Add(EKeys::Eight, ImGuiKey_8); UnrealToImGuiKeyMap.Add(EKeys::Nine, ImGuiKey_9); - + UnrealToImGuiKeyMap.Add(EKeys::Equals, ImGuiKey_Equal); UnrealToImGuiKeyMap.Add(EKeys::Comma, ImGuiKey_Comma); UnrealToImGuiKeyMap.Add(EKeys::Period, ImGuiKey_Period); @@ -188,62 +129,64 @@ namespace ImGuiInterops UnrealToImGuiKeyMap.Add(EKeys::Subtract, ImGuiKey_KeypadSubtract); UnrealToImGuiKeyMap.Add(EKeys::Decimal, ImGuiKey_KeypadDecimal); UnrealToImGuiKeyMap.Add(EKeys::Divide, ImGuiKey_KeypadDivide); - } - // Simple transform mapping key codes to 0-511 range used in ImGui. - // From what I can tell, on most supported platforms key codes should comfortably fit in that range anyway - // but the SDL key-codes used on Linux can go way out of this range (because of the extra flag). However, - // after this transform they should fit in the range without conflicts. - // NOTE: Should any of the platforms have other conflicts or any trouble with inputs, this is the likely - // candidate for change. - static uint32 MapKeyCode(uint32 KeyCode) - { - return (KeyCode < 512) ? KeyCode : 256 + (KeyCode % 256); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_Special_Right, ImGuiKey_GamepadStart); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_Special_Left, ImGuiKey_GamepadBack); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_FaceButton_Bottom, ImGuiKey_GamepadFaceDown); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_FaceButton_Right, ImGuiKey_GamepadFaceRight); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_FaceButton_Top, ImGuiKey_GamepadFaceUp); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_FaceButton_Left, ImGuiKey_GamepadFaceLeft); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_DPad_Left, ImGuiKey_GamepadDpadLeft); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_DPad_Right, ImGuiKey_GamepadDpadRight); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_DPad_Up, ImGuiKey_GamepadDpadUp); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_DPad_Down, ImGuiKey_GamepadDpadDown); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_LeftShoulder, ImGuiKey_GamepadL1); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_RightShoulder, ImGuiKey_GamepadR1); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_LeftTrigger, ImGuiKey_GamepadL2); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_RightTrigger, ImGuiKey_GamepadR2); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_LeftThumbstick, ImGuiKey_GamepadL3); + UnrealToImGuiKeyMap.Add(EKeys::Gamepad_RightThumbstick, ImGuiKey_GamepadR3); + + UnrealToImGuiMouseMap.Add(EKeys::LeftMouseButton, 0); + UnrealToImGuiMouseMap.Add(EKeys::RightMouseButton, 1); + UnrealToImGuiMouseMap.Add(EKeys::MiddleMouseButton, 2); + UnrealToImGuiMouseMap.Add(EKeys::ThumbMouseButton, 3); + UnrealToImGuiMouseMap.Add(EKeys::ThumbMouseButton2, 4); + + UnrealToImGuiModMap.Add(EKeys::LeftControl, ImGuiMod_Ctrl); + UnrealToImGuiModMap.Add(EKeys::RightControl, ImGuiMod_Ctrl); + UnrealToImGuiModMap.Add(EKeys::LeftShift, ImGuiMod_Shift); + UnrealToImGuiModMap.Add(EKeys::RightShift, ImGuiMod_Shift); + UnrealToImGuiModMap.Add(EKeys::LeftAlt, ImGuiMod_Alt); + UnrealToImGuiModMap.Add(EKeys::RightAlt, ImGuiMod_Alt); + UnrealToImGuiModMap.Add(EKeys::LeftCommand, ImGuiMod_Super); + UnrealToImGuiModMap.Add(EKeys::RightCommand, ImGuiMod_Super); } - uint32 GetKeyIndex(const FKey& Key) + ImGuiKey UnrealToImGuiKey(const FKey& Key) { - const uint32* pKeyCode = nullptr; - const uint32* pCharCode = nullptr; - - FInputKeyManager::Get().GetCodesFromKey(Key, pKeyCode, pCharCode); - - const uint32 KeyCode = - pKeyCode ? *pKeyCode - : pCharCode ? *pCharCode - : 0; - - return MapKeyCode(KeyCode); + if (UnrealToImGuiKeyMap.Contains(Key)) + { + return UnrealToImGuiKeyMap[Key]; + } + return ImGuiKey_None; } - uint32 GetKeyIndex(const FKeyEvent& KeyEvent) + ImGuiKey UnrealToImGuiMod(const FKey& Key) { - return MapKeyCode(KeyEvent.GetKeyCode()); + if (UnrealToImGuiModMap.Contains(Key)) + { + return UnrealToImGuiModMap[Key]; + } + return ImGuiKey_None; } - uint32 GetMouseIndex(const FKey& MouseButton) + int GetMouseIndex(const FKey& MouseButton) { - if (MouseButton == EKeys::LeftMouseButton) - { - return 0; - } - else if (MouseButton == EKeys::MiddleMouseButton) - { - return 2; - } - else if (MouseButton == EKeys::RightMouseButton) + if (UnrealToImGuiMouseMap.Contains(MouseButton)) { - return 1; + return UnrealToImGuiMouseMap[MouseButton]; } - else if (MouseButton == EKeys::ThumbMouseButton) - { - return 3; - } - else if (MouseButton == EKeys::ThumbMouseButton2) - { - return 4; - } - return -1; } @@ -251,184 +194,67 @@ namespace ImGuiInterops { switch (MouseCursor) { - case ImGuiMouseCursor_Arrow: - return EMouseCursor::Default; - case ImGuiMouseCursor_TextInput: - return EMouseCursor::TextEditBeam; - case ImGuiMouseCursor_ResizeAll: - return EMouseCursor::CardinalCross; - case ImGuiMouseCursor_ResizeNS: - return EMouseCursor::ResizeUpDown; - case ImGuiMouseCursor_ResizeEW: - return EMouseCursor::ResizeLeftRight; - case ImGuiMouseCursor_ResizeNESW: - return EMouseCursor::ResizeSouthWest; - case ImGuiMouseCursor_ResizeNWSE: - return EMouseCursor::ResizeSouthEast; - case ImGuiMouseCursor_None: - default: - return EMouseCursor::None; + case ImGuiMouseCursor_Arrow: + return EMouseCursor::Default; + case ImGuiMouseCursor_TextInput: + return EMouseCursor::TextEditBeam; + case ImGuiMouseCursor_ResizeAll: + return EMouseCursor::CardinalCross; + case ImGuiMouseCursor_ResizeNS: + return EMouseCursor::ResizeUpDown; + case ImGuiMouseCursor_ResizeEW: + return EMouseCursor::ResizeLeftRight; + case ImGuiMouseCursor_ResizeNESW: + return EMouseCursor::ResizeSouthWest; + case ImGuiMouseCursor_ResizeNWSE: + return EMouseCursor::ResizeSouthEast; + case ImGuiMouseCursor_None: + default: + return EMouseCursor::None; } } namespace { - inline void UpdateKey(const FKey& Key, const FKey& KeyCondition, float& Value, bool bIsDown) - { - if (Key == KeyCondition) - { - Value = (bIsDown) ? 1.f : 0.f; - } - } - - inline void UpdateAxisValues(float& Axis, float& Opposite, float Value) + inline void UpdateAxisValues(ImGuiIO& IOFunctions, ImGuiKey Axis, ImGuiKey Opposite, float Value) { constexpr float AxisInputThreshold = 0.166f; // Filter out small values to avoid false positives (helpful in case of worn controllers). - Axis = FMath::Max(0.f, Value - AxisInputThreshold); - Opposite = 0.f; + const float AxisValue = FMath::Max(0.f, Value - AxisInputThreshold); + + IOFunctions.AddKeyAnalogEvent(Axis, AxisValue > 0.10f, AxisValue); + IOFunctions.AddKeyAnalogEvent(Opposite, false, 0.f); } - inline void UpdateSymmetricAxis(const FKey& Key, const FKey& KeyCondition, float& Negative, float& Positive, float Value) + inline void UpdateSymmetricAxis(ImGuiIO& IOFunctions, const FKey& Key, const FKey& KeyCondition, ImGuiKey Negative, ImGuiKey Positive, float Value) { if (Key == KeyCondition) { if (Value < 0.f) { - UpdateAxisValues(Negative, Positive, -Value); + UpdateAxisValues(IOFunctions, Negative, Positive, -Value); } else { - UpdateAxisValues(Positive, Negative, Value); + UpdateAxisValues(IOFunctions, Positive, Negative, Value); } } } } - void SetGamepadNavigationKey(ImGuiTypes::FNavInputArray& NavInputs, const FKey& Key, bool bIsDown) - { -#define MAP_KEY(KeyCondition, NavIndex) UpdateKey(Key, KeyCondition, NavInputs[NavIndex], bIsDown) - - if (Key.IsGamepadKey()) - { - MAP_KEY(EKeys::Gamepad_FaceButton_Bottom, ImGuiNavInput_Activate); - MAP_KEY(EKeys::Gamepad_FaceButton_Right, ImGuiNavInput_Cancel); - MAP_KEY(EKeys::Gamepad_FaceButton_Top, ImGuiNavInput_Input); - MAP_KEY(EKeys::Gamepad_FaceButton_Left, ImGuiNavInput_Menu); - MAP_KEY(EKeys::Gamepad_DPad_Left, ImGuiNavInput_DpadLeft); - MAP_KEY(EKeys::Gamepad_DPad_Right, ImGuiNavInput_DpadRight); - MAP_KEY(EKeys::Gamepad_DPad_Up, ImGuiNavInput_DpadUp); - MAP_KEY(EKeys::Gamepad_DPad_Down, ImGuiNavInput_DpadDown); - MAP_KEY(EKeys::Gamepad_LeftShoulder, ImGuiNavInput_FocusPrev); - MAP_KEY(EKeys::Gamepad_RightShoulder, ImGuiNavInput_FocusNext); - MAP_KEY(EKeys::Gamepad_LeftShoulder, ImGuiNavInput_TweakSlow); - MAP_KEY(EKeys::Gamepad_RightShoulder, ImGuiNavInput_TweakFast); - } - -#undef MAP_KEY - } - - void SetGamepadNavigationAxis(ImGuiTypes::FNavInputArray& NavInputs, const FKey& Key, float Value) + void SetGamepadNavigationAxis(ImGuiIO& IOFunctions, const FKey& Key, float Value) { -#define MAP_SYMMETRIC_AXIS(KeyCondition, NegNavIndex, PosNavIndex) UpdateSymmetricAxis(Key, KeyCondition, NavInputs[NegNavIndex], NavInputs[PosNavIndex], Value) +#define MAP_SYMMETRIC_AXIS(KeyCondition, NegNavIndex, PosNavIndex) UpdateSymmetricAxis(IOFunctions, Key, KeyCondition, NegNavIndex, PosNavIndex, Value) if (Key.IsGamepadKey()) { - MAP_SYMMETRIC_AXIS(EKeys::Gamepad_LeftX, ImGuiNavInput_LStickLeft, ImGuiNavInput_LStickRight); - MAP_SYMMETRIC_AXIS(EKeys::Gamepad_LeftY, ImGuiNavInput_LStickDown, ImGuiNavInput_LStickUp); + MAP_SYMMETRIC_AXIS(EKeys::Gamepad_LeftX, ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight); + MAP_SYMMETRIC_AXIS(EKeys::Gamepad_LeftY, ImGuiKey_GamepadLStickDown, ImGuiKey_GamepadLStickUp); + MAP_SYMMETRIC_AXIS(EKeys::Gamepad_RightX, ImGuiKey_GamepadRStickLeft, ImGuiKey_GamepadRStickRight); + MAP_SYMMETRIC_AXIS(EKeys::Gamepad_RightY, ImGuiKey_GamepadRStickDown, ImGuiKey_GamepadRStickUp); } #undef MAP_SYMMETRIC_AXIS } - - //==================================================================================================== - // Input State Copying - //==================================================================================================== - - template - static inline constexpr void SetFlag(TFlags& Flags, TFlag Flag, bool bSet) - { - Flags = bSet ? Flags | Flag : Flags & ~Flag; - } - - void CopyInput(ImGuiIO& IO, const FImGuiInputState& InputState) - { - static const uint32 LeftControl = GetKeyIndex(EKeys::LeftControl); - static const uint32 RightControl = GetKeyIndex(EKeys::RightControl); - static const uint32 LeftShift = GetKeyIndex(EKeys::LeftShift); - static const uint32 RightShift = GetKeyIndex(EKeys::RightShift); - static const uint32 LeftAlt = GetKeyIndex(EKeys::LeftAlt); - static const uint32 RightAlt = GetKeyIndex(EKeys::RightAlt); - - // Update modifier key events - IO.AddKeyEvent(ImGuiKey_ModCtrl, InputState.IsControlDown()); - IO.AddKeyEvent(ImGuiKey_ModShift, InputState.IsShiftDown()); - IO.AddKeyEvent(ImGuiKey_ModAlt, InputState.IsAltDown()); - IO.AddKeyEvent(ImGuiKey_ModSuper, false); - - // Copy buffers. - if (!InputState.GetKeysUpdateRange().IsEmpty()) - { - // Key down events - for(const auto& Pair : InputState.KeyDownEvents) - { - if(UnrealToImGuiKeyMap.Contains(Pair.Value.GetKey())) - { - IO.AddKeyEvent(UnrealToImGuiKeyMap[Pair.Value.GetKey()], true); - } - } - - // Key up events - for(const auto& Pair : InputState.KeyUpEvents) - { - if(UnrealToImGuiKeyMap.Contains(Pair.Value.GetKey())) - { - IO.AddKeyEvent(UnrealToImGuiKeyMap[Pair.Value.GetKey()], false); - } - } - } - - if (!InputState.GetMouseButtonsUpdateRange().IsEmpty()) - { - Copy(InputState.GetMouseButtons(), IO.MouseDown, InputState.GetMouseButtonsUpdateRange()); - } - - for (const TCHAR Char : InputState.GetCharacters()) - { - IO.AddInputCharacter(CastInputChar(Char)); - } - - if (InputState.IsGamepadNavigationEnabled() && InputState.HasGamepad()) - { - Copy(InputState.GetNavigationInputs(), IO.NavInputs); - } - - SetFlag(IO.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard, InputState.IsKeyboardNavigationEnabled()); - SetFlag(IO.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad, InputState.IsGamepadNavigationEnabled()); - SetFlag(IO.BackendFlags, ImGuiBackendFlags_HasGamepad, InputState.HasGamepad()); - - // Check whether we need to draw cursor. - IO.MouseDrawCursor = InputState.HasMousePointer(); - - // If touch is enabled and active, give it a precedence. - if (InputState.IsTouchActive()) - { - // Copy the touch position to mouse position. - IO.MousePos.x = InputState.GetTouchPosition().X; - IO.MousePos.y = InputState.GetTouchPosition().Y; - - // With touch active one frame longer than it is down, we have one frame to processed touch up. - IO.MouseDown[0] = InputState.IsTouchDown(); - } - else - { - // Copy the mouse position. - IO.MousePos.x = InputState.GetMousePosition().X; - IO.MousePos.y = InputState.GetMousePosition().Y; - - // Copy mouse wheel delta. - IO.MouseWheel += InputState.GetMouseWheelDelta(); - } - } } diff --git a/Source/ImGui/Private/ImGuiInteroperability.h b/Source/ImGui/Private/ImGuiInteroperability.h index 52ce6e9..e07ad33 100644 --- a/Source/ImGui/Private/ImGuiInteroperability.h +++ b/Source/ImGui/Private/ImGuiInteroperability.h @@ -9,44 +9,30 @@ #include +// If TCHAR is wider than ImWchar, enable or disable validation of input character before conversions. +#define VALIDATE_INPUT_CHARACTERS 1 -class FImGuiInputState; +#if VALIDATE_INPUT_CHARACTERS +DEFINE_LOG_CATEGORY_STATIC(LogImGuiInput, Warning, All); +#endif // Utilities to help standardise operations between Unreal and ImGui. namespace ImGuiInterops { - //==================================================================================================== - // ImGui Types - //==================================================================================================== - - namespace ImGuiTypes - { - using FMouseButtonsArray = decltype(ImGuiIO::MouseDown); - using FKeysArray = decltype(ImGuiIO::KeysDown); - using FNavInputArray = decltype(ImGuiIO::NavInputs); - - using FKeyMap = decltype(ImGuiIO::KeyMap); - } - - //==================================================================================================== // Input Mapping //==================================================================================================== - // Set in ImGui IO mapping to recognize indices generated from Unreal input events. - void SetUnrealKeyMap(ImGuiIO& IO); - - // Map FKey to index in keys buffer. - uint32 GetKeyIndex(const FKey& Key); - - // Map key event to index in keys buffer. - uint32 GetKeyIndex(const FKeyEvent& KeyEvent); + // Map Unreal FKey to ImGuiKey + void SetUnrealKeyMap(); + ImGuiKey UnrealToImGuiKey(const FKey& Key); + ImGuiKey UnrealToImGuiMod(const FKey& Key); // Map mouse FKey to index in mouse buttons buffer. - uint32 GetMouseIndex(const FKey& MouseButton); + int GetMouseIndex(const FKey& MouseButton); // Map pointer event to index in mouse buttons buffer. - FORCEINLINE uint32 GetMouseIndex(const FPointerEvent& MouseEvent) + FORCEINLINE int GetMouseIndex(const FPointerEvent& MouseEvent) { return GetMouseIndex(MouseEvent.GetEffectingButton()); } @@ -54,27 +40,49 @@ namespace ImGuiInterops // Convert from ImGuiMouseCursor type to EMouseCursor. EMouseCursor::Type ToSlateMouseCursor(ImGuiMouseCursor MouseCursor); - // Set in the target array navigation input corresponding to gamepad key. - // @param NavInputs - Target array - // @param Key - Gamepad key mapped to navigation input (non-mapped keys will be ignored) - // @param bIsDown - True, if key is down - void SetGamepadNavigationKey(ImGuiTypes::FNavInputArray& NavInputs, const FKey& Key, bool bIsDown); - // Set in the target array navigation input corresponding to gamepad axis. // @param NavInputs - Target array // @param Key - Gamepad axis key mapped to navigation input (non-axis or non-mapped inputs will be ignored) // @param Value - Axis value (-1..1 values from Unreal are mapped to separate ImGui axes with values in range 0..1) - void SetGamepadNavigationAxis(ImGuiTypes::FNavInputArray& NavInputs, const FKey& Key, float Value); + void SetGamepadNavigationAxis(ImGuiIO& IO, const FKey& Key, float Value); //==================================================================================================== - // Input State Copying + // Character conversion //==================================================================================================== - // Copy input to ImGui IO. - // @param IO - Target ImGui IO - // @param InputState - Input state to copy - void CopyInput(ImGuiIO& IO, const FImGuiInputState& InputState); + template* = nullptr> + ImWchar CastInputChar(T Char) + { + return static_cast(Char); + } + + template* = nullptr> + ImWchar CastInputChar(T Char) + { +#if VALIDATE_INPUT_CHARACTERS + // We only need a runtime validation if TCHAR is wider than ImWchar. + // Signed and unsigned integral types with the same size as ImWchar should be safely converted. As long as the + // char value is in that range we can safely use it, otherwise we should log an error to notify about possible + // truncations. + static constexpr auto MinLimit = (std::numeric_limits>::min)(); + static constexpr auto MaxLimit = (std::numeric_limits>::max)(); + UE_CLOG(!(Char >= MinLimit && Char <= MaxLimit), LogImGuiInput, Error, + TEXT("TCHAR value '%c' (%#x) is out of range %d (%#x) to %u (%#x) that can be safely converted to ImWchar. ") + TEXT("If you wish to disable this validation, please set VALIDATE_INPUT_CHARACTERS in ImGuiInputState.cpp to 0."), + Char, Char, MinLimit, MinLimit, MaxLimit, MaxLimit); +#endif + return static_cast(Char); + } + + //==================================================================================================== + // Input Flags + //==================================================================================================== + + template + static constexpr void SetFlag(TFlags& Flags, TFlag Flag, bool bSet) { + Flags = bSet ? Flags | Flag : Flags & ~Flag; + } //==================================================================================================== diff --git a/Source/ImGui/Public/ImGuiInputHandler.h b/Source/ImGui/Public/ImGuiInputHandler.h index 093ea63..fa65d94 100644 --- a/Source/ImGui/Public/ImGuiInputHandler.h +++ b/Source/ImGui/Public/ImGuiInputHandler.h @@ -134,15 +134,12 @@ class IMGUI_API UImGuiInputHandler : public UObject protected: - /** Copy state of modifier keys to input state. */ - void CopyModifierKeys(const FInputEvent& InputEvent); - /** * Checks whether this is a key event that can open console. * @param KeyEvent - Key event to test. * @returns True, if this key event can open console. */ - bool IsConsoleEvent(const FKeyEvent& KeyEvent) const; + static bool IsConsoleEvent(const FKeyEvent& KeyEvent); #if WITH_EDITOR /** diff --git a/Source/ThirdParty/ImGuiLibrary/Include/imconfig.h b/Source/ThirdParty/ImGuiLibrary/Include/imconfig.h index 0e676cc..5b24f82 100644 --- a/Source/ThirdParty/ImGuiLibrary/Include/imconfig.h +++ b/Source/ThirdParty/ImGuiLibrary/Include/imconfig.h @@ -28,7 +28,7 @@ //#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names. -//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS +#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS //#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS. //---- Disable all of Dear ImGui or don't implement standard windows/tools.