#include "MujocoComponentDetails.h" #include "Components/MujocoBodyComponent.h" #include "DetailLayoutBuilder.h" #include "DetailCategoryBuilder.h" #include "DetailWidgetRow.h" #include "Widgets/Input/SSlider.h" #include "PropertyHandle.h" #include "IDetailChildrenBuilder.h" #include "IPropertyTypeCustomization.h" #include "Structs/MujocoActuator.h" #include "Widgets/Input/SComboBox.h" #include "Widgets/Text/STextBlock.h" #include "Engine/SimpleConstructionScript.h" #include "Engine/SCS_Node.h" #include "Components/MujocoEqualityComponent.h" #include "Components/MujocoActuatorComponent.h" #include "Components/MujocoSiteComponent.h" #include "Components/MujocoJointComponent.h" #include "Components/MujocoTendonComponent.h" #include "Components/MujocoGeomComponent.h" #define LOCTEXT_NAMESPACE "MujocoComponentDetails" TSharedRef FMujocoPropertyCustomization::MakeNameComboBox(const TSharedPtr& PropertyHandle, TArray>& Options) { // clang-format off return SNew(SComboBox>) .OptionsSource(&Options) .OnGenerateWidget_Lambda([](TSharedPtr Option) -> TSharedRef { return SNew(STextBlock).Text(FText::FromName(*Option)); }) .OnSelectionChanged_Lambda([PropertyHandle](TSharedPtr NewSelection, ESelectInfo::Type) { if (PropertyHandle->IsValidHandle() && NewSelection.IsValid()) { PropertyHandle->SetValue(NewSelection->IsNone() ? NAME_None : *NewSelection, EPropertyValueSetFlags::InteractiveChange); } else { PropertyHandle->NotifyPostChange(EPropertyValueSetFlags::InteractiveChange); } }) .Content() [ SNew(STextBlock).Text_Lambda([PropertyHandle]() -> FText { FString Selected; return (!PropertyHandle->IsValidHandle() || !PropertyHandle->GetValueAsDisplayString(Selected)) ? FText::FromName(NAME_None) : FText::FromString(Selected); }) ]; // clang-format on } TSharedRef FMujocoPropertyCustomization::MakeInstance() { return MakeShareable(new FMujocoPropertyCustomization()); } void FMujocoPropertyCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) { TSharedPtr JointHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, Joint)); TSharedPtr ETypeHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMujocoEquality, Type)); if (JointHandle && JointHandle->IsValidHandle()) { FString JointStr; FString TypeStr; TSharedPtr TypeHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, Type)); if (!JointHandle->GetValueAsDisplayString(JointStr)) { JointStr = "None"; } if (TypeHandle && TypeHandle->IsValidHandle()) { if (!TypeHandle->GetValueAsDisplayString(TypeStr)) { TypeStr = "None"; } } // clang-format off HeaderRow.NameContent() [ PropertyHandle->CreatePropertyNameWidget() ] .ValueContent() [ SNew(STextBlock).Text(FText::FromString(FString::Printf(TEXT("%s (%s)"), *JointStr, *TypeStr))) ]; // clang-format on return; } else if (ETypeHandle && ETypeHandle->IsValidHandle()) { FString Joint1Str; FString Joint2Str; TSharedPtr Joint1Handle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMujocoEquality, Joint1)); TSharedPtr Joint2Handle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMujocoEquality, Joint2)); if (Joint1Handle && Joint1Handle->IsValidHandle() && Joint1Handle->GetValueAsDisplayString(Joint1Str)) { if (Joint2Handle && Joint2Handle->IsValidHandle() && Joint2Handle->GetValueAsDisplayString(Joint2Str)) { // clang-format off HeaderRow.NameContent() [ PropertyHandle->CreatePropertyNameWidget() ] .ValueContent() [ SNew(STextBlock).Text(FText::FromString(FString::Printf(TEXT("%s -> %s"), *Joint1Str, *Joint2Str))) ]; // clang-format on return; } } } HeaderRow.NameContent()[PropertyHandle->CreatePropertyNameWidget()]; } void FMujocoPropertyCustomization::CustomizeChildren(TSharedRef StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { JointOptions.Reset(); BodyOptions.Reset(); SiteOptions.Reset(); TendonOptions.Reset(); FlexOptions.Reset(); GeomOptions.Reset(); JointOptions.Add(MakeShareable(new FName("None"))); BodyOptions.Add(MakeShareable(new FName("None"))); SiteOptions.Add(MakeShareable(new FName("None"))); TendonOptions.Add(MakeShareable(new FName("None"))); FlexOptions.Add(MakeShareable(new FName("None"))); GeomOptions.Add(MakeShareable(new FName("None"))); TArray OuterObjects; StructPropertyHandle->GetOuterObjects(OuterObjects); for (UObject* Outer : OuterObjects) { if (UActorComponent* ModelComponent = Cast(Outer)) { auto ModelOuter = ModelComponent->GetOuter(); if (UBlueprintGeneratedClass* BpGenerated = Cast(ModelOuter)) { if (BpGenerated->SimpleConstructionScript) { for (USCS_Node* Node : BpGenerated->SimpleConstructionScript->GetAllNodes()) { if (UMujocoBodyComponent* BodyComponent = Cast(Node->ComponentTemplate)) { BodyOptions.Add(MakeShareable(new FName(Node->GetVariableName()))); } else if (UMujocoSiteComponent* SiteComponent = Cast(Node->ComponentTemplate)) { SiteOptions.Add(MakeShareable(new FName(Node->GetVariableName()))); } else if (UMujocoJointComponent* JointComponent = Cast(Node->ComponentTemplate)) { JointOptions.Add(MakeShareable(new FName(Node->GetVariableName()))); } else if (UMujocoTendonComponent* TendonComponent = Cast(Node->ComponentTemplate)) { TendonOptions.Add(MakeShareable(new FName(Node->GetVariableName()))); } else if (UMujocoGeomComponent* GeomComponent = Cast(Node->ComponentTemplate)) { GeomOptions.Add(MakeShareable(new FName(Node->GetVariableName()))); } } } } } } uint32 NumChildren = 0; StructPropertyHandle->GetNumChildren(NumChildren); for (uint32 i = 0; i < NumChildren; ++i) { TSharedPtr ChildHandle = StructPropertyHandle->GetChildHandle(i); if (ChildHandle.IsValid()) { if (ChildHandle->HasMetaData("EditConditionHides") && !ChildHandle->IsEditable()) { continue; } if (!ChildHandle->IsEditable()) { StructBuilder.AddProperty(ChildHandle.ToSharedRef()); continue; } FName PropertyName = ChildHandle->GetProperty()->GetFName(); if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, Joint) || PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, JointInParent)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("JointName", "Joint Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, JointOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, Site) || PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, RefSite) || PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, CrankSite) || PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, SliderSite)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("SiteName", "Site Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, SiteOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, Body)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("BodyName", "Body Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, BodyOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, Tendon)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("TendonName", "Tendon Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, TendonOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoActuatorV2, Type)) { ChildHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda([this, ChildHandle]() { ChildHandle->GetParentHandle()->RequestRebuildChildren(); })); StructBuilder.AddProperty(ChildHandle.ToSharedRef()); } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoEquality, Joint1)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("Joint1Name", "Joint 1 Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, JointOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoEquality, Joint2)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("Joint2Name", "Joint 2 Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, JointOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoEquality, Body1)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("Body1Name", "Body 1 Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, BodyOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoEquality, Body2)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("Body2Name", "Body 2 Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, BodyOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoEquality, Site1)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("Site1Name", "Site 1 Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, SiteOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoEquality, Site2)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("Site2Name", "Site 2 Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, SiteOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoEquality, Tendon1)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("Tendon1Name", "Tendon 1 Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, TendonOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoEquality, Tendon2)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("Tendon2Name", "Tendon 2 Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, TendonOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoEquality, Flex)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("FlexName", "Flex Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, FlexOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoTendonSpatialSite, Site)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("SiteName", "Site Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, SiteOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoTendonSpatialGeom, Geom)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("GeomName", "Geom Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, GeomOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoTendonSpatialGeom, SideSite)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("SideSiteName", "Side Site Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, SiteOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoTendonFixedJoint, Joint)) { // clang-format off StructBuilder.AddCustomRow(LOCTEXT("JointName", "Joint Name")) .NameContent() [ ChildHandle->CreatePropertyNameWidget() ] .ValueContent() [ MakeNameComboBox(ChildHandle, JointOptions) ]; // clang-format on } else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMujocoEquality, Type)) { ChildHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateLambda([this, ChildHandle]() { ChildHandle->GetParentHandle()->RequestRebuildChildren(); })); StructBuilder.AddProperty(ChildHandle.ToSharedRef()); } else { StructBuilder.AddProperty(ChildHandle.ToSharedRef()); } } } } #undef LOCTEXT_NAMESPACE