setting_re2

设置架构革新中
This commit is contained in:
lincube
2026-03-13 00:33:00 +08:00
parent 40a3a00cfe
commit c4df243610
92 changed files with 2048 additions and 10520 deletions

View File

@@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace LanMountainDesktop.PluginSdk;
public interface IComponentSettingsAccessor
{
string ComponentId { get; }
string? PlacementId { get; }
T LoadSnapshot<T>() where T : new();
void SaveSnapshot<T>(T snapshot, IReadOnlyCollection<string>? changedKeys = null);
T LoadSection<T>(string sectionId) where T : new();
void SaveSection<T>(string sectionId, T section, IReadOnlyCollection<string>? changedKeys = null);
void DeleteSection(string sectionId);
T? GetValue<T>(string key);
void SetValue<T>(string key, T value, IReadOnlyCollection<string>? changedKeys = null);
}

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace LanMountainDesktop.PluginSdk;
public interface ISettingsCatalog
{
IReadOnlyList<SettingsSectionDefinition> GetSections();
IReadOnlyList<SettingsSectionDefinition> GetSections(SettingsScope scope);
}

View File

@@ -0,0 +1,56 @@
using System.Collections.Generic;
namespace LanMountainDesktop.PluginSdk;
public interface ISettingsService
{
event EventHandler<SettingsChangedEvent>? Changed;
T LoadSnapshot<T>(SettingsScope scope, string? subjectId = null, string? placementId = null) where T : new();
void SaveSnapshot<T>(
SettingsScope scope,
T snapshot,
string? subjectId = null,
string? placementId = null,
string? sectionId = null,
IReadOnlyCollection<string>? changedKeys = null);
T LoadSection<T>(
SettingsScope scope,
string subjectId,
string sectionId,
string? placementId = null) where T : new();
void SaveSection<T>(
SettingsScope scope,
string subjectId,
string sectionId,
T section,
string? placementId = null,
IReadOnlyCollection<string>? changedKeys = null);
void DeleteSection(
SettingsScope scope,
string subjectId,
string sectionId,
string? placementId = null);
T? GetValue<T>(
SettingsScope scope,
string key,
string? subjectId = null,
string? placementId = null,
string? sectionId = null);
void SetValue<T>(
SettingsScope scope,
string key,
T value,
string? subjectId = null,
string? placementId = null,
string? sectionId = null,
IReadOnlyCollection<string>? changedKeys = null);
IComponentSettingsAccessor GetComponentAccessor(string componentId, string? placementId);
}

View File

@@ -5,20 +5,26 @@ namespace LanMountainDesktop.PluginSdk;
public static class PluginServiceCollectionExtensions
{
public static IServiceCollection AddPluginSettingsPage<TControl>(
public static IServiceCollection AddPluginSettingsSection(
this IServiceCollection services,
string id,
string title,
string titleLocalizationKey,
Action<PluginSettingsSectionBuilder> configure,
string? descriptionLocalizationKey = null,
string iconKey = "PuzzlePiece",
int sortOrder = 0)
where TControl : Control
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configure);
services.AddSingleton(new PluginSettingsPageRegistration(
var builder = new PluginSettingsSectionBuilder(
id,
title,
provider => ActivatorUtilities.CreateInstance<TControl>(provider),
sortOrder));
titleLocalizationKey,
descriptionLocalizationKey,
iconKey,
sortOrder);
configure(builder);
services.AddSingleton(builder.Build());
return services;
}

View File

@@ -1,39 +0,0 @@
using Avalonia.Controls;
namespace LanMountainDesktop.PluginSdk;
public sealed class PluginSettingsPageRegistration
{
public PluginSettingsPageRegistration(
string id,
string title,
Func<IServiceProvider, Control> contentFactory,
int sortOrder = 0)
{
ArgumentException.ThrowIfNullOrWhiteSpace(id);
ArgumentException.ThrowIfNullOrWhiteSpace(title);
ArgumentNullException.ThrowIfNull(contentFactory);
Id = id.Trim();
Title = title.Trim();
ContentFactory = contentFactory;
SortOrder = sortOrder;
}
public PluginSettingsPageRegistration(
string id,
string title,
Func<Control> contentFactory,
int sortOrder = 0)
: this(id, title, _ => contentFactory(), sortOrder)
{
}
public string Id { get; }
public string Title { get; }
public int SortOrder { get; }
public Func<IServiceProvider, Control> ContentFactory { get; }
}

View File

@@ -0,0 +1,147 @@
using System.Collections.Generic;
namespace LanMountainDesktop.PluginSdk;
public sealed class PluginSettingsSectionBuilder
{
private readonly List<SettingsOptionDefinition> _options = [];
internal PluginSettingsSectionBuilder(
string id,
string titleLocalizationKey,
string? descriptionLocalizationKey,
string iconKey,
int sortOrder)
{
Id = id;
TitleLocalizationKey = titleLocalizationKey;
DescriptionLocalizationKey = descriptionLocalizationKey;
IconKey = iconKey;
SortOrder = sortOrder;
}
public string Id { get; }
public string TitleLocalizationKey { get; }
public string? DescriptionLocalizationKey { get; }
public string IconKey { get; }
public int SortOrder { get; }
public IReadOnlyList<SettingsOptionDefinition> Options => _options;
public PluginSettingsSectionBuilder AddOption(SettingsOptionDefinition option)
{
ArgumentNullException.ThrowIfNull(option);
_options.Add(option);
return this;
}
public PluginSettingsSectionBuilder AddToggle(
string key,
string titleLocalizationKey,
string? descriptionLocalizationKey = null,
bool defaultValue = false)
{
return AddOption(new SettingsOptionDefinition(
key,
SettingsOptionType.Toggle,
titleLocalizationKey,
descriptionLocalizationKey,
defaultValue));
}
public PluginSettingsSectionBuilder AddText(
string key,
string titleLocalizationKey,
string? descriptionLocalizationKey = null,
string defaultValue = "",
string? validationPattern = null)
{
return AddOption(new SettingsOptionDefinition(
key,
SettingsOptionType.Text,
titleLocalizationKey,
descriptionLocalizationKey,
defaultValue,
validationPattern: validationPattern));
}
public PluginSettingsSectionBuilder AddNumber(
string key,
string titleLocalizationKey,
string? descriptionLocalizationKey = null,
double defaultValue = 0,
double? minimum = null,
double? maximum = null)
{
return AddOption(new SettingsOptionDefinition(
key,
SettingsOptionType.Number,
titleLocalizationKey,
descriptionLocalizationKey,
defaultValue,
minimum: minimum,
maximum: maximum));
}
public PluginSettingsSectionBuilder AddSelect(
string key,
string titleLocalizationKey,
IEnumerable<SettingsOptionChoice> choices,
string? descriptionLocalizationKey = null,
string? defaultValue = null)
{
ArgumentNullException.ThrowIfNull(choices);
var normalizedChoices = choices.ToArray();
return AddOption(new SettingsOptionDefinition(
key,
SettingsOptionType.Select,
titleLocalizationKey,
descriptionLocalizationKey,
defaultValue,
normalizedChoices));
}
public PluginSettingsSectionBuilder AddPath(
string key,
string titleLocalizationKey,
string? descriptionLocalizationKey = null,
string defaultValue = "")
{
return AddOption(new SettingsOptionDefinition(
key,
SettingsOptionType.Path,
titleLocalizationKey,
descriptionLocalizationKey,
defaultValue));
}
public PluginSettingsSectionBuilder AddList(
string key,
string titleLocalizationKey,
string? descriptionLocalizationKey = null,
IReadOnlyList<string>? defaultValue = null)
{
return AddOption(new SettingsOptionDefinition(
key,
SettingsOptionType.List,
titleLocalizationKey,
descriptionLocalizationKey,
defaultValue ?? Array.Empty<string>()));
}
internal PluginSettingsSectionRegistration Build()
{
return new PluginSettingsSectionRegistration(
Id,
TitleLocalizationKey,
_options.ToArray(),
DescriptionLocalizationKey,
IconKey,
SortOrder);
}
}

View File

@@ -0,0 +1,40 @@
using System.Collections.Generic;
namespace LanMountainDesktop.PluginSdk;
public sealed class PluginSettingsSectionRegistration
{
public PluginSettingsSectionRegistration(
string id,
string titleLocalizationKey,
IReadOnlyList<SettingsOptionDefinition> options,
string? descriptionLocalizationKey = null,
string iconKey = "PuzzlePiece",
int sortOrder = 0)
{
ArgumentException.ThrowIfNullOrWhiteSpace(id);
ArgumentException.ThrowIfNullOrWhiteSpace(titleLocalizationKey);
ArgumentException.ThrowIfNullOrWhiteSpace(iconKey);
Id = id.Trim();
TitleLocalizationKey = titleLocalizationKey.Trim();
DescriptionLocalizationKey = string.IsNullOrWhiteSpace(descriptionLocalizationKey)
? null
: descriptionLocalizationKey.Trim();
IconKey = iconKey.Trim();
SortOrder = sortOrder;
Options = options ?? [];
}
public string Id { get; }
public string TitleLocalizationKey { get; }
public string? DescriptionLocalizationKey { get; }
public string IconKey { get; }
public int SortOrder { get; }
public IReadOnlyList<SettingsOptionDefinition> Options { get; }
}

View File

@@ -0,0 +1,14 @@
namespace LanMountainDesktop.PluginSdk;
public static class SettingsCategories
{
public const string General = "General";
public const string Appearance = "Appearance";
public const string Components = "Components";
public const string Plugins = "Plugins";
public const string PluginMarket = "PluginMarket";
public const string Update = "Update";
public const string About = "About";
public const string Advanced = "Advanced";
public const string External = "External";
}

View File

@@ -0,0 +1,32 @@
using System.Collections.Generic;
namespace LanMountainDesktop.PluginSdk;
public sealed class SettingsChangedEvent
{
public SettingsChangedEvent(
SettingsScope scope,
string? subjectId = null,
string? placementId = null,
string? sectionId = null,
IReadOnlyCollection<string>? changedKeys = null)
{
Scope = scope;
SubjectId = string.IsNullOrWhiteSpace(subjectId) ? null : subjectId.Trim();
PlacementId = string.IsNullOrWhiteSpace(placementId) ? null : placementId.Trim();
SectionId = string.IsNullOrWhiteSpace(sectionId) ? null : sectionId.Trim();
ChangedKeys = changedKeys is { Count: > 0 }
? changedKeys.ToArray()
: [];
}
public SettingsScope Scope { get; }
public string? SubjectId { get; }
public string? PlacementId { get; }
public string? SectionId { get; }
public IReadOnlyCollection<string> ChangedKeys { get; }
}

View File

@@ -0,0 +1,17 @@
namespace LanMountainDesktop.PluginSdk;
public sealed class SettingsOptionChoice
{
public SettingsOptionChoice(string value, string titleLocalizationKey)
{
ArgumentException.ThrowIfNullOrWhiteSpace(value);
ArgumentException.ThrowIfNullOrWhiteSpace(titleLocalizationKey);
Value = value.Trim();
TitleLocalizationKey = titleLocalizationKey.Trim();
}
public string Value { get; }
public string TitleLocalizationKey { get; }
}

View File

@@ -0,0 +1,53 @@
using System.Collections.Generic;
namespace LanMountainDesktop.PluginSdk;
public sealed class SettingsOptionDefinition
{
public SettingsOptionDefinition(
string key,
SettingsOptionType optionType,
string titleLocalizationKey,
string? descriptionLocalizationKey = null,
object? defaultValue = null,
IReadOnlyList<SettingsOptionChoice>? choices = null,
double? minimum = null,
double? maximum = null,
string? validationPattern = null)
{
ArgumentException.ThrowIfNullOrWhiteSpace(key);
ArgumentException.ThrowIfNullOrWhiteSpace(titleLocalizationKey);
Key = key.Trim();
OptionType = optionType;
TitleLocalizationKey = titleLocalizationKey.Trim();
DescriptionLocalizationKey = string.IsNullOrWhiteSpace(descriptionLocalizationKey)
? null
: descriptionLocalizationKey.Trim();
DefaultValue = defaultValue;
Choices = choices ?? [];
Minimum = minimum;
Maximum = maximum;
ValidationPattern = string.IsNullOrWhiteSpace(validationPattern)
? null
: validationPattern.Trim();
}
public string Key { get; }
public SettingsOptionType OptionType { get; }
public string TitleLocalizationKey { get; }
public string? DescriptionLocalizationKey { get; }
public object? DefaultValue { get; }
public IReadOnlyList<SettingsOptionChoice> Choices { get; }
public double? Minimum { get; }
public double? Maximum { get; }
public string? ValidationPattern { get; }
}

View File

@@ -0,0 +1,11 @@
namespace LanMountainDesktop.PluginSdk;
public enum SettingsOptionType
{
Toggle = 0,
Select = 1,
Text = 2,
Number = 3,
Path = 4,
List = 5
}

View File

@@ -0,0 +1,9 @@
namespace LanMountainDesktop.PluginSdk;
public enum SettingsScope
{
App = 0,
Launcher = 1,
Plugin = 2,
ComponentInstance = 3
}

View File

@@ -0,0 +1,53 @@
using System.Collections.Generic;
namespace LanMountainDesktop.PluginSdk;
public sealed class SettingsSectionDefinition
{
public SettingsSectionDefinition(
string id,
string category,
SettingsScope scope,
string titleLocalizationKey,
string? descriptionLocalizationKey = null,
string iconKey = "Settings",
int sortOrder = 0,
string? subjectId = null,
IReadOnlyList<SettingsOptionDefinition>? options = null)
{
ArgumentException.ThrowIfNullOrWhiteSpace(id);
ArgumentException.ThrowIfNullOrWhiteSpace(category);
ArgumentException.ThrowIfNullOrWhiteSpace(titleLocalizationKey);
ArgumentException.ThrowIfNullOrWhiteSpace(iconKey);
Id = id.Trim();
Category = category.Trim();
Scope = scope;
TitleLocalizationKey = titleLocalizationKey.Trim();
DescriptionLocalizationKey = string.IsNullOrWhiteSpace(descriptionLocalizationKey)
? null
: descriptionLocalizationKey.Trim();
IconKey = iconKey.Trim();
SortOrder = sortOrder;
SubjectId = string.IsNullOrWhiteSpace(subjectId) ? null : subjectId.Trim();
Options = options ?? [];
}
public string Id { get; }
public string Category { get; }
public SettingsScope Scope { get; }
public string TitleLocalizationKey { get; }
public string? DescriptionLocalizationKey { get; }
public string IconKey { get; }
public int SortOrder { get; }
public string? SubjectId { get; }
public IReadOnlyList<SettingsOptionDefinition> Options { get; }
}