/ Source / ReasonablePlanningAIEditor / Private / Slate / SCachedPropertyPathStructPropertyPicker.cpp
SCachedPropertyPathStructPropertyPicker.cpp
1 // Copyright (C) 2025 Radaway Software LLC. All Rights Reserved. 2 3 #include "Slate/SCachedPropertyPathStructPropertyPicker.h" 4 #include "ISinglePropertyView.h" 5 #include "PropertyPathHelpers.h" 6 #include "Editor.h" 7 #include "Widgets/SBoxPanel.h" 8 #include "SlateCore.h" 9 #include "EditorStyleSet.h" 10 #include "PropertyPath.h" 11 12 #define LOCTEXT_NAMESPACE "ReasonablePlanningAIEditor" 13 14 BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION 15 void SCachedPropertyPathStructPropertyPicker::Construct(const FArguments& InArgs) 16 { 17 PickerClass = InArgs._PickerClass; 18 OnPropertyPathPicked = InArgs._OnPropertyPathPicked; 19 SelectionString = InArgs._InitialPath; 20 21 if (PickerClass.IsSet() || PickerClass.IsBound()) 22 { 23 CachedPickerClass = InArgs._PickerClass.Get(); 24 ChildSlot 25 [ 26 SAssignNew(ComboButton, SComboButton) 27 .OnGetMenuContent(FOnGetContent::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::GetPropertyPathDropdown)) 28 .ButtonContent() 29 [ 30 SNew(STextBlock) 31 .Text(TAttribute<FText>::Create(TAttribute<FText>::FGetter::CreateLambda([this]() -> FText { 32 if (SelectionString.IsEmpty()) 33 { 34 return LOCTEXT("RpaiPickProperty", "Pick Property"); 35 } 36 else 37 { 38 //TODO: verify exsists and get type for display 39 return FText::FromString(SelectionString); 40 } 41 }))) 42 ] 43 ]; 44 } 45 } 46 END_SLATE_FUNCTION_BUILD_OPTIMIZATION 47 48 void SCachedPropertyPathStructPropertyPicker::RecursiveMenuBuilder(FMenuBuilder& MenuBuilder, UStruct* GivenPickerClass, TArray<FProperty*> Parents) 49 { 50 // 5 is a hardcoded value for the max recursion level of the box filters. 51 const int32 MaxSubMenus = 5; 52 53 for (TFieldIterator<FProperty> Piter(GivenPickerClass, EFieldIterationFlags::IncludeDeprecated | EFieldIterationFlags::IncludeSuper); Piter; ++Piter) 54 { 55 FProperty* Prop = *Piter; 56 if (Piter->IsA<FBoolProperty>()) 57 { 58 MenuBuilder.AddMenuEntry( 59 Piter->GetDisplayNameText(), 60 Piter->GetToolTipText(), 61 FSlateIcon(FEditorStyle::GetStyleSetName(), TEXT("Kismet.Tabs.Variables")), 62 FUIAction(FExecuteAction::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::OnSelection, Prop, Parents)) 63 ); 64 } 65 else if (Piter->IsA<FClassProperty>()) 66 { 67 MenuBuilder.AddMenuEntry( 68 Piter->GetDisplayNameText(), 69 Piter->GetToolTipText(), 70 FSlateIcon(FEditorStyle::GetStyleSetName(), TEXT("Kismet.Tabs.Variables")), 71 FUIAction(FExecuteAction::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::OnSelection, Prop, Parents)) 72 ); 73 } 74 else if (Piter->IsA<FEnumProperty>()) 75 { 76 MenuBuilder.AddMenuEntry( 77 Piter->GetDisplayNameText(), 78 Piter->GetToolTipText(), 79 FSlateIcon(FEditorStyle::GetStyleSetName(), TEXT("Kismet.Tabs.Variables")), 80 FUIAction(FExecuteAction::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::OnSelection, Prop, Parents)) 81 ); 82 } 83 else if (Piter->IsA<FFloatProperty>()) 84 { 85 MenuBuilder.AddMenuEntry( 86 Piter->GetDisplayNameText(), 87 Piter->GetToolTipText(), 88 FSlateIcon(FEditorStyle::GetStyleSetName(), TEXT("Kismet.Tabs.Variables")), 89 FUIAction(FExecuteAction::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::OnSelection, Prop, Parents)) 90 ); 91 } 92 else if (Piter->IsA<FDoubleProperty>()) 93 { 94 MenuBuilder.AddMenuEntry( 95 Piter->GetDisplayNameText(), 96 Piter->GetToolTipText(), 97 FSlateIcon(FEditorStyle::GetStyleSetName(), TEXT("Kismet.Tabs.Variables")), 98 FUIAction(FExecuteAction::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::OnSelection, Prop, Parents)) 99 ); 100 } 101 else if (Piter->IsA<FIntProperty>()) 102 { 103 MenuBuilder.AddMenuEntry( 104 Piter->GetDisplayNameText(), 105 Piter->GetToolTipText(), 106 FSlateIcon(FEditorStyle::GetStyleSetName(), TEXT("Kismet.Tabs.Variables")), 107 FUIAction(FExecuteAction::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::OnSelection, Prop, Parents)) 108 ); 109 } 110 else if (Piter->IsA<FNameProperty>()) 111 { 112 MenuBuilder.AddMenuEntry( 113 Piter->GetDisplayNameText(), 114 Piter->GetToolTipText(), 115 FSlateIcon(FEditorStyle::GetStyleSetName(), TEXT("Kismet.Tabs.Variables")), 116 FUIAction(FExecuteAction::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::OnSelection, Prop, Parents)) 117 ); 118 } 119 else if (Piter->IsA<FStrProperty>()) 120 { 121 MenuBuilder.AddMenuEntry( 122 Piter->GetDisplayNameText(), 123 Piter->GetToolTipText(), 124 FSlateIcon(FEditorStyle::GetStyleSetName(), TEXT("Kismet.Tabs.Variables")), 125 FUIAction(FExecuteAction::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::OnSelection, Prop, Parents)) 126 ); 127 } 128 else if (Piter->IsA<FObjectProperty>() && Parents.Num() < MaxSubMenus) 129 { 130 TArray<FProperty*> NewParentBranch = Parents; 131 NewParentBranch.Add(Prop); 132 UStruct* NewType = CastField<FObjectProperty>(*Piter)->PropertyClass; 133 MenuBuilder.AddSubMenu( 134 Prop->GetDisplayNameText(), 135 Prop->GetToolTipText(), 136 FNewMenuDelegate::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::RecursiveMenuBuilder, NewType, NewParentBranch) 137 ); 138 } 139 else if (Piter->IsA<FStructProperty>()) 140 { 141 if (auto StructProperty = CastField<FStructProperty>(*Piter)) 142 { 143 if (StructProperty->Struct == TBaseStructure<FVector>::Get()) 144 { 145 MenuBuilder.AddMenuEntry( 146 Piter->GetDisplayNameText(), 147 Piter->GetToolTipText(), 148 FSlateIcon(FEditorStyle::GetStyleSetName(), TEXT("Kismet.Tabs.Variables")), 149 FUIAction(FExecuteAction::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::OnSelection, Prop, Parents)) 150 ); 151 } 152 else if (StructProperty->Struct == TBaseStructure<FRotator>::Get()) 153 { 154 MenuBuilder.AddMenuEntry( 155 Piter->GetDisplayNameText(), 156 Piter->GetToolTipText(), 157 FSlateIcon(FEditorStyle::GetStyleSetName(), TEXT("Kismet.Tabs.Variables")), 158 FUIAction(FExecuteAction::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::OnSelection, Prop, Parents)) 159 ); 160 } 161 else if (Parents.Num() < MaxSubMenus) 162 { 163 TArray<FProperty*> NewParentBranch = Parents; 164 NewParentBranch.Add(Prop); 165 UStruct* NewType = StructProperty->Struct.Get(); 166 MenuBuilder.AddSubMenu( 167 Prop->GetDisplayNameText(), 168 Prop->GetToolTipText(), 169 FNewMenuDelegate::CreateSP(this, &SCachedPropertyPathStructPropertyPicker::RecursiveMenuBuilder, NewType, NewParentBranch) 170 ); 171 } 172 } 173 } 174 } 175 } 176 177 void SCachedPropertyPathStructPropertyPicker::OnSelection(FProperty* Leaf, TArray<FProperty*> Parents) 178 { 179 FString NewDisplayName; 180 bool FirstAddition = true; 181 for (int PropertyIndex = 0; PropertyIndex < Parents.Num(); PropertyIndex++) 182 { 183 const FProperty* Property = Parents[PropertyIndex]; 184 if (!FirstAddition) 185 { 186 NewDisplayName += TEXT("."); 187 } 188 189 NewDisplayName += Property->GetFName().ToString(); 190 FirstAddition = false; 191 } 192 193 if (Parents.Num() > 0) 194 { 195 NewDisplayName += TEXT("."); 196 } 197 NewDisplayName += Leaf->GetFName().ToString(); 198 199 SelectionString = NewDisplayName; 200 OnPropertyPathPicked.ExecuteIfBound(SelectionString); 201 } 202 203 TSharedRef<SWidget> SCachedPropertyPathStructPropertyPicker::GetPropertyPathDropdown() 204 { 205 FMenuBuilder MenuBuilder(true, nullptr, TSharedPtr<FExtender>(), false, &FCoreStyle::Get(), true, NAME_None, false); 206 if (PickerClass.IsSet() || PickerClass.IsBound()) 207 { 208 UStruct* CurrentPickerClass = PickerClass.Get(); 209 if (CurrentPickerClass != CachedPickerClass || !CachedPropertyPathDropdown.IsValid()) 210 { 211 TArray<FProperty*> Parents; 212 RecursiveMenuBuilder(MenuBuilder, CurrentPickerClass, Parents); 213 CachedPropertyPathDropdown = MenuBuilder.MakeWidget(); 214 CachedPickerClass = CurrentPickerClass; 215 } 216 } 217 else 218 { 219 CachedPropertyPathDropdown = SNullWidget::NullWidget; 220 } 221 return CachedPropertyPathDropdown.ToSharedRef(); 222 }