现已支持更改关键设置时提醒重启功能
This commit is contained in:
lincube
2026-03-09 22:26:42 +08:00
parent e97db00999
commit ec7b78bc63
17 changed files with 564 additions and 100 deletions

View File

@@ -96,15 +96,7 @@ public partial class App : Application
private void OnTrayRestartClick(object? sender, EventArgs e) private void OnTrayRestartClick(object? sender, EventArgs e)
{ {
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) AppRestartService.TryRestartApplication();
{
return;
}
if (AppRestartService.TryRestartCurrentProcess())
{
desktop.Shutdown();
}
} }
private void DisableAvaloniaDataAnnotationValidation() private void DisableAvaloniaDataAnnotationValidation()

View File

@@ -249,6 +249,13 @@
"settings.about.render_mode.current_format": "Current backend: {0}", "settings.about.render_mode.current_format": "Current backend: {0}",
"settings.about.render_mode.impl_format": "Runtime implementation: {0}", "settings.about.render_mode.impl_format": "Runtime implementation: {0}",
"settings.about.render_mode.impl_unavailable": "Runtime implementation details are unavailable.", "settings.about.render_mode.impl_unavailable": "Runtime implementation details are unavailable.",
"settings.restart_dialog.title": "Restart required",
"settings.restart_dialog.render_mode_message": "Restart the app to switch the rendering mode from \"{0}\" to \"{1}\". Restart now?",
"settings.restart_dialog.restart": "Restart now",
"settings.restart_dialog.cancel": "Cancel",
"settings.restart_dock.title": "Restart required",
"settings.restart_dock.description": "Some changes will take effect after restarting the app.",
"settings.restart_dock.button": "Restart app",
"settings.footer": "LanMountainDesktop Settings", "settings.footer": "LanMountainDesktop Settings",
"filepicker.title": "Select wallpaper", "filepicker.title": "Select wallpaper",
"filepicker.image_files": "Image files", "filepicker.image_files": "Image files",

View File

@@ -249,6 +249,13 @@
"settings.about.render_mode.current_format": "当前后端:{0}", "settings.about.render_mode.current_format": "当前后端:{0}",
"settings.about.render_mode.impl_format": "运行时实现:{0}", "settings.about.render_mode.impl_format": "运行时实现:{0}",
"settings.about.render_mode.impl_unavailable": "当前无法获取运行时实现信息。", "settings.about.render_mode.impl_unavailable": "当前无法获取运行时实现信息。",
"settings.restart_dialog.title": "需要重启应用",
"settings.restart_dialog.render_mode_message": "需要重启应用,才能将渲染模式从“{0}”切换到“{1}”。是否现在重启?",
"settings.restart_dialog.restart": "立即重启",
"settings.restart_dialog.cancel": "取消",
"settings.restart_dock.title": "需要重启应用",
"settings.restart_dock.description": "部分更改需要在重启应用后才会生效。",
"settings.restart_dock.button": "重启应用",
"settings.footer": "LanMountainDesktop 设置", "settings.footer": "LanMountainDesktop 设置",
"filepicker.title": "选择壁纸", "filepicker.title": "选择壁纸",
"filepicker.image_files": "图片文件", "filepicker.image_files": "图片文件",

View File

@@ -3,11 +3,28 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
namespace LanMountainDesktop.Services; namespace LanMountainDesktop.Services;
public static class AppRestartService public static class AppRestartService
{ {
public static bool TryRestartApplication()
{
if (!TryRestartCurrentProcess())
{
return false;
}
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.Shutdown();
}
return true;
}
public static bool TryRestartCurrentProcess() public static bool TryRestartCurrentProcess()
{ {
try try

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
namespace LanMountainDesktop.Services;
public static class PendingRestartStateService
{
public const string RenderModeReason = "RenderMode";
private static readonly object Gate = new();
private static readonly HashSet<string> PendingReasons = new(StringComparer.OrdinalIgnoreCase);
public static event Action? StateChanged;
public static bool HasPendingRestart
{
get
{
lock (Gate)
{
return PendingReasons.Count > 0;
}
}
}
public static bool HasPendingReason(string reason)
{
lock (Gate)
{
return PendingReasons.Contains(reason);
}
}
public static void SetPending(string reason, bool pending)
{
if (string.IsNullOrWhiteSpace(reason))
{
return;
}
var changed = false;
lock (Gate)
{
changed = pending
? PendingReasons.Add(reason)
: PendingReasons.Remove(reason);
}
if (changed)
{
StateChanged?.Invoke();
}
}
}

View File

@@ -327,6 +327,7 @@ public partial class MainWindow
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Wgl, L("settings.about.render_mode.wgl", "WGL")); SetAppRenderModeComboItemContent(AppRenderingModeHelper.Wgl, L("settings.about.render_mode.wgl", "WGL"));
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Vulkan, L("settings.about.render_mode.vulkan", "Vulkan")); SetAppRenderModeComboItemContent(AppRenderingModeHelper.Vulkan, L("settings.about.render_mode.vulkan", "Vulkan"));
UpdateCurrentRenderBackendStatus(); UpdateCurrentRenderBackendStatus();
UpdatePendingRestartDock();
if (WallpaperPlacementComboBox?.ItemCount >= 5) if (WallpaperPlacementComboBox?.ItemCount >= 5)
{ {

View File

@@ -0,0 +1,112 @@
using System.Threading.Tasks;
using Avalonia.Interactivity;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using LanMountainDesktop.Services;
namespace LanMountainDesktop.Views;
public partial class MainWindow
{
private bool _isRestartPromptVisible;
private void OnPendingRestartStateChanged()
{
if (Dispatcher.UIThread.CheckAccess())
{
UpdatePendingRestartDock();
return;
}
Dispatcher.UIThread.Post(UpdatePendingRestartDock);
}
private void UpdatePendingRestartDock()
{
PendingRestartDock.IsVisible = PendingRestartStateService.HasPendingRestart;
PendingRestartDockTitleTextBlock.Text = L("settings.restart_dock.title", "Restart required");
PendingRestartDockDescriptionTextBlock.Text = L(
"settings.restart_dock.description",
"Some changes will take effect after restarting the app.");
PendingRestartDockButtonTextBlock.Text = L("settings.restart_dock.button", "Restart app");
}
private async void OnPendingRestartDockButtonClick(object? sender, RoutedEventArgs e)
{
await ShowGenericRestartPromptAsync();
}
private Task ShowRenderModeRestartPromptAsync(string selectedMode)
{
var message = Lf(
"settings.restart_dialog.render_mode_message",
"Restart the app to switch the rendering mode from \"{0}\" to \"{1}\". Restart now?",
GetLocalizedAppRenderModeDisplayName(_runningAppRenderMode),
GetLocalizedAppRenderModeDisplayName(selectedMode));
return ShowRestartPromptCoreAsync(message);
}
private Task ShowGenericRestartPromptAsync()
{
return ShowRestartPromptCoreAsync(L(
"settings.restart_dock.description",
"Some changes will take effect after restarting the app."));
}
private async Task ShowRestartPromptCoreAsync(string message)
{
if (_isRestartPromptVisible)
{
return;
}
_isRestartPromptVisible = true;
try
{
var dialog = new ContentDialog
{
Title = L("settings.restart_dialog.title", "Restart required"),
Content = message,
PrimaryButtonText = L("settings.restart_dialog.restart", "Restart now"),
CloseButtonText = L("settings.restart_dialog.cancel", "Cancel"),
DefaultButton = ContentDialogButton.Primary
};
var result = await dialog.ShowAsync(this);
if (result == ContentDialogResult.Primary)
{
if (!AppRestartService.TryRestartApplication())
{
UpdatePendingRestartDock();
}
return;
}
UpdatePendingRestartDock();
}
finally
{
_isRestartPromptVisible = false;
}
}
private string GetLocalizedAppRenderModeDisplayName(string renderMode)
{
if (renderMode == AppRenderBackendDiagnostics.Unknown)
{
return L("settings.about.render_mode.unknown", "Unknown");
}
return AppRenderingModeHelper.Normalize(renderMode) switch
{
AppRenderingModeHelper.Software => L("settings.about.render_mode.software", "Software"),
AppRenderingModeHelper.AngleEgl => L("settings.about.render_mode.angle_egl", "angleEgl"),
AppRenderingModeHelper.Wgl => L("settings.about.render_mode.wgl", "WGL"),
AppRenderingModeHelper.Vulkan => L("settings.about.render_mode.vulkan", "Vulkan"),
_ => L("settings.about.render_mode.default", "Default")
};
}
}

View File

@@ -1225,6 +1225,10 @@ public partial class MainWindow
private void InitializeAppRenderModeSetting(AppSettingsSnapshot snapshot) private void InitializeAppRenderModeSetting(AppSettingsSnapshot snapshot)
{ {
_selectedAppRenderMode = AppRenderingModeHelper.Normalize(snapshot.AppRenderMode); _selectedAppRenderMode = AppRenderingModeHelper.Normalize(snapshot.AppRenderMode);
_runningAppRenderMode = ResolveActiveAppRenderModeForUi(_selectedAppRenderMode);
var renderModeForUi = PendingRestartStateService.HasPendingReason(PendingRestartStateService.RenderModeReason)
? _selectedAppRenderMode
: _runningAppRenderMode;
if (AppRenderModeComboBox is null) if (AppRenderModeComboBox is null)
{ {
@@ -1235,7 +1239,7 @@ public partial class MainWindow
try try
{ {
AppRenderModeComboBox.IsEnabled = OperatingSystem.IsWindows(); AppRenderModeComboBox.IsEnabled = OperatingSystem.IsWindows();
SelectAppRenderModeInUi(_selectedAppRenderMode); SelectAppRenderModeInUi(renderModeForUi);
} }
finally finally
{ {
@@ -1250,13 +1254,27 @@ public partial class MainWindow
return; return;
} }
var selectedItem = AppRenderModeComboBox.Items AppRenderModeComboBox.SelectedIndex = GetAppRenderModeComboBoxIndex(renderMode);
.OfType<ComboBoxItem>() }
.FirstOrDefault(item =>
string.Equals(item.Tag?.ToString(), renderMode, StringComparison.OrdinalIgnoreCase));
AppRenderModeComboBox.SelectedItem = selectedItem private static int GetAppRenderModeComboBoxIndex(string renderMode)
?? AppRenderModeComboBox.Items.OfType<ComboBoxItem>().FirstOrDefault(); {
return AppRenderingModeHelper.Normalize(renderMode) switch
{
AppRenderingModeHelper.Software => 1,
AppRenderingModeHelper.AngleEgl => 2,
AppRenderingModeHelper.Wgl => 3,
AppRenderingModeHelper.Vulkan => 4,
_ => 0
};
}
private static string ResolveActiveAppRenderModeForUi(string configuredRenderMode)
{
var detectedRenderMode = AppRenderBackendDiagnostics.Detect().ActualBackend;
return string.Equals(detectedRenderMode, AppRenderBackendDiagnostics.Unknown, StringComparison.Ordinal)
? configuredRenderMode
: AppRenderingModeHelper.Normalize(detectedRenderMode);
} }
private static WeatherLocationMode ParseWeatherLocationMode(string? value) private static WeatherLocationMode ParseWeatherLocationMode(string? value)
@@ -1534,7 +1552,7 @@ public partial class MainWindow
} }
var selectedMode = AppRenderingModeHelper.Normalize( var selectedMode = AppRenderingModeHelper.Normalize(
(AppRenderModeComboBox.SelectedItem as ComboBoxItem)?.Tag?.ToString()); TryGetSelectedComboBoxTag(AppRenderModeComboBox));
if (string.Equals(_selectedAppRenderMode, selectedMode, StringComparison.Ordinal)) if (string.Equals(_selectedAppRenderMode, selectedMode, StringComparison.Ordinal))
{ {
@@ -1543,6 +1561,14 @@ public partial class MainWindow
_selectedAppRenderMode = selectedMode; _selectedAppRenderMode = selectedMode;
PersistSettings(); PersistSettings();
var requiresRestart = !string.Equals(_runningAppRenderMode, selectedMode, StringComparison.Ordinal);
PendingRestartStateService.SetPending(PendingRestartStateService.RenderModeReason, requiresRestart);
UpdatePendingRestartDock();
if (requiresRestart)
{
_ = ShowRenderModeRestartPromptAsync(selectedMode);
}
} }
private async void OnSearchWeatherCityClick(object? sender, RoutedEventArgs e) private async void OnSearchWeatherCityClick(object? sender, RoutedEventArgs e)

View File

@@ -377,7 +377,10 @@
<Border Classes="mica-strong" <Border Classes="mica-strong"
CornerRadius="{DynamicResource DesignCornerRadiusXl}" CornerRadius="{DynamicResource DesignCornerRadiusXl}"
Padding="18"> Padding="18">
<Grid RowDefinitions="*,Auto"
RowSpacing="14">
<ui:NavigationView x:Name="SettingsNavView" <ui:NavigationView x:Name="SettingsNavView"
Grid.Row="0"
PaneDisplayMode="Left" PaneDisplayMode="Left"
IsSettingsVisible="False" IsSettingsVisible="False"
OpenPaneLength="220" OpenPaneLength="220"
@@ -459,6 +462,53 @@
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</ui:NavigationView> </ui:NavigationView>
<Border x:Name="PendingRestartDock"
Grid.Row="1"
IsVisible="False"
Classes="glass-panel"
CornerRadius="18"
Padding="14,12">
<Grid ColumnDefinitions="Auto,*,Auto"
ColumnSpacing="12">
<Border Width="34"
Height="34"
CornerRadius="17"
Background="{DynamicResource AdaptiveAccentBrush}">
<fi:FluentIcon Icon="ArrowSync"
IconVariant="Regular"
FontSize="16"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<StackPanel Grid.Column="1"
Spacing="2"
VerticalAlignment="Center">
<TextBlock x:Name="PendingRestartDockTitleTextBlock"
FontSize="13"
FontWeight="SemiBold"
Text="Restart required" />
<TextBlock x:Name="PendingRestartDockDescriptionTextBlock"
TextWrapping="Wrap"
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
Text="Your changes will apply after restarting the app." />
</StackPanel>
<Button x:Name="PendingRestartDockButton"
Grid.Column="2"
Padding="14,8"
Click="OnPendingRestartDockButtonClick">
<StackPanel Orientation="Horizontal" Spacing="8">
<fi:FluentIcon Icon="ArrowSync"
IconVariant="Regular" />
<TextBlock x:Name="PendingRestartDockButtonTextBlock"
VerticalAlignment="Center"
Text="Restart app" />
</StackPanel>
</Button>
</Grid>
</Border>
</Grid>
</Border> </Border>
</Border> </Border>
</Grid> </Grid>

View File

@@ -169,6 +169,7 @@ public partial class MainWindow : Window
private bool _suppressAutoStartToggleEvents; private bool _suppressAutoStartToggleEvents;
private bool _suppressAppRenderModeSelectionEvents; private bool _suppressAppRenderModeSelectionEvents;
private string _selectedAppRenderMode = AppRenderingModeHelper.Default; private string _selectedAppRenderMode = AppRenderingModeHelper.Default;
private string _runningAppRenderMode = AppRenderingModeHelper.Default;
private string _weatherSearchKeyword = string.Empty; private string _weatherSearchKeyword = string.Empty;
private bool _isWeatherSearchInProgress; private bool _isWeatherSearchInProgress;
private bool _isWeatherPreviewInProgress; private bool _isWeatherPreviewInProgress;
@@ -190,6 +191,7 @@ public partial class MainWindow : Window
_fluentAvaloniaTheme = Application.Current?.Styles.OfType<FluentAvaloniaTheme>().FirstOrDefault(); _fluentAvaloniaTheme = Application.Current?.Styles.OfType<FluentAvaloniaTheme>().FirstOrDefault();
AppSettingsService.SettingsSaved += OnExternalAppSettingsSaved; AppSettingsService.SettingsSaved += OnExternalAppSettingsSaved;
LauncherSettingsService.SettingsSaved += OnExternalLauncherSettingsSaved; LauncherSettingsService.SettingsSaved += OnExternalLauncherSettingsSaved;
PendingRestartStateService.StateChanged += OnPendingRestartStateChanged;
PropertyChanged += OnWindowPropertyChanged; PropertyChanged += OnWindowPropertyChanged;
InitializeDesktopSurfaceSwipeHandlers(); InitializeDesktopSurfaceSwipeHandlers();
InitializeDesktopComponentDragHandlers(); InitializeDesktopComponentDragHandlers();
@@ -314,6 +316,7 @@ public partial class MainWindow : Window
InitializeWeatherSettings(snapshot); InitializeWeatherSettings(snapshot);
_ = _componentSettingsService.Load(); _ = _componentSettingsService.Load();
InitializeAutoStartWithWindowsSetting(snapshot); InitializeAutoStartWithWindowsSetting(snapshot);
InitializeAppRenderModeSetting(snapshot);
InitializeUpdateSettings(snapshot); InitializeUpdateSettings(snapshot);
InitializeDesktopSurfaceState(desktopLayoutSnapshot); InitializeDesktopSurfaceState(desktopLayoutSnapshot);
InitializeLauncherVisibilitySettings(launcherSnapshot); InitializeLauncherVisibilitySettings(launcherSnapshot);
@@ -379,6 +382,7 @@ public partial class MainWindow : Window
_wallpaperBitmap = null; _wallpaperBitmap = null;
AppSettingsService.SettingsSaved -= OnExternalAppSettingsSaved; AppSettingsService.SettingsSaved -= OnExternalAppSettingsSaved;
LauncherSettingsService.SettingsSaved -= OnExternalLauncherSettingsSaved; LauncherSettingsService.SettingsSaved -= OnExternalLauncherSettingsSaved;
PendingRestartStateService.StateChanged -= OnPendingRestartStateChanged;
PropertyChanged -= OnWindowPropertyChanged; PropertyChanged -= OnWindowPropertyChanged;
DesktopHost.SizeChanged -= OnDesktopHostSizeChanged; DesktopHost.SizeChanged -= OnDesktopHostSizeChanged;
WallpaperPreviewHost.SizeChanged -= OnWallpaperPreviewHostSizeChanged; WallpaperPreviewHost.SizeChanged -= OnWallpaperPreviewHostSizeChanged;

View File

@@ -64,6 +64,7 @@
<ui:SettingsExpander.Footer> <ui:SettingsExpander.Footer>
<ComboBox x:Name="AppRenderModeComboBox" <ComboBox x:Name="AppRenderModeComboBox"
MinWidth="180" MinWidth="180"
SelectedIndex="0"
HorizontalAlignment="Right"> HorizontalAlignment="Right">
<ComboBoxItem Content="Default" Tag="Default" /> <ComboBoxItem Content="Default" Tag="Default" />
<ComboBoxItem Content="Software" Tag="Software" /> <ComboBoxItem Content="Software" Tag="Software" />

View File

@@ -12,6 +12,7 @@ using FluentIcons.Avalonia.Fluent;
using FluentIcons.Common; using FluentIcons.Common;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services;
using LanMountainDesktop.Views.Components; using LanMountainDesktop.Views.Components;
namespace LanMountainDesktop.Views; namespace LanMountainDesktop.Views;
@@ -43,6 +44,7 @@ public partial class SettingsWindow
} }
_launcherIconCache.Clear(); _launcherIconCache.Clear();
PendingRestartStateService.StateChanged -= OnPendingRestartStateChanged;
base.OnClosed(e); base.OnClosed(e);
} }

View File

@@ -137,6 +137,7 @@ public partial class SettingsWindow
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Wgl, L("settings.about.render_mode.wgl", "WGL")); SetAppRenderModeComboItemContent(AppRenderingModeHelper.Wgl, L("settings.about.render_mode.wgl", "WGL"));
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Vulkan, L("settings.about.render_mode.vulkan", "Vulkan")); SetAppRenderModeComboItemContent(AppRenderingModeHelper.Vulkan, L("settings.about.render_mode.vulkan", "Vulkan"));
UpdateCurrentRenderBackendStatus(); UpdateCurrentRenderBackendStatus();
UpdatePendingRestartDock();
var placementItems = WallpaperPlacementComboBox.Items.OfType<ComboBoxItem>().ToList(); var placementItems = WallpaperPlacementComboBox.Items.OfType<ComboBoxItem>().ToList();
if (placementItems.Count >= 5) if (placementItems.Count >= 5)

View File

@@ -0,0 +1,112 @@
using System.Threading.Tasks;
using Avalonia.Interactivity;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using LanMountainDesktop.Services;
namespace LanMountainDesktop.Views;
public partial class SettingsWindow
{
private bool _isRestartPromptVisible;
private void OnPendingRestartStateChanged()
{
if (Dispatcher.UIThread.CheckAccess())
{
UpdatePendingRestartDock();
return;
}
Dispatcher.UIThread.Post(UpdatePendingRestartDock);
}
private void UpdatePendingRestartDock()
{
PendingRestartDock.IsVisible = PendingRestartStateService.HasPendingRestart;
PendingRestartDockTitleTextBlock.Text = L("settings.restart_dock.title", "Restart required");
PendingRestartDockDescriptionTextBlock.Text = L(
"settings.restart_dock.description",
"Some changes will take effect after restarting the app.");
PendingRestartDockButtonTextBlock.Text = L("settings.restart_dock.button", "Restart app");
}
private async void OnPendingRestartDockButtonClick(object? sender, RoutedEventArgs e)
{
await ShowGenericRestartPromptAsync();
}
private Task ShowRenderModeRestartPromptAsync(string selectedMode)
{
var message = Lf(
"settings.restart_dialog.render_mode_message",
"Restart the app to switch the rendering mode from \"{0}\" to \"{1}\". Restart now?",
GetLocalizedAppRenderModeDisplayName(_runningAppRenderMode),
GetLocalizedAppRenderModeDisplayName(selectedMode));
return ShowRestartPromptCoreAsync(message);
}
private Task ShowGenericRestartPromptAsync()
{
return ShowRestartPromptCoreAsync(L(
"settings.restart_dock.description",
"Some changes will take effect after restarting the app."));
}
private async Task ShowRestartPromptCoreAsync(string message)
{
if (_isRestartPromptVisible)
{
return;
}
_isRestartPromptVisible = true;
try
{
var dialog = new ContentDialog
{
Title = L("settings.restart_dialog.title", "Restart required"),
Content = message,
PrimaryButtonText = L("settings.restart_dialog.restart", "Restart now"),
CloseButtonText = L("settings.restart_dialog.cancel", "Cancel"),
DefaultButton = ContentDialogButton.Primary
};
var result = await dialog.ShowAsync(this);
if (result == ContentDialogResult.Primary)
{
if (!AppRestartService.TryRestartApplication())
{
UpdatePendingRestartDock();
}
return;
}
UpdatePendingRestartDock();
}
finally
{
_isRestartPromptVisible = false;
}
}
private string GetLocalizedAppRenderModeDisplayName(string renderMode)
{
if (renderMode == AppRenderBackendDiagnostics.Unknown)
{
return L("settings.about.render_mode.unknown", "Unknown");
}
return AppRenderingModeHelper.Normalize(renderMode) switch
{
AppRenderingModeHelper.Software => L("settings.about.render_mode.software", "Software"),
AppRenderingModeHelper.AngleEgl => L("settings.about.render_mode.angle_egl", "angleEgl"),
AppRenderingModeHelper.Wgl => L("settings.about.render_mode.wgl", "WGL"),
AppRenderingModeHelper.Vulkan => L("settings.about.render_mode.vulkan", "Vulkan"),
_ => L("settings.about.render_mode.default", "Default")
};
}
}

View File

@@ -92,12 +92,16 @@ public partial class SettingsWindow
private void InitializeAppRenderModeSetting(AppSettingsSnapshot snapshot) private void InitializeAppRenderModeSetting(AppSettingsSnapshot snapshot)
{ {
_selectedAppRenderMode = AppRenderingModeHelper.Normalize(snapshot.AppRenderMode); _selectedAppRenderMode = AppRenderingModeHelper.Normalize(snapshot.AppRenderMode);
_runningAppRenderMode = ResolveActiveAppRenderModeForUi(_selectedAppRenderMode);
var renderModeForUi = PendingRestartStateService.HasPendingReason(PendingRestartStateService.RenderModeReason)
? _selectedAppRenderMode
: _runningAppRenderMode;
_suppressAppRenderModeSelectionEvents = true; _suppressAppRenderModeSelectionEvents = true;
try try
{ {
AppRenderModeComboBox.IsEnabled = OperatingSystem.IsWindows(); AppRenderModeComboBox.IsEnabled = OperatingSystem.IsWindows();
SelectAppRenderModeInUi(_selectedAppRenderMode); SelectAppRenderModeInUi(renderModeForUi);
} }
finally finally
{ {
@@ -107,13 +111,27 @@ public partial class SettingsWindow
private void SelectAppRenderModeInUi(string renderMode) private void SelectAppRenderModeInUi(string renderMode)
{ {
var selectedItem = AppRenderModeComboBox.Items AppRenderModeComboBox.SelectedIndex = GetAppRenderModeComboBoxIndex(renderMode);
.OfType<ComboBoxItem>() }
.FirstOrDefault(item =>
string.Equals(item.Tag?.ToString(), renderMode, StringComparison.OrdinalIgnoreCase));
AppRenderModeComboBox.SelectedItem = selectedItem private static int GetAppRenderModeComboBoxIndex(string renderMode)
?? AppRenderModeComboBox.Items.OfType<ComboBoxItem>().FirstOrDefault(); {
return AppRenderingModeHelper.Normalize(renderMode) switch
{
AppRenderingModeHelper.Software => 1,
AppRenderingModeHelper.AngleEgl => 2,
AppRenderingModeHelper.Wgl => 3,
AppRenderingModeHelper.Vulkan => 4,
_ => 0
};
}
private static string ResolveActiveAppRenderModeForUi(string configuredRenderMode)
{
var detectedRenderMode = AppRenderBackendDiagnostics.Detect().ActualBackend;
return string.Equals(detectedRenderMode, AppRenderBackendDiagnostics.Unknown, StringComparison.Ordinal)
? configuredRenderMode
: AppRenderingModeHelper.Normalize(detectedRenderMode);
} }
private static WeatherLocationMode ParseWeatherLocationMode(string? value) private static WeatherLocationMode ParseWeatherLocationMode(string? value)
@@ -354,7 +372,7 @@ public partial class SettingsWindow
} }
var selectedMode = AppRenderingModeHelper.Normalize( var selectedMode = AppRenderingModeHelper.Normalize(
(AppRenderModeComboBox.SelectedItem as ComboBoxItem)?.Tag?.ToString()); TryGetSelectedComboBoxTag(AppRenderModeComboBox));
if (string.Equals(_selectedAppRenderMode, selectedMode, StringComparison.Ordinal)) if (string.Equals(_selectedAppRenderMode, selectedMode, StringComparison.Ordinal))
{ {
@@ -363,6 +381,14 @@ public partial class SettingsWindow
_selectedAppRenderMode = selectedMode; _selectedAppRenderMode = selectedMode;
PersistSettings(); PersistSettings();
var requiresRestart = !string.Equals(_runningAppRenderMode, selectedMode, StringComparison.Ordinal);
PendingRestartStateService.SetPending(PendingRestartStateService.RenderModeReason, requiresRestart);
UpdatePendingRestartDock();
if (requiresRestart)
{
_ = ShowRenderModeRestartPromptAsync(selectedMode);
}
} }
private async void OnSearchWeatherCityClick(object? sender, RoutedEventArgs e) private async void OnSearchWeatherCityClick(object? sender, RoutedEventArgs e)

View File

@@ -79,7 +79,10 @@
Classes="mica-strong" Classes="mica-strong"
CornerRadius="0,0,24,24" CornerRadius="0,0,24,24"
Padding="18"> Padding="18">
<Grid RowDefinitions="*,Auto"
RowSpacing="14">
<ui:NavigationView x:Name="SettingsNavView" <ui:NavigationView x:Name="SettingsNavView"
Grid.Row="0"
PaneDisplayMode="Left" PaneDisplayMode="Left"
IsSettingsVisible="False" IsSettingsVisible="False"
OpenPaneLength="240" OpenPaneLength="240"
@@ -155,6 +158,53 @@
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</ui:NavigationView> </ui:NavigationView>
<Border x:Name="PendingRestartDock"
Grid.Row="1"
IsVisible="False"
Classes="glass-panel"
CornerRadius="18"
Padding="14,12">
<Grid ColumnDefinitions="Auto,*,Auto"
ColumnSpacing="12">
<Border Width="34"
Height="34"
CornerRadius="17"
Background="{DynamicResource AdaptiveAccentBrush}">
<fi:FluentIcon Icon="ArrowSync"
IconVariant="Regular"
FontSize="16"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<StackPanel Grid.Column="1"
Spacing="2"
VerticalAlignment="Center">
<TextBlock x:Name="PendingRestartDockTitleTextBlock"
FontSize="13"
FontWeight="SemiBold"
Text="Restart required" />
<TextBlock x:Name="PendingRestartDockDescriptionTextBlock"
TextWrapping="Wrap"
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
Text="Your changes will apply after restarting the app." />
</StackPanel>
<Button x:Name="PendingRestartDockButton"
Grid.Column="2"
Padding="14,8"
Click="OnPendingRestartDockButtonClick">
<StackPanel Orientation="Horizontal" Spacing="8">
<fi:FluentIcon Icon="ArrowSync"
IconVariant="Regular" />
<TextBlock x:Name="PendingRestartDockButtonTextBlock"
VerticalAlignment="Center"
Text="Restart app" />
</StackPanel>
</Button>
</Grid>
</Border>
</Grid>
</Border> </Border>
</Grid> </Grid>
</Border> </Border>

View File

@@ -142,6 +142,7 @@ public partial class SettingsWindow : Window
private int _statusBarCustomSpacingPercent = 12; private int _statusBarCustomSpacingPercent = 12;
private int _desktopEdgeInsetPercent = DefaultEdgeInsetPercent; private int _desktopEdgeInsetPercent = DefaultEdgeInsetPercent;
private string _selectedAppRenderMode = AppRenderingModeHelper.Default; private string _selectedAppRenderMode = AppRenderingModeHelper.Default;
private string _runningAppRenderMode = AppRenderingModeHelper.Default;
private string _taskbarLayoutMode = TaskbarLayoutBottomFullRowMacStyle; private string _taskbarLayoutMode = TaskbarLayoutBottomFullRowMacStyle;
private string _languageCode = "zh-CN"; private string _languageCode = "zh-CN";
private WeatherLocationMode _weatherLocationMode = WeatherLocationMode.CitySearch; private WeatherLocationMode _weatherLocationMode = WeatherLocationMode.CitySearch;
@@ -165,6 +166,7 @@ public partial class SettingsWindow : Window
InitializePluginSettingsNavigation(); InitializePluginSettingsNavigation();
_fluentAvaloniaTheme = Application.Current?.Styles.OfType<FluentAvaloniaTheme>().FirstOrDefault(); _fluentAvaloniaTheme = Application.Current?.Styles.OfType<FluentAvaloniaTheme>().FirstOrDefault();
RequestedThemeVariant = Application.Current?.RequestedThemeVariant ?? ThemeVariant.Default; RequestedThemeVariant = Application.Current?.RequestedThemeVariant ?? ThemeVariant.Default;
PendingRestartStateService.StateChanged += OnPendingRestartStateChanged;
HookEvents(); HookEvents();
} }