TabView.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. using System;
  2. using System.Collections.Generic;
  3. using JetBrains.Annotations;
  4. using Unity.Cloud.Collaborate.Assets;
  5. using Unity.Cloud.Collaborate.UserInterface;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using UnityEngine.Assertions;
  9. using UnityEngine.UIElements;
  10. namespace Unity.Cloud.Collaborate.Components
  11. {
  12. [UsedImplicitly]
  13. internal class TabView : VisualElement
  14. {
  15. public const string UssClassName = "unity-tab-view";
  16. public const string ContentContainerUssClassName = UssClassName + "__content-container";
  17. public const string TabHeaderUssClassName = UssClassName + "__tab-header";
  18. public const string ToolbarUssClassName = UssClassName + "__toolbar";
  19. static readonly string k_StylePath = $"{CollaborateWindow.StylePath}/{nameof(TabView)}.uss";
  20. const int k_NoTabs = -1;
  21. int m_ActiveTabIndex = k_NoTabs;
  22. bool m_Active;
  23. readonly VisualElement m_Content;
  24. readonly VisualElement m_Toolbar;
  25. readonly List<(TextButton button, TabPageComponent tab)> m_TabList;
  26. public event Action<int> TabSwitched;
  27. public TabView()
  28. {
  29. AddToClassList(UssClassName);
  30. styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(k_StylePath));
  31. m_Toolbar = new VisualElement { name = "unity-tab-view-toolbar" };
  32. m_Toolbar.AddToClassList(ToolbarUssClassName);
  33. m_Toolbar.AddToClassList(UiConstants.ussDefaultInset);
  34. hierarchy.Add(m_Toolbar);
  35. m_Content = new VisualElement { name = "unity-content-container" };
  36. m_Content.AddToClassList(ContentContainerUssClassName);
  37. hierarchy.Add(m_Content);
  38. m_TabList = new List<(TextButton button, TabPageComponent tab)>();
  39. }
  40. public void SetActive()
  41. {
  42. Assert.IsFalse(m_Active, "TabView is already active.");
  43. m_Active = true;
  44. if (m_ActiveTabIndex != k_NoTabs)
  45. {
  46. m_TabList[m_ActiveTabIndex].tab.SetActive(true);
  47. }
  48. }
  49. public void SetInactive()
  50. {
  51. Assert.IsTrue(m_Active, "TabView is already inactive.");
  52. m_Active = false;
  53. if (m_ActiveTabIndex != k_NoTabs)
  54. {
  55. m_TabList[m_ActiveTabIndex].tab.SetActive(false);
  56. }
  57. }
  58. /// <summary>
  59. /// Add a tab to the view.
  60. /// </summary>
  61. /// <param name="tabName">Title of the tab.</param>
  62. /// <param name="tab">Tab content to display.</param>
  63. public void AddTab(string tabName, TabPageComponent tab)
  64. {
  65. // Get the tab index
  66. var index = m_TabList.Count;
  67. tab.style.flexGrow = 1;
  68. tab.style.flexShrink = 1;
  69. tab.style.flexBasis = new StyleLength(StyleKeyword.Auto);
  70. // Copy value to avoid modification of the closure scope.
  71. var indexCopy = index;
  72. var btn = new TextButton(() => SwitchTabInternal(indexCopy)) { text = tabName };
  73. btn.AddToClassList(TabHeaderUssClassName);
  74. m_Toolbar.Add(btn);
  75. // Add tab to list
  76. m_TabList.Add((btn, tab));
  77. // If no currently active tab, switch to this newly added one.
  78. if (m_ActiveTabIndex == k_NoTabs)
  79. {
  80. SwitchToNextTab();
  81. }
  82. }
  83. /// <summary>
  84. /// Switch to the tab with the given index. Does nothing with an invalid index.
  85. /// </summary>
  86. /// <param name="index">Index of the tab to switch to.</param>
  87. public void SwitchTab(int index)
  88. {
  89. // Sanitise index to be passed into the internal switch method.
  90. if (index == k_NoTabs) return;
  91. if (index < m_TabList.Count)
  92. {
  93. SwitchTabInternal(index);
  94. }
  95. }
  96. /// <summary>
  97. /// Switch to tab with the given index. Does *NOT* check the validity of the index.
  98. /// </summary>
  99. /// <param name="index">Index of the tab to switch to.</param>
  100. void SwitchTabInternal(int index)
  101. {
  102. // Reset tab state of previously active content/button - if there was one.
  103. if (m_ActiveTabIndex != k_NoTabs)
  104. {
  105. m_TabList[m_ActiveTabIndex].tab.RemoveFromHierarchy();
  106. if (m_Active)
  107. {
  108. m_TabList[m_ActiveTabIndex].tab.SetActive(false);
  109. }
  110. m_TabList[m_ActiveTabIndex].button.RemoveFromClassList(UiConstants.ussActive);
  111. }
  112. // Set new active tab.
  113. m_ActiveTabIndex = index;
  114. // Set tab state for newly active content/button.
  115. if (m_Active)
  116. {
  117. m_TabList[m_ActiveTabIndex].tab.SetActive(true);
  118. }
  119. m_TabList[m_ActiveTabIndex].button.AddToClassList(UiConstants.ussActive);
  120. m_Content.Add(m_TabList[m_ActiveTabIndex].tab);
  121. TabSwitched?.Invoke(index);
  122. }
  123. /// <summary>
  124. /// Switch to the next valid tab. Wraps if there's no direct successor.
  125. /// </summary>
  126. void SwitchToNextTab()
  127. {
  128. var index = (m_ActiveTabIndex + 1) % m_TabList.Count;
  129. SwitchTabInternal(index);
  130. }
  131. public override VisualElement contentContainer => m_Content;
  132. [UsedImplicitly]
  133. public new class UxmlFactory : UxmlFactory<TabView> { }
  134. }
  135. }