mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-01-18 08:20: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.
|
||||
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;
|
||||
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.
|
||||
ImGuiInterops::SetUnrealKeyMap(IO);
|
||||
|
@ -76,6 +76,13 @@ public:
|
||||
// @param Position - New mouse 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.
|
||||
bool IsControlDown() const { return bIsControlDown; }
|
||||
|
||||
@ -132,6 +139,8 @@ private:
|
||||
FKeysArray KeysDown;
|
||||
FKeysIndexRange KeysUpdateRange;
|
||||
|
||||
bool bHasMousePointer = false;
|
||||
|
||||
bool bIsControlDown = false;
|
||||
bool bIsShiftDown = false;
|
||||
bool bIsAltDown = false;
|
||||
|
@ -146,6 +146,9 @@ namespace ImGuiInterops
|
||||
static const uint32 LeftAlt = GetKeyIndex(EKeys::LeftAlt);
|
||||
static const uint32 RightAlt = GetKeyIndex(EKeys::RightAlt);
|
||||
|
||||
// Check whether we need to draw cursor.
|
||||
IO.MouseDrawCursor = InputState.HasMousePointer();
|
||||
|
||||
// Copy mouse position.
|
||||
IO.MousePos.x = InputState.GetMousePosition().X;
|
||||
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 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"))
|
||||
|
||||
|
||||
@ -47,6 +51,9 @@ void SImGuiWidget::Construct(const FArguments& InArgs)
|
||||
ModuleManager = InArgs._ModuleManager;
|
||||
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.
|
||||
SetVisibilityFromInputEnabled();
|
||||
|
||||
@ -93,6 +100,8 @@ void SImGuiWidget::Tick(const FGeometry& AllottedGeometry, const double InCurren
|
||||
{
|
||||
Super::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||
|
||||
UpdateMouseStatus();
|
||||
|
||||
// Note: Moving that update to console variable sink or callback might seem like a better alternative but input
|
||||
// setup in this function is better handled here.
|
||||
UpdateInputEnabled();
|
||||
@ -177,6 +186,9 @@ FReply SImGuiWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEven
|
||||
InputState.SetMousePosition(MouseEvent.GetScreenSpacePosition() - MyGeometry.AbsolutePosition);
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
// 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();
|
||||
return FReply::Handled();
|
||||
@ -200,7 +212,7 @@ void SImGuiWidget::OnFocusLost(const FFocusEvent& FocusEvent)
|
||||
|
||||
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)
|
||||
@ -240,7 +252,7 @@ void SImGuiWidget::CopyModifierKeys(const FInputEvent& InputEvent)
|
||||
|
||||
void SImGuiWidget::CopyModifierKeys(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
if (InputMode == EInputMode::MouseOnly)
|
||||
if (InputMode == EInputMode::MousePointerOnly)
|
||||
{
|
||||
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 =
|
||||
bNeedKeyboard ? EInputMode::MouseAndKeyboard :
|
||||
bNeedMouse ? EInputMode::MouseOnly :
|
||||
bHasKeyboardFocus ? EInputMode::MouseAndKeyboard :
|
||||
bHasMousePointer ? EInputMode::MousePointerOnly :
|
||||
EInputMode::None;
|
||||
|
||||
if (InputMode != NewInputMode)
|
||||
@ -354,6 +366,26 @@ void SImGuiWidget::UpdateInputMode(bool bNeedKeyboard, bool bNeedMouse)
|
||||
}
|
||||
|
||||
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;
|
||||
if (bDebug)
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(380, 320), ImGuiSetCond_Once);
|
||||
ImGui::SetNextWindowSize(ImVec2(380, 340), ImGuiSetCond_Once);
|
||||
if (ImGui::Begin("ImGui Widget Debug", &bDebug))
|
||||
{
|
||||
ImGui::Columns(2, nullptr, false);
|
||||
@ -459,6 +491,7 @@ void SImGuiWidget::OnDebugDraw()
|
||||
|
||||
TwoColumns::Value("Input Enabled", bInputEnabled);
|
||||
TwoColumns::Value("Input Mode", TEXT_INPUT_MODE(InputMode));
|
||||
TwoColumns::Value("Input Has Mouse Pointer", InputState.HasMousePointer());
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
@ -79,7 +79,9 @@ private:
|
||||
enum class EInputMode : uint8
|
||||
{
|
||||
None,
|
||||
MouseOnly,
|
||||
// Mouse pointer only without user focus
|
||||
MousePointerOnly,
|
||||
// Full input with user focus
|
||||
MouseAndKeyboard
|
||||
};
|
||||
|
||||
@ -98,8 +100,14 @@ private:
|
||||
// Update input enabled state from console variable.
|
||||
void UpdateInputEnabled();
|
||||
|
||||
// Determine new input mode based on requirement hints.
|
||||
void UpdateInputMode(bool bNeedKeyboard, bool bNeedMouse);
|
||||
// Determine new input mode based on hints.
|
||||
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();
|
||||
|
||||
@ -119,6 +127,7 @@ private:
|
||||
|
||||
EInputMode InputMode = EInputMode::None;
|
||||
bool bInputEnabled = false;
|
||||
bool bReceivedMouseEvent = false;
|
||||
|
||||
FImGuiInputState InputState;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user