Property manipulation now getting closer

This commit is contained in:
2026-04-02 04:39:05 -04:00
parent 2e2bb89de0
commit 5fc9ce63ad
3 changed files with 82 additions and 70 deletions

View File

@@ -8,39 +8,10 @@
#include "Components/PanelSlot.h"
#include "MaterialGraph/MaterialGraphNode.h"
#include "Materials/MaterialExpression.h"
#include "DetailTreeNode.h"
#include "PropertyNode.h"
#include "ObjectPropertyNode.h"
#include "PropertyNode.h"
#include "ObjectPropertyNode.h"
#include "ItemPropertyNode.h"
#include "CategoryPropertyNode.h"
/////////////////////////////////////////////////////////////////////////////
//
// IsSubObject
//
/////////////////////////////////////////////////////////////////////////////
bool WingPropHandle::IsSubObject(const TSharedRef<IDetailTreeNode>& Node)
{
FDetailTreeNode& DetailNode = static_cast<FDetailTreeNode&>(*Node);
TSharedPtr<FPropertyNode> PropNode = DetailNode.GetPropertyNode();
if (!PropNode.IsValid()) return false;
FPropertyNode* Current = PropNode.Get();
while (true)
{
if (Current == nullptr) return false;
FPropertyNode *Parent = Current->GetParentNode();
if (Current->AsObjectNode()) return (Parent != nullptr);
Current = Parent;
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Get Generator
// Get Root
//
/////////////////////////////////////////////////////////////////////////////
@@ -52,29 +23,66 @@ TSharedRef<IPropertyRowGenerator> WingPropHandle::CreateGenerator()
return Module.CreatePropertyRowGenerator(Args);
}
TSharedRef<IPropertyRowGenerator> WingPropHandle::GetGeneratorForObject(UObject* Obj)
WingPropHandle::Root& WingPropHandle::GetRootForObject(UObject* Obj)
{
for (auto& Pair : Generators)
for (Root& R : Roots)
{
if (Pair.Key == Obj) return Pair.Value.ToSharedRef();
if (R.Base == (uint8*)Obj) return R;
}
TSharedRef<IPropertyRowGenerator> Gen = CreateGenerator();
Gen->SetObjects({Obj});
Generators.Add({Obj, Gen});
return Gen;
Root& R = Roots.AddDefaulted_GetRef();
R.Struct = Obj->GetClass();
R.Base = (uint8*)Obj;
R.End = R.Base + R.Struct->GetStructureSize();
R.Generator = Gen;
return R;
}
TSharedRef<IPropertyRowGenerator> WingPropHandle::GetGeneratorForStruct(const UStruct* ScriptStruct, uint8* Data)
WingPropHandle::Root& WingPropHandle::GetRootForStruct(const UStruct* ScriptStruct, uint8* Data)
{
for (auto& Pair : Generators)
for (Root& R : Roots)
{
if (Pair.Key == Data) return Pair.Value.ToSharedRef();
if (R.Base == Data) return R;
}
TSharedRef<IPropertyRowGenerator> Gen = CreateGenerator();
TSharedPtr<FStructOnScope> Wrapper = MakeShared<FStructOnScope>(ScriptStruct, Data);
Gen->SetStructure(Wrapper);
Generators.Add({Data, Gen});
return Gen;
Root& R = Roots.AddDefaulted_GetRef();
R.Struct = ScriptStruct;
R.Base = Data;
R.End = Data + ScriptStruct->GetStructureSize();
R.Generator = Gen;
return R;
}
/////////////////////////////////////////////////////////////////////////////
//
// IsInsideRootObject
//
/////////////////////////////////////////////////////////////////////////////
bool WingPropHandle::IsInsideRootObject(const Root& Root, IPropertyHandle& Handle)
{
// Walk up to the topmost property handle that still has a property.
// Keep the shared pointers alive so the raw pointer stays valid.
TSharedPtr<IPropertyHandle> Held;
IPropertyHandle* Top = &Handle;
while (true)
{
TSharedPtr<IPropertyHandle> Parent = Top->GetParentHandle();
if (!Parent.IsValid() || !Parent->GetProperty()) break;
Held = Parent;
Top = Held.Get();
}
// Get the address of the topmost property's data.
void* Addr = nullptr;
if (Top->GetValueData(Addr) != FPropertyAccess::Success || !Addr)
return false;
uint8* DataPtr = (uint8*)Addr;
return DataPtr >= Root.Base && DataPtr < Root.End;
}
/////////////////////////////////////////////////////////////////////////////
@@ -85,7 +93,6 @@ TSharedRef<IPropertyRowGenerator> WingPropHandle::GetGeneratorForStruct(const US
void WingPropHandle::AllTreeNodesRecursive(const TSharedRef<IDetailTreeNode>& Node, FlatTree& Out)
{
if (IsSubObject(Node)) return;
if (Node->GetNodeType() == EDetailNodeType::Category)
{
TArray<TSharedRef<IDetailTreeNode>> Children;
@@ -100,12 +107,12 @@ void WingPropHandle::AllTreeNodesRecursive(const TSharedRef<IDetailTreeNode>& No
Out.Add(&*Node);
}
WingPropHandle::FlatTree WingPropHandle::AllTreeNodes(TSharedRef<IPropertyRowGenerator> Generator)
WingPropHandle::FlatTree WingPropHandle::AllTreeNodes(Root& Root)
{
FlatTree Result;
for (const TSharedRef<IDetailTreeNode>& Root : Generator->GetRootTreeNodes())
for (const TSharedRef<IDetailTreeNode>& TreeRoot : Root.Generator->GetRootTreeNodes())
{
AllTreeNodesRecursive(Root, Result);
AllTreeNodesRecursive(TreeRoot, Result);
}
return Result;
}
@@ -116,33 +123,34 @@ WingPropHandle::FlatTree WingPropHandle::AllTreeNodes(TSharedRef<IPropertyRowGen
//
/////////////////////////////////////////////////////////////////////////////
WingPropHandle::Handles WingPropHandle::AllProperties(TSharedRef<IPropertyRowGenerator> Generator, EPropertyFlags Filter)
WingPropHandle::Handles WingPropHandle::AllProperties(Root& Root, EPropertyFlags Filter)
{
Handles Result;
for (IDetailTreeNode* Node : AllTreeNodes(Generator))
for (IDetailTreeNode* Node : AllTreeNodes(Root))
{
TSharedPtr<IPropertyHandle> Handle = Node->CreatePropertyHandle();
if (Handle.IsValid() && Handle->GetProperty())
{
if (Filter == CPF_None || Handle->GetProperty()->HasAllPropertyFlags(Filter))
{
if (Filter != CPF_None && !Handle->GetProperty()->HasAllPropertyFlags(Filter))
continue;
if (!IsInsideRootObject(Root, *Handle))
continue;
Result.Add(Handle);
}
}
}
return Result;
}
WingPropHandle::Handles WingPropHandle::AllProperties(UObject* Obj, EPropertyFlags Filter)
{
if (!Obj) return {};
return AllProperties(GetGeneratorForObject(Obj), Filter);
return AllProperties(GetRootForObject(Obj), Filter);
}
WingPropHandle::Handles WingPropHandle::AllProperties(const UStruct* ScriptStruct, uint8* Data, EPropertyFlags Filter)
{
if (!ScriptStruct || !Data) return {};
return AllProperties(GetGeneratorForStruct(ScriptStruct, Data), Filter);
return AllProperties(GetRootForStruct(ScriptStruct, Data), Filter);
}
/////////////////////////////////////////////////////////////////////////////
@@ -151,12 +159,14 @@ WingPropHandle::Handles WingPropHandle::AllProperties(const UStruct* ScriptStruc
//
/////////////////////////////////////////////////////////////////////////////
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(TSharedRef<IPropertyRowGenerator> Generator, FName Name)
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(Root& Root, FName Name)
{
for (IDetailTreeNode* Node : AllTreeNodes(Generator))
for (IDetailTreeNode* Node : AllTreeNodes(Root))
{
if (Node->GetNodeName() == Name)
return Node->CreatePropertyHandle();
if (Node->GetNodeName() != Name) continue;
TSharedPtr<IPropertyHandle> Handle = Node->CreatePropertyHandle();
if (Handle.IsValid() && IsInsideRootObject(Root, *Handle))
return Handle;
}
return nullptr;
}
@@ -164,13 +174,13 @@ TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(TSharedRef<IPropertyRo
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(UObject* Obj, FName Name)
{
if (!Obj) return nullptr;
return NamedProperty(GetGeneratorForObject(Obj), Name);
return NamedProperty(GetRootForObject(Obj), Name);
}
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name)
{
if (!ScriptStruct || !Data) return nullptr;
return NamedProperty(GetGeneratorForStruct(ScriptStruct, Data), Name);
return NamedProperty(GetRootForStruct(ScriptStruct, Data), Name);
}
/////////////////////////////////////////////////////////////////////////////
@@ -231,4 +241,3 @@ WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable, E
return Result;
}

View File

@@ -44,18 +44,23 @@ public:
TSharedPtr<IPropertyHandle> NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name);
private:
TArray<TPair<void*, TSharedPtr<IPropertyRowGenerator>>> Generators;
struct Root
{
const UStruct* Struct = nullptr;
uint8* Base = nullptr;
uint8* End = nullptr;
TSharedPtr<IPropertyRowGenerator> Generator;
};
// Check whether a detail tree node's property comes from a sub-object.
static bool IsSubObject(const TSharedRef<IDetailTreeNode>& Node);
static bool IsInsideRootObject(const Root& Root, IPropertyHandle& Handle);
TArray<Root> Roots;
static TSharedRef<IPropertyRowGenerator> CreateGenerator();
TSharedRef<IPropertyRowGenerator> GetGeneratorForObject(UObject* Obj);
TSharedRef<IPropertyRowGenerator> GetGeneratorForStruct(const UStruct* ScriptStruct, uint8* Data);
Root& GetRootForObject(UObject* Obj);
Root& GetRootForStruct(const UStruct* ScriptStruct, uint8* Data);
static void AllTreeNodesRecursive(const TSharedRef<IDetailTreeNode>& Node, FlatTree& Out);
static FlatTree AllTreeNodes(TSharedRef<IPropertyRowGenerator> Generator);
static FlatTree AllTreeNodes(Root& Root);
Handles AllProperties(TSharedRef<IPropertyRowGenerator> Generator, EPropertyFlags Filter);
static TSharedPtr<IPropertyHandle> NamedProperty(TSharedRef<IPropertyRowGenerator> Generator, FName Name);
Handles AllProperties(Root& Root, EPropertyFlags Filter);
static TSharedPtr<IPropertyHandle> NamedProperty(Root& Root, FName Name);
};

View File

@@ -6,8 +6,6 @@ public class UEWingman : ModuleRules
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
// Needed for debug dump of FPropertyNode tree (private headers)
PrivateIncludePaths.Add(System.IO.Path.Combine(EngineDirectory, "Source/Editor/PropertyEditor/Private"));
PublicDependencyModuleNames.AddRange(new string[]