This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
{
"timestamp": 1635869217,
"signature": "LMFxOqiU0KCf0EHCGqOI/E2aL55VgxvepFLUjNHhBaGvugAbF/OVpr4EZ3rkSV3Vn6gmCkpDfkW6383edGYvq7ERI9M3XyjYDHlylwF0uiSR3O8pV4KXIhUwz8NvzXk8NAMyneESQuTXnM871sBU9qHMgbbfymUBKqwclGd+eaj0IGVe+ZZkMkUKDKujoyEtFdnc0ZBBeQ+lONSsrUtFaVwcEzY2SDAx3vuEw6z5UfiKOiZGj8GZYhdY9H8afBGzL62y/clHJgQTqxvv3yBcAp5B9q+bus+vo2TA1QGavHZEYXwkP2TpVvPKDeqIOPv5sICwkeLLiVB9huJuu1obPsad6ByZJsAF2puwb73xdhwLc59TQ/P0RK/VonGkCl8Rj+jN03b7+nTjlxJxdYXq94qMFpKIrfeIEs0FzF2tJNFKpCdlKVddtrInk349A5pLc1eioFtxqSNAFImavqwDNrzALRt5Oq5+IBSZ/Q40+eAdOikn1UK7xUDPaBumVog0",
"publicKey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQm9qQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FZOEFNSUlCaWdLQ0FZRUFzdUhXYUhsZ0I1cVF4ZEJjTlJKSAordHR4SmoxcVY1NTdvMlZaRE1XaXhYRVBkRTBEMVFkT1JIRXNSS1RscmplUXlERU83ZlNQS0ZwZ1A3MU5TTnJCCkFHM2NFSU45aHNQVDhOVmllZmdWem5QTkVMenFkVmdEbFhpb2VpUnV6OERKWFgvblpmU1JWKytwbk9ySTRibG4KS0twelJlNW14OTc1SjhxZ1FvRktKT0NNRlpHdkJMR2MxSzZZaEIzOHJFODZCZzgzbUovWjBEYkVmQjBxZm13cgo2ZDVFUXFsd0E5Y3JZT1YyV1VpWXprSnBLNmJZNzRZNmM1TmpBcEFKeGNiaTFOaDlRVEhUcU44N0ZtMDF0R1ZwCjVNd1pXSWZuYVRUemEvTGZLelR5U0pka0tldEZMVGdkYXpMYlpzUEE2aHBSK0FJRTJhc0tLTi84UUk1N3UzU2cKL2xyMnZKS1IvU2l5eEN1Q20vQWJkYnJMbXk0WjlSdm1jMGdpclA4T0lLQWxBRWZ2TzV5Z2hSKy8vd1RpTFlzUQp1SllDM0V2UE16ZGdKUzdGR2FscnFLZzlPTCsxVzROY05yNWdveVdSUUJ0cktKaWlTZEJVWmVxb0RvSUY5NHpCCndGbzJJT1JFdXFqcU51M3diMWZIM3p1dGdtalFra3IxVjJhd3hmcExLWlROQWdNQkFBRT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg"
}

View File

@@ -0,0 +1,83 @@
# Changelog
All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [2.0.1] - 2021-11-02
### Bug Fixes
- [case:1289586] Fix performance problem when using Perforce and Polybrush due to AssetDatabase.IsOpenForEdit.
- [case:1347042] Fix performance problem when using Perforce and Polybrush due to AssetDatabase.IsOpenForEdit.
## [1.0.3] - 2020-06-21
### Bug Fixes
- Fixed `PackageSettingsRepository` dirtying the settings file when no changes are present.
## [1.0.2] - 2020-02-26
### Bug Fixes
- Fixed obsolete API use in Unity 2019.3.
### Changes
- Update Yamato configuration.
## [1.0.1] - 2019-11-25
### Changes
- Make sure version control integration grants write access before trying to save package settings.
### Bug Fixes
- Fixed samples not compiling with Unity 2019.3.
- Fix package settings repo potentially initializing with a null dictionary.
## [1.0.0] - 2019-04-03
### Bug Fixes
- Fixed compile errors on Unity 2018.4.
## [0.1.0-preview.8] - 2019-03-29
### Features
- Support saving multiple settings repositories within a project
### Changes
- Rename `ProjectSettingsRepository` -> `PackageSettingsRepository`.
- Update readme with a complete code example.
- Add additional documentation and unit tests.
- Setting repositories now have names.
### Bug Fixes
- Fixed missing gear icon in Settings Provider implementation.
## [0.1.0-preview.4] - 2019-02-28
- Package configuration update.
## [0.1.0-preview.3] - 2019-02-27
- Small code update in sample.
## [0.1.0-preview.2] - 2019-02-22
- Rebuild meta files.
## [0.1.0-preview.1] - 2019-02-01
- Move samples outside of main package.
## [0.1.0-preview.0] - 2018-10-08
This is the first release of *Unity Package Settings Manager*.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4774b2f29b63d2844915d86afff02b47
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3d9bd4cbe22e8472781b1cde707d154d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,115 @@
fileFormatVersion: 2
guid: 9eff477e4332346818c96745043dae9e
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
apiRules:
- exclude:
hasAttribute:
uid: System.ObsoleteAttribute
type: Member
- exclude:
hasAttribute:
uid: System.ObsoleteAttribute
type: Type
- exclude:
uidRegex: Tests(.Framework)$
type: Namespace
- exclude:
uidRegex: ^Global Namespace.*
type: Namespace
- exclude:
uidRegex: EditorTests*
type: Namespace

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

View File

@@ -0,0 +1,117 @@
# Settings Manager
The Settings Manager is a framework that lets you convert any serializable field into a setting, including a pre-built settings interface.
## Installation
To install this package, follow the instructions in the [Package Manager documentation](https://docs.unity3d.com/Manual/upm-ui-install.html).
This package provides a sample that demonstrates how to implement custom user settings. To install them, follow these instructions:
1. Make sure the Settings Manager package is installed in your Unity project.
2. In the Package Manager window, locate the Settings Manager package select it from the list.
The [Details view](https://docs.unity3d.com/Manual/upm-ui-details.html) displays information about the Settings Manager package.
3. From the Details view, click the **Import** button under the **Samples** section.
## Requirements
This version of the Settings Manager package is compatible with the following versions of the Unity Editor:
* 2018.4 and later
## Using the Settings Manager
The [Settings](xref:UnityEditor.SettingsManagement.Settings) class is responsible for setting and retrieving serialized values from a settings repository.
Use settings repositories to save and load settings for a specific scope. This package provides two settings repositories:
* The [UserSettingsRepository](xref:UnityEditor.SettingsManagement.UserSettingsRepository), backed by the [EditorPrefs](xref:UnityEditor.EditorPrefs) class, lets you save [user preferences](https://docs.unity3d.com/Manual/Preferences.html).
* The [FileSettingsRepository](xref:UnityEditor.SettingsManagement.FileSettingsRepository) saves a JSON file to the `ProjectSettings` directory in order to save [project settings](https://docs.unity3d.com/Manual/comp-ManagerGroup.html).
You can create and manage all settings from a singleton `Settings` instance. For example:
```c#
using UnityEditor.SettingsManagement;
namespace UnityEditor.SettingsManagement.Examples
{
static class MySettingsManager
{
internal const string k_PackageName = "com.example.my-settings-example";
static Settings s_Instance;
internal static Settings instance
{
get
{
if (s_Instance == null)
s_Instance = new Settings(k_PackageName);
return s_Instance;
}
}
}
}
```
### Getting and setting values
Your `Settings` instance should implement generic methods to set and retrieve values:
```
MySettingsManager.instance.Get<float>("myFloatValue", SettingsScope.Project);
```
There are two arguments: key, and scope. The [Settings](xref:UnityEditor.SettingsManagement.Settings) class finds an appropriate [ISettingsRepository](xref:UnityEditor.SettingsManagement.ISettingsRepository) for the scope, while `key` and `T` are used to find the value. Keys are unique among types: you can re-use keys as long as its type is different.
Alternatively, you can use the [UserSetting&lt;T&gt;](xref:UnityEditor.SettingsManagement.UserSetting`1) class to manage settings. This is a wrapper class around the `Settings` get/set properties, which makes it easy to make any field a saved setting.
```c#
// UserSetting<T>(Settings instance, string key, T defaultValue, SettingsScope scope = SettingsScope.Project)
Setting<int> myIntValue = new Setting<int>(MySettingsManager.instance, "int.key", 42, SettingsScope.User);
```
[UserSetting&lt;T&gt;](xref:UnityEditor.SettingsManagement.UserSetting`1) caches the current value, and keeps a copy of the default value so that it may be reset. You can also use `UserSetting<T>` fields with the `[UserSettingAttribute]` attribute, which lets the `SettingsManagerProvider` automatically add it to a settings inspector.
## Settings Provider
To register your settings so they appear in the [Project Settings](https://docs.unity3d.com/Manual/comp-ManagerGroup.html) window, you can either write your own [SettingsProvider](xref:UnityEditor.SettingsProvider) implementation, or use the [UserSettingsProvider](xref:UnityEditor.SettingsManagement.UserSettingsProvider) and let it automatically create your interface.
Making use of `UserSettingsProvider` comes with many benefits, including a uniform look for your settings UI, support for search, and per-field or mass reset support.
```
using UnityEngine;
namespace UnityEditor.SettingsManagement.Examples
{
static class MySettingsProvider
{
const string k_PreferencesPath = "Preferences/My Settings";
[SettingsProvider]
static SettingsProvider CreateSettingsProvider()
{
// The last parameter tells the provider where to search for settings.
var provider = new SettingsManagerProvider(k_PreferencesPath,
MySettingsManager.instance,
new [] { typeof(MySettingsProvider).Assembly });
return provider;
}
}
}
```
To register a field with the [UserSettingsProvider](xref:UnityEditor.SettingsManagement.UserSettingsProvider), decorate it with `[UserSettingAttribute(string displayCategory, string key)]`.
> [!NOTE]
> The `[UserSettingAttribute]` decoration is only valid for static fields.
For more complex settings that require additional UI (or that don't have a built-in Editor), use [UserSettingBlockAttribute](xref:UnityEditor.SettingsManagement.UserSettingBlockAttribute) to access the settings provider GUI. For more information, look at the sample source file `SettingsExamples.cs` under the `Assets/Samples/Settings Manager/<version>/User Settings Example/PackageWithProjectAndUserSettings` folder in your Unity project root.
> [!TIP]
> If you don't see this path or file, follow the steps under the Installation section to import it.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c2910bfb37c064309a7230f4175d6c3a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
com.unity.settings-manager copyright © 2021 Unity Technologies ApS
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License).
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 27264456a8bbc094d9479c5d5f9b0aa5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,108 @@
# Settings Manager
A framework for making any serializable field a setting, complete with an procedurally populated Settings Provider.
![in action](Documentation~/images/settings.gif)
## Quick Start
Settings are saved in `ISettingsRepository` instances. Two default implementations are provided, one for saving user settings (`UserSettingsRepository`) and one for per-project settings (`ProjectSettingsRepository`). Settings repositories are responsible for saving and loading preferences.
You may work directly with `ISettingsRepository`, or create a `Settings` instance to manage them. Creating a `Settings` is convenient because it allows you to make use of the `UserSetting` class and attribute.
The most common case will be for packages to create a `Settings` manager with two repositories, one for user settings (`SettingsScope.User`) and one for per-project settings (`SettingsScope.Project`).
Below is an example of how most packages will use this api.
```
using UnityEditor;
using UnityEditor.SettingsManagement;
using UnityEngine;
public class MySettings
{
static Settings s_SettingsInstance;
public static Settings instance
{
get
{
if(s_SettingsInstance == null)
s_SettingsInstance = new Settings("com.unity.my-package");
return s_SettingsInstance;
}
}
// Register a new SettingsProvider that will scrape the owning assembly for [UserSetting] marked fields.
[SettingsProvider]
static SettingsProvider CreateSettingsProvider()
{
var provider = new UserSettingsProvider("Preferences/My Settings",
instance,
new [] { typeof(MySettings).Assembly });
return provider;
}
}
public class Test
{
[UserSetting("User Settings", "My User Int Value")]
static UserSetting<int> userSetting = new UserSetting<int>(MySettings.instance, "MyInteger", 42, SettingsScope.User);
[UserSetting("Project Settings", "My Project Int Value")]
static UserSetting<int> projectSetting = new UserSetting<int>(MySettings.instance, "MyInteger", 42, SettingsScope.Project);
[MenuItem("Debug/Print Settings Values")]
static void PrintValues()
{
Debug.Log($"User integer is: {(int) userSetting}, and project integer is {(int) projectSetting}");
}
}
```
Values are set and retrieved using generic methods on on your `Settings` instance:
```
MySettingsManager.instance.Get<float>("myFloatValue", SettingsScopes.Project);
```
The `Settings` class will handle finding an appropriate `ISettingsRepository` for the scope (and optional repository name), while `key` and `T` are used to find the value. Setting keys are unique among types, meaning you may re-use keys as long as the setting type is different.
```
// UserSetting<T> is a wrapper class that handles saving and loading serializable values. It is compatible with the `[UserSetting]` attribute, which is used to automatically populate a settings provider.
UserSetting<int> myIntValue = new UserSetting<int>(MySettingsManager.instance, "MyIntegerKey", 42, SettingsScopes.User);
```
`UserSetting<T>` caches the current value, and keeps a copy of the default value so that it may be reset. `UserSetting<T>` fields are also eligible for use with the `[UserSetting]` attribute, which lets the `UserSettingsProvider` automatically add it to a settings inspector.
## Settings Provider
To register your settings in the `Settings Window` you can either write your own `SettingsProvider` implementation, or use the provided `UserSettingsProvider` and let it automatically create your interface.
Making use of `UserSettingsProvider` comes with many benefits, including a uniform look for your settings UI, support for search, and per-field or mass reset support.
```
using UnityEngine;
namespace UnityEditor.SettingsManagement.Examples
{
static class MySettingsProvider
{
[SettingsProvider]
static SettingsProvider CreateSettingsProvider()
{
var provider = new UserSettingsProvider("Preferences/My Settings",
MySettingsManager.instance,
new [] { typeof(MySettingsProvider).Assembly });
return provider;
}
}
}
```
To register a field with `UserSettingsProvider`, simply decorate it with `[UserSetting(string displayCategory, string key)]`. `[SettingAttribute]` is only valid for static fields.
For more complex settings that require additional UI (or simply don't have a built-in editor), you can use `UserSettingBlockAttribute`. This provides access to the settings provider GUI. See `SettingsExamples.cs` for more on this.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 561e43493ab773746acefbe0f624795e
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,5 @@
{
"displayName":"User Settings Example",
"description": "Example code showing use of the Settings Manager attributes.",
"createSeparatePackage": false
}

View File

@@ -0,0 +1,94 @@
using UnityEditor.EditorTools;
using UnityEngine;
namespace UnityEditor.SettingsManagement.Examples
{
/// <summary>
/// This example shows how to access multiple project setting repositories without making use of
/// <see cref="UserSetting{T}"/>.
/// </summary>
[EditorTool("Editor Tool Settings Example")]
class PerPlatformSettingsTool : EditorTool
{
#if !UNITY_2019_2_OR_NEWER
public override GUIContent toolbarIcon
{
get { return new GUIContent("Settings Example Tool", "Settings Manager Example Tool"); }
}
#endif
// This example creates two project settings repositories, A and B.
static readonly string[] k_ProjectRepositories = new[]
{
"Settings A",
"Settings B"
};
// The settings manager.
static Settings s_Settings;
// This is the key that is used to store the color setting.
const string k_ToolColorSetting = "ToolColor";
// Current tool color
Color m_ToolColor;
// The repository that color is read from and written to.
int m_Repository;
Vector3 m_HandlePosition;
// Get the color value from a repository, setting a default value if the key does not already exist. This is
// handled for you if using UserSetting{T}.
Color GetToolColor(string repository, Color defaultColor)
{
if (!s_Settings.ContainsKey<Color>(k_ToolColorSetting, repository))
s_Settings.Set<Color>(k_ToolColorSetting, defaultColor, repository);
return s_Settings.Get<Color>(k_ToolColorSetting, k_ProjectRepositories[m_Repository]);
}
void OnEnable()
{
s_Settings = new Settings(new ISettingsRepository[]
{
new UserSettingsRepository(),
new PackageSettingsRepository("com.unity.settings-manager-examples", k_ProjectRepositories[0]),
new PackageSettingsRepository("com.unity.settings-manager-examples", k_ProjectRepositories[1])
});
m_Repository = s_Settings.Get<int>("ToolColorRepositoryName", SettingsScope.User);
m_ToolColor = GetToolColor(k_ProjectRepositories[m_Repository], Color.blue);
}
public override void OnToolGUI(EditorWindow window)
{
Handles.BeginGUI();
GUILayout.BeginVertical(GUILayout.MaxWidth(300));
EditorGUI.BeginChangeCheck();
m_Repository = EditorGUILayout.IntPopup(m_Repository, k_ProjectRepositories, new int[] { 0, 1 });
if (EditorGUI.EndChangeCheck())
m_ToolColor = GetToolColor(k_ProjectRepositories[m_Repository], Color.blue);
EditorGUI.BeginChangeCheck();
m_ToolColor = EditorGUILayout.ColorField(m_ToolColor);
if (EditorGUI.EndChangeCheck())
{
s_Settings.Set<Color>(k_ToolColorSetting, m_ToolColor, k_ProjectRepositories[m_Repository]);
s_Settings.Save();
}
GUILayout.EndVertical();
Handles.EndGUI();
using (new Handles.DrawingScope(m_ToolColor))
{
m_HandlePosition = Handles.Slider(m_HandlePosition, Vector3.right);
}
}
}
}

View File

@@ -0,0 +1,49 @@
using UnityEditor.SettingsManagement;
namespace UnityEditor.SettingsManagement.Examples
{
/// <summary>
/// This class will act as a manager for the <see cref="Settings"/> singleton.
/// </summary>
static class MySettingsManager
{
// Replace this with your own package name. Project settings will be stored in a JSON file in a directory matching
// this name.
internal const string k_PackageName = "com.unity.settings-manager-examples";
static Settings s_Instance;
internal static Settings instance
{
get
{
if (s_Instance == null)
s_Instance = new Settings(k_PackageName);
return s_Instance;
}
}
// The rest of this file is just forwarding the various setting methods to the instance.
public static void Save()
{
instance.Save();
}
public static T Get<T>(string key, SettingsScope scope = SettingsScope.Project, T fallback = default(T))
{
return instance.Get<T>(key, scope, fallback);
}
public static void Set<T>(string key, T value, SettingsScope scope = SettingsScope.Project)
{
instance.Set<T>(key, value, scope);
}
public static bool ContainsKey<T>(string key, SettingsScope scope = SettingsScope.Project)
{
return instance.ContainsKey<T>(key, scope);
}
}
}

View File

@@ -0,0 +1,43 @@
using System;
using UnityEngine;
namespace UnityEditor.SettingsManagement.Examples
{
/// <summary>
/// To create an entry in the Preferences window, define a new SettingsProvider inheriting <see cref="UserSettingsProvider"/>.
/// You can also choose to implement your own SettingsProvider and ignore this implementation. The benefit of using
/// <see cref="UserSettingsProvider"/> is that all <see cref="UserSetting{T}"/> fields in the assembly are automatically
/// populated within the preferences, with support for search and resetting default values.
/// </summary>
static class MySettingsProvider
{
const string k_PreferencesPath = "Preferences/Package With Project and User Settings";
#if UNITY_2018_3_OR_NEWER
[SettingsProvider]
static SettingsProvider CreateSettingsProvider()
{
var provider = new UserSettingsProvider(k_PreferencesPath,
MySettingsManager.instance,
new [] { typeof(MySettingsProvider).Assembly });
return provider;
}
#else
// For backwards compatibility it is possible to create an instance of UserSettingsProvider and invoke OnGUI manually.
[NonSerialized]
static UserSettingsProvider s_SettingsProvider;
[PreferenceItem("ProBuilder")]
static void ProBuilderPreferencesGUI()
{
if (s_SettingsProvider == null)
s_SettingsProvider = new UserSettingsProvider(MySettingsManager.instance, new[] { typeof(MySettingsProvider).Assembly });
s_SettingsProvider.OnGUI(null);
}
#endif
}
}

View File

@@ -0,0 +1,16 @@
using UnityEditor.SettingsManagement;
namespace UnityEditor.SettingsManagement.Examples
{
// Usually you will only have a single Settings instance, so it is convenient to define a UserSetting<T> implementation
// that points to your instance. In this way you avoid having to pass the Settings parameter in setting field definitions.
class MySetting<T> : UserSetting<T>
{
public MySetting(string key, T value, SettingsScope scope = SettingsScope.Project)
: base(MySettingsManager.instance, key, value, scope)
{}
MySetting(Settings settings, string key, T value, SettingsScope scope = SettingsScope.Project)
: base(settings, key, value, scope) { }
}
}

View File

@@ -0,0 +1,104 @@
using System;
using UnityEngine;
namespace UnityEditor.SettingsManagement.Examples
{
[Serializable]
class FooClass
{
public int intValue;
public string stringValue;
public FooClass()
{
intValue = 42;
stringValue = "I'm some text";
}
}
class MySettingsExamples : EditorWindow
{
#pragma warning disable 414
// [UserSetting] attribute registers this setting with the UserSettingsProvider so that it can be automatically
// shown in the UI.
[UserSetting("General Settings", "Days Without Incident")]
static MySetting<int> s_NumberOfDaysWithoutIncident = new MySetting<int>("general.daysWithoutIncident", 0, SettingsScope.User);
[UserSetting("General Settings", "Favorite Color")]
static MySetting<Color> s_FavoriteColor = new MySetting<Color>("general.favoriteColor", Color.magenta);
[UserSetting("General Settings", "Vector2 Field")]
static MySetting<Vector2> s_Vector2Value = new MySetting<Vector2>("general.vector2Value", new Vector2(2f, 4f));
[UserSetting("General Settings", "Editor Flags")]
static MySetting<StaticEditorFlags> s_EditorFlags = new MySetting<StaticEditorFlags>("general.editorFlags", StaticEditorFlags.BatchingStatic);
#pragma warning restore 414
// [UserSetting] with no arguments simply registers the key with UserSettingsProvider so that it can be included
// in debug views and reset with the options gizmo. Usually this is used in conjunction with [UserSettingsBlock].
[UserSetting]
static MySetting<FooClass> s_Foo = new MySetting<FooClass>("general.foo", new FooClass(), SettingsScope.Project);
[UserSetting]
static MySetting<int> s_NumberWithSlider = new MySetting<int>("general.conditionalValue", 5, SettingsScope.Project);
// A UserSettingBlock is a callback invoked from the UserSettingsProvider. It allows you to draw more complicated
// UI elements without the need to create a new SettingsProvider. Parameters are "category" and "search keywords."
// For maximum compatibility, use `SettingsGUILayout` searchable and settings fields to get features like search
// and per-setting reset with a context click.
[UserSettingBlock("Custom GUI Settings")]
static void ConditionalValueGUI(string searchContext)
{
EditorGUI.BeginChangeCheck();
s_NumberWithSlider.value = SettingsGUILayout.SettingsSlider("Number With Slider", s_NumberWithSlider, 0, 10, searchContext);
var foo = s_Foo.value;
using(new SettingsGUILayout.IndentedGroup("Foo Class"))
{
EditorGUI.BeginChangeCheck();
foo.intValue = SettingsGUILayout.SearchableIntField("Int Value", foo.intValue, searchContext);
foo.stringValue = SettingsGUILayout.SearchableTextField("String Value", foo.stringValue, searchContext);
// Because FooClass is a reference type, we need to apply the changes to the backing repository (SetValue
// would also work here).
if (EditorGUI.EndChangeCheck())
s_Foo.ApplyModifiedProperties();
}
SettingsGUILayout.DoResetContextMenuForLastRect(s_Foo);
if (EditorGUI.EndChangeCheck())
MySettingsManager.Save();
}
const string k_ColorInstanceFieldKey = "MySettingsExamples.m_ColorField";
// It is also possible to forego the UserSetting<T> wrapper and use a settings instance directly. To register
// a setting with the "Reset All" option of UserSettingsProvider, apply the [UserSetting] (or for instance fields [SettingsKey]) attribute.
Color m_ColorField;
[MenuItem("Window/Show Settings Examples")]
static void Init()
{
GetWindow<MySettingsExamples>();
}
void OnEnable()
{
m_ColorField = MySettingsManager.Get<Color>(k_ColorInstanceFieldKey);
}
void OnGUI()
{
EditorGUI.BeginChangeCheck();
m_ColorField = EditorGUILayout.ColorField("Color", m_ColorField);
if (EditorGUI.EndChangeCheck())
MySettingsManager.Set<Color>(k_ColorInstanceFieldKey, m_ColorField);
}
}
}

View File

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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8f491bec7bcc4db45ad9590a6158e0fd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More