// Copyright Epic Games, Inc. All Rights Reserved. #include "Models/NavigationSimulationNode.h" #include "Dom/JsonObject.h" #include "Dom/JsonValue.h" #include "Layout/WidgetPath.h" #include "Models/WidgetReflectorNode.h" #include "Widgets/SWidget.h" #define LOCTEXT_NAMESPACE "NavigationSimulationNode" /** * ----------------------------------------------------------------------------- * FNavigationSimulationWidgetInfo * ----------------------------------------------------------------------------- */ FNavigationSimulationWidgetInfo::FNavigationSimulationWidgetInfo(const TSharedPtr& Widget) : WidgetPtr(FWidgetReflectorNodeUtils::GetWidgetAddress(Widget)) , WidgetLive(Widget) , WidgetTypeAndShortName(FWidgetReflectorNodeUtils::GetWidgetTypeAndShortName(Widget)) , bHasGeometry(false) { } FNavigationSimulationWidgetInfo::FNavigationSimulationWidgetInfo(const FWidgetPath& WidgetPath) : WidgetPtr(0) , bHasGeometry(WidgetPath.IsValid()) { if (bHasGeometry) { WidgetLive = WidgetPath.GetLastWidget(); WidgetPtr = FWidgetReflectorNodeUtils::GetWidgetAddress(WidgetPath.GetLastWidget()); WidgetTypeAndShortName = FWidgetReflectorNodeUtils::GetWidgetTypeAndShortName(WidgetPath.GetLastWidget()); WidgetGeometry = WidgetPath.Widgets.Last().Geometry; } } FNavigationSimulationWidgetNodeItem::FNavigationSimulationWidgetNodeItem(const FSlateNavigationEventSimulator::FSimulationResult& Result) : NavigationType(Result.NavigationType) , Destination(Result.NavigationDestination) , ReplyEventHandler(Result.NavigationReply.EventHandler) , ReplyFocusRecipient(Result.NavigationReply.FocusRecipient) , ReplyBoundaryRule(Result.NavigationReply.BoundaryRule) , RoutedReason(Result.RoutedReason) , WidgetThatShouldReceivedFocus(Result.WidgetThatShouldReceivedFocus) , FocusedWidget(Result.FocusedWidgetPath) , bIsDynamic(Result.bIsDynamic) , bAlwaysHandleNavigationAttempt(Result.bAlwaysHandleNavigationAttempt) , bCanFindWidgetForSetFocus(Result.bCanFindWidgetForSetFocus) , bRoutedHandlerHasNavigationMeta(Result.bRoutedHandlerHasNavigationMeta) , bHandledByViewport(Result.bHandledByViewport) { } void FNavigationSimulationWidgetNodeItem::ResetLiveWidget() { Destination.WidgetLive.Reset(); ReplyEventHandler.WidgetLive.Reset(); ReplyFocusRecipient.WidgetLive.Reset(); WidgetThatShouldReceivedFocus.WidgetLive.Reset(); FocusedWidget.WidgetLive.Reset(); } FNavigationSimulationWidgetNode::FNavigationSimulationWidgetNode(ENavigationSimulationNodeType NodeType, const FWidgetPath& InNavigationSource) : NavigationSource(InNavigationSource) , NodeType(NodeType) { if (NodeType == ENavigationSimulationNodeType::Snapshot) { NavigationSource.WidgetLive.Reset(); } } namespace NavigationSimulationNodeUtilsInternal { TArray BuildNavigationSimulationNodeList(ENavigationSimulationNodeType NodeType, const TArray& SimulationResult) { TMap WidgetMap; for (const FSlateNavigationEventSimulator::FSimulationResult& Element : SimulationResult) { if (Element.IsValid()) { const SWidget* ElementWidget = &Element.NavigationSource.GetLastWidget().Get(); if (!WidgetMap.Contains(ElementWidget)) { WidgetMap.Add(ElementWidget, MakeShared(NodeType, Element.NavigationSource)); } FNavigationSimulationWidgetNodeItem& NodeItem = WidgetMap[ElementWidget]->Simulations.Emplace_GetRef(Element); if (NodeType == ENavigationSimulationNodeType::Snapshot) { NodeItem.ResetLiveWidget(); } } } TArray GeneratedArray; WidgetMap.GenerateValueArray(GeneratedArray); return MoveTemp(GeneratedArray); } } TSharedRef FNavigationSimulationWidgetNode::ToJson(const FNavigationSimulationWidgetNode& Node) { struct Internal { static FString ConvertPtrIntToString(FNavigationSimulationWidgetInfo::TPointerAsInt Value) { return FWidgetReflectorNodeUtils::WidgetAddressToString(Value); } static TSharedRef CreateJsonValue(const UE::Slate::FDeprecateVector2DResult& InVec2D) { return CreateJsonValue(FVector2D(InVec2D)); } static TSharedRef CreateJsonValue(const FVector2D& InVec2D) { TArray> StructJsonArray; StructJsonArray.Add(MakeShared(InVec2D.X)); StructJsonArray.Add(MakeShared(InVec2D.Y)); return MakeShared(StructJsonArray); } static TSharedRef CreateJsonValue(const FMatrix2x2& InMatrix) { float m00, m01, m10, m11; InMatrix.GetMatrix(m00, m01, m10, m11); TArray> StructJsonArray; StructJsonArray.Add(MakeShared(m00)); StructJsonArray.Add(MakeShared(m01)); StructJsonArray.Add(MakeShared(m10)); StructJsonArray.Add(MakeShared(m11)); return MakeShared(StructJsonArray); } static TSharedRef CreateJsonValue(const FSlateLayoutTransform& InLayoutTransform) { TSharedRef StructJsonObject = MakeShared(); StructJsonObject->SetNumberField(TEXT("Scale"), InLayoutTransform.GetScale()); StructJsonObject->SetField(TEXT("Translation"), CreateJsonValue(InLayoutTransform.GetTranslation())); return MakeShared(StructJsonObject); } static TSharedRef CreateJsonValue(const FSlateRenderTransform& InRenderTransform) { TSharedRef StructJsonObject = MakeShared(); StructJsonObject->SetField(TEXT("Matrix"), CreateJsonValue(InRenderTransform.GetMatrix())); StructJsonObject->SetField(TEXT("Translation"), CreateJsonValue(InRenderTransform.GetTranslation())); return MakeShared(StructJsonObject); } static TSharedRef CreateJsonValue(const FGeometry& InGeo) { TSharedRef StructJsonObject = MakeShared(); StructJsonObject->SetField(TEXT("AccumulatedRenderTransform"), CreateJsonValue(InGeo.GetAccumulatedRenderTransform())); StructJsonObject->SetField(TEXT("AccumulatedLayoutTransform"), CreateJsonValue(InGeo.GetAccumulatedLayoutTransform())); StructJsonObject->SetField(TEXT("LocalSize"), CreateJsonValue(InGeo.GetLocalSize())); return MakeShared(StructJsonObject); } static TSharedRef CreateJsonValue(const FNavigationSimulationWidgetInfo& Value) { TSharedRef StructJsonObject = MakeShared(); StructJsonObject->SetStringField(TEXT("WidgetPtr"), ConvertPtrIntToString(Value.WidgetPtr)); StructJsonObject->SetStringField(TEXT("WidgetTypeAndShortName"), Value.WidgetTypeAndShortName.ToString()); StructJsonObject->SetBoolField(TEXT("bHasGeometry"), Value.bHasGeometry); if (Value.bHasGeometry) { StructJsonObject->SetField(TEXT("WidgetGeometry"), CreateJsonValue(Value.WidgetGeometry)); } return MakeShared(StructJsonObject); } static TSharedRef CreateJsonValue(const FNavigationSimulationWidgetNodeItem& Value) { TSharedRef StructJsonObject = MakeShared(); StructJsonObject->SetNumberField(TEXT("NavigationType"), (int32)Value.NavigationType); StructJsonObject->SetNumberField(TEXT("ReplyBoundaryRule"), (int32)Value.ReplyBoundaryRule); StructJsonObject->SetNumberField(TEXT("RoutedReason"), (int32)Value.RoutedReason); StructJsonObject->SetField(TEXT("Destination"), CreateJsonValue(Value.Destination)); StructJsonObject->SetField(TEXT("ReplyEventHandler"), CreateJsonValue(Value.ReplyEventHandler)); StructJsonObject->SetField(TEXT("ReplyFocusRecipient"), CreateJsonValue(Value.ReplyFocusRecipient)); StructJsonObject->SetField(TEXT("WidgetThatShouldReceivedFocus"), CreateJsonValue(Value.WidgetThatShouldReceivedFocus)); StructJsonObject->SetField(TEXT("FocusedWidget"), CreateJsonValue(Value.FocusedWidget)); StructJsonObject->SetBoolField(TEXT("bIsDynamic"), Value.bIsDynamic); StructJsonObject->SetBoolField(TEXT("bAlwaysHandleNavigationAttempt"), Value.bAlwaysHandleNavigationAttempt); StructJsonObject->SetBoolField(TEXT("bCanFindWidgetForSetFocus"), Value.bCanFindWidgetForSetFocus); StructJsonObject->SetBoolField(TEXT("bRoutedHandlerHasNavigationMeta"), Value.bRoutedHandlerHasNavigationMeta); StructJsonObject->SetBoolField(TEXT("bHandledByViewport"), Value.bHandledByViewport); return MakeShared(StructJsonObject); } }; TSharedRef RootJsonObject = MakeShared(); RootJsonObject->SetField(TEXT("NavigationSource"), Internal::CreateJsonValue(Node.NavigationSource)); RootJsonObject->SetNumberField(TEXT("NodeType"), (int32)Node.NodeType); TArray> SimulationArray; for (const FNavigationSimulationWidgetNodeItem& Item : Node.Simulations) { SimulationArray.Add(Internal::CreateJsonValue(Item)); } RootJsonObject->SetArrayField(TEXT("Simulations"), SimulationArray); return MakeShared(RootJsonObject); } FNavigationSimulationWidgetNodePtr FNavigationSimulationWidgetNode::FromJson(const TSharedRef& RootJsonValue) { struct Internal { static void ParseJsonValue(const TSharedPtr& InJsonValue, FVector2D& Out) { if (!InJsonValue.IsValid()) { return; } const TArray>& StructJsonArray = InJsonValue->AsArray(); check(StructJsonArray.Num() == 2); Out = FVector2D((float)StructJsonArray[0]->AsNumber(), (float)StructJsonArray[1]->AsNumber()); } static FNavigationSimulationWidgetInfo::TPointerAsInt ParsePtrIntFromString(const FString& Value) { FWidgetReflectorNodeBase::TPointerAsInt Result = 0; LexFromString(Result, *Value); return Result; } static void ParseJsonValue(const TSharedPtr& InJsonValue, FMatrix2x2& Out) { if (!InJsonValue.IsValid()) { return; } const TArray>& StructJsonArray = InJsonValue->AsArray(); check(StructJsonArray.Num() == 4); Out = FMatrix2x2( (float)StructJsonArray[0]->AsNumber(), (float)StructJsonArray[1]->AsNumber(), (float)StructJsonArray[2]->AsNumber(), (float)StructJsonArray[3]->AsNumber() ); } static void ParseJsonValue(const TSharedPtr& InJsonValue, FSlateLayoutTransform& Out) { if (!InJsonValue.IsValid()) { return; } const TSharedPtr& StructJsonObject = InJsonValue->AsObject(); check(StructJsonObject.IsValid()); float Scale = (float)StructJsonObject->GetNumberField(TEXT("Scale")); FVector2D Translation; ParseJsonValue(StructJsonObject->GetField(TEXT("Translation")), Translation); Out = FSlateLayoutTransform(Scale, Translation); } static void ParseJsonValue(const TSharedPtr& InJsonValue, FSlateRenderTransform& Out) { if (!InJsonValue.IsValid()) { return; } const TSharedPtr& StructJsonObject = InJsonValue->AsObject(); check(StructJsonObject.IsValid()); FMatrix2x2 Matrix; ParseJsonValue(StructJsonObject->GetField(TEXT("Matrix")), Matrix); FVector2D Translation; ParseJsonValue(StructJsonObject->GetField(TEXT("Translation")), Translation); Out = FSlateRenderTransform(Matrix, Translation); } static void ParseJsonValue(const TSharedPtr& InJsonValue, FGeometry& Out) { if (!InJsonValue.IsValid()) { return; } const TSharedPtr& StructJsonObject = InJsonValue->AsObject(); check(StructJsonObject.IsValid()); FSlateLayoutTransform LayoutTransform; FSlateRenderTransform RenderTransform; ParseJsonValue(StructJsonObject->GetField(TEXT("AccumulatedRenderTransform")), RenderTransform); ParseJsonValue(StructJsonObject->GetField(TEXT("AccumulatedLayoutTransform")), LayoutTransform); FVector2D LocalSize; ParseJsonValue(StructJsonObject->GetField(TEXT("LocalSize")), LocalSize); Out = FGeometry::MakeRoot(LocalSize, LayoutTransform, RenderTransform); } static void ParseJsonValue(const TSharedPtr& InJsonValue, FNavigationSimulationWidgetInfo& Out) { if (!InJsonValue.IsValid()) { return; } const TSharedPtr& StructJsonObject = InJsonValue->AsObject(); check(StructJsonObject.IsValid()); Out.WidgetPtr = ParsePtrIntFromString(StructJsonObject->GetStringField(TEXT("WidgetPtr"))); Out.WidgetTypeAndShortName = FText::FromString(StructJsonObject->GetStringField(TEXT("WidgetTypeAndShortName"))); Out.bHasGeometry = StructJsonObject->GetBoolField(TEXT("bHasGeometry")); if (Out.bHasGeometry) { ParseJsonValue(StructJsonObject->GetField(TEXT("WidgetGeometry")), Out.WidgetGeometry); } } static void ParseJsonValue(const TSharedPtr& InJsonValue, FNavigationSimulationWidgetNodeItem& Out) { if (!InJsonValue.IsValid()) { return; } const TSharedPtr& StructJsonObject = InJsonValue->AsObject(); check(StructJsonObject.IsValid()); Out.NavigationType = (EUINavigation)(int32)StructJsonObject->GetNumberField(TEXT("NavigationType")); Out.ReplyBoundaryRule = (EUINavigationRule)(int32)StructJsonObject->GetNumberField(TEXT("ReplyBoundaryRule")); Out.RoutedReason = (FSlateNavigationEventSimulator::ERoutedReason)(int32)StructJsonObject->GetNumberField(TEXT("RoutedReason")); ParseJsonValue(StructJsonObject->GetField(TEXT("Destination")), Out.Destination); ParseJsonValue(StructJsonObject->GetField(TEXT("ReplyEventHandler")), Out.ReplyEventHandler); ParseJsonValue(StructJsonObject->GetField(TEXT("ReplyFocusRecipient")), Out.ReplyFocusRecipient); ParseJsonValue(StructJsonObject->GetField(TEXT("WidgetThatShouldReceivedFocus")), Out.WidgetThatShouldReceivedFocus); ParseJsonValue(StructJsonObject->GetField(TEXT("FocusedWidget")), Out.FocusedWidget); Out.bIsDynamic = StructJsonObject->GetBoolField(TEXT("bIsDynamic")); Out.bAlwaysHandleNavigationAttempt = StructJsonObject->GetBoolField(TEXT("bAlwaysHandleNavigationAttempt")); Out.bCanFindWidgetForSetFocus = StructJsonObject->GetBoolField(TEXT("bCanFindWidgetForSetFocus")); Out.bRoutedHandlerHasNavigationMeta = StructJsonObject->GetBoolField(TEXT("bRoutedHandlerHasNavigationMeta")); Out.bHandledByViewport = StructJsonObject->GetBoolField(TEXT("bHandledByViewport")); } }; TSharedRef Result = MakeShared(); const TSharedPtr& RootJsonObject = RootJsonValue->AsObject(); check(RootJsonObject.IsValid()); Internal::ParseJsonValue(RootJsonObject->GetField(TEXT("NavigationSource")), Result->NavigationSource); Result->NodeType = (ENavigationSimulationNodeType)((int32)RootJsonObject->GetNumberField(TEXT("NodeType"))); const TArray>& SimulationsJsonArray = RootJsonObject->GetArrayField(TEXT("Simulations")); for (const TSharedPtr& SimulationsJsonValue : SimulationsJsonArray) { FNavigationSimulationWidgetNodeItem NodeItem; Internal::ParseJsonValue(SimulationsJsonValue.ToSharedRef(), NodeItem); Result->Simulations.Add(MoveTemp(NodeItem)); } return Result; } TArray FNavigationSimulationNodeUtils::BuildNavigationSimulationNodeListForLive(const TArray& SimulationResult) { return NavigationSimulationNodeUtilsInternal::BuildNavigationSimulationNodeList(ENavigationSimulationNodeType::Live, SimulationResult); } TArray FNavigationSimulationNodeUtils::BuildNavigationSimulationNodeListForSnapshot(const TArray& SimulationResult) { return NavigationSimulationNodeUtilsInternal::BuildNavigationSimulationNodeList(ENavigationSimulationNodeType::Snapshot, SimulationResult); } TArray FNavigationSimulationNodeUtils::FindReflectorNodes(const TArray& Nodes, const TArray>& ToFind) { TArray Result; Result.Reserve(ToFind.Num()); for (const TSharedRef& ReflectorNode : ToFind) { int32 FoundIndex = IndexOfSnapshotWidget(Nodes, ReflectorNode->GetWidgetAddress()); if (Nodes.IsValidIndex(FoundIndex)) { Result.Add(Nodes[FoundIndex]); } } return Result; } int32 FNavigationSimulationNodeUtils::IndexOfLiveWidget(const TArray& Nodes, const TSharedPtr& WidgetToFind) { return Nodes.IndexOfByPredicate([WidgetToFind](const FNavigationSimulationWidgetNodePtr& Node) { return Node->NavigationSource.WidgetLive == WidgetToFind; }); } int32 FNavigationSimulationNodeUtils::IndexOfSnapshotWidget(const TArray& Nodes, FNavigationSimulationWidgetInfo::TPointerAsInt WidgetToFind) { return Nodes.IndexOfByPredicate([WidgetToFind](const FNavigationSimulationWidgetNodePtr& Node) { return Node->NavigationSource.WidgetPtr == WidgetToFind; }); } #undef LOCTEXT_NAMESPACE