mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
Add Windows system chrome patchers (Harmony)
Introduce support for toggling the system chrome on Windows using Harmony patchers. Adds Lib.Harmony.Thin to package props and project, new patcher infrastructure (ChromePatchState, PatcherEntrance) and two Harmony patches that disable FluentAvalonia's Windows chrome when configured. Program.cs now loads the chrome setting and installs patchers conditionally on Windows/x86-x64. Settings viewmodel and view updated: expose IsWindowsOs, require restart on appearance changes, migrate SettingsWindow to FAAppWindow and adapt titlebar/layout (include Windows caption placeholder and footer menu items). Also add a .gitkeep and a build log file.
This commit is contained in:
1
.cursor/skills/.gitkeep
Normal file
1
.cursor/skills/.gitkeep
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
<PackageVersion Include="Downloader" Version="5.4.0" />
|
<PackageVersion Include="Downloader" Version="5.4.0" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="3.0.0-preview2" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="3.0.0-preview2" />
|
||||||
<PackageVersion Include="FluentIcons.Avalonia" Version="2.1.325" />
|
<PackageVersion Include="FluentIcons.Avalonia" Version="2.1.325" />
|
||||||
|
<PackageVersion Include="Lib.Harmony.Thin" Version="2.4.2" />
|
||||||
<PackageVersion Include="Material.Avalonia" Version="3.16.1" />
|
<PackageVersion Include="Material.Avalonia" Version="3.16.1" />
|
||||||
<PackageVersion Include="MaterialColorUtilities" Version="0.3.0" />
|
<PackageVersion Include="MaterialColorUtilities" Version="0.3.0" />
|
||||||
<PackageVersion Include="Material.Icons.Avalonia" Version="3.0.3-nightly.0.2" />
|
<PackageVersion Include="Material.Icons.Avalonia" Version="3.0.3-nightly.0.2" />
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
<PackageReference Include="Downloader" />
|
<PackageReference Include="Downloader" />
|
||||||
<PackageReference Include="FluentAvaloniaUI" />
|
<PackageReference Include="FluentAvaloniaUI" />
|
||||||
<PackageReference Include="FluentIcons.Avalonia" />
|
<PackageReference Include="FluentIcons.Avalonia" />
|
||||||
|
<PackageReference Include="Lib.Harmony.Thin" />
|
||||||
<PackageReference Include="Material.Avalonia" />
|
<PackageReference Include="Material.Avalonia" />
|
||||||
<PackageReference Include="Material.Icons.Avalonia" />
|
<PackageReference Include="Material.Icons.Avalonia" />
|
||||||
<PackageReference Include="ClassIsland.Markdown.Avalonia" />
|
<PackageReference Include="ClassIsland.Markdown.Avalonia" />
|
||||||
|
|||||||
6
LanMountainDesktop/Platform/Windows/ChromePatchState.cs
Normal file
6
LanMountainDesktop/Platform/Windows/ChromePatchState.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace LanMountainDesktop.Platform.Windows;
|
||||||
|
|
||||||
|
internal static class ChromePatchState
|
||||||
|
{
|
||||||
|
public static bool UseSystemChrome { get; set; }
|
||||||
|
}
|
||||||
12
LanMountainDesktop/Platform/Windows/PatcherEntrance.cs
Normal file
12
LanMountainDesktop/Platform/Windows/PatcherEntrance.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Platform.Windows;
|
||||||
|
|
||||||
|
internal static class PatcherEntrance
|
||||||
|
{
|
||||||
|
public static void InstallPatchers()
|
||||||
|
{
|
||||||
|
var harmony = new Harmony("dev.lanmountain.desktop.patchers");
|
||||||
|
harmony.PatchAll(typeof(PatcherEntrance).Assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using FluentAvalonia.UI.Windowing;
|
||||||
|
using LanMountainDesktop.Platform.Windows;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Platform.Windows.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(FAAppWindow), "InitializeAppWindow")]
|
||||||
|
internal class AppWindowInitializeAppWindowPatcher
|
||||||
|
{
|
||||||
|
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_PseudoClasses")]
|
||||||
|
private static extern IPseudoClasses GetPseudoClasses(StyledElement window);
|
||||||
|
|
||||||
|
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_IsWindows")]
|
||||||
|
private static extern void SetIsWindowsProperty(FAAppWindow window, bool v);
|
||||||
|
|
||||||
|
static void Postfix(FAAppWindow __instance)
|
||||||
|
{
|
||||||
|
if (!ChromePatchState.UseSystemChrome) return;
|
||||||
|
GetPseudoClasses(__instance).Remove(":windows");
|
||||||
|
SetIsWindowsProperty(__instance, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using FluentAvalonia.UI.Windowing;
|
||||||
|
using HarmonyLib;
|
||||||
|
using LanMountainDesktop.Platform.Windows;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Platform.Windows.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch]
|
||||||
|
internal class Win32WindowManagerConstructorPatcher
|
||||||
|
{
|
||||||
|
[HarmonyTargetMethod]
|
||||||
|
static System.Reflection.MethodBase TargetMethod()
|
||||||
|
{
|
||||||
|
var type = AccessTools.TypeByName("FluentAvalonia.UI.Windowing.Win32WindowManager");
|
||||||
|
return AccessTools.Constructor(type!, [typeof(FAAppWindow)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Prefix(FAAppWindow window)
|
||||||
|
{
|
||||||
|
return !ChromePatchState.UseSystemChrome;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -87,6 +87,8 @@ public sealed class Program
|
|||||||
|
|
||||||
AppLogger.Info("SingleInstance", "Activation acknowledged before Avalonia App was ready.");
|
AppLogger.Info("SingleInstance", "Activation acknowledged before Avalonia App was ready.");
|
||||||
});
|
});
|
||||||
|
LoadChromePatchState();
|
||||||
|
InstallChromePatchersIfNeeded();
|
||||||
BuildAvaloniaApp(renderMode).StartWithClassicDesktopLifetime(args);
|
BuildAvaloniaApp(renderMode).StartWithClassicDesktopLifetime(args);
|
||||||
AppLogger.Info("Startup", "Application exited normally.");
|
AppLogger.Info("Startup", "Application exited normally.");
|
||||||
}
|
}
|
||||||
@@ -198,6 +200,49 @@ public sealed class Program
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void LoadChromePatchState()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var snapshot = HostSettingsFacadeProvider.GetOrCreate()
|
||||||
|
.Settings
|
||||||
|
.LoadSnapshot<AppSettingsSnapshot>(LanMountainDesktop.PluginSdk.SettingsScope.App);
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
LanMountainDesktop.Platform.Windows.ChromePatchState.UseSystemChrome = snapshot.UseSystemChrome;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppLogger.Warn("Startup", "Failed to load chrome patch state. Falling back to FA chrome.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InstallChromePatchersIfNeeded()
|
||||||
|
{
|
||||||
|
if (!OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var arch = System.Runtime.InteropServices.RuntimeInformation.OSArchitecture;
|
||||||
|
if (arch != System.Runtime.InteropServices.Architecture.X64 &&
|
||||||
|
arch != System.Runtime.InteropServices.Architecture.X86)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LanMountainDesktop.Platform.Windows.PatcherEntrance.InstallPatchers();
|
||||||
|
AppLogger.Info("Startup", $"Chrome patchers installed. UseSystemChrome={LanMountainDesktop.Platform.Windows.ChromePatchState.UseSystemChrome}.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppLogger.Warn("Startup", "Failed to install chrome patchers.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void WaitForRestartParentExit(int processId, DateTime deadlineUtc)
|
private static void WaitForRestartParentExit(int processId, DateTime deadlineUtc)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -31,12 +31,14 @@ public sealed partial class SettingsWindowViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
_localizationService = new();
|
_localizationService = new();
|
||||||
_languageCode = "zh-CN";
|
_languageCode = "zh-CN";
|
||||||
|
IsWindowsOs = OperatingSystem.IsWindows();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SettingsWindowViewModel(LocalizationService localizationService, string languageCode)
|
public SettingsWindowViewModel(LocalizationService localizationService, string languageCode)
|
||||||
{
|
{
|
||||||
_localizationService = localizationService;
|
_localizationService = localizationService;
|
||||||
_languageCode = languageCode;
|
_languageCode = languageCode;
|
||||||
|
IsWindowsOs = OperatingSystem.IsWindows();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string L(string key) => _localizationService.GetString(_languageCode, key, key);
|
private string L(string key) => _localizationService.GetString(_languageCode, key, key);
|
||||||
@@ -86,6 +88,10 @@ public sealed partial class SettingsWindowViewModel : ViewModelBase
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _isDrawerOpen;
|
private bool _isDrawerOpen;
|
||||||
|
|
||||||
|
/// <summary>用于标题栏右侧系统按钮占位(与 SecRandom / ClassIsland 一致,仅 Windows 显示)。</summary>
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isWindowsOs;
|
||||||
|
|
||||||
public SettingsWindowViewModel Initialize()
|
public SettingsWindowViewModel Initialize()
|
||||||
{
|
{
|
||||||
RefreshLanguage(_languageCode);
|
RefreshLanguage(_languageCode);
|
||||||
@@ -855,7 +861,7 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PersistCurrentState(restartRequired: false);
|
PersistCurrentState(restartRequired: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnSelectedCornerRadiusStyleChanged(SelectionOption? value)
|
partial void OnSelectedCornerRadiusStyleChanged(SelectionOption? value)
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
<Window xmlns="https://github.com/avaloniaui"
|
<faWindowing:FAAppWindow xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
||||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
xmlns:fi="using:FluentIcons.Avalonia"
|
xmlns:fi="using:FluentIcons.Avalonia"
|
||||||
x:Class="LanMountainDesktop.Views.SettingsWindow"
|
xmlns:faWindowing="using:FluentAvalonia.UI.Windowing"
|
||||||
x:DataType="vm:SettingsWindowViewModel"
|
x:Class="LanMountainDesktop.Views.SettingsWindow"
|
||||||
Width="1120"
|
x:DataType="vm:SettingsWindowViewModel"
|
||||||
Height="760"
|
Width="1120"
|
||||||
MinWidth="560"
|
Height="760"
|
||||||
MinHeight="480"
|
MinWidth="560"
|
||||||
CanResize="True"
|
MinHeight="480"
|
||||||
WindowStartupLocation="Manual"
|
CanResize="True"
|
||||||
WindowDecorations="BorderOnly"
|
WindowStartupLocation="Manual"
|
||||||
FontFamily="{DynamicResource AppFontFamily}"
|
FontFamily="{DynamicResource AppFontFamily}"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
Title="{Binding Title}">
|
Title="{Binding Title}">
|
||||||
|
|
||||||
<Window.Resources>
|
<faWindowing:FAAppWindow.Resources>
|
||||||
<x:Double x:Key="SettingsContainerMaxWidth">960</x:Double>
|
<x:Double x:Key="SettingsContainerMaxWidth">960</x:Double>
|
||||||
</Window.Resources>
|
</faWindowing:FAAppWindow.Resources>
|
||||||
|
|
||||||
<Window.Styles>
|
<faWindowing:FAAppWindow.Styles>
|
||||||
<Style Selector="Grid.page-title-container">
|
<Style Selector="Grid.page-title-container">
|
||||||
<Setter Property="Margin" Value="0,16,0,0" />
|
<Setter Property="Margin" Value="0,16,0,0" />
|
||||||
<Setter Property="MaxWidth" Value="{DynamicResource SettingsContainerMaxWidth}" />
|
<Setter Property="MaxWidth" Value="{DynamicResource SettingsContainerMaxWidth}" />
|
||||||
@@ -34,94 +34,107 @@
|
|||||||
<Style Selector="TextBlock.page-title-text:narrow">
|
<Style Selector="TextBlock.page-title-text:narrow">
|
||||||
<Setter Property="FontSize" Value="24" />
|
<Setter Property="FontSize" Value="24" />
|
||||||
</Style>
|
</Style>
|
||||||
</Window.Styles>
|
</faWindowing:FAAppWindow.Styles>
|
||||||
|
|
||||||
<Grid x:Name="RootGrid"
|
<Grid x:Name="RootGrid"
|
||||||
Classes="settings-scope"
|
Classes="settings-scope"
|
||||||
Background="{DynamicResource AdaptiveSettingsWindowBackgroundBrush}"
|
Background="{DynamicResource AdaptiveSettingsWindowBackgroundBrush}"
|
||||||
RowDefinitions="Auto,*">
|
RowDefinitions="Auto,*">
|
||||||
|
<!-- 顶栏布局对齐 SecRandom:左(折叠/品牌/标题)+ 中(透明拖窗区)+ 右(重启 / Windows caption 占位) -->
|
||||||
<Border x:Name="WindowTitleBarHost"
|
<Border x:Name="WindowTitleBarHost"
|
||||||
Height="48"
|
Height="48"
|
||||||
Padding="12,0,12,0"
|
Background="Transparent"
|
||||||
Background="{DynamicResource AdaptiveSettingsWindowBackgroundBrush}"
|
|
||||||
BorderBrush="{DynamicResource AdaptiveSettingsWindowBorderBrush}"
|
BorderBrush="{DynamicResource AdaptiveSettingsWindowBorderBrush}"
|
||||||
BorderThickness="0,0,0,1"
|
BorderThickness="0,0,0,1">
|
||||||
PointerPressed="OnWindowTitleBarPointerPressed">
|
<Grid ColumnDefinitions="Auto,*,Auto"
|
||||||
<Grid ColumnDefinitions="Auto,Auto,*,Auto,Auto"
|
VerticalAlignment="Stretch">
|
||||||
ColumnSpacing="8"
|
<StackPanel Grid.Column="0"
|
||||||
VerticalAlignment="Center">
|
|
||||||
<Button x:Name="TogglePaneButton"
|
|
||||||
Classes="pane-toggle-button"
|
|
||||||
Click="OnTogglePaneButtonClick">
|
|
||||||
<Grid>
|
|
||||||
<fi:FluentIcon x:Name="TogglePaneButtonIcon"
|
|
||||||
Icon="Navigation"
|
|
||||||
IconVariant="Regular"
|
|
||||||
FontSize="16" />
|
|
||||||
</Grid>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<fi:FluentIcon x:Name="WindowBrandIcon"
|
|
||||||
Grid.Column="1"
|
|
||||||
Margin="6,0,2,0"
|
|
||||||
Icon="Settings"
|
|
||||||
IconVariant="Filled"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
VerticalAlignment="Center" />
|
|
||||||
|
|
||||||
<StackPanel Grid.Column="2"
|
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
|
Margin="8,0,8,0"
|
||||||
|
Spacing="8"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<Button x:Name="TogglePaneButton"
|
||||||
|
Classes="pane-toggle-button"
|
||||||
|
Margin="-7,-8,-8,-8"
|
||||||
|
MinWidth="40"
|
||||||
|
Width="48"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Spacing="10">
|
IsVisible="{Binding !#RootNavigationView.IsPaneToggleButtonVisible}"
|
||||||
|
Click="OnTogglePaneButtonClick">
|
||||||
|
<Grid>
|
||||||
|
<fi:FluentIcon x:Name="TogglePaneButtonIcon"
|
||||||
|
Icon="Navigation"
|
||||||
|
IconVariant="Regular"
|
||||||
|
FontSize="16" />
|
||||||
|
</Grid>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<fi:FluentIcon x:Name="WindowBrandIcon"
|
||||||
|
Margin="0,0,0,0"
|
||||||
|
Icon="Settings"
|
||||||
|
IconVariant="Filled"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<TextBlock x:Name="WindowTitleTextBlock"
|
<TextBlock x:Name="WindowTitleTextBlock"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
FontWeight="SemiBold"
|
FontWeight="SemiBold"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
IsHitTestVisible="False"
|
IsHitTestVisible="False"
|
||||||
Text="{Binding Title}" />
|
Text="{Binding Title}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Button x:Name="RestartNowButton"
|
<Border x:Name="TitleBarDragZone"
|
||||||
Grid.Column="3"
|
Grid.Column="1"
|
||||||
Padding="10,6"
|
|
||||||
Margin="0,0,4,0"
|
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
IsVisible="{Binding IsRestartRequested}"
|
PointerPressed="OnTitleBarDragZonePointerPressed" />
|
||||||
Click="OnRestartNowClick">
|
|
||||||
<StackPanel Orientation="Horizontal"
|
|
||||||
Spacing="6">
|
|
||||||
<fi:FluentIcon x:Name="RestartButtonIcon"
|
|
||||||
Icon="ArrowSync"
|
|
||||||
IconVariant="Regular" />
|
|
||||||
<TextBlock x:Name="RestartButtonTextBlock"
|
|
||||||
Text="{Binding RestartButtonText}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button x:Name="CloseWindowButton"
|
<StackPanel Grid.Column="2"
|
||||||
Grid.Column="4"
|
Orientation="Horizontal"
|
||||||
Width="40"
|
Spacing="8"
|
||||||
Height="32"
|
VerticalAlignment="Center"
|
||||||
Padding="0"
|
Margin="0,0,8,0">
|
||||||
Background="Transparent"
|
<Button x:Name="RestartNowButton"
|
||||||
BorderThickness="0"
|
Padding="10,6"
|
||||||
Click="OnCloseWindowClick">
|
Margin="0,-8,0,-8"
|
||||||
<fi:FluentIcon x:Name="CloseWindowButtonIcon"
|
VerticalAlignment="Center"
|
||||||
Icon="Dismiss"
|
Background="Transparent"
|
||||||
IconVariant="Regular" />
|
IsVisible="{Binding IsRestartRequested}"
|
||||||
</Button>
|
Click="OnRestartNowClick">
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
Spacing="6">
|
||||||
|
<fi:FluentIcon x:Name="RestartButtonIcon"
|
||||||
|
Icon="ArrowSync"
|
||||||
|
IconVariant="Regular" />
|
||||||
|
<TextBlock x:Name="RestartButtonTextBlock"
|
||||||
|
Text="{Binding RestartButtonText}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Border Width="140"
|
||||||
|
Background="Transparent"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
IsVisible="{Binding IsWindowsOs}" />
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<ui:FANavigationView x:Name="RootNavigationView"
|
<ui:FANavigationView x:Name="RootNavigationView"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Margin="0,8,0,0"
|
Margin="0"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
PaneDisplayMode="Auto"
|
PaneDisplayMode="Auto"
|
||||||
OpenPaneLength="283"
|
OpenPaneLength="283"
|
||||||
IsSettingsVisible="False"
|
IsSettingsVisible="False"
|
||||||
IsPaneToggleButtonVisible="False"
|
|
||||||
IsBackButtonVisible="False"
|
IsBackButtonVisible="False"
|
||||||
SelectionChanged="OnNavigationSelectionChanged">
|
SelectionChanged="OnNavigationSelectionChanged">
|
||||||
|
<ui:FANavigationView.Styles>
|
||||||
|
<Style Selector="ui|FANavigationView#RootNavigationView:minimal">
|
||||||
|
<Setter Property="IsPaneToggleButtonVisible" Value="False"/>
|
||||||
|
</Style>
|
||||||
|
</ui:FANavigationView.Styles>
|
||||||
|
|
||||||
<ui:FANavigationView.Resources>
|
<ui:FANavigationView.Resources>
|
||||||
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" />
|
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" />
|
||||||
<SolidColorBrush x:Key="NavigationViewContentGridBorderBrush" Color="Transparent" />
|
<SolidColorBrush x:Key="NavigationViewContentGridBorderBrush" Color="Transparent" />
|
||||||
@@ -182,4 +195,4 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</ui:FANavigationView>
|
</ui:FANavigationView>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</faWindowing:FAAppWindow>
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ using System.Threading.Tasks;
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Media;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using FluentAvalonia.UI.Windowing;
|
||||||
using LanMountainDesktop.PluginSdk;
|
using LanMountainDesktop.PluginSdk;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
using LanMountainDesktop.Services.Settings;
|
using LanMountainDesktop.Services.Settings;
|
||||||
@@ -16,7 +17,7 @@ using Symbol = FluentIcons.Common.Symbol;
|
|||||||
|
|
||||||
namespace LanMountainDesktop.Views;
|
namespace LanMountainDesktop.Views;
|
||||||
|
|
||||||
public partial class SettingsWindow : Window, ISettingsPageHostContext
|
public partial class SettingsWindow : FAAppWindow, ISettingsPageHostContext
|
||||||
{
|
{
|
||||||
private const double BaseSettingsContainerWidth = 960d;
|
private const double BaseSettingsContainerWidth = 960d;
|
||||||
private const double MinSettingsContentWidth = 320d;
|
private const double MinSettingsContentWidth = 320d;
|
||||||
@@ -56,7 +57,7 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
_hostApplicationLifecycle = hostApplicationLifecycle;
|
_hostApplicationLifecycle = hostApplicationLifecycle;
|
||||||
DataContext = ViewModel;
|
DataContext = ViewModel;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Icon = _appLogoService.CreateWindowIcon();
|
SetValue(Window.IconProperty, _appLogoService.CreateWindowIcon());
|
||||||
ApplyChromeMode(useSystemChrome);
|
ApplyChromeMode(useSystemChrome);
|
||||||
|
|
||||||
if (RootNavigationView is not null)
|
if (RootNavigationView is not null)
|
||||||
@@ -75,6 +76,14 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
|
|
||||||
private void OnLoaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
private void OnLoaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
TitleBar.Height = 48;
|
||||||
|
TitleBar.ExtendsContentIntoTitleBar = true;
|
||||||
|
|
||||||
|
// SecRandom MainWindow:标题栏按钮悬停/按下/非活动色,与系统 caption 更一致
|
||||||
|
TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(23, 0, 0, 0);
|
||||||
|
TitleBar.ButtonPressedBackgroundColor = Color.FromArgb(52, 0, 0, 0);
|
||||||
|
TitleBar.ButtonInactiveForegroundColor = Colors.Gray;
|
||||||
|
|
||||||
SyncPendingRestartState();
|
SyncPendingRestartState();
|
||||||
SyncTitleText();
|
SyncTitleText();
|
||||||
UpdateChromeMetrics();
|
UpdateChromeMetrics();
|
||||||
@@ -160,11 +169,11 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
{
|
{
|
||||||
_useSystemChrome = useSystemChrome || OperatingSystem.IsMacOS();
|
_useSystemChrome = useSystemChrome || OperatingSystem.IsMacOS();
|
||||||
|
|
||||||
|
ExtendClientAreaToDecorationsHint = true;
|
||||||
|
WindowDecorations = WindowDecorations.Full;
|
||||||
|
|
||||||
if (_useSystemChrome)
|
if (_useSystemChrome)
|
||||||
{
|
{
|
||||||
ExtendClientAreaToDecorationsHint = true;
|
|
||||||
WindowDecorations = WindowDecorations.Full;
|
|
||||||
|
|
||||||
if (WindowTitleBarHost is { })
|
if (WindowTitleBarHost is { })
|
||||||
{
|
{
|
||||||
WindowTitleBarHost.IsVisible = false;
|
WindowTitleBarHost.IsVisible = false;
|
||||||
@@ -172,9 +181,6 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowDecorations = WindowDecorations.BorderOnly;
|
|
||||||
ExtendClientAreaToDecorationsHint = true;
|
|
||||||
|
|
||||||
if (WindowTitleBarHost is { })
|
if (WindowTitleBarHost is { })
|
||||||
{
|
{
|
||||||
WindowTitleBarHost.IsVisible = true;
|
WindowTitleBarHost.IsVisible = true;
|
||||||
@@ -195,21 +201,32 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
RootNavigationView.MenuItems.Clear();
|
RootNavigationView.MenuItems.Clear();
|
||||||
|
RootNavigationView.FooterMenuItems.Clear();
|
||||||
|
|
||||||
SettingsPageCategory? previousCategory = null;
|
SettingsPageCategory? previousCategory = null;
|
||||||
|
|
||||||
foreach (var page in ViewModel.Pages)
|
foreach (var page in ViewModel.Pages)
|
||||||
{
|
{
|
||||||
|
var item = new FANavigationViewItem
|
||||||
|
{
|
||||||
|
Content = page.Title,
|
||||||
|
Tag = page.PageId,
|
||||||
|
IconSource = CreateSettingsIconSource(MapIcon(page.IconKey))
|
||||||
|
};
|
||||||
|
|
||||||
|
if (page.Category == SettingsPageCategory.About ||
|
||||||
|
page.Category == SettingsPageCategory.Dev)
|
||||||
|
{
|
||||||
|
RootNavigationView.FooterMenuItems.Add(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (previousCategory is not null && previousCategory != page.Category)
|
if (previousCategory is not null && previousCategory != page.Category)
|
||||||
{
|
{
|
||||||
RootNavigationView.MenuItems.Add(new FANavigationViewItemSeparator());
|
RootNavigationView.MenuItems.Add(new FANavigationViewItemSeparator());
|
||||||
}
|
}
|
||||||
|
|
||||||
RootNavigationView.MenuItems.Add(new FANavigationViewItem
|
RootNavigationView.MenuItems.Add(item);
|
||||||
{
|
|
||||||
Content = page.Title,
|
|
||||||
Tag = page.PageId,
|
|
||||||
IconSource = CreateSettingsIconSource(MapIcon(page.IconKey))
|
|
||||||
});
|
|
||||||
|
|
||||||
previousCategory = page.Category;
|
previousCategory = page.Category;
|
||||||
}
|
}
|
||||||
@@ -293,7 +310,10 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var item in RootNavigationView.MenuItems.OfType<FANavigationViewItem>())
|
var allItems = RootNavigationView.MenuItems.OfType<FANavigationViewItem>()
|
||||||
|
.Concat(RootNavigationView.FooterMenuItems.OfType<FANavigationViewItem>());
|
||||||
|
|
||||||
|
foreach (var item in allItems)
|
||||||
{
|
{
|
||||||
if (string.Equals(item.Tag as string, pageId, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(item.Tag as string, pageId, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@@ -494,7 +514,7 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
TelemetryServices.Usage?.TrackSettingsWindowClosed("SettingsWindow.OnClosed", ViewModel.CurrentPageId);
|
TelemetryServices.Usage?.TrackSettingsWindowClosed("SettingsWindow.OnClosed", ViewModel.CurrentPageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnWindowTitleBarPointerPressed(object? sender, PointerPressedEventArgs e)
|
private void OnTitleBarDragZonePointerPressed(object? sender, PointerPressedEventArgs e)
|
||||||
{
|
{
|
||||||
_ = sender;
|
_ = sender;
|
||||||
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||||
@@ -518,13 +538,6 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
RequestResponsiveLayoutRefresh();
|
RequestResponsiveLayoutRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCloseWindowClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
_ = sender;
|
|
||||||
_ = e;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnRootNavigationViewPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
private void OnRootNavigationViewPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
_ = sender;
|
_ = sender;
|
||||||
@@ -573,6 +586,10 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TogglePaneButtonIcon.Icon = RootNavigationView.IsPaneOpen
|
||||||
|
? FluentIcons.Common.Icon.LineHorizontal3
|
||||||
|
: FluentIcons.Common.Icon.Navigation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateChromeMetrics()
|
private void UpdateChromeMetrics()
|
||||||
@@ -594,8 +611,6 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
RestartNowButton is null ||
|
RestartNowButton is null ||
|
||||||
RestartButtonIcon is null ||
|
RestartButtonIcon is null ||
|
||||||
RestartButtonTextBlock is null ||
|
RestartButtonTextBlock is null ||
|
||||||
CloseWindowButton is null ||
|
|
||||||
CloseWindowButtonIcon is null ||
|
|
||||||
DrawerTitleTextBlock is null ||
|
DrawerTitleTextBlock is null ||
|
||||||
RootNavigationView is null)
|
RootNavigationView is null)
|
||||||
{
|
{
|
||||||
@@ -606,7 +621,7 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
var height = Bounds.Height > 1 ? Bounds.Height : Math.Max(Height, MinHeight);
|
var height = Bounds.Height > 1 ? Bounds.Height : Math.Max(Height, MinHeight);
|
||||||
var layoutScale = Math.Clamp(Math.Min(width / 1120d, height / 760d), 0.90, 1.18);
|
var layoutScale = Math.Clamp(Math.Min(width / 1120d, height / 760d), 0.90, 1.18);
|
||||||
|
|
||||||
var titleBarHeight = Math.Clamp(48d * layoutScale, 44d, 58d);
|
const double titleBarHeight = 48d;
|
||||||
var titleBarButtonWidth = Math.Clamp(40d * layoutScale, 36d, 48d);
|
var titleBarButtonWidth = Math.Clamp(40d * layoutScale, 36d, 48d);
|
||||||
var titleBarButtonHeight = Math.Clamp(32d * layoutScale, 30d, 38d);
|
var titleBarButtonHeight = Math.Clamp(32d * layoutScale, 30d, 38d);
|
||||||
var titleFontSize = Math.Clamp(12d * layoutScale, 11d, 14d);
|
var titleFontSize = Math.Clamp(12d * layoutScale, 11d, 14d);
|
||||||
@@ -618,7 +633,6 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
ExtendClientAreaTitleBarHeightHint = titleBarHeight;
|
ExtendClientAreaTitleBarHeightHint = titleBarHeight;
|
||||||
|
|
||||||
WindowTitleBarHost.Height = titleBarHeight;
|
WindowTitleBarHost.Height = titleBarHeight;
|
||||||
WindowTitleBarHost.Padding = new Thickness(chromePadding, 0, chromePadding, 0);
|
|
||||||
|
|
||||||
TogglePaneButton.Width = titleBarButtonWidth;
|
TogglePaneButton.Width = titleBarButtonWidth;
|
||||||
TogglePaneButton.Height = titleBarButtonHeight;
|
TogglePaneButton.Height = titleBarButtonHeight;
|
||||||
@@ -636,10 +650,6 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
RestartButtonIcon.FontSize = titleBarIconSize;
|
RestartButtonIcon.FontSize = titleBarIconSize;
|
||||||
RestartButtonTextBlock.FontSize = titleFontSize;
|
RestartButtonTextBlock.FontSize = titleFontSize;
|
||||||
|
|
||||||
CloseWindowButton.Width = titleBarButtonWidth;
|
|
||||||
CloseWindowButton.Height = titleBarButtonHeight;
|
|
||||||
CloseWindowButtonIcon.FontSize = titleBarIconSize;
|
|
||||||
|
|
||||||
DrawerTitleTextBlock.FontSize = drawerTitleFontSize;
|
DrawerTitleTextBlock.FontSize = drawerTitleFontSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
_b.txt
Normal file
13
_b.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
正在确定要还原的项目…
|
||||||
|
已还原 D:\github\LanMountainDesktop\LanMountainDesktop.PluginIsolation.Contracts\LanMountainDesktop.PluginIsolation.Contracts.csproj (用时 265 毫秒)。
|
||||||
|
已还原 D:\github\LanMountainDesktop\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj (用时 597 毫秒)。
|
||||||
|
已还原 D:\github\LanMountainDesktop\LanMountainDesktop.Shared.IPC\LanMountainDesktop.Shared.IPC.csproj (用时 264 毫秒)。
|
||||||
|
C:\Program Files\dotnet\sdk\10.0.201\NuGet.targets(196,5): error : 磁盘空间不足。 [D:\github\LanMountainDesktop\LanMountainDesktop\LanMountainDesktop.csproj]
|
||||||
|
|
||||||
|
生成失败。
|
||||||
|
|
||||||
|
C:\Program Files\dotnet\sdk\10.0.201\NuGet.targets(196,5): error : 磁盘空间不足。 [D:\github\LanMountainDesktop\LanMountainDesktop\LanMountainDesktop.csproj]
|
||||||
|
0 个警告
|
||||||
|
1 个错误
|
||||||
|
|
||||||
|
已用时间 00:00:07.94
|
||||||
Reference in New Issue
Block a user