Added debug code to ImGui Widget:

- Added ImGui.Debug.Widget console variable to enable/disable widget state debugging.
- Added ImGui ImGui debug for widget state.
- Added logging for widget state transitions.
This commit is contained in:
Sebastian 2017-09-09 11:40:44 +01:00
parent f7816ee1fd
commit f696393089
2 changed files with 108 additions and 2 deletions

View File

@ -12,6 +12,12 @@
#include "Utilities/ScopeGuards.h" #include "Utilities/ScopeGuards.h"
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_BOOL(Val) ((Val) ? TEXT("true") : TEXT("false"))
namespace CVars namespace CVars
{ {
TAutoConsoleVariable<int> InputEnabled(TEXT("ImGui.InputEnabled"), 0, TAutoConsoleVariable<int> InputEnabled(TEXT("ImGui.InputEnabled"), 0,
@ -19,24 +25,44 @@ namespace CVars
TEXT("0: disabled (default)\n") TEXT("0: disabled (default)\n")
TEXT("1: enabled, input is routed to ImGui and with a few exceptions is consumed."), TEXT("1: enabled, input is routed to ImGui and with a few exceptions is consumed."),
ECVF_Default); ECVF_Default);
TAutoConsoleVariable<int> DebugWidget(TEXT("ImGui.Debug.Widget"), 0,
TEXT("Show debug for SImGuiWidget.\n")
TEXT("0: disabled (default)\n")
TEXT("1: enabled."),
ECVF_Default);
} }
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SImGuiWidget::Construct(const FArguments& InArgs) void SImGuiWidget::Construct(const FArguments& InArgs)
{ {
checkf(InArgs._ModuleManager, TEXT("Null Module Manager argument")); checkf(InArgs._ModuleManager, TEXT("Null Module Manager argument"));
ModuleManager = InArgs._ModuleManager; ModuleManager = InArgs._ModuleManager;
ContextIndex = InArgs._ContextIndex; ContextIndex = InArgs._ContextIndex;
ModuleManager->OnPostImGuiUpdate().AddRaw(this, &SImGuiWidget::OnPostImGuiUpdate);
// Sync visibility with default input enabled state. // Sync visibility with default input enabled state.
SetVisibilityFromInputEnabled(); SetVisibilityFromInputEnabled();
// Register to get post-update notifications, so we can clean frame updates.
ModuleManager->OnPostImGuiUpdate().AddRaw(this, &SImGuiWidget::OnPostImGuiUpdate);
// Register self-debug function.
auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
checkf(ContextProxy, TEXT("Missing context during widget construction: ContextIndex = %d"), ContextIndex);
ContextProxy->OnDraw().AddRaw(this, &SImGuiWidget::OnDebugDraw);
} }
END_SLATE_FUNCTION_BUILD_OPTIMIZATION END_SLATE_FUNCTION_BUILD_OPTIMIZATION
SImGuiWidget::~SImGuiWidget() SImGuiWidget::~SImGuiWidget()
{ {
// Remove binding between this widget and its context proxy.
if (auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
{
ContextProxy->OnDraw().RemoveAll(this);
}
// Unregister from post-update notifications.
ModuleManager->OnPostImGuiUpdate().RemoveAll(this); ModuleManager->OnPostImGuiUpdate().RemoveAll(this);
} }
@ -122,6 +148,8 @@ FReply SImGuiWidget::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEv
{ {
Super::OnFocusReceived(MyGeometry, FocusEvent); Super::OnFocusReceived(MyGeometry, FocusEvent);
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Focus Received."), ContextIndex);
// 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, true);
@ -133,6 +161,8 @@ void SImGuiWidget::OnFocusLost(const FFocusEvent& FocusEvent)
{ {
Super::OnFocusLost(FocusEvent); Super::OnFocusLost(FocusEvent);
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Focus Lost."), ContextIndex);
UpdateInputMode(false, IsHovered()); UpdateInputMode(false, IsHovered());
} }
@ -140,6 +170,8 @@ void SImGuiWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent
{ {
Super::OnMouseEnter(MyGeometry, MouseEvent); Super::OnMouseEnter(MyGeometry, MouseEvent);
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Mouse Enter."), ContextIndex);
// If mouse enters while input is active then we need to update mouse buttons because there is a chance that we // If mouse enters while input is active then we need to update mouse buttons because there is a chance that we
// missed some events. // missed some events.
if (InputMode != EInputMode::None) if (InputMode != EInputMode::None)
@ -157,6 +189,8 @@ void SImGuiWidget::OnMouseLeave(const FPointerEvent& MouseEvent)
{ {
Super::OnMouseLeave(MouseEvent); Super::OnMouseLeave(MouseEvent);
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Mouse Leave."), ContextIndex);
UpdateInputMode(HasKeyboardFocus(), false); UpdateInputMode(HasKeyboardFocus(), false);
} }
@ -179,6 +213,9 @@ void SImGuiWidget::SetVisibilityFromInputEnabled()
{ {
// If we don't use input disable hit test to make this widget invisible for cursors hit detection. // If we don't use input disable hit test to make this widget invisible for cursors hit detection.
SetVisibility(bInputEnabled ? EVisibility::Visible : EVisibility::HitTestInvisible); SetVisibility(bInputEnabled ? EVisibility::Visible : EVisibility::HitTestInvisible);
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Visibility updated to '%s'."),
ContextIndex, *GetVisibility().ToString());
} }
void SImGuiWidget::UpdateInputEnabled() void SImGuiWidget::UpdateInputEnabled()
@ -188,6 +225,9 @@ void SImGuiWidget::UpdateInputEnabled()
{ {
bInputEnabled = bEnabled; bInputEnabled = bEnabled;
UE_LOG(LogImGuiWidget, Log, TEXT("ImGui Widget %d - Input Enabled changed to '%s'."),
ContextIndex, TEXT_BOOL(bInputEnabled));
SetVisibilityFromInputEnabled(); SetVisibilityFromInputEnabled();
// Setup input to show cursor and take focus when we use input or clear state and pass focus back to viewport // Setup input to show cursor and take focus when we use input or clear state and pass focus back to viewport
@ -219,6 +259,9 @@ void SImGuiWidget::UpdateInputMode(bool bNeedKeyboard, bool bNeedMouse)
if (InputMode != NewInputMode) if (InputMode != NewInputMode)
{ {
UE_LOG(LogImGuiWidget, Verbose, TEXT("ImGui Widget %d - Input Mode changed from '%s' to '%s'."),
ContextIndex, TEXT_INPUT_MODE(InputMode), TEXT_INPUT_MODE(NewInputMode));
// We need to reset input components if we are either fully shutting down or we are downgrading from full to // We need to reset input components if we are either fully shutting down or we are downgrading from full to
// mouse-only input mode. // mouse-only input mode.
if (NewInputMode == EInputMode::None) if (NewInputMode == EInputMode::None)
@ -291,3 +334,64 @@ FVector2D SImGuiWidget::ComputeDesiredSize(float) const
{ {
return FVector2D{ 3840.f, 2160.f }; return FVector2D{ 3840.f, 2160.f };
} }
// Controls tweaked for 2-columns layout.
namespace TwoColumns
{
static void Value(const char* Label, int Value)
{
ImGui::Text("%s:", Label); ImGui::NextColumn();
ImGui::Text("%d", Value); ImGui::NextColumn();
}
static void Value(const char* Label, bool bValue)
{
ImGui::Text("%s:", Label); ImGui::NextColumn();
ImGui::Text("%ls", TEXT_BOOL(bValue)); ImGui::NextColumn();
}
static void Value(const char* Label, const TCHAR* Value)
{
ImGui::Text("%s:", Label); ImGui::NextColumn();
ImGui::Text("%ls", Value); ImGui::NextColumn();
}
}
void SImGuiWidget::OnDebugDraw()
{
bool bDebug = CVars::DebugWidget.GetValueOnGameThread() > 0;
if (bDebug)
{
ImGui::SetNextWindowSize(ImVec2(300, 200), ImGuiSetCond_Once);
if (ImGui::Begin("ImGui Widget Debug", &bDebug))
{
ImGui::Columns(2, nullptr, false);
TwoColumns::Value("Context Index", ContextIndex);
ImGui::Separator();
TwoColumns::Value("Input Enabled", bInputEnabled);
TwoColumns::Value("Input Mode", TEXT_INPUT_MODE(InputMode));
ImGui::Separator();
TwoColumns::Value("Visibility", *GetVisibility().ToString());
TwoColumns::Value("Is Hovered", IsHovered());
TwoColumns::Value("Is Directly Hovered", IsDirectlyHovered());
TwoColumns::Value("Has Keyboard Input", HasKeyboardFocus());
ImGui::Columns(1);
}
ImGui::End();
if (!bDebug)
{
CVars::DebugWidget->ClearFlags(ECVF_SetByConsole);
CVars::DebugWidget->Set(0);
}
}
}
#undef TEXT_INPUT_MODE
#undef TEXT_BOOL

View File

@ -91,6 +91,8 @@ private:
virtual FVector2D ComputeDesiredSize(float) const override; virtual FVector2D ComputeDesiredSize(float) const override;
void OnDebugDraw();
FImGuiModuleManager* ModuleManager = nullptr; FImGuiModuleManager* ModuleManager = nullptr;
mutable TArray<FSlateVertex> VertexBuffer; mutable TArray<FSlateVertex> VertexBuffer;