Compare commits

..

674 Commits

Author SHA1 Message Date
CJK_mkp 2097a1250b Merge pull request #146 from InkCanvasForClass/beta
ICC CE 1.7.9.0
2025-08-31 10:24:44 +08:00
CJK_mkp 22025f9b00 Merge pull request #145 from InkCanvasForClass/beta
ICC CE 1.7.9.0
2025-08-31 10:22:57 +08:00
CJKmkp 80c1badba5 更新版本号 2025-08-31 10:08:49 +08:00
CJKmkp be308ba280 improve:截图 2025-08-31 10:06:11 +08:00
CJKmkp fa7f3d44e4 improve:截图及浮动栏 2025-08-31 09:59:49 +08:00
CJKmkp 9bb00489fe improve:截图及浮动栏及代码优化 2025-08-31 09:54:13 +08:00
CJKmkp 33948c604c improve:图片插入 2025-08-31 09:20:35 +08:00
CJKmkp b8fe5bbd66 improve:图片插入 2025-08-31 09:18:30 +08:00
CJKmkp 3fcc01c253 improve:图片插入 2025-08-31 09:15:53 +08:00
CJKmkp 658d48c17b improve:图片插入 2025-08-31 09:12:30 +08:00
CJKmkp 8d76c014c8 优化代码 2025-08-31 09:09:22 +08:00
CJKmkp 26cd125534 优化代码 2025-08-31 09:00:32 +08:00
CJKmkp 1bc23af61a 优化代码 2025-08-31 08:51:50 +08:00
CJKmkp a2aa6b48a8 improve:用户体验分级及图片插入 2025-08-31 08:04:46 +08:00
CJKmkp 5784c974f7 优化代码 2025-08-31 07:56:26 +08:00
CJKmkp 80503dc42e 优化代码 2025-08-31 07:54:43 +08:00
PrefacedCorg d76195f7ae fix:二级菜单下不去 2025-08-31 01:55:17 +08:00
PrefacedCorg 2fe482b802 让警告没那么多
应该不会出问题的吧
2025-08-31 01:47:00 +08:00
PrefacedCorg 16283f4643 添加空值检查 2025-08-31 01:00:59 +08:00
CJKmkp cd7a801400 还未完成的选择工具栏 2025-08-31 00:39:40 +08:00
CJKmkp f6d8558d07 improve:自动更新 2025-08-31 00:33:41 +08:00
CJKmkp de20b506f0 improve:用户体验分级 2025-08-31 00:25:22 +08:00
CJKmkp ea1d52292e improve:快捷键 2025-08-30 23:53:26 +08:00
CJKmkp 463e506ca3 improve:快捷键 2025-08-30 23:28:28 +08:00
CJKmkp 3bae64a2c7 improve:插入图片improve:插入图片 2025-08-30 23:19:53 +08:00
CJKmkp 5d13c8b543 improve:插入图片 2025-08-30 23:14:00 +08:00
CJKmkp c6c0789794 improve:自动更新 2025-08-30 23:10:02 +08:00
CJKmkp d7df39290f improve:插入图片及墨迹平滑 2025-08-30 22:59:12 +08:00
CJKmkp b64cefad46 improve:插入图片及墨迹平滑 2025-08-30 22:54:16 +08:00
CJKmkp 4690ab3c30 improve:插入图片 2025-08-30 22:14:39 +08:00
CJKmkp b61c7490b3 improve:截图 2025-08-30 21:23:36 +08:00
CJKmkp 9e511d29a6 delete:图片插入选中和移动相关 2025-08-30 21:18:34 +08:00
CJKmkp a9cdc36967 improve:长按翻页 2025-08-30 20:59:50 +08:00
CJKmkp 96c51cd7ff 优化代码 2025-08-30 19:40:14 +08:00
CJKmkp 636769c0ef fix:插入图片子面板折叠问题 2025-08-30 18:59:32 +08:00
CJKmkp 7beb2a3cc5 更新版本号 2025-08-30 18:49:31 +08:00
CJKmkp 52d318054c fix:部分情况高光错位 2025-08-30 18:48:46 +08:00
CJKmkp 5ee2ad6f3d improve:浮动栏动态调整 2025-08-30 18:36:20 +08:00
CJKmkp 097d414d0d fix:隐藏按钮导致的高光错位 2025-08-30 18:33:57 +08:00
CJK_mkp e0ce119374 Merge pull request #144 from InkCanvasForClass/beta
ICC CE 1.7.8.3
2025-08-30 18:24:09 +08:00
CJKmkp da9a5242cb 上传日志 2025-08-30 18:22:37 +08:00
CJKmkp 0211b38be9 更新版本号 2025-08-30 18:12:43 +08:00
CJKmkp 512e9b21cd improve:issue #143 2025-08-30 18:10:55 +08:00
CJKmkp 9181ad55ae fix:部分窗口无法点击 2025-08-30 16:25:10 +08:00
CJKmkp a1cb9e1b28 improve:自动更新 2025-08-30 16:10:20 +08:00
CJKmkp ea09a3e798 improve:自动更新 2025-08-30 15:49:37 +08:00
CJKmkp 21fa146f6f improve:自动更新 2025-08-30 15:28:51 +08:00
CJKmkp 1a3130041f improve:自动更新 2025-08-30 14:56:59 +08:00
CJKmkp 7095a5890c fix:issue #133 2025-08-30 14:53:50 +08:00
CJKmkp 4a86d1aa05 improve:自动更新 2025-08-30 14:11:39 +08:00
CJKmkp 19fe7223fb improve:快捷键 2025-08-30 11:34:29 +08:00
CJKmkp 8867fde3f2 fix:issue #141 2025-08-30 11:29:16 +08:00
CJKmkp 588d1822b1 fix:issue #141 2025-08-30 11:20:53 +08:00
CJK_mkp d2b3b38d9e fix:issue #141 2025-08-28 22:13:18 +08:00
CJK_mkp 206ae3d9ed fix:issue #141 2025-08-28 21:02:47 +08:00
CJK_mkp a67b7a2fd0 fix:issue #141 2025-08-28 20:13:43 +08:00
CJK_mkp f5a08b225c fix:issue #141 2025-08-28 18:35:17 +08:00
CJK_mkp c807f97c29 fix:issue #141 2025-08-28 18:32:47 +08:00
CJK_mkp 8382413137 fix:issue #141 2025-08-28 18:31:59 +08:00
CJK_mkp c6acafd3a6 fix:issue #141 2025-08-28 18:28:17 +08:00
CJK_mkp 9d43056361 fix:issue #141 2025-08-28 18:25:04 +08:00
CJK_mkp 52f8e24954 fix:issue #141 2025-08-28 18:24:12 +08:00
CJK_mkp dac05fec84 fix:issue #141 2025-08-28 18:20:44 +08:00
CJK_mkp cb4ed77572 fix:issue #141 2025-08-28 18:17:29 +08:00
CJK_mkp 3427cbdc2e fix:issue #141 2025-08-28 18:11:26 +08:00
CJK_mkp ec0fb0c3cf fix:issue #141 2025-08-28 18:10:52 +08:00
CJK_mkp 357983179c fix:issue #141 2025-08-28 18:07:39 +08:00
CJK_mkp 78b66c141e fix:issue #141 2025-08-28 18:05:20 +08:00
CJK_mkp 3ac88bb400 fix:issue #141 2025-08-28 18:02:44 +08:00
CJK_mkp 27493b857c 撤销更改 2025-08-28 17:41:29 +08:00
CJK_mkp e5a976abcf fix:issue #141 2025-08-28 17:37:08 +08:00
CJK_mkp 9e89bf5303 Merge pull request #142 from InkCanvasForClass/all-contributors/add-Jursin
docs: add Jursin as a contributor for design
2025-08-28 09:01:25 +08:00
allcontributors[bot] 54eb330711 docs: update .all-contributorsrc 2025-08-28 01:01:08 +00:00
allcontributors[bot] e9014c6f5d docs: update README.md 2025-08-28 01:01:07 +00:00
CJK_mkp 620bcf6fab fix:issue #140 2025-08-27 17:43:04 +08:00
CJK_mkp c03bbd9e13 fix:issue #140 2025-08-27 17:41:58 +08:00
CJK_mkp b428b4ec5b 回滚等待重构截图功能 2025-08-27 15:57:06 +08:00
CJK_mkp 876cc116ea fix:issue #140 2025-08-27 12:23:05 +08:00
CJK_mkp 0ba00c08ef fix:更新日志显示问题 2025-08-25 09:53:01 +08:00
CJK_mkp d41ecd9d55 fix:修正错误地址 2025-08-25 09:00:45 +08:00
CJK_mkp 35d351ce6b Merge pull request #138 from InkCanvasForClass/beta
ICC CE 1.7.8.0
2025-08-24 18:02:28 +08:00
CJKmkp 5d3af58361 fix:浮动栏动画异常 2025-08-24 18:01:04 +08:00
CJKmkp 8554b92f42 improve:外部点名 2025-08-24 17:26:26 +08:00
CJKmkp 76363b4263 更新版本号 2025-08-24 16:29:51 +08:00
CJKmkp b4250b9161 fix:开关状态显示错误 2025-08-24 16:24:29 +08:00
CJKmkp 441f40886f fix:issue #133 2025-08-24 16:09:14 +08:00
CJKmkp e8e8ad5d63 更新版本号 2025-08-24 12:13:55 +08:00
CJKmkp 454078ec82 add:更多插件支持 2025-08-24 12:02:29 +08:00
CJK_mkp 63f29c2686 Merge pull request #137 from InkCanvasForClass/beta
ICC CE 1.7.7.7
2025-08-24 11:58:06 +08:00
CJKmkp 280445f613 fix:浮动栏动画异常 2025-08-24 11:47:39 +08:00
CJKmkp d7f6433b53 更新版本号 2025-08-24 11:09:41 +08:00
CJKmkp 22da4cc408 fix:无焦点无法翻页 2025-08-24 11:08:13 +08:00
CJKmkp 4e185ef584 improve:长按翻页 2025-08-24 10:39:24 +08:00
CJKmkp fd9b4b4ba6 improve:墨迹渐隐开关 2025-08-24 10:17:21 +08:00
CJKmkp 2f79bbcb0f fix:错误的唤起URL 2025-08-24 09:46:52 +08:00
CJKmkp 2c70d243df improve:墨迹渐隐开关 2025-08-24 09:33:27 +08:00
CJKmkp 7710b77255 improve:快捷键和墨迹渐隐 2025-08-24 09:17:58 +08:00
CJKmkp d1eed23399 improve:快捷键 2025-08-24 09:09:48 +08:00
CJKmkp ff9ce4df44 imrpove:快捷键 2025-08-24 08:45:53 +08:00
CJKmkp b7c52842f2 improve:快捷键 2025-08-24 02:30:38 +08:00
CJKmkp 365459f649 improve:快捷键 2025-08-24 02:27:57 +08:00
CJKmkp e6354f724f 优化日志 2025-08-24 02:03:34 +08:00
CJKmkp 14eedca939 fix:时间戳问题 2025-08-24 01:46:48 +08:00
CJKmkp 83529cfe09 add:issue #135 #136 2025-08-24 00:05:26 +08:00
CJKmkp bd4e1c1810 更新版本号 2025-08-23 23:32:44 +08:00
CJKmkp 5665fcc823 add:issue #131 2025-08-23 23:13:39 +08:00
CJKmkp 710a9014dd improve:issue #112 2025-08-23 21:39:00 +08:00
CJKmkp 108c6b2b17 improve:使用数据保存 2025-08-23 20:24:48 +08:00
CJKmkp 70735943c3 add:长按翻页 2025-08-23 19:43:09 +08:00
CJKmkp d01a24f879 add:底部翻页控件预览 2025-08-23 19:32:13 +08:00
CJKmkp ec2d5043ff add:底部PPT翻页按钮调节 2025-08-23 19:27:30 +08:00
CJKmkp 15082c2c52 fix:墨迹出现在不对应的PPT文档上 2025-08-23 19:16:19 +08:00
CJKmkp 44278d68b4 fix:翻页控件不合理的显示时机 2025-08-23 19:10:53 +08:00
CJKmkp 40eeb9db66 fix:退出放映模式后浮动栏异常问题 2025-08-23 19:04:46 +08:00
CJKmkp a5eb1dfca7 fix:开启部分功能后手势面板显示异常 2025-08-23 18:58:57 +08:00
CJKmkp 8719677f11 fix:开启部分功能后手势面板显示异常 2025-08-23 18:55:36 +08:00
CJKmkp 8f01b6c5fe fix:切换时荧光笔状态异常 2025-08-23 18:51:16 +08:00
CJKmkp 9e63a3f49b fix:快捷调色板不支持荧光笔 2025-08-23 18:43:47 +08:00
CJKmkp 84edb7bbe6 fix:快捷调色板不支持荧光笔 2025-08-23 18:39:12 +08:00
CJKmkp c86ce00a17 fix:快捷调色板不支持荧光笔 2025-08-23 18:29:24 +08:00
CJKmkp aba6c18a25 fix:issue #133 2025-08-23 18:24:24 +08:00
2,2,3-三甲基戊烷 62c81e6d44 Merge pull request #134 from InkCanvasForClass/all-contributors/add-PrefacedCorg 2025-08-23 10:44:32 +08:00
allcontributors[bot] 68ea323855 docs: update .all-contributorsrc 2025-08-23 02:44:15 +00:00
allcontributors[bot] f94d81ad20 docs: update README.md 2025-08-23 02:44:14 +00:00
CJK_mkp 063f8b1751 优化日志 2025-08-19 18:04:45 +08:00
CJK_mkp 35cfd26ece fix:退出不完全时更新 2025-08-19 17:52:02 +08:00
CJK_mkp f331cb1b4d fix:issue #110 2025-08-19 17:41:42 +08:00
CJK_mkp 502e205071 fix:关机时不保存使用数据 2025-08-18 18:01:23 +08:00
CJK_mkp 975b563b8d fix:关机时不保存使用数据 2025-08-18 17:57:56 +08:00
CJK_mkp cc88630859 fix:关机时不保存使用数据 2025-08-18 17:54:41 +08:00
CJK_mkp cce0b930ec fix:关机时不保存使用数据 2025-08-18 17:49:33 +08:00
CJK_mkp 723c825df2 fix:关机时不保存使用数据 2025-08-18 17:42:36 +08:00
CJK_mkp 5b4c966354 优化日志 2025-08-13 22:03:18 +08:00
CJK_mkp 69023f98d9 优化日志 2025-08-13 22:00:53 +08:00
CJK_mkp dce5722e96 优化日志 2025-08-13 21:59:35 +08:00
CJK_mkp cbdbc93274 Merge pull request #127 from InkCanvasForClass/beta
ICC CE 1.7.7.5
2025-08-13 14:29:53 +08:00
CJKmkp 042b153684 improve:窗口置顶 2025-08-13 12:52:56 +08:00
CJKmkp 4054423721 更新版本号 2025-08-13 12:37:27 +08:00
CJKmkp 2e63a6eaca improve:面积擦子面板显示位置 2025-08-13 12:31:58 +08:00
CJKmkp 9eca7eb2ee fix:插入图片面板点击空白不折叠 2025-08-13 12:16:55 +08:00
CJKmkp 1489fb645e add:橡皮子面板中的清空按钮,更符合操作逻辑 2025-08-13 12:11:44 +08:00
CJKmkp 5c0ca841d7 fix:调色盘导致的按钮高光显示错位 2025-08-13 11:54:36 +08:00
CJKmkp a4d3d3ff9c 删除无用文件 2025-08-13 10:28:54 +08:00
CJKmkp c3b67f4a4b 更新版本号 2025-08-12 15:51:26 +08:00
CJKmkp cde5daf19a add:issue #126 2025-08-12 15:45:33 +08:00
CJKmkp 904b2c0988 add:issue #124 2025-08-12 15:33:19 +08:00
CJKmkp f9907e2ec6 improve:按钮显示控制 2025-08-12 12:50:29 +08:00
CJKmkp 7329c0097c improve:快捷调色盘 2025-08-12 12:30:48 +08:00
CJKmkp 0478949305 improve:快捷调色盘 2025-08-12 12:19:55 +08:00
CJKmkp 34172a54fe 更新版本号 2025-08-12 11:14:24 +08:00
CJKmkp 6dd629eda5 improve:快捷调色盘 2025-08-12 11:13:35 +08:00
CJKmkp 636dd2b8d5 add:快捷调色盘 2025-08-12 11:08:24 +08:00
CJKmkp b20dbc5202 更新版本号 2025-08-11 22:48:25 +08:00
CJKmkp ee45104eb9 improve:快捷调色盘 2025-08-11 22:47:43 +08:00
CJKmkp 152be89860 add:按钮显示控制 2025-08-11 22:42:36 +08:00
CJKmkp 6b5a375542 更新版本号 2025-08-11 21:49:29 +08:00
CJKmkp 03d049846d 更新版本号 2025-08-11 20:32:12 +08:00
CJKmkp 6ed084bb94 add:快捷调色盘 2025-08-11 20:28:33 +08:00
CJKmkp f5332b63a9 add:快捷调色盘 2025-08-11 20:23:14 +08:00
CJKmkp 87356215c3 add:快捷调色盘 2025-08-11 20:19:01 +08:00
CJKmkp 07c7acc37a add:按钮显示控制 2025-08-11 19:18:21 +08:00
CJKmkp 4a6d9dee67 add:按钮显示控制 2025-08-11 19:08:05 +08:00
CJKmkp 52eb93e59c 更新版本号 2025-08-11 11:38:35 +08:00
CJKmkp 2245a018e6 fix:触摸线擦 2025-08-11 09:13:12 +08:00
CJKmkp 8b327fd715 fix:几何触摸绘制 2025-08-10 14:08:07 +08:00
CJKmkp 7a7289a4c8 更新版本号 2025-08-10 12:21:24 +08:00
CJKmkp ceb259819f fix:几何触摸绘制 2025-08-10 12:20:51 +08:00
CJKmkp fdfbaedbd7 fix:几何触摸绘制 2025-08-10 11:58:58 +08:00
PrefacedCorg 9591fbf146 我也不知道我改了啥 2025-08-09 13:16:22 +08:00
CJKmkp b2a09dbf6d fix:几何墨迹问题 2025-08-04 20:25:42 +08:00
PrefacedCorg 11a5a7fdbe 代码清理 2025-08-03 16:46:33 +08:00
PrefacedCorg 745b798d89 Update MainWindow.xaml 2025-08-03 15:23:46 +08:00
PrefacedCorg 7bac32e3c4 Update MainWindow.xaml 2025-08-01 02:56:53 +08:00
PrefacedCorg 880ca08571 fix time 2025-08-01 02:56:24 +08:00
PrefacedCorg 85d4d8a71e 修复二级菜单移位
修复二级菜单移位(和优化江陵写的屎山)
2025-07-31 20:38:30 +08:00
CJKmkp abb8ed0bcc fix:插入图片 2025-07-31 16:49:53 +08:00
CJKmkp bea0d10a6c Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-07-31 16:36:42 +08:00
CJK_mkp 80d943af23 Merge pull request #123 from InkCanvasForClass/beta-PrefacedCorg
合并分支
2025-07-31 15:22:18 +08:00
PrefacedCorg 90b85e469d Update MainWindow.xaml 2025-07-31 15:07:39 +08:00
PrefacedCorg 0d419c1323 fix 二级菜单移位 2025-07-31 14:52:44 +08:00
PrefacedCorg f7a2ecc2e0 给二级菜单加了个x :( 2025-07-31 14:12:49 +08:00
PrefacedCorg 5fdbf83f32 新增图片选择选项 2025-07-31 13:23:33 +08:00
CJKmkp c513ad9291 improve:插入图片 2025-07-31 10:55:01 +08:00
CJKmkp a2dfa55dbd improve:插入图片 2025-07-31 10:27:19 +08:00
CJKmkp 78f8d9a271 fix:退出确认按钮 2025-07-31 10:03:29 +08:00
CJK_mkp b25a5aac8c Merge pull request #122 from MKStoler1024/beta
fix: gitignore
2025-07-31 09:46:24 +08:00
MKStoler1024 25cc70035e fix: gitignore 2025-07-31 09:39:46 +08:00
MKStoler1024 e5d70d5a0e del: unneeded files 2025-07-31 09:38:07 +08:00
MKStoler1024 066b0d1f8a fix: gitignore 2025-07-31 09:35:00 +08:00
CJKmkp 71b2e56187 improve:截图功能 2025-07-30 20:06:43 +08:00
CJKmkp 457832fbbe improve:截图功能 2025-07-30 20:06:29 +08:00
CJKmkp 00554e066b add:截图功能 2025-07-30 19:56:22 +08:00
CJKmkp 9f9775e585 fix:图片插入 2025-07-30 14:18:45 +08:00
CJKmkp 9cf70ae74e fix:图片插入 2025-07-30 14:12:10 +08:00
CJKmkp 4f046987c6 fix:线擦切换至批注的面板弹出及高光显示问题 2025-07-30 13:24:52 +08:00
CJKmkp 699666c23b fix:issue #120 2025-07-29 23:14:20 +08:00
CJKmkp 6a8cdd0155 fix:issue #118 2025-07-29 19:07:59 +08:00
CJKmkp ce61f0e2b7 improve:IACore 2025-07-29 19:00:45 +08:00
CJKmkp b3cb0bf93f improve:IACore 2025-07-29 19:00:32 +08:00
CJKmkp 07328eea2d Revert "delete:IACore释放"
This reverts commit bfb6346812.
2025-07-29 18:37:00 +08:00
CJK_mkp 5f7a6f2e07 Merge pull request #119 from InkCanvasForClass/beta
ICC CE 1.7.6.0
2025-07-29 18:19:26 +08:00
CJKmkp 39d54cc493 更新版本号 2025-07-29 18:06:59 +08:00
CJKmkp d31f40408b fix:图片墨迹保存问题 2025-07-29 17:59:41 +08:00
CJKmkp 7f88d9ae27 fix:图片墨迹保存问题 2025-07-29 17:44:15 +08:00
CJKmkp 1c1dd81474 fix:图片墨迹保存问题 2025-07-29 17:38:58 +08:00
CJKmkp 53a498c581 fix:IACore 2025-07-29 16:28:22 +08:00
CJKmkp bfb6346812 delete:IACore释放 2025-07-29 16:08:24 +08:00
CJKmkp bd2fa1e427 improve:PPT模块 2025-07-29 09:43:18 +08:00
CJKmkp 6c4bfeff29 improve:PPT模块 2025-07-29 09:35:47 +08:00
CJKmkp 299a77eea8 improve:PPT模块 2025-07-29 09:19:27 +08:00
CJKmkp 351a00fb1e 更新版本号 2025-07-29 09:08:39 +08:00
CJKmkp 2140e1ebe1 improve:墨迹平滑 2025-07-29 09:02:03 +08:00
CJKmkp 497a820ba2 improve:PPT模块 2025-07-29 08:44:17 +08:00
CJKmkp 7182f3554a Revert "fix:issue #110"
This reverts commit f8e4732dcd.
2025-07-29 02:33:19 +08:00
CJKmkp 506ba52502 improve:直线拉直 2025-07-29 01:40:35 +08:00
CJKmkp 90c1630af4 improve:PPT模块 2025-07-29 01:34:51 +08:00
CJKmkp 5934abd448 fix:进退白板墨迹消失 2025-07-29 01:31:21 +08:00
CJKmkp 86f432ef01 improve:PPT模块 2025-07-29 01:24:42 +08:00
CJKmkp 4913019c5c 优化PPT模块 2025-07-29 01:15:32 +08:00
CJKmkp a6316797e6 更新软件信息 2025-07-29 00:01:47 +08:00
CJKmkp 00d7549bde 更新互斥锁 2025-07-28 23:57:50 +08:00
CJKmkp 8fc33f5649 更新软件名称 2025-07-28 23:51:14 +08:00
CJKmkp 729b544675 improve:PPT模块 2025-07-28 22:36:02 +08:00
CJK_mkp 865415a6c0 Merge pull request #113 from InkCanvasForClass/beta
ICC CE 1.7.5.0
2025-07-28 19:16:24 +08:00
CJKmkp 32e8324275 更新日志 2025-07-28 19:12:12 +08:00
CJKmkp 842f6dd726 更新版本号 2025-07-28 19:10:51 +08:00
CJKmkp 59f7d11df3 improve:单文件 2025-07-28 19:08:06 +08:00
CJKmkp dcd2f52c59 improve:插入图片 2025-07-28 19:02:45 +08:00
CJKmkp b45413c232 fix:墨迹纠正失效 2025-07-28 18:29:22 +08:00
CJKmkp b4481ff680 fix:issue #111 2025-07-28 18:23:07 +08:00
CJKmkp d7d7a3919f improve:日志输出 2025-07-28 18:11:20 +08:00
CJKmkp f8e4732dcd fix:issue #110 2025-07-28 17:44:15 +08:00
CJKmkp 1b92ed66b7 更新README 2025-07-28 16:19:19 +08:00
CJKmkp 9ba74b9504 更新版本号 2025-07-28 16:12:40 +08:00
CJKmkp 8cd49f12d1 更新README 2025-07-28 16:10:40 +08:00
CJKmkp c48ca9ee89 fix:issue #93 2025-07-28 16:04:36 +08:00
CJKmkp 43bcf71bf5 fix:issue #93 2025-07-28 15:47:18 +08:00
CJKmkp ee48813df1 fix:issue #93 2025-07-28 15:18:33 +08:00
CJKmkp f4a67e2822 fix:issue #93 2025-07-28 14:45:37 +08:00
CJKmkp f03733da04 优化代码 2025-07-28 14:40:44 +08:00
CJK_mkp 207560bcc7 Merge pull request #109 from InkCanvasForClass/beta
ICC CE 1.7.4.1
2025-07-28 11:46:23 +08:00
CJKmkp f38313ff2c 更新版本号 2025-07-28 11:37:58 +08:00
CJKmkp b5d9e21f37 improve:用户体验分级 2025-07-28 11:35:47 +08:00
CJKmkp 5d42a8957e fix:issue #93 2025-07-28 11:15:35 +08:00
CJK_mkp b276c60909 Merge pull request #108 from InkCanvasForClass/all-contributors/add-PrefacedCorg
docs: add PrefacedCorg as a contributor for code
2025-07-28 02:05:44 +08:00
allcontributors[bot] d03564bce9 docs: update .all-contributorsrc 2025-07-27 18:05:04 +00:00
allcontributors[bot] 41409c39d5 docs: update README.md 2025-07-27 18:05:03 +00:00
CJK_mkp aa9c5107d0 Merge pull request #107 from PrefacedCorg/beta
fix:issue #93
2025-07-28 02:02:57 +08:00
PrefacedCorg 7a141822e7 fix:issue #93 2025-07-28 01:49:54 +08:00
CJKmkp a4fd301d5a fix:issue #93 2025-07-28 00:48:50 +08:00
CJKmkp b2ef8b96ef fix:issue #93 2025-07-27 22:52:04 +08:00
CJKmkp baa9e1003e fix:issue #93 2025-07-27 22:00:37 +08:00
CJK_mkp 5d17a586d5 Merge pull request #106 from InkCanvasForClass/beta
ICC CE 1.7.4.0
2025-07-27 16:20:53 +08:00
CJKmkp 36b97b2adc 更新版本号 2025-07-27 16:15:36 +08:00
CJKmkp f13bffa834 add:优化至单文件版本 2025-07-27 16:05:38 +08:00
CJKmkp 4394566ed3 fix:issue #105 2025-07-27 00:13:41 +08:00
2,2,3-三甲基戊烷 de95c24f66 fix: Fix the problems of the bandage of STCN 2025-07-26 19:42:27 +08:00
CJK_mkp 6058cb9cff Merge pull request #104 from InkCanvasForClass/beta
ICC CE 1.7.3.0
2025-07-26 19:38:22 +08:00
CJKmkp 6b20d3e268 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-07-26 19:37:12 +08:00
CJKmkp ce037a437a 更新版本号 2025-07-26 19:36:39 +08:00
2,2,3-三甲基戊烷 1349dab6d4 fix: Update README.md 2025-07-26 19:35:16 +08:00
CJKmkp d3d31925ee Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-07-26 19:34:22 +08:00
CJKmkp d9fa77c86b 更新版本号 2025-07-26 19:34:06 +08:00
CJKmkp dad01235b0 fix:侧边栏中的退出放映按钮退出不完全的问题 2025-07-26 19:30:56 +08:00
2,2,3-三甲基戊烷 52d9c26076 Merge pull request #103 from InkCanvasForClass/all-contributors/add-awesome-iwb
docs: add awesome-iwb as a contributor for doc
2025-07-26 19:26:01 +08:00
allcontributors[bot] 1a0236237d docs: update .all-contributorsrc 2025-07-26 11:25:43 +00:00
allcontributors[bot] 58bb7a5ebc docs: update README.md 2025-07-26 11:25:42 +00:00
CJK_mkp efbab58bca Merge pull request #102 from InkCanvasForClass/all-contributors/add-Alan-CRL
docs: add Alan-CRL as a contributor for financial
2025-07-26 19:20:37 +08:00
2,2,3-三甲基戊烷 eedbc7a863 Merge branch 'beta' into all-contributors/add-Alan-CRL 2025-07-26 19:20:26 +08:00
allcontributors[bot] 626cda63ff docs: update .all-contributorsrc 2025-07-26 11:17:27 +00:00
allcontributors[bot] 7b7a9d93aa docs: update README.md 2025-07-26 11:17:26 +00:00
2,2,3-三甲基戊烷 f5c68dac61 Merge pull request #101 from InkCanvasForClass/all-contributors/add-MKStoler1024
docs: add MKStoler1024 as a contributor for doc, code, and design
2025-07-26 19:16:22 +08:00
allcontributors[bot] 2b6b106771 docs: update .all-contributorsrc 2025-07-26 11:15:47 +00:00
allcontributors[bot] 047586883e docs: update README.md 2025-07-26 11:15:46 +00:00
2,2,3-三甲基戊烷 0c1a25dd6b feat: Merge pull request #100 from InkCanvasForClass/all-contributors/add-Alan-CRL
docs: add Alan-CRL as a contributor for code, infra, and doc
2025-07-26 19:11:45 +08:00
allcontributors[bot] 7840a9a713 docs: update .all-contributorsrc 2025-07-26 11:11:01 +00:00
allcontributors[bot] 28f96ffcd3 docs: update README.md 2025-07-26 11:11:00 +00:00
CJKmkp 04b2663183 improve:墨迹平滑 2025-07-26 19:03:07 +08:00
2,2,3-三甲基戊烷 1a11027871 feat: Merge pull request #99 from InkCanvasForClass/all-contributors/add-2-2-3-trimethylpentane
docs: add 2-2-3-trimethylpentane as a contributor for blog, doc, and design
2025-07-26 19:00:48 +08:00
allcontributors[bot] 854a00803e docs: update .all-contributorsrc 2025-07-26 11:00:10 +00:00
allcontributors[bot] 11ae5f157f docs: update README.md 2025-07-26 11:00:09 +00:00
CJKmkp ecfe05139e improve:插件功能 2025-07-26 18:37:08 +08:00
CJKmkp 4a392e03a7 更新版本号 2025-07-26 16:24:30 +08:00
CJKmkp a66037f886 improve:用户体验分级 2025-07-26 16:22:55 +08:00
CJKmkp 58c399dcbe improve:用户体验分级 2025-07-26 16:08:22 +08:00
CJKmkp f33e617f44 improve:用户体验分级 2025-07-26 15:57:28 +08:00
CJKmkp 3e976c1026 improve:用户体验分级 2025-07-26 15:01:00 +08:00
CJKmkp c15c75075c improve:用户体验分级 2025-07-26 15:00:31 +08:00
CJKmkp bf8d988c75 improve:用户体验分级 2025-07-26 14:58:10 +08:00
CJKmkp 6fb7af3d46 add:用户体验分级 2025-07-26 14:29:24 +08:00
CJKmkp 1c2860c180 更新版本号 2025-07-26 12:41:44 +08:00
CJKmkp fc41f10c37 improve:自动更新 2025-07-26 12:37:39 +08:00
CJKmkp dedf366866 improve:自动更新 2025-07-26 12:33:08 +08:00
CJKmkp edb10096d6 improve:自动更新 2025-07-26 12:20:23 +08:00
CJKmkp ba42a1e6c9 improve:自动更新 2025-07-26 12:02:58 +08:00
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
CJK_mkp e9ec424625 Merge pull request #86 from InkCanvasForClass/beta
ICC CE 1.7.1.0
2025-07-16 14:09:49 +08:00
CJKmkp 1e96477127 更新版本号 2025-07-16 14:04:40 +08:00
CJKmkp 87ffa48265 improve:插件功能 2025-07-16 14:02:54 +08:00
CJKmkp 8b64df435c improve:插件功能 2025-07-16 13:50:32 +08:00
CJKmkp dcf88ee510 improve:插件功能 2025-07-16 13:39:24 +08:00
CJKmkp b21a15376d add:插件功能 2025-07-16 13:35:17 +08:00
CJKmkp 3e87ed4f9b add:墨迹全屏保存 #79 2025-07-16 09:49:41 +08:00
CJKmkp a6cd9c955b 更新版本号 2025-07-16 09:17:46 +08:00
CJKmkp 86b52b76ed add:自定义白板背景色 #39 2025-07-16 09:16:36 +08:00
CJKmkp 853f380e5d improve:手掌擦机制 2025-07-16 08:03:08 +08:00
CJKmkp 027686a3b5 更新版本号 2025-07-15 21:43:44 +08:00
CJKmkp 79b87e59be improve:设置侧边栏 2025-07-15 21:10:49 +08:00
CJKmkp 3a37e55162 add:退出收纳模式自动进入批注选项 2025-07-15 21:00:00 +08:00
CJKmkp 1ff40c0016 add:自定义点名背景 2025-07-15 20:50:12 +08:00
CJKmkp 4f7c1021c8 improve:自定义图标保存了位置 2025-07-15 20:33:47 +08:00
CJKmkp e687c78ba8 add:自定义浮动栏图标 2025-07-15 20:30:10 +08:00
CJKmkp 2c2f46a0d8 improve:端点吸附 2025-07-15 18:30:25 +08:00
CJKmkp 29fa565258 fix:issue #75 2025-07-15 18:09:38 +08:00
CJKmkp 51f3d410c9 fix:issue #47 2025-07-15 17:55:22 +08:00
CJKmkp a0d159da9d improve:进程检测 2025-07-15 16:06:29 +08:00
CJKmkp 9edb58ee27 improve:自动更新 2025-07-15 14:36:54 +08:00
CJK_mkp a8de65ab0e Merge pull request #85 from InkCanvasForClass/beta
合并分支
2025-07-15 14:11:29 +08:00
CJK_mkp ef8a894489 Merge pull request #84 from InkCanvasForClass/all-contributors/add-CreeperAWA
All contributors/add creeper awa
2025-07-15 14:09:46 +08:00
allcontributors[bot] 992f388a56 docs: update .all-contributorsrc 2025-07-14 08:23:15 +00:00
allcontributors[bot] a54e7206a0 docs: update README.md 2025-07-14 08:23:14 +00:00
Hydrogen c4e74b21fc Merge pull request #82 from InkCanvasForClass/all-contributors/add-Hydro11451
docs: add Hydro11451 as a contributor for code
2025-07-14 16:22:51 +08:00
allcontributors[bot] 7b9e07bdda docs: update .all-contributorsrc 2025-07-14 08:22:19 +00:00
allcontributors[bot] 19f5204253 docs: update README.md 2025-07-14 08:22:18 +00:00
Hydrogen c4ad5c5f81 Merge pull request #81 from InkCanvasForClass/all-contributors/add-CJKmkp
docs: add CJKmkp as a contributor for code
2025-07-14 16:21:27 +08:00
allcontributors[bot] e888118a20 docs: update .all-contributorsrc 2025-07-14 08:21:08 +00:00
allcontributors[bot] 7afce136f6 docs: update README.md 2025-07-14 08:21:07 +00:00
Hydrogen 71c4dadeee Delete myself from All Contributors
我已经退出ICC-CE管理组。——Hydrogen
2025-07-14 16:19:40 +08:00
CJK_mkp c6a95c99ef Merge pull request #80 from InkCanvasForClass/beta
合并分支
2025-07-14 08:58:57 +08:00
Hydrogen a6d44685b3 Update README.md 2025-07-14 08:42:19 +08:00
CJK_mkp a8ce61dad9 Update Settings.cs 2025-07-12 17:33:13 +08:00
CJK_mkp 0f47d1473d Update Settings.cs 2025-07-12 17:32:05 +08:00
CJK_mkp 2134ee516a Update Settings.cs 2025-07-12 15:59:09 +08:00
CJK_mkp 6c60306bee Update MW_Settings.cs 2025-07-12 12:16:41 +08:00
CJK_mkp 816748833c Update MainWindow.xaml 2025-07-12 12:14:43 +08:00
CJK_mkp cac0fca3bb Update MW_Settings.cs 2025-07-12 10:49:24 +08:00
CJK_mkp 5ed28b121e Update MW_PPT.cs 2025-07-12 09:09:20 +08:00
CJK_mkp c4180eba6f Update MW_Save&OpenStrokes.cs 2025-07-12 09:01:49 +08:00
CJK_mkp 0065a1f81f Update MW_Screenshot.cs 2025-07-12 09:00:51 +08:00
CJK_mkp e1f3a6ada4 Merge pull request #78 from InkCanvasForClass/beta
合并分支
2025-07-10 21:04:50 +08:00
CJK_mkp ed7ffbcb13 Merge pull request #77 from 2-2-3-trimethylpentane/beta
fix: correct the URL in the poster
2025-07-10 21:03:56 +08:00
2,2,3-三甲基戊烷 6a8d9db407 Rename ICC-CE宣传图.png to icc ce.png 2025-07-10 11:41:46 +08:00
2,2,3-三甲基戊烷 81140837f1 fix: Delete Images/icc ce.png 2025-07-10 11:41:20 +08:00
2,2,3-三甲基戊烷 70232645a8 fix: correct the URL in the picture 2025-07-10 11:38:51 +08:00
CJK_mkp 4d3b3ef3df Update MW_TouchEvents.cs 2025-07-06 22:07:05 +08:00
CJK_mkp d203314424 Update MW_FloatingBarIcons.cs 2025-07-06 21:09:01 +08:00
CJK_mkp b95da2d8d5 Merge pull request #76 from InkCanvasForClass/beta
ICC CE Beta 1.7.0.4
2025-07-06 16:24:32 +08:00
CJK_mkp a8c5ccda17 更新版本号 2025-07-06 16:23:33 +08:00
CJK_mkp 86b996637c fix:崩溃后重启选项无法修改,部分触摸问题 improve::自动更新 2025-07-06 15:39:37 +08:00
DotteringDoge471 1480d4a14e Update README.md 2025-07-05 23:13:16 +08:00
Hydrogen 8586735ca8 Merge pull request #74 from InkCanvasForClass/beta
Beta
2025-07-05 22:25:30 +08:00
Hydrogen b8013afd64 Merge pull request #73 from InkCanvasForClass/all-contributors/add-CJKmkp
docs: add CJKmkp as a contributor for doc
2025-07-05 22:25:04 +08:00
allcontributors[bot] 812bc83939 docs: update .all-contributorsrc 2025-07-05 14:24:19 +00:00
allcontributors[bot] d6829744bb docs: update README.md 2025-07-05 14:24:18 +00:00
CJK_mkp a7a03544b6 Merge pull request #72 from InkCanvasForClass/main
合并分支
2025-07-03 22:12:55 +08:00
DotteringDoge471 ffa8729d10 Merge pull request #71 from CreeperAWA/issue_template 2025-07-03 04:24:02 +08:00
CreeperAWA ce291d3a77 improve:删除旧的 Bug 报告和功能请求模板,添加新的 .yml 格式模板以方便用户填写 2025-06-30 19:02:32 +08:00
CJK_mkp 297dbad60c Merge pull request #67 from InkCanvasForClass/beta
ICC CE Beta 1.7.0.3 更新合并
2025-06-29 16:42:40 +08:00
CJK_mkp 54982218f5 更新版本号 2025-06-29 16:25:32 +08:00
CJK_mkp c14f4df494 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-06-29 16:00:42 +08:00
CJK_mkp f53f23aa4b improve:更新日志格式 2025-06-29 16:00:37 +08:00
CJK_mkp 10c59fb4b4 Update UpdateLog.md 2025-06-29 15:59:31 +08:00
CJK_mkp a5cffc5ea4 Rename UpdateLog.txt to UpdateLog.md 2025-06-29 15:58:21 +08:00
CJK_mkp 200c299ce7 fix:数位笔相关问题 2025-06-29 15:45:18 +08:00
CJK_mkp 8547276549 fix:手掌擦面积问题 2025-06-29 15:35:28 +08:00
CJK_mkp b09afcf725 fix:修复板擦面积问题 2025-06-29 15:29:25 +08:00
CJK_mkp a2860be7a3 fix:issue #40 2025-06-29 15:15:51 +08:00
CJK_mkp de9d026e3a Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-06-29 15:07:17 +08:00
CJK_mkp 27a1baae7b fix:修复了自动收纳的相关问题 2025-06-29 15:07:13 +08:00
Hydrogen f2584309d1 Merge pull request #66 from InkCanvasForClass/beta
Beta
2025-06-29 15:04:10 +08:00
Hydrogen 831fd09615 Update gesture-enabled icon
Replaces the existing gesture-enabled.png icon with a new version in the Resources/new-icons directory.
2025-06-29 15:01:42 +08:00
Hydrogen e0e22a09c0 Update gesture icon image
Replaces the existing gesture.png icon in Resources/new-icons with a new version.
2025-06-29 14:52:19 +08:00
CJK_mkp 60c6e0632d add:高精度直线拉直 2025-06-29 14:15:20 +08:00
CJK_mkp 6da13c4fc0 add:直接调Ci点名 2025-06-29 13:01:33 +08:00
CJK_mkp ec330aea69 improve:改进日志功能 2025-06-29 12:40:15 +08:00
CJK_mkp 318682b63a fix:版本修复按钮 2025-06-29 12:20:45 +08:00
CJK_mkp dd53d7ff0a Merge pull request #62 from InkCanvasForClass/beta
Update UpdateLog.txt
2025-06-29 12:09:00 +08:00
CJK_mkp b03207d287 Update UpdateLog.txt 2025-06-29 12:06:46 +08:00
CJK_mkp 7db87b7c36 Merge pull request #61 from InkCanvasForClass/beta
ICC CE 1.7.0.0 (ICC CE Beta1.7.0.0)
2025-06-29 12:01:50 +08:00
CJK_mkp 54d0aaca04 improve:改进自动更新 2025-06-29 11:56:38 +08:00
CJK_mkp 352aa886c8 Merge pull request #60 from InkCanvasForClass/main
合并分支
2025-06-29 10:18:20 +08:00
Hydrogen 8fada629f0 Update README.md 2025-06-29 09:21:08 +08:00
Hydrogen 34bb3cd0bd Merge pull request #59 from InkCanvasForClass/beta
Beta
2025-06-29 09:12:42 +08:00
Hydrogen 5555a67422 Merge pull request #58 from InkCanvasForClass/all-contributors/add-Hydro11451
docs: add Hydro11451 as a contributor for doc
2025-06-29 09:11:46 +08:00
allcontributors[bot] 3631d35946 docs: update .all-contributorsrc 2025-06-29 01:04:17 +00:00
allcontributors[bot] 460d4a5ac1 docs: update README.md 2025-06-29 01:04:16 +00:00
Hydrogen b7b5db18fd Update .all-contributorsrc 2025-06-29 09:03:45 +08:00
Hydrogen 58d877af6b Update README.md 2025-06-29 09:02:19 +08:00
Hydrogen ab20e0d8f2 Update .all-contributorsrc 2025-06-29 09:02:00 +08:00
Hydrogen 42e55c51b0 Update .all-contributorsrc 2025-06-29 08:57:00 +08:00
Hydrogen ca82c24c76 Merge pull request #57 from InkCanvasForClass/revert-56-all-contributors/add-Hydro11451
Revert "docs: add Hydro11451 as a contributor for doc"
2025-06-29 08:56:25 +08:00
Hydrogen 66874e7a85 Revert "docs: add Hydro11451 as a contributor for doc" 2025-06-29 08:55:59 +08:00
Hydrogen e365a94f71 Update README.md 2025-06-29 08:52:42 +08:00
Hydrogen 81298e5980 Merge pull request #55 from InkCanvasForClass/beta
Beta
2025-06-29 08:51:59 +08:00
Hydrogen 4eb920553b Merge pull request #56 from InkCanvasForClass/all-contributors/add-Hydro11451
docs: add Hydro11451 as a contributor for doc
2025-06-29 08:51:44 +08:00
Hydrogen 1be5408302 Update .all-contributorsrc 2025-06-29 08:51:04 +08:00
allcontributors[bot] 1e9a7b037a docs: update .all-contributorsrc 2025-06-29 00:49:32 +00:00
allcontributors[bot] e79ed438ef docs: update README.md 2025-06-29 00:49:31 +00:00
Hydrogen ff658409be Update README.md 2025-06-29 08:43:52 +08:00
dubi906w 9c26353fed fix: gitignore and purge. 2025-06-29 01:55:52 +08:00
CJK_mkp 2a3ce9549a Create UpdateLog.txt 2025-06-28 22:09:24 +08:00
CJK_mkp 8093f55b4f Merge pull request #54 from InkCanvasForClass/main
合并分支
2025-06-28 22:03:16 +08:00
Hydrogen 4a46892486 修改ac badge样式 2025-06-28 21:57:41 +08:00
DotteringDoge471 35fafc39a8 Merge pull request #52 from InkCanvasForClass/all-contributors/add-Hydro11451
docs: add Hydro11451 as a contributor for maintenance
2025-06-28 21:11:36 +08:00
allcontributors[bot] 3976970bb5 docs: update .all-contributorsrc 2025-06-28 13:09:17 +00:00
allcontributors[bot] 1a3ab49849 docs: update README.md 2025-06-28 13:09:15 +00:00
CJK_mkp 32d75ae23d Merge pull request #51 from InkCanvasForClass/all-contributors/add-CJKmkp
docs: add CJKmkp as a contributor for maintenance
2025-06-28 21:08:17 +08:00
CJK_mkp 29c6844390 Merge pull request #50 from InkCanvasForClass/beta
Beta
2025-06-28 21:07:20 +08:00
allcontributors[bot] 9c6eee59a8 docs: update .all-contributorsrc 2025-06-28 13:06:42 +00:00
allcontributors[bot] 4ff2be67d3 docs: update README.md 2025-06-28 13:06:41 +00:00
CJK_mkp 47d3eb13ce Update AssemblyInfo.cs 2025-06-28 21:06:26 +08:00
CJK_mkp 5de3043544 Update AssemblyInfo.cs 2025-06-28 21:05:02 +08:00
CJK_mkp 4b1d544c6b Update AutomaticUpdateVersionControl.txt 2025-06-28 21:04:09 +08:00
Hydrogen 6cb8fe3a6e Update README.md 2025-06-28 21:02:13 +08:00
CJK_mkp 1acf2e044e Merge pull request #49 from InkCanvasForClass/beta
合并分支
2025-06-28 21:01:43 +08:00
Hydrogen 9e9e960fa3 修修补补x4
啊啊啊啊啊啊啊
2025-06-28 20:29:08 +08:00
Hydrogen e767873ff1 Create .all-contributorsrc 2025-06-28 20:28:01 +08:00
Hydrogen b57762afed Update README.md
shields.io需要亿点点尝逝
2025-06-28 20:24:06 +08:00
Hydrogen 24526c5a48 Update README.md 2025-06-28 20:23:07 +08:00
Hydrogen ed1b62ec50 Update README.md 2025-06-28 20:22:49 +08:00
Hydrogen 98e727cf55 修修补补x3
《梅开三度》
2025-06-28 20:20:21 +08:00
Hydrogen 6c4ed850b0 修修补补x2
《梅开二度》(这个词是这么用的吧)
2025-06-28 20:17:56 +08:00
CJK_mkp db6b9da945 Merge pull request #48 from InkCanvasForClass/beta
合并分支
2025-06-28 20:17:38 +08:00
Hydrogen 52c930624a 修修补补 2025-06-28 20:16:32 +08:00
Hydrogen b7ddcb5419 增加极为先进的allcontributors 2025-06-28 20:15:36 +08:00
CJK_mkp 34f27c8e8d Update bug_report.md 2025-06-28 20:14:13 +08:00
CJK_mkp 3f7055327b Update MainWindow.xaml 2025-06-28 20:13:20 +08:00
CJK_mkp 51fb13b448 Merge pull request #46 from InkCanvasForClass/beta
合并分支
2025-06-28 09:01:21 +08:00
CJK_mkp 5273990305 Update README.md 2025-06-28 08:59:52 +08:00
CJK_mkp a44badddaa Update README.md 2025-06-28 08:56:02 +08:00
CJK_mkp 3e8d186dc8 Update README.md 2025-06-28 08:53:20 +08:00
CJK_mkp 51f8a0541d Add files via upload 2025-06-28 08:52:34 +08:00
CJK_mkp 808da0a3a7 Merge pull request #45 from InkCanvasForClass/beta
Update README.md
2025-06-27 16:50:50 +08:00
CJK_mkp 1c522840cd Update README.md 2025-06-27 16:50:22 +08:00
CJK_mkp 6fb8506551 Merge pull request #44 from InkCanvasForClass/beta
合并分支
2025-06-27 16:48:57 +08:00
CJK_mkp 7bf0438960 Update MainWindow.xaml 2025-06-27 16:47:13 +08:00
CJK_mkp fe02ec1852 Update HasNewUpdateWindow.xaml 2025-06-27 16:46:05 +08:00
CJK_mkp 4020925af1 Merge pull request #43 from InkCanvasForClass/beta
Update README.md
2025-06-24 17:25:22 +08:00
CJK_mkp 863b42f516 Update README.md 2025-06-24 17:22:23 +08:00
CJK_mkp 7f6b03be27 Merge pull request #42 from InkCanvasForClass/beta
合并分支,修改部分内容
2025-06-24 17:18:07 +08:00
CJK_mkp 880770b718 Merge branch 'main' into beta 2025-06-24 17:17:55 +08:00
CJK_mkp ed74f98919 Update README.md 2025-06-24 17:08:46 +08:00
CJK_mkp 4a1e42d5ee Update AutoUpdateHelper.cs 2025-06-24 16:53:49 +08:00
Hydrogen b21e608eea 增加dc链接
不知怎么回事,dc的在线人数显示不出来(从aiwb inkways的dc标签修改而来)
2025-06-23 15:01:42 +08:00
Hydrogen 671ec2fba6 更新 README 文件,加入贡献指南
在自述文件中添加了一个新部分,提供贡献指南,包括提交代码到测试分支的说明。同时作为构建过程的一部分,更新了生成的文件和缓存文件。
(标题和介绍由Copilot生成并使用巨硬机翻)
2025-06-23 14:48:35 +08:00
Hydrogen 293a89575a 更新README
将新readme同步到beta

Co-Authored-By: DotteringDoge471 <185512682+DotteringDoge471@users.noreply.github.com>
2025-06-23 14:37:33 +08:00
Hydrogen 542812eb74 初步添加重启计数器
1. 防止异常重启死循环
2. 为未来可能的新功能做准备
2025-06-23 14:33:58 +08:00
DotteringDoge471 e44cb715d0 fix(docs): typo. 2025-06-23 02:46:33 +08:00
DotteringDoge471 f7a1a1c851 feat(docs): 更新了README.md。 2025-06-23 02:44:17 +08:00
CJKmkp 757c08cd02 improve:UI改进 2025-06-20 13:51:18 +08:00
CJKmkp 15fcd50151 improve:改进多指触摸操作 2025-06-20 11:23:57 +08:00
CJKmkp 867a747853 improve:改进多指触摸操作 2025-06-19 22:49:11 +08:00
CJKmkp 02dfbb54b6 fix:灵敏度阈值范围过小导致的调节无效 2025-06-19 15:12:40 +08:00
CJK_mkp c42c6c8dfe Merge pull request #37 from awesome-iwb/beta
ICC CE 1.6.7 (ICC CE Beta 1.6.14)
2025-06-19 14:38:19 +08:00
CJKmkp 316d568cbc 更新版本号 2025-06-19 14:33:01 +08:00
CJKmkp 0b72b1f1b3 fix:设置窗口关闭导致的无法点击 2025-06-19 14:32:16 +08:00
CJKmkp 25b52def92 fix:直线拉直及端点吸附 2025-06-19 14:30:24 +08:00
CJK_mkp ab67ea48f0 Merge pull request #36 from awesome-iwb/beta
ICC CE 1.6.6 (ICC CE Beta 1.6.13)
2025-06-19 12:00:20 +08:00
CJK_mkp 912c1b91e4 更新版本号 2025-06-19 11:56:36 +08:00
CJK_mkp d4f1d21634 improve:静默更新 2025-06-19 11:47:16 +08:00
CJK_mkp 6c0e7c2e64 fix:触屏类问题及设置内点击问题 2025-06-19 11:25:15 +08:00
CJK_mkp e054a50b55 Merge pull request #35 from awesome-iwb/beta
Update AutomaticUpdateVersionControl.txt
2025-06-18 23:09:27 +08:00
CJK_mkp ff005e6398 Update AutomaticUpdateVersionControl.txt 2025-06-18 23:08:47 +08:00
CJK_mkp 74cc8e7dda Merge pull request #34 from awesome-iwb/beta
修改版本号
2025-06-18 22:59:52 +08:00
CJK_mkp 254d70a787 修改版本号 2025-06-18 22:58:18 +08:00
CJK_mkp 28b728822c Merge pull request #33 from awesome-iwb/beta
ICC CE 1.6.4(ICC CE Beta1.6.8)
2025-06-18 22:44:30 +08:00
CJK_mkp 5a53471bcd improve:自动更新 2025-06-18 22:42:21 +08:00
CJK_mkp a3fee5d77c improve:自动更新 2025-06-18 22:40:48 +08:00
CJK_mkp 6f961225d7 更新版本号 2025-06-18 22:29:28 +08:00
CJK_mkp 1b0b4f7505 去除了miku 2025-06-18 22:19:34 +08:00
CJK_mkp fac66fafbd fix:墨迹压感问题 2025-06-18 18:31:03 +08:00
CJK_mkp b9d6ac3047 Merge pull request #32 from awesome-iwb/beta
ICC CE 1.6.4(ICC CE Beta1.6.7)
2025-06-18 18:20:56 +08:00
CJK_mkp e7c723de46 fix:进程崩溃判定错误 improve:重新配置了自动更新 2025-06-18 18:16:48 +08:00
CJK_mkp 10629b253c 修复自动更新 2025-06-18 16:12:47 +08:00
CJK_mkp dace6ad486 修改自动更新 2025-06-18 16:08:26 +08:00
CJK_mkp 4c70f35714 Merge pull request #31 from awesome-iwb/beta
ICC CE 1.6.3 (ICC CE Beta1.6.6)
2025-06-18 15:44:17 +08:00
CJK_mkp 7b4d13c16f update:自动更新服务 2025-06-18 15:40:07 +08:00
CJK_mkp 0e561ad24e 修改版本 2025-06-18 15:32:45 +08:00
CJK_mkp d09d8223c9 更新版本号 2025-06-18 15:12:44 +08:00
CJK_mkp 6dafde9735 improve:直线拉直灵敏度设置 2025-06-18 15:10:33 +08:00
CJK_mkp a0f84bf017 Merge pull request #30 from awesome-iwb/beta
ICC CE 1.6.2 (ICC CE Beta  1.6.4)
2025-06-18 13:42:52 +08:00
CJK_mkp 7049c53889 fix:墨迹纠正 2025-06-18 13:37:52 +08:00
CJK_mkp 7750fa799d fix:墨迹纠正 2025-06-18 13:24:50 +08:00
CJK_mkp 5f828736de fix:issue #13 #23 add:设置页面侧边栏 2025-06-18 09:08:38 +08:00
CJK_mkp 869c8ce31b Merge pull request #29 from awesome-iwb/beta
ICC CE 1.6.1 (Beta 1.6.1)
2025-06-17 22:38:48 +08:00
CJK_mkp 0b7b55224f add:直线拉直与端点吸附 2025-06-17 22:37:37 +08:00
CJK_mkp 2a23be44f2 add:有关触屏体验改进的功能 2025-06-17 22:05:22 +08:00
CJK_mkp fa0cbb4d3f fix:修复日志重复输出 2025-06-17 21:45:50 +08:00
CJK_mkp 38bc4decf6 fix:issue #13 #23 2025-06-17 21:24:47 +08:00
CJK_mkp b5ec6e0d79 Merge pull request #28 from awesome-iwb/beta
ICC CE 1.5.2 (Beta 1.5.14)
2025-06-17 19:22:32 +08:00
CJK_mkp 82486c707d fix:issue #5 2025-06-17 19:15:22 +08:00
CJK_mkp 5ff437bed5 improve:PPT联动 2025-06-17 19:09:31 +08:00
CJK_mkp 2f7e0b85c0 fix:issue #3 2025-06-17 19:00:47 +08:00
CJK_mkp c9548af008 fix:issue #3 2025-06-17 18:52:34 +08:00
CJK_mkp b20e4a041f fix:issue #13 #23 2025-06-17 18:45:55 +08:00
CJK_mkp 87f64ccc81 improve:对随机抽功能增加禁用开关 2025-06-17 18:21:14 +08:00
CJK_mkp 58028ea95c fix:issue #13 #23 2025-06-17 13:20:06 +08:00
CJK_mkp 35fa062cc3 improve:尝试使用注册表方法禁用受保护的视图功能(不确定可用性) 2025-06-13 13:44:50 +08:00
CJK_mkp 9de6555519 fix:issue #13 #23 2025-06-13 11:43:50 +08:00
CJK_mkp a9baf47823 fix:修复崩溃自动重启的误判 2025-06-13 09:19:44 +08:00
CJK_mkp a9b64d2899 fix:issue #3 2025-06-12 22:48:25 +08:00
CJK_mkp 4d9fa754e8 Merge pull request #26 from awesome-iwb/beta
ICC CE 1.5.1 (Bate1.5.5)
2025-06-12 19:19:18 +08:00
CJK_mkp 862ac27212 fix:issue #13 #23 2025-06-12 19:14:58 +08:00
CJK_mkp 4ada8b05e7 fix:只读状态下翻页控件无法使用 2025-06-12 18:55:50 +08:00
CJK_mkp d250e83df9 fix:只读状态下无法识别的问题 2025-06-12 14:26:01 +08:00
CJK_mkp 8e8f4256ac fix:issue #13 #23 2025-06-12 11:23:59 +08:00
CJK_mkp d6502251e1 fix:issue #13 #23 2025-06-12 11:21:45 +08:00
CJK_mkp c3159c61ee improve:优化进程崩溃检测机制 2025-06-12 10:34:29 +08:00
CJK_mkp 0e5b31a8e4 add:崩溃后操作 2025-06-12 10:13:47 +08:00
CJK_mkp ad18277223 Merge pull request #25 from awesome-iwb/beta
I CC CE 1.4.8 (Beta1.4.17)
2025-06-12 09:42:18 +08:00
CJK_mkp 661fd21626 fix:issue #5 2025-06-11 21:58:40 +08:00
CJK_mkp d8fd231476 fix:issue #5 2025-06-11 21:27:31 +08:00
CJK_mkp 8d611a22a2 Update README.md 2025-06-11 15:27:59 +08:00
CJK_mkp 6cb8b188ec fix:issue #13 #23 2025-06-11 15:09:34 +08:00
CJK_mkp d1d0e00959 fix:issue #13 #23 2025-06-11 14:57:51 +08:00
CJK_mkp cbc317795e fix:issue #23 #13 2025-06-11 14:31:20 +08:00
CJK_mkp ab88c34abc fix:issue #23 2025-06-11 14:03:04 +08:00
CJK_mkp 18b0556f7a delete:无用的using 2025-06-11 11:23:05 +08:00
CJK_mkp 7bea23005d improve:issue #24 2025-06-11 10:47:04 +08:00
CJK_mkp b46cbcc15f fix:issue #23 2025-06-11 10:26:19 +08:00
CJK_mkp 71ad15ce25 Merge pull request #22 from awesome-iwb/beta
ICC CE 1.4.7 (Beta1.4.14)
2025-06-10 22:15:45 +08:00
CJK_mkp 5a3101a182 修改版本号 2025-06-10 22:10:27 +08:00
CJK_mkp eba888082a Revert "try to fix:issue #3"
This reverts commit 43beffeadd.
2025-06-10 18:00:31 +08:00
CJK_mkp 43beffeadd try to fix:issue #3 2025-06-10 17:50:31 +08:00
CJK_mkp 3645906067 try to fix:issue #13 2025-06-10 17:33:46 +08:00
CJK_mkp 0206273200 improve:Ci点名功能 2025-06-10 17:21:26 +08:00
CJK_mkp 0a50d15da0 fix:PPT放映模式下进退白板翻页按钮不显示 2025-06-10 17:10:53 +08:00
CJK_mkp 85d98c0b70 improve:改进了日志输出 2025-06-10 16:48:45 +08:00
466 changed files with 40732 additions and 150762 deletions
+103
View File
@@ -0,0 +1,103 @@
{
"projectName": "community",
"projectOwner": "InkCanvasForClass",
"files": [
"README.md"
],
"commitType": "docs",
"commitConvention": "angular",
"contributorsPerLine": 7,
"contributors": [
{
"login": "CJKmkp",
"name": "CJK_mkp",
"avatar_url": "https://avatars.githubusercontent.com/u/113243675?v=4",
"profile": "https://github.com/CJKmkp",
"contributions": [
"maintenance",
"doc",
"code"
]
},
{
"login": "Hydro11451",
"name": "Hydrogen",
"avatar_url": "https://avatars.githubusercontent.com/u/214308559?v=4",
"profile": "http://hydro11451.qzz.io",
"contributions": [
"code"
]
},
{
"login": "CreeperAWA",
"name": "CreeperAWA",
"avatar_url": "https://avatars.githubusercontent.com/u/134939494?v=4",
"profile": "https://github.com/CreeperAWA",
"contributions": [
"code"
]
},
{
"login": "2-2-3-trimethylpentane",
"name": "2,2,3-三甲基戊烷",
"avatar_url": "https://avatars.githubusercontent.com/u/141403762?v=4",
"profile": "https://github.com/2-2-3-trimethylpentane",
"contributions": [
"blog",
"doc",
"design"
]
},
{
"login": "Alan-CRL",
"name": "Alan-CRL",
"avatar_url": "https://avatars.githubusercontent.com/u/92425617?v=4",
"profile": "https://github.com/Alan-CRL",
"contributions": [
"code",
"infra",
"doc",
"financial"
]
},
{
"login": "MKStoler1024",
"name": "MKStoler1024",
"avatar_url": "https://avatars.githubusercontent.com/u/158786854?v=4",
"profile": "https://github.com/MKStoler1024",
"contributions": [
"doc",
"code",
"design"
]
},
{
"login": "awesome-iwb",
"name": "Awesome Iwb",
"avatar_url": "https://avatars.githubusercontent.com/u/184760810?v=4",
"profile": "https://github.com/awesome-iwb",
"contributions": [
"doc"
]
},
{
"login": "PrefacedCorg",
"name": "PrefacedCorg",
"avatar_url": "https://avatars.githubusercontent.com/u/129855423?v=4",
"profile": "https://github.com/PrefacedCorg",
"contributions": [
"code",
"design"
]
},
{
"login": "Jursin",
"name": "Jursin",
"avatar_url": "https://avatars.githubusercontent.com/u/127487914?v=4",
"profile": "http://blog.jursin.top",
"contributions": [
"design"
]
}
]
}
-22
View File
@@ -1,22 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
## Description
## Reproduction
## Expected behavior
## Screenshots
## Additional context
+56
View File
@@ -0,0 +1,56 @@
name: Bug 报告 | Bug Report
description: 反馈软件缺陷或异常 | Report a bug to help us improve
labels: [bug]
body:
- type: markdown
attributes:
value: |
感谢你的反馈!请详细填写以下内容,便于我们定位问题。
Thank you for your feedback! Please fill out the following information to help us locate the issue.
- type: input
id: version
attributes:
label: 软件版本 | App Version
description: 可在设置中的“关于”界面查看 | You can find it on the "About" interface in the settings
placeholder: 例如 v1.2.3 | e.g. v1.2.3
validations:
required: true
- type: input
id: os
attributes:
label: 操作系统及版本 | OS & Version
placeholder: 例如 Windows 10 22H2 64位 | e.g. Windows 10 22H2 64bit
validations:
required: true
- type: textarea
id: description
attributes:
label: 问题描述 | Description
description: 简要描述遇到的问题 | Briefly describe the problem
validations:
required: true
- type: textarea
id: steps
attributes:
label: 复现步骤 | Steps to Reproduce
description: 如何复现该问题?如有必要可附截图/录屏 | How to reproduce this bug? Screenshots/recordings if needed
placeholder: |
1.
2.
3.
validations:
required: false
- type: textarea
id: expected
attributes:
label: 期望结果 | Expected Behavior
description: 你期望的正确行为或结果 | What did you expect to happen?
validations:
required: false
- type: textarea
id: extra
attributes:
label: 其他补充信息 | Additional Info
description: 其他相关信息(如日志、配置、特殊环境等)| Any other context, logs, configs, special environment, etc.
validations:
required: false
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
-10
View File
@@ -1,10 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
## Description
@@ -0,0 +1,37 @@
name: 功能请求 | Feature Request
description: 提出你对本项目的功能建议 | Suggest an idea for this project
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
感谢你的建议!请详细描述你的需求。
Thank you for your suggestion! Please describe your needs in detail.
- type: textarea
id: description
attributes:
label: 功能描述 | Description
description: 请描述你希望添加的功能 | Describe the feature you want
validations:
required: true
- type: textarea
id: motivation
attributes:
label: 需求动机 | Motivation
description: 为什么需要这个功能?| Why do you need this feature?
validations:
required: false
- type: textarea
id: design
attributes:
label: 期望设计 | Expected Design
description: (可选)描述或画出你期望的界面或交互 | (Optional) Describe or sketch the expected UI/UX
validations:
required: false
- type: textarea
id: extra
attributes:
label: 其他补充信息 | Additional Info
description: 其他补充说明或建议 | Any other context or suggestions
validations:
required: false
+4 -3
View File
@@ -1,3 +1,4 @@
/Ink Canvas/obj
/Ink Canvas/bin
/.vs
obj/
bin/
.vs/
/Ink Canvas/obj
+1
View File
@@ -2,5 +2,6 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../alpha" vcs="Git" />
</component>
</project>
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,183 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\administrator\\desktop\\icc ce\\icc ce 1.2.5\\ink canvas\\mainwindow_cs\\mw_floatingbaricons.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow_cs\\mw_floatingbaricons.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\privacy.txt||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:privacy.txt||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Manual.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Manual.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\LICENSE||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:LICENSE||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas.sln.DotSettings.user||{FA3CD31E-987B-443A-9B81-186104E8DAC1}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Ink Canvas.sln.DotSettings.user||{FA3CD31E-987B-443A-9B81-186104E8DAC1}"
},
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\administrator\\desktop\\icc ce\\icc ce 1.2.5\\ink canvas\\mainwindow_cs\\mw_ppt.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow_cs\\mw_ppt.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\administrator\\desktop\\icc ce\\icc ce 1.2.5\\ink canvas\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "MW_FloatingBarIcons.cs",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs*",
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs*",
"ViewState": "AgIAAFgHAAAAAAAAAAAgwGcHAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-31T10:49:24.719Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "README.md",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\README.md",
"RelativeDocumentMoniker": "README.md",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\README.md",
"RelativeToolTip": "README.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-31T10:48:22.883Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "MainWindow.xaml",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow.xaml",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow.xaml",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow.xaml",
"RelativeToolTip": "Ink Canvas\\MainWindow.xaml",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|",
"WhenOpened": "2025-05-24T13:22:56.715Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "Microsoft.Common.CurrentVersion.targets",
"DocumentMoniker": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"RelativeDocumentMoniker": "..\\..\\..\\..\\..\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"ToolTip": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"RelativeToolTip": "..\\..\\..\\..\\..\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"ViewState": "AgIAAGsJAAAAAAAAAAAQwIEJAAAEAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003801|",
"WhenOpened": "2025-05-24T13:06:01.053Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "MW_PPT.cs",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"ViewState": "AgIAAFgAAAAAAAAAAAAUwHQAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-24T13:04:47.205Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "README.md",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"RelativeDocumentMoniker": "..\\icc-0610.2.3\\README.md",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"RelativeToolTip": "..\\icc-0610.2.3\\README.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-24T13:04:01.407Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "privacy.txt",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\privacy.txt",
"RelativeDocumentMoniker": "privacy.txt",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\privacy.txt",
"RelativeToolTip": "privacy.txt",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
"WhenOpened": "2025-05-24T13:04:01.337Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Manual.md",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Manual.md",
"RelativeDocumentMoniker": "Manual.md",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Manual.md",
"RelativeToolTip": "Manual.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-24T13:04:00.986Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "LICENSE",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\LICENSE",
"RelativeDocumentMoniker": "LICENSE",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\LICENSE",
"RelativeToolTip": "LICENSE",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
"WhenOpened": "2025-05-24T13:04:00.902Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "Ink Canvas.sln.DotSettings.user",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas.sln.DotSettings.user",
"RelativeDocumentMoniker": "Ink Canvas.sln.DotSettings.user",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas.sln.DotSettings.user",
"RelativeToolTip": "Ink Canvas.sln.DotSettings.user",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003464|",
"WhenOpened": "2025-05-24T13:04:00.792Z",
"EditorCaption": ""
}
]
}
]
}
]
}
-183
View File
@@ -1,183 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\administrator\\desktop\\icc ce\\icc ce 1.2.5\\ink canvas\\mainwindow_cs\\mw_floatingbaricons.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow_cs\\mw_floatingbaricons.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\privacy.txt||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:privacy.txt||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Manual.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Manual.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\LICENSE||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:LICENSE||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas.sln.DotSettings.user||{FA3CD31E-987B-443A-9B81-186104E8DAC1}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Ink Canvas.sln.DotSettings.user||{FA3CD31E-987B-443A-9B81-186104E8DAC1}"
},
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\administrator\\desktop\\icc ce\\icc ce 1.2.5\\ink canvas\\mainwindow_cs\\mw_ppt.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow_cs\\mw_ppt.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\administrator\\desktop\\icc ce\\icc ce 1.2.5\\ink canvas\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "MW_FloatingBarIcons.cs",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"ViewState": "AgIAAFgHAAAAAAAAAAAgwGcHAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-31T10:49:24.719Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "README.md",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\README.md",
"RelativeDocumentMoniker": "README.md",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\README.md",
"RelativeToolTip": "README.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-31T10:48:22.883Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "MainWindow.xaml",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow.xaml",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow.xaml",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow.xaml",
"RelativeToolTip": "Ink Canvas\\MainWindow.xaml",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|",
"WhenOpened": "2025-05-24T13:22:56.715Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "Microsoft.Common.CurrentVersion.targets",
"DocumentMoniker": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"RelativeDocumentMoniker": "..\\..\\..\\..\\..\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"ToolTip": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"RelativeToolTip": "..\\..\\..\\..\\..\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"ViewState": "AgIAAGsJAAAAAAAAAAAQwIEJAAAEAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003801|",
"WhenOpened": "2025-05-24T13:06:01.053Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "MW_PPT.cs",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"ViewState": "AgIAAFgAAAAAAAAAAAAUwHQAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-24T13:04:47.205Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "README.md",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"RelativeDocumentMoniker": "..\\icc-0610.2.3\\README.md",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"RelativeToolTip": "..\\icc-0610.2.3\\README.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-24T13:04:01.407Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "privacy.txt",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\privacy.txt",
"RelativeDocumentMoniker": "privacy.txt",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\privacy.txt",
"RelativeToolTip": "privacy.txt",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
"WhenOpened": "2025-05-24T13:04:01.337Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Manual.md",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Manual.md",
"RelativeDocumentMoniker": "Manual.md",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Manual.md",
"RelativeToolTip": "Manual.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-24T13:04:00.986Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "LICENSE",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\LICENSE",
"RelativeDocumentMoniker": "LICENSE",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\LICENSE",
"RelativeToolTip": "LICENSE",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
"WhenOpened": "2025-05-24T13:04:00.902Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "Ink Canvas.sln.DotSettings.user",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas.sln.DotSettings.user",
"RelativeDocumentMoniker": "Ink Canvas.sln.DotSettings.user",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas.sln.DotSettings.user",
"RelativeToolTip": "Ink Canvas.sln.DotSettings.user",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003464|",
"WhenOpened": "2025-05-24T13:04:00.792Z",
"EditorCaption": ""
}
]
}
]
}
]
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
-6
View File
@@ -1,6 +0,0 @@
{
"ExpandedNodes": [
""
],
"PreviewInSolutionExplorer": false
}
Binary file not shown.
@@ -1,290 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config||{FA3CD31E-987B-443A-9B81-186104E8DAC1}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "System.ValueTuple.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\System.ValueTuple.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\System.ValueTuple.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:48.138Z"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "Settings.json",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Settings.json",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Settings.json",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
"WhenOpened": "2025-05-24T13:02:44.878Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "OSVersionExt.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\OSVersionExt.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\OSVersionExt.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.837Z"
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Office.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Office.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Office.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.774Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "NHotkey.Wpf.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\NHotkey.Wpf.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\NHotkey.Wpf.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.718Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "NHotkey.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\NHotkey.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\NHotkey.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.662Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "Newtonsoft.Json.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Newtonsoft.Json.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Newtonsoft.Json.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.589Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "Microsoft.Office.Interop.PowerPoint.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.932Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "MdXaml.Plugins.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\MdXaml.Plugins.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\MdXaml.Plugins.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.838Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "MdXaml.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\MdXaml.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\MdXaml.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.776Z"
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "iNKORE.UI.WPF.Modern.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.573Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "iNKORE.UI.WPF.Modern.Controls.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.432Z"
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "iNKORE.UI.WPF.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:42.807Z"
},
{
"$type": "Document",
"DocumentIndex": 13,
"Title": "InkCanvasForClass.exe.config",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000601|",
"WhenOpened": "2025-05-24T13:02:27.288Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 14,
"Title": "ICSharpCode.AvalonEdit.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.847Z"
},
{
"$type": "Document",
"DocumentIndex": 15,
"Title": "IAWinFX.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IAWinFX.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IAWinFX.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.816Z"
},
{
"$type": "Document",
"DocumentIndex": 16,
"Title": "IALoader.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IALoader.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IALoader.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.784Z"
},
{
"$type": "Document",
"DocumentIndex": 17,
"Title": "IACore.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IACore.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IACore.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.753Z"
},
{
"$type": "Document",
"DocumentIndex": 18,
"Title": "Hardcodet.NotifyIcon.Wpf.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.113Z"
}
]
}
]
}
]
}
-338
View File
@@ -1,338 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config||{FA3CD31E-987B-443A-9B81-186104E8DAC1}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "icc.png - PNG [256x256, 32 \u4F4D, PNG]",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\icc.png",
"RelativeDocumentMoniker": "icc.png",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\icc.png - PNG [256x256, 32 \u4F4D, PNG]",
"RelativeToolTip": "icc.png - PNG [256x256, 32 \u4F4D, PNG]",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2025-05-24T13:03:45.63Z",
"EditorCaption": " - PNG [256x256, 32 \u4F4D, PNG]"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "AutomaticUpdateVersionControl.txt",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\AutomaticUpdateVersionControl.txt",
"RelativeDocumentMoniker": "AutomaticUpdateVersionControl.txt",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\AutomaticUpdateVersionControl.txt",
"RelativeToolTip": "AutomaticUpdateVersionControl.txt",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
"WhenOpened": "2025-05-24T13:03:45.517Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": ".gitignore",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\.gitignore",
"RelativeDocumentMoniker": ".gitignore",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\.gitignore",
"RelativeToolTip": ".gitignore",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
"WhenOpened": "2025-05-24T13:03:43.13Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "System.ValueTuple.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\System.ValueTuple.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\System.ValueTuple.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:48.138Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "Settings.json",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Settings.json",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Settings.json",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
"WhenOpened": "2025-05-24T13:02:44.878Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "OSVersionExt.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\OSVersionExt.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\OSVersionExt.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.837Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "Office.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Office.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Office.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.774Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "NHotkey.Wpf.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\NHotkey.Wpf.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\NHotkey.Wpf.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.718Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "NHotkey.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\NHotkey.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\NHotkey.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.662Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "Newtonsoft.Json.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Newtonsoft.Json.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Newtonsoft.Json.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.589Z"
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "Microsoft.Office.Interop.PowerPoint.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.932Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "MdXaml.Plugins.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\MdXaml.Plugins.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\MdXaml.Plugins.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.838Z"
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "MdXaml.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\MdXaml.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\MdXaml.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.776Z"
},
{
"$type": "Document",
"DocumentIndex": 13,
"Title": "iNKORE.UI.WPF.Modern.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.573Z"
},
{
"$type": "Document",
"DocumentIndex": 14,
"Title": "iNKORE.UI.WPF.Modern.Controls.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.432Z"
},
{
"$type": "Document",
"DocumentIndex": 15,
"Title": "iNKORE.UI.WPF.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:42.807Z"
},
{
"$type": "Document",
"DocumentIndex": 16,
"Title": "InkCanvasForClass.exe.config",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000601|",
"WhenOpened": "2025-05-24T13:02:27.288Z"
},
{
"$type": "Document",
"DocumentIndex": 17,
"Title": "ICSharpCode.AvalonEdit.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.847Z"
},
{
"$type": "Document",
"DocumentIndex": 18,
"Title": "IAWinFX.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IAWinFX.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IAWinFX.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.816Z"
},
{
"$type": "Document",
"DocumentIndex": 19,
"Title": "IALoader.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IALoader.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IALoader.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.784Z"
},
{
"$type": "Document",
"DocumentIndex": 20,
"Title": "IACore.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IACore.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IACore.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.753Z"
},
{
"$type": "Document",
"DocumentIndex": 21,
"Title": "Hardcodet.NotifyIcon.Wpf.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.113Z"
}
]
}
]
}
]
}
Binary file not shown.
Binary file not shown.
@@ -1,67 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "icc.png",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png",
"RelativeDocumentMoniker": "icc.png",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png",
"RelativeToolTip": "icc.png",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2025-05-24T13:12:49.619Z"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "AutomaticUpdateVersionControl.txt",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt",
"RelativeDocumentMoniker": "AutomaticUpdateVersionControl.txt",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt",
"RelativeToolTip": "AutomaticUpdateVersionControl.txt",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
"WhenOpened": "2025-05-24T13:12:49.575Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": ".gitignore",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore",
"RelativeDocumentMoniker": ".gitignore",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore",
"RelativeToolTip": ".gitignore",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
"WhenOpened": "2025-05-24T13:12:49.025Z"
}
]
}
]
}
]
}
-72
View File
@@ -1,72 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 1,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "icc.png - PNG [1328x1328, 32 \u4F4D, PNG]",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png",
"RelativeDocumentMoniker": "icc.png",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png - PNG [1328x1328, 32 \u4F4D, PNG]",
"RelativeToolTip": "icc.png - PNG [1328x1328, 32 \u4F4D, PNG]",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2025-05-24T13:12:49.619Z",
"EditorCaption": " - PNG [1328x1328, 32 \u4F4D, PNG]"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "AutomaticUpdateVersionControl.txt",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt",
"RelativeDocumentMoniker": "AutomaticUpdateVersionControl.txt",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt",
"RelativeToolTip": "AutomaticUpdateVersionControl.txt",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
"WhenOpened": "2025-05-24T13:12:49.575Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": ".gitignore",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore",
"RelativeDocumentMoniker": ".gitignore",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore",
"RelativeToolTip": ".gitignore",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
"WhenOpened": "2025-05-24T13:12:49.025Z"
}
]
}
]
}
]
}
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
5.0.1
1.7.9.0
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 550 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

+3 -1
View File
@@ -1,2 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/PencilsConfiguration/ActualSeverity/@EntryValue">WARNING</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeInspection/PencilsConfiguration/ActualSeverity/@EntryValue">WARNING</s:String>
<s:String x:Key="/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue">C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\amd64\MSBuild.exe</s:String>
<s:Int64 x:Key="/Default/Environment/Hierarchy/Build/BuildTool/MsbuildVersion/@EntryValue">1114112</s:Int64></wpf:ResourceDictionary>
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -4,7 +4,7 @@
xmlns:local="clr-namespace:Ink_Canvas"
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
StartupUri="MainWindow.xaml">
>
<Application.Resources>
<ResourceDictionary>
<Style TargetType="ui:ScrollViewerEx">
+1370 -29
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
@@ -8,7 +8,7 @@ using System.Windows;
[assembly: AssemblyTitle("InkCanvasForClass")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Dubi906w")]
[assembly: AssemblyCompany("CJK_mkp")]
[assembly: AssemblyProduct("InkCanvasForClass")]
[assembly: AssemblyCopyright("Copyright © HARKOTEK Studio 2024")]
[assembly: AssemblyTrademark("")]
@@ -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("5.0.2.0")]
[assembly: AssemblyFileVersion("5.0.2.0")]
[assembly: AssemblyVersion("1.7.9.0")]
[assembly: AssemblyFileVersion("1.7.9.0")]
+3
View File
@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Costura ExcludeAssemblies="IACore|IALoader|IAWinFX" />
</Weavers>
+176
View File
@@ -0,0 +1,176 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>Obsolete, use UnmanagedWinX86Assemblies instead</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="UnmanagedWinX86Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged X86 (32 bit) assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>Obsolete, use UnmanagedWinX64Assemblies instead.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="UnmanagedWinX64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged X64 (64 bit) assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="UnmanagedWinArm64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged Arm64 (64 bit) assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeReferences" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if runtime assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseRuntimeReferencePaths" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCompression" type="xs:boolean">
<xs:annotation>
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCleanup" type="xs:boolean">
<xs:annotation>
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableEventSubscription" type="xs:boolean">
<xs:annotation>
<xs:documentation>The attach method no longer subscribes to the `AppDomain.AssemblyResolve` (.NET 4.x) and `AssemblyLoadContext.Resolving` (.NET 6.0+) events.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>Obsolete, use UnmanagedWinX86Assemblies instead</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UnmanagedWinX86Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged X86 (32 bit) assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>Obsolete, use UnmanagedWinX64Assemblies instead</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UnmanagedWinX64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged X64 (64 bit) assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UnmanagedWinArm64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged Arm64 (64 bit) assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
@@ -0,0 +1,726 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Threading;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 改进的异步硬件加速墨迹平滑处理器,使用优化的三次贝塞尔曲线拟合
/// </summary>
public class AsyncAdvancedBezierSmoothing
{
private readonly SemaphoreSlim _processingSemaphore;
private readonly ConcurrentDictionary<Stroke, CancellationTokenSource> _processingTasks;
private readonly Dispatcher _uiDispatcher;
public AsyncAdvancedBezierSmoothing(Dispatcher uiDispatcher)
{
_uiDispatcher = uiDispatcher;
_processingSemaphore = new SemaphoreSlim(Environment.ProcessorCount, Environment.ProcessorCount);
_processingTasks = new ConcurrentDictionary<Stroke, CancellationTokenSource>();
}
public double SmoothingStrength { get; set; } = 0.4; // 适中的平滑强度
public double ResampleInterval { get; set; } = 2.5; // 适中的重采样间隔
public int InterpolationSteps { get; set; } = 12; // 增加插值步数提高精度
public bool UseHardwareAcceleration { get; set; } = true;
public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount;
public bool UseAdaptiveInterpolation { get; set; } = true; // 自适应插值
public double CurveTension { get; set; } = 0.3; // 曲线张力参数
/// <summary>
/// 异步平滑笔画
/// </summary>
public async Task<Stroke> SmoothStrokeAsync(Stroke originalStroke,
Action<Stroke, Stroke> onCompleted = null,
CancellationToken cancellationToken = default)
{
if (originalStroke == null || originalStroke.StylusPoints.Count < 2)
return originalStroke;
// 取消之前对同一笔画的处理
if (_processingTasks.TryGetValue(originalStroke, out var existingCts))
{
existingCts.Cancel();
_processingTasks.TryRemove(originalStroke, out _);
}
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
_processingTasks[originalStroke] = cts;
try
{
await _processingSemaphore.WaitAsync(cts.Token);
var smoothedStroke = await Task.Run(() =>
ProcessStrokeInternal(originalStroke, cts.Token), cts.Token);
// 在UI线程上执行回调
if (onCompleted != null && !cts.Token.IsCancellationRequested)
{
await _uiDispatcher.InvokeAsync(() => onCompleted(originalStroke, smoothedStroke));
}
return smoothedStroke;
}
catch (OperationCanceledException)
{
return originalStroke;
}
finally
{
_processingSemaphore.Release();
_processingTasks.TryRemove(originalStroke, out _);
cts.Dispose();
}
}
private Stroke ProcessStrokeInternal(Stroke stroke, CancellationToken cancellationToken)
{
var originalPoints = stroke.StylusPoints.ToArray();
// 如果点数太少,直接返回原始笔画
if (originalPoints.Length < 3)
return stroke;
cancellationToken.ThrowIfCancellationRequested();
// 使用改进的贝塞尔曲线拟合
var smoothedPoints = ApplyImprovedBezierSmoothing(originalPoints);
cancellationToken.ThrowIfCancellationRequested();
// 严格控制点数,避免产生过多点
if (smoothedPoints.Length > originalPoints.Length * 2)
{
// 如果点数增加太多,进行重采样
smoothedPoints = ResampleEquidistantOptimized(smoothedPoints, ResampleInterval);
}
// 最终检查:确保点数不会过多
if (smoothedPoints.Length > originalPoints.Length * 1.5)
{
// 如果仍然太多点,使用原始笔画
return stroke;
}
// 创建平滑后的笔画
var smoothedStroke = new Stroke(new StylusPointCollection(smoothedPoints))
{
DrawingAttributes = stroke.DrawingAttributes.Clone()
};
return smoothedStroke;
}
/// <summary>
/// 改进的贝塞尔曲线平滑处理
/// </summary>
private StylusPoint[] ApplyImprovedBezierSmoothing(StylusPoint[] points)
{
if (points.Length < 4) return points;
var result = new List<StylusPoint>();
// 添加第一个点
result.Add(points[0]);
// 使用非重叠的窗口进行贝塞尔曲线拟合
for (int i = 0; i < points.Length - 3; i += 3) // 每次移动3个点,避免重叠
{
var p0 = points[i];
var p1 = points[Math.Min(i + 1, points.Length - 1)];
var p2 = points[Math.Min(i + 2, points.Length - 1)];
var p3 = points[Math.Min(i + 3, points.Length - 1)];
// 计算改进的控制点
var controlPoints = CalculateImprovedControlPoints(p0, p1, p2, p3);
// 限制插值步数,避免点数爆炸
int steps = Math.Min(UseAdaptiveInterpolation ?
CalculateAdaptiveSteps(p0, p1, p2, p3) : InterpolationSteps, 16);
// 生成贝塞尔曲线点,但跳过第一个点避免重复
for (int j = 1; j <= steps; j++)
{
double t = (double)j / steps;
var bezierPoint = CubicBezierWithControlPoints(controlPoints, t, p0, p3);
result.Add(bezierPoint);
}
}
// 添加最后一个点
result.Add(points[points.Length - 1]);
// 去重和优化点数
return RemoveDuplicatePoints(result.ToArray());
}
/// <summary>
/// 计算改进的控制点
/// </summary>
private (Point cp1, Point cp2) CalculateImprovedControlPoints(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
{
// 计算切线方向
var tangent1 = new Vector(p1.X - p0.X, p1.Y - p0.Y);
var tangent2 = new Vector(p3.X - p2.X, p3.Y - p2.Y);
// 归一化切线
if (tangent1.Length > 0) tangent1.Normalize();
if (tangent2.Length > 0) tangent2.Normalize();
// 计算控制点距离(基于点间距离)
double dist1 = Math.Sqrt((p1.X - p0.X) * (p1.X - p0.X) + (p1.Y - p0.Y) * (p1.Y - p0.Y));
double dist2 = Math.Sqrt((p3.X - p2.X) * (p3.X - p2.X) + (p3.Y - p2.Y) * (p3.Y - p2.Y));
double controlDist1 = dist1 * CurveTension;
double controlDist2 = dist2 * CurveTension;
// 计算控制点
var cp1 = new Point(
p1.X + tangent1.X * controlDist1,
p1.Y + tangent1.Y * controlDist1
);
var cp2 = new Point(
p2.X - tangent2.X * controlDist2,
p2.Y - tangent2.Y * controlDist2
);
return (cp1, cp2);
}
/// <summary>
/// 自适应插值步数计算
/// </summary>
private int CalculateAdaptiveSteps(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
{
// 基于曲线长度和复杂度计算步数
double totalLength = 0;
totalLength += Math.Sqrt((p1.X - p0.X) * (p1.X - p0.X) + (p1.Y - p0.Y) * (p1.Y - p0.Y));
totalLength += Math.Sqrt((p2.X - p1.X) * (p2.X - p1.X) + (p2.Y - p1.Y) * (p2.Y - p1.Y));
totalLength += Math.Sqrt((p3.X - p2.X) * (p3.X - p2.X) + (p3.Y - p2.Y) * (p3.Y - p2.Y));
// 计算曲率(简化版本)
double curvature = CalculateCurvature(p0, p1, p2, p3);
// 基于长度和曲率计算步数
int baseSteps = Math.Max(8, Math.Min(20, (int)(totalLength / 10)));
int curvatureSteps = (int)(curvature * 10);
return Math.Max(InterpolationSteps, Math.Min(24, baseSteps + curvatureSteps));
}
/// <summary>
/// 计算曲率(简化版本)
/// </summary>
private double CalculateCurvature(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
{
// 计算三个向量的角度变化
var v1 = new Vector(p1.X - p0.X, p1.Y - p0.Y);
var v2 = new Vector(p2.X - p1.X, p2.Y - p1.Y);
var v3 = new Vector(p3.X - p2.X, p3.Y - p2.Y);
if (v1.Length == 0 || v2.Length == 0 || v3.Length == 0) return 0;
v1.Normalize();
v2.Normalize();
v3.Normalize();
// 计算角度变化
double angle1 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v1, v2))));
double angle2 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v2, v3))));
return (angle1 + angle2) / Math.PI; // 归一化到0-1
}
/// <summary>
/// 去除重复和过近的点
/// </summary>
private StylusPoint[] RemoveDuplicatePoints(StylusPoint[] points)
{
if (points.Length < 2) return points;
var result = new List<StylusPoint>();
result.Add(points[0]);
double minDistance = ResampleInterval * 0.5; // 最小距离阈值
for (int i = 1; i < points.Length; i++)
{
var lastPoint = result[result.Count - 1];
var currentPoint = points[i];
// 计算距离
double distance = Math.Sqrt(
(currentPoint.X - lastPoint.X) * (currentPoint.X - lastPoint.X) +
(currentPoint.Y - lastPoint.Y) * (currentPoint.Y - lastPoint.Y));
// 如果距离足够大,添加这个点
if (distance >= minDistance)
{
result.Add(currentPoint);
}
}
return result.ToArray();
}
/// <summary>
/// 使用控制点的三次贝塞尔曲线计算
/// </summary>
private StylusPoint CubicBezierWithControlPoints((Point cp1, Point cp2) controlPoints, double t, StylusPoint p0, StylusPoint p3)
{
var p1 = controlPoints.cp1;
var p2 = controlPoints.cp2;
double u = 1 - t;
double tt = t * t;
double uu = u * u;
double uuu = uu * u;
double ttt = tt * t;
// 预计算系数
double c0 = uuu;
double c1 = 3 * uu * t;
double c2 = 3 * u * tt;
double c3 = ttt;
double x = c0 * p0.X + c1 * p1.X + c2 * p2.X + c3 * p3.X;
double y = c0 * p0.Y + c1 * p1.Y + c2 * p2.Y + c3 * p3.Y;
// 插值压力值
float pressure = (float)(p0.PressureFactor * u + p3.PressureFactor * t);
pressure = Math.Max(pressure, 0.1f);
return new StylusPoint(x, y, pressure);
}
/// <summary>
/// 硬件加速的向量化指数平滑
/// </summary>
private StylusPoint[] ApplyExponentialSmoothingVectorized(StylusPoint[] points, double alpha)
{
if (points.Length == 0) return points;
var result = new StylusPoint[points.Length];
result[0] = points[0];
double lastX = points[0].X;
double lastY = points[0].Y;
float lastPressure = points[0].PressureFactor;
double oneMinusAlpha = 1.0 - alpha;
// 向量化处理,减少分支预测失败
for (int i = 1; i < points.Length; i++)
{
var p = points[i];
lastX = alpha * p.X + oneMinusAlpha * lastX;
lastY = alpha * p.Y + oneMinusAlpha * lastY;
lastPressure = (float)(alpha * p.PressureFactor + oneMinusAlpha * lastPressure);
lastPressure = Math.Max(lastPressure, 0.1f); // 避免分支
result[i] = new StylusPoint(lastX, lastY, lastPressure);
}
return result;
}
/// <summary>
/// 优化的等距重采样
/// </summary>
private StylusPoint[] ResampleEquidistantOptimized(StylusPoint[] points, double interval)
{
if (points.Length == 0) return points;
var result = new List<StylusPoint>(points.Length) { points[0] };
double accumulated = 0;
for (int i = 1; i < points.Length; i++)
{
var prev = result[result.Count - 1];
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);
pressure = Math.Max(pressure, 0.1f);
result.Add(new StylusPoint(x, y, pressure));
accumulated = 0;
i--; // 重新处理当前点
}
else
{
accumulated += dist;
}
}
return result.ToArray();
}
/// <summary>
/// 硬件加速的贝塞尔曲线拟合
/// </summary>
private StylusPoint[] SlidingBezierFitHardwareAccelerated(StylusPoint[] points, int window, int steps)
{
if (points.Length < window) return points;
var result = new List<StylusPoint>(points.Length * steps / window);
// 使用并行处理加速计算
var segments = new List<StylusPoint[]>();
Parallel.For(0, points.Length - window + 1, i =>
{
var segmentPoints = new StylusPoint[steps];
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;
segmentPoints[j] = CubicBezierOptimized(p0, p1, p2, p3, t);
}
lock (segments)
{
segments.Add(segmentPoints);
}
});
// 合并结果
foreach (var segment in segments)
{
result.AddRange(segment);
}
result.Add(points[points.Length - 1]);
return result.ToArray();
}
/// <summary>
/// 优化的单线程贝塞尔拟合
/// </summary>
private StylusPoint[] SlidingBezierFitOptimized(StylusPoint[] points, int window, int steps)
{
if (points.Length < window) return points;
var result = new List<StylusPoint>(points.Length * steps / window);
for (int i = 0; i <= points.Length - 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;
result.Add(CubicBezierOptimized(p0, p1, p2, p3, t));
}
}
result.Add(points[points.Length - 1]);
return result.ToArray();
}
/// <summary>
/// 优化的三次贝塞尔曲线计算
/// </summary>
private StylusPoint CubicBezierOptimized(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 c0 = uuu;
double c1 = 3 * uu * t;
double c2 = 3 * u * tt;
double c3 = ttt;
double x = c0 * p0.X + c1 * p1.X + c2 * p2.X + c3 * p3.X;
double y = c0 * p0.Y + c1 * p1.Y + c2 * p2.Y + c3 * p3.Y;
float pressure = (float)(p1.PressureFactor * u + p2.PressureFactor * t);
pressure = Math.Max(pressure, 0.1f);
return new StylusPoint(x, y, pressure);
}
/// <summary>
/// 兼容性方法:传统指数平滑
/// </summary>
private StylusPoint[] ApplyExponentialSmoothing(StylusPoint[] points, double alpha)
{
if (points.Length == 0) return points;
var result = new StylusPoint[points.Length];
result[0] = points[0];
double lastX = points[0].X;
double lastY = points[0].Y;
float lastPressure = points[0].PressureFactor;
for (int i = 1; i < points.Length; 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);
lastPressure = Math.Max(lastPressure, 0.1f);
result[i] = new StylusPoint(lastX, lastY, lastPressure);
}
return result;
}
/// <summary>
/// 取消所有正在进行的处理任务
/// </summary>
public void CancelAllTasks()
{
foreach (var kvp in _processingTasks)
{
kvp.Value.Cancel();
}
_processingTasks.Clear();
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
CancelAllTasks();
_processingSemaphore?.Dispose();
}
}
/// <summary>
/// 原有的同步版本(保持向后兼容)
/// </summary>
public class AdvancedBezierSmoothing
{
public double SmoothingStrength { get; set; } = 0.3;
public double ResampleInterval { get; set; } = 3.0;
public int InterpolationSteps { get; set; } = 8;
public Stroke SmoothStroke(Stroke stroke)
{
if (stroke == null || stroke.StylusPoints.Count < 3)
return stroke;
var originalPoints = stroke.StylusPoints.ToList();
// 简化处理:只进行轻度平滑
var smoothedPoints = ApplyLightExponentialSmoothing(originalPoints, 0.2); // 很轻的平滑
// 检查点数是否合理
if (smoothedPoints.Count > originalPoints.Count * 1.5)
{
return stroke; // 如果点数增加太多,返回原始笔画
}
var smoothedStroke = new Stroke(new StylusPointCollection(smoothedPoints))
{
DrawingAttributes = stroke.DrawingAttributes.Clone()
};
return smoothedStroke;
}
/// <summary>
/// 轻度指数平滑
/// </summary>
private List<StylusPoint> ApplyLightExponentialSmoothing(List<StylusPoint> points, double alpha)
{
var result = new List<StylusPoint>();
if (points.Count == 0) return result;
result.Add(points[0]);
for (int i = 1; i < points.Count; i++)
{
var prev = result[result.Count - 1];
var curr = points[i];
double x = alpha * curr.X + (1 - alpha) * prev.X;
double y = alpha * curr.Y + (1 - alpha) * prev.Y;
float pressure = (float)(alpha * curr.PressureFactor + (1 - alpha) * prev.PressureFactor);
pressure = Math.Max(pressure, 0.1f);
result.Add(new StylusPoint(x, y, pressure));
}
return result;
}
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 = 48) // 从24增加到48
{
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;
}
}
/// <summary>
/// 性能监控器
/// </summary>
public class InkSmoothingPerformanceMonitor
{
private readonly Queue<TimeSpan> _processingTimes = new Queue<TimeSpan>();
private readonly object _lock = new object();
private const int MaxSamples = 100;
public void RecordProcessingTime(TimeSpan time)
{
lock (_lock)
{
_processingTimes.Enqueue(time);
if (_processingTimes.Count > MaxSamples)
_processingTimes.Dequeue();
}
}
public double GetAverageProcessingTimeMs()
{
lock (_lock)
{
return _processingTimes.Count > 0 ?
_processingTimes.Average(t => t.TotalMilliseconds) : 0;
}
}
public double GetMaxProcessingTimeMs()
{
lock (_lock)
{
return _processingTimes.Count > 0 ?
_processingTimes.Max(t => t.TotalMilliseconds) : 0;
}
}
public int GetSampleCount()
{
lock (_lock)
{
return _processingTimes.Count;
}
}
}
}
File diff suppressed because it is too large Load Diff
+20 -20
View File
@@ -1,23 +1,23 @@
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Forms;
using System.Windows.Interop;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 防止窗口进入全屏状态的辅助类
/// </summary>
public static partial class AvoidFullScreenHelper
public static class AvoidFullScreenHelper
{
private static readonly DependencyProperty IsAvoidFullScreenEnabledProperty =
DependencyProperty.RegisterAttached(
"IsAvoidFullScreenEnabled",
typeof(bool),
"IsAvoidFullScreenEnabled",
typeof(bool),
typeof(AvoidFullScreenHelper));
private static bool _isBoardMode = false;
private static bool _isBoardMode;
public static void SetBoardMode(bool isBoardMode)
{
_isBoardMode = isBoardMode;
@@ -121,20 +121,20 @@ namespace Ink_Canvas.Helpers
private static Rect GetWorkingArea(Rect windowRect)
{
// 获取所有显示器
var screens = System.Windows.Forms.Screen.AllScreens;
var screens = Screen.AllScreens;
// 确定窗口主要位于哪个显示器上
System.Windows.Forms.Screen targetScreen = null;
Screen targetScreen = null;
double maxIntersection = 0;
foreach (var screen in screens)
{
var screenRect = new Rect(
screen.WorkingArea.X,
screen.WorkingArea.Y,
screen.WorkingArea.Width,
screen.WorkingArea.X,
screen.WorkingArea.Y,
screen.WorkingArea.Width,
screen.WorkingArea.Height);
var intersection = Rect.Intersect(windowRect, screenRect);
if (intersection.Width * intersection.Height > maxIntersection)
{
@@ -142,11 +142,11 @@ namespace Ink_Canvas.Helpers
targetScreen = screen;
}
}
// 如果没找到,使用主显示器
if (targetScreen == null)
targetScreen = System.Windows.Forms.Screen.PrimaryScreen;
targetScreen = Screen.PrimaryScreen;
return new Rect(
targetScreen.WorkingArea.X,
targetScreen.WorkingArea.Y,
@@ -159,21 +159,21 @@ namespace Ink_Canvas.Helpers
// 调整尺寸以适应工作区域
if (windowRect.Width > workingArea.Width)
windowRect.Width = workingArea.Width;
if (windowRect.Height > workingArea.Height)
windowRect.Height = workingArea.Height;
// 调整位置以确保窗口完全在工作区域内
if (windowRect.Left < workingArea.Left)
windowRect.X = workingArea.Left;
else if (windowRect.Right > workingArea.Right)
windowRect.X = workingArea.Right - windowRect.Width;
if (windowRect.Top < workingArea.Top)
windowRect.Y = workingArea.Top;
else if (windowRect.Bottom > workingArea.Bottom)
windowRect.Y = workingArea.Bottom - windowRect.Height;
return windowRect;
}
}
+29 -47
View File
@@ -9,125 +9,107 @@ namespace Ink_Canvas.Converter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value == true)
if ((bool)value)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value == true)
if ((bool)value)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
return Visibility.Collapsed;
}
}
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Visibility visibility = (Visibility)value;
if (visibility == Visibility.Visible)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
Visibility visibility = (Visibility)value;
if (visibility == Visibility.Visible)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
return Visibility.Visible;
}
}
public class IntNumberToString : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((double)value == 0)
{
return "无限制";
}
else
{
return ((double)value).ToString() + "人";
}
return ((double)value) + "人";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((double)value == 0)
{
return "无限制";
}
else
{
return ((double)value).ToString() + "人";
}
return ((double)value) + "人";
}
}
public class IntNumberToString2 : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((double)value == 0)
{
return "自动截图";
}
else
{
return ((double)value).ToString() + "条";
}
return ((double)value) + "条";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((double)value == 0)
{
return "自动截图";
}
else
{
return ((double)value).ToString() + "条";
}
return ((double)value) + "条";
}
}
public class IsEnabledToOpacityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isChecked = (bool)value;
if (isChecked == true)
if (isChecked)
{
return 1d;
}
else
{
return 0.35;
}
return 0.35;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); }
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); }
}
}
+34 -18
View File
@@ -1,48 +1,64 @@
using System;
using System.IO;
using System.Windows;
namespace Ink_Canvas.Helpers {
internal class DelAutoSavedFiles {
public static void DeleteFilesOlder(string directoryPath, int daysThreshold) {
namespace Ink_Canvas.Helpers
{
internal class DelAutoSavedFiles
{
public static void DeleteFilesOlder(string directoryPath, int daysThreshold)
{
string[] extensionsToDel = { ".icstk", ".png" };
if (Directory.Exists(directoryPath)) {
if (Directory.Exists(directoryPath))
{
// 获取目录中的所有子目录
string[] subDirectories = Directory.GetDirectories(directoryPath, "*", SearchOption.AllDirectories);
foreach (string subDirectory in subDirectories) {
try {
foreach (string subDirectory in subDirectories)
{
try
{
// 获取子目录下的所有文件
string[] files = Directory.GetFiles(subDirectory);
foreach (string filePath in files) {
foreach (string filePath in files)
{
// 获取文件的创建日期
DateTime creationDate = File.GetCreationTime(filePath);
// 获取文件的扩展名
string fileExtension = Path.GetExtension(filePath);
// 如果文件的创建日期早于指定天数且是要删除的扩展名,则删除文件
if (creationDate < DateTime.Now.AddDays(-daysThreshold)) {
if (creationDate < DateTime.Now.AddDays(-daysThreshold))
{
if (Array.Exists(extensionsToDel, ext => ext.Equals(fileExtension, StringComparison.OrdinalIgnoreCase))
|| Path.GetFileName(filePath).Equals("Position", StringComparison.OrdinalIgnoreCase)) {
|| Path.GetFileName(filePath).Equals("Position", StringComparison.OrdinalIgnoreCase))
{
File.Delete(filePath);
}
}
}
} catch (Exception ex) {
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex.ToString(), LogHelper.LogType.Error);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex, LogHelper.LogType.Error);
}
}
try { // 递归删除空文件夹
try
{ // 递归删除空文件夹
DeleteEmptyFolders(directoryPath);
} catch (Exception ex) {
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex.ToString(), LogHelper.LogType.Error);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex, LogHelper.LogType.Error);
}
}
}
private static void DeleteEmptyFolders(string directoryPath) {
foreach (string dir in Directory.GetDirectories(directoryPath)) {
private static void DeleteEmptyFolders(string directoryPath)
{
foreach (string dir in Directory.GetDirectories(directoryPath))
{
DeleteEmptyFolders(dir);
if (Directory.GetFiles(dir).Length == 0 && Directory.GetDirectories(dir).Length == 0) {
if (Directory.GetFiles(dir).Length == 0 && Directory.GetDirectories(dir).Length == 0)
{
Directory.Delete(dir, false);
}
}
+6 -7
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
@@ -18,10 +14,13 @@ namespace Ink_Canvas.Helpers
/// <param name="inv">同步的對象,一般傳入控件,不需要可null</param>
public void DebounceAction(int timeMs, ISynchronizeInvoke inv, Action action)
{
lock (this) {
if (_timerDebounce == null) {
lock (this)
{
if (_timerDebounce == null)
{
_timerDebounce = new Timer(timeMs) { AutoReset = false };
_timerDebounce.Elapsed += (o, e) => {
_timerDebounce.Elapsed += (o, e) =>
{
_timerDebounce.Stop(); _timerDebounce.Close(); _timerDebounce = null;
InvokeAction(action, inv);
};
File diff suppressed because it is too large Load Diff
+9 -21
View File
@@ -1,19 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualBasic;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
namespace Ink_Canvas.Helpers
{
@@ -83,7 +71,7 @@ namespace Ink_Canvas.Helpers
[FieldOffset(8)]
private DateTime date;
[FieldOffset(8)]
private System.Runtime.InteropServices.ComTypes.FILETIME filetime;
private FILETIME filetime;
[FieldOffset(8)]
private Blob blobVal;
@@ -128,7 +116,7 @@ namespace Ink_Canvas.Helpers
case VarEnum.VT_BLOB:
return GetBlob();
}
throw new NotImplementedException("PropVariant " + ve.ToString());
throw new NotImplementedException("PropVariant " + ve);
}
}
}
@@ -157,17 +145,17 @@ namespace Ink_Canvas.Helpers
#region "Interfaces"
[ComImport(), Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport, Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPropertyStore
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetCount([Out(), In()] ref uint cProps);
void GetCount([Out, In] ref uint cProps);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetAt([In()] uint iProp, ref PropertyKey pkey);
void GetAt([In] uint iProp, ref PropertyKey pkey);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetValue([In()] ref PropertyKey key, ref PropVariant pv);
void GetValue([In] ref PropertyKey key, ref PropVariant pv);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void SetValue([In()] ref PropertyKey key, [In()] ref PropVariant pv);
void SetValue([In] ref PropertyKey key, [In] ref PropVariant pv);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void Commit();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
+28 -8
View File
@@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace Ink_Canvas.Helpers
{
@@ -24,7 +25,8 @@ namespace Ink_Canvas.Helpers
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public struct RECT
{
public int Left;
public int Top;
public int Right;
@@ -34,7 +36,8 @@ namespace Ink_Canvas.Helpers
public int Height => Bottom - Top;
}
public static string WindowTitle() {
public static string WindowTitle()
{
IntPtr foregroundWindowHandle = GetForegroundWindow();
const int nChars = 256;
@@ -44,7 +47,8 @@ namespace Ink_Canvas.Helpers
return windowTitle.ToString();
}
public static string WindowClassName() {
public static string WindowClassName()
{
IntPtr foregroundWindowHandle = GetForegroundWindow();
const int nChars = 256;
@@ -54,7 +58,8 @@ namespace Ink_Canvas.Helpers
return className.ToString();
}
public static RECT WindowRect() {
public static RECT WindowRect()
{
IntPtr foregroundWindowHandle = GetForegroundWindow();
RECT windowRect;
@@ -63,15 +68,19 @@ namespace Ink_Canvas.Helpers
return windowRect;
}
public static string ProcessName() {
public static string ProcessName()
{
IntPtr foregroundWindowHandle = GetForegroundWindow();
uint processId;
GetWindowThreadProcessId(foregroundWindowHandle, out processId);
try {
try
{
Process process = Process.GetProcessById((int)processId);
return process.ProcessName;
} catch (ArgumentException) {
}
catch (ArgumentException)
{
// Process with the given ID not found
return "Unknown";
}
@@ -88,10 +97,21 @@ namespace Ink_Canvas.Helpers
Process process = Process.GetProcessById((int)processId);
return process.MainModule.FileName;
}
catch {
catch
{
// Process with the given ID not found
return "Unknown";
}
}
public static double GetTaskbarHeight(Screen screen, double dpiScaleY)
{
// 获取工作区和屏幕高度的差值
var workingArea = screen.WorkingArea;
var bounds = screen.Bounds;
int taskbarHeight = bounds.Height - workingArea.Height;
// 考虑 DPI 缩放
return taskbarHeight / dpiScaleY;
}
}
}
+10 -12
View File
@@ -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
@@ -83,7 +81,7 @@ namespace Ink_Canvas.Helpers
public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
public static IntPtr GetWindowLongPtr(IntPtr hWnd, GetWindowLongFields nIndex) =>
GetWindowLongPtr(hWnd, (int) nIndex);
GetWindowLongPtr(hWnd, (int)nIndex);
public static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
{
@@ -101,7 +99,7 @@ namespace Ink_Canvas.Helpers
public static extern IntPtr GetWindowLongPtr_x64(IntPtr hWnd, int nIndex);
public static IntPtr SetWindowLongPtr(IntPtr hWnd, GetWindowLongFields nIndex, IntPtr dwNewLong) =>
SetWindowLongPtr(hWnd, (int) nIndex, dwNewLong);
SetWindowLongPtr(hWnd, (int)nIndex, dwNewLong);
public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
@@ -266,8 +264,8 @@ namespace Ink_Canvas.Helpers
/// </summary>
public int Width
{
get { return unchecked((int) (Right - Left)); }
set { Right = unchecked((int) (Left + value)); }
get { return unchecked(Right - Left); }
set { Right = unchecked(Left + value); }
}
/// <summary>
@@ -275,8 +273,8 @@ namespace Ink_Canvas.Helpers
/// </summary>
public int Height
{
get { return unchecked((int) (Bottom - Top)); }
set { Bottom = unchecked((int) (Top + value)); }
get { return unchecked(Bottom - Top); }
set { Bottom = unchecked(Top + value); }
}
public bool Equals(Rectangle other)
@@ -298,10 +296,10 @@ namespace Ink_Canvas.Helpers
{
unchecked
{
var hashCode = (int) Left;
hashCode = (hashCode * 397) ^ (int) Top;
hashCode = (hashCode * 397) ^ (int) Right;
hashCode = (hashCode * 397) ^ (int) Bottom;
var hashCode = Left;
hashCode = (hashCode * 397) ^ Top;
hashCode = (hashCode * 397) ^ Right;
hashCode = (hashCode * 397) ^ Bottom;
return hashCode;
}
}
+9 -12
View File
@@ -67,12 +67,12 @@ namespace Ink_Canvas.Helpers
//获取当前窗口的位置大小状态并保存
var placement = new WINDOWPLACEMENT();
placement.Size = (uint) Marshal.SizeOf(placement);
placement.Size = (uint)Marshal.SizeOf(placement);
Win32.User32.GetWindowPlacement(hwnd, ref placement);
window.SetValue(BeforeFullScreenWindowPlacementProperty, placement);
//修改窗口样式
var style = (WindowStyles) Win32.User32.GetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE);
var style = (WindowStyles)Win32.User32.GetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE);
window.SetValue(BeforeFullScreenWindowStyleProperty, style);
//将窗口恢复到还原模式,在有标题栏的情况下最大化模式下无法全屏,
//这里采用还原,不修改标题栏的方式
@@ -81,7 +81,7 @@ namespace Ink_Canvas.Helpers
//去掉WS_MAXIMIZEBOX,禁用最大化,如果最大化会退出全屏
//去掉WS_MAXIMIZE,使窗口变成还原状态,不使用ShowWindow(hwnd, ShowWindowCommands.SW_RESTORE),避免看到窗口变成还原状态这一过程(也避免影响窗口的Visible状态)
style &= (~(WindowStyles.WS_THICKFRAME | WindowStyles.WS_MAXIMIZEBOX | WindowStyles.WS_MAXIMIZE));
Win32.User32.SetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE, (IntPtr) style);
Win32.User32.SetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE, (IntPtr)style);
//禁用 DWM 过渡动画 忽略返回值,若DWM关闭不做处理
Win32.Dwmapi.DwmSetWindowAttribute(hwnd, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 1,
@@ -95,8 +95,8 @@ namespace Ink_Canvas.Helpers
//不能用 placement 的坐标,placement是工作区坐标,不是屏幕坐标。
//使用窗口当前的矩形调用下设置窗口位置和尺寸的方法,让Hook来进行调整窗口位置和尺寸到全屏模式
Win32.User32.SetWindowPos(hwnd, (IntPtr) HwndZOrder.HWND_TOPMOST, rect.Left, rect.Top, rect.Width,
rect.Height, (int) WindowPositionFlags.SWP_NOZORDER);
Win32.User32.SetWindowPos(hwnd, (IntPtr)HwndZOrder.HWND_TOPMOST, rect.Left, rect.Top, rect.Width,
rect.Height, (int)WindowPositionFlags.SWP_NOZORDER);
}
}
}
@@ -139,7 +139,7 @@ namespace Ink_Canvas.Helpers
//不要改变Style里的WS_MAXIMIZE,否则会使窗口变成最大化状态,但是尺寸不对
//也不要设置回Style里的WS_MINIMIZE,否则会导致窗口最小化按钮显示成还原按钮
Win32.User32.SetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE,
(IntPtr) (style & (~(WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE))));
(IntPtr)(style & (~(WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE))));
if ((style & WindowStyles.WS_MINIMIZE) != 0)
{
@@ -201,7 +201,7 @@ namespace Ink_Canvas.Helpers
try
{
//得到WINDOWPOS结构体
var pos = (WindowPosition) Marshal.PtrToStructure(lParam, typeof(WindowPosition));
var pos = (WindowPosition)Marshal.PtrToStructure(lParam, typeof(WindowPosition));
if ((pos.Flags & WindowPositionFlags.SWP_NOMOVE) != 0 &&
(pos.Flags & WindowPositionFlags.SWP_NOSIZE) != 0)
@@ -245,7 +245,7 @@ namespace Ink_Canvas.Helpers
//使用目标矩形获取显示器信息
var monitor = Win32.User32.MonitorFromRect(targetRect, MonitorFlag.MONITOR_DEFAULTTOPRIMARY);
var info = new MonitorInfo();
info.Size = (uint) Marshal.SizeOf(info);
info.Size = (uint)Marshal.SizeOf(info);
if (Win32.User32.GetMonitorInfo(monitor, ref info))
{
//基于显示器信息设置窗口尺寸位置
@@ -278,10 +278,7 @@ namespace Ink_Canvas.Helpers
window.Width = logicalSize.X;
window.Height = logicalSize.Y;
}
else
{
//这个hwnd是前面从Window来的,如果现在他不是Window...... 你信么
}
//这个hwnd是前面从Window来的,如果现在他不是Window...... 你信么
}
//将修改后的结构体拷贝回去
+864
View File
@@ -0,0 +1,864 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Input;
using Newtonsoft.Json;
using NHotkey.Wpf;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 全局快捷键管理器 - 使用NHotkey库实现全局快捷键功能
/// </summary>
public class GlobalHotkeyManager : IDisposable
{
#region Private Fields
private readonly Dictionary<string, HotkeyInfo> _registeredHotkeys;
private readonly MainWindow _mainWindow;
private bool _isDisposed;
private bool _hotkeysShouldBeRegistered = true; // 启动时注册热键
// 配置文件路径
private static readonly string HotkeyConfigFile = Path.Combine(App.RootPath, "HotkeyConfig.json");
#endregion
#region Constructor
public GlobalHotkeyManager(MainWindow mainWindow)
{
_mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow));
_registeredHotkeys = new Dictionary<string, HotkeyInfo>();
_hotkeysShouldBeRegistered = true; // 启动时注册热键
}
#endregion
#region Public Methods
/// <summary>
/// 注册全局快捷键
/// </summary>
/// <param name="hotkeyName">快捷键名称</param>
/// <param name="key">按键</param>
/// <param name="modifiers">修饰键</param>
/// <param name="action">执行动作</param>
/// <returns>是否注册成功</returns>
public bool RegisterHotkey(string hotkeyName, Key key, ModifierKeys modifiers, Action action)
{
try
{
if (_isDisposed)
return false;
// 如果快捷键已存在,先注销
if (_registeredHotkeys.ContainsKey(hotkeyName))
{
UnregisterHotkey(hotkeyName);
}
// 创建快捷键信息
var hotkeyInfo = new HotkeyInfo
{
Name = hotkeyName,
Key = key,
Modifiers = modifiers,
Action = action
};
// 注册快捷键
HotkeyManager.Current.AddOrReplace(hotkeyName, key, modifiers, (sender, e) =>
{
try
{
// 确保在主线程中执行
_mainWindow.Dispatcher.Invoke(() =>
{
action?.Invoke();
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"执行快捷键 {hotkeyName} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
});
_registeredHotkeys[hotkeyName] = hotkeyInfo;
// 成功注册全局快捷键
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注册全局快捷键 {hotkeyName} 失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 注销指定快捷键
/// </summary>
/// <param name="hotkeyName">快捷键名称</param>
/// <returns>是否注销成功</returns>
public bool UnregisterHotkey(string hotkeyName)
{
try
{
if (_isDisposed || !_registeredHotkeys.ContainsKey(hotkeyName))
return false;
HotkeyManager.Current.Remove(hotkeyName);
_registeredHotkeys.Remove(hotkeyName);
// 成功注销全局快捷键
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注销全局快捷键 {hotkeyName} 失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 注销所有快捷键
/// </summary>
public void UnregisterAllHotkeys()
{
try
{
if (_isDisposed)
return;
foreach (var hotkeyName in _registeredHotkeys.Keys)
{
try
{
HotkeyManager.Current.Remove(hotkeyName);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注销快捷键 {hotkeyName} 时出错: {ex.Message}", LogHelper.LogType.Warning);
}
}
_registeredHotkeys.Clear();
// 已注销所有全局快捷键,集合已清空
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注销所有快捷键时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 检查快捷键是否已注册
/// </summary>
/// <param name="hotkeyName">快捷键名称</param>
/// <returns>是否已注册</returns>
public bool IsHotkeyRegistered(string hotkeyName)
{
return _registeredHotkeys.ContainsKey(hotkeyName);
}
/// <summary>
/// 获取已注册的快捷键列表
/// </summary>
/// <returns>快捷键信息列表</returns>
public List<HotkeyInfo> GetRegisteredHotkeys()
{
return new List<HotkeyInfo>(_registeredHotkeys.Values);
}
/// <summary>
/// 获取配置文件中的快捷键信息(不注册,仅用于显示)
/// </summary>
/// <returns>配置文件中的快捷键列表</returns>
public List<HotkeyInfo> GetHotkeysFromConfigFile()
{
try
{
if (!File.Exists(HotkeyConfigFile))
{
LogHelper.WriteLogToFile("快捷键配置文件不存在");
return new List<HotkeyInfo>();
}
// 读取配置文件内容
string jsonContent = File.ReadAllText(HotkeyConfigFile, Encoding.UTF8);
if (string.IsNullOrEmpty(jsonContent))
{
LogHelper.WriteLogToFile("快捷键配置文件为空", LogHelper.LogType.Warning);
return new List<HotkeyInfo>();
}
// 反序列化配置
var config = JsonConvert.DeserializeObject<HotkeyConfig>(jsonContent);
if (config?.Hotkeys == null || config.Hotkeys.Count == 0)
{
LogHelper.WriteLogToFile("快捷键配置为空或格式错误", LogHelper.LogType.Warning);
return new List<HotkeyInfo>();
}
// 转换为HotkeyInfo列表(不注册,仅用于显示)
var hotkeyList = new List<HotkeyInfo>();
foreach (var hotkeyConfig in config.Hotkeys)
{
hotkeyList.Add(new HotkeyInfo
{
Name = hotkeyConfig.Name,
Key = hotkeyConfig.Key,
Modifiers = hotkeyConfig.Modifiers,
Action = null // 不设置动作,仅用于显示
});
}
LogHelper.WriteLogToFile($"从配置文件读取到 {hotkeyList.Count} 个快捷键信息");
return hotkeyList;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从配置文件读取快捷键信息时出错: {ex.Message}", LogHelper.LogType.Error);
return new List<HotkeyInfo>();
}
}
/// <summary>
/// 注册默认快捷键集合
/// </summary>
public void RegisterDefaultHotkeys()
{
try
{
// 开始注册默认快捷键集合
// 基本操作快捷键
RegisterHotkey("Undo", Key.Z, ModifierKeys.Control, () => _mainWindow.SymbolIconUndo_MouseUp(null, null));
RegisterHotkey("Redo", Key.Y, ModifierKeys.Control, () => _mainWindow.SymbolIconRedo_MouseUp(null, null));
RegisterHotkey("Clear", Key.E, ModifierKeys.Control, () => _mainWindow.SymbolIconDelete_MouseUp(null, null));
RegisterHotkey("Paste", Key.V, ModifierKeys.Control, () => _mainWindow.HandleGlobalPaste(null, null));
// 工具切换快捷键
RegisterHotkey("SelectTool", Key.S, ModifierKeys.Alt, () => _mainWindow.SymbolIconSelect_MouseUp(null, null));
RegisterHotkey("DrawTool", Key.D, ModifierKeys.Alt, () => _mainWindow.PenIcon_Click(null, null));
RegisterHotkey("EraserTool", Key.E, ModifierKeys.Alt, () => _mainWindow.EraserIcon_Click(null, null));
RegisterHotkey("BlackboardTool", Key.B, ModifierKeys.Alt, () => _mainWindow.ImageBlackboard_MouseUp(null, null));
RegisterHotkey("QuitDrawTool", Key.Q, ModifierKeys.Alt, () => _mainWindow.KeyChangeToQuitDrawTool(null, null));
// 画笔快捷键 - 使用反射访问penType字段
RegisterHotkey("Pen1", Key.D1, ModifierKeys.Alt, () => SwitchToPenType(0));
RegisterHotkey("Pen2", Key.D2, ModifierKeys.Alt, () => SwitchToPenType(1));
RegisterHotkey("Pen3", Key.D3, ModifierKeys.Alt, () => SwitchToPenType(2));
RegisterHotkey("Pen4", Key.D4, ModifierKeys.Alt, () => SwitchToPenType(3));
RegisterHotkey("Pen5", Key.D5, ModifierKeys.Alt, () => SwitchToPenType(4));
// 功能快捷键
RegisterHotkey("DrawLine", Key.L, ModifierKeys.Alt, () => _mainWindow.BtnDrawLine_Click(null, null));
RegisterHotkey("Screenshot", Key.C, ModifierKeys.Alt, () => _mainWindow.SaveScreenShotToDesktop());
RegisterHotkey("Hide", Key.V, ModifierKeys.Alt, () => _mainWindow.SymbolIconEmoji_MouseUp(null, null));
// 退出快捷键
RegisterHotkey("Exit", Key.Escape, ModifierKeys.None, () => _mainWindow.KeyExit(null, null));
// 已注册默认全局快捷键集合
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注册默认快捷键时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 从配置文件加载快捷键
/// </summary>
public void LoadHotkeysFromSettings()
{
try
{
// 开始从配置文件加载快捷键设置
// 检查是否应该注册快捷键
if (!_hotkeysShouldBeRegistered)
{
// 当前状态不允许注册快捷键,跳过加载
return;
}
// 尝试从配置文件加载
if (LoadHotkeysFromConfigFile())
{
// 成功从配置文件加载快捷键设置
_hotkeysShouldBeRegistered = true;
LogHelper.WriteLogToFile("成功从配置文件加载快捷键设置");
}
else
{
// 如果配置文件不存在或加载失败,使用默认快捷键
if (!File.Exists(HotkeyConfigFile))
{
LogHelper.WriteLogToFile("配置文件不存在,注册默认快捷键");
RegisterDefaultHotkeys();
_hotkeysShouldBeRegistered = true;
}
else
{
LogHelper.WriteLogToFile("配置文件存在但加载失败,回退到默认快捷键", LogHelper.LogType.Warning);
RegisterDefaultHotkeys();
_hotkeysShouldBeRegistered = true;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从设置加载快捷键时出错: {ex.Message}", LogHelper.LogType.Error);
// 出错时不自动使用默认快捷键,保持当前状态
}
}
/// <summary>
/// 保存快捷键配置到设置
/// </summary>
public void SaveHotkeysToSettings()
{
try
{
LogHelper.WriteLogToFile("开始保存快捷键配置到配置文件", LogHelper.LogType.Event);
if (SaveHotkeysToConfigFile())
{
LogHelper.WriteLogToFile("快捷键配置已成功保存到配置文件", LogHelper.LogType.Event);
}
else
{
LogHelper.WriteLogToFile("保存快捷键配置失败", LogHelper.LogType.Error);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存快捷键配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 启用快捷键注册功能
/// 调用此方法后,快捷键将被允许注册
/// </summary>
public void EnableHotkeyRegistration()
{
try
{
if (!_hotkeysShouldBeRegistered)
{
_hotkeysShouldBeRegistered = true;
LogHelper.WriteLogToFile("启用快捷键注册功能");
// 立即加载快捷键设置
LoadHotkeysFromSettings();
}
else
{
LogHelper.WriteLogToFile("快捷键注册功能已经启用,重新加载快捷键设置");
// 即使已经启用,也要重新加载快捷键设置以确保快捷键正常工作
LoadHotkeysFromSettings();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启用快捷键注册功能时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 禁用快捷键注册功能
/// 调用此方法后,快捷键将被注销
/// </summary>
public void DisableHotkeyRegistration()
{
try
{
if (_hotkeysShouldBeRegistered)
{
_hotkeysShouldBeRegistered = false;
LogHelper.WriteLogToFile("禁用快捷键注册功能");
// 注销所有快捷键
UnregisterAllHotkeys();
}
else
{
LogHelper.WriteLogToFile("快捷键注册功能已经禁用");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"禁用快捷键注册功能时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 根据当前工具模式更新快捷键状态
/// 在工具切换时调用此方法
/// </summary>
/// <param name="isMouseMode">是否为鼠标模式(选择模式)</param>
public void UpdateHotkeyStateForToolMode(bool isMouseMode)
{
try
{
if (isMouseMode)
{
// 鼠标模式下禁用快捷键,让键盘操作放行
DisableHotkeyRegistration();
LogHelper.WriteLogToFile("切换到鼠标模式,禁用快捷键以放行键盘操作");
}
else
{
// 非鼠标模式下启用快捷键
EnableHotkeyRegistration();
LogHelper.WriteLogToFile("切换到非鼠标模式,启用快捷键");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新快捷键状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新快捷键配置
/// </summary>
/// <param name="hotkeyName">快捷键名称</param>
/// <param name="key">新按键</param>
/// <param name="modifiers">新修饰键</param>
/// <returns>是否更新成功</returns>
public bool UpdateHotkey(string hotkeyName, Key key, ModifierKeys modifiers)
{
try
{
if (!_registeredHotkeys.ContainsKey(hotkeyName))
{
LogHelper.WriteLogToFile($"快捷键 {hotkeyName} 不存在,无法更新", LogHelper.LogType.Warning);
return false;
}
// 获取原有的动作
var originalAction = _registeredHotkeys[hotkeyName].Action;
// 注销原有快捷键
UnregisterHotkey(hotkeyName);
// 注册新的快捷键
var success = RegisterHotkey(hotkeyName, key, modifiers, originalAction);
if (success)
{
LogHelper.WriteLogToFile($"成功更新快捷键 {hotkeyName}: {modifiers}+{key}", LogHelper.LogType.Event);
// 自动保存配置
SaveHotkeysToSettings();
}
return success;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新快捷键 {hotkeyName} 时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
#endregion
#region Private Helper Methods
/// <summary>
/// 切换到指定笔类型
/// </summary>
/// <param name="penTypeIndex">笔类型索引</param>
private void SwitchToPenType(int penTypeIndex)
{
try
{
// 通过反射访问主窗口的penType字段
var penTypeField = _mainWindow.GetType().GetField("penType",
BindingFlags.NonPublic | BindingFlags.Instance);
if (penTypeField != null)
{
penTypeField.SetValue(_mainWindow, penTypeIndex);
// 调用CheckPenTypeUIState方法更新UI状态
var checkPenTypeMethod = _mainWindow.GetType().GetMethod("CheckPenTypeUIState",
BindingFlags.NonPublic | BindingFlags.Instance);
if (checkPenTypeMethod != null)
{
checkPenTypeMethod.Invoke(_mainWindow, null);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换到笔类型{penTypeIndex}时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 从配置文件加载快捷键设置
/// </summary>
/// <returns>是否加载成功</returns>
private bool LoadHotkeysFromConfigFile()
{
try
{
if (!File.Exists(HotkeyConfigFile))
{
LogHelper.WriteLogToFile($"快捷键配置文件不存在: {HotkeyConfigFile}", LogHelper.LogType.Warning);
return false;
}
// 读取配置文件内容
string jsonContent = File.ReadAllText(HotkeyConfigFile, Encoding.UTF8);
if (string.IsNullOrEmpty(jsonContent))
{
LogHelper.WriteLogToFile("快捷键配置文件为空", LogHelper.LogType.Warning);
return false;
}
// 反序列化配置
var config = JsonConvert.DeserializeObject<HotkeyConfig>(jsonContent);
if (config?.Hotkeys == null || config.Hotkeys.Count == 0)
{
LogHelper.WriteLogToFile("快捷键配置为空或格式错误", LogHelper.LogType.Warning);
return false;
}
// 注册配置中的快捷键
int successCount = 0;
foreach (var hotkeyConfig in config.Hotkeys)
{
try
{
// 根据快捷键名称获取对应的动作
var action = GetActionByName(hotkeyConfig.Name);
if (action != null)
{
if (RegisterHotkey(hotkeyConfig.Name, hotkeyConfig.Key, hotkeyConfig.Modifiers, action))
{
successCount++;
}
}
else
{
LogHelper.WriteLogToFile($"未找到快捷键 {hotkeyConfig.Name} 对应的动作", LogHelper.LogType.Warning);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注册快捷键 {hotkeyConfig.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
LogHelper.WriteLogToFile($"成功加载 {successCount}/{config.Hotkeys.Count} 个快捷键配置", LogHelper.LogType.Event);
if (successCount > 0)
{
_hotkeysShouldBeRegistered = true;
}
return successCount > 0;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从配置文件加载快捷键时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 保存快捷键配置到配置文件
/// </summary>
/// <returns>是否保存成功</returns>
private bool SaveHotkeysToConfigFile()
{
try
{
// 确保配置目录存在
string configDir = Path.GetDirectoryName(HotkeyConfigFile);
if (!Directory.Exists(configDir))
{
Directory.CreateDirectory(configDir);
}
// 创建配置对象
var config = new HotkeyConfig
{
Version = "1.0",
LastModified = DateTime.Now,
Hotkeys = new List<HotkeyConfigItem>()
};
// 添加所有已注册的快捷键
foreach (var hotkey in _registeredHotkeys.Values)
{
config.Hotkeys.Add(new HotkeyConfigItem
{
Name = hotkey.Name,
Key = hotkey.Key,
Modifiers = hotkey.Modifiers
});
}
// 序列化为JSON
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented
};
string jsonContent = JsonConvert.SerializeObject(config, settings);
// 直接写入原文件,覆盖原有内容
File.WriteAllText(HotkeyConfigFile, jsonContent, Encoding.UTF8);
LogHelper.WriteLogToFile($"快捷键配置已保存到: {HotkeyConfigFile}", LogHelper.LogType.Event);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存快捷键配置到配置文件时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 根据快捷键名称获取对应的动作
/// </summary>
/// <param name="hotkeyName">快捷键名称</param>
/// <returns>对应的动作,如果不存在则返回null</returns>
private Action GetActionByName(string hotkeyName)
{
try
{
switch (hotkeyName)
{
case "Undo":
return () => _mainWindow.SymbolIconUndo_MouseUp(null, null);
case "Redo":
return () => _mainWindow.SymbolIconRedo_MouseUp(null, null);
case "Clear":
return () => _mainWindow.SymbolIconDelete_MouseUp(null, null);
case "Paste":
return () => _mainWindow.HandleGlobalPaste(null, null);
case "SelectTool":
return () => _mainWindow.SymbolIconSelect_MouseUp(null, null);
case "DrawTool":
return () => _mainWindow.PenIcon_Click(null, null);
case "EraserTool":
return () => _mainWindow.EraserIcon_Click(null, null);
case "BlackboardTool":
return () => _mainWindow.ImageBlackboard_MouseUp(null, null);
case "QuitDrawTool":
return () => _mainWindow.KeyChangeToQuitDrawTool(null, null);
case "Pen1":
return () => SwitchToPenType(0);
case "Pen2":
return () => SwitchToPenType(1);
case "Pen3":
return () => SwitchToPenType(2);
case "Pen4":
return () => SwitchToPenType(3);
case "Pen5":
return () => SwitchToPenType(4);
case "DrawLine":
return () => _mainWindow.BtnDrawLine_Click(null, null);
case "Screenshot":
return () => _mainWindow.SaveScreenShotToDesktop();
case "Hide":
return () => _mainWindow.SymbolIconEmoji_MouseUp(null, null);
case "Exit":
return () => _mainWindow.KeyExit(null, null);
default:
LogHelper.WriteLogToFile($"未知的快捷键名称: {hotkeyName}", LogHelper.LogType.Warning);
return null;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取快捷键 {hotkeyName} 对应动作时出错: {ex.Message}", LogHelper.LogType.Error);
return null;
}
}
/// <summary>
/// 检查当前是否处于鼠标模式(选择模式)
/// </summary>
/// <returns>如果处于鼠标模式则返回true(不应该注册快捷键),否则返回false(应该注册快捷键)</returns>
private bool IsInSelectMode()
{
try
{
// 通过反射访问主窗口的FloatingbarSelectionBG字段
var floatingbarSelectionBGField = _mainWindow.GetType().GetField("FloatingbarSelectionBG",
BindingFlags.NonPublic | BindingFlags.Instance);
if (floatingbarSelectionBGField != null)
{
var floatingbarSelectionBG = floatingbarSelectionBGField.GetValue(_mainWindow);
if (floatingbarSelectionBG != null)
{
// 检查高光是否可见
var visibilityProperty = floatingbarSelectionBG.GetType().GetProperty("Visibility");
if (visibilityProperty != null)
{
var visibility = visibilityProperty.GetValue(floatingbarSelectionBG);
if (visibility != null && visibility.ToString() == "Hidden")
{
// 高光隐藏,说明没有选中任何工具,此时应该注销快捷键以释放系统快捷键
return true; // 返回true表示应该注销快捷键
}
}
// 通过反射访问Canvas.GetLeft方法来获取高光位置
var canvasType = Type.GetType("System.Windows.Controls.Canvas, PresentationFramework");
if (canvasType != null)
{
var getLeftMethod = canvasType.GetMethod("GetLeft", BindingFlags.Public | BindingFlags.Static);
if (getLeftMethod != null)
{
var leftPosition = getLeftMethod.Invoke(null, new[] { floatingbarSelectionBG });
if (leftPosition != null)
{
var position = Convert.ToDouble(leftPosition);
// 根据高光位置判断当前选中的工具
// 位置计算基于SetFloatingBarHighlightPosition方法中的逻辑
bool isMouseMode;
// 简化判断:如果位置接近0,说明是鼠标模式
// 如果位置接近28,说明是批注模式
// 如果位置更大,说明是其他工具
if (position < 5) // 鼠标模式:marginOffset + (cursorWidth - actualHighlightWidth) / 2 ≈ 0
{
isMouseMode = true;
}
else if (position < 35) // 批注模式:marginOffset + cursorWidth + (penWidth - actualHighlightWidth) / 2 ≈ 28
{
isMouseMode = false;
}
else // 其他工具(橡皮擦、选择等)
{
isMouseMode = false;
}
return isMouseMode;
}
}
}
}
}
// 如果无法获取高光状态,则回退到inkCanvas.EditingMode判断
// 通过反射访问主窗口的inkCanvas字段
var inkCanvasField = _mainWindow.GetType().GetField("inkCanvas",
BindingFlags.NonPublic | BindingFlags.Instance);
if (inkCanvasField != null)
{
var inkCanvas = inkCanvasField.GetValue(_mainWindow);
if (inkCanvas != null)
{
// 通过反射访问inkCanvas的EditingMode属性
var editingModeProperty = inkCanvas.GetType().GetProperty("EditingMode");
if (editingModeProperty != null)
{
var editingMode = editingModeProperty.GetValue(inkCanvas);
if (editingMode != null)
{
// 检查是否为批注模式
var isInkMode = editingMode.ToString().Contains("Ink");
var isSelectMode = editingMode.ToString().Contains("Select");
// 如果是批注模式或选择模式,则应该注册快捷键(返回false)
// 如果是橡皮擦模式或其他模式,则不应该注册快捷键(返回true)
var shouldNotRegisterHotkeys = !isInkMode && !isSelectMode;
return shouldNotRegisterHotkeys;
}
}
}
}
// 如果无法获取任何状态信息,则回退到原来的判断逻辑
// 通过反射访问主窗口的currentMode字段(作为最后的备用方案)
var currentModeField = _mainWindow.GetType().GetField("currentMode",
BindingFlags.NonPublic | BindingFlags.Instance);
if (currentModeField != null)
{
var currentMode = currentModeField.GetValue(_mainWindow);
if (currentMode != null)
{
var modeValue = currentMode.ToString();
// 注意:这里的逻辑需要修正
// currentMode == 0 表示屏幕模式(PPT放映),此时应该允许快捷键
// currentMode == 1 表示黑板/白板模式,此时也应该允许快捷键
var isSelectMode = false; // 修正:所有模式都应该允许快捷键
return isSelectMode;
}
}
return false; // 默认允许快捷键
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检查鼠标模式状态时出错: {ex.Message}", LogHelper.LogType.Warning);
return false; // 出错时默认允许快捷键
}
}
#endregion
#region IDisposable Implementation
public void Dispose()
{
if (!_isDisposed)
{
_isDisposed = true;
}
}
#endregion
#region Nested Classes
/// <summary>
/// 快捷键信息类
/// </summary>
public class HotkeyInfo
{
public string Name { get; set; }
public Key Key { get; set; }
public ModifierKeys Modifiers { get; set; }
public Action Action { get; set; }
public override string ToString()
{
var modifiersText = Modifiers == ModifierKeys.None ? "" : $"{Modifiers}+";
return $"{modifiersText}{Key}";
}
}
/// <summary>
/// 快捷键配置类
/// </summary>
private class HotkeyConfig
{
public string Version { get; set; }
public DateTime LastModified { get; set; }
public List<HotkeyConfigItem> Hotkeys { get; set; }
}
/// <summary>
/// 快捷键配置项类
/// </summary>
private class HotkeyConfigItem
{
public string Name { get; set; }
public Key Key { get; set; }
public ModifierKeys Modifiers { get; set; }
}
#endregion
}
}
@@ -0,0 +1,199 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 硬件加速的墨迹处理器,利用WPF的GPU渲染能力
/// </summary>
public class HardwareAcceleratedInkProcessor
{
private readonly RenderTargetBitmap _renderTarget;
private readonly DrawingVisual _drawingVisual;
private bool _isInitialized;
public HardwareAcceleratedInkProcessor(int width = 1920, int height = 1080)
{
// 创建硬件加速的渲染目标
_renderTarget = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
_drawingVisual = new DrawingVisual();
// 启用硬件加速
RenderOptions.SetBitmapScalingMode(_drawingVisual, BitmapScalingMode.HighQuality);
RenderOptions.SetEdgeMode(_drawingVisual, EdgeMode.Aliased);
_isInitialized = true;
}
/// <summary>
/// 使用GPU加速的贝塞尔曲线平滑
/// </summary>
public async Task<Stroke> SmoothStrokeWithGPU(Stroke originalStroke)
{
if (!_isInitialized || originalStroke == null || originalStroke.StylusPoints.Count < 2)
return originalStroke;
return await Task.Run(() =>
{
try
{
// 使用PathGeometry进行硬件加速的曲线拟合
var pathGeometry = CreateSmoothPathGeometry(originalStroke.StylusPoints);
// 将PathGeometry转换回StylusPoint集合
var smoothedPoints = ConvertPathGeometryToStylusPoints(pathGeometry, originalStroke.StylusPoints);
return new Stroke(new StylusPointCollection(smoothedPoints))
{
DrawingAttributes = originalStroke.DrawingAttributes.Clone()
};
}
catch
{
return originalStroke;
}
});
}
/// <summary>
/// 创建平滑的路径几何体
/// </summary>
private PathGeometry CreateSmoothPathGeometry(StylusPointCollection points)
{
var pathGeometry = new PathGeometry();
var pathFigure = new PathFigure();
if (points.Count < 2) return pathGeometry;
pathFigure.StartPoint = new Point(points[0].X, points[0].Y);
// 使用贝塞尔曲线段创建平滑路径,增加插点密度
for (int i = 0; i < points.Count - 1; i += 2) // 从i+=3改为i+=2,增加插点密度
{
var p1 = i + 1 < points.Count ? new Point(points[i + 1].X, points[i + 1].Y) : pathFigure.StartPoint;
var p2 = i + 2 < points.Count ? new Point(points[i + 2].X, points[i + 2].Y) : p1;
var p3 = i + 3 < points.Count ? new Point(points[i + 3].X, points[i + 3].Y) : p2;
var bezierSegment = new BezierSegment(p1, p2, p3, true);
pathFigure.Segments.Add(bezierSegment);
}
pathGeometry.Figures.Add(pathFigure);
return pathGeometry;
}
/// <summary>
/// 将PathGeometry转换为StylusPoint集合
/// </summary>
private List<StylusPoint> ConvertPathGeometryToStylusPoints(PathGeometry pathGeometry, StylusPointCollection originalPoints)
{
var result = new List<StylusPoint>();
var flattened = pathGeometry.GetFlattenedPathGeometry();
foreach (var figure in flattened.Figures)
{
result.Add(new StylusPoint(figure.StartPoint.X, figure.StartPoint.Y, 0.5f));
foreach (var segment in figure.Segments)
{
if (segment is LineSegment lineSegment)
{
result.Add(new StylusPoint(lineSegment.Point.X, lineSegment.Point.Y, 0.5f));
}
else if (segment is PolyLineSegment polyLineSegment)
{
foreach (var point in polyLineSegment.Points)
{
result.Add(new StylusPoint(point.X, point.Y, 0.5f));
}
}
}
}
// 保持原始压感信息
InterpolatePressure(result, originalPoints);
return result;
}
/// <summary>
/// 插值压感信息
/// </summary>
private void InterpolatePressure(List<StylusPoint> smoothedPoints, StylusPointCollection originalPoints)
{
if (originalPoints.Count == 0 || smoothedPoints.Count == 0) return;
for (int i = 0; i < smoothedPoints.Count; i++)
{
double ratio = (double)i / (smoothedPoints.Count - 1);
int originalIndex = (int)(ratio * (originalPoints.Count - 1));
originalIndex = Math.Max(0, Math.Min(originalIndex, originalPoints.Count - 1));
var point = smoothedPoints[i];
float pressure = originalPoints[originalIndex].PressureFactor;
smoothedPoints[i] = new StylusPoint(point.X, point.Y, Math.Max(pressure, 0.1f));
}
}
/// <summary>
/// 使用GPU加速的并行贝塞尔计算
/// </summary>
public static StylusPoint[] ParallelBezierInterpolation(StylusPoint[] controlPoints, int segments = 32)
{
if (controlPoints.Length < 4) return controlPoints;
var result = new StylusPoint[segments * (controlPoints.Length / 4)];
Parallel.For(0, controlPoints.Length / 4, segmentIndex =>
{
var p0 = controlPoints[segmentIndex * 4];
var p1 = controlPoints[segmentIndex * 4 + 1];
var p2 = controlPoints[segmentIndex * 4 + 2];
var p3 = controlPoints[segmentIndex * 4 + 3];
for (int i = 0; i < segments; i++)
{
double t = (double)i / (segments - 1);
result[segmentIndex * segments + i] = CubicBezierFast(p0, p1, p2, p3, t);
}
});
return result;
}
/// <summary>
/// 优化的三次贝塞尔曲线计算
/// </summary>
private static StylusPoint CubicBezierFast(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 * u + p2.PressureFactor * t);
return new StylusPoint(x, y, Math.Max(pressure, 0.1f));
}
/// <summary>
/// 释放GPU资源
/// </summary>
public void Dispose()
{
_renderTarget?.Clear();
_isInitialized = false;
}
}
}
+167
View File
@@ -0,0 +1,167 @@
using System;
using System.IO;
using System.Reflection;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// IACore DLL自动释放器
/// 在应用启动时自动释放IACore相关的DLL文件到应用程序目录
/// </summary>
public static class IACoreDllExtractor
{
private static readonly string[] RequiredDlls = {
"IACore.dll",
"IALoader.dll",
"IAWinFX.dll"
};
/// <summary>
/// 在应用启动时释放IACore相关DLL
/// </summary>
public static void ExtractIACoreDlls()
{
try
{
string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
LogHelper.WriteLogToFile("开始检查并释放IACore相关DLL文件");
foreach (string dllName in RequiredDlls)
{
string targetPath = Path.Combine(appDirectory, dllName);
// 检查文件是否已存在且有效
if (File.Exists(targetPath) && IsValidDll(targetPath))
{
LogHelper.WriteLogToFile($"{dllName} 已存在且有效,跳过释放");
continue;
}
// 从嵌入资源中释放DLL
if (ExtractDllFromResource(dllName, targetPath))
{
LogHelper.WriteLogToFile($"成功释放 {dllName} 到 {targetPath}");
}
else
{
LogHelper.WriteLogToFile($"警告:无法释放 {dllName},可能影响形状识别功能", LogHelper.LogType.Warning);
}
}
LogHelper.WriteLogToFile("IACore DLL释放检查完成");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"释放IACore DLL时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 从嵌入资源中提取DLL文件
/// </summary>
private static bool ExtractDllFromResource(string dllName, string targetPath)
{
try
{
Assembly assembly = Assembly.GetExecutingAssembly();
string resourceName = $"Ink_Canvas.Resources.IACore.{dllName}";
using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
{
if (resourceStream == null)
{
LogHelper.WriteLogToFile($"未找到嵌入资源: {resourceName}", LogHelper.LogType.Warning);
return false;
}
// 确保目标目录存在
string targetDirectory = Path.GetDirectoryName(targetPath);
if (!Directory.Exists(targetDirectory))
{
Directory.CreateDirectory(targetDirectory);
}
// 写入文件
using (FileStream fileStream = new FileStream(targetPath, FileMode.Create, FileAccess.Write))
{
resourceStream.CopyTo(fileStream);
}
return true;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从资源提取 {dllName} 失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 检查DLL文件是否有效
/// </summary>
private static bool IsValidDll(string filePath)
{
try
{
if (!File.Exists(filePath))
return false;
FileInfo fileInfo = new FileInfo(filePath);
// 检查文件大小(空文件或过小的文件可能无效)
if (fileInfo.Length < 1024) // 小于1KB可能无效
return false;
// 简单检查PE头(DLL文件应该以MZ开头)
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[2];
if (fs.Read(buffer, 0, 2) == 2)
{
return buffer[0] == 0x4D && buffer[1] == 0x5A; // "MZ"
}
}
return false;
}
catch
{
return false;
}
}
/// <summary>
/// 清理释放的DLL文件(可选,在应用退出时调用)
/// </summary>
public static void CleanupExtractedDlls()
{
try
{
string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
foreach (string dllName in RequiredDlls)
{
string filePath = Path.Combine(appDirectory, dllName);
if (File.Exists(filePath))
{
try
{
File.Delete(filePath);
LogHelper.WriteLogToFile($"已清理 {dllName}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理 {dllName} 失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理IACore DLL时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}
@@ -0,0 +1,325 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Ink;
using System.Windows.Input;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 改进的三次贝塞尔曲线平滑算法
/// </summary>
public class ImprovedBezierSmoothing
{
private readonly InkSmoothingConfig _config;
public ImprovedBezierSmoothing(InkSmoothingConfig config = null)
{
_config = config ?? new InkSmoothingConfig();
}
/// <summary>
/// 使用改进的贝塞尔曲线算法平滑笔画
/// </summary>
public Stroke SmoothStroke(Stroke originalStroke)
{
if (originalStroke == null || originalStroke.StylusPoints.Count < 3)
return originalStroke;
var originalPoints = originalStroke.StylusPoints.ToArray();
// 预处理:去除噪声点
var cleanedPoints = RemoveNoisePoints(originalPoints);
// 使用改进的贝塞尔曲线拟合
var smoothedPoints = ApplyCubicBezierSmoothing(cleanedPoints);
// 后处理:重采样和优化
var finalPoints = PostProcessPoints(smoothedPoints);
return new Stroke(new StylusPointCollection(finalPoints))
{
DrawingAttributes = originalStroke.DrawingAttributes.Clone()
};
}
/// <summary>
/// 去除噪声点
/// </summary>
private StylusPoint[] RemoveNoisePoints(StylusPoint[] points)
{
if (points.Length < 3) return points;
var result = new List<StylusPoint> { points[0] };
double minDistance = _config.ResampleInterval * 0.5;
for (int i = 1; i < points.Length - 1; i++)
{
var prev = result[result.Count - 1];
var curr = points[i];
var next = points[i + 1];
// 计算到前一个点的距离
double distToPrev = Math.Sqrt((curr.X - prev.X) * (curr.X - prev.X) +
(curr.Y - prev.Y) * (curr.Y - prev.Y));
// 如果距离太近,跳过这个点
if (distToPrev < minDistance)
continue;
// 检查是否为异常点(与前后点形成锐角)
if (IsOutlierPoint(prev, curr, next))
continue;
result.Add(curr);
}
result.Add(points[points.Length - 1]);
return result.ToArray();
}
/// <summary>
/// 检查是否为异常点
/// </summary>
private bool IsOutlierPoint(StylusPoint prev, StylusPoint curr, StylusPoint next)
{
var v1 = new Vector(curr.X - prev.X, curr.Y - prev.Y);
var v2 = new Vector(next.X - curr.X, next.Y - curr.Y);
if (v1.Length == 0 || v2.Length == 0) return false;
v1.Normalize();
v2.Normalize();
double dotProduct = Vector.Multiply(v1, v2);
double angle = Math.Acos(Math.Max(-1, Math.Min(1, dotProduct)));
// 如果角度小于30度,认为是异常点
return angle < Math.PI / 6;
}
/// <summary>
/// 应用三次贝塞尔曲线平滑
/// </summary>
private StylusPoint[] ApplyCubicBezierSmoothing(StylusPoint[] points)
{
if (points.Length < 4) return points;
var result = new List<StylusPoint>();
result.Add(points[0]);
// 使用滑动窗口进行贝塞尔曲线拟合
for (int i = 0; i <= points.Length - 4; i++)
{
var p0 = points[i];
var p1 = points[i + 1];
var p2 = points[i + 2];
var p3 = points[i + 3];
// 计算控制点
var controlPoints = CalculateOptimalControlPoints(p0, p1, p2, p3);
// 计算插值步数
int steps = CalculateInterpolationSteps(p0, p1, p2, p3);
// 生成贝塞尔曲线点
for (int j = 1; j <= steps; j++)
{
double t = (double)j / steps;
var bezierPoint = CalculateBezierPoint(p0, controlPoints.cp1, controlPoints.cp2, p3, t);
result.Add(bezierPoint);
}
}
result.Add(points[points.Length - 1]);
return result.ToArray();
}
/// <summary>
/// 计算最优控制点
/// </summary>
private (Point cp1, Point cp2) CalculateOptimalControlPoints(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
{
// 计算切线方向
var tangent1 = CalculateTangent(p0, p1, p2);
var tangent2 = CalculateTangent(p1, p2, p3);
// 计算控制点距离
double dist1 = CalculateDistance(p0, p1);
double dist2 = CalculateDistance(p2, p3);
double controlDist1 = dist1 * _config.CurveTension;
double controlDist2 = dist2 * _config.CurveTension;
// 计算控制点
var cp1 = new Point(
p1.X + tangent1.X * controlDist1,
p1.Y + tangent1.Y * controlDist1
);
var cp2 = new Point(
p2.X - tangent2.X * controlDist2,
p2.Y - tangent2.Y * controlDist2
);
return (cp1, cp2);
}
/// <summary>
/// 计算切线方向
/// </summary>
private Vector CalculateTangent(StylusPoint p0, StylusPoint p1, StylusPoint p2)
{
var v1 = new Vector(p1.X - p0.X, p1.Y - p0.Y);
var v2 = new Vector(p2.X - p1.X, p2.Y - p1.Y);
// 如果向量长度为零,返回零向量
if (v1.Length == 0 || v2.Length == 0)
return new Vector(0, 0);
v1.Normalize();
v2.Normalize();
// 返回平均方向
var tangent = (v1 + v2) / 2;
if (tangent.Length > 0)
tangent.Normalize();
return tangent;
}
/// <summary>
/// 计算两点间距离
/// </summary>
private double CalculateDistance(StylusPoint p1, StylusPoint p2)
{
double dx = p2.X - p1.X;
double dy = p2.Y - p1.Y;
return Math.Sqrt(dx * dx + dy * dy);
}
/// <summary>
/// 计算插值步数
/// </summary>
private int CalculateInterpolationSteps(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
{
if (!_config.UseAdaptiveInterpolation)
return _config.InterpolationSteps;
// 计算曲线长度
double totalLength = CalculateDistance(p0, p1) + CalculateDistance(p1, p2) + CalculateDistance(p2, p3);
// 计算曲率
double curvature = CalculateCurvature(p0, p1, p2, p3);
// 基于长度和曲率计算步数
int baseSteps = Math.Max(8, Math.Min(20, (int)(totalLength / 10)));
int curvatureSteps = (int)(curvature * 15);
return Math.Max(_config.InterpolationSteps, Math.Min(30, baseSteps + curvatureSteps));
}
/// <summary>
/// 计算曲率
/// </summary>
private double CalculateCurvature(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
{
var v1 = new Vector(p1.X - p0.X, p1.Y - p0.Y);
var v2 = new Vector(p2.X - p1.X, p2.Y - p1.Y);
var v3 = new Vector(p3.X - p2.X, p3.Y - p2.Y);
if (v1.Length == 0 || v2.Length == 0 || v3.Length == 0) return 0;
v1.Normalize();
v2.Normalize();
v3.Normalize();
// 计算角度变化
double angle1 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v1, v2))));
double angle2 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v2, v3))));
return (angle1 + angle2) / Math.PI; // 归一化到0-1
}
/// <summary>
/// 计算贝塞尔曲线上的点
/// </summary>
private StylusPoint CalculateBezierPoint(StylusPoint p0, Point cp1, Point cp2, 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 c0 = uuu;
double c1 = 3 * uu * t;
double c2 = 3 * u * tt;
double c3 = ttt;
double x = c0 * p0.X + c1 * cp1.X + c2 * cp2.X + c3 * p3.X;
double y = c0 * p0.Y + c1 * cp1.Y + c2 * cp2.Y + c3 * p3.Y;
// 插值压力值
float pressure = (float)(p0.PressureFactor * u + p3.PressureFactor * t);
pressure = Math.Max(pressure, 0.1f);
return new StylusPoint(x, y, pressure);
}
/// <summary>
/// 后处理点集
/// </summary>
private StylusPoint[] PostProcessPoints(StylusPoint[] points)
{
if (points.Length == 0) return points;
// 如果点数过多,进行重采样
if (points.Length > _config.MaxPointsPerStroke)
{
return ResamplePoints(points, _config.ResampleInterval);
}
return points;
}
/// <summary>
/// 重采样点集
/// </summary>
private StylusPoint[] ResamplePoints(StylusPoint[] points, double interval)
{
var result = new List<StylusPoint> { points[0] };
double accumulated = 0;
for (int i = 1; i < points.Length; i++)
{
var prev = result[result.Count - 1];
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);
pressure = Math.Max(pressure, 0.1f);
result.Add(new StylusPoint(x, y, pressure));
accumulated = 0;
i--; // 重新处理当前点
}
else
{
accumulated += dist;
}
}
return result.ToArray();
}
}
}
+832
View File
@@ -0,0 +1,832 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 墨迹渐隐管理器 - 管理墨迹的渐隐动画和状态
/// </summary>
public class InkFadeManager
{
#region Properties
/// <summary>
/// 是否启用墨迹渐隐功能
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 墨迹渐隐时间(毫秒)
/// </summary>
public int FadeTime { get; set; } = 3000;
/// <summary>
/// 渐隐动画持续时间(毫秒)
/// </summary>
public int AnimationDuration { get; set; } = 1000;
#endregion
#region Private Fields
private readonly MainWindow _mainWindow;
private readonly Dispatcher _dispatcher;
private readonly Dictionary<Stroke, DispatcherTimer> _fadeTimers;
private readonly Dictionary<Stroke, UIElement> _strokeVisuals;
private readonly Dictionary<Stroke, Point> _strokeStartPoints;
private readonly Dictionary<Stroke, Point> _strokeEndPoints;
#endregion
#region Constructor
public InkFadeManager(MainWindow mainWindow)
{
_mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow));
_dispatcher = _mainWindow.Dispatcher;
_fadeTimers = new Dictionary<Stroke, DispatcherTimer>();
_strokeVisuals = new Dictionary<Stroke, UIElement>();
_strokeStartPoints = new Dictionary<Stroke, Point>();
_strokeEndPoints = new Dictionary<Stroke, Point>();
}
#endregion
#region Public Methods
/// <summary>
/// 添加需要渐隐的墨迹
/// </summary>
/// <param name="stroke">墨迹对象</param>
/// <param name="startPoint">落笔点</param>
/// <param name="endPoint">抬笔点</param>
public void AddFadingStroke(Stroke stroke, Point startPoint, Point endPoint)
{
if (!IsEnabled || stroke == null)
{
return;
}
try
{
// 记录墨迹的起点和终点
_strokeStartPoints[stroke] = startPoint;
_strokeEndPoints[stroke] = endPoint;
// 创建墨迹的视觉元素(湿墨迹状态)
var strokeVisual = CreateStrokeVisual(stroke);
if (strokeVisual == null) return;
_strokeVisuals[stroke] = strokeVisual;
// 创建定时器,在指定时间后开始渐隐动画
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(FadeTime)
};
timer.Tick += (sender, e) =>
{
StartFadeAnimation(stroke);
timer.Stop();
_fadeTimers.Remove(stroke);
};
_fadeTimers[stroke] = timer;
timer.Start();
// 将视觉元素添加到画布上
_dispatcher.InvokeAsync(() =>
{
try
{
if (_mainWindow.inkCanvas != null)
{
// 将墨迹添加到 inkCanvas 的父容器中,而不是 inkCanvas.Children
// 这样可以避免坐标系统问题
var parent = _mainWindow.inkCanvas.Parent as Panel;
if (parent != null)
{
parent.Children.Add(strokeVisual);
}
else
{
// 如果无法获取父容器,则添加到 inkCanvas.Children
_mainWindow.inkCanvas.Children.Add(strokeVisual);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"添加墨迹视觉元素到画布失败: {ex}", LogHelper.LogType.Error);
}
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"添加渐隐墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 移除墨迹
/// </summary>
/// <param name="stroke">要移除的墨迹</param>
public void RemoveStroke(Stroke stroke)
{
if (stroke == null) return;
try
{
if (_fadeTimers.TryGetValue(stroke, out var timer))
{
timer.Stop();
_fadeTimers.Remove(stroke);
}
if (_strokeVisuals.TryGetValue(stroke, out var visual))
{
_dispatcher.InvokeAsync(() =>
{
try
{
// 从父容器中移除墨迹
var parent = _mainWindow.inkCanvas?.Parent as Panel;
if (parent != null && parent.Children.Contains(visual))
{
parent.Children.Remove(visual);
}
else if (_mainWindow.inkCanvas != null && _mainWindow.inkCanvas.Children.Contains(visual))
{
_mainWindow.inkCanvas.Children.Remove(visual);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从画布移除墨迹视觉元素失败: {ex}", LogHelper.LogType.Error);
}
});
_strokeVisuals.Remove(stroke);
}
_strokeStartPoints.Remove(stroke);
_strokeEndPoints.Remove(stroke);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"移除渐隐墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 清除所有渐隐墨迹
/// </summary>
public void ClearAllFadingStrokes()
{
try
{
foreach (var timer in _fadeTimers.Values)
{
timer.Stop();
}
_fadeTimers.Clear();
_dispatcher.InvokeAsync(() =>
{
try
{
if (_mainWindow.inkCanvas != null)
{
var parent = _mainWindow.inkCanvas.Parent as Panel;
foreach (var visual in _strokeVisuals.Values)
{
if (parent != null && parent.Children.Contains(visual))
{
parent.Children.Remove(visual);
}
else if (_mainWindow.inkCanvas.Children.Contains(visual))
{
_mainWindow.inkCanvas.Children.Remove(visual);
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除所有墨迹视觉元素失败: {ex}", LogHelper.LogType.Error);
}
});
_strokeVisuals.Clear();
_strokeStartPoints.Clear();
_strokeEndPoints.Clear();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除所有渐隐墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新渐隐时间设置
/// </summary>
/// <param name="fadeTime">新的渐隐时间(毫秒)</param>
public void UpdateFadeTime(int fadeTime)
{
FadeTime = fadeTime;
foreach (var kvp in _fadeTimers)
{
var stroke = kvp.Key;
var timer = kvp.Value;
timer.Stop();
timer.Interval = TimeSpan.FromMilliseconds(FadeTime);
timer.Start();
}
}
/// <summary>
/// 启用墨迹渐隐功能
/// </summary>
public void Enable()
{
IsEnabled = true;
LogHelper.WriteLogToFile("墨迹渐隐功能已启用");
}
/// <summary>
/// 禁用墨迹渐隐功能
/// </summary>
public void Disable()
{
IsEnabled = false;
LogHelper.WriteLogToFile("墨迹渐隐功能已禁用");
}
#endregion
#region Private Methods
/// <summary>
/// 创建墨迹的视觉元素
/// </summary>
/// <param name="stroke">墨迹对象</param>
/// <returns>视觉元素</returns>
private UIElement CreateStrokeVisual(Stroke stroke)
{
try
{
// 创建路径几何,使用墨迹的实际位置
var geometry = stroke.GetGeometry();
if (geometry == null)
{
return null;
}
// 获取绘画属性
var drawingAttribs = stroke.DrawingAttributes;
// 创建路径元素,确保使用正确的绘画属性
var path = new Path
{
Data = geometry,
Stroke = new SolidColorBrush(drawingAttribs.Color),
StrokeThickness = drawingAttribs.Width, // 使用原始墨迹的粗细
StrokeStartLineCap = PenLineCap.Round,
StrokeEndLineCap = PenLineCap.Round,
StrokeLineJoin = PenLineJoin.Round,
Fill = drawingAttribs.IsHighlighter ? new SolidColorBrush(drawingAttribs.Color) : null, // 高亮笔需要填充
Opacity = 0.95, // 初始透明度更高,显得更自然
// 优化渲染质量
UseLayoutRounding = false,
SnapsToDevicePixels = false
};
// 如果是高亮笔,调整透明度和混合模式
if (drawingAttribs.IsHighlighter)
{
path.Opacity = 0.4; // 高亮笔初始透明度更低,更符合荧光笔特性
// 为高亮笔添加特殊的混合效果
// 使用更柔和的笔触样式
path.StrokeStartLineCap = PenLineCap.Flat;
path.StrokeEndLineCap = PenLineCap.Flat;
path.StrokeLineJoin = PenLineJoin.Miter;
// 高亮笔通常需要更宽的笔触来覆盖下面的内容
if (drawingAttribs.Width < 20)
{
path.StrokeThickness = Math.Max(drawingAttribs.Width * 1.5, 20);
}
}
// 不设置任何变换,保持墨迹原有粗细
var bounds = geometry.Bounds;
// 设置墨迹的初始位置
System.Windows.Controls.Canvas.SetLeft(path, bounds.Left);
System.Windows.Controls.Canvas.SetTop(path, bounds.Top);
return path;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// 开始渐隐动画
/// </summary>
/// <param name="stroke">要渐隐的墨迹</param>
private void StartFadeAnimation(Stroke stroke)
{
if (!_strokeVisuals.TryGetValue(stroke, out var visual)) return;
try
{
_dispatcher.InvokeAsync(() =>
{
// 获取当前透明度和判断是否为高亮笔
var currentOpacity = visual.Opacity;
var isHighlighter = stroke.DrawingAttributes.IsHighlighter;
// 根据墨迹类型选择不同的动画效果
if (isHighlighter)
{
StartHighlighterFadeAnimation(visual, stroke, currentOpacity);
}
else
{
StartNormalStrokeFadeAnimation(visual, stroke, currentOpacity);
}
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"开始渐隐动画失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 开始普通墨迹的渐隐动画
/// </summary>
private void StartNormalStrokeFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity)
{
try
{
StartProgressiveFadeAnimation(visual, stroke, currentOpacity, AnimationDuration);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"开始普通墨迹渐隐动画失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 开始高亮笔的渐隐动画
/// </summary>
private void StartHighlighterFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity)
{
try
{
StartProgressiveFadeAnimation(visual, stroke, currentOpacity, (int)(AnimationDuration * 1.5));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"开始高亮笔渐隐动画失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 渐进式渐隐动画 - 从起点到终点逐渐消失
/// </summary>
private void StartProgressiveFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity, int duration)
{
try
{
// 确保所有墨迹都能显示动画,包括短墨迹
if (stroke.StylusPoints.Count < 2)
{
// 只有1个点的墨迹也使用分段动画,确保视觉效果
CreateSegmentedStroke(visual, stroke, currentOpacity, duration);
return;
}
// 将墨迹分段并创建多个 Path
CreateSegmentedStroke(visual, stroke, currentOpacity, duration);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"渐进式渐隐动画失败: {ex}", LogHelper.LogType.Error);
// 失败时回退到简单动画
StartSimpleFadeAnimation(visual, stroke, currentOpacity, duration);
}
}
/// <summary>
/// 创建分段墨迹并开始渐进消失
/// </summary>
private void CreateSegmentedStroke(UIElement originalVisual, Stroke stroke, double opacity, int duration)
{
try
{
var stylusPoints = stroke.StylusPoints;
var totalPoints = stylusPoints.Count;
// 分段算法 - 确保所有墨迹都有足够的动画效果
var strokeLength = CalculateStrokeLength(stylusPoints);
var segmentCount = CalculateOptimalSegmentCount(totalPoints, strokeLength);
// 强制最小分段数量,确保短墨迹也有动画效果
segmentCount = Math.Max(segmentCount, 4);
var pointsPerSegment = Math.Max(1, totalPoints / segmentCount);
// 隐藏原始视觉元素
originalVisual.Visibility = Visibility.Hidden;
var segments = new List<UIElement>();
var parent = _mainWindow.inkCanvas?.Parent as Panel;
if (parent == null)
{
// 如果父容器不是Panel,直接使用InkCanvas
parent = null; // 稍后会检查并使用InkCanvas.Children
}
// 创建各个分段 - 确保短墨迹也能正确分段
for (int i = 0; i < segmentCount; i++)
{
var startIndex = i * pointsPerSegment;
var endIndex = (i == segmentCount - 1) ? totalPoints - 1 : (i + 1) * pointsPerSegment;
// 确保有足够的点来创建分段,对于短墨迹特殊处理
if (endIndex <= startIndex && totalPoints > 1)
{
// 短墨迹:每个点作为一个分段
startIndex = i;
endIndex = Math.Min(i + 1, totalPoints - 1);
}
// 为每个分段添加重叠,确保连接处平滑
var overlap = Math.Max(1, pointsPerSegment / 6); // 15%的重叠,平衡平滑与速度
var actualStartIndex = Math.Max(0, startIndex - overlap);
var actualEndIndex = Math.Min(totalPoints - 1, endIndex + overlap);
var segment = CreateStrokeSegment(stroke, actualStartIndex, actualEndIndex, opacity);
if (segment != null)
{
segments.Add(segment);
if (parent != null)
{
parent.Children.Add(segment);
}
else if (_mainWindow.inkCanvas != null)
{
_mainWindow.inkCanvas.Children.Add(segment);
}
}
}
// 开始分段渐隐动画
StartSegmentedFadeAnimation(segments, stroke, originalVisual, duration);
}
catch (Exception)
{
StartSimpleFadeAnimation(originalVisual, stroke, opacity, duration);
}
}
/// <summary>
/// 创建墨迹分段
/// </summary>
private UIElement CreateStrokeSegment(Stroke originalStroke, int startIndex, int endIndex, double opacity)
{
try
{
// 创建分段的 StylusPoint 集合
var segmentPoints = new StylusPointCollection();
for (int i = startIndex; i <= endIndex && i < originalStroke.StylusPoints.Count; i++)
{
segmentPoints.Add(originalStroke.StylusPoints[i]);
}
if (segmentPoints.Count < 2) return null;
// 创建分段墨迹
var segmentStroke = new Stroke(segmentPoints)
{
DrawingAttributes = originalStroke.DrawingAttributes.Clone()
};
// 创建分段的视觉元素
var geometry = segmentStroke.GetGeometry();
if (geometry == null) return null;
var drawingAttribs = segmentStroke.DrawingAttributes;
var path = new Path
{
Data = geometry,
Stroke = new SolidColorBrush(drawingAttribs.Color),
StrokeThickness = drawingAttribs.Width,
StrokeStartLineCap = drawingAttribs.IsHighlighter ? PenLineCap.Flat : PenLineCap.Round,
StrokeEndLineCap = drawingAttribs.IsHighlighter ? PenLineCap.Flat : PenLineCap.Round,
StrokeLineJoin = drawingAttribs.IsHighlighter ? PenLineJoin.Miter : PenLineJoin.Round,
Fill = drawingAttribs.IsHighlighter ? new SolidColorBrush(drawingAttribs.Color) : null,
Opacity = opacity,
UseLayoutRounding = false,
SnapsToDevicePixels = false
};
// 设置位置
var bounds = geometry.Bounds;
System.Windows.Controls.Canvas.SetLeft(path, bounds.Left);
System.Windows.Controls.Canvas.SetTop(path, bounds.Top);
return path;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// 开始分段渐隐动画
/// </summary>
private void StartSegmentedFadeAnimation(List<UIElement> segments, Stroke originalStroke, UIElement originalVisual, int totalDuration)
{
try
{
// 动画时序算法
var segmentDuration = CalculateOptimalSegmentDuration(totalDuration, segments.Count);
var animationCurve = CreateAppleStyleAnimationCurve(segments.Count, totalDuration);
// 跟踪动画完成状态
var completedSegments = new HashSet<UIElement>();
var totalSegments = segments.Count;
// 渐隐效果 - 使用自然的动画曲线
for (int i = 0; i < segments.Count; i++)
{
var segment = segments[i];
// 使用预计算的动画曲线获取延迟时间
var delay = animationCurve[i];
// 使用定时器延迟启动每个分段的动画
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(delay)
};
int segmentIndex = i; // 捕获当前索引
timer.Tick += (sender, e) =>
{
StartSingleSegmentFadeAnimation(segment, segmentDuration, () =>
{
// 动画完成回调
lock (completedSegments)
{
completedSegments.Add(segment);
// 检查是否所有分段都完成了
if (completedSegments.Count >= totalSegments)
{
CleanupSegmentedAnimation(segments, originalStroke, originalVisual);
}
}
});
timer.Stop();
};
timer.Start();
}
// 设置一个安全超时定时器,防止无限等待
var safetyTimeout = totalDuration + (segments.Count * segmentDuration) + 1200; // 额外1.2秒缓冲,确保动画完整
var safetyTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(safetyTimeout)
};
safetyTimer.Tick += (sender, e) =>
{
CleanupSegmentedAnimation(segments, originalStroke, originalVisual);
safetyTimer.Stop();
};
safetyTimer.Start();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"分段渐隐动画失败: {ex}", LogHelper.LogType.Error);
CleanupSegmentedAnimation(segments, originalStroke, originalVisual);
}
}
/// <summary>
/// 单个分段的渐隐动画
/// </summary>
private void StartSingleSegmentFadeAnimation(UIElement segment, int duration, Action onCompleted = null)
{
try
{
// 只使用透明度动画,保持墨迹原有粗细
var fadeAnimation = new DoubleAnimation
{
From = segment.Opacity,
To = 0.0,
Duration = TimeSpan.FromMilliseconds(duration),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } // 更平滑的缓动
};
// 添加动画完成事件
if (onCompleted != null)
{
fadeAnimation.Completed += (sender, e) =>
{
onCompleted?.Invoke();
};
}
// 只应用透明度动画,不改变墨迹大小
segment.BeginAnimation(UIElement.OpacityProperty, fadeAnimation);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"单个分段渐隐动画失败: {ex}", LogHelper.LogType.Error);
// 即使失败也要调用完成回调
onCompleted?.Invoke();
}
}
/// <summary>
/// 清理分段动画
/// </summary>
private void CleanupSegmentedAnimation(List<UIElement> segments, Stroke originalStroke, UIElement originalVisual)
{
try
{
// 移除所有分段
var parent = _mainWindow.inkCanvas?.Parent as Panel;
foreach (var segment in segments)
{
if (parent != null && parent.Children.Contains(segment))
{
parent.Children.Remove(segment);
}
else if (_mainWindow.inkCanvas != null && _mainWindow.inkCanvas.Children.Contains(segment))
{
_mainWindow.inkCanvas.Children.Remove(segment);
}
}
// 清理原始墨迹
OnAnimationCompleted(originalVisual, originalStroke);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理分段动画失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 简单渐隐动画(备用方案)
/// </summary>
private void StartSimpleFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity, int duration)
{
try
{
var fadeAnimation = new DoubleAnimation
{
From = currentOpacity,
To = 0.0,
Duration = TimeSpan.FromMilliseconds(duration),
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseIn }
};
fadeAnimation.Completed += (sender, e) => OnAnimationCompleted(visual, stroke);
visual.BeginAnimation(UIElement.OpacityProperty, fadeAnimation);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"简单渐隐动画失败: {ex}", LogHelper.LogType.Error);
OnAnimationCompleted(visual, stroke);
}
}
/// <summary>
/// 计算墨迹的实际长度
/// </summary>
private double CalculateStrokeLength(StylusPointCollection points)
{
if (points.Count < 2) return 0;
double totalLength = 0;
for (int i = 1; i < points.Count; i++)
{
var p1 = points[i - 1].ToPoint();
var p2 = points[i].ToPoint();
totalLength += Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
}
return totalLength;
}
/// <summary>
/// 根据墨迹特性计算最优分段数量 - 平衡速度与完整性
/// </summary>
private int CalculateOptimalSegmentCount(int pointCount, double strokeLength)
{
// 平衡速度与完整性,确保动画效果的同时提高速度
const double PIXELS_PER_SEGMENT = 12.0; // 每段适中长度,平衡效果与速度
const int MIN_SEGMENTS = 5; // 适当的最小分段数,确保动画效果
const int MAX_SEGMENTS = 100; // 适中的最大分段数,平衡性能与效果
// 根据长度计算基础分段数
var lengthBasedSegments = Math.Max(MIN_SEGMENTS, (int)(strokeLength / PIXELS_PER_SEGMENT));
// 根据点密度调整,平衡效果与速度
var density = pointCount > 0 ? strokeLength / pointCount : 1;
var densityFactor = Math.Max(0.4, Math.Min(2.5, density / 1.8));
var finalSegments = (int)(lengthBasedSegments * densityFactor);
// 对于短墨迹,确保至少有4个分段
if (pointCount <= 5)
{
finalSegments = Math.Max(finalSegments, 4);
}
// 限制在合理范围内
return Math.Min(MAX_SEGMENTS, Math.Max(MIN_SEGMENTS, finalSegments));
}
/// <summary>
/// 计算最优的单段动画持续时间 - 平衡速度与完整性
/// </summary>
private int CalculateOptimalSegmentDuration(int totalDuration, int segmentCount)
{
// 平衡速度与动画完整性
var baseDuration = totalDuration / Math.Max(segmentCount, 1);
var minDuration = 150; // 每段最少150ms,确保动画完整显示
var maxDuration = 500; // 每段最多500ms,平衡速度与完整性
return Math.Max(minDuration, Math.Min(maxDuration, baseDuration));
}
/// <summary>
/// 创建优化的动画时间曲线 - 平衡速度与完整性
/// </summary>
private int[] CreateAppleStyleAnimationCurve(int segmentCount, int totalDuration)
{
var curve = new int[segmentCount];
// 平衡速度与完整性,确保动画有足够时间播放
var availableTime = totalDuration * 0.6; // 使用60%的总时间,给动画留足够缓冲
var delayBetweenSegments = Math.Max(60, availableTime / Math.Max(segmentCount, 1));
for (int i = 0; i < segmentCount; i++)
{
// 线性延迟,确保每个分段都有足够时间
curve[i] = (int)(i * delayBetweenSegments);
}
return curve;
}
/// <summary>
/// 动画完成后的统一处理
/// </summary>
private void OnAnimationCompleted(UIElement visual, Stroke stroke)
{
try
{
// 从父容器中移除墨迹
var parent = _mainWindow.inkCanvas?.Parent as Panel;
if (parent != null && parent.Children.Contains(visual))
{
parent.Children.Remove(visual);
}
else if (_mainWindow.inkCanvas != null && _mainWindow.inkCanvas.Children.Contains(visual))
{
_mainWindow.inkCanvas.Children.Remove(visual);
}
RemoveStroke(stroke);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"渐隐动画完成后清理墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
#endregion
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ namespace Ink_Canvas.Helpers
var analyzer = new InkAnalyzer();
analyzer.AddStrokes(strokes);
analyzer.SetStrokesType(strokes, System.Windows.Ink.StrokeType.Drawing);
analyzer.SetStrokesType(strokes, StrokeType.Drawing);
AnalysisAlternate analysisAlternate = null;
int strokesCount = strokes.Count;
+155
View File
@@ -0,0 +1,155 @@
using System;
using System.Diagnostics;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 墨迹平滑配置类
/// </summary>
public class InkSmoothingConfig
{
// 基本平滑参数
public double SmoothingStrength { get; set; } = 0.4;
public double ResampleInterval { get; set; } = 2.5;
public int InterpolationSteps { get; set; } = 12;
// 贝塞尔曲线参数
public bool UseAdaptiveInterpolation { get; set; } = true;
public double CurveTension { get; set; } = 0.3;
public double MinCurvatureThreshold { get; set; } = 0.1;
public double MaxCurvatureThreshold { get; set; } = 0.8;
// 性能参数
public bool UseHardwareAcceleration { get; set; } = true;
public bool UseAsyncProcessing { get; set; } = true;
public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount;
public int MaxPointsPerStroke { get; set; } = 10000;
// 质量设置
public SmoothingQuality Quality { get; set; } = SmoothingQuality.Balanced;
public enum SmoothingQuality
{
Performance, // 性能优先
Balanced, // 平衡
Quality // 质量优先
}
// 兼容性枚举
public enum InkSmoothingQuality
{
HighPerformance = 0, // 高性能低质量
Balanced = 1, // 平衡
HighQuality = 2 // 高质量低性能
}
/// <summary>
/// 从设置中加载配置
/// </summary>
public static InkSmoothingConfig FromSettings()
{
var config = new InkSmoothingConfig();
try
{
// 尝试从MainWindow.Settings加载配置(兼容性)
if (MainWindow.Settings?.Canvas != null)
{
config.Quality = (SmoothingQuality)MainWindow.Settings.Canvas.InkSmoothingQuality;
config.UseHardwareAcceleration = MainWindow.Settings.Canvas.UseHardwareAcceleration;
config.UseAsyncProcessing = MainWindow.Settings.Canvas.UseAsyncInkSmoothing;
config.MaxConcurrentTasks = MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ?
MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount;
}
}
catch (Exception ex)
{
Debug.WriteLine($"加载平滑配置失败: {ex.Message}");
}
return config;
}
/// <summary>
/// 应用质量设置
/// </summary>
public void ApplyQualitySettings()
{
switch (Quality)
{
case SmoothingQuality.Performance:
SmoothingStrength = 0.2;
ResampleInterval = 4.0;
InterpolationSteps = 6;
UseAdaptiveInterpolation = false;
CurveTension = 0.2;
MaxConcurrentTasks = Math.Max(1, Environment.ProcessorCount / 2);
break;
case SmoothingQuality.Balanced:
SmoothingStrength = 0.4;
ResampleInterval = 2.5;
InterpolationSteps = 12;
UseAdaptiveInterpolation = true;
CurveTension = 0.3;
MaxConcurrentTasks = Environment.ProcessorCount;
break;
case SmoothingQuality.Quality:
SmoothingStrength = 0.6;
ResampleInterval = 1.5;
InterpolationSteps = 20;
UseAdaptiveInterpolation = true;
CurveTension = 0.4;
MaxConcurrentTasks = Environment.ProcessorCount;
break;
}
}
/// <summary>
/// 保存配置到设置
/// </summary>
public void SaveToSettings()
{
try
{
// 尝试保存到MainWindow.Settings(兼容性)
if (MainWindow.Settings?.Canvas != null)
{
MainWindow.Settings.Canvas.InkSmoothingQuality = (int)Quality;
MainWindow.Settings.Canvas.UseHardwareAcceleration = UseHardwareAcceleration;
MainWindow.Settings.Canvas.UseAsyncInkSmoothing = UseAsyncProcessing;
MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks = MaxConcurrentTasks;
}
}
catch (Exception ex)
{
Debug.WriteLine($"保存平滑配置失败: {ex.Message}");
}
}
/// <summary>
/// 验证配置参数
/// </summary>
public bool Validate()
{
return SmoothingStrength >= 0.0 && SmoothingStrength <= 1.0 &&
ResampleInterval > 0.0 &&
InterpolationSteps > 0 && InterpolationSteps <= 50 &&
CurveTension >= 0.0 && CurveTension <= 1.0 &&
MaxConcurrentTasks > 0 &&
MaxPointsPerStroke > 0;
}
/// <summary>
/// 获取配置摘要
/// </summary>
public string GetSummary()
{
return $"质量: {Quality}, 强度: {SmoothingStrength:F2}, 间隔: {ResampleInterval:F1}, " +
$"步数: {InterpolationSteps}, 自适应: {UseAdaptiveInterpolation}, " +
$"张力: {CurveTension:F2}, 硬件加速: {UseHardwareAcceleration}";
}
}
}
+262
View File
@@ -0,0 +1,262 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Ink;
using System.Windows.Media;
using System.Windows.Threading;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 统一的墨迹平滑管理器,整合异步处理和硬件加速
/// </summary>
public class InkSmoothingManager : IDisposable
{
private readonly AsyncAdvancedBezierSmoothing _asyncSmoothing;
private readonly HardwareAcceleratedInkProcessor _hardwareProcessor;
private readonly InkSmoothingPerformanceMonitor _performanceMonitor;
private readonly InkSmoothingConfig _config;
private readonly Dispatcher _uiDispatcher;
private bool _disposed;
public InkSmoothingManager(Dispatcher uiDispatcher)
{
_uiDispatcher = uiDispatcher;
_config = InkSmoothingConfig.FromSettings();
_config.ApplyQualitySettings();
_asyncSmoothing = new AsyncAdvancedBezierSmoothing(uiDispatcher)
{
SmoothingStrength = _config.SmoothingStrength,
ResampleInterval = _config.ResampleInterval,
InterpolationSteps = _config.InterpolationSteps,
UseHardwareAcceleration = _config.UseHardwareAcceleration,
MaxConcurrentTasks = _config.MaxConcurrentTasks
};
_hardwareProcessor = new HardwareAcceleratedInkProcessor();
_performanceMonitor = new InkSmoothingPerformanceMonitor();
}
/// <summary>
/// 平滑笔画(自动选择最佳方法)
/// </summary>
public async Task<Stroke> SmoothStrokeAsync(Stroke originalStroke,
Action<Stroke, Stroke> onCompleted = null,
CancellationToken cancellationToken = default)
{
if (originalStroke == null || originalStroke.StylusPoints.Count < 2)
return originalStroke;
var stopwatch = Stopwatch.StartNew();
Stroke result = originalStroke;
try
{
if (_config.UseAsyncProcessing)
{
// 使用异步处理
result = await _asyncSmoothing.SmoothStrokeAsync(originalStroke, onCompleted, cancellationToken);
}
else if (_config.UseHardwareAcceleration)
{
// 使用硬件加速但同步处理
result = await _hardwareProcessor.SmoothStrokeWithGPU(originalStroke);
onCompleted?.Invoke(originalStroke, result);
}
else
{
// 回退到传统同步处理
result = await Task.Run(() =>
{
var traditionalSmoothing = new AdvancedBezierSmoothing();
return traditionalSmoothing.SmoothStroke(originalStroke);
}, cancellationToken);
onCompleted?.Invoke(originalStroke, result);
}
}
catch (OperationCanceledException)
{
result = originalStroke;
}
catch (Exception ex)
{
Debug.WriteLine($"墨迹平滑失败: {ex.Message}");
result = originalStroke;
}
finally
{
stopwatch.Stop();
_performanceMonitor.RecordProcessingTime(stopwatch.Elapsed);
}
return result;
}
/// <summary>
/// 同步平滑笔画(用于向后兼容)
/// </summary>
public Stroke SmoothStroke(Stroke originalStroke)
{
if (originalStroke == null || originalStroke.StylusPoints.Count < 2)
return originalStroke;
var stopwatch = Stopwatch.StartNew();
Stroke result;
try
{
if (_config.UseHardwareAcceleration)
{
// 使用硬件加速的同步版本
var task = _hardwareProcessor.SmoothStrokeWithGPU(originalStroke);
task.Wait(5000); // 5秒超时
result = task.Status == TaskStatus.RanToCompletion ? task.Result : originalStroke;
}
else
{
// 传统同步处理
var traditionalSmoothing = new AdvancedBezierSmoothing();
result = traditionalSmoothing.SmoothStroke(originalStroke);
}
}
catch (Exception ex)
{
Debug.WriteLine($"同步墨迹平滑失败: {ex.Message}");
result = originalStroke;
}
finally
{
stopwatch.Stop();
_performanceMonitor.RecordProcessingTime(stopwatch.Elapsed);
}
return result;
}
/// <summary>
/// 更新配置
/// </summary>
public void UpdateConfig()
{
var newConfig = InkSmoothingConfig.FromSettings();
newConfig.ApplyQualitySettings();
_asyncSmoothing.SmoothingStrength = newConfig.SmoothingStrength;
_asyncSmoothing.ResampleInterval = newConfig.ResampleInterval;
_asyncSmoothing.InterpolationSteps = newConfig.InterpolationSteps;
_asyncSmoothing.UseHardwareAcceleration = newConfig.UseHardwareAcceleration;
_asyncSmoothing.MaxConcurrentTasks = newConfig.MaxConcurrentTasks;
}
/// <summary>
/// 获取性能统计信息
/// </summary>
public string GetPerformanceStats()
{
return $"平均处理时间: {_performanceMonitor.GetAverageProcessingTimeMs():F2}ms, " +
$"最大处理时间: {_performanceMonitor.GetMaxProcessingTimeMs():F2}ms, " +
$"样本数: {_performanceMonitor.GetSampleCount()}";
}
/// <summary>
/// 取消所有正在进行的任务
/// </summary>
public void CancelAllTasks()
{
_asyncSmoothing?.CancelAllTasks();
}
/// <summary>
/// 检查系统是否支持硬件加速
/// </summary>
public static bool IsHardwareAccelerationSupported()
{
try
{
return RenderCapability.Tier >= 0x00020000;
}
catch
{
return false;
}
}
/// <summary>
/// 获取推荐的配置
/// </summary>
public static InkSmoothingConfig GetRecommendedConfig()
{
var config = new InkSmoothingConfig();
// 根据系统性能调整配置
var processorCount = Environment.ProcessorCount;
var isHardwareAccelerated = IsHardwareAccelerationSupported();
if (processorCount >= 4 && isHardwareAccelerated)
{
// 降低高质量模式的门槛,4核以上且支持硬件加速就使用高质量
config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.HighQuality;
config.UseHardwareAcceleration = true;
config.UseAsyncProcessing = true;
config.MaxConcurrentTasks = Math.Min(processorCount, 8);
}
else if (processorCount >= 2)
{
// 2核以上使用平衡模式
config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.Balanced;
config.UseHardwareAcceleration = isHardwareAccelerated;
config.UseAsyncProcessing = true;
config.MaxConcurrentTasks = Math.Min(processorCount, 4);
}
else
{
// 单核或性能较低的设备使用高性能模式
config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.HighPerformance;
config.UseHardwareAcceleration = false;
config.UseAsyncProcessing = false;
config.MaxConcurrentTasks = 1;
}
config.ApplyQualitySettings();
return config;
}
/// <summary>
/// 应用推荐配置到设置
/// </summary>
public static void ApplyRecommendedSettings()
{
var config = GetRecommendedConfig();
MainWindow.Settings.Canvas.InkSmoothingQuality = (int)config.Quality;
MainWindow.Settings.Canvas.UseHardwareAcceleration = config.UseHardwareAcceleration;
MainWindow.Settings.Canvas.UseAsyncInkSmoothing = config.UseAsyncProcessing;
MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks = config.MaxConcurrentTasks;
}
public void Dispose()
{
if (!_disposed)
{
CancelAllTasks();
_asyncSmoothing?.Dispose();
_hardwareProcessor?.Dispose();
_disposed = true;
}
}
}
/// <summary>
/// 墨迹平滑事件参数
/// </summary>
public class InkSmoothingEventArgs : EventArgs
{
public Stroke OriginalStroke { get; set; }
public Stroke SmoothedStroke { get; set; }
public TimeSpan ProcessingTime { get; set; }
public bool WasAsync { get; set; }
public bool UsedHardwareAcceleration { get; set; }
}
}
+16 -12
View File
@@ -1,32 +1,36 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Interop;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;
using Point = System.Windows.Point;
namespace Ink_Canvas.Helpers {
internal class IsOutsideOfScreenHelper {
public static bool IsOutsideOfScreen(FrameworkElement target) {
namespace Ink_Canvas.Helpers
{
internal class IsOutsideOfScreenHelper
{
public static bool IsOutsideOfScreen(FrameworkElement target)
{
var hwndSource = (HwndSource)PresentationSource.FromVisual(target);
if (hwndSource is null) {
if (hwndSource is null)
{
return true;
}
var hWnd = hwndSource.Handle;
var targetBounds = GetPixelBoundsToScreen(target);
var screens = System.Windows.Forms.Screen.AllScreens;
var screens = Screen.AllScreens;
return !screens.Any(x => x.Bounds.IntersectsWith(targetBounds));
System.Drawing.Rectangle GetPixelBoundsToScreen(FrameworkElement visual) {
Rectangle GetPixelBoundsToScreen(FrameworkElement visual)
{
var pixelBoundsToScreen = Rect.Empty;
pixelBoundsToScreen.Union(visual.PointToScreen(new Point(0, 0)));
pixelBoundsToScreen.Union(visual.PointToScreen(new Point(visual.ActualWidth, 0)));
pixelBoundsToScreen.Union(visual.PointToScreen(new Point(0, visual.ActualHeight)));
pixelBoundsToScreen.Union(visual.PointToScreen(new Point(visual.ActualWidth, visual.ActualHeight)));
return new System.Drawing.Rectangle(
return new Rectangle(
(int)pixelBoundsToScreen.X, (int)pixelBoundsToScreen.Y,
(int)pixelBoundsToScreen.Width, (int)pixelBoundsToScreen.Height);
}
+99 -20
View File
@@ -1,54 +1,133 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
namespace Ink_Canvas.Helpers
{
class LogHelper
{
public static string LogFile = "Log.txt";
private static string LogsFolder = "Logs";
private static string AppStartTime = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
private static readonly long MaxLogsFolderSizeBytes = 5 * 1024 * 1024; // 5MB
public static void NewLog(string str)
{
WriteLogToFile(str, LogType.Info);
WriteLogToFile(str);
}
public static void NewLog(Exception ex)
{
if (ex == null) return;
var stackTrace = ex.StackTrace ?? "<no stack trace>";
var msg = $"[Exception] Type: {ex.GetType().FullName}\nMessage: {ex.Message}\nStackTrace: {stackTrace}";
if (ex.InnerException != null)
{
msg += $"\nInnerException: {ex.InnerException.GetType().FullName} - {ex.InnerException.Message}\n{ex.InnerException.StackTrace}";
}
WriteLogToFile(msg, LogType.Error);
}
public static void WriteLogToFile(string str, LogType logType = LogType.Info)
{
string strLogType = "Info";
switch (logType)
{
case LogType.Event:
strLogType = "Event";
break;
case LogType.Trace:
strLogType = "Trace";
break;
case LogType.Error:
strLogType = "Error";
break;
}
// 检查日志是否启用
if (MainWindow.Settings != null && MainWindow.Settings.Advanced != null && !MainWindow.Settings.Advanced.IsLogEnabled) return;
string strLogType = logType.ToString();
try
{
var file = App.RootPath + LogFile;
string file;
// 检查是否启用了日期保存功能
if (MainWindow.Settings != null && MainWindow.Settings.Advanced != null && MainWindow.Settings.Advanced.IsSaveLogByDate)
{
// 确保Logs文件夹存在
string logsPath = Path.Combine(App.RootPath, LogsFolder);
if (!Directory.Exists(logsPath))
{
Directory.CreateDirectory(logsPath);
}
// 检查Logs文件夹大小,如果超过5MB则清空
CheckAndCleanLogsFolder(logsPath);
// 使用软件启动时间作为日志文件名
file = Path.Combine(logsPath, $"Log_{AppStartTime}.txt");
}
else
{
file = App.RootPath + LogFile;
}
if (!Directory.Exists(App.RootPath))
{
Directory.CreateDirectory(App.RootPath);
}
StreamWriter sw = new StreamWriter(file, true);
sw.WriteLine(string.Format("{0} [{1}] {2}", DateTime.Now.ToString("O"), strLogType, str));
sw.Close();
var threadId = Thread.CurrentThread.ManagedThreadId;
var callingMethod = new StackTrace(2, true).GetFrame(0);
string callerInfo = "<unknown>";
if (callingMethod != null)
{
var method = callingMethod.GetMethod();
if (method != null)
{
var className = method.DeclaringType != null ? method.DeclaringType.FullName : "<no class>";
callerInfo = $"{className}.{method.Name}";
}
}
string logLine = string.Format("{0} [T{1}] [{2}] [{3}] {4}", DateTime.Now.ToString("O"), threadId, strLogType, callerInfo, str);
using (StreamWriter sw = new StreamWriter(file, true))
{
sw.WriteLine(logLine);
}
}
catch { }
}
private static void CheckAndCleanLogsFolder(string logsPath)
{
try
{
long totalSize = 0;
DirectoryInfo dirInfo = new DirectoryInfo(logsPath);
// 如果目录不存在,直接返回
if (!dirInfo.Exists) return;
// 计算文件夹大小
foreach (FileInfo file in dirInfo.GetFiles())
{
totalSize += file.Length;
}
// 如果超过5MB,清空文件夹
if (totalSize > MaxLogsFolderSizeBytes)
{
foreach (FileInfo file in dirInfo.GetFiles())
{
try
{
file.Delete();
}
catch { }
}
// 记录清理操作
string cleanupMessage = $"Logs folder exceeded size limit ({totalSize / 1024.0 / 1024.0:F2} MB > {MaxLogsFolderSizeBytes / 1024.0 / 1024.0:F2} MB). Folder cleaned.";
using (StreamWriter sw = new StreamWriter(Path.Combine(logsPath, $"Log_{AppStartTime}.txt"), true))
{
sw.WriteLine($"{DateTime.Now:O} [Cleanup] {cleanupMessage}");
}
}
}
catch { }
}
internal static void WriteLogToFile(string v, object warning)
{
throw new NotImplementedException();
WriteLogToFile($"[Warning] {v}", LogType.Warning);
}
public enum LogType
+1 -1
View File
@@ -32,7 +32,7 @@ namespace Ink_Canvas.Helpers
/// <summary>
/// 创建显示笔迹的类
/// </summary>
public StrokeVisual() : this(new DrawingAttributes()
public StrokeVisual() : this(new DrawingAttributes
{
Color = Colors.Red,
//FitToCurve = true,
+413
View File
@@ -0,0 +1,413 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Ink;
using Microsoft.Office.Interop.PowerPoint;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// PPT墨迹管理器 - 负责PPT中墨迹的保存、加载和同步
/// </summary>
public class PPTInkManager : IDisposable
{
#region Properties
public bool IsAutoSaveEnabled { get; set; } = true;
public string AutoSaveLocation { get; set; } = "";
public StrokeCollection CurrentStrokes { get; private set; } = new StrokeCollection();
#endregion
#region Private Fields
private MemoryStream[] _memoryStreams;
private int _maxSlides = 100;
private string _currentPresentationId = "";
private readonly object _lockObject = new object();
private bool _disposed;
// 墨迹锁定机制,防止翻页时的墨迹冲突
private DateTime _inkLockUntil = DateTime.MinValue;
private int _lockedSlideIndex = -1;
private const int InkLockMilliseconds = 500;
#endregion
#region Constructor
public PPTInkManager()
{
InitializeMemoryStreams();
}
private void InitializeMemoryStreams()
{
_memoryStreams = new MemoryStream[_maxSlides + 2];
}
#endregion
#region Public Methods
/// <summary>
/// 初始化新的演示文稿
/// </summary>
public void InitializePresentation(Presentation presentation)
{
if (presentation == null) return;
lock (_lockObject)
{
try
{
// 完全清理之前的墨迹状态
ClearAllStrokes();
// 重置墨迹锁定状态
_inkLockUntil = DateTime.MinValue;
_lockedSlideIndex = -1;
// 生成演示文稿唯一标识符
_currentPresentationId = GeneratePresentationId(presentation);
// 重新初始化内存流数组
var slideCount = presentation.Slides.Count;
_memoryStreams = new MemoryStream[slideCount + 2];
// 如果启用自动保存,尝试加载已保存的墨迹
if (IsAutoSaveEnabled && !string.IsNullOrEmpty(AutoSaveLocation))
{
LoadSavedStrokes();
}
LogHelper.WriteLogToFile($"已初始化演示文稿墨迹管理: {presentation.Name}, 幻灯片数量: {slideCount}", LogHelper.LogType.Trace);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化演示文稿墨迹管理失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 保存当前页面的墨迹
/// </summary>
public void SaveCurrentSlideStrokes(int slideIndex, StrokeCollection strokes)
{
if (slideIndex <= 0 || strokes == null) return;
lock (_lockObject)
{
try
{
// 检查墨迹锁定
if (!CanWriteInk(slideIndex))
{
LogHelper.WriteLogToFile($"墨迹写入被锁定,当前页:{slideIndex},锁定页:{_lockedSlideIndex}", LogHelper.LogType.Warning);
return;
}
if (slideIndex < _memoryStreams.Length)
{
var ms = new MemoryStream();
strokes.Save(ms);
ms.Position = 0;
// 释放旧的内存流
_memoryStreams[slideIndex]?.Dispose();
_memoryStreams[slideIndex] = ms;
LogHelper.WriteLogToFile($"已保存第{slideIndex}页墨迹,大小: {ms.Length} bytes", LogHelper.LogType.Trace);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存第{slideIndex}页墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 加载指定页面的墨迹
/// </summary>
public StrokeCollection LoadSlideStrokes(int slideIndex)
{
if (slideIndex <= 0) return new StrokeCollection();
lock (_lockObject)
{
try
{
if (slideIndex < _memoryStreams.Length && _memoryStreams[slideIndex] != null && _memoryStreams[slideIndex].Length > 0)
{
_memoryStreams[slideIndex].Position = 0;
var strokes = new StrokeCollection(_memoryStreams[slideIndex]);
LogHelper.WriteLogToFile($"已加载第{slideIndex}页墨迹,笔画数量: {strokes.Count}", LogHelper.LogType.Trace);
return strokes;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载第{slideIndex}页墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
return new StrokeCollection();
}
/// <summary>
/// 切换到指定页面并加载墨迹
/// </summary>
public StrokeCollection SwitchToSlide(int slideIndex, StrokeCollection currentStrokes = null)
{
lock (_lockObject)
{
try
{
// 如果有当前墨迹,先保存到正确的页面
if (currentStrokes != null && currentStrokes.Count > 0)
{
// 确定要保存的页面索引
int saveToSlideIndex = _lockedSlideIndex > 0 ? _lockedSlideIndex : slideIndex;
// 确保页面索引有效
if (saveToSlideIndex > 0 && saveToSlideIndex < _memoryStreams.Length)
{
SaveCurrentSlideStrokes(saveToSlideIndex, currentStrokes);
LogHelper.WriteLogToFile($"已保存第{saveToSlideIndex}页墨迹,墨迹数量: {currentStrokes.Count}", LogHelper.LogType.Trace);
}
}
// 设置墨迹锁定
LockInkForSlide(slideIndex);
// 加载新页面的墨迹
var newStrokes = LoadSlideStrokes(slideIndex);
LogHelper.WriteLogToFile($"已切换到第{slideIndex}页,加载墨迹数量: {newStrokes.Count}", LogHelper.LogType.Trace);
return newStrokes;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换到第{slideIndex}页失败: {ex}", LogHelper.LogType.Error);
return new StrokeCollection();
}
}
}
/// <summary>
/// 保存所有墨迹到文件
/// </summary>
public void SaveAllStrokesToFile(Presentation presentation)
{
if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation) || presentation == null) return;
lock (_lockObject)
{
try
{
var folderPath = GetPresentationFolderPath();
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
// 保存位置信息
try
{
File.WriteAllText(Path.Combine(folderPath, "Position"), _lockedSlideIndex.ToString());
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存位置信息失败: {ex}", LogHelper.LogType.Error);
}
// 保存所有页面的墨迹
int savedCount = 0;
for (int i = 1; i <= presentation.Slides.Count && i < _memoryStreams.Length; i++)
{
if (_memoryStreams[i] != null)
{
try
{
if (_memoryStreams[i].Length > 8)
{
var srcBuf = new byte[_memoryStreams[i].Length];
_memoryStreams[i].Position = 0;
var byteLength = _memoryStreams[i].Read(srcBuf, 0, srcBuf.Length);
var filePath = Path.Combine(folderPath, i.ToString("0000") + ".icstk");
File.WriteAllBytes(filePath, srcBuf);
savedCount++;
LogHelper.WriteLogToFile($"已保存第{i}页墨迹,大小: {byteLength} bytes", LogHelper.LogType.Trace);
}
else
{
// 删除空的墨迹文件
var filePath = Path.Combine(folderPath, i.ToString("0000") + ".icstk");
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存第{i}页墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
LogHelper.WriteLogToFile($"已保存{savedCount}页墨迹到文件", LogHelper.LogType.Event);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存墨迹到文件失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 从文件加载已保存的墨迹
/// </summary>
public void LoadSavedStrokes()
{
if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation)) return;
lock (_lockObject)
{
try
{
var folderPath = GetPresentationFolderPath();
if (!Directory.Exists(folderPath)) return;
var files = new DirectoryInfo(folderPath).GetFiles("*.icstk");
int loadedCount = 0;
foreach (var file in files)
{
try
{
if (int.TryParse(Path.GetFileNameWithoutExtension(file.Name), out int slideIndex))
{
if (slideIndex > 0 && slideIndex < _memoryStreams.Length)
{
var fileBytes = File.ReadAllBytes(file.FullName);
_memoryStreams[slideIndex] = new MemoryStream(fileBytes);
_memoryStreams[slideIndex].Position = 0;
loadedCount++;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载墨迹文件{file.Name}失败: {ex}", LogHelper.LogType.Error);
}
}
LogHelper.WriteLogToFile($"已从文件加载{loadedCount}页墨迹", LogHelper.LogType.Event);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从文件加载墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 清除所有墨迹
/// </summary>
public void ClearAllStrokes()
{
lock (_lockObject)
{
try
{
for (int i = 0; i < _memoryStreams.Length; i++)
{
_memoryStreams[i]?.Dispose();
_memoryStreams[i] = null;
}
CurrentStrokes.Clear();
LogHelper.WriteLogToFile("已清除所有墨迹", LogHelper.LogType.Trace);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 翻页后锁定墨迹写入
/// </summary>
public void LockInkForSlide(int slideIndex)
{
_inkLockUntil = DateTime.Now.AddMilliseconds(InkLockMilliseconds);
_lockedSlideIndex = slideIndex;
}
/// <summary>
/// 检查是否可以写入墨迹
/// </summary>
public bool CanWriteInk(int currentSlideIndex)
{
if (DateTime.Now < _inkLockUntil) return false;
if (currentSlideIndex != _lockedSlideIndex && _lockedSlideIndex > 0) return false;
return true;
}
#endregion
#region Private Methods
private string GeneratePresentationId(Presentation presentation)
{
try
{
var presentationPath = presentation.FullName;
var fileHash = GetFileHash(presentationPath);
return $"{presentation.Name}_{presentation.Slides.Count}_{fileHash}";
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"生成演示文稿ID失败: {ex}", LogHelper.LogType.Error);
return $"unknown_{DateTime.Now.Ticks}";
}
}
private string GetFileHash(string filePath)
{
try
{
if (string.IsNullOrEmpty(filePath)) return "unknown";
using (var md5 = MD5.Create())
{
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(filePath));
return BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 8);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"计算文件哈希值失败: {ex}", LogHelper.LogType.Error);
return "error";
}
}
private string GetPresentationFolderPath()
{
return Path.Combine(AutoSaveLocation, "Auto Saved - Presentations", _currentPresentationId);
}
#endregion
#region Dispose
public void Dispose()
{
if (!_disposed)
{
lock (_lockObject)
{
ClearAllStrokes();
}
_disposed = true;
}
}
#endregion
}
}
File diff suppressed because it is too large Load Diff
+468
View File
@@ -0,0 +1,468 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// PPT UI管理器 - 统一管理PPT相关的UI更新和样式设置
/// </summary>
public class PPTUIManager
{
#region Properties
public bool ShowPPTButton { get; set; } = true;
public int PPTButtonsDisplayOption { get; set; } = 2222;
public int PPTSButtonsOption { get; set; } = 221;
public int PPTBButtonsOption { get; set; } = 121;
public int PPTLSButtonPosition { get; set; } = 0;
public int PPTRSButtonPosition { get; set; } = 0;
public int PPTLBButtonPosition { get; set; } = 0;
public int PPTRBButtonPosition { get; set; } = 0;
public bool EnablePPTButtonPageClickable { get; set; } = true;
public bool EnablePPTButtonLongPressPageTurn { get; set; } = true;
#endregion
#region Private Fields
private readonly MainWindow _mainWindow;
private readonly Dispatcher _dispatcher;
#endregion
#region Constructor
public PPTUIManager(MainWindow mainWindow)
{
_mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow));
_dispatcher = _mainWindow.Dispatcher;
}
#endregion
#region Public Methods
/// <summary>
/// 更新PPT连接状态UI
/// </summary>
public void UpdateConnectionStatus(bool isConnected)
{
_dispatcher.InvokeAsync(() =>
{
try
{
if (isConnected)
{
_mainWindow.StackPanelPPTControls.Visibility = Visibility.Visible;
_mainWindow.BtnPPTSlideShow.Visibility = Visibility.Visible;
}
else
{
_mainWindow.StackPanelPPTControls.Visibility = Visibility.Collapsed;
_mainWindow.BtnPPTSlideShow.Visibility = Visibility.Collapsed;
_mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed;
HideAllNavigationPanels();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新PPT连接状态UI失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 更新幻灯片放映状态UI
/// </summary>
public void UpdateSlideShowStatus(bool isInSlideShow, int currentSlide = 0, int totalSlides = 0)
{
_dispatcher.InvokeAsync(() =>
{
try
{
if (isInSlideShow)
{
_mainWindow.BtnPPTSlideShow.Visibility = Visibility.Collapsed;
_mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Visible;
if (currentSlide > 0 && totalSlides > 0)
{
_mainWindow.PPTBtnPageNow.Text = currentSlide.ToString();
_mainWindow.PPTBtnPageTotal.Text = $"/ {totalSlides}";
}
UpdateNavigationPanelsVisibility();
UpdateNavigationButtonStyles();
}
else
{
_mainWindow.BtnPPTSlideShow.Visibility = Visibility.Visible;
_mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed;
HideAllNavigationPanels();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新幻灯片放映状态UI失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 更新当前页码显示
/// </summary>
public void UpdateCurrentSlideNumber(int currentSlide, int totalSlides)
{
_dispatcher.InvokeAsync(() =>
{
try
{
_mainWindow.PPTBtnPageNow.Text = currentSlide.ToString();
_mainWindow.PPTBtnPageTotal.Text = $"/ {totalSlides}";
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新页码显示失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 处理PPT放映状态变化
/// </summary>
public void OnSlideShowStateChanged(bool isInSlideShow)
{
_dispatcher.InvokeAsync(() =>
{
try
{
if (!isInSlideShow)
{
// 如果不在放映模式,隐藏所有导航面板
HideAllNavigationPanels();
LogHelper.WriteLogToFile("PPT放映状态变化:隐藏导航面板", LogHelper.LogType.Trace);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理PPT放映状态变化失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 更新导航面板显示状态
/// </summary>
public void UpdateNavigationPanelsVisibility()
{
_dispatcher.InvokeAsync(() =>
{
try
{
// 检查是否应该显示PPT按钮
// 不仅要检查按钮设置,还要确保确实在PPT放映模式下
bool shouldShowButtons = ShowPPTButton &&
_mainWindow.BtnPPTSlideShowEnd.Visibility == Visibility.Visible &&
_mainWindow.PPTManager?.IsInSlideShow == true;
if (!shouldShowButtons)
{
HideAllNavigationPanels();
return;
}
// 设置侧边按钮位置
_mainWindow.LeftSidePanelForPPTNavigation.Margin = new Thickness(0, 0, 0, PPTLSButtonPosition * 2);
_mainWindow.RightSidePanelForPPTNavigation.Margin = new Thickness(0, 0, 0, PPTRSButtonPosition * 2);
// 设置底部按钮水平位置
_mainWindow.LeftBottomPanelForPPTNavigation.Margin = new Thickness(6 + PPTLBButtonPosition, 0, 0, 6);
_mainWindow.RightBottomPanelForPPTNavigation.Margin = new Thickness(0, 0, 6 + PPTRBButtonPosition, 6);
// 根据显示选项设置面板可见性
var displayOption = PPTButtonsDisplayOption.ToString();
if (displayOption.Length >= 4)
{
var options = displayOption.ToCharArray();
// 左下角面板
if (options[0] == '2')
AnimationsHelper.ShowWithFadeIn(_mainWindow.LeftBottomPanelForPPTNavigation);
else
_mainWindow.LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
// 右下角面板
if (options[1] == '2')
AnimationsHelper.ShowWithFadeIn(_mainWindow.RightBottomPanelForPPTNavigation);
else
_mainWindow.RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
// 左侧面板
if (options[2] == '2')
AnimationsHelper.ShowWithFadeIn(_mainWindow.LeftSidePanelForPPTNavigation);
else
_mainWindow.LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
// 右侧面板
if (options[3] == '2')
AnimationsHelper.ShowWithFadeIn(_mainWindow.RightSidePanelForPPTNavigation);
else
_mainWindow.RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新导航面板显示状态失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 更新导航按钮样式
/// </summary>
public void UpdateNavigationButtonStyles()
{
_dispatcher.InvokeAsync(() =>
{
try
{
UpdateSideButtonStyles();
UpdateBottomButtonStyles();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新导航按钮样式失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 隐藏所有导航面板
/// </summary>
public void HideAllNavigationPanels()
{
_dispatcher.InvokeAsync(() =>
{
try
{
_mainWindow.LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
_mainWindow.RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
_mainWindow.LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
_mainWindow.RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"隐藏导航面板失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 显示/隐藏侧边栏退出按钮
/// </summary>
public void UpdateSidebarExitButtons(bool show)
{
_dispatcher.InvokeAsync(() =>
{
try
{
var visibility = show ? Visibility.Visible : Visibility.Collapsed;
if (_mainWindow.BtnExitPptFromSidebarLeft != null)
_mainWindow.BtnExitPptFromSidebarLeft.Visibility = visibility;
if (_mainWindow.BtnExitPptFromSidebarRight != null)
_mainWindow.BtnExitPptFromSidebarRight.Visibility = visibility;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新侧边栏退出按钮失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 设置浮动栏透明度
/// </summary>
public void SetFloatingBarOpacity(double opacity)
{
_dispatcher.InvokeAsync(() =>
{
try
{
_mainWindow.ViewboxFloatingBar.Opacity = opacity;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"设置浮动栏透明度失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 设置主面板边距
/// </summary>
public void SetMainPanelMargin(Thickness margin)
{
_dispatcher.InvokeAsync(() =>
{
try
{
_mainWindow.ViewBoxStackPanelMain.Margin = margin;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"设置主面板边距失败: {ex}", LogHelper.LogType.Error);
}
});
}
#endregion
#region Private Methods
private void UpdateSideButtonStyles()
{
try
{
var sideOption = PPTSButtonsOption.ToString();
if (sideOption.Length < 3) return;
var options = sideOption.ToCharArray();
// 页码按钮显示
var pageButtonVisibility = options[0] == '2' ? Visibility.Visible : Visibility.Collapsed;
_mainWindow.PPTLSPageButton.Visibility = pageButtonVisibility;
_mainWindow.PPTRSPageButton.Visibility = pageButtonVisibility;
// 透明度设置
var opacity = options[1] == '2' ? 0.5 : 1.0;
_mainWindow.PPTBtnLSBorder.Opacity = opacity;
_mainWindow.PPTBtnRSBorder.Opacity = opacity;
// 颜色主题
bool isDarkTheme = options[2] == '2';
ApplyButtonTheme(_mainWindow.PPTBtnLSBorder, _mainWindow.PPTBtnRSBorder, isDarkTheme, true);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新侧边按钮样式失败: {ex}", LogHelper.LogType.Error);
}
}
private void UpdateBottomButtonStyles()
{
try
{
var bottomOption = PPTBButtonsOption.ToString();
if (bottomOption.Length < 3) return;
var options = bottomOption.ToCharArray();
// 页码按钮显示
var pageButtonVisibility = options[0] == '2' ? Visibility.Visible : Visibility.Collapsed;
_mainWindow.PPTLBPageButton.Visibility = pageButtonVisibility;
_mainWindow.PPTRBPageButton.Visibility = pageButtonVisibility;
// 透明度设置
var opacity = options[1] == '2' ? 0.5 : 1.0;
_mainWindow.PPTBtnLBBorder.Opacity = opacity;
_mainWindow.PPTBtnRBBorder.Opacity = opacity;
// 颜色主题
bool isDarkTheme = options[2] == '2';
ApplyButtonTheme(_mainWindow.PPTBtnLBBorder, _mainWindow.PPTBtnRBBorder, isDarkTheme, false);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新底部按钮样式失败: {ex}", LogHelper.LogType.Error);
}
}
private void ApplyButtonTheme(Border leftBorder, Border rightBorder, bool isDarkTheme, bool isSideButton)
{
try
{
Color backgroundColor, borderColor, foregroundColor, feedbackColor;
if (isDarkTheme)
{
backgroundColor = Color.FromRgb(39, 39, 42);
borderColor = Color.FromRgb(82, 82, 91);
foregroundColor = Colors.White;
feedbackColor = Colors.White;
}
else
{
backgroundColor = Color.FromRgb(244, 244, 245);
borderColor = Color.FromRgb(161, 161, 170);
foregroundColor = Color.FromRgb(39, 39, 42);
feedbackColor = Color.FromRgb(24, 24, 27);
}
// 应用背景和边框颜色
var backgroundBrush = new SolidColorBrush(backgroundColor);
var borderBrush = new SolidColorBrush(borderColor);
leftBorder.Background = backgroundBrush;
leftBorder.BorderBrush = borderBrush;
rightBorder.Background = backgroundBrush;
rightBorder.BorderBrush = borderBrush;
// 应用图标和文字颜色
var foregroundBrush = new SolidColorBrush(foregroundColor);
var feedbackBrush = new SolidColorBrush(feedbackColor);
if (isSideButton)
{
ApplySideButtonColors(foregroundBrush, feedbackBrush);
}
else
{
ApplyBottomButtonColors(foregroundBrush, feedbackBrush);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用按钮主题失败: {ex}", LogHelper.LogType.Error);
}
}
private void ApplySideButtonColors(SolidColorBrush foregroundBrush, SolidColorBrush feedbackBrush)
{
// 图标颜色
_mainWindow.PPTLSPreviousButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTRSPreviousButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTLSNextButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTRSNextButtonGeometry.Brush = foregroundBrush;
// 反馈背景颜色
_mainWindow.PPTLSPreviousButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRSPreviousButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTLSPageButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRSPageButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTLSNextButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRSNextButtonFeedbackBorder.Background = feedbackBrush;
// 文字颜色
TextBlock.SetForeground(_mainWindow.PPTLSPageButton, foregroundBrush);
TextBlock.SetForeground(_mainWindow.PPTRSPageButton, foregroundBrush);
}
private void ApplyBottomButtonColors(SolidColorBrush foregroundBrush, SolidColorBrush feedbackBrush)
{
// 图标颜色
_mainWindow.PPTLBPreviousButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTRBPreviousButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTLBNextButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTRBNextButtonGeometry.Brush = foregroundBrush;
// 反馈背景颜色
_mainWindow.PPTLBPreviousButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRBPreviousButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTLBPageButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRBPageButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTLBNextButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRBNextButtonFeedbackBorder.Background = feedbackBrush;
// 文字颜色
TextBlock.SetForeground(_mainWindow.PPTLBPageButton, foregroundBrush);
TextBlock.SetForeground(_mainWindow.PPTRBPageButton, foregroundBrush);
}
#endregion
}
}
@@ -0,0 +1,274 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using iNKORE.UI.WPF.Modern.Controls;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
/// <summary>
/// 启动台按钮控件
/// </summary>
public class LauncherButton
{
/// <summary>
/// 父插件
/// </summary>
private readonly SuperLauncherPlugin _plugin;
/// <summary>
/// 实际按钮控件
/// </summary>
private readonly SimpleStackPanel _panel;
/// <summary>
/// 获取按钮UI元素
/// </summary>
public UIElement Element => _panel;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="plugin">父插件</param>
public LauncherButton(SuperLauncherPlugin plugin)
{
try
{
_plugin = plugin;
LogHelper.WriteLogToFile("开始创建启动台按钮");
// 创建SimpleStackPanel
_panel = new SimpleStackPanel
{
Name = "Launcher_Icon",
Orientation = Orientation.Vertical,
HorizontalAlignment = HorizontalAlignment.Center,
Width = 28,
Margin = new Thickness(0, -2, 0, 0),
Background = Brushes.Transparent
};
LogHelper.WriteLogToFile("创建SimpleStackPanel完成");
// 添加图标
var image = CreateIconImage();
_panel.Children.Add(image);
// 添加文本
TextBlock textBlock = new TextBlock
{
Text = "启动台",
Foreground = Brushes.Black,
FontSize = 8,
Margin = new Thickness(0, 1, 0, 0),
TextAlignment = TextAlignment.Center
};
_panel.Children.Add(textBlock);
// 设置鼠标事件
_panel.MouseDown += Panel_MouseDown;
_panel.MouseUp += Panel_MouseUp;
_panel.MouseLeave += Panel_MouseLeave;
// 右键菜单支持
_panel.ContextMenu = CreateContextMenu();
// 设置工具提示
_panel.ToolTip = "启动台";
LogHelper.WriteLogToFile("启动台按钮创建完成");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"创建启动台按钮时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 创建右键菜单
/// </summary>
private ContextMenu CreateContextMenu()
{
try
{
// 创建菜单
ContextMenu menu = new ContextMenu();
// 创建位置切换菜单项
MenuItem positionMenuItem = new MenuItem();
positionMenuItem.Header = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
"移至右侧" : "移至左侧";
positionMenuItem.Click += (s, e) =>
{
// 切换位置
_plugin.Config.ButtonPosition = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
LauncherButtonPosition.Right : LauncherButtonPosition.Left;
// 更新按钮位置
_plugin.UpdateButtonPosition();
// 保存配置
_plugin.SaveConfig();
LogHelper.WriteLogToFile($"通过右键菜单切换启动台按钮位置为: {_plugin.Config.ButtonPosition}");
};
menu.Items.Add(positionMenuItem);
// 添加设置菜单项
MenuItem settingsMenuItem = new MenuItem();
settingsMenuItem.Header = "打开设置";
settingsMenuItem.Click += (s, e) =>
{
// 打开插件设置窗口
var mainWindow = Application.Current.MainWindow;
if (mainWindow != null)
{
try
{
// 使用反射调用主窗口的ShowPluginSettings方法
var method = mainWindow.GetType().GetMethod("ShowPluginSettings");
if (method != null)
{
method.Invoke(mainWindow, null);
LogHelper.WriteLogToFile("已打开插件设置窗口");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"打开插件设置窗口失败: {ex.Message}", LogHelper.LogType.Error);
}
}
};
menu.Items.Add(settingsMenuItem);
return menu;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"创建右键菜单时出错: {ex.Message}", LogHelper.LogType.Error);
return null;
}
}
/// <summary>
/// 获取实际的UI元素
/// </summary>
[Obsolete("使用Element属性代替")]
public UIElement GetUIElement()
{
return _panel;
}
/// <summary>
/// 创建图标图像
/// </summary>
private Image CreateIconImage()
{
try
{
// 创建图像
Image image = new Image
{
Height = 17,
Margin = new Thickness(0, 3, 0, 0)
};
// 设置位图缩放模式
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
// 创建绘图图像
DrawingImage drawingImage = new DrawingImage();
DrawingGroup drawingGroup = new DrawingGroup();
drawingGroup.ClipGeometry = Geometry.Parse("M0,0 V24 H24 V0 H0 Z");
// 使用提供的应用网格图标
GeometryDrawing geometryDrawing = new GeometryDrawing
{
Brush = new SolidColorBrush(Color.FromRgb(0x1B, 0x1B, 0x1B)),
Geometry = Geometry.Parse("F0 M24,24z M0,0z M4.41721,4.29873C4.35178,4.29873,4.29873,4.35178,4.29873,4.41721L4.29873,9.15646C4.29873,9.22189,4.35178,9.27494,4.41721,9.27494L9.15646,9.27494C9.22189,9.27494,9.27494,9.22189,9.27494,9.15646L9.27494,4.41721C9.27494,4.35178,9.22189,4.29873,9.15646,4.29873L4.41721,4.29873z M2.64,4.41721C2.64,3.43569,3.43569,2.64,4.41721,2.64L9.15646,2.64C10.138,2.64,10.9337,3.43569,10.9337,4.41721L10.9337,9.15646C10.9337,10.138,10.138,10.9337,9.15646,10.9337L4.41721,10.9337C3.43569,10.9337,2.64,10.138,2.64,9.15646L2.64,4.41721z M14.8435,4.29873C14.7781,4.29873,14.7251,4.35178,14.7251,4.41721L14.7251,9.15646C14.7251,9.22189,14.7781,9.27494,14.8435,9.27494L19.5828,9.27494C19.6482,9.27494,19.7013,9.22189,19.7013,9.15646L19.7013,4.41721C19.7013,4.35178,19.6482,4.29873,19.5828,4.29873L14.8435,4.29873z M13.0663,4.41721C13.0663,3.43569,13.862,2.64,14.8435,2.64L19.5828,2.64C20.5643,2.64,21.36,3.43569,21.36,4.41721L21.36,9.15646C21.36,10.138,20.5643,10.9337,19.5828,10.9337L14.8435,10.9337C13.862,10.9337,13.0663,10.138,13.0663,9.15646L13.0663,4.41721z M14.8435,14.7251C14.7781,14.7251,14.7251,14.7781,14.7251,14.8435L14.7251,19.5828C14.7251,19.6482,14.7781,19.7013,14.8435,19.7013L19.5828,19.7013C19.6482,19.7013,19.7013,19.6482,19.7013,19.5828L19.7013,14.8435C19.7013,14.7781,19.6482,14.7251,19.5828,14.7251L14.8435,14.7251z M13.0663,14.8435C13.0663,13.862,13.862,13.0663,14.8435,13.0663L19.5828,13.0663C20.5643,13.0663,21.36,13.862,21.36,14.8435L21.36,19.5828C21.36,20.5643,20.5643,21.36,19.5828,21.36L14.8435,21.36C13.862,21.36,13.0663,20.5643,13.0663,19.5828L13.0663,14.8435z M4.41721,14.7251C4.35178,14.7251,4.29873,14.7781,4.29873,14.8435L4.29873,19.5828C4.29873,19.6482,4.35178,19.7013,4.41721,19.7013L9.15646,19.7013C9.22189,19.7013,9.27494,19.6482,9.27494,19.5828L9.27494,14.8435C9.27494,14.7781,9.22189,14.7251,9.15646,14.7251L4.41721,14.7251z M2.64,14.8435C2.64,13.862,3.43569,13.0663,4.41721,13.0663L9.15646,13.0663C10.138,13.0663,10.9337,13.862,10.9337,14.8435L10.9337,19.5828C10.9337,20.5643,10.138,21.36,9.15646,21.36L4.41721,21.36C3.43569,21.36,2.64,20.5643,2.64,19.5828L2.64,14.8435z")
};
drawingGroup.Children.Add(geometryDrawing);
// 设置图像源
drawingImage.Drawing = drawingGroup;
image.Source = drawingImage;
return image;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"创建图标图像时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
// 返回一个空图像
return new Image();
}
}
/// <summary>
/// 鼠标按下事件
/// </summary>
private void Panel_MouseDown(object sender, MouseButtonEventArgs e)
{
try
{
// 提供反馈
_panel.Background = new SolidColorBrush(Color.FromArgb(40, 0, 0, 0));
LogHelper.WriteLogToFile("启动台按钮鼠标按下");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动台按钮鼠标按下事件出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 鼠标抬起事件
/// </summary>
private void Panel_MouseUp(object sender, MouseButtonEventArgs e)
{
try
{
// 只有左键点击才显示启动台窗口
if (e.ChangedButton != MouseButton.Left)
{
return;
}
// 恢复背景
_panel.Background = Brushes.Transparent;
LogHelper.WriteLogToFile("启动台按钮鼠标抬起,准备显示启动台窗口");
// 获取按钮在屏幕上的位置
Point buttonPosition = _panel.PointToScreen(new Point(_panel.ActualWidth / 2, 0));
// 显示启动台窗口
_plugin.ShowLauncherWindow(buttonPosition);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动台按钮鼠标抬起事件出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 鼠标离开事件
/// </summary>
private void Panel_MouseLeave(object sender, MouseEventArgs e)
{
try
{
// 恢复背景
_panel.Background = Brushes.Transparent;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动台按钮鼠标离开事件出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}
@@ -0,0 +1,332 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using Newtonsoft.Json;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
/// <summary>
/// 启动台按钮位置
/// </summary>
public enum LauncherButtonPosition
{
/// <summary>
/// 左侧
/// </summary>
Left,
/// <summary>
/// 右侧
/// </summary>
Right
}
/// <summary>
/// 启动台配置
/// </summary>
public class LauncherConfig
{
/// <summary>
/// 启动台按钮位置
/// </summary>
public LauncherButtonPosition ButtonPosition { get; set; } = LauncherButtonPosition.Right;
/// <summary>
/// 启动台应用程序列表
/// </summary>
public List<LauncherItem> Items { get; set; } = new List<LauncherItem>();
}
/// <summary>
/// 启动台应用项
/// </summary>
public class LauncherItem
{
/// <summary>
/// 应用程序名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 应用程序路径
/// </summary>
public string Path { get; set; }
/// <summary>
/// 是否可见
/// </summary>
public bool IsVisible { get; set; } = true;
/// <summary>
/// 在启动台中的位置(0-39
/// </summary>
public int Position { get; set; } = -1;
/// <summary>
/// 是否已固定位置
/// </summary>
public bool IsPositionFixed { get; set; } = false;
/// <summary>
/// 图标缓存
/// </summary>
[JsonIgnore]
private ImageSource _iconCache;
/// <summary>
/// 获取应用程序图标
/// </summary>
[JsonIgnore]
public ImageSource Icon
{
get
{
if (_iconCache != null)
{
return _iconCache;
}
try
{
if (File.Exists(Path))
{
// 从文件中获取图标
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(Path);
if (icon != null)
{
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
icon.Dispose();
return _iconCache;
}
}
else
{
// 从注册表中获取文件类型关联图标
string extension = System.IO.Path.GetExtension(Path);
if (!string.IsNullOrEmpty(extension))
{
string fileType = Registry.ClassesRoot.OpenSubKey(extension)?.GetValue(string.Empty) as string;
if (!string.IsNullOrEmpty(fileType))
{
string iconPath = Registry.ClassesRoot.OpenSubKey(fileType + "\\DefaultIcon")?.GetValue(string.Empty) as string;
if (!string.IsNullOrEmpty(iconPath))
{
string[] parts = iconPath.Split(',');
string iconFile = parts[0].Trim('"');
int iconIndex = parts.Length > 1 ? Convert.ToInt32(parts[1]) : 0;
if (File.Exists(iconFile))
{
Icon icon = IconExtractor.Extract(iconFile, iconIndex, true);
if (icon != null)
{
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
icon.Dispose();
return _iconCache;
}
}
}
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取应用图标时出错: {ex.Message}", LogHelper.LogType.Error);
}
// 返回默认图标
return GetDefaultIcon();
}
}
/// <summary>
/// 获取默认图标
/// </summary>
private ImageSource GetDefaultIcon()
{
try
{
// 对于资源管理器,使用特定图标
if (Path.EndsWith("explorer.exe", StringComparison.OrdinalIgnoreCase))
{
try
{
// 直接从C:\Windows\explorer.exe获取图标
string explorerPath = @"C:\Windows\explorer.exe";
if (File.Exists(explorerPath))
{
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(explorerPath);
if (icon != null)
{
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
icon.Dispose();
return _iconCache;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取资源管理器图标时出错: {ex.Message}", LogHelper.LogType.Warning);
// 如果获取Windows图标失败,回退到默认图标
}
// 回退到备用图标
string explorerIconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-Fluent", "ic_fluent_folder_24_regular.png");
if (File.Exists(explorerIconPath))
{
Uri uri = new Uri(explorerIconPath);
BitmapImage image = new BitmapImage(uri);
_iconCache = image;
return _iconCache;
}
}
// 返回一个简单的默认图标
string iconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-png", "icc.png");
if (File.Exists(iconPath))
{
Uri uri = new Uri(iconPath);
BitmapImage image = new BitmapImage(uri);
_iconCache = image;
return _iconCache;
}
// 如果还是没有找到,尝试使用应用程序图标
string appIconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-Fluent", "ic_fluent_apps_24_regular.png");
if (File.Exists(appIconPath))
{
Uri uri = new Uri(appIconPath);
BitmapImage image = new BitmapImage(uri);
_iconCache = image;
return _iconCache;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取默认图标时出错: {ex.Message}", LogHelper.LogType.Error);
}
return null;
}
/// <summary>
/// 启动应用程序
/// </summary>
public void Launch()
{
try
{
if (string.IsNullOrEmpty(Path))
{
LogHelper.WriteLogToFile("无法启动应用程序:路径为空", LogHelper.LogType.Error);
return;
}
// 检查文件是否存在
if (!File.Exists(Path) && !Path.Contains(":\\"))
{
// 可能是系统命令,如explorer.exe
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = Path,
UseShellExecute = true
};
Process.Start(psi);
}
else
{
// 使用Process.Start启动应用程序
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = Path,
UseShellExecute = true
};
Process.Start(psi);
}
LogHelper.WriteLogToFile($"已启动应用程序: {Path}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动应用程序时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"启动应用程序时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/// <summary>
/// 图标提取工具类
/// </summary>
public static class IconExtractor
{
/// <summary>
/// 从文件中提取图标
/// </summary>
/// <param name="file">文件路径</param>
/// <param name="index">图标索引</param>
/// <param name="largeIcon">是否提取大图标</param>
/// <returns>提取的图标</returns>
public static Icon Extract(string file, int index, bool largeIcon)
{
try
{
IntPtr large;
IntPtr small;
ExtractIconEx(file, index, out large, out small, 1);
try
{
return Icon.FromHandle(largeIcon ? large : small);
}
catch
{
return null;
}
finally
{
if (large != IntPtr.Zero)
DestroyIcon(large);
if (small != IntPtr.Zero)
DestroyIcon(small);
}
}
catch
{
return null;
}
}
[DllImport("Shell32.dll", EntryPoint = "ExtractIconEx")]
private static extern int ExtractIconEx(
[MarshalAs(UnmanagedType.LPStr)] string lpszFile,
int nIconIndex,
out IntPtr phiconLarge,
out IntPtr phiconSmall,
int nIcons);
[DllImport("User32.dll")]
private static extern int DestroyIcon(IntPtr hIcon);
}
}
@@ -0,0 +1,143 @@
<UserControl x:Class="Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher.LauncherSettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="600">
<UserControl.Resources>
<!-- 自定义按钮样式 -->
<Style x:Key="DefaultButtonStyle" TargetType="Button">
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
TextElement.Foreground="{TemplateBinding Foreground}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Opacity" Value="0.8"/>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Black" Direction="270" ShadowDepth="2" Opacity="0.3" BlurRadius="4"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Opacity" Value="0.6"/>
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="0.95" ScaleY="0.95"/>
</Setter.Value>
</Setter>
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="border" Property="Opacity" Value="0.4"/>
<Setter Property="Cursor" Value="Arrow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 标题 -->
<TextBlock Grid.Row="0" Text="超级启动台设置" FontSize="16" FontWeight="Bold" Margin="0,0,0,15" Foreground="Black"/>
<!-- 基本设置 -->
<StackPanel Grid.Row="1" Margin="0,0,0,15">
<TextBlock Text="基本设置" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,10" Foreground="Black"/>
<Grid Margin="10,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 按钮位置 -->
<TextBlock Grid.Row="0" Grid.Column="0" Text="按钮位置:" VerticalAlignment="Center" Foreground="Black"/>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Margin="0,5">
<RadioButton x:Name="RbtnLeft" Content="浮动栏左侧" Margin="0,0,20,0" Checked="RbtnPosition_Checked" Foreground="Black"/>
<RadioButton x:Name="RbtnRight" Content="浮动栏右侧" IsChecked="True" Checked="RbtnPosition_Checked" Foreground="Black"/>
</StackPanel>
</Grid>
</StackPanel>
<!-- 应用管理 -->
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="应用管理" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,10" Foreground="Black"/>
<Border Grid.Row="1" BorderThickness="1" BorderBrush="#CCCCCC" CornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 应用列表 -->
<DataGrid Grid.Row="0" x:Name="DgApps" AutoGenerateColumns="False" Margin="5"
CanUserAddRows="False" CanUserDeleteRows="False"
HeadersVisibility="Column" SelectionMode="Single"
SelectionChanged="DgApps_SelectionChanged">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="显示" Binding="{Binding IsVisible}" Width="50"/>
<DataGridTextColumn Header="名称" Binding="{Binding Name}" Width="150"/>
<DataGridTextColumn Header="路径" Binding="{Binding Path}" Width="*"/>
<DataGridTextColumn Header="位置" Binding="{Binding Position}" Width="50"/>
</DataGrid.Columns>
</DataGrid>
<!-- 操作按钮 -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5">
<Button x:Name="BtnAdd" Content="添加" Padding="10,5" Margin="0,5,5,5" Click="BtnAdd_Click"
Background="#FF007ACC" Foreground="White" BorderBrush="#FF005A9B" BorderThickness="1"
Style="{StaticResource DefaultButtonStyle}"/>
<Button x:Name="BtnEdit" Content="编辑" Padding="10,5" Margin="5" Click="BtnEdit_Click"
Background="#FF6C757D" Foreground="White" BorderBrush="#FF5A6268" BorderThickness="1"
Style="{StaticResource DefaultButtonStyle}"/>
<Button x:Name="BtnDelete" Content="删除" Padding="10,5" Margin="5" Click="BtnDelete_Click"
Background="#FFDC3545" Foreground="White" BorderBrush="#FFBD2130" BorderThickness="1"
Style="{StaticResource DefaultButtonStyle}"/>
</StackPanel>
</Grid>
</Border>
</Grid>
<!-- 底部按钮 -->
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0">
<Button x:Name="BtnSave" Content="保存设置" Padding="15,5" Click="BtnSave_Click"
Background="#FF28A745" Foreground="White" BorderBrush="#FF1E7E34" BorderThickness="1"
Style="{StaticResource DefaultButtonStyle}"/>
</StackPanel>
</Grid>
</UserControl>
@@ -0,0 +1,396 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Ink_Canvas.Windows;
using Microsoft.Win32;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
/// <summary>
/// LauncherSettingsControl.xaml 的交互逻辑
/// </summary>
public partial class LauncherSettingsControl : UserControl
{
/// <summary>
/// 父插件
/// </summary>
private readonly SuperLauncherPlugin _plugin;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="plugin">父插件</param>
public LauncherSettingsControl(SuperLauncherPlugin plugin)
{
InitializeComponent();
_plugin = plugin;
// 设置按钮位置
RbtnLeft.IsChecked = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left;
RbtnRight.IsChecked = _plugin.Config.ButtonPosition == LauncherButtonPosition.Right;
// 绑定应用列表
DgApps.ItemsSource = _plugin.LauncherItems;
// 初始化按钮状态
UpdateButtonStates();
}
/// <summary>
/// 更新按钮状态
/// </summary>
private void UpdateButtonStates()
{
bool hasSelection = DgApps.SelectedItem != null;
BtnEdit.IsEnabled = hasSelection;
BtnDelete.IsEnabled = hasSelection;
}
/// <summary>
/// 位置单选按钮选择事件
/// </summary>
private void RbtnPosition_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded) return;
LauncherButtonPosition oldPosition = _plugin.Config.ButtonPosition;
if (sender == RbtnLeft)
{
_plugin.Config.ButtonPosition = LauncherButtonPosition.Left;
}
else if (sender == RbtnRight)
{
_plugin.Config.ButtonPosition = LauncherButtonPosition.Right;
}
// 如果位置发生变化,更新按钮位置
if (oldPosition != _plugin.Config.ButtonPosition)
{
try
{
// 更新按钮位置
_plugin.UpdateButtonPosition();
// 保存配置
_plugin.SaveConfig();
LogHelper.WriteLogToFile($"启动台按钮位置已更改为: {_plugin.Config.ButtonPosition}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新启动台按钮位置时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"更新启动台按钮位置时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/// <summary>
/// 添加按钮点击事件
/// </summary>
private void BtnAdd_Click(object sender, RoutedEventArgs e)
{
try
{
// 创建新的启动项
LauncherItem item = new LauncherItem
{
Name = "",
Path = "",
IsVisible = true,
Position = -1 // 让插件管理器分配位置
};
// 直接显示编辑对话框
EditLauncherItem(item, true);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"添加启动项时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"添加启动项时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 编辑应用按钮点击事件
/// </summary>
private void BtnEdit_Click(object sender, RoutedEventArgs e)
{
if (DgApps.SelectedItem is LauncherItem item)
{
EditLauncherItem(item, false);
}
}
/// <summary>
/// 删除应用按钮点击事件
/// </summary>
private void BtnDelete_Click(object sender, RoutedEventArgs e)
{
if (DgApps.SelectedItem is LauncherItem item)
{
// 确认删除
MessageBoxResult result = MessageBox.Show(
$"确定要删除 {item.Name} 吗?",
"删除确认",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
// 从集合中移除
_plugin.LauncherItems.Remove(item);
// 保存配置
_plugin.SaveConfig();
}
}
}
/// <summary>
/// 保存设置按钮点击事件
/// </summary>
private void BtnSave_Click(object sender, RoutedEventArgs e)
{
try
{
// 保存配置
_plugin.SaveConfig();
// 如果插件已启用,重新加载启动台按钮
if (_plugin.IsEnabled)
{
_plugin.Disable();
_plugin.Enable();
}
else
{
// 如果插件未启用,则启用它
_plugin.Enable();
// 通知PluginSettingsWindow刷新插件列表
var window = Window.GetWindow(this);
if (window is PluginSettingsWindow pluginSettingsWindow)
{
// 触发刷新
pluginSettingsWindow.RefreshPluginList();
}
}
MessageBox.Show("设置已保存并应用!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存设置时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"保存设置时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 应用项选择变更事件
/// </summary>
private void DgApps_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
UpdateButtonStates();
}
/// <summary>
/// 编辑启动项
/// </summary>
/// <param name="item">启动项</param>
/// <param name="isNew">是否为新建</param>
private void EditLauncherItem(LauncherItem item, bool isNew)
{
// 创建简单的编辑窗口
Window editWindow = new Window
{
Title = isNew ? "添加" : "编辑应用",
Width = 400,
Height = 200,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
ResizeMode = ResizeMode.NoResize
};
// 创建编辑表单
Grid grid = new Grid
{
Margin = new Thickness(20)
};
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
// 名称输入框
TextBlock nameLabel = new TextBlock
{
Text = "名称:",
VerticalAlignment = VerticalAlignment.Center
};
TextBox nameTextBox = new TextBox
{
Text = item.Name,
Margin = new Thickness(0, 5, 0, 5)
};
Grid.SetRow(nameLabel, 0);
Grid.SetColumn(nameLabel, 0);
Grid.SetRow(nameTextBox, 0);
Grid.SetColumn(nameTextBox, 1);
grid.Children.Add(nameLabel);
grid.Children.Add(nameTextBox);
// 路径输入框
TextBlock pathLabel = new TextBlock
{
Text = "路径:",
VerticalAlignment = VerticalAlignment.Center
};
Grid pathGrid = new Grid();
pathGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
pathGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength() });
TextBox pathTextBox = new TextBox
{
Text = item.Path,
Margin = new Thickness(0, 5, 5, 5)
};
Button browseButton = new Button
{
Content = "浏览",
Padding = new Thickness(5, 0, 5, 0),
Margin = new Thickness(0, 5, 0, 5)
};
browseButton.Click += (s, e) =>
{
OpenFileDialog dialog = new OpenFileDialog
{
Title = "选择应用程序",
Filter = "应用程序 (*.exe)|*.exe|所有文件 (*.*)|*.*",
Multiselect = false,
FileName = pathTextBox.Text
};
if (dialog.ShowDialog() == true)
{
pathTextBox.Text = dialog.FileName;
// 如果选择的是.exe文件,自动获取文件名填入名称字段
if (Path.GetExtension(dialog.FileName).ToLower() == ".exe")
{
string fileName = Path.GetFileNameWithoutExtension(dialog.FileName);
// 只有在名称字段为空或者是新建项目时才自动填入
if (string.IsNullOrWhiteSpace(nameTextBox.Text) || isNew)
{
nameTextBox.Text = fileName;
}
}
}
};
Grid.SetColumn(pathTextBox, 0);
Grid.SetColumn(browseButton, 1);
pathGrid.Children.Add(pathTextBox);
pathGrid.Children.Add(browseButton);
Grid.SetRow(pathLabel, 1);
Grid.SetColumn(pathLabel, 0);
Grid.SetRow(pathGrid, 1);
Grid.SetColumn(pathGrid, 1);
grid.Children.Add(pathLabel);
grid.Children.Add(pathGrid);
// 确认和取消按钮
StackPanel buttonPanel = new StackPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Right,
Margin = new Thickness(0, 10, 0, 0)
};
Button okButton = new Button
{
Content = "确定",
Padding = new Thickness(15, 5, 15, 5),
Margin = new Thickness(0, 0, 10, 0),
IsDefault = true
};
Button cancelButton = new Button
{
Content = "取消",
Padding = new Thickness(15, 5, 15, 5),
IsCancel = true
};
okButton.Click += (s, e) =>
{
// 验证输入
if (string.IsNullOrWhiteSpace(nameTextBox.Text))
{
MessageBox.Show("请输入应用名称!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (string.IsNullOrWhiteSpace(pathTextBox.Text))
{
MessageBox.Show("请输入应用路径!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
// 更新项目
item.Name = nameTextBox.Text;
item.Path = pathTextBox.Text;
// 如果是新建,添加到集合
if (isNew)
{
_plugin.AddLauncherItem(item);
}
else
{
// 触发属性变更通知,刷新DataGrid
if (DgApps.ItemsSource is ICollectionView view)
{
view.Refresh();
}
// 保存配置
_plugin.SaveConfig();
}
editWindow.DialogResult = true;
editWindow.Close();
};
cancelButton.Click += (s, e) =>
{
editWindow.DialogResult = false;
editWindow.Close();
};
buttonPanel.Children.Add(okButton);
buttonPanel.Children.Add(cancelButton);
Grid.SetRow(buttonPanel, 2);
Grid.SetColumnSpan(buttonPanel, 2);
grid.Children.Add(buttonPanel);
// 设置窗口内容
editWindow.Content = grid;
// 显示窗口
editWindow.ShowDialog();
}
}
}
@@ -0,0 +1,91 @@
<Window x:Class="Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher.LauncherWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher"
mc:Ignorable="d"
Title="启动台"
Width="400"
Height="300"
WindowStyle="None"
AllowsTransparency="True"
Background="#80000000"
ResizeMode="NoResize"
Topmost="True"
Deactivated="Window_Deactivated"
ShowInTaskbar="False">
<Window.Resources>
<!-- 应用项样式 -->
<Style x:Key="LauncherItemStyle" TargetType="Button">
<Setter Property="Width" Value="80"/>
<Setter Property="Height" Value="80"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Background" Value="#40FFFFFF"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="8">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding Icon}" Width="32" Height="32" Margin="0,10,0,5"/>
<TextBlock Grid.Row="1" Text="{Binding Name}" TextWrapping="Wrap" TextAlignment="Center"
Margin="2,0,2,8" FontSize="11" Foreground="White"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#80FFFFFF"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#C0FFFFFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Border CornerRadius="15" Background="#80000000" BorderThickness="1" BorderBrush="#40FFFFFF">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 标题栏 -->
<Grid Grid.Row="0" Height="40">
<TextBlock Text="启动台" Foreground="White" FontSize="18" FontWeight="Bold"
VerticalAlignment="Center" Margin="15,0,0,0"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,10,0">
<Button x:Name="BtnFixMode" Click="BtnFixMode_Click" Width="30" Height="30"
Margin="5,0" Background="Transparent" BorderThickness="0"
ToolTip="切换固定模式">
<Path x:Name="FixModeIcon" Data="M7,2V13H10V22L17,10H13L17,2H7Z" Fill="White" Stretch="Uniform" Width="16" Height="16"/>
</Button>
<Button x:Name="BtnClose" Click="BtnClose_Click" Width="30" Height="30"
Background="Transparent" BorderThickness="0"
ToolTip="关闭">
<Path Data="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
Fill="White" Stretch="Uniform" Width="16" Height="16"/>
</Button>
</StackPanel>
</Grid>
<!-- 应用网格 -->
<ScrollViewer Grid.Row="1" Margin="10" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<WrapPanel x:Name="AppPanel" Orientation="Horizontal" HorizontalAlignment="Center"/>
</ScrollViewer>
</Grid>
</Border>
</Window>
@@ -0,0 +1,466 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
/// <summary>
/// LauncherWindow.xaml 的交互逻辑
/// </summary>
public partial class LauncherWindow : Window
{
/// <summary>
/// 父插件
/// </summary>
private readonly SuperLauncherPlugin _plugin;
/// <summary>
/// 是否处于固定模式
/// </summary>
private bool _isFixMode;
/// <summary>
/// 应用项按钮列表
/// </summary>
private readonly Dictionary<Button, LauncherItem> _appButtons = new Dictionary<Button, LauncherItem>();
/// <summary>
/// 拖拽中的按钮
/// </summary>
private Button _draggingButton;
/// <summary>
/// 拖拽开始位置
/// </summary>
private Point _dragStartPoint;
/// <summary>
/// 构造函数
/// </summary>
public LauncherWindow(SuperLauncherPlugin plugin)
{
InitializeComponent();
_plugin = plugin;
// 加载应用项
LoadLauncherItems();
// 添加鼠标按下事件(用于拖动窗口)
MouseDown += (s, e) =>
{
if (e.ChangedButton == MouseButton.Left && e.ButtonState == MouseButtonState.Pressed)
{
DragMove();
}
};
// 根据应用数量调整窗口大小
AdjustWindowSize();
}
/// <summary>
/// 加载启动台应用项
/// </summary>
private void LoadLauncherItems()
{
// 清空现有应用项
AppPanel.Children.Clear();
_appButtons.Clear();
// 获取显示的应用项
var visibleItems = _plugin.LauncherItems
.Where(item => item.IsVisible)
.OrderBy(item => item.Position)
.ToList();
foreach (var item in visibleItems)
{
// 创建应用按钮
Button appButton = new Button
{
Style = (Style)FindResource("LauncherItemStyle"),
DataContext = item,
Tag = item.Position
};
// 添加点击事件
appButton.Click += AppButton_Click;
// 在固定模式下,添加拖拽事件
appButton.PreviewMouseDown += AppButton_PreviewMouseDown;
appButton.PreviewMouseMove += AppButton_PreviewMouseMove;
appButton.PreviewMouseUp += AppButton_PreviewMouseUp;
// 记录按钮和项目的对应关系
_appButtons.Add(appButton, item);
// 添加到面板
AppPanel.Children.Add(appButton);
}
}
/// <summary>
/// 根据应用数量调整窗口大小
/// </summary>
private void AdjustWindowSize()
{
try
{
// 每行最多显示4个应用
const int appsPerRow = 4;
// 计算行数
int visibleCount = _appButtons.Count;
int rowCount = (int)Math.Ceiling(visibleCount / (double)appsPerRow);
// 设置窗口宽度(每个应用90像素宽 = 80 + 5*2
Width = Math.Min(appsPerRow * 90 + 40, 400); // 最大宽度400
// 设置窗口高度(每个应用90像素高 = 80 + 5*2
Height = Math.Min(rowCount * 90 + 60, 600); // 最大高度600,标题栏40 + 边距20
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"调整启动台窗口大小时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 应用按钮点击事件
/// </summary>
private void AppButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (_isFixMode) return; // 在固定模式下,不响应点击事件
if (sender is Button button && _appButtons.TryGetValue(button, out LauncherItem item))
{
// 获取应用路径和名称,用于后续启动
string appPath = item.Path;
string appName = item.Name;
LogHelper.WriteLogToFile($"点击启动应用: {appName}, 路径: {appPath}");
// 首先标记窗口正在关闭
IsClosing = true;
// 创建一个应用启动任务
var launchTask = new Task(() =>
{
try
{
// 等待一段时间,确保窗口关闭流程已经开始
Thread.Sleep(200);
// 使用UI线程启动应用
Application.Current.Dispatcher.Invoke(() =>
{
try
{
// 检查应用路径是否存在
if (File.Exists(appPath) || !appPath.Contains(":\\"))
{
// 创建进程启动信息
var psi = new ProcessStartInfo
{
FileName = appPath,
UseShellExecute = true,
};
// 启动应用程序
var process = Process.Start(psi);
LogHelper.WriteLogToFile($"应用程序 {appName} 已启动");
}
else
{
LogHelper.WriteLogToFile($"应用路径不存在: {appPath}", LogHelper.LogType.Error);
MessageBox.Show($"找不到应用程序: {appPath}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动应用程序失败: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"启动应用程序失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用启动任务出错: {ex.Message}", LogHelper.LogType.Error);
}
});
// 关闭窗口
try
{
Dispatcher.BeginInvoke(new Action(() =>
{
try { Close(); } catch { }
// 启动应用程序任务
launchTask.Start();
}), DispatcherPriority.Background);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"关闭窗口或启动任务时出错: {ex.Message}", LogHelper.LogType.Error);
// 如果无法通过UI关闭窗口,直接启动任务
launchTask.Start();
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用按钮点击事件出错: {ex.Message}", LogHelper.LogType.Error);
try { IsClosing = true; Close(); } catch { }
}
}
#region
/// <summary>
/// 应用按钮鼠标按下事件
/// </summary>
private void AppButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (!_isFixMode) return;
if (e.ChangedButton == MouseButton.Left && sender is Button button)
{
_draggingButton = button;
_dragStartPoint = e.GetPosition(AppPanel);
button.CaptureMouse();
button.Opacity = 0.7;
// 阻止事件冒泡,以避免触发按钮点击
e.Handled = true;
}
}
/// <summary>
/// 应用按钮鼠标移动事件
/// </summary>
private void AppButton_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (!_isFixMode || _draggingButton == null) return;
if (e.LeftButton == MouseButtonState.Pressed)
{
Point currentPosition = e.GetPosition(AppPanel);
// 移动按钮
System.Windows.Controls.Canvas.SetLeft(_draggingButton, currentPosition.X - _draggingButton.ActualWidth / 2);
System.Windows.Controls.Canvas.SetTop(_draggingButton, currentPosition.Y - _draggingButton.ActualHeight / 2);
// 将按钮移到最上层
Panel.SetZIndex(_draggingButton, 100);
// 阻止事件冒泡
e.Handled = true;
}
}
/// <summary>
/// 应用按钮鼠标释放事件
/// </summary>
private void AppButton_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (!_isFixMode || _draggingButton == null) return;
// 释放鼠标捕获
_draggingButton.ReleaseMouseCapture();
// 计算新位置
Point releasePoint = e.GetPosition(AppPanel);
int newPosition = CalculateGridPosition(releasePoint);
// 获取当前项目
LauncherItem currentItem = _appButtons[_draggingButton];
// 重新排序
ReorderItems(currentItem, newPosition);
// 重新加载应用项
LoadLauncherItems();
// 保存配置
_plugin.SaveConfig();
// 清除拖拽状态
_draggingButton.Opacity = 1;
Panel.SetZIndex(_draggingButton, 0);
_draggingButton = null;
// 阻止事件冒泡
e.Handled = true;
}
/// <summary>
/// 计算网格位置
/// </summary>
private int CalculateGridPosition(Point point)
{
// 计算行和列
int columnCount = 4; // 每行最多4个应用
int columnWidth = 90; // 应用宽度(包括边距)
int rowHeight = 90; // 应用高度(包括边距)
int column = (int)(point.X / columnWidth);
int row = (int)(point.Y / rowHeight);
// 确保在有效范围内
column = Math.Max(0, Math.Min(column, columnCount - 1));
row = Math.Max(0, row);
// 计算位置索引
return row * columnCount + column;
}
/// <summary>
/// 重新排序应用项
/// </summary>
private void ReorderItems(LauncherItem item, int newPosition)
{
try
{
// 设置项目为固定位置
item.IsPositionFixed = true;
// 如果位置相同,无需调整
if (item.Position == newPosition)
{
return;
}
// 获取所有可见项目
var visibleItems = _plugin.LauncherItems
.Where(i => i.IsVisible)
.OrderBy(i => i.Position)
.ToList();
// 移除当前项目
visibleItems.Remove(item);
// 查找插入位置
int insertIndex = 0;
for (int i = 0; i < visibleItems.Count; i++)
{
if (visibleItems[i].Position >= newPosition)
{
insertIndex = i;
break;
}
insertIndex = i + 1;
}
// 插入项目
visibleItems.Insert(insertIndex, item);
// 重新分配位置
for (int i = 0; i < visibleItems.Count; i++)
{
visibleItems[i].Position = i;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"重新排序应用项时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region
/// <summary>
/// 窗口失去焦点事件
/// </summary>
private void Window_Deactivated(object sender, EventArgs e)
{
try
{
// 只有在非固定模式、窗口已加载、未处于关闭状态且IsLoaded=true时关闭窗口
if (!_isFixMode && IsLoaded && !IsClosing)
{
// 标记为正在关闭
IsClosing = true;
// 使用Dispatcher.BeginInvoke而不是直接调用Close,避免冲突
Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 再次检查窗口状态
if (IsLoaded && !IsClosing)
{
Close();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"延迟关闭窗口时出错: {ex.Message}", LogHelper.LogType.Error);
}
}), DispatcherPriority.Background);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"窗口失去焦点关闭时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 窗口是否正在关闭
/// </summary>
private bool IsClosing { get; set; }
/// <summary>
/// 重写OnClosing方法,标记窗口正在关闭
/// </summary>
protected override void OnClosing(CancelEventArgs e)
{
IsClosing = true;
base.OnClosing(e);
}
/// <summary>
/// 关闭按钮点击事件
/// </summary>
private void BtnClose_Click(object sender, RoutedEventArgs e)
{
Close();
}
/// <summary>
/// 固定模式按钮点击事件
/// </summary>
private void BtnFixMode_Click(object sender, RoutedEventArgs e)
{
// 切换固定模式
_isFixMode = !_isFixMode;
// 更新固定模式按钮图标颜色
FixModeIcon.Fill = _isFixMode ? Brushes.Yellow : Brushes.White;
// 显示提示
if (_isFixMode)
{
MessageBox.Show("已进入固定模式,您可以拖动应用图标调整位置。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
#endregion
}
}
@@ -0,0 +1,589 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
using Newtonsoft.Json;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn
{
/// <summary>
/// 超级启动台插件
/// </summary>
public class SuperLauncherPlugin : PluginBase
{
#region
public override string Name => "超级启动台";
public override string Description => "在浮动栏添加一个启动台按钮,可快速启动常用应用程序。";
public override Version Version => new Version(1, 0, 1);
public override string Author => "ICC CE 团队";
public override bool IsBuiltIn => true;
#endregion
#region
/// <summary>
/// 启动台配置
/// </summary>
public LauncherConfig Config { get; private set; }
/// <summary>
/// 启动台应用程序列表
/// </summary>
public ObservableCollection<LauncherItem> LauncherItems { get; private set; }
/// <summary>
/// 启动台按钮
/// </summary>
private LauncherButton _launcherButton;
/// <summary>
/// 启动台窗口
/// </summary>
private LauncherWindow _launcherWindow;
/// <summary>
/// 配置文件路径
/// </summary>
private readonly string _configPath = Path.Combine(App.RootPath, "PluginConfigs", "SuperLauncher.json");
/// <summary>
/// 标记是否已添加到浮动栏
/// </summary>
private bool _isAddedToFloatingBar;
#endregion
#region
public override void Initialize()
{
try
{
base.Initialize();
// 创建配置目录
string configDir = Path.Combine(App.RootPath, "PluginConfigs");
if (!Directory.Exists(configDir))
{
Directory.CreateDirectory(configDir);
}
// 加载配置
LoadConfig();
LogHelper.WriteLogToFile("超级启动台插件已初始化");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
public override void Enable()
{
try
{
if (IsEnabled) return; // 防止重复启用
// 创建启动台按钮
if (_launcherButton == null)
{
_launcherButton = new LauncherButton(this);
LogHelper.WriteLogToFile("超级启动台按钮已创建");
}
// 添加启动台按钮到浮动栏
AddLauncherButtonToFloatingBar();
// 设置启用状态
base.Enable();
// 保存插件配置
SavePluginSettings();
LogHelper.WriteLogToFile("超级启动台插件已启用");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启用超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
public override void Disable()
{
try
{
if (!IsEnabled) return; // 防止重复禁用
// 从浮动栏移除启动台按钮
RemoveLauncherButtonFromFloatingBar();
// 如果启动台窗口打开,则关闭
if (_launcherWindow != null && _launcherWindow.IsVisible)
{
_launcherWindow.Close();
_launcherWindow = null;
}
// 设置禁用状态
base.Disable();
// 保存插件配置
SavePluginSettings();
LogHelper.WriteLogToFile("超级启动台插件已禁用");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"禁用超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
public override UserControl GetSettingsView()
{
return new LauncherSettingsControl(this);
}
public override void Cleanup()
{
// 保存配置
SaveConfig();
// 从浮动栏移除启动台按钮
RemoveLauncherButtonFromFloatingBar();
// 如果启动台窗口打开,则关闭
if (_launcherWindow != null && _launcherWindow.IsVisible)
{
_launcherWindow.Close();
_launcherWindow = null;
}
base.Cleanup();
}
/// <summary>
/// 保存插件设置
/// </summary>
public override void SavePluginSettings()
{
try
{
// 确保配置已加载
if (Config == null)
{
LoadConfig();
}
// 更新其他设置,但不更改插件启用状态
// 保存配置
SaveConfig();
LogHelper.WriteLogToFile("超级启动台插件设置已保存");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存超级启动台插件设置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region
/// <summary>
/// 加载配置
/// </summary>
private void LoadConfig()
{
try
{
if (File.Exists(_configPath))
{
string json = File.ReadAllText(_configPath);
Config = JsonConvert.DeserializeObject<LauncherConfig>(json) ?? CreateDefaultConfig();
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items ?? new List<LauncherItem>());
// 注意:不再根据配置更改插件启用状态
// 插件状态由PluginManager统一管理
}
else
{
Config = CreateDefaultConfig();
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items);
SaveConfig();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载超级启动台配置时出错: {ex.Message}", LogHelper.LogType.Error);
Config = CreateDefaultConfig();
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items);
}
}
/// <summary>
/// 保存配置
/// </summary>
public void SaveConfig()
{
try
{
// 同步LauncherItems到Config
Config.Items = new List<LauncherItem>(LauncherItems);
// 序列化并保存配置
string json = JsonConvert.SerializeObject(Config, Formatting.Indented);
File.WriteAllText(_configPath, json);
LogHelper.WriteLogToFile("超级启动台配置已保存");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存超级启动台配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 创建默认配置
/// </summary>
private LauncherConfig CreateDefaultConfig()
{
var config = new LauncherConfig
{
ButtonPosition = LauncherButtonPosition.Right,
// 不再使用IsEnabled,插件状态由PluginManager管理
Items = new List<LauncherItem>
{
new LauncherItem
{
Name = "资源管理器",
Path = @"C:\Windows\explorer.exe",
IsVisible = true,
Position = 0
}
}
};
return config;
}
#endregion
#region
/// <summary>
/// 将启动台按钮添加到浮动栏
/// </summary>
private void AddLauncherButtonToFloatingBar()
{
try
{
// 如果已经添加,先移除
if (_isAddedToFloatingBar)
{
RemoveLauncherButtonFromFloatingBar();
_isAddedToFloatingBar = false;
}
// 获取主窗口实例
var mainWindow = Application.Current.MainWindow;
if (mainWindow == null)
{
LogHelper.WriteLogToFile("未找到主窗口实例,无法添加启动台按钮", LogHelper.LogType.Error);
return;
}
// 创建启动台按钮
_launcherButton = new LauncherButton(this);
var buttonElement = _launcherButton.Element;
// 查找浮动栏
var floatingBar = mainWindow.FindName("StackPanelFloatingBar") as Panel;
if (floatingBar == null)
{
// 如果直接查找失败,则尝试遍历可视树查找
Panel floatingBarPanelFromTree = null;
FindStackPanelFloatingBar(mainWindow, ref floatingBarPanelFromTree);
floatingBar = floatingBarPanelFromTree;
}
if (floatingBar == null)
{
LogHelper.WriteLogToFile("未找到浮动栏,无法添加启动台按钮", LogHelper.LogType.Error);
return;
}
// 添加启动台按钮到浮动栏
if (Config.ButtonPosition == LauncherButtonPosition.Left)
{
floatingBar.Children.Insert(0, buttonElement);
LogHelper.WriteLogToFile("启动台按钮已添加到浮动栏左侧");
}
else
{
floatingBar.Children.Add(buttonElement);
LogHelper.WriteLogToFile("启动台按钮已添加到浮动栏右侧");
}
_isAddedToFloatingBar = true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"添加启动台按钮到浮动栏时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 递归查找StackPanelFloatingBar
/// </summary>
private void FindStackPanelFloatingBar(DependencyObject parent, ref Panel result)
{
if (parent == null || result != null) return;
try
{
// 检查当前对象是否为我们要找的面板
if (parent is Panel panel && panel.Name == "StackPanelFloatingBar")
{
result = panel;
return;
}
// 获取子元素数量
int childCount = VisualTreeHelper.GetChildrenCount(parent);
// 遍历所有子元素
for (int i = 0; i < childCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
FindStackPanelFloatingBar(child, ref result);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"查找StackPanelFloatingBar时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 从浮动栏移除启动台按钮
/// </summary>
private void RemoveLauncherButtonFromFloatingBar()
{
try
{
if (!_isAddedToFloatingBar || _launcherButton == null)
{
return;
}
// 获取主窗口实例
var mainWindow = Application.Current.MainWindow;
if (mainWindow == null)
{
LogHelper.WriteLogToFile("未找到主窗口实例,无法移除启动台按钮", LogHelper.LogType.Error);
return;
}
// 获取按钮元素
var buttonElement = _launcherButton.Element;
// 查找浮动栏
var floatingBar = mainWindow.FindName("StackPanelFloatingBar") as Panel;
if (floatingBar == null)
{
// 如果直接查找失败,则尝试遍历可视树查找
Panel floatingBarPanelFromTree = null;
FindStackPanelFloatingBar(mainWindow, ref floatingBarPanelFromTree);
floatingBar = floatingBarPanelFromTree;
}
if (floatingBar == null)
{
LogHelper.WriteLogToFile("未找到浮动栏,无法移除启动台按钮", LogHelper.LogType.Error);
return;
}
// 从浮动栏移除启动台按钮
if (floatingBar.Children.Contains(buttonElement))
{
floatingBar.Children.Remove(buttonElement);
LogHelper.WriteLogToFile("启动台按钮已从浮动栏移除");
}
_isAddedToFloatingBar = false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"移除启动台按钮时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 更新启动台按钮位置
/// </summary>
public void UpdateButtonPosition()
{
try
{
// 如果按钮已添加到浮动栏,重新添加以更新位置
if (_isAddedToFloatingBar)
{
RemoveLauncherButtonFromFloatingBar();
AddLauncherButtonToFloatingBar();
LogHelper.WriteLogToFile($"启动台按钮位置已更新为: {Config.ButtonPosition}");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新启动台按钮位置时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
}
}
#endregion
#region
/// <summary>
/// 显示启动台窗口
/// </summary>
/// <param name="buttonPosition">按钮在屏幕上的位置</param>
public void ShowLauncherWindow(Point buttonPosition)
{
try
{
// 如果窗口已存在,关闭它
if (_launcherWindow != null && _launcherWindow.IsVisible)
{
_launcherWindow.Close();
_launcherWindow = null;
return;
}
// 创建新的启动台窗口
_launcherWindow = new LauncherWindow(this);
// 计算窗口位置,使其位于按钮上方
PositionLauncherWindow(_launcherWindow, buttonPosition);
// 显示窗口
_launcherWindow.Show();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"显示启动台窗口时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 设置启动台窗口位置
/// </summary>
/// <param name="window">启动台窗口</param>
/// <param name="buttonPosition">按钮在屏幕上的位置</param>
private void PositionLauncherWindow(LauncherWindow window, Point buttonPosition)
{
// 确保窗口已加载
if (window.ActualWidth == 0 || window.ActualHeight == 0)
{
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
// 设置窗口加载完成后的位置
window.Loaded += (s, e) =>
{
// 窗口位于按钮上方居中
double left = buttonPosition.X - (window.ActualWidth / 2);
double top = buttonPosition.Y - window.ActualHeight - 10; // 在按钮上方留出一些间距
// 确保窗口在屏幕内
left = Math.Max(0, Math.Min(left, SystemParameters.WorkArea.Width - window.ActualWidth));
top = Math.Max(0, Math.Min(top, SystemParameters.WorkArea.Height - window.ActualHeight));
window.Left = left;
window.Top = top;
};
}
else
{
// 窗口位于按钮上方居中
double left = buttonPosition.X - (window.ActualWidth / 2);
double top = buttonPosition.Y - window.ActualHeight - 10; // 在按钮上方留出一些间距
// 确保窗口在屏幕内
left = Math.Max(0, Math.Min(left, SystemParameters.WorkArea.Width - window.ActualWidth));
top = Math.Max(0, Math.Min(top, SystemParameters.WorkArea.Height - window.ActualHeight));
window.Left = left;
window.Top = top;
}
}
/// <summary>
/// 添加应用到启动台
/// </summary>
/// <param name="item">启动台项</param>
public void AddLauncherItem(LauncherItem item)
{
// 如果项目数量已达上限,则不添加
if (LauncherItems.Count >= 40)
{
MessageBox.Show("启动台项目数量已达上限(40个)!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 寻找合适的位置
if (item.Position < 0)
{
item.Position = FindNextAvailablePosition();
}
// 添加项目并保存配置
LauncherItems.Add(item);
SaveConfig();
}
/// <summary>
/// 查找下一个可用位置
/// </summary>
private int FindNextAvailablePosition()
{
// 获取已使用的位置列表
var usedPositions = new HashSet<int>();
foreach (var item in LauncherItems)
{
usedPositions.Add(item.Position);
}
// 查找第一个可用位置
for (int i = 0; i < 40; i++)
{
if (!usedPositions.Contains(i))
{
return i;
}
}
// 如果所有位置都已使用,则返回0
return 0;
}
#endregion
}
}
@@ -0,0 +1,92 @@
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 增强的插件基类,提供对插件服务的访问和基本实现
/// </summary>
public abstract class EnhancedPluginBase : PluginBase, IEnhancedPlugin
{
/// <summary>
/// 插件服务实例
/// </summary>
public IPluginService PluginService { get; private set; }
/// <summary>
/// 构造函数
/// </summary>
protected EnhancedPluginBase()
{
PluginService = PluginServiceManager.Instance;
}
/// <summary>
/// 插件启动时调用,在Initialize之后
/// </summary>
public virtual void OnStartup()
{
LogHelper.WriteLogToFile($"插件 {Name} 已启动");
}
/// <summary>
/// 插件关闭时调用,在Cleanup之前
/// </summary>
public virtual void OnShutdown()
{
LogHelper.WriteLogToFile($"插件 {Name} 正在关闭");
}
/// <summary>
/// 获取插件的菜单项
/// </summary>
/// <returns>菜单项集合</returns>
public virtual MenuItem[] GetMenuItems()
{
return new MenuItem[0];
}
/// <summary>
/// 获取插件的工具栏按钮
/// </summary>
/// <returns>工具栏按钮集合</returns>
public virtual Button[] GetToolbarButtons()
{
return new Button[0];
}
/// <summary>
/// 获取插件的状态栏信息
/// </summary>
/// <returns>状态栏信息</returns>
public virtual string GetStatusBarInfo()
{
return $"{Name} v{Version} - {(IsEnabled ? "" : "")}";
}
/// <summary>
/// 插件配置变更时调用
/// </summary>
public virtual void OnConfigurationChanged()
{
LogHelper.WriteLogToFile($"插件 {Name} 配置已变更");
}
/// <summary>
/// 重写初始化方法,调用OnStartup
/// </summary>
public override void Initialize()
{
base.Initialize();
OnStartup();
}
/// <summary>
/// 重写清理方法,调用OnShutdown
/// </summary>
public override void Cleanup()
{
OnShutdown();
base.Cleanup();
}
}
}
@@ -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;
/// <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} 已初始化");
_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} 已启用");
}
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} 已禁用");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"禁用 ICCPP 插件 {Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 清理插件资源
/// </summary>
public override void Cleanup()
{
try
{
// 这里可以添加 .iccpp 插件的清理逻辑
// 例如,释放资源等
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已清理资源");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理 ICCPP 插件 {Name} 资源时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
}
}
@@ -0,0 +1,48 @@
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 增强的插件接口,提供对插件服务的访问
/// </summary>
public interface IEnhancedPlugin : IPlugin
{
/// <summary>
/// 获取插件服务实例
/// </summary>
IPluginService PluginService { get; }
/// <summary>
/// 插件启动时调用,在Initialize之后
/// </summary>
void OnStartup();
/// <summary>
/// 插件关闭时调用,在Cleanup之前
/// </summary>
void OnShutdown();
/// <summary>
/// 获取插件的菜单项
/// </summary>
/// <returns>菜单项集合</returns>
MenuItem[] GetMenuItems();
/// <summary>
/// 获取插件的工具栏按钮
/// </summary>
/// <returns>工具栏按钮集合</returns>
Button[] GetToolbarButtons();
/// <summary>
/// 获取插件的状态栏信息
/// </summary>
/// <returns>状态栏信息</returns>
string GetStatusBarInfo();
/// <summary>
/// 插件配置变更时调用
/// </summary>
void OnConfigurationChanged();
}
}
+67
View File
@@ -0,0 +1,67 @@
using System;
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 定义插件的基本接口
/// </summary>
public interface IPlugin
{
/// <summary>
/// 插件名称
/// </summary>
string Name { get; }
/// <summary>
/// 插件描述
/// </summary>
string Description { get; }
/// <summary>
/// 插件版本
/// </summary>
Version Version { get; }
/// <summary>
/// 插件作者
/// </summary>
string Author { get; }
/// <summary>
/// 是否为内置插件
/// </summary>
bool IsBuiltIn { get; }
/// <summary>
/// 初始化插件
/// 此方法在插件加载时被调用,用于执行一些初始化工作
/// </summary>
void Initialize();
/// <summary>
/// 启用插件
/// 此方法在插件被用户或系统启用时调用,激活插件功能
/// </summary>
void Enable();
/// <summary>
/// 禁用插件
/// 此方法在插件被用户或系统禁用时调用,停用插件功能
/// </summary>
void Disable();
/// <summary>
/// 获取插件设置界面
/// 此方法返回插件的设置界面控件,用于展示在设置窗口
/// </summary>
/// <returns>插件设置界面</returns>
UserControl GetSettingsView();
/// <summary>
/// 插件卸载时的清理工作
/// 此方法在插件被卸载前调用,用于释放资源和执行清理
/// </summary>
void Cleanup();
}
}
@@ -0,0 +1,557 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件服务接口,提供对软件内部功能的访问
/// </summary>
public interface IPluginService
{
#region UI访问
/// <summary>
/// 获取主窗口引用
/// </summary>
Window MainWindow { get; }
/// <summary>
/// 获取当前画布
/// </summary>
InkCanvas CurrentCanvas { get; }
/// <summary>
/// 获取所有画布页面
/// </summary>
List<Canvas> AllCanvasPages { get; }
/// <summary>
/// 获取当前页面索引
/// </summary>
int CurrentPageIndex { get; }
/// <summary>
/// 获取当前页面数量
/// </summary>
int TotalPageCount { get; }
/// <summary>
/// 获取浮动工具栏
/// </summary>
FrameworkElement FloatingToolBar { get; }
/// <summary>
/// 获取左侧面板
/// </summary>
FrameworkElement LeftPanel { get; }
/// <summary>
/// 获取右侧面板
/// </summary>
FrameworkElement RightPanel { get; }
/// <summary>
/// 获取顶部面板
/// </summary>
FrameworkElement TopPanel { get; }
/// <summary>
/// 获取底部面板
/// </summary>
FrameworkElement BottomPanel { get; }
#endregion
#region
/// <summary>
/// 获取当前绘制模式
/// </summary>
int CurrentDrawingMode { get; }
/// <summary>
/// 获取当前笔触宽度
/// </summary>
double CurrentInkWidth { get; }
/// <summary>
/// 获取当前笔触颜色
/// </summary>
Color CurrentInkColor { get; }
/// <summary>
/// 获取当前高亮笔宽度
/// </summary>
double CurrentHighlighterWidth { get; }
/// <summary>
/// 获取当前橡皮擦大小
/// </summary>
int CurrentEraserSize { get; }
/// <summary>
/// 获取当前橡皮擦类型
/// </summary>
int CurrentEraserType { get; }
/// <summary>
/// 获取当前橡皮擦形状
/// </summary>
int CurrentEraserShape { get; }
/// <summary>
/// 获取当前笔触透明度
/// </summary>
double CurrentInkAlpha { get; }
/// <summary>
/// 获取当前笔触样式
/// </summary>
int CurrentInkStyle { get; }
/// <summary>
/// 获取当前背景颜色
/// </summary>
string CurrentBackgroundColor { get; }
#endregion
#region
/// <summary>
/// 获取当前主题模式
/// </summary>
bool IsDarkTheme { get; }
/// <summary>
/// 获取当前是否为白板模式
/// </summary>
bool IsWhiteboardMode { get; }
/// <summary>
/// 获取当前是否为PPT模式
/// </summary>
bool IsPPTMode { get; }
/// <summary>
/// 获取当前是否为全屏模式
/// </summary>
bool IsFullScreenMode { get; }
/// <summary>
/// 获取当前是否为画板模式
/// </summary>
bool IsCanvasMode { get; }
/// <summary>
/// 获取当前是否为选择模式
/// </summary>
bool IsSelectionMode { get; }
/// <summary>
/// 获取当前是否为擦除模式
/// </summary>
bool IsEraserMode { get; }
/// <summary>
/// 获取当前是否为形状绘制模式
/// </summary>
bool IsShapeDrawingMode { get; }
/// <summary>
/// 获取当前是否为高亮模式
/// </summary>
bool IsHighlighterMode { get; }
#endregion
#region
/// <summary>
/// 清除当前画布
/// </summary>
void ClearCanvas();
/// <summary>
/// 清除所有画布
/// </summary>
void ClearAllCanvases();
/// <summary>
/// 添加新页面
/// </summary>
void AddNewPage();
/// <summary>
/// 删除当前页面
/// </summary>
void DeleteCurrentPage();
/// <summary>
/// 切换到指定页面
/// </summary>
/// <param name="pageIndex">页面索引</param>
void SwitchToPage(int pageIndex);
/// <summary>
/// 切换到下一页
/// </summary>
void NextPage();
/// <summary>
/// 切换到上一页
/// </summary>
void PreviousPage();
#endregion
#region
/// <summary>
/// 设置绘制模式
/// </summary>
/// <param name="mode">绘制模式</param>
void SetDrawingMode(int mode);
/// <summary>
/// 设置笔触宽度
/// </summary>
/// <param name="width">宽度</param>
void SetInkWidth(double width);
/// <summary>
/// 设置笔触颜色
/// </summary>
/// <param name="color">颜色</param>
void SetInkColor(Color color);
/// <summary>
/// 设置高亮笔宽度
/// </summary>
/// <param name="width">宽度</param>
void SetHighlighterWidth(double width);
/// <summary>
/// 设置橡皮擦大小
/// </summary>
/// <param name="size">大小</param>
void SetEraserSize(int size);
/// <summary>
/// 设置橡皮擦类型
/// </summary>
/// <param name="type">类型</param>
void SetEraserType(int type);
/// <summary>
/// 设置橡皮擦形状
/// </summary>
/// <param name="shape">形状</param>
void SetEraserShape(int shape);
/// <summary>
/// 设置笔触透明度
/// </summary>
/// <param name="alpha">透明度</param>
void SetInkAlpha(double alpha);
/// <summary>
/// 设置笔触样式
/// </summary>
/// <param name="style">样式</param>
void SetInkStyle(int style);
/// <summary>
/// 设置背景颜色
/// </summary>
/// <param name="color">颜色</param>
void SetBackgroundColor(string color);
#endregion
#region
/// <summary>
/// 保存画布内容
/// </summary>
/// <param name="filePath">文件路径</param>
void SaveCanvas(string filePath);
/// <summary>
/// 加载画布内容
/// </summary>
/// <param name="filePath">文件路径</param>
void LoadCanvas(string filePath);
/// <summary>
/// 导出为图片
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="format">图片格式</param>
void ExportAsImage(string filePath, string format);
/// <summary>
/// 导出为PDF
/// </summary>
/// <param name="filePath">文件路径</param>
void ExportAsPDF(string filePath);
#endregion
#region
/// <summary>
/// 撤销操作
/// </summary>
void Undo();
/// <summary>
/// 重做操作
/// </summary>
void Redo();
/// <summary>
/// 是否可以撤销
/// </summary>
bool CanUndo { get; }
/// <summary>
/// 是否可以重做
/// </summary>
bool CanRedo { get; }
#endregion
#region
/// <summary>
/// 全选
/// </summary>
void SelectAll();
/// <summary>
/// 取消选择
/// </summary>
void DeselectAll();
/// <summary>
/// 删除选中内容
/// </summary>
void DeleteSelected();
/// <summary>
/// 复制选中内容
/// </summary>
void CopySelected();
/// <summary>
/// 剪切选中内容
/// </summary>
void CutSelected();
/// <summary>
/// 粘贴内容
/// </summary>
void Paste();
#endregion
#region
/// <summary>
/// 显示设置窗口
/// </summary>
void ShowSettingsWindow();
/// <summary>
/// 隐藏设置窗口
/// </summary>
void HideSettingsWindow();
/// <summary>
/// 显示插件设置窗口
/// </summary>
void ShowPluginSettingsWindow();
/// <summary>
/// 隐藏插件设置窗口
/// </summary>
void HidePluginSettingsWindow();
/// <summary>
/// 显示帮助窗口
/// </summary>
void ShowHelpWindow();
/// <summary>
/// 隐藏帮助窗口
/// </summary>
void HideHelpWindow();
/// <summary>
/// 显示关于窗口
/// </summary>
void ShowAboutWindow();
/// <summary>
/// 隐藏关于窗口
/// </summary>
void HideAboutWindow();
#endregion
#region
/// <summary>
/// 显示通知消息
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="type">消息类型</param>
void ShowNotification(string message, NotificationType type = NotificationType.Info);
/// <summary>
/// 显示确认对话框
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="title">标题</param>
/// <returns>用户选择结果</returns>
bool ShowConfirmDialog(string message, string title = "确认");
/// <summary>
/// 显示输入对话框
/// </summary>
/// <param name="message">提示消息</param>
/// <param name="title">标题</param>
/// <param name="defaultValue">默认值</param>
/// <returns>用户输入内容</returns>
string ShowInputDialog(string message, string title = "输入", string defaultValue = "");
#endregion
#region
/// <summary>
/// 获取系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>设置值</returns>
T GetSetting<T>(string key, T defaultValue = default(T));
/// <summary>
/// 设置系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="value">设置值</param>
void SetSetting<T>(string key, T value);
/// <summary>
/// 保存设置到文件
/// </summary>
void SaveSettings();
/// <summary>
/// 从文件加载设置
/// </summary>
void LoadSettings();
/// <summary>
/// 重置设置为默认值
/// </summary>
void ResetSettings();
#endregion
#region
/// <summary>
/// 获取所有已加载的插件
/// </summary>
/// <returns>插件列表</returns>
List<IPlugin> GetAllPlugins();
/// <summary>
/// 获取指定插件
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <returns>插件实例</returns>
IPlugin GetPlugin(string pluginName);
/// <summary>
/// 启用插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void EnablePlugin(string pluginName);
/// <summary>
/// 禁用插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void DisablePlugin(string pluginName);
/// <summary>
/// 卸载插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void UnloadPlugin(string pluginName);
#endregion
#region
/// <summary>
/// 注册事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
void RegisterEventHandler(string eventName, EventHandler handler);
/// <summary>
/// 注销事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
void UnregisterEventHandler(string eventName, EventHandler handler);
/// <summary>
/// 触发事件
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="sender">事件发送者</param>
/// <param name="args">事件参数</param>
void TriggerEvent(string eventName, object sender, EventArgs args);
#endregion
}
/// <summary>
/// 通知类型枚举
/// </summary>
public enum NotificationType
{
/// <summary>
/// 信息
/// </summary>
Info,
/// <summary>
/// 成功
/// </summary>
Success,
/// <summary>
/// 警告
/// </summary>
Warning,
/// <summary>
/// 错误
/// </summary>
Error
}
}
+161
View File
@@ -0,0 +1,161 @@
using System;
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件基类,提供基本实现
/// </summary>
public abstract class PluginBase : IPlugin
{
/// <summary>
/// 插件状态(私有字段)
/// </summary>
private bool _isEnabled;
/// <summary>
/// 插件状态(公共属性)
/// </summary>
public bool IsEnabled
{
get => _isEnabled;
protected set
{
if (_isEnabled != value)
{
_isEnabled = value;
OnEnabledStateChanged(value);
}
}
}
/// <summary>
/// 插件ID
/// </summary>
public string Id { get; protected set; }
/// <summary>
/// 插件路径
/// </summary>
public string PluginPath { get; set; }
/// <summary>
/// 插件名称
/// </summary>
public abstract string Name { get; }
/// <summary>
/// 插件描述
/// </summary>
public abstract string Description { get; }
/// <summary>
/// 插件版本
/// </summary>
public abstract Version Version { get; }
/// <summary>
/// 插件作者
/// </summary>
public abstract string Author { get; }
/// <summary>
/// 是否为内置插件
/// </summary>
public virtual bool IsBuiltIn => false;
/// <summary>
/// 状态变更事件
/// </summary>
public event EventHandler<bool> EnabledStateChanged;
/// <summary>
/// 初始化插件
/// </summary>
public virtual void Initialize()
{
Id = GetType().FullName;
// 添加日志,记录插件名称
try
{
string name = Name;
LogHelper.WriteLogToFile($"初始化插件: ID={Id}, 名称={name ?? ""}");
if (string.IsNullOrEmpty(name))
{
LogHelper.WriteLogToFile($"警告: 插件 {Id} 的名称为空", LogHelper.LogType.Warning);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取插件名称时出错: {ex.Message}", LogHelper.LogType.Error);
}
LogHelper.WriteLogToFile($"插件 {Name} 已初始化");
}
/// <summary>
/// 启用插件
/// </summary>
public virtual void Enable()
{
if (!IsEnabled)
{
IsEnabled = true;
LogHelper.WriteLogToFile($"插件 {Name} 已启用");
}
}
/// <summary>
/// 禁用插件
/// </summary>
public virtual void Disable()
{
if (IsEnabled)
{
IsEnabled = false;
LogHelper.WriteLogToFile($"插件 {Name} 已禁用");
}
}
/// <summary>
/// 获取插件设置界面
/// </summary>
/// <returns>插件设置界面</returns>
public virtual UserControl GetSettingsView()
{
// 默认返回空设置页面
return new UserControl();
}
/// <summary>
/// 插件卸载时的清理工作
/// </summary>
public virtual void Cleanup()
{
LogHelper.WriteLogToFile($"插件 {Name} 已卸载");
}
/// <summary>
/// 保存插件自身的设置
/// 注意:此方法仅用于保存插件的特定设置,不应影响插件启用/禁用状态
/// 插件启用状态由PluginManager统一管理
/// </summary>
public virtual void SavePluginSettings()
{
// 默认实现不做任何事情
// 子类可以重写此方法,将自身设置保存到配置文件中
LogHelper.WriteLogToFile($"插件 {Name} 设置已保存", LogHelper.LogType.Event);
}
/// <summary>
/// 触发状态变更事件
/// </summary>
/// <param name="isEnabled">是否启用</param>
protected virtual void OnEnabledStateChanged(bool isEnabled)
{
EnabledStateChanged?.Invoke(this, isEnabled);
}
}
}
@@ -0,0 +1,273 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件配置管理器,允许插件管理自己的配置
/// </summary>
public class PluginConfigurationManager
{
private static readonly string PluginConfigDirectory = Path.Combine(App.RootPath, "PluginConfigs");
private static readonly Dictionary<string, Dictionary<string, object>> _pluginConfigs = new Dictionary<string, Dictionary<string, object>>();
private static readonly object _lockObject = new object();
static PluginConfigurationManager()
{
// 确保配置目录存在
if (!Directory.Exists(PluginConfigDirectory))
{
Directory.CreateDirectory(PluginConfigDirectory);
}
}
/// <summary>
/// 获取插件配置值
/// </summary>
/// <typeparam name="T">配置值类型</typeparam>
/// <param name="pluginName">插件名称</param>
/// <param name="key">配置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>配置值</returns>
public static T GetConfiguration<T>(string pluginName, string key, T defaultValue = default(T))
{
lock (_lockObject)
{
try
{
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
{
if (pluginConfig.TryGetValue(key, out var value))
{
if (value is T typedValue)
{
return typedValue;
}
// 尝试类型转换
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
return defaultValue;
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
return defaultValue;
}
}
/// <summary>
/// 设置插件配置值
/// </summary>
/// <typeparam name="T">配置值类型</typeparam>
/// <param name="pluginName">插件名称</param>
/// <param name="key">配置键</param>
/// <param name="value">配置值</param>
public static void SetConfiguration<T>(string pluginName, string key, T value)
{
lock (_lockObject)
{
try
{
if (!_pluginConfigs.ContainsKey(pluginName))
{
_pluginConfigs[pluginName] = new Dictionary<string, object>();
}
_pluginConfigs[pluginName][key] = value;
// 异步保存配置
Task.Run(() => SavePluginConfiguration(pluginName));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"设置插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 删除插件配置
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <param name="key">配置键</param>
public static void RemoveConfiguration(string pluginName, string key)
{
lock (_lockObject)
{
try
{
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
{
if (pluginConfig.Remove(key))
{
// 异步保存配置
Task.Run(() => SavePluginConfiguration(pluginName));
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"删除插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 获取插件的所有配置
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <returns>配置字典</returns>
public static Dictionary<string, object> GetAllConfigurations(string pluginName)
{
lock (_lockObject)
{
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
{
return new Dictionary<string, object>(pluginConfig);
}
return new Dictionary<string, object>();
}
}
/// <summary>
/// 清除插件的所有配置
/// </summary>
/// <param name="pluginName">插件名称</param>
public static void ClearAllConfigurations(string pluginName)
{
lock (_lockObject)
{
try
{
if (_pluginConfigs.Remove(pluginName))
{
// 删除配置文件
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
if (File.Exists(configFile))
{
File.Delete(configFile);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除插件 {pluginName} 所有配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 加载插件配置
/// </summary>
/// <param name="pluginName">插件名称</param>
public static void LoadPluginConfiguration(string pluginName)
{
try
{
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
if (File.Exists(configFile))
{
string json = File.ReadAllText(configFile);
var config = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
lock (_lockObject)
{
_pluginConfigs[pluginName] = config ?? new Dictionary<string, object>();
}
LogHelper.WriteLogToFile($"已加载插件 {pluginName} 的配置");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载插件 {pluginName} 配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 保存插件配置
/// </summary>
/// <param name="pluginName">插件名称</param>
private static void SavePluginConfiguration(string pluginName)
{
try
{
Dictionary<string, object> pluginConfig;
lock (_lockObject)
{
if (!_pluginConfigs.TryGetValue(pluginName, out pluginConfig))
{
return;
}
}
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
string json = JsonConvert.SerializeObject(pluginConfig, Formatting.Indented);
File.WriteAllText(configFile, json);
LogHelper.WriteLogToFile($"已保存插件 {pluginName} 的配置");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存插件 {pluginName} 配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 加载所有插件的配置
/// </summary>
public static void LoadAllPluginConfigurations()
{
try
{
if (Directory.Exists(PluginConfigDirectory))
{
string[] configFiles = Directory.GetFiles(PluginConfigDirectory, "*.json");
foreach (string configFile in configFiles)
{
string pluginName = Path.GetFileNameWithoutExtension(configFile);
LoadPluginConfiguration(pluginName);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载所有插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 保存所有插件的配置
/// </summary>
public static void SaveAllPluginConfigurations()
{
try
{
lock (_lockObject)
{
foreach (string pluginName in _pluginConfigs.Keys)
{
SavePluginConfiguration(pluginName);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存所有插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,455 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件服务管理器,实现IPluginService接口,提供对软件内部功能的访问
/// </summary>
public class PluginServiceManager : IPluginService
{
private static PluginServiceManager _instance;
private MainWindow _mainWindow;
private Dictionary<string, EventHandler> _eventHandlers;
/// <summary>
/// 单例实例
/// </summary>
public static PluginServiceManager Instance
{
get
{
if (_instance == null)
{
_instance = new PluginServiceManager();
}
return _instance;
}
}
private PluginServiceManager()
{
_eventHandlers = new Dictionary<string, EventHandler>();
}
/// <summary>
/// 设置主窗口引用
/// </summary>
/// <param name="mainWindow">主窗口实例</param>
public void SetMainWindow(MainWindow mainWindow)
{
_mainWindow = mainWindow;
}
#region UI访问
public Window MainWindow => _mainWindow;
public InkCanvas CurrentCanvas => null; // 暂时返回null,避免访问权限问题
public List<Canvas> AllCanvasPages => new List<Canvas>(); // 暂时返回空列表
public int CurrentPageIndex => 0; // 暂时返回0
public int TotalPageCount => 0; // 暂时返回0
public FrameworkElement FloatingToolBar => _mainWindow?.ViewboxFloatingBar;
public FrameworkElement LeftPanel => _mainWindow?.BlackboardLeftSide;
public FrameworkElement RightPanel => _mainWindow?.BlackboardRightSide;
public FrameworkElement TopPanel => _mainWindow?.BorderTools;
public FrameworkElement BottomPanel => _mainWindow?.BorderSettings;
#endregion
#region
public int CurrentDrawingMode => 0; // 暂时返回0
public double CurrentInkWidth => 2.5; // 暂时返回默认值
public Color CurrentInkColor => Colors.Black; // 暂时返回默认值
public double CurrentHighlighterWidth => 20.0; // 暂时返回默认值
public int CurrentEraserSize => 2; // 暂时返回默认值
public int CurrentEraserType => 0; // 暂时返回默认值
public int CurrentEraserShape => 0; // 暂时返回默认值
public double CurrentInkAlpha => 255.0; // 暂时返回默认值
public int CurrentInkStyle => 0; // 暂时返回默认值
public string CurrentBackgroundColor => "#162924"; // 暂时返回默认值
#endregion
#region
public bool IsDarkTheme => false; // 暂时返回默认值
public bool IsWhiteboardMode => false; // 暂时返回默认值
public bool IsPPTMode => false; // 暂时返回默认值
public bool IsFullScreenMode => false; // 暂时返回默认值
public bool IsCanvasMode => true; // 暂时返回默认值
public bool IsSelectionMode => false; // 暂时返回默认值
public bool IsEraserMode => false; // 暂时返回默认值
public bool IsShapeDrawingMode => false; // 暂时返回默认值
public bool IsHighlighterMode => false; // 暂时返回默认值
#endregion
#region
public void ClearCanvas()
{
// 暂时不实现,避免访问权限问题
}
public void ClearAllCanvases()
{
// 暂时不实现,避免访问权限问题
}
public void AddNewPage()
{
// 暂时不实现,避免访问权限问题
}
public void DeleteCurrentPage()
{
// 暂时不实现,避免访问权限问题
}
public void SwitchToPage(int pageIndex)
{
// 暂时不实现,避免访问权限问题
}
public void NextPage()
{
// 暂时不实现,避免访问权限问题
}
public void PreviousPage()
{
// 暂时不实现,避免访问权限问题
}
#endregion
#region
public void SetDrawingMode(int mode)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkWidth(double width)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkColor(Color color)
{
// 暂时不实现,避免访问权限问题
}
public void SetHighlighterWidth(double width)
{
// 暂时不实现,避免访问权限问题
}
public void SetEraserSize(int size)
{
// 暂时不实现,避免访问权限问题
}
public void SetEraserType(int type)
{
// 暂时不实现,避免访问权限问题
}
public void SetEraserShape(int shape)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkAlpha(double alpha)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkStyle(int style)
{
// 暂时不实现,避免访问权限问题
}
public void SetBackgroundColor(string color)
{
// 暂时不实现,避免访问权限问题
}
#endregion
#region
public void SaveCanvas(string filePath)
{
// 暂时不实现,避免访问权限问题
}
public void LoadCanvas(string filePath)
{
// 暂时不实现,避免访问权限问题
}
public void ExportAsImage(string filePath, string format)
{
// 暂时不实现,避免访问权限问题
}
public void ExportAsPDF(string filePath)
{
// 暂时不实现,避免访问权限问题
}
#endregion
#region
public void Undo()
{
// 暂时不实现,避免访问权限问题
}
public void Redo()
{
// 暂时不实现,避免访问权限问题
}
public bool CanUndo => false; // 暂时返回默认值
public bool CanRedo => false; // 暂时返回默认值
#endregion
#region
public void SelectAll()
{
// 暂时不实现,避免访问权限问题
}
public void DeselectAll()
{
// 暂时不实现,避免访问权限问题
}
public void DeleteSelected()
{
// 暂时不实现,避免访问权限问题
}
public void CopySelected()
{
// 暂时不实现,避免访问权限问题
}
public void CutSelected()
{
// 暂时不实现,避免访问权限问题
}
public void Paste()
{
// 暂时不实现,避免访问权限问题
}
#endregion
#region
public void ShowSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HideSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowPluginSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HidePluginSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowHelpWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HideHelpWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowAboutWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HideAboutWindow()
{
// 暂时不实现,避免访问权限问题
}
#endregion
#region
public void ShowNotification(string message, NotificationType type = NotificationType.Info)
{
// 暂时不实现,避免访问权限问题
}
public bool ShowConfirmDialog(string message, string title = "确认")
{
// 暂时不实现,避免访问权限问题
return false;
}
public string ShowInputDialog(string message, string title = "输入", string defaultValue = "")
{
// 暂时不实现,避免访问权限问题
return defaultValue;
}
#endregion
#region
public T GetSetting<T>(string key, T defaultValue = default(T))
{
// 暂时不实现,避免访问权限问题
return defaultValue;
}
public void SetSetting<T>(string key, T value)
{
// 暂时不实现,避免访问权限问题
}
public void SaveSettings()
{
// 暂时不实现,避免访问权限问题
}
public void LoadSettings()
{
// 暂时不实现,避免访问权限问题
}
public void ResetSettings()
{
// 暂时不实现,避免访问权限问题
}
#endregion
#region
public List<IPlugin> GetAllPlugins()
{
return new List<IPlugin>(PluginManager.Instance.Plugins);
}
public IPlugin GetPlugin(string pluginName)
{
return PluginManager.Instance.Plugins.FirstOrDefault(p => p.Name == pluginName);
}
public void EnablePlugin(string pluginName)
{
var plugin = GetPlugin(pluginName);
if (plugin != null)
{
PluginManager.Instance.TogglePlugin(plugin, true);
}
}
public void DisablePlugin(string pluginName)
{
var plugin = GetPlugin(pluginName);
if (plugin != null)
{
PluginManager.Instance.TogglePlugin(plugin, false);
}
}
public void UnloadPlugin(string pluginName)
{
var plugin = GetPlugin(pluginName);
if (plugin != null)
{
PluginManager.Instance.UnloadPlugin(plugin);
}
}
#endregion
#region
public void RegisterEventHandler(string eventName, EventHandler handler)
{
if (!_eventHandlers.ContainsKey(eventName))
{
_eventHandlers[eventName] = handler;
}
else
{
_eventHandlers[eventName] += handler;
}
}
public void UnregisterEventHandler(string eventName, EventHandler handler)
{
if (_eventHandlers.ContainsKey(eventName))
{
_eventHandlers[eventName] -= handler;
}
}
public void TriggerEvent(string eventName, object sender, EventArgs args)
{
if (_eventHandlers.ContainsKey(eventName))
{
_eventHandlers[eventName]?.Invoke(sender, args);
}
}
#endregion
}
}
@@ -0,0 +1,276 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件模板,用于开发者参考
/// 注意:实际开发时,请将此类移到单独的程序集中
/// </summary>
public class PluginTemplate : PluginBase
{
#region
/// <summary>
/// 插件名称
/// </summary>
public override string Name => "插件模板";
/// <summary>
/// 插件描述
/// </summary>
public override string Description => "这是一个插件开发模板,用于开发者参考。";
/// <summary>
/// 插件版本
/// </summary>
public override Version Version => new Version(1, 0, 0);
/// <summary>
/// 插件作者
/// </summary>
public override string Author => "Your Name";
/// <summary>
/// 是否为内置插件(外部插件请返回false)
/// </summary>
public override bool IsBuiltIn => false;
#endregion
#region
/// <summary>
/// 插件初始化
/// 在这里进行插件的初始化工作,如加载配置、注册事件等
/// </summary>
public override void Initialize()
{
// 先调用基类方法,这样会设置插件ID和记录日志
base.Initialize();
// TODO: 在这里进行插件初始化工作
// 示例:记录初始化信息
LogHelper.WriteLogToFile($"插件 {Name} 开始初始化");
// 示例:加载配置
LoadConfig();
// 示例:注册自定义事件
// MainWindow.Instance.SomeEvent += OnSomeEvent;
LogHelper.WriteLogToFile($"插件 {Name} 初始化完成");
}
/// <summary>
/// 启用插件
/// 在这里激活插件功能
/// </summary>
public override void Enable()
{
// 先调用基类方法,这样会设置插件状态和记录日志
base.Enable();
// TODO: 在这里启用插件功能
LogHelper.WriteLogToFile($"插件 {Name} 已启用");
}
/// <summary>
/// 禁用插件
/// 在这里停用插件功能
/// </summary>
public override void Disable()
{
// 先调用基类方法,这样会设置插件状态和记录日志
base.Disable();
// TODO: 在这里禁用插件功能
LogHelper.WriteLogToFile($"插件 {Name} 已禁用");
}
/// <summary>
/// 清理资源
/// 在插件卸载时调用,清理资源
/// </summary>
public override void Cleanup()
{
// TODO: 在这里清理插件资源
// 示例:取消注册事件
// MainWindow.Instance.SomeEvent -= OnSomeEvent;
// 示例:保存配置
SaveConfig();
// 最后调用基类方法
base.Cleanup();
}
#endregion
#region
/// <summary>
/// 加载插件配置
/// </summary>
private void LoadConfig()
{
try
{
// TODO: 从文件或其他位置加载配置
// 示例:
// string configPath = Path.Combine(App.RootPath, "PluginConfigs", "YourPluginName.json");
// if (File.Exists(configPath))
// {
// string json = File.ReadAllText(configPath);
// YourConfig = Newtonsoft.Json.JsonConvert.DeserializeObject<YourConfigClass>(json);
// }
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 保存插件配置
/// </summary>
private void SaveConfig()
{
try
{
// TODO: 保存配置到文件或其他位置
// 示例:
// string configDir = Path.Combine(App.RootPath, "PluginConfigs");
// if (!Directory.Exists(configDir))
// {
// Directory.CreateDirectory(configDir);
// }
// string configPath = Path.Combine(configDir, "YourPluginName.json");
// string json = Newtonsoft.Json.JsonConvert.SerializeObject(YourConfig, Newtonsoft.Json.Formatting.Indented);
// File.WriteAllText(configPath, json);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region
/// <summary>
/// 获取插件设置界面
/// </summary>
/// <returns>插件设置界面</returns>
public override UserControl GetSettingsView()
{
// 创建插件设置界面
return new PluginTemplateSettingsControl();
}
#endregion
#region
// TODO: 在这里添加插件的具体功能方法
/// <summary>
/// 示例方法:执行一些功能
/// </summary>
public void DoSomething()
{
if (!IsEnabled) return;
try
{
// TODO: 实现你的功能
MessageBox.Show("插件功能执行示例", "插件模板", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"执行插件功能时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
}
/// <summary>
/// 插件设置控件
/// </summary>
public class PluginTemplateSettingsControl : UserControl
{
public PluginTemplateSettingsControl()
{
// 创建设置界面布局
var panel = new StackPanel
{
Margin = new Thickness(10)
};
// 添加标题
panel.Children.Add(new TextBlock
{
Text = "插件模板设置",
FontSize = 16,
FontWeight = FontWeights.Bold,
Margin = new Thickness(0, 0, 0, 10)
});
// 添加说明文字
panel.Children.Add(new TextBlock
{
Text = "这是一个示例设置界面,你可以在这里添加自己的设置控件。",
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0, 0, 0, 15)
});
// 添加示例设置选项
var checkBox = new CheckBox
{
Content = "启用某项功能",
Margin = new Thickness(0, 0, 0, 10)
};
panel.Children.Add(checkBox);
// 添加文本输入框
panel.Children.Add(new TextBlock
{
Text = "设置项:",
Margin = new Thickness(0, 5, 0, 5)
});
panel.Children.Add(new TextBox
{
Margin = new Thickness(0, 0, 0, 10),
Width = 200,
HorizontalAlignment = HorizontalAlignment.Left
});
// 添加按钮
var button = new Button
{
Content = "保存设置",
Padding = new Thickness(10, 5, 10, 5),
Margin = new Thickness(0, 10, 0, 0),
HorizontalAlignment = HorizontalAlignment.Left
};
button.Click += (sender, e) =>
{
MessageBox.Show("设置已保存!", "插件模板", MessageBoxButton.OK, MessageBoxImage.Information);
};
panel.Children.Add(button);
// 设置控件内容
Content = panel;
}
}
}
+6 -8
View File
@@ -1,7 +1,8 @@
using Microsoft.Win32;
using System;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Ink_Canvas.Helpers
{
@@ -27,10 +28,7 @@ namespace Ink_Canvas.Helpers
//MessageBox.Show("启动失败: " + ex.Message);
}
}
else
{
//Console.WriteLine(softwareName + " 未找到可执行文件路径。");
}
//Console.WriteLine(softwareName + " 未找到可执行文件路径。");
}
private static string FindEasiCameraExecutablePath(string softwareName)
@@ -51,7 +49,7 @@ namespace Ink_Canvas.Helpers
{
if (!string.IsNullOrEmpty(installLocation))
{
executablePath = System.IO.Path.Combine(installLocation, "sweclauncher.exe");
executablePath = Path.Combine(installLocation, "sweclauncher.exe");
}
else if (!string.IsNullOrEmpty(uninstallString))
{
@@ -59,7 +57,7 @@ namespace Ink_Canvas.Helpers
if (lastSlashIndex >= 0)
{
string folderPath = uninstallString.Substring(0, lastSlashIndex);
executablePath = System.IO.Path.Combine(folderPath, "sweclauncher", "sweclauncher.exe");
executablePath = Path.Combine(folderPath, "sweclauncher", "sweclauncher.exe");
}
}
break;
+51
View File
@@ -0,0 +1,51 @@
using System.IO;
namespace Ink_Canvas.Helpers
{
public static class StartupCount
{
private static readonly string CountFilePath = Path.Combine(App.RootPath, "startup-count");
private static readonly object fileLock = new object();
public static int GetCount()
{
try
{
if (File.Exists(CountFilePath))
{
var text = File.ReadAllText(CountFilePath).Trim();
if (int.TryParse(text, out int count))
return count;
}
}
catch { }
return 0;
}
public static void Increment()
{
lock (fileLock)
{
int count = GetCount() + 1;
try
{
File.WriteAllText(CountFilePath, count.ToString());
}
catch { }
}
}
public static void Reset()
{
lock (fileLock)
{
try
{
if (File.Exists(CountFilePath))
File.Delete(CountFilePath);
}
catch { }
}
}
}
}
+40 -4
View File
@@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
// Added for UIElement
namespace Ink_Canvas.Helpers
{
public class TimeMachine
public partial class TimeMachine
{
private readonly List<TimeMachineHistory> _currentStrokeHistory = new List<TimeMachineHistory>();
@@ -133,12 +135,13 @@ namespace Ink_Canvas.Helpers
public class TimeMachineHistory
{
public TimeMachineHistoryType CommitType;
public bool StrokeHasBeenCleared = false;
public bool StrokeHasBeenCleared;
public StrokeCollection CurrentStroke;
public StrokeCollection ReplacedStroke;
//这里说一下 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 +166,11 @@ namespace Ink_Canvas.Helpers
StrokeHasBeenCleared = strokeHasBeenCleared;
ReplacedStroke = replacedStroke;
}
public TimeMachineHistory(UIElement element, TimeMachineHistoryType commitType) // 新增
{
CommitType = commitType;
InsertedElement = element;
}
}
public enum TimeMachineHistoryType
@@ -171,6 +179,34 @@ 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();
}
public void CommitElementRemoveHistory(UIElement element)
{
if (_currentIndex + 1 < _currentStrokeHistory.Count)
{
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
}
var history = new TimeMachineHistory(element, TimeMachineHistoryType.ElementInsert);
history.StrokeHasBeenCleared = true; // 标记为已清除
_currentStrokeHistory.Add(history);
_currentIndex = _currentStrokeHistory.Count - 1;
NotifyUndoRedoState();
}
}
}
+21 -10
View File
@@ -1,7 +1,9 @@
using System.Windows.Automation;
namespace Ink_Canvas.Helpers {
internal class WinTabWindowsChecker {
namespace Ink_Canvas.Helpers
{
internal class WinTabWindowsChecker
{
/*
public static bool IsWindowMinimized(string windowName, bool matchFullName = true) {
// 获取Win+Tab预览中的窗口
@@ -40,28 +42,37 @@ namespace Ink_Canvas.Helpers {
}
*/
public static bool IsWindowExisted(string windowName, bool matchFullName = true) {
public static bool IsWindowExisted(string windowName, bool matchFullName = true)
{
// 获取Win+Tab预览中的窗口
AutomationElementCollection windows = AutomationElement.RootElement.FindAll(
TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
foreach (AutomationElement window in windows) {
foreach (AutomationElement window in windows)
{
//LogHelper.WriteLogToFile("" + window.Current.Name);
string windowTitle = window.Current.Name;
// 如果窗口标题包含 windowName,则进行检查
if (!string.IsNullOrEmpty(windowTitle) && windowTitle.Contains(windowName)) {
if (matchFullName) {
if (windowTitle.Length == windowName.Length) {
if (!string.IsNullOrEmpty(windowTitle) && windowTitle.Contains(windowName))
{
if (matchFullName)
{
if (windowTitle.Length == windowName.Length)
{
WindowPattern windowPattern = window.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
if (windowPattern != null) {
if (windowPattern != null)
{
return true;
}
}
} else {
}
else
{
WindowPattern windowPattern = window.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
if (windowPattern != null) {
if (windowPattern != null)
{
return true;
}
}
+96
View File
@@ -0,0 +1,96 @@
{
"Version": "1.0",
"LastModified": "2025-01-28T15:30:00",
"Hotkeys": [
{
"Name": "Undo",
"Key": "Z",
"Modifiers": "Control"
},
{
"Name": "Redo",
"Key": "Y",
"Modifiers": "Control"
},
{
"Name": "Clear",
"Key": "E",
"Modifiers": "Control"
},
{
"Name": "Paste",
"Key": "V",
"Modifiers": "Control"
},
{
"Name": "SelectTool",
"Key": "S",
"Modifiers": "Alt"
},
{
"Name": "DrawTool",
"Key": "D",
"Modifiers": "Alt"
},
{
"Name": "EraserTool",
"Key": "E",
"Modifiers": "Alt"
},
{
"Name": "BlackboardTool",
"Key": "B",
"Modifiers": "Alt"
},
{
"Name": "QuitDrawTool",
"Key": "Q",
"Modifiers": "Alt"
},
{
"Name": "Pen1",
"Key": "D1",
"Modifiers": "Alt"
},
{
"Name": "Pen2",
"Key": "D2",
"Modifiers": "Alt"
},
{
"Name": "Pen3",
"Key": "D3",
"Modifiers": "Alt"
},
{
"Name": "Pen4",
"Key": "D4",
"Modifiers": "Alt"
},
{
"Name": "Pen5",
"Key": "D5",
"Modifiers": "Alt"
},
{
"Name": "DrawLine",
"Key": "L",
"Modifiers": "Alt"
},
{
"Name": "Screenshot",
"Key": "C",
"Modifiers": "Alt"
},
{
"Name": "Hide",
"Key": "V",
"Modifiers": "Alt"
},
{
"Name": "Exit",
"Key": "Escape",
"Modifiers": "None"
}
]
}
+14
View File
@@ -113,12 +113,15 @@
<ItemGroup>
<Reference Include="IACore">
<HintPath>.\IACore.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="IALoader">
<HintPath>.\IALoader.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="IAWinFX">
<HintPath>.\IAWinFX.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="netstandard" />
@@ -132,12 +135,18 @@
<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>
<None Include="app.manifest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Costura.Fody" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.9.27" />
<PackageReference Include="MdXaml" Version="1.27.0" />
@@ -181,6 +190,11 @@
<ItemGroup>
<None Include="Resources\TimerDownNotice.wav" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\IACore\IACore.dll" />
<EmbeddedResource Include="Resources\IACore\IALoader.dll" />
<EmbeddedResource Include="Resources\IACore\IAWinFX.dll" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Cursors\Cursor.cur" />
<Resource Include="Resources\Cursors\Pen.cur" />
+2168 -276
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+111 -61
View File
@@ -1,7 +1,4 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
using System;
using System.Diagnostics;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@@ -9,14 +6,18 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
namespace Ink_Canvas {
public partial class MainWindow : Window {
public bool isFloatingBarFolded = false;
private bool isFloatingBarChangingHideMode = false;
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
public bool isFloatingBarFolded;
private bool isFloatingBarChangingHideMode;
private void CloseWhiteboardImmediately() {
private void CloseWhiteboardImmediately()
{
if (isDisplayingOrHidingBlackboard) return;
isDisplayingOrHidingBlackboard = true;
HideSubPanelsImmediately();
@@ -30,21 +31,24 @@ namespace Ink_Canvas {
BtnSwitch_Click(BtnSwitch, null);
BtnExit.Foreground = Brushes.White;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
new Thread(new ThreadStart(() => {
new Thread(() =>
{
Thread.Sleep(200);
Application.Current.Dispatcher.Invoke(() => { isDisplayingOrHidingBlackboard = false; });
})).Start();
}).Start();
}
public async void FoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e) {
public async void FoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e)
{
await FoldFloatingBar(sender);
}
public async Task FoldFloatingBar(object sender)
public async Task FoldFloatingBar(object sender, bool isAutoFoldCommand = false)
{
var isShouldRejectAction = false;
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
if (sender == Fold_Icon && lastBorderMouseDownObject != Fold_Icon) isShouldRejectAction = true;
@@ -54,14 +58,15 @@ namespace Ink_Canvas {
// FloatingBarIcons_MouseUp_New(sender);
if (sender == null)
foldFloatingBarByUser = true;
foldFloatingBarByUser = false;
else
foldFloatingBarByUser = true;
unfoldFloatingBarByUser = false;
if (isFloatingBarChangingHideMode) return;
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
InkCanvasForInkReplay.Visibility = Visibility.Collapsed;
InkCanvasGridForInkReplay.Visibility = Visibility.Visible;
InkCanvasGridForInkReplay.IsHitTestVisible = true;
@@ -73,7 +78,8 @@ namespace Ink_Canvas {
isStopInkReplay = true;
});
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
isFloatingBarChangingHideMode = true;
isFloatingBarFolded = true;
if (currentMode != 0) CloseWhiteboardImmediately();
@@ -84,13 +90,15 @@ namespace Ink_Canvas {
CursorWithDelIcon_Click(sender, null);
});
await Task.Delay(10);
await Task.Delay(300);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
GridForFloatingBarDraging.Visibility = Visibility.Collapsed;
ViewboxFloatingBarMarginAnimation(-60);
HideSubPanels("cursor");
SidePannelMarginAnimation(-10);
@@ -98,12 +106,16 @@ namespace Ink_Canvas {
isFloatingBarChangingHideMode = false;
}
private async void LeftUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e) {
if (Settings.Appearance.IsShowQuickPanel == true) {
private async void LeftUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e)
{
if (Settings.Appearance.IsShowQuickPanel)
{
HideRightQuickPanel();
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Visible;
await Dispatcher.InvokeAsync(() => {
var marginAnimation = new ThicknessAnimation {
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = new Thickness(-50, 0, 0, -150),
To = new Thickness(-1, 0, 0, -150)
@@ -113,21 +125,27 @@ namespace Ink_Canvas {
});
await Task.Delay(100);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
LeftUnFoldButtonQuickPanel.Margin = new Thickness(-1, 0, 0, -150);
});
}
else {
else
{
UnFoldFloatingBar_MouseUp(sender, e);
}
}
private async void RightUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e) {
if (Settings.Appearance.IsShowQuickPanel == true) {
private async void RightUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e)
{
if (Settings.Appearance.IsShowQuickPanel)
{
HideLeftQuickPanel();
RightUnFoldButtonQuickPanel.Visibility = Visibility.Visible;
await Dispatcher.InvokeAsync(() => {
var marginAnimation = new ThicknessAnimation {
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = new Thickness(0, 0, -50, -150),
To = new Thickness(0, 0, -1, -150)
@@ -137,19 +155,25 @@ namespace Ink_Canvas {
});
await Task.Delay(100);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
RightUnFoldButtonQuickPanel.Margin = new Thickness(0, 0, -1, -150);
});
}
else {
else
{
UnFoldFloatingBar_MouseUp(sender, e);
}
}
private async void HideLeftQuickPanel() {
if (LeftUnFoldButtonQuickPanel.Visibility == Visibility.Visible) {
await Dispatcher.InvokeAsync(() => {
var marginAnimation = new ThicknessAnimation {
private async void HideLeftQuickPanel()
{
if (LeftUnFoldButtonQuickPanel.Visibility == Visibility.Visible)
{
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = new Thickness(-1, 0, 0, -150),
To = new Thickness(-50, 0, 0, -150)
@@ -159,17 +183,22 @@ namespace Ink_Canvas {
});
await Task.Delay(100);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
LeftUnFoldButtonQuickPanel.Margin = new Thickness(0, 0, -50, -150);
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
});
}
}
private async void HideRightQuickPanel() {
if (RightUnFoldButtonQuickPanel.Visibility == Visibility.Visible) {
await Dispatcher.InvokeAsync(() => {
var marginAnimation = new ThicknessAnimation {
private async void HideRightQuickPanel()
{
if (RightUnFoldButtonQuickPanel.Visibility == Visibility.Visible)
{
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = new Thickness(0, 0, -1, -150),
To = new Thickness(0, 0, -50, -150)
@@ -179,25 +208,29 @@ namespace Ink_Canvas {
});
await Task.Delay(100);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
RightUnFoldButtonQuickPanel.Margin = new Thickness(0, 0, -50, -150);
RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
});
}
}
private void HideQuickPanel_MouseUp(object sender, MouseButtonEventArgs e) {
private void HideQuickPanel_MouseUp(object sender, MouseButtonEventArgs e)
{
HideLeftQuickPanel();
HideRightQuickPanel();
}
public async void UnFoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e) {
public async void UnFoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e)
{
await UnFoldFloatingBar(sender);
}
public async Task UnFoldFloatingBar(object sender)
{
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
});
@@ -209,28 +242,41 @@ namespace Ink_Canvas {
if (isFloatingBarChangingHideMode) return;
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
isFloatingBarChangingHideMode = true;
isFloatingBarFolded = false;
});
await Task.Delay(0);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
// 根据设置决定是否自动切换至批注模式
if (Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode && currentMode == 0)
{
// 切换至批注模式
PenIcon_Click(null, null);
}
if (StackPanelPPTControls.Visibility == Visibility.Visible)
{
var dops = Settings.PowerPointSettings.PPTButtonsDisplayOption.ToString();
var dopsc = dops.ToCharArray();
if (dopsc[0] == '2' && isDisplayingOrHidingBlackboard == false) AnimationsHelper.ShowWithFadeIn(LeftBottomPanelForPPTNavigation);
if (dopsc[1] == '2' && isDisplayingOrHidingBlackboard == false) AnimationsHelper.ShowWithFadeIn(RightBottomPanelForPPTNavigation);
if (dopsc[2] == '2' && isDisplayingOrHidingBlackboard == false) AnimationsHelper.ShowWithFadeIn(LeftSidePanelForPPTNavigation);
if (dopsc[3] == '2' && isDisplayingOrHidingBlackboard == false) AnimationsHelper.ShowWithFadeIn(RightSidePanelForPPTNavigation);
if (dopsc[0] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(LeftBottomPanelForPPTNavigation);
if (dopsc[1] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(RightBottomPanelForPPTNavigation);
if (dopsc[2] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(LeftSidePanelForPPTNavigation);
if (dopsc[3] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(RightSidePanelForPPTNavigation);
}
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible)
ViewboxFloatingBarMarginAnimation(60);
else
ViewboxFloatingBarMarginAnimation(100, true);
// 新增:只在屏幕模式下显示浮动栏
if (currentMode == 0)
{
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible)
ViewboxFloatingBarMarginAnimation(60);
else
ViewboxFloatingBarMarginAnimation(100, true);
}
SidePannelMarginAnimation(-50, !unfoldFloatingBarByUser);
});
@@ -239,17 +285,20 @@ namespace Ink_Canvas {
private async void SidePannelMarginAnimation(int MarginFromEdge, bool isNoAnimation = false) // Possible value: -50, -10
{
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
if (MarginFromEdge == -10) LeftSidePanel.Visibility = Visibility.Visible;
var LeftSidePanelmarginAnimation = new ThicknessAnimation {
Duration = isNoAnimation == true ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(0.175),
var LeftSidePanelmarginAnimation = new ThicknessAnimation
{
Duration = isNoAnimation ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(0.175),
From = LeftSidePanel.Margin,
To = new Thickness(MarginFromEdge, 0, 0, -150)
};
LeftSidePanelmarginAnimation.EasingFunction = new CubicEase();
var RightSidePanelmarginAnimation = new ThicknessAnimation {
Duration = isNoAnimation == true ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(0.175),
var RightSidePanelmarginAnimation = new ThicknessAnimation
{
Duration = isNoAnimation ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(0.175),
From = RightSidePanel.Margin,
To = new Thickness(0, 0, MarginFromEdge, -150)
};
@@ -260,7 +309,8 @@ namespace Ink_Canvas {
await Task.Delay(600);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
LeftSidePanel.Margin = new Thickness(MarginFromEdge, 0, 0, -150);
RightSidePanel.Margin = new Thickness(0, 0, MarginFromEdge, -150);
+19 -11
View File
@@ -1,16 +1,22 @@
using IWshRuntimeLibrary;
using System;
using System;
using System.Windows;
using IWshRuntimeLibrary;
using Application = System.Windows.Forms.Application;
using File = System.IO.File;
namespace Ink_Canvas {
public partial class MainWindow : Window {
public static bool StartAutomaticallyCreate(string exeName) {
try {
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
public static bool StartAutomaticallyCreate(string exeName)
{
try
{
var shell = new WshShell();
var shortcut = (IWshShortcut)shell.CreateShortcut(
Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\" + exeName + ".lnk");
//设置快捷方式的目标所在的位置(源程序完整路径)
shortcut.TargetPath = System.Windows.Forms.Application.ExecutablePath;
shortcut.TargetPath = Application.ExecutablePath;
//应用程序的工作目录
//当用户没有指定一个具体的目录时,快捷方式的目标应用程序将使用该属性所指定的目录来装载或保存文件。
shortcut.WorkingDirectory = Environment.CurrentDirectory;
@@ -28,10 +34,12 @@ namespace Ink_Canvas {
return false;
}
public static bool StartAutomaticallyDel(string exeName) {
try {
System.IO.File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\" + exeName +
".lnk");
public static bool StartAutomaticallyDel(string exeName)
{
try
{
File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\" + exeName +
".lnk");
return true;
}
catch (Exception) { }
+36 -27
View File
@@ -1,50 +1,55 @@
using Microsoft.Win32;
using iNKORE.UI.WPF.Modern;
using System;
using System;
using System.Windows;
using System.Windows.Media;
using iNKORE.UI.WPF.Modern;
using Microsoft.Win32;
using Application = System.Windows.Application;
namespace Ink_Canvas {
public partial class MainWindow : Window {
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
private Color FloatBarForegroundColor = Color.FromRgb(102, 102, 102);
private void SetTheme(string theme) {
if (theme == "Light") {
var rd1 = new ResourceDictionary()
{ Source = new Uri("Resources/Styles/Light.xaml", UriKind.Relative) };
private void SetTheme(string theme)
{
if (theme == "Light")
{
var rd1 = new ResourceDictionary
{ Source = new Uri("Resources/Styles/Light.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd1);
var rd2 = new ResourceDictionary()
{ Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative) };
var rd2 = new ResourceDictionary
{ Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd2);
var rd3 = new ResourceDictionary()
{ Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative) };
var rd3 = new ResourceDictionary
{ Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd3);
var rd4 = new ResourceDictionary()
{ Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative) };
var rd4 = new ResourceDictionary
{ Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd4);
ThemeManager.SetRequestedTheme(window, ElementTheme.Light);
FloatBarForegroundColor = (Color)Application.Current.FindResource("FloatBarForegroundColor");
}
else if (theme == "Dark") {
var rd1 = new ResourceDictionary() { Source = new Uri("Resources/Styles/Dark.xaml", UriKind.Relative) };
else if (theme == "Dark")
{
var rd1 = new ResourceDictionary { Source = new Uri("Resources/Styles/Dark.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd1);
var rd2 = new ResourceDictionary()
{ Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative) };
var rd2 = new ResourceDictionary
{ Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd2);
var rd3 = new ResourceDictionary()
{ Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative) };
var rd3 = new ResourceDictionary
{ Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd3);
var rd4 = new ResourceDictionary()
{ Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative) };
var rd4 = new ResourceDictionary
{ Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd4);
ThemeManager.SetRequestedTheme(window, ElementTheme.Dark);
@@ -53,8 +58,10 @@ namespace Ink_Canvas {
}
}
private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) {
switch (Settings.Appearance.Theme) {
private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
{
switch (Settings.Appearance.Theme)
{
case 0:
SetTheme("Light");
break;
@@ -68,9 +75,11 @@ namespace Ink_Canvas {
}
}
private bool IsSystemThemeLight() {
private bool IsSystemThemeLight()
{
var light = false;
try {
try
{
var registryKey = Registry.CurrentUser;
var themeKey =
registryKey.OpenSubKey("software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
+243 -82
View File
@@ -1,20 +1,17 @@
using Ink_Canvas.Helpers;
using System;
using System.Collections.ObjectModel;
using System;
using System.Collections.Generic;
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 System.Windows.Ink;
using System.Windows.Media;
using Ink_Canvas.Helpers;
namespace Ink_Canvas {
public partial class MainWindow : Window {
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
private StrokeCollection[] strokeCollections = new StrokeCollection[101];
private bool[] whiteboadLastModeIsRedo = new bool[101];
private StrokeCollection lastTouchDownStrokeCollection = new StrokeCollection();
@@ -23,46 +20,173 @@ namespace Ink_Canvas {
private int WhiteboardTotalCount = 1;
private TimeMachineHistory[][] TimeMachineHistories = new TimeMachineHistory[101][]; //最多99页,0用来存储非白板时的墨迹以便还原
private void SaveStrokes(bool isBackupMain = false) {
if (isBackupMain) {
// 保存每页白板图片信息
private void SaveStrokes(bool isBackupMain = false)
{
// 确保画布上的所有UI元素都被保存到时间机器历史记录中
var currentHistory = timeMachine.ExportTimeMachineHistory();
var elementsInHistory = new HashSet<UIElement>();
// 收集已经在历史记录中的元素
if (currentHistory != null)
{
foreach (var h in currentHistory)
{
if (h.CommitType == TimeMachineHistoryType.ElementInsert &&
h.InsertedElement != null &&
!h.StrokeHasBeenCleared)
{
elementsInHistory.Add(h.InsertedElement);
}
}
}
// 检查画布上的所有UI元素,确保它们都在历史记录中
var missingElements = 0;
foreach (UIElement child in inkCanvas.Children)
{
if (child is Image || child is MediaElement)
{
if (!elementsInHistory.Contains(child))
{
timeMachine.CommitElementInsertHistory(child);
missingElements++;
}
}
}
// 确保画布上的所有墨迹都被保存
if (inkCanvas.Strokes.Count > 0)
{
// 检查是否有墨迹没有在时间机器历史记录中
var strokesInHistory = new HashSet<Stroke>();
if (currentHistory != null)
{
foreach (var h in currentHistory)
{
if (h.CommitType == TimeMachineHistoryType.UserInput &&
h.CurrentStroke != null &&
!h.StrokeHasBeenCleared)
{
foreach (Stroke stroke in h.CurrentStroke)
{
strokesInHistory.Add(stroke);
}
}
}
}
// 收集没有在历史记录中的墨迹
var missingStrokes = new StrokeCollection();
foreach (Stroke stroke in inkCanvas.Strokes)
{
if (!strokesInHistory.Contains(stroke))
{
missingStrokes.Add(stroke);
}
}
if (missingStrokes.Count > 0)
{
timeMachine.CommitStrokeUserInputHistory(missingStrokes);
}
}
if (isBackupMain)
{
var timeMachineHistory = timeMachine.ExportTimeMachineHistory();
TimeMachineHistories[0] = timeMachineHistory;
timeMachine.ClearStrokeHistory();
} else {
}
else
{
var timeMachineHistory = timeMachine.ExportTimeMachineHistory();
TimeMachineHistories[CurrentWhiteboardIndex] = timeMachineHistory;
timeMachine.ClearStrokeHistory();
}
}
private void ClearStrokes(bool isErasedByCode) {
private void ClearStrokes(bool isErasedByCode)
{
_currentCommitType = CommitReason.ClearingCanvas;
if (isErasedByCode) _currentCommitType = CommitReason.CodeInput;
// 只清除笔画,不清除图片元素
// 图片元素的清除由调用方决定
inkCanvas.Strokes.Clear();
_currentCommitType = CommitReason.UserInput;
}
private void RestoreStrokes(bool isBackupMain = false) {
try {
if (TimeMachineHistories[CurrentWhiteboardIndex] == null) return; //防止白板打开后不居中
if (isBackupMain) {
// 恢复每页白板图片信息
private void RestoreStrokes(bool isBackupMain = false)
{
try
{
// 隐藏图片选择工具栏
if (currentSelectedElement != null)
{
// 保存当前编辑模式
var previousEditingMode = inkCanvas.EditingMode;
UnselectElement(currentSelectedElement);
// 恢复编辑模式
inkCanvas.EditingMode = previousEditingMode;
currentSelectedElement = null;
}
var targetIndex = isBackupMain ? 0 : CurrentWhiteboardIndex;
// 先清空当前画布的墨迹
inkCanvas.Strokes.Clear();
// 清空当前画布的所有内容(墨迹和图片)
// 这里必须清除图片,因为页面切换时需要完全重置画布状态
inkCanvas.Children.Clear();
// 如果历史记录为空,直接返回(新页面或空页面)
if (TimeMachineHistories[targetIndex] == null)
{
timeMachine.ClearStrokeHistory();
return;
}
if (isBackupMain)
{
timeMachine.ImportTimeMachineHistory(TimeMachineHistories[0]);
foreach (var item in TimeMachineHistories[0]) ApplyHistoryToCanvas(item);
} else {
}
else
{
timeMachine.ImportTimeMachineHistory(TimeMachineHistories[CurrentWhiteboardIndex]);
// 通过时间机器历史恢复所有内容(墨迹和图片)
foreach (var item in TimeMachineHistories[CurrentWhiteboardIndex]) ApplyHistoryToCanvas(item);
}
}
catch {
catch
{
// ignored
}
}
private async void BtnWhiteBoardPageIndex_Click(object sender, EventArgs e) {
if (sender == BtnLeftPageListWB) {
if (BoardBorderLeftPageListView.Visibility == Visibility.Visible) {
private async void BtnWhiteBoardPageIndex_Click(object sender, EventArgs e)
{
if (sender == BtnLeftPageListWB)
{
if (BoardBorderLeftPageListView.Visibility == Visibility.Visible)
{
AnimationsHelper.HideWithSlideAndFade(BoardBorderLeftPageListView);
} else {
}
else
{
AnimationsHelper.HideWithSlideAndFade(BoardBorderRightPageListView);
RefreshBlackBoardSidePageListView();
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardBorderLeftPageListView);
@@ -71,11 +195,15 @@ namespace Ink_Canvas {
(ListViewItem)BlackBoardLeftSidePageListView.ItemContainerGenerator.ContainerFromIndex(
CurrentWhiteboardIndex - 1), BlackBoardLeftSidePageListScrollViewer);
}
} else if (sender == BtnRightPageListWB)
}
else if (sender == BtnRightPageListWB)
{
if (BoardBorderRightPageListView.Visibility == Visibility.Visible) {
if (BoardBorderRightPageListView.Visibility == Visibility.Visible)
{
AnimationsHelper.HideWithSlideAndFade(BoardBorderRightPageListView);
} else {
}
else
{
AnimationsHelper.HideWithSlideAndFade(BoardBorderLeftPageListView);
RefreshBlackBoardSidePageListView();
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardBorderRightPageListView);
@@ -88,9 +216,21 @@ namespace Ink_Canvas {
}
private void BtnWhiteBoardSwitchPrevious_Click(object sender, EventArgs e) {
private void BtnWhiteBoardSwitchPrevious_Click(object sender, EventArgs e)
{
if (CurrentWhiteboardIndex <= 1) return;
// 隐藏图片选择工具栏
if (currentSelectedElement != null)
{
// 保存当前编辑模式
var previousEditingMode = inkCanvas.EditingMode;
UnselectElement(currentSelectedElement);
// 恢复编辑模式
inkCanvas.EditingMode = previousEditingMode;
currentSelectedElement = null;
}
SaveStrokes();
ClearStrokes(true);
@@ -101,15 +241,29 @@ namespace Ink_Canvas {
UpdateIndexInfoDisplay();
}
private void BtnWhiteBoardSwitchNext_Click(object sender, EventArgs e) {
private void BtnWhiteBoardSwitchNext_Click(object sender, EventArgs e)
{
Trace.WriteLine("113223234");
if (Settings.Automation.IsAutoSaveStrokesAtClear &&
inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber) SaveScreenShot(true);
if (CurrentWhiteboardIndex >= WhiteboardTotalCount) {
if (CurrentWhiteboardIndex >= WhiteboardTotalCount)
{
// 在最后一页时,点击"新页面"按钮直接新增一页
BtnWhiteBoardAdd_Click(sender, e);
return;
}
// 隐藏图片选择工具栏
if (currentSelectedElement != null)
{
// 保存当前编辑模式
var previousEditingMode = inkCanvas.EditingMode;
UnselectElement(currentSelectedElement);
// 恢复编辑模式
inkCanvas.EditingMode = previousEditingMode;
currentSelectedElement = null;
}
SaveStrokes();
@@ -121,10 +275,23 @@ namespace Ink_Canvas {
UpdateIndexInfoDisplay();
}
private void BtnWhiteBoardAdd_Click(object sender, EventArgs e) {
private void BtnWhiteBoardAdd_Click(object sender, EventArgs e)
{
if (WhiteboardTotalCount >= 99) return;
if (Settings.Automation.IsAutoSaveStrokesAtClear &&
inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber) SaveScreenShot(true);
// 隐藏图片选择工具栏
if (currentSelectedElement != null)
{
// 保存当前编辑模式
var previousEditingMode = inkCanvas.EditingMode;
UnselectElement(currentSelectedElement);
// 恢复编辑模式
inkCanvas.EditingMode = previousEditingMode;
currentSelectedElement = null;
}
SaveStrokes();
ClearStrokes(true);
@@ -135,16 +302,35 @@ namespace Ink_Canvas {
for (var i = WhiteboardTotalCount; i > CurrentWhiteboardIndex; i--)
TimeMachineHistories[i] = TimeMachineHistories[i - 1];
// 确保新页面的历史记录为空
TimeMachineHistories[CurrentWhiteboardIndex] = null;
// 恢复新页面(这会清空画布,因为历史记录为null)
RestoreStrokes();
UpdateIndexInfoDisplay();
if (WhiteboardTotalCount >= 99) BtnWhiteBoardAdd.IsEnabled = false;
if (BlackBoardLeftSidePageListView.Visibility == Visibility.Visible) {
if (BlackBoardLeftSidePageListView.Visibility == Visibility.Visible)
{
RefreshBlackBoardSidePageListView();
}
}
private void BtnWhiteBoardDelete_Click(object sender, RoutedEventArgs e) {
private void BtnWhiteBoardDelete_Click(object sender, RoutedEventArgs e)
{
// 隐藏图片选择工具栏
if (currentSelectedElement != null)
{
// 保存当前编辑模式
var previousEditingMode = inkCanvas.EditingMode;
UnselectElement(currentSelectedElement);
// 恢复编辑模式
inkCanvas.EditingMode = previousEditingMode;
currentSelectedElement = null;
}
ClearStrokes(true);
if (CurrentWhiteboardIndex != WhiteboardTotalCount)
@@ -162,68 +348,43 @@ namespace Ink_Canvas {
if (WhiteboardTotalCount < 99) BtnWhiteBoardAdd.IsEnabled = true;
}
private void UpdateIndexInfoDisplay() {
private void UpdateIndexInfoDisplay()
{
TextBlockWhiteBoardIndexInfo.Text =
$"{CurrentWhiteboardIndex}/{WhiteboardTotalCount}";
if (CurrentWhiteboardIndex == WhiteboardTotalCount) {
var newImageSource = new BitmapImage();
newImageSource.BeginInit();
newImageSource.UriSource = new Uri("/Resources/Icons-Fluent/ic_fluent_add_circle_24_regular.png",
UriKind.RelativeOrAbsolute);
newImageSource.EndInit();
//BoardLeftPannelNextPage.Source = newImageSource;
//BoardRightPannelNextPage.Source = newImageSource;
//BoardRightPannelNextPageTextBlock.Text = "加页";
//BoardLeftPannelNextPageTextBlock.Text = "加页";
} else {
var newImageSource = new BitmapImage();
newImageSource.BeginInit();
newImageSource.UriSource =
new Uri("/Resources/Icons-Fluent/ic_fluent_arrow_circle_right_24_regular.png",
UriKind.RelativeOrAbsolute);
newImageSource.EndInit();
//BoardLeftPannelNextPage.Source = newImageSource;
//BoardRightPannelNextPage.Source = newImageSource;
//BoardRightPannelNextPageTextBlock.Text = "下一页";
//BoardLeftPannelNextPageTextBlock.Text = "下一页";
}
bool isLastPage = CurrentWhiteboardIndex == WhiteboardTotalCount;
bool isMaxPage = WhiteboardTotalCount >= 99;
// 设置按钮文本
BtnLeftWhiteBoardSwitchNextLabel.Text = isLastPage ? "新页面" : "下一页";
BtnRightWhiteBoardSwitchNextLabel.Text = isLastPage ? "新页面" : "下一页";
// 始终允许点击“下一页/新页面”按钮(除非已达最大页数)
BtnWhiteBoardSwitchNext.IsEnabled = !isMaxPage;
// 保持按钮常亮(高亮)
BtnLeftWhiteBoardSwitchNextGeometry.Brush = new SolidColorBrush(Color.FromArgb(255, 24, 24, 27));
BtnLeftWhiteBoardSwitchNextLabel.Opacity = 1;
BtnRightWhiteBoardSwitchNextGeometry.Brush = new SolidColorBrush(Color.FromArgb(255, 24, 24, 27));
BtnRightWhiteBoardSwitchNextLabel.Opacity = 1;
BtnWhiteBoardSwitchPrevious.IsEnabled = true;
BtnWhiteBoardSwitchNext.IsEnabled = true;
if (CurrentWhiteboardIndex == 1) {
if (CurrentWhiteboardIndex == 1)
{
BtnWhiteBoardSwitchPrevious.IsEnabled = false;
BtnLeftWhiteBoardSwitchPreviousGeometry.Brush = new SolidColorBrush(Color.FromArgb(127, 24, 24, 27));
BtnLeftWhiteBoardSwitchPreviousLabel.Opacity = 0.5;
BtnLeftWhiteBoardSwitchNextGeometry.Brush = new SolidColorBrush(Color.FromArgb(255, 24, 24, 27));
BtnLeftWhiteBoardSwitchNextLabel.Opacity = 1;
BtnRightWhiteBoardSwitchPreviousGeometry.Brush = new SolidColorBrush(Color.FromArgb(127, 24, 24, 27));
BtnRightWhiteBoardSwitchPreviousLabel.Opacity = 0.5;
BtnRightWhiteBoardSwitchNextGeometry.Brush = new SolidColorBrush(Color.FromArgb(255, 24, 24, 27));
BtnRightWhiteBoardSwitchNextLabel.Opacity = 1;
} else {
}
else
{
BtnLeftWhiteBoardSwitchPreviousGeometry.Brush = new SolidColorBrush(Color.FromArgb(255, 24, 24, 27));
BtnLeftWhiteBoardSwitchPreviousLabel.Opacity = 1;
BtnRightWhiteBoardSwitchPreviousGeometry.Brush = new SolidColorBrush(Color.FromArgb(255, 24, 24, 27));
BtnRightWhiteBoardSwitchPreviousLabel.Opacity = 1;
if (CurrentWhiteboardIndex == WhiteboardTotalCount) {
BtnLeftWhiteBoardSwitchNextGeometry.Brush = new SolidColorBrush(Color.FromArgb(127, 24, 24, 27));
BtnLeftWhiteBoardSwitchNextLabel.Opacity = 0.5;
BtnRightWhiteBoardSwitchNextGeometry.Brush = new SolidColorBrush(Color.FromArgb(127, 24, 24, 27));
BtnRightWhiteBoardSwitchNextLabel.Opacity = 0.5;
BtnWhiteBoardSwitchNext.IsEnabled = false;
} else {
BtnLeftWhiteBoardSwitchNextGeometry.Brush = new SolidColorBrush(Color.FromArgb(255, 24, 24, 27));
BtnLeftWhiteBoardSwitchNextLabel.Opacity = 1;
BtnRightWhiteBoardSwitchNextGeometry.Brush = new SolidColorBrush(Color.FromArgb(255, 24, 24, 27));
BtnRightWhiteBoardSwitchNextLabel.Opacity = 1;
}
}
BtnWhiteBoardDelete.IsEnabled = WhiteboardTotalCount != 1;

Some files were not shown because too many files have changed in this diff Show More