mirror of
				https://github.com/kevinporetti/UnrealImGui.git
				synced 2025-11-03 23:33:16 +00:00 
			
		
		
		
	Added handling of special input cases:
- Different handling of 'Esc' key. If ImGui has active items then it is used to exit edit mode, otherwise it is passed to editor to cancel session. - Suppressing keyboard input when console is opened and retaking it when it is closed. - Fixed issue with losing input when console is closed.
This commit is contained in:
		
							parent
							
								
									c47d911f22
								
							
						
					
					
						commit
						5625365310
					
				@ -39,6 +39,7 @@ FImGuiContextProxy::FImGuiContextProxy()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
FImGuiContextProxy::FImGuiContextProxy(FImGuiContextProxy&& Other)
 | 
					FImGuiContextProxy::FImGuiContextProxy(FImGuiContextProxy&& Other)
 | 
				
			||||||
	: Context(std::move(Other.Context))
 | 
						: Context(std::move(Other.Context))
 | 
				
			||||||
 | 
						, bHasActiveItem(Other.bHasActiveItem)
 | 
				
			||||||
	, DrawEvent(std::move(Other.DrawEvent))
 | 
						, DrawEvent(std::move(Other.DrawEvent))
 | 
				
			||||||
	, InputState(std::move(Other.InputState))
 | 
						, InputState(std::move(Other.InputState))
 | 
				
			||||||
	, DrawLists(std::move(Other.DrawLists))
 | 
						, DrawLists(std::move(Other.DrawLists))
 | 
				
			||||||
@ -50,6 +51,7 @@ FImGuiContextProxy& FImGuiContextProxy::operator=(FImGuiContextProxy&& Other)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	Context = std::move(Other.Context);
 | 
						Context = std::move(Other.Context);
 | 
				
			||||||
	Other.Context = nullptr;
 | 
						Other.Context = nullptr;
 | 
				
			||||||
 | 
						bHasActiveItem = Other.bHasActiveItem;
 | 
				
			||||||
	DrawEvent = std::move(Other.DrawEvent);
 | 
						DrawEvent = std::move(Other.DrawEvent);
 | 
				
			||||||
	InputState = std::move(Other.InputState);
 | 
						InputState = std::move(Other.InputState);
 | 
				
			||||||
	DrawLists = std::move(Other.DrawLists);
 | 
						DrawLists = std::move(Other.DrawLists);
 | 
				
			||||||
@ -91,6 +93,9 @@ void FImGuiContextProxy::Tick(float DeltaSeconds)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Begin a new frame and set the context back to a state in which it allows to draw controls.
 | 
						// Begin a new frame and set the context back to a state in which it allows to draw controls.
 | 
				
			||||||
	BeginFrame(DeltaSeconds);
 | 
						BeginFrame(DeltaSeconds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Update context information.
 | 
				
			||||||
 | 
						bHasActiveItem = ImGui::IsAnyItemActive();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void FImGuiContextProxy::BeginFrame(float DeltaTime)
 | 
					void FImGuiContextProxy::BeginFrame(float DeltaTime)
 | 
				
			||||||
 | 
				
			|||||||
@ -39,6 +39,8 @@ public:
 | 
				
			|||||||
	// Set this context as current ImGui context.
 | 
						// Set this context as current ImGui context.
 | 
				
			||||||
	void SetAsCurrent() { ImGui::SetCurrentContext(Context); }
 | 
						void SetAsCurrent() { ImGui::SetCurrentContext(Context); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool HasActiveItem() const { return bHasActiveItem; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Delegate called right before ending the frame to allows listeners draw their controls.
 | 
						// Delegate called right before ending the frame to allows listeners draw their controls.
 | 
				
			||||||
	FSimpleMulticastDelegate& OnDraw() { return DrawEvent; }
 | 
						FSimpleMulticastDelegate& OnDraw() { return DrawEvent; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,6 +56,8 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	ImGuiContext* Context = nullptr;
 | 
						ImGuiContext* Context = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool bHasActiveItem = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool bIsFrameStarted = false;
 | 
						bool bIsFrameStarted = false;
 | 
				
			||||||
	FSimpleMulticastDelegate DrawEvent;
 | 
						FSimpleMulticastDelegate DrawEvent;
 | 
				
			||||||
	const FImGuiInputState* InputState = nullptr;
 | 
						const FImGuiInputState* InputState = nullptr;
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,8 @@
 | 
				
			|||||||
#include "TextureManager.h"
 | 
					#include "TextureManager.h"
 | 
				
			||||||
#include "Utilities/ScopeGuards.h"
 | 
					#include "Utilities/ScopeGuards.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Engine/Console.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// High enough z-order guarantees that ImGui output is rendered on top of the game UI.
 | 
					// High enough z-order guarantees that ImGui output is rendered on top of the game UI.
 | 
				
			||||||
constexpr int32 IMGUI_WIDGET_Z_ORDER = 10000;
 | 
					constexpr int32 IMGUI_WIDGET_Z_ORDER = 10000;
 | 
				
			||||||
@ -98,6 +100,11 @@ void SImGuiWidget::Tick(const FGeometry& AllottedGeometry, const double InCurren
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
FReply SImGuiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent)
 | 
					FReply SImGuiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						if (IsConsoleOpened())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return FReply::Unhandled();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	InputState.AddCharacter(CharacterEvent.GetCharacter());
 | 
						InputState.AddCharacter(CharacterEvent.GetCharacter());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return FReply::Handled();
 | 
						return FReply::Handled();
 | 
				
			||||||
@ -105,6 +112,11 @@ 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 (IsConsoleOpened() || IgnoreKeyEvent(KeyEvent))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return FReply::Unhandled();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), true);
 | 
						InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), true);
 | 
				
			||||||
	CopyModifierKeys(KeyEvent);
 | 
						CopyModifierKeys(KeyEvent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -119,10 +131,13 @@ FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& Key
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
 | 
					FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						// 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.
 | 
				
			||||||
	InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), false);
 | 
						InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), false);
 | 
				
			||||||
	CopyModifierKeys(KeyEvent);
 | 
						CopyModifierKeys(KeyEvent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return FReply::Handled();
 | 
						// 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() : FReply::Handled();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FReply SImGuiWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 | 
					FReply SImGuiWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 | 
				
			||||||
@ -175,6 +190,7 @@ FReply SImGuiWidget::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEv
 | 
				
			|||||||
	// area it won't generate events but we freeze its state until it either comes back or input is completely lost.
 | 
						// area it won't generate events but we freeze its state until it either comes back or input is completely lost.
 | 
				
			||||||
	UpdateInputMode(true, true);
 | 
						UpdateInputMode(true, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						FSlateApplication::Get().ResetToDefaultPointerInputSettings();
 | 
				
			||||||
	return FReply::Handled();
 | 
						return FReply::Handled();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -230,6 +246,32 @@ void SImGuiWidget::CopyModifierKeys(const FPointerEvent& MouseEvent)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool SImGuiWidget::IsConsoleOpened() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return GameViewport->ViewportConsole && GameViewport->ViewportConsole->ConsoleState != NAME_None;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool SImGuiWidget::IgnoreKeyEvent(const FKeyEvent& KeyEvent) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Ignore console open/close events.
 | 
				
			||||||
 | 
						if (KeyEvent.GetKey() == EKeys::Tilde)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ignore escape keys unless they are needed to cancel operations in ImGui.
 | 
				
			||||||
 | 
						if (KeyEvent.GetKey() == EKeys::Escape)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
 | 
				
			||||||
 | 
							if (!ContextProxy || !ContextProxy->HasActiveItem())
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SImGuiWidget::ResetInputState()
 | 
					void SImGuiWidget::ResetInputState()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	bInputEnabled = false;
 | 
						bInputEnabled = false;
 | 
				
			||||||
@ -258,24 +300,9 @@ void SImGuiWidget::UpdateInputEnabled()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		SetVisibilityFromInputEnabled();
 | 
							SetVisibilityFromInputEnabled();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Setup input to show cursor and to pass keyboard/user focus between viewport and widget. Note that we should
 | 
							if (!bInputEnabled)
 | 
				
			||||||
		// only pass focus if it is inside of the parent viewport, otherwise we would be stealing from other viewports
 | 
							{
 | 
				
			||||||
		// or windows.
 | 
					 | 
				
			||||||
			auto& Slate = FSlateApplication::Get();
 | 
								auto& Slate = FSlateApplication::Get();
 | 
				
			||||||
		if (bInputEnabled)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			const auto& ViewportWidget = GameViewport->GetGameViewportWidget();
 | 
					 | 
				
			||||||
			if (ViewportWidget->HasKeyboardFocus() || ViewportWidget->HasFocusedDescendants())
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				// Remember where is user focus, so we will have an option to restore it.
 | 
					 | 
				
			||||||
				PreviousUserFocusedWidget = Slate.GetUserFocusedWidget(Slate.GetUserIndexForKeyboard());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				Slate.ResetToDefaultPointerInputSettings();
 | 
					 | 
				
			||||||
				Slate.SetKeyboardFocus(SharedThis(this));
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if (Slate.GetKeyboardFocusedWidget().Get() == this)
 | 
								if (Slate.GetKeyboardFocusedWidget().Get() == this)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				Slate.ResetToDefaultPointerInputSettings();
 | 
									Slate.ResetToDefaultPointerInputSettings();
 | 
				
			||||||
@ -288,6 +315,19 @@ void SImGuiWidget::UpdateInputEnabled()
 | 
				
			|||||||
			UpdateInputMode(false, false);
 | 
								UpdateInputMode(false, false);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Note: Some widgets, like console, can reset focus to viewport after we already grabbed it. If we detect that
 | 
				
			||||||
 | 
						// viewport has a focus while input is enabled we will take it.
 | 
				
			||||||
 | 
						if (bInputEnabled && !HasKeyboardFocus() && !IsConsoleOpened())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const auto& ViewportWidget = GameViewport->GetGameViewportWidget();
 | 
				
			||||||
 | 
							if (ViewportWidget->HasKeyboardFocus() || ViewportWidget->HasFocusedDescendants())
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								auto& Slate = FSlateApplication::Get();
 | 
				
			||||||
 | 
								PreviousUserFocusedWidget = Slate.GetUserFocusedWidget(Slate.GetUserIndexForKeyboard());
 | 
				
			||||||
 | 
								Slate.SetKeyboardFocus(SharedThis(this));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SImGuiWidget::UpdateInputMode(bool bNeedKeyboard, bool bNeedMouse)
 | 
					void SImGuiWidget::UpdateInputMode(bool bNeedKeyboard, bool bNeedMouse)
 | 
				
			||||||
 | 
				
			|||||||
@ -86,6 +86,10 @@ private:
 | 
				
			|||||||
	FORCEINLINE void CopyModifierKeys(const FInputEvent& InputEvent);
 | 
						FORCEINLINE void CopyModifierKeys(const FInputEvent& InputEvent);
 | 
				
			||||||
	FORCEINLINE void CopyModifierKeys(const FPointerEvent& MouseEvent);
 | 
						FORCEINLINE void CopyModifierKeys(const FPointerEvent& MouseEvent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool IsConsoleOpened() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool IgnoreKeyEvent(const FKeyEvent& KeyEvent) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void ResetInputState();
 | 
						void ResetInputState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update visibility based on input enabled state.
 | 
						// Update visibility based on input enabled state.
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user