Files
UnrealEngine/Engine/Source/Developer/AutomationDriver/Private/SlateWidgetElement.cpp
2025-05-18 13:04:45 +08:00

551 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SlateWidgetElement.h"
#include "IApplicationElement.h"
#include "Framework/Application/SlateApplication.h"
#include "Locators/SlateWidgetLocatorByUniqueTag.h"
#include "MetaData/DriverUniqueTagMetaData.h"
#include "Framework/MetaData/DriverIdMetaData.h"
#include "Widgets/Text/SRichTextBlock.h"
#include "Widgets/Input/SMultiLineEditableTextBox.h"
#include "Widgets/Text/SMultiLineEditableText.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SEditableText.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Widgets/Input/SCheckBox.h"
#include "UObject/NameTypes.h"
class FSlateWidgetElement
: public IApplicationElement
{
public:
virtual ~FSlateWidgetElement()
{ }
virtual FString ToDebugString() const
{
const TSharedRef<SWidget> Widget = WidgetPath.Widgets.Last().Widget;
const TArray<TSharedRef<FDriverIdMetaData>> AllIdMetaData = Widget->GetAllMetaData<FDriverIdMetaData>();
const TArray<TSharedRef<FTagMetaData>> AllTagMetaData = Widget->GetAllMetaData<FTagMetaData>();
//example (STextBlock) 0x00082034 [#Piano|Keyboard] : SlateWidgetElement.cpp(20)
FString DebugString;
const FString Address = FString::Printf(TEXT("0x%p"), &Widget.Get());
DebugString += TEXT("(") + Widget->GetTypeAsString() + TEXT(") ") + Address;
if (AllIdMetaData.Num() + AllTagMetaData.Num() > 0 || !Widget->GetTag().IsNone())
{
DebugString += TEXT(" [");
bool bHasPrecedingIdentifier = false;
for (const TSharedRef<FDriverIdMetaData>& MetaData : AllIdMetaData)
{
if (bHasPrecedingIdentifier)
{
DebugString += TEXT("|");
}
DebugString += TEXT("#");
DebugString += MetaData->Id.ToString();
bHasPrecedingIdentifier = true;
}
if (!Widget->GetTag().IsNone())
{
if (bHasPrecedingIdentifier)
{
DebugString += TEXT("|");
}
DebugString += Widget->GetTag().ToString();
}
for (const TSharedRef<FTagMetaData>& MetaData : AllTagMetaData)
{
if (bHasPrecedingIdentifier)
{
DebugString += TEXT("|");
}
DebugString += MetaData->Tag.ToString();
bHasPrecedingIdentifier = true;
}
DebugString += TEXT("]");
}
DebugString += TEXT(" : ") + Widget->GetReadableLocation();
return DebugString;
}
virtual FVector2D GetAbsolutePosition() const override
{
return WidgetPath.Widgets.Last().Geometry.LocalToAbsolute(FVector2D::ZeroVector);
}
virtual FVector2D GetSize() const override
{
return WidgetPath.Widgets.Last().Geometry.GetDrawSize();
}
virtual TSharedPtr<FGenericWindow> GetWindow() const override
{
const TSharedPtr<SWindow> Window = WidgetPath.GetWindow();
if (!Window.IsValid())
{
return nullptr;
}
return Window->GetNativeWindow();
}
virtual bool IsVisible() const override
{
static FName SWindowType(TEXT("SWindow"));
const TSharedRef<SWidget> Widget = WidgetPath.Widgets.Last().Widget;
if (Widget->GetVisibility().IsVisible())
{
FVector2D FakeCursorPosition = GetAbsolutePosition() + (GetSize() / 2);
TArray<TSharedRef<SWindow>> Windows;
if (WidgetPath.TopLevelWindow.IsValid())
{
Windows.Add(WidgetPath.TopLevelWindow.ToSharedRef());
}
else if (WidgetPath.Widgets[0].Widget->GetType() == SWindowType)
{
Windows.Add(StaticCastSharedRef<SWindow>(WidgetPath.Widgets[0].Widget));
}
else
{
return false;
}
FWidgetPath UnderCursor = FSlateApplication::Get().LocateWindowUnderMouse(FakeCursorPosition, Windows, true);
int32 Count = UnderCursor.Widgets.Num();
for (int32 Index = 0; Index < Count; Index++)
{
TSharedRef<SWidget> FoundWidget = UnderCursor.Widgets[Index].Widget;
if (FoundWidget == Widget)
{
return true;
}
}
}
return false;
}
virtual bool IsInteractable() const override
{
static FName SWindowType(TEXT("SWindow"));
const TSharedRef<SWidget> Widget = WidgetPath.Widgets.Last().Widget;
if (Widget->IsEnabled() && Widget->GetVisibility().IsHitTestVisible())
{
FVector2D FakeCursorPosition = GetAbsolutePosition() + (GetSize() / 2);
TArray<TSharedRef<SWindow>> Windows;
if (WidgetPath.TopLevelWindow.IsValid())
{
Windows.Add(WidgetPath.TopLevelWindow.ToSharedRef());
}
else if (WidgetPath.Widgets[0].Widget->GetType() == SWindowType)
{
Windows.Add(StaticCastSharedRef<SWindow>(WidgetPath.Widgets[0].Widget));
}
else
{
return false;
}
FWidgetPath UnderCursor = FSlateApplication::Get().LocateWindowUnderMouse(FakeCursorPosition, Windows, false);
int32 Count = UnderCursor.Widgets.Num();
for (int32 Index = 0; Index < Count; Index++)
{
TSharedRef<SWidget> FoundWidget = UnderCursor.Widgets[Index].Widget;
if (FoundWidget == Widget)
{
return true;
}
}
}
return false;
}
virtual bool IsChecked() const override
{
static FName SCheckBoxType(TEXT("SCheckBox"));
const TSharedRef<SWidget> RootWidget = WidgetPath.Widgets.Last().Widget;
TArray<TSharedRef<SWidget>> Stack;
Stack.Push(RootWidget);
bool bFoundCheck = false;
bool bResult = false;
while (Stack.Num() > 0)
{
const TSharedRef<SWidget> Widget = Stack.Pop();
if (Widget->GetType() == SCheckBoxType)
{
if (bFoundCheck == true)
{
return false;
}
bResult = StaticCastSharedRef<SCheckBox, SWidget>(Widget)->IsChecked();
bFoundCheck = true;
}
else
{
FChildren* ChildWidgets = Widget->GetChildren();
int32 ChildCount = ChildWidgets->Num();
for (int32 i = 0; i < ChildCount; i++)
{
Stack.Push(ChildWidgets->GetChildAt(i));
}
}
}
return bResult;
}
virtual FText GetText() const override
{
static FName SRichTextBlockType(TEXT("SRichTextBlock"));
static FName STextBlockType(TEXT("STextBlock"));
static FName SEditableTextType(TEXT("SEditableText"));
static FName SEditableTextBoxType(TEXT("SEditableTextBox"));
static FName SMultiLineEditableTextType(TEXT("SMultiLineEditableText"));
static FName SMultiLineEditableTextBoxType(TEXT("SMultiLineEditableTextBox"));
const TSharedRef<SWidget> RootWidget = WidgetPath.Widgets.Last().Widget;
TArray<TSharedRef<SWidget>> Stack;
Stack.Push(RootWidget);
bool bFoundText = false;
FText Result = FText::GetEmpty();
while(Stack.Num() > 0)
{
const TSharedRef<SWidget> Widget = Stack.Pop();
if (Widget->GetType() == STextBlockType)
{
if (bFoundText == true)
{
return FText::GetEmpty();
}
Result = StaticCastSharedRef<STextBlock, SWidget>(Widget)->GetText();
bFoundText = true;
}
else if (Widget->GetType() == SEditableTextType)
{
if (bFoundText == true)
{
return FText::GetEmpty();
}
Result = StaticCastSharedRef<SEditableText, SWidget>(Widget)->GetText();
bFoundText = true;
}
else if (Widget->GetType() == SEditableTextBoxType)
{
if (bFoundText == true)
{
return FText::GetEmpty();
}
Result = StaticCastSharedRef<SEditableTextBox, SWidget>(Widget)->GetText();
bFoundText = true;
}
else if (Widget->GetType() == SRichTextBlockType)
{
if (bFoundText == true)
{
return FText::GetEmpty();
}
Result = StaticCastSharedRef<SRichTextBlock, SWidget>(Widget)->GetText();
bFoundText = true;
}
else if (Widget->GetType() == SMultiLineEditableTextType)
{
if (bFoundText == true)
{
return FText::GetEmpty();
}
Result = StaticCastSharedRef<SMultiLineEditableText, SWidget>(Widget)->GetText();
bFoundText = true;
}
else if (Widget->GetType() == SMultiLineEditableTextBoxType)
{
if (bFoundText == true)
{
return FText::GetEmpty();
}
Result = StaticCastSharedRef<SMultiLineEditableTextBox, SWidget>(Widget)->GetText();
bFoundText = true;
}
else
{
FChildren* ChildWidgets = Widget->GetChildren();
int32 ChildCount = ChildWidgets->Num();
for (int32 i = 0; i < ChildCount; i++)
{
Stack.Push(ChildWidgets->GetChildAt(i));
}
}
}
return Result;
}
virtual TSharedRef<IElementLocator, ESPMode::ThreadSafe> CreateLocator() const override
{
const TSharedRef<SWidget> Widget = WidgetPath.Widgets.Last().Widget;
const TSharedRef<FDriverUniqueTagMetaData> UniqueMetaData = MakeShareable(new FDriverUniqueTagMetaData());
Widget->AddMetadata(UniqueMetaData);
return FSlateWidgetLocatorByUniqueTagFactory::Create(UniqueMetaData);
}
virtual bool CanFocus() const override
{
const TSharedRef<SWidget> Widget = WidgetPath.Widgets.Last().Widget;
return Widget->SupportsKeyboardFocus();
}
virtual bool Focus() const override
{
return FSlateApplication::Get().SetUserFocus(FSlateApplication::Get().GetUserIndexForKeyboard(), WidgetPath, EFocusCause::SetDirectly);
}
virtual bool Focus(uint32 UserIndex) const override
{
return FSlateApplication::Get().SetUserFocus(UserIndex, WidgetPath, EFocusCause::SetDirectly);
}
virtual bool IsFocused() const override
{
const TSharedRef<SWidget> Widget = WidgetPath.Widgets.Last().Widget;
return FSlateApplication::Get().GetUserFocusedWidget(FSlateApplication::Get().GetUserIndexForKeyboard()) == Widget;
}
virtual bool IsFocused(uint32 UserIndex) const override
{
const TSharedRef<SWidget> Widget = WidgetPath.Widgets.Last().Widget;
return FSlateApplication::Get().GetUserFocusedWidget(UserIndex) == Widget;
}
virtual bool HasFocusedDescendants() const override
{
const TSharedRef<SWidget> Widget = WidgetPath.Widgets.Last().Widget;
return FSlateApplication::Get().HasFocusedDescendants(Widget);
}
virtual bool HasFocusedDescendants(uint32 UserIndex) const override
{
const TSharedRef<SWidget> Widget = WidgetPath.Widgets.Last().Widget;
return FSlateApplication::Get().HasUserFocusedDescendants(Widget, UserIndex);
}
virtual bool IsHovered() const override
{
const TSharedRef<SWidget> Widget = WidgetPath.Widgets.Last().Widget;
return Widget->IsHovered();
}
static TSharedPtr<SScrollBar> HasScrollableDescendants(const FWidgetPath& WidgetPath, int32& ErrorCode)
{
TArray<TSharedRef<SWidget>> IgnoreWidgets;
return HasScrollableDescendants(WidgetPath, IgnoreWidgets, ErrorCode);
}
static TSharedPtr<SScrollBar> HasScrollableDescendants(const FWidgetPath& WidgetPath, const TArray<TSharedRef<SWidget>>& IgnoreWidgets, int32& ErrorCode)
{
static FName SScrollBarType(TEXT("SScrollBar"));
TArray<TSharedRef<SWidget>> Stack;
Stack.Push(WidgetPath.Widgets.Last().Widget);
TSharedPtr<SScrollBar> ScrollBar;
while (Stack.Num() > 0)
{
const TSharedRef<SWidget> Widget = Stack.Pop();
if (Widget->GetType() == SScrollBarType)
{
TSharedPtr<SScrollBar> FoundScrollBar = StaticCastSharedRef<SScrollBar, SWidget>(Widget);
if (FoundScrollBar->IsNeeded())
{
if (ScrollBar.IsValid() && ScrollBar != FoundScrollBar)
{
ErrorCode = 2;
return nullptr;
}
ScrollBar = FoundScrollBar;
}
}
else
{
FChildren* ChildWidgets = Widget->GetChildren();
int32 ChildCount = ChildWidgets->Num();
for (int32 i = 0; i < ChildCount; i++)
{
const TSharedRef<SWidget> ChildWidget = ChildWidgets->GetChildAt(i);
if (!IgnoreWidgets.Contains(ChildWidget))
{
Stack.Push(ChildWidget);
}
}
}
}
if (!ScrollBar.IsValid())
{
ErrorCode = 1;
return nullptr;
}
ErrorCode = 0;
return ScrollBar;
}
virtual bool IsScrollable() const override
{
EOrientation ScrollOrientation;
return IsScrollable(ScrollOrientation);
}
virtual bool IsScrollable(EOrientation& OutScrollOrientation) const override
{
int32 ErrorCode;
const TSharedPtr<SScrollBar> ScrollBar = HasScrollableDescendants(WidgetPath, ErrorCode);
if (ScrollBar.IsValid())
{
OutScrollOrientation = ScrollBar->GetOrientation();
}
return ScrollBar.IsValid();
}
virtual bool IsScrolledToBeginning() const override
{
int32 ErrorCode;
const TSharedPtr<SScrollBar> ScrollBar = HasScrollableDescendants(WidgetPath, ErrorCode);
if (!ScrollBar.IsValid())
{
return true;
}
return ScrollBar->DistanceFromTop() < KINDA_SMALL_NUMBER;
}
virtual bool IsScrolledToEnd() const override
{
int32 ErrorCode;
const TSharedPtr<SScrollBar> ScrollBar = HasScrollableDescendants(WidgetPath, ErrorCode);
if (!ScrollBar.IsValid())
{
return true;
}
return ScrollBar->DistanceFromBottom() < KINDA_SMALL_NUMBER;
}
virtual TSharedPtr<IApplicationElement> GetScrollableParent() const override
{
FWidgetPath ParentWidgetPath = WidgetPath;
ParentWidgetPath.Widgets.Remove(ParentWidgetPath.Widgets.Num() - 1);
TArray<TSharedRef<SWidget>> IgnoreWidgets;
IgnoreWidgets.Add(WidgetPath.Widgets.Last().Widget);
bool bFoundScrollableParent = false;
for (int Index = WidgetPath.Widgets.Num() - 2; Index >= 0; Index--)
{
const TSharedRef<SWidget> ParentWidget = WidgetPath.Widgets[Index].Widget;
int32 ErrorCode;
const TSharedPtr<SScrollBar> ScrollBar = HasScrollableDescendants(ParentWidgetPath, IgnoreWidgets, ErrorCode);
if (ScrollBar.IsValid())
{
bFoundScrollableParent = true;
break;
}
else if (ErrorCode == 2)
{
bFoundScrollableParent = false;
break;
}
IgnoreWidgets.Add(ParentWidget);
ParentWidgetPath.Widgets.Remove(ParentWidgetPath.Widgets.Num() - 1);
}
if (bFoundScrollableParent)
{
return FSlateWidgetElementFactory::Create(ParentWidgetPath);
}
return nullptr;
}
virtual void* GetRawElement() const override
{
if (!WidgetPath.IsValid())
{
return nullptr;
}
return (void*)(&WidgetPath);
}
private:
FSlateWidgetElement(
const FWidgetPath& InWidgetPath)
: WidgetPath(InWidgetPath)
{ }
private:
const FWidgetPath WidgetPath;
friend FSlateWidgetElementFactory;
};
TSharedRef<IApplicationElement> FSlateWidgetElementFactory::Create(
const FWidgetPath& WidgetPath)
{
return MakeShareable(new FSlateWidgetElement(WidgetPath));
}