Compare commits

...

147 Commits

Author SHA1 Message Date
CJK_mkp f5f989d140 Merge pull request #98 from InkCanvasForClass/beta
ICC CE 1.7.2.0
2025-07-25 18:45:27 +08:00
CJKmkp a0058c104d 更新日志 2025-07-25 18:36:23 +08:00
CJKmkp 7f1f322d04 improve:白板时间显示 2025-07-25 18:32:03 +08:00
CJKmkp 57ac8d8771 更新版本号 2025-07-25 18:28:38 +08:00
CJKmkp bcd0509eff improve:窗口无焦点 2025-07-25 18:21:16 +08:00
CJKmkp 624af87795 fix:侧边栏显示及浮动栏高度计算 2025-07-25 18:12:46 +08:00
CJKmkp 616df56657 improve:任务栏高度计算 2025-07-25 18:02:46 +08:00
CJKmkp 4efd6abb56 improve:任务栏高度计算 2025-07-25 17:57:04 +08:00
CJKmkp 8c657a4ccf improve:PPT模块 2025-07-25 17:50:07 +08:00
CJKmkp db582f6c88 更新版本号 2025-07-25 11:07:12 +08:00
CJKmkp a7b861f83c improve:PPT模块 2025-07-25 10:21:55 +08:00
CJKmkp 19b1c7ae8b improve:PPT模块 2025-07-25 09:58:53 +08:00
CJKmkp 192cec68c7 improve:自动更新 2025-07-24 23:18:44 +08:00
CJK_mkp 3541522fc6 Merge pull request #97 from InkCanvasForClass/beta
ICC CE 1.7.1.14
2025-07-24 22:27:06 +08:00
CJKmkp 4afa66f3f3 更新版本号 2025-07-24 22:18:31 +08:00
CJKmkp 7fde157184 improve:自动更新 2025-07-24 22:16:38 +08:00
CJKmkp a1935e8299 improve:直线拉直 2025-07-24 21:02:00 +08:00
CJKmkp 50e993fd89 improve:直线拉直 2025-07-24 20:59:21 +08:00
CJKmkp a25ec6b0af fix:白板使用擦除后出现的线条异常BUG 2025-07-24 02:02:34 +08:00
CJKmkp efeb99aaac fix:触摸时长方体绘制多余直线 2025-07-24 00:58:03 +08:00
CJKmkp f19118432d improve:自动保存 2025-07-24 00:20:00 +08:00
CJKmkp b69eac2886 improve:默认设置 2025-07-23 23:16:49 +08:00
CJKmkp 4efc4e7f34 improve:默认配置文件 2025-07-23 23:14:29 +08:00
CJKmkp 4755eb06e2 improve:手掌擦 2025-07-23 23:12:37 +08:00
CJKmkp e4dc1c0b4e improve:手掌擦 2025-07-23 23:10:10 +08:00
CJKmkp 89f54c2b4a 更新版本号 2025-07-23 23:06:39 +08:00
CJKmkp abf52f0d49 add:新版手掌擦 2025-07-23 23:05:28 +08:00
CJKmkp 044df3f09c delete:手掌擦 2025-07-23 22:57:16 +08:00
CJKmkp 271829f9c1 Revert "improve:手掌擦"
This reverts commit eb0cf27218.
2025-07-23 22:16:43 +08:00
CJKmkp 13625b37a8 improve:自动更新 2025-07-23 21:45:29 +08:00
CJK_mkp cceadd2a3d Merge pull request #96 from InkCanvasForClass/beta
ICC CE 1.7.1.12
2025-07-23 19:52:01 +08:00
CJKmkp 781191196f 更新版本号 2025-07-23 19:44:06 +08:00
CJKmkp eb0cf27218 improve:手掌擦 2025-07-23 19:42:41 +08:00
CJKmkp 1706341283 improve:自动更新 2025-07-23 19:29:44 +08:00
CJKmkp 71f46b3bff improve:自动更新 2025-07-22 22:39:02 +08:00
CJKmkp 4c39798682 improve:设置界面 2025-07-22 22:02:21 +08:00
CJKmkp b861aa385d improve:设置界面 2025-07-22 21:39:22 +08:00
CJKmkp 156e8a2686 improve:自动更新 2025-07-22 21:01:25 +08:00
CJKmkp f88acf1375 improve:设置页面 2025-07-22 19:06:35 +08:00
CJKmkp be770d4607 improve:自动更新 2025-07-22 18:45:43 +08:00
CJKmkp 7565f624c9 improve:自动更新 2025-07-22 18:36:37 +08:00
CJKmkp 65d56297fd improve:自动更新 2025-07-22 18:25:04 +08:00
CJKmkp 86caac4a1d improve:自动更新 2025-07-22 18:08:22 +08:00
CJKmkp d382ac4fa2 improve:自动更新 2025-07-22 18:02:29 +08:00
CJKmkp f9ceeaad44 improve:自动更新 2025-07-22 17:36:02 +08:00
CJKmkp 0691293973 improve:自动更新 2025-07-22 17:07:27 +08:00
CJKmkp 9491b48eb6 improve:窗口无焦点 2025-07-22 16:20:37 +08:00
CJKmkp 6c7f63270f improve:白板时间显示 2025-07-22 16:12:42 +08:00
CJKmkp a62f793288 improve:设置 2025-07-21 22:45:55 +08:00
CJK_mkp 5fc715b7d0 Merge pull request #95 from InkCanvasForClass/beta
ICC CE 1.7.10
2025-07-21 22:33:53 +08:00
CJKmkp 901b54b829 更新版本号 2025-07-21 21:39:08 +08:00
CJKmkp 0efa1127a3 fix:issue #94 2025-07-21 21:02:23 +08:00
CJKmkp 28fc53799a fix:issue #92 #94 2025-07-21 20:30:07 +08:00
CJKmkp bdc4af7cd4 improve:任务栏高度计算 2025-07-21 18:53:47 +08:00
CJKmkp c68996ff20 improve:手掌擦 2025-07-21 18:39:42 +08:00
CJKmkp f517a63cda improve:手掌擦 2025-07-21 18:37:25 +08:00
CJKmkp fefb9b490e 更新版本号 2025-07-21 18:13:27 +08:00
CJKmkp 383e1d5368 improve:鸿合屏幕书写查杀后自动进入批注 #38 2025-07-21 17:57:08 +08:00
CJKmkp 4e4ce36e76 add:鸿合屏幕书写查杀后自动进入批注 #38 2025-07-21 17:28:52 +08:00
CJKmkp 19983a113e add:进入PPT放映自动回到首页 #38 2025-07-21 17:19:27 +08:00
CJKmkp 8eaac465ff improve:退出PPT放映自动恢复收纳模式 #38 2025-07-21 17:08:17 +08:00
CJKmkp 17b2d744ba add:退出PPT放映自动恢复收纳模式 #38 2025-07-21 16:53:11 +08:00
CJKmkp 166e0d400a add:侧边栏退出放映按钮 #38 2025-07-21 16:43:29 +08:00
CJKmkp 5cf409ce0b improve:PPT模块 2025-07-21 16:15:42 +08:00
CJKmkp e37e847a8b 删除无用using 2025-07-21 16:00:31 +08:00
CJKmkp 32c77d3743 improve:插入图片 2025-07-21 15:20:05 +08:00
CJKmkp 45e368d9e7 fix:issue #91 2025-07-21 14:35:30 +08:00
CJKmkp 97496302fb improve:插入照片 2025-07-21 14:19:55 +08:00
CJKmkp 675959e615 improve:插入图片 2025-07-21 12:44:05 +08:00
CJKmkp f641b282b6 improve:插入图片 2025-07-21 12:15:56 +08:00
CJKmkp e92a05683c add:插入图片 2025-07-21 11:37:20 +08:00
CJKmkp 93bef2e144 fix:白板页面预览无法使用触摸 2025-07-21 10:14:21 +08:00
CJKmkp 44e331ae96 fix:issue #91 2025-07-21 09:56:51 +08:00
CJKmkp f295c85668 fix:issue #90 2025-07-21 09:54:58 +08:00
CJKmkp 6f843bface improve:PPT模块 2025-07-21 08:43:16 +08:00
CJKmkp 2dea6076f0 improve:PPT模块 2025-07-21 08:37:37 +08:00
CJKmkp 78c6c7b571 improve:手掌擦 2025-07-20 22:54:49 +08:00
CJKmkp a50bec9326 improve:插件图标 2025-07-20 22:40:55 +08:00
CJKmkp 7d193d7de6 fix:错误的侧边栏按钮 2025-07-20 21:49:47 +08:00
CJKmkp 5bbb3e6a6c fix:错误的侧边栏按钮 2025-07-20 21:43:03 +08:00
CJK_mkp 32c1ddab46 Merge pull request #88 from InkCanvasForClass/beta
ICC CE 1.7.1.6
2025-07-20 21:22:04 +08:00
CJKmkp 15198c32ea improve:PPT模块 2025-07-20 21:14:59 +08:00
CJKmkp 476503338f improve:窗口无焦点 2025-07-20 21:03:42 +08:00
CJKmkp ba6303dca0 improve:窗口无焦点 2025-07-20 21:01:15 +08:00
CJKmkp 23432465ac improve:PPT模块 2025-07-20 19:52:59 +08:00
CJKmkp 1f0b5cebeb improve:PPT模块 2025-07-20 19:40:10 +08:00
CJKmkp 1f83c84a7e improve:PPT模块 2025-07-20 19:36:58 +08:00
CJKmkp e209af9c57 improve:PPT模块 2025-07-20 19:31:01 +08:00
CJKmkp a714d312a8 improve:PPT日志输出 2025-07-20 19:23:43 +08:00
CJKmkp 4a776c1fe8 improve:墨迹平滑方案 2025-07-20 19:17:32 +08:00
CJKmkp da86f07cbe fix:PPT墨迹对应问题 2025-07-20 18:27:08 +08:00
CJKmkp 48e0ca887d improve:PPT联动模块 2025-07-20 17:20:33 +08:00
CJKmkp 0f3b6c4cec improve:墨迹平滑方案 2025-07-20 16:48:16 +08:00
CJKmkp 9ce9631135 更新版本号 2025-07-20 16:16:17 +08:00
CJKmkp c73a5c3a7e fix:翻页控件崩溃 2025-07-20 16:14:26 +08:00
CJKmkp 62d35127b1 improve:墨迹平滑方案 2025-07-20 15:57:51 +08:00
CJKmkp 09f17caabe fix:白板颜色无法保存的问题 2025-07-20 15:42:11 +08:00
CJKmkp c5355d7497 improve:墨迹平滑方案 2025-07-20 15:27:02 +08:00
CJKmkp d5142ad82c improve:墨迹平滑方案 2025-07-20 15:21:59 +08:00
CJKmkp 428b278c78 更新版本号 2025-07-20 15:02:03 +08:00
CJKmkp 33c1e04934 improve:issue #5 2025-07-20 15:00:11 +08:00
CJKmkp 6c075d80ba improve:issue #5 2025-07-20 12:36:21 +08:00
CJKmkp 38713108e0 improve:issue #5 2025-07-20 12:19:32 +08:00
CJKmkp 6b75fea813 improve:issue #5 2025-07-20 12:01:30 +08:00
CJKmkp de6fc84fec Revert "improve:联动模块"
This reverts commit ecd276a3a0.
2025-07-20 09:09:39 +08:00
CJKmkp ecd276a3a0 improve:联动模块 2025-07-20 00:37:13 +08:00
CJKmkp 86fbd0cebc 撤回模块改进 2025-07-20 00:25:33 +08:00
CJKmkp 6a97919f7a Revert "improve:联动模块"
This reverts commit f2e7e17bd1.
2025-07-20 00:21:16 +08:00
CJKmkp f2e7e17bd1 improve:联动模块 2025-07-20 00:16:23 +08:00
CJKmkp eedd6cee1d improve:联动模块 2025-07-20 00:13:21 +08:00
CJKmkp 25e11fa9de improve:联动模块 2025-07-20 00:09:48 +08:00
CJKmkp 4742600b86 improve:联动模块 2025-07-19 23:42:43 +08:00
CJKmkp b3e4850413 improve:插件图标 2025-07-19 22:01:02 +08:00
CJK_mkp 1085f6c57a Merge pull request #87 from InkCanvasForClass/beta
ICC CE 1.7.1.3
2025-07-19 17:41:15 +08:00
CJKmkp fd8d13447c 更新版本号 2025-07-19 17:31:08 +08:00
CJKmkp 758a3d3f99 improve:issue #79 2025-07-19 17:27:27 +08:00
CJKmkp fd8b4d94d6 improve:issue #79 2025-07-19 17:21:59 +08:00
CJKmkp fa7dae8177 fix:win7无法自动更新 2025-07-19 16:51:03 +08:00
CJKmkp 21638218c6 improve:启动台插件 2025-07-19 16:37:44 +08:00
CJKmkp a29a414e8a improve:issue #79 2025-07-19 16:21:03 +08:00
CJKmkp 251c7f399e improve:自动更新 2025-07-19 16:03:45 +08:00
CJKmkp 1da55f2011 improve:自动更新 2025-07-19 15:36:05 +08:00
CJKmkp 856125edc2 更新版本号 2025-07-19 14:35:33 +08:00
CJKmkp 32ef30ebd8 improve:橡皮图标 2025-07-19 14:34:06 +08:00
CJKmkp 8972e42fee Revert "improve:橡皮光标"
This reverts commit 91f206aad0.
2025-07-19 14:10:29 +08:00
CJKmkp 91f206aad0 improve:橡皮光标 2025-07-19 14:04:35 +08:00
CJKmkp 5626babcdf improve:橡皮光标 2025-07-19 13:36:42 +08:00
CJKmkp 98175152db improve:光标显示 2025-07-19 13:35:28 +08:00
CJKmkp 817ade3e34 improve:光标显示 2025-07-19 10:56:30 +08:00
CJKmkp ab5493f8c4 improve:光标显示 2025-07-19 10:18:16 +08:00
CJKmkp 796bd99377 improve:插件功能 2025-07-18 21:51:32 +08:00
CJKmkp a0dc60a403 improve:联动模块 2025-07-18 21:29:55 +08:00
CJKmkp a92e58abf1 improve:白板画布 2025-07-18 19:20:06 +08:00
CJKmkp e8f0793feb 更新版本号 2025-07-18 18:52:01 +08:00
CJKmkp 2d7eff8205 fix:触摸问题 2025-07-18 17:59:17 +08:00
CJKmkp 6412892985 fix:触摸问题 2025-07-18 17:02:17 +08:00
CJKmkp 69ae0ffc71 fix:触摸问题 2025-07-18 17:01:18 +08:00
CJKmkp 854be23cfb fix:触摸问题 2025-07-18 16:28:50 +08:00
CJKmkp 02e143217e fix:触摸问题 2025-07-18 16:21:39 +08:00
CJKmkp 4feec82b03 fix:触摸问题 2025-07-18 16:12:04 +08:00
CJKmkp 938ca648f1 撤销操作 2025-07-18 11:53:55 +08:00
CJKmkp ee018ea287 撤销 Commit ab88c34 2025-07-18 11:50:44 +08:00
CJKmkp 8ab60ad000 撤销 Commit ab88c34 2025-07-18 11:49:00 +08:00
CJKmkp 17eaf34500 撤销 Commit b46cbcc 2025-07-18 11:47:41 +08:00
CJKmkp 2485958f6e 撤销Commit 3645906 2025-07-18 11:38:32 +08:00
CJKmkp 7ac3a22fa6 improve:插件功能 2025-07-17 22:30:12 +08:00
CJKmkp 285f211f50 add:备份功能 2025-07-16 15:28:36 +08:00
59 changed files with 6220 additions and 2258 deletions
+1 -1
View File
@@ -1 +1 @@
1.7.1.0
1.7.2.0
+40 -4
View File
@@ -5,17 +5,15 @@ using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Win32;
using System.Security;
using System.Security;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Threading;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using MessageBox = System.Windows.MessageBox;
using Window = System.Windows.Window;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Net;
namespace Ink_Canvas
{
@@ -48,6 +46,9 @@ namespace Ink_Canvas
public App()
{
// 配置TLS协议以支持Windows 7
ConfigureTlsForWindows7();
// 如果是看门狗子进程,直接进入看门狗主循环并终止主流程
var args = Environment.GetCommandLineArgs();
if (args.Length >= 2 && args[1] == "--watchdog")
@@ -75,6 +76,41 @@ namespace Ink_Canvas
this.Exit += App_Exit; // 注册退出事件
}
// 新增:配置TLS协议以支持Windows 7
private void ConfigureTlsForWindows7()
{
try
{
// 检测操作系统版本
var osVersion = Environment.OSVersion;
bool isWindows7 = osVersion.Version.Major == 6 && osVersion.Version.Minor == 1;
if (isWindows7)
{
LogHelper.WriteLogToFile("检测到Windows 7系统,配置TLS协议支持", LogHelper.LogType.Info);
// 启用所有TLS版本以支持Windows 7
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
// 配置ServicePointManager以支持Windows 7
ServicePointManager.DefaultConnectionLimit = 10;
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
LogHelper.WriteLogToFile("TLS协议配置完成,已启用TLS 1.2/1.1/1.0支持", LogHelper.LogType.Info);
}
else
{
// 对于更新的Windows版本,不进行任何TLS配置,使用系统默认设置
LogHelper.WriteLogToFile($"检测到Windows版本: {osVersion.VersionString},使用系统默认TLS配置", LogHelper.LogType.Info);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"配置TLS协议时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
// 新增:初始化崩溃监听器
private void InitializeCrashListeners()
{
+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.1.0")]
[assembly: AssemblyFileVersion("1.7.1.0")]
[assembly: AssemblyVersion("1.7.2.0")]
[assembly: AssemblyFileVersion("1.7.2.0")]
@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Ink;
using System.Windows.Input;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 适合手写/触摸的墨迹平滑方案:指数平滑+等距重采样+Catmull-Rom样条插值,防止自交和异常填充
/// </summary>
public class AdvancedBezierSmoothing
{
public double SmoothingStrength { get; set; } = 0.8;
public double ResampleInterval { get; set; } = 0.8;
public int InterpolationSteps { get; set; } = 64;
public Stroke SmoothStroke(Stroke stroke)
{
if (stroke == null || stroke.StylusPoints.Count < 2)
return stroke;
var originalPoints = stroke.StylusPoints.ToList();
var smoothedPoints = ApplyExponentialSmoothing(originalPoints, SmoothingStrength);
var resampledPoints = ResampleEquidistant(smoothedPoints, ResampleInterval);
var interpolatedPoints = SlidingBezierFit(resampledPoints, 4, 24);
var finalPoints = ApplyExponentialSmoothing(interpolatedPoints, 0.5); // 二次平滑
var ultraSmoothPoints = SlidingWindowSmooth(finalPoints, 7); // 滑动窗口平滑
var smoothedStroke = new Stroke(new StylusPointCollection(ultraSmoothPoints))
{
DrawingAttributes = stroke.DrawingAttributes.Clone()
};
return smoothedStroke;
}
private List<StylusPoint> ApplyExponentialSmoothing(List<StylusPoint> points, double alpha)
{
var result = new List<StylusPoint>();
if (points.Count == 0) return result;
result.Add(points[0]);
double lastX = points[0].X;
double lastY = points[0].Y;
float lastPressure = points[0].PressureFactor;
for (int i = 1; i < points.Count; i++)
{
var p = points[i];
lastX = alpha * p.X + (1 - alpha) * lastX;
lastY = alpha * p.Y + (1 - alpha) * lastY;
lastPressure = (float)(alpha * p.PressureFactor + (1 - alpha) * lastPressure);
if (lastPressure < 0.1f) lastPressure = 0.1f;
result.Add(new StylusPoint(lastX, lastY, lastPressure));
}
return result;
}
private List<StylusPoint> ResampleEquidistant(List<StylusPoint> points, double interval = 2.0)
{
var result = new List<StylusPoint>();
if (points.Count == 0) return result;
result.Add(points[0]);
double accumulated = 0;
for (int i = 1; i < points.Count; i++)
{
var prev = result.Last();
var curr = points[i];
double dx = curr.X - prev.X;
double dy = curr.Y - prev.Y;
double dist = Math.Sqrt(dx * dx + dy * dy);
if (dist + accumulated >= interval)
{
double t = (interval - accumulated) / dist;
double x = prev.X + t * dx;
double y = prev.Y + t * dy;
float pressure = (float)(prev.PressureFactor * (1 - t) + curr.PressureFactor * t);
if (pressure < 0.1f) pressure = 0.1f;
var newPoint = new StylusPoint(x, y, pressure);
result.Add(newPoint);
accumulated = 0;
i--; // 重新处理当前点
}
else
{
accumulated += dist;
}
}
return result;
}
private List<StylusPoint> SlidingBezierFit(List<StylusPoint> points, int window = 4, int steps = 24)
{
var result = new List<StylusPoint>();
if (points.Count < window) return points;
for (int i = 0; i <= points.Count - window; i++)
{
var p0 = points[i];
var p1 = points[i + 1];
var p2 = points[i + 2];
var p3 = points[i + 3];
for (int j = 0; j < steps; j++)
{
double t = (double)j / steps;
var pt = CubicBezier(p0, p1, p2, p3, t);
result.Add(pt);
}
}
// 保证最后一个点被包含
result.Add(points.Last());
return result;
}
private StylusPoint CubicBezier(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3, double t)
{
double u = 1 - t;
double tt = t * t;
double uu = u * u;
double uuu = uu * u;
double ttt = tt * t;
double x = uuu * p0.X + 3 * uu * t * p1.X + 3 * u * tt * p2.X + ttt * p3.X;
double y = uuu * p0.Y + 3 * uu * t * p1.Y + 3 * u * tt * p2.Y + ttt * p3.Y;
float pressure = (float)(p1.PressureFactor * (1 - t) + p2.PressureFactor * t);
if (pressure < 0.1f) pressure = 0.1f;
return new StylusPoint(x, y, pressure);
}
private List<StylusPoint> SlidingWindowSmooth(List<StylusPoint> points, int window = 5)
{
var result = new List<StylusPoint>();
int half = window / 2;
for (int i = 0; i < points.Count; i++)
{
double sumX = 0, sumY = 0, sumP = 0;
int count = 0;
for (int j = Math.Max(0, i - half); j <= Math.Min(points.Count - 1, i + half); j++)
{
sumX += points[j].X;
sumY += points[j].Y;
sumP += points[j].PressureFactor;
count++;
}
result.Add(new StylusPoint(sumX / count, sumY / count, (float)(sumP / count)));
}
return result;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,6 @@
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Forms;
namespace Ink_Canvas.Helpers
{
-1
View File
@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Windows;
namespace Ink_Canvas.Helpers {
internal class DelAutoSavedFiles {
-4
View File
@@ -1,9 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace Ink_Canvas.Helpers
+10 -104
View File
@@ -23,44 +23,6 @@ namespace Ink_Canvas.Helpers
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("shell32.dll")]
private static extern IntPtr SHAppBarMessage(uint dwMessage, ref APPBARDATA pData);
[DllImport("user32.dll")]
private static extern int SystemParametersInfo(uint uiAction, uint uiParam, IntPtr pvParam, uint fWinIni);
[DllImport("user32.dll")]
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
[DllImport("user32.dll")]
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
private const uint ABM_GETTASKBARPOS = 0x00000005;
private const uint ABM_GETSTATE = 0x00000004;
private const int SPI_GETWORKAREA = 0x0030;
private const uint MONITOR_DEFAULTTOPRIMARY = 1;
private const int ABS_AUTOHIDE = 0x0000001;
[StructLayout(LayoutKind.Sequential)]
private struct MONITORINFO
{
public int cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public uint uCallbackMessage;
public uint uEdge;
public RECT rc;
public IntPtr lParam;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public int Left;
@@ -72,72 +34,6 @@ namespace Ink_Canvas.Helpers
public int Height => Bottom - Top;
}
/// <summary>
/// 获取Windows任务栏的高度(仅计算任务栏,不包括其他应用的停靠栏)
/// </summary>
/// <param name="screen">当前屏幕</param>
/// <param name="dpiScaleY">DPI缩放Y值</param>
/// <returns>任务栏高度</returns>
public static double GetTaskbarHeight(System.Windows.Forms.Screen screen, double dpiScaleY)
{
try
{
// 创建APPBARDATA结构
var abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
// 获取任务栏状态
IntPtr state = SHAppBarMessage(ABM_GETSTATE, ref abd);
bool isAutoHide = (state.ToInt32() & ABS_AUTOHIDE) == ABS_AUTOHIDE;
// 如果任务栏是自动隐藏的,返回0
if (isAutoHide)
{
LogHelper.WriteLogToFile("任务栏处于自动隐藏状态", LogHelper.LogType.Info);
return 0;
}
// 获取任务栏信息
IntPtr result = SHAppBarMessage(ABM_GETTASKBARPOS, ref abd);
if (result != IntPtr.Zero)
{
// 获取当前屏幕的工作区
RECT workArea = new RECT();
SystemParametersInfo(SPI_GETWORKAREA, 0, Marshal.AllocHGlobal(Marshal.SizeOf(workArea)), 0);
// 根据任务栏位置计算高度
int taskbarHeight = 0;
// 任务栏的uEdge: 0=左, 1=上, 2=右, 3=下
switch (abd.uEdge)
{
case 1: // 上
taskbarHeight = abd.rc.Height;
break;
case 3: // 下
taskbarHeight = abd.rc.Height;
break;
case 0: // 左
case 2: // 右
// 水平任务栏不影响高度
taskbarHeight = 0;
break;
}
// 考虑DPI缩放
return taskbarHeight / dpiScaleY;
}
}
catch (Exception ex)
{
Debug.WriteLine($"获取任务栏高度出错: {ex.Message}");
LogHelper.WriteLogToFile($"获取任务栏高度出错: {ex.Message}", LogHelper.LogType.Error);
}
// 如果获取失败,回退到通用方法
return (screen.Bounds.Height - screen.WorkingArea.Height) / dpiScaleY;
}
public static string WindowTitle() {
IntPtr foregroundWindowHandle = GetForegroundWindow();
@@ -197,5 +93,15 @@ namespace Ink_Canvas.Helpers
return "Unknown";
}
}
public static double GetTaskbarHeight(System.Windows.Forms.Screen screen, double dpiScaleY)
{
// 获取工作区和屏幕高度的差值
var workingArea = screen.WorkingArea;
var bounds = screen.Bounds;
int taskbarHeight = bounds.Height - workingArea.Height;
// 考虑 DPI 缩放
return taskbarHeight / dpiScaleY;
}
}
}
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
// 由衷感謝 lindexi 提供的 《WPF 稳定的全屏化窗口方法》
// 文章鏈接:https://blog.lindexi.com/post/WPF-%E7%A8%B3%E5%AE%9A%E7%9A%84%E5%85%A8%E5%B1%8F%E5%8C%96%E7%AA%97%E5%8F%A3%E6%96%B9%E6%B3%95.html
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
using System.Windows.Interop;
using System.Windows;
@@ -3,7 +3,6 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using iNKORE.UI.WPF.Modern.Controls;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
@@ -71,7 +71,7 @@
<!-- 操作按钮 -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5">
<Button x:Name="BtnAdd" Content="添加应用" Padding="10,5" Margin="0,5,5,5" Click="BtnAdd_Click"/>
<Button x:Name="BtnAdd" Content="添加" Padding="10,5" Margin="0,5,5,5" Click="BtnAdd_Click"/>
<Button x:Name="BtnEdit" Content="编辑" Padding="10,5" Margin="5" Click="BtnEdit_Click"/>
<Button x:Name="BtnDelete" Content="删除" Padding="10,5" Margin="5" Click="BtnDelete_Click"/>
</StackPanel>
@@ -88,34 +88,23 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
}
/// <summary>
/// 添加应用按钮点击事件
/// 添加按钮点击事件
/// </summary>
private void BtnAdd_Click(object sender, RoutedEventArgs e)
{
try
{
// 弹出文件选择对话框
OpenFileDialog dialog = new OpenFileDialog
// 创建新的启动项
LauncherItem item = new LauncherItem
{
Title = "选择应用程序",
Filter = "应用程序 (*.exe)|*.exe|所有文件 (*.*)|*.*",
Multiselect = false
Name = "",
Path = "",
IsVisible = true,
Position = -1 // 让插件管理器分配位置
};
if (dialog.ShowDialog() == true)
{
// 创建新的启动项
LauncherItem item = new LauncherItem
{
Name = System.IO.Path.GetFileNameWithoutExtension(dialog.FileName),
Path = dialog.FileName,
IsVisible = true,
Position = -1 // 让插件管理器分配位置
};
// 显示编辑对话框
EditLauncherItem(item, true);
}
// 直接显示编辑对话框
EditLauncherItem(item, true);
}
catch (Exception ex)
{
@@ -217,7 +206,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
// 创建简单的编辑窗口
Window editWindow = new Window
{
Title = isNew ? "添加应用" : "编辑应用",
Title = isNew ? "添加" : "编辑应用",
Width = 400,
Height = 200,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
@@ -292,6 +281,17 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
if (dialog.ShowDialog() == true)
{
pathTextBox.Text = dialog.FileName;
// 如果选择的是.exe文件,自动获取文件名填入名称字段
if (System.IO.Path.GetExtension(dialog.FileName).ToLower() == ".exe")
{
string fileName = System.IO.Path.GetFileNameWithoutExtension(dialog.FileName);
// 只有在名称字段为空或者是新建项目时才自动填入
if (string.IsNullOrWhiteSpace(nameTextBox.Text) || isNew)
{
nameTextBox.Text = fileName;
}
}
}
};
@@ -21,7 +21,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
public override string Description => "在浮动栏添加一个启动台按钮,可快速启动常用应用程序。";
public override Version Version => new Version(1, 0, 0);
public override Version Version => new Version(1, 0, 1);
public override string Author => "ICC CE 团队";
@@ -0,0 +1,178 @@
using System;
using System.IO;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// ICCPP 插件适配器,用于加载和管理 .iccpp 格式的插件
/// </summary>
public class ICCPPPluginAdapter : PluginBase
{
private readonly byte[] _pluginData;
private readonly string _pluginPath;
private readonly string _pluginName;
private readonly Version _pluginVersion;
private bool _isInitialized = false;
/// <summary>
/// 创建 ICCPP 插件适配器
/// </summary>
/// <param name="pluginPath">插件文件路径</param>
/// <param name="pluginData">插件文件数据</param>
public ICCPPPluginAdapter(string pluginPath, byte[] pluginData)
{
_pluginPath = pluginPath;
_pluginData = pluginData;
PluginPath = pluginPath;
// 从文件名获取插件名称
_pluginName = Path.GetFileNameWithoutExtension(pluginPath);
_pluginVersion = new Version(1, 0, 0); // 默认版本
// 尝试从插件数据中读取更多信息
TryReadPluginMetadata();
}
public ICCPPPluginAdapter()
{
_pluginPath = string.Empty;
_pluginData = new byte[0];
PluginPath = string.Empty;
_pluginName = "ICCPPPlugin";
_pluginVersion = new Version(1, 0, 0);
// 可选:初始化其他字段
}
/// <summary>
/// 尝试从插件数据中读取元数据
/// </summary>
private void TryReadPluginMetadata()
{
try
{
// 这里可以根据 .iccpp 文件的实际格式解析元数据
// 例如,如果文件有特定的头部结构,可以在这里解析
// 示例:如果前100字节包含元数据
if (_pluginData.Length > 100)
{
// 解析元数据的代码...
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"解析插件 {_pluginName} 元数据时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#region IPlugin
/// <summary>
/// 插件名称
/// </summary>
public override string Name => _pluginName;
/// <summary>
/// 插件描述
/// </summary>
public override string Description => $"{_pluginName} (ICCPP 格式插件)";
/// <summary>
/// 插件版本
/// </summary>
public override Version Version => _pluginVersion;
/// <summary>
/// 插件作者
/// </summary>
public override string Author => "未知";
/// <summary>
/// 是否为内置插件
/// </summary>
public override bool IsBuiltIn => false;
/// <summary>
/// 初始化插件
/// </summary>
public override void Initialize()
{
if (_isInitialized) return;
try
{
// 这里可以添加 .iccpp 插件的初始化逻辑
// 例如,根据文件格式加载特定资源
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已初始化", LogHelper.LogType.Info);
_isInitialized = true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化 ICCPP 插件 {Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 启用插件
/// </summary>
public override void Enable()
{
if (IsEnabled) return;
try
{
// 这里可以添加 .iccpp 插件的启用逻辑
// 例如,加载动态库、注册事件等
base.Enable(); // 设置启用状态并触发事件
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已启用", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启用 ICCPP 插件 {Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 禁用插件
/// </summary>
public override void Disable()
{
if (!IsEnabled) return;
try
{
// 这里可以添加 .iccpp 插件的禁用逻辑
// 例如,卸载动态库、注销事件等
base.Disable(); // 设置禁用状态并触发事件
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已禁用", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"禁用 ICCPP 插件 {Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 清理插件资源
/// </summary>
public override void Cleanup()
{
try
{
// 这里可以添加 .iccpp 插件的清理逻辑
// 例如,释放资源等
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已清理资源", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理 ICCPP 插件 {Name} 资源时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
}
}
-1
View File
@@ -1,5 +1,4 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
+96 -100
View File
@@ -195,8 +195,12 @@ namespace Ink_Canvas.Helpers.Plugins
return;
}
// 获取所有插件文件
var pluginFiles = Directory.GetFiles(PluginsDirectory, "*.iccpp", SearchOption.TopDirectoryOnly);
// 获取所有插件文件(支持 .iccpp 和 .dll 格式)
var pluginFiles = Directory.GetFiles(PluginsDirectory, "*.iccpp", SearchOption.TopDirectoryOnly)
.Concat(Directory.GetFiles(PluginsDirectory, "*.dll", SearchOption.TopDirectoryOnly))
.ToArray();
LogHelper.WriteLogToFile($"发现 {pluginFiles.Length} 个外部插件文件", LogHelper.LogType.Info);
foreach (var pluginFile in pluginFiles)
{
@@ -222,6 +226,14 @@ namespace Ink_Canvas.Helpers.Plugins
string fileHash = CalculateFileHash(pluginPath);
_pluginHashes[pluginPath] = fileHash;
// 检查文件扩展名
string extension = Path.GetExtension(pluginPath).ToLowerInvariant();
if (extension == ".iccpp")
{
// 创建 ICCPP 插件适配器
return CreateICCPPPluginAdapter(pluginPath);
}
// 加载插件程序集
Assembly pluginAssembly = LoadPluginAssembly(pluginPath);
if (pluginAssembly == null) return null;
@@ -253,16 +265,48 @@ namespace Ink_Canvas.Helpers.Plugins
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"实例化插件 {pluginType.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"实例化插件类型 {pluginType.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
LogHelper.WriteLogToFile($"在程序集 {Path.GetFileName(pluginPath)} 中未找到有效的插件类型", LogHelper.LogType.Warning);
return null;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载插件 {Path.GetFileName(pluginPath)} 时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"加载外部插件 {Path.GetFileName(pluginPath)} 时出错: {ex.Message}", LogHelper.LogType.Error);
return null;
}
}
/// <summary>
/// 创建 ICCPP 插件适配器
/// </summary>
/// <param name="pluginPath">插件文件路径</param>
/// <returns>适配的插件实例</returns>
private IPlugin CreateICCPPPluginAdapter(string pluginPath)
{
try
{
// 读取插件文件内容
byte[] pluginData = File.ReadAllBytes(pluginPath);
// 创建适配器插件实例
var pluginAdapter = new ICCPPPluginAdapter(pluginPath, pluginData);
// 添加到插件列表
Plugins.Add(pluginAdapter);
LogHelper.WriteLogToFile($"已创建 ICCPP 插件适配器: {pluginAdapter.Name} 来自 {Path.GetFileName(pluginPath)}",
LogHelper.LogType.Info);
return pluginAdapter;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"创建 ICCPP 插件适配器时出错: {ex.Message}", LogHelper.LogType.Error);
return null;
}
return null;
}
/// <summary>
@@ -280,7 +324,7 @@ namespace Ink_Canvas.Helpers.Plugins
return loadedAssembly;
}
// 加载程序集
// 直接加载程序集
Assembly pluginAssembly = Assembly.LoadFrom(pluginPath);
_loadedAssemblies[pluginPath] = pluginAssembly;
@@ -483,117 +527,63 @@ namespace Ink_Canvas.Helpers.Plugins
{
try
{
LogHelper.WriteLogToFile($"开始重载插件: {plugin.Name}", LogHelper.LogType.Info);
// 记录插件状态和信息
bool wasEnabled = plugin.IsEnabled;
string pluginPath = plugin.PluginPath;
string pluginTypeName = plugin.GetType().FullName;
// 记录日志,方便排查问题
LogHelper.WriteLogToFile($"重载前插件状态 - 类型: {pluginTypeName}, 状态: {(wasEnabled ? "" : "")}", LogHelper.LogType.Info);
// 如果配置中有该插件的状态,记录配置中的状态
if (PluginStates.TryGetValue(pluginTypeName, out bool currentConfigState))
if (string.IsNullOrEmpty(pluginPath) || !File.Exists(pluginPath))
{
LogHelper.WriteLogToFile($"配置中插件状态: {(currentConfigState ? "" : "")}", LogHelper.LogType.Info);
LogHelper.WriteLogToFile($"无法重新加载插件 {plugin.Name}: 插件文件不存在", LogHelper.LogType.Error);
return;
}
// 卸载插件,但不从PluginStates中移除状态信息
UnloadPlugin(plugin);
LogHelper.WriteLogToFile($"开始热重载插件: {plugin.Name} ({Path.GetFileName(pluginPath)})", LogHelper.LogType.Info);
// 清除程序集缓存,确保加载最新版本
_loadedAssemblies.Remove(pluginPath);
// 保存插件的当前状态
bool wasEnabled = plugin.IsEnabled;
string pluginTypeName = plugin.GetType().FullName;
// 卸载插件
UnloadPlugin(plugin, false);
// 从加载缓存中移除
if (_loadedAssemblies.ContainsKey(pluginPath))
{
_loadedAssemblies.Remove(pluginPath);
}
// 计算新的文件哈希
string newHash = CalculateFileHash(pluginPath);
_pluginHashes[pluginPath] = newHash;
// 重新加载插件
IPlugin newPlugin = LoadExternalPlugin(pluginPath);
if (newPlugin != null)
{
// 更新配置中的插件状态
string newPluginTypeName = newPlugin.GetType().FullName;
// 恢复插件状态
if (wasEnabled)
{
newPlugin.Enable();
}
// 如果插件类型名称变化,需要更新配置
if (newPluginTypeName != pluginTypeName && PluginStates.ContainsKey(pluginTypeName))
// 更新配置(如果类型名称变化)
string newPluginTypeName = newPlugin.GetType().FullName;
if (pluginTypeName != newPluginTypeName && PluginStates.ContainsKey(pluginTypeName))
{
bool state = PluginStates[pluginTypeName];
PluginStates.Remove(pluginTypeName);
PluginStates[newPluginTypeName] = state;
LogHelper.WriteLogToFile($"插件类型名称已变更: {pluginTypeName} -> {newPluginTypeName}, 已更新配置", LogHelper.LogType.Info);
_configDirty = true;
SaveConfig();
}
// 应用正确的状态
bool shouldBeEnabled = false;
if (PluginStates.TryGetValue(newPluginTypeName, out bool storedConfigState))
{
shouldBeEnabled = storedConfigState;
LogHelper.WriteLogToFile($"从配置获取插件状态: {(shouldBeEnabled ? "" : "")}", LogHelper.LogType.Info);
}
else
{
shouldBeEnabled = wasEnabled;
PluginStates[newPluginTypeName] = shouldBeEnabled;
LogHelper.WriteLogToFile($"使用之前的状态: {(shouldBeEnabled ? "" : "")}", LogHelper.LogType.Info);
}
LogHelper.WriteLogToFile($"插件 {newPlugin.Name} v{newPlugin.Version} 热重载成功", LogHelper.LogType.Info);
// 获取重载后的实际状态
bool currentState = newPlugin is PluginBase pluginBaseState && pluginBaseState.IsEnabled;
LogHelper.WriteLogToFile($"重载后实际状态: {(currentState ? "" : "")}", LogHelper.LogType.Info);
// 根据应该启用的状态启用或禁用插件
if (shouldBeEnabled != currentState)
{
if (shouldBeEnabled)
{
newPlugin.Enable();
LogHelper.WriteLogToFile($"插件 {newPlugin.Name} 已重载并启用", LogHelper.LogType.Info);
}
else
{
newPlugin.Disable();
LogHelper.WriteLogToFile($"插件 {newPlugin.Name} 已重载并禁用", LogHelper.LogType.Info);
}
// 检查状态是否正确应用
currentState = newPlugin is PluginBase reloadedBase && reloadedBase.IsEnabled;
LogHelper.WriteLogToFile($"应用状态后实际状态: {(currentState ? "" : "")}", LogHelper.LogType.Info);
if (currentState != shouldBeEnabled)
{
LogHelper.WriteLogToFile($"警告: 插件状态应用失败,目标状态: {(shouldBeEnabled ? "" : "")}, 实际状态: {(currentState ? "" : "")}", LogHelper.LogType.Warning);
}
}
else
{
LogHelper.WriteLogToFile($"插件 {newPlugin.Name} 已重载并保持{(shouldBeEnabled ? "" : "")}状态", LogHelper.LogType.Info);
}
// 保存插件设置
if (newPlugin is PluginBase pluginBaseInstance)
{
try
{
// 保存插件设置(与启用状态无关)
pluginBaseInstance.SavePluginSettings();
LogHelper.WriteLogToFile($"已保存插件 {newPlugin.Name} 设置", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存插件 {newPlugin.Name} 设置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
// 立即保存配置
LogHelper.WriteLogToFile($"重载后保存插件配置...", LogHelper.LogType.Info);
SaveConfig();
// 通知UI刷新
NotifyUIRefresh();
}
else
{
LogHelper.WriteLogToFile($"插件 {plugin.Name} 重载失败: 无法加载新插件", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"插件 {plugin.Name} 重载失败", LogHelper.LogType.Error);
}
// 更新UI
NotifyUIRefresh();
}
catch (Exception ex)
{
@@ -605,11 +595,14 @@ namespace Ink_Canvas.Helpers.Plugins
/// 卸载插件
/// </summary>
/// <param name="plugin">要卸载的插件</param>
/// <param name="removeFromConfig">是否从配置中移除插件状态(默认为false</param>
/// <param name="removeFromConfig">是否从配置中移除</param>
public void UnloadPlugin(IPlugin plugin, bool removeFromConfig = false)
{
try
{
// 保存插件名称,以便在卸载后使用
string pluginName = plugin.Name;
// 如果插件已启用,先禁用它
if (plugin is PluginBase pluginBase && pluginBase.IsEnabled)
{
@@ -633,11 +626,11 @@ namespace Ink_Canvas.Helpers.Plugins
}
}
LogHelper.WriteLogToFile($"已卸载插件: {plugin.Name}", LogHelper.LogType.Info);
LogHelper.WriteLogToFile($"已卸载插件: {pluginName}", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"卸载插件 {plugin.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"卸载插件时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
@@ -656,6 +649,9 @@ namespace Ink_Canvas.Helpers.Plugins
return false;
}
// 保存插件名称,以便在删除后使用
string pluginName = plugin.Name;
// 获取插件路径
string pluginPath = null;
if (plugin is PluginBase pluginBase)
@@ -681,12 +677,12 @@ namespace Ink_Canvas.Helpers.Plugins
// 保存配置
SaveConfig();
LogHelper.WriteLogToFile($"已删除插件: {plugin.Name}", LogHelper.LogType.Info);
LogHelper.WriteLogToFile($"已删除插件: {pluginName}", LogHelper.LogType.Info);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"删除插件 {plugin.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"删除插件时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
-1
View File
@@ -1,4 +1,3 @@
using System;
using System.IO;
namespace Ink_Canvas.Helpers
+24 -3
View File
@@ -2,11 +2,11 @@
using System.Collections.Generic;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows; // Added for UIElement
namespace Ink_Canvas.Helpers
{
public class TimeMachine
public partial class TimeMachine
{
private readonly List<TimeMachineHistory> _currentStrokeHistory = new List<TimeMachineHistory>();
@@ -139,6 +139,7 @@ namespace Ink_Canvas.Helpers
//这里说一下 Tuple的 Value1 是初始值 ; Value 2 是改变值
public Dictionary<Stroke, Tuple<StylusPointCollection, StylusPointCollection>> StylusPointDictionary;
public Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>> DrawingAttributes;
public UIElement InsertedElement; // 新增
public TimeMachineHistory(StrokeCollection currentStroke, TimeMachineHistoryType commitType, bool strokeHasBeenCleared)
{
CommitType = commitType;
@@ -163,6 +164,11 @@ namespace Ink_Canvas.Helpers
StrokeHasBeenCleared = strokeHasBeenCleared;
ReplacedStroke = replacedStroke;
}
public TimeMachineHistory(UIElement element, TimeMachineHistoryType commitType) // 新增
{
CommitType = commitType;
InsertedElement = element;
}
}
public enum TimeMachineHistoryType
@@ -171,6 +177,21 @@ namespace Ink_Canvas.Helpers
ShapeRecognition,
Clear,
Manipulation,
DrawingAttributes
DrawingAttributes,
ElementInsert // 新增
}
public partial class TimeMachine // 新增partial,便于扩展
{
public void CommitElementInsertHistory(UIElement element)
{
if (_currentIndex + 1 < _currentStrokeHistory.Count)
{
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
}
_currentStrokeHistory.Add(new TimeMachineHistory(element, TimeMachineHistoryType.ElementInsert));
_currentIndex = _currentStrokeHistory.Count - 1;
NotifyUndoRedoState();
}
}
}
+2
View File
@@ -132,6 +132,8 @@
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="WindowsFormsIntegration" />
</ItemGroup>
<ItemGroup>
@@ -134,6 +134,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
<PackageReference Include="OSVersionExt" Version="3.0.0" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<COMReference Include="IWshRuntimeLibrary">
@@ -405,6 +406,7 @@
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Data.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Drawing.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.IO.Compression.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.IO.Compression.FileSystem.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Net.Http.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Numerics.dll" />
+181 -19
View File
@@ -33,7 +33,13 @@
Stylus.IsTouchFeedbackEnabled="False">
<!--资源中添加命令-->
<Window.Resources>
<c:IsEnabledToOpacityConverter x:Key="IsEnabledToOpacityConverter" />
<ResourceDictionary>
<!-- 合并新橡皮擦资源 -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MainWindow_cs/MW_Eraser.xaml"/>
</ResourceDictionary.MergedDictionaries>
<c:IsEnabledToOpacityConverter x:Key="IsEnabledToOpacityConverter" />
<c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<c:IntNumberToString x:Key="IntNumberToString" />
<c:IntNumberToString2 x:Key="IntNumberToString2" />
@@ -121,6 +127,7 @@
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Window.Resources>
<!--输入命令绑定-->
<Window.InputBindings>
@@ -203,8 +210,18 @@
<!-- Plugins -->
<Button Width="40" Height="40" Margin="0,5,0,0" Style="{StaticResource NavButton}"
Click="NavPlugins_Click" Tag="plugins" ToolTip="插件">
<TextBlock Text="&#xE735;" FontFamily="Segoe MDL2 Assets" FontSize="18"/>
<Image Width="20" Height="20">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing Brush="White"
Geometry="M342.826667 213.333333a149.333333 149.333333 0 0 1 295.68 0H682.666667a128 128 0 0 1 128 128v44.16a149.333333 149.333333 0 0 1 0 295.68V725.333333a128 128 0 0 1-128 128h-128v-21.333333a64 64 0 0 0-128 0v21.333333H298.666667a128 128 0 0 1-128-128v-128h21.333333a64 64 0 0 0 0-128H170.666667V341.333333a128 128 0 0 1 128-128h44.16zM426.666667 234.666667V298.666667H298.666667q-17.664 0-30.165334 12.501333T256 341.333333v56.576q22.528 10.752 41.6 29.866667Q341.333333 471.466667 341.333333 533.333333q0 61.866667-43.733333 105.6-19.072 19.072-41.6 29.824V725.333333q0 17.664 12.501333 30.165334T298.666667 768h56.576q10.752-22.528 29.866666-41.6Q428.8 682.666667 490.666667 682.666667q61.866667 0 105.6 43.733333 19.072 19.072 29.824 41.6H682.666667q17.664 0 30.165333-12.501333T725.333333 725.333333v-128h64a64 64 0 0 0 0-128H725.333333V341.333333q0-17.664-12.501333-30.165333T682.666667 298.666667h-128V234.666667a64 64 0 1 0-128 0z"/>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Button>
<!-- Startup -->
<Button Width="40" Height="40" Margin="0,10,0,0" Style="{StaticResource NavButton}"
Click="NavStartup_Click" Tag="startup" ToolTip="启动设置">
@@ -601,6 +618,13 @@
FontSize="26" />
</GroupBox.Header>
<ui:SimpleStackPanel Spacing="6">
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="窗口无焦点模式" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchNoFocusMode"
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchNoFocusMode_Toggled" />
</ui:SimpleStackPanel>
<ui:ToggleSwitch OnContent="" OffContent=""
Name="ToggleSwitchIsAutoUpdate" Header="自动检查更新"
FontFamily="Microsoft YaHei UI"
@@ -628,8 +652,10 @@
Width="120" HorizontalAlignment="Left" Click="FixVersionButton_Click"/>
<TextBlock Text="# 版本修复会根据当前选择的通道下载最新版本并执行安装,可用于修复损坏的安装"
TextWrapping="Wrap" Foreground="#a1a1aa" />
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Padding="12"
<Button x:Name="HistoryRollbackButton" Content="历史版本回滚" Width="120" Margin="0,10,0,0" Click="HistoryRollbackButton_Click"/>
<TextBlock Text="# 历史版本回滚,点击后会弹出相应页面供用户手动回滚到之前的版本"
TextWrapping="Wrap" Foreground="#a1a1aa" />
<Border BorderBrush="White" BorderThickness="1" CornerRadius="5" Padding="12"
Visibility="{Binding ElementName=ToggleSwitchIsAutoUpdateWithSilence, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}">
<ui:SimpleStackPanel Spacing="12">
<TextBlock
@@ -638,12 +664,12 @@
<ui:SimpleStackPanel x:Name="AutoUpdateTimePeriodBlock" Spacing="12">
<ui:SimpleStackPanel Spacing="12">
<TextBlock Text="静默更新时间段" FontSize="15" FontWeight="Bold"
TextWrapping="Wrap" Foreground="Black" />
TextWrapping="Wrap" Foreground="#fafafa" />
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="12">
<ui:SimpleStackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
Text="起始时间" FontSize="14" TextWrapping="Wrap"
Foreground="Black" />
Foreground="#fafafa" />
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox"
Width="90"
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged" />
@@ -651,7 +677,7 @@
<ui:SimpleStackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
Text="终止时间" FontSize="14" TextWrapping="Wrap"
Foreground="Black" />
Foreground="#fafafa" />
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
Width="90"
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged" />
@@ -748,6 +774,12 @@
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchClearCanvasAndClearTimeMachine_Toggled" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="插入图片时自动压缩(大于1920x1080" VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchCompressPicturesUploaded"
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchCompressPicturesUploaded_Toggled" />
</ui:SimpleStackPanel>
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
StrokeThickness="1" Margin="0,4,0,4" />
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
@@ -764,12 +796,19 @@
<TextBlock Text="# 请注意,若不保留双曲线渐近线可能会有遇到撤回相关的 BUG 影响用。" TextWrapping="Wrap"
Foreground="#a1a1aa" />
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="让墨迹使用贝塞尔曲线平滑处理"
<TextBlock Foreground="#fafafa" Text="使用WPF默认贝塞尔曲线平滑"
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchFitToCurve"
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchFitToCurve_Toggled" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="使用高级曲线平滑(推荐)"
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchAdvancedBezierSmoothing"
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchAdvancedBezierSmoothing_Toggled" />
</ui:SimpleStackPanel>
</ui:SimpleStackPanel>
</GroupBox>
<!-- 新增:崩溃后操作设置 -->
@@ -819,6 +858,16 @@
</ui:SimpleStackPanel>
<TextBlock Text="# 允许选中墨迹后对墨迹进行双指或多指缩放操作(此设置不受“允许双指旋转”设置的影响)" TextWrapping="Wrap"
Foreground="#a1a1aa" />
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
StrokeThickness="1" Margin="0,4,0,4" />
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="启用手掌擦" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchEnablePalmEraser"
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchEnablePalmEraser_Toggled" />
</ui:SimpleStackPanel>
<TextBlock Text="# 开启后,两个及以上触点且触摸面积较大时自动切换为橡皮擦,抬手后恢复原编辑模式。" TextWrapping="Wrap" Foreground="#a1a1aa" />
</ui:SimpleStackPanel>
</GroupBox>
<GroupBox Name="GroupBoxInkRecognition">
@@ -904,7 +953,7 @@
<TextBlock Foreground="#fafafa" Text="长度阈值" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<Slider Name="AutoStraightenLineThresholdSlider" Width="150" Minimum="30" Maximum="300"
Value="30" TickFrequency="30" IsSnapToTickEnabled="True"
Value="80" TickFrequency="30" IsSnapToTickEnabled="True"
ValueChanged="AutoStraightenLineThresholdSlider_ValueChanged" />
<TextBlock Foreground="#fafafa" Text="{Binding ElementName=AutoStraightenLineThresholdSlider, Path=Value, StringFormat={}{0:0}}"
VerticalAlignment="Center" FontSize="14" Margin="16,0,0,0" />
@@ -1166,6 +1215,17 @@
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchSupportWPS_Toggled" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/WPS.png" Margin="0,0,6,0" Width="28"
Height="28" VerticalAlignment="Center" />
<TextBlock Foreground="#fafafa" Text="WPP进程查杀(防止WPP残留进程)" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchEnableWppProcessKill"
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchEnableWppProcessKill_Toggled" />
</ui:SimpleStackPanel>
<TextBlock Text="# 关闭后将不会自动查杀WPP残留进程,可能导致WPP关闭卡顿或无法彻底退出。" TextWrapping="Wrap" Foreground="#a1a1aa" />
<TextBlock Text="# 如果您只使用PowerPoint请不要打开WPS联动开关,如果使用WPS建议不要使用PowerPoint" TextWrapping="Wrap" Foreground="#a1a1aa" />
<Border BorderBrush="#ef4444"
BorderThickness="2" Padding="8" CornerRadius="6" Background="#991b1b">
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="4">
@@ -1544,6 +1604,14 @@
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchNotifyPreviousPage_Toggled" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="进入放映时回到首页" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent=""
Name="ToggleSwitchAlwaysGoToFirstPageOnReenter" IsOn="False"
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchAlwaysGoToFirstPageOnReenter_Toggled" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="提示隐藏幻灯片" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
@@ -1564,7 +1632,7 @@
</GroupBox>
<GroupBox>
<GroupBox.Header>
<TextBlock Margin="0,12,0,0" Text="高级" FontWeight="Bold" Foreground="#fafafa"
<TextBlock Margin="0,12,0,0" Text="高级设置" FontWeight="Bold" Foreground="#fafafa"
FontSize="26" />
</GroupBox.Header>
<ui:SimpleStackPanel Spacing="6">
@@ -1877,6 +1945,29 @@
</ui:SimpleStackPanel>
<TextBlock TextWrapping="Wrap" Foreground="#a1a1aa"
Text="# 当检测到系统分辨率变化时,会尝试检测FloatingBar是否在屏幕内显示,如果不在屏幕内显示将会尝试移动到屏幕内可见区域(分辨率调小可能会触发,如果在屏幕内不会自动调整位置,请手动挡)。" />
<!-- 添加备份相关按钮 -->
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
StrokeThickness="1" Margin="0,4,0,4" />
<TextBlock Text="设置备份与还原" FontWeight="Bold" Foreground="#fafafa"
FontSize="16" Margin="0,5,0,5" />
<TextBlock TextWrapping="Wrap" Foreground="#a1a1aa"
Text="# 可手动备份当前设置或还原之前的备份,自动更新前也会自动备份" />
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="自动更新前备份" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchIsAutoBackupBeforeUpdate"
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchIsAutoBackupBeforeUpdate_Toggled" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,5,0,0">
<Button x:Name="BtnManualBackup" Content="手动备份" Click="BtnManualBackup_Click"
Background="#2563eb" Foreground="White" Padding="12,6" Margin="0,0,12,0" />
<Button x:Name="BtnRestoreBackup" Content="还原备份" Click="BtnRestoreBackup_Click"
Background="#2563eb" Foreground="White" Padding="12,6" />
</ui:SimpleStackPanel>
</ui:SimpleStackPanel>
</GroupBox>
<GroupBox>
@@ -2191,6 +2282,15 @@
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchAutoKillHiteAnnotation_Toggled" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="鸿合屏幕书写查杀后自动进入批注" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent=""
Name="ToggleSwitchAutoEnterAnnotationAfterKillHite"
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchAutoEnterAnnotationAfterKillHite_Toggled" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Image Source="/Resources/Icons-png/VComYouJiao.png" Margin="0,0,6,0"
Width="28"
@@ -2293,7 +2393,7 @@
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchSaveFullPageStrokes_Toggled" />
</ui:SimpleStackPanel>
<TextBlock Text="# 开启后自动保存和手动保存墨迹时将以全屏模式保存而非单独保存每条墨迹" TextWrapping="Wrap" Foreground="#a1a1aa" />
<TextBlock Text="# 开启后自动保存和手动保存墨迹时将以全屏模式保存。如果存在多个画布和墨迹,将把所有页面的墨迹按照每页为单位保存进一个压缩包中(注意,白板的墨迹只能在白板模式下打开,PPT的墨迹只能在PPT放映模式下打开)" TextWrapping="Wrap" Foreground="#a1a1aa" />
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
StrokeThickness="1" Margin="0,4,0,4" />
@@ -2383,8 +2483,16 @@
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode_Toggled" />
</ui:SimpleStackPanel>
<TextBlock Text="# 开启后,退出收纳模式时将自动切换至批注模式,便于快速批注" TextWrapping="Wrap" Foreground="#a1a1aa" />
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="退出PPT放映后自动恢复浮动栏状态"
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent=""
Name="ToggleSwitchAutoFoldAfterPPTSlideShow"
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchAutoFoldAfterPPTSlideShow_Toggled" />
</ui:SimpleStackPanel>
<TextBlock Text="# 开启后,如果进入PPT放映前为收纳模式则退出后也为收纳模式,如果进入前不是收纳模式则退出后也不是收纳模式" TextWrapping="Wrap" Foreground="#a1a1aa" />
</ui:SimpleStackPanel>
</GroupBox>
<GroupBox Name="GroupBoxRandWindow">
@@ -2800,7 +2908,7 @@
</ui:ScrollViewerEx>
<!-- 底部按钮区域 -->
<Grid Grid.Row="2" VerticalAlignment="Bottom" Height="50">
<Grid Grid.Row="2" VerticalAlignment="Bottom" Height="65">
<Button FontFamily="Microsoft YaHei UI"
Width="120" Margin="10"
HorizontalAlignment="Right"
@@ -2846,7 +2954,7 @@
<Label Name="Label" Visibility="Collapsed" Foreground="Gray" Content="0" />
<Grid Name="InkCanvasGridForInkReplay">
<InkCanvas x:Name="inkCanvas" ForceCursor="True" UseCustomCursor="True"
TouchUp="Main_Grid_TouchUp" TouchDown="Main_Grid_TouchDown"
TouchDown="Main_Grid_TouchDown"
TouchMove="inkCanvas_TouchMove"
ManipulationDelta="Main_Grid_ManipulationDelta"
ManipulationCompleted="Main_Grid_ManipulationCompleted"
@@ -2861,6 +2969,13 @@
ManipulationStarting="inkCanvas_ManipulationStarting"
SelectionChanged="inkCanvas_SelectionChanged"
StrokeCollected="inkCanvas_StrokeCollected" ClipToBounds="False" Background="Transparent" />
<!-- 新橡皮擦覆盖层 - 用于高级橡皮擦系统 -->
<Border x:Name="AdvancedEraserOverlay"
Background="Transparent"
IsHitTestVisible="False"
Loaded="EraserOverlay_Loaded"
Panel.ZIndex="1000" />
</Grid>
<Canvas IsHitTestVisible="False">
@@ -3091,7 +3206,9 @@
<ui:ScrollViewerEx Name="BlackBoardLeftSidePageListScrollViewer"
Height="460"
VerticalScrollBarVisibility="Hidden"
ForceUseSmoothScroll="True">
ForceUseSmoothScroll="True"
PanningMode="VerticalOnly"
IsManipulationEnabled="True">
<ListView ScrollViewer.CanContentScroll="False" SelectionMode="Single"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
@@ -4714,6 +4831,29 @@
</Viewbox>
</Border>
</Border>
<Border x:Name="BoardInsertImage" Width="60" Height="50" MouseDown="Border_MouseDown"
MouseUp="InsertImage_MouseUp" BorderThickness="0,1,0,1"
BorderBrush="#a1a1aa"
Background="#f4f4f5"
Opacity="0.95">
<Grid Margin="6,6,6,4">
<Image VerticalAlignment="Top"
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#18181b"
Geometry="F1 M24,24z M0,0z M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2-0.9 2-2V5C21,3.9 20.1,3 19,3zM19,19H5V5h14V19z M17,7c-1.1,0-2,0.9-2,2s0.9,2 2,2 2-0.9 2-2S18.1,7 17,7zM7,17l2.5-3.01 1.96,2.36 2.54-3.21L17,17H7z"/>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Text="插入图片" Foreground="Black" VerticalAlignment="Bottom"
HorizontalAlignment="Center" FontSize="12" />
</Grid>
</Border>
<Border x:Name="BoardUndo" Width="60" Height="50" MouseDown="Border_MouseDown"
MouseUp="SymbolIconUndo_MouseUp" BorderThickness="0,1,0,1"
BorderBrush="#a1a1aa"
@@ -5129,7 +5269,9 @@
<ui:ScrollViewerEx Name="BlackBoardRightSidePageListScrollViewer"
Height="460"
VerticalScrollBarVisibility="Hidden"
ForceUseSmoothScroll="True">
ForceUseSmoothScroll="True"
PanningMode="VerticalOnly"
IsManipulationEnabled="True">
<ListView ScrollViewer.CanContentScroll="False" SelectionMode="Single"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
@@ -5707,7 +5849,7 @@
</Viewbox>
<Grid Name="FloatingbarUIForInkReplay">
<Viewbox Name="ViewboxFloatingBar" Margin="100,5,0,0"
<Viewbox Name="ViewboxFloatingBar" Margin="100,5,0,0" Cursor="Arrow"
HorizontalAlignment="Left" Height="58" VerticalAlignment="Top" Width="733"
RenderTransformOrigin="0.5,0.5">
<Viewbox.LayoutTransform>
@@ -5736,7 +5878,7 @@
</Canvas>
</Border>
</Canvas>
<ui:SimpleStackPanel Margin="2,0" Name="StackPanelFloatingBar" Orientation="Horizontal">
<ui:SimpleStackPanel Margin="2,0" Name="StackPanelFloatingBar" Orientation="Horizontal" Cursor="Arrow">
<!--<ui:SimpleStackPanel Name="Cursor_Icon" MouseDown="Border_MouseDown" MouseUp="CursorIcon_Click"-->
<ui:SimpleStackPanel Name="Cursor_Icon"
MouseDown="FloatingBarToolBtnMouseDownFeedback_Panel"
@@ -7744,6 +7886,16 @@
RenderOptions.BitmapScalingMode="HighQuality" Height="17" Margin="0,3,0,0" />
<TextBlock Text="白板" Foreground="Black" FontSize="8" Margin="0,2,0,3" TextAlignment="Center" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel
x:Name="BtnExitPptFromSidebarLeft"
MouseUp="ExitPPTSlideShow_MouseUp"
Background="Transparent" Orientation="Vertical" HorizontalAlignment="Center"
Width="32" Margin="0" Visibility="Collapsed">
<Image Source="/Resources/new-icons/end-slides-show.png"
RenderOptions.BitmapScalingMode="HighQuality" Height="17" Margin="0,3,0,0" />
<TextBlock Text="退出放映" Foreground="Black" FontSize="8" Margin="0,2,0,3" TextAlignment="Center" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel
MouseUp="UnFoldFloatingBar_MouseUp"
Background="Transparent" Orientation="Vertical" HorizontalAlignment="Center"
@@ -7817,6 +7969,16 @@
RenderOptions.BitmapScalingMode="HighQuality" Height="17" Margin="0,3,0,0" />
<TextBlock Text="白板" Foreground="Black" FontSize="8" Margin="0,2,0,3" TextAlignment="Center" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel
x:Name="BtnExitPptFromSidebarRight"
MouseUp="ExitPPTSlideShow_MouseUp"
Background="Transparent" Orientation="Vertical" HorizontalAlignment="Center"
Width="32" Margin="0" Visibility="Collapsed">
<Image Source="/Resources/new-icons/end-slides-show.png"
RenderOptions.BitmapScalingMode="HighQuality" Height="17" Margin="0,3,0,0" />
<TextBlock Text="退出放映" Foreground="Black" FontSize="8" Margin="0,2,0,3" TextAlignment="Center" />
</ui:SimpleStackPanel>
<ui:SimpleStackPanel
MouseUp="UnFoldFloatingBar_MouseUp"
Background="Transparent" Orientation="Vertical" HorizontalAlignment="Center"
+321 -54
View File
@@ -1,7 +1,6 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
@@ -12,21 +11,26 @@ using File = System.IO.File;
using MessageBox = System.Windows.MessageBox;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Controls.Primitives;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32;
using System.Windows.Input;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media.Animation;
using System.Reflection;
using Brushes = System.Windows.Media.Brushes;
using Point = System.Windows.Point;
using System.Collections.Generic;
using iNKORE.UI.WPF.Modern.Controls;
namespace Ink_Canvas {
public partial class MainWindow : Window {
// 新增:每一页一个Canvas对象
private List<System.Windows.Controls.Canvas> whiteboardPages = new List<System.Windows.Controls.Canvas>();
private int currentPageIndex = 0;
private System.Windows.Controls.Canvas currentCanvas = null;
private AutoUpdateHelper.UpdateLineGroup AvailableLatestLineGroup = null;
#region Window Initialization
public MainWindow() {
@@ -109,10 +113,68 @@ namespace Ink_Canvas {
// 注册输入事件
inkCanvas.PreviewMouseDown += inkCanvas_PreviewMouseDown;
inkCanvas.StylusDown += inkCanvas_StylusDown;
inkCanvas.TouchDown += inkCanvas_TouchDown;
inkCanvas.TouchUp += inkCanvas_TouchUp;
// 初始化第一页Canvas
var firstCanvas = new System.Windows.Controls.Canvas();
whiteboardPages.Add(firstCanvas);
InkCanvasGridForInkReplay.Children.Add(firstCanvas);
currentPageIndex = 0;
ShowPage(currentPageIndex);
// 手动实现触摸滑动
double leftTouchStartY = 0;
double leftScrollStartOffset = 0;
bool leftIsTouching = false;
BlackBoardLeftSidePageListScrollViewer.TouchDown += (s, e) => {
leftIsTouching = true;
leftTouchStartY = e.GetTouchPoint(BlackBoardLeftSidePageListScrollViewer).Position.Y;
leftScrollStartOffset = BlackBoardLeftSidePageListScrollViewer.VerticalOffset;
BlackBoardLeftSidePageListScrollViewer.CaptureTouch(e.TouchDevice);
e.Handled = true;
};
BlackBoardLeftSidePageListScrollViewer.TouchMove += (s, e) => {
if (leftIsTouching) {
double currentY = e.GetTouchPoint(BlackBoardLeftSidePageListScrollViewer).Position.Y;
double delta = leftTouchStartY - currentY;
BlackBoardLeftSidePageListScrollViewer.ScrollToVerticalOffset(leftScrollStartOffset + delta);
e.Handled = true;
}
};
BlackBoardLeftSidePageListScrollViewer.TouchUp += (s, e) => {
leftIsTouching = false;
BlackBoardLeftSidePageListScrollViewer.ReleaseTouchCapture(e.TouchDevice);
e.Handled = true;
};
double rightTouchStartY = 0;
double rightScrollStartOffset = 0;
bool rightIsTouching = false;
BlackBoardRightSidePageListScrollViewer.TouchDown += (s, e) => {
rightIsTouching = true;
rightTouchStartY = e.GetTouchPoint(BlackBoardRightSidePageListScrollViewer).Position.Y;
rightScrollStartOffset = BlackBoardRightSidePageListScrollViewer.VerticalOffset;
BlackBoardRightSidePageListScrollViewer.CaptureTouch(e.TouchDevice);
e.Handled = true;
};
BlackBoardRightSidePageListScrollViewer.TouchMove += (s, e) => {
if (rightIsTouching) {
double currentY = e.GetTouchPoint(BlackBoardRightSidePageListScrollViewer).Position.Y;
double delta = rightTouchStartY - currentY;
BlackBoardRightSidePageListScrollViewer.ScrollToVerticalOffset(rightScrollStartOffset + delta);
e.Handled = true;
}
};
BlackBoardRightSidePageListScrollViewer.TouchUp += (s, e) => {
rightIsTouching = false;
BlackBoardRightSidePageListScrollViewer.ReleaseTouchCapture(e.TouchDevice);
e.Handled = true;
};
// 初始化无焦点模式开关
ToggleSwitchNoFocusMode.IsOn = Settings.Advanced.IsNoFocusMode;
ApplyNoFocusMode();
}
#endregion
#region Ink Canvas Functions
@@ -131,7 +193,15 @@ namespace Ink_Canvas {
drawingAttributes.Height = 2.5;
drawingAttributes.Width = 2.5;
drawingAttributes.IsHighlighter = false;
drawingAttributes.FitToCurve = Settings.Canvas.FitToCurve;
// 默认使用高级贝塞尔曲线平滑,如果未启用则使用原来的FitToCurve
if (Settings.Canvas.UseAdvancedBezierSmoothing)
{
drawingAttributes.FitToCurve = false;
}
else
{
drawingAttributes.FitToCurve = Settings.Canvas.FitToCurve;
}
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
inkCanvas.Gesture += InkCanvas_Gesture;
@@ -160,11 +230,42 @@ namespace Ink_Canvas {
private void inkCanvas_EditingModeChanged(object sender, RoutedEventArgs e) {
var inkCanvas1 = sender as InkCanvas;
if (inkCanvas1 == null) return;
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode(inkCanvas1);
if (Settings.Canvas.IsShowCursor) {
if (inkCanvas1.EditingMode == InkCanvasEditingMode.Ink ||
inkCanvas1.EditingMode == InkCanvasEditingMode.Select ||
drawingShapeMode != 0)
inkCanvas1.ForceCursor = true;
else
inkCanvas1.ForceCursor = false;
} else {
// 套索选择模式下始终强制显示光标,即使用户设置不显示光标
if (inkCanvas1.EditingMode == InkCanvasEditingMode.Select) {
inkCanvas1.ForceCursor = true;
} else {
inkCanvas1.ForceCursor = false;
}
}
if (inkCanvas1.EditingMode == InkCanvasEditingMode.Ink) forcePointEraser = !forcePointEraser;
// 处理高级橡皮擦覆盖层的启用/禁用
var eraserOverlay = this.FindName("AdvancedEraserOverlay") as Border;
if (eraserOverlay != null) {
if (inkCanvas1.EditingMode == InkCanvasEditingMode.EraseByPoint) {
// 橡皮擦模式下启用覆盖层
eraserOverlay.IsHitTestVisible = true;
Trace.WriteLine("Advanced Eraser: Overlay enabled in eraser mode");
} else {
// 其他模式下禁用覆盖层
eraserOverlay.IsHitTestVisible = false;
// 同时禁用高级橡皮擦系统
DisableAdvancedEraserSystem();
Trace.WriteLine("Advanced Eraser: Overlay disabled in non-eraser mode");
}
}
}
#endregion Ink Canvas
@@ -174,11 +275,49 @@ namespace Ink_Canvas {
public static Settings Settings = new Settings();
public static string settingsFileName = "Settings.json";
private bool isLoaded = false;
private bool forcePointEraser = false;
private void Window_Loaded(object sender, RoutedEventArgs e) {
loadPenCanvas();
//加载设置
LoadSettings(true);
// 检查保存路径是否可用,不可用则修正
try
{
string savePath = Settings.Automation.AutoSavedStrokesLocation;
bool needFix = false;
if (string.IsNullOrWhiteSpace(savePath) || !System.IO.Directory.Exists(savePath))
{
needFix = true;
}
else
{
// 检查是否可写
try
{
string testFile = System.IO.Path.Combine(savePath, "test.tmp");
System.IO.File.WriteAllText(testFile, "test");
System.IO.File.Delete(testFile);
}
catch
{
needFix = true;
}
}
if (needFix)
{
string newPath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "saves");
Settings.Automation.AutoSavedStrokesLocation = newPath;
if (!System.IO.Directory.Exists(newPath))
System.IO.Directory.CreateDirectory(newPath);
SaveSettingsToFile();
LogHelper.WriteLogToFile($"自动修正保存路径为: {newPath}");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检测或修正保存路径时出错: {ex.Message}", LogHelper.LogType.Error);
}
// 加载自定义背景颜色
LoadCustomBackgroundColor();
@@ -234,14 +373,8 @@ namespace Ink_Canvas {
RadioCrashSilentRestart.IsChecked = true;
else
RadioCrashNoAction.IsChecked = true;
// 注册系统关机事件处理
RegisterShutdownHandler();
// 设置默认为黑板模式
Settings.Canvas.UsingWhiteboard = false;
Settings.Canvas.CustomBackgroundColor = "#162924"; // 黑板默认颜色 RGB(22, 41, 36)
SaveSettingsToFile();
// 如果当前不是黑板模式,则切换到黑板模式
if (currentMode == 0)
@@ -264,6 +397,9 @@ namespace Ink_Canvas {
// 初始化插件系统
InitializePluginSystem();
// 确保开关和设置同步
ToggleSwitchNoFocusMode.IsOn = Settings.Advanced.IsNoFocusMode;
ApplyNoFocusMode();
}
private void SystemEventsOnDisplaySettingsChanged(object sender, EventArgs e) {
@@ -381,9 +517,46 @@ namespace Ink_Canvas {
}
}
// 辅助方法:使用多线路组下载更新
private async Task<bool> DownloadUpdateWithFallback(string version, AutoUpdateHelper.UpdateLineGroup primaryGroup, UpdateChannel channel)
{
try
{
// 如果主要线路组可用,直接使用
if (primaryGroup != null)
{
LogHelper.WriteLogToFile($"AutoUpdate | 使用主要线路组下载: {primaryGroup.GroupName}");
return await AutoUpdateHelper.DownloadSetupFile(version, primaryGroup);
}
// 如果主要线路组不可用,获取所有可用线路组
LogHelper.WriteLogToFile("AutoUpdate | 主要线路组不可用,获取所有可用线路组");
var availableGroups = await AutoUpdateHelper.GetAvailableLineGroupsOrdered(channel);
if (availableGroups.Count == 0)
{
LogHelper.WriteLogToFile("AutoUpdate | 没有可用的线路组", LogHelper.LogType.Error);
return false;
}
LogHelper.WriteLogToFile($"AutoUpdate | 使用 {availableGroups.Count} 个可用线路组进行下载");
return await AutoUpdateHelper.DownloadSetupFileWithFallback(version, availableGroups);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 下载更新时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
private async void AutoUpdate() {
// 清除之前的更新状态,确保使用新通道重新检查
AvailableLatestVersion = null;
AvailableLatestLineGroup = null;
// 使用当前选择的更新通道检查更新
AvailableLatestVersion = await AutoUpdateHelper.CheckForUpdates(null, Settings.Startup.UpdateChannel);
var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(Settings.Startup.UpdateChannel);
AvailableLatestVersion = remoteVersion;
AvailableLatestLineGroup = lineGroup;
// 声明下载状态变量,用于整个方法
bool isDownloadSuccessful = false;
@@ -415,8 +588,8 @@ namespace Ink_Canvas {
if (Settings.Startup.IsAutoUpdateWithSilence) {
LogHelper.WriteLogToFile("AutoUpdate | Silent update enabled, downloading update automatically without notification");
// 静默下载更新,传递当前选择的更新通道
isDownloadSuccessful = await AutoUpdateHelper.DownloadSetupFileAndSaveStatus(AvailableLatestVersion, "", Settings.Startup.UpdateChannel);
// 静默下载更新,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel);
if (isDownloadSuccessful) {
LogHelper.WriteLogToFile("AutoUpdate | Update downloaded successfully, will install when conditions are met");
@@ -465,8 +638,8 @@ namespace Ink_Canvas {
// 显示下载进度提示
MessageBox.Show("开始下载更新,请稍候...", "正在更新", MessageBoxButton.OK, MessageBoxImage.Information);
// 下载更新文件,传递当前选择的更新通道
isDownloadSuccessful = await AutoUpdateHelper.DownloadSetupFileAndSaveStatus(AvailableLatestVersion, "", Settings.Startup.UpdateChannel);
// 下载更新文件,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel);
if (isDownloadSuccessful) {
// 下载成功,提示用户准备安装
@@ -495,8 +668,8 @@ namespace Ink_Canvas {
// 稍后更新:静默下载,在软件关闭时自动安装
LogHelper.WriteLogToFile("AutoUpdate | User chose to update later");
// 不管设置如何,都进行下载
isDownloadSuccessful = await AutoUpdateHelper.DownloadSetupFileAndSaveStatus(AvailableLatestVersion, "", Settings.Startup.UpdateChannel);
// 不管设置如何,都进行下载,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel);
if (isDownloadSuccessful) {
LogHelper.WriteLogToFile("AutoUpdate | Update downloaded successfully, will install when application closes");
@@ -557,12 +730,22 @@ namespace Ink_Canvas {
}
// 添加一个辅助方法,根据当前编辑模式设置光标
private void SetCursorBasedOnEditingMode(InkCanvas canvas)
public void SetCursorBasedOnEditingMode(InkCanvas canvas)
{
// 套索选择模式下光标始终显示,无论用户设置如何
if (canvas.EditingMode == InkCanvasEditingMode.Select) {
canvas.UseCustomCursor = true;
canvas.ForceCursor = true;
canvas.Cursor = Cursors.Cross;
System.Windows.Forms.Cursor.Show();
return;
}
// 其他模式按照用户设置处理
if (Settings.Canvas.IsShowCursor) {
canvas.UseCustomCursor = true;
canvas.ForceCursor = true;
// 根据编辑模式设置不同的光标
if (canvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
canvas.Cursor = Cursors.Cross;
@@ -570,16 +753,11 @@ namespace Ink_Canvas {
var sri = Application.GetResourceStream(new Uri("Resources/Cursors/Pen.cur", UriKind.Relative));
if (sri != null)
canvas.Cursor = new Cursor(sri.Stream);
} else if (canvas.EditingMode == InkCanvasEditingMode.Select) {
canvas.Cursor = Cursors.Cross;
}
// 确保光标可见,无论是鼠标、触控还是手写笔
System.Windows.Forms.Cursor.Show();
// 强制应用光标设置
canvas.ForceCursor = true;
// 确保手写笔模式下也能显示光标
if (Tablet.TabletDevices.Count > 0) {
foreach (TabletDevice device in Tablet.TabletDevices) {
@@ -601,36 +779,17 @@ namespace Ink_Canvas {
private void inkCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode(inkCanvas);
SetCursorBasedOnEditingMode(sender as InkCanvas);
}
// 手写笔输入
private void inkCanvas_StylusDown(object sender, StylusDownEventArgs e)
{
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode(inkCanvas);
}
// 触摸输入,不隐藏光标
private void inkCanvas_TouchDown(object sender, TouchEventArgs e)
{
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode(inkCanvas);
SetCursorBasedOnEditingMode(sender as InkCanvas);
}
// 触摸结束,恢复光标
private void inkCanvas_TouchUp(object sender, TouchEventArgs e)
{
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode(inkCanvas);
// 确保光标可见
if (Settings.Canvas.IsShowCursor) {
inkCanvas.ForceCursor = true;
inkCanvas.UseCustomCursor = true;
System.Windows.Forms.Cursor.Show();
}
}
#endregion Definations and Loading
@@ -1106,5 +1265,113 @@ namespace Ink_Canvas {
MessageBox.Show($"打开插件管理器时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
// 在MainWindow类中添加:
private void ApplyCurrentEraserShape()
{
double k = 1;
switch (Settings.Canvas.EraserSize)
{
case 0:
k = Settings.Canvas.EraserShapeType == 0 ? 0.5 : 0.7;
break;
case 1:
k = Settings.Canvas.EraserShapeType == 0 ? 0.8 : 0.9;
break;
case 3:
k = Settings.Canvas.EraserShapeType == 0 ? 1.25 : 1.2;
break;
case 4:
k = Settings.Canvas.EraserShapeType == 0 ? 1.5 : 1.3;
break;
}
if (Settings.Canvas.EraserShapeType == 0)
{
inkCanvas.EraserShape = new EllipseStylusShape(k * 90, k * 90);
}
else if (Settings.Canvas.EraserShapeType == 1)
{
inkCanvas.EraserShape = new RectangleStylusShape(k * 90 * 0.6, k * 90);
}
}
// 显示指定页
private void ShowPage(int index)
{
if (index < 0 || index >= whiteboardPages.Count) return;
// 只切换可见性
for (int i = 0; i < whiteboardPages.Count; i++)
{
whiteboardPages[i].Visibility = (i == index) ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}
currentCanvas = whiteboardPages[index];
currentPageIndex = index;
}
// 新建页面
private void AddNewPage()
{
var newCanvas = new System.Windows.Controls.Canvas();
whiteboardPages.Add(newCanvas);
InkCanvasGridForInkReplay.Children.Add(newCanvas);
ShowPage(whiteboardPages.Count - 1);
}
// 删除当前页面
private void DeleteCurrentPage()
{
if (whiteboardPages.Count <= 1) return;
InkCanvasGridForInkReplay.Children.Remove(currentCanvas);
whiteboardPages.RemoveAt(currentPageIndex);
if (currentPageIndex >= whiteboardPages.Count)
currentPageIndex = whiteboardPages.Count - 1;
ShowPage(currentPageIndex);
}
// 快速面板退出PPT放映按钮事件
private void ExitPPTSlideShow_MouseUp(object sender, MouseButtonEventArgs e)
{
// 直接调用PPT放映结束按钮的逻辑
BtnPPTSlideShowEnd_Click(BtnPPTSlideShowEnd, null);
}
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e)
{
// 收起设置面板(与插件面板一致)
BorderSettings.Visibility = Visibility.Hidden;
BorderSettingsMask.Visibility = Visibility.Hidden;
var win = new HistoryRollbackWindow(Settings.Startup.UpdateChannel);
win.ShowDialog();
// 可选:回滚窗口关闭后恢复设置面板显示
BorderSettings.Visibility = Visibility.Visible;
BorderSettingsMask.Visibility = Visibility.Visible;
}
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
private const int GWL_EXSTYLE = -20;
private const int WS_EX_NOACTIVATE = 0x08000000;
private void ApplyNoFocusMode()
{
var hwnd = new WindowInteropHelper(this).Handle;
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
if (Settings.Advanced.IsNoFocusMode)
{
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_NOACTIVATE);
}
else
{
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_NOACTIVATE);
}
}
private void ToggleSwitchNoFocusMode_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
var toggle = sender as ToggleSwitch;
Settings.Advanced.IsNoFocusMode = toggle != null && toggle.IsOn;
SaveSettingsToFile();
ApplyNoFocusMode();
}
}
}
+2 -4
View File
@@ -1,7 +1,6 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@@ -9,7 +8,6 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
namespace Ink_Canvas {
public partial class MainWindow : Window {
@@ -40,7 +38,7 @@ namespace Ink_Canvas {
await FoldFloatingBar(sender);
}
public async Task FoldFloatingBar(object sender)
public async Task FoldFloatingBar(object sender, bool isAutoFoldCommand = false)
{
var isShouldRejectAction = false;
@@ -84,7 +82,7 @@ namespace Ink_Canvas {
CursorWithDelIcon_Click(sender, null);
});
await Task.Delay(10);
await Task.Delay(300);
await Dispatcher.InvokeAsync(() => {
LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
+2 -2
View File
@@ -1,6 +1,6 @@
using IWshRuntimeLibrary;
using System;
using System;
using System.Windows;
using IWshRuntimeLibrary;
namespace Ink_Canvas {
public partial class MainWindow : Window {
+48 -4
View File
@@ -1,17 +1,16 @@
using Ink_Canvas.Helpers;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Xml.Linq;
using System.Windows.Controls;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
namespace Ink_Canvas {
public partial class MainWindow : Window {
@@ -23,6 +22,7 @@ namespace Ink_Canvas {
private int WhiteboardTotalCount = 1;
private TimeMachineHistory[][] TimeMachineHistories = new TimeMachineHistory[101][]; //最多99页,0用来存储非白板时的墨迹以便还原
// 保存每页白板图片信息
private void SaveStrokes(bool isBackupMain = false) {
if (isBackupMain) {
var timeMachineHistory = timeMachine.ExportTimeMachineHistory();
@@ -32,6 +32,26 @@ namespace Ink_Canvas {
var timeMachineHistory = timeMachine.ExportTimeMachineHistory();
TimeMachineHistories[CurrentWhiteboardIndex] = timeMachineHistory;
timeMachine.ClearStrokeHistory();
// 保存当前页图片信息
var elementInfos = new List<CanvasElementInfo>();
foreach (var child in inkCanvas.Children)
{
if (child is Image img && img.Source is BitmapImage bmp)
{
elementInfos.Add(new CanvasElementInfo
{
Type = "Image",
SourcePath = bmp.UriSource?.LocalPath ?? "",
Left = InkCanvas.GetLeft(img),
Top = InkCanvas.GetTop(img),
Width = img.Width,
Height = img.Height
});
}
}
var savePath = Settings.Automation.AutoSavedStrokesLocation;
if (!Directory.Exists(savePath)) Directory.CreateDirectory(savePath);
File.WriteAllText(System.IO.Path.Combine(savePath, $"elements_page{CurrentWhiteboardIndex}.json"), JsonConvert.SerializeObject(elementInfos, Formatting.Indented));
}
}
@@ -42,6 +62,7 @@ namespace Ink_Canvas {
_currentCommitType = CommitReason.UserInput;
}
// 恢复每页白板图片信息
private void RestoreStrokes(bool isBackupMain = false) {
try {
if (TimeMachineHistories[CurrentWhiteboardIndex] == null) return; //防止白板打开后不居中
@@ -51,6 +72,29 @@ namespace Ink_Canvas {
} else {
timeMachine.ImportTimeMachineHistory(TimeMachineHistories[CurrentWhiteboardIndex]);
foreach (var item in TimeMachineHistories[CurrentWhiteboardIndex]) ApplyHistoryToCanvas(item);
// 恢复当前页图片信息
inkCanvas.Children.Clear();
var savePath = Settings.Automation.AutoSavedStrokesLocation;
var elementsFile = System.IO.Path.Combine(savePath, $"elements_page{CurrentWhiteboardIndex}.json");
if (File.Exists(elementsFile))
{
var elementInfos = JsonConvert.DeserializeObject<List<CanvasElementInfo>>(File.ReadAllText(elementsFile));
foreach (var info in elementInfos)
{
if (info.Type == "Image" && File.Exists(info.SourcePath))
{
var img = new Image
{
Source = new BitmapImage(new Uri(info.SourcePath)),
Width = info.Width,
Height = info.Height
};
InkCanvas.SetLeft(img, info.Left);
InkCanvas.SetTop(img, info.Top);
inkCanvas.Children.Add(img);
}
}
}
}
}
catch {
+6 -25
View File
@@ -6,8 +6,6 @@ using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Controls.Primitives;
namespace Ink_Canvas {
public partial class MainWindow : Window {
@@ -688,29 +686,12 @@ namespace Ink_Canvas {
}
}
private void BoardEraserIcon_Click(object sender, RoutedEventArgs e) {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint ||
inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke) {
if (BoardEraserSizePanel.Visibility == Visibility.Collapsed) {
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardEraserSizePanel);
} else {
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
}
} else {
forceEraser = true;
forcePointEraser = true;
// 使用统一的方法应用橡皮擦形状,确保一致性
ApplyCurrentEraserShape();
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
drawingShapeMode = 0;
inkCanvas_EditingModeChanged(inkCanvas, null);
CancelSingleFingerDragMode();
HideSubPanels("eraser");
}
private void BoardLassoIcon_Click(object sender, RoutedEventArgs e) {
forceEraser = false;
forcePointEraser = false;
drawingShapeMode = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
SetCursorBasedOnEditingMode(inkCanvas);
}
private void BoardEraserIconByStrokes_Click(object sender, RoutedEventArgs e) {
-21
View File
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Ink;
@@ -54,7 +53,6 @@ namespace Ink_Canvas {
drawingShapeMode = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
CancelSingleFingerDragMode();
forceEraser = false;
CheckColorTheme();
}
@@ -553,62 +551,52 @@ namespace Ink_Canvas {
private void BtnColorBlack_Click(object sender, RoutedEventArgs e) {
CheckLastColor(0);
forceEraser = false;
ColorSwitchCheck();
}
private void BtnColorRed_Click(object sender, RoutedEventArgs e) {
CheckLastColor(1);
forceEraser = false;
ColorSwitchCheck();
}
private void BtnColorGreen_Click(object sender, RoutedEventArgs e) {
CheckLastColor(2);
forceEraser = false;
ColorSwitchCheck();
}
private void BtnColorBlue_Click(object sender, RoutedEventArgs e) {
CheckLastColor(3);
forceEraser = false;
ColorSwitchCheck();
}
private void BtnColorYellow_Click(object sender, RoutedEventArgs e) {
CheckLastColor(4);
forceEraser = false;
ColorSwitchCheck();
}
private void BtnColorWhite_Click(object sender, RoutedEventArgs e) {
CheckLastColor(5);
forceEraser = false;
ColorSwitchCheck();
}
private void BtnColorPink_Click(object sender, RoutedEventArgs e) {
CheckLastColor(6);
forceEraser = false;
ColorSwitchCheck();
}
private void BtnColorOrange_Click(object sender, RoutedEventArgs e) {
CheckLastColor(8);
forceEraser = false;
ColorSwitchCheck();
}
private void BtnColorTeal_Click(object sender, RoutedEventArgs e) {
CheckLastColor(7);
forceEraser = false;
ColorSwitchCheck();
}
private void BtnHighlighterColorBlack_Click(object sender, RoutedEventArgs e) {
CheckLastColor(100, true);
penType = 1;
forceEraser = false;
CheckPenTypeUIState();
ColorSwitchCheck();
}
@@ -616,7 +604,6 @@ namespace Ink_Canvas {
private void BtnHighlighterColorWhite_Click(object sender, RoutedEventArgs e) {
CheckLastColor(101, true);
penType = 1;
forceEraser = false;
CheckPenTypeUIState();
ColorSwitchCheck();
}
@@ -624,7 +611,6 @@ namespace Ink_Canvas {
private void BtnHighlighterColorRed_Click(object sender, RoutedEventArgs e) {
CheckLastColor(102, true);
penType = 1;
forceEraser = false;
CheckPenTypeUIState();
ColorSwitchCheck();
}
@@ -632,7 +618,6 @@ namespace Ink_Canvas {
private void BtnHighlighterColorYellow_Click(object sender, RoutedEventArgs e) {
CheckLastColor(103, true);
penType = 1;
forceEraser = false;
CheckPenTypeUIState();
ColorSwitchCheck();
}
@@ -640,7 +625,6 @@ namespace Ink_Canvas {
private void BtnHighlighterColorGreen_Click(object sender, RoutedEventArgs e) {
CheckLastColor(104, true);
penType = 1;
forceEraser = false;
CheckPenTypeUIState();
ColorSwitchCheck();
}
@@ -648,7 +632,6 @@ namespace Ink_Canvas {
private void BtnHighlighterColorZinc_Click(object sender, RoutedEventArgs e) {
CheckLastColor(105, true);
penType = 1;
forceEraser = false;
CheckPenTypeUIState();
ColorSwitchCheck();
}
@@ -656,7 +639,6 @@ namespace Ink_Canvas {
private void BtnHighlighterColorBlue_Click(object sender, RoutedEventArgs e) {
CheckLastColor(106, true);
penType = 1;
forceEraser = false;
CheckPenTypeUIState();
ColorSwitchCheck();
}
@@ -664,7 +646,6 @@ namespace Ink_Canvas {
private void BtnHighlighterColorPurple_Click(object sender, RoutedEventArgs e) {
CheckLastColor(107, true);
penType = 1;
forceEraser = false;
CheckPenTypeUIState();
ColorSwitchCheck();
}
@@ -672,7 +653,6 @@ namespace Ink_Canvas {
private void BtnHighlighterColorTeal_Click(object sender, RoutedEventArgs e) {
CheckLastColor(108, true);
penType = 1;
forceEraser = false;
CheckPenTypeUIState();
ColorSwitchCheck();
}
@@ -680,7 +660,6 @@ namespace Ink_Canvas {
private void BtnHighlighterColorOrange_Click(object sender, RoutedEventArgs e) {
CheckLastColor(109, true);
penType = 1;
forceEraser = false;
CheckPenTypeUIState();
ColorSwitchCheck();
}
@@ -0,0 +1,164 @@
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
#region Image
private async void BtnImageInsert_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Image files (*.jpg; *.jpeg; *.png; *.bmp)|*.jpg;*.jpeg;*.png;*.bmp";
if (openFileDialog.ShowDialog() == true)
{
string filePath = openFileDialog.FileName;
Image image = await CreateAndCompressImageAsync(filePath);
if (image != null)
{
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
// 新缩放逻辑:最大宽高为画布一半,并居中
double maxWidth = inkCanvas.ActualWidth / 2;
double maxHeight = inkCanvas.ActualHeight / 2;
double scaleX = maxWidth / image.Width;
double scaleY = maxHeight / image.Height;
double scale = Math.Min(1, Math.Min(scaleX, scaleY));
image.Width = image.Width * scale;
image.Height = image.Height * scale;
InkCanvas.SetLeft(image, (inkCanvas.ActualWidth - image.Width) / 2);
InkCanvas.SetTop(image, (inkCanvas.ActualHeight - image.Height) / 2);
inkCanvas.Children.Add(image);
timeMachine.CommitElementInsertHistory(image);
}
}
}
private async Task<Image> CreateAndCompressImageAsync(string filePath)
{
string savePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "File Dependency");
if (!Directory.Exists(savePath))
{
Directory.CreateDirectory(savePath);
}
string fileExtension = Path.GetExtension(filePath);
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
string newFilePath = Path.Combine(savePath, timestamp + fileExtension);
await Task.Run(() => File.Copy(filePath, newFilePath, true));
return await Dispatcher.InvokeAsync(() =>
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(newFilePath);
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
int width = bitmapImage.PixelWidth;
int height = bitmapImage.PixelHeight;
Image image = new Image();
if (isLoaded && Settings.Canvas.IsCompressPicturesUploaded && (width > 1920 || height > 1080))
{
double scaleX = 1920.0 / width;
double scaleY = 1080.0 / height;
double scale = Math.Min(scaleX, scaleY);
TransformedBitmap transformedBitmap = new TransformedBitmap(bitmapImage, new ScaleTransform(scale, scale));
image.Source = transformedBitmap;
image.Width = transformedBitmap.PixelWidth;
image.Height = transformedBitmap.PixelHeight;
}
else
{
image.Source = bitmapImage;
image.Width = width;
image.Height = height;
}
return image;
});
}
#endregion
#region Media
private async void BtnMediaInsert_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Media files (*.mp4; *.avi; *.wmv)|*.mp4;*.avi;*.wmv";
if (openFileDialog.ShowDialog() == true)
{
string filePath = openFileDialog.FileName;
byte[] mediaBytes = await Task.Run(() => File.ReadAllBytes(filePath));
MediaElement mediaElement = await CreateMediaElementAsync(filePath);
if (mediaElement != null)
{
InkCanvas.SetLeft(mediaElement, 0);
InkCanvas.SetTop(mediaElement, 0);
inkCanvas.Children.Add(mediaElement);
mediaElement.LoadedBehavior = MediaState.Manual;
mediaElement.UnloadedBehavior = MediaState.Manual;
mediaElement.Loaded += async (_, args) =>
{
mediaElement.Play();
await Task.Delay(100);
mediaElement.Pause();
};
timeMachine.CommitElementInsertHistory(mediaElement);
}
}
}
private async Task<MediaElement> CreateMediaElementAsync(string filePath)
{
string savePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "File Dependency");
if (!Directory.Exists(savePath))
{
Directory.CreateDirectory(savePath);
}
return await Dispatcher.InvokeAsync(() =>
{
MediaElement mediaElement = new MediaElement();
mediaElement.Source = new Uri(filePath);
string timestamp = "media_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
mediaElement.Name = timestamp;
mediaElement.LoadedBehavior = MediaState.Manual;
mediaElement.UnloadedBehavior = MediaState.Manual;
mediaElement.Width = 256;
mediaElement.Height = 256;
string fileExtension = Path.GetExtension(filePath);
string newFilePath = Path.Combine(savePath, mediaElement.Name + fileExtension);
File.Copy(filePath, newFilePath, true);
mediaElement.Source = new Uri(newFilePath);
return mediaElement;
});
}
#endregion
}
}
+734
View File
@@ -0,0 +1,734 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using Ink_Canvas.Helpers;
namespace Ink_Canvas {
public partial class MainWindow : Window {
// 新橡皮擦系统的核心变量
public bool isUsingAdvancedEraser = false;
private IncrementalStrokeHitTester advancedHitTester = null;
// 橡皮擦配置
public double currentEraserSize = 64;
public bool isCurrentEraserCircle = false;
public bool isUsingStrokeEraser = false;
// 视觉反馈相关
private Matrix eraserTransformMatrix = new Matrix();
private Point lastEraserPosition = new Point();
private bool isEraserVisible = false;
// 性能优化相关
private DateTime lastEraserUpdate = DateTime.Now;
private const double ERASER_UPDATE_INTERVAL = 16.67; // 约60FPS
// 锁定笔画的GUID(如果不存在则创建一个默认值)
private static readonly Guid IsLockGuid = new Guid("12345678-1234-1234-1234-123456789ABC");
// 橡皮擦视觉反馈控件
private DrawingVisual eraserVisual = new DrawingVisual();
private VisualCanvas eraserOverlayCanvas = null;
private Border eraserVisualBorder = null; // 用于显示橡皮擦视觉反馈的Border
// 兼容性属性:模拟原有的EraserOverlay_DrawingVisual
private VisualCanvas EraserOverlay_DrawingVisual => eraserOverlayCanvas;
// 兼容性保持
[Obsolete("使用 isUsingAdvancedEraser 替代")]
public bool isUsingGeometryEraser
{
get => isUsingAdvancedEraser;
set => isUsingAdvancedEraser = value;
}
[Obsolete("使用 currentEraserSize 替代")]
public double eraserWidth
{
get => currentEraserSize;
set => currentEraserSize = value;
}
[Obsolete("使用 isCurrentEraserCircle 替代")]
public bool isEraserCircleShape
{
get => isCurrentEraserCircle;
set => isCurrentEraserCircle = value;
}
[Obsolete("使用 isUsingStrokeEraser 替代")]
public bool isUsingStrokesEraser
{
get => isUsingStrokeEraser;
set => isUsingStrokeEraser = value;
}
[Obsolete("使用 eraserTransformMatrix 替代")]
private Matrix scaleMatrix
{
get => eraserTransformMatrix;
set => eraserTransformMatrix = value;
}
/// <summary>
/// 新橡皮擦覆盖层加载事件处理
/// </summary>
private void EraserOverlay_Loaded(object sender, RoutedEventArgs e) {
var border = (Border)sender;
// 初始化覆盖层
InitializeEraserOverlay(border);
Trace.WriteLine("Advanced Eraser: Overlay loaded and initialized");
}
/// <summary>
/// 开始高级橡皮擦操作
/// </summary>
private void StartAdvancedEraserOperation(object sender) {
if (isUsingAdvancedEraser) return;
// 设置操作状态
isUsingAdvancedEraser = true;
isEraserVisible = true;
// 更新橡皮擦尺寸
UpdateEraserSize();
// 获取inkCanvas引用
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
if (inkCanvas == null) return;
// 根据橡皮擦形状创建碰撞检测器
StylusShape eraserShape = CreateEraserShape();
advancedHitTester = inkCanvas.Strokes.GetIncrementalStrokeHitTester(eraserShape);
advancedHitTester.StrokeHit += OnAdvancedEraserStrokeHit;
// 初始化变换矩阵
InitializeEraserTransform();
}
/// <summary>
/// 创建橡皮擦形状
/// </summary>
private StylusShape CreateEraserShape() {
if (isCurrentEraserCircle) {
return new EllipseStylusShape(currentEraserSize, currentEraserSize);
} else {
// 矩形橡皮擦,使用与原来相同的逻辑
return new RectangleStylusShape(currentEraserSize, currentEraserSize / 0.6);
}
}
/// <summary>
/// 初始化橡皮擦变换矩阵
/// </summary>
private void InitializeEraserTransform() {
eraserTransformMatrix = new Matrix();
if (isCurrentEraserCircle) {
// 圆形橡皮擦:等比例缩放
var scale = currentEraserSize / 56.0; // 基于56x56的基准尺寸
eraserTransformMatrix.ScaleAt(scale, scale, 0, 0);
} else {
// 矩形橡皮擦:保持传统比例
var scaleX = currentEraserSize / 38.0;
var scaleY = (currentEraserSize * 56 / 38) / 56.0;
eraserTransformMatrix.ScaleAt(scaleX, scaleY, 0, 0);
}
}
/// <summary>
/// 更新橡皮擦尺寸
/// </summary>
private void UpdateEraserSize() {
// 使用与原来相同的逻辑计算橡皮擦尺寸
double k = 1.0;
switch (Settings.Canvas.EraserSize) {
case 0: k = Settings.Canvas.EraserShapeType == 0 ? 0.5 : 0.7; break;
case 1: k = Settings.Canvas.EraserShapeType == 0 ? 0.8 : 0.9; break;
case 2: k = 1.0; break;
case 3: k = Settings.Canvas.EraserShapeType == 0 ? 1.25 : 1.2; break;
case 4: k = Settings.Canvas.EraserShapeType == 0 ? 1.5 : 1.3; break;
}
// 更新形状类型
isCurrentEraserCircle = (Settings.Canvas.EraserShapeType == 0);
// 根据形状类型设置尺寸
if (isCurrentEraserCircle) {
currentEraserSize = k * 90; // 圆形橡皮擦
} else {
currentEraserSize = k * 90 * 0.6; // 矩形橡皮擦宽度
}
}
/// <summary>
/// 结束高级橡皮擦操作
/// </summary>
private void EndAdvancedEraserOperation(object sender) {
if (!isUsingAdvancedEraser) return;
// 重置操作状态
isUsingAdvancedEraser = false;
isEraserVisible = false;
// 释放鼠标捕获
if (sender is Border border) {
border.ReleaseMouseCapture();
}
// 隐藏橡皮擦视觉反馈
HideEraserFeedback();
// 结束碰撞检测
if (advancedHitTester != null) {
advancedHitTester.EndHitTesting();
advancedHitTester = null;
}
// 提交橡皮擦历史记录
CommitEraserHistory();
}
/// <summary>
/// 隐藏橡皮擦视觉反馈
/// </summary>
private void HideEraserFeedback() {
try {
if (eraserVisualBorder != null) {
eraserVisualBorder.Visibility = Visibility.Collapsed;
}
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error hiding feedback - {ex.Message}");
}
}
/// <summary>
/// 提交橡皮擦历史记录
/// </summary>
private void CommitEraserHistory() {
try {
if (ReplacedStroke != null || AddedStroke != null) {
timeMachine.CommitStrokeEraseHistory(ReplacedStroke, AddedStroke);
AddedStroke = null;
ReplacedStroke = null;
}
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error committing history - {ex.Message}");
}
}
/// <summary>
/// 高级橡皮擦笔画碰撞事件处理
/// </summary>
private void OnAdvancedEraserStrokeHit(object sender, StrokeHitEventArgs args) {
try {
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
if (inkCanvas == null) return;
var eraseResult = args.GetPointEraseResults();
var strokeToReplace = new StrokeCollection { args.HitStroke };
// 过滤锁定的笔画
var filteredToReplace = strokeToReplace.Where(stroke => !stroke.ContainsPropertyData(IsLockGuid));
var filteredToReplaceArray = filteredToReplace as Stroke[] ?? filteredToReplace.ToArray();
if (!filteredToReplaceArray.Any()) return;
var filteredResult = eraseResult.Where(stroke => !stroke.ContainsPropertyData(IsLockGuid));
var filteredResultArray = filteredResult as Stroke[] ?? filteredResult.ToArray();
// 执行笔画替换或删除
if (filteredResultArray.Any()) {
inkCanvas.Strokes.Replace(
new StrokeCollection(filteredToReplaceArray),
new StrokeCollection(filteredResultArray)
);
} else {
inkCanvas.Strokes.Remove(new StrokeCollection(filteredToReplaceArray));
}
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error in stroke hit - {ex.Message}");
}
}
/// <summary>
/// 更新高级橡皮擦位置
/// </summary>
private void UpdateAdvancedEraserPosition(object sender, Point position) {
// 移除isUsingAdvancedEraser检查,让视觉反馈始终更新
// if (!isUsingAdvancedEraser) return;
// 性能优化:限制更新频率
var now = DateTime.Now;
if ((now - lastEraserUpdate).TotalMilliseconds < ERASER_UPDATE_INTERVAL) {
return;
}
lastEraserUpdate = now;
// 更新位置
lastEraserPosition = position;
// 更新视觉反馈(始终执行)
UpdateEraserVisualFeedback(position);
// 只有在实际使用橡皮擦时才处理擦除
if (isUsingAdvancedEraser) {
// 处理不同的橡皮擦模式
if (isUsingStrokeEraser) {
ProcessStrokeEraserAtPosition(position);
} else {
ProcessGeometryEraserAtPosition(position);
}
}
}
/// <summary>
/// 在指定位置处理笔画橡皮擦
/// </summary>
private void ProcessStrokeEraserAtPosition(Point position) {
try {
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
if (inkCanvas == null) return;
var hitStrokes = inkCanvas.Strokes.HitTest(position)
.Where(stroke => !stroke.ContainsPropertyData(IsLockGuid));
var strokesArray = hitStrokes as Stroke[] ?? hitStrokes.ToArray();
if (strokesArray.Any()) {
inkCanvas.Strokes.Remove(new StrokeCollection(strokesArray));
}
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error in stroke eraser - {ex.Message}");
}
}
/// <summary>
/// 在指定位置处理几何橡皮擦
/// </summary>
private void ProcessGeometryEraserAtPosition(Point position) {
try {
if (advancedHitTester != null) {
advancedHitTester.AddPoint(position);
}
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error in geometry eraser - {ex.Message}");
}
}
/// <summary>
/// 更新橡皮擦视觉反馈
/// </summary>
private void UpdateEraserVisualFeedback(Point position) {
try {
// 获取或创建橡皮擦视觉反馈Border
if (eraserVisualBorder == null) {
eraserVisualBorder = new Border {
Background = new SolidColorBrush(Colors.Transparent),
BorderBrush = new SolidColorBrush(Colors.Transparent),
BorderThickness = new Thickness(0),
IsHitTestVisible = false,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
Opacity = 1
};
Panel.SetZIndex(eraserVisualBorder, 1001);
// 将Border添加到InkCanvasGridForInkReplay中
var inkCanvasGrid = this.FindName("InkCanvasGridForInkReplay") as Grid;
if (inkCanvasGrid != null) {
inkCanvasGrid.Children.Add(eraserVisualBorder);
Trace.WriteLine("Advanced Eraser: Visual feedback border added to grid");
} else {
Trace.WriteLine("Advanced Eraser: Failed to find InkCanvasGridForInkReplay");
return; // 如果找不到Grid,直接返回
}
}
if (eraserVisualBorder != null) {
// 创建橡皮擦视觉反馈
var eraserImage = CreateEraserVisualImage();
// 清除Border的内容并添加新的图像
eraserVisualBorder.Child = eraserImage;
// 更新橡皮擦位置和大小
if (isCurrentEraserCircle) {
var radius = currentEraserSize / 2;
eraserVisualBorder.Width = currentEraserSize;
eraserVisualBorder.Height = currentEraserSize;
// 使用Margin来定位,因为Border在Grid中
eraserVisualBorder.Margin = new Thickness(
position.X - radius,
position.Y - radius,
0, 0);
} else {
// 矩形橡皮擦,使用与原来相同的逻辑
var height = currentEraserSize / 0.6;
eraserVisualBorder.Width = currentEraserSize;
eraserVisualBorder.Height = height;
// 使用Margin来定位,因为Border在Grid中
eraserVisualBorder.Margin = new Thickness(
position.X - currentEraserSize / 2,
position.Y - height / 2,
0, 0);
}
eraserVisualBorder.Visibility = Visibility.Visible;
Trace.WriteLine($"Advanced Eraser: Visual feedback updated to ({position.X:F1}, {position.Y:F1})");
}
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error updating visual feedback - {ex.Message}");
}
}
/// <summary>
/// 创建橡皮擦视觉图像
/// </summary>
private Image CreateEraserVisualImage() {
try {
// 根据橡皮擦形状选择对应的DrawingGroup资源
string resourceKey = isCurrentEraserCircle ? "EraserCircleDrawingGroup" : "EraserDrawingGroup";
// 尝试从资源字典中获取DrawingGroup
var drawingGroup = this.TryFindResource(resourceKey) as DrawingGroup;
if (drawingGroup == null) {
// 如果找不到资源,创建默认的橡皮擦图像
return CreateDefaultEraserImage();
}
// 创建变换后的DrawingGroup
var transformedGroup = new DrawingGroup();
transformedGroup.Children.Add(drawingGroup);
// 应用缩放变换
var transform = new ScaleTransform();
if (isCurrentEraserCircle) {
var scale = currentEraserSize / 56.0; // 基于56x56的基准尺寸
transform.ScaleX = scale;
transform.ScaleY = scale;
} else {
var scaleX = currentEraserSize / 38.0;
var scaleY = (currentEraserSize / 0.6) / 56.0;
transform.ScaleX = scaleX;
transform.ScaleY = scaleY;
}
transformedGroup.Transform = transform;
// 创建DrawingImage
var drawingImage = new DrawingImage(transformedGroup);
// 创建Image控件
var image = new Image {
Source = drawingImage,
Stretch = Stretch.None
};
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
return image;
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error creating eraser visual image - {ex.Message}");
return CreateDefaultEraserImage();
}
}
/// <summary>
/// 创建默认的橡皮擦图像(当资源不可用时)
/// </summary>
private Image CreateDefaultEraserImage() {
try {
// 创建一个简单的几何图形作为默认橡皮擦
Geometry geometry;
if (isCurrentEraserCircle) {
geometry = new EllipseGeometry(new Point(28, 28), 28, 28);
} else {
geometry = new RectangleGeometry(new Rect(0, 0, 38, 56));
}
var brush = new SolidColorBrush(Colors.LightGray);
var pen = new Pen(new SolidColorBrush(Colors.DarkGray), 1);
var geometryDrawing = new GeometryDrawing(brush, pen, geometry);
var drawingGroup = new DrawingGroup();
drawingGroup.Children.Add(geometryDrawing);
// 应用缩放变换
var transform = new ScaleTransform();
if (isCurrentEraserCircle) {
var scale = currentEraserSize / 56.0;
transform.ScaleX = scale;
transform.ScaleY = scale;
} else {
var scaleX = currentEraserSize / 38.0;
var scaleY = (currentEraserSize / 0.6) / 56.0;
transform.ScaleX = scaleX;
transform.ScaleY = scaleY;
}
drawingGroup.Transform = transform;
var drawingImage = new DrawingImage(drawingGroup);
var image = new Image {
Source = drawingImage,
Stretch = Stretch.None
};
return image;
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error creating default eraser image - {ex.Message}");
return null;
}
}
/// <summary>
/// 兼容性方法:旧版橡皮擦几何碰撞处理
/// </summary>
[Obsolete("使用 OnAdvancedEraserStrokeHit 替代")]
private void EraserGeometry_StrokeHit(object sender, StrokeHitEventArgs args) {
OnAdvancedEraserStrokeHit(sender, args);
}
/// <summary>
/// 兼容性方法:旧版橡皮擦移动处理
/// </summary>
[Obsolete("使用 UpdateAdvancedEraserPosition 替代")]
private void EraserOverlay_PointerMove(object sender, Point pt) {
UpdateAdvancedEraserPosition(sender, pt);
}
/// <summary>
/// 兼容性方法:旧版橡皮擦按下处理
/// </summary>
[Obsolete("使用 StartAdvancedEraserOperation 替代")]
private void EraserOverlay_PointerDown(object sender) {
StartAdvancedEraserOperation(sender);
}
/// <summary>
/// 兼容性方法:旧版橡皮擦抬起处理
/// </summary>
[Obsolete("使用 EndAdvancedEraserOperation 替代")]
private void EraserOverlay_PointerUp(object sender) {
EndAdvancedEraserOperation(sender);
}
/// <summary>
/// 获取当前橡皮擦状态信息(用于调试)
/// </summary>
public string GetEraserStatusInfo() {
return $"Advanced Eraser Status:\n" +
$"- Active: {isUsingAdvancedEraser}\n" +
$"- Size: {currentEraserSize:F1}\n" +
$"- Shape: {(isCurrentEraserCircle ? "Circle" : "Rectangle")}\n" +
$"- Mode: {(isUsingStrokeEraser ? "Stroke" : "Geometry")}\n" +
$"- Visible: {isEraserVisible}\n" +
$"- Last Position: ({lastEraserPosition.X:F1}, {lastEraserPosition.Y:F1})";
}
/// <summary>
/// 重置橡皮擦状态
/// </summary>
public void ResetEraserState() {
isUsingAdvancedEraser = false;
isEraserVisible = false;
lastEraserPosition = new Point();
if (advancedHitTester != null) {
advancedHitTester.EndHitTesting();
advancedHitTester = null;
}
HideEraserFeedback();
// 清理视觉反馈Border
if (eraserVisualBorder != null) {
var inkCanvasGrid = this.FindName("InkCanvasGridForInkReplay") as Grid;
if (inkCanvasGrid != null) {
inkCanvasGrid.Children.Remove(eraserVisualBorder);
}
eraserVisualBorder = null;
}
}
/// <summary>
/// 应用高级橡皮擦形状到InkCanvas
/// </summary>
public void ApplyAdvancedEraserShape() {
try {
var inkCanvas = this.FindName("inkCanvas") as InkCanvas;
if (inkCanvas == null) return;
// 更新橡皮擦尺寸和形状
UpdateEraserSize();
// 创建橡皮擦形状
StylusShape eraserShape = CreateEraserShape();
// 应用到InkCanvas
inkCanvas.EraserShape = eraserShape;
Trace.WriteLine($"Advanced Eraser: Applied shape - Size: {currentEraserSize}, Circle: {isCurrentEraserCircle}");
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error applying shape - {ex.Message}");
// 回退到传统方法
try {
ApplyCurrentEraserShape();
} catch (Exception fallbackEx) {
Trace.WriteLine($"Advanced Eraser: Fallback also failed - {fallbackEx.Message}");
}
}
}
/// <summary>
/// 启用高级橡皮擦系统
/// </summary>
public void EnableAdvancedEraserSystem() {
try {
// 获取橡皮擦覆盖层
var eraserOverlay = this.FindName("AdvancedEraserOverlay") as Border;
if (eraserOverlay != null) {
// 启用覆盖层的交互
eraserOverlay.IsHitTestVisible = true;
// 确保覆盖层在橡皮擦模式下启用
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
eraserOverlay.IsHitTestVisible = true;
Trace.WriteLine("Advanced Eraser: Overlay enabled for eraser mode");
}
// 设置覆盖层的大小以覆盖整个InkCanvas
var inkCanvasControl = this.FindName("inkCanvas") as InkCanvas;
if (inkCanvasControl != null) {
eraserOverlay.Width = inkCanvasControl.ActualWidth;
eraserOverlay.Height = inkCanvasControl.ActualHeight;
Trace.WriteLine($"Advanced Eraser: Overlay size set to {eraserOverlay.Width}x{eraserOverlay.Height}");
}
Trace.WriteLine("Advanced Eraser: System enabled successfully");
} else {
Trace.WriteLine("Advanced Eraser: Failed to find eraser overlay");
}
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error enabling system - {ex.Message}");
}
}
/// <summary>
/// 初始化橡皮擦覆盖层
/// </summary>
private void InitializeEraserOverlay(Border overlay) {
try {
// 设置覆盖层的基本属性
overlay.Background = new SolidColorBrush(Colors.Transparent);
overlay.IsHitTestVisible = false; // 默认禁用,只在橡皮擦模式下启用
// 绑定事件处理
overlay.MouseDown += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
overlay.CaptureMouse();
StartAdvancedEraserOperation(sender);
}
};
overlay.MouseUp += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
overlay.ReleaseMouseCapture();
EndAdvancedEraserOperation(sender);
}
};
overlay.MouseMove += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
var position = e.GetPosition((UIElement)this.FindName("inkCanvas"));
Trace.WriteLine($"Advanced Eraser: Mouse move event triggered at ({position.X:F1}, {position.Y:F1})");
UpdateAdvancedEraserPosition(sender, position);
} else {
Trace.WriteLine($"Advanced Eraser: Mouse move ignored - not in eraser mode, current mode: {inkCanvas.EditingMode}");
}
};
// 触控笔事件
overlay.StylusDown += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
e.Handled = true;
if (e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus) {
overlay.CaptureStylus();
}
StartAdvancedEraserOperation(sender);
}
};
overlay.StylusUp += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
e.Handled = true;
if (e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus) {
overlay.ReleaseStylusCapture();
}
EndAdvancedEraserOperation(sender);
}
};
overlay.StylusMove += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
e.Handled = true;
var position = e.GetPosition((UIElement)this.FindName("inkCanvas"));
UpdateAdvancedEraserPosition(sender, position);
Trace.WriteLine($"Advanced Eraser: Stylus move at ({position.X:F1}, {position.Y:F1})");
}
};
Trace.WriteLine("Advanced Eraser: Overlay initialized successfully");
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error initializing overlay - {ex.Message}");
}
}
/// <summary>
/// 禁用高级橡皮擦系统
/// </summary>
public void DisableAdvancedEraserSystem() {
try {
// 重置橡皮擦状态
ResetEraserState();
// 获取橡皮擦覆盖层并禁用
var eraserOverlay = this.FindName("AdvancedEraserOverlay") as Border;
if (eraserOverlay != null) {
eraserOverlay.IsHitTestVisible = false;
}
// 确保视觉反馈被隐藏
HideEraserFeedback();
Trace.WriteLine("Advanced Eraser: System disabled successfully");
} catch (Exception ex) {
Trace.WriteLine($"Advanced Eraser: Error disabling system - {ex.Message}");
}
}
/// <summary>
/// 切换橡皮擦形状(圆形/矩形)
/// </summary>
public void ToggleEraserShape() {
isCurrentEraserCircle = !isCurrentEraserCircle;
// 更新设置
Settings.Canvas.EraserShapeType = isCurrentEraserCircle ? 0 : 1;
// 应用新形状
ApplyAdvancedEraserShape();
Trace.WriteLine($"Advanced Eraser: Toggled to {(isCurrentEraserCircle ? "Circle" : "Rectangle")}");
}
}
}
+36
View File
@@ -0,0 +1,36 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DrawingGroup x:Key="EraserDrawingGroup" ClipGeometry="M0,0 V56 H38 V0 H0 Z">
<GeometryDrawing Brush="#FFF2EEEB" Geometry="F1 M38,56z M0,0z M0,4C0,1.79086,1.79086,0,4,0L34,0C36.2091,0,38,1.79086,38,4L38,52C38,54.2091,36.2091,56,34,56L4,56C1.79086,56,0,54.2091,0,52L0,4z" />
<GeometryDrawing Brush="#FFCDCDCD" Geometry="F0 M38,56z M0,0z M34,1L4,1C2.34315,1,1,2.34315,1,4L1,52C1,53.6569,2.34315,55,4,55L34,55C35.6569,55,37,53.6569,37,52L37,4C37,2.34315,35.6569,1,34,1z M4,0C1.79086,0,0,1.79086,0,4L0,52C0,54.2091,1.79086,56,4,56L34,56C36.2091,56,38,54.2091,38,52L38,4C38,1.79086,36.2091,0,34,0L4,0z" />
<GeometryDrawing Brush="#FFD1CFCD" Geometry="F1 M38,56z M0,0z M12,19.5C12,18.1193,13.1193,17,14.5,17L14.5,17C15.8807,17,17,18.1193,17,19.5L17,36.5C17,37.8807,15.8807,39,14.5,39L14.5,39C13.1193,39,12,37.8807,12,36.5L12,19.5z" />
<GeometryDrawing Geometry="F0 M38,56z M0,0z M11.5,19.5C11.5,17.8431 12.8431,16.5 14.5,16.5 16.1569,16.5 17.5,17.8431 17.5,19.5L17.5,36.5C17.5,38.1569 16.1569,39.5 14.5,39.5 12.8431,39.5 11.5,38.1569 11.5,36.5L11.5,19.5z M14.5,17.5C13.3954,17.5,12.5,18.3954,12.5,19.5L12.5,36.5C12.5,37.6046 13.3954,38.5 14.5,38.5 15.6046,38.5 16.5,37.6046 16.5,36.5L16.5,19.5C16.5,18.3954,15.6046,17.5,14.5,17.5z">
<GeometryDrawing.Brush>
<SolidColorBrush Color="#FF6F6F6F" Opacity="0.25" />
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Brush="#FFD1CFCD" Geometry="F1 M38,56z M0,0z M21,19.5C21,18.1193,22.1193,17,23.5,17L23.5,17C24.8807,17,26,18.1193,26,19.5L26,36.5C26,37.8807,24.8807,39,23.5,39L23.5,39C22.1193,39,21,37.8807,21,36.5L21,19.5z" />
<GeometryDrawing Geometry="F0 M38,56z M0,0z M20.5,19.5C20.5,17.8431 21.8431,16.5 23.5,16.5 25.1569,16.5 26.5,17.8431 26.5,19.5L26.5,36.5C26.5,38.1569 25.1569,39.5 23.5,39.5 21.8431,39.5 20.5,38.1569 20.5,36.5L20.5,19.5z M23.5,17.5C22.3954,17.5,21.5,18.3954,21.5,19.5L21.5,36.5C21.5,37.6046 22.3954,38.5 23.5,38.5 24.6046,38.5 25.5,37.6046 25.5,36.5L25.5,19.5C25.5,18.3954,24.6046,17.5,23.5,17.5z">
<GeometryDrawing.Brush>
<SolidColorBrush Color="#FF6F6F6F" Opacity="0.25" />
</GeometryDrawing.Brush>
</GeometryDrawing>
</DrawingGroup>
<DrawingGroup x:Key="EraserCircleDrawingGroup" ClipGeometry="M0,0 V56 H56 V0 H0 Z">
<GeometryDrawing Brush="#FFF2EEEB" Geometry="F1 M56,56z M0,0z M0,28C0,12.536,12.536,0,28,0L28,0C43.464,0,56,12.536,56,28L56,28C56,43.464,43.464,56,28,56L28,56C12.536,56,0,43.464,0,28L0,28z" />
<GeometryDrawing Brush="#FFCDCDCD" Geometry="F0 M56,56z M0,0z M1,28C1,42.9117 13.0883,55 28,55 42.9117,55 55,42.9117 55,28 55,13.0883 42.9117,1 28,1 13.0883,1 1,13.0883 1,28z M28,0C12.536,0 0,12.536 0,28 0,43.464 12.536,56 28,56 43.464,56 56,43.464 56,28 56,12.536 43.464,0 28,0z" />
<GeometryDrawing Brush="#FFD1CFCD" Geometry="F1 M56,56z M0,0z M21,19.5C21,18.1193,22.1193,17,23.5,17L23.5,17C24.8807,17,26,18.1193,26,19.5L26,36.5C26,37.8807,24.8807,39,23.5,39L23.5,39C22.1193,39,21,37.8807,21,36.5L21,19.5z" />
<GeometryDrawing Geometry="F0 M56,56z M0,0z M20.5,19.5C20.5,17.8431 21.8431,16.5 23.5,16.5 25.1569,16.5 26.5,17.8431 26.5,19.5L26.5,36.5C26.5,38.1569 25.1569,39.5 23.5,39.5 21.8431,39.5 20.5,38.1569 20.5,36.5L20.5,19.5z M23.5,17.5C22.3954,17.5,21.5,18.3954,21.5,19.5L21.5,36.5C21.5,37.6046 22.3954,38.5 23.5,38.5 24.6046,38.5 25.5,37.6046 25.5,36.5L25.5,19.5C25.5,18.3954,24.6046,17.5,23.5,17.5z">
<GeometryDrawing.Brush>
<SolidColorBrush Color="#FF6F6F6F" Opacity="0.25" />
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Brush="#FFD1CFCD" Geometry="F1 M56,56z M0,0z M30,19.5C30,18.1193,31.1193,17,32.5,17L32.5,17C33.8807,17,35,18.1193,35,19.5L35,36.5C35,37.8807,33.8807,39,32.5,39L32.5,39C31.1193,39,30,37.8807,30,36.5L30,19.5z" />
<GeometryDrawing Geometry="F0 M56,56z M0,0z M29.5,19.5C29.5,17.8431 30.8431,16.5 32.5,16.5 34.1569,16.5 35.5,17.8431 35.5,19.5L35.5,36.5C35.5,38.1569 34.1569,39.5 32.5,39.5 30.8431,39.5 29.5,38.1569 29.5,36.5L29.5,19.5z M32.5,17.5C31.3954,17.5,30.5,18.3954,30.5,19.5L30.5,36.5C30.5,37.6046 31.3954,38.5 32.5,38.5 33.6046,38.5 34.5,37.6046 34.5,36.5L34.5,19.5C34.5,18.3954,33.6046,17.5,32.5,17.5z">
<GeometryDrawing.Brush>
<SolidColorBrush Color="#FF6F6F6F" Opacity="0.25" />
</GeometryDrawing.Brush>
</GeometryDrawing>
</DrawingGroup>
</ResourceDictionary>
+114 -91
View File
@@ -15,14 +15,8 @@ using System.Threading;
using Application = System.Windows.Application;
using Point = System.Windows.Point;
using System.Diagnostics;
using iNKORE.UI.WPF.Modern.Controls;
using System.IO;
using System.Windows.Media.Effects;
using static System.Net.Mime.MediaTypeNames;
using System.Text;
using System.Globalization;
using System.Windows.Data;
using System.Xml.Linq;
using Image = System.Windows.Controls.Image;
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
namespace Ink_Canvas {
@@ -514,6 +508,8 @@ namespace Ink_Canvas {
Thread.Sleep(100);
Application.Current.Dispatcher.Invoke(() => { ViewboxFloatingBarMarginAnimation(60); });
})).Start();
HideSubPanels();
if (GridTransparencyFakeBackground.Background == Brushes.Transparent)
{
@@ -527,11 +523,6 @@ namespace Ink_Canvas {
}
BtnHideInkCanvas_Click(BtnHideInkCanvas, null);
HideSubPanels();
}
else
{
HideSubPanels();
}
if (Settings.Gesture.AutoSwitchTwoFingerGesture) // 自动关闭多指书写、开启双指移动
@@ -1280,6 +1271,10 @@ namespace Ink_Canvas {
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
if (sender==Cursor_Icon && lastBorderMouseDownObject != Cursor_Icon) return;
// 禁用高级橡皮擦系统
DisableAdvancedEraserSystem();
// 隱藏高亮
FloatingbarSelectionBG.Visibility = Visibility.Hidden;
System.Windows.Controls.Canvas.SetLeft(FloatingbarSelectionBG, 0);
@@ -1358,6 +1353,9 @@ namespace Ink_Canvas {
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
if (sender == Pen_Icon && lastBorderMouseDownObject != Pen_Icon) return;
// 禁用高级橡皮擦系统
DisableAdvancedEraserSystem();
FloatingbarSelectionBG.Visibility = Visibility.Visible;
System.Windows.Controls.Canvas.SetLeft(FloatingbarSelectionBG, 28);
@@ -1429,6 +1427,10 @@ namespace Ink_Canvas {
}
}
else {
// 切换到批注模式时,确保保存当前图片信息
if (currentMode != 0) {
SaveStrokes();
}
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
ColorSwitchCheck();
HideSubPanels("pen", true);
@@ -1443,86 +1445,75 @@ namespace Ink_Canvas {
}
private void EraserIcon_Click(object sender, RoutedEventArgs e) {
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
if (sender == Eraser_Icon && lastBorderMouseDownObject != Eraser_Icon) return;
FloatingbarSelectionBG.Visibility = Visibility.Visible;
System.Windows.Controls.Canvas.SetLeft(FloatingbarSelectionBG, 84);
forceEraser = true;
EnterMultiTouchModeIfNeeded();
bool isAlreadyEraser = inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint;
forceEraser = false;
forcePointEraser = true;
// 即使手掌触发过面积擦,也强制应用当前的EraserShapeType设置
ApplyCurrentEraserShape();
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
if (EraserSizePanel.Visibility == Visibility.Collapsed) {
AnimationsHelper.HideWithSlideAndFade(BorderTools);
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
AnimationsHelper.HideWithSlideAndFade(PenPalette);
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
AnimationsHelper.HideWithSlideAndFade(BorderTools);
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
AnimationsHelper.ShowWithSlideFromBottomAndFade(EraserSizePanel);
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardEraserSizePanel);
} else {
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
AnimationsHelper.HideWithSlideAndFade(BorderTools);
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
AnimationsHelper.HideWithSlideAndFade(PenPalette);
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
AnimationsHelper.HideWithSlideAndFade(BorderTools);
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
}
}
else {
HideSubPanels("eraser");
}
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
drawingShapeMode = 0;
inkCanvas_EditingModeChanged(inkCanvas, null);
CancelSingleFingerDragMode();
}
// 新增方法,根据当前设置应用橡皮擦形状
public void ApplyCurrentEraserShape() {
double k = 1;
switch (Settings.Canvas.EraserSize) {
case 0:
k = Settings.Canvas.EraserShapeType == 0 ? 0.5 : 0.7;
break;
case 1:
k = Settings.Canvas.EraserShapeType == 0 ? 0.8 : 0.9;
break;
case 3:
k = Settings.Canvas.EraserShapeType == 0 ? 1.25 : 1.2;
break;
case 4:
k = Settings.Canvas.EraserShapeType == 0 ? 1.5 : 1.3;
break;
// 切换到橡皮擦模式时,确保保存当前图片信息
if (!isAlreadyEraser && currentMode != 0) {
SaveStrokes();
}
if (Settings.Canvas.EraserShapeType == 0) {
// 圆形擦
inkCanvas.EraserShape = new EllipseStylusShape(k * 90, k * 90);
} else if (Settings.Canvas.EraserShapeType == 1) {
// 矩形黑板擦
inkCanvas.EraserShape = new RectangleStylusShape(k * 90 * 0.6, k * 90);
// 启用新的高级橡皮擦系统
EnableAdvancedEraserSystem();
// 使用新的高级橡皮擦系统
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
ApplyAdvancedEraserShape(); // 使用新的橡皮擦形状应用方法
SetCursorBasedOnEditingMode(inkCanvas);
HideSubPanels("eraser"); // 高亮橡皮按钮
// 显示橡皮擦视觉反馈(用于测试)
// 注意:eraserVisualBorder在MW_Eraser.cs中定义,这里无法直接访问
Trace.WriteLine($"Advanced Eraser: Eraser button clicked, current size: {currentEraserSize}, circle: {isCurrentEraserCircle}");
if (isAlreadyEraser) {
// 已是橡皮状态,再次点击才弹出/收起面板
if (EraserSizePanel.Visibility == Visibility.Collapsed) {
AnimationsHelper.ShowWithSlideFromBottomAndFade(EraserSizePanel);
if (BoardEraserSizePanel != null)
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardEraserSizePanel);
} else {
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
if (BoardEraserSizePanel != null)
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
}
}
}
private void BoardEraserIcon_Click(object sender, RoutedEventArgs e) {
EnterMultiTouchModeIfNeeded();
bool isAlreadyEraser = inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint;
forceEraser = false;
forcePointEraser = true;
drawingShapeMode = 0;
// 启用新的高级橡皮擦系统
EnableAdvancedEraserSystem();
// 使用新的高级橡皮擦系统
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
ApplyAdvancedEraserShape(); // 使用新的橡皮擦形状应用方法
SetCursorBasedOnEditingMode(inkCanvas);
HideSubPanels("eraser"); // 高亮橡皮按钮
if (isAlreadyEraser) {
// 已是橡皮状态,再次点击才弹出/收起面板
if (BoardEraserSizePanel != null && BoardEraserSizePanel.Visibility == Visibility.Collapsed) {
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardEraserSizePanel);
AnimationsHelper.ShowWithSlideFromBottomAndFade(EraserSizePanel);
} else {
if (BoardEraserSizePanel != null)
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
}
}
}
private void EraserIconByStrokes_Click(object sender, RoutedEventArgs e) {
EnterMultiTouchModeIfNeeded();
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
@@ -1555,6 +1546,9 @@ namespace Ink_Canvas {
}
private void SelectIcon_MouseUp(object sender, RoutedEvent e) {
// 禁用高级橡皮擦系统
DisableAdvancedEraserSystem();
forceEraser = true;
drawingShapeMode = 0;
inkCanvas.IsManipulationEnabled = false;
@@ -1772,6 +1766,9 @@ namespace Ink_Canvas {
ClearStrokes(true);
RestoreStrokes();
// 退出白板时清空图片
inkCanvas.Children.Clear();
if (BtnSwitchTheme.Content.ToString() == "浅色") {
BtnSwitch.Content = "黑板";
BtnExit.Foreground = Brushes.White;
@@ -1780,12 +1777,10 @@ namespace Ink_Canvas {
BtnSwitch.Content = "白板";
if (isPresentationHaveBlackSpace) {
BtnExit.Foreground = Brushes.White;
//SymbolIconBtnColorBlackContent.Foreground = Brushes.White;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
}
else {
BtnExit.Foreground = Brushes.Black;
//SymbolIconBtnColorBlackContent.Foreground = Brushes.White;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light;
}
}
@@ -1809,22 +1804,22 @@ namespace Ink_Canvas {
ClearStrokes(true);
RestoreStrokes(true);
// 退出白板时清空图片
inkCanvas.Children.Clear();
if (BtnSwitchTheme.Content.ToString() == "浅色") {
BtnSwitch.Content = "黑板";
BtnExit.Foreground = Brushes.White;
//SymbolIconBtnColorBlackContent.Foreground = Brushes.Black;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
}
else {
BtnSwitch.Content = "白板";
if (isPresentationHaveBlackSpace) {
BtnExit.Foreground = Brushes.White;
//SymbolIconBtnColorBlackContent.Foreground = Brushes.White;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
}
else {
BtnExit.Foreground = Brushes.Black;
//SymbolIconBtnColorBlackContent.Foreground = Brushes.White;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light;
}
}
@@ -1846,12 +1841,10 @@ namespace Ink_Canvas {
BtnSwitch.Content = "屏幕";
if (BtnSwitchTheme.Content.ToString() == "浅色") {
BtnExit.Foreground = Brushes.White;
//SymbolIconBtnColorBlackContent.Foreground = Brushes.Black;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
}
else {
BtnExit.Foreground = Brushes.Black;
//SymbolIconBtnColorBlackContent.Foreground = Brushes.White;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light;
}
@@ -1996,7 +1989,37 @@ namespace Ink_Canvas {
#endregion
private async void InsertImage_MouseUp(object sender, MouseButtonEventArgs e)
{
var dialog = new Microsoft.Win32.OpenFileDialog
{
Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
};
if (dialog.ShowDialog() == true)
{
string filePath = dialog.FileName;
Image image = await CreateAndCompressImageAsync(filePath); // 补充image定义
if (image != null)
{
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
// 新缩放逻辑:最大宽高为画布一半,并居中
double maxWidth = inkCanvas.ActualWidth / 2;
double maxHeight = inkCanvas.ActualHeight / 2;
double scaleX = maxWidth / image.Width;
double scaleY = maxHeight / image.Height;
double scale = Math.Min(1, Math.Min(scaleX, scaleY));
image.Width = image.Width * scale;
image.Height = image.Height * scale;
InkCanvas.SetLeft(image, (inkCanvas.ActualWidth - image.Width) / 2);
InkCanvas.SetTop(image, (inkCanvas.ActualHeight - image.Height) / 2);
inkCanvas.Children.Add(image);
timeMachine.CommitElementInsertHistory(image);
}
}
}
}
}
+1 -7
View File
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ink_Canvas {
namespace Ink_Canvas {
public static class XamlGraphicsIconGeometries {
public static string LinedCursorIcon =
"F1 M24,24z M0,0z M5.72106,15.9716L3.71327,3.00395C3.6389,2.6693 3.65747,2.41831 3.76902,2.25099 3.88057,2.08366 4.0479,2 4.271,2 4.4941,2 4.71711,2.07437 4.94021,2.2231 6.72502,3.39438 9.28149,5.10481 12.6094,7.3544 15.677,9.45526 18.1125,11.1285 19.9159,12.3742 20.1204,12.5229 20.2505,12.6995 20.3062,12.904 20.362,13.1085 20.3249,13.2944 20.1947,13.4618 20.0832,13.6105 19.8973,13.6849 19.637,13.6849L13.3902,13.6849 17.6291,19.7365C17.722,19.8666 17.75,20.0153 17.7128,20.1827 17.6942,20.3314 17.6198,20.4522 17.4897,20.5452L15.5654,21.8838C15.4353,21.9768 15.2865,22.0139 15.1192,21.9953 14.9704,21.9582 14.8496,21.8745 14.7566,21.7444L10.2389,15.2745 7.58956,19.9038C7.45942,20.1269 7.30144,20.2756 7.11552,20.35 6.92961,20.4058 6.75292,20.3872 6.5856,20.2942 6.43686,20.2013 6.34392,20.0339 6.30673,19.7922L6.00007,17.8959C5.88852,17.0779,5.79543,16.4364,5.72106,15.9716z";
File diff suppressed because it is too large Load Diff
+1 -9
View File
@@ -1,17 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using Ink_Canvas.Helpers;
namespace Ink_Canvas
+539 -64
View File
@@ -1,5 +1,4 @@
using Ink_Canvas.Helpers;
using Microsoft.Win32;
using System;
using System.IO;
using System.Windows;
@@ -8,10 +7,22 @@ using System.Windows.Input;
using File = System.IO.File;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
using System.Collections.Generic;
using System.Windows.Controls;
using Newtonsoft.Json;
namespace Ink_Canvas {
// 1. 定义元素信息结构
public class CanvasElementInfo
{
public string Type { get; set; } // "Image"
public string SourcePath { get; set; }
public double Left { get; set; }
public double Top { get; set; }
public double Width { get; set; }
public double Height { get; set; }
}
public partial class MainWindow : Window {
private void SymbolIconSaveStrokes_MouseUp(object sender, MouseButtonEventArgs e) {
if (lastBorderMouseDownObject != sender || inkCanvas.Visibility != Visibility.Visible) return;
@@ -38,76 +49,93 @@ namespace Ink_Canvas {
//savePathWithName = savePath + @"\" + DateTime.Now.ToString("u").Replace(':', '-') + ".icstk";
savePathWithName = savePath + @"\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss-fff") + ".icstk";
var fs = new FileStream(savePathWithName, FileMode.Create);
if (Settings.Automation.IsSaveFullPageStrokes)
{
// 全页面保存模式 - 保存整个墨迹页面的图像
var bitmap = new System.Drawing.Bitmap(
(int)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
(int)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height);
// 全页面保存模式 - 检查是否存在多页面墨迹
bool hasMultiplePages = false;
List<StrokeCollection> allPageStrokes = new List<StrokeCollection>();
using (var g = System.Drawing.Graphics.FromImage(bitmap))
// 检查PPT放映模式下的多页面墨迹
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible && pptApplication != null)
{
// 创建黑色或透明背景
System.Drawing.Color bgColor = Settings.Canvas.UsingWhiteboard
? System.Drawing.Color.White
: System.Drawing.Color.FromArgb(22, 41, 36); // 黑板背景色
g.Clear(bgColor);
// 将InkCanvas墨迹渲染到Visual
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
hasMultiplePages = true;
// 收集PPT放映模式下的所有页面墨迹
for (int i = 1; i <= pptApplication.SlideShowWindows[1].Presentation.Slides.Count; i++)
{
// 创建一个VisualBrush,使用inkCanvas作为源
var visualBrush = new VisualBrush(inkCanvas);
// 绘制矩形并填充为inkCanvas的内容
dc.DrawRectangle(visualBrush, null, new Rect(0, 0, inkCanvas.ActualWidth, inkCanvas.ActualHeight));
if (memoryStreams[i] != null && memoryStreams[i].Length > 8)
{
memoryStreams[i].Position = 0;
allPageStrokes.Add(new StrokeCollection(memoryStreams[i]));
}
else if (i == previousSlideID && inkCanvas.Strokes.Count > 0)
{
// 当前页面的墨迹
allPageStrokes.Add(inkCanvas.Strokes.Clone());
}
else
{
allPageStrokes.Add(new StrokeCollection()); // 空页面
}
}
// 创建适合墨迹画布尺寸的渲染位图
var rtb = new RenderTargetBitmap(
(int)inkCanvas.ActualWidth, (int)inkCanvas.ActualHeight,
96, 96,
PixelFormats.Pbgra32);
rtb.Render(visual);
// 转换为GDI+ Bitmap并保存
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
using (var ms = new MemoryStream())
}
// 检查白板模式下的多页面墨迹
else if (currentMode != 0 && WhiteboardTotalCount > 1)
{
hasMultiplePages = true;
// 收集白板模式下的所有页面墨迹
for (int i = 1; i <= WhiteboardTotalCount; i++)
{
encoder.Save(ms);
ms.Seek(0, SeekOrigin.Begin);
var imgBitmap = new System.Drawing.Bitmap(ms);
// 将生成的墨迹图像绘制到屏幕截图上
// 居中绘制,确保墨迹位于屏幕中央
int x = (bitmap.Width - imgBitmap.Width) / 2;
int y = (bitmap.Height - imgBitmap.Height) / 2;
g.DrawImage(imgBitmap, x, y);
// 保存为PNG
string imagePathWithName = Path.ChangeExtension(savePathWithName, "png");
bitmap.Save(imagePathWithName, System.Drawing.Imaging.ImageFormat.Png);
// 仍然保存墨迹文件以兼容旧版本
inkCanvas.Strokes.Save(fs);
if (TimeMachineHistories[i] != null)
{
// 从历史记录中恢复墨迹
var strokes = ApplyHistoriesToNewStrokeCollection(TimeMachineHistories[i]);
allPageStrokes.Add(strokes);
}
else
{
allPageStrokes.Add(new StrokeCollection()); // 空页面
}
}
}
// 显示提示
if (newNotice) ShowNotification("墨迹成功全页面保存至 " + Path.ChangeExtension(savePathWithName, "png"));
if (hasMultiplePages && allPageStrokes.Count > 0)
{
// 多页面墨迹保存为压缩包
string zipFileName = Path.ChangeExtension(savePathWithName, "zip");
SaveMultiPageStrokesAsZip(allPageStrokes, zipFileName, newNotice);
}
else
{
// 单页面墨迹保存为图像
SaveSinglePageStrokesAsImage(savePathWithName, newNotice);
}
}
else
{
// 常规保存模式 - 仅保存墨迹对象
var fs = new FileStream(savePathWithName, FileMode.Create);
inkCanvas.Strokes.Save(fs);
fs.Close();
// 保存元素信息
var elementInfos = new List<CanvasElementInfo>();
foreach (var child in inkCanvas.Children)
{
if (child is Image img && img.Source is BitmapImage bmp)
{
elementInfos.Add(new CanvasElementInfo
{
Type = "Image",
SourcePath = bmp.UriSource?.LocalPath ?? "",
Left = InkCanvas.GetLeft(img),
Top = InkCanvas.GetTop(img),
Width = img.Width,
Height = img.Height
});
}
}
File.WriteAllText(Path.ChangeExtension(savePathWithName, ".elements.json"), JsonConvert.SerializeObject(elementInfos, Formatting.Indented));
if (newNotice) ShowNotification("墨迹成功保存至 " + savePathWithName);
}
fs.Close();
}
catch (Exception ex) {
ShowNotification("墨迹保存失败");
@@ -115,6 +143,194 @@ namespace Ink_Canvas {
}
}
/// <summary>
/// 将多页面墨迹保存为压缩包
/// </summary>
private void SaveMultiPageStrokesAsZip(List<StrokeCollection> allPageStrokes, string zipFileName, bool newNotice)
{
try
{
// 创建临时目录来存放文件
string tempDir = Path.Combine(Path.GetTempPath(), $"InkCanvas_MultiPage_{DateTime.Now:yyyyMMdd_HHmmss}");
Directory.CreateDirectory(tempDir);
try
{
// 保存所有页面的文件到临时目录
for (int i = 0; i < allPageStrokes.Count; i++)
{
var strokes = allPageStrokes[i];
if (strokes.Count > 0)
{
// 保存墨迹文件
string strokeFileName = Path.Combine(tempDir, $"page_{i + 1:D4}.icstk");
using (var fs = new FileStream(strokeFileName, FileMode.Create))
{
strokes.Save(fs);
}
// 保存页面图像
string imageFileName = Path.Combine(tempDir, $"page_{i + 1:D4}.png");
using (var fs = new FileStream(imageFileName, FileMode.Create))
{
SavePageAsImage(strokes, fs);
}
}
}
// 保存元数据信息
string metadataFile = Path.Combine(tempDir, "metadata.txt");
using (var writer = new StreamWriter(metadataFile, false, System.Text.Encoding.UTF8))
{
writer.WriteLine($"保存时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
writer.WriteLine($"总页数: {allPageStrokes.Count}");
writer.WriteLine($"模式: {(currentMode == 0 ? "PPT放映" : "")}");
if (currentMode != 0)
{
writer.WriteLine($"当前页面: {CurrentWhiteboardIndex}");
writer.WriteLine($"总页面数: {WhiteboardTotalCount}");
}
else if (pptApplication != null)
{
writer.WriteLine($"PPT名称: {pptApplication.SlideShowWindows[1].Presentation.Name}");
writer.WriteLine($"PPT总页数: {pptApplication.SlideShowWindows[1].Presentation.Slides.Count}");
writer.WriteLine($"PPT文件路径: {pptApplication.SlideShowWindows[1].Presentation.FullName}");
}
for (int i = 0; i < allPageStrokes.Count; i++)
{
writer.WriteLine($"页面 {i + 1}: {allPageStrokes[i].Count} 条墨迹");
}
}
// 使用.NET Framework内置的压缩功能创建ZIP文件
if (File.Exists(zipFileName))
File.Delete(zipFileName);
// 使用System.IO.Compression.FileSystem来创建ZIP
System.IO.Compression.ZipFile.CreateFromDirectory(tempDir, zipFileName);
if (newNotice) ShowNotification($"多页面墨迹成功保存至压缩包 {zipFileName}");
}
finally
{
// 清理临时目录
try
{
if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理临时目录失败: {ex.ToString()}", LogHelper.LogType.Warning);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存多页面墨迹压缩包失败: {ex.ToString()}", LogHelper.LogType.Error);
throw;
}
}
/// <summary>
/// 将单页面墨迹保存为图像
/// </summary>
private void SaveSinglePageStrokesAsImage(string savePathWithName, bool newNotice)
{
// 全页面保存模式 - 保存整个墨迹页面的图像
var bitmap = new System.Drawing.Bitmap(
(int)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
(int)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height);
using (var g = System.Drawing.Graphics.FromImage(bitmap))
{
// 创建黑色或透明背景
System.Drawing.Color bgColor = Settings.Canvas.UsingWhiteboard
? System.Drawing.Color.White
: System.Drawing.Color.FromArgb(22, 41, 36); // 黑板背景色
g.Clear(bgColor);
// 将InkCanvas墨迹渲染到Visual
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
// 创建一个VisualBrush,使用inkCanvas作为源
var visualBrush = new VisualBrush(inkCanvas);
// 绘制矩形并填充为inkCanvas的内容
dc.DrawRectangle(visualBrush, null, new Rect(0, 0, inkCanvas.ActualWidth, inkCanvas.ActualHeight));
}
// 创建适合墨迹画布尺寸的渲染位图
var rtb = new RenderTargetBitmap(
(int)inkCanvas.ActualWidth, (int)inkCanvas.ActualHeight,
96, 96,
PixelFormats.Pbgra32);
rtb.Render(visual);
// 转换为GDI+ Bitmap并保存
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
using (var ms = new MemoryStream())
{
encoder.Save(ms);
ms.Seek(0, SeekOrigin.Begin);
var imgBitmap = new System.Drawing.Bitmap(ms);
// 将生成的墨迹图像绘制到屏幕截图上
// 居中绘制,确保墨迹位于屏幕中央
int x = (bitmap.Width - imgBitmap.Width) / 2;
int y = (bitmap.Height - imgBitmap.Height) / 2;
g.DrawImage(imgBitmap, x, y);
// 保存为PNG
string imagePathWithName = Path.ChangeExtension(savePathWithName, "png");
bitmap.Save(imagePathWithName, System.Drawing.Imaging.ImageFormat.Png);
// 仍然保存墨迹文件以兼容旧版本
var fs = new FileStream(savePathWithName, FileMode.Create);
inkCanvas.Strokes.Save(fs);
fs.Close();
}
}
// 显示提示
if (newNotice) ShowNotification("墨迹成功全页面保存至 " + Path.ChangeExtension(savePathWithName, "png"));
}
/// <summary>
/// 将指定墨迹集合保存为图像到指定流
/// </summary>
private void SavePageAsImage(StrokeCollection strokes, Stream outputStream)
{
try
{
// 创建临时InkCanvas来渲染墨迹
var tempCanvas = new InkCanvas();
tempCanvas.Strokes = strokes;
tempCanvas.Width = inkCanvas.ActualWidth;
tempCanvas.Height = inkCanvas.ActualHeight;
// 创建渲染位图
var rtb = new RenderTargetBitmap(
(int)tempCanvas.Width, (int)tempCanvas.Height,
96, 96,
PixelFormats.Pbgra32);
rtb.Render(tempCanvas);
// 保存为PNG
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
encoder.Save(outputStream);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存页面图像失败: {ex.ToString()}", LogHelper.LogType.Error);
throw;
}
}
private void SymbolIconOpenStrokes_MouseUp(object sender, MouseButtonEventArgs e) {
if (lastBorderMouseDownObject != sender) return;
AnimationsHelper.HideWithSlideAndFade(BorderTools);
@@ -123,13 +339,256 @@ namespace Ink_Canvas {
var openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = Settings.Automation.AutoSavedStrokesLocation;
openFileDialog.Title = "打开墨迹文件";
openFileDialog.Filter = "Ink Canvas Strokes File (*.icstk)|*.icstk";
openFileDialog.Filter = "Ink Canvas Strokes File (*.icstk)|*.icstk|ICC压缩包 (*.zip)|*.zip";
if (openFileDialog.ShowDialog() != true) return;
LogHelper.WriteLogToFile($"Strokes Insert: Name: {openFileDialog.FileName}",
LogHelper.LogType.Event);
try {
string fileExtension = Path.GetExtension(openFileDialog.FileName).ToLower();
if (fileExtension == ".zip") {
// 处理ICC压缩包
OpenICCZipFile(openFileDialog.FileName);
} else {
// 处理单个墨迹文件
OpenSingleStrokeFile(openFileDialog.FileName);
}
if (inkCanvas.Visibility != Visibility.Visible) SymbolIconCursor_Click(sender, null);
}
catch (Exception ex) {
ShowNotification("墨迹打开失败");
LogHelper.WriteLogToFile($"墨迹打开失败: {ex.ToString()}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 打开ICC创建的.zip压缩包
/// </summary>
private void OpenICCZipFile(string zipFilePath) {
try {
// 创建临时目录来解压文件
string tempDir = Path.Combine(Path.GetTempPath(), $"InkCanvas_Open_{DateTime.Now:yyyyMMdd_HHmmss}");
Directory.CreateDirectory(tempDir);
try {
// 解压ZIP文件
System.IO.Compression.ZipFile.ExtractToDirectory(zipFilePath, tempDir);
// 读取元数据文件
string metadataFile = Path.Combine(tempDir, "metadata.txt");
if (!File.Exists(metadataFile)) {
throw new Exception("压缩包中未找到元数据文件");
}
var metadata = ReadMetadataFile(metadataFile);
// 根据元数据信息决定恢复模式
bool isPPTMode = metadata.ContainsKey("模式") && metadata["模式"].Contains("PPT放映");
bool isWhiteboardMode = metadata.ContainsKey("模式") && metadata["模式"].Contains("白板");
// 检查当前是否处于PPT模式
bool isCurrentlyInPPTMode = BtnPPTSlideShowEnd.Visibility == Visibility.Visible && pptApplication != null;
// 检查当前是否处于白板模式
bool isCurrentlyInWhiteboardMode = currentMode != 0;
// 严格模式隔离:只在对应模式下恢复对应墨迹
if (isPPTMode && isCurrentlyInPPTMode) {
// 只在PPT放映模式下恢复PPT墨迹
RestorePPTStrokesFromZip(tempDir, metadata);
} else if (isWhiteboardMode && isCurrentlyInWhiteboardMode) {
// 只在白板模式下恢复白板墨迹
RestoreWhiteboardStrokesFromZip(tempDir, metadata);
} else {
// 模式不匹配时,显示提示信息
string savedMode = isPPTMode ? "PPT放映" : (isWhiteboardMode ? "白板" : "未知");
string currentMode = isCurrentlyInPPTMode ? "PPT放映" : (isCurrentlyInWhiteboardMode ? "白板" : "桌面");
ShowNotification($"墨迹保存模式({savedMode})与当前模式({currentMode})不匹配,无法恢复墨迹");
LogHelper.WriteLogToFile($"模式不匹配:保存模式={savedMode},当前模式={currentMode}", LogHelper.LogType.Warning);
}
ShowNotification($"成功打开ICC压缩包,共{(metadata.ContainsKey("") ? metadata[""] : "0")}页");
}
finally {
// 清理临时目录
try {
if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true);
}
catch (Exception ex) {
LogHelper.WriteLogToFile($"清理临时目录失败: {ex.ToString()}", LogHelper.LogType.Warning);
}
}
}
catch (Exception ex) {
LogHelper.WriteLogToFile($"打开ICC压缩包失败: {ex.ToString()}", LogHelper.LogType.Error);
throw;
}
}
/// <summary>
/// 读取元数据文件
/// </summary>
private Dictionary<string, string> ReadMetadataFile(string metadataPath) {
var metadata = new Dictionary<string, string>();
using (var reader = new StreamReader(metadataPath, System.Text.Encoding.UTF8)) {
string line;
while ((line = reader.ReadLine()) != null) {
if (line.Contains(":")) {
var parts = line.Split(new[] { ':' }, 2);
if (parts.Length == 2) {
metadata[parts[0].Trim()] = parts[1].Trim();
}
}
}
}
return metadata;
}
/// <summary>
/// 从ZIP文件恢复PPT墨迹
/// </summary>
private void RestorePPTStrokesFromZip(string tempDir, Dictionary<string, string> metadata) {
try {
// 确保当前处于PPT放映模式
if (BtnPPTSlideShowEnd.Visibility != Visibility.Visible || pptApplication == null) {
throw new InvalidOperationException("当前不在PPT放映模式,无法恢复PPT墨迹");
}
// 检查PPT文件路径是否匹配
if (metadata.ContainsKey("PPT文件路径"))
{
string savedPptPath = metadata["PPT文件路径"];
string currentPptPath = pptApplication.SlideShowWindows[1].Presentation.FullName;
if (!string.IsNullOrEmpty(savedPptPath) && !string.IsNullOrEmpty(currentPptPath))
{
// 使用文件路径哈希值进行比较,避免路径格式差异
string savedHash = GetFileHash(savedPptPath);
string currentHash = GetFileHash(currentPptPath);
if (savedHash != currentHash)
{
throw new InvalidOperationException($"墨迹文件与当前PPT文件不匹配。保存的PPT: {savedPptPath},当前PPT: {currentPptPath}");
}
}
}
// 清空当前墨迹
ClearStrokes(true);
timeMachine.ClearStrokeHistory();
// 重置PPT墨迹存储
if (memoryStreams == null) {
memoryStreams = new MemoryStream[50];
}
// 读取所有页面的墨迹文件
var files = Directory.GetFiles(tempDir, "page_*.icstk");
foreach (var file in files) {
var fileName = Path.GetFileNameWithoutExtension(file);
if (fileName.StartsWith("page_") && int.TryParse(fileName.Substring(5), out int pageNumber)) {
using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read)) {
var strokes = new StrokeCollection(fs);
if (strokes.Count > 0) {
var ms = new MemoryStream();
strokes.Save(ms);
ms.Position = 0;
memoryStreams[pageNumber] = ms;
}
}
}
}
// 恢复当前页面的墨迹
if (pptApplication.SlideShowWindows.Count > 0) {
int currentSlide = pptApplication.SlideShowWindows[1].View.CurrentShowPosition;
if (memoryStreams[currentSlide] != null && memoryStreams[currentSlide].Length > 0) {
memoryStreams[currentSlide].Position = 0;
inkCanvas.Strokes.Add(new StrokeCollection(memoryStreams[currentSlide]));
}
previousSlideID = currentSlide;
}
LogHelper.WriteLogToFile($"成功恢复PPT墨迹,共{files.Length}页");
}
catch (Exception ex) {
LogHelper.WriteLogToFile($"恢复PPT墨迹失败: {ex.ToString()}", LogHelper.LogType.Error);
throw;
}
}
/// <summary>
/// 从ZIP文件恢复白板墨迹
/// </summary>
private void RestoreWhiteboardStrokesFromZip(string tempDir, Dictionary<string, string> metadata) {
try {
// 确保当前处于白板模式
if (currentMode == 0) {
throw new InvalidOperationException("当前不在白板模式,无法恢复白板墨迹");
}
// 清空当前墨迹
ClearStrokes(true);
timeMachine.ClearStrokeHistory();
// 读取总页数
int totalPages = 1;
if (metadata.ContainsKey("总页数") && int.TryParse(metadata["总页数"], out int parsedPages)) {
totalPages = parsedPages;
}
// 重置白板状态
WhiteboardTotalCount = totalPages;
CurrentWhiteboardIndex = 1;
// 清空历史记录
for (int i = 0; i < TimeMachineHistories.Length; i++) {
TimeMachineHistories[i] = null;
}
// 读取所有页面的墨迹文件
var files = Directory.GetFiles(tempDir, "page_*.icstk");
foreach (var file in files) {
var fileName = Path.GetFileNameWithoutExtension(file);
if (fileName.StartsWith("page_") && int.TryParse(fileName.Substring(5), out int pageNumber)) {
using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read)) {
var strokes = new StrokeCollection(fs);
if (strokes.Count > 0) {
// 创建历史记录
var history = new TimeMachineHistory(strokes, TimeMachineHistoryType.UserInput, false);
TimeMachineHistories[pageNumber] = new TimeMachineHistory[] { history };
}
}
}
}
// 恢复第一页的墨迹
if (TimeMachineHistories[1] != null) {
RestoreStrokes();
}
// 更新UI显示
UpdateIndexInfoDisplay();
LogHelper.WriteLogToFile($"成功恢复白板墨迹,共{totalPages}页");
}
catch (Exception ex) {
LogHelper.WriteLogToFile($"恢复白板墨迹失败: {ex.ToString()}", LogHelper.LogType.Error);
throw;
}
}
/// <summary>
/// 打开单个墨迹文件
/// </summary>
private void OpenSingleStrokeFile(string filePath) {
var fileStreamHasNoStroke = false;
using (var fs = new FileStream(openFileDialog.FileName, FileMode.Open, FileAccess.Read)) {
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) {
var strokes = new StrokeCollection(fs);
fileStreamHasNoStroke = strokes.Count == 0;
if (!fileStreamHasNoStroke) {
@@ -140,20 +599,36 @@ namespace Ink_Canvas {
}
}
// 恢复元素信息
var elementsFile = Path.ChangeExtension(filePath, ".elements.json");
if (File.Exists(elementsFile))
{
var elementInfos = JsonConvert.DeserializeObject<List<CanvasElementInfo>>(File.ReadAllText(elementsFile));
foreach (var info in elementInfos)
{
if (info.Type == "Image" && File.Exists(info.SourcePath))
{
var img = new Image
{
Source = new BitmapImage(new Uri(info.SourcePath)),
Width = info.Width,
Height = info.Height
};
InkCanvas.SetLeft(img, info.Left);
InkCanvas.SetTop(img, info.Top);
inkCanvas.Children.Add(img);
}
}
}
if (fileStreamHasNoStroke)
using (var ms = new MemoryStream(File.ReadAllBytes(openFileDialog.FileName))) {
using (var ms = new MemoryStream(File.ReadAllBytes(filePath))) {
ms.Seek(0, SeekOrigin.Begin);
var strokes = new StrokeCollection(ms);
ClearStrokes(true);
timeMachine.ClearStrokeHistory();
inkCanvas.Strokes.Add(strokes);
LogHelper.NewLog($"Strokes Insert (2): Strokes Count: {strokes.Count.ToString()}");
}
if (inkCanvas.Visibility != Visibility.Visible) SymbolIconCursor_Click(sender, null);
}
catch {
ShowNotification("墨迹打开失败");
}
}
}
@@ -3,11 +3,9 @@ using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Point = System.Windows.Point;
namespace Ink_Canvas {
@@ -244,6 +242,7 @@ namespace Ink_Canvas {
}
private void BtnSelect_Click(object sender, RoutedEventArgs e) {
ExitMultiTouchModeIfNeeded();
forceEraser = true;
drawingShapeMode = 0;
inkCanvas.IsManipulationEnabled = false;
@@ -323,13 +322,9 @@ namespace Ink_Canvas {
}
private void GridInkCanvasSelectionCover_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) {
// 手掌擦时禁止移动/缩放
if (isLastTouchEraser || inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
return;
// 三指及以上禁止缩放
bool disableScale = dec.Count >= 3;
try {
if (dec.Count >= 1) {
bool disableScale = dec.Count >= 3;
var md = e.DeltaManipulation;
var trans = md.Translation; // 获得位移矢量
var rotate = md.Rotation; // 获得旋转角度
@@ -422,5 +417,24 @@ namespace Ink_Canvas {
StrokesSelectionClone = new StrokeCollection();
}
}
private void LassoSelect_Click(object sender, RoutedEventArgs e) {
ExitMultiTouchModeIfNeeded();
forceEraser = false;
forcePointEraser = false;
drawingShapeMode = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
SetCursorBasedOnEditingMode(inkCanvas);
}
private void BtnLassoSelect_Click(object sender, RoutedEventArgs e) {
ExitMultiTouchModeIfNeeded();
forceEraser = false;
forcePointEraser = false;
drawingShapeMode = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
inkCanvas.IsManipulationEnabled = true;
SetCursorBasedOnEditingMode(inkCanvas);
}
}
}
+210 -22
View File
@@ -8,11 +8,12 @@ using System.Windows.Controls;
using System.Windows.Input;
using File = System.IO.File;
using System.Windows.Media;
using System.Windows.Ink;
using System.Windows.Media.Imaging;
using System.Windows.Interop;
using Hardcodet.Wpf.TaskbarNotification;
using OSVersionExtension;
using Microsoft.Win32;
using System.IO;
namespace Ink_Canvas {
public partial class MainWindow : Window {
@@ -867,12 +868,19 @@ namespace Ink_Canvas {
private void ComboBoxEraserSize_SelectionChanged(object sender, SelectionChangedEventArgs e) {
if (!isLoaded) return;
Settings.Canvas.EraserSize = ComboBoxEraserSize.SelectedIndex;
// 使用新的高级橡皮擦形状应用方法
ApplyAdvancedEraserShape();
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
}
SaveSettingsToFile();
}
private void ComboBoxEraserSizeFloatingBar_SelectionChanged(object sender, SelectionChangedEventArgs e) {
if (!isLoaded) return;
ComboBox s = (ComboBox)sender;
Settings.Canvas.EraserSize = s.SelectedIndex;
if (s == ComboBoxEraserSizeFloatingBar) {
@@ -882,17 +890,14 @@ namespace Ink_Canvas {
ComboBoxEraserSizeFloatingBar.SelectedIndex = s.SelectedIndex;
ComboBoxEraserSize.SelectedIndex = s.SelectedIndex;
}
// 使用统一的方法应用橡皮擦形状
ApplyCurrentEraserShape();
// 确保当前处于橡皮擦模式时能立即看到效果
// 使用新的高级橡皮擦形状应用方法
ApplyAdvancedEraserShape();
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
// 先切换一下模式,再切回来,确保橡皮擦形状得到刷新
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
}
SaveSettingsToFile();
}
@@ -901,10 +906,10 @@ namespace Ink_Canvas {
Settings.Canvas.EraserShapeType = 0;
SaveSettingsToFile();
CheckEraserTypeTab();
// 使用统一的方法应用橡皮擦形状
ApplyCurrentEraserShape();
// 使用新的高级橡皮擦形状应用方法
ApplyAdvancedEraserShape();
// 确保当前处于橡皮擦模式时能立即看到效果
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
@@ -915,10 +920,10 @@ namespace Ink_Canvas {
Settings.Canvas.EraserShapeType = 1;
SaveSettingsToFile();
CheckEraserTypeTab();
// 使用统一的方法应用橡皮擦形状
ApplyCurrentEraserShape();
// 使用新的高级橡皮擦形状应用方法
ApplyAdvancedEraserShape();
// 确保当前处于橡皮擦模式时能立即看到效果
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
@@ -1279,9 +1284,32 @@ namespace Ink_Canvas {
if (!isLoaded) return;
drawingAttributes.FitToCurve = ToggleSwitchFitToCurve.IsOn;
Settings.Canvas.FitToCurve = ToggleSwitchFitToCurve.IsOn;
// 启用原来的FitToCurve时自动禁用高级贝塞尔平滑
if (ToggleSwitchFitToCurve.IsOn)
{
ToggleSwitchAdvancedBezierSmoothing.IsOn = false;
Settings.Canvas.UseAdvancedBezierSmoothing = false;
}
SaveSettingsToFile();
}
private void ToggleSwitchAdvancedBezierSmoothing_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
Settings.Canvas.UseAdvancedBezierSmoothing = ToggleSwitchAdvancedBezierSmoothing.IsOn;
// 启用高级贝塞尔平滑时自动禁用原来的FitToCurve
if (ToggleSwitchAdvancedBezierSmoothing.IsOn)
{
ToggleSwitchFitToCurve.IsOn = false;
Settings.Canvas.FitToCurve = false;
drawingAttributes.FitToCurve = false;
}
SaveSettingsToFile();
}
private void ToggleSwitchAutoSaveStrokesInPowerPoint_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
Settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint = ToggleSwitchAutoSaveStrokesInPowerPoint.IsOn;
@@ -1571,7 +1599,7 @@ namespace Ink_Canvas {
Settings.PowerPointSettings.IsNotifyHiddenPage = false;
Settings.PowerPointSettings.IsEnableTwoFingerGestureInPresentationMode = false;
Settings.PowerPointSettings.IsEnableFingerGestureSlideShowControl = false;
Settings.PowerPointSettings.IsSupportWPS = true;
Settings.PowerPointSettings.IsSupportWPS = false;
Settings.Canvas.InkWidth = 2.5;
Settings.Canvas.IsShowCursor = false;
@@ -1582,11 +1610,12 @@ namespace Ink_Canvas {
Settings.Canvas.EraserShapeType = 1;
Settings.Canvas.HideStrokeWhenSelecting = false;
Settings.Canvas.ClearCanvasAndClearTimeMachine = false;
Settings.Canvas.FitToCurve = true;
Settings.Canvas.FitToCurve = false;
Settings.Canvas.UseAdvancedBezierSmoothing = true;
Settings.Canvas.EnablePressureTouchMode = false;
Settings.Canvas.DisablePressure = false;
Settings.Canvas.AutoStraightenLine = true;
Settings.Canvas.AutoStraightenLineThreshold = 30;
Settings.Canvas.AutoStraightenLineThreshold = 80;
Settings.Canvas.LineEndpointSnapping = true;
Settings.Canvas.LineEndpointSnappingThreshold = 15;
Settings.Canvas.UsingWhiteboard = false;
@@ -1813,6 +1842,100 @@ namespace Ink_Canvas {
Settings.Advanced.IsSecondConfirmWhenShutdownApp = ToggleSwitchIsSecondConfimeWhenShutdownApp.IsOn;
SaveSettingsToFile();
}
private void ToggleSwitchIsAutoBackupBeforeUpdate_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
Settings.Advanced.IsAutoBackupBeforeUpdate = ToggleSwitchIsAutoBackupBeforeUpdate.IsOn;
SaveSettingsToFile();
}
private void BtnManualBackup_Click(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
try {
// 确保Backups目录存在
string backupDir = Path.Combine(App.RootPath, "Backups");
if (!Directory.Exists(backupDir)) {
Directory.CreateDirectory(backupDir);
LogHelper.WriteLogToFile($"创建备份目录: {backupDir}", LogHelper.LogType.Info);
}
// 创建备份文件名(使用当前日期时间)
string backupFileName = $"Settings_Backup_{DateTime.Now:yyyyMMdd_HHmmss}.json";
string backupPath = Path.Combine(backupDir, backupFileName);
// 序列化当前设置并保存到备份文件
string settingsJson = JsonConvert.SerializeObject(Settings, Formatting.Indented);
File.WriteAllText(backupPath, settingsJson);
LogHelper.WriteLogToFile($"成功创建设置备份: {backupPath}", LogHelper.LogType.Info);
MessageBox.Show($"设置已成功备份到:\n{backupPath}", "备份成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex) {
LogHelper.WriteLogToFile($"创建设置备份时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"创建备份失败: {ex.Message}", "备份失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void BtnRestoreBackup_Click(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
try {
// 确保Backups目录存在
string backupDir = Path.Combine(App.RootPath, "Backups");
if (!Directory.Exists(backupDir)) {
Directory.CreateDirectory(backupDir);
LogHelper.WriteLogToFile($"创建备份目录: {backupDir}", LogHelper.LogType.Info);
MessageBox.Show("没有找到备份文件,请先创建备份", "还原失败", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
// 打开文件选择对话框
OpenFileDialog dlg = new OpenFileDialog();
dlg.InitialDirectory = backupDir;
dlg.Filter = "设置备份文件|Settings_Backup_*.json|所有JSON文件|*.json";
dlg.Title = "选择要还原的备份文件";
if (dlg.ShowDialog() == true) {
// 读取备份文件
string backupJson = File.ReadAllText(dlg.FileName);
// 反序列化备份数据
Settings backupSettings = JsonConvert.DeserializeObject<Settings>(backupJson);
if (backupSettings != null) {
// 确认是否要还原
if (MessageBox.Show("确定要还原选择的备份文件吗?当前设置将被覆盖。", "确认还原",
MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) {
// 备份当前设置,以防出错
string currentSettingsJson = JsonConvert.SerializeObject(Settings, Formatting.Indented);
string tempBackupPath = Path.Combine(backupDir, $"Settings_Before_Restore_{DateTime.Now:yyyyMMdd_HHmmss}.json");
File.WriteAllText(tempBackupPath, currentSettingsJson);
// 还原设置
Settings = backupSettings;
// 保存还原后的设置到文件
SaveSettingsToFile();
// 重新加载设置到UI
LoadSettings();
LogHelper.WriteLogToFile($"成功从备份还原设置: {dlg.FileName}", LogHelper.LogType.Info);
MessageBox.Show("设置已成功还原,部分设置可能需要重启软件后生效。", "还原成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
else {
MessageBox.Show("无法解析备份文件,文件可能已损坏", "还原失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
catch (Exception ex) {
LogHelper.WriteLogToFile($"还原设置备份时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"还原备份失败: {ex.Message}", "还原失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
@@ -1892,14 +2015,42 @@ namespace Ink_Canvas {
HideSubPanels();
}
private void UpdateChannelSelector_Checked(object sender, RoutedEventArgs e) {
private async void UpdateChannelSelector_Checked(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
var radioButton = sender as System.Windows.Controls.RadioButton;
if (radioButton != null) {
string channel = radioButton.Tag.ToString();
Settings.Startup.UpdateChannel = channel == "Beta" ? UpdateChannel.Beta : UpdateChannel.Release;
UpdateChannel newChannel = channel == "Beta" ? UpdateChannel.Beta : UpdateChannel.Release;
// 如果通道没有变化,不需要执行更新检查
if (Settings.Startup.UpdateChannel == newChannel) {
return;
}
Settings.Startup.UpdateChannel = newChannel;
LogHelper.WriteLogToFile($"Settings | Update channel changed to {Settings.Startup.UpdateChannel}");
SaveSettingsToFile();
// 如果启用了自动更新,立即执行完整的检查更新操作
if (Settings.Startup.IsAutoUpdate) {
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check");
// 执行完整的更新检查
await Task.Run(async () => {
try {
// 调用主窗口的AutoUpdate方法,它会自动清除之前的更新状态并使用新通道重新检查
Dispatcher.Invoke(() => {
AutoUpdate();
});
}
catch (Exception ex) {
LogHelper.WriteLogToFile($"AutoUpdate | Error during channel switch update check: {ex.Message}", LogHelper.LogType.Error);
}
});
}
else {
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, but auto-update is disabled");
}
}
}
@@ -2007,5 +2158,42 @@ namespace Ink_Canvas {
dialog.Owner = this;
dialog.ShowDialog();
}
private void ToggleSwitchEnableWppProcessKill_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
Settings.PowerPointSettings.EnableWppProcessKill = ToggleSwitchEnableWppProcessKill.IsOn;
SaveSettingsToFile();
}
private void ToggleSwitchCompressPicturesUploaded_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
Settings.Canvas.IsCompressPicturesUploaded = ToggleSwitchCompressPicturesUploaded.IsOn;
SaveSettingsToFile();
}
private void ToggleSwitchAutoFoldAfterPPTSlideShow_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
Settings.Automation.IsAutoFoldAfterPPTSlideShow = ToggleSwitchAutoFoldAfterPPTSlideShow.IsOn;
SaveSettingsToFile();
}
private void ToggleSwitchAlwaysGoToFirstPageOnReenter_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
Settings.PowerPointSettings.IsAlwaysGoToFirstPageOnReenter = ToggleSwitchAlwaysGoToFirstPageOnReenter.IsOn;
SaveSettingsToFile();
}
private void ToggleSwitchAutoEnterAnnotationAfterKillHite_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
Settings.Automation.IsAutoEnterAnnotationAfterKillHite = ToggleSwitchAutoEnterAnnotationAfterKillHite.IsOn;
SaveSettingsToFile();
}
private void ToggleSwitchEnablePalmEraser_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
Settings.Canvas.EnablePalmEraser = ToggleSwitchEnablePalmEraser.IsOn;
SaveSettingsToFile();
}
}
}
+30 -6
View File
@@ -10,7 +10,6 @@ using System.Windows.Ink;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using File = System.IO.File;
using OperatingSystem = OSVersionExtension.OperatingSystem;
@@ -375,6 +374,8 @@ namespace Ink_Canvas {
ToggleSwitchAutoSaveScreenShotInPowerPoint.IsOn =
Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint;
ToggleSwitchEnableWppProcessKill.IsOn = Settings.PowerPointSettings.EnableWppProcessKill;
ToggleSwitchAlwaysGoToFirstPageOnReenter.IsOn = Settings.PowerPointSettings.IsAlwaysGoToFirstPageOnReenter;
} else {
Settings.PowerPointSettings = new PowerPointSettings();
}
@@ -515,13 +516,28 @@ namespace Ink_Canvas {
ToggleSwitchHideStrokeWhenSelecting.IsOn = Settings.Canvas.HideStrokeWhenSelecting;
if (Settings.Canvas.FitToCurve) {
ToggleSwitchFitToCurve.IsOn = true;
drawingAttributes.FitToCurve = true;
} else {
// 初始化贝塞尔曲线平滑设置
if (Settings.Canvas.UseAdvancedBezierSmoothing)
{
// 如果启用高级贝塞尔平滑,则禁用原来的FitToCurve
ToggleSwitchAdvancedBezierSmoothing.IsOn = true;
ToggleSwitchFitToCurve.IsOn = false;
drawingAttributes.FitToCurve = false;
}
else if (Settings.Canvas.FitToCurve)
{
// 如果启用原来的FitToCurve,则禁用高级贝塞尔平滑
ToggleSwitchFitToCurve.IsOn = true;
ToggleSwitchAdvancedBezierSmoothing.IsOn = false;
drawingAttributes.FitToCurve = true;
}
else
{
// 两者都禁用
ToggleSwitchFitToCurve.IsOn = false;
ToggleSwitchAdvancedBezierSmoothing.IsOn = false;
drawingAttributes.FitToCurve = false;
}
// 初始化直线自动拉直相关设置
ToggleSwitchAutoStraightenLine.IsOn = Settings.Canvas.AutoStraightenLine;
@@ -533,11 +549,16 @@ namespace Ink_Canvas {
// 初始化直线端点吸附相关设置
ToggleSwitchLineEndpointSnapping.IsOn = Settings.Canvas.LineEndpointSnapping;
LineEndpointSnappingThresholdSlider.Value = Settings.Canvas.LineEndpointSnappingThreshold;
ToggleSwitchCompressPicturesUploaded.IsOn = Settings.Canvas.IsCompressPicturesUploaded;
} else {
Settings.Canvas = new Canvas();
}
// Palm Eraser
if (Settings.Canvas != null) {
ToggleSwitchEnablePalmEraser.IsOn = Settings.Canvas.EnablePalmEraser;
}
// Advanced
if (Settings.Advanced != null) {
TouchMultiplierSlider.Value = Settings.Advanced.TouchMultiplier;
@@ -555,6 +576,7 @@ namespace Ink_Canvas {
ToggleSwitchIsEnableResolutionChangeDetection.IsOn = Settings.Advanced.IsEnableResolutionChangeDetection;
ToggleSwitchIsEnableDPIChangeDetection.IsOn = Settings.Advanced.IsEnableDPIChangeDetection;
ToggleSwitchIsEnableAvoidFullScreenHelper.IsOn = Settings.Advanced.IsEnableAvoidFullScreenHelper;
ToggleSwitchIsAutoBackupBeforeUpdate.IsOn = Settings.Advanced.IsAutoBackupBeforeUpdate;
if (Settings.Advanced.IsEnableFullScreenHelper) {
FullScreenHelper.MarkFullscreenWindowTaskbarList(new WindowInteropHelper(this).Handle, true);
}
@@ -667,6 +689,8 @@ namespace Ink_Canvas {
ToggleSwitchAutoFoldInPPTSlideShow.IsOn = Settings.Automation.IsAutoFoldInPPTSlideShow;
ToggleSwitchAutoFoldAfterPPTSlideShow.IsOn = Settings.Automation.IsAutoFoldAfterPPTSlideShow;
if (Settings.Automation.IsAutoKillEasiNote || Settings.Automation.IsAutoKillPptService ||
Settings.Automation.IsAutoKillHiteAnnotation || Settings.Automation.IsAutoKillInkCanvas
|| Settings.Automation.IsAutoKillICA || Settings.Automation.IsAutoKillIDT ||
+112 -206
View File
@@ -103,12 +103,22 @@ namespace Ink_Canvas {
}
private void BtnPen_Click(object sender, RoutedEventArgs e) {
// 禁用高级橡皮擦系统
DisableAdvancedEraserSystem();
// 如果当前已是批注模式,再次点击弹出批注子面板
if (penType == 0 && inkCanvas.EditingMode == InkCanvasEditingMode.Ink && !drawingAttributes.IsHighlighter) {
return;
}
// 否则只切换到批注模式,不弹出子面板
forceEraser = false;
forcePointEraser = false;
drawingShapeMode = 0;
penType = 0;
drawingAttributes.IsHighlighter = false;
drawingAttributes.StylusTip = StylusTip.Ellipse;
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
inkCanvas.IsManipulationEnabled = true;
CancelSingleFingerDragMode();
isLongPressSelected = false;
SetCursorBasedOnEditingMode(inkCanvas);
}
private Task<bool> CheckIsDrawingShapesInMultiTouchMode() {
@@ -131,351 +141,247 @@ namespace Ink_Canvas {
private async void BtnDrawLine_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
if (lastMouseDownSender == sender) {
forceEraser = true;
drawingShapeMode = 1;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
CancelSingleFingerDragMode();
}
else {
// 即使不是长按,也设置必要的绘图状态
forceEraser = true;
drawingShapeMode = 1;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
isLongPressSelected = true; // 设置为选中状态,避免抬笔后切换回笔模式
}
EnterShapeDrawingMode(1);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
if (isLongPressSelected) {
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
var dA = new DoubleAnimation(1, 1, new Duration(TimeSpan.FromMilliseconds(0)));
ImageDrawLine.BeginAnimation(OpacityProperty, dA);
}
DrawShapePromptToPen();
}
private async void BtnDrawDashedLine_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
if (lastMouseDownSender == sender) {
forceEraser = true;
drawingShapeMode = 8;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
CancelSingleFingerDragMode();
}
EnterShapeDrawingMode(8);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
if (isLongPressSelected) {
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
var dA = new DoubleAnimation(1, 1, new Duration(TimeSpan.FromMilliseconds(0)));
ImageDrawDashedLine.BeginAnimation(OpacityProperty, dA);
}
DrawShapePromptToPen();
}
private async void BtnDrawDotLine_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
if (lastMouseDownSender == sender) {
forceEraser = true;
drawingShapeMode = 18;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
CancelSingleFingerDragMode();
}
EnterShapeDrawingMode(18);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
if (isLongPressSelected) {
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
var dA = new DoubleAnimation(1, 1, new Duration(TimeSpan.FromMilliseconds(0)));
ImageDrawDotLine.BeginAnimation(OpacityProperty, dA);
}
DrawShapePromptToPen();
}
private async void BtnDrawArrow_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
if (lastMouseDownSender == sender) {
forceEraser = true;
drawingShapeMode = 2;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
CancelSingleFingerDragMode();
}
else {
// 即使不是长按,也设置必要的绘图状态
forceEraser = true;
drawingShapeMode = 2;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
isLongPressSelected = true; // 设置为选中状态,避免抬笔后切换回笔模式
}
EnterShapeDrawingMode(2);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
if (isLongPressSelected) {
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
var dA = new DoubleAnimation(1, 1, new Duration(TimeSpan.FromMilliseconds(0)));
ImageDrawArrow.BeginAnimation(OpacityProperty, dA);
}
DrawShapePromptToPen();
}
private async void BtnDrawParallelLine_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
if (lastMouseDownSender == sender) {
forceEraser = true;
drawingShapeMode = 15;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
CancelSingleFingerDragMode();
}
EnterShapeDrawingMode(15);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
if (isLongPressSelected) {
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
var dA = new DoubleAnimation(1, 1, new Duration(TimeSpan.FromMilliseconds(0)));
ImageDrawParallelLine.BeginAnimation(OpacityProperty, dA);
}
DrawShapePromptToPen();
}
private async void BtnDrawCoordinate1_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 11;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(11);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawCoordinate2_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 12;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(12);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawCoordinate3_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 13;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(13);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawCoordinate4_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 14;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(14);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawCoordinate5_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 17;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(17);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawRectangle_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 3;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
isLongPressSelected = true; // 设置为选中状态,避免抬笔后切换回笔模式
EnterShapeDrawingMode(3);
CancelSingleFingerDragMode();
isLongPressSelected = true; // 设置为选中状态,避免抬笔后切换回笔模式
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawRectangleCenter_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 19;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(19);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawEllipse_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 4;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(4);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawCircle_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 5;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(5);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawCenterEllipse_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 16;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(16);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawCenterEllipseWithFocalPoint_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 23;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(23);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawDashedCircle_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 10;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(10);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawHyperbola_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 24;
EnterShapeDrawingMode(24);
drawMultiStepShapeCurrentStep = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawHyperbolaWithFocalPoint_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 25;
EnterShapeDrawingMode(25);
drawMultiStepShapeCurrentStep = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawParabola1_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 20;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(20);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawParabolaWithFocalPoint_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 22;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(22);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawParabola2_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 21;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(21);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawCylinder_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 6;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(6);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawCone_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 7;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
EnterShapeDrawingMode(7);
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
private async void BtnDrawCuboid_Click(object sender, MouseButtonEventArgs e) {
await CheckIsDrawingShapesInMultiTouchMode();
forceEraser = true;
drawingShapeMode = 9;
EnterShapeDrawingMode(9);
isFirstTouchCuboid = true;
CuboidFrontRectIniP = new Point();
CuboidFrontRectEndP = new Point();
inkCanvas.EditingMode = InkCanvasEditingMode.None;
inkCanvas.IsManipulationEnabled = true;
CancelSingleFingerDragMode();
lastMouseDownSender = null;
DrawShapePromptToPen();
}
#endregion
private void inkCanvas_TouchMove(object sender, TouchEventArgs e) {
// 如果处于手掌擦状态,继续使用相同的橡皮形状
if (isLastTouchEraser && currentPalmEraserShape != null) {
inkCanvas.EraserShape = currentPalmEraserShape;
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
return;
// 确保套索选择模式下触摸移动时光标保持可见
if (inkCanvas.EditingMode == InkCanvasEditingMode.Select) {
SetCursorBasedOnEditingMode(inkCanvas);
}
if (isSingleFingerDragMode) return;
// 处理形状绘制模式
// 处理几何绘制模式
if (drawingShapeMode != 0) {
if (isLastTouchEraser) return;
//EraserContainer.Background = null;
//ImageEraser.Visibility = Visibility.Visible;
// 修复触屏状态下几何绘制功能不可用的问题
// 在几何绘制模式下,即使isWaitUntilNextTouchDown为true,也应该处理触摸移动事件
// 只有当多点触控时才需要等待下一次触摸
if (isWaitUntilNextTouchDown && dec.Count > 1) return;
if (dec.Count > 1) {
isWaitUntilNextTouchDown = true;
try {
inkCanvas.Strokes.Remove(lastTempStroke);
inkCanvas.Strokes.Remove(lastTempStrokeCollection);
@@ -485,19 +391,19 @@ namespace Ink_Canvas {
}
return;
}
// 在几何绘制模式下,确保处理单点触控的移动事件
Point touchPoint = e.GetTouchPoint(inkCanvas).Position;
MouseTouchMove(touchPoint);
return; // 处理完几何绘制后直接返回,不执行后面的代码
}
// 触摸移动时保持自定义光标显示
if (inkCanvas.EditingMode != InkCanvasEditingMode.None)
inkCanvas.EditingMode = InkCanvasEditingMode.None;
MouseTouchMove(e.GetTouchPoint(inkCanvas).Position);
// 其它模式下,允许橡皮、套索、批注等正常工作,不覆盖EditingMode
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint ||
inkCanvas.EditingMode == InkCanvasEditingMode.Select ||
inkCanvas.EditingMode == InkCanvasEditingMode.Ink) {
// 允许正常橡皮、套索、批注
return;
}
}
private int drawMultiStepShapeCurrentStep = 0; //多笔完成的图形 当前所处在的笔画
@@ -511,6 +417,7 @@ namespace Ink_Canvas {
#region
private void MouseTouchMove(Point endP) {
// 禁用原有的FitToCurve,使用新的高级贝塞尔曲线平滑
if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = false;
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
@@ -1317,42 +1224,6 @@ namespace Ink_Canvas {
private Point CuboidFrontRectIniP = new Point();
private Point CuboidFrontRectEndP = new Point();
private void Main_Grid_TouchUp(object sender, TouchEventArgs e) {
inkCanvas.ReleaseAllTouchCaptures();
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
// 在几何绘制模式下,确保正确处理触摸抬起事件
if (drawingShapeMode != 0) {
// 如果是几何绘制模式,确保将临时绘制的图形添加到永久图形中
if (lastTempStroke != null) {
// 将临时笔画添加到历史记录中
var strokes = new StrokeCollection();
strokes.Add(lastTempStroke);
timeMachine.CommitStrokeUserInputHistory(strokes);
// 清除临时笔画引用,以便下次绘制
lastTempStroke = null;
}
if (lastTempStrokeCollection != null && lastTempStrokeCollection.Count > 0) {
// 将临时笔画集合添加到历史记录中
timeMachine.CommitStrokeUserInputHistory(lastTempStrokeCollection);
// 清除临时笔画集合引用,以便下次绘制
lastTempStrokeCollection = new StrokeCollection();
}
// 如果不是长按选中的状态,则需要在抬起手指后重置isWaitUntilNextTouchDown
if (!isLongPressSelected && dec.Count == 0) {
isWaitUntilNextTouchDown = false;
}
}
inkCanvas_MouseUp(sender, null);
// 修改此处逻辑,在长按选择图形模式下保持isWaitUntilNextTouchDown
if (dec.Count == 0 && !isLongPressSelected) isWaitUntilNextTouchDown = false;
}
private Stroke lastTempStroke = null;
private StrokeCollection lastTempStrokeCollection = new StrokeCollection();
@@ -1703,7 +1574,33 @@ namespace Ink_Canvas {
}
}
if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = true;
// 应用高级贝塞尔曲线平滑
if (Settings.Canvas.UseAdvancedBezierSmoothing)
{
try
{
var advancedSmoothing = new Helpers.AdvancedBezierSmoothing
{
};
// 对临时笔画应用平滑
if (lastTempStroke != null)
{
var smoothedStroke = advancedSmoothing.SmoothStroke(lastTempStroke);
inkCanvas.Strokes.Remove(lastTempStroke);
lastTempStroke = smoothedStroke;
inkCanvas.Strokes.Add(smoothedStroke);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"形状绘制高级贝塞尔曲线平滑失败: {ex.Message}");
}
}
else if (Settings.Canvas.FitToCurve == true)
{
drawingAttributes.FitToCurve = true;
}
}
private bool NeedUpdateIniP() {
@@ -1735,5 +1632,14 @@ namespace Ink_Canvas {
}
}
}
// 在MainWindow类中添加:
private void EnterShapeDrawingMode(int mode) {
forceEraser = true;
forcePointEraser = false;
drawingShapeMode = mode;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
SetCursorBasedOnEditingMode(inkCanvas);
}
}
}
@@ -16,6 +16,10 @@ namespace Ink_Canvas {
private const double LINE_STRAIGHTEN_THRESHOLD = 0.20; // 默认灵敏度阈值,与UI默认值对应
private void inkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e) {
// 标记是否进行了直线拉直
bool wasStraightened = false;
// 禁用原有的FitToCurve,使用新的高级贝塞尔曲线平滑
if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = false;
try {
@@ -117,8 +121,9 @@ namespace Ink_Canvas {
}
}
// Apply line straightening and endpoint snapping if ink-to-shape is enabled
if (Settings.InkToShape.IsInkToShapeEnabled) {
// Apply line straightening and endpoint snapping if ink-to-shape is enabled
if (Settings.InkToShape.IsInkToShapeEnabled) {
// 检查是否启用了直线自动拉直功能
if (Settings.Canvas.AutoStraightenLine && IsPotentialStraightLine(e.Stroke)) {
// Get start and end points of the stroke
@@ -168,6 +173,8 @@ namespace Ink_Canvas {
newStrokes.Remove(e.Stroke);
newStrokes.Add(straightStroke);
}
wasStraightened = true; // 标记已进行直线拉直
}
}
}
@@ -572,7 +579,38 @@ namespace Ink_Canvas {
}
catch { }
if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = true;
// 应用高级贝塞尔曲线平滑(仅在未进行直线拉直时)
if (Settings.Canvas.UseAdvancedBezierSmoothing && !wasStraightened)
{
try
{
// 检查原始笔画是否仍然存在于画布中
if (inkCanvas.Strokes.Contains(e.Stroke))
{
var advancedSmoothing = new Helpers.AdvancedBezierSmoothing
{
};
var smoothedStroke = advancedSmoothing.SmoothStroke(e.Stroke);
// 替换原始笔画
SetNewBackupOfStroke();
_currentCommitType = CommitReason.ShapeRecognition;
inkCanvas.Strokes.Remove(e.Stroke);
inkCanvas.Strokes.Add(smoothedStroke);
_currentCommitType = CommitReason.UserInput;
}
}
catch (Exception ex)
{
// 如果高级平滑失败,回退到原始笔画
System.Diagnostics.Debug.WriteLine($"高级贝塞尔曲线平滑失败: {ex.Message}");
}
}
else if (Settings.Canvas.FitToCurve == true && !wasStraightened)
{
drawingAttributes.FitToCurve = true;
}
}
// New method: Checks if a stroke is potentially a straight line
@@ -584,9 +622,10 @@ namespace Ink_Canvas {
Point start = stroke.StylusPoints.First().ToPoint();
Point end = stroke.StylusPoints.Last().ToPoint();
double lineLength = GetDistance(start, end);
// 线条必须足够长才考虑拉直,使用设置中的阈值
if (lineLength < Settings.Canvas.AutoStraightenLineThreshold)
// 分辨率自适应阈值
double adaptiveThreshold = Settings.Canvas.AutoStraightenLineThreshold * GetResolutionScale();
// 线条必须足够长才考虑拉直,使用自适应阈值
if (lineLength < adaptiveThreshold)
return false;
// 获取用户设置的灵敏度值,确保使用正确的设置
@@ -653,16 +692,14 @@ namespace Ink_Canvas {
// New method: Determines if a stroke should be straightened into a line
private bool ShouldStraightenLine(Stroke stroke) {
// Basic implementation: check if points roughly follow a straight line
Point start = stroke.StylusPoints.First().ToPoint();
Point end = stroke.StylusPoints.Last().ToPoint();
// Calculate max deviation from the straight line between start and end
double maxDeviation = 0;
double lineLength = GetDistance(start, end);
// 如果线条太短,不进行拉直处理,使用设置中的阈值
if (lineLength < Settings.Canvas.AutoStraightenLineThreshold) {
// 分辨率自适应阈值
double adaptiveThreshold = Settings.Canvas.AutoStraightenLineThreshold * GetResolutionScale();
// 如果线条太短,不进行拉直处理,使用自适应阈值
if (lineLength < adaptiveThreshold) {
// 显示调试信息 - 线条长度不足
// MessageBox.Show($"线条太短: {lineLength} < {Settings.Canvas.AutoStraightenLineThreshold}", "调试信息");
return false;
@@ -1134,5 +1171,19 @@ namespace Ink_Canvas {
public StylusPoint GetCenterPoint(StylusPoint point1, StylusPoint point2) {
return new StylusPoint((point1.X + point2.X) / 2, (point1.Y + point2.Y) / 2);
}
// 分辨率自适应:以1080P为基准,返回当前分辨率下的阈值倍数
private double GetResolutionScale()
{
// 以1920x1080为基准
double baseWidth = 1920.0;
double baseHeight = 1080.0;
double screenWidth = SystemParameters.PrimaryScreenWidth;
double screenHeight = SystemParameters.PrimaryScreenHeight;
// 取宽高平均缩放,防止极端比例
double scaleW = screenWidth / baseWidth;
double scaleH = screenHeight / baseHeight;
return (scaleW + scaleH) / 2.0;
}
}
}
+10 -1
View File
@@ -7,7 +7,6 @@ using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Diagnostics;
using System.Windows.Media.Imaging;
namespace Ink_Canvas {
public partial class MainWindow : Window {
@@ -130,6 +129,16 @@ namespace Ink_Canvas {
if (canvas.Strokes.Contains(currentStroke))
canvas.Strokes.Remove(currentStroke);
}
} else if (item.CommitType == TimeMachineHistoryType.ElementInsert) {
if (!item.StrokeHasBeenCleared) {
// Undo: 移除元素
if (item.InsertedElement != null && inkCanvas.Children.Contains(item.InsertedElement))
inkCanvas.Children.Remove(item.InsertedElement);
} else {
// Redo: 添加元素
if (item.InsertedElement != null && !inkCanvas.Children.Contains(item.InsertedElement))
inkCanvas.Children.Add(item.InsertedElement);
}
}
_currentCommitType = CommitReason.UserInput;
+77 -9
View File
@@ -5,11 +5,13 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Timers;
using System.Windows;
using MessageBox = System.Windows.MessageBox;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Net;
using System.Net.Sockets;
using System.Linq;
namespace Ink_Canvas {
public class TimeViewModel : INotifyPropertyChanged {
@@ -56,6 +58,35 @@ namespace Ink_Canvas {
private TimeViewModel nowTimeVM = new TimeViewModel();
private async Task<DateTime> GetNetworkTimeAsync()
{
try
{
const string ntpServer = "ntp.ntsc.ac.cn";
var ntpData = new byte[48];
ntpData[0] = 0x1B;
var addresses = await Dns.GetHostAddressesAsync(ntpServer);
var ipEndPoint = new IPEndPoint(addresses[0], 123);
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
socket.ReceiveTimeout = 2000;
socket.Connect(ipEndPoint);
await Task.Factory.FromAsync(socket.BeginSend(ntpData, 0, ntpData.Length, SocketFlags.None, null, socket), socket.EndSend);
await Task.Factory.FromAsync(socket.BeginReceive(ntpData, 0, ntpData.Length, SocketFlags.None, null, socket), socket.EndReceive);
}
const byte serverReplyTime = 40;
ulong intPart = BitConverter.ToUInt32(ntpData.Skip(serverReplyTime).Take(4).Reverse().ToArray(), 0);
ulong fractPart = BitConverter.ToUInt32(ntpData.Skip(serverReplyTime + 4).Take(4).Reverse().ToArray(), 0);
var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
var networkDateTime = (new DateTime(1900, 1, 1)).AddMilliseconds((long)milliseconds);
return networkDateTime.ToLocalTime();
}
catch
{
return DateTime.Now;
}
}
private void InitTimers() {
timerCheckPPT.Elapsed += TimerCheckPPT_Elapsed;
timerCheckPPT.Interval = 500;
@@ -67,23 +98,29 @@ namespace Ink_Canvas {
timerCheckAutoUpdateWithSilence.Interval = 1000 * 60 * 10;
WaterMarkTime.DataContext = nowTimeVM;
WaterMarkDate.DataContext = nowTimeVM;
timerDisplayTime.Elapsed += TimerDisplayTime_Elapsed;
timerDisplayTime.Elapsed += async (s, e) => await TimerDisplayTime_ElapsedAsync();
timerDisplayTime.Interval = 1000;
timerDisplayTime.Start();
timerDisplayDate.Elapsed += TimerDisplayDate_Elapsed;
timerDisplayDate.Interval = 1000 * 60 * 60 * 1;
timerDisplayDate.Start();
timerKillProcess.Start();
nowTimeVM.nowDate = DateTime.Now.ToShortDateString().ToString();
nowTimeVM.nowTime = DateTime.Now.ToShortTimeString().ToString();
nowTimeVM.nowDate = DateTime.Now.ToString("yyyy/M/d");
nowTimeVM.nowTime = DateTime.Now.ToString("HH:mm");
}
private void TimerDisplayTime_Elapsed(object sender, ElapsedEventArgs e) {
nowTimeVM.nowTime = DateTime.Now.ToShortTimeString().ToString();
private async Task TimerDisplayTime_ElapsedAsync()
{
DateTime now = await GetNetworkTimeAsync();
// 只更新时间,日期由原有逻辑定时更新即可
Dispatcher.Invoke(() =>
{
nowTimeVM.nowTime = now.ToString("HH:mm");
});
}
private void TimerDisplayDate_Elapsed(object sender, ElapsedEventArgs e) {
nowTimeVM.nowDate = DateTime.Now.ToShortDateString().ToString();
nowTimeVM.nowDate = DateTime.Now.ToString("yyyy/M/d");
}
private void TimerKillProcess_Elapsed(object sender, ElapsedEventArgs e) {
@@ -151,6 +188,10 @@ namespace Ink_Canvas {
if (arg.Contains("HiteAnnotation")) {
Dispatcher.Invoke(() => {
ShowNotification("“鸿合屏幕书写”已自动关闭");
if (Settings.Automation.IsAutoKillHiteAnnotation && Settings.Automation.IsAutoEnterAnnotationAfterKillHite) {
// 自动进入批注状态
PenIcon_Click(null, null);
}
});
}
@@ -329,9 +370,36 @@ namespace Ink_Canvas {
if (!File.Exists(statusFilePath) || File.ReadAllText(statusFilePath).Trim().ToLower() != "true") {
LogHelper.WriteLogToFile("AutoUpdate | Update file not downloaded yet");
// 尝试下载更新文件
// 尝试下载更新文件,使用多线路组下载功能
Task.Run(async () => {
bool isDownloadSuccessful = await AutoUpdateHelper.DownloadSetupFileAndSaveStatus(AvailableLatestVersion);
bool isDownloadSuccessful = false;
try
{
// 如果主要线路组可用,直接使用
if (AvailableLatestLineGroup != null)
{
LogHelper.WriteLogToFile($"AutoUpdate | 使用主要线路组下载: {AvailableLatestLineGroup.GroupName}");
isDownloadSuccessful = await AutoUpdateHelper.DownloadSetupFile(AvailableLatestVersion, AvailableLatestLineGroup);
}
// 如果主要线路组不可用或下载失败,获取所有可用线路组
if (!isDownloadSuccessful)
{
LogHelper.WriteLogToFile("AutoUpdate | 主要线路组不可用或下载失败,获取所有可用线路组");
var availableGroups = await AutoUpdateHelper.GetAvailableLineGroupsOrdered(Settings.Startup.UpdateChannel);
if (availableGroups.Count > 0)
{
LogHelper.WriteLogToFile($"AutoUpdate | 使用 {availableGroups.Count} 个可用线路组进行下载");
isDownloadSuccessful = await AutoUpdateHelper.DownloadSetupFileWithFallback(AvailableLatestVersion, availableGroups);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | 下载更新时出错: {ex.Message}", LogHelper.LogType.Error);
}
if (isDownloadSuccessful) {
LogHelper.WriteLogToFile("AutoUpdate | Update downloaded successfully, will check again for installation");
// 重新启动计时器,下次检查时安装
+151 -179
View File
@@ -9,12 +9,17 @@ using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using Point = System.Windows.Point;
using System.Diagnostics;
namespace Ink_Canvas {
public partial class MainWindow : Window {
#region Multi-Touch
private bool isInMultiTouchMode = false;
private List<int> dec = new List<int>();
private bool isSingleFingerDragMode = false;
private Point centerPoint = new Point(0, 0);
private InkCanvasEditingMode lastInkCanvasEditingMode = InkCanvasEditingMode.Ink;
private void BorderMultiTouchMode_MouseUp(object sender, MouseButtonEventArgs e) {
if (isInMultiTouchMode) {
@@ -23,27 +28,23 @@ namespace Ink_Canvas {
inkCanvas.StylusUp -= MainWindow_StylusUp;
inkCanvas.TouchDown -= MainWindow_TouchDown;
inkCanvas.TouchDown += Main_Grid_TouchDown;
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
inkCanvas.Children.Clear();
isInMultiTouchMode = false;
// 退出多指书写模式后,恢复手掌擦功能
// 这里不需要特别操作,因为设置了isInMultiTouchMode = false后,
// 下次触发Main_Grid_TouchDown时会自动判断并启用手掌擦功能
}
else {
// 进入多指书写模式前,如果当前处于手掌擦状态,先关闭手掌擦
if (isLastTouchEraser) {
isLastTouchEraser = false;
currentPalmEraserShape = null;
}
inkCanvas.StylusDown += MainWindow_StylusDown;
inkCanvas.StylusMove += MainWindow_StylusMove;
inkCanvas.StylusUp += MainWindow_StylusUp;
inkCanvas.TouchDown += MainWindow_TouchDown;
inkCanvas.TouchDown -= Main_Grid_TouchDown;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
inkCanvas.Children.Clear();
isInMultiTouchMode = true;
}
@@ -59,28 +60,16 @@ namespace Ink_Canvas {
HideSubPanels(); // 书写时自动隐藏二级菜单
}
double boundWidth = e.GetTouchPoint(null).Bounds.Width, eraserMultiplier = 1.0;
if (!Settings.Advanced.EraserBindTouchMultiplier && Settings.Advanced.IsSpecialScreen)
eraserMultiplier = 1 / Settings.Advanced.TouchMultiplier;
if ((Settings.Advanced.TouchMultiplier != 0 || !Settings.Advanced.IsSpecialScreen) //启用特殊屏幕且触摸倍数为 0 时禁用橡皮
&& boundWidth > BoundsWidth * 2.5) {
if (drawingShapeMode == 0 && forceEraser) return;
currentPalmEraserShape = GetPalmRectangleEraserShape(eraserMultiplier);
inkCanvas.EraserShape = currentPalmEraserShape;
TouchDownPointsList[e.TouchDevice.Id] = InkCanvasEditingMode.EraseByPoint;
isLastTouchEraser = true;
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
}
else {
TouchDownPointsList[e.TouchDevice.Id] = InkCanvasEditingMode.None;
// 修复面积擦时不显示橡皮形状:无论 forcePointEraser 状态,均显示 50x50 橡皮
inkCanvas.EraserShape = new EllipseStylusShape(50, 50);
// 只保留普通橡皮逻辑
TouchDownPointsList[e.TouchDevice.Id] = InkCanvasEditingMode.None;
inkCanvas.EraserShape = new EllipseStylusShape(50, 50);
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
}
private void MainWindow_StylusDown(object sender, StylusDownEventArgs e) {
SetCursorBasedOnEditingMode(inkCanvas);
inkCanvas.CaptureStylus();
ViewboxFloatingBar.IsHitTestVisible = false;
@@ -199,153 +188,78 @@ namespace Ink_Canvas {
private int lastTouchDownTime = 0, lastTouchUpTime = 0;
private Point iniP = new Point(0, 0);
private bool isLastTouchEraser = false;
private bool forcePointEraser = true;
// 用于记录手掌擦的尺寸和形状
private StylusShape currentPalmEraserShape = null;
/// <summary>
/// 根据用户在设置面板中选择的橡皮大小,生成"手掌橡皮"默认的矩形黑板擦形状。
/// 该形状大小不随触控面积等实时变化,仅受设置的橡皮大小影响。
/// </summary>
/// <param name="multiplier">特殊屏幕触摸倍数修正系数</param>
/// <returns>RectangleStylusShape</returns>
private StylusShape GetPalmRectangleEraserShape(double multiplier = 1.0) {
double k = 1;
switch (Settings.Canvas.EraserSize) {
case 0:
k = 0.5;
break;
case 1:
k = 0.8;
break;
case 3:
k = 1.25;
break;
case 4:
k = 1.5;
break;
}
// 参照圆形橡皮 k*90 的基准,将矩形宽度压缩到 0.6,保持高度一致
double baseLen = k * 90 * multiplier;
return new RectangleStylusShape(baseLen * 0.6, baseLen);
}
private void Main_Grid_TouchDown(object sender, TouchEventArgs e) {
// 确保触摸时显示自定义光标
if (Settings.Canvas.IsShowCursor) {
inkCanvas.ForceCursor = true;
System.Windows.Forms.Cursor.Show();
}
SetCursorBasedOnEditingMode(inkCanvas);
inkCanvas.CaptureTouch(e.TouchDevice);
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
if (!isHidingSubPanelsWhenInking) {
isHidingSubPanelsWhenInking = true;
HideSubPanels(); // 书写时自动隐藏二级菜单
}
if (NeedUpdateIniP()) iniP = e.GetTouchPoint(inkCanvas).Position;
if (drawingShapeMode == 9 && isFirstTouchCuboid == false) MouseTouchMove(iniP);
inkCanvas.Opacity = 1;
// 如果已处于多指书写模式,禁用手掌擦功能
if (isInMultiTouchMode) {
isLastTouchEraser = false;
currentPalmEraserShape = null;
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
// 橡皮状态下只return,保证橡皮状态可保持
return;
}
// 如果已经处于手掌擦状态,保持状态不变
if (isLastTouchEraser && currentPalmEraserShape != null) {
inkCanvas.EraserShape = currentPalmEraserShape;
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
if (inkCanvas.EditingMode == InkCanvasEditingMode.Select) {
// 套索选状态下只return,保证套索选可用
return;
}
double boundsWidth = GetTouchBoundWidth(e), eraserMultiplier = 1.0;
if (!Settings.Advanced.EraserBindTouchMultiplier && Settings.Advanced.IsSpecialScreen)
eraserMultiplier = 1 / Settings.Advanced.TouchMultiplier;
// 检查触控点数量,只有大于等于三个触控点时才激活手掌擦功能
if (dec.Count >= 3 && boundsWidth > BoundsWidth) {
// 保存当前的编辑模式,以便恢复
if (!isLastTouchEraser) {
prePalmEraserEditingMode = inkCanvas.EditingMode;
// 模拟点击橡皮选项卡
EraserIcon_Click(null, null);
}
isLastTouchEraser = true;
if (drawingShapeMode == 0 && forceEraser) return;
if (boundsWidth > BoundsWidth * 2.5) {
// 直接使用固定尺寸的矩形黑板擦形状,不随触控面积动态变化
currentPalmEraserShape = GetPalmRectangleEraserShape(eraserMultiplier);
inkCanvas.EraserShape = currentPalmEraserShape;
TouchDownPointsList[e.TouchDevice.Id] = InkCanvasEditingMode.EraseByPoint;
}
else {
if (StackPanelPPTControls.Visibility == Visibility.Visible && inkCanvas.Strokes.Count == 0 &&
Settings.PowerPointSettings.IsEnableFingerGestureSlideShowControl) {
isLastTouchEraser = false;
currentPalmEraserShape = null;
inkCanvas.EditingMode = InkCanvasEditingMode.GestureOnly;
inkCanvas.Opacity = 0.1;
}
else {
// 手掌橡皮固定为矩形黑板擦,大小由设置决定
currentPalmEraserShape = GetPalmRectangleEraserShape(eraserMultiplier);
inkCanvas.EraserShape = currentPalmEraserShape;
TouchDownPointsList[e.TouchDevice.Id] = InkCanvasEditingMode.EraseByPoint;
}
if (drawingShapeMode == 9) {
if (isFirstTouchCuboid) {
CuboidFrontRectIniP = e.GetTouchPoint(inkCanvas).Position;
}
// 允许MouseTouchMove在TouchMove时处理
return;
}
else {
isLastTouchEraser = false;
currentPalmEraserShape = null;
// 修复面积擦时不显示橡皮形状:无论 forcePointEraser 状态,均显示 50x50 橡皮
inkCanvas.EraserShape = new EllipseStylusShape(50, 50);
// 修复触屏状态下几何绘制功能不可用的问题:在几何绘制模式下不应该因为forceEraser而直接返回
if (forceEraser && drawingShapeMode == 0) return;
if (drawingShapeMode != 0) {
return;
}
if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink) {
return;
}
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
}
private double GetTouchBoundWidth(TouchEventArgs e) {
var args = e.GetTouchPoint(null).Bounds;
double value;
if (!Settings.Advanced.IsQuadIR) value = args.Width;
else value = Math.Sqrt(args.Width * args.Height); //四边红外
if (Settings.Advanced.IsSpecialScreen) value *= Settings.Advanced.TouchMultiplier;
return value;
}
// 手掌擦相关变量
private bool isPalmEraserActive = false;
private InkCanvasEditingMode palmEraserLastEditingMode = InkCanvasEditingMode.Ink;
private bool palmEraserLastIsHighlighter = false;
private bool palmEraserWasEnabledBeforeMultiTouch = false;
//记录触摸设备ID
private List<int> dec = new List<int>();
//中心点
private Point centerPoint;
private InkCanvasEditingMode lastInkCanvasEditingMode = InkCanvasEditingMode.Ink;
private bool isSingleFingerDragMode = false;
// 保存触发手掌擦前的编辑模式,用于手掌擦结束后恢复
private InkCanvasEditingMode prePalmEraserEditingMode = InkCanvasEditingMode.Ink;
private void inkCanvas_PreviewTouchDown(object sender, TouchEventArgs e) {
// 橡皮状态下不做任何切换,直接return,保证橡皮可持续
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
return;
}
SetCursorBasedOnEditingMode(inkCanvas);
inkCanvas.CaptureTouch(e.TouchDevice);
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
dec.Add(e.TouchDevice.Id);
// Palm Eraser 逻辑
if (Settings.Canvas.EnablePalmEraser && dec.Count >= 2 && !isPalmEraserActive) {
var bounds = e.GetTouchPoint(inkCanvas).Bounds;
double palmThreshold = 40; // 触摸面积阈值,可根据实际调整
if (bounds.Width >= palmThreshold || bounds.Height >= palmThreshold) {
// 记录当前编辑模式和高光状态
palmEraserLastEditingMode = inkCanvas.EditingMode;
palmEraserLastIsHighlighter = drawingAttributes.IsHighlighter;
// 切换为橡皮擦
EraserIcon_Click(null, null);
isPalmEraserActive = true;
}
}
//设备1个的时候,记录中心点
if (dec.Count == 1) {
var touchPoint = e.GetTouchPoint(inkCanvas);
centerPoint = touchPoint.Position;
// 新增:几何绘制模式下,记录初始点
if (drawingShapeMode != 0) {
iniP = touchPoint.Position;
}
//记录第一根手指点击时的 StrokeCollection
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
}
@@ -355,47 +269,55 @@ namespace Ink_Canvas {
if (inkCanvas.EditingMode == InkCanvasEditingMode.None ||
inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
lastInkCanvasEditingMode = inkCanvas.EditingMode;
inkCanvas.EditingMode = InkCanvasEditingMode.None;
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
}
}
private void inkCanvas_PreviewTouchUp(object sender, TouchEventArgs e) {
// 橡皮状态下不做任何切换,直接return,保证橡皮可持续
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint && !isPalmEraserActive) {
return;
}
inkCanvas.ReleaseAllTouchCaptures();
ViewboxFloatingBar.IsHitTestVisible = true;
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
// Palm Eraser 逻辑:所有点抬起后恢复原编辑模式
dec.Remove(e.TouchDevice.Id);
if (isPalmEraserActive && dec.Count == 0) {
// 恢复高光状态
drawingAttributes.IsHighlighter = palmEraserLastIsHighlighter;
// 恢复编辑模式
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
if (palmEraserLastEditingMode == InkCanvasEditingMode.Ink) {
PenIcon_Click(null, null);
} else if (palmEraserLastEditingMode == InkCanvasEditingMode.Select) {
SymbolIconSelect_MouseUp(null, null);
} else {
inkCanvas.EditingMode = palmEraserLastEditingMode;
}
}
isPalmEraserActive = false;
}
// 新增:几何绘制模式下,触摸抬手时自动落笔
if (drawingShapeMode != 0) {
var mouseArgs = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left)
{
RoutedEvent = UIElement.MouseLeftButtonUpEvent,
Source = inkCanvas
};
inkCanvas_MouseUp(inkCanvas, mouseArgs);
}
//手势完成后切回之前的状态
if (dec.Count > 1)
if (inkCanvas.EditingMode == InkCanvasEditingMode.None)
inkCanvas.EditingMode = lastInkCanvasEditingMode;
dec.Remove(e.TouchDevice.Id);
inkCanvas.Opacity = 1;
// 如果是手掌触发的面积擦抬起,需要确保橡皮擦形状被正确重置
if (isLastTouchEraser && dec.Count == 0) {
isLastTouchEraser = false;
currentPalmEraserShape = null; // 清除保存的手掌擦形状
// 当手掌擦消失时,恢复到之前的编辑模式
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
// 根据之前的编辑模式模拟点击相应的选项卡
if (prePalmEraserEditingMode == InkCanvasEditingMode.Ink) {
// 模拟点击批注选项卡
PenIcon_Click(null, null);
} else if (prePalmEraserEditingMode == InkCanvasEditingMode.None ||
prePalmEraserEditingMode == InkCanvasEditingMode.Select) {
// 模拟点击光标选项卡
CursorIcon_Click(null, null);
} else {
// 其他编辑模式时恢复之前的模式
inkCanvas.EditingMode = prePalmEraserEditingMode;
if (forcePointEraser) {
// 重新应用当前设置的橡皮擦形状
ApplyCurrentEraserShape();
}
if (lastInkCanvasEditingMode != InkCanvasEditingMode.EraseByPoint) {
inkCanvas.EditingMode = lastInkCanvasEditingMode;
}
}
}
inkCanvas.Opacity = 1;
if (dec.Count == 0)
if (lastTouchDownStrokeCollection.Count() != inkCanvas.Strokes.Count() &&
@@ -414,13 +336,14 @@ namespace Ink_Canvas {
private void Main_Grid_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) {
if (e.Manipulators.Count() != 0) return;
if (forceEraser) return;
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
if (drawingShapeMode == 0 && inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
}
private void Main_Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) {
// 手掌擦时禁止移动/缩放
if (isLastTouchEraser || inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
return;
// 三指及以上禁止缩放
bool disableScale = dec.Count >= 3;
@@ -508,5 +431,54 @@ namespace Ink_Canvas {
}
}
}
// 退出多指书写模式,恢复InkCanvas的TouchDown事件绑定
private void ExitMultiTouchModeIfNeeded()
{
if (isInMultiTouchMode)
{
inkCanvas.StylusDown -= MainWindow_StylusDown;
inkCanvas.StylusMove -= MainWindow_StylusMove;
inkCanvas.StylusUp -= MainWindow_StylusUp;
inkCanvas.TouchDown -= MainWindow_TouchDown;
inkCanvas.TouchDown += Main_Grid_TouchDown;
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint)
{
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
inkCanvas.Children.Clear();
isInMultiTouchMode = false;
// 关闭多指书写时,恢复手掌擦开关
if (palmEraserWasEnabledBeforeMultiTouch) {
Settings.Canvas.EnablePalmEraser = true;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = true;
}
}
}
// 进入多指书写模式,绑定Main_Grid_TouchDown
private void EnterMultiTouchModeIfNeeded()
{
if (!isInMultiTouchMode)
{
inkCanvas.StylusDown += MainWindow_StylusDown;
inkCanvas.StylusMove += MainWindow_StylusMove;
inkCanvas.StylusUp += MainWindow_StylusUp;
inkCanvas.TouchDown += MainWindow_TouchDown;
inkCanvas.TouchDown -= Main_Grid_TouchDown;
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint)
{
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
inkCanvas.Children.Clear();
isInMultiTouchMode = true;
// 启用多指书写时,自动禁用手掌擦
palmEraserWasEnabledBeforeMultiTouch = Settings.Canvas.EnablePalmEraser;
Settings.Canvas.EnablePalmEraser = false;
if (ToggleSwitchEnablePalmEraser != null)
ToggleSwitchEnablePalmEraser.IsOn = false;
}
}
}
}
-7
View File
@@ -1,17 +1,10 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.VisualStyles;
using System.Windows.Interop;
using System.Windows.Media.Animation;
using Hardcodet.Wpf.TaskbarNotification;
namespace Ink_Canvas
+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.1.0")]
[assembly: AssemblyFileVersion("1.7.1.0")]
[assembly: AssemblyVersion("1.7.2.0")]
[assembly: AssemblyFileVersion("1.7.2.0")]
+1 -7
View File
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ink_Canvas
namespace Ink_Canvas
{
public static class ChickenSoup {
public static string[] OSUPlayerYuLu = new string[] {
+25 -3
View File
@@ -47,7 +47,9 @@ namespace Ink_Canvas
[JsonProperty("hideStrokeWhenSelecting")]
public bool HideStrokeWhenSelecting { get; set; } = true;
[JsonProperty("fitToCurve")]
public bool FitToCurve { get; set; } = true;
public bool FitToCurve { get; set; } = false; // 默认关闭原来的贝塞尔平滑
[JsonProperty("useAdvancedBezierSmoothing")]
public bool UseAdvancedBezierSmoothing { get; set; } = true; // 默认启用高级贝塞尔曲线平滑
[JsonProperty("clearCanvasAndClearTimeMachine")]
public bool ClearCanvasAndClearTimeMachine { get; set; } = false;
[JsonProperty("enablePressureTouchMode")]
@@ -57,7 +59,7 @@ namespace Ink_Canvas
[JsonProperty("autoStraightenLine")]
public bool AutoStraightenLine { get; set; } = true; // 是否启用直线自动拉直
[JsonProperty("autoStraightenLineThreshold")]
public int AutoStraightenLineThreshold { get; set; } = 30; // 直线自动拉直的长度阈值(像素)
public int AutoStraightenLineThreshold { get; set; } = 80; // 直线自动拉直的长度阈值(像素)
[JsonProperty("highPrecisionLineStraighten")]
public bool HighPrecisionLineStraighten { get; set; } = true; // 是否启用高精度直线拉直
[JsonProperty("lineEndpointSnapping")]
@@ -73,6 +75,10 @@ namespace Ink_Canvas
[JsonProperty("hyperbolaAsymptoteOption")]
public OptionalOperation HyperbolaAsymptoteOption { get; set; } = OptionalOperation.Ask;
[JsonProperty("isCompressPicturesUploaded")]
public bool IsCompressPicturesUploaded { get; set; } = false;
[JsonProperty("enablePalmEraser")]
public bool EnablePalmEraser { get; set; } = true;
}
public enum OptionalOperation
@@ -232,7 +238,11 @@ namespace Ink_Canvas
[JsonProperty("isEnableFingerGestureSlideShowControl")]
public bool IsEnableFingerGestureSlideShowControl { get; set; } = true;
[JsonProperty("isSupportWPS")]
public bool IsSupportWPS { get; set; } = true;
public bool IsSupportWPS { get; set; } = false;
[JsonProperty("enableWppProcessKill")]
public bool EnableWppProcessKill { get; set; } = true;
[JsonProperty("isAlwaysGoToFirstPageOnReenter")]
public bool IsAlwaysGoToFirstPageOnReenter { get; set; } = false;
}
public class Automation
@@ -317,6 +327,9 @@ namespace Ink_Canvas
[JsonProperty("isAutoFoldInPPTSlideShow")]
public bool IsAutoFoldInPPTSlideShow { get; set; } = false;
[JsonProperty("isAutoFoldAfterPPTSlideShow")]
public bool IsAutoFoldAfterPPTSlideShow { get; set; } = false;
[JsonProperty("isAutoKillPptService")]
public bool IsAutoKillPptService { get; set; } = false;
@@ -367,6 +380,9 @@ namespace Ink_Canvas
[JsonProperty("isSaveFullPageStrokes")]
public bool IsSaveFullPageStrokes = false;
[JsonProperty("isAutoEnterAnnotationAfterKillHite")]
public bool IsAutoEnterAnnotationAfterKillHite { get; set; } = false;
}
public class Advanced
@@ -418,6 +434,12 @@ namespace Ink_Canvas
[JsonProperty("isEnableAvoidFullScreenHelper")]
public bool IsEnableAvoidFullScreenHelper { get; set; } = false;
[JsonProperty("isAutoBackupBeforeUpdate")]
public bool IsAutoBackupBeforeUpdate { get; set; } = true;
[JsonProperty("isNoFocusMode")]
public bool IsNoFocusMode { get; set; } = true;
}
public class InkToShape
@@ -1,7 +1,6 @@
using Ink_Canvas.Helpers;
using System;
using System.Media;
using System.Runtime.InteropServices;
using System.Timers;
using System.Windows;
using System.Windows.Input;
@@ -1,5 +1,3 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
@@ -78,6 +78,12 @@
Background="#f8fafc" BorderBrush="#cbd5e1" ToolTip="跳过此版本更新" Visibility="Visible" IsEnabled="True"/>
</ui:SimpleStackPanel>
</Border>
<!-- 下载进度条和状态 -->
<StackPanel x:Name="DownloadProgressPanel" Orientation="Vertical" HorizontalAlignment="Center" Margin="0,10,0,0" Visibility="Collapsed">
<ProgressBar x:Name="DownloadProgressBar" Width="360" Height="18" Minimum="0" Maximum="100" Value="0"/>
<TextBlock x:Name="DownloadProgressText" Text="正在下载..." FontSize="14" Foreground="#2563eb" HorizontalAlignment="Center" Margin="0,6,0,0"/>
</StackPanel>
</ui:SimpleStackPanel>
</Grid>
</ScrollViewer>
+68 -13
View File
@@ -1,21 +1,15 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern.Controls.Helpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
namespace Ink_Canvas
{
@@ -138,16 +132,59 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile("AutoUpdate | Update dialog buttons visibility ensured");
}
private void UpdateNowButton_Click(object sender, RoutedEventArgs e)
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;
DownloadProgressBar.Value = 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(() => {
DownloadProgressBar.Value = percent;
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)
{
DownloadProgressBar.Value = 100;
DownloadProgressText.Text = "下载完成,准备安装...";
await Task.Delay(800);
// 设置结果为立即更新
Result = UpdateResult.UpdateNow;
// 关闭窗口,返回到MainWindow处理后续下载和安装流程
DialogResult = true;
Close();
}
else
{
DownloadProgressText.Text = "下载失败,请检查网络后重试。";
UpdateNowButton.IsEnabled = true;
UpdateLaterButton.IsEnabled = true;
SkipVersionButton.IsEnabled = true;
}
}
private void UpdateLaterButton_Click(object sender, RoutedEventArgs e)
@@ -288,5 +325,23 @@ namespace Ink_Canvas
}
}
}
// 多线程分块下载并自动安装
private async Task<bool> DownloadAndInstallVersion(string version, string downloadUrl, CancellationToken token)
{
if (string.IsNullOrEmpty(downloadUrl))
{
// 自动更新场景下,downloadUrl为null,直接用主下载目录
string updatesFolderPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "AutoUpdate");
downloadUrl = Path.Combine(updatesFolderPath, $"InkCanvasForClass.CE.{version}.zip");
}
LogHelper.WriteLogToFile($"AutoUpdate | 开始安装版本: {version}");
AutoUpdateHelper.InstallNewVersionApp(version, false);
App.IsAppExitByUser = true;
Application.Current.Dispatcher.Invoke(() => {
Application.Current.Shutdown();
});
return true;
}
}
}
@@ -0,0 +1,24 @@
<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:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
mc:Ignorable="d"
Title="历史版本回滚" Height="600" Width="850" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Grid Background="#fafafa">
<ui:SimpleStackPanel VerticalAlignment="Stretch" Spacing="0">
<TextBlock Text="选择历史版本进行回滚" FontSize="24" FontWeight="Bold" Foreground="#2563eb" Margin="24,24,0,12"/>
<ComboBox x:Name="VersionComboBox" Width="400" Height="36" Margin="24,0,0,0" DisplayMemberPath="Version" SelectionChanged="VersionComboBox_SelectionChanged"/>
<Border BorderBrush="#3f3f46" Background="White" BorderThickness="1" CornerRadius="4" Margin="24,16,24,0" Height="180">
<mdxam:MarkdownScrollViewer x:Name="ReleaseNotesViewer" Foreground="Black" MarkdownStyleName="GithubLike"/>
</Border>
<Button x:Name="RollbackButton" Content="回滚到此版本" Width="360" Height="48" Margin="24,24,0,0" Click="RollbackButton_Click"/>
<StackPanel x:Name="DownloadProgressPanel" Orientation="Vertical" HorizontalAlignment="Center" Margin="0,10,0,0" Visibility="Collapsed">
<ProgressBar x:Name="DownloadProgressBar" Width="360" Height="18" Minimum="0" Maximum="100" Value="0"/>
<TextBlock x:Name="DownloadProgressText" Text="正在下载..." FontSize="14" Foreground="#2563eb" HorizontalAlignment="Center" Margin="0,6,0,0"/>
</StackPanel>
</ui:SimpleStackPanel>
</Grid>
</Window>
@@ -0,0 +1,142 @@
using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Linq; // Added for OrderByDescending
using System.ComponentModel;
using System.Threading;
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 = null;
private UpdateChannel channel = UpdateChannel.Release;
private CancellationTokenSource downloadCts = null;
public HistoryRollbackWindow(UpdateChannel channel = UpdateChannel.Release)
{
InitializeComponent();
this.channel = channel;
LoadVersions();
}
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;
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);
this.DialogResult = true;
this.Close();
}
else
{
DownloadProgressText.Text = "下载失败,请检查网络后重试。";
RollbackButton.IsEnabled = true;
VersionComboBox.IsEnabled = true;
}
}
protected override void OnClosing(CancelEventArgs e)
{
downloadCts?.Cancel();
base.OnClosing(e);
}
}
}
@@ -1,6 +1,4 @@
using Ink_Canvas.Helpers.Plugins;
using Ink_Canvas.Helpers.Plugins.BuiltIn;
using Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
using Microsoft.Win32;
using System;
using System.Collections.ObjectModel;
@@ -292,9 +290,12 @@ namespace Ink_Canvas.Windows
return;
}
// 保存插件名称,以便在删除后使用
string pluginName = SelectedPlugin.Name;
// 确认删除
MessageBoxResult result = MessageBox.Show(
$"确定要删除插件 {SelectedPlugin.Name} 吗?\n此操作将永久删除插件文件,无法恢复。",
$"确定要删除插件 {pluginName} 吗?\n此操作将永久删除插件文件,无法恢复。",
"删除确认",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
@@ -315,11 +316,11 @@ namespace Ink_Canvas.Windows
PluginListView.SelectedIndex = 0;
}
MessageBox.Show($"插件 {SelectedPlugin.Name} 已成功删除。", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
MessageBox.Show($"插件 {pluginName} 已成功删除。", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
MessageBox.Show("删除插件失败,请稍后重试。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
MessageBox.Show($"删除插件 {pluginName} 失败,请稍后重试。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
@@ -0,0 +1,20 @@
InkCanvasForClass
winexe
C#
.cs
E:\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\
Ink_Canvas
none
false
TRACE;DEBUG;NETFRAMEWORK;NET472;;NET30_OR_GREATER;NET35_OR_GREATER;NET40_OR_GREATER;NET45_OR_GREATER;NET451_OR_GREATER;NET452_OR_GREATER;NET46_OR_GREATER;NET461_OR_GREATER;NET462_OR_GREATER;NET47_OR_GREATER;NET471_OR_GREATER;NET472_OR_GREATER
E:\ICC CE\ICC CE main\community\Ink Canvas\App.xaml
22-2143008179
76-141727233
471037513499
Helpers\Plugins\BuiltIn\SuperLauncher\LauncherSettingsControl.xaml;Helpers\Plugins\BuiltIn\SuperLauncher\LauncherWindow.xaml;MainWindow.xaml;MainWindow_cs\MW_Eraser.xaml;Resources\DrawShapeImageDictionary.xaml;Resources\IconImageDictionary.xaml;Resources\SeewoImageDictionary.xaml;Resources\Styles\Dark.xaml;Resources\Styles\Light.xaml;Windows\AddCustomIconWindow.xaml;Windows\AddPickNameBackgroundWindow.xaml;Windows\CountdownTimerWindow.xaml;Windows\CustomIconWindow.xaml;Windows\CycleProcessBar.xaml;Windows\HasNewUpdateWindow.xaml;Windows\HistoryRollbackWindow.xaml;Windows\ManagePickNameBackgroundsWindow.xaml;Windows\NamesInputWindow.xaml;Windows\OperatingGuideWindow.xaml;Windows\PluginSettingsWindow.xaml;Windows\RandWindow.xaml;Windows\YesOrNoNotificationWindow.xaml;
False
+31 -1
View File
@@ -1 +1,31 @@
1. 更新了自动更新
1. 改进端点吸附
2. 新增自定义浮动栏图标
3. 新增自定义点名背景
4. 新增退出收纳模式自动进入批注选项
5. 改进自动更新
6. 改进进程检测
7. 改进设置侧边栏
8. 修复PPT联动模块
9. 修复使用正方形预设时多出来一条直线
10. 新增白板自定义调色盘
11. 改进手掌擦逻辑
12. 新增并改进了插件功能
13. 修复大量触摸问题
14. 改进橡皮
15. 新增设置配置备份功能
16. 新增墨迹全屏保存功能
17. 修复win7下的自动更新不可用的问题
18. 改进墨迹打开功能
19. 改进插件功能及启动台插件
20. 改进墨迹平滑方案
21. 改进窗口无焦点
22. 修复白板页面预览不可触摸的问题
23. 新增插入图片功能
24. 新增侧边栏退出放映按钮
25. 新增退出PPT自动恢复收纳模式
26. 新增PPT自动回到首页
27. 新增查杀鸿合屏幕书写后进入批注
28. 修复浮动栏高度计算
29. 修复墨迹错页
30. 改进直线拉直
31. 改进白板时间显示