mirror of
				https://github.com/kevinporetti/UnrealImGui.git
				synced 2025-11-03 23:33:16 +00:00 
			
		
		
		
	Enabled experimental ImGui gamepad navigation feature:
- Updated InputNavigation CVar to support 1: keyboard navigation, 2: gamepad navigation and 3: keyboard + gamepad navigation simultaneously. - Added ImGui interoperability utilities mapping gamepad input events to values in ImGui navigation inputs array. - Added to ImGui Input State support for gamepad navigation inputs. - Refactored ImGui Input State to keep consistent interface for keyboard, mouse and gamepad updates.
This commit is contained in:
		
							parent
							
								
									e83f37d518
								
							
						
					
					
						commit
						82729a12c8
					
				@ -82,7 +82,7 @@ void FImGuiInputState::SetMouseDown(uint32 MouseIndex, bool bIsDown)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void FImGuiInputState::Reset(bool bKeyboard, bool bMouse)
 | 
					void FImGuiInputState::Reset(bool bKeyboard, bool bMouse, bool bNavigation)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (bKeyboard)
 | 
						if (bKeyboard)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@ -100,6 +100,11 @@ void FImGuiInputState::Reset(bool bKeyboard, bool bMouse)
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		ClearModifierKeys();
 | 
							ClearModifierKeys();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bNavigation)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ClearNavigationInputs();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void FImGuiInputState::ClearUpdateState()
 | 
					void FImGuiInputState::ClearUpdateState()
 | 
				
			||||||
@ -152,3 +157,10 @@ void FImGuiInputState::ClearModifierKeys()
 | 
				
			|||||||
	bIsShiftDown = false;
 | 
						bIsShiftDown = false;
 | 
				
			||||||
	bIsAltDown = false;
 | 
						bIsAltDown = false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FImGuiInputState::ClearNavigationInputs()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						using std::fill;
 | 
				
			||||||
 | 
						fill(NavigationInputs, &NavigationInputs[Utilities::GetArraySize(NavigationInputs)], 0.f);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,9 @@ public:
 | 
				
			|||||||
	// Array for key states.
 | 
						// Array for key states.
 | 
				
			||||||
	using FKeysArray = ImGuiInterops::ImGuiTypes::FKeysArray;
 | 
						using FKeysArray = ImGuiInterops::ImGuiTypes::FKeysArray;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Array for navigation input states.
 | 
				
			||||||
 | 
						using FNavInputArray = ImGuiInterops::ImGuiTypes::FNavInputArray;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Pair of indices defining range in mouse buttons array.
 | 
						// Pair of indices defining range in mouse buttons array.
 | 
				
			||||||
	using FMouseButtonsIndexRange = Utilities::TArrayIndexRange<FMouseButtonsArray, uint32>;
 | 
						using FMouseButtonsIndexRange = Utilities::TArrayIndexRange<FMouseButtonsArray, uint32>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,9 +50,14 @@ public:
 | 
				
			|||||||
	const FKeysIndexRange& GetKeysUpdateRange() const { return KeysUpdateRange; }
 | 
						const FKeysIndexRange& GetKeysUpdateRange() const { return KeysUpdateRange; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Change state of the key in the keys array and expand range bounding dirty part of the array.
 | 
						// Change state of the key in the keys array and expand range bounding dirty part of the array.
 | 
				
			||||||
	// @param KeyIndex - Index of the key
 | 
						// @param KeyEvent - Key event representing the key
 | 
				
			||||||
	// @param bIsDown - True, if key is down
 | 
						// @param bIsDown - True, if key is down
 | 
				
			||||||
	void SetKeyDown(uint32 KeyIndex, bool bIsDown);
 | 
						void SetKeyDown(const FKeyEvent& KeyEvent, bool bIsDown) { SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), 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.
 | 
						// Get reference to the array with mouse button down states.
 | 
				
			||||||
	const FMouseButtonsArray& GetMouseButtons() const { return MouseButtonsDown; }
 | 
						const FMouseButtonsArray& GetMouseButtons() const { return MouseButtonsDown; }
 | 
				
			||||||
@ -58,9 +66,14 @@ public:
 | 
				
			|||||||
	const FMouseButtonsIndexRange& GetMouseButtonsUpdateRange() const { return MouseButtonsUpdateRange; }
 | 
						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 button in the mouse buttons array and expand range bounding dirty part of the array.
 | 
				
			||||||
	// @param MouseIndex - Index of the mouse button
 | 
						// @param MouseEvent - Mouse event representing mouse button
 | 
				
			||||||
	// @param bIsDown - True, if button is down
 | 
						// @param bIsDown - True, if button is down
 | 
				
			||||||
	void SetMouseDown(uint32 MouseIndex, bool IsDown);
 | 
						void SetMouseDown(const FPointerEvent& MouseEvent, bool bIsDown) { SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), bIsDown); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Change state of the button in the mouse buttons array and expand range bounding dirty part of the array.
 | 
				
			||||||
 | 
						// @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.
 | 
						// Get mouse wheel delta accumulated during the last frame.
 | 
				
			||||||
	float GetMouseWheelDelta() const { return MouseWheelDelta; }
 | 
						float GetMouseWheelDelta() const { return MouseWheelDelta; }
 | 
				
			||||||
@ -81,7 +94,7 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Set whether input has active mouse pointer.
 | 
						// Set whether input has active mouse pointer.
 | 
				
			||||||
	// @param bHasPointer - True, if input has active mouse pointer
 | 
						// @param bHasPointer - True, if input has active mouse pointer
 | 
				
			||||||
	void SetMousePointer(bool bHasPointer) { bHasMousePointer = bHasPointer; }
 | 
						void SetMousePointer(bool bInHasMousePointer) { bHasMousePointer = bInHasMousePointer; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get Control down state.
 | 
						// Get Control down state.
 | 
				
			||||||
	bool IsControlDown() const { return bIsControlDown; }
 | 
						bool IsControlDown() const { return bIsControlDown; }
 | 
				
			||||||
@ -104,18 +117,18 @@ public:
 | 
				
			|||||||
	// @param bIsDown - True, if Alt is down
 | 
						// @param bIsDown - True, if Alt is down
 | 
				
			||||||
	void SetAltDown(bool bIsDown) { bIsAltDown = bIsDown; }
 | 
						void SetAltDown(bool bIsDown) { bIsAltDown = bIsDown; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Reset state and mark as dirty.
 | 
						// Get reference to the array with navigation input states.
 | 
				
			||||||
	void ResetState() { Reset(true, true); }
 | 
						const FNavInputArray& GetNavigationInputs() const { return NavigationInputs; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Reset keyboard state and mark as dirty.
 | 
						// Change state of the navigation input associated with this gamepad key.
 | 
				
			||||||
	void ResetKeyboardState() { Reset(true, false); }
 | 
						// @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); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Reset mouse state and mark as dirty.
 | 
						// Change state of the navigation input associated with this gamepad axis.
 | 
				
			||||||
	void ResetMouseState() { Reset(false, true); }
 | 
						// @param AnalogInputEvent - Analogue input event with gamepad axis input
 | 
				
			||||||
 | 
						// @param Value - Analogue value that should be set for this axis
 | 
				
			||||||
	// Clear part of the state that is meant to be updated in every frame like: accumulators, buffers and information
 | 
						void SetGamepadNavigationAxis(const FAnalogInputEvent& AnalogInputEvent, float Value) { ImGuiInterops::SetGamepadNavigationAxis(NavigationInputs, AnalogInputEvent.GetKey(), Value); }
 | 
				
			||||||
	// about dirty parts of keys or mouse buttons arrays.
 | 
					 | 
				
			||||||
	void ClearUpdateState();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check whether keyboard navigation is enabled.
 | 
						// Check whether keyboard navigation is enabled.
 | 
				
			||||||
	bool IsKeyboardNavigationEnabled() const { return bKeyboardNavigationEnabled; }
 | 
						bool IsKeyboardNavigationEnabled() const { return bKeyboardNavigationEnabled; }
 | 
				
			||||||
@ -124,15 +137,49 @@ public:
 | 
				
			|||||||
	// @param bEnabled - True, if navigation is enabled
 | 
						// @param bEnabled - True, if navigation is enabled
 | 
				
			||||||
	void SetKeyboardNavigationEnabled(bool bEnabled) { bKeyboardNavigationEnabled = bEnabled; }
 | 
						void SetKeyboardNavigationEnabled(bool bEnabled) { bKeyboardNavigationEnabled = 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; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Reset the whole state and mark as dirty.
 | 
				
			||||||
 | 
						void ResetState() { Reset(true, true, true); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Reset keyboard state and mark as dirty.
 | 
				
			||||||
 | 
						void ResetKeyboardState() { Reset(true, false, false); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Reset mouse state and mark as dirty.
 | 
				
			||||||
 | 
						void ResetMouseState() { Reset(false, true, false); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Reset navigation state.
 | 
				
			||||||
 | 
						void ResetNavigationState() { Reset(false, false, true); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void Reset(bool bKeyboard, bool bMouse);
 | 
						void SetKeyDown(uint32 KeyIndex, bool bIsDown);
 | 
				
			||||||
 | 
						void SetMouseDown(uint32 MouseIndex, bool IsDown);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void Reset(bool bKeyboard, bool bMouse, bool bNavigation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void ClearCharacters();
 | 
						void ClearCharacters();
 | 
				
			||||||
	void ClearKeys();
 | 
						void ClearKeys();
 | 
				
			||||||
	void ClearMouseButtons();
 | 
						void ClearMouseButtons();
 | 
				
			||||||
	void ClearMouseAnalogue();
 | 
						void ClearMouseAnalogue();
 | 
				
			||||||
	void ClearModifierKeys();
 | 
						void ClearModifierKeys();
 | 
				
			||||||
 | 
						void ClearNavigationInputs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	FVector2D MousePosition = FVector2D::ZeroVector;
 | 
						FVector2D MousePosition = FVector2D::ZeroVector;
 | 
				
			||||||
	float MouseWheelDelta = 0.f;
 | 
						float MouseWheelDelta = 0.f;
 | 
				
			||||||
@ -146,6 +193,8 @@ private:
 | 
				
			|||||||
	FKeysArray KeysDown;
 | 
						FKeysArray KeysDown;
 | 
				
			||||||
	FKeysIndexRange KeysUpdateRange;
 | 
						FKeysIndexRange KeysUpdateRange;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						FNavInputArray NavigationInputs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool bHasMousePointer = false;
 | 
						bool bHasMousePointer = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool bIsControlDown = false;
 | 
						bool bIsControlDown = false;
 | 
				
			||||||
@ -153,4 +202,6 @@ private:
 | 
				
			|||||||
	bool bIsAltDown = false;
 | 
						bool bIsAltDown = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool bKeyboardNavigationEnabled = false;
 | 
						bool bKeyboardNavigationEnabled = false;
 | 
				
			||||||
 | 
						bool bGamepadNavigationEnabled = false;
 | 
				
			||||||
 | 
						bool bHasGamepad = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -159,6 +159,77 @@ namespace ImGuiInterops
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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
 | 
						// Input State Copying
 | 
				
			||||||
	//====================================================================================================
 | 
						//====================================================================================================
 | 
				
			||||||
@ -210,6 +281,13 @@ namespace ImGuiInterops
 | 
				
			|||||||
			Copy(InputState.GetCharacters(), IO.InputCharacters);
 | 
								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_NavEnableKeyboard, InputState.IsKeyboardNavigationEnabled());
 | 
				
			||||||
 | 
							SetFlag(IO.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad, InputState.IsGamepadNavigationEnabled());
 | 
				
			||||||
 | 
							SetFlag(IO.BackendFlags, ImGuiBackendFlags_HasGamepad, InputState.HasGamepad());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -22,6 +22,7 @@ namespace ImGuiInterops
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		using FMouseButtonsArray = decltype(ImGuiIO::MouseDown);
 | 
							using FMouseButtonsArray = decltype(ImGuiIO::MouseDown);
 | 
				
			||||||
		using FKeysArray = decltype(ImGuiIO::KeysDown);
 | 
							using FKeysArray = decltype(ImGuiIO::KeysDown);
 | 
				
			||||||
 | 
							using FNavInputArray = decltype(ImGuiIO::NavInputs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		using FInputCharactersBuffer = decltype(ImGuiIO::InputCharacters);
 | 
							using FInputCharactersBuffer = decltype(ImGuiIO::InputCharacters);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,8 +55,21 @@ namespace ImGuiInterops
 | 
				
			|||||||
		return GetMouseIndex(MouseEvent.GetEffectingButton());
 | 
							return GetMouseIndex(MouseEvent.GetEffectingButton());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Convert from ImGuiMouseCursor type to EMouseCursor.
 | 
				
			||||||
	EMouseCursor::Type ToSlateMouseCursor(ImGuiMouseCursor MouseCursor);
 | 
						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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//====================================================================================================
 | 
						//====================================================================================================
 | 
				
			||||||
	// Input State Copying
 | 
						// Input State Copying
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ constexpr int32 IMGUI_WIDGET_Z_ORDER = 10000;
 | 
				
			|||||||
DEFINE_LOG_CATEGORY_STATIC(LogImGuiWidget, Warning, All);
 | 
					DEFINE_LOG_CATEGORY_STATIC(LogImGuiWidget, Warning, All);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define TEXT_INPUT_MODE(Val) (\
 | 
					#define TEXT_INPUT_MODE(Val) (\
 | 
				
			||||||
	(Val) == EInputMode::MouseAndKeyboard ? TEXT("MouseAndKeyboard") :\
 | 
						(Val) == EInputMode::Full ? TEXT("Full") :\
 | 
				
			||||||
	(Val) == EInputMode::MousePointerOnly ? TEXT("MousePointerOnly") :\
 | 
						(Val) == EInputMode::MousePointerOnly ? TEXT("MousePointerOnly") :\
 | 
				
			||||||
	TEXT("None"))
 | 
						TEXT("None"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -52,9 +52,11 @@ namespace CVars
 | 
				
			|||||||
		ECVF_Default);
 | 
							ECVF_Default);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TAutoConsoleVariable<int> InputNavigation(TEXT("ImGui.InputNavigation"), 0,
 | 
						TAutoConsoleVariable<int> InputNavigation(TEXT("ImGui.InputNavigation"), 0,
 | 
				
			||||||
		TEXT("[EXPERIMENTAL, WIP] Set ImGui navigation mode.\n")
 | 
							TEXT("EXPERIMENTAL Set ImGui navigation mode.\n")
 | 
				
			||||||
		TEXT("0: navigation is disabled\n")
 | 
							TEXT("0: navigation is disabled\n")
 | 
				
			||||||
		TEXT("1: keyboard navigation"),
 | 
							TEXT("1: keyboard navigation\n")
 | 
				
			||||||
 | 
							TEXT("2: gamepad navigation (gamepad input is consumed)\n")
 | 
				
			||||||
 | 
							TEXT("3: keyboard and gamepad navigation (gamepad input is consumed)"),
 | 
				
			||||||
		ECVF_Default);
 | 
							ECVF_Default);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TAutoConsoleVariable<int> DrawMouseCursor(TEXT("ImGui.DrawMouseCursor"), 0,
 | 
						TAutoConsoleVariable<int> DrawMouseCursor(TEXT("ImGui.DrawMouseCursor"), 0,
 | 
				
			||||||
@ -153,25 +155,56 @@ FReply SImGuiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEven
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
 | 
					FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (KeyEvent.GetKey().IsGamepadKey())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (InputState.IsGamepadNavigationEnabled())
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								InputState.SetGamepadNavigationKey(KeyEvent, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return FReply::Handled();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return Super::OnKeyDown(MyGeometry, KeyEvent);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (IsConsoleOpened() || IgnoreKeyEvent(KeyEvent))
 | 
							if (IsConsoleOpened() || IgnoreKeyEvent(KeyEvent))
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return FReply::Unhandled();
 | 
								return FReply::Unhandled();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), true);
 | 
							InputState.SetKeyDown(KeyEvent, true);
 | 
				
			||||||
		CopyModifierKeys(KeyEvent);
 | 
							CopyModifierKeys(KeyEvent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		UpdateCanvasMapMode(KeyEvent);
 | 
							UpdateCanvasMapMode(KeyEvent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return WithMouseLockRequests(FReply::Handled());
 | 
							return WithMouseLockRequests(FReply::Handled());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
 | 
					FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (KeyEvent.GetKey().IsGamepadKey())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (InputState.IsGamepadNavigationEnabled())
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								InputState.SetGamepadNavigationKey(KeyEvent, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return FReply::Handled();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return Super::OnKeyUp(MyGeometry, KeyEvent);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		// Even if we don't send new keystrokes to ImGui, we still handle key up events, to make sure that we clear keys
 | 
							// Even if we don't send new keystrokes to ImGui, we still handle key up events, to make sure that we clear keys
 | 
				
			||||||
		// pressed before suppressing keyboard input.
 | 
							// pressed before suppressing keyboard input.
 | 
				
			||||||
	InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), false);
 | 
							InputState.SetKeyDown(KeyEvent, false);
 | 
				
			||||||
		CopyModifierKeys(KeyEvent);
 | 
							CopyModifierKeys(KeyEvent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		UpdateCanvasMapMode(KeyEvent);
 | 
							UpdateCanvasMapMode(KeyEvent);
 | 
				
			||||||
@ -179,10 +212,25 @@ FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEv
 | 
				
			|||||||
		// If console is opened we notify key change but we also let event trough, so it can be handled by console.
 | 
							// If console is opened we notify key change but we also let event trough, so it can be handled by console.
 | 
				
			||||||
		return IsConsoleOpened() ? FReply::Unhandled() : WithMouseLockRequests(FReply::Handled());
 | 
							return IsConsoleOpened() ? FReply::Unhandled() : WithMouseLockRequests(FReply::Handled());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FReply SImGuiWidget::OnAnalogValueChanged(const FGeometry& MyGeometry, const FAnalogInputEvent& AnalogInputEvent)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (AnalogInputEvent.GetKey().IsGamepadKey() && InputState.IsGamepadNavigationEnabled())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							InputState.SetGamepadNavigationAxis(AnalogInputEvent, AnalogInputEvent.GetAnalogValue());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return FReply::Handled();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return Super::OnAnalogValueChanged(MyGeometry, AnalogInputEvent);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FReply SImGuiWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 | 
					FReply SImGuiWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), true);
 | 
						InputState.SetMouseDown(MouseEvent, true);
 | 
				
			||||||
	CopyModifierKeys(MouseEvent);
 | 
						CopyModifierKeys(MouseEvent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	UpdateCanvasMapMode(MouseEvent);
 | 
						UpdateCanvasMapMode(MouseEvent);
 | 
				
			||||||
@ -193,7 +241,7 @@ FReply SImGuiWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPoint
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
FReply SImGuiWidget::OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 | 
					FReply SImGuiWidget::OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), true);
 | 
						InputState.SetMouseDown(MouseEvent, true);
 | 
				
			||||||
	CopyModifierKeys(MouseEvent);
 | 
						CopyModifierKeys(MouseEvent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	UpdateCanvasMapMode(MouseEvent);
 | 
						UpdateCanvasMapMode(MouseEvent);
 | 
				
			||||||
@ -204,7 +252,7 @@ FReply SImGuiWidget::OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
FReply SImGuiWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 | 
					FReply SImGuiWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), false);
 | 
						InputState.SetMouseDown(MouseEvent, false);
 | 
				
			||||||
	CopyModifierKeys(MouseEvent);
 | 
						CopyModifierKeys(MouseEvent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	UpdateCanvasMapMode(MouseEvent);
 | 
						UpdateCanvasMapMode(MouseEvent);
 | 
				
			||||||
@ -299,7 +347,7 @@ void SImGuiWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		for (const FKey& Button : { EKeys::LeftMouseButton, EKeys::MiddleMouseButton, EKeys::RightMouseButton, EKeys::ThumbMouseButton, EKeys::ThumbMouseButton2 })
 | 
							for (const FKey& Button : { EKeys::LeftMouseButton, EKeys::MiddleMouseButton, EKeys::RightMouseButton, EKeys::ThumbMouseButton, EKeys::ThumbMouseButton2 })
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(Button), MouseEvent.IsMouseButtonDown(Button));
 | 
								InputState.SetMouseDown(Button, MouseEvent.IsMouseButtonDown(Button));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -440,18 +488,24 @@ void SImGuiWidget::UpdateInputEnabled()
 | 
				
			|||||||
	// We don't get any events when application loses focus (we get OnMouseLeave but not always) but we fix it with
 | 
						// We don't get any events when application loses focus (we get OnMouseLeave but not always) but we fix it with
 | 
				
			||||||
	// this manual check. We still allow the above code to run, even if we need to suppress keyboard input right after
 | 
						// this manual check. We still allow the above code to run, even if we need to suppress keyboard input right after
 | 
				
			||||||
	// that.
 | 
						// that.
 | 
				
			||||||
	if (bInputEnabled && !GameViewport->Viewport->IsForegroundWindow() && InputMode == EInputMode::MouseAndKeyboard)
 | 
						if (bInputEnabled && !GameViewport->Viewport->IsForegroundWindow() && InputMode == EInputMode::Full)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		UpdateInputMode(false, IsDirectlyHovered());
 | 
							UpdateInputMode(false, IsDirectlyHovered());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	InputState.SetKeyboardNavigationEnabled(CVars::InputNavigation.GetValueOnGameThread() > 0);
 | 
						if (bInputEnabled)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							InputState.SetKeyboardNavigationEnabled(CVars::InputNavigation.GetValueOnGameThread() & 1);
 | 
				
			||||||
 | 
							InputState.SetGamepadNavigationEnabled(CVars::InputNavigation.GetValueOnGameThread() & 2);
 | 
				
			||||||
 | 
							const auto& Application = FSlateApplication::Get().GetPlatformApplication();
 | 
				
			||||||
 | 
							InputState.SetGamepad(Application.IsValid() && Application->IsGamepadAttached());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SImGuiWidget::UpdateInputMode(bool bHasKeyboardFocus, bool bHasMousePointer)
 | 
					void SImGuiWidget::UpdateInputMode(bool bHasKeyboardFocus, bool bHasMousePointer)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const EInputMode NewInputMode =
 | 
						const EInputMode NewInputMode =
 | 
				
			||||||
		bHasKeyboardFocus ? EInputMode::MouseAndKeyboard :
 | 
							bHasKeyboardFocus ? EInputMode::Full :
 | 
				
			||||||
		bHasMousePointer ? EInputMode::MousePointerOnly :
 | 
							bHasMousePointer ? EInputMode::MousePointerOnly :
 | 
				
			||||||
		EInputMode::None;
 | 
							EInputMode::None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -466,16 +520,17 @@ void SImGuiWidget::UpdateInputMode(bool bHasKeyboardFocus, bool bHasMousePointer
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			InputState.ResetState();
 | 
								InputState.ResetState();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else if (InputMode == EInputMode::MouseAndKeyboard)
 | 
							else if (InputMode == EInputMode::Full)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			InputState.ResetKeyboardState();
 | 
								InputState.ResetKeyboardState();
 | 
				
			||||||
 | 
								InputState.ResetNavigationState();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		InputMode = NewInputMode;
 | 
							InputMode = NewInputMode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ClearMouseEventNotification();
 | 
							ClearMouseEventNotification();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (InputMode != EInputMode::MouseAndKeyboard)
 | 
							if (InputMode != EInputMode::Full)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			SetCanvasMapMode(false);
 | 
								SetCanvasMapMode(false);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -53,6 +53,8 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) override;
 | 
						virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual FReply OnAnalogValueChanged(const FGeometry& MyGeometry, const FAnalogInputEvent& AnalogInputEvent) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
 | 
						virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual FReply OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
 | 
						virtual FReply OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
 | 
				
			||||||
@ -80,8 +82,8 @@ private:
 | 
				
			|||||||
		None,
 | 
							None,
 | 
				
			||||||
		// Mouse pointer only without user focus
 | 
							// Mouse pointer only without user focus
 | 
				
			||||||
		MousePointerOnly,
 | 
							MousePointerOnly,
 | 
				
			||||||
		// Full input with user focus
 | 
							// Full input with user focus (mouse, keyboard and depending on navigation mode gamepad)
 | 
				
			||||||
		MouseAndKeyboard
 | 
							Full
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If needed, add to event reply a mouse lock or unlock request.
 | 
						// If needed, add to event reply a mouse lock or unlock request.
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user