mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 10:54:55 +08:00
Compare commits
299 Commits
v1.1.165.0
...
48bdffb2fe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48bdffb2fe | ||
|
|
d324a7552f | ||
|
|
78c308c393 | ||
|
|
a1bba00db6 | ||
|
|
0787f5b111 | ||
|
|
884026594b | ||
|
|
b0a777db8d | ||
|
|
83f3a3bd6d | ||
|
|
bfb2ed29e5 | ||
|
|
131a0f0eb1 | ||
|
|
ac2a7b3f7b | ||
|
|
36eea7f8f2 | ||
|
|
6b338deb55 | ||
|
|
af323ecd00 | ||
|
|
c79d01c75b | ||
|
|
b51ec1e60f | ||
|
|
7fe925bcba | ||
|
|
0626472d66 | ||
|
|
33099bc186 | ||
|
|
e653efc227 | ||
|
|
074fef3faf | ||
|
|
029cbbd343 | ||
|
|
802b2a4c1c | ||
|
|
eccc4d519c | ||
|
|
5f274ea28a | ||
|
|
aa1a1f5d58 | ||
|
|
3a56d53487 | ||
|
|
bbc5eb772c | ||
|
|
05b491052b | ||
|
|
8accbf0431 | ||
|
|
1174209c2a | ||
|
|
23ed719046 | ||
|
|
a34f00662e | ||
|
|
f783314258 | ||
|
|
215a39c5d5 | ||
|
|
16bcef5f64 | ||
|
|
fbba9a3c36 | ||
|
|
f205ab0364 | ||
|
|
10314f3c2f | ||
|
|
b4710e87d3 | ||
|
|
282a934cd2 | ||
|
|
b4c4e394ef | ||
|
|
17cfdf37bd | ||
|
|
900a8e1e7c | ||
|
|
ea9a9c2f5f | ||
|
|
0c4d02b337 | ||
|
|
d137d82ecf | ||
|
|
02551e2053 | ||
|
|
026926e9b8 | ||
|
|
4c811db16a | ||
|
|
6f83fa11db | ||
|
|
bc8e15c144 | ||
|
|
85de1eb2cd | ||
|
|
d2bf19ed3d | ||
|
|
43c205c839 | ||
|
|
9664b1ab78 | ||
|
|
08c5f6b515 | ||
|
|
260de40f81 | ||
|
|
c00d0eb005 | ||
|
|
32e761724c | ||
|
|
9fd08af582 | ||
|
|
266dcfc930 | ||
|
|
8764585f2c | ||
|
|
91ab3a48c0 | ||
|
|
80fa34d9e8 | ||
|
|
b4ca4fd990 | ||
|
|
86527f6b82 | ||
|
|
d8066bc683 | ||
|
|
b261a86791 | ||
|
|
34f2a51b74 | ||
|
|
b1e9c25e01 | ||
|
|
346de93c3f | ||
|
|
6f48cbcd16 | ||
|
|
85b3121479 | ||
|
|
94f00d1a31 | ||
|
|
be9e4bba0f | ||
|
|
2454927582 | ||
|
|
aca5f8e00d | ||
|
|
09709e8e62 | ||
|
|
98fd8b43c4 | ||
|
|
3051180eb9 | ||
|
|
d48c81cfa1 | ||
|
|
695147be9b | ||
|
|
e782944a44 | ||
|
|
01462d42ce | ||
|
|
65b7dfcc44 | ||
|
|
ec3146d4a7 | ||
|
|
9ec0bf0b1a | ||
|
|
47e4b93613 | ||
|
|
192ad4a503 | ||
|
|
091e33ae08 | ||
|
|
3b010ed674 | ||
|
|
a9f685d51b | ||
|
|
c6c31f8839 | ||
|
|
78c53760cc | ||
|
|
0bb6b5a204 | ||
|
|
dff36a5e4d | ||
|
|
0188e443db | ||
|
|
5a9cdedc0c | ||
|
|
31460fcc6d | ||
|
|
c12fc6f381 | ||
|
|
e5e0342994 | ||
|
|
061958f20c | ||
|
|
95c73d0a34 | ||
|
|
026a12ac87 | ||
|
|
da53f2166f | ||
|
|
717277e17c | ||
|
|
1dc3ea57e9 | ||
|
|
4ec2ba8b59 | ||
|
|
91d9f253f0 | ||
|
|
90cf373e50 | ||
|
|
cf2778da7a | ||
|
|
45ff7d7aa8 | ||
|
|
eb37cb1b55 | ||
|
|
45aa1d787d | ||
|
|
0b28419ab5 | ||
|
|
258bf9220e | ||
|
|
9ece9f3edc | ||
|
|
40c1f0a5ce | ||
|
|
5f75e6c63c | ||
|
|
43387ce4c8 | ||
|
|
34eda9a262 | ||
|
|
804673696f | ||
|
|
b69e3bb24b | ||
|
|
c028aa8e46 | ||
|
|
fe3e257215 | ||
|
|
eae2428d85 | ||
|
|
b078365136 | ||
|
|
1ede8dbef4 | ||
|
|
a66051b937 | ||
|
|
1eca21c285 | ||
|
|
2254a28e40 | ||
|
|
812eca369d | ||
|
|
132d3d8ac8 | ||
|
|
641a23621f | ||
|
|
6802d10142 | ||
|
|
36f43e6d54 | ||
|
|
e8298ec7bd | ||
|
|
99a21cb935 | ||
|
|
b6da7bea5d | ||
|
|
cf5bf75346 | ||
|
|
7497d7014d | ||
|
|
dd8c62ffa5 | ||
|
|
15b147ba06 | ||
|
|
85146ffc95 | ||
|
|
e9dce765e4 | ||
|
|
3b2c4477b5 | ||
|
|
9d71c4aecf | ||
|
|
7184c148c4 | ||
|
|
85f928ce3b | ||
|
|
7c5032b0c2 | ||
|
|
2c3bd056b7 | ||
|
|
9f2843b7a0 | ||
|
|
7fb6d5346e | ||
|
|
27125d9051 | ||
|
|
5b2fb8b345 | ||
|
|
d558811cb4 | ||
|
|
6e30aa7ebd | ||
|
|
15fc337944 | ||
|
|
b7ef159b9e | ||
|
|
393b33ed83 | ||
|
|
23dfda4413 | ||
|
|
fde7340f4d | ||
|
|
22330d7fe9 | ||
|
|
c64e5776e8 | ||
|
|
ffa2cd75a0 | ||
|
|
873e75a7e9 | ||
|
|
ffa4101d5f | ||
|
|
1c12b582c2 | ||
|
|
c50d31ced7 | ||
|
|
f8108151b6 | ||
|
|
2932366767 | ||
|
|
cbf643ca70 | ||
|
|
a72d0f5c28 | ||
|
|
3b4d98f9a3 | ||
|
|
d5828101d8 | ||
|
|
56051537ea | ||
|
|
6b465a09b1 | ||
|
|
450b86ebaf | ||
|
|
c0078baa13 | ||
|
|
6b28212ec3 | ||
|
|
9a3c2f5f70 | ||
|
|
31be2bd8f7 | ||
|
|
47056e07a1 | ||
|
|
f30673b9d3 | ||
|
|
d8624c49d0 | ||
|
|
72810e7440 | ||
|
|
e881d36743 | ||
|
|
aa3e79d3ff | ||
|
|
9979474ce1 | ||
|
|
2e7cd93cfe | ||
|
|
bdc31c3e0d | ||
|
|
631d079aa2 | ||
|
|
f76ef87167 | ||
|
|
76aa5ee8d0 | ||
|
|
d7f4978a66 | ||
|
|
0905c46e45 | ||
|
|
d0991c5ddb | ||
|
|
619a3ba196 | ||
|
|
13526bb85c | ||
|
|
61f4f608db | ||
|
|
f690da8501 | ||
|
|
145c13a0e6 | ||
|
|
cea4fbb54d | ||
|
|
1d489c68e9 | ||
|
|
90e7fa42d0 | ||
|
|
29a6879e45 | ||
|
|
58499a2d09 | ||
|
|
580255699b | ||
|
|
9cac7818f1 | ||
|
|
118668a457 | ||
|
|
37621dbf2a | ||
|
|
aa7d56f1cb | ||
|
|
8dbe76e790 | ||
|
|
de6410492e | ||
|
|
26df7c7f67 | ||
|
|
3c411374bd | ||
|
|
99f0b9443b | ||
|
|
a3bc148816 | ||
|
|
cea757702b | ||
|
|
8938a5c798 | ||
|
|
46f4589b64 | ||
|
|
adb02658f4 | ||
|
|
3d7e6061e9 | ||
|
|
a51220c7b9 | ||
|
|
22b813e687 | ||
|
|
fda94d5020 | ||
|
|
205cbe8fb6 | ||
|
|
816f7064db | ||
|
|
132c5267b0 | ||
|
|
4e866818df | ||
|
|
9b7b56a0ee | ||
|
|
66f2da0e4c | ||
|
|
1735c6a7e6 | ||
|
|
8c06c98068 | ||
|
|
e2ac4c166c | ||
|
|
728397cafa | ||
|
|
059787a28f | ||
|
|
4c4231b48c | ||
|
|
2412927b29 | ||
|
|
f3bdbba83e | ||
|
|
4c811b12ca | ||
|
|
933103c57f | ||
|
|
718e7bdad3 | ||
|
|
42284b4f45 | ||
|
|
7da8af7c2a | ||
|
|
a4fc457065 | ||
|
|
d3c2ee592c | ||
|
|
2eef88523c | ||
|
|
220b1063ac | ||
|
|
28bcd8ddfc | ||
|
|
5750bd3ad7 | ||
|
|
1667c701b0 | ||
|
|
160398f7ab | ||
|
|
c25ddf770f | ||
|
|
44fa3312b2 | ||
|
|
81f3d1f6bf | ||
|
|
2f627d7531 | ||
|
|
caed76e9b9 | ||
|
|
a74dc705c9 | ||
|
|
21eb8fcf9c | ||
|
|
4662d3d54d | ||
|
|
b10f108f93 | ||
|
|
2defe620c7 | ||
|
|
315c10c83c | ||
|
|
981bc3f933 | ||
|
|
88460899bd | ||
|
|
8341642658 | ||
|
|
26f4ff3a58 | ||
|
|
b1b763c6fe | ||
|
|
28323c39f6 | ||
|
|
b099965715 | ||
|
|
8f9fdc18bb | ||
|
|
5a549bd5f5 | ||
|
|
48e94f275b | ||
|
|
691071d725 | ||
|
|
591c1a6d00 | ||
|
|
4e6a2df2cb | ||
|
|
8b196e45a4 | ||
|
|
0ddeaef126 | ||
|
|
e3747c113a | ||
|
|
1b1449ce3b | ||
|
|
ea246a96be | ||
|
|
28722d325a | ||
|
|
7238713ff5 | ||
|
|
66c42f81f2 | ||
|
|
3e6ba725d2 | ||
|
|
ffb0c58a58 | ||
|
|
3b61f568d0 | ||
|
|
b925a10d69 | ||
|
|
720f4b311e | ||
|
|
6c03002051 | ||
|
|
9cebd56bd5 | ||
|
|
f0a4c1251d | ||
|
|
a8418d4234 | ||
|
|
53abc4526c | ||
|
|
4d3b982904 | ||
|
|
5faace562d | ||
|
|
290b7f38b4 |
291
.devin/wiki.json
Normal file
291
.devin/wiki.json
Normal file
@@ -0,0 +1,291 @@
|
||||
{
|
||||
"repo_notes": [
|
||||
{
|
||||
"content": "Always use the latest files in the repo to generate the wiki"
|
||||
}
|
||||
],
|
||||
"pages": [
|
||||
{
|
||||
"title": "Overview",
|
||||
"purpose": "Introduce BetterLyrics, its purpose as a WinUI3 lyrics display application, key features, and supported music players",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Getting Started",
|
||||
"purpose": "Guide users through installation, initial setup, and basic usage of BetterLyrics",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Installation and Deployment",
|
||||
"purpose": "Explain how to install BetterLyrics from Microsoft Store or build from source, system requirements, and supported Windows versions",
|
||||
"parent": "Getting Started",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Initial Configuration",
|
||||
"purpose": "Walk through first-time setup including media player configuration, folder selection, and basic settings",
|
||||
"parent": "Getting Started",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Architecture",
|
||||
"purpose": "Provide technical overview of BetterLyrics' internal architecture, design patterns, and component organization",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Application Entry Point and Dependency Injection",
|
||||
"purpose": "Document the App.xaml.cs entry point, service registration, and dependency injection container configuration",
|
||||
"parent": "Architecture",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Service Layer Architecture",
|
||||
"purpose": "Explain the service-oriented architecture, service interfaces, and their implementations including MediaSessionsService, LyricsSearchService, and SettingsService",
|
||||
"parent": "Architecture",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Data Models",
|
||||
"purpose": "Document core data models including LyricsLine, LyricsData, SongInfo, and configuration models",
|
||||
"parent": "Architecture",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Multi-Window System",
|
||||
"purpose": "Explain how BetterLyrics manages multiple simultaneous lyrics windows, window lifecycle, and state management",
|
||||
"parent": "Architecture",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "User Interface",
|
||||
"purpose": "Document the UI components, windows, and user interaction patterns in BetterLyrics",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Now Playing Window",
|
||||
"purpose": "Detail the main lyrics display window, its components, and integration with the rendering system",
|
||||
"parent": "User Interface",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Display Modes and Window Configurations",
|
||||
"purpose": "Explain different display modes (Standard, Desktop, Docked, Fullscreen, Narrow, Taskbar) and how to configure them",
|
||||
"parent": "User Interface",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Settings and Customization",
|
||||
"purpose": "Document the settings interface, configuration options, and how to customize lyrics appearance and behavior",
|
||||
"parent": "User Interface",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Music Gallery",
|
||||
"purpose": "Explain the local music library management feature, including playback, playlist management, and integration with media controls",
|
||||
"parent": "User Interface",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "System Tray and Global Controls",
|
||||
"purpose": "Document the system tray integration, global hotkeys, and application-wide controls",
|
||||
"parent": "User Interface",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Lyrics System",
|
||||
"purpose": "Comprehensive documentation of the lyrics acquisition, processing, and display pipeline",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Lyrics Search and Providers",
|
||||
"purpose": "Document the lyrics search system, supported providers (QQ Music, Netease, Kugou, LrcLib, Apple Music, local files), and search strategies",
|
||||
"parent": "Lyrics System",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Parsing and Translation",
|
||||
"purpose": "Explain how lyrics are parsed from different formats (LRC, QRC, TTML), translation system using LibreTranslate, and metadata matching",
|
||||
"parent": "Lyrics System",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Rendering Pipeline",
|
||||
"purpose": "Document the Win2D-based rendering system, LyricsCanvas, PlayingLineRenderer, UnplayingLineRenderer, and LyricsLayoutManager",
|
||||
"parent": "Lyrics System",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Visual Effects and Animation",
|
||||
"purpose": "Explain character-level effects (glow, float, scale), background effects (fluid, snow, fog, spectrum), and the animation transition system",
|
||||
"parent": "Lyrics System",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Media Integration",
|
||||
"purpose": "Document how BetterLyrics integrates with music players and manages media sessions",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Windows Media Transport Controls Integration",
|
||||
"purpose": "Explain how BetterLyrics uses Windows SMTC to work universally with media players",
|
||||
"parent": "Media Integration",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Supported Players and Special Configurations",
|
||||
"purpose": "List supported media players, document special configurations (Apple Music token, LX Music SSE), and player-specific handling",
|
||||
"parent": "Media Integration",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Album Art and Theme Colors",
|
||||
"purpose": "Document album art retrieval from multiple sources, color palette generation (MedianCut, OctTree), and adaptive theming",
|
||||
"parent": "Media Integration",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Internationalization",
|
||||
"purpose": "Explain the localization system, supported languages, and how resources are managed",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Development",
|
||||
"purpose": "Technical documentation for developers contributing to or extending BetterLyrics",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Build Configuration and Deployment",
|
||||
"purpose": "Document the build process, publish profiles for different architectures, CI/CD pipeline, and packaging for Microsoft Store",
|
||||
"parent": "Development",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Helper Utilities and Extensions",
|
||||
"purpose": "Document utility classes including TaskbarHook, WindowHook, ColorHelper, ImageHelper, and various extension methods",
|
||||
"parent": "Development",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "External Dependencies and Libraries",
|
||||
"purpose": "List and explain third-party dependencies including Win2D, NAudio, ATL.NET, FlaUI, and their usage in the application",
|
||||
"parent": "Development",
|
||||
"page_notes": [
|
||||
{
|
||||
"content": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -53,27 +53,27 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<DefaultLanguage>en</DefaultLanguage>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<DefaultLanguage>en</DefaultLanguage>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<DefaultLanguage>en</DefaultLanguage>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<DefaultLanguage>en</DefaultLanguage>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<DefaultLanguage>en</DefaultLanguage>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<DefaultLanguage>en</DefaultLanguage>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.1.165.0" />
|
||||
Version="1.1.221.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
@@ -28,11 +28,22 @@
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="en-US"/>
|
||||
<Resource Language="zh-CN"/>
|
||||
<Resource Language="zh-TW"/>
|
||||
<Resource Language="ja-JP"/>
|
||||
<Resource Language="ko-KR"/>
|
||||
<Resource Language="ar"/>
|
||||
<Resource Language="de"/>
|
||||
<Resource Language="en"/>
|
||||
<Resource Language="es"/>
|
||||
<Resource Language="fr"/>
|
||||
<Resource Language="hi"/>
|
||||
<Resource Language="id"/>
|
||||
<Resource Language="ja"/>
|
||||
<Resource Language="ko"/>
|
||||
<Resource Language="ms"/>
|
||||
<Resource Language="pt"/>
|
||||
<Resource Language="ru"/>
|
||||
<Resource Language="th"/>
|
||||
<Resource Language="vi"/>
|
||||
<Resource Language="zh-Hans"/>
|
||||
<Resource Language="zh-Hant"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
<converter:CornerRadiusToDoubleConverter x:Key="CornerRadiusToDoubleConverter" />
|
||||
<converter:LyricsSearchProviderToDisplayNameConverter x:Key="LyricsSearchProviderToDisplayNameConverter" />
|
||||
<converter:TranslationSearchProviderToDisplayNameConverter x:Key="TranslationSearchProviderToDisplayNameConverter" />
|
||||
<converter:TransliterationSearchProviderToDisplayNameConverter x:Key="TransliterationSearchProviderToDisplayNameConverter" />
|
||||
<converter:AlbumArtSearchProviderToDisplayNameConverter x:Key="AlbumArtSearchProviderToDisplayNameConverter" />
|
||||
<converter:SecondsToFormattedTimeConverter x:Key="SecondsToFormattedTimeConverter" />
|
||||
<converter:MillisecondsToFormattedTimeConverter x:Key="MillisecondsToFormattedTimeConverter" />
|
||||
@@ -64,7 +65,6 @@
|
||||
<converter:ByteArrayToImageConverter x:Key="ByteArrayToImageConverter" />
|
||||
<converter:DisplayLanguageCodeToIndexConverter x:Key="DisplayLanguageCodeToIndexConverter" />
|
||||
<converter:PathToParentFolderConverter x:Key="PathToParentFolderConverter" />
|
||||
<converter:TrackToLyricsConverter x:Key="TrackToLyricsConverter" />
|
||||
<converter:IntToBoolConverter x:Key="IntToBoolConverter" />
|
||||
<converter:IndexToDisplayConverter x:Key="IndexToDisplayConverter" />
|
||||
<converter:IntToDoubleConverter x:Key="IntToDoubleConverter" />
|
||||
@@ -74,6 +74,8 @@
|
||||
<converter:TextAlignmentTypeToHorizontalAlignmentConverter x:Key="TextAlignmentTypeToHorizontalAlignmentConverter" />
|
||||
<converter:LyricsLayoutOrientationToOrientationConverter x:Key="LyricsLayoutOrientationToOrientationConverter" />
|
||||
<converter:LyricsLayoutOrientationNegationToOrientationConverter x:Key="LyricsLayoutOrientationNegationToOrientationConverter" />
|
||||
<converter:FileSourceTypeToIconConverter x:Key="FileSourceTypeToIconConverter" />
|
||||
<converter:PathToImageConverter x:Key="PathToImageConverter" />
|
||||
|
||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||
@@ -95,8 +97,7 @@
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="16,9,16,11" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Padding" Value="16,9,16,9" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style x:Key="GhostButtonStyle" TargetType="Button">
|
||||
@@ -110,10 +111,10 @@
|
||||
x:Key="TitleBarToggleButtonStyle"
|
||||
BasedOn="{StaticResource ToggleButtonRevealStyle}"
|
||||
TargetType="ToggleButton">
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="16,9,16,11" />
|
||||
<Setter Property="Padding" Value="14,6,14,9" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style x:Key="GhostToggleButtonStyle" TargetType="ToggleButton">
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
|
||||
using BetterLyrics.WinUI3.Services.DiscordService;
|
||||
using BetterLyrics.WinUI3.Services.FileSystemService;
|
||||
using BetterLyrics.WinUI3.Services.LastFMService;
|
||||
using BetterLyrics.WinUI3.Services.LibWatcherService;
|
||||
using BetterLyrics.WinUI3.Services.LiveStatesService;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
||||
using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.Services.TranslateService;
|
||||
using BetterLyrics.WinUI3.Services.TranslationService;
|
||||
using BetterLyrics.WinUI3.Services.TransliterationService;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
@@ -19,8 +20,11 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
using Microsoft.Windows.Globalization;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -68,8 +72,29 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
|
||||
if (Ioc.Default.GetRequiredService<ISettingsService>().AppSettings.MusicGallerySettings.AutoOpen)
|
||||
var settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
var fileSystemService = Ioc.Default.GetRequiredService<IFileSystemService>();
|
||||
fileSystemService.StartAllFolderTimers();
|
||||
|
||||
WindowHook.OpenOrShowWindow<SystemTrayWindow>();
|
||||
|
||||
if (settingsService.AppSettings.GeneralSettings.AutoStartLyricsWindow)
|
||||
{
|
||||
var defaultStatus = settingsService.AppSettings.WindowBoundsRecords.Where(x => x.IsDefault);
|
||||
if (defaultStatus != null)
|
||||
{
|
||||
foreach (var item in defaultStatus)
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>(item);
|
||||
if (!settingsService.AppSettings.GeneralSettings.MultiNowPlayingWindowMode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (settingsService.AppSettings.MusicGallerySettings.AutoOpen)
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
|
||||
}
|
||||
@@ -91,16 +116,16 @@ namespace BetterLyrics.WinUI3
|
||||
loggingBuilder.AddSerilog();
|
||||
})
|
||||
// Services
|
||||
.AddSingleton<ILiveStatesService, LiveStatesService>()
|
||||
.AddSingleton<ISettingsService, SettingsService>()
|
||||
.AddSingleton<IMediaSessionsService, MediaSessionsService>()
|
||||
.AddSingleton<IAlbumArtSearchService, AlbumArtSearchService>()
|
||||
.AddSingleton<ILyricsSearchService, LyricsSearchService>()
|
||||
.AddSingleton<ILibWatcherService, LibWatcherService>()
|
||||
.AddSingleton<ITranslateService, TranslateService>()
|
||||
.AddSingleton<ITranslationService, TranslationService>()
|
||||
.AddSingleton<ITransliterationService, TransliterationService>()
|
||||
.AddSingleton<ILastFMService, LastFMService>()
|
||||
.AddSingleton<IResourceService, ResourceService>()
|
||||
.AddSingleton<IDiscordService, DiscordService>()
|
||||
.AddSingleton<ILocalizationService, LocalizationService>()
|
||||
.AddSingleton<IFileSystemService, FileSystemService>()
|
||||
// ViewModels
|
||||
.AddSingleton<AppSettingsControlViewModel>()
|
||||
.AddSingleton<PlaybackSettingsControlViewModel>()
|
||||
@@ -109,13 +134,17 @@ namespace BetterLyrics.WinUI3
|
||||
.AddSingleton<LyricsWindowSettingsControlViewModel>()
|
||||
.AddSingleton<LyricsWindowSwitchControlViewModel>()
|
||||
.AddSingleton<LyricsWindowSwitchWindowViewModel>()
|
||||
.AddSingleton<NowPlayingWindowViewModel>()
|
||||
.AddSingleton<SettingsWindowViewModel>()
|
||||
.AddSingleton<SystemTrayViewModel>()
|
||||
.AddSingleton<SettingsPageViewModel>()
|
||||
.AddSingleton<NowPlayingPageViewModel>()
|
||||
.AddSingleton<MusicGalleryViewModel>()
|
||||
.AddSingleton<MusicGalleryPageViewModel>()
|
||||
.AddSingleton<AboutControlViewModel>()
|
||||
.AddSingleton<MusicGalleryWindowViewModel>()
|
||||
|
||||
.AddTransient<NowPlayingWindowViewModel>()
|
||||
.AddTransient<NowPlayingPageViewModel>()
|
||||
.AddTransient<NowPlayingBarViewModel>()
|
||||
|
||||
.BuildServiceProvider()
|
||||
);
|
||||
}
|
||||
|
||||
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Folder.png
Normal file
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Folder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -10,38 +10,50 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<AppxDefaultResourceQualifiers>Language=ar;de;en;es;fr;hi;id;ja;ko;ms;pt;ru;th;vi;zh-Hans;zh-Hant;</AppxDefaultResourceQualifiers>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="TemplateSelector\**" />
|
||||
<Compile Remove="ViewModels\Lyrics\**" />
|
||||
<Content Remove="TemplateSelector\**" />
|
||||
<Content Remove="ViewModels\Lyrics\**" />
|
||||
<EmbeddedResource Remove="TemplateSelector\**" />
|
||||
<EmbeddedResource Remove="ViewModels\Lyrics\**" />
|
||||
<None Remove="TemplateSelector\**" />
|
||||
<None Remove="ViewModels\Lyrics\**" />
|
||||
<Page Remove="TemplateSelector\**" />
|
||||
<Page Remove="ViewModels\Lyrics\**" />
|
||||
<PRIResource Remove="TemplateSelector\**" />
|
||||
<PRIResource Remove="ViewModels\Lyrics\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Segoe Fluent Icons.ttf" />
|
||||
<None Remove="Assets\Wiki82.profile.xml" />
|
||||
<None Remove="Controls\AboutControl.xaml" />
|
||||
<None Remove="Controls\AlbumArtLayoutSettingsControl.xaml" />
|
||||
<None Remove="Controls\AlbumArtAreaEffectSettingsControl.xaml" />
|
||||
<None Remove="Controls\AppSettingsControl.xaml" />
|
||||
<None Remove="Controls\DemoWindowGrid.xaml" />
|
||||
<None Remove="Controls\ExtendedSlider.xaml" />
|
||||
<None Remove="Controls\FontFamilyAutoSuggestBox.xaml" />
|
||||
<None Remove="Controls\ImageSwitcher.xaml" />
|
||||
<None Remove="Controls\LyricsSearchControl.xaml" />
|
||||
<None Remove="Controls\LyricsStyleSettingsControl.xaml" />
|
||||
<None Remove="Controls\LyricsWindowSettingsControl.xaml" />
|
||||
<None Remove="Controls\LyricsWindowSwitchControl.xaml" />
|
||||
<None Remove="Controls\MediaSettingsControl.xaml" />
|
||||
<None Remove="Controls\NowPlayingBar.xaml" />
|
||||
<None Remove="Controls\PlaybackSettingsControl.xaml" />
|
||||
<None Remove="Controls\PropertyRow.xaml" />
|
||||
<None Remove="Controls\RemoteServerConfigControl.xaml" />
|
||||
<None Remove="Controls\ShortcutTextBox.xaml" />
|
||||
<None Remove="Controls\SystemTray.xaml" />
|
||||
<None Remove="Controls\WindowSettingsControl.xaml" />
|
||||
<None Remove="Views\LyricsSearchWindow.xaml" />
|
||||
<None Remove="Views\LyricsWindowSwitchWindow.xaml" />
|
||||
<None Remove="Views\MusicGalleryPage.xaml" />
|
||||
<None Remove="Views\MusicGalleryWindow.xaml" />
|
||||
<None Remove="Views\SettingsWindow.xaml" />
|
||||
<None Remove="Views\SystemTrayWindow.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Logo.ico" />
|
||||
@@ -54,36 +66,38 @@
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.Controls.OpacityMaskView" Version="0.1.251021-build.2365" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.Shimmer" Version="0.1.250703-build.2173" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Triggers" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.251219" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.251219" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.251219" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.251219" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.251219" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.251219" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.251219" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Triggers" Version="8.2.251219" />
|
||||
<PackageReference Include="ComputeSharp.D2D1.WinUI" Version="3.2.0" />
|
||||
<PackageReference Include="csharp-kana" Version="1.0.2" />
|
||||
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
|
||||
<PackageReference Include="DevWinUI.Controls" Version="9.6.0" />
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||
<PackageReference Include="F23.StringSimilarity" Version="7.0.0" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.2" />
|
||||
<PackageReference Include="DevWinUI.Controls" Version="9.8.1" />
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.6" />
|
||||
<PackageReference Include="F23.StringSimilarity" Version="7.0.1" />
|
||||
<PackageReference Include="FlaUI.UIA3" Version="5.0.0" />
|
||||
<PackageReference Include="FluentFTP" Version="53.0.2" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.4.1" />
|
||||
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
|
||||
<PackageReference Include="Interop.UIAutomationClient" Version="10.19041.0" />
|
||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.1" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||
<PackageReference Include="NAudio.Wasapi" Version="2.2.1" />
|
||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
||||
<PackageReference Include="NTextCat" Version="0.3.65" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="10.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="10.0.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.0" />
|
||||
<PackageReference Include="SMBLibrary" Version="1.5.5.1" />
|
||||
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="10.0.1" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.1" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
|
||||
<PackageReference Include="Vanara.PInvoke.DwmApi" Version="4.2.1" />
|
||||
@@ -92,6 +106,7 @@
|
||||
<PackageReference Include="Vanara.PInvoke.User32" Version="4.2.1" />
|
||||
<PackageReference Include="Vanara.Windows.Shell" Version="4.2.1" />
|
||||
<PackageReference Include="VCollab.DiscordRichPresence" Version="1.7.0" />
|
||||
<PackageReference Include="WebDav.Client" Version="2.9.0" />
|
||||
<PackageReference Include="WinUIEx" Version="2.9.0" />
|
||||
<PackageReference Include="z440.atl.core" Version="7.9.0" />
|
||||
</ItemGroup>
|
||||
@@ -111,6 +126,8 @@
|
||||
</ItemGroup>
|
||||
<!--Disable Trimming for Specific Packages-->
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="FlaUI.UIA3" />
|
||||
<TrimmerRootAssembly Include="Interop.UIAutomationClient" />
|
||||
<TrimmerRootAssembly Include="NAudio.Wasapi" />
|
||||
<TrimmerRootAssembly Include="TagLibSharp" />
|
||||
<TrimmerRootAssembly Include="Vanara.PInvoke.DwmApi" />
|
||||
@@ -153,6 +170,9 @@
|
||||
<Content Update="Assets\EmptyState.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Folder.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\foobar2000.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -292,7 +312,7 @@
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\AlbumArtLayoutSettingsControl.xaml">
|
||||
<Page Update="Controls\AlbumArtAreaStyleSettingsControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
@@ -327,12 +347,39 @@
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PRIResource Update="Strings\en-US\Resources.resw">
|
||||
<Generator></Generator>
|
||||
</PRIResource>
|
||||
<Page Update="Controls\RemoteServerConfigControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="TemplateSelector\" />
|
||||
<Page Update="Controls\NowPlayingBar.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Views\SystemTrayWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\WindowSettingsControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\AlbumArtAreaEffectSettingsControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\ImageSwitcher.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\ShadowImage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\PropertyRow.xaml">
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
public static class App
|
||||
{
|
||||
public const string AppAuthor = "Zhe Fang";
|
||||
public const string AppAuthorNicknameEN = "jayfunc";
|
||||
public const string AppAuthorNicknameZH = "摘叶飞镖";
|
||||
public const string AppName = "BetterLyrics";
|
||||
|
||||
public const string AutoStartupTaskId = "AutoStartup";
|
||||
|
||||
@@ -4,15 +4,25 @@
|
||||
{
|
||||
public const string MicrosoftStore = "https://apps.microsoft.com/detail/9p1wcd1p597r";
|
||||
|
||||
public const string GitHub = "https://github.com/jayfunc/BetterLyrics";
|
||||
public const string ShareHub = $"{GitHub}/blob/dev/ShareHub/index.md";
|
||||
public const string TermsOfService = $"{GitHub}/blob/dev/TermsofService.md";
|
||||
public const string PrivacyPolicy = $"{GitHub}/blob/dev/PrivacyPolicy.md";
|
||||
public const string UserGuide = $"{GitHub}/wiki/User-Guide";
|
||||
public const string AuthorGitHub = "https://github.com/jayfunc";
|
||||
|
||||
public const string Crowdin = "https://crowdin.com/project/betterlyrics/invite?h=413bb0df7afa420247a98fefdae5e12c2647410";
|
||||
|
||||
public const string BetterLyricsGitHub = $"{AuthorGitHub}/BetterLyrics";
|
||||
|
||||
public const string ShareHub = $"{BetterLyricsGitHub}/blob/dev/ShareHub/index.md";
|
||||
public const string TermsOfService = $"{BetterLyricsGitHub}/blob/dev/TermsofService.md";
|
||||
public const string PrivacyPolicy = $"{BetterLyricsGitHub}/blob/dev/PrivacyPolicy.md";
|
||||
public const string UserGuide = $"{BetterLyricsGitHub}/wiki/User-Guide";
|
||||
|
||||
public const string AppleMusicCfg = $"{UserGuide}#lyrics-source-configuration";
|
||||
|
||||
public const string QQGroup = "https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info";
|
||||
public const string Discord = "https://discord.gg/5yAQPnyCKv";
|
||||
public const string Telegram = "https://t.me/+svhSLZ7awPsxNGY1";
|
||||
|
||||
public const string BuyMeACoffee = "https://buymeacoffee.com/founchoo";
|
||||
public const string PayPal = "https://paypal.me/zhefangpay";
|
||||
public const string Afdian = "https://afdian.com/a/jayfunc";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,30 +28,37 @@
|
||||
</StackPanel>
|
||||
</dev:SettingsExpander.Header>
|
||||
<dev:SettingsExpander.Description>
|
||||
<StackPanel
|
||||
Margin="0,2,0,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="2">
|
||||
<TextBlock Text="©" />
|
||||
<HyperlinkButton
|
||||
Margin="0,-1,0,0"
|
||||
Content="Zhe Fang"
|
||||
NavigateUri="https://github.com/jayfunc" />
|
||||
<TextBlock Text="2025" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<StackPanel
|
||||
Margin="0,2,0,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="2">
|
||||
<TextBlock Text="©" />
|
||||
<HyperlinkButton
|
||||
Margin="0,-1,0,0"
|
||||
Content="{x:Bind const:App.AppAuthor}"
|
||||
NavigateUri="https://github.com/jayfunc" />
|
||||
<TextBlock Text="2025" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</dev:SettingsExpander.Description>
|
||||
<RichTextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run x:Uid="SettingsPageVersion" />
|
||||
<Run Text="{x:Bind helper:MetadataHelper.AppVersion}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<RichTextBlock
|
||||
Margin="0,-1,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run x:Uid="SettingsPageVersion" />
|
||||
<Run Text="{x:Bind helper:MetadataHelper.AppVersion}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</StackPanel>
|
||||
<dev:SettingsExpander.Items>
|
||||
|
||||
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||
<StackPanel Spacing="6">
|
||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.GitHub}" />
|
||||
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.BetterLyricsGitHub}" />
|
||||
<HyperlinkButton x:Uid="UserGuide" NavigateUri="{x:Bind const:Link.UserGuide}" />
|
||||
<HyperlinkButton x:Uid="PrivacyPolicy" NavigateUri="{x:Bind const:Link.PrivacyPolicy}" />
|
||||
<HyperlinkButton x:Uid="TermsOfService" NavigateUri="{x:Bind const:Link.TermsOfService}" />
|
||||
@@ -64,9 +71,9 @@
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock x:Uid="SetingsPageFeedback" />
|
||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||
<HyperlinkButton x:Uid="SettingsPageQQGroup" NavigateUri="{x:Bind const:Link.QQGroup}" />
|
||||
<HyperlinkButton x:Uid="SettingsPageDiscord" NavigateUri="{x:Bind const:Link.Discord}" />
|
||||
<HyperlinkButton x:Uid="SettingsPageTelegram" NavigateUri="{x:Bind const:Link.Telegram}" />
|
||||
<HyperlinkButton Content="QQ 反馈交流群" NavigateUri="{x:Bind const:Link.QQGroup}" />
|
||||
<HyperlinkButton Content="Discord" NavigateUri="{x:Bind const:Link.Discord}" />
|
||||
<HyperlinkButton Content="Telegram" NavigateUri="{x:Bind const:Link.Telegram}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
@@ -75,14 +82,14 @@
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock x:Uid="SetingsPageDonation" />
|
||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||
<HyperlinkButton Content="Buy Me a Coffee" NavigateUri="https://buymeacoffee.com/founchoo" />
|
||||
<HyperlinkButton Content="PayPal" NavigateUri="https://paypal.me/zhefangpay" />
|
||||
<Button
|
||||
Content="支付宝"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<HyperlinkButton Content="Buy Me a Coffee" NavigateUri="{x:Bind const:Link.BuyMeACoffee}" />
|
||||
<HyperlinkButton Content="PayPal" NavigateUri="{x:Bind const:Link.PayPal}" />
|
||||
<HyperlinkButton
|
||||
x:Name="AlipayButton"
|
||||
Click="AlipayButton_Click"
|
||||
Content="支付宝">
|
||||
<HyperlinkButton.ContextFlyout>
|
||||
<Flyout x:Name="AlipayFlyout">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style TargetType="FlyoutPresenter">
|
||||
<Setter Property="CornerRadius" Value="12" />
|
||||
@@ -91,14 +98,14 @@
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<Image Height="300" Source="/Assets/Alipay.jpg" />
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<Button
|
||||
Content="微信"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
</HyperlinkButton.ContextFlyout>
|
||||
</HyperlinkButton>
|
||||
<HyperlinkButton
|
||||
x:Name="WeChatButton"
|
||||
Click="WeChat_Click"
|
||||
Content="微信">
|
||||
<HyperlinkButton.ContextFlyout>
|
||||
<Flyout x:Name="WeChatFlyout">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style TargetType="FlyoutPresenter">
|
||||
<Setter Property="CornerRadius" Value="12" />
|
||||
@@ -107,8 +114,9 @@
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<Image Height="300" Source="/Assets/WeChatReward.png" />
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</HyperlinkButton.ContextFlyout>
|
||||
</HyperlinkButton>
|
||||
<HyperlinkButton Content="爱发电" NavigateUri="{x:Bind const:Link.Afdian}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="*" />
|
||||
@@ -120,16 +128,13 @@
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock x:Uid="SetingsPageContributors" />
|
||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||
<HyperlinkButton Content="jayfunc" NavigateUri="https://github.com/jayfunc" />
|
||||
<HyperlinkButton Content="Raspberry-Monster" NavigateUri="https://github.com/Raspberry-Monster" />
|
||||
<HyperlinkButton Content="ZHider" NavigateUri="https://github.com/ZHider" />
|
||||
<HyperlinkButton Content="kusutori" NavigateUri="https://github.com/kusutori" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<dev:SettingsCard x:Uid="SettingsPageThanksList">
|
||||
<Button
|
||||
Click="Patron_Click"
|
||||
Content="{ui:FontIcon FontSize=16,
|
||||
FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
</dev:SettingsExpander.Items>
|
||||
@@ -177,10 +182,6 @@
|
||||
</dev:SettingsExpander.ItemsHeader>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDebugOverlay">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsDebugOverlayEnabled, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageFixedTimeStep" Visibility="Collapsed">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.AdvancedSettings.IsFixedTimeStep, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
@@ -198,5 +199,190 @@
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid
|
||||
x:Name="CreditsReel"
|
||||
Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"
|
||||
Opacity="0"
|
||||
SizeChanged="CreditsReel_SizeChanged"
|
||||
Tapped="CreditsReel_Tapped"
|
||||
Visibility="Collapsed">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
<ScrollViewer
|
||||
x:Name="CreditsReelScrollViewer"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Hidden"
|
||||
ScrollViewer.VerticalScrollMode="Disabled">
|
||||
<RichTextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalTextAlignment="Center"
|
||||
LineHeight="28"
|
||||
PointerEntered="RichTextBlock_PointerEntered"
|
||||
PointerExited="RichTextBlock_PointerExited">
|
||||
|
||||
<Paragraph x:Name="CreditsReelHeader" />
|
||||
|
||||
<!-- 贡献者 -->
|
||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
||||
<Run x:Uid="SetingsPageContributors" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/jayfunc">
|
||||
<Run Text="jayfunc" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/Raspberry-Monster">
|
||||
<Run Text="Raspberry-Monster" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/ZHider">
|
||||
<Run Text="ZHider" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/kusutori">
|
||||
<Run Text="kusutori" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
|
||||
<!-- 赞助 -->
|
||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
||||
<Run x:Uid="SettingsPagePatrons" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Run Text="YE" />
|
||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Dec 3, 2025" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Run Text="**玄" />
|
||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 23, 2025" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Run Text="**智" />
|
||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 21, 2025" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Run Text="*鹤" />
|
||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 17, 2025" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Run Text="借过" />
|
||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 2, 2025" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Run Text="**华" />
|
||||
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Aug 28, 2025" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Run x:Uid="SettingsPageUserWhoPurchased" />
|
||||
</Paragraph>
|
||||
|
||||
<!-- 特别鸣谢 -->
|
||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
||||
<Run x:Uid="SetingsPageSpecialThanks" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Run x:Uid="SettingsPageYouNowUsing" />
|
||||
</Paragraph>
|
||||
|
||||
<!-- 代码参考 -->
|
||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
||||
<Run x:Uid="SetingsPageDeps" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://gist.github.com/mcworkaholic/82fbf203e3f1043bbe534b5b2974c0ce">
|
||||
<Run Text="Get album artwork from ITunes (with Python3 or C#)" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://stackoverflow.com/a/32013610/11048731">
|
||||
<Run Text="FullyObservableCollection" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/Storyteller-Studios/Impressionist">
|
||||
<Run Text="Impressionist" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/Storyteller-Studios/ColorThief.WinUI3">
|
||||
<Run Text="ColorThief.WinUI3" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/Johnwikix/SpectrumVisualization">
|
||||
<Run Text="SpectrumVisualization" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://www.shadertoy.com/view/Mdt3Df">
|
||||
<Run Text="Snow (as shown in sweden)" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://www.shadertoy.com/view/lllSR2">
|
||||
<Run Text="w10" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/mo-jinran/Taskbar-Lyrics">
|
||||
<Run Text="Taskbar-Lyrics" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/jayfunc/BetterLyrics/network/dependencies">
|
||||
<Run Text="..." />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
|
||||
<!-- UI/UX 设计参考 -->
|
||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
||||
<Run x:Uid="SetingsPageUIUXRef" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/solstice23/refined-now-playing-netease">
|
||||
<Run Text="refined-now-playing-netease" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/WXRIW/Lyricify-App">
|
||||
<Run Text="Lyricify" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://moriafly.com/program/salt-player">
|
||||
<Run Text="椒盐音乐 Salt Player" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="https://github.com/TwilightLemon/MyToolBar">
|
||||
<Run Text="MyToolBar" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Hyperlink NavigateUri="">
|
||||
<Run Text="" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
|
||||
<Run Text="{x:Bind const:App.AppName}" />
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
<Run Text="Proudly built by" />
|
||||
<Hyperlink NavigateUri="{x:Bind const:Link.AuthorGitHub}">
|
||||
<Run Text="{x:Bind const:App.AppAuthor}" />
|
||||
</Hyperlink>
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph x:Name="CreditsReelFooter" />
|
||||
|
||||
</RichTextBlock>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
@@ -9,6 +11,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class AboutControl : UserControl
|
||||
{
|
||||
private bool _isCreditsScrolling = false;
|
||||
public AboutControlViewModel ViewModel => (AboutControlViewModel)DataContext;
|
||||
|
||||
public AboutControl()
|
||||
@@ -16,5 +19,56 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
InitializeComponent();
|
||||
DataContext = Ioc.Default.GetRequiredService<AboutControlViewModel>();
|
||||
}
|
||||
|
||||
private async void Patron_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
CompositionTarget.Rendering += CompositionTarget_Rendering;
|
||||
CreditsReel.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
|
||||
CreditsReel.Opacity = 1;
|
||||
_isCreditsScrolling = true;
|
||||
}
|
||||
|
||||
private void CompositionTarget_Rendering(object? sender, object e)
|
||||
{
|
||||
if (_isCreditsScrolling)
|
||||
{
|
||||
CreditsReelScrollViewer.ChangeView(null, CreditsReelScrollViewer.VerticalOffset + 0.5, null);
|
||||
}
|
||||
}
|
||||
|
||||
private async void CreditsReel_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
|
||||
{
|
||||
CreditsReel.Opacity = 0;
|
||||
await Task.Delay(Constants.Time.AnimationDuration);
|
||||
CreditsReel.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
||||
CompositionTarget.Rendering -= CompositionTarget_Rendering;
|
||||
CreditsReelScrollViewer.ChangeView(null, 0, null);
|
||||
}
|
||||
|
||||
private void CreditsReel_SizeChanged(object sender, Microsoft.UI.Xaml.SizeChangedEventArgs e)
|
||||
{
|
||||
CreditsReelHeader.LineHeight = e.NewSize.Height;
|
||||
CreditsReelFooter.LineHeight = e.NewSize.Height / 2;
|
||||
}
|
||||
|
||||
private void RichTextBlock_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
_isCreditsScrolling = false;
|
||||
}
|
||||
|
||||
private void RichTextBlock_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
_isCreditsScrolling = true;
|
||||
}
|
||||
|
||||
private void WeChat_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
WeChatFlyout.ShowAt(WeChatButton);
|
||||
}
|
||||
|
||||
private void AlipayButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
AlipayFlyout.ShowAt(AlipayButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.AlbumArtAreaEffectSettingsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:dev="using:DevWinUI"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAlbumEffect" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageImageSwitchType" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind AlbumArtAreaEffectSettings.ImageSwitchType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageCrossfade" />
|
||||
<ComboBoxItem x:Uid="SettingsPageSlide" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,25 @@
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls;
|
||||
|
||||
public sealed partial class AlbumArtAreaEffectSettingsControl : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty AlbumArtAreaEffectSettingsProperty =
|
||||
DependencyProperty.Register(nameof(AlbumArtAreaEffectSettings), typeof(AlbumArtAreaEffectSettings), typeof(AlbumArtAreaEffectSettingsControl), new PropertyMetadata(default));
|
||||
|
||||
public AlbumArtAreaEffectSettings AlbumArtAreaEffectSettings
|
||||
{
|
||||
get => (AlbumArtAreaEffectSettings)GetValue(AlbumArtAreaEffectSettingsProperty);
|
||||
set => SetValue(AlbumArtAreaEffectSettingsProperty, value);
|
||||
}
|
||||
|
||||
public AlbumArtAreaEffectSettingsControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.AlbumArtLayoutSettingsControl"
|
||||
x:Class="BetterLyrics.WinUI3.Controls.AlbumArtAreaStyleSettingsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
@@ -16,8 +16,9 @@
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<TextBlock x:Uid="SettingsPageAlbumStyle" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<!-- 整体对齐 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind AlbumArtLayoutSettings.SongInfoAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLeft" />
|
||||
@@ -28,6 +29,26 @@
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAlbumArt" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<!-- 专辑高度 -->
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAlbumArtHeight"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
|
||||
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.IsAutoCoverImageHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard IsEnabled="{x:Bind AlbumArtLayoutSettings.IsAutoCoverImageHeight, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
|
||||
<NumberBox
|
||||
Minimum="0"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind AlbumArtLayoutSettings.CoverImageHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<!-- 专辑圆角 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ExtendedSlider
|
||||
Default="12"
|
||||
@@ -37,6 +58,7 @@
|
||||
Value="{x:Bind AlbumArtLayoutSettings.CoverImageRadius, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<!-- 专辑阴影 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageAlbumShadowAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ExtendedSlider
|
||||
Default="12"
|
||||
@@ -7,18 +7,18 @@ using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class AlbumArtLayoutSettingsControl : UserControl
|
||||
public sealed partial class AlbumArtAreaStyleSettingsControl : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty AlbumArtLayoutSettingsProperty =
|
||||
DependencyProperty.Register(nameof(AlbumArtLayoutSettings), typeof(AlbumArtLayoutSettings), typeof(AlbumArtLayoutSettingsControl), new PropertyMetadata(default));
|
||||
DependencyProperty.Register(nameof(AlbumArtLayoutSettings), typeof(AlbumArtAreaStyleSettings), typeof(AlbumArtAreaStyleSettingsControl), new PropertyMetadata(default));
|
||||
|
||||
public AlbumArtLayoutSettings AlbumArtLayoutSettings
|
||||
public AlbumArtAreaStyleSettings AlbumArtLayoutSettings
|
||||
{
|
||||
get => (AlbumArtLayoutSettings)GetValue(AlbumArtLayoutSettingsProperty);
|
||||
get => (AlbumArtAreaStyleSettings)GetValue(AlbumArtLayoutSettingsProperty);
|
||||
set => SetValue(AlbumArtLayoutSettingsProperty, value);
|
||||
}
|
||||
|
||||
public AlbumArtLayoutSettingsControl()
|
||||
public AlbumArtAreaStyleSettingsControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
x:Class="BetterLyrics.WinUI3.Controls.AppSettingsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:consts="using:BetterLyrics.WinUI3.Constants"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:dev="using:DevWinUI"
|
||||
@@ -42,11 +43,19 @@
|
||||
<Button x:Uid="SettingsPageRestart" Command="{x:Bind ViewModel.RestartAppCommand}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
<dev:SettingsExpander.ItemsFooter>
|
||||
<InfoBar IsClosable="False" IsOpen="True">
|
||||
<HyperlinkButton
|
||||
x:Uid="SettingsPageHelpUsTranslate"
|
||||
Padding="0"
|
||||
NavigateUri="{x:Bind consts:Link.Crowdin}" />
|
||||
</InfoBar>
|
||||
</dev:SettingsExpander.ItemsFooter>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<!-- App behavior -->
|
||||
<!-- Startup -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAppBehavior" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<TextBlock x:Uid="SettingsPageStartup" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageAutoStart" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch
|
||||
@@ -59,42 +68,46 @@
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.AutoOpen, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageAutoOpenLyricsWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.AutoStartLyricsWindow, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<!-- Music gallery window -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPageMusicGallery" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageAutoPlayWhenOpenMusicGalleryWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.AutoPlay, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageStopTrackOnGalleryWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.StopOnWindowClosed, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageExitOnGalleryWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.ExitOnWindowClosed, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<!-- Lyrics window -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPageLyricsWindow" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageExitOnLyricsWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ExitOnLyricsWindowClosed, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageListenNewSession" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ListenOnNewPlaybackSource, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<!-- Playback shortcut -->
|
||||
|
||||
<dev:SettingsCard x:Uid="LyricsSearchControlIgnoreCache" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IgnoreCacheWhenSearching, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<TextBlock x:Uid="SettingsPageShortcut" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageShowHideHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.ShowOrHideLyricsWindowShortcut, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageBorderlessHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.BorderlessShortcut, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageClickThroughHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.ClickThroughShortcut, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsWindowSwitchHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.LyricsWindowSwitchShortcut, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<!-- Playback shortcut -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPagePlaybackShortcut" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPagePlayOrPauseSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.PlayOrPauseShortcut, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid
|
||||
@@ -91,6 +92,47 @@
|
||||
Text="{x:Bind LyricsWindowStatus.Name, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
|
||||
<Grid Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}" Opacity="0">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerEntered">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerExited">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Click="OpenButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="SystemTrayLyrics" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Click="CloseButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsEnabled="{x:Bind LyricsWindowStatus.IsOpened, Mode=OneWay}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="SettingsPageCloseStatus" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Linq;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
@@ -9,6 +14,8 @@ namespace BetterLyrics.WinUI3.Controls;
|
||||
|
||||
public sealed partial class DemoWindowGrid : UserControl
|
||||
{
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
public DemoWindowGrid()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -22,4 +29,32 @@ public sealed partial class DemoWindowGrid : UserControl
|
||||
get => (LyricsWindowStatus)GetValue(LyricsWindowStatusProperty);
|
||||
set => SetValue(LyricsWindowStatusProperty, value);
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var data = (LyricsWindowStatus)(((FrameworkElement)sender).DataContext);
|
||||
var window = WindowHook.GetWindows<NowPlayingWindow>().FirstOrDefault(x => x.LyricsWindowStatus == data);
|
||||
window?.CloseWindow();
|
||||
}
|
||||
|
||||
private void OpenButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var status = (LyricsWindowStatus)(((FrameworkElement)sender).DataContext);
|
||||
// <20>ģʽ
|
||||
if (_settingsService.AppSettings.GeneralSettings.MultiNowPlayingWindowMode)
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
|
||||
}
|
||||
// <20><><EFBFBD><EFBFBD>ģʽ
|
||||
else
|
||||
{
|
||||
var openedWindows = WindowHook.GetWindows<NowPlayingWindow>();
|
||||
foreach (var item in openedWindows.Where(x => x.LyricsWindowStatus != status))
|
||||
{
|
||||
item.CloseWindow();
|
||||
}
|
||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,12 +4,8 @@ using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
@@ -52,7 +48,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
// FontFamilies = fontFamilies;
|
||||
// });
|
||||
//});
|
||||
FontFamilies = FontHelper.SystemFontFamilies.OrderBy(x => x).ToList();
|
||||
FontFamilies = FontHelper.GetSystemFontFamilies();
|
||||
}
|
||||
|
||||
private void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.ImageSwitcher"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:const="using:BetterLyrics.WinUI3.Constants"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition Duration="{x:Bind const:Time.AnimationDuration}" />
|
||||
</Grid.OpacityTransition>
|
||||
<local:ShadowImage
|
||||
x:Name="LastAlbumArtImage"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
CornerRadiusAmount="{x:Bind CornerRadiusAmount, Mode=OneWay}"
|
||||
ShadowAmount="{x:Bind ShadowAmount, Mode=OneWay}" />
|
||||
<local:ShadowImage
|
||||
x:Name="AlbumArtImage"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
CornerRadiusAmount="{x:Bind CornerRadiusAmount, Mode=OneWay}"
|
||||
ShadowAmount="{x:Bind ShadowAmount, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,133 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls;
|
||||
|
||||
public sealed partial class ImageSwitcher : UserControl
|
||||
{
|
||||
public int CornerRadiusAmount
|
||||
{
|
||||
get { return (int)GetValue(CornerRadiusAmountProperty); }
|
||||
set { SetValue(CornerRadiusAmountProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CornerRadiusAmountProperty =
|
||||
DependencyProperty.Register(nameof(CornerRadiusAmount), typeof(int), typeof(ImageSwitcher), new PropertyMetadata(0));
|
||||
|
||||
public int ShadowAmount
|
||||
{
|
||||
get { return (int)GetValue(ShadowAmountProperty); }
|
||||
set { SetValue(ShadowAmountProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ShadowAmountProperty =
|
||||
DependencyProperty.Register(nameof(ShadowAmount), typeof(int), typeof(ImageSwitcher), new PropertyMetadata(0));
|
||||
|
||||
public ImageSource? Source
|
||||
{
|
||||
get { return (ImageSource?)GetValue(SourceProperty); }
|
||||
set { SetValue(SourceProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SourceProperty =
|
||||
DependencyProperty.Register(nameof(Source), typeof(ImageSource), typeof(ImageSwitcher), new PropertyMetadata(null, OnDependencyPropertyChanged));
|
||||
|
||||
public ImageSwitchType SwitchType
|
||||
{
|
||||
get { return (ImageSwitchType)GetValue(SwitchTypeProperty); }
|
||||
set { SetValue(SwitchTypeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SwitchTypeProperty =
|
||||
DependencyProperty.Register(nameof(SwitchType), typeof(ImageSwitchType), typeof(ImageSwitcher), new PropertyMetadata(ImageSwitchType.Crossfade));
|
||||
|
||||
public ImageSwitcher()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void UpdateSource()
|
||||
{
|
||||
switch (SwitchType)
|
||||
{
|
||||
case ImageSwitchType.Crossfade:
|
||||
UpdateSourceCrossfade();
|
||||
break;
|
||||
case ImageSwitchType.Slide:
|
||||
UpdateSourceSlide();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSourceCrossfade()
|
||||
{
|
||||
// Ϊ<><CEAA><EFBFBD><EFBFBD>ͼƬ<CDBC><C6AC><EFBFBD>þ<EFBFBD>Դ
|
||||
LastAlbumArtImage.Source = AlbumArtImage.Source;
|
||||
// ʹ<><CAB9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD>
|
||||
LastAlbumArtImage.TranslationTransition = null;
|
||||
LastAlbumArtImage.OpacityTransition = null;
|
||||
LastAlbumArtImage.Translation = new();
|
||||
LastAlbumArtImage.Opacity = 1;
|
||||
LastAlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||
|
||||
// ʹǰ<CAB9><C7B0>ͼƬ<CDBC><C6AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD>
|
||||
AlbumArtImage.TranslationTransition = null;
|
||||
AlbumArtImage.OpacityTransition = null;
|
||||
AlbumArtImage.Translation = new();
|
||||
AlbumArtImage.Opacity = 0;
|
||||
AlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||
// ֮<><D6AE>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
|
||||
AlbumArtImage.Source = Source;
|
||||
|
||||
// <20><><EFBFBD>浭<EFBFBD><E6B5AD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
LastAlbumArtImage.Opacity = 0;
|
||||
AlbumArtImage.Opacity = 1;
|
||||
}
|
||||
|
||||
private void UpdateSourceSlide()
|
||||
{
|
||||
// Ϊ<><CEAA><EFBFBD><EFBFBD>ͼƬ<CDBC><C6AC><EFBFBD>þ<EFBFBD>Դ
|
||||
LastAlbumArtImage.Source = AlbumArtImage.Source;
|
||||
// ʹ<><CAB9><EFBFBD><EFBFBD>λ
|
||||
LastAlbumArtImage.TranslationTransition = null;
|
||||
LastAlbumArtImage.OpacityTransition = null;
|
||||
LastAlbumArtImage.Translation = new();
|
||||
LastAlbumArtImage.Opacity = 1;
|
||||
LastAlbumArtImage.TranslationTransition = new Vector3Transition { Duration = Constants.Time.AnimationDuration };
|
||||
LastAlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||
|
||||
// ʹǰ<CAB9><C7B0>ͼƬ<CDBC><C6AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD>
|
||||
AlbumArtImage.TranslationTransition = null;
|
||||
AlbumArtImage.OpacityTransition = null;
|
||||
AlbumArtImage.Translation = new(-(float)ActualWidth, 0, 0);
|
||||
AlbumArtImage.Opacity = 0;
|
||||
AlbumArtImage.TranslationTransition = new Vector3Transition { Duration = Constants.Time.AnimationDuration };
|
||||
AlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||
// ֮<><D6AE>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
|
||||
AlbumArtImage.Source = Source;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>
|
||||
LastAlbumArtImage.Opacity = 0;
|
||||
AlbumArtImage.Opacity = 1;
|
||||
LastAlbumArtImage.Translation = new(-(float)ActualWidth, 0, 0);
|
||||
AlbumArtImage.Translation = new();
|
||||
}
|
||||
|
||||
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is ImageSwitcher imageSwitcher)
|
||||
{
|
||||
if (e.Property == SourceProperty)
|
||||
{
|
||||
imageSwitcher.UpdateSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<TextBlock x:Uid="SettingsPageBackgroundOverlay" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageTheme" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox x:Name="ThemeComboBox" SelectedIndex="{x:Bind LyricsBackgroundSettings.LyricsBackgroundTheme, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
@@ -47,6 +47,43 @@
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAlbumArtLayer"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageOpacity" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
||||
<uc:ExtendedSlider
|
||||
Default="100"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Unit="%"
|
||||
Value="{x:Bind LyricsBackgroundSettings.CoverOverlayOpacity, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageSpeed" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
||||
<uc:ExtendedSlider
|
||||
Default="50"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Unit="%"
|
||||
Value="{x:Bind LyricsBackgroundSettings.CoverOverlaySpeed, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageBlurAmount" IsEnabled="{x:Bind LyricsBackgroundSettings.IsCoverOverlayEnabled, Mode=OneWay}">
|
||||
<uc:ExtendedSlider
|
||||
Default="100"
|
||||
Maximum="200"
|
||||
Minimum="0"
|
||||
Value="{x:Bind LyricsBackgroundSettings.CoverOverlayBlurAmount, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageFluidLayer"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
|
||||
@@ -8,51 +8,51 @@ using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using BetterLyrics.WinUI3.Renderer;
|
||||
using BetterLyrics.WinUI3.Services.LastFMService;
|
||||
using BetterLyrics.WinUI3.Services.LiveStatesService;
|
||||
using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class LyricsCanvas : UserControl,
|
||||
IRecipient<PropertyChangedMessage<int>>,
|
||||
IRecipient<PropertyChangedMessage<AlbumArtThemeColors>>,
|
||||
IRecipient<PropertyChangedMessage<TimeSpan>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsData?>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsWindowStatus>>,
|
||||
IRecipient<PropertyChangedMessage<SongInfo?>>,
|
||||
IRecipient<PropertyChangedMessage<int>>,
|
||||
IRecipient<PropertyChangedMessage<double>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<TextAlignmentType>>,
|
||||
IRecipient<PropertyChangedMessage<SongInfo?>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsFontWeight>>,
|
||||
IRecipient<PropertyChangedMessage<string>>
|
||||
IRecipient<PropertyChangedMessage<string>>,
|
||||
IRecipient<PropertyChangedMessage<IRandomAccessStream?>>
|
||||
{
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||
private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
|
||||
private readonly ILastFMService _lastFMService = Ioc.Default.GetRequiredService<ILastFMService>();
|
||||
|
||||
private readonly LyricsRenderer _lyricsRenderer = new();
|
||||
private readonly FluidBackgroundRenderer _fluidRenderer = new();
|
||||
private readonly CoverBackgroundRenderer _coverRenderer = new();
|
||||
private readonly PureColorBackgroundRenderer _pureColorRenderer = new();
|
||||
private readonly SnowRenderer _snowRenderer = new();
|
||||
private readonly FogRenderer _fogRenderer = new();
|
||||
private readonly SpectrumRenderer _spectrumRenderer = new();
|
||||
|
||||
private readonly LyricsSynchronizer _synchronizer = new();
|
||||
private readonly LyricsLayoutManager _layoutManager = new();
|
||||
private readonly LyricsAnimator _animator = new();
|
||||
|
||||
private readonly SpectrumAnalyzer _spectrumAnalyzer = new();
|
||||
@@ -97,6 +97,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
|
||||
private TimeSpan _songPositionWithOffset;
|
||||
private TimeSpan _songPosition; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1>
|
||||
private TimeSpan _totalPlayedTime; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ػ<EFBFBD><D8BB>ظ<EFBFBD><D8B8><EFBFBD><EFBFBD>ŵ<EFBFBD>ʱ<EFBFBD>䣩
|
||||
private bool _isLastFMTracked = false;
|
||||
@@ -107,13 +108,16 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
private double _renderLyricsHeight = 0;
|
||||
private double _renderLyricsOpacity = 0;
|
||||
|
||||
private LyricsWindowStatus? _lyricsWindowStatus = null;
|
||||
private AlbumArtThemeColors _albumArtThemeColors = new();
|
||||
|
||||
private Point _mousePosition = new(0, 0);
|
||||
private int _mouseHoverLineIndex = -1;
|
||||
private bool _isMouseInLyricsArea = false;
|
||||
private bool _isMousePressing = false;
|
||||
private bool _isMouseScrolling = false;
|
||||
|
||||
private LyricsData? _lyricsData;
|
||||
private List<RenderLyricsLine>? _renderLyricsLines = null;
|
||||
|
||||
private bool _isLayoutChanged = true;
|
||||
private bool _isMouseScrollingChanged = false;
|
||||
@@ -124,9 +128,27 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
public TimeSpan SongPosition => _songPosition;
|
||||
public double CurrentCanvasYScroll => _canvasYScrollTransition.Value;
|
||||
public double ActualLyricsHeight => _layoutManager.CalculateActualHeight(_lyricsData?.LyricsLines);
|
||||
public double ActualLyricsHeight => LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines);
|
||||
public int CurrentHoveringLineIndex => _mouseHoverLineIndex;
|
||||
|
||||
public LyricsWindowStatus? LyricsWindowStatus
|
||||
{
|
||||
get { return (LyricsWindowStatus?)GetValue(LyricsWindowStatusProperty); }
|
||||
set { SetValue(LyricsWindowStatusProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsWindowStatusProperty =
|
||||
DependencyProperty.Register(nameof(LyricsWindowStatus), typeof(LyricsWindowStatus), typeof(LyricsCanvas), new PropertyMetadata(null, OnDependencyPropertyChanged));
|
||||
|
||||
public AlbumArtThemeColors AlbumArtThemeColors
|
||||
{
|
||||
get { return (AlbumArtThemeColors)GetValue(AlbumArtThemeColorsProperty); }
|
||||
set { SetValue(AlbumArtThemeColorsProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty AlbumArtThemeColorsProperty =
|
||||
DependencyProperty.Register(nameof(AlbumArtThemeColors), typeof(AlbumArtThemeColors), typeof(LyricsCanvas), new PropertyMetadata(new AlbumArtThemeColors(), OnDependencyPropertyChanged));
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC> X <20><><EFBFBD><EFBFBD>
|
||||
public double LyricsStartX
|
||||
{
|
||||
@@ -135,7 +157,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsStartXProperty =
|
||||
DependencyProperty.Register(nameof(LyricsStartX), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(LyricsStartX), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ Y <20><><EFBFBD><EFBFBD>
|
||||
public double LyricsStartY
|
||||
@@ -145,7 +167,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsStartYProperty =
|
||||
DependencyProperty.Register(nameof(LyricsStartY), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(LyricsStartY), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
public double LyricsWidth
|
||||
@@ -155,7 +177,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsWidthProperty =
|
||||
DependencyProperty.Register(nameof(LyricsWidth), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(LyricsWidth), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
|
||||
public double LyricsHeight
|
||||
@@ -165,7 +187,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsHeightProperty =
|
||||
DependencyProperty.Register(nameof(LyricsHeight), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(LyricsHeight), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><CDB8><EFBFBD><EFBFBD>
|
||||
public double LyricsOpacity
|
||||
@@ -175,7 +197,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsOpacityProperty =
|
||||
DependencyProperty.Register(nameof(LyricsOpacity), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(LyricsOpacity), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// <20>û<EFBFBD><C3BB>ٿ<EFBFBD><D9BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѹ<EFBFBD><D1B9><EFBFBD><EFBFBD>ľ<EFBFBD><C4BE>루<EFBFBD><EBA3A8> 0 <20><>ʼ<EFBFBD>㣩
|
||||
@@ -187,7 +209,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MouseScrollOffsetProperty =
|
||||
DependencyProperty.Register(nameof(MouseScrollOffset), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(MouseScrollOffset), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// <20>û<EFBFBD><C3BB><EFBFBD><EFBFBD>굱ǰ<EAB5B1><C7B0>λ<EFBFBD>ã<EFBFBD><C3A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڸ<EFBFBD><DAB8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͻǣ<CFBD>
|
||||
@@ -199,7 +221,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MousePositionProperty =
|
||||
DependencyProperty.Register(nameof(MousePosition), typeof(Point), typeof(LyricsCanvas), new PropertyMetadata(new Point(0, 0), OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(MousePosition), typeof(Point), typeof(LyricsCanvas), new PropertyMetadata(new Point(0, 0), OnDependencyPropertyChanged));
|
||||
|
||||
public bool IsMouseInLyricsArea
|
||||
{
|
||||
@@ -208,7 +230,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsMouseInLyricsAreaProperty =
|
||||
DependencyProperty.Register(nameof(IsMouseInLyricsArea), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(IsMouseInLyricsArea), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnDependencyPropertyChanged));
|
||||
|
||||
public bool IsMousePressing
|
||||
{
|
||||
@@ -217,7 +239,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsMousePressingProperty =
|
||||
DependencyProperty.Register(nameof(IsMousePressing), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(IsMousePressing), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnDependencyPropertyChanged));
|
||||
|
||||
public bool IsMouseScrolling
|
||||
{
|
||||
@@ -226,30 +248,27 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsMouseScrollingProperty =
|
||||
DependencyProperty.Register(nameof(IsMouseScrolling), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(IsMouseScrolling), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnDependencyPropertyChanged));
|
||||
|
||||
public LyricsCanvas()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<int>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<AlbumArtThemeColors>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<TimeSpan>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsData?>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsWindowStatus>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<double>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<bool>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<TextAlignmentType>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<SongInfo?>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsFontWeight>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<string>>(this);
|
||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||
|
||||
UpdateRenderLyricsLines();
|
||||
}
|
||||
|
||||
private static void OnLayoutPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is LyricsCanvas canvas)
|
||||
{
|
||||
if (e.Property == LyricsStartXProperty)
|
||||
if (e.Property == LyricsWindowStatusProperty)
|
||||
{
|
||||
canvas._lyricsWindowStatus = (LyricsWindowStatus)e.NewValue;
|
||||
canvas._isLayoutChanged = true;
|
||||
}
|
||||
else if (e.Property == LyricsStartXProperty)
|
||||
{
|
||||
canvas._renderLyricsStartX = Convert.ToDouble(e.NewValue);
|
||||
canvas._isLayoutChanged = true;
|
||||
@@ -299,6 +318,18 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
canvas._isMouseScrolling = value;
|
||||
}
|
||||
else if (e.Property == AlbumArtThemeColorsProperty)
|
||||
{
|
||||
var albumArtThemeColors = (AlbumArtThemeColors)e.NewValue;
|
||||
canvas._immersiveBgColorTransition.StartTransition(albumArtThemeColors.EnvColor);
|
||||
canvas._accentColor1Transition.StartTransition(albumArtThemeColors.AccentColor1);
|
||||
canvas._accentColor2Transition.StartTransition(albumArtThemeColors.AccentColor2);
|
||||
canvas._accentColor3Transition.StartTransition(albumArtThemeColors.AccentColor3);
|
||||
canvas._accentColor4Transition.StartTransition(albumArtThemeColors.AccentColor4);
|
||||
|
||||
canvas._albumArtThemeColors = albumArtThemeColors;
|
||||
canvas._isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,25 +337,22 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
private void Canvas_Draw(ICanvasAnimatedControl sender, CanvasAnimatedDrawEventArgs args)
|
||||
{
|
||||
if (_lyricsWindowStatus == null) return;
|
||||
|
||||
var bounds = new Rect(0, 0, sender.Size.Width, sender.Size.Height);
|
||||
|
||||
var status = _liveStatesService.LiveStates.LyricsWindowStatus;
|
||||
var albumArtLayout = status.AlbumArtLayoutSettings;
|
||||
var lyricsBg = status.LyricsBackgroundSettings;
|
||||
var lyricsStyle = status.LyricsStyleSettings;
|
||||
var lyricsEffect = status.LyricsEffectSettings;
|
||||
var albumArtLayout = _lyricsWindowStatus.AlbumArtLayoutSettings;
|
||||
var lyricsBg = _lyricsWindowStatus.LyricsBackgroundSettings;
|
||||
var lyricsStyle = _lyricsWindowStatus.LyricsStyleSettings;
|
||||
var lyricsEffect = _lyricsWindowStatus.LyricsEffectSettings;
|
||||
|
||||
double songDuration = _mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0;
|
||||
bool isForceWordByWord = _settingsService.AppSettings.GeneralSettings.IsForceWordByWordEffect;
|
||||
|
||||
double fixedSongPositionMs = _songPosition.TotalMilliseconds + (_mediaSessionsService.CurrentMediaSourceProviderInfo?.PositionOffset ?? 0);
|
||||
|
||||
var lyricsThemeColors = _mediaSessionsService.AlbumArtThemeColors;
|
||||
|
||||
Color overlayColor;
|
||||
double finalOpacity;
|
||||
|
||||
if (status.IsAdaptToEnvironment)
|
||||
if (_lyricsWindowStatus.IsAdaptToEnvironment)
|
||||
{
|
||||
// <20><><EFBFBD><EFBFBD>Ӧɫ
|
||||
overlayColor = _immersiveBgColorTransition.Value;
|
||||
@@ -345,8 +373,8 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
lyricsBg.IsPureColorOverlayEnabled
|
||||
);
|
||||
|
||||
_fluidRenderer.Opacity = lyricsBg.FluidOverlayOpacity;
|
||||
_fluidRenderer.IsEnabled = lyricsBg.IsFluidOverlayEnabled;
|
||||
_coverRenderer.Draw(sender, args.DrawingSession);
|
||||
|
||||
_fluidRenderer.Draw(sender, args.DrawingSession);
|
||||
|
||||
_snowRenderer.Draw(sender, args.DrawingSession);
|
||||
@@ -356,7 +384,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
_lyricsRenderer.Draw(
|
||||
control: sender,
|
||||
ds: args.DrawingSession,
|
||||
lyricsData: _lyricsData,
|
||||
lines: _renderLyricsLines,
|
||||
playingLineIndex: _playingLineIndex,
|
||||
mouseHoverLineIndex: _mouseHoverLineIndex,
|
||||
isMousePressing: _isMousePressing,
|
||||
@@ -368,21 +396,22 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
lyricsHeight: _renderLyricsHeight,
|
||||
userScrollOffset: _mouseYScrollTransition.Value,
|
||||
lyricsOpacity: _renderLyricsOpacity,
|
||||
windowStatus: status,
|
||||
strokeColor: lyricsThemeColors.StrokeFontColor,
|
||||
bgColor: lyricsThemeColors.BgFontColor,
|
||||
fgColor: lyricsThemeColors.FgFontColor,
|
||||
playingLineTopOffsetFactor: lyricsStyle.PlayingLineTopOffset / 100.0,
|
||||
windowStatus: _lyricsWindowStatus,
|
||||
strokeColor: _albumArtThemeColors.StrokeFontColor,
|
||||
bgColor: _albumArtThemeColors.BgFontColor,
|
||||
fgColor: _albumArtThemeColors.FgFontColor,
|
||||
getPlaybackState: (lineIndex) =>
|
||||
{
|
||||
if (_lyricsData == null) return new LinePlaybackState();
|
||||
if (_renderLyricsLines == null) return new LinePlaybackState();
|
||||
|
||||
var line = _lyricsData.LyricsLines.ElementAtOrDefault(lineIndex);
|
||||
var line = _renderLyricsLines.ElementAtOrDefault(lineIndex);
|
||||
if (line == null) return new LinePlaybackState();
|
||||
|
||||
var nextLine = _lyricsData.LyricsLines.ElementAtOrDefault(lineIndex + 1);
|
||||
var nextLine = _renderLyricsLines.ElementAtOrDefault(lineIndex + 1);
|
||||
|
||||
return _synchronizer.GetLinePlayingProgress(
|
||||
fixedSongPositionMs,
|
||||
_songPositionWithOffset.TotalMilliseconds,
|
||||
line,
|
||||
nextLine,
|
||||
songDuration,
|
||||
@@ -403,32 +432,35 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
style: lyricsBg.SpectrumStyle,
|
||||
canvasWidth: sender.Size.Width,
|
||||
canvasHeight: sender.Size.Height,
|
||||
fillColor: lyricsThemeColors.BgFontColor
|
||||
fillColor: _albumArtThemeColors.BgFontColor
|
||||
);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
args.DrawingSession.DrawText(
|
||||
$"Lyrics render start pos: ({(int)_renderLyricsStartX}, {(int)_renderLyricsStartY})\n" +
|
||||
$"Lyrics render size: [{(int)_renderLyricsWidth} x {(int)_renderLyricsHeight}]\n" +
|
||||
$"Lyrics actual height: {_layoutManager.CalculateActualHeight(_lyricsData?.LyricsLines)}\n" +
|
||||
$"Playing line (idx): {_playingLineIndex}\n" +
|
||||
$"Mouse hovering line (idx): {_mouseHoverLineIndex}\n" +
|
||||
$"Visible lines range (idx): [{_visibleRange.Start}, {_visibleRange.End}]\n" +
|
||||
$"Total line count: {_layoutManager.CalculateMaxRange(_lyricsData?.LyricsLines).End + 1}\n" +
|
||||
$"Played: {TimeSpan.FromMilliseconds(fixedSongPositionMs)} / {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
|
||||
$"Y offset: {_canvasYScrollTransition.Value}\n" +
|
||||
$"User scroll offset: {_mouseYScrollTransition.Value}",
|
||||
new Vector2(0, 0), Colors.Red);
|
||||
//args.DrawingSession.DrawText(
|
||||
// $"Lyrics render start pos: ({(int)_renderLyricsStartX}, {(int)_renderLyricsStartY})\n" +
|
||||
// $"Lyrics render size: [{(int)_renderLyricsWidth} x {(int)_renderLyricsHeight}]\n" +
|
||||
// $"Lyrics actual height: {LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines)}\n" +
|
||||
// $"Playing line (idx): {_playingLineIndex}\n" +
|
||||
// $"Mouse hovering line (idx): {_mouseHoverLineIndex}\n" +
|
||||
// $"Visible lines range (idx): [{_visibleRange.Start}, {_visibleRange.End}]\n" +
|
||||
// $"Total line count: {LyricsLayoutManager.CalculateMaxRange(_renderLyricsLines).End + 1}\n" +
|
||||
// $"Played: {_songPosition} / {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
|
||||
// $"Y offset: {_canvasYScrollTransition.Value}\n" +
|
||||
// $"User scroll offset: {_mouseYScrollTransition.Value}",
|
||||
// new Vector2(0, 0), Colors.Red);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
private void Canvas_Update(ICanvasAnimatedControl sender, CanvasAnimatedUpdateEventArgs args)
|
||||
{
|
||||
var lyricsBg = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings;
|
||||
var lyricsEffect = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings;
|
||||
var albumArtThemeColors = _mediaSessionsService.AlbumArtThemeColors;
|
||||
if (_lyricsWindowStatus == null) return;
|
||||
|
||||
var lyricsBg = _lyricsWindowStatus.LyricsBackgroundSettings;
|
||||
var lyricsStyle = _lyricsWindowStatus.LyricsStyleSettings;
|
||||
var lyricsEffect = _lyricsWindowStatus.LyricsEffectSettings;
|
||||
var lyricsData = _mediaSessionsService.CurrentLyricsData;
|
||||
|
||||
TimeSpan elapsedTime = args.Timing.ElapsedTime;
|
||||
|
||||
@@ -446,7 +478,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
#region UpdatePlayingLineIndex
|
||||
|
||||
int newPlayingIndex = _synchronizer.GetCurrentLineIndex(_songPosition.TotalMilliseconds, _lyricsData);
|
||||
int newPlayingIndex = _synchronizer.GetCurrentLineIndex(_songPositionWithOffset.TotalMilliseconds, lyricsData);
|
||||
bool isPlayingLineChanged = newPlayingIndex != _playingLineIndex;
|
||||
_playingLineIndex = newPlayingIndex;
|
||||
|
||||
@@ -456,7 +488,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
if (isPlayingLineChanged || _isLayoutChanged)
|
||||
{
|
||||
var targetScroll = _layoutManager.CalculateTargetScrollOffset(_lyricsData, _playingLineIndex);
|
||||
var targetScroll = LyricsLayoutManager.CalculateTargetScrollOffset(_renderLyricsLines, _playingLineIndex);
|
||||
if (targetScroll.HasValue) _canvasTargetScrollOffset = targetScroll.Value;
|
||||
|
||||
_canvasYScrollTransition.SetEasingType(lyricsEffect.LyricsScrollEasingType);
|
||||
@@ -469,36 +501,40 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
_mouseYScrollTransition.Update(elapsedTime);
|
||||
|
||||
_mouseHoverLineIndex = _layoutManager.FindMouseHoverLineIndex(
|
||||
_lyricsData?.LyricsLines,
|
||||
_mouseHoverLineIndex = LyricsLayoutManager.FindMouseHoverLineIndex(
|
||||
_renderLyricsLines,
|
||||
_isMouseInLyricsArea,
|
||||
_mousePosition,
|
||||
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value,
|
||||
_renderLyricsStartY,
|
||||
_renderLyricsHeight
|
||||
_mousePosition,
|
||||
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value,
|
||||
_renderLyricsStartY,
|
||||
_renderLyricsHeight,
|
||||
lyricsStyle.PlayingLineTopOffset / 100.0
|
||||
);
|
||||
|
||||
_visibleRange = _layoutManager.CalculateVisibleRange(
|
||||
_lyricsData?.LyricsLines,
|
||||
_visibleRange = LyricsLayoutManager.CalculateVisibleRange(
|
||||
_renderLyricsLines,
|
||||
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value, // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>λ<EFBFBD><CEBB>
|
||||
_renderLyricsStartY,
|
||||
_renderLyricsHeight,
|
||||
sender.Size.Height
|
||||
sender.Size.Height,
|
||||
lyricsStyle.PlayingLineTopOffset / 100.0
|
||||
);
|
||||
|
||||
var maxRange = _layoutManager.CalculateMaxRange(_lyricsData?.LyricsLines);
|
||||
var maxRange = LyricsLayoutManager.CalculateMaxRange(_renderLyricsLines);
|
||||
|
||||
_animator.UpdateLines(
|
||||
_lyricsData,
|
||||
_renderLyricsLines,
|
||||
_isMouseScrolling ? maxRange.Start : _visibleRange.Start,
|
||||
_isMouseScrolling ? maxRange.End : _visibleRange.End,
|
||||
_playingLineIndex,
|
||||
sender.Size.Height,
|
||||
_canvasTargetScrollOffset,
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings,
|
||||
lyricsStyle.PlayingLineTopOffset / 100.0,
|
||||
_lyricsWindowStatus.LyricsStyleSettings,
|
||||
_lyricsWindowStatus.LyricsEffectSettings,
|
||||
_canvasYScrollTransition,
|
||||
albumArtThemeColors.BgFontColor,
|
||||
albumArtThemeColors.FgFontColor,
|
||||
_albumArtThemeColors.BgFontColor,
|
||||
_albumArtThemeColors.FgFontColor,
|
||||
elapsedTime,
|
||||
_isMouseScrolling,
|
||||
_isLayoutChanged,
|
||||
@@ -509,25 +545,31 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
_isMouseScrollingChanged = false;
|
||||
|
||||
_lyricsRenderer.CalculateLyrics3DMatrix(
|
||||
lyricsStyle: lyricsStyle,
|
||||
lyricsEffect: lyricsEffect,
|
||||
lyricsX: _renderLyricsStartX,
|
||||
lyricsY: _renderLyricsStartY,
|
||||
lyricsWidth: _renderLyricsWidth,
|
||||
canvasHeight: sender.Size.Height
|
||||
lyricsHeight: _renderLyricsHeight
|
||||
);
|
||||
|
||||
_isLayoutChanged = false;
|
||||
|
||||
if (_fluidRenderer.IsEnabled)
|
||||
{
|
||||
_fluidRenderer.UpdateColors(
|
||||
_accentColor1Transition.Value,
|
||||
_accentColor2Transition.Value,
|
||||
_accentColor3Transition.Value,
|
||||
_accentColor4Transition.Value
|
||||
);
|
||||
_fluidRenderer.Update(elapsedTime);
|
||||
}
|
||||
_fluidRenderer.IsEnabled = lyricsBg.IsFluidOverlayEnabled;
|
||||
_fluidRenderer.Opacity = lyricsBg.FluidOverlayOpacity / 100.0;
|
||||
_fluidRenderer.UpdateColors(
|
||||
_accentColor1Transition.Value,
|
||||
_accentColor2Transition.Value,
|
||||
_accentColor3Transition.Value,
|
||||
_accentColor4Transition.Value
|
||||
);
|
||||
_fluidRenderer.Update(elapsedTime);
|
||||
|
||||
_coverRenderer.IsEnabled = lyricsBg.IsCoverOverlayEnabled;
|
||||
_coverRenderer.Opacity = lyricsBg.CoverOverlayOpacity;
|
||||
_coverRenderer.BlurAmount = lyricsBg.CoverOverlayBlurAmount;
|
||||
_coverRenderer.Speed = lyricsBg.CoverOverlaySpeed;
|
||||
_coverRenderer.Update(elapsedTime);
|
||||
|
||||
_snowRenderer.IsEnabled = lyricsBg.IsSnowFlakeOverlayEnabled;
|
||||
_snowRenderer.Amount = lyricsBg.SnowFlakeOverlayAmount / 100f;
|
||||
@@ -554,19 +596,29 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
private void Canvas_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Canvas.RemoveFromVisualTree();
|
||||
Canvas = null;
|
||||
|
||||
_fluidRenderer.Dispose();
|
||||
_coverRenderer.Dispose();
|
||||
_snowRenderer.Dispose();
|
||||
_fogRenderer.Dispose();
|
||||
_spectrumRenderer.Dispose();
|
||||
|
||||
_renderLyricsLines = null;
|
||||
|
||||
DisposeAnalyzer();
|
||||
|
||||
Canvas.RemoveFromVisualTree();
|
||||
Canvas = null;
|
||||
}
|
||||
|
||||
private async void Canvas_CreateResources(CanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
|
||||
{
|
||||
args.TrackAsyncAction(_fluidRenderer.LoadResourcesAsync().AsAsyncAction());
|
||||
var tasks = new Task[]
|
||||
{
|
||||
_fluidRenderer.LoadResourcesAsync(),
|
||||
ReloadCoverBackgroundResourcesAsync()
|
||||
};
|
||||
args.TrackAsyncAction(Task.WhenAll(tasks).AsAsyncAction());
|
||||
|
||||
_snowRenderer.LoadResources();
|
||||
_fogRenderer.LoadResources();
|
||||
|
||||
@@ -587,12 +639,12 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
private void TriggerRelayout()
|
||||
{
|
||||
if (_layoutManager == null || _lyricsData == null || !_isLayoutChanged) return;
|
||||
if (_renderLyricsLines == null || !_isLayoutChanged || _lyricsWindowStatus == null) return;
|
||||
|
||||
_layoutManager.MeasureAndArrange(
|
||||
LyricsLayoutManager.MeasureAndArrange(
|
||||
resourceCreator: Canvas,
|
||||
lyricsData: _lyricsData,
|
||||
status: _liveStatesService.LiveStates.LyricsWindowStatus,
|
||||
lines: _renderLyricsLines,
|
||||
status: _lyricsWindowStatus,
|
||||
appSettings: _settingsService.AppSettings,
|
||||
canvasWidth: Canvas.Size.Width,
|
||||
canvasHeight: Canvas.Size.Height,
|
||||
@@ -607,6 +659,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
_songPosition += elapsedTime;
|
||||
_totalPlayedTime += elapsedTime;
|
||||
_songPositionWithOffset = _songPosition + TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentMediaSourceProviderInfo?.PositionOffset ?? 0);
|
||||
CheckAndScrobbleLastFM();
|
||||
}
|
||||
}
|
||||
@@ -628,26 +681,32 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
private void ResetPlaybackState()
|
||||
{
|
||||
_totalPlayedTime = TimeSpan.Zero;
|
||||
_songPosition = TimeSpan.Zero;
|
||||
_totalPlayedTime = TimeSpan.Zero;
|
||||
_isLastFMTracked = false;
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<AlbumArtThemeColors> message)
|
||||
private void UpdateRenderLyricsLines()
|
||||
{
|
||||
if (message.Sender is IMediaSessionsService)
|
||||
_renderLyricsLines = null;
|
||||
_renderLyricsLines = _mediaSessionsService.CurrentLyricsData?.LyricsLines.Select(x => new RenderLyricsLine()
|
||||
{
|
||||
if (message.PropertyName == nameof(IMediaSessionsService.AlbumArtThemeColors))
|
||||
{
|
||||
var lyricsThemeColors = message.NewValue;
|
||||
_immersiveBgColorTransition.StartTransition(lyricsThemeColors.EnvColor);
|
||||
_accentColor1Transition.StartTransition(lyricsThemeColors.AccentColor1);
|
||||
_accentColor2Transition.StartTransition(lyricsThemeColors.AccentColor2);
|
||||
_accentColor3Transition.StartTransition(lyricsThemeColors.AccentColor3);
|
||||
_accentColor4Transition.StartTransition(lyricsThemeColors.AccentColor4);
|
||||
LyricsSyllables = x.LyricsSyllables,
|
||||
StartMs = x.StartMs,
|
||||
EndMs = x.EndMs,
|
||||
PhoneticText = x.PhoneticText,
|
||||
OriginalText = x.OriginalText,
|
||||
TranslatedText = x.TranslatedText
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
private async Task ReloadCoverBackgroundResourcesAsync()
|
||||
{
|
||||
if (_mediaSessionsService.AlbumArtBitmapStream is IRandomAccessStream stream)
|
||||
{
|
||||
stream.Seek(0);
|
||||
CanvasBitmap bitmap = await CanvasBitmap.LoadAsync(Canvas, stream);
|
||||
_coverRenderer.SetCoverBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,26 +749,26 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
if (message.PropertyName == nameof(IMediaSessionsService.CurrentLyricsData))
|
||||
{
|
||||
_lyricsData = message.NewValue;
|
||||
UpdateRenderLyricsLines();
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<LyricsWindowStatus> message)
|
||||
public void Receive(PropertyChangedMessage<SongInfo?> message)
|
||||
{
|
||||
if (message.Sender is LiveStates)
|
||||
if (message.Sender is IMediaSessionsService)
|
||||
{
|
||||
if (message.PropertyName == nameof(LiveStates.LyricsWindowStatus))
|
||||
if (message.PropertyName == nameof(IMediaSessionsService.CurrentSongInfo))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
ResetPlaybackState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<int> message)
|
||||
{
|
||||
if (message.Sender is LyricsStyleSettings)
|
||||
if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsStyleSettings.PhoneticLyricsFontSize))
|
||||
{
|
||||
@@ -727,8 +786,24 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsStyleSettings.PlayingLineTopOffset))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsStyleSettings.PhoneticLyricsOpacity))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsStyleSettings.OriginalLyricsOpacity))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsStyleSettings.TranslatedLyricsOpacity))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsEffectSettings)
|
||||
else if (message.Sender == LyricsWindowStatus?.LyricsEffectSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollDuration))
|
||||
{
|
||||
@@ -759,7 +834,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
public void Receive(PropertyChangedMessage<double> message)
|
||||
{
|
||||
if (message.Sender is LyricsStyleSettings)
|
||||
if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsLineSpacingFactor))
|
||||
{
|
||||
@@ -770,14 +845,26 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
{
|
||||
if (message.Sender is LyricsEffectSettings)
|
||||
if (message.Sender == LyricsWindowStatus?.LyricsEffectSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsEffectSettings.IsFanLyricsEnabled))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsEffectSettings.IsLyricsBlurEffectEnabled))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsEffectSettings.IsLyricsFadeOutEffectEnabled))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsEffectSettings.IsLyricsOutOfSightEffectEnabled))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsStyleSettings)
|
||||
else if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsStyleSettings.IsDynamicLyricsFontSize))
|
||||
{
|
||||
@@ -788,7 +875,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
public void Receive(PropertyChangedMessage<TextAlignmentType> message)
|
||||
{
|
||||
if (message.Sender is LyricsStyleSettings)
|
||||
if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsAlignmentType))
|
||||
{
|
||||
@@ -797,20 +884,9 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<SongInfo?> message)
|
||||
{
|
||||
if (message.Sender is IMediaSessionsService)
|
||||
{
|
||||
if (message.PropertyName == nameof(IMediaSessionsService.CurrentSongInfo))
|
||||
{
|
||||
ResetPlaybackState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<LyricsFontWeight> message)
|
||||
{
|
||||
if (message.Sender is LyricsStyleSettings)
|
||||
if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontWeight))
|
||||
{
|
||||
@@ -821,7 +897,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
public void Receive(PropertyChangedMessage<string> message)
|
||||
{
|
||||
if (message.Sender is LyricsStyleSettings)
|
||||
if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsCJKFontFamily))
|
||||
{
|
||||
@@ -833,5 +909,16 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<IRandomAccessStream?> message)
|
||||
{
|
||||
if (message.Sender is IMediaSessionsService)
|
||||
{
|
||||
if (message.PropertyName == nameof(IMediaSessionsService.AlbumArtBitmapStream))
|
||||
{
|
||||
_ = ReloadCoverBackgroundResourcesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,20 +23,93 @@
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="Effect" />
|
||||
|
||||
<!-- 辉光效果 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsGlowEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
|
||||
<!-- 模糊效果 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsBlurEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsBlurEffectEnabled, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<!-- 淡出效果 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsFadeOutEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsFadeOutEffectEnabled, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<!-- 远离视野 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsOutOfSightEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsOutOfSightEffectEnabled, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<!-- 辉光效果 -->
|
||||
<dev:SettingsExpander x:Uid="SettingsPageLyricsGlowEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageScope">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsGlowEffectScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageEffectScopeLongDurationSyllable" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEffectLineStartToCurrentChar" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageLongSyllableDuration">
|
||||
<local:ExtendedSlider
|
||||
Default="700"
|
||||
Maximum="1000"
|
||||
Minimum="0"
|
||||
Unit="ms"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsGlowEffectLongSyllableDuration, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectAmountAutoAdjust, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectAmountAutoAdjust, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
|
||||
<local:ExtendedSlider
|
||||
Maximum="16"
|
||||
Minimum="0"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsGlowEffectAmount, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<!-- 缩放效果 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsScaleEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<dev:SettingsExpander x:Uid="SettingsPageLyricsScaleEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsScaleEffectEnabled, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageLongSyllableDuration">
|
||||
<local:ExtendedSlider
|
||||
Default="700"
|
||||
Maximum="1000"
|
||||
Minimum="0"
|
||||
Unit="ms"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsScaleEffectLongSyllableDuration, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsScaleEffectAmountAutoAdjust, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsScaleEffectAmountAutoAdjust, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
|
||||
<local:ExtendedSlider
|
||||
Default="115"
|
||||
Maximum="200"
|
||||
Minimum="100"
|
||||
Unit="%"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsScaleEffectAmount, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<!-- 浮动动画 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsFloatAnimation" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<dev:SettingsExpander x:Uid="SettingsPageLyricsFloatAnimation" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsFloatAnimationEnabled, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsFloatAnimationAmountAutoAdjust, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsFloatAnimationAmountAutoAdjust, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
|
||||
<local:ExtendedSlider
|
||||
Default="4"
|
||||
Maximum="16"
|
||||
Minimum="0"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsFloatAnimationAmount, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<!-- 扇形歌词 -->
|
||||
<dev:SettingsExpander
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0">
|
||||
<ScrollViewer>
|
||||
<ScrollViewer Padding="8,0">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock x:Uid="LyricsSearchControlSongInfoMapping" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
@@ -105,14 +105,6 @@
|
||||
<Run Text="*" />
|
||||
<Run x:Uid="ArtistsSplitHint" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
|
||||
<RichTextBlock
|
||||
FontSize="12"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Loaded="ArtistsSplitHintRichTextBlock_Loaded"
|
||||
TextWrapping="Wrap">
|
||||
<Paragraph>
|
||||
<Run Text="; , / ; 、 ," />
|
||||
</Paragraph>
|
||||
@@ -161,11 +153,14 @@
|
||||
<CheckBox x:Uid="LyricsSearchControlMarkAsPureMusic" IsChecked="{x:Bind ViewModel.MappedSongSearchQuery.IsMarkedAsPureMusic, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="LyricsSearchControlTargetSearchProvider">
|
||||
<Button
|
||||
x:Uid="LyricsSearchControlSearch"
|
||||
Command="{x:Bind ViewModel.SearchCommand}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
<Button
|
||||
x:Uid="LyricsSearchControlSearch"
|
||||
HorizontalAlignment="Stretch"
|
||||
Command="{x:Bind ViewModel.SearchCommand}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="LyricsSearchControlIgnoreCache">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IgnoreCacheWhenSearching, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
@@ -187,10 +182,7 @@
|
||||
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsSearchControlDurauion"
|
||||
Unit="s"
|
||||
Value="{x:Bind Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="LyricsSearchControlDurauion" Value="{x:Bind Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsPageMatchPercentage"
|
||||
Unit="%"
|
||||
@@ -248,8 +240,6 @@
|
||||
<ProgressBar
|
||||
VerticalAlignment="Top"
|
||||
IsIndeterminate="True"
|
||||
ShowError="False"
|
||||
ShowPaused="False"
|
||||
Visibility="{x:Bind ViewModel.IsSearching, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<Grid Grid.Column="2">
|
||||
@@ -282,18 +272,43 @@
|
||||
</Pivot.HeaderTemplate>
|
||||
<Pivot.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:LyricsData">
|
||||
<ListView ItemsSource="{x:Bind LyricsLines, Mode=OneWay}" SelectionChanged="ListView_SelectionChanged">
|
||||
<ListView
|
||||
ItemContainerStyle="{StaticResource ListViewStretchedItemContainerStyle}"
|
||||
ItemsSource="{x:Bind LyricsLines, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:LyricsLine">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{x:Bind StartMs, Mode=OneWay, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
||||
<TextBlock
|
||||
Margin="1,0"
|
||||
Foreground="{ThemeResource SystemFillColorNeutralBrush}"
|
||||
Text="-" />
|
||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{x:Bind EndMs, Mode=OneWay, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
||||
<TextBlock Margin="6,0" Text="{x:Bind OriginalText, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<Grid Margin="0,6" ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0">
|
||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{x:Bind StartMs, Mode=OneWay, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
||||
<Button
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Click="PlayLyricsLineButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=16,
|
||||
Glyph=}"
|
||||
Opacity="0"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<Button.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Button.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerEntered">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerExited">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
</Grid>
|
||||
<local:PropertyRow Grid.Column="1" Value="{x:Bind OriginalText, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
@@ -325,8 +340,8 @@
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
@@ -2,8 +2,6 @@ using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Documents;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
@@ -20,30 +18,10 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
DataContext = Ioc.Default.GetRequiredService<LyricsSearchControlViewModel>();
|
||||
}
|
||||
|
||||
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
private void PlayLyricsLineButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.SelectedLyricsLine = e.OriginalSource as LyricsLine;
|
||||
}
|
||||
|
||||
private void ArtistsSplitHintRichTextBlock_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
if (sender is RichTextBlock richTextBlock)
|
||||
{
|
||||
TextHighlighter highlighter = new()
|
||||
{
|
||||
Background = App.Current.Resources["AccentTextFillColorPrimaryBrush"] as SolidColorBrush,
|
||||
Ranges =
|
||||
{
|
||||
new() { StartIndex = 0, Length = 1 },
|
||||
new() { StartIndex = 5, Length = 1 },
|
||||
new() { StartIndex = 10, Length = 1 },
|
||||
new() { StartIndex = 15, Length = 1 },
|
||||
new() { StartIndex = 20, Length = 1 },
|
||||
new() { StartIndex = 25, Length = 1 },
|
||||
}
|
||||
};
|
||||
richTextBlock.TextHighlighters.Add(highlighter);
|
||||
}
|
||||
var lyricsLine = (LyricsLine)((Button)sender).DataContext;
|
||||
ViewModel.PlayLyricsLine(lyricsLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,26 @@
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsCenterTopOffset" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ExtendedSlider
|
||||
Default="50"
|
||||
Maximum="99"
|
||||
Minimum="1"
|
||||
Unit="%"
|
||||
Value="{x:Bind LyricsStyleSettings.PlayingLineTopOffset, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ExtendedSlider
|
||||
x:Uid="SettingsPageLyricsLineSpacingFactorSlider"
|
||||
Default="0.5"
|
||||
Frequency="0.1"
|
||||
Maximum="2"
|
||||
Minimum="0"
|
||||
Unit="x"
|
||||
Value="{x:Bind LyricsStyleSettings.LyricsLineSpacingFactor, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsExpander x:Uid="SettingsPageLyricsFontFamily" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageCJK">
|
||||
@@ -207,16 +227,35 @@
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ExtendedSlider
|
||||
x:Uid="SettingsPageLyricsLineSpacingFactorSlider"
|
||||
Default="0.5"
|
||||
Frequency="0.1"
|
||||
Maximum="2"
|
||||
Minimum="0"
|
||||
Unit="x"
|
||||
Value="{x:Bind LyricsStyleSettings.LyricsLineSpacingFactor, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<!-- 字体不透明度 -->
|
||||
<dev:SettingsExpander x:Uid="SettingsPageLyricsOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPagePhoneticText">
|
||||
<local:ExtendedSlider
|
||||
Default="60"
|
||||
Frequency="1"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Value="{x:Bind LyricsStyleSettings.PhoneticLyricsOpacity, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageOriginalText">
|
||||
<local:ExtendedSlider
|
||||
Default="30"
|
||||
Frequency="1"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Value="{x:Bind LyricsStyleSettings.OriginalLyricsOpacity, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageTranslatedText">
|
||||
<local:ExtendedSlider
|
||||
Default="60"
|
||||
Frequency="1"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Value="{x:Bind LyricsStyleSettings.TranslatedLyricsOpacity, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
Loaded="UserControl_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
@@ -21,24 +22,7 @@
|
||||
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageRecordedWindowStatus"
|
||||
Grid.Column="0"
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Margin="0,30,0,0"
|
||||
Command="{x:Bind ViewModel.OpenConfigPanelCommand}"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<TextBlock x:Uid="LyricsWindowSettingsControlCurrentLyricsWindowConfig" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<TextBlock x:Uid="SettingsPageRecordedWindowStatus" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="3">
|
||||
|
||||
@@ -54,6 +38,7 @@
|
||||
<MenuFlyoutItem x:Uid="SettingsPageDockedMode" Command="{x:Bind ViewModel.CreateDockedLyricsWindowStatusCommand}" />
|
||||
<MenuFlyoutItem x:Uid="SettingsPageFullscreenMode" Command="{x:Bind ViewModel.CreateFullLyricsWindowStatusCommand}" />
|
||||
<MenuFlyoutItem x:Uid="SettingsPageNarrowMode" Command="{x:Bind ViewModel.CreateNarrowLyricsWindowStatusCommand}" />
|
||||
<MenuFlyoutItem x:Uid="SettingsPageTaskbarMode" Command="{x:Bind ViewModel.CreateTaskbarLyricsWindowStatusCommand}" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
@@ -87,7 +72,7 @@
|
||||
Padding="0,12"
|
||||
CornerRadius="4"
|
||||
ItemsSource="{x:Bind ViewModel.AppSettings.WindowBoundsRecords, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.LiveStates.LyricsWindowStatus, Mode=TwoWay}">
|
||||
SelectionMode="None">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel HorizontalSpacing="0" VerticalSpacing="0" />
|
||||
@@ -101,10 +86,6 @@
|
||||
Spacing="6">
|
||||
<StackPanel.ContextFlyout>
|
||||
<MenuBarItemFlyout>
|
||||
<MenuFlyoutItem
|
||||
x:Uid="LyricsWindowSettingsControlSetDefault"
|
||||
Click="SetDefaultMenuFlyoutItem_Click"
|
||||
IsEnabled="{Binding IsDefault, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" />
|
||||
<MenuFlyoutItem x:Uid="SettingsPageCreateFromCurrent" Click="CopyMenuFlyoutItem_Click" />
|
||||
<MenuFlyoutItem x:Uid="LyricsWindowSettingsControlShare" Click="ShareMenuFlyoutItem_Click" />
|
||||
<MenuFlyoutItem
|
||||
@@ -113,13 +94,50 @@
|
||||
IsEnabled="{Binding IsDefault, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" />
|
||||
</MenuBarItemFlyout>
|
||||
</StackPanel.ContextFlyout>
|
||||
<uc:DemoWindowGrid LyricsWindowStatus="{Binding}" />
|
||||
<Grid>
|
||||
<Border
|
||||
BorderBrush="{ThemeResource AccentAAFillColorDefaultBrush}"
|
||||
BorderThickness="4"
|
||||
CornerRadius="4"
|
||||
Visibility="{Binding IsOpened, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<uc:DemoWindowGrid Margin="4" LyricsWindowStatus="{Binding}" />
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ToggleButton
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="SetDefaultMenuFlyoutItem_Click"
|
||||
IsChecked="{Binding IsDefault, Mode=OneWay}">
|
||||
<TextBlock x:Uid="LyricsWindowSettingsControlSetDefault" />
|
||||
</ToggleButton>
|
||||
</Grid>
|
||||
<Grid ColumnSpacing="4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="ConfigButton_Click">
|
||||
<TextBlock x:Uid="LyricsWindowSettingsControlLyricsWindowConfig" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageMultiNowPlayingWindows">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.MultiNowPlayingWindowMode, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageMusicGalleryLyrics">
|
||||
<Button HorizontalAlignment="Stretch" Click="EmbeddedConfigButton_Click">
|
||||
<TextBlock x:Uid="LyricsWindowSettingsControlLyricsWindowConfig" />
|
||||
</Button>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
@@ -137,8 +155,15 @@
|
||||
<Grid.TranslationTransition>
|
||||
<Vector3Transition />
|
||||
</Grid.TranslationTransition>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Padding="36,0" Style="{StaticResource SettingsGridStyle}">
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Padding="36,0"
|
||||
Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -147,9 +172,9 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="LyricsWindowSettingsControlCurrentLyricsWindowConfig"
|
||||
Grid.Column="0"
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="{x:Bind LyricsWindowStatus.Name, Mode=OneWay}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Margin="0,30,0,0"
|
||||
@@ -160,68 +185,61 @@
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</Grid>
|
||||
|
||||
<Pivot SelectionChanged="Pivot_SelectionChanged">
|
||||
<controls:Segmented
|
||||
x:Name="ConfigSegmented"
|
||||
HorizontalAlignment="Stretch"
|
||||
SelectionChanged="ConfigSegmented_SelectionChanged"
|
||||
Style="{StaticResource PivotSegmentedStyle}">
|
||||
|
||||
<PivotItem Tag="General">
|
||||
<PivotItem.Header>
|
||||
<TextBlock
|
||||
x:Uid="AppSettingsControlGeneral"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}" />
|
||||
</PivotItem.Header>
|
||||
</PivotItem>
|
||||
<controls:SegmentedItem x:Name="WindowSegmentedItem" Tag="Window">
|
||||
<TextBlock
|
||||
x:Uid="AppSettingsControlGeneral"
|
||||
MaxWidth="120"
|
||||
TextWrapping="Wrap" />
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem x:Name="LayoutSegmentedItem" Tag="Layout">
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageLayout"
|
||||
MaxWidth="120"
|
||||
TextWrapping="Wrap" />
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem x:Name="AlbumArtStyleSegmentedItem" Tag="AlbumArtStyle">
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageAlbumStyle"
|
||||
MaxWidth="120"
|
||||
TextWrapping="Wrap" />
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem Tag="AlbumArtEffect">
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageAlbumEffect"
|
||||
MaxWidth="120"
|
||||
TextWrapping="Wrap" />
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem Tag="LyricsStyle">
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageLyricsStyle"
|
||||
MaxWidth="120"
|
||||
TextWrapping="Wrap" />
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem Tag="LyricsEffect">
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageLyricsEffect"
|
||||
MaxWidth="120"
|
||||
TextWrapping="Wrap" />
|
||||
</controls:SegmentedItem>
|
||||
<controls:SegmentedItem Tag="LyricsBackground">
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageBackgroundOverlay"
|
||||
MaxWidth="120"
|
||||
TextWrapping="Wrap" />
|
||||
</controls:SegmentedItem>
|
||||
|
||||
<PivotItem Tag="Layout">
|
||||
<PivotItem.Header>
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageLayout"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}" />
|
||||
</PivotItem.Header>
|
||||
</PivotItem>
|
||||
|
||||
<PivotItem Tag="AlbumArtStyle">
|
||||
<PivotItem.Header>
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageAlbumStyle"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}" />
|
||||
</PivotItem.Header>
|
||||
</PivotItem>
|
||||
|
||||
<PivotItem Tag="LyricsStyle">
|
||||
<PivotItem.Header>
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageLyricsStyle"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}" />
|
||||
</PivotItem.Header>
|
||||
</PivotItem>
|
||||
|
||||
<PivotItem Tag="LyricsEffect">
|
||||
<PivotItem.Header>
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageLyricsEffect"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}" />
|
||||
</PivotItem.Header>
|
||||
</PivotItem>
|
||||
|
||||
<PivotItem Tag="LyricsBackground">
|
||||
<PivotItem.Header>
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageBackgroundOverlay"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}" />
|
||||
</PivotItem.Header>
|
||||
</PivotItem>
|
||||
|
||||
</Pivot>
|
||||
</controls:Segmented>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<controls:SwitchPresenter Margin="0,110,0,0" Value="{x:Bind ViewModel.ListViewSelectedItemTag, Mode=OneWay}">
|
||||
<controls:SwitchPresenter Grid.Row="1" Value="{x:Bind ViewModel.SelectorBarSelectedItemTag, Mode=OneWay}">
|
||||
<controls:SwitchPresenter.ContentTransitions>
|
||||
<TransitionCollection>
|
||||
<PopupThemeTransition />
|
||||
@@ -229,155 +247,8 @@
|
||||
</controls:SwitchPresenter.ContentTransitions>
|
||||
|
||||
<!-- Window -->
|
||||
<controls:Case Value="General">
|
||||
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel
|
||||
Margin="0,6,0,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<TextBox Text="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.Name, Mode=TwoWay}" TextWrapping="Wrap" />
|
||||
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageWorkArea"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageWorkAreaHeight" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<uc:ExtendedSlider
|
||||
Default="64"
|
||||
Maximum="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.MonitorBounds.Height, Mode=OneWay}"
|
||||
Minimum="64"
|
||||
Unit="px"
|
||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.DockHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDockPlacement" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDockMonitor" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.MonitorDeviceName, Mode=TwoWay}" />
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.RefreshMonitorDeviceNamesCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAdaptEnvColor"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard
|
||||
x:Uid="SettingsPageEnvColorSample"
|
||||
Header="Environment color sample mode"
|
||||
IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.EnvironmentSampleMode, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleBelow" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleAbove" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleInner" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleEdge" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageWindowBounds"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard Header="X">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowX, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard Header="Y">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowY, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageWidth">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowWidth, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageHeight">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAOT"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTop, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageForceAlwaysOnTop" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTop, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTopPolling, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageShowInSwitchers" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsShownInSwitchers, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageClickThrough" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsClickThrough, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageBorderless" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsBorderless, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDragArea" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.TitleBarArea, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaNone" />
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaTop" />
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaWhole" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
<controls:Case Value="Window">
|
||||
<uc:WindowSettingsControl LyricsWindowStatus="{x:Bind LyricsWindowStatus, Mode=OneWay}" />
|
||||
</controls:Case>
|
||||
|
||||
<!-- Layout -->
|
||||
@@ -386,17 +257,17 @@
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<TextBlock x:Uid="SettingsPageLayout" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsExpander x:Uid="SettingsPageDisplayTypeSwitcher" IsExpanded="True">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
|
||||
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
|
||||
<ComboBoxItem x:Uid="MainPageSplitView" />
|
||||
</ComboBox>
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageLayoutOrientation">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsLayoutOrientation, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.LyricsLayoutOrientation, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLayoutOrientationHorizontal" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLayoutOrientationVertical" />
|
||||
</ComboBox>
|
||||
@@ -411,22 +282,27 @@
|
||||
|
||||
<!-- Album art area style -->
|
||||
<controls:Case Value="AlbumArtStyle">
|
||||
<uc:AlbumArtLayoutSettingsControl AlbumArtLayoutSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings, Mode=OneWay}" />
|
||||
<uc:AlbumArtAreaStyleSettingsControl AlbumArtLayoutSettings="{x:Bind LyricsWindowStatus.AlbumArtLayoutSettings, Mode=OneWay}" />
|
||||
</controls:Case>
|
||||
|
||||
<!-- Album art area effect -->
|
||||
<controls:Case Value="AlbumArtEffect">
|
||||
<uc:AlbumArtAreaEffectSettingsControl AlbumArtAreaEffectSettings="{x:Bind LyricsWindowStatus.AlbumArtAreaEffectSettings, Mode=OneWay}" />
|
||||
</controls:Case>
|
||||
|
||||
<!-- Lyrics style -->
|
||||
<controls:Case Value="LyricsStyle">
|
||||
<uc:LyricsStyleSettingsControl LyricsStyleSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsStyleSettings, Mode=OneWay}" />
|
||||
<uc:LyricsStyleSettingsControl LyricsStyleSettings="{x:Bind LyricsWindowStatus.LyricsStyleSettings, Mode=OneWay}" />
|
||||
</controls:Case>
|
||||
|
||||
<!-- Lyrics effect -->
|
||||
<controls:Case Value="LyricsEffect">
|
||||
<uc:LyricsEffectSettingsControl LyricsEffectSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsEffectSettings, Mode=OneWay}" />
|
||||
<uc:LyricsEffectSettingsControl LyricsEffectSettings="{x:Bind LyricsWindowStatus.LyricsEffectSettings, Mode=OneWay}" />
|
||||
</controls:Case>
|
||||
|
||||
<!-- Lyrics background -->
|
||||
<controls:Case Value="LyricsBackground">
|
||||
<uc:LyricsBackgroundSettingsControl LyricsBackgroundSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings, Mode=OneWay}" />
|
||||
<uc:LyricsBackgroundSettingsControl LyricsBackgroundSettings="{x:Bind LyricsWindowStatus.LyricsBackgroundSettings, Mode=OneWay}" />
|
||||
</controls:Case>
|
||||
|
||||
</controls:SwitchPresenter>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Serialization;
|
||||
using BetterLyrics.WinUI3.Services.LiveStatesService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using NTextCat.Commons;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -25,7 +25,15 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
public LyricsWindowSettingsControlViewModel ViewModel => (LyricsWindowSettingsControlViewModel)DataContext;
|
||||
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||
|
||||
public LyricsWindowStatus? LyricsWindowStatus
|
||||
{
|
||||
get { return (LyricsWindowStatus?)GetValue(LyricsWindowStatusProperty); }
|
||||
set { SetValue(LyricsWindowStatusProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsWindowStatusProperty =
|
||||
DependencyProperty.Register(nameof(LyricsWindowStatus), typeof(LyricsWindowStatus), typeof(LyricsWindowSettingsControl), new PropertyMetadata(null));
|
||||
|
||||
public LyricsWindowSettingsControl()
|
||||
{
|
||||
@@ -39,10 +47,9 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
if (menuFlyoutItem.DataContext is LyricsWindowStatus data)
|
||||
{
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus == data)
|
||||
{
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus = ViewModel.AppSettings.WindowBoundsRecords.First();
|
||||
}
|
||||
var windows = WindowHook.GetWindows<NowPlayingWindow>();
|
||||
var window = windows.FirstOrDefault(x => x.LyricsWindowStatus == data);
|
||||
window?.CloseWindow();
|
||||
ViewModel.AppSettings.WindowBoundsRecords.Remove(data);
|
||||
}
|
||||
}
|
||||
@@ -50,12 +57,11 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
private void SetDefaultMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is MenuFlyoutItem menuFlyoutItem)
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
if (menuFlyoutItem.DataContext is LyricsWindowStatus data)
|
||||
if (element.DataContext is LyricsWindowStatus data)
|
||||
{
|
||||
ViewModel.AppSettings.WindowBoundsRecords.ForEach(x => x.IsDefault = false);
|
||||
data.IsDefault = true;
|
||||
data.IsDefault = !data.IsDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,17 +121,6 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is Pivot pivot)
|
||||
{
|
||||
if (pivot.SelectedItem is PivotItem pivotItem)
|
||||
{
|
||||
ViewModel?.ListViewSelectedItemTag = pivotItem.Tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void ImportButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string[] fileTypeFilter = [".json"];
|
||||
@@ -154,5 +149,31 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
ViewModel.DisplayPanelHeight = e.NewSize.Height;
|
||||
}
|
||||
|
||||
private void ConfigButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowSegmentedItem.IsEnabled = LayoutSegmentedItem.IsEnabled = true;
|
||||
ConfigSegmented.SelectedItem = WindowSegmentedItem;
|
||||
LyricsWindowStatus = (LyricsWindowStatus)((Button)sender).DataContext;
|
||||
ViewModel.OpenConfigPanel();
|
||||
}
|
||||
|
||||
private void EmbeddedConfigButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowSegmentedItem.IsEnabled = LayoutSegmentedItem.IsEnabled = false;
|
||||
ConfigSegmented.SelectedItem = AlbumArtStyleSegmentedItem;
|
||||
LyricsWindowStatus = _settingsService.AppSettings.MusicGallerySettings.LyricsWindowStatus;
|
||||
ViewModel.OpenConfigPanel();
|
||||
}
|
||||
|
||||
private void ConfigSegmented_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
ViewModel.SelectorBarSelectedItemTag = (string)((SegmentedItem)((Segmented)sender).SelectedItem).Tag;
|
||||
}
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.CloseConfigPanelCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,9 @@
|
||||
|
||||
<ListView
|
||||
Margin="48,56"
|
||||
ItemContainerStyle="{StaticResource ListViewStretchedItemContainerStyle}"
|
||||
ItemsSource="{x:Bind ViewModel.AppSettings.WindowBoundsRecords, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.LiveStates.LyricsWindowStatus, Mode=TwoWay}">
|
||||
SelectionMode="None">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel HorizontalSpacing="0" VerticalSpacing="0" />
|
||||
@@ -54,12 +55,14 @@
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid
|
||||
Margin="0,10"
|
||||
Padding="5"
|
||||
AllowFocusOnInteraction="True"
|
||||
Margin="16"
|
||||
CornerRadius="4"
|
||||
Tapped="Grid_Tapped">
|
||||
<uc:DemoWindowGrid LyricsWindowStatus="{Binding}" />
|
||||
<Border
|
||||
BorderBrush="{ThemeResource AccentAAFillColorDefaultBrush}"
|
||||
BorderThickness="4"
|
||||
Visibility="{Binding IsOpened, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<uc:DemoWindowGrid Margin="4" LyricsWindowStatus="{Binding}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
@@ -16,6 +17,8 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public LyricsWindowSwitchControlViewModel ViewModel => (LyricsWindowSwitchControlViewModel)DataContext;
|
||||
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
public LyricsWindowSwitchControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -37,7 +40,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
var lyricsWindowSwitchWindow = WindowHook.GetWindow<LyricsWindowSwitchWindow>();
|
||||
lyricsWindowSwitchWindow?.ViewModel.RootGridOpacity = 0;
|
||||
await Task.Delay(300);
|
||||
WindowHook.HideWindow<LyricsWindowSwitchWindow>();
|
||||
lyricsWindowSwitchWindow?.HideWindow();
|
||||
}
|
||||
|
||||
private void ShadowRect_Loaded(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.MediaSettingsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
@@ -9,6 +8,7 @@
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -49,42 +49,131 @@
|
||||
ItemsSource="{x:Bind ViewModel.AppSettings.LocalMediaFolders, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<dev:SettingsExpander>
|
||||
<DataTemplate x:DataType="models:MediaFolder">
|
||||
<dev:SettingsExpander IsExpanded="True">
|
||||
|
||||
<dev:SettingsExpander.HeaderIcon>
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="{x:Bind SourceType, Converter={StaticResource FileSourceTypeToIconConverter}, Mode=OneWay}" />
|
||||
</dev:SettingsExpander.HeaderIcon>
|
||||
<dev:SettingsExpander.Header>
|
||||
<HyperlinkButton
|
||||
Click="LocalFolderHyperlinkButton_Click"
|
||||
Content="{Binding Path, Mode=OneWay}"
|
||||
Tag="{Binding Path, Mode=OneWay}" />
|
||||
<TextBlock IsTextSelectionEnabled="True" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
</dev:SettingsExpander.Header>
|
||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Description>
|
||||
<TextBlock IsTextSelectionEnabled="True" Text="{x:Bind ConnectionSummary, Mode=OneWay}" />
|
||||
</dev:SettingsExpander.Description>
|
||||
|
||||
<ToggleSwitch IsOn="{x:Bind IsEnabled, Mode=TwoWay}" />
|
||||
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard>
|
||||
<dev:SettingsCard.Header>
|
||||
<HyperlinkButton
|
||||
x:Uid="SettingsPageRemovePath"
|
||||
Click="SettingsPageRemovePathButton_Click"
|
||||
Tag="{Binding}" />
|
||||
</dev:SettingsCard.Header>
|
||||
<dev:SettingsCard x:Uid="MediaSettingsControlNameSetting">
|
||||
<TextBox VerticalAlignment="Center" Text="{x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageMusicLibRealTimeWatch">
|
||||
<ToggleSwitch IsOn="{Binding IsRealTimeWatchEnabled, Mode=TwoWay}" />
|
||||
<dev:SettingsCard x:Uid="MediaSettingsControlLastSyncTime" Description="{x:Bind LastSyncTime.ToString(), Mode=OneWay, TargetNullValue=N/A}">
|
||||
<Button
|
||||
x:Uid="MediaSettingsControlSyncNow"
|
||||
Click="SyncNowButton_Click"
|
||||
IsEnabled="{x:Bind IsEnabled, Mode=OneWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="MusicSettingsControlAutoSyncInterval">
|
||||
<ComboBox IsEnabled="{x:Bind IsEnabled, Mode=OneWay}" SelectedIndex="{x:Bind ScanInterval, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="MusicSettingsControlAutoSyncIntervalDisabled" />
|
||||
<ComboBoxItem x:Uid="MusicSettingsControlAutoSyncIntervalEveryFifteenMin" />
|
||||
<ComboBoxItem x:Uid="MusicSettingsControlAutoSyncIntervalEveryHour" />
|
||||
<ComboBoxItem x:Uid="MusicSettingsControlAutoSyncIntervalEverySixHrs" />
|
||||
<ComboBoxItem x:Uid="MusicSettingsControlAutoSyncIntervalEveryDay" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard>
|
||||
<Button x:Uid="SettingsPageRemovePath" Click="SettingsPageRemovePathButton_Click" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
|
||||
<dev:SettingsExpander.ItemsFooter>
|
||||
<StackPanel>
|
||||
<!-- Index info -->
|
||||
<InfoBar
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind IsIndexing, Mode=OneWay}"
|
||||
Message="{x:Bind IndexingStatusText, Mode=OneWay}" />
|
||||
<ProgressBar Visibility="{x:Bind IsIndexing, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" Value="{x:Bind IndexingProgress, Mode=OneWay}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind IndexingProgress, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="IsIndeterminate" Value="True" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind IndexingProgress, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="IsIndeterminate" Value="False" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ProgressBar>
|
||||
<!-- Clean up info -->
|
||||
<InfoBar
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind IsCleaningUp, Mode=OneWay}"
|
||||
Message="{x:Bind CleaningUpStatusText, Mode=OneWay}" />
|
||||
<ProgressBar IsIndeterminate="True" Visibility="{x:Bind IsCleaningUp, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</StackPanel>
|
||||
</dev:SettingsExpander.ItemsFooter>
|
||||
|
||||
</dev:SettingsExpander>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageAddFolder" Style="{StaticResource DefaultSettingsExpanderItemStyle}">
|
||||
<Button
|
||||
x:Uid="SettingsPageAddFolderButton"
|
||||
Command="{x:Bind ViewModel.SelectAndAddFolderCommand}"
|
||||
CommandParameter="{Binding ElementName=RootGrid}" />
|
||||
<dev:SettingsCard Style="{StaticResource DefaultSettingsExpanderItemStyle}">
|
||||
<DropDownButton x:Uid="SettingsPageAddFolderButton">
|
||||
<DropDownButton.Flyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem
|
||||
x:Uid="SettingsPageLocalFolder"
|
||||
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||
CommandParameter="Local">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
|
||||
<MenuFlyoutSeparator />
|
||||
|
||||
<MenuFlyoutItem
|
||||
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||
CommandParameter="SMB"
|
||||
Text="SMB">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
|
||||
<MenuFlyoutItem
|
||||
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||
CommandParameter="FTP"
|
||||
Text="FTP">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
|
||||
<MenuFlyoutItem
|
||||
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||
CommandParameter="WebDAV"
|
||||
Text="WebDAV">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
|
||||
</MenuFlyout>
|
||||
</DropDownButton.Flyout>
|
||||
</DropDownButton>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -4,6 +4,7 @@ using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.System;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
@@ -22,18 +23,14 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
private void SettingsPageRemovePathButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.RemoveFolderAsync((LocalMediaFolder)(sender as HyperlinkButton)!.Tag);
|
||||
var folder = (MediaFolder)((FrameworkElement)sender).DataContext;
|
||||
ViewModel.RemoveFolder(folder);
|
||||
}
|
||||
|
||||
private async void LocalFolderHyperlinkButton_Click(object sender, RoutedEventArgs e)
|
||||
private void SyncNowButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is HyperlinkButton button && button.Tag is string uriStr)
|
||||
{
|
||||
if (Uri.TryCreate(uriStr, UriKind.Absolute, out var uri))
|
||||
{
|
||||
await Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
}
|
||||
var folder = (MediaFolder)((FrameworkElement)sender).DataContext;
|
||||
ViewModel.SyncFolder(folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.NowPlayingBar"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="RootGrid">
|
||||
|
||||
<Grid
|
||||
x:Name="BottomCommandGrid"
|
||||
Background="{ThemeResource LayerOnMicaBaseAltFillColorDefaultBrush}"
|
||||
Opacity="{x:Bind ViewModel.BottomCommandGridOpacity, Mode=OneWay}"
|
||||
PointerEntered="BottomCommandGrid_PointerEntered"
|
||||
PointerExited="BottomCommandGrid_PointerExited">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<Grid x:Name="BottomCommandContent">
|
||||
|
||||
<Grid HorizontalAlignment="Left">
|
||||
<StackPanel
|
||||
x:Name="BottomLeftCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
|
||||
<StackPanel
|
||||
x:Name="SongInfoStackPanel"
|
||||
Margin="8"
|
||||
Padding="8"
|
||||
VerticalAlignment="Center"
|
||||
CornerRadius="4"
|
||||
Opacity="{x:Bind ShowSongInfo, Mode=OneWay, Converter={StaticResource BoolToOpacityConverter}}"
|
||||
Orientation="Horizontal"
|
||||
Spacing="12"
|
||||
Tapped="SongInfoStackPanel_Tapped"
|
||||
Visibility="{x:Bind ShowSongInfo, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerEntered">
|
||||
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerExited">
|
||||
<interactivity:ChangePropertyAction PropertyName="Background" Value="Transparent" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerPressed">
|
||||
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorTertiaryBrush}" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerReleased">
|
||||
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
<Grid VerticalAlignment="Center" CornerRadius="4">
|
||||
<local:ImageSwitcher
|
||||
x:Name="AlbumArtImageSwitcher"
|
||||
Width="36"
|
||||
Height="36" />
|
||||
</Grid>
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock x:Name="TitleTextBlock" />
|
||||
<TextBlock
|
||||
x:Name="ArtistsTextBlock"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
x:Name="TimeStackPanel"
|
||||
Margin="8"
|
||||
Padding="8"
|
||||
VerticalAlignment="Center"
|
||||
Opacity="{x:Bind ShowTime, Mode=OneWay, Converter={StaticResource BoolToOpacityConverter}}"
|
||||
Orientation="Horizontal"
|
||||
Spacing="2"
|
||||
Tapped="TimeStackPanel_Tapped"
|
||||
Visibility="{x:Bind ShowTime, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerEntered">
|
||||
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerExited">
|
||||
<interactivity:ChangePropertyAction PropertyName="Background" Value="Transparent" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerPressed">
|
||||
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorTertiaryBrush}" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerReleased">
|
||||
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{Binding ElementName=TimelineSlider, Path=Value, Converter={StaticResource SecondsToFormattedTimeConverter}}" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="/" />
|
||||
<TextBlock Text="{Binding ElementName=TimelineSlider, Path=Maximum, Converter={StaticResource SecondsToFormattedTimeConverter}}" />
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid HorizontalAlignment="Center">
|
||||
<StackPanel
|
||||
x:Name="BottomCenterCommandStackPanel"
|
||||
Padding="16"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PreviousSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PauseSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PlaySongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.NextSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid HorizontalAlignment="Right">
|
||||
<StackPanel
|
||||
x:Name="BottomRightCommandStackPanel"
|
||||
Padding="16"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
|
||||
<!-- Volume -->
|
||||
<Button Click="VolumeButton_Click" Style="{StaticResource GhostButtonStyle}">
|
||||
<Grid>
|
||||
|
||||
<!-- Volumn: 0 -->
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</FontIcon>
|
||||
|
||||
<!-- Volumn: 1-32 -->
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel1"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
ComparisonCondition="GreaterThanOrEqual"
|
||||
Value="1">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
ComparisonCondition="LessThan"
|
||||
Value="1">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</FontIcon>
|
||||
|
||||
<!-- Volumn: 33-65 -->
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel2"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
ComparisonCondition="GreaterThanOrEqual"
|
||||
Value="33">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
ComparisonCondition="LessThan"
|
||||
Value="33">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</FontIcon>
|
||||
|
||||
<!-- Volumn: 66-100 -->
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel3"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
ComparisonCondition="GreaterThanOrEqual"
|
||||
Value="66">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
ComparisonCondition="LessThan"
|
||||
Value="66">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</FontIcon>
|
||||
</Grid>
|
||||
|
||||
<Button.ContextFlyout>
|
||||
<Flyout x:Name="VolumeFlyout" ShouldConstrainToRootBounds="False">
|
||||
<local:ExtendedSlider
|
||||
Frequency="10"
|
||||
IsSliderEnabled="False"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
ResetButtonVisibility="Collapsed"
|
||||
Unit="%"
|
||||
ValueChangedByUser="ExtendedSlider_ValueChangedByUser"
|
||||
Value="{x:Bind ViewModel.Volume, Mode=TwoWay}" />
|
||||
</Flyout>
|
||||
</Button.ContextFlyout>
|
||||
</Button>
|
||||
|
||||
<!-- More -->
|
||||
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" Style="{StaticResource GhostButtonStyle}">
|
||||
<Button.Flyout>
|
||||
<MenuFlyout>
|
||||
<!-- Lyrics search window -->
|
||||
<MenuFlyoutItem
|
||||
x:Uid="LyricsPageLyricsSearch"
|
||||
Command="{x:Bind ViewModel.OpenLyricsSearchWindowCommand}"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}" />
|
||||
<!-- Lyrics window manager shortcut settings -->
|
||||
<MenuFlyoutItem
|
||||
x:Uid="LyricsPageLyricsSettings"
|
||||
Click="LyricsSettingsShortcutMenuFlyoutItem_Click"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}">
|
||||
<MenuFlyoutItem.ContextFlyout>
|
||||
<Flyout
|
||||
x:Name="LyricsSettingsFlyout"
|
||||
Closed="LyricsSettingsFlyout_Closed"
|
||||
FlyoutPresenterStyle="{StaticResource FlyoutPageStyle}"
|
||||
Placement="Right"
|
||||
ShouldConstrainToRootBounds="False" />
|
||||
</MenuFlyoutItem.ContextFlyout>
|
||||
</MenuFlyoutItem>
|
||||
<!-- Playback shortcut settings -->
|
||||
<MenuFlyoutItem
|
||||
x:Uid="LyricsPagePlaybackSource"
|
||||
Click="PlaybackSettingsShortcutMenuFlyoutItem_Click"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}">
|
||||
<MenuFlyoutItem.ContextFlyout>
|
||||
<Flyout
|
||||
x:Name="PlaybackSettingsFlyout"
|
||||
Closed="PlaybackSettingsFlyout_Closed"
|
||||
FlyoutPresenterStyle="{StaticResource FlyoutPageStyle}"
|
||||
Placement="Right"
|
||||
ShouldConstrainToRootBounds="False" />
|
||||
</MenuFlyoutItem.ContextFlyout>
|
||||
</MenuFlyoutItem>
|
||||
<!-- Settings -->
|
||||
<MenuFlyoutItem
|
||||
x:Uid="LyricsPageSettings"
|
||||
Command="{x:Bind ViewModel.OpenSettingsWindowCommand}"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Slider
|
||||
x:Name="TimelineSlider"
|
||||
Margin="0,-14,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Maximum="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DurationMs, Mode=OneWay, Converter={StaticResource MillisecondsToSecondsConverter}}"
|
||||
Minimum="0"
|
||||
Style="{StaticResource GhostSliderStyle}"
|
||||
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}" />
|
||||
|
||||
<Grid
|
||||
x:Name="TimelineSliderLyricsLineInfo"
|
||||
Margin="0,-32,0,0"
|
||||
Padding="8,4"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Background="{ThemeResource LayerOnMicaBaseAltFillColorDefaultBrush}"
|
||||
CornerRadius="6"
|
||||
Opacity="{x:Bind ViewModel.TimelineSliderThumbOpacity, Mode=OneWay}">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
<Grid.TranslationTransition>
|
||||
<Vector3Transition />
|
||||
</Grid.TranslationTransition>
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<TextBlock
|
||||
Margin="0,0,0,2"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.StartMs, Converter={StaticResource MillisecondsToFormattedTimeConverter}, Mode=OneWay}" />
|
||||
<!-- TODO 原文翻译共同显示 -->
|
||||
<TextBlock Margin="0,0,0,2" Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.OriginalText, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid
|
||||
Height="32"
|
||||
Margin="0,-12,0,0"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
PointerEntered="TimelineSliderOverlay_PointerEntered"
|
||||
PointerExited="TimelineSliderOverlay_PointerExited"
|
||||
PointerMoved="TimelineSliderOverlay_PointerMoved"
|
||||
PointerPressed="TimelineSliderOverlay_PointerPressed" />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- Bottom command flyout trigger -->
|
||||
<Grid
|
||||
x:Name="BottomCommandFlyoutTrigger"
|
||||
Height="12"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="Transparent"
|
||||
CornerRadius="3,3,0,0"
|
||||
Opacity="{x:Bind ViewModel.BottomCommandFlyoutTriggerOpacity, Mode=OneWay}"
|
||||
PointerEntered="BottomCommandFlyoutTrigger_PointerEntered"
|
||||
PointerExited="BottomCommandFlyoutTrigger_PointerExited"
|
||||
Tapped="BottomCommandFlyoutTrigger_Tapped">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
<Grid
|
||||
x:Name="BottomCommandFlyoutTriggerHint"
|
||||
Width="150"
|
||||
Margin="4"
|
||||
Background="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
CornerRadius="2"
|
||||
Translation="0,0,0">
|
||||
<Grid.TranslationTransition>
|
||||
<Vector3Transition />
|
||||
</Grid.TranslationTransition>
|
||||
</Grid>
|
||||
<Grid.ContextFlyout>
|
||||
<Flyout x:Name="BottomCommandFlyout" ShouldConstrainToRootBounds="False">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style TargetType="FlyoutPresenter">
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="MinWidth" Value="400" />
|
||||
<Setter Property="MinHeight" Value="100" />
|
||||
<Setter Property="CornerRadius" Value="12" />
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<Grid x:Name="BottomCommandFlyoutContainer" VerticalAlignment="Bottom" />
|
||||
</Flyout>
|
||||
</Grid.ContextFlyout>
|
||||
</Grid>
|
||||
|
||||
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,298 @@
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls;
|
||||
|
||||
public sealed partial class NowPlayingBar : UserControl,
|
||||
IRecipient<PropertyChangedMessage<SongInfo?>>,
|
||||
IRecipient<PropertyChangedMessage<BitmapImage?>>,
|
||||
IRecipient<PropertyChangedMessage<TimeSpan>>
|
||||
{
|
||||
public NowPlayingBarViewModel ViewModel => (NowPlayingBarViewModel)DataContext;
|
||||
|
||||
public event EventHandler? SongInfoTapped;
|
||||
public event EventHandler? TimeTapped;
|
||||
|
||||
public bool ShowTime
|
||||
{
|
||||
get { return (bool)GetValue(ShowTimeProperty); }
|
||||
set { SetValue(ShowTimeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ShowTimeProperty =
|
||||
DependencyProperty.Register(nameof(ShowTime), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false));
|
||||
|
||||
public bool ShowSongInfo
|
||||
{
|
||||
get { return (bool)GetValue(ShowSongInfoProperty); }
|
||||
set { SetValue(ShowSongInfoProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ShowSongInfoProperty =
|
||||
DependencyProperty.Register(nameof(ShowSongInfo), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false));
|
||||
|
||||
public bool IsCompactMode
|
||||
{
|
||||
get { return (bool)GetValue(IsCompactModeProperty); }
|
||||
set { SetValue(IsCompactModeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsCompactModeProperty =
|
||||
DependencyProperty.Register(nameof(IsCompactMode), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false, OnDependencyPropertyChanged));
|
||||
|
||||
public bool IsAutoHideEnabled
|
||||
{
|
||||
get { return (bool)GetValue(IsAutoHideEnabledProperty); }
|
||||
set { SetValue(IsAutoHideEnabledProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsAutoHideEnabledProperty =
|
||||
DependencyProperty.Register(nameof(IsAutoHideEnabled), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false, OnDependencyPropertyChanged));
|
||||
|
||||
private bool _isPointerInBottomCommandGrid = false;
|
||||
|
||||
public NowPlayingBar()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = Ioc.Default.GetRequiredService<NowPlayingBarViewModel>();
|
||||
|
||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||
}
|
||||
|
||||
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is NowPlayingBar self)
|
||||
{
|
||||
if (e.Property == IsCompactModeProperty)
|
||||
{
|
||||
self.OnIsCompactModeChanged();
|
||||
}
|
||||
else if (e.Property == IsAutoHideEnabledProperty)
|
||||
{
|
||||
self.OnIsAutoHideEnabledChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnIsAutoHideEnabledChanged()
|
||||
{
|
||||
if (IsAutoHideEnabled)
|
||||
{
|
||||
if (!_isPointerInBottomCommandGrid)
|
||||
{
|
||||
ViewModel.BottomCommandGridOpacity = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewModel.BottomCommandGridOpacity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnIsCompactModeChanged()
|
||||
{
|
||||
if (IsCompactMode)
|
||||
{
|
||||
if (BottomCommandGrid.Children.Count != 0)
|
||||
{
|
||||
BottomCommandGrid.Children.Remove(BottomCommandContent);
|
||||
BottomCommandFlyoutContainer.Children.Add(BottomCommandContent);
|
||||
}
|
||||
BottomCommandFlyoutTriggerHint.Translation = new Vector3(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BottomCommandFlyoutContainer.Children.Count != 0)
|
||||
{
|
||||
BottomCommandFlyout.Hide();
|
||||
BottomCommandFlyoutContainer.Children.Remove(BottomCommandContent);
|
||||
BottomCommandGrid.Children.Add(BottomCommandContent);
|
||||
}
|
||||
BottomCommandFlyoutTriggerHint.Translation = new Vector3(0, 12, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaybackSettingsShortcutMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
PlaybackSettingsFlyout.Content = new PlaybackSettingsControl
|
||||
{
|
||||
MaxHeight = 500,
|
||||
MaxWidth = 850,
|
||||
};
|
||||
PlaybackSettingsFlyout.ShowAt(BottomRightCommandStackPanel);
|
||||
}
|
||||
|
||||
private void VolumeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
VolumeFlyout.ShowAt(BottomRightCommandStackPanel);
|
||||
}
|
||||
|
||||
private void PlaybackSettingsFlyout_Closed(object sender, object e)
|
||||
{
|
||||
PlaybackSettingsFlyout.Content = null;
|
||||
}
|
||||
|
||||
private void LyricsSettingsFlyout_Closed(object sender, object e)
|
||||
{
|
||||
LyricsSettingsFlyout.Content = null;
|
||||
}
|
||||
|
||||
private void LyricsSettingsShortcutMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LyricsSettingsFlyout.Content = new LyricsWindowSettingsControl
|
||||
{
|
||||
MaxHeight = 500,
|
||||
MaxWidth = 850,
|
||||
};
|
||||
LyricsSettingsFlyout.ShowAt(BottomRightCommandStackPanel);
|
||||
}
|
||||
|
||||
private void TimelineSliderOverlay_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
var grid = (Grid)sender;
|
||||
var pos = e.GetCurrentPoint(grid).Position;
|
||||
var ratio = pos.X / grid.ActualWidth;
|
||||
ViewModel.MediaSessionsService.ChangePosition(TimelineSlider.Maximum * ratio);
|
||||
}
|
||||
|
||||
private void TimelineSliderOverlay_PointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
float targetX;
|
||||
var grid = (Grid)sender;
|
||||
var pos = e.GetCurrentPoint(grid).Position;
|
||||
var ratio = pos.X / grid.ActualWidth;
|
||||
ViewModel.TimelineSliderThumbSeconds = TimelineSlider.Maximum * ratio;
|
||||
if (pos.X + TimelineSliderLyricsLineInfo.ActualWidth > grid.ActualWidth)
|
||||
{
|
||||
targetX = (float)(grid.ActualWidth - TimelineSliderLyricsLineInfo.ActualWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetX = (float)pos.X;
|
||||
}
|
||||
TimelineSliderLyricsLineInfo.Translation = new Vector3(targetX, 0, 0);
|
||||
}
|
||||
|
||||
private void TimelineSliderOverlay_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
ViewModel.TimelineSliderThumbOpacity = 1f;
|
||||
}
|
||||
|
||||
private void TimelineSliderOverlay_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
ViewModel.TimelineSliderThumbOpacity = 0f;
|
||||
}
|
||||
|
||||
private void ExtendedSlider_ValueChangedByUser(object sender, Events.ExtendedSliderValueChangedByUserEventArgs e)
|
||||
{
|
||||
SystemVolumeHook.MasterVolume = ViewModel.Volume;
|
||||
}
|
||||
|
||||
private void LyricsSearchShortcutButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<LyricsSearchWindow>();
|
||||
}
|
||||
|
||||
private void SongInfoStackPanel_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
SongInfoTapped?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void TimeStackPanel_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
TimeTapped?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
_isPointerInBottomCommandGrid = true;
|
||||
if (IsAutoHideEnabled && BottomCommandGrid.Children.Count != 0)
|
||||
{
|
||||
ViewModel.BottomCommandGridOpacity = 1f;
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void BottomCommandGrid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
_isPointerInBottomCommandGrid = false;
|
||||
if (IsAutoHideEnabled && BottomCommandGrid.Children.Count != 0)
|
||||
{
|
||||
ViewModel.BottomCommandGridOpacity = 0f;
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void BottomCommandFlyoutTrigger_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
if (BottomCommandFlyoutContainer.Children.Count != 0)
|
||||
{
|
||||
ViewModel.BottomCommandFlyoutTriggerOpacity = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
private void BottomCommandFlyoutTrigger_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
if (BottomCommandFlyoutContainer.Children.Count != 0)
|
||||
{
|
||||
ViewModel.BottomCommandFlyoutTriggerOpacity = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void BottomCommandFlyoutTrigger_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
|
||||
{
|
||||
if (BottomCommandFlyoutContainer.Children.Count != 0)
|
||||
{
|
||||
BottomCommandFlyout.ShowAt(BottomCommandFlyoutTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<SongInfo?> message)
|
||||
{
|
||||
if (message.Sender is IMediaSessionsService)
|
||||
{
|
||||
if (message.PropertyName == nameof(IMediaSessionsService.CurrentSongInfo))
|
||||
{
|
||||
TitleTextBlock.Text = message.NewValue?.Title;
|
||||
ArtistsTextBlock.Text = message.NewValue?.DisplayArtists;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Receive(PropertyChangedMessage<BitmapImage?> message)
|
||||
{
|
||||
if (message.Sender is IMediaSessionsService)
|
||||
{
|
||||
if (message.PropertyName == nameof(IMediaSessionsService.AlbumArtBitmapImage))
|
||||
{
|
||||
AlbumArtImageSwitcher.Source = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<TimeSpan> message)
|
||||
{
|
||||
if (message.Sender is IMediaSessionsService)
|
||||
{
|
||||
if (message.PropertyName == nameof(IMediaSessionsService.CurrentPosition))
|
||||
{
|
||||
TimelineSlider.Value = message.NewValue.TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,8 +22,9 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0" RowSpacing="18">
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
@@ -42,51 +43,53 @@
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
|
||||
<!-- 播放源列表 -->
|
||||
<ListView
|
||||
x:Name="MediaSourceProvidersListView"
|
||||
<InfoBar
|
||||
x:Uid="SettingsPageMusicGalleryOpened"
|
||||
Grid.Row="0"
|
||||
AllowDrop="True"
|
||||
CanDragItems="True"
|
||||
CanReorderItems="True"
|
||||
DragItemsCompleted="MediaSourceProvidersListView_DragItemsCompleted"
|
||||
ItemsSource="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo, Mode=OneWay}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||
ScrollViewer.HorizontalScrollMode="Enabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollMode="Disabled"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedMediaSourceProvider, Mode=TwoWay}">
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:MediaSourceProviderInfo">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{Binding Provider, Mode=OneWay}" />
|
||||
</ToolTipService.ToolTip>
|
||||
<FontIcon
|
||||
FontFamily="Segoe UI Symbol"
|
||||
FontSize="12"
|
||||
Glyph="⠿" />
|
||||
<Grid
|
||||
Width="16"
|
||||
Height="16"
|
||||
CornerRadius="4">
|
||||
<ImageIcon Source="{Binding LogoPath}" />
|
||||
</Grid>
|
||||
<TextBlock
|
||||
MaxWidth="200"
|
||||
Text="{Binding DisplayName, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.AppSettings.MusicGallerySettings.LyricsWindowStatus.IsOpened, Mode=OneWay}"
|
||||
Severity="Informational" />
|
||||
|
||||
<ScrollViewer Grid.Row="1" Style="{StaticResource SettingsScrollViewerStyle}">
|
||||
<!-- 播放源列表 -->
|
||||
<Grid Grid.Row="1" Margin="36,0,36,4">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<TextBlock x:Uid="SettingsPageConfigPlaybackSource" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<ComboBox
|
||||
x:Name="MediaSourceProvidersListView"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedMediaSourceProvider, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:MediaSourceProviderInfo">
|
||||
<Grid Padding="2,4" ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{Binding Provider, Mode=OneWay}" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
CornerRadius="4">
|
||||
<ImageIcon Source="{Binding LogoPath}" />
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding DisplayName, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 播放源配置 -->
|
||||
<ScrollViewer Grid.Row="2" Style="{StaticResource SettingsScrollViewerStyle}">
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
@@ -269,6 +272,7 @@
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- 播放源空白占位 -->
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"
|
||||
@@ -294,6 +298,8 @@
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- 右侧杂项设置 -->
|
||||
<Grid Grid.Column="1">
|
||||
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
@@ -322,10 +328,7 @@
|
||||
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsSearchControlDurauion"
|
||||
Unit="s"
|
||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="LyricsSearchControlDurauion" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DurationMs, TargetNullValue=N/A, Converter={StaticResource MillisecondsToFormattedTimeConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
|
||||
@@ -338,16 +341,15 @@
|
||||
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsSearchControlDurauion"
|
||||
Unit="s"
|
||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="LyricsSearchControlDurauion" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="LyricsPageLanguageCode" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsData.LanguageCode, TargetNullValue=N/A, Mode=OneWay, Converter={StaticResource LanguageCodeToDisplayedNameConverter}}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsPageLyricsProviderPrefix"
|
||||
Link="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, TargetNullValue=N/A, Mode=OneWay}"
|
||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||
<local:PropertyRow x:Uid="LyricsPageTranslationProviderPrefix" Value="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
||||
<local:PropertyRow x:Uid="LyricsPageTransliterationProviderPrefix" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.TransliterationProvider, Mode=OneWay, Converter={StaticResource TransliterationSearchProviderToDisplayNameConverter}}" />
|
||||
<local:PropertyRow x:Uid="LyricsPageTranslationProviderPrefix" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.TranslationProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsPageMatchPercentage"
|
||||
Unit="%"
|
||||
@@ -359,6 +361,10 @@
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageListenNewSession">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ListenOnNewPlaybackSource, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageForceWordByWordEffect">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IsForceWordByWordEffect, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
@@ -386,16 +392,23 @@
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageLibreTranslateServer" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<Grid ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox
|
||||
x:Uid="LibreTranslateServerTextBox"
|
||||
Grid.Column="0"
|
||||
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
||||
Text="{x:Bind ViewModel.AppSettings.TranslationSettings.LibreTranslateServer, Mode=TwoWay}" />
|
||||
Text="{x:Bind ViewModel.AppSettings.TranslationSettings.LibreTranslateServer, Mode=TwoWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Uid="SettingsPageServerTestButton"
|
||||
Grid.Column="1"
|
||||
Command="{x:Bind ViewModel.LibreTranslateServerTestCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
@@ -413,9 +426,33 @@
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
<dev:SettingsCard x:Uid="SettingsPageJapanese">
|
||||
<dev:SettingsExpander x:Uid="SettingsPageJapanese" IsExpanded="{x:Bind ViewModel.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageCutletDockerServer" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled, Mode=OneWay}">
|
||||
<dev:SettingsCard.Description>
|
||||
<HyperlinkButton Content="https://github.com/jayfunc/cutlet-docker" NavigateUri="https://github.com/jayfunc/cutlet-docker" />
|
||||
</dev:SettingsCard.Description>
|
||||
<Grid ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox
|
||||
x:Uid="CutletServerTextBox"
|
||||
Grid.Column="0"
|
||||
IsEnabled="{x:Bind ViewModel.IsCutletDockerServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
||||
Text="{x:Bind ViewModel.AppSettings.TranslationSettings.CutletDockerServer, Mode=TwoWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Uid="SettingsPageServerTestButton"
|
||||
Grid.Column="1"
|
||||
Command="{x:Bind ViewModel.CutletDockerServerTestCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.IsCutletDockerServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<!-- 中文简体繁体偏好 -->
|
||||
<TextBlock x:Uid="SettingsPageChineseLyrics" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
@@ -424,9 +461,9 @@
|
||||
</dev:SettingsCard>
|
||||
|
||||
<!-- Last.fm -->
|
||||
<TextBlock x:Uid="SettingsPageLastFM" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="Last.fm" />
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageLastFMManager"
|
||||
Header="Last.fm"
|
||||
HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/LastFM.png}"
|
||||
IsExpanded="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
@@ -514,6 +551,7 @@
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.RemoteServerConfigControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<ScrollViewer>
|
||||
<StackPanel Width="400" Spacing="16">
|
||||
<ProgressBar
|
||||
x:Name="ProgressBar"
|
||||
IsIndeterminate="True"
|
||||
Visibility="Collapsed" />
|
||||
<InfoBar
|
||||
x:Name="ErrorInfoBar"
|
||||
IsClosable="True"
|
||||
IsOpen="False"
|
||||
Severity="Error" />
|
||||
|
||||
<TextBox
|
||||
x:Name="NameBox"
|
||||
x:Uid="RemoteServerConfigControlName"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<StackPanel x:Name="RemoteFieldsPanel" Spacing="16">
|
||||
<Grid ColumnDefinitions="*, Auto" ColumnSpacing="12">
|
||||
<TextBox
|
||||
x:Name="HostBox"
|
||||
x:Uid="RemoteServerConfigControlServerAddress"
|
||||
Grid.Column="0"
|
||||
InputScope="Url"
|
||||
PlaceholderText="192.168.1.x"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<NumberBox
|
||||
x:Name="PortBox"
|
||||
x:Uid="RemoteServerConfigControlPort"
|
||||
Grid.Column="1"
|
||||
MinWidth="100"
|
||||
LargeChange="10"
|
||||
SmallChange="1"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
ToolTipService.ToolTip="80"
|
||||
Value="80" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<Grid ColumnDefinitions="*, Auto" ColumnSpacing="8">
|
||||
<TextBox
|
||||
x:Name="PathBox"
|
||||
x:Uid="RemoteServerConfigControlPath"
|
||||
Grid.Column="0"
|
||||
TextChanged="PathBox_TextChanged"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<Button
|
||||
x:Name="BrowseButton"
|
||||
x:Uid="RemoteServerConfigControlBrowse"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Bottom"
|
||||
Click="BrowseButton_Click"
|
||||
Visibility="Collapsed" />
|
||||
</Grid>
|
||||
|
||||
<InfoBar
|
||||
x:Name="PathWarningBar"
|
||||
IsClosable="False"
|
||||
IsOpen="False"
|
||||
Severity="Warning" />
|
||||
|
||||
<StackPanel x:Name="AuthFieldsPanel" Spacing="16">
|
||||
<Grid ColumnDefinitions="*, *" ColumnSpacing="12">
|
||||
<TextBox
|
||||
x:Name="UserBox"
|
||||
x:Uid="RemoteServerConfigControlUsername"
|
||||
Grid.Column="0"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<PasswordBox
|
||||
x:Name="PwdBox"
|
||||
x:Uid="RemoteServerConfigControlPassword"
|
||||
Grid.Column="1"
|
||||
PasswordRevealMode="Peek" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,197 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class RemoteServerConfigControl : UserControl
|
||||
{
|
||||
private readonly string _protocolType;
|
||||
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
public RemoteServerConfigControl(string protocolType)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
_protocolType = protocolType;
|
||||
|
||||
SetupDefaults();
|
||||
CheckPathForWarning();
|
||||
}
|
||||
|
||||
private void SetupDefaults()
|
||||
{
|
||||
if (_protocolType.Equals("Local", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
RemoteFieldsPanel.Visibility = Visibility.Collapsed;
|
||||
AuthFieldsPanel.Visibility = Visibility.Collapsed;
|
||||
|
||||
BrowseButton.Visibility = Visibility.Visible;
|
||||
|
||||
PathBox.PlaceholderText = @"D:\Music";
|
||||
}
|
||||
else
|
||||
{
|
||||
BrowseButton.Visibility = Visibility.Collapsed;
|
||||
RemoteFieldsPanel.Visibility = Visibility.Visible;
|
||||
AuthFieldsPanel.Visibility = Visibility.Visible;
|
||||
|
||||
switch (_protocolType.ToUpper())
|
||||
{
|
||||
case "SMB":
|
||||
PortBox.Value = 445;
|
||||
PathBox.PlaceholderText = "SharedMusic";
|
||||
break;
|
||||
case "FTP":
|
||||
PortBox.Value = 21;
|
||||
PathBox.PlaceholderText = "/pub/music";
|
||||
break;
|
||||
case "WEBDAV":
|
||||
PortBox.Value = 80;
|
||||
PathBox.PlaceholderText = "/dav/music";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetScheme()
|
||||
{
|
||||
string scheme = string.Empty;
|
||||
switch (_protocolType.ToUpper())
|
||||
{
|
||||
case "SMB":
|
||||
scheme = "smb";
|
||||
break;
|
||||
case "FTP":
|
||||
scheme = "ftp";
|
||||
break;
|
||||
case "WEBDAV":
|
||||
scheme = "https";
|
||||
break;
|
||||
}
|
||||
return scheme;
|
||||
}
|
||||
|
||||
public MediaFolder GetConfig()
|
||||
{
|
||||
string finalName = HostBox.Text.Trim();
|
||||
|
||||
if (_protocolType.Equals("Local", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(PathBox.Text))
|
||||
throw new ArgumentException(_localizationService.GetLocalizedString("RemoteServerConfigControlPathRequired"));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(NameBox.Text))
|
||||
finalName = NameBox.Text.Trim();
|
||||
else
|
||||
finalName = PathBox.Text.TrimEnd(System.IO.Path.DirectorySeparatorChar);
|
||||
|
||||
return new MediaFolder
|
||||
{
|
||||
Name = finalName,
|
||||
SourceType = FileSourceType.Local,
|
||||
UriScheme = "file",
|
||||
UriPath = PathBox.Text.Trim(),
|
||||
};
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(HostBox.Text))
|
||||
throw new ArgumentException(_localizationService.GetLocalizedString("RemoteServerConfigControlServerAddressRequired"));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(NameBox.Text))
|
||||
{
|
||||
finalName = NameBox.Text.Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
finalName = $"{_protocolType} - {HostBox.Text}";
|
||||
}
|
||||
|
||||
Enum.TryParse(_protocolType, true, out FileSourceType sourceType);
|
||||
|
||||
string scheme = GetScheme();
|
||||
|
||||
var folder = new MediaFolder
|
||||
{
|
||||
Name = finalName,
|
||||
SourceType = sourceType,
|
||||
|
||||
UriScheme = scheme,
|
||||
UriHost = HostBox.Text.Trim(), // ȥ<><C8A5><EFBFBD><EFBFBD>β<EFBFBD>ո<EFBFBD>
|
||||
UriPort = (int)PortBox.Value,
|
||||
|
||||
UriPath = PathBox.Text.Trim(),
|
||||
|
||||
UserName = UserBox.Text.Trim(),
|
||||
Password = PwdBox.Password,
|
||||
};
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
public void ShowError(string? message)
|
||||
{
|
||||
ErrorInfoBar.Message = message;
|
||||
ErrorInfoBar.IsOpen = !string.IsNullOrWhiteSpace(message);
|
||||
}
|
||||
|
||||
public void SetProgressBarVisibility(Visibility visibility)
|
||||
{
|
||||
ProgressBar.Visibility = visibility;
|
||||
}
|
||||
|
||||
private void PathBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
CheckPathForWarning();
|
||||
}
|
||||
|
||||
private void CheckPathForWarning()
|
||||
{
|
||||
string? path = PathBox.Text?.Trim();
|
||||
|
||||
bool isSymbolRoot = string.IsNullOrEmpty(path) ||
|
||||
path == "/" ||
|
||||
path == "\\";
|
||||
|
||||
bool isDriveRoot = false;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
var normalized = path.TrimEnd('\\', '/');
|
||||
isDriveRoot = normalized.EndsWith(":") && normalized.Length == 2;
|
||||
}
|
||||
|
||||
bool isRoot = isSymbolRoot || isDriveRoot;
|
||||
|
||||
if (isRoot)
|
||||
{
|
||||
PathWarningBar.Message = _localizationService.GetLocalizedString("FileSystemServiceRootDirectoryWarning");
|
||||
PathWarningBar.IsOpen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
PathWarningBar.IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void BrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var folder = await PickerHelper.PickSingleFolderAsync<SettingsWindow>();
|
||||
if (folder != null)
|
||||
{
|
||||
PathBox.Text = folder.Path;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowError(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.ShadowImage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:const="using:BetterLyrics.WinUI3.Constants"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</UserControl.OpacityTransition>
|
||||
|
||||
<Grid Margin="-32" Padding="32">
|
||||
<Grid
|
||||
x:Name="ShadowCastGrid"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
SizeChanged="ShadowCastGrid_SizeChanged">
|
||||
<Image Source="{x:Bind Source, Mode=OneWay}" Stretch="Uniform" />
|
||||
</Grid>
|
||||
<Border
|
||||
x:Name="ShadowRect"
|
||||
Loaded="ShadowRect_Loaded"
|
||||
SizeChanged="ShadowRect_SizeChanged"
|
||||
Translation="0,0,0">
|
||||
<Border.Shadow>
|
||||
<ThemeShadow x:Name="Shadow" />
|
||||
</Border.Shadow>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,93 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class ShadowImage : UserControl
|
||||
{
|
||||
public int CornerRadiusAmount
|
||||
{
|
||||
get { return (int)GetValue(CornerRadiusAmountProperty); }
|
||||
set { SetValue(CornerRadiusAmountProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CornerRadiusAmountProperty =
|
||||
DependencyProperty.Register(nameof(CornerRadiusAmount), typeof(int), typeof(ShadowImage), new PropertyMetadata(0, OnDependencyPropertyChanged));
|
||||
|
||||
public int ShadowAmount
|
||||
{
|
||||
get { return (int)GetValue(ShadowAmountProperty); }
|
||||
set { SetValue(ShadowAmountProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ShadowAmountProperty =
|
||||
DependencyProperty.Register(nameof(ShadowAmount), typeof(int), typeof(ShadowImage), new PropertyMetadata(0, OnDependencyPropertyChanged));
|
||||
|
||||
public ImageSource? Source
|
||||
{
|
||||
get { return (ImageSource?)GetValue(SourceProperty); }
|
||||
set { SetValue(SourceProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SourceProperty =
|
||||
DependencyProperty.Register(nameof(Source), typeof(ImageSource), typeof(ShadowImage), new PropertyMetadata(null));
|
||||
|
||||
public ShadowImage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is ShadowImage shadowImage)
|
||||
{
|
||||
if (e.Property == CornerRadiusAmountProperty)
|
||||
{
|
||||
shadowImage.UpdateShadowCastGridCornerRadius();
|
||||
shadowImage.UpdateShadowRectCornerRadius();
|
||||
}
|
||||
else if (e.Property == ShadowAmountProperty)
|
||||
{
|
||||
shadowImage.UpdateShadowRectShadow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateShadowRectShadow()
|
||||
{
|
||||
ShadowRect.Translation = new(0, 0, ShadowAmount);
|
||||
}
|
||||
|
||||
private void UpdateShadowCastGridCornerRadius()
|
||||
{
|
||||
var minSize = Math.Min(ShadowCastGrid.ActualHeight, ShadowCastGrid.ActualWidth);
|
||||
ShadowCastGrid.CornerRadius = new(CornerRadiusAmount / 100.0 * (minSize / 2));
|
||||
}
|
||||
|
||||
private void UpdateShadowRectCornerRadius()
|
||||
{
|
||||
var minSize = Math.Min(ShadowRect.ActualHeight, ShadowRect.ActualWidth);
|
||||
ShadowRect.CornerRadius = new(CornerRadiusAmount / 100.0 * (minSize / 2));
|
||||
}
|
||||
|
||||
private void ShadowCastGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
UpdateShadowCastGridCornerRadius();
|
||||
}
|
||||
|
||||
private void ShadowRect_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
UpdateShadowRectCornerRadius();
|
||||
}
|
||||
|
||||
private void ShadowRect_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Shadow.Receivers.Add(ShadowCastGrid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Input;
|
||||
using Microsoft.UI.Xaml;
|
||||
@@ -16,7 +16,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class ShortcutTextBox : UserControl
|
||||
{
|
||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
public ShortcutTextBox()
|
||||
{
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
x:Name="TrayIcon"
|
||||
x:FieldModifier="public"
|
||||
ContextMenuMode="SecondWindow"
|
||||
DoubleClickCommand="{x:Bind ViewModel.OpenLyricsCommand}"
|
||||
IconSource="ms-appx:///Assets/Logo.ico"
|
||||
LeftClickCommand="{x:Bind ViewModel.OpenLyricsCommand}"
|
||||
LeftClickCommand="{x:Bind ViewModel.OpenLyricsWindowSwitchCommand}"
|
||||
NoLeftClickDelay="True"
|
||||
ToolTipText="{x:Bind ViewModel.ToolTipText, Mode=OneWay}">
|
||||
<tb:TaskbarIcon.ContextFlyout>
|
||||
@@ -39,11 +38,6 @@
|
||||
Command="{x:Bind ViewModel.OpenLyricsSearchWindowCommand}"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}" />
|
||||
<MenuFlyoutItem
|
||||
x:Uid="SystemTrayLyrics"
|
||||
Command="{x:Bind ViewModel.OpenLyricsCommand}"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}" />
|
||||
<MenuFlyoutItem
|
||||
x:Uid="SystemTrayMusicGallery"
|
||||
Command="{x:Bind ViewModel.OpenMusicGalleryCommand}"
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.WindowSettingsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:dev="using:DevWinUI"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock x:Uid="AppSettingsControlGeneral" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<TextBox Text="{x:Bind LyricsWindowStatus.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextWrapping="Wrap" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageWorkArea"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageWorkAreaHeight" IsEnabled="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<local:ExtendedSlider
|
||||
Default="64"
|
||||
Maximum="{x:Bind LyricsWindowStatus.MonitorBounds.Height, Mode=OneWay}"
|
||||
Minimum="64"
|
||||
Unit="px"
|
||||
Value="{x:Bind LyricsWindowStatus.DockHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDockPlacement" IsEnabled="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDockMonitor" IsEnabled="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ComboBox ItemsSource="{x:Bind MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind LyricsWindowStatus.MonitorDeviceName, Mode=TwoWay}" />
|
||||
<Button
|
||||
Click="RefreshMonitorButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAdaptEnvColor"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAdaptToEnvironment, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard
|
||||
x:Uid="SettingsPageEnvColorSample"
|
||||
Header="Environment color sample mode"
|
||||
IsEnabled="{x:Bind LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.EnvironmentSampleMode, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleBelow" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleAbove" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleInner" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleEdge" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPagePinToTaskbar"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind LyricsWindowStatus.IsPinToTaskbar, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsPinToTaskbar, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageTaskbarPlacement">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.TaskbarPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLeft" />
|
||||
<ComboBoxItem x:Uid="SettingsPageRight" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageAlwaysHideUnlockButton" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAlwaysHideUnlockButton, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAOT"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAlwaysOnTop, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageForceAlwaysOnTop" IsEnabled="{x:Bind LyricsWindowStatus.IsAlwaysOnTop, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAlwaysOnTopPolling, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.AutoShowOrHideWindow, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageShowInSwitchers" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsShownInSwitchers, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDragArea" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.TitleBarArea, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaNone" />
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaTop" />
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaWhole" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,43 @@
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls;
|
||||
|
||||
public sealed partial class WindowSettingsControl : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty LyricsWindowStatusProperty =
|
||||
DependencyProperty.Register(nameof(LyricsWindowStatus), typeof(AlbumArtAreaEffectSettings), typeof(WindowSettingsControl), new PropertyMetadata(default));
|
||||
|
||||
public LyricsWindowStatus LyricsWindowStatus
|
||||
{
|
||||
get => (LyricsWindowStatus)GetValue(LyricsWindowStatusProperty);
|
||||
set => SetValue(LyricsWindowStatusProperty, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<string> MonitorDeviceNames { get; set; } = [];
|
||||
|
||||
public WindowSettingsControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
MonitorDeviceNames = [.. MonitorHook.GetAllMonitorDeviceNames()];
|
||||
}
|
||||
|
||||
private void RefreshMonitorDeviceNames()
|
||||
{
|
||||
MonitorDeviceNames = [.. MonitorHook.GetAllMonitorDeviceNames()];
|
||||
LyricsWindowStatus.MonitorDeviceName = MonitorDeviceNames.FirstOrDefault() ?? "";
|
||||
}
|
||||
|
||||
private void RefreshMonitorButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RefreshMonitorDeviceNames();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System;
|
||||
@@ -8,7 +8,7 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class AlbumArtSearchProviderToDisplayNameConverter : IValueConverter
|
||||
{
|
||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
@@ -16,8 +16,8 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
AlbumArtSearchProvider.Local => _resourceService.GetLocalizedString("AlbumArtSearchLocalProvider"),
|
||||
AlbumArtSearchProvider.SMTC => _resourceService.GetLocalizedString("AlbumArtSearchSMTCProvider"),
|
||||
AlbumArtSearchProvider.Local => _localizationService.GetLocalizedString("AlbumArtSearchLocalProvider"),
|
||||
AlbumArtSearchProvider.SMTC => _localizationService.GetLocalizedString("AlbumArtSearchSMTCProvider"),
|
||||
AlbumArtSearchProvider.iTunes => "iTunes",
|
||||
_ => throw new Exception($"Unknown AlbumArtSearchProvider: {provider}"),
|
||||
};
|
||||
|
||||
@@ -17,20 +17,16 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
using (var ms = new MemoryStream(byteArray))
|
||||
{
|
||||
var stream = ms.AsRandomAccessStream();
|
||||
|
||||
var bitmapImage = new BitmapImage();
|
||||
|
||||
bitmapImage.SetSource(stream);
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return PathHelper.AlbumArtPlaceholderPath;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return PathHelper.AlbumArtPlaceholderPath;
|
||||
return new BitmapImage(new Uri(PathHelper.AlbumArtPlaceholderPath));
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
|
||||
@@ -11,7 +11,8 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
if (value is string langCode)
|
||||
{
|
||||
return LanguageHelper.SupportedDisplayLanguages.FindIndex(x => x.LanguageCode == langCode);
|
||||
var found = LanguageHelper.SupportedDisplayLanguages.FindIndex(x => x.LanguageCode == langCode);
|
||||
return found == -1 ? 0 : found;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class FileSourceTypeToIconConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is FileSourceType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
FileSourceType.Local => "\uE8B7", // Folder
|
||||
FileSourceType.SMB => "\uE839", // Network
|
||||
FileSourceType.FTP => "\uE838", // Globe
|
||||
FileSourceType.WebDav => "\uE753", // Cloud
|
||||
_ => "\uE8B7"
|
||||
};
|
||||
}
|
||||
return "\uE8B7";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,11 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
if (value is string langCode)
|
||||
{
|
||||
if (PhoneticHelper.IsPhoneticCode(langCode))
|
||||
if (langCode == "N/A")
|
||||
{
|
||||
return langCode;
|
||||
}
|
||||
else if (PhoneticHelper.IsPhoneticCode(langCode))
|
||||
{
|
||||
return PhoneticHelper.GetDisplayName(langCode);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System;
|
||||
@@ -10,7 +10,7 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class LyricsSearchProviderToDisplayNameConverter : IValueConverter
|
||||
{
|
||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
@@ -24,10 +24,10 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
LyricsSearchProvider.Kugou => "酷狗音乐",
|
||||
LyricsSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
||||
LyricsSearchProvider.AppleMusic => "Apple Music",
|
||||
LyricsSearchProvider.LocalLrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
||||
LyricsSearchProvider.LocalMusicFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
||||
LyricsSearchProvider.LocalEslrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
||||
LyricsSearchProvider.LocalTtmlFile => _resourceService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
||||
LyricsSearchProvider.LocalLrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
||||
LyricsSearchProvider.LocalMusicFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
||||
LyricsSearchProvider.LocalEslrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
||||
LyricsSearchProvider.LocalTtmlFile => _localizationService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
||||
_ => "N/A",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
@@ -10,13 +11,13 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class MatchedLocalFilesPathToVisibilityConverter : IValueConverter
|
||||
{
|
||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is string path)
|
||||
{
|
||||
if (path == _resourceService.GetLocalizedString("MainPageNoLocalFilesMatched"))
|
||||
if (path == _localizationService.GetLocalizedString("MainPageNoLocalFilesMatched"))
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public class MillisecondsToSecondsConverter : IValueConverter
|
||||
public partial class MillisecondsToSecondsConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class PathToImageConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
string targetPath = PathHelper.AlbumArtPlaceholderPath;
|
||||
if (value is string path)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
targetPath = path;
|
||||
}
|
||||
}
|
||||
return new BitmapImage(new Uri(targetPath));
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class TrackToLyricsConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is Track track)
|
||||
{
|
||||
return track.GetRawLyrics();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System;
|
||||
@@ -10,7 +10,7 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class TranslationSearchProviderToDisplayNameConverter : IValueConverter
|
||||
{
|
||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
@@ -24,10 +24,10 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
TranslationSearchProvider.Kugou => "酷狗音乐",
|
||||
TranslationSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
||||
TranslationSearchProvider.AppleMusic => "Apple Music",
|
||||
TranslationSearchProvider.LocalLrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
||||
TranslationSearchProvider.LocalMusicFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
||||
TranslationSearchProvider.LocalEslrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
||||
TranslationSearchProvider.LocalTtmlFile => _resourceService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
||||
TranslationSearchProvider.LocalLrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
||||
TranslationSearchProvider.LocalMusicFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
||||
TranslationSearchProvider.LocalEslrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
||||
TranslationSearchProvider.LocalTtmlFile => _localizationService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
||||
TranslationSearchProvider.LibreTranslate => "LibreTranslate",
|
||||
_ => "N/A",
|
||||
};
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class TransliterationSearchProviderToDisplayNameConverter : IValueConverter
|
||||
{
|
||||
private readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is TransliterationSearchProvider provider)
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
TransliterationSearchProvider.LrcLib => "LrcLib",
|
||||
TransliterationSearchProvider.QQ => "QQ 音乐",
|
||||
TransliterationSearchProvider.Netease => "网易云音乐",
|
||||
TransliterationSearchProvider.Kugou => "酷狗音乐",
|
||||
TransliterationSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
||||
TransliterationSearchProvider.AppleMusic => "Apple Music",
|
||||
TransliterationSearchProvider.LocalLrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
|
||||
TransliterationSearchProvider.LocalMusicFile => _localizationService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
|
||||
TransliterationSearchProvider.LocalEslrcFile => _localizationService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
|
||||
TransliterationSearchProvider.LocalTtmlFile => _localizationService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
|
||||
TransliterationSearchProvider.BetterLyrics => "BetterLyrics",
|
||||
TransliterationSearchProvider.CutletDocker => "cutlet-docker",
|
||||
_ => "N/A",
|
||||
};
|
||||
}
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum AutoScanInterval
|
||||
{
|
||||
Disabled,
|
||||
Every15Minutes,
|
||||
EveryHour,
|
||||
Every6Hours,
|
||||
Daily
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum FileSourceType
|
||||
{
|
||||
Local,
|
||||
SMB,
|
||||
FTP,
|
||||
WebDav
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum ImageSwitchType
|
||||
{
|
||||
Crossfade,
|
||||
Slide
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum LineMaskType
|
||||
{
|
||||
Glow,
|
||||
Highlight,
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum LineRenderingType
|
||||
{
|
||||
CurrentChar,
|
||||
LineStartToCurrentChar,
|
||||
CurrentLine
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum LyricsEffectScope
|
||||
{
|
||||
LongDurationSyllable,
|
||||
LineStartToCurrentChar,
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,6 @@
|
||||
public enum ShortcutID
|
||||
{
|
||||
LyricsWindowShowOrHide,
|
||||
Borderless,
|
||||
ClickThrough,
|
||||
LyricsWindowSwitch,
|
||||
PlayOrPauseSong,
|
||||
NextSong,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum TaskbarPlacement
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum TransliterationSearchProvider
|
||||
{
|
||||
QQ,
|
||||
Kugou,
|
||||
Netease,
|
||||
LrcLib,
|
||||
AmllTtmlDb,
|
||||
AppleMusic,
|
||||
LocalMusicFile,
|
||||
LocalLrcFile,
|
||||
LocalEslrcFile,
|
||||
LocalTtmlFile,
|
||||
BetterLyrics,
|
||||
CutletDocker
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Events
|
||||
{
|
||||
public class InteractiveAreaEventArgs : EventArgs
|
||||
{
|
||||
public IList<FrameworkElement> Elements { get; set; }
|
||||
|
||||
public InteractiveAreaEventArgs(IList<FrameworkElement> elements)
|
||||
{
|
||||
Elements = elements;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Events
|
||||
{
|
||||
public class TaskbarFreeBoundsChangedEventArgs : EventArgs
|
||||
{
|
||||
public Rect TaskbarFreeBounds { get; }
|
||||
public TaskbarFreeBoundsChangedEventArgs(Rect taskbarBounds)
|
||||
{
|
||||
TaskbarFreeBounds = taskbarBounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
public static class LyricsDataExtensions
|
||||
{
|
||||
extension(LyricsData lyricsData)
|
||||
{
|
||||
public static LyricsData GetLoadingPlaceholder()
|
||||
{
|
||||
return new LyricsData()
|
||||
{
|
||||
LyricsLines = [
|
||||
new LyricsLine
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||
OriginalText = "● ● ●",
|
||||
},
|
||||
],
|
||||
LanguageCode = "N/A",
|
||||
};
|
||||
}
|
||||
|
||||
public void SetTranslatedText(LyricsData translationData, int toleranceMs = 50)
|
||||
{
|
||||
foreach (var line in lyricsData.LyricsLines)
|
||||
{
|
||||
// 在翻译歌词中查找与当前行开始时间最接近且在容忍范围内的行
|
||||
var transLine = translationData.LyricsLines
|
||||
.FirstOrDefault(t => Math.Abs(t.StartMs - line.StartMs) <= toleranceMs);
|
||||
|
||||
if (transLine != null)
|
||||
{
|
||||
// 此处 transLine.OriginalText 指翻译中的“原文”属性
|
||||
line.TranslatedText = transLine.OriginalText;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有匹配的翻译
|
||||
line.TranslatedText = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPhoneticText(LyricsData phoneticData, int toleranceMs = 50)
|
||||
{
|
||||
foreach (var line in lyricsData.LyricsLines)
|
||||
{
|
||||
// 在音译歌词中查找与当前行开始时间最接近且在容忍范围内的行
|
||||
var transLine = phoneticData.LyricsLines
|
||||
.FirstOrDefault(t => Math.Abs(t.StartMs - line.StartMs) <= toleranceMs);
|
||||
|
||||
if (transLine != null)
|
||||
{
|
||||
// 此处 transLine.OriginalText 指音译中的“原文”属性
|
||||
line.PhoneticText = transLine.OriginalText;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有匹配的音译
|
||||
line.PhoneticText = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTranslation(string translation)
|
||||
{
|
||||
List<string> translationArr = translation.Split(StringHelper.NewLine).ToList();
|
||||
int i = 0;
|
||||
foreach (var line in lyricsData.LyricsLines)
|
||||
{
|
||||
if (i >= translationArr.Count)
|
||||
{
|
||||
line.TranslatedText = ""; // No translation available, keep empty
|
||||
}
|
||||
else
|
||||
{
|
||||
line.TranslatedText = translationArr[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTransliteration(string transliteration)
|
||||
{
|
||||
List<string> transliterationArr = transliteration.Split(StringHelper.NewLine).ToList();
|
||||
int i = 0;
|
||||
foreach (var line in lyricsData.LyricsLines)
|
||||
{
|
||||
if (i >= transliterationArr.Count)
|
||||
{
|
||||
line.PhoneticText = ""; // No transliteration available, keep empty
|
||||
}
|
||||
else
|
||||
{
|
||||
line.PhoneticText = transliterationArr[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public LyricsLine? GetLyricsLine(double sec)
|
||||
{
|
||||
for (int i = 0; i < lyricsData.LyricsLines.Count; i++)
|
||||
{
|
||||
var line = lyricsData.LyricsLines[i];
|
||||
if (line.StartMs > sec * 1000)
|
||||
{
|
||||
return lyricsData.LyricsLines.ElementAtOrDefault(i - 1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,21 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
LyricsSearchProvider.LocalTtmlFile => TranslationSearchProvider.LocalTtmlFile,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
public TransliterationSearchProvider? ToTransliterationSearchProvider() => provider switch
|
||||
{
|
||||
LyricsSearchProvider.LrcLib => TransliterationSearchProvider.LrcLib,
|
||||
LyricsSearchProvider.QQ => TransliterationSearchProvider.QQ,
|
||||
LyricsSearchProvider.Kugou => TransliterationSearchProvider.Kugou,
|
||||
LyricsSearchProvider.Netease => TransliterationSearchProvider.Netease,
|
||||
LyricsSearchProvider.AmllTtmlDb => TransliterationSearchProvider.AmllTtmlDb,
|
||||
LyricsSearchProvider.AppleMusic => TransliterationSearchProvider.AppleMusic,
|
||||
LyricsSearchProvider.LocalMusicFile => TransliterationSearchProvider.LocalMusicFile,
|
||||
LyricsSearchProvider.LocalLrcFile => TransliterationSearchProvider.LocalLrcFile,
|
||||
LyricsSearchProvider.LocalEslrcFile => TransliterationSearchProvider.LocalEslrcFile,
|
||||
LyricsSearchProvider.LocalTtmlFile => TransliterationSearchProvider.LocalTtmlFile,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
public static class LyricsWindowStatusExtensions
|
||||
{
|
||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
private static readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
public static LyricsWindowStatus DesktopMode()
|
||||
public static LyricsWindowStatus DesktopMode(Window? window = null)
|
||||
{
|
||||
return new LyricsWindowStatus
|
||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||
return new LyricsWindowStatus(window)
|
||||
{
|
||||
Name = _resourceService.GetLocalizedString("DesktopMode"),
|
||||
Name = _localizationService.GetLocalizedString("DesktopMode"),
|
||||
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
|
||||
WindowBounds = new Rect(100, 100, 600, 250),
|
||||
IsLocked = true,
|
||||
IsAlwaysOnTop = true,
|
||||
IsAlwaysOnTopPolling = true,
|
||||
IsBorderless = true,
|
||||
IsClickThrough = true,
|
||||
IsAdaptToEnvironment = true,
|
||||
IsShownInSwitchers = false,
|
||||
EnvironmentSampleMode = WindowPixelSampleMode.WindowEdge,
|
||||
@@ -36,20 +39,19 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
};
|
||||
}
|
||||
|
||||
public static LyricsWindowStatus DockedMode()
|
||||
public static LyricsWindowStatus DockedMode(Window? window = null)
|
||||
{
|
||||
var status = new LyricsWindowStatus
|
||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||
var status = new LyricsWindowStatus(window)
|
||||
{
|
||||
Name = _resourceService.GetLocalizedString("DockedMode"),
|
||||
Name = _localizationService.GetLocalizedString("DockedMode"),
|
||||
IsWorkArea = true,
|
||||
IsAlwaysOnTop = true,
|
||||
IsAlwaysOnTopPolling = true,
|
||||
IsBorderless = true,
|
||||
IsAdaptToEnvironment = true,
|
||||
IsShownInSwitchers = false,
|
||||
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
|
||||
EnvironmentSampleMode = WindowPixelSampleMode.BelowWindow,
|
||||
TitleBarArea = TitleBarArea.None,
|
||||
LyricsStyleSettings = new LyricsStyleSettings
|
||||
{
|
||||
LyricsAlignmentType = TextAlignmentType.Center,
|
||||
@@ -64,19 +66,18 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
return status;
|
||||
}
|
||||
|
||||
public static LyricsWindowStatus FullscreenMode()
|
||||
public static LyricsWindowStatus FullscreenMode(Window? window = null)
|
||||
{
|
||||
var status = new LyricsWindowStatus
|
||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||
var status = new LyricsWindowStatus(window)
|
||||
{
|
||||
Name = _resourceService.GetLocalizedString("FullscreenMode"),
|
||||
IsBorderless = true,
|
||||
IsAlwaysOnTop = true,
|
||||
TitleBarArea = TitleBarArea.None,
|
||||
Name = _localizationService.GetLocalizedString("FullscreenMode"),
|
||||
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
|
||||
LyricsStyleSettings = new LyricsStyleSettings
|
||||
{
|
||||
LyricsAlignmentType = TextAlignmentType.Center,
|
||||
},
|
||||
IsFullscreen = true,
|
||||
};
|
||||
status.WindowBounds = new Rect(
|
||||
status.MonitorBounds.X,
|
||||
@@ -87,22 +88,49 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
return status;
|
||||
}
|
||||
|
||||
public static LyricsWindowStatus StandardMode()
|
||||
public static LyricsWindowStatus StandardMode(Window? window = null)
|
||||
{
|
||||
return new LyricsWindowStatus
|
||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||
return new LyricsWindowStatus(window)
|
||||
{
|
||||
Name = _resourceService.GetLocalizedString("StandardMode"),
|
||||
Name = _localizationService.GetLocalizedString("StandardMode"),
|
||||
};
|
||||
}
|
||||
|
||||
public static LyricsWindowStatus NarrowMode()
|
||||
public static LyricsWindowStatus NarrowMode(Window? window = null)
|
||||
{
|
||||
return new LyricsWindowStatus
|
||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||
return new LyricsWindowStatus(window)
|
||||
{
|
||||
Name = _resourceService.GetLocalizedString("NarrowMode"),
|
||||
Name = _localizationService.GetLocalizedString("NarrowMode"),
|
||||
WindowBounds = new Rect(100, 100, 400, 800),
|
||||
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
|
||||
};
|
||||
}
|
||||
|
||||
public static LyricsWindowStatus TaskbarMode(Window? window = null)
|
||||
{
|
||||
window ??= WindowHook.GetWindow<SystemTrayWindow>();
|
||||
return new LyricsWindowStatus(window)
|
||||
{
|
||||
Name = _localizationService.GetLocalizedString("TaskbarMode"),
|
||||
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
|
||||
IsPinToTaskbar = true,
|
||||
IsLocked = true,
|
||||
IsAlwaysOnTop = true,
|
||||
IsAlwaysOnTopPolling = true,
|
||||
IsAdaptToEnvironment = true,
|
||||
IsShownInSwitchers = false,
|
||||
EnvironmentSampleMode = WindowPixelSampleMode.WindowEdge,
|
||||
LyricsStyleSettings = new()
|
||||
{
|
||||
LyricsAlignmentType = TextAlignmentType.Center,
|
||||
},
|
||||
LyricsBackgroundSettings = new LyricsBackgroundSettings
|
||||
{
|
||||
IsFluidOverlayEnabled = false,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
|
||||
public Point AddX(double deltaX) => new(point.X + deltaX, point.Y);
|
||||
public Point AddY(double deltaY) => new(point.X, point.Y + deltaY);
|
||||
|
||||
public Point WithX(double x) => new(x, point.Y);
|
||||
public Point WithY(double y) => new(point.X, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Windows.Foundation;
|
||||
using Vanara.PInvoke;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Extensions
|
||||
@@ -77,5 +78,15 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension(RECT rect)
|
||||
{
|
||||
public Rect ToRect() => new(
|
||||
rect.Left,
|
||||
rect.Top,
|
||||
rect.Right - rect.Left,
|
||||
rect.Bottom - rect.Top
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Drawing;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
public static class RectangleExtensions
|
||||
{
|
||||
extension(Rectangle rect)
|
||||
{
|
||||
public Rect ToRect() => new(
|
||||
rect.Left,
|
||||
rect.Top,
|
||||
rect.Right - rect.Left,
|
||||
rect.Bottom - rect.Top
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using ATL;
|
||||
using System.IO;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
public static class TrackExtensions
|
||||
{
|
||||
extension(Track track)
|
||||
{
|
||||
public string GetParentFolderName() => Directory.GetParent(track.Path)?.Name ?? "";
|
||||
|
||||
public string GetParentFolderPath() => Directory.GetParent(track.Path)?.FullName ?? "";
|
||||
|
||||
public string GetRawLyrics()
|
||||
{
|
||||
if (track.Path is string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return TagLib.File.Create(path).Tag.Lyrics;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
@@ -9,16 +9,24 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
public static class WindowExtensions
|
||||
{
|
||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
private static readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
extension(Window window)
|
||||
{
|
||||
public void Init(
|
||||
string titleKey,
|
||||
string titleKey = "",
|
||||
string title = "",
|
||||
TitleBarHeightOption titleBarHeightOption = TitleBarHeightOption.Standard,
|
||||
BackdropType backdropType = BackdropType.DesktopAcrylic)
|
||||
{
|
||||
window.Title = _resourceService.GetLocalizedString(titleKey);
|
||||
if (titleKey != "")
|
||||
{
|
||||
window.Title = _localizationService.GetLocalizedString(titleKey);
|
||||
}
|
||||
if (title != "")
|
||||
{
|
||||
window.Title = title;
|
||||
}
|
||||
window.AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
|
||||
window.AppWindow.SetIcons();
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Ude;
|
||||
|
||||
@@ -86,5 +88,15 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
".wav", ".aiff", ".aif", ".pcm", ".cda", ".dsf", ".dff", ".au", ".snd",
|
||||
".mid", ".midi", ".mod", ".xm", ".it", ".s3m"
|
||||
};
|
||||
|
||||
public static readonly string[] LyricExtensions =
|
||||
Enum.GetValues(typeof(LyricsSearchProvider)).Cast<LyricsSearchProvider>()
|
||||
.Where(x => x.IsLocal())
|
||||
.Select(x => x.GetLyricsFormat())
|
||||
.Where(x => x != LyricsFormat.NotSpecified)
|
||||
.Select(x => x.ToFileExtension())
|
||||
.ToArray();
|
||||
|
||||
public static readonly HashSet<string> AllSupportedExtensions = new(MusicExtensions.Union(LyricExtensions));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Collections.ObjectModel;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
|
||||
public static class FolderTreeBuilder
|
||||
{
|
||||
public static ObservableCollection<FolderNode> Build(List<ExtendedTrack> tracks, List<MediaFolder> folderConfigs)
|
||||
{
|
||||
var rootNodes = new ObservableCollection<FolderNode>();
|
||||
|
||||
// 按 MediaFolderId 分组
|
||||
var folderGroups = tracks.GroupBy(t => t.MediaFolderId);
|
||||
|
||||
foreach (var group in folderGroups)
|
||||
{
|
||||
var config = folderConfigs.FirstOrDefault(f => f.Id == group.Key);
|
||||
if (config == null) continue;
|
||||
|
||||
string baseUri = config.GetStandardUri().AbsoluteUri.TrimEnd('/');
|
||||
|
||||
var rootNode = new FolderNode
|
||||
{
|
||||
SourceType = config.SourceType,
|
||||
FolderName = config.Name ?? config.ConnectionSummary, // 显示用户自定义的名字
|
||||
MediaFolderId = group.Key,
|
||||
FolderPath = baseUri,
|
||||
IsExpanded = true
|
||||
};
|
||||
|
||||
foreach (var track in group)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!track.Uri.StartsWith(baseUri)) continue; // 防御性编程
|
||||
|
||||
string relativePart = track.Uri.Substring(baseUri.Length);
|
||||
|
||||
var segments = relativePart
|
||||
.Split('/', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => System.Net.WebUtility.UrlDecode(s))
|
||||
.ToArray();
|
||||
|
||||
if (segments.Length > 1) // 长度大于1说明在子文件夹里
|
||||
{
|
||||
var folderSegments = segments.Take(segments.Length - 1).ToArray();
|
||||
CreateFolderStructure(rootNode, folderSegments, baseUri);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
rootNodes.Add(rootNode);
|
||||
}
|
||||
|
||||
return rootNodes;
|
||||
}
|
||||
|
||||
private static void CreateFolderStructure(FolderNode parent, string[] segments, string rootBaseUri)
|
||||
{
|
||||
var current = parent;
|
||||
string currentFullPath = parent.FolderPath;
|
||||
|
||||
foreach (var segmentName in segments)
|
||||
{
|
||||
var existingChild = current.SubFolders.FirstOrDefault(f => f.FolderName == segmentName);
|
||||
|
||||
currentFullPath += "/" + System.Net.WebUtility.UrlEncode(segmentName);
|
||||
|
||||
if (existingChild == null)
|
||||
{
|
||||
var newFolder = new FolderNode
|
||||
{
|
||||
FolderName = segmentName,
|
||||
FolderPath = currentFullPath, // 存完整的 URI
|
||||
MediaFolderId = parent.MediaFolderId
|
||||
};
|
||||
current.SubFolders.Add(newFolder);
|
||||
current = newFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = existingChild;
|
||||
currentFullPath = existingChild.FolderPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows.Markup;
|
||||
@@ -10,8 +8,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class FontHelper
|
||||
{
|
||||
public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies().Order().ToArray();
|
||||
|
||||
public static string GetLocalizedFontFamilyName(string sourceName, string langCode)
|
||||
{
|
||||
if (langCode == "")
|
||||
@@ -35,5 +31,20 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
return sourceName;
|
||||
}
|
||||
|
||||
public static List<string> GetSystemFontFamilies()
|
||||
{
|
||||
List<string> fontFamilies = new();
|
||||
|
||||
foreach (var font in Fonts.SystemFontFamilies)
|
||||
{
|
||||
if (font.FamilyNames.TryGetValue(XmlLanguage.GetLanguage("en-us"), out string englishFamilyName))
|
||||
{
|
||||
fontFamilies.Add(englishFamilyName);
|
||||
}
|
||||
}
|
||||
|
||||
return fontFamilies.Order().ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using Impressionist.Abstractions;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -53,42 +52,13 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static async Task<BitmapDecoder> MakeSquareWithThemeColor(IBuffer buffer, PaletteGeneratorType generatorType)
|
||||
public static async Task<BitmapDecoder> GetBitmapDecoder(IBuffer buffer)
|
||||
{
|
||||
using var stream = new InMemoryRandomAccessStream();
|
||||
await stream.WriteAsync(buffer);
|
||||
var decoder = await BitmapDecoder.CreateAsync(stream);
|
||||
|
||||
if (decoder.PixelWidth == decoder.PixelHeight)
|
||||
{
|
||||
// 已经是正方形,直接返回
|
||||
return decoder;
|
||||
}
|
||||
|
||||
using var device = CanvasDevice.GetSharedDevice();
|
||||
using var canvasBitmap = await CanvasBitmap.LoadAsync(device, stream);
|
||||
var size = Math.Max(decoder.PixelWidth, decoder.PixelHeight);
|
||||
|
||||
var result = await GetAccentColorAsync(decoder, generatorType);
|
||||
var color = Windows.UI.Color.FromArgb(255, (byte)result.Color.X, (byte)result.Color.Y, (byte)result.Color.Z);
|
||||
using var renderTarget = new CanvasRenderTarget(device, size, size, 96);
|
||||
|
||||
int offsetX = (int)(size - decoder.PixelWidth) / 2;
|
||||
int offsetY = (int)(size - decoder.PixelHeight) / 2;
|
||||
using (var ds = renderTarget.CreateDrawingSession())
|
||||
{
|
||||
ds.FillRectangle(0, 0, size, size, color);
|
||||
ds.DrawImage(canvasBitmap, offsetX, offsetY);
|
||||
}
|
||||
|
||||
// 保存为 PNG 并转为 byte[]
|
||||
stream.Seek(0);
|
||||
stream.Size = 0;
|
||||
await renderTarget.SaveAsync(stream, CanvasBitmapFileFormat.Png);
|
||||
stream.Seek(0);
|
||||
var newDecoder = await BitmapDecoder.CreateAsync(stream);
|
||||
return newDecoder;
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
public static byte[] GenerateNoiseBGRA(int width, int height)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using NTextCat;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Windows.Globalization;
|
||||
|
||||
@@ -10,9 +12,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class LanguageHelper
|
||||
{
|
||||
private static readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
private static readonly RankedLanguageIdentifierFactory _factory = new();
|
||||
private static readonly RankedLanguageIdentifier _identifier;
|
||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
|
||||
public const string ChineseCode = "zh";
|
||||
public const string JapaneseCode = "ja";
|
||||
@@ -92,12 +94,23 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
public static List<ExtendedLanguage> SupportedDisplayLanguages { get; set; } =
|
||||
[
|
||||
new ExtendedLanguage("", _resourceService.GetLocalizedString("SettingsPageSystemLanguage")),
|
||||
new ExtendedLanguage("en-US", "English"),
|
||||
new ExtendedLanguage("ja-JP"),
|
||||
new ExtendedLanguage("ko-KR"),
|
||||
new ExtendedLanguage("zh-CN", "简体中文"),
|
||||
new ExtendedLanguage("zh-TW", "繁體中文"),
|
||||
new ExtendedLanguage(CultureInfo.CurrentUICulture.Name, _localizationService.GetLocalizedString("SettingsPageSystemLanguage")),
|
||||
new ExtendedLanguage("ar"),
|
||||
new ExtendedLanguage("de"),
|
||||
new ExtendedLanguage("en"),
|
||||
new ExtendedLanguage("es"),
|
||||
new ExtendedLanguage("fr"),
|
||||
new ExtendedLanguage("hi"),
|
||||
new ExtendedLanguage("id"),
|
||||
new ExtendedLanguage("ja"),
|
||||
new ExtendedLanguage("ko"),
|
||||
new ExtendedLanguage("ms"),
|
||||
new ExtendedLanguage("pt"),
|
||||
new ExtendedLanguage("ru"),
|
||||
new ExtendedLanguage("th"),
|
||||
new ExtendedLanguage("vi"),
|
||||
new ExtendedLanguage("zh-Hans"),
|
||||
new ExtendedLanguage("zh-Hant"),
|
||||
];
|
||||
|
||||
static LanguageHelper()
|
||||
@@ -107,7 +120,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
public static string? DetectLanguageCode(string? text)
|
||||
{
|
||||
if (text == null) return null;
|
||||
if (string.IsNullOrWhiteSpace(text)) return null;
|
||||
var guessList = _identifier.Identify(text);
|
||||
string? code = guessList?.FirstOrDefault()?.Item1.Iso639_2T;
|
||||
code = code switch
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using BetterLyrics.WinUI3.Events;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Vanara.PInvoke;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于管理覆盖层窗口的鼠标交互区域检测
|
||||
/// </summary>
|
||||
public class OverlayInputHelper
|
||||
{
|
||||
private readonly Window _window;
|
||||
private readonly IntPtr _hwnd;
|
||||
private readonly DispatcherTimer _timer;
|
||||
private readonly List<FrameworkElement> _interactiveControls = new();
|
||||
|
||||
private bool _wasOverControl = false;
|
||||
|
||||
public Action<InteractiveAreaEventArgs> OnInteractiveAreaEntered;
|
||||
public Action<InteractiveAreaEventArgs> OnInteractiveAreaMoved;
|
||||
public Action OnInteractiveAreaExited;
|
||||
|
||||
public OverlayInputHelper(Window window)
|
||||
{
|
||||
_window = window;
|
||||
_hwnd = WinRT.Interop.WindowNative.GetWindowHandle(_window);
|
||||
|
||||
_timer = new DispatcherTimer();
|
||||
_timer.Interval = TimeSpan.FromMilliseconds(50);
|
||||
_timer.Tick += Timer_Tick;
|
||||
}
|
||||
|
||||
public void Register(FrameworkElement element)
|
||||
{
|
||||
if (!_interactiveControls.Contains(element)) _interactiveControls.Add(element);
|
||||
}
|
||||
|
||||
public void Unregister(FrameworkElement element)
|
||||
{
|
||||
if (_interactiveControls.Contains(element)) _interactiveControls.Remove(element);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_timer.Start();
|
||||
OnInteractiveAreaExited?.Invoke();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_timer.Stop();
|
||||
}
|
||||
|
||||
private void Timer_Tick(object? sender, object e)
|
||||
{
|
||||
User32.GetCursorPos(out var mousePoint);
|
||||
|
||||
bool isOverAnyControl = false;
|
||||
|
||||
List<FrameworkElement> overlappedElements = new();
|
||||
|
||||
foreach (var control in _interactiveControls)
|
||||
{
|
||||
if (control.XamlRoot == null || !control.XamlRoot.IsHostVisible) continue;
|
||||
|
||||
var bounds = GetElementScreenBounds(control);
|
||||
|
||||
if (mousePoint.X >= bounds.X &&
|
||||
mousePoint.X <= (bounds.X + bounds.Width) &&
|
||||
mousePoint.Y >= bounds.Y &&
|
||||
mousePoint.Y <= (bounds.Y + bounds.Height))
|
||||
{
|
||||
isOverAnyControl = true;
|
||||
overlappedElements.Add(control);
|
||||
}
|
||||
}
|
||||
|
||||
if (isOverAnyControl)
|
||||
{
|
||||
OnInteractiveAreaMoved?.Invoke(new InteractiveAreaEventArgs(overlappedElements));
|
||||
}
|
||||
|
||||
if (isOverAnyControl != _wasOverControl)
|
||||
{
|
||||
if (isOverAnyControl)
|
||||
{
|
||||
OnInteractiveAreaEntered?.Invoke(new InteractiveAreaEventArgs(overlappedElements));
|
||||
}
|
||||
else
|
||||
{
|
||||
OnInteractiveAreaExited?.Invoke();
|
||||
}
|
||||
|
||||
_wasOverControl = isOverAnyControl;
|
||||
}
|
||||
}
|
||||
|
||||
private Rect GetElementScreenBounds(FrameworkElement element)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transform = element.TransformToVisual(null);
|
||||
var topLeft = transform.TransformPoint(new Point(0, 0));
|
||||
double dpiScale = element.XamlRoot.RasterizationScale;
|
||||
|
||||
int clientX = (int)(topLeft.X * dpiScale);
|
||||
int clientY = (int)(topLeft.Y * dpiScale);
|
||||
|
||||
var point = new POINT { X = clientX, Y = clientY };
|
||||
User32.ClientToScreen(_hwnd, ref point);
|
||||
|
||||
return new Rect(point.X, point.Y, element.ActualWidth * dpiScale, element.ActualHeight * dpiScale);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Rect.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Windows.Security.Credentials;
|
||||
using System;
|
||||
using Windows.Security.Credentials;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
@@ -12,23 +13,13 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
/// <param name="value">要保存的值</param>
|
||||
public static void Save(string resource, string key, string value)
|
||||
{
|
||||
// 删除旧值(避免重复存储)
|
||||
try
|
||||
{
|
||||
var vault = new PasswordVault();
|
||||
|
||||
var oldCredential = vault.Retrieve(resource, key);
|
||||
if (oldCredential != null)
|
||||
{
|
||||
vault.Remove(oldCredential);
|
||||
}
|
||||
|
||||
vault.Add(new PasswordCredential(resource, key, value));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 没有旧值就忽略
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -47,7 +38,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
credential.RetrievePassword();
|
||||
return credential.Password;
|
||||
}
|
||||
catch
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -65,10 +56,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
var credential = vault.Retrieve(resource, key);
|
||||
vault.Remove(credential);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 不存在就忽略
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,8 +54,10 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
public static string AlbumArtCacheDirectory => Path.Combine(CacheFolder, "album-art");
|
||||
public static string iTunesAlbumArtCacheDirectory => Path.Combine(AlbumArtCacheDirectory, "itunes");
|
||||
public static string LocalAlbumArtCacheDirectory => Path.Combine(AlbumArtCacheDirectory, "local");
|
||||
|
||||
public static string PlayQueuePath => Path.Combine(CacheFolder, "play-queue.m3u");
|
||||
public static string FilesCachePath => Path.Combine(CacheFolder, "files_cache.db");
|
||||
|
||||
public static void EnsureDirectories()
|
||||
{
|
||||
@@ -75,6 +77,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
Directory.CreateDirectory(LocalTtmlCacheDirectory);
|
||||
|
||||
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
||||
Directory.CreateDirectory(LocalAlbumArtCacheDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using System;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class PhoneticHelper
|
||||
{
|
||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
private static readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
public const string PinyinCode = "zh-cmn-pinyin";
|
||||
public const string JyutpingCode = "zh-yue-jyutping";
|
||||
@@ -22,21 +22,16 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
switch (code)
|
||||
{
|
||||
case PinyinCode:
|
||||
return _resourceService.GetLocalizedString("Pinyin");
|
||||
return _localizationService.GetLocalizedString("Pinyin");
|
||||
case JyutpingCode:
|
||||
return _resourceService.GetLocalizedString("Jyutping");
|
||||
return _localizationService.GetLocalizedString("Jyutping");
|
||||
case RomanCode:
|
||||
return _resourceService.GetLocalizedString("Romaji");
|
||||
return _localizationService.GetLocalizedString("Romaji");
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(code));
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToRomaji(string text)
|
||||
{
|
||||
return Kana.Kana.KanaToRomaji(text, Kana.Error.Ignore).ToStr();
|
||||
}
|
||||
|
||||
public static string ToPinyin(string text, Pinyin.ManTone.Style style = Pinyin.ManTone.Style.TONE)
|
||||
{
|
||||
return Pinyin.Pinyin.Instance.HanziToPinyin(text, style).ToStr();
|
||||
|
||||
@@ -45,6 +45,12 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static async Task<StorageFile?> PickSaveFileAsync<T>(IDictionary<string, IList<string>> fileTypeChoices)
|
||||
{
|
||||
var window = WindowHook.GetWindow<T>();
|
||||
|
||||
return await PickSaveFileAsync(window, fileTypeChoices);
|
||||
}
|
||||
|
||||
public static async Task<StorageFile?> PickSaveFileAsync<T>(T? window, IDictionary<string, IList<string>> fileTypeChoices)
|
||||
{
|
||||
if (window == null) return null;
|
||||
|
||||
var picker = new Windows.Storage.Pickers.FileSavePicker();
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
public static bool IsAppleMusic(string? id) => id is PlayerID.AppleMusic or PlayerID.AppleMusicAlternative;
|
||||
|
||||
public static bool IsBetterLyrics(string? id) => id is PlayerID.BetterLyrics or PlayerID.BetterLyricsDebug;
|
||||
|
||||
public static string? GetDisplayName(string? id) => id switch
|
||||
{
|
||||
PlayerID.Spotify => PlayerName.Spotify,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class StreamFileAbstraction : TagLib.File.IFileAbstraction
|
||||
{
|
||||
private readonly string _name;
|
||||
private readonly Stream _stream;
|
||||
private readonly bool _closeStreamOnDispose;
|
||||
|
||||
public StreamFileAbstraction(string path, Stream? stream, bool closeStreamOnDispose = false)
|
||||
{
|
||||
_name = Path.GetFileName(path);
|
||||
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
|
||||
_closeStreamOnDispose = closeStreamOnDispose;
|
||||
}
|
||||
|
||||
public string Name => _name;
|
||||
|
||||
public Stream ReadStream => _stream;
|
||||
|
||||
public Stream WriteStream
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_stream.CanWrite)
|
||||
{
|
||||
return _stream;
|
||||
}
|
||||
throw new InvalidOperationException("The underlying stream is read-only. Tag saving is not supported for this source.");
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseStream(Stream stream)
|
||||
{
|
||||
if (_closeStreamOnDispose)
|
||||
{
|
||||
stream?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
@@ -8,12 +8,12 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class ToastHelper
|
||||
{
|
||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
private static readonly ILocalizationService _localizationService = Ioc.Default.GetRequiredService<ILocalizationService>();
|
||||
|
||||
public static void ShowToast(string localizedTitleKey, string? description, InfoBarSeverity severity)
|
||||
{
|
||||
AppNotification notification = new AppNotificationBuilder()
|
||||
.AddText(_resourceService.GetLocalizedString(localizedTitleKey))
|
||||
.AddText(_localizationService.GetLocalizedString(localizedTitleKey))
|
||||
.AddText(description)
|
||||
.BuildNotification();
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
|
||||
_hooks.Clear();
|
||||
|
||||
//_timer.Stop();
|
||||
_timer.Stop();
|
||||
}
|
||||
|
||||
private void Timer_Tick(object? sender, object e)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Vanara.PInvoke;
|
||||
@@ -19,13 +20,10 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
/// <param name="id"></param>
|
||||
/// <param name="keys"></param>
|
||||
/// <param name="action"></param>
|
||||
private static void RegisterHotKey<T>(ShortcutID id, List<string> keys, Action action)
|
||||
private static void RegisterHotKey(Window window, ShortcutID id, List<string> keys, Action action)
|
||||
{
|
||||
if (keys.Count == 0) return;
|
||||
|
||||
var window = WindowHook.GetWindow<T>();
|
||||
if (window == null) return;
|
||||
|
||||
HWND hwnd = WindowNative.GetWindowHandle(window);
|
||||
User32.HotKeyModifiers modifiers = User32.HotKeyModifiers.MOD_NONE;
|
||||
VirtualKey key = VirtualKey.None;
|
||||
@@ -60,21 +58,18 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnregisterHotKey<T>(ShortcutID id)
|
||||
private static void UnregisterHotKey(Window window, ShortcutID id)
|
||||
{
|
||||
var window = WindowHook.GetWindow<T>();
|
||||
if (window == null) return;
|
||||
|
||||
HWND hwnd = WindowNative.GetWindowHandle(window);
|
||||
User32.UnregisterHotKey(hwnd, (int)id);
|
||||
_actions.Remove((int)id);
|
||||
_keys.Remove((int)id);
|
||||
}
|
||||
|
||||
public static void UpdateHotKey<T>(ShortcutID id, List<string> keys, Action action)
|
||||
public static void UpdateHotKey(Window window, ShortcutID id, List<string> keys, Action action)
|
||||
{
|
||||
UnregisterHotKey<T>(id);
|
||||
RegisterHotKey<T>(id, keys, action);
|
||||
UnregisterHotKey(window, id);
|
||||
RegisterHotKey(window, id, keys, action);
|
||||
}
|
||||
|
||||
public static bool IsHotKeyRegistered(ShortcutID id)
|
||||
|
||||
213
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Hooks/TaskbarHook.cs
Normal file
213
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Hooks/TaskbarHook.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Events;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using FlaUI.Core.AutomationElements;
|
||||
using FlaUI.Core.Definitions;
|
||||
using FlaUI.Core.EventHandlers;
|
||||
using FlaUI.UIA3;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Hooks
|
||||
{
|
||||
public partial class TaskbarHook : IDisposable
|
||||
{
|
||||
private readonly UIA3Automation _automation;
|
||||
private AutomationElement? _taskbar;
|
||||
|
||||
private StructureChangedEventHandlerBase? _structureHandler;
|
||||
private PropertyChangedEventHandlerBase? _propertyHandler;
|
||||
|
||||
private TaskbarPlacement _currentPlacement;
|
||||
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private readonly Action<TaskbarFreeBoundsChangedEventArgs> _onLayoutChanged;
|
||||
private Timer? _debounceTimer;
|
||||
private const int DebounceDelay = 150;
|
||||
private bool _isDisposed;
|
||||
|
||||
public TaskbarHook(TaskbarPlacement placement, Action<TaskbarFreeBoundsChangedEventArgs> onLayoutChanged)
|
||||
{
|
||||
_automation = new UIA3Automation();
|
||||
_onLayoutChanged = onLayoutChanged;
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
_currentPlacement = placement;
|
||||
|
||||
StartHook();
|
||||
}
|
||||
|
||||
public void UpdatePlacement(TaskbarPlacement newPlacement)
|
||||
{
|
||||
_currentPlacement = newPlacement;
|
||||
RequestUpdate(); // 立即刷新位置
|
||||
}
|
||||
|
||||
private void StartHook()
|
||||
{
|
||||
try
|
||||
{
|
||||
var desktop = _automation.GetDesktop();
|
||||
_taskbar = desktop.FindFirstChild(cf => cf.ByClassName("Shell_TrayWnd"));
|
||||
|
||||
if (_taskbar == null) return;
|
||||
|
||||
// 监听结构变化
|
||||
// 这里的返回值就是一个可以 Dispose 的对象
|
||||
_structureHandler = _taskbar.RegisterStructureChangedEvent(
|
||||
TreeScope.Descendants,
|
||||
(element, type, id) => RequestUpdate());
|
||||
|
||||
// 监听属性变化
|
||||
_propertyHandler = _taskbar.RegisterPropertyChangedEvent(
|
||||
TreeScope.Element,
|
||||
(element, id, val) => RequestUpdate(),
|
||||
_automation.PropertyLibrary.Element.BoundingRectangle);
|
||||
|
||||
RequestUpdate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Hook Init Failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void RequestUpdate()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
_debounceTimer?.Dispose();
|
||||
_debounceTimer = new Timer(_ =>
|
||||
{
|
||||
Rectangle voidRect = CalculateVoidRect(_currentPlacement);
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
if (!_isDisposed && voidRect != Rectangle.Empty)
|
||||
{
|
||||
_onLayoutChanged?.Invoke(new TaskbarFreeBoundsChangedEventArgs(voidRect.ToRect()));
|
||||
}
|
||||
});
|
||||
}, null, DebounceDelay, Timeout.Infinite);
|
||||
}
|
||||
|
||||
private Rectangle CalculateVoidRect(TaskbarPlacement placement)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_taskbar == null) return Rectangle.Empty;
|
||||
|
||||
try { var _ = _taskbar.BoundingRectangle; }
|
||||
catch
|
||||
{
|
||||
var desktop = _automation.GetDesktop();
|
||||
_taskbar = desktop.FindFirstChild(cf => cf.ByClassName("Shell_TrayWnd"));
|
||||
if (_taskbar == null) return Rectangle.Empty;
|
||||
}
|
||||
|
||||
Rectangle taskbarRect = _taskbar.BoundingRectangle;
|
||||
|
||||
// 绝对右边界:托盘
|
||||
int barrierRight = taskbarRect.Right;
|
||||
var tray = _taskbar.FindFirstDescendant(cf => cf.ByAutomationId("SystemTrayIcon")); // Win11
|
||||
if (tray == null) tray = _taskbar.FindFirstDescendant(cf => cf.ByClassName("TrayNotifyWnd")); // Win10
|
||||
if (tray != null) barrierRight = tray.BoundingRectangle.Left;
|
||||
|
||||
// 绝对左边界:任务栏左边缘 或 小组件(Win11)
|
||||
int barrierLeft = taskbarRect.Left;
|
||||
var widgets = _taskbar.FindFirstDescendant(cf => cf.ByAutomationId("WidgetsButton"));
|
||||
|
||||
// 只有当小组件确实在最左侧时 (Win11默认),它才构成左边界
|
||||
// 如果用户把任务栏设为靠左对齐,小组件会在开始按钮右边,这时候不把它当做左边界
|
||||
if (widgets != null && widgets.BoundingRectangle.Left < taskbarRect.Left + 100)
|
||||
{
|
||||
barrierLeft = (int)widgets.BoundingRectangle.Right;
|
||||
}
|
||||
|
||||
|
||||
// 寻找 中间内容区域 (Start + Search + Apps) 的 左右极值
|
||||
int contentMinLeft = barrierRight;
|
||||
int contentMaxRight = barrierLeft;
|
||||
|
||||
// 定义所有中间元素
|
||||
string[] systemButtonIds = new[] {
|
||||
"StartButton", "SearchButton", "TaskViewButton", "ChatButton"
|
||||
};
|
||||
|
||||
// 系统按钮
|
||||
foreach (var id in systemButtonIds)
|
||||
{
|
||||
var btn = _taskbar.FindFirstDescendant(cf => cf.ByAutomationId(id));
|
||||
if (btn != null)
|
||||
{
|
||||
var rect = btn.BoundingRectangle;
|
||||
// 排除不可见的
|
||||
if (rect.Width <= 0) continue;
|
||||
|
||||
// 更新极值
|
||||
if (rect.Left < contentMinLeft) contentMinLeft = (int)rect.Left;
|
||||
if (rect.Right > contentMaxRight) contentMaxRight = (int)rect.Right;
|
||||
}
|
||||
}
|
||||
|
||||
// App 图标
|
||||
var appIcons = _taskbar.FindAllDescendants(cf => cf.ByClassName("Taskbar.TaskListButtonAutomationPeer"));
|
||||
foreach (var icon in appIcons)
|
||||
{
|
||||
var rect = icon.BoundingRectangle;
|
||||
if (rect.Width <= 0) continue;
|
||||
|
||||
if (rect.Left < contentMinLeft) contentMinLeft = (int)rect.Left;
|
||||
if (rect.Right > contentMaxRight) contentMaxRight = (int)rect.Right;
|
||||
}
|
||||
|
||||
// 如果完全没找到内容,重置为中间
|
||||
if (contentMinLeft == barrierRight) contentMinLeft = taskbarRect.Left;
|
||||
if (contentMaxRight == barrierLeft) contentMaxRight = taskbarRect.Left;
|
||||
|
||||
int finalLeft, finalRight;
|
||||
int padding = 10;
|
||||
|
||||
if (placement == TaskbarPlacement.Left)
|
||||
{
|
||||
// 【小组件】... [空隙] ...【开始按钮】
|
||||
// 如果是 Win10 或 Win11左对齐,contentMinLeft 几乎等于 barrierLeft,空隙为0
|
||||
finalLeft = barrierLeft + padding;
|
||||
finalRight = contentMinLeft - padding;
|
||||
}
|
||||
else // Right
|
||||
{
|
||||
// 【最后一个图标】... [空隙] ...【托盘】
|
||||
finalLeft = contentMaxRight + padding;
|
||||
finalRight = barrierRight - padding;
|
||||
}
|
||||
|
||||
int width = finalRight - finalLeft;
|
||||
|
||||
if (width < 20) return Rectangle.Empty;
|
||||
|
||||
var finalRect = new Rectangle(finalLeft, taskbarRect.Top, width, taskbarRect.Height);
|
||||
return finalRect;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Calc Rect Error: {ex.Message}");
|
||||
return Rectangle.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
_isDisposed = true;
|
||||
|
||||
_debounceTimer?.Dispose();
|
||||
|
||||
_structureHandler?.Dispose();
|
||||
_propertyHandler?.Dispose();
|
||||
|
||||
_automation?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Services.LiveStatesService;
|
||||
using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Windowing;
|
||||
@@ -26,42 +23,31 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
private static List<object> _activeWindows = [];
|
||||
private static List<object> _workAreas = [];
|
||||
|
||||
private static readonly Dictionary<HWND, WindowStyle> _defaultWindowStyle = [];
|
||||
private static readonly Dictionary<HWND, ExtendedWindowStyle> _defaultExtendedWindowStyle = [];
|
||||
private static WindowStyle? _defaultWindowStyle;
|
||||
private static ExtendedWindowStyle? _defaultExtendedWindowStyle;
|
||||
|
||||
private static readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||
private static readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
|
||||
|
||||
private static DispatcherQueueTimer? _setLyricsWindowVisibilityByPlayingStatusTimer;
|
||||
|
||||
public static void HideWindow<T>()
|
||||
public static void HideWindow(this Window window)
|
||||
{
|
||||
var window = _activeWindows.Find(w => w is T);
|
||||
var castedWindow = window as Window;
|
||||
castedWindow?.Hide();
|
||||
window.Hide();
|
||||
}
|
||||
|
||||
public static void CloseWindow<T>()
|
||||
public static void CloseWindow(this Window window)
|
||||
{
|
||||
if (typeof(T) == typeof(NowPlayingWindow))
|
||||
if (window is NowPlayingWindow nowPlayingWindow)
|
||||
{
|
||||
EnsureDockModeReleased();
|
||||
}
|
||||
var window = _activeWindows.Find(w => w is T);
|
||||
if (window is Window w)
|
||||
{
|
||||
w.Close();
|
||||
_activeWindows.Remove(w);
|
||||
if (GetWindowHandle(window) is IntPtr hwnd)
|
||||
{
|
||||
UnregisterWorkArea(hwnd);
|
||||
}
|
||||
nowPlayingWindow.LyricsWindowStatus.IsOpened = false;
|
||||
}
|
||||
window.Close();
|
||||
_activeWindows.Remove(window);
|
||||
}
|
||||
|
||||
public static void MinimizeWindow<T>()
|
||||
public static void MinimizeWindow(this Window window)
|
||||
{
|
||||
var window = _activeWindows.Find(w => w is T);
|
||||
if (window is Window w)
|
||||
{
|
||||
w.Minimize();
|
||||
}
|
||||
window.Minimize();
|
||||
}
|
||||
|
||||
public static T? GetWindow<T>()
|
||||
@@ -76,15 +62,28 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
return default;
|
||||
}
|
||||
|
||||
public static List<T> GetWindows<T>()
|
||||
{
|
||||
var windows = new List<T>();
|
||||
foreach (var window in _activeWindows)
|
||||
{
|
||||
if (window is T castedWindow)
|
||||
{
|
||||
windows.Add(castedWindow);
|
||||
}
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
|
||||
public static IntPtr? GetWindowHandle(object? obj)
|
||||
{
|
||||
if (obj is FrameworkElement frameworkElement)
|
||||
{
|
||||
return frameworkElement.XamlRoot.ContentIslandEnvironment.AppWindowId.GetWindowHandle();
|
||||
}
|
||||
else if (obj != null)
|
||||
else if (obj is Window window)
|
||||
{
|
||||
return WindowNative.GetWindowHandle(obj);
|
||||
return WindowNative.GetWindowHandle(window);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -97,15 +96,22 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
return GetWindowHandle(GetWindow<T>());
|
||||
}
|
||||
|
||||
public static void OpenOrShowWindow<T>()
|
||||
public static T OpenOrShowWindow<T>(LyricsWindowStatus? status = null)
|
||||
{
|
||||
var window = _activeWindows.Find(w => w is T);
|
||||
var window = _activeWindows.Find(w =>
|
||||
(typeof(T) != typeof(NowPlayingWindow) && w is T) ||
|
||||
(typeof(T) == typeof(NowPlayingWindow) && w is T && ((NowPlayingWindow)w).LyricsWindowStatus == status)
|
||||
);
|
||||
|
||||
if (window == null)
|
||||
{
|
||||
if (typeof(T) == typeof(NowPlayingWindow))
|
||||
{
|
||||
window = new NowPlayingWindow();
|
||||
((NowPlayingWindow)window).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
|
||||
if (status == null)
|
||||
{
|
||||
throw new NullReferenceException(nameof(status));
|
||||
}
|
||||
window = new NowPlayingWindow(status);
|
||||
}
|
||||
else if (typeof(T) == typeof(SettingsWindow))
|
||||
{
|
||||
@@ -123,6 +129,10 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
{
|
||||
window = new LyricsWindowSwitchWindow();
|
||||
}
|
||||
else if (typeof(T) == typeof(SystemTrayWindow))
|
||||
{
|
||||
window = new SystemTrayWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Unsupported window type", nameof(T));
|
||||
@@ -130,33 +140,24 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
|
||||
TrackWindow(window);
|
||||
var castedWindow = (Window)window;
|
||||
|
||||
castedWindow.Restore();
|
||||
castedWindow.Activate();
|
||||
|
||||
if (typeof(T) == typeof(SystemTrayWindow))
|
||||
{
|
||||
_defaultWindowStyle = castedWindow.GetWindowStyle();
|
||||
_defaultExtendedWindowStyle = castedWindow.GetExtendedWindowStyle();
|
||||
castedWindow.HideWindow();
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(NowPlayingWindow))
|
||||
{
|
||||
_liveStatesService.InitLyricsWindowStatus();
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(castedWindow);
|
||||
_defaultWindowStyle.Add(hwnd, castedWindow.GetWindowStyle());
|
||||
_defaultExtendedWindowStyle.Add(hwnd, castedWindow.GetExtendedWindowStyle());
|
||||
|
||||
var lyricsWindow = (NowPlayingWindow)window;
|
||||
lyricsWindow.ViewModel.InitShortcuts();
|
||||
lyricsWindow.ViewModel.InitFgWindowWatcher();
|
||||
|
||||
_mediaSessionsService.InitPlaybackShortcuts();
|
||||
|
||||
//TaskbarList.ThumbBarAddButtons(hwnd,
|
||||
// [
|
||||
// new Shell32.THUMBBUTTON()
|
||||
// {
|
||||
// szTip = "Previous",
|
||||
// dwFlags = Shell32.THUMBBUTTONFLAGS.THBF_ENABLED,
|
||||
// dwMask = Shell32.THUMBBUTTONMASK.THB_TOOLTIP | Shell32.THUMBBUTTONMASK.THB_FLAGS,
|
||||
// }
|
||||
// ]
|
||||
//);
|
||||
lyricsWindow.InitStatus();
|
||||
lyricsWindow.InitFgWindowWatcher();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -165,6 +166,13 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
castedWindow.Activate();
|
||||
castedWindow.AppWindow.MoveInZOrderAtTop();
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(NowPlayingWindow))
|
||||
{
|
||||
((NowPlayingWindow)window).LyricsWindowStatus.IsOpened = true;
|
||||
}
|
||||
|
||||
return (T)window;
|
||||
}
|
||||
|
||||
public static void RestartApp(string args = "")
|
||||
@@ -189,13 +197,19 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
|
||||
public static void ExitApp()
|
||||
{
|
||||
EnsureDockModeReleased();
|
||||
EnsureAllWorkAreasReleased();
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private static void EnsureDockModeReleased()
|
||||
private static void EnsureAllWorkAreasReleased()
|
||||
{
|
||||
SetIsWorkArea<NowPlayingWindow>(false);
|
||||
foreach (var item in _workAreas)
|
||||
{
|
||||
if (GetWindowHandle(item) is IntPtr hwnd)
|
||||
{
|
||||
UnregisterWorkArea(hwnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void TrackWindow(object window)
|
||||
@@ -210,43 +224,18 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
|
||||
private static void WindowHelper_Closed(object sender, WindowEventArgs args)
|
||||
{
|
||||
if (_activeWindows.Contains(sender))
|
||||
{
|
||||
_activeWindows.Remove(sender);
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(sender);
|
||||
_defaultWindowStyle.Remove(hwnd);
|
||||
_defaultExtendedWindowStyle.Remove(hwnd);
|
||||
}
|
||||
_activeWindows.Remove(sender);
|
||||
}
|
||||
|
||||
public static void SetIsClickThrough<T>(bool enable)
|
||||
public static void SetIsWorkArea(this NowPlayingWindow window, bool enable)
|
||||
{
|
||||
Window? window = GetWindow<T>() as Window;
|
||||
if (window == null) return;
|
||||
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
if (enable)
|
||||
{
|
||||
window.SetExtendedWindowStyle(_defaultExtendedWindowStyle[hwnd] | ExtendedWindowStyle.Transparent | ExtendedWindowStyle.Layered);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.SetExtendedWindowStyle(_defaultExtendedWindowStyle[hwnd]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIsWorkArea<T>(bool enable)
|
||||
{
|
||||
Window? window = GetWindow<T>() as Window;
|
||||
if (window == null) return;
|
||||
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
if (enable)
|
||||
{
|
||||
RegisterWorkArea(hwnd);
|
||||
RegisterWorkArea(hwnd, window.LyricsWindowStatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -254,35 +243,86 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIsBorderless<T>(bool enable)
|
||||
public static void SetIsLocked(this Window window, bool enable)
|
||||
{
|
||||
var window = GetWindow<T>() as Window;
|
||||
if (window == null) return;
|
||||
SetIsBorderless(window, enable);
|
||||
SetIsClickThrough(window, enable);
|
||||
}
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
if (enable)
|
||||
public static void SetIsClickThrough(this Window window, bool enable)
|
||||
{
|
||||
if (_defaultExtendedWindowStyle is ExtendedWindowStyle style)
|
||||
{
|
||||
window.SetWindowStyle(WindowStyle.Popup | WindowStyle.Visible);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.SetWindowStyle(_defaultWindowStyle[hwnd]);
|
||||
if (enable)
|
||||
{
|
||||
window.SetExtendedWindowStyle(style | ExtendedWindowStyle.Layered | ExtendedWindowStyle.Transparent);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.SetExtendedWindowStyle(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIsShowInSwitchers<T>(bool enable)
|
||||
public static void SetIsBorderless(this Window window, bool enable)
|
||||
{
|
||||
var window = GetWindow<T>() as Window;
|
||||
if (window == null) return;
|
||||
if (_defaultWindowStyle is WindowStyle style)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
window.SetWindowStyle(WindowStyle.Popup | WindowStyle.Visible);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.SetWindowStyle(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetIsFullscreen(this Window window, bool enable, bool defaultExtendsContentIntoTitleBar = true)
|
||||
{
|
||||
if (window.AppWindow == null) return false;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
window.ExtendsContentIntoTitleBar = false;
|
||||
window.AppWindow.SetPresenter(AppWindowPresenterKind.FullScreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.ExtendsContentIntoTitleBar = defaultExtendsContentIntoTitleBar;
|
||||
window.AppWindow.SetPresenter(AppWindowPresenterKind.Overlapped);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool SetIsMaximized(this Window window, bool enable)
|
||||
{
|
||||
if (window.AppWindow == null) return false;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
window.Maximize();
|
||||
}
|
||||
else
|
||||
{
|
||||
window.Restore();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void SetIsShowInSwitchers(this Window window, bool enable)
|
||||
{
|
||||
if (window.AppWindow == null) return;
|
||||
|
||||
window.AppWindow.IsShownInSwitchers = enable;
|
||||
}
|
||||
|
||||
public static void SetIsAlwaysOnTop<T>(bool enable)
|
||||
public static void SetIsAlwaysOnTop(this Window window, bool enable)
|
||||
{
|
||||
var window = GetWindow<T>() as Window;
|
||||
if (window == null) return;
|
||||
if (window.AppWindow == null) return;
|
||||
|
||||
if (window.AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
@@ -290,36 +330,27 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
}
|
||||
}
|
||||
|
||||
public static void MoveAndResize<T>(Rect rect)
|
||||
public static void MoveAndResize(this Window window, Rect rect)
|
||||
{
|
||||
var window = GetWindow<T>() as Window;
|
||||
if (window == null) return;
|
||||
if (window.AppWindow == null) return;
|
||||
|
||||
window.AppWindow.Move(new Windows.Graphics.PointInt32((int)rect.X, (int)rect.Y));
|
||||
window.AppWindow.Resize(new Windows.Graphics.SizeInt32((int)rect.Width, (int)rect.Height));
|
||||
}
|
||||
|
||||
public static void SetTitleBarArea<T>(TitleBarArea titleBarArea)
|
||||
public static void SetTitleBarArea(this NowPlayingWindow window, TitleBarArea titleBarArea)
|
||||
{
|
||||
if (typeof(T) == typeof(NowPlayingWindow))
|
||||
{
|
||||
NowPlayingWindow? lyricsWindow = GetWindow<NowPlayingWindow>();
|
||||
lyricsWindow?.SetTitleBarArea(titleBarArea);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Unsupported window type: {typeof(T).FullName}");
|
||||
}
|
||||
window.SetTitleBarArea(titleBarArea);
|
||||
}
|
||||
|
||||
private static void RegisterWorkArea(IntPtr hwnd)
|
||||
private static void RegisterWorkArea(IntPtr hwnd, LyricsWindowStatus status)
|
||||
{
|
||||
if (_workAreas.Contains(hwnd)) return;
|
||||
|
||||
var uEdge = _liveStatesService.LiveStates.LyricsWindowStatus.DockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
|
||||
var uEdge = status.DockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
|
||||
|
||||
double top = _liveStatesService.LiveStates.LyricsWindowStatus.DockPlacement == DockPlacement.Top ? _liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Top : _liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Bottom - _liveStatesService.LiveStates.LyricsWindowStatus.DockHeight;
|
||||
double bottom = top + _liveStatesService.LiveStates.LyricsWindowStatus.DockHeight;
|
||||
double top = status.DockPlacement == DockPlacement.Top ? status.MonitorBounds.Top : status.MonitorBounds.Bottom - status.DockHeight;
|
||||
double bottom = top + status.DockHeight;
|
||||
|
||||
Shell32.APPBARDATA abd = new()
|
||||
{
|
||||
@@ -328,9 +359,9 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
uEdge = uEdge,
|
||||
rc = new RECT
|
||||
{
|
||||
Left = (int)_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Left,
|
||||
Left = (int)status.MonitorBounds.Left,
|
||||
Top = (int)top,
|
||||
Right = (int)_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Right,
|
||||
Right = (int)status.MonitorBounds.Right,
|
||||
Bottom = (int)bottom,
|
||||
},
|
||||
};
|
||||
@@ -358,23 +389,22 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
_workAreas.Remove(hwnd);
|
||||
}
|
||||
|
||||
public static void UpdateWorkArea<T>()
|
||||
public static void UpdateWorkArea(this NowPlayingWindow window)
|
||||
{
|
||||
var window = GetWindow<T>() as Window;
|
||||
if (window == null) return;
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
if (!_workAreas.Contains(hwnd))
|
||||
return;
|
||||
|
||||
var uEdge = _liveStatesService.LiveStates.LyricsWindowStatus.DockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
|
||||
var status = window.LyricsWindowStatus;
|
||||
|
||||
double top = _liveStatesService.LiveStates.LyricsWindowStatus.DockPlacement == DockPlacement.Top ?
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Top :
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Bottom - _liveStatesService.LiveStates.LyricsWindowStatus.DockHeight;
|
||||
var uEdge = status.DockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
|
||||
|
||||
double bottom = top + _liveStatesService.LiveStates.LyricsWindowStatus.DockHeight;
|
||||
double top = status.DockPlacement == DockPlacement.Top ?
|
||||
status.MonitorBounds.Top :
|
||||
status.MonitorBounds.Bottom - status.DockHeight;
|
||||
|
||||
double bottom = top + status.DockHeight;
|
||||
|
||||
Shell32.APPBARDATA abd = new()
|
||||
{
|
||||
@@ -383,9 +413,9 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
uEdge = uEdge,
|
||||
rc = new RECT
|
||||
{
|
||||
Left = (int)_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Left,
|
||||
Left = (int)status.MonitorBounds.Left,
|
||||
Top = (int)top,
|
||||
Right = (int)_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Right,
|
||||
Right = (int)status.MonitorBounds.Right,
|
||||
Bottom = (int)bottom,
|
||||
},
|
||||
};
|
||||
@@ -398,37 +428,32 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="dispatcherQueue">请确保此参数指向同一个对象,建议传值 BaseViewModel._dispatcherQueue</param>
|
||||
public static void SetLyricsWindowVisibilityByPlayingStatus(DispatcherQueue dispatcherQueue)
|
||||
public static void SetLyricsWindowVisibilityByPlayingStatus(this NowPlayingWindow window, bool isPlaying, DispatcherQueue dispatcherQueue)
|
||||
{
|
||||
_setLyricsWindowVisibilityByPlayingStatusTimer ??= dispatcherQueue.CreateTimer();
|
||||
var status = window.LyricsWindowStatus;
|
||||
|
||||
_setLyricsWindowVisibilityByPlayingStatusTimer.Debounce(() =>
|
||||
status.VisibilityTimer ??= dispatcherQueue.CreateTimer();
|
||||
|
||||
status.VisibilityTimer.Debounce(() =>
|
||||
{
|
||||
var window = GetWindow<NowPlayingWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.CurrentIsPlaying)
|
||||
if (status.AutoShowOrHideWindow && !isPlaying)
|
||||
{
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
if (status.IsWorkArea)
|
||||
{
|
||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = true;
|
||||
SetIsWorkArea<NowPlayingWindow>(false);
|
||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = false;
|
||||
window.SetIsWorkArea(false);
|
||||
}
|
||||
HideWindow<NowPlayingWindow>();
|
||||
window.HideWindow();
|
||||
}
|
||||
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.CurrentIsPlaying)
|
||||
else if (status.AutoShowOrHideWindow && isPlaying)
|
||||
{
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
if (status.IsWorkArea)
|
||||
{
|
||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = true;
|
||||
SetIsWorkArea<NowPlayingWindow>(true);
|
||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = false;
|
||||
window.SetIsWorkArea(true);
|
||||
}
|
||||
OpenOrShowWindow<NowPlayingWindow>();
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
OpenOrShowWindow<NowPlayingWindow>(status);
|
||||
if (status.IsWorkArea)
|
||||
{
|
||||
MoveAndResize<NowPlayingWindow>(_liveStatesService.LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
|
||||
window.MoveAndResize(status.GetWindowBoundsWhenWorkArea());
|
||||
}
|
||||
}
|
||||
}, Constants.Time.DebounceTimeout);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Windows.UI;
|
||||
|
||||
@@ -13,12 +14,14 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
private readonly double _highlightedScale = 1.0f;
|
||||
|
||||
public void UpdateLines(
|
||||
LyricsData? lyricsData,
|
||||
IList<RenderLyricsLine>? lines,
|
||||
int startIndex,
|
||||
int endIndex,
|
||||
int playingLineIndex,
|
||||
double canvasHeight,
|
||||
double targetYScrollOffset,
|
||||
double playingLineTopOffsetFactor,
|
||||
LyricsStyleSettings lyricsStyle,
|
||||
LyricsEffectSettings lyricsEffect,
|
||||
ValueTransition<double> canvasYScrollTransition,
|
||||
Color bgColor,
|
||||
@@ -30,14 +33,18 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
bool isMouseScrollingChanged
|
||||
)
|
||||
{
|
||||
if (lyricsData == null) return;
|
||||
if (lines == null) return;
|
||||
|
||||
var currentPlayingLine = lyricsData.LyricsLines.ElementAtOrDefault(playingLineIndex);
|
||||
var currentPlayingLine = lines.ElementAtOrDefault(playingLineIndex);
|
||||
if (currentPlayingLine == null) return;
|
||||
|
||||
var phoneticOpacity = lyricsStyle.PhoneticLyricsOpacity / 100.0;
|
||||
var originalOpacity = lyricsStyle.OriginalLyricsOpacity / 100.0;
|
||||
var translatedOpacity = lyricsStyle.TranslatedLyricsOpacity / 100.0;
|
||||
|
||||
for (int i = startIndex; i <= endIndex + 1; i++)
|
||||
{
|
||||
var line = lyricsData.LyricsLines.ElementAtOrDefault(i);
|
||||
var line = lines.ElementAtOrDefault(i);
|
||||
if (line == null) continue;
|
||||
|
||||
if (isLayoutChanged || isPlayingLineChanged || isMouseScrollingChanged)
|
||||
@@ -45,7 +52,16 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
int lineCountDelta = i - playingLineIndex;
|
||||
int absLineCountDelta = Math.Abs(lineCountDelta);
|
||||
double distanceFromPlayingLine = Math.Abs(line.OriginalPosition.Y - currentPlayingLine.OriginalPosition.Y);
|
||||
double distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight / 2), 0, 1);
|
||||
|
||||
double distanceFactor = 0;
|
||||
if (lineCountDelta < 0)
|
||||
{
|
||||
distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight * playingLineTopOffsetFactor), 0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight * (1 - playingLineTopOffsetFactor)), 0, 1);
|
||||
}
|
||||
|
||||
double yScrollDuration;
|
||||
double yScrollDelay;
|
||||
@@ -72,27 +88,34 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
|
||||
line.BlurAmountTransition.SetDuration(yScrollDuration);
|
||||
line.BlurAmountTransition.SetDelay(yScrollDelay);
|
||||
line.BlurAmountTransition.StartTransition(isMouseScrolling ? 0 : (5 * distanceFactor));
|
||||
line.BlurAmountTransition.StartTransition(isMouseScrolling ? 0 : (lyricsEffect.IsLyricsBlurEffectEnabled ? (5 * distanceFactor) : 0));
|
||||
|
||||
line.ScaleTransition.SetDuration(yScrollDuration);
|
||||
line.ScaleTransition.SetDelay(yScrollDelay);
|
||||
line.ScaleTransition.StartTransition(_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale));
|
||||
line.ScaleTransition.StartTransition(
|
||||
lyricsEffect.IsLyricsOutOfSightEffectEnabled ?
|
||||
(_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale)) :
|
||||
_highlightedScale);
|
||||
|
||||
line.PhoneticOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.PhoneticOpacityTransition.SetDelay(yScrollDelay);
|
||||
line.PhoneticOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.6 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||
line.PhoneticOpacityTransition.StartTransition(
|
||||
CalculateTargetOpacity(phoneticOpacity, phoneticOpacity, distanceFactor, isMouseScrolling, lyricsEffect));
|
||||
|
||||
line.PlayedOriginalOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.PlayedOriginalOpacityTransition.SetDelay(yScrollDelay);
|
||||
line.PlayedOriginalOpacityTransition.StartTransition(absLineCountDelta == 0 ? 1 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||
line.PlayedOriginalOpacityTransition.StartTransition(
|
||||
CalculateTargetOpacity(originalOpacity, 1.0, distanceFactor, isMouseScrolling, lyricsEffect));
|
||||
|
||||
line.UnplayedOriginalOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.UnplayedOriginalOpacityTransition.SetDelay(yScrollDelay);
|
||||
line.UnplayedOriginalOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.3 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||
line.UnplayedOriginalOpacityTransition.StartTransition(
|
||||
CalculateTargetOpacity(originalOpacity, originalOpacity, distanceFactor, isMouseScrolling, lyricsEffect));
|
||||
|
||||
line.TranslatedOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.TranslatedOpacityTransition.SetDelay(yScrollDelay);
|
||||
line.TranslatedOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.6 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||
line.TranslatedOpacityTransition.StartTransition(
|
||||
CalculateTargetOpacity(translatedOpacity, translatedOpacity, distanceFactor, isMouseScrolling, lyricsEffect));
|
||||
|
||||
line.ColorTransition.SetDuration(yScrollDuration);
|
||||
line.ColorTransition.SetDelay(yScrollDelay);
|
||||
@@ -101,8 +124,10 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
line.AngleTransition.SetEasingType(canvasYScrollTransition.EasingType);
|
||||
line.AngleTransition.SetDuration(yScrollDuration);
|
||||
line.AngleTransition.SetDelay(yScrollDelay);
|
||||
line.AngleTransition.StartTransition(lyricsEffect.IsFanLyricsEnabled ?
|
||||
Math.PI * (lyricsEffect.FanLyricsAngle / 180.0) * distanceFactor * (i > playingLineIndex ? 1 : -1) : 0);
|
||||
line.AngleTransition.StartTransition(
|
||||
(lyricsEffect.IsFanLyricsEnabled && !isMouseScrolling) ?
|
||||
Math.PI * (lyricsEffect.FanLyricsAngle / 180.0) * distanceFactor * (i > playingLineIndex ? 1 : -1) :
|
||||
0);
|
||||
|
||||
line.YOffsetTransition.SetEasingType(canvasYScrollTransition.EasingType);
|
||||
line.YOffsetTransition.SetDuration(yScrollDuration);
|
||||
@@ -123,5 +148,33 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
line.ColorTransition.Update(elapsedTime);
|
||||
}
|
||||
}
|
||||
|
||||
private static double CalculateTargetOpacity(double baseOpacity, double baseOpacityWhenZeroDistanceFactor, double distanceFactor, bool isMouseScrolling, LyricsEffectSettings lyricsEffect)
|
||||
{
|
||||
double targetOpacity;
|
||||
if (distanceFactor == 0)
|
||||
{
|
||||
targetOpacity = baseOpacityWhenZeroDistanceFactor;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isMouseScrolling)
|
||||
{
|
||||
targetOpacity = baseOpacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lyricsEffect.IsLyricsFadeOutEffectEnabled)
|
||||
{
|
||||
targetOpacity = (1 - distanceFactor) * baseOpacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetOpacity = baseOpacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
return targetOpacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
/// <param name="canvasHeight"></param>
|
||||
/// <param name="lyricsWidth"></param>
|
||||
/// <param name="lyricsHeight"></param>
|
||||
public void MeasureAndArrange(
|
||||
public static void MeasureAndArrange(
|
||||
ICanvasAnimatedControl resourceCreator,
|
||||
LyricsData? lyricsData,
|
||||
IList<RenderLyricsLine>? lines,
|
||||
LyricsWindowStatus status,
|
||||
AppSettings appSettings,
|
||||
double canvasWidth,
|
||||
@@ -33,7 +33,7 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
double lyricsWidth,
|
||||
double lyricsHeight)
|
||||
{
|
||||
if (lyricsData == null || resourceCreator == null) return;
|
||||
if (lines == null || resourceCreator == null) return;
|
||||
|
||||
// 计算字体大小
|
||||
int originalFontSize, phoneticFontSize, translatedFontSize;
|
||||
@@ -60,7 +60,7 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
double currentY = 0;
|
||||
double actualWidth = 0;
|
||||
|
||||
foreach (var line in lyricsData.LyricsLines)
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line == null) continue;
|
||||
|
||||
@@ -129,11 +129,10 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
/// <summary>
|
||||
/// 计算为了让当前歌词行的竖直几何中心点对齐到 0(原点),画布应该移动的距离(从画布最初始状态计算的值)
|
||||
/// </summary>
|
||||
public double? CalculateTargetScrollOffset(
|
||||
LyricsData? lyricsData,
|
||||
public static double? CalculateTargetScrollOffset(
|
||||
IList<RenderLyricsLine>? lines,
|
||||
int playingLineIndex)
|
||||
{
|
||||
var lines = lyricsData?.LyricsLines;
|
||||
if (lines == null || lines.Count == 0) return null;
|
||||
|
||||
var currentLine = lines.ElementAtOrDefault(playingLineIndex);
|
||||
@@ -141,27 +140,26 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
|
||||
if (currentLine?.OriginalCanvasTextLayout == null || firstLine == null) return null;
|
||||
|
||||
return -currentLine.OriginalPosition.Y
|
||||
+ firstLine.OriginalPosition.Y
|
||||
- (currentLine.TranslatedPosition.Y
|
||||
+ (currentLine.TranslatedCanvasTextLayout?.LayoutBounds.Height ?? 0)
|
||||
- currentLine.PhoneticPosition.Y) / 2.0;
|
||||
return -currentLine.OriginalPosition.Y + firstLine.OriginalPosition.Y
|
||||
- (currentLine.BottomRightPosition.Y - currentLine.TopLeftPosition.Y) / 2.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算当前屏幕可见的行范围
|
||||
/// 返回值: (StartVisibleIndex, EndVisibleIndex)
|
||||
/// </summary>
|
||||
public (int Start, int End) CalculateVisibleRange(
|
||||
IList<LyricsLine>? lines,
|
||||
public static (int Start, int End) CalculateVisibleRange(
|
||||
IList<RenderLyricsLine>? lines,
|
||||
double currentScrollOffset,
|
||||
double lyricsY,
|
||||
double lyricsHeight,
|
||||
double canvasHeight)
|
||||
double canvasHeight,
|
||||
double playingLineTopOffsetFactor
|
||||
)
|
||||
{
|
||||
if (lines == null || lines.Count == 0) return (-1, -1);
|
||||
|
||||
double offset = currentScrollOffset + lyricsY + lyricsHeight / 2;
|
||||
double offset = currentScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
|
||||
|
||||
int start = FindFirstVisibleLine(lines, offset, lyricsY);
|
||||
int end = FindLastVisibleLine(lines, offset, lyricsY, lyricsHeight, canvasHeight);
|
||||
@@ -175,34 +173,35 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
return (start, end);
|
||||
}
|
||||
|
||||
public (int Start, int End) CalculateMaxRange(IList<LyricsLine>? lines)
|
||||
public static (int Start, int End) CalculateMaxRange(IList<RenderLyricsLine>? lines)
|
||||
{
|
||||
if (lines == null || lines.Count == 0) return (-1, -1);
|
||||
|
||||
return (0, lines.Count - 1);
|
||||
}
|
||||
|
||||
public double CalculateActualHeight(IList<LyricsLine>? lines)
|
||||
public static double CalculateActualHeight(IList<RenderLyricsLine>? lines)
|
||||
{
|
||||
if (lines == null || lines.Count == 0) return 0;
|
||||
|
||||
return lines.Last().BottomRightPosition.Y;
|
||||
}
|
||||
|
||||
public int FindMouseHoverLineIndex(
|
||||
IList<LyricsLine>? lines,
|
||||
public static int FindMouseHoverLineIndex(
|
||||
IList<RenderLyricsLine>? lines,
|
||||
bool isMouseInLyricsArea,
|
||||
Point mousePosition,
|
||||
double currentScrollOffset,
|
||||
double lyricsY,
|
||||
double lyricsHeight
|
||||
double lyricsHeight,
|
||||
double playingLineTopOffsetFactor
|
||||
)
|
||||
{
|
||||
if (!isMouseInLyricsArea) return -1;
|
||||
|
||||
if (lines == null || lines.Count == 0) return -1;
|
||||
|
||||
double offset = currentScrollOffset + lyricsY + lyricsHeight / 2;
|
||||
double offset = currentScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
|
||||
|
||||
int left = 0, right = lines.Count - 1, result = -1;
|
||||
while (left <= right)
|
||||
@@ -214,10 +213,21 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
if (value >= mousePosition.Y) { result = mid; right = mid - 1; }
|
||||
else { left = mid + 1; }
|
||||
}
|
||||
|
||||
if (result != -1)
|
||||
{
|
||||
var line = lines[result];
|
||||
double lineTopY = offset + line.TopLeftPosition.Y;
|
||||
if (mousePosition.Y < lineTopY)
|
||||
{
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int FindFirstVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY)
|
||||
private static int FindFirstVisibleLine(IList<RenderLyricsLine> lines, double offset, double lyricsY)
|
||||
{
|
||||
int left = 0, right = lines.Count - 1, result = -1;
|
||||
while (left <= right)
|
||||
@@ -234,7 +244,7 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
return result;
|
||||
}
|
||||
|
||||
private int FindLastVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY, double lyricsHeight, double canvasHeight)
|
||||
private static int FindLastVisibleLine(IList<RenderLyricsLine> lines, double offset, double lyricsY, double lyricsHeight, double canvasHeight)
|
||||
{
|
||||
int left = 0, right = lines.Count - 1, result = -1;
|
||||
while (left <= right)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class CutletDockerRequest
|
||||
{
|
||||
[JsonPropertyName("text")]
|
||||
public string Text { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class CutletDockerResponse
|
||||
{
|
||||
[JsonPropertyName("romaji")]
|
||||
public string RomajiText { get; set; }
|
||||
}
|
||||
}
|
||||
224
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/ExtendedTrack.cs
Normal file
224
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/ExtendedTrack.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class ExtendedTrack
|
||||
{
|
||||
public string Uri { get; private set; } = "";
|
||||
|
||||
public string DecodedAbsoluteUri
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(Uri)) return "";
|
||||
try
|
||||
{
|
||||
var u = new Uri(Uri);
|
||||
return u.IsFile ? u.LocalPath : System.Net.WebUtility.UrlDecode(u.AbsoluteUri);
|
||||
}
|
||||
catch { return Uri; }
|
||||
}
|
||||
}
|
||||
|
||||
public string? RawLyrics { get; set; }
|
||||
public string? LocalAlbumArtPath { get; set; }
|
||||
public byte[]? AlbumArtByteArray { get; set; }
|
||||
|
||||
public string ParentFolderName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(Uri)) return "";
|
||||
try
|
||||
{
|
||||
// 使用 Uri Segments 安全获取倒数第二层 (文件夹名)
|
||||
// Segments 示例: "/", "Music/", "Artist/", "Song.mp3"
|
||||
var u = new System.Uri(Uri);
|
||||
if (u.Segments.Length > 1)
|
||||
{
|
||||
// 取倒数第二个 segment (如果是文件)
|
||||
// 注意处理末尾斜杠
|
||||
string folder = u.Segments[u.Segments.Length - 2];
|
||||
return System.Net.WebUtility.UrlDecode(folder.TrimEnd('/', '\\'));
|
||||
}
|
||||
return "";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ParentFolderPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(Uri)) return "";
|
||||
try
|
||||
{
|
||||
var u = new System.Uri(Uri);
|
||||
if (u.IsFile)
|
||||
{
|
||||
// 本地文件:返回目录路径 C:\Music
|
||||
return System.IO.Path.GetDirectoryName(u.LocalPath) ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
// 远程文件:返回去掉文件名的 URI
|
||||
// new Uri(u, ".") 表示当前目录
|
||||
return new System.Uri(u, ".").AbsoluteUri;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string FileName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(Uri)) return "";
|
||||
try
|
||||
{
|
||||
var u = new System.Uri(Uri);
|
||||
if (u.IsFile) return System.IO.Path.GetFileName(u.LocalPath);
|
||||
|
||||
// 远程文件:获取 AbsolutePath 的最后一段并解码
|
||||
// 例如: /Music/My%20Song.mp3 -> My Song.mp3
|
||||
string rawName = System.IO.Path.GetFileName(u.AbsolutePath);
|
||||
return System.Net.WebUtility.UrlDecode(rawName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return System.IO.Path.GetFileName(Uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
public string MediaFolderId { get; set; } = "";
|
||||
|
||||
public string Title { get; set; } = "";
|
||||
public string Artist { get; set; } = "";
|
||||
public string Album { get; set; } = "";
|
||||
public int? Year { get; set; }
|
||||
public int Bitrate { get; set; }
|
||||
public double SampleRate { get; set; }
|
||||
public int BitDepth { get; set; }
|
||||
public int Duration { get; set; }
|
||||
public string AudioFormatName { get; set; } = "";
|
||||
public string AudioFormatShortName { get; set; } = "";
|
||||
public string Encoder { get; set; } = "";
|
||||
|
||||
|
||||
public ExtendedTrack() : base() { }
|
||||
|
||||
public ExtendedTrack(string uriString) : base()
|
||||
{
|
||||
Uri = uriString;
|
||||
|
||||
string atlPath = uriString;
|
||||
try
|
||||
{
|
||||
var u = new Uri(uriString);
|
||||
if (u.IsFile) atlPath = u.LocalPath;
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 用于本地文件
|
||||
var track = new Track(atlPath);
|
||||
SetFromTrack(track);
|
||||
}
|
||||
|
||||
public ExtendedTrack(FileCacheEntity? entity, Stream? stream = null) : base()
|
||||
{
|
||||
if (entity == null) return;
|
||||
|
||||
this.MediaFolderId = entity.MediaFolderId;
|
||||
this.Uri = entity.Uri;
|
||||
|
||||
this.Title = entity.Title;
|
||||
this.Artist = entity.Artists;
|
||||
this.Album = entity.Album;
|
||||
this.Year = entity.Year;
|
||||
this.Bitrate = entity.Bitrate;
|
||||
this.SampleRate = entity.SampleRate;
|
||||
this.BitDepth = entity.BitDepth;
|
||||
|
||||
this.Duration = entity.Duration;
|
||||
|
||||
this.AudioFormatName = entity.AudioFormatName;
|
||||
this.AudioFormatShortName = entity.AudioFormatShortName;
|
||||
|
||||
this.Encoder = entity.Encoder;
|
||||
|
||||
this.RawLyrics = entity.EmbeddedLyrics;
|
||||
this.LocalAlbumArtPath = entity.LocalAlbumArtPath;
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
var track = new Track(stream, Path.GetExtension(FileName));
|
||||
SetFromTrack(track);
|
||||
SetRawLyrics(new StreamFileAbstraction(Uri, stream));
|
||||
}
|
||||
}
|
||||
|
||||
private void SetFromTrack(Track? track)
|
||||
{
|
||||
if (track == null) return;
|
||||
|
||||
this.Title = track.Title;
|
||||
this.Artist = track.Artist;
|
||||
this.Album = track.Album;
|
||||
this.Year = track.Year;
|
||||
this.Bitrate = track.Bitrate;
|
||||
this.SampleRate = track.SampleRate;
|
||||
this.BitDepth = track.BitDepth;
|
||||
|
||||
this.Duration = track.Duration;
|
||||
|
||||
this.AudioFormatName = track.AudioFormat.Name;
|
||||
this.AudioFormatShortName = track.AudioFormat.ShortName;
|
||||
|
||||
this.Encoder = track.Encoder;
|
||||
|
||||
this.AlbumArtByteArray = null;
|
||||
|
||||
if (track.EmbeddedPictures != null && track.EmbeddedPictures.Count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
var validPics = track.EmbeddedPictures.Where(p => p != null).ToList();
|
||||
|
||||
if (validPics.Count > 0)
|
||||
{
|
||||
var cover = validPics.FirstOrDefault(p => p.PicType == PictureInfo.PIC_TYPE.Front);
|
||||
|
||||
if (cover == null)
|
||||
{
|
||||
cover = validPics.First();
|
||||
}
|
||||
|
||||
this.AlbumArtByteArray = cover.PictureData;
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
|
||||
private void SetRawLyrics(StreamFileAbstraction streamFileAbstraction)
|
||||
{
|
||||
try
|
||||
{
|
||||
RawLyrics = TagLib.File.Create(streamFileAbstraction).Tag.Lyrics;
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using SQLite;
|
||||
using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
[Table("FileCache")]
|
||||
public class FileCacheEntity
|
||||
{
|
||||
[PrimaryKey, AutoIncrement]
|
||||
public int Id { get; set; }
|
||||
|
||||
// 【新增】关键字段!
|
||||
// 关联到 MediaFolder.Id。
|
||||
// 作用:
|
||||
// 1. 区分不同配置(即使两个配置连的是同一个 SMB,但在 APP 里视为不同源)。
|
||||
// 2. 删除配置时,可以由 MediaFolderId 快速级联删除所有缓存。
|
||||
[Indexed]
|
||||
public string MediaFolderId { get; set; }
|
||||
|
||||
// 【修改】从 ParentPath 改为 ParentUri
|
||||
// 存储父文件夹的标准 URI (smb://host/share/parent)
|
||||
// 根目录文件的 ParentUri 可以为空,或者等于 MediaFolder 的 Base Uri
|
||||
[Indexed]
|
||||
public string? ParentUri { get; set; }
|
||||
|
||||
// 【核心】标准化的完整 URI (smb://host/share/folder/file.ext)
|
||||
// 确保它是 URL 编码过且格式统一的
|
||||
[Indexed(Unique = true)]
|
||||
public string Uri { get; set; }
|
||||
|
||||
public string FileName { get; set; } = "";
|
||||
|
||||
public bool IsDirectory { get; set; }
|
||||
|
||||
// 记录文件大小,同步时用来对比文件是否变化
|
||||
public long FileSize { get; set; }
|
||||
|
||||
// 记录修改时间,同步时对比使用
|
||||
public DateTime? LastModified { get; set; }
|
||||
|
||||
// ------ 元数据部分 (保持不变) ------
|
||||
public string Title { get; set; } = "";
|
||||
public string Artists { get; set; } = "";
|
||||
public string Album { get; set; } = "";
|
||||
public int? Year { get; set; }
|
||||
public int Bitrate { get; set; }
|
||||
public double SampleRate { get; set; }
|
||||
public int BitDepth { get; set; }
|
||||
public int Duration { get; set; }
|
||||
public string AudioFormatName { get; set; } = "";
|
||||
public string AudioFormatShortName { get; set; } = "";
|
||||
public string Encoder { get; set; } = "";
|
||||
public string? EmbeddedLyrics { get; set; }
|
||||
public string? LocalAlbumArtPath { get; set; }
|
||||
public bool IsMetadataParsed { get; set; }
|
||||
}
|
||||
}
|
||||
24
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/FolderNode.cs
Normal file
24
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/FolderNode.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public partial class FolderNode : ObservableObject
|
||||
{
|
||||
public FileSourceType SourceType { get; set; } = FileSourceType.Local;
|
||||
|
||||
public string FolderName { get; set; } = "";
|
||||
|
||||
public string FolderPath { get; set; } = "";
|
||||
|
||||
public string MediaFolderId { get; set; } = "";
|
||||
|
||||
public ObservableCollection<FolderNode> SubFolders { get; set; } = new();
|
||||
|
||||
[ObservableProperty] public partial bool IsExpanded { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class TranslateResponse
|
||||
public class LibreTranslateResponse
|
||||
{
|
||||
[JsonPropertyName("translatedText")]
|
||||
public string TranslatedText { get; set; }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user