diff --git a/.gitignore b/.gitignore index a2037bb..56e5892 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ # dotenv files .env +# Local NuGet global packages (NuGet.Config globalPackagesFolder) +.nuget/packages/ + # User-specific files *.rsuser *.suo diff --git a/LanMountainDesktop/Services/AppearanceThemeService.cs b/LanMountainDesktop/Services/AppearanceThemeService.cs index 805c365..7139342 100644 --- a/LanMountainDesktop/Services/AppearanceThemeService.cs +++ b/LanMountainDesktop/Services/AppearanceThemeService.cs @@ -353,6 +353,26 @@ internal sealed class MaterialSurfaceService : IMaterialSurfaceService MaterialSurfaceRole role, bool isNightMode) { + // Settings 根层(如 RootGrid)叠在 Transparent + Mica/Acrylic 上:过高 alpha 会完全盖住系统 backdrop。 + // 保持非 None 下较低 alpha;None 仍用不透明白底等价。BlurRadius=0(由 DWM 提供模糊)。 + if (role == MaterialSurfaceRole.SettingsWindowBackground) + { + return materialMode switch + { + ThemeAppearanceValues.MaterialAcrylic => ( + 0.20, + 0.14, + isNightMode ? (byte)0x8E : (byte)0x96, + 0), + ThemeAppearanceValues.MaterialMica => ( + 0.14, + 0.08, + isNightMode ? (byte)0x9E : (byte)0xA6, + 0), + _ => (0.08, 0.05, (byte)0xFF, 0) + }; + } + var isOverlay = role is MaterialSurfaceRole.DockBackground or MaterialSurfaceRole.StatusBarBackground or MaterialSurfaceRole.OverlayPanel; return materialMode switch { @@ -491,7 +511,8 @@ internal sealed class AppearanceThemeService : IAppearanceThemeService, IDisposa // Avoid hot-switching real backdrops on already-visible windows. This has been // a stability hotspot when users flip theme source/material at runtime. - if (window.IsVisible) + // SettingsWindowBackground 是唯一需要材质与资源同步热切换的宿主角色;其它窗口仍保持「仅创建时」应用以降低风险。 + if (window.IsVisible && role != MaterialSurfaceRole.SettingsWindowBackground) { return; } diff --git a/LanMountainDesktop/Services/GlassEffectService.cs b/LanMountainDesktop/Services/GlassEffectService.cs index 2f071b7..9c0b2fd 100644 --- a/LanMountainDesktop/Services/GlassEffectService.cs +++ b/LanMountainDesktop/Services/GlassEffectService.cs @@ -69,6 +69,15 @@ public static class GlassEffectService resources["AdaptiveWindowBackgroundBrush"] = new SolidColorBrush(windowSurface.BackgroundColor); resources["AdaptiveWindowBorderBrush"] = new SolidColorBrush(windowSurface.BorderColor); resources["AdaptiveSettingsWindowBackgroundBrush"] = new SolidColorBrush(settingsWindowSurface.BackgroundColor); + // 可选:叠在内容区上的可读性 tint(半透明);不改变 AdaptiveSettingsWindowBackgroundBrush 的语义权重,供 P1 绑定内容层。 + var settingsTintBase = settingsWindowSurface.BackgroundColor; + var settingsTintAlpha = ResolveSettingsWindowTintAlpha(context); + resources["AdaptiveSettingsWindowTintBrush"] = new SolidColorBrush( + Color.FromArgb( + settingsTintAlpha, + settingsTintBase.R, + settingsTintBase.G, + settingsTintBase.B)); resources["AdaptiveSettingsWindowBorderBrush"] = new SolidColorBrush(settingsWindowSurface.BorderColor); resources["AdaptiveDockBackgroundBrush"] = new SolidColorBrush(dockSurface.BackgroundColor); resources["AdaptiveDockBorderBrush"] = new SolidColorBrush(dockSurface.BorderColor); @@ -100,4 +109,16 @@ public static class GlassEffectService resources["AdaptiveDesktopComponentHostOpacity"] = desktopComponentSurface.Opacity; resources["AdaptiveStatusBarComponentHostOpacity"] = statusBarComponentSurface.Opacity; } + + /// 可选内容叠层 alpha,与设置窗表面色相一致;None 为 0 避免重复染色。 + private static byte ResolveSettingsWindowTintAlpha(ThemeColorContext context) + { + var mode = ThemeAppearanceValues.NormalizeSystemMaterialMode(context.SystemMaterialMode); + return mode switch + { + ThemeAppearanceValues.MaterialAcrylic => context.IsNightMode ? (byte)0x58 : (byte)0x4C, + ThemeAppearanceValues.MaterialMica => context.IsNightMode ? (byte)0x50 : (byte)0x44, + _ => (byte)0x00 + }; + } } diff --git a/LanMountainDesktop/Services/Settings/SettingsWindowService.cs b/LanMountainDesktop/Services/Settings/SettingsWindowService.cs index edc0ad1..147e4d6 100644 --- a/LanMountainDesktop/Services/Settings/SettingsWindowService.cs +++ b/LanMountainDesktop/Services/Settings/SettingsWindowService.cs @@ -71,7 +71,7 @@ internal sealed class SettingsWindowService : ISettingsWindowService _window ??= CreateWindow(); var appearanceSnapshot = _appearanceThemeService.GetCurrent(); _window.ApplyChromeMode(appearanceSnapshot.UseSystemChrome); - ApplyTheme(_window); + ApplyThemeVariantAndResources(_window); var targetPageId = request.PageId ?? _window.ViewModel.CurrentPageId; _window.ReloadPages(targetPageId); @@ -79,6 +79,7 @@ internal sealed class SettingsWindowService : ISettingsWindowService if (!_window.IsVisible) { CenterWindow(_window, request); + _appearanceThemeService.ApplyWindowMaterial(_window, MaterialSurfaceRole.SettingsWindowBackground); _window.Show(); NotifyStateChanged(); CenterWindowLater(_window, request); @@ -90,6 +91,7 @@ internal sealed class SettingsWindowService : ISettingsWindowService _window.WindowState = WindowState.Normal; } + _appearanceThemeService.ApplyWindowMaterial(_window, MaterialSurfaceRole.SettingsWindowBackground); _window.Activate(); } @@ -113,7 +115,6 @@ internal sealed class SettingsWindowService : ISettingsWindowService _pageRegistry, _hostApplicationLifecycle, useSystemChrome); - ApplyTheme(window); window.ShowInTaskbar = true; window.Closed += (_, _) => { @@ -285,13 +286,23 @@ internal sealed class SettingsWindowService : ISettingsWindowService }, DispatcherPriority.Background); } - private void ApplyTheme(SettingsWindow window) + private static void ApplyThemeVariantAndResources(SettingsWindow window, IAppearanceThemeService appearanceThemeService) { - var appearanceSnapshot = _appearanceThemeService.GetCurrent(); + var appearanceSnapshot = appearanceThemeService.GetCurrent(); window.RequestedThemeVariant = appearanceSnapshot.IsNightMode ? ThemeVariant.Dark : ThemeVariant.Light; - _appearanceThemeService.ApplyThemeResources(window.Resources); + appearanceThemeService.ApplyThemeResources(window.Resources); + } + + private void ApplyThemeVariantAndResources(SettingsWindow window) + { + ApplyThemeVariantAndResources(window, _appearanceThemeService); + } + + private void ApplyTheme(SettingsWindow window) + { + ApplyThemeVariantAndResources(window, _appearanceThemeService); _appearanceThemeService.ApplyWindowMaterial(window, MaterialSurfaceRole.SettingsWindowBackground); } diff --git a/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml index 8394886..c0a2455 100644 --- a/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml @@ -1,7 +1,6 @@  - - - - diff --git a/LanMountainDesktop/Views/SettingsPages/UpdateSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/UpdateSettingsPage.axaml index e2c540a..5af4903 100644 --- a/LanMountainDesktop/Views/SettingsPages/UpdateSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/UpdateSettingsPage.axaml @@ -10,7 +10,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - @@ -81,6 +164,7 @@ FontWeight="SemiBold" Margin="8,0,0,0" VerticalAlignment="Center" + Foreground="{DynamicResource TextFillColorPrimaryBrush}" IsHitTestVisible="False" Text="{Binding Title}" /> @@ -119,80 +203,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/LanMountainDesktop/Views/SettingsWindow.axaml.cs b/LanMountainDesktop/Views/SettingsWindow.axaml.cs index 83f1e66..845b0f6 100644 --- a/LanMountainDesktop/Views/SettingsWindow.axaml.cs +++ b/LanMountainDesktop/Views/SettingsWindow.axaml.cs @@ -87,7 +87,8 @@ public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext SyncPendingRestartState(); SyncTitleText(); UpdateChromeMetrics(); - UpdatePaneToggleIcon(); + UpdatePaneFooterToggleVisibility(); + UpdatePaneFooterToggleIcon(); UpdateResponsiveLayout(); RequestResponsiveLayoutRefresh(); } @@ -104,6 +105,7 @@ public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext CloseDrawer(); RebuildNavigationItems(); NavigateTo(pageId ?? ViewModel.Pages.FirstOrDefault()?.PageId); + UpdatePaneFooterToggleVisibility(); } public void RebuildAndNavigateToDevPage() @@ -266,6 +268,7 @@ public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext ViewModel.IsPageTitleVisible = !descriptor.HidePageTitle; TrySelectNavigationItem(descriptor.PageId); SyncTitleText(); + UpdatePaneFooterToggleVisibility(); UpdateResponsiveLayout(); RequestResponsiveLayoutRefresh(); if (!string.Equals(previousPageId, descriptor.PageId, StringComparison.OrdinalIgnoreCase)) @@ -523,7 +526,7 @@ public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext } } - private void OnTogglePaneButtonClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnPaneFooterToggleClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { _ = sender; _ = e; @@ -533,7 +536,7 @@ public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext } RootNavigationView.IsPaneOpen = !RootNavigationView.IsPaneOpen; - UpdatePaneToggleIcon(); + UpdatePaneFooterToggleIcon(); UpdateResponsiveLayout(); RequestResponsiveLayoutRefresh(); } @@ -544,13 +547,33 @@ public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext if (e.Property == FANavigationView.IsPaneOpenProperty || e.Property == FANavigationView.OpenPaneLengthProperty || - e.Property == FANavigationView.PaneDisplayModeProperty) + e.Property == FANavigationView.PaneDisplayModeProperty || + e.Property == FANavigationView.IsPaneToggleButtonVisibleProperty) { - UpdatePaneToggleIcon(); + if (e.Property == FANavigationView.IsPaneToggleButtonVisibleProperty) + { + UpdatePaneFooterToggleVisibility(); + } + + UpdatePaneFooterToggleIcon(); RequestResponsiveLayoutRefresh(); } } + /// + /// 仅在 :minimal 为 false)时显示侧栏底部备胎按钮。 + /// 根 DataContext 为 ViewModel 时,对 #RootNavigationView 的绑定易失效,故用代码同步可见性。 + /// + private void UpdatePaneFooterToggleVisibility() + { + if (PaneFooterToggleButton is null || RootNavigationView is null) + { + return; + } + + PaneFooterToggleButton.IsVisible = !RootNavigationView.IsPaneToggleButtonVisible; + } + private void RequestResponsiveLayoutRefresh() { if (_isResponsiveRefreshPending) @@ -580,14 +603,14 @@ public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext : compactPaneWidth; } - private void UpdatePaneToggleIcon() + private void UpdatePaneFooterToggleIcon() { - if (TogglePaneButtonIcon is null || RootNavigationView is null) + if (PaneFooterToggleButtonIcon is null || RootNavigationView is null) { return; } - TogglePaneButtonIcon.Icon = RootNavigationView.IsPaneOpen + PaneFooterToggleButtonIcon.Icon = RootNavigationView.IsPaneOpen ? FluentIcons.Common.Icon.LineHorizontal3 : FluentIcons.Common.Icon.Navigation; } @@ -604,8 +627,6 @@ public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext } if (WindowTitleBarHost is null || - TogglePaneButton is null || - TogglePaneButtonIcon is null || WindowBrandIcon is null || WindowTitleTextBlock is null || RestartNowButton is null || @@ -622,8 +643,6 @@ public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext var layoutScale = Math.Clamp(Math.Min(width / 1120d, height / 760d), 0.90, 1.18); const double titleBarHeight = 48d; - var titleBarButtonWidth = Math.Clamp(40d * layoutScale, 36d, 48d); - var titleBarButtonHeight = Math.Clamp(32d * layoutScale, 30d, 38d); var titleFontSize = Math.Clamp(12d * layoutScale, 11d, 14d); var titleBarIconSize = Math.Clamp(16d * layoutScale, 15d, 20d); var drawerTitleFontSize = Math.Clamp(16d * layoutScale, 14d, 20d); @@ -634,9 +653,6 @@ public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext WindowTitleBarHost.Height = titleBarHeight; - TogglePaneButton.Width = titleBarButtonWidth; - TogglePaneButton.Height = titleBarButtonHeight; - TogglePaneButtonIcon.FontSize = titleBarIconSize; WindowBrandIcon.FontSize = titleBarIconSize + 2; WindowTitleTextBlock.FontSize = titleFontSize; diff --git a/NuGet.Config b/NuGet.Config new file mode 100644 index 0000000..696b4d2 --- /dev/null +++ b/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + +