add:更新面板
This commit is contained in:
@@ -27,6 +27,37 @@ namespace Ink_Canvas.Helpers
|
||||
private static readonly string updatesFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoUpdate");
|
||||
private static string statusFilePath;
|
||||
|
||||
// 全局下载取消令牌;UI 通过 RequestCancelDownload 取消当前下载
|
||||
private static CancellationTokenSource _activeDownloadCts;
|
||||
private static readonly object _activeDownloadLock = new object();
|
||||
|
||||
public static void RequestCancelDownload()
|
||||
{
|
||||
lock (_activeDownloadLock)
|
||||
{
|
||||
try { _activeDownloadCts?.Cancel(); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
private static CancellationTokenSource BeginDownloadSession()
|
||||
{
|
||||
lock (_activeDownloadLock)
|
||||
{
|
||||
try { _activeDownloadCts?.Cancel(); } catch { }
|
||||
_activeDownloadCts = new CancellationTokenSource();
|
||||
return _activeDownloadCts;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EndDownloadSession(CancellationTokenSource cts)
|
||||
{
|
||||
lock (_activeDownloadLock)
|
||||
{
|
||||
if (ReferenceEquals(_activeDownloadCts, cts)) _activeDownloadCts = null;
|
||||
}
|
||||
try { cts?.Dispose(); } catch { }
|
||||
}
|
||||
|
||||
public static bool IsX64UpdatePackageSelected()
|
||||
{
|
||||
try
|
||||
@@ -383,6 +414,8 @@ namespace Ink_Canvas.Helpers
|
||||
// 获取所有可用线路组,按延迟排序
|
||||
public static async Task<List<UpdateLineGroup>> GetAvailableLineGroupsOrdered(UpdateChannel channel)
|
||||
{
|
||||
var cached = TryGetCachedOrderedGroups(channel);
|
||||
if (cached != null) return cached;
|
||||
var groups = ChannelLineGroups[channel];
|
||||
var availableGroups = new List<(UpdateLineGroup group, long delay)>();
|
||||
|
||||
@@ -468,9 +501,46 @@ namespace Ink_Canvas.Helpers
|
||||
LogHelper.WriteLogToFile("AutoUpdate | 所有线路组均不可用", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
CacheOrderedGroups(channel, orderedGroups);
|
||||
return orderedGroups;
|
||||
}
|
||||
|
||||
// 缓存按延迟排序后的线路组,避免短时间内重复测速
|
||||
private static readonly Dictionary<UpdateChannel, (List<UpdateLineGroup> groups, DateTime cachedAt)> _orderedGroupsCache
|
||||
= new Dictionary<UpdateChannel, (List<UpdateLineGroup>, DateTime)>();
|
||||
private static readonly TimeSpan _orderedGroupsCacheTtl = TimeSpan.FromMinutes(15);
|
||||
|
||||
private static List<UpdateLineGroup> TryGetCachedOrderedGroups(UpdateChannel channel)
|
||||
{
|
||||
lock (_orderedGroupsCache)
|
||||
{
|
||||
if (_orderedGroupsCache.TryGetValue(channel, out var entry) &&
|
||||
entry.groups != null && entry.groups.Count > 0 &&
|
||||
DateTime.UtcNow - entry.cachedAt < _orderedGroupsCacheTtl)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 复用线路组延迟检测缓存({entry.groups.Count} 个)");
|
||||
return new List<UpdateLineGroup>(entry.groups);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CacheOrderedGroups(UpdateChannel channel, List<UpdateLineGroup> groups)
|
||||
{
|
||||
lock (_orderedGroupsCache)
|
||||
{
|
||||
_orderedGroupsCache[channel] = (new List<UpdateLineGroup>(groups), DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InvalidateOrderedGroupsCache()
|
||||
{
|
||||
lock (_orderedGroupsCache)
|
||||
{
|
||||
_orderedGroupsCache.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<long> GetDownloadUrlDelay(string url)
|
||||
{
|
||||
try
|
||||
@@ -945,6 +1015,7 @@ namespace Ink_Canvas.Helpers
|
||||
// 使用多线路组下载新版(支持自动切换)
|
||||
public static async Task<bool> DownloadSetupFileWithFallback(string version, List<UpdateLineGroup> groups, Action<double, string> progressCallback = null)
|
||||
{
|
||||
var session = BeginDownloadSession();
|
||||
try
|
||||
{
|
||||
version = NormalizeVersionForUpdate(version);
|
||||
@@ -1021,6 +1092,13 @@ namespace Ink_Canvas.Helpers
|
||||
progressCallback?.Invoke(0, "所有线路组下载均失败");
|
||||
return false;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | 下载已被用户取消", LogHelper.LogType.Warning);
|
||||
SaveDownloadStatus(false);
|
||||
progressCallback?.Invoke(0, "下载已取消");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载更新时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
@@ -1033,6 +1111,10 @@ namespace Ink_Canvas.Helpers
|
||||
progressCallback?.Invoke(0, $"下载异常: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndDownloadSession(session);
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件的具体实现
|
||||
@@ -1043,6 +1125,12 @@ namespace Ink_Canvas.Helpers
|
||||
// 降低并发数,减少网络压力
|
||||
int[] threadOptions = { 32, 16, 8, 4, 1 };
|
||||
|
||||
CancellationToken externalToken;
|
||||
lock (_activeDownloadLock)
|
||||
{
|
||||
externalToken = _activeDownloadCts?.Token ?? CancellationToken.None;
|
||||
}
|
||||
|
||||
// 检查服务器是否支持Range分块下载
|
||||
bool supportRange = false;
|
||||
long totalSize = -1;
|
||||
@@ -1146,7 +1234,7 @@ namespace Ink_Canvas.Helpers
|
||||
// 增加连接超时设置
|
||||
client.Timeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token);
|
||||
var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, externalToken);
|
||||
var lastReadTime = DateTime.UtcNow;
|
||||
bool dataReceived = false;
|
||||
|
||||
@@ -1339,12 +1427,18 @@ namespace Ink_Canvas.Helpers
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 开始单线程下载: {fileUrl}");
|
||||
progressCallback?.Invoke(0, "开始单线程下载");
|
||||
|
||||
CancellationToken token;
|
||||
lock (_activeDownloadLock)
|
||||
{
|
||||
token = _activeDownloadCts?.Token ?? CancellationToken.None;
|
||||
}
|
||||
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
|
||||
client.Timeout = TimeSpan.FromMinutes(10); // 单线程下载设置更长的超时时间
|
||||
|
||||
using (var resp = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead))
|
||||
using (var resp = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, token))
|
||||
{
|
||||
resp.EnsureSuccessStatusCode();
|
||||
using (var fs = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
@@ -1355,9 +1449,9 @@ namespace Ink_Canvas.Helpers
|
||||
long downloaded = 0;
|
||||
var lastProgressUpdate = DateTime.UtcNow;
|
||||
|
||||
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
||||
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
|
||||
{
|
||||
await fs.WriteAsync(buffer, 0, read);
|
||||
await fs.WriteAsync(buffer, 0, read, token);
|
||||
downloaded += read;
|
||||
|
||||
// 限制进度更新频率,避免UI卡顿
|
||||
@@ -1379,6 +1473,13 @@ namespace Ink_Canvas.Helpers
|
||||
LogHelper.WriteLogToFile("AutoUpdate | 单线程下载完成");
|
||||
return true;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | 单线程下载已被取消", LogHelper.LogType.Warning);
|
||||
progressCallback?.Invoke(0, "下载已取消");
|
||||
try { if (File.Exists(destinationPath)) File.Delete(destinationPath); } catch { }
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 单线程下载失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
|
||||
@@ -2268,11 +2268,15 @@
|
||||
<!-- 通知弹窗 -->
|
||||
<Grid Name="GridNotifications" Margin="0,110" Visibility="Collapsed" HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom">
|
||||
<Border CornerRadius="6" MaxHeight="200" Padding="20 10" Background="{DynamicResource SettingsPageBackground}" Opacity="0.95"
|
||||
BorderBrush="#b91c1c" BorderThickness="1.5">
|
||||
<TextBlock Name="TextBlockNotice" Text="{i18n:I18n Key=Notification_TestText}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
FontSize="16" Foreground="{DynamicResource SettingsPageForeground}" />
|
||||
</Border>
|
||||
<ui:InfoBar x:Name="NotificationInfoBar"
|
||||
IsOpen="True"
|
||||
IsClosable="False"
|
||||
Severity="Warning"
|
||||
Title="提醒"
|
||||
MaxWidth="900"
|
||||
Message="{Binding Text, ElementName=TextBlockNotice}" />
|
||||
<TextBlock x:Name="TextBlockNotice" Visibility="Collapsed"
|
||||
Text="{i18n:I18n Key=Notification_TestText}" />
|
||||
</Grid>
|
||||
<!--// Old UI //-->
|
||||
<Viewbox Name="ViewBoxStackPanelMain" Visibility="Collapsed" Margin="10,10,10,55" HorizontalAlignment="Right"
|
||||
|
||||
+10
-123
@@ -45,7 +45,7 @@ namespace Ink_Canvas
|
||||
private List<System.Windows.Controls.Canvas> whiteboardPages = new List<System.Windows.Controls.Canvas>();
|
||||
private int currentPageIndex;
|
||||
private System.Windows.Controls.Canvas currentCanvas;
|
||||
private AutoUpdateHelper.UpdateLineGroup AvailableLatestLineGroup;
|
||||
internal AutoUpdateHelper.UpdateLineGroup AvailableLatestLineGroup;
|
||||
|
||||
// 全局快捷键管理器
|
||||
private GlobalHotkeyManager _globalHotkeyManager;
|
||||
@@ -1203,7 +1203,6 @@ namespace Ink_Canvas
|
||||
LoadCustomBackgroundColor();
|
||||
SetWindowMode();
|
||||
|
||||
// HasNewUpdateWindow hasNewUpdateWindow = new HasNewUpdateWindow();
|
||||
// 根据设置应用主题
|
||||
switch (Settings.Appearance.Theme)
|
||||
{
|
||||
@@ -1846,6 +1845,7 @@ namespace Ink_Canvas
|
||||
var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(Settings.Startup.UpdateChannel);
|
||||
AvailableLatestVersion = remoteVersion;
|
||||
AvailableLatestLineGroup = lineGroup;
|
||||
AvailableLatestReleaseNotes = apiReleaseNotes;
|
||||
|
||||
// 声明下载状态变量,用于整个方法
|
||||
bool isDownloadSuccessful = false;
|
||||
@@ -1882,9 +1882,6 @@ namespace Ink_Canvas
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 获取当前版本
|
||||
string currentVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
|
||||
// 如果启用了静默更新,则自动下载更新而不显示提示
|
||||
if (Settings.Startup.IsAutoUpdateWithSilence)
|
||||
{
|
||||
@@ -1908,121 +1905,10 @@ namespace Ink_Canvas
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果没有启用静默更新,则显示常规更新窗口
|
||||
string releaseDate = DateTime.Now.ToString("yyyy年MM月dd日");
|
||||
|
||||
// 从服务器获取更新日志
|
||||
string releaseNotes = await AutoUpdateHelper.GetUpdateLog(Settings.Startup.UpdateChannel);
|
||||
|
||||
// 如果获取失败,使用默认文本
|
||||
if (string.IsNullOrEmpty(releaseNotes))
|
||||
{
|
||||
releaseNotes = $@"# InkCanvasForClass v{AvailableLatestVersion}更新
|
||||
|
||||
无法获取更新日志,但新版本已准备就绪。";
|
||||
}
|
||||
|
||||
// 创建并显示更新窗口
|
||||
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, AvailableLatestVersion, releaseDate, releaseNotes);
|
||||
updateWindow.Owner = this;
|
||||
bool? dialogResult = updateWindow.ShowDialog();
|
||||
|
||||
// 如果窗口被关闭但没有点击按钮,则不执行任何操作
|
||||
if (dialogResult != true)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Update dialog closed without selection");
|
||||
return;
|
||||
}
|
||||
|
||||
// 不再从更新窗口获取自动更新设置
|
||||
|
||||
// 根据用户选择处理更新
|
||||
switch (updateWindow.Result)
|
||||
{
|
||||
case HasNewUpdateWindow.UpdateResult.UpdateNow:
|
||||
// 立即更新:显示下载进度,下载完成后立即安装
|
||||
LogHelper.WriteLogToFile("AutoUpdate | User chose to update now");
|
||||
|
||||
// 显示下载进度提示
|
||||
MessageBox.Show("开始下载更新,请稍候...", "正在更新", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
// 下载更新文件,使用多线路组下载功能
|
||||
isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel);
|
||||
|
||||
if (isDownloadSuccessful)
|
||||
{
|
||||
// 下载成功,提示用户准备安装
|
||||
MessageBoxResult result = MessageBox.Show("更新已下载完成,点击确定后将关闭软件并安装新版本!", "安装更新", MessageBoxButton.OKCancel, MessageBoxImage.Information);
|
||||
|
||||
// 只有当用户点击确定按钮后才关闭软件
|
||||
if (result == MessageBoxResult.OK)
|
||||
{
|
||||
// 设置为用户主动退出,避免被看门狗判定为崩溃
|
||||
App.IsAppExitByUser = true;
|
||||
|
||||
// 准备批处理脚本
|
||||
AutoUpdateHelper.InstallNewVersionApp(AvailableLatestVersion, true); // 修改为静默模式,避免重复启动进程
|
||||
|
||||
// 关闭软件,让安装程序接管
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | User cancelled update installation");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 下载失败
|
||||
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
break;
|
||||
|
||||
case HasNewUpdateWindow.UpdateResult.UpdateLater:
|
||||
// 稍后更新:静默下载,在软件关闭时自动安装
|
||||
LogHelper.WriteLogToFile("AutoUpdate | User chose to update later");
|
||||
|
||||
// 不管设置如何,都进行下载,使用多线路组下载功能
|
||||
isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel);
|
||||
|
||||
if (isDownloadSuccessful)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Update downloaded successfully, will install when application closes");
|
||||
|
||||
// 设置标志,在应用程序关闭时安装
|
||||
Settings.Startup.IsAutoUpdate = true;
|
||||
Settings.Startup.IsAutoUpdateWithSilence = true;
|
||||
|
||||
// 启动检查定时器
|
||||
timerCheckAutoUpdateWithSilence.Start();
|
||||
|
||||
// 通知用户
|
||||
MessageBox.Show("更新已下载完成,将在软件关闭时自动安装。", "更新已准备就绪", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Update download failed", LogHelper.LogType.Error);
|
||||
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
break;
|
||||
|
||||
case HasNewUpdateWindow.UpdateResult.SkipVersion:
|
||||
// 跳过该版本:记录到设置中
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | User chose to skip version {AvailableLatestVersion}");
|
||||
|
||||
// 记录要跳过的版本号
|
||||
Settings.Startup.SkippedVersion = AvailableLatestVersion;
|
||||
|
||||
// 保存设置到文件
|
||||
SaveSettingsToFile();
|
||||
|
||||
// 通知用户
|
||||
MessageBox.Show($"已设置跳过版本 {AvailableLatestVersion},在下次发布新版本之前不会再提示更新。",
|
||||
"已跳过此版本",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Information);
|
||||
break;
|
||||
}
|
||||
// 如果没有启用静默更新,则记录日志并依赖 Toast 通知用户。
|
||||
// 用户可在 设置 → 更新 中查看版本说明并选择更新方式。
|
||||
LogHelper.WriteLogToFile(
|
||||
$"AutoUpdate | New version {AvailableLatestVersion} available; user notified via toast, will act from settings page.");
|
||||
}
|
||||
else if (hasValidLineGroup)
|
||||
{
|
||||
@@ -2271,9 +2157,10 @@ namespace Ink_Canvas
|
||||
|
||||
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var win = new HistoryRollbackWindow(Settings.Startup.UpdateChannel);
|
||||
win.Owner = this;
|
||||
win.ShowDialog();
|
||||
var settingsWindow = new Windows.SettingsViews.SettingsWindow();
|
||||
settingsWindow.Owner = this;
|
||||
settingsWindow.Show();
|
||||
settingsWindow.NavigateToPage("UpdatePage");
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
|
||||
@@ -95,7 +95,11 @@ namespace Ink_Canvas
|
||||
/// <summary>
|
||||
/// 可用的最新版本号
|
||||
/// </summary>
|
||||
private string AvailableLatestVersion;
|
||||
internal string AvailableLatestVersion;
|
||||
/// <summary>
|
||||
/// 最近一次自动检查得到的更新说明(Markdown)
|
||||
/// </summary>
|
||||
internal string AvailableLatestReleaseNotes;
|
||||
/// <summary>
|
||||
/// 静默更新检查定时器
|
||||
/// </summary>
|
||||
|
||||
@@ -1,346 +0,0 @@
|
||||
<Window x:Class="Ink_Canvas.HasNewUpdateWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Ink_Canvas"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
|
||||
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
|
||||
mc:Ignorable="d"
|
||||
ui:WindowHelper.UseModernWindowStyle="False"
|
||||
ui:WindowHelper.SystemBackdropType="Mica"
|
||||
Title="InkCanvasForClass CE有新版本可用" Height="650" Width="900" ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterScreen" WindowStyle="None"
|
||||
Background="Transparent">
|
||||
<Window.Template>
|
||||
<ControlTemplate TargetType="Window">
|
||||
<Border Background="{DynamicResource SettingsPageBackground}"
|
||||
CornerRadius="8"
|
||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
||||
BorderThickness="1">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect Color="#000000" BlurRadius="20" ShadowDepth="0" Opacity="0.1"/>
|
||||
</Border.Effect>
|
||||
<ContentPresenter/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Window.Template>
|
||||
<Window.Resources>
|
||||
<!-- 主题相关颜色资源 -->
|
||||
<SolidColorBrush x:Key="UpdateWindowPrimaryBrush" Color="#2563eb"/>
|
||||
<SolidColorBrush x:Key="UpdateWindowPrimaryHoverBrush" Color="#1d4ed8"/>
|
||||
<SolidColorBrush x:Key="UpdateWindowPrimaryPressedBrush" Color="#1e40af"/>
|
||||
<SolidColorBrush x:Key="UpdateWindowCardBackgroundBrush" Color="#f8fafc"/>
|
||||
<SolidColorBrush x:Key="UpdateWindowCardBorderBrush" Color="#e5e7eb"/>
|
||||
<SolidColorBrush x:Key="UpdateWindowTextPrimaryBrush" Color="#1f2937"/>
|
||||
<SolidColorBrush x:Key="UpdateWindowTextSecondaryBrush" Color="#6b7280"/>
|
||||
<SolidColorBrush x:Key="UpdateWindowCloseButtonBrush" Color="#666666"/>
|
||||
|
||||
<!-- 渐变背景定义 -->
|
||||
<LinearGradientBrush x:Key="HeaderGradient" StartPoint="0,0" EndPoint="1,1">
|
||||
<GradientStop Color="#2563eb" Offset="0"/>
|
||||
<GradientStop Color="White" Offset="1"/>
|
||||
</LinearGradientBrush>
|
||||
|
||||
<!-- 现代按钮样式 -->
|
||||
<Style x:Key="ModernPrimaryButton" TargetType="Button">
|
||||
<Setter Property="Background" Value="{DynamicResource UpdateWindowPrimaryBrush}"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="20,10"/>
|
||||
<Setter Property="Height" Value="40"/>
|
||||
<Setter Property="MinWidth" Value="200"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="6"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource UpdateWindowPrimaryHoverBrush}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource UpdateWindowPrimaryPressedBrush}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ModernSecondaryButton" TargetType="Button">
|
||||
<Setter Property="Background" Value="{DynamicResource SettingsPageBackground}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource UpdateWindowTextPrimaryBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource SettingsPageBorderBrush}"/>
|
||||
<Setter Property="FontWeight" Value="Medium"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="20,10"/>
|
||||
<Setter Property="Height" Value="40"/>
|
||||
<Setter Property="MinWidth" Value="200"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="6"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.1"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.2"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ModernTertiaryButton" TargetType="Button">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="FontWeight" Value="Medium"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="20,10"/>
|
||||
<Setter Property="Height" Value="40"/>
|
||||
<Setter Property="MinWidth" Value="200"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="6"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.1"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Foreground" Value="{DynamicResource UpdateWindowTextPrimaryBrush}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.2"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<Grid Margin="0,0,0,0" Background="Transparent">
|
||||
<ikw:SimpleStackPanel VerticalAlignment="Stretch">
|
||||
<!-- 统一的主容器 -->
|
||||
<Border Background="{DynamicResource SettingsPageBackground}" CornerRadius="8" Margin="0,0,0,0"
|
||||
BorderThickness="1" BorderBrush="{DynamicResource SettingsPageBorderBrush}" Padding="0">
|
||||
|
||||
<ikw:SimpleStackPanel>
|
||||
<!-- 现代化标题栏 -->
|
||||
<Border Background="{StaticResource HeaderGradient}" CornerRadius="8,8,0,0" Margin="0,0,0,0"
|
||||
MouseLeftButtonDown="TitleBar_MouseLeftButtonDown">
|
||||
<Grid>
|
||||
<!-- 标题栏内容 -->
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="32,24,80,24" Spacing="16">
|
||||
<Border Background="White" CornerRadius="16" Padding="16" Margin="0,0,0,0">
|
||||
<Image Source="/Resources/Icons-fluent/party.png" Width="24" Height="24"/>
|
||||
</Border>
|
||||
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="4">
|
||||
<TextBlock Text="新版本发布" FontSize="24" FontWeight="Bold" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" TextAlignment="Left"/>
|
||||
<TextBlock Text="InkCanvasForClass CE 为您带来全新体验" FontSize="14" TextAlignment="Left" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
</ikw:SimpleStackPanel>
|
||||
|
||||
<!-- 自定义关闭按钮 -->
|
||||
<Button x:Name="CloseButton"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Margin="0,12,12,0"
|
||||
Width="32" Height="32"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Click="CloseButton_Click"
|
||||
Cursor="Hand"
|
||||
ToolTip="关闭">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="16"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}">
|
||||
<Path Data="M8,8 L16,16 M16,8 L8,16"
|
||||
Stroke="{DynamicResource UpdateWindowCloseButtonBrush}"
|
||||
StrokeThickness="2"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.1"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.2"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 内容标题 -->
|
||||
<Border Background="{DynamicResource UpdateWindowCardBackgroundBrush}" Padding="24,20,24,16">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12">
|
||||
<Border Background="{DynamicResource UpdateWindowPrimaryBrush}" CornerRadius="8" Padding="8">
|
||||
<TextBlock Text="📝" FontSize="16" Foreground="White"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="2">
|
||||
<TextBlock Text="更新内容" FontSize="18" FontWeight="SemiBold" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}"/>
|
||||
<TextBlock Text="查看本次更新的详细内容" FontSize="14" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<ui:ScrollViewerEx Margin="0" VerticalScrollBarVisibility="Auto" Height="200" PanningMode="VerticalOnly">
|
||||
<mdxam:MarkdownScrollViewer x:Name="markdownContent" xml:space="preserve"
|
||||
Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" MarkdownStyleName="GithubLike"
|
||||
Margin="24,16,24,16">
|
||||
# InkCanvasForClass v5.0.2更新
|
||||
|
||||
你好,旅行者们,本次InkCanvasForClass Community Edition更新带来了如下新功能供您探索:
|
||||
|
||||
1. 全新设计的UI界面,包括浮动工具栏和白板页面均经过重新设计,更加现代化的UI让您在使用的过程中更加舒适。
|
||||
2. 带来了实时修改橡皮大小和橡皮形状的菜单。您可以选择使用圆形橡皮,方形橡皮,和类似希沃白板的真实黑板擦(矩形)橡皮。
|
||||
3. 白板页面支持显示当前时间和日期
|
||||
4. 自动收纳新增对希沃轻白板、智绘教、鸿合屏幕书写等软件的支持,自动查杀新增对鸿合屏幕书写、希沃轻白板等软件的支持。
|
||||
5. 为设置界面重写了全新的UI。
|
||||
6. 重写了随机抽选模块,现在支持更丰富的抽选机制和自定义选项。
|
||||
7. 修复了部分小Bug,提升了整体的用户体验。
|
||||
8. 带来了基于FitToCurve的笔迹平滑,基于贝塞尔曲线平滑,让墨迹线条更加优美好看。
|
||||
</mdxam:MarkdownScrollViewer>
|
||||
</ui:ScrollViewerEx>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 现代化版本信息 -->
|
||||
<Border Background="{DynamicResource UpdateWindowCardBackgroundBrush}" Margin="0,0,0,0" Padding="24,20">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="24">
|
||||
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="4" HorizontalAlignment="Center">
|
||||
<TextBlock Text="版本信息" FontSize="14" FontWeight="Medium" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
|
||||
<TextBlock x:Name="updateVersionInfo" Text="本次更新: 4.9.1 -> 5.9.1"
|
||||
FontWeight="SemiBold" FontSize="16" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" TextAlignment="Center"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
<Border Width="1" Background="{DynamicResource SettingsPageBorderBrush}" Margin="0,8,0,8"/>
|
||||
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="4" HorizontalAlignment="Center">
|
||||
<TextBlock Text="发布日期" FontSize="14" FontWeight="Medium" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
|
||||
<TextBlock x:Name="updateDateInfo" Text="2024年8月4日发布更新"
|
||||
FontSize="16" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" TextAlignment="Center"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 现代化按钮区域 -->
|
||||
<Border Background="{DynamicResource UpdateWindowCardBackgroundBrush}" Margin="0,0,0,0" Padding="32,24" CornerRadius="0,0,8,8">
|
||||
<ikw:SimpleStackPanel Orientation="Vertical" HorizontalAlignment="Center" Spacing="16">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12" HorizontalAlignment="Center">
|
||||
<Border Background="{DynamicResource UpdateWindowPrimaryBrush}" CornerRadius="8" Padding="8">
|
||||
<TextBlock Text="⚡" FontSize="16" Foreground="White"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<TextBlock Text="选择更新方式" FontWeight="SemiBold" FontSize="18"
|
||||
Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" VerticalAlignment="Center"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
|
||||
<!-- 立即更新按钮 -->
|
||||
<Button x:Name="UpdateNowButton" Content="立即下载并安装"
|
||||
Style="{StaticResource ModernPrimaryButton}"
|
||||
Click="UpdateNowButton_Click" ToolTip="立即下载更新并在完成后安装"
|
||||
Visibility="Visible" IsEnabled="True"/>
|
||||
|
||||
<!-- 稍后更新按钮 -->
|
||||
<Button x:Name="UpdateLaterButton" Content="下载并在软件关闭时安装"
|
||||
Style="{StaticResource ModernSecondaryButton}"
|
||||
Click="UpdateLaterButton_Click"
|
||||
ToolTip="后台下载更新,在软件关闭时自动安装"
|
||||
Visibility="Visible" IsEnabled="True"/>
|
||||
|
||||
<!-- 跳过版本按钮 -->
|
||||
<Button x:Name="SkipVersionButton" Content="跳过该版本"
|
||||
Style="{StaticResource ModernTertiaryButton}"
|
||||
Click="SkipVersionButton_Click"
|
||||
ToolTip="跳过此版本更新" Visibility="Visible" IsEnabled="True"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 现代化下载进度指示器 -->
|
||||
<Border x:Name="DownloadProgressPanel" Background="{DynamicResource UpdateWindowCardBackgroundBrush}"
|
||||
Margin="0,0,0,0" Padding="32,24" Visibility="Collapsed">
|
||||
<ikw:SimpleStackPanel Orientation="Vertical" HorizontalAlignment="Center" Spacing="16">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12" HorizontalAlignment="Center">
|
||||
<Border Background="#10b981" CornerRadius="8" Padding="8">
|
||||
<TextBlock Text="📥" FontSize="16" Foreground="White"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<TextBlock Text="正在下载更新" FontWeight="SemiBold" FontSize="18"
|
||||
Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" VerticalAlignment="Center"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
|
||||
<!-- 现代化进度条 -->
|
||||
<Border Background="{DynamicResource SettingsPageBackground}" CornerRadius="6" Height="6" Width="400">
|
||||
<Border x:Name="ProgressFill" Background="{DynamicResource UpdateWindowPrimaryBrush}" CornerRadius="6"
|
||||
Width="0" HorizontalAlignment="Left">
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<TextBlock x:Name="DownloadProgressText" Text="正在准备下载..."
|
||||
FontSize="14" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}" HorizontalAlignment="Center"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Border>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Window>
|
||||
@@ -1,513 +0,0 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Modern;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using MdXaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
/// <summary>
|
||||
/// HasNewUpdateWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
///
|
||||
|
||||
|
||||
public partial class HasNewUpdateWindow : Window
|
||||
{
|
||||
|
||||
[DllImport("dwmapi.dll")]
|
||||
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
|
||||
|
||||
private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
|
||||
private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
|
||||
|
||||
private static bool UseImmersiveDarkMode(IntPtr handle, bool enabled)
|
||||
{
|
||||
if (IsWindows10OrGreater(17763))
|
||||
{
|
||||
var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
|
||||
if (IsWindows10OrGreater(18985))
|
||||
{
|
||||
attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
|
||||
}
|
||||
|
||||
int useImmersiveDarkMode = enabled ? 1 : 0;
|
||||
return DwmSetWindowAttribute(handle, attribute, ref useImmersiveDarkMode, sizeof(int)) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsWindows10OrGreater(int build = -1)
|
||||
{
|
||||
return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用当前主题设置
|
||||
/// </summary>
|
||||
private void ApplyCurrentTheme()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 根据主窗口的主题设置应用主题
|
||||
switch (MainWindow.Settings.Appearance.Theme)
|
||||
{
|
||||
case 0: // 浅色主题
|
||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
|
||||
UpdateColorsForLightTheme();
|
||||
break;
|
||||
case 1: // 深色主题
|
||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
|
||||
UpdateColorsForDarkTheme();
|
||||
break;
|
||||
case 2: // 跟随系统
|
||||
if (IsSystemThemeLight())
|
||||
{
|
||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
|
||||
UpdateColorsForLightTheme();
|
||||
}
|
||||
else
|
||||
{
|
||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
|
||||
UpdateColorsForDarkTheme();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用主题时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新为浅色主题颜色
|
||||
/// </summary>
|
||||
private void UpdateColorsForLightTheme()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 更新主要颜色资源
|
||||
Resources["UpdateWindowPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb));
|
||||
Resources["UpdateWindowPrimaryHoverBrush"] = new SolidColorBrush(Color.FromRgb(0x1d, 0x4e, 0xd8));
|
||||
Resources["UpdateWindowPrimaryPressedBrush"] = new SolidColorBrush(Color.FromRgb(0x1e, 0x40, 0xaf));
|
||||
Resources["UpdateWindowCardBackgroundBrush"] = new SolidColorBrush(Color.FromRgb(0xf8, 0xfa, 0xfc));
|
||||
Resources["UpdateWindowCardBorderBrush"] = new SolidColorBrush(Color.FromRgb(0xe5, 0xe7, 0xeb));
|
||||
Resources["UpdateWindowTextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x1f, 0x29, 0x37));
|
||||
Resources["UpdateWindowTextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x6b, 0x72, 0x80));
|
||||
Resources["UpdateWindowCloseButtonBrush"] = new SolidColorBrush(Color.FromRgb(0x66, 0x66, 0x66));
|
||||
|
||||
// 更新渐变背景
|
||||
var gradient = new LinearGradientBrush();
|
||||
gradient.StartPoint = new Point(0, 0);
|
||||
gradient.EndPoint = new Point(1, 1);
|
||||
gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0x25, 0x63, 0xeb), 0));
|
||||
gradient.GradientStops.Add(new GradientStop(Colors.White, 1));
|
||||
Resources["HeaderGradient"] = gradient;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"更新浅色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新为深色主题颜色
|
||||
/// </summary>
|
||||
private void UpdateColorsForDarkTheme()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 更新主要颜色资源
|
||||
Resources["UpdateWindowPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x3b, 0x82, 0xf6));
|
||||
Resources["UpdateWindowPrimaryHoverBrush"] = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb));
|
||||
Resources["UpdateWindowPrimaryPressedBrush"] = new SolidColorBrush(Color.FromRgb(0x1d, 0x4e, 0xd8));
|
||||
Resources["UpdateWindowCardBackgroundBrush"] = new SolidColorBrush(Color.FromRgb(0x2a, 0x2a, 0x2a));
|
||||
Resources["UpdateWindowCardBorderBrush"] = new SolidColorBrush(Color.FromRgb(0x40, 0x40, 0x40));
|
||||
Resources["UpdateWindowTextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0xf9, 0xfa, 0xfb));
|
||||
Resources["UpdateWindowTextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x9c, 0xa3, 0xaf));
|
||||
Resources["UpdateWindowCloseButtonBrush"] = new SolidColorBrush(Color.FromRgb(0x9c, 0xa3, 0xaf));
|
||||
|
||||
// 更新渐变背景
|
||||
var gradient = new LinearGradientBrush();
|
||||
gradient.StartPoint = new Point(0, 0);
|
||||
gradient.EndPoint = new Point(1, 1);
|
||||
gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0x3b, 0x82, 0xf6), 0));
|
||||
gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0x1f, 0x1f, 0x1f), 1));
|
||||
Resources["HeaderGradient"] = gradient;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"更新深色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查系统是否为浅色主题
|
||||
/// </summary>
|
||||
private bool IsSystemThemeLight()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"))
|
||||
{
|
||||
if (key?.GetValue("AppsUseLightTheme") is int value)
|
||||
{
|
||||
return value == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 如果无法读取注册表,默认返回true(浅色主题)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 存储更新版本信息
|
||||
public string CurrentVersion { get; set; }
|
||||
public string NewVersion { get; set; }
|
||||
public string ReleaseDate { get; set; }
|
||||
public string ReleaseNotes { get; set; }
|
||||
|
||||
// 更新按钮结果
|
||||
public enum UpdateResult
|
||||
{
|
||||
UpdateNow,
|
||||
UpdateLater,
|
||||
SkipVersion
|
||||
}
|
||||
|
||||
public UpdateResult Result { get; private set; } = UpdateResult.UpdateLater;
|
||||
|
||||
public HasNewUpdateWindow(string currentVersion, string newVersion, string releaseDate, string releaseNotes = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// 应用当前主题
|
||||
ApplyCurrentTheme();
|
||||
|
||||
// 设置版本信息
|
||||
CurrentVersion = currentVersion;
|
||||
NewVersion = newVersion;
|
||||
ReleaseDate = releaseDate;
|
||||
ReleaseNotes = releaseNotes;
|
||||
|
||||
// 更新UI
|
||||
updateVersionInfo.Text = $"本次更新: {CurrentVersion} -> {NewVersion}";
|
||||
updateDateInfo.Text = $"{ReleaseDate}发布更新";
|
||||
|
||||
// 如果有发布说明,设置到Markdown内容中
|
||||
if (!string.IsNullOrEmpty(ReleaseNotes))
|
||||
{
|
||||
markdownContent.Markdown = ReleaseNotes;
|
||||
}
|
||||
|
||||
// 自动更新和静默更新设置已移至设置界面,此处不再需要
|
||||
|
||||
// 确保按钮可见且可用
|
||||
EnsureButtonsVisibility();
|
||||
|
||||
// 显示窗口动画
|
||||
AnimationsHelper.ShowWithFadeIn(this, 0.25);
|
||||
|
||||
// 设置深色模式
|
||||
UseImmersiveDarkMode(new WindowInteropHelper(this).Handle, true);
|
||||
|
||||
// 窗口加载完成后再次确保按钮可见
|
||||
Loaded += HasNewUpdateWindow_Loaded;
|
||||
}
|
||||
|
||||
private void HasNewUpdateWindow_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 窗口加载完成后再次确保按钮可见
|
||||
EnsureButtonsVisibility();
|
||||
|
||||
// 调整窗口大小以适应屏幕分辨率
|
||||
AdjustWindowSizeForScreenResolution();
|
||||
}
|
||||
|
||||
// 确保按钮可见并启用
|
||||
private void EnsureButtonsVisibility()
|
||||
{
|
||||
// 确保立即更新按钮可见
|
||||
UpdateNowButton.Visibility = Visibility.Visible;
|
||||
UpdateNowButton.IsEnabled = true;
|
||||
|
||||
// 确保稍后更新按钮可见
|
||||
UpdateLaterButton.Visibility = Visibility.Visible;
|
||||
UpdateLaterButton.IsEnabled = true;
|
||||
|
||||
// 确保跳过版本按钮可见
|
||||
SkipVersionButton.Visibility = Visibility.Visible;
|
||||
SkipVersionButton.IsEnabled = true;
|
||||
|
||||
// 强制刷新UI
|
||||
UpdateLayout();
|
||||
|
||||
// 记录日志
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Update dialog buttons visibility ensured");
|
||||
}
|
||||
|
||||
private async void UpdateNowButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Update Now button clicked");
|
||||
// 禁用按钮,显示进度条
|
||||
UpdateNowButton.IsEnabled = false;
|
||||
UpdateLaterButton.IsEnabled = false;
|
||||
SkipVersionButton.IsEnabled = false;
|
||||
DownloadProgressPanel.Visibility = Visibility.Visible;
|
||||
|
||||
// 重置进度条
|
||||
var progressFill = FindName("ProgressFill") as Border;
|
||||
if (progressFill != null)
|
||||
{
|
||||
progressFill.Width = 0;
|
||||
}
|
||||
DownloadProgressText.Text = "正在准备下载...";
|
||||
|
||||
// 启动多线路下载
|
||||
bool downloadSuccess = false;
|
||||
try
|
||||
{
|
||||
// 获取当前通道的所有线路组
|
||||
var groups = AutoUpdateHelper.ChannelLineGroups[MainWindow.Settings.Startup.UpdateChannel];
|
||||
downloadSuccess = await AutoUpdateHelper.DownloadSetupFileWithFallback(NewVersion, groups, (percent, text) =>
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
// 更新自定义进度条
|
||||
progressFill = FindName("ProgressFill") as Border;
|
||||
if (progressFill != null)
|
||||
{
|
||||
progressFill.Width = (percent / 100.0) * 400; // 400是进度条总宽度
|
||||
}
|
||||
DownloadProgressText.Text = text;
|
||||
});
|
||||
});
|
||||
if (downloadSuccess)
|
||||
{
|
||||
// 下载完成后自动安装
|
||||
await DownloadAndInstallVersion(NewVersion, null, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DownloadProgressText.Text = $"下载失败: {ex.Message}";
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载异常: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
if (downloadSuccess)
|
||||
{
|
||||
// 设置进度条为100%
|
||||
progressFill = FindName("ProgressFill") as Border;
|
||||
if (progressFill != null)
|
||||
{
|
||||
progressFill.Width = 400; // 100%完成
|
||||
}
|
||||
DownloadProgressText.Text = "下载完成,准备安装...";
|
||||
await Task.Delay(800);
|
||||
// 设置结果为立即更新
|
||||
Result = UpdateResult.UpdateNow;
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
DownloadProgressText.Text = "下载失败,请检查网络后重试。";
|
||||
UpdateNowButton.IsEnabled = true;
|
||||
UpdateLaterButton.IsEnabled = true;
|
||||
SkipVersionButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLaterButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Update Later button clicked");
|
||||
|
||||
// 设置结果为稍后更新
|
||||
Result = UpdateResult.UpdateLater;
|
||||
|
||||
// 关闭窗口
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void SkipVersionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Skip Version button clicked");
|
||||
|
||||
// 设置结果为跳过该版本
|
||||
Result = UpdateResult.SkipVersion;
|
||||
|
||||
// 关闭窗口
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Close button clicked");
|
||||
|
||||
// 设置结果为稍后更新(默认行为)
|
||||
Result = UpdateResult.UpdateLater;
|
||||
|
||||
// 关闭窗口
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// 开始拖动窗口
|
||||
if (e.LeftButton == MouseButtonState.Pressed)
|
||||
{
|
||||
DragMove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 根据屏幕分辨率调整窗口大小
|
||||
private void AdjustWindowSizeForScreenResolution()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取主屏幕分辨率
|
||||
double screenWidth = SystemParameters.PrimaryScreenWidth;
|
||||
double screenHeight = SystemParameters.PrimaryScreenHeight;
|
||||
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Screen resolution: {screenWidth}x{screenHeight}");
|
||||
|
||||
// 始终确保窗口不超过屏幕大小的85%
|
||||
double maxHeight = screenHeight * 0.85;
|
||||
double maxWidth = screenWidth * 0.85;
|
||||
|
||||
bool needsAdjustment = false;
|
||||
|
||||
// 如果窗口高度超过最大允许高度,调整窗口高度
|
||||
if (Height > maxHeight)
|
||||
{
|
||||
Height = maxHeight;
|
||||
needsAdjustment = true;
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Adjusted window height to: {Height}");
|
||||
}
|
||||
|
||||
// 如果窗口宽度超过最大允许宽度,调整窗口宽度
|
||||
if (Width > maxWidth)
|
||||
{
|
||||
Width = maxWidth;
|
||||
needsAdjustment = true;
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Adjusted window width to: {Width}");
|
||||
}
|
||||
|
||||
// 如果屏幕分辨率较低,调整更多UI元素
|
||||
if (screenHeight < 768 || screenWidth < 1024 || needsAdjustment)
|
||||
{
|
||||
// 查找相关控件并调整大小
|
||||
var markdownViewer = FindName("markdownContent") as MarkdownScrollViewer;
|
||||
var updateNowButton = FindName("UpdateNowButton") as Button;
|
||||
var updateLaterButton = FindName("UpdateLaterButton") as Button;
|
||||
var skipVersionButton = FindName("SkipVersionButton") as Button;
|
||||
|
||||
// 查找包含ScrollViewer的边框控件,减小其高度
|
||||
var contentBorders = FindVisualChildren<Border>().ToList();
|
||||
foreach (var border in contentBorders)
|
||||
{
|
||||
if (border.Child is ScrollViewer || border.Child is ScrollViewerEx)
|
||||
{
|
||||
// 减小内容显示区域的高度
|
||||
if (border.Height > 180)
|
||||
{
|
||||
border.Height = 160;
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Reduced content area height");
|
||||
}
|
||||
else if (border.Child is ScrollViewerEx scrollViewer && scrollViewer.Height > 160)
|
||||
{
|
||||
scrollViewer.Height = 160;
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Reduced scroll viewer height");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 调整按钮大小
|
||||
if (updateNowButton != null && updateLaterButton != null && skipVersionButton != null)
|
||||
{
|
||||
updateNowButton.Height = 42;
|
||||
updateLaterButton.Height = 42;
|
||||
skipVersionButton.Height = 42;
|
||||
updateNowButton.Padding = new Thickness(15, 8, 15, 8);
|
||||
updateLaterButton.Padding = new Thickness(15, 8, 15, 8);
|
||||
skipVersionButton.Padding = new Thickness(15, 8, 15, 8);
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Reduced button sizes for small screen");
|
||||
}
|
||||
}
|
||||
|
||||
// 确保窗口在屏幕范围内
|
||||
if (Left < 0) Left = 0;
|
||||
if (Top < 0) Top = 0;
|
||||
if (Left + Width > screenWidth) Left = screenWidth - Width;
|
||||
if (Top + Height > screenHeight) Top = screenHeight - Height;
|
||||
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Final window size: {Width}x{Height}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Error adjusting window size: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// 递归查找指定类型的所有子控件
|
||||
private IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj = null) where T : DependencyObject
|
||||
{
|
||||
if (depObj == null)
|
||||
depObj = this;
|
||||
|
||||
if (depObj != null)
|
||||
{
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
|
||||
{
|
||||
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
|
||||
if (child != null && child is T)
|
||||
{
|
||||
yield return (T)child;
|
||||
}
|
||||
|
||||
foreach (T childOfChild in FindVisualChildren<T>(child))
|
||||
{
|
||||
yield return childOfChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 多线程分块下载并自动安装
|
||||
private async Task<bool> DownloadAndInstallVersion(string version, string downloadUrl, CancellationToken token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(downloadUrl))
|
||||
{
|
||||
// 自动更新场景下,downloadUrl为null,直接用主下载目录
|
||||
downloadUrl = AutoUpdateHelper.GetLocalUpdateZipFilePath(version);
|
||||
}
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 开始安装版本: {version}");
|
||||
AutoUpdateHelper.InstallNewVersionApp(version, true);
|
||||
App.IsAppExitByUser = true;
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
<Window x:Class="Ink_Canvas.HistoryRollbackWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
|
||||
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
|
||||
mc:Ignorable="d"
|
||||
Title="历史版本回滚" Height="650" Width="900" ResizeMode="CanResize"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Topmost="True"
|
||||
Background="{DynamicResource SettingsPageBackground}" MinHeight="550" MinWidth="800"
|
||||
SnapsToDevicePixels="True"
|
||||
TextOptions.TextRenderingMode="ClearType"
|
||||
TextOptions.TextFormattingMode="Display"
|
||||
ui:ThemeManager.IsThemeAware="True"
|
||||
ui:TitleBar.ExtendViewIntoTitleBar="True"
|
||||
ui:WindowHelper.SystemBackdropType="Mica"
|
||||
ui:WindowHelper.UseModernWindowStyle="True"
|
||||
ui:TitleBar.Height="48">
|
||||
<Window.Resources>
|
||||
<!-- 主题相关颜色资源 -->
|
||||
<SolidColorBrush x:Key="PrimaryBrush" Color="#FF2563eb"/>
|
||||
<SolidColorBrush x:Key="TextPrimaryBrush" Color="#FF1f2937"/>
|
||||
<SolidColorBrush x:Key="TextSecondaryBrush" Color="#FF6b7280"/>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 自定义标题栏 -->
|
||||
<Border x:Name="Border_TitleBarRoot"
|
||||
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.Height)}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=Title}"
|
||||
VerticalAlignment="Center" Margin="12,0,0,0" FontSize="12" FontWeight="SemiBold"/>
|
||||
|
||||
<!--Right Inset-->
|
||||
<Rectangle Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.SystemOverlayRightInset)}"
|
||||
Grid.Column="2"/>
|
||||
|
||||
<!--Right Buttons-->
|
||||
<ikw:SimpleStackPanel x:Name="StackPanel_RightButtons"
|
||||
Orientation="Horizontal" Grid.Column="1" Spacing="5">
|
||||
</ikw:SimpleStackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<Grid Grid.Row="1" Background="{DynamicResource SettingsPageBackground}" Margin="20,20,20,20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题区域 -->
|
||||
<ikw:SimpleStackPanel Grid.Row="0" Orientation="Horizontal" Spacing="12" Margin="0,0,0,16">
|
||||
<Border Background="{DynamicResource PrimaryBrush}" CornerRadius="16" Padding="12" Margin="0,0,0,0">
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Undo}" FontSize="20" Foreground="White"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ikw:SimpleStackPanel VerticalAlignment="Center" Spacing="4">
|
||||
<TextBlock Text="选择要回滚到的历史版本" FontSize="22" FontWeight="Bold"
|
||||
Foreground="{DynamicResource TextPrimaryBrush}"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
</ikw:SimpleStackPanel>
|
||||
|
||||
<!-- 版本选择卡片 -->
|
||||
<Border Grid.Row="1"
|
||||
Background="{DynamicResource SettingsPageBackground}"
|
||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="12"
|
||||
Margin="0,0,0,16"
|
||||
Padding="20">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect Color="#000000" BlurRadius="8" ShadowDepth="0" Opacity="0.05"/>
|
||||
</Border.Effect>
|
||||
<ikw:SimpleStackPanel Spacing="16">
|
||||
<TextBlock Text="选择版本" FontSize="16" FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextPrimaryBrush}"/>
|
||||
<ComboBox x:Name="VersionComboBox"
|
||||
Width="300" Height="40"
|
||||
DisplayMemberPath="Version"
|
||||
SelectionChanged="VersionComboBox_SelectionChanged"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 发布说明卡片 -->
|
||||
<Border Grid.Row="2"
|
||||
Background="{DynamicResource SettingsPageBackground}"
|
||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="12"
|
||||
Margin="0,0,0,16"
|
||||
Padding="20">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect Color="#000000" BlurRadius="8" ShadowDepth="0" Opacity="0.05"/>
|
||||
</Border.Effect>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="版本更新说明" FontSize="16" FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextPrimaryBrush}"
|
||||
Margin="0,0,0,16"/>
|
||||
<Border Grid.Row="1"
|
||||
Background="{DynamicResource SettingsPageBackground}"
|
||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect Color="#000000" BlurRadius="4" ShadowDepth="0" Opacity="0.03"/>
|
||||
</Border.Effect>
|
||||
<ScrollViewer x:Name="InnerReleaseNotesScrollViewer"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
Padding="16"
|
||||
PanningMode="VerticalOnly"
|
||||
PanningRatio="1.0"
|
||||
IsManipulationEnabled="True">
|
||||
<mdxam:MarkdownScrollViewer x:Name="ReleaseNotesViewer"
|
||||
Foreground="{DynamicResource TextPrimaryBrush}"
|
||||
MarkdownStyleName="GithubLike"
|
||||
IsHitTestVisible="False"
|
||||
IsManipulationEnabled="False"/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<ikw:SimpleStackPanel Grid.Row="3" Spacing="16" Margin="0,0,0,0">
|
||||
<Button x:Name="RollbackButton"
|
||||
Content="回滚到此版本"
|
||||
HorizontalAlignment="Center"
|
||||
Click="RollbackButton_Click"
|
||||
Style="{DynamicResource AccentButtonStyle}"
|
||||
Width="200" Height="48"
|
||||
FontSize="16" FontWeight="SemiBold"/>
|
||||
|
||||
<!-- 下载进度面板 -->
|
||||
<Border x:Name="DownloadProgressPanel"
|
||||
Background="{DynamicResource SettingsPageBackground}"
|
||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="12"
|
||||
Visibility="Collapsed"
|
||||
Margin="0,8,0,0"
|
||||
Padding="24">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect Color="#000000" BlurRadius="8" ShadowDepth="0" Opacity="0.05"/>
|
||||
</Border.Effect>
|
||||
<ikw:SimpleStackPanel Spacing="12">
|
||||
<ProgressBar x:Name="DownloadProgressBar"
|
||||
Width="300" Height="8"
|
||||
Minimum="0" Maximum="100" Value="0"/>
|
||||
<TextBlock x:Name="DownloadProgressText"
|
||||
Text="正在下载..."
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource TextPrimaryBrush}"
|
||||
HorizontalAlignment="Center"/>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Border>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -1,370 +0,0 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Modern;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
// Added for OrderByDescending
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
public partial class HistoryRollbackWindow : Window
|
||||
{
|
||||
private class VersionItem
|
||||
{
|
||||
public string Version { get; set; }
|
||||
public string DownloadUrl { get; set; }
|
||||
public string ReleaseNotes { get; set; }
|
||||
}
|
||||
|
||||
private List<VersionItem> versionList = new List<VersionItem>();
|
||||
private VersionItem selectedItem;
|
||||
private UpdateChannel channel = UpdateChannel.Release;
|
||||
private CancellationTokenSource downloadCts = null;
|
||||
|
||||
public HistoryRollbackWindow(UpdateChannel channel = UpdateChannel.Release)
|
||||
{
|
||||
InitializeComponent();
|
||||
this.channel = channel;
|
||||
|
||||
// 设置窗口置顶
|
||||
this.Topmost = true;
|
||||
|
||||
// 应用当前主题
|
||||
ApplyCurrentTheme();
|
||||
|
||||
// 隐藏主窗口
|
||||
HideMainWindow();
|
||||
|
||||
LoadVersions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏主窗口
|
||||
/// </summary>
|
||||
private void HideMainWindow()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取主窗口实例
|
||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||||
if (mainWindow != null)
|
||||
{
|
||||
// 隐藏主窗口
|
||||
mainWindow.Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"隐藏主窗口时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用当前主题设置
|
||||
/// </summary>
|
||||
private void ApplyCurrentTheme()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 根据主窗口的主题设置应用主题
|
||||
switch (MainWindow.Settings.Appearance.Theme)
|
||||
{
|
||||
case 0: // 浅色主题
|
||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
|
||||
UpdateColorsForLightTheme();
|
||||
break;
|
||||
case 1: // 深色主题
|
||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
|
||||
UpdateColorsForDarkTheme();
|
||||
break;
|
||||
case 2: // 跟随系统
|
||||
if (IsSystemThemeLight())
|
||||
{
|
||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
|
||||
UpdateColorsForLightTheme();
|
||||
}
|
||||
else
|
||||
{
|
||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
|
||||
UpdateColorsForDarkTheme();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用主题时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新为浅色主题颜色
|
||||
/// </summary>
|
||||
private void UpdateColorsForLightTheme()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 更新主要颜色资源
|
||||
Resources["PrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb));
|
||||
Resources["TextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x1f, 0x29, 0x37));
|
||||
Resources["TextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x6b, 0x72, 0x80));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"更新浅色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新为深色主题颜色
|
||||
/// </summary>
|
||||
private void UpdateColorsForDarkTheme()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 更新主要颜色资源
|
||||
Resources["PrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x3b, 0x82, 0xf6));
|
||||
Resources["TextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0xf9, 0xfa, 0xfb));
|
||||
Resources["TextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x9c, 0xa3, 0xaf));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"更新深色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查系统是否为浅色主题
|
||||
/// </summary>
|
||||
private bool IsSystemThemeLight()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"))
|
||||
{
|
||||
if (key?.GetValue("AppsUseLightTheme") is int value)
|
||||
{
|
||||
return value == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 如果无法读取注册表,默认返回true(浅色主题)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void LoadVersions()
|
||||
{
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 开始加载历史版本,通道: {channel}");
|
||||
RollbackButton.IsEnabled = false;
|
||||
VersionComboBox.Items.Clear();
|
||||
DownloadProgressPanel.Visibility = Visibility.Collapsed;
|
||||
DownloadProgressBar.Value = 0;
|
||||
DownloadProgressText.Text = "";
|
||||
ReleaseNotesViewer.Markdown = "正在获取历史版本...";
|
||||
var releases = await AutoUpdateHelper.GetAllGithubReleases(channel);
|
||||
versionList.Clear();
|
||||
foreach (var (version, url, notes) in releases)
|
||||
{
|
||||
versionList.Add(new VersionItem { Version = version, DownloadUrl = url, ReleaseNotes = notes });
|
||||
}
|
||||
// 按版本号数字降序排列
|
||||
versionList = versionList.OrderByDescending(v => ParseVersionForSort(v.Version)).ToList();
|
||||
VersionComboBox.ItemsSource = versionList;
|
||||
if (versionList.Count > 0)
|
||||
{
|
||||
VersionComboBox.SelectedIndex = 0;
|
||||
RollbackButton.IsEnabled = true;
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 加载到 {versionList.Count} 个历史版本");
|
||||
}
|
||||
else
|
||||
{
|
||||
ReleaseNotesViewer.Markdown = "未获取到历史版本信息。";
|
||||
LogHelper.WriteLogToFile("HistoryRollback | 未获取到历史版本信息", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:解析版本号用于排序
|
||||
private Version ParseVersionForSort(string version)
|
||||
{
|
||||
var v = version.TrimStart('v', 'V');
|
||||
Version result;
|
||||
if (Version.TryParse(v, out result))
|
||||
return result;
|
||||
return new Version(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private void VersionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
selectedItem = VersionComboBox.SelectedItem as VersionItem;
|
||||
if (selectedItem != null)
|
||||
{
|
||||
ReleaseNotesViewer.Markdown = selectedItem.ReleaseNotes ?? "无更新日志";
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择版本: {selectedItem.Version}");
|
||||
}
|
||||
// 取消聚焦,防止父级自动滚动
|
||||
Keyboard.ClearFocus();
|
||||
}
|
||||
|
||||
private async void RollbackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (selectedItem == null) return;
|
||||
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
Title = "暂停自动更新",
|
||||
PrimaryButtonText = "确定",
|
||||
SecondaryButtonText = "取消"
|
||||
};
|
||||
|
||||
var panel = new iNKORE.UI.WPF.Controls.SimpleStackPanel
|
||||
{
|
||||
Spacing = 16,
|
||||
Margin = new Thickness(0, 10, 0, 0)
|
||||
};
|
||||
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = "请选择在回滚后多久不再接收自动更新:",
|
||||
FontSize = 14,
|
||||
Foreground = (Brush)Resources["TextPrimaryBrush"]
|
||||
};
|
||||
|
||||
var daysComboBox = new System.Windows.Controls.ComboBox
|
||||
{
|
||||
Width = 200,
|
||||
Height = 36,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Left
|
||||
};
|
||||
|
||||
for (int i = 0; i <= 7; i++)
|
||||
{
|
||||
daysComboBox.Items.Add(new System.Windows.Controls.ComboBoxItem
|
||||
{
|
||||
Content = $"{i} 天",
|
||||
Tag = i
|
||||
});
|
||||
}
|
||||
|
||||
daysComboBox.SelectedIndex = 0;
|
||||
|
||||
panel.Children.Add(textBlock);
|
||||
panel.Children.Add(daysComboBox);
|
||||
dialog.Content = panel;
|
||||
|
||||
var dialogResult = await dialog.ShowAsync();
|
||||
|
||||
if (dialogResult == ContentDialogResult.Primary)
|
||||
{
|
||||
int days = 1;
|
||||
if (daysComboBox.SelectedItem is System.Windows.Controls.ComboBoxItem selectedItemCombo &&
|
||||
selectedItemCombo.Tag != null &&
|
||||
int.TryParse(selectedItemCombo.Tag.ToString(), out int selectedDays))
|
||||
{
|
||||
days = selectedDays;
|
||||
}
|
||||
|
||||
if (days == 0)
|
||||
{
|
||||
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
DateTime pauseUntilDate = DateTime.Now.AddDays(days);
|
||||
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = pauseUntilDate.ToString("yyyy-MM-dd");
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择暂停自动更新 {days} 天,截止日期: {pauseUntilDate:yyyy-MM-dd}");
|
||||
}
|
||||
|
||||
MainWindow.SaveSettingsToFile();
|
||||
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择暂停自动更新 {days} 天");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("HistoryRollback | 用户取消了回滚操作");
|
||||
return;
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户确认回滚,目标版本: {selectedItem.Version}");
|
||||
RollbackButton.IsEnabled = false;
|
||||
VersionComboBox.IsEnabled = false;
|
||||
DownloadProgressPanel.Visibility = Visibility.Visible;
|
||||
DownloadProgressBar.Value = 0;
|
||||
DownloadProgressText.Text = "正在准备下载...";
|
||||
|
||||
bool downloadSuccess = false;
|
||||
try
|
||||
{
|
||||
downloadSuccess = await AutoUpdateHelper.StartManualDownloadAndInstall(
|
||||
selectedItem.Version,
|
||||
channel,
|
||||
(percent, text) =>
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
DownloadProgressBar.Value = percent;
|
||||
DownloadProgressText.Text = text;
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DownloadProgressText.Text = $"下载失败: {ex.Message}";
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 下载异常: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
if (downloadSuccess)
|
||||
{
|
||||
DownloadProgressBar.Value = 100;
|
||||
DownloadProgressText.Text = "下载完成,准备安装...";
|
||||
await Task.Delay(800);
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
DownloadProgressText.Text = "下载失败,请检查网络后重试。";
|
||||
RollbackButton.IsEnabled = true;
|
||||
VersionComboBox.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
downloadCts?.Cancel();
|
||||
|
||||
try
|
||||
{
|
||||
// 获取主窗口实例
|
||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||||
if (mainWindow != null)
|
||||
{
|
||||
// 重新显示主窗口
|
||||
mainWindow.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"显示主窗口时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
base.OnClosing(e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -10,140 +10,308 @@
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:controls="clr-namespace:Ink_Canvas.Controls;assembly=InkCanvas.Controls"
|
||||
xmlns:c="clr-namespace:Ink_Canvas.Converter"
|
||||
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
|
||||
mc:Ignorable="d"
|
||||
|
||||
Title="更新">
|
||||
|
||||
<ScrollViewer PanningMode="VerticalFirst">
|
||||
<Grid Margin="59,0,59,0">
|
||||
<FrameworkElement.Resources>
|
||||
<sys:Double x:Key="SettingsCardSpacing">4</sys:Double>
|
||||
<c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||
<Style x:Key="SettingsSectionHeaderTextBlockStyle"
|
||||
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
|
||||
TargetType="TextBlock">
|
||||
<Style.Setters>
|
||||
<sys:Double x:Key="SettingsCardSpacing">4</sys:Double>
|
||||
<c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||
<Style x:Key="SettingsSectionHeaderTextBlockStyle"
|
||||
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="Margin" Value="1,30,0,6" />
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
</FrameworkElement.Resources>
|
||||
<Grid>
|
||||
<ikw:SimpleStackPanel MaxWidth="1000"
|
||||
HorizontalAlignment="Stretch"
|
||||
Spacing="{StaticResource SettingsCardSpacing}">
|
||||
</Style>
|
||||
</FrameworkElement.Resources>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="自动更新" />
|
||||
<ikw:SimpleStackPanel MaxWidth="1000"
|
||||
HorizontalAlignment="Stretch"
|
||||
Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<controls:LabeledSettingsCard x:Name="CardAutoUpdate"
|
||||
Header="{i18n:I18n Key=Header_AutoUpdate}"
|
||||
Description="{i18n:I18n Key=Startup_AutoUpdateHint}"
|
||||
Icon="{x:Static ui:SegoeFluentIcons.Sync}"
|
||||
SwitchName="ToggleSwitchIsAutoUpdate"
|
||||
Toggled="ToggleSwitchIsAutoUpdate_Toggled" />
|
||||
<!-- 状态横幅 -->
|
||||
<Border Margin="0,16,0,0"
|
||||
Padding="20"
|
||||
CornerRadius="8"
|
||||
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:LabeledSettingsCard x:Name="CardSilentUpdate"
|
||||
Header="{i18n:I18n Key=Header_SilentUpdate}"
|
||||
Description="{i18n:I18n Key=Startup_SilentUpdateHint}"
|
||||
Icon="{x:Static ui:SegoeFluentIcons.QuietHours}"
|
||||
SwitchName="ToggleSwitchIsAutoUpdateWithSilence"
|
||||
ShowWhen="{Binding IsOn, ElementName=CardAutoUpdate}"
|
||||
d:Visibility="Visible"
|
||||
Toggled="ToggleSwitchIsAutoUpdateWithSilence_Toggled" />
|
||||
<Border Grid.Column="0"
|
||||
Width="48" Height="48"
|
||||
CornerRadius="24"
|
||||
VerticalAlignment="Top"
|
||||
Margin="0,0,16,0"
|
||||
Background="{DynamicResource SystemControlHighlightAccentBrush}">
|
||||
<ui:FontIcon x:Name="StatusIcon"
|
||||
FontSize="22"
|
||||
Foreground="White"
|
||||
Icon="{x:Static ui:SegoeFluentIcons.Completed}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
|
||||
<ui:SettingsExpander x:Name="ExpanderSilentUpdateTime"
|
||||
Header="{i18n:I18n Key=Startup_SilentUpdateTimePeriod}"
|
||||
Visibility="{Binding IsOn, ElementName=CardSilentUpdate, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
IsExpanded="{Binding IsOn, ElementName=CardSilentUpdate, Mode=OneWay}"
|
||||
d:Visibility="Visible" d:IsExpanded="True">
|
||||
<ui:SettingsExpander.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Settings}" />
|
||||
</ui:SettingsExpander.HeaderIcon>
|
||||
<ui:SettingsExpander.Items>
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_SilentUpdateFullHint}">
|
||||
<ui:SettingsCard.Content>
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
||||
Text="{i18n:I18n Key=Startup_StartTime}" FontSize="14" />
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox"
|
||||
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged" />
|
||||
</ikw:SimpleStackPanel>
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
||||
Text="{i18n:I18n Key=Startup_EndTime}" FontSize="14" />
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
|
||||
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged" />
|
||||
</ikw:SimpleStackPanel>
|
||||
<ikw:SimpleStackPanel Grid.Column="1" Spacing="4">
|
||||
<TextBlock x:Name="StatusTitle"
|
||||
FontSize="22" FontWeight="SemiBold"
|
||||
Text="正在加载更新状态..." />
|
||||
<TextBlock x:Name="StatusSubtitle"
|
||||
Opacity="0.75"
|
||||
TextWrapping="Wrap"
|
||||
Text="" />
|
||||
|
||||
<!-- 进度区 -->
|
||||
<ikw:SimpleStackPanel x:Name="ProgressPanel"
|
||||
Visibility="Collapsed"
|
||||
Margin="0,8,0,0"
|
||||
Spacing="4">
|
||||
<TextBlock x:Name="ProgressText" FontSize="13" Opacity="0.8" />
|
||||
<ProgressBar x:Name="ProgressBar"
|
||||
Style="{DynamicResource DefaultProgressBarStyle}"
|
||||
Height="6"
|
||||
Minimum="0" Maximum="100"
|
||||
Value="0" />
|
||||
</ikw:SimpleStackPanel>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal"
|
||||
Margin="0,12,0,0"
|
||||
Spacing="8">
|
||||
<Button x:Name="CheckUpdateButton"
|
||||
Style="{DynamicResource AccentButtonStyle}"
|
||||
Click="CheckUpdateButton_Click">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Refresh}" FontSize="14" />
|
||||
<TextBlock Text="检查更新" />
|
||||
</ikw:SimpleStackPanel>
|
||||
</ui:SettingsCard.Content>
|
||||
</Button>
|
||||
<Button x:Name="UpdateNowButton"
|
||||
Visibility="Collapsed"
|
||||
Style="{DynamicResource AccentButtonStyle}"
|
||||
Click="UpdateNowButton_Click">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Download}" FontSize="14" />
|
||||
<TextBlock Text="立即下载并安装" />
|
||||
</ikw:SimpleStackPanel>
|
||||
</Button>
|
||||
<Button x:Name="UpdateLaterButton"
|
||||
Visibility="Collapsed"
|
||||
Click="UpdateLaterButton_Click">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.QuietHours}" FontSize="14" />
|
||||
<TextBlock Text="退出时安装" />
|
||||
</ikw:SimpleStackPanel>
|
||||
</Button>
|
||||
<Button x:Name="SkipVersionButton"
|
||||
Visibility="Collapsed"
|
||||
Click="SkipVersionButton_Click">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Cancel}" FontSize="14" />
|
||||
<TextBlock Text="跳过此版本" />
|
||||
</ikw:SimpleStackPanel>
|
||||
</Button>
|
||||
<Button x:Name="CancelDownloadButton"
|
||||
Visibility="Collapsed"
|
||||
Click="CancelDownloadButton_Click">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Cancel}" FontSize="14" />
|
||||
<TextBlock Text="取消" />
|
||||
</ikw:SimpleStackPanel>
|
||||
</Button>
|
||||
</ikw:SimpleStackPanel>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Tab 切换 -->
|
||||
<TabControl x:Name="UpdateTabControl"
|
||||
Margin="0,16,0,0"
|
||||
HorizontalContentAlignment="Left"
|
||||
Style="{StaticResource TabControlPivotStyle}"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
SelectionChanged="UpdateTabControl_SelectionChanged">
|
||||
<TabControl.Resources>
|
||||
<sys:Double x:Key="PivotHeaderItemFontSize">16</sys:Double>
|
||||
</TabControl.Resources>
|
||||
<!-- 更新日志 -->
|
||||
<TabItem Header="更新日志">
|
||||
<Border Margin="0,8,0,0"
|
||||
Padding="16"
|
||||
CornerRadius="8"
|
||||
MinHeight="200"
|
||||
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
PanningMode="VerticalOnly"
|
||||
MaxHeight="500">
|
||||
<mdxam:MarkdownScrollViewer x:Name="ChangelogViewer"
|
||||
MarkdownStyleName="GithubLike" />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</TabItem>
|
||||
|
||||
<!-- 更新设置 -->
|
||||
<TabItem Header="更新设置">
|
||||
<ikw:SimpleStackPanel Margin="0,8,0,0"
|
||||
Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="自动更新" />
|
||||
|
||||
<controls:LabeledSettingsCard x:Name="CardAutoUpdate"
|
||||
Header="{i18n:I18n Key=Header_AutoUpdate}"
|
||||
Description="{i18n:I18n Key=Startup_AutoUpdateHint}"
|
||||
Icon="{x:Static ui:SegoeFluentIcons.Sync}"
|
||||
SwitchName="ToggleSwitchIsAutoUpdate"
|
||||
Toggled="ToggleSwitchIsAutoUpdate_Toggled" />
|
||||
|
||||
<controls:LabeledSettingsCard x:Name="CardSilentUpdate"
|
||||
Header="{i18n:I18n Key=Header_SilentUpdate}"
|
||||
Description="{i18n:I18n Key=Startup_SilentUpdateHint}"
|
||||
Icon="{x:Static ui:SegoeFluentIcons.QuietHours}"
|
||||
SwitchName="ToggleSwitchIsAutoUpdateWithSilence"
|
||||
ShowWhen="{Binding IsOn, ElementName=CardAutoUpdate}"
|
||||
d:Visibility="Visible"
|
||||
Toggled="ToggleSwitchIsAutoUpdateWithSilence_Toggled" />
|
||||
|
||||
<ui:SettingsExpander x:Name="ExpanderSilentUpdateTime"
|
||||
Header="{i18n:I18n Key=Startup_SilentUpdateTimePeriod}"
|
||||
Visibility="{Binding IsOn, ElementName=CardSilentUpdate, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
IsExpanded="{Binding IsOn, ElementName=CardSilentUpdate, Mode=OneWay}"
|
||||
d:Visibility="Visible" d:IsExpanded="True">
|
||||
<ui:SettingsExpander.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Settings}" />
|
||||
</ui:SettingsExpander.HeaderIcon>
|
||||
<ui:SettingsExpander.Items>
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_SilentUpdateFullHint}">
|
||||
<ui:SettingsCard.Content>
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
||||
Text="{i18n:I18n Key=Startup_StartTime}" FontSize="14" />
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox"
|
||||
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged" />
|
||||
</ikw:SimpleStackPanel>
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
||||
Text="{i18n:I18n Key=Startup_EndTime}" FontSize="14" />
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
|
||||
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged" />
|
||||
</ikw:SimpleStackPanel>
|
||||
</ikw:SimpleStackPanel>
|
||||
</ui:SettingsCard.Content>
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_TimePeriodHint}" />
|
||||
</ui:SettingsExpander.Items>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="更新通道" />
|
||||
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdateChannel}"
|
||||
Description="{i18n:I18n Key=Startup_UpdateChannelHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.SyncFolder}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<ComboBox x:Name="UpdateChannelSelector"
|
||||
SelectionChanged="UpdateChannelSelector_SelectionChanged">
|
||||
<ComboBoxItem Content="{i18n:I18n Key=Update_Release}" Tag="Release" />
|
||||
<ComboBoxItem Content="{i18n:I18n Key=Update_Preview}" Tag="Preview" />
|
||||
<ComboBoxItem Content="{i18n:I18n Key=Update_Beta}" Tag="Beta" />
|
||||
</ComboBox>
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_TimePeriodHint}" />
|
||||
</ui:SettingsExpander.Items>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="更新通道" />
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdatePackageArchitecture}"
|
||||
Description="{i18n:I18n Key=Startup_UpdatePackageArchitectureHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.DeveloperTools}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<ComboBox x:Name="UpdatePackageArchitectureSelector"
|
||||
SelectionChanged="UpdatePackageArchitectureSelector_SelectionChanged">
|
||||
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X86}" Tag="X86" />
|
||||
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X64}" Tag="X64" />
|
||||
</ComboBox>
|
||||
</ui:SettingsCard>
|
||||
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdateChannel}"
|
||||
Description="{i18n:I18n Key=Startup_UpdateChannelHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.SyncFolder}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<ComboBox x:Name="UpdateChannelSelector"
|
||||
SelectionChanged="UpdateChannelSelector_SelectionChanged">
|
||||
<ComboBoxItem Content="{i18n:I18n Key=Update_Release}" Tag="Release" />
|
||||
<ComboBoxItem Content="{i18n:I18n Key=Update_Preview}" Tag="Preview" />
|
||||
<ComboBoxItem Content="{i18n:I18n Key=Update_Beta}" Tag="Beta" />
|
||||
</ComboBox>
|
||||
</ui:SettingsCard>
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="维护" />
|
||||
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdatePackageArchitecture}"
|
||||
Description="{i18n:I18n Key=Startup_UpdatePackageArchitectureHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.DeveloperTools}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<ComboBox x:Name="UpdatePackageArchitectureSelector"
|
||||
SelectionChanged="UpdatePackageArchitectureSelector_SelectionChanged">
|
||||
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X86}" Tag="X86" />
|
||||
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X64}" Tag="X64" />
|
||||
</ComboBox>
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Btn_FixVersion}"
|
||||
Description="{i18n:I18n Key=Startup_FixVersionHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Repair}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<Button x:Name="FixVersionButton" Content="{i18n:I18n Key=Btn_FixVersion}"
|
||||
Click="FixVersionButton_Click" />
|
||||
</ui:SettingsCard>
|
||||
</ikw:SimpleStackPanel>
|
||||
</TabItem>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="手动操作" />
|
||||
<!-- 历史版本 -->
|
||||
<TabItem x:Name="HistoryTabItem" Header="历史版本">
|
||||
<ikw:SimpleStackPanel Margin="0,8,0,0" Spacing="12">
|
||||
<ui:SettingsCard Header="选择历史版本"
|
||||
Description="选择一个旧版本进行回滚(将下载并安装该版本)。">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.History}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<ComboBox x:Name="VersionComboBox"
|
||||
MinWidth="220"
|
||||
DisplayMemberPath="Version"
|
||||
SelectionChanged="VersionComboBox_SelectionChanged" />
|
||||
</ui:SettingsCard>
|
||||
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Btn_ManualUpdate}"
|
||||
Description="{i18n:I18n Key=Startup_ManualUpdateHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Search}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<Button x:Name="ManualUpdateButton" Content="{i18n:I18n Key=Btn_ManualUpdate}"
|
||||
Click="ManualUpdateButton_Click" />
|
||||
</ui:SettingsCard>
|
||||
<Border Padding="16"
|
||||
CornerRadius="8"
|
||||
MinHeight="200"
|
||||
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
|
||||
<ScrollViewer x:Name="ReleaseNotesScrollViewer"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
PanningMode="VerticalOnly"
|
||||
MaxHeight="350">
|
||||
<mdxam:MarkdownScrollViewer x:Name="ReleaseNotesViewer"
|
||||
MarkdownStyleName="GithubLike"
|
||||
IsHitTestVisible="False" />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Btn_FixVersion}"
|
||||
Description="{i18n:I18n Key=Startup_FixVersionHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Repair}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<Button x:Name="FixVersionButton" Content="{i18n:I18n Key=Btn_FixVersion}"
|
||||
Click="FixVersionButton_Click" />
|
||||
</ui:SettingsCard>
|
||||
<Button x:Name="RollbackButton"
|
||||
HorizontalAlignment="Left"
|
||||
IsEnabled="False"
|
||||
Style="{DynamicResource AccentButtonStyle}"
|
||||
Click="RollbackButton_Click">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Undo}" FontSize="14" />
|
||||
<TextBlock Text="回滚到此版本" />
|
||||
</ikw:SimpleStackPanel>
|
||||
</Button>
|
||||
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Btn_HistoryRollback}"
|
||||
Description="{i18n:I18n Key=Startup_HistoryRollbackHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.History}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<Button Content="{i18n:I18n Key=Btn_HistoryRollback}"
|
||||
Click="HistoryRollbackButton_Click" />
|
||||
</ui:SettingsCard>
|
||||
<Border x:Name="RollbackProgressPanel"
|
||||
Visibility="Collapsed"
|
||||
Padding="12"
|
||||
CornerRadius="8"
|
||||
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
|
||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
||||
BorderThickness="1">
|
||||
<ikw:SimpleStackPanel Spacing="6">
|
||||
<TextBlock x:Name="RollbackProgressText" Text="正在准备下载..." FontSize="13" />
|
||||
<ProgressBar x:Name="RollbackProgressBar"
|
||||
Style="{DynamicResource DefaultProgressBarStyle}"
|
||||
Height="6"
|
||||
Minimum="0" Maximum="100" Value="0" />
|
||||
</ikw:SimpleStackPanel>
|
||||
</Border>
|
||||
</ikw:SimpleStackPanel>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
|
||||
<Rectangle Height="48" />
|
||||
<Rectangle Height="48" />
|
||||
|
||||
</ikw:SimpleStackPanel>
|
||||
</Grid>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</ui:Page>
|
||||
</ui:Page>
|
||||
@@ -1,9 +1,15 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using Ink_Canvas.Windows.SettingsViews.Helpers;
|
||||
using iNKORE.UI.WPF.Modern.Common.IconKeys;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
|
||||
|
||||
@@ -11,9 +17,35 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
{
|
||||
public partial class UpdatePage : iNKORE.UI.WPF.Modern.Controls.Page
|
||||
{
|
||||
private bool _isLoaded = false;
|
||||
private bool _isChangingUpdateChannelInternally = false;
|
||||
private bool _isChangingUpdatePackageArchInternally = false;
|
||||
private enum UpdateUiState
|
||||
{
|
||||
Idle,
|
||||
Checking,
|
||||
UpdateAvailable,
|
||||
Downloading,
|
||||
Downloaded,
|
||||
NetworkError
|
||||
}
|
||||
|
||||
private class VersionItem
|
||||
{
|
||||
public string Version { get; set; }
|
||||
public string DownloadUrl { get; set; }
|
||||
public string ReleaseNotes { get; set; }
|
||||
}
|
||||
|
||||
private bool _isLoaded;
|
||||
private bool _isChangingUpdateChannelInternally;
|
||||
private bool _isChangingUpdatePackageArchInternally;
|
||||
|
||||
private UpdateUiState _state = UpdateUiState.Idle;
|
||||
private string _remoteVersion;
|
||||
private AutoUpdateHelper.UpdateLineGroup _remoteLineGroup;
|
||||
private string _remoteReleaseNotes;
|
||||
|
||||
private List<VersionItem> _versionList = new List<VersionItem>();
|
||||
private VersionItem _selectedHistoricalItem;
|
||||
private bool _isHistoryLoaded;
|
||||
|
||||
public UpdatePage()
|
||||
{
|
||||
@@ -21,12 +53,52 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
Loaded += UpdatePage_Loaded;
|
||||
}
|
||||
|
||||
private void UpdatePage_Loaded(object sender, RoutedEventArgs e)
|
||||
private async void UpdatePage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LoadSettings();
|
||||
_isLoaded = true;
|
||||
|
||||
// 复用启动时自动检查的结果,避免二次检查
|
||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||||
if (mainWindow != null && !string.IsNullOrEmpty(mainWindow.AvailableLatestVersion))
|
||||
{
|
||||
_remoteVersion = mainWindow.AvailableLatestVersion;
|
||||
_remoteLineGroup = mainWindow.AvailableLatestLineGroup;
|
||||
_remoteReleaseNotes = mainWindow.AvailableLatestReleaseNotes;
|
||||
|
||||
try
|
||||
{
|
||||
var statusFile = AutoUpdateHelper.GetUpdateDownloadStatusFilePath(_remoteVersion);
|
||||
if (System.IO.File.Exists(statusFile) &&
|
||||
System.IO.File.ReadAllText(statusFile).Trim().ToLower() == "true")
|
||||
{
|
||||
ApplyState(UpdateUiState.Downloaded);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyState(UpdateUiState.UpdateAvailable);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
ApplyState(UpdateUiState.UpdateAvailable);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_remoteReleaseNotes))
|
||||
ChangelogViewer.Markdown = _remoteReleaseNotes;
|
||||
else
|
||||
ChangelogViewer.Markdown = "切换到 *历史版本* 或点击 *检查更新* 查看具体更新日志。";
|
||||
return;
|
||||
}
|
||||
|
||||
// 没有缓存的检查结果时,仅展示空白;不主动联网,避免打开页面就触发请求
|
||||
ApplyState(UpdateUiState.Idle);
|
||||
ChangelogViewer.Markdown = "点击 *检查更新* 来获取最新版本及更新日志。";
|
||||
await System.Threading.Tasks.Task.CompletedTask;
|
||||
}
|
||||
|
||||
#region 更新设置加载
|
||||
|
||||
private void LoadSettings()
|
||||
{
|
||||
_isLoaded = false;
|
||||
@@ -82,6 +154,8 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
_isLoaded = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 自动更新事件处理
|
||||
|
||||
private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e)
|
||||
@@ -113,8 +187,7 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
|
||||
try
|
||||
{
|
||||
bool newState = CardSilentUpdate.IsOn;
|
||||
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = newState;
|
||||
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = CardSilentUpdate.IsOn;
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -197,7 +270,7 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
if (isTestChannel && !SettingsManager.Settings.Startup.HasAcceptedTelemetryPrivacy)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"加入预览 / 测试通道前,请先在关于页面勾选\u201C我已阅读并同意 privacy 中的隐私说明\u201D。",
|
||||
"加入预览 / 测试通道前,请先在关于页面勾选“我已阅读并同意 privacy 中的隐私说明”。",
|
||||
"需要同意隐私说明",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Warning);
|
||||
@@ -238,6 +311,13 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
LogHelper.WriteLogToFile($"Settings | Update channel changed to {SettingsManager.Settings.Startup.UpdateChannel}");
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
|
||||
// 通道切换后强制刷新更新日志和历史版本缓存
|
||||
_isHistoryLoaded = false;
|
||||
_versionList.Clear();
|
||||
VersionComboBox.ItemsSource = null;
|
||||
ReleaseNotesViewer.Markdown = "";
|
||||
await LoadChangelogAsync();
|
||||
|
||||
if (SettingsManager.Settings.Startup.IsAutoUpdate)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check");
|
||||
@@ -262,10 +342,6 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, but auto-update is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
private void RevertChannelSelection(UpdateChannel targetChannel)
|
||||
@@ -295,124 +371,520 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
|
||||
#endregion
|
||||
|
||||
#region 手动操作事件处理
|
||||
#region 更新状态机
|
||||
|
||||
private async void ManualUpdateButton_Click(object sender, RoutedEventArgs e)
|
||||
private void ApplyState(UpdateUiState state, string customSubtitle = null)
|
||||
{
|
||||
ManualUpdateButton.IsEnabled = false;
|
||||
ManualUpdateButton.Content = "正在检查更新...";
|
||||
_state = state;
|
||||
|
||||
CheckUpdateButton.Visibility = Visibility.Collapsed;
|
||||
UpdateNowButton.Visibility = Visibility.Collapsed;
|
||||
UpdateLaterButton.Visibility = Visibility.Collapsed;
|
||||
SkipVersionButton.Visibility = Visibility.Collapsed;
|
||||
CancelDownloadButton.Visibility = Visibility.Collapsed;
|
||||
ProgressPanel.Visibility = Visibility.Collapsed;
|
||||
|
||||
CheckUpdateButton.IsEnabled = true;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case UpdateUiState.Idle:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Completed;
|
||||
StatusTitle.Text = "已是最新版本";
|
||||
StatusSubtitle.Text = customSubtitle ?? BuildLastCheckSubtitle();
|
||||
CheckUpdateButton.Visibility = Visibility.Visible;
|
||||
break;
|
||||
|
||||
case UpdateUiState.Checking:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Sync;
|
||||
StatusTitle.Text = "正在检查更新...";
|
||||
StatusSubtitle.Text = "";
|
||||
CheckUpdateButton.Visibility = Visibility.Visible;
|
||||
CheckUpdateButton.IsEnabled = false;
|
||||
ProgressPanel.Visibility = Visibility.Visible;
|
||||
ProgressText.Text = "正在连接更新服务器...";
|
||||
ProgressBar.IsIndeterminate = true;
|
||||
break;
|
||||
|
||||
case UpdateUiState.UpdateAvailable:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Upload;
|
||||
StatusTitle.Text = $"检测到新版本 {_remoteVersion}";
|
||||
StatusSubtitle.Text = customSubtitle ?? $"当前版本 {GetCurrentVersion()} → {_remoteVersion}";
|
||||
UpdateNowButton.Visibility = Visibility.Visible;
|
||||
UpdateLaterButton.Visibility = Visibility.Visible;
|
||||
SkipVersionButton.Visibility = Visibility.Visible;
|
||||
break;
|
||||
|
||||
case UpdateUiState.Downloading:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Download;
|
||||
StatusTitle.Text = "正在下载更新...";
|
||||
StatusSubtitle.Text = customSubtitle ?? $"目标版本 {_remoteVersion}";
|
||||
ProgressPanel.Visibility = Visibility.Visible;
|
||||
ProgressBar.IsIndeterminate = false;
|
||||
break;
|
||||
|
||||
case UpdateUiState.Downloaded:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Download;
|
||||
StatusTitle.Text = "更新已下载完成";
|
||||
StatusSubtitle.Text = customSubtitle ?? $"将在软件关闭时自动安装 {_remoteVersion}";
|
||||
CheckUpdateButton.Visibility = Visibility.Visible;
|
||||
break;
|
||||
|
||||
case UpdateUiState.NetworkError:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Error;
|
||||
StatusTitle.Text = "网络错误";
|
||||
StatusSubtitle.Text = customSubtitle ?? "请检查网络连接后重试。";
|
||||
CheckUpdateButton.Visibility = Visibility.Visible;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildLastCheckSubtitle()
|
||||
{
|
||||
string current = GetCurrentVersion();
|
||||
return $"当前版本 {current}";
|
||||
}
|
||||
|
||||
private static string GetCurrentVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 更新日志
|
||||
|
||||
private async System.Threading.Tasks.Task LoadChangelogAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
ChangelogViewer.Markdown = "正在加载更新日志...";
|
||||
|
||||
// 优先尝试从 GitHub API 获取最新 Release 的 body
|
||||
try
|
||||
{
|
||||
var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
var latest = releases?
|
||||
.OrderByDescending(r => ParseVersionForSort(r.version))
|
||||
.Select(r => (Tuple<string, string, string>)Tuple.Create(r.version, r.downloadUrl, r.releaseNotes))
|
||||
.FirstOrDefault();
|
||||
if (latest != null && !string.IsNullOrWhiteSpace(latest.Item3))
|
||||
{
|
||||
ChangelogViewer.Markdown = latest.Item3;
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"UpdatePage | GitHub API 获取更新日志失败,回退到镜像源: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
// 回退到镜像源
|
||||
string md = await AutoUpdateHelper.GetUpdateLog(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
ChangelogViewer.Markdown = string.IsNullOrEmpty(md) ? "暂无更新日志。" : md;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ChangelogViewer.Markdown = $"加载更新日志失败:{ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 检查 / 下载 / 安装
|
||||
|
||||
private async void CheckUpdateButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ApplyState(UpdateUiState.Checking);
|
||||
try
|
||||
{
|
||||
LogHelper.WriteLogToFile("ManualUpdate | Manual update button clicked");
|
||||
|
||||
var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(SettingsManager.Settings.Startup.UpdateChannel, true, false);
|
||||
string remoteVersion = null;
|
||||
string apiReleaseNotes = null;
|
||||
AutoUpdateHelper.UpdateLineGroup lineGroup = null;
|
||||
|
||||
if (remoteVersion != null)
|
||||
// 优先通过 GitHub Releases API 获取最新版本
|
||||
try
|
||||
{
|
||||
LogHelper.WriteLogToFile($"ManualUpdate | Found new version: {remoteVersion}");
|
||||
|
||||
string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
|
||||
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, remoteVersion, "", apiReleaseNotes);
|
||||
updateWindow.Owner = Application.Current.MainWindow;
|
||||
bool? dialogResult = updateWindow.ShowDialog();
|
||||
|
||||
if (dialogResult != true)
|
||||
var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
var latest = releases?
|
||||
.OrderByDescending(r => ParseVersionForSort(r.version))
|
||||
.Select(r => Tuple.Create(r.version, r.downloadUrl, r.releaseNotes))
|
||||
.FirstOrDefault();
|
||||
if (latest != null && !string.IsNullOrEmpty(latest.Item1))
|
||||
{
|
||||
LogHelper.WriteLogToFile("ManualUpdate | Update dialog closed without selection");
|
||||
return;
|
||||
var localVersion = new Version(GetCurrentVersion());
|
||||
var remote = ParseVersionForSort(latest.Item1);
|
||||
if (remote > localVersion)
|
||||
{
|
||||
remoteVersion = latest.Item1.TrimStart('v', 'V');
|
||||
apiReleaseNotes = latest.Item3;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"UpdatePage | GitHub API 检查更新失败,回退到 CheckForUpdates: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||||
// 回退:调用统一的 CheckForUpdates(包含镜像源 txt 方案)
|
||||
if (string.IsNullOrEmpty(remoteVersion))
|
||||
{
|
||||
var (rv, lg, notes) = await AutoUpdateHelper.CheckForUpdates(
|
||||
SettingsManager.Settings.Startup.UpdateChannel, true, false);
|
||||
remoteVersion = rv;
|
||||
lineGroup = lg;
|
||||
if (string.IsNullOrEmpty(apiReleaseNotes)) apiReleaseNotes = notes;
|
||||
}
|
||||
|
||||
switch (updateWindow.Result)
|
||||
{
|
||||
case HasNewUpdateWindow.UpdateResult.UpdateNow:
|
||||
LogHelper.WriteLogToFile("ManualUpdate | User chose to update now");
|
||||
MessageBox.Show("开始下载更新,请稍候...", "正在更新", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
if (!string.IsNullOrEmpty(remoteVersion))
|
||||
{
|
||||
_remoteVersion = remoteVersion;
|
||||
_remoteLineGroup = lineGroup;
|
||||
_remoteReleaseNotes = apiReleaseNotes;
|
||||
|
||||
bool isDownloadSuccessful = mainWindow != null
|
||||
&& await mainWindow.DownloadUpdateWithFallback(remoteVersion, lineGroup, SettingsManager.Settings.Startup.UpdateChannel);
|
||||
if (!string.IsNullOrEmpty(apiReleaseNotes))
|
||||
ChangelogViewer.Markdown = apiReleaseNotes;
|
||||
|
||||
if (isDownloadSuccessful)
|
||||
{
|
||||
MessageBoxResult result = MessageBox.Show("更新已下载完成,点击确定后将关闭软件并安装新版本!", "安装更新", MessageBoxButton.OKCancel, MessageBoxImage.Information);
|
||||
|
||||
if (result == MessageBoxResult.OK)
|
||||
{
|
||||
App.IsAppExitByUser = true;
|
||||
AutoUpdateHelper.InstallNewVersionApp(remoteVersion, true);
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("ManualUpdate | User cancelled update installation");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
break;
|
||||
|
||||
case HasNewUpdateWindow.UpdateResult.UpdateLater:
|
||||
LogHelper.WriteLogToFile("ManualUpdate | User chose to update later");
|
||||
|
||||
isDownloadSuccessful = mainWindow != null
|
||||
&& await mainWindow.DownloadUpdateWithFallback(remoteVersion, lineGroup, SettingsManager.Settings.Startup.UpdateChannel);
|
||||
|
||||
if (isDownloadSuccessful)
|
||||
{
|
||||
LogHelper.WriteLogToFile("ManualUpdate | Update downloaded successfully, will install when application closes");
|
||||
SettingsManager.Settings.Startup.IsAutoUpdate = true;
|
||||
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = true;
|
||||
|
||||
if (mainWindow != null)
|
||||
mainWindow.StartSilentUpdateTimer();
|
||||
|
||||
MessageBox.Show("更新已下载完成,将在软件关闭时自动安装。", "更新已准备就绪", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("ManualUpdate | Update download failed", LogHelper.LogType.Error);
|
||||
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
break;
|
||||
|
||||
case HasNewUpdateWindow.UpdateResult.SkipVersion:
|
||||
LogHelper.WriteLogToFile($"ManualUpdate | User chose to skip version {remoteVersion}");
|
||||
SettingsManager.Settings.Startup.SkippedVersion = remoteVersion;
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
MessageBox.Show($"已设置跳过版本 {remoteVersion},在下次发布新版本之前不会再提示更新。",
|
||||
"已跳过此版本",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Information);
|
||||
break;
|
||||
}
|
||||
ApplyState(UpdateUiState.UpdateAvailable);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("ManualUpdate | No updates available");
|
||||
MessageBox.Show("当前已是最新版本!", "无可用更新", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
ApplyState(UpdateUiState.Idle);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"Error in ManualUpdateButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show(
|
||||
$"手动更新过程中发生错误: {ex.Message}",
|
||||
"更新错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ManualUpdateButton.IsEnabled = true;
|
||||
ManualUpdateButton.Content = "手动更新";
|
||||
LogHelper.WriteLogToFile($"Error in CheckUpdateButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
ApplyState(UpdateUiState.NetworkError, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private bool _downloadCancelled;
|
||||
private static List<AutoUpdateHelper.UpdateLineGroup> _cachedOrderedGroups;
|
||||
private static UpdateChannel _cachedGroupsChannel;
|
||||
|
||||
private static async System.Threading.Tasks.Task<List<AutoUpdateHelper.UpdateLineGroup>> GetOrderedGroupsCachedAsync(UpdateChannel channel)
|
||||
{
|
||||
if (_cachedOrderedGroups != null && _cachedOrderedGroups.Count > 0 && _cachedGroupsChannel == channel)
|
||||
return _cachedOrderedGroups;
|
||||
var groups = await AutoUpdateHelper.GetAvailableLineGroupsOrdered(channel);
|
||||
_cachedOrderedGroups = groups;
|
||||
_cachedGroupsChannel = channel;
|
||||
return groups;
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task<bool> DownloadWithProgressAsync()
|
||||
{
|
||||
_downloadCancelled = false;
|
||||
var groups = await GetOrderedGroupsCachedAsync(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
if (groups == null || groups.Count == 0)
|
||||
{
|
||||
LogHelper.WriteLogToFile("UpdatePage | 没有可用的下载线路组", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
return await AutoUpdateHelper.DownloadSetupFileWithFallback(_remoteVersion, groups, (percent, text) =>
|
||||
{
|
||||
if (_downloadCancelled) return;
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (_state != UpdateUiState.Downloading) return;
|
||||
ProgressBar.IsIndeterminate = false;
|
||||
ProgressBar.Value = percent;
|
||||
ProgressText.Text = text;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async void UpdateNowButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_remoteVersion)) return;
|
||||
|
||||
ApplyState(UpdateUiState.Downloading);
|
||||
CancelDownloadButton.Visibility = Visibility.Visible;
|
||||
ProgressBar.Value = 0;
|
||||
ProgressText.Text = "正在准备下载...";
|
||||
|
||||
try
|
||||
{
|
||||
bool ok = await DownloadWithProgressAsync();
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
ApplyState(UpdateUiState.NetworkError, "更新下载失败,请检查网络连接后重试。");
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBoxResult result = MessageBox.Show(
|
||||
"更新已下载完成,点击确定后将关闭软件并安装新版本!",
|
||||
"安装更新",
|
||||
MessageBoxButton.OKCancel,
|
||||
MessageBoxImage.Information);
|
||||
|
||||
if (result == MessageBoxResult.OK)
|
||||
{
|
||||
App.IsAppExitByUser = true;
|
||||
AutoUpdateHelper.InstallNewVersionApp(_remoteVersion, true);
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyState(UpdateUiState.Downloaded);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"Error in UpdateNowButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
ApplyState(UpdateUiState.NetworkError, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async void UpdateLaterButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_remoteVersion)) return;
|
||||
|
||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||||
if (mainWindow == null) return;
|
||||
|
||||
ApplyState(UpdateUiState.Downloading);
|
||||
CancelDownloadButton.Visibility = Visibility.Visible;
|
||||
ProgressBar.Value = 0;
|
||||
ProgressText.Text = "正在后台下载...";
|
||||
|
||||
try
|
||||
{
|
||||
bool ok = await DownloadWithProgressAsync();
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
ApplyState(UpdateUiState.NetworkError, "更新下载失败,请检查网络连接后重试。");
|
||||
return;
|
||||
}
|
||||
|
||||
SettingsManager.Settings.Startup.IsAutoUpdate = true;
|
||||
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = true;
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
CardAutoUpdate.IsOn = true;
|
||||
CardSilentUpdate.IsOn = true;
|
||||
|
||||
mainWindow.StartSilentUpdateTimer();
|
||||
ApplyState(UpdateUiState.Downloaded);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"Error in UpdateLaterButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
ApplyState(UpdateUiState.NetworkError, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void SkipVersionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_remoteVersion)) return;
|
||||
|
||||
SettingsManager.Settings.Startup.SkippedVersion = _remoteVersion;
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
LogHelper.WriteLogToFile($"ManualUpdate | User chose to skip version {_remoteVersion}");
|
||||
|
||||
ApplyState(UpdateUiState.Idle, $"已跳过版本 {_remoteVersion}");
|
||||
}
|
||||
|
||||
private void CancelDownloadButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_downloadCancelled = true;
|
||||
AutoUpdateHelper.RequestCancelDownload();
|
||||
ApplyState(UpdateUiState.UpdateAvailable);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 历史版本回滚
|
||||
|
||||
private async void UpdateTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.OriginalSource != UpdateTabControl) return;
|
||||
if (UpdateTabControl.SelectedItem == HistoryTabItem && !_isHistoryLoaded)
|
||||
{
|
||||
await LoadHistoryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async void HistoryTabItem_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 兼容用户首次切到历史版本 Tab 时再加载
|
||||
if (_isHistoryLoaded) return;
|
||||
await LoadHistoryAsync();
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task LoadHistoryAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_isHistoryLoaded = true;
|
||||
ReleaseNotesViewer.Markdown = "正在获取历史版本...";
|
||||
RollbackButton.IsEnabled = false;
|
||||
|
||||
var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
_versionList = releases
|
||||
.Select(r => new VersionItem { Version = r.version, DownloadUrl = r.downloadUrl, ReleaseNotes = r.releaseNotes })
|
||||
.OrderByDescending(v => ParseVersionForSort(v.Version))
|
||||
.ToList();
|
||||
VersionComboBox.ItemsSource = _versionList;
|
||||
|
||||
if (_versionList.Count > 0)
|
||||
{
|
||||
VersionComboBox.SelectedIndex = 0;
|
||||
RollbackButton.IsEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ReleaseNotesViewer.Markdown = "未获取到历史版本信息。";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ReleaseNotesViewer.Markdown = $"加载历史版本失败:{ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private static Version ParseVersionForSort(string version)
|
||||
{
|
||||
var v = (version ?? "").TrimStart('v', 'V');
|
||||
return Version.TryParse(v, out var result) ? result : new Version(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private void VersionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
_selectedHistoricalItem = VersionComboBox.SelectedItem as VersionItem;
|
||||
if (_selectedHistoricalItem != null)
|
||||
{
|
||||
ReleaseNotesViewer.Markdown = _selectedHistoricalItem.ReleaseNotes ?? "无更新日志";
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择版本: {_selectedHistoricalItem.Version}");
|
||||
}
|
||||
Keyboard.ClearFocus();
|
||||
}
|
||||
|
||||
private async void RollbackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_selectedHistoricalItem == null) return;
|
||||
|
||||
int days = await AskPauseDaysAsync();
|
||||
if (days < 0)
|
||||
{
|
||||
LogHelper.WriteLogToFile("HistoryRollback | 用户取消了回滚操作");
|
||||
return;
|
||||
}
|
||||
|
||||
if (days == 0)
|
||||
{
|
||||
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
DateTime pauseUntilDate = DateTime.Now.AddDays(days);
|
||||
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = pauseUntilDate.ToString("yyyy-MM-dd");
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择暂停自动更新 {days} 天,截止日期: {pauseUntilDate:yyyy-MM-dd}");
|
||||
}
|
||||
MainWindow.SaveSettingsToFile();
|
||||
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户确认回滚,目标版本: {_selectedHistoricalItem.Version}");
|
||||
RollbackButton.IsEnabled = false;
|
||||
VersionComboBox.IsEnabled = false;
|
||||
RollbackProgressPanel.Visibility = Visibility.Visible;
|
||||
RollbackProgressBar.Value = 0;
|
||||
RollbackProgressText.Text = "正在准备下载...";
|
||||
|
||||
bool downloadSuccess = false;
|
||||
try
|
||||
{
|
||||
downloadSuccess = await AutoUpdateHelper.StartManualDownloadAndInstall(
|
||||
_selectedHistoricalItem.Version,
|
||||
SettingsManager.Settings.Startup.UpdateChannel,
|
||||
(percent, text) =>
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
RollbackProgressBar.Value = percent;
|
||||
RollbackProgressText.Text = text;
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RollbackProgressText.Text = $"下载失败: {ex.Message}";
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 下载异常: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
if (downloadSuccess)
|
||||
{
|
||||
RollbackProgressBar.Value = 100;
|
||||
RollbackProgressText.Text = "下载完成,准备安装...";
|
||||
}
|
||||
else
|
||||
{
|
||||
RollbackProgressText.Text = "下载失败,请检查网络后重试。";
|
||||
RollbackButton.IsEnabled = true;
|
||||
VersionComboBox.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task<int> AskPauseDaysAsync()
|
||||
{
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
Title = "暂停自动更新",
|
||||
PrimaryButtonText = "确定",
|
||||
SecondaryButtonText = "取消"
|
||||
};
|
||||
|
||||
var panel = new iNKORE.UI.WPF.Controls.SimpleStackPanel
|
||||
{
|
||||
Spacing = 16,
|
||||
Margin = new Thickness(0, 10, 0, 0)
|
||||
};
|
||||
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = "请选择在回滚后多久不再接收自动更新:",
|
||||
FontSize = 14
|
||||
};
|
||||
|
||||
var daysComboBox = new ComboBox
|
||||
{
|
||||
Width = 200,
|
||||
Height = 36,
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
};
|
||||
for (int i = 0; i <= 7; i++)
|
||||
{
|
||||
daysComboBox.Items.Add(new ComboBoxItem { Content = $"{i} 天", Tag = i });
|
||||
}
|
||||
daysComboBox.SelectedIndex = 0;
|
||||
|
||||
panel.Children.Add(textBlock);
|
||||
panel.Children.Add(daysComboBox);
|
||||
dialog.Content = panel;
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
if (result != ContentDialogResult.Primary) return -1;
|
||||
|
||||
if (daysComboBox.SelectedItem is ComboBoxItem cbi && cbi.Tag is int days)
|
||||
return days;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 维护
|
||||
|
||||
private async void FixVersionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var confirm = MessageBox.Show(
|
||||
@@ -421,49 +893,39 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
if (confirm == MessageBoxResult.Yes)
|
||||
if (confirm != MessageBoxResult.Yes) return;
|
||||
|
||||
FixVersionButton.IsEnabled = false;
|
||||
FixVersionButton.Content = "正在修复...";
|
||||
|
||||
try
|
||||
{
|
||||
FixVersionButton.IsEnabled = false;
|
||||
FixVersionButton.Content = "正在修复...";
|
||||
|
||||
try
|
||||
bool result = await AutoUpdateHelper.FixVersion(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
if (!result)
|
||||
{
|
||||
bool result = await AutoUpdateHelper.FixVersion(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"版本修复失败,可能是网络问题或当前已是最新版本。",
|
||||
"修复失败",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
|
||||
FixVersionButton.IsEnabled = true;
|
||||
FixVersionButton.Content = "版本修复";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"Error in FixVersionButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show(
|
||||
$"版本修复过程中发生错误: {ex.Message}",
|
||||
"修复错误",
|
||||
"版本修复失败,可能是网络问题或当前已是最新版本。",
|
||||
"修复失败",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
|
||||
FixVersionButton.IsEnabled = true;
|
||||
FixVersionButton.Content = "版本修复";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var win = new HistoryRollbackWindow(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
win.Owner = Application.Current.MainWindow;
|
||||
win.ShowDialog();
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"Error in FixVersionButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show(
|
||||
$"版本修复过程中发生错误: {ex.Message}",
|
||||
"修复错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
FixVersionButton.IsEnabled = true;
|
||||
FixVersionButton.Content = "版本修复";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user