2026-03-12 09:22:03 +08:00
|
|
|
using Avalonia.Controls;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
|
|
|
|
|
namespace LanMountainDesktop.PluginSdk;
|
|
|
|
|
|
|
|
|
|
public static class PluginServiceCollectionExtensions
|
|
|
|
|
{
|
2026-03-13 00:33:00 +08:00
|
|
|
public static IServiceCollection AddPluginSettingsSection(
|
2026-03-12 09:22:03 +08:00
|
|
|
this IServiceCollection services,
|
|
|
|
|
string id,
|
2026-03-13 00:33:00 +08:00
|
|
|
string titleLocalizationKey,
|
|
|
|
|
Action<PluginSettingsSectionBuilder> configure,
|
|
|
|
|
string? descriptionLocalizationKey = null,
|
|
|
|
|
string iconKey = "PuzzlePiece",
|
2026-03-12 09:22:03 +08:00
|
|
|
int sortOrder = 0)
|
|
|
|
|
{
|
|
|
|
|
ArgumentNullException.ThrowIfNull(services);
|
2026-03-13 00:33:00 +08:00
|
|
|
ArgumentNullException.ThrowIfNull(configure);
|
2026-03-12 09:22:03 +08:00
|
|
|
|
2026-03-13 00:33:00 +08:00
|
|
|
var builder = new PluginSettingsSectionBuilder(
|
2026-03-12 09:22:03 +08:00
|
|
|
id,
|
2026-03-13 00:33:00 +08:00
|
|
|
titleLocalizationKey,
|
|
|
|
|
descriptionLocalizationKey,
|
|
|
|
|
iconKey,
|
|
|
|
|
sortOrder);
|
|
|
|
|
configure(builder);
|
|
|
|
|
services.AddSingleton(builder.Build());
|
2026-03-12 09:22:03 +08:00
|
|
|
return services;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static IServiceCollection AddPluginDesktopComponent<TControl>(
|
|
|
|
|
this IServiceCollection services,
|
|
|
|
|
string componentId,
|
|
|
|
|
string displayName,
|
|
|
|
|
string iconKey = "PuzzlePiece",
|
|
|
|
|
string category = "Plugins",
|
|
|
|
|
int minWidthCells = 2,
|
|
|
|
|
int minHeightCells = 2,
|
|
|
|
|
bool allowDesktopPlacement = true,
|
|
|
|
|
bool allowStatusBarPlacement = false,
|
|
|
|
|
PluginDesktopComponentResizeMode resizeMode = PluginDesktopComponentResizeMode.Proportional,
|
|
|
|
|
string? displayNameLocalizationKey = null,
|
|
|
|
|
Func<double, double>? cornerRadiusResolver = null)
|
|
|
|
|
where TControl : Control
|
|
|
|
|
{
|
|
|
|
|
ArgumentNullException.ThrowIfNull(services);
|
|
|
|
|
|
|
|
|
|
services.AddSingleton(new PluginDesktopComponentRegistration(
|
|
|
|
|
componentId,
|
|
|
|
|
displayName,
|
|
|
|
|
(provider, context) => ActivatorUtilities.CreateInstance<TControl>(provider, context),
|
|
|
|
|
iconKey,
|
|
|
|
|
category,
|
|
|
|
|
minWidthCells,
|
|
|
|
|
minHeightCells,
|
|
|
|
|
allowDesktopPlacement,
|
|
|
|
|
allowStatusBarPlacement,
|
|
|
|
|
resizeMode,
|
|
|
|
|
displayNameLocalizationKey,
|
|
|
|
|
cornerRadiusResolver));
|
|
|
|
|
return services;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-14 22:45:09 +08:00
|
|
|
public static IServiceCollection AddPluginDesktopComponentEditor<TControl>(
|
|
|
|
|
this IServiceCollection services,
|
|
|
|
|
string componentId,
|
|
|
|
|
double preferredWidth = 720d,
|
|
|
|
|
double preferredHeight = 540d,
|
|
|
|
|
double minScale = 0.85d,
|
|
|
|
|
double maxScale = 1.45d)
|
|
|
|
|
where TControl : Control
|
|
|
|
|
{
|
|
|
|
|
ArgumentNullException.ThrowIfNull(services);
|
|
|
|
|
|
|
|
|
|
services.AddSingleton(new PluginDesktopComponentEditorRegistration(
|
|
|
|
|
componentId,
|
|
|
|
|
(provider, context) => ActivatorUtilities.CreateInstance<TControl>(provider, context),
|
|
|
|
|
preferredWidth,
|
|
|
|
|
preferredHeight,
|
|
|
|
|
minScale,
|
|
|
|
|
maxScale));
|
|
|
|
|
return services;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-12 09:22:03 +08:00
|
|
|
public static IServiceCollection AddPluginExport<TContract, TImplementation>(this IServiceCollection services)
|
|
|
|
|
where TContract : class
|
|
|
|
|
where TImplementation : class, TContract
|
|
|
|
|
{
|
|
|
|
|
ArgumentNullException.ThrowIfNull(services);
|
|
|
|
|
|
|
|
|
|
EnsureSingletonRegistration<TContract, TImplementation>(services);
|
|
|
|
|
|
|
|
|
|
if (!services.Any(descriptor =>
|
|
|
|
|
descriptor.ServiceType == typeof(PluginServiceExportRegistration) &&
|
|
|
|
|
descriptor.ImplementationInstance is PluginServiceExportRegistration existing &&
|
|
|
|
|
existing.ContractType == typeof(TContract) &&
|
|
|
|
|
existing.ImplementationType == typeof(TImplementation)))
|
|
|
|
|
{
|
|
|
|
|
services.AddSingleton(new PluginServiceExportRegistration(typeof(TContract), typeof(TImplementation)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return services;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void EnsureSingletonRegistration<TContract, TImplementation>(IServiceCollection services)
|
|
|
|
|
where TContract : class
|
|
|
|
|
where TImplementation : class, TContract
|
|
|
|
|
{
|
|
|
|
|
var contractDescriptor = services.LastOrDefault(descriptor => descriptor.ServiceType == typeof(TContract));
|
|
|
|
|
if (contractDescriptor is null)
|
|
|
|
|
{
|
|
|
|
|
services.AddSingleton<TContract, TImplementation>();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (contractDescriptor.Lifetime != ServiceLifetime.Singleton)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Exported contract '{typeof(TContract).FullName}' must be registered as Singleton.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|