Compare commits

...

1 Commits

Author SHA1 Message Date
lincube
915739ff7b 0.6.9
改变无声
2026-03-20 00:41:14 +08:00
82 changed files with 977 additions and 225 deletions

8
Directory.Build.props Normal file
View File

@@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<Version>1.0.0</Version>
<TargetFramework Condition="'$(TargetFramework)' == ''">net10.0</TargetFramework>
<Nullable Condition="'$(Nullable)' == ''">enable</Nullable>
<ImplicitUsings Condition="'$(ImplicitUsings)' == ''">enable</ImplicitUsings>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,27 @@
using Avalonia;
using LanMountainDesktop.Settings.Core;
using LanMountainDesktop.Shared.Contracts;
namespace LanMountainDesktop.Appearance;
public static class AppearanceCornerRadiusTokenFactory
{
public static AppearanceCornerRadiusTokens Create(double scale)
{
var normalizedScale = GlobalAppearanceSettings.NormalizeCornerRadiusScale(scale);
return new AppearanceCornerRadiusTokens(
Radius(6, normalizedScale),
Radius(10, normalizedScale),
Radius(14, normalizedScale),
Radius(18, normalizedScale),
Radius(24, normalizedScale),
Radius(30, normalizedScale),
Radius(36, normalizedScale));
}
private static CornerRadius Radius(double value, double scale)
{
var scaled = Math.Round(value * scale * 2, MidpointRounding.AwayFromZero) / 2d;
return new CornerRadius(scaled);
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LanMountainDesktop.PluginSdk\LanMountainDesktop.PluginSdk.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Settings.Core\LanMountainDesktop.Settings.Core.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Host.Abstractions\LanMountainDesktop.Host.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LanMountainDesktop.PluginSdk\LanMountainDesktop.PluginSdk.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Settings.Core\LanMountainDesktop.Settings.Core.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Appearance\LanMountainDesktop.Appearance.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Host.Abstractions\LanMountainDesktop.Host.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,27 @@
using System;
using Avalonia;
namespace LanMountainDesktop.DesktopHost;
public static class DesktopBootstrap
{
public static void InitializeStartupServices(Action initializeDeviceId, Action initializeCrashReporting, Action initializeUserBehaviorAnalytics, Action scheduleStartupCleanup)
{
ArgumentNullException.ThrowIfNull(initializeDeviceId);
ArgumentNullException.ThrowIfNull(initializeCrashReporting);
ArgumentNullException.ThrowIfNull(initializeUserBehaviorAnalytics);
ArgumentNullException.ThrowIfNull(scheduleStartupCleanup);
initializeDeviceId();
initializeCrashReporting();
initializeUserBehaviorAnalytics();
scheduleStartupCleanup();
}
public static void InitializeApplication(Application application, Action initializeShell)
{
ArgumentNullException.ThrowIfNull(application);
ArgumentNullException.ThrowIfNull(initializeShell);
initializeShell();
}
}

View File

@@ -0,0 +1,55 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using LanMountainDesktop.Host.Abstractions;
namespace LanMountainDesktop.DesktopHost;
public sealed class DesktopShellHost : IDesktopShellHost
{
private readonly Action _initializePluginRuntime;
private readonly Action _initializeTrayIcon;
private readonly Action<IClassicDesktopStyleApplicationLifetime> _createAndAssignMainWindow;
private readonly Action _performExitCleanup;
private readonly Action _startActivationListener;
private readonly Action _startWeatherRefresh;
public DesktopShellHost(
Action initializePluginRuntime,
Action initializeTrayIcon,
Action<IClassicDesktopStyleApplicationLifetime> createAndAssignMainWindow,
Action performExitCleanup,
Action startActivationListener,
Action startWeatherRefresh)
{
_initializePluginRuntime = initializePluginRuntime;
_initializeTrayIcon = initializeTrayIcon;
_createAndAssignMainWindow = createAndAssignMainWindow;
_performExitCleanup = performExitCleanup;
_startActivationListener = startActivationListener;
_startWeatherRefresh = startWeatherRefresh;
}
public void Initialize()
{
throw new InvalidOperationException("An application instance is required to initialize the desktop shell.");
}
public void Initialize(Application application)
{
ArgumentNullException.ThrowIfNull(application);
_initializePluginRuntime();
_initializeTrayIcon();
if (application.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.Exit += (_, _) => _performExitCleanup();
_createAndAssignMainWindow(desktop);
_startActivationListener();
}
_startWeatherRefresh();
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace LanMountainDesktop.DesktopHost;
public sealed class DesktopStartupCoordinator
{
private readonly Action _restoreWorkspaceState;
public DesktopStartupCoordinator(Action restoreWorkspaceState)
{
_restoreWorkspaceState = restoreWorkspaceState ?? throw new ArgumentNullException(nameof(restoreWorkspaceState));
}
public void Restore() => _restoreWorkspaceState();
}

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.12" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LanMountainDesktop.PluginSdk\LanMountainDesktop.PluginSdk.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Settings.Core\LanMountainDesktop.Settings.Core.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Appearance\LanMountainDesktop.Appearance.csproj" />
<ProjectReference Include="..\LanMountainDesktop.DesktopComponents.Runtime\LanMountainDesktop.DesktopComponents.Runtime.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Host.Abstractions\LanMountainDesktop.Host.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,18 @@
using System;
namespace LanMountainDesktop.DesktopHost;
public sealed class SettingsWindowHost
{
private readonly Action<string, string?> _openSettingsWindow;
public SettingsWindowHost(Action<string, string?> openSettingsWindow)
{
_openSettingsWindow = openSettingsWindow ?? throw new ArgumentNullException(nameof(openSettingsWindow));
}
public void Open(string source, string? pageId = null)
{
_openSettingsWindow(source, pageId);
}
}

View File

@@ -0,0 +1,19 @@
using System;
namespace LanMountainDesktop.DesktopHost;
public sealed class ShutdownCoordinator
{
private readonly Action<bool, string> _prepareForShutdown;
private readonly Action<string> _resetShutdownIntent;
public ShutdownCoordinator(Action<bool, string> prepareForShutdown, Action<string> resetShutdownIntent)
{
_prepareForShutdown = prepareForShutdown ?? throw new ArgumentNullException(nameof(prepareForShutdown));
_resetShutdownIntent = resetShutdownIntent ?? throw new ArgumentNullException(nameof(resetShutdownIntent));
}
public void Prepare(bool isRestart, string source) => _prepareForShutdown(isRestart, source);
public void Reset(string source) => _resetShutdownIntent(source);
}

View File

@@ -0,0 +1,12 @@
using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Shared.Contracts;
namespace LanMountainDesktop.Host.Abstractions;
public sealed record ComponentChromeContext(
string ComponentId,
string? PlacementId,
double CellSize,
double GlobalCornerRadiusScale,
AppearanceCornerRadiusTokens CornerRadiusTokens,
SettingsScope Scope = SettingsScope.App);

View File

@@ -0,0 +1,6 @@
namespace LanMountainDesktop.Host.Abstractions;
public interface IDesktopShellHost
{
void Initialize();
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LanMountainDesktop.PluginSdk\LanMountainDesktop.PluginSdk.csproj" />
</ItemGroup>
</Project>

View File

@@ -12,6 +12,7 @@
<PackageReference Include="Avalonia" Version="11.3.12" /> <PackageReference Include="Avalonia" Version="11.3.12" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
<ProjectReference Include="..\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,3 +1,5 @@
using LanMountainDesktop.Shared.Contracts;
namespace LanMountainDesktop.PluginSdk; namespace LanMountainDesktop.PluginSdk;
public sealed class PluginDesktopComponentContext public sealed class PluginDesktopComponentContext
@@ -11,6 +13,8 @@ public sealed class PluginDesktopComponentContext
string componentId, string componentId,
string? placementId, string? placementId,
double cellSize, double cellSize,
double globalCornerRadiusScale,
AppearanceCornerRadiusTokens cornerRadiusTokens,
IPluginSettingsService? pluginSettings = null) IPluginSettingsService? pluginSettings = null)
{ {
ArgumentNullException.ThrowIfNull(manifest); ArgumentNullException.ThrowIfNull(manifest);
@@ -19,6 +23,7 @@ public sealed class PluginDesktopComponentContext
ArgumentException.ThrowIfNullOrWhiteSpace(componentId); ArgumentException.ThrowIfNullOrWhiteSpace(componentId);
ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(properties); ArgumentNullException.ThrowIfNull(properties);
ArgumentNullException.ThrowIfNull(cornerRadiusTokens);
Manifest = manifest; Manifest = manifest;
PluginDirectory = pluginDirectory; PluginDirectory = pluginDirectory;
@@ -28,6 +33,8 @@ public sealed class PluginDesktopComponentContext
ComponentId = componentId.Trim(); ComponentId = componentId.Trim();
PlacementId = string.IsNullOrWhiteSpace(placementId) ? null : placementId.Trim(); PlacementId = string.IsNullOrWhiteSpace(placementId) ? null : placementId.Trim();
CellSize = Math.Max(1, cellSize); CellSize = Math.Max(1, cellSize);
GlobalCornerRadiusScale = Math.Max(0.1d, globalCornerRadiusScale);
CornerRadiusTokens = cornerRadiusTokens;
PluginSettings = pluginSettings; PluginSettings = pluginSettings;
} }
@@ -47,8 +54,22 @@ public sealed class PluginDesktopComponentContext
public double CellSize { get; } public double CellSize { get; }
public double GlobalCornerRadiusScale { get; }
public AppearanceCornerRadiusTokens CornerRadiusTokens { get; }
public IPluginSettingsService? PluginSettings { get; } public IPluginSettingsService? PluginSettings { get; }
public double ResolveScaledCornerRadius(double baseRadius, double? minimum = null, double? maximum = null)
{
var scaled = Math.Max(0d, baseRadius) * GlobalCornerRadiusScale;
var scaledMin = minimum.HasValue ? minimum.Value * GlobalCornerRadiusScale : scaled;
var scaledMax = maximum.HasValue ? maximum.Value * GlobalCornerRadiusScale : scaled;
return minimum.HasValue || maximum.HasValue
? Math.Clamp(scaled, scaledMin, scaledMax)
: scaled;
}
public T? GetService<T>() public T? GetService<T>()
{ {
return (T?)Services.GetService(typeof(T)); return (T?)Services.GetService(typeof(T));

View File

@@ -0,0 +1,20 @@
namespace LanMountainDesktop.Settings.Core;
public static class GlobalAppearanceSettings
{
public const double DefaultCornerRadiusScale = 1.0;
public const double MinimumCornerRadiusScale = 0.70;
public const double MaximumCornerRadiusScale = 1.40;
public const double CornerRadiusScaleStep = 0.05;
public static double NormalizeCornerRadiusScale(double value)
{
if (double.IsNaN(value) || double.IsInfinity(value))
{
return DefaultCornerRadiusScale;
}
var clamped = Math.Clamp(value, MinimumCornerRadiusScale, MaximumCornerRadiusScale);
return Math.Round(clamped / CornerRadiusScaleStep, MidpointRounding.AwayFromZero) * CornerRadiusScaleStep;
}
}

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LanMountainDesktop.PluginSdk\LanMountainDesktop.PluginSdk.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,12 @@
using Avalonia;
namespace LanMountainDesktop.Shared.Contracts;
public sealed record AppearanceCornerRadiusTokens(
CornerRadius Micro,
CornerRadius Xs,
CornerRadius Sm,
CornerRadius Md,
CornerRadius Lg,
CornerRadius Xl,
CornerRadius Island);

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.12" />
</ItemGroup>
</Project>

View File

@@ -1,5 +1,11 @@
<Solution> <Solution>
<Project Path="LanAirApp/tools/LanMountainDesktop.PluginPackager/LanMountainDesktop.PluginPackager.csproj" /> <Project Path="LanAirApp/tools/LanMountainDesktop.PluginPackager/LanMountainDesktop.PluginPackager.csproj" />
<Project Path="LanMountainDesktop.Host.Abstractions/LanMountainDesktop.Host.Abstractions.csproj" />
<Project Path="LanMountainDesktop.Shared.Contracts/LanMountainDesktop.Shared.Contracts.csproj" />
<Project Path="LanMountainDesktop.Settings.Core/LanMountainDesktop.Settings.Core.csproj" />
<Project Path="LanMountainDesktop.Appearance/LanMountainDesktop.Appearance.csproj" />
<Project Path="LanMountainDesktop.DesktopComponents.Runtime/LanMountainDesktop.DesktopComponents.Runtime.csproj" />
<Project Path="LanMountainDesktop.DesktopHost/LanMountainDesktop.DesktopHost.csproj" />
<Project Path="LanMountainDesktop.PluginSdk/LanMountainDesktop.PluginSdk.csproj" /> <Project Path="LanMountainDesktop.PluginSdk/LanMountainDesktop.PluginSdk.csproj" />
<Project Path="LanMountainDesktop.PluginsInstallHelper/LanMountainDesktop.PluginsInstallHelper.csproj" /> <Project Path="LanMountainDesktop.PluginsInstallHelper/LanMountainDesktop.PluginsInstallHelper.csproj" />
<Project Path="LanMountainDesktop/LanMountainDesktop.csproj" /> <Project Path="LanMountainDesktop/LanMountainDesktop.csproj" />

View File

@@ -15,6 +15,7 @@ using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using AvaloniaWebView; using AvaloniaWebView;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.DesktopHost;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -61,6 +62,7 @@ public partial class App : Application
private MainWindow? _mainWindow; private MainWindow? _mainWindow;
private bool _mainWindowClosed; private bool _mainWindowClosed;
private bool _uiUnhandledExceptionHooked; private bool _uiUnhandledExceptionHooked;
private DesktopShellHost? _desktopShellHost;
internal static SingleInstanceService? CurrentSingleInstanceService { get; set; } internal static SingleInstanceService? CurrentSingleInstanceService { get; set; }
internal static (UserBehaviorAnalyticsService?, CrashReportService?) AnalyticsServices { get; set; } internal static (UserBehaviorAnalyticsService?, CrashReportService?) AnalyticsServices { get; set; }
@@ -116,28 +118,32 @@ public partial class App : Application
AppLogger.Info("App", "Framework initialization completed."); AppLogger.Info("App", "Framework initialization completed.");
RegisterUiUnhandledExceptionGuard(); RegisterUiUnhandledExceptionGuard();
LinuxDesktopEntryInstaller.EnsureInstalled(); LinuxDesktopEntryInstaller.EnsureInstalled();
InitializePluginRuntime(); DesktopBootstrap.InitializeApplication(this, InitializeDesktopShell);
InitializeTrayIcon();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) base.OnFrameworkInitializationCompleted();
}
private void InitializeDesktopShell()
{
_desktopShellHost ??= new DesktopShellHost(
InitializePluginRuntime,
InitializeTrayIcon,
desktop =>
{ {
// Avoid duplicate validations from both Avalonia and the CommunityToolkit. // Avoid duplicate validations from both Avalonia and the CommunityToolkit.
// More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
DisableAvaloniaDataAnnotationValidation(); DisableAvaloniaDataAnnotationValidation();
desktop.ShutdownMode = Avalonia.Controls.ShutdownMode.OnExplicitShutdown; desktop.ShutdownMode = Avalonia.Controls.ShutdownMode.OnExplicitShutdown;
desktop.Exit += (_, _) => CreateAndAssignMainWindow(desktop, "FrameworkInitialization");
},
() =>
{ {
AppLogger.Info("App", "Desktop lifetime exit triggered."); AppLogger.Info("App", "Desktop lifetime exit triggered.");
PerformExitCleanup(); PerformExitCleanup();
}; },
() => CurrentSingleInstanceService?.StartActivationListener(ActivateMainWindow),
CreateAndAssignMainWindow(desktop, "FrameworkInitialization"); StartWeatherLocationRefreshIfNeeded);
CurrentSingleInstanceService?.StartActivationListener(ActivateMainWindow); _desktopShellHost.Initialize(this);
}
StartWeatherLocationRefreshIfNeeded();
base.OnFrameworkInitializationCompleted();
} }
private void OnTrayExitClick(object? sender, EventArgs e) private void OnTrayExitClick(object? sender, EventArgs e)
@@ -493,6 +499,7 @@ public partial class App : Application
refreshAll || refreshAll ||
changedKeys.Contains(nameof(AppSettingsSnapshot.IsNightMode), StringComparer.OrdinalIgnoreCase) || changedKeys.Contains(nameof(AppSettingsSnapshot.IsNightMode), StringComparer.OrdinalIgnoreCase) ||
changedKeys.Contains(nameof(AppSettingsSnapshot.UseSystemChrome), StringComparer.OrdinalIgnoreCase) || changedKeys.Contains(nameof(AppSettingsSnapshot.UseSystemChrome), StringComparer.OrdinalIgnoreCase) ||
changedKeys.Contains(nameof(AppSettingsSnapshot.GlobalCornerRadiusScale), StringComparer.OrdinalIgnoreCase) ||
(string.Equals(liveAppearance.ThemeColorMode, ThemeAppearanceValues.ColorModeSeedMonet, StringComparison.OrdinalIgnoreCase) && (string.Equals(liveAppearance.ThemeColorMode, ThemeAppearanceValues.ColorModeSeedMonet, StringComparison.OrdinalIgnoreCase) &&
changedKeys.Contains(nameof(AppSettingsSnapshot.ThemeColor), StringComparer.OrdinalIgnoreCase)) || changedKeys.Contains(nameof(AppSettingsSnapshot.ThemeColor), StringComparer.OrdinalIgnoreCase)) ||
(string.Equals(liveAppearance.ThemeColorMode, ThemeAppearanceValues.ColorModeWallpaperMonet, StringComparison.OrdinalIgnoreCase) && (string.Equals(liveAppearance.ThemeColorMode, ThemeAppearanceValues.ColorModeWallpaperMonet, StringComparison.OrdinalIgnoreCase) &&

View File

@@ -1,3 +1,4 @@
using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Services.Settings; using LanMountainDesktop.Services.Settings;
@@ -10,5 +11,6 @@ public sealed record DesktopComponentRuntimeContext(
ISettingsFacadeService SettingsFacade, ISettingsFacadeService SettingsFacade,
ISettingsService SettingsService, ISettingsService SettingsService,
IAppearanceThemeService AppearanceTheme, IAppearanceThemeService AppearanceTheme,
ComponentChromeContext Chrome,
IComponentSettingsAccessor ComponentSettingsAccessor, IComponentSettingsAccessor ComponentSettingsAccessor,
IComponentInstanceSettingsStore ComponentSettingsStore); IComponentInstanceSettingsStore ComponentSettingsStore);

View File

@@ -0,0 +1,8 @@
using LanMountainDesktop.Host.Abstractions;
namespace LanMountainDesktop.ComponentSystem;
public interface IComponentChromeContextAware
{
void SetComponentChromeContext(ComponentChromeContext context);
}

View File

@@ -29,6 +29,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\LanMountainDesktop.Host.Abstractions\LanMountainDesktop.Host.Abstractions.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Settings.Core\LanMountainDesktop.Settings.Core.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Appearance\LanMountainDesktop.Appearance.csproj" />
<ProjectReference Include="..\LanMountainDesktop.DesktopComponents.Runtime\LanMountainDesktop.DesktopComponents.Runtime.csproj" />
<ProjectReference Include="..\LanMountainDesktop.DesktopHost\LanMountainDesktop.DesktopHost.csproj" />
<ProjectReference Include="..\LanMountainDesktop.PluginSdk\LanMountainDesktop.PluginSdk.csproj" /> <ProjectReference Include="..\LanMountainDesktop.PluginSdk\LanMountainDesktop.PluginSdk.csproj" />
<ProjectReference Include="..\LanMountainDesktop.PluginsInstallHelper\LanMountainDesktop.PluginsInstallHelper.csproj" ReferenceOutputAssembly="false" /> <ProjectReference Include="..\LanMountainDesktop.PluginsInstallHelper\LanMountainDesktop.PluginsInstallHelper.csproj" ReferenceOutputAssembly="false" />
</ItemGroup> </ItemGroup>

View File

@@ -25,7 +25,7 @@
"settings.nav.group_system": "System", "settings.nav.group_system": "System",
"settings.nav.group_extensions": "Extensions", "settings.nav.group_extensions": "Extensions",
"settings.nav.wallpaper": "Wallpaper", "settings.nav.wallpaper": "Wallpaper",
"settings.nav.grid": "Grid", "settings.nav.grid": "Components",
"settings.nav.color": "Color", "settings.nav.color": "Color",
"settings.nav.status_bar": "Status Bar", "settings.nav.status_bar": "Status Bar",
"settings.nav.weather": "Weather", "settings.nav.weather": "Weather",
@@ -303,8 +303,17 @@
"settings.status_bar.clock_format.hm": "Hour:Minute", "settings.status_bar.clock_format.hm": "Hour:Minute",
"settings.status_bar.clock_format.hms": "Hour:Minute:Second", "settings.status_bar.clock_format.hms": "Hour:Minute:Second",
"settings.components.title": "Components", "settings.components.title": "Components",
"settings.components.description": "Adjust desktop grid density and widget placement.", "settings.components.description": "Adjust component layout and corner design.",
"settings.components.grid_header": "Grid Layout", "settings.components.grid_header": "Grid Settings",
"settings.components.header": "Grid Settings",
"settings.components.short_side_label": "Short Side Cells",
"settings.components.edge_inset_label": "Screen Inset",
"settings.components.spacing_label": "Component Spacing",
"settings.components.spacing_compact": "Compact",
"settings.components.spacing_relaxed": "Relaxed",
"settings.components.corner_radius.header": "Corner Design",
"settings.components.corner_radius.label": "Component Corner Radius",
"settings.components.corner_radius.description": "Adjust the shared corner radius used by component containers, and expand the internal safe area with it.",
"settings.update.title": "Update", "settings.update.title": "Update",
"settings.update.current_version_label": "Current Version", "settings.update.current_version_label": "Current Version",
"settings.update.latest_version_label": "Latest Release", "settings.update.latest_version_label": "Latest Release",

View File

@@ -25,7 +25,7 @@
"settings.nav.group_system": "系统", "settings.nav.group_system": "系统",
"settings.nav.group_extensions": "扩展", "settings.nav.group_extensions": "扩展",
"settings.nav.wallpaper": "壁纸", "settings.nav.wallpaper": "壁纸",
"settings.nav.grid": "网格", "settings.nav.grid": "组件",
"settings.nav.color": "颜色", "settings.nav.color": "颜色",
"settings.nav.status_bar": "状态栏", "settings.nav.status_bar": "状态栏",
"settings.nav.weather": "天气", "settings.nav.weather": "天气",
@@ -301,9 +301,18 @@
"settings.status_bar.clock_format_label": "时钟格式", "settings.status_bar.clock_format_label": "时钟格式",
"settings.status_bar.clock_format.hm": "时:分", "settings.status_bar.clock_format.hm": "时:分",
"settings.status_bar.clock_format.hms": "时:分:秒", "settings.status_bar.clock_format.hms": "时:分:秒",
"settings.components.title": "网格", "settings.components.title": "组件",
"settings.components.description": "调整桌面网格与布局。", "settings.components.description": "调整组件布局与圆角设计。",
"settings.components.grid_header": "网格布局", "settings.components.grid_header": "网格设置",
"settings.components.header": "网格设置",
"settings.components.short_side_label": "短边格数",
"settings.components.edge_inset_label": "屏幕边距",
"settings.components.spacing_label": "组件间距",
"settings.components.spacing_compact": "紧凑",
"settings.components.spacing_relaxed": "宽松",
"settings.components.corner_radius.header": "圆角设计",
"settings.components.corner_radius.label": "组件圆角",
"settings.components.corner_radius.description": "统一调整组件容器圆角,并随圆角增大同步扩展内部安全区。",
"settings.update.title": "更新", "settings.update.title": "更新",
"settings.update.current_version_label": "当前版本", "settings.update.current_version_label": "当前版本",
"settings.update.latest_version_label": "最新发布", "settings.update.latest_version_label": "最新发布",

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using LanMountainDesktop.Settings.Core;
namespace LanMountainDesktop.Models; namespace LanMountainDesktop.Models;
@@ -16,6 +17,8 @@ public sealed class AppSettingsSnapshot
public bool UseSystemChrome { get; set; } public bool UseSystemChrome { get; set; }
public double GlobalCornerRadiusScale { get; set; } = GlobalAppearanceSettings.DefaultCornerRadiusScale;
public string ThemeColorMode { get; set; } = "default_neutral"; public string ThemeColorMode { get; set; } = "default_neutral";
public string SystemMaterialMode { get; set; } = "none"; public string SystemMaterialMode { get; set; } = "none";

View File

@@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia; using Avalonia;
using Avalonia.WebView.Desktop; using Avalonia.WebView.Desktop;
using LanMountainDesktop.DesktopHost;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Services.Settings; using LanMountainDesktop.Services.Settings;
@@ -20,9 +21,11 @@ sealed class Program
{ {
AppLogger.Initialize(); AppLogger.Initialize();
RegisterGlobalExceptionLogging(); RegisterGlobalExceptionLogging();
InitializeDeviceId(); DesktopBootstrap.InitializeStartupServices(
InitializeCrashReporting(); InitializeDeviceId,
InitializeUserBehaviorAnalytics(); InitializeCrashReporting,
InitializeUserBehaviorAnalytics,
ScheduleWhiteboardNoteStartupCleanup);
var restartParentProcessId = AppRestartService.TryGetRestartParentProcessId(args); var restartParentProcessId = AppRestartService.TryGetRestartParentProcessId(args);
using var singleInstance = AcquireSingleInstance(restartParentProcessId); using var singleInstance = AcquireSingleInstance(restartParentProcessId);
@@ -43,7 +46,6 @@ sealed class Program
var diagnostics = StartupDiagnosticsService.Run(args); var diagnostics = StartupDiagnosticsService.Run(args);
StartupDiagnosticsService.ShowLegacyExecutableWarningIfNeeded(diagnostics); StartupDiagnosticsService.ShowLegacyExecutableWarningIfNeeded(diagnostics);
ScheduleWhiteboardNoteStartupCleanup();
try try
{ {

View File

@@ -11,9 +11,12 @@ using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using LanMountainDesktop.Appearance;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services.Settings; using LanMountainDesktop.Services.Settings;
using LanMountainDesktop.Settings.Core;
using LanMountainDesktop.Shared.Contracts;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
using Microsoft.Win32; using Microsoft.Win32;
@@ -41,6 +44,8 @@ public sealed record AppearanceThemeSnapshot(
string ThemeColorMode, string ThemeColorMode,
string? UserThemeColor, string? UserThemeColor,
string? SelectedWallpaperSeed, string? SelectedWallpaperSeed,
double GlobalCornerRadiusScale,
AppearanceCornerRadiusTokens CornerRadiusTokens,
string ResolvedSeedSource, string ResolvedSeedSource,
MonetPalette MonetPalette, MonetPalette MonetPalette,
Color AccentColor, Color AccentColor,
@@ -464,6 +469,13 @@ internal sealed class AppearanceThemeService : IAppearanceThemeService, IDisposa
var context = CreateThemeContext(snapshot); var context = CreateThemeContext(snapshot);
ThemeColorSystemService.ApplyThemeResources(resources, context); ThemeColorSystemService.ApplyThemeResources(resources, context);
GlassEffectService.ApplyGlassResources(resources, context); GlassEffectService.ApplyGlassResources(resources, context);
resources["DesignCornerRadiusMicro"] = snapshot.CornerRadiusTokens.Micro;
resources["DesignCornerRadiusXs"] = snapshot.CornerRadiusTokens.Xs;
resources["DesignCornerRadiusSm"] = snapshot.CornerRadiusTokens.Sm;
resources["DesignCornerRadiusMd"] = snapshot.CornerRadiusTokens.Md;
resources["DesignCornerRadiusLg"] = snapshot.CornerRadiusTokens.Lg;
resources["DesignCornerRadiusXl"] = snapshot.CornerRadiusTokens.Xl;
resources["DesignCornerRadiusIsland"] = snapshot.CornerRadiusTokens.Island;
} }
public AppearanceMaterialSurface GetMaterialSurface(MaterialSurfaceRole role) public AppearanceMaterialSurface GetMaterialSurface(MaterialSurfaceRole role)
@@ -538,6 +550,7 @@ internal sealed class AppearanceThemeService : IAppearanceThemeService, IDisposa
if (!refreshAll && if (!refreshAll &&
!changedKeys.Contains(nameof(AppSettingsSnapshot.IsNightMode), StringComparer.OrdinalIgnoreCase) && !changedKeys.Contains(nameof(AppSettingsSnapshot.IsNightMode), StringComparer.OrdinalIgnoreCase) &&
!changedKeys.Contains(nameof(AppSettingsSnapshot.UseSystemChrome), StringComparer.OrdinalIgnoreCase) && !changedKeys.Contains(nameof(AppSettingsSnapshot.UseSystemChrome), StringComparer.OrdinalIgnoreCase) &&
!changedKeys.Contains(nameof(AppSettingsSnapshot.GlobalCornerRadiusScale), StringComparer.OrdinalIgnoreCase) &&
!(respondsToThemeColor && !(respondsToThemeColor &&
changedKeys.Contains(nameof(AppSettingsSnapshot.ThemeColor), StringComparer.OrdinalIgnoreCase)) && changedKeys.Contains(nameof(AppSettingsSnapshot.ThemeColor), StringComparer.OrdinalIgnoreCase)) &&
!(respondsToWallpaper && !(respondsToWallpaper &&
@@ -559,6 +572,8 @@ internal sealed class AppearanceThemeService : IAppearanceThemeService, IDisposa
bool queueWallpaperPaletteBuild) bool queueWallpaperPaletteBuild)
{ {
var availableModes = _windowMaterialService.GetAvailableModes(); var availableModes = _windowMaterialService.GetAvailableModes();
var globalCornerRadiusScale = GlobalAppearanceSettings.NormalizeCornerRadiusScale(themeState.GlobalCornerRadiusScale);
var cornerRadiusTokens = AppearanceCornerRadiusTokenFactory.Create(globalCornerRadiusScale);
MonetPalette palette; MonetPalette palette;
IReadOnlyList<Color> wallpaperSeedCandidates; IReadOnlyList<Color> wallpaperSeedCandidates;
Color effectiveSeedColor; Color effectiveSeedColor;
@@ -598,6 +613,8 @@ internal sealed class AppearanceThemeService : IAppearanceThemeService, IDisposa
themeColorMode, themeColorMode,
themeState.ThemeColor, themeState.ThemeColor,
selectedWallpaperSeed, selectedWallpaperSeed,
globalCornerRadiusScale,
cornerRadiusTokens,
resolvedSeedSource, resolvedSeedSource,
palette, palette,
ResolveAccentColor(themeColorMode, themeState.ThemeColor, palette), ResolveAccentColor(themeColorMode, themeState.ThemeColor, palette),

View File

@@ -122,6 +122,7 @@ public static class DesktopComponentRegistryFactory
var pluginSettings = new PluginScopedSettingsService( var pluginSettings = new PluginScopedSettingsService(
contribution.Plugin.Manifest.Id, contribution.Plugin.Manifest.Id,
settingsService); settingsService);
var appearanceSnapshot = HostAppearanceThemeProvider.GetOrCreate().GetCurrent();
var pluginContext = new PluginDesktopComponentContext( var pluginContext = new PluginDesktopComponentContext(
contribution.Plugin.Manifest, contribution.Plugin.Manifest,
contribution.Plugin.Context.PluginDirectory, contribution.Plugin.Context.PluginDirectory,
@@ -131,6 +132,8 @@ public static class DesktopComponentRegistryFactory
contribution.Registration.ComponentId, contribution.Registration.ComponentId,
context.PlacementId, context.PlacementId,
context.CellSize, context.CellSize,
appearanceSnapshot.GlobalCornerRadiusScale,
appearanceSnapshot.CornerRadiusTokens,
pluginSettings); pluginSettings);
return contribution.Registration.ControlFactory(contribution.Plugin.Services, pluginContext); return contribution.Registration.ControlFactory(contribution.Plugin.Services, pluginContext);

View File

@@ -20,6 +20,7 @@ public sealed record ComponentLibraryCategoryEntry(
public sealed record ComponentLibraryCreateContext( public sealed record ComponentLibraryCreateContext(
double CellSize, double CellSize,
double GlobalCornerRadiusScale,
TimeZoneService TimeZoneService, TimeZoneService TimeZoneService,
IWeatherInfoService WeatherInfoService, IWeatherInfoService WeatherInfoService,
IRecommendationInfoService RecommendationInfoService, IRecommendationInfoService RecommendationInfoService,

View File

@@ -17,7 +17,7 @@ internal sealed class SettingsCatalogService : ISettingsCatalog
[ [
new SettingsSectionDefinition("general", SettingsCategories.General, SettingsScope.App, "settings.general.title", iconKey: "Settings", sortOrder: 0), new SettingsSectionDefinition("general", SettingsCategories.General, SettingsScope.App, "settings.general.title", iconKey: "Settings", sortOrder: 0),
new SettingsSectionDefinition("appearance", SettingsCategories.Appearance, SettingsScope.App, "settings.appearance.title", iconKey: "DesignIdeas", sortOrder: 10), new SettingsSectionDefinition("appearance", SettingsCategories.Appearance, SettingsScope.App, "settings.appearance.title", iconKey: "DesignIdeas", sortOrder: 10),
new SettingsSectionDefinition("components", SettingsCategories.Components, SettingsScope.ComponentInstance, "settings.components.title", iconKey: "GridDots", sortOrder: 20), new SettingsSectionDefinition("components", SettingsCategories.Components, SettingsScope.ComponentInstance, "settings.components.title", iconKey: "Apps", sortOrder: 20),
new SettingsSectionDefinition("plugins", SettingsCategories.Plugins, SettingsScope.Plugin, "settings.plugins.title", iconKey: "PuzzlePiece", sortOrder: 30), new SettingsSectionDefinition("plugins", SettingsCategories.Plugins, SettingsScope.Plugin, "settings.plugins.title", iconKey: "PuzzlePiece", sortOrder: 30),
new SettingsSectionDefinition("about", SettingsCategories.About, SettingsScope.App, "settings.about.title", iconKey: "Info", sortOrder: 40) new SettingsSectionDefinition("about", SettingsCategories.About, SettingsScope.App, "settings.about.title", iconKey: "Info", sortOrder: 40)
]); ]);

View File

@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Settings.Core;
namespace LanMountainDesktop.Services.Settings; namespace LanMountainDesktop.Services.Settings;
@@ -20,6 +21,7 @@ public sealed record ThemeAppearanceSettingsState(
bool IsNightMode, bool IsNightMode,
string? ThemeColor, string? ThemeColor,
bool UseSystemChrome, bool UseSystemChrome,
double GlobalCornerRadiusScale = GlobalAppearanceSettings.DefaultCornerRadiusScale,
string ThemeColorMode = ThemeAppearanceValues.ColorModeDefaultNeutral, string ThemeColorMode = ThemeAppearanceValues.ColorModeDefaultNeutral,
string SystemMaterialMode = ThemeAppearanceValues.MaterialNone, string SystemMaterialMode = ThemeAppearanceValues.MaterialNone,
string? SelectedWallpaperSeed = null); string? SelectedWallpaperSeed = null);

View File

@@ -10,6 +10,7 @@ using Avalonia.Media.Imaging;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Settings.Core;
using LanMountainDesktop.Services.PluginMarket; using LanMountainDesktop.Services.PluginMarket;
namespace LanMountainDesktop.Services.Settings; namespace LanMountainDesktop.Services.Settings;
@@ -242,6 +243,7 @@ internal sealed class ThemeAppearanceService : IThemeAppearanceService
snapshot.IsNightMode ?? false, snapshot.IsNightMode ?? false,
snapshot.ThemeColor, snapshot.ThemeColor,
snapshot.UseSystemChrome, snapshot.UseSystemChrome,
GlobalAppearanceSettings.NormalizeCornerRadiusScale(snapshot.GlobalCornerRadiusScale),
ThemeAppearanceValues.NormalizeThemeColorMode(snapshot.ThemeColorMode, snapshot.ThemeColor), ThemeAppearanceValues.NormalizeThemeColorMode(snapshot.ThemeColorMode, snapshot.ThemeColor),
ThemeAppearanceValues.NormalizeSystemMaterialMode(snapshot.SystemMaterialMode), ThemeAppearanceValues.NormalizeSystemMaterialMode(snapshot.SystemMaterialMode),
snapshot.SelectedWallpaperSeed); snapshot.SelectedWallpaperSeed);
@@ -252,6 +254,7 @@ internal sealed class ThemeAppearanceService : IThemeAppearanceService
var snapshot = _settingsService.Load(); var snapshot = _settingsService.Load();
var changedKeys = new List<string>(); var changedKeys = new List<string>();
var normalizedThemeColor = string.IsNullOrWhiteSpace(state.ThemeColor) ? null : state.ThemeColor; var normalizedThemeColor = string.IsNullOrWhiteSpace(state.ThemeColor) ? null : state.ThemeColor;
var normalizedCornerRadiusScale = GlobalAppearanceSettings.NormalizeCornerRadiusScale(state.GlobalCornerRadiusScale);
var normalizedThemeColorMode = ThemeAppearanceValues.NormalizeThemeColorMode(state.ThemeColorMode, state.ThemeColor); var normalizedThemeColorMode = ThemeAppearanceValues.NormalizeThemeColorMode(state.ThemeColorMode, state.ThemeColor);
var normalizedSystemMaterialMode = ThemeAppearanceValues.NormalizeSystemMaterialMode(state.SystemMaterialMode); var normalizedSystemMaterialMode = ThemeAppearanceValues.NormalizeSystemMaterialMode(state.SystemMaterialMode);
var normalizedSelectedWallpaperSeed = string.IsNullOrWhiteSpace(state.SelectedWallpaperSeed) var normalizedSelectedWallpaperSeed = string.IsNullOrWhiteSpace(state.SelectedWallpaperSeed)
@@ -276,6 +279,12 @@ internal sealed class ThemeAppearanceService : IThemeAppearanceService
changedKeys.Add(nameof(AppSettingsSnapshot.UseSystemChrome)); changedKeys.Add(nameof(AppSettingsSnapshot.UseSystemChrome));
} }
if (Math.Abs(GlobalAppearanceSettings.NormalizeCornerRadiusScale(snapshot.GlobalCornerRadiusScale) - normalizedCornerRadiusScale) > 0.0001d)
{
snapshot.GlobalCornerRadiusScale = normalizedCornerRadiusScale;
changedKeys.Add(nameof(AppSettingsSnapshot.GlobalCornerRadiusScale));
}
if (!string.Equals(snapshot.ThemeColorMode, normalizedThemeColorMode, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(snapshot.ThemeColorMode, normalizedThemeColorMode, StringComparison.OrdinalIgnoreCase))
{ {
snapshot.ThemeColorMode = normalizedThemeColorMode; snapshot.ThemeColorMode = normalizedThemeColorMode;

View File

@@ -18,7 +18,7 @@
<Setter Property="Background" Value="{DynamicResource EditorSelectFieldBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource EditorSelectFieldBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource EditorSelectOutlineBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource EditorSelectOutlineBrush}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="18" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusMd}" />
<Setter Property="Padding" Value="16,14,12,14" /> <Setter Property="Padding" Value="16,14,12,14" />
<Setter Property="MinHeight" Value="56" /> <Setter Property="MinHeight" Value="56" />
<Setter Property="FontSize" Value="14" /> <Setter Property="FontSize" Value="14" />
@@ -40,7 +40,7 @@
<Setter Property="Background" Value="{DynamicResource EditorSelectFieldBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource EditorSelectFieldBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource EditorSelectOutlineBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource EditorSelectOutlineBrush}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="18" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusMd}" />
<Setter Property="Padding" Value="16,14,12,14" /> <Setter Property="Padding" Value="16,14,12,14" />
<Setter Property="MinHeight" Value="56" /> <Setter Property="MinHeight" Value="56" />
<Setter Property="Foreground" Value="{DynamicResource ComponentEditorPrimaryTextBrush}" /> <Setter Property="Foreground" Value="{DynamicResource ComponentEditorPrimaryTextBrush}" />
@@ -61,7 +61,7 @@
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="16,12" /> <Setter Property="Padding" Value="16,12" />
<Setter Property="Margin" Value="6,4" /> <Setter Property="Margin" Value="6,4" />
<Setter Property="CornerRadius" Value="14" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusSm}" />
<Setter Property="MinHeight" Value="44" /> <Setter Property="MinHeight" Value="44" />
</Style> </Style>
@@ -100,7 +100,7 @@
<Setter Property="Background" Value="{DynamicResource EditorSurfaceContainerHighBrush}" /> <Setter Property="Background" Value="{DynamicResource EditorSurfaceContainerHighBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource EditorSelectOutlineBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource EditorSelectOutlineBrush}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="20" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusLg}" />
<Setter Property="Padding" Value="4" /> <Setter Property="Padding" Value="4" />
</Style> </Style>
@@ -108,7 +108,7 @@
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" /> <Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" /> <Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="16" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusSm}" />
<Setter Property="Padding" Value="18,12" /> <Setter Property="Padding" Value="18,12" />
<Setter Property="MinHeight" Value="48" /> <Setter Property="MinHeight" Value="48" />
<Setter Property="FontSize" Value="14" /> <Setter Property="FontSize" Value="14" />
@@ -139,14 +139,14 @@
<Setter Property="Background" Value="{DynamicResource ComponentEditorHeroBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource ComponentEditorHeroBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComponentEditorCardBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource ComponentEditorCardBorderBrush}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="28" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusXl}" />
</Style> </Style>
<Style Selector="Border.component-editor-card"> <Style Selector="Border.component-editor-card">
<Setter Property="Background" Value="{DynamicResource ComponentEditorCardBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource ComponentEditorCardBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComponentEditorCardBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource ComponentEditorCardBorderBrush}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="24" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusLg}" />
</Style> </Style>
<Style Selector="TextBlock.component-editor-headline"> <Style Selector="TextBlock.component-editor-headline">

View File

@@ -4,11 +4,13 @@
<Styles.Resources> <Styles.Resources>
<!-- Unified corner radius tokens used across settings and widget panels --> <!-- Unified corner radius tokens used across settings and widget panels -->
<CornerRadius x:Key="DesignCornerRadiusMicro">6</CornerRadius>
<CornerRadius x:Key="DesignCornerRadiusXl">32</CornerRadius> <CornerRadius x:Key="DesignCornerRadiusXl">32</CornerRadius>
<CornerRadius x:Key="DesignCornerRadiusLg">28</CornerRadius> <CornerRadius x:Key="DesignCornerRadiusLg">28</CornerRadius>
<CornerRadius x:Key="DesignCornerRadiusMd">20</CornerRadius> <CornerRadius x:Key="DesignCornerRadiusMd">20</CornerRadius>
<CornerRadius x:Key="DesignCornerRadiusSm">14</CornerRadius> <CornerRadius x:Key="DesignCornerRadiusSm">14</CornerRadius>
<CornerRadius x:Key="DesignCornerRadiusXs">12</CornerRadius> <CornerRadius x:Key="DesignCornerRadiusXs">12</CornerRadius>
<CornerRadius x:Key="DesignCornerRadiusIsland">36</CornerRadius>
</Styles.Resources> </Styles.Resources>
<Style Selector="TextBlock"> <Style Selector="TextBlock">
@@ -19,7 +21,7 @@
<Setter Property="Background" Value="{DynamicResource AdaptiveButtonBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource AdaptiveButtonBackgroundBrush}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveButtonBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource AdaptiveButtonBorderBrush}" />
<Setter Property="CornerRadius" Value="20" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusMd}" />
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}" /> <Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}" />
<Setter Property="FontSize" Value="14" /> <Setter Property="FontSize" Value="14" />
<Setter Property="Padding" Value="16,10" /> <Setter Property="Padding" Value="16,10" />
@@ -155,7 +157,7 @@
<Style Selector="Button.swatch-button"> <Style Selector="Button.swatch-button">
<Setter Property="BorderThickness" Value="0" /> <Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="16" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusXs}" />
<Setter Property="Opacity" Value="0.88" /> <Setter Property="Opacity" Value="0.88" />
</Style> </Style>
@@ -175,7 +177,7 @@
<Setter Property="Background" Value="{DynamicResource AdaptiveGlassPanelBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource AdaptiveGlassPanelBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassPanelBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassPanelBorderBrush}" />
<Setter Property="BorderThickness" Value="1.2" /> <Setter Property="BorderThickness" Value="1.2" />
<Setter Property="CornerRadius" Value="28" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusLg}" />
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassPanelOpacity}" /> <Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassPanelOpacity}" />
<Setter Property="BoxShadow" Value="0 4 12 #1A000000" /> <Setter Property="BoxShadow" Value="0 4 12 #1A000000" />
</Style> </Style>
@@ -184,7 +186,7 @@
<Setter Property="Background" Value="{DynamicResource AdaptiveGlassStrongBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource AdaptiveGlassStrongBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassStrongBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassStrongBorderBrush}" />
<Setter Property="BorderThickness" Value="1.5" /> <Setter Property="BorderThickness" Value="1.5" />
<Setter Property="CornerRadius" Value="32" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusXl}" />
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" /> <Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" />
<Setter Property="BoxShadow" Value="0 8 24 #26000000" /> <Setter Property="BoxShadow" Value="0 8 24 #26000000" />
</Style> </Style>
@@ -193,7 +195,7 @@
<Setter Property="Background" Value="{DynamicResource AdaptiveDockGlassBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource AdaptiveDockGlassBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveDockGlassBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource AdaptiveDockGlassBorderBrush}" />
<Setter Property="BorderThickness" Value="1.5" /> <Setter Property="BorderThickness" Value="1.5" />
<Setter Property="CornerRadius" Value="36" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusIsland}" />
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" /> <Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" />
<Setter Property="BoxShadow" Value="0 12 32 #33000000" /> <Setter Property="BoxShadow" Value="0 12 32 #33000000" />
<Setter Property="Transitions"> <Setter Property="Transitions">
@@ -206,7 +208,7 @@
<Style Selector="Border.surface-solid-strong"> <Style Selector="Border.surface-solid-strong">
<Setter Property="Background" Value="{DynamicResource AdaptiveGlassStrongBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource AdaptiveGlassStrongBackgroundBrush}" />
<Setter Property="BorderThickness" Value="0" /> <Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="36" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusIsland}" />
<Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" /> <Setter Property="Opacity" Value="{DynamicResource AdaptiveGlassStrongOpacity}" />
<Setter Property="BoxShadow" Value="0 8 22 #2A000000" /> <Setter Property="BoxShadow" Value="0 8 22 #2A000000" />
</Style> </Style>

View File

@@ -48,21 +48,21 @@
</Style> </Style>
<Style Selector="Border.settings-section-card"> <Style Selector="Border.settings-section-card">
<Setter Property="CornerRadius" Value="18" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusLg}" />
<Setter Property="Padding" Value="20" /> <Setter Property="Padding" Value="20" />
<Setter Property="Margin" Value="0,0,0,14" /> <Setter Property="Margin" Value="0,0,0,14" />
<Setter Property="BoxShadow" Value="0 2 8 #12000000" /> <Setter Property="BoxShadow" Value="0 2 8 #12000000" />
</Style> </Style>
<Style Selector="Border.settings-option-card"> <Style Selector="Border.settings-option-card">
<Setter Property="CornerRadius" Value="14" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusSm}" />
<Setter Property="Padding" Value="16" /> <Setter Property="Padding" Value="16" />
<Setter Property="Margin" Value="0,0,0,12" /> <Setter Property="Margin" Value="0,0,0,12" />
<Setter Property="BoxShadow" Value="0 1 4 #0F000000" /> <Setter Property="BoxShadow" Value="0 1 4 #0F000000" />
</Style> </Style>
<Style Selector="Border.settings-list-item"> <Style Selector="Border.settings-list-item">
<Setter Property="CornerRadius" Value="14" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusSm}" />
<Setter Property="Padding" Value="16" /> <Setter Property="Padding" Value="16" />
<Setter Property="Margin" Value="0,0,0,10" /> <Setter Property="Margin" Value="0,0,0,10" />
<Setter Property="BoxShadow" Value="0 1 4 #0F000000" /> <Setter Property="BoxShadow" Value="0 1 4 #0F000000" />
@@ -77,7 +77,7 @@
<Setter Property="Background" Value="{DynamicResource AdaptiveButtonBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource AdaptiveButtonBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveButtonBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource AdaptiveButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="12" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusXs}" />
<Setter Property="HorizontalAlignment" Value="Left" /> <Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Top" /> <Setter Property="VerticalAlignment" Value="Top" />
</Style> </Style>
@@ -201,7 +201,7 @@
<Setter Property="Background" Value="{DynamicResource AdaptiveSurfaceRaisedBrush}" /> <Setter Property="Background" Value="{DynamicResource AdaptiveSurfaceRaisedBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassPanelBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassPanelBorderBrush}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="12" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusXs}" />
<Setter Property="Padding" Value="14,12" /> <Setter Property="Padding" Value="14,12" />
<Setter Property="Margin" Value="0,0,0,8" /> <Setter Property="Margin" Value="0,0,0,8" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" /> <Setter Property="HorizontalContentAlignment" Value="Stretch" />
@@ -229,7 +229,7 @@
<Setter Property="Background" Value="{DynamicResource AdaptiveAccentBrush}" /> <Setter Property="Background" Value="{DynamicResource AdaptiveAccentBrush}" />
<Setter Property="Foreground" Value="{DynamicResource AdaptiveOnAccentBrush}" /> <Setter Property="Foreground" Value="{DynamicResource AdaptiveOnAccentBrush}" />
<Setter Property="BorderThickness" Value="0" /> <Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="10" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusXs}" />
<Setter Property="Padding" Value="16,10" /> <Setter Property="Padding" Value="16,10" />
<Setter Property="MinHeight" Value="36" /> <Setter Property="MinHeight" Value="36" />
</Style> </Style>
@@ -254,7 +254,7 @@
<Setter Property="Width" Value="36" /> <Setter Property="Width" Value="36" />
<Setter Property="Height" Value="36" /> <Setter Property="Height" Value="36" />
<Setter Property="Padding" Value="0" /> <Setter Property="Padding" Value="0" />
<Setter Property="CornerRadius" Value="10" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusXs}" />
<Setter Property="MinHeight" Value="36" /> <Setter Property="MinHeight" Value="36" />
<Setter Property="Background" Value="{DynamicResource AdaptiveButtonBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource AdaptiveButtonBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveButtonBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource AdaptiveButtonBorderBrush}" />

View File

@@ -12,6 +12,7 @@ using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Services.Settings; using LanMountainDesktop.Services.Settings;
using LanMountainDesktop.Settings.Core;
namespace LanMountainDesktop.ViewModels; namespace LanMountainDesktop.ViewModels;
@@ -481,6 +482,9 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
[ObservableProperty] [ObservableProperty]
private bool _useSystemChrome; private bool _useSystemChrome;
[ObservableProperty]
private double _globalCornerRadiusScale = GlobalAppearanceSettings.DefaultCornerRadiusScale;
[ObservableProperty] [ObservableProperty]
private SelectionOption _selectedThemeColorMode = new(ThemeAppearanceValues.ColorModeSeedMonet, "User theme color Monet"); private SelectionOption _selectedThemeColorMode = new(ThemeAppearanceValues.ColorModeSeedMonet, "User theme color Monet");
@@ -547,6 +551,12 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
[ObservableProperty] [ObservableProperty]
private string _systemMaterialLabel = string.Empty; private string _systemMaterialLabel = string.Empty;
[ObservableProperty]
private string _globalCornerRadiusLabel = string.Empty;
[ObservableProperty]
private string _globalCornerRadiusDescription = string.Empty;
[ObservableProperty] [ObservableProperty]
private string _themeHeader = string.Empty; private string _themeHeader = string.Empty;
@@ -668,6 +678,32 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
PersistCurrentState(restartRequired: false); PersistCurrentState(restartRequired: false);
} }
partial void OnGlobalCornerRadiusScaleChanged(double value)
{
if (_isInitializing)
{
return;
}
var normalized = GlobalAppearanceSettings.NormalizeCornerRadiusScale(value);
if (Math.Abs(normalized - value) > 0.0001d)
{
_isInitializing = true;
try
{
GlobalCornerRadiusScale = normalized;
}
finally
{
_isInitializing = false;
}
return;
}
PersistCurrentState(restartRequired: false);
}
partial void OnSelectedThemeColorModeChanged(SelectionOption value) partial void OnSelectedThemeColorModeChanged(SelectionOption value)
{ {
if (_isInitializing || value is null) if (_isInitializing || value is null)
@@ -732,6 +768,8 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
ThemeColorLabel = L("settings.color.theme_color_label", "Theme Accent Color"); ThemeColorLabel = L("settings.color.theme_color_label", "Theme Accent Color");
ThemeColorModeLabel = L("settings.appearance.theme_color_mode_label", "Theme color source"); ThemeColorModeLabel = L("settings.appearance.theme_color_mode_label", "Theme color source");
SystemMaterialLabel = L("settings.appearance.system_material_label", "System material"); SystemMaterialLabel = L("settings.appearance.system_material_label", "System material");
GlobalCornerRadiusLabel = L("settings.appearance.corner_radius.label", "Global corner radius");
GlobalCornerRadiusDescription = L("settings.appearance.corner_radius.description", "Adjust the shared radius scale used by cards, panels, and component containers.");
ThemeSourceNeutralText = L("settings.appearance.theme_color_mode.neutral", "Default neutral"); ThemeSourceNeutralText = L("settings.appearance.theme_color_mode.neutral", "Default neutral");
ThemeSourceUserColorText = L("settings.appearance.theme_color_mode.user", "User theme color Monet"); ThemeSourceUserColorText = L("settings.appearance.theme_color_mode.user", "User theme color Monet");
ThemeSourceWallpaperText = L("settings.appearance.theme_color_mode.wallpaper", "Wallpaper Monet"); ThemeSourceWallpaperText = L("settings.appearance.theme_color_mode.wallpaper", "Wallpaper Monet");
@@ -776,6 +814,7 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
IsNightMode = theme.IsNightMode; IsNightMode = theme.IsNightMode;
ThemeColor = theme.ThemeColor ?? string.Empty; ThemeColor = theme.ThemeColor ?? string.Empty;
UseSystemChrome = theme.UseSystemChrome; UseSystemChrome = theme.UseSystemChrome;
GlobalCornerRadiusScale = GlobalAppearanceSettings.NormalizeCornerRadiusScale(theme.GlobalCornerRadiusScale);
_selectedWallpaperSeed = theme.SelectedWallpaperSeed; _selectedWallpaperSeed = theme.SelectedWallpaperSeed;
SyncCustomSeedPickerWithSavedThemeColor(); SyncCustomSeedPickerWithSavedThemeColor();
@@ -825,6 +864,7 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
IsNightMode, IsNightMode,
themeColor, themeColor,
UseSystemChrome, UseSystemChrome,
GlobalAppearanceSettings.NormalizeCornerRadiusScale(GlobalCornerRadiusScale),
themeColorMode, themeColorMode,
ThemeAppearanceValues.NormalizeSystemMaterialMode(SelectedSystemMaterialMode?.Value), ThemeAppearanceValues.NormalizeSystemMaterialMode(SelectedSystemMaterialMode?.Value),
_selectedWallpaperSeed); _selectedWallpaperSeed);
@@ -956,7 +996,7 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase
private string _pageDescription = string.Empty; private string _pageDescription = string.Empty;
[ObservableProperty] [ObservableProperty]
private string _gridHeader = string.Empty; private string _componentsHeader = string.Empty;
[ObservableProperty] [ObservableProperty]
private string _shortSideCellsLabel = string.Empty; private string _shortSideCellsLabel = string.Empty;
@@ -967,6 +1007,18 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase
[ObservableProperty] [ObservableProperty]
private string _spacingPresetLabel = string.Empty; private string _spacingPresetLabel = string.Empty;
[ObservableProperty]
private double _globalCornerRadiusScale = GlobalAppearanceSettings.DefaultCornerRadiusScale;
[ObservableProperty]
private string _componentRadiusHeader = string.Empty;
[ObservableProperty]
private string _globalCornerRadiusLabel = string.Empty;
[ObservableProperty]
private string _globalCornerRadiusDescription = string.Empty;
public void Load() public void Load()
{ {
var state = _settingsFacade.Grid.Get(); var state = _settingsFacade.Grid.Get();
@@ -976,6 +1028,9 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase
SelectedSpacingPreset = SpacingPresets.FirstOrDefault(option => SelectedSpacingPreset = SpacingPresets.FirstOrDefault(option =>
string.Equals(option.Value, spacingPreset, StringComparison.OrdinalIgnoreCase)) string.Equals(option.Value, spacingPreset, StringComparison.OrdinalIgnoreCase))
?? SpacingPresets[1]; ?? SpacingPresets[1];
var theme = _settingsFacade.Theme.Get();
GlobalCornerRadiusScale = GlobalAppearanceSettings.NormalizeCornerRadiusScale(theme.GlobalCornerRadiusScale);
} }
partial void OnShortSideCellsChanged(int value) partial void OnShortSideCellsChanged(int value)
@@ -1008,6 +1063,32 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase
SaveGrid(); SaveGrid();
} }
partial void OnGlobalCornerRadiusScaleChanged(double value)
{
if (_isInitializing)
{
return;
}
var normalized = GlobalAppearanceSettings.NormalizeCornerRadiusScale(value);
if (Math.Abs(normalized - value) > 0.0001d)
{
_isInitializing = true;
try
{
GlobalCornerRadiusScale = normalized;
}
finally
{
_isInitializing = false;
}
return;
}
SaveComponentCornerRadius();
}
private void SaveGrid() private void SaveGrid()
{ {
_settingsFacade.Grid.Save(new GridSettingsState( _settingsFacade.Grid.Save(new GridSettingsState(
@@ -1016,23 +1097,39 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase
Math.Clamp(EdgeInsetPercent, 0, 30))); Math.Clamp(EdgeInsetPercent, 0, 30)));
} }
private void SaveComponentCornerRadius()
{
var theme = _settingsFacade.Theme.Get();
_settingsFacade.Theme.Save(new ThemeAppearanceSettingsState(
theme.IsNightMode,
theme.ThemeColor,
theme.UseSystemChrome,
GlobalAppearanceSettings.NormalizeCornerRadiusScale(GlobalCornerRadiusScale),
theme.ThemeColorMode,
theme.SystemMaterialMode,
theme.SelectedWallpaperSeed));
}
private IReadOnlyList<SelectionOption> CreateSpacingPresets() private IReadOnlyList<SelectionOption> CreateSpacingPresets()
{ {
return return
[ [
new SelectionOption("Compact", L("settings.grid.spacing_compact", "Compact")), new SelectionOption("Compact", L("settings.components.spacing_compact", "Compact")),
new SelectionOption("Relaxed", L("settings.grid.spacing_relaxed", "Relaxed")) new SelectionOption("Relaxed", L("settings.components.spacing_relaxed", "Relaxed"))
]; ];
} }
private void RefreshLocalizedText() private void RefreshLocalizedText()
{ {
PageTitle = L("settings.components.title", "Components"); PageTitle = L("settings.components.title", "Components");
PageDescription = L("settings.components.description", "Desktop grid and widget placement density."); PageDescription = L("settings.components.description", "Adjust component layout and corner design.");
GridHeader = L("settings.components.grid_header", "Grid Layout"); ComponentsHeader = L("settings.components.header", "Grid Settings");
ShortSideCellsLabel = L("settings.grid.short_side_label", "Short Side Cells"); ShortSideCellsLabel = L("settings.components.short_side_label", "Short Side Cells");
EdgeInsetPercentLabel = L("settings.grid.edge_inset_label", "Screen Inset"); EdgeInsetPercentLabel = L("settings.components.edge_inset_label", "Screen Inset");
SpacingPresetLabel = L("settings.grid.spacing_label", "Grid Spacing"); SpacingPresetLabel = L("settings.components.spacing_label", "Component Spacing");
ComponentRadiusHeader = L("settings.components.corner_radius.header", "Corner Design");
GlobalCornerRadiusLabel = L("settings.components.corner_radius.label", "Component Corner Radius");
GlobalCornerRadiusDescription = L("settings.components.corner_radius.description", "Adjust the shared corner radius used by component containers, and expand the internal safe area with it.");
} }
private string L(string key, string fallback) private string L(string key, string fallback)

View File

@@ -53,7 +53,7 @@
<!-- MD3 Button Styles --> <!-- MD3 Button Styles -->
<Style Selector="Button.component-editor-footer-button"> <Style Selector="Button.component-editor-footer-button">
<Setter Property="CornerRadius" Value="20" /> <Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusMd}" />
<Setter Property="Background" Value="{DynamicResource EditorPrimaryBrush}" /> <Setter Property="Background" Value="{DynamicResource EditorPrimaryBrush}" />
<Setter Property="Foreground" Value="{DynamicResource EditorOnPrimaryBrush}" /> <Setter Property="Foreground" Value="{DynamicResource EditorOnPrimaryBrush}" />
<Setter Property="Height" Value="40" /> <Setter Property="Height" Value="40" />
@@ -118,7 +118,7 @@
Height="64" Height="64"
Background="{DynamicResource EditorPrimaryBrush}" Background="{DynamicResource EditorPrimaryBrush}"
Foreground="{DynamicResource EditorOnPrimaryBrush}" Foreground="{DynamicResource EditorOnPrimaryBrush}"
CornerRadius="18" CornerRadius="{DynamicResource DesignCornerRadiusMd}"
Classes="accent" Classes="accent"
Click="OnCloseClick"> Click="OnCloseClick">
<Button.Styles> <Button.Styles>

View File

@@ -39,7 +39,7 @@
ColumnDefinitions="240,*" ColumnDefinitions="240,*"
ColumnSpacing="12"> ColumnSpacing="12">
<Border Classes="surface-translucent-panel" <Border Classes="surface-translucent-panel"
CornerRadius="24" CornerRadius="{DynamicResource DesignCornerRadiusLg}"
Padding="10"> Padding="10">
<ListBox x:Name="CategoryListBox" <ListBox x:Name="CategoryListBox"
Background="Transparent" Background="Transparent"
@@ -50,7 +50,7 @@
<DataTemplate x:DataType="vm:ComponentLibraryCategoryViewModel"> <DataTemplate x:DataType="vm:ComponentLibraryCategoryViewModel">
<Border Padding="10" <Border Padding="10"
Margin="0,0,0,6" Margin="0,0,0,6"
CornerRadius="14" CornerRadius="{DynamicResource DesignCornerRadiusSm}"
Background="{DynamicResource AdaptiveNavItemBackgroundBrush}"> Background="{DynamicResource AdaptiveNavItemBackgroundBrush}">
<Grid ColumnDefinitions="Auto,*" <Grid ColumnDefinitions="Auto,*"
ColumnSpacing="8"> ColumnSpacing="8">
@@ -71,7 +71,7 @@
<Border Grid.Column="1" <Border Grid.Column="1"
Classes="surface-translucent-strong" Classes="surface-translucent-strong"
CornerRadius="24" CornerRadius="{DynamicResource DesignCornerRadiusLg}"
Padding="10"> Padding="10">
<ScrollViewer VerticalScrollBarVisibility="Auto" <ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"> HorizontalScrollBarVisibility="Disabled">
@@ -87,14 +87,14 @@
<Border Width="240" <Border Width="240"
Height="220" Height="220"
Margin="6" Margin="6"
CornerRadius="18" CornerRadius="{DynamicResource DesignCornerRadiusLg}"
Padding="10" Padding="10"
Background="{DynamicResource AdaptiveSurfaceRaisedBrush}" Background="{DynamicResource AdaptiveSurfaceRaisedBrush}"
BorderBrush="{DynamicResource AdaptiveButtonBorderBrush}" BorderBrush="{DynamicResource AdaptiveButtonBorderBrush}"
BorderThickness="1"> BorderThickness="1">
<Grid RowDefinitions="*,Auto,Auto" <Grid RowDefinitions="*,Auto,Auto"
RowSpacing="8"> RowSpacing="8">
<Border CornerRadius="12" <Border CornerRadius="{DynamicResource DesignCornerRadiusXs}"
Background="{DynamicResource AdaptiveGlassPanelBackgroundBrush}" Background="{DynamicResource AdaptiveGlassPanelBackgroundBrush}"
BorderThickness="1" BorderThickness="1"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}" BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"

View File

@@ -326,7 +326,7 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
var scale = ResolveScale(); var scale = ResolveScale();
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(42 * scale, 16, 56)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(42 * scale, 16, 56);
RootBorder.Padding = new Thickness(Math.Clamp(14 * scale, 8, 26)); RootBorder.Padding = new Thickness(Math.Clamp(14 * scale, 8, 26));
ApplyModeVisualIfNeeded(); ApplyModeVisualIfNeeded();
} }

View File

@@ -381,12 +381,12 @@ public partial class BaiduHotSearchWidget : UserControl, IDesktopComponentWidget
var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells; var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells;
var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells; var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * softScale, 16, 52)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * softScale, 16, 52);
RootBorder.Padding = new Thickness(0); RootBorder.Padding = new Thickness(0);
var horizontalPadding = Math.Clamp(16 * softScale, 8, 24); var horizontalPadding = Math.Clamp(16 * softScale, 8, 24);
var verticalPadding = Math.Clamp(14 * softScale, 7, 20); var verticalPadding = Math.Clamp(14 * softScale, 7, 20);
CardBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * softScale, 16, 52)); CardBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * softScale, 16, 52);
CardBorder.Padding = new Thickness(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); CardBorder.Padding = new Thickness(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
var innerWidth = Math.Max(120, totalWidth - (horizontalPadding * 2d)); var innerWidth = Math.Max(120, totalWidth - (horizontalPadding * 2d));

View File

@@ -386,12 +386,12 @@ public partial class BilibiliHotSearchWidget : UserControl, IDesktopComponentWid
var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells; var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells;
var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells; var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * softScale, 16, 52)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * softScale, 16, 52);
RootBorder.Padding = new Thickness(0); RootBorder.Padding = new Thickness(0);
var horizontalPadding = Math.Clamp(16 * softScale, 8, 24); var horizontalPadding = Math.Clamp(16 * softScale, 8, 24);
var verticalPadding = Math.Clamp(14 * softScale, 7, 20); var verticalPadding = Math.Clamp(14 * softScale, 7, 20);
CardBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * softScale, 16, 52)); CardBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * softScale, 16, 52);
CardBorder.Padding = new Thickness(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); CardBorder.Padding = new Thickness(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
var innerWidth = Math.Max(120, totalWidth - (horizontalPadding * 2d)); var innerWidth = Math.Max(120, totalWidth - (horizontalPadding * 2d));

View File

@@ -79,11 +79,11 @@ public partial class BrowserWidget : UserControl, IDesktopComponentWidget,
{ {
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.34, 12, 28)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.34, 12, 28);
RootBorder.Padding = new Thickness(Math.Clamp(_currentCellSize * 0.20, 8, 18)); RootBorder.Padding = new Thickness(Math.Clamp(_currentCellSize * 0.20, 8, 18));
WebViewHostBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.24, 10, 22)); WebViewHostBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.24, 10, 22);
AddressBarBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.22, 10, 20)); AddressBarBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.22, 10, 20);
AddressBarBorder.Padding = new Thickness(8, 6); AddressBarBorder.Padding = new Thickness(8, 6);
if (RootBorder.Child is Grid rootGrid) if (RootBorder.Child is Grid rootGrid)

View File

@@ -613,18 +613,17 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
? CreateBrush("#FF4FC3F7") ? CreateBrush("#FF4FC3F7")
: CreateBrush("#FF3250"); : CreateBrush("#FF3250");
var cornerRadius = Math.Clamp(_currentCellSize * 0.45, 24, 44); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.45, 24, 44);
RootBorder.CornerRadius = new CornerRadius(cornerRadius);
RootBorder.Background = _isNightVisual RootBorder.Background = _isNightVisual
? CreateGradientBrush("#171A21", "#0C0E14") ? CreateGradientBrush("#171A21", "#0C0E14")
: CreateGradientBrush("#F7F8FC", "#ECEFF6"); : CreateGradientBrush("#F7F8FC", "#ECEFF6");
RootBorder.BorderBrush = CreateBrush(_isNightVisual ? "#24FFFFFF" : "#15000000"); RootBorder.BorderBrush = CreateBrush(_isNightVisual ? "#24FFFFFF" : "#15000000");
var rootPadding = new Thickness( var rootPadding = new Thickness(
Math.Clamp(16 * scale, 10, 24), ComponentChromeCornerRadiusHelper.SafeValue(16 * scale, 10, 24),
Math.Clamp(14 * scale, 9, 20), ComponentChromeCornerRadiusHelper.SafeValue(14 * scale, 9, 20),
Math.Clamp(16 * scale, 10, 24), ComponentChromeCornerRadiusHelper.SafeValue(16 * scale, 10, 24),
Math.Clamp(14 * scale, 8, 20)); ComponentChromeCornerRadiusHelper.SafeValue(14 * scale, 8, 20));
RootBorder.Padding = rootPadding; RootBorder.Padding = rootPadding;
LayoutGrid.RowSpacing = Math.Clamp(14 * scale, 6, 20); LayoutGrid.RowSpacing = Math.Clamp(14 * scale, 6, 20);

View File

@@ -480,7 +480,7 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
{ {
Width = 160, Width = 160,
Height = 90, Height = 90,
CornerRadius = new CornerRadius(16), CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16, 8, 22),
ClipToBounds = true, ClipToBounds = true,
Background = new SolidColorBrush(Color.Parse("#E6E6E6")), Background = new SolidColorBrush(Color.Parse("#E6E6E6")),
IsHitTestVisible = false IsHitTestVisible = false
@@ -545,10 +545,10 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
var scale = ResolveScale(); var scale = ResolveScale();
var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells; var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * scale, 16, 52)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * scale, 16, 52);
RootBorder.Padding = new Thickness(0); RootBorder.Padding = new Thickness(0);
CardBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * scale, 16, 52)); CardBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * scale, 16, 52);
CardBorder.Padding = new Thickness( CardBorder.Padding = new Thickness(
Math.Clamp(16 * scale, 8, 24), Math.Clamp(16 * scale, 8, 24),
Math.Clamp(14 * scale, 7, 22), Math.Clamp(14 * scale, 7, 22),
@@ -573,8 +573,8 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
News1ImageHost.Height = imageHeight; News1ImageHost.Height = imageHeight;
News2ImageHost.Width = imageWidth; News2ImageHost.Width = imageWidth;
News2ImageHost.Height = imageHeight; News2ImageHost.Height = imageHeight;
News1ImageHost.CornerRadius = new CornerRadius(Math.Clamp(16 * scale, 8, 22)); News1ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22);
News2ImageHost.CornerRadius = new CornerRadius(Math.Clamp(16 * scale, 8, 22)); News2ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22);
var columnGap = Math.Clamp(12 * scale, 6, 18); var columnGap = Math.Clamp(12 * scale, 6, 18);
NewsItem1Grid.ColumnSpacing = columnGap; NewsItem1Grid.ColumnSpacing = columnGap;
@@ -611,7 +611,7 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
row.ImageHost.Width = imageWidth; row.ImageHost.Width = imageWidth;
row.ImageHost.Height = imageHeight; row.ImageHost.Height = imageHeight;
row.ImageHost.CornerRadius = new CornerRadius(Math.Clamp(16 * scale, 8, 22)); row.ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22);
row.TitleTextBlock.MaxWidth = availableTextWidth; row.TitleTextBlock.MaxWidth = availableTextWidth;
row.TitleTextBlock.FontSize = Math.Clamp(19 * scale, 10, 25); row.TitleTextBlock.FontSize = Math.Clamp(19 * scale, 10, 25);

View File

@@ -0,0 +1,72 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Services;
namespace LanMountainDesktop.Views.Components;
internal static class ComponentChromeCornerRadiusHelper
{
public static double ResolveScale(ComponentChromeContext? chromeContext = null)
{
if (chromeContext is not null)
{
return Math.Max(0.1d, chromeContext.GlobalCornerRadiusScale);
}
return Math.Max(0.1d, HostAppearanceThemeProvider.GetOrCreate().GetCurrent().GlobalCornerRadiusScale);
}
public static CornerRadius Scale(double baseRadius, double min, double max, ComponentChromeContext? chromeContext = null)
{
var scale = ResolveScale(chromeContext);
return new CornerRadius(Math.Clamp(baseRadius * scale, min * scale, max * scale));
}
public static void Apply(CornerRadius radius, params Border?[] chromeLayers)
{
foreach (var chromeLayer in chromeLayers)
{
if (chromeLayer is not null)
{
chromeLayer.CornerRadius = radius;
}
}
}
public static CornerRadius ResolveToken(string key, double fallback)
{
var application = Application.Current;
return application is not null &&
application.Resources.TryGetResource(key, application.ActualThemeVariant, out var resource) &&
resource is CornerRadius radius
? radius
: new CornerRadius(fallback);
}
public static double ScaleValue(double value, ComponentChromeContext? chromeContext = null)
{
return value * ResolveScale(chromeContext);
}
public static double ResolveContentSafetyScale(
ComponentChromeContext? chromeContext = null,
double responsiveness = 0.45d)
{
var scale = ResolveScale(chromeContext);
var normalizedResponsiveness = Math.Clamp(responsiveness, 0d, 1d);
return 1d + ((scale - 1d) * normalizedResponsiveness);
}
public static double SafeValue(
double baseValue,
double min,
double max,
ComponentChromeContext? chromeContext = null,
double responsiveness = 0.45d)
{
var safetyScale = ResolveContentSafetyScale(chromeContext, responsiveness);
return Math.Clamp(baseValue * safetyScale, min * safetyScale, max * safetyScale);
}
}

View File

@@ -101,7 +101,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
var scale = ResolveScale(); var scale = ResolveScale();
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * scale, 16, 52)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * scale, 16, 52);
InfoPanel.Padding = new Thickness( InfoPanel.Padding = new Thickness(
Math.Clamp(18 * scale, 10, 28), Math.Clamp(18 * scale, 10, 28),

View File

@@ -92,7 +92,7 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
var scale = ResolveScale(); var scale = ResolveScale();
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * scale, 16, 52)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * scale, 16, 52);
RootBorder.Padding = new Thickness( RootBorder.Padding = new Thickness(
Math.Clamp(20 * scale, 10, 34), Math.Clamp(20 * scale, 10, 34),
Math.Clamp(16 * scale, 8, 28), Math.Clamp(16 * scale, 8, 28),

View File

@@ -328,7 +328,7 @@ public partial class DailyWord2x2Widget : UserControl, IDesktopComponentWidget,
var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells; var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells;
var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells; var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(30 * scale, 14, 40)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(30 * scale, 14, 40);
CardBorder.CornerRadius = RootBorder.CornerRadius; CardBorder.CornerRadius = RootBorder.CornerRadius;
CardBorder.Padding = new Thickness( CardBorder.Padding = new Thickness(
Math.Clamp(12 * scale, 8, 18), Math.Clamp(12 * scale, 8, 18),

View File

@@ -298,10 +298,11 @@ public partial class DailyWordWidget : UserControl, IDesktopComponentWidget, IRe
isFourByThree = widthRatio >= 0.9 && heightRatio >= 1.35; isFourByThree = widthRatio >= 0.9 && heightRatio >= 1.35;
} }
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * scale, 16, 52)); var containerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * scale, 16, 52);
RootBorder.CornerRadius = containerRadius;
RootBorder.Padding = new Thickness(0); RootBorder.Padding = new Thickness(0);
CardBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * scale, 16, 52)); CardBorder.CornerRadius = containerRadius;
CardBorder.Padding = new Thickness( CardBorder.Padding = new Thickness(
Math.Clamp(16 * scale, 8, 24), Math.Clamp(16 * scale, 8, 24),
Math.Clamp(14 * scale, 7, 22), Math.Clamp(14 * scale, 7, 22),

View File

@@ -325,7 +325,7 @@ public partial class DateWidget : UserControl, IDesktopComponentWidget, ITimeZon
{ {
var scale = ResolveScale(); var scale = ResolveScale();
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(28 * scale, 16, 40)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(28 * scale, 16, 40);
RootBorder.Padding = new Thickness(Math.Clamp(11 * scale, 7, 17)); RootBorder.Padding = new Thickness(Math.Clamp(11 * scale, 7, 17));
LayoutRoot.ColumnSpacing = Math.Clamp(10 * scale, 6, 16); LayoutRoot.ColumnSpacing = Math.Clamp(10 * scale, 6, 16);
@@ -337,7 +337,7 @@ public partial class DateWidget : UserControl, IDesktopComponentWidget, ITimeZon
Math.Clamp(2.4 * scale, 1, 4)); Math.Clamp(2.4 * scale, 1, 4));
CalendarGrid.Margin = new Thickness(0, 0, 0, Math.Clamp(0.8 * scale, 0, 2)); CalendarGrid.Margin = new Thickness(0, 0, 0, Math.Clamp(0.8 * scale, 0, 2));
LunarCardBorder.CornerRadius = new CornerRadius(Math.Clamp(24 * scale, 14, 34)); LunarCardBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(24 * scale, 14, 34);
LunarCardBorder.Padding = new Thickness(Math.Clamp(14 * scale, 8, 20)); LunarCardBorder.Padding = new Thickness(Math.Clamp(14 * scale, 8, 20));
RightPanelGrid.RowSpacing = Math.Clamp(7.5 * scale, 3.5, 11); RightPanelGrid.RowSpacing = Math.Clamp(7.5 * scale, 3.5, 11);
DividerBorder.Margin = new Thickness(0, Math.Clamp(1 * scale, 0, 2), 0, Math.Clamp(1 * scale, 0, 2)); DividerBorder.Margin = new Thickness(0, Math.Clamp(1 * scale, 0, 2), 0, Math.Clamp(1 * scale, 0, 2));

View File

@@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Avalonia.Controls; using Avalonia.Controls;
using LanMountainDesktop.Appearance;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Services.Settings; using LanMountainDesktop.Services.Settings;
@@ -30,7 +32,11 @@ public sealed class DesktopComponentRuntimeRegistration
string? displayNameLocalizationKey, string? displayNameLocalizationKey,
Func<Control> controlFactory, Func<Control> controlFactory,
Func<double, double>? cornerRadiusResolver = null) Func<double, double>? cornerRadiusResolver = null)
: this(componentId, displayNameLocalizationKey, _ => controlFactory(), cornerRadiusResolver) : this(
componentId,
displayNameLocalizationKey,
_ => controlFactory(),
cornerRadiusResolver is null ? null : chromeContext => cornerRadiusResolver(chromeContext.CellSize))
{ {
} }
@@ -39,6 +45,19 @@ public sealed class DesktopComponentRuntimeRegistration
string? displayNameLocalizationKey, string? displayNameLocalizationKey,
Func<DesktopComponentControlFactoryContext, Control> controlFactory, Func<DesktopComponentControlFactoryContext, Control> controlFactory,
Func<double, double>? cornerRadiusResolver = null) Func<double, double>? cornerRadiusResolver = null)
: this(
componentId,
displayNameLocalizationKey,
controlFactory,
cornerRadiusResolver is null ? null : chromeContext => cornerRadiusResolver(chromeContext.CellSize))
{
}
public DesktopComponentRuntimeRegistration(
string componentId,
string? displayNameLocalizationKey,
Func<DesktopComponentControlFactoryContext, Control> controlFactory,
Func<ComponentChromeContext, double>? cornerRadiusResolver = null)
{ {
ArgumentException.ThrowIfNullOrWhiteSpace(componentId); ArgumentException.ThrowIfNullOrWhiteSpace(componentId);
ArgumentNullException.ThrowIfNull(controlFactory); ArgumentNullException.ThrowIfNull(controlFactory);
@@ -57,22 +76,27 @@ public sealed class DesktopComponentRuntimeRegistration
public Func<DesktopComponentControlFactoryContext, Control> ControlFactory { get; } public Func<DesktopComponentControlFactoryContext, Control> ControlFactory { get; }
public Func<double, double>? CornerRadiusResolver { get; } public Func<ComponentChromeContext, double>? CornerRadiusResolver { get; }
} }
public sealed class DesktopComponentRuntimeDescriptor public sealed class DesktopComponentRuntimeDescriptor
{ {
private static readonly Func<double, double> DefaultCornerRadiusResolver = private static readonly Func<ComponentChromeContext, double> DefaultCornerRadiusResolver =
cellSize => Math.Clamp(cellSize * 0.22, 8, 18); chromeContext =>
{
var scale = Math.Max(0.1d, chromeContext.GlobalCornerRadiusScale);
var baseRadius = Math.Clamp(chromeContext.CellSize * 0.22, 8, 18);
return Math.Clamp(baseRadius * scale, 8 * scale, 18 * scale);
};
private readonly Func<DesktopComponentControlFactoryContext, Control> _controlFactory; private readonly Func<DesktopComponentControlFactoryContext, Control> _controlFactory;
private readonly Func<double, double> _cornerRadiusResolver; private readonly Func<ComponentChromeContext, double> _cornerRadiusResolver;
internal DesktopComponentRuntimeDescriptor( internal DesktopComponentRuntimeDescriptor(
DesktopComponentDefinition definition, DesktopComponentDefinition definition,
string? displayNameLocalizationKey, string? displayNameLocalizationKey,
Func<DesktopComponentControlFactoryContext, Control> controlFactory, Func<DesktopComponentControlFactoryContext, Control> controlFactory,
Func<double, double>? cornerRadiusResolver) Func<ComponentChromeContext, double>? cornerRadiusResolver)
{ {
Definition = definition; Definition = definition;
DisplayNameLocalizationKey = displayNameLocalizationKey; DisplayNameLocalizationKey = displayNameLocalizationKey;
@@ -97,9 +121,16 @@ public sealed class DesktopComponentRuntimeDescriptor
var settingsService = settingsFacade.Settings; var settingsService = settingsFacade.Settings;
var appearanceTheme = HostAppearanceThemeProvider.GetOrCreate(); var appearanceTheme = HostAppearanceThemeProvider.GetOrCreate();
var appearanceSnapshot = appearanceTheme.GetCurrent();
var componentAccessor = settingsService.GetComponentAccessor(Definition.Id, placementId); var componentAccessor = settingsService.GetComponentAccessor(Definition.Id, placementId);
var componentSettingsStore = new ComponentSettingsService(settingsService); var componentSettingsStore = new ComponentSettingsService(settingsService);
componentSettingsStore.SetScopedComponentContext(Definition.Id, placementId); componentSettingsStore.SetScopedComponentContext(Definition.Id, placementId);
var chromeContext = new ComponentChromeContext(
Definition.Id,
placementId,
cellSize,
appearanceSnapshot.GlobalCornerRadiusScale,
appearanceSnapshot.CornerRadiusTokens);
var control = _controlFactory(new DesktopComponentControlFactoryContext( var control = _controlFactory(new DesktopComponentControlFactoryContext(
Definition, Definition,
cellSize, cellSize,
@@ -118,6 +149,7 @@ public sealed class DesktopComponentRuntimeDescriptor
settingsFacade, settingsFacade,
settingsService, settingsService,
appearanceTheme, appearanceTheme,
chromeContext,
componentAccessor, componentAccessor,
componentSettingsStore); componentSettingsStore);
@@ -145,6 +177,11 @@ public sealed class DesktopComponentRuntimeDescriptor
placementAwareComponent.SetComponentPlacementContext(Definition.Id, placementId); placementAwareComponent.SetComponentPlacementContext(Definition.Id, placementId);
} }
if (control is IComponentChromeContextAware chromeContextAwareComponent)
{
chromeContextAwareComponent.SetComponentChromeContext(chromeContext);
}
if (control is IDesktopComponentWidget sizedComponent) if (control is IDesktopComponentWidget sizedComponent)
{ {
sizedComponent.ApplyCellSize(cellSize); sizedComponent.ApplyCellSize(cellSize);
@@ -173,9 +210,22 @@ public sealed class DesktopComponentRuntimeDescriptor
return control; return control;
} }
public double ResolveCornerRadius(ComponentChromeContext chromeContext)
{
ArgumentNullException.ThrowIfNull(chromeContext);
var resolved = _cornerRadiusResolver(chromeContext with { CellSize = Math.Max(1, chromeContext.CellSize) });
return double.IsFinite(resolved) ? Math.Max(0d, resolved) : DefaultCornerRadiusResolver(chromeContext);
}
public double ResolveCornerRadius(double cellSize) public double ResolveCornerRadius(double cellSize)
{ {
return _cornerRadiusResolver(Math.Max(1, cellSize)); return ResolveCornerRadius(new ComponentChromeContext(
Definition.Id,
null,
Math.Max(1, cellSize),
1d,
AppearanceCornerRadiusTokenFactory.Create(1d)));
} }
private static void ApplySettingsDependencies( private static void ApplySettingsDependencies(

View File

@@ -80,8 +80,8 @@ public partial class ExchangeRateCalculatorWidget : UserControl, IDesktopCompone
{ {
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
var scale = ResolveScale(); var scale = ResolveScale();
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * scale, 14, 48)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * scale, 14, 48);
RootBorder.Padding = new Thickness(Math.Clamp(12 * scale, 6, 18)); RootBorder.Padding = new Thickness(ComponentChromeCornerRadiusHelper.SafeValue(12 * scale, 6, 18));
} }
public void SetRecommendationInfoService(IRecommendationInfoService recommendationInfoService) public void SetRecommendationInfoService(IRecommendationInfoService recommendationInfoService)

View File

@@ -10,6 +10,7 @@ using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -18,7 +19,7 @@ using LanMountainDesktop.Theme;
namespace LanMountainDesktop.Views.Components; namespace LanMountainDesktop.Views.Components;
public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentChromeContextAware
{ {
private static readonly IWeatherInfoService DefaultWeatherInfoService = new XiaomiWeatherService(); private static readonly IWeatherInfoService DefaultWeatherInfoService = new XiaomiWeatherService();
private static readonly IReadOnlyList<int> SupportedAutoRefreshIntervalsMinutes = RefreshIntervalCatalog.SupportedIntervalsMinutes; private static readonly IReadOnlyList<int> SupportedAutoRefreshIntervalsMinutes = RefreshIntervalCatalog.SupportedIntervalsMinutes;
@@ -34,6 +35,7 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
private TimeZoneService? _timeZoneService; private TimeZoneService? _timeZoneService;
private CancellationTokenSource? _refreshCts; private CancellationTokenSource? _refreshCts;
private double _currentCellSize = 48; private double _currentCellSize = 48;
private ComponentChromeContext? _chromeContext;
private double _phase; private double _phase;
private bool _isAttached; private bool _isAttached;
private bool _isOnActivePage = true; private bool _isOnActivePage = true;
@@ -122,21 +124,34 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
var metrics = HyperOS3WeatherTheme.ResolveMetrics(HyperOS3WeatherWidgetKind.Extended4x4); var metrics = HyperOS3WeatherTheme.ResolveMetrics(HyperOS3WeatherWidgetKind.Extended4x4);
var width = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 4; var width = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 4;
var height = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * 4; var height = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * 4;
var radius = Math.Clamp(_currentCellSize * metrics.CornerRadiusScale, 28, 54); var radius = ComponentChromeCornerRadiusHelper.Scale(
RootBorder.CornerRadius = new CornerRadius(radius); _currentCellSize * metrics.CornerRadiusScale,
BackgroundImageLayer.CornerRadius = new CornerRadius(radius); 28,
BackgroundMotionLayer.CornerRadius = new CornerRadius(radius); 54,
BackgroundTintLayer.CornerRadius = new CornerRadius(radius); _chromeContext);
BackgroundLightLayer.CornerRadius = new CornerRadius(radius); ComponentChromeCornerRadiusHelper.Apply(
BackgroundShadeLayer.CornerRadius = new CornerRadius(radius); radius,
RootBorder,
BackgroundImageLayer,
BackgroundMotionLayer,
BackgroundTintLayer,
BackgroundLightLayer,
BackgroundShadeLayer);
var horizontalPadding = Math.Clamp(Math.Min(width * metrics.HorizontalPaddingScale * 0.30, width * 0.11), 4, 34); var horizontalPadding = Math.Clamp(Math.Min(width * metrics.HorizontalPaddingScale * 0.30, width * 0.11), 4, 34);
var verticalPadding = Math.Clamp(Math.Min(height * metrics.VerticalPaddingScale * 0.30, height * 0.11), 4, 34); var verticalPadding = Math.Clamp(Math.Min(height * metrics.VerticalPaddingScale * 0.30, height * 0.11), 4, 34);
ContentPaddingBorder.Padding = new Thickness( ContentPaddingBorder.Padding = new Thickness(
horizontalPadding, ComponentChromeCornerRadiusHelper.SafeValue(horizontalPadding, 4, 34, _chromeContext),
verticalPadding); ComponentChromeCornerRadiusHelper.SafeValue(verticalPadding, 4, 34, _chromeContext));
ApplyTypography(width, height); ApplyTypography(width, height);
} }
public void SetComponentChromeContext(ComponentChromeContext context)
{
ArgumentNullException.ThrowIfNull(context);
_chromeContext = context;
ApplyCellSize(_currentCellSize);
}
public void SetTimeZoneService(TimeZoneService timeZoneService) public void SetTimeZoneService(TimeZoneService timeZoneService)
{ {
if (_timeZoneService is not null) if (_timeZoneService is not null)

View File

@@ -216,8 +216,8 @@ public partial class HolidayCalendarWidget : UserControl, IDesktopComponentWidge
var titleNeedsTwoLines = isUltraCompact || titleUnits >= (isCompact ? 13 : 17); var titleNeedsTwoLines = isUltraCompact || titleUnits >= (isCompact ? 13 : 17);
var dateNeedsTwoLines = isUltraCompact || dateUnits >= (isCompact ? 15 : 20); var dateNeedsTwoLines = isUltraCompact || dateUnits >= (isCompact ? 15 : 20);
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(shortSide * 0.13, 10, 46)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(shortSide * 0.13, 10, 46);
var padding = Math.Clamp(shortSide * 0.05, 4.5, 21); var padding = ComponentChromeCornerRadiusHelper.SafeValue(shortSide * 0.05, 4.5, 21);
RootBorder.Padding = new Thickness(padding); RootBorder.Padding = new Thickness(padding);
LayoutRoot.RowSpacing = Math.Clamp(shortSide * 0.028, 2.2, 12); LayoutRoot.RowSpacing = Math.Clamp(shortSide * 0.028, 2.2, 12);
var rowWeights = ApplyAdaptiveRowHeights(isCompact, isUltraCompact, titleNeedsTwoLines, dateNeedsTwoLines); var rowWeights = ApplyAdaptiveRowHeights(isCompact, isUltraCompact, titleNeedsTwoLines, dateNeedsTwoLines);

View File

@@ -12,13 +12,14 @@ using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
namespace LanMountainDesktop.Views.Components; namespace LanMountainDesktop.Views.Components;
public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentChromeContextAware
{ {
private enum WeatherVisualKind private enum WeatherVisualKind
{ {
@@ -117,6 +118,7 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
private WeatherSnapshot? _latestSnapshot; private WeatherSnapshot? _latestSnapshot;
private string _languageCode = "zh-CN"; private string _languageCode = "zh-CN";
private double _currentCellSize = 48; private double _currentCellSize = 48;
private ComponentChromeContext? _chromeContext;
private WeatherVisualKind _activeVisualKind = WeatherVisualKind.ClearDay; private WeatherVisualKind _activeVisualKind = WeatherVisualKind.ClearDay;
private double _animationPhase; private double _animationPhase;
private int _activeParticleCount; private int _activeParticleCount;
@@ -254,6 +256,13 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
} }
} }
public void SetComponentChromeContext(ComponentChromeContext context)
{
ArgumentNullException.ThrowIfNull(context);
_chromeContext = context;
ApplyCellSize(_currentCellSize);
}
public void ApplyCellSize(double cellSize) public void ApplyCellSize(double cellSize)
{ {
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
@@ -261,17 +270,23 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
var scale = ResolveScale(); var scale = ResolveScale();
var hostWidth = Bounds.Width > 1 ? Bounds.Width : Math.Max(140, _currentCellSize * 4); var hostWidth = Bounds.Width > 1 ? Bounds.Width : Math.Max(140, _currentCellSize * 4);
var hostHeight = Bounds.Height > 1 ? Bounds.Height : Math.Max(78, _currentCellSize * 2); var hostHeight = Bounds.Height > 1 ? Bounds.Height : Math.Max(78, _currentCellSize * 2);
var cornerRadius = Math.Clamp(_currentCellSize * metrics.CornerRadiusScale, 24, 46); var cornerRadius = ComponentChromeCornerRadiusHelper.Scale(
_currentCellSize * metrics.CornerRadiusScale,
24,
46,
_chromeContext);
RootBorder.CornerRadius = new CornerRadius(cornerRadius); ComponentChromeCornerRadiusHelper.Apply(
BackgroundImageLayer.CornerRadius = new CornerRadius(cornerRadius); cornerRadius,
BackgroundMotionLayer.CornerRadius = new CornerRadius(cornerRadius); RootBorder,
BackgroundTintLayer.CornerRadius = new CornerRadius(cornerRadius); BackgroundImageLayer,
BackgroundLightLayer.CornerRadius = new CornerRadius(cornerRadius); BackgroundMotionLayer,
BackgroundShadeLayer.CornerRadius = new CornerRadius(cornerRadius); BackgroundTintLayer,
BackgroundLightLayer,
BackgroundShadeLayer);
ContentPaddingBorder.Padding = new Thickness( ContentPaddingBorder.Padding = new Thickness(
Math.Clamp(Math.Min((_currentCellSize * metrics.HorizontalPaddingScale) * scale, hostWidth * 0.034), 4, 22), ComponentChromeCornerRadiusHelper.SafeValue(Math.Min((_currentCellSize * metrics.HorizontalPaddingScale) * scale, hostWidth * 0.034), 4, 22, _chromeContext),
Math.Clamp(Math.Min((_currentCellSize * metrics.VerticalPaddingScale) * scale, hostHeight * 0.068), 3, 18)); ComponentChromeCornerRadiusHelper.SafeValue(Math.Min((_currentCellSize * metrics.VerticalPaddingScale) * scale, hostHeight * 0.068), 3, 18, _chromeContext));
ApplyAdaptiveTypography(); ApplyAdaptiveTypography();
ResetParticles(); ResetParticles();
} }

View File

@@ -400,8 +400,8 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells; var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells;
var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells; var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(32 * softScale, 16, 46)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(32 * softScale, 16, 46);
CardBorder.CornerRadius = new CornerRadius(Math.Clamp(32 * softScale, 16, 46)); CardBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(32 * softScale, 16, 46);
var horizontalPadding = Math.Clamp(14 * softScale, 8, 20); var horizontalPadding = Math.Clamp(14 * softScale, 8, 20);
var verticalPadding = Math.Clamp(14 * softScale, 8, 20); var verticalPadding = Math.Clamp(14 * softScale, 8, 20);
@@ -452,7 +452,7 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
visual.ImageHost.Width = imageWidth; visual.ImageHost.Width = imageWidth;
visual.ImageHost.Height = imageHeight; visual.ImageHost.Height = imageHeight;
visual.ImageHost.CornerRadius = new CornerRadius(Math.Clamp(imageHeight * 0.15, 8, 16)); visual.ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(imageHeight * 0.15, 8, 16);
visual.TitleTextBlock.MaxWidth = textWidth; visual.TitleTextBlock.MaxWidth = textWidth;
visual.TitleTextBlock.FontSize = titleFont; visual.TitleTextBlock.FontSize = titleFont;

View File

@@ -182,8 +182,8 @@ public partial class LunarCalendarWidget : UserControl, IDesktopComponentWidget,
{ {
var scale = ResolveScale(); var scale = ResolveScale();
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(30 * scale, 16, 44)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(30 * scale, 16, 44);
RootBorder.Padding = new Thickness(Math.Clamp(16 * scale, 8, 24)); RootBorder.Padding = new Thickness(ComponentChromeCornerRadiusHelper.SafeValue(16 * scale, 8, 24));
LayoutRoot.RowSpacing = Math.Clamp(10 * scale, 5, 18); LayoutRoot.RowSpacing = Math.Clamp(10 * scale, 5, 18);
DividerBorder.Margin = new Thickness( DividerBorder.Margin = new Thickness(
Math.Clamp(8 * scale, 3, 14), Math.Clamp(8 * scale, 3, 14),

View File

@@ -217,8 +217,8 @@ public partial class MonthCalendarWidget : UserControl, IDesktopComponentWidget,
{ {
var scale = ResolveScale(); var scale = ResolveScale();
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(28 * scale, 14, 40)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(28 * scale, 14, 40);
RootBorder.Padding = new Thickness(Math.Clamp(14 * scale, 8, 22)); RootBorder.Padding = new Thickness(ComponentChromeCornerRadiusHelper.SafeValue(14 * scale, 8, 22));
LayoutRoot.RowSpacing = Math.Clamp(10 * scale, 5, 16); LayoutRoot.RowSpacing = Math.Clamp(10 * scale, 5, 16);
LayoutRoot.Width = Math.Clamp(280 * scale, 220, 420); LayoutRoot.Width = Math.Clamp(280 * scale, 220, 420);
LayoutRoot.Height = Math.Clamp(280 * scale, 220, 420); LayoutRoot.Height = Math.Clamp(280 * scale, 220, 420);

View File

@@ -10,13 +10,14 @@ using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
namespace LanMountainDesktop.Views.Components; namespace LanMountainDesktop.Views.Components;
public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentChromeContextAware
{ {
private enum WeatherVisualKind private enum WeatherVisualKind
{ {
@@ -115,6 +116,7 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
private WeatherSnapshot? _latestSnapshot; private WeatherSnapshot? _latestSnapshot;
private string _languageCode = "zh-CN"; private string _languageCode = "zh-CN";
private double _currentCellSize = 48; private double _currentCellSize = 48;
private ComponentChromeContext? _chromeContext;
private WeatherVisualKind _activeVisualKind = WeatherVisualKind.ClearDay; private WeatherVisualKind _activeVisualKind = WeatherVisualKind.ClearDay;
private double _animationPhase; private double _animationPhase;
private int _activeParticleCount; private int _activeParticleCount;
@@ -252,6 +254,13 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
} }
} }
public void SetComponentChromeContext(ComponentChromeContext context)
{
ArgumentNullException.ThrowIfNull(context);
_chromeContext = context;
ApplyCellSize(_currentCellSize);
}
public void ApplyCellSize(double cellSize) public void ApplyCellSize(double cellSize)
{ {
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
@@ -259,17 +268,23 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
var scale = ResolveScale(); var scale = ResolveScale();
var hostWidth = Bounds.Width > 1 ? Bounds.Width : Math.Max(140, _currentCellSize * 4); var hostWidth = Bounds.Width > 1 ? Bounds.Width : Math.Max(140, _currentCellSize * 4);
var hostHeight = Bounds.Height > 1 ? Bounds.Height : Math.Max(78, _currentCellSize * 2); var hostHeight = Bounds.Height > 1 ? Bounds.Height : Math.Max(78, _currentCellSize * 2);
var cornerRadius = Math.Clamp(_currentCellSize * metrics.CornerRadiusScale, 24, 46); var cornerRadius = ComponentChromeCornerRadiusHelper.Scale(
_currentCellSize * metrics.CornerRadiusScale,
24,
46,
_chromeContext);
RootBorder.CornerRadius = new CornerRadius(cornerRadius); ComponentChromeCornerRadiusHelper.Apply(
BackgroundImageLayer.CornerRadius = new CornerRadius(cornerRadius); cornerRadius,
BackgroundMotionLayer.CornerRadius = new CornerRadius(cornerRadius); RootBorder,
BackgroundTintLayer.CornerRadius = new CornerRadius(cornerRadius); BackgroundImageLayer,
BackgroundLightLayer.CornerRadius = new CornerRadius(cornerRadius); BackgroundMotionLayer,
BackgroundShadeLayer.CornerRadius = new CornerRadius(cornerRadius); BackgroundTintLayer,
BackgroundLightLayer,
BackgroundShadeLayer);
ContentPaddingBorder.Padding = new Thickness( ContentPaddingBorder.Padding = new Thickness(
Math.Clamp(Math.Min((_currentCellSize * metrics.HorizontalPaddingScale) * scale, hostWidth * 0.034), 4, 22), ComponentChromeCornerRadiusHelper.SafeValue(Math.Min((_currentCellSize * metrics.HorizontalPaddingScale) * scale, hostWidth * 0.034), 4, 22, _chromeContext),
Math.Clamp(Math.Min((_currentCellSize * metrics.VerticalPaddingScale) * scale, hostHeight * 0.068), 3, 18)); ComponentChromeCornerRadiusHelper.SafeValue(Math.Min((_currentCellSize * metrics.VerticalPaddingScale) * scale, hostHeight * 0.068), 3, 18, _chromeContext));
ApplyAdaptiveTypography(); ApplyAdaptiveTypography();
ResetParticles(); ResetParticles();
} }

View File

@@ -63,15 +63,15 @@ public partial class RecordingWidget : UserControl, IDesktopComponentWidget, IDe
var chromeScale = Math.Clamp(rawScale, 0.62, 2.0); var chromeScale = Math.Clamp(rawScale, 0.62, 2.0);
var contentScale = Math.Clamp(rawScale, 0.74, 1.0); var contentScale = Math.Clamp(rawScale, 0.74, 1.0);
var rootRadius = Math.Clamp(34 * chromeScale, 16, 56); var rootRadius = ComponentChromeCornerRadiusHelper.Scale(34 * chromeScale, 16, 56);
RootBorder.CornerRadius = new CornerRadius(rootRadius); RootBorder.CornerRadius = rootRadius;
RootBorder.Padding = new Thickness(0); RootBorder.Padding = new Thickness(0);
RecorderCardBorder.CornerRadius = new CornerRadius(rootRadius); RecorderCardBorder.CornerRadius = rootRadius;
RecorderContentGrid.Margin = new Thickness( RecorderContentGrid.Margin = new Thickness(
Math.Clamp(24 * contentScale, 14, 26), ComponentChromeCornerRadiusHelper.SafeValue(24 * contentScale, 14, 26),
Math.Clamp(18 * contentScale, 10, 22), ComponentChromeCornerRadiusHelper.SafeValue(18 * contentScale, 10, 22),
Math.Clamp(24 * contentScale, 14, 26), ComponentChromeCornerRadiusHelper.SafeValue(24 * contentScale, 14, 26),
Math.Clamp(18 * contentScale, 10, 24)); ComponentChromeCornerRadiusHelper.SafeValue(18 * contentScale, 10, 24));
var sideButtonSize = Math.Clamp(54 * contentScale, 34, 58); var sideButtonSize = Math.Clamp(54 * contentScale, 34, 58);
DiscardButtonBorder.Width = sideButtonSize; DiscardButtonBorder.Width = sideButtonSize;

View File

@@ -347,13 +347,12 @@ public partial class RemovableStorageWidget : UserControl, IDesktopComponentWidg
var scale = ResolveScale(); var scale = ResolveScale();
var width = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 2; var width = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 2;
var cornerRadius = Math.Clamp(_currentCellSize * 0.44, 18, 34); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.44, 18, 34);
RootBorder.CornerRadius = new CornerRadius(cornerRadius);
RootBorder.Padding = new Thickness( RootBorder.Padding = new Thickness(
Math.Clamp(16 * scale, 10, 24), ComponentChromeCornerRadiusHelper.SafeValue(16 * scale, 10, 24),
Math.Clamp(15 * scale, 10, 22), ComponentChromeCornerRadiusHelper.SafeValue(15 * scale, 10, 22),
Math.Clamp(16 * scale, 10, 24), ComponentChromeCornerRadiusHelper.SafeValue(16 * scale, 10, 24),
Math.Clamp(15 * scale, 10, 22)); ComponentChromeCornerRadiusHelper.SafeValue(15 * scale, 10, 22));
LayoutGrid.RowSpacing = Math.Clamp(10 * scale, 8, 16); LayoutGrid.RowSpacing = Math.Clamp(10 * scale, 8, 16);
HeaderGrid.ColumnSpacing = Math.Clamp(12 * scale, 8, 16); HeaderGrid.ColumnSpacing = Math.Clamp(12 * scale, 8, 16);

View File

@@ -602,8 +602,8 @@ public partial class Stcn24ForumWidget : UserControl, IDesktopComponentWidget, I
var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells; var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells;
var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells; var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(30 * softScale, 14, 44)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(30 * softScale, 14, 44);
CardBorder.CornerRadius = new CornerRadius(Math.Clamp(30 * softScale, 14, 44)); CardBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(30 * softScale, 14, 44);
CardBorder.Padding = new Thickness( CardBorder.Padding = new Thickness(
Math.Clamp(12 * softScale, 8, 18), Math.Clamp(12 * softScale, 8, 18),
Math.Clamp(12 * softScale, 8, 18), Math.Clamp(12 * softScale, 8, 18),
@@ -628,7 +628,6 @@ public partial class Stcn24ForumWidget : UserControl, IDesktopComponentWidget, I
var innerWidth = Math.Max(100, totalWidth - CardBorder.Padding.Left - CardBorder.Padding.Right); var innerWidth = Math.Max(100, totalWidth - CardBorder.Padding.Left - CardBorder.Padding.Right);
var rowPaddingHorizontal = Math.Clamp(8 * softScale, 5, 14); var rowPaddingHorizontal = Math.Clamp(8 * softScale, 5, 14);
var rowPaddingVertical = Math.Clamp(6 * softScale, 3, 10); var rowPaddingVertical = Math.Clamp(6 * softScale, 3, 10);
var itemCornerRadius = Math.Clamp(10 * softScale, 6, 14);
var avatarSize = Math.Clamp(30 * softScale, 20, 40); var avatarSize = Math.Clamp(30 * softScale, 20, 40);
var avatarFont = Math.Clamp(13 * softScale, 9, 18); var avatarFont = Math.Clamp(13 * softScale, 9, 18);
var titleFont = Math.Clamp(14 * softScale, 10, 19); var titleFont = Math.Clamp(14 * softScale, 10, 19);
@@ -658,7 +657,7 @@ public partial class Stcn24ForumWidget : UserControl, IDesktopComponentWidget, I
foreach (var visual in _itemVisuals) foreach (var visual in _itemVisuals)
{ {
visual.Host.CornerRadius = new CornerRadius(itemCornerRadius); visual.Host.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(10 * softScale, 6, 14);
visual.Host.Padding = new Thickness(rowPaddingHorizontal, rowPaddingVertical); visual.Host.Padding = new Thickness(rowPaddingHorizontal, rowPaddingVertical);
visual.RowGrid.ColumnSpacing = Math.Clamp(8 * softScale, 4, 12); visual.RowGrid.ColumnSpacing = Math.Clamp(8 * softScale, 4, 12);

View File

@@ -229,7 +229,7 @@ public partial class StudyDeductionReasonsWidget : UserControl, IDesktopComponen
_isUltraCompactMode = scale < 0.72 || (Bounds.Width > 1 && Bounds.Width < 300) || (Bounds.Height > 1 && Bounds.Height < 145); _isUltraCompactMode = scale < 0.72 || (Bounds.Width > 1 && Bounds.Width < 300) || (Bounds.Height > 1 && Bounds.Height < 145);
var compactMultiplier = _isUltraCompactMode ? 0.76 : _isCompactMode ? 0.88 : 1.0; var compactMultiplier = _isUltraCompactMode ? 0.76 : _isCompactMode ? 0.88 : 1.0;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.46, 12, 34)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.46, 12, 34);
RootBorder.Padding = new Thickness( RootBorder.Padding = new Thickness(
Math.Clamp(12 * scale * compactMultiplier, 6, 18), Math.Clamp(12 * scale * compactMultiplier, 6, 18),
Math.Clamp(10 * scale * compactMultiplier, 5, 16)); Math.Clamp(10 * scale * compactMultiplier, 5, 16));

View File

@@ -52,7 +52,7 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
var scale = Math.Clamp(_currentCellSize / 48d, 0.82, 2.2); var scale = Math.Clamp(_currentCellSize / 48d, 0.82, 2.2);
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.34, 10, 28)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.34, 10, 28);
RootBorder.Padding = new Thickness( RootBorder.Padding = new Thickness(
Math.Clamp(14 * scale, 8, 20), Math.Clamp(14 * scale, 8, 20),
Math.Clamp(10 * scale, 6, 16)); Math.Clamp(10 * scale, 6, 16));

View File

@@ -255,7 +255,7 @@ public partial class StudyInterruptDensityWidget : UserControl, IDesktopComponen
_isUltraCompactMode = scale < 0.72 || (Bounds.Width > 1 && Bounds.Width < 295) || (Bounds.Height > 1 && Bounds.Height < 130); _isUltraCompactMode = scale < 0.72 || (Bounds.Width > 1 && Bounds.Width < 295) || (Bounds.Height > 1 && Bounds.Height < 130);
var compactMultiplier = _isUltraCompactMode ? 0.76 : _isCompactMode ? 0.88 : 1.0; var compactMultiplier = _isUltraCompactMode ? 0.76 : _isCompactMode ? 0.88 : 1.0;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.46, 12, 34)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.46, 12, 34);
RootBorder.Padding = new Thickness( RootBorder.Padding = new Thickness(
Math.Clamp(12 * scale * compactMultiplier, 6, 18), Math.Clamp(12 * scale * compactMultiplier, 6, 18),
Math.Clamp(9 * scale * compactMultiplier, 5, 16)); Math.Clamp(9 * scale * compactMultiplier, 5, 16));

View File

@@ -105,7 +105,7 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
var scale = Math.Clamp(_currentCellSize / 48d, 0.78, 2.4); var scale = Math.Clamp(_currentCellSize / 48d, 0.78, 2.4);
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.44, 14, 42)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.44, 14, 42);
RootBorder.Padding = new Thickness( RootBorder.Padding = new Thickness(
Math.Clamp(14 * scale, 8, 22), Math.Clamp(14 * scale, 8, 22),
Math.Clamp(10 * scale, 6, 16)); Math.Clamp(10 * scale, 6, 16));

View File

@@ -323,7 +323,7 @@ public partial class StudyNoiseDistributionWidget : UserControl, IDesktopCompone
_isUltraCompactMode = scale < 0.74 || (Bounds.Width > 1 && Bounds.Width < 300) || (Bounds.Height > 1 && Bounds.Height < 142); _isUltraCompactMode = scale < 0.74 || (Bounds.Width > 1 && Bounds.Width < 300) || (Bounds.Height > 1 && Bounds.Height < 142);
var compactMultiplier = _isUltraCompactMode ? 0.76 : _isCompactMode ? 0.88 : 1.0; var compactMultiplier = _isUltraCompactMode ? 0.76 : _isCompactMode ? 0.88 : 1.0;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.44, 12, 34)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.44, 12, 34);
RootBorder.Padding = new Thickness( RootBorder.Padding = new Thickness(
Math.Clamp(12 * scale * compactMultiplier, 6, 18), Math.Clamp(12 * scale * compactMultiplier, 6, 18),
Math.Clamp(9 * scale * compactMultiplier, 5, 16)); Math.Clamp(9 * scale * compactMultiplier, 5, 16));

View File

@@ -258,7 +258,7 @@ public partial class StudyScoreOverviewWidget : UserControl, IDesktopComponentWi
var compactMultiplier = _isUltraCompactMode ? 0.76 : _isCompactMode ? 0.88 : 1.0; var compactMultiplier = _isUltraCompactMode ? 0.76 : _isCompactMode ? 0.88 : 1.0;
var expandedMultiplier = _isExpandedMode ? 1.12 : 1.0; var expandedMultiplier = _isExpandedMode ? 1.12 : 1.0;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.50, 14, 42)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.50, 14, 42);
RootBorder.Padding = new Thickness( RootBorder.Padding = new Thickness(
Math.Clamp(16 * scale * compactMultiplier * expandedMultiplier, 8, 30), Math.Clamp(16 * scale * compactMultiplier * expandedMultiplier, 8, 30),
Math.Clamp(14 * scale * compactMultiplier * expandedMultiplier, 6, 26)); Math.Clamp(14 * scale * compactMultiplier * expandedMultiplier, 6, 26));
@@ -305,7 +305,7 @@ public partial class StudyScoreOverviewWidget : UserControl, IDesktopComponentWi
var cardPadding = new Thickness( var cardPadding = new Thickness(
Math.Clamp(10 * scale * compactMultiplier * expandedMultiplier, 6, 20), Math.Clamp(10 * scale * compactMultiplier * expandedMultiplier, 6, 20),
Math.Clamp(8 * scale * compactMultiplier * expandedMultiplier, 4, 16)); Math.Clamp(8 * scale * compactMultiplier * expandedMultiplier, 4, 16));
var cardCornerRadius = new CornerRadius(Math.Clamp(10 * scale, 6, 18)); var cardCornerRadius = ComponentChromeCornerRadiusHelper.Scale(10 * scale, 6, 18);
AverageCardBorder.Padding = cardPadding; AverageCardBorder.Padding = cardPadding;
MinimumCardBorder.Padding = cardPadding; MinimumCardBorder.Padding = cardPadding;
MaximumCardBorder.Padding = cardPadding; MaximumCardBorder.Padding = cardPadding;

View File

@@ -268,7 +268,7 @@ public partial class StudySessionControlWidget : UserControl, IDesktopComponentW
_isUltraCompactMode = scale < 0.74 || (Bounds.Width > 1 && Bounds.Width < 180) || (Bounds.Height > 1 && Bounds.Height < 76); _isUltraCompactMode = scale < 0.74 || (Bounds.Width > 1 && Bounds.Width < 180) || (Bounds.Height > 1 && Bounds.Height < 76);
var compactMultiplier = _isUltraCompactMode ? 0.78 : _isCompactMode ? 0.90 : 1.0; var compactMultiplier = _isUltraCompactMode ? 0.78 : _isCompactMode ? 0.90 : 1.0;
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.34, 10, 28)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.34, 10, 28);
RootBorder.Padding = new Thickness( RootBorder.Padding = new Thickness(
Math.Clamp(14 * scale * compactMultiplier, 7, 22), Math.Clamp(14 * scale * compactMultiplier, 7, 22),
Math.Clamp(10 * scale * compactMultiplier, 5, 16)); Math.Clamp(10 * scale * compactMultiplier, 5, 16));

View File

@@ -237,7 +237,7 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
var rowBorder = new Border var rowBorder = new Border
{ {
CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.20, 8, 14)), CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.20, 8, 14),
Background = new SolidColorBrush(rowBackground), Background = new SolidColorBrush(rowBackground),
BorderBrush = new SolidColorBrush(rowBorderColor), BorderBrush = new SolidColorBrush(rowBorderColor),
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
@@ -588,7 +588,7 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
_isCompactMode = scale < 0.92 || (Bounds.Width > 1 && Bounds.Width < 320) || (Bounds.Height > 1 && Bounds.Height < 145); _isCompactMode = scale < 0.92 || (Bounds.Width > 1 && Bounds.Width < 320) || (Bounds.Height > 1 && Bounds.Height < 145);
_isUltraCompactMode = scale < 0.78 || (Bounds.Width > 1 && Bounds.Width < 280) || (Bounds.Height > 1 && Bounds.Height < 120); _isUltraCompactMode = scale < 0.78 || (Bounds.Width > 1 && Bounds.Width < 280) || (Bounds.Height > 1 && Bounds.Height < 120);
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.44, 12, 36)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.44, 12, 36);
RootBorder.Padding = new Thickness( RootBorder.Padding = new Thickness(
Math.Clamp(12 * scale, 7, 22), Math.Clamp(12 * scale, 7, 22),
Math.Clamp(9 * scale, 5, 16)); Math.Clamp(9 * scale, 5, 16));
@@ -606,7 +606,7 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
DialogOverlayBorder.Padding = new Thickness( DialogOverlayBorder.Padding = new Thickness(
Math.Clamp(12 * scale, 8, 20), Math.Clamp(12 * scale, 8, 20),
Math.Clamp(10 * scale, 8, 18)); Math.Clamp(10 * scale, 8, 18));
DialogCardBorder.CornerRadius = new CornerRadius(Math.Clamp(12 * scale, 10, 18)); DialogCardBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(12 * scale, 10, 18);
DialogCardBorder.Padding = new Thickness( DialogCardBorder.Padding = new Thickness(
Math.Clamp(12 * scale, 9, 20), Math.Clamp(12 * scale, 9, 20),
Math.Clamp(11 * scale, 8, 18)); Math.Clamp(11 * scale, 8, 18));

View File

@@ -197,9 +197,9 @@ public partial class TimerWidget : UserControl, IDesktopComponentWidget
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
var scale = ResolveScale(); var scale = ResolveScale();
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * scale, 12, 48)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * scale, 12, 48);
RootBorder.Padding = new Thickness(Math.Clamp(14 * scale, 7, 22)); RootBorder.Padding = new Thickness(Math.Clamp(14 * scale, 7, 22));
TimerPanelBorder.CornerRadius = new CornerRadius(Math.Clamp(32 * scale, 12, 42)); TimerPanelBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(32 * scale, 12, 42);
PlayButtonBorder.Width = Math.Clamp(42 * scale, 28, 58); PlayButtonBorder.Width = Math.Clamp(42 * scale, 28, 58);
PlayButtonBorder.Height = Math.Clamp(42 * scale, 28, 58); PlayButtonBorder.Height = Math.Clamp(42 * scale, 28, 58);

View File

@@ -11,12 +11,13 @@ using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
namespace LanMountainDesktop.Views.Components; namespace LanMountainDesktop.Views.Components;
public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentChromeContextAware
{ {
private sealed record WeatherClockConfig( private sealed record WeatherClockConfig(
string LanguageCode, string LanguageCode,
@@ -52,6 +53,7 @@ public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget,
private TimeZoneService? _timeZoneService; private TimeZoneService? _timeZoneService;
private CancellationTokenSource? _refreshCts; private CancellationTokenSource? _refreshCts;
private double _currentCellSize = 48; private double _currentCellSize = 48;
private ComponentChromeContext? _chromeContext;
private bool _isAttached; private bool _isAttached;
private bool _dialInitialized; private bool _dialInitialized;
private bool _handsInitialized; private bool _handsInitialized;
@@ -128,6 +130,13 @@ public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget,
RefreshFromSettings(); RefreshFromSettings();
} }
public void SetComponentChromeContext(ComponentChromeContext context)
{
ArgumentNullException.ThrowIfNull(context);
_chromeContext = context;
ApplyCellSize(_currentCellSize);
}
public void ApplyCellSize(double cellSize) public void ApplyCellSize(double cellSize)
{ {
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
@@ -142,12 +151,24 @@ public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget,
var compactness = Math.Clamp((176 - targetWidth) / 86d, 0, 1); var compactness = Math.Clamp((176 - targetWidth) / 86d, 0, 1);
var ultraCompact = targetWidth < 126 || targetHeight < 46; var ultraCompact = targetWidth < 126 || targetHeight < 46;
var compactFactor = Lerp(1, ultraCompact ? 0.64 : 0.72, compactness); var compactFactor = Lerp(1, ultraCompact ? 0.64 : 0.72, compactness);
var cornerRadius = Math.Clamp(targetHeight * metrics.CornerRadiusScale, 15, 36); var cornerRadius = ComponentChromeCornerRadiusHelper.Scale(
targetHeight * metrics.CornerRadiusScale,
15,
36,
_chromeContext);
var horizontalPadding = Math.Clamp(targetHeight * Lerp(0.18, 0.12, compactness), 5, 30); var horizontalPadding = ComponentChromeCornerRadiusHelper.SafeValue(
var verticalPadding = Math.Clamp(targetHeight * Lerp(0.14, 0.10, compactness), 3, 20); targetHeight * Lerp(0.18, 0.12, compactness),
5,
30,
_chromeContext);
var verticalPadding = ComponentChromeCornerRadiusHelper.SafeValue(
targetHeight * Lerp(0.14, 0.10, compactness),
3,
20,
_chromeContext);
RootBorder.CornerRadius = new CornerRadius(cornerRadius); RootBorder.CornerRadius = cornerRadius;
RootBorder.Padding = new Thickness(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); RootBorder.Padding = new Thickness(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
var columnSpacing = Math.Clamp(targetHeight * Lerp(0.16, 0.08, compactness), 2, 22); var columnSpacing = Math.Clamp(targetHeight * Lerp(0.16, 0.08, compactness), 2, 22);

View File

@@ -12,13 +12,14 @@ using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
namespace LanMountainDesktop.Views.Components; namespace LanMountainDesktop.Views.Components;
public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentChromeContextAware
{ {
private enum WeatherVisualKind private enum WeatherVisualKind
{ {
@@ -111,6 +112,7 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
private WeatherSnapshot? _latestSnapshot; private WeatherSnapshot? _latestSnapshot;
private string _languageCode = "zh-CN"; private string _languageCode = "zh-CN";
private double _currentCellSize = 48; private double _currentCellSize = 48;
private ComponentChromeContext? _chromeContext;
private WeatherVisualKind _activeVisualKind = WeatherVisualKind.ClearDay; private WeatherVisualKind _activeVisualKind = WeatherVisualKind.ClearDay;
private double _animationPhase; private double _animationPhase;
private int _activeParticleCount; private int _activeParticleCount;
@@ -197,6 +199,13 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
} }
} }
public void SetComponentChromeContext(ComponentChromeContext context)
{
ArgumentNullException.ThrowIfNull(context);
_chromeContext = context;
ApplyCellSize(_currentCellSize);
}
public void ApplyCellSize(double cellSize) public void ApplyCellSize(double cellSize)
{ {
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
@@ -204,19 +213,25 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
var metrics = HyperOS3WeatherTheme.ResolveMetrics(HyperOS3WeatherWidgetKind.Realtime2x2); var metrics = HyperOS3WeatherTheme.ResolveMetrics(HyperOS3WeatherWidgetKind.Realtime2x2);
var hostWidth = Bounds.Width > 1 ? Bounds.Width : Math.Max(80, _currentCellSize * 2); var hostWidth = Bounds.Width > 1 ? Bounds.Width : Math.Max(80, _currentCellSize * 2);
var hostHeight = Bounds.Height > 1 ? Bounds.Height : Math.Max(80, _currentCellSize * 2); var hostHeight = Bounds.Height > 1 ? Bounds.Height : Math.Max(80, _currentCellSize * 2);
var cornerRadius = Math.Clamp(_currentCellSize * metrics.CornerRadiusScale, 26, 46); var cornerRadius = ComponentChromeCornerRadiusHelper.Scale(
_currentCellSize * metrics.CornerRadiusScale,
26,
46,
_chromeContext);
var horizontalPadding = Math.Clamp(_currentCellSize * metrics.HorizontalPaddingScale, 10, 24); var horizontalPadding = Math.Clamp(_currentCellSize * metrics.HorizontalPaddingScale, 10, 24);
var verticalPadding = Math.Clamp(_currentCellSize * metrics.VerticalPaddingScale, 10, 24); var verticalPadding = Math.Clamp(_currentCellSize * metrics.VerticalPaddingScale, 10, 24);
RootBorder.CornerRadius = new CornerRadius(cornerRadius); ComponentChromeCornerRadiusHelper.Apply(
BackgroundImageLayer.CornerRadius = new CornerRadius(cornerRadius); cornerRadius,
BackgroundMotionLayer.CornerRadius = new CornerRadius(cornerRadius); RootBorder,
BackgroundTintLayer.CornerRadius = new CornerRadius(cornerRadius); BackgroundImageLayer,
BackgroundLightLayer.CornerRadius = new CornerRadius(cornerRadius); BackgroundMotionLayer,
BackgroundShadeLayer.CornerRadius = new CornerRadius(cornerRadius); BackgroundTintLayer,
BackgroundLightLayer,
BackgroundShadeLayer);
ContentPaddingBorder.Padding = new Thickness( ContentPaddingBorder.Padding = new Thickness(
Math.Clamp(Math.Min(horizontalPadding * scale, hostWidth * 0.12), 3, 24), ComponentChromeCornerRadiusHelper.SafeValue(Math.Min(horizontalPadding * scale, hostWidth * 0.12), 3, 24, _chromeContext),
Math.Clamp(Math.Min(verticalPadding * scale, hostHeight * 0.12), 3, 24)); ComponentChromeCornerRadiusHelper.SafeValue(Math.Min(verticalPadding * scale, hostHeight * 0.12), 3, 24, _chromeContext));
ApplyAdaptiveTypography(); ApplyAdaptiveTypography();
ResetParticles(); ResetParticles();
} }

View File

@@ -117,11 +117,13 @@ public partial class WhiteboardWidget : UserControl, IDesktopComponentWidget, IC
var toolbarPaddingHorizontal = Math.Clamp(buttonSize * 0.36, 6, 12); var toolbarPaddingHorizontal = Math.Clamp(buttonSize * 0.36, 6, 12);
var toolbarPaddingVertical = Math.Clamp(buttonSize * 0.24, 4, 8); var toolbarPaddingVertical = Math.Clamp(buttonSize * 0.24, 4, 8);
RootBorder.Padding = new Thickness(Math.Clamp(_currentCellSize * 0.14, 6, 14)); RootBorder.Padding = new Thickness(ComponentChromeCornerRadiusHelper.SafeValue(_currentCellSize * 0.14, 6, 14));
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.34, 12, 28)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.34, 12, 28);
CanvasBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.24, 10, 22)); CanvasBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.24, 10, 22);
ToolbarBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.22, 10, 20)); ToolbarBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.22, 10, 20);
ToolbarBorder.Padding = new Thickness(toolbarPaddingHorizontal, toolbarPaddingVertical); ToolbarBorder.Padding = new Thickness(
ComponentChromeCornerRadiusHelper.SafeValue(toolbarPaddingHorizontal, 6, 12),
ComponentChromeCornerRadiusHelper.SafeValue(toolbarPaddingVertical, 4, 8));
ToolbarButtonsPanel.Spacing = toolbarSpacing; ToolbarButtonsPanel.Spacing = toolbarSpacing;
foreach (var button in new[] { PenButton, EraserButton, ClearButton, ExportButton }) foreach (var button in new[] { PenButton, EraserButton, ClearButton, ExportButton })

View File

@@ -170,7 +170,7 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
var horizontalPadding = Math.Clamp(10 * scale, 4, 26); var horizontalPadding = Math.Clamp(10 * scale, 4, 26);
var verticalPadding = Math.Clamp(8 * scale, 3, 22); var verticalPadding = Math.Clamp(8 * scale, 3, 22);
RootBorder.Padding = new Thickness(horizontalPadding, verticalPadding); RootBorder.Padding = new Thickness(horizontalPadding, verticalPadding);
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(24 * scale, 10, 46)); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(24 * scale, 10, 46);
var usableWidth = Math.Max(48, totalWidth - horizontalPadding * 2); var usableWidth = Math.Max(48, totalWidth - horizontalPadding * 2);
var usableHeight = Math.Max(28, totalHeight - verticalPadding * 2); var usableHeight = Math.Max(28, totalHeight - verticalPadding * 2);

View File

@@ -13,11 +13,13 @@ using Avalonia.VisualTree;
using FluentIcons.Avalonia; using FluentIcons.Avalonia;
using FluentIcons.Common; using FluentIcons.Common;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Services.Settings; using LanMountainDesktop.Services.Settings;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
using LanMountainDesktop.Views.Components; using LanMountainDesktop.Views.Components;
using PathShape = Avalonia.Controls.Shapes.Path;
namespace LanMountainDesktop.Views; namespace LanMountainDesktop.Views;
@@ -579,13 +581,18 @@ public partial class MainWindow
{ {
var window = new ComponentLibraryWindow( var window = new ComponentLibraryWindow(
_componentLibraryService, _componentLibraryService,
cellSize => new ComponentLibraryCreateContext( cellSize =>
{
var appearanceSnapshot = HostAppearanceThemeProvider.GetOrCreate().GetCurrent();
return new ComponentLibraryCreateContext(
cellSize, cellSize,
appearanceSnapshot.GlobalCornerRadiusScale,
_timeZoneService, _timeZoneService,
_weatherDataService, _weatherDataService,
_recommendationInfoService, _recommendationInfoService,
_calculatorDataService, _calculatorDataService,
_settingsFacade), _settingsFacade);
},
L); L);
window.AddComponentRequested += OnDetachedComponentLibraryAddComponentRequested; window.AddComponentRequested += OnDetachedComponentLibraryAddComponentRequested;
window.Closed += OnDetachedComponentLibraryClosed; window.Closed += OnDetachedComponentLibraryClosed;
@@ -1258,7 +1265,7 @@ public partial class MainWindow
Height = handleVisualSize, Height = handleVisualSize,
IsHitTestVisible = false IsHitTestVisible = false
}; };
resizeHandleVisual.Children.Add(new Path resizeHandleVisual.Children.Add(new PathShape
{ {
Data = arcData, Data = arcData,
Stretch = Stretch.Fill, Stretch = Stretch.Fill,
@@ -1266,7 +1273,7 @@ public partial class MainWindow
StrokeThickness = arcThickness + 3, StrokeThickness = arcThickness + 3,
StrokeLineCap = PenLineCap.Round StrokeLineCap = PenLineCap.Round
}); });
resizeHandleVisual.Children.Add(new Path resizeHandleVisual.Children.Add(new PathShape
{ {
Data = arcData, Data = arcData,
Stretch = Stretch.Fill, Stretch = Stretch.Fill,
@@ -1513,12 +1520,20 @@ public partial class MainWindow
private double GetComponentCornerRadius(string componentId) private double GetComponentCornerRadius(string componentId)
{ {
var appearanceSnapshot = HostAppearanceThemeProvider.GetOrCreate().GetCurrent();
if (_componentRuntimeRegistry.TryGetDescriptor(componentId, out var runtimeDescriptor)) if (_componentRuntimeRegistry.TryGetDescriptor(componentId, out var runtimeDescriptor))
{ {
return runtimeDescriptor.ResolveCornerRadius(_currentDesktopCellSize); return runtimeDescriptor.ResolveCornerRadius(new ComponentChromeContext(
componentId,
null,
_currentDesktopCellSize,
appearanceSnapshot.GlobalCornerRadiusScale,
appearanceSnapshot.CornerRadiusTokens));
} }
return Math.Clamp(_currentDesktopCellSize * 0.22, 8, 18); var scale = Math.Max(0.1d, appearanceSnapshot.GlobalCornerRadiusScale);
return Math.Clamp(_currentDesktopCellSize * 0.22, 8, 18) * scale;
} }
private Thickness GetDesktopComponentVisualInset(int widthCells, int heightCells) private Thickness GetDesktopComponentVisualInset(int widthCells, int heightCells)
@@ -1767,8 +1782,10 @@ public partial class MainWindow
{ {
try try
{ {
var appearanceSnapshot = HostAppearanceThemeProvider.GetOrCreate().GetCurrent();
var createContext = new ComponentLibraryCreateContext( var createContext = new ComponentLibraryCreateContext(
cellSize, cellSize,
appearanceSnapshot.GlobalCornerRadiusScale,
_timeZoneService, _timeZoneService,
_weatherDataService, _weatherDataService,
_recommendationInfoService, _recommendationInfoService,

View File

@@ -83,7 +83,7 @@
Background="{Binding NeutralLightPreviewBrush}" Background="{Binding NeutralLightPreviewBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}" BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="14" /> CornerRadius="{DynamicResource DesignCornerRadiusSm}" />
<TextBlock Text="{Binding PreviewNeutralLightLabel}" <TextBlock Text="{Binding PreviewNeutralLightLabel}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -94,7 +94,7 @@
Background="{Binding NeutralDarkPreviewBrush}" Background="{Binding NeutralDarkPreviewBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}" BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="14" /> CornerRadius="{DynamicResource DesignCornerRadiusSm}" />
<TextBlock Text="{Binding PreviewNeutralDarkLabel}" <TextBlock Text="{Binding PreviewNeutralDarkLabel}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -110,7 +110,7 @@
Background="{Binding PrimarySwatchBrush}" Background="{Binding PrimarySwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}" BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="14" /> CornerRadius="{DynamicResource DesignCornerRadiusSm}" />
<TextBlock Text="{Binding PreviewPrimaryLabel}" <TextBlock Text="{Binding PreviewPrimaryLabel}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -122,7 +122,7 @@
Background="{Binding SecondarySwatchBrush}" Background="{Binding SecondarySwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}" BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="14" /> CornerRadius="{DynamicResource DesignCornerRadiusSm}" />
<TextBlock Text="{Binding PreviewSecondaryLabel}" <TextBlock Text="{Binding PreviewSecondaryLabel}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -134,7 +134,7 @@
Background="{Binding TertiarySwatchBrush}" Background="{Binding TertiarySwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}" BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="14" /> CornerRadius="{DynamicResource DesignCornerRadiusSm}" />
<TextBlock Text="{Binding PreviewTertiaryLabel}" <TextBlock Text="{Binding PreviewTertiaryLabel}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -146,7 +146,7 @@
Background="{Binding NeutralSwatchBrush}" Background="{Binding NeutralSwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}" BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="14" /> CornerRadius="{DynamicResource DesignCornerRadiusSm}" />
<TextBlock Text="{Binding PreviewNeutralLabel}" <TextBlock Text="{Binding PreviewNeutralLabel}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -177,7 +177,7 @@
Background="{Binding SeedSwatchBrush}" Background="{Binding SeedSwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}" BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="14" /> CornerRadius="{DynamicResource DesignCornerRadiusSm}" />
<TextBlock Text="{Binding PreviewSeedLabel}" <TextBlock Text="{Binding PreviewSeedLabel}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -217,7 +217,7 @@
Background="{Binding Brush}" Background="{Binding Brush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}" BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="12" /> CornerRadius="{DynamicResource DesignCornerRadiusXs}" />
<TextBlock Text="{Binding Label}" <TextBlock Text="{Binding Label}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextAlignment="Center" TextAlignment="Center"
@@ -240,7 +240,7 @@
Background="{Binding SeedSwatchBrush}" Background="{Binding SeedSwatchBrush}"
BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}" BorderBrush="{DynamicResource AdaptiveGlassPanelBorderBrush}"
BorderThickness="1" BorderThickness="1"
CornerRadius="14" /> CornerRadius="{DynamicResource DesignCornerRadiusSm}" />
<TextBlock Text="{Binding PreviewSeedLabel}" <TextBlock Text="{Binding PreviewSeedLabel}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextAlignment="Center" /> TextAlignment="Center" />

View File

@@ -8,16 +8,14 @@
x:DataType="vm:ComponentsSettingsPageViewModel"> x:DataType="vm:ComponentsSettingsPageViewModel">
<ScrollViewer VerticalScrollBarVisibility="Auto"> <ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Classes="settings-page-container"> <StackPanel Classes="settings-page-container">
<controls:IconText Icon="Apps"
<!-- 网格布局设置分组 --> Text="{Binding ComponentsHeader}"
<controls:IconText Icon="GridDots"
Text="{Binding GridHeader}"
Margin="0,0,0,4" /> Margin="0,0,0,4" />
<ui:SettingsExpander Header="{Binding GridHeader}" <ui:SettingsExpander Header="{Binding ComponentsHeader}"
IsExpanded="True"> IsExpanded="True">
<ui:SettingsExpander.IconSource> <ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="GridDots" /> <fi:SymbolIconSource Symbol="Apps" />
</ui:SettingsExpander.IconSource> </ui:SettingsExpander.IconSource>
<ui:SettingsExpanderItem> <ui:SettingsExpanderItem>
<Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="16"> <Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="16">
@@ -71,6 +69,33 @@
</ui:SettingsExpanderItem> </ui:SettingsExpanderItem>
</ui:SettingsExpander> </ui:SettingsExpander>
<controls:IconText Icon="ShapeOrganic"
Text="{Binding ComponentRadiusHeader}"
Margin="0,12,0,4" />
<ui:SettingsExpander Header="{Binding GlobalCornerRadiusLabel}"
Description="{Binding GlobalCornerRadiusDescription}">
<ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="ShapeOrganic" />
</ui:SettingsExpander.IconSource>
<ui:SettingsExpanderItem>
<Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="16">
<TextBlock Text="{Binding GlobalCornerRadiusLabel}"
VerticalAlignment="Center" />
<Slider Grid.Column="1"
Minimum="0.7"
Maximum="1.4"
IsSnapToTickEnabled="True"
TickFrequency="0.05"
Value="{Binding GlobalCornerRadiusScale}" />
<TextBlock Grid.Column="2"
Width="48"
Text="{Binding GlobalCornerRadiusScale, StringFormat={}{0:F2}x}"
VerticalAlignment="Center"
HorizontalAlignment="Right" />
</Grid>
</ui:SettingsExpanderItem>
</ui:SettingsExpander>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</UserControl> </UserControl>

View File

@@ -8,7 +8,7 @@ namespace LanMountainDesktop.Views.SettingsPages;
"components", "components",
"Components", "Components",
SettingsPageCategory.Components, SettingsPageCategory.Components,
IconKey = "GridDots", IconKey = "Apps",
SortOrder = 20, SortOrder = 20,
TitleLocalizationKey = "settings.components.title", TitleLocalizationKey = "settings.components.title",
DescriptionLocalizationKey = "settings.components.description")] DescriptionLocalizationKey = "settings.components.description")]

View File

@@ -515,7 +515,7 @@ internal sealed class PluginMarketEmbeddedView : UserControl, IDisposable
Background = isSelected ? SelectedSurfaceBrush : SurfaceBrush, Background = isSelected ? SelectedSurfaceBrush : SurfaceBrush,
BorderBrush = isSelected ? SelectedBorderBrush : CardBorderBrush, BorderBrush = isSelected ? SelectedBorderBrush : CardBorderBrush,
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(18), CornerRadius = ResolveCornerRadiusResource("DesignCornerRadiusMd", 18),
Padding = new Thickness(14), Padding = new Thickness(14),
Child = layoutGrid Child = layoutGrid
}; };
@@ -597,7 +597,7 @@ internal sealed class PluginMarketEmbeddedView : UserControl, IDisposable
detailPanel.Children.Add(new Border detailPanel.Children.Add(new Border
{ {
Background = new SolidColorBrush(Color.Parse("#24FFC42B1C")), Background = new SolidColorBrush(Color.Parse("#24FFC42B1C")),
CornerRadius = new CornerRadius(14), CornerRadius = ResolveCornerRadiusResource("DesignCornerRadiusSm", 14),
Padding = new Thickness(12), Padding = new Thickness(12),
Child = new TextBlock Child = new TextBlock
{ {
@@ -617,7 +617,7 @@ internal sealed class PluginMarketEmbeddedView : UserControl, IDisposable
Background = SurfaceBrush, Background = SurfaceBrush,
BorderBrush = CardBorderBrush, BorderBrush = CardBorderBrush,
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(16), CornerRadius = ResolveCornerRadiusResource("DesignCornerRadiusMd", 16),
Padding = new Thickness(16), Padding = new Thickness(16),
Child = new TextBlock Child = new TextBlock
{ {
@@ -1011,7 +1011,7 @@ internal sealed class PluginMarketEmbeddedView : UserControl, IDisposable
return new Border return new Border
{ {
Background = SurfaceBrush, Background = SurfaceBrush,
CornerRadius = new CornerRadius(18), CornerRadius = ResolveCornerRadiusResource("DesignCornerRadiusMd", 18),
Padding = new Thickness(padding) Padding = new Thickness(padding)
}; };
} }
@@ -1021,7 +1021,7 @@ internal sealed class PluginMarketEmbeddedView : UserControl, IDisposable
return new Border return new Border
{ {
Background = SurfaceBrush, Background = SurfaceBrush,
CornerRadius = new CornerRadius(16), CornerRadius = ResolveCornerRadiusResource("DesignCornerRadiusMd", 16),
BorderBrush = CardBorderBrush, BorderBrush = CardBorderBrush,
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
Padding = new Thickness(18), Padding = new Thickness(18),
@@ -1124,7 +1124,7 @@ internal sealed class PluginMarketEmbeddedView : UserControl, IDisposable
Background = SurfaceBrush, Background = SurfaceBrush,
BorderBrush = CardBorderBrush, BorderBrush = CardBorderBrush,
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(14), CornerRadius = ResolveCornerRadiusResource("DesignCornerRadiusSm", 14),
Padding = new Thickness(14), Padding = new Thickness(14),
Child = new StackPanel Child = new StackPanel
{ {
@@ -1154,7 +1154,7 @@ internal sealed class PluginMarketEmbeddedView : UserControl, IDisposable
Background = SurfaceBrush, Background = SurfaceBrush,
BorderBrush = CardBorderBrush, BorderBrush = CardBorderBrush,
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(14), CornerRadius = ResolveCornerRadiusResource("DesignCornerRadiusSm", 14),
Padding = new Thickness(14), Padding = new Thickness(14),
Child = new StackPanel Child = new StackPanel
{ {
@@ -1257,4 +1257,11 @@ internal sealed class PluginMarketEmbeddedView : UserControl, IDisposable
_ => "Install" _ => "Install"
}; };
} }
private static CornerRadius ResolveCornerRadiusResource(string key, double fallback)
{
return Application.Current?.TryFindResource(key, out var resource) == true && resource is CornerRadius radius
? radius
: new CornerRadius(fallback);
}
} }