Files
UnrealEngine/Engine/Plugins/Animation/LiveLink/Source/LiveLinkEditor/Private/LiveLinkPanelController.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

257 lines
7.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LiveLinkPanelController.h"
#include "Async/TaskGraphInterfaces.h"
#include "Framework/Commands/UICommandList.h"
#include "Features/IModularFeatures.h"
#include "IDetailsView.h"
#include "LiveLinkClient.h"
#include "LiveLinkClientCommands.h"
#include "LiveLinkClientPanelViews.h"
#include "Misc/ConfigCacheIni.h"
#include "SLiveLinkDataView.h"
FLiveLinkPanelController::FLiveLinkPanelController(TAttribute<bool> bInReadOnly)
{
bSeparateSourcesSubjects = GConfig->GetBoolOrDefault(TEXT("LiveLink"), TEXT("bPanelControllerSeparateSourcesSubjects"), false, GEngineIni);
Client = (FLiveLinkClient*)&IModularFeatures::Get().GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName);
OnSourcesChangedHandle = Client->OnLiveLinkSourcesChanged().AddRaw(this, &FLiveLinkPanelController::OnSourcesChangedHandler);
OnSubjectsChangedHandle = Client->OnLiveLinkSubjectsChanged().AddRaw(this, &FLiveLinkPanelController::OnSubjectsChangedHandler);
FLiveLinkClientCommands::Register();
CommandList = MakeShared<FUICommandList>();
BindCommands();
SourcesView = MakeShared<FLiveLinkSourcesView>(Client, CommandList, bInReadOnly, FLiveLinkSourcesView::FOnSourceSelectionChanged::CreateRaw(this, &FLiveLinkPanelController::OnSourceSelectionChangedHandler));
SubjectsView = MakeShared<FLiveLinkSubjectsView>(FLiveLinkSubjectsView::FOnSubjectSelectionChanged::CreateRaw(this, &FLiveLinkPanelController::OnSubjectSelectionChangedHandler), CommandList, bInReadOnly);
SourcesDetailsView = UE::LiveLink::CreateSourcesDetailsView(SourcesView, bInReadOnly);
SubjectsDetailsView = UE::LiveLink::CreateSubjectsDetailsView(Client, bInReadOnly);
RebuildSourceList();
RebuildSubjectList();
}
FLiveLinkPanelController::~FLiveLinkPanelController()
{
if (IModularFeatures::Get().IsModularFeatureAvailable(ILiveLinkClient::ModularFeatureName))
{
ILiveLinkClient* LiveLinkClient = (FLiveLinkClient*)&IModularFeatures::Get().GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName);
if (LiveLinkClient)
{
LiveLinkClient->OnLiveLinkSourcesChanged().Remove(OnSourcesChangedHandle);
OnSourcesChangedHandle.Reset();
LiveLinkClient->OnLiveLinkSubjectsChanged().Remove(OnSubjectsChangedHandle);
OnSubjectsChangedHandle.Reset();
}
}
}
void FLiveLinkPanelController::OnSourceSelectionChangedHandler(FLiveLinkSourceUIEntryPtr Entry, ESelectInfo::Type SelectionType) const
{
if (bSelectionChangedGuard)
{
return;
}
TGuardValue<bool> ReentrantGuard(bSelectionChangedGuard, true);
SubjectsDetailsView->SetSubjectKey(FLiveLinkSubjectKey());
int32 FoundSubjectEntryIndex = INDEX_NONE;
if (Entry.IsValid())
{
SourcesDetailsView->SetObject(Entry->GetSourceSettings());
// Find the corresponding subject
FoundSubjectEntryIndex = SubjectsView->SubjectData.IndexOfByPredicate([Entry](const FLiveLinkSubjectUIEntryPtr& SubjectEntry) { return SubjectEntry->SubjectKey.Source == Entry->GetGuid() && SubjectEntry->IsSource(); });
}
else
{
SourcesDetailsView->SetObject(nullptr);
}
// Set the corresponding subject
if (FoundSubjectEntryIndex != INDEX_NONE)
{
SubjectsView->SubjectsTreeView->SetSelection(SubjectsView->SubjectData[FoundSubjectEntryIndex]);
}
else
{
SubjectsView->SubjectsTreeView->ClearSelection();
}
}
void FLiveLinkPanelController::OnSubjectSelectionChangedHandler(FLiveLinkSubjectUIEntryPtr SubjectEntry, ESelectInfo::Type SelectInfo)
{
if (bSelectionChangedGuard)
{
return;
}
TGuardValue<bool> ReentrantGuard(bSelectionChangedGuard, true);
int32 FoundSourceIndex = INDEX_NONE;
bool bDetailViewSet = false;
if (SubjectEntry.IsValid())
{
// Find the corresponding Source entry
FGuid SourceGuid = SubjectEntry->SubjectKey.Source;
FoundSourceIndex = SourcesView->SourceData.IndexOfByPredicate([SourceGuid](FLiveLinkSourceUIEntryPtr SourceEntry) { return SourceEntry->GetGuid() == SourceGuid; });
if (SubjectEntry->IsSource())
{
SourcesDetailsView->SetObject(SubjectEntry->GetSettings());
if (!bSeparateSourcesSubjects)
{
SubjectsDetailsView->SetSubjectKey(FLiveLinkSubjectKey());
}
}
else
{
if (!bSeparateSourcesSubjects)
{
SourcesDetailsView->SetObject(nullptr);
}
SubjectsDetailsView->SetSubjectKey(SubjectEntry->SubjectKey);
}
bDetailViewSet = true;
SubjectSelectionChangedDelegate.Broadcast(SubjectEntry->SubjectKey);
}
if (!bDetailViewSet)
{
if (!bSeparateSourcesSubjects)
{
SourcesDetailsView->SetObject(nullptr);
}
SubjectsDetailsView->SetSubjectKey(FLiveLinkSubjectKey());
}
// Select the corresponding Source entry
if (FoundSourceIndex != INDEX_NONE)
{
SourcesView->SourcesListView->SetSelection(SourcesView->SourceData[FoundSourceIndex]);
if (bSeparateSourcesSubjects)
{
// Update source details to the selected subject's source.
SourcesDetailsView->SetObject(SourcesView->SourceData[FoundSourceIndex]->GetSourceSettings());
}
}
else
{
SourcesView->SourcesListView->ClearSelection();
}
}
void FLiveLinkPanelController::BindCommands()
{
CommandList->MapAction(FLiveLinkClientCommands::Get().RemoveSource,
FExecuteAction::CreateRaw(this, &FLiveLinkPanelController::HandleRemoveSource),
FCanExecuteAction::CreateRaw(this, &FLiveLinkPanelController::CanRemoveSource)
);
CommandList->MapAction(FLiveLinkClientCommands::Get().RemoveAllSources,
FExecuteAction::CreateRaw(this, &FLiveLinkPanelController::HandleRemoveAllSources),
FCanExecuteAction::CreateRaw(this, &FLiveLinkPanelController::HasSource)
);
CommandList->MapAction(FLiveLinkClientCommands::Get().RemoveSubject,
FExecuteAction::CreateRaw(this, &FLiveLinkPanelController::HandleRemoveSubject),
FCanExecuteAction::CreateRaw(this, &FLiveLinkPanelController::CanRemoveSubject)
);
CommandList->MapAction(FLiveLinkClientCommands::Get().PauseSubject,
FExecuteAction::CreateRaw(this, &FLiveLinkPanelController::HandlePauseSubject),
FCanExecuteAction::CreateRaw(this, &FLiveLinkPanelController::CanPauseSubject)
);
}
void FLiveLinkPanelController::OnSourcesChangedHandler()
{
// Since this can be called from any thread, make sure we only update slate on the game thread.
FFunctionGraphTask::CreateAndDispatchWhenReady([this]()
{
SourcesView->RefreshSourceData(true);
RebuildSubjectList();
}, TStatId(), nullptr, ENamedThreads::GameThread);
}
void FLiveLinkPanelController::OnSubjectsChangedHandler()
{
// Since this can be called from any thread, make sure we only update slate on the game thread.
FFunctionGraphTask::CreateAndDispatchWhenReady([this]()
{
RebuildSubjectList();
SourcesDetailsView->ForceRefresh();
}, TStatId(), nullptr, ENamedThreads::GameThread);
}
void FLiveLinkPanelController::RebuildSourceList()
{
SourcesView->RefreshSourceData(true);
}
void FLiveLinkPanelController::RebuildSubjectList()
{
if (SubjectsView)
{
SubjectsView->RefreshSubjects();
}
}
bool FLiveLinkPanelController::HasSource() const
{
constexpr bool bIncludeVirtualSources = true;
return Client->GetDisplayableSources(bIncludeVirtualSources).Num() > 0;
}
bool FLiveLinkPanelController::CanRemoveSource() const
{
return SourcesView->SourcesListView->GetNumItemsSelected() > 0;
}
void FLiveLinkPanelController::HandleRemoveSource()
{
TArray<FLiveLinkSourceUIEntryPtr> Selected;
SourcesView->SourcesListView->GetSelectedItems(Selected);
if (Selected.Num() > 0)
{
Selected[0]->RemoveFromClient();
}
}
void FLiveLinkPanelController::HandleRemoveAllSources()
{
Client->RemoveAllSources();
}
bool FLiveLinkPanelController::CanRemoveSubject() const
{
return SubjectsView->CanRemoveSubject();
}
void FLiveLinkPanelController::HandleRemoveSubject()
{
TArray<FLiveLinkSubjectUIEntryPtr> Selected;
SubjectsView->SubjectsTreeView->GetSelectedItems(Selected);
if (Selected.Num() > 0 && Selected[0])
{
Selected[0]->RemoveFromClient();
}
}
bool FLiveLinkPanelController::CanPauseSubject() const
{
return SubjectsView->CanPauseSubject();
}
void FLiveLinkPanelController::HandlePauseSubject()
{
SubjectsView->HandlePauseSubject();
}