-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
…erences-v2 feat: display settings in Preferences or Project Settings
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#if UNITY_2019_4_OR_NEWER | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using StansAssets.Foundation.UIElements; | ||
using UnityEngine.Assertions; | ||
using UnityEngine.UIElements; | ||
|
||
namespace StansAssets.Plugins.Editor | ||
{ | ||
/// <summary> | ||
/// Tab controller based on <see cref="ButtonStrip"/> to switch between tabs | ||
/// and <see cref="ScrollView"/> to display their contents | ||
/// </summary> | ||
public class TabController | ||
{ | ||
readonly Dictionary<string, VisualElement> m_Tabs = new Dictionary<string, VisualElement>(); | ||
|
||
readonly ButtonStrip m_TabsButtons; | ||
readonly ScrollView m_TabsContainer; | ||
|
||
/// <summary> | ||
/// Available tabs' labels. | ||
/// </summary> | ||
public IEnumerable<string> Tabs => m_Tabs.Keys; | ||
|
||
/// <summary> | ||
/// Active tab label from <see cref="Tabs"/>. | ||
/// </summary> | ||
public string ActiveTab => m_TabsButtons.Value; | ||
|
||
/// <summary> | ||
/// This constructor will looking for already existing elements: | ||
/// <see cref="ButtonStrip"/> (without name) and <see cref="ScrollView"/> named "tabs-container" | ||
/// The purpose of this is to support <see cref="PackageSettingsWindow{TWindow}"/>. | ||
/// </summary> | ||
/// <param name="root">Element that contains <see cref="ButtonStrip"/> and <see cref="ScrollView"/> named tabs-container</param> | ||
public TabController(VisualElement root) | ||
{ | ||
m_TabsButtons = root.Q<ButtonStrip>(); | ||
m_TabsContainer = root.Q<ScrollView>("tabs-container"); | ||
|
||
Init(); | ||
} | ||
|
||
/// <summary> | ||
/// Add tab to the window top bar. | ||
/// </summary> | ||
/// <param name="label">Tab label.</param> | ||
/// <param name="content">Tab content.</param> | ||
/// <exception cref="ArgumentException">Will throw tab with the same label was already added.</exception> | ||
public void AddTab(string label, VisualElement content) | ||
{ | ||
if (!m_Tabs.ContainsKey(label)) | ||
{ | ||
m_TabsButtons.AddChoice(label, label); | ||
m_Tabs.Add(label, content); | ||
content.viewDataKey = label; | ||
} | ||
else | ||
{ | ||
throw new ArgumentException($"Tab '{label}' already added", nameof(label)); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Activate tab by label | ||
/// </summary> | ||
/// <param name="label">Early specified tab label</param> | ||
public void ActivateTab(string label) | ||
{ | ||
if (!m_Tabs.ContainsKey(label)) | ||
{ | ||
return; | ||
} | ||
|
||
m_TabsButtons.SetValue(label); | ||
} | ||
|
||
/// <summary> | ||
/// Set the flexible growth property of tabs content container | ||
/// </summary> | ||
/// <param name="styleFloat"></param> | ||
public void ContentContainerFlexGrow(StyleFloat styleFloat) | ||
{ | ||
m_TabsContainer.contentContainer.style.flexGrow = styleFloat; | ||
} | ||
|
||
/// <summary> | ||
/// Refresh current tab | ||
/// </summary> | ||
public void RefreshActiveTab() | ||
{ | ||
if (string.IsNullOrEmpty(ActiveTab)) | ||
{ | ||
return; | ||
} | ||
|
||
foreach (var tab in m_Tabs) | ||
{ | ||
tab.Value.RemoveFromHierarchy(); | ||
} | ||
|
||
var element = m_Tabs.First(i => i.Key.Equals(m_TabsButtons.Value)).Value; | ||
m_TabsContainer.Add(element); | ||
} | ||
|
||
void Init() | ||
{ | ||
Assert.IsNotNull(m_TabsButtons); | ||
Assert.IsNotNull(m_TabsContainer); | ||
|
||
m_TabsButtons.CleanUp(); | ||
m_TabsButtons.OnButtonClick += RefreshActiveTab; | ||
|
||
RefreshActiveTab(); | ||
} | ||
} | ||
} | ||
|
||
#endif |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using UnityEngine.UIElements; | ||
|
||
namespace StansAssets.Plugins.Editor | ||
{ | ||
public static class ListViewExtensions | ||
{ | ||
/// <summary> | ||
/// Rebuild/refresh ListView in compatible mode with Unity 2019/2021 editor | ||
/// </summary> | ||
/// <param name="listView"></param> | ||
public static void RebuildInCompatibleMode(this ListView listView) | ||
{ | ||
#if UNITY_2019_4_40 | ||
listView.Refresh(); | ||
#else | ||
listView.Rebuild(); | ||
#endif | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#if UNITY_2019_4_OR_NEWER | ||
|
||
using JetBrains.Annotations; | ||
using StansAssets.Foundation.Editor; | ||
using UnityEditor; | ||
using UnityEngine.UIElements; | ||
using PackageInfo = UnityEditor.PackageManager.PackageInfo; | ||
|
||
namespace StansAssets.Plugins.Editor | ||
{ | ||
[UsedImplicitly] | ||
sealed class AboutPreferencesWindow : PackagePreferencesWindow | ||
{ | ||
protected override PackageInfo GetPackageInfo() | ||
=> PackageManagerUtility.GetPackageInfo(PluginsDevKitPackage.Name); | ||
|
||
protected override string SettingsPath => $"{PluginsDevKitPackage.RootMenu}/{GetPackageInfo().displayName}"; | ||
protected override SettingsScope Scope => SettingsScope.User; | ||
|
||
protected override void OnActivate(string searchContext, VisualElement rootElement) | ||
{ | ||
ContentContainerFlexGrow(1); | ||
AddTab("About", new AboutTab()); | ||
} | ||
|
||
protected override void OnDeactivate() { } | ||
} | ||
} | ||
|
||
#endif |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
#if UNITY_2019_4_OR_NEWER | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using StansAssets.Foundation.Editor; | ||
using UnityEditor; | ||
using UnityEditor.UIElements; | ||
using UnityEngine.UIElements; | ||
|
||
namespace StansAssets.Plugins.Editor | ||
{ | ||
/// <summary> | ||
/// Base class for Plugin Preferences Window | ||
/// </summary> | ||
public abstract class PackagePreferencesWindow | ||
{ | ||
TabController m_TabController; | ||
|
||
/// <summary> | ||
/// Structure describing a Unity Package. | ||
/// </summary> | ||
protected abstract UnityEditor.PackageManager.PackageInfo GetPackageInfo(); | ||
|
||
|
||
/// <summary> | ||
/// Gets Path used to place the SettingsProvider in the tree view of the Preferences or Project Settings window. | ||
/// The path should be unique among all other settings paths and should use "/" as its separator. | ||
/// </summary> | ||
protected abstract string SettingsPath { get; } | ||
|
||
/// <summary> | ||
/// Gets the Scope of the SettingsProvider. The Scope determines whether the SettingsProvider appears | ||
/// in the Preferences window (SettingsScope.User) or the Settings window (SettingsScope.Project). | ||
/// </summary> | ||
protected abstract SettingsScope Scope { get; } | ||
|
||
/// <summary> | ||
/// Add tab to the window top bar. | ||
/// </summary> | ||
/// <param name="label">Tab label.</param> | ||
/// <param name="content">Tab content.</param> | ||
/// <exception cref="ArgumentException">Will throw tab with the same label was already added.</exception> | ||
protected void AddTab(string label, VisualElement content) | ||
{ | ||
m_TabController.AddTab(label, content); | ||
} | ||
|
||
/// <summary> | ||
/// Activate tab by name | ||
/// </summary> | ||
/// <param name="name">Early specified tab name</param> | ||
protected void ActivateTab(string name) | ||
{ | ||
m_TabController.ActivateTab(name); | ||
} | ||
|
||
/// <summary> | ||
/// Set the flexible growth property of tabs content container | ||
/// </summary> | ||
/// <param name="styleFloat"></param> | ||
protected void ContentContainerFlexGrow(StyleFloat styleFloat) | ||
{ | ||
m_TabController.ContentContainerFlexGrow(styleFloat); | ||
} | ||
|
||
/// <summary> | ||
/// Overrides SettingsProvider.OnActivate. | ||
/// </summary> | ||
protected abstract void OnActivate(string searchContext, VisualElement rootElement); | ||
|
||
/// <summary> | ||
/// Overrides SettingsProvider.OnDeactivate. | ||
/// </summary> | ||
protected abstract void OnDeactivate(); | ||
|
||
void OnActivateWindow(string searchContext, VisualElement rootElement) | ||
{ | ||
UIToolkitEditorUtility.CloneTreeAndApplyStyle(rootElement, | ||
$"{PluginsDevKitPackage.UIToolkitPath}/SettingsWindow/PackageSettingsWindow"); | ||
|
||
// Hide search bar from PackageSettingsWindow. In preferences we already have search bar | ||
// and it's value in "searchContext" parameter | ||
var searchBar = rootElement.Q<ToolbarSearchField>(); | ||
if (searchBar != null) | ||
{ | ||
searchBar.style.visibility = Visibility.Hidden; | ||
} | ||
|
||
var packageInfo = GetPackageInfo(); | ||
rootElement.Q<Label>("display-name").text = packageInfo.displayName; | ||
rootElement.Q<Label>("description").text = packageInfo.description; | ||
rootElement.Q<Label>("version").text = $"Version: {packageInfo.version}"; | ||
|
||
m_TabController = new TabController(rootElement); | ||
} | ||
|
||
void OnActivateHandler(string searchContext, VisualElement rootElement) | ||
{ | ||
OnActivate(searchContext, rootElement); | ||
|
||
EditorApplication.delayCall += () => | ||
{ | ||
m_TabController.RefreshActiveTab(); | ||
}; | ||
} | ||
|
||
SettingsProvider ConstructSettingsProvider() | ||
{ | ||
var packageInfo = GetPackageInfo(); | ||
var settingsProvider = new SettingsProvider(SettingsPath, Scope, packageInfo.keywords) | ||
{ | ||
label = packageInfo.displayName, | ||
}; | ||
|
||
settingsProvider.activateHandler += OnActivateWindow; | ||
settingsProvider.activateHandler += OnActivateHandler; | ||
settingsProvider.deactivateHandler += OnDeactivate; | ||
|
||
return settingsProvider; | ||
} | ||
|
||
[SettingsProviderGroup] | ||
static SettingsProvider[] RegisterSettingsProviderGroup() | ||
{ | ||
var baseType = typeof(PackagePreferencesWindow); | ||
var group = new List<SettingsProvider>(); | ||
|
||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) | ||
{ | ||
var derivedTypes = assembly.GetTypes() | ||
.Where(t => baseType.IsAssignableFrom(t) && t != baseType) | ||
.ToArray(); | ||
|
||
foreach (var derivedType in derivedTypes) | ||
{ | ||
var instance = (PackagePreferencesWindow)Activator.CreateInstance(derivedType); | ||
var settingsProvider = instance.ConstructSettingsProvider(); | ||
group.Add(settingsProvider); | ||
} | ||
} | ||
|
||
return group.ToArray(); | ||
} | ||
} | ||
} | ||
|
||
#endif |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.