Files
2026-06-17 20:44:53 +03:00

216 lines
8.2 KiB
C#

using System;
using UnityEngine;
using Blocks.Common;
using Unity.Properties;
using UnityEngine.UIElements;
using Blocks.Sessions.Common;
using Unity.Services.Multiplayer;
using System.Collections.Generic;
namespace Blocks.Sessions
{
[UxmlElement]
public partial class SessionBrowserElement : ListView
{
private const string k_JoinButtonText = "JOIN";
private const string k_RefreshButtonText = "REFRESH LIST";
private const string k_NoSessionFoundText = "No sessions found";
private const string k_SessionNameLabel = "SessionNameLabel";
private const string k_SessionPlayerCountLabel = "SessionPlayerCountLabel";
private int m_MaxSessionsDisplayed = 20;
private SessionSettings m_SessionSettings;
private SessionBrowserViewModel m_ViewModel;
private List<DataBinding> m_DataBindings;
private Button m_RefreshButton;
private Button m_JoinSessionButton;
[CreateProperty, UxmlAttribute]
public SessionSettings SessionSettings
{
get => m_SessionSettings;
set
{
if (m_SessionSettings == value)
return;
m_SessionSettings = value;
if (panel != null)
{
UpdateBindingSources();
}
}
}
[CreateProperty, UxmlAttribute]
public int MaxSessionsDisplayed
{
get => m_MaxSessionsDisplayed;
set => m_MaxSessionsDisplayed = value;
}
public SessionBrowserElement()
{
virtualizationMethod = CollectionVirtualizationMethod.FixedHeight;
fixedItemHeight = 56f;
// required for making each element inheriting an indexed array item for the data source path
// if not set, you need to specify the datasource path manually in the bindItem callback for each item
bindingSourceSelectionMode = BindingSourceSelectionMode.AutoAssign;
AddToClassList(BlocksTheme.ScrollView);
AddToClassList(BlocksTheme.SpaceBottom);
makeNoneElement = MakeNoneElement;
makeItem = MakeDefaultItem;
makeFooter = MakeFooter;
RegisterCallback<AttachToPanelEvent>(OnAttachToPanelEvent);
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanelEvent);
}
private VisualElement MakeFooter()
{
var buttonsContainer = new VisualElement();
buttonsContainer.AddToClassList(BlocksTheme.ContainerHorizontal);
buttonsContainer.AddToClassList(BlocksTheme.ContainerAlignedRight);
m_JoinSessionButton = new Button { text = k_JoinButtonText };
m_JoinSessionButton.AddToClassList(BlocksTheme.Button);
m_JoinSessionButton.AddToClassList(BlocksTheme.SpaceRight);
buttonsContainer.Add(m_JoinSessionButton);
m_RefreshButton = new Button { text = k_RefreshButtonText };
m_RefreshButton.AddToClassList(BlocksTheme.Button);
buttonsContainer.Add(m_RefreshButton);
return buttonsContainer;
}
private void OnRefreshButtonClicked()
{
ClearSelection();
// fire and forget, so we don't block the UI thread
_ = m_ViewModel?.UpdateSessionListAsync(MaxSessionsDisplayed);
}
private void JoinSession()
{
if (!m_ViewModel.SelectedAndAvailable)
{
Debug.LogError("Selected session is no longer selected.");
return;
}
// fire and forget, so we don't block the UI thread
_ = m_ViewModel.JoinSessionAsync(SessionSettings.ToJoinSessionOptions());
}
private void UpdateBindingSources()
{
CleanupBindings();
m_ViewModel = new SessionBrowserViewModel(SessionSettings?.sessionType);
foreach (var dataBinding in m_DataBindings)
{
dataBinding.dataSource = m_ViewModel;
}
}
private void CleanupBindings()
{
m_ViewModel?.Dispose();
m_ViewModel = null;
foreach (var dataBinding in m_DataBindings)
{
dataBinding.dataSource = null;
}
}
private void OnDetachFromPanelEvent(DetachFromPanelEvent evt)
{
CleanupBindings();
m_RefreshButton.clicked -= OnRefreshButtonClicked;
m_RefreshButton.ClearBinding(nameof(SessionBrowserViewModel.CanRefresh));
m_JoinSessionButton.clicked -= JoinSession;
m_JoinSessionButton.ClearBinding(nameof(enabledSelf));
ClearBindings();
}
private void OnAttachToPanelEvent(AttachToPanelEvent evt)
{
m_DataBindings = new List<DataBinding>();
var listBinding = new DataBinding { dataSourcePath = new PropertyPath(nameof(SessionBrowserViewModel.Sessions)), bindingMode = BindingMode.ToTarget };
SetBinding(new BindingId(nameof(ListView.itemsSource)), listBinding);
m_DataBindings.Add(listBinding);
var selectionBinding = new DataBinding { dataSourcePath = new PropertyPath(nameof(SessionBrowserViewModel.SelectedSessionIndex)), bindingMode = BindingMode.TwoWay };
SetBinding(new BindingId(nameof(ListView.selectedIndex)), selectionBinding);
m_DataBindings.Add(selectionBinding);
var joinSessionBinding = new DataBinding { dataSourcePath = new PropertyPath(nameof(SessionBrowserViewModel.SelectedAndAvailable)), bindingMode = BindingMode.ToTarget };
m_JoinSessionButton.SetBinding(new BindingId(nameof(enabledSelf)), joinSessionBinding);
m_DataBindings.Add(joinSessionBinding);
m_JoinSessionButton.clicked += JoinSession;
var refreshBinding = new DataBinding { dataSourcePath = new PropertyPath(nameof(SessionBrowserViewModel.CanRefresh)), bindingMode = BindingMode.ToTarget };
m_RefreshButton.SetBinding(new BindingId(nameof(enabledSelf)), refreshBinding);
m_DataBindings.Add(refreshBinding);
m_RefreshButton.clicked += OnRefreshButtonClicked;
UpdateBindingSources();
}
private static VisualElement MakeNoneElement()
{
var label = new Label(k_NoSessionFoundText);
label.AddToClassList(BlocksTheme.Label);
label.AddToClassList(BlocksTheme.SpaceLeft);
return label;
}
private static VisualElement MakeDefaultItem()
{
var container = new VisualElement();
container.AddToClassList(BlocksTheme.ContainerHorizontal);
container.AddToClassList(BlocksTheme.ScrollViewElement);
container.AddToClassList(BlocksTheme.ContainerSpaceBetween);
var sessionNameLabel = new Label { name = k_SessionNameLabel };
sessionNameLabel.AddToClassList(BlocksTheme.Label);
sessionNameLabel.AddToClassList(BlocksTheme.SpaceLeft);
container.Add(sessionNameLabel);
var db = new DataBinding
{
dataSourcePath = PropertyPath.FromName(nameof(SessionInfoViewModel.Name)),
bindingMode = BindingMode.ToTarget,
updateTrigger = BindingUpdateTrigger.OnSourceChanged
};
sessionNameLabel.SetBinding(nameof(Label.text), db);
var sessionPlayerCountLabel = new Label { name = k_SessionPlayerCountLabel };
sessionPlayerCountLabel.AddToClassList(BlocksTheme.Label);
sessionPlayerCountLabel.AddToClassList(BlocksTheme.SpaceRight);
container.Add(sessionPlayerCountLabel);
var sessionPlayerCountBinding = new DataBinding { bindingMode = BindingMode.ToTarget };
// register a local converter to display relevant session properties as a formatted string
sessionPlayerCountBinding.sourceToUiConverters
.AddConverter((ref SessionInfoViewModel session) => $"{session.MaxPlayers - session.AvailableSlots}/{session.MaxPlayers} Players");
sessionPlayerCountLabel.SetBinding(nameof(Label.text), sessionPlayerCountBinding);
return container;
}
}
}