mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
0.5.5
现已支持更改关键设置时提醒重启功能
This commit is contained in:
@@ -96,15 +96,7 @@ public partial class App : Application
|
||||
|
||||
private void OnTrayRestartClick(object? sender, EventArgs e)
|
||||
{
|
||||
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AppRestartService.TryRestartCurrentProcess())
|
||||
{
|
||||
desktop.Shutdown();
|
||||
}
|
||||
AppRestartService.TryRestartApplication();
|
||||
}
|
||||
|
||||
private void DisableAvaloniaDataAnnotationValidation()
|
||||
|
||||
@@ -249,6 +249,13 @@
|
||||
"settings.about.render_mode.current_format": "Current backend: {0}",
|
||||
"settings.about.render_mode.impl_format": "Runtime implementation: {0}",
|
||||
"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",
|
||||
"filepicker.title": "Select wallpaper",
|
||||
"filepicker.image_files": "Image files",
|
||||
|
||||
@@ -249,6 +249,13 @@
|
||||
"settings.about.render_mode.current_format": "当前后端:{0}",
|
||||
"settings.about.render_mode.impl_format": "运行时实现:{0}",
|
||||
"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 设置",
|
||||
"filepicker.title": "选择壁纸",
|
||||
"filepicker.image_files": "图片文件",
|
||||
|
||||
@@ -3,11 +3,28 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
|
||||
namespace LanMountainDesktop.Services;
|
||||
|
||||
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()
|
||||
{
|
||||
try
|
||||
|
||||
54
LanMountainDesktop/Services/PendingRestartStateService.cs
Normal file
54
LanMountainDesktop/Services/PendingRestartStateService.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,6 +327,7 @@ public partial class MainWindow
|
||||
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Wgl, L("settings.about.render_mode.wgl", "WGL"));
|
||||
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Vulkan, L("settings.about.render_mode.vulkan", "Vulkan"));
|
||||
UpdateCurrentRenderBackendStatus();
|
||||
UpdatePendingRestartDock();
|
||||
|
||||
if (WallpaperPlacementComboBox?.ItemCount >= 5)
|
||||
{
|
||||
|
||||
112
LanMountainDesktop/Views/MainWindow.RestartPrompt.cs
Normal file
112
LanMountainDesktop/Views/MainWindow.RestartPrompt.cs
Normal 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")
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1225,6 +1225,10 @@ public partial class MainWindow
|
||||
private void InitializeAppRenderModeSetting(AppSettingsSnapshot snapshot)
|
||||
{
|
||||
_selectedAppRenderMode = AppRenderingModeHelper.Normalize(snapshot.AppRenderMode);
|
||||
_runningAppRenderMode = ResolveActiveAppRenderModeForUi(_selectedAppRenderMode);
|
||||
var renderModeForUi = PendingRestartStateService.HasPendingReason(PendingRestartStateService.RenderModeReason)
|
||||
? _selectedAppRenderMode
|
||||
: _runningAppRenderMode;
|
||||
|
||||
if (AppRenderModeComboBox is null)
|
||||
{
|
||||
@@ -1235,7 +1239,7 @@ public partial class MainWindow
|
||||
try
|
||||
{
|
||||
AppRenderModeComboBox.IsEnabled = OperatingSystem.IsWindows();
|
||||
SelectAppRenderModeInUi(_selectedAppRenderMode);
|
||||
SelectAppRenderModeInUi(renderModeForUi);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1250,13 +1254,27 @@ public partial class MainWindow
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedItem = AppRenderModeComboBox.Items
|
||||
.OfType<ComboBoxItem>()
|
||||
.FirstOrDefault(item =>
|
||||
string.Equals(item.Tag?.ToString(), renderMode, StringComparison.OrdinalIgnoreCase));
|
||||
AppRenderModeComboBox.SelectedIndex = GetAppRenderModeComboBoxIndex(renderMode);
|
||||
}
|
||||
|
||||
AppRenderModeComboBox.SelectedItem = selectedItem
|
||||
?? AppRenderModeComboBox.Items.OfType<ComboBoxItem>().FirstOrDefault();
|
||||
private static int GetAppRenderModeComboBoxIndex(string renderMode)
|
||||
{
|
||||
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)
|
||||
@@ -1534,7 +1552,7 @@ public partial class MainWindow
|
||||
}
|
||||
|
||||
var selectedMode = AppRenderingModeHelper.Normalize(
|
||||
(AppRenderModeComboBox.SelectedItem as ComboBoxItem)?.Tag?.ToString());
|
||||
TryGetSelectedComboBoxTag(AppRenderModeComboBox));
|
||||
|
||||
if (string.Equals(_selectedAppRenderMode, selectedMode, StringComparison.Ordinal))
|
||||
{
|
||||
@@ -1543,6 +1561,14 @@ public partial class MainWindow
|
||||
|
||||
_selectedAppRenderMode = selectedMode;
|
||||
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)
|
||||
|
||||
@@ -377,88 +377,138 @@
|
||||
<Border Classes="mica-strong"
|
||||
CornerRadius="{DynamicResource DesignCornerRadiusXl}"
|
||||
Padding="18">
|
||||
<ui:NavigationView x:Name="SettingsNavView"
|
||||
PaneDisplayMode="Left"
|
||||
IsSettingsVisible="False"
|
||||
OpenPaneLength="220"
|
||||
SelectionChanged="OnSettingsNavSelectionChanged">
|
||||
<ui:NavigationView.MenuItems>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavWallpaperItem" Content="壁纸" Tag="Wallpaper" ToolTip.Tip="壁纸">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Wallpaper" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavGridItem" Content="网格" Tag="Grid" ToolTip.Tip="网格">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Grid" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavColorItem" Content="颜色" Tag="Color" ToolTip.Tip="颜色">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Color" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavStatusBarItem" Content="状态栏" Tag="StatusBar" ToolTip.Tip="状态栏">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Status" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavWeatherItem" Content="天气" Tag="Weather" ToolTip.Tip="天气">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="WeatherSunny" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavRegionItem" Content="地区" Tag="Region" ToolTip.Tip="地区">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Globe" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavUpdateItem" Content="更新" Tag="Update" ToolTip.Tip="更新">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="ArrowSync" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavAboutItem" Content="关于" Tag="About" ToolTip.Tip="关于">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Info" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavLauncherItem" Content="应用启动台" Tag="Launcher" ToolTip.Tip="应用启动台">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Apps" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavPluginsItem" Content="插件" Tag="Plugins" ToolTip.Tip="插件">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="PuzzlePiece" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
</ui:NavigationView.MenuItems>
|
||||
<Grid RowDefinitions="*,Auto"
|
||||
RowSpacing="14">
|
||||
<ui:NavigationView x:Name="SettingsNavView"
|
||||
Grid.Row="0"
|
||||
PaneDisplayMode="Left"
|
||||
IsSettingsVisible="False"
|
||||
OpenPaneLength="220"
|
||||
SelectionChanged="OnSettingsNavSelectionChanged">
|
||||
<ui:NavigationView.MenuItems>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavWallpaperItem" Content="壁纸" Tag="Wallpaper" ToolTip.Tip="壁纸">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Wallpaper" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavGridItem" Content="网格" Tag="Grid" ToolTip.Tip="网格">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Grid" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavColorItem" Content="颜色" Tag="Color" ToolTip.Tip="颜色">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Color" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavStatusBarItem" Content="状态栏" Tag="StatusBar" ToolTip.Tip="状态栏">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Status" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavWeatherItem" Content="天气" Tag="Weather" ToolTip.Tip="天气">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="WeatherSunny" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavRegionItem" Content="地区" Tag="Region" ToolTip.Tip="地区">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Globe" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavUpdateItem" Content="更新" Tag="Update" ToolTip.Tip="更新">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="ArrowSync" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavAboutItem" Content="关于" Tag="About" ToolTip.Tip="关于">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Info" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavLauncherItem" Content="应用启动台" Tag="Launcher" ToolTip.Tip="应用启动台">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Apps" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavPluginsItem" Content="插件" Tag="Plugins" ToolTip.Tip="插件">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="PuzzlePiece" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
</ui:NavigationView.MenuItems>
|
||||
|
||||
<ScrollViewer x:Name="SettingsContentScrollViewer"
|
||||
Padding="0,0,16,0"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Grid x:Name="SettingsContentPagesHost">
|
||||
<pages:WallpaperSettingsPage x:Name="WallpaperSettingsPanel" IsVisible="True" />
|
||||
<ScrollViewer x:Name="SettingsContentScrollViewer"
|
||||
Padding="0,0,16,0"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Grid x:Name="SettingsContentPagesHost">
|
||||
<pages:WallpaperSettingsPage x:Name="WallpaperSettingsPanel" IsVisible="True" />
|
||||
|
||||
<pages:GridSettingsPage x:Name="GridSettingsPanel" IsVisible="False" />
|
||||
<pages:GridSettingsPage x:Name="GridSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:ColorSettingsPage x:Name="ColorSettingsPanel" IsVisible="False" />
|
||||
<pages:ColorSettingsPage x:Name="ColorSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:StatusBarSettingsPage x:Name="StatusBarSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:WeatherSettingsPage x:Name="WeatherSettingsPanel" IsVisible="False" />
|
||||
<pages:RegionSettingsPage x:Name="RegionSettingsPanel" IsVisible="False" />
|
||||
<pages:StatusBarSettingsPage x:Name="StatusBarSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:WeatherSettingsPage x:Name="WeatherSettingsPanel" IsVisible="False" />
|
||||
<pages:RegionSettingsPage x:Name="RegionSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:UpdateSettingsPage x:Name="UpdateSettingsPanel" IsVisible="False" />
|
||||
<pages:UpdateSettingsPage x:Name="UpdateSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:LauncherSettingsPage x:Name="LauncherSettingsPanel" IsVisible="False" />
|
||||
<pages:AboutSettingsPage x:Name="AboutSettingsPanel" IsVisible="False" />
|
||||
<pages:PluginSettingsPage x:Name="PluginSettingsPanel" IsVisible="False" />
|
||||
<pages:LauncherSettingsPage x:Name="LauncherSettingsPanel" IsVisible="False" />
|
||||
<pages:AboutSettingsPage x:Name="AboutSettingsPanel" IsVisible="False" />
|
||||
<pages:PluginSettingsPage x:Name="PluginSettingsPanel" IsVisible="False" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</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>
|
||||
</ScrollViewer>
|
||||
</ui:NavigationView>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
@@ -169,6 +169,7 @@ public partial class MainWindow : Window
|
||||
private bool _suppressAutoStartToggleEvents;
|
||||
private bool _suppressAppRenderModeSelectionEvents;
|
||||
private string _selectedAppRenderMode = AppRenderingModeHelper.Default;
|
||||
private string _runningAppRenderMode = AppRenderingModeHelper.Default;
|
||||
private string _weatherSearchKeyword = string.Empty;
|
||||
private bool _isWeatherSearchInProgress;
|
||||
private bool _isWeatherPreviewInProgress;
|
||||
@@ -190,6 +191,7 @@ public partial class MainWindow : Window
|
||||
_fluentAvaloniaTheme = Application.Current?.Styles.OfType<FluentAvaloniaTheme>().FirstOrDefault();
|
||||
AppSettingsService.SettingsSaved += OnExternalAppSettingsSaved;
|
||||
LauncherSettingsService.SettingsSaved += OnExternalLauncherSettingsSaved;
|
||||
PendingRestartStateService.StateChanged += OnPendingRestartStateChanged;
|
||||
PropertyChanged += OnWindowPropertyChanged;
|
||||
InitializeDesktopSurfaceSwipeHandlers();
|
||||
InitializeDesktopComponentDragHandlers();
|
||||
@@ -314,6 +316,7 @@ public partial class MainWindow : Window
|
||||
InitializeWeatherSettings(snapshot);
|
||||
_ = _componentSettingsService.Load();
|
||||
InitializeAutoStartWithWindowsSetting(snapshot);
|
||||
InitializeAppRenderModeSetting(snapshot);
|
||||
InitializeUpdateSettings(snapshot);
|
||||
InitializeDesktopSurfaceState(desktopLayoutSnapshot);
|
||||
InitializeLauncherVisibilitySettings(launcherSnapshot);
|
||||
@@ -379,6 +382,7 @@ public partial class MainWindow : Window
|
||||
_wallpaperBitmap = null;
|
||||
AppSettingsService.SettingsSaved -= OnExternalAppSettingsSaved;
|
||||
LauncherSettingsService.SettingsSaved -= OnExternalLauncherSettingsSaved;
|
||||
PendingRestartStateService.StateChanged -= OnPendingRestartStateChanged;
|
||||
PropertyChanged -= OnWindowPropertyChanged;
|
||||
DesktopHost.SizeChanged -= OnDesktopHostSizeChanged;
|
||||
WallpaperPreviewHost.SizeChanged -= OnWallpaperPreviewHostSizeChanged;
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ComboBox x:Name="AppRenderModeComboBox"
|
||||
MinWidth="180"
|
||||
SelectedIndex="0"
|
||||
HorizontalAlignment="Right">
|
||||
<ComboBoxItem Content="Default" Tag="Default" />
|
||||
<ComboBoxItem Content="Software" Tag="Software" />
|
||||
|
||||
@@ -12,6 +12,7 @@ using FluentIcons.Avalonia.Fluent;
|
||||
using FluentIcons.Common;
|
||||
using LanMountainDesktop.ComponentSystem;
|
||||
using LanMountainDesktop.Models;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Views.Components;
|
||||
|
||||
namespace LanMountainDesktop.Views;
|
||||
@@ -43,6 +44,7 @@ public partial class SettingsWindow
|
||||
}
|
||||
|
||||
_launcherIconCache.Clear();
|
||||
PendingRestartStateService.StateChanged -= OnPendingRestartStateChanged;
|
||||
base.OnClosed(e);
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ public partial class SettingsWindow
|
||||
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Wgl, L("settings.about.render_mode.wgl", "WGL"));
|
||||
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Vulkan, L("settings.about.render_mode.vulkan", "Vulkan"));
|
||||
UpdateCurrentRenderBackendStatus();
|
||||
UpdatePendingRestartDock();
|
||||
|
||||
var placementItems = WallpaperPlacementComboBox.Items.OfType<ComboBoxItem>().ToList();
|
||||
if (placementItems.Count >= 5)
|
||||
|
||||
112
LanMountainDesktop/Views/SettingsWindow.RestartPrompt.cs
Normal file
112
LanMountainDesktop/Views/SettingsWindow.RestartPrompt.cs
Normal 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")
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -92,12 +92,16 @@ public partial class SettingsWindow
|
||||
private void InitializeAppRenderModeSetting(AppSettingsSnapshot snapshot)
|
||||
{
|
||||
_selectedAppRenderMode = AppRenderingModeHelper.Normalize(snapshot.AppRenderMode);
|
||||
_runningAppRenderMode = ResolveActiveAppRenderModeForUi(_selectedAppRenderMode);
|
||||
var renderModeForUi = PendingRestartStateService.HasPendingReason(PendingRestartStateService.RenderModeReason)
|
||||
? _selectedAppRenderMode
|
||||
: _runningAppRenderMode;
|
||||
|
||||
_suppressAppRenderModeSelectionEvents = true;
|
||||
try
|
||||
{
|
||||
AppRenderModeComboBox.IsEnabled = OperatingSystem.IsWindows();
|
||||
SelectAppRenderModeInUi(_selectedAppRenderMode);
|
||||
SelectAppRenderModeInUi(renderModeForUi);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -107,13 +111,27 @@ public partial class SettingsWindow
|
||||
|
||||
private void SelectAppRenderModeInUi(string renderMode)
|
||||
{
|
||||
var selectedItem = AppRenderModeComboBox.Items
|
||||
.OfType<ComboBoxItem>()
|
||||
.FirstOrDefault(item =>
|
||||
string.Equals(item.Tag?.ToString(), renderMode, StringComparison.OrdinalIgnoreCase));
|
||||
AppRenderModeComboBox.SelectedIndex = GetAppRenderModeComboBoxIndex(renderMode);
|
||||
}
|
||||
|
||||
AppRenderModeComboBox.SelectedItem = selectedItem
|
||||
?? AppRenderModeComboBox.Items.OfType<ComboBoxItem>().FirstOrDefault();
|
||||
private static int GetAppRenderModeComboBoxIndex(string renderMode)
|
||||
{
|
||||
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)
|
||||
@@ -354,7 +372,7 @@ public partial class SettingsWindow
|
||||
}
|
||||
|
||||
var selectedMode = AppRenderingModeHelper.Normalize(
|
||||
(AppRenderModeComboBox.SelectedItem as ComboBoxItem)?.Tag?.ToString());
|
||||
TryGetSelectedComboBoxTag(AppRenderModeComboBox));
|
||||
|
||||
if (string.Equals(_selectedAppRenderMode, selectedMode, StringComparison.Ordinal))
|
||||
{
|
||||
@@ -363,6 +381,14 @@ public partial class SettingsWindow
|
||||
|
||||
_selectedAppRenderMode = selectedMode;
|
||||
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)
|
||||
|
||||
@@ -79,7 +79,10 @@
|
||||
Classes="mica-strong"
|
||||
CornerRadius="0,0,24,24"
|
||||
Padding="18">
|
||||
<Grid RowDefinitions="*,Auto"
|
||||
RowSpacing="14">
|
||||
<ui:NavigationView x:Name="SettingsNavView"
|
||||
Grid.Row="0"
|
||||
PaneDisplayMode="Left"
|
||||
IsSettingsVisible="False"
|
||||
OpenPaneLength="240"
|
||||
@@ -155,6 +158,53 @@
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</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>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -142,6 +142,7 @@ public partial class SettingsWindow : Window
|
||||
private int _statusBarCustomSpacingPercent = 12;
|
||||
private int _desktopEdgeInsetPercent = DefaultEdgeInsetPercent;
|
||||
private string _selectedAppRenderMode = AppRenderingModeHelper.Default;
|
||||
private string _runningAppRenderMode = AppRenderingModeHelper.Default;
|
||||
private string _taskbarLayoutMode = TaskbarLayoutBottomFullRowMacStyle;
|
||||
private string _languageCode = "zh-CN";
|
||||
private WeatherLocationMode _weatherLocationMode = WeatherLocationMode.CitySearch;
|
||||
@@ -165,6 +166,7 @@ public partial class SettingsWindow : Window
|
||||
InitializePluginSettingsNavigation();
|
||||
_fluentAvaloniaTheme = Application.Current?.Styles.OfType<FluentAvaloniaTheme>().FirstOrDefault();
|
||||
RequestedThemeVariant = Application.Current?.RequestedThemeVariant ?? ThemeVariant.Default;
|
||||
PendingRestartStateService.StateChanged += OnPendingRestartStateChanged;
|
||||
HookEvents();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user