mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-01-18 00:10:32 +00:00
Moved the whole input handling from SImGuiWidget to UImGuiInputHandler:
- Moved responsibility for updating input state from the widget to the input handler. - Changed the widget to fully delegate input events to the input handler and only manage its own input state. - Changed the input handler interface to allow handling of all the necessary input events. - Changed the input handler interface to use FReply as a response type and removed obsolete FImGuiInputResponse.
This commit is contained in:
parent
852a501022
commit
979903722a
@ -5,6 +5,7 @@
|
||||
#include "ImGuiInputHandler.h"
|
||||
|
||||
#include "ImGuiContextProxy.h"
|
||||
#include "ImGuiInputState.h"
|
||||
#include "ImGuiModuleManager.h"
|
||||
#include "ImGuiModuleSettings.h"
|
||||
|
||||
@ -20,44 +21,182 @@
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogImGuiInputHandler);
|
||||
|
||||
static FImGuiInputResponse IgnoreResponse{ false, false };
|
||||
|
||||
FImGuiInputResponse UImGuiInputHandler::OnKeyDown(const FKeyEvent& KeyEvent)
|
||||
namespace
|
||||
{
|
||||
// Ignore console events, so we don't block it from opening.
|
||||
if (IsConsoleEvent(KeyEvent))
|
||||
FReply ToReply(bool bConsume)
|
||||
{
|
||||
return IgnoreResponse;
|
||||
return bConsume ? FReply::Handled() : FReply::Unhandled();
|
||||
}
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnKeyChar(const struct FCharacterEvent& CharacterEvent)
|
||||
{
|
||||
InputState->AddCharacter(CharacterEvent.GetCharacter());
|
||||
return ToReply(!ModuleManager->GetProperties().IsKeyboardInputShared());
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnKeyDown(const FKeyEvent& KeyEvent)
|
||||
{
|
||||
if (KeyEvent.GetKey().IsGamepadKey())
|
||||
{
|
||||
bool bConsume = false;
|
||||
if (InputState->IsGamepadNavigationEnabled())
|
||||
{
|
||||
InputState->SetGamepadNavigationKey(KeyEvent, true);
|
||||
bConsume = !ModuleManager->GetProperties().IsGamepadInputShared();
|
||||
}
|
||||
|
||||
return ToReply(bConsume);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore console events, so we don't block it from opening.
|
||||
if (IsConsoleEvent(KeyEvent))
|
||||
{
|
||||
return ToReply(false);
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
// If there is no active ImGui control that would get precedence and this key event is bound to a stop play session
|
||||
// command, then ignore that event and let the command execute.
|
||||
if (!HasImGuiActiveItem() && IsStopPlaySessionEvent(KeyEvent))
|
||||
{
|
||||
return IgnoreResponse;
|
||||
}
|
||||
// If there is no active ImGui control that would get precedence and this key event is bound to a stop play session
|
||||
// command, then ignore that event and let the command execute.
|
||||
if (!HasImGuiActiveItem() && IsStopPlaySessionEvent(KeyEvent))
|
||||
{
|
||||
return ToReply(false);
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
const FImGuiInputResponse Response = GetDefaultKeyboardResponse();
|
||||
const bool bConsume = !ModuleManager->GetProperties().IsKeyboardInputShared();
|
||||
|
||||
// With shared input we can leave command bindings for DebugExec to handle, otherwise we need to do it here.
|
||||
if (Response.HasConsumeRequest() && IsToggleInputEvent(KeyEvent))
|
||||
// With shared input we can leave command bindings for DebugExec to handle, otherwise we need to do it here.
|
||||
if (bConsume && IsToggleInputEvent(KeyEvent))
|
||||
{
|
||||
ModuleManager->GetProperties().ToggleInput();
|
||||
}
|
||||
|
||||
InputState->SetKeyDown(KeyEvent, true);
|
||||
CopyModifierKeys(KeyEvent);
|
||||
|
||||
return ToReply(bConsume);
|
||||
}
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnKeyUp(const FKeyEvent& KeyEvent)
|
||||
{
|
||||
if (KeyEvent.GetKey().IsGamepadKey())
|
||||
{
|
||||
ModuleManager->GetProperties().ToggleInput();
|
||||
bool bConsume = false;
|
||||
if (InputState->IsGamepadNavigationEnabled())
|
||||
{
|
||||
InputState->SetGamepadNavigationKey(KeyEvent, false);
|
||||
bConsume = !ModuleManager->GetProperties().IsGamepadInputShared();
|
||||
}
|
||||
|
||||
return ToReply(bConsume);
|
||||
}
|
||||
else
|
||||
{
|
||||
InputState->SetKeyDown(KeyEvent, false);
|
||||
CopyModifierKeys(KeyEvent);
|
||||
|
||||
return ToReply(!ModuleManager->GetProperties().IsKeyboardInputShared());
|
||||
}
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnAnalogValueChanged(const FAnalogInputEvent& AnalogInputEvent)
|
||||
{
|
||||
bool bConsume = false;
|
||||
|
||||
if (AnalogInputEvent.GetKey().IsGamepadKey() && InputState->IsGamepadNavigationEnabled())
|
||||
{
|
||||
InputState->SetGamepadNavigationAxis(AnalogInputEvent, AnalogInputEvent.GetAnalogValue());
|
||||
bConsume = !ModuleManager->GetProperties().IsGamepadInputShared();
|
||||
}
|
||||
|
||||
return Response;
|
||||
return ToReply(bConsume);
|
||||
}
|
||||
|
||||
FImGuiInputResponse UImGuiInputHandler::GetDefaultKeyboardResponse() const
|
||||
FReply UImGuiInputHandler::OnMouseButtonDown(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
return FImGuiInputResponse{ true, !ModuleManager->GetProperties().IsKeyboardInputShared() };
|
||||
InputState->SetMouseDown(MouseEvent, true);
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FImGuiInputResponse UImGuiInputHandler::GetDefaultGamepadResponse() const
|
||||
FReply UImGuiInputHandler::OnMouseButtonDoubleClick(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
return FImGuiInputResponse{ true, !ModuleManager->GetProperties().IsGamepadInputShared() };
|
||||
InputState->SetMouseDown(MouseEvent, true);
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnMouseButtonUp(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState->SetMouseDown(MouseEvent, false);
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnMouseWheel(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState->AddMouseWheelDelta(MouseEvent.GetWheelDelta());
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnMouseMove(const FVector2D& MousePosition)
|
||||
{
|
||||
InputState->SetMousePosition(MousePosition);
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnKeyboardInputEnabled()
|
||||
{
|
||||
bKeyboardInputEnabled = true;
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnKeyboardInputDisabled()
|
||||
{
|
||||
if (bKeyboardInputEnabled)
|
||||
{
|
||||
bKeyboardInputEnabled = false;
|
||||
InputState->ResetKeyboard();
|
||||
}
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnGamepadInputEnabled()
|
||||
{
|
||||
bGamepadInputEnabled = true;
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnGamepadInputDisabled()
|
||||
{
|
||||
if (bGamepadInputEnabled)
|
||||
{
|
||||
bGamepadInputEnabled = false;
|
||||
InputState->ResetGamepadNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnMouseInputEnabled()
|
||||
{
|
||||
if (!bMouseInputEnabled)
|
||||
{
|
||||
bMouseInputEnabled = true;
|
||||
UpdateInputStatePointer();
|
||||
}
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnMouseInputDisabled()
|
||||
{
|
||||
if (bMouseInputEnabled)
|
||||
{
|
||||
bMouseInputEnabled = false;
|
||||
InputState->ResetMouse();
|
||||
UpdateInputStatePointer();
|
||||
}
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::CopyModifierKeys(const FInputEvent& InputEvent)
|
||||
{
|
||||
InputState->SetControlDown(InputEvent.IsControlDown());
|
||||
InputState->SetShiftDown(InputEvent.IsShiftDown());
|
||||
InputState->SetAltDown(InputEvent.IsAltDown());
|
||||
}
|
||||
|
||||
bool UImGuiInputHandler::IsConsoleEvent(const FKeyEvent& KeyEvent) const
|
||||
@ -113,12 +252,47 @@ bool UImGuiInputHandler::HasImGuiActiveItem() const
|
||||
return ContextProxy && ContextProxy->HasActiveItem();
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::UpdateInputStatePointer()
|
||||
{
|
||||
InputState->SetMousePointer(bMouseInputEnabled && ModuleManager->GetSettings().UseSoftwareCursor());
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnSoftwareCursorChanged(bool)
|
||||
{
|
||||
UpdateInputStatePointer();
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnPostImGuiUpdate()
|
||||
{
|
||||
InputState->ClearUpdateState();
|
||||
|
||||
// TODO Replace with delegates after adding property change events.
|
||||
InputState->SetKeyboardNavigationEnabled(ModuleManager->GetProperties().IsKeyboardNavigationEnabled());
|
||||
InputState->SetGamepadNavigationEnabled(ModuleManager->GetProperties().IsGamepadNavigationEnabled());
|
||||
|
||||
const auto& PlatformApplication = FSlateApplication::Get().GetPlatformApplication();
|
||||
InputState->SetGamepad(PlatformApplication.IsValid() && PlatformApplication->IsGamepadAttached());
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::Initialize(FImGuiModuleManager* InModuleManager, UGameViewportClient* InGameViewport, int32 InContextIndex)
|
||||
{
|
||||
ModuleManager = InModuleManager;
|
||||
GameViewport = InGameViewport;
|
||||
ContextIndex = InContextIndex;
|
||||
|
||||
auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
checkf(ContextProxy, TEXT("Missing context during initialization of input handler: ContextIndex = %d"), ContextIndex);
|
||||
InputState = &ContextProxy->GetInputState();
|
||||
|
||||
// Register to get post-update notifications, so we can clean frame updates.
|
||||
ModuleManager->OnPostImGuiUpdate().AddUObject(this, &UImGuiInputHandler::OnPostImGuiUpdate);
|
||||
|
||||
auto& Settings = ModuleManager->GetSettings();
|
||||
if (!Settings.OnUseSoftwareCursorChanged.IsBoundToObject(this))
|
||||
{
|
||||
Settings.OnUseSoftwareCursorChanged.AddUObject(this, &UImGuiInputHandler::OnSoftwareCursorChanged);
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
StopPlaySessionCommandInfo = FInputBindingManager::Get().FindCommandInContext("PlayWorld", "StopPlaySession");
|
||||
if (!StopPlaySessionCommandInfo.IsValid())
|
||||
@ -128,3 +302,14 @@ void UImGuiInputHandler::Initialize(FImGuiModuleManager* InModuleManager, UGameV
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::BeginDestroy()
|
||||
{
|
||||
Super::BeginDestroy();
|
||||
|
||||
if (ModuleManager)
|
||||
{
|
||||
ModuleManager->GetSettings().OnUseSoftwareCursorChanged.RemoveAll(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,8 @@
|
||||
|
||||
#include "ImGuiContextManager.h"
|
||||
#include "ImGuiContextProxy.h"
|
||||
#include "ImGuiImplementation.h"
|
||||
#include "ImGuiInputHandler.h"
|
||||
#include "ImGuiInputHandlerFactory.h"
|
||||
#include "ImGuiInputState.h"
|
||||
#include "ImGuiInteroperability.h"
|
||||
#include "ImGuiModuleManager.h"
|
||||
#include "ImGuiModuleSettings.h"
|
||||
@ -69,34 +67,28 @@ void SImGuiWidget::Construct(const FArguments& InArgs)
|
||||
GameViewport = InArgs._GameViewport;
|
||||
ContextIndex = InArgs._ContextIndex;
|
||||
|
||||
// Disable mouse cursor over this widget as we will use ImGui to draw it.
|
||||
SetCursor(EMouseCursor::None);
|
||||
|
||||
// Sync visibility with default input enabled state.
|
||||
UpdateVisibility();
|
||||
|
||||
// Register to get post-update notifications, so we can clean frame updates.
|
||||
// Register to get post-update notifications.
|
||||
ModuleManager->OnPostImGuiUpdate().AddRaw(this, &SImGuiWidget::OnPostImGuiUpdate);
|
||||
|
||||
// Bind this widget to its context proxy.
|
||||
// Register debug delegate.
|
||||
auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
checkf(ContextProxy, TEXT("Missing context during widget construction: ContextIndex = %d"), ContextIndex);
|
||||
#if IMGUI_WIDGET_DEBUG
|
||||
ContextProxy->OnDraw().AddRaw(this, &SImGuiWidget::OnDebugDraw);
|
||||
#endif // IMGUI_WIDGET_DEBUG
|
||||
InputState = &ContextProxy->GetInputState();
|
||||
|
||||
// Register for settings change.
|
||||
RegisterImGuiSettingsDelegates();
|
||||
|
||||
// Get initial settings.
|
||||
const auto& Settings = ModuleManager->GetSettings();
|
||||
|
||||
// Cache locally software cursor mode.
|
||||
SetUseSoftwareCursor(Settings.UseSoftwareCursor());
|
||||
|
||||
// Create ImGui Input Handler.
|
||||
SetHideMouseCursor(Settings.UseSoftwareCursor());
|
||||
CreateInputHandler(Settings.GetImGuiInputHandlerClass());
|
||||
|
||||
// Initialize state.
|
||||
UpdateVisibility();
|
||||
UpdateMouseCursor();
|
||||
|
||||
ChildSlot
|
||||
[
|
||||
SAssignNew(CanvasControlWidget, SImGuiCanvasControl).OnTransformChanged(this, &SImGuiWidget::SetImGuiTransform)
|
||||
@ -130,139 +122,74 @@ void SImGuiWidget::Tick(const FGeometry& AllottedGeometry, const double InCurren
|
||||
{
|
||||
Super::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||
|
||||
// Note: Moving that update to console variable sink or callback might seem like a better alternative but input
|
||||
// setup in this function is better handled here.
|
||||
UpdateInputEnabled();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
FReply ToSlateReply(const FImGuiInputResponse& HandlingResponse)
|
||||
{
|
||||
return HandlingResponse.HasConsumeRequest() ? FReply::Handled() : FReply::Unhandled();
|
||||
}
|
||||
UpdateInputState();
|
||||
HandleWindowFocusLost();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent)
|
||||
{
|
||||
const FImGuiInputResponse Response = InputHandler->OnKeyChar(CharacterEvent);
|
||||
if (Response.HasProcessingRequest())
|
||||
{
|
||||
InputState->AddCharacter(CharacterEvent.GetCharacter());
|
||||
}
|
||||
|
||||
return ToSlateReply(Response);
|
||||
return InputHandler->OnKeyChar(CharacterEvent);
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
|
||||
{
|
||||
if (KeyEvent.GetKey().IsGamepadKey())
|
||||
{
|
||||
if (InputState->IsGamepadNavigationEnabled())
|
||||
{
|
||||
const FImGuiInputResponse Response = InputHandler->OnGamepadKeyDown(KeyEvent);
|
||||
if (Response.HasProcessingRequest())
|
||||
{
|
||||
InputState->SetGamepadNavigationKey(KeyEvent, true);
|
||||
}
|
||||
|
||||
return ToSlateReply(Response);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Super::OnKeyDown(MyGeometry, KeyEvent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCanvasControlMode(KeyEvent);
|
||||
|
||||
const FImGuiInputResponse Response = InputHandler->OnKeyDown(KeyEvent);
|
||||
if (Response.HasProcessingRequest())
|
||||
{
|
||||
InputState->SetKeyDown(KeyEvent, true);
|
||||
CopyModifierKeys(KeyEvent);
|
||||
}
|
||||
|
||||
return ToSlateReply(Response);
|
||||
}
|
||||
UpdateCanvasControlMode(KeyEvent);
|
||||
return InputHandler->OnKeyDown(KeyEvent);
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
|
||||
{
|
||||
if (KeyEvent.GetKey().IsGamepadKey())
|
||||
{
|
||||
if (InputState->IsGamepadNavigationEnabled())
|
||||
{
|
||||
// Always handle key up events to protect from leaving accidental keys not cleared in ImGui input state.
|
||||
InputState->SetGamepadNavigationKey(KeyEvent, false);
|
||||
|
||||
return ToSlateReply(InputHandler->OnGamepadKeyUp(KeyEvent));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Super::OnKeyUp(MyGeometry, KeyEvent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCanvasControlMode(KeyEvent);
|
||||
|
||||
// Always handle key up events to protect from leaving accidental keys not cleared in ImGui input state.
|
||||
InputState->SetKeyDown(KeyEvent, false);
|
||||
CopyModifierKeys(KeyEvent);
|
||||
|
||||
return ToSlateReply(InputHandler->OnKeyUp(KeyEvent));
|
||||
}
|
||||
UpdateCanvasControlMode(KeyEvent);
|
||||
return InputHandler->OnKeyUp(KeyEvent);
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnAnalogValueChanged(const FGeometry& MyGeometry, const FAnalogInputEvent& AnalogInputEvent)
|
||||
{
|
||||
if (AnalogInputEvent.GetKey().IsGamepadKey() && InputState->IsGamepadNavigationEnabled())
|
||||
{
|
||||
const FImGuiInputResponse Response = InputHandler->OnGamepadAxis(AnalogInputEvent);
|
||||
if (Response.HasProcessingRequest())
|
||||
{
|
||||
InputState->SetGamepadNavigationAxis(AnalogInputEvent, AnalogInputEvent.GetAnalogValue());
|
||||
}
|
||||
|
||||
return ToSlateReply(Response);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Super::OnAnalogValueChanged(MyGeometry, AnalogInputEvent);
|
||||
}
|
||||
return InputHandler->OnAnalogValueChanged(AnalogInputEvent);
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState->SetMouseDown(MouseEvent, true);
|
||||
return FReply::Handled();
|
||||
return InputHandler->OnMouseButtonDown(MouseEvent).LockMouseToWidget(SharedThis(this));
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState->SetMouseDown(MouseEvent, true);
|
||||
return FReply::Handled();
|
||||
return InputHandler->OnMouseButtonDoubleClick(MouseEvent).LockMouseToWidget(SharedThis(this));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool NeedMouseLock(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
#if FROM_ENGINE_VERSION(4, 20)
|
||||
return FSlateApplication::Get().GetPressedMouseButtons().Num() > 0;
|
||||
#else
|
||||
return MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton) || MouseEvent.IsMouseButtonDown(EKeys::MiddleMouseButton)
|
||||
|| MouseEvent.IsMouseButtonDown(EKeys::RightMouseButton);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState->SetMouseDown(MouseEvent, false);
|
||||
return FReply::Handled();
|
||||
FReply Reply = InputHandler->OnMouseButtonUp(MouseEvent);
|
||||
if (!NeedMouseLock(MouseEvent))
|
||||
{
|
||||
Reply.ReleaseMouseLock();
|
||||
}
|
||||
return Reply;
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState->AddMouseWheelDelta(MouseEvent.GetWheelDelta());
|
||||
return FReply::Handled();
|
||||
return InputHandler->OnMouseWheel(MouseEvent);
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
const FSlateRenderTransform ImGuiToScreen = ImGuiTransform.Concatenate(MyGeometry.GetAccumulatedRenderTransform());
|
||||
InputState->SetMousePosition(ImGuiToScreen.Inverse().TransformPoint(MouseEvent.GetScreenSpacePosition()));
|
||||
return FReply::Handled();
|
||||
return InputHandler->OnMouseMove(ImGuiToScreen.Inverse().TransformPoint(MouseEvent.GetScreenSpacePosition()));
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& FocusEvent)
|
||||
@ -271,9 +198,9 @@ FReply SImGuiWidget::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEv
|
||||
|
||||
IMGUI_WIDGET_LOG(VeryVerbose, TEXT("ImGui Widget %d - Focus Received."), ContextIndex);
|
||||
|
||||
// If widget has a keyboard focus we always maintain mouse input. Technically, if mouse is outside of the widget
|
||||
// area it won't generate events but we freeze its state until it either comes back or input is completely lost.
|
||||
UpdateInputMode(true, IsDirectlyHovered());
|
||||
bForegroundWindow = GameViewport->Viewport->IsForegroundWindow();
|
||||
InputHandler->OnKeyboardInputEnabled();
|
||||
InputHandler->OnGamepadInputEnabled();
|
||||
|
||||
FSlateApplication::Get().ResetToDefaultPointerInputSettings();
|
||||
return FReply::Handled();
|
||||
@ -285,7 +212,8 @@ void SImGuiWidget::OnFocusLost(const FFocusEvent& FocusEvent)
|
||||
|
||||
IMGUI_WIDGET_LOG(VeryVerbose, TEXT("ImGui Widget %d - Focus Lost."), ContextIndex);
|
||||
|
||||
UpdateInputMode(false, IsDirectlyHovered());
|
||||
InputHandler->OnKeyboardInputDisabled();
|
||||
InputHandler->OnGamepadInputDisabled();
|
||||
}
|
||||
|
||||
void SImGuiWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
@ -294,17 +222,7 @@ void SImGuiWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent
|
||||
|
||||
IMGUI_WIDGET_LOG(VeryVerbose, TEXT("ImGui Widget %d - Mouse Enter."), ContextIndex);
|
||||
|
||||
// If mouse enters while input is active then we need to update mouse buttons because there is a chance that we
|
||||
// missed some events.
|
||||
if (InputMode != EInputMode::None)
|
||||
{
|
||||
for (const FKey& Button : { EKeys::LeftMouseButton, EKeys::MiddleMouseButton, EKeys::RightMouseButton, EKeys::ThumbMouseButton, EKeys::ThumbMouseButton2 })
|
||||
{
|
||||
InputState->SetMouseDown(Button, MouseEvent.IsMouseButtonDown(Button));
|
||||
}
|
||||
}
|
||||
|
||||
UpdateInputMode(HasKeyboardFocus(), true);
|
||||
InputHandler->OnMouseInputEnabled();
|
||||
}
|
||||
|
||||
void SImGuiWidget::OnMouseLeave(const FPointerEvent& MouseEvent)
|
||||
@ -313,23 +231,7 @@ void SImGuiWidget::OnMouseLeave(const FPointerEvent& MouseEvent)
|
||||
|
||||
IMGUI_WIDGET_LOG(VeryVerbose, TEXT("ImGui Widget %d - Mouse Leave."), ContextIndex);
|
||||
|
||||
// We don't get any events when application loses focus, but often this is followed by OnMouseLeave, so we can use
|
||||
// this event to immediately disable keyboard input if application lost focus.
|
||||
UpdateInputMode(HasKeyboardFocus() && GameViewport->Viewport->IsForegroundWindow(), false);
|
||||
}
|
||||
|
||||
FCursorReply SImGuiWidget::OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const
|
||||
{
|
||||
EMouseCursor::Type MouseCursor = EMouseCursor::None;
|
||||
if (!bUseSoftwareCursor)
|
||||
{
|
||||
if (FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
|
||||
{
|
||||
MouseCursor = ContextProxy->GetMouseCursor();
|
||||
}
|
||||
}
|
||||
|
||||
return FCursorReply::Cursor(MouseCursor);
|
||||
InputHandler->OnMouseInputDisabled();
|
||||
}
|
||||
|
||||
void SImGuiWidget::CreateInputHandler(const FStringClassReference& HandlerClassReference)
|
||||
@ -361,7 +263,7 @@ void SImGuiWidget::RegisterImGuiSettingsDelegates()
|
||||
}
|
||||
if (!Settings.OnUseSoftwareCursorChanged.IsBoundToObject(this))
|
||||
{
|
||||
Settings.OnUseSoftwareCursorChanged.AddRaw(this, &SImGuiWidget::SetUseSoftwareCursor);
|
||||
Settings.OnUseSoftwareCursorChanged.AddRaw(this, &SImGuiWidget::SetHideMouseCursor);
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,11 +275,13 @@ void SImGuiWidget::UnregisterImGuiSettingsDelegates()
|
||||
Settings.OnUseSoftwareCursorChanged.RemoveAll(this);
|
||||
}
|
||||
|
||||
void SImGuiWidget::CopyModifierKeys(const FInputEvent& InputEvent)
|
||||
void SImGuiWidget::SetHideMouseCursor(bool bHide)
|
||||
{
|
||||
InputState->SetControlDown(InputEvent.IsControlDown());
|
||||
InputState->SetShiftDown(InputEvent.IsShiftDown());
|
||||
InputState->SetAltDown(InputEvent.IsAltDown());
|
||||
if (bHideMouseCursor != bHide)
|
||||
{
|
||||
bHideMouseCursor = bHide;
|
||||
UpdateMouseCursor();
|
||||
}
|
||||
}
|
||||
|
||||
bool SImGuiWidget::IsConsoleOpened() const
|
||||
@ -394,6 +298,19 @@ void SImGuiWidget::UpdateVisibility()
|
||||
ContextIndex, *GetVisibility().ToString());
|
||||
}
|
||||
|
||||
void SImGuiWidget::UpdateMouseCursor()
|
||||
{
|
||||
if (!bHideMouseCursor)
|
||||
{
|
||||
const FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
SetCursor(ContextProxy ? ContextProxy->GetMouseCursor() : EMouseCursor::Default);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCursor(EMouseCursor::None);
|
||||
}
|
||||
}
|
||||
|
||||
ULocalPlayer* SImGuiWidget::GetLocalPlayer() const
|
||||
{
|
||||
if (GameViewport.IsValid())
|
||||
@ -453,28 +370,39 @@ void SImGuiWidget::ReturnFocus()
|
||||
PreviousUserFocusedWidget.Reset();
|
||||
}
|
||||
|
||||
void SImGuiWidget::UpdateInputEnabled()
|
||||
void SImGuiWidget::UpdateInputState()
|
||||
{
|
||||
const bool bEnabled = ModuleManager && ModuleManager->GetProperties().IsInputEnabled();
|
||||
if (bInputEnabled != bEnabled)
|
||||
{
|
||||
IMGUI_WIDGET_LOG(Log, TEXT("ImGui Widget %d - Input Enabled changed to '%s'."),
|
||||
ContextIndex, TEXT_BOOL(bEnabled));
|
||||
|
||||
bInputEnabled = bEnabled;
|
||||
|
||||
IMGUI_WIDGET_LOG(Log, TEXT("ImGui Widget %d - Input Enabled changed to '%s'."),
|
||||
ContextIndex, TEXT_BOOL(bInputEnabled));
|
||||
|
||||
UpdateVisibility();
|
||||
UpdateMouseCursor();
|
||||
|
||||
if (!bInputEnabled)
|
||||
if (bInputEnabled)
|
||||
{
|
||||
// We won't get mouse enter, if viewport is already hovered.
|
||||
if (GameViewport->GetGameViewportWidget()->IsHovered())
|
||||
{
|
||||
InputHandler->OnMouseInputEnabled();
|
||||
}
|
||||
|
||||
// Focus is handled later as it can depend on additional factors.
|
||||
}
|
||||
else
|
||||
{
|
||||
ReturnFocus();
|
||||
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())
|
||||
// We should request a focus, if we are in the input mode and don't have one. But we should wait, if this is not
|
||||
// a foreground window (application), if viewport doesn't have a focus or if console is opened. Note that this
|
||||
// will keep this widget from releasing focus to viewport or other widgets as long as we are in the input mode.
|
||||
if (bInputEnabled && GameViewport->Viewport->IsForegroundWindow() && !HasKeyboardFocus() && !IsConsoleOpened())
|
||||
{
|
||||
const auto& ViewportWidget = GameViewport->GetGameViewportWidget();
|
||||
if (ViewportWidget->HasKeyboardFocus() || ViewportWidget->HasFocusedDescendants())
|
||||
@ -482,52 +410,33 @@ void SImGuiWidget::UpdateInputEnabled()
|
||||
TakeFocus();
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// that.
|
||||
if (bInputEnabled && !GameViewport->Viewport->IsForegroundWindow() && InputMode == EInputMode::Full)
|
||||
{
|
||||
UpdateInputMode(false, IsDirectlyHovered());
|
||||
}
|
||||
|
||||
if (bInputEnabled)
|
||||
{
|
||||
InputState->SetKeyboardNavigationEnabled(ModuleManager && ModuleManager->GetProperties().IsKeyboardNavigationEnabled());
|
||||
InputState->SetGamepadNavigationEnabled(ModuleManager && ModuleManager->GetProperties().IsGamepadNavigationEnabled());
|
||||
const auto& Application = FSlateApplication::Get().GetPlatformApplication();
|
||||
InputState->SetGamepad(Application.IsValid() && Application->IsGamepadAttached());
|
||||
}
|
||||
}
|
||||
|
||||
void SImGuiWidget::UpdateInputMode(bool bHasKeyboardFocus, bool bHasMousePointer)
|
||||
void SImGuiWidget::HandleWindowFocusLost()
|
||||
{
|
||||
const EInputMode NewInputMode =
|
||||
bHasKeyboardFocus ? EInputMode::Full :
|
||||
bHasMousePointer ? EInputMode::MousePointerOnly :
|
||||
EInputMode::None;
|
||||
|
||||
if (InputMode != NewInputMode)
|
||||
// We can use window foreground status to notify about application losing or receiving focus. In some situations
|
||||
// we get mouse leave or enter events, but they are only sent if mouse pointer is inside of the viewport.
|
||||
if (bInputEnabled && HasKeyboardFocus())
|
||||
{
|
||||
IMGUI_WIDGET_LOG(Verbose, TEXT("ImGui Widget %d - Input Mode changed from '%s' to '%s'."),
|
||||
ContextIndex, TEXT_INPUT_MODE(InputMode), TEXT_INPUT_MODE(NewInputMode));
|
||||
|
||||
// We need to reset input components if we are either fully shutting down or we are downgrading from full to
|
||||
// mouse-only input mode.
|
||||
if (NewInputMode == EInputMode::None)
|
||||
if (bForegroundWindow != GameViewport->Viewport->IsForegroundWindow())
|
||||
{
|
||||
InputState->Reset();
|
||||
}
|
||||
else if (InputMode == EInputMode::Full)
|
||||
{
|
||||
InputState->ResetKeyboard();
|
||||
InputState->ResetGamepadNavigation();
|
||||
}
|
||||
bForegroundWindow = !bForegroundWindow;
|
||||
|
||||
InputMode = NewInputMode;
|
||||
IMGUI_WIDGET_LOG(VeryVerbose, TEXT("ImGui Widget %d - Updating input after %s foreground window status."),
|
||||
ContextIndex, bForegroundWindow ? TEXT("getting") : TEXT("losing"));
|
||||
|
||||
if (bForegroundWindow)
|
||||
{
|
||||
InputHandler->OnKeyboardInputEnabled();
|
||||
InputHandler->OnGamepadInputEnabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
InputHandler->OnKeyboardInputDisabled();
|
||||
InputHandler->OnGamepadInputDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InputState->SetMousePointer(bUseSoftwareCursor && bHasMousePointer);
|
||||
}
|
||||
|
||||
void SImGuiWidget::UpdateCanvasControlMode(const FInputEvent& InputEvent)
|
||||
@ -538,6 +447,7 @@ void SImGuiWidget::UpdateCanvasControlMode(const FInputEvent& InputEvent)
|
||||
void SImGuiWidget::OnPostImGuiUpdate()
|
||||
{
|
||||
ImGuiRenderTransform = ImGuiTransform;
|
||||
UpdateMouseCursor();
|
||||
}
|
||||
|
||||
namespace
|
||||
@ -755,6 +665,8 @@ namespace Styles
|
||||
|
||||
void SImGuiWidget::OnDebugDraw()
|
||||
{
|
||||
FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
|
||||
if (CVars::DebugWidget.GetValueOnGameThread() > 0)
|
||||
{
|
||||
bool bDebug = true;
|
||||
@ -766,7 +678,6 @@ void SImGuiWidget::OnDebugDraw()
|
||||
TwoColumns::CollapsingGroup("Context", [&]()
|
||||
{
|
||||
TwoColumns::Value("Context Index", ContextIndex);
|
||||
FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
TwoColumns::Value("Context Name", ContextProxy ? *ContextProxy->GetName() : TEXT("< Null >"));
|
||||
TwoColumns::Value("Game Viewport", *GameViewport->GetName());
|
||||
});
|
||||
@ -774,8 +685,6 @@ void SImGuiWidget::OnDebugDraw()
|
||||
TwoColumns::CollapsingGroup("Input Mode", [&]()
|
||||
{
|
||||
TwoColumns::Value("Input Enabled", bInputEnabled);
|
||||
TwoColumns::Value("Input Mode", TEXT_INPUT_MODE(InputMode));
|
||||
TwoColumns::Value("Input Has Mouse Pointer", InputState->HasMousePointer());
|
||||
});
|
||||
|
||||
TwoColumns::CollapsingGroup("Widget", [&]()
|
||||
@ -807,8 +716,10 @@ void SImGuiWidget::OnDebugDraw()
|
||||
}
|
||||
}
|
||||
|
||||
if (CVars::DebugInput.GetValueOnGameThread() > 0)
|
||||
if (ContextProxy && CVars::DebugInput.GetValueOnGameThread() > 0)
|
||||
{
|
||||
FImGuiInputState& InputState = ContextProxy->GetInputState();
|
||||
|
||||
bool bDebug = true;
|
||||
ImGui::SetNextWindowSize(ImVec2(460, 480), ImGuiSetCond_Once);
|
||||
if (ImGui::Begin("ImGui Input State", &bDebug))
|
||||
@ -832,7 +743,7 @@ void SImGuiWidget::OnDebugDraw()
|
||||
{
|
||||
const FKey& Key = Keys[Idx];
|
||||
const uint32 KeyIndex = ImGuiInterops::GetKeyIndex(Key);
|
||||
Styles::TextHighlight(InputState->GetKeys()[KeyIndex], [&]()
|
||||
Styles::TextHighlight(InputState.GetKeys()[KeyIndex], [&]()
|
||||
{
|
||||
TwoColumns::Value(*Key.GetDisplayName().ToString(), KeyIndex);
|
||||
});
|
||||
@ -847,9 +758,9 @@ void SImGuiWidget::OnDebugDraw()
|
||||
|
||||
Columns::CollapsingGroup("Modifier Keys", 4, [&]()
|
||||
{
|
||||
Styles::TextHighlight(InputState->IsShiftDown(), [&]() { ImGui::Text("Shift"); }); ImGui::NextColumn();
|
||||
Styles::TextHighlight(InputState->IsControlDown(), [&]() { ImGui::Text("Control"); }); ImGui::NextColumn();
|
||||
Styles::TextHighlight(InputState->IsAltDown(), [&]() { ImGui::Text("Alt"); }); ImGui::NextColumn();
|
||||
Styles::TextHighlight(InputState.IsShiftDown(), [&]() { ImGui::Text("Shift"); }); ImGui::NextColumn();
|
||||
Styles::TextHighlight(InputState.IsControlDown(), [&]() { ImGui::Text("Control"); }); ImGui::NextColumn();
|
||||
Styles::TextHighlight(InputState.IsAltDown(), [&]() { ImGui::Text("Alt"); }); ImGui::NextColumn();
|
||||
ImGui::NextColumn();
|
||||
});
|
||||
|
||||
@ -872,7 +783,7 @@ void SImGuiWidget::OnDebugDraw()
|
||||
{
|
||||
const FKey& Button = Buttons[Idx];
|
||||
const uint32 MouseIndex = ImGuiInterops::GetMouseIndex(Button);
|
||||
Styles::TextHighlight(InputState->GetMouseButtons()[MouseIndex], [&]()
|
||||
Styles::TextHighlight(InputState.GetMouseButtons()[MouseIndex], [&]()
|
||||
{
|
||||
TwoColumns::Value(*Button.GetDisplayName().ToString(), MouseIndex);
|
||||
});
|
||||
@ -887,9 +798,9 @@ void SImGuiWidget::OnDebugDraw()
|
||||
|
||||
Columns::CollapsingGroup("Mouse Axes", 4, [&]()
|
||||
{
|
||||
TwoColumns::Value("Position X", InputState->GetMousePosition().X);
|
||||
TwoColumns::Value("Position Y", InputState->GetMousePosition().Y);
|
||||
TwoColumns::Value("Wheel Delta", InputState->GetMouseWheelDelta());
|
||||
TwoColumns::Value("Position X", InputState.GetMousePosition().X);
|
||||
TwoColumns::Value("Position Y", InputState.GetMousePosition().Y);
|
||||
TwoColumns::Value("Wheel Delta", InputState.GetMouseWheelDelta());
|
||||
ImGui::NextColumn(); ImGui::NextColumn();
|
||||
});
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
// Hide ImGui Widget debug in non-developer mode.
|
||||
#define IMGUI_WIDGET_DEBUG IMGUI_MODULE_DEVELOPER
|
||||
|
||||
class FImGuiInputState;
|
||||
class FImGuiModuleManager;
|
||||
class SImGuiCanvasControl;
|
||||
class UImGuiInputHandler;
|
||||
@ -71,42 +70,31 @@ public:
|
||||
|
||||
virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override;
|
||||
|
||||
virtual FCursorReply OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const override;
|
||||
|
||||
private:
|
||||
|
||||
enum class EInputMode : uint8
|
||||
{
|
||||
None,
|
||||
// Mouse pointer only without user focus
|
||||
MousePointerOnly,
|
||||
// Full input with user focus (mouse, keyboard and depending on navigation mode gamepad)
|
||||
Full
|
||||
};
|
||||
|
||||
void CreateInputHandler(const FStringClassReference& HandlerClassReference);
|
||||
void ReleaseInputHandler();
|
||||
|
||||
void SetUseSoftwareCursor(bool bUse) { bUseSoftwareCursor = bUse; }
|
||||
|
||||
void RegisterImGuiSettingsDelegates();
|
||||
void UnregisterImGuiSettingsDelegates();
|
||||
|
||||
FORCEINLINE void CopyModifierKeys(const FInputEvent& InputEvent);
|
||||
void SetHideMouseCursor(bool bHide);
|
||||
|
||||
bool IsConsoleOpened() const;
|
||||
|
||||
// Update visibility based on input enabled state.
|
||||
// Update visibility based on input state.
|
||||
void UpdateVisibility();
|
||||
|
||||
// Update cursor based on input state.
|
||||
void UpdateMouseCursor();
|
||||
|
||||
ULocalPlayer* GetLocalPlayer() const;
|
||||
void TakeFocus();
|
||||
void ReturnFocus();
|
||||
|
||||
void UpdateInputEnabled();
|
||||
|
||||
// Determine new input mode based on hints.
|
||||
void UpdateInputMode(bool bHasKeyboardFocus, bool bHasMousePointer);
|
||||
// Update input state.
|
||||
void UpdateInputState();
|
||||
void HandleWindowFocusLost();
|
||||
|
||||
void UpdateCanvasControlMode(const FInputEvent& InputEvent);
|
||||
|
||||
@ -134,13 +122,9 @@ private:
|
||||
|
||||
int32 ContextIndex = 0;
|
||||
|
||||
FImGuiInputState* InputState;
|
||||
|
||||
EInputMode InputMode = EInputMode::None;
|
||||
bool bInputEnabled = false;
|
||||
|
||||
// Whether or not ImGui should draw its own cursor.
|
||||
bool bUseSoftwareCursor = false;
|
||||
bool bForegroundWindow = false;
|
||||
bool bHideMouseCursor = true;
|
||||
|
||||
TSharedPtr<SImGuiCanvasControl> CanvasControlWidget;
|
||||
TWeakPtr<SWidget> PreviousUserFocusedWidget;
|
||||
|
@ -21,77 +21,9 @@ class FUICommandInfo;
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
|
||||
/** Response used by ImGui Input Handler to communicate input handling requests. */
|
||||
struct IMGUI_API FImGuiInputResponse
|
||||
{
|
||||
/** Create empty response with no requests. */
|
||||
FImGuiInputResponse() = default;
|
||||
|
||||
/**
|
||||
* Create response with custom request configuration.
|
||||
*
|
||||
* @param bInProcess - State of the processing request.
|
||||
* @param bInConsume - State of the consume request.
|
||||
*/
|
||||
FImGuiInputResponse(bool bInProcess, bool bInConsume)
|
||||
: bProcess(bInProcess)
|
||||
, bConsume(bInConsume)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Check whether this response contains processing request.
|
||||
*
|
||||
* @returns True, if processing was requested and false otherwise.
|
||||
*/
|
||||
FORCEINLINE bool HasProcessingRequest() const { return bProcess; }
|
||||
|
||||
/**
|
||||
* Check whether this response contains consume request.
|
||||
*
|
||||
* @returns True, if consume was requested and false otherwise.
|
||||
*/
|
||||
FORCEINLINE bool HasConsumeRequest() const { return bConsume; }
|
||||
|
||||
/**
|
||||
* Set the processing request.
|
||||
*
|
||||
* @param bInProcess - True, to request input processing (implicit) and false otherwise.
|
||||
* @returns Reference to this response (for chaining requests).
|
||||
*/
|
||||
FORCEINLINE FImGuiInputResponse& RequestProcessing(bool bInProcess = true) { bProcess = bInProcess; return *this; }
|
||||
|
||||
/**
|
||||
* Set the consume request.
|
||||
*
|
||||
* @param bInConsume - True, to request input consume (implicit) and false otherwise.
|
||||
* @returns Reference to this response (for chaining requests).
|
||||
*/
|
||||
FORCEINLINE FImGuiInputResponse& RequestConsume(bool bInConsume = true) { bConsume = bInConsume; return *this; }
|
||||
|
||||
private:
|
||||
|
||||
bool bProcess = false;
|
||||
|
||||
bool bConsume = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines behaviour when handling input events. It allows to customize handling of the keyboard and gamepad input,
|
||||
* primarily to support shortcuts in ImGui input mode. Since mouse is not really needed for this functionality and
|
||||
* mouse pointer state and focus are closely connected to input mode, mouse events are left out of this interface.
|
||||
*
|
||||
* When receiving keyboard and gamepad events ImGui Widget calls input handler to query expected behaviour. By default,
|
||||
* with a few exceptions (see @ OnKeyDown) all events are expected to be processed and consumed. Custom implementations
|
||||
* may tweak that behaviour and/or inject custom code.
|
||||
*
|
||||
* Note that returned response is only treated as a hint. In current implementation all consume requests are respected
|
||||
* but to protect from locking ImGui input states, key up events are always processed. Decision about blocking certain
|
||||
* inputs can be taken during key down events and processing corresponding key up events should not make difference.
|
||||
*
|
||||
* Also note that input handler functions are only called when ImGui Widget is receiving input events, what can be for
|
||||
* instance suppressed by opening console.
|
||||
*
|
||||
* See @ Project Settings/Plugins/ImGui/Extensions/ImGuiInputHandlerClass property to set custom implementation.
|
||||
* Handles input and sends it to the input state, which is copied to the ImGui IO at the beginning of the frame.
|
||||
* Implementation of the input handler can be changed in the ImGui project settings by changing ImGuiInputHandlerClass.
|
||||
*/
|
||||
UCLASS()
|
||||
class IMGUI_API UImGuiInputHandler : public UObject
|
||||
@ -101,77 +33,85 @@ class IMGUI_API UImGuiInputHandler : public UObject
|
||||
public:
|
||||
|
||||
/**
|
||||
* Called when handling character events.
|
||||
*
|
||||
* @returns Response with rules how input should be handled. Default implementation contains requests to process
|
||||
* and consume this event.
|
||||
* Called to handle character events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FImGuiInputResponse OnKeyChar(const struct FCharacterEvent& CharacterEvent) { return GetDefaultKeyboardResponse(); }
|
||||
virtual FReply OnKeyChar(const struct FCharacterEvent& CharacterEvent);
|
||||
|
||||
/**
|
||||
* Called when handling keyboard key down events.
|
||||
*
|
||||
* @returns Response with rules how input should be handled. Default implementation contains requests to process
|
||||
* and consume most of the key, but unlike other cases it requests to ignore certain events, like those that are
|
||||
* needed to open console or close PIE session in editor.
|
||||
* Called to handle key down events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FImGuiInputResponse OnKeyDown(const FKeyEvent& KeyEvent);
|
||||
virtual FReply OnKeyDown(const FKeyEvent& KeyEvent);
|
||||
|
||||
/**
|
||||
* Called when handling keyboard key up events.
|
||||
*
|
||||
* Note that regardless of returned response, key up events are always processed by ImGui Widget.
|
||||
*
|
||||
* @returns Response with rules how input should be handled. Default implementation contains requests to consume
|
||||
* this event.
|
||||
* Called to handle key up events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FImGuiInputResponse OnKeyUp(const FKeyEvent& KeyEvent) { return GetDefaultKeyboardResponse(); }
|
||||
virtual FReply OnKeyUp(const FKeyEvent& KeyEvent);
|
||||
|
||||
/**
|
||||
* Called when handling gamepad key down events.
|
||||
*
|
||||
* @returns Response with rules how input should be handled. Default implementation contains requests to process
|
||||
* and consume this event.
|
||||
* Called to handle analog value change events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FImGuiInputResponse OnGamepadKeyDown(const FKeyEvent& GamepadKeyEvent) { return GetDefaultGamepadResponse(); }
|
||||
virtual FReply OnAnalogValueChanged(const FAnalogInputEvent& AnalogInputEvent);
|
||||
|
||||
/**
|
||||
* Called when handling gamepad key up events.
|
||||
*
|
||||
* Note that regardless of returned response, key up events are always processed by ImGui Widget.
|
||||
*
|
||||
* @returns Response with rules how input should be handled. Default implementation contains requests to consume
|
||||
* this event.
|
||||
* Called to handle mouse button down events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FImGuiInputResponse OnGamepadKeyUp(const FKeyEvent& GamepadKeyEvent) { return GetDefaultGamepadResponse(); }
|
||||
virtual FReply OnMouseButtonDown(const FPointerEvent& MouseEvent);
|
||||
|
||||
/**
|
||||
* Called when handling gamepad analog events.
|
||||
*
|
||||
* @returns Response with rules how input should be handled. Default implementation contains requests to process
|
||||
* and consume this event.
|
||||
* Called to handle mouse button double-click events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FImGuiInputResponse OnGamepadAxis(const FAnalogInputEvent& GamepadAxisEvent) { return GetDefaultGamepadResponse(); }
|
||||
virtual FReply OnMouseButtonDoubleClick(const FPointerEvent& MouseEvent);
|
||||
|
||||
/**
|
||||
* Called to handle mouse button up events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnMouseButtonUp(const FPointerEvent& MouseEvent);
|
||||
|
||||
/**
|
||||
* Called to handle mouse wheel events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnMouseWheel(const FPointerEvent& MouseEvent);
|
||||
|
||||
/**
|
||||
* Called to handle mouse move events.
|
||||
* @param Mouse position (in ImGui space)
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnMouseMove(const FVector2D& MousePosition);
|
||||
|
||||
/** Called to handle activation of the keyboard input. */
|
||||
virtual void OnKeyboardInputEnabled();
|
||||
|
||||
/** Called to handle deactivation of the keyboard input. */
|
||||
virtual void OnKeyboardInputDisabled();
|
||||
|
||||
/** Called to handle activation of the gamepad input. */
|
||||
virtual void OnGamepadInputEnabled();
|
||||
|
||||
/** Called to handle deactivation of the gamepad input. */
|
||||
virtual void OnGamepadInputDisabled();
|
||||
|
||||
/** Called to handle activation of the mouse input. */
|
||||
virtual void OnMouseInputEnabled();
|
||||
|
||||
/** Called to handle deactivation of the mouse input. */
|
||||
virtual void OnMouseInputDisabled();
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Get default keyboard response, with consume request based on IsKeyboardInputShared property.
|
||||
*
|
||||
* @returns Default response for keyboard inputs.
|
||||
*/
|
||||
FImGuiInputResponse GetDefaultKeyboardResponse() const;
|
||||
|
||||
/**
|
||||
* Get default gamepad response, with consume request based on IsGamepadInputShared property.
|
||||
*
|
||||
* @returns Default response for gamepad inputs.
|
||||
*/
|
||||
FImGuiInputResponse GetDefaultGamepadResponse() const;
|
||||
/** Copy state of modifier keys to input state. */
|
||||
void CopyModifierKeys(const FInputEvent& InputEvent);
|
||||
|
||||
/**
|
||||
* Checks whether this is a key event that can open console.
|
||||
*
|
||||
* @param KeyEvent - Key event to test.
|
||||
* @returns True, if this key event can open console.
|
||||
*/
|
||||
@ -180,7 +120,6 @@ protected:
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Checks whether this is a key event that can stop PIE session.
|
||||
*
|
||||
* @param KeyEvent - Key event to test.
|
||||
* @returns True, if this key event can stop PIE session.
|
||||
*/
|
||||
@ -189,7 +128,6 @@ protected:
|
||||
|
||||
/**
|
||||
* Checks whether this key event can toggle ImGui input (as defined in settings).
|
||||
*
|
||||
* @param KeyEvent - Key event to test.
|
||||
* @returns True, if this key is bound to 'ImGui.ToggleInput' command that switches ImGui input mode.
|
||||
*/
|
||||
@ -197,15 +135,28 @@ protected:
|
||||
|
||||
/**
|
||||
* Checks whether corresponding ImGui context has an active item (holding cursor focus).
|
||||
*
|
||||
* @returns True, if corresponding context has an active item.
|
||||
*/
|
||||
bool HasImGuiActiveItem() const;
|
||||
|
||||
private:
|
||||
|
||||
void UpdateInputStatePointer();
|
||||
|
||||
void OnSoftwareCursorChanged(bool);
|
||||
|
||||
void OnPostImGuiUpdate();
|
||||
|
||||
void Initialize(FImGuiModuleManager* InModuleManager, UGameViewportClient* InGameViewport, int32 InContextIndex);
|
||||
|
||||
virtual void BeginDestroy() override;
|
||||
|
||||
class FImGuiInputState* InputState = nullptr;
|
||||
|
||||
bool bMouseInputEnabled = false;
|
||||
bool bKeyboardInputEnabled = false;
|
||||
bool bGamepadInputEnabled = false;
|
||||
|
||||
FImGuiModuleManager* ModuleManager = nullptr;
|
||||
|
||||
TWeakObjectPtr<UGameViewportClient> GameViewport;
|
||||
|
Loading…
Reference in New Issue
Block a user