Compare commits
398 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 865415a6c0 | |||
| 32e8324275 | |||
| 842f6dd726 | |||
| 59f7d11df3 | |||
| dcd2f52c59 | |||
| b45413c232 | |||
| b4481ff680 | |||
| d7d7a3919f | |||
| f8e4732dcd | |||
| 1b92ed66b7 | |||
| 9ba74b9504 | |||
| 8cd49f12d1 | |||
| c48ca9ee89 | |||
| 43bcf71bf5 | |||
| ee48813df1 | |||
| f4a67e2822 | |||
| f03733da04 | |||
| 207560bcc7 | |||
| f38313ff2c | |||
| b5d9e21f37 | |||
| 5d42a8957e | |||
| b276c60909 | |||
| d03564bce9 | |||
| 41409c39d5 | |||
| aa9c5107d0 | |||
| 7a141822e7 | |||
| a4fd301d5a | |||
| b2ef8b96ef | |||
| baa9e1003e | |||
| 5d17a586d5 | |||
| 36b97b2adc | |||
| f13bffa834 | |||
| 4394566ed3 | |||
| de95c24f66 | |||
| 6058cb9cff | |||
| 6b20d3e268 | |||
| ce037a437a | |||
| 1349dab6d4 | |||
| d3d31925ee | |||
| d9fa77c86b | |||
| dad01235b0 | |||
| 52d9c26076 | |||
| 1a0236237d | |||
| 58bb7a5ebc | |||
| efbab58bca | |||
| eedbc7a863 | |||
| 626cda63ff | |||
| 7b7a9d93aa | |||
| f5c68dac61 | |||
| 2b6b106771 | |||
| 047586883e | |||
| 0c1a25dd6b | |||
| 7840a9a713 | |||
| 28f96ffcd3 | |||
| 04b2663183 | |||
| 1a11027871 | |||
| 854a00803e | |||
| 11ae5f157f | |||
| ecfe05139e | |||
| 4a392e03a7 | |||
| a66037f886 | |||
| 58c399dcbe | |||
| f33e617f44 | |||
| 3e976c1026 | |||
| c15c75075c | |||
| bf8d988c75 | |||
| 6fb7af3d46 | |||
| 1c2860c180 | |||
| fc41f10c37 | |||
| dedf366866 | |||
| edb10096d6 | |||
| ba42a1e6c9 | |||
| f5f989d140 | |||
| a0058c104d | |||
| 7f1f322d04 | |||
| 57ac8d8771 | |||
| bcd0509eff | |||
| 624af87795 | |||
| 616df56657 | |||
| 4efd6abb56 | |||
| 8c657a4ccf | |||
| db582f6c88 | |||
| a7b861f83c | |||
| 19b1c7ae8b | |||
| 192cec68c7 | |||
| 3541522fc6 | |||
| 4afa66f3f3 | |||
| 7fde157184 | |||
| a1935e8299 | |||
| 50e993fd89 | |||
| a25ec6b0af | |||
| efeb99aaac | |||
| f19118432d | |||
| b69eac2886 | |||
| 4efc4e7f34 | |||
| 4755eb06e2 | |||
| e4dc1c0b4e | |||
| 89f54c2b4a | |||
| abf52f0d49 | |||
| 044df3f09c | |||
| 271829f9c1 | |||
| 13625b37a8 | |||
| cceadd2a3d | |||
| 781191196f | |||
| eb0cf27218 | |||
| 1706341283 | |||
| 71f46b3bff | |||
| 4c39798682 | |||
| b861aa385d | |||
| 156e8a2686 | |||
| f88acf1375 | |||
| be770d4607 | |||
| 7565f624c9 | |||
| 65d56297fd | |||
| 86caac4a1d | |||
| d382ac4fa2 | |||
| f9ceeaad44 | |||
| 0691293973 | |||
| 9491b48eb6 | |||
| 6c7f63270f | |||
| a62f793288 | |||
| 5fc715b7d0 | |||
| 901b54b829 | |||
| 0efa1127a3 | |||
| 28fc53799a | |||
| bdc4af7cd4 | |||
| c68996ff20 | |||
| f517a63cda | |||
| fefb9b490e | |||
| 383e1d5368 | |||
| 4e4ce36e76 | |||
| 19983a113e | |||
| 8eaac465ff | |||
| 17b2d744ba | |||
| 166e0d400a | |||
| 5cf409ce0b | |||
| e37e847a8b | |||
| 32c77d3743 | |||
| 45e368d9e7 | |||
| 97496302fb | |||
| 675959e615 | |||
| f641b282b6 | |||
| e92a05683c | |||
| 93bef2e144 | |||
| 44e331ae96 | |||
| f295c85668 | |||
| 6f843bface | |||
| 2dea6076f0 | |||
| 78c6c7b571 | |||
| a50bec9326 | |||
| 7d193d7de6 | |||
| 5bbb3e6a6c | |||
| 32c1ddab46 | |||
| 15198c32ea | |||
| 476503338f | |||
| ba6303dca0 | |||
| 23432465ac | |||
| 1f0b5cebeb | |||
| 1f83c84a7e | |||
| e209af9c57 | |||
| a714d312a8 | |||
| 4a776c1fe8 | |||
| da86f07cbe | |||
| 48e0ca887d | |||
| 0f3b6c4cec | |||
| 9ce9631135 | |||
| c73a5c3a7e | |||
| 62d35127b1 | |||
| 09f17caabe | |||
| c5355d7497 | |||
| d5142ad82c | |||
| 428b278c78 | |||
| 33c1e04934 | |||
| 6c075d80ba | |||
| 38713108e0 | |||
| 6b75fea813 | |||
| de6fc84fec | |||
| ecd276a3a0 | |||
| 86fbd0cebc | |||
| 6a97919f7a | |||
| f2e7e17bd1 | |||
| eedd6cee1d | |||
| 25e11fa9de | |||
| 4742600b86 | |||
| b3e4850413 | |||
| 1085f6c57a | |||
| fd8d13447c | |||
| 758a3d3f99 | |||
| fd8b4d94d6 | |||
| fa7dae8177 | |||
| 21638218c6 | |||
| a29a414e8a | |||
| 251c7f399e | |||
| 1da55f2011 | |||
| 856125edc2 | |||
| 32ef30ebd8 | |||
| 8972e42fee | |||
| 91f206aad0 | |||
| 5626babcdf | |||
| 98175152db | |||
| 817ade3e34 | |||
| ab5493f8c4 | |||
| 796bd99377 | |||
| a0dc60a403 | |||
| a92e58abf1 | |||
| e8f0793feb | |||
| 2d7eff8205 | |||
| 6412892985 | |||
| 69ae0ffc71 | |||
| 854be23cfb | |||
| 02e143217e | |||
| 4feec82b03 | |||
| 938ca648f1 | |||
| ee018ea287 | |||
| 8ab60ad000 | |||
| 17eaf34500 | |||
| 2485958f6e | |||
| 7ac3a22fa6 | |||
| 285f211f50 | |||
| e9ec424625 | |||
| 1e96477127 | |||
| 87ffa48265 | |||
| 8b64df435c | |||
| dcf88ee510 | |||
| b21a15376d | |||
| 3e87ed4f9b | |||
| a6cd9c955b | |||
| 86b52b76ed | |||
| 853f380e5d | |||
| 027686a3b5 | |||
| 79b87e59be | |||
| 3a37e55162 | |||
| 1ff40c0016 | |||
| 4f7c1021c8 | |||
| e687c78ba8 | |||
| 2c2f46a0d8 | |||
| 29fa565258 | |||
| 51f3d410c9 | |||
| a0d159da9d | |||
| 9edb58ee27 | |||
| a8de65ab0e | |||
| ef8a894489 | |||
| 992f388a56 | |||
| a54e7206a0 | |||
| c4e74b21fc | |||
| 7b9e07bdda | |||
| 19f5204253 | |||
| c4ad5c5f81 | |||
| e888118a20 | |||
| 7afce136f6 | |||
| 71c4dadeee | |||
| c6a95c99ef | |||
| a6d44685b3 | |||
| a8ce61dad9 | |||
| 0f47d1473d | |||
| 2134ee516a | |||
| 6c60306bee | |||
| 816748833c | |||
| cac0fca3bb | |||
| 5ed28b121e | |||
| c4180eba6f | |||
| 0065a1f81f | |||
| e1f3a6ada4 | |||
| ed7ffbcb13 | |||
| 6a8d9db407 | |||
| 81140837f1 | |||
| 70232645a8 | |||
| 4d3b3ef3df | |||
| d203314424 | |||
| b95da2d8d5 | |||
| a8c5ccda17 | |||
| 86b996637c | |||
| 1480d4a14e | |||
| 8586735ca8 | |||
| b8013afd64 | |||
| 812bc83939 | |||
| d6829744bb | |||
| a7a03544b6 | |||
| ffa8729d10 | |||
| ce291d3a77 | |||
| 297dbad60c | |||
| 54982218f5 | |||
| c14f4df494 | |||
| f53f23aa4b | |||
| 10c59fb4b4 | |||
| a5cffc5ea4 | |||
| 200c299ce7 | |||
| 8547276549 | |||
| b09afcf725 | |||
| a2860be7a3 | |||
| de9d026e3a | |||
| 27a1baae7b | |||
| f2584309d1 | |||
| 831fd09615 | |||
| e0e22a09c0 | |||
| 60c6e0632d | |||
| 6da13c4fc0 | |||
| ec330aea69 | |||
| 318682b63a | |||
| dd53d7ff0a | |||
| b03207d287 | |||
| 7db87b7c36 | |||
| 54d0aaca04 | |||
| 352aa886c8 | |||
| 8fada629f0 | |||
| 34bb3cd0bd | |||
| 5555a67422 | |||
| 3631d35946 | |||
| 460d4a5ac1 | |||
| b7b5db18fd | |||
| 58d877af6b | |||
| ab20e0d8f2 | |||
| 42e55c51b0 | |||
| ca82c24c76 | |||
| 66874e7a85 | |||
| e365a94f71 | |||
| 81298e5980 | |||
| 4eb920553b | |||
| 1be5408302 | |||
| 1e9a7b037a | |||
| e79ed438ef | |||
| ff658409be | |||
| 9c26353fed | |||
| 2a3ce9549a | |||
| 8093f55b4f | |||
| 4a46892486 | |||
| 35fafc39a8 | |||
| 3976970bb5 | |||
| 1a3ab49849 | |||
| 32d75ae23d | |||
| 29c6844390 | |||
| 9c6eee59a8 | |||
| 4ff2be67d3 | |||
| 47d3eb13ce | |||
| 5de3043544 | |||
| 4b1d544c6b | |||
| 6cb8fe3a6e | |||
| 1acf2e044e | |||
| 9e9e960fa3 | |||
| e767873ff1 | |||
| b57762afed | |||
| 24526c5a48 | |||
| ed1b62ec50 | |||
| 98e727cf55 | |||
| 6c4ed850b0 | |||
| db6b9da945 | |||
| 52c930624a | |||
| b7ddcb5419 | |||
| 34f27c8e8d | |||
| 3f7055327b | |||
| 51fb13b448 | |||
| 5273990305 | |||
| a44badddaa | |||
| 3e8d186dc8 | |||
| 51f8a0541d | |||
| 808da0a3a7 | |||
| 1c522840cd | |||
| 6fb8506551 | |||
| 7bf0438960 | |||
| fe02ec1852 | |||
| 4020925af1 | |||
| 863b42f516 | |||
| 7f6b03be27 | |||
| 880770b718 | |||
| ed74f98919 | |||
| 4a1e42d5ee | |||
| b21e608eea | |||
| 671ec2fba6 | |||
| 293a89575a | |||
| 542812eb74 | |||
| e44cb715d0 | |||
| f7a1a1c851 | |||
| 757c08cd02 | |||
| 15fcd50151 | |||
| 867a747853 | |||
| 02dfbb54b6 | |||
| c42c6c8dfe | |||
| 316d568cbc | |||
| 0b72b1f1b3 | |||
| 25b52def92 | |||
| ab67ea48f0 | |||
| 912c1b91e4 | |||
| d4f1d21634 | |||
| 6c0e7c2e64 | |||
| e054a50b55 | |||
| ff005e6398 | |||
| 74cc8e7dda | |||
| 254d70a787 | |||
| 28b728822c | |||
| 5a53471bcd | |||
| a3fee5d77c | |||
| 6f961225d7 | |||
| 1b0b4f7505 | |||
| fac66fafbd | |||
| b9d6ac3047 | |||
| e7c723de46 | |||
| 10629b253c | |||
| dace6ad486 |
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"projectName": "community",
|
||||
"projectOwner": "InkCanvasForClass",
|
||||
"files": [
|
||||
"README.md"
|
||||
],
|
||||
"commitType": "docs",
|
||||
"commitConvention": "angular",
|
||||
"contributorsPerLine": 7,
|
||||
"contributors": [
|
||||
{
|
||||
"login": "CJKmkp",
|
||||
"name": "CJK_mkp",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/113243675?v=4",
|
||||
"profile": "https://github.com/CJKmkp",
|
||||
"contributions": [
|
||||
"maintenance",
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Hydro11451",
|
||||
"name": "Hydrogen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/214308559?v=4",
|
||||
"profile": "http://hydro11451.qzz.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "CreeperAWA",
|
||||
"name": "CreeperAWA",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/134939494?v=4",
|
||||
"profile": "https://github.com/CreeperAWA",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "2-2-3-trimethylpentane",
|
||||
"name": "2,2,3-三甲基戊烷",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/141403762?v=4",
|
||||
"profile": "https://github.com/2-2-3-trimethylpentane",
|
||||
"contributions": [
|
||||
"blog",
|
||||
"doc",
|
||||
"design"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Alan-CRL",
|
||||
"name": "Alan-CRL",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/92425617?v=4",
|
||||
"profile": "https://github.com/Alan-CRL",
|
||||
"contributions": [
|
||||
"code",
|
||||
"infra",
|
||||
"doc",
|
||||
"financial"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MKStoler1024",
|
||||
"name": "MKStoler1024",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/158786854?v=4",
|
||||
"profile": "https://github.com/MKStoler1024",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code",
|
||||
"design"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "awesome-iwb",
|
||||
"name": "Awesome Iwb",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/184760810?v=4",
|
||||
"profile": "https://github.com/awesome-iwb",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "PrefacedCorg",
|
||||
"name": "PrefacedCorg",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/129855423?v=4",
|
||||
"profile": "https://github.com/PrefacedCorg",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Bug 报告 | Bug Report
|
||||
description: 反馈软件缺陷或异常 | Report a bug to help us improve
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢你的反馈!请详细填写以下内容,便于我们定位问题。
|
||||
Thank you for your feedback! Please fill out the following information to help us locate the issue.
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: 软件版本 | App Version
|
||||
description: 可在设置中的“关于”界面查看 | You can find it on the "About" interface in the settings
|
||||
placeholder: 例如 v1.2.3 | e.g. v1.2.3
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: 操作系统及版本 | OS & Version
|
||||
placeholder: 例如 Windows 10 22H2 64位 | e.g. Windows 10 22H2 64bit
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 问题描述 | Description
|
||||
description: 简要描述遇到的问题 | Briefly describe the problem
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: 复现步骤 | Steps to Reproduce
|
||||
description: 如何复现该问题?如有必要可附截图/录屏 | How to reproduce this bug? Screenshots/recordings if needed
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: 期望结果 | Expected Behavior
|
||||
description: 你期望的正确行为或结果 | What did you expect to happen?
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: extra
|
||||
attributes:
|
||||
label: 其他补充信息 | Additional Info
|
||||
description: 其他相关信息(如日志、配置、特殊环境等)| Any other context, logs, configs, special environment, etc.
|
||||
validations:
|
||||
required: false
|
||||
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Description
|
||||
@@ -0,0 +1,37 @@
|
||||
name: 功能请求 | Feature Request
|
||||
description: 提出你对本项目的功能建议 | Suggest an idea for this project
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢你的建议!请详细描述你的需求。
|
||||
Thank you for your suggestion! Please describe your needs in detail.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 功能描述 | Description
|
||||
description: 请描述你希望添加的功能 | Describe the feature you want
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: motivation
|
||||
attributes:
|
||||
label: 需求动机 | Motivation
|
||||
description: 为什么需要这个功能?| Why do you need this feature?
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: design
|
||||
attributes:
|
||||
label: 期望设计 | Expected Design
|
||||
description: (可选)描述或画出你期望的界面或交互 | (Optional) Describe or sketch the expected UI/UX
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: extra
|
||||
attributes:
|
||||
label: 其他补充信息 | Additional Info
|
||||
description: 其他补充说明或建议 | Any other context or suggestions
|
||||
validations:
|
||||
required: false
|
||||
@@ -1,3 +1,3 @@
|
||||
/Ink Canvas/obj
|
||||
/Ink Canvas/bin
|
||||
/.vs
|
||||
obj/
|
||||
bin/
|
||||
.vs
|
||||
@@ -1,37 +1,37 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\",
|
||||
"WorkspaceRootPath": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\",
|
||||
"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}",
|
||||
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\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}"
|
||||
},
|
||||
{
|
||||
@@ -47,18 +47,210 @@
|
||||
"VerticalTabListWidth": 256,
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": 0,
|
||||
"DockedWidth": 210,
|
||||
"SelectedChildIndex": 48,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{e506b91c-c606-466a-90a9-123d1d1e12b3}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{387cb18d-6153-4156-9257-9ac3f9207bbe}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{e8b06f52-6d01-11d2-aa7d-00c04f990343}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{46c87f81-5a06-43a8-9e25-85d33bac49f8}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{269a02dc-6af8-11d3-bdc4-00c04f688e50}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{99b8fa2f-ab90-4f57-9c32-949f146f1914}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{c79b74ff-f1d7-4c94-aefa-4d22bfe1b1f9}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:128:0:{13b12e3e-c1b4-4539-9371-4fe9a0d523fc}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:2:0:{34c7837f-3b3a-449c-bdf0-bdad86cbaf4a}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{37aba9be-445a-11d3-9949-00c04f68fd0a}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{dcc4ea97-1c0c-482b-b205-e541c0df9728}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:128:0:{75188d03-9892-4ae2-abf1-207126247ce5}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:129:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{eefa5220-e298-11d0-8f78-00a0c9110057}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{4a9b7e51-aa16-11d0-a8c5-00a0c921a4d2}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:132:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:133:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:3:0:{34c7837f-3b3a-449c-bdf0-bdad86cbaf4a}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:153:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:154:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:152:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:151:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:150:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:148:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:149:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:136:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:144:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:147:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:145:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:146:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:143:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:142:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:141:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:140:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:139:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:138:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:137:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:135:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:134:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:129:0:{13b12e3e-c1b4-4539-9371-4fe9a0d523fc}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:130:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:129:0:{75188d03-9892-4ae2-abf1-207126247ce5}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{cce594b6-0c39-4442-ba28-10c64ac7e89f}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{b1e99781-ab81-11d0-b683-00aa00a3ee26}"
|
||||
},
|
||||
{
|
||||
"$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",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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==",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
|
||||
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
|
||||
"ViewState": "AgIAAOgCAAAAAAAAAAAuwEoGAAAIAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2025-05-31T10:49:24.719Z",
|
||||
"EditorCaption": ""
|
||||
@@ -67,22 +259,21 @@
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 1,
|
||||
"Title": "README.md",
|
||||
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\README.md",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\README.md",
|
||||
"RelativeDocumentMoniker": "README.md",
|
||||
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\README.md",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\README.md",
|
||||
"RelativeToolTip": "README.md",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
|
||||
"WhenOpened": "2025-05-31T10:48:22.883Z",
|
||||
"EditorCaption": ""
|
||||
"WhenOpened": "2025-05-31T10:48:22.883Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 7,
|
||||
"Title": "MainWindow.xaml",
|
||||
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow.xaml",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow.xaml",
|
||||
"RelativeToolTip": "Ink Canvas\\MainWindow.xaml",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|",
|
||||
"WhenOpened": "2025-05-24T13:22:56.715Z"
|
||||
@@ -103,9 +294,9 @@
|
||||
"$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",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
|
||||
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
|
||||
"ViewState": "AgIAAFgAAAAAAAAAAAAUwHQAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
@@ -116,9 +307,9 @@
|
||||
"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",
|
||||
"RelativeDocumentMoniker": "..\\..\\..\\..\\Administrator\\Desktop\\ICC CE\\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",
|
||||
"RelativeToolTip": "..\\..\\..\\..\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
|
||||
"WhenOpened": "2025-05-24T13:04:01.407Z"
|
||||
@@ -127,53 +318,77 @@
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 2,
|
||||
"Title": "privacy.txt",
|
||||
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\privacy.txt",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\privacy.txt",
|
||||
"RelativeDocumentMoniker": "privacy.txt",
|
||||
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\privacy.txt",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\privacy.txt",
|
||||
"RelativeToolTip": "privacy.txt",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
|
||||
"WhenOpened": "2025-05-24T13:04:01.337Z",
|
||||
"EditorCaption": ""
|
||||
"WhenOpened": "2025-05-24T13:04:01.337Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 3,
|
||||
"Title": "Manual.md",
|
||||
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Manual.md",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Manual.md",
|
||||
"RelativeDocumentMoniker": "Manual.md",
|
||||
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Manual.md",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Manual.md",
|
||||
"RelativeToolTip": "Manual.md",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
|
||||
"WhenOpened": "2025-05-24T13:04:00.986Z",
|
||||
"EditorCaption": ""
|
||||
"WhenOpened": "2025-05-24T13:04:00.986Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 4,
|
||||
"Title": "LICENSE",
|
||||
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\LICENSE",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\LICENSE",
|
||||
"RelativeDocumentMoniker": "LICENSE",
|
||||
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\LICENSE",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\LICENSE",
|
||||
"RelativeToolTip": "LICENSE",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
|
||||
"WhenOpened": "2025-05-24T13:04:00.902Z",
|
||||
"EditorCaption": ""
|
||||
"WhenOpened": "2025-05-24T13:04:00.902Z"
|
||||
},
|
||||
{
|
||||
"$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",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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": ""
|
||||
"WhenOpened": "2025-05-24T13:04:00.792Z"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{d84ee353-0bef-5a41-a649-8f89aca5d84d}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"DockedWidth": 204,
|
||||
"SelectedChildIndex": -1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:1:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{590a070c-4fcd-52d9-87da-dfaa11710261}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"DockedWidth": 190,
|
||||
"SelectedChildIndex": -1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{f4fc5ff1-28ef-4b04-ad02-6b298310ccc7}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\",
|
||||
"WorkspaceRootPath": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\",
|
||||
"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}",
|
||||
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\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}",
|
||||
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\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}"
|
||||
},
|
||||
{
|
||||
@@ -47,18 +47,210 @@
|
||||
"VerticalTabListWidth": 256,
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": 0,
|
||||
"DockedWidth": 210,
|
||||
"SelectedChildIndex": 48,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{e506b91c-c606-466a-90a9-123d1d1e12b3}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{387cb18d-6153-4156-9257-9ac3f9207bbe}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{e8b06f52-6d01-11d2-aa7d-00c04f990343}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{46c87f81-5a06-43a8-9e25-85d33bac49f8}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{269a02dc-6af8-11d3-bdc4-00c04f688e50}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{99b8fa2f-ab90-4f57-9c32-949f146f1914}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{c79b74ff-f1d7-4c94-aefa-4d22bfe1b1f9}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:128:0:{13b12e3e-c1b4-4539-9371-4fe9a0d523fc}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:2:0:{34c7837f-3b3a-449c-bdf0-bdad86cbaf4a}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{37aba9be-445a-11d3-9949-00c04f68fd0a}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{dcc4ea97-1c0c-482b-b205-e541c0df9728}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:128:0:{75188d03-9892-4ae2-abf1-207126247ce5}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:129:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{eefa5220-e298-11d0-8f78-00a0c9110057}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{4a9b7e51-aa16-11d0-a8c5-00a0c921a4d2}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:132:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:133:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:3:0:{34c7837f-3b3a-449c-bdf0-bdad86cbaf4a}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:153:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:154:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:152:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:151:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:150:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:148:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:149:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:136:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:144:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:147:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:145:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:146:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:143:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:142:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:141:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:140:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:139:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:138:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:137:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:135:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:134:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:129:0:{13b12e3e-c1b4-4539-9371-4fe9a0d523fc}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:130:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:129:0:{75188d03-9892-4ae2-abf1-207126247ce5}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{cce594b6-0c39-4442-ba28-10c64ac7e89f}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{b1e99781-ab81-11d0-b683-00aa00a3ee26}"
|
||||
},
|
||||
{
|
||||
"$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",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
|
||||
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
|
||||
"ViewState": "AgIAAFgHAAAAAAAAAAAgwGcHAAAIAAAAAAAAAA==",
|
||||
"ViewState": "AgIAAOgCAAAAAAAAAAAuwEoGAAAIAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2025-05-31T10:49:24.719Z",
|
||||
"EditorCaption": ""
|
||||
@@ -67,22 +259,21 @@
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 1,
|
||||
"Title": "README.md",
|
||||
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\README.md",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\README.md",
|
||||
"RelativeDocumentMoniker": "README.md",
|
||||
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\README.md",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\README.md",
|
||||
"RelativeToolTip": "README.md",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
|
||||
"WhenOpened": "2025-05-31T10:48:22.883Z",
|
||||
"EditorCaption": ""
|
||||
"WhenOpened": "2025-05-31T10:48:22.883Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 7,
|
||||
"Title": "MainWindow.xaml",
|
||||
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Ink Canvas\\MainWindow.xaml",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow.xaml",
|
||||
"RelativeToolTip": "Ink Canvas\\MainWindow.xaml",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|",
|
||||
"WhenOpened": "2025-05-24T13:22:56.715Z"
|
||||
@@ -103,9 +294,9 @@
|
||||
"$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",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
|
||||
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
|
||||
"ViewState": "AgIAAFgAAAAAAAAAAAAUwHQAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
@@ -116,9 +307,9 @@
|
||||
"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",
|
||||
"RelativeDocumentMoniker": "..\\..\\..\\..\\Administrator\\Desktop\\ICC CE\\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",
|
||||
"RelativeToolTip": "..\\..\\..\\..\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
|
||||
"WhenOpened": "2025-05-24T13:04:01.407Z"
|
||||
@@ -127,53 +318,77 @@
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 2,
|
||||
"Title": "privacy.txt",
|
||||
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\privacy.txt",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\privacy.txt",
|
||||
"RelativeDocumentMoniker": "privacy.txt",
|
||||
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\privacy.txt",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\privacy.txt",
|
||||
"RelativeToolTip": "privacy.txt",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
|
||||
"WhenOpened": "2025-05-24T13:04:01.337Z",
|
||||
"EditorCaption": ""
|
||||
"WhenOpened": "2025-05-24T13:04:01.337Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 3,
|
||||
"Title": "Manual.md",
|
||||
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Manual.md",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Manual.md",
|
||||
"RelativeDocumentMoniker": "Manual.md",
|
||||
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\Manual.md",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Manual.md",
|
||||
"RelativeToolTip": "Manual.md",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
|
||||
"WhenOpened": "2025-05-24T13:04:00.986Z",
|
||||
"EditorCaption": ""
|
||||
"WhenOpened": "2025-05-24T13:04:00.986Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 4,
|
||||
"Title": "LICENSE",
|
||||
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\LICENSE",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\LICENSE",
|
||||
"RelativeDocumentMoniker": "LICENSE",
|
||||
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\ICC CE 1.2.5\\LICENSE",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\LICENSE",
|
||||
"RelativeToolTip": "LICENSE",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
|
||||
"WhenOpened": "2025-05-24T13:04:00.902Z",
|
||||
"EditorCaption": ""
|
||||
"WhenOpened": "2025-05-24T13:04:00.902Z"
|
||||
},
|
||||
{
|
||||
"$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",
|
||||
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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",
|
||||
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\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": ""
|
||||
"WhenOpened": "2025-05-24T13:04:00.792Z"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{d84ee353-0bef-5a41-a649-8f89aca5d84d}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"DockedWidth": 204,
|
||||
"SelectedChildIndex": -1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:1:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{590a070c-4fcd-52d9-87da-dfaa11710261}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"DockedWidth": 190,
|
||||
"SelectedChildIndex": -1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{f4fc5ff1-28ef-4b04-ad02-6b298310ccc7}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
"ExpandedNodes": [
|
||||
""
|
||||
],
|
||||
"SelectedNode": "\\Ink Canvas.sln",
|
||||
"PreviewInSolutionExplorer": false
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
1.6.3
|
||||
1.7.5.0
|
||||
|
||||
|
After Width: | Height: | Size: 550 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.7 MiB |
@@ -1,20 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using Hardcodet.Wpf.TaskbarNotification;
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Microsoft.Win32;
|
||||
using System.Security; // 添加SecurityException所需命名空间
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
|
||||
using Newtonsoft.Json;
|
||||
using Application = System.Windows.Application;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using Window = System.Windows.Window;
|
||||
using System.Collections.Generic;
|
||||
using Timer = System.Threading.Timer;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
@@ -23,43 +29,401 @@ namespace Ink_Canvas
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
System.Threading.Mutex mutex;
|
||||
Mutex mutex;
|
||||
|
||||
public static string[] StartArgs = null;
|
||||
public static string[] StartArgs;
|
||||
public static string RootPath = Environment.GetEnvironmentVariable("APPDATA") + "\\Ink Canvas\\";
|
||||
|
||||
// 新增:保存看门狗进程对象
|
||||
private static Process watchdogProcess = null;
|
||||
private static Process watchdogProcess;
|
||||
// 新增:标记是否为软件内主动退出
|
||||
public static bool IsAppExitByUser = false;
|
||||
public static bool IsAppExitByUser;
|
||||
// 新增:退出信号文件路径
|
||||
private static string watchdogExitSignalFile = Path.Combine(Path.GetTempPath(), "icc_watchdog_exit_" + System.Diagnostics.Process.GetCurrentProcess().Id + ".flag");
|
||||
private static string watchdogExitSignalFile = Path.Combine(Path.GetTempPath(), "icc_watchdog_exit_" + Process.GetCurrentProcess().Id + ".flag");
|
||||
// 新增:崩溃日志文件路径
|
||||
private static string crashLogFile = Path.Combine(Environment.GetEnvironmentVariable("APPDATA"), "Ink Canvas", "crash_logs");
|
||||
// 新增:进程ID
|
||||
private static int currentProcessId = Process.GetCurrentProcess().Id;
|
||||
// 新增:应用启动时间
|
||||
private static DateTime appStartTime = DateTime.Now;
|
||||
// 新增:最后一次错误信息
|
||||
private static string lastErrorMessage = string.Empty;
|
||||
// 新增:是否已初始化崩溃监听器
|
||||
private static bool crashListenersInitialized;
|
||||
|
||||
public App()
|
||||
{
|
||||
this.Startup += new StartupEventHandler(App_Startup);
|
||||
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
|
||||
// 配置TLS协议以支持Windows 7
|
||||
ConfigureTlsForWindows7();
|
||||
|
||||
// 如果是看门狗子进程,直接进入看门狗主循环并终止主流程
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
if (args.Length >= 2 && args[1] == "--watchdog")
|
||||
{
|
||||
RunWatchdogIfNeeded();
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动时优先同步设置,确保CrashAction为最新
|
||||
SyncCrashActionFromSettings();
|
||||
|
||||
Startup += App_Startup;
|
||||
DispatcherUnhandledException += App_DispatcherUnhandledException;
|
||||
StartHeartbeatMonitor();
|
||||
StartWatchdogIfNeeded();
|
||||
this.Exit += App_Exit; // 注册退出事件
|
||||
|
||||
// 新增:初始化全局异常和进程结束处理
|
||||
InitializeCrashListeners();
|
||||
|
||||
// 仅在崩溃后操作为静默重启时才启动看门狗
|
||||
if (CrashAction == CrashActionType.SilentRestart)
|
||||
{
|
||||
StartWatchdogIfNeeded();
|
||||
}
|
||||
Exit += App_Exit; // 注册退出事件
|
||||
}
|
||||
|
||||
// 新增:配置TLS协议以支持Windows 7
|
||||
private void ConfigureTlsForWindows7()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检测操作系统版本
|
||||
var osVersion = Environment.OSVersion;
|
||||
bool isWindows7 = osVersion.Version.Major == 6 && osVersion.Version.Minor == 1;
|
||||
|
||||
if (isWindows7)
|
||||
{
|
||||
LogHelper.WriteLogToFile("检测到Windows 7系统,配置TLS协议支持");
|
||||
|
||||
// 启用所有TLS版本以支持Windows 7
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
|
||||
|
||||
// 配置ServicePointManager以支持Windows 7
|
||||
ServicePointManager.DefaultConnectionLimit = 10;
|
||||
ServicePointManager.Expect100Continue = false;
|
||||
ServicePointManager.UseNagleAlgorithm = false;
|
||||
|
||||
LogHelper.WriteLogToFile("TLS协议配置完成,已启用TLS 1.2/1.1/1.0支持");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 对于更新的Windows版本,不进行任何TLS配置,使用系统默认设置
|
||||
LogHelper.WriteLogToFile($"检测到Windows版本: {osVersion.VersionString},使用系统默认TLS配置");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"配置TLS协议时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:初始化崩溃监听器
|
||||
private void InitializeCrashListeners()
|
||||
{
|
||||
if (crashListenersInitialized) return;
|
||||
|
||||
try
|
||||
{
|
||||
// 确保崩溃日志目录存在
|
||||
if (!Directory.Exists(crashLogFile))
|
||||
{
|
||||
Directory.CreateDirectory(crashLogFile);
|
||||
}
|
||||
|
||||
// 注册非UI线程未处理异常处理程序
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
|
||||
// 注册控制台Ctrl+C等终止信号处理
|
||||
Console.CancelKeyPress += Console_CancelKeyPress;
|
||||
|
||||
// 注册系统会话结束事件(关机、注销等)
|
||||
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
|
||||
|
||||
// 注册进程退出处理程序
|
||||
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
|
||||
|
||||
// 尝试注册Windows关闭消息监听
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, true);
|
||||
|
||||
// 如果系统支持,添加Windows Management Instrumentation监听器
|
||||
try
|
||||
{
|
||||
// 使用反射动态加载和调用WMI
|
||||
TrySetupWmiMonitoring();
|
||||
}
|
||||
catch (Exception wmiEx)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"设置WMI进程监控失败: {wmiEx.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
crashListenersInitialized = true;
|
||||
LogHelper.WriteLogToFile("已初始化崩溃监听器");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"初始化崩溃监听器失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:动态加载WMI监控(避免直接引用System.Management)
|
||||
private void TrySetupWmiMonitoring()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查System.Management程序集是否可用
|
||||
var assemblyName = "System.Management, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
|
||||
var assembly = Assembly.Load(assemblyName);
|
||||
if (assembly == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到System.Management程序集,跳过WMI监控", LogHelper.LogType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用反射创建WMI查询
|
||||
var watcherType = assembly.GetType("System.Management.ManagementEventWatcher");
|
||||
if (watcherType == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到ManagementEventWatcher类型,跳过WMI监控", LogHelper.LogType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建WMI查询字符串
|
||||
string queryString = $"SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.ProcessId = {currentProcessId}";
|
||||
|
||||
// 创建ManagementEventWatcher实例
|
||||
object watcher = Activator.CreateInstance(watcherType, queryString);
|
||||
|
||||
// 获取EventArrived事件信息
|
||||
var eventInfo = watcherType.GetEvent("EventArrived");
|
||||
if (eventInfo == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到EventArrived事件,跳过WMI监控", LogHelper.LogType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建委托并订阅事件
|
||||
Type delegateType = eventInfo.EventHandlerType;
|
||||
var handler = Delegate.CreateDelegate(delegateType, this, GetType().GetMethod("WmiEventHandler", BindingFlags.NonPublic | BindingFlags.Instance));
|
||||
eventInfo.AddEventHandler(watcher, handler);
|
||||
|
||||
// 启动监听
|
||||
var startMethod = watcherType.GetMethod("Start");
|
||||
startMethod.Invoke(watcher, null);
|
||||
|
||||
LogHelper.WriteLogToFile("已成功启动WMI进程监控");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"动态加载WMI监控失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
// WMI事件处理方法(通过反射调用)
|
||||
private void WmiEventHandler(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 尝试从事件参数中提取信息
|
||||
dynamic eventArgs = e;
|
||||
dynamic newEvent = eventArgs.NewEvent;
|
||||
if (newEvent != null)
|
||||
{
|
||||
dynamic targetInstance = newEvent["TargetInstance"];
|
||||
if (targetInstance != null)
|
||||
{
|
||||
string processName = targetInstance["Name"]?.ToString() ?? "未知进程";
|
||||
WriteCrashLog($"WMI检测到进程{processName}(ID:{currentProcessId})已终止");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"处理WMI事件时出错: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:Windows控制台控制处理程序
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handler, bool add);
|
||||
|
||||
private delegate bool ConsoleCtrlDelegate(int ctrlType);
|
||||
|
||||
private static bool ConsoleCtrlHandler(int ctrlType)
|
||||
{
|
||||
string eventType = "未知控制类型";
|
||||
|
||||
// 使用传统switch语句替代switch表达式
|
||||
switch (ctrlType)
|
||||
{
|
||||
case 0:
|
||||
eventType = "CTRL_C_EVENT";
|
||||
break;
|
||||
case 1:
|
||||
eventType = "CTRL_BREAK_EVENT";
|
||||
break;
|
||||
case 2:
|
||||
eventType = "CTRL_CLOSE_EVENT";
|
||||
break;
|
||||
case 5:
|
||||
eventType = "CTRL_LOGOFF_EVENT";
|
||||
break;
|
||||
case 6:
|
||||
eventType = "CTRL_SHUTDOWN_EVENT";
|
||||
break;
|
||||
default:
|
||||
eventType = $"未知控制类型({ctrlType})";
|
||||
break;
|
||||
}
|
||||
|
||||
WriteCrashLog($"接收到系统控制信号: {eventType}");
|
||||
|
||||
// 返回true表示已处理该事件
|
||||
return false;
|
||||
}
|
||||
|
||||
// 新增:系统会话结束事件处理
|
||||
private void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
|
||||
{
|
||||
string reason = e.Reason == SessionEndReasons.Logoff ? "用户注销" : "系统关机";
|
||||
WriteCrashLog($"系统会话即将结束: {reason}");
|
||||
}
|
||||
|
||||
// 新增:控制台取消事件处理
|
||||
private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
|
||||
{
|
||||
WriteCrashLog($"接收到控制台中断信号: {e.SpecialKey}");
|
||||
e.Cancel = true; // 取消默认处理
|
||||
}
|
||||
|
||||
// 新增:处理非UI线程的未处理异常
|
||||
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exception = e.ExceptionObject as Exception;
|
||||
string errorMessage = exception?.ToString() ?? "未知异常";
|
||||
lastErrorMessage = errorMessage;
|
||||
|
||||
WriteCrashLog($"捕获到未处理的异常: {errorMessage}");
|
||||
|
||||
if (e.IsTerminating)
|
||||
{
|
||||
WriteCrashLog("应用程序即将终止");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 尝试在最后时刻记录错误
|
||||
try
|
||||
{
|
||||
File.AppendAllText(
|
||||
Path.Combine(crashLogFile, $"critical_error_{DateTime.Now:yyyyMMdd_HHmmss}.log"),
|
||||
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 记录未处理异常时发生错误: {ex.Message}\r\n"
|
||||
);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:处理进程退出事件
|
||||
private void CurrentDomain_ProcessExit(object sender, EventArgs e)
|
||||
{
|
||||
TimeSpan runDuration = DateTime.Now - appStartTime;
|
||||
WriteCrashLog($"应用程序退出,运行时长: {runDuration}");
|
||||
|
||||
// 如果有最后错误消息,记录到日志
|
||||
if (!string.IsNullOrEmpty(lastErrorMessage))
|
||||
{
|
||||
WriteCrashLog($"最后错误信息: {lastErrorMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:记录崩溃日志
|
||||
private static void WriteCrashLog(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保目录存在
|
||||
if (!Directory.Exists(crashLogFile))
|
||||
{
|
||||
Directory.CreateDirectory(crashLogFile);
|
||||
}
|
||||
|
||||
string logFileName = Path.Combine(crashLogFile, $"crash_{DateTime.Now:yyyyMMdd}.log");
|
||||
|
||||
// 收集系统状态信息
|
||||
string memoryUsage = (Process.GetCurrentProcess().WorkingSet64 / (1024 * 1024)) + " MB";
|
||||
string cpuTime = Process.GetCurrentProcess().TotalProcessorTime.ToString();
|
||||
string processUptime = (DateTime.Now - Process.GetCurrentProcess().StartTime).ToString();
|
||||
|
||||
string statusInfo = $"[内存: {memoryUsage}, CPU时间: {cpuTime}, 运行时长: {processUptime}]";
|
||||
|
||||
// 写入日志
|
||||
File.AppendAllText(
|
||||
logFileName,
|
||||
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [PID:{currentProcessId}] {message}\r\n{statusInfo}\r\n\r\n"
|
||||
);
|
||||
|
||||
// 同时记录到主日志
|
||||
LogHelper.WriteLogToFile(message, LogHelper.LogType.Error);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// 增加字段保存崩溃后操作设置
|
||||
public static CrashActionType CrashAction = CrashActionType.SilentRestart;
|
||||
|
||||
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
|
||||
// 修正:允许静态调用
|
||||
public static void SyncCrashActionFromSettings()
|
||||
{
|
||||
Ink_Canvas.MainWindow.ShowNewMessage("抱歉,出现未预期的异常,可能导致 InkCanvasForClass 运行不稳定。\n建议保存墨迹后重启应用。", true);
|
||||
try
|
||||
{
|
||||
// 优先从 Settings.json 直接读取
|
||||
var settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Settings.json");
|
||||
if (File.Exists(settingsPath))
|
||||
{
|
||||
var json = File.ReadAllText(settingsPath);
|
||||
dynamic obj = JsonConvert.DeserializeObject(json);
|
||||
int crashAction = 0;
|
||||
try { crashAction = (int)(obj["startup"]["crashAction"] ?? 0); } catch { }
|
||||
CrashAction = (CrashActionType)crashAction;
|
||||
}
|
||||
// 兜底:从主窗口同步
|
||||
else if (Ink_Canvas.MainWindow.Settings != null && Ink_Canvas.MainWindow.Settings.Startup != null)
|
||||
{
|
||||
CrashAction = (CrashActionType)Ink_Canvas.MainWindow.Settings.Startup.CrashAction;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
Ink_Canvas.MainWindow.ShowNewMessage("抱歉,出现未预期的异常,可能导致 InkCanvasForClass 运行不稳定。\n建议保存墨迹后重启应用。");
|
||||
LogHelper.NewLog(e.Exception.ToString());
|
||||
|
||||
// 新增:记录到崩溃日志
|
||||
lastErrorMessage = e.Exception.ToString();
|
||||
WriteCrashLog($"UI线程未处理异常: {e.Exception}");
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
// 修改:仅当非用户主动退出时才触发自动重启
|
||||
SyncCrashActionFromSettings(); // 新增:崩溃时同步最新设置
|
||||
|
||||
if (CrashAction == CrashActionType.SilentRestart && !IsAppExitByUser)
|
||||
{
|
||||
StartupCount.Increment();
|
||||
if (StartupCount.GetCount() >= 5)
|
||||
{
|
||||
MessageBox.Show("检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。", "重启次数过多", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
StartupCount.Reset();
|
||||
Environment.Exit(1);
|
||||
}
|
||||
try
|
||||
{
|
||||
string exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
|
||||
System.Diagnostics.Process.Start(exePath);
|
||||
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
Process.Start(exePath, "-m");
|
||||
}
|
||||
catch { }
|
||||
Environment.Exit(1);
|
||||
@@ -71,19 +435,44 @@ namespace Ink_Canvas
|
||||
|
||||
void App_Startup(object sender, StartupEventArgs e)
|
||||
{
|
||||
RunWatchdogIfNeeded();
|
||||
/*if (!StoreHelper.IsStoreApp) */RootPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
|
||||
|
||||
LogHelper.NewLog(string.Format("Ink Canvas Starting (Version: {0})", Assembly.GetExecutingAssembly().GetName().Version.ToString()));
|
||||
LogHelper.NewLog(string.Format("Ink Canvas Starting (Version: {0})", Assembly.GetExecutingAssembly().GetName().Version));
|
||||
|
||||
// 在应用启动时自动释放IACore相关DLL
|
||||
try
|
||||
{
|
||||
Helpers.IACoreDllExtractor.ExtractIACoreDlls();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"释放IACore DLL时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
// 记录应用启动(设备标识符)
|
||||
DeviceIdentifier.RecordAppLaunch();
|
||||
LogHelper.WriteLogToFile($"App | 设备ID: {DeviceIdentifier.GetDeviceId()}");
|
||||
LogHelper.WriteLogToFile($"App | 使用频率: {DeviceIdentifier.GetUsageFrequency()}");
|
||||
LogHelper.WriteLogToFile($"App | 更新优先级: {DeviceIdentifier.GetUpdatePriority()}");
|
||||
|
||||
bool ret;
|
||||
mutex = new System.Threading.Mutex(true, "InkCanvasForClass", out ret);
|
||||
mutex = new Mutex(true, "InkCanvasForClass", out ret);
|
||||
|
||||
if (!ret && !e.Args.Contains("-m")) //-m multiple
|
||||
{
|
||||
LogHelper.NewLog("Detected existing instance");
|
||||
MessageBox.Show("已有一个程序实例正在运行");
|
||||
LogHelper.NewLog("Ink Canvas automatically closed");
|
||||
IsAppExitByUser = true; // 多开时标记为用户主动退出
|
||||
// 写入退出信号,确保看门狗不会重启
|
||||
try {
|
||||
StartupCount.Reset();
|
||||
File.WriteAllText(watchdogExitSignalFile, "exit");
|
||||
if (watchdogProcess != null && !watchdogProcess.HasExited)
|
||||
{
|
||||
watchdogProcess.Kill();
|
||||
}
|
||||
} catch { }
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
@@ -118,7 +507,7 @@ namespace Ink_Canvas
|
||||
|
||||
try
|
||||
{
|
||||
using (Microsoft.Win32.RegistryKey baseKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(regPath))
|
||||
using (RegistryKey baseKey = Registry.CurrentUser.OpenSubKey(regPath))
|
||||
{
|
||||
if (baseKey == null)
|
||||
{
|
||||
@@ -126,12 +515,12 @@ namespace Ink_Canvas
|
||||
// 尝试创建路径
|
||||
try
|
||||
{
|
||||
using (Microsoft.Win32.RegistryKey createKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(regPath, true))
|
||||
using (RegistryKey createKey = Registry.CurrentUser.CreateSubKey(regPath, true))
|
||||
{
|
||||
if (createKey != null)
|
||||
{
|
||||
createKey.SetValue("DisableProtectedView", 1, Microsoft.Win32.RegistryValueKind.DWord);
|
||||
LogHelper.WriteLogToFile($"创建并设置注册表路径: {regPath}", LogHelper.LogType.Info);
|
||||
createKey.SetValue("DisableProtectedView", 1, RegistryValueKind.DWord);
|
||||
LogHelper.WriteLogToFile($"创建并设置注册表路径: {regPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,11 +545,11 @@ namespace Ink_Canvas
|
||||
LogHelper.WriteLogToFile($"创建备份文件: {backupFile}");
|
||||
|
||||
// 使用UTF8编码写入注册表文件
|
||||
using (StreamWriter sw = new StreamWriter(backupFile, false, System.Text.Encoding.UTF8))
|
||||
using (StreamWriter sw = new StreamWriter(backupFile, false, Encoding.UTF8))
|
||||
{
|
||||
sw.WriteLine("Windows Registry Editor Version 5.00\n");
|
||||
sw.WriteLine();
|
||||
sw.WriteLine($"[{Microsoft.Win32.Registry.CurrentUser.Name}\\{regPath}]");
|
||||
sw.WriteLine($"[{Registry.CurrentUser.Name}\\{regPath}]");
|
||||
|
||||
foreach (string valueName in baseKey.GetValueNames())
|
||||
{
|
||||
@@ -170,13 +559,13 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(regPath, true))
|
||||
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(regPath, true))
|
||||
{
|
||||
// 仅在值不存在或不等于1时更新
|
||||
object currentValue = key.GetValue("DisableProtectedView");
|
||||
if (currentValue == null || (int)currentValue != 1)
|
||||
{
|
||||
key.SetValue("DisableProtectedView", 1, Microsoft.Win32.RegistryValueKind.DWord);
|
||||
key.SetValue("DisableProtectedView", 1, RegistryValueKind.DWord);
|
||||
LogHelper.WriteLogToFile($"Office {version} 注册表值已设置: DisableProtectedView = 1");
|
||||
}
|
||||
else
|
||||
@@ -189,7 +578,6 @@ namespace Ink_Canvas
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"处理Office版本 {version} 时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,21 +597,21 @@ namespace Ink_Canvas
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"未知错误: {ex.GetType().FullName} - {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.WriteLogToFile(ex.StackTrace, LogHelper.LogType.Info);
|
||||
LogHelper.WriteLogToFile(ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
private void ScrollViewer_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
|
||||
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (System.Windows.Forms.SystemInformation.MouseWheelScrollLines == -1)
|
||||
if (SystemInformation.MouseWheelScrollLines == -1)
|
||||
e.Handled = false;
|
||||
else
|
||||
try
|
||||
{
|
||||
ScrollViewerEx SenderScrollViewer = (ScrollViewerEx)sender;
|
||||
SenderScrollViewer.ScrollToVerticalOffset(SenderScrollViewer.VerticalOffset - e.Delta * 10 * System.Windows.Forms.SystemInformation.MouseWheelScrollLines / (double)120);
|
||||
SenderScrollViewer.ScrollToVerticalOffset(SenderScrollViewer.VerticalOffset - e.Delta * 10 * SystemInformation.MouseWheelScrollLines / (double)120);
|
||||
e.Handled = true;
|
||||
}
|
||||
catch { }
|
||||
@@ -253,12 +641,20 @@ namespace Ink_Canvas
|
||||
if ((DateTime.Now - lastHeartbeat).TotalSeconds > 10)
|
||||
{
|
||||
LogHelper.NewLog("检测到主线程无响应,自动重启。");
|
||||
SyncCrashActionFromSettings(); // 新增:心跳检测时同步最新设置
|
||||
if (CrashAction == CrashActionType.SilentRestart)
|
||||
{
|
||||
StartupCount.Increment();
|
||||
if (StartupCount.GetCount() >= 5)
|
||||
{
|
||||
MessageBox.Show("检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。", "重启次数过多", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
StartupCount.Reset();
|
||||
Environment.Exit(1);
|
||||
}
|
||||
try
|
||||
{
|
||||
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
Process.Start(exePath);
|
||||
Process.Start(exePath, "-m");
|
||||
}
|
||||
catch { }
|
||||
Environment.Exit(1);
|
||||
@@ -306,9 +702,21 @@ namespace Ink_Canvas
|
||||
}
|
||||
Thread.Sleep(2000);
|
||||
}
|
||||
// 主进程异常退出,自动重启
|
||||
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
Process.Start(exePath);
|
||||
// 主进程异常退出,自动重启前判断崩溃后操作
|
||||
SyncCrashActionFromSettings(); // 新增:同步设置
|
||||
if (CrashAction == CrashActionType.SilentRestart)
|
||||
{
|
||||
StartupCount.Increment();
|
||||
if (StartupCount.GetCount() >= 5)
|
||||
{
|
||||
MessageBox.Show("检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。", "重启次数过多", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
StartupCount.Reset();
|
||||
Environment.Exit(1);
|
||||
}
|
||||
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
Process.Start(exePath, "-m");
|
||||
}
|
||||
// CrashActionType.NoAction 时不重启,直接退出
|
||||
}
|
||||
catch { }
|
||||
Environment.Exit(0);
|
||||
@@ -320,9 +728,25 @@ namespace Ink_Canvas
|
||||
// 仅在软件内主动退出时关闭看门狗,并写入退出信号
|
||||
try
|
||||
{
|
||||
// 新增:记录应用退出状态
|
||||
string exitType = IsAppExitByUser ? "用户主动退出" : "应用程序退出";
|
||||
WriteCrashLog($"{exitType},退出代码: {e.ApplicationExitCode}");
|
||||
|
||||
// 记录应用退出(设备标识符)
|
||||
try
|
||||
{
|
||||
DeviceIdentifier.RecordAppExit();
|
||||
LogHelper.WriteLogToFile($"App | 应用运行时长: {(DateTime.Now - appStartTime).TotalMinutes:F1}分钟");
|
||||
}
|
||||
catch (Exception deviceEx)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"记录设备标识符退出信息失败: {deviceEx.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
if (IsAppExitByUser)
|
||||
{
|
||||
// 写入退出信号文件,通知看门狗正常退出
|
||||
StartupCount.Reset();
|
||||
File.WriteAllText(watchdogExitSignalFile, "exit");
|
||||
if (watchdogProcess != null && !watchdogProcess.HasExited)
|
||||
{
|
||||
@@ -330,7 +754,15 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 尝试记录最后的错误
|
||||
try
|
||||
{
|
||||
LogHelper.WriteLogToFile($"退出处理时发生错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -342,60 +774,60 @@ namespace Ink_Canvas
|
||||
{
|
||||
// 检查多个可能的注册表路径
|
||||
// 1. 检查传统的Office版本
|
||||
using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office"))
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office"))
|
||||
{
|
||||
if (key != null && key.GetSubKeyNames().Any(name => name.Contains(".0")))
|
||||
{
|
||||
LogHelper.WriteLogToFile("检测到传统Office安装", LogHelper.LogType.Info);
|
||||
LogHelper.WriteLogToFile("检测到传统Office安装");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 检查64位注册表中的Office
|
||||
using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software\\Wow6432Node\\Microsoft\\Office"))
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Wow6432Node\\Microsoft\\Office"))
|
||||
{
|
||||
if (key != null && key.GetSubKeyNames().Any(name => name.Contains(".0")))
|
||||
{
|
||||
LogHelper.WriteLogToFile("检测到64位注册表中的Office安装", LogHelper.LogType.Info);
|
||||
LogHelper.WriteLogToFile("检测到64位注册表中的Office安装");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 检查Office 365/Click-to-Run安装
|
||||
using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\ClickToRun"))
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\ClickToRun"))
|
||||
{
|
||||
if (key != null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("检测到Office 365 Click-to-Run", LogHelper.LogType.Info);
|
||||
LogHelper.WriteLogToFile("检测到Office 365 Click-to-Run");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 检查Office 365部署配置
|
||||
using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\15.0\\ClickToRun"))
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\15.0\\ClickToRun"))
|
||||
{
|
||||
if (key != null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("检测到Office 365 (15.0)", LogHelper.LogType.Info);
|
||||
LogHelper.WriteLogToFile("检测到Office 365 (15.0)");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\16.0\\ClickToRun"))
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\16.0\\ClickToRun"))
|
||||
{
|
||||
if (key != null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("检测到Office 365 (16.0)", LogHelper.LogType.Info);
|
||||
LogHelper.WriteLogToFile("检测到Office 365 (16.0)");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 检查Office 365零售订阅信息
|
||||
using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\ClickToRun\\Configuration"))
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\ClickToRun\\Configuration"))
|
||||
{
|
||||
if (key != null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("检测到Office 365配置", LogHelper.LogType.Info);
|
||||
LogHelper.WriteLogToFile("检测到Office 365配置");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -417,7 +849,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
const string message = "需要管理员权限才能完成此操作\n请以管理员身份重新启动应用程序";
|
||||
LogHelper.WriteLogToFile(message, LogHelper.LogType.Error);
|
||||
System.Windows.MessageBox.Show(message, "权限错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
MessageBox.Show(message, "权限错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -642,11 +1074,11 @@ namespace Ink_Canvas
|
||||
LogHelper.WriteLogToFile($"创建Office 365 {app}备份文件: {backupFile}");
|
||||
|
||||
// 使用UTF8编码写入注册表文件
|
||||
using (StreamWriter sw = new StreamWriter(backupFile, false, System.Text.Encoding.UTF8))
|
||||
using (StreamWriter sw = new StreamWriter(backupFile, false, Encoding.UTF8))
|
||||
{
|
||||
sw.WriteLine("Windows Registry Editor Version 5.00\n");
|
||||
sw.WriteLine();
|
||||
sw.WriteLine($"[{Microsoft.Win32.Registry.CurrentUser.Name}\\{regPath}]");
|
||||
sw.WriteLine($"[{Registry.CurrentUser.Name}\\{regPath}]");
|
||||
|
||||
foreach (string valueName in baseKey.GetValueNames())
|
||||
{
|
||||
@@ -698,11 +1130,11 @@ namespace Ink_Canvas
|
||||
LogHelper.WriteLogToFile($"创建信任中心备份文件: {backupFile}");
|
||||
|
||||
// 使用UTF8编码写入注册表文件
|
||||
using (StreamWriter sw = new StreamWriter(backupFile, false, System.Text.Encoding.UTF8))
|
||||
using (StreamWriter sw = new StreamWriter(backupFile, false, Encoding.UTF8))
|
||||
{
|
||||
sw.WriteLine("Windows Registry Editor Version 5.00\n");
|
||||
sw.WriteLine();
|
||||
sw.WriteLine($"[{Microsoft.Win32.Registry.CurrentUser.Name}\\{trustCenterPath}]");
|
||||
sw.WriteLine($"[{Registry.CurrentUser.Name}\\{trustCenterPath}]");
|
||||
|
||||
foreach (string valueName in baseKey.GetValueNames())
|
||||
{
|
||||
@@ -742,11 +1174,11 @@ namespace Ink_Canvas
|
||||
LogHelper.WriteLogToFile($"创建策略备份文件: {backupFile}");
|
||||
|
||||
// 使用UTF8编码写入注册表文件
|
||||
using (StreamWriter sw = new StreamWriter(backupFile, false, System.Text.Encoding.UTF8))
|
||||
using (StreamWriter sw = new StreamWriter(backupFile, false, Encoding.UTF8))
|
||||
{
|
||||
sw.WriteLine("Windows Registry Editor Version 5.00\n");
|
||||
sw.WriteLine();
|
||||
sw.WriteLine($"[{Microsoft.Win32.Registry.CurrentUser.Name}\\{policyPath}]");
|
||||
sw.WriteLine($"[{Registry.CurrentUser.Name}\\{policyPath}]");
|
||||
|
||||
foreach (string valueName in baseKey.GetValueNames())
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
@@ -49,5 +49,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.6.3.0")]
|
||||
[assembly: AssemblyFileVersion("1.6.3.0")]
|
||||
[assembly: AssemblyVersion("1.7.5.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.5.0")]
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<Costura ExcludeAssemblies="IACore|IALoader|IAWinFX" />
|
||||
</Weavers>
|
||||
@@ -0,0 +1,176 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
|
||||
<xs:element name="Weavers">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeRuntimeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="IncludeRuntimeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Obsolete, use UnmanagedWinX86Assemblies instead</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="UnmanagedWinX86Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged X86 (32 bit) assembly names to include, delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Obsolete, use UnmanagedWinX64Assemblies instead.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="UnmanagedWinX64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged X64 (64 bit) assembly names to include, delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="UnmanagedWinArm64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged Arm64 (64 bit) assembly names to include, delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IncludeRuntimeReferences" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Controls if runtime assemblies are also embedded.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UseRuntimeReferencePaths" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="DisableCompression" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="DisableCleanup" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="DisableEventSubscription" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The attach method no longer subscribes to the `AppDomain.AssemblyResolve` (.NET 4.x) and `AssemblyLoadContext.Resolving` (.NET 6.0+) events.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="ExcludeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IncludeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="ExcludeRuntimeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IncludeRuntimeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Obsolete, use UnmanagedWinX86Assemblies instead</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UnmanagedWinX86Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged X86 (32 bit) assembly names to include, delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Obsolete, use UnmanagedWinX64Assemblies instead</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UnmanagedWinX64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged X64 (64 bit) assembly names to include, delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UnmanagedWinArm64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged Arm64 (64 bit) assembly names to include, delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="PreloadOrder" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="VerifyAssembly" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="GenerateXsd" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,562 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步硬件加速的墨迹平滑处理器
|
||||
/// </summary>
|
||||
public class AsyncAdvancedBezierSmoothing
|
||||
{
|
||||
private readonly SemaphoreSlim _processingSemaphore;
|
||||
private readonly ConcurrentDictionary<Stroke, CancellationTokenSource> _processingTasks;
|
||||
private readonly Dispatcher _uiDispatcher;
|
||||
|
||||
public AsyncAdvancedBezierSmoothing(Dispatcher uiDispatcher)
|
||||
{
|
||||
_uiDispatcher = uiDispatcher;
|
||||
_processingSemaphore = new SemaphoreSlim(Environment.ProcessorCount, Environment.ProcessorCount);
|
||||
_processingTasks = new ConcurrentDictionary<Stroke, CancellationTokenSource>();
|
||||
}
|
||||
|
||||
public double SmoothingStrength { get; set; } = 0.3; // 大幅降低强度
|
||||
public double ResampleInterval { get; set; } = 3.0; // 大幅增加间隔减少点数
|
||||
public int InterpolationSteps { get; set; } = 4; // 极大减少插值步数
|
||||
public bool UseHardwareAcceleration { get; set; } = true;
|
||||
public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount;
|
||||
|
||||
/// <summary>
|
||||
/// 异步平滑笔画
|
||||
/// </summary>
|
||||
public async Task<Stroke> SmoothStrokeAsync(Stroke originalStroke,
|
||||
Action<Stroke, Stroke> onCompleted = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (originalStroke == null || originalStroke.StylusPoints.Count < 2)
|
||||
return originalStroke;
|
||||
|
||||
// 取消之前对同一笔画的处理
|
||||
if (_processingTasks.TryGetValue(originalStroke, out var existingCts))
|
||||
{
|
||||
existingCts.Cancel();
|
||||
_processingTasks.TryRemove(originalStroke, out _);
|
||||
}
|
||||
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
_processingTasks[originalStroke] = cts;
|
||||
|
||||
try
|
||||
{
|
||||
await _processingSemaphore.WaitAsync(cts.Token);
|
||||
|
||||
var smoothedStroke = await Task.Run(() =>
|
||||
ProcessStrokeInternal(originalStroke, cts.Token), cts.Token);
|
||||
|
||||
// 在UI线程上执行回调
|
||||
if (onCompleted != null && !cts.Token.IsCancellationRequested)
|
||||
{
|
||||
await _uiDispatcher.InvokeAsync(() => onCompleted(originalStroke, smoothedStroke));
|
||||
}
|
||||
|
||||
return smoothedStroke;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return originalStroke;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_processingSemaphore.Release();
|
||||
_processingTasks.TryRemove(originalStroke, out _);
|
||||
cts.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private Stroke ProcessStrokeInternal(Stroke stroke, CancellationToken cancellationToken)
|
||||
{
|
||||
var originalPoints = stroke.StylusPoints.ToArray();
|
||||
|
||||
// 如果点数太少,直接返回原始笔画
|
||||
if (originalPoints.Length < 3)
|
||||
return stroke;
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// 简化处理:只进行轻度平滑,避免点数爆炸
|
||||
var smoothedPoints = ApplyLightSmoothing(originalPoints);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// 确保点数不会过多
|
||||
if (smoothedPoints.Length > originalPoints.Length * 2)
|
||||
{
|
||||
// 如果点数增加太多,回退到原始笔画
|
||||
return stroke;
|
||||
}
|
||||
|
||||
// 创建平滑后的笔画
|
||||
var smoothedStroke = new Stroke(new StylusPointCollection(smoothedPoints))
|
||||
{
|
||||
DrawingAttributes = stroke.DrawingAttributes.Clone()
|
||||
};
|
||||
|
||||
return smoothedStroke;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轻度平滑处理,避免点数爆炸
|
||||
/// </summary>
|
||||
private StylusPoint[] ApplyLightSmoothing(StylusPoint[] points)
|
||||
{
|
||||
if (points.Length < 3) return points;
|
||||
|
||||
var result = new List<StylusPoint>();
|
||||
result.Add(points[0]); // 保持第一个点
|
||||
|
||||
// 简单的3点平均平滑
|
||||
for (int i = 1; i < points.Length - 1; i++)
|
||||
{
|
||||
var prev = points[i - 1];
|
||||
var curr = points[i];
|
||||
var next = points[i + 1];
|
||||
|
||||
// 3点平均
|
||||
double x = (prev.X + curr.X + next.X) / 3.0;
|
||||
double y = (prev.Y + curr.Y + next.Y) / 3.0;
|
||||
float pressure = (prev.PressureFactor + curr.PressureFactor + next.PressureFactor) / 3.0f;
|
||||
|
||||
result.Add(new StylusPoint(x, y, Math.Max(pressure, 0.1f)));
|
||||
}
|
||||
|
||||
result.Add(points[points.Length - 1]); // 保持最后一个点
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
/// <summary>
|
||||
/// 硬件加速的向量化指数平滑
|
||||
/// </summary>
|
||||
private StylusPoint[] ApplyExponentialSmoothingVectorized(StylusPoint[] points, double alpha)
|
||||
{
|
||||
if (points.Length == 0) return points;
|
||||
|
||||
var result = new StylusPoint[points.Length];
|
||||
result[0] = points[0];
|
||||
|
||||
double lastX = points[0].X;
|
||||
double lastY = points[0].Y;
|
||||
float lastPressure = points[0].PressureFactor;
|
||||
double oneMinusAlpha = 1.0 - alpha;
|
||||
|
||||
// 向量化处理,减少分支预测失败
|
||||
for (int i = 1; i < points.Length; i++)
|
||||
{
|
||||
var p = points[i];
|
||||
lastX = alpha * p.X + oneMinusAlpha * lastX;
|
||||
lastY = alpha * p.Y + oneMinusAlpha * lastY;
|
||||
lastPressure = (float)(alpha * p.PressureFactor + oneMinusAlpha * lastPressure);
|
||||
lastPressure = Math.Max(lastPressure, 0.1f); // 避免分支
|
||||
result[i] = new StylusPoint(lastX, lastY, lastPressure);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 优化的等距重采样
|
||||
/// </summary>
|
||||
private StylusPoint[] ResampleEquidistantOptimized(StylusPoint[] points, double interval)
|
||||
{
|
||||
if (points.Length == 0) return points;
|
||||
|
||||
var result = new List<StylusPoint>(points.Length) { points[0] };
|
||||
double accumulated = 0;
|
||||
|
||||
for (int i = 1; i < points.Length; i++)
|
||||
{
|
||||
var prev = result[result.Count - 1];
|
||||
var curr = points[i];
|
||||
double dx = curr.X - prev.X;
|
||||
double dy = curr.Y - prev.Y;
|
||||
double dist = Math.Sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (dist + accumulated >= interval)
|
||||
{
|
||||
double t = (interval - accumulated) / dist;
|
||||
double x = prev.X + t * dx;
|
||||
double y = prev.Y + t * dy;
|
||||
float pressure = (float)(prev.PressureFactor * (1 - t) + curr.PressureFactor * t);
|
||||
pressure = Math.Max(pressure, 0.1f);
|
||||
|
||||
result.Add(new StylusPoint(x, y, pressure));
|
||||
accumulated = 0;
|
||||
i--; // 重新处理当前点
|
||||
}
|
||||
else
|
||||
{
|
||||
accumulated += dist;
|
||||
}
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 硬件加速的贝塞尔曲线拟合
|
||||
/// </summary>
|
||||
private StylusPoint[] SlidingBezierFitHardwareAccelerated(StylusPoint[] points, int window, int steps)
|
||||
{
|
||||
if (points.Length < window) return points;
|
||||
|
||||
var result = new List<StylusPoint>(points.Length * steps / window);
|
||||
|
||||
// 使用并行处理加速计算
|
||||
var segments = new List<StylusPoint[]>();
|
||||
|
||||
Parallel.For(0, points.Length - window + 1, i =>
|
||||
{
|
||||
var segmentPoints = new StylusPoint[steps];
|
||||
var p0 = points[i];
|
||||
var p1 = points[i + 1];
|
||||
var p2 = points[i + 2];
|
||||
var p3 = points[i + 3];
|
||||
|
||||
for (int j = 0; j < steps; j++)
|
||||
{
|
||||
double t = (double)j / steps;
|
||||
segmentPoints[j] = CubicBezierOptimized(p0, p1, p2, p3, t);
|
||||
}
|
||||
|
||||
lock (segments)
|
||||
{
|
||||
segments.Add(segmentPoints);
|
||||
}
|
||||
});
|
||||
|
||||
// 合并结果
|
||||
foreach (var segment in segments)
|
||||
{
|
||||
result.AddRange(segment);
|
||||
}
|
||||
|
||||
result.Add(points[points.Length - 1]);
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 优化的单线程贝塞尔拟合
|
||||
/// </summary>
|
||||
private StylusPoint[] SlidingBezierFitOptimized(StylusPoint[] points, int window, int steps)
|
||||
{
|
||||
if (points.Length < window) return points;
|
||||
|
||||
var result = new List<StylusPoint>(points.Length * steps / window);
|
||||
|
||||
for (int i = 0; i <= points.Length - window; i++)
|
||||
{
|
||||
var p0 = points[i];
|
||||
var p1 = points[i + 1];
|
||||
var p2 = points[i + 2];
|
||||
var p3 = points[i + 3];
|
||||
|
||||
for (int j = 0; j < steps; j++)
|
||||
{
|
||||
double t = (double)j / steps;
|
||||
result.Add(CubicBezierOptimized(p0, p1, p2, p3, t));
|
||||
}
|
||||
}
|
||||
|
||||
result.Add(points[points.Length - 1]);
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 优化的三次贝塞尔曲线计算
|
||||
/// </summary>
|
||||
private StylusPoint CubicBezierOptimized(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3, double t)
|
||||
{
|
||||
double u = 1 - t;
|
||||
double tt = t * t;
|
||||
double uu = u * u;
|
||||
double uuu = uu * u;
|
||||
double ttt = tt * t;
|
||||
|
||||
// 预计算系数
|
||||
double c0 = uuu;
|
||||
double c1 = 3 * uu * t;
|
||||
double c2 = 3 * u * tt;
|
||||
double c3 = ttt;
|
||||
|
||||
double x = c0 * p0.X + c1 * p1.X + c2 * p2.X + c3 * p3.X;
|
||||
double y = c0 * p0.Y + c1 * p1.Y + c2 * p2.Y + c3 * p3.Y;
|
||||
float pressure = (float)(p1.PressureFactor * u + p2.PressureFactor * t);
|
||||
pressure = Math.Max(pressure, 0.1f);
|
||||
|
||||
return new StylusPoint(x, y, pressure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 兼容性方法:传统指数平滑
|
||||
/// </summary>
|
||||
private StylusPoint[] ApplyExponentialSmoothing(StylusPoint[] points, double alpha)
|
||||
{
|
||||
if (points.Length == 0) return points;
|
||||
|
||||
var result = new StylusPoint[points.Length];
|
||||
result[0] = points[0];
|
||||
|
||||
double lastX = points[0].X;
|
||||
double lastY = points[0].Y;
|
||||
float lastPressure = points[0].PressureFactor;
|
||||
|
||||
for (int i = 1; i < points.Length; i++)
|
||||
{
|
||||
var p = points[i];
|
||||
lastX = alpha * p.X + (1 - alpha) * lastX;
|
||||
lastY = alpha * p.Y + (1 - alpha) * lastY;
|
||||
lastPressure = (float)(alpha * p.PressureFactor + (1 - alpha) * lastPressure);
|
||||
lastPressure = Math.Max(lastPressure, 0.1f);
|
||||
result[i] = new StylusPoint(lastX, lastY, lastPressure);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消所有正在进行的处理任务
|
||||
/// </summary>
|
||||
public void CancelAllTasks()
|
||||
{
|
||||
foreach (var kvp in _processingTasks)
|
||||
{
|
||||
kvp.Value.Cancel();
|
||||
}
|
||||
_processingTasks.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放资源
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
CancelAllTasks();
|
||||
_processingSemaphore?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 原有的同步版本(保持向后兼容)
|
||||
/// </summary>
|
||||
public class AdvancedBezierSmoothing
|
||||
{
|
||||
public double SmoothingStrength { get; set; } = 0.3;
|
||||
public double ResampleInterval { get; set; } = 3.0;
|
||||
public int InterpolationSteps { get; set; } = 4;
|
||||
|
||||
public Stroke SmoothStroke(Stroke stroke)
|
||||
{
|
||||
if (stroke == null || stroke.StylusPoints.Count < 3)
|
||||
return stroke;
|
||||
|
||||
var originalPoints = stroke.StylusPoints.ToList();
|
||||
|
||||
// 简化处理:只进行轻度平滑
|
||||
var smoothedPoints = ApplyLightExponentialSmoothing(originalPoints, 0.2); // 很轻的平滑
|
||||
|
||||
// 检查点数是否合理
|
||||
if (smoothedPoints.Count > originalPoints.Count * 1.5)
|
||||
{
|
||||
return stroke; // 如果点数增加太多,返回原始笔画
|
||||
}
|
||||
|
||||
var smoothedStroke = new Stroke(new StylusPointCollection(smoothedPoints))
|
||||
{
|
||||
DrawingAttributes = stroke.DrawingAttributes.Clone()
|
||||
};
|
||||
return smoothedStroke;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轻度指数平滑
|
||||
/// </summary>
|
||||
private List<StylusPoint> ApplyLightExponentialSmoothing(List<StylusPoint> points, double alpha)
|
||||
{
|
||||
var result = new List<StylusPoint>();
|
||||
if (points.Count == 0) return result;
|
||||
|
||||
result.Add(points[0]);
|
||||
|
||||
for (int i = 1; i < points.Count; i++)
|
||||
{
|
||||
var prev = result[result.Count - 1];
|
||||
var curr = points[i];
|
||||
|
||||
double x = alpha * curr.X + (1 - alpha) * prev.X;
|
||||
double y = alpha * curr.Y + (1 - alpha) * prev.Y;
|
||||
float pressure = (float)(alpha * curr.PressureFactor + (1 - alpha) * prev.PressureFactor);
|
||||
pressure = Math.Max(pressure, 0.1f);
|
||||
|
||||
result.Add(new StylusPoint(x, y, pressure));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<StylusPoint> ApplyExponentialSmoothing(List<StylusPoint> points, double alpha)
|
||||
{
|
||||
var result = new List<StylusPoint>();
|
||||
if (points.Count == 0) return result;
|
||||
result.Add(points[0]);
|
||||
double lastX = points[0].X;
|
||||
double lastY = points[0].Y;
|
||||
float lastPressure = points[0].PressureFactor;
|
||||
for (int i = 1; i < points.Count; i++)
|
||||
{
|
||||
var p = points[i];
|
||||
lastX = alpha * p.X + (1 - alpha) * lastX;
|
||||
lastY = alpha * p.Y + (1 - alpha) * lastY;
|
||||
lastPressure = (float)(alpha * p.PressureFactor + (1 - alpha) * lastPressure);
|
||||
if (lastPressure < 0.1f) lastPressure = 0.1f;
|
||||
result.Add(new StylusPoint(lastX, lastY, lastPressure));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<StylusPoint> ResampleEquidistant(List<StylusPoint> points, double interval = 2.0)
|
||||
{
|
||||
var result = new List<StylusPoint>();
|
||||
if (points.Count == 0) return result;
|
||||
result.Add(points[0]);
|
||||
double accumulated = 0;
|
||||
for (int i = 1; i < points.Count; i++)
|
||||
{
|
||||
var prev = result.Last();
|
||||
var curr = points[i];
|
||||
double dx = curr.X - prev.X;
|
||||
double dy = curr.Y - prev.Y;
|
||||
double dist = Math.Sqrt(dx * dx + dy * dy);
|
||||
if (dist + accumulated >= interval)
|
||||
{
|
||||
double t = (interval - accumulated) / dist;
|
||||
double x = prev.X + t * dx;
|
||||
double y = prev.Y + t * dy;
|
||||
float pressure = (float)(prev.PressureFactor * (1 - t) + curr.PressureFactor * t);
|
||||
if (pressure < 0.1f) pressure = 0.1f;
|
||||
var newPoint = new StylusPoint(x, y, pressure);
|
||||
result.Add(newPoint);
|
||||
accumulated = 0;
|
||||
i--; // 重新处理当前点
|
||||
}
|
||||
else
|
||||
{
|
||||
accumulated += dist;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<StylusPoint> SlidingBezierFit(List<StylusPoint> points, int window = 4, int steps = 24)
|
||||
{
|
||||
var result = new List<StylusPoint>();
|
||||
if (points.Count < window) return points;
|
||||
for (int i = 0; i <= points.Count - window; i++)
|
||||
{
|
||||
var p0 = points[i];
|
||||
var p1 = points[i + 1];
|
||||
var p2 = points[i + 2];
|
||||
var p3 = points[i + 3];
|
||||
for (int j = 0; j < steps; j++)
|
||||
{
|
||||
double t = (double)j / steps;
|
||||
var pt = CubicBezier(p0, p1, p2, p3, t);
|
||||
result.Add(pt);
|
||||
}
|
||||
}
|
||||
// 保证最后一个点被包含
|
||||
result.Add(points.Last());
|
||||
return result;
|
||||
}
|
||||
|
||||
private StylusPoint CubicBezier(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3, double t)
|
||||
{
|
||||
double u = 1 - t;
|
||||
double tt = t * t;
|
||||
double uu = u * u;
|
||||
double uuu = uu * u;
|
||||
double ttt = tt * t;
|
||||
double x = uuu * p0.X + 3 * uu * t * p1.X + 3 * u * tt * p2.X + ttt * p3.X;
|
||||
double y = uuu * p0.Y + 3 * uu * t * p1.Y + 3 * u * tt * p2.Y + ttt * p3.Y;
|
||||
float pressure = (float)(p1.PressureFactor * (1 - t) + p2.PressureFactor * t);
|
||||
if (pressure < 0.1f) pressure = 0.1f;
|
||||
return new StylusPoint(x, y, pressure);
|
||||
}
|
||||
|
||||
private List<StylusPoint> SlidingWindowSmooth(List<StylusPoint> points, int window = 5)
|
||||
{
|
||||
var result = new List<StylusPoint>();
|
||||
int half = window / 2;
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
{
|
||||
double sumX = 0, sumY = 0, sumP = 0;
|
||||
int count = 0;
|
||||
for (int j = Math.Max(0, i - half); j <= Math.Min(points.Count - 1, i + half); j++)
|
||||
{
|
||||
sumX += points[j].X;
|
||||
sumY += points[j].Y;
|
||||
sumP += points[j].PressureFactor;
|
||||
count++;
|
||||
}
|
||||
result.Add(new StylusPoint(sumX / count, sumY / count, (float)(sumP / count)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 性能监控器
|
||||
/// </summary>
|
||||
public class InkSmoothingPerformanceMonitor
|
||||
{
|
||||
private readonly Queue<TimeSpan> _processingTimes = new Queue<TimeSpan>();
|
||||
private readonly object _lock = new object();
|
||||
private const int MaxSamples = 100;
|
||||
|
||||
public void RecordProcessingTime(TimeSpan time)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_processingTimes.Enqueue(time);
|
||||
if (_processingTimes.Count > MaxSamples)
|
||||
_processingTimes.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public double GetAverageProcessingTimeMs()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _processingTimes.Count > 0 ?
|
||||
_processingTimes.Average(t => t.TotalMilliseconds) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public double GetMaxProcessingTimeMs()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _processingTimes.Count > 0 ?
|
||||
_processingTimes.Max(t => t.TotalMilliseconds) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetSampleCount()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _processingTimes.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
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(
|
||||
@@ -17,7 +17,7 @@ namespace Ink_Canvas.Helpers
|
||||
typeof(bool),
|
||||
typeof(AvoidFullScreenHelper));
|
||||
|
||||
private static bool _isBoardMode = false;
|
||||
private static bool _isBoardMode;
|
||||
public static void SetBoardMode(bool isBoardMode)
|
||||
{
|
||||
_isBoardMode = isBoardMode;
|
||||
@@ -121,10 +121,10 @@ 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)
|
||||
@@ -145,7 +145,7 @@ namespace Ink_Canvas.Helpers
|
||||
|
||||
// 如果没找到,使用主显示器
|
||||
if (targetScreen == null)
|
||||
targetScreen = System.Windows.Forms.Screen.PrimaryScreen;
|
||||
targetScreen = Screen.PrimaryScreen;
|
||||
|
||||
return new Rect(
|
||||
targetScreen.WorkingArea.X,
|
||||
|
||||
@@ -9,125 +9,107 @@ namespace Ink_Canvas.Converter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if ((bool)value == true)
|
||||
if ((bool)value)
|
||||
{
|
||||
return Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if ((bool)value == true)
|
||||
if ((bool)value)
|
||||
{
|
||||
return Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
public class VisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
Visibility visibility = (Visibility)value;
|
||||
if (visibility == Visibility.Visible)
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Visibility.Visible;
|
||||
}
|
||||
|
||||
return Visibility.Visible;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
Visibility visibility = (Visibility)value;
|
||||
if (visibility == Visibility.Visible)
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Visibility.Visible;
|
||||
}
|
||||
|
||||
return Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
public class IntNumberToString : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if ((double)value == 0)
|
||||
{
|
||||
return "无限制";
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((double)value).ToString() + "人";
|
||||
}
|
||||
|
||||
return ((double)value) + "人";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if ((double)value == 0)
|
||||
{
|
||||
return "无限制";
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((double)value).ToString() + "人";
|
||||
}
|
||||
|
||||
return ((double)value) + "人";
|
||||
}
|
||||
}
|
||||
|
||||
public class IntNumberToString2 : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if ((double)value == 0)
|
||||
{
|
||||
return "自动截图";
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((double)value).ToString() + "条";
|
||||
}
|
||||
|
||||
return ((double)value) + "条";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if ((double)value == 0)
|
||||
{
|
||||
return "自动截图";
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((double)value).ToString() + "条";
|
||||
}
|
||||
|
||||
return ((double)value) + "条";
|
||||
}
|
||||
}
|
||||
|
||||
public class IsEnabledToOpacityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
bool isChecked = (bool)value;
|
||||
if (isChecked == true)
|
||||
if (isChecked)
|
||||
{
|
||||
return 1d;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.35;
|
||||
}
|
||||
|
||||
return 0.35;
|
||||
}
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); }
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
||||
namespace Ink_Canvas.Helpers {
|
||||
internal class DelAutoSavedFiles {
|
||||
@@ -27,14 +26,14 @@ namespace Ink_Canvas.Helpers {
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex.ToString(), LogHelper.LogType.Error);
|
||||
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex, LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
try { // 递归删除空文件夹
|
||||
DeleteEmptyFolders(directoryPath);
|
||||
} catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex.ToString(), LogHelper.LogType.Error);
|
||||
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex, LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
@@ -70,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;
|
||||
@@ -115,7 +116,7 @@ namespace Ink_Canvas.Helpers
|
||||
case VarEnum.VT_BLOB:
|
||||
return GetBlob();
|
||||
}
|
||||
throw new NotImplementedException("PropVariant " + ve.ToString());
|
||||
throw new NotImplementedException("PropVariant " + ve);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,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)]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
@@ -23,44 +24,6 @@ namespace Ink_Canvas.Helpers
|
||||
[DllImport("user32.dll")]
|
||||
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
[DllImport("shell32.dll")]
|
||||
private static extern IntPtr SHAppBarMessage(uint dwMessage, ref APPBARDATA pData);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int SystemParametersInfo(uint uiAction, uint uiParam, IntPtr pvParam, uint fWinIni);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
|
||||
|
||||
private const uint ABM_GETTASKBARPOS = 0x00000005;
|
||||
private const uint ABM_GETSTATE = 0x00000004;
|
||||
private const int SPI_GETWORKAREA = 0x0030;
|
||||
private const uint MONITOR_DEFAULTTOPRIMARY = 1;
|
||||
private const int ABS_AUTOHIDE = 0x0000001;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct MONITORINFO
|
||||
{
|
||||
public int cbSize;
|
||||
public RECT rcMonitor;
|
||||
public RECT rcWork;
|
||||
public uint dwFlags;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct APPBARDATA
|
||||
{
|
||||
public int cbSize;
|
||||
public IntPtr hWnd;
|
||||
public uint uCallbackMessage;
|
||||
public uint uEdge;
|
||||
public RECT rc;
|
||||
public IntPtr lParam;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT {
|
||||
public int Left;
|
||||
@@ -72,72 +35,6 @@ namespace Ink_Canvas.Helpers
|
||||
public int Height => Bottom - Top;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Windows任务栏的高度(仅计算任务栏,不包括其他应用的停靠栏)
|
||||
/// </summary>
|
||||
/// <param name="screen">当前屏幕</param>
|
||||
/// <param name="dpiScaleY">DPI缩放Y值</param>
|
||||
/// <returns>任务栏高度</returns>
|
||||
public static double GetTaskbarHeight(System.Windows.Forms.Screen screen, double dpiScaleY)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建APPBARDATA结构
|
||||
var abd = new APPBARDATA();
|
||||
abd.cbSize = Marshal.SizeOf(abd);
|
||||
|
||||
// 获取任务栏状态
|
||||
IntPtr state = SHAppBarMessage(ABM_GETSTATE, ref abd);
|
||||
bool isAutoHide = (state.ToInt32() & ABS_AUTOHIDE) == ABS_AUTOHIDE;
|
||||
|
||||
// 如果任务栏是自动隐藏的,返回0
|
||||
if (isAutoHide)
|
||||
{
|
||||
LogHelper.WriteLogToFile("任务栏处于自动隐藏状态", LogHelper.LogType.Info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 获取任务栏信息
|
||||
IntPtr result = SHAppBarMessage(ABM_GETTASKBARPOS, ref abd);
|
||||
if (result != IntPtr.Zero)
|
||||
{
|
||||
// 获取当前屏幕的工作区
|
||||
RECT workArea = new RECT();
|
||||
SystemParametersInfo(SPI_GETWORKAREA, 0, Marshal.AllocHGlobal(Marshal.SizeOf(workArea)), 0);
|
||||
|
||||
// 根据任务栏位置计算高度
|
||||
int taskbarHeight = 0;
|
||||
|
||||
// 任务栏的uEdge: 0=左, 1=上, 2=右, 3=下
|
||||
switch (abd.uEdge)
|
||||
{
|
||||
case 1: // 上
|
||||
taskbarHeight = abd.rc.Height;
|
||||
break;
|
||||
case 3: // 下
|
||||
taskbarHeight = abd.rc.Height;
|
||||
break;
|
||||
case 0: // 左
|
||||
case 2: // 右
|
||||
// 水平任务栏不影响高度
|
||||
taskbarHeight = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// 考虑DPI缩放
|
||||
return taskbarHeight / dpiScaleY;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"获取任务栏高度出错: {ex.Message}");
|
||||
LogHelper.WriteLogToFile($"获取任务栏高度出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
// 如果获取失败,回退到通用方法
|
||||
return (screen.Bounds.Height - screen.WorkingArea.Height) / dpiScaleY;
|
||||
}
|
||||
|
||||
public static string WindowTitle() {
|
||||
IntPtr foregroundWindowHandle = GetForegroundWindow();
|
||||
|
||||
@@ -197,5 +94,15 @@ namespace Ink_Canvas.Helpers
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,10 +278,7 @@ namespace Ink_Canvas.Helpers
|
||||
window.Width = logicalSize.X;
|
||||
window.Height = logicalSize.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
//这个hwnd是前面从Window来的,如果现在他不是Window...... 你信么
|
||||
}
|
||||
//这个hwnd是前面从Window来的,如果现在他不是Window...... 你信么
|
||||
}
|
||||
|
||||
//将修改后的结构体拷贝回去
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
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 readonly DrawingContext _drawingContext;
|
||||
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 += 3)
|
||||
{
|
||||
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 = 16)
|
||||
{
|
||||
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()
|
||||
{
|
||||
_drawingContext?.Close();
|
||||
_renderTarget?.Clear();
|
||||
_isInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 质量配置枚举
|
||||
/// </summary>
|
||||
public enum InkSmoothingQuality
|
||||
{
|
||||
HighPerformance = 0, // 高性能低质量
|
||||
Balanced = 1, // 平衡
|
||||
HighQuality = 2 // 高质量低性能
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 墨迹平滑配置
|
||||
/// </summary>
|
||||
public class InkSmoothingConfig
|
||||
{
|
||||
public InkSmoothingQuality Quality { get; set; } = InkSmoothingQuality.Balanced;
|
||||
public bool UseHardwareAcceleration { get; set; } = true;
|
||||
public bool UseAsyncProcessing { get; set; } = true;
|
||||
public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount;
|
||||
public double SmoothingStrength { get; set; } = 0.6;
|
||||
public double ResampleInterval { get; set; } = 1.2;
|
||||
public int InterpolationSteps { get; set; } = 16;
|
||||
|
||||
public static InkSmoothingConfig FromSettings()
|
||||
{
|
||||
return new InkSmoothingConfig
|
||||
{
|
||||
Quality = (InkSmoothingQuality)MainWindow.Settings.Canvas.InkSmoothingQuality,
|
||||
UseHardwareAcceleration = MainWindow.Settings.Canvas.UseHardwareAcceleration,
|
||||
UseAsyncProcessing = MainWindow.Settings.Canvas.UseAsyncInkSmoothing,
|
||||
MaxConcurrentTasks = MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ?
|
||||
MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount
|
||||
};
|
||||
}
|
||||
|
||||
public void ApplyQualitySettings()
|
||||
{
|
||||
switch (Quality)
|
||||
{
|
||||
case InkSmoothingQuality.HighPerformance:
|
||||
SmoothingStrength = 0.4;
|
||||
ResampleInterval = 2.0;
|
||||
InterpolationSteps = 8;
|
||||
break;
|
||||
case InkSmoothingQuality.Balanced:
|
||||
SmoothingStrength = 0.6;
|
||||
ResampleInterval = 1.2;
|
||||
InterpolationSteps = 16;
|
||||
break;
|
||||
case InkSmoothingQuality.HighQuality:
|
||||
SmoothingStrength = 0.8;
|
||||
ResampleInterval = 0.8;
|
||||
InterpolationSteps = 32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
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 >= 8 && isHardwareAccelerated)
|
||||
{
|
||||
config.Quality = InkSmoothingQuality.HighQuality;
|
||||
config.UseHardwareAcceleration = true;
|
||||
config.UseAsyncProcessing = true;
|
||||
config.MaxConcurrentTasks = Math.Min(processorCount, 8);
|
||||
}
|
||||
else if (processorCount >= 4)
|
||||
{
|
||||
config.Quality = InkSmoothingQuality.Balanced;
|
||||
config.UseHardwareAcceleration = isHardwareAccelerated;
|
||||
config.UseAsyncProcessing = true;
|
||||
config.MaxConcurrentTasks = Math.Min(processorCount, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.Quality = 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; }
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
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 {
|
||||
@@ -17,16 +16,16 @@ namespace Ink_Canvas.Helpers {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Ink_Canvas.Helpers
|
||||
/// <summary>
|
||||
/// 创建显示笔迹的类
|
||||
/// </summary>
|
||||
public StrokeVisual() : this(new DrawingAttributes()
|
||||
public StrokeVisual() : this(new DrawingAttributes
|
||||
{
|
||||
Color = Colors.Red,
|
||||
//FitToCurve = true,
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动台按钮控件
|
||||
/// </summary>
|
||||
public class LauncherButton
|
||||
{
|
||||
/// <summary>
|
||||
/// 父插件
|
||||
/// </summary>
|
||||
private readonly SuperLauncherPlugin _plugin;
|
||||
|
||||
/// <summary>
|
||||
/// 实际按钮控件
|
||||
/// </summary>
|
||||
private readonly SimpleStackPanel _panel;
|
||||
|
||||
/// <summary>
|
||||
/// 获取按钮UI元素
|
||||
/// </summary>
|
||||
public UIElement Element => _panel;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="plugin">父插件</param>
|
||||
public LauncherButton(SuperLauncherPlugin plugin)
|
||||
{
|
||||
try
|
||||
{
|
||||
_plugin = plugin;
|
||||
LogHelper.WriteLogToFile("开始创建启动台按钮");
|
||||
|
||||
// 创建SimpleStackPanel
|
||||
_panel = new SimpleStackPanel
|
||||
{
|
||||
Name = "Launcher_Icon",
|
||||
Orientation = Orientation.Vertical,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
Width = 28,
|
||||
Margin = new Thickness(0, -2, 0, 0),
|
||||
Background = Brushes.Transparent
|
||||
};
|
||||
|
||||
LogHelper.WriteLogToFile("创建SimpleStackPanel完成");
|
||||
|
||||
// 添加图标
|
||||
var image = CreateIconImage();
|
||||
_panel.Children.Add(image);
|
||||
|
||||
// 添加文本
|
||||
TextBlock textBlock = new TextBlock
|
||||
{
|
||||
Text = "启动台",
|
||||
Foreground = Brushes.Black,
|
||||
FontSize = 8,
|
||||
Margin = new Thickness(0, 1, 0, 0),
|
||||
TextAlignment = TextAlignment.Center
|
||||
};
|
||||
_panel.Children.Add(textBlock);
|
||||
|
||||
// 设置鼠标事件
|
||||
_panel.MouseDown += Panel_MouseDown;
|
||||
_panel.MouseUp += Panel_MouseUp;
|
||||
_panel.MouseLeave += Panel_MouseLeave;
|
||||
|
||||
// 右键菜单支持
|
||||
_panel.ContextMenu = CreateContextMenu();
|
||||
|
||||
// 设置工具提示
|
||||
_panel.ToolTip = "启动台";
|
||||
|
||||
LogHelper.WriteLogToFile("启动台按钮创建完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"创建启动台按钮时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建右键菜单
|
||||
/// </summary>
|
||||
private ContextMenu CreateContextMenu()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建菜单
|
||||
ContextMenu menu = new ContextMenu();
|
||||
|
||||
// 创建位置切换菜单项
|
||||
MenuItem positionMenuItem = new MenuItem();
|
||||
positionMenuItem.Header = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
|
||||
"移至右侧" : "移至左侧";
|
||||
positionMenuItem.Click += (s, e) =>
|
||||
{
|
||||
// 切换位置
|
||||
_plugin.Config.ButtonPosition = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
|
||||
LauncherButtonPosition.Right : LauncherButtonPosition.Left;
|
||||
|
||||
// 更新按钮位置
|
||||
_plugin.UpdateButtonPosition();
|
||||
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
|
||||
LogHelper.WriteLogToFile($"通过右键菜单切换启动台按钮位置为: {_plugin.Config.ButtonPosition}");
|
||||
};
|
||||
menu.Items.Add(positionMenuItem);
|
||||
|
||||
// 添加设置菜单项
|
||||
MenuItem settingsMenuItem = new MenuItem();
|
||||
settingsMenuItem.Header = "打开设置";
|
||||
settingsMenuItem.Click += (s, e) =>
|
||||
{
|
||||
// 打开插件设置窗口
|
||||
var mainWindow = Application.Current.MainWindow;
|
||||
if (mainWindow != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用反射调用主窗口的ShowPluginSettings方法
|
||||
var method = mainWindow.GetType().GetMethod("ShowPluginSettings");
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(mainWindow, null);
|
||||
LogHelper.WriteLogToFile("已打开插件设置窗口");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"打开插件设置窗口失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
};
|
||||
menu.Items.Add(settingsMenuItem);
|
||||
|
||||
return menu;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"创建右键菜单时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取实际的UI元素
|
||||
/// </summary>
|
||||
[Obsolete("使用Element属性代替")]
|
||||
public UIElement GetUIElement()
|
||||
{
|
||||
return _panel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建图标图像
|
||||
/// </summary>
|
||||
private Image CreateIconImage()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建图像
|
||||
Image image = new Image
|
||||
{
|
||||
Height = 17,
|
||||
Margin = new Thickness(0, 3, 0, 0)
|
||||
};
|
||||
|
||||
// 设置位图缩放模式
|
||||
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
|
||||
|
||||
// 创建绘图图像
|
||||
DrawingImage drawingImage = new DrawingImage();
|
||||
DrawingGroup drawingGroup = new DrawingGroup();
|
||||
drawingGroup.ClipGeometry = Geometry.Parse("M0,0 V24 H24 V0 H0 Z");
|
||||
|
||||
// 使用提供的应用网格图标
|
||||
GeometryDrawing geometryDrawing = new GeometryDrawing
|
||||
{
|
||||
Brush = new SolidColorBrush(Color.FromRgb(0x1B, 0x1B, 0x1B)),
|
||||
Geometry = Geometry.Parse("F0 M24,24z M0,0z M4.41721,4.29873C4.35178,4.29873,4.29873,4.35178,4.29873,4.41721L4.29873,9.15646C4.29873,9.22189,4.35178,9.27494,4.41721,9.27494L9.15646,9.27494C9.22189,9.27494,9.27494,9.22189,9.27494,9.15646L9.27494,4.41721C9.27494,4.35178,9.22189,4.29873,9.15646,4.29873L4.41721,4.29873z M2.64,4.41721C2.64,3.43569,3.43569,2.64,4.41721,2.64L9.15646,2.64C10.138,2.64,10.9337,3.43569,10.9337,4.41721L10.9337,9.15646C10.9337,10.138,10.138,10.9337,9.15646,10.9337L4.41721,10.9337C3.43569,10.9337,2.64,10.138,2.64,9.15646L2.64,4.41721z M14.8435,4.29873C14.7781,4.29873,14.7251,4.35178,14.7251,4.41721L14.7251,9.15646C14.7251,9.22189,14.7781,9.27494,14.8435,9.27494L19.5828,9.27494C19.6482,9.27494,19.7013,9.22189,19.7013,9.15646L19.7013,4.41721C19.7013,4.35178,19.6482,4.29873,19.5828,4.29873L14.8435,4.29873z M13.0663,4.41721C13.0663,3.43569,13.862,2.64,14.8435,2.64L19.5828,2.64C20.5643,2.64,21.36,3.43569,21.36,4.41721L21.36,9.15646C21.36,10.138,20.5643,10.9337,19.5828,10.9337L14.8435,10.9337C13.862,10.9337,13.0663,10.138,13.0663,9.15646L13.0663,4.41721z M14.8435,14.7251C14.7781,14.7251,14.7251,14.7781,14.7251,14.8435L14.7251,19.5828C14.7251,19.6482,14.7781,19.7013,14.8435,19.7013L19.5828,19.7013C19.6482,19.7013,19.7013,19.6482,19.7013,19.5828L19.7013,14.8435C19.7013,14.7781,19.6482,14.7251,19.5828,14.7251L14.8435,14.7251z M13.0663,14.8435C13.0663,13.862,13.862,13.0663,14.8435,13.0663L19.5828,13.0663C20.5643,13.0663,21.36,13.862,21.36,14.8435L21.36,19.5828C21.36,20.5643,20.5643,21.36,19.5828,21.36L14.8435,21.36C13.862,21.36,13.0663,20.5643,13.0663,19.5828L13.0663,14.8435z M4.41721,14.7251C4.35178,14.7251,4.29873,14.7781,4.29873,14.8435L4.29873,19.5828C4.29873,19.6482,4.35178,19.7013,4.41721,19.7013L9.15646,19.7013C9.22189,19.7013,9.27494,19.6482,9.27494,19.5828L9.27494,14.8435C9.27494,14.7781,9.22189,14.7251,9.15646,14.7251L4.41721,14.7251z M2.64,14.8435C2.64,13.862,3.43569,13.0663,4.41721,13.0663L9.15646,13.0663C10.138,13.0663,10.9337,13.862,10.9337,14.8435L10.9337,19.5828C10.9337,20.5643,10.138,21.36,9.15646,21.36L4.41721,21.36C3.43569,21.36,2.64,20.5643,2.64,19.5828L2.64,14.8435z")
|
||||
};
|
||||
|
||||
drawingGroup.Children.Add(geometryDrawing);
|
||||
|
||||
// 设置图像源
|
||||
drawingImage.Drawing = drawingGroup;
|
||||
image.Source = drawingImage;
|
||||
|
||||
return image;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"创建图标图像时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
|
||||
// 返回一个空图像
|
||||
return new Image();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 鼠标按下事件
|
||||
/// </summary>
|
||||
private void Panel_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 提供反馈
|
||||
_panel.Background = new SolidColorBrush(Color.FromArgb(40, 0, 0, 0));
|
||||
LogHelper.WriteLogToFile("启动台按钮鼠标按下");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动台按钮鼠标按下事件出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 鼠标抬起事件
|
||||
/// </summary>
|
||||
private void Panel_MouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 只有左键点击才显示启动台窗口
|
||||
if (e.ChangedButton != MouseButton.Left)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 恢复背景
|
||||
_panel.Background = Brushes.Transparent;
|
||||
LogHelper.WriteLogToFile("启动台按钮鼠标抬起,准备显示启动台窗口");
|
||||
|
||||
// 获取按钮在屏幕上的位置
|
||||
Point buttonPosition = _panel.PointToScreen(new Point(_panel.ActualWidth / 2, 0));
|
||||
|
||||
// 显示启动台窗口
|
||||
_plugin.ShowLauncherWindow(buttonPosition);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动台按钮鼠标抬起事件出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 鼠标离开事件
|
||||
/// </summary>
|
||||
private void Panel_MouseLeave(object sender, MouseEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 恢复背景
|
||||
_panel.Background = Brushes.Transparent;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动台按钮鼠标离开事件出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Microsoft.Win32;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动台按钮位置
|
||||
/// </summary>
|
||||
public enum LauncherButtonPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// 左侧
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// 右侧
|
||||
/// </summary>
|
||||
Right
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动台配置
|
||||
/// </summary>
|
||||
public class LauncherConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动台按钮位置
|
||||
/// </summary>
|
||||
public LauncherButtonPosition ButtonPosition { get; set; } = LauncherButtonPosition.Right;
|
||||
|
||||
/// <summary>
|
||||
/// 启动台应用程序列表
|
||||
/// </summary>
|
||||
public List<LauncherItem> Items { get; set; } = new List<LauncherItem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动台应用项
|
||||
/// </summary>
|
||||
public class LauncherItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用程序名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用程序路径
|
||||
/// </summary>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否可见
|
||||
/// </summary>
|
||||
public bool IsVisible { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 在启动台中的位置(0-39)
|
||||
/// </summary>
|
||||
public int Position { get; set; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 是否已固定位置
|
||||
/// </summary>
|
||||
public bool IsPositionFixed { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 图标缓存
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
private ImageSource _iconCache;
|
||||
|
||||
/// <summary>
|
||||
/// 获取应用程序图标
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public ImageSource Icon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_iconCache != null)
|
||||
{
|
||||
return _iconCache;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(Path))
|
||||
{
|
||||
// 从文件中获取图标
|
||||
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(Path);
|
||||
if (icon != null)
|
||||
{
|
||||
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
|
||||
icon.Handle,
|
||||
Int32Rect.Empty,
|
||||
BitmapSizeOptions.FromEmptyOptions());
|
||||
|
||||
icon.Dispose();
|
||||
return _iconCache;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 从注册表中获取文件类型关联图标
|
||||
string extension = System.IO.Path.GetExtension(Path);
|
||||
if (!string.IsNullOrEmpty(extension))
|
||||
{
|
||||
string fileType = Registry.ClassesRoot.OpenSubKey(extension)?.GetValue(string.Empty) as string;
|
||||
if (!string.IsNullOrEmpty(fileType))
|
||||
{
|
||||
string iconPath = Registry.ClassesRoot.OpenSubKey(fileType + "\\DefaultIcon")?.GetValue(string.Empty) as string;
|
||||
if (!string.IsNullOrEmpty(iconPath))
|
||||
{
|
||||
string[] parts = iconPath.Split(',');
|
||||
string iconFile = parts[0].Trim('"');
|
||||
int iconIndex = parts.Length > 1 ? Convert.ToInt32(parts[1]) : 0;
|
||||
|
||||
if (File.Exists(iconFile))
|
||||
{
|
||||
Icon icon = IconExtractor.Extract(iconFile, iconIndex, true);
|
||||
if (icon != null)
|
||||
{
|
||||
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
|
||||
icon.Handle,
|
||||
Int32Rect.Empty,
|
||||
BitmapSizeOptions.FromEmptyOptions());
|
||||
|
||||
icon.Dispose();
|
||||
return _iconCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取应用图标时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
// 返回默认图标
|
||||
return GetDefaultIcon();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取默认图标
|
||||
/// </summary>
|
||||
private ImageSource GetDefaultIcon()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 对于资源管理器,使用特定图标
|
||||
if (Path.EndsWith("explorer.exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 直接从C:\Windows\explorer.exe获取图标
|
||||
string explorerPath = @"C:\Windows\explorer.exe";
|
||||
if (File.Exists(explorerPath))
|
||||
{
|
||||
Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(explorerPath);
|
||||
if (icon != null)
|
||||
{
|
||||
_iconCache = Imaging.CreateBitmapSourceFromHIcon(
|
||||
icon.Handle,
|
||||
Int32Rect.Empty,
|
||||
BitmapSizeOptions.FromEmptyOptions());
|
||||
|
||||
icon.Dispose();
|
||||
return _iconCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取资源管理器图标时出错: {ex.Message}", LogHelper.LogType.Warning);
|
||||
// 如果获取Windows图标失败,回退到默认图标
|
||||
}
|
||||
|
||||
// 回退到备用图标
|
||||
string explorerIconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-Fluent", "ic_fluent_folder_24_regular.png");
|
||||
if (File.Exists(explorerIconPath))
|
||||
{
|
||||
Uri uri = new Uri(explorerIconPath);
|
||||
BitmapImage image = new BitmapImage(uri);
|
||||
_iconCache = image;
|
||||
return _iconCache;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回一个简单的默认图标
|
||||
string iconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-png", "icc.png");
|
||||
if (File.Exists(iconPath))
|
||||
{
|
||||
Uri uri = new Uri(iconPath);
|
||||
BitmapImage image = new BitmapImage(uri);
|
||||
_iconCache = image;
|
||||
return _iconCache;
|
||||
}
|
||||
|
||||
// 如果还是没有找到,尝试使用应用程序图标
|
||||
string appIconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-Fluent", "ic_fluent_apps_24_regular.png");
|
||||
if (File.Exists(appIconPath))
|
||||
{
|
||||
Uri uri = new Uri(appIconPath);
|
||||
BitmapImage image = new BitmapImage(uri);
|
||||
_iconCache = image;
|
||||
return _iconCache;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"获取默认图标时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动应用程序
|
||||
/// </summary>
|
||||
public void Launch()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(Path))
|
||||
{
|
||||
LogHelper.WriteLogToFile("无法启动应用程序:路径为空", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查文件是否存在
|
||||
if (!File.Exists(Path) && !Path.Contains(":\\"))
|
||||
{
|
||||
// 可能是系统命令,如explorer.exe
|
||||
ProcessStartInfo psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = Path,
|
||||
UseShellExecute = true
|
||||
};
|
||||
Process.Start(psi);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 使用Process.Start启动应用程序
|
||||
ProcessStartInfo psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = Path,
|
||||
UseShellExecute = true
|
||||
};
|
||||
Process.Start(psi);
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile($"已启动应用程序: {Path}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动应用程序时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"启动应用程序时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图标提取工具类
|
||||
/// </summary>
|
||||
public static class IconExtractor
|
||||
{
|
||||
/// <summary>
|
||||
/// 从文件中提取图标
|
||||
/// </summary>
|
||||
/// <param name="file">文件路径</param>
|
||||
/// <param name="index">图标索引</param>
|
||||
/// <param name="largeIcon">是否提取大图标</param>
|
||||
/// <returns>提取的图标</returns>
|
||||
public static Icon Extract(string file, int index, bool largeIcon)
|
||||
{
|
||||
try
|
||||
{
|
||||
IntPtr large;
|
||||
IntPtr small;
|
||||
ExtractIconEx(file, index, out large, out small, 1);
|
||||
|
||||
try
|
||||
{
|
||||
return Icon.FromHandle(largeIcon ? large : small);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (large != IntPtr.Zero)
|
||||
DestroyIcon(large);
|
||||
|
||||
if (small != IntPtr.Zero)
|
||||
DestroyIcon(small);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("Shell32.dll", EntryPoint = "ExtractIconEx")]
|
||||
private static extern int ExtractIconEx(
|
||||
[MarshalAs(UnmanagedType.LPStr)] string lpszFile,
|
||||
int nIconIndex,
|
||||
out IntPtr phiconLarge,
|
||||
out IntPtr phiconSmall,
|
||||
int nIcons);
|
||||
|
||||
[DllImport("User32.dll")]
|
||||
private static extern int DestroyIcon(IntPtr hIcon);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<UserControl x:Class="Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher.LauncherSettingsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="500" d:DesignWidth="600">
|
||||
|
||||
<UserControl.Resources>
|
||||
<!-- 自定义按钮样式 -->
|
||||
<Style x:Key="DefaultButtonStyle" TargetType="Button">
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="border"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
TextElement.Foreground="{TemplateBinding Foreground}"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="border" Property="Opacity" Value="0.8"/>
|
||||
<Setter Property="Effect">
|
||||
<Setter.Value>
|
||||
<DropShadowEffect Color="Black" Direction="270" ShadowDepth="2" Opacity="0.3" BlurRadius="4"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="border" Property="Opacity" Value="0.6"/>
|
||||
<Setter Property="RenderTransform">
|
||||
<Setter.Value>
|
||||
<ScaleTransform ScaleX="0.95" ScaleY="0.95"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="border" Property="Opacity" Value="0.4"/>
|
||||
<Setter Property="Cursor" Value="Arrow"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题 -->
|
||||
<TextBlock Grid.Row="0" Text="超级启动台设置" FontSize="16" FontWeight="Bold" Margin="0,0,0,15" Foreground="Black"/>
|
||||
|
||||
<!-- 基本设置 -->
|
||||
<StackPanel Grid.Row="1" Margin="0,0,0,15">
|
||||
<TextBlock Text="基本设置" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,10" Foreground="Black"/>
|
||||
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 按钮位置 -->
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="按钮位置:" VerticalAlignment="Center" Foreground="Black"/>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Margin="0,5">
|
||||
<RadioButton x:Name="RbtnLeft" Content="浮动栏左侧" Margin="0,0,20,0" Checked="RbtnPosition_Checked" Foreground="Black"/>
|
||||
<RadioButton x:Name="RbtnRight" Content="浮动栏右侧" IsChecked="True" Checked="RbtnPosition_Checked" Foreground="Black"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 应用管理 -->
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="应用管理" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,10" Foreground="Black"/>
|
||||
|
||||
<Border Grid.Row="1" BorderThickness="1" BorderBrush="#CCCCCC" CornerRadius="5">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 应用列表 -->
|
||||
<DataGrid Grid.Row="0" x:Name="DgApps" AutoGenerateColumns="False" Margin="5"
|
||||
CanUserAddRows="False" CanUserDeleteRows="False"
|
||||
HeadersVisibility="Column" SelectionMode="Single"
|
||||
SelectionChanged="DgApps_SelectionChanged">
|
||||
<DataGrid.Columns>
|
||||
<DataGridCheckBoxColumn Header="显示" Binding="{Binding IsVisible}" Width="50"/>
|
||||
<DataGridTextColumn Header="名称" Binding="{Binding Name}" Width="150"/>
|
||||
<DataGridTextColumn Header="路径" Binding="{Binding Path}" Width="*"/>
|
||||
<DataGridTextColumn Header="位置" Binding="{Binding Position}" Width="50"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5">
|
||||
<Button x:Name="BtnAdd" Content="添加" Padding="10,5" Margin="0,5,5,5" Click="BtnAdd_Click"
|
||||
Background="#FF007ACC" Foreground="White" BorderBrush="#FF005A9B" BorderThickness="1"
|
||||
Style="{StaticResource DefaultButtonStyle}"/>
|
||||
<Button x:Name="BtnEdit" Content="编辑" Padding="10,5" Margin="5" Click="BtnEdit_Click"
|
||||
Background="#FF6C757D" Foreground="White" BorderBrush="#FF5A6268" BorderThickness="1"
|
||||
Style="{StaticResource DefaultButtonStyle}"/>
|
||||
<Button x:Name="BtnDelete" Content="删除" Padding="10,5" Margin="5" Click="BtnDelete_Click"
|
||||
Background="#FFDC3545" Foreground="White" BorderBrush="#FFBD2130" BorderThickness="1"
|
||||
Style="{StaticResource DefaultButtonStyle}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0">
|
||||
<Button x:Name="BtnSave" Content="保存设置" Padding="15,5" Click="BtnSave_Click"
|
||||
Background="#FF28A745" Foreground="White" BorderBrush="#FF1E7E34" BorderThickness="1"
|
||||
Style="{StaticResource DefaultButtonStyle}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,396 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Ink_Canvas.Windows;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
|
||||
{
|
||||
/// <summary>
|
||||
/// LauncherSettingsControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class LauncherSettingsControl : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// 父插件
|
||||
/// </summary>
|
||||
private readonly SuperLauncherPlugin _plugin;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="plugin">父插件</param>
|
||||
public LauncherSettingsControl(SuperLauncherPlugin plugin)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_plugin = plugin;
|
||||
|
||||
// 设置按钮位置
|
||||
RbtnLeft.IsChecked = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left;
|
||||
RbtnRight.IsChecked = _plugin.Config.ButtonPosition == LauncherButtonPosition.Right;
|
||||
|
||||
// 绑定应用列表
|
||||
DgApps.ItemsSource = _plugin.LauncherItems;
|
||||
|
||||
// 初始化按钮状态
|
||||
UpdateButtonStates();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新按钮状态
|
||||
/// </summary>
|
||||
private void UpdateButtonStates()
|
||||
{
|
||||
bool hasSelection = DgApps.SelectedItem != null;
|
||||
BtnEdit.IsEnabled = hasSelection;
|
||||
BtnDelete.IsEnabled = hasSelection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 位置单选按钮选择事件
|
||||
/// </summary>
|
||||
private void RbtnPosition_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
|
||||
LauncherButtonPosition oldPosition = _plugin.Config.ButtonPosition;
|
||||
|
||||
if (sender == RbtnLeft)
|
||||
{
|
||||
_plugin.Config.ButtonPosition = LauncherButtonPosition.Left;
|
||||
}
|
||||
else if (sender == RbtnRight)
|
||||
{
|
||||
_plugin.Config.ButtonPosition = LauncherButtonPosition.Right;
|
||||
}
|
||||
|
||||
// 如果位置发生变化,更新按钮位置
|
||||
if (oldPosition != _plugin.Config.ButtonPosition)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 更新按钮位置
|
||||
_plugin.UpdateButtonPosition();
|
||||
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
|
||||
LogHelper.WriteLogToFile($"启动台按钮位置已更改为: {_plugin.Config.ButtonPosition}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"更新启动台按钮位置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"更新启动台按钮位置时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnAdd_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建新的启动项
|
||||
LauncherItem item = new LauncherItem
|
||||
{
|
||||
Name = "",
|
||||
Path = "",
|
||||
IsVisible = true,
|
||||
Position = -1 // 让插件管理器分配位置
|
||||
};
|
||||
|
||||
// 直接显示编辑对话框
|
||||
EditLauncherItem(item, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"添加启动项时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"添加启动项时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑应用按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnEdit_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DgApps.SelectedItem is LauncherItem item)
|
||||
{
|
||||
EditLauncherItem(item, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除应用按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnDelete_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DgApps.SelectedItem is LauncherItem item)
|
||||
{
|
||||
// 确认删除
|
||||
MessageBoxResult result = MessageBox.Show(
|
||||
$"确定要删除 {item.Name} 吗?",
|
||||
"删除确认",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
// 从集合中移除
|
||||
_plugin.LauncherItems.Remove(item);
|
||||
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存设置按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnSave_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
|
||||
// 如果插件已启用,重新加载启动台按钮
|
||||
if (_plugin.IsEnabled)
|
||||
{
|
||||
_plugin.Disable();
|
||||
_plugin.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果插件未启用,则启用它
|
||||
_plugin.Enable();
|
||||
|
||||
// 通知PluginSettingsWindow刷新插件列表
|
||||
var window = Window.GetWindow(this);
|
||||
if (window is PluginSettingsWindow pluginSettingsWindow)
|
||||
{
|
||||
// 触发刷新
|
||||
pluginSettingsWindow.RefreshPluginList();
|
||||
}
|
||||
}
|
||||
|
||||
MessageBox.Show("设置已保存并应用!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存设置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"保存设置时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用项选择变更事件
|
||||
/// </summary>
|
||||
private void DgApps_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
UpdateButtonStates();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑启动项
|
||||
/// </summary>
|
||||
/// <param name="item">启动项</param>
|
||||
/// <param name="isNew">是否为新建</param>
|
||||
private void EditLauncherItem(LauncherItem item, bool isNew)
|
||||
{
|
||||
// 创建简单的编辑窗口
|
||||
Window editWindow = new Window
|
||||
{
|
||||
Title = isNew ? "添加" : "编辑应用",
|
||||
Width = 400,
|
||||
Height = 200,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen,
|
||||
ResizeMode = ResizeMode.NoResize
|
||||
};
|
||||
|
||||
// 创建编辑表单
|
||||
Grid grid = new Grid
|
||||
{
|
||||
Margin = new Thickness(20)
|
||||
};
|
||||
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
||||
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
|
||||
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80) });
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
|
||||
// 名称输入框
|
||||
TextBlock nameLabel = new TextBlock
|
||||
{
|
||||
Text = "名称:",
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
TextBox nameTextBox = new TextBox
|
||||
{
|
||||
Text = item.Name,
|
||||
Margin = new Thickness(0, 5, 0, 5)
|
||||
};
|
||||
|
||||
Grid.SetRow(nameLabel, 0);
|
||||
Grid.SetColumn(nameLabel, 0);
|
||||
Grid.SetRow(nameTextBox, 0);
|
||||
Grid.SetColumn(nameTextBox, 1);
|
||||
|
||||
grid.Children.Add(nameLabel);
|
||||
grid.Children.Add(nameTextBox);
|
||||
|
||||
// 路径输入框
|
||||
TextBlock pathLabel = new TextBlock
|
||||
{
|
||||
Text = "路径:",
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
Grid pathGrid = new Grid();
|
||||
pathGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
pathGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength() });
|
||||
|
||||
TextBox pathTextBox = new TextBox
|
||||
{
|
||||
Text = item.Path,
|
||||
Margin = new Thickness(0, 5, 5, 5)
|
||||
};
|
||||
Button browseButton = new Button
|
||||
{
|
||||
Content = "浏览",
|
||||
Padding = new Thickness(5, 0, 5, 0),
|
||||
Margin = new Thickness(0, 5, 0, 5)
|
||||
};
|
||||
|
||||
browseButton.Click += (s, e) =>
|
||||
{
|
||||
OpenFileDialog dialog = new OpenFileDialog
|
||||
{
|
||||
Title = "选择应用程序",
|
||||
Filter = "应用程序 (*.exe)|*.exe|所有文件 (*.*)|*.*",
|
||||
Multiselect = false,
|
||||
FileName = pathTextBox.Text
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
pathTextBox.Text = dialog.FileName;
|
||||
|
||||
// 如果选择的是.exe文件,自动获取文件名填入名称字段
|
||||
if (Path.GetExtension(dialog.FileName).ToLower() == ".exe")
|
||||
{
|
||||
string fileName = Path.GetFileNameWithoutExtension(dialog.FileName);
|
||||
// 只有在名称字段为空或者是新建项目时才自动填入
|
||||
if (string.IsNullOrWhiteSpace(nameTextBox.Text) || isNew)
|
||||
{
|
||||
nameTextBox.Text = fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Grid.SetColumn(pathTextBox, 0);
|
||||
Grid.SetColumn(browseButton, 1);
|
||||
pathGrid.Children.Add(pathTextBox);
|
||||
pathGrid.Children.Add(browseButton);
|
||||
|
||||
Grid.SetRow(pathLabel, 1);
|
||||
Grid.SetColumn(pathLabel, 0);
|
||||
Grid.SetRow(pathGrid, 1);
|
||||
Grid.SetColumn(pathGrid, 1);
|
||||
|
||||
grid.Children.Add(pathLabel);
|
||||
grid.Children.Add(pathGrid);
|
||||
|
||||
// 确认和取消按钮
|
||||
StackPanel buttonPanel = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
Margin = new Thickness(0, 10, 0, 0)
|
||||
};
|
||||
|
||||
Button okButton = new Button
|
||||
{
|
||||
Content = "确定",
|
||||
Padding = new Thickness(15, 5, 15, 5),
|
||||
Margin = new Thickness(0, 0, 10, 0),
|
||||
IsDefault = true
|
||||
};
|
||||
|
||||
Button cancelButton = new Button
|
||||
{
|
||||
Content = "取消",
|
||||
Padding = new Thickness(15, 5, 15, 5),
|
||||
IsCancel = true
|
||||
};
|
||||
|
||||
okButton.Click += (s, e) =>
|
||||
{
|
||||
// 验证输入
|
||||
if (string.IsNullOrWhiteSpace(nameTextBox.Text))
|
||||
{
|
||||
MessageBox.Show("请输入应用名称!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(pathTextBox.Text))
|
||||
{
|
||||
MessageBox.Show("请输入应用路径!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新项目
|
||||
item.Name = nameTextBox.Text;
|
||||
item.Path = pathTextBox.Text;
|
||||
|
||||
// 如果是新建,添加到集合
|
||||
if (isNew)
|
||||
{
|
||||
_plugin.AddLauncherItem(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 触发属性变更通知,刷新DataGrid
|
||||
if (DgApps.ItemsSource is ICollectionView view)
|
||||
{
|
||||
view.Refresh();
|
||||
}
|
||||
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
}
|
||||
|
||||
editWindow.DialogResult = true;
|
||||
editWindow.Close();
|
||||
};
|
||||
|
||||
cancelButton.Click += (s, e) =>
|
||||
{
|
||||
editWindow.DialogResult = false;
|
||||
editWindow.Close();
|
||||
};
|
||||
|
||||
buttonPanel.Children.Add(okButton);
|
||||
buttonPanel.Children.Add(cancelButton);
|
||||
|
||||
Grid.SetRow(buttonPanel, 2);
|
||||
Grid.SetColumnSpan(buttonPanel, 2);
|
||||
|
||||
grid.Children.Add(buttonPanel);
|
||||
|
||||
// 设置窗口内容
|
||||
editWindow.Content = grid;
|
||||
|
||||
// 显示窗口
|
||||
editWindow.ShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<Window x:Class="Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher.LauncherWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher"
|
||||
mc:Ignorable="d"
|
||||
Title="启动台"
|
||||
Width="400"
|
||||
Height="300"
|
||||
WindowStyle="None"
|
||||
AllowsTransparency="True"
|
||||
Background="#80000000"
|
||||
ResizeMode="NoResize"
|
||||
Topmost="True"
|
||||
Deactivated="Window_Deactivated"
|
||||
ShowInTaskbar="False">
|
||||
|
||||
<Window.Resources>
|
||||
<!-- 应用项样式 -->
|
||||
<Style x:Key="LauncherItemStyle" TargetType="Button">
|
||||
<Setter Property="Width" Value="80"/>
|
||||
<Setter Property="Height" Value="80"/>
|
||||
<Setter Property="Margin" Value="5"/>
|
||||
<Setter Property="Background" Value="#40FFFFFF"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="border" Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="8">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Image Grid.Row="0" Source="{Binding Icon}" Width="32" Height="32" Margin="0,10,0,5"/>
|
||||
<TextBlock Grid.Row="1" Text="{Binding Name}" TextWrapping="Wrap" TextAlignment="Center"
|
||||
Margin="2,0,2,8" FontSize="11" Foreground="White"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#80FFFFFF"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#C0FFFFFF"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Border CornerRadius="15" Background="#80000000" BorderThickness="1" BorderBrush="#40FFFFFF">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题栏 -->
|
||||
<Grid Grid.Row="0" Height="40">
|
||||
<TextBlock Text="启动台" Foreground="White" FontSize="18" FontWeight="Bold"
|
||||
VerticalAlignment="Center" Margin="15,0,0,0"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,10,0">
|
||||
<Button x:Name="BtnFixMode" Click="BtnFixMode_Click" Width="30" Height="30"
|
||||
Margin="5,0" Background="Transparent" BorderThickness="0"
|
||||
ToolTip="切换固定模式">
|
||||
<Path x:Name="FixModeIcon" Data="M7,2V13H10V22L17,10H13L17,2H7Z" Fill="White" Stretch="Uniform" Width="16" Height="16"/>
|
||||
</Button>
|
||||
<Button x:Name="BtnClose" Click="BtnClose_Click" Width="30" Height="30"
|
||||
Background="Transparent" BorderThickness="0"
|
||||
ToolTip="关闭">
|
||||
<Path Data="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
|
||||
Fill="White" Stretch="Uniform" Width="16" Height="16"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 应用网格 -->
|
||||
<ScrollViewer Grid.Row="1" Margin="10" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<WrapPanel x:Name="AppPanel" Orientation="Horizontal" HorizontalAlignment="Center"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
||||
@@ -0,0 +1,466 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
|
||||
{
|
||||
/// <summary>
|
||||
/// LauncherWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class LauncherWindow : Window
|
||||
{
|
||||
/// <summary>
|
||||
/// 父插件
|
||||
/// </summary>
|
||||
private readonly SuperLauncherPlugin _plugin;
|
||||
|
||||
/// <summary>
|
||||
/// 是否处于固定模式
|
||||
/// </summary>
|
||||
private bool _isFixMode;
|
||||
|
||||
/// <summary>
|
||||
/// 应用项按钮列表
|
||||
/// </summary>
|
||||
private readonly Dictionary<Button, LauncherItem> _appButtons = new Dictionary<Button, LauncherItem>();
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽中的按钮
|
||||
/// </summary>
|
||||
private Button _draggingButton;
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽开始位置
|
||||
/// </summary>
|
||||
private Point _dragStartPoint;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public LauncherWindow(SuperLauncherPlugin plugin)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_plugin = plugin;
|
||||
|
||||
// 加载应用项
|
||||
LoadLauncherItems();
|
||||
|
||||
// 添加鼠标按下事件(用于拖动窗口)
|
||||
MouseDown += (s, e) =>
|
||||
{
|
||||
if (e.ChangedButton == MouseButton.Left && e.ButtonState == MouseButtonState.Pressed)
|
||||
{
|
||||
DragMove();
|
||||
}
|
||||
};
|
||||
|
||||
// 根据应用数量调整窗口大小
|
||||
AdjustWindowSize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载启动台应用项
|
||||
/// </summary>
|
||||
private void LoadLauncherItems()
|
||||
{
|
||||
// 清空现有应用项
|
||||
AppPanel.Children.Clear();
|
||||
_appButtons.Clear();
|
||||
|
||||
// 获取显示的应用项
|
||||
var visibleItems = _plugin.LauncherItems
|
||||
.Where(item => item.IsVisible)
|
||||
.OrderBy(item => item.Position)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in visibleItems)
|
||||
{
|
||||
// 创建应用按钮
|
||||
Button appButton = new Button
|
||||
{
|
||||
Style = (Style)FindResource("LauncherItemStyle"),
|
||||
DataContext = item,
|
||||
Tag = item.Position
|
||||
};
|
||||
|
||||
// 添加点击事件
|
||||
appButton.Click += AppButton_Click;
|
||||
|
||||
// 在固定模式下,添加拖拽事件
|
||||
appButton.PreviewMouseDown += AppButton_PreviewMouseDown;
|
||||
appButton.PreviewMouseMove += AppButton_PreviewMouseMove;
|
||||
appButton.PreviewMouseUp += AppButton_PreviewMouseUp;
|
||||
|
||||
// 记录按钮和项目的对应关系
|
||||
_appButtons.Add(appButton, item);
|
||||
|
||||
// 添加到面板
|
||||
AppPanel.Children.Add(appButton);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据应用数量调整窗口大小
|
||||
/// </summary>
|
||||
private void AdjustWindowSize()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 每行最多显示4个应用
|
||||
const int appsPerRow = 4;
|
||||
|
||||
// 计算行数
|
||||
int visibleCount = _appButtons.Count;
|
||||
int rowCount = (int)Math.Ceiling(visibleCount / (double)appsPerRow);
|
||||
|
||||
// 设置窗口宽度(每个应用90像素宽 = 80 + 5*2)
|
||||
Width = Math.Min(appsPerRow * 90 + 40, 400); // 最大宽度400
|
||||
|
||||
// 设置窗口高度(每个应用90像素高 = 80 + 5*2)
|
||||
Height = Math.Min(rowCount * 90 + 60, 600); // 最大高度600,标题栏40 + 边距20
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"调整启动台窗口大小时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用按钮点击事件
|
||||
/// </summary>
|
||||
private void AppButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_isFixMode) return; // 在固定模式下,不响应点击事件
|
||||
|
||||
if (sender is Button button && _appButtons.TryGetValue(button, out LauncherItem item))
|
||||
{
|
||||
// 获取应用路径和名称,用于后续启动
|
||||
string appPath = item.Path;
|
||||
string appName = item.Name;
|
||||
|
||||
LogHelper.WriteLogToFile($"点击启动应用: {appName}, 路径: {appPath}");
|
||||
|
||||
// 首先标记窗口正在关闭
|
||||
IsClosing = true;
|
||||
|
||||
// 创建一个应用启动任务
|
||||
var launchTask = new Task(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 等待一段时间,确保窗口关闭流程已经开始
|
||||
Thread.Sleep(200);
|
||||
|
||||
// 使用UI线程启动应用
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查应用路径是否存在
|
||||
if (File.Exists(appPath) || !appPath.Contains(":\\"))
|
||||
{
|
||||
// 创建进程启动信息
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = appPath,
|
||||
UseShellExecute = true,
|
||||
};
|
||||
|
||||
// 启动应用程序
|
||||
var process = Process.Start(psi);
|
||||
LogHelper.WriteLogToFile($"应用程序 {appName} 已启动");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用路径不存在: {appPath}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"找不到应用程序: {appPath}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启动应用程序失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"启动应用程序失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用启动任务出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
});
|
||||
|
||||
// 关闭窗口
|
||||
try
|
||||
{
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try { Close(); } catch { }
|
||||
|
||||
// 启动应用程序任务
|
||||
launchTask.Start();
|
||||
}), DispatcherPriority.Background);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"关闭窗口或启动任务时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
// 如果无法通过UI关闭窗口,直接启动任务
|
||||
launchTask.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用按钮点击事件出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
try { IsClosing = true; Close(); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
#region 固定模式拖拽事件
|
||||
|
||||
/// <summary>
|
||||
/// 应用按钮鼠标按下事件
|
||||
/// </summary>
|
||||
private void AppButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!_isFixMode) return;
|
||||
|
||||
if (e.ChangedButton == MouseButton.Left && sender is Button button)
|
||||
{
|
||||
_draggingButton = button;
|
||||
_dragStartPoint = e.GetPosition(AppPanel);
|
||||
button.CaptureMouse();
|
||||
button.Opacity = 0.7;
|
||||
|
||||
// 阻止事件冒泡,以避免触发按钮点击
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用按钮鼠标移动事件
|
||||
/// </summary>
|
||||
private void AppButton_PreviewMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (!_isFixMode || _draggingButton == null) return;
|
||||
|
||||
if (e.LeftButton == MouseButtonState.Pressed)
|
||||
{
|
||||
Point currentPosition = e.GetPosition(AppPanel);
|
||||
|
||||
// 移动按钮
|
||||
System.Windows.Controls.Canvas.SetLeft(_draggingButton, currentPosition.X - _draggingButton.ActualWidth / 2);
|
||||
System.Windows.Controls.Canvas.SetTop(_draggingButton, currentPosition.Y - _draggingButton.ActualHeight / 2);
|
||||
|
||||
// 将按钮移到最上层
|
||||
Panel.SetZIndex(_draggingButton, 100);
|
||||
|
||||
// 阻止事件冒泡
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用按钮鼠标释放事件
|
||||
/// </summary>
|
||||
private void AppButton_PreviewMouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!_isFixMode || _draggingButton == null) return;
|
||||
|
||||
// 释放鼠标捕获
|
||||
_draggingButton.ReleaseMouseCapture();
|
||||
|
||||
// 计算新位置
|
||||
Point releasePoint = e.GetPosition(AppPanel);
|
||||
int newPosition = CalculateGridPosition(releasePoint);
|
||||
|
||||
// 获取当前项目
|
||||
LauncherItem currentItem = _appButtons[_draggingButton];
|
||||
|
||||
// 重新排序
|
||||
ReorderItems(currentItem, newPosition);
|
||||
|
||||
// 重新加载应用项
|
||||
LoadLauncherItems();
|
||||
|
||||
// 保存配置
|
||||
_plugin.SaveConfig();
|
||||
|
||||
// 清除拖拽状态
|
||||
_draggingButton.Opacity = 1;
|
||||
Panel.SetZIndex(_draggingButton, 0);
|
||||
_draggingButton = null;
|
||||
|
||||
// 阻止事件冒泡
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算网格位置
|
||||
/// </summary>
|
||||
private int CalculateGridPosition(Point point)
|
||||
{
|
||||
// 计算行和列
|
||||
int columnCount = 4; // 每行最多4个应用
|
||||
int columnWidth = 90; // 应用宽度(包括边距)
|
||||
int rowHeight = 90; // 应用高度(包括边距)
|
||||
|
||||
int column = (int)(point.X / columnWidth);
|
||||
int row = (int)(point.Y / rowHeight);
|
||||
|
||||
// 确保在有效范围内
|
||||
column = Math.Max(0, Math.Min(column, columnCount - 1));
|
||||
row = Math.Max(0, row);
|
||||
|
||||
// 计算位置索引
|
||||
return row * columnCount + column;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新排序应用项
|
||||
/// </summary>
|
||||
private void ReorderItems(LauncherItem item, int newPosition)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 设置项目为固定位置
|
||||
item.IsPositionFixed = true;
|
||||
|
||||
// 如果位置相同,无需调整
|
||||
if (item.Position == newPosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取所有可见项目
|
||||
var visibleItems = _plugin.LauncherItems
|
||||
.Where(i => i.IsVisible)
|
||||
.OrderBy(i => i.Position)
|
||||
.ToList();
|
||||
|
||||
// 移除当前项目
|
||||
visibleItems.Remove(item);
|
||||
|
||||
// 查找插入位置
|
||||
int insertIndex = 0;
|
||||
for (int i = 0; i < visibleItems.Count; i++)
|
||||
{
|
||||
if (visibleItems[i].Position >= newPosition)
|
||||
{
|
||||
insertIndex = i;
|
||||
break;
|
||||
}
|
||||
insertIndex = i + 1;
|
||||
}
|
||||
|
||||
// 插入项目
|
||||
visibleItems.Insert(insertIndex, item);
|
||||
|
||||
// 重新分配位置
|
||||
for (int i = 0; i < visibleItems.Count; i++)
|
||||
{
|
||||
visibleItems[i].Position = i;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"重新排序应用项时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 窗口事件处理
|
||||
|
||||
/// <summary>
|
||||
/// 窗口失去焦点事件
|
||||
/// </summary>
|
||||
private void Window_Deactivated(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 只有在非固定模式、窗口已加载、未处于关闭状态且IsLoaded=true时关闭窗口
|
||||
if (!_isFixMode && IsLoaded && !IsClosing)
|
||||
{
|
||||
// 标记为正在关闭
|
||||
IsClosing = true;
|
||||
|
||||
// 使用Dispatcher.BeginInvoke而不是直接调用Close,避免冲突
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 再次检查窗口状态
|
||||
if (IsLoaded && !IsClosing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"延迟关闭窗口时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}), DispatcherPriority.Background);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"窗口失去焦点关闭时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 窗口是否正在关闭
|
||||
/// </summary>
|
||||
private bool IsClosing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 重写OnClosing方法,标记窗口正在关闭
|
||||
/// </summary>
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
IsClosing = true;
|
||||
base.OnClosing(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnClose_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 固定模式按钮点击事件
|
||||
/// </summary>
|
||||
private void BtnFixMode_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 切换固定模式
|
||||
_isFixMode = !_isFixMode;
|
||||
|
||||
// 更新固定模式按钮图标颜色
|
||||
FixModeIcon.Fill = _isFixMode ? Brushes.Yellow : Brushes.White;
|
||||
|
||||
// 显示提示
|
||||
if (_isFixMode)
|
||||
{
|
||||
MessageBox.Show("已进入固定模式,您可以拖动应用图标调整位置。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,589 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins.BuiltIn
|
||||
{
|
||||
/// <summary>
|
||||
/// 超级启动台插件
|
||||
/// </summary>
|
||||
public class SuperLauncherPlugin : PluginBase
|
||||
{
|
||||
#region 插件基本信息
|
||||
|
||||
public override string Name => "超级启动台";
|
||||
|
||||
public override string Description => "在浮动栏添加一个启动台按钮,可快速启动常用应用程序。";
|
||||
|
||||
public override Version Version => new Version(1, 0, 1);
|
||||
|
||||
public override string Author => "ICC CE 团队";
|
||||
|
||||
public override bool IsBuiltIn => true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件属性和字段
|
||||
|
||||
/// <summary>
|
||||
/// 启动台配置
|
||||
/// </summary>
|
||||
public LauncherConfig Config { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启动台应用程序列表
|
||||
/// </summary>
|
||||
public ObservableCollection<LauncherItem> LauncherItems { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启动台按钮
|
||||
/// </summary>
|
||||
private LauncherButton _launcherButton;
|
||||
|
||||
/// <summary>
|
||||
/// 启动台窗口
|
||||
/// </summary>
|
||||
private LauncherWindow _launcherWindow;
|
||||
|
||||
/// <summary>
|
||||
/// 配置文件路径
|
||||
/// </summary>
|
||||
private readonly string _configPath = Path.Combine(App.RootPath, "PluginConfigs", "SuperLauncher.json");
|
||||
|
||||
/// <summary>
|
||||
/// 标记是否已添加到浮动栏
|
||||
/// </summary>
|
||||
private bool _isAddedToFloatingBar;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件生命周期
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
// 创建配置目录
|
||||
string configDir = Path.Combine(App.RootPath, "PluginConfigs");
|
||||
if (!Directory.Exists(configDir))
|
||||
{
|
||||
Directory.CreateDirectory(configDir);
|
||||
}
|
||||
|
||||
// 加载配置
|
||||
LoadConfig();
|
||||
|
||||
LogHelper.WriteLogToFile("超级启动台插件已初始化");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"初始化超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Enable()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsEnabled) return; // 防止重复启用
|
||||
|
||||
// 创建启动台按钮
|
||||
if (_launcherButton == null)
|
||||
{
|
||||
_launcherButton = new LauncherButton(this);
|
||||
LogHelper.WriteLogToFile("超级启动台按钮已创建");
|
||||
}
|
||||
|
||||
// 添加启动台按钮到浮动栏
|
||||
AddLauncherButtonToFloatingBar();
|
||||
|
||||
// 设置启用状态
|
||||
base.Enable();
|
||||
|
||||
// 保存插件配置
|
||||
SavePluginSettings();
|
||||
|
||||
LogHelper.WriteLogToFile("超级启动台插件已启用");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启用超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Disable()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsEnabled) return; // 防止重复禁用
|
||||
|
||||
// 从浮动栏移除启动台按钮
|
||||
RemoveLauncherButtonFromFloatingBar();
|
||||
|
||||
// 如果启动台窗口打开,则关闭
|
||||
if (_launcherWindow != null && _launcherWindow.IsVisible)
|
||||
{
|
||||
_launcherWindow.Close();
|
||||
_launcherWindow = null;
|
||||
}
|
||||
|
||||
// 设置禁用状态
|
||||
base.Disable();
|
||||
|
||||
// 保存插件配置
|
||||
SavePluginSettings();
|
||||
|
||||
LogHelper.WriteLogToFile("超级启动台插件已禁用");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"禁用超级启动台插件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public override UserControl GetSettingsView()
|
||||
{
|
||||
return new LauncherSettingsControl(this);
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
// 保存配置
|
||||
SaveConfig();
|
||||
|
||||
// 从浮动栏移除启动台按钮
|
||||
RemoveLauncherButtonFromFloatingBar();
|
||||
|
||||
// 如果启动台窗口打开,则关闭
|
||||
if (_launcherWindow != null && _launcherWindow.IsVisible)
|
||||
{
|
||||
_launcherWindow.Close();
|
||||
_launcherWindow = null;
|
||||
}
|
||||
|
||||
base.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存插件设置
|
||||
/// </summary>
|
||||
public override void SavePluginSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保配置已加载
|
||||
if (Config == null)
|
||||
{
|
||||
LoadConfig();
|
||||
}
|
||||
|
||||
// 更新其他设置,但不更改插件启用状态
|
||||
|
||||
// 保存配置
|
||||
SaveConfig();
|
||||
|
||||
LogHelper.WriteLogToFile("超级启动台插件设置已保存");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存超级启动台插件设置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 配置管理
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置
|
||||
/// </summary>
|
||||
private void LoadConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_configPath))
|
||||
{
|
||||
string json = File.ReadAllText(_configPath);
|
||||
Config = JsonConvert.DeserializeObject<LauncherConfig>(json) ?? CreateDefaultConfig();
|
||||
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items ?? new List<LauncherItem>());
|
||||
|
||||
// 注意:不再根据配置更改插件启用状态
|
||||
// 插件状态由PluginManager统一管理
|
||||
}
|
||||
else
|
||||
{
|
||||
Config = CreateDefaultConfig();
|
||||
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items);
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"加载超级启动台配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
Config = CreateDefaultConfig();
|
||||
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存配置
|
||||
/// </summary>
|
||||
public void SaveConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 同步LauncherItems到Config
|
||||
Config.Items = new List<LauncherItem>(LauncherItems);
|
||||
|
||||
// 序列化并保存配置
|
||||
string json = JsonConvert.SerializeObject(Config, Formatting.Indented);
|
||||
File.WriteAllText(_configPath, json);
|
||||
|
||||
LogHelper.WriteLogToFile("超级启动台配置已保存");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存超级启动台配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建默认配置
|
||||
/// </summary>
|
||||
private LauncherConfig CreateDefaultConfig()
|
||||
{
|
||||
var config = new LauncherConfig
|
||||
{
|
||||
ButtonPosition = LauncherButtonPosition.Right,
|
||||
// 不再使用IsEnabled,插件状态由PluginManager管理
|
||||
Items = new List<LauncherItem>
|
||||
{
|
||||
new LauncherItem
|
||||
{
|
||||
Name = "资源管理器",
|
||||
Path = @"C:\Windows\explorer.exe",
|
||||
IsVisible = true,
|
||||
Position = 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 启动台按钮管理
|
||||
|
||||
/// <summary>
|
||||
/// 将启动台按钮添加到浮动栏
|
||||
/// </summary>
|
||||
private void AddLauncherButtonToFloatingBar()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果已经添加,先移除
|
||||
if (_isAddedToFloatingBar)
|
||||
{
|
||||
RemoveLauncherButtonFromFloatingBar();
|
||||
_isAddedToFloatingBar = false;
|
||||
}
|
||||
|
||||
// 获取主窗口实例
|
||||
var mainWindow = Application.Current.MainWindow;
|
||||
if (mainWindow == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到主窗口实例,无法添加启动台按钮", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建启动台按钮
|
||||
_launcherButton = new LauncherButton(this);
|
||||
var buttonElement = _launcherButton.Element;
|
||||
|
||||
// 查找浮动栏
|
||||
var floatingBar = mainWindow.FindName("StackPanelFloatingBar") as Panel;
|
||||
if (floatingBar == null)
|
||||
{
|
||||
// 如果直接查找失败,则尝试遍历可视树查找
|
||||
Panel floatingBarPanelFromTree = null;
|
||||
FindStackPanelFloatingBar(mainWindow, ref floatingBarPanelFromTree);
|
||||
floatingBar = floatingBarPanelFromTree;
|
||||
}
|
||||
|
||||
if (floatingBar == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到浮动栏,无法添加启动台按钮", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加启动台按钮到浮动栏
|
||||
if (Config.ButtonPosition == LauncherButtonPosition.Left)
|
||||
{
|
||||
floatingBar.Children.Insert(0, buttonElement);
|
||||
LogHelper.WriteLogToFile("启动台按钮已添加到浮动栏左侧");
|
||||
}
|
||||
else
|
||||
{
|
||||
floatingBar.Children.Add(buttonElement);
|
||||
LogHelper.WriteLogToFile("启动台按钮已添加到浮动栏右侧");
|
||||
}
|
||||
|
||||
_isAddedToFloatingBar = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"添加启动台按钮到浮动栏时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归查找StackPanelFloatingBar
|
||||
/// </summary>
|
||||
private void FindStackPanelFloatingBar(DependencyObject parent, ref Panel result)
|
||||
{
|
||||
if (parent == null || result != null) return;
|
||||
|
||||
try
|
||||
{
|
||||
// 检查当前对象是否为我们要找的面板
|
||||
if (parent is Panel panel && panel.Name == "StackPanelFloatingBar")
|
||||
{
|
||||
result = panel;
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取子元素数量
|
||||
int childCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||
|
||||
// 遍历所有子元素
|
||||
for (int i = 0; i < childCount; i++)
|
||||
{
|
||||
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
|
||||
FindStackPanelFloatingBar(child, ref result);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"查找StackPanelFloatingBar时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从浮动栏移除启动台按钮
|
||||
/// </summary>
|
||||
private void RemoveLauncherButtonFromFloatingBar()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_isAddedToFloatingBar || _launcherButton == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取主窗口实例
|
||||
var mainWindow = Application.Current.MainWindow;
|
||||
if (mainWindow == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到主窗口实例,无法移除启动台按钮", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取按钮元素
|
||||
var buttonElement = _launcherButton.Element;
|
||||
|
||||
// 查找浮动栏
|
||||
var floatingBar = mainWindow.FindName("StackPanelFloatingBar") as Panel;
|
||||
if (floatingBar == null)
|
||||
{
|
||||
// 如果直接查找失败,则尝试遍历可视树查找
|
||||
Panel floatingBarPanelFromTree = null;
|
||||
FindStackPanelFloatingBar(mainWindow, ref floatingBarPanelFromTree);
|
||||
floatingBar = floatingBarPanelFromTree;
|
||||
}
|
||||
|
||||
if (floatingBar == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile("未找到浮动栏,无法移除启动台按钮", LogHelper.LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 从浮动栏移除启动台按钮
|
||||
if (floatingBar.Children.Contains(buttonElement))
|
||||
{
|
||||
floatingBar.Children.Remove(buttonElement);
|
||||
LogHelper.WriteLogToFile("启动台按钮已从浮动栏移除");
|
||||
}
|
||||
|
||||
_isAddedToFloatingBar = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"移除启动台按钮时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新启动台按钮位置
|
||||
/// </summary>
|
||||
public void UpdateButtonPosition()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果按钮已添加到浮动栏,重新添加以更新位置
|
||||
if (_isAddedToFloatingBar)
|
||||
{
|
||||
RemoveLauncherButtonFromFloatingBar();
|
||||
AddLauncherButtonToFloatingBar();
|
||||
LogHelper.WriteLogToFile($"启动台按钮位置已更新为: {Config.ButtonPosition}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"更新启动台按钮位置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.NewLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 启动台功能
|
||||
|
||||
/// <summary>
|
||||
/// 显示启动台窗口
|
||||
/// </summary>
|
||||
/// <param name="buttonPosition">按钮在屏幕上的位置</param>
|
||||
public void ShowLauncherWindow(Point buttonPosition)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果窗口已存在,关闭它
|
||||
if (_launcherWindow != null && _launcherWindow.IsVisible)
|
||||
{
|
||||
_launcherWindow.Close();
|
||||
_launcherWindow = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建新的启动台窗口
|
||||
_launcherWindow = new LauncherWindow(this);
|
||||
|
||||
// 计算窗口位置,使其位于按钮上方
|
||||
PositionLauncherWindow(_launcherWindow, buttonPosition);
|
||||
|
||||
// 显示窗口
|
||||
_launcherWindow.Show();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"显示启动台窗口时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置启动台窗口位置
|
||||
/// </summary>
|
||||
/// <param name="window">启动台窗口</param>
|
||||
/// <param name="buttonPosition">按钮在屏幕上的位置</param>
|
||||
private void PositionLauncherWindow(LauncherWindow window, Point buttonPosition)
|
||||
{
|
||||
// 确保窗口已加载
|
||||
if (window.ActualWidth == 0 || window.ActualHeight == 0)
|
||||
{
|
||||
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
|
||||
// 设置窗口加载完成后的位置
|
||||
window.Loaded += (s, e) =>
|
||||
{
|
||||
// 窗口位于按钮上方居中
|
||||
double left = buttonPosition.X - (window.ActualWidth / 2);
|
||||
double top = buttonPosition.Y - window.ActualHeight - 10; // 在按钮上方留出一些间距
|
||||
|
||||
// 确保窗口在屏幕内
|
||||
left = Math.Max(0, Math.Min(left, SystemParameters.WorkArea.Width - window.ActualWidth));
|
||||
top = Math.Max(0, Math.Min(top, SystemParameters.WorkArea.Height - window.ActualHeight));
|
||||
|
||||
window.Left = left;
|
||||
window.Top = top;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// 窗口位于按钮上方居中
|
||||
double left = buttonPosition.X - (window.ActualWidth / 2);
|
||||
double top = buttonPosition.Y - window.ActualHeight - 10; // 在按钮上方留出一些间距
|
||||
|
||||
// 确保窗口在屏幕内
|
||||
left = Math.Max(0, Math.Min(left, SystemParameters.WorkArea.Width - window.ActualWidth));
|
||||
top = Math.Max(0, Math.Min(top, SystemParameters.WorkArea.Height - window.ActualHeight));
|
||||
|
||||
window.Left = left;
|
||||
window.Top = top;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加应用到启动台
|
||||
/// </summary>
|
||||
/// <param name="item">启动台项</param>
|
||||
public void AddLauncherItem(LauncherItem item)
|
||||
{
|
||||
// 如果项目数量已达上限,则不添加
|
||||
if (LauncherItems.Count >= 40)
|
||||
{
|
||||
MessageBox.Show("启动台项目数量已达上限(40个)!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 寻找合适的位置
|
||||
if (item.Position < 0)
|
||||
{
|
||||
item.Position = FindNextAvailablePosition();
|
||||
}
|
||||
|
||||
// 添加项目并保存配置
|
||||
LauncherItems.Add(item);
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找下一个可用位置
|
||||
/// </summary>
|
||||
private int FindNextAvailablePosition()
|
||||
{
|
||||
// 获取已使用的位置列表
|
||||
var usedPositions = new HashSet<int>();
|
||||
foreach (var item in LauncherItems)
|
||||
{
|
||||
usedPositions.Add(item.Position);
|
||||
}
|
||||
|
||||
// 查找第一个可用位置
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
if (!usedPositions.Contains(i))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有位置都已使用,则返回0
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,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,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,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,276 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Ink_Canvas.Helpers.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件模板,用于开发者参考
|
||||
/// 注意:实际开发时,请将此类移到单独的程序集中
|
||||
/// </summary>
|
||||
public class PluginTemplate : PluginBase
|
||||
{
|
||||
#region 插件基本信息
|
||||
|
||||
/// <summary>
|
||||
/// 插件名称
|
||||
/// </summary>
|
||||
public override string Name => "插件模板";
|
||||
|
||||
/// <summary>
|
||||
/// 插件描述
|
||||
/// </summary>
|
||||
public override string Description => "这是一个插件开发模板,用于开发者参考。";
|
||||
|
||||
/// <summary>
|
||||
/// 插件版本
|
||||
/// </summary>
|
||||
public override Version Version => new Version(1, 0, 0);
|
||||
|
||||
/// <summary>
|
||||
/// 插件作者
|
||||
/// </summary>
|
||||
public override string Author => "Your Name";
|
||||
|
||||
/// <summary>
|
||||
/// 是否为内置插件(外部插件请返回false)
|
||||
/// </summary>
|
||||
public override bool IsBuiltIn => false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件生命周期
|
||||
|
||||
/// <summary>
|
||||
/// 插件初始化
|
||||
/// 在这里进行插件的初始化工作,如加载配置、注册事件等
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
// 先调用基类方法,这样会设置插件ID和记录日志
|
||||
base.Initialize();
|
||||
|
||||
// TODO: 在这里进行插件初始化工作
|
||||
|
||||
// 示例:记录初始化信息
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 开始初始化");
|
||||
|
||||
// 示例:加载配置
|
||||
LoadConfig();
|
||||
|
||||
// 示例:注册自定义事件
|
||||
// MainWindow.Instance.SomeEvent += OnSomeEvent;
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 初始化完成");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用插件
|
||||
/// 在这里激活插件功能
|
||||
/// </summary>
|
||||
public override void Enable()
|
||||
{
|
||||
// 先调用基类方法,这样会设置插件状态和记录日志
|
||||
base.Enable();
|
||||
|
||||
// TODO: 在这里启用插件功能
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已启用");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 禁用插件
|
||||
/// 在这里停用插件功能
|
||||
/// </summary>
|
||||
public override void Disable()
|
||||
{
|
||||
// 先调用基类方法,这样会设置插件状态和记录日志
|
||||
base.Disable();
|
||||
|
||||
// TODO: 在这里禁用插件功能
|
||||
|
||||
LogHelper.WriteLogToFile($"插件 {Name} 已禁用");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理资源
|
||||
/// 在插件卸载时调用,清理资源
|
||||
/// </summary>
|
||||
public override void Cleanup()
|
||||
{
|
||||
// TODO: 在这里清理插件资源
|
||||
|
||||
// 示例:取消注册事件
|
||||
// MainWindow.Instance.SomeEvent -= OnSomeEvent;
|
||||
|
||||
// 示例:保存配置
|
||||
SaveConfig();
|
||||
|
||||
// 最后调用基类方法
|
||||
base.Cleanup();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件配置
|
||||
|
||||
/// <summary>
|
||||
/// 加载插件配置
|
||||
/// </summary>
|
||||
private void LoadConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: 从文件或其他位置加载配置
|
||||
// 示例:
|
||||
// string configPath = Path.Combine(App.RootPath, "PluginConfigs", "YourPluginName.json");
|
||||
// if (File.Exists(configPath))
|
||||
// {
|
||||
// string json = File.ReadAllText(configPath);
|
||||
// YourConfig = Newtonsoft.Json.JsonConvert.DeserializeObject<YourConfigClass>(json);
|
||||
// }
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"加载插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存插件配置
|
||||
/// </summary>
|
||||
private void SaveConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: 保存配置到文件或其他位置
|
||||
// 示例:
|
||||
// string configDir = Path.Combine(App.RootPath, "PluginConfigs");
|
||||
// if (!Directory.Exists(configDir))
|
||||
// {
|
||||
// Directory.CreateDirectory(configDir);
|
||||
// }
|
||||
// string configPath = Path.Combine(configDir, "YourPluginName.json");
|
||||
// string json = Newtonsoft.Json.JsonConvert.SerializeObject(YourConfig, Newtonsoft.Json.Formatting.Indented);
|
||||
// File.WriteAllText(configPath, json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件设置界面
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件设置界面
|
||||
/// </summary>
|
||||
/// <returns>插件设置界面</returns>
|
||||
public override UserControl GetSettingsView()
|
||||
{
|
||||
// 创建插件设置界面
|
||||
return new PluginTemplateSettingsControl();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件功能方法
|
||||
|
||||
// TODO: 在这里添加插件的具体功能方法
|
||||
|
||||
/// <summary>
|
||||
/// 示例方法:执行一些功能
|
||||
/// </summary>
|
||||
public void DoSomething()
|
||||
{
|
||||
if (!IsEnabled) return;
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: 实现你的功能
|
||||
MessageBox.Show("插件功能执行示例", "插件模板", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"执行插件功能时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件设置控件
|
||||
/// </summary>
|
||||
public class PluginTemplateSettingsControl : UserControl
|
||||
{
|
||||
public PluginTemplateSettingsControl()
|
||||
{
|
||||
// 创建设置界面布局
|
||||
var panel = new StackPanel
|
||||
{
|
||||
Margin = new Thickness(10)
|
||||
};
|
||||
|
||||
// 添加标题
|
||||
panel.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "插件模板设置",
|
||||
FontSize = 16,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Margin = new Thickness(0, 0, 0, 10)
|
||||
});
|
||||
|
||||
// 添加说明文字
|
||||
panel.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "这是一个示例设置界面,你可以在这里添加自己的设置控件。",
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Margin = new Thickness(0, 0, 0, 15)
|
||||
});
|
||||
|
||||
// 添加示例设置选项
|
||||
var checkBox = new CheckBox
|
||||
{
|
||||
Content = "启用某项功能",
|
||||
Margin = new Thickness(0, 0, 0, 10)
|
||||
};
|
||||
panel.Children.Add(checkBox);
|
||||
|
||||
// 添加文本输入框
|
||||
panel.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "设置项:",
|
||||
Margin = new Thickness(0, 5, 0, 5)
|
||||
});
|
||||
|
||||
panel.Children.Add(new TextBox
|
||||
{
|
||||
Margin = new Thickness(0, 0, 0, 10),
|
||||
Width = 200,
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
});
|
||||
|
||||
// 添加按钮
|
||||
var button = new Button
|
||||
{
|
||||
Content = "保存设置",
|
||||
Padding = new Thickness(10, 5, 10, 5),
|
||||
Margin = new Thickness(0, 10, 0, 0),
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
};
|
||||
|
||||
button.Click += (sender, e) =>
|
||||
{
|
||||
MessageBox.Show("设置已保存!", "插件模板", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
};
|
||||
|
||||
panel.Children.Add(button);
|
||||
|
||||
// 设置控件内容
|
||||
Content = panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
@@ -27,10 +28,7 @@ namespace Ink_Canvas.Helpers
|
||||
//MessageBox.Show("启动失败: " + ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Console.WriteLine(softwareName + " 未找到可执行文件路径。");
|
||||
}
|
||||
//Console.WriteLine(softwareName + " 未找到可执行文件路径。");
|
||||
}
|
||||
|
||||
private static string FindEasiCameraExecutablePath(string softwareName)
|
||||
@@ -51,7 +49,7 @@ namespace Ink_Canvas.Helpers
|
||||
{
|
||||
if (!string.IsNullOrEmpty(installLocation))
|
||||
{
|
||||
executablePath = System.IO.Path.Combine(installLocation, "sweclauncher.exe");
|
||||
executablePath = Path.Combine(installLocation, "sweclauncher.exe");
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(uninstallString))
|
||||
{
|
||||
@@ -59,7 +57,7 @@ namespace Ink_Canvas.Helpers
|
||||
if (lastSlashIndex >= 0)
|
||||
{
|
||||
string folderPath = uninstallString.Substring(0, lastSlashIndex);
|
||||
executablePath = System.IO.Path.Combine(folderPath, "sweclauncher", "sweclauncher.exe");
|
||||
executablePath = Path.Combine(folderPath, "sweclauncher", "sweclauncher.exe");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public static class StartupCount
|
||||
{
|
||||
private static readonly string CountFilePath = Path.Combine(App.RootPath, "startup-count");
|
||||
private static readonly object fileLock = new object();
|
||||
|
||||
public static int GetCount()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(CountFilePath))
|
||||
{
|
||||
var text = File.ReadAllText(CountFilePath).Trim();
|
||||
if (int.TryParse(text, out int count))
|
||||
return count;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void Increment()
|
||||
{
|
||||
lock (fileLock)
|
||||
{
|
||||
int count = GetCount() + 1;
|
||||
try
|
||||
{
|
||||
File.WriteAllText(CountFilePath, count.ToString());
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
lock (fileLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(CountFilePath))
|
||||
File.Delete(CountFilePath);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
// Added for UIElement
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public class TimeMachine
|
||||
public partial class TimeMachine
|
||||
{
|
||||
private readonly List<TimeMachineHistory> _currentStrokeHistory = new List<TimeMachineHistory>();
|
||||
|
||||
@@ -133,12 +135,13 @@ namespace Ink_Canvas.Helpers
|
||||
public class TimeMachineHistory
|
||||
{
|
||||
public TimeMachineHistoryType CommitType;
|
||||
public bool StrokeHasBeenCleared = false;
|
||||
public bool StrokeHasBeenCleared;
|
||||
public StrokeCollection CurrentStroke;
|
||||
public StrokeCollection ReplacedStroke;
|
||||
//这里说一下 Tuple的 Value1 是初始值 ; Value 2 是改变值
|
||||
public Dictionary<Stroke, Tuple<StylusPointCollection, StylusPointCollection>> StylusPointDictionary;
|
||||
public Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>> DrawingAttributes;
|
||||
public UIElement InsertedElement; // 新增
|
||||
public TimeMachineHistory(StrokeCollection currentStroke, TimeMachineHistoryType commitType, bool strokeHasBeenCleared)
|
||||
{
|
||||
CommitType = commitType;
|
||||
@@ -163,6 +166,11 @@ namespace Ink_Canvas.Helpers
|
||||
StrokeHasBeenCleared = strokeHasBeenCleared;
|
||||
ReplacedStroke = replacedStroke;
|
||||
}
|
||||
public TimeMachineHistory(UIElement element, TimeMachineHistoryType commitType) // 新增
|
||||
{
|
||||
CommitType = commitType;
|
||||
InsertedElement = element;
|
||||
}
|
||||
}
|
||||
|
||||
public enum TimeMachineHistoryType
|
||||
@@ -171,6 +179,34 @@ namespace Ink_Canvas.Helpers
|
||||
ShapeRecognition,
|
||||
Clear,
|
||||
Manipulation,
|
||||
DrawingAttributes
|
||||
DrawingAttributes,
|
||||
ElementInsert // 新增
|
||||
}
|
||||
|
||||
public partial class TimeMachine // 新增partial,便于扩展
|
||||
{
|
||||
public void CommitElementInsertHistory(UIElement element)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
_currentStrokeHistory.Add(new TimeMachineHistory(element, TimeMachineHistoryType.ElementInsert));
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public void CommitElementRemoveHistory(UIElement element)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
var history = new TimeMachineHistory(element, TimeMachineHistoryType.ElementInsert);
|
||||
history.StrokeHasBeenCleared = true; // 标记为已清除
|
||||
_currentStrokeHistory.Add(history);
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,12 +113,15 @@
|
||||
<ItemGroup>
|
||||
<Reference Include="IACore">
|
||||
<HintPath>.\IACore.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="IALoader">
|
||||
<HintPath>.\IALoader.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="IAWinFX">
|
||||
<HintPath>.\IAWinFX.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualBasic" />
|
||||
<Reference Include="netstandard" />
|
||||
@@ -132,12 +135,18 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="WindowsFormsIntegration" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.manifest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Costura.Fody" Version="6.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
|
||||
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.9.27" />
|
||||
<PackageReference Include="MdXaml" Version="1.27.0" />
|
||||
@@ -181,6 +190,11 @@
|
||||
<ItemGroup>
|
||||
<None Include="Resources\TimerDownNotice.wav" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\IACore\IACore.dll" />
|
||||
<EmbeddedResource Include="Resources\IACore\IALoader.dll" />
|
||||
<EmbeddedResource Include="Resources\IACore\IAWinFX.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Cursors\Cursor.cur" />
|
||||
<Resource Include="Resources\Cursors\Pen.cur" />
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<AssemblyName>InkCanvasForClass</AssemblyName>
|
||||
<IntermediateOutputPath>obj\Debug\</IntermediateOutputPath>
|
||||
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
|
||||
<MSBuildProjectExtensionsPath>C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\</MSBuildProjectExtensionsPath>
|
||||
<_TargetAssemblyProjectName>InkCanvasForClass</_TargetAssemblyProjectName>
|
||||
<RootNamespace>Ink_Canvas</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk.WindowsDesktop" />
|
||||
<PropertyGroup>
|
||||
<RuntimeIdentifiers>win;win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>Ink_Canvas</RootNamespace>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>2</ApplicationRevision>
|
||||
<ApplicationVersion>2.0.2.%2a</ApplicationVersion>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>false</BootstrapperEnabled>
|
||||
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
|
||||
<UseWPF>true</UseWPF>
|
||||
<Configurations>Debug;Release;x86 Debug</Configurations>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugType>embedded</DebugType>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<Prefer32Bit>True</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86 Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<Prefer32Bit>True</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>embedded</DebugType>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<Prefer32Bit>True</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>Resources\icc.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
|
||||
<DebugType>full</DebugType>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86 Debug|x86'">
|
||||
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
|
||||
<DebugType>full</DebugType>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<Title>InkCanvasForClass</Title>
|
||||
<Version>5.0.4</Version>
|
||||
<Authors>Dubi906w</Authors>
|
||||
<Product>InkCanvasForClass</Product>
|
||||
<Copyright>© Copyright HARKOTEK Studio 2024-now</Copyright>
|
||||
<PackageProjectUrl>https://icc.bliemhax.com</PackageProjectUrl>
|
||||
<FileVersion>bundled</FileVersion>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM64'">
|
||||
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
|
||||
<DebugType>full</DebugType>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86 Debug|ARM64'">
|
||||
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
|
||||
<DebugType>full</DebugType>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM64'">
|
||||
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
|
||||
<DebugType>full</DebugType>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86 Debug|x64'">
|
||||
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
|
||||
<DebugType>full</DebugType>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.manifest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
|
||||
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.9.27" />
|
||||
<PackageReference Include="MdXaml" Version="1.27.0" />
|
||||
<PackageReference Include="Microsoft.Office.Interop.PowerPoint" Version="15.0.4420.1018" />
|
||||
<PackageReference Include="MicrosoftOfficeCore" Version="15.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
|
||||
<PackageReference Include="OSVersionExt" Version="3.0.0" />
|
||||
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="IWshRuntimeLibrary">
|
||||
<Guid>{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="stdole">
|
||||
<Guid>{00020430-0000-0000-C000-000000000046}</Guid>
|
||||
<VersionMajor>2</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>primary</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="VBIDE">
|
||||
<Guid>{0002E157-0000-0000-C000-000000000046}</Guid>
|
||||
<VersionMajor>5</VersionMajor>
|
||||
<VersionMinor>3</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>primary</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\TimerDownNotice.wav" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="MainWindow.xaml~RF6c3144.TMP" />
|
||||
<None Remove="Resources\Cursors\Cursor.cur" />
|
||||
<None Remove="Resources\Cursors\Pen.cur" />
|
||||
<None Remove="Resources\DeveloperAvatars\aaaaaaccd.jpg" />
|
||||
<None Remove="Resources\DeveloperAvatars\Alan-CRL.png" />
|
||||
<None Remove="Resources\DeveloperAvatars\NetheriteBowl.png" />
|
||||
<None Remove="Resources\DeveloperAvatars\NotYoojun.png" />
|
||||
<None Remove="Resources\DeveloperAvatars\RaspberryKan.jpg" />
|
||||
<None Remove="Resources\DeveloperAvatars\wwei.png" />
|
||||
<None Remove="Resources\DeveloperAvatars\yuwenhui2020.png" />
|
||||
<None Remove="Resources\icc.ico" />
|
||||
<None Remove="Resources\Icons-png\AdmoxBooth.png" />
|
||||
<None Remove="Resources\Icons-png\AdmoxWhiteboard.png" />
|
||||
<None Remove="Resources\Icons-png\Donview.png" />
|
||||
<None Remove="Resources\Icons-png\EasiNote3.png" />
|
||||
<None Remove="Resources\Icons-png\HiteAnnotation.png" />
|
||||
<None Remove="Resources\Icons-png\HiteLightBoard.png" />
|
||||
<None Remove="Resources\Icons-png\ica.png" />
|
||||
<None Remove="Resources\Icons-png\icc-transparent-dark-small.png" />
|
||||
<None Remove="Resources\Icons-png\icc-transparent-dark.png" />
|
||||
<None Remove="Resources\Icons-png\icc-transparent.png" />
|
||||
<None Remove="Resources\Icons-png\icc.png" />
|
||||
<None Remove="Resources\Icons-png\idt.png" />
|
||||
<None Remove="Resources\Icons-png\InkCanvas.png" />
|
||||
<None Remove="Resources\Icons-png\kuanciya.png" />
|
||||
<None Remove="Resources\Icons-png\kuandogeyuanliangwo.png" />
|
||||
<None Remove="Resources\Icons-png\kuandoujiyanhuaji.png" />
|
||||
<None Remove="Resources\Icons-png\kuanneikuhuaji.png" />
|
||||
<None Remove="Resources\Icons-png\kuanshounvhuaji.png" />
|
||||
<None Remove="Resources\Icons-png\MaxHubWhiteboard.png" />
|
||||
<None Remove="Resources\Icons-png\Seewo2Annotation.png" />
|
||||
<None Remove="Resources\Icons-png\tiebahuaji.png" />
|
||||
<None Remove="Resources\Icons-png\transparent-grid.png" />
|
||||
<None Remove="Resources\Icons-png\VComYouJiao.png" />
|
||||
<None Remove="Resources\Icons-png\WenXiang.png" />
|
||||
<None Remove="Resources\Icons-png\YiYunVisualPresenter.png" />
|
||||
<None Remove="Resources\Icons-png\YiYunWhiteboard.png" />
|
||||
<None Remove="Resources\new-icons\chevron-left.png" />
|
||||
<None Remove="Resources\new-icons\end-slides-show.png" />
|
||||
<None Remove="Resources\new-icons\eye.png" />
|
||||
<None Remove="Resources\new-icons\hand-move.png" />
|
||||
<None Remove="Resources\new-icons\highlighter-white.png" />
|
||||
<None Remove="Resources\new-icons\multi-touch.png" />
|
||||
<None Remove="Resources\new-icons\osu-lazer-triangles.png" />
|
||||
<None Remove="Resources\new-icons\pen-white.png" />
|
||||
<None Remove="Resources\new-icons\rotate.png" />
|
||||
<None Remove="Resources\new-icons\unfold-chevron.png" />
|
||||
<None Remove="Resources\new-icons\zoom.png" />
|
||||
<None Remove="Resources\PresentationExample\bottombar-dark.png" />
|
||||
<None Remove="Resources\PresentationExample\bottombar-white.png" />
|
||||
<None Remove="Resources\PresentationExample\page.jpg" />
|
||||
<None Remove="Resources\PresentationExample\sidebar-dark.png" />
|
||||
<None Remove="Resources\PresentationExample\sidebar-white.png" />
|
||||
<None Remove="Resources\PresentationExample\toolbar.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\hardcodet.notifyicon.wpf\1.1.0\lib\net472\Hardcodet.NotifyIcon.Wpf.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\IACore.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\IALoader.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\IAWinFX.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\avalonedit\6.3.0.90\lib\net462\ICSharpCode.AvalonEdit.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\inkore.ui.wpf.modern\0.9.27\lib\net452\iNKORE.UI.WPF.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\inkore.ui.wpf.modern\0.9.27\lib\net452\iNKORE.UI.WPF.Modern.Controls.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\inkore.ui.wpf.modern\0.9.27\lib\net452\iNKORE.UI.WPF.Modern.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\mdxaml\1.27.0\lib\net462\MdXaml.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\mdxaml.plugins\1.27.0\lib\net462\MdXaml.Plugins.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\Microsoft.CSharp.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\microsoft.office.interop.powerpoint\15.0.4420.1018\lib\net20\Microsoft.Office.Interop.PowerPoint.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\Microsoft.VisualBasic.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\mscorlib.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\Facades\netstandard.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\newtonsoft.json\13.0.3\lib\net45\Newtonsoft.Json.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\nhotkey\3.0.0\lib\net462\NHotkey.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\nhotkey.wpf\3.0.0\lib\net462\NHotkey.Wpf.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\microsoftofficecore\15.0.0\lib\net35\Office.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\osversionext\3.0.0\lib\net462\OSVersionExt.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\PresentationCore.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\PresentationFramework.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Core.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Data.DataSetExtensions.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Data.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Drawing.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.IO.Compression.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.IO.Compression.FileSystem.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Net.Http.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Numerics.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Runtime.Serialization.dll" />
|
||||
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\system.valuetuple\4.5.0\ref\net47\System.ValueTuple.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Windows.Controls.Ribbon.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Windows.Forms.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Xaml.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Xml.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Xml.Linq.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\UIAutomationClient.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\UIAutomationClientsideProviders.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\UIAutomationProvider.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\UIAutomationTypes.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\WindowsBase.dll" />
|
||||
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\WindowsFormsIntegration.dll" />
|
||||
<ReferencePath Include="C:\Windows\assembly\GAC\stdole\7.0.3300.0__b03f5f7f11d50a3a\stdole.dll">
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</ReferencePath>
|
||||
<ReferencePath Include="C:\Windows\assembly\GAC_MSIL\Microsoft.Vbe.Interop\15.0.0.0__71e9bce111e9429c\Microsoft.Vbe.Interop.dll">
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</ReferencePath>
|
||||
<ReferencePath Include="obj\Debug\net472\Interop.IWshRuntimeLibrary.dll">
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</ReferencePath>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\MainWindow.g.cs" />
|
||||
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\CountdownTimerWindow.g.cs" />
|
||||
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\CycleProcessBar.g.cs" />
|
||||
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\HasNewUpdateWindow.g.cs" />
|
||||
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\NamesInputWindow.g.cs" />
|
||||
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\OperatingGuideWindow.g.cs" />
|
||||
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\RandWindow.g.cs" />
|
||||
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\YesOrNoNotificationWindow.g.cs" />
|
||||
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\App.g.cs" />
|
||||
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\GeneratedInternalTypeHelper.g.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk.WindowsDesktop" />
|
||||
</Project>
|
||||
@@ -33,7 +33,13 @@
|
||||
Stylus.IsTouchFeedbackEnabled="False">
|
||||
<!--资源中添加命令-->
|
||||
<Window.Resources>
|
||||
<c:IsEnabledToOpacityConverter x:Key="IsEnabledToOpacityConverter" />
|
||||
<ResourceDictionary>
|
||||
<!-- 合并新橡皮擦资源 -->
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="MainWindow_cs/MW_Eraser.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<c:IsEnabledToOpacityConverter x:Key="IsEnabledToOpacityConverter" />
|
||||
<c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||
<c:IntNumberToString x:Key="IntNumberToString" />
|
||||
<c:IntNumberToString2 x:Key="IntNumberToString2" />
|
||||
@@ -113,11 +119,15 @@
|
||||
<DataTrigger Binding="{Binding Tag}" Value="about">
|
||||
<Setter Property="Background" Value="#3b82f6"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Tag}" Value="plugins">
|
||||
<Setter Property="Background" Value="#3b82f6"/>
|
||||
</DataTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<!--输入命令绑定-->
|
||||
<Window.InputBindings>
|
||||
@@ -170,7 +180,7 @@
|
||||
<Grid x:Name="Main_Grid">
|
||||
|
||||
<!--// 设置界面 //-->
|
||||
<Grid Panel.ZIndex="999" x:Name="BorderSettingsMask" MouseDown="SettingsOverlayClick" IsHitTestVisible="False"
|
||||
<Grid Panel.ZIndex="999" x:Name="BorderSettingsMask" MouseDown="SettingsOverlayClick" IsHitTestVisible="True"
|
||||
Margin="0,0,0,0">
|
||||
<Border Name="BorderSettings" Background="#ee18181b" ui:ThemeManager.RequestedTheme="Dark" Width="490"
|
||||
HorizontalAlignment="Left" Margin="300,150,0,350" Visibility="Visible">
|
||||
@@ -197,6 +207,21 @@
|
||||
<!-- 主要导航按钮 -->
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel>
|
||||
<!-- Plugins -->
|
||||
<Button Width="40" Height="40" Margin="0,5,0,0" Style="{StaticResource NavButton}"
|
||||
Click="NavPlugins_Click" Tag="plugins" ToolTip="插件">
|
||||
<Image Width="20" Height="20">
|
||||
<Image.Source>
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M342.826667 213.333333a149.333333 149.333333 0 0 1 295.68 0H682.666667a128 128 0 0 1 128 128v44.16a149.333333 149.333333 0 0 1 0 295.68V725.333333a128 128 0 0 1-128 128h-128v-21.333333a64 64 0 0 0-128 0v21.333333H298.666667a128 128 0 0 1-128-128v-128h21.333333a64 64 0 0 0 0-128H170.666667V341.333333a128 128 0 0 1 128-128h44.16zM426.666667 234.666667V298.666667H298.666667q-17.664 0-30.165334 12.501333T256 341.333333v56.576q22.528 10.752 41.6 29.866667Q341.333333 471.466667 341.333333 533.333333q0 61.866667-43.733333 105.6-19.072 19.072-41.6 29.824V725.333333q0 17.664 12.501333 30.165334T298.666667 768h56.576q10.752-22.528 29.866666-41.6Q428.8 682.666667 490.666667 682.666667q61.866667 0 105.6 43.733333 19.072 19.072 29.824 41.6H682.666667q17.664 0 30.165333-12.501333T725.333333 725.333333v-128h64a64 64 0 0 0 0-128H725.333333V341.333333q0-17.664-12.501333-30.165333T682.666667 298.666667h-128V234.666667a64 64 0 1 0-128 0z"/>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
</Image>
|
||||
</Button>
|
||||
|
||||
<!-- Startup -->
|
||||
<Button Width="40" Height="40" Margin="0,10,0,0" Style="{StaticResource NavButton}"
|
||||
Click="NavStartup_Click" Tag="startup" ToolTip="启动设置">
|
||||
@@ -205,7 +230,7 @@
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M2.81,14.12L5.64,11.29L8.17,10.79C11.39,6.41 17.55,4.22 19.78,4.22C19.78,6.45 17.59,12.61 13.21,15.83L12.71,18.36L9.88,21.19L9.17,17.66C7.76,17.66 7.76,17.66 7.05,16.95C6.34,16.24 6.34,16.24 6.34,14.83L2.81,14.12M5.64,16.95L7.05,18.36L4.39,21.03H2.97V19.61L5.64,16.95M4.22,15.54L5.46,15.71L3,18.16V16.74L4.22,15.54M8.29,18.54L8.46,19.78L7.26,21H5.84L8.29,18.54M13,9.5A1.5,1.5 0 0,0 11.5,11A1.5,1.5 0 0,0 13,12.5A1.5,1.5 0 0,0 14.5,11A1.5,1.5 0 0,0 13,9.5Z"/>
|
||||
Geometry="F0 M24,24z M0,0z M6.63415,3.34528C6.87134,3.21274,7.16167,3.21885,7.39307,3.36126L20.3931,11.3613C20.6149,11.4978 20.75,11.7396 20.75,12 20.75,12.2604 20.6149,12.5022 20.3931,12.6387L7.39307,20.6387C7.16167,20.7811 6.87134,20.7873 6.63415,20.6547 6.39696,20.5222 6.25,20.2717 6.25,20L6.25,4C6.25,3.72829,6.39696,3.47783,6.63415,3.34528z M7.75,5.34217L7.75,18.6578 18.569,12 7.75,5.34217z" />
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
@@ -220,7 +245,7 @@
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M19,1L17.74,3.75L15,5L17.74,6.26L19,9L20.25,6.26L23,5L20.25,3.75M9,4L6.5,9.5L1,12L6.5,14.5L9,20L11.5,14.5L17,12L11.5,9.5"/>
|
||||
Geometry="F0 M24,24z M0,0z M4.11612,6.11612C4.35054,5.8817,4.66848,5.75,5,5.75L19,5.75C19.3315,5.75 19.6495,5.8817 19.8839,6.11612 20.1183,6.35054 20.25,6.66848 20.25,7L20.25,18C20.25,18.0663 20.2237,18.1299 20.1768,18.1768 20.1299,18.2237 20.0663,18.25 20,18.25 19.5858,18.25 19.25,18.5858 19.25,19 19.25,19.4142 19.5858,19.75 20,19.75 20.4641,19.75 20.9092,19.5656 21.2374,19.2374 21.5656,18.9092 21.75,18.4641 21.75,18L21.75,7C21.75,6.27065 21.4603,5.57118 20.9445,5.05546 20.4288,4.53973 19.7293,4.25 19,4.25L5,4.25C4.27065,4.25 3.57118,4.53973 3.05546,5.05546 2.53973,5.57118 2.25,6.27065 2.25,7L2.25,17C2.25,17.7293 2.53973,18.4288 3.05546,18.9445 3.57118,19.4603 4.27065,19.75 5,19.75L8,19.75C8.41421,19.75 8.75,19.4142 8.75,19 8.75,18.5858 8.41421,18.25 8,18.25L5,18.25C4.66848,18.25 4.35054,18.1183 4.11612,17.8839 3.8817,17.6495 3.75,17.3315 3.75,17L3.75,7C3.75,6.66848,3.8817,6.35054,4.11612,6.11612z M11.8232,16.8232C11.8701,16.7763,11.9337,16.75,12,16.75L16,16.75C16.0663,16.75 16.1299,16.7763 16.1768,16.8232 16.2237,16.8701 16.25,16.9337 16.25,17L16.25,18C16.25,18.0663 16.2237,18.1299 16.1768,18.1768 16.1299,18.2237 16.0663,18.25 16,18.25L12,18.25C11.9337,18.25 11.8701,18.2237 11.8232,18.1768 11.7763,18.1299 11.75,18.0663 11.75,18L11.75,17C11.75,16.9337,11.7763,16.8701,11.8232,16.8232z M12,15.25C11.5359,15.25 11.0908,15.4344 10.7626,15.7626 10.4344,16.0908 10.25,16.5359 10.25,17L10.25,18C10.25,18.4641 10.4344,18.9092 10.7626,19.2374 11.0908,19.5656 11.5359,19.75 12,19.75L16,19.75C16.4641,19.75 16.9092,19.5656 17.2374,19.2374 17.5656,18.9092 17.75,18.4641 17.75,18L17.75,17C17.75,16.5359 17.5656,16.0908 17.2374,15.7626 16.9092,15.4344 16.4641,15.25 16,15.25L12,15.25z" />
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
@@ -235,7 +260,7 @@
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M13,6V11H18V7.75L22.25,12L18,16.25V13H13V18H16.25L12,22.25L7.75,18H11V13H6V16.25L1.75,12L6,7.75V11H11V6H7.75L12,1.75L16.25,6H13Z"/>
|
||||
Geometry="F0 M24,24z M0,0z M7.98145,10.6889L7.98145,3.97896C7.98145,3.37896 8.21145,2.80897 8.64145,2.38896 9.49145,1.53896 10.9714,1.53896 11.8214,2.38896 12.2414,2.80897 12.4814,3.37896 12.4814,3.97896L12.4814,6.87304C13.2752,6.60639 14.2109,6.77837 14.8214,7.38896 14.9934,7.56094 15.1352,7.75807 15.2427,7.97211 16.0798,7.5855 17.157,7.72439 17.8214,8.38879 17.9978,8.56516 18.1424,8.76797 18.2509,8.98835 19.0867,8.60643 20.1592,8.74658 20.8214,9.40881 21.2414,9.82881 21.4814,10.3988 21.4814,10.9988L21.4814,15.4988C21.4814,17.2788 20.7614,19.0188 19.5014,20.2688 18.2314,21.5488 16.5314,22.2488 14.7314,22.2488L14.7314,22.2288 12.7314,22.2288 12.6214,22.2288C11.6114,22.1788 10.6214,21.9088 9.73138,21.4288 8.75138,20.8988 7.91138,20.1288 7.29138,19.1988L7.09138,18.8988C6.76138,18.3988 5.68138,16.5088 3.78138,13.1288 3.49138,12.6188 3.41138,12.0188 3.57138,11.4488 3.72138,10.8788 4.08138,10.3988 4.59138,10.0988 5.09138,9.79881 5.68138,9.67881 6.26138,9.74881 6.84138,9.81881 7.38138,10.0888 7.79138,10.4988L7.98145,10.6889z M16.9814,11.0086L16.9814,11.4788C16.9814,11.8888 17.3214,12.2288 17.7314,12.2288 18.1414,12.2288 18.4814,11.8888 18.4814,11.4788L18.4814,10.9889C18.484,10.7926 18.5637,10.6065 18.7014,10.4688 18.9814,10.1888 19.4814,10.1888 19.7614,10.4688 19.9014,10.6088 19.9814,10.7988 19.9814,10.9988L19.9814,15.4988C19.9814,16.8788 19.4214,18.2288 18.4414,19.2088 17.4514,20.1988 16.1314,20.7488 14.7314,20.7488L12.9414,20.7488C12.0714,20.7488 11.2114,20.5288 10.4514,20.1188 9.69138,19.7088 9.04138,19.1088 8.56138,18.3888L8.36138,18.0888C8.06138,17.6188 6.96138,15.7088 5.10138,12.3988 5.00138,12.2288 4.98138,12.0288 5.03138,11.8388 5.08138,11.6488 5.20138,11.4888 5.37138,11.3888 5.58138,11.2588 5.83138,11.2088 6.08138,11.2388 6.32138,11.2688 6.55138,11.3888 6.73138,11.5588L8.20138,13.0288C8.49138,13.3188 8.97138,13.3188 9.26138,13.0288 9.38653,12.9037 9.45767,12.7431 9.4748,12.5778 9.47918,12.5454 9.48145,12.5124 9.48145,12.479L9.48145,3.97896C9.48145,3.77897 9.56145,3.58896 9.70145,3.44896 9.98145,3.16896 10.4814,3.16896 10.7614,3.44896 10.9014,3.58896 10.9814,3.77897 10.9814,3.97896L10.9814,8.97896 10.9814,10.979 10.9814,11.479C10.9814,11.889 11.3214,12.229 11.7314,12.229 12.1414,12.229 12.4814,11.889 12.4814,11.479L12.4814,10.979 12.4814,8.97896C12.4814,8.77897 12.5614,8.58897 12.7014,8.44897 12.9814,8.16897 13.4814,8.16897 13.7614,8.44897 13.9014,8.58897 13.9814,8.77897 13.9814,8.97896L13.9814,9.97879 13.9814,11.479C13.9814,11.889 14.3214,12.229 14.7314,12.229 15.1414,12.229 15.4814,11.889 15.4814,11.479L15.4814,9.97879C15.4814,9.77879 15.5614,9.58879 15.7014,9.44879 15.9814,9.16879 16.4814,9.16879 16.7614,9.44879 16.9014,9.58879 16.9814,9.77879 16.9814,9.97879L16.9814,10.9814 16.9814,10.9988 16.9814,11.0086z M3.27144,5.81876C3.04144,5.81876 2.81144,5.70876 2.66144,5.50876 2.42144,5.17876 2.49144,4.70876 2.82144,4.45876 3.63144,3.86876 4.50144,3.35876 5.42144,2.94876 5.80144,2.77876 6.24144,2.94876 6.41144,3.32875 6.58144,3.70876 6.41144,4.14876 6.03144,4.31876 5.21144,4.68876 4.43144,5.13875 3.71144,5.66876 3.58144,5.76876 3.42144,5.80875 3.27144,5.80875L3.27144,5.81876z M18.0113,5.17885C18.1413,5.25885,18.2713,5.29885,18.4113,5.29885L18.4113,5.28885C18.6613,5.28885 18.9113,5.16885 19.0513,4.93885 19.2713,4.58885 19.1613,4.12885 18.8113,3.90885 17.6013,3.14885 16.3013,2.57885 14.9213,2.20885 14.5113,2.09885 14.1113,2.33885 14.0013,2.73885 13.8913,3.13885 14.1313,3.54885 14.5313,3.65885 15.7613,3.98885 16.9313,4.49885 18.0113,5.17885z" />
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
@@ -250,7 +275,7 @@
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z"/>
|
||||
Geometry="F0 M24,24z M0,0z M15.1306,4.19378C15.5647,4.01395 16.0301,3.92139 16.5,3.92139 16.9699,3.92139 17.4353,4.01395 17.8694,4.19378 18.3036,4.37361 18.698,4.6372 19.0303,4.96948 19.3626,5.30177 19.6262,5.69625 19.806,6.13041 19.9859,6.56457 20.0784,7.02989 20.0784,7.49981 20.0784,7.96974 19.9859,8.43506 19.806,8.86922 19.6262,9.30337 19.3626,9.69786 19.0303,10.0301L18.0403,11.0202 18.0303,11.0303 18.0202,11.0403 8.53033,20.5301C8.38968,20.6708,8.19891,20.7498,8,20.7498L4,20.7498C3.58579,20.7498,3.25,20.414,3.25,19.9998L3.25,15.9998C3.25,15.8009,3.32902,15.6101,3.46967,15.4695L13.9697,4.96948C14.302,4.6372,14.6964,4.37361,15.1306,4.19378z M17.9697,8.96948L17.4999,9.43925 14.5606,6.49991 15.0303,6.03014C15.2233,5.83714 15.4525,5.68405 15.7046,5.5796 15.9568,5.47515 16.2271,5.42139 16.5,5.42139 16.7729,5.42139 17.0432,5.47515 17.2954,5.5796 17.5475,5.68405 17.7767,5.83714 17.9697,6.03014 18.1627,6.22314 18.3158,6.45227 18.4202,6.70443 18.5247,6.9566 18.5784,7.22687 18.5784,7.49981 18.5784,7.77276 18.5247,8.04303 18.4202,8.29519 18.3158,8.54736 18.1627,8.77648 17.9697,8.96948z M4.75,16.3105L13.4999,7.56057 16.4392,10.4999 7.68934,19.2498 4.75,19.2498 4.75,16.3105z M21.5303,17.5303C21.8232,17.2374 21.8232,16.7626 21.5303,16.4697 21.2374,16.1768 20.7626,16.1768 20.4697,16.4697L17,19.9393 15.5303,18.4697C15.2374,18.1768 14.7626,18.1768 14.4697,18.4697 14.1768,18.7626 14.1768,19.2374 14.4697,19.5303L16.4697,21.5303C16.7626,21.8232,17.2374,21.8232,17.5303,21.5303L21.5303,17.5303z" />
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
@@ -280,7 +305,7 @@
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M2 2H22V4H2V2M2 8H22V10H2V8M2 14H22V16H2V14M2 20H22V22H2V20Z"/>
|
||||
Geometry="F0 M24,24z M0,0z M12.6955,2.31464C12.8562,2.2432,13.037,2.23053,13.2061,2.27886L20.2061,4.27886C20.5281,4.37085,20.7501,4.66514,20.7501,5L20.7501,18C20.7501,18.3,20.5713,18.5712,20.2955,18.6894L13.2955,21.6894C13.1267,21.7617,12.9372,21.7696,12.7629,21.7115L3.7629,18.7115C3.41393,18.5952 3.20083,18.243 3.25975,17.8799 3.31867,17.5168 3.63223,17.25 4.00007,17.25L12.2501,17.25 12.2501,7.10778 8.75007,8.50778 8.75007,13C8.75007,13.2841,8.58957,13.5438,8.33548,13.6708L4.33548,15.6708C4.10299,15.7871 3.82688,15.7746 3.60577,15.638 3.38466,15.5013 3.25007,15.2599 3.25007,15L3.25007,7C3.25007,6.70361,3.42462,6.43502,3.69546,6.31464L12.6955,2.31464z M13.0558,3.79595L4.75007,7.48741 4.75007,13.7865 7.25007,12.5365 7.25007,8C7.25007,7.69332,7.43678,7.41754,7.72153,7.30364L12.7215,5.30364C12.9526,5.21122 13.2145,5.23943 13.4205,5.37895 13.6266,5.51847 13.7501,5.75113 13.7501,6L13.7501,18C13.7501,18.4142,13.4143,18.75,13.0001,18.75L8.62178,18.75 12.9667,20.1983 19.2501,17.5055 19.2501,5.56573 13.0558,3.79595z" />
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
@@ -295,7 +320,7 @@
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"/>
|
||||
Geometry="F0 M24,24z M0,0z M7.8679,3.80724L10.5302,6.46952C10.6708,6.61018,10.7499,6.80094,10.7499,6.99985L10.7499,9.99985C10.7499,10.4141,10.4141,10.7499,9.99985,10.7499L6.99985,10.7499C6.80094,10.7499,6.61018,10.6708,6.46952,10.5302L3.80724,7.8679C3.64358,8.5543 3.61907,9.27047 3.73968,9.97343 3.92327,11.0435 4.43407,12.0303 5.20176,12.798 5.96944,13.5656 6.95625,14.0764 8.02628,14.26 9.09632,14.4436 10.197,14.291 11.1766,13.8231 11.4634,13.6861 11.8054,13.7448 12.0302,13.9695L18.0302,19.9695C18.2874,20.2267 18.6362,20.3712 18.9999,20.3712 19.3636,20.3712 19.7124,20.2267 19.9695,19.9695 20.2267,19.7124 20.3712,19.3636 20.3712,18.9999 20.3712,18.6362 20.2267,18.2874 19.9695,18.0302L13.9695,12.0302C13.7448,11.8054 13.6861,11.4634 13.8231,11.1766 14.291,10.197 14.4436,9.09632 14.26,8.02628 14.0764,6.95625 13.5656,5.96944 12.798,5.20176 12.0303,4.43407 11.0435,3.92327 9.97343,3.73968 9.27047,3.61907 8.5543,3.64358 7.8679,3.80724z M6.17663,2.82308C7.43621,2.22151 8.85132,2.02523 10.2271,2.26128 11.6028,2.49732 12.8716,3.15407 13.8586,4.1411 14.8456,5.12812 15.5024,6.39687 15.7384,7.77263 15.944,8.97097 15.8216,10.1992 15.3891,11.3284L21.0302,16.9695C21.5687,17.508 21.8712,18.2383 21.8712,18.9999 21.8712,19.7614 21.5687,20.4917 21.0302,21.0302 20.4917,21.5687 19.7614,21.8712 18.9999,21.8712 18.2383,21.8712 17.508,21.5687 16.9695,21.0302L11.3284,15.3891C10.1992,15.8216 8.97097,15.944 7.77263,15.7384 6.39687,15.5024 5.12812,14.8456 4.1411,13.8586 3.15407,12.8716 2.49732,11.6028 2.26128,10.2271 2.02523,8.85132 2.22151,7.43621 2.82308,6.17663 2.92801,5.95693 3.13306,5.80183 3.37303,5.76066 3.613,5.71948 3.85802,5.79736 4.03018,5.96952L7.31051,9.24985 9.24985,9.24985 9.24985,7.31051 5.96952,4.03018C5.79736,3.85802 5.71948,3.613 5.76066,3.37303 5.80183,3.13306 5.95693,2.92801 6.17663,2.82308z" />
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
@@ -310,7 +335,7 @@
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M12,0C8.96,0 6.21,1.23 4.22,3.22L5.63,4.63C7.24,3.01 9.5,2 12,2C14.5,2 16.76,3.01 18.36,4.63L19.77,3.22C17.79,1.23 15.04,0 12,0M7.05,6.05L8.46,7.46C9.37,6.56 10.62,6 12,6C13.38,6 14.63,6.56 15.54,7.46L16.95,6.05C15.68,4.78 13.93,4 12,4C10.07,4 8.32,4.78 7.05,6.05M12,15A2,2 0 0,1 10,13A2,2 0 0,1 12,11A2,2 0 0,1 14,13A2,2 0 0,1 12,15M15,9H9A1,1 0 0,0 8,10V22A1,1 0 0,0 9,23H15A1,1 0 0,0 16,22V10A1,1 0 0,0 15,9Z"/>
|
||||
Geometry="F0 M24,24z M0,0z M7.81828,2.27257C8.22012,2.17211,8.62732,2.41643,8.72778,2.81828L9.08572,4.25 14.9146,4.25 15.2726,2.81828C15.373,2.41643 15.7802,2.17211 16.1821,2.27257 16.5839,2.37303 16.8282,2.78023 16.7278,3.18208L16.4608,4.25 18,4.25C18.7293,4.25 19.4288,4.53973 19.9445,5.05546 20.4603,5.57118 20.75,6.27065 20.75,7L20.75,19C20.75,19.7293 20.4603,20.4288 19.9445,20.9445 19.4288,21.4603 18.7293,21.75 18,21.75L6,21.75C5.27065,21.75 4.57118,21.4603 4.05546,20.9445 3.53973,20.4288 3.25,19.7293 3.25,19L3.25,7C3.25,6.27065 3.53973,5.57118 4.05546,5.05546 4.57118,4.53973 5.27065,4.25 6,4.25L7.53955,4.25 7.27257,3.18208C7.17211,2.78023,7.41643,2.37303,7.81828,2.27257z M14.5396,5.75L14.2726,6.81828C14.1721,7.22012 14.4164,7.62732 14.8183,7.72778 15.2201,7.82825 15.6273,7.58393 15.7278,7.18208L16.0858,5.75 18,5.75C18.3315,5.75 18.6495,5.8817 18.8839,6.11612 19.1183,6.35054 19.25,6.66848 19.25,7L19.25,19C19.25,19.3315 19.1183,19.6495 18.8839,19.8839 18.6495,20.1183 18.3315,20.25 18,20.25L6,20.25C5.66848,20.25 5.35054,20.1183 5.11612,19.8839 4.8817,19.6495 4.75,19.3315 4.75,19L4.75,7C4.75,6.66848 4.8817,6.35054 5.11612,6.11612 5.35054,5.8817 5.66848,5.75 6,5.75L7.91455,5.75 8.27257,7.18208C8.37303,7.58393 8.78023,7.82825 9.18208,7.72778 9.58393,7.62732 9.82825,7.22012 9.72778,6.81828L9.46072,5.75 14.5396,5.75z M9.41625,15.3761C9.07166,15.1463 8.60598,15.2393 8.37614,15.5839 8.14629,15.9285 8.23932,16.3942 8.58391,16.624 9.6821,17.3565 10.8245,17.7501 12.0001,17.7501 13.1757,17.7501 14.3181,17.3565 15.4162,16.624 15.7608,16.3942 15.8539,15.9285 15.624,15.5839 15.3942,15.2393 14.9285,15.1463 14.5839,15.3761 13.6821,15.9776 12.8245,16.2501 12.0001,16.2501 11.1757,16.2501 10.3181,15.9776 9.41625,15.3761z M9,10.25C9.41421,10.25,9.75,10.5858,9.75,11L9.75,12C9.75,12.4142 9.41421,12.75 9,12.75 8.58579,12.75 8.25,12.4142 8.25,12L8.25,11C8.25,10.5858,8.58579,10.25,9,10.25z M15.75,11C15.75,10.5858 15.4142,10.25 15,10.25 14.5858,10.25 14.25,10.5858 14.25,11L14.25,12C14.25,12.4142 14.5858,12.75 15,12.75 15.4142,12.75 15.75,12.4142 15.75,12L15.75,11z" />
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
@@ -325,7 +350,7 @@
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M4,2H20A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H16L12,22L8,18H4A2,2 0 0,1 2,16V4A2,2 0 0,1 4,2M4,4V16H8.83L12,19.17L15.17,16H20V4H4M6,7H18V9H6V7M6,11H16V13H6V11Z"/>
|
||||
Geometry="F0 M24,24z M0,0z M18.5303,3.46967C18.2374,3.17678 17.7626,3.17678 17.4697,3.46967 17.1768,3.76256 17.1768,4.23744 17.4697,4.53033L19.1893,6.25 16,6.25C14.475,6.25 13.0125,6.8558 11.9341,7.93414 11.5659,8.30239 11.2527,8.71546 11,9.16052 10.7473,8.71546 10.4341,8.30239 10.0659,7.93414 8.98753,6.8558 7.52499,6.25 6,6.25L3,6.25C2.58579,6.25 2.25,6.58579 2.25,7 2.25,7.41421 2.58579,7.75 3,7.75L6,7.75C7.12717,7.75 8.20817,8.19777 9.0052,8.9948 9.80223,9.79183 10.25,10.8728 10.25,12 10.25,13.1272 9.80223,14.2082 9.0052,15.0052 8.20817,15.8022 7.12717,16.25 6,16.25L3,16.25C2.58579,16.25 2.25,16.5858 2.25,17 2.25,17.4142 2.58579,17.75 3,17.75L6,17.75C7.52499,17.75 8.98753,17.1442 10.0659,16.0659 10.4341,15.6976 10.7473,15.2845 11,14.8395 11.2527,15.2845 11.5659,15.6976 11.9341,16.0659 13.0125,17.1442 14.475,17.75 16,17.75L19.1893,17.75 17.4697,19.4697C17.1768,19.7626 17.1768,20.2374 17.4697,20.5303 17.7626,20.8232 18.2374,20.8232 18.5303,20.5303L21.5303,17.5303C21.6022,17.4584 21.6565,17.3755 21.6931,17.2871 21.7298,17.1987 21.75,17.1017 21.75,17 21.75,16.8081 21.6768,16.6161 21.5303,16.4697L18.5303,13.4697C18.2374,13.1768 17.7626,13.1768 17.4697,13.4697 17.1768,13.7626 17.1768,14.2374 17.4697,14.5303L19.1893,16.25 16,16.25C14.8728,16.25 13.7918,15.8022 12.9948,15.0052 12.1978,14.2082 11.75,13.1272 11.75,12 11.75,10.8728 12.1978,9.79183 12.9948,8.9948 13.7918,8.19777 14.8728,7.75 16,7.75L19.1893,7.75 17.4697,9.46967C17.1768,9.76256 17.1768,10.2374 17.4697,10.5303 17.7626,10.8232 18.2374,10.8232 18.5303,10.5303L21.5303,7.53033C21.6768,7.38388 21.75,7.19194 21.75,7 21.75,6.89831 21.7298,6.80134 21.6931,6.71291 21.6565,6.62445 21.6022,6.54158 21.5303,6.46967L18.5303,3.46967z" />
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
@@ -340,7 +365,7 @@
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M12,18V6A6,6 0 0,1 18,12A6,6 0 0,1 12,18M20,15.31L23.31,12L20,8.69V4H15.31L12,0.69L8.69,4H4V8.69L0.69,12L4,15.31V20H8.69L12,23.31L15.31,20H20V15.31Z"/>
|
||||
Geometry="F0 M24,24z M0,0z M9.43853,3.39157C9.63411,3.53254 9.75,3.75892 9.75,4.00001 9.75,4.59674 9.98705,5.16904 10.409,5.591 10.831,6.01295 11.4033,6.25001 12,6.25001 12.5967,6.25001 13.169,6.01295 13.591,5.591 14.0129,5.16904 14.25,4.59674 14.25,4.00001 14.25,3.75892 14.3659,3.53254 14.5615,3.39157 14.757,3.25061 15.0085,3.21226 15.2372,3.28849L21.2372,5.28849C21.5434,5.39058,21.75,5.67718,21.75,6.00001L21.75,11C21.75,11.4142,21.4142,11.75,21,11.75L18.75,11.75 18.75,19C18.75,19.4641 18.5656,19.9093 18.2374,20.2374 17.9092,20.5656 17.4641,20.75 17,20.75L7,20.75C6.53587,20.75 6.09075,20.5656 5.76256,20.2374 5.43437,19.9093 5.25,19.4641 5.25,19L5.25,11.75 3,11.75C2.58579,11.75,2.25,11.4142,2.25,11L2.25,6.00001C2.25,5.67718,2.45657,5.39058,2.76283,5.28849L8.76283,3.28849C8.99154,3.21226,9.24296,3.25061,9.43853,3.39157z M3.75,6.54058L3.75,10.25 6,10.25C6.41421,10.25,6.75,10.5858,6.75,11L6.75,19C6.75,19.0663 6.77634,19.1299 6.82322,19.1768 6.87011,19.2237 6.93369,19.25 7,19.25L17,19.25C17.0663,19.25 17.1299,19.2237 17.1768,19.1768 17.2237,19.1299 17.25,19.0663 17.25,19L17.25,11C17.25,10.5858,17.5858,10.25,18,10.25L20.25,10.25 20.25,6.54058 15.6154,4.99571C15.4444,5.61643 15.1149,6.1884 14.6517,6.65166 13.9484,7.35492 12.9946,7.75001 12,7.75001 11.0054,7.75001 10.0516,7.35492 9.34835,6.65166 8.8851,6.1884 8.55557,5.61643 8.3846,4.99571L3.75,6.54058z" />
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
@@ -370,7 +395,7 @@
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z"/>
|
||||
Geometry="F0 M24,24z M0,0z M5.10571,5.10571C6.93419,3.27723 9.41414,2.25 12,2.25 14.5859,2.25 17.0658,3.27723 18.8943,5.10571 20.7228,6.93419 21.75,9.41414 21.75,12 21.75,13.2804 21.4978,14.5482 21.0078,15.7312 20.5178,16.9141 19.7997,17.9889 18.8943,18.8943 17.9889,19.7997 16.9141,20.5178 15.7312,21.0078 14.5482,21.4978 13.2804,21.75 12,21.75 10.7196,21.75 9.45176,21.4978 8.26884,21.0078 7.08591,20.5178 6.01108,19.7997 5.10571,18.8943 4.20034,17.9889 3.48216,16.9141 2.99217,15.7312 2.50219,14.5482 2.25,13.2804 2.25,12 2.25,9.41414 3.27723,6.93419 5.10571,5.10571z M12,3.75C9.81196,3.75 7.71354,4.61919 6.16637,6.16637 4.61919,7.71354 3.75,9.81196 3.75,12 3.75,13.0834 3.96339,14.1562 4.37799,15.1571 4.79259,16.1581 5.40029,17.0675 6.16637,17.8336 6.93245,18.5997 7.84193,19.2074 8.84286,19.622 9.8438,20.0366 10.9166,20.25 12,20.25 13.0834,20.25 14.1562,20.0366 15.1571,19.622 16.1581,19.2074 17.0675,18.5997 17.8336,17.8336 18.5997,17.0675 19.2074,16.1581 19.622,15.1571 20.0366,14.1562 20.25,13.0834 20.25,12 20.25,9.81196 19.3808,7.71354 17.8336,6.16637 16.2865,4.61919 14.188,3.75 12,3.75z M11.25,9C11.25,8.58579,11.5858,8.25,12,8.25L12.01,8.25C12.4242,8.25 12.76,8.58579 12.76,9 12.76,9.41421 12.4242,9.75 12.01,9.75L12,9.75C11.5858,9.75,11.25,9.41421,11.25,9z M11,11.25C10.5858,11.25 10.25,11.5858 10.25,12 10.25,12.4142 10.5858,12.75 11,12.75L11.25,12.75 11.25,16C11.25,16.4142,11.5858,16.75,12,16.75L13,16.75C13.4142,16.75 13.75,16.4142 13.75,16 13.75,15.5858 13.4142,15.25 13,15.25L12.75,15.25 12.75,12C12.75,11.5858,12.4142,11.25,12,11.25L11,11.25z" />
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
@@ -572,96 +597,66 @@
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
<Border Margin="0,0,0,10" Height="100" CornerRadius="5" BorderBrush="#a1a1aa"
|
||||
BorderThickness="1">
|
||||
<ui:SimpleStackPanel VerticalAlignment="Center">
|
||||
<TextBlock Foreground="#fafafa" HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" FontSize="15" Margin="0,0,0,10"
|
||||
Text="开发中...请不要点击,可能会导致ICC异常崩溃" />
|
||||
<ui:SimpleStackPanel Spacing="5">
|
||||
<ui:SimpleStackPanel Spacing="5" Orientation="Horizontal"
|
||||
HorizontalAlignment="Center">
|
||||
<Button Width="116" Height="45" FontFamily="Microsoft YaHei UI"
|
||||
Click="BtnRestart_Click">
|
||||
<Button.Resources>
|
||||
</Button.Resources>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center" Spacing="0">
|
||||
<Image RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Margin="0,0,6,0" Height="20" Width="20">
|
||||
<Image.Source>
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M6.34315,6.34315C7.84299,4.8433 9.87707,4.0005 11.9981,4 14.2527,4.00897 16.4167,4.88785 18.039,6.45324L18.5858,7 16,7C15.4477,7 15,7.44772 15,8 15,8.55228 15.4477,9 16,9L21,9C21.1356,9 21.2649,8.97301 21.3828,8.92412 21.5007,8.87532 21.6112,8.80298 21.7071,8.70711 21.8902,8.52405 21.9874,8.28768 21.9989,8.04797 21.9996,8.03199 22,8.016 22,8L22,3C22,2.44772 21.5523,2 21,2 20.4477,2 20,2.44772 20,3L20,5.58579 19.4471,5.03289 19.435,5.02103C17.4405,3.09289,14.7779,2.01044,12.0038,2L12,2C9.34784,2 6.8043,3.05357 4.92893,4.92893 3.05357,6.8043 2,9.34784 2,12 2,12.5523 2.44772,13 3,13 3.55228,13 4,12.5523 4,12 4,9.87827 4.84285,7.84344 6.34315,6.34315z" />
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M22,12C22,14.6522 20.9464,17.1957 19.0711,19.0711 17.1957,20.9464 14.6522,22 12,22L11.9962,22C9.22213,21.9896,6.55946,20.9071,4.56496,18.979L4.55289,18.9671 4,18.4142 4,21C4,21.5523 3.55228,22 3,22 2.44772,22 2,21.5523 2,21L2,16.0002C2,15.8646 2.02699,15.7351 2.07588,15.6172 2.12432,15.5001 2.19595,15.3904 2.29078,15.295 2.29219,15.2936 2.2936,15.2922 2.29502,15.2908 2.48924,15.0977 2.74301,15.0008 2.997,15 2.998,15 2.999,15 3,15L8,15C8.55228,15 9,15.4477 9,16 9,16.5523 8.55228,17 8,17L5.41421,17 5.96095,17.5467C7.5833,19.1122 9.74736,19.9911 12.002,20 14.123,19.9995 16.157,19.1567 17.6569,17.6569 19.1571,16.1566 20,14.1217 20,12 20,11.4477 20.4477,11 21,11 21.5523,11 22,11.4477 22,12z" />
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<Label FontSize="16" Foreground="#fafafa"
|
||||
VerticalAlignment="Center" FontFamily="Microsoft YaHei UI"
|
||||
FontWeight="Bold">
|
||||
测试
|
||||
</Label>
|
||||
</ui:SimpleStackPanel>
|
||||
</Button>
|
||||
<Button Width="116" Height="45" FontFamily="Microsoft YaHei UI"
|
||||
Click="BtnResetToSuggestion_Click"
|
||||
Margin="0,0,0,0">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center" Spacing="0">
|
||||
<Image
|
||||
Margin="0,0,4,0" RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Height="20" Width="20">
|
||||
<Image.Source>
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M2,6C2,5.44772,2.44772,5,3,5L21,5C21.5523,5 22,5.44772 22,6 22,6.55228 21.5523,7 21,7L3,7C2.44772,7,2,6.55228,2,6z" />
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M2,12C2,11.4477,2.44772,11,3,11L7,11C7.55228,11 8,11.4477 8,12 8,12.5523 7.55228,13 7,13L3,13C2.44772,13,2,12.5523,2,12z" />
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M3,17C2.44772,17 2,17.4477 2,18 2,18.5523 2.44772,19 3,19L7,19C7.55228,19 8,18.5523 8,18 8,17.4477 7.55228,17 7,17L3,17z" />
|
||||
<GeometryDrawing Brush="White"
|
||||
Geometry="F1 M24,24z M0,0z M12.3829,11.2029C13.4335,10.1522 14.8952,9.5 16.5,9.5 17.9587,9.5 19.3576,10.0795 20.3891,11.1109 21.4205,12.1424 22,13.5413 22,15 22,16.2593 21.6038,17.4867 20.8675,18.5083 20.1311,19.5299 19.092,20.2939 17.8974,20.6921 16.7027,21.0903 15.413,21.1026 14.211,20.7271 13.009,20.3516 11.9556,19.6074 11.2,18.6 10.8686,18.1582 10.9582,17.5314 11.4,17.2 11.8418,16.8686 12.4686,16.9582 12.8,17.4 13.3037,18.0716 14.006,18.5677 14.8073,18.8181 15.6087,19.0684 16.4685,19.0602 17.2649,18.7947 18.0614,18.5292 18.7541,18.0199 19.245,17.3388 19.7359,16.6578 20,15.8395 20,15 20,14.0717 19.6313,13.1815 18.9749,12.5251 18.3185,11.8687 17.4283,11.5 16.5,11.5 15.4448,11.5 14.4865,11.9278 13.7971,12.6171L13.4142,13 15,13C15.5523,13 16,13.4477 16,14 16,14.5523 15.5523,15 15,15L11.0007,15C10.9997,15 10.998,15 10.997,15 10.8625,14.9996 10.7343,14.9727 10.6172,14.9241 10.5001,14.8757 10.3904,14.804 10.295,14.7092 10.2936,14.7078 10.2922,14.7064 10.2908,14.705 10.196,14.6096 10.1243,14.4999 10.0759,14.3828 10.027,14.2649 10,14.1356 10,14L10,10C10,9.44772 10.4477,9 11,9 11.5523,9 12,9.44772 12,10L12,11.5858 12.3829,11.2029z" />
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<Label Margin="2,0,0,0" FontSize="16" VerticalAlignment="Center"
|
||||
FontFamily="Microsoft YaHei UI">
|
||||
测试
|
||||
</Label>
|
||||
</ui:SimpleStackPanel>
|
||||
</Button>
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
<GroupBox Name="GroupBoxPlugins">
|
||||
<GroupBox.Header>
|
||||
<TextBlock Margin="0,12,0,0" Text="插件管理" FontWeight="Bold" Foreground="#fafafa"
|
||||
FontSize="26" />
|
||||
</GroupBox.Header>
|
||||
<ui:SimpleStackPanel Spacing="6" Margin="0,10,0,0">
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10" Foreground="#fafafa">
|
||||
通过插件扩展InkCanvas的功能。您可以启用或禁用插件,或加载自定义插件。
|
||||
</TextBlock>
|
||||
<Button x:Name="BtnOpenPluginManager" Content="打开插件管理器"
|
||||
HorizontalAlignment="Left" Click="BtnOpenPluginManager_Click"
|
||||
Padding="15,5" Margin="0,10,0,0"/>
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox>
|
||||
<GroupBox.Header>
|
||||
<TextBlock Margin="0,12,0,0" Text="启动" FontWeight="Bold" Foreground="#fafafa"
|
||||
FontSize="26" />
|
||||
</GroupBox.Header>
|
||||
<ui:SimpleStackPanel Spacing="6">
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Visibility="Collapsed"
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="窗口无焦点模式" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchNoFocusMode"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchNoFocusMode_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:ToggleSwitch OnContent="" OffContent=""
|
||||
Name="ToggleSwitchIsAutoUpdate" Header="自动检查更新"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
Toggled="ToggleSwitchIsAutoUpdate_Toggled" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Visibility="Collapsed"
|
||||
<ui:ToggleSwitch OnContent="" OffContent=""
|
||||
Name="ToggleSwitchIsAutoUpdateWithSilence" Header="静默更新"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
Toggled="ToggleSwitchIsAutoUpdateWithSilence_Toggled" />
|
||||
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Padding="12"
|
||||
Visibility="{Binding ElementName=ToggleSwitchIsAutoUpdateWithSilence, Path=Visibility}">
|
||||
<TextBlock Text="# 静默更新将在软件不使用时自动安装,无需手动操作" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
|
||||
<!-- 更新通道选择 -->
|
||||
<ui:SimpleStackPanel Spacing="8" Margin="0,8,0,0">
|
||||
<TextBlock Text="更新通道" FontSize="15" FontWeight="Bold" Foreground="#fafafa"/>
|
||||
<ui:RadioButtons x:Name="UpdateChannelSelector" Margin="0,4,0,0">
|
||||
<RadioButton Content="稳定版 (Release)" GroupName="UpdateChannel"
|
||||
Tag="Release" Checked="UpdateChannelSelector_Checked"/>
|
||||
<RadioButton Content="测试版 (Beta)" GroupName="UpdateChannel"
|
||||
Tag="Beta" Checked="UpdateChannelSelector_Checked"/>
|
||||
</ui:RadioButtons>
|
||||
<TextBlock Text="# 稳定版提供可靠更新,测试版提供新功能抢先体验" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<!-- 版本修复按钮 -->
|
||||
<Button x:Name="FixVersionButton" Content="版本修复" Margin="0,8,0,0"
|
||||
Width="120" HorizontalAlignment="Left" Click="FixVersionButton_Click"/>
|
||||
<TextBlock Text="# 版本修复会根据当前选择的通道下载最新版本并执行安装,可用于修复损坏的安装"
|
||||
TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<Button x:Name="HistoryRollbackButton" Content="历史版本回滚" Width="120" Margin="0,10,0,0" Click="HistoryRollbackButton_Click"/>
|
||||
<TextBlock Text="# 历史版本回滚,点击后会弹出相应页面供用户手动回滚到之前的版本"
|
||||
TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<Border BorderBrush="White" BorderThickness="1" CornerRadius="5" Padding="12"
|
||||
Visibility="{Binding ElementName=ToggleSwitchIsAutoUpdateWithSilence, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<ui:SimpleStackPanel Spacing="12">
|
||||
<TextBlock
|
||||
Text="# 关闭静默更新后,已完成安装包的下载后将会弹窗询问是否进行更新,开启静默更新后将会在安装包下载完成后每隔十分钟进行如下检测:①处于静默更新时间段内 ②未处于书写模式 ③未处于画板内。若以上检测通过即会关闭软件进行自动更新。"
|
||||
@@ -669,12 +664,12 @@
|
||||
<ui:SimpleStackPanel x:Name="AutoUpdateTimePeriodBlock" Spacing="12">
|
||||
<ui:SimpleStackPanel Spacing="12">
|
||||
<TextBlock Text="静默更新时间段" FontSize="15" FontWeight="Bold"
|
||||
TextWrapping="Wrap" Foreground="Black" />
|
||||
TextWrapping="Wrap" Foreground="#fafafa" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="12">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
||||
Text="起始时间" FontSize="14" TextWrapping="Wrap"
|
||||
Foreground="Black" />
|
||||
Foreground="#fafafa" />
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox"
|
||||
Width="90"
|
||||
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged" />
|
||||
@@ -682,7 +677,7 @@
|
||||
<ui:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
||||
Text="终止时间" FontSize="14" TextWrapping="Wrap"
|
||||
Foreground="Black" />
|
||||
Foreground="#fafafa" />
|
||||
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
|
||||
Width="90"
|
||||
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged" />
|
||||
@@ -779,6 +774,18 @@
|
||||
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchClearCanvasAndClearTimeMachine_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="清空画布时同时清空图片" VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchClearCanvasAlsoClearImages"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchClearCanvasAlsoClearImages_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="插入图片时自动压缩(大于1920x1080)" VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchCompressPicturesUploaded"
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchCompressPicturesUploaded_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,4,0,4" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
@@ -795,12 +802,19 @@
|
||||
<TextBlock Text="# 请注意,若不保留双曲线渐近线可能会有遇到撤回相关的 BUG 影响用。" TextWrapping="Wrap"
|
||||
Foreground="#a1a1aa" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="让墨迹使用贝塞尔曲线平滑处理"
|
||||
<TextBlock Foreground="#fafafa" Text="使用WPF默认贝塞尔曲线平滑"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchFitToCurve"
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchFitToCurve_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="使用高级曲线平滑(推荐)"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchAdvancedBezierSmoothing"
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchAdvancedBezierSmoothing_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
</GroupBox>
|
||||
<!-- 新增:崩溃后操作设置 -->
|
||||
@@ -850,6 +864,16 @@
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 允许选中墨迹后对墨迹进行双指或多指缩放操作(此设置不受“允许双指旋转”设置的影响)" TextWrapping="Wrap"
|
||||
Foreground="#a1a1aa" />
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,4,0,4" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="启用手掌擦" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchEnablePalmEraser"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchEnablePalmEraser_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 开启后,两个及以上触点且触摸面积较大时自动切换为橡皮擦,抬手后恢复原编辑模式。" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
</ui:SimpleStackPanel>
|
||||
</GroupBox>
|
||||
<GroupBox Name="GroupBoxInkRecognition">
|
||||
@@ -935,7 +959,7 @@
|
||||
<TextBlock Foreground="#fafafa" Text="长度阈值" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<Slider Name="AutoStraightenLineThresholdSlider" Width="150" Minimum="30" Maximum="300"
|
||||
Value="30" TickFrequency="30" IsSnapToTickEnabled="True"
|
||||
Value="80" TickFrequency="30" IsSnapToTickEnabled="True"
|
||||
ValueChanged="AutoStraightenLineThresholdSlider_ValueChanged" />
|
||||
<TextBlock Foreground="#fafafa" Text="{Binding ElementName=AutoStraightenLineThresholdSlider, Path=Value, StringFormat={}{0:0}}"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="16,0,0,0" />
|
||||
@@ -944,13 +968,21 @@
|
||||
Visibility="{Binding ElementName=ToggleSwitchAutoStraightenLine, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TextBlock Foreground="#fafafa" Text="灵敏度" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<Slider Name="LineStraightenSensitivitySlider" Width="150" Minimum="0.05" Maximum="0.75"
|
||||
Value="0.10" TickFrequency="0.05" IsSnapToTickEnabled="True"
|
||||
<Slider Name="LineStraightenSensitivitySlider" Width="150" Minimum="0.05" Maximum="2.0"
|
||||
Value="0.20" TickFrequency="0.05" IsSnapToTickEnabled="True"
|
||||
ValueChanged="LineStraightenSensitivitySlider_ValueChanged" />
|
||||
<TextBlock Foreground="#fafafa" Text="{Binding ElementName=LineStraightenSensitivitySlider, Path=Value, StringFormat={}{0:F2}}"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="16,0,0,0" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 开启后,当绘制的直线超过设定长度阈值时,将自动调整为完美直线。灵敏度范围0.05-0.75,越小要求越严格,弯曲的线条越不容易被拉直;值越大越容易识别为直线。" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left"
|
||||
Visibility="{Binding ElementName=ToggleSwitchAutoStraightenLine, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TextBlock Foreground="#fafafa" Text="高精度直线拉直" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchHighPrecisionLineStraighten"
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchHighPrecisionLineStraighten_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 开启后,当绘制的直线超过设定长度阈值时,将自动调整为完美直线。灵敏度范围0.05-2.0,越小要求越严格,弯曲的线条越不容易被拉直;值越大越容易识别为直线。高精度模式下,每隔10像素取一个计数点,获取更准确的平均值用于判断。" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0"
|
||||
Stroke="#3f3f46" StrokeThickness="1" Margin="0,4,0,4" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
@@ -995,6 +1027,15 @@
|
||||
<ComboBoxItem Content="贴吧滑稽" FontFamily="Microsoft YaHei UI" />
|
||||
</ComboBox>
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,6,0,0">
|
||||
<TextBlock Foreground="#fafafa" Text="自定义浮动栏图标" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<Button Name="ButtonAddCustomIcon" Content="上传" FontFamily="Microsoft YaHei UI"
|
||||
Click="ButtonAddCustomIcon_Click" Padding="10,3"/>
|
||||
<Button Name="ButtonManageCustomIcons" Content="管理" FontFamily="Microsoft YaHei UI"
|
||||
Click="ButtonManageCustomIcons_Click" Padding="10,3" Margin="5,0,0,0"/>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="浮动工具栏缩放" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
@@ -1180,6 +1221,17 @@
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchSupportWPS_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<Image Source="/Resources/Icons-png/WPS.png" Margin="0,0,6,0" Width="28"
|
||||
Height="28" VerticalAlignment="Center" />
|
||||
<TextBlock Foreground="#fafafa" Text="WPP进程查杀(防止WPP残留进程)" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchEnableWppProcessKill"
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchEnableWppProcessKill_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 关闭后将不会自动查杀WPP残留进程,可能导致WPP关闭卡顿或无法彻底退出。" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<TextBlock Text="# 如果您只使用PowerPoint请不要打开WPS联动开关,如果使用WPS建议不要使用PowerPoint!" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<Border BorderBrush="#ef4444"
|
||||
BorderThickness="2" Padding="8" CornerRadius="6" Background="#991b1b">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="4">
|
||||
@@ -1558,6 +1610,14 @@
|
||||
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchNotifyPreviousPage_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="进入放映时回到首页" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent=""
|
||||
Name="ToggleSwitchAlwaysGoToFirstPageOnReenter" IsOn="False"
|
||||
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchAlwaysGoToFirstPageOnReenter_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="提示隐藏幻灯片" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
@@ -1578,7 +1638,7 @@
|
||||
</GroupBox>
|
||||
<GroupBox>
|
||||
<GroupBox.Header>
|
||||
<TextBlock Margin="0,12,0,0" Text="高级项" FontWeight="Bold" Foreground="#fafafa"
|
||||
<TextBlock Margin="0,12,0,0" Text="高级设置" FontWeight="Bold" Foreground="#fafafa"
|
||||
FontSize="26" />
|
||||
</GroupBox.Header>
|
||||
<ui:SimpleStackPanel Spacing="6">
|
||||
@@ -1673,7 +1733,14 @@
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchIsLogEnabled_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 日志文件超过 512 KB 时会自动删除。" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="日志以日期保存" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchIsSaveLogByDate"
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchIsSaveLogByDate_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 日志文件超过 512 KB 时会自动删除。开启日期保存后,日志将保存在Logs文件夹中,当文件夹大小超过5MB时自动清空。" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="关闭软件时二次弹窗确认" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
@@ -1884,6 +1951,29 @@
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock TextWrapping="Wrap" Foreground="#a1a1aa"
|
||||
Text="# 当检测到系统分辨率变化时,会尝试检测FloatingBar是否在屏幕内显示,如果不在屏幕内显示将会尝试移动到屏幕内可见区域(分辨率调小可能会触发,如果在屏幕内不会自动调整位置,请手动挡)。" />
|
||||
|
||||
<!-- 添加备份相关按钮 -->
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,4,0,4" />
|
||||
<TextBlock Text="设置备份与还原" FontWeight="Bold" Foreground="#fafafa"
|
||||
FontSize="16" Margin="0,5,0,5" />
|
||||
<TextBlock TextWrapping="Wrap" Foreground="#a1a1aa"
|
||||
Text="# 可手动备份当前设置或还原之前的备份,自动更新前也会自动备份" />
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="自动更新前备份" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchIsAutoBackupBeforeUpdate"
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchIsAutoBackupBeforeUpdate_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,5,0,0">
|
||||
<Button x:Name="BtnManualBackup" Content="手动备份" Click="BtnManualBackup_Click"
|
||||
Background="#2563eb" Foreground="White" Padding="12,6" Margin="0,0,12,0" />
|
||||
<Button x:Name="BtnRestoreBackup" Content="还原备份" Click="BtnRestoreBackup_Click"
|
||||
Background="#2563eb" Foreground="White" Padding="12,6" />
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
</GroupBox>
|
||||
<GroupBox>
|
||||
@@ -2198,6 +2288,15 @@
|
||||
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchAutoKillHiteAnnotation_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="鸿合屏幕书写查杀后自动进入批注" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent=""
|
||||
Name="ToggleSwitchAutoEnterAnnotationAfterKillHite"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchAutoEnterAnnotationAfterKillHite_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<Image Source="/Resources/Icons-png/VComYouJiao.png" Margin="0,0,6,0"
|
||||
Width="28"
|
||||
@@ -2291,6 +2390,17 @@
|
||||
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchAutoSaveStrokesAtScreenshot_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="墨迹全页面保存" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent=""
|
||||
Name="ToggleSwitchSaveFullPageStrokes" IsOn="False"
|
||||
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchSaveFullPageStrokes_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 开启后自动保存和手动保存墨迹时将以全屏模式保存。如果存在多个画布和墨迹,将把所有页面的墨迹按照每页为单位保存进一个压缩包中(注意,白板的墨迹只能在白板模式下打开,PPT的墨迹只能在PPT放映模式下打开)" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,4,0,4" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
@@ -2312,8 +2422,7 @@
|
||||
<TextBlock Foreground="#fafafa" Text="墨迹与截图的保存路径" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="10">
|
||||
<TextBox Width="320" x:Name="AutoSavedStrokesLocation"
|
||||
Text="{Binding AppDomain.CurrentDomain.BaseDirectory, StringFormat={}Saves}"
|
||||
<TextBox Width="320" x:Name="AutoSavedStrokesLocation" Text="D:\Ink Canvas"
|
||||
TextWrapping="Wrap"
|
||||
TextChanged="AutoSavedStrokesLocationTextBox_TextChanged" />
|
||||
<Button Name="AutoSavedStrokesLocationButton" Content="浏览"
|
||||
@@ -2365,6 +2474,31 @@
|
||||
<TextBlock Foreground="#fafafa" Text="天" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="8,0,0,0" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,8,0,8" />
|
||||
|
||||
<TextBlock Margin="0,0,0,8" Text="收纳模式" FontWeight="Bold" Foreground="#fafafa"
|
||||
FontSize="20" />
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="退出收纳模式时自动切换至批注模式"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent=""
|
||||
Name="ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 开启后,退出收纳模式时将自动切换至批注模式,便于快速批注" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="退出PPT放映后自动恢复浮动栏状态"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent=""
|
||||
Name="ToggleSwitchAutoFoldAfterPPTSlideShow"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchAutoFoldAfterPPTSlideShow_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<TextBlock Text="# 开启后,如果进入PPT放映前为收纳模式则退出后也为收纳模式,如果进入前不是收纳模式则退出后也不是收纳模式" TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
</ui:SimpleStackPanel>
|
||||
</GroupBox>
|
||||
<GroupBox Name="GroupBoxRandWindow">
|
||||
@@ -2372,7 +2506,7 @@
|
||||
<TextBlock Margin="0,12,0,0" Text="随机点名" FontWeight="Bold" Foreground="#fafafa"
|
||||
FontSize="26" />
|
||||
</GroupBox.Header>
|
||||
<ui:SimpleStackPanel Spacing="6">
|
||||
<ui:SimpleStackPanel Spacing="12">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="显示修改随机点名名单的按钮"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
@@ -2382,6 +2516,29 @@
|
||||
FontWeight="Bold"
|
||||
Toggled="ToggleSwitchDisplayRandWindowNamesInputBtn_OnToggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<TextBlock Foreground="#fafafa" Text="点名窗口背景设置"
|
||||
FontSize="16" FontWeight="Bold" Margin="0,10,0,5" />
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,5,0,0">
|
||||
<TextBlock Foreground="#fafafa" Text="背景选择:" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ComboBox Name="ComboBoxPickNameBackground" FontFamily="Microsoft YaHei UI"
|
||||
SelectedIndex="0" Width="180"
|
||||
SelectionChanged="ComboBoxPickNameBackground_SelectionChanged">
|
||||
<ComboBoxItem Content="默认背景" FontFamily="Microsoft YaHei UI" />
|
||||
<!-- 自定义背景会在代码中动态添加 -->
|
||||
</ComboBox>
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,5,0,0">
|
||||
<TextBlock Foreground="#fafafa" Text="自定义背景:" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<Button Name="ButtonAddCustomBackground" Content="上传" FontFamily="Microsoft YaHei UI"
|
||||
Click="ButtonAddCustomBackground_Click" Padding="10,3"/>
|
||||
<Button Name="ButtonManageBackgrounds" Content="管理" FontFamily="Microsoft YaHei UI"
|
||||
Click="ButtonManageBackgrounds_Click" Padding="10,3" Margin="5,0,0,0"/>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="启用随机抽和单次抽按钮"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
@@ -2391,6 +2548,15 @@
|
||||
FontWeight="Bold"
|
||||
Toggled="ToggleSwitchShowRandomAndSingleDraw_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="直接调用Ci点名"
|
||||
VerticalAlignment="Center" FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent=""
|
||||
Name="ToggleSwitchDirectCallCiRand"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI"
|
||||
FontWeight="Bold"
|
||||
Toggled="ToggleSwitchDirectCallCiRand_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0"
|
||||
Stroke="#3f3f46" StrokeThickness="1" Margin="0,4,0,4" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
@@ -2434,6 +2600,38 @@
|
||||
<TextBlock x:Name="AppVersionTextBlock" FontSize="18" FontWeight="Bold"
|
||||
Text="1.X.X.X" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<!-- 设备信息 -->
|
||||
<Border BorderBrush="#a1a1aa" BorderThickness="1" CornerRadius="8"
|
||||
Background="#18181b" Padding="12">
|
||||
<ui:SimpleStackPanel Spacing="8">
|
||||
<TextBlock Text="设备信息" FontWeight="Bold" Foreground="#fafafa" FontSize="14" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="设备ID:" Foreground="#a1a1aa" FontSize="12" />
|
||||
<TextBlock x:Name="DeviceIdTextBlock" Text="正在获取..." Foreground="#fafafa" FontSize="12" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="使用频率:" Foreground="#a1a1aa" FontSize="12" />
|
||||
<TextBlock x:Name="UsageFrequencyTextBlock" Text="正在获取..." Foreground="#fafafa" FontSize="12" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="更新优先级:" Foreground="#a1a1aa" FontSize="12" />
|
||||
<TextBlock x:Name="UpdatePriorityTextBlock" Text="正在获取..." Foreground="#fafafa" FontSize="12" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="启动次数:" Foreground="#a1a1aa" FontSize="12" />
|
||||
<TextBlock x:Name="LaunchCountTextBlock" Text="正在获取..." Foreground="#fafafa" FontSize="12" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="总使用时长:" Foreground="#a1a1aa" FontSize="12" />
|
||||
<TextBlock x:Name="TotalUsageTextBlock" Text="正在获取..." Foreground="#fafafa" FontSize="12" />
|
||||
</ui:SimpleStackPanel>
|
||||
<Button x:Name="RefreshDeviceInfoButton" Content="刷新设备信息"
|
||||
Width="120" Height="30" Margin="0,8,0,0"
|
||||
Click="RefreshDeviceInfo_Click" />
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
Text="# 使用和分发本软件前,请您应当且务必知晓相关开源协议,且您应当知晓本软件基于 https://github.com/WXRIW/Ink-Canvas 修改而成。"
|
||||
TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
@@ -2732,10 +2930,10 @@
|
||||
</Grid>
|
||||
<ui:SimpleStackPanel Spacing="3" Orientation="Vertical">
|
||||
<TextBlock
|
||||
Text="© 2024 Dubi906w(Kriastans) 版权所有" FontWeight="Bold"
|
||||
Text="© 2025 CJK_mkp 版权所有" FontWeight="Bold"
|
||||
TextWrapping="Wrap" Foreground="White" />
|
||||
<TextBlock
|
||||
Text="© 2022-2024 HarkoTek Studio 提供技术支持"
|
||||
Text="© 2022-2025 HarkoTek Studio 提供技术支持"
|
||||
TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<TextBlock
|
||||
Text="We love Open-Source forever!" FontWeight="Bold"
|
||||
@@ -2748,7 +2946,7 @@
|
||||
</ui:ScrollViewerEx>
|
||||
|
||||
<!-- 底部按钮区域 -->
|
||||
<Grid Grid.Row="2" VerticalAlignment="Bottom" Height="50">
|
||||
<Grid Grid.Row="2" VerticalAlignment="Bottom" Height="65">
|
||||
<Button FontFamily="Microsoft YaHei UI"
|
||||
Width="120" Margin="10"
|
||||
HorizontalAlignment="Right"
|
||||
@@ -2794,7 +2992,7 @@
|
||||
<Label Name="Label" Visibility="Collapsed" Foreground="Gray" Content="0" />
|
||||
<Grid Name="InkCanvasGridForInkReplay">
|
||||
<InkCanvas x:Name="inkCanvas" ForceCursor="True" UseCustomCursor="True"
|
||||
TouchUp="Main_Grid_TouchUp" TouchDown="Main_Grid_TouchDown"
|
||||
TouchDown="Main_Grid_TouchDown"
|
||||
TouchMove="inkCanvas_TouchMove"
|
||||
ManipulationDelta="Main_Grid_ManipulationDelta"
|
||||
ManipulationCompleted="Main_Grid_ManipulationCompleted"
|
||||
@@ -2809,6 +3007,13 @@
|
||||
ManipulationStarting="inkCanvas_ManipulationStarting"
|
||||
SelectionChanged="inkCanvas_SelectionChanged"
|
||||
StrokeCollected="inkCanvas_StrokeCollected" ClipToBounds="False" Background="Transparent" />
|
||||
|
||||
<!-- 新橡皮擦覆盖层 - 用于高级橡皮擦系统 -->
|
||||
<Border x:Name="AdvancedEraserOverlay"
|
||||
Background="Transparent"
|
||||
IsHitTestVisible="False"
|
||||
Loaded="EraserOverlay_Loaded"
|
||||
Panel.ZIndex="1000" />
|
||||
</Grid>
|
||||
|
||||
<Canvas IsHitTestVisible="False">
|
||||
@@ -2944,6 +3149,52 @@
|
||||
</ui:SimpleStackPanel>
|
||||
</Viewbox>
|
||||
</Border>
|
||||
|
||||
<!-- 图片选择工具栏 -->
|
||||
<Border Name="BorderImageSelectionControl"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Top" Margin="800,900,0,0"
|
||||
CornerRadius="5" Height="80"
|
||||
Background="{DynamicResource FloatBarBackground}"
|
||||
Visibility="Collapsed"
|
||||
BorderThickness="1" BorderBrush="{DynamicResource FloatBarBorderBrush}">
|
||||
<Viewbox Margin="0,-2.5">
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="10" Height="60">
|
||||
<Border Name="BorderImageRotateLeft" Margin="0,2,-9,2" Background="Transparent"
|
||||
CornerRadius="{Binding ElementName=BorderImageSelectionControl, Path=CornerRadius}"
|
||||
Width="40" MouseDown="Border_MouseDown" MouseUp="BorderImageRotateLeft_MouseUp">
|
||||
<ui:SimpleStackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<Image Source="/Resources/Icons-Fluent/ic_fluent_flip_horizontal_24_regular.png"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="25" Width="25" />
|
||||
<TextBlock Text="左转" FontSize="10" Foreground="{DynamicResource FloatBarForeground}"
|
||||
HorizontalAlignment="Center" />
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
<Border Name="BorderImageRotateRight" Margin="0,2,-9,2" Background="Transparent"
|
||||
CornerRadius="{Binding ElementName=BorderImageSelectionControl, Path=CornerRadius}"
|
||||
Width="40" MouseDown="Border_MouseDown" MouseUp="BorderImageRotateRight_MouseUp">
|
||||
<ui:SimpleStackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<Image Source="/Resources/Icons-Fluent/ic_fluent_flip_vertical_24_regular.png"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="25" Width="25" />
|
||||
<TextBlock Text="右转" FontSize="10" Foreground="{DynamicResource FloatBarForeground}"
|
||||
HorizontalAlignment="Center" />
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
<Border Width="1" Height="45" BorderBrush="{DynamicResource FloatBarForeground}"
|
||||
Background="{DynamicResource FloatBarForeground}" />
|
||||
<Border Name="BorderImageDelete" Margin="-8,0,0,0"
|
||||
CornerRadius="{Binding ElementName=BorderImageSelectionControl, Path=CornerRadius}"
|
||||
Width="40" MouseDown="Border_MouseDown" MouseUp="BorderImageDelete_MouseUp">
|
||||
<ui:SimpleStackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<Image Source="/Resources/Icons-Fluent/ic_fluent_delete_24_regular.png"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="25" Width="25" />
|
||||
<TextBlock Margin="0,5,0,0" Text="删除" FontSize="10"
|
||||
Foreground="{DynamicResource FloatBarForeground}"
|
||||
HorizontalAlignment="Center" />
|
||||
</ui:SimpleStackPanel>
|
||||
</Border>
|
||||
</ui:SimpleStackPanel>
|
||||
</Viewbox>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<Grid Visibility="Collapsed">
|
||||
@@ -3039,7 +3290,9 @@
|
||||
<ui:ScrollViewerEx Name="BlackBoardLeftSidePageListScrollViewer"
|
||||
Height="460"
|
||||
VerticalScrollBarVisibility="Hidden"
|
||||
ForceUseSmoothScroll="True">
|
||||
ForceUseSmoothScroll="True"
|
||||
PanningMode="VerticalOnly"
|
||||
IsManipulationEnabled="True">
|
||||
<ListView ScrollViewer.CanContentScroll="False" SelectionMode="Single"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
@@ -3621,7 +3874,7 @@
|
||||
TickFrequency="1" TickPlacement="None"
|
||||
ValueChanged="HighlighterWidthSlider_ValueChanged" />
|
||||
<TextBlock
|
||||
Text="{Binding Value, ElementName=HighlighterWidthSlider, Mode=OneWay}"
|
||||
Text="{Binding Value, ElementName=BoardHighlighterWidthSlider, Mode=OneWay}"
|
||||
FontFamily="Consolas"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="10,0,0,4.5" FontSize="15" />
|
||||
@@ -4662,6 +4915,29 @@
|
||||
</Viewbox>
|
||||
</Border>
|
||||
</Border>
|
||||
<Border x:Name="BoardInsertImage" Width="60" Height="50" MouseDown="Border_MouseDown"
|
||||
MouseUp="InsertImage_MouseUp" BorderThickness="0,1,0,1"
|
||||
BorderBrush="#a1a1aa"
|
||||
Background="#f4f4f5"
|
||||
Opacity="0.95">
|
||||
<Grid Margin="6,6,6,4">
|
||||
<Image VerticalAlignment="Top"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="20" Width="20">
|
||||
<Image.Source>
|
||||
<DrawingImage>
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
|
||||
<GeometryDrawing Brush="#18181b"
|
||||
Geometry="F1 M24,24z M0,0z M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2-0.9 2-2V5C21,3.9 20.1,3 19,3zM19,19H5V5h14V19z M17,7c-1.1,0-2,0.9-2,2s0.9,2 2,2 2-0.9 2-2S18.1,7 17,7zM7,17l2.5-3.01 1.96,2.36 2.54-3.21L17,17H7z"/>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<TextBlock Text="插入图片" Foreground="Black" VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Center" FontSize="12" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border x:Name="BoardUndo" Width="60" Height="50" MouseDown="Border_MouseDown"
|
||||
MouseUp="SymbolIconUndo_MouseUp" BorderThickness="0,1,0,1"
|
||||
BorderBrush="#a1a1aa"
|
||||
@@ -5077,7 +5353,9 @@
|
||||
<ui:ScrollViewerEx Name="BlackBoardRightSidePageListScrollViewer"
|
||||
Height="460"
|
||||
VerticalScrollBarVisibility="Hidden"
|
||||
ForceUseSmoothScroll="True">
|
||||
ForceUseSmoothScroll="True"
|
||||
PanningMode="VerticalOnly"
|
||||
IsManipulationEnabled="True">
|
||||
<ListView ScrollViewer.CanContentScroll="False" SelectionMode="Single"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
@@ -5561,7 +5839,6 @@
|
||||
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
FontSize="10" Text="/ 999" Name="PPTBtnPageTotal" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Width="50" Height="50" MouseUp="GridPPTControlNext_MouseUp"
|
||||
@@ -5655,7 +5932,7 @@
|
||||
</Viewbox>
|
||||
|
||||
<Grid Name="FloatingbarUIForInkReplay">
|
||||
<Viewbox Name="ViewboxFloatingBar" Margin="100,5,0,0"
|
||||
<Viewbox Name="ViewboxFloatingBar" Margin="100,5,0,0" Cursor="Arrow"
|
||||
HorizontalAlignment="Left" Height="58" VerticalAlignment="Top" Width="733"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<Viewbox.LayoutTransform>
|
||||
@@ -5684,7 +5961,7 @@
|
||||
</Canvas>
|
||||
</Border>
|
||||
</Canvas>
|
||||
<ui:SimpleStackPanel Margin="2,0" Name="StackPanelFloatingBar" Orientation="Horizontal">
|
||||
<ui:SimpleStackPanel Margin="2,0" Name="StackPanelFloatingBar" Orientation="Horizontal" Cursor="Arrow">
|
||||
<!--<ui:SimpleStackPanel Name="Cursor_Icon" MouseDown="Border_MouseDown" MouseUp="CursorIcon_Click"-->
|
||||
<ui:SimpleStackPanel Name="Cursor_Icon"
|
||||
MouseDown="FloatingBarToolBtnMouseDownFeedback_Panel"
|
||||
@@ -6943,6 +7220,9 @@
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
<Grid Width="0">
|
||||
<Grid x:Name="BackgroundPaletteGrid" Margin="-203,-128,83,37">
|
||||
<!-- 背景面板将在代码中动态添加 -->
|
||||
</Grid>
|
||||
<Border Visibility="Visible" ClipToBounds="True" Name="EraserSizePanel"
|
||||
Margin="-203,-128,83,37" CornerRadius="5" Background="#fafafa" Opacity="1"
|
||||
BorderBrush="#2563eb" BorderThickness="1">
|
||||
@@ -7689,6 +7969,16 @@
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="17" Margin="0,3,0,0" />
|
||||
<TextBlock Text="白板" Foreground="Black" FontSize="8" Margin="0,2,0,3" TextAlignment="Center" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel
|
||||
x:Name="BtnExitPptFromSidebarLeft"
|
||||
MouseUp="ExitPPTSlideShow_MouseUp"
|
||||
Background="Transparent" Orientation="Vertical" HorizontalAlignment="Center"
|
||||
Width="32" Margin="0" Visibility="Collapsed">
|
||||
<Image Source="/Resources/new-icons/end-slides-show.png"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="17" Margin="0,3,0,0" />
|
||||
<TextBlock Text="退出放映" Foreground="Black" FontSize="8" Margin="0,2,0,3" TextAlignment="Center" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<ui:SimpleStackPanel
|
||||
MouseUp="UnFoldFloatingBar_MouseUp"
|
||||
Background="Transparent" Orientation="Vertical" HorizontalAlignment="Center"
|
||||
@@ -7762,6 +8052,16 @@
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="17" Margin="0,3,0,0" />
|
||||
<TextBlock Text="白板" Foreground="Black" FontSize="8" Margin="0,2,0,3" TextAlignment="Center" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel
|
||||
x:Name="BtnExitPptFromSidebarRight"
|
||||
MouseUp="ExitPPTSlideShow_MouseUp"
|
||||
Background="Transparent" Orientation="Vertical" HorizontalAlignment="Center"
|
||||
Width="32" Margin="0" Visibility="Collapsed">
|
||||
<Image Source="/Resources/new-icons/end-slides-show.png"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" Height="17" Margin="0,3,0,0" />
|
||||
<TextBlock Text="退出放映" Foreground="Black" FontSize="8" Margin="0,2,0,3" TextAlignment="Center" />
|
||||
</ui:SimpleStackPanel>
|
||||
|
||||
<ui:SimpleStackPanel
|
||||
MouseUp="UnFoldFloatingBar_MouseUp"
|
||||
Background="Transparent" Orientation="Vertical" HorizontalAlignment="Center"
|
||||
@@ -7791,4 +8091,4 @@
|
||||
</Border>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
</Window>
|
||||
</Window>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Modern;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
@@ -9,12 +6,13 @@ using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Modern;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
public bool isFloatingBarFolded = false;
|
||||
private bool isFloatingBarChangingHideMode = false;
|
||||
public bool isFloatingBarFolded;
|
||||
private bool isFloatingBarChangingHideMode;
|
||||
|
||||
private void CloseWhiteboardImmediately() {
|
||||
if (isDisplayingOrHidingBlackboard) return;
|
||||
@@ -30,17 +28,17 @@ namespace Ink_Canvas {
|
||||
BtnSwitch_Click(BtnSwitch, null);
|
||||
BtnExit.Foreground = Brushes.White;
|
||||
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
|
||||
new Thread(new ThreadStart(() => {
|
||||
new Thread(() => {
|
||||
Thread.Sleep(200);
|
||||
Application.Current.Dispatcher.Invoke(() => { isDisplayingOrHidingBlackboard = false; });
|
||||
})).Start();
|
||||
}).Start();
|
||||
}
|
||||
|
||||
public async void FoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e) {
|
||||
await FoldFloatingBar(sender);
|
||||
}
|
||||
|
||||
public async Task FoldFloatingBar(object sender)
|
||||
public async Task FoldFloatingBar(object sender, bool isAutoFoldCommand = false)
|
||||
{
|
||||
var isShouldRejectAction = false;
|
||||
|
||||
@@ -54,7 +52,7 @@ namespace Ink_Canvas {
|
||||
|
||||
// FloatingBarIcons_MouseUp_New(sender);
|
||||
if (sender == null)
|
||||
foldFloatingBarByUser = true;
|
||||
foldFloatingBarByUser = false;
|
||||
else
|
||||
foldFloatingBarByUser = true;
|
||||
unfoldFloatingBarByUser = false;
|
||||
@@ -84,13 +82,14 @@ namespace Ink_Canvas {
|
||||
CursorWithDelIcon_Click(sender, null);
|
||||
});
|
||||
|
||||
await Task.Delay(10);
|
||||
await Task.Delay(300);
|
||||
|
||||
await Dispatcher.InvokeAsync(() => {
|
||||
LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
|
||||
RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
|
||||
LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
|
||||
RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
|
||||
GridForFloatingBarDraging.Visibility = Visibility.Collapsed;
|
||||
ViewboxFloatingBarMarginAnimation(-60);
|
||||
HideSubPanels("cursor");
|
||||
SidePannelMarginAnimation(-10);
|
||||
@@ -99,7 +98,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private async void LeftUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e) {
|
||||
if (Settings.Appearance.IsShowQuickPanel == true) {
|
||||
if (Settings.Appearance.IsShowQuickPanel) {
|
||||
HideRightQuickPanel();
|
||||
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Visible;
|
||||
await Dispatcher.InvokeAsync(() => {
|
||||
@@ -123,7 +122,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private async void RightUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e) {
|
||||
if (Settings.Appearance.IsShowQuickPanel == true) {
|
||||
if (Settings.Appearance.IsShowQuickPanel) {
|
||||
HideLeftQuickPanel();
|
||||
RightUnFoldButtonQuickPanel.Visibility = Visibility.Visible;
|
||||
await Dispatcher.InvokeAsync(() => {
|
||||
@@ -217,6 +216,13 @@ namespace Ink_Canvas {
|
||||
await Task.Delay(0);
|
||||
|
||||
await Dispatcher.InvokeAsync(() => {
|
||||
// 根据设置决定是否自动切换至批注模式
|
||||
if (Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode && currentMode == 0)
|
||||
{
|
||||
// 切换至批注模式
|
||||
PenIcon_Click(null, null);
|
||||
}
|
||||
|
||||
if (StackPanelPPTControls.Visibility == Visibility.Visible)
|
||||
{
|
||||
var dops = Settings.PowerPointSettings.PPTButtonsDisplayOption.ToString();
|
||||
@@ -243,13 +249,13 @@ namespace Ink_Canvas {
|
||||
if (MarginFromEdge == -10) LeftSidePanel.Visibility = Visibility.Visible;
|
||||
|
||||
var LeftSidePanelmarginAnimation = new ThicknessAnimation {
|
||||
Duration = isNoAnimation == true ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(0.175),
|
||||
Duration = isNoAnimation ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(0.175),
|
||||
From = LeftSidePanel.Margin,
|
||||
To = new Thickness(MarginFromEdge, 0, 0, -150)
|
||||
};
|
||||
LeftSidePanelmarginAnimation.EasingFunction = new CubicEase();
|
||||
var RightSidePanelmarginAnimation = new ThicknessAnimation {
|
||||
Duration = isNoAnimation == true ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(0.175),
|
||||
Duration = isNoAnimation ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(0.175),
|
||||
From = RightSidePanel.Margin,
|
||||
To = new Thickness(0, 0, MarginFromEdge, -150)
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using IWshRuntimeLibrary;
|
||||
using System;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using IWshRuntimeLibrary;
|
||||
using Application = System.Windows.Forms.Application;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
@@ -10,7 +12,7 @@ namespace Ink_Canvas {
|
||||
var shortcut = (IWshShortcut)shell.CreateShortcut(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\" + exeName + ".lnk");
|
||||
//设置快捷方式的目标所在的位置(源程序完整路径)
|
||||
shortcut.TargetPath = System.Windows.Forms.Application.ExecutablePath;
|
||||
shortcut.TargetPath = Application.ExecutablePath;
|
||||
//应用程序的工作目录
|
||||
//当用户没有指定一个具体的目录时,快捷方式的目标应用程序将使用该属性所指定的目录来装载或保存文件。
|
||||
shortcut.WorkingDirectory = Environment.CurrentDirectory;
|
||||
@@ -30,8 +32,8 @@ namespace Ink_Canvas {
|
||||
|
||||
public static bool StartAutomaticallyDel(string exeName) {
|
||||
try {
|
||||
System.IO.File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\" + exeName +
|
||||
".lnk");
|
||||
File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\" + exeName +
|
||||
".lnk");
|
||||
return true;
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Microsoft.Win32;
|
||||
using iNKORE.UI.WPF.Modern;
|
||||
using System;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using iNKORE.UI.WPF.Modern;
|
||||
using Microsoft.Win32;
|
||||
using Application = System.Windows.Application;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
@@ -11,19 +11,19 @@ namespace Ink_Canvas {
|
||||
|
||||
private void SetTheme(string theme) {
|
||||
if (theme == "Light") {
|
||||
var rd1 = new ResourceDictionary()
|
||||
var rd1 = new ResourceDictionary
|
||||
{ Source = new Uri("Resources/Styles/Light.xaml", UriKind.Relative) };
|
||||
Application.Current.Resources.MergedDictionaries.Add(rd1);
|
||||
|
||||
var rd2 = new ResourceDictionary()
|
||||
var rd2 = new ResourceDictionary
|
||||
{ Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative) };
|
||||
Application.Current.Resources.MergedDictionaries.Add(rd2);
|
||||
|
||||
var rd3 = new ResourceDictionary()
|
||||
var rd3 = new ResourceDictionary
|
||||
{ Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative) };
|
||||
Application.Current.Resources.MergedDictionaries.Add(rd3);
|
||||
|
||||
var rd4 = new ResourceDictionary()
|
||||
var rd4 = new ResourceDictionary
|
||||
{ Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative) };
|
||||
Application.Current.Resources.MergedDictionaries.Add(rd4);
|
||||
|
||||
@@ -32,18 +32,18 @@ namespace Ink_Canvas {
|
||||
FloatBarForegroundColor = (Color)Application.Current.FindResource("FloatBarForegroundColor");
|
||||
}
|
||||
else if (theme == "Dark") {
|
||||
var rd1 = new ResourceDictionary() { Source = new Uri("Resources/Styles/Dark.xaml", UriKind.Relative) };
|
||||
var rd1 = new ResourceDictionary { Source = new Uri("Resources/Styles/Dark.xaml", UriKind.Relative) };
|
||||
Application.Current.Resources.MergedDictionaries.Add(rd1);
|
||||
|
||||
var rd2 = new ResourceDictionary()
|
||||
var rd2 = new ResourceDictionary
|
||||
{ Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative) };
|
||||
Application.Current.Resources.MergedDictionaries.Add(rd2);
|
||||
|
||||
var rd3 = new ResourceDictionary()
|
||||
var rd3 = new ResourceDictionary
|
||||
{ Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative) };
|
||||
Application.Current.Resources.MergedDictionaries.Add(rd3);
|
||||
|
||||
var rd4 = new ResourceDictionary()
|
||||
var rd4 = new ResourceDictionary
|
||||
{ Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative) };
|
||||
Application.Current.Resources.MergedDictionaries.Add(rd4);
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Xml.Linq;
|
||||
using System.Windows.Controls;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
@@ -23,6 +22,7 @@ namespace Ink_Canvas {
|
||||
private int WhiteboardTotalCount = 1;
|
||||
private TimeMachineHistory[][] TimeMachineHistories = new TimeMachineHistory[101][]; //最多99页,0用来存储非白板时的墨迹以便还原
|
||||
|
||||
// 保存每页白板图片信息
|
||||
private void SaveStrokes(bool isBackupMain = false) {
|
||||
if (isBackupMain) {
|
||||
var timeMachineHistory = timeMachine.ExportTimeMachineHistory();
|
||||
@@ -38,18 +38,39 @@ namespace Ink_Canvas {
|
||||
private void ClearStrokes(bool isErasedByCode) {
|
||||
_currentCommitType = CommitReason.ClearingCanvas;
|
||||
if (isErasedByCode) _currentCommitType = CommitReason.CodeInput;
|
||||
|
||||
// 取消任何UI元素的选择,隐藏拉伸控件
|
||||
DeselectUIElement();
|
||||
|
||||
// 只清除笔画,不清除图片元素
|
||||
// 图片元素的清除由调用方决定
|
||||
inkCanvas.Strokes.Clear();
|
||||
|
||||
_currentCommitType = CommitReason.UserInput;
|
||||
}
|
||||
|
||||
// 恢复每页白板图片信息
|
||||
private void RestoreStrokes(bool isBackupMain = false) {
|
||||
try {
|
||||
if (TimeMachineHistories[CurrentWhiteboardIndex] == null) return; //防止白板打开后不居中
|
||||
var targetIndex = isBackupMain ? 0 : CurrentWhiteboardIndex;
|
||||
|
||||
// 先清空当前画布的所有内容(墨迹和图片)
|
||||
// 这里必须清除图片,因为页面切换时需要完全重置画布状态
|
||||
inkCanvas.Strokes.Clear();
|
||||
inkCanvas.Children.Clear();
|
||||
|
||||
// 如果历史记录为空,直接返回(新页面或空页面)
|
||||
if (TimeMachineHistories[targetIndex] == null) {
|
||||
timeMachine.ClearStrokeHistory();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBackupMain) {
|
||||
timeMachine.ImportTimeMachineHistory(TimeMachineHistories[0]);
|
||||
foreach (var item in TimeMachineHistories[0]) ApplyHistoryToCanvas(item);
|
||||
} else {
|
||||
timeMachine.ImportTimeMachineHistory(TimeMachineHistories[CurrentWhiteboardIndex]);
|
||||
// 通过时间机器历史恢复所有内容(墨迹和图片)
|
||||
foreach (var item in TimeMachineHistories[CurrentWhiteboardIndex]) ApplyHistoryToCanvas(item);
|
||||
}
|
||||
}
|
||||
@@ -91,6 +112,9 @@ namespace Ink_Canvas {
|
||||
private void BtnWhiteBoardSwitchPrevious_Click(object sender, EventArgs e) {
|
||||
if (CurrentWhiteboardIndex <= 1) return;
|
||||
|
||||
// 取消任何UI元素的选择
|
||||
DeselectUIElement();
|
||||
|
||||
SaveStrokes();
|
||||
|
||||
ClearStrokes(true);
|
||||
@@ -112,6 +136,9 @@ namespace Ink_Canvas {
|
||||
return;
|
||||
}
|
||||
|
||||
// 取消任何UI元素的选择
|
||||
DeselectUIElement();
|
||||
|
||||
SaveStrokes();
|
||||
|
||||
ClearStrokes(true);
|
||||
@@ -126,6 +153,10 @@ namespace Ink_Canvas {
|
||||
if (WhiteboardTotalCount >= 99) return;
|
||||
if (Settings.Automation.IsAutoSaveStrokesAtClear &&
|
||||
inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber) SaveScreenShot(true);
|
||||
|
||||
// 取消任何UI元素的选择
|
||||
DeselectUIElement();
|
||||
|
||||
SaveStrokes();
|
||||
ClearStrokes(true);
|
||||
|
||||
@@ -136,6 +167,12 @@ namespace Ink_Canvas {
|
||||
for (var i = WhiteboardTotalCount; i > CurrentWhiteboardIndex; i--)
|
||||
TimeMachineHistories[i] = TimeMachineHistories[i - 1];
|
||||
|
||||
// 确保新页面的历史记录为空
|
||||
TimeMachineHistories[CurrentWhiteboardIndex] = null;
|
||||
|
||||
// 恢复新页面(这会清空画布,因为历史记录为null)
|
||||
RestoreStrokes();
|
||||
|
||||
UpdateIndexInfoDisplay();
|
||||
|
||||
if (WhiteboardTotalCount >= 99) BtnWhiteBoardAdd.IsEnabled = false;
|
||||
|
||||
@@ -1,86 +1,699 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Ink_Canvas.Helpers;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
private void BoardChangeBackgroundColorBtn_MouseUp(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
|
||||
// 创建背景选项面板(如果不存在)
|
||||
if (BackgroundPalette == null)
|
||||
{
|
||||
CreateBackgroundPalette();
|
||||
}
|
||||
|
||||
// 显示或隐藏背景选项面板
|
||||
if (BackgroundPalette != null)
|
||||
{
|
||||
if (BackgroundPalette.Visibility == Visibility.Visible)
|
||||
{
|
||||
// 如果面板已经显示,则隐藏它
|
||||
AnimationsHelper.HideWithSlideAndFade(BackgroundPalette);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 隐藏其他可能显示的面板
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
|
||||
// 显示背景选项面板
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BackgroundPalette);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 原有的背景切换代码
|
||||
Settings.Canvas.UsingWhiteboard = !Settings.Canvas.UsingWhiteboard;
|
||||
SaveSettingsToFile();
|
||||
if (Settings.Canvas.UsingWhiteboard) {
|
||||
if (inkColor == 5) lastBoardInkColor = 0;
|
||||
ICCWaterMarkDark.Visibility = Visibility.Visible;
|
||||
ICCWaterMarkWhite.Visibility = Visibility.Collapsed;
|
||||
|
||||
// 设置为白板默认背景色
|
||||
Color defaultWhiteboardColor = Color.FromRgb(234, 235, 237);
|
||||
|
||||
if (currentMode == 1) // 白板模式
|
||||
{
|
||||
// 设置背景为默认白板背景色
|
||||
GridBackgroundCover.Background = new SolidColorBrush(defaultWhiteboardColor);
|
||||
|
||||
// 更新RGB滑块的值为默认白板背景色
|
||||
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateRGBSliders(defaultWhiteboardColor);
|
||||
}
|
||||
|
||||
// 更新自定义背景色为默认白板背景色
|
||||
CustomBackgroundColor = defaultWhiteboardColor;
|
||||
|
||||
// 保存到设置
|
||||
string colorHex = $"#{defaultWhiteboardColor.R:X2}{defaultWhiteboardColor.G:X2}{defaultWhiteboardColor.B:X2}";
|
||||
Settings.Canvas.CustomBackgroundColor = colorHex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 设置墨迹颜色为黑色
|
||||
CheckLastColor(0);
|
||||
forceEraser = false;
|
||||
}
|
||||
else {
|
||||
if (inkColor == 0) lastBoardInkColor = 5;
|
||||
ICCWaterMarkWhite.Visibility = Visibility.Visible;
|
||||
ICCWaterMarkDark.Visibility = Visibility.Collapsed;
|
||||
|
||||
// 设置为黑板默认背景色
|
||||
Color defaultBlackboardColor = Color.FromRgb(22, 41, 36);
|
||||
|
||||
if (currentMode == 1) // 黑板模式
|
||||
{
|
||||
// 设置背景为默认黑板背景色
|
||||
GridBackgroundCover.Background = new SolidColorBrush(defaultBlackboardColor);
|
||||
|
||||
// 更新RGB滑块的值为默认黑板背景色
|
||||
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateRGBSliders(defaultBlackboardColor);
|
||||
}
|
||||
|
||||
// 更新自定义背景色为默认黑板背景色
|
||||
CustomBackgroundColor = defaultBlackboardColor;
|
||||
|
||||
// 保存到设置
|
||||
string colorHex = $"#{defaultBlackboardColor.R:X2}{defaultBlackboardColor.G:X2}{defaultBlackboardColor.B:X2}";
|
||||
Settings.Canvas.CustomBackgroundColor = colorHex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 设置墨迹颜色为白色
|
||||
CheckLastColor(5);
|
||||
forceEraser = false;
|
||||
}
|
||||
|
||||
CheckColorTheme(true);
|
||||
}
|
||||
|
||||
private void BoardEraserIcon_Click(object sender, RoutedEventArgs e) {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint ||
|
||||
inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke) {
|
||||
if (BoardEraserSizePanel.Visibility == Visibility.Collapsed) {
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardEraserSizePanel);
|
||||
} else {
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
}
|
||||
} else {
|
||||
forceEraser = true;
|
||||
forcePointEraser = true;
|
||||
double k = 1;
|
||||
if (Settings.Canvas.EraserShapeType == 0) {
|
||||
switch (BoardComboBoxEraserSize.SelectedIndex)
|
||||
{
|
||||
case 0:
|
||||
k = 0.5;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.8;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.25;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.8;
|
||||
break;
|
||||
}
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(k * 90, k * 90);
|
||||
} else if (Settings.Canvas.EraserShapeType == 1) {
|
||||
switch (BoardComboBoxEraserSize.SelectedIndex)
|
||||
{
|
||||
case 0:
|
||||
k = 0.7;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.9;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.2;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.6;
|
||||
break;
|
||||
}
|
||||
inkCanvas.EraserShape = new RectangleStylusShape(k * 90 * 0.6, k * 90);
|
||||
}
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
drawingShapeMode = 0;
|
||||
// 创建背景选项面板
|
||||
private void CreateBackgroundPalette()
|
||||
{
|
||||
// 确保加载自定义背景色
|
||||
LoadCustomBackgroundColor();
|
||||
|
||||
// 创建一个类似于PenPalette的面板
|
||||
BackgroundPalette = new Border
|
||||
{
|
||||
Name = "BackgroundPalette",
|
||||
Visibility = Visibility.Collapsed,
|
||||
Background = new SolidColorBrush(Colors.White),
|
||||
Opacity = 1,
|
||||
BorderBrush = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Width = 300,
|
||||
MaxHeight = 400
|
||||
};
|
||||
|
||||
// 确保面板显示在顶层
|
||||
Panel.SetZIndex(BackgroundPalette, 1000);
|
||||
|
||||
inkCanvas_EditingModeChanged(inkCanvas, null);
|
||||
CancelSingleFingerDragMode();
|
||||
// 创建面板内容
|
||||
var stackPanel = new StackPanel();
|
||||
|
||||
HideSubPanels("eraser");
|
||||
// 创建标题栏
|
||||
var titleBorder = new Border
|
||||
{
|
||||
BorderBrush = new SolidColorBrush(Color.FromRgb(0x1e, 0x3a, 0x8a)),
|
||||
Height = 32,
|
||||
BorderThickness = new Thickness(0, 0, 0, 1),
|
||||
CornerRadius = new CornerRadius(8, 8, 0, 0),
|
||||
Background = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
|
||||
Margin = new Thickness(-1, -1, -1, 0),
|
||||
Padding = new Thickness(1, 1, 1, 0)
|
||||
};
|
||||
|
||||
var titleCanvas = new System.Windows.Controls.Canvas { Height = 24, ClipToBounds = true };
|
||||
var titleText = new TextBlock
|
||||
{
|
||||
Text = "背景设置",
|
||||
Foreground = new SolidColorBrush(Colors.White),
|
||||
Padding = new Thickness(0, 5, 0, 0),
|
||||
FontSize = 11,
|
||||
FontWeight = FontWeights.Bold,
|
||||
TextAlignment = TextAlignment.Center
|
||||
};
|
||||
System.Windows.Controls.Canvas.SetLeft(titleText, 8);
|
||||
titleCanvas.Children.Add(titleText);
|
||||
|
||||
// 关闭按钮
|
||||
var closeImage = new Image
|
||||
{
|
||||
Source = new BitmapImage(new Uri("/Resources/new-icons/close-white.png", UriKind.Relative)),
|
||||
Height = 16,
|
||||
Width = 16
|
||||
};
|
||||
RenderOptions.SetBitmapScalingMode(closeImage, BitmapScalingMode.HighQuality);
|
||||
closeImage.MouseUp += CloseBordertools_MouseUp;
|
||||
System.Windows.Controls.Canvas.SetRight(closeImage, 8);
|
||||
System.Windows.Controls.Canvas.SetTop(closeImage, 4);
|
||||
titleCanvas.Children.Add(closeImage);
|
||||
|
||||
titleBorder.Child = titleCanvas;
|
||||
stackPanel.Children.Add(titleBorder);
|
||||
|
||||
// 创建背景选项内容区域
|
||||
var contentPanel = new StackPanel { Margin = new Thickness(8) };
|
||||
|
||||
// 黑板/白板选择
|
||||
var modeTitle = new TextBlock
|
||||
{
|
||||
Text = "白板模式",
|
||||
Foreground = new SolidColorBrush(Color.FromRgb(0x17, 0x25, 0x54)),
|
||||
FontSize = 10,
|
||||
FontWeight = FontWeights.Bold,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
Margin = new Thickness(0, 4, 0, 8)
|
||||
};
|
||||
contentPanel.Children.Add(modeTitle);
|
||||
|
||||
var modePanel = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center };
|
||||
|
||||
// 白板按钮
|
||||
var whiteboardButton = new Border
|
||||
{
|
||||
Width = 60,
|
||||
Height = 30,
|
||||
Background = Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) : new SolidColorBrush(Colors.LightGray),
|
||||
CornerRadius = new CornerRadius(4),
|
||||
Margin = new Thickness(0, 0, 8, 0)
|
||||
};
|
||||
var whiteboardText = new TextBlock
|
||||
{
|
||||
Text = "白板",
|
||||
Foreground = Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Colors.White) : new SolidColorBrush(Colors.Black),
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
whiteboardButton.Child = whiteboardText;
|
||||
whiteboardButton.MouseUp += (s, args) => {
|
||||
Settings.Canvas.UsingWhiteboard = true;
|
||||
SaveSettingsToFile();
|
||||
ICCWaterMarkDark.Visibility = Visibility.Visible;
|
||||
ICCWaterMarkWhite.Visibility = Visibility.Collapsed;
|
||||
|
||||
// 设置为白板默认背景色
|
||||
Color defaultWhiteboardColor = Color.FromRgb(234, 235, 237);
|
||||
|
||||
if (currentMode == 1) // 白板模式
|
||||
{
|
||||
// 设置背景为默认白板背景色
|
||||
GridBackgroundCover.Background = new SolidColorBrush(defaultWhiteboardColor);
|
||||
|
||||
// 更新RGB滑块的值为默认白板背景色
|
||||
UpdateRGBSliders(defaultWhiteboardColor);
|
||||
|
||||
// 更新自定义背景色为默认白板背景色
|
||||
CustomBackgroundColor = defaultWhiteboardColor;
|
||||
|
||||
// 保存到设置
|
||||
string colorHex = $"#{defaultWhiteboardColor.R:X2}{defaultWhiteboardColor.G:X2}{defaultWhiteboardColor.B:X2}";
|
||||
Settings.Canvas.CustomBackgroundColor = colorHex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 设置墨迹颜色为黑色
|
||||
CheckLastColor(0);
|
||||
forceEraser = false;
|
||||
|
||||
CheckColorTheme(true);
|
||||
UpdateBackgroundButtonsState();
|
||||
};
|
||||
modePanel.Children.Add(whiteboardButton);
|
||||
|
||||
// 黑板按钮
|
||||
var blackboardButton = new Border
|
||||
{
|
||||
Width = 60,
|
||||
Height = 30,
|
||||
Background = !Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) : new SolidColorBrush(Colors.LightGray),
|
||||
CornerRadius = new CornerRadius(4)
|
||||
};
|
||||
var blackboardText = new TextBlock
|
||||
{
|
||||
Text = "黑板",
|
||||
Foreground = !Settings.Canvas.UsingWhiteboard ? new SolidColorBrush(Colors.White) : new SolidColorBrush(Colors.Black),
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
blackboardButton.Child = blackboardText;
|
||||
blackboardButton.MouseUp += (s, args) => {
|
||||
Settings.Canvas.UsingWhiteboard = false;
|
||||
SaveSettingsToFile();
|
||||
ICCWaterMarkWhite.Visibility = Visibility.Visible;
|
||||
ICCWaterMarkDark.Visibility = Visibility.Collapsed;
|
||||
|
||||
// 设置为黑板默认背景色
|
||||
Color defaultBlackboardColor = Color.FromRgb(22, 41, 36);
|
||||
|
||||
if (currentMode == 1) // 黑板模式
|
||||
{
|
||||
// 设置背景为默认黑板背景色
|
||||
GridBackgroundCover.Background = new SolidColorBrush(defaultBlackboardColor);
|
||||
|
||||
// 更新RGB滑块的值为默认黑板背景色
|
||||
UpdateRGBSliders(defaultBlackboardColor);
|
||||
|
||||
// 更新自定义背景色为默认黑板背景色
|
||||
CustomBackgroundColor = defaultBlackboardColor;
|
||||
|
||||
// 保存到设置
|
||||
string colorHex = $"#{defaultBlackboardColor.R:X2}{defaultBlackboardColor.G:X2}{defaultBlackboardColor.B:X2}";
|
||||
Settings.Canvas.CustomBackgroundColor = colorHex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 设置墨迹颜色为白色
|
||||
CheckLastColor(5);
|
||||
forceEraser = false;
|
||||
|
||||
CheckColorTheme(true);
|
||||
UpdateBackgroundButtonsState();
|
||||
};
|
||||
modePanel.Children.Add(blackboardButton);
|
||||
|
||||
contentPanel.Children.Add(modePanel);
|
||||
|
||||
// 添加一条分隔线
|
||||
var separator = new Border
|
||||
{
|
||||
Height = 1,
|
||||
Background = new SolidColorBrush(Color.FromRgb(0xd4, 0xd4, 0xd8)),
|
||||
Margin = new Thickness(0, 12, 0, 12)
|
||||
};
|
||||
contentPanel.Children.Add(separator);
|
||||
|
||||
// 添加RGB颜色选择器部分
|
||||
var colorTitle = new TextBlock
|
||||
{
|
||||
Text = "背景颜色",
|
||||
Foreground = new SolidColorBrush(Color.FromRgb(0x17, 0x25, 0x54)),
|
||||
FontSize = 10,
|
||||
FontWeight = FontWeights.Bold,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
Margin = new Thickness(0, 4, 0, 8)
|
||||
};
|
||||
contentPanel.Children.Add(colorTitle);
|
||||
|
||||
// 创建颜色预览
|
||||
Border colorPreview = new Border
|
||||
{
|
||||
Width = 100,
|
||||
Height = 40,
|
||||
BorderThickness = new Thickness(1),
|
||||
BorderBrush = new SolidColorBrush(Color.FromRgb(0xd4, 0xd4, 0xd8)),
|
||||
Background = new SolidColorBrush(Colors.White),
|
||||
CornerRadius = new CornerRadius(4),
|
||||
Margin = new Thickness(0, 0, 0, 10),
|
||||
HorizontalAlignment = HorizontalAlignment.Center
|
||||
};
|
||||
contentPanel.Children.Add(colorPreview);
|
||||
|
||||
// 获取当前背景颜色
|
||||
Color currentBackgroundColor;
|
||||
if (currentMode == 1) // 白板或黑板模式
|
||||
{
|
||||
if (GridBackgroundCover.Background is SolidColorBrush brush)
|
||||
{
|
||||
currentBackgroundColor = brush.Color;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认颜色
|
||||
currentBackgroundColor = Settings.Canvas.UsingWhiteboard ?
|
||||
Color.FromRgb(234, 235, 237) : // 白板默认颜色
|
||||
Color.FromRgb(22, 41, 36); // 黑板默认颜色
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认白色
|
||||
currentBackgroundColor = Colors.White;
|
||||
}
|
||||
|
||||
// 更新颜色预览
|
||||
colorPreview.Background = new SolidColorBrush(currentBackgroundColor);
|
||||
|
||||
// 先创建所有滑块控件
|
||||
// R滑块和文本框
|
||||
var rPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
|
||||
var rLabel = new TextBlock { Text = "R:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
|
||||
var rSlider = new Slider
|
||||
{
|
||||
Minimum = 0,
|
||||
Maximum = 255,
|
||||
Value = currentBackgroundColor.R,
|
||||
Width = 150,
|
||||
Margin = new Thickness(5, 0, 5, 0),
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
var rValueText = new TextBlock
|
||||
{
|
||||
Text = currentBackgroundColor.R.ToString(),
|
||||
Width = 30,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextAlignment = TextAlignment.Right
|
||||
};
|
||||
|
||||
// G滑块和文本框
|
||||
var gPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
|
||||
var gLabel = new TextBlock { Text = "G:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
|
||||
var gSlider = new Slider
|
||||
{
|
||||
Minimum = 0,
|
||||
Maximum = 255,
|
||||
Value = currentBackgroundColor.G,
|
||||
Width = 150,
|
||||
Margin = new Thickness(5, 0, 5, 0),
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
var gValueText = new TextBlock
|
||||
{
|
||||
Text = currentBackgroundColor.G.ToString(),
|
||||
Width = 30,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextAlignment = TextAlignment.Right
|
||||
};
|
||||
|
||||
// B滑块和文本框
|
||||
var bPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
|
||||
var bLabel = new TextBlock { Text = "B:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
|
||||
var bSlider = new Slider
|
||||
{
|
||||
Minimum = 0,
|
||||
Maximum = 255,
|
||||
Value = currentBackgroundColor.B,
|
||||
Width = 150,
|
||||
Margin = new Thickness(5, 0, 5, 0),
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
var bValueText = new TextBlock
|
||||
{
|
||||
Text = currentBackgroundColor.B.ToString(),
|
||||
Width = 30,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextAlignment = TextAlignment.Right
|
||||
};
|
||||
|
||||
// 现在添加事件处理程序
|
||||
rSlider.ValueChanged += (s, e) => {
|
||||
int value = (int)e.NewValue;
|
||||
rValueText.Text = value.ToString();
|
||||
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
|
||||
};
|
||||
|
||||
gSlider.ValueChanged += (s, e) => {
|
||||
int value = (int)e.NewValue;
|
||||
gValueText.Text = value.ToString();
|
||||
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
|
||||
};
|
||||
|
||||
bSlider.ValueChanged += (s, e) => {
|
||||
int value = (int)e.NewValue;
|
||||
bValueText.Text = value.ToString();
|
||||
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
|
||||
};
|
||||
|
||||
// 添加控件到面板
|
||||
rPanel.Children.Add(rLabel);
|
||||
rPanel.Children.Add(rSlider);
|
||||
rPanel.Children.Add(rValueText);
|
||||
contentPanel.Children.Add(rPanel);
|
||||
|
||||
gPanel.Children.Add(gLabel);
|
||||
gPanel.Children.Add(gSlider);
|
||||
gPanel.Children.Add(gValueText);
|
||||
contentPanel.Children.Add(gPanel);
|
||||
|
||||
bPanel.Children.Add(bLabel);
|
||||
bPanel.Children.Add(bSlider);
|
||||
bPanel.Children.Add(bValueText);
|
||||
contentPanel.Children.Add(bPanel);
|
||||
|
||||
// 应用按钮
|
||||
var applyButton = new Button
|
||||
{
|
||||
Content = "应用颜色",
|
||||
Margin = new Thickness(0, 10, 0, 0),
|
||||
Padding = new Thickness(10, 5, 10, 5),
|
||||
Background = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
|
||||
Foreground = new SolidColorBrush(Colors.White),
|
||||
BorderThickness = new Thickness(0),
|
||||
HorizontalAlignment = HorizontalAlignment.Center
|
||||
};
|
||||
|
||||
applyButton.Click += (s, e) => {
|
||||
Color selectedColor = Color.FromRgb(
|
||||
(byte)rSlider.Value,
|
||||
(byte)gSlider.Value,
|
||||
(byte)bSlider.Value
|
||||
);
|
||||
ApplyCustomBackgroundColor(selectedColor);
|
||||
};
|
||||
|
||||
contentPanel.Children.Add(applyButton);
|
||||
|
||||
stackPanel.Children.Add(contentPanel);
|
||||
|
||||
// 将面板添加到父容器
|
||||
BackgroundPalette.Child = stackPanel;
|
||||
|
||||
// 获取主窗口中的根网格,确保面板添加到顶层
|
||||
Grid mainGrid = FindName("Main_Grid") as Grid;
|
||||
if (mainGrid != null)
|
||||
{
|
||||
// 删除可能已存在的BackgroundPalette
|
||||
foreach (UIElement element in mainGrid.Children)
|
||||
{
|
||||
if (element is Border border && border.Name == "BackgroundPalette")
|
||||
{
|
||||
mainGrid.Children.Remove(border);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 重新定位面板
|
||||
BackgroundPalette.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
BackgroundPalette.VerticalAlignment = VerticalAlignment.Center;
|
||||
BackgroundPalette.Margin = new Thickness(0, 0, 0, 0);
|
||||
|
||||
// 添加到主网格
|
||||
mainGrid.Children.Add(BackgroundPalette);
|
||||
|
||||
// 设置面板位置
|
||||
var clickElement = FindName("BoardChangeBackgroundColorBtn") as FrameworkElement;
|
||||
if (clickElement != null)
|
||||
{
|
||||
Point position = clickElement.TranslatePoint(new Point(0, 0), mainGrid);
|
||||
BackgroundPalette.Margin = new Thickness(
|
||||
position.X - 150,
|
||||
position.Y + clickElement.ActualHeight + 5,
|
||||
0, 0);
|
||||
BackgroundPalette.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
BackgroundPalette.VerticalAlignment = VerticalAlignment.Top;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新背景按钮状态
|
||||
private void UpdateBackgroundButtonsState()
|
||||
{
|
||||
if (BackgroundPalette != null && BackgroundPalette.Child is StackPanel stackPanel)
|
||||
{
|
||||
if (stackPanel.Children.Count > 1 && stackPanel.Children[1] is StackPanel contentPanel)
|
||||
{
|
||||
if (contentPanel.Children.Count > 1 && contentPanel.Children[1] is StackPanel modePanel)
|
||||
{
|
||||
if (modePanel.Children.Count > 1)
|
||||
{
|
||||
var whiteboardButton = modePanel.Children[0] as Border;
|
||||
var blackboardButton = modePanel.Children[1] as Border;
|
||||
|
||||
if (whiteboardButton != null && whiteboardButton.Child is TextBlock whiteboardText)
|
||||
{
|
||||
whiteboardButton.Background = Settings.Canvas.UsingWhiteboard ?
|
||||
new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) :
|
||||
new SolidColorBrush(Colors.LightGray);
|
||||
whiteboardText.Foreground = Settings.Canvas.UsingWhiteboard ?
|
||||
new SolidColorBrush(Colors.White) :
|
||||
new SolidColorBrush(Colors.Black);
|
||||
}
|
||||
|
||||
if (blackboardButton != null && blackboardButton.Child is TextBlock blackboardText)
|
||||
{
|
||||
blackboardButton.Background = !Settings.Canvas.UsingWhiteboard ?
|
||||
new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) :
|
||||
new SolidColorBrush(Colors.LightGray);
|
||||
blackboardText.Foreground = !Settings.Canvas.UsingWhiteboard ?
|
||||
new SolidColorBrush(Colors.White) :
|
||||
new SolidColorBrush(Colors.Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加成员变量保存背景面板引用
|
||||
private Border BackgroundPalette { get; set; }
|
||||
|
||||
// 添加成员变量保存当前自定义背景色
|
||||
private Color? CustomBackgroundColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新颜色预览框的颜色
|
||||
/// </summary>
|
||||
private void UpdateColorPreview(Border colorPreview, Slider rSlider, Slider gSlider, Slider bSlider)
|
||||
{
|
||||
Color previewColor = Color.FromRgb(
|
||||
(byte)rSlider.Value,
|
||||
(byte)gSlider.Value,
|
||||
(byte)bSlider.Value
|
||||
);
|
||||
colorPreview.Background = new SolidColorBrush(previewColor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用自定义背景颜色
|
||||
/// </summary>
|
||||
private void ApplyCustomBackgroundColor(Color color)
|
||||
{
|
||||
// 保存当前选择的颜色
|
||||
CustomBackgroundColor = color;
|
||||
|
||||
// 将颜色转换为十六进制字符串并保存到设置中
|
||||
string colorHex = $"#{color.R:X2}{color.G:X2}{color.B:X2}";
|
||||
Settings.Canvas.CustomBackgroundColor = colorHex;
|
||||
|
||||
// 只在白板或黑板模式下应用自定义背景色
|
||||
if (currentMode == 1) // 白板或黑板模式
|
||||
{
|
||||
// 设置白板/黑板模式下的背景
|
||||
GridBackgroundCover.Background = new SolidColorBrush(color);
|
||||
}
|
||||
|
||||
// 保存设置
|
||||
SaveSettingsToFile();
|
||||
|
||||
// 立即更新界面
|
||||
if (BackgroundPalette != null)
|
||||
{
|
||||
UpdateBackgroundButtonsState();
|
||||
UpdateRGBSliders(color); // 更新RGB滑块的值
|
||||
}
|
||||
|
||||
// 显示提示信息
|
||||
ShowNotification($"已应用自定义背景色: {colorHex}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从设置中加载自定义背景色
|
||||
/// </summary>
|
||||
private void LoadCustomBackgroundColor()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Settings.Canvas.CustomBackgroundColor))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 解析颜色字符串
|
||||
string colorHex = Settings.Canvas.CustomBackgroundColor;
|
||||
if (colorHex.StartsWith("#") && colorHex.Length == 7) // #RRGGBB 格式
|
||||
{
|
||||
byte r = Convert.ToByte(colorHex.Substring(1, 2), 16);
|
||||
byte g = Convert.ToByte(colorHex.Substring(3, 2), 16);
|
||||
byte b = Convert.ToByte(colorHex.Substring(5, 2), 16);
|
||||
|
||||
// 保存到内存中
|
||||
CustomBackgroundColor = Color.FromRgb(r, g, b);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 解析失败,根据当前模式设置默认颜色
|
||||
if (!Settings.Canvas.UsingWhiteboard)
|
||||
{
|
||||
// 黑板模式默认颜色
|
||||
CustomBackgroundColor = Color.FromRgb(22, 41, 36);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 白板模式默认颜色
|
||||
CustomBackgroundColor = Color.FromRgb(234, 235, 237);
|
||||
}
|
||||
|
||||
// 可以在这里记录日志
|
||||
Console.WriteLine($"解析自定义背景色失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有设置自定义背景色,根据当前模式设置默认颜色
|
||||
if (!Settings.Canvas.UsingWhiteboard)
|
||||
{
|
||||
// 黑板模式默认颜色
|
||||
CustomBackgroundColor = Color.FromRgb(22, 41, 36);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 白板模式默认颜色
|
||||
CustomBackgroundColor = Color.FromRgb(234, 235, 237);
|
||||
}
|
||||
}
|
||||
|
||||
// 只在白板或黑板模式下应用自定义背景色
|
||||
if (currentMode == 1 && CustomBackgroundColor.HasValue) // 白板或黑板模式
|
||||
{
|
||||
// 设置白板/黑板模式下的背景
|
||||
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
|
||||
|
||||
// 更新RGB滑块的值(如果调色板已经创建)
|
||||
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateRGBSliders(CustomBackgroundColor.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BoardLassoIcon_Click(object sender, RoutedEventArgs e) {
|
||||
forceEraser = false;
|
||||
forcePointEraser = false;
|
||||
drawingShapeMode = 0;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
}
|
||||
|
||||
private void BoardEraserIconByStrokes_Click(object sender, RoutedEventArgs e) {
|
||||
//if (BoardEraserByStrokes.Background.ToString() == "#FF679CF4") {
|
||||
@@ -104,12 +717,44 @@ namespace Ink_Canvas {
|
||||
private void BoardSymbolIconDelete_MouseUp(object sender, RoutedEventArgs e) {
|
||||
PenIcon_Click(null, null);
|
||||
SymbolIconDelete_MouseUp(null, null);
|
||||
|
||||
// 根据设置决定是否清空图片
|
||||
if (Settings.Canvas.ClearCanvasAlsoClearImages) {
|
||||
// 如果设置为清空图片,则直接清空所有子元素
|
||||
Debug.WriteLine("BoardSymbolIconDelete: Clearing all children including images");
|
||||
inkCanvas.Children.Clear();
|
||||
} else {
|
||||
// 保存非笔画元素(如图片)
|
||||
Debug.WriteLine("BoardSymbolIconDelete: Preserving non-stroke elements (images)");
|
||||
var preservedElements = PreserveNonStrokeElements();
|
||||
Debug.WriteLine($"BoardSymbolIconDelete: Preserved elements count: {preservedElements.Count}");
|
||||
inkCanvas.Children.Clear();
|
||||
// 恢复非笔画元素
|
||||
RestoreNonStrokeElements(preservedElements);
|
||||
Debug.WriteLine($"BoardSymbolIconDelete: inkCanvas.Children.Count after restore: {inkCanvas.Children.Count}");
|
||||
}
|
||||
}
|
||||
private void BoardSymbolIconDeleteInkAndHistories_MouseUp(object sender, RoutedEventArgs e)
|
||||
{
|
||||
PenIcon_Click(null, null);
|
||||
SymbolIconDelete_MouseUp(null, null);
|
||||
if (Settings.Canvas.ClearCanvasAndClearTimeMachine == false) timeMachine.ClearStrokeHistory();
|
||||
|
||||
// 根据设置决定是否清空图片
|
||||
if (Settings.Canvas.ClearCanvasAlsoClearImages) {
|
||||
// 如果设置为清空图片,则直接清空所有子元素
|
||||
Debug.WriteLine("BoardSymbolIconDeleteInkAndHistories: Clearing all children including images");
|
||||
inkCanvas.Children.Clear();
|
||||
} else {
|
||||
// 保存非笔画元素(如图片)
|
||||
Debug.WriteLine("BoardSymbolIconDeleteInkAndHistories: Preserving non-stroke elements (images)");
|
||||
var preservedElements = PreserveNonStrokeElements();
|
||||
Debug.WriteLine($"BoardSymbolIconDeleteInkAndHistories: Preserved elements count: {preservedElements.Count}");
|
||||
inkCanvas.Children.Clear();
|
||||
// 恢复非笔画元素
|
||||
RestoreNonStrokeElements(preservedElements);
|
||||
Debug.WriteLine($"BoardSymbolIconDeleteInkAndHistories: inkCanvas.Children.Count after restore: {inkCanvas.Children.Count}");
|
||||
}
|
||||
}
|
||||
|
||||
private void BoardLaunchEasiCamera_MouseUp(object sender, MouseButtonEventArgs e) {
|
||||
@@ -122,5 +767,59 @@ namespace Ink_Canvas {
|
||||
ImageBlackboard_MouseUp(null, null);
|
||||
Process.Start("https://www.desmos.com/calculator?lang=zh-CN");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据当前背景颜色更新RGB滑块的值
|
||||
/// </summary>
|
||||
private void UpdateRGBSliders(Color color)
|
||||
{
|
||||
if (BackgroundPalette != null && BackgroundPalette.Child is StackPanel stackPanel)
|
||||
{
|
||||
if (stackPanel.Children.Count > 1 && stackPanel.Children[1] is StackPanel contentPanel)
|
||||
{
|
||||
// 查找RGB滑块
|
||||
Slider rSlider = null;
|
||||
Slider gSlider = null;
|
||||
Slider bSlider = null;
|
||||
|
||||
// 遍历面板查找RGB滑块
|
||||
foreach (var child in contentPanel.Children)
|
||||
{
|
||||
if (child is StackPanel panel && panel.Orientation == Orientation.Horizontal)
|
||||
{
|
||||
foreach (var panelChild in panel.Children)
|
||||
{
|
||||
if (panelChild is Slider slider)
|
||||
{
|
||||
if (panel.Children.Count > 0 && panel.Children[0] is TextBlock label)
|
||||
{
|
||||
if (label.Text == "R:")
|
||||
{
|
||||
rSlider = slider;
|
||||
}
|
||||
else if (label.Text == "G:")
|
||||
{
|
||||
gSlider = slider;
|
||||
}
|
||||
else if (label.Text == "B:")
|
||||
{
|
||||
bSlider = slider;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新滑块值
|
||||
if (rSlider != null && gSlider != null && bSlider != null)
|
||||
{
|
||||
rSlider.Value = color.R;
|
||||
gSlider.Value = color.G;
|
||||
bSlider.Value = color.B;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Media;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Ink_Canvas.Helpers;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
@@ -54,15 +52,14 @@ namespace Ink_Canvas {
|
||||
drawingShapeMode = 0;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
CancelSingleFingerDragMode();
|
||||
forceEraser = false;
|
||||
CheckColorTheme();
|
||||
}
|
||||
|
||||
isLongPressSelected = false;
|
||||
}
|
||||
|
||||
private bool isUselightThemeColor = false, isDesktopUselightThemeColor = false;
|
||||
private int penType = 0; // 0是签字笔,1是荧光笔
|
||||
private bool isUselightThemeColor, isDesktopUselightThemeColor;
|
||||
private int penType; // 0是签字笔,1是荧光笔
|
||||
private int lastDesktopInkColor = 1, lastBoardInkColor = 5;
|
||||
private int highlighterColor = 102;
|
||||
|
||||
@@ -70,14 +67,24 @@ namespace Ink_Canvas {
|
||||
if (changeColorTheme)
|
||||
if (currentMode != 0) {
|
||||
if (Settings.Canvas.UsingWhiteboard) {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(Color.FromRgb(234, 235, 237));
|
||||
// 检查是否有自定义背景色,如果有则使用自定义背景色
|
||||
if (CustomBackgroundColor.HasValue) {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
|
||||
} else {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(Color.FromRgb(234, 235, 237));
|
||||
}
|
||||
WaterMarkTime.Foreground = new SolidColorBrush(Color.FromRgb(22, 41, 36));
|
||||
WaterMarkDate.Foreground = new SolidColorBrush(Color.FromRgb(22, 41, 36));
|
||||
BlackBoardWaterMark.Foreground = new SolidColorBrush(Color.FromRgb(22, 41, 36));
|
||||
isUselightThemeColor = false;
|
||||
}
|
||||
else {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(Color.FromRgb(22, 41, 36));
|
||||
// 黑板模式下,检查是否有自定义背景色
|
||||
if (CustomBackgroundColor.HasValue) {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
|
||||
} else {
|
||||
GridBackgroundCover.Background = new SolidColorBrush(Color.FromRgb(22, 41, 36));
|
||||
}
|
||||
WaterMarkTime.Foreground = new SolidColorBrush(Color.FromRgb(234, 235, 237));
|
||||
WaterMarkDate.Foreground = new SolidColorBrush(Color.FromRgb(234, 235, 237));
|
||||
BlackBoardWaterMark.Foreground = new SolidColorBrush(Color.FromRgb(234, 235, 237));
|
||||
@@ -380,7 +387,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private void CheckLastColor(int inkColor, bool isHighlighter = false) {
|
||||
if (isHighlighter == true) {
|
||||
if (isHighlighter) {
|
||||
highlighterColor = inkColor;
|
||||
}
|
||||
else {
|
||||
@@ -543,62 +550,52 @@ namespace Ink_Canvas {
|
||||
|
||||
private void BtnColorBlack_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(0);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
private void BtnColorRed_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(1);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
private void BtnColorGreen_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(2);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
private void BtnColorBlue_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(3);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
private void BtnColorYellow_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(4);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
private void BtnColorWhite_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(5);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
private void BtnColorPink_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(6);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
private void BtnColorOrange_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(8);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
private void BtnColorTeal_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(7);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
private void BtnHighlighterColorBlack_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(100, true);
|
||||
penType = 1;
|
||||
forceEraser = false;
|
||||
CheckPenTypeUIState();
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
@@ -606,7 +603,6 @@ namespace Ink_Canvas {
|
||||
private void BtnHighlighterColorWhite_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(101, true);
|
||||
penType = 1;
|
||||
forceEraser = false;
|
||||
CheckPenTypeUIState();
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
@@ -614,7 +610,6 @@ namespace Ink_Canvas {
|
||||
private void BtnHighlighterColorRed_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(102, true);
|
||||
penType = 1;
|
||||
forceEraser = false;
|
||||
CheckPenTypeUIState();
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
@@ -622,7 +617,6 @@ namespace Ink_Canvas {
|
||||
private void BtnHighlighterColorYellow_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(103, true);
|
||||
penType = 1;
|
||||
forceEraser = false;
|
||||
CheckPenTypeUIState();
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
@@ -630,7 +624,6 @@ namespace Ink_Canvas {
|
||||
private void BtnHighlighterColorGreen_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(104, true);
|
||||
penType = 1;
|
||||
forceEraser = false;
|
||||
CheckPenTypeUIState();
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
@@ -638,7 +631,6 @@ namespace Ink_Canvas {
|
||||
private void BtnHighlighterColorZinc_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(105, true);
|
||||
penType = 1;
|
||||
forceEraser = false;
|
||||
CheckPenTypeUIState();
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
@@ -646,7 +638,6 @@ namespace Ink_Canvas {
|
||||
private void BtnHighlighterColorBlue_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(106, true);
|
||||
penType = 1;
|
||||
forceEraser = false;
|
||||
CheckPenTypeUIState();
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
@@ -654,7 +645,6 @@ namespace Ink_Canvas {
|
||||
private void BtnHighlighterColorPurple_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(107, true);
|
||||
penType = 1;
|
||||
forceEraser = false;
|
||||
CheckPenTypeUIState();
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
@@ -662,7 +652,6 @@ namespace Ink_Canvas {
|
||||
private void BtnHighlighterColorTeal_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(108, true);
|
||||
penType = 1;
|
||||
forceEraser = false;
|
||||
CheckPenTypeUIState();
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
@@ -670,7 +659,6 @@ namespace Ink_Canvas {
|
||||
private void BtnHighlighterColorOrange_Click(object sender, RoutedEventArgs e) {
|
||||
CheckLastColor(109, true);
|
||||
penType = 1;
|
||||
forceEraser = false;
|
||||
CheckPenTypeUIState();
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
#region Image
|
||||
private async void BtnImageInsert_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OpenFileDialog openFileDialog = new OpenFileDialog();
|
||||
openFileDialog.Filter = "Image files (*.jpg; *.jpeg; *.png; *.bmp)|*.jpg;*.jpeg;*.png;*.bmp";
|
||||
|
||||
if (openFileDialog.ShowDialog() == true)
|
||||
{
|
||||
string filePath = openFileDialog.FileName;
|
||||
|
||||
Image image = await CreateAndCompressImageAsync(filePath);
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
|
||||
image.Name = timestamp;
|
||||
|
||||
// 新缩放逻辑:最大宽高为画布一半,并居中
|
||||
double maxWidth = inkCanvas.ActualWidth / 2;
|
||||
double maxHeight = inkCanvas.ActualHeight / 2;
|
||||
double scaleX = maxWidth / image.Width;
|
||||
double scaleY = maxHeight / image.Height;
|
||||
double scale = Math.Min(1, Math.Min(scaleX, scaleY));
|
||||
image.Width = image.Width * scale;
|
||||
image.Height = image.Height * scale;
|
||||
InkCanvas.SetLeft(image, (inkCanvas.ActualWidth - image.Width) / 2);
|
||||
InkCanvas.SetTop(image, (inkCanvas.ActualHeight - image.Height) / 2);
|
||||
|
||||
inkCanvas.Children.Add(image);
|
||||
|
||||
// 添加鼠标事件处理,使图片可以被选择
|
||||
image.MouseDown += UIElement_MouseDown;
|
||||
image.IsManipulationEnabled = true;
|
||||
|
||||
timeMachine.CommitElementInsertHistory(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Image> CreateAndCompressImageAsync(string filePath)
|
||||
{
|
||||
string savePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "File Dependency");
|
||||
if (!Directory.Exists(savePath))
|
||||
{
|
||||
Directory.CreateDirectory(savePath);
|
||||
}
|
||||
|
||||
string fileExtension = Path.GetExtension(filePath);
|
||||
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
|
||||
string newFilePath = Path.Combine(savePath, timestamp + fileExtension);
|
||||
|
||||
await Task.Run(() => File.Copy(filePath, newFilePath, true));
|
||||
|
||||
return await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
BitmapImage bitmapImage = new BitmapImage();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.UriSource = new Uri(newFilePath);
|
||||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmapImage.EndInit();
|
||||
|
||||
int width = bitmapImage.PixelWidth;
|
||||
int height = bitmapImage.PixelHeight;
|
||||
|
||||
Image image = new Image();
|
||||
// 设置拉伸模式为Fill,支持任意比例缩放
|
||||
image.Stretch = Stretch.Fill;
|
||||
|
||||
if (isLoaded && Settings.Canvas.IsCompressPicturesUploaded && (width > 1920 || height > 1080))
|
||||
{
|
||||
double scaleX = 1920.0 / width;
|
||||
double scaleY = 1080.0 / height;
|
||||
double scale = Math.Min(scaleX, scaleY);
|
||||
|
||||
TransformedBitmap transformedBitmap = new TransformedBitmap(bitmapImage, new ScaleTransform(scale, scale));
|
||||
|
||||
image.Source = transformedBitmap;
|
||||
image.Width = transformedBitmap.PixelWidth;
|
||||
image.Height = transformedBitmap.PixelHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
image.Source = bitmapImage;
|
||||
image.Width = width;
|
||||
image.Height = height;
|
||||
}
|
||||
|
||||
return image;
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Media
|
||||
private async void BtnMediaInsert_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OpenFileDialog openFileDialog = new OpenFileDialog();
|
||||
openFileDialog.Filter = "Media files (*.mp4; *.avi; *.wmv)|*.mp4;*.avi;*.wmv";
|
||||
|
||||
if (openFileDialog.ShowDialog() == true)
|
||||
{
|
||||
string filePath = openFileDialog.FileName;
|
||||
|
||||
byte[] mediaBytes = await Task.Run(() => File.ReadAllBytes(filePath));
|
||||
|
||||
MediaElement mediaElement = await CreateMediaElementAsync(filePath);
|
||||
|
||||
if (mediaElement != null)
|
||||
{
|
||||
InkCanvas.SetLeft(mediaElement, 0);
|
||||
InkCanvas.SetTop(mediaElement, 0);
|
||||
inkCanvas.Children.Add(mediaElement);
|
||||
|
||||
// 添加鼠标事件处理,使媒体元素可以被选择
|
||||
mediaElement.MouseDown += UIElement_MouseDown;
|
||||
mediaElement.IsManipulationEnabled = true;
|
||||
|
||||
mediaElement.LoadedBehavior = MediaState.Manual;
|
||||
mediaElement.UnloadedBehavior = MediaState.Manual;
|
||||
mediaElement.Loaded += async (_, args) =>
|
||||
{
|
||||
mediaElement.Play();
|
||||
await Task.Delay(100);
|
||||
mediaElement.Pause();
|
||||
};
|
||||
|
||||
timeMachine.CommitElementInsertHistory(mediaElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<MediaElement> CreateMediaElementAsync(string filePath)
|
||||
{
|
||||
string savePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "File Dependency");
|
||||
if (!Directory.Exists(savePath))
|
||||
{
|
||||
Directory.CreateDirectory(savePath);
|
||||
}
|
||||
return await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
MediaElement mediaElement = new MediaElement();
|
||||
mediaElement.Source = new Uri(filePath);
|
||||
string timestamp = "media_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
|
||||
mediaElement.Name = timestamp;
|
||||
mediaElement.LoadedBehavior = MediaState.Manual;
|
||||
mediaElement.UnloadedBehavior = MediaState.Manual;
|
||||
|
||||
mediaElement.Width = 256;
|
||||
mediaElement.Height = 256;
|
||||
|
||||
string fileExtension = Path.GetExtension(filePath);
|
||||
string newFilePath = Path.Combine(savePath, mediaElement.Name + fileExtension);
|
||||
|
||||
File.Copy(filePath, newFilePath, true);
|
||||
|
||||
mediaElement.Source = new Uri(newFilePath);
|
||||
|
||||
return mediaElement;
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Image Operations
|
||||
|
||||
/// <summary>
|
||||
/// 旋转图片
|
||||
/// </summary>
|
||||
/// <param name="image">要旋转的图片</param>
|
||||
/// <param name="angle">旋转角度(正数为顺时针,负数为逆时针)</param>
|
||||
private void RotateImage(Image image, double angle)
|
||||
{
|
||||
if (image == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
// 获取当前的变换
|
||||
var transformGroup = image.RenderTransform as TransformGroup ?? new TransformGroup();
|
||||
|
||||
// 查找现有的旋转变换
|
||||
RotateTransform rotateTransform = null;
|
||||
foreach (Transform transform in transformGroup.Children)
|
||||
{
|
||||
if (transform is RotateTransform rt)
|
||||
{
|
||||
rotateTransform = rt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有旋转变换,创建一个新的
|
||||
if (rotateTransform == null)
|
||||
{
|
||||
rotateTransform = new RotateTransform();
|
||||
transformGroup.Children.Add(rotateTransform);
|
||||
}
|
||||
|
||||
// 设置旋转中心为图片中心
|
||||
rotateTransform.CenterX = image.ActualWidth / 2;
|
||||
rotateTransform.CenterY = image.ActualHeight / 2;
|
||||
|
||||
// 累加旋转角度
|
||||
rotateTransform.Angle = (rotateTransform.Angle + angle) % 360;
|
||||
|
||||
// 应用变换
|
||||
image.RenderTransform = transformGroup;
|
||||
|
||||
// 提交到时间机器以支持撤销
|
||||
// 注意:旋转操作目前不支持撤销,因为需要更复杂的历史记录机制
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录错误但不中断程序
|
||||
System.Diagnostics.Debug.WriteLine($"旋转图片时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除图片
|
||||
/// </summary>
|
||||
/// <param name="image">要删除的图片</param>
|
||||
private void DeleteImage(Image image)
|
||||
{
|
||||
if (image == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
// 从画布中移除图片
|
||||
if (inkCanvas.Children.Contains(image))
|
||||
{
|
||||
inkCanvas.Children.Remove(image);
|
||||
|
||||
// 取消选择
|
||||
DeselectUIElement();
|
||||
|
||||
// 提交到时间机器以支持撤销
|
||||
timeMachine.CommitElementRemoveHistory(image);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录错误但不中断程序
|
||||
System.Diagnostics.Debug.WriteLine($"删除图片时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,735 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using Ink_Canvas.Helpers;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
|
||||
// 新橡皮擦系统的核心变量
|
||||
public bool isUsingAdvancedEraser;
|
||||
private IncrementalStrokeHitTester advancedHitTester;
|
||||
|
||||
// 橡皮擦配置
|
||||
public double currentEraserSize = 64;
|
||||
public bool isCurrentEraserCircle;
|
||||
public bool isUsingStrokeEraser;
|
||||
|
||||
// 视觉反馈相关
|
||||
private Matrix eraserTransformMatrix;
|
||||
private Point lastEraserPosition;
|
||||
private bool isEraserVisible;
|
||||
|
||||
// 性能优化相关
|
||||
private DateTime lastEraserUpdate = DateTime.Now;
|
||||
private const double ERASER_UPDATE_INTERVAL = 16.67; // 约60FPS
|
||||
|
||||
// 锁定笔画的GUID(如果不存在则创建一个默认值)
|
||||
private static readonly Guid IsLockGuid = new Guid("12345678-1234-1234-1234-123456789ABC");
|
||||
|
||||
// 橡皮擦视觉反馈控件
|
||||
private DrawingVisual eraserVisual = new DrawingVisual();
|
||||
private VisualCanvas eraserOverlayCanvas = null;
|
||||
private Border eraserVisualBorder; // 用于显示橡皮擦视觉反馈的Border
|
||||
|
||||
// 兼容性属性:模拟原有的EraserOverlay_DrawingVisual
|
||||
private VisualCanvas EraserOverlay_DrawingVisual => eraserOverlayCanvas;
|
||||
|
||||
// 兼容性保持
|
||||
[Obsolete("使用 isUsingAdvancedEraser 替代")]
|
||||
public bool isUsingGeometryEraser
|
||||
{
|
||||
get => isUsingAdvancedEraser;
|
||||
set => isUsingAdvancedEraser = value;
|
||||
}
|
||||
|
||||
[Obsolete("使用 currentEraserSize 替代")]
|
||||
public double eraserWidth
|
||||
{
|
||||
get => currentEraserSize;
|
||||
set => currentEraserSize = value;
|
||||
}
|
||||
|
||||
[Obsolete("使用 isCurrentEraserCircle 替代")]
|
||||
public bool isEraserCircleShape
|
||||
{
|
||||
get => isCurrentEraserCircle;
|
||||
set => isCurrentEraserCircle = value;
|
||||
}
|
||||
|
||||
[Obsolete("使用 isUsingStrokeEraser 替代")]
|
||||
public bool isUsingStrokesEraser
|
||||
{
|
||||
get => isUsingStrokeEraser;
|
||||
set => isUsingStrokeEraser = value;
|
||||
}
|
||||
|
||||
[Obsolete("使用 eraserTransformMatrix 替代")]
|
||||
private Matrix scaleMatrix
|
||||
{
|
||||
get => eraserTransformMatrix;
|
||||
set => eraserTransformMatrix = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新橡皮擦覆盖层加载事件处理
|
||||
/// </summary>
|
||||
private void EraserOverlay_Loaded(object sender, RoutedEventArgs e) {
|
||||
var border = (Border)sender;
|
||||
|
||||
// 初始化覆盖层
|
||||
InitializeEraserOverlay(border);
|
||||
|
||||
Trace.WriteLine("Advanced Eraser: Overlay loaded and initialized");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始高级橡皮擦操作
|
||||
/// </summary>
|
||||
private void StartAdvancedEraserOperation(object sender) {
|
||||
if (isUsingAdvancedEraser) return;
|
||||
|
||||
// 设置操作状态
|
||||
isUsingAdvancedEraser = true;
|
||||
isEraserVisible = true;
|
||||
|
||||
// 更新橡皮擦尺寸
|
||||
UpdateEraserSize();
|
||||
|
||||
// 获取inkCanvas引用
|
||||
var inkCanvas = FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas == null) return;
|
||||
|
||||
// 根据橡皮擦形状创建碰撞检测器
|
||||
StylusShape eraserShape = CreateEraserShape();
|
||||
advancedHitTester = inkCanvas.Strokes.GetIncrementalStrokeHitTester(eraserShape);
|
||||
advancedHitTester.StrokeHit += OnAdvancedEraserStrokeHit;
|
||||
|
||||
// 初始化变换矩阵
|
||||
InitializeEraserTransform();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建橡皮擦形状
|
||||
/// </summary>
|
||||
private StylusShape CreateEraserShape()
|
||||
{
|
||||
if (isCurrentEraserCircle) {
|
||||
return new EllipseStylusShape(currentEraserSize, currentEraserSize);
|
||||
}
|
||||
|
||||
// 矩形橡皮擦,使用与原来相同的逻辑
|
||||
return new RectangleStylusShape(currentEraserSize, currentEraserSize / 0.6);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化橡皮擦变换矩阵
|
||||
/// </summary>
|
||||
private void InitializeEraserTransform() {
|
||||
eraserTransformMatrix = new Matrix();
|
||||
|
||||
if (isCurrentEraserCircle) {
|
||||
// 圆形橡皮擦:等比例缩放
|
||||
var scale = currentEraserSize / 56.0; // 基于56x56的基准尺寸
|
||||
eraserTransformMatrix.ScaleAt(scale, scale, 0, 0);
|
||||
} else {
|
||||
// 矩形橡皮擦:保持传统比例
|
||||
var scaleX = currentEraserSize / 38.0;
|
||||
var scaleY = (currentEraserSize * 56 / 38) / 56.0;
|
||||
eraserTransformMatrix.ScaleAt(scaleX, scaleY, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新橡皮擦尺寸
|
||||
/// </summary>
|
||||
private void UpdateEraserSize() {
|
||||
// 使用与原来相同的逻辑计算橡皮擦尺寸
|
||||
double k = 1.0;
|
||||
|
||||
switch (Settings.Canvas.EraserSize) {
|
||||
case 0: k = Settings.Canvas.EraserShapeType == 0 ? 0.5 : 0.7; break;
|
||||
case 1: k = Settings.Canvas.EraserShapeType == 0 ? 0.8 : 0.9; break;
|
||||
case 2: k = 1.0; break;
|
||||
case 3: k = Settings.Canvas.EraserShapeType == 0 ? 1.25 : 1.2; break;
|
||||
case 4: k = Settings.Canvas.EraserShapeType == 0 ? 1.5 : 1.3; break;
|
||||
}
|
||||
|
||||
// 更新形状类型
|
||||
isCurrentEraserCircle = (Settings.Canvas.EraserShapeType == 0);
|
||||
|
||||
// 根据形状类型设置尺寸
|
||||
if (isCurrentEraserCircle) {
|
||||
currentEraserSize = k * 90; // 圆形橡皮擦
|
||||
} else {
|
||||
currentEraserSize = k * 90 * 0.6; // 矩形橡皮擦宽度
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束高级橡皮擦操作
|
||||
/// </summary>
|
||||
private void EndAdvancedEraserOperation(object sender) {
|
||||
if (!isUsingAdvancedEraser) return;
|
||||
|
||||
// 重置操作状态
|
||||
isUsingAdvancedEraser = false;
|
||||
isEraserVisible = false;
|
||||
|
||||
// 释放鼠标捕获
|
||||
if (sender is Border border) {
|
||||
border.ReleaseMouseCapture();
|
||||
}
|
||||
|
||||
// 隐藏橡皮擦视觉反馈
|
||||
HideEraserFeedback();
|
||||
|
||||
// 结束碰撞检测
|
||||
if (advancedHitTester != null) {
|
||||
advancedHitTester.EndHitTesting();
|
||||
advancedHitTester = null;
|
||||
}
|
||||
|
||||
// 提交橡皮擦历史记录
|
||||
CommitEraserHistory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏橡皮擦视觉反馈
|
||||
/// </summary>
|
||||
private void HideEraserFeedback() {
|
||||
try {
|
||||
if (eraserVisualBorder != null) {
|
||||
eraserVisualBorder.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error hiding feedback - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交橡皮擦历史记录
|
||||
/// </summary>
|
||||
private void CommitEraserHistory() {
|
||||
try {
|
||||
if (ReplacedStroke != null || AddedStroke != null) {
|
||||
timeMachine.CommitStrokeEraseHistory(ReplacedStroke, AddedStroke);
|
||||
AddedStroke = null;
|
||||
ReplacedStroke = null;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error committing history - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 高级橡皮擦笔画碰撞事件处理
|
||||
/// </summary>
|
||||
private void OnAdvancedEraserStrokeHit(object sender, StrokeHitEventArgs args) {
|
||||
try {
|
||||
var inkCanvas = FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas == null) return;
|
||||
|
||||
var eraseResult = args.GetPointEraseResults();
|
||||
var strokeToReplace = new StrokeCollection { args.HitStroke };
|
||||
|
||||
// 过滤锁定的笔画
|
||||
var filteredToReplace = strokeToReplace.Where(stroke => !stroke.ContainsPropertyData(IsLockGuid));
|
||||
var filteredToReplaceArray = filteredToReplace as Stroke[] ?? filteredToReplace.ToArray();
|
||||
|
||||
if (!filteredToReplaceArray.Any()) return;
|
||||
|
||||
var filteredResult = eraseResult.Where(stroke => !stroke.ContainsPropertyData(IsLockGuid));
|
||||
var filteredResultArray = filteredResult as Stroke[] ?? filteredResult.ToArray();
|
||||
|
||||
// 执行笔画替换或删除
|
||||
if (filteredResultArray.Any()) {
|
||||
inkCanvas.Strokes.Replace(
|
||||
new StrokeCollection(filteredToReplaceArray),
|
||||
new StrokeCollection(filteredResultArray)
|
||||
);
|
||||
} else {
|
||||
inkCanvas.Strokes.Remove(new StrokeCollection(filteredToReplaceArray));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error in stroke hit - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新高级橡皮擦位置
|
||||
/// </summary>
|
||||
private void UpdateAdvancedEraserPosition(object sender, Point position) {
|
||||
// 移除isUsingAdvancedEraser检查,让视觉反馈始终更新
|
||||
// if (!isUsingAdvancedEraser) return;
|
||||
|
||||
// 性能优化:限制更新频率
|
||||
var now = DateTime.Now;
|
||||
if ((now - lastEraserUpdate).TotalMilliseconds < ERASER_UPDATE_INTERVAL) {
|
||||
return;
|
||||
}
|
||||
lastEraserUpdate = now;
|
||||
|
||||
// 更新位置
|
||||
lastEraserPosition = position;
|
||||
|
||||
// 更新视觉反馈(始终执行)
|
||||
UpdateEraserVisualFeedback(position);
|
||||
|
||||
// 只有在实际使用橡皮擦时才处理擦除
|
||||
if (isUsingAdvancedEraser) {
|
||||
// 处理不同的橡皮擦模式
|
||||
if (isUsingStrokeEraser) {
|
||||
ProcessStrokeEraserAtPosition(position);
|
||||
} else {
|
||||
ProcessGeometryEraserAtPosition(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定位置处理笔画橡皮擦
|
||||
/// </summary>
|
||||
private void ProcessStrokeEraserAtPosition(Point position) {
|
||||
try {
|
||||
var inkCanvas = FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas == null) return;
|
||||
|
||||
var hitStrokes = inkCanvas.Strokes.HitTest(position)
|
||||
.Where(stroke => !stroke.ContainsPropertyData(IsLockGuid));
|
||||
var strokesArray = hitStrokes as Stroke[] ?? hitStrokes.ToArray();
|
||||
|
||||
if (strokesArray.Any()) {
|
||||
inkCanvas.Strokes.Remove(new StrokeCollection(strokesArray));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error in stroke eraser - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定位置处理几何橡皮擦
|
||||
/// </summary>
|
||||
private void ProcessGeometryEraserAtPosition(Point position) {
|
||||
try {
|
||||
if (advancedHitTester != null) {
|
||||
advancedHitTester.AddPoint(position);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error in geometry eraser - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新橡皮擦视觉反馈
|
||||
/// </summary>
|
||||
private void UpdateEraserVisualFeedback(Point position) {
|
||||
try {
|
||||
// 获取或创建橡皮擦视觉反馈Border
|
||||
if (eraserVisualBorder == null) {
|
||||
eraserVisualBorder = new Border {
|
||||
Background = new SolidColorBrush(Colors.Transparent),
|
||||
BorderBrush = new SolidColorBrush(Colors.Transparent),
|
||||
BorderThickness = new Thickness(0),
|
||||
IsHitTestVisible = false,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Top,
|
||||
Opacity = 1
|
||||
};
|
||||
Panel.SetZIndex(eraserVisualBorder, 1001);
|
||||
|
||||
// 将Border添加到InkCanvasGridForInkReplay中
|
||||
var inkCanvasGrid = FindName("InkCanvasGridForInkReplay") as Grid;
|
||||
if (inkCanvasGrid != null) {
|
||||
inkCanvasGrid.Children.Add(eraserVisualBorder);
|
||||
Trace.WriteLine("Advanced Eraser: Visual feedback border added to grid");
|
||||
} else {
|
||||
Trace.WriteLine("Advanced Eraser: Failed to find InkCanvasGridForInkReplay");
|
||||
return; // 如果找不到Grid,直接返回
|
||||
}
|
||||
}
|
||||
|
||||
if (eraserVisualBorder != null) {
|
||||
// 创建橡皮擦视觉反馈
|
||||
var eraserImage = CreateEraserVisualImage();
|
||||
|
||||
// 清除Border的内容并添加新的图像
|
||||
eraserVisualBorder.Child = eraserImage;
|
||||
|
||||
// 更新橡皮擦位置和大小
|
||||
if (isCurrentEraserCircle) {
|
||||
var radius = currentEraserSize / 2;
|
||||
eraserVisualBorder.Width = currentEraserSize;
|
||||
eraserVisualBorder.Height = currentEraserSize;
|
||||
|
||||
// 使用Margin来定位,因为Border在Grid中
|
||||
eraserVisualBorder.Margin = new Thickness(
|
||||
position.X - radius,
|
||||
position.Y - radius,
|
||||
0, 0);
|
||||
} else {
|
||||
// 矩形橡皮擦,使用与原来相同的逻辑
|
||||
var height = currentEraserSize / 0.6;
|
||||
eraserVisualBorder.Width = currentEraserSize;
|
||||
eraserVisualBorder.Height = height;
|
||||
|
||||
// 使用Margin来定位,因为Border在Grid中
|
||||
eraserVisualBorder.Margin = new Thickness(
|
||||
position.X - currentEraserSize / 2,
|
||||
position.Y - height / 2,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
eraserVisualBorder.Visibility = Visibility.Visible;
|
||||
Trace.WriteLine($"Advanced Eraser: Visual feedback updated to ({position.X:F1}, {position.Y:F1})");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error updating visual feedback - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建橡皮擦视觉图像
|
||||
/// </summary>
|
||||
private Image CreateEraserVisualImage() {
|
||||
try {
|
||||
// 根据橡皮擦形状选择对应的DrawingGroup资源
|
||||
string resourceKey = isCurrentEraserCircle ? "EraserCircleDrawingGroup" : "EraserDrawingGroup";
|
||||
|
||||
// 尝试从资源字典中获取DrawingGroup
|
||||
var drawingGroup = TryFindResource(resourceKey) as DrawingGroup;
|
||||
if (drawingGroup == null) {
|
||||
// 如果找不到资源,创建默认的橡皮擦图像
|
||||
return CreateDefaultEraserImage();
|
||||
}
|
||||
|
||||
// 创建变换后的DrawingGroup
|
||||
var transformedGroup = new DrawingGroup();
|
||||
transformedGroup.Children.Add(drawingGroup);
|
||||
|
||||
// 应用缩放变换
|
||||
var transform = new ScaleTransform();
|
||||
if (isCurrentEraserCircle) {
|
||||
var scale = currentEraserSize / 56.0; // 基于56x56的基准尺寸
|
||||
transform.ScaleX = scale;
|
||||
transform.ScaleY = scale;
|
||||
} else {
|
||||
var scaleX = currentEraserSize / 38.0;
|
||||
var scaleY = (currentEraserSize / 0.6) / 56.0;
|
||||
transform.ScaleX = scaleX;
|
||||
transform.ScaleY = scaleY;
|
||||
}
|
||||
transformedGroup.Transform = transform;
|
||||
|
||||
// 创建DrawingImage
|
||||
var drawingImage = new DrawingImage(transformedGroup);
|
||||
|
||||
// 创建Image控件
|
||||
var image = new Image {
|
||||
Source = drawingImage,
|
||||
Stretch = Stretch.None
|
||||
};
|
||||
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
|
||||
|
||||
return image;
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error creating eraser visual image - {ex.Message}");
|
||||
return CreateDefaultEraserImage();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建默认的橡皮擦图像(当资源不可用时)
|
||||
/// </summary>
|
||||
private Image CreateDefaultEraserImage() {
|
||||
try {
|
||||
// 创建一个简单的几何图形作为默认橡皮擦
|
||||
Geometry geometry;
|
||||
if (isCurrentEraserCircle) {
|
||||
geometry = new EllipseGeometry(new Point(28, 28), 28, 28);
|
||||
} else {
|
||||
geometry = new RectangleGeometry(new Rect(0, 0, 38, 56));
|
||||
}
|
||||
|
||||
var brush = new SolidColorBrush(Colors.LightGray);
|
||||
var pen = new Pen(new SolidColorBrush(Colors.DarkGray), 1);
|
||||
|
||||
var geometryDrawing = new GeometryDrawing(brush, pen, geometry);
|
||||
var drawingGroup = new DrawingGroup();
|
||||
drawingGroup.Children.Add(geometryDrawing);
|
||||
|
||||
// 应用缩放变换
|
||||
var transform = new ScaleTransform();
|
||||
if (isCurrentEraserCircle) {
|
||||
var scale = currentEraserSize / 56.0;
|
||||
transform.ScaleX = scale;
|
||||
transform.ScaleY = scale;
|
||||
} else {
|
||||
var scaleX = currentEraserSize / 38.0;
|
||||
var scaleY = (currentEraserSize / 0.6) / 56.0;
|
||||
transform.ScaleX = scaleX;
|
||||
transform.ScaleY = scaleY;
|
||||
}
|
||||
drawingGroup.Transform = transform;
|
||||
|
||||
var drawingImage = new DrawingImage(drawingGroup);
|
||||
var image = new Image {
|
||||
Source = drawingImage,
|
||||
Stretch = Stretch.None
|
||||
};
|
||||
|
||||
return image;
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error creating default eraser image - {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 兼容性方法:旧版橡皮擦几何碰撞处理
|
||||
/// </summary>
|
||||
[Obsolete("使用 OnAdvancedEraserStrokeHit 替代")]
|
||||
private void EraserGeometry_StrokeHit(object sender, StrokeHitEventArgs args) {
|
||||
OnAdvancedEraserStrokeHit(sender, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 兼容性方法:旧版橡皮擦移动处理
|
||||
/// </summary>
|
||||
[Obsolete("使用 UpdateAdvancedEraserPosition 替代")]
|
||||
private void EraserOverlay_PointerMove(object sender, Point pt) {
|
||||
UpdateAdvancedEraserPosition(sender, pt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 兼容性方法:旧版橡皮擦按下处理
|
||||
/// </summary>
|
||||
[Obsolete("使用 StartAdvancedEraserOperation 替代")]
|
||||
private void EraserOverlay_PointerDown(object sender) {
|
||||
StartAdvancedEraserOperation(sender);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 兼容性方法:旧版橡皮擦抬起处理
|
||||
/// </summary>
|
||||
[Obsolete("使用 EndAdvancedEraserOperation 替代")]
|
||||
private void EraserOverlay_PointerUp(object sender) {
|
||||
EndAdvancedEraserOperation(sender);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前橡皮擦状态信息(用于调试)
|
||||
/// </summary>
|
||||
public string GetEraserStatusInfo() {
|
||||
return "Advanced Eraser Status:\n" +
|
||||
$"- Active: {isUsingAdvancedEraser}\n" +
|
||||
$"- Size: {currentEraserSize:F1}\n" +
|
||||
$"- Shape: {(isCurrentEraserCircle ? "Circle" : "Rectangle")}\n" +
|
||||
$"- Mode: {(isUsingStrokeEraser ? "Stroke" : "Geometry")}\n" +
|
||||
$"- Visible: {isEraserVisible}\n" +
|
||||
$"- Last Position: ({lastEraserPosition.X:F1}, {lastEraserPosition.Y:F1})";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置橡皮擦状态
|
||||
/// </summary>
|
||||
public void ResetEraserState() {
|
||||
isUsingAdvancedEraser = false;
|
||||
isEraserVisible = false;
|
||||
lastEraserPosition = new Point();
|
||||
|
||||
if (advancedHitTester != null) {
|
||||
advancedHitTester.EndHitTesting();
|
||||
advancedHitTester = null;
|
||||
}
|
||||
|
||||
HideEraserFeedback();
|
||||
|
||||
// 清理视觉反馈Border
|
||||
if (eraserVisualBorder != null) {
|
||||
var inkCanvasGrid = FindName("InkCanvasGridForInkReplay") as Grid;
|
||||
if (inkCanvasGrid != null) {
|
||||
inkCanvasGrid.Children.Remove(eraserVisualBorder);
|
||||
}
|
||||
eraserVisualBorder = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用高级橡皮擦形状到InkCanvas
|
||||
/// </summary>
|
||||
public void ApplyAdvancedEraserShape() {
|
||||
try {
|
||||
var inkCanvas = FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvas == null) return;
|
||||
|
||||
// 更新橡皮擦尺寸和形状
|
||||
UpdateEraserSize();
|
||||
|
||||
// 创建橡皮擦形状
|
||||
StylusShape eraserShape = CreateEraserShape();
|
||||
|
||||
// 应用到InkCanvas
|
||||
inkCanvas.EraserShape = eraserShape;
|
||||
|
||||
Trace.WriteLine($"Advanced Eraser: Applied shape - Size: {currentEraserSize}, Circle: {isCurrentEraserCircle}");
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error applying shape - {ex.Message}");
|
||||
|
||||
// 回退到传统方法
|
||||
try {
|
||||
ApplyCurrentEraserShape();
|
||||
} catch (Exception fallbackEx) {
|
||||
Trace.WriteLine($"Advanced Eraser: Fallback also failed - {fallbackEx.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用高级橡皮擦系统
|
||||
/// </summary>
|
||||
public void EnableAdvancedEraserSystem() {
|
||||
try {
|
||||
// 获取橡皮擦覆盖层
|
||||
var eraserOverlay = FindName("AdvancedEraserOverlay") as Border;
|
||||
if (eraserOverlay != null) {
|
||||
// 启用覆盖层的交互
|
||||
eraserOverlay.IsHitTestVisible = true;
|
||||
|
||||
// 确保覆盖层在橡皮擦模式下启用
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
eraserOverlay.IsHitTestVisible = true;
|
||||
Trace.WriteLine("Advanced Eraser: Overlay enabled for eraser mode");
|
||||
}
|
||||
|
||||
// 设置覆盖层的大小以覆盖整个InkCanvas
|
||||
var inkCanvasControl = FindName("inkCanvas") as InkCanvas;
|
||||
if (inkCanvasControl != null) {
|
||||
eraserOverlay.Width = inkCanvasControl.ActualWidth;
|
||||
eraserOverlay.Height = inkCanvasControl.ActualHeight;
|
||||
Trace.WriteLine($"Advanced Eraser: Overlay size set to {eraserOverlay.Width}x{eraserOverlay.Height}");
|
||||
}
|
||||
|
||||
Trace.WriteLine("Advanced Eraser: System enabled successfully");
|
||||
} else {
|
||||
Trace.WriteLine("Advanced Eraser: Failed to find eraser overlay");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error enabling system - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化橡皮擦覆盖层
|
||||
/// </summary>
|
||||
private void InitializeEraserOverlay(Border overlay) {
|
||||
try {
|
||||
// 设置覆盖层的基本属性
|
||||
overlay.Background = new SolidColorBrush(Colors.Transparent);
|
||||
overlay.IsHitTestVisible = false; // 默认禁用,只在橡皮擦模式下启用
|
||||
|
||||
// 绑定事件处理
|
||||
overlay.MouseDown += (sender, e) => {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
overlay.CaptureMouse();
|
||||
StartAdvancedEraserOperation(sender);
|
||||
}
|
||||
};
|
||||
|
||||
overlay.MouseUp += (sender, e) => {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
overlay.ReleaseMouseCapture();
|
||||
EndAdvancedEraserOperation(sender);
|
||||
}
|
||||
};
|
||||
|
||||
overlay.MouseMove += (sender, e) => {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
var position = e.GetPosition((UIElement)FindName("inkCanvas"));
|
||||
Trace.WriteLine($"Advanced Eraser: Mouse move event triggered at ({position.X:F1}, {position.Y:F1})");
|
||||
UpdateAdvancedEraserPosition(sender, position);
|
||||
} else {
|
||||
Trace.WriteLine($"Advanced Eraser: Mouse move ignored - not in eraser mode, current mode: {inkCanvas.EditingMode}");
|
||||
}
|
||||
};
|
||||
|
||||
// 触控笔事件
|
||||
overlay.StylusDown += (sender, e) => {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
e.Handled = true;
|
||||
if (e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus) {
|
||||
overlay.CaptureStylus();
|
||||
}
|
||||
StartAdvancedEraserOperation(sender);
|
||||
}
|
||||
};
|
||||
|
||||
overlay.StylusUp += (sender, e) => {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
e.Handled = true;
|
||||
if (e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus) {
|
||||
overlay.ReleaseStylusCapture();
|
||||
}
|
||||
EndAdvancedEraserOperation(sender);
|
||||
}
|
||||
};
|
||||
|
||||
overlay.StylusMove += (sender, e) => {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
e.Handled = true;
|
||||
var position = e.GetPosition((UIElement)FindName("inkCanvas"));
|
||||
UpdateAdvancedEraserPosition(sender, position);
|
||||
Trace.WriteLine($"Advanced Eraser: Stylus move at ({position.X:F1}, {position.Y:F1})");
|
||||
}
|
||||
};
|
||||
|
||||
Trace.WriteLine("Advanced Eraser: Overlay initialized successfully");
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error initializing overlay - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 禁用高级橡皮擦系统
|
||||
/// </summary>
|
||||
public void DisableAdvancedEraserSystem() {
|
||||
try {
|
||||
// 重置橡皮擦状态
|
||||
ResetEraserState();
|
||||
|
||||
// 获取橡皮擦覆盖层并禁用
|
||||
var eraserOverlay = FindName("AdvancedEraserOverlay") as Border;
|
||||
if (eraserOverlay != null) {
|
||||
eraserOverlay.IsHitTestVisible = false;
|
||||
}
|
||||
|
||||
// 确保视觉反馈被隐藏
|
||||
HideEraserFeedback();
|
||||
|
||||
Trace.WriteLine("Advanced Eraser: System disabled successfully");
|
||||
} catch (Exception ex) {
|
||||
Trace.WriteLine($"Advanced Eraser: Error disabling system - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 切换橡皮擦形状(圆形/矩形)
|
||||
/// </summary>
|
||||
public void ToggleEraserShape() {
|
||||
isCurrentEraserCircle = !isCurrentEraserCircle;
|
||||
|
||||
// 更新设置
|
||||
Settings.Canvas.EraserShapeType = isCurrentEraserCircle ? 0 : 1;
|
||||
|
||||
// 应用新形状
|
||||
ApplyAdvancedEraserShape();
|
||||
|
||||
Trace.WriteLine($"Advanced Eraser: Toggled to {(isCurrentEraserCircle ? "Circle" : "Rectangle")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<DrawingGroup x:Key="EraserDrawingGroup" ClipGeometry="M0,0 V56 H38 V0 H0 Z">
|
||||
<GeometryDrawing Brush="#FFF2EEEB" Geometry="F1 M38,56z M0,0z M0,4C0,1.79086,1.79086,0,4,0L34,0C36.2091,0,38,1.79086,38,4L38,52C38,54.2091,36.2091,56,34,56L4,56C1.79086,56,0,54.2091,0,52L0,4z" />
|
||||
<GeometryDrawing Brush="#FFCDCDCD" Geometry="F0 M38,56z M0,0z M34,1L4,1C2.34315,1,1,2.34315,1,4L1,52C1,53.6569,2.34315,55,4,55L34,55C35.6569,55,37,53.6569,37,52L37,4C37,2.34315,35.6569,1,34,1z M4,0C1.79086,0,0,1.79086,0,4L0,52C0,54.2091,1.79086,56,4,56L34,56C36.2091,56,38,54.2091,38,52L38,4C38,1.79086,36.2091,0,34,0L4,0z" />
|
||||
<GeometryDrawing Brush="#FFD1CFCD" Geometry="F1 M38,56z M0,0z M12,19.5C12,18.1193,13.1193,17,14.5,17L14.5,17C15.8807,17,17,18.1193,17,19.5L17,36.5C17,37.8807,15.8807,39,14.5,39L14.5,39C13.1193,39,12,37.8807,12,36.5L12,19.5z" />
|
||||
<GeometryDrawing Geometry="F0 M38,56z M0,0z M11.5,19.5C11.5,17.8431 12.8431,16.5 14.5,16.5 16.1569,16.5 17.5,17.8431 17.5,19.5L17.5,36.5C17.5,38.1569 16.1569,39.5 14.5,39.5 12.8431,39.5 11.5,38.1569 11.5,36.5L11.5,19.5z M14.5,17.5C13.3954,17.5,12.5,18.3954,12.5,19.5L12.5,36.5C12.5,37.6046 13.3954,38.5 14.5,38.5 15.6046,38.5 16.5,37.6046 16.5,36.5L16.5,19.5C16.5,18.3954,15.6046,17.5,14.5,17.5z">
|
||||
<GeometryDrawing.Brush>
|
||||
<SolidColorBrush Color="#FF6F6F6F" Opacity="0.25" />
|
||||
</GeometryDrawing.Brush>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFD1CFCD" Geometry="F1 M38,56z M0,0z M21,19.5C21,18.1193,22.1193,17,23.5,17L23.5,17C24.8807,17,26,18.1193,26,19.5L26,36.5C26,37.8807,24.8807,39,23.5,39L23.5,39C22.1193,39,21,37.8807,21,36.5L21,19.5z" />
|
||||
<GeometryDrawing Geometry="F0 M38,56z M0,0z M20.5,19.5C20.5,17.8431 21.8431,16.5 23.5,16.5 25.1569,16.5 26.5,17.8431 26.5,19.5L26.5,36.5C26.5,38.1569 25.1569,39.5 23.5,39.5 21.8431,39.5 20.5,38.1569 20.5,36.5L20.5,19.5z M23.5,17.5C22.3954,17.5,21.5,18.3954,21.5,19.5L21.5,36.5C21.5,37.6046 22.3954,38.5 23.5,38.5 24.6046,38.5 25.5,37.6046 25.5,36.5L25.5,19.5C25.5,18.3954,24.6046,17.5,23.5,17.5z">
|
||||
<GeometryDrawing.Brush>
|
||||
<SolidColorBrush Color="#FF6F6F6F" Opacity="0.25" />
|
||||
</GeometryDrawing.Brush>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
|
||||
<DrawingGroup x:Key="EraserCircleDrawingGroup" ClipGeometry="M0,0 V56 H56 V0 H0 Z">
|
||||
<GeometryDrawing Brush="#FFF2EEEB" Geometry="F1 M56,56z M0,0z M0,28C0,12.536,12.536,0,28,0L28,0C43.464,0,56,12.536,56,28L56,28C56,43.464,43.464,56,28,56L28,56C12.536,56,0,43.464,0,28L0,28z" />
|
||||
<GeometryDrawing Brush="#FFCDCDCD" Geometry="F0 M56,56z M0,0z M1,28C1,42.9117 13.0883,55 28,55 42.9117,55 55,42.9117 55,28 55,13.0883 42.9117,1 28,1 13.0883,1 1,13.0883 1,28z M28,0C12.536,0 0,12.536 0,28 0,43.464 12.536,56 28,56 43.464,56 56,43.464 56,28 56,12.536 43.464,0 28,0z" />
|
||||
<GeometryDrawing Brush="#FFD1CFCD" Geometry="F1 M56,56z M0,0z M21,19.5C21,18.1193,22.1193,17,23.5,17L23.5,17C24.8807,17,26,18.1193,26,19.5L26,36.5C26,37.8807,24.8807,39,23.5,39L23.5,39C22.1193,39,21,37.8807,21,36.5L21,19.5z" />
|
||||
<GeometryDrawing Geometry="F0 M56,56z M0,0z M20.5,19.5C20.5,17.8431 21.8431,16.5 23.5,16.5 25.1569,16.5 26.5,17.8431 26.5,19.5L26.5,36.5C26.5,38.1569 25.1569,39.5 23.5,39.5 21.8431,39.5 20.5,38.1569 20.5,36.5L20.5,19.5z M23.5,17.5C22.3954,17.5,21.5,18.3954,21.5,19.5L21.5,36.5C21.5,37.6046 22.3954,38.5 23.5,38.5 24.6046,38.5 25.5,37.6046 25.5,36.5L25.5,19.5C25.5,18.3954,24.6046,17.5,23.5,17.5z">
|
||||
<GeometryDrawing.Brush>
|
||||
<SolidColorBrush Color="#FF6F6F6F" Opacity="0.25" />
|
||||
</GeometryDrawing.Brush>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFD1CFCD" Geometry="F1 M56,56z M0,0z M30,19.5C30,18.1193,31.1193,17,32.5,17L32.5,17C33.8807,17,35,18.1193,35,19.5L35,36.5C35,37.8807,33.8807,39,32.5,39L32.5,39C31.1193,39,30,37.8807,30,36.5L30,19.5z" />
|
||||
<GeometryDrawing Geometry="F0 M56,56z M0,0z M29.5,19.5C29.5,17.8431 30.8431,16.5 32.5,16.5 34.1569,16.5 35.5,17.8431 35.5,19.5L35.5,36.5C35.5,38.1569 34.1569,39.5 32.5,39.5 30.8431,39.5 29.5,38.1569 29.5,36.5L29.5,19.5z M32.5,17.5C31.3954,17.5,30.5,18.3954,30.5,19.5L30.5,36.5C30.5,37.6046 31.3954,38.5 32.5,38.5 33.6046,38.5 34.5,37.6046 34.5,36.5L34.5,19.5C34.5,18.3954,33.6046,17.5,32.5,17.5z">
|
||||
<GeometryDrawing.Brush>
|
||||
<SolidColorBrush Color="#FF6F6F6F" Opacity="0.25" />
|
||||
</GeometryDrawing.Brush>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
</ResourceDictionary>
|
||||
@@ -1,28 +1,27 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Threading;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Modern;
|
||||
using System.Threading;
|
||||
using Application = System.Windows.Application;
|
||||
using Button = System.Windows.Controls.Button;
|
||||
using HorizontalAlignment = System.Windows.HorizontalAlignment;
|
||||
using Point = System.Windows.Point;
|
||||
using System.Diagnostics;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using System.IO;
|
||||
using System.Windows.Media.Effects;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Xml.Linq;
|
||||
using Image = System.Windows.Controls.Image;
|
||||
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
|
||||
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
|
||||
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
|
||||
using Panel = System.Windows.Controls.Panel;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
@@ -118,7 +117,7 @@ namespace Ink_Canvas {
|
||||
|| BorderFloatingBarMainControls.Visibility != Visibility.Visible) {
|
||||
EnableTwoFingerGestureBorder.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
else if (isVisible == true) {
|
||||
else if (isVisible) {
|
||||
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible)
|
||||
EnableTwoFingerGestureBorder.Visibility = Visibility.Collapsed;
|
||||
else EnableTwoFingerGestureBorder.Visibility = Visibility.Visible;
|
||||
@@ -132,9 +131,9 @@ namespace Ink_Canvas {
|
||||
|
||||
#region 浮動工具欄的拖動實現
|
||||
|
||||
private bool isDragDropInEffect = false;
|
||||
private Point pos = new Point();
|
||||
private Point downPos = new Point();
|
||||
private bool isDragDropInEffect;
|
||||
private Point pos;
|
||||
private Point downPos;
|
||||
private Point pointDesktop = new Point(-1, -1); //用于记录上次在桌面时的坐标
|
||||
private Point pointPPT = new Point(-1, -1); //用于记录上次在PPT中的坐标
|
||||
|
||||
@@ -280,8 +279,16 @@ namespace Ink_Canvas {
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderLeftPageListView);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderRightPageListView);
|
||||
|
||||
// 隐藏背景设置面板
|
||||
var bgPalette = LogicalTreeHelper.FindLogicalNode(this, "BackgroundPalette") as Border;
|
||||
if (bgPalette != null)
|
||||
{
|
||||
AnimationsHelper.HideWithSlideAndFade(bgPalette);
|
||||
}
|
||||
|
||||
if (BorderSettings.Visibility == Visibility.Visible) {
|
||||
// 设置蒙版为不可点击,并移除背景
|
||||
BorderSettingsMask.IsHitTestVisible = false;
|
||||
BorderSettingsMask.Background = null;
|
||||
var sb = new Storyboard();
|
||||
@@ -292,7 +299,7 @@ namespace Ink_Canvas {
|
||||
To = BorderSettings.RenderTransform.Value.OffsetX - 440,
|
||||
Duration = TimeSpan.FromSeconds(0.6)
|
||||
};
|
||||
slideAnimation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };
|
||||
slideAnimation.EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut };
|
||||
Storyboard.SetTargetProperty(slideAnimation,
|
||||
new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
|
||||
|
||||
@@ -307,7 +314,7 @@ namespace Ink_Canvas {
|
||||
BorderSettings.RenderTransform = new TranslateTransform();
|
||||
|
||||
isOpeningOrHidingSettingsPane = true;
|
||||
sb.Begin((FrameworkElement)BorderSettings);
|
||||
sb.Begin(BorderSettings);
|
||||
}
|
||||
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
@@ -403,6 +410,11 @@ namespace Ink_Canvas {
|
||||
System.Windows.Controls.Canvas.SetLeft(FloatingbarSelectionBG, 28 * 5);
|
||||
break;
|
||||
}
|
||||
case "shape": {
|
||||
// 对图形模式进行特殊处理,不修改按钮UI状态
|
||||
// 只隐藏相关面板,但保持图形绘制模式
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -412,7 +424,7 @@ namespace Ink_Canvas {
|
||||
await Task.Delay(50);
|
||||
ViewboxFloatingBarMarginAnimation(60);
|
||||
}
|
||||
else if (Topmost == true) //非黑板
|
||||
else if (Topmost) //非黑板
|
||||
{
|
||||
await Task.Delay(50);
|
||||
ViewboxFloatingBarMarginAnimation(100, true);
|
||||
@@ -461,7 +473,7 @@ namespace Ink_Canvas {
|
||||
#region 白板按鈕和退出白板模式按鈕
|
||||
|
||||
//private bool Not_Enter_Blackboard_fir_Mouse_Click = true;
|
||||
private bool isDisplayingOrHidingBlackboard = false;
|
||||
private bool isDisplayingOrHidingBlackboard;
|
||||
|
||||
private void ImageBlackboard_MouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
@@ -496,10 +508,12 @@ namespace Ink_Canvas {
|
||||
Not_Enter_Blackboard_fir_Mouse_Click = false;
|
||||
}
|
||||
*/
|
||||
new Thread(new ThreadStart(() => {
|
||||
new Thread(() => {
|
||||
Thread.Sleep(100);
|
||||
Application.Current.Dispatcher.Invoke(() => { ViewboxFloatingBarMarginAnimation(60); });
|
||||
})).Start();
|
||||
}).Start();
|
||||
|
||||
HideSubPanels();
|
||||
|
||||
if (GridTransparencyFakeBackground.Background == Brushes.Transparent)
|
||||
{
|
||||
@@ -513,11 +527,6 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
BtnHideInkCanvas_Click(BtnHideInkCanvas, null);
|
||||
HideSubPanels();
|
||||
}
|
||||
else
|
||||
{
|
||||
HideSubPanels();
|
||||
}
|
||||
|
||||
if (Settings.Gesture.AutoSwitchTwoFingerGesture) // 自动关闭多指书写、开启双指移动
|
||||
@@ -526,7 +535,7 @@ namespace Ink_Canvas {
|
||||
if (isInMultiTouchMode) ToggleSwitchEnableMultiTouchMode.IsOn = false;
|
||||
}
|
||||
|
||||
if (Settings.Appearance.EnableTimeDisplayInWhiteboardMode == true)
|
||||
if (Settings.Appearance.EnableTimeDisplayInWhiteboardMode)
|
||||
{
|
||||
WaterMarkTime.Visibility = Visibility.Visible;
|
||||
WaterMarkDate.Visibility = Visibility.Visible;
|
||||
@@ -535,7 +544,7 @@ namespace Ink_Canvas {
|
||||
WaterMarkDate.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if (Settings.Appearance.EnableChickenSoupInWhiteboardMode == true)
|
||||
if (Settings.Appearance.EnableChickenSoupInWhiteboardMode)
|
||||
{
|
||||
BlackBoardWaterMark.Visibility = Visibility.Visible;
|
||||
} else {
|
||||
@@ -592,15 +601,15 @@ namespace Ink_Canvas {
|
||||
inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber) SaveScreenShot(true);
|
||||
|
||||
if (BtnPPTSlideShowEnd.Visibility == Visibility.Collapsed)
|
||||
new Thread(new ThreadStart(() => {
|
||||
new Thread(() => {
|
||||
Thread.Sleep(300);
|
||||
Application.Current.Dispatcher.Invoke(() => { ViewboxFloatingBarMarginAnimation(100, true); });
|
||||
})).Start();
|
||||
}).Start();
|
||||
else
|
||||
new Thread(new ThreadStart(() => {
|
||||
new Thread(() => {
|
||||
Thread.Sleep(300);
|
||||
Application.Current.Dispatcher.Invoke(() => { ViewboxFloatingBarMarginAnimation(60); });
|
||||
})).Start();
|
||||
}).Start();
|
||||
|
||||
if (System.Windows.Controls.Canvas.GetLeft(FloatingbarSelectionBG)!=28) PenIcon_Click(null, null);
|
||||
|
||||
@@ -623,10 +632,10 @@ namespace Ink_Canvas {
|
||||
BtnExit.Foreground = Brushes.White;
|
||||
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
|
||||
|
||||
new Thread(new ThreadStart(() => {
|
||||
new Thread(() => {
|
||||
Thread.Sleep(200);
|
||||
Application.Current.Dispatcher.Invoke(() => { isDisplayingOrHidingBlackboard = false; });
|
||||
})).Start();
|
||||
}).Start();
|
||||
|
||||
SwitchToDefaultPen(null, null);
|
||||
CheckColorTheme(true);
|
||||
@@ -710,7 +719,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private void SymbolIconSettings_Click(object sender, RoutedEventArgs e) {
|
||||
if (isOpeningOrHidingSettingsPane != false) return;
|
||||
if (isOpeningOrHidingSettingsPane) return;
|
||||
HideSubPanels();
|
||||
BtnSettings_Click(null, null);
|
||||
}
|
||||
@@ -819,7 +828,24 @@ namespace Ink_Canvas {
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
|
||||
new RandWindow(Settings, true).ShowDialog();
|
||||
// 检查是否启用了直接调用ClassIsland点名功能
|
||||
if (Settings.RandSettings.DirectCallCiRand) {
|
||||
try {
|
||||
Process.Start(new ProcessStartInfo {
|
||||
FileName = "classisland://plugins/IslandCaller/Run",
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
MessageBox.Show("无法调用ClassIsland点名:" + ex.Message);
|
||||
|
||||
// 调用失败时回退到默认的随机点名窗口
|
||||
new RandWindow(Settings, true).ShowDialog();
|
||||
}
|
||||
} else {
|
||||
// 使用默认的随机点名窗口
|
||||
new RandWindow(Settings, true).ShowDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private void GridInkReplayButton_MouseUp(object sender, MouseButtonEventArgs e) {
|
||||
@@ -939,9 +965,9 @@ namespace Ink_Canvas {
|
||||
}).Start();
|
||||
}
|
||||
|
||||
private bool isStopInkReplay = false;
|
||||
private bool isPauseInkReplay = false;
|
||||
private bool isRestartInkReplay = false;
|
||||
private bool isStopInkReplay;
|
||||
private bool isPauseInkReplay;
|
||||
private bool isRestartInkReplay;
|
||||
private double inkReplaySpeed = 1;
|
||||
|
||||
private void InkCanvasForInkReplay_MouseDown(object sender, MouseButtonEventArgs e) {
|
||||
@@ -1056,7 +1082,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
private bool isViewboxFloatingBarMarginAnimationRunning = false;
|
||||
private bool isViewboxFloatingBarMarginAnimationRunning;
|
||||
|
||||
public async void ViewboxFloatingBarMarginAnimation(int MarginFromEdge,
|
||||
bool PosXCaculatedWithTaskbarHeight = false) {
|
||||
@@ -1076,7 +1102,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
var windowHandle = new WindowInteropHelper(this).Handle;
|
||||
var screen = System.Windows.Forms.Screen.FromHandle(windowHandle);
|
||||
var screen = Screen.FromHandle(windowHandle);
|
||||
double screenWidth = screen.Bounds.Width / dpiScaleX, screenHeight = screen.Bounds.Height / dpiScaleY;
|
||||
// 仅计算Windows任务栏高度,不考虑其他程序对工作区的影响
|
||||
var toolbarHeight = ForegroundWindowInfo.GetTaskbarHeight(screen, dpiScaleY);
|
||||
@@ -1088,21 +1114,21 @@ namespace Ink_Canvas {
|
||||
if (toolbarHeight == 0)
|
||||
{
|
||||
pos.Y = screenHeight - MarginFromEdge * ViewboxFloatingBarScaleTransform.ScaleY;
|
||||
LogHelper.WriteLogToFile($"任务栏隐藏,使用固定边距: {MarginFromEdge}", LogHelper.LogType.Info);
|
||||
LogHelper.WriteLogToFile($"任务栏隐藏,使用固定边距: {MarginFromEdge}");
|
||||
}
|
||||
else
|
||||
{
|
||||
pos.Y = screenHeight - MarginFromEdge * ViewboxFloatingBarScaleTransform.ScaleY;
|
||||
}
|
||||
}
|
||||
else if (PosXCaculatedWithTaskbarHeight == true)
|
||||
else if (PosXCaculatedWithTaskbarHeight)
|
||||
{
|
||||
// 如果任务栏高度为0(隐藏状态),则使用固定高度
|
||||
if (toolbarHeight == 0)
|
||||
{
|
||||
pos.Y = screenHeight - ViewboxFloatingBar.ActualHeight * ViewboxFloatingBarScaleTransform.ScaleY -
|
||||
3 * ViewboxFloatingBarScaleTransform.ScaleY;
|
||||
LogHelper.WriteLogToFile($"任务栏隐藏,使用固定高度: {ViewboxFloatingBar.ActualHeight}", LogHelper.LogType.Info);
|
||||
LogHelper.WriteLogToFile($"任务栏隐藏,使用固定高度: {ViewboxFloatingBar.ActualHeight}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1162,7 +1188,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
var windowHandle = new WindowInteropHelper(this).Handle;
|
||||
var screen = System.Windows.Forms.Screen.FromHandle(windowHandle);
|
||||
var screen = Screen.FromHandle(windowHandle);
|
||||
double screenWidth = screen.Bounds.Width / dpiScaleX, screenHeight = screen.Bounds.Height / dpiScaleY;
|
||||
// 仅计算Windows任务栏高度,不考虑其他程序对工作区的影响
|
||||
var toolbarHeight = ForegroundWindowInfo.GetTaskbarHeight(screen, dpiScaleY);
|
||||
@@ -1173,7 +1199,7 @@ namespace Ink_Canvas {
|
||||
{
|
||||
pos.Y = screenHeight - ViewboxFloatingBar.ActualHeight * ViewboxFloatingBarScaleTransform.ScaleY -
|
||||
3 * ViewboxFloatingBarScaleTransform.ScaleY;
|
||||
LogHelper.WriteLogToFile($"任务栏隐藏,使用固定高度: {ViewboxFloatingBar.ActualHeight}", LogHelper.LogType.Info);
|
||||
LogHelper.WriteLogToFile($"任务栏隐藏,使用固定高度: {ViewboxFloatingBar.ActualHeight}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1215,7 +1241,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
var windowHandle = new WindowInteropHelper(this).Handle;
|
||||
var screen = System.Windows.Forms.Screen.FromHandle(windowHandle);
|
||||
var screen = Screen.FromHandle(windowHandle);
|
||||
double screenWidth = screen.Bounds.Width / dpiScaleX, screenHeight = screen.Bounds.Height / dpiScaleY;
|
||||
// 仅计算Windows任务栏高度,不考虑其他程序对工作区的影响
|
||||
var toolbarHeight = ForegroundWindowInfo.GetTaskbarHeight(screen, dpiScaleY);
|
||||
@@ -1249,6 +1275,10 @@ namespace Ink_Canvas {
|
||||
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
|
||||
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
|
||||
if (sender==Cursor_Icon && lastBorderMouseDownObject != Cursor_Icon) return;
|
||||
|
||||
// 禁用高级橡皮擦系统
|
||||
DisableAdvancedEraserSystem();
|
||||
|
||||
// 隱藏高亮
|
||||
FloatingbarSelectionBG.Visibility = Visibility.Hidden;
|
||||
System.Windows.Controls.Canvas.SetLeft(FloatingbarSelectionBG, 0);
|
||||
@@ -1295,7 +1325,14 @@ namespace Ink_Canvas {
|
||||
|
||||
if (currentMode != 0) {
|
||||
SaveStrokes();
|
||||
RestoreStrokes(true);
|
||||
// 检查是否在PPT放映模式,如果不在则不恢复可能包含PPT墨迹的备份
|
||||
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) {
|
||||
RestoreStrokes(true);
|
||||
} else {
|
||||
// 不在PPT模式时,清空备份以避免显示已结束PPT的墨迹
|
||||
TimeMachineHistories[0] = null;
|
||||
timeMachine.ClearStrokeHistory();
|
||||
}
|
||||
}
|
||||
|
||||
if (BtnSwitchTheme.Content.ToString() == "浅色")
|
||||
@@ -1327,6 +1364,9 @@ namespace Ink_Canvas {
|
||||
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
|
||||
if (sender == Pen_Icon && lastBorderMouseDownObject != Pen_Icon) return;
|
||||
|
||||
// 禁用高级橡皮擦系统
|
||||
DisableAdvancedEraserSystem();
|
||||
|
||||
FloatingbarSelectionBG.Visibility = Visibility.Visible;
|
||||
System.Windows.Controls.Canvas.SetLeft(FloatingbarSelectionBG, 28);
|
||||
|
||||
@@ -1398,6 +1438,10 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 切换到批注模式时,确保保存当前图片信息
|
||||
if (currentMode != 0) {
|
||||
SaveStrokes();
|
||||
}
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
ColorSwitchCheck();
|
||||
HideSubPanels("pen", true);
|
||||
@@ -1412,94 +1456,75 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private void EraserIcon_Click(object sender, RoutedEventArgs e) {
|
||||
|
||||
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
|
||||
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
|
||||
if (sender == Eraser_Icon && lastBorderMouseDownObject != Eraser_Icon) return;
|
||||
|
||||
FloatingbarSelectionBG.Visibility = Visibility.Visible;
|
||||
System.Windows.Controls.Canvas.SetLeft(FloatingbarSelectionBG, 84);
|
||||
|
||||
forceEraser = true;
|
||||
EnterMultiTouchModeIfNeeded();
|
||||
bool isAlreadyEraser = inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint;
|
||||
forceEraser = false;
|
||||
forcePointEraser = true;
|
||||
if (Settings.Canvas.EraserShapeType == 0) {
|
||||
double k = 1;
|
||||
switch (Settings.Canvas.EraserSize) {
|
||||
case 0:
|
||||
k = 0.5;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.8;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.25;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.8;
|
||||
break;
|
||||
}
|
||||
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(k * 90, k * 90);
|
||||
}
|
||||
else if (Settings.Canvas.EraserShapeType == 1) {
|
||||
double k = 1;
|
||||
switch (Settings.Canvas.EraserSize) {
|
||||
case 0:
|
||||
k = 0.7;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.9;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.2;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.6;
|
||||
break;
|
||||
}
|
||||
|
||||
inkCanvas.EraserShape = new RectangleStylusShape(k * 90 * 0.6, k * 90);
|
||||
}
|
||||
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
if (EraserSizePanel.Visibility == Visibility.Collapsed) {
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(EraserSizePanel);
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardEraserSizePanel);
|
||||
} else {
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(PenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardPenPalette);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderDrawShape);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
|
||||
}
|
||||
}
|
||||
else {
|
||||
HideSubPanels("eraser");
|
||||
}
|
||||
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
drawingShapeMode = 0;
|
||||
|
||||
inkCanvas_EditingModeChanged(inkCanvas, null);
|
||||
CancelSingleFingerDragMode();
|
||||
// 切换到橡皮擦模式时,确保保存当前图片信息
|
||||
if (!isAlreadyEraser && currentMode != 0) {
|
||||
SaveStrokes();
|
||||
}
|
||||
|
||||
// 启用新的高级橡皮擦系统
|
||||
EnableAdvancedEraserSystem();
|
||||
|
||||
// 使用新的高级橡皮擦系统
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
ApplyAdvancedEraserShape(); // 使用新的橡皮擦形状应用方法
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
HideSubPanels("eraser"); // 高亮橡皮按钮
|
||||
|
||||
// 显示橡皮擦视觉反馈(用于测试)
|
||||
// 注意:eraserVisualBorder在MW_Eraser.cs中定义,这里无法直接访问
|
||||
Trace.WriteLine($"Advanced Eraser: Eraser button clicked, current size: {currentEraserSize}, circle: {isCurrentEraserCircle}");
|
||||
|
||||
if (isAlreadyEraser) {
|
||||
// 已是橡皮状态,再次点击才弹出/收起面板
|
||||
if (EraserSizePanel.Visibility == Visibility.Collapsed) {
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(EraserSizePanel);
|
||||
if (BoardEraserSizePanel != null)
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardEraserSizePanel);
|
||||
} else {
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
if (BoardEraserSizePanel != null)
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BoardEraserIcon_Click(object sender, RoutedEventArgs e) {
|
||||
EnterMultiTouchModeIfNeeded();
|
||||
bool isAlreadyEraser = inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint;
|
||||
forceEraser = false;
|
||||
forcePointEraser = true;
|
||||
drawingShapeMode = 0;
|
||||
|
||||
// 启用新的高级橡皮擦系统
|
||||
EnableAdvancedEraserSystem();
|
||||
|
||||
// 使用新的高级橡皮擦系统
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
ApplyAdvancedEraserShape(); // 使用新的橡皮擦形状应用方法
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
HideSubPanels("eraser"); // 高亮橡皮按钮
|
||||
|
||||
if (isAlreadyEraser) {
|
||||
// 已是橡皮状态,再次点击才弹出/收起面板
|
||||
if (BoardEraserSizePanel != null && BoardEraserSizePanel.Visibility == Visibility.Collapsed) {
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(EraserSizePanel);
|
||||
} else {
|
||||
if (BoardEraserSizePanel != null)
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
|
||||
AnimationsHelper.HideWithSlideAndFade(EraserSizePanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EraserIconByStrokes_Click(object sender, RoutedEventArgs e) {
|
||||
EnterMultiTouchModeIfNeeded();
|
||||
|
||||
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
|
||||
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
|
||||
@@ -1532,6 +1557,9 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private void SelectIcon_MouseUp(object sender, RoutedEvent e) {
|
||||
// 禁用高级橡皮擦系统
|
||||
DisableAdvancedEraserSystem();
|
||||
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 0;
|
||||
inkCanvas.IsManipulationEnabled = false;
|
||||
@@ -1548,8 +1576,9 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private void DrawShapePromptToPen() {
|
||||
if (isLongPressSelected == true) {
|
||||
HideSubPanels("pen");
|
||||
if (isLongPressSelected) {
|
||||
// 如果是长按选中的状态,只隐藏面板,不切换到笔模式
|
||||
HideSubPanels("shape");
|
||||
}
|
||||
else {
|
||||
if (StackPanelCanvasControls.Visibility == Visibility.Visible)
|
||||
@@ -1611,7 +1640,7 @@ namespace Ink_Canvas {
|
||||
|
||||
#region Right Side Panel
|
||||
|
||||
public static bool CloseIsFromButton = false;
|
||||
public static bool CloseIsFromButton;
|
||||
|
||||
public void BtnExit_Click(object sender, RoutedEventArgs e) {
|
||||
App.IsAppExitByUser = true;
|
||||
@@ -1621,24 +1650,45 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
public void BtnRestart_Click(object sender, RoutedEventArgs e) {
|
||||
Process.Start(System.Windows.Forms.Application.ExecutablePath, "-m");
|
||||
App.IsAppExitByUser = true;
|
||||
CloseIsFromButton = true;
|
||||
Application.Current.Shutdown();
|
||||
try {
|
||||
Process.Start(System.Windows.Forms.Application.ExecutablePath, "-m");
|
||||
App.IsAppExitByUser = true;
|
||||
CloseIsFromButton = true;
|
||||
Application.Current.Shutdown();
|
||||
} catch (Exception ex) {
|
||||
LogHelper.NewLog($"重启程序时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsOverlayClick(object sender, MouseButtonEventArgs e) {
|
||||
if (isOpeningOrHidingSettingsPane == true) return;
|
||||
BtnSettings_Click(null, null);
|
||||
if (isOpeningOrHidingSettingsPane) return;
|
||||
|
||||
// 获取点击的位置
|
||||
Point clickPoint = e.GetPosition(BorderSettingsMask);
|
||||
|
||||
// 获取BorderSettings的位置和大小
|
||||
Point settingsPosition = BorderSettings.TranslatePoint(new Point(0, 0), BorderSettingsMask);
|
||||
Rect settingsRect = new Rect(
|
||||
settingsPosition.X,
|
||||
settingsPosition.Y,
|
||||
BorderSettings.ActualWidth,
|
||||
BorderSettings.ActualHeight
|
||||
);
|
||||
|
||||
// 如果点击位置不在设置界面内部,才关闭设置界面
|
||||
if (!settingsRect.Contains(clickPoint)) {
|
||||
BtnSettings_Click(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private bool isOpeningOrHidingSettingsPane = false;
|
||||
private bool isOpeningOrHidingSettingsPane;
|
||||
|
||||
private void BtnSettings_Click(object sender, RoutedEventArgs e) {
|
||||
if (BorderSettings.Visibility == Visibility.Visible) {
|
||||
HideSubPanels();
|
||||
}
|
||||
else {
|
||||
// 设置蒙版为可点击,并添加半透明背景
|
||||
BorderSettingsMask.IsHitTestVisible = true;
|
||||
BorderSettingsMask.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
|
||||
SettingsPanelScrollViewer.ScrollToTop();
|
||||
@@ -1650,7 +1700,7 @@ namespace Ink_Canvas {
|
||||
To = 0,
|
||||
Duration = TimeSpan.FromSeconds(0.6)
|
||||
};
|
||||
slideAnimation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };
|
||||
slideAnimation.EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut };
|
||||
Storyboard.SetTargetProperty(slideAnimation,
|
||||
new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
|
||||
|
||||
@@ -1662,13 +1712,13 @@ namespace Ink_Canvas {
|
||||
BorderSettings.RenderTransform = new TranslateTransform();
|
||||
|
||||
isOpeningOrHidingSettingsPane = true;
|
||||
sb.Begin((FrameworkElement)BorderSettings);
|
||||
sb.Begin(BorderSettings);
|
||||
}
|
||||
}
|
||||
|
||||
private void BtnThickness_Click(object sender, RoutedEventArgs e) { }
|
||||
|
||||
private bool forceEraser = false;
|
||||
private bool forceEraser;
|
||||
|
||||
|
||||
private void BtnClear_Click(object sender, RoutedEventArgs e) {
|
||||
@@ -1691,14 +1741,18 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
ClearStrokes(false);
|
||||
// 保存非笔画元素(如图片)
|
||||
var preservedElements = PreserveNonStrokeElements();
|
||||
inkCanvas.Children.Clear();
|
||||
// 恢复非笔画元素
|
||||
RestoreNonStrokeElements(preservedElements);
|
||||
|
||||
CancelSingleFingerDragMode();
|
||||
|
||||
if (Settings.Canvas.ClearCanvasAndClearTimeMachine) timeMachine.ClearStrokeHistory();
|
||||
}
|
||||
|
||||
private bool lastIsInMultiTouchMode = false;
|
||||
private bool lastIsInMultiTouchMode;
|
||||
|
||||
private void CancelSingleFingerDragMode() {
|
||||
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
|
||||
@@ -1716,7 +1770,7 @@ namespace Ink_Canvas {
|
||||
StackPanelControl.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private int currentMode = 0;
|
||||
private int currentMode;
|
||||
|
||||
private void BtnSwitch_Click(object sender, RoutedEventArgs e) {
|
||||
if (GridTransparencyFakeBackground.Background == Brushes.Transparent) {
|
||||
@@ -1727,9 +1781,25 @@ namespace Ink_Canvas {
|
||||
AnimationsHelper.HideWithSlideAndFade(BlackboardCenterSide);
|
||||
AnimationsHelper.HideWithSlideAndFade(BlackboardRightSide);
|
||||
|
||||
// 取消任何UI元素的选择
|
||||
DeselectUIElement();
|
||||
|
||||
SaveStrokes(true);
|
||||
ClearStrokes(true);
|
||||
RestoreStrokes();
|
||||
|
||||
// 检查是否在PPT放映模式,如果不在则不恢复可能包含PPT墨迹的备份
|
||||
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) {
|
||||
LogHelper.WriteLogToFile("退出白板:当前在PPT放映模式,恢复备份墨迹", LogHelper.LogType.Trace);
|
||||
RestoreStrokes();
|
||||
} else {
|
||||
// 不在PPT模式时,清空备份以避免显示已结束PPT的墨迹
|
||||
LogHelper.WriteLogToFile("退出白板:当前不在PPT放映模式,清空备份以避免显示已结束PPT的墨迹", LogHelper.LogType.Trace);
|
||||
TimeMachineHistories[0] = null;
|
||||
timeMachine.ClearStrokeHistory();
|
||||
}
|
||||
|
||||
// 退出白板时清空图片
|
||||
inkCanvas.Children.Clear();
|
||||
|
||||
if (BtnSwitchTheme.Content.ToString() == "浅色") {
|
||||
BtnSwitch.Content = "黑板";
|
||||
@@ -1739,12 +1809,10 @@ namespace Ink_Canvas {
|
||||
BtnSwitch.Content = "白板";
|
||||
if (isPresentationHaveBlackSpace) {
|
||||
BtnExit.Foreground = Brushes.White;
|
||||
//SymbolIconBtnColorBlackContent.Foreground = Brushes.White;
|
||||
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
|
||||
}
|
||||
else {
|
||||
BtnExit.Foreground = Brushes.Black;
|
||||
//SymbolIconBtnColorBlackContent.Foreground = Brushes.White;
|
||||
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light;
|
||||
}
|
||||
}
|
||||
@@ -1764,26 +1832,37 @@ namespace Ink_Canvas {
|
||||
AnimationsHelper.HideWithSlideAndFade(BlackboardCenterSide);
|
||||
AnimationsHelper.HideWithSlideAndFade(BlackboardRightSide);
|
||||
|
||||
// 取消任何UI元素的选择
|
||||
DeselectUIElement();
|
||||
|
||||
SaveStrokes();
|
||||
ClearStrokes(true);
|
||||
RestoreStrokes(true);
|
||||
|
||||
// 检查是否在PPT放映模式,如果不在则不恢复可能包含PPT墨迹的备份
|
||||
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) {
|
||||
RestoreStrokes(true);
|
||||
} else {
|
||||
// 不在PPT模式时,清空备份以避免显示已结束PPT的墨迹
|
||||
TimeMachineHistories[0] = null;
|
||||
timeMachine.ClearStrokeHistory();
|
||||
}
|
||||
|
||||
// 退出白板时清空图片
|
||||
inkCanvas.Children.Clear();
|
||||
|
||||
if (BtnSwitchTheme.Content.ToString() == "浅色") {
|
||||
BtnSwitch.Content = "黑板";
|
||||
BtnExit.Foreground = Brushes.White;
|
||||
//SymbolIconBtnColorBlackContent.Foreground = Brushes.Black;
|
||||
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
|
||||
}
|
||||
else {
|
||||
BtnSwitch.Content = "白板";
|
||||
if (isPresentationHaveBlackSpace) {
|
||||
BtnExit.Foreground = Brushes.White;
|
||||
//SymbolIconBtnColorBlackContent.Foreground = Brushes.White;
|
||||
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
|
||||
}
|
||||
else {
|
||||
BtnExit.Foreground = Brushes.Black;
|
||||
//SymbolIconBtnColorBlackContent.Foreground = Brushes.White;
|
||||
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light;
|
||||
}
|
||||
}
|
||||
@@ -1798,29 +1877,49 @@ namespace Ink_Canvas {
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BlackboardCenterSide);
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(BlackboardRightSide);
|
||||
|
||||
// 取消任何UI元素的选择
|
||||
DeselectUIElement();
|
||||
|
||||
SaveStrokes(true);
|
||||
ClearStrokes(true);
|
||||
RestoreStrokes();
|
||||
|
||||
// 检查是否在PPT放映模式,如果不在则不恢复可能包含PPT墨迹的备份
|
||||
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) {
|
||||
RestoreStrokes();
|
||||
} else {
|
||||
// 不在PPT模式时,清空备份以避免显示已结束PPT的墨迹
|
||||
TimeMachineHistories[0] = null;
|
||||
timeMachine.ClearStrokeHistory();
|
||||
}
|
||||
|
||||
BtnSwitch.Content = "屏幕";
|
||||
if (BtnSwitchTheme.Content.ToString() == "浅色") {
|
||||
BtnExit.Foreground = Brushes.White;
|
||||
//SymbolIconBtnColorBlackContent.Foreground = Brushes.Black;
|
||||
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
|
||||
}
|
||||
else {
|
||||
BtnExit.Foreground = Brushes.Black;
|
||||
//SymbolIconBtnColorBlackContent.Foreground = Brushes.White;
|
||||
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light;
|
||||
}
|
||||
|
||||
if (Settings.Canvas.UsingWhiteboard)
|
||||
{
|
||||
BtnColorBlack_Click(null, null);
|
||||
// 如果有自定义背景色并且是白板模式,应用自定义背景色
|
||||
if (CustomBackgroundColor.HasValue)
|
||||
{
|
||||
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
|
||||
}
|
||||
// 白板模式下设置墨迹颜色为黑色
|
||||
CheckLastColor(0);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
else
|
||||
{
|
||||
BtnColorWhite_Click(null, null);
|
||||
// 黑板模式下设置墨迹颜色为白色
|
||||
CheckLastColor(5);
|
||||
forceEraser = false;
|
||||
ColorSwitchCheck();
|
||||
}
|
||||
|
||||
StackPanelPPTButtons.Visibility = Visibility.Collapsed;
|
||||
@@ -1944,7 +2043,39 @@ namespace Ink_Canvas {
|
||||
|
||||
#endregion
|
||||
|
||||
private async void InsertImage_MouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
|
||||
};
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
string filePath = dialog.FileName;
|
||||
Image image = await CreateAndCompressImageAsync(filePath); // 补充image定义
|
||||
if (image != null)
|
||||
{
|
||||
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
|
||||
image.Name = timestamp;
|
||||
|
||||
// 新缩放逻辑:最大宽高为画布一半,并居中
|
||||
double maxWidth = inkCanvas.ActualWidth / 2;
|
||||
double maxHeight = inkCanvas.ActualHeight / 2;
|
||||
double scaleX = maxWidth / image.Width;
|
||||
double scaleY = maxHeight / image.Height;
|
||||
double scale = Math.Min(1, Math.Min(scaleX, scaleY));
|
||||
image.Width = image.Width * scale;
|
||||
image.Height = image.Height * scale;
|
||||
InkCanvas.SetLeft(image, (inkCanvas.ActualWidth - image.Width) / 2);
|
||||
InkCanvas.SetTop(image, (inkCanvas.ActualHeight - image.Height) / 2);
|
||||
|
||||
inkCanvas.Children.Add(image);
|
||||
|
||||
timeMachine.CommitElementInsertHistory(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
namespace Ink_Canvas {
|
||||
public static class XamlGraphicsIconGeometries {
|
||||
public static string LinedCursorIcon =
|
||||
"F1 M24,24z M0,0z M5.72106,15.9716L3.71327,3.00395C3.6389,2.6693 3.65747,2.41831 3.76902,2.25099 3.88057,2.08366 4.0479,2 4.271,2 4.4941,2 4.71711,2.07437 4.94021,2.2231 6.72502,3.39438 9.28149,5.10481 12.6094,7.3544 15.677,9.45526 18.1125,11.1285 19.9159,12.3742 20.1204,12.5229 20.2505,12.6995 20.3062,12.904 20.362,13.1085 20.3249,13.2944 20.1947,13.4618 20.0832,13.6105 19.8973,13.6849 19.637,13.6849L13.3902,13.6849 17.6291,19.7365C17.722,19.8666 17.75,20.0153 17.7128,20.1827 17.6942,20.3314 17.6198,20.4522 17.4897,20.5452L15.5654,21.8838C15.4353,21.9768 15.2865,22.0139 15.1192,21.9953 14.9704,21.9582 14.8496,21.8745 14.7566,21.7444L10.2389,15.2745 7.58956,19.9038C7.45942,20.1269 7.30144,20.2756 7.11552,20.35 6.92961,20.4058 6.75292,20.3872 6.5856,20.2942 6.43686,20.2013 6.34392,20.0339 6.30673,19.7922L6.00007,17.8959C5.88852,17.0779,5.79543,16.4364,5.72106,15.9716z";
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using Ink_Canvas.Helpers;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
private int lastNotificationShowTime = 0;
|
||||
private int lastNotificationShowTime;
|
||||
private int notificationShowTime = 2500;
|
||||
|
||||
public static void ShowNewMessage(string notice, bool isShowImmediately = true) {
|
||||
@@ -21,13 +21,13 @@ namespace Ink_Canvas {
|
||||
TextBlockNotice.Text = notice;
|
||||
AnimationsHelper.ShowWithSlideFromBottomAndFade(GridNotifications);
|
||||
|
||||
new Thread(new ThreadStart(() => {
|
||||
new Thread(() => {
|
||||
Thread.Sleep(notificationShowTime + 300);
|
||||
if (Environment.TickCount - lastNotificationShowTime >= notificationShowTime)
|
||||
Application.Current.Dispatcher.Invoke(() => {
|
||||
AnimationsHelper.HideWithSlideAndFade(GridNotifications);
|
||||
});
|
||||
})).Start();
|
||||
}).Start();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using Ink_Canvas.Helpers;
|
||||
|
||||
namespace Ink_Canvas
|
||||
@@ -36,7 +28,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
var st = ApplyHistoriesToNewStrokeCollection(TimeMachineHistories[index]);
|
||||
st.Clip(new Rect(0, 0, (int)inkCanvas.ActualWidth, (int)inkCanvas.ActualHeight));
|
||||
var pitem = new PageListViewItem()
|
||||
var pitem = new PageListViewItem
|
||||
{
|
||||
Index = index,
|
||||
Strokes = st,
|
||||
@@ -48,7 +40,7 @@ namespace Ink_Canvas
|
||||
foreach (int index in Enumerable.Range(1, WhiteboardTotalCount)) {
|
||||
var st = ApplyHistoriesToNewStrokeCollection(TimeMachineHistories[index]);
|
||||
st.Clip(new Rect(0,0, (int)inkCanvas.ActualWidth, (int)inkCanvas.ActualHeight));
|
||||
var pitem = new PageListViewItem()
|
||||
var pitem = new PageListViewItem
|
||||
{
|
||||
Index = index,
|
||||
Strokes = st,
|
||||
@@ -59,7 +51,7 @@ namespace Ink_Canvas
|
||||
|
||||
var _st = inkCanvas.Strokes.Clone();
|
||||
_st.Clip(new Rect(0, 0, (int)inkCanvas.ActualWidth, (int)inkCanvas.ActualHeight));
|
||||
var _pitem = new PageListViewItem()
|
||||
var _pitem = new PageListViewItem
|
||||
{
|
||||
Index = CurrentWhiteboardIndex,
|
||||
Strokes = _st,
|
||||
@@ -86,11 +78,19 @@ namespace Ink_Canvas
|
||||
var index = BlackBoardLeftSidePageListView.SelectedIndex;
|
||||
if (item != null)
|
||||
{
|
||||
SaveStrokes();
|
||||
ClearStrokes(true);
|
||||
CurrentWhiteboardIndex= index+1;
|
||||
RestoreStrokes();
|
||||
UpdateIndexInfoDisplay();
|
||||
// 只有当选择的页面与当前页面不同时才进行切换
|
||||
if (index + 1 != CurrentWhiteboardIndex)
|
||||
{
|
||||
// 取消任何UI元素的选择(只在真正切换页面时)
|
||||
DeselectUIElement();
|
||||
|
||||
SaveStrokes();
|
||||
ClearStrokes(true);
|
||||
CurrentWhiteboardIndex = index + 1;
|
||||
RestoreStrokes();
|
||||
UpdateIndexInfoDisplay();
|
||||
}
|
||||
// 无论是否切换页面,都更新选择索引
|
||||
BlackBoardLeftSidePageListView.SelectedIndex = index;
|
||||
}
|
||||
}
|
||||
@@ -103,11 +103,19 @@ namespace Ink_Canvas
|
||||
var index = BlackBoardRightSidePageListView.SelectedIndex;
|
||||
if (item != null)
|
||||
{
|
||||
SaveStrokes();
|
||||
ClearStrokes(true);
|
||||
CurrentWhiteboardIndex = index + 1;
|
||||
RestoreStrokes();
|
||||
UpdateIndexInfoDisplay();
|
||||
// 只有当选择的页面与当前页面不同时才进行切换
|
||||
if (index + 1 != CurrentWhiteboardIndex)
|
||||
{
|
||||
// 取消任何UI元素的选择(只在真正切换页面时)
|
||||
DeselectUIElement();
|
||||
|
||||
SaveStrokes();
|
||||
ClearStrokes(true);
|
||||
CurrentWhiteboardIndex = index + 1;
|
||||
RestoreStrokes();
|
||||
UpdateIndexInfoDisplay();
|
||||
}
|
||||
// 无论是否切换页面,都更新选择索引
|
||||
BlackBoardRightSidePageListView.SelectedIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,29 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using File = System.IO.File;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Controls;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
// 1. 定义元素信息结构
|
||||
public class CanvasElementInfo
|
||||
{
|
||||
public string Type { get; set; } // "Image"
|
||||
public string SourcePath { get; set; }
|
||||
public double Left { get; set; }
|
||||
public double Top { get; set; }
|
||||
public double Width { get; set; }
|
||||
public double Height { get; set; }
|
||||
public string Stretch { get; set; } = "Fill"; // 默认为Fill
|
||||
}
|
||||
public partial class MainWindow : Window {
|
||||
private void SymbolIconSaveStrokes_MouseUp(object sender, MouseButtonEventArgs e) {
|
||||
if (lastBorderMouseDownObject != sender || inkCanvas.Visibility != Visibility.Visible) return;
|
||||
@@ -20,57 +36,108 @@ namespace Ink_Canvas {
|
||||
SaveInkCanvasStrokes(true, true);
|
||||
}
|
||||
|
||||
private void SaveInkCanvasStrokes(Boolean newNotice, Boolean saveByUser) {
|
||||
private void SaveInkCanvasStrokes(bool newNotice = true, bool saveByUser = false) {
|
||||
try {
|
||||
// 修改保存路径为软件根目录下的Saves文件夹
|
||||
string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
if (string.IsNullOrEmpty(appDirectory))
|
||||
{
|
||||
appDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
||||
}
|
||||
|
||||
string savePath = Path.Combine(appDirectory, "Saves",
|
||||
(saveByUser ? @"User Saved - " : @"Auto Saved - ") +
|
||||
(currentMode == 0 ? "Annotation Strokes" : "BlackBoard Strokes"));
|
||||
|
||||
var savePath = Settings.Automation.AutoSavedStrokesLocation
|
||||
+ (saveByUser ? @"\User Saved - " : @"\Auto Saved - ")
|
||||
+ (currentMode == 0 ? "Annotation Strokes" : "BlackBoard Strokes");
|
||||
if (!Directory.Exists(savePath)) Directory.CreateDirectory(savePath);
|
||||
string savePathWithName;
|
||||
if (currentMode != 0) // 黑板模式下
|
||||
savePathWithName = savePath + @"\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss-fff") + " Page-" +
|
||||
CurrentWhiteboardIndex + " StrokesCount-" + inkCanvas.Strokes.Count + ".icstk";
|
||||
savePathWithName = savePath + @"\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss-fff") + " Page-" +
|
||||
CurrentWhiteboardIndex + " StrokesCount-" + inkCanvas.Strokes.Count + ".icstk";
|
||||
else
|
||||
//savePathWithName = savePath + @"\" + DateTime.Now.ToString("u").Replace(':', '-') + ".icstk";
|
||||
savePathWithName = savePath + @"\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss-fff") + ".icstk";
|
||||
|
||||
try {
|
||||
using (FileStream fs = new FileStream(savePathWithName, FileMode.Create)) {
|
||||
inkCanvas.Strokes.Save(fs);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is DirectoryNotFoundException) {
|
||||
// 修改异常处理中的备用路径为软件根目录下的Saves文件夹
|
||||
string fallbackPath = Path.Combine(appDirectory, "Saves");
|
||||
Directory.CreateDirectory(fallbackPath);
|
||||
|
||||
if (Settings.Automation.IsSaveFullPageStrokes)
|
||||
{
|
||||
// 全页面保存模式 - 检查是否存在多页面墨迹
|
||||
bool hasMultiplePages = false;
|
||||
List<StrokeCollection> allPageStrokes = new List<StrokeCollection>();
|
||||
|
||||
string fileName = Path.GetFileNameWithoutExtension(savePathWithName) + "_retry.icstk";
|
||||
string newPath = Path.Combine(fallbackPath, fileName);
|
||||
|
||||
try {
|
||||
using (FileStream fs = new FileStream(newPath, FileMode.Create)) {
|
||||
inkCanvas.Strokes.Save(fs);
|
||||
savePathWithName = newPath;
|
||||
// 检查PPT放映模式下的多页面墨迹
|
||||
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible && pptApplication != null)
|
||||
{
|
||||
hasMultiplePages = true;
|
||||
// 收集PPT放映模式下的所有页面墨迹
|
||||
for (int i = 1; i <= pptApplication.SlideShowWindows[1].Presentation.Slides.Count; i++)
|
||||
{
|
||||
if (memoryStreams[i] != null && memoryStreams[i].Length > 8)
|
||||
{
|
||||
memoryStreams[i].Position = 0;
|
||||
allPageStrokes.Add(new StrokeCollection(memoryStreams[i]));
|
||||
}
|
||||
else if (i == previousSlideID && inkCanvas.Strokes.Count > 0)
|
||||
{
|
||||
// 当前页面的墨迹
|
||||
allPageStrokes.Add(inkCanvas.Strokes.Clone());
|
||||
}
|
||||
else
|
||||
{
|
||||
allPageStrokes.Add(new StrokeCollection()); // 空页面
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception fallbackEx) {
|
||||
ShowNotification($"墨迹保存失败: {fallbackEx.Message}");
|
||||
return;
|
||||
// 检查白板模式下的多页面墨迹
|
||||
else if (currentMode != 0 && WhiteboardTotalCount > 1)
|
||||
{
|
||||
hasMultiplePages = true;
|
||||
// 收集白板模式下的所有页面墨迹
|
||||
for (int i = 1; i <= WhiteboardTotalCount; i++)
|
||||
{
|
||||
if (TimeMachineHistories[i] != null)
|
||||
{
|
||||
// 从历史记录中恢复墨迹
|
||||
var strokes = ApplyHistoriesToNewStrokeCollection(TimeMachineHistories[i]);
|
||||
allPageStrokes.Add(strokes);
|
||||
}
|
||||
else
|
||||
{
|
||||
allPageStrokes.Add(new StrokeCollection()); // 空页面
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMultiplePages && allPageStrokes.Count > 0)
|
||||
{
|
||||
// 多页面墨迹保存为压缩包
|
||||
string zipFileName = Path.ChangeExtension(savePathWithName, "zip");
|
||||
SaveMultiPageStrokesAsZip(allPageStrokes, zipFileName, newNotice);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 单页面墨迹保存为图像
|
||||
SaveSinglePageStrokesAsImage(savePathWithName, newNotice);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ShowNotification($"墨迹保存失败: {ex.Message}");
|
||||
return;
|
||||
else
|
||||
{
|
||||
// 常规保存模式 - 仅保存墨迹对象
|
||||
var fs = new FileStream(savePathWithName, FileMode.Create);
|
||||
inkCanvas.Strokes.Save(fs);
|
||||
fs.Close();
|
||||
// 保存元素信息
|
||||
var elementInfos = new List<CanvasElementInfo>();
|
||||
foreach (var child in inkCanvas.Children)
|
||||
{
|
||||
if (child is Image img && img.Source is BitmapImage bmp)
|
||||
{
|
||||
elementInfos.Add(new CanvasElementInfo
|
||||
{
|
||||
Type = "Image",
|
||||
SourcePath = bmp.UriSource?.LocalPath ?? "",
|
||||
Left = InkCanvas.GetLeft(img),
|
||||
Top = InkCanvas.GetTop(img),
|
||||
Width = img.Width,
|
||||
Height = img.Height,
|
||||
Stretch = img.Stretch.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
File.WriteAllText(Path.ChangeExtension(savePathWithName, ".elements.json"), JsonConvert.SerializeObject(elementInfos, Formatting.Indented));
|
||||
if (newNotice) ShowNotification("墨迹成功保存至 " + savePathWithName);
|
||||
}
|
||||
|
||||
if (newNotice) ShowNotification("墨迹成功保存至 " + savePathWithName);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ShowNotification("墨迹保存失败");
|
||||
@@ -78,20 +145,452 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将多页面墨迹保存为压缩包
|
||||
/// </summary>
|
||||
private void SaveMultiPageStrokesAsZip(List<StrokeCollection> allPageStrokes, string zipFileName, bool newNotice)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建临时目录来存放文件
|
||||
string tempDir = Path.Combine(Path.GetTempPath(), $"InkCanvas_MultiPage_{DateTime.Now:yyyyMMdd_HHmmss}");
|
||||
Directory.CreateDirectory(tempDir);
|
||||
|
||||
try
|
||||
{
|
||||
// 保存所有页面的文件到临时目录
|
||||
for (int i = 0; i < allPageStrokes.Count; i++)
|
||||
{
|
||||
var strokes = allPageStrokes[i];
|
||||
if (strokes.Count > 0)
|
||||
{
|
||||
// 保存墨迹文件
|
||||
string strokeFileName = Path.Combine(tempDir, $"page_{i + 1:D4}.icstk");
|
||||
using (var fs = new FileStream(strokeFileName, FileMode.Create))
|
||||
{
|
||||
strokes.Save(fs);
|
||||
}
|
||||
|
||||
// 保存页面图像
|
||||
string imageFileName = Path.Combine(tempDir, $"page_{i + 1:D4}.png");
|
||||
using (var fs = new FileStream(imageFileName, FileMode.Create))
|
||||
{
|
||||
SavePageAsImage(strokes, fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存元数据信息
|
||||
string metadataFile = Path.Combine(tempDir, "metadata.txt");
|
||||
using (var writer = new StreamWriter(metadataFile, false, System.Text.Encoding.UTF8))
|
||||
{
|
||||
writer.WriteLine($"保存时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||
writer.WriteLine($"总页数: {allPageStrokes.Count}");
|
||||
writer.WriteLine($"模式: {(currentMode == 0 ? "PPT放映" : "白板")}");
|
||||
if (currentMode != 0)
|
||||
{
|
||||
writer.WriteLine($"当前页面: {CurrentWhiteboardIndex}");
|
||||
writer.WriteLine($"总页面数: {WhiteboardTotalCount}");
|
||||
}
|
||||
else if (pptApplication != null)
|
||||
{
|
||||
writer.WriteLine($"PPT名称: {pptApplication.SlideShowWindows[1].Presentation.Name}");
|
||||
writer.WriteLine($"PPT总页数: {pptApplication.SlideShowWindows[1].Presentation.Slides.Count}");
|
||||
writer.WriteLine($"PPT文件路径: {pptApplication.SlideShowWindows[1].Presentation.FullName}");
|
||||
}
|
||||
|
||||
for (int i = 0; i < allPageStrokes.Count; i++)
|
||||
{
|
||||
writer.WriteLine($"页面 {i + 1}: {allPageStrokes[i].Count} 条墨迹");
|
||||
}
|
||||
}
|
||||
|
||||
// 使用.NET Framework内置的压缩功能创建ZIP文件
|
||||
if (File.Exists(zipFileName))
|
||||
File.Delete(zipFileName);
|
||||
|
||||
// 使用System.IO.Compression.FileSystem来创建ZIP
|
||||
System.IO.Compression.ZipFile.CreateFromDirectory(tempDir, zipFileName);
|
||||
|
||||
if (newNotice) ShowNotification($"多页面墨迹成功保存至压缩包 {zipFileName}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 清理临时目录
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(tempDir))
|
||||
Directory.Delete(tempDir, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"清理临时目录失败: {ex.ToString()}", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存多页面墨迹压缩包失败: {ex.ToString()}", LogHelper.LogType.Error);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将单页面墨迹保存为图像
|
||||
/// </summary>
|
||||
private void SaveSinglePageStrokesAsImage(string savePathWithName, bool newNotice)
|
||||
{
|
||||
// 全页面保存模式 - 保存整个墨迹页面的图像
|
||||
var bitmap = new System.Drawing.Bitmap(
|
||||
(int)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
|
||||
(int)System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height);
|
||||
|
||||
using (var g = System.Drawing.Graphics.FromImage(bitmap))
|
||||
{
|
||||
// 创建黑色或透明背景
|
||||
System.Drawing.Color bgColor = Settings.Canvas.UsingWhiteboard
|
||||
? System.Drawing.Color.White
|
||||
: System.Drawing.Color.FromArgb(22, 41, 36); // 黑板背景色
|
||||
g.Clear(bgColor);
|
||||
|
||||
// 将InkCanvas墨迹渲染到Visual
|
||||
var visual = new DrawingVisual();
|
||||
using (var dc = visual.RenderOpen())
|
||||
{
|
||||
// 创建一个VisualBrush,使用inkCanvas作为源
|
||||
var visualBrush = new VisualBrush(inkCanvas);
|
||||
// 绘制矩形并填充为inkCanvas的内容
|
||||
dc.DrawRectangle(visualBrush, null, new Rect(0, 0, inkCanvas.ActualWidth, inkCanvas.ActualHeight));
|
||||
}
|
||||
|
||||
// 创建适合墨迹画布尺寸的渲染位图
|
||||
var rtb = new RenderTargetBitmap(
|
||||
(int)inkCanvas.ActualWidth, (int)inkCanvas.ActualHeight,
|
||||
96, 96,
|
||||
PixelFormats.Pbgra32);
|
||||
rtb.Render(visual);
|
||||
|
||||
// 转换为GDI+ Bitmap并保存
|
||||
var encoder = new PngBitmapEncoder();
|
||||
encoder.Frames.Add(BitmapFrame.Create(rtb));
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
encoder.Save(ms);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
var imgBitmap = new System.Drawing.Bitmap(ms);
|
||||
|
||||
// 将生成的墨迹图像绘制到屏幕截图上
|
||||
// 居中绘制,确保墨迹位于屏幕中央
|
||||
int x = (bitmap.Width - imgBitmap.Width) / 2;
|
||||
int y = (bitmap.Height - imgBitmap.Height) / 2;
|
||||
g.DrawImage(imgBitmap, x, y);
|
||||
|
||||
// 保存为PNG
|
||||
string imagePathWithName = Path.ChangeExtension(savePathWithName, "png");
|
||||
bitmap.Save(imagePathWithName, System.Drawing.Imaging.ImageFormat.Png);
|
||||
|
||||
// 仍然保存墨迹文件以兼容旧版本
|
||||
var fs = new FileStream(savePathWithName, FileMode.Create);
|
||||
inkCanvas.Strokes.Save(fs);
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
|
||||
// 显示提示
|
||||
if (newNotice) ShowNotification("墨迹成功全页面保存至 " + Path.ChangeExtension(savePathWithName, "png"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定墨迹集合保存为图像到指定流
|
||||
/// </summary>
|
||||
private void SavePageAsImage(StrokeCollection strokes, Stream outputStream)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建临时InkCanvas来渲染墨迹
|
||||
var tempCanvas = new InkCanvas();
|
||||
tempCanvas.Strokes = strokes;
|
||||
tempCanvas.Width = inkCanvas.ActualWidth;
|
||||
tempCanvas.Height = inkCanvas.ActualHeight;
|
||||
|
||||
// 创建渲染位图
|
||||
var rtb = new RenderTargetBitmap(
|
||||
(int)tempCanvas.Width, (int)tempCanvas.Height,
|
||||
96, 96,
|
||||
PixelFormats.Pbgra32);
|
||||
rtb.Render(tempCanvas);
|
||||
|
||||
// 保存为PNG
|
||||
var encoder = new PngBitmapEncoder();
|
||||
encoder.Frames.Add(BitmapFrame.Create(rtb));
|
||||
encoder.Save(outputStream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存页面图像失败: {ex.ToString()}", LogHelper.LogType.Error);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void SymbolIconOpenStrokes_MouseUp(object sender, MouseButtonEventArgs e) {
|
||||
if (lastBorderMouseDownObject != sender) return;
|
||||
AnimationsHelper.HideWithSlideAndFade(BorderTools);
|
||||
AnimationsHelper.HideWithSlideAndFade(BoardBorderTools);
|
||||
|
||||
var openFileDialog = new OpenFileDialog();
|
||||
openFileDialog.InitialDirectory = Settings.Automation.AutoSavedStrokesLocation;
|
||||
openFileDialog.Title = "打开墨迹文件";
|
||||
openFileDialog.Filter = "Ink Canvas Strokes File (*.icstk)|*.icstk";
|
||||
openFileDialog.Filter = "Ink Canvas Strokes File (*.icstk)|*.icstk|ICC压缩包 (*.zip)|*.zip";
|
||||
if (openFileDialog.ShowDialog() != true) return;
|
||||
LogHelper.WriteLogToFile($"Strokes Insert: Name: {openFileDialog.FileName}",
|
||||
LogHelper.LogType.Event);
|
||||
|
||||
try {
|
||||
string fileExtension = Path.GetExtension(openFileDialog.FileName).ToLower();
|
||||
|
||||
if (fileExtension == ".zip") {
|
||||
// 处理ICC压缩包
|
||||
OpenICCZipFile(openFileDialog.FileName);
|
||||
} else {
|
||||
// 处理单个墨迹文件
|
||||
OpenSingleStrokeFile(openFileDialog.FileName);
|
||||
}
|
||||
|
||||
if (inkCanvas.Visibility != Visibility.Visible) SymbolIconCursor_Click(sender, null);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ShowNotification("墨迹打开失败");
|
||||
LogHelper.WriteLogToFile($"墨迹打开失败: {ex.ToString()}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开ICC创建的.zip压缩包
|
||||
/// </summary>
|
||||
private void OpenICCZipFile(string zipFilePath) {
|
||||
try {
|
||||
// 创建临时目录来解压文件
|
||||
string tempDir = Path.Combine(Path.GetTempPath(), $"InkCanvas_Open_{DateTime.Now:yyyyMMdd_HHmmss}");
|
||||
Directory.CreateDirectory(tempDir);
|
||||
|
||||
try {
|
||||
// 解压ZIP文件
|
||||
System.IO.Compression.ZipFile.ExtractToDirectory(zipFilePath, tempDir);
|
||||
|
||||
// 读取元数据文件
|
||||
string metadataFile = Path.Combine(tempDir, "metadata.txt");
|
||||
if (!File.Exists(metadataFile)) {
|
||||
throw new Exception("压缩包中未找到元数据文件");
|
||||
}
|
||||
|
||||
var metadata = ReadMetadataFile(metadataFile);
|
||||
|
||||
// 根据元数据信息决定恢复模式
|
||||
bool isPPTMode = metadata.ContainsKey("模式") && metadata["模式"].Contains("PPT放映");
|
||||
bool isWhiteboardMode = metadata.ContainsKey("模式") && metadata["模式"].Contains("白板");
|
||||
|
||||
// 检查当前是否处于PPT模式
|
||||
bool isCurrentlyInPPTMode = BtnPPTSlideShowEnd.Visibility == Visibility.Visible && pptApplication != null;
|
||||
|
||||
// 检查当前是否处于白板模式
|
||||
bool isCurrentlyInWhiteboardMode = currentMode != 0;
|
||||
|
||||
// 严格模式隔离:只在对应模式下恢复对应墨迹
|
||||
if (isPPTMode && isCurrentlyInPPTMode) {
|
||||
// 只在PPT放映模式下恢复PPT墨迹
|
||||
RestorePPTStrokesFromZip(tempDir, metadata);
|
||||
} else if (isWhiteboardMode && isCurrentlyInWhiteboardMode) {
|
||||
// 只在白板模式下恢复白板墨迹
|
||||
RestoreWhiteboardStrokesFromZip(tempDir, metadata);
|
||||
} else {
|
||||
// 模式不匹配时,显示提示信息
|
||||
string savedMode = isPPTMode ? "PPT放映" : (isWhiteboardMode ? "白板" : "未知");
|
||||
string currentMode = isCurrentlyInPPTMode ? "PPT放映" : (isCurrentlyInWhiteboardMode ? "白板" : "桌面");
|
||||
ShowNotification($"墨迹保存模式({savedMode})与当前模式({currentMode})不匹配,无法恢复墨迹");
|
||||
LogHelper.WriteLogToFile($"模式不匹配:保存模式={savedMode},当前模式={currentMode}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
ShowNotification($"成功打开ICC压缩包,共{(metadata.ContainsKey("总页数") ? metadata["总页数"] : "0")}页");
|
||||
}
|
||||
finally {
|
||||
// 清理临时目录
|
||||
try {
|
||||
if (Directory.Exists(tempDir))
|
||||
Directory.Delete(tempDir, true);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile($"清理临时目录失败: {ex.ToString()}", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile($"打开ICC压缩包失败: {ex.ToString()}", LogHelper.LogType.Error);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取元数据文件
|
||||
/// </summary>
|
||||
private Dictionary<string, string> ReadMetadataFile(string metadataPath) {
|
||||
var metadata = new Dictionary<string, string>();
|
||||
|
||||
using (var reader = new StreamReader(metadataPath, System.Text.Encoding.UTF8)) {
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null) {
|
||||
if (line.Contains(":")) {
|
||||
var parts = line.Split(new[] { ':' }, 2);
|
||||
if (parts.Length == 2) {
|
||||
metadata[parts[0].Trim()] = parts[1].Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从ZIP文件恢复PPT墨迹
|
||||
/// </summary>
|
||||
private void RestorePPTStrokesFromZip(string tempDir, Dictionary<string, string> metadata) {
|
||||
try {
|
||||
// 确保当前处于PPT放映模式
|
||||
if (BtnPPTSlideShowEnd.Visibility != Visibility.Visible || pptApplication == null) {
|
||||
throw new InvalidOperationException("当前不在PPT放映模式,无法恢复PPT墨迹");
|
||||
}
|
||||
|
||||
// 检查PPT文件路径是否匹配
|
||||
if (metadata.ContainsKey("PPT文件路径"))
|
||||
{
|
||||
string savedPptPath = metadata["PPT文件路径"];
|
||||
string currentPptPath = pptApplication.SlideShowWindows[1].Presentation.FullName;
|
||||
|
||||
if (!string.IsNullOrEmpty(savedPptPath) && !string.IsNullOrEmpty(currentPptPath))
|
||||
{
|
||||
// 使用文件路径哈希值进行比较,避免路径格式差异
|
||||
string savedHash = GetFileHash(savedPptPath);
|
||||
string currentHash = GetFileHash(currentPptPath);
|
||||
|
||||
if (savedHash != currentHash)
|
||||
{
|
||||
throw new InvalidOperationException($"墨迹文件与当前PPT文件不匹配。保存的PPT: {savedPptPath},当前PPT: {currentPptPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清空当前墨迹
|
||||
ClearStrokes(true);
|
||||
timeMachine.ClearStrokeHistory();
|
||||
|
||||
// 重置PPT墨迹存储
|
||||
if (memoryStreams == null) {
|
||||
memoryStreams = new MemoryStream[50];
|
||||
}
|
||||
|
||||
// 读取所有页面的墨迹文件
|
||||
var files = Directory.GetFiles(tempDir, "page_*.icstk");
|
||||
foreach (var file in files) {
|
||||
var fileName = Path.GetFileNameWithoutExtension(file);
|
||||
if (fileName.StartsWith("page_") && int.TryParse(fileName.Substring(5), out int pageNumber)) {
|
||||
using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read)) {
|
||||
var strokes = new StrokeCollection(fs);
|
||||
if (strokes.Count > 0) {
|
||||
var ms = new MemoryStream();
|
||||
strokes.Save(ms);
|
||||
ms.Position = 0;
|
||||
memoryStreams[pageNumber] = ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复当前页面的墨迹
|
||||
if (pptApplication.SlideShowWindows.Count > 0) {
|
||||
int currentSlide = pptApplication.SlideShowWindows[1].View.CurrentShowPosition;
|
||||
if (memoryStreams[currentSlide] != null && memoryStreams[currentSlide].Length > 0) {
|
||||
memoryStreams[currentSlide].Position = 0;
|
||||
inkCanvas.Strokes.Add(new StrokeCollection(memoryStreams[currentSlide]));
|
||||
}
|
||||
previousSlideID = currentSlide;
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile($"成功恢复PPT墨迹,共{files.Length}页");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile($"恢复PPT墨迹失败: {ex.ToString()}", LogHelper.LogType.Error);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从ZIP文件恢复白板墨迹
|
||||
/// </summary>
|
||||
private void RestoreWhiteboardStrokesFromZip(string tempDir, Dictionary<string, string> metadata) {
|
||||
try {
|
||||
// 确保当前处于白板模式
|
||||
if (currentMode == 0) {
|
||||
throw new InvalidOperationException("当前不在白板模式,无法恢复白板墨迹");
|
||||
}
|
||||
|
||||
// 清空当前墨迹
|
||||
ClearStrokes(true);
|
||||
timeMachine.ClearStrokeHistory();
|
||||
|
||||
// 读取总页数
|
||||
int totalPages = 1;
|
||||
if (metadata.ContainsKey("总页数") && int.TryParse(metadata["总页数"], out int parsedPages)) {
|
||||
totalPages = parsedPages;
|
||||
}
|
||||
|
||||
// 重置白板状态
|
||||
WhiteboardTotalCount = totalPages;
|
||||
CurrentWhiteboardIndex = 1;
|
||||
|
||||
// 清空历史记录
|
||||
for (int i = 0; i < TimeMachineHistories.Length; i++) {
|
||||
TimeMachineHistories[i] = null;
|
||||
}
|
||||
|
||||
// 读取所有页面的墨迹文件
|
||||
var files = Directory.GetFiles(tempDir, "page_*.icstk");
|
||||
foreach (var file in files) {
|
||||
var fileName = Path.GetFileNameWithoutExtension(file);
|
||||
if (fileName.StartsWith("page_") && int.TryParse(fileName.Substring(5), out int pageNumber)) {
|
||||
using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read)) {
|
||||
var strokes = new StrokeCollection(fs);
|
||||
if (strokes.Count > 0) {
|
||||
// 创建历史记录
|
||||
var history = new TimeMachineHistory(strokes, TimeMachineHistoryType.UserInput, false);
|
||||
TimeMachineHistories[pageNumber] = new TimeMachineHistory[] { history };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复第一页的墨迹
|
||||
if (TimeMachineHistories[1] != null) {
|
||||
RestoreStrokes();
|
||||
}
|
||||
|
||||
// 更新UI显示
|
||||
UpdateIndexInfoDisplay();
|
||||
|
||||
LogHelper.WriteLogToFile($"成功恢复白板墨迹,共{totalPages}页");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile($"恢复白板墨迹失败: {ex.ToString()}", LogHelper.LogType.Error);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开单个墨迹文件
|
||||
/// </summary>
|
||||
private void OpenSingleStrokeFile(string filePath) {
|
||||
var fileStreamHasNoStroke = false;
|
||||
using (var fs = new FileStream(openFileDialog.FileName, FileMode.Open, FileAccess.Read)) {
|
||||
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) {
|
||||
var strokes = new StrokeCollection(fs);
|
||||
fileStreamHasNoStroke = strokes.Count == 0;
|
||||
if (!fileStreamHasNoStroke) {
|
||||
@@ -102,21 +601,38 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复元素信息
|
||||
var elementsFile = Path.ChangeExtension(filePath, ".elements.json");
|
||||
if (File.Exists(elementsFile))
|
||||
{
|
||||
var elementInfos = JsonConvert.DeserializeObject<List<CanvasElementInfo>>(File.ReadAllText(elementsFile));
|
||||
foreach (var info in elementInfos)
|
||||
{
|
||||
if (info.Type == "Image" && File.Exists(info.SourcePath))
|
||||
{
|
||||
var img = new Image
|
||||
{
|
||||
Source = new BitmapImage(new Uri(info.SourcePath)),
|
||||
Width = info.Width,
|
||||
Height = info.Height,
|
||||
Stretch = Enum.TryParse<Stretch>(info.Stretch, out var stretch) ? stretch : Stretch.Fill
|
||||
};
|
||||
InkCanvas.SetLeft(img, info.Left);
|
||||
InkCanvas.SetTop(img, info.Top);
|
||||
inkCanvas.Children.Add(img);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fileStreamHasNoStroke)
|
||||
using (var ms = new MemoryStream(File.ReadAllBytes(openFileDialog.FileName))) {
|
||||
using (var ms = new MemoryStream(File.ReadAllBytes(filePath))) {
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
var strokes = new StrokeCollection(ms);
|
||||
ClearStrokes(true);
|
||||
timeMachine.ClearStrokeHistory();
|
||||
inkCanvas.Strokes.Add(strokes);
|
||||
LogHelper.NewLog($"Strokes Insert (2): Strokes Count: {strokes.Count.ToString()}");
|
||||
}
|
||||
|
||||
if (inkCanvas.Visibility != Visibility.Visible) SymbolIconCursor_Click(sender, null);
|
||||
}
|
||||
catch {
|
||||
ShowNotification("墨迹打开失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
@@ -14,7 +15,7 @@ namespace Ink_Canvas {
|
||||
CaptureAndSaveScreenshot(savePath, isHideNotification);
|
||||
|
||||
if (Settings.Automation.IsAutoSaveStrokesAtScreenshot)
|
||||
SaveInkCanvasStrokes(false, false);
|
||||
SaveInkCanvasStrokes(false);
|
||||
}
|
||||
|
||||
private void SaveScreenShotToDesktop() {
|
||||
@@ -25,101 +26,53 @@ namespace Ink_Canvas {
|
||||
CaptureAndSaveScreenshot(desktopPath, false);
|
||||
|
||||
if (Settings.Automation.IsAutoSaveStrokesAtScreenshot)
|
||||
SaveInkCanvasStrokes(false, false);
|
||||
SaveInkCanvasStrokes(false);
|
||||
}
|
||||
|
||||
// 提取公共的截图和保存逻辑
|
||||
private void CaptureAndSaveScreenshot(string savePath, bool isHideNotification) {
|
||||
var rc = System.Windows.Forms.SystemInformation.VirtualScreen;
|
||||
var rc = SystemInformation.VirtualScreen;
|
||||
|
||||
using (var bitmap = new System.Drawing.Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb))
|
||||
using (var memoryGraphics = System.Drawing.Graphics.FromImage(bitmap)) {
|
||||
memoryGraphics.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, System.Drawing.CopyPixelOperation.SourceCopy);
|
||||
using (var bitmap = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb))
|
||||
using (var memoryGraphics = Graphics.FromImage(bitmap)) {
|
||||
memoryGraphics.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, CopyPixelOperation.SourceCopy);
|
||||
|
||||
// 确保目录存在
|
||||
var directory = Path.GetDirectoryName(savePath);
|
||||
if (!Directory.Exists(directory)) {
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
try {
|
||||
// 新增双重目录检查
|
||||
Directory.CreateDirectory(directory); // 防止多线程场景下的竞争条件
|
||||
bitmap.Save(savePath, ImageFormat.Png);
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException ||
|
||||
ex is UnauthorizedAccessException ||
|
||||
ex is ExternalException) { // 新增GDI+异常捕获
|
||||
// 改进备用路径处理
|
||||
var docPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
|
||||
"Auto Saved - Screenshots",
|
||||
DateTime.Now.ToString("yyyyMMdd"),
|
||||
Path.GetFileNameWithoutExtension(savePath) + "_retry.png"); // 添加重试后缀
|
||||
|
||||
try {
|
||||
var docDir = Path.GetDirectoryName(docPath);
|
||||
Directory.CreateDirectory(docDir);
|
||||
bitmap.Save(docPath, ImageFormat.Png);
|
||||
savePath = docPath;
|
||||
}
|
||||
catch (Exception fallbackEx) {
|
||||
// 最终错误处理
|
||||
if (!isHideNotification) {
|
||||
ShowNotification($"截图保存失败: {fallbackEx.Message}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bitmap.Save(savePath, ImageFormat.Png);
|
||||
}
|
||||
|
||||
if (!isHideNotification) {
|
||||
try {
|
||||
ShowNotification($"截图成功保存至 {savePath}");
|
||||
}
|
||||
catch {
|
||||
// 防止通知系统自身异常导致崩溃
|
||||
}
|
||||
ShowNotification($"截图成功保存至 {savePath}");
|
||||
}
|
||||
}
|
||||
|
||||
// 获取日期文件夹路径
|
||||
private string GetDateFolderPath(string fileName) {
|
||||
var basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Saves");
|
||||
var dateFolder = DateTime.Now.ToString("yyyyMMdd");
|
||||
var fullPath = Path.Combine(basePath, "Auto Saved - Screenshots", dateFolder);
|
||||
|
||||
try {
|
||||
if (!Directory.Exists(fullPath)) {
|
||||
Directory.CreateDirectory(fullPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when
|
||||
(ex is IOException ||
|
||||
ex is UnauthorizedAccessException)
|
||||
{
|
||||
// 如果创建失败则使用软件根目录作为最终备选
|
||||
basePath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
fullPath = Path.Combine(basePath, "Auto Saved - Screenshots", dateFolder);
|
||||
|
||||
Directory.CreateDirectory(fullPath);
|
||||
if (string.IsNullOrWhiteSpace(fileName)) {
|
||||
fileName = DateTime.Now.ToString("HH-mm-ss");
|
||||
}
|
||||
|
||||
return Path.Combine(fullPath, $"{fileName}.png");
|
||||
var basePath = Settings.Automation.AutoSavedStrokesLocation;
|
||||
var dateFolder = DateTime.Now.ToString("yyyyMMdd");
|
||||
|
||||
return Path.Combine(
|
||||
basePath,
|
||||
"Auto Saved - Screenshots",
|
||||
dateFolder,
|
||||
$"{fileName}.png");
|
||||
}
|
||||
|
||||
// 获取默认文件夹路径
|
||||
private string GetDefaultFolderPath() {
|
||||
var basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Saves");
|
||||
var basePath = Settings.Automation.AutoSavedStrokesLocation;
|
||||
var screenshotsFolder = Path.Combine(basePath, "Auto Saved - Screenshots");
|
||||
|
||||
try {
|
||||
if (!Directory.Exists(screenshotsFolder)) {
|
||||
Directory.CreateDirectory(screenshotsFolder);
|
||||
}
|
||||
}
|
||||
catch (Exception) {
|
||||
// 如果创建失败则使用文档目录
|
||||
basePath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
screenshotsFolder = Path.Combine(basePath, "Auto Saved - Screenshots");
|
||||
|
||||
if (!Directory.Exists(screenshotsFolder)) {
|
||||
Directory.CreateDirectory(screenshotsFolder);
|
||||
}
|
||||
|
||||
@@ -128,4 +81,4 @@ namespace Ink_Canvas {
|
||||
$"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using Point = System.Windows.Point;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
@@ -27,7 +26,7 @@ namespace Ink_Canvas {
|
||||
lastBorderMouseDownObject = sender;
|
||||
}
|
||||
|
||||
private bool isStrokeSelectionCloneOn = false;
|
||||
private bool isStrokeSelectionCloneOn;
|
||||
|
||||
private void BorderStrokeSelectionClone_MouseUp(object sender, MouseButtonEventArgs e) {
|
||||
if (lastBorderMouseDownObject != sender) return;
|
||||
@@ -230,7 +229,7 @@ namespace Ink_Canvas {
|
||||
|
||||
#endregion
|
||||
|
||||
private bool isGridInkCanvasSelectionCoverMouseDown = false;
|
||||
private bool isGridInkCanvasSelectionCoverMouseDown;
|
||||
private StrokeCollection StrokesSelectionClone = new StrokeCollection();
|
||||
|
||||
private void GridInkCanvasSelectionCover_MouseDown(object sender, MouseButtonEventArgs e) {
|
||||
@@ -244,6 +243,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private void BtnSelect_Click(object sender, RoutedEventArgs e) {
|
||||
ExitMultiTouchModeIfNeeded();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 0;
|
||||
inkCanvas.IsManipulationEnabled = false;
|
||||
@@ -267,18 +267,37 @@ namespace Ink_Canvas {
|
||||
|
||||
private double BorderStrokeSelectionControlWidth = 490.0;
|
||||
private double BorderStrokeSelectionControlHeight = 80.0;
|
||||
private bool isProgramChangeStrokeSelection = false;
|
||||
private bool isProgramChangeStrokeSelection;
|
||||
|
||||
private void inkCanvas_SelectionChanged(object sender, EventArgs e) {
|
||||
if (isProgramChangeStrokeSelection) return;
|
||||
if (inkCanvas.GetSelectedStrokes().Count == 0) {
|
||||
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
|
||||
// 当没有选中笔画时,检查是否有选中的UIElement
|
||||
CheckUIElementSelection();
|
||||
}
|
||||
else {
|
||||
GridInkCanvasSelectionCover.Visibility = Visibility.Visible;
|
||||
BorderStrokeSelectionClone.Background = Brushes.Transparent;
|
||||
isStrokeSelectionCloneOn = false;
|
||||
updateBorderStrokeSelectionControlLocation();
|
||||
// 当选中笔画时,取消UIElement选择
|
||||
DeselectUIElement();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckUIElementSelection()
|
||||
{
|
||||
// 检查InkCanvas中的UIElement是否被选中
|
||||
var selectedElements = inkCanvas.GetSelectedElements();
|
||||
if (selectedElements.Count > 0)
|
||||
{
|
||||
var element = selectedElements[0];
|
||||
SelectUIElement(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeselectUIElement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,6 +344,7 @@ namespace Ink_Canvas {
|
||||
private void GridInkCanvasSelectionCover_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) {
|
||||
try {
|
||||
if (dec.Count >= 1) {
|
||||
bool disableScale = dec.Count >= 3;
|
||||
var md = e.DeltaManipulation;
|
||||
var trans = md.Translation; // 获得位移矢量
|
||||
var rotate = md.Rotation; // 获得旋转角度
|
||||
@@ -341,7 +361,8 @@ namespace Ink_Canvas {
|
||||
|
||||
// Update matrix to reflect translation/rotation
|
||||
m.Translate(trans.X, trans.Y); // 移动
|
||||
m.ScaleAt(scale.X, scale.Y, center.X, center.Y); // 缩放
|
||||
if (!disableScale)
|
||||
m.ScaleAt(scale.X, scale.Y, center.X, center.Y); // 缩放
|
||||
|
||||
var strokes = inkCanvas.GetSelectedStrokes();
|
||||
if (StrokesSelectionClone.Count != 0)
|
||||
@@ -416,5 +437,451 @@ namespace Ink_Canvas {
|
||||
StrokesSelectionClone = new StrokeCollection();
|
||||
}
|
||||
}
|
||||
|
||||
private void LassoSelect_Click(object sender, RoutedEventArgs e) {
|
||||
ExitMultiTouchModeIfNeeded();
|
||||
forceEraser = false;
|
||||
forcePointEraser = false;
|
||||
drawingShapeMode = 0;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
}
|
||||
|
||||
private void BtnLassoSelect_Click(object sender, RoutedEventArgs e) {
|
||||
ExitMultiTouchModeIfNeeded();
|
||||
forceEraser = false;
|
||||
forcePointEraser = false;
|
||||
drawingShapeMode = 0;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
}
|
||||
|
||||
#region UIElement Selection and Resize
|
||||
|
||||
private UIElement selectedUIElement;
|
||||
private System.Windows.Controls.Canvas resizeHandlesCanvas;
|
||||
private readonly List<Rectangle> resizeHandles = new List<Rectangle>();
|
||||
private bool isResizing;
|
||||
private ResizeDirection currentResizeDirection = ResizeDirection.None;
|
||||
private Point resizeStartPoint;
|
||||
private Rect originalElementBounds;
|
||||
|
||||
// 图片工具栏相关
|
||||
private Border borderImageSelectionControl;
|
||||
private double BorderImageSelectionControlWidth = 200.0;
|
||||
private double BorderImageSelectionControlHeight = 80.0;
|
||||
|
||||
private enum ResizeDirection
|
||||
{
|
||||
None,
|
||||
TopLeft,
|
||||
TopCenter,
|
||||
TopRight,
|
||||
MiddleLeft,
|
||||
MiddleRight,
|
||||
BottomLeft,
|
||||
BottomCenter,
|
||||
BottomRight
|
||||
}
|
||||
|
||||
private void InitializeUIElementSelection()
|
||||
{
|
||||
// 创建拖拽手柄画布
|
||||
if (resizeHandlesCanvas == null)
|
||||
{
|
||||
resizeHandlesCanvas = new System.Windows.Controls.Canvas
|
||||
{
|
||||
Background = Brushes.Transparent,
|
||||
IsHitTestVisible = true,
|
||||
Visibility = Visibility.Collapsed
|
||||
};
|
||||
|
||||
// 将手柄画布添加到主网格中,确保它在InkCanvas之上
|
||||
var mainGrid = inkCanvas.Parent as Grid;
|
||||
if (mainGrid != null)
|
||||
{
|
||||
mainGrid.Children.Add(resizeHandlesCanvas);
|
||||
Panel.SetZIndex(resizeHandlesCanvas, 1000); // 确保在最上层
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化图片工具栏引用
|
||||
if (borderImageSelectionControl == null)
|
||||
{
|
||||
borderImageSelectionControl = FindName("BorderImageSelectionControl") as Border;
|
||||
}
|
||||
|
||||
// 创建8个拖拽手柄
|
||||
CreateResizeHandles();
|
||||
}
|
||||
|
||||
private void CreateResizeHandles()
|
||||
{
|
||||
resizeHandles.Clear();
|
||||
resizeHandlesCanvas.Children.Clear();
|
||||
|
||||
var directions = new[]
|
||||
{
|
||||
ResizeDirection.TopLeft, ResizeDirection.TopCenter, ResizeDirection.TopRight,
|
||||
ResizeDirection.MiddleLeft, ResizeDirection.MiddleRight,
|
||||
ResizeDirection.BottomLeft, ResizeDirection.BottomCenter, ResizeDirection.BottomRight
|
||||
};
|
||||
|
||||
foreach (var direction in directions)
|
||||
{
|
||||
var handle = new Rectangle
|
||||
{
|
||||
Width = 12,
|
||||
Height = 12,
|
||||
Fill = Brushes.White,
|
||||
Stroke = Brushes.DodgerBlue,
|
||||
StrokeThickness = 2,
|
||||
Cursor = GetCursorForDirection(direction),
|
||||
Tag = direction
|
||||
};
|
||||
|
||||
handle.MouseDown += ResizeHandle_MouseDown;
|
||||
handle.MouseMove += ResizeHandle_MouseMove;
|
||||
handle.MouseUp += ResizeHandle_MouseUp;
|
||||
|
||||
resizeHandles.Add(handle);
|
||||
resizeHandlesCanvas.Children.Add(handle);
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor GetCursorForDirection(ResizeDirection direction)
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case ResizeDirection.TopLeft:
|
||||
case ResizeDirection.BottomRight:
|
||||
return Cursors.SizeNWSE;
|
||||
case ResizeDirection.TopRight:
|
||||
case ResizeDirection.BottomLeft:
|
||||
return Cursors.SizeNESW;
|
||||
case ResizeDirection.TopCenter:
|
||||
case ResizeDirection.BottomCenter:
|
||||
return Cursors.SizeNS;
|
||||
case ResizeDirection.MiddleLeft:
|
||||
case ResizeDirection.MiddleRight:
|
||||
return Cursors.SizeWE;
|
||||
default:
|
||||
return Cursors.Arrow;
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectUIElement(UIElement element)
|
||||
{
|
||||
if (selectedUIElement == element) return;
|
||||
|
||||
// 取消之前的选择
|
||||
DeselectUIElement();
|
||||
|
||||
// 清除笔画选择
|
||||
if (inkCanvas.GetSelectedStrokes().Count > 0)
|
||||
{
|
||||
isProgramChangeStrokeSelection = true;
|
||||
inkCanvas.Select(new StrokeCollection());
|
||||
isProgramChangeStrokeSelection = false;
|
||||
}
|
||||
|
||||
selectedUIElement = element;
|
||||
|
||||
if (element != null)
|
||||
{
|
||||
// 初始化选择系统(如果还没有初始化)
|
||||
if (resizeHandlesCanvas == null)
|
||||
{
|
||||
InitializeUIElementSelection();
|
||||
}
|
||||
|
||||
// 根据元素类型显示不同的工具栏
|
||||
if (element is Image)
|
||||
{
|
||||
ShowImageToolbar();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 对于其他UI元素,显示拖拽手柄
|
||||
ShowResizeHandles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DeselectUIElement()
|
||||
{
|
||||
selectedUIElement = null;
|
||||
HideResizeHandles();
|
||||
HideImageToolbar();
|
||||
}
|
||||
|
||||
private void ShowResizeHandles()
|
||||
{
|
||||
if (selectedUIElement == null || resizeHandlesCanvas == null) return;
|
||||
|
||||
var bounds = GetUIElementBounds(selectedUIElement);
|
||||
UpdateResizeHandlesPosition(bounds);
|
||||
resizeHandlesCanvas.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void HideResizeHandles()
|
||||
{
|
||||
if (resizeHandlesCanvas != null)
|
||||
{
|
||||
resizeHandlesCanvas.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowImageToolbar()
|
||||
{
|
||||
if (selectedUIElement == null || borderImageSelectionControl == null) return;
|
||||
|
||||
var bounds = GetUIElementBounds(selectedUIElement);
|
||||
UpdateImageToolbarPosition(bounds);
|
||||
borderImageSelectionControl.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void HideImageToolbar()
|
||||
{
|
||||
if (borderImageSelectionControl != null)
|
||||
{
|
||||
borderImageSelectionControl.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateImageToolbarPosition(Rect bounds)
|
||||
{
|
||||
if (borderImageSelectionControl == null) return;
|
||||
|
||||
// 计算工具栏位置,类似于墨迹选择工具栏的逻辑
|
||||
var toolbarX = bounds.X + bounds.Width / 2 - BorderImageSelectionControlWidth / 2;
|
||||
var toolbarY = bounds.Y + bounds.Height + 10; // 在图片下方10像素处
|
||||
|
||||
// 确保工具栏不会超出画布边界
|
||||
if (toolbarX < 0) toolbarX = 0;
|
||||
if (toolbarX + BorderImageSelectionControlWidth > inkCanvas.ActualWidth)
|
||||
toolbarX = inkCanvas.ActualWidth - BorderImageSelectionControlWidth;
|
||||
|
||||
if (toolbarY + BorderImageSelectionControlHeight > inkCanvas.ActualHeight)
|
||||
toolbarY = bounds.Y - BorderImageSelectionControlHeight - 10; // 如果下方空间不够,显示在上方
|
||||
|
||||
borderImageSelectionControl.Margin = new Thickness(toolbarX, toolbarY, 0, 0);
|
||||
}
|
||||
|
||||
private Rect GetUIElementBounds(UIElement element)
|
||||
{
|
||||
var left = InkCanvas.GetLeft(element);
|
||||
var top = InkCanvas.GetTop(element);
|
||||
|
||||
if (double.IsNaN(left)) left = 0;
|
||||
if (double.IsNaN(top)) top = 0;
|
||||
|
||||
var width = 0.0;
|
||||
var height = 0.0;
|
||||
|
||||
if (element is FrameworkElement fe)
|
||||
{
|
||||
width = fe.ActualWidth > 0 ? fe.ActualWidth : fe.Width;
|
||||
height = fe.ActualHeight > 0 ? fe.ActualHeight : fe.Height;
|
||||
}
|
||||
|
||||
return new Rect(left, top, width, height);
|
||||
}
|
||||
|
||||
private void UpdateResizeHandlesPosition(Rect bounds)
|
||||
{
|
||||
if (resizeHandles.Count != 8) return;
|
||||
|
||||
var handleSize = 12.0;
|
||||
var halfHandle = handleSize / 2;
|
||||
|
||||
// 计算手柄位置
|
||||
var positions = new[]
|
||||
{
|
||||
new Point(bounds.Left - halfHandle, bounds.Top - halfHandle), // TopLeft
|
||||
new Point(bounds.Left + bounds.Width / 2 - halfHandle, bounds.Top - halfHandle), // TopCenter
|
||||
new Point(bounds.Right - halfHandle, bounds.Top - halfHandle), // TopRight
|
||||
new Point(bounds.Left - halfHandle, bounds.Top + bounds.Height / 2 - halfHandle), // MiddleLeft
|
||||
new Point(bounds.Right - halfHandle, bounds.Top + bounds.Height / 2 - halfHandle), // MiddleRight
|
||||
new Point(bounds.Left - halfHandle, bounds.Bottom - halfHandle), // BottomLeft
|
||||
new Point(bounds.Left + bounds.Width / 2 - halfHandle, bounds.Bottom - halfHandle), // BottomCenter
|
||||
new Point(bounds.Right - halfHandle, bounds.Bottom - halfHandle) // BottomRight
|
||||
};
|
||||
|
||||
for (int i = 0; i < resizeHandles.Count && i < positions.Length; i++)
|
||||
{
|
||||
System.Windows.Controls.Canvas.SetLeft(resizeHandles[i], positions[i].X);
|
||||
System.Windows.Controls.Canvas.SetTop(resizeHandles[i], positions[i].Y);
|
||||
}
|
||||
}
|
||||
|
||||
private void ResizeHandle_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (selectedUIElement == null) return;
|
||||
|
||||
var handle = sender as Rectangle;
|
||||
if (handle?.Tag is ResizeDirection direction)
|
||||
{
|
||||
isResizing = true;
|
||||
currentResizeDirection = direction;
|
||||
resizeStartPoint = e.GetPosition(inkCanvas);
|
||||
originalElementBounds = GetUIElementBounds(selectedUIElement);
|
||||
|
||||
handle.CaptureMouse();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResizeHandle_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (!isResizing || selectedUIElement == null) return;
|
||||
|
||||
var currentPoint = e.GetPosition(inkCanvas);
|
||||
var deltaX = currentPoint.X - resizeStartPoint.X;
|
||||
var deltaY = currentPoint.Y - resizeStartPoint.Y;
|
||||
|
||||
ResizeUIElement(deltaX, deltaY);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void ResizeHandle_MouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (isResizing)
|
||||
{
|
||||
isResizing = false;
|
||||
currentResizeDirection = ResizeDirection.None;
|
||||
|
||||
var handle = sender as Rectangle;
|
||||
handle?.ReleaseMouseCapture();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResizeUIElement(double deltaX, double deltaY)
|
||||
{
|
||||
if (selectedUIElement == null) return;
|
||||
|
||||
var newBounds = originalElementBounds;
|
||||
const double minSize = 20.0;
|
||||
|
||||
switch (currentResizeDirection)
|
||||
{
|
||||
case ResizeDirection.TopLeft:
|
||||
var newWidth = originalElementBounds.Width - deltaX;
|
||||
var newHeight = originalElementBounds.Height - deltaY;
|
||||
if (newWidth >= minSize && newHeight >= minSize)
|
||||
{
|
||||
newBounds.X = originalElementBounds.X + deltaX;
|
||||
newBounds.Y = originalElementBounds.Y + deltaY;
|
||||
newBounds.Width = newWidth;
|
||||
newBounds.Height = newHeight;
|
||||
}
|
||||
break;
|
||||
|
||||
case ResizeDirection.TopCenter:
|
||||
var newHeightTC = originalElementBounds.Height - deltaY;
|
||||
if (newHeightTC >= minSize)
|
||||
{
|
||||
newBounds.Y = originalElementBounds.Y + deltaY;
|
||||
newBounds.Height = newHeightTC;
|
||||
}
|
||||
break;
|
||||
|
||||
case ResizeDirection.TopRight:
|
||||
var newWidthTR = originalElementBounds.Width + deltaX;
|
||||
var newHeightTR = originalElementBounds.Height - deltaY;
|
||||
if (newWidthTR >= minSize && newHeightTR >= minSize)
|
||||
{
|
||||
newBounds.Y = originalElementBounds.Y + deltaY;
|
||||
newBounds.Width = newWidthTR;
|
||||
newBounds.Height = newHeightTR;
|
||||
}
|
||||
break;
|
||||
|
||||
case ResizeDirection.MiddleLeft:
|
||||
var newWidthML = originalElementBounds.Width - deltaX;
|
||||
if (newWidthML >= minSize)
|
||||
{
|
||||
newBounds.X = originalElementBounds.X + deltaX;
|
||||
newBounds.Width = newWidthML;
|
||||
}
|
||||
break;
|
||||
|
||||
case ResizeDirection.MiddleRight:
|
||||
var newWidthMR = originalElementBounds.Width + deltaX;
|
||||
if (newWidthMR >= minSize)
|
||||
{
|
||||
newBounds.Width = newWidthMR;
|
||||
}
|
||||
break;
|
||||
|
||||
case ResizeDirection.BottomLeft:
|
||||
var newWidthBL = originalElementBounds.Width - deltaX;
|
||||
var newHeightBL = originalElementBounds.Height + deltaY;
|
||||
if (newWidthBL >= minSize && newHeightBL >= minSize)
|
||||
{
|
||||
newBounds.X = originalElementBounds.X + deltaX;
|
||||
newBounds.Width = newWidthBL;
|
||||
newBounds.Height = newHeightBL;
|
||||
}
|
||||
break;
|
||||
|
||||
case ResizeDirection.BottomCenter:
|
||||
var newHeightBC = originalElementBounds.Height + deltaY;
|
||||
if (newHeightBC >= minSize)
|
||||
{
|
||||
newBounds.Height = newHeightBC;
|
||||
}
|
||||
break;
|
||||
|
||||
case ResizeDirection.BottomRight:
|
||||
var newWidthBR = originalElementBounds.Width + deltaX;
|
||||
var newHeightBR = originalElementBounds.Height + deltaY;
|
||||
if (newWidthBR >= minSize && newHeightBR >= minSize)
|
||||
{
|
||||
newBounds.Width = newWidthBR;
|
||||
newBounds.Height = newHeightBR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 应用新的尺寸和位置
|
||||
ApplyUIElementBounds(selectedUIElement, newBounds);
|
||||
|
||||
// 更新手柄位置
|
||||
UpdateResizeHandlesPosition(newBounds);
|
||||
}
|
||||
|
||||
private void ApplyUIElementBounds(UIElement element, Rect bounds)
|
||||
{
|
||||
InkCanvas.SetLeft(element, bounds.X);
|
||||
InkCanvas.SetTop(element, bounds.Y);
|
||||
|
||||
if (element is FrameworkElement fe)
|
||||
{
|
||||
fe.Width = bounds.Width;
|
||||
fe.Height = bounds.Height;
|
||||
}
|
||||
}
|
||||
|
||||
private void UIElement_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.Select)
|
||||
{
|
||||
var element = sender as UIElement;
|
||||
if (element != null)
|
||||
{
|
||||
// 切换到选择模式并选择这个元素
|
||||
inkCanvas.Select(new[] { element });
|
||||
SelectUIElement(element);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,26 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using File = System.IO.File;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Hardcodet.Wpf.TaskbarNotification;
|
||||
using Ink_Canvas.Helpers;
|
||||
using Newtonsoft.Json;
|
||||
using OSVersionExtension;
|
||||
using Application = System.Windows.Application;
|
||||
using CheckBox = System.Windows.Controls.CheckBox;
|
||||
using ComboBox = System.Windows.Controls.ComboBox;
|
||||
using File = System.IO.File;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
|
||||
using OperatingSystem = OSVersionExtension.OperatingSystem;
|
||||
using RadioButton = System.Windows.Controls.RadioButton;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
@@ -21,16 +29,33 @@ namespace Ink_Canvas {
|
||||
private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Startup.IsAutoUpdate = ToggleSwitchIsAutoUpdate.IsOn;
|
||||
ToggleSwitchIsAutoUpdateWithSilence.Visibility =
|
||||
|
||||
// 自动更新关闭时隐藏静默更新选项
|
||||
ToggleSwitchIsAutoUpdateWithSilence.Visibility =
|
||||
ToggleSwitchIsAutoUpdate.IsOn ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
// 如果关闭了自动更新,同时也关闭静默更新
|
||||
if (!ToggleSwitchIsAutoUpdate.IsOn) {
|
||||
Settings.Startup.IsAutoUpdateWithSilence = false;
|
||||
ToggleSwitchIsAutoUpdateWithSilence.IsOn = false;
|
||||
}
|
||||
|
||||
// 无论如何,静默更新时间区域的显示都要跟随静默更新设置
|
||||
AutoUpdateTimePeriodBlock.Visibility =
|
||||
(Settings.Startup.IsAutoUpdateWithSilence && Settings.Startup.IsAutoUpdate) ?
|
||||
Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchIsAutoUpdateWithSilence_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Startup.IsAutoUpdateWithSilence = ToggleSwitchIsAutoUpdateWithSilence.IsOn;
|
||||
|
||||
// 静默更新的时间设置区域只在静默更新开启时显示
|
||||
AutoUpdateTimePeriodBlock.Visibility =
|
||||
Settings.Startup.IsAutoUpdateWithSilence ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
@@ -226,41 +251,97 @@ namespace Ink_Canvas {
|
||||
public void ComboBoxFloatingBarImg_SelectionChanged(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Appearance.FloatingBarImg = ComboBoxFloatingBarImg.SelectedIndex;
|
||||
if (ComboBoxFloatingBarImg.SelectedIndex == 0) {
|
||||
UpdateFloatingBarIcon();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
public void UpdateFloatingBarIcon()
|
||||
{
|
||||
int index = Settings.Appearance.FloatingBarImg;
|
||||
|
||||
if (index == 0) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/icc.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(0.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 1) {
|
||||
} else if (index == 1) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(
|
||||
new Uri("pack://application:,,,/Resources/Icons-png/icc-transparent-dark-small.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(1.2);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 2) {
|
||||
} else if (index == 2) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuandoujiyanhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 3) {
|
||||
} else if (index == 3) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanshounvhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 4) {
|
||||
} else if (index == 4) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanciya.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 5) {
|
||||
} else if (index == 5) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanneikuhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 6) {
|
||||
} else if (index == 6) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuandogeyuanliangwo.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 7) {
|
||||
} else if (index == 7) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/tiebahuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1);
|
||||
} else if (index >= 8 && index - 8 < Settings.Appearance.CustomFloatingBarImgs.Count) {
|
||||
// 使用自定义图标
|
||||
var customIcon = Settings.Appearance.CustomFloatingBarImgs[index - 8];
|
||||
try {
|
||||
FloatingbarHeadIconImg.Source = new BitmapImage(new Uri(customIcon.FilePath));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2);
|
||||
} catch {
|
||||
// 如果加载失败,使用默认图标
|
||||
FloatingbarHeadIconImg.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/icc.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(0.5);
|
||||
}
|
||||
}
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
public void UpdateCustomIconsInComboBox()
|
||||
{
|
||||
// 保留前8个内置图标选项
|
||||
while (ComboBoxFloatingBarImg.Items.Count > 8)
|
||||
{
|
||||
ComboBoxFloatingBarImg.Items.RemoveAt(ComboBoxFloatingBarImg.Items.Count - 1);
|
||||
}
|
||||
|
||||
// 添加自定义图标选项
|
||||
foreach (var customIcon in Settings.Appearance.CustomFloatingBarImgs)
|
||||
{
|
||||
ComboBoxItem item = new ComboBoxItem();
|
||||
item.Content = customIcon.Name;
|
||||
item.FontFamily = new FontFamily("Microsoft YaHei UI");
|
||||
ComboBoxFloatingBarImg.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonAddCustomIcon_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AddCustomIconWindow dialog = new AddCustomIconWindow(this);
|
||||
dialog.Owner = this;
|
||||
dialog.ShowDialog();
|
||||
|
||||
if (dialog.IsSuccess)
|
||||
{
|
||||
// 自动选中新添加的图标
|
||||
ComboBoxFloatingBarImg.SelectedIndex = ComboBoxFloatingBarImg.Items.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonManageCustomIcons_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CustomIconWindow dialog = new CustomIconWindow(this);
|
||||
dialog.Owner = this;
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void ToggleSwitchEnableTimeDisplayInWhiteboardMode_Toggled(object sender, RoutedEventArgs e) {
|
||||
@@ -753,7 +834,24 @@ namespace Ink_Canvas {
|
||||
private void LineStraightenSensitivitySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
|
||||
if (!isLoaded) return;
|
||||
|
||||
// 记录旧值用于调试
|
||||
double oldValue = Settings.InkToShape.LineStraightenSensitivity;
|
||||
|
||||
// 确保灵敏度值被正确保存到设置中
|
||||
Settings.InkToShape.LineStraightenSensitivity = e.NewValue;
|
||||
|
||||
// 输出调试信息,观察值变化
|
||||
Debug.WriteLine($"LineStraightenSensitivity changed: {oldValue} -> {e.NewValue}");
|
||||
|
||||
// 立即保存设置到文件,确保设置不会丢失
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchHighPrecisionLineStraighten_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
|
||||
Settings.Canvas.HighPrecisionLineStraighten = ToggleSwitchHighPrecisionLineStraighten.IsOn;
|
||||
Debug.WriteLine($"HighPrecisionLineStraighten changed: {Settings.Canvas.HighPrecisionLineStraighten}");
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
@@ -777,12 +875,19 @@ namespace Ink_Canvas {
|
||||
private void ComboBoxEraserSize_SelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.EraserSize = ComboBoxEraserSize.SelectedIndex;
|
||||
|
||||
// 使用新的高级橡皮擦形状应用方法
|
||||
ApplyAdvancedEraserShape();
|
||||
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
}
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ComboBoxEraserSizeFloatingBar_SelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
|
||||
ComboBox s = (ComboBox)sender;
|
||||
Settings.Canvas.EraserSize = s.SelectedIndex;
|
||||
if (s == ComboBoxEraserSizeFloatingBar) {
|
||||
@@ -792,46 +897,14 @@ namespace Ink_Canvas {
|
||||
ComboBoxEraserSizeFloatingBar.SelectedIndex = s.SelectedIndex;
|
||||
ComboBoxEraserSize.SelectedIndex = s.SelectedIndex;
|
||||
}
|
||||
if (Settings.Canvas.EraserShapeType == 0) {
|
||||
double k = 1;
|
||||
switch (s.SelectedIndex) {
|
||||
case 0:
|
||||
k = 0.5;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.8;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.25;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.8;
|
||||
break;
|
||||
}
|
||||
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(k * 90, k * 90);
|
||||
} else if (Settings.Canvas.EraserShapeType == 1) {
|
||||
double k = 1;
|
||||
switch (s.SelectedIndex) {
|
||||
case 0:
|
||||
k = 0.7;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.9;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.2;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.6;
|
||||
break;
|
||||
}
|
||||
// 使用新的高级橡皮擦形状应用方法
|
||||
ApplyAdvancedEraserShape();
|
||||
|
||||
inkCanvas.EraserShape = new RectangleStylusShape(k * 90 * 0.6, k * 90);
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
}
|
||||
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
@@ -840,23 +913,11 @@ namespace Ink_Canvas {
|
||||
Settings.Canvas.EraserShapeType = 0;
|
||||
SaveSettingsToFile();
|
||||
CheckEraserTypeTab();
|
||||
double k = 1;
|
||||
switch (ComboBoxEraserSizeFloatingBar.SelectedIndex) {
|
||||
case 0:
|
||||
k = 0.5;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.8;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.25;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.8;
|
||||
break;
|
||||
}
|
||||
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(k * 90, k * 90);
|
||||
// 使用新的高级橡皮擦形状应用方法
|
||||
ApplyAdvancedEraserShape();
|
||||
|
||||
// 确保当前处于橡皮擦模式时能立即看到效果
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
}
|
||||
@@ -866,23 +927,11 @@ namespace Ink_Canvas {
|
||||
Settings.Canvas.EraserShapeType = 1;
|
||||
SaveSettingsToFile();
|
||||
CheckEraserTypeTab();
|
||||
double k = 1;
|
||||
switch (ComboBoxEraserSizeFloatingBar.SelectedIndex) {
|
||||
case 0:
|
||||
k = 0.7;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.9;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.2;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.6;
|
||||
break;
|
||||
}
|
||||
|
||||
inkCanvas.EraserShape = new RectangleStylusShape(k * 90 * 0.6, k * 90);
|
||||
// 使用新的高级橡皮擦形状应用方法
|
||||
ApplyAdvancedEraserShape();
|
||||
|
||||
// 确保当前处于橡皮擦模式时能立即看到效果
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
}
|
||||
@@ -1198,6 +1247,13 @@ namespace Ink_Canvas {
|
||||
else
|
||||
timerKillProcess.Stop();
|
||||
}
|
||||
|
||||
private void ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode = ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchSaveScreenshotsInDateFolders_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
@@ -1235,9 +1291,84 @@ namespace Ink_Canvas {
|
||||
if (!isLoaded) return;
|
||||
drawingAttributes.FitToCurve = ToggleSwitchFitToCurve.IsOn;
|
||||
Settings.Canvas.FitToCurve = ToggleSwitchFitToCurve.IsOn;
|
||||
|
||||
// 启用原来的FitToCurve时自动禁用高级贝塞尔平滑
|
||||
if (ToggleSwitchFitToCurve.IsOn)
|
||||
{
|
||||
ToggleSwitchAdvancedBezierSmoothing.IsOn = false;
|
||||
Settings.Canvas.UseAdvancedBezierSmoothing = false;
|
||||
}
|
||||
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchAdvancedBezierSmoothing_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.UseAdvancedBezierSmoothing = ToggleSwitchAdvancedBezierSmoothing.IsOn;
|
||||
|
||||
// 启用高级贝塞尔平滑时自动禁用原来的FitToCurve
|
||||
if (ToggleSwitchAdvancedBezierSmoothing.IsOn)
|
||||
{
|
||||
ToggleSwitchFitToCurve.IsOn = false;
|
||||
Settings.Canvas.FitToCurve = false;
|
||||
drawingAttributes.FitToCurve = false;
|
||||
}
|
||||
|
||||
// 更新墨迹平滑管理器配置
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
// 注释掉这些方法,因为对应的UI控件还没有在XAML中定义
|
||||
/*
|
||||
private void ToggleSwitchAsyncInkSmoothing_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.UseAsyncInkSmoothing = ToggleSwitchAsyncInkSmoothing.IsOn;
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchHardwareAcceleration_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.UseHardwareAcceleration = ToggleSwitchHardwareAcceleration.IsOn;
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ComboBoxInkSmoothingQuality_SelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.InkSmoothingQuality = ComboBoxInkSmoothingQuality.SelectedIndex;
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void SliderMaxConcurrentTasks_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.MaxConcurrentSmoothingTasks = (int)SliderMaxConcurrentTasks.Value;
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ButtonApplyRecommendedSettings_Click(object sender, RoutedEventArgs e) {
|
||||
// 应用推荐的性能设置
|
||||
Helpers.InkSmoothingManager.ApplyRecommendedSettings();
|
||||
LoadSettings(false);
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
|
||||
ShowNotification("已应用推荐的性能设置");
|
||||
}
|
||||
|
||||
private void ButtonShowPerformanceStats_Click(object sender, RoutedEventArgs e) {
|
||||
if (_inkSmoothingManager != null)
|
||||
{
|
||||
var stats = _inkSmoothingManager.GetPerformanceStats();
|
||||
ShowNotification($"性能统计: {stats}");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private void ToggleSwitchAutoSaveStrokesInPowerPoint_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint = ToggleSwitchAutoSaveStrokesInPowerPoint.IsOn;
|
||||
@@ -1275,21 +1406,21 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private void AutoSavedStrokesLocationButton_Click(object sender, RoutedEventArgs e) {
|
||||
var folderBrowser = new System.Windows.Forms.FolderBrowserDialog();
|
||||
var folderBrowser = new FolderBrowserDialog();
|
||||
folderBrowser.ShowDialog();
|
||||
if (folderBrowser.SelectedPath.Length > 0) AutoSavedStrokesLocation.Text = folderBrowser.SelectedPath;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void SetAutoSavedStrokesLocationToDiskDButton_Click(object sender, RoutedEventArgs e) {
|
||||
// 修改默认路径为软件根目录下的 Saves 文件夹
|
||||
string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string savesPath = System.IO.Path.Combine(appDirectory, "Saves");
|
||||
AutoSavedStrokesLocation.Text = savesPath;
|
||||
AutoSavedStrokesLocation.Text = @"D:\Ink Canvas";
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void SetAutoSavedStrokesLocationToDocumentFolderButton_Click(object sender, RoutedEventArgs e) {
|
||||
AutoSavedStrokesLocation.Text =
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Ink Canvas";
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchAutoDelSavedFiles_Toggled(object sender, RoutedEventArgs e) {
|
||||
@@ -1312,6 +1443,12 @@ namespace Ink_Canvas {
|
||||
ToggleSwitchAutoSaveScreenShotInPowerPoint.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchSaveFullPageStrokes_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Automation.IsSaveFullPageStrokes = ToggleSwitchSaveFullPageStrokes.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1347,29 +1484,56 @@ namespace Ink_Canvas {
|
||||
BoardToggleSwitchEnableMultiTouchMode.IsOn = ToggleSwitchEnableMultiTouchMode.IsOn;
|
||||
else
|
||||
ToggleSwitchEnableMultiTouchMode.IsOn = BoardToggleSwitchEnableMultiTouchMode.IsOn;
|
||||
|
||||
if (ToggleSwitchEnableMultiTouchMode.IsOn) {
|
||||
if (!isInMultiTouchMode) {
|
||||
// 保存当前编辑模式和绘图工具状态
|
||||
InkCanvasEditingMode currentEditingMode = inkCanvas.EditingMode;
|
||||
int currentDrawingShapeMode = drawingShapeMode;
|
||||
bool currentForceEraser = forceEraser;
|
||||
|
||||
inkCanvas.StylusDown += MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove += MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp += MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown += MainWindow_TouchDown;
|
||||
inkCanvas.TouchDown -= Main_Grid_TouchDown;
|
||||
|
||||
// 先设为None再设回原来的模式,避免可能的事件冲突
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
inkCanvas.Children.Clear();
|
||||
isInMultiTouchMode = true;
|
||||
|
||||
// 恢复到之前的编辑状态
|
||||
inkCanvas.EditingMode = currentEditingMode;
|
||||
drawingShapeMode = currentDrawingShapeMode;
|
||||
forceEraser = currentForceEraser;
|
||||
}
|
||||
} else {
|
||||
if (isInMultiTouchMode) {
|
||||
// 保存当前编辑模式和绘图工具状态
|
||||
InkCanvasEditingMode currentEditingMode = inkCanvas.EditingMode;
|
||||
int currentDrawingShapeMode = drawingShapeMode;
|
||||
bool currentForceEraser = forceEraser;
|
||||
|
||||
inkCanvas.StylusDown -= MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove -= MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp -= MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown -= MainWindow_TouchDown;
|
||||
inkCanvas.TouchDown += Main_Grid_TouchDown;
|
||||
|
||||
// 先设为None再设回原来的模式,避免可能的事件冲突
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
// 保存非笔画元素(如图片)
|
||||
var preservedElements = PreserveNonStrokeElements();
|
||||
inkCanvas.Children.Clear();
|
||||
// 恢复非笔画元素
|
||||
RestoreNonStrokeElements(preservedElements);
|
||||
isInMultiTouchMode = false;
|
||||
|
||||
// 恢复到之前的编辑状态
|
||||
inkCanvas.EditingMode = currentEditingMode;
|
||||
drawingShapeMode = currentDrawingShapeMode;
|
||||
forceEraser = currentForceEraser;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1498,7 +1662,7 @@ namespace Ink_Canvas {
|
||||
Settings.PowerPointSettings.IsNotifyHiddenPage = false;
|
||||
Settings.PowerPointSettings.IsEnableTwoFingerGestureInPresentationMode = false;
|
||||
Settings.PowerPointSettings.IsEnableFingerGestureSlideShowControl = false;
|
||||
Settings.PowerPointSettings.IsSupportWPS = true;
|
||||
Settings.PowerPointSettings.IsSupportWPS = false;
|
||||
|
||||
Settings.Canvas.InkWidth = 2.5;
|
||||
Settings.Canvas.IsShowCursor = false;
|
||||
@@ -1509,11 +1673,12 @@ namespace Ink_Canvas {
|
||||
Settings.Canvas.EraserShapeType = 1;
|
||||
Settings.Canvas.HideStrokeWhenSelecting = false;
|
||||
Settings.Canvas.ClearCanvasAndClearTimeMachine = false;
|
||||
Settings.Canvas.FitToCurve = true;
|
||||
Settings.Canvas.FitToCurve = false;
|
||||
Settings.Canvas.UseAdvancedBezierSmoothing = true;
|
||||
Settings.Canvas.EnablePressureTouchMode = false;
|
||||
Settings.Canvas.DisablePressure = false;
|
||||
Settings.Canvas.AutoStraightenLine = true;
|
||||
Settings.Canvas.AutoStraightenLineThreshold = 30;
|
||||
Settings.Canvas.AutoStraightenLineThreshold = 80;
|
||||
Settings.Canvas.LineEndpointSnapping = true;
|
||||
Settings.Canvas.LineEndpointSnappingThreshold = 15;
|
||||
Settings.Canvas.UsingWhiteboard = false;
|
||||
@@ -1536,8 +1701,8 @@ namespace Ink_Canvas {
|
||||
Settings.Startup.IsEnableNibMode = false;
|
||||
Settings.Startup.IsAutoUpdate = true;
|
||||
Settings.Startup.IsAutoUpdateWithSilence = true;
|
||||
Settings.Startup.AutoUpdateWithSilenceStartTime = "18:20";
|
||||
Settings.Startup.AutoUpdateWithSilenceEndTime = "07:40";
|
||||
Settings.Startup.AutoUpdateWithSilenceStartTime = "06:00";
|
||||
Settings.Startup.AutoUpdateWithSilenceEndTime = "22:00";
|
||||
Settings.Startup.IsFoldAtStartup = false;
|
||||
}
|
||||
|
||||
@@ -1663,7 +1828,7 @@ namespace Ink_Canvas {
|
||||
private void ToggleSwitchIsEnableEdgeGestureUtil_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Advanced.IsEnableEdgeGestureUtil = ToggleSwitchIsEnableEdgeGestureUtil.IsOn;
|
||||
if (OSVersion.GetOperatingSystem() >= OSVersionExtension.OperatingSystem.Windows10) EdgeGestureUtil.DisableEdgeGestures(new WindowInteropHelper(this).Handle, ToggleSwitchIsEnableEdgeGestureUtil.IsOn);
|
||||
if (OSVersion.GetOperatingSystem() >= OperatingSystem.Windows10) EdgeGestureUtil.DisableEdgeGestures(new WindowInteropHelper(this).Handle, ToggleSwitchIsEnableEdgeGestureUtil.IsOn);
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
@@ -1728,12 +1893,112 @@ namespace Ink_Canvas {
|
||||
Settings.Advanced.IsLogEnabled = ToggleSwitchIsLogEnabled.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchIsSaveLogByDate_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Advanced.IsSaveLogByDate = ToggleSwitchIsSaveLogByDate.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchIsSecondConfimeWhenShutdownApp_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Advanced.IsSecondConfirmWhenShutdownApp = ToggleSwitchIsSecondConfimeWhenShutdownApp.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchIsAutoBackupBeforeUpdate_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Advanced.IsAutoBackupBeforeUpdate = ToggleSwitchIsAutoBackupBeforeUpdate.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void BtnManualBackup_Click(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
|
||||
try {
|
||||
// 确保Backups目录存在
|
||||
string backupDir = Path.Combine(App.RootPath, "Backups");
|
||||
if (!Directory.Exists(backupDir)) {
|
||||
Directory.CreateDirectory(backupDir);
|
||||
LogHelper.WriteLogToFile($"创建备份目录: {backupDir}");
|
||||
}
|
||||
|
||||
// 创建备份文件名(使用当前日期时间)
|
||||
string backupFileName = $"Settings_Backup_{DateTime.Now:yyyyMMdd_HHmmss}.json";
|
||||
string backupPath = Path.Combine(backupDir, backupFileName);
|
||||
|
||||
// 序列化当前设置并保存到备份文件
|
||||
string settingsJson = JsonConvert.SerializeObject(Settings, Formatting.Indented);
|
||||
File.WriteAllText(backupPath, settingsJson);
|
||||
|
||||
LogHelper.WriteLogToFile($"成功创建设置备份: {backupPath}");
|
||||
MessageBox.Show($"设置已成功备份到:\n{backupPath}", "备份成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile($"创建设置备份时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"创建备份失败: {ex.Message}", "备份失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void BtnRestoreBackup_Click(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
|
||||
try {
|
||||
// 确保Backups目录存在
|
||||
string backupDir = Path.Combine(App.RootPath, "Backups");
|
||||
if (!Directory.Exists(backupDir)) {
|
||||
Directory.CreateDirectory(backupDir);
|
||||
LogHelper.WriteLogToFile($"创建备份目录: {backupDir}");
|
||||
MessageBox.Show("没有找到备份文件,请先创建备份", "还原失败", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 打开文件选择对话框
|
||||
OpenFileDialog dlg = new OpenFileDialog();
|
||||
dlg.InitialDirectory = backupDir;
|
||||
dlg.Filter = "设置备份文件|Settings_Backup_*.json|所有JSON文件|*.json";
|
||||
dlg.Title = "选择要还原的备份文件";
|
||||
|
||||
if (dlg.ShowDialog() == true) {
|
||||
// 读取备份文件
|
||||
string backupJson = File.ReadAllText(dlg.FileName);
|
||||
|
||||
// 反序列化备份数据
|
||||
Settings backupSettings = JsonConvert.DeserializeObject<Settings>(backupJson);
|
||||
|
||||
if (backupSettings != null) {
|
||||
// 确认是否要还原
|
||||
if (MessageBox.Show("确定要还原选择的备份文件吗?当前设置将被覆盖。", "确认还原",
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) {
|
||||
|
||||
// 备份当前设置,以防出错
|
||||
string currentSettingsJson = JsonConvert.SerializeObject(Settings, Formatting.Indented);
|
||||
string tempBackupPath = Path.Combine(backupDir, $"Settings_Before_Restore_{DateTime.Now:yyyyMMdd_HHmmss}.json");
|
||||
File.WriteAllText(tempBackupPath, currentSettingsJson);
|
||||
|
||||
// 还原设置
|
||||
Settings = backupSettings;
|
||||
|
||||
// 保存还原后的设置到文件
|
||||
SaveSettingsToFile();
|
||||
|
||||
// 重新加载设置到UI
|
||||
LoadSettings();
|
||||
|
||||
LogHelper.WriteLogToFile($"成功从备份还原设置: {dlg.FileName}");
|
||||
MessageBox.Show("设置已成功还原,部分设置可能需要重启软件后生效。", "还原成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
else {
|
||||
MessageBox.Show("无法解析备份文件,文件可能已损坏", "还原失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile($"还原设置备份时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"还原备份失败: {ex.Message}", "还原失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1773,6 +2038,16 @@ namespace Ink_Canvas {
|
||||
// 保存设置到文件
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchDirectCallCiRand_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
|
||||
// 获取开关状态并保存到设置中
|
||||
Settings.RandSettings.DirectCallCiRand = ToggleSwitchDirectCallCiRand.IsOn;
|
||||
|
||||
// 保存设置到文件
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1794,13 +2069,203 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private void HyperlinkSourceToPresentRepository_Click(object sender, RoutedEventArgs e) {
|
||||
Process.Start("https://github.com/ChangSakura/Ink-Canvas");
|
||||
Process.Start("https://bgithub.xyz/ChangSakura/Ink-Canvas");
|
||||
HideSubPanels();
|
||||
}
|
||||
|
||||
private void HyperlinkSourceToOringinalRepository_Click(object sender, RoutedEventArgs e) {
|
||||
Process.Start("https://github.com/WXRIW/Ink-Canvas");
|
||||
Process.Start("https://bgithub.xyz/WXRIW/Ink-Canvas");
|
||||
HideSubPanels();
|
||||
}
|
||||
|
||||
private async void UpdateChannelSelector_Checked(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
var radioButton = sender as RadioButton;
|
||||
if (radioButton != null) {
|
||||
string channel = radioButton.Tag.ToString();
|
||||
UpdateChannel newChannel = channel == "Beta" ? UpdateChannel.Beta : UpdateChannel.Release;
|
||||
|
||||
// 如果通道没有变化,不需要执行更新检查
|
||||
if (Settings.Startup.UpdateChannel == newChannel) {
|
||||
return;
|
||||
}
|
||||
|
||||
Settings.Startup.UpdateChannel = newChannel;
|
||||
LogHelper.WriteLogToFile($"Settings | Update channel changed to {Settings.Startup.UpdateChannel}");
|
||||
SaveSettingsToFile();
|
||||
|
||||
// 如果启用了自动更新,立即执行完整的检查更新操作
|
||||
if (Settings.Startup.IsAutoUpdate) {
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check");
|
||||
|
||||
// 执行完整的更新检查
|
||||
await Task.Run(async () => {
|
||||
try {
|
||||
// 调用主窗口的AutoUpdate方法,它会自动清除之前的更新状态并使用新通道重新检查
|
||||
Dispatcher.Invoke(() => {
|
||||
AutoUpdate();
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Error during channel switch update check: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, but auto-update is disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void FixVersionButton_Click(object sender, RoutedEventArgs e) {
|
||||
// 显示确认对话框
|
||||
var confirm = MessageBox.Show(
|
||||
"此操作将下载当前选择通道的最新版本并安装,软件将自动关闭并更新。\n\n确定要执行版本修复吗?",
|
||||
"版本修复确认",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
if (confirm == MessageBoxResult.Yes) {
|
||||
// 禁用按钮,避免重复点击
|
||||
FixVersionButton.IsEnabled = false;
|
||||
FixVersionButton.Content = "正在修复...";
|
||||
|
||||
try {
|
||||
// 执行版本修复
|
||||
bool result = await AutoUpdateHelper.FixVersion(Settings.Startup.UpdateChannel);
|
||||
|
||||
if (!result) {
|
||||
MessageBox.Show(
|
||||
"版本修复失败,可能是网络问题或当前已是最新版本。",
|
||||
"修复失败",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
|
||||
// 恢复按钮状态
|
||||
FixVersionButton.IsEnabled = true;
|
||||
FixVersionButton.Content = "版本修复";
|
||||
}
|
||||
// 成功则会自动关闭应用程序并安装
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile($"Error in FixVersionButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show(
|
||||
$"版本修复过程中发生错误: {ex.Message}",
|
||||
"修复错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
|
||||
// 恢复按钮状态
|
||||
FixVersionButton.IsEnabled = true;
|
||||
FixVersionButton.Content = "版本修复";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义点名背景相关方法
|
||||
public void UpdatePickNameBackgroundsInComboBox()
|
||||
{
|
||||
// 清除现有的自定义背景选项
|
||||
if (ComboBoxPickNameBackground != null)
|
||||
{
|
||||
// 保留第一个默认选项
|
||||
while (ComboBoxPickNameBackground.Items.Count > 1)
|
||||
{
|
||||
ComboBoxPickNameBackground.Items.RemoveAt(ComboBoxPickNameBackground.Items.Count - 1);
|
||||
}
|
||||
|
||||
// 添加自定义背景选项
|
||||
foreach (var background in Settings.RandSettings.CustomPickNameBackgrounds)
|
||||
{
|
||||
ComboBoxItem item = new ComboBoxItem();
|
||||
item.Content = background.Name;
|
||||
item.FontFamily = new FontFamily("Microsoft YaHei UI");
|
||||
ComboBoxPickNameBackground.Items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePickNameBackgroundDisplay()
|
||||
{
|
||||
// 此方法主要用于在外部窗口更改背景后更新UI
|
||||
if (ComboBoxPickNameBackground != null)
|
||||
{
|
||||
ComboBoxPickNameBackground.SelectedIndex = Settings.RandSettings.SelectedBackgroundIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void ComboBoxPickNameBackground_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
|
||||
Settings.RandSettings.SelectedBackgroundIndex = ComboBoxPickNameBackground.SelectedIndex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ButtonAddCustomBackground_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AddPickNameBackgroundWindow dialog = new AddPickNameBackgroundWindow(this);
|
||||
dialog.Owner = this;
|
||||
dialog.ShowDialog();
|
||||
|
||||
if (dialog.IsSuccess)
|
||||
{
|
||||
// 自动选中新添加的背景
|
||||
ComboBoxPickNameBackground.SelectedIndex = ComboBoxPickNameBackground.Items.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void ButtonManageBackgrounds_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ManagePickNameBackgroundsWindow dialog = new ManagePickNameBackgroundsWindow(this);
|
||||
dialog.Owner = this;
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void ToggleSwitchEnableWppProcessKill_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.PowerPointSettings.EnableWppProcessKill = ToggleSwitchEnableWppProcessKill.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchClearCanvasAlsoClearImages_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.ClearCanvasAlsoClearImages = ToggleSwitchClearCanvasAlsoClearImages.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchCompressPicturesUploaded_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.IsCompressPicturesUploaded = ToggleSwitchCompressPicturesUploaded.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchAutoFoldAfterPPTSlideShow_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Automation.IsAutoFoldAfterPPTSlideShow = ToggleSwitchAutoFoldAfterPPTSlideShow.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchAlwaysGoToFirstPageOnReenter_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.PowerPointSettings.IsAlwaysGoToFirstPageOnReenter = ToggleSwitchAlwaysGoToFirstPageOnReenter.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchAutoEnterAnnotationAfterKillHite_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Automation.IsAutoEnterAnnotationAfterKillHite = ToggleSwitchAutoEnterAnnotationAfterKillHite.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchEnablePalmEraser_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.EnablePalmEraser = ToggleSwitchEnablePalmEraser.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using Hardcodet.Wpf.TaskbarNotification;
|
||||
using Ink_Canvas.Helpers;
|
||||
using Newtonsoft.Json;
|
||||
using OSVersionExtension;
|
||||
using System;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -10,12 +6,15 @@ using System.Windows.Ink;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
|
||||
using Hardcodet.Wpf.TaskbarNotification;
|
||||
using Ink_Canvas.Helpers;
|
||||
using Newtonsoft.Json;
|
||||
using OSVersionExtension;
|
||||
using File = System.IO.File;
|
||||
using OperatingSystem = OSVersionExtension.OperatingSystem;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : System.Windows.Window {
|
||||
public partial class MainWindow : Window {
|
||||
private void LoadSettings(bool isStartup = false) {
|
||||
AppVersionTextBlock.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
try {
|
||||
@@ -70,16 +69,37 @@ namespace Ink_Canvas {
|
||||
BoundsWidth = Settings.Advanced.FingerModeBoundsWidth;
|
||||
}
|
||||
|
||||
// 设置自动更新相关选项
|
||||
ToggleSwitchIsAutoUpdate.IsOn = Settings.Startup.IsAutoUpdate;
|
||||
|
||||
// 只有在启用了自动更新功能时才检查更新
|
||||
if (Settings.Startup.IsAutoUpdate) {
|
||||
ToggleSwitchIsAutoUpdate.IsOn = true;
|
||||
if (isStartup) {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Running auto-update check at startup");
|
||||
AutoUpdate();
|
||||
}
|
||||
// 当设置被修改时也检查更新(非启动时)
|
||||
else {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Running auto-update check after settings change");
|
||||
AutoUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
// ToggleSwitchIsAutoUpdateWithSilence.Visibility = Settings.Startup.IsAutoUpdate ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (Settings.Startup.IsAutoUpdateWithSilence) {
|
||||
ToggleSwitchIsAutoUpdateWithSilence.IsOn = true;
|
||||
}
|
||||
|
||||
// 初始化更新通道选择
|
||||
foreach (var radioButton in UpdateChannelSelector.Items) {
|
||||
if (radioButton is RadioButton rb) {
|
||||
if (rb.Tag.ToString() == Settings.Startup.UpdateChannel.ToString()) {
|
||||
rb.IsChecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AutoUpdateTimePeriodBlock.Visibility = Settings.Startup.IsAutoUpdateWithSilence
|
||||
? Visibility.Visible
|
||||
: Visibility.Collapsed;
|
||||
@@ -94,6 +114,22 @@ namespace Ink_Canvas {
|
||||
Settings.Startup = new Startup();
|
||||
}
|
||||
|
||||
// 恢复崩溃后操作设置
|
||||
if (Settings.Startup != null)
|
||||
{
|
||||
// 恢复崩溃后操作选项
|
||||
if (Settings.Startup.CrashAction == 0)
|
||||
{
|
||||
App.CrashAction = App.CrashActionType.SilentRestart;
|
||||
if (RadioCrashSilentRestart != null) RadioCrashSilentRestart.IsChecked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
App.CrashAction = App.CrashActionType.NoAction;
|
||||
if (RadioCrashNoAction != null) RadioCrashNoAction.IsChecked = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Appearance
|
||||
if (Settings.Appearance != null) {
|
||||
if (!Settings.Appearance.IsEnableDisPlayNibModeToggler) {
|
||||
@@ -210,41 +246,20 @@ namespace Ink_Canvas {
|
||||
new SolidColorBrush(StringToColor("#FF555555"));
|
||||
}
|
||||
|
||||
ComboBoxFloatingBarImg.SelectedIndex = Settings.Appearance.FloatingBarImg;
|
||||
if (ComboBoxFloatingBarImg.SelectedIndex == 0) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/icc.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(0.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 1) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(
|
||||
new Uri("pack://application:,,,/Resources/Icons-png/icc-transparent-dark-small.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(1.2);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 2) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuandoujiyanhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 3) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanshounvhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 4) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanciya.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 5) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuanneikuhuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 6) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/kuandogeyuanliangwo.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1.5);
|
||||
} else if (ComboBoxFloatingBarImg.SelectedIndex == 7) {
|
||||
FloatingbarHeadIconImg.Source =
|
||||
new BitmapImage(new Uri("pack://application:,,,/Resources/Icons-png/tiebahuaji.png"));
|
||||
FloatingbarHeadIconImg.Margin = new Thickness(2, 2, 2, 1);
|
||||
// 更新自定义图标下拉列表
|
||||
UpdateCustomIconsInComboBox();
|
||||
|
||||
// 设置选中的图标索引
|
||||
// 如果索引超出范围(自定义图标可能已删除),使用默认图标
|
||||
if (Settings.Appearance.FloatingBarImg >= ComboBoxFloatingBarImg.Items.Count)
|
||||
{
|
||||
Settings.Appearance.FloatingBarImg = 0;
|
||||
}
|
||||
|
||||
ComboBoxFloatingBarImg.SelectedIndex = Settings.Appearance.FloatingBarImg;
|
||||
|
||||
// 更新浮动栏图标
|
||||
UpdateFloatingBarIcon();
|
||||
|
||||
ToggleSwitchEnableTimeDisplayInWhiteboardMode.IsOn =
|
||||
Settings.Appearance.EnableTimeDisplayInWhiteboardMode;
|
||||
@@ -359,6 +374,8 @@ namespace Ink_Canvas {
|
||||
|
||||
ToggleSwitchAutoSaveScreenShotInPowerPoint.IsOn =
|
||||
Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint;
|
||||
ToggleSwitchEnableWppProcessKill.IsOn = Settings.PowerPointSettings.EnableWppProcessKill;
|
||||
ToggleSwitchAlwaysGoToFirstPageOnReenter.IsOn = Settings.PowerPointSettings.IsAlwaysGoToFirstPageOnReenter;
|
||||
} else {
|
||||
Settings.PowerPointSettings = new PowerPointSettings();
|
||||
}
|
||||
@@ -448,7 +465,8 @@ namespace Ink_Canvas {
|
||||
BoardComboBoxEraserSize.SelectedIndex = Settings.Canvas.EraserSize;
|
||||
|
||||
ToggleSwitchClearCanvasAndClearTimeMachine.IsOn =
|
||||
Settings.Canvas.ClearCanvasAndClearTimeMachine == true;
|
||||
Settings.Canvas.ClearCanvasAndClearTimeMachine;
|
||||
ToggleSwitchClearCanvasAlsoClearImages.IsOn = Settings.Canvas.ClearCanvasAlsoClearImages;
|
||||
|
||||
switch (Settings.Canvas.EraserShapeType) {
|
||||
case 0: {
|
||||
@@ -464,7 +482,7 @@ namespace Ink_Canvas {
|
||||
k = 1.25;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.8;
|
||||
k = 1.5;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -485,7 +503,7 @@ namespace Ink_Canvas {
|
||||
k = 1.2;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.6;
|
||||
k = 1.5;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -499,66 +517,97 @@ namespace Ink_Canvas {
|
||||
|
||||
ToggleSwitchHideStrokeWhenSelecting.IsOn = Settings.Canvas.HideStrokeWhenSelecting;
|
||||
|
||||
if (Settings.Canvas.FitToCurve) {
|
||||
ToggleSwitchFitToCurve.IsOn = true;
|
||||
drawingAttributes.FitToCurve = true;
|
||||
} else {
|
||||
// 初始化贝塞尔曲线平滑设置
|
||||
if (Settings.Canvas.UseAdvancedBezierSmoothing)
|
||||
{
|
||||
// 如果启用高级贝塞尔平滑,则禁用原来的FitToCurve
|
||||
ToggleSwitchAdvancedBezierSmoothing.IsOn = true;
|
||||
ToggleSwitchFitToCurve.IsOn = false;
|
||||
drawingAttributes.FitToCurve = false;
|
||||
}
|
||||
else if (Settings.Canvas.FitToCurve)
|
||||
{
|
||||
// 如果启用原来的FitToCurve,则禁用高级贝塞尔平滑
|
||||
ToggleSwitchFitToCurve.IsOn = true;
|
||||
ToggleSwitchAdvancedBezierSmoothing.IsOn = false;
|
||||
drawingAttributes.FitToCurve = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 两者都禁用
|
||||
ToggleSwitchFitToCurve.IsOn = false;
|
||||
ToggleSwitchAdvancedBezierSmoothing.IsOn = false;
|
||||
drawingAttributes.FitToCurve = false;
|
||||
}
|
||||
|
||||
// 注释掉新的墨迹平滑性能设置,因为UI控件还没有定义
|
||||
/*
|
||||
// 初始化新的墨迹平滑性能设置
|
||||
ToggleSwitchAsyncInkSmoothing.IsOn = Settings.Canvas.UseAsyncInkSmoothing;
|
||||
ToggleSwitchHardwareAcceleration.IsOn = Settings.Canvas.UseHardwareAcceleration;
|
||||
ComboBoxInkSmoothingQuality.SelectedIndex = Settings.Canvas.InkSmoothingQuality;
|
||||
SliderMaxConcurrentTasks.Value = Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ?
|
||||
Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount;
|
||||
|
||||
// 检查硬件加速支持
|
||||
if (!Helpers.InkSmoothingManager.IsHardwareAccelerationSupported())
|
||||
{
|
||||
ToggleSwitchHardwareAcceleration.IsEnabled = false;
|
||||
// 可以添加提示文本说明硬件加速不可用
|
||||
}
|
||||
*/
|
||||
|
||||
// 初始化直线自动拉直相关设置
|
||||
ToggleSwitchAutoStraightenLine.IsOn = Settings.Canvas.AutoStraightenLine;
|
||||
AutoStraightenLineThresholdSlider.Value = Settings.Canvas.AutoStraightenLineThreshold;
|
||||
// 直线拉直灵敏度也在这里初始化,即使它存储在InkToShape中
|
||||
LineStraightenSensitivitySlider.Value = Settings.InkToShape.LineStraightenSensitivity;
|
||||
// 初始化高精度直线拉直设置
|
||||
ToggleSwitchHighPrecisionLineStraighten.IsOn = Settings.Canvas.HighPrecisionLineStraighten;
|
||||
|
||||
// 初始化直线端点吸附相关设置
|
||||
ToggleSwitchLineEndpointSnapping.IsOn = Settings.Canvas.LineEndpointSnapping;
|
||||
LineEndpointSnappingThresholdSlider.Value = Settings.Canvas.LineEndpointSnappingThreshold;
|
||||
ToggleSwitchCompressPicturesUploaded.IsOn = Settings.Canvas.IsCompressPicturesUploaded;
|
||||
} else {
|
||||
Settings.Canvas = new Canvas();
|
||||
}
|
||||
|
||||
// Palm Eraser
|
||||
if (Settings.Canvas != null) {
|
||||
ToggleSwitchEnablePalmEraser.IsOn = Settings.Canvas.EnablePalmEraser;
|
||||
}
|
||||
|
||||
// Advanced
|
||||
if (Settings.Advanced != null) {
|
||||
TouchMultiplierSlider.Value = Settings.Advanced.TouchMultiplier;
|
||||
FingerModeBoundsWidthSlider.Value = Settings.Advanced.FingerModeBoundsWidth;
|
||||
NibModeBoundsWidthSlider.Value = Settings.Advanced.NibModeBoundsWidth;
|
||||
ToggleSwitchIsLogEnabled.IsOn = Settings.Advanced.IsLogEnabled;
|
||||
|
||||
ToggleSwitchIsSaveLogByDate.IsOn = Settings.Advanced.IsSaveLogByDate;
|
||||
ToggleSwitchIsSecondConfimeWhenShutdownApp.IsOn = Settings.Advanced.IsSecondConfirmWhenShutdownApp;
|
||||
|
||||
ToggleSwitchEraserBindTouchMultiplier.IsOn = Settings.Advanced.EraserBindTouchMultiplier;
|
||||
|
||||
ToggleSwitchIsSpecialScreen.IsOn = Settings.Advanced.IsSpecialScreen;
|
||||
|
||||
TouchMultiplierSlider.Visibility =
|
||||
ToggleSwitchIsSpecialScreen.IsOn ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
ToggleSwitchIsQuadIR.IsOn = Settings.Advanced.IsQuadIR;
|
||||
|
||||
ToggleSwitchEraserBindTouchMultiplier.IsOn = Settings.Advanced.EraserBindTouchMultiplier;
|
||||
ToggleSwitchIsEnableFullScreenHelper.IsOn = Settings.Advanced.IsEnableFullScreenHelper;
|
||||
ToggleSwitchIsEnableEdgeGestureUtil.IsOn = Settings.Advanced.IsEnableEdgeGestureUtil;
|
||||
ToggleSwitchIsEnableForceFullScreen.IsOn = Settings.Advanced.IsEnableForceFullScreen;
|
||||
ToggleSwitchIsEnableResolutionChangeDetection.IsOn = Settings.Advanced.IsEnableResolutionChangeDetection;
|
||||
ToggleSwitchIsEnableDPIChangeDetection.IsOn = Settings.Advanced.IsEnableDPIChangeDetection;
|
||||
ToggleSwitchIsEnableAvoidFullScreenHelper.IsOn = Settings.Advanced.IsEnableAvoidFullScreenHelper;
|
||||
ToggleSwitchIsAutoBackupBeforeUpdate.IsOn = Settings.Advanced.IsAutoBackupBeforeUpdate;
|
||||
if (Settings.Advanced.IsEnableFullScreenHelper) {
|
||||
FullScreenHelper.MarkFullscreenWindowTaskbarList(new WindowInteropHelper(this).Handle, true);
|
||||
}
|
||||
|
||||
ToggleSwitchIsEnableAvoidFullScreenHelper.IsOn = Settings.Advanced.IsEnableAvoidFullScreenHelper;
|
||||
if (Settings.Advanced.IsEnableAvoidFullScreenHelper)
|
||||
{
|
||||
AvoidFullScreenHelper.StartAvoidFullScreen(this);
|
||||
}
|
||||
|
||||
ToggleSwitchIsEnableEdgeGestureUtil.IsOn = Settings.Advanced.IsEnableEdgeGestureUtil;
|
||||
if (Settings.Advanced.IsEnableEdgeGestureUtil) {
|
||||
if (OSVersion.GetOperatingSystem() >= OperatingSystem.Windows10)
|
||||
EdgeGestureUtil.DisableEdgeGestures(new WindowInteropHelper(this).Handle, true);
|
||||
}
|
||||
|
||||
ToggleSwitchIsEnableForceFullScreen.IsOn = Settings.Advanced.IsEnableForceFullScreen;
|
||||
|
||||
ToggleSwitchIsEnableDPIChangeDetection.IsOn = Settings.Advanced.IsEnableDPIChangeDetection;
|
||||
|
||||
ToggleSwitchIsEnableResolutionChangeDetection.IsOn =
|
||||
Settings.Advanced.IsEnableResolutionChangeDetection;
|
||||
TouchMultiplierSlider.Visibility =
|
||||
ToggleSwitchIsSpecialScreen.IsOn ? Visibility.Visible : Visibility.Collapsed;
|
||||
} else {
|
||||
Settings.Advanced = new Advanced();
|
||||
}
|
||||
@@ -579,8 +628,7 @@ namespace Ink_Canvas {
|
||||
|
||||
ToggleCheckboxEnableInkToShapeRounded.IsChecked = Settings.InkToShape.IsInkToShapeRounded;
|
||||
|
||||
// 初始化直线拉直灵敏度
|
||||
LineStraightenSensitivitySlider.Value = Settings.InkToShape.LineStraightenSensitivity;
|
||||
// 直线拉直灵敏度在Canvas部分已经初始化,这里不再重复
|
||||
} else {
|
||||
Settings.InkToShape = new InkToShape();
|
||||
}
|
||||
@@ -591,13 +639,25 @@ namespace Ink_Canvas {
|
||||
RandWindowOnceCloseLatencySlider.Value = Settings.RandSettings.RandWindowOnceCloseLatency;
|
||||
RandWindowOnceMaxStudentsSlider.Value = Settings.RandSettings.RandWindowOnceMaxStudents;
|
||||
ToggleSwitchShowRandomAndSingleDraw.IsOn = Settings.RandSettings.ShowRandomAndSingleDraw;
|
||||
ToggleSwitchDirectCallCiRand.IsOn = Settings.RandSettings.DirectCallCiRand;
|
||||
RandomDrawPanel.Visibility = Settings.RandSettings.ShowRandomAndSingleDraw ? Visibility.Visible : Visibility.Collapsed;
|
||||
SingleDrawPanel.Visibility = Settings.RandSettings.ShowRandomAndSingleDraw ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
// 加载自定义点名背景
|
||||
UpdatePickNameBackgroundsInComboBox();
|
||||
|
||||
// 设置选择的背景索引
|
||||
if (Settings.RandSettings.SelectedBackgroundIndex >= ComboBoxPickNameBackground.Items.Count)
|
||||
{
|
||||
Settings.RandSettings.SelectedBackgroundIndex = 0;
|
||||
}
|
||||
ComboBoxPickNameBackground.SelectedIndex = Settings.RandSettings.SelectedBackgroundIndex;
|
||||
} else {
|
||||
Settings.RandSettings = new RandSettings();
|
||||
ToggleSwitchDisplayRandWindowNamesInputBtn.IsOn = Settings.RandSettings.DisplayRandWindowNamesInputBtn;
|
||||
RandWindowOnceCloseLatencySlider.Value = Settings.RandSettings.RandWindowOnceCloseLatency;
|
||||
RandWindowOnceMaxStudentsSlider.Value = Settings.RandSettings.RandWindowOnceMaxStudents;
|
||||
ToggleSwitchDirectCallCiRand.IsOn = Settings.RandSettings.DirectCallCiRand;
|
||||
}
|
||||
|
||||
// Automation
|
||||
@@ -647,6 +707,8 @@ namespace Ink_Canvas {
|
||||
|
||||
ToggleSwitchAutoFoldInPPTSlideShow.IsOn = Settings.Automation.IsAutoFoldInPPTSlideShow;
|
||||
|
||||
ToggleSwitchAutoFoldAfterPPTSlideShow.IsOn = Settings.Automation.IsAutoFoldAfterPPTSlideShow;
|
||||
|
||||
if (Settings.Automation.IsAutoKillEasiNote || Settings.Automation.IsAutoKillPptService ||
|
||||
Settings.Automation.IsAutoKillHiteAnnotation || Settings.Automation.IsAutoKillInkCanvas
|
||||
|| Settings.Automation.IsAutoKillICA || Settings.Automation.IsAutoKillIDT ||
|
||||
@@ -679,6 +741,8 @@ namespace Ink_Canvas {
|
||||
ToggleSwitchSaveScreenshotsInDateFolders.IsOn = Settings.Automation.IsSaveScreenshotsInDateFolders;
|
||||
|
||||
ToggleSwitchAutoSaveStrokesAtScreenshot.IsOn = Settings.Automation.IsAutoSaveStrokesAtScreenshot;
|
||||
|
||||
ToggleSwitchSaveFullPageStrokes.IsOn = Settings.Automation.IsSaveFullPageStrokes;
|
||||
|
||||
SideControlMinimumAutomationSlider.Value = Settings.Automation.MinimumAutomationStrokeNumber;
|
||||
|
||||
@@ -686,6 +750,9 @@ namespace Ink_Canvas {
|
||||
ToggleSwitchAutoDelSavedFiles.IsOn = Settings.Automation.AutoDelSavedFiles;
|
||||
ComboBoxAutoDelSavedFilesDaysThreshold.Text =
|
||||
Settings.Automation.AutoDelSavedFilesDaysThreshold.ToString();
|
||||
|
||||
// 加载退出收纳模式自动切换至批注模式设置
|
||||
ToggleSwitchAutoEnterAnnotationModeWhenExitFoldMode.IsOn = Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode;
|
||||
} else {
|
||||
Settings.Automation = new Automation();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
@@ -9,6 +8,8 @@ using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using Point = System.Windows.Point;
|
||||
|
||||
@@ -55,8 +56,8 @@ namespace Ink_Canvas {
|
||||
|
||||
#endregion Floating Bar Control
|
||||
|
||||
private int drawingShapeMode = 0;
|
||||
private bool isLongPressSelected = false; // 用于存是否是"选中"状态,便于后期抬笔后不做切换到笔的处理
|
||||
private int drawingShapeMode;
|
||||
private bool isLongPressSelected; // 用于存是否是"选中"状态,便于后期抬笔后不做切换到笔的处理
|
||||
|
||||
#region Buttons
|
||||
|
||||
@@ -66,12 +67,12 @@ namespace Ink_Canvas {
|
||||
ToggleSwitchDrawShapeBorderAutoHide.IsOn = !ToggleSwitchDrawShapeBorderAutoHide.IsOn;
|
||||
|
||||
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn)
|
||||
((iNKORE.UI.WPF.Modern.Controls.SymbolIcon)sender).Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.Pin;
|
||||
((SymbolIcon)sender).Symbol = Symbol.Pin;
|
||||
else
|
||||
((iNKORE.UI.WPF.Modern.Controls.SymbolIcon)sender).Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.UnPin;
|
||||
((SymbolIcon)sender).Symbol = Symbol.UnPin;
|
||||
}
|
||||
|
||||
private object lastMouseDownSender = null;
|
||||
private object lastMouseDownSender;
|
||||
private DateTime lastMouseDownTime = DateTime.MinValue;
|
||||
|
||||
private async void Image_MouseDown(object sender, MouseButtonEventArgs e) {
|
||||
@@ -103,17 +104,36 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private void BtnPen_Click(object sender, RoutedEventArgs e) {
|
||||
// 禁用高级橡皮擦系统
|
||||
DisableAdvancedEraserSystem();
|
||||
|
||||
// 如果当前已是批注模式,再次点击弹出批注子面板
|
||||
if (penType == 0 && inkCanvas.EditingMode == InkCanvasEditingMode.Ink && !drawingAttributes.IsHighlighter) {
|
||||
return;
|
||||
}
|
||||
// 否则只切换到批注模式,不弹出子面板
|
||||
forceEraser = false;
|
||||
forcePointEraser = false;
|
||||
drawingShapeMode = 0;
|
||||
penType = 0;
|
||||
drawingAttributes.IsHighlighter = false;
|
||||
drawingAttributes.StylusTip = StylusTip.Ellipse;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
CancelSingleFingerDragMode();
|
||||
isLongPressSelected = false;
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
}
|
||||
|
||||
private Task<bool> CheckIsDrawingShapesInMultiTouchMode() {
|
||||
if (isInMultiTouchMode) {
|
||||
ToggleSwitchEnableMultiTouchMode.IsOn = false;
|
||||
// 不关闭多指书写模式,而是保存状态,暂时禁用多指书写相关的事件处理
|
||||
// 不再调用 ToggleSwitchEnableMultiTouchMode.IsOn = false;
|
||||
|
||||
// 暂时禁用多指书写事件处理,以避免冲突
|
||||
inkCanvas.StylusDown -= MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove -= MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp -= MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown -= MainWindow_TouchDown;
|
||||
|
||||
// 记录已暂时禁用多指书写模式,但实际上多指书写开关仍然为打开状态
|
||||
lastIsInMultiTouchMode = true;
|
||||
}
|
||||
|
||||
@@ -122,322 +142,247 @@ namespace Ink_Canvas {
|
||||
|
||||
private async void BtnDrawLine_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
if (lastMouseDownSender == sender) {
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 1;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
CancelSingleFingerDragMode();
|
||||
}
|
||||
|
||||
EnterShapeDrawingMode(1);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
if (isLongPressSelected) {
|
||||
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
|
||||
var dA = new DoubleAnimation(1, 1, new Duration(TimeSpan.FromMilliseconds(0)));
|
||||
ImageDrawLine.BeginAnimation(OpacityProperty, dA);
|
||||
}
|
||||
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawDashedLine_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
if (lastMouseDownSender == sender) {
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 8;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
CancelSingleFingerDragMode();
|
||||
}
|
||||
|
||||
EnterShapeDrawingMode(8);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
if (isLongPressSelected) {
|
||||
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
|
||||
var dA = new DoubleAnimation(1, 1, new Duration(TimeSpan.FromMilliseconds(0)));
|
||||
ImageDrawDashedLine.BeginAnimation(OpacityProperty, dA);
|
||||
}
|
||||
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawDotLine_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
if (lastMouseDownSender == sender) {
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 18;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
CancelSingleFingerDragMode();
|
||||
}
|
||||
|
||||
EnterShapeDrawingMode(18);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
if (isLongPressSelected) {
|
||||
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
|
||||
var dA = new DoubleAnimation(1, 1, new Duration(TimeSpan.FromMilliseconds(0)));
|
||||
ImageDrawDotLine.BeginAnimation(OpacityProperty, dA);
|
||||
}
|
||||
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawArrow_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
if (lastMouseDownSender == sender) {
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 2;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
CancelSingleFingerDragMode();
|
||||
}
|
||||
|
||||
EnterShapeDrawingMode(2);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
if (isLongPressSelected) {
|
||||
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
|
||||
var dA = new DoubleAnimation(1, 1, new Duration(TimeSpan.FromMilliseconds(0)));
|
||||
ImageDrawArrow.BeginAnimation(OpacityProperty, dA);
|
||||
}
|
||||
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawParallelLine_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
if (lastMouseDownSender == sender) {
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 15;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
CancelSingleFingerDragMode();
|
||||
}
|
||||
|
||||
EnterShapeDrawingMode(15);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
if (isLongPressSelected) {
|
||||
if (ToggleSwitchDrawShapeBorderAutoHide.IsOn) CollapseBorderDrawShape();
|
||||
var dA = new DoubleAnimation(1, 1, new Duration(TimeSpan.FromMilliseconds(0)));
|
||||
ImageDrawParallelLine.BeginAnimation(OpacityProperty, dA);
|
||||
}
|
||||
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCoordinate1_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 11;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(11);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCoordinate2_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 12;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(12);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCoordinate3_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 13;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(13);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCoordinate4_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 14;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(14);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCoordinate5_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 17;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(17);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawRectangle_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 3;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(3);
|
||||
CancelSingleFingerDragMode();
|
||||
isLongPressSelected = true; // 设置为选中状态,避免抬笔后切换回笔模式
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawRectangleCenter_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 19;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(19);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawEllipse_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 4;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(4);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCircle_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 5;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(5);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCenterEllipse_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 16;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(16);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCenterEllipseWithFocalPoint_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 23;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(23);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawDashedCircle_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 10;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(10);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawHyperbola_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 24;
|
||||
EnterShapeDrawingMode(24);
|
||||
drawMultiStepShapeCurrentStep = 0;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawHyperbolaWithFocalPoint_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 25;
|
||||
EnterShapeDrawingMode(25);
|
||||
drawMultiStepShapeCurrentStep = 0;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawParabola1_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 20;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(20);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawParabolaWithFocalPoint_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 22;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(22);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawParabola2_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 21;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(21);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCylinder_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 6;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(6);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCone_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 7;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
EnterShapeDrawingMode(7);
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
private async void BtnDrawCuboid_Click(object sender, MouseButtonEventArgs e) {
|
||||
await CheckIsDrawingShapesInMultiTouchMode();
|
||||
forceEraser = true;
|
||||
drawingShapeMode = 9;
|
||||
EnterShapeDrawingMode(9);
|
||||
isFirstTouchCuboid = true;
|
||||
CuboidFrontRectIniP = new Point();
|
||||
CuboidFrontRectEndP = new Point();
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
inkCanvas.IsManipulationEnabled = true;
|
||||
CancelSingleFingerDragMode();
|
||||
lastMouseDownSender = null;
|
||||
DrawShapePromptToPen();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void inkCanvas_TouchMove(object sender, TouchEventArgs e) {
|
||||
if (isSingleFingerDragMode) return;
|
||||
// 确保套索选择模式下触摸移动时光标保持可见
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.Select) {
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
}
|
||||
|
||||
// 处理形状绘制模式
|
||||
// 处理几何绘制模式
|
||||
if (drawingShapeMode != 0) {
|
||||
if (isLastTouchEraser) return;
|
||||
//EraserContainer.Background = null;
|
||||
//ImageEraser.Visibility = Visibility.Visible;
|
||||
if (isWaitUntilNextTouchDown) return;
|
||||
if (isWaitUntilNextTouchDown && dec.Count > 1) return;
|
||||
if (dec.Count > 1) {
|
||||
isWaitUntilNextTouchDown = true;
|
||||
try {
|
||||
inkCanvas.Strokes.Remove(lastTempStroke);
|
||||
inkCanvas.Strokes.Remove(lastTempStrokeCollection);
|
||||
@@ -447,94 +392,33 @@ namespace Ink_Canvas {
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 在几何绘制模式下,确保处理单点触控的移动事件
|
||||
Point touchPoint = e.GetTouchPoint(inkCanvas).Position;
|
||||
MouseTouchMove(touchPoint);
|
||||
return; // 处理完几何绘制后直接返回,不执行后面的代码
|
||||
}
|
||||
|
||||
// 触摸移动时保持自定义光标显示
|
||||
if (Settings.Canvas.IsShowCursor) {
|
||||
inkCanvas.ForceCursor = true;
|
||||
inkCanvas.UseCustomCursor = true; // 确保使用自定义光标
|
||||
System.Windows.Forms.Cursor.Show();
|
||||
}
|
||||
|
||||
if (NeedUpdateIniP()) iniP = e.GetTouchPoint(inkCanvas).Position;
|
||||
if (drawingShapeMode == 9 && isFirstTouchCuboid == false) MouseTouchMove(iniP);
|
||||
inkCanvas.Opacity = 1;
|
||||
double boundsWidth = GetTouchBoundWidth(e), eraserMultiplier = 1.0;
|
||||
if (!Settings.Advanced.EraserBindTouchMultiplier && Settings.Advanced.IsSpecialScreen)
|
||||
eraserMultiplier = 1 / Settings.Advanced.TouchMultiplier;
|
||||
if (boundsWidth > BoundsWidth) {
|
||||
isLastTouchEraser = true;
|
||||
if (drawingShapeMode == 0 && forceEraser) return;
|
||||
if (boundsWidth > BoundsWidth * 2.5) {
|
||||
double k = 1;
|
||||
switch (Settings.Canvas.EraserSize) {
|
||||
case 0:
|
||||
k = 0.5;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.8;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.25;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.8;
|
||||
break;
|
||||
}
|
||||
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(boundsWidth * k * eraserMultiplier,
|
||||
boundsWidth * k * eraserMultiplier);
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
|
||||
// 立即应用光标设置
|
||||
if (Settings.Canvas.IsShowCursor) {
|
||||
inkCanvas.Cursor = Cursors.Cross;
|
||||
System.Windows.Forms.Cursor.Show();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (StackPanelPPTControls.Visibility == Visibility.Visible && inkCanvas.Strokes.Count == 0 &&
|
||||
Settings.PowerPointSettings.IsEnableFingerGestureSlideShowControl) {
|
||||
isLastTouchEraser = false;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.GestureOnly;
|
||||
inkCanvas.Opacity = 0.1;
|
||||
}
|
||||
else {
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(5, 5);
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
isLastTouchEraser = false;
|
||||
// 修复面积擦时不显示橡皮形状:无论 forcePointEraser 状态,均显示 50x50 橡皮
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(50, 50);
|
||||
if (forceEraser) return;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
}
|
||||
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.None)
|
||||
return;
|
||||
|
||||
if (e.TouchDevice == null) {
|
||||
System.Windows.Forms.Cursor.Show();
|
||||
} else {
|
||||
System.Windows.Forms.Cursor.Hide();
|
||||
// 其它模式下,允许橡皮、套索、批注等正常工作,不覆盖EditingMode
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint ||
|
||||
inkCanvas.EditingMode == InkCanvasEditingMode.Select ||
|
||||
inkCanvas.EditingMode == InkCanvasEditingMode.Ink) {
|
||||
// 允许正常橡皮、套索、批注
|
||||
}
|
||||
}
|
||||
|
||||
private int drawMultiStepShapeCurrentStep = 0; //多笔完成的图形 当前所处在的笔画
|
||||
private int drawMultiStepShapeCurrentStep; //多笔完成的图形 当前所处在的笔画
|
||||
|
||||
private StrokeCollection drawMultiStepShapeSpecialStrokeCollection = new StrokeCollection(); //多笔完成的图形 当前所处在的笔画
|
||||
|
||||
//double drawMultiStepShapeSpecialParameter1 = 0.0; //多笔完成的图形 特殊参数 通常用于表示a
|
||||
//double drawMultiStepShapeSpecialParameter2 = 0.0; //多笔完成的图形 特殊参数 通常用于表示b
|
||||
private double drawMultiStepShapeSpecialParameter3 = 0.0; //多笔完成的图形 特殊参数 通常用于表示k
|
||||
private double drawMultiStepShapeSpecialParameter3; //多笔完成的图形 特殊参数 通常用于表示k
|
||||
|
||||
#region 形状绘制主函数
|
||||
|
||||
private void MouseTouchMove(Point endP) {
|
||||
if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = false;
|
||||
// 禁用原有的FitToCurve,使用新的高级贝塞尔曲线平滑
|
||||
if (Settings.Canvas.FitToCurve) drawingAttributes.FitToCurve = false;
|
||||
ViewboxFloatingBar.IsHitTestVisible = false;
|
||||
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
|
||||
List<Point> pointList;
|
||||
@@ -1157,7 +1041,7 @@ namespace Ink_Canvas {
|
||||
strokes.Add(stroke.Clone());
|
||||
//底部椭圆
|
||||
pointList = GenerateEllipseGeometry(new Point(newIniP.X, endP.Y - topB / 2),
|
||||
new Point(endP.X, endP.Y + topB / 2), false, true);
|
||||
new Point(endP.X, endP.Y + topB / 2), false);
|
||||
point = new StylusPointCollection(pointList);
|
||||
stroke = new Stroke(point) {
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
@@ -1206,7 +1090,7 @@ namespace Ink_Canvas {
|
||||
var bottomB = bottomA / 2.646;
|
||||
//底部椭圆
|
||||
pointList = GenerateEllipseGeometry(new Point(newIniP.X, endP.Y - bottomB / 2),
|
||||
new Point(endP.X, endP.Y + bottomB / 2), false, true);
|
||||
new Point(endP.X, endP.Y + bottomB / 2), false);
|
||||
point = new StylusPointCollection(pointList);
|
||||
stroke = new Stroke(point) {
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
@@ -1337,23 +1221,13 @@ namespace Ink_Canvas {
|
||||
#endregion
|
||||
|
||||
private bool isFirstTouchCuboid = true;
|
||||
private Point CuboidFrontRectIniP = new Point();
|
||||
private Point CuboidFrontRectEndP = new Point();
|
||||
private Point CuboidFrontRectIniP;
|
||||
private Point CuboidFrontRectEndP;
|
||||
|
||||
private void Main_Grid_TouchUp(object sender, TouchEventArgs e) {
|
||||
|
||||
inkCanvas.ReleaseAllTouchCaptures();
|
||||
ViewboxFloatingBar.IsHitTestVisible = true;
|
||||
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
|
||||
|
||||
inkCanvas_MouseUp(sender, null);
|
||||
if (dec.Count == 0) isWaitUntilNextTouchDown = false;
|
||||
}
|
||||
|
||||
private Stroke lastTempStroke = null;
|
||||
private Stroke lastTempStroke;
|
||||
private StrokeCollection lastTempStrokeCollection = new StrokeCollection();
|
||||
|
||||
private bool isWaitUntilNextTouchDown = false;
|
||||
private bool isWaitUntilNextTouchDown;
|
||||
|
||||
private List<Point> GenerateEllipseGeometry(Point st, Point ed, bool isDrawTop = true,
|
||||
bool isDrawBottom = true) {
|
||||
@@ -1503,7 +1377,7 @@ namespace Ink_Canvas {
|
||||
return strokes;
|
||||
}
|
||||
|
||||
private bool isMouseDown = false;
|
||||
private bool isMouseDown;
|
||||
|
||||
private void inkCanvas_MouseDown(object sender, MouseButtonEventArgs e) {
|
||||
inkCanvas.CaptureMouse();
|
||||
@@ -1537,17 +1411,37 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
if (lastIsInMultiTouchMode) {
|
||||
ToggleSwitchEnableMultiTouchMode.IsOn = true;
|
||||
// 不再重新启用开关,而是恢复多指书写相关的事件处理
|
||||
// ToggleSwitchEnableMultiTouchMode.IsOn = true;
|
||||
|
||||
// 恢复多指书写事件处理
|
||||
inkCanvas.StylusDown += MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove += MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp += MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown += MainWindow_TouchDown;
|
||||
|
||||
lastIsInMultiTouchMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 修改此处逻辑,确保在正确的情况下才切换回笔模式
|
||||
if (drawingShapeMode != 9 && drawingShapeMode != 0 && drawingShapeMode != 24 && drawingShapeMode != 25) {
|
||||
if (isLongPressSelected) { }
|
||||
if (isLongPressSelected) {
|
||||
// 如果是长按选中的情况,保持图形模式,不做任何切换
|
||||
isWaitUntilNextTouchDown = true; // 保持当前绘图模式直到下一次触摸
|
||||
}
|
||||
else {
|
||||
BtnPen_Click(null, null); //画完一次还原到笔模式
|
||||
if (lastIsInMultiTouchMode) {
|
||||
ToggleSwitchEnableMultiTouchMode.IsOn = true;
|
||||
// 不再重新启用开关,而是恢复多指书写相关的事件处理
|
||||
// ToggleSwitchEnableMultiTouchMode.IsOn = true;
|
||||
|
||||
// 恢复多指书写事件处理
|
||||
inkCanvas.StylusDown += MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove += MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp += MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown += MainWindow_TouchDown;
|
||||
|
||||
lastIsInMultiTouchMode = false;
|
||||
}
|
||||
}
|
||||
@@ -1573,7 +1467,15 @@ namespace Ink_Canvas {
|
||||
else {
|
||||
BtnPen_Click(null, null); //画完还原到笔模式
|
||||
if (lastIsInMultiTouchMode) {
|
||||
ToggleSwitchEnableMultiTouchMode.IsOn = true;
|
||||
// 不再重新启用开关,而是恢复多指书写相关的事件处理
|
||||
// ToggleSwitchEnableMultiTouchMode.IsOn = true;
|
||||
|
||||
// 恢复多指书写事件处理
|
||||
inkCanvas.StylusDown += MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove += MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp += MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown += MainWindow_TouchDown;
|
||||
|
||||
lastIsInMultiTouchMode = false;
|
||||
}
|
||||
|
||||
@@ -1619,7 +1521,15 @@ namespace Ink_Canvas {
|
||||
|
||||
BtnPen_Click(null, null); //画完还原到笔模式
|
||||
if (lastIsInMultiTouchMode) {
|
||||
ToggleSwitchEnableMultiTouchMode.IsOn = true;
|
||||
// 不再重新启用开关,而是恢复多指书写相关的事件处理
|
||||
// ToggleSwitchEnableMultiTouchMode.IsOn = true;
|
||||
|
||||
// 恢复多指书写事件处理
|
||||
inkCanvas.StylusDown += MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove += MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp += MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown += MainWindow_TouchDown;
|
||||
|
||||
lastIsInMultiTouchMode = false;
|
||||
}
|
||||
}
|
||||
@@ -1637,7 +1547,7 @@ namespace Ink_Canvas {
|
||||
StrokeCollection collection = null;
|
||||
if (lastTempStrokeCollection != null && lastTempStrokeCollection.Count > 0)
|
||||
collection = lastTempStrokeCollection;
|
||||
else if (lastTempStroke != null) collection = new StrokeCollection() { lastTempStroke };
|
||||
else if (lastTempStroke != null) collection = new StrokeCollection { lastTempStroke };
|
||||
if (collection != null) timeMachine.CommitStrokeUserInputHistory(collection);
|
||||
}
|
||||
|
||||
@@ -1664,7 +1574,32 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = true;
|
||||
// 应用高级贝塞尔曲线平滑
|
||||
if (Settings.Canvas.UseAdvancedBezierSmoothing)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 对临时笔画应用平滑
|
||||
if (lastTempStroke != null && _inkSmoothingManager != null)
|
||||
{
|
||||
var smoothedStroke = _inkSmoothingManager.SmoothStroke(lastTempStroke);
|
||||
if (smoothedStroke != lastTempStroke)
|
||||
{
|
||||
inkCanvas.Strokes.Remove(lastTempStroke);
|
||||
lastTempStroke = smoothedStroke;
|
||||
inkCanvas.Strokes.Add(smoothedStroke);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"形状绘制高级贝塞尔曲线平滑失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else if (Settings.Canvas.FitToCurve)
|
||||
{
|
||||
drawingAttributes.FitToCurve = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool NeedUpdateIniP() {
|
||||
@@ -1696,5 +1631,14 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 在MainWindow类中添加:
|
||||
private void EnterShapeDrawingMode(int mode) {
|
||||
forceEraser = true;
|
||||
forcePointEraser = false;
|
||||
drawingShapeMode = mode;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,28 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using Ink_Canvas.Helpers;
|
||||
using Point = System.Windows.Point;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
private StrokeCollection newStrokes = new StrokeCollection();
|
||||
private List<Circle> circles = new List<Circle>();
|
||||
private const double SNAP_THRESHOLD = 15.0; // Distance threshold for endpoint snapping
|
||||
private const double LINE_STRAIGHTEN_THRESHOLD = 0.10; // 降低阈值,让直线检测更严格
|
||||
private const double LINE_STRAIGHTEN_THRESHOLD = 0.20; // 默认灵敏度阈值,与UI默认值对应
|
||||
|
||||
private void inkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e) {
|
||||
if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = false;
|
||||
// 标记是否进行了直线拉直
|
||||
bool wasStraightened = false;
|
||||
|
||||
// 禁用原有的FitToCurve,使用新的高级贝塞尔曲线平滑
|
||||
if (Settings.Canvas.FitToCurve) drawingAttributes.FitToCurve = false;
|
||||
|
||||
try {
|
||||
inkCanvas.Opacity = 1;
|
||||
@@ -118,27 +123,40 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
// Apply line straightening and endpoint snapping if ink-to-shape is enabled
|
||||
if (Settings.InkToShape.IsInkToShapeEnabled) {
|
||||
// Check if this stroke could be a straight line
|
||||
if (IsPotentialStraightLine(e.Stroke)) {
|
||||
// Apply line straightening and endpoint snapping if ink-to-shape is enabled
|
||||
|
||||
if (Settings.InkToShape.IsInkToShapeEnabled) {
|
||||
// 检查是否启用了直线自动拉直功能
|
||||
if (Settings.Canvas.AutoStraightenLine && IsPotentialStraightLine(e.Stroke)) {
|
||||
// Get start and end points of the stroke
|
||||
Point startPoint = e.Stroke.StylusPoints[0].ToPoint();
|
||||
Point endPoint = e.Stroke.StylusPoints[e.Stroke.StylusPoints.Count - 1].ToPoint();
|
||||
|
||||
// Try to snap endpoints to existing strokes
|
||||
bool snapped = false;
|
||||
if (Settings.InkToShape.IsInkToShapeRectangle || Settings.InkToShape.IsInkToShapeTriangle) {
|
||||
Point[] snappedPoints = GetSnappedEndpoints(startPoint, endPoint);
|
||||
if (snappedPoints != null) {
|
||||
startPoint = snappedPoints[0];
|
||||
endPoint = snappedPoints[1];
|
||||
snapped = true;
|
||||
// 先完成所有直线判定,再考虑端点吸附
|
||||
// 读取实际的灵敏度设置值
|
||||
double sensitivity = Settings.InkToShape.LineStraightenSensitivity;
|
||||
Debug.WriteLine($"当前灵敏度值: {sensitivity}");
|
||||
|
||||
// 判断是否应该拉直线条
|
||||
bool shouldStraighten = ShouldStraightenLine(e.Stroke);
|
||||
|
||||
// 输出一些调试信息,帮助理解灵敏度设置的效果
|
||||
Debug.WriteLine($"LineStraightenSensitivity: {Settings.InkToShape.LineStraightenSensitivity}, ShouldStraighten: {shouldStraighten}");
|
||||
|
||||
// 只有当确定要拉直线条时,才检查端点吸附
|
||||
if (shouldStraighten && Settings.Canvas.LineEndpointSnapping) {
|
||||
// 只有在启用了形状识别(矩形或三角形)时才执行端点吸附
|
||||
if (Settings.InkToShape.IsInkToShapeRectangle || Settings.InkToShape.IsInkToShapeTriangle) {
|
||||
Point[] snappedPoints = GetSnappedEndpoints(startPoint, endPoint);
|
||||
if (snappedPoints != null) {
|
||||
startPoint = snappedPoints[0];
|
||||
endPoint = snappedPoints[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create straight line stroke
|
||||
if (snapped || ShouldStraightenLine(e.Stroke)) {
|
||||
// 如果确定要拉直,则创建直线
|
||||
if (shouldStraighten) {
|
||||
StylusPointCollection straightLinePoints = CreateStraightLine(startPoint, endPoint);
|
||||
Stroke straightStroke = new Stroke(straightLinePoints) {
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
@@ -157,6 +175,8 @@ namespace Ink_Canvas {
|
||||
newStrokes.Remove(e.Stroke);
|
||||
newStrokes.Add(straightStroke);
|
||||
}
|
||||
|
||||
wasStraightened = true; // 标记已进行直线拉直
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,7 +209,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
if (result.InkDrawingNode.GetShapeName() == "Circle" &&
|
||||
Settings.InkToShape.IsInkToShapeRounded == true) {
|
||||
Settings.InkToShape.IsInkToShapeRounded) {
|
||||
var shape = result.InkDrawingNode.GetShape();
|
||||
if (shape.Width > 75) {
|
||||
foreach (var circle in circles)
|
||||
@@ -245,7 +265,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
else if (result.InkDrawingNode.GetShapeName().Contains("Ellipse") &&
|
||||
Settings.InkToShape.IsInkToShapeRounded == true) {
|
||||
Settings.InkToShape.IsInkToShapeRounded) {
|
||||
var shape = result.InkDrawingNode.GetShape();
|
||||
//var shape1 = result.InkDrawingNode.GetShape();
|
||||
//shape1.Fill = Brushes.Gray;
|
||||
@@ -315,14 +335,14 @@ namespace Ink_Canvas {
|
||||
inkCanvas.Strokes.Remove(result.InkDrawingNode.Strokes);
|
||||
newStrokes = new StrokeCollection();
|
||||
|
||||
var _pointList = GenerateEllipseGeometry(iniP, endP, false, true);
|
||||
var _pointList = GenerateEllipseGeometry(iniP, endP, false);
|
||||
var _point = new StylusPointCollection(_pointList);
|
||||
var _stroke = new Stroke(_point) {
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
};
|
||||
var _dashedLineStroke =
|
||||
GenerateDashedLineEllipseStrokeCollection(iniP, endP, true, false);
|
||||
var strokes = new StrokeCollection() {
|
||||
var strokes = new StrokeCollection {
|
||||
_stroke,
|
||||
_dashedLineStroke
|
||||
};
|
||||
@@ -379,7 +399,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
else if (result.InkDrawingNode.GetShapeName().Contains("Triangle") &&
|
||||
Settings.InkToShape.IsInkToShapeTriangle == true) {
|
||||
Settings.InkToShape.IsInkToShapeTriangle) {
|
||||
var shape = result.InkDrawingNode.GetShape();
|
||||
var p = result.InkDrawingNode.HotPoints;
|
||||
if ((Math.Max(Math.Max(p[0].X, p[1].X), p[2].X) -
|
||||
@@ -418,7 +438,7 @@ namespace Ink_Canvas {
|
||||
result.InkDrawingNode.GetShapeName().Contains("Parallelogram") ||
|
||||
result.InkDrawingNode.GetShapeName().Contains("Square") ||
|
||||
result.InkDrawingNode.GetShapeName().Contains("Trapezoid")) &&
|
||||
Settings.InkToShape.IsInkToShapeRectangle == true) {
|
||||
Settings.InkToShape.IsInkToShapeRectangle) {
|
||||
var shape = result.InkDrawingNode.GetShape();
|
||||
var p = result.InkDrawingNode.HotPoints;
|
||||
if ((Math.Max(Math.Max(Math.Max(p[0].X, p[1].X), p[2].X), p[3].X) -
|
||||
@@ -495,7 +515,7 @@ namespace Ink_Canvas {
|
||||
var speed = GetPointSpeed(e.Stroke.StylusPoints[Math.Max(i - 1, 0)].ToPoint(),
|
||||
e.Stroke.StylusPoints[i].ToPoint(),
|
||||
e.Stroke.StylusPoints[Math.Min(i + 1, n)].ToPoint());
|
||||
s += speed.ToString() + "\t";
|
||||
s += speed + "\t";
|
||||
var point = new StylusPoint();
|
||||
if (speed >= 0.25)
|
||||
point.PressureFactor = (float)(0.5 - 0.3 * (Math.Min(speed, 1.5) - 0.3) / 1.2);
|
||||
@@ -561,7 +581,73 @@ namespace Ink_Canvas {
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = true;
|
||||
// 应用高级贝塞尔曲线平滑(仅在未进行直线拉直时)
|
||||
if (Settings.Canvas.UseAdvancedBezierSmoothing && !wasStraightened)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查原始笔画是否仍然存在于画布中
|
||||
if (inkCanvas.Strokes.Contains(e.Stroke))
|
||||
{
|
||||
// 使用新的异步墨迹平滑管理器
|
||||
if (Settings.Canvas.UseAsyncInkSmoothing && _inkSmoothingManager != null)
|
||||
{
|
||||
// 异步处理
|
||||
_ = ProcessStrokeAsync(e.Stroke);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 同步处理(向后兼容)
|
||||
var smoothedStroke = _inkSmoothingManager?.SmoothStroke(e.Stroke) ?? e.Stroke;
|
||||
|
||||
if (smoothedStroke != e.Stroke)
|
||||
{
|
||||
// 替换原始笔画
|
||||
SetNewBackupOfStroke();
|
||||
_currentCommitType = CommitReason.ShapeRecognition;
|
||||
inkCanvas.Strokes.Remove(e.Stroke);
|
||||
inkCanvas.Strokes.Add(smoothedStroke);
|
||||
_currentCommitType = CommitReason.UserInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 如果高级平滑失败,回退到原始笔画
|
||||
Debug.WriteLine($"高级贝塞尔曲线平滑失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else if (Settings.Canvas.FitToCurve && !wasStraightened)
|
||||
{
|
||||
drawingAttributes.FitToCurve = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步处理笔画平滑
|
||||
/// </summary>
|
||||
private async Task ProcessStrokeAsync(Stroke originalStroke)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _inkSmoothingManager.SmoothStrokeAsync(originalStroke, (original, smoothed) =>
|
||||
{
|
||||
// 在UI线程上执行笔画替换
|
||||
if (inkCanvas.Strokes.Contains(original) && smoothed != original)
|
||||
{
|
||||
SetNewBackupOfStroke();
|
||||
_currentCommitType = CommitReason.ShapeRecognition;
|
||||
inkCanvas.Strokes.Remove(original);
|
||||
inkCanvas.Strokes.Add(smoothed);
|
||||
_currentCommitType = CommitReason.UserInput;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"异步墨迹平滑失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// New method: Checks if a stroke is potentially a straight line
|
||||
@@ -573,15 +659,31 @@ namespace Ink_Canvas {
|
||||
Point start = stroke.StylusPoints.First().ToPoint();
|
||||
Point end = stroke.StylusPoints.Last().ToPoint();
|
||||
double lineLength = GetDistance(start, end);
|
||||
|
||||
// 线条必须足够长才考虑拉直,至少30像素
|
||||
if (lineLength < 30)
|
||||
// 分辨率自适应阈值
|
||||
double adaptiveThreshold = Settings.Canvas.AutoStraightenLineThreshold * GetResolutionScale();
|
||||
// 线条必须足够长才考虑拉直,使用自适应阈值
|
||||
if (lineLength < adaptiveThreshold)
|
||||
return false;
|
||||
|
||||
// 获取用户设置的灵敏度值
|
||||
// 获取用户设置的灵敏度值,确保使用正确的设置
|
||||
double sensitivity = Settings.InkToShape.LineStraightenSensitivity;
|
||||
// 快速检查使用略宽松的阈值
|
||||
double quickThreshold = Math.Min(sensitivity * 1.5, 0.20);
|
||||
|
||||
// 输出当前灵敏度值(调试用)
|
||||
Debug.WriteLine($"IsPotentialStraightLine - sensitivity: {sensitivity}, length: {lineLength}");
|
||||
|
||||
// 根据灵敏度调整快速检查阈值
|
||||
double quickThreshold;
|
||||
|
||||
// 如果灵敏度超过1.0,使用更宽松的快速检查标准
|
||||
if (sensitivity > 1.0) {
|
||||
// 高灵敏度模式 - 使用更宽松的阈值
|
||||
quickThreshold = Math.Min(0.2 + (sensitivity - 1.0) * 0.3, 0.5); // 映射到0.2-0.5范围
|
||||
} else {
|
||||
// 常规灵敏度模式
|
||||
quickThreshold = Math.Min(sensitivity * 1.5, 0.20);
|
||||
}
|
||||
|
||||
Debug.WriteLine($"使用快速检查阈值: {quickThreshold}");
|
||||
|
||||
// 快速检查:计算几个关键点与直线的距离
|
||||
if (stroke.StylusPoints.Count >= 10) {
|
||||
@@ -598,12 +700,27 @@ namespace Ink_Canvas {
|
||||
double midDeviation = DistanceFromLineToPoint(start, end, midPoint);
|
||||
double threeQuarterDeviation = DistanceFromLineToPoint(start, end, threeQuarterPoint);
|
||||
|
||||
// 如果任一点偏离太大,直接排除
|
||||
// 使用相对偏差:偏差与线长的比例,并使用灵敏度进行调整
|
||||
double quickRelativeThreshold = lineLength * quickThreshold;
|
||||
if (quarterDeviation > quickRelativeThreshold ||
|
||||
midDeviation > quickRelativeThreshold ||
|
||||
threeQuarterDeviation > quickRelativeThreshold) {
|
||||
return false;
|
||||
|
||||
// 记录检测到的偏差(调试用)
|
||||
Debug.WriteLine($"Deviations: q={quarterDeviation}, m={midDeviation}, tq={threeQuarterDeviation}, threshold={quickRelativeThreshold}");
|
||||
|
||||
// 如果灵敏度超过1.5,则即使有一个点满足条件也认为可能是直线
|
||||
if (sensitivity > 1.5) {
|
||||
// 超高灵敏度模式:只要有一个关键点偏差小,就认为可能是直线
|
||||
if (quarterDeviation <= quickRelativeThreshold ||
|
||||
midDeviation <= quickRelativeThreshold ||
|
||||
threeQuarterDeviation <= quickRelativeThreshold) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// 常规判断:如果任一点偏离太大,直接排除
|
||||
if (quarterDeviation > quickRelativeThreshold ||
|
||||
midDeviation > quickRelativeThreshold ||
|
||||
threeQuarterDeviation > quickRelativeThreshold) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,69 +729,254 @@ namespace Ink_Canvas {
|
||||
|
||||
// New method: Determines if a stroke should be straightened into a line
|
||||
private bool ShouldStraightenLine(Stroke stroke) {
|
||||
// Basic implementation: check if points roughly follow a straight line
|
||||
Point start = stroke.StylusPoints.First().ToPoint();
|
||||
Point end = stroke.StylusPoints.Last().ToPoint();
|
||||
|
||||
// Calculate max deviation from the straight line between start and end
|
||||
double maxDeviation = 0;
|
||||
double lineLength = GetDistance(start, end);
|
||||
|
||||
// 如果线条太短,不进行拉直处理
|
||||
if (lineLength < 50) {
|
||||
// 分辨率自适应阈值
|
||||
double adaptiveThreshold = Settings.Canvas.AutoStraightenLineThreshold * GetResolutionScale();
|
||||
// 如果线条太短,不进行拉直处理,使用自适应阈值
|
||||
if (lineLength < adaptiveThreshold) {
|
||||
// 显示调试信息 - 线条长度不足
|
||||
// MessageBox.Show($"线条太短: {lineLength} < {Settings.Canvas.AutoStraightenLineThreshold}", "调试信息");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取用户设置的灵敏度值
|
||||
// 获取用户设置的灵敏度值,确保使用正确的值进行后续判断
|
||||
double sensitivity = Settings.InkToShape.LineStraightenSensitivity;
|
||||
|
||||
// 输出详细的调试信息
|
||||
Debug.WriteLine($"ShouldStraightenLine - sensitivity: {sensitivity}, length: {lineLength}");
|
||||
|
||||
// 临时:显示调试消息框
|
||||
// MessageBox.Show($"灵敏度值: {sensitivity}", "调试信息");
|
||||
|
||||
// 计算点与直线的偏差
|
||||
double totalDeviation = 0;
|
||||
int pointCount = 0;
|
||||
|
||||
// Calculate deviation for each point
|
||||
foreach (StylusPoint sp in stroke.StylusPoints) {
|
||||
Point p = sp.ToPoint();
|
||||
double deviation = DistanceFromLineToPoint(start, end, p);
|
||||
maxDeviation = Math.Max(maxDeviation, deviation);
|
||||
totalDeviation += deviation;
|
||||
pointCount++;
|
||||
// 检查是否启用了高精度直线拉直
|
||||
bool useHighPrecision = Settings.Canvas.HighPrecisionLineStraighten;
|
||||
|
||||
if (useHighPrecision) {
|
||||
Debug.WriteLine("使用高精度直线拉直模式");
|
||||
|
||||
// 高精度模式:每隔10像素取一个计数点
|
||||
double strokeLength = 0;
|
||||
double sampleInterval = 10.0; // 10像素间隔
|
||||
|
||||
// 计算笔画的总长度,用于后续采样
|
||||
for (int i = 1; i < stroke.StylusPoints.Count; i++) {
|
||||
Point p1 = stroke.StylusPoints[i-1].ToPoint();
|
||||
Point p2 = stroke.StylusPoints[i].ToPoint();
|
||||
strokeLength += GetDistance(p1, p2);
|
||||
}
|
||||
|
||||
// 如果笔画太短,直接使用所有点
|
||||
if (strokeLength < sampleInterval * 5) {
|
||||
foreach (StylusPoint sp in stroke.StylusPoints) {
|
||||
Point p = sp.ToPoint();
|
||||
double deviation = DistanceFromLineToPoint(start, end, p);
|
||||
maxDeviation = Math.Max(maxDeviation, deviation);
|
||||
totalDeviation += deviation;
|
||||
pointCount++;
|
||||
}
|
||||
} else {
|
||||
// 使用等距采样点
|
||||
double currentLength = 0;
|
||||
double nextSampleAt = 0;
|
||||
|
||||
// 总是包含起点
|
||||
Point lastPoint = start;
|
||||
double deviation = DistanceFromLineToPoint(start, end, lastPoint);
|
||||
maxDeviation = Math.Max(maxDeviation, deviation);
|
||||
totalDeviation += deviation;
|
||||
pointCount++;
|
||||
|
||||
// 采样中间点
|
||||
for (int i = 1; i < stroke.StylusPoints.Count; i++) {
|
||||
Point currentPoint = stroke.StylusPoints[i].ToPoint();
|
||||
double segmentLength = GetDistance(lastPoint, currentPoint);
|
||||
|
||||
// 如果这段线段跨越了下一个采样点
|
||||
while (currentLength + segmentLength >= nextSampleAt) {
|
||||
// 计算采样点在线段上的位置
|
||||
double t = (nextSampleAt - currentLength) / segmentLength;
|
||||
Point samplePoint = new Point(
|
||||
lastPoint.X + t * (currentPoint.X - lastPoint.X),
|
||||
lastPoint.Y + t * (currentPoint.Y - lastPoint.Y)
|
||||
);
|
||||
|
||||
// 计算采样点的偏差
|
||||
deviation = DistanceFromLineToPoint(start, end, samplePoint);
|
||||
maxDeviation = Math.Max(maxDeviation, deviation);
|
||||
totalDeviation += deviation;
|
||||
pointCount++;
|
||||
|
||||
// 设置下一个采样点位置
|
||||
nextSampleAt += sampleInterval;
|
||||
|
||||
// 防止无限循环
|
||||
if (nextSampleAt > strokeLength) break;
|
||||
}
|
||||
|
||||
currentLength += segmentLength;
|
||||
lastPoint = currentPoint;
|
||||
}
|
||||
|
||||
// 总是包含终点
|
||||
deviation = DistanceFromLineToPoint(start, end, end);
|
||||
maxDeviation = Math.Max(maxDeviation, deviation);
|
||||
totalDeviation += deviation;
|
||||
pointCount++;
|
||||
}
|
||||
} else {
|
||||
// 原始模式:使用所有点
|
||||
foreach (StylusPoint sp in stroke.StylusPoints) {
|
||||
Point p = sp.ToPoint();
|
||||
double deviation = DistanceFromLineToPoint(start, end, p);
|
||||
maxDeviation = Math.Max(maxDeviation, deviation);
|
||||
totalDeviation += deviation;
|
||||
pointCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算平均偏差
|
||||
double avgDeviation = totalDeviation / pointCount;
|
||||
|
||||
// 更详细的调试信息
|
||||
Debug.WriteLine($"Max deviation: {maxDeviation}, Avg: {avgDeviation}, Threshold: {sensitivity * lineLength}, Points: {pointCount}");
|
||||
|
||||
// 支持更广泛的灵敏度范围 (0.05-2.0)
|
||||
|
||||
// 如果灵敏度高于1.0,使用更宽松的判断标准
|
||||
if (sensitivity > 1.0) {
|
||||
// 高灵敏度模式 - 允许更大的偏差
|
||||
double adjustedSensitivity = 0.5 + (sensitivity - 1.0) * 1.5; // 映射到0.5-2.0范围
|
||||
|
||||
// 只判断平均偏差和相对偏差
|
||||
if (maxDeviation / lineLength < adjustedSensitivity && avgDeviation < lineLength * 0.1 * adjustedSensitivity) {
|
||||
Debug.WriteLine("接受拉直 (高灵敏度模式)");
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.WriteLine("拒绝拉直 (高灵敏度模式)");
|
||||
return false;
|
||||
}
|
||||
// 否则使用常规判断标准
|
||||
|
||||
// 检查点分布的一致性 - 如果有些点偏离很大而其他点很接近直线,表明线条有明显弯曲
|
||||
double deviationVariance = 0;
|
||||
foreach (StylusPoint sp in stroke.StylusPoints) {
|
||||
Point p = sp.ToPoint();
|
||||
double deviation = DistanceFromLineToPoint(start, end, p);
|
||||
deviationVariance += Math.Pow(deviation - avgDeviation, 2);
|
||||
|
||||
// 使用相同的高精度/原始模式来计算方差
|
||||
if (useHighPrecision) {
|
||||
// 高精度模式:重新采样计算方差
|
||||
double strokeLength = 0;
|
||||
double sampleInterval = 10.0; // 10像素间隔
|
||||
|
||||
// 计算笔画的总长度,用于后续采样
|
||||
for (int i = 1; i < stroke.StylusPoints.Count; i++) {
|
||||
Point p1 = stroke.StylusPoints[i-1].ToPoint();
|
||||
Point p2 = stroke.StylusPoints[i].ToPoint();
|
||||
strokeLength += GetDistance(p1, p2);
|
||||
}
|
||||
|
||||
// 如果笔画太短,直接使用所有点
|
||||
if (strokeLength < sampleInterval * 5) {
|
||||
foreach (StylusPoint sp in stroke.StylusPoints) {
|
||||
Point p = sp.ToPoint();
|
||||
double deviation = DistanceFromLineToPoint(start, end, p);
|
||||
deviationVariance += Math.Pow(deviation - avgDeviation, 2);
|
||||
}
|
||||
} else {
|
||||
// 使用等距采样点
|
||||
double currentLength = 0;
|
||||
double nextSampleAt = 0;
|
||||
Point lastPoint = start;
|
||||
|
||||
// 起点方差
|
||||
double deviation = DistanceFromLineToPoint(start, end, lastPoint);
|
||||
deviationVariance += Math.Pow(deviation - avgDeviation, 2);
|
||||
|
||||
// 采样中间点
|
||||
for (int i = 1; i < stroke.StylusPoints.Count; i++) {
|
||||
Point currentPoint = stroke.StylusPoints[i].ToPoint();
|
||||
double segmentLength = GetDistance(lastPoint, currentPoint);
|
||||
|
||||
// 如果这段线段跨越了下一个采样点
|
||||
while (currentLength + segmentLength >= nextSampleAt) {
|
||||
// 计算采样点在线段上的位置
|
||||
double t = (nextSampleAt - currentLength) / segmentLength;
|
||||
Point samplePoint = new Point(
|
||||
lastPoint.X + t * (currentPoint.X - lastPoint.X),
|
||||
lastPoint.Y + t * (currentPoint.Y - lastPoint.Y)
|
||||
);
|
||||
|
||||
// 计算采样点的方差
|
||||
deviation = DistanceFromLineToPoint(start, end, samplePoint);
|
||||
deviationVariance += Math.Pow(deviation - avgDeviation, 2);
|
||||
|
||||
// 设置下一个采样点位置
|
||||
nextSampleAt += sampleInterval;
|
||||
|
||||
// 防止无限循环
|
||||
if (nextSampleAt > strokeLength) break;
|
||||
}
|
||||
|
||||
currentLength += segmentLength;
|
||||
lastPoint = currentPoint;
|
||||
}
|
||||
|
||||
// 终点方差
|
||||
deviation = DistanceFromLineToPoint(start, end, end);
|
||||
deviationVariance += Math.Pow(deviation - avgDeviation, 2);
|
||||
}
|
||||
} else {
|
||||
// 原始模式:使用所有点计算方差
|
||||
foreach (StylusPoint sp in stroke.StylusPoints) {
|
||||
Point p = sp.ToPoint();
|
||||
double deviation = DistanceFromLineToPoint(start, end, p);
|
||||
deviationVariance += Math.Pow(deviation - avgDeviation, 2);
|
||||
}
|
||||
}
|
||||
|
||||
deviationVariance /= pointCount;
|
||||
|
||||
|
||||
// 输出更多调试信息
|
||||
Debug.WriteLine($"Deviation variance: {deviationVariance}, Threshold: {sensitivity * lineLength * 0.05}");
|
||||
|
||||
// 如果最大偏差超过线长的阈值比例,或者偏差方差较大(表示不均匀弯曲),则不拉直
|
||||
// 灵敏度越大,容许的偏差越大,更容易将线条识别为直线
|
||||
if ((maxDeviation / lineLength) > sensitivity) {
|
||||
Debug.WriteLine("拒绝拉直:最大偏差过大");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 如果偏差方差大,说明线条弯曲不均匀
|
||||
// 灵敏度越大,容许的偏差方差越大
|
||||
if (deviationVariance > (sensitivity * lineLength * 0.05)) {
|
||||
Debug.WriteLine("拒绝拉直:偏差方差过大");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查中点偏离情况 - 针对弧形线条特别有效
|
||||
if (stroke.StylusPoints.Count > 10) {
|
||||
int midIndex = stroke.StylusPoints.Count / 2;
|
||||
Point midPoint = stroke.StylusPoints[midIndex].ToPoint();
|
||||
double midDeviation = DistanceFromLineToPoint(start, end, midPoint);
|
||||
|
||||
|
||||
// 输出中点偏差信息
|
||||
Debug.WriteLine($"Mid deviation: {midDeviation}, Threshold: {lineLength * sensitivity * 0.8}");
|
||||
|
||||
// 如果中点偏离过大,不拉直
|
||||
// 使用灵敏度作为判断基准,灵敏度越大,容许的中点偏离越大
|
||||
if (midDeviation > (lineLength * sensitivity * 0.8)) {
|
||||
Debug.WriteLine("拒绝拉直:中点偏差过大");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Debug.WriteLine("接受拉直");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -682,14 +984,32 @@ namespace Ink_Canvas {
|
||||
private StylusPointCollection CreateStraightLine(Point start, Point end) {
|
||||
StylusPointCollection points = new StylusPointCollection();
|
||||
|
||||
// Create a more natural pressure profile for the line
|
||||
if (Settings.InkToShape.IsInkToShapeNoFakePressureRectangle == true || penType == 1) {
|
||||
points.Add(new StylusPoint(start.X, start.Y));
|
||||
points.Add(new StylusPoint(end.X, end.Y));
|
||||
// 根据是否启用压感触屏模式决定如何设置压感
|
||||
// 如果未启用压感触屏模式,则使用均匀粗细
|
||||
if (!Settings.Canvas.EnablePressureTouchMode || Settings.Canvas.DisablePressure ||
|
||||
Settings.InkToShape.IsInkToShapeNoFakePressureRectangle || penType == 1) {
|
||||
// 使用均匀粗细(所有点压感值都是0.5f)
|
||||
points.Add(new StylusPoint(start.X, start.Y, 0.5f));
|
||||
|
||||
// 可以添加一些额外的中间点使线条更平滑(均匀粗细)
|
||||
double distance = GetDistance(start, end);
|
||||
if (distance > 100) {
|
||||
// 对于较长的线条,添加几个中间点
|
||||
for (int i = 1; i < 3; i++) {
|
||||
double ratio = i / 3.0;
|
||||
Point midPoint = new Point(
|
||||
start.X + (end.X - start.X) * ratio,
|
||||
start.Y + (end.Y - start.Y) * ratio);
|
||||
points.Add(new StylusPoint(midPoint.X, midPoint.Y, 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
points.Add(new StylusPoint(end.X, end.Y, 0.5f));
|
||||
} else {
|
||||
// 启用了压感触屏模式,使用变化的粗细(原有行为)
|
||||
points.Add(new StylusPoint(start.X, start.Y, 0.4f));
|
||||
|
||||
// Add a middle point with higher pressure
|
||||
// 添加中点,压感值较高,使线条中间较粗
|
||||
Point midPoint = new Point((start.X + end.X) / 2, (start.Y + end.Y) / 2);
|
||||
points.Add(new StylusPoint(midPoint.X, midPoint.Y, 0.8f));
|
||||
|
||||
@@ -714,11 +1034,19 @@ namespace Ink_Canvas {
|
||||
|
||||
// New method: Attempts to snap endpoints to existing stroke endpoints
|
||||
private Point[] GetSnappedEndpoints(Point start, Point end) {
|
||||
// 如果端点吸附功能关闭,直接返回null
|
||||
// 这里不再返回原始点,因为调用此方法的地方会判断返回值是否为null
|
||||
if (!Settings.Canvas.LineEndpointSnapping)
|
||||
return null;
|
||||
|
||||
bool startSnapped = false;
|
||||
bool endSnapped = false;
|
||||
Point snappedStart = start;
|
||||
Point snappedEnd = end;
|
||||
|
||||
// 使用设置中的吸附距离阈值
|
||||
double snapThreshold = Settings.Canvas.LineEndpointSnappingThreshold;
|
||||
|
||||
// Check all strokes in canvas for potential snap points
|
||||
foreach (Stroke stroke in inkCanvas.Strokes) {
|
||||
if (stroke.StylusPoints.Count == 0) continue;
|
||||
@@ -729,10 +1057,10 @@ namespace Ink_Canvas {
|
||||
|
||||
// Check if start point should snap to an endpoint
|
||||
if (!startSnapped) {
|
||||
if (GetDistance(start, strokeStart) < SNAP_THRESHOLD) {
|
||||
if (GetDistance(start, strokeStart) < snapThreshold) {
|
||||
snappedStart = strokeStart;
|
||||
startSnapped = true;
|
||||
} else if (GetDistance(start, strokeEnd) < SNAP_THRESHOLD) {
|
||||
} else if (GetDistance(start, strokeEnd) < snapThreshold) {
|
||||
snappedStart = strokeEnd;
|
||||
startSnapped = true;
|
||||
}
|
||||
@@ -740,10 +1068,10 @@ namespace Ink_Canvas {
|
||||
|
||||
// Check if end point should snap to an endpoint
|
||||
if (!endSnapped) {
|
||||
if (GetDistance(end, strokeStart) < SNAP_THRESHOLD) {
|
||||
if (GetDistance(end, strokeStart) < snapThreshold) {
|
||||
snappedEnd = strokeStart;
|
||||
endSnapped = true;
|
||||
} else if (GetDistance(end, strokeEnd) < SNAP_THRESHOLD) {
|
||||
} else if (GetDistance(end, strokeEnd) < snapThreshold) {
|
||||
snappedEnd = strokeEnd;
|
||||
endSnapped = true;
|
||||
}
|
||||
@@ -755,7 +1083,7 @@ namespace Ink_Canvas {
|
||||
|
||||
// Return snapped points if any snapping occurred
|
||||
if (startSnapped || endSnapped) {
|
||||
return new Point[] { snappedStart, snappedEnd };
|
||||
return new[] { snappedStart, snappedEnd };
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -812,7 +1140,7 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
public StylusPointCollection GenerateFakePressureTriangle(StylusPointCollection points) {
|
||||
if (Settings.InkToShape.IsInkToShapeNoFakePressureTriangle == true || penType == 1) {
|
||||
if (Settings.InkToShape.IsInkToShapeNoFakePressureTriangle || penType == 1) {
|
||||
var newPoint = new StylusPointCollection();
|
||||
newPoint.Add(new StylusPoint(points[0].X, points[0].Y));
|
||||
var cPoint = GetCenterPoint(points[0], points[1]);
|
||||
@@ -846,30 +1174,30 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
public StylusPointCollection GenerateFakePressureRectangle(StylusPointCollection points) {
|
||||
if (Settings.InkToShape.IsInkToShapeNoFakePressureRectangle == true || penType == 1) {
|
||||
public StylusPointCollection GenerateFakePressureRectangle(StylusPointCollection points)
|
||||
{
|
||||
if (Settings.InkToShape.IsInkToShapeNoFakePressureRectangle || penType == 1) {
|
||||
return points;
|
||||
}
|
||||
else {
|
||||
var newPoint = new StylusPointCollection();
|
||||
newPoint.Add(new StylusPoint(points[0].X, points[0].Y, (float)0.4));
|
||||
var cPoint = GetCenterPoint(points[0], points[1]);
|
||||
newPoint.Add(new StylusPoint(cPoint.X, cPoint.Y, (float)0.8));
|
||||
newPoint.Add(new StylusPoint(points[1].X, points[1].Y, (float)0.4));
|
||||
newPoint.Add(new StylusPoint(points[1].X, points[1].Y, (float)0.4));
|
||||
cPoint = GetCenterPoint(points[1], points[2]);
|
||||
newPoint.Add(new StylusPoint(cPoint.X, cPoint.Y, (float)0.8));
|
||||
newPoint.Add(new StylusPoint(points[2].X, points[2].Y, (float)0.4));
|
||||
newPoint.Add(new StylusPoint(points[2].X, points[2].Y, (float)0.4));
|
||||
cPoint = GetCenterPoint(points[2], points[3]);
|
||||
newPoint.Add(new StylusPoint(cPoint.X, cPoint.Y, (float)0.8));
|
||||
newPoint.Add(new StylusPoint(points[3].X, points[3].Y, (float)0.4));
|
||||
newPoint.Add(new StylusPoint(points[3].X, points[3].Y, (float)0.4));
|
||||
cPoint = GetCenterPoint(points[3], points[0]);
|
||||
newPoint.Add(new StylusPoint(cPoint.X, cPoint.Y, (float)0.8));
|
||||
newPoint.Add(new StylusPoint(points[0].X, points[0].Y, (float)0.4));
|
||||
return newPoint;
|
||||
}
|
||||
|
||||
var newPoint = new StylusPointCollection();
|
||||
newPoint.Add(new StylusPoint(points[0].X, points[0].Y, (float)0.4));
|
||||
var cPoint = GetCenterPoint(points[0], points[1]);
|
||||
newPoint.Add(new StylusPoint(cPoint.X, cPoint.Y, (float)0.8));
|
||||
newPoint.Add(new StylusPoint(points[1].X, points[1].Y, (float)0.4));
|
||||
newPoint.Add(new StylusPoint(points[1].X, points[1].Y, (float)0.4));
|
||||
cPoint = GetCenterPoint(points[1], points[2]);
|
||||
newPoint.Add(new StylusPoint(cPoint.X, cPoint.Y, (float)0.8));
|
||||
newPoint.Add(new StylusPoint(points[2].X, points[2].Y, (float)0.4));
|
||||
newPoint.Add(new StylusPoint(points[2].X, points[2].Y, (float)0.4));
|
||||
cPoint = GetCenterPoint(points[2], points[3]);
|
||||
newPoint.Add(new StylusPoint(cPoint.X, cPoint.Y, (float)0.8));
|
||||
newPoint.Add(new StylusPoint(points[3].X, points[3].Y, (float)0.4));
|
||||
newPoint.Add(new StylusPoint(points[3].X, points[3].Y, (float)0.4));
|
||||
cPoint = GetCenterPoint(points[3], points[0]);
|
||||
newPoint.Add(new StylusPoint(cPoint.X, cPoint.Y, (float)0.8));
|
||||
newPoint.Add(new StylusPoint(points[0].X, points[0].Y, (float)0.4));
|
||||
return newPoint;
|
||||
}
|
||||
|
||||
public Point GetCenterPoint(Point point1, Point point2) {
|
||||
@@ -879,5 +1207,19 @@ namespace Ink_Canvas {
|
||||
public StylusPoint GetCenterPoint(StylusPoint point1, StylusPoint point2) {
|
||||
return new StylusPoint((point1.X + point2.X) / 2, (point1.Y + point2.Y) / 2);
|
||||
}
|
||||
|
||||
// 分辨率自适应:以1080P为基准,返回当前分辨率下的阈值倍数
|
||||
private double GetResolutionScale()
|
||||
{
|
||||
// 以1920x1080为基准
|
||||
double baseWidth = 1920.0;
|
||||
double baseHeight = 1080.0;
|
||||
double screenWidth = SystemParameters.PrimaryScreenWidth;
|
||||
double screenHeight = SystemParameters.PrimaryScreenHeight;
|
||||
// 取宽高平均缩放,防止极端比例
|
||||
double scaleW = screenWidth / baseWidth;
|
||||
double scaleH = screenHeight / baseHeight;
|
||||
return (scaleW + scaleH) / 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Ink_Canvas.Helpers;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
@@ -33,7 +32,7 @@ namespace Ink_Canvas {
|
||||
private Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>> DrawingAttributesHistory =
|
||||
new Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>>();
|
||||
|
||||
private Dictionary<Guid, List<Stroke>> DrawingAttributesHistoryFlag = new Dictionary<Guid, List<Stroke>>() {
|
||||
private Dictionary<Guid, List<Stroke>> DrawingAttributesHistoryFlag = new Dictionary<Guid, List<Stroke>> {
|
||||
{ DrawingAttributeIds.Color, new List<Stroke>() },
|
||||
{ DrawingAttributeIds.DrawingFlags, new List<Stroke>() },
|
||||
{ DrawingAttributeIds.IsHighlighter, new List<Stroke>() },
|
||||
@@ -130,13 +129,26 @@ namespace Ink_Canvas {
|
||||
if (canvas.Strokes.Contains(currentStroke))
|
||||
canvas.Strokes.Remove(currentStroke);
|
||||
}
|
||||
} else if (item.CommitType == TimeMachineHistoryType.ElementInsert) {
|
||||
// 使用传入的canvas参数,而不是总是使用inkCanvas
|
||||
var targetCanvas = canvas ?? inkCanvas;
|
||||
|
||||
if (item.StrokeHasBeenCleared) {
|
||||
// Undo: 移除元素
|
||||
if (item.InsertedElement != null && targetCanvas.Children.Contains(item.InsertedElement))
|
||||
targetCanvas.Children.Remove(item.InsertedElement);
|
||||
} else {
|
||||
// Redo: 添加元素
|
||||
if (item.InsertedElement != null && !targetCanvas.Children.Contains(item.InsertedElement))
|
||||
targetCanvas.Children.Add(item.InsertedElement);
|
||||
}
|
||||
}
|
||||
|
||||
_currentCommitType = CommitReason.UserInput;
|
||||
}
|
||||
|
||||
private StrokeCollection ApplyHistoriesToNewStrokeCollection(TimeMachineHistory[] items) {
|
||||
InkCanvas fakeInkCanv = new InkCanvas() {
|
||||
InkCanvas fakeInkCanv = new InkCanvas {
|
||||
Width = inkCanvas.ActualWidth,
|
||||
Height = inkCanvas.ActualHeight,
|
||||
EditingMode = InkCanvasEditingMode.None,
|
||||
@@ -144,13 +156,34 @@ namespace Ink_Canvas {
|
||||
|
||||
if (items != null && items.Length > 0) {
|
||||
foreach (var timeMachineHistory in items) {
|
||||
ApplyHistoryToCanvas(timeMachineHistory, fakeInkCanv);
|
||||
// 只处理笔画历史,不处理图片元素历史
|
||||
// 因为页面预览只需要显示笔画,图片元素会影响主画布
|
||||
if (timeMachineHistory.CommitType != TimeMachineHistoryType.ElementInsert) {
|
||||
ApplyHistoryToCanvas(timeMachineHistory, fakeInkCanv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fakeInkCanv.Strokes;
|
||||
}
|
||||
|
||||
// 新增:获取页面的所有图片元素
|
||||
private List<UIElement> GetPageImageElements(TimeMachineHistory[] items) {
|
||||
var imageElements = new List<UIElement>();
|
||||
|
||||
if (items != null && items.Length > 0) {
|
||||
foreach (var timeMachineHistory in items) {
|
||||
if (timeMachineHistory.CommitType == TimeMachineHistoryType.ElementInsert &&
|
||||
timeMachineHistory.InsertedElement != null &&
|
||||
!timeMachineHistory.StrokeHasBeenCleared) {
|
||||
imageElements.Add(timeMachineHistory.InsertedElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imageElements;
|
||||
}
|
||||
|
||||
private void TimeMachine_OnUndoStateChanged(bool status) {
|
||||
var result = status ? Visibility.Visible : Visibility.Collapsed;
|
||||
BtnUndo.Visibility = result;
|
||||
@@ -193,24 +226,23 @@ namespace Ink_Canvas {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Added.Count != 0) {
|
||||
if (e.Added.Count != 0)
|
||||
{
|
||||
if (_currentCommitType == CommitReason.ShapeRecognition) {
|
||||
timeMachine.CommitStrokeShapeHistory(ReplacedStroke, e.Added);
|
||||
ReplacedStroke = null;
|
||||
return;
|
||||
} else {
|
||||
timeMachine.CommitStrokeUserInputHistory(e.Added);
|
||||
return;
|
||||
}
|
||||
|
||||
timeMachine.CommitStrokeUserInputHistory(e.Added);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Removed.Count != 0) {
|
||||
if (_currentCommitType == CommitReason.ShapeRecognition) {
|
||||
ReplacedStroke = e.Removed;
|
||||
return;
|
||||
} else if (!IsEraseByPoint || _currentCommitType == CommitReason.ClearingCanvas) {
|
||||
timeMachine.CommitStrokeEraseHistory(e.Removed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using System.Windows;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using System.Windows.Controls;
|
||||
using Ink_Canvas.Helpers;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public class TimeViewModel : INotifyPropertyChanged {
|
||||
@@ -43,15 +49,44 @@ namespace Ink_Canvas {
|
||||
private Timer timerCheckPPT = new Timer();
|
||||
private Timer timerKillProcess = new Timer();
|
||||
private Timer timerCheckAutoFold = new Timer();
|
||||
private string AvailableLatestVersion = null;
|
||||
private string AvailableLatestVersion;
|
||||
private Timer timerCheckAutoUpdateWithSilence = new Timer();
|
||||
private bool isHidingSubPanelsWhenInking = false; // 避免书写时触发二次关闭二级菜单导致动画不连续
|
||||
private bool isHidingSubPanelsWhenInking; // 避免书写时触发二次关闭二级菜单导致动画不连续
|
||||
|
||||
private Timer timerDisplayTime = new Timer();
|
||||
private Timer timerDisplayDate = new Timer();
|
||||
|
||||
private TimeViewModel nowTimeVM = new TimeViewModel();
|
||||
|
||||
private async Task<DateTime> GetNetworkTimeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
const string ntpServer = "ntp.ntsc.ac.cn";
|
||||
var ntpData = new byte[48];
|
||||
ntpData[0] = 0x1B;
|
||||
var addresses = await Dns.GetHostAddressesAsync(ntpServer);
|
||||
var ipEndPoint = new IPEndPoint(addresses[0], 123);
|
||||
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
|
||||
{
|
||||
socket.ReceiveTimeout = 2000;
|
||||
socket.Connect(ipEndPoint);
|
||||
await Task.Factory.FromAsync(socket.BeginSend(ntpData, 0, ntpData.Length, SocketFlags.None, null, socket), socket.EndSend);
|
||||
await Task.Factory.FromAsync(socket.BeginReceive(ntpData, 0, ntpData.Length, SocketFlags.None, null, socket), socket.EndReceive);
|
||||
}
|
||||
const byte serverReplyTime = 40;
|
||||
ulong intPart = BitConverter.ToUInt32(ntpData.Skip(serverReplyTime).Take(4).Reverse().ToArray(), 0);
|
||||
ulong fractPart = BitConverter.ToUInt32(ntpData.Skip(serverReplyTime + 4).Take(4).Reverse().ToArray(), 0);
|
||||
var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
|
||||
var networkDateTime = (new DateTime(1900, 1, 1)).AddMilliseconds((long)milliseconds);
|
||||
return networkDateTime.ToLocalTime();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitTimers() {
|
||||
timerCheckPPT.Elapsed += TimerCheckPPT_Elapsed;
|
||||
timerCheckPPT.Interval = 500;
|
||||
@@ -63,23 +98,29 @@ namespace Ink_Canvas {
|
||||
timerCheckAutoUpdateWithSilence.Interval = 1000 * 60 * 10;
|
||||
WaterMarkTime.DataContext = nowTimeVM;
|
||||
WaterMarkDate.DataContext = nowTimeVM;
|
||||
timerDisplayTime.Elapsed += TimerDisplayTime_Elapsed;
|
||||
timerDisplayTime.Elapsed += async (s, e) => await TimerDisplayTime_ElapsedAsync();
|
||||
timerDisplayTime.Interval = 1000;
|
||||
timerDisplayTime.Start();
|
||||
timerDisplayDate.Elapsed += TimerDisplayDate_Elapsed;
|
||||
timerDisplayDate.Interval = 1000 * 60 * 60 * 1;
|
||||
timerDisplayDate.Start();
|
||||
timerKillProcess.Start();
|
||||
nowTimeVM.nowDate = DateTime.Now.ToShortDateString().ToString();
|
||||
nowTimeVM.nowTime = DateTime.Now.ToShortTimeString().ToString();
|
||||
nowTimeVM.nowDate = DateTime.Now.ToString("yyyy/M/d");
|
||||
nowTimeVM.nowTime = DateTime.Now.ToString("HH:mm");
|
||||
}
|
||||
|
||||
private void TimerDisplayTime_Elapsed(object sender, ElapsedEventArgs e) {
|
||||
nowTimeVM.nowTime = DateTime.Now.ToShortTimeString().ToString();
|
||||
private async Task TimerDisplayTime_ElapsedAsync()
|
||||
{
|
||||
DateTime now = await GetNetworkTimeAsync();
|
||||
// 只更新时间,日期由原有逻辑定时更新即可
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
nowTimeVM.nowTime = now.ToString("HH:mm");
|
||||
});
|
||||
}
|
||||
|
||||
private void TimerDisplayDate_Elapsed(object sender, ElapsedEventArgs e) {
|
||||
nowTimeVM.nowDate = DateTime.Now.ToShortDateString().ToString();
|
||||
nowTimeVM.nowDate = DateTime.Now.ToString("yyyy/M/d");
|
||||
}
|
||||
|
||||
private void TimerKillProcess_Elapsed(object sender, ElapsedEventArgs e) {
|
||||
@@ -147,6 +188,10 @@ namespace Ink_Canvas {
|
||||
if (arg.Contains("HiteAnnotation")) {
|
||||
Dispatcher.Invoke(() => {
|
||||
ShowNotification("“鸿合屏幕书写”已自动关闭");
|
||||
if (Settings.Automation.IsAutoKillHiteAnnotation && Settings.Automation.IsAutoEnterAnnotationAfterKillHite) {
|
||||
// 自动进入批注状态
|
||||
PenIcon_Click(null, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -187,8 +232,8 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
|
||||
private bool foldFloatingBarByUser = false, // 保持收纳操作不受自动收纳的控制
|
||||
unfoldFloatingBarByUser = false; // 允许用户在希沃软件内进行展开操作
|
||||
private bool foldFloatingBarByUser, // 保持收纳操作不受自动收纳的控制
|
||||
unfoldFloatingBarByUser; // 允许用户在希沃软件内进行展开操作
|
||||
|
||||
private void timerCheckAutoFold_Elapsed(object sender, ElapsedEventArgs e) {
|
||||
if (isFloatingBarChangingHideMode) return;
|
||||
@@ -302,24 +347,131 @@ namespace Ink_Canvas {
|
||||
}
|
||||
|
||||
private void timerCheckAutoUpdateWithSilence_Elapsed(object sender, ElapsedEventArgs e) {
|
||||
Dispatcher.Invoke(() => {
|
||||
try {
|
||||
if (!Topmost || inkCanvas.Strokes.Count > 0) return;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error);
|
||||
}
|
||||
});
|
||||
// 停止计时器,避免重复触发
|
||||
timerCheckAutoUpdateWithSilence.Stop();
|
||||
|
||||
try {
|
||||
if (AutoUpdateWithSilenceTimeComboBox.CheckIsInSilencePeriod(
|
||||
Settings.Startup.AutoUpdateWithSilenceStartTime,
|
||||
Settings.Startup.AutoUpdateWithSilenceEndTime)) {
|
||||
// 检查是否有可用的更新
|
||||
if (string.IsNullOrEmpty(AvailableLatestVersion)) {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | No available update version found");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否启用了静默更新
|
||||
if (!Settings.Startup.IsAutoUpdateWithSilence) {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Silent update is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查更新文件是否已下载
|
||||
string updatesFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoUpdate");
|
||||
string statusFilePath = Path.Combine(updatesFolderPath, $"DownloadV{AvailableLatestVersion}Status.txt");
|
||||
|
||||
if (!File.Exists(statusFilePath) || File.ReadAllText(statusFilePath).Trim().ToLower() != "true") {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Update file not downloaded yet");
|
||||
|
||||
// 尝试下载更新文件,使用多线路组下载功能
|
||||
Task.Run(async () => {
|
||||
bool isDownloadSuccessful = false;
|
||||
|
||||
try
|
||||
{
|
||||
// 如果主要线路组可用,直接使用
|
||||
if (AvailableLatestLineGroup != null)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 使用主要线路组下载: {AvailableLatestLineGroup.GroupName}");
|
||||
isDownloadSuccessful = await AutoUpdateHelper.DownloadSetupFile(AvailableLatestVersion, AvailableLatestLineGroup);
|
||||
}
|
||||
|
||||
// 如果主要线路组不可用或下载失败,获取所有可用线路组
|
||||
if (!isDownloadSuccessful)
|
||||
{
|
||||
LogHelper.WriteLogToFile("AutoUpdate | 主要线路组不可用或下载失败,获取所有可用线路组");
|
||||
var availableGroups = await AutoUpdateHelper.GetAvailableLineGroupsOrdered(Settings.Startup.UpdateChannel);
|
||||
if (availableGroups.Count > 0)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 使用 {availableGroups.Count} 个可用线路组进行下载");
|
||||
isDownloadSuccessful = await AutoUpdateHelper.DownloadSetupFileWithFallback(AvailableLatestVersion, availableGroups);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载更新时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
if (isDownloadSuccessful) {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Update downloaded successfully, will check again for installation");
|
||||
// 重新启动计时器,下次检查时安装
|
||||
timerCheckAutoUpdateWithSilence.Start();
|
||||
} else {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Failed to download update", LogHelper.LogType.Error);
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否在静默更新时间段内
|
||||
bool isInSilencePeriod = AutoUpdateWithSilenceTimeComboBox.CheckIsInSilencePeriod(
|
||||
Settings.Startup.AutoUpdateWithSilenceStartTime,
|
||||
Settings.Startup.AutoUpdateWithSilenceEndTime);
|
||||
|
||||
if (!isInSilencePeriod) {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Not in silence update time period");
|
||||
// 重新启动计时器,稍后再检查
|
||||
timerCheckAutoUpdateWithSilence.Start();
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查应用程序状态,确保可以安全更新
|
||||
// 空闲状态的判定为不处于批注模式和画板模式
|
||||
bool canSafelyUpdate = false;
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
try {
|
||||
// 判断是否处于批注模式(inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
|
||||
// 判断是否处于画板模式(!Topmost)
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.Ink && Topmost) {
|
||||
// 检查是否有未保存的内容或正在进行的操作
|
||||
if (!isHidingSubPanelsWhenInking) {
|
||||
canSafelyUpdate = true;
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Application is in a safe state for update - not in ink or board mode");
|
||||
} else {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Application is currently performing operations");
|
||||
}
|
||||
} else {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Application is in ink or board mode, cannot update now");
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Error checking application state: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
});
|
||||
|
||||
if (canSafelyUpdate) {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Installing update now");
|
||||
|
||||
// 设置为用户主动退出,避免被看门狗判定为崩溃
|
||||
App.IsAppExitByUser = true;
|
||||
|
||||
// 执行更新安装
|
||||
AutoUpdateHelper.InstallNewVersionApp(AvailableLatestVersion, true);
|
||||
timerCheckAutoUpdateWithSilence.Stop();
|
||||
|
||||
// 关闭应用程序
|
||||
Dispatcher.Invoke(() => {
|
||||
Application.Current.Shutdown();
|
||||
});
|
||||
} else {
|
||||
LogHelper.WriteLogToFile("AutoUpdate | Cannot safely update now, will try again later");
|
||||
// 重新启动计时器,稍后再检查
|
||||
timerCheckAutoUpdateWithSilence.Start();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error);
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Error in silent update check: {ex.Message}", LogHelper.LogType.Error);
|
||||
// 出错时重新启动计时器,稍后再检查
|
||||
timerCheckAutoUpdateWithSilence.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -8,13 +7,58 @@ using System.Windows.Controls;
|
||||
using System.Windows.Ink;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using Ink_Canvas.Helpers;
|
||||
using Point = System.Windows.Point;
|
||||
|
||||
namespace Ink_Canvas {
|
||||
public partial class MainWindow : Window {
|
||||
#region Multi-Touch
|
||||
|
||||
private bool isInMultiTouchMode = false;
|
||||
private bool isInMultiTouchMode;
|
||||
private List<int> dec = new List<int>();
|
||||
private bool isSingleFingerDragMode;
|
||||
private Point centerPoint = new Point(0, 0);
|
||||
private InkCanvasEditingMode lastInkCanvasEditingMode = InkCanvasEditingMode.Ink;
|
||||
|
||||
/// <summary>
|
||||
/// 保存画布上的非笔画元素(如图片、媒体元素等)
|
||||
/// </summary>
|
||||
private List<UIElement> PreserveNonStrokeElements()
|
||||
{
|
||||
var preservedElements = new List<UIElement>();
|
||||
|
||||
// 遍历inkCanvas的所有子元素
|
||||
for (int i = inkCanvas.Children.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var child = inkCanvas.Children[i];
|
||||
|
||||
// 保存图片、媒体元素等非笔画相关的UI元素
|
||||
if (child is Image || child is MediaElement ||
|
||||
(child is Border border && border.Name != "AdvancedEraserOverlay"))
|
||||
{
|
||||
preservedElements.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
return preservedElements;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复之前保存的非笔画元素到画布
|
||||
/// </summary>
|
||||
private void RestoreNonStrokeElements(List<UIElement> preservedElements)
|
||||
{
|
||||
if (preservedElements == null) return;
|
||||
|
||||
foreach (var element in preservedElements)
|
||||
{
|
||||
// 确保元素没有父容器再添加到inkCanvas
|
||||
if (element is FrameworkElement fe && fe.Parent == null)
|
||||
{
|
||||
inkCanvas.Children.Add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BorderMultiTouchMode_MouseUp(object sender, MouseButtonEventArgs e) {
|
||||
if (isInMultiTouchMode) {
|
||||
@@ -23,18 +67,32 @@ namespace Ink_Canvas {
|
||||
inkCanvas.StylusUp -= MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown -= MainWindow_TouchDown;
|
||||
inkCanvas.TouchDown += Main_Grid_TouchDown;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
}
|
||||
// 保存非笔画元素(如图片)
|
||||
var preservedElements = PreserveNonStrokeElements();
|
||||
inkCanvas.Children.Clear();
|
||||
// 恢复非笔画元素
|
||||
RestoreNonStrokeElements(preservedElements);
|
||||
isInMultiTouchMode = false;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
inkCanvas.StylusDown += MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove += MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp += MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown += MainWindow_TouchDown;
|
||||
inkCanvas.TouchDown -= Main_Grid_TouchDown;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
}
|
||||
// 保存非笔画元素(如图片)
|
||||
var preservedElements = PreserveNonStrokeElements();
|
||||
inkCanvas.Children.Clear();
|
||||
// 恢复非笔画元素
|
||||
RestoreNonStrokeElements(preservedElements);
|
||||
isInMultiTouchMode = true;
|
||||
}
|
||||
}
|
||||
@@ -49,48 +107,39 @@ namespace Ink_Canvas {
|
||||
HideSubPanels(); // 书写时自动隐藏二级菜单
|
||||
}
|
||||
|
||||
double boundWidth = e.GetTouchPoint(null).Bounds.Width, eraserMultiplier = 1.0;
|
||||
if (!Settings.Advanced.EraserBindTouchMultiplier && Settings.Advanced.IsSpecialScreen)
|
||||
eraserMultiplier = 1 / Settings.Advanced.TouchMultiplier;
|
||||
|
||||
if ((Settings.Advanced.TouchMultiplier != 0 || !Settings.Advanced.IsSpecialScreen) //启用特殊屏幕且触摸倍数为 0 时禁用橡皮
|
||||
&& boundWidth > BoundsWidth * 2.5) {
|
||||
if (drawingShapeMode == 0 && forceEraser) return;
|
||||
double k = 1;
|
||||
switch (Settings.Canvas.EraserSize) {
|
||||
case 0:
|
||||
k = 0.5;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.8;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.25;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.8;
|
||||
break;
|
||||
}
|
||||
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(boundWidth * k * eraserMultiplier * 0.25,
|
||||
boundWidth * k * eraserMultiplier * 0.25);
|
||||
TouchDownPointsList[e.TouchDevice.Id] = InkCanvasEditingMode.EraseByPoint;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
}
|
||||
else {
|
||||
TouchDownPointsList[e.TouchDevice.Id] = InkCanvasEditingMode.None;
|
||||
// 修复面积擦时不显示橡皮形状:无论 forcePointEraser 状态,均显示 50x50 橡皮
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(50, 50);
|
||||
// 只保留普通橡皮逻辑
|
||||
TouchDownPointsList[e.TouchDevice.Id] = InkCanvasEditingMode.None;
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(50, 50);
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
}
|
||||
}
|
||||
|
||||
private void MainWindow_StylusDown(object sender, StylusDownEventArgs e) {
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
|
||||
inkCanvas.CaptureStylus();
|
||||
ViewboxFloatingBar.IsHitTestVisible = false;
|
||||
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
|
||||
|
||||
// 确保手写笔模式下显示光标
|
||||
if (Settings.Canvas.IsShowCursor) {
|
||||
inkCanvas.ForceCursor = true;
|
||||
inkCanvas.UseCustomCursor = true;
|
||||
|
||||
// 根据当前编辑模式设置不同的光标
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
inkCanvas.Cursor = Cursors.Cross;
|
||||
} else if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink) {
|
||||
var sri = Application.GetResourceStream(new Uri("Resources/Cursors/Pen.cur", UriKind.Relative));
|
||||
if (sri != null)
|
||||
inkCanvas.Cursor = new Cursor(sri.Stream);
|
||||
}
|
||||
|
||||
// 强制显示光标
|
||||
System.Windows.Forms.Cursor.Show();
|
||||
}
|
||||
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint
|
||||
|| inkCanvas.EditingMode == InkCanvasEditingMode.EraseByStroke
|
||||
|| inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
|
||||
@@ -116,7 +165,12 @@ namespace Ink_Canvas {
|
||||
VisualCanvasList.Remove(e.StylusDevice.Id);
|
||||
TouchDownPointsList.Remove(e.StylusDevice.Id);
|
||||
if (StrokeVisualList.Count == 0 || VisualCanvasList.Count == 0 || TouchDownPointsList.Count == 0) {
|
||||
inkCanvas.Children.Clear();
|
||||
// 只清除手写笔预览相关的Canvas,不清除所有子元素
|
||||
foreach (var canvas in VisualCanvasList.Values.ToList()) {
|
||||
if (inkCanvas.Children.Contains(canvas)) {
|
||||
inkCanvas.Children.Remove(canvas);
|
||||
}
|
||||
}
|
||||
StrokeVisualList.Clear();
|
||||
VisualCanvasList.Clear();
|
||||
TouchDownPointsList.Clear();
|
||||
@@ -137,6 +191,13 @@ namespace Ink_Canvas {
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 确保手写笔移动时光标保持可见
|
||||
if (Settings.Canvas.IsShowCursor) {
|
||||
inkCanvas.ForceCursor = true;
|
||||
inkCanvas.UseCustomCursor = true;
|
||||
System.Windows.Forms.Cursor.Show();
|
||||
}
|
||||
|
||||
var strokeVisual = GetStrokeVisual(e.StylusDevice.Id);
|
||||
var stylusPointCollection = e.GetStylusPoints(this);
|
||||
foreach (var stylusPoint in stylusPointCollection)
|
||||
@@ -179,106 +240,78 @@ namespace Ink_Canvas {
|
||||
private int lastTouchDownTime = 0, lastTouchUpTime = 0;
|
||||
|
||||
private Point iniP = new Point(0, 0);
|
||||
private bool isLastTouchEraser = false;
|
||||
private bool forcePointEraser = true;
|
||||
|
||||
private void Main_Grid_TouchDown(object sender, TouchEventArgs e) {
|
||||
// 确保触摸时显示自定义光标
|
||||
if (Settings.Canvas.IsShowCursor) {
|
||||
inkCanvas.ForceCursor = true;
|
||||
System.Windows.Forms.Cursor.Show();
|
||||
}
|
||||
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
inkCanvas.CaptureTouch(e.TouchDevice);
|
||||
ViewboxFloatingBar.IsHitTestVisible = false;
|
||||
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
|
||||
|
||||
if (!isHidingSubPanelsWhenInking) {
|
||||
isHidingSubPanelsWhenInking = true;
|
||||
HideSubPanels(); // 书写时自动隐藏二级菜单
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
// 橡皮状态下只return,保证橡皮状态可保持
|
||||
return;
|
||||
}
|
||||
|
||||
if (NeedUpdateIniP()) iniP = e.GetTouchPoint(inkCanvas).Position;
|
||||
if (drawingShapeMode == 9 && isFirstTouchCuboid == false) MouseTouchMove(iniP);
|
||||
inkCanvas.Opacity = 1;
|
||||
double boundsWidth = GetTouchBoundWidth(e), eraserMultiplier = 1.0;
|
||||
if (!Settings.Advanced.EraserBindTouchMultiplier && Settings.Advanced.IsSpecialScreen)
|
||||
eraserMultiplier = 1 / Settings.Advanced.TouchMultiplier;
|
||||
if (boundsWidth > BoundsWidth) {
|
||||
isLastTouchEraser = true;
|
||||
if (drawingShapeMode == 0 && forceEraser) return;
|
||||
if (boundsWidth > BoundsWidth * 2.5) {
|
||||
double k = 1;
|
||||
switch (Settings.Canvas.EraserSize) {
|
||||
case 0:
|
||||
k = 0.5;
|
||||
break;
|
||||
case 1:
|
||||
k = 0.8;
|
||||
break;
|
||||
case 3:
|
||||
k = 1.25;
|
||||
break;
|
||||
case 4:
|
||||
k = 1.8;
|
||||
break;
|
||||
}
|
||||
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(boundsWidth * k * eraserMultiplier,
|
||||
boundsWidth * k * eraserMultiplier);
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
|
||||
}
|
||||
else {
|
||||
if (StackPanelPPTControls.Visibility == Visibility.Visible && inkCanvas.Strokes.Count == 0 &&
|
||||
Settings.PowerPointSettings.IsEnableFingerGestureSlideShowControl) {
|
||||
isLastTouchEraser = false;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.GestureOnly;
|
||||
inkCanvas.Opacity = 0.1;
|
||||
}
|
||||
else {
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(5, 5);
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
|
||||
}
|
||||
}
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.Select) {
|
||||
// 套索选状态下只return,保证套索选可用
|
||||
return;
|
||||
}
|
||||
else {
|
||||
isLastTouchEraser = false;
|
||||
// 修复面积擦时不显示橡皮形状:无论 forcePointEraser 状态,均显示 50x50 橡皮
|
||||
inkCanvas.EraserShape = new EllipseStylusShape(50, 50);
|
||||
if (forceEraser) return;
|
||||
if (drawingShapeMode == 9) {
|
||||
if (isFirstTouchCuboid) {
|
||||
CuboidFrontRectIniP = e.GetTouchPoint(inkCanvas).Position;
|
||||
}
|
||||
// 允许MouseTouchMove在TouchMove时处理
|
||||
return;
|
||||
}
|
||||
if (drawingShapeMode != 0) {
|
||||
return;
|
||||
}
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink) {
|
||||
return;
|
||||
}
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
}
|
||||
}
|
||||
|
||||
private double GetTouchBoundWidth(TouchEventArgs e) {
|
||||
var args = e.GetTouchPoint(null).Bounds;
|
||||
double value;
|
||||
if (!Settings.Advanced.IsQuadIR) value = args.Width;
|
||||
else value = Math.Sqrt(args.Width * args.Height); //四边红外
|
||||
if (Settings.Advanced.IsSpecialScreen) value *= Settings.Advanced.TouchMultiplier;
|
||||
return value;
|
||||
}
|
||||
|
||||
//记录触摸设备ID
|
||||
private List<int> dec = new List<int>();
|
||||
|
||||
//中心点
|
||||
private Point centerPoint;
|
||||
private InkCanvasEditingMode lastInkCanvasEditingMode = InkCanvasEditingMode.Ink;
|
||||
private bool isSingleFingerDragMode = false;
|
||||
// 手掌擦相关变量
|
||||
private bool isPalmEraserActive;
|
||||
private InkCanvasEditingMode palmEraserLastEditingMode = InkCanvasEditingMode.Ink;
|
||||
private bool palmEraserLastIsHighlighter;
|
||||
private bool palmEraserWasEnabledBeforeMultiTouch;
|
||||
|
||||
private void inkCanvas_PreviewTouchDown(object sender, TouchEventArgs e) {
|
||||
// 橡皮状态下不做任何切换,直接return,保证橡皮可持续
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
return;
|
||||
}
|
||||
SetCursorBasedOnEditingMode(inkCanvas);
|
||||
|
||||
inkCanvas.CaptureTouch(e.TouchDevice);
|
||||
ViewboxFloatingBar.IsHitTestVisible = false;
|
||||
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
|
||||
|
||||
dec.Add(e.TouchDevice.Id);
|
||||
// Palm Eraser 逻辑
|
||||
if (Settings.Canvas.EnablePalmEraser && dec.Count >= 2 && !isPalmEraserActive) {
|
||||
var bounds = e.GetTouchPoint(inkCanvas).Bounds;
|
||||
double palmThreshold = 40; // 触摸面积阈值,可根据实际调整
|
||||
if (bounds.Width >= palmThreshold || bounds.Height >= palmThreshold) {
|
||||
// 记录当前编辑模式和高光状态
|
||||
palmEraserLastEditingMode = inkCanvas.EditingMode;
|
||||
palmEraserLastIsHighlighter = drawingAttributes.IsHighlighter;
|
||||
// 切换为橡皮擦
|
||||
EraserIcon_Click(null, null);
|
||||
isPalmEraserActive = true;
|
||||
}
|
||||
}
|
||||
//设备1个的时候,记录中心点
|
||||
if (dec.Count == 1) {
|
||||
var touchPoint = e.GetTouchPoint(inkCanvas);
|
||||
centerPoint = touchPoint.Position;
|
||||
|
||||
// 新增:几何绘制模式下,记录初始点
|
||||
if (drawingShapeMode != 0) {
|
||||
iniP = touchPoint.Position;
|
||||
}
|
||||
|
||||
//记录第一根手指点击时的 StrokeCollection
|
||||
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
|
||||
}
|
||||
@@ -288,22 +321,56 @@ namespace Ink_Canvas {
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.None ||
|
||||
inkCanvas.EditingMode == InkCanvasEditingMode.Select) return;
|
||||
lastInkCanvasEditingMode = inkCanvas.EditingMode;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void inkCanvas_PreviewTouchUp(object sender, TouchEventArgs e) {
|
||||
|
||||
// 橡皮状态下不做任何切换,直接return,保证橡皮可持续
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint && !isPalmEraserActive) {
|
||||
return;
|
||||
}
|
||||
inkCanvas.ReleaseAllTouchCaptures();
|
||||
ViewboxFloatingBar.IsHitTestVisible = true;
|
||||
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
|
||||
|
||||
// Palm Eraser 逻辑:所有点抬起后恢复原编辑模式
|
||||
dec.Remove(e.TouchDevice.Id);
|
||||
if (isPalmEraserActive && dec.Count == 0) {
|
||||
// 恢复高光状态
|
||||
drawingAttributes.IsHighlighter = palmEraserLastIsHighlighter;
|
||||
// 恢复编辑模式
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
|
||||
if (palmEraserLastEditingMode == InkCanvasEditingMode.Ink) {
|
||||
PenIcon_Click(null, null);
|
||||
} else if (palmEraserLastEditingMode == InkCanvasEditingMode.Select) {
|
||||
SymbolIconSelect_MouseUp(null, null);
|
||||
} else {
|
||||
inkCanvas.EditingMode = palmEraserLastEditingMode;
|
||||
}
|
||||
}
|
||||
isPalmEraserActive = false;
|
||||
}
|
||||
// 新增:几何绘制模式下,触摸抬手时自动落笔
|
||||
if (drawingShapeMode != 0) {
|
||||
var mouseArgs = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left)
|
||||
{
|
||||
RoutedEvent = MouseLeftButtonUpEvent,
|
||||
Source = inkCanvas
|
||||
};
|
||||
inkCanvas_MouseUp(inkCanvas, mouseArgs);
|
||||
}
|
||||
|
||||
//手势完成后切回之前的状态
|
||||
if (dec.Count > 1)
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.None)
|
||||
inkCanvas.EditingMode = lastInkCanvasEditingMode;
|
||||
dec.Remove(e.TouchDevice.Id);
|
||||
if (lastInkCanvasEditingMode != InkCanvasEditingMode.EraseByPoint) {
|
||||
inkCanvas.EditingMode = lastInkCanvasEditingMode;
|
||||
}
|
||||
inkCanvas.Opacity = 1;
|
||||
|
||||
if (dec.Count == 0)
|
||||
if (lastTouchDownStrokeCollection.Count() != inkCanvas.Strokes.Count() &&
|
||||
!(drawingShapeMode == 9 && !isFirstTouchCuboid)) {
|
||||
@@ -321,31 +388,17 @@ namespace Ink_Canvas {
|
||||
|
||||
private void Main_Grid_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) {
|
||||
if (e.Manipulators.Count() != 0) return;
|
||||
if (forceEraser) return;
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
if (drawingShapeMode == 0 && inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint) {
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
}
|
||||
}
|
||||
|
||||
// -- removed --
|
||||
//
|
||||
//private void inkCanvas_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
|
||||
//{
|
||||
// if (isInMultiTouchMode || !Settings.Gesture.IsEnableTwoFingerGesture || inkCanvas.Strokes.Count == 0 || dec.Count() < 2) return;
|
||||
// _currentCommitType = CommitReason.Manipulation;
|
||||
// StrokeCollection strokes = inkCanvas.GetSelectedStrokes();
|
||||
// if (strokes.Count != 0)
|
||||
// {
|
||||
// inkCanvas.Strokes.Replace(strokes, strokes.Clone());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// var originalStrokes = inkCanvas.Strokes;
|
||||
// var targetStrokes = originalStrokes.Clone();
|
||||
// originalStrokes.Replace(originalStrokes, targetStrokes);
|
||||
// }
|
||||
// _currentCommitType = CommitReason.UserInput;
|
||||
//}
|
||||
|
||||
private void Main_Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) {
|
||||
// 手掌擦时禁止移动/缩放
|
||||
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
|
||||
return;
|
||||
// 三指及以上禁止缩放
|
||||
bool disableScale = dec.Count >= 3;
|
||||
if (isInMultiTouchMode || !Settings.Gesture.IsEnableTwoFingerGesture) return;
|
||||
if ((dec.Count >= 2 && (Settings.PowerPointSettings.IsEnableTwoFingerGestureInPresentationMode ||
|
||||
StackPanelPPTControls.Visibility != Visibility.Visible ||
|
||||
@@ -370,7 +423,7 @@ namespace Ink_Canvas {
|
||||
|
||||
if (Settings.Gesture.IsEnableTwoFingerRotation)
|
||||
m.RotateAt(rotate, center.X, center.Y); // 旋转
|
||||
if (Settings.Gesture.IsEnableTwoFingerZoom)
|
||||
if (Settings.Gesture.IsEnableTwoFingerZoom && !disableScale)
|
||||
m.ScaleAt(scale.X, scale.Y, center.X, center.Y); // 缩放
|
||||
}
|
||||
|
||||
@@ -430,5 +483,62 @@ namespace Ink_Canvas {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 退出多指书写模式,恢复InkCanvas的TouchDown事件绑定
|
||||
private void ExitMultiTouchModeIfNeeded()
|
||||
{
|
||||
if (isInMultiTouchMode)
|
||||
{
|
||||
inkCanvas.StylusDown -= MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove -= MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp -= MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown -= MainWindow_TouchDown;
|
||||
inkCanvas.TouchDown += Main_Grid_TouchDown;
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint)
|
||||
{
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
|
||||
}
|
||||
// 保存非笔画元素(如图片)
|
||||
var preservedElements = PreserveNonStrokeElements();
|
||||
inkCanvas.Children.Clear();
|
||||
// 恢复非笔画元素
|
||||
RestoreNonStrokeElements(preservedElements);
|
||||
isInMultiTouchMode = false;
|
||||
// 关闭多指书写时,恢复手掌擦开关
|
||||
if (palmEraserWasEnabledBeforeMultiTouch) {
|
||||
Settings.Canvas.EnablePalmEraser = true;
|
||||
if (ToggleSwitchEnablePalmEraser != null)
|
||||
ToggleSwitchEnablePalmEraser.IsOn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 进入多指书写模式,绑定Main_Grid_TouchDown
|
||||
private void EnterMultiTouchModeIfNeeded()
|
||||
{
|
||||
if (!isInMultiTouchMode)
|
||||
{
|
||||
inkCanvas.StylusDown += MainWindow_StylusDown;
|
||||
inkCanvas.StylusMove += MainWindow_StylusMove;
|
||||
inkCanvas.StylusUp += MainWindow_StylusUp;
|
||||
inkCanvas.TouchDown += MainWindow_TouchDown;
|
||||
inkCanvas.TouchDown -= Main_Grid_TouchDown;
|
||||
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint)
|
||||
{
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
}
|
||||
// 保存非笔画元素(如图片)
|
||||
var preservedElements = PreserveNonStrokeElements();
|
||||
inkCanvas.Children.Clear();
|
||||
// 恢复非笔画元素
|
||||
RestoreNonStrokeElements(preservedElements);
|
||||
isInMultiTouchMode = true;
|
||||
// 启用多指书写时,自动禁用手掌擦
|
||||
palmEraserWasEnabledBeforeMultiTouch = Settings.Canvas.EnablePalmEraser;
|
||||
Settings.Canvas.EnablePalmEraser = false;
|
||||
if (ToggleSwitchEnablePalmEraser != null)
|
||||
ToggleSwitchEnablePalmEraser.IsOn = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms.VisualStyles;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media.Animation;
|
||||
using Hardcodet.Wpf.TaskbarNotification;
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using Application = System.Windows.Application;
|
||||
using ContextMenu = System.Windows.Controls.ContextMenu;
|
||||
using MenuItem = System.Windows.Controls.MenuItem;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
@@ -28,7 +25,7 @@ namespace Ink_Canvas
|
||||
(TextBlock)((SimpleStackPanel)((MenuItem)s.Items[s.Items.Count - 5]).Header).Children[0];
|
||||
var ResetFloatingBarPositionTrayIconMenuItem = (MenuItem)s.Items[s.Items.Count - 4];
|
||||
var HideICCMainWindowTrayIconMenuItem = (MenuItem)s.Items[s.Items.Count - 9];
|
||||
var mainWin = (MainWindow)Application.Current.MainWindow;
|
||||
var mainWin = (MainWindow)Current.MainWindow;
|
||||
if (mainWin.IsLoaded) {
|
||||
// 判斷是否在收納模式中
|
||||
if (mainWin.isFloatingBarFolded) {
|
||||
@@ -53,35 +50,50 @@ namespace Ink_Canvas
|
||||
}
|
||||
|
||||
private void CloseAppTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e) {
|
||||
var mainWin = (MainWindow)Application.Current.MainWindow;
|
||||
var mainWin = (MainWindow)Current.MainWindow;
|
||||
if (mainWin.IsLoaded) {
|
||||
App.IsAppExitByUser = true;
|
||||
Application.Current.Shutdown();
|
||||
IsAppExitByUser = true;
|
||||
Current.Shutdown();
|
||||
// mainWin.BtnExit_Click(null,null);
|
||||
}
|
||||
}
|
||||
|
||||
private void RestartAppTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e) {
|
||||
var mainWin = (MainWindow)Application.Current.MainWindow;
|
||||
var mainWin = (MainWindow)Current.MainWindow;
|
||||
if (mainWin.IsLoaded) {
|
||||
App.IsAppExitByUser = true;
|
||||
Application.Current.Shutdown();
|
||||
// mainWin.BtnExit_Click(null,null);
|
||||
IsAppExitByUser = true;
|
||||
|
||||
try {
|
||||
// 启动新实例,添加 -m 参数允许多实例启动
|
||||
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo();
|
||||
startInfo.FileName = exePath;
|
||||
startInfo.Arguments = "-m";
|
||||
startInfo.UseShellExecute = true;
|
||||
|
||||
// 启动进程但不等待
|
||||
Process.Start(startInfo);
|
||||
} catch (Exception ex) {
|
||||
LogHelper.NewLog($"重启程序时出错: {ex.Message}");
|
||||
}
|
||||
|
||||
// 退出当前实例
|
||||
Current.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceFullScreenTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e) {
|
||||
var mainWin = (MainWindow)Application.Current.MainWindow;
|
||||
var mainWin = (MainWindow)Current.MainWindow;
|
||||
if (mainWin.IsLoaded) {
|
||||
Ink_Canvas.MainWindow.MoveWindow(new WindowInteropHelper(mainWin).Handle, 0, 0,
|
||||
System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height, true);
|
||||
Ink_Canvas.MainWindow.ShowNewMessage($"已强制全屏化:{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width}x{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height}(缩放比例为{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth}x{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height / SystemParameters.PrimaryScreenHeight})");
|
||||
Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, true);
|
||||
Ink_Canvas.MainWindow.ShowNewMessage($"已强制全屏化:{Screen.PrimaryScreen.Bounds.Width}x{Screen.PrimaryScreen.Bounds.Height}(缩放比例为{Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth}x{Screen.PrimaryScreen.Bounds.Height / SystemParameters.PrimaryScreenHeight})");
|
||||
}
|
||||
}
|
||||
|
||||
private void FoldFloatingBarTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var mainWin = (MainWindow)Application.Current.MainWindow;
|
||||
var mainWin = (MainWindow)Current.MainWindow;
|
||||
if (mainWin.IsLoaded)
|
||||
if (mainWin.isFloatingBarFolded) mainWin.UnFoldFloatingBar_MouseUp(new object(),null);
|
||||
else mainWin.FoldFloatingBar_MouseUp(new object(),null);
|
||||
@@ -89,7 +101,7 @@ namespace Ink_Canvas
|
||||
|
||||
private void ResetFloatingBarPositionTrayIconMenuItem_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var mainWin = (MainWindow)Application.Current.MainWindow;
|
||||
var mainWin = (MainWindow)Current.MainWindow;
|
||||
if (mainWin.IsLoaded) {
|
||||
var isInPPTPresentationMode = false;
|
||||
Dispatcher.Invoke(() => {
|
||||
@@ -104,10 +116,10 @@ namespace Ink_Canvas
|
||||
|
||||
private void HideICCMainWindowTrayIconMenuItem_Checked(object sender, RoutedEventArgs e) {
|
||||
var mi = (MenuItem)sender;
|
||||
var mainWin = (MainWindow)Application.Current.MainWindow;
|
||||
var mainWin = (MainWindow)Current.MainWindow;
|
||||
if (mainWin.IsLoaded) {
|
||||
mainWin.Hide();
|
||||
var s = ((TaskbarIcon)Application.Current.Resources["TaskbarTrayIcon"]).ContextMenu;
|
||||
var s = ((TaskbarIcon)Current.Resources["TaskbarTrayIcon"]).ContextMenu;
|
||||
var ResetFloatingBarPositionTrayIconMenuItem = (MenuItem)s.Items[s.Items.Count - 4];
|
||||
var FoldFloatingBarTrayIconMenuItem = (MenuItem)s.Items[s.Items.Count - 5];
|
||||
var ForceFullScreenTrayIconMenuItem = (MenuItem)s.Items[s.Items.Count - 6];
|
||||
@@ -125,10 +137,10 @@ namespace Ink_Canvas
|
||||
|
||||
private void HideICCMainWindowTrayIconMenuItem_UnChecked(object sender, RoutedEventArgs e) {
|
||||
var mi = (MenuItem)sender;
|
||||
var mainWin = (MainWindow)Application.Current.MainWindow;
|
||||
var mainWin = (MainWindow)Current.MainWindow;
|
||||
if (mainWin.IsLoaded) {
|
||||
mainWin.Show();
|
||||
var s = ((TaskbarIcon)Application.Current.Resources["TaskbarTrayIcon"]).ContextMenu;
|
||||
var s = ((TaskbarIcon)Current.Resources["TaskbarTrayIcon"]).ContextMenu;
|
||||
var ResetFloatingBarPositionTrayIconMenuItem = (MenuItem)s.Items[s.Items.Count - 4];
|
||||
var FoldFloatingBarTrayIconMenuItem = (MenuItem)s.Items[s.Items.Count - 5];
|
||||
var ForceFullScreenTrayIconMenuItem = (MenuItem)s.Items[s.Items.Count - 6];
|
||||
|
||||
@@ -49,5 +49,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.6.3.0")]
|
||||
[assembly: AssemblyFileVersion("1.6.3.0")]
|
||||
[assembly: AssemblyVersion("1.7.5.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.5.0")]
|
||||
|
||||
@@ -8,10 +8,16 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.CodeDom.Compiler;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ink_Canvas.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
@@ -19,27 +25,27 @@ namespace Ink_Canvas.Properties {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[DebuggerNonUserCode()]
|
||||
[CompilerGenerated()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
private static ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
private static CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
internal static ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Ink_Canvas.Properties.Resources", typeof(Resources).Assembly);
|
||||
if (ReferenceEquals(resourceMan, null)) {
|
||||
ResourceManager temp = new ResourceManager("Ink_Canvas.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
@@ -50,8 +56,8 @@ namespace Ink_Canvas.Properties {
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
internal static CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
@@ -63,7 +69,7 @@ namespace Ink_Canvas.Properties {
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream.
|
||||
/// </summary>
|
||||
internal static System.IO.UnmanagedMemoryStream TimerDownNotice {
|
||||
internal static UnmanagedMemoryStream TimerDownNotice {
|
||||
get {
|
||||
return ResourceManager.GetStream("TimerDownNotice", resourceCulture);
|
||||
}
|
||||
|
||||
@@ -8,14 +8,18 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Configuration;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ink_Canvas.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.7.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
[CompilerGenerated()]
|
||||
[GeneratedCode("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.7.0.0")]
|
||||
internal sealed partial class Settings : ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
private static Settings defaultInstance = ((Settings)(Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ink_Canvas
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
public static class ChickenSoup {
|
||||
public static string[] OSUPlayerYuLu = new string[] {
|
||||
public static string[] OSUPlayerYuLu = {
|
||||
"澳洲原神,启动!",
|
||||
"一眼丁真,鉴定为玩osu!玩的",
|
||||
"喊MimosaM给我炒两菜",
|
||||
@@ -38,7 +32,7 @@ namespace Ink_Canvas
|
||||
"又有人2000了"
|
||||
};
|
||||
|
||||
public static string[] MingYanJingJu = new string[] {
|
||||
public static string[] MingYanJingJu = {
|
||||
"老骥伏枥,志在千里;烈士暮年,壮心不已",
|
||||
"有志者,事竟成",
|
||||
"有志者自有千方百计,无志者只感千难万难",
|
||||
@@ -104,7 +98,7 @@ namespace Ink_Canvas
|
||||
"有志不在年高,无志空活百岁",
|
||||
};
|
||||
|
||||
public static string[] GaoKaoPhrases = new string[] {
|
||||
public static string[] GaoKaoPhrases = {
|
||||
"以梦为马,不负韶华。祝你高考顺利,未来可期!",
|
||||
"高考顺利,愿你乘风破浪,前程似锦!",
|
||||
"愿你高考顺利,不负韶华,梦想成真!",
|
||||
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -1,4 +1,7 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ink_Canvas
|
||||
{
|
||||
@@ -33,29 +36,41 @@ namespace Ink_Canvas
|
||||
[JsonProperty("inkAlpha")]
|
||||
public double InkAlpha { get; set; } = 255;
|
||||
[JsonProperty("isShowCursor")]
|
||||
public bool IsShowCursor { get; set; } = false;
|
||||
public bool IsShowCursor { get; set; }
|
||||
[JsonProperty("inkStyle")]
|
||||
public int InkStyle { get; set; } = 0;
|
||||
public int InkStyle { get; set; }
|
||||
[JsonProperty("eraserSize")]
|
||||
public int EraserSize { get; set; } = 2;
|
||||
[JsonProperty("eraserType")]
|
||||
public int EraserType { get; set; } = 0; // 0 - 图标切换模式 1 - 面积擦 2 - 线条擦
|
||||
public int EraserType { get; set; } // 0 - 图标切换模式 1 - 面积擦 2 - 线条擦
|
||||
[JsonProperty("eraserShapeType")]
|
||||
public int EraserShapeType { get; set; } = 0; // 0 - 圆形擦 1 - 黑板擦
|
||||
public int EraserShapeType { get; set; } // 0 - 圆形擦 1 - 黑板擦
|
||||
[JsonProperty("hideStrokeWhenSelecting")]
|
||||
public bool HideStrokeWhenSelecting { get; set; } = true;
|
||||
[JsonProperty("fitToCurve")]
|
||||
public bool FitToCurve { get; set; } = true;
|
||||
public bool FitToCurve { get; set; } // 默认关闭原来的贝塞尔平滑
|
||||
[JsonProperty("useAdvancedBezierSmoothing")]
|
||||
public bool UseAdvancedBezierSmoothing { get; set; } = true; // 默认启用高级贝塞尔曲线平滑
|
||||
[JsonProperty("useAsyncInkSmoothing")]
|
||||
public bool UseAsyncInkSmoothing { get; set; } = true; // 默认启用异步墨迹平滑
|
||||
[JsonProperty("useHardwareAcceleration")]
|
||||
public bool UseHardwareAcceleration { get; set; } = true; // 默认启用硬件加速
|
||||
[JsonProperty("inkSmoothingQuality")]
|
||||
public int InkSmoothingQuality { get; set; } = 1; // 0-低质量高性能, 1-平衡, 2-高质量低性能
|
||||
[JsonProperty("maxConcurrentSmoothingTasks")]
|
||||
public int MaxConcurrentSmoothingTasks { get; set; } // 0表示自动检测CPU核心数
|
||||
[JsonProperty("clearCanvasAndClearTimeMachine")]
|
||||
public bool ClearCanvasAndClearTimeMachine { get; set; } = false;
|
||||
public bool ClearCanvasAndClearTimeMachine { get; set; }
|
||||
[JsonProperty("enablePressureTouchMode")]
|
||||
public bool EnablePressureTouchMode { get; set; } = false; // 是否启用压感触屏模式
|
||||
public bool EnablePressureTouchMode { get; set; } // 是否启用压感触屏模式
|
||||
[JsonProperty("disablePressure")]
|
||||
public bool DisablePressure { get; set; } = false; // 是否屏蔽压感
|
||||
public bool DisablePressure { get; set; } // 是否屏蔽压感
|
||||
[JsonProperty("autoStraightenLine")]
|
||||
public bool AutoStraightenLine { get; set; } = true; // 是否启用直线自动拉直
|
||||
[JsonProperty("autoStraightenLineThreshold")]
|
||||
public int AutoStraightenLineThreshold { get; set; } = 30; // 直线自动拉直的长度阈值(像素)
|
||||
public int AutoStraightenLineThreshold { get; set; } = 80; // 直线自动拉直的长度阈值(像素)
|
||||
[JsonProperty("highPrecisionLineStraighten")]
|
||||
public bool HighPrecisionLineStraighten { get; set; } = true; // 是否启用高精度直线拉直
|
||||
[JsonProperty("lineEndpointSnapping")]
|
||||
public bool LineEndpointSnapping { get; set; } = true; // 是否启用直线端点吸附
|
||||
[JsonProperty("lineEndpointSnappingThreshold")]
|
||||
@@ -64,8 +79,17 @@ namespace Ink_Canvas
|
||||
[JsonProperty("usingWhiteboard")]
|
||||
public bool UsingWhiteboard { get; set; }
|
||||
|
||||
[JsonProperty("customBackgroundColor")]
|
||||
public string CustomBackgroundColor { get; set; } = "#162924";
|
||||
|
||||
[JsonProperty("hyperbolaAsymptoteOption")]
|
||||
public OptionalOperation HyperbolaAsymptoteOption { get; set; } = OptionalOperation.Ask;
|
||||
[JsonProperty("isCompressPicturesUploaded")]
|
||||
public bool IsCompressPicturesUploaded { get; set; }
|
||||
[JsonProperty("enablePalmEraser")]
|
||||
public bool EnablePalmEraser { get; set; } = true;
|
||||
[JsonProperty("clearCanvasAlsoClearImages")]
|
||||
public bool ClearCanvasAlsoClearImages { get; set; } = true;
|
||||
}
|
||||
|
||||
public enum OptionalOperation
|
||||
@@ -90,9 +114,16 @@ namespace Ink_Canvas
|
||||
[JsonProperty("AutoSwitchTwoFingerGesture")]
|
||||
public bool AutoSwitchTwoFingerGesture { get; set; } = true;
|
||||
[JsonProperty("isEnableTwoFingerRotation")]
|
||||
public bool IsEnableTwoFingerRotation { get; set; } = false;
|
||||
public bool IsEnableTwoFingerRotation { get; set; }
|
||||
[JsonProperty("isEnableTwoFingerRotationOnSelection")]
|
||||
public bool IsEnableTwoFingerRotationOnSelection { get; set; } = false;
|
||||
public bool IsEnableTwoFingerRotationOnSelection { get; set; }
|
||||
}
|
||||
|
||||
// 更新通道枚举
|
||||
public enum UpdateChannel
|
||||
{
|
||||
Release,
|
||||
Beta
|
||||
}
|
||||
|
||||
public class Startup
|
||||
@@ -100,21 +131,21 @@ namespace Ink_Canvas
|
||||
[JsonProperty("isAutoUpdate")]
|
||||
public bool IsAutoUpdate { get; set; } = true;
|
||||
[JsonProperty("isAutoUpdateWithSilence")]
|
||||
public bool IsAutoUpdateWithSilence { get; set; } = false;
|
||||
public bool IsAutoUpdateWithSilence { get; set; }
|
||||
[JsonProperty("isAutoUpdateWithSilenceStartTime")]
|
||||
public string AutoUpdateWithSilenceStartTime { get; set; } = "00:00";
|
||||
public string AutoUpdateWithSilenceStartTime { get; set; } = "06:00";
|
||||
[JsonProperty("isAutoUpdateWithSilenceEndTime")]
|
||||
public string AutoUpdateWithSilenceEndTime { get; set; } = "00:00";
|
||||
|
||||
public string AutoUpdateWithSilenceEndTime { get; set; } = "22:00";
|
||||
[JsonProperty("updateChannel")]
|
||||
public UpdateChannel UpdateChannel { get; set; } = UpdateChannel.Release;
|
||||
[JsonProperty("skippedVersion")]
|
||||
public string SkippedVersion { get; set; } = "";
|
||||
[JsonProperty("isEnableNibMode")]
|
||||
public bool IsEnableNibMode { get; set; } = false;
|
||||
/*
|
||||
[JsonProperty("isAutoHideCanvas")]
|
||||
public bool IsAutoHideCanvas { get; set; } = true;
|
||||
[JsonProperty("isAutoEnterModeFinger")]
|
||||
public bool IsAutoEnterModeFinger { get; set; } = false;*/
|
||||
public bool IsEnableNibMode { get; set; }
|
||||
[JsonProperty("isFoldAtStartup")]
|
||||
public bool IsFoldAtStartup { get; set; } = false;
|
||||
public bool IsFoldAtStartup { get; set; }
|
||||
[JsonProperty("crashAction")]
|
||||
public int CrashAction { get; set; }
|
||||
}
|
||||
|
||||
public class Appearance
|
||||
@@ -122,13 +153,15 @@ namespace Ink_Canvas
|
||||
[JsonProperty("isEnableDisPlayNibModeToggler")]
|
||||
public bool IsEnableDisPlayNibModeToggler { get; set; } = true;
|
||||
[JsonProperty("isColorfulViewboxFloatingBar")]
|
||||
public bool IsColorfulViewboxFloatingBar { get; set; } = false;
|
||||
public bool IsColorfulViewboxFloatingBar { get; set; }
|
||||
// [JsonProperty("enableViewboxFloatingBarScaleTransform")]
|
||||
// public bool EnableViewboxFloatingBarScaleTransform { get; set; } = false;
|
||||
[JsonProperty("viewboxFloatingBarScaleTransformValue")]
|
||||
public double ViewboxFloatingBarScaleTransformValue { get; set; } = 1.0;
|
||||
[JsonProperty("floatingBarImg")]
|
||||
public int FloatingBarImg { get; set; } = 0;
|
||||
public int FloatingBarImg { get; set; }
|
||||
[JsonProperty("customFloatingBarImgs")]
|
||||
public List<CustomFloatingBarIcon> CustomFloatingBarImgs { get; set; } = new List<CustomFloatingBarIcon>();
|
||||
[JsonProperty("viewboxFloatingBarOpacityValue")]
|
||||
public double ViewboxFloatingBarOpacityValue { get; set; } = 1.0;
|
||||
[JsonProperty("enableTrayIcon")]
|
||||
@@ -136,7 +169,7 @@ namespace Ink_Canvas
|
||||
[JsonProperty("viewboxFloatingBarOpacityInPPTValue")]
|
||||
public double ViewboxFloatingBarOpacityInPPTValue { get; set; } = 0.5;
|
||||
[JsonProperty("enableViewboxBlackBoardScaleTransform")]
|
||||
public bool EnableViewboxBlackBoardScaleTransform { get; set; } = false;
|
||||
public bool EnableViewboxBlackBoardScaleTransform { get; set; }
|
||||
[JsonProperty("isTransparentButtonBackground")]
|
||||
public bool IsTransparentButtonBackground { get; set; } = true;
|
||||
[JsonProperty("isShowExitButton")]
|
||||
@@ -148,11 +181,11 @@ namespace Ink_Canvas
|
||||
[JsonProperty("enableChickenSoupInWhiteboardMode")]
|
||||
public bool EnableChickenSoupInWhiteboardMode { get; set; } = true;
|
||||
[JsonProperty("isShowHideControlButton")]
|
||||
public bool IsShowHideControlButton { get; set; } = false;
|
||||
public bool IsShowHideControlButton { get; set; }
|
||||
[JsonProperty("unFoldButtonImageType")]
|
||||
public int UnFoldButtonImageType { get; set; } = 0;
|
||||
public int UnFoldButtonImageType { get; set; }
|
||||
[JsonProperty("isShowLRSwitchButton")]
|
||||
public bool IsShowLRSwitchButton { get; set; } = false;
|
||||
public bool IsShowLRSwitchButton { get; set; }
|
||||
[JsonProperty("isShowQuickPanel")]
|
||||
public bool IsShowQuickPanel { get; set; } = true;
|
||||
[JsonProperty("chickenSoupSource")]
|
||||
@@ -160,7 +193,7 @@ namespace Ink_Canvas
|
||||
[JsonProperty("isShowModeFingerToggleSwitch")]
|
||||
public bool IsShowModeFingerToggleSwitch { get; set; } = true;
|
||||
[JsonProperty("theme")]
|
||||
public int Theme { get; set; } = 0;
|
||||
public int Theme { get; set; }
|
||||
}
|
||||
|
||||
public class PowerPointSettings
|
||||
@@ -176,11 +209,11 @@ namespace Ink_Canvas
|
||||
|
||||
// 0居中,+就是往上,-就是往下
|
||||
[JsonProperty("pptLSButtonPosition")]
|
||||
public int PPTLSButtonPosition { get; set; } = 0;
|
||||
public int PPTLSButtonPosition { get; set; }
|
||||
|
||||
// 0居中,+就是往上,-就是往下
|
||||
[JsonProperty("pptRSButtonPosition")]
|
||||
public int PPTRSButtonPosition { get; set; } = 0;
|
||||
public int PPTRSButtonPosition { get; set; }
|
||||
|
||||
[JsonProperty("pptSButtonsOption")]
|
||||
public int PPTSButtonsOption { get; set; } = 221;
|
||||
@@ -200,23 +233,27 @@ namespace Ink_Canvas
|
||||
[JsonProperty("isNoClearStrokeOnSelectWhenInPowerPoint")]
|
||||
public bool IsNoClearStrokeOnSelectWhenInPowerPoint { get; set; } = true;
|
||||
[JsonProperty("isShowStrokeOnSelectInPowerPoint")]
|
||||
public bool IsShowStrokeOnSelectInPowerPoint { get; set; } = false;
|
||||
public bool IsShowStrokeOnSelectInPowerPoint { get; set; }
|
||||
[JsonProperty("isAutoSaveStrokesInPowerPoint")]
|
||||
public bool IsAutoSaveStrokesInPowerPoint { get; set; } = true;
|
||||
[JsonProperty("isAutoSaveScreenShotInPowerPoint")]
|
||||
public bool IsAutoSaveScreenShotInPowerPoint { get; set; } = false;
|
||||
public bool IsAutoSaveScreenShotInPowerPoint { get; set; }
|
||||
[JsonProperty("isNotifyPreviousPage")]
|
||||
public bool IsNotifyPreviousPage { get; set; } = false;
|
||||
public bool IsNotifyPreviousPage { get; set; }
|
||||
[JsonProperty("isNotifyHiddenPage")]
|
||||
public bool IsNotifyHiddenPage { get; set; } = true;
|
||||
[JsonProperty("isNotifyAutoPlayPresentation")]
|
||||
public bool IsNotifyAutoPlayPresentation { get; set; } = true;
|
||||
[JsonProperty("isEnableTwoFingerGestureInPresentationMode")]
|
||||
public bool IsEnableTwoFingerGestureInPresentationMode { get; set; } = false;
|
||||
public bool IsEnableTwoFingerGestureInPresentationMode { get; set; }
|
||||
[JsonProperty("isEnableFingerGestureSlideShowControl")]
|
||||
public bool IsEnableFingerGestureSlideShowControl { get; set; } = true;
|
||||
[JsonProperty("isSupportWPS")]
|
||||
public bool IsSupportWPS { get; set; } = true;
|
||||
public bool IsSupportWPS { get; set; }
|
||||
[JsonProperty("enableWppProcessKill")]
|
||||
public bool EnableWppProcessKill { get; set; } = true;
|
||||
[JsonProperty("isAlwaysGoToFirstPageOnReenter")]
|
||||
public bool IsAlwaysGoToFirstPageOnReenter { get; set; }
|
||||
}
|
||||
|
||||
public class Automation
|
||||
@@ -240,120 +277,132 @@ namespace Ink_Canvas
|
||||
|| IsAutoFoldInYiYunVisualPresenter
|
||||
|| IsAutoFoldInMaxHubWhiteboard;
|
||||
|
||||
[JsonProperty("isAutoEnterAnnotationModeWhenExitFoldMode")]
|
||||
public bool IsAutoEnterAnnotationModeWhenExitFoldMode { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInEasiNote")]
|
||||
public bool IsAutoFoldInEasiNote { get; set; } = false;
|
||||
public bool IsAutoFoldInEasiNote { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInEasiNoteIgnoreDesktopAnno")]
|
||||
public bool IsAutoFoldInEasiNoteIgnoreDesktopAnno { get; set; } = false;
|
||||
public bool IsAutoFoldInEasiNoteIgnoreDesktopAnno { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInEasiCamera")]
|
||||
public bool IsAutoFoldInEasiCamera { get; set; } = false;
|
||||
public bool IsAutoFoldInEasiCamera { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInEasiNote3")]
|
||||
public bool IsAutoFoldInEasiNote3 { get; set; } = false;
|
||||
public bool IsAutoFoldInEasiNote3 { get; set; }
|
||||
[JsonProperty("isAutoFoldInEasiNote3C")]
|
||||
public bool IsAutoFoldInEasiNote3C { get; set; } = false;
|
||||
public bool IsAutoFoldInEasiNote3C { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInEasiNote5C")]
|
||||
public bool IsAutoFoldInEasiNote5C { get; set; } = false;
|
||||
public bool IsAutoFoldInEasiNote5C { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInSeewoPincoTeacher")]
|
||||
public bool IsAutoFoldInSeewoPincoTeacher { get; set; } = false;
|
||||
public bool IsAutoFoldInSeewoPincoTeacher { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInHiteTouchPro")]
|
||||
public bool IsAutoFoldInHiteTouchPro { get; set; } = false;
|
||||
public bool IsAutoFoldInHiteTouchPro { get; set; }
|
||||
[JsonProperty("isAutoFoldInHiteLightBoard")]
|
||||
public bool IsAutoFoldInHiteLightBoard { get; set; } = false;
|
||||
public bool IsAutoFoldInHiteLightBoard { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInHiteCamera")]
|
||||
public bool IsAutoFoldInHiteCamera { get; set; } = false;
|
||||
public bool IsAutoFoldInHiteCamera { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInWxBoardMain")]
|
||||
public bool IsAutoFoldInWxBoardMain { get; set; } = false;
|
||||
public bool IsAutoFoldInWxBoardMain { get; set; }
|
||||
/*
|
||||
[JsonProperty("isAutoFoldInZySmartBoard")]
|
||||
public bool IsAutoFoldInZySmartBoard { get; set; } = false;
|
||||
*/
|
||||
[JsonProperty("isAutoFoldInOldZyBoard")]
|
||||
public bool IsAutoFoldInOldZyBoard { get; set; } = false;
|
||||
public bool IsAutoFoldInOldZyBoard { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInMSWhiteboard")]
|
||||
public bool IsAutoFoldInMSWhiteboard { get; set; } = false;
|
||||
public bool IsAutoFoldInMSWhiteboard { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInAdmoxWhiteboard")]
|
||||
public bool IsAutoFoldInAdmoxWhiteboard { get; set; } = false;
|
||||
public bool IsAutoFoldInAdmoxWhiteboard { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInAdmoxBooth")]
|
||||
public bool IsAutoFoldInAdmoxBooth { get; set; } = false;
|
||||
public bool IsAutoFoldInAdmoxBooth { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInQPoint")]
|
||||
public bool IsAutoFoldInQPoint { get; set; } = false;
|
||||
public bool IsAutoFoldInQPoint { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInYiYunVisualPresenter")]
|
||||
public bool IsAutoFoldInYiYunVisualPresenter { get; set; } = false;
|
||||
public bool IsAutoFoldInYiYunVisualPresenter { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInMaxHubWhiteboard")]
|
||||
public bool IsAutoFoldInMaxHubWhiteboard { get; set; } = false;
|
||||
public bool IsAutoFoldInMaxHubWhiteboard { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldInPPTSlideShow")]
|
||||
public bool IsAutoFoldInPPTSlideShow { get; set; } = false;
|
||||
public bool IsAutoFoldInPPTSlideShow { get; set; }
|
||||
|
||||
[JsonProperty("isAutoFoldAfterPPTSlideShow")]
|
||||
public bool IsAutoFoldAfterPPTSlideShow { get; set; }
|
||||
|
||||
[JsonProperty("isAutoKillPptService")]
|
||||
public bool IsAutoKillPptService { get; set; } = false;
|
||||
public bool IsAutoKillPptService { get; set; }
|
||||
|
||||
[JsonProperty("isAutoKillEasiNote")]
|
||||
public bool IsAutoKillEasiNote { get; set; } = false;
|
||||
public bool IsAutoKillEasiNote { get; set; }
|
||||
|
||||
[JsonProperty("isAutoKillHiteAnnotation")]
|
||||
public bool IsAutoKillHiteAnnotation { get; set; } = false;
|
||||
public bool IsAutoKillHiteAnnotation { get; set; }
|
||||
|
||||
[JsonProperty("isAutoKillVComYouJiao")]
|
||||
public bool IsAutoKillVComYouJiao { get; set; } = false;
|
||||
public bool IsAutoKillVComYouJiao { get; set; }
|
||||
|
||||
[JsonProperty("isAutoKillSeewoLauncher2DesktopAnnotation")]
|
||||
public bool IsAutoKillSeewoLauncher2DesktopAnnotation { get; set; } = false;
|
||||
public bool IsAutoKillSeewoLauncher2DesktopAnnotation { get; set; }
|
||||
|
||||
[JsonProperty("isAutoKillInkCanvas")]
|
||||
public bool IsAutoKillInkCanvas { get; set; } = false;
|
||||
public bool IsAutoKillInkCanvas { get; set; }
|
||||
|
||||
[JsonProperty("isAutoKillICA")]
|
||||
public bool IsAutoKillICA { get; set; } = false;
|
||||
public bool IsAutoKillICA { get; set; }
|
||||
|
||||
[JsonProperty("isAutoKillIDT")]
|
||||
public bool IsAutoKillIDT { get; set; } = false;
|
||||
public bool IsAutoKillIDT { get; set; }
|
||||
|
||||
[JsonProperty("isSaveScreenshotsInDateFolders")]
|
||||
public bool IsSaveScreenshotsInDateFolders { get; set; } = false;
|
||||
public bool IsSaveScreenshotsInDateFolders { get; set; }
|
||||
|
||||
[JsonProperty("isAutoSaveStrokesAtScreenshot")]
|
||||
public bool IsAutoSaveStrokesAtScreenshot { get; set; } = false;
|
||||
public bool IsAutoSaveStrokesAtScreenshot { get; set; }
|
||||
|
||||
[JsonProperty("isAutoSaveStrokesAtClear")]
|
||||
public bool IsAutoSaveStrokesAtClear { get; set; } = false;
|
||||
public bool IsAutoSaveStrokesAtClear { get; set; }
|
||||
|
||||
[JsonProperty("isAutoClearWhenExitingWritingMode")]
|
||||
public bool IsAutoClearWhenExitingWritingMode { get; set; } = false;
|
||||
public bool IsAutoClearWhenExitingWritingMode { get; set; }
|
||||
|
||||
[JsonProperty("minimumAutomationStrokeNumber")]
|
||||
public int MinimumAutomationStrokeNumber { get; set; } = 0;
|
||||
public int MinimumAutomationStrokeNumber { get; set; }
|
||||
|
||||
[JsonProperty("autoSavedStrokesLocation")]
|
||||
public string AutoSavedStrokesLocation = @"D:\Ink Canvas";
|
||||
public string AutoSavedStrokesLocation = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "saves");
|
||||
|
||||
[JsonProperty("autoDelSavedFiles")]
|
||||
public bool AutoDelSavedFiles = false;
|
||||
public bool AutoDelSavedFiles;
|
||||
|
||||
[JsonProperty("autoDelSavedFilesDaysThreshold")]
|
||||
public int AutoDelSavedFilesDaysThreshold = 15;
|
||||
|
||||
[JsonProperty("isSaveFullPageStrokes")]
|
||||
public bool IsSaveFullPageStrokes;
|
||||
|
||||
[JsonProperty("isAutoEnterAnnotationAfterKillHite")]
|
||||
public bool IsAutoEnterAnnotationAfterKillHite { get; set; }
|
||||
}
|
||||
|
||||
public class Advanced
|
||||
{
|
||||
[JsonProperty("isSpecialScreen")]
|
||||
public bool IsSpecialScreen { get; set; } = false;
|
||||
public bool IsSpecialScreen { get; set; }
|
||||
|
||||
[JsonProperty("isQuadIR")]
|
||||
public bool IsQuadIR { get; set; } = false;
|
||||
public bool IsQuadIR { get; set; }
|
||||
|
||||
[JsonProperty("touchMultiplier")]
|
||||
public double TouchMultiplier { get; set; } = 0.25;
|
||||
@@ -365,34 +414,43 @@ namespace Ink_Canvas
|
||||
public int FingerModeBoundsWidth { get; set; } = 30;
|
||||
|
||||
[JsonProperty("eraserBindTouchMultiplier")]
|
||||
public bool EraserBindTouchMultiplier { get; set; } = false;
|
||||
public bool EraserBindTouchMultiplier { get; set; }
|
||||
|
||||
[JsonProperty("isLogEnabled")]
|
||||
public bool IsLogEnabled { get; set; } = true;
|
||||
|
||||
[JsonProperty("isSaveLogByDate")]
|
||||
public bool IsSaveLogByDate { get; set; } = true;
|
||||
|
||||
[JsonProperty("isEnableFullScreenHelper")]
|
||||
public bool IsEnableFullScreenHelper { get; set; } = false;
|
||||
public bool IsEnableFullScreenHelper { get; set; }
|
||||
|
||||
[JsonProperty("isEnableEdgeGestureUtil")]
|
||||
public bool IsEnableEdgeGestureUtil { get; set; } = false;
|
||||
public bool IsEnableEdgeGestureUtil { get; set; }
|
||||
|
||||
[JsonProperty("edgeGestureUtilOnlyAffectBlackboardMode")]
|
||||
public bool EdgeGestureUtilOnlyAffectBlackboardMode { get; set; } = false;
|
||||
public bool EdgeGestureUtilOnlyAffectBlackboardMode { get; set; }
|
||||
|
||||
[JsonProperty("isEnableForceFullScreen")]
|
||||
public bool IsEnableForceFullScreen { get; set; } = false;
|
||||
public bool IsEnableForceFullScreen { get; set; }
|
||||
|
||||
[JsonProperty("isEnableResolutionChangeDetection")]
|
||||
public bool IsEnableResolutionChangeDetection { get; set; } = false;
|
||||
public bool IsEnableResolutionChangeDetection { get; set; }
|
||||
|
||||
[JsonProperty("isEnableDPIChangeDetection")]
|
||||
public bool IsEnableDPIChangeDetection { get; set; } = false;
|
||||
public bool IsEnableDPIChangeDetection { get; set; }
|
||||
|
||||
[JsonProperty("isSecondConfirmWhenShutdownApp")]
|
||||
public bool IsSecondConfirmWhenShutdownApp { get; set; } = false;
|
||||
public bool IsSecondConfirmWhenShutdownApp { get; set; }
|
||||
|
||||
[JsonProperty("isEnableAvoidFullScreenHelper")]
|
||||
public bool IsEnableAvoidFullScreenHelper { get; set; } = false;
|
||||
public bool IsEnableAvoidFullScreenHelper { get; set; }
|
||||
|
||||
[JsonProperty("isAutoBackupBeforeUpdate")]
|
||||
public bool IsAutoBackupBeforeUpdate { get; set; } = true;
|
||||
|
||||
[JsonProperty("isNoFocusMode")]
|
||||
public bool IsNoFocusMode { get; set; } = true;
|
||||
}
|
||||
|
||||
public class InkToShape
|
||||
@@ -400,9 +458,9 @@ namespace Ink_Canvas
|
||||
[JsonProperty("isInkToShapeEnabled")]
|
||||
public bool IsInkToShapeEnabled { get; set; } = true;
|
||||
[JsonProperty("isInkToShapeNoFakePressureRectangle")]
|
||||
public bool IsInkToShapeNoFakePressureRectangle { get; set; } = false;
|
||||
public bool IsInkToShapeNoFakePressureRectangle { get; set; }
|
||||
[JsonProperty("isInkToShapeNoFakePressureTriangle")]
|
||||
public bool IsInkToShapeNoFakePressureTriangle { get; set; } = false;
|
||||
public bool IsInkToShapeNoFakePressureTriangle { get; set; }
|
||||
[JsonProperty("isInkToShapeTriangle")]
|
||||
public bool IsInkToShapeTriangle { get; set; } = true;
|
||||
[JsonProperty("isInkToShapeRectangle")]
|
||||
@@ -410,17 +468,59 @@ namespace Ink_Canvas
|
||||
[JsonProperty("isInkToShapeRounded")]
|
||||
public bool IsInkToShapeRounded { get; set; } = true;
|
||||
[JsonProperty("lineStraightenSensitivity")]
|
||||
public double LineStraightenSensitivity { get; set; } = 0.10; // 直线检测灵敏度,值越小越严格(0.05-0.75)
|
||||
public double LineStraightenSensitivity { get; set; } = 0.20; // 直线检测灵敏度,值越小越严格(0.05-2.0)
|
||||
}
|
||||
|
||||
public class RandSettings {
|
||||
[JsonProperty("displayRandWindowNamesInputBtn")]
|
||||
public bool DisplayRandWindowNamesInputBtn { get; set; } = false;
|
||||
public bool DisplayRandWindowNamesInputBtn { get; set; }
|
||||
[JsonProperty("randWindowOnceCloseLatency")]
|
||||
public double RandWindowOnceCloseLatency { get; set; } = 2.5;
|
||||
[JsonProperty("randWindowOnceMaxStudents")]
|
||||
public int RandWindowOnceMaxStudents { get; set; } = 10;
|
||||
[JsonProperty("showRandomAndSingleDraw")]
|
||||
public bool ShowRandomAndSingleDraw { get; set; } = true;
|
||||
[JsonProperty("directCallCiRand")]
|
||||
public bool DirectCallCiRand { get; set; }
|
||||
[JsonProperty("selectedBackgroundIndex")]
|
||||
public int SelectedBackgroundIndex { get; set; }
|
||||
[JsonProperty("customPickNameBackgrounds")]
|
||||
public List<CustomPickNameBackground> CustomPickNameBackgrounds { get; set; } = new List<CustomPickNameBackground>();
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomPickNameBackground
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("filePath")]
|
||||
public string FilePath { get; set; }
|
||||
|
||||
public CustomPickNameBackground(string name, string filePath)
|
||||
{
|
||||
Name = name;
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
// 用于JSON序列化
|
||||
public CustomPickNameBackground() { }
|
||||
}
|
||||
|
||||
public class CustomFloatingBarIcon
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("filePath")]
|
||||
public string FilePath { get; set; }
|
||||
|
||||
public CustomFloatingBarIcon(string name, string filePath)
|
||||
{
|
||||
Name = name;
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
// 用于JSON序列化
|
||||
public CustomFloatingBarIcon() { }
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,44 @@
|
||||
<Window x:Class="Ink_Canvas.AddCustomIconWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||
xmlns:local="clr-namespace:Ink_Canvas"
|
||||
mc:Ignorable="d"
|
||||
Title="添加自定义图标" Height="500" Width="750" WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="添加自定义浮动栏图标" FontSize="18" FontWeight="Bold" Margin="0,0,0,15" Grid.Row="0"/>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,20">
|
||||
<TextBlock Text="选择图标文件:" VerticalAlignment="Center" FontSize="14"/>
|
||||
<TextBox Name="IconPathTextBox" Width="220" IsReadOnly="True" Margin="10,0" Height="25"/>
|
||||
<Button Content="浏览..." Click="BrowseButton_Click" Width="80" Height="45"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,0,0,20">
|
||||
<TextBlock Text="图标名称:" VerticalAlignment="Center" FontSize="14"/>
|
||||
<TextBox Name="IconNameTextBox" Width="280" Margin="28,0,0,0" Height="25"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="3" Text="预览:" Margin="0,0,0,8" FontSize="14"/>
|
||||
|
||||
<Border Grid.Row="4" BorderBrush="#CCCCCC" BorderThickness="1" Padding="8" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<Image Name="IconPreviewImage" Width="72" Height="72" Stretch="Uniform"/>
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,25,0,0">
|
||||
<Button Content="取消" Width="100" Height="30" Click="CancelButton_Click" Margin="0,0,15,0"/>
|
||||
<Button Name="SaveButton" Content="保存" Width="100" Height="30" Click="SaveButton_Click" IsEnabled="False"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||