WidgetReflector

WidgetReflector

#Overview

name: WidgetReflector

This variable is created as a Console Variable (cvar).

It is referenced in 15 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of WidgetReflector is to provide a debugging and inspection tool for Slate widgets in Unreal Engine 5. It allows developers to examine the structure, properties, and behavior of UI elements in real-time during development.

WidgetReflector is primarily used by the Slate UI system and the development tools within Unreal Engine. It is part of the SlateReflector module, which is a developer-focused tool for inspecting and debugging Slate UI elements.

The value of this variable is typically set through the SetWidgetReflector function in the SlateApplication class. This function is called to initialize the WidgetReflector with the necessary delegates for source code and asset access.

WidgetReflector interacts with several other components of the Slate system, including:

  1. SlateApplication: for handling input events and managing the widget hierarchy
  2. SWidget: the base class for all Slate widgets, which provides debug information to the reflector
  3. FInputEventVisualizer: for visualizing input events

Developers should be aware of the following when using WidgetReflector:

  1. It is primarily a development tool and should not be used in production builds.
  2. It can impact performance when active, so it should be used judiciously during development.
  3. The reflector can be activated using the “WidgetReflector” console command.

Best practices when using WidgetReflector include:

  1. Use it to debug layout issues, input handling problems, and widget hierarchy concerns.
  2. Leverage its ability to visualize widget boundaries and properties to optimize UI designs.
  3. Utilize the event logging features to track down input-related issues.
  4. Remember to disable it when not needed to avoid performance overhead.

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/EngineGlobals.cpp:203

Scope: file

Source code excerpt:

static FAutoConsoleCommand GShowWidgetReflector
(
	TEXT("WidgetReflector"),
	TEXT("Displays the Slate widget reflector"),
	FConsoleCommandDelegate::CreateStatic(ShowWidgetReflector)
);

static FAutoConsoleCommand GShowTextureAtlasReflector
(

#Loc: <Workspace>/Engine/Source/Developer/SlateReflector/Private/SlateReflectorModule.cpp:44

Scope (from outer to inner):

file
class        class FSlateReflectorModuleImpl : public FSlateReflectorModule
function     TSharedRef<SWidget> GetWidgetReflector

Source code excerpt:

	TSharedRef<SWidget> GetWidgetReflector(const TSharedRef<SDockTab>& InParentTab)
	{
		TSharedPtr<SWidgetReflector> WidgetReflector = WidgetReflectorPtr.Pin();

		if (!WidgetReflector.IsValid())
		{
			WidgetReflector = SNew(SWidgetReflector)
				.ParentTab(InParentTab)
				.WidgetSnapshotService(WidgetSnapshotService);
			WidgetReflectorPtr = WidgetReflector;
			FSlateApplication::Get().SetWidgetReflector(WidgetReflector.ToSharedRef());
		}

		return WidgetReflector.ToSharedRef();
	}

	TSharedRef<SWidget> GetAtlasVisualizer( ISlateAtlasProvider* InAtlasProvider )
	{
		return SNew(SAtlasVisualizer)
			.AtlasProvider(InAtlasProvider);

#Loc: <Workspace>/Engine/Source/Developer/SlateReflector/Private/SlateReflectorModule.h:22

Scope (from outer to inner):

file
class        class FSlateReflectorModule : public ISlateReflectorModule

Source code excerpt:

	}
	
	/** Get the current instance of the WidgetReflector */
	virtual TSharedPtr<SWidgetReflector> GetWidgetReflectorInstance() = 0;

	/** Get the current instance of the Input Event Visualizer */
	virtual FInputEventVisualizer* GetInputEventVisualizer() = 0;
};

#Loc: <Workspace>/Engine/Source/Developer/SlateReflector/Private/Widgets/SWidgetReflector.cpp:1066

Scope (from outer to inner):

file
namespace    WidgetReflectorImpl
function     void SWidgetReflector::SaveSettings

Source code excerpt:

void SWidgetReflector::SaveSettings()
{
	GConfig->SetArray(TEXT("WidgetReflector"), TEXT("HiddenReflectorTreeColumns"), HiddenReflectorTreeColumns, *GEditorPerProjectIni);
	GConfig->SetInt(TEXT("WidgetReflector"), TEXT("LastPickingMode"), static_cast<int32>(LastPickingMode), *GEditorPerProjectIni);
}


void SWidgetReflector::LoadSettings()
{
	if (GConfig->DoesSectionExist(TEXT("WidgetReflector"), *GEditorPerProjectIni))
	{
		int32 LastPickingModeAsInt = static_cast<int32>(EWidgetPickingMode::HitTesting);
		GConfig->GetInt(TEXT("WidgetReflector"), TEXT("LastPickingMode"), LastPickingModeAsInt, *GEditorPerProjectIni);
		LastPickingMode = ConvertToWidgetPickingMode(LastPickingModeAsInt);
		if (LastPickingMode == EWidgetPickingMode::None)
		{
			LastPickingMode = EWidgetPickingMode::HitTesting;
		}

		GConfig->GetArray(TEXT("WidgetReflector"), TEXT("HiddenReflectorTreeColumns"), HiddenReflectorTreeColumns, *GEditorPerProjectIni);
	}
}


void SWidgetReflector::OnTabSpawned(const FName& TabIdentifier, const TSharedRef<SDockTab>& SpawnedTab)
{

#Loc: <Workspace>/Engine/Source/Developer/SlateReflector/Public/SlateNavigationEventSimulator.h:17

Scope: file

Source code excerpt:

 * Test the static result via FSlateNavigationEventSimulator.
 * If the simulation doesn't work, your widget is probably not be configured properly.
 * If the simulation does work, use the "WidgetReflector events log" or the "Console Slate Debugger" to get information on the navigation event while playing.
 *  A - If you do not find the event, check it has been consumed by the viewport or by a widget with OnKeyDown.
 *  B - If you find the event, note if the Reply and Consumed widget are different.
 *    i- If they are different, then use the "WidgetReflector routing" tool to understand why another widget has consumed the routing.
 *       For example, the widget can be disabled or it may have a custom OnNavigation event.
 *    ii- If they are not different, then the GameViewportClient or the application may have consumed the event.
 * You may also check if you received the focus event with the "WidgetReflector events log" or with the "Console Slate Debugger".
 */

/**
 * Simulate navigation attempt and collect the result for later display.
 * Some elements may steel the navigation attempt dynamically and can be evaluated until evaluated in game.
 * A list of elements that can still the navigation attempt and modify the result.
 * 1. The viewport may consume the Navigation.
 * 2. A widget may override the OnNavigation function and return a different result dynamically.
 * 3. A widget can change its "enabled" and "supports keyboard focus" flags dynamically during gameplay.
 * 4. The GameViewportClient may consume the event via the CustomNavigationEvent.
 * 5. The application or a widget can consume the "set focus" event. Resulting in different behavior.
 */
class SLATEREFLECTOR_API FSlateNavigationEventSimulator
{
public:
	DECLARE_DELEGATE_RetVal_TwoParams(TOptional<FWidgetPath>, FHandleNavigation, uint32 /*UserIndex*/, const TSharedPtr<SWidget>& /*InDestination*/);

#Loc: <Workspace>/Engine/Source/Developer/TraceInsights/Private/Insights/TraceInsightsModule.cpp:465

Scope (from outer to inner):

file
function     void FTraceInsightsModule::AddAreaForWidgetReflector

Source code excerpt:

	const float DPIScaleFactor = FPlatformApplicationMisc::GetDPIScaleFactorAtPoint(10.0f, 10.0f);

	// Create area and tab for Slate's WidgetReflector.
	Layout->AddArea
	(
		FTabManager::NewArea(800.0f * DPIScaleFactor, 400.0f * DPIScaleFactor)
		->SetWindow(FVector2D(10.0f * DPIScaleFactor, 10.0f * DPIScaleFactor), false)
		->Split
		(

#Loc: <Workspace>/Engine/Source/Programs/ChaosVisualDebugger/Private/ChaosVisualDebuggerMain.cpp:86

Scope (from outer to inner):

file
function     void BuildChaosVDBUserInterface

Source code excerpt:

			(
				// The area contains a single tab with the widget reflector, for debugging purposes
				FTabManager::NewStack()->AddTab("WidgetReflector", ETabState::OpenedTab)
			)
		)*/
		;

	FGlobalTabmanager::Get()->RestoreFrom(Layout, TSharedPtr<SWindow>());
}


int32 ChaosVisualDebuggerMain(const TCHAR* CommandLine)
{
	UE_LOG(LogChaosVisualDebugger, Display, TEXT("Chaos Visual Debugger - Early Prototype Development"));

	// Override the stack size for the thread pool.
	FQueuedThreadPool::OverrideStackSize = 256 * 1024;

	FCommandLine::Set(CommandLine);

#Loc: <Workspace>/Engine/Source/Runtime/Slate/Private/Framework/Application/SlateApplication.cpp:1185

Scope (from outer to inner):

file
function     void FSlateApplication::DrawWindowAndChildren

Source code excerpt:

		const bool bVisualizeLayoutUnderCursor = DrawWindowArgs.WidgetsToVisualizeUnderCursor.IsValid();
		const bool bCapturingFromThisWindow = bVisualizeLayoutUnderCursor && DrawWindowArgs.WidgetsToVisualizeUnderCursor.TopLevelWindow == WindowToDraw;
		TSharedPtr<IWidgetReflector> WidgetReflector = WidgetReflectorPtr.Pin();
		if ( bCapturingFromThisWindow || (WidgetReflector.IsValid() && WidgetReflector->ReflectorNeedsToDrawIn(WindowToDraw)) )
		{
			MaxLayerId = WidgetReflector->Visualize( DrawWindowArgs.WidgetsToVisualizeUnderCursor, WindowElementList, MaxLayerId );
		}

#endif

#if WITH_SLATE_DEBUGGING
		if (GSlateVerifyWidgetLayerId)

#Loc: <Workspace>/Engine/Source/Runtime/Slate/Private/Framework/Application/SlateApplication.cpp:2845

Scope (from outer to inner):

file
function     bool FSlateApplication::SetUserFocus

Source code excerpt:

	}

	TSharedPtr<IWidgetReflector> WidgetReflector = WidgetReflectorPtr.Pin();
	const bool bReflectorShowingFocus = WidgetReflector.IsValid() && WidgetReflector->IsShowingFocus();

	// Get the old Widget information
	const FWeakWidgetPath OldFocusedWidgetPath = User.GetWeakFocusPath();
	TSharedPtr<SWidget> OldFocusedWidget = OldFocusedWidgetPath.IsValid() ? OldFocusedWidgetPath.GetLastWidget().Pin() : TSharedPtr< SWidget >();

	// Get the new widget information by finding the first widget in the path that supports focus

#Loc: <Workspace>/Engine/Source/Runtime/Slate/Private/Framework/Application/SlateApplication.cpp:2973

Scope (from outer to inner):

file
function     bool FSlateApplication::SetUserFocus

Source code excerpt:

	if (bReflectorShowingFocus)
	{
		WidgetReflector->SetWidgetsToVisualize(NewFocusedWidgetPath);
	}
#endif

	// Let the new widget know that it's received keyboard focus
	if (NewFocusedWidget.IsValid())
	{

#Loc: <Workspace>/Engine/Source/Runtime/Slate/Private/Framework/Application/SlateApplication.cpp:4741

Scope (from outer to inner):

file
function     bool FSlateApplication::ProcessKeyDownEvent

Source code excerpt:

		if ( InKeyEvent.GetKey() == EKeys::Escape )
		{
			TSharedPtr<IWidgetReflector> WidgetReflector = WidgetReflectorPtr.Pin();
			const bool bIsWidgetReflectorPicking = WidgetReflector.IsValid() && WidgetReflector->IsInPickingMode();
			if ( bIsWidgetReflectorPicking )
			{
					WidgetReflector->OnWidgetPicked();
					Reply = FReply::Handled();

					return Reply.IsEventHandled();
			}
		}
#endif

#Loc: <Workspace>/Engine/Source/Runtime/Slate/Private/Framework/Application/SlateApplication.cpp:7278

Scope (from outer to inner):

file
function     void FSlateApplication::SetWidgetReflector

Source code excerpt:

}

void FSlateApplication::SetWidgetReflector(const TSharedRef<IWidgetReflector>& WidgetReflector)
{
	if ( SourceCodeAccessDelegate.IsBound() )
	{
		WidgetReflector->SetSourceAccessDelegate(SourceCodeAccessDelegate);
	}

	if ( AssetAccessDelegate.IsBound() )
	{
		WidgetReflector->SetAssetAccessDelegate(AssetAccessDelegate);
	}

	WidgetReflectorPtr = WidgetReflector;
}

bool FSlateApplication::IsDragDropping() const
{
	return GetCursorUser()->IsDragDropping();
}

#Loc: <Workspace>/Engine/Source/Runtime/Slate/Public/Framework/Application/SlateApplication.h:776

Scope: file

Source code excerpt:

	 * Sets the widget reflector.
	 *
	 * @param WidgetReflector The widget reflector to set.
	 */
	SLATE_API void SetWidgetReflector( const TSharedRef<IWidgetReflector>& WidgetReflector );

	/** @param AccessDelegate The delegate to pass along to the widget reflector */
	void SetWidgetReflectorSourceAccessDelegate(FAccessSourceCode AccessDelegate)
	{
		SourceCodeAccessDelegate = AccessDelegate;
	}

	/** @param QueryAccessDelegate The delegate to pass along to the widget reflector */

#Loc: <Workspace>/Engine/Source/Runtime/SlateCore/Public/Types/SlateAttribute.h:111

Scope: file

Source code excerpt:

 *  .cpp
 *	// This is optional. It is still a good practice to implement it since it will allow user to override the default behavior and control the update order.
 *	//The WidgetReflector use that information.
 *	//See NewWayWithout_SLATE_DECLARE_WIDGET_ for an example of how to use the system without the SLATE_DECLARE_WIDGET
 *
 *	SLATE_IMPLEMENT_WIDGET(SMyNewWidget)
 *	void SMyNewWidget::PrivateRegisterAttributes(FSlateAttributeInitializer& AttributeInitializer)
 *	{
 *		SLATE_ADD_MEMBER_ATTRIBUTE_DEFINITION(AttributeInitializer, NewWay, EInvalidationReason::Paint)
 *			.SetPrerequisite("bEnabled"); // SetPrerequisite is not needed here. This is just an example to show how you could do it if needed.
 *
 *		//bEnabled invalidate paint, we need it to invalidate the Layout.
 *		AttributeInitializer.OverrideInvalidationReason("bEnabled",
			FSlateAttributeDescriptor::FInvalidateWidgetReasonAttribute{EWidgetInvalidationReason::Layout | EWidgetInvalidationReason::Paint});
 *	}
 *
 *
 *	// We used to do this. Keep using this method when you are not able to provide the "this" pointer to TSlateAttribute.
 *	class SMyOldWidget : public SLeafWidget
 *	{
 *		SLATE_BEGIN_ARGS(SMyOldWidget)
 *			SLATE_ATTRIBUTE(FLinearColor, Color)
 *		SLATE_END_ARGS()
 *
 *		// Widget will be updated every frame if a TAttribute is bound. (Even if the value didn't change).
 *		virtual bool ComputeVolatility() const override { return SLeafWidget::ComputeVolatility() || OldWay.IsBound(); }
 *
 *		virtual int32 OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const override
 *		{
 *			++LayerId;
 *			FSlateDrawElement::MakeDebugQuad(
 *				OutDrawElements,
 *				LayerId,
 *				AllottedGeometry.ToPaintGeometry(),
 *				OldWay.Get(FLinearColor::White)		// Fetch the value of OldWays and use White, if the value is not set.
 *			);
 *			return LayerId;
 *		}
 *
 *		virtual FVector2D ComputeDesiredSize(float) const override { return IsEnabled() ? FVector2D(10.f, 10.f) : FVector2D(20.f, 20.f); }
 *
 *		void Construct(const FArguments& InArgs)
 *		{
 *			NewWays = InArgs._Color;
 *		}
 *
 *	private:
 *		TAttribute<FLinearColor> OldWay;
 * };
 */


class SWidget;


/** Base struct of all SlateAttribute type. */
struct FSlateAttributeBase
{
	/**
	 * Not all invalidation is supported by SlateAttribute.
	 * ChildOrder: The update of SlateAttribute is done in the SlatePrepass. We can't add or remove children in SlatePrepass.
	 * AttributeRegistration: In FastPath, the SlateAttribute are updated in a loop. The iterator can't be modified while we are looping.
	 */
	template<typename T>
	constexpr static bool IsInvalidateWidgetReasonSupported(T Reason)
	{
		return false;
	}
	constexpr static bool IsInvalidateWidgetReasonSupported(EInvalidateWidgetReason Reason)
	{
		return (Reason & (EInvalidateWidgetReason::ChildOrder | EInvalidateWidgetReason::AttributeRegistration)) == EInvalidateWidgetReason::None;
	}
};


/** Default predicate to compare of Object for SlateAttribute. */
template<typename ComparePredicate = TEqualTo<>>
struct TSlateAttributeComparePredicate
{
	template<typename ObjectType>
	static bool IdenticalTo(const SWidget&, ObjectType&& Lhs, ObjectType&& Rhs)
	{
		// If you have an error here, do you have a operator== const?
		return ComparePredicate{}(Forward<ObjectType>(Lhs), Forward<ObjectType>(Rhs));
	}
};


/** Default predicate to compare FText. */
struct TSlateAttributeFTextComparePredicate
{
	static bool IdenticalTo(const SWidget&, const FText& Lhs, const FText& Rhs)
	{
		return Lhs.IdenticalTo(Rhs, ETextIdenticalModeFlags::DeepCompare | ETextIdenticalModeFlags::LexicalCompareInvariants);
	}
};


/** Predicate that returns the InvalidationReason defined as argument type. */
template<EInvalidateWidgetReason InvalidationReason>
struct TSlateAttributeInvalidationReason
{
	static_assert(FSlateAttributeBase::IsInvalidateWidgetReasonSupported(InvalidationReason), "The invalidation is not supported by the SlateAttribute.");
	static constexpr EInvalidateWidgetReason GetInvalidationReason(const SWidget&) { return InvalidationReason; }
};


/** A structure used to help the user identify deprecated TAttribute that are now TSlateAttribute. */
template<typename ObjectType>
struct TSlateDeprecatedTAttribute
{
	TSlateDeprecatedTAttribute() = default;

	using FGetter = typename TAttribute<ObjectType>::FGetter;

	template<typename OtherType>
	TSlateDeprecatedTAttribute(const OtherType& InInitialValue)	{ }

	TSlateDeprecatedTAttribute(ObjectType&& InInitialValue)	{ }

	template<class SourceType>
	TSlateDeprecatedTAttribute(TSharedRef<SourceType> InUserObject, typename FGetter::template TConstMethodPtr< SourceType > InMethodPtr) {}

	template< class SourceType >
	TSlateDeprecatedTAttribute(SourceType* InUserObject, typename FGetter::template TConstMethodPtr< SourceType > InMethodPtr) { }

	bool IsSet() const { return false; }

	template<typename OtherType>
	void Set(const OtherType& InNewValue) {}

	const ObjectType& Get(const ObjectType& DefaultValue) const { return DefaultValue; }
	const ObjectType& Get() const { static ObjectType Temp; return Temp; }
	FGetter GetBinding() const { return false; }

	void Bind(const FGetter& InGetter) {}
	template<class SourceType>
	void Bind(SourceType* InUserObject, typename FGetter::template TConstMethodPtr< SourceType > InMethodPtr) {}
	bool IsBound() const { return false; }

	bool IdenticalTo(const TAttribute<ObjectType>& InOther) const { return false; }
};

// IWYU pragma: begin_exports
#include "Types/Attributes/SlateAttributeDefinition.inl"
#include "Types/Attributes/SlateAttributeBase.inl"

#include "Types/Attributes/SlateAttributeContained.inl"

#Loc: <Workspace>/Engine/Source/Runtime/SlateCore/Public/Widgets/SWidget.h:886

Scope: file

Source code excerpt:

	/**
	 * Returns all Widgets, including widget hidden from the invalidation system.
	 * This is used by the WidgetReflector.
	 */
	virtual FChildren* Debug_GetChildrenForReflector() { return GetAllChildren(); }
#endif

	/**
	 * Checks to see if this widget supports keyboard focus.  Override this in derived classes.
	 *
	 * @return  True if this widget can take keyboard focus
	 */