Files
UnrealEngine/Engine/Source/Developer/SessionFrontend/Private/Widgets/Browser/SSessionBrowser.cpp
2025-05-18 13:04:45 +08:00

510 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Widgets/Browser/SSessionBrowser.h"
#include "Misc/MessageDialog.h"
#include "Misc/App.h"
#include "SlateOptMacros.h"
#include "Styling/AppStyle.h"
#include "Models/SessionBrowserTreeItems.h"
#include "Widgets/Browser/SSessionBrowserTreeGroupRow.h"
#include "Widgets/Browser/SSessionBrowserTreeInstanceRow.h"
#include "Widgets/Browser/SSessionBrowserTreeSessionRow.h"
#define LOCTEXT_NAMESPACE "SSessionBrowser"
/* SSessionBrowser structors
*****************************************************************************/
SSessionBrowser::~SSessionBrowser()
{
if (SessionManager.IsValid())
{
for (auto& SessionInfo : AvailableSessions)
{
SessionInfo->OnInstanceDiscovered().RemoveAll(this);
}
SessionManager->OnInstanceSelectionChanged().RemoveAll(this);
SessionManager->OnSelectedSessionChanged().RemoveAll(this);
SessionManager->OnSessionsUpdated().RemoveAll(this);
}
}
/* SSessionBrowser interface
*****************************************************************************/
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SSessionBrowser::Construct( const FArguments& InArgs, TSharedRef<ISessionManager> InSessionManager )
{
IgnoreSessionManagerEvents = false;
updatingTreeExpansion = false;
bCanSetDefaultSelection = true;
SessionManager = InSessionManager;
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SOverlay)
+ SOverlay::Slot()
[
// session tree
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(0.0f)
[
SAssignNew(SessionTreeView, STreeView<TSharedPtr<FSessionBrowserTreeItem>>)
.OnExpansionChanged(this, &SSessionBrowser::HandleSessionTreeViewExpansionChanged)
.OnGenerateRow(this, &SSessionBrowser::HandleSessionTreeViewGenerateRow)
.OnGetChildren(this, &SSessionBrowser::HandleSessionTreeViewGetChildren)
.OnSelectionChanged(this, &SSessionBrowser::HandleSessionTreeViewSelectionChanged)
.SelectionMode(ESelectionMode::Single)
.TreeItemsSource(&SessionTreeItems)
.HeaderRow
(
SNew(SHeaderRow)
+ SHeaderRow::Column("Name")
.DefaultLabel(LOCTEXT("InstanceListNameColumnHeader", "Name"))
.FillWidth(0.4f)
+ SHeaderRow::Column("Type")
.DefaultLabel(LOCTEXT("InstanceListTypeColumnHeader", "Type"))
.FillWidth(0.15f)
+ SHeaderRow::Column("Device")
.DefaultLabel(LOCTEXT("InstanceListDeviceColumnHeader", "Device"))
.FillWidth(0.3f)
+ SHeaderRow::Column("Status")
.DefaultLabel(LOCTEXT("InstanceListStatusColumnHeader", "Status"))
.FillWidth(0.15f)
.HAlignCell(HAlign_Right)
.HAlignHeader(HAlign_Right)
)
]
]
+ SOverlay::Slot()
.VAlign(EVerticalAlignment::VAlign_Bottom)
.HAlign(EHorizontalAlignment::HAlign_Left)
.Padding(FMargin(3.0f, 0.0f, 3.0f, 5.0f))
[
SNew(STextBlock)
.AutoWrapText(true)
.ColorAndOpacity(FSlateColor(EStyleColor::AccentGray))
.Visibility_Lambda([this]() { return ItemMap.Num() < 10 ? EVisibility::Visible : EVisibility::Collapsed; })
.Text(LOCTEXT("MessagingInfo", "Sessions must be launched with the \"-messaging\" argument to be visible."))
]
]
];
AppGroupItem = MakeShareable(new FSessionBrowserGroupTreeItem(LOCTEXT("AppGroupName", "This Application"), LOCTEXT("AppGroupToolTip", "The application instance that this session browser belongs to")));
OtherGroupItem = MakeShareable(new FSessionBrowserGroupTreeItem(LOCTEXT("OtherGroupName", "Other Sessions"), LOCTEXT("OtherGroupToolTip", "All sessions that belong to other users")));
OwnerGroupItem = MakeShareable(new FSessionBrowserGroupTreeItem(LOCTEXT("OwnerGroupName", "My Sessions"), LOCTEXT("OwnerGroupToolTip", "All sessions that were started by you")));
StandaloneGroupItem = MakeShareable(new FSessionBrowserGroupTreeItem(LOCTEXT("StandaloneGroupName", "Standalone Instances"), LOCTEXT("StandaloneGroupToolTip", "Engine instances that don't belong to any particular session")));
SessionTreeItems.Add(AppGroupItem);
SessionTreeItems.Add(OwnerGroupItem);
SessionTreeItems.Add(OtherGroupItem);
SessionTreeItems.Add(StandaloneGroupItem);
SessionManager->OnInstanceSelectionChanged().AddSP(this, &SSessionBrowser::HandleSessionManagerInstanceSelectionChanged);
SessionManager->OnSelectedSessionChanged().AddSP(this, &SSessionBrowser::HandleSessionManagerSelectedSessionChanged);
SessionManager->OnSessionsUpdated().AddSP(this, &SSessionBrowser::HandleSessionManagerSessionsUpdated);
ReloadSessions();
updatingTreeExpansion = true;
SessionTreeView->SetSingleExpandedItem(AppGroupItem);
updatingTreeExpansion = false;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
/* SSessionBrowser implementation
*****************************************************************************/
void SSessionBrowser::ExpandItem(const TSharedPtr<FSessionBrowserTreeItem>& Item)
{
SessionTreeView->SetItemExpansion(Item, true);
if (Item.IsValid())
{
const auto& ParentItem = Item->GetParent();
if (ParentItem.IsValid())
{
SessionTreeView->SetItemExpansion(ParentItem, true);
}
}
}
void SSessionBrowser::FilterSessions()
{
// clear tree groups
AppGroupItem->ClearChildren();
OwnerGroupItem->ClearChildren();
OtherGroupItem->ClearChildren();
StandaloneGroupItem->ClearChildren();
// update tree items
TMap<FGuid, TSharedPtr<FSessionBrowserTreeItem>> NewItemMap;
for (auto& SessionInfo : AvailableSessions)
{
// process session
bool LocalOwner = SessionInfo->GetSessionOwner() == FPlatformProcess::UserName(false);
if (!SessionInfo->IsStandalone() && !LocalOwner)
{
continue;
}
TSharedPtr<FSessionBrowserTreeItem> SessionItem = ItemMap.FindRef(SessionInfo->GetSessionId());
if (SessionItem.IsValid())
{
SessionItem->ClearChildren();
}
else
{
SessionItem = MakeShareable(new FSessionBrowserSessionTreeItem(SessionInfo.ToSharedRef()));
}
NewItemMap.Add(SessionInfo->GetSessionId(), SessionItem);
// add session to group
TArray<TSharedPtr<ISessionInstanceInfo>> Instances;
SessionInfo->GetInstances(Instances);
if (LocalOwner)
{
if (!FApp::IsThisInstance(Instances[0]->GetInstanceId()))
{
OwnerGroupItem->AddChild(SessionItem.ToSharedRef());
SessionItem->SetParent(OwnerGroupItem);
}
}
else if (SessionInfo->IsStandalone())
{
StandaloneGroupItem->AddChild(SessionItem.ToSharedRef());
SessionItem->SetParent(StandaloneGroupItem);
}
else
{
OtherGroupItem->AddChild(SessionItem.ToSharedRef());
SessionItem->SetParent(OtherGroupItem);
}
// process session instances
for (auto& InstanceInfo : Instances)
{
TSharedPtr<FSessionBrowserTreeItem> InstanceItem = ItemMap.FindRef(InstanceInfo->GetInstanceId());
if (!InstanceItem.IsValid())
{
InstanceItem = MakeShareable(new FSessionBrowserInstanceTreeItem(InstanceInfo.ToSharedRef()));
}
AddInstanceItemToTree(SessionItem, InstanceItem, InstanceInfo);
NewItemMap.Add(InstanceInfo->GetInstanceId(), InstanceItem);
}
}
ItemMap = NewItemMap;
// refresh tree view
SessionTreeView->RequestTreeRefresh();
if ( bCanSetDefaultSelection && SessionTreeView->GetNumItemsSelected() == 0 && ThisAppInstance.IsValid() && !FApp::GetSessionName().Equals("UnrealInsights"))
{
bCanSetDefaultSelection = false;
SessionTreeView->SetItemSelection(ThisAppInstance.Pin(), true, ESelectInfo::Direct);
}
}
void SSessionBrowser::AddInstanceItemToTree(TSharedPtr<FSessionBrowserTreeItem>& SessionItem, const TSharedPtr<FSessionBrowserTreeItem>& InstanceItem, const TSharedPtr<ISessionInstanceInfo>& InstanceInfo)
{
// add instance to group or session
if (FApp::IsThisInstance(InstanceInfo->GetInstanceId()))
{
AppGroupItem->AddChild(InstanceItem.ToSharedRef());
InstanceItem->SetParent(AppGroupItem);
ThisAppInstance = InstanceItem;
}
else
{
InstanceItem->SetParent(SessionItem);
SessionItem->AddChild(InstanceItem.ToSharedRef());
}
}
void SSessionBrowser::ReloadSessions()
{
for (auto& SessionInfo : AvailableSessions)
{
SessionInfo->OnInstanceDiscovered().RemoveAll(this);
}
SessionManager->GetSessions(AvailableSessions);
for (auto& SessionInfo : AvailableSessions)
{
SessionInfo->OnInstanceDiscovered().AddSP(this, &SSessionBrowser::HandleSessionManagerInstanceDiscovered);
}
FilterSessions();
}
/* SSessionBrowser event handlers
*****************************************************************************/
void SSessionBrowser::HandleSessionManagerInstanceSelectionChanged(const TSharedPtr<ISessionInstanceInfo>& Instance, bool Selected)
{
if (IgnoreSessionManagerEvents)
{
return;
}
const auto& InstanceItem = ItemMap.FindRef(Instance->GetInstanceId());
if (InstanceItem.IsValid())
{
SessionTreeView->ClearSelection();
SessionTreeView->SetItemSelection(InstanceItem, Selected);
}
}
void SSessionBrowser::HandleSessionManagerSelectedSessionChanged(const TSharedPtr<ISessionInfo>& SelectedSession)
{
if (IgnoreSessionManagerEvents)
{
return;
}
updatingTreeExpansion = true;
{
if (SelectedSession.IsValid())
{
ExpandItem(ItemMap.FindRef(SelectedSession->GetSessionId()));
}
else
{
SessionTreeView->SetSingleExpandedItem(nullptr);
}
}
updatingTreeExpansion = false;
}
void SSessionBrowser::HandleSessionManagerInstanceDiscovered(const TSharedRef<ISessionInfo>& OwnerSession, const TSharedRef<ISessionInstanceInfo>& DiscoveredInstance)
{
TSharedPtr<FSessionBrowserTreeItem> SessionItem = ItemMap.FindRef(OwnerSession->GetSessionId());
if (SessionItem.IsValid())
{
// add the item if it's not already there
TSharedPtr<FSessionBrowserTreeItem> InstanceItem = ItemMap.FindRef(DiscoveredInstance->GetInstanceId());
if (!InstanceItem.IsValid())
{
InstanceItem = MakeShareable(new FSessionBrowserInstanceTreeItem(DiscoveredInstance));
AddInstanceItemToTree(SessionItem, InstanceItem, DiscoveredInstance);
ItemMap.Add(DiscoveredInstance->GetInstanceId(), InstanceItem);
// refresh tree view
SessionTreeView->RequestTreeRefresh();
}
}
}
void SSessionBrowser::HandleSessionManagerSessionsUpdated()
{
ReloadSessions();
}
FText SSessionBrowser::HandleSessionTreeRowGetToolTipText(TSharedPtr<FSessionBrowserTreeItem> Item) const
{
FTextBuilder ToolTipTextBuilder;
if (Item->GetType() == ESessionBrowserTreeNodeType::Instance)
{
TSharedPtr<ISessionInstanceInfo> InstanceInfo = StaticCastSharedPtr<FSessionBrowserInstanceTreeItem>(Item)->GetInstanceInfo();
if (InstanceInfo.IsValid())
{
const FCoreTexts& CoreTexts = FCoreTexts::Get();
ToolTipTextBuilder.AppendLineFormat(LOCTEXT("InstanceToolTipInstanceId", "Instance ID: {0}"), FText::FromString(InstanceInfo->GetInstanceId().ToString(EGuidFormats::DigitsWithHyphensInBraces)));
ToolTipTextBuilder.AppendLine();
ToolTipTextBuilder.AppendLineFormat(LOCTEXT("InstanceToolTipBuildDate", "Build Date: {0}"), FText::FromString(InstanceInfo->GetBuildDate()));
ToolTipTextBuilder.AppendLineFormat(LOCTEXT("InstanceToolTipEngineVersion", "Engine Version: {0}"), InstanceInfo->GetEngineVersion() == 0 ? LOCTEXT("CustomBuildVersion", "Custom Build") : FText::FromString(FString::FromInt(InstanceInfo->GetEngineVersion())));
ToolTipTextBuilder.AppendLineFormat(LOCTEXT("InstanceToolTipPlatform", "Platform: {0}"), FText::FromString(InstanceInfo->GetPlatformName()));
ToolTipTextBuilder.AppendLineFormat(LOCTEXT("InstanceToolTipCurrentLevel", "Current Level: {0}"), FText::FromString(InstanceInfo->GetCurrentLevel()));
ToolTipTextBuilder.AppendLineFormat(LOCTEXT("InstanceToolTipWorldTimeSeconds", "World Time: {0}"), FText::AsTimespan(FTimespan::FromSeconds(InstanceInfo->GetWorldTimeSeconds())));
ToolTipTextBuilder.AppendLineFormat(LOCTEXT("InstanceToolTipPlayBegun", "Play Has Begun: {0}"), InstanceInfo->PlayHasBegun() ? FCoreTexts::Get().Yes : FCoreTexts::Get().No);
ToolTipTextBuilder.AppendLine();
ToolTipTextBuilder.AppendLineFormat(LOCTEXT("SessionToolTipLastUpdateTime", "Last Update Time: {0}"), FText::AsDateTime(InstanceInfo->GetLastUpdateTime()));
}
}
return ToolTipTextBuilder.ToText();
}
void SSessionBrowser::HandleSessionTreeViewExpansionChanged(TSharedPtr<FSessionBrowserTreeItem> TreeItem, bool bIsExpanded)
{
if ( updatingTreeExpansion || !TreeItem.IsValid())
{
return;
}
if (TreeItem->GetType() == ESessionBrowserTreeNodeType::Instance)
{
return;
}
IgnoreSessionManagerEvents = true;
{
if (bIsExpanded)
{
updatingTreeExpansion = true;
{
ExpandItem(TreeItem);
}
updatingTreeExpansion = false;
// select session
if (TreeItem->GetType() == ESessionBrowserTreeNodeType::Session)
{
SessionManager->SelectSession(StaticCastSharedPtr<FSessionBrowserSessionTreeItem>(TreeItem)->GetSessionInfo());
}
else
{
SessionManager->SelectSession(nullptr);
}
}
else if (TreeItem->GetType() != ESessionBrowserTreeNodeType::Instance)
{
// deselect session
SessionManager->SelectSession(nullptr);
}
}
IgnoreSessionManagerEvents = false;
}
TSharedRef<ITableRow> SSessionBrowser::HandleSessionTreeViewGenerateRow(TSharedPtr<FSessionBrowserTreeItem> Item, const TSharedRef<STableViewBase>& OwnerTable)
{
if (Item->GetType() == ESessionBrowserTreeNodeType::Group)
{
return SNew(SSessionBrowserTreeGroupRow, OwnerTable)
.Item(StaticCastSharedRef<FSessionBrowserGroupTreeItem>(Item.ToSharedRef()));
}
if (Item->GetType() == ESessionBrowserTreeNodeType::Session)
{
return SNew(SSessionBrowserTreeSessionRow, OwnerTable)
.Item(StaticCastSharedRef<FSessionBrowserSessionTreeItem>(Item.ToSharedRef()));
}
return SNew(SSessionBrowserTreeInstanceRow, OwnerTable)
.Item(StaticCastSharedRef<FSessionBrowserInstanceTreeItem>(Item.ToSharedRef()))
.ToolTipText(this, &SSessionBrowser::HandleSessionTreeRowGetToolTipText, Item)
.ShowSelection(true);
}
void SSessionBrowser::HandleSessionTreeViewGetChildren(TSharedPtr<FSessionBrowserTreeItem> Item, TArray<TSharedPtr<FSessionBrowserTreeItem>>& OutChildren)
{
if (Item.IsValid())
{
OutChildren = Item->GetChildren();
}
}
void SSessionBrowser::HandleSessionTreeViewSelectionChanged(const TSharedPtr<FSessionBrowserTreeItem> Item, ESelectInfo::Type SelectInfo)
{
IgnoreSessionManagerEvents = true;
{
if (Item.IsValid())
{
if (Item->GetType() == ESessionBrowserTreeNodeType::Instance)
{
const auto& InstanceInfo = StaticCastSharedPtr<FSessionBrowserInstanceTreeItem>(Item)->GetInstanceInfo();
if (InstanceInfo.IsValid())
{
SessionManager->SelectSession(InstanceInfo->GetOwnerSession());
SessionManager->SetInstanceSelected(InstanceInfo.ToSharedRef(), true);
}
}
}
{
// check if any instances are no longer selected
TArray<TSharedPtr<ISessionInstanceInfo>> UnselectedSessions;
for (const auto& InstanceInfo : SessionManager->GetSelectedInstances())
{
const TSharedPtr<FSessionBrowserTreeItem>& InstanceItem = ItemMap.FindRef(InstanceInfo->GetInstanceId());
if (!SessionTreeView->IsItemSelected(InstanceItem))
{
UnselectedSessions.Add(InstanceInfo);
}
}
for (const auto& Session : UnselectedSessions)
{
SessionManager->SetInstanceSelected(Session.ToSharedRef(), false);
}
}
}
IgnoreSessionManagerEvents = false;
}
FReply SSessionBrowser::HandleTerminateSessionButtonClicked()
{
int32 DialogResult = FMessageDialog::Open(EAppMsgType::YesNo, LOCTEXT("TerminateSessionDialogPrompt", "Are you sure you want to terminate this session and its instances?"));
if (DialogResult == EAppReturnType::Yes)
{
const TSharedPtr<ISessionInfo>& SelectedSession = SessionManager->GetSelectedSession();
if (SelectedSession.IsValid())
{
if (SelectedSession->GetSessionOwner() == FPlatformProcess::UserName(false))
{
SelectedSession->Terminate();
}
else
{
FMessageDialog::Open( EAppMsgType::Ok, FText::Format( LOCTEXT("TerminateDeniedPrompt", "You are not authorized to terminate the currently selected session, because it is owned by {0}"), FText::FromString( SelectedSession->GetSessionOwner() ) ) );
}
}
}
return FReply::Handled();
}
bool SSessionBrowser::HandleTerminateSessionButtonIsEnabled() const
{
return SessionManager->GetSelectedSession().IsValid();
}
#undef LOCTEXT_NAMESPACE