mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-01-18 08:20:32 +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.
|
||||
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 (!bInputEnabled)
|
||||
{
|
||||
auto& Slate = FSlateApplication::Get();
|
||||
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