mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
2 Commits
feat/IFile
...
feat/wikis
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b86783493 | ||
|
|
e3adc2e595 |
31
README.md
31
README.md
@@ -1,36 +1,41 @@
|
||||

|
||||

|
||||
|
||||
|
||||
胡桃工具箱是一款以 MIT 协议开源的原神工具箱,专为现代化 Windows 平台设计,旨在改善桌面端玩家的游戏体验。通过将既有的官方资源与开发团队设计的全新功能相结合,提供了一套完整且实用的工具集,且无需依赖任何移动设备。它不对游戏客户端进行任何破坏性修改以确保工具箱的安全性
|
||||
胡桃工具箱是一款以 MIT 协议开源的原神工具箱,专为现代化 Windows 平台设计,旨在改善桌面端玩家的游戏体验。通过将既有的官方资源与开发团队设计的全新 功能相结合,它提供了一套完整且实用的工具集,且无需依赖任何移动设备。它不对游戏客户端进行任何破坏性修改以确保工具箱的安全性
|
||||
|
||||
Snap Hutao is an open-source Genshin Impact toolkit under MIT license, designed for modern Windows platform to improve the gaming experience for desktop players. By combining existing official resources with new features designed by the development team, it provides a complete and useful set of tools without the need to rely on mobile devices. Snap Hutao does not take any destructive modification to the game client to ensure the security of the toolkit.
|
||||
|
||||
## 安装 / Installation
|
||||
## 下载使用 / Download
|
||||
|
||||
 [](https://github.com/DGP-Studio/Snap.Hutao/releases/latest) []()
|
||||
|
||||
---
|
||||
|
||||
你可以按照[快速开始](https://hut.ao/zh/quick-start.html)文档中提供的流程安装并设置 Snap Hutao。
|
||||
#### 使用安装器安装 / Install with Snap.Hutao.Depolyment Installer
|
||||
|
||||
You can follow the instructions in the [Quick Start](https://hut.ao/en/quick-start.html) document to install and set up Snap Hutao.
|
||||
Snap.Hutao.Depolyment 是一个由 DGP-Studio 重新包装的 Windows 应用安装器,适用于缺少专业计算机知识的一般用户,可以在安装时同时解决缺少必要系统环境的问题。
|
||||
|
||||
## 本地化翻译 / Localization
|
||||
Snap.Hutao.Depolyment is a Windows application installer repackaged by DGP-Studio for the users who lacks computer knowledge and can solve the problem of missing necessary system environment at the same time as the installation.
|
||||
|
||||
].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15670597-565845.json) ].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15670597-565845.json) ].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15670597-565845.json) ].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15670597-565845.json) ].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15670597-565845.json)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15670597-565845.json) ].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15670597-565845.json)
|
||||
[从 GitHub 发布页获取 / Download from GitHub release](https://github.com/DGP-Studio/Snap.Hutao.Deployment/releases/latest)
|
||||
|
||||
Snap Hutao 使用 [Crowdin](https://translate.hut.ao/) 作为客户端文本翻译平台,在该平台上你可以为你熟悉的语言提交翻译文本。我们感谢每一个为 Snap Hutao 做出贡献的社区成员,并且欢迎更多的朋友能参与到这个项目中。
|
||||
[从极狐Lab 发布页获取 / Download from Jihu Gitlab release](https://jihulab.com/DGP-Studio/Snap.Hutao.Deployment/-/releases)
|
||||
|
||||
Snap Hutao uses [Crowdin](https://translate.hut.ao/) as a client text translation platform where you can submit translated text for languages you are familiar with. We are grateful to every community member who has contributed to Snap Hutao and welcome more friends to participate in this project.
|
||||
#### 使用 MSIX 包安装 / Install with MSIX Package
|
||||
|
||||
## 社区 / Community
|
||||
直接使用 Snap Hutao MSIX 安装包,使用 Windows 内置的 App Installer 即可安装。如在安装中出现问题,请查阅我们的[常见问题](https://hut.ao/zh/advanced/FAQ.html)文档
|
||||
|
||||
[](https://discord.gg/CcH5XtDtvR) [](https://qm.qq.com/q/WJKykrY9W)
|
||||
Install with Snap Hutao MSIX package, can be installed with Windows built-in App Installer. If you faced any issue, please check our [FAQ](https://hut.ao/en/advanced/FAQ.html) document.
|
||||
|
||||
[从 GitHub 发布页获取 / Download from GitHub release](https://github.com/DGP-Studio/Snap.Hutao/releases/latest)
|
||||
|
||||
[从极狐Lab 发布页获取 / Download from Jihu Gitlab release](https://jihulab.com/DGP-Studio/Snap.Hutao/-/releases)
|
||||
|
||||
## 贡献 / Contribute
|
||||
|
||||
* [向我们提交 PR / Make Pull Requests](https://hut.ao/development/contribute.html)
|
||||
* [为我们更新文档 / Enhance our Document](https://github.com/DGP-Studio/Snap.Hutao.Docs)
|
||||
* [向我们提交 PR / Make Pull Requests](https://github.com/DGP-Studio/Snap.Hutao/pulls)
|
||||
* [在 Crowdin 上进行本地化 / Translate Project on Crowdin](https://translate.hut.ao/)
|
||||
* [为我们更新文档 / Enhance our Document ](https://github.com/DGP-Studio/Snap.Hutao.Docs)
|
||||
|
||||
## 特别感谢 / Special Thanks
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| >=1.9.0 | :white_check_mark: |
|
||||
| <1.9.0 | :x: |
|
||||
| >=1.6.0 | :white_check_mark: |
|
||||
| <1.6.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
24
build.cake
24
build.cake
@@ -69,15 +69,6 @@ else if (AppVeyor.IsRunningOnAppVeyor)
|
||||
})[..^2];
|
||||
Information($"Version: {version}");
|
||||
}
|
||||
else // Local
|
||||
{
|
||||
repoDir = System.Environment.CurrentDirectory;
|
||||
outputPath = System.IO.Path.Combine(repoDir, "src", "output");
|
||||
|
||||
version = System.DateTime.Now.ToString("yyyy.M.d.") + ((int)((System.DateTime.Now - System.DateTime.Today).TotalSeconds / 86400 * 65535)).ToString();
|
||||
|
||||
Information($"Version: {version}");
|
||||
}
|
||||
|
||||
Task("Build")
|
||||
.IsDependentOn("Build binary package")
|
||||
@@ -121,17 +112,6 @@ Task("Generate AppxManifest")
|
||||
Information("Using Release configuration");
|
||||
content = System.Text.RegularExpressions.Regex.Replace(content, " Publisher=\"([^\"]*)\"", " Publisher=\"CN=SignPath Foundation, O=SignPath Foundation, L=Lewes, S=Delaware, C=US\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
Information("Using Local configuration.");
|
||||
content = content
|
||||
.Replace("Snap Hutao", "Snap Hutao Local")
|
||||
.Replace("胡桃", "胡桃 Local")
|
||||
.Replace("DGP Studio", "DGP Studio CI");
|
||||
content = System.Text.RegularExpressions.Regex.Replace(content, " Name=\"([^\"]*)\"", " Name=\"E8B6E2B3-D2A0-4435-A81D-2A16AAF405C7\"");
|
||||
content = System.Text.RegularExpressions.Regex.Replace(content, " Publisher=\"([^\"]*)\"", " Publisher=\"E=admin@dgp-studio.cn, CN=DGP Studio CI, OU=CI, O=DGP-Studio, L=San Jose, S=CA, C=US\"");
|
||||
content = System.Text.RegularExpressions.Regex.Replace(content, " Version=\"([0-9\\.]+)\"", $" Version=\"{version}\"");
|
||||
}
|
||||
|
||||
System.IO.File.WriteAllText(manifest, content);
|
||||
|
||||
@@ -193,10 +173,6 @@ Task("Build MSIX")
|
||||
{
|
||||
arguments = "pack /d " + binPath + " /p " + System.IO.Path.Combine(outputPath, $"Snap.Hutao-{version}.msix");
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments = "pack /d " + binPath + " /p " + System.IO.Path.Combine(outputPath, $"Snap.Hutao.Local-{version}.msix");
|
||||
}
|
||||
var p = StartProcess(
|
||||
"makeappx.exe",
|
||||
new ProcessSettings
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -13,9 +13,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.2.2" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.2.2" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.2.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.2.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources/>
|
||||
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.TokenizingTextBox/TokenizingTextBox.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Loading.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Image/CachedImage.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Card.xaml"/>
|
||||
@@ -23,13 +22,11 @@
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/PageOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/PivotOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/ScrollViewer.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/SegmentedOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/SettingsStyle.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Thickness.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/TransitionCollection.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Uri.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/WindowOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///View/Card/Primitive/CardProgressBar.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style
|
||||
|
||||
@@ -7,11 +7,8 @@ using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Core.Shell;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using static Snap.Hutao.Core.Logging.ConsoleVirtualTerminalSequences;
|
||||
|
||||
namespace Snap.Hutao;
|
||||
|
||||
@@ -24,17 +21,17 @@ namespace Snap.Hutao;
|
||||
[SuppressMessage("", "SH001")]
|
||||
public sealed partial class App : Application
|
||||
{
|
||||
private const string ConsoleBanner = $"""
|
||||
private const string ConsoleBanner = """
|
||||
----------------------------------------------------------------
|
||||
_____ _ _ _
|
||||
/ ____| | | | | | |
|
||||
| (___ _ __ __ _ _ __ | |__| | _ _ | |_ __ _ ___
|
||||
\___ \ | '_ \ / _` || '_ \ | __ || | | || __|/ _` | / _ \
|
||||
_____ _ _ _
|
||||
/ ____| | | | | | |
|
||||
| (___ _ __ __ _ _ __ | |__| | _ _ | |_ __ _ ___
|
||||
\___ \ | '_ \ / _` || '_ \ | __ || | | || __|/ _` | / _ \
|
||||
____) || | | || (_| || |_) |_ | | | || |_| || |_| (_| || (_) |
|
||||
|_____/ |_| |_| \__,_|| .__/(_)|_| |_| \__,_| \__|\__,_| \___/
|
||||
| |
|
||||
|_|
|
||||
|
||||
|_____/ |_| |_| \__,_|| .__/(_)|_| |_| \__,_| \__|\__,_| \___/
|
||||
| |
|
||||
|_|
|
||||
|
||||
Snap.Hutao is a open source software developed by DGP Studio.
|
||||
Copyright (C) 2022 - 2024 DGP Studio, All Rights Reserved.
|
||||
----------------------------------------------------------------
|
||||
@@ -72,7 +69,7 @@ public sealed partial class App : Application
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogColorizedInformation((ConsoleBanner, ConsoleColor.DarkYellow));
|
||||
logger.LogInformation(ConsoleBanner);
|
||||
LogDiagnosticInformation();
|
||||
|
||||
// manually invoke
|
||||
@@ -92,8 +89,8 @@ public sealed partial class App : Application
|
||||
{
|
||||
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
|
||||
logger.LogColorizedInformation(("FamilyName: {Name}", ConsoleColor.Blue), (runtimeOptions.FamilyName, ConsoleColor.Cyan));
|
||||
logger.LogColorizedInformation(("Version: {Version}", ConsoleColor.Blue), (runtimeOptions.Version, ConsoleColor.Cyan));
|
||||
logger.LogColorizedInformation(("LocalCache: {Path}", ConsoleColor.Blue), (runtimeOptions.LocalCache, ConsoleColor.Cyan));
|
||||
logger.LogInformation("FamilyName: {name}", runtimeOptions.FamilyName);
|
||||
logger.LogInformation("Version: {version}", runtimeOptions.Version);
|
||||
logger.LogInformation("LocalCache: {folder}", runtimeOptions.LocalCache);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI;
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
using System.Collections;
|
||||
|
||||
namespace Snap.Hutao.Control.AutoSuggestBox;
|
||||
|
||||
[DependencyProperty("FilterCommand", typeof(ICommand))]
|
||||
[DependencyProperty("FilterCommandParameter", typeof(object))]
|
||||
[DependencyProperty("AvailableTokens", typeof(IReadOnlyDictionary<string, SearchToken>))]
|
||||
internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox
|
||||
{
|
||||
public AutoSuggestTokenBox()
|
||||
{
|
||||
DefaultStyleKey = typeof(TokenizingTextBox);
|
||||
TextChanged += OnFilterSuggestionRequested;
|
||||
QuerySubmitted += OnQuerySubmitted;
|
||||
TokenItemAdding += OnTokenItemAdding;
|
||||
TokenItemAdded += OnTokenItemCollectionChanged;
|
||||
TokenItemRemoved += OnTokenItemCollectionChanged;
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (this.FindDescendant("SuggestionsPopup") is Popup { Child: Border { Child: ListView listView } border })
|
||||
{
|
||||
IAppResourceProvider appResourceProvider = this.ServiceProvider().GetRequiredService<IAppResourceProvider>();
|
||||
|
||||
listView.Background = null;
|
||||
listView.Margin = appResourceProvider.GetResource<Thickness>("AutoSuggestListPadding");
|
||||
|
||||
border.Background = appResourceProvider.GetResource<Microsoft.UI.Xaml.Media.Brush>("AutoSuggestBoxSuggestionsListBackground");
|
||||
CornerRadius overlayCornerRadius = appResourceProvider.GetResource<CornerRadius>("OverlayCornerRadius");
|
||||
CornerRadiusFilterConverter cornerRadiusFilterConverter = new() { Filter = CornerRadiusFilterKind.Bottom };
|
||||
border.CornerRadius = (CornerRadius)cornerRadiusFilterConverter.Convert(overlayCornerRadius, typeof(CornerRadius), default, default);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFilterSuggestionRequested(Microsoft.UI.Xaml.Controls.AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Text))
|
||||
{
|
||||
sender.ItemsSource = AvailableTokens
|
||||
.OrderBy(kvp => kvp.Value.Kind)
|
||||
.Select(kvp => kvp.Value);
|
||||
}
|
||||
|
||||
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
|
||||
{
|
||||
sender.ItemsSource = AvailableTokens
|
||||
.Where(kvp => kvp.Value.Value.Contains(Text, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(kvp => kvp.Value.Kind)
|
||||
.ThenBy(kvp => kvp.Value.Order)
|
||||
.Select(kvp => kvp.Value)
|
||||
.DefaultIfEmpty(SearchToken.NotFound);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnQuerySubmitted(Microsoft.UI.Xaml.Controls.AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
|
||||
{
|
||||
if (args.ChosenSuggestion is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CommandInvocation.TryExecute(FilterCommand, FilterCommandParameter);
|
||||
}
|
||||
|
||||
private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(args.TokenText))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AvailableTokens.GetValueOrDefault(args.TokenText) is { } token)
|
||||
{
|
||||
args.Item = token;
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTokenItemCollectionChanged(TokenizingTextBox sender, object args)
|
||||
{
|
||||
if (args is SearchToken { Kind: SearchTokenKind.None } token)
|
||||
{
|
||||
((IList)sender.ItemsSource).Remove(token);
|
||||
}
|
||||
|
||||
FilterCommand.TryExecute(FilterCommandParameter);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Control.AutoSuggestBox;
|
||||
|
||||
internal sealed class SearchToken
|
||||
{
|
||||
public static readonly SearchToken NotFound = new(SearchTokenKind.None, SH.ControlAutoSuggestBoxNotFoundValue, 0);
|
||||
|
||||
public SearchToken(SearchTokenKind kind, string value, int order, Uri? iconUri = null, Uri? sideIconUri = null, Color? quality = null)
|
||||
{
|
||||
Value = value;
|
||||
Kind = kind;
|
||||
IconUri = iconUri;
|
||||
SideIconUri = sideIconUri;
|
||||
Quality = quality;
|
||||
Order = order;
|
||||
}
|
||||
|
||||
public SearchTokenKind Kind { get; }
|
||||
|
||||
public string Value { get; set; } = default!;
|
||||
|
||||
public Uri? IconUri { get; }
|
||||
|
||||
public Uri? SideIconUri { get; }
|
||||
|
||||
public Color? Quality { get; }
|
||||
|
||||
public int Order { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.AutoSuggestBox;
|
||||
|
||||
internal enum SearchTokenKind
|
||||
{
|
||||
None,
|
||||
ItemQuality,
|
||||
WeaponType,
|
||||
FightProperty,
|
||||
ElementName,
|
||||
AssociationType,
|
||||
BodyType,
|
||||
Avatar,
|
||||
Weapon,
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
|
||||
namespace Snap.Hutao.Control.Behavior;
|
||||
|
||||
@@ -46,6 +45,10 @@ internal sealed partial class InvokeCommandOnLoadedBehavior : BehaviorBase<UIEle
|
||||
return;
|
||||
}
|
||||
|
||||
executed = Command.TryExecute(CommandParameter);
|
||||
if (Command is not null && Command.CanExecute(CommandParameter))
|
||||
{
|
||||
Command.Execute(CommandParameter);
|
||||
executed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
|
||||
namespace Snap.Hutao.Control.Behavior;
|
||||
|
||||
@@ -33,7 +32,6 @@ internal sealed partial class PeriodicInvokeCommandOrOnActualThemeChangedBehavio
|
||||
|
||||
protected override bool Uninitialize()
|
||||
{
|
||||
periodicTimerCancellationTokenSource.Cancel();
|
||||
AssociatedObject.ActualThemeChanged -= OnActualThemeChanged;
|
||||
return true;
|
||||
}
|
||||
@@ -51,7 +49,10 @@ internal sealed partial class PeriodicInvokeCommandOrOnActualThemeChangedBehavio
|
||||
return;
|
||||
}
|
||||
|
||||
Command.TryExecute(CommandParameter);
|
||||
if (Command is not null && Command.CanExecute(CommandParameter))
|
||||
{
|
||||
Command.Execute(CommandParameter);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask RunCoreAsync()
|
||||
|
||||
@@ -13,4 +13,6 @@ namespace Snap.Hutao.Control;
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[DependencyProperty("DataContext", typeof(object))]
|
||||
internal sealed partial class BindingProxy : DependencyObject;
|
||||
internal sealed partial class BindingProxy : DependencyObject
|
||||
{
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Builder.ButtonBase;
|
||||
|
||||
internal class ButtonBaseBuilder<TButton> : IButtonBaseBuilder<TButton>
|
||||
where TButton : Microsoft.UI.Xaml.Controls.Primitives.ButtonBase, new()
|
||||
{
|
||||
public TButton Button { get; } = new();
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Abstraction.Extension;
|
||||
|
||||
namespace Snap.Hutao.Control.Builder.ButtonBase;
|
||||
|
||||
internal static class ButtonBaseBuilderExtension
|
||||
{
|
||||
public static TBuilder SetContent<TBuilder, TButton>(this TBuilder builder, object? content)
|
||||
where TBuilder : IButtonBaseBuilder<TButton>
|
||||
where TButton : Microsoft.UI.Xaml.Controls.Primitives.ButtonBase
|
||||
{
|
||||
builder.Configure(builder => builder.Button.Content = content);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TBuilder SetCommand<TBuilder, TButton>(this TBuilder builder, ICommand command)
|
||||
where TBuilder : IButtonBaseBuilder<TButton>
|
||||
where TButton : Microsoft.UI.Xaml.Controls.Primitives.ButtonBase
|
||||
{
|
||||
builder.Configure(builder => builder.Button.Command = command);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Snap.Hutao.Control.Builder.ButtonBase;
|
||||
|
||||
internal sealed class ButtonBuilder : ButtonBaseBuilder<Button>;
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Snap.Hutao.Control.Builder.ButtonBase;
|
||||
|
||||
internal static class ButtonBuilderExtension
|
||||
{
|
||||
public static ButtonBuilder SetContent(this ButtonBuilder builder, object? content)
|
||||
{
|
||||
return builder.SetContent<ButtonBuilder, Button>(content);
|
||||
}
|
||||
|
||||
public static ButtonBuilder SetCommand(this ButtonBuilder builder, ICommand command)
|
||||
{
|
||||
return builder.SetCommand<ButtonBuilder, Button>(command);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
|
||||
namespace Snap.Hutao.Control.Builder.ButtonBase;
|
||||
|
||||
internal interface IButtonBaseBuilder<TButton> : IBuilder
|
||||
where TButton : Microsoft.UI.Xaml.Controls.Primitives.ButtonBase
|
||||
{
|
||||
TButton Button { get; }
|
||||
}
|
||||
@@ -30,7 +30,7 @@ internal sealed class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, IN
|
||||
private WeakEventListener<AdvancedCollectionView<T>, object?, NotifyCollectionChangedEventArgs>? sourceWeakEventListener;
|
||||
|
||||
public AdvancedCollectionView()
|
||||
: this([])
|
||||
: this(new List<T>(0))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ using Windows.Foundation.Collections;
|
||||
|
||||
namespace Snap.Hutao.Control.Collection.Alternating;
|
||||
|
||||
[Obsolete("Use SettingsCard instead")]
|
||||
[DependencyProperty("ItemAlternateBackground", typeof(Microsoft.UI.Xaml.Media.Brush))]
|
||||
internal sealed partial class AlternatingItemsControl : ItemsControl
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
namespace Snap.Hutao.Control.Collection.Alternating;
|
||||
|
||||
[Obsolete("Use SettingsCard instead")]
|
||||
internal interface IAlternatingItem
|
||||
{
|
||||
public Microsoft.UI.Xaml.Media.Brush? Background { get; set; }
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Extension;
|
||||
|
||||
internal static class CommandInvocation
|
||||
{
|
||||
public static bool TryExecute(this ICommand? command, object? parameter = null)
|
||||
{
|
||||
if (command is not null && command.CanExecute(parameter))
|
||||
{
|
||||
command.Execute(parameter);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,11 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Control.Extension;
|
||||
|
||||
internal static class DependencyObjectExtension
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IServiceProvider ServiceProvider(this DependencyObject obj)
|
||||
{
|
||||
return Ioc.Default;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Snap.Hutao.Control.Helper;
|
||||
|
||||
[SuppressMessage("", "SH001")]
|
||||
[DependencyProperty("VisibilityObject", typeof(object), null, nameof(OnVisibilityObjectChanged), IsAttached = true, AttachedType = typeof(UIElement))]
|
||||
[DependencyProperty("OpacityObject", typeof(object), null, nameof(OnOpacityObjectChanged), IsAttached = true, AttachedType = typeof(UIElement))]
|
||||
public sealed partial class UIElementHelper
|
||||
{
|
||||
private static void OnVisibilityObjectChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
UIElement element = (UIElement)dp;
|
||||
element.Visibility = e.NewValue is null ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
private static void OnOpacityObjectChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
UIElement element = (UIElement)dp;
|
||||
element.Opacity = e.NewValue is null ? 0D : 1D;
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,7 @@
|
||||
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
using Snap.Hutao.Core.Caching;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Control.Image;
|
||||
@@ -21,9 +19,6 @@ internal sealed class CachedImage : Implementation.ImageEx
|
||||
/// </summary>
|
||||
public CachedImage()
|
||||
{
|
||||
DefaultStyleKey = typeof(CachedImage);
|
||||
DefaultStyleResourceUri = "ms-appx:///Control/Image/CachedImage.xaml".ToUri();
|
||||
|
||||
IsCacheEnabled = true;
|
||||
EnableLazyLoading = false;
|
||||
}
|
||||
@@ -31,11 +26,12 @@ internal sealed class CachedImage : Implementation.ImageEx
|
||||
/// <inheritdoc/>
|
||||
protected override async Task<ImageSource?> ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
|
||||
{
|
||||
IImageCache imageCache = this.ServiceProvider().GetRequiredService<IImageCache>();
|
||||
// We can only use Ioc to retrieve IImageCache, no IServiceProvider is available.
|
||||
IImageCache imageCache = Ioc.Default.GetRequiredService<IImageCache>();
|
||||
|
||||
try
|
||||
{
|
||||
HutaoException.ThrowIf(string.IsNullOrEmpty(imageUri.Host), HutaoExceptionKind.ImageCacheInvalidUri, SH.ControlImageCachedImageInvalidResourceUri);
|
||||
Verify.Operation(!string.IsNullOrEmpty(imageUri.Host), SH.ControlImageCachedImageInvalidResourceUri);
|
||||
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true); // BitmapImage need to be created by main thread.
|
||||
token.ThrowIfCancellationRequested(); // check token state to determine whether the operation should be canceled.
|
||||
return new BitmapImage(file.ToUri()); // BitmapImage initialize with a uri will increase image quality and loading speed.
|
||||
|
||||
@@ -168,7 +168,6 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
|
||||
if (surface.DecodedPhysicalSize.Size() <= 0D)
|
||||
{
|
||||
await Task.WhenAny(surfaceLoadTaskCompletionSource.Task, Task.Delay(5000, token)).ConfigureAwait(true);
|
||||
await Task.Delay(50, token).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
LoadImageSurfaceCompleted(surface);
|
||||
|
||||
@@ -7,14 +7,21 @@ using Windows.Media.Casting;
|
||||
|
||||
namespace Snap.Hutao.Control.Image.Implementation;
|
||||
|
||||
[DependencyProperty("NineGrid", typeof(Thickness))]
|
||||
internal partial class ImageEx : ImageExBase
|
||||
internal class ImageEx : ImageExBase
|
||||
{
|
||||
private static readonly DependencyProperty NineGridProperty = DependencyProperty.Register(nameof(NineGrid), typeof(Thickness), typeof(ImageEx), new PropertyMetadata(default(Thickness)));
|
||||
|
||||
public ImageEx()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public Thickness NineGrid
|
||||
{
|
||||
get => (Thickness)GetValue(NineGridProperty);
|
||||
set => SetValue(NineGridProperty, value);
|
||||
}
|
||||
|
||||
public override CompositionBrush GetAlphaMask()
|
||||
{
|
||||
if (IsInitialized && Image is Microsoft.UI.Xaml.Controls.Image image)
|
||||
|
||||
@@ -6,7 +6,6 @@ using Microsoft.UI.Composition;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Snap.Hutao.Win32;
|
||||
using System.IO;
|
||||
using Windows.Foundation;
|
||||
|
||||
@@ -159,6 +158,7 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
||||
if (value)
|
||||
{
|
||||
control.LayoutUpdated += control.OnImageExBaseLayoutUpdated;
|
||||
|
||||
control.InvalidateLazyLoading();
|
||||
}
|
||||
else
|
||||
@@ -169,7 +169,7 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
||||
|
||||
private static void LazyLoadingThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is ImageExBase { EnableLazyLoading: true } control)
|
||||
if (d is ImageExBase control && control.EnableLazyLoading)
|
||||
{
|
||||
control.InvalidateLazyLoading();
|
||||
}
|
||||
@@ -229,6 +229,9 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
||||
|
||||
private void AttachPlaceholderSource(ImageSource? source)
|
||||
{
|
||||
// Setting the source at this point should call ImageExOpened/VisualStateManager.GoToState
|
||||
// as we register to both the ImageOpened/ImageFailed events of the underlying control.
|
||||
// We only need to call those methods if we fail in other cases before we get here.
|
||||
if (PlaceholderImage is Microsoft.UI.Xaml.Controls.Image image)
|
||||
{
|
||||
image.Source = source;
|
||||
@@ -237,15 +240,6 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
||||
{
|
||||
brush.ImageSource = source;
|
||||
}
|
||||
|
||||
if (source is null)
|
||||
{
|
||||
VisualStateManager.GoToState(this, UnloadedState, true);
|
||||
}
|
||||
else if (source is BitmapSource { PixelHeight: > 0, PixelWidth: > 0 })
|
||||
{
|
||||
VisualStateManager.GoToState(this, LoadedState, true);
|
||||
}
|
||||
}
|
||||
|
||||
private async void SetSource(object? source)
|
||||
@@ -317,7 +311,8 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
||||
}
|
||||
|
||||
tokenSource?.Cancel();
|
||||
tokenSource = new();
|
||||
|
||||
tokenSource = new CancellationTokenSource();
|
||||
|
||||
AttachPlaceholderSource(null);
|
||||
|
||||
@@ -448,10 +443,9 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
||||
return;
|
||||
}
|
||||
|
||||
Rect controlRect = TransformToVisual(hostElement).TransformBounds(StructMarshal.Rect(ActualSize));
|
||||
Rect controlRect = TransformToVisual(hostElement)
|
||||
.TransformBounds(new Rect(0, 0, ActualWidth, ActualHeight));
|
||||
double lazyLoadingThreshold = LazyLoadingThreshold;
|
||||
|
||||
// Left/Top 1 Threshold, Right/Bottom 2 Threshold
|
||||
Rect hostRect = new(
|
||||
0 - lazyLoadingThreshold,
|
||||
0 - lazyLoadingThreshold,
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Composition;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Hosting;
|
||||
using System.Numerics;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Snap.Hutao.Control.Layout;
|
||||
|
||||
internal sealed class DefaultItemCollectionTransitionProvider : ItemCollectionTransitionProvider
|
||||
{
|
||||
private const double DefaultAnimationDurationInMs = 300.0;
|
||||
|
||||
static DefaultItemCollectionTransitionProvider()
|
||||
{
|
||||
AnimationSlowdownFactor = 1.0;
|
||||
}
|
||||
|
||||
public static double AnimationSlowdownFactor { get; set; }
|
||||
|
||||
protected override bool ShouldAnimateCore(ItemCollectionTransition transition)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void StartTransitions(IList<ItemCollectionTransition> transitions)
|
||||
{
|
||||
List<ItemCollectionTransition> addTransitions = [];
|
||||
List<ItemCollectionTransition> removeTransitions = [];
|
||||
List<ItemCollectionTransition> moveTransitions = [];
|
||||
|
||||
foreach (ItemCollectionTransition transition in addTransitions)
|
||||
{
|
||||
switch (transition.Operation)
|
||||
{
|
||||
case ItemCollectionTransitionOperation.Add:
|
||||
addTransitions.Add(transition);
|
||||
break;
|
||||
case ItemCollectionTransitionOperation.Remove:
|
||||
removeTransitions.Add(transition);
|
||||
break;
|
||||
case ItemCollectionTransitionOperation.Move:
|
||||
moveTransitions.Add(transition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StartAddTransitions(addTransitions, removeTransitions.Count > 0, moveTransitions.Count > 0);
|
||||
StartRemoveTransitions(removeTransitions);
|
||||
StartMoveTransitions(moveTransitions, removeTransitions.Count > 0);
|
||||
}
|
||||
|
||||
private static void StartAddTransitions(IList<ItemCollectionTransition> transitions, bool hasRemoveTransitions, bool hasMoveTransitions)
|
||||
{
|
||||
foreach (ItemCollectionTransition transition in transitions)
|
||||
{
|
||||
ItemCollectionTransitionProgress progress = transition.Start();
|
||||
Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
|
||||
Compositor compositor = visual.Compositor;
|
||||
|
||||
ScalarKeyFrameAnimation fadeInAnimation = compositor.CreateScalarKeyFrameAnimation();
|
||||
fadeInAnimation.InsertKeyFrame(0.0f, 0.0f);
|
||||
|
||||
if (hasMoveTransitions && hasRemoveTransitions)
|
||||
{
|
||||
fadeInAnimation.InsertKeyFrame(0.66f, 0.0f);
|
||||
}
|
||||
else if (hasMoveTransitions || hasRemoveTransitions)
|
||||
{
|
||||
fadeInAnimation.InsertKeyFrame(0.5f, 0.0f);
|
||||
}
|
||||
|
||||
fadeInAnimation.InsertKeyFrame(1.0f, 1.0f);
|
||||
fadeInAnimation.Duration = TimeSpan.FromMilliseconds(
|
||||
DefaultAnimationDurationInMs * ((hasRemoveTransitions ? 1 : 0) + (hasMoveTransitions ? 1 : 0) + 1) * AnimationSlowdownFactor);
|
||||
|
||||
CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
||||
visual.StartAnimation("Opacity", fadeInAnimation);
|
||||
batch.End();
|
||||
batch.Completed += (_, _) => progress.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
private static void StartRemoveTransitions(IList<ItemCollectionTransition> transitions)
|
||||
{
|
||||
foreach (ItemCollectionTransition transition in transitions)
|
||||
{
|
||||
ItemCollectionTransitionProgress progress = transition.Start();
|
||||
Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
|
||||
Compositor compositor = visual.Compositor;
|
||||
|
||||
ScalarKeyFrameAnimation fadeOutAnimation = compositor.CreateScalarKeyFrameAnimation();
|
||||
fadeOutAnimation.InsertExpressionKeyFrame(0.0f, "this.CurrentValue");
|
||||
fadeOutAnimation.InsertKeyFrame(1.0f, 0.0f);
|
||||
fadeOutAnimation.Duration = TimeSpan.FromMilliseconds(DefaultAnimationDurationInMs * AnimationSlowdownFactor);
|
||||
|
||||
CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
||||
visual.StartAnimation(nameof(Visual.Opacity), fadeOutAnimation);
|
||||
batch.End();
|
||||
batch.Completed += (_, _) =>
|
||||
{
|
||||
visual.Opacity = 1.0f;
|
||||
progress.Complete();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static void StartMoveTransitions(IList<ItemCollectionTransition> transitions, bool hasRemoveAnimations)
|
||||
{
|
||||
foreach (ItemCollectionTransition transition in transitions)
|
||||
{
|
||||
ItemCollectionTransitionProgress progress = transition.Start();
|
||||
Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
|
||||
Compositor compositor = visual.Compositor;
|
||||
CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
||||
|
||||
// Animate offset.
|
||||
if (transition.OldBounds.X != transition.NewBounds.X ||
|
||||
transition.OldBounds.Y != transition.NewBounds.Y)
|
||||
{
|
||||
AnimateOffset(visual, compositor, transition.OldBounds, transition.NewBounds, hasRemoveAnimations);
|
||||
}
|
||||
|
||||
batch.End();
|
||||
batch.Completed += (_, _) => progress.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
private static void AnimateOffset(Visual visual, Compositor compositor, Rect oldBounds, Rect newBounds, bool hasRemoveAnimations)
|
||||
{
|
||||
Vector2KeyFrameAnimation offsetAnimation = compositor.CreateVector2KeyFrameAnimation();
|
||||
|
||||
offsetAnimation.SetVector2Parameter("delta", new Vector2(
|
||||
(float)(oldBounds.X - newBounds.X),
|
||||
(float)(oldBounds.Y - newBounds.Y)));
|
||||
offsetAnimation.SetVector2Parameter("final", default);
|
||||
offsetAnimation.InsertExpressionKeyFrame(0.0f, "this.CurrentValue + delta");
|
||||
if (hasRemoveAnimations)
|
||||
{
|
||||
offsetAnimation.InsertExpressionKeyFrame(0.5f, "delta");
|
||||
}
|
||||
|
||||
offsetAnimation.InsertExpressionKeyFrame(1.0f, "final");
|
||||
offsetAnimation.Duration = TimeSpan.FromMilliseconds(
|
||||
DefaultAnimationDurationInMs * ((hasRemoveAnimations ? 1 : 0) + 1) * AnimationSlowdownFactor);
|
||||
|
||||
visual.StartAnimation("TransformMatrix._41_42", offsetAnimation);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Foundation;
|
||||
|
||||
@@ -19,12 +18,14 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
||||
protected override void InitializeForContextCore(VirtualizingLayoutContext context)
|
||||
{
|
||||
context.LayoutState = new UniformStaggeredLayoutState(context);
|
||||
base.InitializeForContextCore(context);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UninitializeForContextCore(VirtualizingLayoutContext context)
|
||||
{
|
||||
context.LayoutState = null;
|
||||
base.UninitializeForContextCore(context);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -81,10 +82,16 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
||||
|
||||
(int numberOfColumns, double columnWidth) = GetNumberOfColumnsAndWidth(availableWidth, MinItemWidth, MinColumnSpacing);
|
||||
|
||||
if (columnWidth != state.ColumnWidth)
|
||||
{
|
||||
// The items will need to be remeasured
|
||||
state.Clear();
|
||||
}
|
||||
|
||||
state.ColumnWidth = columnWidth;
|
||||
|
||||
double totalWidth = ((state.ColumnWidth + MinColumnSpacing) * numberOfColumns) - MinColumnSpacing;
|
||||
|
||||
// adjust for column spacing on all columns expect the first
|
||||
double totalWidth = state.ColumnWidth + ((numberOfColumns - 1) * (state.ColumnWidth + MinColumnSpacing));
|
||||
if (totalWidth > availableWidth)
|
||||
{
|
||||
numberOfColumns--;
|
||||
@@ -96,6 +103,7 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
||||
|
||||
if (numberOfColumns != state.NumberOfColumns)
|
||||
{
|
||||
// The items will not need to be remeasured, but they will need to go into new columns
|
||||
state.ClearColumns();
|
||||
}
|
||||
|
||||
@@ -162,7 +170,7 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
||||
item.Element.Measure(new Size(state.ColumnWidth, availableHeight));
|
||||
if (item.Height != item.Element.DesiredSize.Height)
|
||||
{
|
||||
// this item changed size; we need to recalculate layout for everything after this item
|
||||
// this item changed size; we need to recalculate layout for everything after this
|
||||
state.RemoveFromIndex(i + 1);
|
||||
item.Height = item.Element.DesiredSize.Height;
|
||||
columnHeights[columnIndex] = item.Top + item.Height;
|
||||
@@ -193,16 +201,16 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
||||
// Cycle through each column and arrange the items that are within the realization bounds
|
||||
for (int columnIndex = 0; columnIndex < state.NumberOfColumns; columnIndex++)
|
||||
{
|
||||
foreach (ref readonly UniformStaggeredItem item in CollectionsMarshal.AsSpan(state.GetColumnLayout(columnIndex)))
|
||||
UniformStaggeredColumnLayout layout = state.GetColumnLayout(columnIndex);
|
||||
foreach (ref readonly UniformStaggeredItem item in CollectionsMarshal.AsSpan(layout))
|
||||
{
|
||||
double bottom = item.Top + item.Height;
|
||||
if (bottom < context.RealizationRect.Top)
|
||||
{
|
||||
// Element is above the realization bounds
|
||||
// element is above the realization bounds
|
||||
continue;
|
||||
}
|
||||
|
||||
// Partial or fully in the view
|
||||
if (item.Top <= context.RealizationRect.Bottom)
|
||||
{
|
||||
double itemHorizontalOffset = (state.ColumnWidth * columnIndex) + (MinColumnSpacing * columnIndex);
|
||||
@@ -221,22 +229,21 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
private static (int NumberOfColumns, double ColumnWidth) GetNumberOfColumnsAndWidth(double availableWidth, double minItemWidth, double columnSpacing)
|
||||
private static (int NumberOfColumns, double ColumnWidth) GetNumberOfColumnsAndWidth(double availableWidth, double minItemWidth, double minColumnSpacing)
|
||||
{
|
||||
// test if the width can fit in 2 items
|
||||
if ((2 * minItemWidth) + columnSpacing > availableWidth)
|
||||
if ((2 * minItemWidth) + minColumnSpacing > availableWidth)
|
||||
{
|
||||
return (1, availableWidth);
|
||||
}
|
||||
|
||||
int columnCount = Math.Max(1, (int)((availableWidth + columnSpacing) / (minItemWidth + columnSpacing)));
|
||||
double columnWidthWithSpacing = (availableWidth + columnSpacing) / columnCount;
|
||||
return (columnCount, columnWidthWithSpacing - columnSpacing);
|
||||
int columnCount = Math.Max(1, (int)((availableWidth + minColumnSpacing) / (minItemWidth + minColumnSpacing)));
|
||||
double columnWidthAddSpacing = (availableWidth + minColumnSpacing) / columnCount;
|
||||
return (columnCount, columnWidthAddSpacing - minColumnSpacing);
|
||||
}
|
||||
|
||||
private static int GetLowestColumnIndex(in ReadOnlySpan<double> columnHeights)
|
||||
{
|
||||
// We want to find the leftest column with the lowest height
|
||||
int columnIndex = 0;
|
||||
double height = columnHeights[0];
|
||||
for (int j = 1; j < columnHeights.Length; j++)
|
||||
@@ -253,11 +260,13 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
||||
|
||||
private static void OnMinItemWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((UniformStaggeredLayout)d).InvalidateMeasure();
|
||||
UniformStaggeredLayout panel = (UniformStaggeredLayout)d;
|
||||
panel.InvalidateMeasure();
|
||||
}
|
||||
|
||||
private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((UniformStaggeredLayout)d).InvalidateMeasure();
|
||||
UniformStaggeredLayout panel = (UniformStaggeredLayout)d;
|
||||
panel.InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -66,6 +67,46 @@ internal sealed class UniformStaggeredLayoutState
|
||||
return columnLayout[columnIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear everything that has been calculated.
|
||||
/// </summary>
|
||||
internal void Clear()
|
||||
{
|
||||
// https://github.com/DGP-Studio/Snap.Hutao/issues/1079
|
||||
// The first element must be force refreshed otherwise
|
||||
// it will use the old one realized
|
||||
// https://github.com/DGP-Studio/Snap.Hutao/issues/1099
|
||||
// Now we need to refresh the first element of each column
|
||||
// https://github.com/DGP-Studio/Snap.Hutao/issues/1099
|
||||
// Finally we need to refresh the whole layout when we reset
|
||||
if (context.ItemCount > 0)
|
||||
{
|
||||
for (int i = 0; i < context.ItemCount; i++)
|
||||
{
|
||||
RecycleElementAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
columnLayout.Clear();
|
||||
items.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the layout columns so they will be recalculated.
|
||||
/// </summary>
|
||||
internal void ClearColumns()
|
||||
{
|
||||
columnLayout.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the estimated height of the layout.
|
||||
/// </summary>
|
||||
/// <returns>The estimated height of the layout.</returns>
|
||||
/// <remarks>
|
||||
/// If all of the items have been calculated then the actual height will be returned.
|
||||
/// If all of the items have not been calculated then an estimated height will be calculated based on the average height of the items.
|
||||
/// </remarks>
|
||||
internal double GetHeight()
|
||||
{
|
||||
double desiredHeight = columnLayout.Values.Max(c => c.Height);
|
||||
@@ -98,37 +139,10 @@ internal sealed class UniformStaggeredLayoutState
|
||||
return desiredHeight;
|
||||
}
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
RecycleElements();
|
||||
ClearColumns();
|
||||
ClearItems();
|
||||
}
|
||||
|
||||
internal void ClearColumns()
|
||||
{
|
||||
columnLayout.Clear();
|
||||
}
|
||||
|
||||
internal void ClearItems()
|
||||
{
|
||||
items.Clear();
|
||||
}
|
||||
|
||||
internal void RecycleElements()
|
||||
{
|
||||
if (context.ItemCount > 0)
|
||||
{
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
RecycleElementAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RecycleElementAt(int index)
|
||||
{
|
||||
context.RecycleElement(context.GetOrCreateElementAt(index));
|
||||
UIElement element = context.GetOrCreateElementAt(index);
|
||||
context.RecycleElement(element);
|
||||
}
|
||||
|
||||
internal void RemoveFromIndex(int index)
|
||||
@@ -161,7 +175,7 @@ internal sealed class UniformStaggeredLayoutState
|
||||
{
|
||||
for (int i = startIndex; i <= endIndex; i++)
|
||||
{
|
||||
if (i >= items.Count)
|
||||
if (i > items.Count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -170,7 +184,7 @@ internal sealed class UniformStaggeredLayoutState
|
||||
item.Height = 0;
|
||||
item.Top = 0;
|
||||
|
||||
// We must recycle all removed elements to ensure that it gets the correct context
|
||||
// We must recycle all elements to ensure that it gets the correct context
|
||||
RecycleElementAt(i);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ internal class Loading : Microsoft.UI.Xaml.Controls.ContentControl
|
||||
public Loading()
|
||||
{
|
||||
DefaultStyleKey = typeof(Loading);
|
||||
DefaultStyleResourceUri = "ms-appx:///Control/Loading.xaml".ToUri();
|
||||
DefaultStyleResourceUri = new("ms-appx:///Control/Loading.xaml");
|
||||
}
|
||||
|
||||
public bool IsLoading
|
||||
|
||||
@@ -12,6 +12,7 @@ internal sealed class UInt32Extension : MarkupExtension
|
||||
|
||||
protected override object ProvideValue()
|
||||
{
|
||||
return XamlBindingHelper.ConvertValue(typeof(uint), Value);
|
||||
_ = uint.TryParse(Value, out uint result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,24 @@ internal static class SoftwareBitmapExtension
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe double Luminance(this SoftwareBitmap softwareBitmap)
|
||||
{
|
||||
using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Read))
|
||||
{
|
||||
using (IMemoryBufferReference reference = buffer.CreateReference())
|
||||
{
|
||||
reference.As<IMemoryBufferByteAccess>().GetBuffer(out Span<Bgra32> bytes);
|
||||
double sum = 0;
|
||||
foreach (ref readonly Bgra32 pixel in bytes)
|
||||
{
|
||||
sum += pixel.Luminance;
|
||||
}
|
||||
|
||||
return sum / bytes.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe Bgra32 GetAccentColor(this SoftwareBitmap softwareBitmap)
|
||||
{
|
||||
using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Read))
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Data;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Snap.Hutao.Control.Panel;
|
||||
|
||||
[DependencyProperty("Spacing", typeof(double), default(double), nameof(OnSpacingChanged))]
|
||||
internal partial class EqualPanel : Microsoft.UI.Xaml.Controls.Panel
|
||||
{
|
||||
private double maxItemWidth;
|
||||
private double maxItemHeight;
|
||||
private int visibleItemsCount;
|
||||
|
||||
public EqualPanel()
|
||||
{
|
||||
RegisterPropertyChangedCallback(HorizontalAlignmentProperty, OnHorizontalAlignmentChanged);
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
maxItemWidth = 0;
|
||||
maxItemHeight = 0;
|
||||
|
||||
List<UIElement> elements = [.. Children.Where(element => element.Visibility == Visibility.Visible)];
|
||||
visibleItemsCount = elements.Count;
|
||||
|
||||
foreach (ref readonly UIElement child in CollectionsMarshal.AsSpan(elements))
|
||||
{
|
||||
child.Measure(availableSize);
|
||||
maxItemWidth = Math.Max(maxItemWidth, child.DesiredSize.Width);
|
||||
maxItemHeight = Math.Max(maxItemHeight, child.DesiredSize.Height);
|
||||
}
|
||||
|
||||
if (visibleItemsCount > 0)
|
||||
{
|
||||
// Return equal widths based on the widest item
|
||||
// In very specific edge cases the AvailableWidth might be infinite resulting in a crash.
|
||||
if (HorizontalAlignment is not HorizontalAlignment.Stretch || double.IsInfinity(availableSize.Width))
|
||||
{
|
||||
return new Size((maxItemWidth * visibleItemsCount) + (Spacing * (visibleItemsCount - 1)), maxItemHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Equal columns based on the available width, adjust for spacing
|
||||
double totalWidth = availableSize.Width - (Spacing * (visibleItemsCount - 1));
|
||||
maxItemWidth = totalWidth / visibleItemsCount;
|
||||
return new Size(availableSize.Width, maxItemHeight);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Size(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
double x = 0;
|
||||
|
||||
// Check if there's more (little) width available - if so, set max item width to the maximum possible as we have an almost perfect height.
|
||||
if (finalSize.Width > (visibleItemsCount * maxItemWidth) + (Spacing * (visibleItemsCount - 1)))
|
||||
{
|
||||
maxItemWidth = (finalSize.Width - (Spacing * (visibleItemsCount - 1))) / visibleItemsCount;
|
||||
}
|
||||
|
||||
IEnumerable<UIElement> elements = Children.Where(static e => e.Visibility == Visibility.Visible);
|
||||
foreach (UIElement child in elements)
|
||||
{
|
||||
child.Arrange(new Rect(x, 0, maxItemWidth, maxItemHeight));
|
||||
x += maxItemWidth + Spacing;
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
(d as EqualPanel)?.InvalidateMeasure();
|
||||
}
|
||||
|
||||
private static void OnHorizontalAlignmentChanged(DependencyObject d, DependencyProperty dp)
|
||||
{
|
||||
(d as EqualPanel)?.InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Snap.Hutao.Control.Panel;
|
||||
|
||||
[DependencyProperty("MinItemWidth", typeof(double))]
|
||||
[DependencyProperty("Spacing", typeof(double))]
|
||||
internal partial class HorizontalEqualPanel : Microsoft.UI.Xaml.Controls.Panel
|
||||
{
|
||||
public HorizontalEqualPanel()
|
||||
{
|
||||
Loaded += OnLoaded;
|
||||
SizeChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
foreach (UIElement child in Children)
|
||||
{
|
||||
// ScrollViewer will always return an Infinity Size, we should use ActualWidth for this situation.
|
||||
double availableWidth = double.IsInfinity(availableSize.Width) ? ActualWidth : availableSize.Width;
|
||||
double childAvailableWidth = (availableWidth + Spacing) / Children.Count;
|
||||
double childMaxAvailableWidth = Math.Max(MinItemWidth, childAvailableWidth);
|
||||
child.Measure(new(childMaxAvailableWidth - Spacing, ActualHeight));
|
||||
}
|
||||
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
int itemCount = Children.Count;
|
||||
double availableItemWidth = (finalSize.Width - (Spacing * (itemCount - 1))) / itemCount;
|
||||
double actualItemWidth = Math.Max(MinItemWidth, availableItemWidth);
|
||||
|
||||
double offset = 0;
|
||||
foreach (UIElement child in Children)
|
||||
{
|
||||
child.Arrange(new Rect(offset, 0, actualItemWidth, finalSize.Height));
|
||||
offset += actualItemWidth + Spacing;
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
private static void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
HorizontalEqualPanel panel = (HorizontalEqualPanel)sender;
|
||||
panel.MinWidth = (panel.MinItemWidth * panel.Children.Count) + (panel.Spacing * (panel.Children.Count - 1));
|
||||
}
|
||||
|
||||
private static void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
((HorizontalEqualPanel)sender).InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
Style="{StaticResource DefaultSegmentedStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<cwc:SegmentedItem
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using System.Collections.Frozen;
|
||||
|
||||
namespace Snap.Hutao.Control.Panel;
|
||||
|
||||
@@ -20,11 +19,11 @@ internal sealed partial class PanelSelector : Segmented
|
||||
public const string List = nameof(List);
|
||||
public const string Grid = nameof(Grid);
|
||||
|
||||
private static readonly FrozenDictionary<int, string> IndexTypeMap = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(0, List),
|
||||
KeyValuePair.Create(1, Grid),
|
||||
]);
|
||||
private static readonly Dictionary<int, string> IndexTypeMap = new()
|
||||
{
|
||||
[0] = List,
|
||||
[1] = Grid,
|
||||
};
|
||||
|
||||
private readonly RoutedEventHandler loadedEventHandler;
|
||||
private readonly RoutedEventHandler unloadedEventHandler;
|
||||
|
||||
@@ -1,55 +1,21 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Windows.Foundation;
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
|
||||
namespace Snap.Hutao.Control.Panel;
|
||||
|
||||
[DependencyProperty("MinItemWidth", typeof(double))]
|
||||
[DependencyProperty("ColumnSpacing", typeof(double))]
|
||||
[DependencyProperty("RowSpacing", typeof(double))]
|
||||
internal sealed partial class UniformPanel : Microsoft.UI.Xaml.Controls.Panel
|
||||
internal sealed partial class UniformPanel : UniformGrid
|
||||
{
|
||||
private int columns;
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
public UniformPanel()
|
||||
{
|
||||
columns = (int)((availableSize.Width + ColumnSpacing) / (MinItemWidth + ColumnSpacing));
|
||||
double availableItemWidth = ((availableSize.Width + ColumnSpacing) / columns) - ColumnSpacing;
|
||||
|
||||
double maxDesiredHeight = 0;
|
||||
foreach (UIElement child in Children)
|
||||
{
|
||||
child.Measure(new Size(availableItemWidth, availableSize.Height));
|
||||
maxDesiredHeight = Math.Max(maxDesiredHeight, child.DesiredSize.Height);
|
||||
}
|
||||
|
||||
int desiredRows = (int)Math.Ceiling(Children.Count / (double)columns);
|
||||
double desiredHeight = ((maxDesiredHeight + RowSpacing) * desiredRows) - RowSpacing;
|
||||
|
||||
return new Size(availableSize.Width, desiredHeight);
|
||||
Columns = 1;
|
||||
SizeChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
private void OnSizeChanged(object sender, Microsoft.UI.Xaml.SizeChangedEventArgs e)
|
||||
{
|
||||
double itemWidth = ((finalSize.Width + ColumnSpacing) / columns) - ColumnSpacing;
|
||||
|
||||
for (int index = 0; index < Children.Count; index++)
|
||||
{
|
||||
UIElement child = Children[index];
|
||||
|
||||
int row = index / columns;
|
||||
int column = index % columns;
|
||||
|
||||
double x = column * (itemWidth + ColumnSpacing);
|
||||
double y = row * (child.DesiredSize.Height + RowSpacing);
|
||||
|
||||
child.Arrange(new Rect(x, y, itemWidth, child.DesiredSize.Height));
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
Columns = (int)((e.NewSize.Width + ColumnSpacing) / (MinItemWidth + ColumnSpacing));
|
||||
}
|
||||
}
|
||||
@@ -41,12 +41,14 @@ internal class ScopedPage : Page
|
||||
/// 应当在 InitializeComponent() 前调用
|
||||
/// </summary>
|
||||
/// <typeparam name="TViewModel">视图模型类型</typeparam>
|
||||
protected void InitializeWith<TViewModel>()
|
||||
protected TViewModel InitializeWith<TViewModel>()
|
||||
where TViewModel : class, IViewModel
|
||||
{
|
||||
IViewModel viewModel = currentScope.ServiceProvider.GetRequiredService<TViewModel>();
|
||||
viewModel.CancellationToken = viewCancellationTokenSource.Token;
|
||||
DataContext = viewModel;
|
||||
|
||||
return (TViewModel)viewModel;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -72,11 +74,7 @@ internal class ScopedPage : Page
|
||||
DisposeViewModel();
|
||||
}
|
||||
|
||||
if (this.IsDisposed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DataContext = null;
|
||||
Unloaded -= unloadEventHandler;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,15 @@ internal sealed partial class DescriptionTextBlock : ContentControl
|
||||
private static void OnDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
TextBlock textBlock = (TextBlock)((DescriptionTextBlock)d).Content;
|
||||
UpdateDescription(textBlock, MiHoYoSyntaxTree.Parse(SpecialNameHandler.Handle((string)e.NewValue)));
|
||||
|
||||
try
|
||||
{
|
||||
UpdateDescription(textBlock, MiHoYoSyntaxTree.Parse(SpecialNameHandler.Handle((string)e.NewValue)));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = ex;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnTextStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
|
||||
@@ -14,7 +14,6 @@ using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Control.Text;
|
||||
|
||||
// TODO: change the parsing to syntax tree
|
||||
[DependencyProperty("Description", typeof(string), "", nameof(OnDescriptionChanged))]
|
||||
[DependencyProperty("TextStyle", typeof(Style), default(Style), nameof(OnTextStyleChanged))]
|
||||
internal sealed partial class HtmlDescriptionTextBlock : ContentControl
|
||||
|
||||
@@ -4,19 +4,21 @@
|
||||
xmlns:cwm="using:CommunityToolkit.WinUI.Media">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<x:Double x:Key="CompatShadowThemeOpacity">0.14</x:Double>
|
||||
<cwm:AttachedCardShadow
|
||||
x:Key="CompatCardShadow"
|
||||
BlurRadius="8"
|
||||
Opacity="0.14"
|
||||
Offset="0,4,0"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<x:Double x:Key="CompatShadowThemeOpacity">0.28</x:Double>
|
||||
<cwm:AttachedCardShadow
|
||||
x:Key="CompatCardShadow"
|
||||
BlurRadius="8"
|
||||
Opacity="0.28"
|
||||
Offset="0,4,0"/>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<cwm:AttachedCardShadow
|
||||
x:Key="CompatCardShadow"
|
||||
BlurRadius="8"
|
||||
Opacity="{ThemeResource CompatShadowThemeOpacity}"
|
||||
Offset="0,4,0"/>
|
||||
|
||||
<Style x:Key="BorderCardStyle" TargetType="Border">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
<ItemsPanelTemplate x:Key="WrapPanelSpacing0Template">
|
||||
<cwcont:WrapPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="WrapPanelSpacing2Template">
|
||||
<cwcont:WrapPanel HorizontalSpacing="2" VerticalSpacing="2"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="WrapPanelSpacing4Template">
|
||||
<cwcont:WrapPanel HorizontalSpacing="4" VerticalSpacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
@@ -20,9 +17,6 @@
|
||||
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing2Template">
|
||||
<StackPanel Orientation="Horizontal" Spacing="2"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing4Template">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="StackPanelSpacing4Template">
|
||||
<StackPanel Spacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
|
||||
@@ -11,6 +11,4 @@ internal static class KnownColors
|
||||
public static readonly Color Orange = StructMarshal.Color(0xFFBC6932);
|
||||
public static readonly Color Purple = StructMarshal.Color(0xFFA156E0);
|
||||
public static readonly Color Blue = StructMarshal.Color(0xFF5180CB);
|
||||
public static readonly Color Green = StructMarshal.Color(0xFF2A8F72);
|
||||
public static readonly Color White = StructMarshal.Color(0xFF72778B);
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cw="using:CommunityToolkit.WinUI"
|
||||
xmlns:cwc="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:shcp="using:Snap.Hutao.Control.Panel"
|
||||
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.Segmented/SegmentedItem/SegmentedItem.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<StaticResource x:Key="SegmentedBackground" ResourceKey="ControlAltFillColorSecondaryBrush"/>
|
||||
<StaticResource x:Key="SegmentedBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush"/>
|
||||
<Thickness x:Key="SegmentedBorderThickness">1</Thickness>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="SegmentedBackground" ResourceKey="ControlAltFillColorSecondaryBrush"/>
|
||||
<StaticResource x:Key="SegmentedBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush"/>
|
||||
<Thickness x:Key="SegmentedBorderThickness">1</Thickness>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="SegmentedBackground" ResourceKey="SystemColorButtonFaceColor"/>
|
||||
<StaticResource x:Key="SegmentedBorderBrush" ResourceKey="SystemColorHighlightColorBrush"/>
|
||||
<Thickness x:Key="SegmentedBorderThickness">1</Thickness>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<x:Double x:Key="SegmentedItemSpacing">1</x:Double>
|
||||
<x:Double x:Key="ButtonItemSpacing">2</x:Double>
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultSegmentedStyle}" TargetType="cwc:Segmented"/>
|
||||
|
||||
<Style x:Key="DefaultSegmentedStyle" TargetType="cwc:Segmented">
|
||||
<Style.Setters>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SegmentedBackground}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SegmentedBorderBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource SegmentedBorderThickness}"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="SelectionMode" Value="Single"/>
|
||||
<Setter Property="IsItemClickEnabled" Value="False"/>
|
||||
<win:Setter Property="SingleSelectionFollowsFocus" Value="False"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="TabNavigation" Value="Once"/>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<shcp:EqualPanel
|
||||
HorizontalAlignment="{Binding (cw:FrameworkElementExtensions.Ancestor).HorizontalAlignment, RelativeSource={RelativeSource Self}}"
|
||||
cw:FrameworkElementExtensions.AncestorType="cwc:Segmented"
|
||||
Spacing="{ThemeResource SegmentedItemSpacing}"/>
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="cwc:Segmented">
|
||||
<Grid>
|
||||
<Border
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"/>
|
||||
<ItemsPresenter Margin="{TemplateBinding Padding}"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="PivotSegmentedStyle"
|
||||
BasedOn="{StaticResource DefaultSegmentedStyle}"
|
||||
TargetType="cwc:Segmented">
|
||||
<Style.Setters>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="ItemContainerStyle" Value="{StaticResource PivotSegmentedItemStyle}"/>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="{ThemeResource SegmentedItemSpacing}"/>
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="ButtonSegmentedStyle"
|
||||
BasedOn="{StaticResource DefaultSegmentedStyle}"
|
||||
TargetType="cwc:Segmented">
|
||||
<Style.Setters>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="ItemContainerStyle" Value="{StaticResource ButtonSegmentedItemStyle}"/>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="{ThemeResource ButtonItemSpacing}"/>
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Control.Theme;
|
||||
|
||||
internal static class SystemColors
|
||||
{
|
||||
public static Color BaseLowColor(bool isDarkMode)
|
||||
{
|
||||
return isDarkMode ? StructMarshal.Color(0x33FFFFFF) : StructMarshal.Color(0x33000000);
|
||||
}
|
||||
|
||||
public static Color BaseMediumLowColor(bool isDarkMode)
|
||||
{
|
||||
return isDarkMode ? StructMarshal.Color(0x66FFFFFF) : StructMarshal.Color(0x66000000);
|
||||
}
|
||||
|
||||
public static Color BaseHighColor(bool isDarkMode)
|
||||
{
|
||||
return isDarkMode ? StructMarshal.Color(0xFFFFFFFF) : StructMarshal.Color(0xFF000000);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@
|
||||
<x:String x:Key="UI_EmotionIcon25">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon25.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon52">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon52.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon71">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon71.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon89">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon89.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon250">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon250.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon271">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon271.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon272">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon272.png</x:String>
|
||||
|
||||
18
src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ICloneable.cs
Normal file
18
src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ICloneable.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Abstraction;
|
||||
|
||||
/// <summary>
|
||||
/// 可克隆
|
||||
/// </summary>
|
||||
/// <typeparam name="TSelf">自身类型</typeparam>
|
||||
[HighQuality]
|
||||
internal interface ICloneable<out TSelf>
|
||||
{
|
||||
/// <summary>
|
||||
/// 克隆
|
||||
/// </summary>
|
||||
/// <returns>新的克隆</returns>
|
||||
TSelf Clone();
|
||||
}
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Core.IO.Hashing;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Frozen;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Snap.Hutao.Core.Caching;
|
||||
|
||||
@@ -26,12 +26,12 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
{
|
||||
private const string CacheFolderName = nameof(ImageCache);
|
||||
|
||||
private readonly FrozenDictionary<int, TimeSpan> retryCountToDelay = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(0, TimeSpan.FromSeconds(4)),
|
||||
KeyValuePair.Create(1, TimeSpan.FromSeconds(16)),
|
||||
KeyValuePair.Create(2, TimeSpan.FromSeconds(64)),
|
||||
]);
|
||||
private readonly FrozenDictionary<int, TimeSpan> retryCountToDelay = new Dictionary<int, TimeSpan>()
|
||||
{
|
||||
[0] = TimeSpan.FromSeconds(4),
|
||||
[1] = TimeSpan.FromSeconds(16),
|
||||
[2] = TimeSpan.FromSeconds(64),
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
private readonly ConcurrentDictionary<string, Task> concurrentTasks = new();
|
||||
|
||||
@@ -104,12 +104,12 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
{
|
||||
if (concurrentTasks.TryAdd(fileName, taskCompletionSource.Task))
|
||||
{
|
||||
logger.LogColorizedInformation("Begin to download file from '{Uri}' to '{File}'", (uri, ConsoleColor.Cyan), (filePath, ConsoleColor.Cyan));
|
||||
logger.LogDebug("Begin downloading image file from '{Uri}' to '{File}'", uri, filePath);
|
||||
await DownloadFileAsync(uri, filePath).ConfigureAwait(false);
|
||||
}
|
||||
else if (concurrentTasks.TryGetValue(fileName, out Task? task))
|
||||
{
|
||||
logger.LogDebug("Waiting for a queued image download task to complete for '{Uri}'", (uri, ConsoleColor.Cyan));
|
||||
logger.LogDebug("Waiting for a queued image download task to complete for '{Uri}'", uri);
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -132,7 +132,10 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
|
||||
private static string GetCacheFileName(Uri uri)
|
||||
{
|
||||
return Hash.SHA1HexString(uri.ToString());
|
||||
string url = uri.ToString();
|
||||
byte[] chars = Encoding.UTF8.GetBytes(url);
|
||||
byte[] hash = SHA1.HashData(chars);
|
||||
return System.Convert.ToHexString(hash);
|
||||
}
|
||||
|
||||
private static bool IsFileInvalid(string file, bool treatNullFileAsInvalid = true)
|
||||
|
||||
@@ -24,13 +24,7 @@ internal static class DependencyInjection
|
||||
ServiceProvider serviceProvider = new ServiceCollection()
|
||||
|
||||
// Microsoft extension
|
||||
.AddLogging(builder =>
|
||||
{
|
||||
builder
|
||||
.SetMinimumLevel(LogLevel.Trace)
|
||||
.AddDebug()
|
||||
.AddConsoleWindow();
|
||||
})
|
||||
.AddLogging(builder => builder.AddDebug().AddConsoleWindow())
|
||||
.AddMemoryCache()
|
||||
|
||||
// Hutao extensions
|
||||
|
||||
@@ -25,7 +25,6 @@ internal static partial class IocHttpClientConfiguration
|
||||
.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
|
||||
{
|
||||
HttpClientHandler clientHandler = (HttpClientHandler)handler;
|
||||
clientHandler.AllowAutoRedirect = true;
|
||||
clientHandler.UseProxy = true;
|
||||
clientHandler.Proxy = provider.GetRequiredService<DynamicHttpProxy>();
|
||||
});
|
||||
|
||||
@@ -23,16 +23,8 @@ internal sealed partial class ExceptionRecorder
|
||||
public void Record(Application app)
|
||||
{
|
||||
app.UnhandledException += OnAppUnhandledException;
|
||||
app.DebugSettings.FailFastOnErrors = false;
|
||||
|
||||
app.DebugSettings.IsBindingTracingEnabled = true;
|
||||
app.DebugSettings.BindingFailed += OnXamlBindingFailed;
|
||||
|
||||
app.DebugSettings.IsXamlResourceReferenceTracingEnabled = true;
|
||||
app.DebugSettings.XamlResourceReferenceFailed += OnXamlResourceReferenceFailed;
|
||||
|
||||
app.DebugSettings.LayoutCycleTracingLevel = LayoutCycleTracingLevel.High;
|
||||
app.DebugSettings.LayoutCycleDebugBreakLevel = LayoutCycleDebugBreakLevel.High;
|
||||
}
|
||||
|
||||
[SuppressMessage("", "CA2012")]
|
||||
|
||||
@@ -32,31 +32,9 @@ internal sealed class HutaoException : Exception
|
||||
}
|
||||
}
|
||||
|
||||
public static void ThrowIfNot(bool condition, HutaoExceptionKind kind, string message, Exception? innerException = default)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw new HutaoException(kind, message, innerException);
|
||||
}
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
public static HutaoException GachaStatisticsInvalidItemId(uint id, Exception? innerException = default)
|
||||
{
|
||||
string message = SH.FormatServiceGachaStatisticsFactoryItemIdInvalid(id);
|
||||
throw new HutaoException(HutaoExceptionKind.GachaStatisticsInvalidItemId, message, innerException);
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
public static InvalidCastException InvalidCast<TFrom, TTo>(string name, Exception? innerException = default)
|
||||
public static HutaoException ServiceTypeCastFailed<TFrom, TTo>(string name, Exception? innerException = default)
|
||||
{
|
||||
string message = $"This instance of '{typeof(TFrom).FullName}' '{name}' doesn't implement '{typeof(TTo).FullName}'";
|
||||
throw new InvalidCastException(message, innerException);
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
public static OperationCanceledException OperationCanceled(string message, Exception? innerException = default)
|
||||
{
|
||||
return new OperationCanceledException(message, innerException);
|
||||
throw new HutaoException(HutaoExceptionKind.ServiceTypeCastFailed, message, innerException);
|
||||
}
|
||||
}
|
||||
@@ -6,15 +6,7 @@ namespace Snap.Hutao.Core.ExceptionService;
|
||||
internal enum HutaoExceptionKind
|
||||
{
|
||||
None,
|
||||
|
||||
// Foundation
|
||||
ImageCacheInvalidUri,
|
||||
|
||||
// IO
|
||||
ServiceTypeCastFailed,
|
||||
FileSystemCreateFileInsufficientPermissions,
|
||||
PrivateNamedPipeContentHashIncorrect,
|
||||
|
||||
// Service
|
||||
GachaStatisticsInvalidItemId,
|
||||
GameFpsUnlockingFailed,
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Snap.Hutao.Core.IO.Hashing;
|
||||
|
||||
internal static class Hash
|
||||
{
|
||||
public static string SHA1HexString(string input)
|
||||
{
|
||||
return HashCore(BitConverter.ToString, SHA1.HashData, Encoding.UTF8.GetBytes, input);
|
||||
}
|
||||
|
||||
private static TResult HashCore<TInput, TResult>(Func<byte[], TResult> resultConverter, Func<byte[], byte[]> hashMethod, Func<TInput, byte[]> bytesConverter, TInput input)
|
||||
{
|
||||
return resultConverter(hashMethod(bytesConverter(input)));
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
internal static class ConsoleVirtualTerminalSequences
|
||||
{
|
||||
public const string Default = "\u001b[0m";
|
||||
public const string Bold = "\u001b[1m";
|
||||
|
||||
public const string Underline = "\u001b[4m";
|
||||
|
||||
public const string Negative = "\u001b[7m";
|
||||
|
||||
public const string NoBold = "\u001b[22m";
|
||||
|
||||
public const string NoUnderline = "\u001b[24m";
|
||||
|
||||
public const string Positive = "\u001b[27m";
|
||||
|
||||
public const string ForegroundBlack = "\u001b[30m";
|
||||
public const string ForegroundRed = "\u001b[31m";
|
||||
public const string ForegroundGreen = "\u001b[32m";
|
||||
public const string ForegroundYellow = "\u001b[33m";
|
||||
public const string ForegroundBlue = "\u001b[34m";
|
||||
public const string ForegroundMagenta = "\u001b[35m";
|
||||
public const string ForegroundCyan = "\u001b[36m";
|
||||
public const string ForegroundWhite = "\u001b[37m";
|
||||
public const string ForegroundExtended = "\u001b[38m";
|
||||
public const string ForegroundDefault = "\u001b[39m";
|
||||
public const string BackgroundBlack = "\u001b[40m";
|
||||
public const string BackgroundRed = "\u001b[41m";
|
||||
public const string BackgroundGreen = "\u001b[42m";
|
||||
public const string BackgroundYellow = "\u001b[43m";
|
||||
public const string BackgroundBlue = "\u001b[44m";
|
||||
public const string BackgroundMagenta = "\u001b[45m";
|
||||
public const string BackgroundCyan = "\u001b[46m";
|
||||
public const string BackgroundWhite = "\u001b[47m";
|
||||
public const string BackgroundExtended = "\u001b[48m";
|
||||
public const string BackgroundDefault = "\u001b[49m";
|
||||
|
||||
public const string BrightForegroundBlack = "\u001b[1m\u001b[30m";
|
||||
public const string BrightForegroundRed = "\u001b[1m\u001b[31m";
|
||||
public const string BrightForegroundGreen = "\u001b[1m\u001b[32m";
|
||||
public const string BrightForegroundYellow = "\u001b[1m\u001b[33m";
|
||||
public const string BrightForegroundBlue = "\u001b[1m\u001b[34m";
|
||||
public const string BrightForegroundMagenta = "\u001b[1m\u001b[35m";
|
||||
public const string BrightForegroundCyan = "\u001b[1m\u001b[36m";
|
||||
public const string BrightForegroundWhite = "\u001b[1m\u001b[37m";
|
||||
public const string BrightBackgroundBlack = "\u001b[1m\u001b[40m";
|
||||
public const string BrightBackgroundRed = "\u001b[1m\u001b[41m";
|
||||
public const string BrightBackgroundGreen = "\u001b[1m\u001b[42m";
|
||||
public const string BrightBackgroundYellow = "\u001b[1m\u001b[43m";
|
||||
public const string BrightBackgroundBlue = "\u001b[1m\u001b[44m";
|
||||
public const string BrightBackgroundMagenta = "\u001b[1m\u001b[45m";
|
||||
public const string BrightBackgroundCyan = "\u001b[1m\u001b[46m";
|
||||
public const string BrightBackgroundWhite = "\u001b[1m\u001b[47m";
|
||||
|
||||
public const string Dim = "\u001b[2m";
|
||||
public const string Italic = "\u001b[3m";
|
||||
|
||||
public const string Blink = "\u001b[5m";
|
||||
|
||||
public const string Hidden = "\u001b[8m";
|
||||
public const string StrikeThrough = "\u001b[9m";
|
||||
public const string DoubleUnderline = "\u001b[21m";
|
||||
|
||||
public const string NoItalic = "\u001b[23m";
|
||||
|
||||
public const string NoBlink = "\u001b[25m";
|
||||
|
||||
public const string NoHidden = "\u001b[28m";
|
||||
public const string NoStrikeThrough = "\u001b[29m";
|
||||
|
||||
public static string FromConsoleColor(ConsoleColor color, bool foreground)
|
||||
{
|
||||
return (foreground, color) switch
|
||||
{
|
||||
(true, ConsoleColor.Black) => ForegroundBlack,
|
||||
(true, ConsoleColor.DarkBlue) => ForegroundBlue,
|
||||
(true, ConsoleColor.DarkGreen) => ForegroundGreen,
|
||||
(true, ConsoleColor.DarkCyan) => ForegroundCyan,
|
||||
(true, ConsoleColor.DarkRed) => ForegroundRed,
|
||||
(true, ConsoleColor.DarkMagenta) => ForegroundMagenta,
|
||||
(true, ConsoleColor.DarkYellow) => ForegroundYellow,
|
||||
(true, ConsoleColor.DarkGray) => BrightForegroundBlack,
|
||||
(true, ConsoleColor.Gray) => ForegroundWhite,
|
||||
(true, ConsoleColor.Blue) => BrightForegroundBlue,
|
||||
(true, ConsoleColor.Green) => BrightForegroundGreen,
|
||||
(true, ConsoleColor.Cyan) => BrightForegroundCyan,
|
||||
(true, ConsoleColor.Red) => BrightForegroundRed,
|
||||
(true, ConsoleColor.Magenta) => BrightForegroundMagenta,
|
||||
(true, ConsoleColor.Yellow) => BrightForegroundYellow,
|
||||
(true, ConsoleColor.White) => BrightForegroundWhite,
|
||||
(false, ConsoleColor.Black) => BackgroundBlack,
|
||||
(false, ConsoleColor.DarkBlue) => BackgroundBlue,
|
||||
(false, ConsoleColor.DarkGreen) => BackgroundGreen,
|
||||
(false, ConsoleColor.DarkCyan) => BackgroundCyan,
|
||||
(false, ConsoleColor.DarkRed) => BackgroundRed,
|
||||
(false, ConsoleColor.DarkMagenta) => BackgroundMagenta,
|
||||
(false, ConsoleColor.DarkYellow) => BackgroundYellow,
|
||||
(false, ConsoleColor.DarkGray) => BrightBackgroundBlack,
|
||||
(false, ConsoleColor.Gray) => BackgroundWhite,
|
||||
(false, ConsoleColor.Blue) => BrightBackgroundBlue,
|
||||
(false, ConsoleColor.Green) => BrightBackgroundGreen,
|
||||
(false, ConsoleColor.Cyan) => BrightBackgroundCyan,
|
||||
(false, ConsoleColor.Red) => BrightBackgroundRed,
|
||||
(false, ConsoleColor.Magenta) => BrightBackgroundMagenta,
|
||||
(false, ConsoleColor.Yellow) => BrightBackgroundYellow,
|
||||
(false, ConsoleColor.White) => BrightBackgroundWhite,
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ internal sealed class ConsoleWindowLifeTime : IDisposable
|
||||
HANDLE inputHandle = GetStdHandle(STD_HANDLE.STD_INPUT_HANDLE);
|
||||
if (GetConsoleMode(inputHandle, out CONSOLE_MODE mode))
|
||||
{
|
||||
mode &= ~CONSOLE_MODE.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
mode &= ~CONSOLE_MODE.ENABLE_QUICK_EDIT_MODE;
|
||||
SetConsoleMode(inputHandle, mode);
|
||||
}
|
||||
|
||||
@@ -38,4 +38,4 @@ internal sealed class ConsoleWindowLifeTime : IDisposable
|
||||
FreeConsole();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
internal readonly struct LogArgument
|
||||
{
|
||||
public readonly object? Argument;
|
||||
public readonly ConsoleColor? ForegroundColor;
|
||||
public readonly ConsoleColor? BackgroundColor;
|
||||
|
||||
public LogArgument(object? argument, ConsoleColor? foreground = default, ConsoleColor? background = default)
|
||||
{
|
||||
Argument = argument;
|
||||
ForegroundColor = foreground;
|
||||
BackgroundColor = background;
|
||||
}
|
||||
|
||||
public static implicit operator LogArgument(string argument)
|
||||
{
|
||||
return new(argument);
|
||||
}
|
||||
|
||||
public static implicit operator LogArgument((object? Argument, ConsoleColor Foreground) tuple)
|
||||
{
|
||||
return new(tuple.Argument, tuple.Foreground);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
internal readonly struct LogMessage
|
||||
{
|
||||
public readonly string Message;
|
||||
public readonly ConsoleColor? ForegroundColor;
|
||||
public readonly ConsoleColor? BackgroundColor;
|
||||
|
||||
public LogMessage(string message, ConsoleColor? foreground = default, ConsoleColor? background = default)
|
||||
{
|
||||
Message = message;
|
||||
ForegroundColor = foreground;
|
||||
BackgroundColor = background;
|
||||
}
|
||||
|
||||
public static implicit operator LogMessage(string value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static implicit operator LogMessage((string Value, ConsoleColor? Foreground) tuple)
|
||||
{
|
||||
return new(tuple.Value, tuple.Foreground);
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
internal static class LoggerExtension
|
||||
{
|
||||
public static void LogColorizedDebug(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Debug, exception, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedDebug(this ILogger logger, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Debug, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedTrace(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Trace, exception, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedTrace(this ILogger logger, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Trace, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedInformation(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Information, exception, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedInformation(this ILogger logger, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Information, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedWarning(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Warning, exception, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedWarning(this ILogger logger, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Warning, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedError(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Error, exception, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedError(this ILogger logger, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Error, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedCritical(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Critical, exception, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorizedCritical(this ILogger logger, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(LogLevel.Critical, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorized(this ILogger logger, LogLevel logLevel, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(logLevel, 0, null, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorized(this ILogger logger, LogLevel logLevel, EventId eventId, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(logLevel, eventId, null, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorized(this ILogger logger, LogLevel logLevel, Exception? exception, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
logger.LogColorized(logLevel, 0, exception, message, args);
|
||||
}
|
||||
|
||||
public static void LogColorized(this ILogger logger, LogLevel logLevel, EventId eventId, Exception? exception, LogMessage message, params LogArgument[] args)
|
||||
{
|
||||
string colorizedMessage = Colorize(message, args, out object?[] outArgs)!;
|
||||
logger.Log(logLevel, eventId, exception, colorizedMessage, outArgs);
|
||||
}
|
||||
|
||||
private static string? Colorize(LogMessage message, LogArgument[] args, out object?[] outArgs)
|
||||
{
|
||||
StringBuilder resultMessageBuilder = new(message.Message.Length);
|
||||
ReadOnlySpan<char> messageSpan = message.Message.AsSpan();
|
||||
|
||||
// Message base colors
|
||||
ConsoleColor? messageForeground = message.ForegroundColor;
|
||||
ConsoleColor? messageBackground = message.BackgroundColor;
|
||||
|
||||
if (messageForeground.HasValue)
|
||||
{
|
||||
resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(messageForeground.Value, true));
|
||||
}
|
||||
|
||||
if (messageBackground.HasValue)
|
||||
{
|
||||
resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(messageBackground.Value, false));
|
||||
}
|
||||
|
||||
ReadOnlySpan<LogArgument> argSpan = args.AsSpan();
|
||||
outArgs = new object?[args.Length];
|
||||
|
||||
int argIndex = 0;
|
||||
for (int index = 0; index < messageSpan.Length; index++)
|
||||
{
|
||||
if (messageSpan[index] == '{')
|
||||
{
|
||||
ref readonly LogArgument arg = ref argSpan[argIndex];
|
||||
outArgs[argIndex] = arg.Argument;
|
||||
argIndex++;
|
||||
if (arg.ForegroundColor.HasValue)
|
||||
{
|
||||
resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(arg.ForegroundColor.Value, true));
|
||||
}
|
||||
|
||||
if (arg.BackgroundColor.HasValue)
|
||||
{
|
||||
resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(arg.BackgroundColor.Value, false));
|
||||
}
|
||||
|
||||
int closingIndex = messageSpan[index..].IndexOf('}');
|
||||
resultMessageBuilder.Append(messageSpan.Slice(index, closingIndex + 1));
|
||||
|
||||
index += closingIndex;
|
||||
|
||||
if (arg.ForegroundColor.HasValue || arg.BackgroundColor.HasValue)
|
||||
{
|
||||
// Restore message colors
|
||||
if (messageForeground.HasValue || messageBackground.HasValue)
|
||||
{
|
||||
if (messageForeground.HasValue)
|
||||
{
|
||||
resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(messageForeground.Value, true));
|
||||
}
|
||||
|
||||
if (messageBackground.HasValue)
|
||||
{
|
||||
resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(messageBackground.Value, false));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resultMessageBuilder.Append(messageSpan[index]);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore default colors
|
||||
if (message.ForegroundColor.HasValue || message.BackgroundColor.HasValue)
|
||||
{
|
||||
resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.Default);
|
||||
}
|
||||
|
||||
return resultMessageBuilder.ToString();
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,13 @@
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
internal static class LoggerFactoryExtension
|
||||
internal static class LoggerFactoryExtensions
|
||||
{
|
||||
public static ILoggingBuilder AddConsoleWindow(this ILoggingBuilder builder)
|
||||
{
|
||||
builder.Services.AddSingleton<ConsoleWindowLifeTime>();
|
||||
|
||||
builder.AddSimpleConsole(options =>
|
||||
{
|
||||
options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff ";
|
||||
});
|
||||
|
||||
builder.AddSimpleConsole();
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,11 @@ internal static class RuntimeOptionsExtension
|
||||
|
||||
public static string GetDataFolderServerCacheFolder(this RuntimeOptions options)
|
||||
{
|
||||
string directory = Path.Combine(options.DataFolder, "ServerCache");
|
||||
Directory.CreateDirectory(directory);
|
||||
return directory;
|
||||
return Path.Combine(options.DataFolder, "ServerCache");
|
||||
}
|
||||
|
||||
public static string GetDataFolderBackgroundFolder(this RuntimeOptions options)
|
||||
{
|
||||
string directory = Path.Combine(options.DataFolder, "Background");
|
||||
Directory.CreateDirectory(directory);
|
||||
return directory;
|
||||
return Path.Combine(options.DataFolder, "Background");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Collections.Frozen;
|
||||
using System.Text;
|
||||
|
||||
namespace Snap.Hutao.Core;
|
||||
@@ -15,25 +14,25 @@ internal static class TypeNameHelper
|
||||
{
|
||||
private const char DefaultNestedTypeDelimiter = '+';
|
||||
|
||||
private static readonly FrozenDictionary<Type, string> BuiltInTypeNames = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(typeof(void), "void"),
|
||||
KeyValuePair.Create(typeof(bool), "bool"),
|
||||
KeyValuePair.Create(typeof(byte), "byte"),
|
||||
KeyValuePair.Create(typeof(char), "char"),
|
||||
KeyValuePair.Create(typeof(decimal), "decimal"),
|
||||
KeyValuePair.Create(typeof(double), "double"),
|
||||
KeyValuePair.Create(typeof(float), "float"),
|
||||
KeyValuePair.Create(typeof(int), "int"),
|
||||
KeyValuePair.Create(typeof(long), "long"),
|
||||
KeyValuePair.Create(typeof(object), "object"),
|
||||
KeyValuePair.Create(typeof(sbyte), "sbyte"),
|
||||
KeyValuePair.Create(typeof(short), "short"),
|
||||
KeyValuePair.Create(typeof(string), "string"),
|
||||
KeyValuePair.Create(typeof(uint), "uint"),
|
||||
KeyValuePair.Create(typeof(ulong), "ulong"),
|
||||
KeyValuePair.Create(typeof(ushort), "ushort"),
|
||||
]);
|
||||
private static readonly Dictionary<Type, string> BuiltInTypeNames = new()
|
||||
{
|
||||
{ typeof(void), "void" },
|
||||
{ typeof(bool), "bool" },
|
||||
{ typeof(byte), "byte" },
|
||||
{ typeof(char), "char" },
|
||||
{ typeof(decimal), "decimal" },
|
||||
{ typeof(double), "double" },
|
||||
{ typeof(float), "float" },
|
||||
{ typeof(int), "int" },
|
||||
{ typeof(long), "long" },
|
||||
{ typeof(object), "object" },
|
||||
{ typeof(sbyte), "sbyte" },
|
||||
{ typeof(short), "short" },
|
||||
{ typeof(string), "string" },
|
||||
{ typeof(uint), "uint" },
|
||||
{ typeof(ulong), "ulong" },
|
||||
{ typeof(ushort), "ushort" },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象类型的显示名称
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace Snap.Hutao.Core.Validation;
|
||||
/// 封装验证方法,简化微软验证
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Obsolete("Use HutaoException instead")]
|
||||
internal static class Must
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -13,7 +13,6 @@ using Snap.Hutao.Win32;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.Graphics.Dwm;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.Collections.Frozen;
|
||||
using System.IO;
|
||||
using Windows.Graphics;
|
||||
using Windows.UI;
|
||||
@@ -57,22 +56,25 @@ internal sealed class WindowController
|
||||
private void InitializeCore()
|
||||
{
|
||||
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
|
||||
|
||||
window.AppWindow.Title = SH.FormatAppNameAndVersion(runtimeOptions.Version);
|
||||
window.AppWindow.SetIcon(Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico"));
|
||||
ExtendsContentIntoTitleBar();
|
||||
|
||||
RecoverOrInitWindowSize();
|
||||
UpdateElementTheme(appOptions.ElementTheme);
|
||||
UpdateImmersiveDarkMode(options.TitleBar, default!);
|
||||
|
||||
// appWindow.Show(true);
|
||||
// appWindow.Show can't bring window to top.
|
||||
window.Activate();
|
||||
options.BringToForeground();
|
||||
UpdateSystemBackdrop(appOptions.BackdropType);
|
||||
appOptions.PropertyChanged += OnOptionsPropertyChanged;
|
||||
|
||||
if (options.UseSystemBackdrop)
|
||||
{
|
||||
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
|
||||
UpdateSystemBackdrop(appOptions.BackdropType);
|
||||
appOptions.PropertyChanged += OnOptionsPropertyChanged;
|
||||
}
|
||||
|
||||
subclass.Initialize();
|
||||
|
||||
@@ -120,17 +122,13 @@ internal sealed class WindowController
|
||||
|
||||
private void OnOptionsPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (sender is not AppOptions options)
|
||||
if (e.PropertyName is nameof(AppOptions.BackdropType))
|
||||
{
|
||||
return;
|
||||
if (sender is AppOptions options)
|
||||
{
|
||||
UpdateSystemBackdrop(options.BackdropType);
|
||||
}
|
||||
}
|
||||
|
||||
_ = e.PropertyName switch
|
||||
{
|
||||
nameof(AppOptions.BackdropType) => UpdateSystemBackdrop(options.BackdropType),
|
||||
nameof(AppOptions.ElementTheme) => UpdateElementTheme(options.ElementTheme),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
private void OnWindowClosed(object sender, WindowEventArgs args)
|
||||
@@ -160,7 +158,7 @@ internal sealed class WindowController
|
||||
}
|
||||
}
|
||||
|
||||
private bool UpdateSystemBackdrop(BackdropType backdropType)
|
||||
private void UpdateSystemBackdrop(BackdropType backdropType)
|
||||
{
|
||||
window.SystemBackdrop = backdropType switch
|
||||
{
|
||||
@@ -170,15 +168,6 @@ internal sealed class WindowController
|
||||
BackdropType.Acrylic => new DesktopAcrylicBackdrop(),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool UpdateElementTheme(ElementTheme theme)
|
||||
{
|
||||
((FrameworkElement)window.Content).RequestedTheme = theme;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateTitleButtonColor()
|
||||
@@ -188,12 +177,12 @@ internal sealed class WindowController
|
||||
appTitleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||
appTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||
|
||||
bool isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(options.TitleBar.ActualTheme);
|
||||
IAppResourceProvider resourceProvider = serviceProvider.GetRequiredService<IAppResourceProvider>();
|
||||
|
||||
Color systemBaseLowColor = Control.Theme.SystemColors.BaseLowColor(isDarkMode);
|
||||
Color systemBaseLowColor = resourceProvider.GetResource<Color>("SystemBaseLowColor");
|
||||
appTitleBar.ButtonHoverBackgroundColor = systemBaseLowColor;
|
||||
|
||||
Color systemBaseMediumLowColor = Control.Theme.SystemColors.BaseMediumLowColor(isDarkMode);
|
||||
Color systemBaseMediumLowColor = resourceProvider.GetResource<Color>("SystemBaseMediumLowColor");
|
||||
appTitleBar.ButtonPressedBackgroundColor = systemBaseMediumLowColor;
|
||||
|
||||
// The Foreground doesn't accept Alpha channel. So we translate it to gray.
|
||||
@@ -201,7 +190,7 @@ internal sealed class WindowController
|
||||
byte result = (byte)((systemBaseMediumLowColor.A / 255.0) * light);
|
||||
appTitleBar.ButtonInactiveForegroundColor = Color.FromArgb(0xFF, result, result, result);
|
||||
|
||||
Color systemBaseHighColor = Control.Theme.SystemColors.BaseHighColor(isDarkMode);
|
||||
Color systemBaseHighColor = resourceProvider.GetResource<Color>("SystemBaseHighColor");
|
||||
appTitleBar.ButtonForegroundColor = systemBaseHighColor;
|
||||
appTitleBar.ButtonHoverForegroundColor = systemBaseHighColor;
|
||||
appTitleBar.ButtonPressedForegroundColor = systemBaseHighColor;
|
||||
|
||||
@@ -41,18 +41,21 @@ internal readonly struct WindowOptions
|
||||
/// </summary>
|
||||
public readonly bool PersistSize;
|
||||
|
||||
public readonly bool UseSystemBackdrop;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用 Win UI 3 自带的拓展标题栏实现
|
||||
/// </summary>
|
||||
public readonly bool UseLegacyDragBarImplementation = !AppWindowTitleBar.IsCustomizationSupported();
|
||||
|
||||
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, bool persistSize = false)
|
||||
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, bool persistSize = false, bool useSystemBackdrop = true)
|
||||
{
|
||||
Hwnd = WindowNative.GetWindowHandle(window);
|
||||
InputNonClientPointerSource = InputNonClientPointerSource.GetForWindowId(window.AppWindow.Id);
|
||||
TitleBar = titleBar;
|
||||
InitSize = initSize;
|
||||
PersistSize = persistSize;
|
||||
UseSystemBackdrop = useSystemBackdrop;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -200,13 +200,6 @@ internal static partial class EnumerableExtension
|
||||
return list;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public static List<TSource> SortBy<TSource, TKey>(this List<TSource> list, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
|
||||
{
|
||||
list.Sort((left, right) => comparer.Compare(keySelector(left), keySelector(right)));
|
||||
return list;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public static List<TSource> SortByDescending<TSource, TKey>(this List<TSource> list, Func<TSource, TKey> keySelector)
|
||||
where TKey : IComparable
|
||||
@@ -214,11 +207,4 @@ internal static partial class EnumerableExtension
|
||||
list.Sort((left, right) => keySelector(right).CompareTo(keySelector(left)));
|
||||
return list;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public static List<TSource> SortByDescending<TSource, TKey>(this List<TSource> list, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
|
||||
{
|
||||
list.Sort((left, right) => comparer.Compare(keySelector(right), keySelector(left)));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,6 @@ namespace Snap.Hutao.Extension;
|
||||
[HighQuality]
|
||||
internal static partial class EnumerableExtension
|
||||
{
|
||||
public static void Deconstruct<TKey, TElement>(this IGrouping<TKey, TElement> grouping, out TKey key, out IEnumerable<TElement> elements)
|
||||
{
|
||||
key = grouping.Key;
|
||||
elements = grouping;
|
||||
}
|
||||
|
||||
public static TElement? ElementAtOrLastOrDefault<TElement>(this IEnumerable<TElement> source, int index)
|
||||
{
|
||||
return source.ElementAtOrDefault(index) ?? source.LastOrDefault();
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Service;
|
||||
|
||||
namespace Snap.Hutao.Factory.ContentDialog;
|
||||
|
||||
@@ -16,7 +15,6 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
private readonly ICurrentWindowReference currentWindowReference;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly AppOptions appOptions;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<ContentDialogResult> CreateForConfirmAsync(string title, string content)
|
||||
@@ -29,7 +27,6 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
Content = content,
|
||||
DefaultButton = ContentDialogButton.Primary,
|
||||
PrimaryButtonText = SH.ContentDialogConfirmPrimaryButtonText,
|
||||
RequestedTheme = appOptions.ElementTheme,
|
||||
};
|
||||
|
||||
return await dialog.ShowAsync();
|
||||
@@ -47,7 +44,6 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
DefaultButton = defaultButton,
|
||||
PrimaryButtonText = SH.ContentDialogConfirmPrimaryButtonText,
|
||||
CloseButtonText = SH.ContentDialogCancelCloseButtonText,
|
||||
RequestedTheme = appOptions.ElementTheme,
|
||||
};
|
||||
|
||||
return await dialog.ShowAsync();
|
||||
@@ -62,7 +58,6 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||
Title = title,
|
||||
Content = new ProgressBar() { IsIndeterminate = true },
|
||||
RequestedTheme = appOptions.ElementTheme,
|
||||
};
|
||||
|
||||
return dialog;
|
||||
@@ -74,7 +69,6 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
||||
contentDialog.RequestedTheme = appOptions.ElementTheme;
|
||||
return contentDialog;
|
||||
}
|
||||
|
||||
@@ -83,7 +77,6 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
{
|
||||
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
||||
contentDialog.RequestedTheme = appOptions.ElementTheme;
|
||||
return contentDialog;
|
||||
}
|
||||
}
|
||||
@@ -41,30 +41,6 @@
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "FurnitureId",
|
||||
"Documentation": "家具 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "FurnitureMakeId",
|
||||
"Documentation": "家具配方 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "FurnitureSuiteId",
|
||||
"Documentation": "家具套装 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "FurnitureTypeId",
|
||||
"Documentation": "家具分类 Id",
|
||||
"Equatable": true,
|
||||
"EqualityOperators": true
|
||||
},
|
||||
{
|
||||
"Name": "Level",
|
||||
"Documentation": "等级 1 - 90",
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Model.Entity.Configuration;
|
||||
using System.Diagnostics;
|
||||
|
||||
@@ -35,7 +34,7 @@ internal sealed class AppDbContext : DbContext
|
||||
: this(options)
|
||||
{
|
||||
this.logger = logger;
|
||||
logger.LogColorizedInformation("{Name}[{Id}] {Action}", nameof(AppDbContext), (ContextId, ConsoleColor.DarkCyan), ("created", ConsoleColor.Green));
|
||||
logger.LogInformation("{Name}[{Id}] created", nameof(AppDbContext), ContextId);
|
||||
}
|
||||
|
||||
public DbSet<SettingEntry> Settings { get; set; } = default!;
|
||||
@@ -88,7 +87,7 @@ internal sealed class AppDbContext : DbContext
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
logger?.LogColorizedInformation("{Name}[{Id}] {Action}", nameof(AppDbContext), (ContextId, ConsoleColor.DarkCyan), ("disposed", ConsoleColor.Red));
|
||||
logger?.LogInformation("{Name}[{Id}] disposed", nameof(AppDbContext), ContextId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -42,14 +42,14 @@ internal sealed partial class GachaItem
|
||||
/// <summary>
|
||||
/// 祈愿记录分类
|
||||
/// </summary>
|
||||
public GachaType GachaType { get; set; }
|
||||
public GachaConfigType GachaType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿记录查询分类
|
||||
/// 合并保底的卡池使用此属性
|
||||
/// 仅4种(不含400)
|
||||
/// </summary>
|
||||
public GachaType QueryType { get; set; }
|
||||
public GachaConfigType QueryType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
|
||||
@@ -13,7 +13,6 @@ internal sealed partial class SettingEntry
|
||||
public const string Culture = "Culture";
|
||||
|
||||
public const string SystemBackdropType = "SystemBackdropType";
|
||||
public const string ElementTheme = "ElementTheme";
|
||||
public const string BackgroundImageType = "BackgroundImageType";
|
||||
|
||||
public const string AnnouncementRegion = "AnnouncementRegion";
|
||||
|
||||
@@ -17,7 +17,7 @@ internal sealed class UIGF : IJsonOnSerializing, IJsonOnDeserialized
|
||||
/// <summary>
|
||||
/// 当前版本
|
||||
/// </summary>
|
||||
public const string CurrentVersion = "v3.0";
|
||||
public const string CurrentVersion = "v2.4";
|
||||
|
||||
/// <summary>
|
||||
/// 信息
|
||||
@@ -61,7 +61,6 @@ internal sealed class UIGF : IJsonOnSerializing, IJsonOnDeserialized
|
||||
"v2.2" => UIGFVersion.Major2Minor2OrLower,
|
||||
"v2.3" => UIGFVersion.Major2Minor3OrHigher,
|
||||
"v2.4" => UIGFVersion.Major2Minor3OrHigher,
|
||||
"v3.0" => UIGFVersion.Major2Minor3OrHigher,
|
||||
_ => UIGFVersion.NotSupported,
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ internal sealed class UIGFItem : GachaLogItem, IMappingFrom<UIGFItem, GachaItem,
|
||||
/// </summary>
|
||||
[JsonPropertyName("uigf_gacha_type")]
|
||||
[JsonEnum(JsonSerializeType.NumberString)]
|
||||
public GachaType UIGFGachaType { get; set; } = default!;
|
||||
public GachaConfigType UIGFGachaType { get; set; } = default!;
|
||||
|
||||
public static UIGFItem From(GachaItem item, INameQuality nameQuality)
|
||||
{
|
||||
|
||||
@@ -11,26 +11,34 @@ namespace Snap.Hutao.Model.Intrinsic.Frozen;
|
||||
[HighQuality]
|
||||
internal static class IntrinsicFrozen
|
||||
{
|
||||
/// <summary>
|
||||
/// 所属地区
|
||||
/// </summary>
|
||||
public static FrozenSet<string> AssociationTypes { get; } = Enum.GetValues<AssociationType>().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType<string>().ToFrozenSet();
|
||||
|
||||
public static FrozenSet<NameValue<AssociationType>> AssociationTypeNameValues { get; } = Enum.GetValues<AssociationType>().Select(e => new NameValue<AssociationType>(e.GetLocalizedDescriptionOrDefault()!, e)).Where(nv => !string.IsNullOrEmpty(nv.Name)).ToFrozenSet();
|
||||
|
||||
/// <summary>
|
||||
/// 武器类型
|
||||
/// </summary>
|
||||
public static FrozenSet<string> WeaponTypes { get; } = Enum.GetValues<WeaponType>().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType<string>().ToFrozenSet();
|
||||
|
||||
public static FrozenSet<NameValue<WeaponType>> WeaponTypeNameValues { get; } = Enum.GetValues<WeaponType>().Select(e => new NameValue<WeaponType>(e.GetLocalizedDescriptionOrDefault()!, e)).Where(nv => !string.IsNullOrEmpty(nv.Name)).ToFrozenSet();
|
||||
|
||||
/// <summary>
|
||||
/// 物品类型
|
||||
/// </summary>
|
||||
public static FrozenSet<string> ItemQualities { get; } = Enum.GetValues<QualityType>().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType<string>().ToFrozenSet();
|
||||
|
||||
public static FrozenSet<NameValue<QualityType>> ItemQualityNameValues { get; } = Enum.GetValues<QualityType>().Select(e => new NameValue<QualityType>(e.GetLocalizedDescriptionOrDefault()!, e)).Where(nv => !string.IsNullOrEmpty(nv.Name)).ToFrozenSet();
|
||||
|
||||
/// <summary>
|
||||
/// 身材类型
|
||||
/// </summary>
|
||||
public static FrozenSet<string> BodyTypes { get; } = Enum.GetValues<BodyType>().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType<string>().ToFrozenSet();
|
||||
|
||||
public static FrozenSet<NameValue<BodyType>> BodyTypeNameValues { get; } = Enum.GetValues<BodyType>().Select(e => new NameValue<BodyType>(e.GetLocalizedDescriptionOrDefault()!, e)).Where(nv => !string.IsNullOrEmpty(nv.Name)).ToFrozenSet();
|
||||
|
||||
/// <summary>
|
||||
/// 战斗属性
|
||||
/// </summary>
|
||||
public static FrozenSet<string> FightProperties { get; } = Enum.GetValues<FightProperty>().Select(e => e.GetLocalizedDescriptionOrDefault()).OfType<string>().ToFrozenSet();
|
||||
|
||||
public static FrozenSet<NameValue<FightProperty>> FightPropertyNameValues { get; } = Enum.GetValues<FightProperty>().Select(e => new NameValue<FightProperty>(e.GetLocalizedDescriptionOrDefault()!, e)).Where(nv => !string.IsNullOrEmpty(nv.Name)).ToFrozenSet();
|
||||
|
||||
/// <summary>
|
||||
/// 元素名称
|
||||
/// </summary>
|
||||
public static FrozenSet<string> ElementNames { get; } = FrozenSet.ToFrozenSet(
|
||||
[
|
||||
SH.ModelIntrinsicElementNameFire,
|
||||
@@ -42,17 +50,6 @@ internal static class IntrinsicFrozen
|
||||
SH.ModelIntrinsicElementNameRock,
|
||||
]);
|
||||
|
||||
public static FrozenSet<NameValue<int>> ElementNameValues { get; } = FrozenSet.ToFrozenSet(
|
||||
[
|
||||
new NameValue<int>(SH.ModelIntrinsicElementNameFire, 1),
|
||||
new NameValue<int>(SH.ModelIntrinsicElementNameWater, 2),
|
||||
new NameValue<int>(SH.ModelIntrinsicElementNameGrass, 3),
|
||||
new NameValue<int>(SH.ModelIntrinsicElementNameElec, 4),
|
||||
new NameValue<int>(SH.ModelIntrinsicElementNameWind, 5),
|
||||
new NameValue<int>(SH.ModelIntrinsicElementNameIce, 6),
|
||||
new NameValue<int>(SH.ModelIntrinsicElementNameRock, 7),
|
||||
]);
|
||||
|
||||
public static FrozenSet<string> MaterialTypeDescriptions { get; } = FrozenSet.ToFrozenSet(
|
||||
[
|
||||
SH.ModelMetadataMaterialCharacterAndWeaponEnhancementMaterial,
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
internal enum FurnitureDeploySurfaceType
|
||||
{
|
||||
Ground = 0,
|
||||
Wall = 1,
|
||||
Ceil = 2,
|
||||
StackObjPlane = 3,
|
||||
Door = 4,
|
||||
Chandelier = 5,
|
||||
Floor = 6,
|
||||
WallBody = 7,
|
||||
Carpet = 8,
|
||||
LegoRockery = 9,
|
||||
Stair = 10,
|
||||
NPC = 11,
|
||||
Animal = 12,
|
||||
Apartment = 13,
|
||||
FurnitureSuite = 14,
|
||||
Road = 15,
|
||||
Terrain = 16,
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
internal enum FurnitureDeployType
|
||||
{
|
||||
Interior,
|
||||
Exterior,
|
||||
InteriorRoom,
|
||||
InteriorHall,
|
||||
Skybox,
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
internal enum GroupRecordType
|
||||
{
|
||||
None,
|
||||
Racing,
|
||||
Balloon,
|
||||
Stake,
|
||||
Seek,
|
||||
Explosion,
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
internal enum SpecialFurnitureType
|
||||
{
|
||||
NormalFurniture,
|
||||
BlockDependent,
|
||||
FarmField,
|
||||
TeleportPoint,
|
||||
Fishpond,
|
||||
Npc,
|
||||
Apartment,
|
||||
FurnitureSuite,
|
||||
Paimon,
|
||||
Fish,
|
||||
CustomBaseFurniture,
|
||||
CustomNodeFurniture,
|
||||
VirtualFurniture,
|
||||
GroupFurniture,
|
||||
CoopPictureFrame,
|
||||
ChangeBgmFurniture,
|
||||
ServerGadget,
|
||||
Fishtank,
|
||||
}
|
||||
@@ -1,9 +1,22 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Achievement;
|
||||
|
||||
/// <summary>
|
||||
/// 奖励
|
||||
/// </summary>
|
||||
internal sealed class Reward : IdCount;
|
||||
internal sealed class Reward
|
||||
{
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public MaterialId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数量
|
||||
/// </summary>
|
||||
public uint Count { get; set; }
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata;
|
||||
|
||||
// CityTaskOpenExcelConfig
|
||||
internal enum City : uint
|
||||
{
|
||||
Mondstadt = 1,
|
||||
Liyue = 2,
|
||||
Inazuma = 3,
|
||||
Sumeru = 4,
|
||||
Fontaine = 5,
|
||||
Natlan,
|
||||
Snezhnaya,
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using System.Collections.Frozen;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
internal sealed class AssociationTypeIconConverter : ValueConverter<AssociationType, Uri?>
|
||||
{
|
||||
public static Uri? AssociationTypeToIconUri(AssociationType type)
|
||||
{
|
||||
string? association = type switch
|
||||
{
|
||||
AssociationType.ASSOC_TYPE_MONDSTADT => "Mengde",
|
||||
AssociationType.ASSOC_TYPE_LIYUE => "Liyue",
|
||||
AssociationType.ASSOC_TYPE_FATUI => default,
|
||||
AssociationType.ASSOC_TYPE_INAZUMA => "Inazuma",
|
||||
AssociationType.ASSOC_TYPE_RANGER => default,
|
||||
AssociationType.ASSOC_TYPE_SUMERU => "Sumeru",
|
||||
AssociationType.ASSOC_TYPE_FONTAINE => "Fontaine",
|
||||
AssociationType.ASSOC_TYPE_NATLAN => default,
|
||||
AssociationType.ASSOC_TYPE_SNEZHNAYA => default,
|
||||
_ => throw Must.NeverHappen(),
|
||||
};
|
||||
|
||||
return association is null
|
||||
? default
|
||||
: Web.HutaoEndpoints.StaticRaw("ChapterIcon", $"UI_ChapterIcon_{association}.png").ToUri();
|
||||
}
|
||||
|
||||
public override Uri? Convert(AssociationType from)
|
||||
{
|
||||
return AssociationTypeToIconUri(from);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using System.Collections.Frozen;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
@@ -13,27 +12,27 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
[HighQuality]
|
||||
internal sealed class ElementNameIconConverter : ValueConverter<string, Uri>
|
||||
{
|
||||
private static readonly FrozenDictionary<string, string> LocalizedNameToElementIconName = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameElec, "Electric"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameFire, "Fire"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameGrass, "Grass"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameIce, "Ice"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameRock, "Rock"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameWater, "Water"),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameWind, "Wind"),
|
||||
]);
|
||||
private static readonly Dictionary<string, string> LocalizedNameToElementIconName = new()
|
||||
{
|
||||
[SH.ModelIntrinsicElementNameElec] = "Electric",
|
||||
[SH.ModelIntrinsicElementNameFire] = "Fire",
|
||||
[SH.ModelIntrinsicElementNameGrass] = "Grass",
|
||||
[SH.ModelIntrinsicElementNameIce] = "Ice",
|
||||
[SH.ModelIntrinsicElementNameRock] = "Rock",
|
||||
[SH.ModelIntrinsicElementNameWater] = "Water",
|
||||
[SH.ModelIntrinsicElementNameWind] = "Wind",
|
||||
};
|
||||
|
||||
private static readonly FrozenDictionary<string, ElementType> LocalizedNameToElementType = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameElec, ElementType.Electric),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameFire, ElementType.Fire),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameGrass, ElementType.Grass),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameIce, ElementType.Ice),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameRock, ElementType.Rock),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameWater, ElementType.Water),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicElementNameWind, ElementType.Wind),
|
||||
]);
|
||||
private static readonly Dictionary<string, ElementType> LocalizedNameToElementType = new()
|
||||
{
|
||||
[SH.ModelIntrinsicElementNameElec] = ElementType.Electric,
|
||||
[SH.ModelIntrinsicElementNameFire] = ElementType.Fire,
|
||||
[SH.ModelIntrinsicElementNameGrass] = ElementType.Grass,
|
||||
[SH.ModelIntrinsicElementNameIce] = ElementType.Ice,
|
||||
[SH.ModelIntrinsicElementNameRock] = ElementType.Rock,
|
||||
[SH.ModelIntrinsicElementNameWater] = ElementType.Water,
|
||||
[SH.ModelIntrinsicElementNameWind] = ElementType.Wind,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 将中文元素名称转换为图标链接
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
|
||||
using Microsoft.UI;
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using System.Collections.Frozen;
|
||||
using Snap.Hutao.Win32;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
@@ -16,39 +15,17 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
[HighQuality]
|
||||
internal sealed class QualityColorConverter : ValueConverter<QualityType, Color>
|
||||
{
|
||||
private static readonly FrozenDictionary<string, QualityType> LocalizedNameToQualityType = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityWhite, QualityType.QUALITY_WHITE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityGreen, QualityType.QUALITY_GREEN),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityBlue, QualityType.QUALITY_BLUE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityPurple, QualityType.QUALITY_PURPLE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityOrange, QualityType.QUALITY_ORANGE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicItemQualityRed, QualityType.QUALITY_ORANGE_SP),
|
||||
]);
|
||||
|
||||
private static readonly FrozenDictionary<QualityType, Color> QualityTypeToColor = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(QualityType.QUALITY_WHITE, KnownColors.White),
|
||||
KeyValuePair.Create(QualityType.QUALITY_GREEN, KnownColors.Green),
|
||||
KeyValuePair.Create(QualityType.QUALITY_BLUE, KnownColors.Blue),
|
||||
KeyValuePair.Create(QualityType.QUALITY_PURPLE, KnownColors.Purple),
|
||||
KeyValuePair.Create(QualityType.QUALITY_ORANGE, KnownColors.Orange),
|
||||
KeyValuePair.Create(QualityType.QUALITY_ORANGE_SP, KnownColors.Orange),
|
||||
]);
|
||||
|
||||
public static Color QualityNameToColor(string qualityName)
|
||||
{
|
||||
return QualityToColor(LocalizedNameToQualityType.GetValueOrDefault(qualityName));
|
||||
}
|
||||
|
||||
public static Color QualityToColor(QualityType quality)
|
||||
{
|
||||
return QualityTypeToColor.GetValueOrDefault(quality, Colors.Transparent);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Color Convert(QualityType from)
|
||||
{
|
||||
return QualityToColor(from);
|
||||
return from switch
|
||||
{
|
||||
QualityType.QUALITY_WHITE => StructMarshal.Color(0xFF72778B),
|
||||
QualityType.QUALITY_GREEN => StructMarshal.Color(0xFF2A8F72),
|
||||
QualityType.QUALITY_BLUE => StructMarshal.Color(0xFF5180CB),
|
||||
QualityType.QUALITY_PURPLE => StructMarshal.Color(0xFFA156E0),
|
||||
QualityType.QUALITY_ORANGE or QualityType.QUALITY_ORANGE_SP => StructMarshal.Color(0xFFBC6932),
|
||||
_ => Colors.Transparent,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using System.Collections.Frozen;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
@@ -13,20 +12,6 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
[HighQuality]
|
||||
internal sealed class WeaponTypeIconConverter : ValueConverter<WeaponType, Uri>
|
||||
{
|
||||
private static readonly FrozenDictionary<string, WeaponType> LocalizedNameToWeaponType = FrozenDictionary.ToFrozenDictionary(
|
||||
[
|
||||
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeSwordOneHand, WeaponType.WEAPON_SWORD_ONE_HAND),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeBow, WeaponType.WEAPON_BOW),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypePole, WeaponType.WEAPON_POLE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeClaymore, WeaponType.WEAPON_CLAYMORE),
|
||||
KeyValuePair.Create(SH.ModelIntrinsicWeaponTypeCatalyst, WeaponType.WEAPON_CATALYST),
|
||||
]);
|
||||
|
||||
public static Uri WeaponTypeNameToIconUri(string weaponTypeName)
|
||||
{
|
||||
return WeaponTypeToIconUri(LocalizedNameToWeaponType.GetValueOrDefault(weaponTypeName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将武器类型转换为图标链接
|
||||
/// </summary>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user