-
Notifications
You must be signed in to change notification settings - Fork 17
Settings
Settings in RD3 have been completely overhauled, so much that the Update Server will need a tool to import and convert any relevant RD2/legacy settings.
The abstraction level has changed: rather than being expressed as properties of a given type, settings are now expressed as their own entities, and their properties are metadata that is then used for templating the settings UI.
This means all RD3 settings inherit the same base record class: RubberduckSetting
, and the most-derived type to de/serialize must have a corresponding JsonDerivedTypeAttribute
, because RubberduckSetting
uses System.Text.Json.Serialization
and the JsonPolymorphic
serialization attribute. This may change in the future as it is an implementation detail.
⚠️ When adding a new setting, we must remember to add a correspondingJsonDerivedTypeAttribute
to theRubberduckSetting
definition.[JsonDerivedType(typeof(NewSetting), nameof(NewSetting))]
Otherwise, adding a new setting is a very straightforward experience.
This would be the most common case: you're writing a feature, and suddenly you come across a value that could/should be configurable, so you add a //TODO make this configurable
comment and think "meh, I'll get back to this later".
// TODO make this configurable
var isEnabled = true;
If you're "in the zone", it's probably a good idea to do this. In RD2 adding a new setting would entail crafting a UI for it, and then modifying the model all the way from the ViewModel to the serialized XML - in other words adding a new setting touches on multiple layers and can make a simple configurable feature turn into a very complex endeavor, and there goes your focus.
In RD3 however, you don't need to context-shift as much, because all you need to do is to inherit the correct record class type, and then go and add it to an appropriate existing setting group... and that's about it: you don't have to think about the settings UI at all - unless you're introducing a new data type, that is.
Figuring out which record class type to inherit would be the hard part.
The RD3 settings model has lots of types, but it boils down to having a record class type for each setting data type we want to be able to represent in the settings UI, and then inheriting it whenever we need a new setting.
RubberduckSetting
TypedRubberduckSetting<TValue> : RubberduckSetting
- ⭐
BooleanRubberduckSetting : TypedRubberduckSetting<bool>
- ⭐
NumericRubberduckSetting : TypedRubberduckSetting<double>
- ⭐
StringRubberduckSetting : TypedRubberduckSetting<string>
- ⭐
UriRubberduckSetting : TypedRubberduckSetting<Uri>
TypedSettingGroup : TypedRubberduckSetting<RubberduckSetting[]>
ToolWindowSettings : TypedSettingGroup
The rest of the model is just specific types inheriting TypedSettingGroup
or one of the ⭐ common/named types.
When adding a setting that needs a new parent setting group, inherit TypedSettingGroup
using another existing implementation as a reference.
- Typed settings define a
public static T DefaultSettingValue { get; } = ...
property- Typed settings' default constructor sets
DefaultValue = DefaultSettingValue
- Typed settings' default constructor sets
- Setting groups define a
private static readonly RubberduckSetting[] DefaultSettings = [...];
field that gets theDefault
value for each setting in the setting group.- Setting groups' default constructor sets
Value = DefaultValue = DefaultSettings;
- Setting groups implementing
IDefaultSettingsProvider<T>
expose apublic static T Default { get; } = new() { Value = DefaultSettings, DefaultValue = DefaultSettings };
static property that is returned by an explicit implementation ofIDefaultSettingsProvider<T>.Default
, soT IDefaultSettingsProvider<T>.Default => Default;
- Setting groups should expose a
[JsonIgnore]
-decorated public property getter that returnsGetSettings<T>() ?? T.Default;
- Setting groups' default constructor sets
- For now, resources are under
Rubberduck.Resources.v3.SettingsUI.resx
. The name of the class type serves as part of both keys that need to be added:$"{SettingTypeName}_Title"
$"{SettingTypeName}_Description"