mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-01-18 08:20:32 +00:00
Added input support:
- Added ImGui Input State to collect and store input updates. - Changed Slate ImGui Widget to handle Slate input events and store them in input state. - Changed ImGui Context Proxy to copy input state to its context.
This commit is contained in:
parent
35f2d342a0
commit
393460f330
@ -41,6 +41,7 @@ public class ImGui : ModuleRules
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"InputCore",
|
||||
"Slate",
|
||||
"SlateCore"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
|
@ -20,6 +20,9 @@ FImGuiContextProxy::FImGuiContextProxy()
|
||||
unsigned char* Pixels;
|
||||
IO.Fonts->GetTexDataAsRGBA32(&Pixels, nullptr, nullptr);
|
||||
|
||||
// Initialize key mapping, so context can correctly interpret input state.
|
||||
ImGuiInterops::SetUnrealKeyMap(IO);
|
||||
|
||||
// Begin frame to complete context initialization (this is to avoid problems with other systems calling to ImGui
|
||||
// during startup).
|
||||
BeginFrame();
|
||||
@ -30,7 +33,7 @@ FImGuiContextProxy::~FImGuiContextProxy()
|
||||
ImGui::Shutdown();
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::Tick(float DeltaSeconds)
|
||||
void FImGuiContextProxy::Tick(float DeltaSeconds, const FImGuiInputState* InputState)
|
||||
{
|
||||
if (bIsFrameStarted)
|
||||
{
|
||||
@ -46,16 +49,21 @@ void FImGuiContextProxy::Tick(float DeltaSeconds)
|
||||
}
|
||||
|
||||
// Begin a new frame and set the context back to a state in which it allows to draw controls.
|
||||
BeginFrame(DeltaSeconds);
|
||||
BeginFrame(DeltaSeconds, InputState);
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::BeginFrame(float DeltaTime)
|
||||
void FImGuiContextProxy::BeginFrame(float DeltaTime, const FImGuiInputState* InputState)
|
||||
{
|
||||
if (!bIsFrameStarted)
|
||||
{
|
||||
ImGuiIO& IO = ImGui::GetIO();
|
||||
IO.DeltaTime = DeltaTime;
|
||||
|
||||
if (InputState)
|
||||
{
|
||||
ImGuiInterops::CopyInput(IO, *InputState);
|
||||
}
|
||||
|
||||
ImGui::NewFrame();
|
||||
|
||||
bIsFrameStarted = true;
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include <imgui.h>
|
||||
|
||||
|
||||
class FImGuiInputState;
|
||||
|
||||
// Represents a single ImGui context. All the context updates should be done through this proxy. During update it
|
||||
// broadcasts draw events to allow listeners draw their controls. After update it stores produced draw data.
|
||||
// TODO: Add dynamically created contexts, so we can have a better support for multi-PIE.
|
||||
@ -30,11 +32,13 @@ public:
|
||||
FSimpleMulticastDelegate& OnDraw() { return DrawEvent; }
|
||||
|
||||
// Tick to advance context to the next frame.
|
||||
void Tick(float DeltaSeconds);
|
||||
// @param DeltaSeconds - Time delta in seconds (will be passed to ImGui)
|
||||
// @param InputState - Input state for ImGui IO or null if there is no input for this context
|
||||
void Tick(float DeltaSeconds, const FImGuiInputState* InputState = nullptr);
|
||||
|
||||
private:
|
||||
|
||||
void BeginFrame(float DeltaTime = 1.f / 60.f);
|
||||
void BeginFrame(float DeltaTime = 1.f / 60.f, const FImGuiInputState* InputState = nullptr);
|
||||
void EndFrame();
|
||||
|
||||
void UpdateDrawData(ImDrawData* DrawData);
|
||||
|
75
Source/ImGui/Private/ImGuiInputState.cpp
Normal file
75
Source/ImGui/Private/ImGuiInputState.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "ImGuiInputState.h"
|
||||
|
||||
|
||||
FImGuiInputState::FImGuiInputState()
|
||||
{
|
||||
ClearState();
|
||||
}
|
||||
|
||||
void FImGuiInputState::AddCharacter(TCHAR Char)
|
||||
{
|
||||
static_assert(sizeof(TCHAR) <= sizeof(InputCharacters[0]), "Size of elements in Input Characters buffer is smaller than size of 'TCHAR'. Possible truncation.");
|
||||
|
||||
if (InputCharactersNum < Utilities::GetArraySize(InputCharacters))
|
||||
{
|
||||
InputCharacters[InputCharactersNum++] = static_cast<ImWchar>(Char);
|
||||
InputCharacters[InputCharactersNum] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiInputState::SetKeyDown(uint32 KeyIndex, bool bIsDown)
|
||||
{
|
||||
if (KeyIndex < Utilities::GetArraySize(KeysDown))
|
||||
{
|
||||
if (KeysDown[KeyIndex] != bIsDown)
|
||||
{
|
||||
KeysDown[KeyIndex] = bIsDown;
|
||||
KeysUpdateRange.AddPosition(KeyIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiInputState::SetMouseDown(uint32 MouseIndex, bool bIsDown)
|
||||
{
|
||||
if (MouseIndex < Utilities::GetArraySize(MouseButtonsDown))
|
||||
{
|
||||
if (MouseButtonsDown[MouseIndex] != bIsDown)
|
||||
{
|
||||
MouseButtonsDown[MouseIndex] = bIsDown;
|
||||
MouseButtonsUpdateRange.AddPosition(MouseIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiInputState::ClearState()
|
||||
{
|
||||
ClearCharacters();
|
||||
|
||||
using std::fill;
|
||||
fill(KeysDown, &KeysDown[Utilities::GetArraySize(KeysDown)], false);
|
||||
fill(MouseButtonsDown, &MouseButtonsDown[Utilities::GetArraySize(MouseButtonsDown)], false);
|
||||
|
||||
// Fully expanding dirty parts of both arrays, to inform about the change.
|
||||
KeysUpdateRange.SetFull();
|
||||
MouseButtonsUpdateRange.SetFull();
|
||||
}
|
||||
|
||||
void FImGuiInputState::ClearUpdateState()
|
||||
{
|
||||
ClearCharacters();
|
||||
|
||||
KeysUpdateRange.SetEmpty();
|
||||
MouseButtonsUpdateRange.SetEmpty();
|
||||
|
||||
MouseWheelDelta = 0.f;
|
||||
}
|
||||
|
||||
void FImGuiInputState::ClearCharacters()
|
||||
{
|
||||
InputCharactersNum = 0;
|
||||
InputCharacters[0];
|
||||
}
|
101
Source/ImGui/Private/ImGuiInputState.h
Normal file
101
Source/ImGui/Private/ImGuiInputState.h
Normal file
@ -0,0 +1,101 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ImGuiInteroperability.h"
|
||||
#include "Utilities/Arrays.h"
|
||||
|
||||
|
||||
// Collects and stores input state and updates for ImGui IO.
|
||||
class FImGuiInputState
|
||||
{
|
||||
public:
|
||||
|
||||
// Characters buffer.
|
||||
using FCharactersBuffer = ImGuiInterops::ImGuiTypes::FInputCharactersBuffer;
|
||||
|
||||
// Array for mouse button states.
|
||||
using FMouseButtonsArray = ImGuiInterops::ImGuiTypes::FMouseButtonsArray;
|
||||
|
||||
// Array for key states.
|
||||
using FKeysArray = ImGuiInterops::ImGuiTypes::FKeysArray;
|
||||
|
||||
// Pair of indices defining range in mouse buttons array.
|
||||
using FMouseButtonsIndexRange = Utilities::TArrayIndexRange<FMouseButtonsArray, uint32>;
|
||||
|
||||
// Pair of indices defining range in keys array.
|
||||
using FKeysIndexRange = Utilities::TArrayIndexRange<FKeysArray, uint32>;
|
||||
|
||||
// Create empty state with whole range instance with the whole update state marked as dirty.
|
||||
FImGuiInputState();
|
||||
|
||||
// Get reference to input characters buffer.
|
||||
const FCharactersBuffer& GetCharacters() const { return InputCharacters; }
|
||||
|
||||
// Get number of characters in input characters buffer.
|
||||
int32 GetCharactersNum() const { return InputCharactersNum; }
|
||||
|
||||
// Add a character to the characters buffer. We can store and send to ImGui up to 16 characters per frame. Any
|
||||
// character beyond that limit will be discarded.
|
||||
// @param Char - Character to add
|
||||
void AddCharacter(TCHAR Char);
|
||||
|
||||
// Get reference to the array with key down states.
|
||||
const FKeysArray& GetKeys() const { return KeysDown; }
|
||||
|
||||
// Get possibly empty range of indices bounding dirty part of the keys array.
|
||||
const FKeysIndexRange& GetKeysUpdateRange() const { return KeysUpdateRange; }
|
||||
|
||||
// Change state of the key in the keys array and expand range bounding dirty part of the array.
|
||||
// @param KeyIndex - Index of the key
|
||||
// @param bIsDown - True, if key is down
|
||||
void SetKeyDown(uint32 KeyIndex, bool bIsDown);
|
||||
|
||||
// Get reference to the array with mouse button down states.
|
||||
const FMouseButtonsArray& GetMouseButtons() const { return MouseButtonsDown; }
|
||||
|
||||
// Get possibly empty range of indices bounding dirty part of the mouse buttons array.
|
||||
const FMouseButtonsIndexRange& GetMouseButtonsUpdateRange() const { return MouseButtonsUpdateRange; }
|
||||
|
||||
// Change state of the button in the mouse buttons array and expand range bounding dirty part of the array.
|
||||
// @param MouseIndex - Index of the mouse button
|
||||
// @param bIsDown - True, if button is down
|
||||
void SetMouseDown(uint32 MouseIndex, bool IsDown);
|
||||
|
||||
// Get mouse wheel delta accumulated during the last frame.
|
||||
float GetMouseWheelDelta() const { return MouseWheelDelta; }
|
||||
|
||||
// Add mouse wheel delta.
|
||||
// @param DeltaValue - Mouse wheel delta to add
|
||||
void AddMouseWheelDelta(float DeltaValue) { MouseWheelDelta += DeltaValue; }
|
||||
|
||||
// Get the current mouse position.
|
||||
const FVector2D& GetMousePosition() const { return MousePosition; }
|
||||
|
||||
// Set mouse position.
|
||||
// @param Position - New mouse position
|
||||
void SetMousePosition(const FVector2D& Position) { MousePosition = Position; }
|
||||
|
||||
// Clear state and mark as dirty.
|
||||
void ClearState();
|
||||
|
||||
// Clear part of the state that is meant to be updated in every frame like: accumulators, buffers and information
|
||||
// about dirty parts of keys or mouse buttons arrays.
|
||||
void ClearUpdateState();
|
||||
|
||||
private:
|
||||
|
||||
void ClearCharacters();
|
||||
|
||||
FVector2D MousePosition = FVector2D::ZeroVector;
|
||||
float MouseWheelDelta = 0.f;
|
||||
|
||||
FMouseButtonsArray MouseButtonsDown;
|
||||
FMouseButtonsIndexRange MouseButtonsUpdateRange;
|
||||
|
||||
FCharactersBuffer InputCharacters;
|
||||
uint32 InputCharactersNum = 0;
|
||||
|
||||
FKeysArray KeysDown;
|
||||
FKeysIndexRange KeysUpdateRange;
|
||||
};
|
178
Source/ImGui/Private/ImGuiInteroperability.cpp
Normal file
178
Source/ImGui/Private/ImGuiInteroperability.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "ImGuiInteroperability.h"
|
||||
#include "ImGuiInputState.h"
|
||||
#include "Utilities/Arrays.h"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
//====================================================================================================
|
||||
// Copying Utilities
|
||||
//====================================================================================================
|
||||
|
||||
// Copy all elements from source to destination array of the same size.
|
||||
template<typename TArray>
|
||||
void Copy(const TArray& Src, TArray& Dst)
|
||||
{
|
||||
using std::copy;
|
||||
using std::begin;
|
||||
using std::end;
|
||||
copy(begin(Src), end(Src), begin(Dst));
|
||||
}
|
||||
|
||||
// Copy subrange of source array to destination array of the same size.
|
||||
template<typename TArray, typename SizeType>
|
||||
void Copy(const TArray& Src, TArray& Dst, const Utilities::TArrayIndexRange<typename TArray, typename SizeType>& Range)
|
||||
{
|
||||
using std::copy;
|
||||
using std::begin;
|
||||
copy(begin(Src) + Range.GetBegin(), begin(Src) + Range.GetEnd(), begin(Dst) + Range.GetBegin());
|
||||
}
|
||||
|
||||
// Copy number of elements from the beginning of source array to the beginning of destination array of the same size.
|
||||
template<typename TArray, typename SizeType>
|
||||
void Copy(const TArray& Src, TArray& Dst, SizeType Count)
|
||||
{
|
||||
checkf(Count < Utilities::ArraySize<TArray>::value, TEXT("Number of copied elements is larger than array size."));
|
||||
|
||||
using std::copy;
|
||||
using std::begin;
|
||||
copy(begin(Src), begin(Src) + Count, begin(Dst));
|
||||
}
|
||||
}
|
||||
|
||||
namespace ImGuiInterops
|
||||
{
|
||||
//====================================================================================================
|
||||
// Input Mapping
|
||||
//====================================================================================================
|
||||
|
||||
void SetUnrealKeyMap(ImGuiIO& IO)
|
||||
{
|
||||
struct FUnrealToImGuiMapping
|
||||
{
|
||||
FUnrealToImGuiMapping()
|
||||
{
|
||||
KeyMap[ImGuiKey_Tab] = GetKeyIndex(EKeys::Tab);
|
||||
KeyMap[ImGuiKey_LeftArrow] = GetKeyIndex(EKeys::Left);
|
||||
KeyMap[ImGuiKey_RightArrow] = GetKeyIndex(EKeys::Right);
|
||||
KeyMap[ImGuiKey_UpArrow] = GetKeyIndex(EKeys::Up);
|
||||
KeyMap[ImGuiKey_DownArrow] = GetKeyIndex(EKeys::Down);
|
||||
KeyMap[ImGuiKey_PageUp] = GetKeyIndex(EKeys::PageUp);
|
||||
KeyMap[ImGuiKey_PageDown] = GetKeyIndex(EKeys::PageDown);
|
||||
KeyMap[ImGuiKey_Home] = GetKeyIndex(EKeys::Home);
|
||||
KeyMap[ImGuiKey_End] = GetKeyIndex(EKeys::End);
|
||||
KeyMap[ImGuiKey_Delete] = GetKeyIndex(EKeys::Delete);
|
||||
KeyMap[ImGuiKey_Backspace] = GetKeyIndex(EKeys::BackSpace);
|
||||
KeyMap[ImGuiKey_Enter] = GetKeyIndex(EKeys::Enter);
|
||||
KeyMap[ImGuiKey_Escape] = GetKeyIndex(EKeys::Escape);
|
||||
KeyMap[ImGuiKey_A] = GetKeyIndex(EKeys::A);
|
||||
KeyMap[ImGuiKey_C] = GetKeyIndex(EKeys::C);
|
||||
KeyMap[ImGuiKey_V] = GetKeyIndex(EKeys::V);
|
||||
KeyMap[ImGuiKey_X] = GetKeyIndex(EKeys::X);
|
||||
KeyMap[ImGuiKey_Y] = GetKeyIndex(EKeys::Y);
|
||||
KeyMap[ImGuiKey_Z] = GetKeyIndex(EKeys::Z);
|
||||
}
|
||||
|
||||
ImGuiTypes::FKeyMap KeyMap;
|
||||
};
|
||||
|
||||
static const FUnrealToImGuiMapping Mapping;
|
||||
|
||||
Copy(Mapping.KeyMap, IO.KeyMap);
|
||||
}
|
||||
|
||||
uint32 GetKeyIndex(const FKey& Key)
|
||||
{
|
||||
const uint32* pKeyCode = nullptr;
|
||||
const uint32* pCharCode = nullptr;
|
||||
|
||||
FInputKeyManager::Get().GetCodesFromKey(Key, pKeyCode, pCharCode);
|
||||
|
||||
if (pKeyCode)
|
||||
{
|
||||
return *pKeyCode;
|
||||
}
|
||||
|
||||
if (pCharCode)
|
||||
{
|
||||
return *pCharCode;
|
||||
}
|
||||
|
||||
checkf(false, TEXT("Couldn't find a Key Code for key '%s'. Expecting that all keys should have a Key Code."), *Key.GetDisplayName().ToString());
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32 GetMouseIndex(const FKey& MouseButton)
|
||||
{
|
||||
if (MouseButton == EKeys::LeftMouseButton)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (MouseButton == EKeys::MiddleMouseButton)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (MouseButton == EKeys::RightMouseButton)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (MouseButton == EKeys::ThumbMouseButton)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
else if (MouseButton == EKeys::ThumbMouseButton2)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//====================================================================================================
|
||||
// Input State Copying
|
||||
//====================================================================================================
|
||||
|
||||
void CopyInput(ImGuiIO& IO, const FImGuiInputState& InputState)
|
||||
{
|
||||
static const uint32 LeftControl = GetKeyIndex(EKeys::LeftControl);
|
||||
static const uint32 RightControl = GetKeyIndex(EKeys::RightControl);
|
||||
static const uint32 LeftShift = GetKeyIndex(EKeys::LeftShift);
|
||||
static const uint32 RightShift = GetKeyIndex(EKeys::RightShift);
|
||||
static const uint32 LeftAlt = GetKeyIndex(EKeys::LeftAlt);
|
||||
static const uint32 RightAlt = GetKeyIndex(EKeys::RightAlt);
|
||||
|
||||
// Copy mouse position.
|
||||
IO.MousePos.x = InputState.GetMousePosition().X;
|
||||
IO.MousePos.y = InputState.GetMousePosition().Y;
|
||||
|
||||
// Copy mouse wheel delta.
|
||||
IO.MouseWheel += InputState.GetMouseWheelDelta();
|
||||
|
||||
// Copy key modifiers.
|
||||
IO.KeyCtrl = InputState.GetKeys()[LeftControl] || InputState.GetKeys()[RightControl];
|
||||
IO.KeyShift = InputState.GetKeys()[LeftShift] || InputState.GetKeys()[RightShift];
|
||||
IO.KeyAlt = InputState.GetKeys()[LeftAlt] || InputState.GetKeys()[RightAlt];
|
||||
IO.KeySuper = false;
|
||||
|
||||
// Copy buffers.
|
||||
if (!InputState.GetKeysUpdateRange().IsEmpty())
|
||||
{
|
||||
Copy(InputState.GetKeys(), IO.KeysDown, InputState.GetKeysUpdateRange());
|
||||
}
|
||||
|
||||
if (!InputState.GetMouseButtonsUpdateRange().IsEmpty())
|
||||
{
|
||||
Copy(InputState.GetMouseButtons(), IO.MouseDown, InputState.GetMouseButtonsUpdateRange());
|
||||
}
|
||||
|
||||
if (InputState.GetCharactersNum() > 0)
|
||||
{
|
||||
Copy(InputState.GetCharacters(), IO.InputCharacters, InputState.GetCharactersNum());
|
||||
}
|
||||
}
|
||||
}
|
@ -7,9 +7,56 @@
|
||||
#include <imgui.h>
|
||||
|
||||
|
||||
class FImGuiInputState;
|
||||
|
||||
// Utilities to help standardise operations between Unreal and ImGui.
|
||||
namespace ImGuiInterops
|
||||
{
|
||||
//====================================================================================================
|
||||
// ImGui Types
|
||||
//====================================================================================================
|
||||
|
||||
namespace ImGuiTypes
|
||||
{
|
||||
using FMouseButtonsArray = decltype(ImGuiIO::MouseDown);
|
||||
using FKeysArray = decltype(ImGuiIO::KeysDown);
|
||||
|
||||
using FInputCharactersBuffer = decltype(ImGuiIO::InputCharacters);
|
||||
|
||||
using FKeyMap = decltype(ImGuiIO::KeyMap);
|
||||
}
|
||||
|
||||
|
||||
//====================================================================================================
|
||||
// Input Mapping
|
||||
//====================================================================================================
|
||||
|
||||
// Set in ImGui IO mapping to recognize indices generated from Unreal input events.
|
||||
void SetUnrealKeyMap(ImGuiIO& IO);
|
||||
|
||||
// Map FKey to index in keys buffer.
|
||||
uint32 GetKeyIndex(const FKey& Key);
|
||||
|
||||
// Map key event to index in keys buffer.
|
||||
uint32 GetKeyIndex(const FKeyEvent& KeyEvent) { return KeyEvent.GetKeyCode(); }
|
||||
|
||||
// Map mouse FKey to index in mouse buttons buffer.
|
||||
uint32 GetMouseIndex(const FKey& MouseButton);
|
||||
|
||||
// Map pointer event to index in mouse buttons buffer.
|
||||
uint32 GetMouseIndex(const FPointerEvent& MouseEvent) { return GetMouseIndex(MouseEvent.GetEffectingButton()); }
|
||||
|
||||
|
||||
//====================================================================================================
|
||||
// Input State Copying
|
||||
//====================================================================================================
|
||||
|
||||
// Copy input to ImGui IO.
|
||||
// @param IO - Target ImGui IO
|
||||
// @param InputState - Input state to copy
|
||||
void CopyInput(ImGuiIO& IO, const FImGuiInputState& InputState);
|
||||
|
||||
|
||||
//====================================================================================================
|
||||
// Conversions
|
||||
//====================================================================================================
|
||||
|
@ -118,7 +118,10 @@ void FImGuiModuleManager::Tick(float DeltaSeconds)
|
||||
if (IsInUpdateThread())
|
||||
{
|
||||
// Update context proxy to advance to next frame.
|
||||
ContextProxy.Tick(DeltaSeconds);
|
||||
ContextProxy.Tick(DeltaSeconds, ViewportWidget.IsValid() ? &ViewportWidget->GetInputState() : nullptr);
|
||||
|
||||
// Inform that we finished updating ImGui, so other subsystems can react.
|
||||
PostImGuiUpdateEvent.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,9 @@ public:
|
||||
// Get texture resources manager.
|
||||
FTextureManager& GetTextureManager() { return TextureManager; }
|
||||
|
||||
// Event called right after ImGui is updated, to give other subsystems chance to react.
|
||||
FSimpleMulticastDelegate& OnPostImGuiUpdate() { return PostImGuiUpdateEvent; }
|
||||
|
||||
private:
|
||||
|
||||
FImGuiModuleManager();
|
||||
@ -50,6 +53,9 @@ private:
|
||||
void AddWidgetToViewport(UGameViewportClient* GameViewport);
|
||||
void AddWidgetToAllViewports();
|
||||
|
||||
// Event that we call after ImGui is updated.
|
||||
FSimpleMulticastDelegate PostImGuiUpdateEvent;
|
||||
|
||||
// Proxy controlling ImGui context.
|
||||
FImGuiContextProxy ContextProxy;
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include "SImGuiWidget.h"
|
||||
|
||||
#include "ImGuiContextProxy.h"
|
||||
#include "ImGuiInteroperability.h"
|
||||
#include "ImGuiModuleManager.h"
|
||||
#include "TextureManager.h"
|
||||
#include "Utilities/ScopeGuards.h"
|
||||
@ -14,9 +16,76 @@ void SImGuiWidget::Construct(const FArguments& InArgs)
|
||||
{
|
||||
checkf(InArgs._ModuleManager, TEXT("Null Module Manager argument"));
|
||||
ModuleManager = InArgs._ModuleManager;
|
||||
|
||||
ModuleManager->OnPostImGuiUpdate().AddRaw(this, &SImGuiWidget::OnPostImGuiUpdate);
|
||||
}
|
||||
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
|
||||
SImGuiWidget::~SImGuiWidget()
|
||||
{
|
||||
ModuleManager->OnPostImGuiUpdate().RemoveAll(this);
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent)
|
||||
{
|
||||
InputState.AddCharacter(CharacterEvent.GetCharacter());
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
|
||||
{
|
||||
InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), true);
|
||||
|
||||
// If this is tilde key then let input through and release the focus to allow console to process it.
|
||||
if (KeyEvent.GetKey() == EKeys::Tilde)
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
|
||||
{
|
||||
InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), false);
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), true);
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), true);
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), false);
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState.AddMouseWheelDelta(MouseEvent.GetWheelDelta());
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState.SetMousePosition(MouseEvent.GetScreenSpacePosition() - MyGeometry.AbsolutePosition);
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
void SImGuiWidget::OnPostImGuiUpdate()
|
||||
{
|
||||
InputState.ClearUpdateState();
|
||||
}
|
||||
|
||||
int32 SImGuiWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect,
|
||||
FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& WidgetStyle, bool bParentEnabled) const
|
||||
{
|
||||
|
@ -2,11 +2,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ImGuiInputState.h"
|
||||
|
||||
#include <Widgets/SLeafWidget.h>
|
||||
|
||||
|
||||
class FImGuiModuleManager;
|
||||
|
||||
// Slate widget for rendering ImGui output.
|
||||
// Slate widget for rendering ImGui output and storing Slate inputs.
|
||||
class SImGuiWidget : public SLeafWidget
|
||||
{
|
||||
public:
|
||||
@ -18,8 +21,36 @@ public:
|
||||
|
||||
void Construct(const FArguments& InArgs);
|
||||
|
||||
~SImGuiWidget();
|
||||
|
||||
const FImGuiInputState& GetInputState() const { return InputState; }
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// SWidget overrides
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
virtual bool SupportsKeyboardFocus() const override { return true; }
|
||||
|
||||
virtual FReply OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent) override;
|
||||
|
||||
virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) override;
|
||||
|
||||
virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) override;
|
||||
|
||||
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
|
||||
virtual FReply OnMouseButtonDoubleClick(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;
|
||||
|
||||
private:
|
||||
|
||||
void OnPostImGuiUpdate();
|
||||
|
||||
virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& WidgetStyle, bool bParentEnabled) const override;
|
||||
|
||||
virtual FVector2D ComputeDesiredSize(float) const override;
|
||||
@ -28,4 +59,6 @@ private:
|
||||
|
||||
mutable TArray<FSlateVertex> VertexBuffer;
|
||||
mutable TArray<SlateIndex> IndexBuffer;
|
||||
|
||||
FImGuiInputState InputState;
|
||||
};
|
||||
|
63
Source/ImGui/Private/Utilities/Arrays.h
Normal file
63
Source/ImGui/Private/Utilities/Arrays.h
Normal file
@ -0,0 +1,63 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Range.h"
|
||||
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
// Utilities to work with one-dimensional, statically bound arrays. Code relying on those utilities should work without
|
||||
// modifications with fixed-sized arrays (currently used in ImGui) and with standard arrays.
|
||||
|
||||
namespace Utilities
|
||||
{
|
||||
//====================================================================================================
|
||||
// Helper functions
|
||||
//====================================================================================================
|
||||
|
||||
// Function to determine number of elements in fixed size array.
|
||||
template<class T, std::size_t N>
|
||||
constexpr std::size_t GetArraySize(const T(&)[N])
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
// Function to determine number of elements in std array.
|
||||
template<class T, std::size_t N>
|
||||
constexpr std::size_t GetArraySize(const std::array<T, N>&)
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
|
||||
//====================================================================================================
|
||||
// Traits
|
||||
//====================================================================================================
|
||||
|
||||
template<typename TArray>
|
||||
struct ArraySize;
|
||||
|
||||
// Struct to determine number of elements in fixed size array.
|
||||
template<typename T, std::size_t N>
|
||||
struct ArraySize<T[N]> : std::extent<T[N]>
|
||||
{
|
||||
};
|
||||
|
||||
// Struct to determine number of elements in std array.
|
||||
template<typename T, std::size_t N>
|
||||
struct ArraySize<std::array<T, N>> : std::tuple_size<std::array<T, N>>
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
//====================================================================================================
|
||||
// Ranges
|
||||
//====================================================================================================
|
||||
|
||||
// Array indices range. Limited by 0 and array size.
|
||||
template<typename TArray, typename SizeType>
|
||||
using TArrayIndexRange = TBoundedRange<SizeType, 0, ArraySize<TArray>::value>;
|
||||
}
|
173
Source/ImGui/Private/Utilities/Range.h
Normal file
173
Source/ImGui/Private/Utilities/Range.h
Normal file
@ -0,0 +1,173 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace Utilities
|
||||
{
|
||||
//====================================================================================================
|
||||
// Range
|
||||
//====================================================================================================
|
||||
|
||||
template<typename T>
|
||||
class TRange
|
||||
{
|
||||
public:
|
||||
|
||||
TRange() {}
|
||||
|
||||
TRange(const T& RangeBegin, const T& RangeEnd) { SetRange(RangeBegin, RangeEnd); }
|
||||
|
||||
const T& GetBegin() const { return Begin; }
|
||||
const T& GetEnd() const { return End; }
|
||||
|
||||
bool IsEmpty() const { return Begin == End; }
|
||||
|
||||
void SetEmpty() { Begin = End = T(); }
|
||||
|
||||
void SetRange(const T& RangeBegin, const T& RangeEnd)
|
||||
{
|
||||
checkf(RangeBegin <= RangeEnd, TEXT("Invalid arguments: RangeBegin > RangeEnd."));
|
||||
Begin = RangeBegin;
|
||||
End = RangeEnd;
|
||||
}
|
||||
|
||||
void AddPosition(const T& Position)
|
||||
{
|
||||
AddRangeUnchecked(Position, Position + 1);
|
||||
}
|
||||
|
||||
void AddRange(const T& RangeBegin, const T& RangeEnd)
|
||||
{
|
||||
checkf(RangeBegin <= RangeEnd, TEXT("Invalid arguments: RangeBegin > RangeEnd."));
|
||||
AddRangeUnchecked(RangeBegin, RangeEnd);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void AddRangeUnchecked(const T& RangeBegin, const T& RangeEnd)
|
||||
{
|
||||
if (IsEmpty())
|
||||
{
|
||||
Begin = RangeBegin;
|
||||
End = RangeEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Begin > RangeBegin)
|
||||
{
|
||||
Begin = RangeBegin;
|
||||
}
|
||||
|
||||
if (End < RangeEnd)
|
||||
{
|
||||
End = RangeEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T Begin = T();
|
||||
T End = T();
|
||||
};
|
||||
|
||||
|
||||
// Enable range-based loops
|
||||
|
||||
template<typename T>
|
||||
const T& begin(const TRange<T>& Range)
|
||||
{
|
||||
return Range.GetBegin();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& end(const TRange<T>& Range)
|
||||
{
|
||||
return Range.GetEnd();
|
||||
}
|
||||
|
||||
|
||||
//====================================================================================================
|
||||
// Bounded Range
|
||||
//====================================================================================================
|
||||
|
||||
template<typename T, T BeginBound, T EndBound>
|
||||
class TBoundedRange
|
||||
{
|
||||
public:
|
||||
|
||||
constexpr const T& GetLowerBound() const { return BeginBound; }
|
||||
constexpr const T& GetUpperBound() const { return EndBound; }
|
||||
|
||||
const T& GetBegin() const { return Begin; }
|
||||
const T& GetEnd() const { return End; }
|
||||
|
||||
bool IsEmpty() const { return Begin == End; }
|
||||
|
||||
void SetEmpty() { Begin = End = BeginBound; }
|
||||
|
||||
void SetFull()
|
||||
{
|
||||
Begin = BeginBound;
|
||||
End = EndBound;
|
||||
}
|
||||
|
||||
void AddPosition(const T& Position)
|
||||
{
|
||||
checkf(Position >= BeginBound && Position < EndBound, TEXT("Position out of range."));
|
||||
|
||||
AddRangeUnchecked(Position, Position + 1);
|
||||
}
|
||||
|
||||
void AddRange(const T& RangeBegin, const T& RangeEnd)
|
||||
{
|
||||
checkf(RangeBegin <= RangeEnd, TEXT("Invalid arguments: RangeBegin > MaxPosition."));
|
||||
checkf(RangeBegin >= BeginBound, TEXT("RangeBegin out of range."));
|
||||
checkf(RangeBegin <= EndBound, TEXT("RangeEnd out of range."));
|
||||
|
||||
AddRangeUnchecked(RangeBegin, RangeEnd);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void AddRangeUnchecked(const T& RangeBegin, const T& RangeEnd)
|
||||
{
|
||||
if (IsEmpty())
|
||||
{
|
||||
Begin = RangeBegin;
|
||||
End = RangeEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Begin > RangeBegin)
|
||||
{
|
||||
Begin = RangeBegin;
|
||||
}
|
||||
|
||||
if (End < RangeEnd)
|
||||
{
|
||||
End = RangeEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T Begin = EndBound;
|
||||
T End = BeginBound;
|
||||
};
|
||||
|
||||
|
||||
// Enable range-based loops
|
||||
|
||||
template<typename T, T BeginBound, T EndBound>
|
||||
const T& begin(const TBoundedRange<T, BeginBound, EndBound>& Range)
|
||||
{
|
||||
return Range.GetBegin();
|
||||
}
|
||||
|
||||
template<typename T, T BeginBound, T EndBound>
|
||||
const T& end(const TBoundedRange<T, BeginBound, EndBound>& Range)
|
||||
{
|
||||
return Range.GetEnd();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user