File size: 14,354 Bytes
18a519f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
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);
        }
    }
}