mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-01-18 08:20:32 +00:00
Added support for shared mouse input:
- With the mouse sharing enabled, SImGuiWidget can update mouse position without enabling hit-tests. Actual input mode depends whether mouse cursor is hovering any ImGui window or not. - Added to context proxy interface to read whether that context has mouse hovering any window. - Added boilerplate code to support mouse sharing settings, properties and commands.
This commit is contained in:
parent
979903722a
commit
10ce6386d4
@ -5,6 +5,10 @@ Versions marked as 'unofficial' are labelled only for the needs of this changelo
|
||||
Change History
|
||||
--------------
|
||||
|
||||
Version: 1.17 (2019/04)
|
||||
- Added support for sharing with game mouse input.
|
||||
- Refactorization of input handling, with changes in SImGuiWidget and compatibility breaking changes in UImGuiInputHandler.
|
||||
|
||||
Version: 1.16 (2019/05)
|
||||
- Fixed issue with SImGuiLayout blocking mouse input for other Slate widgets, which was introduced by refactorization of widgets (version 1.14, commit c144658f).
|
||||
|
||||
|
@ -172,9 +172,7 @@ In input mode, ImGui will consume all input events. The reason behind the input
|
||||
|
||||
It is possible to modify rules to share keyboard or gamepad inputs.
|
||||
|
||||
The default behaviour can be configured in [input settings](#input) and changed runtime using `Keyboard Input Shared` and `Gamepad Input Shared` [properties](#properties) or `ImGui.ToggleKeyboardInputSharing`and `ImGui.ToggleGamepadInputSharing` [commands](#console-commands).
|
||||
|
||||
>*More work is needed for mouse input. Originally I didn't plan this feature so most probably I will come back to it after refactoring input handling. Ideally, I would like something that is more customizable from code with potentially a few implementations rather than one implementation with growing number of properties. In the meantime, if more control is needed, then `SImGuiWidget` is a good place to look at.*
|
||||
The default behaviour can be configured in [input settings](#input) and changed during runtime by modifying `Keyboard Input Shared`, `Gamepad Input Shared` and `Mouse Input Shared` [properties](#properties) or `ImGui.ToggleKeyboardInputSharing`, `ImGui.ToggleGamepadInputSharing` and `ImGui.ToggleMouseInputSharing` [commands](#console-commands).
|
||||
|
||||
#### Keyboard and gamepad navigation
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "ImGuiDelegatesContainer.h"
|
||||
#include "ImGuiImplementation.h"
|
||||
#include "ImGuiInteroperability.h"
|
||||
#include "Utilities/Arrays.h"
|
||||
|
||||
#include <Runtime/Launch/Resources/Version.h>
|
||||
|
||||
@ -136,6 +137,7 @@ void FImGuiContextProxy::Tick(float DeltaSeconds)
|
||||
// Update context information (some data, like mouse cursor, may be cleaned in new frame, so we should collect it
|
||||
// beforehand).
|
||||
bHasActiveItem = ImGui::IsAnyItemActive();
|
||||
bIsMouseHoveringAnyWindow = ImGui::IsMouseHoveringAnyWindow();
|
||||
MouseCursor = ImGuiInterops::ToSlateMouseCursor(ImGui::GetMouseCursor());
|
||||
DisplaySize = ImGuiInterops::ToVector2D(ImGui::GetIO().DisplaySize);
|
||||
|
||||
|
@ -65,13 +65,16 @@ public:
|
||||
// Set this context as current ImGui context.
|
||||
void SetAsCurrent() { ImGui::SetCurrentContext(Context.Get()); }
|
||||
|
||||
// Context display size (read once per frame during context update and cached here for easy access).
|
||||
// Context display size (read once per frame during context update).
|
||||
const FVector2D& GetDisplaySize() const { return DisplaySize; }
|
||||
|
||||
// Whether this context has an active item (read once per frame during context update and cached here for easy access).
|
||||
// Whether this context has an active item (read once per frame during context update).
|
||||
bool HasActiveItem() const { return bHasActiveItem; }
|
||||
|
||||
// Cursor type desired by this context (this is updated during ImGui frame and cached here during context update, before it is reset).
|
||||
// Whether this context has mouse hovering any window (read once per frame during context update).
|
||||
bool IsMouseHoveringAnyWindow() const { return bIsMouseHoveringAnyWindow; }
|
||||
|
||||
// Cursor type desired by this context (updated once per frame during context update).
|
||||
EMouseCursor::Type GetMouseCursor() const { return MouseCursor; }
|
||||
|
||||
// Delegate called right before ending the frame to allows listeners draw their controls.
|
||||
@ -105,6 +108,7 @@ private:
|
||||
|
||||
EMouseCursor::Type MouseCursor = EMouseCursor::None;
|
||||
bool bHasActiveItem = false;
|
||||
bool bIsMouseHoveringAnyWindow = false;
|
||||
|
||||
bool bIsFrameStarted = false;
|
||||
bool bIsDrawEarlyDebugCalled = false;
|
||||
|
@ -12,6 +12,7 @@ const TCHAR* const FImGuiModuleCommands::ToggleKeyboardNavigation = TEXT("ImGui.
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleGamepadNavigation = TEXT("ImGui.ToggleGamepadNavigation");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleKeyboardInputSharing = TEXT("ImGui.ToggleKeyboardInputSharing");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleGamepadInputSharing = TEXT("ImGui.ToggleGamepadInputSharing");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleMouseInputSharing = TEXT("ImGui.ToggleMouseInputSharing");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleDemo = TEXT("ImGui.ToggleDemo");
|
||||
|
||||
FImGuiModuleCommands::FImGuiModuleCommands(FImGuiModuleProperties& InProperties)
|
||||
@ -31,6 +32,9 @@ FImGuiModuleCommands::FImGuiModuleCommands(FImGuiModuleProperties& InProperties)
|
||||
, ToggleGamepadInputSharingCommand(ToggleGamepadInputSharing,
|
||||
TEXT("Toggle ImGui gamepad input sharing."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadInputSharingImpl))
|
||||
, ToggleMouseInputSharingCommand(ToggleMouseInputSharing,
|
||||
TEXT("Toggle ImGui mouse input sharing."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleMouseInputSharingImpl))
|
||||
, ToggleDemoCommand(ToggleDemo,
|
||||
TEXT("Toggle ImGui demo."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleDemoImpl))
|
||||
@ -67,6 +71,11 @@ void FImGuiModuleCommands::ToggleGamepadInputSharingImpl()
|
||||
Properties.ToggleGamepadInputSharing();
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::ToggleMouseInputSharingImpl()
|
||||
{
|
||||
Properties.ToggleMouseInputSharing();
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::ToggleDemoImpl()
|
||||
{
|
||||
Properties.ToggleDemo();
|
||||
|
@ -18,6 +18,7 @@ public:
|
||||
static const TCHAR* const ToggleGamepadNavigation;
|
||||
static const TCHAR* const ToggleKeyboardInputSharing;
|
||||
static const TCHAR* const ToggleGamepadInputSharing;
|
||||
static const TCHAR* const ToggleMouseInputSharing;
|
||||
static const TCHAR* const ToggleDemo;
|
||||
|
||||
FImGuiModuleCommands(FImGuiModuleProperties& InProperties);
|
||||
@ -31,6 +32,7 @@ private:
|
||||
void ToggleGamepadNavigationImpl();
|
||||
void ToggleKeyboardInputSharingImpl();
|
||||
void ToggleGamepadInputSharingImpl();
|
||||
void ToggleMouseInputSharingImpl();
|
||||
void ToggleDemoImpl();
|
||||
|
||||
FImGuiModuleProperties& Properties;
|
||||
@ -40,5 +42,6 @@ private:
|
||||
FAutoConsoleCommand ToggleGamepadNavigationCommand;
|
||||
FAutoConsoleCommand ToggleKeyboardInputSharingCommand;
|
||||
FAutoConsoleCommand ToggleGamepadInputSharingCommand;
|
||||
FAutoConsoleCommand ToggleMouseInputSharingCommand;
|
||||
FAutoConsoleCommand ToggleDemoCommand;
|
||||
};
|
||||
|
@ -100,6 +100,7 @@ void FImGuiModuleSettings::UpdateSettings()
|
||||
SetImGuiInputHandlerClass(SettingsObject->ImGuiInputHandlerClass);
|
||||
SetShareKeyboardInput(SettingsObject->bShareKeyboardInput);
|
||||
SetShareGamepadInput(SettingsObject->bShareGamepadInput);
|
||||
SetShareMouseInput(SettingsObject->bShareMouseInput);
|
||||
SetUseSoftwareCursor(SettingsObject->bUseSoftwareCursor);
|
||||
SetToggleInputKey(SettingsObject->ToggleInput);
|
||||
}
|
||||
@ -132,6 +133,15 @@ void FImGuiModuleSettings::SetShareGamepadInput(bool bShare)
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetShareMouseInput(bool bShare)
|
||||
{
|
||||
if (bShareMouseInput != bShare)
|
||||
{
|
||||
bShareMouseInput = bShare;
|
||||
Properties.SetMouseInputShared(bShare);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetUseSoftwareCursor(bool bUse)
|
||||
{
|
||||
if (bUseSoftwareCursor != bUse)
|
||||
|
@ -83,6 +83,12 @@ protected:
|
||||
UPROPERTY(EditAnywhere, config, Category = "Input")
|
||||
bool bShareGamepadInput = false;
|
||||
|
||||
// Whether ImGui should share mouse input with game.
|
||||
// This defines initial behaviour which can be later changed using 'ImGui.ToggleMouseInputSharing' command or
|
||||
// module properties interface.
|
||||
UPROPERTY(EditAnywhere, config, Category = "Input")
|
||||
bool bShareMouseInput = false;
|
||||
|
||||
// If true, then in input mode ImGui will draw its own cursor in place of the hardware one.
|
||||
// When disabled (default) there is a noticeable difference between cursor position seen by ImGui and position on
|
||||
// the screen. Enabling this option removes that effect but with lower frame-rates UI becomes quickly unusable.
|
||||
@ -155,6 +161,7 @@ private:
|
||||
void SetImGuiInputHandlerClass(const FStringClassReference& ClassReference);
|
||||
void SetShareKeyboardInput(bool bShare);
|
||||
void SetShareGamepadInput(bool bShare);
|
||||
void SetShareMouseInput(bool bShare);
|
||||
void SetUseSoftwareCursor(bool bUse);
|
||||
void SetToggleInputKey(const FImGuiKeyInfo& KeyInfo);
|
||||
|
||||
@ -169,5 +176,6 @@ private:
|
||||
FImGuiKeyInfo ToggleInputKey;
|
||||
bool bShareKeyboardInput = false;
|
||||
bool bShareGamepadInput = false;
|
||||
bool bShareMouseInput = false;
|
||||
bool bUseSoftwareCursor = false;
|
||||
};
|
||||
|
@ -123,6 +123,7 @@ void SImGuiWidget::Tick(const FGeometry& AllottedGeometry, const double InCurren
|
||||
Super::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||
|
||||
UpdateInputState();
|
||||
UpdateTransparentMouseInput(AllottedGeometry);
|
||||
HandleWindowFocusLost();
|
||||
}
|
||||
|
||||
@ -291,8 +292,9 @@ bool SImGuiWidget::IsConsoleOpened() const
|
||||
|
||||
void SImGuiWidget::UpdateVisibility()
|
||||
{
|
||||
// If we don't use input disable hit test to make this widget invisible for cursors hit detection.
|
||||
SetVisibility(bInputEnabled ? EVisibility::Visible : EVisibility::HitTestInvisible);
|
||||
// Make sure that we do not occlude other widgets, if input is disabled or if mouse is set to work in a transparent
|
||||
// mode (hit-test invisible).
|
||||
SetVisibility(bInputEnabled && !bTransparentMouseInput ? EVisibility::Visible : EVisibility::HitTestInvisible);
|
||||
|
||||
IMGUI_WIDGET_LOG(VeryVerbose, TEXT("ImGui Widget %d - Visibility updated to '%s'."),
|
||||
ContextIndex, *GetVisibility().ToString());
|
||||
@ -372,13 +374,26 @@ void SImGuiWidget::ReturnFocus()
|
||||
|
||||
void SImGuiWidget::UpdateInputState()
|
||||
{
|
||||
const bool bEnabled = ModuleManager && ModuleManager->GetProperties().IsInputEnabled();
|
||||
if (bInputEnabled != bEnabled)
|
||||
auto& Properties = ModuleManager->GetProperties();
|
||||
auto* ContextPropxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
|
||||
const bool bEnableTransparentMouseInput = Properties.IsMouseInputShared() && !ContextPropxy->IsMouseHoveringAnyWindow();
|
||||
if (bTransparentMouseInput != bEnableTransparentMouseInput)
|
||||
{
|
||||
bTransparentMouseInput = bEnableTransparentMouseInput;
|
||||
if (bInputEnabled)
|
||||
{
|
||||
UpdateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
const bool bEnableInput = Properties.IsInputEnabled();
|
||||
if (bInputEnabled != bEnableInput)
|
||||
{
|
||||
IMGUI_WIDGET_LOG(Log, TEXT("ImGui Widget %d - Input Enabled changed to '%s'."),
|
||||
ContextIndex, TEXT_BOOL(bEnabled));
|
||||
ContextIndex, TEXT_BOOL(bEnableInput));
|
||||
|
||||
bInputEnabled = bEnabled;
|
||||
bInputEnabled = bEnableInput;
|
||||
|
||||
UpdateVisibility();
|
||||
UpdateMouseCursor();
|
||||
@ -391,25 +406,49 @@ void SImGuiWidget::UpdateInputState()
|
||||
InputHandler->OnMouseInputEnabled();
|
||||
}
|
||||
|
||||
// Focus is handled later as it can depend on additional factors.
|
||||
TakeFocus();
|
||||
}
|
||||
else
|
||||
{
|
||||
ReturnFocus();
|
||||
}
|
||||
}
|
||||
|
||||
// We should request a focus, if we are in the input mode and don't have one. But we should wait, if this is not
|
||||
// a foreground window (application), if viewport doesn't have a focus or if console is opened. Note that this
|
||||
// will keep this widget from releasing focus to viewport or other widgets as long as we are in the input mode.
|
||||
if (bInputEnabled && GameViewport->Viewport->IsForegroundWindow() && !HasKeyboardFocus() && !IsConsoleOpened())
|
||||
else if(bInputEnabled)
|
||||
{
|
||||
const auto& ViewportWidget = GameViewport->GetGameViewportWidget();
|
||||
if (ViewportWidget->HasKeyboardFocus() || ViewportWidget->HasFocusedDescendants())
|
||||
|
||||
if (bTransparentMouseInput)
|
||||
{
|
||||
// If mouse is in transparent input mode and focus is lost to viewport, let viewport keep it and disable
|
||||
// the whole input to match that state.
|
||||
if (GameViewport->GetGameViewportWidget()->HasMouseCapture())
|
||||
{
|
||||
Properties.SetInputEnabled(false);
|
||||
UpdateInputState();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Widget tends to lose keyboard focus after console is opened. With non-transparent mouse we can fix that
|
||||
// by manually restoring it.
|
||||
if (!HasKeyboardFocus() && !IsConsoleOpened() && (ViewportWidget->HasKeyboardFocus() || ViewportWidget->HasFocusedDescendants()))
|
||||
{
|
||||
TakeFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SImGuiWidget::UpdateTransparentMouseInput(const FGeometry& AllottedGeometry)
|
||||
{
|
||||
if (bInputEnabled && bTransparentMouseInput)
|
||||
{
|
||||
if (!GameViewport->GetGameViewportWidget()->HasMouseCapture())
|
||||
{
|
||||
const FSlateRenderTransform ImGuiToScreen = ImGuiTransform.Concatenate(AllottedGeometry.GetAccumulatedRenderTransform());
|
||||
InputHandler->OnMouseMove(ImGuiToScreen.Inverse().TransformPoint(FSlateApplication::Get().GetCursorPos()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SImGuiWidget::HandleWindowFocusLost()
|
||||
|
@ -94,6 +94,7 @@ private:
|
||||
|
||||
// Update input state.
|
||||
void UpdateInputState();
|
||||
void UpdateTransparentMouseInput(const FGeometry& AllottedGeometry);
|
||||
void HandleWindowFocusLost();
|
||||
|
||||
void UpdateCanvasControlMode(const FInputEvent& InputEvent);
|
||||
@ -125,6 +126,7 @@ private:
|
||||
bool bInputEnabled = false;
|
||||
bool bForegroundWindow = false;
|
||||
bool bHideMouseCursor = true;
|
||||
bool bTransparentMouseInput = false;
|
||||
|
||||
TSharedPtr<SImGuiCanvasControl> CanvasControlWidget;
|
||||
TWeakPtr<SWidget> PreviousUserFocusedWidget;
|
||||
|
@ -53,6 +53,15 @@ public:
|
||||
/** Toggle whether gamepad input should be shared with game. */
|
||||
void ToggleGamepadInputSharing() { SetGamepadInputShared(!IsGamepadInputShared()); }
|
||||
|
||||
/** Check whether mouse input is shared with game. */
|
||||
bool IsMouseInputShared() const { return bMouseInputShared; }
|
||||
|
||||
/** Set whether mouse input should be shared with game. */
|
||||
void SetMouseInputShared(bool bShared) { bMouseInputShared = bShared; }
|
||||
|
||||
/** Toggle whether mouse input should be shared with game. */
|
||||
void ToggleMouseInputSharing() { SetMouseInputShared(!IsMouseInputShared()); }
|
||||
|
||||
/** Check whether ImGui demo is visible. */
|
||||
bool ShowDemo() const { return bShowDemo; }
|
||||
|
||||
@ -71,6 +80,7 @@ private:
|
||||
|
||||
bool bKeyboardInputShared = false;
|
||||
bool bGamepadInputShared = false;
|
||||
bool bMouseInputShared = false;
|
||||
|
||||
bool bShowDemo = false;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user