mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-01-18 16:30: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)
|
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