Created unity project

This commit is contained in:
2024-12-07 20:55:50 +01:00
parent 539250d964
commit 54fe327198
13758 changed files with 865324 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,587 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UnityEditor.SettingsManagement
{
static class SettingsGUIStyles
{
const string k_SettingsGearIcon = "Packages/" + UserSettings.packageName + "/Content/Options.png";
static bool s_Initialized;
public static GUIStyle s_SettingsGizmo;
public static GUIStyle s_SettingsArea;
public static GUIStyle s_IndentedSettingBlock;
static void Init()
{
if (s_Initialized)
return;
s_Initialized = true;
s_SettingsGizmo = new GUIStyle()
{
normal = new GUIStyleState()
{
background = AssetDatabase.LoadAssetAtPath<Texture2D>(k_SettingsGearIcon)
},
fixedWidth = 14,
fixedHeight = 14,
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(4, 4, 4, 4),
imagePosition = ImagePosition.ImageOnly
};
s_SettingsArea = new GUIStyle()
{
margin = new RectOffset(6, 6, 0, 0)
};
s_IndentedSettingBlock = new GUIStyle()
{
padding = new RectOffset(16, 0, 0, 0)
};
}
public static GUIStyle settingsGizmo
{
get
{
Init();
return s_SettingsGizmo;
}
}
public static GUIStyle settingsArea
{
get
{
Init();
return s_SettingsArea;
}
}
public static GUIStyle indentedSettingBlock
{
get
{
Init();
return s_IndentedSettingBlock;
}
}
}
/// <summary>
/// Provides extension methods for <see cref="UnityEngine.GUILayout"/> that also implement settings-specific functionality.
/// </summary>
public static class SettingsGUILayout
{
/// <summary>
/// Provides methods for creating and managing indented GUI sections.
/// </summary>
public class IndentedGroup : IDisposable
{
bool m_IsDisposed;
/// <summary>
/// Creates an indented GUI section.
/// </summary>
public IndentedGroup()
{
EditorGUIUtility.labelWidth -= SettingsGUIStyles.indentedSettingBlock.padding.left - 4;
GUILayout.BeginVertical(SettingsGUIStyles.indentedSettingBlock);
}
/// <summary>
/// Creates an indented GUI section with a label.
/// </summary>
/// <param name="label">The name of the section to display in the UI as a label. </param>
public IndentedGroup(string label)
{
GUILayout.Label(label);
EditorGUIUtility.labelWidth -= SettingsGUIStyles.indentedSettingBlock.padding.left - 4;
GUILayout.BeginVertical(SettingsGUIStyles.indentedSettingBlock);
}
/// <summary>
/// Reverts the GUI indent back to its original value.
/// </summary>
public void Dispose()
{
if (m_IsDisposed)
return;
m_IsDisposed = true;
GUILayout.EndVertical();
EditorGUIUtility.labelWidth += SettingsGUIStyles.indentedSettingBlock.padding.left - 4;
}
}
internal static HashSet<string> s_Keywords = null;
internal static bool MatchSearchGroups(string searchContext, string label)
{
if (s_Keywords != null)
{
foreach (var keyword in label.Split(' '))
s_Keywords.Add(keyword);
}
if (searchContext == null)
return true;
var ctx = searchContext.Trim();
if (string.IsNullOrEmpty(ctx))
return true;
var split = searchContext.Split(' ');
return split.Any(x => !string.IsNullOrEmpty(x) && label.IndexOf(x, StringComparison.InvariantCultureIgnoreCase) > -1);
}
internal static bool DebugModeFilter(IUserSetting pref)
{
if (!EditorPrefs.GetBool("DeveloperMode", false))
return true;
if (pref.scope == SettingsScope.Project && UserSettingsProvider.showProjectSettings)
return true;
if (pref.scope == SettingsScope.User && UserSettingsProvider.showUserSettings)
return true;
return false;
}
/// <summary>
/// Creates a slider that implements search filtering.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="min">The value at the left end of the slider.</param>
/// <param name="max">The value at the right end of the slider.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static float SearchableSlider(GUIContent label, float value, float min, float max, string searchContext)
{
if (!MatchSearchGroups(searchContext, label.text))
return value;
return EditorGUILayout.Slider(label, value, min, max);
}
/// <summary>
/// Creates a slider that implements search filtering.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="min">The value at the left end of the slider.</param>
/// <param name="max">The value at the right end of the slider.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static float SearchableSlider(string label, float value, float min, float max, string searchContext)
{
if (!MatchSearchGroups(searchContext, label))
return value;
return EditorGUILayout.Slider(label, value, min, max);
}
/// <summary>
/// Creates a float field that implements search filtering.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static float SearchableFloatField(GUIContent label, float value, string searchContext)
{
if (!MatchSearchGroups(searchContext, label.text))
return value;
return EditorGUILayout.FloatField(label, value);
}
/// <summary>
/// Creates a float field that implements search filtering.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static float SearchableFloatField(string label, float value, string searchContext)
{
if (!MatchSearchGroups(searchContext, label))
return value;
return EditorGUILayout.FloatField(label, value);
}
/// <summary>
/// Creates an int field that implements search filtering.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static int SearchableIntField(GUIContent label, int value, string searchContext)
{
if (!MatchSearchGroups(searchContext, label.text))
return value;
return EditorGUILayout.IntField(label, value);
}
/// <summary>
/// Creates an int field that implements search filtering.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static int SearchableIntField(string label, int value, string searchContext)
{
if (!MatchSearchGroups(searchContext, label))
return value;
return EditorGUILayout.IntField(label, value);
}
/// <summary>
/// Creates a toggle field that implements search filtering.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static bool SearchableToggle(GUIContent label, bool value, string searchContext)
{
if (!MatchSearchGroups(searchContext, label.text))
return value;
return EditorGUILayout.Toggle(label, value);
}
/// <summary>
/// Creates a toggle field that implements search filtering.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static bool SearchableToggle(string label, bool value, string searchContext)
{
if (!MatchSearchGroups(searchContext, label))
return value;
return EditorGUILayout.Toggle(label, value);
}
/// <summary>
/// Creates a text field that implements search filtering.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static string SearchableTextField(GUIContent label, string value, string searchContext)
{
if (!MatchSearchGroups(searchContext, label.text))
return value;
return EditorGUILayout.TextField(label, value);
}
/// <summary>
/// Creates a text field that implements search filtering.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static string SearchableTextField(string label, string value, string searchContext)
{
if (!MatchSearchGroups(searchContext, label))
return value;
return EditorGUILayout.TextField(label, value);
}
/// <summary>
/// Creates a color field that implements search filtering.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static Color SearchableColorField(GUIContent label, Color value, string searchContext)
{
if (!MatchSearchGroups(searchContext, label.text))
return value;
return EditorGUILayout.ColorField(label, value);
}
/// <summary>
/// Creates a color field that implements search filtering.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static Color SearchableColorField(string label, Color value, string searchContext)
{
if (!MatchSearchGroups(searchContext, label))
return value;
return EditorGUILayout.ColorField(label, value);
}
/// <summary>
/// Creates a float slider that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="min">The value at the left end of the slider.</param>
/// <param name="max">The value at the right end of the slider.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static float SettingsSlider(GUIContent label, UserSetting<float> value, float min, float max, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label.text))
return value;
var res = EditorGUILayout.Slider(label, value, min, max);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates a float slider that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="min">The value at the left end of the slider.</param>
/// <param name="max">The value at the right end of the slider.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static float SettingsSlider(string label, UserSetting<float> value, float min, float max, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label))
return value;
var res = EditorGUILayout.Slider(label, value, min, max);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates an int slider that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="min">The value at the left end of the slider.</param>
/// <param name="max">The value at the right end of the slider.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static int SettingsSlider(GUIContent label, UserSetting<int> value, int min, int max, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label.text))
return value;
var res = EditorGUILayout.IntSlider(label, value, min, max);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates an int slider that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="min">The value at the left end of the slider.</param>
/// <param name="max">The value at the right end of the slider.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static int SettingsSlider(string label, UserSetting<int> value, int min, int max, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label))
return value;
var res = EditorGUILayout.IntSlider(label, value, min, max);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates a float field that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static float SettingsFloatField(GUIContent label, UserSetting<float> value, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label.text))
return value;
var res = EditorGUILayout.FloatField(label, value);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates a float field that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static float SettingsFloatField(string label, UserSetting<float> value, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label))
return value;
var res = EditorGUILayout.FloatField(label, value);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates an integer field that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static int SettingsIntField(GUIContent label, UserSetting<int> value, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label.text))
return value;
var res = EditorGUILayout.IntField(label, value);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates an integer field that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static int SettingsIntField(string label, UserSetting<int> value, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label))
return value;
var res = EditorGUILayout.IntField(label, value);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates a boolean toggle field that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static bool SettingsToggle(GUIContent label, UserSetting<bool> value, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label.text))
return value;
var res = EditorGUILayout.Toggle(label, value);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates a boolean toggle field that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static bool SettingsToggle(string label, UserSetting<bool> value, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label))
return value;
var res = EditorGUILayout.Toggle(label, value);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates a text field that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static string SettingsTextField(GUIContent label, UserSetting<string> value, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label.text))
return value;
var res = EditorGUILayout.TextField(label, value);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates a text field that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static string SettingsTextField(string label, UserSetting<string> value, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label))
return value;
var res = EditorGUILayout.TextField(label, value);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates a color field that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a <see cref="UnityEngine.GUIContent"/> instance to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static Color SettingsColorField(GUIContent label, UserSetting<Color> value, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label.text))
return value;
var res = EditorGUILayout.ColorField(label, value);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Creates a color field that implements search filtering and a <see cref="DoResetContextMenuForLastRect">Reset</see> context menu entry.
/// </summary>
/// <param name="label">Specify a string to set the label that appears beside the value field. </param>
/// <param name="value">The value to edit.</param>
/// <param name="searchContext">A string representing the current search query. Empty or null strings match any value.</param>
/// <returns>The value set by the user.</returns>
public static Color SettingsColorField(string label, UserSetting<Color> value, string searchContext)
{
if (!DebugModeFilter(value) || !MatchSearchGroups(searchContext, label))
return value;
var res = EditorGUILayout.ColorField(label, value);
DoResetContextMenuForLastRect(value);
return res;
}
/// <summary>
/// Implements a "Reset" context menu entry for the specified user setting.
/// using the rect from the last automatic layout.
/// </summary>
/// <param name="setting">The target setting for the "Reset" context menu entry.</param>
public static void DoResetContextMenuForLastRect(IUserSetting setting)
{
DoResetContextMenu(GUILayoutUtility.GetLastRect(), setting);
}
static void DoResetContextMenu(Rect rect, IUserSetting pref)
{
var evt = Event.current;
if (evt.type == EventType.ContextClick && rect.Contains(evt.mousePosition))
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("Reset [" + pref.scope + "] " + pref.key), false, () =>
{
pref.Reset(true);
});
menu.ShowAsContext();
}
}
}
}

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,620 @@
#if UNITY_2018_3_OR_NEWER
#define SETTINGS_PROVIDER_ENABLED
#endif
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
#if SETTINGS_PROVIDER_ENABLED
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
#endif
#endif
namespace UnityEditor.SettingsManagement
{
/// <summary>
/// A <see cref="UnityEditor.SettingsProvider"/> implementation that creates an interface from settings reflected
/// from a collection of assemblies.
/// </summary>
#if SETTINGS_PROVIDER_ENABLED
public sealed class UserSettingsProvider : SettingsProvider
#else
public sealed class UserSettingsProvider
#endif
{
/// <summary>
/// Category string constant to check whether Unity is running in developer (internal) mode.
/// </summary>
public const string developerModeCategory = "Developer Mode";
const string k_SettingsName = "UserSettingsProviderSettings";
#if SETTINGS_PROVIDER_ENABLED
const int k_LabelWidth = 240;
static int labelWidth
{
get
{
if (s_DefaultLabelWidth != null)
return (int)((float)s_DefaultLabelWidth.GetValue(null, null));
return k_LabelWidth;
}
}
static int defaultLayoutMaxWidth
{
get
{
if (s_DefaultLayoutMaxWidth != null)
return (int)((float)s_DefaultLayoutMaxWidth.GetValue(null, null));
return 0;
}
}
#else
const int k_LabelWidth = 180;
int labelWidth
{
get { return k_LabelWidth; }
}
int defaultLayoutMaxWidth
{
get { return 0; }
}
#endif
List<string> m_Categories;
Dictionary<string, List<PrefEntry>> m_Settings;
Dictionary<string, List<MethodInfo>> m_SettingBlocks;
#if !SETTINGS_PROVIDER_ENABLED
HashSet<string> keywords = new HashSet<string>();
#endif
static readonly string[] s_SearchContext = new string[1];
EventType m_SettingsBlockKeywordsInitialized;
Assembly[] m_Assemblies;
static Settings s_Settings;
Settings m_SettingsInstance;
#if SETTINGS_PROVIDER_ENABLED
static PropertyInfo s_DefaultLabelWidth;
static PropertyInfo s_DefaultLayoutMaxWidth;
#endif
static Settings userSettingsProviderSettings
{
get
{
if (s_Settings == null)
s_Settings = new Settings(new[] { new UserSettingsRepository() });
return s_Settings;
}
}
internal static UserSetting<bool> showHiddenSettings = new UserSetting<bool>(userSettingsProviderSettings, "settings.showHidden", false, SettingsScope.User);
internal static UserSetting<bool> showUnregisteredSettings = new UserSetting<bool>(userSettingsProviderSettings, "settings.showUnregistered", false, SettingsScope.User);
internal static UserSetting<bool> listByKey = new UserSetting<bool>(userSettingsProviderSettings, "settings.listByKey", false, SettingsScope.User);
internal static UserSetting<bool> showUserSettings = new UserSetting<bool>(userSettingsProviderSettings, "settings.showUserSettings", true, SettingsScope.User);
internal static UserSetting<bool> showProjectSettings = new UserSetting<bool>(userSettingsProviderSettings, "settings.showProjectSettings", true, SettingsScope.User);
#if SETTINGS_PROVIDER_ENABLED
/// <summary>
/// Initializes and returns a new `UserSettingsProvider` instance.
/// </summary>
/// <param name="path">The settings menu path.</param>
/// <param name="settings">The Settings instance that this provider is inspecting.</param>
/// <param name="assemblies">A collection of assemblies to scan for <see cref="UserSettingAttribute"/> and <see cref="UserSettingBlockAttribute"/> attributes.</param>
/// <param name="scopes">Which scopes this provider is valid for.</param>
/// <exception cref="ArgumentNullException">Thrown if settings or assemblies is null.</exception>
public UserSettingsProvider(string path, Settings settings, Assembly[] assemblies, SettingsScope scopes = SettingsScope.User)
: base(path, scopes)
#else
/// <summary>
/// Initializes and returns a new `UserSettingsProvider` instance.
/// </summary>
/// <param name="settings">The <see cref="Settings"/> instance that this provider is inspecting.</param>
/// <param name="assemblies">A collection of assemblies to scan for <see cref="UserSettingAttribute"/> and <see cref="UserSettingBlockAttribute"/> attributes.</param>
public UserSettingsProvider(Settings settings, Assembly[] assemblies)
#endif
{
if (settings == null)
throw new ArgumentNullException("settings");
if (assemblies == null)
throw new ArgumentNullException("assemblies");
m_SettingsInstance = settings;
m_Assemblies = assemblies;
#if !SETTINGS_PROVIDER_ENABLED
SearchForUserSettingAttributes();
#endif
}
#if SETTINGS_PROVIDER_ENABLED
/// <summary>
/// Invoked by the <see cref="UnityEditor.SettingsProvider"/> when activated in the Editor.
/// </summary>
/// <param name="searchContext">
/// Search context in the search box on the
/// [Settings](https://docs.unity3d.com/Manual/comp-ManagerGroup.html) window.
/// </param>
/// <param name="rootElement">
/// Root of the UIElements tree. If you add to this root, the SettingsProvider uses
/// [UIElements](https://docs.unity3d.com/ScriptReference/UnityEngine.UIElementsModule.html)
/// instead of calling <see cref="OnGUI"/> to build the UI.
/// See <see cref="UnityEditor.SettingsProvider.OnActivate"/> for details.
/// </param>
public override void OnActivate(string searchContext, VisualElement rootElement)
{
base.OnActivate(searchContext, rootElement);
SearchForUserSettingAttributes();
var window = GetType().GetProperty("settingsWindow", BindingFlags.Instance | BindingFlags.NonPublic);
if (window != null)
{
s_DefaultLabelWidth = window.PropertyType.GetProperty("s_DefaultLabelWidth", BindingFlags.Public | BindingFlags.Static);
s_DefaultLayoutMaxWidth = window.PropertyType.GetProperty("s_DefaultLayoutMaxWidth", BindingFlags.Public | BindingFlags.Static);
}
}
#endif
struct PrefEntry
{
GUIContent m_Content;
IUserSetting m_Pref;
public GUIContent content
{
get { return m_Content; }
}
public IUserSetting pref
{
get { return m_Pref; }
}
public PrefEntry(GUIContent content, IUserSetting pref)
{
m_Content = content;
m_Pref = pref;
}
}
void SearchForUserSettingAttributes()
{
var isDeveloperMode = EditorPrefs.GetBool("DeveloperMode", false);
var keywordsHash = new HashSet<string>();
if (m_Settings != null)
m_Settings.Clear();
else
m_Settings = new Dictionary<string, List<PrefEntry>>();
if (m_SettingBlocks != null)
m_SettingBlocks.Clear();
else
m_SettingBlocks = new Dictionary<string, List<MethodInfo>>();
var types = m_Assemblies.SelectMany(x => x.GetTypes());
// collect instance fields/methods too, but only so we can throw a warning that they're invalid.
var fields = types.SelectMany(x =>
x.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
.Where(prop => Attribute.IsDefined(prop, typeof(UserSettingAttribute))));
var methods = types.SelectMany(x => x.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
.Where(y => Attribute.IsDefined(y, typeof(UserSettingBlockAttribute))));
foreach (var field in fields)
{
if (!field.IsStatic)
{
Debug.LogWarning("Cannot create setting entries for instance fields. Skipping \"" + field.Name + "\".");
continue;
}
var attrib = (UserSettingAttribute)Attribute.GetCustomAttribute(field, typeof(UserSettingAttribute));
if (!attrib.visibleInSettingsProvider)
continue;
var pref = (IUserSetting)field.GetValue(null);
if (pref == null)
{
Debug.LogWarning("[UserSettingAttribute] is only valid for types implementing the IUserSetting interface. Skipping \"" + field.Name + "\"");
continue;
}
var category = string.IsNullOrEmpty(attrib.category) ? "Uncategorized" : attrib.category;
var content = listByKey ? new GUIContent(pref.key) : attrib.title;
if (developerModeCategory.Equals(category) && !isDeveloperMode)
continue;
List<PrefEntry> settings;
if (m_Settings.TryGetValue(category, out settings))
settings.Add(new PrefEntry(content, pref));
else
m_Settings.Add(category, new List<PrefEntry>() { new PrefEntry(content, pref) });
}
foreach (var method in methods)
{
var attrib = (UserSettingBlockAttribute)Attribute.GetCustomAttribute(method, typeof(UserSettingBlockAttribute));
var category = string.IsNullOrEmpty(attrib.category) ? "Uncategorized" : attrib.category;
if (developerModeCategory.Equals(category) && !isDeveloperMode)
continue;
List<MethodInfo> blocks;
var parameters = method.GetParameters();
if (!method.IsStatic || parameters.Length < 1 || parameters[0].ParameterType != typeof(string))
{
Debug.LogWarning("[UserSettingBlockAttribute] is only valid for static functions with a single string parameter. Ex, `static void MySettings(string searchContext)`. Skipping \"" + method.Name + "\"");
continue;
}
if (m_SettingBlocks.TryGetValue(category, out blocks))
blocks.Add(method);
else
m_SettingBlocks.Add(category, new List<MethodInfo>() { method });
}
if (showHiddenSettings)
{
var unlisted = new List<PrefEntry>();
m_Settings.Add("Unlisted", unlisted);
foreach (var pref in UserSettings.FindUserSettings(m_Assemblies, SettingVisibility.Unlisted | SettingVisibility.Hidden))
unlisted.Add(new PrefEntry(new GUIContent(pref.key), pref));
}
if (showUnregisteredSettings)
{
var unregistered = new List<PrefEntry>();
m_Settings.Add("Unregistered", unregistered);
foreach (var pref in UserSettings.FindUserSettings(m_Assemblies, SettingVisibility.Unregistered))
unregistered.Add(new PrefEntry(new GUIContent(pref.key), pref));
}
foreach (var cat in m_Settings)
{
foreach (var entry in cat.Value)
{
var content = entry.content;
if (content != null && !string.IsNullOrEmpty(content.text))
{
foreach (var word in content.text.Split(' '))
keywordsHash.Add(word);
}
}
}
keywords = keywordsHash;
m_Categories = m_Settings.Keys.Union(m_SettingBlocks.Keys).ToList();
m_Categories.Sort();
}
#if SETTINGS_PROVIDER_ENABLED
/// <summary>
/// Invoked by the SettingsProvider container when drawing the UI header.
/// </summary>
public override void OnTitleBarGUI()
{
if (GUILayout.Button(GUIContent.none, SettingsGUIStyles.settingsGizmo))
DoContextMenu();
}
#endif
void InitSettingsBlockKeywords()
{
// Have to let the blocks run twice - one for Layout, one for Repaint.
if (m_SettingsBlockKeywordsInitialized == EventType.Repaint)
return;
m_SettingsBlockKeywordsInitialized = Event.current.type;
// Allows SettingsGUILayout.SettingsField to populate keywords
SettingsGUILayout.s_Keywords = new HashSet<string>(keywords);
// Set a dummy value so that GUI blocks with conditional foldouts will behave as though searching.
s_SearchContext[0] = "Search";
foreach (var category in m_SettingBlocks)
{
foreach (var block in category.Value)
block.Invoke(null, s_SearchContext);
}
keywords = SettingsGUILayout.s_Keywords;
SettingsGUILayout.s_Keywords = null;
s_SearchContext[0] = "";
}
void DoContextMenu()
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("Reset All"), false, () =>
{
if (!UnityEditor.EditorUtility.DisplayDialog("Reset All Settings", "Reset all settings? This is not undo-able.", "Reset", "Cancel"))
return;
// Do not reset SettingVisibility.Unregistered
foreach (var pref in UserSettings.FindUserSettings(m_Assemblies, SettingVisibility.Visible | SettingVisibility.Hidden | SettingVisibility.Unlisted))
pref.Reset();
m_SettingsInstance.Save();
});
if (EditorPrefs.GetBool("DeveloperMode", false))
{
menu.AddSeparator("");
menu.AddItem(new GUIContent("Developer/List Settings By Key"), listByKey, () =>
{
listByKey.SetValue(!listByKey, true);
SearchForUserSettingAttributes();
});
menu.AddSeparator("Developer/");
menu.AddItem(new GUIContent("Developer/Show User Settings"), showUserSettings, () =>
{
showUserSettings.SetValue(!showUserSettings, true);
SearchForUserSettingAttributes();
});
menu.AddItem(new GUIContent("Developer/Show Project Settings"), showProjectSettings, () =>
{
showProjectSettings.SetValue(!showProjectSettings, true);
SearchForUserSettingAttributes();
});
menu.AddSeparator("Developer/");
menu.AddItem(new GUIContent("Developer/Show Unlisted Settings"), showHiddenSettings, () =>
{
showHiddenSettings.SetValue(!showHiddenSettings, true);
SearchForUserSettingAttributes();
});
menu.AddItem(new GUIContent("Developer/Show Unregistered Settings"), showUnregisteredSettings, () =>
{
showUnregisteredSettings.SetValue(!showUnregisteredSettings, true);
SearchForUserSettingAttributes();
});
menu.AddSeparator("Developer/");
menu.AddItem(new GUIContent("Developer/Open Project Settings File"), false, () =>
{
var project = m_SettingsInstance.GetRepository(SettingsScope.Project);
if (project != null)
{
var path = Path.GetFullPath(project.path);
System.Diagnostics.Process.Start(path);
}
});
menu.AddItem(new GUIContent("Developer/Print All Settings"), false, () =>
{
Debug.Log(UserSettings.GetSettingsString(m_Assemblies));
});
#if UNITY_2019_1_OR_NEWER
menu.AddSeparator("Developer/");
#if UNITY_2019_3_OR_NEWER
menu.AddItem(new GUIContent("Developer/Recompile Scripts"), false, EditorUtility.RequestScriptReload);
#else
menu.AddItem(new GUIContent("Developer/Recompile Scripts"), false, UnityEditorInternal.InternalEditorUtility.RequestScriptReload);
#endif
#endif
}
menu.ShowAsContext();
}
#if SETTINGS_PROVIDER_ENABLED
/// <summary>
/// Called when the Settings window opens in the Editor.
/// </summary>
/// <param name="searchContext">
/// Search context in the search box on the
/// [Settings](https://docs.unity3d.com/Manual/comp-ManagerGroup.html) window.
/// </param>
/// <seealso cref="UnityEditor.SettingsProvider.OnGUI"/>
public override void OnGUI(string searchContext)
#else
/// <summary>
/// Called when the Settings window opens in the Editor.
/// </summary>
/// <param name="searchContext">
/// Search context in the search box on the
/// [Settings](https://docs.unity3d.com/Manual/comp-ManagerGroup.html) window.
/// </param>
/// <seealso cref="UnityEditor.SettingsProvider.OnGUI"/>
public void OnGUI(string searchContext)
#endif
{
#if !SETTINGS_PROVIDER_ENABLED
var evt = Event.current;
if (evt.type == EventType.ContextClick)
DoContextMenu();
#endif
InitSettingsBlockKeywords();
EditorGUIUtility.labelWidth = labelWidth;
EditorGUI.BeginChangeCheck();
var maxWidth = defaultLayoutMaxWidth;
if (maxWidth != 0)
GUILayout.BeginVertical(SettingsGUIStyles.settingsArea, GUILayout.MaxWidth(maxWidth));
else
GUILayout.BeginVertical(SettingsGUIStyles.settingsArea);
var hasSearchContext = !string.IsNullOrEmpty(searchContext);
s_SearchContext[0] = searchContext;
if (hasSearchContext)
{
// todo - Improve search comparison
var searchKeywords = searchContext.Split(' ');
foreach (var settingField in m_Settings)
{
foreach (var setting in settingField.Value)
{
if (searchKeywords.Any(x => !string.IsNullOrEmpty(x) && setting.content.text.IndexOf(x, StringComparison.InvariantCultureIgnoreCase) > -1))
DoPreferenceField(setting.content, setting.pref);
}
}
foreach (var settingsBlock in m_SettingBlocks)
{
foreach (var block in settingsBlock.Value)
{
block.Invoke(null, s_SearchContext);
}
}
}
else
{
foreach (var key in m_Categories)
{
GUILayout.Label(key, EditorStyles.boldLabel);
List<PrefEntry> settings;
if (m_Settings.TryGetValue(key, out settings))
foreach (var setting in settings)
DoPreferenceField(setting.content, setting.pref);
List<MethodInfo> blocks;
if (m_SettingBlocks.TryGetValue(key, out blocks))
foreach (var block in blocks)
block.Invoke(null, s_SearchContext);
GUILayout.Space(8);
}
}
EditorGUIUtility.labelWidth = 0;
GUILayout.EndVertical();
if (EditorGUI.EndChangeCheck())
{
m_SettingsInstance.Save();
}
}
void DoPreferenceField(GUIContent title, IUserSetting pref)
{
if (EditorPrefs.GetBool("DeveloperMode", false))
{
if (pref.scope == SettingsScope.Project && !showProjectSettings)
return;
if (pref.scope == SettingsScope.User && !showUserSettings)
return;
}
if (pref is UserSetting<float>)
{
var cast = (UserSetting<float>)pref;
cast.value = EditorGUILayout.FloatField(title, cast.value);
}
else if (pref is UserSetting<int>)
{
var cast = (UserSetting<int>)pref;
cast.value = EditorGUILayout.IntField(title, cast.value);
}
else if (pref is UserSetting<bool>)
{
var cast = (UserSetting<bool>)pref;
cast.value = EditorGUILayout.Toggle(title, cast.value);
}
else if (pref is UserSetting<string>)
{
var cast = (UserSetting<string>)pref;
cast.value = EditorGUILayout.TextField(title, cast.value);
}
else if (pref is UserSetting<Color>)
{
var cast = (UserSetting<Color>)pref;
cast.value = EditorGUILayout.ColorField(title, cast.value);
}
#if UNITY_2018_3_OR_NEWER
else if (pref is UserSetting<Gradient>)
{
var cast = (UserSetting<Gradient>)pref;
cast.value = EditorGUILayout.GradientField(title, cast.value);
}
#endif
else if (pref is UserSetting<Vector2>)
{
var cast = (UserSetting<Vector2>)pref;
cast.value = EditorGUILayout.Vector2Field(title, cast.value);
}
else if (pref is UserSetting<Vector3>)
{
var cast = (UserSetting<Vector3>)pref;
cast.value = EditorGUILayout.Vector3Field(title, cast.value);
}
else if (pref is UserSetting<Vector4>)
{
var cast = (UserSetting<Vector4>)pref;
cast.value = EditorGUILayout.Vector4Field(title, cast.value);
}
else if (typeof(Enum).IsAssignableFrom(pref.type))
{
Enum val = (Enum)pref.GetValue();
EditorGUI.BeginChangeCheck();
if (Attribute.IsDefined(pref.type, typeof(FlagsAttribute)))
val = EditorGUILayout.EnumFlagsField(title, val);
else
val = EditorGUILayout.EnumPopup(title, val);
if (EditorGUI.EndChangeCheck())
pref.SetValue(val);
}
else if (typeof(UnityEngine.Object).IsAssignableFrom(pref.type))
{
var obj = (UnityEngine.Object)pref.GetValue();
EditorGUI.BeginChangeCheck();
obj = EditorGUILayout.ObjectField(title, obj, pref.type, false);
if (EditorGUI.EndChangeCheck())
pref.SetValue(obj);
}
else
{
GUILayout.BeginHorizontal();
GUILayout.Label(title, GUILayout.Width(EditorGUIUtility.labelWidth - EditorStyles.label.margin.right * 2));
var obj = pref.GetValue();
GUILayout.Label(obj == null ? "null" : pref.GetValue().ToString());
GUILayout.EndHorizontal();
}
SettingsGUILayout.DoResetContextMenuForLastRect(pref);
}
}
}

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: