mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-01-18 16:30:32 +00:00
Using ImGui to draw mouse cursor.
This commit is contained in:
parent
5625365310
commit
9c62196cbb
@ -25,9 +25,19 @@ FImGuiContextProxy::FImGuiContextProxy()
|
|||||||
// Use pre-defined canvas size.
|
// Use pre-defined canvas size.
|
||||||
IO.DisplaySize = { DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT };
|
IO.DisplaySize = { DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT };
|
||||||
|
|
||||||
// Load texture atlas.
|
// When GetTexData is called for the first time it builds atlas texture and copies mouse cursor data to context.
|
||||||
|
// When multiple contexts share atlas then only the first one will get mouse data. A simple workaround is to use
|
||||||
|
// a temporary atlas if shared one is already built.
|
||||||
unsigned char* Pixels;
|
unsigned char* Pixels;
|
||||||
IO.Fonts->GetTexDataAsRGBA32(&Pixels, nullptr, nullptr);
|
const bool bIsAltasBuilt = IO.Fonts->TexPixelsAlpha8 != nullptr;
|
||||||
|
if (bIsAltasBuilt)
|
||||||
|
{
|
||||||
|
ImFontAtlas().GetTexDataAsRGBA32(&Pixels, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IO.Fonts->GetTexDataAsRGBA32(&Pixels, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize key mapping, so context can correctly interpret input state.
|
// Initialize key mapping, so context can correctly interpret input state.
|
||||||
ImGuiInterops::SetUnrealKeyMap(IO);
|
ImGuiInterops::SetUnrealKeyMap(IO);
|
||||||
|
@ -76,6 +76,13 @@ public:
|
|||||||
// @param Position - New mouse position
|
// @param Position - New mouse position
|
||||||
void SetMousePosition(const FVector2D& Position) { MousePosition = Position; }
|
void SetMousePosition(const FVector2D& Position) { MousePosition = Position; }
|
||||||
|
|
||||||
|
// Check whether input has active mouse pointer.
|
||||||
|
bool HasMousePointer() const { return bHasMousePointer; }
|
||||||
|
|
||||||
|
// Set whether input has active mouse pointer.
|
||||||
|
// @param bHasPointer - True, if input has active mouse pointer
|
||||||
|
void SetMousePointer(bool bHasPointer) { bHasMousePointer = bHasPointer; }
|
||||||
|
|
||||||
// Get Control down state.
|
// Get Control down state.
|
||||||
bool IsControlDown() const { return bIsControlDown; }
|
bool IsControlDown() const { return bIsControlDown; }
|
||||||
|
|
||||||
@ -132,6 +139,8 @@ private:
|
|||||||
FKeysArray KeysDown;
|
FKeysArray KeysDown;
|
||||||
FKeysIndexRange KeysUpdateRange;
|
FKeysIndexRange KeysUpdateRange;
|
||||||
|
|
||||||
|
bool bHasMousePointer = false;
|
||||||
|
|
||||||
bool bIsControlDown = false;
|
bool bIsControlDown = false;
|
||||||
bool bIsShiftDown = false;
|
bool bIsShiftDown = false;
|
||||||
bool bIsAltDown = false;
|
bool bIsAltDown = false;
|
||||||
|
@ -146,6 +146,9 @@ namespace ImGuiInterops
|
|||||||
static const uint32 LeftAlt = GetKeyIndex(EKeys::LeftAlt);
|
static const uint32 LeftAlt = GetKeyIndex(EKeys::LeftAlt);
|
||||||
static const uint32 RightAlt = GetKeyIndex(EKeys::RightAlt);
|
static const uint32 RightAlt = GetKeyIndex(EKeys::RightAlt);
|
||||||
|
|
||||||
|
// Check whether we need to draw cursor.
|
||||||
|
IO.MouseDrawCursor = InputState.HasMousePointer();
|
||||||
|
|
||||||
// Copy mouse position.
|
// Copy mouse position.
|
||||||
IO.MousePos.x = InputState.GetMousePosition().X;
|
IO.MousePos.x = InputState.GetMousePosition().X;
|
||||||
IO.MousePos.y = InputState.GetMousePosition().Y;
|
IO.MousePos.y = InputState.GetMousePosition().Y;
|
||||||
|
@ -20,7 +20,11 @@ constexpr int32 IMGUI_WIDGET_Z_ORDER = 10000;
|
|||||||
|
|
||||||
DEFINE_LOG_CATEGORY_STATIC(LogImGuiWidget, Warning, All);
|
DEFINE_LOG_CATEGORY_STATIC(LogImGuiWidget, Warning, All);
|
||||||
|
|
||||||
#define TEXT_INPUT_MODE(Val) ((Val) == EInputMode::MouseAndKeyboard ? TEXT("MouseAndKeyboard") : (Val) == EInputMode::MouseOnly ? TEXT("MouseOnly") : TEXT("None"))
|
#define TEXT_INPUT_MODE(Val) (\
|
||||||
|
(Val) == EInputMode::MouseAndKeyboard ? TEXT("MouseAndKeyboard") :\
|
||||||
|
(Val) == EInputMode::MousePointerOnly ? TEXT("MousePointerOnly") :\
|
||||||
|
TEXT("None"))
|
||||||
|
|
||||||
#define TEXT_BOOL(Val) ((Val) ? TEXT("true") : TEXT("false"))
|
#define TEXT_BOOL(Val) ((Val) ? TEXT("true") : TEXT("false"))
|
||||||
|
|
||||||
|
|
||||||
@ -47,6 +51,9 @@ void SImGuiWidget::Construct(const FArguments& InArgs)
|
|||||||
ModuleManager = InArgs._ModuleManager;
|
ModuleManager = InArgs._ModuleManager;
|
||||||
ContextIndex = InArgs._ContextIndex;
|
ContextIndex = InArgs._ContextIndex;
|
||||||
|
|
||||||
|
// Disable mouse cursor over this widget as we will use ImGui to draw it.
|
||||||
|
SetCursor(EMouseCursor::None);
|
||||||
|
|
||||||
// Sync visibility with default input enabled state.
|
// Sync visibility with default input enabled state.
|
||||||
SetVisibilityFromInputEnabled();
|
SetVisibilityFromInputEnabled();
|
||||||
|
|
||||||
@ -93,6 +100,8 @@ void SImGuiWidget::Tick(const FGeometry& AllottedGeometry, const double InCurren
|
|||||||
{
|
{
|
||||||
Super::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
Super::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||||
|
|
||||||
|
UpdateMouseStatus();
|
||||||
|
|
||||||
// Note: Moving that update to console variable sink or callback might seem like a better alternative but input
|
// Note: Moving that update to console variable sink or callback might seem like a better alternative but input
|
||||||
// setup in this function is better handled here.
|
// setup in this function is better handled here.
|
||||||
UpdateInputEnabled();
|
UpdateInputEnabled();
|
||||||
@ -177,6 +186,9 @@ FReply SImGuiWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEven
|
|||||||
InputState.SetMousePosition(MouseEvent.GetScreenSpacePosition() - MyGeometry.AbsolutePosition);
|
InputState.SetMousePosition(MouseEvent.GetScreenSpacePosition() - MyGeometry.AbsolutePosition);
|
||||||
CopyModifierKeys(MouseEvent);
|
CopyModifierKeys(MouseEvent);
|
||||||
|
|
||||||
|
// This event is called in every frame when we have a mouse, so we can use it to raise notifications.
|
||||||
|
NotifyMouseEvent();
|
||||||
|
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +200,7 @@ FReply SImGuiWidget::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEv
|
|||||||
|
|
||||||
// If widget has a keyboard focus we always maintain mouse input. Technically, if mouse is outside of the widget
|
// If widget has a keyboard focus we always maintain mouse input. Technically, if mouse is outside of the widget
|
||||||
// area it won't generate events but we freeze its state until it either comes back or input is completely lost.
|
// area it won't generate events but we freeze its state until it either comes back or input is completely lost.
|
||||||
UpdateInputMode(true, true);
|
UpdateInputMode(true, IsDirectlyHovered());
|
||||||
|
|
||||||
FSlateApplication::Get().ResetToDefaultPointerInputSettings();
|
FSlateApplication::Get().ResetToDefaultPointerInputSettings();
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
@ -200,7 +212,7 @@ void SImGuiWidget::OnFocusLost(const FFocusEvent& FocusEvent)
|
|||||||
|
|
||||||
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Focus Lost."), ContextIndex);
|
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Focus Lost."), ContextIndex);
|
||||||
|
|
||||||
UpdateInputMode(false, IsHovered());
|
UpdateInputMode(false, IsDirectlyHovered());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SImGuiWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
void SImGuiWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||||
@ -240,7 +252,7 @@ void SImGuiWidget::CopyModifierKeys(const FInputEvent& InputEvent)
|
|||||||
|
|
||||||
void SImGuiWidget::CopyModifierKeys(const FPointerEvent& MouseEvent)
|
void SImGuiWidget::CopyModifierKeys(const FPointerEvent& MouseEvent)
|
||||||
{
|
{
|
||||||
if (InputMode == EInputMode::MouseOnly)
|
if (InputMode == EInputMode::MousePointerOnly)
|
||||||
{
|
{
|
||||||
CopyModifierKeys(static_cast<const FInputEvent&>(MouseEvent));
|
CopyModifierKeys(static_cast<const FInputEvent&>(MouseEvent));
|
||||||
}
|
}
|
||||||
@ -330,11 +342,11 @@ void SImGuiWidget::UpdateInputEnabled()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SImGuiWidget::UpdateInputMode(bool bNeedKeyboard, bool bNeedMouse)
|
void SImGuiWidget::UpdateInputMode(bool bHasKeyboardFocus, bool bHasMousePointer)
|
||||||
{
|
{
|
||||||
const EInputMode NewInputMode =
|
const EInputMode NewInputMode =
|
||||||
bNeedKeyboard ? EInputMode::MouseAndKeyboard :
|
bHasKeyboardFocus ? EInputMode::MouseAndKeyboard :
|
||||||
bNeedMouse ? EInputMode::MouseOnly :
|
bHasMousePointer ? EInputMode::MousePointerOnly :
|
||||||
EInputMode::None;
|
EInputMode::None;
|
||||||
|
|
||||||
if (InputMode != NewInputMode)
|
if (InputMode != NewInputMode)
|
||||||
@ -354,6 +366,26 @@ void SImGuiWidget::UpdateInputMode(bool bNeedKeyboard, bool bNeedMouse)
|
|||||||
}
|
}
|
||||||
|
|
||||||
InputMode = NewInputMode;
|
InputMode = NewInputMode;
|
||||||
|
|
||||||
|
ClearMouseEventNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputState.SetMousePointer(bHasMousePointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SImGuiWidget::UpdateMouseStatus()
|
||||||
|
{
|
||||||
|
// Note: Mouse leave events can get lost if other viewport takes mouse capture (for instance console is opened by
|
||||||
|
// different viewport when this widget is hovered). With that we lose a chance to cleanup and hide ImGui pointer.
|
||||||
|
// We could either update ImGui pointer in every frame or like below, use mouse events to catch when mouse is lost.
|
||||||
|
|
||||||
|
if (InputMode == EInputMode::MousePointerOnly)
|
||||||
|
{
|
||||||
|
if (!HasMouseEventNotification())
|
||||||
|
{
|
||||||
|
UpdateInputMode(false, IsDirectlyHovered());
|
||||||
|
}
|
||||||
|
ClearMouseEventNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +479,7 @@ void SImGuiWidget::OnDebugDraw()
|
|||||||
bool bDebug = CVars::DebugWidget.GetValueOnGameThread() > 0;
|
bool bDebug = CVars::DebugWidget.GetValueOnGameThread() > 0;
|
||||||
if (bDebug)
|
if (bDebug)
|
||||||
{
|
{
|
||||||
ImGui::SetNextWindowSize(ImVec2(380, 320), ImGuiSetCond_Once);
|
ImGui::SetNextWindowSize(ImVec2(380, 340), ImGuiSetCond_Once);
|
||||||
if (ImGui::Begin("ImGui Widget Debug", &bDebug))
|
if (ImGui::Begin("ImGui Widget Debug", &bDebug))
|
||||||
{
|
{
|
||||||
ImGui::Columns(2, nullptr, false);
|
ImGui::Columns(2, nullptr, false);
|
||||||
@ -459,6 +491,7 @@ void SImGuiWidget::OnDebugDraw()
|
|||||||
|
|
||||||
TwoColumns::Value("Input Enabled", bInputEnabled);
|
TwoColumns::Value("Input Enabled", bInputEnabled);
|
||||||
TwoColumns::Value("Input Mode", TEXT_INPUT_MODE(InputMode));
|
TwoColumns::Value("Input Mode", TEXT_INPUT_MODE(InputMode));
|
||||||
|
TwoColumns::Value("Input Has Mouse Pointer", InputState.HasMousePointer());
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
|
@ -79,7 +79,9 @@ private:
|
|||||||
enum class EInputMode : uint8
|
enum class EInputMode : uint8
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
MouseOnly,
|
// Mouse pointer only without user focus
|
||||||
|
MousePointerOnly,
|
||||||
|
// Full input with user focus
|
||||||
MouseAndKeyboard
|
MouseAndKeyboard
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,8 +100,14 @@ private:
|
|||||||
// Update input enabled state from console variable.
|
// Update input enabled state from console variable.
|
||||||
void UpdateInputEnabled();
|
void UpdateInputEnabled();
|
||||||
|
|
||||||
// Determine new input mode based on requirement hints.
|
// Determine new input mode based on hints.
|
||||||
void UpdateInputMode(bool bNeedKeyboard, bool bNeedMouse);
|
void UpdateInputMode(bool bHasKeyboardFocus, bool bHasMousePointer);
|
||||||
|
|
||||||
|
void UpdateMouseStatus();
|
||||||
|
|
||||||
|
FORCEINLINE bool HasMouseEventNotification() const { return bReceivedMouseEvent; }
|
||||||
|
FORCEINLINE void NotifyMouseEvent() { bReceivedMouseEvent = true; }
|
||||||
|
FORCEINLINE void ClearMouseEventNotification() { bReceivedMouseEvent = false; }
|
||||||
|
|
||||||
void OnPostImGuiUpdate();
|
void OnPostImGuiUpdate();
|
||||||
|
|
||||||
@ -119,6 +127,7 @@ private:
|
|||||||
|
|
||||||
EInputMode InputMode = EInputMode::None;
|
EInputMode InputMode = EInputMode::None;
|
||||||
bool bInputEnabled = false;
|
bool bInputEnabled = false;
|
||||||
|
bool bReceivedMouseEvent = false;
|
||||||
|
|
||||||
FImGuiInputState InputState;
|
FImGuiInputState InputState;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user