// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "AsyncDetailViewDiff.h" #include "UObject/Object.h" #include "Templates/SharedPointer.h" #include "InstanceDataObjectFixupPanel.generated.h" class SLinkableScrollBar; struct FRedirectedPropertyNode : public TSharedFromThis { FRedirectedPropertyNode() = default; FRedirectedPropertyNode(const FRedirectedPropertyNode& Other); FRedirectedPropertyNode(const FPropertyInfo& Info, const TWeakPtr& Parent); FRedirectedPropertyNode(FName InPropertyName, const UE::FPropertyTypeName& InType, int32 InArrayIndex, const TWeakPtr& InParent); TSharedPtr FindOrAdd(const FPropertyPath& Path, int32 PathIndex = 0); TSharedPtr FindOrAdd(const FPropertyInfo& ChildInfo); TSharedPtr FindOrAdd(FName ChildPropertyName, const UE::FPropertyTypeName& ChildType, int32 ChildArrayIndex = 0); bool Remove(const FPropertyPath& Path, int32 PathIndex = 0); bool Remove(const FPropertyInfo& ChildInfo); bool Remove(FName ChildPropertyName, const UE::FPropertyTypeName& ChildType, int32 ChildArrayIndex = 0); TSharedPtr Find(const FPropertyPath& Path, int32 PathIndex = 0) const; TSharedPtr Find(const FPropertyInfo& ChildInfo) const; TSharedPtr Find(FName ChildPropertyName, const UE::FPropertyTypeName& ChildType, int32 ChildArrayIndex = 0) const; bool Move(const FPropertyPath& FromPath, const FPropertyPath& ToPath); int32 FindIndex(const FPropertyInfo& ChildInfo) const; int32 FindIndex(FName ChildPropertyName, const UE::FPropertyTypeName& ChildType, int32 ChildArrayIndex = 0) const; FName PropertyName; UE::FPropertyTypeName Type; int32 ArrayIndex; TWeakPtr Parent; TArray> Children; }; class FInstanceDataObjectFixupPanel : public TSharedFromThis { public: enum class EViewFlags : uint8 { None = 0, HideLooseProperties = (1 << 0), // hide properties with isLoose metadata set to true IncludeOnlySetBySerialization = (1 << 1), // hide properties that weren't set by serialization AllowRemapLooseProperties = (1 << 3), ReadonlyValues = (1 << 2), // displays only properties found in the property bag, allow remapping, and disallow value edits DefaultLeftPanel = IncludeOnlySetBySerialization | AllowRemapLooseProperties | ReadonlyValues, // display only properties found in latest version of the class but allow value edits DefaultRightPanel = HideLooseProperties, }; FInstanceDataObjectFixupPanel( TConstArrayView> InstanceDataObjects, TObjectPtr InstanceDataObjectsOwner, EViewFlags ViewFlags); ~FInstanceDataObjectFixupPanel(); int32 Find(UObject* Value) const; TSharedPtr& GenerateDetailsView(bool bScrollbarOnLeft = false); void SetDiffAgainstLeft(const TSharedPtr& DiffAgainstLeft); void SetDiffAgainstRight(const TSharedPtr& DiffAgainstRight); TSharedPtr GetDiffAgainstLeft() const; TSharedPtr GetDiffAgainstRight() const; bool ShouldSplitterIgnoreRow(const TWeakPtr& DetailTreeNode) const; bool AreAllConflictsRedirected() const; const FPropertyPath& GetOriginalPath(const FPropertyPath& Path) const; void MarkForDelete(const FPropertyPath& Path); // pass-by-copy version for delegates. Use MarkForDelete when possible void OnMarkForDelete(FPropertyPath Path); // mark all conflicted properties for delete (FixupMode only) void AutoApplyMarkDeletedActions(); // the redirected property tree keeps track of which properties in the InstanceDataObject were either set by a property bag during serialization or // redirected to from a floating property. The members of the tree are visible in the left panel. bool IsInRedirectedPropertyTree(const FPropertyPath& Path) const; bool HasViewFlag(EViewFlags Flag); // Initialized by SInstanceDataObjectFixupTool TSharedPtr DetailsView; TSharedPtr LinkableScrollBar; // functor used to generate warnings about a type conversion (or set of conversions) and invoke that conversion on demand struct FTypeConverter { FTypeConverter() = default; // add a conversion that should be performed call operator is invoked. void Push(FProperty* SourceProperty, const void* SourceData, FProperty* DestinationProperty, void* DestinationData); // return whether this is a valid conversion. (only true if all conversions pushed are valid) operator bool() const; // run conversion on all pushed data void operator()() const; // return the most severe text warning for the conversions pushed. FText GetWarning() const; private: enum class EWarning { // sorted by least severe to most severe. SafeConversion, NarrowingConversion, NonInvertibleConversion, InvalidConversion, }; struct FInstanceInfo { FProperty* SourceProperty; const void* SourceData; FProperty* DestinationProperty; void* DestinationData; }; static bool TryConvert(FProperty* SourceProperty, const void* SourceData, FProperty* DestinationProperty, void* DestinationData); static EWarning GenerateWarning(FProperty* SourceProperty, const void* SourceData, FProperty* DestinationProperty); // most severe conversion warning found in all the pushed data EWarning Warning = EWarning::SafeConversion; TArray InstanceInfo; }; FTypeConverter CreateTypeConverter(const FPropertyPath& From, const FPropertyPath& To); private: friend class FInstanceDataObjectFixupSpecification; // for access to Redirects friend class FInstanceDataObjectNameWidgetOverride; friend class UInstanceDataObjectFixupUndoHandler; struct FRevertInfo { TArray OriginalValue; FPropertyPath OriginalPath; bool bHadSkipSerialization; bool bWasHidden; }; void RedirectPropertyHelper(const FPropertyPath& From, const FPropertyPath& To, TOptional& FromRevertInfo, FRevertInfo*& ToRevertInfo); void RedirectProperty(const FPropertyPath& From, const FPropertyPath& To); void RedirectProperty(const FPropertyPath& From, const FPropertyPath& To, const FTypeConverter& TypeConversion); // pass-by-copy version for delegates. Use RedirectProperty when possible void OnRedirectProperty(FPropertyPath From, FPropertyPath To); void OnRedirectProperty(FPropertyPath From, FPropertyPath To, FTypeConverter TypeConversion); void InitRedirectedPropertyTree(); TArray> Instances; // stores either InstanceDataObject property bag or just regular objects TObjectPtr InstancesOwner; // if not null, then it stores the common parent for the instances. // the redirected property tree keeps track of which properties in the InstanceDataObject were either set by a property bag during serialization or // redirected to from a floating property. The members of the tree are visible in the left panel. TSharedPtr RedirectedPropertyTree; TMap RevertInfo; TSet MarkedForDelete; TWeakPtr DiffAgainstLeft; TWeakPtr DiffAgainstRight; EViewFlags ViewFlags = EViewFlags::None; }; UCLASS() class UInstanceDataObjectFixupUndoHandler : public UObject { public: GENERATED_BODY() void Init(const TSharedRef& Panel); void OnRedirect(const FPropertyPath& From, const FPropertyPath& To); virtual void PostEditUndo() override; TWeakPtr InstanceDataObjectPanel; TMap RevertInfo; TSet MarkedForDelete; FPropertyPath RedirectFrom; FPropertyPath RedirectTo; UPROPERTY() int32 ChangeNum = 0; };