Setting.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. using System.ComponentModel;
  2. using System.Diagnostics;
  3. namespace PixiEditor.Extensions.CommonApi.UserPreferences.Settings;
  4. [DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
  5. public abstract class Setting<T> : INotifyPropertyChanged
  6. {
  7. private readonly IPreferences preferences;
  8. private event PropertyChangedEventHandler PropertyChanged;
  9. /// <summary>
  10. /// The name of the preference
  11. /// </summary>
  12. public string Name { get; }
  13. /// <summary>
  14. /// The value of the preference
  15. /// </summary>
  16. public T? Value
  17. {
  18. get => GetValue(preferences, FallbackValue);
  19. set => SetValue(preferences, value);
  20. }
  21. /// <summary>
  22. /// The value used if the preference has not been set before
  23. /// </summary>
  24. public T? FallbackValue { get; }
  25. /// <summary>
  26. /// Called when the value of the preference has changed
  27. /// </summary>
  28. public event SettingChangedHandler<T> ValueChanged;
  29. /// <param name="name">The name of the preference</param>
  30. /// <param name="fallbackValue">The value used if the preference has not been set before</param>
  31. protected Setting(string name, T? fallbackValue = default)
  32. {
  33. SettingHelper.ThrowIfEmptySettingName(name);
  34. Name = name;
  35. FallbackValue = fallbackValue;
  36. preferences = IPreferences.Current;
  37. preferences.AddCallback<T>(Name, SettingChangeCallback);
  38. }
  39. /// <summary>
  40. /// Gets the value of the preference or the <see cref="fallbackValue"/> if the preference has not been set before. Note: This will ignore the <see cref="FallbackValue"/> set in the setting constructor
  41. /// </summary>
  42. /// <param name="fallbackValue">The value used if the preference has not been set before</param>
  43. /// <returns>Either the value of the preference or <see cref="fallbackValue"/></returns>
  44. public T GetValueOrDefault(T fallbackValue) => GetValue(preferences, fallbackValue);
  45. /// <summary>
  46. /// Gets the value of the preference as <typeparamref name="T"/> instead of the type defined by the setting.
  47. /// </summary>
  48. /// <param name="fallbackValue">The value used if the preference has not been set before</param>
  49. /// <returns>Either the value of the preference as <typeparamref name="T"/> or <see cref="fallbackValue"/></returns>
  50. public TAny? As<TAny>(TAny? fallbackValue = default) => GetValue(preferences, fallbackValue);
  51. protected abstract TAny? GetValue<TAny>(IPreferences preferences, TAny fallbackValue);
  52. protected abstract void SetValue(IPreferences preferences, T? value);
  53. private void SettingChangeCallback(string name, T newValue)
  54. {
  55. ValueChanged?.Invoke(this, newValue);
  56. PropertyChanged?.Invoke(this, PropertyChangedConstants.ValueChangedPropertyArgs);
  57. }
  58. private string GetDebuggerDisplay()
  59. {
  60. string value;
  61. try
  62. {
  63. value = Value.ToString();
  64. }
  65. catch (Exception e)
  66. {
  67. value = $"<{e.GetType()}: {e.Message}>";
  68. }
  69. if (typeof(T) == typeof(string))
  70. {
  71. value = $"""
  72. "{value}"
  73. """;
  74. }
  75. var type = typeof(T).ToString();
  76. string preferenceType = this switch
  77. {
  78. LocalSetting<T> => "local",
  79. SyncedSetting<T> => "synced",
  80. _ => "<undefined>"
  81. };
  82. return $"{preferenceType} {Name}: {type} = {value}";
  83. }
  84. event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
  85. {
  86. add => PropertyChanged += value;
  87. remove => PropertyChanged -= value;
  88. }
  89. }
  90. // Generic types would create an instance for every type combination.
  91. file static class PropertyChangedConstants
  92. {
  93. public static readonly PropertyChangedEventArgs ValueChangedPropertyArgs = new("Value");
  94. }