first commit

This commit is contained in:
SimonSayeBabu
2025-01-17 13:10:20 +01:00
commit bd1057cec0
16967 changed files with 1048699 additions and 0 deletions

View File

@@ -0,0 +1,142 @@
using System;
using UnityEngine;
namespace UnityEditor.SettingsManagement
{
/// <summary>
/// A custom attribute for registering a static field of type <see cref="IUserSetting"/> for the <see cref="UserSettingsProvider"/> window.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public sealed class UserSettingAttribute : Attribute
{
string m_Category;
GUIContent m_Title;
bool m_VisibleInSettingsProvider;
/// <summary>
/// Gets the name of the group (category) to assign this settings value to.
/// When Unity finds settings values in assemblies, it displays them in groups, organized by category.
/// </summary>
/// <value>The group or category where this setting appears in the UI.</value>
public string category
{
get { return m_Category; }
}
/// <summary>
/// Gets the label to show for this setting.
/// </summary>
/// <value>The label that appears beside this setting in the UI.</value>
public GUIContent title
{
get { return m_Title; }
}
/// <summary>
/// True to show this field in the <see cref="UserSettingsProvider"/> interface; false if not.
/// </summary>
public bool visibleInSettingsProvider
{
get { return m_VisibleInSettingsProvider; }
}
/// <summary>
/// Registers a static field as a setting. Fields must be of a type that implements <see cref="IUserSetting"/>.
/// </summary>
public UserSettingAttribute()
{
m_VisibleInSettingsProvider = false;
}
/// <summary>
/// Registers a static field as a setting and creates an entry in the UI. The field must be of a type that implements <see cref="IUserSetting"/>.
/// </summary>
/// <param name="category">The category to assign this setting to.</param>
/// <param name="title">The display text for this setting in the UI.</param>
/// <param name="tooltip">Optional. The tooltip for this setting.</param>
public UserSettingAttribute(string category, string title, string tooltip = null)
{
m_Category = category;
m_Title = new GUIContent(title, tooltip);
m_VisibleInSettingsProvider = true;
}
}
/// <summary>
/// A custom attribute for registering a field with <see cref="Settings"/>, but without automatically creating
/// a property field in the <see cref="SettingsProvider"/>.
/// Unlike <see cref="UserSettingAttribute"/>, this attribute is valid for instance properties as well as static. These values
/// don't appear in the SettingsProvider. Unity clears their stored values when "Reset All" is invoked.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public sealed class SettingsKeyAttribute : Attribute
{
string m_Key;
SettingsScope m_Scope;
/// <summary>
/// Gets the key for this value.
/// </summary>
/// <value>The key used to identify this settings value from the repository.</value>
public string key
{
get { return m_Key; }
}
/// <summary>
/// Gets the location where this setting is serialized.
/// </summary>
/// <value>
/// Indicates whether this is a <see cref="UnityEditor.SettingsScope.Project"/> setting
/// or a <see cref="UnityEditor.SettingsScope.User"/> preference.
/// </value>
public SettingsScope scope
{
get { return m_Scope; }
}
/// <summary>
/// Registers a field as a setting. This allows the <see cref="UserSettingsProvider"/> to reset its value and display it
/// in debugging modes.
/// </summary>
/// <param name="key">The key for this setting.</param>
/// <param name="scope">The scope in which this setting is serialized.</param>
public SettingsKeyAttribute(string key, SettingsScope scope = SettingsScope.Project)
{
m_Key = key;
m_Scope = scope;
}
}
/// <summary>
/// A custom attribute for adding a section of settings to a category.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class UserSettingBlockAttribute : Attribute
{
string m_Category;
/// <summary>
/// Returns the title for the settings group.
/// When Unity finds settings values in assemblies, it displays them in groups, organized by category.
/// </summary>
/// <value>The group or category where this setting appears in the UI.</value>
public string category
{
get { return m_Category; }
}
/// <summary>
/// Registers a static method for a callback in the <see cref="UserSettingsProvider"/> Editor window under a category.
/// <code><![CDATA[
/// [UserSettingBlock("General")]
/// static void GeneralSettings(string[] searchContext) {}
/// ]]></code>
/// </summary>
/// <param name="category">Specify the title of the group of settings under which this setting appears in the UI.</param>
public UserSettingBlockAttribute(string category)
{
m_Category = category;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ea67431821494cfca380c0e07258751
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,215 @@
using System;
using System.IO;
using UnityEngine;
namespace UnityEditor.SettingsManagement
{
/// <summary>
/// Represents a settings repository that stores data serialized to a JSON file.
/// </summary>
[Serializable]
public class FileSettingsRepository : ISettingsRepository
{
/// <summary>
/// Location of where the package settings are saved under the `ProjectSettings` directory.
/// </summary>
/// <returns>The folder where package settings are saved under the `ProjectSettings` directory.</returns>
protected const string k_PackageSettingsDirectory = "ProjectSettings/Packages";
/// <summary>
/// Location of where the package settings are saved under the `UserSettings` directory.
/// </summary>
/// <returns>Per-project user settings directory. </returns>
protected const string k_UserProjectSettingsDirectory = "UserSettings/Packages";
const bool k_PrettyPrintJson = true;
bool m_Initialized;
string m_Path;
[SerializeField]
SettingsDictionary m_Dictionary = new SettingsDictionary();
Hash128 m_JsonHash;
/// <summary>
/// Initializes and returns an instance of the FileSettingsRepository
/// with the serialized data location set to the specified path.
/// </summary>
/// <param name="path">The project-relative path to save settings to.</param>
public FileSettingsRepository(string path)
{
m_Path = path;
m_Initialized = false;
AssemblyReloadEvents.beforeAssemblyReload += Save;
EditorApplication.quitting += Save;
}
void Init()
{
if (m_Initialized)
return;
m_Initialized = true;
if (TryLoadSavedJson(out string json))
{
m_Dictionary = null;
m_JsonHash = Hash128.Compute(json);
EditorJsonUtility.FromJsonOverwrite(json, this);
}
if (m_Dictionary == null)
m_Dictionary = new SettingsDictionary();
}
/// <summary>
/// Sets the <see cref="SettingsScope"/> this repository applies to.
/// </summary>
/// <remarks>
/// By default, this repository implementation is relevant to the Project scope, but any implementations
/// that override this method can choose to store this serialized data at a user scope instead.
/// </remarks>
/// <value>
/// <see cref="SettingsScope.Project"/>, meaning that this setting applies to project settings (the default);
/// or <see cref="SettingsScope.User"/>, meaning that this setting applies to user preferences.
/// </value>
/// <seealso cref="ISettingsRepository.scope"/>
public virtual SettingsScope scope => SettingsScope.Project;
/// <summary>
/// Gets the full path to the file containing the serialized settings data.
/// </summary>
/// <value>The location stored for this repository.</value>
/// <seealso cref="ISettingsRepository.path"/>
public string path
{
get { return m_Path; }
}
/// <summary>
/// Sets the name of file containing the serialized settings data.
/// </summary>
/// <value>The bare filename of the settings file.</value>
public string name => Path.GetFileNameWithoutExtension(path);
/// <summary>
/// Loads the JSON file that stores the values for this settings object.
/// </summary>
/// <param name="json">The full path to the JSON file to load.</param>
/// <returns>True if the file exists; false if it doesn't.</returns>
public bool TryLoadSavedJson(out string json)
{
json = string.Empty;
if (!File.Exists(path))
return false;
json = File.ReadAllText(path);
return true;
}
/// <summary>
/// Saves all settings to their serialized state.
/// </summary>
/// <seealso cref="ISettingsRepository.Save"/>
public void Save()
{
Init();
if (!File.Exists(path))
{
var directory = Path.GetDirectoryName(path);
if (string.IsNullOrEmpty(directory))
{
Debug.LogError(
$"Settings file {name} is saved to an invalid path: {path}. Settings will not be saved.");
return;
}
Directory.CreateDirectory(directory);
}
string json = EditorJsonUtility.ToJson(this, k_PrettyPrintJson);
// While unlikely, a hash collision is possible. Always test the actual saved contents before early exit.
if (m_JsonHash == Hash128.Compute(json)
&& TryLoadSavedJson(out string existing)
&& existing.Equals(json))
return;
#if UNITY_2019_3_OR_NEWER
// AssetDatabase.IsOpenForEdit can be a very slow synchronous blocking call when Unity is connected to
// Perforce Version Control. Especially if it's called repeatedly with every EditorGUI redraw.
if (File.Exists(path) && !AssetDatabase.IsOpenForEdit(path))
{
if (!AssetDatabase.MakeEditable(path))
{
Debug.LogWarning($"Could not save package settings to {path}");
return;
}
}
#endif
try
{
m_JsonHash = Hash128.Compute(json);
File.WriteAllText(path, json);
}
catch (UnauthorizedAccessException)
{
Debug.LogWarning($"Could not save package settings to {path}");
}
}
/// <summary>
/// Sets a value for a settings entry with a matching key and type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="value">The value to set. This value must be serializable.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <seealso cref="ISettingsRepository.Set{T}"/>
public void Set<T>(string key, T value)
{
Init();
m_Dictionary.Set<T>(key, value);
}
/// <summary>
/// Returns a value with key of type `T`, or the fallback value if no matching key is found.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="fallback">Specify the value of type `T` to return if the entry can't be found.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <returns>The settings value if a match is found; otherwise, it returns the default (fallback) value.</returns>
/// <seealso cref="ISettingsRepository.Get{T}"/>
public T Get<T>(string key, T fallback = default(T))
{
Init();
return m_Dictionary.Get<T>(key, fallback);
}
/// <summary>
/// Determines whether this repository contains a settings entry that matches the specified key and is of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <returns>True if a match is found for both key and type; false if no entry is found.</returns>
/// <seealso cref="ISettingsRepository.ContainsKey{T}"/>
public bool ContainsKey<T>(string key)
{
Init();
return m_Dictionary.ContainsKey<T>(key);
}
/// <summary>
/// Removes a key-value pair from the settings repository. This method identifies the settings entry to remove
/// by matching the specified key for a value of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <seealso cref="ISettingsRepository.Remove{T}"/>
public void Remove<T>(string key)
{
Init();
m_Dictionary.Remove<T>(key);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0846ad73a5274e88951b2645bf7dac6c
timeCreated: 1604852136

View File

@@ -0,0 +1,67 @@
namespace UnityEditor.SettingsManagement
{
/// <summary>
/// An interface that represents a settings repository, which is responsible for implementing the saving and loading of values.
/// </summary>
public interface ISettingsRepository
{
/// <summary>
/// Implement this property to get the <see cref="SettingsScope"/> this repository applies to.
/// </summary>
/// <value>
/// Indicates whether this is a <see cref="UnityEditor.SettingsScope.Project"/> setting
/// or a <see cref="UnityEditor.SettingsScope.User"/> preference.
/// </value>
SettingsScope scope { get; }
/// <summary>
/// Implement this property to get the name to identify this repository.
/// </summary>
/// <value>The bare filename of this repository.</value>
string name { get; }
/// <summary>
/// Implement this property to get the file path to the serialized settings data.
/// </summary>
/// <value>Full path to the JSON file containing the settings data.</value>
string path { get; }
/// <summary>
/// Implement this method to save all settings to their serialized state.
/// </summary>
void Save();
/// <summary>
/// Implement this method to set a value for a settings entry with a matching key and type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="value">The value to set. Must be serializable.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
void Set<T>(string key, T value);
/// <summary>
/// Implement this method to get a value for a settings entry with a matching key and type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="fallback">Specify the value of type `T` to return if the entry can't be found.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <returns>The value matching both `key` and type `T`. If there was no match, this returns the `fallback` value.</returns>
T Get<T>(string key, T fallback = default(T));
/// <summary>
/// Implement this method to evaluate whether the repository contains a settings entry that matches the specified key and is of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <returns>True if a settings entry matches both `key` and type `T`; false if no entry is found.</returns>
bool ContainsKey<T>(string key);
/// <summary>
/// Implement this method to remove a key-value pair from the settings repository. This method identifies the settings entry to remove
/// by matching the specified key for a value of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
void Remove<T>(string key);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8707cf6fb25b1438b9a0f476f93a76fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
using System;
namespace UnityEditor.SettingsManagement
{
/// <summary>
/// A settings repository that stores data local to a Unity project relative to a package.
/// The settings data is serialized to a JSON file.
/// </summary>
[Serializable]
public sealed class PackageSettingsRepository : FileSettingsRepository
{
/// <summary>
/// Initializes and returns an instance of the PackageSettingsRepository with the
/// serialized data location set to a path defined by the specified `package` and
/// `name` values relative to the `ProjectSettings` directory. For example:
/// `MyUnityProject/ProjectSettings/Packages/com.example.my-package/Settings.json`.
/// </summary>
/// <param name="package">The name of the package to store the serialized data under.</param>
/// <param name="name">The base filename to use for the serialized data location.</param>
public PackageSettingsRepository(string package, string name) : base(GetSettingsPath(package, name))
{
}
// Cannot call FindFromAssembly from a constructor or field initializer
// static string CreateSettingsPath(Assembly assembly, string name)
// {
// var info = PackageManager.PackageInfo.FindForAssembly(assembly);
// return string.Format("{0}/{1}/{2}.json", k_PackageSettingsDirectory, info.name, name);
// }
/// <summary>
/// Builds and returns a path for a settings file relative to the calling assembly's package directory.
/// This method constructs the location from the specified `package` and (filename) `name` under the `ProjectSettings` folder.
/// </summary>
/// <param name="packageName">The name of the package requesting this setting.</param>
/// <param name="name">An optional name for the settings file. Default is "Settings."</param>
/// <returns>A package-scoped path to the settings file inside the project's `ProjectSettings`.</returns>
public static string GetSettingsPath(string packageName, string name = "Settings")
{
return string.Format("{0}/{1}/{2}.json", k_PackageSettingsDirectory, packageName, name);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e2e054ed623847d29efb1262605e8ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
using System;
namespace UnityEditor.SettingsManagement
{
/// <summary>
/// Represents a settings repository that stores data local to a Unity project.
/// The settings data is serialized to a JSON file.
/// </summary>
[Serializable]
public class ProjectUserSettings : FileSettingsRepository
{
/// <summary>
/// Initializes and returns an instance of the ProjectUserSettings repository with the
/// serialized data location set to a path defined by the specified `package` and
/// `name` values relative to the `UserSettings` directory. For example:
/// `MyUnityProject/UserSettings/Packages/com.example.my-package/Settings.json`.
/// </summary>
/// <param name="package">The name of the package to store the serialized data under.</param>
/// <param name="name">The base filename to use for the serialized data location (defaults to "Settings").</param>
public ProjectUserSettings(string package, string name = "Settings") : base(GetUserSettingsPath(package, name))
{
}
/// <summary>
/// Builds and returns a path for a settings file relative to the calling assembly's package directory.
/// This method constructs the location from the specified `package` and (filename) `name` under the `UserSettings` folder.
/// </summary>
/// <param name="package">The name of the package requesting this setting.</param>
/// <param name="name">An optional name for the settings file. Default is "Settings."</param>
/// <returns>A package-scoped path to the settings file inside the project's `UserSettings` folder.</returns>
public static string GetUserSettingsPath(string package, string name)
{
return string.Format("{0}/{1}/{2}.json", k_UserProjectSettingsDirectory, package, name);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 30e7b5764902497297f0c9ca80bb179a
timeCreated: 1604851389

View File

@@ -0,0 +1,311 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UnityEditor.SettingsManagement
{
/// <summary>
/// Represents a collection of objects that implement <see cref="ISettingsRepository"/>.
/// </summary>
public sealed class Settings
{
ISettingsRepository[] m_SettingsRepositories;
/// <summary>
/// Called prior to when an instance of <see cref="ISettingsRepository"/> serializes its current state.
/// </summary>
public event Action beforeSettingsSaved;
/// <summary>
/// Called immediately after an instance of <see cref="ISettingsRepository"/> serializes its current state.
/// </summary>
public event Action afterSettingsSaved;
Settings()
{
}
/// <summary>
/// Creates a new Settings instance with a <see cref="UserSettingsRepository"/> and <see cref="PackageSettingsRepository"/>.
/// </summary>
/// <param name="package">The package name, such as `com.example.my-package`.</param>
/// <param name="settingsFileName">The name of the settings file. The default value is `Settings`.</param>
public Settings(string package, string settingsFileName = "Settings")
{
m_SettingsRepositories = new ISettingsRepository[]
{
new PackageSettingsRepository(package, settingsFileName),
new UserSettingsRepository()
};
}
/// <summary>
/// Creates a new Settings instance with a collection of objects that implement <see cref="ISettingsRepository"/>.
/// </summary>
/// <param name="repositories">The repositories to populate the Settings instance with.</param>
public Settings(IEnumerable<ISettingsRepository> repositories)
{
m_SettingsRepositories = repositories.ToArray();
}
/// <summary>
/// Finds and returns a settings repository that matches the specified scope.
/// </summary>
/// <param name="scope">The <see cref="SettingsScope">scope</see> of the settings repository to match.</param>
/// <returns>
/// An <see cref="ISettingsRepository"/> instance that implements the requested scope; or null if no
/// matching repository is found.
/// </returns>
public ISettingsRepository GetRepository(SettingsScope scope)
{
foreach (var repo in m_SettingsRepositories)
if (repo.scope == scope)
return repo;
return null;
}
/// <summary>
/// Finds and returns a settings repository that matches the specified scope and name.
/// </summary>
/// <param name="scope">The <see cref="SettingsScope">scope</see> of the settings repository to match.</param>
/// <param name="name">The name of the <see cref="ISettingsRepository"/> to match.</param>
/// <returns>
/// An <see cref="ISettingsRepository"/> instance that implements the specified scope and matches the name; or
/// null if no matching repository is found.
/// </returns>
public ISettingsRepository GetRepository(SettingsScope scope, string name)
{
foreach (var repo in m_SettingsRepositories)
if (repo.scope == scope && string.Equals(repo.name, name))
return repo;
return null;
}
/// <summary>
/// Serializes the state of all settings repositories.
/// </summary>
public void Save()
{
if (beforeSettingsSaved != null)
beforeSettingsSaved();
foreach (var repo in m_SettingsRepositories)
repo.Save();
if (afterSettingsSaved != null)
afterSettingsSaved();
}
/// <summary>
/// Sets a value for a settings entry with a matching key and type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="value">The value to set. This must be serializable.</param>
/// <param name="scope">The <see cref="SettingsScope">scope</see> of the settings repository to match.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
public void Set<T>(string key, T value, SettingsScope scope = SettingsScope.Project)
{
if (scope == SettingsScope.Project)
Set<T, PackageSettingsRepository>(key, value);
Set<T, UserSettingsRepository>(key, value);
}
/// <summary>
/// Sets a value for a settings entry with a matching key and type `T` from the specified repository.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="value">The value to set. This must be serializable.</param>
/// <param name="repositoryName">Optional. The name of the repository to set this value in.</param>
/// <param name="scope">The <see cref="SettingsScope">scope</see> of the settings repository to match.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
public void Set<T>(string key, T value, string repositoryName, SettingsScope scope = SettingsScope.Project)
{
if (scope == SettingsScope.Project)
Set<T, PackageSettingsRepository>(key, value, repositoryName);
Set<T, UserSettingsRepository>(key, value, repositoryName);
}
/// <summary>
/// Sets a value for a settings entry with a matching key and type `T` from the specified repository of type `K`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="value">The value to set. This must be serializable.</param>
/// <param name="repositoryName">Optional. The name of the repository to set this value in.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <typeparam name="K">The type of repository to search for matching keys.</typeparam>
public void Set<T, K>(string key, T value, string repositoryName = null) where K : ISettingsRepository
{
bool foundScopeRepository = false;
foreach (var repo in m_SettingsRepositories)
{
if (repo is K && (string.IsNullOrEmpty(repositoryName) || repo.name == repositoryName))
{
repo.Set<T>(key, value);
foundScopeRepository = true;
}
}
if (!foundScopeRepository)
Debug.LogWarning($"No repository with type {typeof(K)} found.");
}
/// <summary>
/// Returns a value for a settings entry with a matching key and type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="scope">The <see cref="SettingsScope">scope</see> of the settings repository to match.</param>
/// <param name="fallback">Specify the value of type `T` to return if the entry can't be found.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <returns>The value from a matching settings entry; or the default value if not found.</returns>
public T Get<T>(string key, SettingsScope scope = SettingsScope.Project, T fallback = default(T))
{
if (scope == SettingsScope.Project)
return Get<T, PackageSettingsRepository>(key, fallback);
return Get<T, UserSettingsRepository>(key, fallback);
}
/// <summary>
/// Returns a value for a settings entry with a matching key and type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="repositoryName">The repository name to match.</param>
/// <param name="scope">The <see cref="SettingsScope">scope</see> of the settings repository to match.</param>
/// <param name="fallback">Specify the value of type `T` to return if the entry can't be found.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <returns>The value from a matching settings entry; or the default value if not found.</returns>
public T Get<T>(string key, string repositoryName, SettingsScope scope = SettingsScope.Project, T fallback = default(T))
{
if (scope == SettingsScope.Project)
return Get<T, PackageSettingsRepository>(key, fallback, repositoryName);
return Get<T, UserSettingsRepository>(key, fallback, repositoryName);
}
/// <summary>
/// Returns a value for a settings entry with a matching key and type `T` from the specified repository of type `K`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="fallback">Specify the value of type `T` to return if the entry can't be found.</param>
/// <param name="repositoryName">If provided, only repositories with a matching name will be searched for the key.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <typeparam name="K">The type of repository to search for matching keys.</typeparam>
/// <returns>The value from a matching settings entry; or the default value if not found.</returns>
public T Get<T, K>(string key, T fallback = default(T), string repositoryName = null) where K : ISettingsRepository
{
foreach (var repo in m_SettingsRepositories)
{
if (repo is K && (string.IsNullOrEmpty(repositoryName) || repo.name == repositoryName))
return repo.Get<T>(key, fallback);
}
Debug.LogWarning($"No repository with type {typeof(K)} found.");
return fallback;
}
/// <summary>
/// Determines whether the repository in the specified <see cref="SettingsScope">scope</see> contains a settings entry
/// that matches the specified key and is of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="scope">The <see cref="SettingsScope">scope</see> of the settings repository to match.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <returns>True if a setting matching both key and type is found; false if no entry is found.</returns>
public bool ContainsKey<T>(string key, SettingsScope scope = SettingsScope.Project)
{
if (scope == SettingsScope.Project)
return ContainsKey<T, PackageSettingsRepository>(key);
return ContainsKey<T, UserSettingsRepository>(key);
}
/// <summary>
/// Determines whether the specified repository contains a settings entry that matches the specified key and is of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="repositoryName">The repository name to match.</param>
/// <param name="scope">The <see cref="SettingsScope">scope</see> of the settings repository to match.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <returns>True if a setting matching both key and type is found; false if no entry is found.</returns>
public bool ContainsKey<T>(string key, string repositoryName, SettingsScope scope = SettingsScope.Project)
{
if (scope == SettingsScope.Project)
return ContainsKey<T, PackageSettingsRepository>(key, repositoryName);
return ContainsKey<T, UserSettingsRepository>(key, repositoryName);
}
/// <summary>
/// Determines whether the specified repository of type `K` contains a settings entry that matches the specified key and is of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="repositoryName">The repository name to match.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <typeparam name="K">The type of repository to search for matching keys.</typeparam>
/// <returns>True if a setting matching both key and type is found; false if no entry is found.</returns>
public bool ContainsKey<T, K>(string key, string repositoryName = null) where K : ISettingsRepository
{
foreach (var repo in m_SettingsRepositories)
{
if (repo is K && (string.IsNullOrEmpty(repositoryName) || repositoryName == repo.name))
return repo.ContainsKey<T>(key);
}
Debug.LogWarning($"No repository with type {typeof(K)} found.");
return false;
}
/// <summary>
/// Removes a key-value pair from a settings repository. This method identifies the settings entry to remove
/// from any repository in the specified <see cref="SettingsScope">scope</see> by matching the specified key
/// for a value of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="scope">The <see cref="SettingsScope">scope</see> of the settings repository to match.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
public void DeleteKey<T>(string key, SettingsScope scope = SettingsScope.Project)
{
if (scope == SettingsScope.Project)
DeleteKey<T, PackageSettingsRepository>(key);
DeleteKey<T, UserSettingsRepository>(key);
}
/// <summary>
/// Removes a key-value pair from a settings repository. This method identifies the settings entry to remove
/// from the specified repository by matching the specified key for a value of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="repositoryName">The repository name to match.</param>
/// <param name="scope">The <see cref="SettingsScope">scope</see> of the settings repository to match.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
public void DeleteKey<T>(string key, string repositoryName, SettingsScope scope = SettingsScope.Project)
{
if (scope == SettingsScope.Project)
DeleteKey<T, PackageSettingsRepository>(key, repositoryName);
DeleteKey<T, UserSettingsRepository>(key, repositoryName);
}
/// <summary>
/// Removes a key-value pair from a settings repository. This method identifies the settings entry to remove
/// from the specified repository of type `K` by matching the specified key for a value of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="repositoryName">The repository name to match.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <typeparam name="K">The type of repository to search for matching keys.</typeparam>
public void DeleteKey<T, K>(string key, string repositoryName = null) where K : ISettingsRepository
{
bool foundScopeRepository = false;
foreach (var repo in m_SettingsRepositories)
{
if (repo is K && (string.IsNullOrEmpty(repositoryName) || repositoryName == repo.name))
{
foundScopeRepository = true;
repo.Remove<T>(key);
}
}
if (!foundScopeRepository)
Debug.LogWarning($"No repository with type {typeof(K)} found.");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d3c815396f3a64c2d925a2100e08e093
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.SettingsManagement
{
[Serializable]
sealed class SettingsDictionary : ISerializationCallbackReceiver
{
[Serializable]
struct SettingsKeyValuePair
{
public string type;
public string key;
public string value;
}
#pragma warning disable 0649
[SerializeField]
List<SettingsKeyValuePair> m_DictionaryValues = new List<SettingsKeyValuePair>();
#pragma warning restore 0649
internal Dictionary<Type, Dictionary<string, string>> dictionary = new Dictionary<Type, Dictionary<string, string>>();
public bool ContainsKey<T>(string key)
{
return dictionary.ContainsKey(typeof(T)) && dictionary[typeof(T)].ContainsKey(key);
}
public void Set<T>(string key, T value)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
var type = typeof(T).AssemblyQualifiedName;
SetJson(type, key, ValueWrapper<T>.Serialize(value));
}
internal void SetJson(string type, string key, string value)
{
var typeValue = Type.GetType(type);
if (typeValue == null)
throw new ArgumentException("\"type\" must be an assembly qualified type name.");
Dictionary<string, string> entries;
if (!dictionary.TryGetValue(typeValue, out entries))
dictionary.Add(typeValue, entries = new Dictionary<string, string>());
if (entries.ContainsKey(key))
entries[key] = value;
else
entries.Add(key, value);
}
public T Get<T>(string key, T fallback = default(T))
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
Dictionary<string, string> entries;
if (dictionary.TryGetValue(typeof(T), out entries) && entries.ContainsKey(key))
{
try
{
return ValueWrapper<T>.Deserialize(entries[key]);
}
catch
{
return fallback;
}
}
return fallback;
}
public void Remove<T>(string key)
{
Dictionary<string, string> entries;
if (!dictionary.TryGetValue(typeof(T), out entries) || !entries.ContainsKey(key))
return;
entries.Remove(key);
}
public void OnBeforeSerialize()
{
if (m_DictionaryValues == null)
return;
m_DictionaryValues.Clear();
foreach (var type in dictionary)
{
foreach (var entry in type.Value)
{
m_DictionaryValues.Add(new SettingsKeyValuePair()
{
type = type.Key.AssemblyQualifiedName,
key = entry.Key,
value = entry.Value
});
}
}
}
public void OnAfterDeserialize()
{
dictionary.Clear();
foreach (var entry in m_DictionaryValues)
{
Dictionary<string, string> entries;
var type = Type.GetType(entry.type);
if (type == null)
{
Debug.LogWarning("Could not instantiate type \"" + entry.type + "\". Skipping key: " + entry.key + ".");
continue;
}
if (dictionary.TryGetValue(type, out entries))
entries.Add(entry.key, entry.value);
else
dictionary.Add(type, new Dictionary<string, string>() { { entry.key, entry.value } });
}
}
public override string ToString()
{
var sb = new System.Text.StringBuilder();
foreach (var type in dictionary)
{
sb.AppendLine("Type: " + type.Key);
foreach (var entry in type.Value)
{
sb.AppendLine(string.Format(" {0,-64}{1}", entry.Key, entry.Value));
}
}
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67c0fca06520e4f78923163f9541ba6c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 223e1a5330ab8468782152a65e857f94
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
{
"name": "Unity.Settings.Editor",
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 49818357e697641afb75d2f8acaf1861
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,397 @@
using System;
using UnityEngine;
namespace UnityEditor.SettingsManagement
{
[Flags]
enum SettingVisibility
{
None = 0 << 0,
/// <summary>
/// Matches any static field implementing IUserSetting and tagged with [UserSettingAttribute(visibleInSettingsProvider = true)].
/// These fields are automatically scraped by the SettingsProvider and displayed.
/// </summary>
Visible = 1 << 0,
/// <summary>
/// Matches any static field implementing IUserSetting and tagged with [UserSettingAttribute(visibleInSettingsProvider = false)].
/// These fields will be reset by the "Reset All" menu in SettingsProvider, but are not shown in the interface.
/// Typically these fields require some conditional formatting or data handling, and are shown in the
/// SettingsProvider UI with a [UserSettingBlockAttribute].
/// </summary>
Hidden = 1 << 1,
/// <summary>
/// A static or instance field tagged with [SettingsKeyAttribute].
/// Unlisted settings are not shown in the SettingsProvider, but are reset to default values by the "Reset All"
/// context menu.
/// </summary>
Unlisted = 1 << 2,
/// <summary>
/// A static field implementing IUserSetting that is not marked with any setting attribute.
/// Unregistered IUserSetting fields are not affected by the SettingsProvider.
/// </summary>
Unregistered = 1 << 3,
All = Visible | Hidden | Unlisted | Unregistered
}
/// <summary>
/// An interface that represents a user setting.
/// Types implementing IUserSetting are eligible for use with <see cref="UserSettingAttribute"/>, which enables
/// fields to automatically populate the <see cref="UserSettingsProvider"/> interface.
/// </summary>
public interface IUserSetting
{
/// <summary>
/// Implement this property to get the key for this value.
/// </summary>
/// <value>The key used to identify the settings entry. This is used along with the <see cref="type"/> to uniquely identify the value.</value>
string key { get; }
/// <summary>
/// Implement this property to get the type of the stored value.
/// </summary>
/// <value>The type of value. This is used along with the <see cref="key"/> to uniquely identify the value.</value>
Type type { get; }
/// <summary>
/// Implement this property to get the location in the UI where this setting will appear.
/// </summary>
/// <value>
/// Indicates whether this is a <see cref="UnityEditor.SettingsScope.Project"/> setting
/// or a <see cref="UnityEditor.SettingsScope.User"/> preference.
/// </value>
SettingsScope scope { get; }
/// <summary>
/// Implement this property to get the name of the <see cref="ISettingsRepository"/> that this setting should be associated with.
/// If null, the first repository matching the <see cref="scope"/> is used.
/// </summary>
/// <value>The bare filename of this repository.</value>
string settingsRepositoryName { get; }
/// <summary>
/// Implement this property to get the <see cref="Settings"/> instance to save and load this setting from.
/// </summary>
/// <value>A reference to <see cref="Settings"/> instance.</value>
Settings settings { get; }
/// <summary>
/// Implement this method to return the stored settings value.
/// If you are implementing IUserSetting, you should cache this value.
/// </summary>
/// <returns>
/// The stored value.
/// </returns>
object GetValue();
/// <summary>
/// Implement this method to return the the default value for this setting.
/// </summary>
/// <returns>
/// The default value for this setting.
/// </returns>
object GetDefaultValue();
/// <summary>
/// Implement this method to set the value for this setting.
/// </summary>
/// <param name="value">The new value.</param>
/// <param name="saveProjectSettingsImmediately">
/// True to immediately serialize the <see cref="ISettingsRepository"/> that is backing this value; or false to postpone.
/// If not serializing immediately, be sure to call <see cref="Settings.Save"/>.
/// </param>
void SetValue(object value, bool saveProjectSettingsImmediately = false);
/// <summary>
/// Implement this method to explicitly update the <see cref="ISettingsRepository"/> that is backing this value.
/// When the inspected type is a reference value, it is possible to change properties without affecting the
/// backing setting. ApplyModifiedProperties provides a method to force serialize these changes.
/// </summary>
void ApplyModifiedProperties();
/// <summary>
/// Implement this method to set the current value back to the default.
/// </summary>
/// <param name="saveProjectSettingsImmediately">True to immediately re-serialize project settings. By default, no values are updated. </param>
void Reset(bool saveProjectSettingsImmediately = false);
/// <summary>
/// Implement this method to delete the saved setting. This does not clear the current value.
/// </summary>
/// <seealso cref="Reset"/>
/// <param name="saveProjectSettingsImmediately">True to immediately re-serialize project settings. By default, no values are updated.</param>
void Delete(bool saveProjectSettingsImmediately = false);
}
/// <summary>
/// A generic implementation of <see cref="IUserSetting"/> to use with a <see cref="Settings"/> instance. This default
/// implementation assumes that the <see cref="Settings"/> instance contains two <see cref="ISettingsRepository"/> interfaces:
/// - Project settings (<see cref="SettingsScope.Project"/>)
/// - User preferences (<see cref="SettingsScope.User"/>)
/// </summary>
/// <typeparam name="T">Type of value.</typeparam>
public class UserSetting<T> : IUserSetting
{
bool m_Initialized;
string m_Key;
string m_Repository;
T m_Value;
T m_DefaultValue;
SettingsScope m_Scope;
Settings m_Settings;
UserSetting() { }
/// <summary>
/// Initializes and returns an instance of the UserSetting&lt;T&gt; type.
/// </summary>
/// <param name="settings">The <see cref="Settings"/> instance to save and load this setting from.</param>
/// <param name="key">The key for this value.</param>
/// <param name="value">The default value for this key.</param>
/// <param name="scope">The scope for this setting. By default, the scope is the project.</param>
public UserSetting(Settings settings, string key, T value, SettingsScope scope = SettingsScope.Project)
{
m_Key = key;
m_Repository = null;
m_Value = value;
m_Scope = scope;
m_Initialized = false;
m_Settings = settings;
}
/// <summary>
/// Initializes and returns an instance of the UserSetting&lt;T&gt; type using the specified repository.
/// </summary>
/// <param name="settings">The <see cref="Settings"/> instance to save and load this setting from.</param>
/// <param name="repository">The <see cref="ISettingsRepository"/> name to save and load this setting from. Specify null to save to the first available instance.</param>
/// <param name="key">The key for this value.</param>
/// <param name="value">The default value for this key.</param>
/// <param name="scope">The scope for this setting. By default, the scope is the project.</param>
public UserSetting(Settings settings, string repository, string key, T value, SettingsScope scope = SettingsScope.Project)
{
m_Key = key;
m_Repository = repository;
m_Value = value;
m_Scope = scope;
m_Initialized = false;
m_Settings = settings;
}
/// <summary>
/// Gets the key for this value.
/// </summary>
/// <seealso cref="IUserSetting.key"/>
public string key
{
get { return m_Key; }
}
/// <summary>
/// Gets the name of the repository that this setting is saved in.
/// </summary>
/// <seealso cref="IUserSetting.settingsRepositoryName" />
public string settingsRepositoryName
{
get { return m_Repository; }
}
/// <summary>
/// Gets the type that this setting represents (&lt;T&gt;).
/// </summary>
/// <seealso cref="IUserSetting.type" />
public Type type
{
get { return typeof(T); }
}
/// <summary>
/// Returns a copy of the default value.
/// </summary>
/// <returns>
/// The default value.
/// </returns>
/// <seealso cref="IUserSetting.GetDefaultValue" />
public object GetDefaultValue()
{
return defaultValue;
}
/// <summary>
/// Returns the currently stored value.
/// </summary>
/// <returns>
/// The value that is currently set.
/// </returns>
/// <seealso cref="IUserSetting.GetValue" />
public object GetValue()
{
return value;
}
/// <summary>
/// Gets the scope (<see cref="ISettingsRepository"/>) where the <see cref="Settings"/> instance saves
/// its data.
/// </summary>
/// <seealso cref="IUserSetting.scope" />
public SettingsScope scope
{
get { return m_Scope; }
}
/// <summary>
/// Gets the <see cref="Settings"/> instance to read from and save to.
/// </summary>
/// <seealso cref="IUserSetting.settings" />
public Settings settings
{
get { return m_Settings; }
}
/// <summary>
/// Sets the value for this setting from the specified object.
/// </summary>
/// <param name="value">The new value to set.</param>
/// <param name="saveProjectSettingsImmediately">
/// Set this value to true if you want to immediately serialize the <see cref="ISettingsRepository"/>
/// that is backing this value. By default, this is false.
///
/// **Note**: If not serializing immediately, you need to call <see cref="Settings.Save"/>.
/// </param>
/// <seealso cref="IUserSetting.SetValue" />
public void SetValue(object value, bool saveProjectSettingsImmediately = false)
{
// we do want to allow null values
if (value != null && !(value is T))
throw new ArgumentException("Value must be of type " + typeof(T) + "\n" + key + " expecting value of type " + type + ", received " + value.GetType());
SetValue((T)value, saveProjectSettingsImmediately);
}
/// <inheritdoc cref="SetValue" />
public void SetValue(T value, bool saveProjectSettingsImmediately = false)
{
Init();
m_Value = value;
settings.Set<T>(key, m_Value, m_Scope);
if (saveProjectSettingsImmediately)
settings.Save();
}
/// <summary>
/// Deletes the saved setting but doesn't clear the current value.
/// </summary>
/// <param name="saveProjectSettingsImmediately">
/// Set this value to true if you want to immediately serialize the <see cref="ISettingsRepository"/>
/// that is backing this value. By default, this is false.
///
/// **Note**: If not serializing immediately, you need to call <see cref="Settings.Save"/>.
/// </param>
/// <seealso cref="Reset" />
/// <seealso cref="IUserSetting.Delete"/>
public void Delete(bool saveProjectSettingsImmediately = false)
{
settings.DeleteKey<T>(key, scope);
// Don't Init() because that will set the key again. We just want to reset the m_Value with default and
// pretend that this field hasn't been initialised yet.
m_Value = ValueWrapper<T>.DeepCopy(m_DefaultValue);
m_Initialized = false;
}
/// <summary>
/// Forces Unity to serialize the changed properties to the <see cref="ISettingsRepository"/> that is backing this value.
/// When the inspected type is a reference value, it is possible to change properties without affecting the
/// backing setting.
/// </summary>
/// <seealso cref="IUserSetting.ApplyModifiedProperties"/>
public void ApplyModifiedProperties()
{
settings.Set<T>(key, m_Value, m_Scope);
settings.Save();
}
/// <summary>
/// Sets the current value back to the default.
/// </summary>
/// <param name="saveProjectSettingsImmediately">
/// Set this value to true if you want to immediately serialize the <see cref="ISettingsRepository"/>
/// that is backing this value. By default, this is false.
///
/// **Note**: If not serializing immediately, you need to call <see cref="Settings.Save"/>.
/// </param>
/// <seealso cref="IUserSetting.Reset"/>
public void Reset(bool saveProjectSettingsImmediately = false)
{
SetValue(defaultValue, saveProjectSettingsImmediately);
}
void Init()
{
if (!m_Initialized)
{
if (m_Scope == SettingsScope.Project && settings == null)
throw new Exception("UserSetting \"" + m_Key + "\" is attempting to access SettingsScope.Project setting with no Settings instance!");
m_Initialized = true;
// DeepCopy uses EditorJsonUtility which is not permitted during construction
m_DefaultValue = ValueWrapper<T>.DeepCopy(m_Value);
if (settings.ContainsKey<T>(m_Key, m_Scope))
m_Value = settings.Get<T>(m_Key, m_Scope);
else
settings.Set<T>(m_Key, m_Value, m_Scope);
}
}
/// <summary>
/// Gets the default value for this setting.
/// </summary>
public T defaultValue
{
get
{
Init();
return ValueWrapper<T>.DeepCopy(m_DefaultValue);
}
}
/// <summary>
/// Gets or sets the currently stored value.
/// </summary>
public T value
{
get
{
Init();
return m_Value;
}
set { SetValue(value); }
}
/// <summary>
/// Implicit casts this setting to the backing type `T`.
/// </summary>
/// <param name="pref">The UserSetting&lt;T&gt; to cast to `T`.</param>
/// <returns>
/// The currently stored <see cref="value"/>.
/// </returns>
public static implicit operator T(UserSetting<T> pref)
{
return pref.value;
}
/// <summary>
/// Returns a string representation of this setting.
/// </summary>
/// <returns>A string summary of this setting of format "[scope] setting. Key: [key] Value: [value]".</returns>
public override string ToString()
{
return string.Format("{0} setting. Key: {1} Value: {2}", scope, key, value);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c3ec6d10638d94feaa03c223b6fee2cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace UnityEditor.SettingsManagement
{
/// <summary>
/// A collection of utilities for working with settings.
/// </summary>
static class UserSettings
{
internal const string packageName = "com.unity.settings-manager";
internal static string GetSettingsString(IEnumerable<Assembly> assemblies, params SettingsScope[] scopes)
{
var settings = FindUserSettings(assemblies, SettingVisibility.All);
if (scopes != null && scopes.Length > 0)
settings = settings.Where(x => scopes.Contains(x.scope));
var sb = new System.Text.StringBuilder();
Type t = null;
foreach (var pref in settings.OrderBy(x => x.type.ToString()))
{
if (pref.type != t)
{
if (t != null)
sb.AppendLine();
t = pref.type;
sb.AppendLine(pref.type.ToString());
}
var val = pref.GetValue();
sb.AppendLine(string.Format("{0,-4}{1,-24}{2,-64}{3}", "", pref.scope, pref.key, val != null ? val.ToString() : "null"));
}
return sb.ToString();
}
/// <summary>
/// Collect all registered UserSetting and HiddenSetting attributes.
/// </summary>
/// <returns></returns>
public static IEnumerable<IUserSetting> FindUserSettings(IEnumerable<Assembly> assemblies, SettingVisibility visibility, BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
{
var loadedTypes = assemblies.SelectMany(x => x.GetTypes());
var loadedFields = loadedTypes.SelectMany(x => x.GetFields(flags));
var settings = new List<IUserSetting>();
if ((visibility & (SettingVisibility.Visible | SettingVisibility.Unlisted)) > 0)
{
var attributes = loadedFields.Where(prop => Attribute.IsDefined(prop, typeof(UserSettingAttribute)));
foreach (var field in attributes)
{
var userSetting = (UserSettingAttribute)Attribute.GetCustomAttribute(field, typeof(UserSettingAttribute));
if (!field.IsStatic || !typeof(IUserSetting).IsAssignableFrom(field.FieldType))
{
Debug.LogError("[UserSetting] is only valid on static fields of a type implementing `interface IUserSetting`. \"" + field.Name + "\" (" + field.FieldType + ")\n" + field.DeclaringType);
continue;
}
bool visible = userSetting.visibleInSettingsProvider;
if (visible && (visibility & SettingVisibility.Visible) == SettingVisibility.Visible)
settings.Add((IUserSetting)field.GetValue(null));
else if (!visible && (visibility & SettingVisibility.Hidden) == SettingVisibility.Hidden)
settings.Add((IUserSetting)field.GetValue(null));
}
}
if ((visibility & SettingVisibility.Unlisted) == SettingVisibility.Unlisted)
{
var settingsKeys = loadedFields.Where(y => Attribute.IsDefined(y, typeof(SettingsKeyAttribute)));
foreach (var field in settingsKeys)
{
if (field.IsStatic)
{
settings.Add((IUserSetting)field.GetValue(null));
}
else
{
var settingAttribute = (SettingsKeyAttribute)Attribute.GetCustomAttribute(field, typeof(SettingsKeyAttribute));
var pref = CreateGenericPref(settingAttribute.key, settingAttribute.scope, field);
if (pref != null)
settings.Add(pref);
else
Debug.LogWarning("Failed adding [SettingsKey] " + field.FieldType + "\"" + settingAttribute.key + "\" in " + field.DeclaringType);
}
}
}
if ((visibility & SettingVisibility.Unregistered) == SettingVisibility.Unregistered)
{
var unregisterd = loadedFields.Where(y => typeof(IUserSetting).IsAssignableFrom(y.FieldType)
&& !Attribute.IsDefined(y, typeof(SettingsKeyAttribute))
&& !Attribute.IsDefined(y, typeof(UserSettingAttribute)));
foreach (var field in unregisterd)
{
if (field.IsStatic)
{
settings.Add((IUserSetting)field.GetValue(null));
}
else
{
#if PB_DEBUG
Log.Warning("Found unregistered instance field: "
+ field.FieldType
+ " "
+ field.Name
+ " in " + field.DeclaringType);
#endif
}
}
}
return settings;
}
static IUserSetting CreateGenericPref(string key, SettingsScope scope, FieldInfo field)
{
try
{
var type = field.FieldType;
if (typeof(IUserSetting).IsAssignableFrom(type) && type.IsGenericType)
type = type.GetGenericArguments().FirstOrDefault();
var genericPrefClass = typeof(UserSetting<>).MakeGenericType(type);
var defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;
return (IUserSetting)Activator.CreateInstance(genericPrefClass, new object[] { key, defaultValue, scope });
}
catch
{
return null;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c24f57f3f3c554e51ab791526e33e337
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0dc75b34a006d47f48c004031e6ef14d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,141 @@
namespace UnityEditor.SettingsManagement
{
/// <summary>
/// Represents a settings repository for user preferences.
/// </summary>
/// <seealso cref="UnityEditor.EditorPrefs"/>
public class UserSettingsRepository : ISettingsRepository
{
static string GetEditorPrefKey<T>(string key)
{
return GetEditorPrefKey(typeof(T).FullName, key);
}
static string GetEditorPrefKey(string fullName, string key)
{
return fullName + "::" + key;
}
static void SetEditorPref<T>(string key, T value)
{
var k = GetEditorPrefKey<T>(key);
if (typeof(T) == typeof(string))
EditorPrefs.SetString(k, (string)(object)value);
else if (typeof(T) == typeof(bool))
EditorPrefs.SetBool(k, (bool)(object)value);
else if (typeof(T) == typeof(float))
EditorPrefs.SetFloat(k, (float)(object)value);
else if (typeof(T) == typeof(int))
EditorPrefs.SetInt(k, (int)(object)value);
else
EditorPrefs.SetString(k, ValueWrapper<T>.Serialize(value));
}
static T GetEditorPref<T>(string key, T fallback = default(T))
{
var k = GetEditorPrefKey<T>(key);
if (!EditorPrefs.HasKey(k))
return fallback;
var o = (object)fallback;
if (typeof(T) == typeof(string))
o = EditorPrefs.GetString(k, (string)o);
else if (typeof(T) == typeof(bool))
o = EditorPrefs.GetBool(k, (bool)o);
else if (typeof(T) == typeof(float))
o = EditorPrefs.GetFloat(k, (float)o);
else if (typeof(T) == typeof(int))
o = EditorPrefs.GetInt(k, (int)o);
else
return ValueWrapper<T>.Deserialize(EditorPrefs.GetString(k));
return (T)o;
}
/// <summary>
/// Gets the <see cref="UnityEditor.SettingsScope">scope</see> this repository applies to.
/// </summary>
/// <value>Indicates that this is a <see cref="UnityEditor.SettingsScope.User"/> preference.</value>
/// <seealso cref="ISettingsRepository.scope"/>
public SettingsScope scope
{
get { return SettingsScope.User; }
}
/// <summary>
/// Gets the identifying name for this repository.
/// </summary>
/// <value>User settings are named "EditorPrefs".</value>
public string name
{
get { return "EditorPrefs"; }
}
/// <summary>
/// Gets the full path to the file containing the serialized settings data.
/// </summary>
/// <remarks>This property returns an empty string.</remarks>
/// <value>The location stored for this repository.</value>
/// <seealso cref="ISettingsRepository.path"/>
public string path
{
get { return string.Empty; }
}
/// <summary>
/// Saves all settings to their serialized state.
/// </summary>
/// <seealso cref="ISettingsRepository.Save"/>
public void Save()
{
}
/// <summary>
/// Sets a value for a settings entry with a matching key and type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="value">The value to set. This must be serializable.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
public void Set<T>(string key, T value)
{
SetEditorPref<T>(key, value);
}
/// <summary>
/// Returns a value for a settings entry with a matching key and type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <param name="fallback">Specify the value of type `T` to return if the entry can't be found.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <returns>The value matching both `key` and type `T`. If there was no match, this returns the `fallback` value.</returns>
public T Get<T>(string key, T fallback = default(T))
{
return GetEditorPref<T>(key, fallback);
}
/// <summary>
/// Determines whether this repository contains a settings entry that matches the specified key and is of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
/// <returns>True if a settings entry matches both `key` and type `T`; false if no entry is found.</returns>
public bool ContainsKey<T>(string key)
{
return EditorPrefs.HasKey(GetEditorPrefKey<T>(key));
}
/// <summary>
/// Removes a key-value pair from this settings repository. This method identifies the settings entry to remove
/// by matching the specified key for a value of type `T`.
/// </summary>
/// <param name="key">The key used to identify the settings entry.</param>
/// <typeparam name="T">The type of value that this key points to.</typeparam>
public void Remove<T>(string key)
{
EditorPrefs.DeleteKey(GetEditorPrefKey<T>(key));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6965211c873de4048aa9043e5e287c73
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,39 @@
using System;
using UnityEngine;
namespace UnityEditor.SettingsManagement
{
[Serializable]
sealed class ValueWrapper<T>
{
#if PRETTY_PRINT_JSON
const bool k_PrettyPrintJson = true;
#else
const bool k_PrettyPrintJson = false;
#endif
[SerializeField]
T m_Value;
public static string Serialize(T value)
{
var obj = new ValueWrapper<T>() { m_Value = value };
return EditorJsonUtility.ToJson(obj, k_PrettyPrintJson);
}
public static T Deserialize(string json)
{
var value = (object)Activator.CreateInstance<ValueWrapper<T>>();
EditorJsonUtility.FromJsonOverwrite(json, value);
return ((ValueWrapper<T>)value).m_Value;
}
public static T DeepCopy(T value)
{
if (typeof(ValueType).IsAssignableFrom(typeof(T)))
return value;
var str = Serialize(value);
return Deserialize(str);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d65bb50aabd144ed9c1a4c2bc6d072e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: