Added ndi receiver and sdi outputs

This commit is contained in:
mchara40
2025-08-25 13:16:29 +03:00
parent 11b8be19f6
commit d046828313
125 changed files with 14757 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
/*
Copyright (C) 2024 Vizrt NDI AB. All rights reserved.
This file and its use within a Product is bound by the terms of NDI SDK license that was provided
as part of the NDI SDK. For more information, please review the license and the NDI SDK documentation.
*/
#include <Factories/NDIMediaReceiverFactory.h>
#include <AssetTypeCategories.h>
#include <Objects/Media/NDIMediaReceiver.h>
#define LOCTEXT_NAMESPACE "NDIIOEditorMediaReceiverFactory"
UNDIMediaReceiverFactory::UNDIMediaReceiverFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {
this->bCreateNew = true;
this->bEditAfterNew = true;
this->SupportedClass = UNDIMediaReceiver::StaticClass();
}
FText UNDIMediaReceiverFactory::GetDisplayName() const { return LOCTEXT("NDIMediaReceiverFactoryDisplayName", "NDI Media Receiver"); }
uint32 UNDIMediaReceiverFactory::GetMenuCategories() const
{
return EAssetTypeCategories::Media;
}
UObject* UNDIMediaReceiverFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
return NewObject<UNDIMediaReceiver>(InParent, InClass, InName, Flags | RF_Transactional);
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,33 @@
/*
Copyright (C) 2024 Vizrt NDI AB. All rights reserved.
This file and its use within a Product is bound by the terms of NDI SDK license that was provided
as part of the NDI SDK. For more information, please review the license and the NDI SDK documentation.
*/
#include <Factories/NDIMediaSenderFactory.h>
#include <AssetTypeCategories.h>
#include <Objects/Media/NDIMediaSender.h>
#define LOCTEXT_NAMESPACE "NDIIOEditorMediaSenderFactory"
UNDIMediaSenderFactory::UNDIMediaSenderFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {
bCreateNew = true;
bEditAfterNew = true;
this->SupportedClass = UNDIMediaSender::StaticClass();
}
FText UNDIMediaSenderFactory::GetDisplayName() const { return LOCTEXT("NDIMediaSenderFactoryDisplayName", "NDI Media Sender"); }
uint32 UNDIMediaSenderFactory::GetMenuCategories() const { return EAssetTypeCategories::Media; }
UObject* UNDIMediaSenderFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
return NewObject<UNDIMediaSender>(InParent, InClass, InName, Flags | RF_Transactional);
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,33 @@
/*
Copyright (C) 2024 Vizrt NDI AB. All rights reserved.
This file and its use within a Product is bound by the terms of NDI SDK license that was provided
as part of the NDI SDK. For more information, please review the license and the NDI SDK documentation.
*/
#include <Factories/NDIMediaSoundWaveFactory.h>
#include <AssetTypeCategories.h>
#include <Objects/Media/NDIMediaSoundWave.h>
#define LOCTEXT_NAMESPACE "NDIIOEditorMediaSoundWaveFactory"
UNDIMediaSoundWaveFactory::UNDIMediaSoundWaveFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {
this->bCreateNew = true;
this->bEditAfterNew = true;
this->SupportedClass = UNDIMediaSoundWave::StaticClass();
}
FText UNDIMediaSoundWaveFactory::GetDisplayName() const { return LOCTEXT("NDIMediaSoundWaveFactoryDisplayName", "NDI Media Sound Wave"); }
uint32 UNDIMediaSoundWaveFactory::GetMenuCategories() const { return EAssetTypeCategories::Sounds; }
UObject* UNDIMediaSoundWaveFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
return NewObject<UNDIMediaSoundWave>(InParent, InName, Flags | RF_Transactional);
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,40 @@
/*
Copyright (C) 2024 Vizrt NDI AB. All rights reserved.
This file and its use within a Product is bound by the terms of NDI SDK license that was provided
as part of the NDI SDK. For more information, please review the license and the NDI SDK documentation.
*/
#include <Factories/NDIMediaTexture2DFactory.h>
#include <AssetTypeCategories.h>
#include <Objects/Media/NDIMediaTexture2D.h>
#include <Misc/EngineVersionComparison.h>
#define LOCTEXT_NAMESPACE "NDIIOEditorMediaSoundWaveFactory"
UNDIMediaTexture2DFactory::UNDIMediaTexture2DFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {
this->bCreateNew = true;
this->bEditAfterNew = true;
this->SupportedClass = UNDIMediaTexture2D::StaticClass();
}
FText UNDIMediaTexture2DFactory::GetDisplayName() const { return LOCTEXT("NDIMediaTexture2DFactoryDisplayName", "NDI Media Texture2D"); }
uint32 UNDIMediaTexture2DFactory::GetMenuCategories() const { return EAssetTypeCategories::Textures; }
UObject* UNDIMediaTexture2DFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
if (UNDIMediaTexture2D* Resource = NewObject<UNDIMediaTexture2D>(InParent, InName, Flags | RF_Transactional))
{
Resource->UpdateResource();
return Resource;
}
return nullptr;
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,136 @@
/*
Copyright (C) 2024 Vizrt NDI AB. All rights reserved.
This file and its use within a Product is bound by the terms of NDI SDK license that was provided
as part of the NDI SDK. For more information, please review the license and the NDI SDK documentation.
*/
#include <NDIIOEditorModule.h>
#include <Editor.h>
#include <PropertyEditorModule.h>
#include <IPlacementModeModule.h>
#include <Interfaces/IPluginManager.h>
#include <Styling/SlateStyleRegistry.h>
#include <Actors/NDIReceiveActor.h>
#include <Actors/NDIBroadcastActor.h>
#include <Framework/Application/SlateApplication.h>
#include <Misc/EngineVersionComparison.h>
#include "Widgets/NDIWidgets.h"
#define LOCTEXT_NAMESPACE "FNDIEditorModule"
#define IMAGE_BRUSH(RelativePath, ...) FSlateImageBrush(StyleInstance->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__)
#define PLACEMENT_CATEGORY TEXT("NDI(R)")
#define PLACEMENT_LOCTEXT NSLOCTEXT("Vizrt", "NDI", "NDI(R)")
#define PLACEMENT_TEXT TEXT("PMNDI")
void FNDIIOEditorModule::StartupModule()
{
const FName& CategoryName = PLACEMENT_CATEGORY;
IPlacementModeModule& PlacementModeModule = IPlacementModeModule::Get();
const FVector2D Icon20x20(20.0f, 20.0f);
const FVector2D Icon64x64(64.0f, 64.0f);
this->StyleInstance = MakeUnique<FSlateStyleSet>("NDIEditorStyle");
if (IPlugin* NDIIOPlugin = IPluginManager::Get().FindPlugin("NDIIOPlugin").Get())
{
StyleInstance->SetContentRoot(FPaths::Combine(NDIIOPlugin->GetContentDir(), TEXT("Editor/Icons")));
StyleInstance->Set("ClassThumbnail.NDIBroadcastActor", new IMAGE_BRUSH("NDIBroadcastActorIcon_x64", Icon64x64));
StyleInstance->Set("ClassIcon.NDIBroadcastActor", new IMAGE_BRUSH("NDIBroadcastActorIcon_x20", Icon20x20));
StyleInstance->Set("ClassThumbnail.NDIReceiveActor", new IMAGE_BRUSH("NDIReceiveActorIcon_x64", Icon64x64));
StyleInstance->Set("ClassIcon.NDIReceiveActor", new IMAGE_BRUSH("NDIReceiveActorIcon_x20", Icon20x20));
StyleInstance->Set("ClassThumbnail.NDIMediaReceiver", new IMAGE_BRUSH("NDIReceiverIcon_x64", Icon64x64));
StyleInstance->Set("ClassIcon.NDIMediaReceiver", new IMAGE_BRUSH("NDIReceiverIcon_x20", Icon20x20));
StyleInstance->Set("ClassThumbnail.NDIMediaSender", new IMAGE_BRUSH("NDISenderIcon_x64", Icon64x64));
StyleInstance->Set("ClassIcon.NDIMediaSender", new IMAGE_BRUSH("NDISenderIcon_x20", Icon20x20));
StyleInstance->Set("ClassThumbnail.NDIMediaSoundWave", new IMAGE_BRUSH("NDISoundWaveIcon_x64", Icon64x64));
StyleInstance->Set("ClassIcon.NDIMediaSoundWave", new IMAGE_BRUSH("NDISoundWaveIcon_x20", Icon20x20));
StyleInstance->Set("ClassThumbnail.NDIMediaTexture2D", new IMAGE_BRUSH("NDIVideoTextureIcon_x64", Icon64x64));
StyleInstance->Set("ClassIcon.NDIMediaTexture2D", new IMAGE_BRUSH("NDIVideoTextureIcon_x20", Icon20x20));
FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance.Get());
PlacementModeModule.RegisterPlacementCategory(
FPlacementCategoryInfo(
PLACEMENT_LOCTEXT,
CategoryName,
PLACEMENT_TEXT,
41, // FBuiltInPlacementCategories::Volumes() == 40
true
)
);
}
// Get the Registered Placement Category
if (const FPlacementCategoryInfo* PlacementCategoryInformation = PlacementModeModule.GetRegisteredPlacementCategory(CategoryName))
{
// Register the NDI Broadcast Actor a placeable item within the editor
PlacementModeModule.RegisterPlaceableItem(PlacementCategoryInformation->UniqueHandle, MakeShareable(
new FPlaceableItem(
*UActorFactory::StaticClass(),
#if (ENGINE_MAJOR_VERSION > 5) || ((ENGINE_MAJOR_VERSION == 5) && (ENGINE_MINOR_VERSION >= 6)) // 5.6 or later
FAssetData(GetDefault<ANDIBroadcastActor>()),
#else
FAssetData(ANDIBroadcastActor::StaticClass()->ClassDefaultObject),
#endif
FName("ClassThumbnail.NDIBroadcastActor"),
NAME_None,
TOptional<FLinearColor>(),
10,
NSLOCTEXT("Vizrt", "NDIBroadcastActor", "NDI Broadcast Actor")
))
);
// Register the NDI Receive Actor a placeable item within the editor
PlacementModeModule.RegisterPlaceableItem(PlacementCategoryInformation->UniqueHandle, MakeShareable(
new FPlaceableItem(
*UActorFactory::StaticClass(),
#if (ENGINE_MAJOR_VERSION > 5) || ((ENGINE_MAJOR_VERSION == 5) && (ENGINE_MINOR_VERSION >= 6)) // 5.6 or later
FAssetData(GetDefault<ANDIReceiveActor>()),
#else
FAssetData(ANDIReceiveActor::StaticClass()->ClassDefaultObject),
#endif
FName("ClassThumbnail.NDIReceiveActor"),
NAME_None,
TOptional<FLinearColor>(),
20,
NSLOCTEXT("Vizrt", "NDIReceiveActor", "NDI Receive Actor")
))
);
}
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomPropertyTypeLayout(FNDIConnectionInformation::StaticStruct()->GetFName(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FNDIConnectionInformationCustomization::MakeInstance));
}
void FNDIIOEditorModule::ShutdownModule()
{
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.UnregisterCustomPropertyTypeLayout(FNDIConnectionInformation::StaticStruct()->GetFName());
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance.Get());
StyleInstance.Reset();
IPlacementModeModule& PlacementModeModule = IPlacementModeModule::Get();
PlacementModeModule.UnregisterPlacementCategory(PLACEMENT_CATEGORY);
}
#undef PLACEMENT_CATEGORY
#undef PLACEMENT_LOCTEXT
#undef PLACEMENT_TEXT
#undef IMAGE_BRUSH
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,380 @@
/*
Copyright (C) 2024 Vizrt NDI AB. All rights reserved.
This file and its use within a Product is bound by the terms of NDI SDK license that was provided
as part of the NDI SDK. For more information, please review the license and the NDI SDK documentation.
*/
#include <Widgets/NDIWidgets.h>
#include <Services/NDIFinderService.h>
#include <DetailLayoutBuilder.h>
#include <DetailWidgetRow.h>
#include <Editor.h>
#include <IDetailChildrenBuilder.h>
#include <IPropertyUtilities.h>
#include <PropertyHandle.h>
#include <Framework/MultiBox/MultiBoxBuilder.h>
#include <Widgets/Input/SComboButton.h>
#include <atomic>
#define LOCTEXT_NAMESPACE "UNDIWidgets"
/**
Organizes NDI sources into a tree
*/
struct FNDISourceTreeItem
{
TArray<TSharedRef<FNDISourceTreeItem> > Children;
FNDIConnectionInformation NDISource;
FText DisplayText;
bool IsExpanded { false };
bool IsSelected { false };
FNDISourceTreeItem()
{}
FNDISourceTreeItem(const FText& DisplayTextIn)
: DisplayText(DisplayTextIn)
{}
FNDISourceTreeItem(const FNDIConnectionInformation& Source)
: NDISource(Source)
{}
FNDISourceTreeItem(TSharedRef<FNDISourceTreeItem>&& Child)
{
Children.Add(Child);
}
static const TSharedRef<FNDISourceTreeItem>* FindMachineNode(const FNDISourceTreeItem& RootNode, const FNDIConnectionInformation& SourceItem)
{
const TSharedRef<FNDISourceTreeItem>* MachineNode = nullptr;
if(!SourceItem.MachineName.IsEmpty())
{
const FString& SearchName = SourceItem.MachineName;
MachineNode = RootNode.Children.FindByPredicate([&SearchName](const TSharedRef<FNDISourceTreeItem>& Child)
{
if(Child->Children.Num() > 0)
return Child->Children[0]->NDISource.MachineName == SearchName;
else
return false;
});
}
else if(!SourceItem.Url.IsEmpty())
{
const FString& SearchName = SourceItem.Url;
MachineNode = RootNode.Children.FindByPredicate([&SearchName](const TSharedRef<FNDISourceTreeItem>& Child)
{
if(Child->Children.Num() > 0)
return Child->Children[0]->NDISource.Url == SearchName;
else
return false;
});
}
return MachineNode;
}
static const TSharedRef<FNDISourceTreeItem>* FindStreamNodeInMachineNode(const TSharedRef<FNDISourceTreeItem>& MachineNode, const FNDIConnectionInformation& SourceItem)
{
const TSharedRef<FNDISourceTreeItem>* StreamNode = nullptr;
if(!SourceItem.StreamName.IsEmpty())
{
const FString& SearchName = SourceItem.StreamName;
StreamNode = MachineNode->Children.FindByPredicate([&SearchName](const TSharedRef<FNDISourceTreeItem>& Child)
{
return Child->NDISource.StreamName == SearchName;
});
}
else if(!SourceItem.Url.IsEmpty())
{
const FString& SearchName = SourceItem.Url;
StreamNode = MachineNode->Children.FindByPredicate([&SearchName](const TSharedRef<FNDISourceTreeItem>& Child)
{
return Child->NDISource.Url == SearchName;
});
}
return StreamNode;
}
void SetFromSources(const TArray<FNDIConnectionInformation>& SourceItems, const FText& SearchingTxt, bool StartExpanded)
{
FNDISourceTreeItem RootNode;
//
// Build new tree
//
for(int32 i = 0; i < SourceItems.Num(); ++i)
{
const TSharedRef<FNDISourceTreeItem>* MachineNode = FindMachineNode(RootNode, SourceItems[i]);
if(MachineNode != nullptr)
{
FNDISourceTreeItem* NewNode = new FNDISourceTreeItem(SourceItems[i]);
(*MachineNode)->Children.Add(MakeShareable(NewNode));
}
else
{
FNDISourceTreeItem* NewNode = new FNDISourceTreeItem(SourceItems[i]);
FNDISourceTreeItem* NewMachineNode = new FNDISourceTreeItem(MakeShareable(NewNode));
RootNode.Children.Add(MakeShareable(NewMachineNode));
}
}
//
// Preserve expansion and selection state by matching with old tree
//
for(int32 i = 0; i < RootNode.Children.Num(); ++i)
{
const TSharedRef<FNDISourceTreeItem>* OldMachineNode = FindMachineNode(*this, RootNode.Children[i]->Children[0]->NDISource);
if(OldMachineNode != nullptr)
{
RootNode.Children[i]->IsExpanded = (*OldMachineNode)->IsExpanded;
for(int32 j = 0; j < RootNode.Children[i]->Children.Num(); ++j)
{
const TSharedRef<FNDISourceTreeItem>* OldStreamNode = FindStreamNodeInMachineNode(*OldMachineNode, RootNode.Children[i]->Children[j]->NDISource);
if(OldStreamNode != nullptr)
{
RootNode.Children[i]->Children[j]->IsSelected = (*OldStreamNode)->IsSelected;
}
}
}
else
{
RootNode.Children[i]->IsExpanded = StartExpanded;
}
}
if(RootNode.Children.Num() == 0)
{
RootNode.Children.Add(MakeShareable(new FNDISourceTreeItem(SearchingTxt)));
}
//
// Set to new tree
//
*this = RootNode;
}
};
/**
A menu widget containing NDI sources
*/
DECLARE_DELEGATE_OneParam(FOnSourceClicked, FNDIConnectionInformation);
class SNDISourcesMenu : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SNDISourcesMenu)
: _OnSourceClicked()
{}
SLATE_EVENT(FOnSourceClicked, OnSourceClicked)
SLATE_END_ARGS()
SNDISourcesMenu()
{}
virtual ~SNDISourcesMenu()
{
FNDIFinderService::EventOnNDISourceCollectionChanged.Remove(SourceCollectionChangedEventHandle);
SourceCollectionChangedEventHandle.Reset();
}
void Construct(const FArguments& InArgs)
{
OnSourceClicked = InArgs._OnSourceClicked;
ChildSlot
[
SNew(SComboButton)
.ButtonContent()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.ToolTipText(LOCTEXT("NDI Sources Tip", "Currently Available NDI Sources"))
.Text(LOCTEXT("NDI Sources", "NDI Sources"))
]
.OnGetMenuContent_Lambda([this]() -> TSharedRef<SWidget>
{
FMenuBuilder MenuBuilder(true, nullptr);
for (const auto& Sources : SourceTreeItems.Children)
ConstructSourceMenu(MenuBuilder, Sources.Get());
return MenuBuilder.MakeWidget();
})
];
UpdateSources = true;
FNDIFinderService::EventOnNDISourceCollectionChanged.Remove(SourceCollectionChangedEventHandle);
SourceCollectionChangedEventHandle.Reset();
SourceCollectionChangedEventHandle = FNDIFinderService::EventOnNDISourceCollectionChanged.AddLambda([this]()
{
UpdateSources = true;
});
}
virtual void Tick(const FGeometry& AllottedGeometry, const double CurrentTime, const float DeltaTime) override
{
bool IsDifferent = false;
if (UpdateSources.exchange(false))
{
IsDifferent = FNDIFinderService::UpdateSourceCollection(SourceItems);
}
if (SourceItems.Num() == 0)
{
FText NewSearchingTxt;
double WholeTime = 0.0;
double FracTime = FMath::Modf(CurrentTime, &WholeTime);
if(FracTime < 1/4.0)
NewSearchingTxt = FText(LOCTEXT("NDI Sources Searching0", "Searching"));
else if(FracTime < 2/4.0)
NewSearchingTxt = FText(LOCTEXT("NDI Sources Searching1", "Searching."));
else if(FracTime < 3/4.0)
NewSearchingTxt = FText(LOCTEXT("NDI Sources Searching2", "Searching.."));
else
NewSearchingTxt = FText(LOCTEXT("NDI Sources Searching3", "Searching..."));
if(!NewSearchingTxt.EqualTo(SearchingTxt))
{
SearchingTxt = NewSearchingTxt;
IsDifferent = true;
}
}
if (IsDifferent)
{
SourceTreeItems.SetFromSources(SourceItems, SearchingTxt, false);
Invalidate(EInvalidateWidgetReason::PaintAndVolatility | EInvalidateWidgetReason::ChildOrder);
}
SCompoundWidget::Tick(AllottedGeometry, CurrentTime, DeltaTime);
}
protected:
void ConstructSourceMenu(FMenuBuilder& MenuBuilder, const FNDISourceTreeItem& SourceTreeItem)
{
if (SourceTreeItem.NDISource.IsValid())
{
MenuBuilder.AddMenuEntry(
FText::FromString(SourceTreeItem.NDISource.StreamName),
FText::GetEmpty(),
FSlateIcon(),
FUIAction(FExecuteAction::CreateLambda([this,&SourceTreeItem]()
{
this->OnSourceClicked.ExecuteIfBound(SourceTreeItem.NDISource);
})),
NAME_None,
EUserInterfaceActionType::Button
);
}
else if (SourceTreeItem.Children.Num() > 0)
{
MenuBuilder.AddSubMenu(
FText::FromString(SourceTreeItem.Children[0]->NDISource.MachineName),
FText::GetEmpty(),
FNewMenuDelegate::CreateLambda([this,&SourceTreeItem](FMenuBuilder& MenuBuilder)
{
for(const auto& ChildSource : SourceTreeItem.Children)
ConstructSourceMenu(MenuBuilder, ChildSource.Get());
})
);
}
else if (!SourceTreeItem.DisplayText.IsEmpty())
{
MenuBuilder.AddMenuEntry(
SourceTreeItem.DisplayText,
FText::GetEmpty(),
FSlateIcon(),
FUIAction(FExecuteAction::CreateLambda([this]
{
})),
NAME_None,
EUserInterfaceActionType::Button
);
}
}
private:
TArray<FNDIConnectionInformation> SourceItems;
FText SearchingTxt;
FNDISourceTreeItem SourceTreeItems;
FDelegateHandle SourceCollectionChangedEventHandle;
std::atomic_bool UpdateSources { false };
FOnSourceClicked OnSourceClicked;
};
/**
Customization of NDIConnectionInformation property
by including a menu to select from currently available NDI sources
*/
TSharedRef<IPropertyTypeCustomization> FNDIConnectionInformationCustomization::MakeInstance()
{
return MakeShareable(new FNDIConnectionInformationCustomization);
}
void FNDIConnectionInformationCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
HeaderRow.NameContent()
[
PropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew(SNDISourcesMenu)
.OnSourceClicked_Lambda([this,PropertyHandle](FNDIConnectionInformation Source)
{
TArray<void*> RawData;
PropertyHandle->AccessRawData(RawData);
FNDIConnectionInformation* ConnectionInformation = reinterpret_cast<FNDIConnectionInformation*>(RawData[0]);
if (ConnectionInformation != nullptr)
{
ConnectionInformation->Url = "";
PropertyHandle->GetChildHandle("SourceName")->SetValue(Source.SourceName);
}
})
].IsEnabled(true);
}
void FNDIConnectionInformationCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
TSharedPtr<IPropertyUtilities> PropertyUtils = CustomizationUtils.GetPropertyUtilities();
uint32 NumberOfChild;
if (PropertyHandle->GetNumChildren(NumberOfChild) == FPropertyAccess::Success)
{
for (uint32 Index = 0; Index < NumberOfChild; ++Index)
{
TSharedRef<IPropertyHandle> ChildPropertyHandle = PropertyHandle->GetChildHandle(Index).ToSharedRef();
ChildBuilder.AddProperty(ChildPropertyHandle)
.ShowPropertyButtons(true)
.IsEnabled(MakeAttributeLambda([=] { return !PropertyHandle->IsEditConst() && PropertyUtils->IsPropertyEditingEnabled(); }));
}
}
}
#undef LOCTEXT_NAMESPACE