UnrealImGui/Source/ImGui/Private/ImGuiInteroperability.cpp
Sebastian 6c6e766a20 Fixed mapping from FKey to ImGui key index to gracefully handle unsupported keys and work on platforms that does not support all the keys defined in ImGui key map.
Instead of throwing assertion unsupported keys are mapped to 0, what guarantees that ImGui key map is always initialized and unsupported keys mapped to index that cannot be reached from key input events that have non-zero key codes.
2019-03-11 19:39:11 +00:00

292 lines
8.9 KiB
C++

// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
#include "ImGuiPrivatePCH.h"
#include "ImGuiInteroperability.h"
#include "ImGuiInputState.h"
#include "Utilities/Arrays.h"
namespace
{
//====================================================================================================
// Copying Utilities
//====================================================================================================
// Copy all elements from source to destination array of the same size.
template<typename TArray>
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<typename TArray, typename SizeType>
void Copy(const TArray& Src, TArray& Dst, const Utilities::TArrayIndexRange<TArray, SizeType>& 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<typename TArray, typename SizeType>
void Copy(const TArray& Src, TArray& Dst, SizeType Count)
{
checkf(Count < Utilities::ArraySize<TArray>::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
{
//====================================================================================================
// Input Mapping
//====================================================================================================
void SetUnrealKeyMap(ImGuiIO& IO)
{
struct FUnrealToImGuiMapping
{
FUnrealToImGuiMapping()
{
KeyMap[ImGuiKey_Tab] = GetKeyIndex(EKeys::Tab);
KeyMap[ImGuiKey_LeftArrow] = GetKeyIndex(EKeys::Left);
KeyMap[ImGuiKey_RightArrow] = GetKeyIndex(EKeys::Right);
KeyMap[ImGuiKey_UpArrow] = GetKeyIndex(EKeys::Up);
KeyMap[ImGuiKey_DownArrow] = GetKeyIndex(EKeys::Down);
KeyMap[ImGuiKey_PageUp] = GetKeyIndex(EKeys::PageUp);
KeyMap[ImGuiKey_PageDown] = GetKeyIndex(EKeys::PageDown);
KeyMap[ImGuiKey_Home] = GetKeyIndex(EKeys::Home);
KeyMap[ImGuiKey_End] = GetKeyIndex(EKeys::End);
KeyMap[ImGuiKey_Insert] = GetKeyIndex(EKeys::Insert);
KeyMap[ImGuiKey_Delete] = GetKeyIndex(EKeys::Delete);
KeyMap[ImGuiKey_Backspace] = GetKeyIndex(EKeys::BackSpace);
KeyMap[ImGuiKey_Space] = GetKeyIndex(EKeys::SpaceBar);
KeyMap[ImGuiKey_Enter] = GetKeyIndex(EKeys::Enter);
KeyMap[ImGuiKey_Escape] = GetKeyIndex(EKeys::Escape);
KeyMap[ImGuiKey_A] = GetKeyIndex(EKeys::A);
KeyMap[ImGuiKey_C] = GetKeyIndex(EKeys::C);
KeyMap[ImGuiKey_V] = GetKeyIndex(EKeys::V);
KeyMap[ImGuiKey_X] = GetKeyIndex(EKeys::X);
KeyMap[ImGuiKey_Y] = GetKeyIndex(EKeys::Y);
KeyMap[ImGuiKey_Z] = GetKeyIndex(EKeys::Z);
}
ImGuiTypes::FKeyMap KeyMap;
};
static const FUnrealToImGuiMapping Mapping;
Copy(Mapping.KeyMap, IO.KeyMap);
}
uint32 GetKeyIndex(const FKey& Key)
{
const uint32* pKeyCode = nullptr;
const uint32* pCharCode = nullptr;
FInputKeyManager::Get().GetCodesFromKey(Key, pKeyCode, pCharCode);
if (pKeyCode)
{
return *pKeyCode;
}
if (pCharCode)
{
return *pCharCode;
}
return 0;
}
uint32 GetMouseIndex(const FKey& MouseButton)
{
if (MouseButton == EKeys::LeftMouseButton)
{
return 0;
}
else if (MouseButton == EKeys::MiddleMouseButton)
{
return 2;
}
else if (MouseButton == EKeys::RightMouseButton)
{
return 1;
}
else if (MouseButton == EKeys::ThumbMouseButton)
{
return 3;
}
else if (MouseButton == EKeys::ThumbMouseButton2)
{
return 4;
}
return -1;
}
EMouseCursor::Type ToSlateMouseCursor(ImGuiMouseCursor MouseCursor)
{
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;
}
}
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)
{
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;
}
inline void UpdateSymmetricAxis(const FKey& Key, const FKey& KeyCondition, float& Negative, float& Positive, float Value)
{
if (Key == KeyCondition)
{
if (Value < 0.f)
{
UpdateAxisValues(Negative, Positive, -Value);
}
else
{
UpdateAxisValues(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)
{
#define MAP_SYMMETRIC_AXIS(KeyCondition, NegNavIndex, PosNavIndex) UpdateSymmetricAxis(Key, KeyCondition, NavInputs[NegNavIndex], NavInputs[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);
}
#undef MAP_SYMMETRIC_AXIS
}
//====================================================================================================
// Input State Copying
//====================================================================================================
template<typename TFlags, typename TFlag>
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);
// Check whether we need to draw cursor.
IO.MouseDrawCursor = InputState.HasMousePointer();
// Copy mouse position.
IO.MousePos.x = InputState.GetMousePosition().X;
IO.MousePos.y = InputState.GetMousePosition().Y;
// Copy mouse wheel delta.
IO.MouseWheel += InputState.GetMouseWheelDelta();
// Copy key modifiers.
IO.KeyCtrl = InputState.IsControlDown();
IO.KeyShift = InputState.IsShiftDown();
IO.KeyAlt = InputState.IsAltDown();
IO.KeySuper = false;
// Copy buffers.
if (!InputState.GetKeysUpdateRange().IsEmpty())
{
Copy(InputState.GetKeys(), IO.KeysDown, InputState.GetKeysUpdateRange());
}
if (!InputState.GetMouseButtonsUpdateRange().IsEmpty())
{
Copy(InputState.GetMouseButtons(), IO.MouseDown, InputState.GetMouseButtonsUpdateRange());
}
if (InputState.GetCharactersNum() > 0)
{
Copy(InputState.GetCharacters(), IO.InputCharacters);
}
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());
}
}