A game about forced loneliness, made by TACStudios
1using System;
2using Unity.Multiplayer.Center.Analytics;
3using UnityEditor;
4using UnityEngine;
5using UnityEngine.UIElements;
6
7namespace Unity.Multiplayer.Center.Window
8{
9 // Note: there is a TabView API in UI Toolkit, but only starting from 2023.2
10 internal interface ITabView
11 {
12 /// <summary>
13 /// The name as displayed in the tab button
14 /// Should be serialized.
15 /// </summary>
16 string Name { get; }
17
18 /// <summary>
19 /// The root visual element of the tab view.
20 /// The setter will only be used if the root visual element is null when the tab is created.
21 /// </summary>
22 VisualElement RootVisualElement { get; set; }
23
24 /// <summary>
25 /// Sets the tab view visible or not.
26 /// </summary>
27 /// <param name="visible">If true, visible.</param>
28 void SetVisible(bool visible);
29
30 /// <summary>
31 /// If true the Tab can be selected by the user.
32 /// </summary>
33 bool IsEnabled => true;
34
35 /// <summary>
36 /// Tooltip which will be shown on the Tab Button.
37 /// </summary>
38 string ToolTip => "";
39
40 /// <summary>
41 /// Refreshes the UI Elements according to latest data.
42 /// If the UI is not created yet, it does it.
43 /// </summary>
44 void Refresh();
45
46 /// <summary>
47 /// Unregister all events and clear UI Elements
48 /// </summary>
49 void Clear();
50
51 /// <summary>
52 /// The Multiplayer Center Analytics provider.
53 /// </summary>
54 IMultiplayerCenterAnalytics MultiplayerCenterAnalytics { get; set; }
55 }
56
57 [Serializable]
58 internal class TabGroup
59 {
60 const string k_TabViewName = "tab-view";
61 const string k_TabZoneName = "tab-zone";
62 const string k_TabButtonUssClass = "tab-button";
63
64 // The container for all the tabs
65 const string k_TabsContainerUssClass ="tabs-container";
66
67 // Gets applied to the root of each tab
68 const string k_TabContentUssClass = "tab-content";
69
70 [field: SerializeField]
71 public int CurrentTab { get; private set; } = -1;
72
73 public int ViewCount => m_TabViews?.Length ?? 0;
74
75 VisualElement[] m_TabButtons;
76
77 [SerializeReference]
78 ITabView[] m_TabViews;
79
80 public VisualElement Root { get; private set; }
81
82 VisualElement m_MainContainer;
83
84 IMultiplayerCenterAnalytics m_MultiplayerCenterAnalytics;
85
86 internal IMultiplayerCenterAnalytics MultiplayerCenterAnalytics
87 {
88 get => m_MultiplayerCenterAnalytics;
89 set
90 {
91 m_MultiplayerCenterAnalytics = value;
92 foreach (var tabView in m_TabViews)
93 {
94 if(tabView != null)
95 tabView.MultiplayerCenterAnalytics = value;
96 }
97 }
98 }
99
100 public TabGroup(IMultiplayerCenterAnalytics analytics, ITabView[] tabViews, int defaultIndex = 0)
101 {
102 m_TabViews = tabViews;
103 CurrentTab = defaultIndex;
104 MultiplayerCenterAnalytics = analytics;
105 }
106
107 public void SetSelected(int index, bool force = false)
108 {
109 // Select the first tab, if the requested tab is not enabled.
110 // This assumes the first tab is always enabled.
111 if (!m_TabViews[index].IsEnabled)
112 index = 0;
113
114 if (index == CurrentTab && !force)
115 return;
116
117 if (CurrentTab >= 0 && CurrentTab < m_TabViews.Length)
118 {
119 m_TabButtons[CurrentTab].RemoveFromClassList("selected");
120 m_TabViews[CurrentTab].SetVisible(false);
121 }
122
123 EditorPrefs.SetInt(PlayerSettings.productName + "_MultiplayerCenter_TabIndex", index);
124 CurrentTab = index;
125 m_TabViews[CurrentTab].Refresh();
126 m_TabButtons[CurrentTab].AddToClassList("selected");
127 m_TabViews[CurrentTab].SetVisible(true);
128 }
129
130 /// <summary>
131 /// Instantiates the visual elements for all the tabs.
132 /// Use this to create the tabs for the first time the UI is shown or after a domain reload.
133 /// </summary>
134 public void CreateTabs()
135 {
136 Root ??= new VisualElement();
137 m_MainContainer ??= new VisualElement();
138
139 if (Root.Q(k_TabZoneName) != null)
140 Root.Q(k_TabZoneName).RemoveFromHierarchy();
141
142 var tabZone = new VisualElement() {name = k_TabZoneName};
143 Root.Add(tabZone);
144 Root.name = k_TabViewName;
145 m_TabButtons = new VisualElement[m_TabViews.Length];
146 for (var i = 0; i < m_TabViews.Length; i++)
147 {
148 var tabView = m_TabViews[i];
149 var index = i; // copy for closure
150 var tabButton = new Button(() => SetSelected(index));
151 tabButton.enabledSelf = tabView.IsEnabled;
152 tabButton.tooltip = tabView.ToolTip;
153 tabButton.AddToClassList(k_TabButtonUssClass);
154 tabButton.text = tabView.Name;
155 tabZone.Add(tabButton);
156 m_TabButtons[i] = tabButton;
157 tabView.RootVisualElement ??= new VisualElement();
158 tabView.RootVisualElement.AddToClassList(k_TabContentUssClass);
159 tabView.RootVisualElement.style.display = DisplayStyle.None;
160 m_MainContainer.Add(m_TabViews[i].RootVisualElement);
161 }
162
163 m_MainContainer.AddToClassList(k_TabsContainerUssClass);
164 Root.Add(m_MainContainer);
165 CurrentTab = EditorPrefs.GetInt(PlayerSettings.productName + "_MultiplayerCenter_TabIndex", 0);
166 }
167
168 static void SetVisible(VisualElement e, bool visible)
169 {
170 e.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None;
171 }
172
173 public void Clear()
174 {
175 if(m_TabViews == null)
176 return;
177
178 foreach (var tabView in m_TabViews)
179 {
180 tabView?.Clear();
181 }
182 }
183
184 public bool TabsAreValid()
185 {
186 if (m_TabViews == null)
187 return false;
188
189 foreach (var tab in m_TabViews)
190 {
191 if (tab == null)
192 return false;
193 }
194 return true;
195 }
196 }
197}