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)
 | 
			
		||||
	: Context(std::move(Other.Context))
 | 
			
		||||
	, bHasActiveItem(Other.bHasActiveItem)
 | 
			
		||||
	, DrawEvent(std::move(Other.DrawEvent))
 | 
			
		||||
	, InputState(std::move(Other.InputState))
 | 
			
		||||
	, DrawLists(std::move(Other.DrawLists))
 | 
			
		||||
@ -50,6 +51,7 @@ FImGuiContextProxy& FImGuiContextProxy::operator=(FImGuiContextProxy&& Other)
 | 
			
		||||
{
 | 
			
		||||
	Context = std::move(Other.Context);
 | 
			
		||||
	Other.Context = nullptr;
 | 
			
		||||
	bHasActiveItem = Other.bHasActiveItem;
 | 
			
		||||
	DrawEvent = std::move(Other.DrawEvent);
 | 
			
		||||
	InputState = std::move(Other.InputState);
 | 
			
		||||
	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.
 | 
			
		||||
	BeginFrame(DeltaSeconds);
 | 
			
		||||
 | 
			
		||||
	// Update context information.
 | 
			
		||||
	bHasActiveItem = ImGui::IsAnyItemActive();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FImGuiContextProxy::BeginFrame(float DeltaTime)
 | 
			
		||||
 | 
			
		||||
@ -39,6 +39,8 @@ public:
 | 
			
		||||
	// Set this context as current ImGui context.
 | 
			
		||||
	void SetAsCurrent() { ImGui::SetCurrentContext(Context); }
 | 
			
		||||
 | 
			
		||||
	bool HasActiveItem() const { return bHasActiveItem; }
 | 
			
		||||
 | 
			
		||||
	// Delegate called right before ending the frame to allows listeners draw their controls.
 | 
			
		||||
	FSimpleMulticastDelegate& OnDraw() { return DrawEvent; }
 | 
			
		||||
 | 
			
		||||
@ -54,6 +56,8 @@ private:
 | 
			
		||||
 | 
			
		||||
	ImGuiContext* Context = nullptr;
 | 
			
		||||
 | 
			
		||||
	bool bHasActiveItem = false;
 | 
			
		||||
 | 
			
		||||
	bool bIsFrameStarted = false;
 | 
			
		||||
	FSimpleMulticastDelegate DrawEvent;
 | 
			
		||||
	const FImGuiInputState* InputState = nullptr;
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,8 @@
 | 
			
		||||
#include "TextureManager.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.
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
	if (IsConsoleOpened())
 | 
			
		||||
	{
 | 
			
		||||
		return FReply::Unhandled();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	InputState.AddCharacter(CharacterEvent.GetCharacter());
 | 
			
		||||
 | 
			
		||||
	return FReply::Handled();
 | 
			
		||||
@ -105,6 +112,11 @@ FReply SImGuiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEven
 | 
			
		||||
 | 
			
		||||
FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
 | 
			
		||||
{
 | 
			
		||||
	if (IsConsoleOpened() || IgnoreKeyEvent(KeyEvent))
 | 
			
		||||
	{
 | 
			
		||||
		return FReply::Unhandled();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), true);
 | 
			
		||||
	CopyModifierKeys(KeyEvent);
 | 
			
		||||
 | 
			
		||||
@ -119,10 +131,13 @@ FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& Key
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
	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)
 | 
			
		||||
@ -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.
 | 
			
		||||
	UpdateInputMode(true, true);
 | 
			
		||||
 | 
			
		||||
	FSlateApplication::Get().ResetToDefaultPointerInputSettings();
 | 
			
		||||
	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()
 | 
			
		||||
{
 | 
			
		||||
	bInputEnabled = false;
 | 
			
		||||
@ -258,24 +300,9 @@ void SImGuiWidget::UpdateInputEnabled()
 | 
			
		||||
 | 
			
		||||
		SetVisibilityFromInputEnabled();
 | 
			
		||||
 | 
			
		||||
		// Setup input to show cursor and to pass keyboard/user focus between viewport and widget. Note that we should
 | 
			
		||||
		// only pass focus if it is inside of the parent viewport, otherwise we would be stealing from other viewports
 | 
			
		||||
		// or windows.
 | 
			
		||||
		if (!bInputEnabled)
 | 
			
		||||
		{
 | 
			
		||||
			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)
 | 
			
		||||
			{
 | 
			
		||||
				Slate.ResetToDefaultPointerInputSettings();
 | 
			
		||||
@ -288,6 +315,19 @@ void SImGuiWidget::UpdateInputEnabled()
 | 
			
		||||
			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)
 | 
			
		||||
 | 
			
		||||
@ -86,6 +86,10 @@ private:
 | 
			
		||||
	FORCEINLINE void CopyModifierKeys(const FInputEvent& InputEvent);
 | 
			
		||||
	FORCEINLINE void CopyModifierKeys(const FPointerEvent& MouseEvent);
 | 
			
		||||
 | 
			
		||||
	bool IsConsoleOpened() const;
 | 
			
		||||
 | 
			
		||||
	bool IgnoreKeyEvent(const FKeyEvent& KeyEvent) const;
 | 
			
		||||
 | 
			
		||||
	void ResetInputState();
 | 
			
		||||
 | 
			
		||||
	// Update visibility based on input enabled state.
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user