2017-03-26 20:32:57 +00:00
|
|
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
|
|
|
|
|
|
|
#include "ImGuiPrivatePCH.h"
|
|
|
|
|
|
|
|
#include "SImGuiWidget.h"
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
#include "ImGuiContextManager.h"
|
2017-04-22 15:38:04 +00:00
|
|
|
#include "ImGuiContextProxy.h"
|
|
|
|
#include "ImGuiInteroperability.h"
|
2017-03-26 20:32:57 +00:00
|
|
|
#include "ImGuiModuleManager.h"
|
|
|
|
#include "TextureManager.h"
|
|
|
|
#include "Utilities/ScopeGuards.h"
|
|
|
|
|
|
|
|
|
2017-09-10 20:05:37 +00:00
|
|
|
// High enough z-order guarantees that ImGui output is rendered on top of the game UI.
|
|
|
|
constexpr int32 IMGUI_WIDGET_Z_ORDER = 10000;
|
|
|
|
|
|
|
|
|
2017-09-09 10:40:44 +00:00
|
|
|
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"))
|
|
|
|
|
|
|
|
|
2017-09-02 17:42:41 +00:00
|
|
|
namespace CVars
|
|
|
|
{
|
|
|
|
TAutoConsoleVariable<int> InputEnabled(TEXT("ImGui.InputEnabled"), 0,
|
|
|
|
TEXT("Allows to enable or disable ImGui input mode.\n")
|
|
|
|
TEXT("0: disabled (default)\n")
|
|
|
|
TEXT("1: enabled, input is routed to ImGui and with a few exceptions is consumed."),
|
|
|
|
ECVF_Default);
|
2017-09-09 10:40:44 +00:00
|
|
|
|
|
|
|
TAutoConsoleVariable<int> DebugWidget(TEXT("ImGui.Debug.Widget"), 0,
|
|
|
|
TEXT("Show debug for SImGuiWidget.\n")
|
|
|
|
TEXT("0: disabled (default)\n")
|
|
|
|
TEXT("1: enabled."),
|
|
|
|
ECVF_Default);
|
2017-09-02 17:42:41 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 20:32:57 +00:00
|
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
void SImGuiWidget::Construct(const FArguments& InArgs)
|
|
|
|
{
|
|
|
|
checkf(InArgs._ModuleManager, TEXT("Null Module Manager argument"));
|
2017-09-09 10:40:44 +00:00
|
|
|
|
2017-03-26 20:32:57 +00:00
|
|
|
ModuleManager = InArgs._ModuleManager;
|
2017-08-28 19:29:07 +00:00
|
|
|
ContextIndex = InArgs._ContextIndex;
|
2017-04-22 15:38:04 +00:00
|
|
|
|
2017-09-02 17:42:41 +00:00
|
|
|
// Sync visibility with default input enabled state.
|
|
|
|
SetVisibilityFromInputEnabled();
|
2017-09-09 10:40:44 +00:00
|
|
|
|
|
|
|
// 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);
|
2017-03-26 20:32:57 +00:00
|
|
|
}
|
|
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
SImGuiWidget::~SImGuiWidget()
|
|
|
|
{
|
2017-09-09 10:40:44 +00:00
|
|
|
// 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.
|
2017-04-22 15:38:04 +00:00
|
|
|
ModuleManager->OnPostImGuiUpdate().RemoveAll(this);
|
|
|
|
}
|
|
|
|
|
2017-09-10 20:05:37 +00:00
|
|
|
void SImGuiWidget::AttachToViewport(UGameViewportClient* InGameViewport, bool bResetInput)
|
|
|
|
{
|
|
|
|
checkf(InGameViewport, TEXT("Null InGameViewport"));
|
|
|
|
checkf(!GameViewport.IsValid() || GameViewport.Get() == InGameViewport,
|
|
|
|
TEXT("Widget is attached to another game viewport and will be available for reuse only after this session ")
|
|
|
|
TEXT("ends. ContextIndex = %d, CurrentGameViewport = %s, InGameViewport = %s"),
|
|
|
|
ContextIndex, *GameViewport->GetName(), InGameViewport->GetName());
|
|
|
|
|
|
|
|
if (bResetInput)
|
|
|
|
{
|
|
|
|
ResetInputState();
|
|
|
|
}
|
|
|
|
|
|
|
|
GameViewport = InGameViewport;
|
|
|
|
GameViewport->AddViewportWidgetContent(SNew(SWeakWidget).PossiblyNullContent(SharedThis(this)), IMGUI_WIDGET_Z_ORDER);
|
|
|
|
}
|
|
|
|
|
2017-09-02 17:42:41 +00:00
|
|
|
void SImGuiWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
|
|
|
{
|
|
|
|
Super::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
FReply SImGuiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent)
|
|
|
|
{
|
|
|
|
InputState.AddCharacter(CharacterEvent.GetCharacter());
|
2017-08-19 20:19:38 +00:00
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
return FReply::Handled();
|
|
|
|
}
|
|
|
|
|
|
|
|
FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
|
|
|
|
{
|
|
|
|
InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), true);
|
2017-08-19 20:19:38 +00:00
|
|
|
CopyModifierKeys(KeyEvent);
|
2017-04-22 15:38:04 +00:00
|
|
|
|
|
|
|
// If this is tilde key then let input through and release the focus to allow console to process it.
|
|
|
|
if (KeyEvent.GetKey() == EKeys::Tilde)
|
|
|
|
{
|
|
|
|
return FReply::Unhandled();
|
|
|
|
}
|
|
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
}
|
|
|
|
|
|
|
|
FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
|
|
|
|
{
|
|
|
|
InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), false);
|
2017-08-19 20:19:38 +00:00
|
|
|
CopyModifierKeys(KeyEvent);
|
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
return FReply::Handled();
|
|
|
|
}
|
|
|
|
|
|
|
|
FReply SImGuiWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
|
|
{
|
|
|
|
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), true);
|
2017-08-19 20:19:38 +00:00
|
|
|
CopyModifierKeys(MouseEvent);
|
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
return FReply::Handled();
|
|
|
|
}
|
|
|
|
|
|
|
|
FReply SImGuiWidget::OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
|
|
{
|
|
|
|
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), true);
|
2017-08-19 20:19:38 +00:00
|
|
|
CopyModifierKeys(MouseEvent);
|
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
return FReply::Handled();
|
|
|
|
}
|
|
|
|
|
|
|
|
FReply SImGuiWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
|
|
{
|
|
|
|
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), false);
|
2017-08-19 20:19:38 +00:00
|
|
|
CopyModifierKeys(MouseEvent);
|
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
return FReply::Handled();
|
|
|
|
}
|
|
|
|
|
|
|
|
FReply SImGuiWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
|
|
{
|
|
|
|
InputState.AddMouseWheelDelta(MouseEvent.GetWheelDelta());
|
2017-08-19 20:19:38 +00:00
|
|
|
CopyModifierKeys(MouseEvent);
|
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
return FReply::Handled();
|
|
|
|
}
|
|
|
|
|
|
|
|
FReply SImGuiWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
|
|
{
|
|
|
|
InputState.SetMousePosition(MouseEvent.GetScreenSpacePosition() - MyGeometry.AbsolutePosition);
|
2017-08-19 20:19:38 +00:00
|
|
|
CopyModifierKeys(MouseEvent);
|
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
return FReply::Handled();
|
|
|
|
}
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
FReply SImGuiWidget::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& FocusEvent)
|
|
|
|
{
|
|
|
|
Super::OnFocusReceived(MyGeometry, FocusEvent);
|
|
|
|
|
2017-09-09 10:40:44 +00:00
|
|
|
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Focus Received."), ContextIndex);
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
// 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);
|
|
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SImGuiWidget::OnFocusLost(const FFocusEvent& FocusEvent)
|
|
|
|
{
|
|
|
|
Super::OnFocusLost(FocusEvent);
|
|
|
|
|
2017-09-09 10:40:44 +00:00
|
|
|
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Focus Lost."), ContextIndex);
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
UpdateInputMode(false, IsHovered());
|
|
|
|
}
|
|
|
|
|
|
|
|
void SImGuiWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
|
|
{
|
|
|
|
Super::OnMouseEnter(MyGeometry, MouseEvent);
|
|
|
|
|
2017-09-09 10:40:44 +00:00
|
|
|
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Mouse Enter."), ContextIndex);
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
// If mouse enters while input is active then we need to update mouse buttons because there is a chance that we
|
|
|
|
// missed some events.
|
|
|
|
if (InputMode != EInputMode::None)
|
|
|
|
{
|
|
|
|
for (const FKey& Button : { EKeys::LeftMouseButton, EKeys::MiddleMouseButton, EKeys::RightMouseButton, EKeys::ThumbMouseButton, EKeys::ThumbMouseButton2 })
|
|
|
|
{
|
|
|
|
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(Button), MouseEvent.IsMouseButtonDown(Button));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateInputMode(HasKeyboardFocus(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SImGuiWidget::OnMouseLeave(const FPointerEvent& MouseEvent)
|
|
|
|
{
|
|
|
|
Super::OnMouseLeave(MouseEvent);
|
|
|
|
|
2017-09-09 10:40:44 +00:00
|
|
|
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Mouse Leave."), ContextIndex);
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
UpdateInputMode(HasKeyboardFocus(), false);
|
|
|
|
}
|
|
|
|
|
2017-08-19 20:19:38 +00:00
|
|
|
void SImGuiWidget::CopyModifierKeys(const FInputEvent& InputEvent)
|
|
|
|
{
|
|
|
|
InputState.SetControlDown(InputEvent.IsControlDown());
|
|
|
|
InputState.SetShiftDown(InputEvent.IsShiftDown());
|
|
|
|
InputState.SetAltDown(InputEvent.IsAltDown());
|
|
|
|
}
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
void SImGuiWidget::CopyModifierKeys(const FPointerEvent& MouseEvent)
|
|
|
|
{
|
|
|
|
if (InputMode == EInputMode::MouseOnly)
|
|
|
|
{
|
|
|
|
CopyModifierKeys(static_cast<const FInputEvent&>(MouseEvent));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-10 20:05:37 +00:00
|
|
|
void SImGuiWidget::ResetInputState()
|
|
|
|
{
|
|
|
|
bInputEnabled = false;
|
|
|
|
SetVisibilityFromInputEnabled();
|
|
|
|
UpdateInputMode(false, false);
|
|
|
|
}
|
|
|
|
|
2017-09-02 17:42:41 +00:00
|
|
|
void SImGuiWidget::SetVisibilityFromInputEnabled()
|
|
|
|
{
|
|
|
|
// If we don't use input disable hit test to make this widget invisible for cursors hit detection.
|
|
|
|
SetVisibility(bInputEnabled ? EVisibility::Visible : EVisibility::HitTestInvisible);
|
2017-09-09 10:40:44 +00:00
|
|
|
|
|
|
|
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Visibility updated to '%s'."),
|
|
|
|
ContextIndex, *GetVisibility().ToString());
|
2017-09-02 17:42:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SImGuiWidget::UpdateInputEnabled()
|
|
|
|
{
|
|
|
|
const bool bEnabled = CVars::InputEnabled.GetValueOnGameThread() > 0;
|
|
|
|
if (bInputEnabled != bEnabled)
|
|
|
|
{
|
|
|
|
bInputEnabled = bEnabled;
|
|
|
|
|
2017-09-09 10:40:44 +00:00
|
|
|
UE_LOG(LogImGuiWidget, Log, TEXT("ImGui Widget %d - Input Enabled changed to '%s'."),
|
|
|
|
ContextIndex, TEXT_BOOL(bInputEnabled));
|
|
|
|
|
2017-09-02 17:42:41 +00:00
|
|
|
SetVisibilityFromInputEnabled();
|
|
|
|
|
2017-09-10 20:05:37 +00:00
|
|
|
// Setup input to show cursor and to pass keyboard/user focus between viewport and widget. Note that we should
|
|
|
|
// only pass focus if it is inside of the parent viewport, otherwise we would be stealing from other viewports
|
|
|
|
// or windows.
|
2017-09-02 17:42:41 +00:00
|
|
|
auto& Slate = FSlateApplication::Get();
|
|
|
|
if (bInputEnabled)
|
|
|
|
{
|
2017-09-10 20:05:37 +00:00
|
|
|
const auto& ViewportWidget = GameViewport->GetGameViewportWidget();
|
|
|
|
if (ViewportWidget->HasKeyboardFocus() || ViewportWidget->HasFocusedDescendants())
|
|
|
|
{
|
|
|
|
// Remember where is user focus, so we will have an option to restore it.
|
|
|
|
PreviousUserFocusedWidget = Slate.GetUserFocusedWidget(Slate.GetUserIndexForKeyboard());
|
|
|
|
|
|
|
|
Slate.ResetToDefaultPointerInputSettings();
|
|
|
|
Slate.SetKeyboardFocus(SharedThis(this));
|
|
|
|
}
|
2017-09-02 17:42:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (Slate.GetKeyboardFocusedWidget().Get() == this)
|
|
|
|
{
|
2017-09-10 20:05:37 +00:00
|
|
|
Slate.ResetToDefaultPointerInputSettings();
|
|
|
|
Slate.SetUserFocus(Slate.GetUserIndexForKeyboard(),
|
|
|
|
PreviousUserFocusedWidget.IsValid() ? PreviousUserFocusedWidget.Pin() : GameViewport->GetGameViewportWidget());
|
2017-09-02 17:42:41 +00:00
|
|
|
}
|
|
|
|
|
2017-09-10 20:05:37 +00:00
|
|
|
PreviousUserFocusedWidget.Reset();
|
|
|
|
|
2017-09-02 17:42:41 +00:00
|
|
|
UpdateInputMode(false, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
void SImGuiWidget::UpdateInputMode(bool bNeedKeyboard, bool bNeedMouse)
|
|
|
|
{
|
|
|
|
const EInputMode NewInputMode =
|
|
|
|
bNeedKeyboard ? EInputMode::MouseAndKeyboard :
|
|
|
|
bNeedMouse ? EInputMode::MouseOnly :
|
|
|
|
EInputMode::None;
|
|
|
|
|
|
|
|
if (InputMode != NewInputMode)
|
|
|
|
{
|
2017-09-09 10:40:44 +00:00
|
|
|
UE_LOG(LogImGuiWidget, Verbose, TEXT("ImGui Widget %d - Input Mode changed from '%s' to '%s'."),
|
|
|
|
ContextIndex, TEXT_INPUT_MODE(InputMode), TEXT_INPUT_MODE(NewInputMode));
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
// We need to reset input components if we are either fully shutting down or we are downgrading from full to
|
|
|
|
// mouse-only input mode.
|
|
|
|
if (NewInputMode == EInputMode::None)
|
|
|
|
{
|
|
|
|
InputState.ResetState();
|
|
|
|
}
|
|
|
|
else if (InputMode == EInputMode::MouseAndKeyboard)
|
|
|
|
{
|
|
|
|
InputState.ResetKeyboardState();
|
|
|
|
}
|
|
|
|
|
|
|
|
InputMode = NewInputMode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
void SImGuiWidget::OnPostImGuiUpdate()
|
|
|
|
{
|
2017-08-28 19:29:07 +00:00
|
|
|
if (InputMode != EInputMode::None)
|
|
|
|
{
|
|
|
|
InputState.ClearUpdateState();
|
|
|
|
}
|
2017-04-22 15:38:04 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 20:32:57 +00:00
|
|
|
int32 SImGuiWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect,
|
|
|
|
FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& WidgetStyle, bool bParentEnabled) const
|
|
|
|
{
|
2017-08-28 19:29:07 +00:00
|
|
|
if (FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
|
|
|
|
{
|
|
|
|
// Calculate offset that will transform vertex positions to screen space - rounded to avoid half pixel offsets.
|
|
|
|
const FVector2D VertexPositionOffset{ FMath::RoundToFloat(MyClippingRect.Left), FMath::RoundToFloat(MyClippingRect.Top) };
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
// Convert clipping rectangle to format required by Slate vertex.
|
|
|
|
const FSlateRotatedRect VertexClippingRect{ MyClippingRect };
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
for (const auto& DrawList : ContextProxy->GetDrawData())
|
|
|
|
{
|
|
|
|
DrawList.CopyVertexData(VertexBuffer, VertexPositionOffset, VertexClippingRect);
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
// Get access to the Slate scissor rectangle defined in Slate Core API, so we can customize elements drawing.
|
|
|
|
extern SLATECORE_API TOptional<FShortRect> GSlateScissorRect;
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
auto GSlateScissorRectSaver = ScopeGuards::MakeStateSaver(GSlateScissorRect);
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
int IndexBufferOffset = 0;
|
|
|
|
for (int CommandNb = 0; CommandNb < DrawList.NumCommands(); CommandNb++)
|
|
|
|
{
|
|
|
|
const auto& DrawCommand = DrawList.GetCommand(CommandNb);
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
DrawList.CopyIndexData(IndexBuffer, IndexBufferOffset, DrawCommand.NumElements);
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
// Advance offset by number of copied elements to position it for the next command.
|
|
|
|
IndexBufferOffset += DrawCommand.NumElements;
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
// Get texture resource handle for this draw command (null index will be also mapped to a valid texture).
|
|
|
|
const FSlateResourceHandle& Handle = ModuleManager->GetTextureManager().GetTextureHandle(DrawCommand.TextureId);
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
// Transform clipping rectangle to screen space and set in Slate, to apply it to elements that we draw.
|
|
|
|
GSlateScissorRect = FShortRect{ DrawCommand.ClippingRect.OffsetBy(MyClippingRect.GetTopLeft()).IntersectionWith(MyClippingRect) };
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
// Add elements to the list.
|
|
|
|
FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId, Handle, VertexBuffer, IndexBuffer, nullptr, 0, 0);
|
|
|
|
}
|
2017-03-26 20:32:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return LayerId;
|
|
|
|
}
|
|
|
|
|
|
|
|
FVector2D SImGuiWidget::ComputeDesiredSize(float) const
|
|
|
|
{
|
|
|
|
return FVector2D{ 3840.f, 2160.f };
|
|
|
|
}
|
2017-09-09 10:40:44 +00:00
|
|
|
|
|
|
|
// Controls tweaked for 2-columns layout.
|
|
|
|
namespace TwoColumns
|
|
|
|
{
|
2017-09-10 20:05:37 +00:00
|
|
|
static void GroupName(const char* Name)
|
|
|
|
{
|
|
|
|
ImGui::TextColored({ 0.5f, 0.5f, 0.5f, 1.f }, Name); ImGui::NextColumn(); ImGui::NextColumn();
|
|
|
|
}
|
|
|
|
|
2017-09-09 10:40:44 +00:00
|
|
|
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)
|
|
|
|
{
|
2017-09-10 20:05:37 +00:00
|
|
|
ImGui::SetNextWindowSize(ImVec2(380, 320), ImGuiSetCond_Once);
|
2017-09-09 10:40:44 +00:00
|
|
|
if (ImGui::Begin("ImGui Widget Debug", &bDebug))
|
|
|
|
{
|
|
|
|
ImGui::Columns(2, nullptr, false);
|
|
|
|
|
|
|
|
TwoColumns::Value("Context Index", ContextIndex);
|
2017-09-10 20:05:37 +00:00
|
|
|
TwoColumns::Value("Game Viewport", *GameViewport->GetName());
|
2017-09-09 10:40:44 +00:00
|
|
|
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
TwoColumns::Value("Input Enabled", bInputEnabled);
|
|
|
|
TwoColumns::Value("Input Mode", TEXT_INPUT_MODE(InputMode));
|
|
|
|
|
|
|
|
ImGui::Separator();
|
|
|
|
|
2017-09-10 20:05:37 +00:00
|
|
|
const float GroupIndent = 5.f;
|
|
|
|
|
|
|
|
TwoColumns::GroupName("Widget");
|
|
|
|
ImGui::Indent(GroupIndent);
|
|
|
|
{
|
|
|
|
TwoColumns::Value("Visibility", *GetVisibility().ToString());
|
|
|
|
TwoColumns::Value("Is Hovered", IsHovered());
|
|
|
|
TwoColumns::Value("Is Directly Hovered", IsDirectlyHovered());
|
|
|
|
TwoColumns::Value("Has Keyboard Input", HasKeyboardFocus());
|
|
|
|
}
|
|
|
|
ImGui::Unindent(GroupIndent);
|
|
|
|
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
TwoColumns::GroupName("Viewport Widget");
|
|
|
|
ImGui::Indent(GroupIndent);
|
|
|
|
{
|
|
|
|
const auto& ViewportWidget = GameViewport->GetGameViewportWidget();
|
|
|
|
TwoColumns::Value("Is Hovered", ViewportWidget->IsHovered());
|
|
|
|
TwoColumns::Value("Is Directly Hovered", ViewportWidget->IsDirectlyHovered());
|
|
|
|
TwoColumns::Value("Has Mouse Capture", ViewportWidget->HasMouseCapture());
|
|
|
|
TwoColumns::Value("Has Keyboard Input", ViewportWidget->HasKeyboardFocus());
|
|
|
|
TwoColumns::Value("Has Focused Descendants", ViewportWidget->HasFocusedDescendants());
|
|
|
|
auto Widget = PreviousUserFocusedWidget.Pin();
|
|
|
|
TwoColumns::Value("Previous User Focused", Widget.IsValid() ? *Widget->GetTypeAsString() : TEXT("None"));
|
|
|
|
}
|
|
|
|
ImGui::Unindent(GroupIndent);
|
2017-09-09 10:40:44 +00:00
|
|
|
|
|
|
|
ImGui::Columns(1);
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
|
|
|
|
if (!bDebug)
|
|
|
|
{
|
|
|
|
CVars::DebugWidget->ClearFlags(ECVF_SetByConsole);
|
|
|
|
CVars::DebugWidget->Set(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef TEXT_INPUT_MODE
|
|
|
|
#undef TEXT_BOOL
|