mirror of
				https://github.com/kevinporetti/UnrealImGui.git
				synced 2025-11-03 23:33:16 +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