/ 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  }