From cf07a05424097318311fccdea6ddbd90e02305c4 Mon Sep 17 00:00:00 2001 From: Arnaud Jamin Date: Tue, 28 Nov 2023 17:08:17 -0500 Subject: [PATCH] CogImGui: Rework input with viewports (still wip) --- Content/Core/Hud/WBP_Menu.uasset | Bin 155708 -> 171719 bytes Content/Core/Input/IMC_Menu.uasset | Bin 3094 -> 3719 bytes .../4/98/HCM4KXTG2Y2G1I0BTX2UNY.uasset | Bin 5766 -> 5766 bytes .../Source/CogImgui/CogImGuiInputProcessor.h | 55 --- .../Private/CogImGuiInputProcessor.cpp | 326 ++---------------- .../CogImgui/Private/CogImguiContext.cpp | 128 ++++++- .../CogImgui/Private/CogImguiInputHelper.cpp | 227 +++++++++++- .../CogImgui/Private/CogImguiWidget.cpp | 226 +++++++++++- .../CogImgui/Public/CogImGuiInputProcessor.h | 18 +- .../Source/CogImgui/Public/CogImguiContext.h | 10 + .../Source/CogImgui/Public/CogImguiHelper.h | 3 + .../CogImgui/Public/CogImguiInputHelper.h | 8 +- .../Source/CogImgui/Public/CogImguiWidget.h | 24 ++ 13 files changed, 632 insertions(+), 393 deletions(-) delete mode 100644 Plugins/Cog/Source/CogImgui/CogImGuiInputProcessor.h diff --git a/Content/Core/Hud/WBP_Menu.uasset b/Content/Core/Hud/WBP_Menu.uasset index a9013810b7a8fdc6acd9c4876e4d0f7eda537278..8970d13e62d102ea00e37dc8e8e4af1c98b9cec9 100644 GIT binary patch literal 171719 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 diff --git a/Content/Core/Input/IMC_Menu.uasset b/Content/Core/Input/IMC_Menu.uasset index b18488abc9cc9da515f8e1e7e86ea33822670030..a4df85e2f6fb591a576b587802286889e09d69f9 100644 GIT binary patch literal 3719 zcmd^CeQZ-z6hDKZxPjmlMZ?tL2#mR|Z)>;Kg^#}WZK*moSBOUc7;oKO_oTEh@4c}V z69XC)(U=fH5~IWzF)>avBS?%IGlPlq1H&K2_<=z|qXN+wvJgb)IrrV$wOhw9`j2|D zcfZc>o_p^3y4~=D%lDog8yj0c6QE=Uz%SSn72$Wfcj&~A>aH`TUntLo-fZ?P!!>I* zz;)Qyw&)vKZ8&YQ+Gxt`b84fl;m%f;>O)>JEW!335`pw+F+c_OTEOe|Hw5Z^K1KD{ zYl_mKs1a`&BvhT(coP?KX$phCR?=Nm^96uf`>2&#T_G^f}cw@n^r@XrDxi$ zw8<0rmzI3*vOdUt2cWN|}Jc6JzvUI&_#$`XtMh=yE;uh_QwF?1WCblAV* zu3f~#G1ue%m|1)o&c(4d93AfK;S0>syLCs06I(`pC?Q0;+vqWvWkbbRy&KT&zy$=$ z1D}&6Xp*(W1HaC`l_SzA24`lzH+LQ)+Pdy#aVLujefio?<+$9g$2aH+c6&OmCymWI zxr1ccrXFWd=47^$nexq#%D0&WwBgmQFK;DFDO39UXJ5zMP}I@br1oc`X|l z*XPhJ&Oy5PTFXcz=$hZI-uNQr#iUGfeP!25`<~o0Saji(8>wu4{3;R^i(7{2@U&d# z+5^r;YgWJ&szBOl@Cu*7=)9$ZG+Q4u#Rows8F%I*hLg zzlPHoMgtCv238%H-i^54-Twr|gYW{_E!!7R42yXyMghk?bMx~ImmxQ%)pK(hPSaXp$x6Jv`ZgjKgJ0l}C;}1)b{=X~v@v zh365eqy(BZMf0mZEg16G*J+y19}I?rnif$T>$OPmFRN7dw^Ujr6ue&azhOmMEXGu1 zBu&JfLmXX)C>KY$Lv$l=0nQ_i@{}kSC*|GbvUp3;N;Dl#?wt-Nb;-s3f%>?oxa|#M zQi}bvR2-0~B2Ps{$e@o1MdisVv{uQHZbXK}xGd)t$dxD9*{+a$!v9|2T-rpusZuO2 zKOe!O-6JkZoAQ{;aiL(=dkUp4ZKqUUgC%kkbL)YYBY~cJQBypeyY!-EFXd19DilUU zm63=JB(jCGu5qUT^b16SFdB9#UH#=<#ne(_&)*Fphv_2 delta 1128 zcmd5)JuE{}6uz(e-qY7A{ZX%4KkpH3MMUhVh0#zQ#7|6Y1_pyPMu|ai!%PgeE+R5SQt*sO^nQTUQOp0 z?hl6cJI(X?rISI-g?U=n@+s4Rr*GVtqGTUnSW*~{3T~d3cK0;LhC`D0nJ3Oi|Kn|g$BY`xMk4ZM~G0)djVcE#qGQ(oi!1@0nhaVkMd11lY$6XDw;i^c-YPpXTR}8 zF+QGEBB91&a)vrxR1}?@%C8ljKPlaz77C-N{j}e9^FDP^xr2V3sr*#U_IfCS@}uog zqEOK-_C%1x;S7-!d@Ni`R1sY!KZsFR6?uNXr3Kro$*Uo(B>a*HrpK~6o^%-!uHgY8 zi%v07D@>kcenJbp>YZk5nU9YAYa4cO)-Ms%7r SlateCursor) override; - - virtual bool HandleKeyDownEvent(FSlateApplication& SlateApp, const FKeyEvent& Event) override; - - virtual bool HandleKeyUpEvent(FSlateApplication& SlateApp, const FKeyEvent& Event) override; - - virtual bool HandleAnalogInputEvent(FSlateApplication& SlateApp, const FAnalogInputEvent& Event) override; - - virtual bool HandleMouseMoveEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) override; - - virtual bool HandleMouseButtonDownEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) override; - - virtual bool HandleMouseButtonUpEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) override; - - virtual bool HandleMouseButtonDoubleClickEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) override; - - virtual bool HandleMouseWheelOrGestureEvent(FSlateApplication& SlateApp, const FPointerEvent& Event, const FPointerEvent* GestureEvent) override; - -protected: - - bool HandleKeyEvent(FSlateApplication& SlateApp, const FKeyEvent& Event, bool IsKeyDown); - - bool HandleMouseButtonEvent(FSlateApplication& SlateApp, const FPointerEvent& Event, bool IsButtonDown); - - bool IsKeyBoundToCommand(const FKeyEvent& KeyEvent); - - static ImGuiKey ToImKey(const FKey& Key); - - static bool IsKeyEventMatchingKeyBind(const FKeyEvent& KeyEvent, const FKeyBind& KeyBind); - - static bool IsConsoleEvent(const FKeyEvent& KeyEvent); - - static bool IsStopPlaySessionEvent(const FKeyEvent& KeyEvent); - - static uint32 ToImGuiMouseButton(const FKey& MouseButton); - - TObjectPtr PlayerInput = nullptr; - - TObjectPtr MainWidget = nullptr; -}; diff --git a/Plugins/Cog/Source/CogImgui/Private/CogImGuiInputProcessor.cpp b/Plugins/Cog/Source/CogImgui/Private/CogImGuiInputProcessor.cpp index fb04382..d79ca3b 100644 --- a/Plugins/Cog/Source/CogImgui/Private/CogImGuiInputProcessor.cpp +++ b/Plugins/Cog/Source/CogImgui/Private/CogImGuiInputProcessor.cpp @@ -4,21 +4,11 @@ #include "CogImguiInputHelper.h" #include "CogImguiWidget.h" #include "CogImguiContext.h" -#include "GameFramework/InputSettings.h" #include "GameFramework/PlayerInput.h" #include "imgui.h" -#include "imgui_internal.h" -#include "Slate/SGameLayerManager.h" - -#if WITH_EDITOR -#include "Kismet2/DebuggerCommands.h" -#endif //WITH_EDITOR - -constexpr bool ForwardEvent = false; -constexpr bool TerminateEvent = true; //-------------------------------------------------------------------------------------------------------------------------- -FImGuiInputProcessor::FImGuiInputProcessor(UPlayerInput* InPlayerInput, FCogImguiContext* InContext, SCogImguiWidget* InMainWidget) +FCogImGuiInputProcessor::FCogImGuiInputProcessor(UPlayerInput* InPlayerInput, FCogImguiContext* InContext, SCogImguiWidget* InMainWidget) { PlayerInput = InPlayerInput; Context = InContext; @@ -26,14 +16,7 @@ FImGuiInputProcessor::FImGuiInputProcessor(UPlayerInput* InPlayerInput, FCogImgu } //-------------------------------------------------------------------------------------------------------------------------- -static FVector2D TransformScreenPointToImGui(const FGeometry& MyGeometry, const FVector2D& Point) -{ - const FSlateRenderTransform ImGuiToScreen = MyGeometry.GetAccumulatedRenderTransform(); - return ImGuiToScreen.Inverse().TransformPoint(Point); -} - -//-------------------------------------------------------------------------------------------------------------------------- -void FImGuiInputProcessor::Tick(const float DeltaTime, FSlateApplication& SlateApp, TSharedRef SlateCursor) +void FCogImGuiInputProcessor::Tick(const float DeltaTime, FSlateApplication& SlateApp, TSharedRef SlateCursor) { ImGuiIO& IO = ImGui::GetIO(); @@ -41,7 +24,7 @@ void FImGuiInputProcessor::Tick(const float DeltaTime, FSlateApplication& SlateA if (bHasGamepad != SlateApp.IsGamepadAttached()) { IO.BackendFlags ^= ImGuiBackendFlags_HasGamepad; - UE_LOG(LogCogImGui, VeryVerbose, TEXT("FImGuiInputProcessor::Tick | HasGamePad Changed")); + UE_LOG(LogCogImGui, VeryVerbose, TEXT("FCogImGuiInputProcessor::Tick | HasGamePad Changed")); } AddMousePosEvent(SlateApp.GetCursorPos()); @@ -56,42 +39,42 @@ void FImGuiInputProcessor::Tick(const float DeltaTime, FSlateApplication& SlateA if (IO.WantSetMousePos) { SlateApp.SetCursorPos(FCogImguiHelper::ToFVector2D(IO.MousePos)); - //UE_LOG(LogCogImGui, VeryVerbose, TEXT("FImGuiInputProcessor::Tick | SetCursorPos")); + //UE_LOG(LogCogImGui, VeryVerbose, TEXT("FCogImGuiInputProcessor::Tick | SetCursorPos")); } } //-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::HandleKeyDownEvent(FSlateApplication& SlateApp, const FKeyEvent& Event) +bool FCogImGuiInputProcessor::HandleKeyDownEvent(FSlateApplication& SlateApp, const FKeyEvent& Event) { return HandleKeyEvent(SlateApp, Event, true); } //-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::HandleKeyUpEvent(FSlateApplication& SlateApp, const FKeyEvent& Event) +bool FCogImGuiInputProcessor::HandleKeyUpEvent(FSlateApplication& SlateApp, const FKeyEvent& Event) { return HandleKeyEvent(SlateApp, Event, false); } //-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::HandleKeyEvent(FSlateApplication& SlateApp, const FKeyEvent& Event, bool IsKeyDown) +bool FCogImGuiInputProcessor::HandleKeyEvent(FSlateApplication& SlateApp, const FKeyEvent& Event, bool IsKeyDown) { //------------------------------------------------------------------------------------------------ // We want the user to be able to open the console command when imgui has the input. //------------------------------------------------------------------------------------------------ - if (IsConsoleEvent(Event)) + if (FCogImguiInputHelper::IsConsoleEvent(Event)) { const bool Result = ForwardEvent; - UE_LOG(LogCogImGui, VeryVerbose, TEXT("FImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | TerminateEvent:%d | ConsoleEvent"), *Event.GetKey().ToString(), IsKeyDown, Result); + UE_LOG(LogCogImGui, VeryVerbose, TEXT("FCogImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | TerminateEvent:%d | ConsoleEvent"), *Event.GetKey().ToString(), IsKeyDown, Result); return Result; } //------------------------------------------------------------------------------------------------ // We want the user to be able to stop its session by pressing Esc, even when imgui has the input //------------------------------------------------------------------------------------------------ - if (IsStopPlaySessionEvent(Event)) + if (FCogImguiInputHelper::IsStopPlaySessionEvent(Event)) { const bool Result = ForwardEvent; - UE_LOG(LogCogImGui, VeryVerbose, TEXT("FImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | TerminateEvent:%d | StopPlaySessionEvent"), *Event.GetKey().ToString(), IsKeyDown, Result); + UE_LOG(LogCogImGui, VeryVerbose, TEXT("FCogImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | TerminateEvent:%d | StopPlaySessionEvent"), *Event.GetKey().ToString(), IsKeyDown, Result); return Result; } @@ -100,16 +83,16 @@ bool FImGuiInputProcessor::HandleKeyEvent(FSlateApplication& SlateApp, const FKe // We actually use a console command to toggle the input from the game to imgui, and other // windows command such as LoadLayout. //------------------------------------------------------------------------------------------------ - if (IsKeyBoundToCommand(Event)) + if (FCogImguiInputHelper::IsKeyBoundToCommand(PlayerInput, Event)) { const bool Result = ForwardEvent; - UE_LOG(LogCogImGui, VeryVerbose, TEXT("FImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | TerminateEvent:%d | KeyBoundToCommand"), *Event.GetKey().ToString(), IsKeyDown, Result); + UE_LOG(LogCogImGui, VeryVerbose, TEXT("FCogImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | TerminateEvent:%d | KeyBoundToCommand"), *Event.GetKey().ToString(), IsKeyDown, Result); return Result; } ImGuiIO& IO = ImGui::GetIO(); - IO.AddKeyEvent(ToImKey(Event.GetKey()), IsKeyDown); + IO.AddKeyEvent(FCogImguiInputHelper::ToImKey(Event.GetKey()), IsKeyDown); const FModifierKeysState& ModifierKeys = Event.GetModifierKeys(); IO.AddKeyEvent(ImGuiMod_Ctrl, ModifierKeys.IsControlDown()); @@ -128,7 +111,7 @@ bool FImGuiInputProcessor::HandleKeyEvent(FSlateApplication& SlateApp, const FKe if (IsModifierKey) { const bool Result = ForwardEvent; - UE_LOG(LogCogImGui, VeryVerbose, TEXT("FImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | TerminateEvent:%d | IsModifierKey"), *Event.GetKey().ToString(), IsKeyDown, Result); + UE_LOG(LogCogImGui, VeryVerbose, TEXT("FCogImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | TerminateEvent:%d | IsModifierKey"), *Event.GetKey().ToString(), IsKeyDown, Result); return Result; } @@ -137,23 +120,23 @@ bool FImGuiInputProcessor::HandleKeyEvent(FSlateApplication& SlateApp, const FKe if (IO.WantCaptureKeyboard && (IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad)) { const bool Result = TerminateEvent; - UE_LOG(LogCogImGui, VeryVerbose, TEXT("FImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | TerminateEvent:%d | NavEnableGamepad"), *Event.GetKey().ToString(), IsKeyDown, Result); + UE_LOG(LogCogImGui, VeryVerbose, TEXT("FCogImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | TerminateEvent:%d | NavEnableGamepad"), *Event.GetKey().ToString(), IsKeyDown, Result); return Result; } } const bool Result = IO.WantCaptureKeyboard ? TerminateEvent : ForwardEvent; - UE_LOG(LogCogImGui, VeryVerbose, TEXT("FImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | WantCaptureKeyboard:%d | TerminateEvent:%d"), *Event.GetKey().ToString(), IsKeyDown, IO.WantCaptureKeyboard, Result); + UE_LOG(LogCogImGui, VeryVerbose, TEXT("FCogImGuiInputProcessor::HandleKeyEvent | Key:%s | IsKeyDown:%d | WantCaptureKeyboard:%d | TerminateEvent:%d"), *Event.GetKey().ToString(), IsKeyDown, IO.WantCaptureKeyboard, Result); return Result; } //-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::HandleAnalogInputEvent(FSlateApplication& SlateApp, const FAnalogInputEvent& Event) +bool FCogImGuiInputProcessor::HandleAnalogInputEvent(FSlateApplication& SlateApp, const FAnalogInputEvent& Event) { const float Value = Event.GetAnalogValue(); ImGuiIO& IO = ImGui::GetIO(); - IO.AddKeyAnalogEvent(ToImKey(Event.GetKey()), FMath::Abs(Value) > 0.0f, Value); + IO.AddKeyAnalogEvent(FCogImguiInputHelper::ToImKey(Event.GetKey()), FMath::Abs(Value) > 0.0f, Value); if (Event.GetKey().IsGamepadKey()) { @@ -176,11 +159,11 @@ bool FImGuiInputProcessor::HandleAnalogInputEvent(FSlateApplication& SlateApp, c } //-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::HandleMouseMoveEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) +bool FCogImGuiInputProcessor::HandleMouseMoveEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) { AddMousePosEvent(Event.GetScreenSpacePosition()); - if (Context->GetEnableInput() && Context->GetShareMouse() == false && IsMouseInsideMainViewport()) + if (Context->GetEnableInput() && Context->GetShareMouse() == false && FCogImguiInputHelper::IsMouseInsideMainViewport()) { return TerminateEvent; } @@ -191,44 +174,44 @@ bool FImGuiInputProcessor::HandleMouseMoveEvent(FSlateApplication& SlateApp, con } //-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::HandleMouseButtonDownEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) +bool FCogImGuiInputProcessor::HandleMouseButtonDownEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) { return HandleMouseButtonEvent(SlateApp, Event, true); } //-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::HandleMouseButtonUpEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) +bool FCogImGuiInputProcessor::HandleMouseButtonUpEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) { return HandleMouseButtonEvent(SlateApp, Event, false); } //-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::HandleMouseButtonDoubleClickEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) +bool FCogImGuiInputProcessor::HandleMouseButtonDoubleClickEvent(FSlateApplication& SlateApp, const FPointerEvent& Event) { return HandleMouseButtonEvent(SlateApp, Event, true); } //-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::HandleMouseButtonEvent(FSlateApplication& SlateApp, const FPointerEvent& Event, bool IsButtonDown) +bool FCogImGuiInputProcessor::HandleMouseButtonEvent(FSlateApplication& SlateApp, const FPointerEvent& Event, bool IsButtonDown) { ImGuiIO& IO = ImGui::GetIO(); - const uint32 Button = ToImGuiMouseButton(Event.GetEffectingButton()); + const uint32 Button = FCogImguiInputHelper::ToImGuiMouseButton(Event.GetEffectingButton()); IO.AddMouseButtonEvent(Button, IsButtonDown); - if (Context->GetEnableInput() && Context->GetShareMouse() == false && IsMouseInsideMainViewport()) + if (Context->GetEnableInput() && Context->GetShareMouse() == false && FCogImguiInputHelper::IsMouseInsideMainViewport()) { const bool Result = TerminateEvent; - UE_LOG(LogCogImGui, VeryVerbose, TEXT("FImGuiInputProcessor::HandleMouseButtonEvent | Button:%d | IsButtonDown:%d | WantCaptureMouse:%d | TerminateEvent:%d | ShareMouse == false"), Button, IsButtonDown, IO.WantCaptureMouse, Result); + UE_LOG(LogCogImGui, VeryVerbose, TEXT("FCogImGuiInputProcessor::HandleMouseButtonEvent | Button:%d | IsButtonDown:%d | WantCaptureMouse:%d | TerminateEvent:%d | ShareMouse == false"), Button, IsButtonDown, IO.WantCaptureMouse, Result); return Result; } const bool Result = IO.WantCaptureMouse ? TerminateEvent : ForwardEvent; - UE_LOG(LogCogImGui, VeryVerbose, TEXT("FImGuiInputProcessor::HandleMouseButtonEvent | Button:%d | IsButtonDown:%d | WantCaptureMouse:%d | TerminateEvent:%d"), Button, IsButtonDown, IO.WantCaptureMouse, Result); + UE_LOG(LogCogImGui, VeryVerbose, TEXT("FCogImGuiInputProcessor::HandleMouseButtonEvent | Button:%d | IsButtonDown:%d | WantCaptureMouse:%d | TerminateEvent:%d"), Button, IsButtonDown, IO.WantCaptureMouse, Result); return Result; } //-------------------------------------------------------------------------------------------------------------------------- -void FImGuiInputProcessor::AddMousePosEvent(const FVector2D& MousePosition) const +void FCogImGuiInputProcessor::AddMousePosEvent(const FVector2D& MousePosition) const { ImGuiIO& IO = ImGui::GetIO(); if (IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) @@ -243,20 +226,7 @@ void FImGuiInputProcessor::AddMousePosEvent(const FVector2D& MousePosition) cons } //-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::IsMouseInsideMainViewport() -{ - if (ImGuiViewportP* Viewport = (ImGuiViewportP*)ImGui::GetMainViewport()) - { - ImGuiIO& IO = ImGui::GetIO(); - const bool Result = Viewport->GetMainRect().Contains(IO.MousePos); - return Result; - } - - return false; -} - -//-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::HandleMouseWheelOrGestureEvent(FSlateApplication& SlateApp, const FPointerEvent& Event, const FPointerEvent* GestureEvent) +bool FCogImGuiInputProcessor::HandleMouseWheelOrGestureEvent(FSlateApplication& SlateApp, const FPointerEvent& Event, const FPointerEvent* GestureEvent) { ImGuiIO& IO = ImGui::GetIO(); @@ -264,237 +234,3 @@ bool FImGuiInputProcessor::HandleMouseWheelOrGestureEvent(FSlateApplication& Sla return IO.WantCaptureMouse; } - -//-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::IsKeyEventMatchingKeyBind(const FKeyEvent& KeyEvent, const FKeyBind& KeyBind) -{ - if (KeyBind.bDisabled) - { - return false; - } - - if (KeyBind.Key != KeyEvent.GetKey()) - { - return false; - } - - const bool bControlPressed = KeyEvent.IsControlDown(); - const bool bShiftPressed = KeyEvent.IsShiftDown(); - const bool bAltPressed = KeyEvent.IsAltDown(); - const bool bCmdPressed = KeyEvent.IsCommandDown(); - - if ((!KeyBind.Control || bControlPressed) - && (!KeyBind.Shift || bShiftPressed) - && (!KeyBind.Alt || bAltPressed) - && (!KeyBind.Cmd || bCmdPressed) - && (!KeyBind.bIgnoreCtrl || !bControlPressed) - && (!KeyBind.bIgnoreShift || !bShiftPressed) - && (!KeyBind.bIgnoreAlt || !bAltPressed) - && (!KeyBind.bIgnoreCmd || !bCmdPressed)) - { - return true; - } - - return false; -} - -//-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::IsKeyBoundToCommand(const FKeyEvent& KeyEvent) -{ - if (PlayerInput == nullptr) - { - return false; - } - - for (const FKeyBind& KeyBind : PlayerInput->DebugExecBindings) - { - if (IsKeyEventMatchingKeyBind(KeyEvent, KeyBind)) - { - return true; - } - } - - return false; -} - -//-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::IsConsoleEvent(const FKeyEvent& KeyEvent) -{ - const bool bModifierDown = KeyEvent.IsControlDown() || KeyEvent.IsShiftDown() || KeyEvent.IsAltDown() || KeyEvent.IsCommandDown(); - const bool Result = !bModifierDown && GetDefault()->ConsoleKeys.Contains(KeyEvent.GetKey()); - return Result; -} - -//-------------------------------------------------------------------------------------------------------------------------- -bool FImGuiInputProcessor::IsStopPlaySessionEvent(const FKeyEvent& KeyEvent) -{ -#if WITH_EDITOR - static TSharedPtr StopPlaySessionCommandInfo = FInputBindingManager::Get().FindCommandInContext("PlayWorld", "StopPlaySession"); - - if (StopPlaySessionCommandInfo.IsValid()) - { - const FInputChord InputChord(KeyEvent.GetKey(), KeyEvent.IsShiftDown(), KeyEvent.IsControlDown(), KeyEvent.IsAltDown(), KeyEvent.IsCommandDown()); - const bool bHasActiveChord = StopPlaySessionCommandInfo->HasActiveChord(InputChord); - return bHasActiveChord && FPlayWorldCommands::GlobalPlayWorldActions->CanExecuteAction(StopPlaySessionCommandInfo.ToSharedRef()); - } -#endif // WITH_EDITOR - - return false; -} - -//-------------------------------------------------------------------------------------------------------------------------- -uint32 FImGuiInputProcessor::ToImGuiMouseButton(const FKey& MouseButton) -{ - if (MouseButton == EKeys::LeftMouseButton) { return 0; } - if (MouseButton == EKeys::RightMouseButton) { return 1; } - if (MouseButton == EKeys::MiddleMouseButton) { return 2; } - if (MouseButton == EKeys::ThumbMouseButton) { return 3; } - if (MouseButton == EKeys::ThumbMouseButton2) { return 4; } - - return -1; -} - -//-------------------------------------------------------------------------------------------------------------------------- -ImGuiKey FImGuiInputProcessor::ToImKey(const FKey& Key) -{ - static const TMap LookupMap = { - { EKeys::Tab, ImGuiKey_Tab }, - - { EKeys::Left, ImGuiKey_LeftArrow }, - { EKeys::Right, ImGuiKey_RightArrow }, - { EKeys::Up, ImGuiKey_UpArrow }, - { EKeys::Down, ImGuiKey_DownArrow }, - - { EKeys::PageUp, ImGuiKey_PageUp }, - { EKeys::PageDown, ImGuiKey_PageDown }, - { EKeys::Home, ImGuiKey_Home }, - { EKeys::End, ImGuiKey_End }, - { EKeys::Insert, ImGuiKey_Insert }, - { EKeys::Delete, ImGuiKey_Delete }, - - { EKeys::BackSpace, ImGuiKey_Backspace }, - { EKeys::SpaceBar, ImGuiKey_Space }, - { EKeys::Enter, ImGuiKey_Enter }, - { EKeys::Escape, ImGuiKey_Escape }, - - { EKeys::LeftControl, ImGuiKey_LeftCtrl }, - { EKeys::LeftShift, ImGuiKey_LeftShift }, - { EKeys::LeftAlt, ImGuiKey_LeftAlt }, - { EKeys::LeftCommand, ImGuiKey_LeftSuper }, - { EKeys::RightControl, ImGuiKey_RightCtrl }, - { EKeys::RightShift, ImGuiKey_RightShift }, - { EKeys::RightAlt, ImGuiKey_RightAlt }, - { EKeys::RightCommand, ImGuiKey_RightSuper }, - - { EKeys::Zero, ImGuiKey_0 }, - { EKeys::One, ImGuiKey_1 }, - { EKeys::Two, ImGuiKey_2 }, - { EKeys::Three, ImGuiKey_3 }, - { EKeys::Four, ImGuiKey_4 }, - { EKeys::Five, ImGuiKey_5 }, - { EKeys::Six, ImGuiKey_6 }, - { EKeys::Seven, ImGuiKey_7 }, - { EKeys::Eight, ImGuiKey_8 }, - { EKeys::Nine, ImGuiKey_9 }, - - { EKeys::A, ImGuiKey_A }, - { EKeys::B, ImGuiKey_B }, - { EKeys::C, ImGuiKey_C }, - { EKeys::D, ImGuiKey_D }, - { EKeys::E, ImGuiKey_E }, - { EKeys::F, ImGuiKey_F }, - { EKeys::G, ImGuiKey_G }, - { EKeys::H, ImGuiKey_H }, - { EKeys::I, ImGuiKey_I }, - { EKeys::J, ImGuiKey_J }, - { EKeys::K, ImGuiKey_K }, - { EKeys::L, ImGuiKey_L }, - { EKeys::M, ImGuiKey_M }, - { EKeys::N, ImGuiKey_N }, - { EKeys::O, ImGuiKey_O }, - { EKeys::P, ImGuiKey_P }, - { EKeys::Q, ImGuiKey_Q }, - { EKeys::R, ImGuiKey_R }, - { EKeys::S, ImGuiKey_S }, - { EKeys::T, ImGuiKey_T }, - { EKeys::U, ImGuiKey_U }, - { EKeys::V, ImGuiKey_V }, - { EKeys::W, ImGuiKey_W }, - { EKeys::X, ImGuiKey_X }, - { EKeys::Y, ImGuiKey_Y }, - { EKeys::Z, ImGuiKey_Z }, - - { EKeys::F1, ImGuiKey_F1 }, - { EKeys::F2, ImGuiKey_F2 }, - { EKeys::F3, ImGuiKey_F3 }, - { EKeys::F4, ImGuiKey_F4 }, - { EKeys::F5, ImGuiKey_F5 }, - { EKeys::F6, ImGuiKey_F6 }, - { EKeys::F7, ImGuiKey_F7 }, - { EKeys::F8, ImGuiKey_F8 }, - { EKeys::F9, ImGuiKey_F9 }, - { EKeys::F10, ImGuiKey_F10 }, - { EKeys::F11, ImGuiKey_F11 }, - { EKeys::F12, ImGuiKey_F12 }, - - { EKeys::Apostrophe, ImGuiKey_Apostrophe }, - { EKeys::Comma, ImGuiKey_Comma }, - { EKeys::Period, ImGuiKey_Period }, - { EKeys::Slash, ImGuiKey_Slash }, - { EKeys::Semicolon, ImGuiKey_Semicolon }, - { EKeys::LeftBracket, ImGuiKey_LeftBracket }, - { EKeys::Backslash, ImGuiKey_Backslash }, - { EKeys::RightBracket, ImGuiKey_RightBracket }, - - { EKeys::CapsLock, ImGuiKey_CapsLock }, - { EKeys::ScrollLock, ImGuiKey_ScrollLock }, - { EKeys::NumLock, ImGuiKey_NumLock }, - { EKeys::Pause, ImGuiKey_Pause }, - - { EKeys::NumPadZero, ImGuiKey_Keypad0 }, - { EKeys::NumPadOne, ImGuiKey_Keypad1 }, - { EKeys::NumPadTwo, ImGuiKey_Keypad2 }, - { EKeys::NumPadThree, ImGuiKey_Keypad3 }, - { EKeys::NumPadFour, ImGuiKey_Keypad4 }, - { EKeys::NumPadFive, ImGuiKey_Keypad5 }, - { EKeys::NumPadSix, ImGuiKey_Keypad6 }, - { EKeys::NumPadSeven, ImGuiKey_Keypad7 }, - { EKeys::NumPadEight, ImGuiKey_Keypad8 }, - { EKeys::NumPadNine, ImGuiKey_Keypad9 }, - - { EKeys::Decimal, ImGuiKey_KeypadDecimal }, - { EKeys::Divide, ImGuiKey_KeypadDivide }, - { EKeys::Multiply, ImGuiKey_KeypadMultiply }, - { EKeys::Subtract, ImGuiKey_KeypadSubtract }, - { EKeys::Add, ImGuiKey_KeypadAdd }, - { EKeys::Equals, ImGuiKey_KeypadEqual }, - - { EKeys::Gamepad_Special_Right, ImGuiKey_GamepadStart }, - { EKeys::Gamepad_Special_Left, ImGuiKey_GamepadBack }, - { EKeys::Gamepad_FaceButton_Left, ImGuiKey_GamepadFaceLeft }, - { EKeys::Gamepad_FaceButton_Right, ImGuiKey_GamepadFaceRight }, - { EKeys::Gamepad_FaceButton_Top, ImGuiKey_GamepadFaceUp }, - { EKeys::Gamepad_FaceButton_Bottom, ImGuiKey_GamepadFaceDown }, - { EKeys::Gamepad_DPad_Left, ImGuiKey_GamepadDpadLeft }, - { EKeys::Gamepad_DPad_Right, ImGuiKey_GamepadDpadRight }, - { EKeys::Gamepad_DPad_Up, ImGuiKey_GamepadDpadUp }, - { EKeys::Gamepad_DPad_Down, ImGuiKey_GamepadDpadDown }, - { EKeys::Gamepad_LeftShoulder, ImGuiKey_GamepadL1 }, - { EKeys::Gamepad_RightShoulder, ImGuiKey_GamepadR1 }, - { EKeys::Gamepad_LeftTrigger, ImGuiKey_GamepadL2 }, - { EKeys::Gamepad_RightTrigger, ImGuiKey_GamepadR2 }, - { EKeys::Gamepad_LeftThumbstick, ImGuiKey_GamepadL3 }, - { EKeys::Gamepad_RightThumbstick, ImGuiKey_GamepadR3 }, - { EKeys::Gamepad_LeftStick_Left, ImGuiKey_GamepadLStickLeft }, - { EKeys::Gamepad_LeftStick_Right, ImGuiKey_GamepadLStickRight }, - { EKeys::Gamepad_LeftStick_Up, ImGuiKey_GamepadLStickUp }, - { EKeys::Gamepad_LeftStick_Down, ImGuiKey_GamepadLStickDown }, - { EKeys::Gamepad_RightStick_Left, ImGuiKey_GamepadRStickLeft }, - { EKeys::Gamepad_RightStick_Right, ImGuiKey_GamepadRStickRight }, - { EKeys::Gamepad_RightStick_Up, ImGuiKey_GamepadRStickUp }, - { EKeys::Gamepad_RightStick_Down, ImGuiKey_GamepadRStickDown } - }; - - const ImGuiKey* Result = LookupMap.Find(Key); - return (Result != nullptr) ? *Result : ImGuiKey_None; -} diff --git a/Plugins/Cog/Source/CogImgui/Private/CogImguiContext.cpp b/Plugins/Cog/Source/CogImgui/Private/CogImguiContext.cpp index c00e9ba..e391b59 100644 --- a/Plugins/Cog/Source/CogImgui/Private/CogImguiContext.cpp +++ b/Plugins/Cog/Source/CogImgui/Private/CogImguiContext.cpp @@ -1,5 +1,6 @@ #include "CogImGuiContext.h" +#include "Application/ThrottleManager.h" #include "CogImguiHelper.h" #include "CogImguiInputHelper.h" #include "CogImguiInputProcessor.h" @@ -40,12 +41,12 @@ void FCogImguiContext::Initialize() //-------------------------------------------------------------------- // Register input processor to forward input events to imgui //-------------------------------------------------------------------- - if (FSlateApplication::IsInitialized()) - { - UPlayerInput* PlayerInput = FCogImguiInputHelper::GetPlayerInput(*GameViewport->GetWorld()); - InputProcessor = MakeShared(PlayerInput, this, MainWidget.Get()); - FSlateApplication::Get().RegisterInputPreProcessor(InputProcessor.ToSharedRef(), 0); - } + //if (FSlateApplication::IsInitialized()) + //{ + // UPlayerInput* PlayerInput = FCogImguiInputHelper::GetPlayerInput(*GameViewport->GetWorld()); + // InputProcessor = MakeShared(PlayerInput, this, MainWidget.Get()); + // FSlateApplication::Get().RegisterInputPreProcessor(InputProcessor.ToSharedRef(), 0); + //} ImGuiContext = ImGui::CreateContext(); PlotContext = ImPlot::CreateContext(); @@ -103,6 +104,7 @@ void FCogImguiContext::Initialize() PlatformApplication->OnDisplayMetricsChanged().AddRaw(this, &FCogImguiContext::OnDisplayMetricsChanged); OnDisplayMetricsChanged(DisplayMetrics); } + } //-------------------------------------------------------------------------------------------------------------------------- @@ -119,10 +121,10 @@ void FCogImguiContext::Shutdown() { FSlateApplication& SlateApp = FSlateApplication::Get(); - if (InputProcessor.IsValid()) - { - SlateApp.UnregisterInputPreProcessor(InputProcessor); - } + //if (InputProcessor.IsValid()) + //{ + // SlateApp.UnregisterInputPreProcessor(InputProcessor); + //} if (const TSharedPtr PlatformApplication = SlateApp.GetPlatformApplication()) { @@ -228,14 +230,14 @@ void FCogImguiContext::BeginFrame(float InDeltaTime) if (bEnableInput) { IO.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; - - TryReleaseGameMouseCapture(); } else { IO.ConfigFlags |= ImGuiConfigFlags_NoMouse; } + TickFocus(); + //------------------------------------------------------------------------------------------------------- // //------------------------------------------------------------------------------------------------------- @@ -246,6 +248,20 @@ void FCogImguiContext::BeginFrame(float InDeltaTime) MainWidget->SetCursor(FCogImguiInputHelper::ToSlateMouseCursor(ImGui::GetMouseCursor())); } + //------------------------------------------------------------------------------------------------------- + // + //------------------------------------------------------------------------------------------------------- + const FVector2D& MousePosition = SlateApp.GetCursorPos(); + if (IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + IO.AddMousePosEvent(MousePosition.X, MousePosition.Y); + } + else + { + const FVector2D TransformedMousePosition = MousePosition - MainWidget->GetTickSpaceGeometry().GetAbsolutePosition(); + IO.AddMousePosEvent(TransformedMousePosition.X, TransformedMousePosition.Y); + } + //------------------------------------------------------------------------------------------------------- // //------------------------------------------------------------------------------------------------------- @@ -260,6 +276,7 @@ void FCogImguiContext::BeginFrame(float InDeltaTime) NewStyle.ScaleAllSizes(DpiScale); } + ImGui::NewFrame(); //DrawDebug(); @@ -313,7 +330,8 @@ void FCogImguiContext::ImGui_CreateWindow(ImGuiViewport* Viewport) const TSharedRef Window = SNew(SWindow) - .Type(bTooltipWindow ? EWindowType::ToolTip : EWindowType::Normal) + //.Type(bTooltipWindow ? EWindowType::ToolTip : EWindowType::Normal) + .Type(EWindowType::ToolTip) .Style(&WindowStyle) .ScreenPosition(FCogImguiHelper::ToFVector2D(Viewport->Pos)) .ClientSize(FCogImguiHelper::ToFVector2D(Viewport->Size)) @@ -343,6 +361,8 @@ void FCogImguiContext::ImGui_CreateWindow(ImGuiViewport* Viewport) FSlateApplication::Get().AddWindow(Window); } + Widget->SetWindow(Window); + FImGuiViewportData* ViewportData = new FImGuiViewportData(); Viewport->PlatformUserData = ViewportData; ViewportData->Context = ParentViewportData->Context; @@ -559,10 +579,15 @@ void FCogImguiContext::SetEnableInput(bool Value) { bEnableInput = Value; + FSlateThrottleManager::Get().DisableThrottle(bEnableInput); + if (bEnableInput == false) { TryGiveMouseCaptureBackToGame(); } + else + { + } } //-------------------------------------------------------------------------------------------------------------------------- @@ -718,3 +743,80 @@ ULocalPlayer* FCogImguiContext::GetLocalPlayer() const ULocalPlayer* LocalPlayer = World->GetFirstLocalPlayerFromController(); return LocalPlayer; } + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogImguiContext::TickFocus() +{ + const bool bShouldEnableInput = bEnableInput; + if (bEnableInput != bShouldEnableInput) + { + bEnableInput = bShouldEnableInput; + + if (bEnableInput) + { + TakeFocus(); + } + else + { + ReturnFocus(); + } + } + else if (bEnableInput) + { + const auto& ViewportWidget = GameViewport->GetGameViewportWidget(); + if (!MainWidget->HasKeyboardFocus() && !IsConsoleOpened() && (ViewportWidget->HasKeyboardFocus() || ViewportWidget->HasFocusedDescendants())) + { + TakeFocus(); + } + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogImguiContext::TakeFocus() +{ + FSlateApplication& SlateApplication = FSlateApplication::Get(); + + PreviousMouseCaptor = SlateApplication.GetUserFocusedWidget(SlateApplication.GetUserIndexForKeyboard()); + + if (ULocalPlayer* LocalPlayer = GetLocalPlayer()) + { + TSharedRef FocusWidget = MainWidget->AsShared(); + LocalPlayer->GetSlateOperations().CaptureMouse(FocusWidget); + LocalPlayer->GetSlateOperations().SetUserFocus(FocusWidget); + } + else + { + SlateApplication.SetKeyboardFocus(MainWidget->AsShared()); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogImguiContext::ReturnFocus() +{ + //if (MainWidget->HasKeyboardFocus()) + //{ + // auto FocusWidgetPtr = PreviousMouseCaptor.IsValid() + // ? PreviousMouseCaptor.Pin() + // : GameViewport->GetGameViewportWidget(); + + // if (ULocalPlayer* LocalPlayer = GetLocalPlayer()) + // { + // auto FocusWidgetRef = FocusWidgetPtr.ToSharedRef(); + + // if (FocusWidgetPtr == GameViewport->GetGameViewportWidget()) + // { + // LocalPlayer->GetSlateOperations().CaptureMouse(FocusWidgetRef); + // } + + // LocalPlayer->GetSlateOperations().SetUserFocus(FocusWidgetRef); + // } + // else + // { + // FSlateApplication& SlateApplication = FSlateApplication::Get(); + // SlateApplication.ResetToDefaultPointerInputSettings(); + // SlateApplication.SetUserFocus(SlateApplication.GetUserIndexForKeyboard(), FocusWidgetPtr); + // } + //} + + //PreviousMouseCaptor.Reset(); +} diff --git a/Plugins/Cog/Source/CogImgui/Private/CogImguiInputHelper.cpp b/Plugins/Cog/Source/CogImgui/Private/CogImguiInputHelper.cpp index 0947109..f7cf4af 100644 --- a/Plugins/Cog/Source/CogImgui/Private/CogImguiInputHelper.cpp +++ b/Plugins/Cog/Source/CogImgui/Private/CogImguiInputHelper.cpp @@ -1,5 +1,6 @@ #include "CogImguiInputHelper.h" +#include "CogImGuiInputProcessor.h" #include "CogImguiModule.h" #include "Framework/Commands/UICommandInfo.h" #include "GameFramework/GameUserSettings.h" @@ -64,7 +65,7 @@ bool FCogImguiInputHelper::IsKeyEventHandled(UWorld* World, const FKeyEvent& Key // If we receive a key modifier, we want to let others systems know about it. // Otherwise, the console command bindings that are bound to something like CTRL+Key // won't work, even if we let the KeyEvent pass with 'IsKeyBoundToCommand' below. - // It seems the command binings system needs to know about the modifier key press event itself, + // It seems the command bindings system needs to know about the modifier key press event itself, // and not the Key+Modifier event. // We update ImGui modifier keys in SCogImguiWidget::TickKeyModifiers(). //------------------------------------------------------------------------------------------------ @@ -74,7 +75,7 @@ bool FCogImguiInputHelper::IsKeyEventHandled(UWorld* World, const FKeyEvent& Key } //------------------------------------------------------------------------------------------------ - // We want the user to be able to use command bingings, even when imgui has the input. + // We want the user to be able to use command bindings, even when imgui has the input. // We actually use a console command to toggle the input from the game to imgui, and other // windows command such as LoadLayout. //------------------------------------------------------------------------------------------------ @@ -163,15 +164,6 @@ void FCogImguiInputHelper::KeyInfoToKeyBind(const FCogImGuiKeyInfo& KeyInfo, FKe BREAK_CHECKBOX_STATE(KeyInfo.Cmd, KeyBind.Cmd, KeyBind.bIgnoreCmd); } -//-------------------------------------------------------------------------------------------------------------------------- -bool FCogImguiInputHelper::IsKeyEventMatchingKeyBind(const FKeyEvent& KeyEvent, const FKeyBind& KeyBind) -{ - FCogImGuiKeyInfo KeyInfo; - KeyBindToKeyInfo(KeyBind, KeyInfo); - const bool Result = IsKeyEventMatchingKeyInfo(KeyEvent, KeyInfo); - return Result; -} - //-------------------------------------------------------------------------------------------------------------------------- bool FCogImguiInputHelper::WasKeyInfoJustPressed(APlayerController& PlayerController, const FCogImGuiKeyInfo& KeyInfo) { @@ -242,7 +234,7 @@ bool FCogImguiInputHelper::IsStopPlaySessionEvent(const FKeyEvent& KeyEvent) } //-------------------------------------------------------------------------------------------------------------------------- -uint32 FCogImguiInputHelper::MouseButtonToImGuiMouseButton(const FKey& MouseButton) +uint32 FCogImguiInputHelper::ToImGuiMouseButton(const FKey& MouseButton) { if (MouseButton == EKeys::LeftMouseButton) { return 0; } if (MouseButton == EKeys::RightMouseButton) { return 1; } @@ -335,3 +327,214 @@ FString FCogImguiInputHelper::KeyBindToString(const FKeyBind& KeyBind) return Result; } + + +//-------------------------------------------------------------------------------------------------------------------------- +bool FCogImguiInputHelper::IsKeyEventMatchingKeyBind(const FKeyEvent& KeyEvent, const FKeyBind& KeyBind) +{ + if (KeyBind.bDisabled) + { + return false; + } + + if (KeyBind.Key != KeyEvent.GetKey()) + { + return false; + } + + const bool bControlPressed = KeyEvent.IsControlDown(); + const bool bShiftPressed = KeyEvent.IsShiftDown(); + const bool bAltPressed = KeyEvent.IsAltDown(); + const bool bCmdPressed = KeyEvent.IsCommandDown(); + + if ((!KeyBind.Control || bControlPressed) + && (!KeyBind.Shift || bShiftPressed) + && (!KeyBind.Alt || bAltPressed) + && (!KeyBind.Cmd || bCmdPressed) + && (!KeyBind.bIgnoreCtrl || !bControlPressed) + && (!KeyBind.bIgnoreShift || !bShiftPressed) + && (!KeyBind.bIgnoreAlt || !bAltPressed) + && (!KeyBind.bIgnoreCmd || !bCmdPressed)) + { + return true; + } + + return false; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool FCogImguiInputHelper::IsKeyBoundToCommand(const UPlayerInput* InPlayerInput, const FKeyEvent& KeyEvent) +{ + if (InPlayerInput == nullptr) + { + return false; + } + + for (const FKeyBind& KeyBind : InPlayerInput->DebugExecBindings) + { + if (IsKeyEventMatchingKeyBind(KeyEvent, KeyBind)) + { + return true; + } + } + + return false; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool FCogImguiInputHelper::IsMouseInsideMainViewport() +{ + if (ImGuiViewportP* Viewport = (ImGuiViewportP*)ImGui::GetMainViewport()) + { + ImGuiIO& IO = ImGui::GetIO(); + const bool Result = Viewport->GetMainRect().Contains(IO.MousePos); + return Result; + } + + return false; +} + +//-------------------------------------------------------------------------------------------------------------------------- +ImGuiKey FCogImguiInputHelper::ToImKey(const FKey& Key) +{ + static const TMap LookupMap = { + { EKeys::Tab, ImGuiKey_Tab }, + + { EKeys::Left, ImGuiKey_LeftArrow }, + { EKeys::Right, ImGuiKey_RightArrow }, + { EKeys::Up, ImGuiKey_UpArrow }, + { EKeys::Down, ImGuiKey_DownArrow }, + + { EKeys::PageUp, ImGuiKey_PageUp }, + { EKeys::PageDown, ImGuiKey_PageDown }, + { EKeys::Home, ImGuiKey_Home }, + { EKeys::End, ImGuiKey_End }, + { EKeys::Insert, ImGuiKey_Insert }, + { EKeys::Delete, ImGuiKey_Delete }, + + { EKeys::BackSpace, ImGuiKey_Backspace }, + { EKeys::SpaceBar, ImGuiKey_Space }, + { EKeys::Enter, ImGuiKey_Enter }, + { EKeys::Escape, ImGuiKey_Escape }, + + { EKeys::LeftControl, ImGuiKey_LeftCtrl }, + { EKeys::LeftShift, ImGuiKey_LeftShift }, + { EKeys::LeftAlt, ImGuiKey_LeftAlt }, + { EKeys::LeftCommand, ImGuiKey_LeftSuper }, + { EKeys::RightControl, ImGuiKey_RightCtrl }, + { EKeys::RightShift, ImGuiKey_RightShift }, + { EKeys::RightAlt, ImGuiKey_RightAlt }, + { EKeys::RightCommand, ImGuiKey_RightSuper }, + + { EKeys::Zero, ImGuiKey_0 }, + { EKeys::One, ImGuiKey_1 }, + { EKeys::Two, ImGuiKey_2 }, + { EKeys::Three, ImGuiKey_3 }, + { EKeys::Four, ImGuiKey_4 }, + { EKeys::Five, ImGuiKey_5 }, + { EKeys::Six, ImGuiKey_6 }, + { EKeys::Seven, ImGuiKey_7 }, + { EKeys::Eight, ImGuiKey_8 }, + { EKeys::Nine, ImGuiKey_9 }, + + { EKeys::A, ImGuiKey_A }, + { EKeys::B, ImGuiKey_B }, + { EKeys::C, ImGuiKey_C }, + { EKeys::D, ImGuiKey_D }, + { EKeys::E, ImGuiKey_E }, + { EKeys::F, ImGuiKey_F }, + { EKeys::G, ImGuiKey_G }, + { EKeys::H, ImGuiKey_H }, + { EKeys::I, ImGuiKey_I }, + { EKeys::J, ImGuiKey_J }, + { EKeys::K, ImGuiKey_K }, + { EKeys::L, ImGuiKey_L }, + { EKeys::M, ImGuiKey_M }, + { EKeys::N, ImGuiKey_N }, + { EKeys::O, ImGuiKey_O }, + { EKeys::P, ImGuiKey_P }, + { EKeys::Q, ImGuiKey_Q }, + { EKeys::R, ImGuiKey_R }, + { EKeys::S, ImGuiKey_S }, + { EKeys::T, ImGuiKey_T }, + { EKeys::U, ImGuiKey_U }, + { EKeys::V, ImGuiKey_V }, + { EKeys::W, ImGuiKey_W }, + { EKeys::X, ImGuiKey_X }, + { EKeys::Y, ImGuiKey_Y }, + { EKeys::Z, ImGuiKey_Z }, + + { EKeys::F1, ImGuiKey_F1 }, + { EKeys::F2, ImGuiKey_F2 }, + { EKeys::F3, ImGuiKey_F3 }, + { EKeys::F4, ImGuiKey_F4 }, + { EKeys::F5, ImGuiKey_F5 }, + { EKeys::F6, ImGuiKey_F6 }, + { EKeys::F7, ImGuiKey_F7 }, + { EKeys::F8, ImGuiKey_F8 }, + { EKeys::F9, ImGuiKey_F9 }, + { EKeys::F10, ImGuiKey_F10 }, + { EKeys::F11, ImGuiKey_F11 }, + { EKeys::F12, ImGuiKey_F12 }, + + { EKeys::Apostrophe, ImGuiKey_Apostrophe }, + { EKeys::Comma, ImGuiKey_Comma }, + { EKeys::Period, ImGuiKey_Period }, + { EKeys::Slash, ImGuiKey_Slash }, + { EKeys::Semicolon, ImGuiKey_Semicolon }, + { EKeys::LeftBracket, ImGuiKey_LeftBracket }, + { EKeys::Backslash, ImGuiKey_Backslash }, + { EKeys::RightBracket, ImGuiKey_RightBracket }, + + { EKeys::CapsLock, ImGuiKey_CapsLock }, + { EKeys::ScrollLock, ImGuiKey_ScrollLock }, + { EKeys::NumLock, ImGuiKey_NumLock }, + { EKeys::Pause, ImGuiKey_Pause }, + + { EKeys::NumPadZero, ImGuiKey_Keypad0 }, + { EKeys::NumPadOne, ImGuiKey_Keypad1 }, + { EKeys::NumPadTwo, ImGuiKey_Keypad2 }, + { EKeys::NumPadThree, ImGuiKey_Keypad3 }, + { EKeys::NumPadFour, ImGuiKey_Keypad4 }, + { EKeys::NumPadFive, ImGuiKey_Keypad5 }, + { EKeys::NumPadSix, ImGuiKey_Keypad6 }, + { EKeys::NumPadSeven, ImGuiKey_Keypad7 }, + { EKeys::NumPadEight, ImGuiKey_Keypad8 }, + { EKeys::NumPadNine, ImGuiKey_Keypad9 }, + + { EKeys::Decimal, ImGuiKey_KeypadDecimal }, + { EKeys::Divide, ImGuiKey_KeypadDivide }, + { EKeys::Multiply, ImGuiKey_KeypadMultiply }, + { EKeys::Subtract, ImGuiKey_KeypadSubtract }, + { EKeys::Add, ImGuiKey_KeypadAdd }, + { EKeys::Equals, ImGuiKey_KeypadEqual }, + + { EKeys::Gamepad_Special_Right, ImGuiKey_GamepadStart }, + { EKeys::Gamepad_Special_Left, ImGuiKey_GamepadBack }, + { EKeys::Gamepad_FaceButton_Left, ImGuiKey_GamepadFaceLeft }, + { EKeys::Gamepad_FaceButton_Right, ImGuiKey_GamepadFaceRight }, + { EKeys::Gamepad_FaceButton_Top, ImGuiKey_GamepadFaceUp }, + { EKeys::Gamepad_FaceButton_Bottom, ImGuiKey_GamepadFaceDown }, + { EKeys::Gamepad_DPad_Left, ImGuiKey_GamepadDpadLeft }, + { EKeys::Gamepad_DPad_Right, ImGuiKey_GamepadDpadRight }, + { EKeys::Gamepad_DPad_Up, ImGuiKey_GamepadDpadUp }, + { EKeys::Gamepad_DPad_Down, ImGuiKey_GamepadDpadDown }, + { EKeys::Gamepad_LeftShoulder, ImGuiKey_GamepadL1 }, + { EKeys::Gamepad_RightShoulder, ImGuiKey_GamepadR1 }, + { EKeys::Gamepad_LeftTrigger, ImGuiKey_GamepadL2 }, + { EKeys::Gamepad_RightTrigger, ImGuiKey_GamepadR2 }, + { EKeys::Gamepad_LeftThumbstick, ImGuiKey_GamepadL3 }, + { EKeys::Gamepad_RightThumbstick, ImGuiKey_GamepadR3 }, + { EKeys::Gamepad_LeftStick_Left, ImGuiKey_GamepadLStickLeft }, + { EKeys::Gamepad_LeftStick_Right, ImGuiKey_GamepadLStickRight }, + { EKeys::Gamepad_LeftStick_Up, ImGuiKey_GamepadLStickUp }, + { EKeys::Gamepad_LeftStick_Down, ImGuiKey_GamepadLStickDown }, + { EKeys::Gamepad_RightStick_Left, ImGuiKey_GamepadRStickLeft }, + { EKeys::Gamepad_RightStick_Right, ImGuiKey_GamepadRStickRight }, + { EKeys::Gamepad_RightStick_Up, ImGuiKey_GamepadRStickUp }, + { EKeys::Gamepad_RightStick_Down, ImGuiKey_GamepadRStickDown } + }; + + const ImGuiKey* Result = LookupMap.Find(Key); + return (Result != nullptr) ? *Result : ImGuiKey_None; +} diff --git a/Plugins/Cog/Source/CogImgui/Private/CogImguiWidget.cpp b/Plugins/Cog/Source/CogImgui/Private/CogImguiWidget.cpp index a5a16f6..727790b 100644 --- a/Plugins/Cog/Source/CogImgui/Private/CogImguiWidget.cpp +++ b/Plugins/Cog/Source/CogImgui/Private/CogImguiWidget.cpp @@ -3,6 +3,8 @@ #include "CogImguiInputHelper.h" #include "CogImguiModule.h" #include "CogImguiWidget.h" + +#include "CogImGuiContext.h" #include "imgui.h" #include "SlateOptMacros.h" @@ -12,7 +14,8 @@ void SCogImguiWidget::Construct(const FArguments& InArgs) { Context = InArgs._Context; - SetVisibility(EVisibility::SelfHitTestInvisible); + //SetVisibility(EVisibility::SelfHitTestInvisible); + SetVisibility(EVisibility::Visible); } END_SLATE_FUNCTION_BUILD_OPTIMIZATION @@ -126,5 +129,226 @@ FReply SCogImguiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterE return Result; } +//-------------------------------------------------------------------------------------------------------------------------- +FReply SCogImguiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) +{ + return HandleKeyEvent(MyGeometry, KeyEvent); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FReply SCogImguiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) +{ + return HandleKeyEvent(MyGeometry, KeyEvent); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FReply SCogImguiWidget::HandleKeyEvent(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) +{ + if (Context->GetEnableInput() == false) + { + return FReply::Unhandled(); + } + if (KeyEvent.GetKey().IsGamepadKey()) + { + //if (bShareGamepad) + //{ + // // TODO: handle imgui gamepad + // return FReply::Unhandled(); + //} + } + else + { + if (FCogImguiInputHelper::IsKeyEventHandled(Context->GetGameViewport()->GetWorld(), KeyEvent) == false) + { + return FReply::Unhandled(); + } + ImGuiIO& IO = ImGui::GetIO(); + IO.AddKeyEvent(FCogImguiInputHelper::ToImKey(KeyEvent.GetKey()), false); + IO.AddKeyEvent(ImGuiMod_Ctrl, KeyEvent.IsControlDown()); + IO.AddKeyEvent(ImGuiMod_Shift, KeyEvent.IsShiftDown()); + IO.AddKeyEvent(ImGuiMod_Alt, KeyEvent.IsAltDown()); + IO.AddKeyEvent(ImGuiMod_Super, KeyEvent.IsCommandDown()); + //if (bShareKeyboard) + //{ + // return FReply::Unhandled(); + //} + } + return FReply::Handled(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FReply SCogImguiWidget::OnAnalogValueChanged(const FGeometry& MyGeometry, const FAnalogInputEvent& AnalogInputEvent) +{ + if (AnalogInputEvent.GetKey().IsGamepadKey()) + { + //if (bShareGamepad) + //{ + // // TODO: handle imgui gamepad + // return FReply::Unhandled(); + //} + + return FReply::Unhandled(); + } + else + { + //if (bShareKeyboard) + //{ + // return FReply::Unhandled(); + //} + } + return FReply::Handled(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FReply SCogImguiWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (Context->GetEnableInput() == false) + { + UE_LOG(LogCogImGui, VeryVerbose, TEXT("SCogImguiWidget::OnMouseButtonDown | %s | Unhandled | EnableInput == false"), Window.IsValid() ? *Window->GetTitle().ToString() : *FString("None")); + return FReply::Unhandled(); + } + const uint32 MouseButton = FCogImguiInputHelper::ToImGuiMouseButton(MouseEvent.GetEffectingButton()); + ImGui::GetIO().AddMouseSourceEvent(ImGuiMouseSource_Mouse); + ImGui::GetIO().AddMouseButtonEvent(MouseButton, true); + + UE_LOG(LogCogImGui, VeryVerbose, TEXT("SCogImguiWidget::OnMouseButtonDown | Window:%s | Handled"), Window.IsValid() ? *Window->GetTitle().ToString() : *FString("None")); + return FReply::Handled(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FReply SCogImguiWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (Context->GetEnableInput() == false) + { + UE_LOG(LogCogImGui, VeryVerbose, TEXT("SCogImguiWidget::OnMouseButtonUp | Window:%s | Unhandled | EnableInput == false"), Window.IsValid() ? *Window->GetTitle().ToString() : *FString("None")); + return FReply::Unhandled(); + } + const uint32 MouseButton = FCogImguiInputHelper::ToImGuiMouseButton(MouseEvent.GetEffectingButton()); + ImGui::GetIO().AddMouseSourceEvent(ImGuiMouseSource_Mouse); + ImGui::GetIO().AddMouseButtonEvent(MouseButton, false); + UE_LOG(LogCogImGui, VeryVerbose, TEXT("SCogImguiWidget::OnMouseButtonUp | Window:%s | Handled"), Window.IsValid() ? *Window->GetTitle().ToString() : *FString("None")); + return FReply::Handled(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FReply SCogImguiWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (Context->GetEnableInput() == false) + { + return FReply::Unhandled(); + } + ImGui::GetIO().AddMouseSourceEvent(ImGuiMouseSource_Mouse); + ImGui::GetIO().AddMouseWheelEvent(0, MouseEvent.GetWheelDelta()); + return FReply::Handled(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FReply SCogImguiWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (Context->GetEnableInput() == false) + { + //UE_LOG(LogCogImGui, VeryVerbose, TEXT("SCogImguiWidget::OnMouseMove | Window:%s | Unhandled | EnableInput == false"), Window.IsValid() ? *Window->GetTitle().ToString() : *FString("None")); + return FReply::Unhandled(); + } + + ImGuiIO& IO = ImGui::GetIO(); + if (IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + IO.AddMousePosEvent(MouseEvent.GetScreenSpacePosition().X, MouseEvent.GetScreenSpacePosition().Y); + } + else + { + const FVector2D TransformedMousePosition = MouseEvent.GetScreenSpacePosition() - Context->GetMainWidget()->GetTickSpaceGeometry().GetAbsolutePosition(); + IO.AddMousePosEvent(TransformedMousePosition.X, TransformedMousePosition.Y); + } + + //UE_LOG(LogCogImGui, VeryVerbose, TEXT("SCogImguiWidget::OnMouseMove | Window:%s | Handled"), Window.IsValid() ? *Window->GetTitle().ToString() : *FString("None")); + return FReply::Handled(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FReply SCogImguiWidget::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& FocusEvent) +{ + return Super::OnFocusReceived(MyGeometry, FocusEvent); +} +//-------------------------------------------------------------------------------------------------------------------------- +//void SCogImguiWidget::TickFocus() +//{ +// FCogImguiModule& Module = FCogImguiModule::Get(); +// +// const bool bShouldEnableInput = Module.GetEnableInput(); +// if (bEnableInput != bShouldEnableInput) +// { +// bEnableInput = bShouldEnableInput; +// +// if (bEnableInput) +// { +// TakeFocus(); +// } +// else +// { +// ReturnFocus(); +// } +// } +// else if (bEnableInput) +// { +// const auto& ViewportWidget = GameViewport->GetGameViewportWidget(); +// if (!HasKeyboardFocus() && !IsConsoleOpened() && (ViewportWidget->HasKeyboardFocus() || ViewportWidget->HasFocusedDescendants())) +// { +// TakeFocus(); +// } +// } +//} +// +// +////-------------------------------------------------------------------------------------------------------------------------- +//void SCogImguiWidget::TakeFocus() +//{ +// FSlateApplication& SlateApplication = FSlateApplication::Get(); +// +// PreviousUserFocusedWidget = SlateApplication.GetUserFocusedWidget(SlateApplication.GetUserIndexForKeyboard()); +// +// if (ULocalPlayer* LocalPlayer = GetLocalPlayer()) +// { +// TSharedRef FocusWidget = SharedThis(this); +// LocalPlayer->GetSlateOperations().CaptureMouse(FocusWidget); +// LocalPlayer->GetSlateOperations().SetUserFocus(FocusWidget); +// } +// else +// { +// SlateApplication.SetKeyboardFocus(SharedThis(this)); +// } +//} +// +////-------------------------------------------------------------------------------------------------------------------------- +//void SCogImguiWidget::ReturnFocus() +//{ +// if (HasKeyboardFocus()) +// { +// auto FocusWidgetPtr = PreviousUserFocusedWidget.IsValid() +// ? PreviousUserFocusedWidget.Pin() +// : GameViewport->GetGameViewportWidget(); +// +// if (ULocalPlayer* LocalPlayer = GetLocalPlayer()) +// { +// auto FocusWidgetRef = FocusWidgetPtr.ToSharedRef(); +// +// if (FocusWidgetPtr == GameViewport->GetGameViewportWidget()) +// { +// LocalPlayer->GetSlateOperations().CaptureMouse(FocusWidgetRef); +// } +// +// LocalPlayer->GetSlateOperations().SetUserFocus(FocusWidgetRef); +// } +// else +// { +// FSlateApplication& SlateApplication = FSlateApplication::Get(); +// SlateApplication.ResetToDefaultPointerInputSettings(); +// SlateApplication.SetUserFocus(SlateApplication.GetUserIndexForKeyboard(), FocusWidgetPtr); +// } +// } +// +// PreviousUserFocusedWidget.Reset(); +//} diff --git a/Plugins/Cog/Source/CogImgui/Public/CogImGuiInputProcessor.h b/Plugins/Cog/Source/CogImgui/Public/CogImGuiInputProcessor.h index 508c874..990f6b6 100644 --- a/Plugins/Cog/Source/CogImgui/Public/CogImGuiInputProcessor.h +++ b/Plugins/Cog/Source/CogImgui/Public/CogImGuiInputProcessor.h @@ -8,11 +8,11 @@ class UPlayerInput; enum ImGuiKey : int; struct FKeyBind; -class FImGuiInputProcessor : public IInputProcessor +class FCogImGuiInputProcessor : public IInputProcessor { public: - FImGuiInputProcessor(UPlayerInput* InPlayerInput, FCogImguiContext* InContext, SCogImguiWidget* InWidget); + FCogImGuiInputProcessor(UPlayerInput* InPlayerInput, FCogImguiContext* InContext, SCogImguiWidget* InWidget); virtual void Tick(const float DeltaTime, FSlateApplication& SlateApp, TSharedRef SlateCursor) override; @@ -38,22 +38,8 @@ protected: bool HandleMouseButtonEvent(FSlateApplication& SlateApp, const FPointerEvent& Event, bool IsButtonDown); - bool IsMouseInsideMainViewport(); - void AddMousePosEvent(const FVector2D& MousePosition) const; - bool IsKeyBoundToCommand(const FKeyEvent& KeyEvent); - - static ImGuiKey ToImKey(const FKey& Key); - - static bool IsKeyEventMatchingKeyBind(const FKeyEvent& KeyEvent, const FKeyBind& KeyBind); - - static bool IsConsoleEvent(const FKeyEvent& KeyEvent); - - static bool IsStopPlaySessionEvent(const FKeyEvent& KeyEvent); - - static uint32 ToImGuiMouseButton(const FKey& MouseButton); - TObjectPtr Context = nullptr; TObjectPtr PlayerInput = nullptr; diff --git a/Plugins/Cog/Source/CogImgui/Public/CogImguiContext.h b/Plugins/Cog/Source/CogImgui/Public/CogImguiContext.h index c188ead..c722aba 100644 --- a/Plugins/Cog/Source/CogImgui/Public/CogImguiContext.h +++ b/Plugins/Cog/Source/CogImgui/Public/CogImguiContext.h @@ -45,6 +45,10 @@ public: void SetDPIScale(float Value); + TObjectPtr GetGameViewport() const { return GameViewport; } + + TSharedPtr GetMainWidget() const { return MainWidget; } + private: void OnDisplayMetricsChanged(const FDisplayMetrics& DisplayMetrics) const; @@ -59,6 +63,12 @@ private: void BuildFont(); + void ReturnFocus(); + + void TickFocus(); + + void TakeFocus(); + ULocalPlayer* GetLocalPlayer() const; static void ImGui_CreateWindow(ImGuiViewport* Viewport); diff --git a/Plugins/Cog/Source/CogImgui/Public/CogImguiHelper.h b/Plugins/Cog/Source/CogImgui/Public/CogImguiHelper.h index b54f84a..7c4324a 100644 --- a/Plugins/Cog/Source/CogImgui/Public/CogImguiHelper.h +++ b/Plugins/Cog/Source/CogImgui/Public/CogImguiHelper.h @@ -10,6 +10,9 @@ struct ImGuiWindow; using CogTextureIndex = int32; +constexpr bool ForwardEvent = false; +constexpr bool TerminateEvent = true; + class COGIMGUI_API FCogImguiHelper { public: diff --git a/Plugins/Cog/Source/CogImgui/Public/CogImguiInputHelper.h b/Plugins/Cog/Source/CogImgui/Public/CogImguiInputHelper.h index 8d92a79..28662f1 100644 --- a/Plugins/Cog/Source/CogImgui/Public/CogImguiInputHelper.h +++ b/Plugins/Cog/Source/CogImgui/Public/CogImguiInputHelper.h @@ -38,7 +38,9 @@ public: static bool IsStopPlaySessionEvent(const FKeyEvent& KeyEvent); - static uint32 MouseButtonToImGuiMouseButton(const FKey& MouseButton); + static uint32 ToImGuiMouseButton(const FKey& MouseButton); + + static ImGuiKey ToImKey(const FKey& Key); static EMouseCursor::Type ToSlateMouseCursor(ImGuiMouseCursor MouseCursor); @@ -48,6 +50,10 @@ public: static FString KeyBindToString(const FKeyBind& KeyBind); + static bool IsMouseInsideMainViewport(); + + static bool IsKeyBoundToCommand(const UPlayerInput* InPlayerInput, const FKeyEvent& KeyEvent); + template* = nullptr> static ImWchar CastInputChar(T Char) { diff --git a/Plugins/Cog/Source/CogImgui/Public/CogImguiWidget.h b/Plugins/Cog/Source/CogImgui/Public/CogImguiWidget.h index 2aa0c5a..05c898d 100644 --- a/Plugins/Cog/Source/CogImgui/Public/CogImguiWidget.h +++ b/Plugins/Cog/Source/CogImgui/Public/CogImguiWidget.h @@ -8,6 +8,7 @@ #include "Widgets/SLeafWidget.h" class FCogImguiContext; +class SWindow; class UGameViewportClient; //-------------------------------------------------------------------------------------------------------------------------- @@ -33,12 +34,35 @@ public: virtual FVector2D ComputeDesiredSize(float Scale) const override; + virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) override; + virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) override; + virtual FReply OnAnalogValueChanged(const FGeometry& MyGeometry, const FAnalogInputEvent& AnalogInputEvent) override; + virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& FocusEvent) override; + void SetDrawData(const ImDrawData* InDrawData); + TSharedPtr GetWindow() const { return Window; } + + void SetWindow(TSharedPtr Value) { Window = Value; } + protected: + FReply HandleKeyEvent(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent); + + void TickFocus(); + + void TakeFocus(); + + void ReturnFocus(); + TObjectPtr Context = nullptr; + TSharedPtr Window = nullptr; + FSlateRenderTransform ImGuiRenderTransform; mutable TArray VertexBuffer;