Files
UnrealEngine/Engine/Plugins/MetaHuman/MetaHumanLiveLink/Source/LiveLinkFaceSourceEditor/Private/LiveLinkFaceSourceCustomization.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

220 lines
5.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LiveLinkFaceSourceCustomization.h"
#include "DetailCategoryBuilder.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "LiveLinkFaceSourceSettings.h"
#include "CoreMinimal.h"
#include "ObjectTools.h"
#include "SLiveLinkFaceDiscoveryPanel.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Text/STextBlock.h"
#define LOCTEXT_NAMESPACE "LiveLinkFaceSourceCustomization"
FLiveLinkFaceSourceCustomization::FLiveLinkFaceSourceCustomization()
: LiveLinkFaceDiscovery(MakeShared<FLiveLinkFaceDiscovery>())
{
LiveLinkFaceDiscovery->OnServersUpdated.BindLambda([this](const TSet<FLiveLinkFaceDiscovery::FServer>& InServers)
{
ListServers.Reset();
for (const FLiveLinkFaceDiscovery::FServer& Server : InServers)
{
ListServers.Add(MakeShared<FLiveLinkFaceDiscovery::FServer>(Server));
}
ListServers.Sort([](const TSharedPtr<FLiveLinkFaceDiscovery::FServer>& Server1, const TSharedPtr<FLiveLinkFaceDiscovery::FServer>& Server2)
{
if (Server1->Name == Server2->Name)
{
return Server1->Id < Server2->Id;
}
else
{
return Server1->Name < Server2->Name;
}
});
if (DiscoveryPanel.IsValid())
{
DiscoveryPanel->Refresh();
}
});
LiveLinkFaceDiscovery->Start();
}
FLiveLinkFaceSourceCustomization::~FLiveLinkFaceSourceCustomization()
{
LiveLinkFaceDiscovery->OnServersUpdated.Unbind();
LiveLinkFaceDiscovery->Stop();
}
void FLiveLinkFaceSourceCustomization::CustomizeDetails(IDetailLayoutBuilder& InDetailBuilder)
{
TArray<TWeakObjectPtr<UObject>> Objects;
InDetailBuilder.GetObjectsBeingCustomized(Objects);
check(Objects.Num() == 1);
ULiveLinkFaceSourceSettings* Settings = Cast<ULiveLinkFaceSourceSettings>(Objects[0]);
IDetailCategoryBuilder& CategoryBuilder = InDetailBuilder.EditCategory("Server", LOCTEXT("Server", "Server"), ECategoryPriority::Important);
DiscoveryPanel = SNew(SLiveLinkFaceDiscoveryPanel)
.Servers(&ListServers)
.OnServerSingleClicked_Lambda([this, Settings](const FString& InServerHost, const uint16 InServerPort)
{
Settings->SetAddress(InServerHost);
Settings->SetPort(InServerPort);
Validate(Settings);
})
.OnServerDoubleClicked_Lambda([this, Settings](const FString& InServerHost, const uint16 InServerPort)
{
Settings->SetAddress(InServerHost);
Settings->SetPort(InServerPort);
if (Validate(Settings))
{
Settings->RequestConnect();
}
});
CategoryBuilder.AddCustomRow(LOCTEXT("Discovery", "Discovery"))
.WholeRowContent()
[
DiscoveryPanel.ToSharedRef()
];
AddressTextBox = SNew(SEditableTextBox)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text_Lambda([Settings]
{
return FText::FromString(Settings->GetAddress());
})
.OnTextChanged_Lambda([this, Settings](const FText& InAddress)
{
Settings->SetAddress(InAddress.ToString());
Validate(Settings);
});
const FText AddressLabel = LOCTEXT("Address", "Address");
CategoryBuilder.AddCustomRow(AddressLabel)
.NameContent()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.ToolTipText(LOCTEXT("AddressTooltip", "The network address of the server."))
.Text(AddressLabel)
]
.ValueContent()
[
AddressTextBox.ToSharedRef()
];
PortEntryBox = SNew(SNumericEntryBox<uint16>)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Value_Lambda([Settings]
{
return Settings->GetPort();
})
.OnValueChanged_Lambda([this, Settings](const uint16 InPort)
{
Settings->SetPort(InPort);
Validate(Settings);
});
const FText PortLabel = LOCTEXT("Port", "Port");
CategoryBuilder.AddCustomRow(PortLabel)
.NameContent()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.ToolTipText(LOCTEXT("PortTooltip", "The control port of the server."))
.Text(PortLabel)
]
.ValueContent()
[
PortEntryBox.ToSharedRef()
];
SubjectNameTextBox = SNew(SEditableTextBox)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text_Lambda([Settings]
{
return FText::FromString(Settings->GetSubjectName());
})
.OnTextChanged_Lambda([this, Settings](const FText& InSubjectName)
{
Settings->SetSubjectName(InSubjectName.ToString());
Validate(Settings);
});
const FText SubjectNameLabel = LOCTEXT("SubjectName", "Subject Name");
CategoryBuilder.AddCustomRow(SubjectNameLabel)
.NameContent()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.ToolTipText(LOCTEXT("SubjectNameTooltip", "The subject name to assign to this animation stream."))
.Text(SubjectNameLabel)
]
.ValueContent()
[
SubjectNameTextBox.ToSharedRef()
];
ButtonTextStyle = FCoreStyle::Get().GetWidgetStyle<FTextBlockStyle>("ButtonText");
ButtonTextStyle.SetFont(IDetailLayoutBuilder::GetDetailFont());
CategoryBuilder.AddCustomRow(LOCTEXT("Connect", "Connect"))
.ValueContent()
[
SNew(SButton)
.Text(LOCTEXT("Connect", "Connect"))
.TextStyle(&ButtonTextStyle)
.HAlign(HAlign_Center)
.IsEnabled_Lambda([this]
{
return bIsConnectEnabled;
})
.OnClicked_Lambda([Settings]()
{
Settings->RequestConnect();
return FReply::Handled();
})
];
}
TSharedRef<IDetailCustomization> FLiveLinkFaceSourceCustomization::MakeInstance()
{
return MakeShared<FLiveLinkFaceSourceCustomization>();
}
bool FLiveLinkFaceSourceCustomization::Validate(const ULiveLinkFaceSourceSettings* InSettings)
{
bool bIsAddressValid = false;
bool bIsSubjectValid = false;
if (ensure(IsValid(InSettings)))
{
bIsAddressValid = InSettings->IsAddressValid();
if (!bIsAddressValid)
{
AddressTextBox->SetError(LOCTEXT("InvalidAddress", "Invalid address"));
}
else
{
AddressTextBox->SetError(TEXT(""));
}
FText InvalidReason;
const FName TestSubjectName(InSettings->GetSubjectName());
bIsSubjectValid = TestSubjectName.IsValidObjectName(InvalidReason);
SubjectNameTextBox->SetError(InvalidReason);
}
bIsConnectEnabled = bIsAddressValid && bIsSubjectValid;
return bIsConnectEnabled;
}
#undef LOCTEXT_NAMESPACE