Merge pull request #144 from InkCanvasForClass/beta

ICC CE 1.7.8.3
This commit is contained in:
CJK_mkp
2025-08-30 18:24:09 +08:00
committed by GitHub
22 changed files with 1264 additions and 382 deletions
+9
View File
@@ -89,6 +89,15 @@
"code",
"design"
]
},
{
"login": "Jursin",
"name": "Jursin",
"avatar_url": "https://avatars.githubusercontent.com/u/127487914?v=4",
"profile": "http://blog.jursin.top",
"contributions": [
"design"
]
}
]
}
+1 -1
View File
@@ -4,7 +4,7 @@
xmlns:local="clr-namespace:Ink_Canvas"
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
StartupUri="MainWindow.xaml">
>
<Application.Resources>
<ResourceDictionary>
<Style TargetType="ui:ScrollViewerEx">
+194 -16
View File
@@ -76,7 +76,12 @@ namespace Ink_Canvas
InitializeCrashListeners();
// 仅在崩溃后操作为静默重启时才启动看门狗
if (CrashAction == CrashActionType.SilentRestart)
// 在更新模式下不启动看门狗,避免干扰更新流程
args = Environment.GetCommandLineArgs();
bool isUpdateMode = args.Contains("--update-mode");
bool isFinalApp = args.Contains("--final-app");
if (CrashAction == CrashActionType.SilentRestart && !isUpdateMode && !isFinalApp)
{
StartWatchdogIfNeeded();
}
@@ -465,6 +470,16 @@ namespace Ink_Canvas
RootPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
LogHelper.NewLog(string.Format("Ink Canvas Starting (Version: {0})", Assembly.GetExecutingAssembly().GetName().Version));
// 检查是否为最终应用启动(更新后的应用)
bool isFinalApp = e.Args.Contains("--final-app");
bool skipMutexCheck = e.Args.Contains("--skip-mutex-check");
// 记录最终应用启动状态
if (isFinalApp)
{
LogHelper.WriteLogToFile("App | 检测到最终应用启动(更新后的应用)");
}
// 在应用启动时自动释放IACore相关DLL
try
@@ -482,32 +497,195 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile($"App | 使用频率: {DeviceIdentifier.GetUsageFrequency()}");
LogHelper.WriteLogToFile($"App | 更新优先级: {DeviceIdentifier.GetUpdatePriority()}");
bool ret;
mutex = new Mutex(true, "InkCanvasForClass CE", out ret);
if (!ret && !e.Args.Contains("-m")) //-m multiple
// 处理更新模式启动
bool isUpdateMode = AutoUpdateHelper.HandleUpdateModeStartup(e.Args);
// 如果是更新模式,不显示主窗口但保持应用运行
if (isUpdateMode)
{
LogHelper.WriteLogToFile("App | 检测到更新模式,跳过主窗口显示,保持应用运行");
return;
}
// 检查是否存在更新标记文件
string updateMarkerFile = Path.Combine(App.RootPath, "update_in_progress.tmp");
bool isUpdateInProgress = false;
// 检查是否以更新模式启动
isUpdateMode = e.Args.Contains("--update-mode");
// 如果是最终应用启动,立即清理更新标记文件
if (isFinalApp)
{
LogHelper.NewLog("Detected existing instance");
MessageBox.Show("已有一个程序实例正在运行");
LogHelper.NewLog("Ink Canvas automatically closed");
IsAppExitByUser = true; // 多开时标记为用户主动退出
// 写入退出信号,确保看门狗不会重启
try
{
StartupCount.Reset();
File.WriteAllText(watchdogExitSignalFile, "exit");
if (watchdogProcess != null && !watchdogProcess.HasExited)
if (File.Exists(updateMarkerFile))
{
watchdogProcess.Kill();
File.Delete(updateMarkerFile);
LogHelper.WriteLogToFile("App | 最终应用启动,清理更新标记文件");
}
}
catch { }
Environment.Exit(0);
catch (Exception ex)
{
LogHelper.WriteLogToFile($"App | 清理更新标记文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
// 如果不是最终应用启动,才检查更新标记文件
if (!isFinalApp && File.Exists(updateMarkerFile))
{
try
{
string updateProcessIdStr = File.ReadAllText(updateMarkerFile).Trim();
if (int.TryParse(updateProcessIdStr, out int updateProcessId))
{
LogHelper.WriteLogToFile($"App | 检测到更新标记文件,更新进程ID: {updateProcessId}");
// 检查更新进程是否还在运行
try
{
Process updateProcess = Process.GetProcessById(updateProcessId);
if (!updateProcess.HasExited)
{
LogHelper.WriteLogToFile("App | 更新进程仍在运行,等待更新完成");
isUpdateInProgress = true;
// 等待更新进程完成
int waitCount = 0;
const int maxWaitCount = 10; // 减少等待时间到10秒
while (waitCount < maxWaitCount && !updateProcess.HasExited)
{
Thread.Sleep(500); // 减少等待间隔到500ms
waitCount++;
LogHelper.WriteLogToFile($"App | 等待更新进程完成... ({waitCount}/{maxWaitCount})");
}
if (updateProcess.HasExited)
{
LogHelper.WriteLogToFile("App | 更新进程已结束");
}
else
{
LogHelper.WriteLogToFile("App | 等待更新进程超时,强制清理", LogHelper.LogType.Warning);
// 超时后强制清理标记文件
try
{
if (File.Exists(updateMarkerFile))
{
File.Delete(updateMarkerFile);
LogHelper.WriteLogToFile("App | 强制清理更新标记文件");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"App | 强制清理更新标记文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
}
else
{
LogHelper.WriteLogToFile("App | 更新进程已结束");
}
}
catch (ArgumentException)
{
LogHelper.WriteLogToFile("App | 更新进程已不存在");
}
// 无论更新进程是否还在运行,都清理标记文件
try
{
if (File.Exists(updateMarkerFile))
{
File.Delete(updateMarkerFile);
LogHelper.WriteLogToFile("App | 清理更新标记文件");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"App | 清理更新标记文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"App | 读取更新标记文件失败: {ex.Message}", LogHelper.LogType.Warning);
// 如果读取失败,也尝试删除标记文件
try
{
if (File.Exists(updateMarkerFile))
{
File.Delete(updateMarkerFile);
LogHelper.WriteLogToFile("App | 清理损坏的更新标记文件");
}
}
catch { }
}
}
// 如果是更新过程、更新模式、最终应用或跳过Mutex检查,跳过Mutex检查
if (!isUpdateInProgress && !isUpdateMode && !isFinalApp && !skipMutexCheck)
{
bool ret;
mutex = new Mutex(true, "InkCanvasForClass CE", out ret);
if (!ret && !e.Args.Contains("-m")) //-m multiple
{
LogHelper.NewLog("Detected existing instance");
MessageBox.Show("已有一个程序实例正在运行");
LogHelper.NewLog("Ink Canvas automatically closed");
IsAppExitByUser = true; // 多开时标记为用户主动退出
// 写入退出信号,确保看门狗不会重启
try
{
StartupCount.Reset();
File.WriteAllText(watchdogExitSignalFile, "exit");
if (watchdogProcess != null && !watchdogProcess.HasExited)
{
watchdogProcess.Kill();
}
}
catch { }
Environment.Exit(0);
}
}
else
{
if (isUpdateMode)
{
LogHelper.WriteLogToFile("App | 更新模式启动,跳过重复运行检测");
}
else if (isFinalApp)
{
LogHelper.WriteLogToFile("App | 最终应用启动,跳过重复运行检测");
}
else if (skipMutexCheck)
{
LogHelper.WriteLogToFile("App | 跳过Mutex检查模式启动,跳过重复运行检测");
}
else
{
LogHelper.WriteLogToFile("App | 更新过程中,跳过重复运行检测");
}
// 在特殊模式下,创建一个临时的Mutex以避免其他检查出错
string mutexName = isFinalApp ? "InkCanvasForClass CE Final" : "InkCanvasForClass CE Update";
mutex = new Mutex(true, mutexName, out bool tempRet);
// 额外等待一小段时间确保更新进程完全退出
Thread.Sleep(1000);
LogHelper.WriteLogToFile("App | 特殊模式等待完成,继续启动");
}
_taskbar = (TaskbarIcon)FindResource("TaskbarTrayIcon");
StartArgs = e.Args;
// 在非更新模式下创建主窗口
var mainWindow = new MainWindow();
MainWindow = mainWindow;
mainWindow.Show();
// 新增:Office注册表检测
try
+2 -2
View File
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.8.0")]
[assembly: AssemblyFileVersion("1.7.8.0")]
[assembly: AssemblyVersion("1.7.8.3")]
[assembly: AssemblyFileVersion("1.7.8.3")]
+526 -103
View File
@@ -1,4 +1,4 @@
using Newtonsoft.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
@@ -28,6 +28,9 @@ namespace Ink_Canvas.Helpers
private static readonly string updatesFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoUpdate");
private static string statusFilePath;
// GitHub Token认证
private static readonly string GitHubToken = "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";
// 线路组结构体(包含版本、下载、日志地址)
public class UpdateLineGroup
{
@@ -45,9 +48,9 @@ namespace Ink_Canvas.Helpers
new UpdateLineGroup
{
GroupName = "GitHub主线",
VersionUrl = "https://bgithub.xyz/InkCanvasForClass/community/raw/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://bgithub.xyz/InkCanvasForClass/community/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community/raw/refs/heads/main/UpdateLog.md"
VersionUrl = "https://github.com/InkCanvasForClass/community/raw/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://github.com/InkCanvasForClass/community/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://github.com/InkCanvasForClass/community/raw/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
@@ -67,11 +70,13 @@ namespace Ink_Canvas.Helpers
{
GroupName = "智教联盟",
DownloadUrlFormat = "https://get.smart-teach.cn/d/Ningbo-S3/shared/jiangling/community/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community/raw/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
GroupName = "inkeys",
DownloadUrlFormat = "https://iccce.inkeys.top/Release/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community/raw/refs/heads/main/UpdateLog.md"
}
}
},
@@ -80,9 +85,9 @@ namespace Ink_Canvas.Helpers
new UpdateLineGroup
{
GroupName = "GitHub主线",
VersionUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://bgithub.xyz/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
VersionUrl = "https://github.com/InkCanvasForClass/community-beta/raw/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://github.com/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://github.com/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
@@ -102,11 +107,13 @@ namespace Ink_Canvas.Helpers
{
GroupName = "智教联盟",
DownloadUrlFormat = "https://get.smart-teach.cn/d/Ningbo-S3/shared/jiangling/community-beta/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
GroupName = "inkeys",
DownloadUrlFormat = "https://iccce.inkeys.top/Beta/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
}
}
}
@@ -409,6 +416,8 @@ namespace Ink_Canvas.Helpers
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
client.DefaultRequestHeaders.Add("Authorization", $"token {GitHubToken}");
LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub Token进行API调用");
var response = await client.GetStringAsync(apiUrl);
var releases = JArray.Parse(response);
@@ -450,6 +459,8 @@ namespace Ink_Canvas.Helpers
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
client.DefaultRequestHeaders.Add("Authorization", $"token {GitHubToken}");
LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub Token进行API调用");
var response = await client.GetStringAsync(apiUrl);
var json = JObject.Parse(response);
string version = json["tag_name"]?.ToString();
@@ -1130,7 +1141,7 @@ namespace Ink_Canvas.Helpers
}
}
// 安装新版本应用
// 安装新版本应用 - 优化版本,不使用命令行
public static void InstallNewVersionApp(string version, bool isInSilence)
{
try
@@ -1183,113 +1194,109 @@ namespace Ink_Canvas.Helpers
LogHelper.WriteLogToFile($"AutoUpdate | ZIP文件大小: {fileInfo.Length} 字节");
string currentAppDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
int currentProcessId = Process.GetCurrentProcess().Id;
string appPath = Assembly.GetExecutingAssembly().Location;
int currentProcessId = Process.GetCurrentProcess().Id;
LogHelper.WriteLogToFile($"AutoUpdate | 当前应用程序目录: {currentAppDir}");
LogHelper.WriteLogToFile($"AutoUpdate | 当前进程ID: {currentProcessId}");
LogHelper.WriteLogToFile($"AutoUpdate | 静默更新模式: {isInSilence}");
string batchFilePath = Path.Combine(Path.GetTempPath(), "UpdateICC_" + Guid.NewGuid().ToString().Substring(0, 8) + ".bat");
LogHelper.WriteLogToFile($"AutoUpdate | 创建更新批处理文件: {batchFilePath}");
StringBuilder batchContent = new StringBuilder();
batchContent.AppendLine("@echo off");
batchContent.AppendLine("echo Set objShell = CreateObject(\"WScript.Shell\") > \"%temp%\\hideme.vbs\"");
batchContent.AppendLine("echo objShell.Run \"cmd /c \"\"\" ^& WScript.Arguments(0) ^& \"\"\"\", 0, True >> \"%temp%\\hideme.vbs\"");
batchContent.AppendLine("echo Wscript.Sleep 100 >> \"%temp%\\hideme.vbs\"");
string updateBatPath = Path.Combine(Path.GetTempPath(), "ICCUpdate_" + Guid.NewGuid().ToString().Substring(0, 8) + ".bat");
batchContent.AppendLine($"echo @echo off > \"{updateBatPath}\"");
batchContent.AppendLine($"echo set PROC_ID={currentProcessId} >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo :CHECK_PROCESS >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo tasklist /fi \"PID eq %PROC_ID%\" ^| find \"%PROC_ID%\" ^> nul >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo if %%ERRORLEVEL%% == 0 ( >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo timeout /t 1 /nobreak ^> nul >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo goto CHECK_PROCESS >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo ) >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo timeout /t 1 /nobreak > nul >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo echo Application closed, starting update process... >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo timeout /t 2 /nobreak ^> nul >> \"{updateBatPath}\"");
// 创建解压目录
string extractPath = Path.Combine(updatesFolderPath, $"Extract_{version}");
batchContent.AppendLine($"echo echo Extracting update files... >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo if exist \"{extractPath}\" rd /s /q \"{extractPath}\" >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo mkdir \"{extractPath}\" >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo powershell -command \"Expand-Archive -Path '{zipFilePath.Replace("'", "''")}' -DestinationPath '{extractPath.Replace("'", "''")}' -Force\" >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo if %%ERRORLEVEL%% neq 0 ( >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo goto ERROR_EXIT >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo ) >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo echo Copying updated files to application directory... >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo xcopy /s /y /e \"{extractPath}\\*\" \"{currentAppDir}\\\" >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo if %%ERRORLEVEL%% neq 0 ( >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo goto ERROR_EXIT >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo ) >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo echo Cleaning up temporary files... >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo if exist \"{extractPath}\" rd /s /q \"{extractPath}\" >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo if exist \"{zipFilePath}\" del /f /q \"{zipFilePath}\" >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo echo Update completed successfully! >> \"{updateBatPath}\"");
if (isInSilence)
if (Directory.Exists(extractPath))
{
batchContent.AppendLine($"echo echo 自动启动应用程序... >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo start \"\" \"{appPath}\" >> \"{updateBatPath}\"");
}
else
{
batchContent.AppendLine($"echo taskkill /F /IM \"InkCanvasForClass.exe\" >nul 2>nul >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo :: 检查应用程序是否已经在运行 >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo tasklist /FI \"IMAGENAME eq InkCanvasForClass.exe\" | find /i \"InkCanvasForClass.exe\" > nul >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo if %%ERRORLEVEL%% neq 0 ( >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo echo 启动应用程序... >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo start \"\" \"{appPath}\" >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo ) else ( >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo echo 应用程序已经在运行,不再重复启动 >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo ) >> \"{updateBatPath}\"");
try
{
Directory.Delete(extractPath, true);
LogHelper.WriteLogToFile($"AutoUpdate | 清理已存在的解压目录: {extractPath}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 清理解压目录失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
batchContent.AppendLine($"echo exit /b 0 >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo goto EXIT >> \"{updateBatPath}\"");
if (isInSilence)
try
{
batchContent.AppendLine($"echo :ERROR_EXIT >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo echo Update failed! >> \"%temp%\\icc_update_error.log\" >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo exit /b 1 >> \"{updateBatPath}\"");
Directory.CreateDirectory(extractPath);
LogHelper.WriteLogToFile($"AutoUpdate | 创建解压目录: {extractPath}");
}
else
catch (Exception ex)
{
batchContent.AppendLine($"echo :ERROR_EXIT >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo start \"\" cmd /c \"echo Update failed! ^& pause\" >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo exit /b 1 >> \"{updateBatPath}\"");
LogHelper.WriteLogToFile($"AutoUpdate | 创建解压目录失败: {ex.Message}", LogHelper.LogType.Error);
return;
}
batchContent.AppendLine($"echo :EXIT >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo del \"{updateBatPath}\" >> \"{updateBatPath}\"");
batchContent.AppendLine($"echo exit >> \"{updateBatPath}\"");
batchContent.AppendLine($"wscript \"%temp%\\hideme.vbs\" \"{updateBatPath}\"");
batchContent.AppendLine("del \"%temp%\\hideme.vbs\"");
batchContent.AppendLine("exit");
File.WriteAllText(batchFilePath, batchContent.ToString());
LogHelper.WriteLogToFile("AutoUpdate | 创建更新批处理文件完成");
Process.Start(new ProcessStartInfo
// 解压ZIP文件
try
{
FileName = batchFilePath,
CreateNoWindow = true,
UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Hidden
});
LogHelper.WriteLogToFile($"AutoUpdate | 开始解压ZIP文件到: {extractPath}");
ZipFile.ExtractToDirectory(zipFilePath, extractPath);
LogHelper.WriteLogToFile("AutoUpdate | ZIP文件解压完成");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 解压ZIP文件失败: {ex.Message}", LogHelper.LogType.Error);
return;
}
LogHelper.WriteLogToFile("AutoUpdate | 启动更新批处理进程(隐藏窗口)");
// 查找解压后的主程序文件
string newAppPath = null;
string[] possibleExeNames = { "InkCanvasForClass.exe", "Ink Canvas.exe", "InkCanvas.exe" };
foreach (string exeName in possibleExeNames)
{
string testPath = Path.Combine(extractPath, exeName);
if (File.Exists(testPath))
{
newAppPath = testPath;
LogHelper.WriteLogToFile($"AutoUpdate | 找到新版本主程序: {newAppPath}");
break;
}
}
if (string.IsNullOrEmpty(newAppPath))
{
LogHelper.WriteLogToFile("AutoUpdate | 在解压目录中未找到主程序文件", LogHelper.LogType.Error);
return;
}
// 启动新版本进程
try
{
LogHelper.WriteLogToFile($"AutoUpdate | 准备启动新版本进程: {newAppPath}");
// 启动新版本进程(以更新模式)
string arguments = $"--update-mode --old-process-id={currentProcessId} --extract-path=\"{extractPath}\" --target-path=\"{currentAppDir}\" --is-silence={isInSilence}";
LogHelper.WriteLogToFile($"AutoUpdate | 启动新进程的命令行: {newAppPath} {arguments}");
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = newAppPath,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = false
};
Process.Start(startInfo);
LogHelper.WriteLogToFile("AutoUpdate | 新版本进程启动命令已执行");
// 等待一小段时间确保新进程启动
Thread.Sleep(2000);
// 关闭当前旧软件进程
LogHelper.WriteLogToFile("AutoUpdate | 关闭当前旧软件进程");
App.IsAppExitByUser = true;
Application.Current.Dispatcher.Invoke(() =>
{
Application.Current.Shutdown();
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 启动新版本进程时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
catch (Exception ex)
{
@@ -1301,6 +1308,421 @@ namespace Ink_Canvas.Helpers
}
}
// 处理更新模式的启动参数
public static bool HandleUpdateModeStartup(string[] args)
{
try
{
// 检查是否以更新模式启动
if (args.Contains("--update-mode"))
{
LogHelper.WriteLogToFile("AutoUpdate | 检测到更新模式启动");
// 解析命令行参数
int oldProcessId = -1;
string extractPath = null;
string targetPath = null;
bool isSilence = false;
// 记录所有参数用于调试
LogHelper.WriteLogToFile($"AutoUpdate | 接收到的命令行参数: {string.Join(" ", args)}");
for (int i = 0; i < args.Length; i++)
{
string arg = args[i];
LogHelper.WriteLogToFile($"AutoUpdate | 处理参数 {i}: {arg}");
if (arg.StartsWith("--old-process-id="))
{
string processIdStr = arg.Substring("--old-process-id=".Length);
if (int.TryParse(processIdStr, out int pid))
{
oldProcessId = pid;
LogHelper.WriteLogToFile($"AutoUpdate | 解析到老进程ID: {oldProcessId}");
}
}
else if (arg.StartsWith("--extract-path="))
{
extractPath = arg.Substring("--extract-path=".Length).Trim('"');
LogHelper.WriteLogToFile($"AutoUpdate | 解析到解压路径: {extractPath}");
}
else if (arg.StartsWith("--target-path="))
{
targetPath = arg.Substring("--target-path=".Length).Trim('"');
LogHelper.WriteLogToFile($"AutoUpdate | 解析到目标路径: {targetPath}");
}
else if (arg.StartsWith("--is-silence="))
{
string silenceStr = arg.Substring("--is-silence=".Length);
if (bool.TryParse(silenceStr, out bool silence))
{
isSilence = silence;
LogHelper.WriteLogToFile($"AutoUpdate | 解析到静默模式: {isSilence}");
}
}
}
LogHelper.WriteLogToFile($"AutoUpdate | 更新参数 - 老进程ID: {oldProcessId}, 解压路径: {extractPath}, 目标路径: {targetPath}, 静默模式: {isSilence}");
if (oldProcessId > 0 && !string.IsNullOrEmpty(extractPath) && !string.IsNullOrEmpty(targetPath))
{
LogHelper.WriteLogToFile($"AutoUpdate | 参数验证通过,启动更新任务");
// 启动更新任务
Task.Run(async () => await PerformUpdate(oldProcessId, extractPath, targetPath, isSilence));
return true; // 返回true表示是更新模式
}
else
{
LogHelper.WriteLogToFile($"AutoUpdate | 参数验证失败 - 老进程ID: {oldProcessId}, 解压路径: {extractPath}, 目标路径: {targetPath}", LogHelper.LogType.Error);
return false;
}
}
return false; // 返回false表示不是更新模式
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 处理更新模式启动时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
// 执行实际的更新操作
private static async Task PerformUpdate(int oldProcessId, string extractPath, string targetPath, bool isSilence)
{
try
{
LogHelper.WriteLogToFile("AutoUpdate | 开始执行更新操作");
// 等待老进程完全退出
LogHelper.WriteLogToFile($"AutoUpdate | 等待老进程 {oldProcessId} 退出");
int waitCount = 0;
const int maxWaitCount = 30; // 最多等待30秒
while (waitCount < maxWaitCount)
{
try
{
Process oldProcess = Process.GetProcessById(oldProcessId);
if (oldProcess.HasExited)
{
LogHelper.WriteLogToFile("AutoUpdate | 老进程已退出");
break;
}
LogHelper.WriteLogToFile($"AutoUpdate | 老进程仍在运行,等待中... ({waitCount + 1}/{maxWaitCount})");
Thread.Sleep(1000);
waitCount++;
}
catch (ArgumentException)
{
// 进程不存在,说明已经退出
LogHelper.WriteLogToFile("AutoUpdate | 老进程已退出(进程不存在)");
break;
}
}
if (waitCount >= maxWaitCount)
{
LogHelper.WriteLogToFile("AutoUpdate | 等待老进程退出超时,尝试强制结束", LogHelper.LogType.Warning);
try
{
Process oldProcess = Process.GetProcessById(oldProcessId);
oldProcess.Kill();
Thread.Sleep(2000); // 等待进程完全结束
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 强制结束老进程失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
// 确保目标目录存在
if (!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
LogHelper.WriteLogToFile($"AutoUpdate | 创建目标目录: {targetPath}");
}
// 复制文件到目标目录
LogHelper.WriteLogToFile($"AutoUpdate | 开始复制文件从 {extractPath} 到 {targetPath}");
try
{
// 使用递归复制方法,支持重试机制
bool copySuccess = await CopyDirectoryWithRetryAsync(extractPath, targetPath);
if (copySuccess)
{
LogHelper.WriteLogToFile("AutoUpdate | 文件复制完成");
}
else
{
LogHelper.WriteLogToFile("AutoUpdate | 文件复制失败,部分文件可能无法覆盖", LogHelper.LogType.Error);
if (!isSilence)
{
MessageBox.Show("更新失败:部分文件无法覆盖,可能是文件正在使用中。\n请关闭所有相关程序后重试。", "更新失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
return;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 文件复制失败: {ex.Message}", LogHelper.LogType.Error);
if (!isSilence)
{
MessageBox.Show($"更新失败:文件复制时出错\n{ex.Message}", "更新失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
return;
}
// 清理临时文件
try
{
LogHelper.WriteLogToFile("AutoUpdate | 清理临时文件");
// 删除解压目录
if (Directory.Exists(extractPath))
{
Directory.Delete(extractPath, true);
LogHelper.WriteLogToFile($"AutoUpdate | 删除解压目录: {extractPath}");
}
// 删除ZIP文件
string zipFile = Path.Combine(updatesFolderPath, $"InkCanvasForClass.CE.*.zip");
string[] zipFiles = Directory.GetFiles(updatesFolderPath, "InkCanvasForClass.CE.*.zip");
foreach (string zip in zipFiles)
{
try
{
File.Delete(zip);
LogHelper.WriteLogToFile($"AutoUpdate | 删除ZIP文件: {zip}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 删除ZIP文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 清理临时文件时出错: {ex.Message}", LogHelper.LogType.Warning);
}
LogHelper.WriteLogToFile("AutoUpdate | 更新操作完成");
// 启动更新后的应用程序
string newAppPath = Path.Combine(targetPath, "InkCanvasForClass.exe");
if (File.Exists(newAppPath))
{
try
{
LogHelper.WriteLogToFile($"AutoUpdate | 准备启动更新后的应用程序: {newAppPath}");
// 获取当前更新进程ID
int currentUpdateProcessId = Process.GetCurrentProcess().Id;
LogHelper.WriteLogToFile($"AutoUpdate | 当前更新进程ID: {currentUpdateProcessId}");
// 创建一个临时标记文件,用于新进程检测更新状态
string updateMarkerFile = Path.Combine(targetPath, "update_in_progress.tmp");
File.WriteAllText(updateMarkerFile, currentUpdateProcessId.ToString());
LogHelper.WriteLogToFile($"AutoUpdate | 创建更新标记文件: {updateMarkerFile}");
// 启动更新后的应用程序(标记为最终应用,不受相同进程影响)
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = newAppPath,
Arguments = "--final-app --skip-mutex-check",
WorkingDirectory = targetPath,
UseShellExecute = false
};
Process newProcess = Process.Start(startInfo);
LogHelper.WriteLogToFile($"AutoUpdate | 最终应用程序启动成功,PID: {newProcess?.Id},已标记为最终应用");
// 等待一小段时间确保最终应用程序启动
Thread.Sleep(2000);
// 结束当前更新进程
LogHelper.WriteLogToFile("AutoUpdate | 更新流程完成,结束更新进程");
// 强制结束当前更新进程
try
{
LogHelper.WriteLogToFile("AutoUpdate | 强制结束更新进程");
// 标记为应用主动退出,避免看门狗重启
App.IsAppExitByUser = true;
// 写入退出信号文件,确保看门狗不会重启
try
{
string watchdogExitSignalFile = Path.Combine(Path.GetTempPath(), "icc_watchdog_exit_" + Process.GetCurrentProcess().Id + ".flag");
File.WriteAllText(watchdogExitSignalFile, "exit");
LogHelper.WriteLogToFile("AutoUpdate | 已写入看门狗退出信号文件");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 写入看门狗退出信号文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
Environment.Exit(0);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 结束当前更新进程失败: {ex.Message}", LogHelper.LogType.Error);
Environment.Exit(0);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 启动更新后的应用程序失败: {ex.Message}", LogHelper.LogType.Error);
if (!isSilence)
{
MessageBox.Show($"更新完成,但启动应用程序失败:{ex.Message}\n请手动启动应用程序。", "启动失败", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
}
else
{
LogHelper.WriteLogToFile($"AutoUpdate | 更新后的应用程序文件不存在: {newAppPath}", LogHelper.LogType.Error);
if (!isSilence)
{
MessageBox.Show($"更新完成,但未找到应用程序文件:{newAppPath}\n请检查更新是否成功。", "文件缺失", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 执行更新操作时出错: {ex.Message}", LogHelper.LogType.Error);
if (!isSilence)
{
MessageBox.Show($"更新失败:{ex.Message}", "更新失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
// 异步复制目录的辅助方法(带重试机制)
private static async Task<bool> CopyDirectoryWithRetryAsync(string sourceDir, string destinationDir)
{
var dir = new DirectoryInfo(sourceDir);
DirectoryInfo[] dirs = dir.GetDirectories();
bool allFilesCopied = true;
// 如果目标目录不存在,则创建它
if (!Directory.Exists(destinationDir))
{
Directory.CreateDirectory(destinationDir);
}
// 复制文件
foreach (FileInfo file in dir.GetFiles())
{
string targetFilePath = Path.Combine(destinationDir, file.Name);
bool fileCopied = false;
// 重试机制,最多重试3次
for (int retry = 0; retry < 3; retry++)
{
try
{
// 如果目标文件存在,先尝试删除
if (File.Exists(targetFilePath))
{
try
{
File.Delete(targetFilePath);
}
catch (IOException)
{
// 文件可能正在使用,等待一下再重试
if (retry < 2)
{
Thread.Sleep(1000);
continue;
}
}
}
await Task.Run(() => file.CopyTo(targetFilePath));
fileCopied = true;
break;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 复制文件失败 (重试 {retry + 1}/3) {file.FullName} -> {targetFilePath}: {ex.Message}", LogHelper.LogType.Warning);
if (retry < 2)
{
Thread.Sleep(1000); // 等待1秒后重试
}
}
}
if (!fileCopied)
{
allFilesCopied = false;
LogHelper.WriteLogToFile($"AutoUpdate | 文件复制最终失败: {file.FullName}", LogHelper.LogType.Error);
}
}
// 递归复制子目录
foreach (DirectoryInfo subDir in dirs)
{
string newDestinationDir = Path.Combine(destinationDir, subDir.Name);
bool subDirCopied = await CopyDirectoryWithRetryAsync(subDir.FullName, newDestinationDir);
if (!subDirCopied)
{
allFilesCopied = false;
}
}
return allFilesCopied;
}
// 异步复制目录的辅助方法(原版本,保留兼容性)
private static async Task CopyDirectoryAsync(string sourceDir, string destinationDir)
{
var dir = new DirectoryInfo(sourceDir);
DirectoryInfo[] dirs = dir.GetDirectories();
// 如果目标目录不存在,则创建它
if (!Directory.Exists(destinationDir))
{
Directory.CreateDirectory(destinationDir);
}
// 复制文件
foreach (FileInfo file in dir.GetFiles())
{
string targetFilePath = Path.Combine(destinationDir, file.Name);
try
{
// 如果目标文件存在且正在使用,先删除
if (File.Exists(targetFilePath))
{
File.Delete(targetFilePath);
}
await Task.Run(() => file.CopyTo(targetFilePath));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 复制文件失败 {file.FullName} -> {targetFilePath}: {ex.Message}", LogHelper.LogType.Warning);
// 继续复制其他文件,不中断整个过程
}
}
// 递归复制子目录
foreach (DirectoryInfo subDir in dirs)
{
string newDestinationDir = Path.Combine(destinationDir, subDir.Name);
await CopyDirectoryAsync(subDir.FullName, newDestinationDir);
}
}
// 获取远程内容的通用方法
public static async Task<string> GetRemoteContent(string fileUrl)
{
@@ -1450,8 +1872,8 @@ namespace Ink_Canvas.Helpers
return false;
}
// 执行安装,静默模式
InstallNewVersionApp(remoteVersion, false);
// 执行安装,静默模式
InstallNewVersionApp(remoteVersion, true);
App.IsAppExitByUser = true;
Application.Current.Dispatcher.Invoke(() =>
{
@@ -1478,6 +1900,8 @@ namespace Ink_Canvas.Helpers
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
client.DefaultRequestHeaders.Add("Authorization", $"token {GitHubToken}");
LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub Token进行API调用");
var response = await client.GetStringAsync(apiUrl);
var arr = JArray.Parse(response);
foreach (var item in arr)
@@ -1515,7 +1939,7 @@ namespace Ink_Canvas.Helpers
LogHelper.WriteLogToFile("AutoUpdate | 开始测试Windows 7 TLS连接...");
// 测试GitHub连接
var testUrl = "https://bgithub.xyz/InkCanvasForClass/community/raw/refs/heads/main/AutomaticUpdateVersionControl.txt";
var testUrl = "https://github.com/InkCanvasForClass/community/raw/refs/heads/main/AutomaticUpdateVersionControl.txt";
using (var handler = new HttpClientHandler())
{
@@ -1580,7 +2004,7 @@ namespace Ink_Canvas.Helpers
return false;
}
LogHelper.WriteLogToFile($"AutoUpdate | 手动安装版本: {version}");
InstallNewVersionApp(version, false);
InstallNewVersionApp(version, true);
App.IsAppExitByUser = true;
Application.Current.Dispatcher.Invoke(() =>
{
@@ -1631,4 +2055,3 @@ namespace Ink_Canvas.Helpers
}
}
}
+67 -54
View File
@@ -17,7 +17,7 @@ namespace Ink_Canvas.Helpers
private readonly Dictionary<string, HotkeyInfo> _registeredHotkeys;
private readonly MainWindow _mainWindow;
private bool _isDisposed = false;
private bool _hotkeysShouldBeRegistered = false; // 启动时注册热键,等待需要时再注册
private bool _hotkeysShouldBeRegistered = true; // 启动时注册热键
// 配置文件路径
private static readonly string HotkeyConfigFile = Path.Combine(App.RootPath, "HotkeyConfig.json");
@@ -28,7 +28,7 @@ namespace Ink_Canvas.Helpers
{
_mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow));
_registeredHotkeys = new Dictionary<string, HotkeyInfo>();
_hotkeysShouldBeRegistered = false; // 启动时注册热键,等待需要时再注册
_hotkeysShouldBeRegistered = true; // 启动时注册热键
}
#endregion
@@ -288,7 +288,7 @@ namespace Ink_Canvas.Helpers
}
else
{
// 如果配置文件不存在,使用默认快捷键
// 如果配置文件不存在或加载失败,使用默认快捷键
if (!File.Exists(HotkeyConfigFile))
{
LogHelper.WriteLogToFile("配置文件不存在,注册默认快捷键", LogHelper.LogType.Info);
@@ -297,7 +297,9 @@ namespace Ink_Canvas.Helpers
}
else
{
LogHelper.WriteLogToFile("配置文件存在但加载失败,保持当前状态", LogHelper.LogType.Warning);
LogHelper.WriteLogToFile("配置文件存在但加载失败,回退到默认快捷键", LogHelper.LogType.Warning);
RegisterDefaultHotkeys();
_hotkeysShouldBeRegistered = true;
}
}
}
@@ -350,7 +352,9 @@ namespace Ink_Canvas.Helpers
}
else
{
LogHelper.WriteLogToFile("快捷键注册功能已经启用", LogHelper.LogType.Info);
LogHelper.WriteLogToFile("快捷键注册功能已经启用,重新加载快捷键设置", LogHelper.LogType.Info);
// 即使已经启用,也要重新加载快捷键设置以确保快捷键正常工作
LoadHotkeysFromSettings();
}
}
catch (Exception ex)
@@ -359,6 +363,61 @@ namespace Ink_Canvas.Helpers
}
}
/// <summary>
/// 禁用快捷键注册功能
/// 调用此方法后,快捷键将被注销
/// </summary>
public void DisableHotkeyRegistration()
{
try
{
if (_hotkeysShouldBeRegistered)
{
_hotkeysShouldBeRegistered = false;
LogHelper.WriteLogToFile("禁用快捷键注册功能", LogHelper.LogType.Info);
// 注销所有快捷键
UnregisterAllHotkeys();
}
else
{
LogHelper.WriteLogToFile("快捷键注册功能已经禁用", LogHelper.LogType.Info);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"禁用快捷键注册功能时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 根据当前工具模式更新快捷键状态
/// 在工具切换时调用此方法
/// </summary>
/// <param name="isMouseMode">是否为鼠标模式(选择模式)</param>
public void UpdateHotkeyStateForToolMode(bool isMouseMode)
{
try
{
if (isMouseMode)
{
// 鼠标模式下禁用快捷键,让键盘操作放行
DisableHotkeyRegistration();
LogHelper.WriteLogToFile("切换到鼠标模式,禁用快捷键以放行键盘操作", LogHelper.LogType.Info);
}
else
{
// 非鼠标模式下启用快捷键
EnableHotkeyRegistration();
LogHelper.WriteLogToFile("切换到非鼠标模式,启用快捷键", LogHelper.LogType.Info);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新快捷键状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新快捷键配置
/// </summary>
@@ -752,53 +811,7 @@ namespace Ink_Canvas.Helpers
}
}
/// <summary>
/// 动态管理快捷键注册状态
/// 根据当前工具选择状态自动注册或注销快捷键
/// </summary>
public void UpdateHotkeyRegistrationState()
{
try
{
bool isMouseMode = IsInSelectMode();
if (isMouseMode)
{
// 在鼠标模式下,注销所有快捷键以释放系统快捷键
if (_hotkeysShouldBeRegistered)
{
UnregisterAllHotkeys();
_hotkeysShouldBeRegistered = false;
}
else
{
// 快捷键已经处于注销状态,无需重复注销
}
}
else
{
// 在批注/选择/其他工具模式下,重新注册所有快捷键
if (!_hotkeysShouldBeRegistered)
{
// 第一次切换到批注/选择/其他工具模式,启用快捷键注册
EnableHotkeyRegistration();
}
else if (_registeredHotkeys.Count == 0)
{
// 快捷键已启用但数量为0,重新注册
LoadHotkeysFromSettings();
}
else
{
// 当前已有快捷键注册,无需重新注册
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新快捷键注册状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region IDisposable Implementation
@@ -806,7 +819,7 @@ namespace Ink_Canvas.Helpers
{
if (!_isDisposed)
{
UnregisterAllHotkeys();
_isDisposed = true;
}
}
@@ -851,4 +864,4 @@ namespace Ink_Canvas.Helpers
}
#endregion
}
}
}
+16 -71
View File
@@ -16,7 +16,6 @@
ShowInTaskbar="False"
Title="InkCanvasforClass"
Topmost="True"
KeyDown="Window_KeyDown"
Closing="Window_Closing"
Closed="Window_Closed"
PreviewKeyDown="Main_Grid_PreviewKeyDown"
@@ -43,25 +42,6 @@
<c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<c:IntNumberToString x:Key="IntNumberToString" />
<c:IntNumberToString2 x:Key="IntNumberToString2" />
<RoutedUICommand x:Key="KeyExit" Text=" " />
<RoutedUICommand x:Key="HotKey_Command_Undo" Text=" " />
<RoutedUICommand x:Key="HotKey_Command_Redo" Text=" " />
<RoutedUICommand x:Key="HotKey_Command_Clear" Text=" " />
<RoutedUICommand x:Key="HotKey_Capture" Text=" " />
<RoutedUICommand x:Key="HotKey_Hide" Text=" " />
<RoutedUICommand x:Key="HotKey_ChangeToDrawTool" Text=" " />
<RoutedUICommand x:Key="HotKey_ChangeToQuitDrawTool" Text=" " />
<RoutedUICommand x:Key="HotKey_ChangeToSelect" Text=" " />
<RoutedUICommand x:Key="HotKey_ChangeToEraser" Text=" " />
<RoutedUICommand x:Key="HotKey_ChangeToBoard" Text=" " />
<RoutedUICommand x:Key="HotKey_ChangeToPen1" Text=" " />
<RoutedUICommand x:Key="HotKey_ChangeToPen2" Text=" " />
<RoutedUICommand x:Key="HotKey_ChangeToPen3" Text=" " />
<RoutedUICommand x:Key="HotKey_ChangeToPen4" Text=" " />
<RoutedUICommand x:Key="HotKey_ChangeToPen5" Text=" " />
<RoutedUICommand x:Key="HotKey_DrawLine" Text=" " />
<RoutedUICommand x:Key="HotKey_Paste" Text=" " />
<RoutedUICommand x:Key="NothingWillHappened" Text=" " />
<!-- Navigation Button Style -->
<Style x:Key="NavButton" TargetType="Button">
@@ -130,56 +110,6 @@
</Style>
</ResourceDictionary>
</Window.Resources>
<!--输入命令绑定-->
<Window.InputBindings>
<KeyBinding Gesture="Escape" Command="{StaticResource KeyExit}" />
<KeyBinding Modifiers="Control" Key="Z" Command="{StaticResource HotKey_Command_Undo}" />
<KeyBinding Modifiers="Control" Key="Y" Command="{StaticResource HotKey_Command_Redo}" />
<KeyBinding Modifiers="Control" Key="E" Command="{StaticResource HotKey_Command_Clear}" />
<KeyBinding Modifiers="Alt" Key="C" Command="{StaticResource HotKey_Capture}" />
<KeyBinding Modifiers="Alt" Key="V" Command="{StaticResource HotKey_Hide}" />
<KeyBinding Modifiers="Alt" Key="S" Command="{StaticResource HotKey_ChangeToSelect}" />
<KeyBinding Modifiers="Alt" Key="D" Command="{StaticResource HotKey_ChangeToDrawTool}" />
<KeyBinding Modifiers="Alt" Key="Q" Command="{StaticResource HotKey_ChangeToQuitDrawTool}" />
<KeyBinding Modifiers="Alt" Key="E" Command="{StaticResource HotKey_ChangeToEraser}" />
<KeyBinding Modifiers="Alt" Key="B" Command="{StaticResource HotKey_ChangeToBoard}" />
<KeyBinding Modifiers="Alt" Key="D1" Command="{StaticResource HotKey_ChangeToPen1}" />
<KeyBinding Modifiers="Alt" Key="D2" Command="{StaticResource HotKey_ChangeToPen2}" />
<KeyBinding Modifiers="Alt" Key="D3" Command="{StaticResource HotKey_ChangeToPen3}" />
<KeyBinding Modifiers="Alt" Key="D4" Command="{StaticResource HotKey_ChangeToPen4}" />
<KeyBinding Modifiers="Alt" Key="D5" Command="{StaticResource HotKey_ChangeToPen5}" />
<KeyBinding Modifiers="Alt" Key="L" Command="{StaticResource HotKey_DrawLine}" />
<KeyBinding Modifiers="Control" Key="V" Command="{StaticResource HotKey_Paste}" />
<KeyBinding Modifiers="Alt" Key="F4" Command="{StaticResource NothingWillHappened}" />
</Window.InputBindings>
<!--命令执行方法绑定-->
<Window.CommandBindings>
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource KeyExit}" Executed="KeyExit" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_Command_Undo}"
Executed="HotKey_Undo" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_Command_Redo}"
Executed="HotKey_Redo" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_Command_Clear}"
Executed="HotKey_Clear" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_ChangeToDrawTool}"
Executed="KeyChangeToDrawTool" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_ChangeToQuitDrawTool}"
Executed="KeyChangeToQuitDrawTool" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_ChangeToSelect}"
Executed="KeyChangeToSelect" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_ChangeToEraser}"
Executed="KeyChangeToEraser" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_ChangeToBoard}"
Executed="KeyChangeToBoard" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_Capture}"
Executed="KeyCapture" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_Hide}"
Executed="KeyHide" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_DrawLine}"
Executed="KeyDrawLine" />
<CommandBinding CanExecute="CommandBinding_CanExecute" Command="{StaticResource HotKey_Paste}"
Executed="HandleGlobalPaste" />
</Window.CommandBindings>
<Grid x:Name="Main_Grid">
@@ -911,7 +841,20 @@
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchEnablePalmEraser_Toggled" />
</ui:SimpleStackPanel>
<TextBlock Text="# 开启后,两个及以上触点且触摸面积较大时自动切换为橡皮擦,抬手后恢复原编辑模式。" TextWrapping="Wrap" Foreground="#a1a1aa" />
<TextBlock Text="# 开启后,两个及以上触点且触摸面积较大时自动切换为橡皮擦,抬手后恢复原编辑模式。" TextWrapping="Wrap" Foreground="#a1a1aa" />
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left"
Visibility="{Binding ElementName=ToggleSwitchEnablePalmEraser, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock Foreground="#fafafa" Text="手掌擦敏感度" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<ComboBox Name="ComboBoxPalmEraserSensitivity" Width="120" SelectionChanged="ComboBoxPalmEraserSensitivity_SelectionChanged">
<ComboBoxItem Content="低敏感度" />
<ComboBoxItem Content="中敏感度" />
<ComboBoxItem Content="高敏感度" />
</ComboBox>
</ui:SimpleStackPanel>
<TextBlock Text="# 低敏感度:需要更大的触摸面积和更多触摸点,减少误判;高敏感度:更容易触发手掌擦,但可能误判手指。"
TextWrapping="Wrap" Foreground="#a1a1aa"
Visibility="{Binding ElementName=ToggleSwitchEnablePalmEraser, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" />
</ui:SimpleStackPanel>
</GroupBox>
<GroupBox Name="GroupBoxInkRecognition">
@@ -5684,6 +5627,7 @@
<ui:SimpleStackPanel Margin="0,0,0,0" Height="40" Spacing="0"
Orientation="Horizontal">
<ui:SimpleStackPanel MouseDown="Border_MouseDown"
MouseUp="SymbolIconScreenshot_MouseUp"
Margin="0,0,0,0" Height="38" Width="32"
Orientation="Vertical">
<Image Margin="0,4,0,2" Height="19" Width="19">
@@ -8676,6 +8620,7 @@
<ui:SimpleStackPanel Margin="0,0,0,0" Height="40" Spacing="0"
Orientation="Horizontal">
<ui:SimpleStackPanel MouseDown="Border_MouseDown"
MouseUp="SymbolIconScreenshot_MouseUp"
Margin="0,0,0,0" Height="38" Width="32"
Orientation="Vertical">
<Image Margin="0,4,0,2" Height="19" Width="19">
+58 -7
View File
@@ -239,6 +239,9 @@ namespace Ink_Canvas
// 添加窗口激活事件处理,确保置顶状态在窗口重新激活时得到保持
this.Activated += Window_Activated;
this.Deactivated += Window_Deactivated;
// 为浮动栏按钮添加触摸事件支持
AddTouchSupportToFloatingBarButtons();
}
@@ -274,7 +277,6 @@ namespace Ink_Canvas
drawingAttributes.FitToCurve = Settings.Canvas.FitToCurve;
}
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
inkCanvas.Gesture += InkCanvas_Gesture;
}
catch { }
@@ -808,6 +810,7 @@ namespace Ink_Canvas
// 创建并显示更新窗口
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, AvailableLatestVersion, releaseDate, releaseNotes);
updateWindow.Owner = this;
bool? dialogResult = updateWindow.ShowDialog();
// 如果窗口被关闭但没有点击按钮,则不执行任何操作
@@ -844,7 +847,7 @@ namespace Ink_Canvas
App.IsAppExitByUser = true;
// 准备批处理脚本
AutoUpdateHelper.InstallNewVersionApp(AvailableLatestVersion, false);
AutoUpdateHelper.InstallNewVersionApp(AvailableLatestVersion, true); // 修改为静默模式,避免重复启动进程
// 关闭软件,让安装程序接管
Application.Current.Shutdown();
@@ -1676,6 +1679,8 @@ namespace Ink_Canvas
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentProcessId();
private const int GWL_EXSTYLE = -20;
@@ -1818,11 +1823,21 @@ namespace Ink_Canvas
return;
}
// 检查当前窗口是否在最顶层
// 检查是否有子窗口打开(模态对话框)
var foregroundWindow = GetForegroundWindow();
if (foregroundWindow != hwnd)
{
// 如果窗口不在最顶层,重新设置置顶
// 检查前景窗口是否是当前应用程序的子窗口
var foregroundWindowProcessId = GetWindowThreadProcessId(foregroundWindow, out uint processId);
var currentProcessId = GetCurrentProcessId();
if (processId == currentProcessId)
{
// 如果有子窗口在前景,暂停置顶维护
return;
}
// 如果窗口不在最顶层且没有子窗口,重新设置置顶
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
@@ -1832,7 +1847,6 @@ namespace Ink_Canvas
{
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
}
}
}
catch (Exception ex)
@@ -2025,8 +2039,11 @@ namespace Ink_Canvas
try
{
_globalHotkeyManager = new GlobalHotkeyManager(this);
// 不在这里加载快捷键,等待需要时再加载
LogHelper.WriteLogToFile("全局快捷键管理器已初始化(未加载快捷键)", LogHelper.LogType.Event);
// 启动时加载快捷键,但默认为鼠标模式,禁用快捷键以放行键盘操作
_globalHotkeyManager.EnableHotkeyRegistration();
// 启动时默认为鼠标模式,禁用快捷键
_globalHotkeyManager.UpdateHotkeyStateForToolMode(true);
LogHelper.WriteLogToFile("全局快捷键管理器已初始化,启动时默认为鼠标模式并禁用快捷键", LogHelper.LogType.Event);
}
catch (Exception ex)
{
@@ -2047,6 +2064,7 @@ namespace Ink_Canvas
return;
}
var hotkeySettingsWindow = new HotkeySettingsWindow(this, _globalHotkeyManager);
hotkeySettingsWindow.Owner = this;
hotkeySettingsWindow.ShowDialog();
}
catch (Exception ex)
@@ -2179,5 +2197,38 @@ namespace Ink_Canvas
}
}
#endregion
/// <summary>
/// 集中管理工具模式切换和快捷键状态更新
/// 避免在每个工具按钮点击时重复刷新快捷键状态
/// </summary>
/// <param name="newMode">新的编辑模式</param>
/// <param name="additionalActions">可选的额外操作委托</param>
internal void SetCurrentToolMode(InkCanvasEditingMode newMode, Action additionalActions = null)
{
try
{
// 执行模式切换
inkCanvas.EditingMode = newMode;
// 根据模式确定是否为鼠标模式(无工具模式)
bool isMouseMode = newMode == InkCanvasEditingMode.None;
// 更新快捷键状态
if (_globalHotkeyManager != null)
{
_globalHotkeyManager.UpdateHotkeyStateForToolMode(isMouseMode);
}
// 执行额外的操作(如果有)
additionalActions?.Invoke();
LogHelper.WriteLogToFile($"工具模式已切换到: {newMode}, 鼠标模式: {isMouseMode}", LogHelper.LogType.Trace);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"设置工具模式时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}
+4 -2
View File
@@ -704,7 +704,8 @@ namespace Ink_Canvas
forceEraser = false;
forcePointEraser = false;
drawingShapeMode = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Select);
SetCursorBasedOnEditingMode(inkCanvas);
}
@@ -721,7 +722,8 @@ namespace Ink_Canvas
forcePointEraser = false;
inkCanvas.EraserShape = new EllipseStylusShape(5, 5);
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.EraseByStroke);
drawingShapeMode = 0;
// 修复:切换到线擦时,确保重置笔的状态
+2 -1
View File
@@ -64,7 +64,8 @@ namespace Ink_Canvas
{
inkCanvas.IsManipulationEnabled = true;
drawingShapeMode = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Ink);
CancelSingleFingerDragMode();
CheckColorTheme();
}
+175 -58
View File
@@ -28,35 +28,6 @@ namespace Ink_Canvas
{
public partial class MainWindow : Window
{
#region
/// <summary>
/// 统一的快捷键状态刷新方法
/// 在工具切换时调用,避免重复代码
/// </summary>
private void RefreshHotkeyState()
{
try
{
var hotkeyManagerField = this.GetType().GetField("_globalHotkeyManager",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (hotkeyManagerField != null)
{
var hotkeyManager = hotkeyManagerField.GetValue(this);
if (hotkeyManager != null)
{
var updateMethod = hotkeyManager.GetType().GetMethod("UpdateHotkeyRegistrationState");
updateMethod?.Invoke(hotkeyManager, null);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"刷新快捷键状态时出错: {ex.Message}", LogHelper.LogType.Warning);
}
}
#endregion
#region "手勢"
@@ -820,8 +791,6 @@ namespace Ink_Canvas
BtnSelect_Click(null, null);
HideSubPanels("select");
// 工具切换完成后,统一刷新快捷键状态
RefreshHotkeyState();
}
#endregion
@@ -915,7 +884,11 @@ namespace Ink_Canvas
HideSubPanels();
BtnSettings_Click(null, null);
}
private async void SymbolIconScreenshot_MouseUp(object sender, MouseButtonEventArgs e) {
HideSubPanelsImmediately();
await Task.Delay(50);
SaveScreenShotToDesktop();
}
private void ImageCountdownTimer_MouseUp(object sender, MouseButtonEventArgs e)
{
@@ -1589,6 +1562,10 @@ namespace Ink_Canvas
// 隱藏高亮
HideFloatingBarHighlight();
// 使用集中化的工具模式切换方法,确保快捷键状态正确更新
// 鼠标模式下应该禁用快捷键以放行键盘操作
SetCurrentToolMode(InkCanvasEditingMode.None);
// 切换前自动截图保存墨迹
if (inkCanvas.Strokes.Count > 0 &&
inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber)
@@ -1681,14 +1658,6 @@ namespace Ink_Canvas
else
ViewboxFloatingBarMarginAnimation(100, true);
}
// 工具切换完成后,统一刷新快捷键状态
RefreshHotkeyState();
if (BtnSwitchTheme.Content.ToString() == "浅色")
BtnSwitch.Content = "黑板";
else
BtnSwitch.Content = "白板";
}
internal void PenIcon_Click(object sender, RoutedEventArgs e)
@@ -1719,7 +1688,8 @@ namespace Ink_Canvas
if (Pen_Icon.Background == null || StackPanelCanvasControls.Visibility == Visibility.Collapsed)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Ink);
GridTransparencyFakeBackground.Opacity = 1;
GridTransparencyFakeBackground.Background = new SolidColorBrush(StringToColor("#01FFFFFF"));
@@ -1752,7 +1722,8 @@ namespace Ink_Canvas
StackPanelCanvasControls.Visibility = Visibility.Visible;
//AnimationsHelper.ShowWithSlideFromLeftAndFade(StackPanelCanvasControls);
CheckEnableTwoFingerGestureBtnVisibility(true);
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Ink);
// 在批注模式下显示快捷调色盘(如果设置中启用了)
if (Settings.Appearance.IsShowQuickColorPalette && QuickColorPalettePanel != null && QuickColorPaletteSingleRowPanel != null)
@@ -1873,7 +1844,8 @@ namespace Ink_Canvas
{
SaveStrokes();
}
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Ink);
// 修复:从线擦切换到批注时,保持之前的笔类型状态
forceEraser = false;
@@ -1901,9 +1873,6 @@ namespace Ink_Canvas
}
}
// 延迟半秒后再刷新快捷键状态
Task.Delay(500).ContinueWith(_ => Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => RefreshHotkeyState())));
// 修复:从线擦切换到批注时,保持之前的笔类型状态
forceEraser = false;
@@ -1936,7 +1905,8 @@ namespace Ink_Canvas
EnableAdvancedEraserSystem();
// 使用新的高级橡皮擦系统
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.EraseByPoint);
ApplyAdvancedEraserShape(); // 使用新的橡皮擦形状应用方法
SetCursorBasedOnEditingMode(inkCanvas);
HideSubPanels("eraser"); // 高亮橡皮按钮
@@ -1961,9 +1931,6 @@ namespace Ink_Canvas
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
}
}
// 工具切换完成后,统一刷新快捷键状态
RefreshHotkeyState();
}
private void BoardEraserIcon_Click(object sender, RoutedEventArgs e)
@@ -1978,7 +1945,8 @@ namespace Ink_Canvas
EnableAdvancedEraserSystem();
// 使用新的高级橡皮擦系统
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.EraseByPoint);
ApplyAdvancedEraserShape(); // 使用新的橡皮擦形状应用方法
SetCursorBasedOnEditingMode(inkCanvas);
HideSubPanels("eraser"); // 高亮橡皮按钮
@@ -1998,9 +1966,6 @@ namespace Ink_Canvas
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
}
}
// 工具切换完成后,统一刷新快捷键状态
RefreshHotkeyState();
}
private void EraserIconByStrokes_Click(object sender, RoutedEventArgs e)
@@ -2018,7 +1983,8 @@ namespace Ink_Canvas
forcePointEraser = false;
inkCanvas.EraserShape = new EllipseStylusShape(5, 5);
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.EraseByStroke);
drawingShapeMode = 0;
// 修复:切换到线擦时,保存当前的笔类型状态,而不是强制重置
@@ -2030,8 +1996,6 @@ namespace Ink_Canvas
HideSubPanels("eraserByStrokes");
// 工具切换完成后,统一刷新快捷键状态
RefreshHotkeyState();
}
private void CursorWithDelIcon_Click(object sender, RoutedEventArgs e)
@@ -2301,7 +2265,8 @@ namespace Ink_Canvas
}
else
{
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Select);
}
}
@@ -3109,6 +3074,158 @@ namespace Ink_Canvas
}
}
#endregion
#region
/// <summary>
/// 为浮动栏按钮添加触摸和手写笔事件支持,让触摸和手写笔点击直接调用对应的鼠标点击方法
/// </summary>
private void AddTouchSupportToFloatingBarButtons()
{
// 为主要的浮动栏按钮添加触摸和手写笔事件支持
if (SymbolIconSelect != null)
{
SymbolIconSelect.TouchDown += (s, e) => SymbolIconSelect_MouseUp(s, null);
SymbolIconSelect.StylusDown += (s, e) => SymbolIconSelect_MouseUp(s, null);
}
if (SymbolIconUndo != null)
{
SymbolIconUndo.TouchDown += (s, e) => SymbolIconUndo_MouseUp(s, null);
SymbolIconUndo.StylusDown += (s, e) => SymbolIconUndo_MouseUp(s, null);
}
if (SymbolIconRedo != null)
{
SymbolIconRedo.TouchDown += (s, e) => SymbolIconRedo_MouseUp(s, null);
SymbolIconRedo.StylusDown += (s, e) => SymbolIconRedo_MouseUp(s, null);
}
if (SymbolIconDelete != null)
{
SymbolIconDelete.TouchDown += (s, e) => SymbolIconDelete_MouseUp(s, null);
SymbolIconDelete.StylusDown += (s, e) => SymbolIconDelete_MouseUp(s, null);
}
if (ToolsFloatingBarBtn != null)
{
ToolsFloatingBarBtn.TouchDown += (s, e) => SymbolIconTools_MouseUp(s, null);
ToolsFloatingBarBtn.StylusDown += (s, e) => SymbolIconTools_MouseUp(s, null);
}
if (RandomDrawPanel != null)
{
RandomDrawPanel.TouchDown += (s, e) => SymbolIconRand_MouseUp(s, null);
RandomDrawPanel.StylusDown += (s, e) => SymbolIconRand_MouseUp(s, null);
}
if (SingleDrawPanel != null)
{
SingleDrawPanel.TouchDown += (s, e) => SymbolIconRandOne_MouseUp(s, null);
SingleDrawPanel.StylusDown += (s, e) => SymbolIconRandOne_MouseUp(s, null);
}
// 注意:Screenshot和Settings按钮在XAML中没有直接的Name属性,需要通过其他方式绑定
// 这些按钮的事件处理已经在XAML中通过MouseUp绑定
if (BorderFloatingBarMoveControls != null)
{
BorderFloatingBarMoveControls.TouchDown += (s, e) => SymbolIconEmoji_MouseUp(s, null);
BorderFloatingBarMoveControls.StylusDown += (s, e) => SymbolIconEmoji_MouseUp(s, null);
}
// 白板模式下的按钮不添加触摸事件支持,保持原有的鼠标事件处理
// 为快捷调色盘按钮添加触摸和手写笔事件支持
if (QuickColorWhite != null)
{
QuickColorWhite.TouchDown += (s, e) => QuickColorWhite_Click(s, null);
QuickColorWhite.StylusDown += (s, e) => QuickColorWhite_Click(s, null);
}
if (QuickColorOrange != null)
{
QuickColorOrange.TouchDown += (s, e) => QuickColorOrange_Click(s, null);
QuickColorOrange.StylusDown += (s, e) => QuickColorOrange_Click(s, null);
}
if (QuickColorYellow != null)
{
QuickColorYellow.TouchDown += (s, e) => QuickColorYellow_Click(s, null);
QuickColorYellow.StylusDown += (s, e) => QuickColorYellow_Click(s, null);
}
if (QuickColorBlack != null)
{
QuickColorBlack.TouchDown += (s, e) => QuickColorBlack_Click(s, null);
QuickColorBlack.StylusDown += (s, e) => QuickColorBlack_Click(s, null);
}
if (QuickColorBlue != null)
{
QuickColorBlue.TouchDown += (s, e) => QuickColorBlue_Click(s, null);
QuickColorBlue.StylusDown += (s, e) => QuickColorBlue_Click(s, null);
}
if (QuickColorRed != null)
{
QuickColorRed.TouchDown += (s, e) => QuickColorRed_Click(s, null);
QuickColorRed.StylusDown += (s, e) => QuickColorRed_Click(s, null);
}
if (QuickColorGreen != null)
{
QuickColorGreen.TouchDown += (s, e) => QuickColorGreen_Click(s, null);
QuickColorGreen.StylusDown += (s, e) => QuickColorGreen_Click(s, null);
}
if (QuickColorPurple != null)
{
QuickColorPurple.TouchDown += (s, e) => QuickColorPurple_Click(s, null);
QuickColorPurple.StylusDown += (s, e) => QuickColorPurple_Click(s, null);
}
// 单行快捷调色盘
if (QuickColorWhiteSingle != null)
{
QuickColorWhiteSingle.TouchDown += (s, e) => QuickColorWhite_Click(s, null);
QuickColorWhiteSingle.StylusDown += (s, e) => QuickColorWhite_Click(s, null);
}
if (QuickColorOrangeSingle != null)
{
QuickColorOrangeSingle.TouchDown += (s, e) => QuickColorOrange_Click(s, null);
QuickColorOrangeSingle.StylusDown += (s, e) => QuickColorOrange_Click(s, null);
}
if (QuickColorYellowSingle != null)
{
QuickColorYellowSingle.TouchDown += (s, e) => QuickColorYellow_Click(s, null);
QuickColorYellowSingle.StylusDown += (s, e) => QuickColorYellow_Click(s, null);
}
if (QuickColorBlackSingle != null)
{
QuickColorBlackSingle.TouchDown += (s, e) => QuickColorBlack_Click(s, null);
QuickColorBlackSingle.StylusDown += (s, e) => QuickColorBlack_Click(s, null);
}
if (QuickColorRedSingle != null)
{
QuickColorRedSingle.TouchDown += (s, e) => QuickColorRed_Click(s, null);
QuickColorRedSingle.StylusDown += (s, e) => QuickColorRed_Click(s, null);
}
if (QuickColorGreenSingle != null)
{
QuickColorGreenSingle.TouchDown += (s, e) => QuickColorGreen_Click(s, null);
QuickColorGreenSingle.StylusDown += (s, e) => QuickColorGreen_Click(s, null);
}
}
#endregion
}
+2 -9
View File
@@ -44,15 +44,8 @@ namespace Ink_Canvas
}
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape) KeyExit(null, null);
}
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
// 保留PPT翻页快捷键处理
// 以下方法保留供全局快捷键调用
private void HotKey_Undo(object sender, ExecutedRoutedEventArgs e)
{
@@ -276,8 +276,10 @@ namespace Ink_Canvas
{
if (inkCanvas.GetSelectedStrokes().Count == inkCanvas.Strokes.Count)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Ink, () => {
SetCurrentToolMode(InkCanvasEditingMode.Select);
});
}
else
{
@@ -290,7 +292,8 @@ namespace Ink_Canvas
}
else
{
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Select);
}
}
@@ -454,7 +457,8 @@ namespace Ink_Canvas
else
{
// 新增:启动套索选择模式
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Select);
inkCanvas.Select(new StrokeCollection());
}
}
@@ -492,7 +496,8 @@ namespace Ink_Canvas
forceEraser = false;
forcePointEraser = false;
drawingShapeMode = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Select);
SetCursorBasedOnEditingMode(inkCanvas);
}
@@ -502,7 +507,8 @@ namespace Ink_Canvas
forceEraser = false;
forcePointEraser = false;
drawingShapeMode = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Select);
inkCanvas.IsManipulationEnabled = true;
SetCursorBasedOnEditingMode(inkCanvas);
}
+6
View File
@@ -3034,6 +3034,12 @@ namespace Ink_Canvas
#endregion
private void ComboBoxPalmEraserSensitivity_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!isLoaded) return;
Settings.Canvas.PalmEraserSensitivity = ComboBoxPalmEraserSensitivity.SelectedIndex;
SaveSettingsToFile();
}
}
}
@@ -683,6 +683,7 @@ namespace Ink_Canvas
if (Settings.Canvas != null)
{
ToggleSwitchEnablePalmEraser.IsOn = Settings.Canvas.EnablePalmEraser;
ComboBoxPalmEraserSensitivity.SelectedIndex = Settings.Canvas.PalmEraserSensitivity;
}
// Advanced
@@ -555,6 +555,7 @@ namespace Ink_Canvas
{
// 禁用原有的FitToCurve,使用新的高级贝塞尔曲线平滑
if (Settings.Canvas.FitToCurve) drawingAttributes.FitToCurve = false;
// 在绘制过程中禁用浮动栏交互,避免干扰绘制
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
List<Point> pointList;
@@ -1668,6 +1669,18 @@ namespace Ink_Canvas
private void inkCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
// 检查鼠标点击是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var mousePoint = e.GetPosition(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
// 如果鼠标点击发生在浮动栏区域,不阻止事件传播,让浮动栏按钮能够接收鼠标事件
if (floatingBarBounds.Contains(mousePoint))
{
// 不设置 ViewboxFloatingBar.IsHitTestVisible = false,让浮动栏按钮能够接收鼠标事件
return;
}
inkCanvas.CaptureMouse();
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
+164 -47
View File
@@ -109,6 +109,18 @@ namespace Ink_Canvas
private void MainWindow_TouchDown(object sender, TouchEventArgs e)
{
// 检查触摸是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var touchPoint = e.GetTouchPoint(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
// 如果触摸发生在浮动栏区域,不阻止事件传播,让浮动栏按钮能够接收触摸事件
if (floatingBarBounds.Contains(touchPoint.Position))
{
// 不设置 ViewboxFloatingBar.IsHitTestVisible = false,让浮动栏按钮能够接收触摸事件
return;
}
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint
|| inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke
|| inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
@@ -139,6 +151,18 @@ namespace Ink_Canvas
private void MainWindow_StylusDown(object sender, StylusDownEventArgs e)
{
// 检查手写笔点击是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var stylusPoint = e.GetPosition(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
// 如果手写笔点击发生在浮动栏区域,不阻止事件传播,让浮动栏按钮能够接收手写笔事件
if (floatingBarBounds.Contains(stylusPoint))
{
// 不设置 ViewboxFloatingBar.IsHitTestVisible = false,让浮动栏按钮能够接收手写笔事件
return;
}
LogHelper.WriteLogToFile($"MainWindow_StylusDown 被调用,笔尾状态: {e.StylusDevice.Inverted}, 当前 drawingShapeMode: {drawingShapeMode}, 当前 EditingMode: {inkCanvas.EditingMode}", LogHelper.LogType.Info);
// 新增:根据是否为笔尾自动切换橡皮擦/画笔模式
@@ -323,6 +347,18 @@ namespace Ink_Canvas
private void Main_Grid_TouchDown(object sender, TouchEventArgs e)
{
// 检查触摸是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var touchPoint = e.GetTouchPoint(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
// 如果触摸发生在浮动栏区域,不阻止事件传播,让浮动栏按钮能够接收触摸事件
if (floatingBarBounds.Contains(touchPoint.Position))
{
// 不设置 ViewboxFloatingBar.IsHitTestVisible = false,让浮动栏按钮能够接收触摸事件
return;
}
SetCursorBasedOnEditingMode(inkCanvas);
inkCanvas.CaptureTouch(e.TouchDevice);
@@ -365,11 +401,24 @@ namespace Ink_Canvas
private bool palmEraserWasEnabledBeforeMultiTouch;
private bool palmEraserTouchDownHandled = false; // 新增:标记手掌擦触摸按下是否已处理
private DateTime palmEraserActivationTime; // 新增:记录手掌擦激活时间
private const int PALM_ERASER_TIMEOUT_MS = 5000; // 新增:手掌擦超时时间(5秒)
private const int PALM_ERASER_TIMEOUT_MS = 3000; // 修改:减少手掌擦超时时间(3秒)
private System.Windows.Threading.DispatcherTimer palmEraserRecoveryTimer; // 新增:手掌擦恢复定时器
private HashSet<int> palmEraserTouchIds = new HashSet<int>(); // 新增:记录参与手掌擦的触摸点ID
private void inkCanvas_PreviewTouchDown(object sender, TouchEventArgs e)
{
// 检查触摸是否发生在浮动栏区域,如果是则允许事件传播到浮动栏按钮
var touchPoint = e.GetTouchPoint(this);
var floatingBarBounds = ViewboxFloatingBar.TransformToAncestor(this).TransformBounds(
new Rect(0, 0, ViewboxFloatingBar.ActualWidth, ViewboxFloatingBar.ActualHeight));
// 如果触摸发生在浮动栏区域,不阻止事件传播,让浮动栏按钮能够接收触摸事件
if (floatingBarBounds.Contains(touchPoint.Position))
{
// 不设置 ViewboxFloatingBar.IsHitTestVisible = false,让浮动栏按钮能够接收触摸事件
return;
}
// 橡皮状态下不做任何切换,直接return,保证橡皮可持续
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint
|| inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke)
@@ -390,21 +439,21 @@ namespace Ink_Canvas
// 修复:几何绘制模式下,只记录几何绘制的起点,不记录触摸轨迹
if (dec.Count == 0)
{
var touchPoint = e.GetTouchPoint(inkCanvas);
var inkTouchPoint = e.GetTouchPoint(inkCanvas);
// 对于双曲线绘制,第一笔时记录起点,第二笔时不更新起点
if (drawingShapeMode == 24 || drawingShapeMode == 25)
{
// 双曲线绘制:第一笔记录起点,第二笔保持第一笔的起点
if (drawMultiStepShapeCurrentStep == 0)
{
iniP = touchPoint.Position;
iniP = inkTouchPoint.Position;
}
// 第二笔时不更新iniP,保持第一笔的起点
}
else
{
// 其他图形正常记录起点
iniP = touchPoint.Position;
iniP = inkTouchPoint.Position;
}
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
}
@@ -419,16 +468,57 @@ namespace Ink_Canvas
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
dec.Add(e.TouchDevice.Id);
// Palm Eraser 逻辑 - 修复:只在触摸按下时处理一次,避免重复触发
// Palm Eraser 逻辑 - 优化:改进手掌判定条件,提高精度
if (Settings.Canvas.EnablePalmEraser && dec.Count >= 2 && !isPalmEraserActive && !palmEraserTouchDownHandled)
{
var bounds = e.GetTouchPoint(inkCanvas).Bounds;
double palmThreshold = 40; // 触摸面积阈值,可根据实际调整
if (bounds.Width >= palmThreshold || bounds.Height >= palmThreshold)
// 根据敏感度设置调整判定参数
double palmThreshold;
double aspectRatioThreshold;
int minTouchPoints;
switch (Settings.Canvas.PalmEraserSensitivity)
{
case 0: // 低敏感度 - 更严格的判定
palmThreshold = 80;
aspectRatioThreshold = 0.4;
minTouchPoints = 4;
break;
case 1: // 中敏感度 - 平衡的判定
palmThreshold = 60;
aspectRatioThreshold = 0.3;
minTouchPoints = 3;
break;
case 2: // 高敏感度 - 较宽松的判定
default:
palmThreshold = 50;
aspectRatioThreshold = 0.25;
minTouchPoints = 2;
break;
}
// 计算宽高比
double aspectRatio = Math.Min(bounds.Width, bounds.Height) / Math.Max(bounds.Width, bounds.Height);
// 更严格的手掌判定条件
bool isLargeTouch = bounds.Width >= palmThreshold && bounds.Height >= palmThreshold;
bool isPalmLikeShape = aspectRatio >= aspectRatioThreshold;
bool hasMultipleTouchPoints = dec.Count >= minTouchPoints;
if (isLargeTouch && isPalmLikeShape && hasMultipleTouchPoints)
{
// 记录当前编辑模式和高光状态
palmEraserLastEditingMode = inkCanvas.EditingMode;
palmEraserLastIsHighlighter = drawingAttributes.IsHighlighter;
// 记录参与手掌擦的触摸点ID
palmEraserTouchIds.Clear();
foreach (int touchId in dec)
{
palmEraserTouchIds.Add(touchId);
}
// 切换为橡皮擦
EraserIcon_Click(null, null);
isPalmEraserActive = true;
@@ -437,13 +527,16 @@ namespace Ink_Canvas
// 启动恢复定时器,防止卡死
StartPalmEraserRecoveryTimer();
// 记录日志
LogHelper.WriteLogToFile($"Palm eraser activated - Sensitivity: {Settings.Canvas.PalmEraserSensitivity}, Touch bounds: {bounds.Width}x{bounds.Height}, Aspect ratio: {aspectRatio:F2}, Touch points: {dec.Count}", LogHelper.LogType.Info);
}
}
// 设备1个的时候,记录中心点
if (dec.Count == 1)
{
var touchPoint = e.GetTouchPoint(inkCanvas);
touchPoint = e.GetTouchPoint(inkCanvas);
centerPoint = touchPoint.Position;
// 修复:只允许在此处赋值iniP,防止TouchMove等其他地方覆盖,保证几何绘制起点一致
@@ -497,41 +590,54 @@ namespace Ink_Canvas
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
// Palm Eraser 逻辑:所有点抬起后恢复原编辑模式
// Palm Eraser 逻辑:优化状态恢复机制
dec.Remove(e.TouchDevice.Id);
if (isPalmEraserActive && dec.Count == 0)
// 如果是手掌擦的触摸点,从记录中移除
if (palmEraserTouchIds.Contains(e.TouchDevice.Id))
{
palmEraserTouchIds.Remove(e.TouchDevice.Id);
}
// 当所有手掌擦触摸点都抬起时,恢复原编辑模式
if (isPalmEraserActive && palmEraserTouchIds.Count == 0)
{
// 恢复高光状态
drawingAttributes.IsHighlighter = palmEraserLastIsHighlighter;
// 恢复编辑模式 - 修复:确保正确恢复状态
// 恢复编辑模式 - 优化:改进状态恢复逻辑
try
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
if (palmEraserLastEditingMode == InkCanvasEditingMode.Ink)
// 根据之前的状态恢复
switch (palmEraserLastEditingMode)
{
PenIcon_Click(null, null);
}
else if (palmEraserLastEditingMode == InkCanvasEditingMode.Select)
{
SymbolIconSelect_MouseUp(null, null);
}
else
{
inkCanvas.EditingMode = palmEraserLastEditingMode;
case InkCanvasEditingMode.Ink:
PenIcon_Click(null, null);
break;
case InkCanvasEditingMode.Select:
SymbolIconSelect_MouseUp(null, null);
break;
default:
inkCanvas.EditingMode = palmEraserLastEditingMode;
break;
}
LogHelper.WriteLogToFile($"Palm eraser recovered to mode: {palmEraserLastEditingMode}", LogHelper.LogType.Info);
}
}
catch (Exception ex)
{
// 如果恢复失败,强制切换到批注模式
Trace.WriteLine($"Palm eraser recovery failed: {ex.Message}, forcing to Ink mode");
LogHelper.WriteLogToFile($"Palm eraser recovery failed: {ex.Message}, forcing to Ink mode", LogHelper.LogType.Error);
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
// 重置手掌擦状态
isPalmEraserActive = false;
palmEraserTouchDownHandled = false;
palmEraserTouchIds.Clear();
// 停止恢复定时器
StopPalmEraserRecoveryTimer();
@@ -542,44 +648,47 @@ namespace Ink_Canvas
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
LogHelper.WriteLogToFile("Palm eraser state reset completed", LogHelper.LogType.Info);
}
// 新增:超时检测 - 如果手掌擦激活时间过长,强制重置状态
if (isPalmEraserActive && dec.Count == 0)
if (isPalmEraserActive)
{
var timeSinceActivation = DateTime.Now - palmEraserActivationTime;
if (timeSinceActivation.TotalMilliseconds > PALM_ERASER_TIMEOUT_MS)
{
Trace.WriteLine($"Palm eraser timeout detected ({timeSinceActivation.TotalMilliseconds}ms), forcing recovery");
LogHelper.WriteLogToFile($"Palm eraser timeout detected ({timeSinceActivation.TotalMilliseconds}ms), forcing recovery", LogHelper.LogType.Warning);
// 强制恢复状态
try
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
if (palmEraserLastEditingMode == InkCanvasEditingMode.Ink)
switch (palmEraserLastEditingMode)
{
PenIcon_Click(null, null);
}
else if (palmEraserLastEditingMode == InkCanvasEditingMode.Select)
{
SymbolIconSelect_MouseUp(null, null);
}
else
{
inkCanvas.EditingMode = palmEraserLastEditingMode;
case InkCanvasEditingMode.Ink:
PenIcon_Click(null, null);
break;
case InkCanvasEditingMode.Select:
SymbolIconSelect_MouseUp(null, null);
break;
default:
inkCanvas.EditingMode = palmEraserLastEditingMode;
break;
}
}
}
catch (Exception ex)
{
Trace.WriteLine($"Palm eraser timeout recovery failed: {ex.Message}, forcing to Ink mode");
LogHelper.WriteLogToFile($"Palm eraser timeout recovery failed: {ex.Message}, forcing to Ink mode", LogHelper.LogType.Error);
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
// 重置所有手掌擦状态
isPalmEraserActive = false;
palmEraserTouchDownHandled = false;
palmEraserTouchIds.Clear();
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
@@ -588,6 +697,8 @@ namespace Ink_Canvas
// 停止恢复定时器
StopPalmEraserRecoveryTimer();
LogHelper.WriteLogToFile("Palm eraser timeout recovery completed", LogHelper.LogType.Info);
}
}
// 修复:几何绘制模式下,触摸抬手时应该正确处理,而不是简单模拟鼠标事件
@@ -656,6 +767,7 @@ namespace Ink_Canvas
// 如果手掌擦还在激活状态但触摸点已清空,强制重置状态
isPalmEraserActive = false;
palmEraserTouchDownHandled = false;
palmEraserTouchIds.Clear(); // 确保清空触摸点ID
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
@@ -902,36 +1014,39 @@ namespace Ink_Canvas
var timeSinceActivation = DateTime.Now - palmEraserActivationTime;
if (timeSinceActivation.TotalMilliseconds > PALM_ERASER_TIMEOUT_MS)
{
Trace.WriteLine($"Palm eraser recovery timer triggered, forcing recovery after {timeSinceActivation.TotalMilliseconds}ms");
LogHelper.WriteLogToFile($"Palm eraser recovery timer triggered, forcing recovery after {timeSinceActivation.TotalMilliseconds}ms", LogHelper.LogType.Warning);
// 强制恢复状态
try
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
if (palmEraserLastEditingMode == InkCanvasEditingMode.Ink)
switch (palmEraserLastEditingMode)
{
PenIcon_Click(null, null);
}
else if (palmEraserLastEditingMode == InkCanvasEditingMode.Select)
{
SymbolIconSelect_MouseUp(null, null);
}
else
{
inkCanvas.EditingMode = palmEraserLastEditingMode;
case InkCanvasEditingMode.Ink:
PenIcon_Click(null, null);
break;
case InkCanvasEditingMode.Select:
SymbolIconSelect_MouseUp(null, null);
break;
default:
inkCanvas.EditingMode = palmEraserLastEditingMode;
break;
}
LogHelper.WriteLogToFile($"Palm eraser timer recovery to mode: {palmEraserLastEditingMode}", LogHelper.LogType.Info);
}
}
catch (Exception ex)
{
Trace.WriteLine($"Palm eraser recovery timer failed: {ex.Message}, forcing to Ink mode");
LogHelper.WriteLogToFile($"Palm eraser recovery timer failed: {ex.Message}, forcing to Ink mode", LogHelper.LogType.Error);
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
// 重置所有手掌擦状态
isPalmEraserActive = false;
palmEraserTouchDownHandled = false;
palmEraserTouchIds.Clear();
inkCanvas.IsHitTestVisible = true;
inkCanvas.IsManipulationEnabled = true;
@@ -940,6 +1055,8 @@ namespace Ink_Canvas
// 停止定时器
StopPalmEraserRecoveryTimer();
LogHelper.WriteLogToFile("Palm eraser timer recovery completed", LogHelper.LogType.Info);
}
}
}
+4 -4
View File
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
@@ -10,7 +10,7 @@ using System.Windows;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("CJK_mkp")]
[assembly: AssemblyProduct("InkCanvasForClass")]
[assembly: AssemblyCopyright("© Copyright HARKOTEK Studio 2024-now")]
[assembly: AssemblyCopyright("Copyright © HARKOTEK Studio 2024")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.8.0")]
[assembly: AssemblyFileVersion("1.7.8.0")]
[assembly: AssemblyVersion("1.7.8.3")]
[assembly: AssemblyFileVersion("1.7.8.3")]
+2
View File
@@ -88,6 +88,8 @@ namespace Ink_Canvas
public bool IsCompressPicturesUploaded { get; set; }
[JsonProperty("enablePalmEraser")]
public bool EnablePalmEraser { get; set; } = true;
[JsonProperty("palmEraserSensitivity")]
public int PalmEraserSensitivity { get; set; } = 2; // 0-低敏感度, 1-中敏感度, 2-高敏感度
[JsonProperty("clearCanvasAlsoClearImages")]
public bool ClearCanvasAlsoClearImages { get; set; } = true;
[JsonProperty("showCircleCenter")]
@@ -340,7 +340,7 @@ namespace Ink_Canvas
downloadUrl = Path.Combine(updatesFolderPath, $"InkCanvasForClass.CE.{version}.zip");
}
LogHelper.WriteLogToFile($"AutoUpdate | 开始安装版本: {version}");
AutoUpdateHelper.InstallNewVersionApp(version, false);
AutoUpdateHelper.InstallNewVersionApp(version, true);
App.IsAppExitByUser = true;
Application.Current.Dispatcher.Invoke(() =>
{
+1
View File
@@ -94,6 +94,7 @@
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PrefacedCorg"><img src="https://avatars.githubusercontent.com/u/129855423?v=4?s=100" width="100px;" alt="PrefacedCorg"/><br /><sub><b>PrefacedCorg</b></sub></a><br /><a href="#code-PrefacedCorg" title="Code">💻</a> <a href="#design-PrefacedCorg" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://blog.jursin.top"><img src="https://avatars.githubusercontent.com/u/127487914?v=4?s=100" width="100px;" alt="Jursin"/><br /><sub><b>Jursin</b></sub></a><br /><a href="#design-Jursin" title="Design">🎨</a></td>
</tr>
</tbody>
</table>
+4
View File
@@ -1,3 +1,4 @@
ICC CE 1.7.X.X更新日志
1. 改进端点吸附
2. 新增自定义浮动栏图标
3. 新增自定义点名背景
@@ -51,3 +52,6 @@
51. 优化调色板
52. 修复浮动栏动画异常
53. 修复开关状态异常
54. 优化浮动栏触摸点击
55. 优化快捷键