Compare commits

...

170 Commits

Author SHA1 Message Date
Lightczx
957b63ac32 1.7.13 package 2023-10-30 19:33:59 +08:00
Masterain
3af8f356cc New translations sh.resx (English) 2023-10-30 19:33:58 +08:00
Masterain
05608cee30 New translations sh.resx (Chinese Traditional) 2023-10-30 19:33:58 +08:00
Masterain
f8d7f72679 New translations sh.resx (Korean) 2023-10-30 19:33:58 +08:00
Masterain
1564f2a97e New translations sh.resx (Japanese) 2023-10-30 19:33:58 +08:00
Masterain
1f5619baa0 New translations sh.resx (Japanese) 2023-10-30 19:33:58 +08:00
Lightczx
7406f5822f ui/ux 2023-10-30 19:33:58 +08:00
Lightczx
38cffe4dc3 remove hutao user changed message 2023-10-30 19:33:27 +08:00
DismissedLight
ba4a7c1447 fix #903 2023-10-30 19:33:27 +08:00
DismissedLight
a03edeef3f implement #911 2023-10-30 19:33:27 +08:00
DismissedLight
981d2d74ee built in resx generator 2023-10-30 19:33:27 +08:00
Lightczx
27aa404e52 roslyn generated resx 2023-10-30 19:33:08 +08:00
Lightczx
78a56ec912 fix #431 input crash 2023-10-30 19:32:38 +08:00
Masterain
8f25769527 New translations sh.resx (English) 2023-10-30 19:32:38 +08:00
Masterain
85e31d122d New translations sh.resx (Chinese Traditional) 2023-10-30 19:32:38 +08:00
Masterain
65191d91bd New translations sh.resx (Korean) 2023-10-30 19:32:38 +08:00
Masterain
c885a6f220 New translations sh.resx (Japanese) 2023-10-30 19:32:38 +08:00
Masterain
3d0ea2dd4e New translations sh.resx (English) 2023-10-30 19:32:38 +08:00
Masterain
9d2e5896e6 New translations sh.resx (English) 2023-10-30 19:32:38 +08:00
Masterain
d376d9b3f6 New translations sh.resx (Chinese Traditional) 2023-10-30 19:32:38 +08:00
Masterain
449ae4499b New translations sh.resx (Korean) 2023-10-30 19:32:38 +08:00
Masterain
2d59fcfb21 New translations sh.resx (Japanese) 2023-10-30 19:32:37 +08:00
Masterain
5600d1fe9e New translations sh.resx (English) 2023-10-30 19:32:37 +08:00
Masterain
02ee082bd0 New translations sh.resx (Japanese) 2023-10-30 19:32:37 +08:00
Masterain
f4a1914735 New translations sh.resx (English) 2023-10-30 19:32:37 +08:00
Masterain
df8f317020 New translations sh.resx (Chinese Traditional) 2023-10-30 19:32:37 +08:00
Masterain
d09e4c3035 New translations sh.resx (Korean) 2023-10-30 19:32:37 +08:00
Masterain
e0d43b0dfb New translations sh.resx (Japanese) 2023-10-30 19:32:37 +08:00
DismissedLight
805d8ed87d ignore designer file 2 2023-10-30 19:32:37 +08:00
DismissedLight
bbbe2eeb79 ignore designer file 1 2023-10-30 19:32:37 +08:00
DismissedLight
7b8cc1b4c3 impl #1021 2023-10-30 19:32:37 +08:00
Lightczx
7c357d3a1c typo fix 2023-10-30 19:32:37 +08:00
Lightczx
7fc4e1a0a3 implement #431 2023-10-30 19:32:37 +08:00
Lightczx
f29fa1e8a0 fix IsGameRunning 2023-10-30 19:32:37 +08:00
DismissedLight
9a54463d7d support server l10n 2023-10-30 19:32:37 +08:00
DismissedLight
ababf74473 fix #1041 2023-10-30 19:32:37 +08:00
Lightczx
ba7ba3af44 fix #899 2023-10-30 19:32:37 +08:00
Lightczx
91f6665849 fix #1023 2023-10-30 19:32:37 +08:00
Lightczx
154f6a1bb6 fix #1035 2023-10-30 19:32:37 +08:00
DismissedLight
45cca2185e launch page redo 2023-10-30 19:32:37 +08:00
DismissedLight
95631df4fa fix #925 2023-10-30 19:32:37 +08:00
Masterain
d8186082bf Update issue templates 2023-10-30 19:32:37 +08:00
Masterain
18d6ee214c Update .github configurations 2023-10-30 19:32:37 +08:00
DismissedLight
bfc967d244 1.7.11 hotfix package 2023-10-18 19:54:21 +08:00
DismissedLight
0c8f757d69 fix launch args 2023-10-18 19:54:21 +08:00
Lightczx
dda4e60813 fix #1028 2023-10-18 19:54:21 +08:00
Masterain
cc01269d9a Update FUNDING.yml 2023-10-18 19:54:20 +08:00
Masterain
b5706c316f Update FUNDING.yml 2023-10-18 19:54:20 +08:00
DismissedLight
bc0e2bd717 migrate to winappsdk notification 2023-10-17 22:13:46 +08:00
DismissedLight
7b8ebd86b1 1.7.10 package 2023-10-17 22:12:58 +08:00
DismissedLight
47b24286b1 Merge pull request #1024 from DGP-Studio/develop 2023-10-17 20:31:47 +08:00
Masterain
0e3e3b9e4a New Crowdin updates (#1019) 2023-10-17 20:31:15 +08:00
DismissedLight
85d7b22e11 auto select existed account when detecting 2023-10-17 20:29:14 +08:00
Lightczx
7caeb17788 use reference in picker factory 2023-10-17 14:51:41 +08:00
Lightczx
b11b90e9f1 re-impl #1020 2023-10-16 11:03:41 +08:00
DismissedLight
58643a60b5 fix bugs 2023-10-15 16:14:22 +08:00
DismissedLight
a29b487c26 impl #1020 2023-10-15 16:14:13 +08:00
DismissedLight
1bd6023e0a fix issues 2023-10-15 12:30:44 +08:00
DismissedLight
579173d464 webview lifetime 2023-10-14 19:29:28 +08:00
DismissedLight
9ed53e8c34 fix spiralabyss damage rank empty crash 2023-10-14 17:26:06 +08:00
DismissedLight
830556a043 fix ICoreWebView2_13 not supported 2023-10-14 00:12:14 +08:00
DismissedLight
7ba27e184f ignore invalid launch schemes 2023-10-13 23:17:08 +08:00
DismissedLight
9aa6a2b57b fix invalid geetest url crash 2023-10-13 22:53:32 +08:00
DismissedLight
5773902f4a fix #1001 2023-10-13 22:36:51 +08:00
DismissedLight
06c5bcad3e fix #1013 #1012 #1011 2023-10-13 21:42:36 +08:00
Lightczx
c0165c57fd support Send on sync context 2023-10-13 15:27:46 +08:00
DismissedLight
4e57520115 Update AvatarIds.cs 2023-10-12 22:55:35 +08:00
Masterain
17c3480dae New Crowdin updates (#1006) 2023-10-12 22:10:54 +08:00
DismissedLight
7d5faadbb5 1.7.9 hotfix package 2023-10-12 22:03:24 +08:00
DismissedLight
b7df968ea7 fix #1008 2023-10-12 21:33:35 +08:00
DismissedLight
5f6cc46774 Merge pull request #1009 from DGP-Studio/develop 2023-10-12 21:04:39 +08:00
Lightczx
a34b5a5101 style 2023-10-12 16:58:29 +08:00
Lightczx
457e3c4af2 LoadImageSurfaceAsync fix 2023-10-12 16:45:59 +08:00
Lightczx
26143079b3 app resource moved 2023-10-12 15:59:42 +08:00
DismissedLight
20ad9aec60 fix 1.7.7 log upload issue 2023-10-11 22:28:10 +08:00
DismissedLight
61dd098d95 fix #1003 2023-10-11 22:12:07 +08:00
DismissedLight
e7233fbf2a fix #1007 2023-10-11 20:50:21 +08:00
DismissedLight
8de526274a fix #1005 2023-10-10 22:28:53 +08:00
DismissedLight
5b1fb6e1dd move resources 2023-10-10 22:27:53 +08:00
DismissedLight
1584fd1428 fix mutiple display window init size incorrectness 2023-10-10 20:23:39 +08:00
Masterain
65179a340f Update README.md 2023-10-09 04:02:38 -07:00
DismissedLight
9f984b4a9b 1.7.7 package 2023-10-08 22:59:36 +08:00
DismissedLight
8a36bfb62c Merge pull request #978 from DGP-Studio/l10n_develop 2023-10-08 22:34:22 +08:00
DismissedLight
c9c8f6a5cf Merge pull request #998 from DGP-Studio/develop 2023-10-08 22:28:58 +08:00
DismissedLight
f9e1ef7ab5 fix #990 2023-10-08 22:26:40 +08:00
DismissedLight
be06a91e0f code style 2023-10-08 22:08:17 +08:00
DismissedLight
174757f94d adjust nullability annotation 2023-10-08 21:51:24 +08:00
Lightczx
1cd3ec0cdb experiment 2023-10-08 17:30:35 +08:00
Lightczx
7b17bb5a01 reset window size 2023-10-08 14:15:27 +08:00
Masterain
c3bab3c677 New translations sh.resx (Japanese) 2023-10-07 15:35:50 -07:00
DismissedLight
17f1c873ae shadow complete 2023-10-07 23:49:23 +08:00
Lightczx
313a17b7eb code style 2023-10-07 11:25:06 +08:00
Lightczx
9ad321e10d fix ci build 2023-10-07 11:15:49 +08:00
Masterain
f18dfc0baf New translations sh.resx (Japanese) 2023-10-06 15:28:34 -07:00
DismissedLight
16946c58fe fix minor request bug 2023-10-06 21:48:10 +08:00
DismissedLight
c7b931956c import reqrest 2023-10-06 21:33:09 +08:00
Masterain
6c0c5b4669 New translations sh.resx (English) 2023-10-05 15:28:01 -07:00
Masterain
0342805293 New translations sh.resx (Chinese Traditional) 2023-10-05 15:28:00 -07:00
Masterain
e0b1896336 New translations sh.resx (Korean) 2023-10-05 15:27:59 -07:00
Masterain
6d1958af4c New translations sh.resx (Japanese) 2023-10-05 15:27:58 -07:00
DismissedLight
42ab2c3000 Merge pull request #989 from Xhichn/develop 2023-10-05 12:29:01 +08:00
Xhichn
d0fb00dccd Impl GetInfoAsync 2023-10-05 10:39:00 +08:00
DismissedLight
5bc484db78 signIn improvement 2023-10-05 10:02:25 +08:00
DismissedLight
c665834f77 Merge pull request #988 from DGP-Studio/main 2023-10-05 09:41:00 +08:00
DismissedLight
81d73568a7 Merge pull request #965 from Xhichn/DailyReward 2023-10-05 09:37:54 +08:00
DismissedLight
c940ec7006 dailynote & spiralabyss adjust 2023-10-05 09:32:17 +08:00
Masterain
71e5e6f190 New translations sh.resx (English) 2023-10-04 15:21:20 -07:00
Masterain
3983eaec0f New translations sh.resx (Chinese Traditional) 2023-10-04 15:21:19 -07:00
Masterain
9392911547 New translations sh.resx (Korean) 2023-10-04 15:21:18 -07:00
Masterain
f3d1fda053 New translations sh.resx (Japanese) 2023-10-04 15:21:16 -07:00
DismissedLight
14c3a14c2d impl #902 2023-10-04 23:15:32 +08:00
DismissedLight
31568ffba9 correct naming 2023-10-04 22:20:44 +08:00
DismissedLight
3d3d004441 rename signin interface 2023-10-04 22:20:06 +08:00
Masterain
a40e5f2a80 Update Qodana configuration 2023-10-03 14:47:34 -07:00
Masterain
186da74704 Merge pull request #985 from Masterain98/main
Create CONTRIBUTING.md
2023-10-03 03:53:59 -07:00
Masterain
c532799eb1 Update Qodana configuration 2023-10-03 03:34:54 -07:00
Masterain
595e19a1df Update CONTRIBUTING.md 2023-10-03 03:24:59 -07:00
Masterain
c376c4e0e8 Create CONTRIBUTING.md
resolve #909
2023-10-03 02:46:18 -07:00
DismissedLight
07f0c37b2c attempt to fix desktop icon creation 2023-10-03 17:39:30 +08:00
DismissedLight
ecdaeccc42 view style 2023-10-03 15:14:44 +08:00
Masterain
64bbcdfc80 New translations sh.resx (English) 2023-10-02 20:29:13 -07:00
Masterain
59fccf0cba New translations sh.resx (Chinese Traditional) 2023-10-02 20:29:12 -07:00
Masterain
f860211632 New translations sh.resx (Korean) 2023-10-02 20:29:11 -07:00
Masterain
a15a8cfe2d New translations sh.resx (Japanese) 2023-10-02 20:29:10 -07:00
DismissedLight
38bed7318f fix #982 2023-10-02 15:42:56 +08:00
DismissedLight
e513b8b00b add string 2023-10-02 13:24:51 +08:00
DismissedLight
1e711172af fix #980 2023-10-02 13:23:45 +08:00
Masterain
8beb47fd2c New translations sh.resx (English) 2023-10-01 20:29:31 -07:00
DismissedLight
41ab80511e separate win32 to increate analyzer response speed 2023-10-01 16:25:39 +08:00
DismissedLight
318b6190d2 fix game convert config not save 2023-10-01 11:29:23 +08:00
Masterain
6e3dbd41c7 New translations sh.resx (English) 2023-09-30 20:10:41 -07:00
Masterain
911f667e98 New translations sh.resx (Chinese Traditional) 2023-09-30 20:10:40 -07:00
Masterain
db6537bbe4 New translations sh.resx (Korean) 2023-09-30 20:10:39 -07:00
Masterain
6b640b5e15 New translations sh.resx (Japanese) 2023-09-30 20:10:38 -07:00
DismissedLight
bfe9837b36 fix several issues 2023-10-01 11:08:24 +08:00
Xhichn
b8255e77a3 Typo fix 2023-09-28 11:42:48 +08:00
DismissedLight
500888c2d1 launch scheme fix 2023-09-27 23:54:05 +08:00
Xhichn
5141d4a567 HoYolab Daily Reward Support 2023-09-27 18:51:41 +08:00
Lightczx
a86f5e1b59 add more compatonly scheme 2023-09-27 17:20:27 +08:00
Lightczx
390b89fc82 fix multiple channel/subchannel crash & setting clear invalid cache path crash 2023-09-27 17:12:32 +08:00
DismissedLight
6d6d97c59a fix mihoyo login no login_ticket crash 2023-09-26 23:40:30 +08:00
DismissedLight
df23fe54d6 fix manual input gacha url no auth_appid crash 2023-09-26 23:31:55 +08:00
DismissedLight
8caef9af0c MultiChannel: [ChannelType:Bili] [SubChannel:Official] [IsOversea: False] 2023-09-26 23:28:58 +08:00
DismissedLight
aac0e268b0 fix hoyoverse login crash 2023-09-26 23:23:02 +08:00
DismissedLight
bafee4ba18 fix detect game account crash 2023-09-26 22:26:01 +08:00
DismissedLight
4a1dccd7ac update intrinsics 2023-09-26 21:49:18 +08:00
Lightczx
1f8e73f255 disable concurrent launch game 2023-09-26 14:16:03 +08:00
DismissedLight
53c1a0156d 1.7.6 package 2023-09-25 22:46:14 +08:00
DismissedLight
165b77aa80 Merge pull request #959 from DGP-Studio/develop 2023-09-25 22:32:54 +08:00
DismissedLight
47cd9807b2 Merge pull request #949 from DGP-Studio/l10n_develop 2023-09-25 22:31:29 +08:00
DismissedLight
cbaf1ef5ae Merge pull request #958 from DGP-Studio/develop 2023-09-25 22:30:36 +08:00
DismissedLight
c2d3bd1798 fix GI 4.1 metadata ccrash on spiralabyss & improve wiki avatar ui 2023-09-25 22:20:14 +08:00
Masterain
bfa5325a09 Create Qodana profile 2023-09-24 17:27:23 -07:00
DismissedLight
43aebf7d0c fix #956 2023-09-24 17:54:08 +08:00
DismissedLight
ffcfb8cd65 fix #948 2023-09-23 21:17:15 +08:00
DismissedLight
7d9612cf13 fix #865 2023-09-23 21:02:11 +08:00
DismissedLight
4fb997c8d0 fix non-elevated auto click 2023-09-23 15:11:08 +08:00
DismissedLight
6029acc7f1 mouse simulator 2023-09-23 14:55:47 +08:00
Lightczx
ef352303e7 remove statichutao 2023-09-22 17:31:00 +08:00
Lightczx
b5579aef6e static resouce download refactor and ui/ux improvement 2023-09-22 16:47:08 +08:00
Masterain
9c20e06387 New translations sh.resx (English) 2023-09-21 17:19:17 -07:00
Masterain
323e9fbcff New translations sh.resx (Chinese Traditional) 2023-09-21 17:19:16 -07:00
Masterain
10d947aa61 New translations sh.resx (Korean) 2023-09-21 17:19:15 -07:00
Masterain
c18581fc34 New translations sh.resx (Japanese) 2023-09-21 17:19:13 -07:00
DismissedLight
b72b5ddf91 refactor window controller 2023-09-21 22:45:40 +08:00
DismissedLight
2821f7f2af refine announcement color matching 2023-09-21 20:57:44 +08:00
Lightczx
427275d122 ui/ux 2023-09-21 17:30:42 +08:00
Lightczx
22393b612c fix #947 2023-09-21 15:03:11 +08:00
Masterain
ffc380f96d Update issue templates 2023-09-20 22:15:18 -07:00
DismissedLight
2a757dce51 UI/UX fine tuning 2023-09-20 23:50:24 +08:00
346 changed files with 9732 additions and 12531 deletions

4
.github/FUNDING.yml vendored
View File

@@ -1,8 +1,8 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
github: [DGP-Studio]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
open_collective: snaphutao
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry

View File

@@ -1,12 +1,12 @@
name: BUG Report 问题反馈
description: 告诉我们你的问题
name: 问题反馈
description: 通过这个议题向开发团队反馈你发现的程序中的问题
title: "[Bug]: 在这里填写一个合适的标题"
labels: ["BUG"]
labels: ["BUG", "priority:none"]
body:
- type: markdown
attributes:
value: |
> **请在上方设置一个合适的工单标题**
> **请在上方以一句话简短地概括你的问题作为标题**
> 请按下方的要求填写完整的问题表单,以便我们更快的定位问题。
- type: checkboxes
@@ -14,17 +14,16 @@ body:
attributes:
label: 检查清单
description: |-
请确保你已完整执行检查清单,否则你的 Issue 可能会被忽略
请确保你已完整执行检查清单,否则你的议题可能会被忽略
options:
- label: 并未完整阅读[胡桃工具箱文档](https://hut.ao/advanced/FAQ.html)
- label: 已阅读 Snap Hutao 文档中的[常见问题](https://hut.ao/advanced/FAQ.html)和[常见程序异常](https://hut.ao/advanced/exceptions.html),我的问题没有在文档中得到解答
required: true
- label: 知道文档站的导航栏中有**搜索功能**也没有搜索过相关关键词
- label: 我知道文档站的导航栏中有**搜索功能**且已经搜索过相关关键词
required: true
- label: 使用的操作系统是不[受支持的版本](https://hut.ao/quick-start.html#%E6%9C%80%E4%BD%8E%E7%B3%BB%E7%BB%9F%E8%A6%81%E6%B1%82)
- label: 我没有**通过搜索功能**确认其他人提出过相同或类似的问题
- label: 我不明白上述的勾选项是**一个有助于快速排查问题的检查清单**,而是随手确认的选项
- label: 的问题不是[已修复](https://github.com/DGP-Studio/Snap.Hutao/issues?q=is%3Aopen+is%3Aissue+label%3A%E5%B7%B2%E4%BF%AE%E5%A4%8D)的问题也不是一个别人已发布的**重复的**问题
required: true
- type: input
id: winver
@@ -32,7 +31,7 @@ body:
label: Windows 版本
description: |
`Win+R` 输入 `winver` 回车后在打开的窗口第二行可以找到
placeholder: 22621.1105
placeholder: 22000.556
validations:
required: true
@@ -52,6 +51,7 @@ body:
description: |
在胡桃工具箱的设置界面,你可以找到并复制你的设备 ID
如果你的问题涉及程序崩溃,请填写该项,这将有助于我们定位问题
如果你的程序已经无法启动,请下载并运行[此PowerShell脚本](https://github.com/DGP-Studio/ISSUE_TEMPLATES/releases/download/get_device_id/GetHutaoDeviceId.ps1),它将显示你的设备 ID
validations:
required: false
@@ -66,13 +66,14 @@ body:
- 角色信息面板
- 游戏启动器
- 实时便笺
- 养成计算
- 用户面板
- 养成计算
- 文件缓存
- 祈愿记录
- 玩家查询
- 胡桃数据库
- 用户界面
- 胡桃云
- 胡桃帐号
- 签到
- Wiki
- 公告
@@ -84,7 +85,7 @@ body:
id: what-happened
attributes:
label: 发生了什么?
description: 详细的描述问题发生前后的行为,以便我们解决问题
description: 详细的描述问题发生前后的行为,以便我们解决问题。**如果你的问题涉及程序崩溃,你应当检查 Windows 事件查看器,并将相关的 `.Net 错误`详情附上**
validations:
required: true
@@ -95,4 +96,13 @@ body:
description: 详细的描述你期望发生的行为,突出与目前(可能不正确的)行为的不同
validations:
required: false
- type: checkboxes
id: checklist-final
attributes:
label: 最后一步
description: 回顾你的回答
options:
- label: 我认为上述的描述已经足以详细,以允许开发人员能复现该问题
required: true

View File

@@ -0,0 +1,27 @@
name: 功能请求
description: 通过这个议题来向开发团队分享你的想法
title: "[Feat]: 在这里填写一个合适的标题"
labels: ["功能", "priority:none"]
assignees:
- Lightczx
body:
- type: markdown
attributes:
value: |
请按下方的要求填写完整的问题表单。
- type: textarea
id: back
attributes:
label: 背景与动机
description: 添加此功能的理由,如果你想要实现多个功能,请分别发起多个单独的议题
validations:
required: true
- type: textarea
id: req
attributes:
label: 想要实现或优化的功能
description: 详细的描述一下你想要的功能,描述的越具体,采纳的可能性越高
validations:
required: true

View File

@@ -1,5 +1,5 @@
name: 网络问题
description: 当网络问题影响到你的程序使用时
description: 通过这个议题来反馈网络问题
title: "[Network]: 在这里填写一个合适的标题"
labels: ["area-Network"]
assignees:
@@ -19,10 +19,10 @@ body:
description: |
停下!
**在填写下面的问题之前请先使用我们的网络诊断工具**
**这个工具将会生成一份报告,请将这份报告拖入下面的框中,让其与你的工单一起被上传提交**
**这个工具将会生成一份报告并加密压缩,请将这份报告拖入下面的框中,让其与你的工单一起被上传提交**
- 你可以点击下面的链接以下载网络诊断工具:
- [胡桃资源站](https://d.hut.ao/d/tools/network-diagnosis-hutao.exe)
- [GitHub](https://github.com/Masterain98/network-diagnosis-tool/releases/latest/download/network-diagnosis-hutao.exe)
- [GitHub](https://github.com/Masterain98/network-diagnosis-tool/releases/latest/download/SH-Network-Diagnosis.exe)
- [极狐 GitLab](https://jihulab.com/DGP-Studio/network-diagnosis-tool/-/jobs/11144011/artifacts/raw/SH-Network-Diagnosis.exe?inline=false)
validations:
required: true
@@ -60,7 +60,6 @@ body:
- 完全无法连接服务器
- 连接速度慢
- 获取到了不正确的页面或数据
- 客户端提示 429 Error
- 客户端图片下载错误
- 客户端图片预下载错误
- 其它
@@ -74,5 +73,12 @@ body:
description: 如果你在上一项中选择了`其它`或者你有更多信息需要提供,请在这里写下来
validations:
required: false
- type: checkboxes
id: checklist-final
attributes:
label: 最后一步
description: 检查你提交的议题
options:
- label: 我已经在该议题中上传了包含网络诊断报告的加密压缩包
required: true

View File

@@ -0,0 +1,108 @@
name: BUG Report [English Form]
description: Tell us what issue you get
title: "[ENG][Bug]: Place your Issue Title Here"
labels: ["BUG", "priority:none"]
body:
- type: markdown
attributes:
value: |
> **Please use one sentence to briefly describe your issue as title above**
> Please follow the instruction below to fill the form, so we can locate the issue quickly
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: |-
Please complete the checklist, otherwise your issue may be ignored
options:
- label: I have read [FAQ page](https://hut.ao/advanced/FAQ.html) and [Exception page](https://hut.ao/advanced/exceptions.html) in Snap Hutao document, and my issue is not answered
required: true
- label: I and tried **search feature** in Snap Hutao document site, and no associated article
required: true
- label: My issue is not a [fixed issue](https://github.com/DGP-Studio/Snap.Hutao/issues?q=is%3Aopen+is%3Aissue+label%3A%E5%B7%B2%E4%BF%AE%E5%A4%8D), and it's not a duplicated issue
required: true
- type: input
id: winver
attributes:
label: Windows Version
description: |
Use `Win+R` and input `winver`, Windows build version is usually at the second line
placeholder: e.g. 22000.556
validations:
required: true
- type: input
id: shver
attributes:
label: Snap Hutao Version
description: You can find the version in application's title bar
placeholder: e.g. 1.4.15.0
validations:
required: true
- type: input
id: deviceid
attributes:
label: Device ID
description: |
In Snap Hutao's settings page, you can find and copy your device ID
If your issue is about program crash, please fill this so we can dump the log and locate the source easier
If your program cannot startup, please download and run [this PowerShell script](https://github.com/DGP-Studio/ISSUE_TEMPLATES/releases/download/get_device_id/GetHutaoDeviceId.ps1), it will shows your device ID.
validations:
required: false
- type: dropdown
id: user-set-category
attributes:
label: Issue Category
description: Please select the most associated category of your issue
options:
- Installation and Environment
- Achievement
- My Character
- Game Launcher
- Realtime Note
- Develop Plan
- File Cache
- Wish Export
- Game Record
- Hutao Database
- User Interface
- Snap Hutao Cloud
- Snap Hutao Account
- Checkin
- Wiki
- Announcement
- Other
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: What Happened?
description: Describe your issue in detail to help us identify the issue. **If your issue is about program crash, you should check Windows Event Viewer, and attach associated `.Net Error` details here**
validations:
required: true
- type: textarea
id: what-expected
attributes:
label: What is expected?
description: Describe expected outcome, highlight the difference with current outcome
validations:
required: false
- type: checkboxes
id: checklist-final
attributes:
label: Last Step
description: Review your Form
options:
- label: I believe the description above is detail enough to allow developers to reproduce the issue
required: true

View File

@@ -0,0 +1,27 @@
name: Feature Request [English Form]
description: Tell us about your thought
title: "[Feat]: Place your title here"
labels: ["功能", "priority:none"]
assignees:
- Lightczx
body:
- type: markdown
attributes:
value: |
Please fill the form below
- type: textarea
id: back
attributes:
label: Background & Motivation
description: Reason why this feature is needed. If multiple features is requested, please open multiple issues for each of them.
validations:
required: true
- type: textarea
id: req
attributes:
label: Detail of the Feature
description: Descripbe the feaure in detail. The more detailed and convincing the desciprtion the more likyly feature will be accepted.
validations:
required: true

View File

@@ -0,0 +1,79 @@
name: Network Issue [English Form]
description: Submit this issue form when network issue affect your client experience
title: "[Network]: Place your title here"
labels: ["area-Network"]
assignees:
- Lightczx
- Masterain98
body:
- type: markdown
attributes:
value: |
**Please use one sentence to briefly describe your issue as title above**
**Please follow the instruction below to fill the form, so we can locate the issue quickly**
- type: textarea
id: network-diagnosis-report
attributes:
label: Submit Your Network Diagnosis Report
description: |
STOP HERE!
**Please run our network diagnosis tool before filling this form**
**The diagnosis tool will generate a report and add it into a password-protected archive. Drag the `.zip` archive to the box below so it can be uploaded.**
- Use the following link to download the Network Diagnosis Tool:
- [GitHub](https://github.com/Masterain98/network-diagnosis-tool/releases/latest/download/SH-Network-Diagnosis.exe)
- [JIHu GitLab](https://jihulab.com/DGP-Studio/network-diagnosis-tool/-/jobs/11144011/artifacts/raw/SH-Network-Diagnosis.exe?inline=false)
validations:
required: true
- type: input
id: user-geo-location
attributes:
label: Your Geographical Location
description: |
Description accurate to country
placeholder: USA
validations:
required: true
- type: input
id: user-isp
attributes:
label: Your ISP Name
description: |
Name of your Internet service provider
placeholder: AT&T
validations:
required: true
- type: dropdown
id: user-issue-category
attributes:
label: Issue Category
description: Select an issue category
options:
- Cannot connect to server completely
- Slow spped
- Fetched wrong page or data
- Image download error in the client
- Image set pre-download error (client welcome wizard process)
- Other
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: Your Issue (cont.)
description: If you selected `Other` in previous dropdown, please explain your issue in detail here.
validations:
required: false
- type: checkboxes
id: checklist-final
attributes:
label: One Last Step
description: Check your issue form
options:
- label: I confirm I have attached the network diagnosis report archive in the issue
required: true

View File

@@ -1,65 +0,0 @@
name: 圣遗物评分细则建议
description: 为圣遗物评分规则提供你的想法
title: "[Artifact Rating] 请在这里填写角色名称"
labels: area-AvatarInfo
assignees: Lightczx
body:
- type: markdown
attributes:
value: |
请按下方的要求填写完整的问题表单
- type: textarea
id: your-suggested-rule
attributes:
label: 评分细则
description: |
请修改下方表格中的**角色名称**和**各属性权重**,并在表格后添加合适的说明
你可以点击预览按钮preview来查看表格最终会显示出的内容
value: |
|项目|评分权重(0-100)|
|-----|-----|
|角色名称| 旅行者 |
|生命值| 10 |
|攻击力| 10 |
|防御力| 10 |
|暴击率| 10 |
|暴击伤害| 10 |
|元素精通| 10 |
|充能效率| 10 |
|治疗加成| 10 |
|元素伤害| 10 |
validations:
required: true
- type: dropdown
id: no-duplicated-dropdown
attributes:
label: 我确认当前没有其它的该角色的圣遗物评分细则建议
description: 如果有,你应该在已有的工单内回复以提出你的建议
options:
-
-
validations:
required: true
- type: dropdown
id: title-filled-dropdown
attributes:
label: 我确认已设置合适的标题
options:
-
-
validations:
required: true
- type: dropdown
id: all-filled-dropdown
attributes:
label: 我确认已完整填写表格
options:
-
-
validations:
required: true

View File

@@ -1,5 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: 胡桃工具箱 - 官方文档
- name: Snap Hutao 官方文档 / Snap Hutao Document
url: https://hut.ao
about: 请在提出问题前阅读文档
about: 请在提出问题前阅读文档 / Read the document before submit the issue
- name: 常见问题 / FAQ
url: https://hut.ao/advanced/FAQ.html
about: 常见的用户提出的问题 / Common questions asked by users
- name: 常见程序异常 / Common Program Exceptions
url: https://hut.ao/advanced/exceptions.html
about: 用户通常能自行解决这些问题 / Users may solve these problems by themselves

View File

@@ -1,28 +0,0 @@
name: Feature Request 功能请求
description: Tell us about your thought 告诉我们你的想法
title: "[Feat]: Place your title here 在这里填写一个合适的标题"
labels: ["功能"]
assignees:
- Lightczx
body:
- type: markdown
attributes:
value: |
Please fill the form below
请按下方的要求填写完整的问题表单。
- type: textarea
id: back
attributes:
label: Background & Motivation 背景与动机
description: Reason why this feature is needed. If multiple features is requested, please open multiple issues for each of them. 添加此功能的理由,如果你想要实现多个功能,请分别发起多个单独的 Issue
validations:
required: true
- type: textarea
id: req
attributes:
label: Detail of the Feature 想要实现或优化的功能
description: Descripbe the feaure in detail. The more detailed and convincing the desciprtion the more likyly feature will be accepted. 详细的描述一下你想要的功能,描述的越具体,采纳的可能性越高
validations:
required: true

6
.gitignore vendored
View File

@@ -10,11 +10,11 @@ src/Snap.Hutao/_ReSharper.Caches
src/Snap.Hutao/Snap.Hutao/bin/
src/Snap.Hutao/Snap.Hutao/obj/
src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
src/Snap.Hutao/Snap.Hutao/Snap.Hutao_TemporaryKey.pfx
src/Snap.Hutao/Snap.Hutao.Installer/bin/
src/Snap.Hutao/Snap.Hutao.Installer/obj/
src/Snap.Hutao/Snap.Hutao.Installer/Properties/PublishProfiles/FolderProfile.pubxml.user
src/Snap.Hutao/Snap.Hutao.Win32/bin/
src/Snap.Hutao/Snap.Hutao.Win32/obj/
src/Snap.Hutao/Snap.Hutao.SourceGeneration/bin/
src/Snap.Hutao/Snap.Hutao.SourceGeneration/obj/

72
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,72 @@
# Contribution Guide
## Contribute Your Code
### Setup Snap.Hutao Project
1. Download and install [Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)
2. Open Visual Studio Installer to complete Visual Studio installation
- You need to install `.NET desktop development`, `Desktop development with C++` and `Universal Windows Platform development` components
3. Install `Single-project MSIX Packaging Tools for VS 2022` provided by Microsoft in Visual Studio marketplace
4. Use git to clone the project `https://github.com/DGP-Studio/Snap.Hutao.git` to your local device
5. Switch git branch to `develop`
6. Open project solution with your Visual Studio and then you are ready to go
### Start Pull Request
- All code-related changes from authors' own branches are only allowed be merged to `develop` branch
- Please use [keywords](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests) to link your PR or commits with issues, so issues can be automatically closed once commits are merged into `main` branch.
### Test Binary Package
Once the code in updated in `develop` and `main` branches, an Azure Pipeline CI script will build the latest code to `Snap Hutao Alpha` package. Once the package is built, it will be released on [GitHub Release page](https://github.com/DGP-Studio/Snap.Hutao/releases) as a pre-released package.
You need to install [Snap.Hutao.CI.cer](https://github.com/DGP-Studio/Snap.Hutao/releases/download/2023.10.3.1/Snap.Hutao.CI.cer) certificate to your local machine, and then install the msix package in the release.
*If the latest release does not contains attached file, that means package is still in uploading process.
## Start New Issue
To help users solve problems faster and increase developers' efficiency in solving problems, Snap Hutao provides detailed documentation to explain common problems and issue templates to guide users to report program problems by submitting issues.
Before submitting a new issue, you should check the following pages:
- [FAQ](https://hut.ao/advanced/FAQ.html) Document
- [Common Program Exceptions ](https://hut.ao/en/advanced/exceptions.html)Document
- [Current Opened BUG Report Issues](https://github.com/DGP-Studio/Snap.Hutao/issues?q=is%3Aissue+is%3Aopen+label%3ABUG)
When starting a new issue, please use the issue templates:
- Describe your issue in details to help developers to reproduce the issue
- Your description of reproduction should be a step-by-step story
- If your issue is about program crash
- Remember to provide your Device ID
- Check Windows Event Viewer, and attach associated `.NET Error` details in the issue body
## Document Modification
Snap Hutao Document site is stored in repository [DGP-Studio/Snap.Hutao.Docs](https://github.com/DGP-Studio/Snap.Hutao.Docs), you can process the following steps to test the site in your local device:
1. Download and install [NodeJS 18](https://nodejs.org/en/download/)
2. Clone the repository
3. Run `npm install` in the root directory of the document project
4. Run `npm run docs:dev` to start test on 8080 port
### Localization
Snap.Hutao.Docs project structure is designed as multiple languages site. Each language has its independent folder under `docs` directory.
**If you wish to add another language document, you can [start an issue in document repository](https://github.com/DGP-Studio/Snap.Hutao.Docs/issues) to ask developer to setup an environment for you, or you can process the following steps by yourself:**
1. make a copy of `zh` folder, rename the new folder as the new language's code
2. Start your translation work in the new language folder
3. In `docs/.vuepress/sidebar` folder, duplicate `zh.ts` file
1. Rename the file to `{language_code}.ts`
2. In the line 4, change `/zh/` to `/{language_code}/`
3. Translate all `text` field
4. In `docs/.vuepress/navbar` folder, duplicate `zh.ts` file
1. Rename the file to `{language_code}.ts`
2. Replace all `/zh/` to `/{language_code}/`
3. Translate all `text` field
5. In `docs/.vuepress/config.ts`file, add your language information in `locales` and `plugins/docsearchPlugin/locales` dictionary
6. In `docs/.vuepress/theme.ts`file, add your language information in `locales` dictionary

View File

@@ -1,8 +1,8 @@
![](res/HutaoRepoBanner2.png)
胡桃工具箱是一个 Windows 平台的开源的原神工具箱,旨在帮助玩家获得更好的游戏体验; 它是对官方移动端工具的一种非破坏性功能扩展,为不习惯在移动端进行原神游戏的 PC 玩家提供一个在 Windows 平台下获得接近移动端功能权利的途径
胡桃工具箱是一款以 MIT 协议开源的原神工具箱,专为现代化 Windows 平台设计,旨在改善桌面端玩家的游戏体验。通过将既有的官方资源与开发团队设计的全新 功能相结合,它提供了一套完整且实用的工具集,且无需依赖任何移动设备。它不对游戏客户端进行任何破坏性修改以确保工具箱的安全性
Snap Hutao is an open-source Genshin Impact toolbox on Windows platform, aim to provide a better gaming experience for players. It's an nondestructive feature extension from Genshin Impact's official mobile application, to provide similar feature on desktop, to allow PC gamers gain deserved benefits from mobile platforms.
Snap Hutao is an open-source Genshin Impact toolkit under MIT license, designed for modern Windows platform to improve the gaming experience for desktop players. By combining existing official resources with new features designed by the development team, it provides a complete and useful set of tools without the need to rely on mobile devices. Snap Hutao does not take any destructive modification to the game client to ensure the security of the toolkit.
## 下载使用 / Download

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using System;
using System.Net.Http;
using System.Runtime.Serialization;
@@ -10,7 +11,18 @@ namespace Snap.Hutao.SourceGeneration.Automation;
[Generator(LanguageNames.CSharp)]
internal sealed class SaltConstantGenerator : IIncrementalGenerator
{
private static readonly HttpClient httpClient = new();
private static readonly HttpClient httpClient;
private static readonly Lazy<Response<SaltLatest>> lazySaltInfo;
static SaltConstantGenerator()
{
httpClient = new();
lazySaltInfo = new Lazy<Response<SaltLatest>>(() =>
{
string body = httpClient.GetStringAsync("https://internal.snapgenshin.cn/Archive/Salt/Latest").GetAwaiter().GetResult();
return JsonParser.FromJson<Response<SaltLatest>>(body)!;
});
}
public void Initialize(IncrementalGeneratorInitializationContext context)
{
@@ -19,8 +31,7 @@ internal sealed class SaltConstantGenerator : IIncrementalGenerator
private static void GenerateSaltContstants(IncrementalGeneratorPostInitializationContext context)
{
string body = httpClient.GetStringAsync("https://internal.snapgenshin.cn/Archive/Salt/Latest").GetAwaiter().GetResult();
Response<SaltLatest> saltInfo = JsonParser.FromJson<Response<SaltLatest>>(body)!;
Response<SaltLatest> saltInfo = lazySaltInfo.Value;
string code = $$"""
namespace Snap.Hutao.Web.Hoyolab;

View File

@@ -20,7 +20,6 @@ internal sealed class HttpClientGenerator : IIncrementalGenerator
private const string HttpClientConfiguration = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientConfiguration.";
private const string PrimaryHttpMessageHandlerAttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.PrimaryHttpMessageHandlerAttribute";
private const string UseDynamicSecretAttributeName = "Snap.Hutao.Web.Hoyolab.DynamicSecret.UseDynamicSecretAttribute";
private const string CRLF = "\r\n";
private static readonly DiagnosticDescriptor injectionShouldOmitDescriptor = new("SH201", "Injection 特性可以省略", "HttpClient 特性已将 {0} 注册为 Transient 服务", "Quality", DiagnosticSeverity.Warning, true);
@@ -134,11 +133,6 @@ internal sealed class HttpClientGenerator : IIncrementalGenerator
lineBuilder.Append(" })");
}
if (context.HasAttributeWithName(UseDynamicSecretAttributeName))
{
lineBuilder.Append(".AddHttpMessageHandler<DynamicSecretHandler>()");
}
lineBuilder.Append(';');
lines.Add(lineBuilder.ToString());

View File

@@ -50,6 +50,8 @@ internal class LocalizedEnumGenerator : IIncrementalGenerator
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Globalization;
namespace Snap.Hutao.Resource.Localization;
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(LocalizedEnumGenerator)}}", "1.0.0.0")]
@@ -79,7 +81,7 @@ internal class LocalizedEnumGenerator : IIncrementalGenerator
}
else
{
return SH.ResourceManager.GetString(key);
return SH.ResourceManager.GetString(key, CultureInfo.CurrentCulture);
}
}
@@ -102,7 +104,7 @@ internal class LocalizedEnumGenerator : IIncrementalGenerator
_ => string.Empty,
};
return SH.ResourceManager.GetString(key);
return SH.ResourceManager.GetString(key, CultureInfo.CurrentCulture);
}
}
""");

View File

@@ -0,0 +1,607 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;
namespace Snap.Hutao.SourceGeneration.Resx;
[Generator]
public sealed class ResxGenerator : IIncrementalGenerator
{
private static readonly DiagnosticDescriptor InvalidResx = new("SH401", "Couldn't parse Resx file", "Couldn't parse Resx file '{0}'", "ResxGenerator", DiagnosticSeverity.Warning, true);
private static readonly DiagnosticDescriptor InvalidPropertiesForNamespace = new("SH402", "Couldn't compute namespace", "Couldn't compute namespace for file '{0}'", "ResxGenerator", DiagnosticSeverity.Warning, true);
private static readonly DiagnosticDescriptor InvalidPropertiesForResourceName = new("SH403", "Couldn't compute resource name", "Couldn't compute resource name for file '{0}'", "ResxGenerator", DiagnosticSeverity.Warning, true);
private static readonly DiagnosticDescriptor InconsistentProperties = new("SH404", "Inconsistent properties", "Property '{0}' values for '{1}' are inconsistent", "ResxGenerator", DiagnosticSeverity.Warning, true);
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<(string? AssemblyName, bool SupportNullableReferenceTypes)> compilationProvider = context.CompilationProvider
.Select(static (compilation, cancellationToken) => (compilation.AssemblyName, SupportNullableReferenceTypes: compilation.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute") is not null));
IncrementalValueProvider<ImmutableArray<AdditionalText>> resxProvider = context.AdditionalTextsProvider
.Where(text => text.Path.EndsWith(".resx", StringComparison.OrdinalIgnoreCase))
.Collect();
context.RegisterSourceOutput(
source: context.AnalyzerConfigOptionsProvider.Combine(compilationProvider.Combine(resxProvider)),
action: (ctx, source) => Execute(ctx, source.Left, source.Right.Left.AssemblyName, source.Right.Left.SupportNullableReferenceTypes, source.Right.Right));
}
private static void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvider options, string? assemblyName, bool supportNullableReferenceTypes, ImmutableArray<AdditionalText> files)
{
// Group additional file by resource kind ((a.resx, a.en.resx, a.en-us.resx), (b.resx, b.en-us.resx))
List<IGrouping<string, AdditionalText>> resxGroups = files
.GroupBy(file => GetResourceName(file.Path), StringComparer.OrdinalIgnoreCase)
.OrderBy(x => x.Key, StringComparer.Ordinal)
.ToList();
foreach (IGrouping<string, AdditionalText>? resxGroug in resxGroups)
{
string? rootNamespaceConfiguration = GetMetadataValue(context, options, "RootNamespace", resxGroug);
string? projectDirConfiguration = GetMetadataValue(context, options, "ProjectDir", resxGroug);
string? namespaceConfiguration = GetMetadataValue(context, options, "Namespace", "DefaultResourcesNamespace", resxGroug);
string? resourceNameConfiguration = GetMetadataValue(context, options, "ResourceName", globalName: null, resxGroug);
string? classNameConfiguration = GetMetadataValue(context, options, "ClassName", globalName: null, resxGroug);
string rootNamespace = rootNamespaceConfiguration ?? assemblyName ?? "";
string projectDir = projectDirConfiguration ?? assemblyName ?? "";
string? defaultResourceName = ComputeResourceName(rootNamespace, projectDir, resxGroug.Key);
string? defaultNamespace = ComputeNamespace(rootNamespace, projectDir, resxGroug.Key);
string? ns = namespaceConfiguration ?? defaultNamespace;
string? resourceName = resourceNameConfiguration ?? defaultResourceName;
string className = classNameConfiguration ?? ToCSharpNameIdentifier(Path.GetFileName(resxGroug.Key));
if (ns == null)
{
context.ReportDiagnostic(Diagnostic.Create(InvalidPropertiesForNamespace, location: null, resxGroug.First().Path));
}
if (resourceName == null)
{
context.ReportDiagnostic(Diagnostic.Create(InvalidPropertiesForResourceName, location: null, resxGroug.First().Path));
}
List<ResxEntry>? entries = LoadResourceFiles(context, resxGroug);
string content = $"""
// Debug info:
// key: {resxGroug.Key}
// files: {string.Join(", ", resxGroug.Select(f => f.Path))}
// RootNamespace (metadata): {rootNamespaceConfiguration}
// ProjectDir (metadata): {projectDirConfiguration}
// Namespace / DefaultResourcesNamespace (metadata): {namespaceConfiguration}
// ResourceName (metadata): {resourceNameConfiguration}
// ClassName (metadata): {classNameConfiguration}
// AssemblyName: {assemblyName}
// RootNamespace (computed): {rootNamespace}
// ProjectDir (computed): {projectDir}
// defaultNamespace: {defaultNamespace}
// defaultResourceName: {defaultResourceName}
// Namespace: {ns}
// ResourceName: {resourceName}
// ClassName: {className}
""";
if (resourceName != null && entries != null)
{
content += GenerateCode(ns, className, resourceName, entries, supportNullableReferenceTypes);
}
context.AddSource($"{Path.GetFileName(resxGroug.Key)}.resx.g.cs", SourceText.From(content, Encoding.UTF8));
}
}
private static string GenerateCode(string? ns, string className, string resourceName, List<ResxEntry> entries, bool enableNullableAttributes)
{
StringBuilder sb = new();
sb.AppendLine();
sb.AppendLine("#nullable enable");
if (ns != null)
{
sb.AppendLine($$"""
namespace {{ns}};
""");
}
sb.AppendLine($$"""
internal partial class {{className}}
{
private static global::System.Resources.ResourceManager? resourceMan;
public {{className}}()
{
}
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager
{
get
{
if (resourceMan is null)
{
resourceMan = new global::System.Resources.ResourceManager("{{resourceName}}", typeof({{className}}).Assembly);
}
return resourceMan;
}
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo? Culture { get; set; }
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
public static object? GetObject(global::System.Globalization.CultureInfo? culture, string name, object? defaultValue)
{
culture ??= Culture;
object? obj = ResourceManager.GetObject(name, culture);
if (obj == null)
{
return defaultValue;
}
return obj;
}
public static object? GetObject(global::System.Globalization.CultureInfo? culture, string name)
{
return GetObject(culture: culture, name: name, defaultValue: null);
}
public static object? GetObject(string name)
{
return GetObject(culture: null, name: name, defaultValue: null);
}
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
public static object? GetObject(string name, object? defaultValue)
{
return GetObject(culture: null, name: name, defaultValue: defaultValue);
}
public static global::System.IO.Stream? GetStream(string name)
{
return GetStream(culture: null, name: name);
}
public static global::System.IO.Stream? GetStream(global::System.Globalization.CultureInfo? culture, string name)
{
culture ??= Culture;
return ResourceManager.GetStream(name, culture);
}
public static string? GetString(global::System.Globalization.CultureInfo? culture, string name)
{
return GetString(culture: culture, name: name, args: null);
}
public static string? GetString(global::System.Globalization.CultureInfo? culture, string name, params object?[]? args)
{
culture ??= Culture;
string? str = ResourceManager.GetString(name, culture);
if (str == null)
{
return null;
}
if (args != null)
{
return string.Format(culture, str, args);
}
else
{
return str;
}
}
public static string? GetString(string name, params object?[]? args)
{
return GetString(culture: null, name: name, args: args);
}
[return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
public static string? GetString(string name, string? defaultValue)
{
return GetStringOrDefault(culture: null, name: name, defaultValue: defaultValue, args: null);
}
public static string? GetString(string name)
{
return GetStringOrDefault(culture: null, name: name, defaultValue: null, args: null);
}
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
public static string? GetStringOrDefault(global::System.Globalization.CultureInfo? culture, string name, string? defaultValue)
{
return GetStringOrDefault(culture: culture, name: name, defaultValue: defaultValue, args: null);
}
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
public static string? GetStringOrDefault(global::System.Globalization.CultureInfo? culture, string name, string? defaultValue, params object?[]? args)
{
culture ??= Culture;
string? str = ResourceManager.GetString(name, culture);
if (str == null)
{
if (defaultValue == null || args == null)
{
return defaultValue;
}
else
{
return string.Format(culture, defaultValue, args);
}
}
if (args != null)
{
return string.Format(culture, str, args);
}
else
{
return str;
}
}
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
public static string? GetStringOrDefault(string name, string? defaultValue, params object?[]? args)
{
return GetStringOrDefault(culture: null, name: name, defaultValue: defaultValue, args: args);
}
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
public static string? GetStringOrDefault(string name, string? defaultValue)
{
return GetStringOrDefault(culture: null, name: name, defaultValue: defaultValue, args: null);
}
""");
foreach (ResxEntry? entry in entries.OrderBy(e => e.Name, StringComparer.Ordinal))
{
if (string.IsNullOrEmpty(entry.Name))
{
continue;
}
if (entry.IsText)
{
XElement summary = new("summary", new XElement("para", $"Looks up a localized string for \"{entry.Name}\"."));
if (!string.IsNullOrWhiteSpace(entry.Comment))
{
summary.Add(new XElement("para", entry.Comment));
}
if (!entry.IsFileRef)
{
summary.Add(new XElement("para", $"Value: \"{entry.Value}\"."));
}
string comment = summary.ToString().Replace("\r\n", "\r\n /// ", StringComparison.Ordinal);
sb.AppendLine($$"""
/// {{comment}}
public static string {{ToCSharpNameIdentifier(entry.Name!)}}
{
get => GetString("{{entry.Name}}")!;
}
""");
if (entry.Value != null)
{
int args = Regex.Matches(entry.Value, "\\{(?<num>[0-9]+)(\\:[^}]*)?\\}", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant)
.Cast<Match>()
.Select(m => int.Parse(m.Groups["num"].Value, CultureInfo.InvariantCulture))
.Distinct()
.DefaultIfEmpty(-1)
.Max();
if (args >= 0)
{
string inParams = string.Join(", ", Enumerable.Range(0, args + 1).Select(arg => "object? arg" + arg.ToString(CultureInfo.InvariantCulture)));
string callParams = string.Join(", ", Enumerable.Range(0, args + 1).Select(arg => "arg" + arg.ToString(CultureInfo.InvariantCulture)));
sb.AppendLine($$"""
/// {{comment}}
public static string Format{{ToCSharpNameIdentifier(entry.Name!)}}(global::System.Globalization.CultureInfo? provider, {{inParams}})
{
return GetString(provider, "{{entry.Name}}", {{callParams}})!;
}
/// {{comment}}
public static string Format{{ToCSharpNameIdentifier(entry.Name!)}}({{inParams}})
{
return GetString("{{entry.Name}}", {{callParams}})!;
}
""");
}
}
}
else
{
sb.AppendLine($$"""
public static global::{{entry.FullTypeName}}? {{ToCSharpNameIdentifier(entry.Name!)}}
{
get => (global::{{entry.FullTypeName}}?)GetObject("{{entry.Name}}");
}
""");
}
}
sb.AppendLine($$"""
}
internal partial class {{className}}Names
{
""");
foreach (ResxEntry entry in entries)
{
if (string.IsNullOrEmpty(entry.Name))
{
continue;
}
sb.AppendLine($$"""
public const string {{ToCSharpNameIdentifier(entry.Name!)}} = "entry.Name";
""");
}
sb.AppendLine("}");
return sb.ToString();
}
private static string? ComputeResourceName(string rootNamespace, string projectDir, string resourcePath)
{
string fullProjectDir = EnsureEndSeparator(Path.GetFullPath(projectDir));
string fullResourcePath = Path.GetFullPath(resourcePath);
if (fullProjectDir == fullResourcePath)
{
return rootNamespace;
}
if (fullResourcePath.StartsWith(fullProjectDir, StringComparison.Ordinal))
{
string relativePath = fullResourcePath.Substring(fullProjectDir.Length);
return rootNamespace + '.' + relativePath.Replace('/', '.').Replace('\\', '.');
}
return null;
}
private static string? ComputeNamespace(string rootNamespace, string projectDir, string resourcePath)
{
string fullProjectDir = EnsureEndSeparator(Path.GetFullPath(projectDir));
string fullResourcePath = EnsureEndSeparator(Path.GetDirectoryName(Path.GetFullPath(resourcePath))!);
if (fullProjectDir == fullResourcePath)
{
return rootNamespace;
}
if (fullResourcePath.StartsWith(fullProjectDir, StringComparison.Ordinal))
{
string relativePath = fullResourcePath.Substring(fullProjectDir.Length);
return rootNamespace + '.' + relativePath.Replace('/', '.').Replace('\\', '.').TrimEnd('.');
}
return null;
}
private static List<ResxEntry>? LoadResourceFiles(SourceProductionContext context, IGrouping<string, AdditionalText> resxGroug)
{
List<ResxEntry> entries = new();
foreach (AdditionalText? entry in resxGroug.OrderBy(file => file.Path, StringComparer.Ordinal))
{
SourceText? content = entry.GetText(context.CancellationToken);
if (content == null)
{
continue;
}
try
{
XDocument document = XDocument.Parse(content.ToString());
foreach (XElement? element in document.XPathSelectElements("/root/data"))
{
string? name = element.Attribute("name")?.Value;
string? type = element.Attribute("type")?.Value;
string? comment = element.Attribute("comment")?.Value;
string? value = element.Element("value")?.Value;
ResxEntry existingEntry = entries.Find(e => e.Name == name);
if (existingEntry != null)
{
existingEntry.Comment ??= comment;
}
else
{
entries.Add(new ResxEntry { Name = name, Value = value, Comment = comment, Type = type });
}
}
}
catch
{
context.ReportDiagnostic(Diagnostic.Create(InvalidResx, location: null, entry.Path));
return null;
}
}
return entries;
}
private static string? GetMetadataValue(SourceProductionContext context, AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, string name, IEnumerable<AdditionalText> additionalFiles)
{
return GetMetadataValue(context, analyzerConfigOptionsProvider, name, name, additionalFiles);
}
private static string? GetMetadataValue(SourceProductionContext context, AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, string name, string? globalName, IEnumerable<AdditionalText> additionalFiles)
{
string? result = null;
foreach (AdditionalText file in additionalFiles)
{
if (analyzerConfigOptionsProvider.GetOptions(file).TryGetValue("build_metadata.AdditionalFiles." + name, out string? value))
{
if (result != null && value != result)
{
context.ReportDiagnostic(Diagnostic.Create(InconsistentProperties, location: null, name, file.Path));
return null;
}
result = value;
}
}
if (!string.IsNullOrEmpty(result))
{
return result;
}
if (globalName != null && analyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property." + globalName, out string? globalValue) && !string.IsNullOrEmpty(globalValue))
{
return globalValue;
}
return null;
}
private static string ToCSharpNameIdentifier(string name)
{
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#identifiers
// https://docs.microsoft.com/en-us/dotnet/api/system.globalization.unicodecategory?view=net-5.0
StringBuilder sb = new();
foreach (char c in name)
{
UnicodeCategory category = char.GetUnicodeCategory(c);
switch (category)
{
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.TitlecaseLetter:
case UnicodeCategory.ModifierLetter:
case UnicodeCategory.OtherLetter:
case UnicodeCategory.LetterNumber:
sb.Append(c);
break;
case UnicodeCategory.DecimalDigitNumber:
case UnicodeCategory.ConnectorPunctuation:
case UnicodeCategory.Format:
if (sb.Length == 0)
{
sb.Append('_');
}
sb.Append(c);
break;
default:
sb.Append('_');
break;
}
}
return sb.ToString();
}
private static string EnsureEndSeparator(string path)
{
if (path[path.Length - 1] == Path.DirectorySeparatorChar)
{
return path;
}
return path + Path.DirectorySeparatorChar;
}
private static string GetResourceName(string path)
{
string pathWithoutExtension = Path.Combine(Path.GetDirectoryName(path)!, Path.GetFileNameWithoutExtension(path));
int indexOf = pathWithoutExtension.LastIndexOf('.');
if (indexOf < 0)
{
return pathWithoutExtension;
}
return Regex.IsMatch(pathWithoutExtension.Substring(indexOf + 1), "^[a-zA-Z]{2}(-[a-zA-Z]{2})?$", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant, TimeSpan.FromSeconds(1))
? pathWithoutExtension.Substring(0, indexOf)
: pathWithoutExtension;
}
private sealed class ResxEntry
{
public string? Name { get; set; }
public string? Value { get; set; }
public string? Comment { get; set; }
public string? Type { get; set; }
public bool IsText
{
get
{
if (Type == null)
{
return true;
}
if (Value != null)
{
string[] parts = Value.Split(';');
if (parts.Length > 1)
{
string type = parts[1];
if (type.StartsWith("System.String,", StringComparison.Ordinal))
{
return true;
}
}
}
return false;
}
}
public string? FullTypeName
{
get
{
if (IsText)
{
return "string";
}
if (Value != null)
{
string[] parts = Value.Split(';');
if (parts.Length > 1)
{
string type = parts[1];
return type.Split(',')[0];
}
}
return null;
}
}
public bool IsFileRef
{
get => Type != null && Type.StartsWith("System.Resources.ResXFileRef,", StringComparison.Ordinal);
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Text;
namespace Snap.Hutao.SourceGeneration.Resx;
internal static class StringExtensions
{
public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
{
StringBuilder sb = new();
int previousIndex = 0;
int index = str.IndexOf(oldValue, comparison);
while (index is not -1)
{
sb.Append(str, previousIndex, index - previousIndex);
sb.Append(newValue);
index += oldValue.Length;
previousIndex = index;
index = str.IndexOf(oldValue, index, comparison);
}
sb.Append(str, previousIndex, str.Length - previousIndex);
return sb.ToString();
}
}

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>

View File

@@ -1,6 +1,5 @@
{
"$schema": "https://raw.githubusercontent.com/microsoft/CsWin32/main/src/Microsoft.Windows.CsWin32/settings.schema.json",
"allowMarshaling": true,
"useSafeHandles": false,
"emitSingleFile": true
"useSafeHandles": false
}

View File

@@ -33,13 +33,17 @@ CoWaitForMultipleObjects
// USER32
AttachThreadInput
FindWindowExW
GetCursorPos
GetDC
GetDpiForWindow
GetForegroundWindow
GetWindowPlacement
GetWindowThreadProcessId
ReleaseDC
RegisterHotKey
SendInput
SetForegroundWindow
UnregisterHotKey
// COM
IPersistFile
@@ -53,6 +57,7 @@ IMemoryBufferByteAccess
// Const value
INFINITE
WM_GETMINMAXINFO
WM_HOTKEY
WM_NCRBUTTONDOWN
WM_NCRBUTTONUP
WM_NULL

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Remove="NativeMethods.json" />
<None Remove="NativeMethods.txt" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="NativeMethods.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.46-beta">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,36 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.CompilerServices;
using Windows.Win32.UI.WindowsAndMessaging;
[assembly: InternalsVisibleTo("Snap.Hutao")]
namespace Snap.Hutao.Win32;
/// <summary>
/// 结构体封送
/// </summary>
internal static class StructMarshal
{
/// <summary>
/// 构造一个新的 <see cref="Windows.Win32.UI.WindowsAndMessaging.WINDOWPLACEMENT"/>
/// </summary>
/// <returns>新的实例</returns>
public static unsafe WINDOWPLACEMENT WINDOWPLACEMENT()
{
return new() { length = SizeOf<WINDOWPLACEMENT>() };
}
/// <summary>
/// 获取结构的大小
/// </summary>
/// <typeparam name="TStruct">结构类型</typeparam>
/// <returns>结构的大小</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe uint SizeOf<TStruct>()
where TStruct : unmanaged
{
return unchecked((uint)sizeof(TStruct));
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Windows.Win32.CsWin32.InteropServices;
internal class WinRTCustomMarshaler : ICustomMarshaler
{
private static readonly string? AssemblyFullName = typeof(Windows.Foundation.IMemoryBuffer).Assembly.FullName;
private readonly string className;
private bool lookedForFromAbi;
private MethodInfo? fromAbiMethod;
private WinRTCustomMarshaler(string className)
{
this.className = className;
}
public static ICustomMarshaler GetInstance(string cookie)
{
return new WinRTCustomMarshaler(cookie);
}
public void CleanUpManagedData(object ManagedObj)
{
}
public void CleanUpNativeData(nint pNativeData)
{
Marshal.Release(pNativeData);
}
public int GetNativeDataSize()
{
throw new NotSupportedException();
}
public nint MarshalManagedToNative(object ManagedObj)
{
throw new NotSupportedException();
}
public object MarshalNativeToManaged(nint thisPtr)
{
return className switch
{
"Windows.System.DispatcherQueueController" => Windows.System.DispatcherQueueController.FromAbi(thisPtr),
_ => MarshalNativeToManagedSlow(thisPtr),
};
}
private object MarshalNativeToManagedSlow(nint pNativeData)
{
if (!lookedForFromAbi)
{
Type? type = Type.GetType($"{className}, {AssemblyFullName}");
fromAbiMethod = type?.GetMethod("FromAbi");
lookedForFromAbi = true;
}
if (fromAbiMethod is not null)
{
return fromAbiMethod.Invoke(default, new object[] { pNativeData })!;
}
else
{
return Marshal.GetObjectForIUnknown(pNativeData);
}
}
}

View File

@@ -14,12 +14,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.SourceGeneration
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Test", "Snap.Hutao.Test\Snap.Hutao.Test.csproj", "{D691BA9F-904C-4229-87A5-E14F2EFF2F64}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Win32", "Snap.Hutao.Win32\Snap.Hutao.Win32.csproj", "{0F7ABEB2-5107-4037-B9DC-84D288FB0801}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug As Fake Elevated|Any CPU = Debug As Fake Elevated|Any CPU
Debug As Fake Elevated|arm64 = Debug As Fake Elevated|arm64
Debug As Fake Elevated|x64 = Debug As Fake Elevated|x64
Debug As Fake Elevated|x86 = Debug As Fake Elevated|x86
Debug|Any CPU = Debug|Any CPU
Debug|arm64 = Debug|arm64
Debug|x64 = Debug|x64
@@ -30,18 +28,6 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|Any CPU.ActiveCfg = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|Any CPU.Build.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|Any CPU.Deploy.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|arm64.ActiveCfg = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|arm64.Build.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|arm64.Deploy.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x64.ActiveCfg = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x64.Build.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x64.Deploy.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x86.ActiveCfg = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x86.Build.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x86.Deploy.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug|Any CPU.ActiveCfg = Debug|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug|Any CPU.Build.0 = Debug|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug|Any CPU.Deploy.0 = Debug|x64
@@ -66,14 +52,6 @@ Global
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.ActiveCfg = Release|x86
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.Build.0 = Release|x86
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.Deploy.0 = Release|x86
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|Any CPU.ActiveCfg = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|Any CPU.Build.0 = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|arm64.ActiveCfg = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|arm64.Build.0 = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x64.ActiveCfg = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x64.Build.0 = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x86.ActiveCfg = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x86.Build.0 = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.ActiveCfg = Debug|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.Build.0 = Debug|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|arm64.ActiveCfg = Debug|Any CPU
@@ -90,14 +68,6 @@ Global
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x64.Build.0 = Release|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x86.ActiveCfg = Release|Any CPU
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x86.Build.0 = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|Any CPU.ActiveCfg = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|Any CPU.Build.0 = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|arm64.ActiveCfg = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|arm64.Build.0 = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x64.ActiveCfg = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x64.Build.0 = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x86.ActiveCfg = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x86.Build.0 = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|arm64.ActiveCfg = Debug|Any CPU
@@ -114,6 +84,22 @@ Global
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|x64.Build.0 = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|x86.ActiveCfg = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|x86.Build.0 = Release|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|arm64.ActiveCfg = Debug|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|arm64.Build.0 = Debug|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|x64.ActiveCfg = Debug|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|x64.Build.0 = Debug|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|x86.ActiveCfg = Debug|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Debug|x86.Build.0 = Debug|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|Any CPU.Build.0 = Release|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|arm64.ActiveCfg = Release|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|arm64.Build.0 = Release|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|x64.ActiveCfg = Release|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|x64.Build.0 = Release|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|x86.ActiveCfg = Release|Any CPU
{0F7ABEB2-5107-4037-B9DC-84D288FB0801}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,153 +1,30 @@
<Application
x:Class="Snap.Hutao.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cwc="using:CommunityToolkit.WinUI.Converters"
xmlns:cwcw="using:CommunityToolkit.WinUI.Controls"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:shci="using:Snap.Hutao.Control.Image"
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
xmlns:shvc="using:Snap.Hutao.View.Converter">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<muxc:XamlControlsResources/>
<ResourceDictionary Source="Control/Theme/FontStyle.xaml"/>
<ResourceDictionary Source="Control/Loading.xaml"/>
<XamlControlsResources/>
<ResourceDictionary Source="ms-appx:///Control/Loading.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Image/CachedImage.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/Card.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/Color.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/Converter.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/CornerRadius.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/FontStyle.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/Glyph.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/InfoBarOverride.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/ItemsPanelTemplate.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/NumericValue.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/PageOverride.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/PivotOverride.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/SettingsStyle.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/TransitionCollection.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/Uri.xaml"/>
<ResourceDictionary Source="ms-appx:///Control/Theme/WindowOverride.xaml"/>
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<Color x:Key="AvatarPropertyAddValueColor">#FF74BF00</Color>
<Color x:Key="CompatBackgroundColor">#FFF4F4F4</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<Color x:Key="AvatarPropertyAddValueColor">#FF90E800</Color>
<Color x:Key="CompatBackgroundColor">#FF242424</Color>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<!-- Modify Window title bar color -->
<StaticResource x:Key="WindowCaptionBackground" ResourceKey="ControlFillColorTransparentBrush"/>
<StaticResource x:Key="WindowCaptionBackgroundDisabled" ResourceKey="ControlFillColorTransparentBrush"/>
<!-- Page Transparent Background -->
<StaticResource x:Key="ApplicationPageBackgroundThemeBrush" ResourceKey="ControlFillColorTransparentBrush"/>
<!-- InfoBar Resource -->
<Thickness x:Key="InfoBarIconMargin">19,16,19,16</Thickness>
<Thickness x:Key="InfoBarContentRootPadding">0,0,0,0</Thickness>
<x:Double x:Key="InfoBarIconFontSize">20</x:Double>
<!-- Pivot Resource -->
<x:Double x:Key="PivotHeaderItemFontSize">16</x:Double>
<Thickness x:Key="PivotHeaderItemMargin">16,0,0,0</Thickness>
<Thickness x:Key="PivotItemMargin">0</Thickness>
<!-- CornerRadius -->
<CornerRadius x:Key="CompatCornerRadius">6</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusTop">6,6,0,0</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusSmall">2</CornerRadius>
<!-- Length -->
<GridLength x:Key="CompatGridLength2">288</GridLength>
<x:Double x:Key="CompatSplitViewOpenPaneLength">212</x:Double>
<x:Double x:Key="CompatSplitViewOpenPaneLength2">304</x:Double>
<x:Double x:Key="CompatSplitViewOpenPaneLength3">320</x:Double>
<x:Double x:Key="HomeAdaptiveCardHeight">180</x:Double>
<x:Double x:Key="ContentDialogMinHeight">64</x:Double>
<x:Double x:Key="LargeAppBarButtonWidth">100</x:Double>
<!-- ProgressBar -->
<x:Double x:Key="LargeBackgroundProgressBarOpacity">0.2</x:Double>
<ThemeShadow x:Key="CompatShadow"/>
<!-- Brushes -->
<SolidColorBrush x:Key="AvatarPropertyAddValueBrush" Color="{ThemeResource AvatarPropertyAddValueColor}"/>
<SolidColorBrush x:Key="BlueBrush" Color="#FF5180CB"/>
<SolidColorBrush x:Key="PurpleBrush" Color="#FFA156E0"/>
<SolidColorBrush x:Key="OrangeBrush" Color="#FFBC6932"/>
<SolidColorBrush x:Key="GuaranteePullBrush" Color="#FF0063FF"/>
<SolidColorBrush x:Key="UpPullBrush" Color="#FFFFA400"/>
<!-- Settings -->
<x:Double x:Key="SettingsCardSpacing">3</x:Double>
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
<Style
x:Key="SettingsSectionHeaderTextBlockStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock">
<Style.Setters>
<Setter Property="Margin" Value="1,29,0,5"/>
</Style.Setters>
</Style>
<Style
x:Key="SettingsContentComboBoxStyle"
BasedOn="{StaticResource DefaultComboBoxStyle}"
TargetType="ComboBox">
<Setter Property="MinWidth" Value="120"/>
</Style>
<!-- Uris -->
<x:String x:Key="DocumentLink_MhyAccountSwitch">https://hut.ao/features/mhy-account-switch.html</x:String>
<x:String x:Key="DocumentLink_BugReport">https://hut.ao/statements/bug-report.html</x:String>
<x:String x:Key="DocumentLink_Translate">https://translate.hut.ao</x:String>
<x:String x:Key="DocumentLink_Home">https://hut.ao</x:String>
<x:String x:Key="HolographicHat_GetToken_Release">https://github.com/HolographicHat/GetToken/releases/latest</x:String>
<x:String x:Key="Sponsor_Afadian">https://afdian.net/a/DismissedLight</x:String>
<!-- Images -->
<x:String x:Key="UI_ItemIcon_None">https://static.snapgenshin.com/Bg/UI_ItemIcon_None.png</x:String>
<x:String x:Key="UI_MarkTower">https://static.snapgenshin.com/Bg/UI_MarkTower.png</x:String>
<x:String x:Key="UI_Icon_Intee_Explore_1">https://static.snapgenshin.com/Bg/UI_Icon_Intee_Explore_1.png</x:String>
<x:String x:Key="UI_MarkQuest_Events_Proce">https://static.snapgenshin.com/Bg/UI_MarkQuest_Events_Proce.png</x:String>
<x:String x:Key="UI_ItemIcon_201">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_201.png</x:String>
<x:String x:Key="UI_ItemIcon_204">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_204.png</x:String>
<x:String x:Key="UI_ItemIcon_210">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_210.png</x:String>
<x:String x:Key="UI_ItemIcon_220021">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_220021.png</x:String>
<x:String x:Key="UI_ImgSign_ItemIcon">https://static.snapgenshin.com/Bg/UI_ImgSign_ItemIcon.png</x:String>
<x:String x:Key="UI_AvatarIcon_Costume_Card">https://static.snapgenshin.com/AvatarCard/UI_AvatarIcon_Costume_Card.png</x:String>
<x:String x:Key="UI_EmotionIcon25">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon25.png</x:String>
<x:String x:Key="UI_EmotionIcon71">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon71.png</x:String>
<x:String x:Key="UI_EmotionIcon250">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon250.png</x:String>
<x:String x:Key="UI_EmotionIcon272">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon272.png</x:String>
<x:String x:Key="UI_EmotionIcon293">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon293.png</x:String>
<!-- FontIcon Content -->
<x:String x:Key="FontIconContentAdd">&#xE710;</x:String>
<x:String x:Key="FontIconContentSetting">&#xE713;</x:String>
<x:String x:Key="FontIconContentRefresh">&#xE72C;</x:String>
<x:String x:Key="FontIconContentDelete">&#xE74D;</x:String>
<x:String x:Key="FontIconContentFolder">&#xE8B7;</x:String>
<x:String x:Key="FontIconContentCheckList">&#xE9D5;</x:String>
<x:String x:Key="FontIconContentAsteriskBadge12">&#xEDAD;</x:String>
<x:String x:Key="FontIconContentZipFolder">&#xF012;</x:String>
<!-- Converters -->
<cwc:BoolNegationConverter x:Key="BoolNegationConverter"/>
<cwc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<cwc:FileSizeToFriendlyStringConverter x:Key="FileSizeToFriendlyStringConverter"/>
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
<shmmc:AvatarCardConverter x:Key="AvatarCardConverter"/>
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
<shmmc:DescriptionsParametersDescriptor x:Key="DescParamDescriptor"/>
<shmmc:ElementNameIconConverter x:Key="ElementNameIconConverter"/>
<shmmc:EmotionIconConverter x:Key="EmotionIconConverter"/>
<shmmc:EquipIconConverter x:Key="EquipIconConverter"/>
<shmmc:GachaAvatarImgConverter x:Key="GachaAvatarImgConverter"/>
<shmmc:GachaAvatarIconConverter x:Key="GachaAvatarIconConverter"/>
<shmmc:GachaEquipIconConverter x:Key="GachaEquipIconConverter"/>
<shmmc:ItemIconConverter x:Key="ItemIconConverter"/>
<shmmc:MonsterIconConverter x:Key="MonsterIconConverter"/>
<shmmc:PropertiesParametersDescriptor x:Key="PropertyDescriptor"/>
<shmmc:QualityColorConverter x:Key="QualityColorConverter"/>
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
<shvc:EmptyObjectToBoolConverter x:Key="EmptyObjectToBoolConverter"/>
<shvc:EmptyObjectToBoolRevertConverter x:Key="EmptyObjectToBoolRevertConverter"/>
<shvc:EmptyObjectToVisibilityConverter x:Key="EmptyObjectToVisibilityConverter"/>
<shvc:EmptyObjectToVisibilityRevertConverter x:Key="EmptyObjectToVisibilityRevertConverter"/>
<shvc:Int32ToVisibilityConverter x:Key="Int32ToVisibilityConverter"/>
<shvc:Int32ToVisibilityRevertConverter x:Key="Int32ToVisibilityRevertConverter"/>
<shvc:StringBoolConverter x:Key="StringBoolConverter"/>
<!-- Styles -->
<Style
x:Key="LargeGridViewItemStyle"
BasedOn="{StaticResource DefaultGridViewItemStyle}"
@@ -155,118 +32,19 @@
<Setter Property="Margin" Value="0,0,12,12"/>
</Style>
<Style
x:Key="SettingButtonStyle"
BasedOn="{StaticResource DefaultButtonStyle}"
TargetType="Button">
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}"/>
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
<Setter Property="Padding" Value="16,6,16,6"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
x:Name="NoneSelectionListViewItemStyle"
BasedOn="{StaticResource DefaultListViewItemStyle}"
TargetType="ListViewItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0,4,0,0"/>
</Style>
<Style x:Key="BorderCardStyle" TargetType="Border">
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="{StaticResource CompatCornerRadius}"/>
<Style
x:Name="NoneSelectionGridViewItemStyle"
BasedOn="{StaticResource DefaultGridViewItemStyle}"
TargetType="GridViewItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0,0,2,4"/>
</Style>
<Style x:Key="BorderGridStyle" TargetType="Grid">
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="{StaticResource CompatCornerRadius}"/>
</Style>
<Style TargetType="shci:CachedImage">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="LazyLoadingThreshold" Value="300"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="shci:CachedImage">
<Grid
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Image
Name="PlaceholderImage"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Opacity="1.0"
Source="{TemplateBinding PlaceholderSource}"
Stretch="{TemplateBinding PlaceholderStretch}"/>
<Image
Name="Image"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
NineGrid="{TemplateBinding NineGrid}"
Opacity="0.0"
Stretch="{TemplateBinding Stretch}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Failed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Loading">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Loaded">
<Storyboard>
<DoubleAnimation
AutoReverse="False"
BeginTime="0"
Storyboard.TargetName="Image"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.5"/>
<DoubleAnimation
AutoReverse="False"
BeginTime="0"
Storyboard.TargetName="PlaceholderImage"
Storyboard.TargetProperty="Opacity"
From="1"
To="0"
Duration="0:0:0.5"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Unloaded"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ItemsPanelTemplate -->
<ItemsPanelTemplate x:Key="ItemsStackPanelTemplate">
<ItemsStackPanel/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="WrapPanelTemplate">
<cwcw:WrapPanel/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="HorizontalStackPanelTemplate">
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
<!-- Transitions -->
<TransitionCollection x:Key="ReorderThemeTransitions">
<ReorderThemeTransition/>
</TransitionCollection>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -52,8 +52,8 @@ public sealed partial class App : Application
if (firstInstance.IsCurrent)
{
// manually invoke
activation.NonRedirectToActivate(firstInstance, activatedEventArgs);
activation.InitializeWith(firstInstance);
activation.NonRedirectToActivate(firstInstance, activatedEventArgs);
LogDiagnosticInformation();
serviceProvider.GetRequiredService<IJumpListInterop>().ConfigureAsync().SafeForget();

View File

@@ -1,48 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Behaviors;
using Microsoft.UI.Xaml;
namespace Snap.Hutao.Control.Behavior;
/// <summary>
/// 按给定比例自动调整高度的行为
/// </summary>
[HighQuality]
[DependencyProperty("TargetWidth", typeof(double), 1.0D)]
[DependencyProperty("TargetHeight", typeof(double), 1.0D)]
internal sealed partial class AutoHeightBehavior : BehaviorBase<FrameworkElement>
{
private readonly SizeChangedEventHandler sizeChangedEventHandler;
public AutoHeightBehavior()
{
sizeChangedEventHandler = OnSizeChanged;
}
/// <inheritdoc/>
protected override bool Initialize()
{
UpdateElement();
AssociatedObject.SizeChanged += sizeChangedEventHandler;
return true;
}
/// <inheritdoc/>
protected override bool Uninitialize()
{
AssociatedObject.SizeChanged -= sizeChangedEventHandler;
return true;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateElement();
}
private void UpdateElement()
{
AssociatedObject.Height = AssociatedObject.ActualWidth * (TargetHeight / TargetWidth);
}
}

View File

@@ -1,48 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Behaviors;
using Microsoft.UI.Xaml;
namespace Snap.Hutao.Control.Behavior;
/// <summary>
/// 按给定比例自动调整高度的行为
/// </summary>
[HighQuality]
[DependencyProperty("TargetWidth", typeof(double), 1.0D)]
[DependencyProperty("TargetHeight", typeof(double), 1.0D)]
internal sealed partial class AutoWidthBehavior : BehaviorBase<FrameworkElement>
{
private readonly SizeChangedEventHandler sizeChangedEventHandler;
public AutoWidthBehavior()
{
sizeChangedEventHandler = OnSizeChanged;
}
/// <inheritdoc/>
protected override bool Initialize()
{
UpdateElement();
AssociatedObject.SizeChanged += sizeChangedEventHandler;
return true;
}
/// <inheritdoc/>
protected override bool Uninitialize()
{
AssociatedObject.SizeChanged -= sizeChangedEventHandler;
return true;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateElement();
}
private void UpdateElement()
{
AssociatedObject.Width = AssociatedObject.Height * (TargetWidth / TargetHeight);
}
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI;
using CommunityToolkit.WinUI.Behaviors;
using Microsoft.UI.Xaml.Controls;
@@ -12,7 +13,7 @@ internal sealed class SelectedItemInViewBehavior : BehaviorBase<ListViewBase>
{
if (AssociatedObject.SelectedItem is { } item)
{
AssociatedObject.ScrollIntoView(item);
AssociatedObject.SmoothScrollIntoViewWithItemAsync(item, ScrollItemPlacement.Center).SafeForget();
}
return true;

View File

@@ -3,7 +3,7 @@
using Windows.UI;
namespace Snap.Hutao.Control;
namespace Snap.Hutao.Control.Brush;
internal sealed class ColorSegment : IColorSegment
{

View File

@@ -3,7 +3,7 @@
using Windows.UI;
namespace Snap.Hutao.Control;
namespace Snap.Hutao.Control.Brush;
internal interface IColorSegment
{

View File

@@ -7,7 +7,7 @@ using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Control;
namespace Snap.Hutao.Control.Brush;
[DependencyProperty("Source", typeof(List<IColorSegment>), default!, nameof(OnSourceChanged))]
internal sealed partial class SegmentedBar : ContentControl

View File

@@ -2,12 +2,11 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Windows.Foundation.Collections;
namespace Snap.Hutao.Control.Alternating;
namespace Snap.Hutao.Control.Collection.Alternating;
[DependencyProperty("ItemAlternateBackground", typeof(Brush))]
[DependencyProperty("ItemAlternateBackground", typeof(Microsoft.UI.Xaml.Media.Brush))]
internal sealed partial class AlternatingItemsControl : ItemsControl
{
private readonly VectorChangedEventHandler<object> itemsVectorChangedEventHandler;

View File

@@ -1,11 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Media;
namespace Snap.Hutao.Control.Alternating;
namespace Snap.Hutao.Control.Collection.Alternating;
internal interface IAlternatingItem
{
public Brush? Background { get; set; }
public Microsoft.UI.Xaml.Media.Brush? Background { get; set; }
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
namespace Snap.Hutao.Control.Collection.Selector;
[DependencyProperty("EnableMemberPath", typeof(string))]
internal sealed partial class ComboBox2 : ComboBox
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
if (element is ComboBoxItem comboBoxItem)
{
Binding binding = new() { Path = new(EnableMemberPath) };
comboBoxItem.SetBinding(IsEnabledProperty, binding);
}
base.PrepareContainerForItemOverride(element, item);
}
}

View File

@@ -10,8 +10,8 @@ internal struct ContentDialogHideToken : IDisposable, IAsyncDisposable
private readonly ContentDialog contentDialog;
private readonly ITaskContext taskContext;
private bool disposed = false;
private bool disposing = false;
private bool disposed = false;
public ContentDialogHideToken(ContentDialog contentDialog, ITaskContext taskContext)
{

View File

@@ -0,0 +1,82 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shci="using:Snap.Hutao.Control.Image">
<Style TargetType="shci:CachedImage">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="LazyLoadingThreshold" Value="256"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="shci:CachedImage">
<Grid
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Image
Name="PlaceholderImage"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Opacity="1.0"
Source="{TemplateBinding PlaceholderSource}"
Stretch="{TemplateBinding PlaceholderStretch}"/>
<Image
Name="Image"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
NineGrid="{TemplateBinding NineGrid}"
Opacity="0.0"
Stretch="{TemplateBinding Stretch}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Failed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Loading">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Loaded">
<Storyboard>
<DoubleAnimation
AutoReverse="False"
BeginTime="0"
Storyboard.TargetName="Image"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.5"/>
<DoubleAnimation
AutoReverse="False"
BeginTime="0"
Storyboard.TargetName="PlaceholderImage"
Storyboard.TargetProperty="Opacity"
From="1"
To="0"
Duration="0:0:0.5"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Unloaded"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -164,11 +164,26 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
private async ValueTask<LoadedImageSurface> LoadImageSurfaceAsync(string file, CancellationToken token)
{
surfaceLoadTaskCompletionSource = new();
LoadedImageSurface surface = LoadedImageSurface.StartLoadFromUri(file.ToUri());
surface.LoadCompleted += loadedImageSourceLoadCompletedEventHandler;
await surfaceLoadTaskCompletionSource.Task.ConfigureAwait(true);
LoadImageSurfaceCompleted(surface);
return surface;
LoadedImageSurface? surface = default;
try
{
surface = LoadedImageSurface.StartLoadFromUri(file.ToUri());
surface.LoadCompleted += loadedImageSourceLoadCompletedEventHandler;
if (surface.DecodedPhysicalSize.Size() <= 0D)
{
await surfaceLoadTaskCompletionSource.Task.ConfigureAwait(true);
}
LoadImageSurfaceCompleted(surface);
return surface;
}
finally
{
if (surface is not null)
{
surface.LoadCompleted -= loadedImageSourceLoadCompletedEventHandler;
}
}
}
private async ValueTask ShowAsync(CancellationToken token)
@@ -216,7 +231,6 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
private void OnLoadImageSurfaceLoadCompleted(LoadedImageSurface surface, LoadedImageSourceLoadCompletedEventArgs e)
{
surfaceLoadTaskCompletionSource?.TrySetResult();
surface.LoadCompleted -= loadedImageSourceLoadCompletedEventHandler;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)

View File

@@ -5,7 +5,6 @@ using Microsoft.UI;
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Snap.Hutao.Win32;
namespace Snap.Hutao.Control.Image;

View File

@@ -12,7 +12,6 @@ internal sealed class Int32Extension : MarkupExtension
protected override object ProvideValue()
{
_ = int.TryParse(Value, out int result);
return result;
return XamlBindingHelper.ConvertValue(typeof(int), Value);
}
}

View File

@@ -1,45 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Windows.Foundation;
namespace Snap.Hutao.Control.Panel;
/// <summary>
/// 纵横比控件
/// </summary>
[HighQuality]
[DependencyProperty("TargetWidth", typeof(double), 1.0D)]
[DependencyProperty("TargetHeight", typeof(double), 1.0D)]
internal sealed partial class AspectRatio : Microsoft.UI.Xaml.Controls.Control
{
private const double Epsilon = 2.2204460492503131e-016;
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
double ratio = TargetWidth / TargetHeight;
double ratioAvailable = availableSize.Width / availableSize.Height;
if (Math.Abs(ratioAvailable - ratio) < Epsilon)
{
return availableSize;
}
// 更宽
if (ratioAvailable > ratio)
{
double newWidth = ratio * availableSize.Height;
return new Size(newWidth, availableSize.Height);
}
// 更高
if (ratioAvailable < ratio)
{
double newHeight = availableSize.Width / ratio;
return new Size(availableSize.Width, newHeight);
}
return availableSize;
}
}

View File

@@ -0,0 +1,21 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cwm="using:CommunityToolkit.WinUI.Media">
<Style x:Key="BorderCardStyle" TargetType="Border">
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
</Style>
<Style x:Key="GridCardStyle" TargetType="Grid">
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
</Style>
<cwm:AttachedCardShadow
x:Key="CompatCardShadow"
Opacity="0.1"
Offset="0,4,0"/>
</ResourceDictionary>

View File

@@ -0,0 +1,31 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<Color x:Key="AvatarPropertyAddValueColor">#FF74BF00</Color>
<Color x:Key="CompatBackgroundColor">#FFF4F4F4</Color>
<Color x:Key="DarkOnlyOverlayMaskColor">#00000000</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<Color x:Key="AvatarPropertyAddValueColor">#FF90E800</Color>
<Color x:Key="CompatBackgroundColor">#FF242424</Color>
<Color x:Key="DarkOnlyOverlayMaskColor">#60000000</Color>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Color x:Key="BlueColor">#FF5180CB</Color>
<Color x:Key="PurpleColor">#FFA156E0</Color>
<Color x:Key="OrangeColor">#FFBC6932</Color>
<Color x:Key="GuaranteePullColor">#FF0063FF</Color>
<Color x:Key="UpPullColor">#FFFFA400</Color>
<SolidColorBrush x:Key="BlueColorBrush" Color="{ThemeResource BlueColor}"/>
<SolidColorBrush x:Key="PurpleColorBrush" Color="{ThemeResource PurpleColor}"/>
<SolidColorBrush x:Key="OrangeColorBrush" Color="{ThemeResource OrangeColor}"/>
<SolidColorBrush x:Key="GuaranteePullCoolorBrush" Color="{ThemeResource GuaranteePullColor}"/>
<SolidColorBrush x:Key="UpPullColorBrush" Color="{ThemeResource UpPullColor}"/>
<SolidColorBrush x:Key="DarkOnlyOverlayMaskColorBrush" Color="{ThemeResource DarkOnlyOverlayMaskColor}"/>
<SolidColorBrush x:Key="AvatarPropertyAddValueColorBrush" Color="{ThemeResource AvatarPropertyAddValueColor}"/>
</ResourceDictionary>

View File

@@ -0,0 +1,35 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cwc="using:CommunityToolkit.WinUI.Converters"
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
xmlns:shvc="using:Snap.Hutao.View.Converter">
<cwc:BoolNegationConverter x:Key="BoolNegationConverter"/>
<cwc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<cwc:FileSizeToFriendlyStringConverter x:Key="FileSizeToFriendlyStringConverter"/>
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
<shmmc:AvatarCardConverter x:Key="AvatarCardConverter"/>
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
<shmmc:DescriptionsParametersDescriptor x:Key="DescParamDescriptor"/>
<shmmc:ElementNameIconConverter x:Key="ElementNameIconConverter"/>
<shmmc:EmotionIconConverter x:Key="EmotionIconConverter"/>
<shmmc:EquipIconConverter x:Key="EquipIconConverter"/>
<shmmc:GachaAvatarImgConverter x:Key="GachaAvatarImgConverter"/>
<shmmc:GachaAvatarIconConverter x:Key="GachaAvatarIconConverter"/>
<shmmc:GachaEquipIconConverter x:Key="GachaEquipIconConverter"/>
<shmmc:ItemIconConverter x:Key="ItemIconConverter"/>
<shmmc:MonsterIconConverter x:Key="MonsterIconConverter"/>
<shmmc:PropertiesParametersDescriptor x:Key="PropertyDescriptor"/>
<shmmc:QualityColorConverter x:Key="QualityColorConverter"/>
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
<shvc:EmptyObjectToBoolConverter x:Key="EmptyObjectToBoolConverter"/>
<shvc:EmptyObjectToBoolRevertConverter x:Key="EmptyObjectToBoolRevertConverter"/>
<shvc:EmptyObjectToVisibilityConverter x:Key="EmptyObjectToVisibilityConverter"/>
<shvc:EmptyObjectToVisibilityRevertConverter x:Key="EmptyObjectToVisibilityRevertConverter"/>
<shvc:Int32ToVisibilityConverter x:Key="Int32ToVisibilityConverter"/>
<shvc:Int32ToVisibilityRevertConverter x:Key="Int32ToVisibilityRevertConverter"/>
<shvc:StringBoolConverter x:Key="StringBoolConverter"/>
</ResourceDictionary>

View File

@@ -0,0 +1,4 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CornerRadius x:Key="ControlCornerRadiusTop">4,4,0,0</CornerRadius>
<CornerRadius x:Key="ControlCornerRadiusBottom">0,0,4,4</CornerRadius>
</ResourceDictionary>

View File

@@ -1,7 +1,7 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shch="using:Snap.Hutao.Control.Helper">
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources/>
</ResourceDictionary.MergedDictionaries>
<FontFamily x:Key="MiSans">ms-appx:///Resource/Font/MiSans-Regular.ttf#MiSans</FontFamily>
<FontFamily x:Key="CascadiaMonoAndMiSans">ms-appx:///Resource/Font/CascadiaMono.ttf#Cascadia Mono, ms-appx:///Resource/Font/MiSans-Regular.ttf#MiSans</FontFamily>
@@ -82,13 +82,6 @@
<Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
</Style>
<x:Double x:Key="SecondaryTextFontSize">12</x:Double>
<Style x:Key="SecondaryTextStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="{StaticResource SecondaryTextFontSize}"/>
<Setter Property="Foreground" Value="{ThemeResource TextFillColorSecondaryBrush}"/>
</Style>
<Style BasedOn="{StaticResource DefaultMenuFlyoutItemStyle}" TargetType="MenuFlyoutItem">
<Setter Property="FontFamily" Value="{StaticResource MiSans}"/>
</Style>
@@ -100,276 +93,4 @@
<Style BasedOn="{StaticResource DefaultScrollViewerStyle}" TargetType="ScrollViewer">
<Setter Property="FontFamily" Value="{StaticResource MiSans}"/>
</Style>
<!-- TODO: When will DefaultInfoBarStyle added -->
<Style TargetType="InfoBar">
<Setter Property="shch:InfoBarHelper.IsTextSelectionEnabled" Value="False"/>
<Setter Property="FontFamily" Value="{StaticResource MiSans}"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="CloseButtonStyle" Value="{StaticResource InfoBarCloseButtonStyle}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{ThemeResource InfoBarBorderBrush}"/>
<Setter Property="BorderThickness" Value="{ThemeResource InfoBarBorderThickness}"/>
<Setter Property="AutomationProperties.LandmarkType" Value="Custom"/>
<Setter Property="AutomationProperties.IsDialog" Value="True"/>
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="InfoBar">
<Border
x:Name="ContentRoot"
VerticalAlignment="Top"
Background="{ThemeResource InfoBarInformationalSeverityBackgroundBrush}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<!-- Background is used here so that it overrides the severity status color if set. -->
<Grid
MinHeight="{ThemeResource InfoBarMinHeight}"
Padding="{StaticResource InfoBarContentRootPadding}"
HorizontalAlignment="Stretch"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<!-- Icon -->
<ColumnDefinition Width="*"/>
<!-- Title, message, and action -->
<ColumnDefinition Width="Auto"/>
<!-- Close button -->
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="StandardIconArea" Visibility="Collapsed">
<TextBlock
x:Name="IconBackground"
Grid.Column="0"
Margin="{StaticResource InfoBarIconMargin}"
VerticalAlignment="Top"
AutomationProperties.AccessibilityView="Raw"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="{StaticResource InfoBarIconFontSize}"
Foreground="{ThemeResource InfoBarInformationalSeverityIconBackground}"
Text="{StaticResource InfoBarIconBackgroundGlyph}"/>
<TextBlock
x:Name="StandardIcon"
Grid.Column="0"
Margin="{StaticResource InfoBarIconMargin}"
VerticalAlignment="Top"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="{StaticResource InfoBarIconFontSize}"
Foreground="{ThemeResource InfoBarInformationalSeverityIconForeground}"
Text="{StaticResource InfoBarInformationalIconGlyph}"/>
</Grid>
<Viewbox
x:Name="UserIconBox"
Grid.Column="0"
MaxWidth="{ThemeResource InfoBarIconFontSize}"
MaxHeight="{ThemeResource InfoBarIconFontSize}"
Margin="{ThemeResource InfoBarIconMargin}"
VerticalAlignment="Top"
Child="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.IconElement}"
Visibility="Collapsed"/>
<InfoBarPanel
Grid.Column="1"
Margin="{StaticResource InfoBarPanelMargin}"
HorizontalOrientationPadding="{StaticResource InfoBarPanelHorizontalOrientationPadding}"
VerticalOrientationPadding="{StaticResource InfoBarPanelVerticalOrientationPadding}">
<TextBlock
x:Name="Title"
FontSize="{StaticResource InfoBarTitleFontSize}"
FontWeight="{StaticResource InfoBarTitleFontWeight}"
Foreground="{ThemeResource InfoBarTitleForeground}"
InfoBarPanel.HorizontalOrientationMargin="{StaticResource InfoBarTitleHorizontalOrientationMargin}"
InfoBarPanel.VerticalOrientationMargin="{StaticResource InfoBarTitleVerticalOrientationMargin}"
IsTextSelectionEnabled="{TemplateBinding shch:InfoBarHelper.IsTextSelectionEnabled}"
Text="{TemplateBinding Title}"
TextWrapping="WrapWholeWords"/>
<TextBlock
x:Name="Message"
FontSize="{StaticResource InfoBarMessageFontSize}"
FontWeight="{StaticResource InfoBarMessageFontWeight}"
Foreground="{ThemeResource InfoBarMessageForeground}"
InfoBarPanel.HorizontalOrientationMargin="{StaticResource InfoBarMessageHorizontalOrientationMargin}"
InfoBarPanel.VerticalOrientationMargin="{StaticResource InfoBarMessageVerticalOrientationMargin}"
IsTextSelectionEnabled="{TemplateBinding shch:InfoBarHelper.IsTextSelectionEnabled}"
Text="{TemplateBinding Message}"
TextWrapping="WrapWholeWords"/>
<ContentPresenter
VerticalAlignment="Top"
Content="{TemplateBinding ActionButton}"
InfoBarPanel.HorizontalOrientationMargin="{StaticResource InfoBarActionHorizontalOrientationMargin}"
InfoBarPanel.VerticalOrientationMargin="{StaticResource InfoBarActionVerticalOrientationMargin}">
<ContentPresenter.Resources>
<Style BasedOn="{StaticResource DefaultHyperlinkButtonStyle}" TargetType="HyperlinkButton">
<Style.Setters>
<Setter Property="Margin" Value="{StaticResource InfoBarHyperlinkButtonMargin}"/>
<Setter Property="Foreground" Value="{ThemeResource InfoBarHyperlinkButtonForeground}"/>
</Style.Setters>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</InfoBarPanel>
<ContentPresenter
Grid.Row="1"
Grid.Column="1"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
<Button
Name="CloseButton"
Grid.Column="2"
Command="{TemplateBinding CloseButtonCommand}"
CommandParameter="{TemplateBinding CloseButtonCommandParameter}"
Style="{TemplateBinding CloseButtonStyle}">
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="ButtonBackground" ResourceKey="AppBarButtonBackground"/>
<StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="AppBarButtonBackgroundPointerOver"/>
<StaticResource x:Key="ButtonBackgroundPressed" ResourceKey="AppBarButtonBackgroundPressed"/>
<StaticResource x:Key="ButtonBackgroundDisabled" ResourceKey="AppBarButtonBackgroundDisabled"/>
<StaticResource x:Key="ButtonForeground" ResourceKey="AppBarButtonForeground"/>
<StaticResource x:Key="ButtonForegroundPointerOver" ResourceKey="AppBarButtonForegroundPointerOver"/>
<StaticResource x:Key="ButtonForegroundPressed" ResourceKey="AppBarButtonForegroundPressed"/>
<StaticResource x:Key="ButtonForegroundDisabled" ResourceKey="AppBarButtonForegroundDisabled"/>
<StaticResource x:Key="ButtonBorderBrush" ResourceKey="AppBarButtonBorderBrush"/>
<StaticResource x:Key="ButtonBorderBrushPointerOver" ResourceKey="AppBarButtonBorderBrushPointerOver"/>
<StaticResource x:Key="ButtonBorderBrushPressed" ResourceKey="AppBarButtonBorderBrushPressed"/>
<StaticResource x:Key="ButtonBorderBrushDisabled" ResourceKey="AppBarButtonBorderBrushDisabled"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<StaticResource x:Key="ButtonBackground" ResourceKey="AppBarButtonBackground"/>
<StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="AppBarButtonBackgroundPointerOver"/>
<StaticResource x:Key="ButtonBackgroundPressed" ResourceKey="AppBarButtonBackgroundPressed"/>
<StaticResource x:Key="ButtonBackgroundDisabled" ResourceKey="AppBarButtonBackgroundDisabled"/>
<StaticResource x:Key="ButtonForeground" ResourceKey="AppBarButtonForeground"/>
<StaticResource x:Key="ButtonForegroundPointerOver" ResourceKey="AppBarButtonForegroundPointerOver"/>
<StaticResource x:Key="ButtonForegroundPressed" ResourceKey="AppBarButtonForegroundPressed"/>
<StaticResource x:Key="ButtonForegroundDisabled" ResourceKey="AppBarButtonForegroundDisabled"/>
<StaticResource x:Key="ButtonBorderBrush" ResourceKey="AppBarButtonBorderBrush"/>
<StaticResource x:Key="ButtonBorderBrushPointerOver" ResourceKey="AppBarButtonBorderBrushPointerOver"/>
<StaticResource x:Key="ButtonBorderBrushPressed" ResourceKey="AppBarButtonBorderBrushPressed"/>
<StaticResource x:Key="ButtonBorderBrushDisabled" ResourceKey="AppBarButtonBorderBrushDisabled"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="ButtonBackground" ResourceKey="AppBarButtonBackground"/>
<StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="AppBarButtonBackgroundPointerOver"/>
<StaticResource x:Key="ButtonBackgroundPressed" ResourceKey="AppBarButtonBackgroundPressed"/>
<StaticResource x:Key="ButtonBackgroundDisabled" ResourceKey="AppBarButtonBackgroundDisabled"/>
<StaticResource x:Key="ButtonForeground" ResourceKey="AppBarButtonForeground"/>
<StaticResource x:Key="ButtonForegroundPointerOver" ResourceKey="AppBarButtonForegroundPointerOver"/>
<StaticResource x:Key="ButtonForegroundPressed" ResourceKey="AppBarButtonForegroundPressed"/>
<StaticResource x:Key="ButtonForegroundDisabled" ResourceKey="AppBarButtonForegroundDisabled"/>
<StaticResource x:Key="ButtonBorderBrush" ResourceKey="AppBarButtonBorderBrush"/>
<StaticResource x:Key="ButtonBorderBrushPointerOver" ResourceKey="AppBarButtonBorderBrushPointerOver"/>
<StaticResource x:Key="ButtonBorderBrushPressed" ResourceKey="AppBarButtonBorderBrushPressed"/>
<StaticResource x:Key="ButtonBorderBrushDisabled" ResourceKey="AppBarButtonBorderBrushDisabled"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<Viewbox
Width="{StaticResource InfoBarCloseButtonGlyphSize}"
Height="{StaticResource InfoBarCloseButtonGlyphSize}"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<SymbolIcon Symbol="{StaticResource InfoBarCloseButtonSymbol}"/>
</Viewbox>
</Button>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SeverityLevels">
<VisualState x:Name="Informational"/>
<VisualState x:Name="Error">
<VisualState.Setters>
<Setter Target="ContentRoot.Background" Value="{ThemeResource InfoBarErrorSeverityBackgroundBrush}"/>
<Setter Target="IconBackground.Foreground" Value="{ThemeResource InfoBarErrorSeverityIconBackground}"/>
<Setter Target="StandardIcon.Text" Value="{StaticResource InfoBarErrorIconGlyph}"/>
<Setter Target="StandardIcon.Foreground" Value="{ThemeResource InfoBarErrorSeverityIconForeground}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Warning">
<VisualState.Setters>
<Setter Target="ContentRoot.Background" Value="{ThemeResource InfoBarWarningSeverityBackgroundBrush}"/>
<Setter Target="IconBackground.Foreground" Value="{ThemeResource InfoBarWarningSeverityIconBackground}"/>
<Setter Target="StandardIcon.Text" Value="{StaticResource InfoBarWarningIconGlyph}"/>
<Setter Target="StandardIcon.Foreground" Value="{ThemeResource InfoBarWarningSeverityIconForeground}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Success">
<VisualState.Setters>
<Setter Target="ContentRoot.Background" Value="{ThemeResource InfoBarSuccessSeverityBackgroundBrush}"/>
<Setter Target="IconBackground.Foreground" Value="{ThemeResource InfoBarSuccessSeverityIconBackground}"/>
<Setter Target="StandardIcon.Text" Value="{StaticResource InfoBarSuccessIconGlyph}"/>
<Setter Target="StandardIcon.Foreground" Value="{ThemeResource InfoBarSuccessSeverityIconForeground}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="IconStates">
<VisualState x:Name="StandardIconVisible">
<VisualState.Setters>
<Setter Target="UserIconBox.Visibility" Value="Collapsed"/>
<Setter Target="StandardIconArea.Visibility" Value="Visible"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="UserIconVisible">
<VisualState.Setters>
<Setter Target="UserIconBox.Visibility" Value="Visible"/>
<Setter Target="StandardIconArea.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="NoIconVisible"/>
</VisualStateGroup>
<VisualStateGroup>
<VisualState x:Name="CloseButtonVisible"/>
<VisualState x:Name="CloseButtonCollapsed">
<VisualState.Setters>
<Setter Target="CloseButton.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="InfoBarVisibility">
<VisualState x:Name="InfoBarVisible"/>
<VisualState x:Name="InfoBarCollapsed">
<VisualState.Setters>
<Setter Target="ContentRoot.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup>
<VisualState x:Name="ForegroundNotSet"/>
<VisualState x:Name="ForegroundSet">
<VisualState.Setters>
<Setter Target="Title.Foreground" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Foreground}"/>
<Setter Target="Message.Foreground" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Foreground}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</ResourceDictionary>

View File

@@ -0,0 +1,10 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="FontIconContentAdd">&#xE710;</x:String>
<x:String x:Key="FontIconContentSetting">&#xE713;</x:String>
<x:String x:Key="FontIconContentRefresh">&#xE72C;</x:String>
<x:String x:Key="FontIconContentDelete">&#xE74D;</x:String>
<x:String x:Key="FontIconContentFolder">&#xE8B7;</x:String>
<x:String x:Key="FontIconContentCheckList">&#xE9D5;</x:String>
<x:String x:Key="FontIconContentAsteriskBadge12">&#xEDAD;</x:String>
<x:String x:Key="FontIconContentZipFolder">&#xF012;</x:String>
</ResourceDictionary>

View File

@@ -0,0 +1,265 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shch="using:Snap.Hutao.Control.Helper">
<Thickness x:Key="InfoBarIconMargin">19,16,19,16</Thickness>
<Thickness x:Key="InfoBarContentRootPadding">0,0,0,0</Thickness>
<x:Double x:Key="InfoBarIconFontSize">20</x:Double>
<!-- TODO: When will DefaultInfoBarStyle added -->
<Style TargetType="InfoBar">
<Setter Property="shch:InfoBarHelper.IsTextSelectionEnabled" Value="False"/>
<Setter Property="FontFamily" Value="{StaticResource MiSans}"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="CloseButtonStyle" Value="{StaticResource InfoBarCloseButtonStyle}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{ThemeResource InfoBarBorderBrush}"/>
<Setter Property="BorderThickness" Value="{ThemeResource InfoBarBorderThickness}"/>
<Setter Property="AutomationProperties.LandmarkType" Value="Custom"/>
<Setter Property="AutomationProperties.IsDialog" Value="True"/>
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="InfoBar">
<Border
x:Name="ContentRoot"
VerticalAlignment="Top"
Background="{ThemeResource InfoBarInformationalSeverityBackgroundBrush}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<!-- Background is used here so that it overrides the severity status color if set. -->
<Grid
MinHeight="{ThemeResource InfoBarMinHeight}"
Padding="{StaticResource InfoBarContentRootPadding}"
HorizontalAlignment="Stretch"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<!-- Icon -->
<ColumnDefinition Width="*"/>
<!-- Title, message, and action -->
<ColumnDefinition Width="Auto"/>
<!-- Close button -->
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="StandardIconArea" Visibility="Collapsed">
<TextBlock
x:Name="IconBackground"
Grid.Column="0"
Margin="{StaticResource InfoBarIconMargin}"
VerticalAlignment="Top"
AutomationProperties.AccessibilityView="Raw"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="{StaticResource InfoBarIconFontSize}"
Foreground="{ThemeResource InfoBarInformationalSeverityIconBackground}"
Text="{StaticResource InfoBarIconBackgroundGlyph}"/>
<TextBlock
x:Name="StandardIcon"
Grid.Column="0"
Margin="{StaticResource InfoBarIconMargin}"
VerticalAlignment="Top"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="{StaticResource InfoBarIconFontSize}"
Foreground="{ThemeResource InfoBarInformationalSeverityIconForeground}"
Text="{StaticResource InfoBarInformationalIconGlyph}"/>
</Grid>
<Viewbox
x:Name="UserIconBox"
Grid.Column="0"
MaxWidth="{ThemeResource InfoBarIconFontSize}"
MaxHeight="{ThemeResource InfoBarIconFontSize}"
Margin="{ThemeResource InfoBarIconMargin}"
VerticalAlignment="Top"
Child="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.IconElement}"
Visibility="Collapsed"/>
<InfoBarPanel
Grid.Column="1"
Margin="{StaticResource InfoBarPanelMargin}"
HorizontalOrientationPadding="{StaticResource InfoBarPanelHorizontalOrientationPadding}"
VerticalOrientationPadding="{StaticResource InfoBarPanelVerticalOrientationPadding}">
<TextBlock
x:Name="Title"
FontSize="{StaticResource InfoBarTitleFontSize}"
FontWeight="{StaticResource InfoBarTitleFontWeight}"
Foreground="{ThemeResource InfoBarTitleForeground}"
InfoBarPanel.HorizontalOrientationMargin="{StaticResource InfoBarTitleHorizontalOrientationMargin}"
InfoBarPanel.VerticalOrientationMargin="{StaticResource InfoBarTitleVerticalOrientationMargin}"
IsTextSelectionEnabled="{TemplateBinding shch:InfoBarHelper.IsTextSelectionEnabled}"
Text="{TemplateBinding Title}"
TextWrapping="WrapWholeWords"/>
<TextBlock
x:Name="Message"
FontSize="{StaticResource InfoBarMessageFontSize}"
FontWeight="{StaticResource InfoBarMessageFontWeight}"
Foreground="{ThemeResource InfoBarMessageForeground}"
InfoBarPanel.HorizontalOrientationMargin="{StaticResource InfoBarMessageHorizontalOrientationMargin}"
InfoBarPanel.VerticalOrientationMargin="{StaticResource InfoBarMessageVerticalOrientationMargin}"
IsTextSelectionEnabled="{TemplateBinding shch:InfoBarHelper.IsTextSelectionEnabled}"
Text="{TemplateBinding Message}"
TextWrapping="WrapWholeWords"/>
<ContentPresenter
VerticalAlignment="Top"
Content="{TemplateBinding ActionButton}"
InfoBarPanel.HorizontalOrientationMargin="{StaticResource InfoBarActionHorizontalOrientationMargin}"
InfoBarPanel.VerticalOrientationMargin="{StaticResource InfoBarActionVerticalOrientationMargin}">
<ContentPresenter.Resources>
<Style BasedOn="{StaticResource DefaultHyperlinkButtonStyle}" TargetType="HyperlinkButton">
<Style.Setters>
<Setter Property="Margin" Value="{StaticResource InfoBarHyperlinkButtonMargin}"/>
<Setter Property="Foreground" Value="{ThemeResource InfoBarHyperlinkButtonForeground}"/>
</Style.Setters>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</InfoBarPanel>
<ContentPresenter
Grid.Row="1"
Grid.Column="1"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
<Button
Name="CloseButton"
Grid.Column="2"
Command="{TemplateBinding CloseButtonCommand}"
CommandParameter="{TemplateBinding CloseButtonCommandParameter}"
Style="{TemplateBinding CloseButtonStyle}">
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="ButtonBackground" ResourceKey="AppBarButtonBackground"/>
<StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="AppBarButtonBackgroundPointerOver"/>
<StaticResource x:Key="ButtonBackgroundPressed" ResourceKey="AppBarButtonBackgroundPressed"/>
<StaticResource x:Key="ButtonBackgroundDisabled" ResourceKey="AppBarButtonBackgroundDisabled"/>
<StaticResource x:Key="ButtonForeground" ResourceKey="AppBarButtonForeground"/>
<StaticResource x:Key="ButtonForegroundPointerOver" ResourceKey="AppBarButtonForegroundPointerOver"/>
<StaticResource x:Key="ButtonForegroundPressed" ResourceKey="AppBarButtonForegroundPressed"/>
<StaticResource x:Key="ButtonForegroundDisabled" ResourceKey="AppBarButtonForegroundDisabled"/>
<StaticResource x:Key="ButtonBorderBrush" ResourceKey="AppBarButtonBorderBrush"/>
<StaticResource x:Key="ButtonBorderBrushPointerOver" ResourceKey="AppBarButtonBorderBrushPointerOver"/>
<StaticResource x:Key="ButtonBorderBrushPressed" ResourceKey="AppBarButtonBorderBrushPressed"/>
<StaticResource x:Key="ButtonBorderBrushDisabled" ResourceKey="AppBarButtonBorderBrushDisabled"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<StaticResource x:Key="ButtonBackground" ResourceKey="AppBarButtonBackground"/>
<StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="AppBarButtonBackgroundPointerOver"/>
<StaticResource x:Key="ButtonBackgroundPressed" ResourceKey="AppBarButtonBackgroundPressed"/>
<StaticResource x:Key="ButtonBackgroundDisabled" ResourceKey="AppBarButtonBackgroundDisabled"/>
<StaticResource x:Key="ButtonForeground" ResourceKey="AppBarButtonForeground"/>
<StaticResource x:Key="ButtonForegroundPointerOver" ResourceKey="AppBarButtonForegroundPointerOver"/>
<StaticResource x:Key="ButtonForegroundPressed" ResourceKey="AppBarButtonForegroundPressed"/>
<StaticResource x:Key="ButtonForegroundDisabled" ResourceKey="AppBarButtonForegroundDisabled"/>
<StaticResource x:Key="ButtonBorderBrush" ResourceKey="AppBarButtonBorderBrush"/>
<StaticResource x:Key="ButtonBorderBrushPointerOver" ResourceKey="AppBarButtonBorderBrushPointerOver"/>
<StaticResource x:Key="ButtonBorderBrushPressed" ResourceKey="AppBarButtonBorderBrushPressed"/>
<StaticResource x:Key="ButtonBorderBrushDisabled" ResourceKey="AppBarButtonBorderBrushDisabled"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="ButtonBackground" ResourceKey="AppBarButtonBackground"/>
<StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="AppBarButtonBackgroundPointerOver"/>
<StaticResource x:Key="ButtonBackgroundPressed" ResourceKey="AppBarButtonBackgroundPressed"/>
<StaticResource x:Key="ButtonBackgroundDisabled" ResourceKey="AppBarButtonBackgroundDisabled"/>
<StaticResource x:Key="ButtonForeground" ResourceKey="AppBarButtonForeground"/>
<StaticResource x:Key="ButtonForegroundPointerOver" ResourceKey="AppBarButtonForegroundPointerOver"/>
<StaticResource x:Key="ButtonForegroundPressed" ResourceKey="AppBarButtonForegroundPressed"/>
<StaticResource x:Key="ButtonForegroundDisabled" ResourceKey="AppBarButtonForegroundDisabled"/>
<StaticResource x:Key="ButtonBorderBrush" ResourceKey="AppBarButtonBorderBrush"/>
<StaticResource x:Key="ButtonBorderBrushPointerOver" ResourceKey="AppBarButtonBorderBrushPointerOver"/>
<StaticResource x:Key="ButtonBorderBrushPressed" ResourceKey="AppBarButtonBorderBrushPressed"/>
<StaticResource x:Key="ButtonBorderBrushDisabled" ResourceKey="AppBarButtonBorderBrushDisabled"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<Viewbox
Width="{StaticResource InfoBarCloseButtonGlyphSize}"
Height="{StaticResource InfoBarCloseButtonGlyphSize}"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<SymbolIcon Symbol="{StaticResource InfoBarCloseButtonSymbol}"/>
</Viewbox>
</Button>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SeverityLevels">
<VisualState x:Name="Informational"/>
<VisualState x:Name="Error">
<VisualState.Setters>
<Setter Target="ContentRoot.Background" Value="{ThemeResource InfoBarErrorSeverityBackgroundBrush}"/>
<Setter Target="IconBackground.Foreground" Value="{ThemeResource InfoBarErrorSeverityIconBackground}"/>
<Setter Target="StandardIcon.Text" Value="{StaticResource InfoBarErrorIconGlyph}"/>
<Setter Target="StandardIcon.Foreground" Value="{ThemeResource InfoBarErrorSeverityIconForeground}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Warning">
<VisualState.Setters>
<Setter Target="ContentRoot.Background" Value="{ThemeResource InfoBarWarningSeverityBackgroundBrush}"/>
<Setter Target="IconBackground.Foreground" Value="{ThemeResource InfoBarWarningSeverityIconBackground}"/>
<Setter Target="StandardIcon.Text" Value="{StaticResource InfoBarWarningIconGlyph}"/>
<Setter Target="StandardIcon.Foreground" Value="{ThemeResource InfoBarWarningSeverityIconForeground}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Success">
<VisualState.Setters>
<Setter Target="ContentRoot.Background" Value="{ThemeResource InfoBarSuccessSeverityBackgroundBrush}"/>
<Setter Target="IconBackground.Foreground" Value="{ThemeResource InfoBarSuccessSeverityIconBackground}"/>
<Setter Target="StandardIcon.Text" Value="{StaticResource InfoBarSuccessIconGlyph}"/>
<Setter Target="StandardIcon.Foreground" Value="{ThemeResource InfoBarSuccessSeverityIconForeground}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="IconStates">
<VisualState x:Name="StandardIconVisible">
<VisualState.Setters>
<Setter Target="UserIconBox.Visibility" Value="Collapsed"/>
<Setter Target="StandardIconArea.Visibility" Value="Visible"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="UserIconVisible">
<VisualState.Setters>
<Setter Target="UserIconBox.Visibility" Value="Visible"/>
<Setter Target="StandardIconArea.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="NoIconVisible"/>
</VisualStateGroup>
<VisualStateGroup>
<VisualState x:Name="CloseButtonVisible"/>
<VisualState x:Name="CloseButtonCollapsed">
<VisualState.Setters>
<Setter Target="CloseButton.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="InfoBarVisibility">
<VisualState x:Name="InfoBarVisible"/>
<VisualState x:Name="InfoBarCollapsed">
<VisualState.Setters>
<Setter Target="ContentRoot.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup>
<VisualState x:Name="ForegroundNotSet"/>
<VisualState x:Name="ForegroundSet">
<VisualState.Setters>
<Setter Target="Title.Foreground" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Foreground}"/>
<Setter Target="Message.Foreground" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Foreground}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,23 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cwcont="using:CommunityToolkit.WinUI.Controls">
<ItemsPanelTemplate x:Key="ItemsStackPanelTemplate">
<ItemsStackPanel/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="WrapPanelSpacing0Template">
<cwcont:WrapPanel/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="WrapPanelSpacing4Template">
<cwcont:WrapPanel HorizontalSpacing="4" VerticalSpacing="4"/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="HorizontalStackPanelTemplate">
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="UniformGridColumns5Spacing4Template">
<cwcont:UniformGrid
ColumnSpacing="4"
Columns="5"
RowSpacing="4"/>
</ItemsPanelTemplate>
</ResourceDictionary>

View File

@@ -0,0 +1,14 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Length -->
<GridLength x:Key="CompatGridLength2">288</GridLength>
<x:Double x:Key="CompatSplitViewOpenPaneLength">212</x:Double>
<x:Double x:Key="CompatSplitViewOpenPaneLength2">304</x:Double>
<x:Double x:Key="CompatSplitViewOpenPaneLength3">320</x:Double>
<x:Double x:Key="HomeAdaptiveCardHeight">180</x:Double>
<x:Double x:Key="ContentDialogMinHeight">64</x:Double>
<x:Double x:Key="LargeAppBarButtonWidth">100</x:Double>
<!-- ProgressBar -->
<x:Double x:Key="LargeBackgroundProgressBarOpacity">0.2</x:Double>
</ResourceDictionary>

View File

@@ -0,0 +1,6 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Page Transparent Background -->
<StaticResource x:Key="ApplicationPageBackgroundThemeBrush" ResourceKey="ControlFillColorTransparentBrush"/>
<!-- https://github.com/microsoft/microsoft-ui-xaml/issues/4811 -->
<x:Int32 x:Key="__DiscardPageOverride">0</x:Int32>
</ResourceDictionary>

View File

@@ -0,0 +1,5 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:Double x:Key="PivotHeaderItemFontSize">16</x:Double>
<Thickness x:Key="PivotHeaderItemMargin">16,0,0,0</Thickness>
<Thickness x:Key="PivotItemMargin">0</Thickness>
</ResourceDictionary>

View File

@@ -0,0 +1,35 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Settings -->
<x:Double x:Key="SettingsCardSpacing">3</x:Double>
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
<x:Double x:Key="SettingsCardContentControlMinWidth">120</x:Double>
<Style
x:Key="SettingsSectionHeaderTextBlockStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock">
<Style.Setters>
<Setter Property="Margin" Value="1,29,0,5"/>
</Style.Setters>
</Style>
<Style
x:Key="SettingsContentComboBoxStyle"
BasedOn="{StaticResource DefaultComboBoxStyle}"
TargetType="ComboBox">
<Setter Property="MinWidth" Value="{ThemeResource SettingsCardContentControlMinWidth}"/>
</Style>
<Style
x:Key="SettingButtonStyle"
BasedOn="{StaticResource DefaultButtonStyle}"
TargetType="Button">
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
<Setter Property="Padding" Value="16,6,16,6"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="MinWidth" Value="{ThemeResource SettingsCardContentControlMinWidth}"/>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TransitionCollection x:Key="ContentThemeTransitions">
<ContentThemeTransition/>
</TransitionCollection>
<TransitionCollection x:Key="EntranceThemeTransitions">
<EntranceThemeTransition/>
</TransitionCollection>
<TransitionCollection x:Key="ListViewLikeThemeTransitions">
<AddDeleteThemeTransition/>
<ContentThemeTransition/>
<ReorderThemeTransition/>
<EntranceThemeTransition IsStaggeringEnabled="False"/>
</TransitionCollection>
<TransitionCollection x:Key="ReorderThemeTransitions">
<ReorderThemeTransition/>
</TransitionCollection>
</ResourceDictionary>

View File

@@ -0,0 +1,34 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- DocumentLink -->
<x:String x:Key="DocumentLink_BugReport">https://hut.ao/statements/bug-report.html</x:String>
<x:String x:Key="DocumentLink_Home">https://hut.ao</x:String>
<x:String x:Key="DocumentLink_MhyAccountSwitch">https://hut.ao/features/mhy-account-switch.html</x:String>
<x:String x:Key="DocumentLink_Translate">https://translate.hut.ao</x:String>
<!-- Other -->
<x:String x:Key="HolographicHat_GetToken_Release">https://github.com/HolographicHat/GetToken/releases/latest</x:String>
<x:String x:Key="Sponsor_Afadian">https://afdian.net/a/DismissedLight</x:String>
<!-- AvatarCard -->
<x:String x:Key="UI_AvatarIcon_Costume_Card">https://static.snapgenshin.com/AvatarCard/UI_AvatarIcon_Costume_Card.png</x:String>
<!-- Bg -->
<x:String x:Key="UI_Icon_Intee_Explore_1">https://static.snapgenshin.com/Bg/UI_Icon_Intee_Explore_1.png</x:String>
<x:String x:Key="UI_ImgSign_ItemIcon">https://static.snapgenshin.com/Bg/UI_ImgSign_ItemIcon.png</x:String>
<x:String x:Key="UI_ItemIcon_None">https://static.snapgenshin.com/Bg/UI_ItemIcon_None.png</x:String>
<x:String x:Key="UI_MarkQuest_Events_Proce">https://static.snapgenshin.com/Bg/UI_MarkQuest_Events_Proce.png</x:String>
<x:String x:Key="UI_MarkTower">https://static.snapgenshin.com/Bg/UI_MarkTower.png</x:String>
<!-- ItemIcon -->
<x:String x:Key="UI_ItemIcon_201">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_201.png</x:String>
<x:String x:Key="UI_ItemIcon_204">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_204.png</x:String>
<x:String x:Key="UI_ItemIcon_210">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_210.png</x:String>
<x:String x:Key="UI_ItemIcon_220021">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_220021.png</x:String>
<!-- EmotionIcon -->
<x:String x:Key="UI_EmotionIcon25">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon25.png</x:String>
<x:String x:Key="UI_EmotionIcon71">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon71.png</x:String>
<x:String x:Key="UI_EmotionIcon250">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon250.png</x:String>
<x:String x:Key="UI_EmotionIcon272">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon272.png</x:String>
<x:String x:Key="UI_EmotionIcon293">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon293.png</x:String>
</ResourceDictionary>

View File

@@ -0,0 +1,5 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Modify Window title bar color -->
<StaticResource x:Key="WindowCaptionBackground" ResourceKey="ControlFillColorTransparentBrush"/>
<StaticResource x:Key="WindowCaptionBackgroundDisabled" ResourceKey="ControlFillColorTransparentBrush"/>
</ResourceDictionary>

View File

@@ -30,9 +30,6 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
[0] = TimeSpan.FromSeconds(4),
[1] = TimeSpan.FromSeconds(16),
[2] = TimeSpan.FromSeconds(64),
[3] = TimeSpan.FromSeconds(4),
[4] = TimeSpan.FromSeconds(16),
[5] = TimeSpan.FromSeconds(64),
};
private readonly ILogger logger;
@@ -173,7 +170,7 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
int retryCount = 0;
HttpClient httpClient = httpClientFactory.CreateClient(nameof(ImageCache));
while (retryCount < 6)
while (retryCount < 3)
{
using (HttpResponseMessage message = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
{
@@ -191,10 +188,6 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
switch (message.StatusCode)
{
case HttpStatusCode.NotFound:
// directly goto https://static.hut.ao
retryCount += 3;
break;
case HttpStatusCode.TooManyRequests:
{
retryCount++;
@@ -208,11 +201,6 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
return;
}
}
if (retryCount == 3)
{
uri = new UriBuilder(uri) { Host = Web.HutaoEndpoints.StaticHutao }.Uri;
}
}
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Windows.ApplicationModel.Resources;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Service;
using System.Globalization;

View File

@@ -14,9 +14,7 @@ namespace Snap.Hutao.Core.ExceptionService;
internal sealed partial class ExceptionRecorder
{
private readonly ILogger<ExceptionRecorder> logger;
#if RELEASE
private readonly IServiceProvider serviceProvider;
#endif
/// <summary>
/// 记录应用程序异常
@@ -29,17 +27,17 @@ internal sealed partial class ExceptionRecorder
app.DebugSettings.XamlResourceReferenceFailed += OnXamlResourceReferenceFailed;
}
[SuppressMessage("", "CA2012")]
private void OnAppUnhandledException(object? sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
{
#if RELEASE
#pragma warning disable VSTHRD002
serviceProvider
ValueTask<string?> task = serviceProvider
.GetRequiredService<Web.Hutao.Log.HomaLogUploadClient>()
.UploadLogAsync(serviceProvider, e.Exception)
.GetAwaiter()
.GetResult();
#pragma warning restore VSTHRD002
#endif
.UploadLogAsync(e.Exception);
if (!task.IsCompleted)
{
task.GetAwaiter().GetResult();
}
logger.LogError("未经处理的全局异常:\r\n{Detail}", ExceptionFormat.Format(e.Exception));
}

View File

@@ -15,8 +15,8 @@ internal sealed class RuntimeEnvironmentException : Exception
/// </summary>
/// <param name="message">消息</param>
/// <param name="innerException">内部错误</param>
public RuntimeEnvironmentException(string message, Exception innerException)
: base($"{message}\n{innerException.Message}", innerException)
public RuntimeEnvironmentException(string message, Exception? innerException)
: base($"{message}\n{innerException?.Message}", innerException)
{
}
}

View File

@@ -14,27 +14,27 @@ namespace Snap.Hutao.Core.ExceptionService;
[System.Diagnostics.StackTraceHidden]
internal static class ThrowHelper
{
/// <summary>
/// 操作取消
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="OperationCanceledException">操作取消异常</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static OperationCanceledException OperationCanceled(string message, Exception? inner = default)
public static ArgumentException Argument(string message, string? paramName)
{
throw new OperationCanceledException(message, inner);
throw new ArgumentException(message, paramName);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static DatabaseCorruptedException DatabaseCorrupted(string message, Exception? inner)
{
throw new DatabaseCorruptedException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static GameFileOperationException GameFileOperation(string message, Exception? inner)
{
throw new GameFileOperationException(message, inner);
}
/// <summary>
/// 无效操作
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="InvalidOperationException">无效操作异常</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static InvalidOperationException InvalidOperation(string message, Exception? inner = default)
@@ -42,71 +42,38 @@ internal static class ThrowHelper
throw new InvalidOperationException(message, inner);
}
/// <summary>
/// 游戏文件操作失败
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="GameFileOperationException">文件操作失败</exception>
public static GameFileOperationException GameFileOperation(string message, Exception inner)
{
throw new GameFileOperationException(message, inner);
}
/// <summary>
/// 包转换错误
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="PackageConvertException">包转换错误异常</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static PackageConvertException PackageConvert(string message, Exception inner)
{
throw new PackageConvertException(message, inner);
}
/// <summary>
/// 用户数据损坏
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="UserdataCorruptedException">数据损坏</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static UserdataCorruptedException UserdataCorrupted(string message, Exception inner)
{
throw new UserdataCorruptedException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static DatabaseCorruptedException DatabaseCorrupted(string message, Exception inner)
{
throw new DatabaseCorruptedException(message, inner);
}
/// <summary>
/// 运行环境异常
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="RuntimeEnvironmentException">环境异常</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static RuntimeEnvironmentException RuntimeEnvironment(string message, Exception inner)
{
throw new RuntimeEnvironmentException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static NotSupportedException NotSupported()
{
throw new NotSupportedException();
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static OperationCanceledException OperationCanceled(string message, Exception? inner = default)
{
throw new OperationCanceledException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static PackageConvertException PackageConvert(string message, Exception? inner)
{
throw new PackageConvertException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static RuntimeEnvironmentException RuntimeEnvironment(string message, Exception? inner)
{
throw new RuntimeEnvironmentException(message, inner);
}
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static UserdataCorruptedException UserdataCorrupted(string message, Exception? inner)
{
throw new UserdataCorruptedException(message, inner);
}
}

View File

@@ -1,9 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Notifications;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Windows.AppLifecycle;
using Microsoft.Windows.AppNotifications;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Service.DailyNote;
using Snap.Hutao.Service.Hutao;
@@ -23,24 +23,9 @@ namespace Snap.Hutao.Core.LifeCycle;
[SuppressMessage("", "CA1001")]
internal sealed partial class Activation : IActivation
{
/// <summary>
/// 操作
/// </summary>
public const string Action = nameof(Action);
/// <summary>
/// Uid
/// </summary>
public const string Uid = nameof(Uid);
/// <summary>
/// 启动游戏启动参数
/// </summary>
public const string LaunchGame = nameof(LaunchGame);
/// <summary>
/// 从剪贴板导入成就
/// </summary>
public const string ImportUIAFFromClipboard = nameof(ImportUIAFFromClipboard);
private const string CategoryAchievement = "ACHIEVEMENT";
@@ -52,50 +37,50 @@ internal sealed partial class Activation : IActivation
private readonly ICurrentWindowReference currentWindowReference;
private readonly ITaskContext taskContext;
private readonly SemaphoreSlim activateSemaphore = new(1);
/// <inheritdoc/>
public void Activate(object? sender, AppActivationArguments args)
{
_ = sender;
if (!ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
{
HandleActivationAsync(args, true).SafeForget();
}
}
private readonly AppNotificationManager appNotificationManager = AppNotificationManager.Default;
/// <inheritdoc/>
public void NonRedirectToActivate(object? sender, AppActivationArguments args)
{
_ = sender;
if (!ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
{
HandleActivationAsync(args, false).SafeForget();
}
HandleActivationAsync(args, false).SafeForget();
}
/// <inheritdoc/>
public void InitializeWith(AppInstance appInstance)
{
appInstance.Activated += Activate;
ToastNotificationManagerCompat.OnActivated += NotificationActivate;
appInstance.Activated += OnAppInstanceActivate;
appNotificationManager.NotificationInvoked += OnAppNotificationManagerNotificationInvoked;
appNotificationManager.Register();
}
private void NotificationActivate(ToastNotificationActivatedEventArgsCompat args)
private void OnAppInstanceActivate(object? sender, AppActivationArguments args)
{
ToastArguments toastArgs = ToastArguments.Parse(args.Argument);
HandleActivationAsync(args, true).SafeForget();
}
if (toastArgs.TryGetValue(Action, out string? action))
private void OnAppNotificationManagerNotificationInvoked(AppNotificationManager manager, AppNotificationActivatedEventArgs args)
{
IDictionary<string, string> arguments = args.Arguments;
HandleAppNotificationActivationAsync(arguments).SafeForget();
}
private async ValueTask HandleAppNotificationActivationAsync(IDictionary<string, string> arguments)
{
if (arguments.TryGetValue(Action, out string? action))
{
if (action == LaunchGame)
{
_ = toastArgs.TryGetValue(Uid, out string? uid);
HandleLaunchGameActionAsync(uid).SafeForget();
if (arguments.TryGetValue(Uid, out string? uid))
{
await HandleLaunchGameActionAsync(uid).ConfigureAwait(false);
}
}
}
}
private async ValueTask HandleActivationAsync(AppActivationArguments args, bool isRedirected)
{
// Refuse other activations
if (activateSemaphore.CurrentCount > 0)
{
using (await activateSemaphore.EnterAsync().ConfigureAwait(false))
@@ -107,32 +92,42 @@ internal sealed partial class Activation : IActivation
private async ValueTask HandleActivationCoreAsync(AppActivationArguments args, bool isRedirected)
{
if (args.Kind == ExtendedActivationKind.Protocol)
switch (args.Kind)
{
if (args.TryGetProtocolActivatedUri(out Uri? uri))
{
await HandleUrlActivationAsync(uri, isRedirected).ConfigureAwait(false);
}
}
else if (args.Kind == ExtendedActivationKind.Launch)
{
if (args.TryGetLaunchActivatedArgument(out string? arguments))
{
switch (arguments)
case ExtendedActivationKind.Launch:
if (args.TryGetLaunchActivatedArgument(out string? command))
{
case LaunchGame:
{
await HandleLaunchGameActionAsync().ConfigureAwait(false);
break;
}
switch (command)
{
case LaunchGame:
{
await HandleLaunchGameActionAsync().ConfigureAwait(false);
break;
}
default:
{
await HandleNormalLaunchActionAsync().ConfigureAwait(false);
break;
}
default:
{
await HandleNormalLaunchActionAsync().ConfigureAwait(false);
break;
}
}
}
}
break;
case ExtendedActivationKind.AppNotification:
if (args.TryGetAppNotificationActivatedArgument(out IDictionary<string, string>? arguments))
{
await HandleAppNotificationActivationAsync(arguments).ConfigureAwait(false);
}
break;
case ExtendedActivationKind.Protocol:
if (args.TryGetProtocolActivatedUri(out Uri? uri))
{
await HandleUrlActivationAsync(uri, isRedirected).ConfigureAwait(false);
}
break;
}
}
@@ -141,6 +136,11 @@ internal sealed partial class Activation : IActivation
// Increase launch times
LocalSetting.Set(SettingKeys.LaunchTimes, LocalSetting.Get(SettingKeys.LaunchTimes, 0) + 1);
if (StaticResource.IsAnyUnfulfilledCategoryPresent())
{
LocalSetting.Set(SettingKeys.Major1Minor7Revision0GuideState, (uint)GuideState.StaticResourceBegin);
}
if (LocalSetting.Get(SettingKeys.Major1Minor7Revision0GuideState, (uint)GuideState.Language) < (uint)GuideState.Completed)
{
await taskContext.SwitchToMainThreadAsync();
@@ -207,8 +207,6 @@ internal sealed partial class Activation : IActivation
private async ValueTask HandleAchievementActionAsync(string action, string parameter, bool isRedirected)
{
_ = parameter;
_ = isRedirected;
switch (action)
{
case UrlActionImport:
@@ -227,7 +225,6 @@ internal sealed partial class Activation : IActivation
private async ValueTask HandleDailyNoteActionAsync(string action, string parameter, bool isRedirected)
{
_ = parameter;
switch (action)
{
case UrlActionRefresh:

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.Windows.AppLifecycle;
using Microsoft.Windows.AppNotifications;
using Windows.ApplicationModel.Activation;
namespace Snap.Hutao.Core.LifeCycle;
@@ -47,4 +48,16 @@ internal static class AppActivationArgumentsExtensions
arguments = launchArgs.Arguments.Trim();
return true;
}
public static bool TryGetAppNotificationActivatedArgument(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out IDictionary<string, string>? arguments)
{
arguments = null;
if (activatedEventArgs.Data is not AppNotificationActivatedEventArgs launchArgs)
{
return false;
}
arguments = launchArgs.Arguments;
return true;
}
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
namespace Snap.Hutao.Core.LifeCycle;
internal static class CurrentWindowReferenceExtension
{
public static XamlRoot GetXamlRoot(this ICurrentWindowReference reference)
{
return reference.Window.Content.XamlRoot;
}
}

View File

@@ -10,14 +10,6 @@ namespace Snap.Hutao.Core.LifeCycle;
/// </summary>
internal interface IActivation
{
/// <summary>
/// 响应激活事件
/// 激活事件一般不会在UI线程上触发
/// </summary>
/// <param name="sender">发送方</param>
/// <param name="args">激活参数</param>
void Activate(object? sender, AppActivationArguments args);
/// <summary>
/// 使用当前 App 实例初始化激活
/// </summary>

View File

@@ -8,4 +8,4 @@ namespace Snap.Hutao.Core.LifeCycle;
internal interface ICurrentWindowReference
{
public Window Window { get; set; }
}
}

View File

@@ -1,84 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.Setting;
/// <summary>
/// 静态资源
/// </summary>
[HighQuality]
internal static class StaticResource
{
/// <summary>
/// 静态资源合约
/// </summary>
public const string V1Contract = "StaticResourceV1Contract";
/// <summary>
/// 静态资源合约V2 成就图标与物品图标
/// </summary>
public const string V2Contract = "StaticResourceV2Contract";
/// <summary>
/// 静态资源合约V3 刷新 Skill Talent
/// </summary>
public const string V3Contract = "StaticResourceV3Contract";
/// <summary>
/// 静态资源合约V4 刷新 AvatarIcon
/// </summary>
public const string V4Contract = "StaticResourceV4Contract";
/// <summary>
/// 静态资源合约V5 刷新 AvatarIcon
/// </summary>
public const string V5Contract = "StaticResourceV5Contract";
/// <summary>
/// 完成所有合约
/// </summary>
public static void FulfillAllContracts()
{
SetContractsState(true);
}
/// <summary>
/// 取消完成所有合约
/// </summary>
public static void FailAllContracts()
{
SetContractsState(false);
}
/// <summary>
/// 提供的合约是否未完成
/// </summary>
/// <param name="contractKey">合约的键</param>
/// <returns>合约是否未完成</returns>
public static bool IsContractUnfulfilled(string contractKey)
{
return !LocalSetting.Get(contractKey, false);
}
/// <summary>
/// 是否有任何静态资源合约尚未完成
/// </summary>
/// <returns>静态资源合约尚未完成</returns>
public static bool IsAnyUnfulfilledContractPresent()
{
return !LocalSetting.Get(V1Contract, false)
|| (!LocalSetting.Get(V2Contract, false))
|| (!LocalSetting.Get(V3Contract, false))
|| (!LocalSetting.Get(V4Contract, false))
|| (!LocalSetting.Get(V5Contract, false));
}
private static void SetContractsState(bool state)
{
LocalSetting.Set(V1Contract, state);
LocalSetting.Set(V2Contract, state);
LocalSetting.Set(V3Contract, state);
LocalSetting.Set(V4Contract, state);
LocalSetting.Set(V5Contract, state);
}
}

View File

@@ -5,5 +5,5 @@ namespace Snap.Hutao.Core.Shell;
internal interface IShellLinkInterop
{
void CreateDesktopShoutcutForElevatedLaunch();
ValueTask<bool> TryCreateDesktopShoutcutForElevatedLaunchAsync();
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using System.IO;
using Windows.Storage;
using Windows.Win32;
using Windows.Win32.System.Com;
using Windows.Win32.UI.Shell;
@@ -15,11 +16,26 @@ internal sealed partial class ShellLinkInterop : IShellLinkInterop
{
private readonly RuntimeOptions runtimeOptions;
public void CreateDesktopShoutcutForElevatedLaunch()
public async ValueTask<bool> TryCreateDesktopShoutcutForElevatedLaunchAsync()
{
string sourceLogoPath = Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico");
Uri sourceLogoUri = "ms-appx:///Assets/Logo.ico".ToUri();
string targetLogoPath = Path.Combine(runtimeOptions.DataFolder, "ShellLinkLogo.ico");
File.Copy(sourceLogoPath, targetLogoPath, true);
try
{
StorageFile iconFile = await StorageFile.GetFileFromApplicationUriAsync(sourceLogoUri);
using (Stream inputStream = (await iconFile.OpenReadAsync()).AsStream())
{
using (FileStream outputStream = File.Create(targetLogoPath))
{
await inputStream.CopyToAsync(outputStream).ConfigureAwait(false);
}
}
}
catch
{
return false;
}
IShellLinkW shellLink = (IShellLinkW)new ShellLink();
shellLink.SetPath("powershell");
@@ -33,6 +49,15 @@ internal sealed partial class ShellLinkInterop : IShellLinkInterop
string target = Path.Combine(desktop, $"{SH.AppNameAndVersion.Format(runtimeOptions.Version)}.lnk");
IPersistFile persistFile = (IPersistFile)shellLink;
persistFile.Save(target, false);
try
{
persistFile.Save(target, false);
}
catch (UnauthorizedAccessException)
{
return false;
}
return true;
}
}

View File

@@ -5,9 +5,8 @@ using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using Windows.Graphics;
using Windows.Win32.UI.WindowsAndMessaging;
namespace Snap.Hutao.Win32;
namespace Snap.Hutao.Core;
/// <summary>
/// 结构体封送
@@ -15,27 +14,6 @@ namespace Snap.Hutao.Win32;
[HighQuality]
internal static class StructMarshal
{
/// <summary>
/// 构造一个新的 <see cref="Windows.Win32.UI.WindowsAndMessaging.WINDOWPLACEMENT"/>
/// </summary>
/// <returns>新的实例</returns>
public static unsafe WINDOWPLACEMENT WINDOWPLACEMENT()
{
return new() { length = SizeOf<WINDOWPLACEMENT>() };
}
/// <summary>
/// 获取结构的大小
/// </summary>
/// <typeparam name="TStruct">结构类型</typeparam>
/// <returns>结构的大小</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe uint SizeOf<TStruct>()
where TStruct : unmanaged
{
return unchecked((uint)sizeof(TStruct));
}
/// <summary>
/// 使用四字节颜色代码初始化一个新的颜色
/// </summary>

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Dispatching;
using System.Runtime.ExceptionServices;
namespace Snap.Hutao.Core.Threading;
@@ -18,15 +19,35 @@ internal static class DispatcherQueueExtension
/// <param name="action">执行的回调</param>
public static void Invoke(this DispatcherQueue dispatcherQueue, Action action)
{
using (ManualResetEventSlim blockEvent = new())
if (dispatcherQueue.HasThreadAccess)
{
action();
return;
}
ExceptionDispatchInfo? exceptionDispatchInfo = null;
using (ManualResetEventSlim blockEvent = new(false))
{
dispatcherQueue.TryEnqueue(() =>
{
action();
blockEvent.Set();
try
{
action();
}
catch (Exception ex)
{
ExceptionDispatchInfo.Capture(ex);
}
finally
{
blockEvent.Set();
}
});
blockEvent.Wait();
#pragma warning disable CA1508
exceptionDispatchInfo?.Throw();
#pragma warning restore CA1508
}
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Dispatching;
namespace Snap.Hutao.Core.Threading;
internal sealed class DispatcherQueueSynchronizationContextSendSupport : SynchronizationContext
{
private readonly DispatcherQueue dispatcherQueue;
public DispatcherQueueSynchronizationContextSendSupport(DispatcherQueue dispatcherQueue)
{
this.dispatcherQueue = dispatcherQueue;
}
public override void Post(SendOrPostCallback d, object? state)
{
ArgumentNullException.ThrowIfNull(d);
dispatcherQueue.TryEnqueue(() => d(state));
}
public override void Send(SendOrPostCallback d, object? state)
{
ArgumentNullException.ThrowIfNull(d);
dispatcherQueue.Invoke(() => d(state));
}
public override SynchronizationContext CreateCopy()
{
return new DispatcherQueueSynchronizationContextSendSupport(dispatcherQueue);
}
}

View File

@@ -11,7 +11,7 @@ namespace Snap.Hutao.Core.Threading;
[Injection(InjectAs.Singleton, typeof(ITaskContext))]
internal sealed class TaskContext : ITaskContext
{
private readonly DispatcherQueueSynchronizationContext dispatcherQueueSynchronizationContext;
private readonly DispatcherQueueSynchronizationContextSendSupport synchronizationContext;
private readonly DispatcherQueue dispatcherQueue;
/// <summary>
@@ -20,8 +20,8 @@ internal sealed class TaskContext : ITaskContext
public TaskContext()
{
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
dispatcherQueueSynchronizationContext = new(dispatcherQueue);
SynchronizationContext.SetSynchronizationContext(dispatcherQueueSynchronizationContext);
synchronizationContext = new(dispatcherQueue);
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
}
/// <inheritdoc/>
@@ -39,18 +39,11 @@ internal sealed class TaskContext : ITaskContext
/// <inheritdoc/>
public void InvokeOnMainThread(Action action)
{
if (dispatcherQueue.HasThreadAccess)
{
action();
}
else
{
dispatcherQueue.Invoke(action);
}
dispatcherQueue.Invoke(action);
}
public IProgress<T> CreateProgressForMainThread<T>(Action<T> handler)
{
return new DispatcherQueueProgress<T>(handler, dispatcherQueueSynchronizationContext);
return new DispatcherQueueProgress<T>(handler, synchronizationContext);
}
}

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Microsoft.UI.Windowing;
using Snap.Hutao.Win32;
using Windows.Graphics;
namespace Snap.Hutao.Core.Windowing;

View File

@@ -9,23 +9,28 @@ namespace Snap.Hutao.Core.Windowing;
[HighQuality]
internal enum BackdropType
{
/// <summary>
/// 透明
/// </summary>
Transparent = -1,
/// <summary>
/// 无
/// </summary>
None,
None = 0,
/// <summary>
/// 亚克力
/// </summary>
Acrylic,
Acrylic = 1,
/// <summary>
/// 云母
/// </summary>
Mica,
Mica = 2,
/// <summary>
/// 变种云母
/// </summary>
MicaAlt,
MicaAlt = 3,
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.CompilerServices;
using Windows.Graphics;
namespace Snap.Hutao.Core.Windowing;
internal readonly struct CompactRect
{
private readonly short x;
private readonly short y;
private readonly short width;
private readonly short height;
private CompactRect(int x, int y, int width, int height)
{
this.x = (short)x;
this.y = (short)y;
this.width = (short)width;
this.height = (short)height;
}
public static implicit operator RectInt32(CompactRect rect)
{
return new(rect.x, rect.y, rect.width, rect.height);
}
public static explicit operator CompactRect(RectInt32 rect)
{
return new(rect.X, rect.Y, rect.Width, rect.Height);
}
public static unsafe explicit operator CompactRect(ulong value)
{
Unsafe.SkipInit(out CompactRect rect);
*(ulong*)&rect = value;
return rect;
}
public static unsafe implicit operator ulong(CompactRect rect)
{
return *(ulong*)&rect;
}
}

View File

@@ -0,0 +1,105 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.InteropServices;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using static Windows.Win32.PInvoke;
namespace Snap.Hutao.Core.Windowing.HotKey;
[SuppressMessage("", "CA1001")]
internal sealed class HotKeyController : IHotKeyController
{
private const int DefaultId = 100000;
private readonly object locker = new();
private readonly WaitCallback runMouseClickRepeatForever;
private readonly HotKeyOptions hotKeyOptions;
private readonly RuntimeOptions runtimeOptions;
private volatile CancellationTokenSource? cancellationTokenSource;
public HotKeyController(IServiceProvider serviceProvider)
{
hotKeyOptions = serviceProvider.GetRequiredService<HotKeyOptions>();
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
runMouseClickRepeatForever = MouseClickRepeatForever;
}
public bool Register(in HWND hwnd)
{
if (runtimeOptions.IsElevated)
{
return RegisterHotKey(hwnd, DefaultId, default, (uint)VIRTUAL_KEY.VK_F8);
}
return false;
}
public bool Unregister(in HWND hwnd)
{
if (runtimeOptions.IsElevated)
{
return UnregisterHotKey(hwnd, DefaultId);
}
return false;
}
public void OnHotKeyPressed(in HotKeyParameter parameter)
{
if (parameter is { Key: VIRTUAL_KEY.VK_F8, NativeModifier: 0 })
{
lock (locker)
{
if (hotKeyOptions.IsMouseClickRepeatForeverOn)
{
cancellationTokenSource?.Cancel();
cancellationTokenSource = default;
hotKeyOptions.IsMouseClickRepeatForeverOn = false;
}
else
{
cancellationTokenSource = new();
ThreadPool.QueueUserWorkItem(runMouseClickRepeatForever, cancellationTokenSource.Token);
hotKeyOptions.IsMouseClickRepeatForeverOn = true;
}
}
}
}
private static unsafe INPUT CreateInputForMouseEvent(MOUSE_EVENT_FLAGS flags)
{
INPUT input = new() { type = INPUT_TYPE.INPUT_MOUSE, };
input.Anonymous.mi.dwFlags = flags;
return input;
}
[SuppressMessage("", "SH007")]
private unsafe void MouseClickRepeatForever(object? state)
{
CancellationToken token = (CancellationToken)state!;
// We want to use this thread for a long time
while (!token.IsCancellationRequested)
{
INPUT[] inputs =
{
CreateInputForMouseEvent(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN),
CreateInputForMouseEvent(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP),
};
if (SendInput(inputs.AsSpan(), sizeof(INPUT)) is 0)
{
Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
}
if (token.IsCancellationRequested)
{
return;
}
Thread.Sleep(Random.Shared.Next(100, 150));
}
}
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
namespace Snap.Hutao.Core.Windowing.HotKey;
[Injection(InjectAs.Singleton)]
internal sealed class HotKeyOptions : ObservableObject
{
private bool isVirtualKeyF8Pressed;
public bool IsMouseClickRepeatForeverOn { get => isVirtualKeyF8Pressed; set => SetProperty(ref isVirtualKeyF8Pressed, value); }
}

View File

@@ -0,0 +1,17 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Windows.Win32.UI.Input.KeyboardAndMouse;
namespace Snap.Hutao.Core.Windowing.HotKey;
internal readonly struct HotKeyParameter
{
public readonly ushort NativeModifier;
public readonly VIRTUAL_KEY Key;
public readonly HOT_KEY_MODIFIERS Modifier
{
get => (HOT_KEY_MODIFIERS)NativeModifier;
}
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Windows.Win32.Foundation;
namespace Snap.Hutao.Core.Windowing.HotKey;
internal interface IHotKeyController
{
void OnHotKeyPressed(in HotKeyParameter parameter);
bool Register(in HWND hwnd);
bool Unregister(in HWND hwnd);
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Windows.Win32.UI.WindowsAndMessaging;
namespace Snap.Hutao.Core.Windowing;
internal interface IMinMaxInfoHandler
{
/// <summary>
/// 处理最大最小信息
/// </summary>
/// <param name="info">信息</param>
/// <param name="scalingFactor">缩放比</param>
unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor);
}

View File

@@ -1,8 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Windows.Win32.UI.WindowsAndMessaging;
namespace Snap.Hutao.Core.Windowing;
/// <summary>
@@ -14,11 +12,4 @@ internal interface IWindowOptionsSource
/// 窗体选项
/// </summary>
WindowOptions WindowOptions { get; }
/// <summary>
/// 处理最大最小信息
/// </summary>
/// <param name="pInfo">信息指针</param>
/// <param name="scalingFactor">缩放比</param>
unsafe void ProcessMinMaxInfo(MINMAXINFO* pInfo, double scalingFactor);
}

View File

@@ -1,113 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Win32;
using System.Runtime.CompilerServices;
using Windows.Graphics;
using Windows.Win32.UI.WindowsAndMessaging;
using static Windows.Win32.PInvoke;
namespace Snap.Hutao.Core.Windowing;
/// <summary>
/// 窗体持久化
/// </summary>
[HighQuality]
internal static class Persistence
{
/// <summary>
/// 设置窗体位置
/// </summary>
/// <typeparam name="TWindow">窗体类型</typeparam>
/// <param name="window">选项窗口</param>
public static void RecoverOrInit<TWindow>(TWindow window)
where TWindow : Window, IWindowOptionsSource
{
WindowOptions options = window.WindowOptions;
// Set first launch size
double scale = options.GetWindowScale();
SizeInt32 transformedSize = options.InitSize.Scale(scale);
RectInt32 rect = StructMarshal.RectInt32(transformedSize);
if (options.PersistSize)
{
RectInt32 persistedRect = (CompactRect)LocalSetting.Get(SettingKeys.WindowRect, (CompactRect)rect);
if (persistedRect.Size() >= options.InitSize.Size())
{
rect = persistedRect;
}
}
TransformToCenterScreen(ref rect);
window.AppWindow.MoveAndResize(rect);
}
/// <summary>
/// 保存窗体的位置
/// </summary>
/// <param name="window">窗口</param>
/// <typeparam name="TWindow">窗体类型</typeparam>
public static void Save<TWindow>(TWindow window)
where TWindow : Window, IWindowOptionsSource
{
WINDOWPLACEMENT windowPlacement = StructMarshal.WINDOWPLACEMENT();
GetWindowPlacement(window.WindowOptions.Hwnd, ref windowPlacement);
// prevent save value when we are maximized.
if (!windowPlacement.showCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
{
LocalSetting.Set(SettingKeys.WindowRect, (CompactRect)window.AppWindow.GetRect());
}
}
private static void TransformToCenterScreen(ref RectInt32 rect)
{
DisplayArea displayArea = DisplayArea.GetFromRect(rect, DisplayAreaFallback.Primary);
RectInt32 workAreaRect = displayArea.WorkArea;
rect.X = workAreaRect.X + ((workAreaRect.Width - rect.Width) / 2);
rect.Y = workAreaRect.Y + ((workAreaRect.Height - rect.Height) / 2);
}
private readonly struct CompactRect
{
private readonly short x;
private readonly short y;
private readonly short width;
private readonly short height;
private CompactRect(int x, int y, int width, int height)
{
this.x = (short)x;
this.y = (short)y;
this.width = (short)width;
this.height = (short)height;
}
public static implicit operator RectInt32(CompactRect rect)
{
return new(rect.x, rect.y, rect.width, rect.height);
}
public static explicit operator CompactRect(RectInt32 rect)
{
return new(rect.X, rect.Y, rect.Width, rect.Height);
}
public static unsafe explicit operator CompactRect(ulong value)
{
Unsafe.SkipInit(out CompactRect rect);
*(ulong*)&rect = value;
return rect;
}
public static unsafe implicit operator ulong(CompactRect rect)
{
return *(ulong*)&rect;
}
}
}

View File

@@ -1,73 +1,63 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI;
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Snap.Hutao.Message;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Service;
using Snap.Hutao.Win32;
using System.IO;
using Windows.Graphics;
using Windows.UI;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;
using Windows.Win32.UI.WindowsAndMessaging;
using static Windows.Win32.PInvoke;
namespace Snap.Hutao.Core.Windowing;
/// <summary>
/// 扩展窗口
/// </summary>
/// <typeparam name="TWindow">窗体类型</typeparam>
[SuppressMessage("", "CA1001")]
internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutStateChangedMessage>
where TWindow : Window, IWindowOptionsSource
internal sealed class WindowController
{
private readonly TWindow window;
private readonly Window window;
private readonly WindowOptions options;
private readonly IServiceProvider serviceProvider;
private readonly WindowSubclass<TWindow> subclass;
private readonly WindowSubclass subclass;
private ExtendedWindow(TWindow window, IServiceProvider serviceProvider)
public WindowController(Window window, in WindowOptions options, IServiceProvider serviceProvider)
{
this.window = window;
this.options = options;
this.serviceProvider = serviceProvider;
subclass = new(window);
subclass = new(window, options, serviceProvider);
InitializeWindow();
InitializeCore();
}
/// <summary>
/// 初始化
/// </summary>
/// <param name="window">窗口</param>
/// <param name="serviceProvider">服务提供器</param>
/// <returns>实例</returns>
public static ExtendedWindow<TWindow> Initialize(TWindow window, IServiceProvider serviceProvider)
private static void TransformToCenterScreen(ref RectInt32 rect)
{
return new(window, serviceProvider);
DisplayArea displayArea = DisplayArea.GetFromRect(rect, DisplayAreaFallback.Nearest);
RectInt32 workAreaRect = displayArea.WorkArea;
rect.Width = Math.Min(workAreaRect.Width, rect.Width);
rect.Height = Math.Min(workAreaRect.Height, rect.Height);
rect.X = workAreaRect.X + ((workAreaRect.Width - rect.Width) / 2);
rect.Y = workAreaRect.Y + ((workAreaRect.Height - rect.Height) / 2);
}
/// <inheritdoc/>
public void Receive(FlyoutStateChangedMessage message)
{
UpdateDragRectangles(message.IsOpen);
}
private void InitializeWindow()
private void InitializeCore()
{
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
WindowOptions options = window.WindowOptions;
window.AppWindow.Title = SH.AppNameAndVersion.Format(hutaoOptions.Version);
window.AppWindow.SetIcon(Path.Combine(hutaoOptions.InstalledLocation, "Assets/Logo.ico"));
ExtendsContentIntoTitleBar();
Persistence.RecoverOrInit(window);
RecoverOrInitWindowSize();
UpdateImmersiveDarkMode(options.TitleBar, default!);
// appWindow.Show(true);
@@ -81,34 +71,67 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutStateChangedMes
subclass.Initialize();
serviceProvider.GetRequiredService<IMessenger>().Register(this);
window.Closed += OnWindowClosed;
options.TitleBar.ActualThemeChanged += UpdateImmersiveDarkMode;
}
private void RecoverOrInitWindowSize()
{
// Set first launch size
double scale = options.GetWindowScale();
SizeInt32 scaledSize = options.InitSize.Scale(scale);
RectInt32 rect = StructMarshal.RectInt32(scaledSize);
if (options.PersistSize)
{
RectInt32 persistedRect = (CompactRect)LocalSetting.Get(SettingKeys.WindowRect, (CompactRect)rect);
if (persistedRect.Size() >= options.InitSize.Size())
{
rect = persistedRect.Scale(scale);
}
}
TransformToCenterScreen(ref rect);
window.AppWindow.MoveAndResize(rect);
}
private void SaveOrSkipWindowSize()
{
if (!options.PersistSize)
{
return;
}
WINDOWPLACEMENT windowPlacement = Win32.StructMarshal.WINDOWPLACEMENT();
GetWindowPlacement(options.Hwnd, ref windowPlacement);
// prevent save value when we are maximized.
if (!windowPlacement.showCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
{
double scale = 1 / options.GetWindowScale();
LocalSetting.Set(SettingKeys.WindowRect, (CompactRect)window.AppWindow.GetRect().Scale(scale));
}
}
private void OnOptionsPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(AppOptions.BackdropType))
{
ArgumentNullException.ThrowIfNull(sender);
UpdateSystemBackdrop(((AppOptions)sender).BackdropType);
if (sender is AppOptions options)
{
UpdateSystemBackdrop(options.BackdropType);
}
}
}
private void OnWindowClosed(object sender, WindowEventArgs args)
{
if (window.WindowOptions.PersistSize)
{
Persistence.Save(window);
}
SaveOrSkipWindowSize();
subclass?.Dispose();
}
private void ExtendsContentIntoTitleBar()
{
WindowOptions options = window.WindowOptions;
if (options.UseLegacyDragBarImplementation)
{
// use normal Window method to extend.
@@ -168,31 +191,17 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutStateChangedMes
private unsafe void UpdateImmersiveDarkMode(FrameworkElement titleBar, object discard)
{
BOOL isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(titleBar.ActualTheme);
DwmSetWindowAttribute(window.WindowOptions.Hwnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, unchecked((uint)sizeof(BOOL)));
DwmSetWindowAttribute(options.Hwnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, unchecked((uint)sizeof(BOOL)));
}
private void UpdateDragRectangles(bool isFlyoutOpened = false)
private void UpdateDragRectangles()
{
AppWindowTitleBar appTitleBar = window.AppWindow.TitleBar;
if (isFlyoutOpened)
{
// set to 0
appTitleBar.SetDragRectangles(default(RectInt32).ToArray());
}
else
{
WindowOptions options = window.WindowOptions;
double scale = options.GetWindowScale();
double scale = options.GetWindowScale();
// 48 is the navigation button leftInset
RectInt32 dragRect = StructMarshal.RectInt32(48, 0, options.TitleBar.ActualSize).Scale(scale);
appTitleBar.SetDragRectangles(dragRect.ToArray());
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/2976
SizeInt32 size = window.AppWindow.ClientSize;
size.Height -= (int)(31 * scale);
window.AppWindow.ResizeClient(size);
}
// 48 is the navigation button leftInset
RectInt32 dragRect = StructMarshal.RectInt32(48, 0, options.TitleBar.ActualSize).Scale(scale);
appTitleBar.SetDragRectangles(dragRect.ToArray());
}
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Core.Windowing;
internal static class WindowExtension
{
private static readonly ConditionalWeakTable<Window, WindowController> WindowControllers = new();
public static void InitializeController<TWindow>(this TWindow window, IServiceProvider serviceProvider)
where TWindow : Window, IWindowOptionsSource
{
WindowController windowController = new(window, window.WindowOptions, serviceProvider);
WindowControllers.Add(window, windowController);
}
}

View File

@@ -2,6 +2,8 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Snap.Hutao.Core.Windowing.HotKey;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;
using Windows.Win32.UI.WindowsAndMessaging;
@@ -12,27 +14,27 @@ namespace Snap.Hutao.Core.Windowing;
/// <summary>
/// 窗体子类管理器
/// </summary>
/// <typeparam name="TWindow">窗体类型</typeparam>
[HighQuality]
internal sealed class WindowSubclass<TWindow> : IDisposable
where TWindow : Window, IWindowOptionsSource
internal sealed class WindowSubclass : IDisposable
{
private const int WindowSubclassId = 101;
private const int DragBarSubclassId = 102;
private readonly TWindow window;
private readonly Window window;
private readonly WindowOptions options;
private readonly IServiceProvider serviceProvider;
private readonly IHotKeyController hotKeyController;
// We have to explicitly hold a reference to SUBCLASSPROC
private SUBCLASSPROC? windowProc;
private SUBCLASSPROC? legacyDragBarProc;
/// <summary>
/// 构造一个新的窗体子类管理器
/// </summary>
/// <param name="window">窗口</param>
public WindowSubclass(TWindow window)
public WindowSubclass(Window window, in WindowOptions options, IServiceProvider serviceProvider)
{
this.window = window;
this.options = options;
this.serviceProvider = serviceProvider;
hotKeyController = new HotKeyController(serviceProvider);
}
/// <summary>
@@ -41,10 +43,9 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
/// <returns>是否设置成功</returns>
public bool Initialize()
{
WindowOptions options = window.WindowOptions;
windowProc = OnSubclassProcedure;
bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, 0);
hotKeyController.Register(options.Hwnd);
bool titleBarHooked = true;
@@ -71,18 +72,16 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
/// <inheritdoc/>
public void Dispose()
{
WindowOptions options = window.WindowOptions;
hotKeyController.Unregister(options.Hwnd);
RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId);
windowProc = null;
if (!options.UseLegacyDragBarImplementation)
if (options.UseLegacyDragBarImplementation)
{
return;
RemoveWindowSubclass(options.Hwnd, legacyDragBarProc, DragBarSubclassId);
legacyDragBarProc = null;
}
RemoveWindowSubclass(options.Hwnd, legacyDragBarProc, DragBarSubclassId);
legacyDragBarProc = null;
}
[SuppressMessage("", "SH002")]
@@ -92,16 +91,24 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
{
case WM_GETMINMAXINFO:
{
uint dpi = GetDpiForWindow(hwnd);
double scalingFactor = Math.Round(dpi / 96D, 2, MidpointRounding.AwayFromZero);
window.ProcessMinMaxInfo((MINMAXINFO*)lParam.Value, scalingFactor);
if (window is IMinMaxInfoHandler handler)
{
handler.HandleMinMaxInfo(ref *(MINMAXINFO*)lParam.Value, options.GetWindowScale());
}
break;
}
case WM_NCRBUTTONDOWN:
case WM_NCRBUTTONUP:
{
return (LRESULT)(nint)WM_NULL;
return default;
}
case WM_HOTKEY:
{
hotKeyController.OnHotKeyPressed(*(HotKeyParameter*)&lParam);
break;
}
}

View File

@@ -3,7 +3,7 @@
using Windows.Graphics;
namespace Snap.Hutao.Win32;
namespace Snap.Hutao.Extension;
/// <summary>
/// 结构扩展
@@ -62,4 +62,9 @@ internal static class StructExtension
{
return sizeInt32.Width * sizeInt32.Height;
}
public static double Size(this Windows.Foundation.Size size)
{
return size.Width * size.Height;
}
}

View File

@@ -23,7 +23,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
await taskContext.SwitchToMainThreadAsync();
ContentDialog dialog = new()
{
XamlRoot = currentWindowReference.Window.Content.XamlRoot,
XamlRoot = currentWindowReference.GetXamlRoot(),
Title = title,
Content = content,
DefaultButton = ContentDialogButton.Primary,
@@ -39,7 +39,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
await taskContext.SwitchToMainThreadAsync();
ContentDialog dialog = new()
{
XamlRoot = currentWindowReference.Window.Content.XamlRoot,
XamlRoot = currentWindowReference.GetXamlRoot(),
Title = title,
Content = content,
DefaultButton = defaultButton,
@@ -56,7 +56,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
await taskContext.SwitchToMainThreadAsync();
ContentDialog dialog = new()
{
XamlRoot = currentWindowReference.Window.Content.XamlRoot,
XamlRoot = currentWindowReference.GetXamlRoot(),
Title = title,
Content = new ProgressBar() { IsIndeterminate = true },
};
@@ -68,12 +68,16 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
where TContentDialog : ContentDialog
{
await taskContext.SwitchToMainThreadAsync();
return serviceProvider.CreateInstance<TContentDialog>(parameters);
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
return contentDialog;
}
public TContentDialog CreateInstance<TContentDialog>(params object[] parameters)
where TContentDialog : ContentDialog
{
return serviceProvider.CreateInstance<TContentDialog>(parameters);
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
return contentDialog;
}
}

View File

@@ -2,6 +2,8 @@
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Core.Windowing;
using Snap.Hutao.Factory.Abstraction;
using Windows.Storage.Pickers;
using WinRT.Interop;
@@ -16,7 +18,7 @@ internal sealed partial class PickerFactory : IPickerFactory
{
private const string AnyType = "*";
private readonly MainWindow mainWindow;
private readonly ICurrentWindowReference currentWindow;
/// <inheritdoc/>
public FileOpenPicker GetFileOpenPicker(PickerLocationId location, string commitButton, params string[] fileTypes)
@@ -78,7 +80,10 @@ internal sealed partial class PickerFactory : IPickerFactory
{
// Create a folder picker.
T picker = new();
InitializeWithWindow.Initialize(picker, mainWindow.WindowOptions.Hwnd);
if (currentWindow.Window is IWindowOptionsSource optionsSource)
{
InitializeWithWindow.Initialize(picker, optionsSource.WindowOptions.Hwnd);
}
return picker;
}

View File

@@ -11,10 +11,10 @@ namespace Snap.Hutao;
/// 指引窗口
/// </summary>
[Injection(InjectAs.Singleton)]
internal sealed partial class GuideWindow : Window, IWindowOptionsSource
internal sealed partial class GuideWindow : Window, IWindowOptionsSource, IMinMaxInfoHandler
{
private const int MinWidth = 800;
private const int MinHeight = 450;
private const int MinWidth = 1000;
private const int MinHeight = 600;
private const int MaxWidth = 1200;
private const int MaxHeight = 750;
@@ -25,16 +25,16 @@ internal sealed partial class GuideWindow : Window, IWindowOptionsSource
{
InitializeComponent();
windowOptions = new(this, DragableGrid, new(MinWidth, MinHeight));
ExtendedWindow<GuideWindow>.Initialize(this, Ioc.Default);
this.InitializeController(serviceProvider);
}
WindowOptions IWindowOptionsSource.WindowOptions { get => windowOptions; }
public unsafe void ProcessMinMaxInfo(MINMAXINFO* pInfo, double scalingFactor)
public unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor)
{
pInfo->ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo->ptMinTrackSize.X);
pInfo->ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo->ptMinTrackSize.Y);
pInfo->ptMaxTrackSize.X = (int)Math.Min(MaxWidth * scalingFactor, pInfo->ptMaxTrackSize.X);
pInfo->ptMaxTrackSize.Y = (int)Math.Min(MaxHeight * scalingFactor, pInfo->ptMaxTrackSize.Y);
info.ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, info.ptMinTrackSize.X);
info.ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, info.ptMinTrackSize.Y);
info.ptMaxTrackSize.X = (int)Math.Min(MaxWidth * scalingFactor, info.ptMaxTrackSize.X);
info.ptMaxTrackSize.Y = (int)Math.Min(MaxHeight * scalingFactor, info.ptMaxTrackSize.Y);
}
}

View File

@@ -13,7 +13,7 @@ namespace Snap.Hutao;
/// </summary>
[HighQuality]
[Injection(InjectAs.Singleton)]
internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOptionsSource
internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOptionsSource, IMinMaxInfoHandler
{
private const int MinWidth = 240;
private const int MinHeight = 240;
@@ -34,7 +34,7 @@ internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOpt
scope = serviceProvider.CreateScope();
windowOptions = new(this, DragableGrid, new(MaxWidth, MaxHeight));
ExtendedWindow<LaunchGameWindow>.Initialize(this, scope.ServiceProvider);
this.InitializeController(serviceProvider);
RootGrid.DataContext = scope.ServiceProvider.GetRequiredService<LaunchGameViewModel>();
}
@@ -48,11 +48,11 @@ internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOpt
}
/// <inheritdoc/>
public unsafe void ProcessMinMaxInfo(MINMAXINFO* pInfo, double scalingFactor)
public unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor)
{
pInfo->ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo->ptMinTrackSize.X);
pInfo->ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo->ptMinTrackSize.Y);
pInfo->ptMaxTrackSize.X = (int)Math.Min(MaxWidth * scalingFactor, pInfo->ptMaxTrackSize.X);
pInfo->ptMaxTrackSize.Y = (int)Math.Min(MaxHeight * scalingFactor, pInfo->ptMaxTrackSize.Y);
info.ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, info.ptMinTrackSize.X);
info.ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, info.ptMinTrackSize.Y);
info.ptMaxTrackSize.X = (int)Math.Min(MaxWidth * scalingFactor, info.ptMaxTrackSize.X);
info.ptMaxTrackSize.Y = (int)Math.Min(MaxHeight * scalingFactor, info.ptMaxTrackSize.Y);
}
}

View File

@@ -14,7 +14,7 @@ namespace Snap.Hutao;
[HighQuality]
[Injection(InjectAs.Singleton)]
[SuppressMessage("", "CA1001")]
internal sealed partial class MainWindow : Window, IWindowOptionsSource
internal sealed partial class MainWindow : Window, IWindowOptionsSource, IMinMaxInfoHandler
{
private const int MinWidth = 848;
private const int MinHeight = 524;
@@ -22,6 +22,7 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource
private readonly WindowOptions windowOptions;
private readonly ILogger<MainWindow> logger;
private readonly TypedEventHandler<object, WindowEventArgs> closedEventHander;
private readonly TypedEventHandler<object, WindowSizeChangedEventArgs> sizeChangedEventHandler;
/// <summary>
/// 构造一个新的主窗体
@@ -31,25 +32,32 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource
{
InitializeComponent();
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true);
ExtendedWindow<MainWindow>.Initialize(this, serviceProvider);
this.InitializeController(serviceProvider);
logger = serviceProvider.GetRequiredService<ILogger<MainWindow>>();
closedEventHander = OnClosed;
sizeChangedEventHandler = OnSizeChanged;
Closed += closedEventHander;
SizeChanged += sizeChangedEventHandler;
}
/// <inheritdoc/>
public WindowOptions WindowOptions { get => windowOptions; }
/// <inheritdoc/>
public unsafe void ProcessMinMaxInfo(MINMAXINFO* pInfo, double scalingFactor)
public unsafe void HandleMinMaxInfo(ref MINMAXINFO pInfo, double scalingFactor)
{
pInfo->ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo->ptMinTrackSize.X);
pInfo->ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo->ptMinTrackSize.Y);
pInfo.ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo.ptMinTrackSize.X);
pInfo.ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo.ptMinTrackSize.Y);
}
private void OnClosed(object sender, WindowEventArgs args)
{
logger.LogInformation("MainWindow Closed");
}
private void OnSizeChanged(object sender, WindowSizeChangedEventArgs args)
{
}
}

View File

@@ -1,29 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Message;
/// <summary>
/// Flyout开启关闭消息
/// </summary>
[HighQuality]
internal sealed class FlyoutStateChangedMessage
{
/// <summary>
/// 构造一个新的Flyout开启关闭消息
/// </summary>
/// <param name="isOpen">是否为开启状态</param>
public FlyoutStateChangedMessage(bool isOpen)
{
IsOpen = isOpen;
}
public static FlyoutStateChangedMessage Open { get; } = new(true);
public static FlyoutStateChangedMessage Close { get; } = new(false);
/// <summary>
/// 是否为开启状态
/// </summary>
public bool IsOpen { get; }
}

View File

@@ -1,6 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.InteropServices;
namespace Snap.Hutao.Model.InterChange.GachaLog;
/// <summary>
@@ -18,6 +20,7 @@ internal sealed class UIGF
/// <summary>
/// 信息
/// </summary>
[JsonRequired]
[JsonPropertyName("info")]
public UIGFInfo Info { get; set; } = default!;
@@ -48,20 +51,35 @@ internal sealed class UIGF
/// <summary>
/// 列表物品是否正常
/// </summary>
/// <param name="itemId">首个出错的Id</param>
/// <param name="id">首个出错的Id</param>
/// <returns>是否正常</returns>
public bool IsMajor2Minor2OrLowerListValid([NotNullWhen(false)] out long itemId)
public bool IsMajor2Minor2OrLowerListValid([NotNullWhen(false)] out long id)
{
foreach (UIGFItem item in List)
foreach (ref readonly UIGFItem item in CollectionsMarshal.AsSpan(List))
{
if (item.ItemType != SH.ModelInterchangeUIGFItemTypeAvatar && item.ItemType != SH.ModelInterchangeUIGFItemTypeWeapon)
{
itemId = item.Id;
id = item.Id;
return false;
}
}
itemId = 0;
id = 0;
return true;
}
public bool IsMajor2Minor3OrHigherListValid([NotNullWhen(false)] out long id)
{
foreach (ref readonly UIGFItem item in CollectionsMarshal.AsSpan(List))
{
if (string.IsNullOrEmpty(item.ItemId))
{
id = item.Id;
return false;
}
}
id = 0;
return true;
}
}

View File

@@ -7,7 +7,7 @@ namespace Snap.Hutao.Model.Intrinsic;
/// 成就信息状态
/// </summary>
[HighQuality]
internal enum AchievementStatus /*: int*/
internal enum AchievementStatus
{
/// <summary>
/// 未识别

View File

@@ -57,4 +57,6 @@ internal enum MaterialType
MATERIAL_DESHRET_MANUAL = 46,
MATERIAL_RENAME_ITEM = 47,
MATERIAL_GCG_EXCHANGE_ITEM = 48,
MATERIAL_QUEST_EVENT_BOOK = 49,
MATERIAL_PROFILE_PICTURE = 50,
}

View File

@@ -91,6 +91,8 @@ internal static class AvatarIds
public static readonly AvatarId Freminet = 10000085;
public static readonly AvatarId Wriothesley = 10000086;
public static readonly AvatarId Neuvillette = 10000087;
public static readonly AvatarId Charlotte = 10000088;
public static readonly AvatarId Furina = 10000089;
/// <summary>
/// 检查该角色是否为主角

View File

@@ -3,8 +3,8 @@
using Microsoft.UI;
using Snap.Hutao.Control;
using Snap.Hutao.Core;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Win32;
using Windows.UI;
namespace Snap.Hutao.Model.Metadata.Converter;

View File

@@ -17,6 +17,8 @@ internal static class MonsterRelationship
5041U => 504U, // 幻形蟹 · 水 (强化)
5070U => 507U, // 幻形花鼠 · 水
5071U => 507U, // 幻形花鼠 · 水 (强化)
5102U => 510U, // 历经百战的浊水粉碎幻灵
5112U => 511U, // 历经百战的浊水喷吐幻灵
60402U => 60401U, // (火)岩龙蜥
60403U => 60401U, // (冰)岩龙蜥
60404U => 60401U, // (雷)岩龙蜥

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