mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
0.7.0.2
This commit is contained in:
@@ -33,7 +33,7 @@ public sealed class PluginDesktopComponentContext
|
||||
ComponentId = componentId.Trim();
|
||||
PlacementId = string.IsNullOrWhiteSpace(placementId) ? null : placementId.Trim();
|
||||
CellSize = Math.Max(1, cellSize);
|
||||
GlobalCornerRadiusScale = Math.Max(0.1d, globalCornerRadiusScale);
|
||||
GlobalCornerRadiusScale = Math.Max(0d, globalCornerRadiusScale);
|
||||
CornerRadiusTokens = cornerRadiusTokens;
|
||||
PluginSettings = pluginSettings;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,8 @@ 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 const double MinimumCornerRadiusScale = 0.0;
|
||||
public const double MaximumCornerRadiusScale = 2.50;
|
||||
|
||||
public static double NormalizeCornerRadiusScale(double value)
|
||||
{
|
||||
@@ -14,7 +13,6 @@ public static class GlobalAppearanceSettings
|
||||
return DefaultCornerRadiusScale;
|
||||
}
|
||||
|
||||
var clamped = Math.Clamp(value, MinimumCornerRadiusScale, MaximumCornerRadiusScale);
|
||||
return Math.Round(clamped / CornerRadiusScaleStep, MidpointRounding.AwayFromZero) * CornerRadiusScaleStep;
|
||||
return Math.Clamp(value, MinimumCornerRadiusScale, MaximumCornerRadiusScale);
|
||||
}
|
||||
}
|
||||
|
||||
67
LanMountainDesktop.Tests/CornerRadiusScaleTests.cs
Normal file
67
LanMountainDesktop.Tests/CornerRadiusScaleTests.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Avalonia;
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using LanMountainDesktop.Settings.Core;
|
||||
using LanMountainDesktop.Shared.Contracts;
|
||||
using Xunit;
|
||||
|
||||
namespace LanMountainDesktop.Tests;
|
||||
|
||||
public sealed class CornerRadiusScaleTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(-1d, 0d)]
|
||||
[InlineData(0d, 0d)]
|
||||
[InlineData(0.33d, 0.33d)]
|
||||
[InlineData(1.234d, 1.234d)]
|
||||
[InlineData(2.5d, 2.5d)]
|
||||
[InlineData(3d, 2.5d)]
|
||||
public void NormalizeCornerRadiusScale_ClampsWithoutSnapping(double input, double expected)
|
||||
{
|
||||
Assert.Equal(expected, GlobalAppearanceSettings.NormalizeCornerRadiusScale(input), 3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NormalizeCornerRadiusScale_UsesDefaultForInvalidValues()
|
||||
{
|
||||
Assert.Equal(
|
||||
GlobalAppearanceSettings.DefaultCornerRadiusScale,
|
||||
GlobalAppearanceSettings.NormalizeCornerRadiusScale(double.NaN),
|
||||
3);
|
||||
Assert.Equal(
|
||||
GlobalAppearanceSettings.DefaultCornerRadiusScale,
|
||||
GlobalAppearanceSettings.NormalizeCornerRadiusScale(double.PositiveInfinity),
|
||||
3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PluginDesktopComponentContext_AllowsZeroRadiusScaling()
|
||||
{
|
||||
var context = new PluginDesktopComponentContext(
|
||||
new PluginManifest("plugin.id", "Plugin Name", "plugin.dll"),
|
||||
"C:\\Plugins\\plugin.id",
|
||||
"C:\\Data\\plugin.id",
|
||||
new NullServiceProvider(),
|
||||
new Dictionary<string, object?>(),
|
||||
"component-1",
|
||||
null,
|
||||
96d,
|
||||
0d,
|
||||
new AppearanceCornerRadiusTokens(
|
||||
new CornerRadius(6),
|
||||
new CornerRadius(10),
|
||||
new CornerRadius(14),
|
||||
new CornerRadius(18),
|
||||
new CornerRadius(24),
|
||||
new CornerRadius(30),
|
||||
new CornerRadius(36)));
|
||||
|
||||
Assert.Equal(0d, context.GlobalCornerRadiusScale, 3);
|
||||
Assert.Equal(0d, context.ResolveScaledCornerRadius(12d), 3);
|
||||
Assert.Equal(0d, context.ResolveScaledCornerRadius(12d, 8d, 18d), 3);
|
||||
}
|
||||
|
||||
private sealed class NullServiceProvider : IServiceProvider
|
||||
{
|
||||
public object? GetService(Type serviceType) => null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using LanMountainDesktop.Host.Abstractions;
|
||||
using LanMountainDesktop.Shared.Contracts;
|
||||
using LanMountainDesktop.Views.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace LanMountainDesktop.Tests;
|
||||
|
||||
public sealed class DesktopComponentRuntimeRegistrationCornerRadiusTests
|
||||
{
|
||||
[Fact]
|
||||
public void LegacyCellSizeResolver_AppliesGlobalCornerRadiusScale()
|
||||
{
|
||||
var registration = new DesktopComponentRuntimeRegistration(
|
||||
componentId: "test.component",
|
||||
displayNameLocalizationKey: null,
|
||||
controlFactory: () => new Border(),
|
||||
cornerRadiusResolver: cellSize => Math.Clamp(cellSize * 0.30, 10, 40));
|
||||
|
||||
var resolver = Assert.IsType<Func<ComponentChromeContext, double>>(registration.CornerRadiusResolver);
|
||||
var resolved = resolver(CreateChromeContext(cellSize: 120, globalScale: 2.0));
|
||||
|
||||
Assert.Equal(72.0, resolved, 3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChromeContextResolver_IsNotDoubleScaledByRegistrationWrapper()
|
||||
{
|
||||
var registration = new DesktopComponentRuntimeRegistration(
|
||||
componentId: "test.component",
|
||||
displayNameLocalizationKey: null,
|
||||
controlFactory: _ => new Border(),
|
||||
cornerRadiusResolver: chromeContext => chromeContext.CellSize + chromeContext.GlobalCornerRadiusScale);
|
||||
|
||||
var resolver = Assert.IsType<Func<ComponentChromeContext, double>>(registration.CornerRadiusResolver);
|
||||
var resolved = resolver(CreateChromeContext(cellSize: 50, globalScale: 2.5));
|
||||
|
||||
Assert.Equal(52.5, resolved, 3);
|
||||
}
|
||||
|
||||
private static ComponentChromeContext CreateChromeContext(double cellSize, double globalScale)
|
||||
{
|
||||
return new ComponentChromeContext(
|
||||
ComponentId: "test.component",
|
||||
PlacementId: null,
|
||||
CellSize: cellSize,
|
||||
GlobalCornerRadiusScale: globalScale,
|
||||
CornerRadiusTokens: new AppearanceCornerRadiusTokens(
|
||||
new CornerRadius(6),
|
||||
new CornerRadius(10),
|
||||
new CornerRadius(14),
|
||||
new CornerRadius(18),
|
||||
new CornerRadius(24),
|
||||
new CornerRadius(30),
|
||||
new CornerRadius(36)));
|
||||
}
|
||||
}
|
||||
@@ -313,7 +313,7 @@
|
||||
"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.components.corner_radius.description": "Adjust the shared corner radius from a square edge to a capsule-like shape, and expand the internal safe area with it.",
|
||||
"settings.update.title": "Update",
|
||||
"settings.update.current_version_label": "Current Version",
|
||||
"settings.update.latest_version_label": "Latest Release",
|
||||
|
||||
@@ -312,7 +312,7 @@
|
||||
"settings.components.spacing_relaxed": "宽松",
|
||||
"settings.components.corner_radius.header": "圆角设计",
|
||||
"settings.components.corner_radius.label": "组件圆角",
|
||||
"settings.components.corner_radius.description": "统一调整组件容器圆角,并随圆角增大同步扩展内部安全区。",
|
||||
"settings.components.corner_radius.description": "将组件容器圆角从直角连续调到接近胶囊的形态,并随圆角增大同步扩展内部安全区。",
|
||||
"settings.update.title": "更新",
|
||||
"settings.update.current_version_label": "当前版本",
|
||||
"settings.update.latest_version_label": "最新发布",
|
||||
|
||||
@@ -1010,6 +1010,10 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase
|
||||
[ObservableProperty]
|
||||
private double _globalCornerRadiusScale = GlobalAppearanceSettings.DefaultCornerRadiusScale;
|
||||
|
||||
public double GlobalCornerRadiusMinimum => GlobalAppearanceSettings.MinimumCornerRadiusScale;
|
||||
|
||||
public double GlobalCornerRadiusMaximum => GlobalAppearanceSettings.MaximumCornerRadiusScale;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _componentRadiusHeader = string.Empty;
|
||||
|
||||
@@ -1129,7 +1133,9 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase
|
||||
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.");
|
||||
GlobalCornerRadiusDescription = L(
|
||||
"settings.components.corner_radius.description",
|
||||
"Adjust the shared corner radius from a square edge to a capsule-like shape, and expand the internal safe area with it.");
|
||||
}
|
||||
|
||||
private string L(string key, string fallback)
|
||||
|
||||
@@ -3,6 +3,7 @@ using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using LanMountainDesktop.Host.Abstractions;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Settings.Core;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
@@ -12,10 +13,12 @@ internal static class ComponentChromeCornerRadiusHelper
|
||||
{
|
||||
if (chromeContext is not null)
|
||||
{
|
||||
return Math.Max(0.1d, chromeContext.GlobalCornerRadiusScale);
|
||||
return Math.Max(GlobalAppearanceSettings.MinimumCornerRadiusScale, chromeContext.GlobalCornerRadiusScale);
|
||||
}
|
||||
|
||||
return Math.Max(0.1d, HostAppearanceThemeProvider.GetOrCreate().GetCurrent().GlobalCornerRadiusScale);
|
||||
return Math.Max(
|
||||
GlobalAppearanceSettings.MinimumCornerRadiusScale,
|
||||
HostAppearanceThemeProvider.GetOrCreate().GetCurrent().GlobalCornerRadiusScale);
|
||||
}
|
||||
|
||||
public static CornerRadius Scale(double baseRadius, double min, double max, ComponentChromeContext? chromeContext = null)
|
||||
|
||||
@@ -9,6 +9,7 @@ using LanMountainDesktop.Host.Abstractions;
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Services.Settings;
|
||||
using LanMountainDesktop.Settings.Core;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
@@ -36,7 +37,10 @@ public sealed class DesktopComponentRuntimeRegistration
|
||||
componentId,
|
||||
displayNameLocalizationKey,
|
||||
_ => controlFactory(),
|
||||
cornerRadiusResolver is null ? null : chromeContext => cornerRadiusResolver(chromeContext.CellSize))
|
||||
cornerRadiusResolver is null
|
||||
? null
|
||||
: chromeContext => cornerRadiusResolver(chromeContext.CellSize) *
|
||||
Math.Max(GlobalAppearanceSettings.MinimumCornerRadiusScale, chromeContext.GlobalCornerRadiusScale))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -49,7 +53,10 @@ public sealed class DesktopComponentRuntimeRegistration
|
||||
componentId,
|
||||
displayNameLocalizationKey,
|
||||
controlFactory,
|
||||
cornerRadiusResolver is null ? null : chromeContext => cornerRadiusResolver(chromeContext.CellSize))
|
||||
cornerRadiusResolver is null
|
||||
? null
|
||||
: chromeContext => cornerRadiusResolver(chromeContext.CellSize) *
|
||||
Math.Max(GlobalAppearanceSettings.MinimumCornerRadiusScale, chromeContext.GlobalCornerRadiusScale))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -84,7 +91,7 @@ public sealed class DesktopComponentRuntimeDescriptor
|
||||
private static readonly Func<ComponentChromeContext, double> DefaultCornerRadiusResolver =
|
||||
chromeContext =>
|
||||
{
|
||||
var scale = Math.Max(0.1d, chromeContext.GlobalCornerRadiusScale);
|
||||
var scale = Math.Max(GlobalAppearanceSettings.MinimumCornerRadiusScale, chromeContext.GlobalCornerRadiusScale);
|
||||
var baseRadius = Math.Clamp(chromeContext.CellSize * 0.22, 8, 18);
|
||||
return Math.Clamp(baseRadius * scale, 8 * scale, 18 * scale);
|
||||
};
|
||||
@@ -492,8 +499,9 @@ public sealed class DesktopComponentRuntimeRegistry
|
||||
new DesktopComponentRuntimeRegistration(
|
||||
BuiltInComponentIds.DesktopOfficeRecentDocuments,
|
||||
"component.office_recent_documents",
|
||||
() => new OfficeRecentDocumentsWidget(),
|
||||
cellSize => Math.Clamp(cellSize * 0.50, 10, 24)),
|
||||
_ => new OfficeRecentDocumentsWidget(),
|
||||
chromeContext => Math.Clamp(chromeContext.CellSize * 0.50, 10, 24) *
|
||||
Math.Max(GlobalAppearanceSettings.MinimumCornerRadiusScale, chromeContext.GlobalCornerRadiusScale)),
|
||||
new DesktopComponentRuntimeRegistration(
|
||||
BuiltInComponentIds.DesktopRemovableStorage,
|
||||
"component.removable_storage",
|
||||
|
||||
@@ -63,8 +63,7 @@ public partial class MusicControlWidget : UserControl, IDesktopComponentWidget,
|
||||
_currentCellSize = Math.Max(1, cellSize);
|
||||
var scale = ResolveScale();
|
||||
|
||||
var rootRadius = Math.Clamp(30 * scale, 16, 44);
|
||||
var rootCornerRadius = new CornerRadius(rootRadius);
|
||||
var rootCornerRadius = ComponentChromeCornerRadiusHelper.Scale(30 * scale, 16, 44);
|
||||
|
||||
RootBorder.CornerRadius = rootCornerRadius;
|
||||
ContentPaddingBorder.Padding = new Thickness(
|
||||
@@ -85,7 +84,7 @@ public partial class MusicControlWidget : UserControl, IDesktopComponentWidget,
|
||||
|
||||
CoverBorder.Width = Math.Clamp(56 * scale, 38, 86);
|
||||
CoverBorder.Height = Math.Clamp(56 * scale, 38, 86);
|
||||
CoverBorder.CornerRadius = new CornerRadius(Math.Clamp(12 * scale, 8, 16));
|
||||
CoverBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(12 * scale, 8, 16);
|
||||
|
||||
TitleTextBlock.FontSize = Math.Clamp(20 * scale, 12, 28);
|
||||
ArtistTextBlock.FontSize = Math.Clamp(14 * scale, 9, 18);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
x:Class="LanMountainDesktop.Views.Components.OfficeRecentDocumentsWidget">
|
||||
|
||||
<Border x:Name="RootBorder"
|
||||
CornerRadius="34"
|
||||
CornerRadius="{DynamicResource DesignCornerRadiusIsland}"
|
||||
Background="#2D5A8E"
|
||||
ClipToBounds="True"
|
||||
BorderThickness="0"
|
||||
@@ -39,7 +39,7 @@
|
||||
Grid.Column="1"
|
||||
Width="28"
|
||||
Height="28"
|
||||
CornerRadius="14"
|
||||
CornerRadius="{DynamicResource DesignCornerRadiusSm}"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0"
|
||||
@@ -67,7 +67,7 @@
|
||||
<Border x:Name="DocumentCard"
|
||||
Width="130"
|
||||
Height="90"
|
||||
CornerRadius="10"
|
||||
CornerRadius="{DynamicResource DesignCornerRadiusXs}"
|
||||
Background="#3AFFFFFF"
|
||||
Padding="10"
|
||||
Cursor="Hand"
|
||||
|
||||
@@ -36,8 +36,7 @@ public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponen
|
||||
return;
|
||||
}
|
||||
|
||||
var scale = cellSize / 100.0;
|
||||
RootBorder.CornerRadius = new Avalonia.CornerRadius(Math.Max(8, 34 * scale));
|
||||
RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(cellSize * 0.50, 10, 24);
|
||||
}
|
||||
|
||||
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
||||
|
||||
@@ -17,6 +17,7 @@ using LanMountainDesktop.Host.Abstractions;
|
||||
using LanMountainDesktop.Models;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Services.Settings;
|
||||
using LanMountainDesktop.Settings.Core;
|
||||
using LanMountainDesktop.Theme;
|
||||
using LanMountainDesktop.Views.Components;
|
||||
using PathShape = Avalonia.Controls.Shapes.Path;
|
||||
@@ -1532,7 +1533,7 @@ public partial class MainWindow
|
||||
appearanceSnapshot.CornerRadiusTokens));
|
||||
}
|
||||
|
||||
var scale = Math.Max(0.1d, appearanceSnapshot.GlobalCornerRadiusScale);
|
||||
var scale = Math.Max(GlobalAppearanceSettings.MinimumCornerRadiusScale, appearanceSnapshot.GlobalCornerRadiusScale);
|
||||
return Math.Clamp(_currentDesktopCellSize * 0.22, 8, 18) * scale;
|
||||
}
|
||||
|
||||
|
||||
@@ -516,6 +516,7 @@ public partial class MainWindow
|
||||
SystemMaterialMode = latestThemeState.SystemMaterialMode,
|
||||
SelectedWallpaperSeed = latestThemeState.SelectedWallpaperSeed,
|
||||
UseSystemChrome = latestThemeState.UseSystemChrome,
|
||||
GlobalCornerRadiusScale = latestThemeState.GlobalCornerRadiusScale,
|
||||
WallpaperPath = latestWallpaperState.WallpaperPath,
|
||||
WallpaperType = latestWallpaperState.Type,
|
||||
WallpaperColor = string.Equals(latestWallpaperState.Type, "SolidColor", StringComparison.OrdinalIgnoreCase)
|
||||
|
||||
@@ -83,13 +83,13 @@
|
||||
<TextBlock Text="{Binding GlobalCornerRadiusLabel}"
|
||||
VerticalAlignment="Center" />
|
||||
<Slider Grid.Column="1"
|
||||
Minimum="0.7"
|
||||
Maximum="1.4"
|
||||
IsSnapToTickEnabled="True"
|
||||
TickFrequency="0.05"
|
||||
Minimum="{Binding GlobalCornerRadiusMinimum}"
|
||||
Maximum="{Binding GlobalCornerRadiusMaximum}"
|
||||
SmallChange="0.01"
|
||||
LargeChange="0.1"
|
||||
Value="{Binding GlobalCornerRadiusScale}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Width="48"
|
||||
Width="56"
|
||||
Text="{Binding GlobalCornerRadiusScale, StringFormat={}{0:F2}x}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right" />
|
||||
|
||||
Reference in New Issue
Block a user