diff --git a/src/Abstractions/NexusMods.Abstractions.Games.Diagnostics/DiagnosticSettings.cs b/src/Abstractions/NexusMods.Abstractions.Games.Diagnostics/DiagnosticSettings.cs
index 3de677e4b6..cdcf75c0e8 100644
--- a/src/Abstractions/NexusMods.Abstractions.Games.Diagnostics/DiagnosticSettings.cs
+++ b/src/Abstractions/NexusMods.Abstractions.Games.Diagnostics/DiagnosticSettings.cs
@@ -17,8 +17,7 @@ public class DiagnosticSettings : ISettings
///
public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder)
{
- // TODO: put in some section
- var sectionId = SectionId.DefaultValue;
+ var sectionId = Sections.General;
return settingsBuilder.AddToUI(builder => builder
.AddPropertyToUI(x => x.MinimumSeverity, propertyBuilder => propertyBuilder
diff --git a/src/Abstractions/NexusMods.Abstractions.Settings/Sections.cs b/src/Abstractions/NexusMods.Abstractions.Settings/Sections.cs
new file mode 100644
index 0000000000..1feb4dfddb
--- /dev/null
+++ b/src/Abstractions/NexusMods.Abstractions.Settings/Sections.cs
@@ -0,0 +1,20 @@
+using JetBrains.Annotations;
+
+namespace NexusMods.Abstractions.Settings;
+
+///
+/// A collection of sections to use.
+///
+[PublicAPI]
+public static class Sections
+{
+ public static readonly SectionId General = SectionId.From(Guid.Parse("3106487b-db84-4caa-acdd-9428506fbf6d"));
+
+ public static readonly SectionId Privacy = SectionId.From(Guid.Parse("4ef7b142-8f0e-4cae-867f-a58d985241c0"));
+
+ public static readonly SectionId Advanced = SectionId.From(Guid.Parse("1531efa7-cb0a-4463-8a04-a865d848ca06"));
+
+ public static readonly SectionId DeveloperTools = SectionId.From(Guid.Parse("c33fb41c-7dc5-4911-b48c-3a8c822083d9"));
+
+ public static readonly SectionId Experimental = SectionId.From(Guid.Parse("864495d4-aa30-48a7-b292-d23adce391f1"));
+}
diff --git a/src/Abstractions/NexusMods.Abstractions.Telemetry/TelemetrySettings.cs b/src/Abstractions/NexusMods.Abstractions.Telemetry/TelemetrySettings.cs
index 0178d2fc5d..80eb5df67a 100644
--- a/src/Abstractions/NexusMods.Abstractions.Telemetry/TelemetrySettings.cs
+++ b/src/Abstractions/NexusMods.Abstractions.Telemetry/TelemetrySettings.cs
@@ -12,16 +12,13 @@ public record TelemetrySettings : ISettings
public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder)
{
- // TODO: put in some section
- var sectionId = SectionId.DefaultValue;
-
return settingsBuilder
.ConfigureStorageBackend(backend => backend.UseJson())
.AddToUI(builder => builder
.AddPropertyToUI(x => x.IsEnabled, propertyBuilder => propertyBuilder
- .AddToSection(sectionId)
- .WithDisplayName("Enable Telemetry")
- .WithDescription("Send anonymous analytics information and usage data to Nexus Mods.")
+ .AddToSection(Sections.Privacy)
+ .WithDisplayName("Send usage data")
+ .WithDescription("Help us improve the App by sending usage data to Nexus Mods.")
.UseBooleanContainer()
.RequiresRestart()
)
diff --git a/src/NexusMods.App.UI/ExperimentalSettings.cs b/src/NexusMods.App.UI/ExperimentalSettings.cs
index 69781d229b..3c8910b955 100644
--- a/src/NexusMods.App.UI/ExperimentalSettings.cs
+++ b/src/NexusMods.App.UI/ExperimentalSettings.cs
@@ -21,23 +21,20 @@ public record ExperimentalSettings : ISettings
public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder)
{
- // TODO: put in some section
- var sectionId = SectionId.DefaultValue;
-
return settingsBuilder
.ConfigureStorageBackend(builder => builder.UseJson())
.AddToUI(builder => builder
.AddPropertyToUI(x => x.EnableAllGames, propertyBuilder => propertyBuilder
- .AddToSection(sectionId)
- .WithDisplayName("[Unsupported] Enable Unsupported Games")
- .WithDescription("When set, 'work-in-progress' games that are not yet fully supported will be enabled in the UI.")
+ .AddToSection(Sections.Experimental)
+ .WithDisplayName("Enable unsupported games")
+ .WithDescription("Allows you to manage unsupported games.")
.UseBooleanContainer()
.RequiresRestart()
)
.AddPropertyToUI(x => x.EnableMultipleLoadouts, propertyBuilder => propertyBuilder
- .AddToSection(sectionId)
- .WithDisplayName("(Experimental) Enable Multiple Loadouts")
- .WithDescription("When set, you will be able to create multiple loadouts for a game.")
+ .AddToSection(Sections.Experimental)
+ .WithDisplayName("Enable multiple loadouts")
+ .WithDescription("Allows you to create multiple Loadouts.")
.UseBooleanContainer()
)
);
diff --git a/src/NexusMods.App.UI/Overlays/MetricsOptIn/MetricsOptInViewModel.cs b/src/NexusMods.App.UI/Overlays/MetricsOptIn/MetricsOptInViewModel.cs
index b70828084f..44766c103c 100644
--- a/src/NexusMods.App.UI/Overlays/MetricsOptIn/MetricsOptInViewModel.cs
+++ b/src/NexusMods.App.UI/Overlays/MetricsOptIn/MetricsOptInViewModel.cs
@@ -29,13 +29,11 @@ public MetricsOptInViewModel(ISettingsManager settingsManager, IMarkdownRenderer
MarkdownRendererViewModel = markdownRendererViewModel;
MarkdownRendererViewModel.Contents = """
-## Telemetry and usage data
+## Help us improve
Help us provide you with the best modding experience.
-With your permission, we will collect analytics information and send it to our team to help us improve quality and performance.
-
-This information is sent anonymously and will never be shared with a 3rd party.
+With your permission, we will collect anonymous analytics information and send it to our team to help us improve quality and performance.
[More information about the data we track](https://help.nexusmods.com/article/132-diagnostics-and-usage-nexus-mods-app)
""";
diff --git a/src/NexusMods.App.UI/Pages/Settings/SettingsView.axaml.cs b/src/NexusMods.App.UI/Pages/Settings/SettingsView.axaml.cs
index ebdcf3d460..e8cbd19a95 100644
--- a/src/NexusMods.App.UI/Pages/Settings/SettingsView.axaml.cs
+++ b/src/NexusMods.App.UI/Pages/Settings/SettingsView.axaml.cs
@@ -37,7 +37,7 @@ public SettingsView()
foreach (var group in grouped)
{
res.Add(dict[group.Key]);
- res.AddRange(group);
+ res.AddRange(group.OrderBy(x => x.PropertyUIDescriptor.DisplayName, StringComparer.OrdinalIgnoreCase));
}
return res;
diff --git a/src/NexusMods.App.UI/Settings/LanguageSettings.cs b/src/NexusMods.App.UI/Settings/LanguageSettings.cs
index 3bea6051c1..42277255d3 100644
--- a/src/NexusMods.App.UI/Settings/LanguageSettings.cs
+++ b/src/NexusMods.App.UI/Settings/LanguageSettings.cs
@@ -30,12 +30,9 @@ static LanguageSettings()
public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder)
{
- // TODO: put in some section
- var sectionId = SectionId.DefaultValue;
-
return settingsBuilder.AddToUI(builder => builder
.AddPropertyToUI(x => x.UICulture, propertyBuilder => propertyBuilder
- .AddToSection(sectionId)
+ .AddToSection(Sections.General)
.WithDisplayName("Language")
.WithDescription("Set the language for the application.")
.UseSingleValueMultipleChoiceContainer(
diff --git a/src/NexusMods.App.UI/Settings/LoadoutGridSettings.cs b/src/NexusMods.App.UI/Settings/LoadoutGridSettings.cs
index 76df40a1f8..b6944d8341 100644
--- a/src/NexusMods.App.UI/Settings/LoadoutGridSettings.cs
+++ b/src/NexusMods.App.UI/Settings/LoadoutGridSettings.cs
@@ -11,20 +11,17 @@ public record LoadoutGridSettings : ISettings
public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder)
{
- // TODO: put in some section
- var sectionId = SectionId.DefaultValue;
-
return settingsBuilder.AddToUI(builder => builder
.AddPropertyToUI(x => x.ShowGameFiles, propertyBuilder => propertyBuilder
- .AddToSection(sectionId)
- .WithDisplayName("Show Game Files")
- .WithDescription("Shows the Game Files in the Mods page.")
+ .AddToSection(Sections.Advanced)
+ .WithDisplayName("Show game files")
+ .WithDescription("Show game files as a mod alongside your added mods.")
.UseBooleanContainer()
)
.AddPropertyToUI(x => x.ShowOverride, propertyBuilder => propertyBuilder
- .AddToSection(sectionId)
- .WithDisplayName("Show Override")
- .WithDescription("Shows Override in the Mods page.")
+ .AddToSection(Sections.Advanced)
+ .WithDisplayName("Show Override mod")
+ .WithDescription("Shows the Override mod, which contains files generated or modified during gameplay that aren't part of any specific mod.")
.UseBooleanContainer()
)
);
diff --git a/src/NexusMods.App.UI/Settings/TextEditorSettings.cs b/src/NexusMods.App.UI/Settings/TextEditorSettings.cs
index 7c6b7f8062..26ee88fa95 100644
--- a/src/NexusMods.App.UI/Settings/TextEditorSettings.cs
+++ b/src/NexusMods.App.UI/Settings/TextEditorSettings.cs
@@ -12,22 +12,6 @@ public record TextEditorSettings : ISettings
public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder)
{
- // TODO: put in some section
- var sectionId = SectionId.DefaultValue;
-
- // TODO: add font size to UI (requires new component)
-
- return settingsBuilder.AddToUI(builder => builder
- .AddPropertyToUI(x => x.ThemeName, propertyBuilder => propertyBuilder
- .AddToSection(sectionId)
- .WithDisplayName("Text Editor Theme")
- .WithDescription("Set the desired theme in the text editor.")
- .UseSingleValueMultipleChoiceContainer(
- valueComparer: EqualityComparer.Default,
- allowedValues: Enum.GetValues(),
- valueToDisplayString: x => x.ToString()
- )
- )
- );
+ return settingsBuilder;
}
}
diff --git a/src/NexusMods.CrossPlatform/LoggingSettings.cs b/src/NexusMods.CrossPlatform/LoggingSettings.cs
index 12f59aeee2..95da734a91 100644
--- a/src/NexusMods.CrossPlatform/LoggingSettings.cs
+++ b/src/NexusMods.CrossPlatform/LoggingSettings.cs
@@ -58,17 +58,14 @@ public record LoggingSettings : ISettings
///
public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder)
{
- // TODO: put in some section
- var sectionId = SectionId.DefaultValue;
-
return settingsBuilder
.ConfigureDefault(serviceProvider => CreateDefault(serviceProvider.GetRequiredService().OS))
.ConfigureStorageBackend(builder => builder.UseJson())
.AddToUI(builder => builder
.AddPropertyToUI(x => x.MinimumLevel, propertyBuilder => propertyBuilder
- .AddToSection(sectionId)
- .WithDisplayName("Minimum Logging Level")
- .WithDescription("Set the minimum logging level. Recommended: Debug")
+ .AddToSection(Sections.General)
+ .WithDisplayName("Minimum logging level")
+ .WithDescription("Sets the minimum logging level. Recommended: Debug")
.UseSingleValueMultipleChoiceContainer(
valueComparer: EqualityComparer.Default,
allowedValues: [
@@ -81,9 +78,9 @@ public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder)
.RequiresRestart()
)
.AddPropertyToUI(x => x.LogToConsole, propertyBuilder => propertyBuilder
- .AddToSection(sectionId)
- .WithDisplayName("Log to Console")
- .WithDescription("When enabled, logs will be written to the console as well as the log file.")
+ .AddToSection(Sections.DeveloperTools)
+ .WithDisplayName("Log to stdout")
+ .WithDescription("Enables the ConsoleTarget for all loggers.")
.UseBooleanContainer()
.RequiresRestart()
)
diff --git a/src/NexusMods.Icons/IconValues.cs b/src/NexusMods.Icons/IconValues.cs
index 0afd8ec667..e7e4d51531 100644
--- a/src/NexusMods.Icons/IconValues.cs
+++ b/src/NexusMods.Icons/IconValues.cs
@@ -136,6 +136,9 @@ public static class IconValues
#region Editor
+ // https://pictogrammers.com/library/mdi/icon/poll
+ public static readonly IconValue BarChart = new ProjektankerIcon("mdi-poll");
+
// https://pictogrammers.com/library/mdi/icon/drag-horizontal-variant/
public static readonly IconValue DragHandleHorizontal = new ProjektankerIcon("mdi-drag-horizontal-variant");
@@ -177,6 +180,9 @@ public static class IconValues
#region Hardware
+ // https://pictogrammers.com/library/mdi/icon/monitor/
+ public static readonly IconValue Desktop = new ProjektankerIcon("mdi-monitor");
+
// https://pictogrammers.com/library/mdi/icon/gamepad-square-outline/
public static readonly IconValue Game = new ProjektankerIcon("mdi-gamepad-square-outline");
@@ -227,6 +233,13 @@ public static class IconValues
#endregion
+#region Social
+
+ // https://pictogrammers.com/library/mdi/icon/school
+ public static readonly IconValue School = new ProjektankerIcon("mdi-school");
+
+#endregion
+
#region Toggle
// https://pictogrammers.com/library/mdi/icon/star/
diff --git a/src/NexusMods.Settings/Services.cs b/src/NexusMods.Settings/Services.cs
index f147bbff56..e6082f3bf5 100644
--- a/src/NexusMods.Settings/Services.cs
+++ b/src/NexusMods.Settings/Services.cs
@@ -12,9 +12,33 @@ public static IServiceCollection AddSettingsManager(this IServiceCollection serv
.AddSingleton()
.AddSettingsSection(new SettingsSectionSetup
{
- Id = SectionId.DefaultValue,
- IconFunc = static () => IconValues.Error,
- Name = "No Section",
+ Id = Sections.General,
+ IconFunc = static () => IconValues.Desktop,
+ Name = "General",
+ })
+ .AddSettingsSection(new SettingsSectionSetup
+ {
+ Id = Sections.Privacy,
+ IconFunc = static () => IconValues.BarChart,
+ Name = "Privacy",
+ })
+ .AddSettingsSection(new SettingsSectionSetup
+ {
+ Id = Sections.Advanced,
+ IconFunc = static () => IconValues.School,
+ Name = "Advanced",
+ })
+ .AddSettingsSection(new SettingsSectionSetup
+ {
+ Id = Sections.DeveloperTools,
+ IconFunc = static () => IconValues.Code,
+ Name = "Developer tools",
+ })
+ .AddSettingsSection(new SettingsSectionSetup
+ {
+ Id = Sections.Experimental,
+ IconFunc = static () => IconValues.WarningAmber,
+ Name = "Experimental - Not currently supported",
});
}
}
diff --git a/src/NexusMods.Settings/SettingsManager.cs b/src/NexusMods.Settings/SettingsManager.cs
index 428c0388cf..462be2b5ab 100644
--- a/src/NexusMods.Settings/SettingsManager.cs
+++ b/src/NexusMods.Settings/SettingsManager.cs
@@ -41,7 +41,7 @@ public SettingsManager(IServiceProvider serviceProvider)
var settingsSectionSetups = serviceProvider.GetServices().ToArray();
- // NOTE(erri120): This has to be Lazy because the Icon isn't available until Avalonia starts up.
+ // NOTE(erri120): This has to be Lazy because icons aren't available until Avalonia starts up.
_sectionDescriptors = new Lazy(() => settingsSectionSetups
.Select(descriptor => (ISettingsSectionDescriptor)new SettingsSectionDescriptor
{
@@ -53,16 +53,6 @@ public SettingsManager(IServiceProvider serviceProvider)
mode: LazyThreadSafetyMode.ExecutionAndPublication
);
- if (CompileConstants.IsDebug)
- {
- var ids = new HashSet();
- foreach (var sectionDescriptor in settingsSectionSetups)
- {
- var id = sectionDescriptor.Id;
- Debug.Assert(ids.Add(id), $"duplicate section ID: {id}");
- }
- }
-
var baseStorageBackendArray = serviceProvider.GetServices().ToArray();
var settingsTypeInformationArray = serviceProvider.GetServices().ToArray();
var defaultBaseStorageBackend = serviceProvider.GetService()?.Backend;
@@ -72,6 +62,21 @@ public SettingsManager(IServiceProvider serviceProvider)
_storageBackendMappings = builderOutput.StorageBackendMappings;
_asyncStorageBackendMappings = builderOutput.AsyncStorageBackendMappings;
_propertyBuilderOutputs = builderOutput.PropertyBuilderOutputs;
+
+ if (CompileConstants.IsDebug)
+ {
+ var ids = new HashSet();
+ foreach (var sectionDescriptor in settingsSectionSetups)
+ {
+ var id = sectionDescriptor.Id;
+ Debug.Assert(ids.Add(id), $"duplicate section ID: {id}");
+ }
+
+ foreach (var propertyBuilderOutput in _propertyBuilderOutputs)
+ {
+ Debug.Assert(settingsSectionSetups.Any(x => x.Id == propertyBuilderOutput.SectionId), $"section not registered: {propertyBuilderOutput.SectionId} (for setting {propertyBuilderOutput.DisplayName})");
+ }
+ }
}
private void CoreSet(T value, bool notify) where T : class, ISettings, new()
diff --git a/src/NexusMods.SingleProcess/CliSettings.cs b/src/NexusMods.SingleProcess/CliSettings.cs
index cea626de7b..fecc9890d1 100644
--- a/src/NexusMods.SingleProcess/CliSettings.cs
+++ b/src/NexusMods.SingleProcess/CliSettings.cs
@@ -12,7 +12,7 @@ public class CliSettings() : ISettings
///
public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder)
{
- var sectionId = SectionId.DefaultValue;
+ var sectionId = Sections.DeveloperTools;
return settingsBuilder
.ConfigureDefault(CreateDefault)
.ConfigureStorageBackend(builder => builder.UseJson())
diff --git a/src/NexusMods.StandardGameLocators/GameLocatorSettings.cs b/src/NexusMods.StandardGameLocators/GameLocatorSettings.cs
index 8b4891be8b..2bf42fe476 100644
--- a/src/NexusMods.StandardGameLocators/GameLocatorSettings.cs
+++ b/src/NexusMods.StandardGameLocators/GameLocatorSettings.cs
@@ -10,16 +10,13 @@ public record GameLocatorSettings : ISettings
public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder)
{
- // TODO: put in some section
- var sectionId = SectionId.DefaultValue;
-
return settingsBuilder
.ConfigureStorageBackend(builder => builder.UseJson())
.AddToUI(builder => builder
.AddPropertyToUI(x => x.EnableXboxGamePass, propertyBuilder => propertyBuilder
- .AddToSection(sectionId)
- .WithDisplayName("(Experimental) Enable Xbox Game Pass support")
- .WithDescription("For testing the Xbox Game Pass detection")
+ .AddToSection(Sections.Experimental)
+ .WithDisplayName("Enable Xbox Game Pass support")
+ .WithDescription("Allows you to manage games installed with Xbox Game Pass.")
.UseBooleanContainer()
)
);