Compare commits

...

1303 Commits

Author SHA1 Message Date
CJK_mkp c7427eb22c Merge pull request #316 from InkCanvasForClass/beta
ICC CE 1.7.18.1
2025-12-13 21:03:41 +08:00
CJKmkp e9fde97453 improve:GitHub工作流 2025-12-13 21:02:11 +08:00
CJKmkp 441f8b6e26 优化按钮名称 2025-12-13 20:47:30 +08:00
CJKmkp 8042b917a0 improve:浮动栏按钮UI
优化按钮显示
2025-12-13 20:45:31 +08:00
CJKmkp 343e7281fe improve:端点吸附
避免点线和虚线吸附
2025-12-13 20:32:43 +08:00
CJKmkp c64e6a4554 improve:issue #252
改进双指拖动
2025-12-13 20:22:23 +08:00
CJKmkp 8190bf275c Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-12-13 20:13:14 +08:00
CJKmkp e792f2637d improve:PPT墨迹加载 2025-12-13 20:13:08 +08:00
CJKmkp 3cd26323dc improve:翻页墨迹加载及浮动栏定位 2025-12-13 20:07:25 +08:00
PrefacedCorg 40e1c4d467 Update MW_FloatingBarIcons.cs 2025-12-13 19:54:10 +08:00
PrefacedCorg 86c22d373a Revert "feat: 添加白板模式自动全屏功能" 2025-12-13 19:48:21 +08:00
PrefacedCorg cb7a76efc5 Delete .vscode directory 2025-12-13 19:39:08 +08:00
PrefacedCorg 545425c4d3 Delete .kiro/steering directory 2025-12-13 19:38:34 +08:00
PrefacedCorg eb1aaa10e4 feat: 添加白板模式自动全屏功能
- 新增"白板模式自动全屏"设置选项
- 进入白板模式时自动全屏,退出时恢复工作区域大小
- 需配合"避免全屏助手"功能使用
- 用户可独立控制白板模式的全屏行为
2025-12-13 19:27:47 +08:00
CJKmkp daf0db312b improve:仅PPT模式
优化浮动栏显示
2025-12-13 18:02:05 +08:00
CJKmkp 8b2bc2f064 improve:退出放映时收纳及PPT记忆上次播放页数 2025-12-13 17:38:35 +08:00
CJKmkp 2e343cbbf9 improve:计时器
优化计时器行为
2025-12-13 17:20:18 +08:00
CJKmkp a75f0470bc improve:点名算法
缩小极差
2025-12-13 17:12:00 +08:00
CJKmkp 287d31a3a9 improve:窗口模式 2025-12-13 17:03:58 +08:00
CJKmkp 430fff0515 优化注释 2025-12-06 23:04:27 +08:00
CJKmkp fbbb7b8ad7 improve:issue #252
改进几何绘制
2025-12-06 23:01:41 +08:00
CJKmkp 54b74d7411 improve:issue #252
改进手掌擦及手势
2025-12-06 22:55:55 +08:00
CJKmkp 82dba31b2a improve:直线拉直
改进高精度拉直
2025-12-06 22:06:28 +08:00
CJKmkp 38d7e782e0 improve:issue #252
修复了几何绘制和多指书写状态问题
2025-11-30 11:51:19 +08:00
CJK_mkp b17e73db90 Merge pull request #298 from InkCanvasForClass/beta
ICC CE 1.7.18.0
2025-11-29 23:15:00 +08:00
CJKmkp 05e5ceeb43 improve:计时器
改进主题加载
2025-11-29 23:11:01 +08:00
CJKmkp fcbbad71d2 更新版本号 2025-11-29 22:23:14 +08:00
CJKmkp 6f9161439f improve:直线拉直
改进直线拉直算法
2025-11-29 22:20:13 +08:00
CJKmkp aa0c4fb841 improve:Dlass服务
优化上传体验
2025-11-29 17:26:47 +08:00
CJKmkp cff50d1f81 fix:快捷键管理 2025-11-29 17:26:41 +08:00
CJKmkp b8581b6368 improve:点名算法
优化概率模型
2025-11-29 16:58:40 +08:00
CJKmkp 094f1223d1 improve:崩溃日志位置 2025-11-29 16:46:33 +08:00
CJKmkp 6802476afa improve:计时器UI
将计时器窗口整合至主窗口,优化全屏计时逻辑
2025-11-29 16:27:35 +08:00
CJKmkp a0539dce9b 更新版本号 2025-11-15 21:36:49 +08:00
CJKmkp bf2b8fec35 improve:计时器UI与点名UI
改进视觉反馈和按钮逻辑及窗口置顶
2025-11-15 21:14:08 +08:00
CJKmkp 082c9a03ec fix:issue #287 2025-11-15 20:48:04 +08:00
CJKmkp 4ccdd862ba improve:PPT模块
修复无焦点状态下键盘翻页无效
2025-11-15 20:24:28 +08:00
CJKmkp e5a20ed0fc improve:点名历史查看 2025-11-15 20:20:56 +08:00
CJKmkp a72022704e improve:点名算法 2025-11-15 19:34:52 +08:00
CJKmkp 2acc7ada30 improve:错误检测 2025-11-15 19:31:57 +08:00
CJKmkp e61882c331 improve:点名
点名算法优化
2025-11-15 19:20:35 +08:00
CJKmkp 61c145689a fix:issue #281 2025-11-15 18:41:42 +08:00
CJKmkp 40ea9664a7 improve:快抽置顶 2025-11-15 18:37:23 +08:00
CJKmkp bccd2d0f3e improve:PPT墨迹保存 2025-11-15 18:19:53 +08:00
CJKmkp b918809dca improve:线擦擦除 2025-11-15 18:06:36 +08:00
CJKmkp e7c2e92879 improve:PPT联动及墨迹管理
改回原处理模式,优化翻页操作
2025-11-15 18:04:22 +08:00
CJK_mkp cf03c921a7 更新版本号 2025-11-08 23:16:36 +08:00
CJK_mkp 2c45c839b1 Revert "improve:无焦点模式"
This reverts commit 83f5fc58d1.
2025-11-08 23:09:04 +08:00
CJK_mkp 83f5fc58d1 improve:无焦点模式 2025-11-08 22:55:18 +08:00
CJK_mkp 1a267f1e5a improve:点名 2025-11-08 22:23:44 +08:00
CJK_mkp c3fd5551d8 improve:快抽按钮 2025-11-08 22:17:52 +08:00
CJK_mkp 1baa74bb69 improve:快抽按钮 2025-11-08 22:07:38 +08:00
CJK_mkp 261ecefb17 improve:注释 2025-11-08 21:42:33 +08:00
CJK_mkp d81d8f7c5d improve:Dlass联动 2025-11-08 21:01:50 +08:00
CJK_mkp da4beb5dcd Merge pull request #279 from InkCanvasForClass/beta
ICC CE 1.7.17.2
2025-11-08 20:46:01 +08:00
CJK_mkp de1af12157 更新版本号 2025-11-08 20:41:43 +08:00
CJK_mkp 803cbbdee9 improve:默认设置 2025-11-08 20:29:18 +08:00
CJK_mkp 008477d5fa improve:默认设置 2025-11-08 20:17:51 +08:00
CJK_mkp 7f0d29ebd2 improve:快抽窗口 2025-11-08 20:07:00 +08:00
CJK_mkp 11bf8cffb2 improve:PPT墨迹加载 2025-11-08 19:55:10 +08:00
CJK_mkp 87b9ebc7e1 improve:PPT墨迹显示 2025-11-08 19:36:05 +08:00
CJK_mkp b89d27411b improve:计时器逻辑 2025-11-08 19:20:26 +08:00
CJK_mkp 24c37f1d3e fix:计时器时间不一致 2025-11-08 19:17:58 +08:00
CJK_mkp 58b0a0a3be fix:负数导致的计时器崩溃 2025-11-08 19:15:16 +08:00
CJK_mkp ed58873a82 fix:issue #272 2025-11-08 19:02:37 +08:00
CJK_mkp a8dcbd4af0 add:点名历史查看 2025-11-08 18:01:56 +08:00
CJK_mkp 4b2f29442a improve:点名 2025-11-07 10:36:24 +08:00
CJK_mkp dfab0d7ddf improvve:点名快抽 2025-11-07 10:35:27 +08:00
CJK_mkp ce1998b701 improve:Dlass 界面UI 2025-11-07 09:46:21 +08:00
CJK_mkp 92c631d6ce improve:操作逻辑 2025-11-02 12:34:50 +08:00
CJK_mkp 01009f9e35 更新版本号 2025-11-02 11:47:52 +08:00
CJK_mkp 74eca093da improve:悬浮快抽按钮 2025-11-02 11:40:27 +08:00
CJK_mkp e7d89e65b2 improve:AutoUpdate 2025-11-02 11:27:46 +08:00
CJK_mkp 24b2bffe8e improve:计时器窗口 2025-11-02 11:12:13 +08:00
CJK_mkp d2906476c8 add:Dlass联动 2025-11-02 10:46:16 +08:00
CJK_mkp 2b31a355ae add:Dlass联动 2025-11-02 10:30:36 +08:00
CJK_mkp b602048186 add:Dlass联动 2025-11-02 10:16:31 +08:00
CJK_mkp 4fb7031060 add:Dlass联动 2025-11-02 10:11:15 +08:00
CJK_mkp 4ef77c2e72 add:Dlass联动 2025-11-02 09:41:53 +08:00
CJK_mkp 72ba1a9f58 add:Dlass联动 2025-11-02 09:29:06 +08:00
CJK_mkp 72d871ecb8 Merge pull request #275 from InkCanvasForClass/beta
ICC CE 1.7.17.0
2025-11-01 22:20:59 +08:00
CJK_mkp 0c3938b652 Merge pull request #274 from MKStoler1024/patch-1
Readme
2025-11-01 22:18:46 +08:00
CJK_mkp 16f80adb0d fix:issue #210 2025-11-01 22:12:47 +08:00
CJK_mkp 637b6bb4f9 fix:issue #210 2025-11-01 22:05:19 +08:00
CJK_mkp 12e91927a5 fix:issue #210 2025-11-01 21:56:31 +08:00
CJK_mkp 8f6f22ba7f 更新版本号 2025-11-01 21:37:55 +08:00
CJK_mkp b520d6a334 fix:issue #210 2025-11-01 21:00:23 +08:00
CJK_mkp b7bff30445 improve:UI 2025-11-01 20:49:37 +08:00
CJK_mkp 0d790bbd80 add:墨迹自动保存 2025-11-01 20:42:18 +08:00
CJK_mkp 8394e99127 improve:主题切换 2025-11-01 20:26:52 +08:00
CJK_mkp 16ae32bfd7 improve:点名UI 2025-11-01 19:17:52 +08:00
CJK_mkp cc6423e384 improve:点名UI 2025-11-01 18:56:38 +08:00
CJK_mkp 06cc587599 improve:启动动画 2025-11-01 18:46:04 +08:00
CJK_mkp 9d36088f1d fix:issue #210 2025-11-01 18:39:52 +08:00
CJK_mkp f9f73b015c improve:启动动画 2025-11-01 18:31:27 +08:00
CJK_mkp 77ffd696bb fix:手势面板不显示 2025-11-01 18:29:58 +08:00
CJK_mkp bdb8bed053 fix:issue #210 2025-11-01 18:25:35 +08:00
MKStoler1024 4b17c8e96e chore: remove sth in readme 2025-10-31 17:56:02 +08:00
MKStoler1024 8109711f4e chore: readme 2025-10-31 16:07:03 +08:00
CJK_mkp 327eba3fa7 Update MW_FloatingBarIcons.cs 2025-10-31 15:35:27 +08:00
CJK_mkp f3dccb2e99 Update MW_FloatingBarIcons.cs 2025-10-31 14:46:20 +08:00
CJK_mkp 7112d58e7c Update MW_ShapeDrawing.cs 2025-10-31 14:44:59 +08:00
CJK_mkp 6e0aad853c Update MW_TouchEvents.cs 2025-10-31 14:44:05 +08:00
CJK_mkp c64b1d0846 Update MW_BoardIcons.cs 2025-10-31 14:43:15 +08:00
CJK_mkp 6eba16ce99 Update MW_BoardIcons.cs 2025-10-31 12:19:30 +08:00
CJK_mkp 2f6f719843 Update MW_ShapeDrawing.cs 2025-10-31 12:18:45 +08:00
CJK_mkp f34bac49e4 Update MW_TouchEvents.cs 2025-10-31 12:16:52 +08:00
CJK_mkp 39cdc6231f Update MW_FloatingBarIcons.cs 2025-10-31 12:16:05 +08:00
CJK_mkp 745e24da70 Update MW_FloatingBarIcons.cs 2025-10-31 12:15:26 +08:00
CJK_mkp a4f4f4fb15 Update MW_TouchEvents.cs 2025-10-31 12:12:51 +08:00
CJK_mkp 3833c229c6 Update MW_FloatingBarIcons.cs 2025-10-31 12:11:02 +08:00
CJK_mkp 3dc3e9b5a8 Update MW_FloatingBarIcons.cs 2025-10-31 10:49:10 +08:00
CJK_mkp 12eeb79e9f 回滚一下 2025-10-30 12:02:29 +08:00
CJK_mkp e08c84f70d improve:点名快抽 2025-10-27 17:54:58 +08:00
CJK_mkp 10f55d5b65 fix:抽选结果截断 2025-10-27 17:45:41 +08:00
CJKmkp 761992d089 Delete PositionConverters.cs 2025-10-26 00:31:32 +08:00
CJK_mkp 86676d40fa Merge pull request #270 from InkCanvasForClass/beta
ICC CE 1.7.16.0
2025-10-26 00:26:52 +08:00
CJKmkp 991a823700 更新版本号 2025-10-26 00:24:28 +08:00
CJKmkp 61dbcf762c add:点名快抽 2025-10-26 00:21:10 +08:00
CJKmkp 60b0149a9c add:新点名UI 2025-10-26 00:00:13 +08:00
CJKmkp 501c034cfa fix:点名UI按钮主题显示 2025-10-25 21:14:59 +08:00
CJK_mkp 5a5d4c1d50 Merge pull request #269 from InkCanvasForClass/beta
ICC CE 1.7.15.0
2025-10-25 20:21:15 +08:00
CJKmkp 11593db23c 更新版本号 2025-10-25 20:14:07 +08:00
CJKmkp a9ae2a004f improve:注释 2025-10-25 20:09:04 +08:00
CJKmkp 7b2a31781c 优化代码 2025-10-25 19:57:56 +08:00
CJKmkp ab73eb9632 优化代码 2025-10-25 19:44:32 +08:00
CJKmkp 8ade170b4e improve:计时器数字 2025-10-25 17:27:19 +08:00
CJKmkp 91c3d1161d fix:issue #210 2025-10-25 17:03:44 +08:00
CJK_mkp bb63805e87 fix:issue #210
Removed the SimulateMultiTouchToggle method and its related comments.
2025-10-23 16:27:39 +08:00
CJK_mkp 8caf04990c 更新信息 2025-10-22 17:30:38 +08:00
CJK_mkp f28dd0a965 Update AssemblyInfo.cs 2025-10-22 17:29:12 +08:00
CJK_mkp b5d83268e5 更新信息 2025-10-22 17:28:56 +08:00
CJK_mkp 19adc42122 Change PPTNavigationBtn_MouseUp to async method 2025-10-22 12:18:03 +08:00
CJK_mkp 8afee913be Update MW_PPT.cs 2025-10-22 12:16:42 +08:00
CJK_mkp 82d101365f Update MW_PPT.cs 2025-10-22 12:12:43 +08:00
CJK_mkp 73e679f268 Make PPTNavigationBtn_MouseDown async
Changed PPTNavigationBtn_MouseDown to be asynchronous and added a delay for animation.
2025-10-22 12:08:29 +08:00
CJK_mkp 372a8a1de1 fix:PPT状态浮动栏位置异常 2025-10-20 17:45:14 +08:00
CJK_mkp 171cc34c91 撤销修改 2025-10-20 12:10:47 +08:00
CJK_mkp fbd217d674 撤销修改8d778a
Removed quick color palette visibility logic from ink editing mode.
2025-10-20 10:49:04 +08:00
CJKmkp f24960ab26 improve:UIA窗口置顶 2025-10-19 01:34:03 +08:00
CJKmkp d1a871c8f6 Reapply "fix:issue # 262"
This reverts commit 086b97906b.
2025-10-18 21:20:54 +08:00
CJKmkp 086b97906b Revert "fix:issue # 262"
This reverts commit 5bfd0c7b2f.
2025-10-18 21:16:24 +08:00
CJKmkp db76a11347 improve:窗口置顶 2025-10-18 20:45:23 +08:00
CJKmkp 729b10cce8 Reapply "improve:UIA窗口置顶"
This reverts commit 4cbd25ccb3.
2025-10-18 20:41:09 +08:00
CJKmkp 4cbd25ccb3 Revert "improve:UIA窗口置顶"
This reverts commit 0ef7b738a9.
2025-10-18 20:38:08 +08:00
CJKmkp 0ef7b738a9 improve:UIA窗口置顶 2025-10-18 20:15:40 +08:00
CJK_mkp 52a99179cd Merge pull request #266 from InkCanvasForClass/beta
ICC CE 1.7.14.0
2025-10-18 19:06:17 +08:00
CJKmkp 33b5563601 更新版本号 2025-10-18 19:05:16 +08:00
CJKmkp f6f799f534 improve:墨迹清空 2025-10-18 19:03:30 +08:00
CJKmkp 8c3f1360e1 improve:注释 2025-10-18 18:45:08 +08:00
CJKmkp fafbabd603 improve:清空墨迹 2025-10-18 18:42:29 +08:00
CJKmkp 254e38895c fix:无焦点功能丢失 2025-10-18 18:29:52 +08:00
CJKmkp aa0bb22cdd add:UIA窗口置顶 2025-10-18 18:26:13 +08:00
CJKmkp 5bfd0c7b2f fix:issue # 262 2025-10-18 17:36:47 +08:00
CJKmkp c3885c170c improve:PPT墨迹管理 2025-10-18 17:24:47 +08:00
CJKmkp 88a7cce269 fix:issue #133 2025-10-18 16:46:09 +08:00
CJKmkp a1fccc2905 fix:PPT上次页数记忆跳转 2025-10-18 16:41:55 +08:00
CJKmkp a889041896 fix:issue #260 2025-10-18 16:31:07 +08:00
CJKmkp 8d778aba2c improve:PPT模块 2025-10-18 16:27:26 +08:00
CJKmkp ba5db63e0b improve:计时器UI 2025-10-18 16:15:19 +08:00
CJKmkp 0259d83429 improve:计时器 2025-10-12 18:06:44 +08:00
CJKmkp b6999e57ae improve:计时器 2025-10-12 18:05:40 +08:00
CJKmkp 04184cf731 improve:计时器 2025-10-12 17:44:44 +08:00
CJKmkp 04f98eb9e7 improve:计时器 2025-10-12 17:17:51 +08:00
CJKmkp 869dd045af improve:计时器 2025-10-12 16:08:36 +08:00
CJK_mkp 339ebb862e improve:计时器UI 2025-10-08 17:34:12 +08:00
CJKmkp 7b8598bf9f Revert "Update MainWindow.xaml.cs"
This reverts commit 298164d6ed.
2025-10-06 22:41:53 +08:00
CJKmkp ab1c460225 Reapply "add:手动更新"
This reverts commit 703a8a4e0d.
2025-10-06 22:41:42 +08:00
CJKmkp 298164d6ed Update MainWindow.xaml.cs 2025-10-06 22:39:18 +08:00
CJKmkp 703a8a4e0d Revert "add:手动更新"
This reverts commit 1fd95a2f2e.
2025-10-06 22:36:59 +08:00
CJKmkp deaf5fcbf6 更新版本号 2025-10-06 22:05:27 +08:00
CJKmkp 18b46689f1 improve:计时器 2025-10-06 21:49:19 +08:00
CJKmkp edbe3f8311 improve:计时器 2025-10-06 21:17:14 +08:00
CJKmkp 94c4c3e2d4 improve:计时器UI 2025-10-06 21:08:47 +08:00
CJKmkp 077f72737d improve:计时器UI 2025-10-06 21:05:22 +08:00
CJKmkp ae089a9390 improve:计时器UI 2025-10-06 20:57:33 +08:00
CJKmkp 0f087c2aa6 improve:计时器UI 2025-10-06 20:46:19 +08:00
CJKmkp 19d2ffb48e improve:计时器UI 2025-10-06 20:44:09 +08:00
CJKmkp a9bb6f73de improve:计时器UI 2025-10-06 20:33:50 +08:00
CJKmkp ca124732fa improve:计时器 2025-10-06 20:23:55 +08:00
CJKmkp 11da14aeab improve:计时器 2025-10-06 20:11:49 +08:00
CJKmkp 7d5f037c85 improve:计时器 2025-10-06 19:59:01 +08:00
CJKmkp f62b415227 improve:计时器UI 2025-10-06 19:56:00 +08:00
CJKmkp f2b8d4014e improve:计时器UI 2025-10-06 19:46:14 +08:00
CJKmkp 3ef047fb41 improve:计时器UI 2025-10-06 19:43:04 +08:00
CJKmkp aa3be4ab0d improve:计时器UI 2025-10-06 19:05:22 +08:00
CJKmkp 7500049ea2 improve:计时器UI 2025-10-06 19:03:10 +08:00
CJKmkp 44600df75c improve:计时器UI 2025-10-06 18:44:00 +08:00
CJKmkp dec2a15773 improve:计时器UI 2025-10-06 18:43:14 +08:00
PrefacedCorg 4da78a04cb 代码清理 2025-10-06 18:29:12 +08:00
CJKmkp 98ec204bab improve:计时器UI 2025-10-06 18:03:15 +08:00
CJKmkp cd9499b064 improve:计时器UI 2025-10-06 17:55:05 +08:00
CJKmkp f5e824be86 improve:计时器 2025-10-06 17:49:43 +08:00
CJKmkp c76254f4f9 improve:计时器UI 2025-10-06 17:28:52 +08:00
CJKmkp 77ac6f88ca improve:计时器UI 2025-10-06 17:28:32 +08:00
CJKmkp 7e10911991 improve:计时器UI 2025-10-06 17:23:16 +08:00
CJKmkp 161b67b09d improve:计时器UI 2025-10-06 17:17:56 +08:00
CJKmkp 9614536a29 improve:计时器UI 2025-10-06 17:14:40 +08:00
CJKmkp 8ba7aab468 improve:计时器UI 2025-10-06 17:08:38 +08:00
CJKmkp 4ea9f79de1 improve:计时器UI 2025-10-06 17:06:02 +08:00
CJKmkp b7f7025d97 improve:计时器UI 2025-10-06 17:00:27 +08:00
CJKmkp 20d5dd2668 improve:计时器UI 2025-10-06 16:53:00 +08:00
CJKmkp adc2d02fbb Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-10-06 16:39:08 +08:00
CJK_mkp ef2dbdc93b Merge pull request #258 from LiuYan-xwx/beta
feat: 优化滑动动画与设置面板逻辑
2025-10-06 16:38:11 +08:00
CJKmkp 9721ec1f0b improve:计时器UI 2025-10-06 16:35:21 +08:00
LiuYan-xwx 4d069d87d7 feat: 优化滑动动画与设置面板逻辑
- 调整滑动动画的目标位置,更新 From 和 To 属性值。
- 移动并优化 `BorderSettings.Visibility` 的设置逻辑。
- 提升代码可读性,减少重复代码,改进动画效果。
2025-10-06 16:32:33 +08:00
CJKmkp 0308f9ce65 improve:计时器UI 2025-10-06 16:30:55 +08:00
CJKmkp 6bcd3cb217 improve:计时器UI 2025-10-06 16:29:03 +08:00
CJKmkp 7f83c490db improve:计时器UI 2025-10-06 16:14:52 +08:00
CJKmkp bbf9c895b8 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-10-06 16:06:15 +08:00
CJKmkp ffa2063c52 improve:计时器UI 2025-10-06 16:04:03 +08:00
CJKmkp d464b1f78e improve:计时器UI 2025-10-06 15:59:48 +08:00
CJKmkp 92cb071408 improve:计时器UI 2025-10-06 15:52:38 +08:00
PrefacedCorg 9141b60d03 Merge pull request #257 from LiuYan-xwx/beta
Update .gitignore
2025-10-06 15:44:52 +08:00
LiuYan-xwx df0a196931 Update .gitignore 2025-10-06 15:41:52 +08:00
CJKmkp a8cb1dd495 improve:计时器 2025-10-06 15:26:46 +08:00
CJKmkp adc4966d49 improve:计时器 2025-10-06 15:00:42 +08:00
CJKmkp 1b2ea8c522 improve:计时器UI 2025-10-06 14:55:52 +08:00
CJKmkp 7c8bdb489b improve:计时器 2025-10-06 14:50:56 +08:00
CJKmkp 16458fbb42 improve:计时器 2025-10-06 14:31:54 +08:00
CJKmkp c72839cdcb improve:计时器 2025-10-06 14:31:04 +08:00
CJKmkp a31ad5803c improve:计时器 2025-10-06 14:28:53 +08:00
CJKmkp cf800cbd36 improve:自动更新 2025-10-06 14:19:24 +08:00
CJKmkp 3c06ef0b1a 优化注释 2025-10-06 14:11:32 +08:00
CJKmkp 2c3b921f09 improve:计时器逻辑 2025-10-06 13:39:31 +08:00
CJKmkp 402ecc66ae improve:计时器逻辑 2025-10-06 13:38:12 +08:00
CJKmkp e25f56a9b5 improve:计时器逻辑 2025-10-06 13:34:52 +08:00
CJKmkp b28fa887a2 improve:计时器逻辑 2025-10-06 13:31:54 +08:00
CJKmkp f83a02e619 improve:UI 2025-10-06 13:15:03 +08:00
CJKmkp eaad089d68 更新版本号 2025-10-06 09:56:07 +08:00
CJKmkp a2fda16df9 improve:主题切换 2025-10-05 23:58:39 +08:00
CJKmkp e9e8ff57ae improve:主题切换 2025-10-05 23:22:02 +08:00
CJKmkp 1fd95a2f2e add:手动更新 2025-10-05 23:09:05 +08:00
CJK_mkp 228584ee48 Update README.md 2025-10-05 21:31:30 +08:00
CJK_mkp c651df0f6e Update README.md 2025-10-05 21:27:56 +08:00
CJKmkp 5cced9baf2 fix:图标数组错误 2025-10-05 21:13:37 +08:00
CJK_mkp 05440248a1 Merge pull request #250 from InkCanvasForClass/beta
ICC CE 1.7.13.0
2025-10-05 12:25:56 +08:00
CJKmkp 5dd26e554b 更新版本号 2025-10-05 12:20:20 +08:00
CJK_mkp 696fd3e8cd Update privacy.txt 2025-10-05 12:07:42 +08:00
CJKmkp 1d19b705d3 fix:issue #202 2025-10-05 11:49:22 +08:00
CJKmkp d54074cb57 improve:主题切换 2025-10-05 09:44:54 +08:00
CJKmkp cc054aeb75 improve:主题切换 2025-10-05 09:33:25 +08:00
CJKmkp c32eaed534 improve:UI 2025-10-05 09:29:31 +08:00
CJKmkp fa23f73ec4 improve:UI 2025-10-05 09:26:14 +08:00
CJKmkp fbf6a13f92 improve:主题切换 2025-10-05 09:16:36 +08:00
CJKmkp 3eba662772 improve:主题切换 2025-10-05 09:14:29 +08:00
CJKmkp e007ee271f improve:主题切换 2025-10-05 08:58:16 +08:00
CJKmkp 59a8d65a89 improve:主题切换 2025-10-05 08:57:04 +08:00
CJKmkp 793519ae1b improve:主题切换 2025-10-05 08:55:18 +08:00
CJKmkp 5c44062aa2 improve:主题切换 2025-10-05 00:26:18 +08:00
CJKmkp fe03a2c2c0 improve:主题切换 2025-10-05 00:24:23 +08:00
CJKmkp d1d74a3770 improve:主题切换 2025-10-05 00:18:58 +08:00
CJKmkp 16108bf779 add:图标 2025-10-04 23:49:12 +08:00
CJKmkp 5bebf077e4 improve:主题切换 2025-10-04 22:59:11 +08:00
CJKmkp efcc01ad6b improve:主题切换 2025-10-04 22:44:47 +08:00
CJKmkp e8a4b45446 improve:主题切换 2025-10-04 22:39:32 +08:00
CJKmkp 6cba297d77 improve:主题切换 2025-10-04 22:37:46 +08:00
CJKmkp b149dc3cb9 improve:主题切换 2025-10-04 22:31:42 +08:00
CJKmkp 1ee4f934b9 improve:主题切换 2025-10-04 22:30:40 +08:00
CJKmkp f7b4adb85d Revert "improve:主题"
This reverts commit da3c41567d.
2025-10-04 22:12:47 +08:00
CJKmkp da3c41567d improve:主题 2025-10-04 22:08:30 +08:00
CJKmkp 5f73795220 improve:主题切换 2025-10-04 22:05:58 +08:00
CJKmkp 36bf1122c6 improve:主题切换 2025-10-04 22:04:24 +08:00
CJKmkp 402f8bb9f9 improve:主题切换 2025-10-04 21:55:55 +08:00
CJKmkp eef2a915fa improve:主题 2025-10-04 21:48:44 +08:00
CJKmkp 26ee4d172f improve:主题切换 2025-10-04 21:44:15 +08:00
CJKmkp 08eb450446 improve:主题切换 2025-10-04 21:40:55 +08:00
CJKmkp ecae7d818c improve:主题切换 2025-10-04 21:00:51 +08:00
CJKmkp 47ffccff68 improve:主题切换 2025-10-04 19:59:42 +08:00
CJKmkp 0ba5286c94 improve:主题切换 2025-10-04 18:42:55 +08:00
CJKmkp db8e1e3589 improve:主题切换 2025-10-04 18:29:42 +08:00
CJKmkp d0764a6a77 improve:主题切换 2025-10-04 18:19:43 +08:00
CJKmkp 7b6c347d6b improve:主题切换 2025-10-04 18:17:11 +08:00
CJKmkp d58fec180c improve:主题切换 2025-10-04 18:10:43 +08:00
CJKmkp ac4e4877a1 improve:主题切换 2025-10-04 17:49:44 +08:00
CJKmkp ddea61245d improve:主题切换 2025-10-04 17:47:52 +08:00
CJKmkp 98915bcff2 improve:主题切换 2025-10-04 17:32:17 +08:00
CJKmkp 8e0f0450df improve:主题切换 2025-10-04 17:30:26 +08:00
CJKmkp 1d9a669829 improve:主题切换 2025-10-04 17:26:11 +08:00
CJKmkp 97bcf168b7 improve:主题 2025-10-04 17:15:58 +08:00
CJKmkp e81198165b improve:主题 2025-10-04 17:14:14 +08:00
CJKmkp b03b8da586 improve:主题 2025-10-04 17:12:14 +08:00
CJKmkp d497e07187 improve:主题 2025-10-04 17:10:11 +08:00
CJKmkp f284a99194 improve:UI 2025-10-04 17:03:03 +08:00
CJKmkp 7d6dd6f805 add:仿希沃计时器 2025-10-04 17:00:15 +08:00
CJKmkp d70e28d198 improve:图标 2025-10-04 16:56:15 +08:00
CJKmkp 4c6f138e5c add:图标 2025-10-04 16:49:03 +08:00
CJKmkp 5974841a7b add:仿希沃计时器 2025-10-04 16:21:14 +08:00
CJKmkp 5df54439ba add:仿希沃计时器 2025-10-04 16:14:56 +08:00
CJKmkp 8f627c6b7f add:图标 2025-10-04 14:55:49 +08:00
CJKmkp 3f28bc5c6c improve:自动更新 2025-10-04 14:47:53 +08:00
CJKmkp 903fa699ca improve:侧边栏图标 2025-10-04 09:49:37 +08:00
PrefacedCorg d9e8f64699 1145141919810
代码清理
2025-10-03 17:08:46 +08:00
CJKmkp 8ad6ca8d41 优化代码 2025-10-03 12:26:15 +08:00
CJK_mkp 4017efc65e Merge pull request #248 from InkCanvasForClass/main
合并一下
2025-10-03 11:51:13 +08:00
CJK_mkp 6ed15d52de Merge pull request #247 from InkCanvasForClass/beta
test action
2025-10-03 11:50:15 +08:00
CJKmkp 5246a2f79b test action 2025-10-03 11:48:09 +08:00
CJK_mkp b670932dd7 Merge pull request #246 from InkCanvasForClass/beta
test action
2025-10-03 11:32:51 +08:00
CJKmkp ddf30bd96b test action 2025-10-03 11:31:54 +08:00
CJKmkp 7e92428485 test action 2025-10-03 11:30:28 +08:00
CJKmkp 4c16050c85 test action 2025-10-03 11:29:46 +08:00
CJK_mkp 2ed035525a Merge pull request #245 from InkCanvasForClass/beta
test action
2025-10-03 11:24:14 +08:00
CJKmkp 1b89b0d7b6 test action 2025-10-03 11:23:46 +08:00
CJK_mkp 0364821f0b Merge pull request #244 from InkCanvasForClass/beta
test action
2025-10-03 11:18:13 +08:00
CJKmkp fa9d6f37ae test action 2025-10-03 11:17:42 +08:00
CJK_mkp a4777904b9 Merge pull request #243 from InkCanvasForClass/beta
test action
2025-10-03 11:14:40 +08:00
CJKmkp 0b3c2f95c5 test action 2025-10-03 11:14:16 +08:00
CJK_mkp 8f54955432 Merge pull request #242 from InkCanvasForClass/beta
test action
2025-10-03 11:11:21 +08:00
CJKmkp 034db2fc27 test action 2025-10-03 11:07:58 +08:00
CJKmkp 59141b0241 test action 2025-10-03 11:04:00 +08:00
CJK_mkp 2e43b96a2c Merge pull request #241 from InkCanvasForClass/beta
合并pre-release action
2025-10-03 11:01:21 +08:00
CJKmkp edff96299c test action 2025-10-03 10:59:36 +08:00
CJKmkp 8d9587b790 test action 2025-10-03 10:57:58 +08:00
CJKmkp ad0cf2a849 test action 2025-10-03 10:56:55 +08:00
CJKmkp f928946c61 improve:白板模式切换 2025-10-03 10:47:28 +08:00
CJKmkp 6cc9d0bee2 fix:issue #219 2025-10-03 09:19:07 +08:00
CJKmkp 287e6bb91f fix:issue #219 2025-10-03 09:12:39 +08:00
CJKmkp 3509036d85 fix:issue #219 2025-10-03 09:04:40 +08:00
CJKmkp 752901dbb9 fix:issue #219 2025-10-03 08:55:28 +08:00
CJKmkp e3add94546 fix:issue #219 2025-10-03 08:52:47 +08:00
CJKmkp 8d31c74b39 add:图标 2025-10-02 22:51:44 +08:00
CJKmkp e6f608d206 add:图标 2025-10-02 22:49:29 +08:00
CJKmkp d80b9e29a7 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-10-02 22:30:00 +08:00
CJK_mkp 092674465d Merge pull request #239 from InkCanvasForClass/all-contributors/add-Tayasui-rainnya
docs: add Tayasui-rainnya as a contributor for design
2025-10-02 22:28:55 +08:00
allcontributors[bot] b2e3a5bb18 docs: update .all-contributorsrc 2025-10-02 14:28:32 +00:00
allcontributors[bot] 147a2f957e docs: update README.md 2025-10-02 14:28:31 +00:00
CJKmkp ce56f2919c add:图标 2025-10-02 22:24:20 +08:00
CJKmkp 18059102a3 add:图标 2025-10-02 22:15:07 +08:00
CJKmkp 4542fcccbb 默认线路组改为inkeys 2025-10-02 21:52:55 +08:00
CJKmkp d12009d30c 修改 2025-10-02 20:31:08 +08:00
CJK_mkp d85e0fbd13 Merge pull request #238 from Tayasui-rainnya/beta
添加dark♂icc-ce图标
2025-10-02 20:29:50 +08:00
CJKmkp f7a5f9ae6b improve:启动动画 2025-10-02 19:51:31 +08:00
tayasui rainnya! 3d4c3d0acc dark♂icc-ce 图标 2025-10-02 19:42:24 +08:00
tayasui rainnya! c106c41048 dark♂图标 2025-10-02 19:41:18 +08:00
CJKmkp cea777e8b2 improve:启动动画 2025-10-02 19:25:57 +08:00
CJKmkp 6f069b73da improve:启动动画 2025-10-02 19:20:03 +08:00
CJKmkp c80af8c984 improve:aovidfullsreen 2025-10-02 19:13:20 +08:00
CJKmkp d51cbd0682 improve:aovidfullsreen 2025-10-02 19:08:33 +08:00
CJKmkp 7e94c945ff improve:aovidfullsreen 2025-10-02 19:05:24 +08:00
CJKmkp bd9095b4c2 improve:aovidfullsreen 2025-10-02 19:04:28 +08:00
CJKmkp c135bf8fb8 improve:aovidfullsreen 2025-10-02 18:57:49 +08:00
CJKmkp f6aebb15b4 improve:aovidfullsreen 2025-10-02 18:54:09 +08:00
CJKmkp 2b4f88becd fix:issue #237 2025-10-02 18:48:11 +08:00
CJKmkp 7fbd3639b6 fix:退出白板后收纳 2025-10-02 18:15:39 +08:00
CJKmkp 9e52c2c31d fix:退出白板后收纳 2025-10-02 18:06:08 +08:00
CJKmkp 46a1e5ff14 improve:主题切换 2025-10-02 17:39:56 +08:00
CJKmkp fa7c1fd646 fix:屏蔽压感 2025-10-02 17:04:12 +08:00
CJKmkp c347809eea improve:启动动画 2025-10-02 16:38:40 +08:00
CJKmkp dd09a42f02 improve:启动动画 2025-10-02 16:33:54 +08:00
CJKmkp db8ffd05ea improve:启动动画 2025-10-02 16:22:50 +08:00
CJK_mkp 045c29ca20 Merge pull request #236 from InkCanvasForClass/beta
ICC CE 1.7.12.0
2025-10-02 16:03:39 +08:00
CJKmkp 4ea6d19602 更新版本号 2025-10-02 15:58:33 +08:00
CJKmkp df00447c41 improve:启动动画 2025-10-02 15:36:52 +08:00
CJKmkp db5b1caea7 improve:启动动画 2025-10-02 15:35:53 +08:00
CJKmkp 8432954b8d add:issue #235 2025-10-02 15:30:51 +08:00
CJK_mkp 4a000e23e9 Update README.md 2025-10-02 15:09:27 +08:00
CJKmkp b42db51346 fix:忽略希沃白板五桌面批注窗口 2025-10-02 15:05:55 +08:00
CJKmkp 1ef968ea93 improve:悬浮窗拦截 2025-10-02 14:57:55 +08:00
CJKmkp 5b50537333 improve:开机动画 2025-10-02 14:53:57 +08:00
CJKmkp 8c624b48fb 删除无用信息 2025-10-02 14:08:22 +08:00
CJKmkp ad329fc2c8 improve:悬浮窗拦截 2025-10-02 11:50:00 +08:00
CJKmkp f045b8f659 fix:悬浮窗拦截 2025-10-02 11:45:55 +08:00
CJKmkp 00a9da45bb improve:启动动画 2025-10-02 02:59:49 +08:00
CJKmkp b7ecd12f8c improve:启动动画 2025-10-02 02:55:53 +08:00
CJKmkp a8a3164ee7 improve:启动动画 2025-10-02 02:44:45 +08:00
CJKmkp b1aec69e98 fix:issue #234 2025-10-02 02:32:29 +08:00
CJKmkp d713a8c5a4 优化注释 2025-10-02 02:30:24 +08:00
CJKmkp 3895faf941 fix:issue #133 2025-10-02 02:16:02 +08:00
CJKmkp bf336fdb10 add:issue #214 2025-10-02 02:11:54 +08:00
CJKmkp c96c26288c add:软件启动动画 2025-10-02 01:51:10 +08:00
CJKmkp 83558c089d add:软件启动动画 2025-10-02 00:17:11 +08:00
CJKmkp 26acb72e17 add:软件启动动画 2025-10-02 00:05:09 +08:00
CJKmkp 4724a432ab add:软件启动动画 2025-10-01 23:26:38 +08:00
CJKmkp 5b9c1627c0 add:软件启动动画 2025-10-01 22:38:40 +08:00
CJKmkp d06c2585cf add:软件启动动画 2025-10-01 22:24:49 +08:00
CJKmkp 3e96e51efd delete:office注册表修改 2025-10-01 22:06:23 +08:00
CJKmkp 9afa7191c4 fix:issue #232 2025-10-01 18:55:03 +08:00
CJKmkp dfddec5586 add:软件启动动画 2025-10-01 18:45:26 +08:00
CJKmkp 93022424b3 add:软件启动动画 2025-10-01 18:38:01 +08:00
CJKmkp aed77a187f add:软件启动动画 2025-10-01 18:17:39 +08:00
CJKmkp 583f47c98b 更新版本号 2025-10-01 15:20:14 +08:00
CJK_mkp ff528b3f9d Merge pull request #233 from InkCanvasForClass/beta
ICC CE 1.7.11.5
2025-10-01 15:17:17 +08:00
CJKmkp 6d98d96ccb fix:issue #217 2025-10-01 14:13:09 +08:00
CJKmkp 6f8e08c21a fix:issue #217 2025-10-01 13:03:11 +08:00
CJKmkp 3a7417f493 优化代码 2025-10-01 12:57:16 +08:00
CJKmkp 66164a0c33 improve:配置文件损坏自动恢复 2025-10-01 11:34:37 +08:00
CJKmkp 0d7a478e16 improve:配置文件损坏自动恢复 2025-10-01 11:29:59 +08:00
CJKmkp 9caef310df 优化代码 2025-10-01 10:04:40 +08:00
CJKmkp 688f742c16 add:老版UI切换 2025-10-01 09:52:15 +08:00
CJKmkp 38dd083cdc add:老版UI切换 2025-10-01 09:48:44 +08:00
CJKmkp 851bd1c3c6 improve:主题切换 2025-10-01 09:13:48 +08:00
CJKmkp c91b8a1a7a add:issue #224 2025-10-01 00:54:03 +08:00
CJKmkp 69c45764b2 Revert "add:issue #224"
This reverts commit 07a62d2f78.
2025-10-01 00:53:22 +08:00
CJKmkp 07a62d2f78 add:issue #224 2025-10-01 00:49:12 +08:00
CJKmkp 46b064b0a8 add:配置文件损坏自动恢复 2025-10-01 00:01:35 +08:00
CJKmkp 1d3b96bb65 更新Action 2025-09-30 23:45:40 +08:00
CJKmkp 916425b7f5 更新Action 2025-09-30 23:38:58 +08:00
CJKmkp 1c570ca242 fix:issue #210 2025-09-30 23:18:16 +08:00
CJKmkp ae41108971 优化代码 2025-09-30 22:56:32 +08:00
CJKmkp 395e7609a4 fix:issue #210 2025-09-30 22:55:06 +08:00
CJKmkp 185c9dc284 Reapply "fix:issue #201"
This reverts commit f40ef15a24.
2025-09-30 19:16:56 +08:00
CJKmkp a9b0ac0595 Revert "优化代码"
This reverts commit 92bb458345.
2025-09-30 19:15:03 +08:00
CJKmkp d9e3524211 fix:issue #201 2025-09-30 19:10:34 +08:00
CJKmkp f40ef15a24 Revert "fix:issue #201"
This reverts commit 9b5d157333.
2025-09-30 19:02:45 +08:00
CJKmkp 4d7544eefb deletefix:issue #210 2025-09-30 18:45:57 +08:00
CJKmkp 92bb458345 优化代码 2025-09-30 17:46:35 +08:00
CJKmkp 92e695ef7c fix:issue #213 2025-09-30 17:22:32 +08:00
CJKmkp 45ff3fbb9b fix:issue #213 2025-09-30 17:18:00 +08:00
CJKmkp ba252e92d3 fix:issue #229 2025-09-30 16:55:44 +08:00
CJK_mkp 4778f459e3 优化日志 2025-09-29 18:19:36 +08:00
CJK_mkp c997854ae0 优化日志 2025-09-29 18:14:39 +08:00
CJK_mkp 65b5322ad5 优化日志 2025-09-29 18:13:11 +08:00
CJK_mkp dd43bf0476 优化日志 2025-09-29 18:11:21 +08:00
CJKmkp 15c808eebd 更新版本号 2025-09-27 18:00:12 +08:00
CJKmkp 28748a99ca improve:手掌擦 2025-09-27 17:58:50 +08:00
CJKmkp fb37d3b9e6 improve:更新弹窗 2025-09-27 17:52:25 +08:00
CJKmkp e1f10e054c improve:issue #223 2025-09-27 17:09:06 +08:00
CJKmkp c670357c01 improve:橡皮擦 2025-09-27 17:01:33 +08:00
CJK_mkp 92dce9b36e Update MainWindow.xaml 2025-09-26 22:14:12 +08:00
PrefacedCorg aa2b62e8da 修复退出放映按钮字体显示不完全
把字体调小了()
2025-09-22 20:23:36 +08:00
CJKmkp a34a65f354 优化代码 2025-09-22 13:26:53 +08:00
CJKmkp eea5f8496c fix:高光显示 2025-09-22 13:11:22 +08:00
CJKmkp 091a256bcc Revert "Replace SymbolIcon with FontIcon throughout UI"
This reverts commit 37a69032f6.
2025-09-22 13:09:43 +08:00
CJKmkp 14c9ce3ce1 fix:issue #219 2025-09-22 12:32:53 +08:00
CJKmkp fd1e5e13fe fix:进入PPT自动收纳 2025-09-22 12:11:03 +08:00
CJKmkp 19685b8f95 fix:进入PPT自动收纳 2025-09-22 11:58:54 +08:00
CJKmkp a9da8dc10c fix:进入PPT自动收纳 2025-09-22 11:48:05 +08:00
CJKmkp fcfba7a978 fix:进入PPT自动收纳 2025-09-22 11:36:55 +08:00
PrefacedCorg 2c8c4351dd Update WenXiang.png 2025-09-25 10:26:38 +08:00
PrefacedCorg 79264b187d Remove unused exception variable in catch block
The exception variable 'ex' in the catch block was unused and has been removed for cleaner code.
2025-09-22 09:44:38 +08:00
PrefacedCorg 52eba9117b Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-09-22 09:34:45 +08:00
PrefacedCorg 37a69032f6 Replace SymbolIcon with FontIcon throughout UI
Updated all XAML and code-behind references from SymbolIcon/Symbol to FontIcon/Glyph for consistency and compatibility with the UI framework. This affects button icons, window controls, and shape drawing logic across multiple windows and components.
2025-09-22 09:34:39 +08:00
CJKmkp dd16b4f5a1 更新版本号 2025-09-21 08:28:32 +08:00
CJKmkp e9c20255a6 imporve:PPT墨迹保存 2025-09-21 08:26:55 +08:00
CJKmkp 630b4edf91 improve:PPT墨迹保存 2025-09-21 07:52:11 +08:00
CJKmkp 04ef638e17 fix:issue #190 2025-09-21 07:43:29 +08:00
CJKmkp 35bad240c2 更新版本号 2025-09-21 02:08:29 +08:00
CJKmkp 0cb4749cf3 add:主题切换 2025-09-21 02:05:49 +08:00
CJKmkp 7c8281eb00 add:主题切换 2025-09-21 01:35:13 +08:00
CJKmkp 1957288219 add:主题切换 2025-09-21 01:15:01 +08:00
CJKmkp c4d2b15c48 add:主题切换 2025-09-21 01:06:42 +08:00
CJKmkp beebfb0dae add:主题切换 2025-09-21 00:39:30 +08:00
CJKmkp d67172b795 优化代码 2025-09-21 00:28:30 +08:00
CJKmkp 2b012bc042 add:主题切换 2025-09-21 00:25:09 +08:00
CJKmkp 84da68f950 improve:历史版本回滚窗口 2025-09-20 22:27:26 +08:00
CJKmkp ad97fd13bf improve:更新弹窗 2025-09-20 22:21:05 +08:00
CJKmkp ebf6d0d5f7 improve:历史版本回滚窗口 2025-09-20 22:20:13 +08:00
CJKmkp 0f3b4b4384 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-09-20 22:02:43 +08:00
PrefacedCorg 226cf46d6b Merge pull request #212 from Hao3288/beta
readme文件中的部分url地址填写错误
2025-09-20 21:55:56 +08:00
CJKmkp 5689e33541 improve:更新弹窗 2025-09-20 21:54:02 +08:00
NoobHao 15e59e7117 Update README.md 2025-09-20 21:46:43 +08:00
NoobHao 6b538f6662 Update README.md 2025-09-20 21:45:38 +08:00
CJKmkp ad6808b696 improve:图片拖动 2025-09-20 21:33:21 +08:00
CJKmkp 8347b18efc 优化代码 2025-09-20 21:25:30 +08:00
CJKmkp f92b132f0a 优化代码 2025-09-20 19:49:16 +08:00
CJKmkp 79057d6757 fix:issue #160 2025-09-20 19:36:00 +08:00
CJKmkp a9b5ee8f62 fix:issue #205 2025-09-20 19:03:00 +08:00
CJKmkp 65da2c449e 更新版本号 2025-09-20 17:13:04 +08:00
CJKmkp 0ef8c12650 improve:issue #180 2025-09-20 17:06:11 +08:00
PrefacedCorg 1dfa48aecb Update MainWindow.xaml 2025-09-20 16:42:35 +08:00
PrefacedCorg b5d1713b43 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-09-20 16:36:02 +08:00
PrefacedCorg 102421997d add DeveloperAvatars 2025-09-20 16:34:55 +08:00
CJKmkp 43370bb8d1 fix:issue #202 2025-09-20 16:13:35 +08:00
CJKmkp 310f50fcde fix:issue #202 2025-09-20 16:10:52 +08:00
CJKmkp 9b5d157333 fix:issue #201 2025-09-20 15:36:45 +08:00
CJKmkp 53fbb7a0bd improve:更新弹窗 2025-09-20 15:36:24 +08:00
CJKmkp fe92117c4e improve:更新弹窗 2025-09-20 15:25:41 +08:00
CJKmkp 4a0d13457a improve:更新弹窗 2025-09-20 15:25:08 +08:00
CJKmkp 1ad6405003 improve:issue #197 2025-09-20 14:49:40 +08:00
CJKmkp 75e7e36011 improve:issue #133 2025-09-20 14:31:38 +08:00
CJKmkp 933c695b8c fix:issue #133 2025-09-20 14:02:29 +08:00
CJKmkp b34f7142f6 improve:墨迹平滑 2025-09-20 13:44:01 +08:00
CJKmkp 7392fa8165 fix:issue #204 2025-09-20 12:38:22 +08:00
CJKmkp 349d417869 fix:issue #205 2025-09-20 12:35:30 +08:00
CJKmkp b8e94cac9c fix:issue #210 2025-09-20 12:22:21 +08:00
CJKmkp cd90490b8d fix:issue #210 2025-09-20 12:15:21 +08:00
CJKmkp a9cc94ccb6 fix:issue #210 2025-09-20 12:03:30 +08:00
CJKmkp 17f137af09 fix:issue #210 2025-09-20 11:55:50 +08:00
CJKmkp ea7d0bbf71 fix:issue #206 2025-09-20 11:49:25 +08:00
CJKmkp 176f1cf405 fix:issue #208 2025-09-20 11:45:35 +08:00
CJKmkp 2ee93bbcc1 fix:issue #209 2025-09-20 11:42:33 +08:00
CJKmkp ba0629000e add:退出白板收纳 2025-09-20 11:22:09 +08:00
CJKmkp 313049f873 fix:issue #203 2025-09-20 11:08:52 +08:00
CJK_mkp d1a1b17d29 fix:白板参数 2025-09-18 18:07:45 +08:00
CJK_mkp eb91d2ce0a fix:白板参数 2025-09-18 18:06:53 +08:00
CJK_mkp 71c77da898 fix:issue #203 2025-09-18 17:58:51 +08:00
CJK_mkp 1b96c70386 fix:issue #160 2025-09-18 17:50:47 +08:00
CJK_mkp 703cecbd4f Merge pull request #196 from InkCanvasForClass/main
没事干合并一下commit
2025-09-13 23:15:56 +08:00
CJK_mkp e20bf41cc7 Merge pull request #195 from InkCanvasForClass/beta
ICC CE 1.7.11.0
2025-09-13 23:05:18 +08:00
CJKmkp 0f7b9524f9 fix:收纳模式下进入PPT自动进入批注异常 2025-09-13 22:53:31 +08:00
CJKmkp 53a9e901ec fix:退出PPT时错误的快捷键状态 2025-09-13 22:44:29 +08:00
CJKmkp 9a79fc71ed improve:PPT墨迹 2025-09-13 22:20:41 +08:00
CJKmkp e7f7a8038f 优化代码 2025-09-13 21:41:34 +08:00
CJKmkp 5361f8ae6f improve:墨迹延迟 2025-09-13 21:38:03 +08:00
CJKmkp ec3bcddc9d 更新版本号 2025-09-13 21:15:58 +08:00
CJKmkp 326a9f1d75 优化代码 2025-09-13 21:10:36 +08:00
CJKmkp 8c07e9b8a3 优化代码 2025-09-13 19:54:21 +08:00
CJKmkp 406d01febf 优化代码 2025-09-13 19:35:36 +08:00
CJKmkp 98663422b1 优化代码 2025-09-13 19:29:17 +08:00
CJKmkp 879dfcea28 add:issue #190 2025-09-13 19:10:40 +08:00
CJKmkp 40edb4c8de Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-09-13 18:53:49 +08:00
CJKmkp 07b6d142ad add:issue #190 2025-09-13 18:52:23 +08:00
2,2,3-三甲基戊烷 5716b3e3cf docs&fix: 移除ssh还存在于readme贡献者名单的bug 2025-09-13 18:45:59 +08:00
CJKmkp 1b242a07e1 delete:ssh 2025-09-13 18:29:13 +08:00
CJK_mkp 1516a73228 Merge pull request #192 from ShihaoShen2025/patch-1
删除all contributors src中有关hydro的信息
2025-09-13 18:22:46 +08:00
CJKmkp eef00eb28f add:issue #190 2025-09-13 18:20:56 +08:00
CJKmkp a5ac282ab6 add:issue #190 2025-09-13 18:13:31 +08:00
CJKmkp dbb88d4999 add:issue #190 2025-09-13 18:07:42 +08:00
电教沈同学 39addad3a1 Delete myself from ac-src
是时候给一切画上一个句号了。
再见,ICC-CE。
2025-09-13 17:58:06 +08:00
电教沈同学 87fd9a4470 Update .all-contributorsrc 2025-09-13 17:52:52 +08:00
电教沈同学 7a9156449f Update .all-contributorsrc 2025-09-13 17:50:36 +08:00
CJKmkp 6e2938e9c1 add:issue #190 2025-09-13 17:37:11 +08:00
CJKmkp 39e1498b26 add:issue #180 2025-09-13 16:36:41 +08:00
CJKmkp cc2dc9c1a7 add:issue #180 2025-09-13 16:28:31 +08:00
CJKmkp fa87198131 add:issue #180 2025-09-13 16:26:23 +08:00
CJKmkp 9b70b952f6 add:issue #180 2025-09-13 16:13:22 +08:00
CJKmkp b29fca1cdb add:issue #180 2025-09-13 16:09:51 +08:00
CJKmkp 501af800ce add:issue #180 2025-09-13 16:06:19 +08:00
CJKmkp 979be117c6 add:issue #180 2025-09-13 16:02:39 +08:00
CJKmkp 13594371bb add:issue #180 2025-09-13 15:34:50 +08:00
CJKmkp 9628d1b27f add:issue #181 2025-09-13 14:59:47 +08:00
CJKmkp fd0bd0b343 fix:托盘功能不可用 2025-09-13 14:33:49 +08:00
CJKmkp 9ea58bfdad 优化代码 2025-09-13 14:21:07 +08:00
CJKmkp c54d140107 improve:截图 2025-09-13 14:16:11 +08:00
CJKmkp 98d4a4213c improve:截图 2025-09-13 13:59:54 +08:00
CJKmkp a7d1de5ee3 improve:截图 2025-09-13 13:50:41 +08:00
CJKmkp fab9e4b265 improve:截图 2025-09-13 13:37:51 +08:00
CJKmkp d5fa46033e 优化日志 2025-09-13 13:03:04 +08:00
CJKmkp 32c179bbf9 improve:弹窗 2025-09-13 12:22:08 +08:00
CJKmkp 99f9886876 improve:直接调用外部点名 2025-09-13 12:08:45 +08:00
CJKmkp c1fcaff28a fix:issue #175 简易实现(后续会改) 2025-09-13 12:05:13 +08:00
CJKmkp 2da5e8d71b fix:动画导致的收纳问题 2025-09-13 11:56:26 +08:00
CJKmkp e80e64a287 fix:手势开关状态显示错误 2025-09-13 11:46:49 +08:00
CJKmkp 0344e51ef7 fix:issue #185 2025-09-13 11:38:46 +08:00
CJKmkp 9fd31b0584 improve:描述性文字 2025-09-13 11:32:08 +08:00
CJKmkp c6a48f79da fix:issue #184 2025-09-13 11:29:48 +08:00
CJKmkp 110d050cc6 fix:issue #188 2025-09-13 11:08:56 +08:00
CJKmkp 18188ef235 improve:弹窗 2025-09-13 11:04:04 +08:00
CJKmkp 3e1c397132 fix:issue #160 2025-09-13 10:58:43 +08:00
CJKmkp cda1f0b77d fix:无焦点功能状态丢失 2025-09-13 10:55:27 +08:00
CJKmkp afd49f154c improve:模式切换 issue #179 2025-09-13 10:51:08 +08:00
CJKmkp 505c620103 improve:PowerPoint联动增强 2025-09-13 10:48:33 +08:00
CJKmkp 5d9e340d6d improve:模式切换 issue #179 2025-09-13 10:46:02 +08:00
CJKmkp f705c4575c Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-09-13 10:43:50 +08:00
CJKmkp 1508bd806f 优化日志 2025-09-13 10:43:22 +08:00
CJK_mkp 1c542e0615 Update bug_report.yml 2025-09-12 15:59:11 +08:00
CJK_mkp ed5bcbde18 Update feature_request.yml 2025-09-12 15:58:36 +08:00
PrefacedCorg 084cbcd362 代码清理 2025-09-07 13:30:46 +08:00
CJKmkp ad8369cfe9 更新版本号 2025-09-07 13:25:20 +08:00
CJKmkp 7d36b3993e 优化代码 2025-09-07 09:41:42 +08:00
CJKmkp ead0854c8e improve:墨迹选择 2025-09-07 07:59:21 +08:00
CJKmkp 60b5655574 improve:墨迹选择 2025-09-07 07:53:05 +08:00
CJKmkp 9037630990 fix:issue #175 2025-09-07 01:50:44 +08:00
CJKmkp ba23fc9506 add:issue #171 2025-09-07 01:45:50 +08:00
CJKmkp 938ca7c0ea improve:墨迹选择 2025-09-07 01:22:46 +08:00
CJKmkp a034f7a9a0 improve:墨迹选择 2025-09-07 01:16:27 +08:00
PrefacedCorg d4c4fcfd74 Update PluginSettingsWindow.xaml 2025-09-07 01:02:34 +08:00
PrefacedCorg 5b457f2a8a 将插件管理器的页面改为无边框 2025-09-07 00:59:24 +08:00
CJKmkp adc22441fc fix:issue #173 2025-09-07 00:57:48 +08:00
CJKmkp 1ba59136c6 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-09-07 00:49:58 +08:00
CJKmkp 77702b2ac9 fix:issue #170 2025-09-07 00:49:10 +08:00
PrefacedCorg 0d6c814908 好像改错了() 2025-09-07 00:49:01 +08:00
PrefacedCorg 42ce58db60 贡献名单add PrefacedCorg(2) 2025-09-07 00:47:30 +08:00
PrefacedCorg 1ef56033fb 贡献名单add PrefacedCorg(1) 2025-09-07 00:31:22 +08:00
CJKmkp 8c1d3c0248 fix:issue #168 2025-09-07 00:20:48 +08:00
CJKmkp a725a12d25 fix:issue #169 2025-09-07 00:14:59 +08:00
CJKmkp f67db9beed fix:issue #178 #177 2025-09-07 00:11:22 +08:00
CJKmkp f22fd7b5d1 fix:issue #172 2025-09-07 00:08:11 +08:00
CJKmkp 060664260b fix:issue #172 2025-09-07 00:03:15 +08:00
CJKmkp 9da5ec7413 撤销操作 2025-09-06 23:58:00 +08:00
CJKmkp 076240f7cd 测试构建 2025-09-06 23:55:54 +08:00
CJKmkp d97a4ad243 fix:墨迹识别 2025-09-06 23:21:05 +08:00
CJKmkp 56209d8491 fix:issue #159 2025-09-06 22:22:41 +08:00
CJK_mkp 61fe7197f5 Merge pull request #166 from InkCanvasForClass/beta
ICC CE 1.7.10.0
2025-09-06 21:40:14 +08:00
CJKmkp 1881bfd69e 更新版本号 2025-09-06 21:35:07 +08:00
CJKmkp 4287b7def5 improve:用户分级 2025-09-06 21:33:06 +08:00
CJKmkp 27d683a7cc improve:PPT及自动更新 2025-09-06 21:26:46 +08:00
CJKmkp 38902c8f88 撤回操作 2025-09-06 20:59:45 +08:00
CJKmkp d8e8142ff4 发行说明测试 2025-09-06 20:57:24 +08:00
CJKmkp fdaf9cb3ef 发行说明测试 2025-09-06 20:37:05 +08:00
CJKmkp b2fe091c88 发行说明测试 2025-09-06 20:27:52 +08:00
CJKmkp 8548244cef 发行说明测试 2025-09-06 20:24:56 +08:00
CJKmkp 91a5881600 发行说明测试 2025-09-06 20:22:59 +08:00
CJKmkp 93fd043b14 发行说明测试 2025-09-06 20:21:24 +08:00
CJKmkp 47948ef530 更新版本号 2025-09-06 20:08:26 +08:00
CJKmkp 61fa618673 improve:用户分级 2025-09-06 20:02:50 +08:00
CJKmkp 545aecc6ae 更新版本号 2025-09-06 19:31:14 +08:00
CJKmkp ee41f53286 add:issue #156 2025-09-06 19:27:17 +08:00
CJKmkp cc8036f736 improve:无焦点 2025-09-06 18:12:45 +08:00
CJKmkp 67a982e6e4 improve:用户分级 2025-09-06 18:03:23 +08:00
CJKmkp b16000f8e5 improve:窗口置顶 2025-09-06 17:51:25 +08:00
CJKmkp a142ce0542 回滚操作 2025-09-06 17:41:34 +08:00
CJKmkp c87b8b65fc improve:高光显示 2025-09-06 17:12:25 +08:00
CJKmkp 7332df1d56 fix:issue #165 #133 及插件窗口问题 2025-09-06 15:50:16 +08:00
CJKmkp de90c17ab1 fix:issue #159 2025-09-06 15:36:40 +08:00
CJKmkp 3d54edf25b fix:issue #161 2025-09-06 15:28:14 +08:00
CJKmkp 111819dbf3 fix:issue #164 #155 2025-09-06 15:16:03 +08:00
CJKmkp 3665753ba2 优化日志 2025-09-06 15:14:53 +08:00
CJKmkp 75e38aa8f3 improve:issue #160 2025-09-06 15:09:34 +08:00
CJKmkp 29dc6938c3 improve:窗口置顶 2025-09-06 15:07:26 +08:00
CJKmkp 304933b02b improve:快捷键 2025-09-06 14:54:49 +08:00
CJKmkp 49f268bb62 improve:快捷键 2025-09-06 14:48:36 +08:00
CJKmkp d0793c546d improve:快捷键 2025-09-06 14:36:07 +08:00
CJKmkp 375aec6f6c improve:按钮显示 2025-09-06 14:24:18 +08:00
CJKmkp 12ec527dcd improve:浮动栏 2025-09-06 14:19:33 +08:00
CJKmkp 52d95173d7 improve:PPT模块及浮动栏 2025-09-06 14:18:09 +08:00
CJKmkp 67e3d51106 improve:窗口置顶 2025-09-06 13:58:49 +08:00
CJKmkp 9081aea926 improve:墨迹渐隐 2025-09-06 13:48:44 +08:00
CJKmkp 02c6caf465 fix:issue #153 2025-09-06 13:30:32 +08:00
CJKmkp 1a9ddf1969 improve:墨迹渐隐 2025-09-06 13:16:10 +08:00
CJKmkp 41bcae9d05 fix:issue #153 2025-09-06 12:38:57 +08:00
CJKmkp 28109a0c97 improve:PPT联动 2025-09-06 12:09:06 +08:00
CJKmkp d8825f0a73 fix:issue #154 2025-09-06 10:20:28 +08:00
CJKmkp 82c045a243 fix:issue #152 2025-09-06 10:17:12 +08:00
CJKmkp 4d11f69282 fix:issue #151 2025-09-06 10:11:19 +08:00
CJKmkp 664e4ea048 add:新设置 2025-08-31 17:11:04 +08:00
CJKmkp 6d21c5e24e add:新设置 2025-08-31 17:07:40 +08:00
CJKmkp ba21b6884d add:新设置 2025-08-31 16:25:52 +08:00
CJKmkp 60065aab3e add:新设置 2025-08-31 16:24:57 +08:00
CJKmkp 4d978936ac add:新设置 2025-08-31 16:17:21 +08:00
CJKmkp 4ea585633b add:新设置 2025-08-31 16:06:36 +08:00
CJKmkp dee0bd7f4d add:新设置 2025-08-31 15:58:58 +08:00
CJKmkp edb206ffa5 add:新设置 2025-08-31 15:35:28 +08:00
CJKmkp 21932056f3 improve:插件 2025-08-31 13:14:05 +08:00
CJK_mkp 0c3809b789 Merge pull request #147 from InkCanvasForClass/beta
ICC CE 1.7.9.1
2025-08-31 12:39:00 +08:00
CJKmkp d5ae596ad6 更新版本号 2025-08-31 12:37:49 +08:00
CJKmkp 42db7e60f2 improve:截图 2025-08-31 12:32:15 +08:00
CJKmkp 85827820a4 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-08-31 12:17:13 +08:00
CJKmkp fad2571edb fix:合并冲突 2025-08-31 12:16:29 +08:00
PrefacedCorg c53b6de68c Update ScreenshotSelectorWindow.xaml.cs 2025-08-31 12:14:46 +08:00
PrefacedCorg c86eb12168 Update YesOrNoNotificationWindow.xaml 2025-08-31 12:07:59 +08:00
CJKmkp fb31f8df11 优化代码 2025-08-31 12:05:26 +08:00
CJKmkp 147b4fc5ed improve:文件关联 2025-08-31 12:03:58 +08:00
CJKmkp cdcb2d2af0 add:文件关联 2025-08-31 11:49:00 +08:00
PrefacedCorg ff086e497c 代码清理 2025-08-31 11:43:52 +08:00
PrefacedCorg a2b711da05 改了个图标 2025-08-31 11:41:17 +08:00
CJKmkp 40ecfbefa3 improve:截图 2025-08-31 10:47:33 +08:00
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
647 changed files with 71708 additions and 150963 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": "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"
]
},
{
"login": "Tayasui-rainnya",
"name": "tayasui rainnya!",
"avatar_url": "https://avatars.githubusercontent.com/u/156585442?v=4",
"profile": "https://github.com/Tayasui-rainnya",
"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: true
- type: textarea
id: expected
attributes:
label: 期望结果 | Expected Behavior
description: 你期望的正确行为或结果 | What did you expect to happen?
validations:
required: true
- 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: true
- 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
+2 -2
View File
@@ -26,10 +26,10 @@ jobs:
- name: Build the Solution
run: |
msbuild -t:restore /p:GitFlow="Github Action"
msbuild /p:platform="Any CPU" /p:configuration="Release" /p:GitFlow="Github Action" "Ink Canvas/InkCanvasForClass.csproj"
msbuild /p:platform="AnyCPU" /p:configuration="Debug" /p:GitFlow="Github Action" "Ink Canvas/InkCanvasForClass.csproj"
- name: Upload to artifact
uses: actions/upload-artifact@v4.5.0
with:
name: InkCanvasForClass
path: "Ink Canvas/bin/Any CPU/Release/net472/"
path: "Ink Canvas/bin/Debug/net472"
+311
View File
@@ -0,0 +1,311 @@
name: Pre-release and Changelog
on:
workflow_dispatch:
inputs:
version_type:
description: 'Version bump type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
- build
prerelease:
description: 'Create as pre-release'
required: true
default: true
type: boolean
jobs:
prerelease:
if: github.ref == 'refs/heads/main'
runs-on: windows-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4.2.2
with:
fetch-depth: 0 # 获取所有历史记录用于生成changelog
- name: Setup MSbuild
uses: microsoft/setup-msbuild@v2
- name: Setup NuGet
uses: NuGet/setup-nuget@v2.0.1
- name: Restore NuGet Packages
run: nuget restore "Ink Canvas.sln"
- name: Get current version from Git tag
id: get_version
run: |
# 获取最新的tag
$latestTag = git describe --tags --abbrev=0 2>$null
if ($latestTag) {
# 移除v前缀(如果有的话)
$version = $latestTag -replace "^v", ""
echo "Found latest tag: $latestTag"
} else {
# 如果没有tag,使用默认版本
$version = "1.0.0.0"
echo "No tags found, using default version"
}
echo "current_version=$version" >> $env:GITHUB_OUTPUT
echo "Current version: $version"
- name: Calculate new version
id: calc_version
run: |
$currentVersion = "${{ steps.get_version.outputs.current_version }}"
$versionParts = $currentVersion.Split('.')
# 确保版本号格式正确(支持4部分)
if ($versionParts.Length -ge 3) {
$major = [int]$versionParts[0]
$minor = [int]$versionParts[1]
$patch = [int]$versionParts[2]
$build = [int]$versionParts[3]
} else {
# 如果版本号格式不正确,使用默认值
$major = 1
$minor = 0
$patch = 0
$build = 0
}
$versionType = "${{ github.event.inputs.version_type }}"
switch ($versionType) {
"major" {
$major++
$minor = 0
$patch = 0
$build = 0
}
"minor" {
$minor++
$patch = 0
$build = 0
}
"patch" {
$patch++
$build = 0
}
"build" {
$build++
}
}
# 生成新版本号(4位格式,如1.7.18.0)
$newVersion = "$major.$minor.$patch.$build"
echo "new_version=$newVersion" >> $env:GITHUB_OUTPUT
echo "New version: $newVersion"
- name: Generate Changelog
id: changelog
run: |
# 获取上次tag到现在的所有commit
$lastTag = git describe --tags --abbrev=0 2>$null
if ($lastTag) {
$commits = git log --pretty=format:"%h|%s|%an|%ad" --date=short "$lastTag..HEAD"
} else {
$commits = git log --pretty=format:"%h|%s|%an|%ad" --date=short
}
# 初始化分类数组
$fixes = @()
$improvements = @()
$additions = @()
$deletions = @()
$versionChanges = @()
$others = @()
# 解析每个commit
foreach ($commit in $commits) {
if ($commit -match "^([^|]+)\|([^|]+)\|([^|]+)\|([^|]+)$") {
$hash = $matches[1]
$message = $matches[2]
$author = $matches[3]
$date = $matches[4]
$commitInfo = @{
Hash = $hash
Message = $message
Author = $author
Date = $date
}
# 根据commit消息分类
if ($message -match "^(fix|修复)") {
$fixes += $commitInfo
} elseif ($message -match "^(improve|改进|优化)") {
$improvements += $commitInfo
} elseif ($message -match "^(add|新增|添加)") {
$additions += $commitInfo
} elseif ($message -match "^(delete|删除|移除)") {
$deletions += $commitInfo
} elseif ($message -match "(版本|version|更新版本号)") {
$versionChanges += $commitInfo
} else {
$others += $commitInfo
}
}
}
# 生成changelog内容
$version = "${{ steps.calc_version.outputs.new_version }}"
$changelog = "# ICC CE $version 更新日志`n`n## 修复 (Fixes)"
if ($fixes.Count -gt 0) {
foreach ($fix in $fixes) {
$changelog += "`n- $($fix.Message) ($($fix.Author), $($fix.Date))"
}
} else {
$changelog += "`n- 无"
}
$changelog += "`n`n## 改进 (Improvements)"
if ($improvements.Count -gt 0) {
foreach ($improvement in $improvements) {
$changelog += "`n- $($improvement.Message) ($($improvement.Author), $($improvement.Date))"
}
} else {
$changelog += "`n- 无"
}
$changelog += "`n`n## 新增功能 (New Features)"
if ($additions.Count -gt 0) {
foreach ($addition in $additions) {
$changelog += "`n- $($addition.Message) ($($addition.Author), $($addition.Date))"
}
} else {
$changelog += "`n- 无"
}
$changelog += "`n`n## 删除功能 (Removed Features)"
if ($deletions.Count -gt 0) {
foreach ($deletion in $deletions) {
$changelog += "`n- $($deletion.Message) ($($deletion.Author), $($deletion.Date))"
}
} else {
$changelog += "`n- 无"
}
$changelog += "`n`n## 版本更新 (Version Updates)"
if ($versionChanges.Count -gt 0) {
foreach ($versionChange in $versionChanges) {
$changelog += "`n- $($versionChange.Message) ($($versionChange.Author), $($versionChange.Date))"
}
} else {
$changelog += "`n- 无"
}
$changelog += "`n`n## 其他更改 (Other Changes)"
if ($others.Count -gt 0) {
foreach ($other in $others) {
$changelog += "`n- $($other.Message) ($($other.Author), $($other.Date))"
}
} else {
$changelog += "`n- 无"
}
$changelog += "`n`n---`n*此更新日志由GitHub Actions自动生成*"
# 保存changelog到文件
$changelog | Out-File -FilePath "CHANGELOG_${{ steps.calc_version.outputs.new_version }}.md" -Encoding UTF8
# 输出changelog内容到步骤输出
echo "changelog<<EOF" >> $env:GITHUB_OUTPUT
echo $changelog >> $env:GITHUB_OUTPUT
echo "EOF" >> $env:GITHUB_OUTPUT
echo "Changelog generated successfully"
- name: Display version info
run: |
echo "Current version: ${{ steps.get_version.outputs.current_version }}"
echo "New version: ${{ steps.calc_version.outputs.new_version }}"
echo "Note: Version will not be automatically updated in repository files"
- name: Build the Solution
run: |
msbuild -t:restore /p:GitFlow="Github Action"
msbuild /p:platform="AnyCPU" /p:configuration="Release" /p:GitFlow="Github Action" "Ink Canvas/InkCanvasForClass.csproj"
- name: Create Release Archive
run: |
$version = "${{ steps.calc_version.outputs.new_version }}"
$archiveName = "InkCanvasForClass.CE.$version.zip"
# 创建发布目录
New-Item -ItemType Directory -Path "release" -Force
# 复制发布文件
Copy-Item "Ink Canvas\bin\Release\net472\*" "release\" -Recurse -Force
# 创建压缩包
Compress-Archive -Path "release\*" -DestinationPath $archiveName -Force
echo "archive_name=$archiveName" >> $env:GITHUB_OUTPUT
- name: Upload Release Assets
uses: actions/upload-artifact@v4.5.0
with:
name: release-files-${{ steps.calc_version.outputs.new_version }}
path: |
InkCanvasForClass.CE.${{ steps.calc_version.outputs.new_version }}.zip
CHANGELOG_${{ steps.calc_version.outputs.new_version }}.md
- name: Prepare Release Info
run: |
$version = "${{ steps.calc_version.outputs.new_version }}"
echo "Preparing release for version: $version"
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.calc_version.outputs.new_version }}
name: ICC CE ${{ steps.calc_version.outputs.new_version }}
body: |
${{ steps.changelog.outputs.changelog }}
draft: false
prerelease: ${{ github.event.inputs.prerelease }}
files: |
InkCanvasForClass.CE.${{ steps.calc_version.outputs.new_version }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Generate UpdateLog preview
run: |
$version = "${{ steps.calc_version.outputs.new_version }}"
$changelogFile = "CHANGELOG_$version.md"
# 读取生成的changelog
$changelogContent = Get-Content $changelogFile -Raw
# 生成预览内容
$previewContent = "ICC CE $version 更新日志`n" + $changelogContent
echo "UpdateLog preview generated (not written to file):"
echo $previewContent
- name: Display Summary
run: |
echo "=== Release Summary ==="
echo "Version: ${{ steps.calc_version.outputs.new_version }}"
echo "Pre-release: ${{ github.event.inputs.prerelease }}"
echo "Changelog: Generated and attached to release"
echo "Archive: ICC_CE_${{ steps.calc_version.outputs.new_version }}.zip"
echo ""
echo "Note: No repository files were modified"
echo "You can manually update version numbers and changelog as needed"
+428 -3
View File
@@ -1,3 +1,428 @@
/Ink Canvas/obj
/Ink Canvas/bin
/.vs
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
*.env
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Dd]ebug/x64/
[Dd]ebugPublic/x64/
[Rr]elease/x64/
[Rr]eleases/x64/
bin/x64/
obj/x64/
[Dd]ebug/x86/
[Dd]ebugPublic/x86/
[Rr]elease/x86/
[Rr]eleases/x86/
bin/x86/
obj/x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
[Aa][Rr][Mm]64[Ee][Cc]/
bld/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Build results on 'Bin' directories
**/[Bb]in/*
# Uncomment if you have tasks that rely on *.refresh files to move binaries
# (https://github.com/github/gitignore/pull/3736)
#!**/[Bb]in/*.refresh
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*.trx
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Approval Tests result files
*.received.*
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.idb
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
**/.paket/paket.exe
paket-files/
# FAKE - F# Make
**/.fake/
# CodeRush personal settings
**/.cr/personal
# Python Tools for Visual Studio (PTVS)
**/__pycache__/
*.pyc
# Cake - Uncomment if you are using it
#tools/**
#!tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
MSBuild_Logs/
# AWS SAM Build and Temporary Artifacts folder
.aws-sam
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
**/.mfractor/
# Local History for Visual Studio
**/.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
**/.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
+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 @@
1.4.7
1.7.18.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

-2
View File
@@ -1,2 +0,0 @@
<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>
File diff suppressed because one or more lines are too long
+4 -5
View File
@@ -4,13 +4,13 @@
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">
<EventSetter Event="PreviewMouseWheel" Handler="ScrollViewer_PreviewMouseWheel"/>
</Style>
<ContextMenu Opened="SysTrayMenu_Opened" x:Shared="false" x:Key="SysTrayMenu" Padding="6" ui:ThemeManager.RequestedTheme="Light">
<ContextMenu Opened="SysTrayMenu_Opened" Closed="SysTrayMenu_Closed" x:Shared="false" x:Key="SysTrayMenu" Padding="6" ui:ThemeManager.RequestedTheme="Light">
<MenuItem IsCheckable="True" IsChecked="False" Checked="HideICCMainWindowTrayIconMenuItem_Checked" Unchecked="HideICCMainWindowTrayIconMenuItem_UnChecked" Name="HideICCMainWindowTrayIconMenuItem">
<MenuItem.Header>
<ui:SimpleStackPanel Orientation="Horizontal" Margin="-4,0,0,0">
@@ -32,7 +32,7 @@
</MenuItem.Icon>
</MenuItem>
<Separator Margin="0,3" />
<MenuItem>
<MenuItem Name="DisableAllHotkeysMenuItem" Click="DisableAllHotkeysMenuItem_Clicked">
<MenuItem.Header>
<ui:SimpleStackPanel Orientation="Horizontal" Margin="-4,0,0,0">
<TextBlock FontSize="14" VerticalAlignment="Center" Foreground="#18181b" Text="禁用所有快捷键" />
@@ -232,12 +232,11 @@
ContextMenu="{StaticResource SysTrayMenu}"
IconSource="/Resources/icc.ico"/>
<ResourceDictionary.MergedDictionaries>
<ui:ThemeResources RequestedTheme="Light"/>
<ui:ThemeResources/>
<ui:XamlControlsResources />
<ResourceDictionary Source="Resources/SeewoImageDictionary.xaml"/>
<ResourceDictionary Source="Resources/DrawShapeImageDictionary.xaml"/>
<ResourceDictionary Source="Resources/IconImageDictionary.xaml"/>
<ResourceDictionary Source="Resources/Styles/Light.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
+1150 -27
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
@@ -8,9 +8,9 @@ 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: AssemblyCopyright("Copyright © CJK_mkp 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("5.0.2.0")]
[assembly: AssemblyFileVersion("5.0.2.0")]
[assembly: AssemblyVersion("1.7.18.0")]
[assembly: AssemblyFileVersion("1.7.18.0")]
@@ -0,0 +1,86 @@
<UserControl x:Class="Ink_Canvas.Controls.QuickDrawFloatingButtonControl"
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"
mc:Ignorable="d"
Width="65" Height="45">
<UserControl.Resources>
<ResourceDictionary>
<!-- 悬浮按钮资源 -->
<SolidColorBrush x:Key="QuickDrawFloatingButtonBackground" Color="#80000000"/>
<SolidColorBrush x:Key="QuickDrawFloatingButtonBorderBrush" Color="#40000000"/>
<SolidColorBrush x:Key="QuickDrawFloatingButtonIconForeground" Color="White"/>
</ResourceDictionary>
</UserControl.Resources>
<Border Background="{DynamicResource QuickDrawFloatingButtonBackground}"
CornerRadius="8"
BorderThickness="1"
BorderBrush="{DynamicResource QuickDrawFloatingButtonBorderBrush}">
<Border.Effect>
<DropShadowEffect Color="Black" Direction="315" ShadowDepth="3" Opacity="0.3" BlurRadius="5"/>
</Border.Effect>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="22"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 拖动区域 -->
<Border Grid.Column="0"
MouseLeftButtonDown="DragArea_MouseLeftButtonDown"
MouseMove="DragArea_MouseMove"
MouseLeftButtonUp="DragArea_MouseLeftButtonUp"
Cursor="SizeAll"
Background="Transparent">
<Grid VerticalAlignment="Center" Height="14" IsHitTestVisible="False">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 三个白色横线 -->
<Border Grid.Row="0" Background="{DynamicResource QuickDrawFloatingButtonIconForeground}" Height="2" Width="10"
HorizontalAlignment="Center"
CornerRadius="1" Opacity="0.8" IsHitTestVisible="False"/>
<Border Grid.Row="2" Background="{DynamicResource QuickDrawFloatingButtonIconForeground}" Height="2" Width="10"
HorizontalAlignment="Center"
CornerRadius="1" Opacity="0.8" IsHitTestVisible="False"/>
<Border Grid.Row="4" Background="{DynamicResource QuickDrawFloatingButtonIconForeground}" Height="2" Width="10"
HorizontalAlignment="Center"
CornerRadius="1" Opacity="0.8" IsHitTestVisible="False"/>
</Grid>
</Border>
<!-- 半透明分割线 -->
<Rectangle Grid.Column="1" Width="1" Fill="#20FFFFFF" Margin="0,8,0,8"/>
<!-- 按钮区域 -->
<Border Grid.Column="2"
MouseLeftButtonDown="FloatingButton_Click"
Cursor="Hand"
Background="Transparent">
<Grid IsHitTestVisible="False">
<Path Data="M5 7C5 8.06087 5.42143 9.07828 6.17157 9.82843C6.92172 10.5786 7.93913 11 9 11C10.0609 11 11.0783 10.5786 11.8284 9.82843C12.5786 9.07828 13 8.06087 13 7C13 5.93913 12.5786 4.92172 11.8284 4.17157C11.0783 3.42143 10.0609 3 9 3C7.93913 3 6.92172 3.42143 6.17157 4.17157C5.42143 4.92172 5 5.93913 5 7Z M3 21V19C3 17.9391 3.42143 16.9217 4.17157 16.1716C4.92172 15.4214 5.93913 15 7 15H11C12.0609 15 13.0783 15.4214 13.8284 16.1716C14.5786 16.9217 15 17.9391 15 19V21 M16 3.13C16.8604 3.35031 17.623 3.85071 18.1676 4.55232C18.7122 5.25392 19.0078 6.11683 19.0078 7.005C19.0078 7.89318 18.7122 8.75608 18.1676 9.45769C17.623 10.1593 16.8604 10.6597 16 10.88 M21 21V19C20.9949 18.1172 20.6979 17.2608 20.1553 16.5644C19.6126 15.868 18.8548 15.3707 18 15.15"
Stroke="{DynamicResource QuickDrawFloatingButtonIconForeground}"
StrokeThickness="2"
StrokeLineJoin="Round"
Fill="Transparent"
Width="20" Height="20"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsHitTestVisible="False"/>
</Grid>
</Border>
</Grid>
</Border>
</UserControl>
@@ -0,0 +1,149 @@
using Ink_Canvas.Windows;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using HorizontalAlignment = System.Windows.HorizontalAlignment;
using VerticalAlignment = System.Windows.VerticalAlignment;
namespace Ink_Canvas.Controls
{
/// <summary>
/// 快抽悬浮按钮控件
/// </summary>
public partial class QuickDrawFloatingButtonControl : UserControl
{
private bool _isDragging = false;
private Point _dragStartPoint;
private Point _controlStartPoint;
public QuickDrawFloatingButtonControl()
{
InitializeComponent();
}
/// <summary>
/// 快抽按钮点击事件
/// </summary>
private void FloatingButton_Click(object sender, MouseButtonEventArgs e)
{
try
{
// 如果正在拖动,不触发点击事件
if (_isDragging) return;
// 打开快抽窗口
var quickDrawWindow = new QuickDrawWindow();
quickDrawWindow.ShowDialog();
}
catch (Exception ex)
{
Helpers.LogHelper.WriteLogToFile($"打开快抽窗口失败: {ex.Message}", Helpers.LogHelper.LogType.Error);
}
}
/// <summary>
/// 拖动区域鼠标按下事件
/// </summary>
private void DragArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_isDragging = false;
// 记录鼠标在屏幕上的初始位置
_dragStartPoint = this.PointToScreen(e.GetPosition(this));
// 记录控件的初始位置
var parent = this.Parent as FrameworkElement;
if (parent != null)
{
var transform = this.TransformToVisual(parent);
var currentPos = transform.Transform(new Point(0, 0));
_controlStartPoint = currentPos;
}
else
{
var currentMargin = this.Margin;
_controlStartPoint = new Point(
double.IsNaN(currentMargin.Left) ? 0 : currentMargin.Left,
double.IsNaN(currentMargin.Top) ? 0 : currentMargin.Top);
}
((UIElement)sender).CaptureMouse();
e.Handled = true;
}
/// <summary>
/// 拖动区域鼠标移动事件
/// </summary>
private void DragArea_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && ((UIElement)sender).IsMouseCaptured)
{
// 获取鼠标在屏幕上的当前位置
Point currentScreenPoint = this.PointToScreen(e.GetPosition(this));
Vector diff = currentScreenPoint - _dragStartPoint;
if (!_isDragging && (Math.Abs(diff.X) > 3 || Math.Abs(diff.Y) > 3))
{
_isDragging = true;
// 切换到绝对定位模式
this.HorizontalAlignment = HorizontalAlignment.Left;
this.VerticalAlignment = VerticalAlignment.Top;
}
if (_isDragging)
{
// 计算新位置
var parent = this.Parent as FrameworkElement;
if (parent != null)
{
// 计算屏幕坐标相对于父容器的位置
var parentPoint = parent.PointFromScreen(currentScreenPoint);
var startParentPoint = parent.PointFromScreen(_dragStartPoint);
// 计算相对于初始位置的偏移
double offsetX = parentPoint.X - startParentPoint.X;
double offsetY = parentPoint.Y - startParentPoint.Y;
// 新位置 = 初始位置 + 偏移
double newLeft = _controlStartPoint.X + offsetX;
double newTop = _controlStartPoint.Y + offsetY;
// 限制在父容器范围内
newLeft = Math.Max(0, Math.Min(newLeft, parent.ActualWidth - this.ActualWidth));
newTop = Math.Max(0, Math.Min(newTop, parent.ActualHeight - this.ActualHeight));
// 更新Margin
this.Margin = new Thickness(newLeft, newTop, 0, 0);
}
}
}
}
/// <summary>
/// 拖动区域鼠标释放事件
/// </summary>
private void DragArea_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (((UIElement)sender).IsMouseCaptured)
{
((UIElement)sender).ReleaseMouseCapture();
}
if (_isDragging)
{
Dispatcher.BeginInvoke(new Action(() => { _isDragging = false; }),
DispatcherPriority.Background);
}
else
{
_isDragging = false;
}
e.Handled = true;
}
}
}
+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>
File diff suppressed because it is too large Load Diff
+215
View File
@@ -0,0 +1,215 @@
using Newtonsoft.Json;
using System;
using System.IO;
using System.Linq;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 自动备份管理器
/// 负责管理配置文件的自动备份功能
/// </summary>
public static class AutoBackupManager
{
private static readonly string BackupDir = Path.Combine(App.RootPath, "Backups");
private static readonly string SettingsFile = Path.Combine(App.RootPath, "Configs", "Settings.json");
private static readonly string BackupPrefix = "Settings_AutoBackup_";
/// <summary>
/// 检查是否需要执行自动备份
/// </summary>
/// <param name="settings">设置对象</param>
/// <returns>如果需要备份返回true,否则返回false</returns>
public static bool ShouldPerformAutoBackup(Settings settings)
{
try
{
// 如果自动备份功能未启用,不执行备份
if (!settings.Advanced.IsAutoBackupEnabled)
{
return false;
}
// 如果从未备份过,需要创建首次备份
if (settings.Advanced.LastAutoBackupTime == DateTime.MinValue)
{
return true;
}
// 检查是否已超过备份间隔
var daysSinceLastBackup = (DateTime.Now - settings.Advanced.LastAutoBackupTime).TotalDays;
return daysSinceLastBackup >= settings.Advanced.AutoBackupIntervalDays;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检查自动备份条件时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 执行自动备份
/// </summary>
/// <param name="settings">设置对象</param>
/// <returns>备份是否成功</returns>
public static bool PerformAutoBackup(Settings settings)
{
try
{
// 确保备份目录存在
if (!Directory.Exists(BackupDir))
{
Directory.CreateDirectory(BackupDir);
}
// 检查主配置文件是否存在
if (!File.Exists(SettingsFile))
{
LogHelper.WriteLogToFile("主配置文件不存在,跳过自动备份", LogHelper.LogType.Warning);
return false;
}
// 创建备份文件名(使用当前日期时间)
string backupFileName = $"{BackupPrefix}{DateTime.Now:yyyyMMdd_HHmmss}.json";
string backupPath = Path.Combine(BackupDir, backupFileName);
// 复制主配置文件到备份位置
File.Copy(SettingsFile, backupPath, true);
// 更新最后备份时间
settings.Advanced.LastAutoBackupTime = DateTime.Now;
MainWindow.SaveSettingsToFile();
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"执行自动备份时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 尝试从备份恢复配置文件
/// </summary>
/// <returns>恢复是否成功</returns>
public static bool TryRestoreFromBackup()
{
try
{
// 确保备份目录存在
if (!Directory.Exists(BackupDir))
{
LogHelper.WriteLogToFile("备份目录不存在,无法从备份恢复", LogHelper.LogType.Warning);
return false;
}
// 查找最新的备份文件
var backupFiles = Directory.GetFiles(BackupDir, $"{BackupPrefix}*.json")
.OrderByDescending(f => File.GetCreationTime(f))
.ToArray();
if (backupFiles.Length == 0)
{
LogHelper.WriteLogToFile("没有找到可用的备份文件", LogHelper.LogType.Warning);
return false;
}
// 尝试使用最新的备份文件
string latestBackup = backupFiles[0];
// 验证备份文件是否有效
try
{
string backupJson = File.ReadAllText(latestBackup);
var testSettings = JsonConvert.DeserializeObject<Settings>(backupJson);
if (testSettings == null)
{
LogHelper.WriteLogToFile("备份文件内容无效,无法恢复", LogHelper.LogType.Error);
return false;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"备份文件验证失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
// 备份当前损坏的配置文件(如果存在)
if (File.Exists(SettingsFile))
{
string corruptedBackup = Path.Combine(BackupDir, $"Settings_Corrupted_{DateTime.Now:yyyyMMdd_HHmmss}.json");
File.Copy(SettingsFile, corruptedBackup, true);
}
// 从备份恢复配置文件
File.Copy(latestBackup, SettingsFile, true);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从备份恢复配置文件时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 清理过期的备份文件
/// 保留最近30天的备份文件
/// </summary>
public static void CleanupOldBackups()
{
try
{
if (!Directory.Exists(BackupDir))
{
return;
}
var cutoffDate = DateTime.Now.AddDays(-30);
var backupFiles = Directory.GetFiles(BackupDir, $"{BackupPrefix}*.json");
int deletedCount = 0;
foreach (var file in backupFiles)
{
if (File.GetCreationTime(file) < cutoffDate)
{
File.Delete(file);
deletedCount++;
}
}
if (deletedCount > 0)
{
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理过期备份文件时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 初始化自动备份功能
/// 在应用程序启动时调用
/// </summary>
/// <param name="settings">设置对象</param>
public static void Initialize(Settings settings)
{
try
{
// 检查是否需要执行自动备份
if (ShouldPerformAutoBackup(settings))
{
PerformAutoBackup(settings);
}
// 清理过期备份
CleanupOldBackups();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化自动备份功能时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}
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;
}
}
+424
View File
@@ -0,0 +1,424 @@
using AForge.Video;
using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
namespace Ink_Canvas.Helpers
{
public class CameraService : IDisposable
{
private VideoCaptureDevice _videoSource;
private bool _isCapturing;
private Bitmap _currentFrame;
private readonly object _frameLock = new object();
private Dispatcher _dispatcher;
// 新增属性
private int _rotationAngle = 0; // 0=0度,1=90度,2=180度,3=270度
private int _resolutionWidth = 640;
private int _resolutionHeight = 480;
public event EventHandler<Bitmap> FrameReceived;
public event EventHandler<string> ErrorOccurred;
public bool IsCapturing => _isCapturing;
public List<FilterInfo> AvailableCameras { get; private set; }
public FilterInfo CurrentCamera { get; private set; }
// 新增属性
public int RotationAngle
{
get => _rotationAngle;
set => _rotationAngle = Math.Max(0, Math.Min(3, value));
}
public int ResolutionWidth
{
get => _resolutionWidth;
set => _resolutionWidth = Math.Max(320, Math.Min(1920, value));
}
public int ResolutionHeight
{
get => _resolutionHeight;
set => _resolutionHeight = Math.Max(240, Math.Min(1080, value));
}
public CameraService()
{
_dispatcher = Dispatcher.CurrentDispatcher;
AvailableCameras = new List<FilterInfo>();
RefreshCameraList();
}
public CameraService(int rotationAngle, int resolutionWidth, int resolutionHeight)
{
_dispatcher = Dispatcher.CurrentDispatcher;
AvailableCameras = new List<FilterInfo>();
_rotationAngle = rotationAngle;
_resolutionWidth = resolutionWidth;
_resolutionHeight = resolutionHeight;
RefreshCameraList();
}
/// <summary>
/// 刷新可用摄像头列表
/// </summary>
public void RefreshCameraList()
{
try
{
AvailableCameras.Clear();
var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
foreach (FilterInfo device in videoDevices)
{
AvailableCameras.Add(device);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"刷新摄像头列表失败: {ex.Message}", LogHelper.LogType.Error);
ErrorOccurred?.Invoke(this, $"刷新摄像头列表失败: {ex.Message}");
}
}
/// <summary>
/// 开始摄像头预览
/// </summary>
/// <param name="cameraIndex">摄像头索引</param>
public bool StartPreview(int cameraIndex = 0)
{
try
{
if (AvailableCameras.Count == 0)
{
RefreshCameraList();
if (AvailableCameras.Count == 0)
{
ErrorOccurred?.Invoke(this, "未找到可用的摄像头设备");
return false;
}
}
if (cameraIndex < 0 || cameraIndex >= AvailableCameras.Count)
{
ErrorOccurred?.Invoke(this, "摄像头索引超出范围");
return false;
}
// 停止当前预览
StopPreview();
CurrentCamera = AvailableCameras[cameraIndex];
_videoSource = new VideoCaptureDevice(CurrentCamera.MonikerString);
// 设置视频源事件处理
_videoSource.NewFrame += VideoSource_NewFrame;
// 启动视频源
_videoSource.Start();
_isCapturing = true;
LogHelper.WriteLogToFile($"开始摄像头预览: {CurrentCamera.Name}");
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动摄像头预览失败: {ex.Message}", LogHelper.LogType.Error);
ErrorOccurred?.Invoke(this, $"启动摄像头预览失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 停止摄像头预览
/// </summary>
public void StopPreview()
{
try
{
if (_videoSource != null && _videoSource.IsRunning)
{
_videoSource.SignalToStop();
_videoSource.WaitForStop();
_videoSource.NewFrame -= VideoSource_NewFrame;
_videoSource = null;
}
_isCapturing = false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"停止摄像头预览失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 切换到指定摄像头
/// </summary>
/// <param name="cameraIndex">摄像头索引</param>
public bool SwitchCamera(int cameraIndex)
{
try
{
if (cameraIndex < 0 || cameraIndex >= AvailableCameras.Count)
{
ErrorOccurred?.Invoke(this, "摄像头索引超出范围");
return false;
}
return StartPreview(cameraIndex);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换摄像头失败: {ex.Message}", LogHelper.LogType.Error);
ErrorOccurred?.Invoke(this, $"切换摄像头失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 获取当前帧的BitmapSource(WPF格式),直接返回可用的WPF位图
/// </summary>
public BitmapSource GetCurrentFrameAsBitmapSource()
{
lock (_frameLock)
{
if (_currentFrame == null)
return null;
try
{
// 验证位图有效性
if (_currentFrame.Width <= 0 || _currentFrame.Height <= 0)
return null;
// 使用更安全的方法转换位图
var bitmapData = _currentFrame.LockBits(
new Rectangle(0, 0, _currentFrame.Width, _currentFrame.Height),
ImageLockMode.ReadOnly,
_currentFrame.PixelFormat);
try
{
// 根据像素格式选择合适的WPF像素格式
System.Windows.Media.PixelFormat wpfPixelFormat;
switch (_currentFrame.PixelFormat)
{
case PixelFormat.Format24bppRgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr24;
break;
case PixelFormat.Format32bppArgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgra32;
break;
case PixelFormat.Format32bppRgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr32;
break;
default:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr24;
break;
}
var bitmapSource = BitmapSource.Create(
bitmapData.Width,
bitmapData.Height,
_currentFrame.HorizontalResolution,
_currentFrame.VerticalResolution,
wpfPixelFormat,
null,
bitmapData.Scan0,
bitmapData.Stride * bitmapData.Height,
bitmapData.Stride);
bitmapSource.Freeze();
return bitmapSource;
}
finally
{
_currentFrame.UnlockBits(bitmapData);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"转换帧为BitmapSource失败: {ex.Message}", LogHelper.LogType.Error);
return null;
}
}
}
/// <summary>
/// 视频源新帧事件处理
/// </summary>
private void VideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
lock (_frameLock)
{
// 释放之前的帧
_currentFrame?.Dispose();
// 创建新的位图,避免Clone的问题
var sourceFrame = eventArgs.Frame;
if (sourceFrame != null)
{
try
{
var width = sourceFrame.Width;
var height = sourceFrame.Height;
if (width > 0 && height > 0)
{
// 应用旋转
Bitmap rotatedFrame = ApplyRotation(sourceFrame);
int targetWidth = _resolutionWidth;
int targetHeight = _resolutionHeight;
if (_rotationAngle == 1 || _rotationAngle == 3)
{
targetWidth = _resolutionHeight;
targetHeight = _resolutionWidth;
}
_currentFrame = ResizeImageWithAspectRatio(rotatedFrame, targetWidth, targetHeight);
rotatedFrame?.Dispose();
}
else
{
_currentFrame = null;
}
}
catch (Exception frameEx)
{
LogHelper.WriteLogToFile($"处理源帧失败: {frameEx.Message}", LogHelper.LogType.Error);
_currentFrame = null;
}
}
else
{
_currentFrame = null;
}
}
// 在UI线程中触发事件
_dispatcher.BeginInvoke(new Action(() =>
{
FrameReceived?.Invoke(this, _currentFrame);
}));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理新帧失败: {ex.Message}", LogHelper.LogType.Error);
ErrorOccurred?.Invoke(this, $"处理新帧失败: {ex.Message}");
}
}
/// <summary>
/// 获取摄像头名称列表
/// </summary>
public List<string> GetCameraNames()
{
return AvailableCameras.Select(camera => camera.Name).ToList();
}
/// <summary>
/// 检查是否有可用摄像头
/// </summary>
public bool HasAvailableCameras()
{
if (AvailableCameras.Count == 0)
{
RefreshCameraList();
}
return AvailableCameras.Count > 0;
}
/// <summary>
/// 应用旋转到图像
/// </summary>
private Bitmap ApplyRotation(Bitmap source)
{
if (_rotationAngle == 0)
return new Bitmap(source);
var rotationType = RotateFlipType.RotateNoneFlipNone;
switch (_rotationAngle)
{
case 1: rotationType = RotateFlipType.Rotate90FlipNone; break;
case 2: rotationType = RotateFlipType.Rotate180FlipNone; break;
case 3: rotationType = RotateFlipType.Rotate270FlipNone; break;
}
var rotated = new Bitmap(source);
rotated.RotateFlip(rotationType);
return rotated;
}
/// <summary>
/// 调整图像大小
/// </summary>
private Bitmap ResizeImageWithAspectRatio(Bitmap source, int targetWidth, int targetHeight)
{
if (source.Width == targetWidth && source.Height == targetHeight)
return new Bitmap(source);
double scaleX = (double)targetWidth / source.Width;
double scaleY = (double)targetHeight / source.Height;
double scale = Math.Min(scaleX, scaleY);
// 计算实际尺寸
int actualWidth = (int)(source.Width * scale);
int actualHeight = (int)(source.Height * scale);
var resized = new Bitmap(actualWidth, actualHeight, PixelFormat.Format24bppRgb);
using (var graphics = Graphics.FromImage(resized))
{
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
graphics.DrawImage(source, 0, 0, actualWidth, actualHeight);
}
return resized;
}
/// <summary>
/// 调整图像大小
/// </summary>
private Bitmap ResizeImage(Bitmap source, int width, int height)
{
if (source.Width == width && source.Height == height)
return new Bitmap(source);
var resized = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (var graphics = Graphics.FromImage(resized))
{
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
graphics.DrawImage(source, 0, 0, width, height);
}
return resized;
}
public void Dispose()
{
StopPreview();
lock (_frameLock)
{
_currentFrame?.Dispose();
}
}
}
}
+52 -47
View File
@@ -9,125 +9,130 @@ 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, CultureInfo culture) { throw new NotImplementedException(); }
}
public class InverseBooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.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
+456
View File
@@ -0,0 +1,456 @@
using Ink_Canvas.Helpers;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// Dlass API 客户端,用于与服务端通信
/// </summary>
public class DlassApiClient : IDisposable
{
private const string DEFAULT_BASE_URL = "https://dlass.tech";
private readonly string _appId;
private readonly string _appSecret;
private readonly string _baseUrl;
private HttpClient _httpClient;
private string _accessToken;
private DateTime _tokenExpiresAt;
private string _userToken;
/// <summary>
/// 初始化 Dlass API 客户端
/// </summary>
/// <param name="appId">应用ID</param>
/// <param name="appSecret">应用密钥</param>
/// <param name="baseUrl">API基础URL,如果为空则使用默认URL</param>
/// <param name="userToken">用户Token,如果提供则优先使用用户token而不是App Secret</param>
public DlassApiClient(string appId, string appSecret, string baseUrl = null, string userToken = null)
{
_appId = appId ?? throw new ArgumentNullException(nameof(appId));
_appSecret = appSecret ?? throw new ArgumentNullException(nameof(appSecret));
_userToken = userToken;
_baseUrl = baseUrl ?? DEFAULT_BASE_URL;
_baseUrl = _baseUrl.TrimEnd('/');
if (!_baseUrl.StartsWith("http://") && !_baseUrl.StartsWith("https://"))
{
_baseUrl = "https://" + _baseUrl;
}
_httpClient = new HttpClient
{
BaseAddress = new Uri(_baseUrl),
Timeout = TimeSpan.FromSeconds(30)
};
_httpClient.DefaultRequestHeaders.Add("User-Agent", "InkCanvas/1.0");
}
/// <summary>
/// 获取访问令牌(Access Token
/// </summary>
public async Task<string> GetAccessTokenAsync()
{
if (!string.IsNullOrEmpty(_userToken))
{
return _userToken;
}
if (!string.IsNullOrEmpty(_accessToken) && DateTime.Now < _tokenExpiresAt.AddMinutes(-5))
{
return _accessToken;
}
try
{
var requestData = new
{
app_id = _appId,
app_secret = _appSecret,
grant_type = "client_credentials"
};
var json = JsonConvert.SerializeObject(requestData);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("/oauth/token", content);
var responseContent = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(responseContent);
_accessToken = tokenResponse.AccessToken;
_tokenExpiresAt = DateTime.Now.AddSeconds(tokenResponse.ExpiresIn ?? 3600);
return _accessToken;
}
else
{
throw new Exception($"获取Access Token失败: {response.StatusCode}");
}
}
catch (HttpRequestException httpEx)
{
throw new Exception($"获取Access Token时网络错误: {httpEx.Message}", httpEx);
}
catch (TaskCanceledException timeoutEx)
{
throw new Exception("获取Access Token时请求超时", timeoutEx);
}
catch (Exception ex)
{
throw new Exception($"获取Access Token时出错: {ex.Message}", ex);
}
}
/// <summary>
/// 发送GET请求
/// </summary>
public async Task<T> GetAsync<T>(string endpoint, bool requireAuth = true)
{
try
{
string token = null;
if (requireAuth)
{
token = await GetAccessTokenAsync();
}
var request = new HttpRequestMessage(HttpMethod.Get, endpoint);
if (requireAuth && !string.IsNullOrEmpty(token))
{
if (!string.IsNullOrEmpty(_userToken))
{
request.Headers.Add("X-User-Token", token);
}
else
{
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
}
var response = await _httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
if (string.IsNullOrEmpty(content))
{
return default(T);
}
return JsonConvert.DeserializeObject<T>(content);
}
else
{
throw new Exception($"API请求失败: {response.StatusCode} - {content}");
}
}
catch (HttpRequestException httpEx)
{
throw new Exception($"发送请求时出错: {httpEx.Message}", httpEx);
}
catch (TaskCanceledException timeoutEx)
{
throw new Exception($"请求超时: {endpoint}", timeoutEx);
}
catch (Exception ex)
{
throw new Exception($"发送请求时出错: {ex.Message}", ex);
}
}
/// <summary>
/// 发送POST请求
/// </summary>
public async Task<T> PostAsync<T>(string endpoint, object data = null, bool requireAuth = true)
{
try
{
string token = null;
if (requireAuth)
{
token = await GetAccessTokenAsync();
}
var request = new HttpRequestMessage(HttpMethod.Post, endpoint);
if (requireAuth && !string.IsNullOrEmpty(token))
{
if (!string.IsNullOrEmpty(_userToken))
{
request.Headers.Add("X-User-Token", token);
}
else
{
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
}
if (data != null)
{
var json = JsonConvert.SerializeObject(data);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
}
var response = await _httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
if (string.IsNullOrEmpty(content))
{
return default(T);
}
return JsonConvert.DeserializeObject<T>(content);
}
else
{
throw new Exception($"API请求失败: {response.StatusCode} - {content}");
}
}
catch (HttpRequestException httpEx)
{
throw new Exception($"发送请求时出错: {httpEx.Message}", httpEx);
}
catch (TaskCanceledException timeoutEx)
{
throw new Exception($"请求超时: {endpoint}", timeoutEx);
}
catch (Exception ex)
{
throw new Exception($"发送请求时出错: {ex.Message}", ex);
}
}
/// <summary>
/// 发送PUT请求
/// </summary>
public async Task<T> PutAsync<T>(string endpoint, object data = null, bool requireAuth = true)
{
try
{
string token = null;
if (requireAuth)
{
token = await GetAccessTokenAsync();
}
var request = new HttpRequestMessage(HttpMethod.Put, endpoint);
if (requireAuth && !string.IsNullOrEmpty(token))
{
// 如果是用户token,使用X-User-Token header
if (!string.IsNullOrEmpty(_userToken))
{
request.Headers.Add("X-User-Token", token);
}
else
{
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
}
if (data != null)
{
var json = JsonConvert.SerializeObject(data);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
}
var response = await _httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
if (string.IsNullOrEmpty(content))
{
return default(T);
}
return JsonConvert.DeserializeObject<T>(content);
}
else
{
throw new Exception($"API请求失败: {response.StatusCode} - {content}");
}
}
catch (HttpRequestException httpEx)
{
throw new Exception($"发送请求时出错: {httpEx.Message}", httpEx);
}
catch (TaskCanceledException timeoutEx)
{
throw new Exception($"请求超时: {endpoint}", timeoutEx);
}
catch (Exception ex)
{
throw new Exception($"发送请求时出错: {ex.Message}", ex);
}
}
/// <summary>
/// 发送DELETE请求
/// </summary>
public async Task<bool> DeleteAsync(string endpoint, bool requireAuth = true)
{
try
{
string token = null;
if (requireAuth)
{
token = await GetAccessTokenAsync();
}
var request = new HttpRequestMessage(HttpMethod.Delete, endpoint);
if (requireAuth && !string.IsNullOrEmpty(token))
{
// 如果是用户token,使用X-User-Token header
if (!string.IsNullOrEmpty(_userToken))
{
request.Headers.Add("X-User-Token", token);
}
else
{
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
}
var response = await _httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
return true;
}
else
{
return false;
}
}
catch (HttpRequestException)
{
return false;
}
catch (TaskCanceledException)
{
return false;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 上传笔记文件
/// </summary>
/// <param name="endpoint">上传端点</param>
/// <param name="filePath">文件路径</param>
/// <param name="boardId">白板ID</param>
/// <param name="secretKey">白板密钥</param>
/// <param name="title">笔记标题(可选)</param>
/// <param name="description">笔记描述(可选)</param>
/// <param name="tags">笔记标签(可选)</param>
public async Task<T> UploadNoteAsync<T>(string endpoint, string filePath, string boardId, string secretKey, string title = null, string description = null, string tags = null)
{
try
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"文件不存在: {filePath}");
}
var request = new HttpRequestMessage(HttpMethod.Post, endpoint);
// 设置白板认证头
request.Headers.Add("X-Board-ID", boardId);
request.Headers.Add("X-Secret-Key", secretKey);
// 创建multipart/form-data内容
var content = new MultipartFormDataContent();
// 添加文件
var fileContent = new ByteArrayContent(File.ReadAllBytes(filePath));
var fileName = Path.GetFileName(filePath);
fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Add(fileContent, "file", fileName);
// 添加可选参数
if (!string.IsNullOrEmpty(title))
{
content.Add(new StringContent(title), "title");
}
if (!string.IsNullOrEmpty(description))
{
content.Add(new StringContent(description), "description");
}
if (!string.IsNullOrEmpty(tags))
{
content.Add(new StringContent(tags), "tags");
}
request.Content = content;
var response = await _httpClient.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
if (string.IsNullOrEmpty(responseContent))
{
return default(T);
}
return JsonConvert.DeserializeObject<T>(responseContent);
}
else
{
throw new Exception($"上传文件失败: {response.StatusCode} - {responseContent}");
}
}
catch (HttpRequestException httpEx)
{
throw new Exception($"上传文件时网络错误: {httpEx.Message}", httpEx);
}
catch (TaskCanceledException timeoutEx)
{
throw new Exception($"上传文件超时: {endpoint}", timeoutEx);
}
catch (Exception ex)
{
throw new Exception($"上传文件时出错: {ex.Message}", ex);
}
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
_httpClient?.Dispose();
}
#region
/// <summary>
/// Token响应模型
/// </summary>
private class TokenResponse
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("expires_in")]
public int? ExpiresIn { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
}
#endregion
}
}
+758
View File
@@ -0,0 +1,758 @@
using Ink_Canvas.Helpers;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// Dlass笔记自动上传辅助类
/// </summary>
public class DlassNoteUploader
{
private const string APP_ID = "app_WkjocWqsrVY7T6zQV2CfiA";
private const string APP_SECRET = "o7dx5b5ASGUMcM72PCpmRQYAhSijqaOVHoGyBK0IxbA";
private const int BATCH_SIZE = 10; // 批量上传大小
private const int MAX_RETRY_COUNT = 3; // 最大重试次数
private const string QUEUE_FILE_NAME = "DlassUploadQueue.json";
/// <summary>
/// 上传队列项
/// </summary>
private class UploadQueueItemData
{
[JsonProperty("file_path")]
public string FilePath { get; set; }
[JsonProperty("retry_count")]
public int RetryCount { get; set; }
[JsonProperty("added_time")]
public DateTime AddedTime { get; set; }
}
/// <summary>
/// 上传队列项
/// </summary>
private class UploadQueueItem
{
public string FilePath { get; set; }
public int RetryCount { get; set; }
}
/// <summary>
/// 上传队列
/// </summary>
private static readonly ConcurrentQueue<UploadQueueItem> _uploadQueue = new ConcurrentQueue<UploadQueueItem>();
/// <summary>
/// 队列处理锁,防止并发处理
/// </summary>
private static readonly SemaphoreSlim _queueProcessingLock = new SemaphoreSlim(1, 1);
/// <summary>
/// 队列保存锁,防止并发保存
/// </summary>
private static readonly SemaphoreSlim _queueSaveLock = new SemaphoreSlim(1, 1);
/// <summary>
/// 是否已初始化队列
/// </summary>
private static bool _isQueueInitialized = false;
/// <summary>
/// 获取队列文件路径
/// </summary>
private static string GetQueueFilePath()
{
var configsDir = Path.Combine(App.RootPath, "Configs");
if (!Directory.Exists(configsDir))
{
Directory.CreateDirectory(configsDir);
}
return Path.Combine(configsDir, QUEUE_FILE_NAME);
}
/// <summary>
/// 初始化上传队列
/// </summary>
public static void InitializeQueue()
{
if (_isQueueInitialized)
{
return;
}
try
{
var queueFilePath = GetQueueFilePath();
if (!File.Exists(queueFilePath))
{
_isQueueInitialized = true;
return;
}
var jsonContent = File.ReadAllText(queueFilePath);
if (string.IsNullOrWhiteSpace(jsonContent))
{
_isQueueInitialized = true;
return;
}
var queueData = JsonConvert.DeserializeObject<List<UploadQueueItemData>>(jsonContent);
if (queueData == null || queueData.Count == 0)
{
_isQueueInitialized = true;
return;
}
int restoredCount = 0;
int skippedCount = 0;
foreach (var item in queueData)
{
// 验证文件是否存在
if (!File.Exists(item.FilePath))
{
skippedCount++;
continue;
}
// 验证文件格式和大小
var fileExtension = Path.GetExtension(item.FilePath).ToLower();
if (fileExtension != ".png" && fileExtension != ".icstk" && fileExtension != ".zip")
{
skippedCount++;
continue;
}
try
{
var fileInfo = new FileInfo(item.FilePath);
long maxSize = fileExtension == ".zip" ? 50 * 1024 * 1024 : 10 * 1024 * 1024;
if (fileInfo.Length > maxSize)
{
skippedCount++;
continue;
}
}
catch
{
skippedCount++;
continue;
}
// 恢复队列项
_uploadQueue.Enqueue(new UploadQueueItem
{
FilePath = item.FilePath,
RetryCount = item.RetryCount
});
restoredCount++;
}
_isQueueInitialized = true;
if (restoredCount > 0)
{
LogHelper.WriteLogToFile($"已恢复上传队列:{restoredCount}个文件,跳过{skippedCount}个无效文件", LogHelper.LogType.Event);
// 如果恢复了队列,触发处理
_ = ProcessUploadQueueAsync();
}
else if (skippedCount > 0)
{
LogHelper.WriteLogToFile($"队列恢复完成:跳过{skippedCount}个无效文件", LogHelper.LogType.Event);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"恢复上传队列时出错: {ex.Message}", LogHelper.LogType.Error);
_isQueueInitialized = true; // 即使出错也标记为已初始化,避免重复尝试
}
}
/// <summary>
/// 保存队列到文件
/// </summary>
private static async Task SaveQueueToFileAsync()
{
if (!await _queueSaveLock.WaitAsync(1000)) // 最多等待1秒
{
return; // 如果无法获取锁,跳过保存(避免阻塞)
}
try
{
var queueData = new List<UploadQueueItemData>();
// 将队列转换为可序列化的格式
foreach (var item in _uploadQueue)
{
queueData.Add(new UploadQueueItemData
{
FilePath = item.FilePath,
RetryCount = item.RetryCount,
AddedTime = DateTime.Now
});
}
var queueFilePath = GetQueueFilePath();
// 如果队列为空,清空文件
if (queueData.Count == 0)
{
ClearQueueFile();
return;
}
var jsonContent = JsonConvert.SerializeObject(queueData, Formatting.Indented);
// 使用临时文件写入,然后替换,确保原子性
var tempFilePath = queueFilePath + ".tmp";
File.WriteAllText(tempFilePath, jsonContent);
// 如果原文件存在,先删除
if (File.Exists(queueFilePath))
{
File.Delete(queueFilePath);
}
// 重命名临时文件
File.Move(tempFilePath, queueFilePath);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存上传队列时出错: {ex.Message}", LogHelper.LogType.Error);
}
finally
{
_queueSaveLock.Release();
}
}
/// <summary>
/// 清空队列文件
/// </summary>
private static void ClearQueueFile()
{
try
{
var queueFilePath = GetQueueFilePath();
if (File.Exists(queueFilePath))
{
File.WriteAllText(queueFilePath, "[]");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清空队列文件时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 上传笔记响应模型
/// </summary>
public class UploadNoteResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("note_id")]
public int? NoteId { get; set; }
[JsonProperty("filename")]
public string Filename { get; set; }
[JsonProperty("file_path")]
public string FilePath { get; set; }
[JsonProperty("file_url")]
public string FileUrl { get; set; }
}
/// <summary>
/// 白板信息模型(用于查找白板)
/// </summary>
private class WhiteboardInfo
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("board_id")]
public string BoardId { get; set; }
[JsonProperty("secret_key")]
public string SecretKey { get; set; }
[JsonProperty("class_name")]
public string ClassName { get; set; }
[JsonProperty("class_id")]
public int ClassId { get; set; }
}
/// <summary>
/// 认证响应模型
/// </summary>
private class AuthWithTokenResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("whiteboards")]
public List<WhiteboardInfo> Whiteboards { get; set; }
}
/// <summary>
/// 异步上传笔记文件到Dlass(支持PNG、ICSTK和ZIP格式)
/// </summary>
/// <param name="filePath">文件路径(支持PNG、ICSTK和ZIP</param>
/// <returns>是否成功加入队列(不等待实际上传完成)</returns>
public static async Task<bool> UploadNoteFileAsync(string filePath)
{
try
{
// 检查是否启用自动上传
if (MainWindow.Settings?.Dlass?.IsAutoUploadNotes != true)
{
return false;
}
// 基本验证
if (!File.Exists(filePath))
{
LogHelper.WriteLogToFile($"上传失败:文件不存在 - {filePath}", LogHelper.LogType.Error);
return false;
}
var fileExtension = Path.GetExtension(filePath).ToLower();
if (fileExtension != ".png" && fileExtension != ".icstk" && fileExtension != ".zip")
{
return false;
}
var fileInfo = new FileInfo(filePath);
long maxSize = fileExtension == ".zip" ? 50 * 1024 * 1024 : 10 * 1024 * 1024;
if (fileInfo.Length > maxSize)
{
LogHelper.WriteLogToFile($"上传失败:文件过大({fileInfo.Length / 1024 / 1024}MB),超过{maxSize / 1024 / 1024}MB限制", LogHelper.LogType.Error);
return false;
}
// 获取上传延迟时间(分钟)
var delayMinutes = MainWindow.Settings?.Dlass?.AutoUploadDelayMinutes ?? 0;
// 如果设置了延迟时间,在后台任务中等待后再加入队列
if (delayMinutes > 0)
{
_ = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromMinutes(delayMinutes));
EnqueueFile(filePath);
});
}
else
{
EnqueueFile(filePath);
}
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加入上传队列时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 将文件加入上传队列
/// </summary>
private static void EnqueueFile(string filePath, int retryCount = 0)
{
_uploadQueue.Enqueue(new UploadQueueItem
{
FilePath = filePath,
RetryCount = retryCount
});
// 异步保存队列到文件
_ = Task.Run(async () => await SaveQueueToFileAsync());
// 如果队列达到批量大小,触发批量上传
if (_uploadQueue.Count >= BATCH_SIZE)
{
_ = ProcessUploadQueueAsync();
}
}
/// <summary>
/// 处理上传队列,批量上传文件
/// </summary>
private static async Task ProcessUploadQueueAsync()
{
// 使用信号量防止并发处理
if (!await _queueProcessingLock.WaitAsync(0))
{
return; // 已有处理任务在运行
}
try
{
var filesToUpload = new List<UploadQueueItem>();
// 从队列中取出最多BATCH_SIZE个文件
while (filesToUpload.Count < BATCH_SIZE && _uploadQueue.TryDequeue(out UploadQueueItem item))
{
// 再次检查文件是否存在
if (File.Exists(item.FilePath))
{
filesToUpload.Add(item);
}
}
if (filesToUpload.Count == 0)
{
return;
}
// 获取共享的白板信息(同一批次的所有文件共享认证信息)
WhiteboardInfo sharedWhiteboard = null;
string apiBaseUrl = null;
string userToken = null;
try
{
var selectedClassName = MainWindow.Settings?.Dlass?.SelectedClassName;
if (string.IsNullOrEmpty(selectedClassName))
{
LogHelper.WriteLogToFile("上传失败:未选择班级", LogHelper.LogType.Error);
// 将文件重新加入队列
foreach (var item in filesToUpload)
{
EnqueueFile(item.FilePath, item.RetryCount);
}
return;
}
userToken = MainWindow.Settings?.Dlass?.UserToken;
if (string.IsNullOrEmpty(userToken))
{
LogHelper.WriteLogToFile("上传失败:未设置用户Token", LogHelper.LogType.Error);
// 将文件重新加入队列
foreach (var item in filesToUpload)
{
EnqueueFile(item.FilePath, item.RetryCount);
}
return;
}
apiBaseUrl = MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
// 获取白板信息(只获取一次,所有文件共享)
using (var apiClient = new DlassApiClient(APP_ID, APP_SECRET, apiBaseUrl, userToken))
{
var authData = new
{
app_id = APP_ID,
app_secret = APP_SECRET,
user_token = userToken
};
var authResult = await apiClient.PostAsync<AuthWithTokenResponse>("/api/whiteboard/framework/auth-with-token", authData, requireAuth: false);
if (authResult == null || !authResult.Success || authResult.Whiteboards == null)
{
LogHelper.WriteLogToFile("上传失败:无法获取白板信息", LogHelper.LogType.Error);
// 将文件重新加入队列
foreach (var item in filesToUpload)
{
EnqueueFile(item.FilePath, item.RetryCount);
}
return;
}
sharedWhiteboard = authResult.Whiteboards
.FirstOrDefault(w => !string.IsNullOrEmpty(w.ClassName) && w.ClassName == selectedClassName);
if (sharedWhiteboard == null || string.IsNullOrEmpty(sharedWhiteboard.BoardId) || string.IsNullOrEmpty(sharedWhiteboard.SecretKey))
{
LogHelper.WriteLogToFile($"上传失败:未找到班级'{selectedClassName}'对应的白板", LogHelper.LogType.Error);
// 将文件重新加入队列
foreach (var item in filesToUpload)
{
EnqueueFile(item.FilePath, item.RetryCount);
}
return;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"批量上传获取白板信息时出错: {ex.Message}", LogHelper.LogType.Error);
// 将文件重新加入队列
foreach (var item in filesToUpload)
{
EnqueueFile(item.FilePath, item.RetryCount);
}
return;
}
// 并发上传所有文件(共享白板信息),并处理失败重试
var uploadTasks = filesToUpload.Select(async item =>
{
try
{
var success = await UploadFileInternalAsync(item.FilePath, sharedWhiteboard, apiBaseUrl, userToken);
if (!success)
{
// 检查是否是可重试的错误
if (IsRetryableError(item.FilePath))
{
// 检查重试次数
if (item.RetryCount < MAX_RETRY_COUNT)
{
LogHelper.WriteLogToFile($"上传失败,将重试 ({item.RetryCount + 1}/{MAX_RETRY_COUNT}): {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Event);
EnqueueFile(item.FilePath, item.RetryCount + 1);
}
else
{
LogHelper.WriteLogToFile($"上传失败,已达到最大重试次数: {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Error);
}
}
}
return success;
}
catch (Exception ex)
{
// 检查是否是可重试的错误(超时、网络错误等)
var errorMessage = ex.Message.ToLower();
bool isRetryable = errorMessage.Contains("超时") ||
errorMessage.Contains("timeout") ||
errorMessage.Contains("网络错误") ||
errorMessage.Contains("network");
if (isRetryable && IsRetryableError(item.FilePath))
{
// 检查重试次数
if (item.RetryCount < MAX_RETRY_COUNT)
{
LogHelper.WriteLogToFile($"上传失败({ex.Message}),将重试 ({item.RetryCount + 1}/{MAX_RETRY_COUNT}): {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Event);
EnqueueFile(item.FilePath, item.RetryCount + 1);
}
else
{
LogHelper.WriteLogToFile($"上传失败,已达到最大重试次数: {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Error);
}
}
return false;
}
});
await Task.WhenAll(uploadTasks);
// 上传完成后保存队列状态
await SaveQueueToFileAsync();
// 如果队列达到批量大小,继续处理
if (_uploadQueue.Count >= BATCH_SIZE)
{
_ = ProcessUploadQueueAsync();
}
}
finally
{
_queueProcessingLock.Release();
}
}
/// <summary>
/// 内部上传方法,执行实际上传操作
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="whiteboard">白板信息(如果为null则重新获取)</param>
/// <param name="apiBaseUrl">API基础URL(如果为null则从设置获取)</param>
/// <param name="userToken">用户Token(如果为null则从设置获取)</param>
private static async Task<bool> UploadFileInternalAsync(string filePath, WhiteboardInfo whiteboard = null, string apiBaseUrl = null, string userToken = null)
{
try
{
// 再次检查文件是否存在(可能在队列等待时被删除)
if (!File.Exists(filePath))
{
return false;
}
// 检查文件扩展名
var fileExtension = Path.GetExtension(filePath).ToLower();
if (fileExtension != ".png" && fileExtension != ".icstk" && fileExtension != ".zip")
{
return false;
}
// 检查文件大小(最大10MB,ZIP文件可能更大,允许50MB)
var fileInfo = new FileInfo(filePath);
long maxSize = fileExtension == ".zip" ? 50 * 1024 * 1024 : 10 * 1024 * 1024;
if (fileInfo.Length > maxSize)
{
LogHelper.WriteLogToFile($"上传失败:文件过大({fileInfo.Length / 1024 / 1024}MB),超过{maxSize / 1024 / 1024}MB限制", LogHelper.LogType.Error);
return false;
}
// 如果白板信息未提供,则重新获取
if (whiteboard == null)
{
var selectedClassName = MainWindow.Settings?.Dlass?.SelectedClassName;
if (string.IsNullOrEmpty(selectedClassName))
{
LogHelper.WriteLogToFile("上传失败:未选择班级", LogHelper.LogType.Error);
return false;
}
userToken = userToken ?? MainWindow.Settings?.Dlass?.UserToken;
if (string.IsNullOrEmpty(userToken))
{
LogHelper.WriteLogToFile("上传失败:未设置用户Token", LogHelper.LogType.Error);
return false;
}
apiBaseUrl = apiBaseUrl ?? MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
// 创建API客户端并获取白板信息
using (var apiClient = new DlassApiClient(APP_ID, APP_SECRET, apiBaseUrl, userToken))
{
var authData = new
{
app_id = APP_ID,
app_secret = APP_SECRET,
user_token = userToken
};
var authResult = await apiClient.PostAsync<AuthWithTokenResponse>("/api/whiteboard/framework/auth-with-token", authData, requireAuth: false);
if (authResult == null || !authResult.Success || authResult.Whiteboards == null)
{
LogHelper.WriteLogToFile("上传失败:无法获取白板信息", LogHelper.LogType.Error);
return false;
}
// 查找匹配班级的白板
whiteboard = authResult.Whiteboards
.FirstOrDefault(w => !string.IsNullOrEmpty(w.ClassName) && w.ClassName == selectedClassName);
if (whiteboard == null || string.IsNullOrEmpty(whiteboard.BoardId) || string.IsNullOrEmpty(whiteboard.SecretKey))
{
LogHelper.WriteLogToFile($"上传失败:未找到班级'{selectedClassName}'对应的白板", LogHelper.LogType.Error);
return false;
}
}
}
// 获取API基础URL和用户Token(如果未提供)
apiBaseUrl = apiBaseUrl ?? MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
userToken = userToken ?? MainWindow.Settings?.Dlass?.UserToken;
// 准备上传参数
var fileName = Path.GetFileNameWithoutExtension(filePath);
var title = fileName;
string fileType;
string tags;
if (fileExtension == ".zip")
{
fileType = "多页面墨迹压缩包";
tags = "自动上传,多页面,zip,压缩包";
}
else if (fileExtension == ".icstk")
{
fileType = "墨迹文件";
tags = "自动上传,墨迹,icstk";
}
else
{
fileType = "笔记";
tags = "自动上传,笔记,png";
}
var description = $"自动上传的{fileType} - {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
// 创建API客户端并上传文件
using (var apiClient = new DlassApiClient(APP_ID, APP_SECRET, apiBaseUrl, userToken))
{
var uploadResult = await apiClient.UploadNoteAsync<UploadNoteResponse>(
"/api/whiteboard/upload_note",
filePath,
whiteboard.BoardId,
whiteboard.SecretKey,
title,
description,
tags);
if (uploadResult != null && uploadResult.Success)
{
LogHelper.WriteLogToFile($"笔记上传成功:{fileName} -> {uploadResult.FileUrl}", LogHelper.LogType.Event);
return true;
}
else
{
LogHelper.WriteLogToFile($"上传失败:服务器响应失败 - {uploadResult?.Message ?? ""}", LogHelper.LogType.Error);
return false;
}
}
}
catch (Exception ex)
{
// 记录错误信息,抛出异常以便调用方判断是否可重试
LogHelper.WriteLogToFile($"上传笔记时出错: {ex.Message}", LogHelper.LogType.Error);
throw;
}
}
/// <summary>
/// 判断错误是否可重试(超时、网络错误等)
/// </summary>
private static bool IsRetryableError(string filePath)
{
// 检查文件是否存在
if (!File.Exists(filePath))
{
return false; // 文件不存在,不可重试
}
// 检查文件扩展名
var fileExtension = Path.GetExtension(filePath).ToLower();
if (fileExtension != ".png" && fileExtension != ".icstk" && fileExtension != ".zip")
{
return false; // 文件格式错误,不可重试
}
// 检查文件大小
try
{
var fileInfo = new FileInfo(filePath);
long maxSize = fileExtension == ".zip" ? 50 * 1024 * 1024 : 10 * 1024 * 1024;
if (fileInfo.Length > maxSize)
{
return false; // 文件过大,不可重试
}
}
catch
{
return false; // 无法读取文件信息,不可重试
}
// 其他错误(超时、网络错误等)可以重试
return true;
}
}
}
+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)]
@@ -0,0 +1,589 @@
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading;
using System.Windows;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 文件关联管理器,用于注册和处理.icstk文件的关联
/// </summary>
public static class FileAssociationManager
{
private const string FileExtension = ".icstk";
private const string FileTypeName = "InkCanvasStrokesFile";
private const string AppName = "Ink Canvas";
private const string AppDescription = "Ink Canvas Strokes File";
// IPC相关常量
private const string IpcMutexName = "InkCanvasFileAssociationIpc";
private const string IpcEventName = "InkCanvasFileAssociationEvent";
private const string IpcFilePrefix = "InkCanvasFileAssociation_";
private const string IpcBoardModePrefix = "InkCanvasBoardMode_";
private const string IpcShowModePrefix = "InkCanvasShowMode_";
private const int IpcTimeout = 5000; // 5秒超时
/// <summary>
/// 注册.icstk文件关联
/// </summary>
public static bool RegisterFileAssociation()
{
try
{
string exePath = Process.GetCurrentProcess().MainModule.FileName;
// 注册文件类型
using (RegistryKey fileTypeKey = Registry.ClassesRoot.CreateSubKey(FileTypeName))
{
fileTypeKey.SetValue("", AppDescription);
fileTypeKey.SetValue("FriendlyTypeName", AppDescription);
// 设置默认图标
using (RegistryKey defaultIconKey = fileTypeKey.CreateSubKey("DefaultIcon"))
{
defaultIconKey.SetValue("", $"\"{exePath}\",0");
}
// 设置打开命令
using (RegistryKey shellKey = fileTypeKey.CreateSubKey("shell"))
using (RegistryKey openKey = shellKey.CreateSubKey("open"))
using (RegistryKey commandKey = openKey.CreateSubKey("command"))
{
commandKey.SetValue("", $"\"{exePath}\" \"%1\"");
}
}
// 注册文件扩展名
using (RegistryKey extensionKey = Registry.ClassesRoot.CreateSubKey(FileExtension))
{
extensionKey.SetValue("", FileTypeName);
}
// 刷新系统文件关联缓存
RefreshSystemFileAssociations();
LogHelper.WriteLogToFile($"成功注册{FileExtension}文件关联", LogHelper.LogType.Event);
return true;
}
catch (SecurityException ex)
{
LogHelper.WriteLogToFile($"注册文件关联时权限不足: {ex.Message}", LogHelper.LogType.Error);
return false;
}
catch (UnauthorizedAccessException ex)
{
LogHelper.WriteLogToFile($"注册文件关联时访问被拒绝: {ex.Message}", LogHelper.LogType.Error);
return false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注册文件关联时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 注销.icstk文件关联
/// </summary>
public static bool UnregisterFileAssociation()
{
try
{
// 删除文件扩展名关联
Registry.ClassesRoot.DeleteSubKeyTree(FileExtension, false);
// 删除文件类型定义
Registry.ClassesRoot.DeleteSubKeyTree(FileTypeName, false);
// 刷新系统文件关联缓存
RefreshSystemFileAssociations();
LogHelper.WriteLogToFile($"成功注销{FileExtension}文件关联", LogHelper.LogType.Event);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注销文件关联时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 检查文件关联是否已注册
/// </summary>
public static bool IsFileAssociationRegistered()
{
try
{
using (RegistryKey extensionKey = Registry.ClassesRoot.OpenSubKey(FileExtension))
{
if (extensionKey == null) return false;
string fileType = extensionKey.GetValue("") as string;
if (string.IsNullOrEmpty(fileType)) return false;
using (RegistryKey fileTypeKey = Registry.ClassesRoot.OpenSubKey(fileType))
{
if (fileTypeKey == null) return false;
using (RegistryKey shellKey = fileTypeKey.OpenSubKey("shell\\open\\command"))
{
if (shellKey == null) return false;
string command = shellKey.GetValue("") as string;
if (string.IsNullOrEmpty(command)) return false;
// 检查命令是否指向当前应用程序
string currentExePath = Process.GetCurrentProcess().MainModule.FileName;
return command.Contains(currentExePath);
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检查文件关联状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
return false;
}
/// <summary>
/// 显示文件关联状态
/// </summary>
public static void ShowFileAssociationStatus()
{
bool isRegistered = IsFileAssociationRegistered();
LogHelper.WriteLogToFile($"{FileExtension}文件关联状态: {(isRegistered ? "" : "")}", LogHelper.LogType.Event);
}
/// <summary>
/// 刷新系统文件关联缓存
/// </summary>
private static void RefreshSystemFileAssociations()
{
try
{
// 通知系统文件关联已更改
SHChangeNotify(0x08000000, 0, IntPtr.Zero, IntPtr.Zero);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"刷新文件关联缓存时出错: {ex.Message}", LogHelper.LogType.Warning);
}
}
/// <summary>
/// 处理命令行参数中的文件路径
/// </summary>
/// <param name="args">命令行参数</param>
/// <returns>找到的.icstk文件路径,如果没有找到则返回null</returns>
public static string GetIcstkFileFromArgs(string[] args)
{
if (args == null || args.Length == 0) return null;
foreach (string arg in args)
{
if (string.IsNullOrEmpty(arg)) continue;
// 检查是否为.icstk文件
if (Path.GetExtension(arg).Equals(FileExtension, StringComparison.OrdinalIgnoreCase))
{
// 检查文件是否存在
if (File.Exists(arg))
{
LogHelper.WriteLogToFile($"从命令行参数中找到.icstk文件: {arg}", LogHelper.LogType.Event);
return arg;
}
else
{
LogHelper.WriteLogToFile($"命令行参数中的.icstk文件不存在: {arg}", LogHelper.LogType.Warning);
}
}
}
return null;
}
/// <summary>
/// 尝试通过IPC将文件路径发送给已运行的实例
/// </summary>
/// <param name="filePath">要打开的文件路径</param>
/// <returns>是否成功发送</returns>
public static bool TrySendFileToExistingInstance(string filePath)
{
try
{
LogHelper.WriteLogToFile($"尝试通过IPC发送文件路径给已运行实例: {filePath}", LogHelper.LogType.Event);
// 创建IPC文件
string tempDir = Path.GetTempPath();
string ipcFileName = IpcFilePrefix + Guid.NewGuid().ToString("N") + ".tmp";
string ipcFilePath = Path.Combine(tempDir, ipcFileName);
// 写入文件路径到IPC文件
File.WriteAllText(ipcFilePath, filePath, Encoding.UTF8);
// 创建事件通知已运行实例
using (EventWaitHandle ipcEvent = new EventWaitHandle(false, EventResetMode.ManualReset, IpcEventName))
{
ipcEvent.Set();
}
// 等待一段时间让已运行实例处理文件
Thread.Sleep(1000);
// 清理IPC文件
try
{
if (File.Exists(ipcFilePath))
{
File.Delete(ipcFilePath);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
LogHelper.WriteLogToFile("IPC文件路径发送完成", LogHelper.LogType.Event);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"通过IPC发送文件路径失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 尝试通过IPC将白板模式命令发送给已运行的实例
/// </summary>
/// <returns>是否成功发送</returns>
public static bool TrySendBoardModeCommandToExistingInstance()
{
try
{
LogHelper.WriteLogToFile("尝试通过IPC发送白板模式命令给已运行实例", LogHelper.LogType.Event);
// 创建IPC文件
string tempDir = Path.GetTempPath();
string ipcFileName = IpcBoardModePrefix + Guid.NewGuid().ToString("N") + ".tmp";
string ipcFilePath = Path.Combine(tempDir, ipcFileName);
// 写入白板模式命令到IPC文件
File.WriteAllText(ipcFilePath, "BOARD_MODE", Encoding.UTF8);
// 创建事件通知已运行实例
using (EventWaitHandle ipcEvent = new EventWaitHandle(false, EventResetMode.ManualReset, IpcEventName))
{
ipcEvent.Set();
}
// 等待一段时间让已运行实例处理命令
Thread.Sleep(1000);
// 清理IPC文件
try
{
if (File.Exists(ipcFilePath))
{
File.Delete(ipcFilePath);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
LogHelper.WriteLogToFile("IPC白板模式命令发送完成", LogHelper.LogType.Event);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"通过IPC发送白板模式命令失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 尝试通过IPC将展开浮动栏命令发送给已运行的实例
/// </summary>
/// <returns>是否成功发送</returns>
public static bool TrySendShowModeCommandToExistingInstance()
{
try
{
LogHelper.WriteLogToFile("尝试通过IPC发送展开浮动栏命令给已运行实例", LogHelper.LogType.Event);
// 创建IPC文件
string tempDir = Path.GetTempPath();
string ipcFileName = IpcShowModePrefix + Guid.NewGuid().ToString("N") + ".tmp";
string ipcFilePath = Path.Combine(tempDir, ipcFileName);
// 写入展开浮动栏命令到IPC文件
File.WriteAllText(ipcFilePath, "SHOW_MODE", Encoding.UTF8);
// 创建事件通知已运行实例
using (EventWaitHandle ipcEvent = new EventWaitHandle(false, EventResetMode.ManualReset, IpcEventName))
{
ipcEvent.Set();
}
// 等待一段时间让已运行实例处理命令
Thread.Sleep(1000);
// 清理IPC文件
try
{
if (File.Exists(ipcFilePath))
{
File.Delete(ipcFilePath);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
LogHelper.WriteLogToFile("IPC展开浮动栏命令发送完成", LogHelper.LogType.Event);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"通过IPC发送展开浮动栏命令失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 启动IPC监听器,等待其他实例发送文件路径
/// </summary>
public static void StartIpcListener()
{
try
{
Thread ipcThread = new Thread(() =>
{
try
{
LogHelper.WriteLogToFile("启动IPC监听器", LogHelper.LogType.Event);
using (EventWaitHandle ipcEvent = new EventWaitHandle(false, EventResetMode.ManualReset, IpcEventName))
{
while (true)
{
// 等待IPC事件
if (ipcEvent.WaitOne(IpcTimeout))
{
// 处理IPC文件
ProcessIpcFiles();
// 重置事件
ipcEvent.Reset();
}
// 检查应用是否还在运行
if (Application.Current == null || Application.Current.Dispatcher == null)
{
break;
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"IPC监听器出错: {ex.Message}", LogHelper.LogType.Error);
}
});
ipcThread.IsBackground = true;
ipcThread.Start();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动IPC监听器失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 处理IPC文件
/// </summary>
private static void ProcessIpcFiles()
{
try
{
string tempDir = Path.GetTempPath();
// 处理文件路径IPC文件
string[] ipcFiles = Directory.GetFiles(tempDir, IpcFilePrefix + "*.tmp");
foreach (string ipcFile in ipcFiles)
{
try
{
// 读取文件路径
string filePath = File.ReadAllText(ipcFile, Encoding.UTF8);
if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
{
LogHelper.WriteLogToFile($"IPC接收到文件路径: {filePath}", LogHelper.LogType.Event);
// 在UI线程中处理文件打开
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 获取主窗口并打开文件
if (Application.Current.MainWindow is MainWindow mainWindow)
{
mainWindow.OpenSingleStrokeFile(filePath);
mainWindow.ShowNotification($"已加载墨迹文件: {Path.GetFileName(filePath)}");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"IPC处理文件打开失败: {ex.Message}", LogHelper.LogType.Error);
}
}));
}
// 删除IPC文件
File.Delete(ipcFile);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
// 尝试删除损坏的IPC文件
try
{
if (File.Exists(ipcFile))
{
File.Delete(ipcFile);
}
}
catch { }
}
}
// 处理白板模式命令IPC文件
string[] boardModeFiles = Directory.GetFiles(tempDir, IpcBoardModePrefix + "*.tmp");
foreach (string ipcFile in boardModeFiles)
{
try
{
// 读取命令内容
string command = File.ReadAllText(ipcFile, Encoding.UTF8);
if (command == "BOARD_MODE")
{
LogHelper.WriteLogToFile("IPC接收到白板模式命令", LogHelper.LogType.Event);
// 在UI线程中处理白板模式切换
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 获取主窗口并切换到白板模式
if (Application.Current.MainWindow is MainWindow mainWindow)
{
mainWindow.SwitchToBoardMode();
mainWindow.ShowNotification("已切换到白板模式");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"IPC处理白板模式切换失败: {ex.Message}", LogHelper.LogType.Error);
}
}));
}
// 删除IPC文件
File.Delete(ipcFile);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理白板模式IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
// 尝试删除损坏的IPC文件
try
{
if (File.Exists(ipcFile))
{
File.Delete(ipcFile);
}
}
catch { }
}
}
// 处理展开浮动栏命令IPC文件
string[] showModeFiles = Directory.GetFiles(tempDir, IpcShowModePrefix + "*.tmp");
foreach (string ipcFile in showModeFiles)
{
try
{
// 读取命令内容
string command = File.ReadAllText(ipcFile, Encoding.UTF8);
if (command == "SHOW_MODE")
{
LogHelper.WriteLogToFile("IPC接收到展开浮动栏命令", LogHelper.LogType.Event);
// 在UI线程中处理展开浮动栏
Application.Current.Dispatcher.BeginInvoke(new Action(async () =>
{
try
{
// 获取主窗口并展开浮动栏
if (Application.Current.MainWindow is MainWindow mainWindow)
{
// 如果当前处于收纳模式,则展开浮动栏
if (mainWindow.isFloatingBarFolded)
{
await mainWindow.UnFoldFloatingBar(new object());
}
mainWindow.ShowNotification("已退出收纳模式并恢复浮动栏");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"IPC处理展开浮动栏失败: {ex.Message}", LogHelper.LogType.Error);
}
}));
}
// 删除IPC文件
File.Delete(ipcFile);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理展开浮动栏IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
// 尝试删除损坏的IPC文件
try
{
if (File.Exists(ipcFile))
{
File.Delete(ipcFile);
}
}
catch { }
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理IPC文件时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
[DllImport("shell32.dll")]
private static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,398 @@
using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ink_Canvas
{
/// <summary>
/// 悬浮窗拦截管理器
/// </summary>
public class FloatingWindowInterceptorManager : IDisposable
{
#region
private FloatingWindowInterceptor _interceptor;
private bool _isInitialized;
private bool _disposed;
private FloatingWindowInterceptorSettings _settings;
#endregion
#region
public event EventHandler<FloatingWindowInterceptor.WindowInterceptedEventArgs> WindowIntercepted;
public event EventHandler<FloatingWindowInterceptor.WindowRestoredEventArgs> WindowRestored;
#endregion
#region
public bool IsEnabled => _interceptor != null && _settings != null && _settings.IsEnabled;
public bool IsRunning => _interceptor != null && _interceptor.IsRunning;
#endregion
#region
/// <summary>
/// 初始化拦截器
/// </summary>
public void Initialize(FloatingWindowInterceptorSettings settings)
{
if (_isInitialized) return;
try
{
_settings = settings ?? new FloatingWindowInterceptorSettings();
_interceptor = new FloatingWindowInterceptor();
// 订阅事件
_interceptor.WindowIntercepted += OnWindowIntercepted;
_interceptor.WindowRestored += OnWindowRestored;
// 应用配置
ApplySettings();
_isInitialized = true;
// 如果设置了自动启动,则启动拦截器
if (_settings.AutoStart && _settings.IsEnabled)
{
Start();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 启动拦截器
/// </summary>
public void Start()
{
if (!_isInitialized || _settings == null) return;
if (_interceptor == null) return;
try
{
_interceptor.Start(_settings.ScanIntervalMs);
LogHelper.WriteLogToFile("悬浮窗拦截器已启动", LogHelper.LogType.Event);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 停止拦截器
/// </summary>
public void Stop()
{
if (_interceptor == null) return;
try
{
_interceptor.Stop();
LogHelper.WriteLogToFile("悬浮窗拦截器已停止", LogHelper.LogType.Event);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"停止悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 设置拦截规则
/// </summary>
public void SetInterceptRule(FloatingWindowInterceptor.InterceptType type, bool enabled)
{
if (_interceptor == null || _settings == null) return;
try
{
_interceptor.SetInterceptRule(type, enabled);
// 更新设置
var ruleName = type.ToString();
if (_settings.InterceptRules.ContainsKey(ruleName))
{
_settings.InterceptRules[ruleName] = enabled;
}
// 获取规则信息以处理父子关系
var rule = _interceptor.GetInterceptRule(type);
if (rule != null)
{
// 如果是父规则,更新所有子规则的设置
if (rule.ChildTypes.Count > 0)
{
foreach (var childType in rule.ChildTypes)
{
var childRuleName = childType.ToString();
if (_settings.InterceptRules.ContainsKey(childRuleName))
{
_settings.InterceptRules[childRuleName] = enabled;
}
}
}
// 如果是子规则,更新父规则的设置
else if (rule.ParentType.HasValue)
{
var parentRule = _interceptor.GetInterceptRule(rule.ParentType.Value);
if (parentRule != null)
{
var parentRuleName = rule.ParentType.Value.ToString();
if (_settings.InterceptRules.ContainsKey(parentRuleName))
{
_settings.InterceptRules[parentRuleName] = parentRule.IsEnabled;
}
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"设置拦截规则失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 获取拦截规则
/// </summary>
public FloatingWindowInterceptor.InterceptRule GetInterceptRule(FloatingWindowInterceptor.InterceptType type)
{
return _interceptor?.GetInterceptRule(type);
}
/// <summary>
/// 获取所有拦截规则
/// </summary>
public Dictionary<FloatingWindowInterceptor.InterceptType, FloatingWindowInterceptor.InterceptRule> GetAllRules()
{
return _interceptor?.GetAllRules() ?? new Dictionary<FloatingWindowInterceptor.InterceptType, FloatingWindowInterceptor.InterceptRule>();
}
/// <summary>
/// 手动扫描一次
/// </summary>
public void ScanOnce()
{
if (_interceptor == null) return;
try
{
_interceptor.ScanOnce();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"手动扫描失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 恢复所有被拦截的窗口
/// </summary>
public void RestoreAllWindows()
{
if (_interceptor == null) return;
try
{
_interceptor.RestoreAllWindows();
LogHelper.WriteLogToFile("已恢复所有被拦截的窗口", LogHelper.LogType.Event);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"恢复窗口失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 应用设置
/// </summary>
public void ApplySettings()
{
if (_interceptor == null || _settings == null) return;
try
{
// 应用拦截规则设置
foreach (var kvp in _settings.InterceptRules)
{
if (Enum.TryParse<FloatingWindowInterceptor.InterceptType>(kvp.Key, out var type))
{
_interceptor.SetInterceptRule(type, kvp.Value);
}
}
// 如果启用了拦截器,则启动
if (_settings.IsEnabled && !IsRunning)
{
Start();
}
// 如果禁用了拦截器,则停止
else if (!_settings.IsEnabled && IsRunning)
{
Stop();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用设置失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新扫描间隔
/// </summary>
public void UpdateScanInterval(int intervalMs)
{
if (_interceptor == null || _settings == null) return;
try
{
_settings.ScanIntervalMs = intervalMs;
// 如果正在运行,重启以应用新间隔
if (IsRunning)
{
Stop();
Start();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新扫描间隔失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 获取拦截统计信息
/// </summary>
public InterceptStatistics GetStatistics()
{
if (_interceptor == null || _settings == null) return new InterceptStatistics();
try
{
var rules = GetAllRules();
var enabledRules = rules.Count(r => r.Value.IsEnabled);
var totalRules = rules.Count;
return new InterceptStatistics
{
TotalRules = totalRules,
EnabledRules = enabledRules,
IsRunning = IsRunning,
ScanIntervalMs = _settings.ScanIntervalMs
};
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取统计信息失败: {ex.Message}", LogHelper.LogType.Error);
return new InterceptStatistics();
}
}
#endregion
#region
private void OnWindowIntercepted(object sender, FloatingWindowInterceptor.WindowInterceptedEventArgs e)
{
try
{
// 记录日志
LogHelper.WriteLogToFile($"拦截窗口: {e.WindowTitle} ({e.InterceptType})", LogHelper.LogType.Event);
// 显示通知(如果启用)
if (_settings != null && _settings.ShowNotifications)
{
ShowNotification($"已拦截悬浮窗: {e.Rule.Description}");
}
// 触发事件
WindowIntercepted?.Invoke(this, e);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理窗口拦截事件失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void OnWindowRestored(object sender, FloatingWindowInterceptor.WindowRestoredEventArgs e)
{
try
{
// 记录日志
LogHelper.WriteLogToFile($"恢复窗口: {e.InterceptType}", LogHelper.LogType.Event);
// 触发事件
WindowRestored?.Invoke(this, e);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理窗口恢复事件失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void ShowNotification(string message)
{
try
{
// 这里可以集成系统通知或自定义通知
// 暂时使用调试输出
System.Diagnostics.Debug.WriteLine($"通知: {message}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"显示通知失败: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region
public class InterceptStatistics
{
public int TotalRules { get; set; }
public int EnabledRules { get; set; }
public bool IsRunning { get; set; }
public int ScanIntervalMs { get; set; }
}
#endregion
#region IDisposable
public void Dispose()
{
if (_disposed) return;
try
{
Stop();
_interceptor?.Dispose();
_interceptor = null;
_isInitialized = false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"释放悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error);
}
finally
{
_disposed = true;
}
}
#endregion
}
}
+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...... 你信么
}
//将修改后的结构体拷贝回去
File diff suppressed because it is too large Load Diff
@@ -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();
}
}
}
+897
View File
@@ -0,0 +1,897 @@
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.Media.Effects;
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
{
// 确保主窗口的InkCanvas保持Ink编辑模式,防止墨迹渐隐时切换到鼠标模式
if (_mainWindow.inkCanvas.EditingMode != InkCanvasEditingMode.Ink)
{
_mainWindow.inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
// 记录墨迹的起点和终点
_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);
}
// 为高亮笔添加轻微的模糊效果,使渐隐更加自然
path.Effect = new BlurEffect
{
Radius = 0.5, // 轻微的模糊效果
KernelType = KernelType.Gaussian
};
}
// 不设置任何变换,保持墨迹原有粗细
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 StartUnifiedFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity, int duration)
{
try
{
// 创建透明度动画,模拟擦除时的效果
var fadeAnimation = new DoubleAnimation
{
From = currentOpacity,
To = 0.0,
Duration = TimeSpan.FromMilliseconds(duration),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
};
// 如果是高亮笔,添加轻微的缩放效果,使渐隐更加自然
if (stroke.DrawingAttributes.IsHighlighter)
{
// 创建轻微的缩放动画,模拟墨迹"蒸发"的效果
var scaleAnimation = new DoubleAnimation
{
From = 1.0,
To = 0.95, // 轻微缩小,增加自然感
Duration = TimeSpan.FromMilliseconds(duration),
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseIn }
};
// 创建缩放变换
var scaleTransform = new ScaleTransform();
visual.RenderTransform = scaleTransform;
visual.RenderTransformOrigin = new Point(0.5, 0.5);
// 应用缩放动画
scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation);
scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation);
}
// 添加动画完成事件
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 void StartHighlighterFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity)
{
try
{
// 高亮笔使用统一的渐隐动画,与擦除效果一致
StartUnifiedFadeAnimation(visual, stroke, currentOpacity, (int)(AnimationDuration * 1.2));
}
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;
+164
View File
@@ -0,0 +1,164 @@
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()
{
// 保存用户设置的异步处理偏好
bool userAsyncPreference = UseAsyncProcessing;
switch (Quality)
{
case SmoothingQuality.Performance:
SmoothingStrength = 0.15;
ResampleInterval = 5.0;
InterpolationSteps = 4;
UseAdaptiveInterpolation = false;
CurveTension = 0.15;
MaxConcurrentTasks = Math.Max(1, Environment.ProcessorCount / 2);
UseHardwareAcceleration = true;
UseAsyncProcessing = userAsyncPreference;
break;
case SmoothingQuality.Balanced:
SmoothingStrength = 0.3;
ResampleInterval = 3.0;
InterpolationSteps = 8;
UseAdaptiveInterpolation = true;
CurveTension = 0.25;
MaxConcurrentTasks = Environment.ProcessorCount;
UseHardwareAcceleration = true;
UseAsyncProcessing = userAsyncPreference;
break;
case SmoothingQuality.Quality:
SmoothingStrength = 0.5;
ResampleInterval = 2.0;
InterpolationSteps = 15;
UseAdaptiveInterpolation = true;
CurveTension = 0.35;
MaxConcurrentTasks = Environment.ProcessorCount;
UseHardwareAcceleration = true;
UseAsyncProcessing = userAsyncPreference;
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);
}
+70 -2
View File
@@ -8,10 +8,13 @@ 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)
@@ -28,14 +31,40 @@ namespace Ink_Canvas.Helpers
public static void WriteLogToFile(string str, LogType logType = LogType.Info)
{
// 检查日志是否启用
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);
}
var threadId = Thread.CurrentThread.ManagedThreadId;
var callingMethod = new StackTrace(2, true).GetFrame(0);
string callerInfo = "<unknown>";
@@ -57,6 +86,45 @@ namespace Ink_Canvas.Helpers
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)
{
WriteLogToFile($"[Warning] {v}", LogType.Warning);
+36 -5
View File
@@ -25,14 +25,18 @@ namespace Ink_Canvas.Helpers
}
/// <summary>
/// 用于显示笔迹的类
/// 用于显示笔迹的类
/// </summary>
public class StrokeVisual : DrawingVisual
{
private bool _needsRedraw = true;
private int _lastPointCount = 0;
private const int REDRAW_THRESHOLD = 3;
/// <summary>
/// 创建显示笔迹的类
/// </summary>
public StrokeVisual() : this(new DrawingAttributes()
public StrokeVisual() : this(new DrawingAttributes
{
Color = Colors.Red,
//FitToCurve = true,
@@ -49,15 +53,20 @@ namespace Ink_Canvas.Helpers
public StrokeVisual(DrawingAttributes drawingAttributes)
{
_drawingAttributes = drawingAttributes;
// 启用硬件加速
RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.HighQuality);
RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);
RenderOptions.SetCachingHint(this, CachingHint.Cache);
}
/// <summary>
/// 设置或获取显示的笔迹
/// 设置或获取显示的笔迹
/// </summary>
public Stroke Stroke { set; get; }
/// <summary>
/// 在笔迹中添加点
/// 在笔迹中添加点
/// </summary>
/// <param name="point"></param>
public void Add(StylusPoint point)
@@ -66,28 +75,50 @@ namespace Ink_Canvas.Helpers
{
var collection = new StylusPointCollection { point };
Stroke = new Stroke(collection) { DrawingAttributes = _drawingAttributes };
_lastPointCount = 1;
}
else
{
Stroke.StylusPoints.Add(point);
_lastPointCount++;
}
// 标记需要重绘
_needsRedraw = true;
}
/// <summary>
/// 重新画出笔迹
/// 重新画出笔迹
/// </summary>
public void Redraw()
{
if (!_needsRedraw || Stroke == null) return;
if (_lastPointCount % REDRAW_THRESHOLD != 0 && _lastPointCount > REDRAW_THRESHOLD)
{
return;
}
try
{
using (var dc = RenderOpen())
{
Stroke.Draw(dc);
}
_needsRedraw = false;
}
catch { }
}
/// <summary>
/// 强制重绘
/// </summary>
public void ForceRedraw()
{
_needsRedraw = true;
Redraw();
}
private readonly DrawingAttributes _drawingAttributes;
public static implicit operator Stroke(StrokeVisual v)
+672
View File
@@ -0,0 +1,672 @@
using Microsoft.Office.Interop.PowerPoint;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Ink;
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;
// 添加快速切换保护机制
private DateTime _lastSwitchTime = DateTime.MinValue;
private int _lastSwitchSlideIndex = -1;
private const int MinSwitchIntervalMs = 100; // 最小切换间隔100毫秒
// 内存管理相关字段
private long _totalMemoryUsage = 0;
private const long MaxMemoryUsageBytes = 100 * 1024 * 1024; // 100MB限制
private DateTime _lastMemoryCleanup = DateTime.MinValue;
private const int MemoryCleanupIntervalMinutes = 5; // 5分钟清理一次
#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);
// 重新初始化内存流数组
int slideCount = 0;
try
{
slideCount = presentation.Slides.Count;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x80048010)
{
return;
}
throw;
}
_memoryStreams = new MemoryStream[slideCount + 2];
// 如果启用自动保存,尝试加载已保存的墨迹
if (IsAutoSaveEnabled && !string.IsNullOrEmpty(AutoSaveLocation))
{
LoadSavedStrokes();
}
}
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))
{
if (DateTime.Now < _inkLockUntil)
{
}
return;
}
if (slideIndex < _memoryStreams.Length)
{
// 先释放旧的内存流,防止内存泄漏
try
{
_memoryStreams[slideIndex]?.Dispose();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"释放旧内存流失败: {ex}", LogHelper.LogType.Warning);
}
// 创建新的内存流
var ms = new MemoryStream();
strokes.Save(ms);
ms.Position = 0;
_memoryStreams[slideIndex] = ms;
if (ms.Length > 0)
{
}
// 检查内存使用情况
CheckAndPerformMemoryCleanup();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存第{slideIndex}页墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 强制保存指定页面的墨迹
/// </summary>
public void ForceSaveSlideStrokes(int slideIndex, StrokeCollection strokes)
{
if (slideIndex <= 0 || strokes == null) return;
lock (_lockObject)
{
try
{
if (slideIndex < _memoryStreams.Length)
{
// 先释放旧的内存流,防止内存泄漏
try
{
_memoryStreams[slideIndex]?.Dispose();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"释放旧内存流失败: {ex}", LogHelper.LogType.Warning);
}
// 创建新的内存流
var ms = new MemoryStream();
strokes.Save(ms);
ms.Position = 0;
_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]);
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
{
// 检查快速切换保护
var now = DateTime.Now;
if (now - _lastSwitchTime < TimeSpan.FromMilliseconds(MinSwitchIntervalMs) &&
_lastSwitchSlideIndex == slideIndex)
{
LogHelper.WriteLogToFile($"快速切换保护:忽略重复的页面切换请求 {slideIndex}", LogHelper.LogType.Warning);
return LoadSlideStrokes(slideIndex);
}
// 设置墨迹锁定
LockInkForSlide(slideIndex);
// 加载新页面的墨迹
var newStrokes = LoadSlideStrokes(slideIndex);
// 更新切换记录
_lastSwitchTime = now;
_lastSwitchSlideIndex = slideIndex;
if (newStrokes.Count > 0)
{
}
return newStrokes;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换到第{slideIndex}页失败: {ex}", LogHelper.LogType.Error);
return new StrokeCollection();
}
}
}
/// <summary>
/// 保存所有墨迹到文件
/// </summary>
/// <param name="presentation">演示文稿对象</param>
/// <param name="currentSlideIndex">当前播放的页码,如果提供则使用此值保存位置,否则使用_lockedSlideIndex</param>
public void SaveAllStrokesToFile(Presentation presentation, int currentSlideIndex = -1)
{
if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation) || presentation == null) return;
lock (_lockObject)
{
try
{
var folderPath = GetPresentationFolderPath();
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
// 保存位置信息
try
{
// 优先使用传入的当前页码,否则使用锁定的页码
int positionToSave = currentSlideIndex > 0 ? currentSlideIndex : _lockedSlideIndex;
// 如果都没有有效值,尝试使用最后切换的页码
if (positionToSave <= 0 && _lastSwitchSlideIndex > 0)
{
positionToSave = _lastSwitchSlideIndex;
}
if (positionToSave > 0)
{
File.WriteAllText(Path.Combine(folderPath, "Position"), positionToSave.ToString());
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存位置信息失败: {ex}", LogHelper.LogType.Error);
}
// 保存所有页面的墨迹
int savedCount = 0;
int slideCount = 0;
try
{
slideCount = presentation.Slides.Count;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x80048010)
{
return;
}
throw;
}
for (int i = 1; i <= slideCount && 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++;
}
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);
}
}
}
}
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);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从文件加载墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 清除所有墨迹
/// </summary>
public void ClearAllStrokes()
{
lock (_lockObject)
{
try
{
// 安全释放所有内存流
if (_memoryStreams != null)
{
for (int i = 0; i < _memoryStreams.Length; i++)
{
try
{
_memoryStreams[i]?.Dispose();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"释放内存流{i}失败: {ex}", LogHelper.LogType.Warning);
}
finally
{
_memoryStreams[i] = null;
}
}
// 重新初始化数组
_memoryStreams = new MemoryStream[_maxSlides + 2];
}
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 true;
}
// 如果当前页面与锁定页面相同,允许写入(用户在当前页面绘制)
if (currentSlideIndex == _lockedSlideIndex)
{
return true;
}
// 如果当前页面不是锁定页面,但锁定时间很短(小于50ms),允许写入
// 这样可以确保旧页面的墨迹能够及时保存
if (DateTime.Now - (_inkLockUntil.AddMilliseconds(-InkLockMilliseconds)) < TimeSpan.FromMilliseconds(50))
{
return true;
}
// 只有在快速切换且页面不同时才锁定
return false;
}
/// <summary>
/// 重置墨迹锁定状态
/// </summary>
public void ResetLockState()
{
lock (_lockObject)
{
_inkLockUntil = DateTime.MinValue;
_lockedSlideIndex = -1;
_lastSwitchTime = DateTime.MinValue;
_lastSwitchSlideIndex = -1;
}
}
/// <summary>
/// 检查并执行内存清理
/// </summary>
private void CheckAndPerformMemoryCleanup()
{
try
{
var now = DateTime.Now;
// 检查是否需要执行内存清理
if (now - _lastMemoryCleanup < TimeSpan.FromMinutes(MemoryCleanupIntervalMinutes))
{
return;
}
// 计算当前内存使用量
long currentMemoryUsage = 0;
if (_memoryStreams != null)
{
for (int i = 0; i < _memoryStreams.Length; i++)
{
if (_memoryStreams[i] != null)
{
currentMemoryUsage += _memoryStreams[i].Length;
}
}
}
_totalMemoryUsage = currentMemoryUsage;
// 如果内存使用量超过限制,执行清理
if (currentMemoryUsage > MaxMemoryUsageBytes)
{
LogHelper.WriteLogToFile($"内存使用量超限 ({currentMemoryUsage / 1024 / 1024}MB),开始清理", LogHelper.LogType.Warning);
// 清理非当前页面的墨迹
CleanupInactiveSlideStrokes();
_lastMemoryCleanup = now;
LogHelper.WriteLogToFile($"内存清理完成,当前使用量: {_totalMemoryUsage / 1024 / 1024}MB", LogHelper.LogType.Trace);
}
else
{
_lastMemoryCleanup = now;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"内存清理检查失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 清理非活跃页面的墨迹
/// </summary>
private void CleanupInactiveSlideStrokes()
{
try
{
if (_memoryStreams == null) return;
int cleanedCount = 0;
long freedMemory = 0;
for (int i = 0; i < _memoryStreams.Length; i++)
{
// 保留当前锁定页面和最近访问的页面
if (i == _lockedSlideIndex || i == _lastSwitchSlideIndex)
{
continue;
}
if (_memoryStreams[i] != null)
{
long memorySize = _memoryStreams[i].Length;
try
{
_memoryStreams[i].Dispose();
freedMemory += memorySize;
cleanedCount++;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理页面{i}墨迹失败: {ex}", LogHelper.LogType.Warning);
}
finally
{
_memoryStreams[i] = null;
}
}
}
if (cleanedCount > 0)
{
LogHelper.WriteLogToFile($"已清理{cleanedCount}个页面的墨迹,释放内存: {freedMemory / 1024}KB", LogHelper.LogType.Trace);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理非活跃页面墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
#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
+516
View File
@@ -0,0 +1,516 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
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}";
}
else
{
// 页数无效时清空页码显示
_mainWindow.PPTBtnPageNow.Text = "?";
_mainWindow.PPTBtnPageTotal.Text = "/ ?";
}
UpdateNavigationPanelsVisibility();
UpdateNavigationButtonStyles();
if (MainWindow.Settings.Advanced.IsEnableAvoidFullScreenHelper)
{
// 设置为画板模式,允许全屏操作
AvoidFullScreenHelper.SetBoardMode(true);
_dispatcher.BeginInvoke(new Action(() =>
{
MainWindow.MoveWindow(new WindowInteropHelper(_mainWindow).Handle, 0, 0,
System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height, true);
}), DispatcherPriority.ApplicationIdle);
}
}
else
{
_mainWindow.BtnPPTSlideShow.Visibility = Visibility.Visible;
_mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed;
HideAllNavigationPanels();
if (MainWindow.Settings.Advanced.IsEnableAvoidFullScreenHelper)
{
// 恢复为非画板模式,重新启用全屏限制
AvoidFullScreenHelper.SetBoardMode(false);
_dispatcher.BeginInvoke(new Action(() =>
{
// 退出PPT放映模式,恢复到工作区域大小
var workingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
MainWindow.MoveWindow(new WindowInteropHelper(_mainWindow).Handle,
workingArea.X, workingArea.Y,
workingArea.Width, workingArea.Height, true);
}), DispatcherPriority.ApplicationIdle);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新幻灯片放映状态UI失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 更新当前页码显示
/// </summary>
public void UpdateCurrentSlideNumber(int currentSlide, int totalSlides)
{
_dispatcher.InvokeAsync(() =>
{
try
{
// 只有在页数有效时才更新页码显示
if (currentSlide > 0 && totalSlides > 0)
{
_mainWindow.PPTBtnPageNow.Text = currentSlide.ToString();
_mainWindow.PPTBtnPageTotal.Text = $"/ {totalSlides}";
}
else
{
// 页数无效时清空页码显示
_mainWindow.PPTBtnPageNow.Text = "?";
_mainWindow.PPTBtnPageTotal.Text = "/ ?";
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新页码显示失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 处理PPT放映状态变化
/// </summary>
public void OnSlideShowStateChanged(bool isInSlideShow)
{
_dispatcher.InvokeAsync(() =>
{
try
{
if (!isInSlideShow)
{
// 如果不在放映模式,隐藏所有导航面板
HideAllNavigationPanels();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理PPT放映状态变化失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 更新导航面板显示状态
/// </summary>
public void UpdateNavigationPanelsVisibility()
{
_dispatcher.InvokeAsync(() =>
{
try
{
// 检查是否应该显示PPT按钮
// 不仅要检查按钮设置,还要确保确实在PPT放映模式下且页数有效
bool isInSlideShow = _mainWindow.PPTManager?.IsInSlideShow == true;
int slidesCount = _mainWindow.PPTManager?.SlidesCount ?? 0;
bool hasValidPageCount = slidesCount > 0;
bool shouldShowButtons = ShowPPTButton &&
_mainWindow.BtnPPTSlideShowEnd.Visibility == Visibility.Visible &&
isInSlideShow &&
hasValidPageCount &&
!MainWindow.Settings.Automation.IsAutoFoldInPPTSlideShow;
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 iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
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 Microsoft.Win32;
using Newtonsoft.Json;
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;
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 Ink_Canvas.Windows;
using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
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 Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
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,241 @@
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 增强的插件基类 V2,提供对三个专门服务接口的访问
/// 插件开发者可以根据需要选择性地使用这些服务
/// </summary>
public abstract class EnhancedPluginBaseV2 : PluginBase, IEnhancedPlugin
{
/// <summary>
/// 获取服务实例
/// </summary>
public IGetService GetService { get; private set; }
/// <summary>
/// 窗口服务实例
/// </summary>
public IWindowService WindowService { get; private set; }
/// <summary>
/// 操作服务实例
/// </summary>
public IActionService ActionService { get; private set; }
/// <summary>
/// 插件服务实例(兼容性)
/// </summary>
public IPluginService PluginService { get; private set; }
/// <summary>
/// 构造函数
/// </summary>
protected EnhancedPluginBaseV2()
{
// 初始化所有服务实例
PluginService = PluginServiceManager.Instance;
GetService = PluginServiceManager.Instance;
WindowService = PluginServiceManager.Instance;
ActionService = 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} 配置已变更");
}
#region 便
/// <summary>
/// 显示通知消息
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="type">消息类型</param>
protected void ShowNotification(string message, NotificationType type = NotificationType.Info)
{
WindowService.ShowNotification(message, type);
}
/// <summary>
/// 显示确认对话框
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="title">标题</param>
/// <returns>用户选择结果</returns>
protected bool ShowConfirmDialog(string message, string title = "确认")
{
return WindowService.ShowConfirmDialog(message, title);
}
/// <summary>
/// 显示输入对话框
/// </summary>
/// <param name="message">提示消息</param>
/// <param name="title">标题</param>
/// <param name="defaultValue">默认值</param>
/// <returns>用户输入内容</returns>
protected string ShowInputDialog(string message, string title = "输入", string defaultValue = "")
{
return WindowService.ShowInputDialog(message, title, defaultValue);
}
/// <summary>
/// 获取系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>设置值</returns>
protected T GetSetting<T>(string key, T defaultValue = default(T))
{
return GetService.GetSetting(key, defaultValue);
}
/// <summary>
/// 设置系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="value">设置值</param>
protected void SetSetting<T>(string key, T value)
{
ActionService.SetSetting(key, value);
}
/// <summary>
/// 保存设置
/// </summary>
protected void SaveSettings()
{
ActionService.SaveSettings();
}
/// <summary>
/// 清除当前画布
/// </summary>
protected void ClearCanvas()
{
ActionService.ClearCanvas();
}
/// <summary>
/// 撤销操作
/// </summary>
protected void Undo()
{
ActionService.Undo();
}
/// <summary>
/// 重做操作
/// </summary>
protected void Redo()
{
ActionService.Redo();
}
/// <summary>
/// 检查是否可以撤销
/// </summary>
protected bool CanUndo => GetService.CanUndo;
/// <summary>
/// 检查是否可以重做
/// </summary>
protected bool CanRedo => GetService.CanRedo;
/// <summary>
/// 获取当前绘制模式
/// </summary>
protected int CurrentDrawingMode => GetService.CurrentDrawingMode;
/// <summary>
/// 设置绘制模式
/// </summary>
/// <param name="mode">绘制模式</param>
protected void SetDrawingMode(int mode)
{
ActionService.SetDrawingMode(mode);
}
/// <summary>
/// 注册事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
protected void RegisterEventHandler(string eventName, System.EventHandler handler)
{
ActionService.RegisterEventHandler(eventName, handler);
}
/// <summary>
/// 注销事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
protected void UnregisterEventHandler(string eventName, System.EventHandler handler)
{
ActionService.UnregisterEventHandler(eventName, handler);
}
/// <summary>
/// 触发事件
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="sender">事件发送者</param>
/// <param name="args">事件参数</param>
protected void TriggerEvent(string eventName, object sender, System.EventArgs args)
{
ActionService.TriggerEvent(eventName, sender, args);
}
#endregion
}
}
@@ -0,0 +1,296 @@
using System;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 操作服务接口,统一所有执行操作相关的方法
/// </summary>
public interface IActionService
{
#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();
#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>
/// <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>
/// <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
#region
/// <summary>
/// 重启应用程序
/// </summary>
void RestartApplication();
/// <summary>
/// 退出应用程序
/// </summary>
void ExitApplication();
/// <summary>
/// 检查更新
/// </summary>
void CheckForUpdates();
/// <summary>
/// 打开帮助文档
/// </summary>
void OpenHelpDocument();
/// <summary>
/// 打开关于页面
/// </summary>
void OpenAboutPage();
#endregion
}
}
@@ -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();
}
}
+214
View File
@@ -0,0 +1,214 @@
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 获取服务接口,统一所有获取类的方法
/// </summary>
public interface IGetService
{
#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>
bool CanUndo { get; }
/// <summary>
/// 获取是否可以重做
/// </summary>
bool CanRedo { get; }
#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));
#endregion
#region
/// <summary>
/// 获取所有已加载的插件
/// </summary>
/// <returns>插件列表</returns>
List<IPlugin> GetAllPlugins();
/// <summary>
/// 获取指定插件
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <returns>插件实例</returns>
IPlugin GetPlugin(string pluginName);
#endregion
}
}
+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,38 @@
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件服务接口,提供对软件内部功能的访问
/// 继承自三个专门的服务接口:获取服务、窗口服务、操作服务
/// </summary>
public interface IPluginService : IGetService, IWindowService, IActionService
{
// 这个接口现在继承自三个专门的服务接口
// 所有方法都在子接口中定义,这里不需要重复定义
}
/// <summary>
/// 通知类型枚举
/// </summary>
public enum NotificationType
{
/// <summary>
/// 信息
/// </summary>
Info,
/// <summary>
/// 成功
/// </summary>
Success,
/// <summary>
/// 警告
/// </summary>
Warning,
/// <summary>
/// 错误
/// </summary>
Error
}
}
@@ -0,0 +1,152 @@
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 窗口服务接口,统一所有窗口操作相关的方法
/// </summary>
public interface IWindowService
{
#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>
/// <param name="isFullScreen">是否全屏</param>
void SetFullScreen(bool isFullScreen);
/// <summary>
/// 设置窗口置顶状态
/// </summary>
/// <param name="isTopMost">是否置顶</param>
void SetTopMost(bool isTopMost);
/// <summary>
/// 设置窗口可见性
/// </summary>
/// <param name="isVisible">是否可见</param>
void SetWindowVisibility(bool isVisible);
/// <summary>
/// 最小化窗口
/// </summary>
void MinimizeWindow();
/// <summary>
/// 最大化窗口
/// </summary>
void MaximizeWindow();
/// <summary>
/// 恢复窗口
/// </summary>
void RestoreWindow();
/// <summary>
/// 关闭窗口
/// </summary>
void CloseWindow();
#endregion
#region
/// <summary>
/// 设置窗口位置
/// </summary>
/// <param name="x">X坐标</param>
/// <param name="y">Y坐标</param>
void SetWindowPosition(double x, double y);
/// <summary>
/// 设置窗口大小
/// </summary>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
void SetWindowSize(double width, double height);
/// <summary>
/// 获取窗口位置
/// </summary>
/// <returns>窗口位置</returns>
(double x, double y) GetWindowPosition();
/// <summary>
/// 获取窗口大小
/// </summary>
/// <returns>窗口大小</returns>
(double width, double height) GetWindowSize();
#endregion
}
}
+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 Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
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

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