using System; using UnityEngine; namespace UnityEditor.SettingsManagement { [Flags] enum SettingVisibility { None = 0 << 0, /// <value> /// Matches any static field implementing IUserSetting and tagged with [UserSettingAttribute(visibleInSettingsProvider = true)]. /// </value> /// <summary> /// These fields are automatically scraped by the SettingsProvider and displayed. /// </summary> Visible = 1 << 0, /// <value> /// Matches any static field implementing IUserSetting and tagged with [UserSettingAttribute(visibleInSettingsProvider = false)]. /// </value> /// <summary> /// 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, /// <value> /// A static or instance field tagged with [SettingsKeyAttribute]. /// </value> /// <summary> /// Unlisted settings are not shown in the SettingsProvider, but are reset to default values by the "Reset All" /// context menu. /// </summary> Unlisted = 1 << 2, /// <value> /// A static field implementing IUserSetting that is not marked with any setting attribute. /// </value> /// <summary> /// Unregistered IUserSetting fields are not affected by the SettingsProvider. /// </summary> Unregistered = 1 << 3, All = Visible | Hidden | Unlisted | Unregistered } /// <summary> /// 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 { /// <value> /// The key for this value. /// </value> string key { get; } /// <value> /// The type of the stored value. /// </value> Type type { get; } /// <value> /// At which scope this setting is saved. /// </value> SettingsScope scope { get; } /// <summary> /// The name of the <see cref="ISettingsRepository"/> that this setting should be associated with. If null, the /// first repository matching the <see cref="scope"/> will be used. /// </summary> string settingsRepositoryName { get; } /// <value> /// The <see cref="Settings"/> instance that this setting should be saved and loaded from. /// </value> Settings settings { get; } /// <summary> /// Get the stored value. /// If you are implementing IUserSetting it is recommended that you cache this value. /// </summary> /// <returns> /// The stored value. /// </returns> object GetValue(); /// <summary> /// Get the default value for this setting. /// </summary> /// <returns> /// The default value for this setting. /// </returns> object GetDefaultValue(); /// <summary> /// Set the value for this setting. /// </summary> /// <param name="value">The new value.</param> /// <param name="saveProjectSettingsImmediately"> /// True to immediately serialize the 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> /// 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> /// Set the current value back to the default. /// </summary> /// <param name="saveProjectSettingsImmediately">True to immediately re-serialize project settings.</param> void Reset(bool saveProjectSettingsImmediately = false); /// <summary> /// Delete the saved setting. Does not clear the current value. /// </summary> /// <see cref="Reset"/> /// <param name="saveProjectSettingsImmediately">True to immediately re-serialize project settings.</param> void Delete(bool saveProjectSettingsImmediately = false); } /// <summary> /// A generic implementation of IUserSetting to be used with a <see cref="Settings"/> instance. This default /// implementation assumes the <see cref="Settings"/> instance contains two <see cref="ISettingsRepository"/>, one /// for <see cref="SettingsScope.Project"/> and one for <see cref="SettingsScope.User"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <inheritdoc /> 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> /// Constructor for UserSetting{T} type. /// </summary> /// <param name="settings">The <see cref="Settings"/> instance that this setting should be saved and loaded 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 at which to save this setting.</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> /// Constructor for UserSetting{T} type. /// </summary> /// <param name="settings">The <see cref="Settings"/> instance that this setting should be saved and loaded from.</param> /// <param name="repository">The <see cref="ISettingsRepository"/> name that this setting should be saved and loaded from. Pass null to save to 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 at which to save this setting.</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; } /// <value> /// The key for this value. /// </value> /// <inheritdoc /> public string key { get { return m_Key; } } /// <value> /// The name of the repository that this setting is saved in. /// </value> /// <inheritdoc /> public string settingsRepositoryName { get { return m_Repository; } } /// <value> /// The type that this setting represents ({T}). /// </value> /// <inheritdoc /> public Type type { get { return typeof(T); } } /// <summary> /// Get a copy of the default value. /// </summary> /// <returns> /// The default value. /// </returns> /// <inheritdoc /> public object GetDefaultValue() { return defaultValue; } /// <summary> /// Get the currently stored value. /// </summary> /// <returns> /// The value that is currently set. /// </returns> /// <inheritdoc /> public object GetValue() { return value; } /// <summary> /// The scope affects which <see cref="ISettingsRepository"/> the <see cref="settings"/> instance will save /// it's data to. /// </summary> /// <value> /// The scope at which to save this key and value. /// </value> /// <inheritdoc /> public SettingsScope scope { get { return m_Scope; } } /// <value> /// The <see cref="Settings"/> instance that this setting will be read from and saved to. /// </value> /// <inheritdoc /> public Settings settings { get { return m_Settings; } } /// <summary> /// Set the value for this setting. /// </summary> /// <param name="value">The new value.</param> /// <param name="saveProjectSettingsImmediately"> /// True to immediately serialize the ISettingsRepository that is backing this value, or false to postpone. /// If not serializing immediately, be sure to call <see cref="Settings.Save"/>. /// </param> /// <inheritdoc /> 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); } /// <summary> /// Set the value for this setting. /// </summary> /// <param name="value">The new value.</param> /// <param name="saveProjectSettingsImmediately"> /// True to immediately serialize the ISettingsRepository that is backing this value, or false to postpone. /// If not serializing immediately, be sure to call <see cref="Settings.Save"/>. /// </param> 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> /// Delete the saved setting. Does not clear the current value. /// </summary> /// <see cref="M:UnityEditor.SettingsManagement.UserSetting`1.Reset(System.Boolean)" /> /// <param name="saveProjectSettingsImmediately">True to immediately re-serialize project settings.</param> /// <inheritdoc 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> /// 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> /// <inheritdoc cref="IUserSetting.ApplyModifiedProperties"/> public void ApplyModifiedProperties() { settings.Set<T>(key, m_Value, m_Scope); settings.Save(); } /// <summary> /// Set the current value back to the default. /// </summary> /// <param name="saveProjectSettingsImmediately">True to immediately re-serialize project settings.</param> /// <inheritdoc 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); } } /// <value> /// The default value for this setting. /// </value> public T defaultValue { get { Init(); return ValueWrapper<T>.DeepCopy(m_DefaultValue); } } /// <value> /// The currently stored value. /// </value> public T value { get { Init(); return m_Value; } set { SetValue(value); } } /// <summary> /// Implicit cast to backing type. /// </summary> /// <param name="pref">The UserSetting{T} 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> /// Get a summary of this setting. /// </summary> /// <returns>A string summary of this setting.</returns> public override string ToString() { return string.Format("{0} setting. Key: {1} Value: {2}", scope, key, value); } } }