1142 lines
38 KiB
C++
1142 lines
38 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "SKismetInspector.h"
|
|
|
|
#include "BitmaskLiteralDetails.h"
|
|
#include "BlueprintDetailsCustomization.h"
|
|
#include "BlueprintEditor.h"
|
|
#include "BlueprintEditorSettings.h"
|
|
#include "BlueprintMemberReferenceCustomization.h"
|
|
#include "BlueprintNamespaceUtilities.h"
|
|
#include "Components/ActorComponent.h"
|
|
#include "Components/ChildActorComponent.h"
|
|
#include "Containers/SparseArray.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "DetailsViewArgs.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "EdGraph/EdGraphNode.h"
|
|
#include "EdGraph/EdGraphNode_Documentation.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "Editor.h"
|
|
#include "Editor/EditorEngine.h"
|
|
#include "Engine/Blueprint.h"
|
|
#include "Engine/MemberReference.h"
|
|
#include "Engine/SCS_Node.h"
|
|
#include "FormatTextDetails.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Framework/Text/TextLayout.h"
|
|
#include "GameFramework/Actor.h"
|
|
#include "HAL/Platform.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "IDetailCustomization.h"
|
|
#include "IDetailsView.h"
|
|
#include "IStructureDetailsView.h"
|
|
#include "Input/Events.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "K2Node.h"
|
|
#include "K2Node_AddComponent.h" // for GetTemplateFromNode()
|
|
#include "K2Node_BitmaskLiteral.h"
|
|
#include "K2Node_CallFunction.h"
|
|
#include "K2Node_EditablePinBase.h"
|
|
#include "K2Node_FormatText.h"
|
|
#include "K2Node_FunctionTerminator.h"
|
|
#include "K2Node_VariableGet.h"
|
|
#include "K2Node_VariableSet.h"
|
|
#include "Kismet/KismetSystemLibrary.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Kismet2/ComponentEditorUtils.h" // For CanEditNativeComponent()
|
|
#include "Layout/Children.h"
|
|
#include "Layout/Margin.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Misc/Attribute.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "PropertyEditorDelegates.h"
|
|
#include "PropertyEditorModule.h"
|
|
#include "Settings/EditorExperimentalSettings.h"
|
|
#include "SlotBase.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Styling/ISlateStyle.h"
|
|
#include "Templates/Casts.h"
|
|
#include "Templates/SubclassOf.h"
|
|
#include "Templates/UnrealTemplate.h"
|
|
#include "Types/ISlateMetaData.h"
|
|
#include "Types/SlateEnums.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/Field.h"
|
|
#include "UObject/Object.h"
|
|
#include "UObject/ObjectMacros.h"
|
|
#include "UObject/ObjectPtr.h"
|
|
#include "UObject/UObjectGlobals.h"
|
|
#include "UObject/UnrealNames.h"
|
|
#include "UObject/UnrealType.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "Widgets/Input/SCheckBox.h"
|
|
#include "Widgets/Layout/SBorder.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
#include "Widgets/SBoxPanel.h"
|
|
#include "Widgets/Text/SRichTextBlock.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
|
|
class FNotifyHook;
|
|
class FStructOnScope;
|
|
class IClassViewerFilter;
|
|
class IDetailLayoutBuilder;
|
|
class SDockTab;
|
|
class SWidget;
|
|
struct FGeometry;
|
|
|
|
#define LOCTEXT_NAMESPACE "KismetInspector"
|
|
|
|
class SKismetInspectorUneditableComponentWarning : public SCompoundWidget
|
|
{
|
|
public:
|
|
SLATE_BEGIN_ARGS(SKismetInspectorUneditableComponentWarning)
|
|
: _WarningText()
|
|
, _OnHyperlinkClicked()
|
|
{}
|
|
|
|
/** The rich text to show in the warning */
|
|
SLATE_ATTRIBUTE(FText, WarningText)
|
|
|
|
/** Called when the hyperlink in the rich text is clicked */
|
|
SLATE_EVENT(FSlateHyperlinkRun::FOnClick, OnHyperlinkClicked)
|
|
|
|
SLATE_END_ARGS()
|
|
|
|
/** Constructs the widget */
|
|
void Construct(const FArguments& InArgs)
|
|
{
|
|
ChildSlot
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FAppStyle::Get().GetBrush("ToolPanel.GroupBorder"))
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
.Padding(2.0f)
|
|
[
|
|
SNew(SImage)
|
|
.Image(FAppStyle::Get().GetBrush("Icons.Warning"))
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(2.0f)
|
|
[
|
|
SNew(SRichTextBlock)
|
|
.DecoratorStyleSet(&FAppStyle::Get())
|
|
.Justification(ETextJustify::Left)
|
|
.TextStyle(FAppStyle::Get(), "DetailsView.BPMessageTextStyle")
|
|
.Text(InArgs._WarningText)
|
|
.AutoWrapText(true)
|
|
+ SRichTextBlock::HyperlinkDecorator(TEXT("HyperlinkDecorator"), InArgs._OnHyperlinkClicked)
|
|
]
|
|
]
|
|
];
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FKismetSelectionInfo
|
|
|
|
struct FKismetSelectionInfo
|
|
{
|
|
public:
|
|
TArray<UActorComponent*> EditableComponentTemplates;
|
|
TArray<UObject*> ObjectsForPropertyEditing;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SKismetInspector
|
|
|
|
void SKismetInspector::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
|
|
{
|
|
if(bRefreshOnTick)
|
|
{
|
|
// if struct is valid, update struct
|
|
if (StructToDisplay.IsValid())
|
|
{
|
|
UpdateFromSingleStruct(StructToDisplay);
|
|
StructToDisplay.Reset();
|
|
}
|
|
else
|
|
{
|
|
FKismetSelectionInfo SelectionInfo;
|
|
RefreshPropertyObjects.Remove(nullptr);
|
|
UpdateFromObjects(RefreshPropertyObjects, SelectionInfo, RefreshOptions);
|
|
RefreshPropertyObjects.Empty();
|
|
}
|
|
|
|
bRefreshOnTick = false;
|
|
}
|
|
}
|
|
|
|
TSharedRef<SWidget> SKismetInspector::MakeContextualEditingWidget(struct FKismetSelectionInfo& SelectionInfo, const FShowDetailsOptions& Options)
|
|
{
|
|
TSharedRef< SVerticalBox > ContextualEditingWidget = SNew( SVerticalBox );
|
|
|
|
if(bShowTitleArea)
|
|
{
|
|
if (SelectedObjects.Num() == 0)
|
|
{
|
|
// Warning about nothing being selected
|
|
ContextualEditingWidget->AddSlot()
|
|
.AutoHeight()
|
|
.HAlign( HAlign_Center )
|
|
.Padding( 2.0f, 14.0f, 2.0f, 2.0f )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( LOCTEXT("NoNodesSelected", "Select a node to edit details.") )
|
|
];
|
|
}
|
|
else
|
|
{
|
|
// Title of things being edited
|
|
ContextualEditingWidget->AddSlot()
|
|
.AutoHeight()
|
|
.Padding( 2.0f, 0.0f, 2.0f, 0.0f )
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SKismetInspector::GetContextualEditingWidgetTitle)
|
|
];
|
|
}
|
|
}
|
|
|
|
// Show the property editor
|
|
PropertyView->HideFilterArea(Options.bHideFilterArea);
|
|
PropertyView->SetObjects(SelectionInfo.ObjectsForPropertyEditing, Options.bForceRefresh);
|
|
|
|
if (SelectionInfo.ObjectsForPropertyEditing.Num())
|
|
{
|
|
ContextualEditingWidget->AddSlot()
|
|
.FillHeight( 0.9f )
|
|
.VAlign( VAlign_Top )
|
|
[
|
|
SNew( SBox )
|
|
.Visibility(this, &SKismetInspector::GetPropertyViewVisibility)
|
|
[
|
|
SNew( SVerticalBox )
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding( FMargin( 0,0,0,1) )
|
|
[
|
|
SNew(SKismetInspectorUneditableComponentWarning)
|
|
.Visibility(this, &SKismetInspector::GetInheritedBlueprintComponentWarningVisibility)
|
|
.WarningText(NSLOCTEXT("SKismetInspector", "BlueprintUneditableInheritedComponentWarning", "Components flagged as not editable when inherited must be edited in the <a id=\"HyperlinkDecorator\" style=\"DetailsView.BPMessageHyperlinkStyle\">Parent Blueprint</>"))
|
|
.OnHyperlinkClicked(this, &SKismetInspector::OnInheritedBlueprintComponentWarningHyperlinkClicked)
|
|
]
|
|
+SVerticalBox::Slot()
|
|
[
|
|
PropertyView.ToSharedRef()
|
|
]
|
|
]
|
|
];
|
|
|
|
ContextualEditingWidget->AddSlot()
|
|
.AutoHeight()
|
|
.VAlign(VAlign_Top)
|
|
[
|
|
SNew(SCheckBox)
|
|
.ToolTipText(LOCTEXT("TogglePublicView", "Toggle Public View"))
|
|
.IsChecked(this, &SKismetInspector::GetPublicViewCheckboxState)
|
|
.OnCheckStateChanged(this, &SKismetInspector::SetPublicViewCheckboxState)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("PublicViewCheckboxLabel", "Public View"))
|
|
]
|
|
.Visibility_Lambda([bShowPublicView = this->bShowPublicView]()
|
|
{
|
|
return bShowPublicView.Get() ? EVisibility::Visible : EVisibility::Hidden;
|
|
})
|
|
];
|
|
}
|
|
|
|
return ContextualEditingWidget;
|
|
}
|
|
|
|
void SKismetInspector::SetOwnerTab(TSharedRef<SDockTab> Tab)
|
|
{
|
|
OwnerTab = Tab;
|
|
}
|
|
|
|
TSharedPtr<SDockTab> SKismetInspector::GetOwnerTab() const
|
|
{
|
|
return OwnerTab.Pin();
|
|
}
|
|
|
|
bool SKismetInspector::IsSelected(UObject* Object) const
|
|
{
|
|
for ( const TWeakObjectPtr<UObject>& SelectedObject : SelectedObjects )
|
|
{
|
|
if ( SelectedObject.Get() == Object )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const TArray< TWeakObjectPtr<UObject> >& SKismetInspector::GetSelectedObjects() const
|
|
{
|
|
return SelectedObjects;
|
|
}
|
|
|
|
void SKismetInspector::AddReferencedObjects(FReferenceCollector& Collector)
|
|
{
|
|
Collector.AddReferencedObjects(RefreshPropertyObjects);
|
|
}
|
|
|
|
FString SKismetInspector::GetReferencerName() const
|
|
{
|
|
return TEXT("SKismetInspector");
|
|
}
|
|
|
|
FText SKismetInspector::GetContextualEditingWidgetTitle() const
|
|
{
|
|
FText Title = PropertyViewTitle;
|
|
if (Title.IsEmpty())
|
|
{
|
|
if (SelectedObjects.Num() == 1 && SelectedObjects[0].IsValid())
|
|
{
|
|
UObject* Object = SelectedObjects[0].Get();
|
|
|
|
if (UEdGraphNode* Node = Cast<UEdGraphNode>(Object))
|
|
{
|
|
Title = Node->GetNodeTitle(ENodeTitleType::ListView);
|
|
}
|
|
else if (USCS_Node* SCSNode = Cast<USCS_Node>(Object))
|
|
{
|
|
if (SCSNode->ComponentTemplate != nullptr)
|
|
{
|
|
const FName VariableName = SCSNode->GetVariableName();
|
|
if (VariableName != NAME_None)
|
|
{
|
|
Title = FText::Format(LOCTEXT("TemplateForFmt", "Template for {0}"), FText::FromName(VariableName));
|
|
}
|
|
else
|
|
{
|
|
Title = FText::Format(LOCTEXT("Name_TemplateFmt", "{0} Template"), FText::FromString(SCSNode->ComponentTemplate->GetClass()->GetName()));
|
|
}
|
|
}
|
|
}
|
|
else if (UK2Node_AddComponent* ComponentNode = Cast<UK2Node_AddComponent>(Object))
|
|
{
|
|
// Edit the component template
|
|
if (UActorComponent* Template = ComponentNode->GetTemplateFromNode())
|
|
{
|
|
Title = FText::Format(LOCTEXT("Name_TemplateFmt", "{0} Template"), FText::FromString(Template->GetClass()->GetName()));
|
|
}
|
|
}
|
|
|
|
if (Title.IsEmpty())
|
|
{
|
|
Title = FText::FromString(UKismetSystemLibrary::GetDisplayName(Object));
|
|
}
|
|
}
|
|
else if (SelectedObjects.Num() > 1)
|
|
{
|
|
UClass* BaseClass = nullptr;
|
|
|
|
for (auto ObjectWkPtrIt = SelectedObjects.CreateConstIterator(); ObjectWkPtrIt; ++ObjectWkPtrIt)
|
|
{
|
|
TWeakObjectPtr<UObject> ObjectWkPtr = *ObjectWkPtrIt;
|
|
if (ObjectWkPtr.IsValid())
|
|
{
|
|
UObject* Object = ObjectWkPtr.Get();
|
|
UClass* ObjClass = Object->GetClass();
|
|
|
|
if (UEdGraphNode* Node = Cast<UEdGraphNode>(Object))
|
|
{
|
|
// Hide any specifics of node types; they're all ed graph nodes
|
|
ObjClass = UEdGraphNode::StaticClass();
|
|
}
|
|
|
|
// Keep track of the class of objects selected
|
|
if (BaseClass == nullptr)
|
|
{
|
|
BaseClass = ObjClass;
|
|
checkSlow(ObjClass);
|
|
}
|
|
while (!ObjClass->IsChildOf(BaseClass))
|
|
{
|
|
BaseClass = BaseClass->GetSuperClass();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BaseClass)
|
|
{
|
|
Title = FText::Format(LOCTEXT("MultipleObjectsSelectedFmt", "{0} {1} selected"), FText::AsNumber(SelectedObjects.Num()), FText::FromString(BaseClass->GetName() + TEXT("s")));
|
|
}
|
|
}
|
|
}
|
|
return Title;
|
|
}
|
|
|
|
void SKismetInspector::Construct(const FArguments& InArgs)
|
|
{
|
|
bShowInspectorPropertyView = true;
|
|
PublicViewState = ECheckBoxState::Unchecked;
|
|
bComponenetDetailsCustomizationEnabled = false;
|
|
bRefreshOnTick = false;
|
|
|
|
BlueprintEditorPtr = InArgs._Kismet2;
|
|
bShowPublicView = InArgs._ShowPublicViewControl;
|
|
bShowTitleArea = InArgs._ShowTitleArea;
|
|
TSharedPtr<FBlueprintEditor> Kismet2 = BlueprintEditorPtr.Pin();
|
|
|
|
// Create a property view
|
|
FPropertyEditorModule& EditModule = FModuleManager::Get().GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
|
|
FNotifyHook* NotifyHook = nullptr;
|
|
if(InArgs._SetNotifyHook)
|
|
{
|
|
NotifyHook = Kismet2.Get();
|
|
}
|
|
|
|
FDetailsViewArgs DetailsViewArgs;
|
|
DetailsViewArgs.NameAreaSettings = InArgs._HideNameArea ? FDetailsViewArgs::HideNameArea : FDetailsViewArgs::ObjectsUseNameArea;
|
|
DetailsViewArgs.bHideSelectionTip = true;
|
|
DetailsViewArgs.NotifyHook = NotifyHook;
|
|
DetailsViewArgs.ViewIdentifier = InArgs._ViewIdentifier;
|
|
DetailsViewArgs.ExternalScrollbar = InArgs._ExternalScrollbar;
|
|
DetailsViewArgs.ScrollbarAlignment = InArgs._ScrollbarAlignment;
|
|
DetailsViewArgs.bShowSectionSelector = InArgs._ShowSectionSelector;
|
|
if (Kismet2.IsValid())
|
|
{
|
|
TSharedPtr<IClassViewerFilter> ImportedClassFilter = Kismet2->GetImportedClassViewerFilter();
|
|
if (ImportedClassFilter.IsValid())
|
|
{
|
|
DetailsViewArgs.ClassViewerFilters.Add(ImportedClassFilter.ToSharedRef());
|
|
}
|
|
}
|
|
|
|
PropertyView = EditModule.CreateDetailView( DetailsViewArgs );
|
|
|
|
//@TODO: .IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() );
|
|
PropertyView->SetIsPropertyVisibleDelegate( FIsPropertyVisible::CreateSP(this, &SKismetInspector::IsPropertyVisible) );
|
|
PropertyView->SetIsPropertyEditingEnabledDelegate(FIsPropertyEditingEnabled::CreateSP(this, &SKismetInspector::IsPropertyEditingEnabled));
|
|
|
|
IsPropertyEditingEnabledDelegate = InArgs._IsPropertyEditingEnabledDelegate;
|
|
UserOnFinishedChangingProperties = InArgs._OnFinishedChangingProperties;
|
|
|
|
TWeakPtr<SMyBlueprint> MyBlueprint = Kismet2.IsValid() ? Kismet2->GetMyBlueprintWidget() : InArgs._MyBlueprintWidget;
|
|
|
|
if( MyBlueprint.IsValid() )
|
|
{
|
|
FOnGetDetailCustomizationInstance LayoutDelegateDetails = FOnGetDetailCustomizationInstance::CreateStatic(&FBlueprintDelegateActionDetails::MakeInstance, MyBlueprint);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UMulticastDelegatePropertyWrapper::StaticClass(), LayoutDelegateDetails);
|
|
|
|
// Register function and variable details customization
|
|
FOnGetDetailCustomizationInstance LayoutGraphDetails = FOnGetDetailCustomizationInstance::CreateStatic(&FBlueprintGraphActionDetails::MakeInstance, MyBlueprint, InArgs._ShowLocalVariables);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UEdGraph::StaticClass(), LayoutGraphDetails);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UK2Node_EditablePinBase::StaticClass(), LayoutGraphDetails);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UK2Node_CallFunction::StaticClass(), LayoutGraphDetails);
|
|
|
|
FOnGetDetailCustomizationInstance LayoutVariableDetails = FOnGetDetailCustomizationInstance::CreateStatic(&FBlueprintVarActionDetails::MakeInstance, MyBlueprint);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UPropertyWrapper::StaticClass(), LayoutVariableDetails);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UK2Node_VariableGet::StaticClass(), LayoutVariableDetails);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UK2Node_VariableSet::StaticClass(), LayoutVariableDetails);
|
|
|
|
PropertyView->RegisterInstancedCustomPropertyTypeLayout(FMemberReference::StaticStruct()->GetFName(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FBlueprintMemberReferenceDetails::MakeInstance, MyBlueprint));
|
|
}
|
|
|
|
if (Kismet2.IsValid() && Kismet2->IsEditingSingleBlueprint())
|
|
{
|
|
FOnGetDetailCustomizationInstance LayoutOptionDetails = FOnGetDetailCustomizationInstance::CreateStatic(&FBlueprintGlobalOptionsDetails::MakeInstance, BlueprintEditorPtr);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UBlueprint::StaticClass(), LayoutOptionDetails);
|
|
|
|
FOnGetDetailCustomizationInstance LayoutFormatTextDetails = FOnGetDetailCustomizationInstance::CreateStatic(&FFormatTextDetails::MakeInstance);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UK2Node_FormatText::StaticClass(), LayoutFormatTextDetails);
|
|
|
|
FOnGetDetailCustomizationInstance LayoutBitmaskLiteralDetails = FOnGetDetailCustomizationInstance::CreateStatic(&FBitmaskLiteralDetails::MakeInstance);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UK2Node_BitmaskLiteral::StaticClass(), LayoutBitmaskLiteralDetails);
|
|
|
|
FOnGetDetailCustomizationInstance LayoutDocumentationDetails = FOnGetDetailCustomizationInstance::CreateStatic(&FBlueprintDocumentationDetails::MakeInstance, BlueprintEditorPtr);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UEdGraphNode_Documentation::StaticClass(), LayoutDocumentationDetails);
|
|
|
|
FOnGetDetailCustomizationInstance GraphNodeDetails = FOnGetDetailCustomizationInstance::CreateStatic(&FBlueprintGraphNodeDetails::MakeInstance, BlueprintEditorPtr);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UEdGraphNode::StaticClass(), GraphNodeDetails);
|
|
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UChildActorComponent::StaticClass(),
|
|
FOnGetDetailCustomizationInstance::CreateStatic(&FChildActorComponentDetails::MakeInstance, BlueprintEditorPtr));
|
|
}
|
|
|
|
// Create the border that all of the content will get stuffed into
|
|
ChildSlot
|
|
[
|
|
SNew(SVerticalBox)
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("BlueprintInspector")))
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
[
|
|
SAssignNew( ContextualEditingBorderWidget, SBorder )
|
|
.Padding(0.0f)
|
|
.BorderImage( FAppStyle::GetBrush("NoBorder") )
|
|
]
|
|
];
|
|
|
|
// Update based on the current (empty) selection set
|
|
TArray<UObject*> InitialSelectedObjects;
|
|
FKismetSelectionInfo SelectionInfo;
|
|
UpdateFromObjects(InitialSelectedObjects, SelectionInfo, SKismetInspector::FShowDetailsOptions(FText::GetEmpty(), true));
|
|
|
|
// create struct to display
|
|
FStructureDetailsViewArgs StructureViewArgs;
|
|
StructureViewArgs.bShowObjects = true;
|
|
StructureViewArgs.bShowAssets = true;
|
|
StructureViewArgs.bShowClasses = true;
|
|
StructureViewArgs.bShowInterfaces = true;
|
|
|
|
FDetailsViewArgs ViewArgs;
|
|
ViewArgs.bAllowSearch = false;
|
|
ViewArgs.bHideSelectionTip = false;
|
|
ViewArgs.bShowObjectLabel = false;
|
|
ViewArgs.NotifyHook = NotifyHook;
|
|
|
|
StructureDetailsView = EditModule.CreateStructureDetailView(ViewArgs, StructureViewArgs, StructToDisplay, LOCTEXT("Struct", "Struct View"));
|
|
StructureDetailsView->GetDetailsView()->SetIsPropertyReadOnlyDelegate(FIsPropertyReadOnly::CreateSP(this, &SKismetInspector::IsStructViewPropertyReadOnly));
|
|
StructureDetailsView->GetOnFinishedChangingPropertiesDelegate().Clear();
|
|
StructureDetailsView->GetOnFinishedChangingPropertiesDelegate().Add(UserOnFinishedChangingProperties);
|
|
}
|
|
|
|
void SKismetInspector::EnableComponentDetailsCustomization(bool bEnable)
|
|
{
|
|
// An "empty" instanced customization that's intended to override any registered global details customization for
|
|
// the AActor class type. This will be applied -only- when the CDO is selected to the Details view in Components mode.
|
|
class FActorDetailsOverrideCustomization : public IDetailCustomization
|
|
{
|
|
public:
|
|
/** IDetailCustomization interface */
|
|
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override {}
|
|
|
|
static TSharedRef<class IDetailCustomization> MakeInstance()
|
|
{
|
|
return MakeShareable(new FActorDetailsOverrideCustomization());
|
|
}
|
|
};
|
|
|
|
bComponenetDetailsCustomizationEnabled = bEnable;
|
|
|
|
if (bEnable)
|
|
{
|
|
FOnGetDetailCustomizationInstance ActorOverrideDetails = FOnGetDetailCustomizationInstance::CreateStatic(&FActorDetailsOverrideCustomization::MakeInstance);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(AActor::StaticClass(), ActorOverrideDetails);
|
|
|
|
FOnGetDetailCustomizationInstance LayoutComponentDetails = FOnGetDetailCustomizationInstance::CreateStatic(&FBlueprintComponentDetails::MakeInstance, BlueprintEditorPtr);
|
|
PropertyView->RegisterInstancedCustomPropertyLayout(UActorComponent::StaticClass(), LayoutComponentDetails);
|
|
}
|
|
else
|
|
{
|
|
PropertyView->UnregisterInstancedCustomPropertyLayout(AActor::StaticClass());
|
|
PropertyView->UnregisterInstancedCustomPropertyLayout(UActorComponent::StaticClass());
|
|
}
|
|
}
|
|
|
|
/** Update the inspector window to show information on the supplied object */
|
|
void SKismetInspector::ShowDetailsForSingleObject(UObject* Object, const FShowDetailsOptions& Options)
|
|
{
|
|
TArray<UObject*> PropertyObjects;
|
|
|
|
if (Object != nullptr)
|
|
{
|
|
PropertyObjects.Add(Object);
|
|
}
|
|
|
|
if(PropertyView)
|
|
{
|
|
PropertyView->SetObjectFilter(nullptr);
|
|
}
|
|
|
|
ShowDetailsForObjects(PropertyObjects, Options);
|
|
}
|
|
|
|
void SKismetInspector::ShowDetailsForObjects(const TArray<UObject*>& PropertyObjects, const FShowDetailsOptions& Options)
|
|
{
|
|
// Refresh is being deferred until the next tick, this prevents batch operations from bombarding the details view with calls to refresh
|
|
RefreshPropertyObjects = PropertyObjects;
|
|
RefreshOptions = Options;
|
|
bRefreshOnTick = true;
|
|
}
|
|
|
|
/** Update the inspector window to show information on the supplied object */
|
|
void SKismetInspector::ShowSingleStruct(TSharedPtr<FStructOnScope> InStructToDisplay)
|
|
{
|
|
static bool bIsReentrant = false;
|
|
if (!bIsReentrant)
|
|
{
|
|
bIsReentrant = true;
|
|
// When the selection is changed, we may be potentially actively editing a property,
|
|
// if this occurs we need need to immediately clear keyboard focus
|
|
if (FSlateApplication::Get().HasFocusedDescendants(AsShared()))
|
|
{
|
|
FSlateApplication::Get().ClearKeyboardFocus(EFocusCause::Mouse);
|
|
}
|
|
bIsReentrant = false;
|
|
}
|
|
|
|
StructToDisplay = InStructToDisplay;
|
|
// we don't defer this becasue StructDetailViews contains TSharedPtr to this sturct,
|
|
// not clearing until next tick causes crash
|
|
// so will update struct view here, but updating widget will happen in the tick
|
|
StructureDetailsView->SetStructureData(InStructToDisplay);
|
|
bRefreshOnTick = true;
|
|
}
|
|
|
|
void SKismetInspector::AddPropertiesRecursive(FProperty* Property)
|
|
{
|
|
if (Property != nullptr)
|
|
{
|
|
// Add this property
|
|
SelectedObjectProperties.Add(Property);
|
|
|
|
// If this is a struct or an array of structs, recursively add the child properties
|
|
FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property);
|
|
FStructProperty* StructProperty = CastField<FStructProperty>(Property);
|
|
if( StructProperty != nullptr && StructProperty->Struct != nullptr)
|
|
{
|
|
for (TFieldIterator<FProperty> StructPropIt(StructProperty->Struct); StructPropIt; ++StructPropIt)
|
|
{
|
|
FProperty* InsideStructProperty = *StructPropIt;
|
|
AddPropertiesRecursive(InsideStructProperty);
|
|
}
|
|
}
|
|
else if( ArrayProperty && ArrayProperty->Inner->IsA<FStructProperty>() )
|
|
{
|
|
AddPropertiesRecursive(ArrayProperty->Inner);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SKismetInspector::UpdateFromSingleStruct(const TSharedPtr<FStructOnScope>& InStructToDisplay)
|
|
{
|
|
if (StructureDetailsView.IsValid())
|
|
{
|
|
SelectedObjects.Empty();
|
|
|
|
// Update our context-sensitive editing widget
|
|
ContextualEditingBorderWidget->SetContent(StructureDetailsView->GetWidget().ToSharedRef());
|
|
}
|
|
}
|
|
|
|
void SKismetInspector::UpdateFromObjects(const TArray<UObject*>& PropertyObjects, struct FKismetSelectionInfo& SelectionInfo, const FShowDetailsOptions& Options)
|
|
{
|
|
// There's not an explicit point where
|
|
// we ender a kind of component editing mode, so instead, just look at what we're selecting.
|
|
// If we select a component, then enable the customization.
|
|
bool bEnableComponentCustomization = false;
|
|
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor = BlueprintEditorPtr.Pin();
|
|
if (BlueprintEditor.IsValid())
|
|
{
|
|
if (BlueprintEditor->CanAccessComponentsMode())
|
|
{
|
|
for (UObject* PropertyObject : PropertyObjects)
|
|
{
|
|
if (PropertyObject && !PropertyObject->IsValidLowLevel())
|
|
{
|
|
ensureMsgf(false, TEXT("Object in KismetInspector is invalid, see TTP 281915"));
|
|
continue;
|
|
}
|
|
|
|
if (PropertyObject->IsA<UActorComponent>())
|
|
{
|
|
bEnableComponentCustomization = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EnableComponentDetailsCustomization(bEnableComponentCustomization);
|
|
|
|
if (!Options.bForceRefresh)
|
|
{
|
|
// Early out if the PropertyObjects and the SelectedObjects are the same
|
|
bool bEquivalentSets = (PropertyObjects.Num() == SelectedObjects.Num());
|
|
if (bEquivalentSets)
|
|
{
|
|
// Verify the elements of the sets are equivalent
|
|
for (int32 i = 0; i < PropertyObjects.Num(); i++)
|
|
{
|
|
if (PropertyObjects[i] != SelectedObjects[i].Get())
|
|
{
|
|
if (PropertyObjects[i] && !PropertyObjects[i]->IsValidLowLevel())
|
|
{
|
|
ensureMsgf(false, TEXT("Object in KismetInspector is invalid, see TTP 281915"));
|
|
continue;
|
|
}
|
|
|
|
bEquivalentSets = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bEquivalentSets)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
PropertyView->OnFinishedChangingProperties().Clear();
|
|
PropertyView->OnFinishedChangingProperties().Add(UserOnFinishedChangingProperties);
|
|
PropertyView->OnFinishedChangingProperties().AddSP(this, &SKismetInspector::OnFinishedChangingProperties);
|
|
|
|
// Proceed to update
|
|
SelectedObjects.Empty();
|
|
|
|
for (auto ObjectIt = PropertyObjects.CreateConstIterator(); ObjectIt; ++ObjectIt)
|
|
{
|
|
if (UObject* Object = *ObjectIt)
|
|
{
|
|
if (!Object->IsValidLowLevel())
|
|
{
|
|
ensureMsgf(false, TEXT("Object in KismetInspector is invalid, see TTP 281915"));
|
|
continue;
|
|
}
|
|
|
|
SelectedObjects.Add(Object);
|
|
|
|
if (USCS_Node* SCSNode = Cast<USCS_Node>(Object))
|
|
{
|
|
// Edit the component template
|
|
UActorComponent* NodeComponent = SCSNode->ComponentTemplate;
|
|
if (NodeComponent != nullptr)
|
|
{
|
|
SelectionInfo.ObjectsForPropertyEditing.Add(NodeComponent);
|
|
SelectionInfo.EditableComponentTemplates.Add(NodeComponent);
|
|
}
|
|
}
|
|
else if (UK2Node* K2Node = Cast<UK2Node>(Object))
|
|
{
|
|
// Edit the component template if it exists
|
|
if (UK2Node_AddComponent* ComponentNode = Cast<UK2Node_AddComponent>(K2Node))
|
|
{
|
|
if (UActorComponent* Template = ComponentNode->GetTemplateFromNode())
|
|
{
|
|
SelectionInfo.ObjectsForPropertyEditing.Add(Template);
|
|
SelectionInfo.EditableComponentTemplates.Add(Template);
|
|
}
|
|
}
|
|
|
|
// See if we should edit properties of the node
|
|
if (K2Node->ShouldShowNodeProperties())
|
|
{
|
|
SelectionInfo.ObjectsForPropertyEditing.Add(Object);
|
|
}
|
|
}
|
|
else if (UActorComponent* ActorComponent = Cast<UActorComponent>(Object))
|
|
{
|
|
AActor* Owner = ActorComponent->GetOwner();
|
|
if(Owner != nullptr && Owner->HasAnyFlags(RF_ClassDefaultObject))
|
|
{
|
|
SelectionInfo.ObjectsForPropertyEditing.AddUnique(ActorComponent);
|
|
SelectionInfo.EditableComponentTemplates.Add(ActorComponent);
|
|
}
|
|
else
|
|
{
|
|
// We're editing a component that exists outside of a CDO, so just edit the component instance directly
|
|
SelectionInfo.ObjectsForPropertyEditing.AddUnique(ActorComponent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Editing any UObject*
|
|
SelectionInfo.ObjectsForPropertyEditing.AddUnique(Object);
|
|
}
|
|
}
|
|
}
|
|
|
|
// By default, no property filtering
|
|
SelectedObjectProperties.Empty();
|
|
|
|
// Add to the property filter list for any editable component templates
|
|
if (SelectionInfo.EditableComponentTemplates.Num())
|
|
{
|
|
for (auto CompIt = SelectionInfo.EditableComponentTemplates.CreateIterator(); CompIt; ++CompIt)
|
|
{
|
|
UActorComponent* EditableComponentTemplate = *CompIt;
|
|
check(EditableComponentTemplate != nullptr);
|
|
|
|
// Add all properties belonging to the component template class
|
|
for (TFieldIterator<FProperty> PropIt(EditableComponentTemplate->GetClass()); PropIt; ++PropIt)
|
|
{
|
|
FProperty* Property = *PropIt;
|
|
check(Property != nullptr);
|
|
|
|
AddPropertiesRecursive(Property);
|
|
}
|
|
|
|
// Attempt to locate a matching property for the current component template
|
|
for (auto ObjIt = SelectionInfo.ObjectsForPropertyEditing.CreateIterator(); ObjIt; ++ObjIt)
|
|
{
|
|
UObject* Object = *ObjIt;
|
|
check(Object != nullptr);
|
|
|
|
if (Object != EditableComponentTemplate)
|
|
{
|
|
if (FObjectProperty* ObjectProperty = FindFProperty<FObjectProperty>(Object->GetClass(), EditableComponentTemplate->GetFName()))
|
|
{
|
|
SelectedObjectProperties.Add(ObjectProperty);
|
|
}
|
|
else
|
|
{
|
|
FProperty* ReferencingProperty = FComponentEditorUtils::GetPropertyForEditableNativeComponent(EditableComponentTemplate);
|
|
if (ReferencingProperty == nullptr)
|
|
{
|
|
if (UActorComponent* Archetype = Cast<UActorComponent>(EditableComponentTemplate->GetArchetype()))
|
|
{
|
|
ReferencingProperty = FComponentEditorUtils::GetPropertyForEditableNativeComponent(Archetype);
|
|
}
|
|
}
|
|
if (ReferencingProperty)
|
|
{
|
|
SelectedObjectProperties.Add(ReferencingProperty);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PropertyViewTitle = Options.ForcedTitle;
|
|
bShowComponents = Options.bShowComponents;
|
|
|
|
// Update our context-sensitive editing widget
|
|
ContextualEditingBorderWidget->SetContent( MakeContextualEditingWidget(SelectionInfo, Options) );
|
|
}
|
|
|
|
bool SKismetInspector::IsStructViewPropertyReadOnly(const struct FPropertyAndParent& PropertyAndParent) const
|
|
{
|
|
const FProperty& Property = PropertyAndParent.Property;
|
|
if (Property.HasAnyPropertyFlags(CPF_EditConst))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SKismetInspector::IsAnyParentOrContainerSelected(const FPropertyAndParent& PropertyAndParent) const
|
|
{
|
|
for (const FProperty* CurrentProperty : PropertyAndParent.ParentProperties)
|
|
{
|
|
if (SelectedObjectProperties.Find(const_cast<FProperty*>(CurrentProperty)))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// the property might be the Inner property of an array (or Key/Value of a map), so check if the outer property is selected
|
|
const FProperty* CurrentOuter = CurrentProperty->GetOwner<FProperty>();
|
|
if (CurrentOuter != nullptr && SelectedObjectProperties.Find(const_cast<FProperty*>(CurrentOuter)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SKismetInspector::IsPropertyVisible( const FPropertyAndParent& PropertyAndParent ) const
|
|
{
|
|
const FProperty& Property = PropertyAndParent.Property;
|
|
|
|
// If we are in 'instance preview' - hide anything marked 'disabled edit on instance'
|
|
if ((ECheckBoxState::Checked == PublicViewState) && Property.HasAnyPropertyFlags(CPF_DisableEditOnInstance))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Only hide EditInstanceOnly properties if we are editing a CDO/archetype
|
|
bool bIsEditingTemplate = true;
|
|
for (const TWeakObjectPtr<UObject>& SelectedObject : SelectedObjects)
|
|
{
|
|
UObject* Object = SelectedObject.Get();
|
|
if (!Object || !Object->IsTemplate())
|
|
{
|
|
bIsEditingTemplate = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bIsEditingTemplate)
|
|
{
|
|
// check if the property (or any of its parent properties) was added by this blueprint
|
|
// this is necessary because of Instanced objects, which will have a different owning class yet are conceptually contained in this blueprint
|
|
bool bVariableAddedInCurrentBlueprint = false;
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor = BlueprintEditorPtr.Pin();
|
|
const UBlueprint* Blueprint = BlueprintEditor.IsValid() ? BlueprintEditor->GetBlueprintObj() : nullptr;
|
|
|
|
auto WasAddedInThisBlueprint = [Blueprint](const FProperty* Property)
|
|
{
|
|
if (const UClass* OwningClass = Property->GetOwnerClass())
|
|
{
|
|
return Blueprint && OwningClass->ClassGeneratedBy.Get() == Blueprint;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
bVariableAddedInCurrentBlueprint |= WasAddedInThisBlueprint(&Property);
|
|
|
|
for (const FProperty* Parent : PropertyAndParent.ParentProperties)
|
|
{
|
|
bVariableAddedInCurrentBlueprint |= WasAddedInThisBlueprint(Parent);
|
|
}
|
|
|
|
// if this property wasn't added in this blueprint, we want to filter it out if it (or any of its parents) are marked EditInstanceOnly or private
|
|
if (!bVariableAddedInCurrentBlueprint)
|
|
{
|
|
if (Property.HasAnyPropertyFlags(CPF_DisableEditOnTemplate) || Property.GetBoolMetaData(FBlueprintMetadata::MD_Private))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (const FProperty* Parent : PropertyAndParent.ParentProperties)
|
|
{
|
|
if (Property.HasAnyPropertyFlags(CPF_DisableEditOnTemplate) || Parent->GetBoolMetaData(FBlueprintMetadata::MD_Private))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// figure out if this Blueprint variable is an Actor variable
|
|
const FArrayProperty* ArrayProperty = CastField<const FArrayProperty>(&Property);
|
|
const FSetProperty* SetProperty = CastField<const FSetProperty>(&Property);
|
|
const FMapProperty* MapProperty = CastField<const FMapProperty>(&Property);
|
|
|
|
const FProperty* TestProperty = ArrayProperty ? ArrayProperty->Inner : &Property;
|
|
const FObjectPropertyBase* ObjectProperty = CastField<const FObjectPropertyBase>(TestProperty);
|
|
bool bIsActorProperty = (ObjectProperty != nullptr && ObjectProperty->PropertyClass && ObjectProperty->PropertyClass->IsChildOf(AActor::StaticClass()));
|
|
|
|
if (bIsEditingTemplate && Property.HasAnyPropertyFlags(CPF_DisableEditOnTemplate) && bIsActorProperty)
|
|
{
|
|
// Actor variables can't have default values (because Blueprint templates are library elements that can
|
|
// bridge multiple levels and different levels might not have the actor that the default is referencing).
|
|
return false;
|
|
}
|
|
|
|
bool bIsComponent = (ObjectProperty != nullptr && ObjectProperty->PropertyClass && ObjectProperty->PropertyClass->IsChildOf(UActorComponent::StaticClass()));
|
|
if (!bShowComponents && bIsComponent)
|
|
{
|
|
// Don't show sub components properties, thats what selecting components in the component tree is for.
|
|
return false;
|
|
}
|
|
|
|
// Filter down to selected properties only if set.
|
|
if (SelectedObjectProperties.Find(const_cast<FProperty*>(&Property)))
|
|
{
|
|
// If the current property is selected, it is visible.
|
|
return true;
|
|
}
|
|
else if (PropertyAndParent.ParentProperties.Num() > 0 && SelectedObjectProperties.Num() > 0)
|
|
{
|
|
if (IsAnyParentOrContainerSelected(PropertyAndParent))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if (ArrayProperty || MapProperty || SetProperty)
|
|
{
|
|
// .Find won't work here because the items inside of the container properties are not FProperties
|
|
for (const TWeakFieldPtr<FProperty>& CurProp : SelectedObjectProperties)
|
|
{
|
|
if ((ArrayProperty && (ArrayProperty->PropertyFlags & CPF_Edit) && CurProp->GetFName() == ArrayProperty->GetFName()) ||
|
|
(MapProperty && (MapProperty->PropertyFlags & CPF_Edit) && CurProp->GetFName() == MapProperty->GetFName()) ||
|
|
(SetProperty && (SetProperty->PropertyFlags & CPF_Edit) && CurProp->GetFName() == SetProperty->GetFName()))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SelectedObjectProperties.Num() == 0;
|
|
}
|
|
|
|
void SKismetInspector::SetPropertyWindowContents(TArray<UObject*> Objects)
|
|
{
|
|
if (FSlateApplication::IsInitialized())
|
|
{
|
|
check(PropertyView.IsValid());
|
|
PropertyView->SetObjects(Objects);
|
|
}
|
|
}
|
|
|
|
EVisibility SKismetInspector::GetPropertyViewVisibility() const
|
|
{
|
|
return bShowInspectorPropertyView? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
bool SKismetInspector::IsPropertyEditingEnabled() const
|
|
{
|
|
bool bIsEditable = true;
|
|
|
|
if (BlueprintEditorPtr.IsValid())
|
|
{
|
|
if (GetDefault<UEditorExperimentalSettings>()->bAllowPotentiallyUnsafePropertyEditing == false)
|
|
{
|
|
bIsEditable = BlueprintEditorPtr.Pin()->InEditingMode();
|
|
}
|
|
else
|
|
{
|
|
// This function is essentially for PIE use so if we are NOT doing PIE use the normal path
|
|
if (GEditor->GetPIEWorldContext() == nullptr)
|
|
{
|
|
bIsEditable = BlueprintEditorPtr.Pin()->InEditingMode();
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const TWeakObjectPtr<UObject>& SelectedObject : SelectedObjects)
|
|
{
|
|
if (UActorComponent* Component = Cast<UActorComponent>(SelectedObject.Get()))
|
|
{
|
|
if(!CastChecked<UActorComponent>(Component->GetArchetype())->IsEditableWhenInherited())
|
|
{
|
|
bIsEditable = false;
|
|
break;
|
|
}
|
|
}
|
|
else if(UEdGraphNode* EdGraphNode = Cast<UEdGraphNode>(SelectedObject.Get()))
|
|
{
|
|
if(UEdGraph* OuterGraph = EdGraphNode->GetGraph())
|
|
{
|
|
if(BlueprintEditorPtr.IsValid() && !BlueprintEditorPtr.Pin()->IsEditable(OuterGraph))
|
|
{
|
|
// Allow property editing on interface function graph terminator nodes (i.e. allow users to modify the interface function signature).
|
|
const bool bIsInterfaceGraphTerminatorNode = FBlueprintEditorUtils::IsInterfaceGraph(OuterGraph) && EdGraphNode->IsA<UK2Node_FunctionTerminator>();
|
|
if (!bIsInterfaceGraphTerminatorNode)
|
|
{
|
|
bIsEditable = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bIsEditable && (!IsPropertyEditingEnabledDelegate.IsBound() || IsPropertyEditingEnabledDelegate.Execute());
|
|
}
|
|
|
|
EVisibility SKismetInspector::GetInheritedBlueprintComponentWarningVisibility() const
|
|
{
|
|
bool bIsUneditableBlueprintComponent = false;
|
|
|
|
// Check to see if any selected components are inherited from blueprint
|
|
for (const TWeakObjectPtr<UObject>& SelectedObject : SelectedObjects)
|
|
{
|
|
UActorComponent* Component = Cast<UActorComponent>(SelectedObject.Get());
|
|
bIsUneditableBlueprintComponent = Component ? !CastChecked<UActorComponent>(Component->GetArchetype())->IsEditableWhenInherited() : false;
|
|
if (bIsUneditableBlueprintComponent)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bIsUneditableBlueprintComponent ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
void SKismetInspector::OnInheritedBlueprintComponentWarningHyperlinkClicked(const FSlateHyperlinkRun::FMetadata& Metadata)
|
|
{
|
|
if (BlueprintEditorPtr.IsValid())
|
|
{
|
|
UBlueprint* Blueprint = BlueprintEditorPtr.Pin()->GetBlueprintObj();
|
|
if (Blueprint && Blueprint->ParentClass->HasAllClassFlags(CLASS_CompiledFromBlueprint))
|
|
{
|
|
// Open the blueprint
|
|
GEditor->EditObject(CastChecked<UBlueprint>(Blueprint->ParentClass->ClassGeneratedBy));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ECheckBoxState SKismetInspector::GetPublicViewCheckboxState() const
|
|
{
|
|
return PublicViewState;
|
|
}
|
|
|
|
void SKismetInspector::SetPublicViewCheckboxState( ECheckBoxState InIsChecked )
|
|
{
|
|
PublicViewState = InIsChecked;
|
|
|
|
//reset the details view
|
|
TArray<UObject*> Objs;
|
|
for(auto It(SelectedObjects.CreateIterator());It;++It)
|
|
{
|
|
Objs.Add(It->Get());
|
|
}
|
|
SelectedObjects.Empty();
|
|
|
|
if(Objs.Num() > 1)
|
|
{
|
|
ShowDetailsForObjects(Objs);
|
|
}
|
|
else if(Objs.Num() == 1)
|
|
{
|
|
ShowDetailsForSingleObject(Objs[0], FShowDetailsOptions(PropertyViewTitle));
|
|
}
|
|
|
|
BlueprintEditorPtr.Pin()->StartEditingDefaults();
|
|
}
|
|
|
|
void SKismetInspector::OnFinishedChangingProperties(const FPropertyChangedEvent& InPropertyChangedEvent)
|
|
{
|
|
ImportNamespacesForPropertyValue(InPropertyChangedEvent.MemberProperty);
|
|
}
|
|
|
|
void SKismetInspector::ImportNamespacesForPropertyValue(const FProperty* InProperty) const
|
|
{
|
|
if (!GetDefault<UBlueprintEditorSettings>()->bEnableNamespaceImportingFeatures)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!InProperty || SelectedObjects.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor = BlueprintEditorPtr.Pin();
|
|
if (BlueprintEditor.IsValid())
|
|
{
|
|
// Gather all namespace identifier strings associated with the property's value for each edited object.
|
|
TSet<FString> AssociatedNamespaces;
|
|
for (const TWeakObjectPtr<UObject>& SelectedObjectPtr : SelectedObjects)
|
|
{
|
|
if (const UObject* SelectedObject = SelectedObjectPtr.Get())
|
|
{
|
|
const UStruct* SelectedType = SelectedObject->GetClass();
|
|
|
|
// Ensure that the selected object type matches the property's owner.
|
|
// For example, a details customization may select an unrelated object
|
|
// and then customize each row with an external object reference. In
|
|
// those cases, the customization would need to handle this explicitly.
|
|
if (!InProperty->IsIn(SelectedType))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FBlueprintNamespaceUtilities::GetPropertyValueNamespaces(InProperty, SelectedObject, AssociatedNamespaces);
|
|
}
|
|
}
|
|
|
|
// Auto-import any namespace(s) associated with the property's value into the current editor context.
|
|
if (AssociatedNamespaces.Num() > 0)
|
|
{
|
|
FBlueprintEditor::FImportNamespaceExParameters Params;
|
|
Params.NamespacesToImport = MoveTemp(AssociatedNamespaces);
|
|
BlueprintEditor->ImportNamespaceEx(Params);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#undef LOCTEXT_NAMESPACE
|