mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
1 Commits
fix/1400
...
feat/reord
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ceb26bb669 |
4
.github/ISSUE_TEMPLATE/CHS-bug-report.yml
vendored
4
.github/ISSUE_TEMPLATE/CHS-bug-report.yml
vendored
@@ -39,7 +39,7 @@ body:
|
||||
id: shver
|
||||
attributes:
|
||||
label: Snap Hutao 版本
|
||||
description: 在应用标题,应用程序的反馈中心界面中可以找到
|
||||
description: 在应用标题,应用程序的设置界面中靠下的位置可以找到
|
||||
placeholder: 例:1.4.15.0
|
||||
validations:
|
||||
required: true
|
||||
@@ -49,7 +49,7 @@ body:
|
||||
attributes:
|
||||
label: 设备 ID
|
||||
description: |
|
||||
在胡桃工具箱的反馈中心界面,你可以找到并复制你的设备 ID
|
||||
在胡桃工具箱的设置界面,你可以找到并复制你的设备 ID
|
||||
如果你的问题涉及程序崩溃,请填写该项,这将有助于我们定位问题
|
||||
如果你的程序已经无法启动,请下载并运行[诊断工具](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe),它将显示你的设备 ID
|
||||
validations:
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/ENG-bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/ENG-bug-report.yml
vendored
@@ -49,7 +49,7 @@ body:
|
||||
attributes:
|
||||
label: Device ID
|
||||
description: |
|
||||
In Snap Hutao's Feedback Center, you can find and copy your device ID
|
||||
In Snap Hutao's settings page, you can find and copy your device ID
|
||||
If your issue is about program crash, please fill this so we can dump the log and locate the source easier
|
||||
If your program cannot startup, please download and run [Diagnosis Tool](https://github.com/DGP-Automation/ISSUE_TEMPLATES/releases/download/diagnosis_tools/Snap.Hutao.DiagTools.exe), it will shows your device ID.
|
||||
validations:
|
||||
|
||||
16
.github/workflows/close_stale.yml
vendored
16
.github/workflows/close_stale.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
any-of-labels: 'needs-more-info,需要更多信息'
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 3 days.'
|
||||
days-before-stale: 7
|
||||
days-before-close: 3
|
||||
close-issue-reason: not_planned
|
||||
20
.github/workflows/issue_similarity.yml
vendored
20
.github/workflows/issue_similarity.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Issues Similarity Analysis
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
|
||||
jobs:
|
||||
similarity-analysis:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: analysis
|
||||
uses: actions-cool/issues-similarity-analysis@v1
|
||||
with:
|
||||
filter-threshold: 0.5
|
||||
comment-title: '### Probable Similar Topics'
|
||||
title-excludes: '[Publish]:,[Bug]:,[Feat]:,[Network]:,[ENG]'
|
||||
comment-body: '${index}. ${similarity} #${number}'
|
||||
show-footer: false
|
||||
show-mentioned: true
|
||||
since-days: 365
|
||||
26
.github/workflows/lock_closed_issues.yml
vendored
26
.github/workflows/lock_closed_issues.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: 'Lock Threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
concurrency:
|
||||
group: lock-threads
|
||||
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v5
|
||||
with:
|
||||
issue-inactive-days: '30'
|
||||
issue-comment: 'This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related topic.'
|
||||
issue-lock-reason: 'resolved'
|
||||
process-only: 'issues'
|
||||
log-output: false
|
||||
119
azure-pipelines.yml
Normal file
119
azure-pipelines.yml
Normal file
@@ -0,0 +1,119 @@
|
||||
# CI process script for Snap.Hutao
|
||||
# Usage:
|
||||
# 1. Append the script in Pipelines
|
||||
# 2. Upload the pfx and cer certificates to Pipelines Library secrets
|
||||
# 3. Permit the pfx usage
|
||||
# 4. Add a `pw` variable in the script variables, which is pfx password
|
||||
# 5. Connect the GitHub in project settings
|
||||
# 6. Run
|
||||
|
||||
trigger: none
|
||||
pr: none
|
||||
# trigger:
|
||||
# branches:
|
||||
# include:
|
||||
# - main
|
||||
# - develop
|
||||
# paths:
|
||||
# exclude:
|
||||
# - README.md
|
||||
# - azure-pipelines.yml
|
||||
# - .github/ISSUE_TEMPLATE/*.yml
|
||||
# - .github/workflows/*.yml
|
||||
# - src/Snap.Hutao/Snap.Hutao/Resource/Localization/*.resx
|
||||
# pr:
|
||||
# branches:
|
||||
# include:
|
||||
# - main
|
||||
# paths:
|
||||
# exclude:
|
||||
# - README.md
|
||||
# - azure-pipelines.yml
|
||||
# - .github/ISSUE_TEMPLATE/*.yml
|
||||
# - .github/workflows/*.yml
|
||||
# - src/Snap.Hutao/Snap.Hutao/Resource/Localization/*.resx
|
||||
|
||||
|
||||
pool:
|
||||
name: Default
|
||||
demands: agent.name -equals Hutao-Server
|
||||
|
||||
variables:
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
solution: '$(Build.SourcesDirectory)/src/Snap.Hutao/Snap.Hutao.sln'
|
||||
project: $(Build.SourcesDirectory)/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj'
|
||||
buildPlatform: 'x64'
|
||||
buildConfiguration: 'Release'
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: Install dotNet
|
||||
inputs:
|
||||
packageType: 'sdk'
|
||||
version: '8.x'
|
||||
includePreviewVersions: true
|
||||
|
||||
- task: CmdLine@2
|
||||
displayName: dotnet cake
|
||||
inputs:
|
||||
script: dotnet tool restore && dotnet cake
|
||||
|
||||
- task: MsixSigning@1
|
||||
name: signMsix
|
||||
displayName: Sign MSIX package
|
||||
inputs:
|
||||
package: '$(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(version).msix'
|
||||
certificate: 'DGP_Studio_CI.pfx'
|
||||
passwordVariable: 'pw'
|
||||
condition: succeeded()
|
||||
|
||||
- task: DownloadSecureFile@1
|
||||
name: cerFile
|
||||
displayName: Download Root CA
|
||||
inputs:
|
||||
secureFile: 'Snap.Hutao.CI.cer'
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
inputs:
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
||||
artifact: 'Snap.Hutao.Alpha-$(version).msix'
|
||||
publishLocation: 'pipeline'
|
||||
|
||||
#- task: GitHubRelease@1
|
||||
# inputs:
|
||||
# gitHubConnection: 'github.com_Masterain'
|
||||
# repositoryName: 'DGP-Automation/Hutao-Auto-Release'
|
||||
# action: 'create'
|
||||
# target: '$(Build.SourceVersion)'
|
||||
# tagSource: 'userSpecifiedTag'
|
||||
# tag: '$(version)'
|
||||
# title: '$(version)'
|
||||
# releaseNotesSource: 'inline'
|
||||
# releaseNotesInline: |
|
||||
# ## 普通用户请勿下载
|
||||
# 该版本是由 CI 程序自动打包生成的 `Alpha` 测试版本,**仅供开发者测试使用**
|
||||
#
|
||||
# 普通用户请[点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/)下载最新的稳定版本
|
||||
#
|
||||
# assets: |
|
||||
# $(Build.ArtifactStagingDirectory)/*
|
||||
# $(cerFile.secureFilePath)
|
||||
# isPreRelease: true
|
||||
# changeLogCompareToRelease: 'lastFullRelease'
|
||||
# changeLogType: 'commitBased'
|
||||
|
||||
|
||||
- task: rclone@1
|
||||
displayName: Upload CI via Rclone
|
||||
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
|
||||
inputs:
|
||||
arguments: 'copy $(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(version).msix downloadDGPCN:/releases/Alpha/'
|
||||
configPath: 'C:\agent\_work\_tasks\rclone.conf'
|
||||
|
||||
- task: rclone@1
|
||||
displayName: Upload PR CI via Rclone
|
||||
condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))
|
||||
inputs:
|
||||
arguments: 'copy $(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(version).msix downloadDGPCN:/releases/PR/'
|
||||
configPath: 'C:\agent\_work\_tasks\rclone.conf'
|
||||
26
build.cake
26
build.cake
@@ -28,7 +28,27 @@ string manifest
|
||||
get => System.IO.Path.Combine(repoDir, "src", "Snap.Hutao", "Snap.Hutao", "Package.appxmanifest");
|
||||
}
|
||||
|
||||
if (GitHubActions.IsRunningOnGitHubActions)
|
||||
if (AzurePipelines.IsRunningOnAzurePipelines)
|
||||
{
|
||||
repoDir = AzurePipelines.Environment.Build.SourcesDirectory.FullPath;
|
||||
outputPath = AzurePipelines.Environment.Build.ArtifactStagingDirectory.FullPath;
|
||||
|
||||
var versionAuth = HasEnvironmentVariable("VERSION_API_TOKEN") ? EnvironmentVariable("VERSION_API_TOKEN") : throw new Exception("Cannot find VERSION_API_TOKEN");
|
||||
version = HttpGet(
|
||||
"https://internal.snapgenshin.cn/BuildIntergration/RequestNewVersion",
|
||||
new HttpSettings
|
||||
{
|
||||
Headers = new Dictionary<string, string>
|
||||
{
|
||||
{ "Authorization", versionAuth }
|
||||
}
|
||||
}
|
||||
);
|
||||
Information($"Version: {version}");
|
||||
|
||||
AzurePipelines.Commands.SetVariable("version", version);
|
||||
}
|
||||
else if (GitHubActions.IsRunningOnGitHubActions)
|
||||
{
|
||||
repoDir = GitHubActions.Environment.Workflow.Workspace.FullPath;
|
||||
outputPath = System.IO.Path.Combine(repoDir, "src", "output");
|
||||
@@ -86,7 +106,7 @@ Task("Generate AppxManifest")
|
||||
|
||||
var content = System.IO.File.ReadAllText(manifest);
|
||||
|
||||
if (GitHubActions.IsRunningOnGitHubActions)
|
||||
if (AzurePipelines.IsRunningOnAzurePipelines || GitHubActions.IsRunningOnGitHubActions)
|
||||
{
|
||||
Information("Using CI configuraion");
|
||||
content = content
|
||||
@@ -155,7 +175,7 @@ Task("Build MSIX")
|
||||
.Does(() =>
|
||||
{
|
||||
var arguments = "arguments";
|
||||
if (GitHubActions.IsRunningOnGitHubActions)
|
||||
if (AzurePipelines.IsRunningOnAzurePipelines || GitHubActions.IsRunningOnGitHubActions)
|
||||
{
|
||||
arguments = "pack /d " + binPath + " /p " + System.IO.Path.Combine(outputPath, $"Snap.Hutao.Alpha-{version}.msix");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
@@ -13,7 +13,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.2.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.2.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/PivotOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/ScrollViewer.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"/>
|
||||
|
||||
@@ -1,768 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
using CommunityToolkit.WinUI.Helpers;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using NotifyCollectionChangedAction = System.Collections.Specialized.NotifyCollectionChangedAction;
|
||||
|
||||
namespace Snap.Hutao.Control.Collection.AdvancedCollectionView;
|
||||
|
||||
internal sealed class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPropertyChanged, ISupportIncrementalLoading, IComparer<object>
|
||||
where T : class
|
||||
{
|
||||
private readonly List<T> view;
|
||||
private readonly ObservableCollection<SortDescription> sortDescriptions;
|
||||
private readonly Dictionary<string, PropertyInfo?> sortProperties;
|
||||
private readonly bool liveShapingEnabled;
|
||||
private readonly HashSet<string?> observedFilterProperties = [];
|
||||
|
||||
private IList<T> source;
|
||||
private Predicate<T>? filter;
|
||||
private int deferCounter;
|
||||
private WeakEventListener<AdvancedCollectionView<T>, object?, NotifyCollectionChangedEventArgs>? sourceWeakEventListener;
|
||||
|
||||
public AdvancedCollectionView()
|
||||
: this(new List<T>(0))
|
||||
{
|
||||
}
|
||||
|
||||
public AdvancedCollectionView(IList<T> source, bool isLiveShaping = false)
|
||||
{
|
||||
liveShapingEnabled = isLiveShaping;
|
||||
view = [];
|
||||
sortDescriptions = [];
|
||||
sortDescriptions.CollectionChanged += SortDescriptionsCollectionChanged;
|
||||
sortProperties = [];
|
||||
Source = source;
|
||||
}
|
||||
|
||||
public event EventHandler<object>? CurrentChanged;
|
||||
|
||||
public event CurrentChangingEventHandler? CurrentChanging;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public event VectorChangedEventHandler<object>? VectorChanged;
|
||||
|
||||
public IList<T> Source
|
||||
{
|
||||
get => source;
|
||||
|
||||
[MemberNotNull(nameof(source))]
|
||||
set
|
||||
{
|
||||
if (ReferenceEquals(source, value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (source is not null)
|
||||
{
|
||||
DetachPropertyChangedHandler(source);
|
||||
}
|
||||
|
||||
source = value;
|
||||
AttachPropertyChangedHandler(source);
|
||||
|
||||
sourceWeakEventListener?.Detach();
|
||||
|
||||
if (source is INotifyCollectionChanged sourceNotifyCollectionChanged)
|
||||
{
|
||||
sourceWeakEventListener = new WeakEventListener<AdvancedCollectionView<T>, object?, NotifyCollectionChangedEventArgs>(this)
|
||||
{
|
||||
// Call the actual collection changed event
|
||||
OnEventAction = (source, changed, arg3) => SourceNotifyCollectionChangedCollectionChanged(source, arg3),
|
||||
|
||||
// The source doesn't exist anymore
|
||||
OnDetachAction = (listener) =>
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(sourceWeakEventListener);
|
||||
sourceNotifyCollectionChanged.CollectionChanged -= sourceWeakEventListener.OnEvent;
|
||||
},
|
||||
};
|
||||
sourceNotifyCollectionChanged.CollectionChanged += sourceWeakEventListener.OnEvent;
|
||||
}
|
||||
|
||||
HandleSourceChanged();
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get => view.Count;
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get => source is null || source.IsReadOnly;
|
||||
}
|
||||
|
||||
public IObservableVector<object> CollectionGroups
|
||||
{
|
||||
get => default!;
|
||||
}
|
||||
|
||||
public T? CurrentItem
|
||||
{
|
||||
get => CurrentPosition > -1 && CurrentPosition < view.Count ? view[CurrentPosition] : default;
|
||||
set => MoveCurrentTo(value);
|
||||
}
|
||||
|
||||
public int CurrentPosition { get; private set; }
|
||||
|
||||
public bool HasMoreItems
|
||||
{
|
||||
get => source is ISupportIncrementalLoading { HasMoreItems: true };
|
||||
}
|
||||
|
||||
public bool IsCurrentAfterLast
|
||||
{
|
||||
get => CurrentPosition >= view.Count;
|
||||
}
|
||||
|
||||
public bool IsCurrentBeforeFirst
|
||||
{
|
||||
get => CurrentPosition < 0;
|
||||
}
|
||||
|
||||
public bool CanFilter
|
||||
{
|
||||
get => true;
|
||||
}
|
||||
|
||||
public Predicate<T>? Filter
|
||||
{
|
||||
get => filter;
|
||||
set
|
||||
{
|
||||
if (filter == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
filter = value;
|
||||
HandleFilterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanSort
|
||||
{
|
||||
get => true;
|
||||
}
|
||||
|
||||
public IList<SortDescription> SortDescriptions
|
||||
{
|
||||
get => sortDescriptions;
|
||||
}
|
||||
|
||||
public IEnumerable<T> SourceCollection
|
||||
{
|
||||
get => source;
|
||||
}
|
||||
|
||||
public IReadOnlyList<T> View
|
||||
{
|
||||
get => view;
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => view[index];
|
||||
set => view[index] = value;
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
HandleSourceChanged();
|
||||
}
|
||||
|
||||
public void RefreshFilter()
|
||||
{
|
||||
HandleFilterChanged();
|
||||
}
|
||||
|
||||
public void RefreshSorting()
|
||||
{
|
||||
HandleSortChanged();
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return view.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return view.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Add(item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return view.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
view.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Remove(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH007")]
|
||||
public int IndexOf(T? item)
|
||||
{
|
||||
return view.IndexOf(item!);
|
||||
}
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Insert(index, item);
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
Remove(view[index]);
|
||||
}
|
||||
|
||||
public bool MoveCurrentTo(T? item)
|
||||
{
|
||||
return (item is not null && item.Equals(CurrentItem)) || MoveCurrentToIndex(IndexOf(item));
|
||||
}
|
||||
|
||||
public bool MoveCurrentToPosition(int index)
|
||||
{
|
||||
return MoveCurrentToIndex(index);
|
||||
}
|
||||
|
||||
public bool MoveCurrentToFirst()
|
||||
{
|
||||
return MoveCurrentToIndex(0);
|
||||
}
|
||||
|
||||
public bool MoveCurrentToLast()
|
||||
{
|
||||
return MoveCurrentToIndex(view.Count - 1);
|
||||
}
|
||||
|
||||
public bool MoveCurrentToNext()
|
||||
{
|
||||
return MoveCurrentToIndex(CurrentPosition + 1);
|
||||
}
|
||||
|
||||
public bool MoveCurrentToPrevious()
|
||||
{
|
||||
return MoveCurrentToIndex(CurrentPosition - 1);
|
||||
}
|
||||
|
||||
public IAsyncOperation<LoadMoreItemsResult>? LoadMoreItemsAsync(uint count)
|
||||
{
|
||||
return (source as ISupportIncrementalLoading)?.LoadMoreItemsAsync(count);
|
||||
}
|
||||
|
||||
public void ObserveFilterProperty(string propertyName)
|
||||
{
|
||||
observedFilterProperties.Add(propertyName);
|
||||
}
|
||||
|
||||
public void ClearObservedFilterProperties()
|
||||
{
|
||||
observedFilterProperties.Clear();
|
||||
}
|
||||
|
||||
public IDisposable DeferRefresh()
|
||||
{
|
||||
return new NotificationDeferrer(this);
|
||||
}
|
||||
|
||||
int IComparer<object>.Compare(object? x, object? y)
|
||||
{
|
||||
if (sortProperties.Count <= 0)
|
||||
{
|
||||
Type listType = source.GetType();
|
||||
Type? type;
|
||||
|
||||
if (listType.IsGenericType)
|
||||
{
|
||||
type = listType.GetGenericArguments()[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
type = x?.GetType();
|
||||
}
|
||||
|
||||
foreach (SortDescription sd in sortDescriptions)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sd.PropertyName))
|
||||
{
|
||||
sortProperties[sd.PropertyName] = type?.GetProperty(sd.PropertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (SortDescription sd in sortDescriptions)
|
||||
{
|
||||
object? cx, cy;
|
||||
|
||||
if (string.IsNullOrEmpty(sd.PropertyName))
|
||||
{
|
||||
cx = x;
|
||||
cy = y;
|
||||
}
|
||||
else
|
||||
{
|
||||
PropertyInfo? pi = sortProperties[sd.PropertyName];
|
||||
|
||||
cx = pi?.GetValue(x);
|
||||
cy = pi?.GetValue(y);
|
||||
}
|
||||
|
||||
int cmp = sd.Comparer.Compare(cx, cy);
|
||||
|
||||
if (cmp is not 0)
|
||||
{
|
||||
return sd.Direction is SortDirection.Ascending ? +cmp : -cmp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal void OnPropertyChanged([CallerMemberName] string propertyName = default!)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
private void ItemOnPropertyChanged(object? item, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (!liveShapingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
T typedItem = (T)item;
|
||||
|
||||
bool? filterResult = filter?.Invoke(typedItem);
|
||||
|
||||
if (filterResult.HasValue && observedFilterProperties.Contains(e.PropertyName))
|
||||
{
|
||||
int viewIndex = view.IndexOf(typedItem);
|
||||
if (viewIndex != -1 && !filterResult.Value)
|
||||
{
|
||||
RemoveFromView(viewIndex, typedItem);
|
||||
}
|
||||
else if (viewIndex == -1 && filterResult.Value)
|
||||
{
|
||||
int index = source.IndexOf(typedItem);
|
||||
HandleItemAdded(index, typedItem);
|
||||
}
|
||||
}
|
||||
|
||||
if ((filterResult ?? true) && SortDescriptions.Any(sd => sd.PropertyName == e.PropertyName))
|
||||
{
|
||||
int oldIndex = view.IndexOf(typedItem);
|
||||
|
||||
// Check if item is in view:
|
||||
if (oldIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
view.RemoveAt(oldIndex);
|
||||
int targetIndex = view.BinarySearch(typedItem, this);
|
||||
if (targetIndex < 0)
|
||||
{
|
||||
targetIndex = ~targetIndex;
|
||||
}
|
||||
|
||||
// Only trigger expensive UI updates if the index really changed:
|
||||
if (targetIndex != oldIndex)
|
||||
{
|
||||
OnVectorChanged(new VectorChangedEventArgs(CollectionChange.ItemRemoved, oldIndex, typedItem));
|
||||
|
||||
view.Insert(targetIndex, typedItem);
|
||||
|
||||
OnVectorChanged(new VectorChangedEventArgs(CollectionChange.ItemInserted, targetIndex, typedItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
view.Insert(targetIndex, typedItem);
|
||||
}
|
||||
}
|
||||
else if (string.IsNullOrEmpty(e.PropertyName))
|
||||
{
|
||||
HandleSourceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void AttachPropertyChangedHandler(IEnumerable items)
|
||||
{
|
||||
if (!liveShapingEnabled || items is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (object item in items)
|
||||
{
|
||||
if (item is INotifyPropertyChanged notifyPropertyChanged)
|
||||
{
|
||||
notifyPropertyChanged.PropertyChanged += ItemOnPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DetachPropertyChangedHandler(IEnumerable items)
|
||||
{
|
||||
if (!liveShapingEnabled || items is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (object item in items)
|
||||
{
|
||||
if (item is INotifyPropertyChanged notifyPropertyChanged)
|
||||
{
|
||||
notifyPropertyChanged.PropertyChanged -= ItemOnPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSortChanged()
|
||||
{
|
||||
sortProperties.Clear();
|
||||
view.Sort(this);
|
||||
sortProperties.Clear();
|
||||
OnVectorChanged(new VectorChangedEventArgs(CollectionChange.Reset));
|
||||
}
|
||||
|
||||
private void HandleFilterChanged()
|
||||
{
|
||||
if (filter is not null)
|
||||
{
|
||||
for (int index = 0; index < view.Count; index++)
|
||||
{
|
||||
T item = view[index];
|
||||
if (filter(item))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RemoveFromView(index, item);
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<T> viewHash = new(view);
|
||||
int viewIndex = 0;
|
||||
for (int index = 0; index < source.Count; index++)
|
||||
{
|
||||
T item = source[index];
|
||||
if (viewHash.Contains(item))
|
||||
{
|
||||
viewIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (HandleItemAdded(index, item, viewIndex))
|
||||
{
|
||||
viewIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSourceChanged()
|
||||
{
|
||||
sortProperties.Clear();
|
||||
T? currentItem = CurrentItem;
|
||||
view.Clear();
|
||||
foreach (T item in Source)
|
||||
{
|
||||
if (filter is not null && !filter(item))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sortDescriptions.Count > 0)
|
||||
{
|
||||
int targetIndex = view.BinarySearch(item, this);
|
||||
if (targetIndex < 0)
|
||||
{
|
||||
targetIndex = ~targetIndex;
|
||||
}
|
||||
|
||||
view.Insert(targetIndex, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
view.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
sortProperties.Clear();
|
||||
OnVectorChanged(new VectorChangedEventArgs(CollectionChange.Reset));
|
||||
MoveCurrentTo(currentItem);
|
||||
}
|
||||
|
||||
private void SourceNotifyCollectionChangedCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
ArgumentNullException.ThrowIfNull(e.NewItems);
|
||||
AttachPropertyChangedHandler(e.NewItems);
|
||||
if (deferCounter <= 0)
|
||||
{
|
||||
if (e.NewItems?.Count == 1)
|
||||
{
|
||||
object? newItem = e.NewItems[0];
|
||||
ArgumentNullException.ThrowIfNull(newItem);
|
||||
HandleItemAdded(e.NewStartingIndex, (T)newItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleSourceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
ArgumentNullException.ThrowIfNull(e.OldItems);
|
||||
DetachPropertyChangedHandler(e.OldItems);
|
||||
if (deferCounter <= 0)
|
||||
{
|
||||
if (e.OldItems?.Count == 1)
|
||||
{
|
||||
object? oldItem = e.OldItems[0];
|
||||
ArgumentNullException.ThrowIfNull(oldItem);
|
||||
HandleItemRemoved(e.OldStartingIndex, (T)oldItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleSourceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
if (deferCounter <= 0)
|
||||
{
|
||||
HandleSourceChanged();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HandleItemAdded(int newStartingIndex, T newItem, int? viewIndex = null)
|
||||
{
|
||||
if (filter is not null && !filter(newItem))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int newViewIndex = view.Count;
|
||||
|
||||
if (sortDescriptions.Count > 0)
|
||||
{
|
||||
sortProperties.Clear();
|
||||
newViewIndex = view.BinarySearch(newItem, this);
|
||||
if (newViewIndex < 0)
|
||||
{
|
||||
newViewIndex = ~newViewIndex;
|
||||
}
|
||||
}
|
||||
else if (filter is not null)
|
||||
{
|
||||
if (source is null)
|
||||
{
|
||||
HandleSourceChanged();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newStartingIndex == 0 || view.Count == 0)
|
||||
{
|
||||
newViewIndex = 0;
|
||||
}
|
||||
else if (newStartingIndex == source.Count - 1)
|
||||
{
|
||||
newViewIndex = view.Count;
|
||||
}
|
||||
else if (viewIndex.HasValue)
|
||||
{
|
||||
newViewIndex = viewIndex.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0, j = 0; i < source.Count; i++)
|
||||
{
|
||||
if (i == newStartingIndex)
|
||||
{
|
||||
newViewIndex = j;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Equals(view[j], source[i]))
|
||||
{
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
view.Insert(newViewIndex, newItem);
|
||||
if (newViewIndex <= CurrentPosition)
|
||||
{
|
||||
CurrentPosition++;
|
||||
}
|
||||
|
||||
OnVectorChanged(new VectorChangedEventArgs(CollectionChange.ItemInserted, newViewIndex, newItem));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HandleItemRemoved(int oldStartingIndex, T oldItem)
|
||||
{
|
||||
if (filter is not null && !filter(oldItem))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldStartingIndex < 0 || oldStartingIndex >= view.Count || !Equals(view[oldStartingIndex], oldItem))
|
||||
{
|
||||
oldStartingIndex = view.IndexOf(oldItem);
|
||||
}
|
||||
|
||||
if (oldStartingIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveFromView(oldStartingIndex, oldItem);
|
||||
}
|
||||
|
||||
private void RemoveFromView(int itemIndex, T item)
|
||||
{
|
||||
view.RemoveAt(itemIndex);
|
||||
if (itemIndex <= CurrentPosition)
|
||||
{
|
||||
CurrentPosition--;
|
||||
}
|
||||
|
||||
OnVectorChanged(new VectorChangedEventArgs(CollectionChange.ItemRemoved, itemIndex, item));
|
||||
}
|
||||
|
||||
private void SortDescriptionsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (deferCounter > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HandleSortChanged();
|
||||
}
|
||||
|
||||
private bool MoveCurrentToIndex(int i)
|
||||
{
|
||||
if (i < -1 || i >= view.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i == CurrentPosition)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentChangingEventArgs e = new();
|
||||
OnCurrentChanging(e);
|
||||
if (e.Cancel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentPosition = i;
|
||||
OnCurrentChanged(default!);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnCurrentChanging(CurrentChangingEventArgs e)
|
||||
{
|
||||
if (deferCounter > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentChanging?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void OnCurrentChanged(object e)
|
||||
{
|
||||
if (deferCounter > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentChanged?.Invoke(this, e);
|
||||
OnPropertyChanged(nameof(CurrentItem));
|
||||
}
|
||||
|
||||
private void OnVectorChanged(IVectorChangedEventArgs e)
|
||||
{
|
||||
if (deferCounter > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VectorChanged?.Invoke(this, e);
|
||||
OnPropertyChanged(nameof(Count));
|
||||
}
|
||||
|
||||
internal sealed class NotificationDeferrer : IDisposable
|
||||
{
|
||||
private readonly AdvancedCollectionView<T> advancedCollectionView;
|
||||
private readonly T? currentItem;
|
||||
|
||||
public NotificationDeferrer(AdvancedCollectionView<T> acvs)
|
||||
{
|
||||
advancedCollectionView = acvs;
|
||||
currentItem = advancedCollectionView.CurrentItem;
|
||||
advancedCollectionView.deferCounter++;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
advancedCollectionView.MoveCurrentTo(currentItem);
|
||||
advancedCollectionView.deferCounter--;
|
||||
advancedCollectionView.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System.Collections;
|
||||
|
||||
namespace Snap.Hutao.Control.Collection.AdvancedCollectionView;
|
||||
|
||||
internal interface IAdvancedCollectionView<T> : ICollectionView, IEnumerable
|
||||
where T : class
|
||||
{
|
||||
bool CanFilter { get; }
|
||||
|
||||
bool CanSort { get; }
|
||||
|
||||
object? ICollectionView.CurrentItem
|
||||
{
|
||||
get => CurrentItem;
|
||||
}
|
||||
|
||||
new T? CurrentItem { get; }
|
||||
|
||||
Predicate<T>? Filter { get; set; }
|
||||
|
||||
IList<SortDescription> SortDescriptions { get; }
|
||||
|
||||
IEnumerable<T> SourceCollection { get; }
|
||||
|
||||
object IList<object>.this[int index]
|
||||
{
|
||||
get => this[index];
|
||||
set => this[index] = (T)value;
|
||||
}
|
||||
|
||||
new T this[int index] { get; set; }
|
||||
|
||||
void ICollection<object>.Add(object item)
|
||||
{
|
||||
Add((T)item);
|
||||
}
|
||||
|
||||
void Add(T item);
|
||||
|
||||
void ClearObservedFilterProperties();
|
||||
|
||||
bool ICollection<object>.Contains(object item)
|
||||
{
|
||||
return Contains((T)item);
|
||||
}
|
||||
|
||||
bool Contains(T item);
|
||||
|
||||
void ICollection<object>.CopyTo(object[] array, int arrayIndex)
|
||||
{
|
||||
CopyTo((T[])array, arrayIndex);
|
||||
}
|
||||
|
||||
void CopyTo(T[] array, int arrayIndex);
|
||||
|
||||
IDisposable DeferRefresh();
|
||||
|
||||
IEnumerator<object> IEnumerable<object>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
new IEnumerator<T> GetEnumerator();
|
||||
|
||||
int IList<object>.IndexOf(object item)
|
||||
{
|
||||
return IndexOf((T)item);
|
||||
}
|
||||
|
||||
int IndexOf(T item);
|
||||
|
||||
void IList<object>.Insert(int index, object item)
|
||||
{
|
||||
Insert(index, (T)item);
|
||||
}
|
||||
|
||||
void Insert(int index, T item);
|
||||
|
||||
bool ICollectionView.MoveCurrentTo(object item)
|
||||
{
|
||||
return MoveCurrentTo((T)item);
|
||||
}
|
||||
|
||||
bool MoveCurrentTo(T item);
|
||||
|
||||
void ObserveFilterProperty(string propertyName);
|
||||
|
||||
void Refresh();
|
||||
|
||||
void RefreshFilter();
|
||||
|
||||
void RefreshSorting();
|
||||
|
||||
bool ICollection<object>.Remove(object item)
|
||||
{
|
||||
return Remove((T)item);
|
||||
}
|
||||
|
||||
bool Remove(T item);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace Snap.Hutao.Control.Collection.AdvancedCollectionView;
|
||||
|
||||
internal sealed class VectorChangedEventArgs : IVectorChangedEventArgs
|
||||
{
|
||||
public VectorChangedEventArgs(CollectionChange cc, int index = -1, object item = null!)
|
||||
{
|
||||
CollectionChange = cc;
|
||||
Index = (uint)index;
|
||||
}
|
||||
|
||||
public CollectionChange CollectionChange { get; }
|
||||
|
||||
public uint Index { get; }
|
||||
}
|
||||
@@ -33,16 +33,6 @@ internal struct Bgra32
|
||||
/// </summary>
|
||||
public byte A;
|
||||
|
||||
public Bgra32(byte b, byte g, byte r, byte a)
|
||||
{
|
||||
B = b;
|
||||
G = g;
|
||||
R = r;
|
||||
A = a;
|
||||
}
|
||||
|
||||
public readonly double Luminance { get => ((0.299 * R) + (0.587 * G) + (0.114 * B)) / 255; }
|
||||
|
||||
/// <summary>
|
||||
/// 从 Color 转换
|
||||
/// </summary>
|
||||
@@ -54,11 +44,4 @@ internal struct Bgra32
|
||||
*(uint*)&bgra8 = BinaryPrimitives.ReverseEndianness(*(uint*)&color);
|
||||
return bgra8;
|
||||
}
|
||||
|
||||
public static unsafe implicit operator Color(Bgra32 bgra8)
|
||||
{
|
||||
Unsafe.SkipInit(out Color color);
|
||||
*(uint*)&color = BinaryPrimitives.ReverseEndianness(*(uint*)&bgra8);
|
||||
return color;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Snap.Hutao.Control.Media;
|
||||
/// <summary>
|
||||
/// Defines a color in Hue/Saturation/Lightness (HSL) space.
|
||||
/// </summary>
|
||||
internal struct Hsla32
|
||||
internal struct Hsl32
|
||||
{
|
||||
/// <summary>
|
||||
/// The Hue in 0..360 range.
|
||||
@@ -46,14 +46,14 @@ internal struct Rgba32
|
||||
/// <summary>
|
||||
/// 使用 RGBA 代码初始化新的结构
|
||||
/// </summary>
|
||||
/// <param name="xrgbaCode">RGBA 代码</param>
|
||||
public unsafe Rgba32(uint xrgbaCode)
|
||||
/// <param name="code">RGBA 代码</param>
|
||||
public unsafe Rgba32(uint code)
|
||||
{
|
||||
// uint layout: 0xRRGGBBAA is AABBGGRR
|
||||
// uint layout: 0xRRGGBBAA -> AABBGGRR
|
||||
// AABBGGRR -> RRGGBBAA
|
||||
fixed (Rgba32* pSelf = &this)
|
||||
{
|
||||
*(uint*)pSelf = BinaryPrimitives.ReverseEndianness(xrgbaCode);
|
||||
*(uint*)pSelf = BinaryPrimitives.ReverseEndianness(code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,14 +67,14 @@ internal struct Rgba32
|
||||
|
||||
public static unsafe implicit operator Color(Rgba32 hexColor)
|
||||
{
|
||||
// Goal : Rgba32:RRGGBBAA(0xAABBGGRR) -> Color: AARRGGBB(0xBBGGRRAA)
|
||||
// Step1: Rgba32:RRGGBBAA(0xAABBGGRR) -> UInt32:AA000000(0x000000AA)
|
||||
uint a = ((*(uint*)&hexColor) >> 24) & 0x000000FF;
|
||||
// AABBGGRR -> BBGGRRAA
|
||||
// AABBGGRR -> 000000AA
|
||||
uint a = (*(uint*)&hexColor) >> 24;
|
||||
|
||||
// Step2: Rgba32:RRGGBBAA(0xAABBGGRR) -> UInt32:00RRGGBB(0xRRGGBB00)
|
||||
uint rgb = ((*(uint*)&hexColor) << 8) & 0xFFFFFF00;
|
||||
// AABBGGRR -> BBGGRR00
|
||||
uint rgb = (*(uint*)&hexColor) << 8;
|
||||
|
||||
// Step2: UInt32:00RRGGBB(0xRRGGBB00) + UInt32:AA000000(0x000000AA) -> UInt32:AARRGGBB(0xRRGGBBAA)
|
||||
// BBGGRR00 + 000000AA
|
||||
uint rgba = rgb + a;
|
||||
|
||||
return *(Color*)&rgba;
|
||||
@@ -85,7 +85,7 @@ internal struct Rgba32
|
||||
/// </summary>
|
||||
/// <param name="hsl">HSL 颜色</param>
|
||||
/// <returns>RGBA8颜色</returns>
|
||||
public static Rgba32 FromHsl(Hsla32 hsl)
|
||||
public static Rgba32 FromHsl(Hsl32 hsl)
|
||||
{
|
||||
double chroma = (1 - Math.Abs((2 * hsl.L) - 1)) * hsl.S;
|
||||
double h1 = hsl.H / 60;
|
||||
@@ -142,7 +142,7 @@ internal struct Rgba32
|
||||
/// 转换到 HSL 颜色
|
||||
/// </summary>
|
||||
/// <returns>HSL 颜色</returns>
|
||||
public readonly Hsla32 ToHsl()
|
||||
public readonly Hsl32 ToHsl()
|
||||
{
|
||||
const double toDouble = 1.0 / 255;
|
||||
double r = toDouble * R;
|
||||
@@ -175,7 +175,7 @@ internal struct Rgba32
|
||||
double lightness = 0.5 * (max + min);
|
||||
double saturation = chroma == 0 ? 0 : chroma / (1 - Math.Abs((2 * lightness) - 1));
|
||||
|
||||
Hsla32 ret;
|
||||
Hsl32 ret;
|
||||
ret.H = 60 * h1;
|
||||
ret.S = saturation;
|
||||
ret.L = lightness;
|
||||
|
||||
@@ -25,7 +25,8 @@ internal static class SoftwareBitmapExtension
|
||||
{
|
||||
using (IMemoryBufferReference reference = buffer.CreateReference())
|
||||
{
|
||||
reference.As<IMemoryBufferByteAccess>().GetBuffer(out Span<Bgra32> bytes);
|
||||
reference.As<IMemoryBufferByteAccess>().GetBuffer(out byte* data, out uint length);
|
||||
Span<Bgra32> bytes = new(data, unchecked((int)length / sizeof(Bgra32)));
|
||||
foreach (ref Bgra32 pixel in bytes)
|
||||
{
|
||||
byte baseAlpha = pixel.A;
|
||||
@@ -38,43 +39,4 @@ 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))
|
||||
{
|
||||
using (IMemoryBufferReference reference = buffer.CreateReference())
|
||||
{
|
||||
reference.As<IMemoryBufferByteAccess>().GetBuffer(out Span<Bgra32> bytes);
|
||||
double b = 0, g = 0, r = 0, a = 0;
|
||||
foreach (ref readonly Bgra32 pixel in bytes)
|
||||
{
|
||||
b += pixel.B;
|
||||
g += pixel.G;
|
||||
r += pixel.R;
|
||||
a += pixel.A;
|
||||
}
|
||||
|
||||
return new((byte)(b / bytes.Length), (byte)(g / bytes.Length), (byte)(r / bytes.Length), (byte)(a / bytes.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ using Microsoft.UI.Xaml.Documents;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
using Snap.Hutao.Control.Media;
|
||||
using Snap.Hutao.Control.Text.Syntax.MiHoYo;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
using Snap.Hutao.Metadata;
|
||||
using Windows.Foundation;
|
||||
@@ -17,12 +16,23 @@ namespace Snap.Hutao.Control.Text;
|
||||
|
||||
/// <summary>
|
||||
/// 专用于呈现描述文本的文本块
|
||||
/// Some part of this file came from:
|
||||
/// https://github.com/xunkong/desktop/tree/main/src/Desktop/Desktop/Pages/CharacterInfoPage.xaml.cs
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[DependencyProperty("Description", typeof(string), "", nameof(OnDescriptionChanged))]
|
||||
[DependencyProperty("TextStyle", typeof(Style), default(Style), nameof(OnTextStyleChanged))]
|
||||
internal sealed partial class DescriptionTextBlock : ContentControl
|
||||
{
|
||||
private static readonly int RgbaColorTagFullLength = "<color=#FFFFFFFF></color>".Length;
|
||||
private static readonly int RgbaColorTagLeftLength = "<color=#FFFFFFFF>".Length;
|
||||
|
||||
private static readonly int RgbColorTagFullLength = "<color=#FFFFFF></color>".Length;
|
||||
private static readonly int RgbColorTagLeftLength = "<color=#FFFFFF>".Length;
|
||||
|
||||
private static readonly int ItalicTagFullLength = "<i></i>".Length;
|
||||
private static readonly int ItalicTagLeftLength = "<i>".Length;
|
||||
|
||||
private readonly TypedEventHandler<FrameworkElement, object> actualThemeChangedEventHandler;
|
||||
|
||||
/// <summary>
|
||||
@@ -45,10 +55,11 @@ internal sealed partial class DescriptionTextBlock : ContentControl
|
||||
private static void OnDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
TextBlock textBlock = (TextBlock)((DescriptionTextBlock)d).Content;
|
||||
ReadOnlySpan<char> description = SpecialNameHandler.Handle((string)e.NewValue);
|
||||
|
||||
try
|
||||
{
|
||||
UpdateDescription(textBlock, MiHoYoSyntaxTree.Parse(SpecialNameHandler.Handle((string)e.NewValue)));
|
||||
UpdateDescription(textBlock, description);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -62,106 +73,120 @@ internal sealed partial class DescriptionTextBlock : ContentControl
|
||||
textBlock.Style = (Style)e.NewValue;
|
||||
}
|
||||
|
||||
private static void UpdateDescription(TextBlock textBlock, MiHoYoSyntaxTree syntaxTree)
|
||||
private static void UpdateDescription(TextBlock textBlock, in ReadOnlySpan<char> description)
|
||||
{
|
||||
textBlock.Inlines.Clear();
|
||||
AppendNode(textBlock, textBlock.Inlines, syntaxTree.Root);
|
||||
}
|
||||
|
||||
private static void AppendNode(TextBlock textBlock, InlineCollection inlines, MiHoYoSyntaxNode node)
|
||||
{
|
||||
switch (node.Kind)
|
||||
int last = 0;
|
||||
for (int i = 0; i < description.Length;)
|
||||
{
|
||||
case MiHoYoSyntaxKind.Root:
|
||||
foreach (MiHoYoSyntaxNode child in ((MiHoYoRootSyntax)node).Children)
|
||||
{
|
||||
AppendNode(textBlock, inlines, child);
|
||||
}
|
||||
// newline
|
||||
if (description[i..].StartsWith(@"\n"))
|
||||
{
|
||||
AppendText(textBlock, description[last..i]);
|
||||
AppendLineBreak(textBlock);
|
||||
i += 2;
|
||||
last = i;
|
||||
}
|
||||
|
||||
break;
|
||||
case MiHoYoSyntaxKind.PlainText:
|
||||
AppendPlainText(textBlock, inlines, (MiHoYoPlainTextSyntax)node);
|
||||
break;
|
||||
case MiHoYoSyntaxKind.ColorText:
|
||||
AppendColorText(textBlock, inlines, (MiHoYoColorTextSyntax)node);
|
||||
break;
|
||||
case MiHoYoSyntaxKind.ItalicText:
|
||||
AppendItalicText(textBlock, inlines, (MiHoYoItalicTextSyntax)node);
|
||||
break;
|
||||
// color tag
|
||||
else if (description[i..].StartsWith("<c"))
|
||||
{
|
||||
switch (description[i..].IndexOf('>'))
|
||||
{
|
||||
case 16: // RgbaColorTag
|
||||
{
|
||||
AppendText(textBlock, description[last..i]);
|
||||
Rgba32 color = new(description.Slice(i + 8, 8).ToString());
|
||||
int length = description[(i + RgbaColorTagLeftLength)..].IndexOf('<');
|
||||
AppendColorText(textBlock, description.Slice(i + RgbaColorTagLeftLength, length), color);
|
||||
|
||||
i += length + RgbaColorTagFullLength;
|
||||
last = i;
|
||||
break;
|
||||
}
|
||||
|
||||
case 14: // RgbColorTag
|
||||
{
|
||||
AppendText(textBlock, description[last..i]);
|
||||
Rgba32 color = new(description.Slice(i + 8, 6).ToString());
|
||||
int length = description[(i + RgbColorTagLeftLength)..].IndexOf('<');
|
||||
AppendColorText(textBlock, description.Slice(i + RgbColorTagLeftLength, length), color);
|
||||
|
||||
i += length + RgbColorTagFullLength;
|
||||
last = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// italic
|
||||
else if (description[i..].StartsWith("<i"))
|
||||
{
|
||||
AppendText(textBlock, description[last..i]);
|
||||
|
||||
int length = description[(i + ItalicTagLeftLength)..].IndexOf('<');
|
||||
AppendItalicText(textBlock, description.Slice(i + ItalicTagLeftLength, length));
|
||||
|
||||
i += length + ItalicTagFullLength;
|
||||
last = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i == description.Length - 1)
|
||||
{
|
||||
AppendText(textBlock, description[last..(i + 1)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendPlainText(TextBlock textBlock, InlineCollection inlines, MiHoYoPlainTextSyntax plainText)
|
||||
private static void AppendText(TextBlock text, in ReadOnlySpan<char> slice)
|
||||
{
|
||||
// PlainText doesn't have children
|
||||
inlines.Add(new Run { Text = plainText.Span.ToString() });
|
||||
text.Inlines.Add(new Run { Text = slice.ToString() });
|
||||
}
|
||||
|
||||
private static void AppendColorText(TextBlock textBlock, InlineCollection inlines, MiHoYoColorTextSyntax colorText)
|
||||
private static void AppendColorText(TextBlock text, in ReadOnlySpan<char> slice, Rgba32 color)
|
||||
{
|
||||
Rgba32 color = new(colorText.ColorSpan.ToString());
|
||||
Color targetColor;
|
||||
if (ThemeHelper.IsDarkMode(textBlock.ActualTheme))
|
||||
if (ThemeHelper.IsDarkMode(text.ActualTheme))
|
||||
{
|
||||
targetColor = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make lighter in light mode
|
||||
Hsla32 hsl = color.ToHsl();
|
||||
Hsl32 hsl = color.ToHsl();
|
||||
hsl.L *= 0.3;
|
||||
targetColor = Rgba32.FromHsl(hsl);
|
||||
}
|
||||
|
||||
if (colorText.Children.Count > 1)
|
||||
text.Inlines.Add(new Run
|
||||
{
|
||||
Span span = new()
|
||||
{
|
||||
Foreground = new SolidColorBrush(targetColor),
|
||||
};
|
||||
|
||||
foreach (MiHoYoSyntaxNode child in colorText.Children)
|
||||
{
|
||||
AppendNode(textBlock, span.Inlines, child);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inlines.Add(new Run
|
||||
{
|
||||
Text = colorText.ContentSpan.ToString(),
|
||||
Foreground = new SolidColorBrush(targetColor),
|
||||
});
|
||||
}
|
||||
Text = slice.ToString(),
|
||||
Foreground = new SolidColorBrush(targetColor),
|
||||
});
|
||||
}
|
||||
|
||||
private static void AppendItalicText(TextBlock textBlock, InlineCollection inlines, MiHoYoItalicTextSyntax italicText)
|
||||
private static void AppendItalicText(TextBlock text, in ReadOnlySpan<char> slice)
|
||||
{
|
||||
if (italicText.Children.Count > 1)
|
||||
text.Inlines.Add(new Run
|
||||
{
|
||||
Span span = new()
|
||||
{
|
||||
FontStyle = Windows.UI.Text.FontStyle.Italic,
|
||||
};
|
||||
Text = slice.ToString(),
|
||||
FontStyle = Windows.UI.Text.FontStyle.Italic,
|
||||
});
|
||||
}
|
||||
|
||||
foreach (MiHoYoSyntaxNode child in italicText.Children)
|
||||
{
|
||||
AppendNode(textBlock, span.Inlines, child);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inlines.Add(new Run
|
||||
{
|
||||
Text = italicText.ContentSpan.ToString(),
|
||||
FontStyle = Windows.UI.Text.FontStyle.Italic,
|
||||
});
|
||||
}
|
||||
private static void AppendLineBreak(TextBlock text)
|
||||
{
|
||||
text.Inlines.Add(new LineBreak());
|
||||
}
|
||||
|
||||
private void OnActualThemeChanged(FrameworkElement sender, object args)
|
||||
{
|
||||
// Simply re-apply texts
|
||||
UpdateDescription((TextBlock)Content, MiHoYoSyntaxTree.Parse(SpecialNameHandler.Handle(Description)));
|
||||
UpdateDescription((TextBlock)Content, Description);
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ internal sealed partial class HtmlDescriptionTextBlock : ContentControl
|
||||
else
|
||||
{
|
||||
// Make lighter in light mode
|
||||
Hsla32 hsl = color.ToHsl();
|
||||
Hsl32 hsl = color.ToHsl();
|
||||
hsl.L *= 0.3;
|
||||
targetColor = Rgba32.FromHsl(hsl);
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
|
||||
|
||||
internal enum MiHoYoColorKind
|
||||
{
|
||||
None,
|
||||
Rgba,
|
||||
Rgb,
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
|
||||
|
||||
internal sealed class MiHoYoColorTextSyntax : MiHoYoXmlElementSyntax
|
||||
{
|
||||
public MiHoYoColorTextSyntax(MiHoYoColorKind colorKind, string text, int start, int end)
|
||||
: base(MiHoYoSyntaxKind.ColorText, text, start, end)
|
||||
{
|
||||
ColorKind = colorKind;
|
||||
}
|
||||
|
||||
public MiHoYoColorTextSyntax(MiHoYoColorKind colorKind, string text, in TextPosition position)
|
||||
: base(MiHoYoSyntaxKind.ColorText, text, position)
|
||||
{
|
||||
ColorKind = colorKind;
|
||||
}
|
||||
|
||||
public MiHoYoColorKind ColorKind { get; }
|
||||
|
||||
public override TextPosition ContentPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return ColorKind switch
|
||||
{
|
||||
MiHoYoColorKind.Rgba => new(Position.Start + 17, Position.End - 8),
|
||||
MiHoYoColorKind.Rgb => new(Position.Start + 15, Position.End - 8),
|
||||
_ => throw Must.NeverHappen(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public TextPosition ColorPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return ColorKind switch
|
||||
{
|
||||
MiHoYoColorKind.Rgba => new(Position.Start + 8, Position.Start + 16),
|
||||
MiHoYoColorKind.Rgb => new(Position.Start + 8, Position.Start + 14),
|
||||
_ => throw Must.NeverHappen(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<char> ColorSpan { get => Text.AsSpan()[ColorPosition.Start..ColorPosition.End]; }
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
|
||||
|
||||
internal sealed class MiHoYoItalicTextSyntax : MiHoYoXmlElementSyntax
|
||||
{
|
||||
public MiHoYoItalicTextSyntax(string text, int start, int end)
|
||||
: base(MiHoYoSyntaxKind.ItalicText, text, start, end)
|
||||
{
|
||||
}
|
||||
|
||||
public MiHoYoItalicTextSyntax(string text, in TextPosition position)
|
||||
: base(MiHoYoSyntaxKind.ItalicText, text, position)
|
||||
{
|
||||
}
|
||||
|
||||
public override TextPosition ContentPosition { get => new(Position.Start + 3, Position.End - 4); }
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
|
||||
|
||||
internal sealed class MiHoYoPlainTextSyntax : MiHoYoSyntaxNode
|
||||
{
|
||||
public MiHoYoPlainTextSyntax(string text, int start, int end)
|
||||
: base(MiHoYoSyntaxKind.PlainText, text, start, end)
|
||||
{
|
||||
}
|
||||
|
||||
public MiHoYoPlainTextSyntax(string text, in TextPosition position)
|
||||
: base(MiHoYoSyntaxKind.PlainText, text, position)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
|
||||
|
||||
internal sealed class MiHoYoRootSyntax : MiHoYoSyntaxNode
|
||||
{
|
||||
public MiHoYoRootSyntax(string text, int start, int end)
|
||||
: base(MiHoYoSyntaxKind.Root, text, start, end)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
|
||||
|
||||
internal enum MiHoYoSyntaxKind
|
||||
{
|
||||
None,
|
||||
Root,
|
||||
PlainText,
|
||||
ColorText,
|
||||
ItalicText,
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
|
||||
|
||||
internal abstract class MiHoYoSyntaxNode : SyntaxNode<MiHoYoSyntaxNode, MiHoYoSyntaxKind>
|
||||
{
|
||||
public MiHoYoSyntaxNode(MiHoYoSyntaxKind kind, string text, int start, int end)
|
||||
: base(kind, text, start, end)
|
||||
{
|
||||
}
|
||||
|
||||
public MiHoYoSyntaxNode(MiHoYoSyntaxKind kind, string text, in TextPosition position)
|
||||
: base(kind, text, position)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
|
||||
|
||||
internal sealed class MiHoYoSyntaxTree
|
||||
{
|
||||
public MiHoYoSyntaxNode Root { get; set; } = default!;
|
||||
|
||||
public string Text { get; set; } = default!;
|
||||
|
||||
public static MiHoYoSyntaxTree Parse(string text)
|
||||
{
|
||||
MiHoYoRootSyntax root = new(text, 0, text.Length);
|
||||
ParseComponents(text, root);
|
||||
|
||||
MiHoYoSyntaxTree tree = new()
|
||||
{
|
||||
Text = text,
|
||||
Root = root,
|
||||
};
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
private static void ParseComponents(string text, MiHoYoSyntaxNode syntax)
|
||||
{
|
||||
TextPosition contentPosition = syntax switch
|
||||
{
|
||||
MiHoYoXmlElementSyntax xmlSyntax => xmlSyntax.ContentPosition,
|
||||
_ => syntax.Position,
|
||||
};
|
||||
ReadOnlySpan<char> contentSpan = text.AsSpan().Slice(contentPosition.Start, contentPosition.Length);
|
||||
|
||||
int endOfProcessedAtContent = 0;
|
||||
while (true)
|
||||
{
|
||||
if (endOfProcessedAtContent >= contentSpan.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int indexOfXmlLeftOpeningAtUnprocessedContent = contentSpan[endOfProcessedAtContent..].IndexOf('<');
|
||||
|
||||
// End of content
|
||||
if (indexOfXmlLeftOpeningAtUnprocessedContent < 0)
|
||||
{
|
||||
TextPosition position = new(contentPosition.Start + endOfProcessedAtContent, contentPosition.End);
|
||||
MiHoYoPlainTextSyntax plainText = new(text, position);
|
||||
syntax.Children.Add(plainText);
|
||||
break;
|
||||
}
|
||||
|
||||
// We have plain text between xml elements
|
||||
if (indexOfXmlLeftOpeningAtUnprocessedContent > 0)
|
||||
{
|
||||
TextPosition position = new(0, indexOfXmlLeftOpeningAtUnprocessedContent);
|
||||
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
|
||||
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
|
||||
MiHoYoPlainTextSyntax plainText = new(text, positionAtText);
|
||||
syntax.Children.Add(plainText);
|
||||
endOfProcessedAtContent = positionAtContent.End;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Peek the next character after '<'
|
||||
int indexOfXmlLeftOpeningAtContent = endOfProcessedAtContent + indexOfXmlLeftOpeningAtUnprocessedContent;
|
||||
switch (contentSpan[indexOfXmlLeftOpeningAtContent + 1])
|
||||
{
|
||||
case 'c':
|
||||
{
|
||||
int endOfXmlColorRightClosingAtUnprocessedContent = EndOfXmlClosing(contentSpan[indexOfXmlLeftOpeningAtContent..], out int endOfXmlColorLeftClosingAtUnprocessedContent);
|
||||
|
||||
MiHoYoColorKind colorKind = endOfXmlColorLeftClosingAtUnprocessedContent switch
|
||||
{
|
||||
17 => MiHoYoColorKind.Rgba,
|
||||
15 => MiHoYoColorKind.Rgb,
|
||||
_ => throw Must.NeverHappen(),
|
||||
};
|
||||
|
||||
TextPosition position = new(0, endOfXmlColorRightClosingAtUnprocessedContent);
|
||||
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
|
||||
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
|
||||
|
||||
MiHoYoColorTextSyntax colorText = new(colorKind, text, positionAtText);
|
||||
ParseComponents(text, colorText);
|
||||
syntax.Children.Add(colorText);
|
||||
endOfProcessedAtContent = positionAtContent.End;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'i':
|
||||
{
|
||||
int endOfXmlItalicRightClosingAtUnprocessedContent = EndOfXmlClosing(contentSpan[indexOfXmlLeftOpeningAtContent..], out _);
|
||||
|
||||
TextPosition position = new(0, endOfXmlItalicRightClosingAtUnprocessedContent);
|
||||
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
|
||||
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
|
||||
|
||||
MiHoYoItalicTextSyntax italicText = new(text, positionAtText);
|
||||
ParseComponents(text, italicText);
|
||||
syntax.Children.Add(italicText);
|
||||
endOfProcessedAtContent = positionAtContent.End;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int EndOfXmlClosing(in ReadOnlySpan<char> span, out int endOfLeftClosing)
|
||||
{
|
||||
endOfLeftClosing = 0;
|
||||
|
||||
int openingCount = 0;
|
||||
int closingCount = 0;
|
||||
|
||||
int current = 0;
|
||||
|
||||
// Considering <i>text1</i>text2<i>text3</i>
|
||||
// Considering <i>text1<span>text2</span>text3</i>
|
||||
while (true)
|
||||
{
|
||||
int leftMarkIndex = span[current..].IndexOf('<');
|
||||
if (span[current..][leftMarkIndex + 1] is '/')
|
||||
{
|
||||
closingCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
openingCount++;
|
||||
}
|
||||
|
||||
current += span[current..].IndexOf('>') + 1;
|
||||
|
||||
if (openingCount is 1 && closingCount is 0)
|
||||
{
|
||||
endOfLeftClosing = current;
|
||||
}
|
||||
|
||||
if (openingCount == closingCount)
|
||||
{
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
|
||||
|
||||
internal abstract class MiHoYoXmlElementSyntax : MiHoYoSyntaxNode
|
||||
{
|
||||
public MiHoYoXmlElementSyntax(MiHoYoSyntaxKind kind, string text, int start, int end)
|
||||
: base(kind, text, start, end)
|
||||
{
|
||||
}
|
||||
|
||||
public MiHoYoXmlElementSyntax(MiHoYoSyntaxKind kind, string text, in TextPosition position)
|
||||
: base(kind, text, position)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract TextPosition ContentPosition { get; }
|
||||
|
||||
public ReadOnlySpan<char> ContentSpan { get => Text.AsSpan(ContentPosition.Start, ContentPosition.Length); }
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax;
|
||||
|
||||
internal abstract class SyntaxNode<TSelf, TKind>
|
||||
where TSelf : SyntaxNode<TSelf, TKind>
|
||||
where TKind : struct, Enum
|
||||
{
|
||||
public SyntaxNode(TKind kind, string text, int start, int end)
|
||||
{
|
||||
Kind = kind;
|
||||
Text = text;
|
||||
Position = new(start, end);
|
||||
}
|
||||
|
||||
public SyntaxNode(TKind kind, string text, in TextPosition position)
|
||||
{
|
||||
Kind = kind;
|
||||
Text = text;
|
||||
Position = position;
|
||||
}
|
||||
|
||||
public TKind Kind { get; protected set; }
|
||||
|
||||
public List<TSelf> Children { get; } = [];
|
||||
|
||||
public TextPosition Position { get; protected set; }
|
||||
|
||||
public ReadOnlySpan<char> Span { get => Text.AsSpan().Slice(Position.Start, Position.Length); }
|
||||
|
||||
protected string Text { get; set; } = default!;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Snap.Hutao.Control.Text.Syntax;
|
||||
|
||||
[DebuggerDisplay("[{Start}..{End}]")]
|
||||
internal readonly struct TextPosition
|
||||
{
|
||||
public readonly int Start;
|
||||
public readonly int End;
|
||||
|
||||
public TextPosition(int start, int end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
public readonly int Length
|
||||
{
|
||||
get => End - Start;
|
||||
}
|
||||
|
||||
public TextPosition LeftShift(int offset)
|
||||
{
|
||||
return new(Start - offset, End - offset);
|
||||
}
|
||||
|
||||
public TextPosition RightShift(int offset)
|
||||
{
|
||||
return new(Start + offset, End + offset);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
Offset="0,4,0"/>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Style x:Key="BorderCardStyle" TargetType="Border">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||
@@ -26,20 +25,20 @@
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="AcrylicBaseHighBorderCardStyle"
|
||||
BasedOn="{StaticResource BorderCardStyle}"
|
||||
TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlBaseHighAcrylicElementBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="AcrylicBorderCardStyle"
|
||||
BasedOn="{StaticResource BorderCardStyle}"
|
||||
TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlAcrylicElementBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="AcrylicSecondaryBorderCardStyle"
|
||||
BasedOn="{StaticResource BorderCardStyle}"
|
||||
TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlChromeMediumAcrylicElementMediumBrush}"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlAcrylicElementMediumHighBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="GridCardStyle" TargetType="Grid">
|
||||
@@ -54,14 +53,14 @@
|
||||
BasedOn="{StaticResource GridCardStyle}"
|
||||
TargetType="Grid">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlAcrylicElementBrush}"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlAcrylicElementMediumHighBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="AcrylicSecondaryGridCardStyle"
|
||||
x:Key="AcrylicBaseHighGridCardStyle"
|
||||
BasedOn="{StaticResource GridCardStyle}"
|
||||
TargetType="Grid">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlChromeMediumAcrylicElementMediumBrush}"/>
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlBaseHighAcrylicElementBrush}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -193,379 +193,6 @@
|
||||
<CompositeTransform x:Name="PivotLayoutElementTranslateTransform"/>
|
||||
</Grid.RenderTransform>
|
||||
|
||||
<ItemsPresenter x:Name="PivotItemPresenter" Grid.Row="1">
|
||||
<ItemsPresenter.RenderTransform>
|
||||
<TransformGroup>
|
||||
<TranslateTransform x:Name="ItemsPresenterTranslateTransform"/>
|
||||
<CompositeTransform x:Name="ItemsPresenterCompositeTransform"/>
|
||||
</TransformGroup>
|
||||
</ItemsPresenter.RenderTransform>
|
||||
</ItemsPresenter>
|
||||
|
||||
<Border
|
||||
Grid.Row="0"
|
||||
Margin="16,16,16,0"
|
||||
cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Grid Style="{ThemeResource AcrylicSecondaryGridCardStyle}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ContentPresenter
|
||||
x:Name="LeftHeaderPresenter"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Content="{TemplateBinding LeftHeader}"
|
||||
ContentTemplate="{TemplateBinding LeftHeaderTemplate}"/>
|
||||
<ContentControl
|
||||
x:Name="HeaderClipper"
|
||||
Grid.Column="1"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
UseSystemFocusVisuals="{StaticResource UseSystemFocusVisuals}">
|
||||
<ContentControl.Clip>
|
||||
<RectangleGeometry x:Name="HeaderClipperGeometry"/>
|
||||
</ContentControl.Clip>
|
||||
<Grid>
|
||||
<Grid.RenderTransform>
|
||||
<CompositeTransform x:Name="HeaderOffsetTranslateTransform"/>
|
||||
</Grid.RenderTransform>
|
||||
<PivotHeaderPanel x:Name="StaticHeader" Visibility="Collapsed">
|
||||
<PivotHeaderPanel.RenderTransform>
|
||||
<CompositeTransform x:Name="StaticHeaderTranslateTransform"/>
|
||||
</PivotHeaderPanel.RenderTransform>
|
||||
</PivotHeaderPanel>
|
||||
<PivotHeaderPanel x:Name="Header">
|
||||
<PivotHeaderPanel.RenderTransform>
|
||||
<CompositeTransform x:Name="HeaderTranslateTransform"/>
|
||||
</PivotHeaderPanel.RenderTransform>
|
||||
</PivotHeaderPanel>
|
||||
<Rectangle
|
||||
x:Name="FocusFollower"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Control.IsTemplateFocusTarget="True"
|
||||
Fill="Transparent"
|
||||
IsHitTestVisible="False"/>
|
||||
</Grid>
|
||||
</ContentControl>
|
||||
<Button
|
||||
x:Name="PreviousButton"
|
||||
Grid.Column="1"
|
||||
Width="20"
|
||||
Height="36"
|
||||
Margin="{ThemeResource PivotNavButtonMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
IsEnabled="False"
|
||||
IsTabStop="False"
|
||||
Opacity="0"
|
||||
Template="{StaticResource PreviousTemplate}"
|
||||
UseSystemFocusVisuals="False"/>
|
||||
<Button
|
||||
x:Name="NextButton"
|
||||
Grid.Column="1"
|
||||
Width="20"
|
||||
Height="36"
|
||||
Margin="{ThemeResource PivotNavButtonMargin}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
IsEnabled="False"
|
||||
IsTabStop="False"
|
||||
Opacity="0"
|
||||
Template="{StaticResource NextTemplate}"
|
||||
UseSystemFocusVisuals="False"/>
|
||||
<ContentPresenter
|
||||
x:Name="RightHeaderPresenter"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Content="{TemplateBinding RightHeader}"
|
||||
ContentTemplate="{TemplateBinding RightHeaderTemplate}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</PivotPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="Orientation">
|
||||
<VisualState x:Name="Portrait">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleContentControl" Storyboard.TargetProperty="Margin">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPortraitThemePadding}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Landscape">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleContentControl" Storyboard.TargetProperty="Margin">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotLandscapeThemePadding}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="NavigationButtonsVisibility">
|
||||
<VisualState x:Name="NavigationButtonsHidden"/>
|
||||
<VisualState x:Name="NavigationButtonsVisible">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="NextButton" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="NextButton" Storyboard.TargetProperty="IsEnabled">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousButton" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousButton" Storyboard.TargetProperty="IsEnabled">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PreviousButtonVisible">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousButton" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PreviousButton" Storyboard.TargetProperty="IsEnabled">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="NextButtonVisible">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="NextButton" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="NextButton" Storyboard.TargetProperty="IsEnabled">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="HeaderStates">
|
||||
<VisualState x:Name="HeaderDynamic"/>
|
||||
<VisualState x:Name="HeaderStatic">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Header" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="StaticHeader" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="CardPivotStyle2" TargetType="Pivot">
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="Background" Value="{ThemeResource PivotBackground}"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<Grid/>
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Pivot">
|
||||
<Grid
|
||||
x:Name="RootElement"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
Background="{TemplateBinding Background}">
|
||||
<Grid.Resources>
|
||||
<Style x:Key="BaseContentControlStyle" TargetType="ContentControl">
|
||||
<Setter Property="FontFamily" Value="XamlAutoFontFamily"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ContentControl">
|
||||
<ContentPresenter
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
OpticalMarginAlignment="TrimSideBearings"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="TitleContentControlStyle"
|
||||
BasedOn="{StaticResource BaseContentControlStyle}"
|
||||
TargetType="ContentControl">
|
||||
<Setter Property="FontFamily" Value="{ThemeResource PivotTitleFontFamily}"/>
|
||||
<Setter Property="FontWeight" Value="{ThemeResource PivotTitleThemeFontWeight}"/>
|
||||
<Setter Property="FontSize" Value="{ThemeResource PivotTitleFontSize}"/>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ContentControl
|
||||
x:Name="TitleContentControl"
|
||||
Grid.Row="0"
|
||||
Margin="{StaticResource PivotPortraitThemePadding}"
|
||||
Content="{TemplateBinding Title}"
|
||||
ContentTemplate="{TemplateBinding TitleTemplate}"
|
||||
IsTabStop="False"
|
||||
Style="{StaticResource TitleContentControlStyle}"
|
||||
Visibility="Collapsed"/>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.Resources>
|
||||
<ControlTemplate x:Key="NextTemplate" TargetType="Button">
|
||||
<Border
|
||||
x:Name="Root"
|
||||
Background="{ThemeResource PivotNextButtonBackground}"
|
||||
BorderBrush="{ThemeResource PivotNextButtonBorderBrush}"
|
||||
BorderThickness="{ThemeResource PivotNavButtonBorderThemeThickness}">
|
||||
<FontIcon
|
||||
x:Name="Arrow"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource PivotNextButtonForeground}"
|
||||
Glyph=""
|
||||
MirroredWhenRightToLeft="True"
|
||||
UseLayoutRounding="False"/>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonBackgroundPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonBorderBrushPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Arrow" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonForegroundPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonBackgroundPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonBorderBrushPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Arrow" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotNextButtonForegroundPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
<ControlTemplate x:Key="PreviousTemplate" TargetType="Button">
|
||||
<Border
|
||||
x:Name="Root"
|
||||
Background="{ThemeResource PivotPreviousButtonBackground}"
|
||||
BorderBrush="{ThemeResource PivotPreviousButtonBorderBrush}"
|
||||
BorderThickness="{ThemeResource PivotNavButtonBorderThemeThickness}">
|
||||
<FontIcon
|
||||
x:Name="Arrow"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource PivotPreviousButtonForeground}"
|
||||
Glyph=""
|
||||
MirroredWhenRightToLeft="True"
|
||||
UseLayoutRounding="False"/>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonBackgroundPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonBorderBrushPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Arrow" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonForegroundPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonBackgroundPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonBorderBrushPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Arrow" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPreviousButtonForegroundPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Grid.Resources>
|
||||
<ScrollViewer
|
||||
x:Name="ScrollViewer"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
VerticalContentAlignment="Stretch"
|
||||
BringIntoViewOnFocusChange="False"
|
||||
HorizontalScrollBarVisibility="Hidden"
|
||||
HorizontalSnapPointsAlignment="Center"
|
||||
HorizontalSnapPointsType="MandatorySingle"
|
||||
Template="{StaticResource ScrollViewerScrollBarlessTemplate}"
|
||||
VerticalScrollBarVisibility="Disabled"
|
||||
VerticalScrollMode="Disabled"
|
||||
VerticalSnapPointsType="None"
|
||||
ZoomMode="Disabled">
|
||||
<PivotPanel x:Name="Panel" VerticalAlignment="Stretch">
|
||||
<Grid x:Name="PivotLayoutElement">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.RenderTransform>
|
||||
<CompositeTransform x:Name="PivotLayoutElementTranslateTransform"/>
|
||||
</Grid.RenderTransform>
|
||||
|
||||
|
||||
|
||||
<ItemsPresenter x:Name="PivotItemPresenter" Grid.Row="1">
|
||||
@@ -581,7 +208,7 @@
|
||||
Grid.Row="0"
|
||||
Margin="16,16,16,0"
|
||||
cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Grid Style="{ThemeResource AcrylicSecondaryGridCardStyle}">
|
||||
<Grid Style="{ThemeResource AcrylicBaseHighGridCardStyle}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cwc="using:CommunityToolkit.WinUI.Controls">
|
||||
<!-- Settings -->
|
||||
<x:Double x:Key="SettingsCardSpacing">3</x:Double>
|
||||
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
|
||||
@@ -21,14 +24,6 @@
|
||||
<Setter Property="Margin" Value="1,29,0,5"/>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="SettingsCardHeaderTextBlockStyle"
|
||||
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
|
||||
TargetType="TextBlock">
|
||||
<Style.Setters>
|
||||
<Setter Property="Margin" Value="1,0,0,5"/>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="SettingsContentComboBoxStyle"
|
||||
BasedOn="{StaticResource DefaultComboBoxStyle}"
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Thickness x:Key="ListViewInSplitPanePadding">0,2</Thickness>
|
||||
<!-- https://github.com/microsoft/microsoft-ui-xaml/issues/4811 -->
|
||||
<x:Int32 x:Key="__DiscardPageOverride">0</x:Int32>
|
||||
</ResourceDictionary>
|
||||
@@ -34,6 +34,5 @@
|
||||
<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>
|
||||
<x:String x:Key="UI_EmotionIcon293">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon293.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon433">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon433.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon445">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon445.png</x:String>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Core.Database;
|
||||
|
||||
internal sealed class ObservableReorderableDbCollection<TEntity> : ObservableCollection<TEntity>
|
||||
where TEntity : class, IReorderable
|
||||
internal sealed class ObservableReorderableDbCollection<T> : ObservableCollection<T>
|
||||
where T : class, IReorderable
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly DbContext dbContext;
|
||||
private bool previousChangeIsRemoved;
|
||||
|
||||
public ObservableReorderableDbCollection(List<TEntity> items, IServiceProvider serviceProvider)
|
||||
: base(AdjustIndex(items.SortBy(x => x.Index)))
|
||||
public ObservableReorderableDbCollection(List<T> items, DbContext dbContext)
|
||||
: base(AdjustIndex(items))
|
||||
{
|
||||
this.serviceProvider = serviceProvider;
|
||||
this.dbContext = dbContext;
|
||||
}
|
||||
|
||||
public IAdvancedCollectionView? View { get; set; }
|
||||
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
base.OnCollectionChanged(e);
|
||||
@@ -31,18 +27,26 @@ internal sealed class ObservableReorderableDbCollection<TEntity> : ObservableCol
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
previousChangeIsRemoved = true;
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
if (!previousChangeIsRemoved)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnReorder();
|
||||
previousChangeIsRemoved = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<TEntity> AdjustIndex(List<TEntity> list)
|
||||
private static List<T> AdjustIndex(List<T> list)
|
||||
{
|
||||
Span<TEntity> span = CollectionsMarshal.AsSpan(list);
|
||||
Span<T> span = CollectionsMarshal.AsSpan(list);
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
ref readonly TEntity item = ref span[i];
|
||||
ref readonly T item = ref span[i];
|
||||
item.Index = i;
|
||||
}
|
||||
|
||||
@@ -51,79 +55,12 @@ internal sealed class ObservableReorderableDbCollection<TEntity> : ObservableCol
|
||||
|
||||
private void OnReorder()
|
||||
{
|
||||
using (View?.DeferRefresh())
|
||||
AdjustIndex((List<T>)Items);
|
||||
|
||||
DbSet<T> dbSet = dbContext.Set<T>();
|
||||
foreach (ref readonly T item in CollectionsMarshal.AsSpan((List<T>)Items))
|
||||
{
|
||||
AdjustIndex((List<TEntity>)Items);
|
||||
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
DbSet<TEntity> dbSet = appDbContext.Set<TEntity>();
|
||||
foreach (ref readonly TEntity item in CollectionsMarshal.AsSpan((List<TEntity>)Items))
|
||||
{
|
||||
dbSet.UpdateAndSave(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SA1402")]
|
||||
internal sealed class ObservableReorderableDbCollection<TEntityOnly, TEntity> : ObservableCollection<TEntityOnly>
|
||||
where TEntityOnly : class, IEntityOnly<TEntity>
|
||||
where TEntity : class, IReorderable
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public ObservableReorderableDbCollection(List<TEntityOnly> items, IServiceProvider serviceProvider)
|
||||
: base(AdjustIndex(items.SortBy(x => x.Entity.Index)))
|
||||
{
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IAdvancedCollectionView? View { get; set; }
|
||||
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
base.OnCollectionChanged(e);
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
OnReorder();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<TEntityOnly> AdjustIndex(List<TEntityOnly> list)
|
||||
{
|
||||
Span<TEntityOnly> span = CollectionsMarshal.AsSpan(list);
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
ref readonly TEntityOnly item = ref span[i];
|
||||
item.Entity.Index = i;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private void OnReorder()
|
||||
{
|
||||
using (View?.DeferRefresh())
|
||||
{
|
||||
AdjustIndex((List<TEntityOnly>)Items);
|
||||
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
DbSet<TEntity> dbSet = appDbContext.Set<TEntity>();
|
||||
foreach (ref readonly TEntityOnly item in CollectionsMarshal.AsSpan((List<TEntityOnly>)Items))
|
||||
{
|
||||
dbSet.UpdateAndSave(item.Entity);
|
||||
}
|
||||
}
|
||||
dbSet.UpdateAndSave(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,12 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.Http;
|
||||
using Snap.Hutao.Core.IO.Http.Proxy;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Service;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Windows.Globalization;
|
||||
|
||||
|
||||
@@ -73,15 +73,6 @@ internal static class ThrowHelper
|
||||
throw new NotSupportedException(message);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void NotSupportedIf(bool condition, string message)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
throw new NotSupportedException(message);
|
||||
}
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static OperationCanceledException OperationCanceled(string message, Exception? inner = default)
|
||||
|
||||
@@ -8,15 +8,12 @@ using System.IO;
|
||||
using System.Security.Principal;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Notifications;
|
||||
|
||||
namespace Snap.Hutao.Core;
|
||||
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed class RuntimeOptions
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
private readonly Lazy<(Version Version, string UserAgent)> lazyVersionAndUserAgent = new(() =>
|
||||
{
|
||||
Version version = Package.Current.Id.Version.ToVersion();
|
||||
@@ -86,14 +83,8 @@ internal sealed class RuntimeOptions
|
||||
private readonly Lazy<string> lazyInstalledLocation = new(() => Package.Current.InstalledLocation.Path);
|
||||
private readonly Lazy<string> lazyFamilyName = new(() => Package.Current.Id.FamilyName);
|
||||
|
||||
private bool isToastAvailable;
|
||||
private bool isToastAvailableInitialized;
|
||||
private object isToastAvailableLock = new();
|
||||
|
||||
public RuntimeOptions(IServiceProvider serviceProvider, ILogger<RuntimeOptions> logger)
|
||||
public RuntimeOptions(ILogger<RuntimeOptions> logger)
|
||||
{
|
||||
this.serviceProvider = serviceProvider;
|
||||
|
||||
AppLaunchTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
@@ -117,19 +108,5 @@ internal sealed class RuntimeOptions
|
||||
|
||||
public bool IsElevated { get => lazyElevated.Value; }
|
||||
|
||||
public bool IsToastAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
return LazyInitializer.EnsureInitialized(ref isToastAvailable, ref isToastAvailableInitialized, ref isToastAvailableLock, GetIsToastAvailable);
|
||||
|
||||
bool GetIsToastAvailable()
|
||||
{
|
||||
ITaskContext taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
||||
return taskContext.InvokeOnMainThread(() => ToastNotificationManager.CreateToastNotifier().Setting is NotificationSetting.Enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DateTimeOffset AppLaunchTime { get; }
|
||||
}
|
||||
@@ -18,9 +18,4 @@ internal static class RuntimeOptionsExtension
|
||||
{
|
||||
return Path.Combine(options.DataFolder, "ServerCache");
|
||||
}
|
||||
|
||||
public static string GetDataFolderBackgroundFolder(this RuntimeOptions options)
|
||||
{
|
||||
return Path.Combine(options.DataFolder, "Background");
|
||||
}
|
||||
}
|
||||
@@ -48,45 +48,4 @@ internal static class DispatcherQueueExtension
|
||||
exceptionDispatchInfo?.Throw();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在调度器队列同步调用,直到执行结束,会持续阻塞当前线程
|
||||
/// </summary>
|
||||
/// <param name="dispatcherQueue">调度器队列</param>
|
||||
/// <param name="action">执行的回调</param>
|
||||
/// <typeparam name="T">返回类型</typeparam>
|
||||
/// <returns>回调返回值</returns>
|
||||
public static T Invoke<T>(this DispatcherQueue dispatcherQueue, Func<T> action)
|
||||
{
|
||||
T result = default!;
|
||||
|
||||
if (dispatcherQueue.HasThreadAccess)
|
||||
{
|
||||
return action();
|
||||
}
|
||||
|
||||
ExceptionDispatchInfo? exceptionDispatchInfo = null;
|
||||
using (ManualResetEventSlim blockEvent = new(false))
|
||||
{
|
||||
dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
result = action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
blockEvent.Set();
|
||||
}
|
||||
});
|
||||
|
||||
blockEvent.Wait();
|
||||
exceptionDispatchInfo?.Throw();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,6 @@ internal interface ITaskContext
|
||||
|
||||
void InvokeOnMainThread(Action action);
|
||||
|
||||
T InvokeOnMainThread<T>(Func<T> action);
|
||||
|
||||
ThreadPoolSwitchOperation SwitchToBackgroundAsync();
|
||||
|
||||
DispatcherQueueSwitchOperation SwitchToMainThreadAsync();
|
||||
|
||||
@@ -44,12 +44,6 @@ internal sealed class TaskContext : ITaskContext, ITaskContextUnsafe
|
||||
dispatcherQueue.Invoke(action);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T InvokeOnMainThread<T>(Func<T> action)
|
||||
{
|
||||
return dispatcherQueue.Invoke(action);
|
||||
}
|
||||
|
||||
public void BeginInvokeOnMainThread(Action action)
|
||||
{
|
||||
dispatcherQueue.TryEnqueue(() => action());
|
||||
|
||||
@@ -69,12 +69,9 @@ internal sealed class WindowController
|
||||
window.Activate();
|
||||
options.BringToForeground();
|
||||
|
||||
if (options.UseSystemBackdrop)
|
||||
{
|
||||
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
|
||||
UpdateSystemBackdrop(appOptions.BackdropType);
|
||||
appOptions.PropertyChanged += OnOptionsPropertyChanged;
|
||||
}
|
||||
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
|
||||
UpdateSystemBackdrop(appOptions.BackdropType);
|
||||
appOptions.PropertyChanged += OnOptionsPropertyChanged;
|
||||
|
||||
subclass.Initialize();
|
||||
|
||||
|
||||
@@ -41,21 +41,25 @@ 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, bool useSystemBackdrop = true)
|
||||
/// <summary>
|
||||
/// 构造一个新的窗体选项
|
||||
/// </summary>
|
||||
/// <param name="window">窗体</param>
|
||||
/// <param name="titleBar">标题栏</param>
|
||||
/// <param name="initSize">初始尺寸</param>
|
||||
/// <param name="persistSize">持久化尺寸</param>
|
||||
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, bool persistSize = false)
|
||||
{
|
||||
Hwnd = WindowNative.GetWindowHandle(window);
|
||||
InputNonClientPointerSource = InputNonClientPointerSource.GetForWindowId(window.AppWindow.Id);
|
||||
TitleBar = titleBar;
|
||||
InitSize = initSize;
|
||||
PersistSize = persistSize;
|
||||
UseSystemBackdrop = useSystemBackdrop;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
@@ -115,25 +113,6 @@ internal static partial class EnumerableExtension
|
||||
return new ObservableCollection<T>(source);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ObservableReorderableDbCollection<TEntity> ToObservableReorderableDbCollection<TEntity>(this IEnumerable<TEntity> source, IServiceProvider serviceProvider)
|
||||
where TEntity : class, IReorderable
|
||||
{
|
||||
return source is List<TEntity> list
|
||||
? new ObservableReorderableDbCollection<TEntity>(list, serviceProvider)
|
||||
: new ObservableReorderableDbCollection<TEntity>([.. source], serviceProvider);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ObservableReorderableDbCollection<TEntityOnly, TEntity> ToObservableReorderableDbCollection<TEntityOnly, TEntity>(this IEnumerable<TEntityOnly> source, IServiceProvider serviceProvider)
|
||||
where TEntityOnly : class, IEntityOnly<TEntity>
|
||||
where TEntity : class, IReorderable
|
||||
{
|
||||
return source is List<TEntityOnly> list
|
||||
? new ObservableReorderableDbCollection<TEntityOnly, TEntity>(list, serviceProvider)
|
||||
: new ObservableReorderableDbCollection<TEntityOnly, TEntity>([.. source], serviceProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates each element from the collection into single string.
|
||||
/// </summary>
|
||||
|
||||
@@ -27,7 +27,7 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource, IMinMax
|
||||
public MainWindow(IServiceProvider serviceProvider)
|
||||
{
|
||||
InitializeComponent();
|
||||
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true, false);
|
||||
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true);
|
||||
this.InitializeController(serviceProvider);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,624 +0,0 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20240202015857_ImplReorderableOnUserAndGameAccount")]
|
||||
partial class ImplReorderableOnUserAndGameAccount
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.1");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Current")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("achievements");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("achievement_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("CalculatorRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("GameRecordRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Info")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("ShowcaseRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("avatar_infos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("cultivate_entries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("AvatarLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("AvatarLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("SkillALevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillALevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("cultivate_entry_level_informations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsFinished")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachedUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("cultivate_projects");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DailyNote")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("DailyTaskNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("DailyTaskNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HomeCoinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HomeCoinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("RefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ResinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ResinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("daily_notes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("gacha_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("GachaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("QueryType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("gacha_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("MihoyoSDK")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("game_accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AppendPropIdList")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MainPropId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_reliquaries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PromoteLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_weapons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("ExpireTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("object_cache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("ScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SpiralAbyss")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("spiral_abysses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Aid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CookieToken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("CookieTokenLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Fingerprint")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("FingerprintLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsOversea")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Ltoken");
|
||||
|
||||
b.Property<string>("Mid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Stoken");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithOne("LevelInformation")
|
||||
.HasForeignKey("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", "EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithMany()
|
||||
.HasForeignKey("EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Navigation("LevelInformation");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ImplReorderableOnUserAndGameAccount : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_cultivate_entry_level_informations_EntryId",
|
||||
table: "cultivate_entry_level_informations");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Index",
|
||||
table: "users",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Index",
|
||||
table: "game_accounts",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_cultivate_entry_level_informations_EntryId",
|
||||
table: "cultivate_entry_level_informations",
|
||||
column: "EntryId",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_cultivate_entry_level_informations_EntryId",
|
||||
table: "cultivate_entry_level_informations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Index",
|
||||
table: "users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Index",
|
||||
table: "game_accounts");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_cultivate_entry_level_informations_EntryId",
|
||||
table: "cultivate_entry_level_informations",
|
||||
column: "EntryId");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Snap.Hutao.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.1");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.0");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
@@ -42,7 +42,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("achievements");
|
||||
b.ToTable("achievements", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||
@@ -60,7 +60,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("achievement_archives");
|
||||
b.ToTable("achievement_archives", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||
@@ -88,7 +88,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("avatar_infos");
|
||||
b.ToTable("avatar_infos", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
@@ -110,7 +110,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("cultivate_entries");
|
||||
b.ToTable("cultivate_entries", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
@@ -154,10 +154,9 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId")
|
||||
.IsUnique();
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_entry_level_informations");
|
||||
b.ToTable("cultivate_entry_level_informations", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
@@ -182,7 +181,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_items");
|
||||
b.ToTable("cultivate_items", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
|
||||
@@ -203,7 +202,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("cultivate_projects");
|
||||
b.ToTable("cultivate_projects", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
@@ -259,7 +258,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("daily_notes");
|
||||
b.ToTable("daily_notes", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||
@@ -277,7 +276,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("gacha_archives");
|
||||
b.ToTable("gacha_archives", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
@@ -308,7 +307,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("gacha_items");
|
||||
b.ToTable("gacha_items", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||
@@ -320,9 +319,6 @@ namespace Snap.Hutao.Migrations
|
||||
b.Property<string>("AttachUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("MihoyoSDK")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -336,7 +332,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("game_accounts");
|
||||
b.ToTable("game_accounts", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
@@ -358,7 +354,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_items");
|
||||
b.ToTable("inventory_items", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
@@ -387,7 +383,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_reliquaries");
|
||||
b.ToTable("inventory_reliquaries", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
@@ -412,7 +408,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_weapons");
|
||||
b.ToTable("inventory_weapons", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
|
||||
@@ -428,7 +424,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("object_cache");
|
||||
b.ToTable("object_cache", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||
@@ -441,7 +437,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("settings");
|
||||
b.ToTable("settings", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
|
||||
@@ -463,7 +459,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("spiral_abysses");
|
||||
b.ToTable("spiral_abysses", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
@@ -487,9 +483,6 @@ namespace Snap.Hutao.Migrations
|
||||
b.Property<DateTimeOffset>("FingerprintLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsOversea")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -509,7 +502,7 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("users");
|
||||
b.ToTable("users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
@@ -537,8 +530,8 @@ namespace Snap.Hutao.Migrations
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithOne("LevelInformation")
|
||||
.HasForeignKey("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", "EntryId")
|
||||
.WithMany()
|
||||
.HasForeignKey("EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
@@ -610,11 +603,6 @@ namespace Snap.Hutao.Migrations
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Navigation("LevelInformation");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
@@ -15,7 +14,7 @@ namespace Snap.Hutao.Model.Entity;
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Table("game_accounts")]
|
||||
internal sealed class GameAccount : ObservableObject, IReorderable, IMappingFrom<GameAccount, string, string, SchemeType>
|
||||
internal sealed class GameAccount : ObservableObject, IMappingFrom<GameAccount, string, string, SchemeType>, ICloneable<GameAccount>
|
||||
{
|
||||
/// <summary>
|
||||
/// 内部Id
|
||||
@@ -45,8 +44,6 @@ internal sealed class GameAccount : ObservableObject, IReorderable, IMappingFrom
|
||||
/// </summary>
|
||||
public string MihoyoSDK { get; set; } = default!;
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public static GameAccount From(string name, string sdk, SchemeType type)
|
||||
{
|
||||
return new()
|
||||
@@ -57,6 +54,17 @@ internal sealed class GameAccount : ObservableObject, IReorderable, IMappingFrom
|
||||
};
|
||||
}
|
||||
|
||||
public GameAccount Clone()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
AttachUid = AttachUid,
|
||||
Name = Name,
|
||||
MihoyoSDK = MihoyoSDK,
|
||||
Type = Type,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新绑定的Uid
|
||||
/// </summary>
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Snap.Hutao.Model.Entity;
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Table("users")]
|
||||
internal sealed class User : ISelectable, IReorderable, IMappingFrom<User, Cookie, bool>
|
||||
internal sealed class User : ISelectable, IMappingFrom<User, Cookie, bool>
|
||||
{
|
||||
/// <summary>
|
||||
/// 内部Id
|
||||
@@ -69,8 +69,6 @@ internal sealed class User : ISelectable, IReorderable, IMappingFrom<User, Cooki
|
||||
|
||||
public DateTimeOffset CookieTokenLastUpdateTime { get; set; }
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的用户
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Control.Text;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
|
||||
@@ -12,11 +12,6 @@ internal static partial class SpecialNameHandler
|
||||
// "#(?!.*(?:F#|M#|NON_BREAK_SPACE|REALNAME\[ID\(1\)(\|HOSTONLY\(true\)|)\]|\{LAYOUT_MOBILE#.+?\}\{LAYOUT_PC#.+?\}\{LAYOUT_PS#.+?\})).*
|
||||
public static string Handle(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
if (input.AsSpan()[0] is not '#')
|
||||
{
|
||||
return input;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutao"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.9.6.0" />
|
||||
Version="1.9.5.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao</DisplayName>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutaoDev"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.9.6.0" />
|
||||
Version="1.9.5.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao Dev</DisplayName>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@@ -145,7 +145,7 @@
|
||||
<value>Save</value>
|
||||
</data>
|
||||
<data name="ControlImageCachedImageInvalidResourceUri" xml:space="preserve">
|
||||
<value>Invalid URL</value>
|
||||
<value>Invalid Uri</value>
|
||||
</data>
|
||||
<data name="ControlImageCompositionImageHttpRequest" xml:space="preserve">
|
||||
<value>HTTP GET {0}</value>
|
||||
@@ -198,21 +198,6 @@
|
||||
<data name="LaunchGameTitle" xml:space="preserve">
|
||||
<value>Select Account to Launch</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
|
||||
<value><color=#1E90FF>Prince</color>/<color=#FFB6C1>Princess</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
|
||||
<value><color=#1E90FF>I</color>/<color=#FFB6C1>I</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameNickname" xml:space="preserve">
|
||||
<value>Traveler</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
|
||||
<value><color=#1E90FF>He</color>/<color=#FFB6C1>She</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameRealNameId1" xml:space="preserve">
|
||||
<value>Wanderer</value>
|
||||
</data>
|
||||
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
|
||||
<value>Refine {0}</value>
|
||||
</data>
|
||||
@@ -512,9 +497,6 @@
|
||||
<data name="ModelMetadataTowerWaveTypeWave4" xml:space="preserve">
|
||||
<value>Wave 4: A wave will spawn only after killing all enemies in the previous wave</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
|
||||
<value>Remain 4 Treasure Hoarders on the field, instantly respawn on death. 12 in total.</value>
|
||||
</data>
|
||||
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
|
||||
<value>Please update showcase data</value>
|
||||
</data>
|
||||
@@ -528,7 +510,7 @@
|
||||
<value>Must sign in to your MiHoYo BBS account and select a user</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceDeleteEntrySucceed" xml:space="preserve">
|
||||
<value>Deleted {1} wish records from UID: {0}</value>
|
||||
<value>Deleted {1} wish record from Uid: {0}</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientRecordSlot" xml:space="preserve">
|
||||
<value>Reached max allowed number of wish history archives on Snap Hutao Cloud</value>
|
||||
@@ -543,7 +525,7 @@
|
||||
<value>Found abnormal data, unable to upload to Snap Hutao Cloud. Please do not upload across accounts or you can attempt to delete cloud data and try again.</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceUploadEntrySucceed" xml:space="preserve">
|
||||
<value>Uploaded {1} wish records of UID: {0}, stored {2}</value>
|
||||
<value>Uploaded {1} wish records of Uid: {0}, stored {2}</value>
|
||||
</data>
|
||||
<data name="ServerPassportLoginRequired" xml:space="preserve">
|
||||
<value>Please login or register Hutao account first</value>
|
||||
@@ -591,7 +573,7 @@
|
||||
<value>The verification request failed, the current email address has been registered</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyTooFrequent" xml:space="preserve">
|
||||
<value>Verification request is too frequent. Please try again in 1 minute.</value>
|
||||
<value>Validation request is too frequent. Please try again in 1 minute.</value>
|
||||
</data>
|
||||
<data name="ServerRecordBannedUid" xml:space="preserve">
|
||||
<value>Failed to upload Sprial Abyss record, current UID is banned by Hutao Database</value>
|
||||
@@ -615,7 +597,7 @@
|
||||
<value>Failed to upload Spiral Abyss record. It is not data for the current schedule.</value>
|
||||
</data>
|
||||
<data name="ServerRecordPreviousRequestNotCompleted" xml:space="preserve">
|
||||
<value>Failed to upload Sprial Abyss record. The record for the current UID is still being processed. Please do not repeat the operation.</value>
|
||||
<value>Failed to upload Sprial Abyss record. The record for the current Uid is still being processed. Please do not repeat the operation.</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessAndGachaLogServiceTimeExtended" xml:space="preserve">
|
||||
<value>Uploaded Spiral Abyss record successfully. Received a privilege extension for Snap Hutao Cloud service.</value>
|
||||
@@ -627,7 +609,7 @@
|
||||
<value>Uploaded abyss record successfully. No Snap Hutao Cloud privilege received as no Snap Hutao account found.</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNotFirstTimeAtCurrentSchedule" xml:space="preserve">
|
||||
<value>Uploaded abyss record successfully. No Snap Hutao Cloud privilege received as this is not the first upload of current schedule.</value>
|
||||
<value>Uploaded abyss record successfully. No Snap Hutao Cloud privilege received as there is not first upload of current schedule.</value>
|
||||
</data>
|
||||
<data name="ServiceAchievementImportResultFormat" xml:space="preserve">
|
||||
<value>New: {0} Achievements | Updated: {1} Achievements | Delete: {2} Achievements</value>
|
||||
@@ -750,10 +732,10 @@
|
||||
<comment>Need EXACT same string in game</comment>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryCalculatorNotRefreshed" xml:space="preserve">
|
||||
<value>Enhancement Progression Calculator: N/A</value>
|
||||
<value>Dev Plan: N/A</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryCalculatorRefreshTimeFormat" xml:space="preserve">
|
||||
<value>Enhancement Progression Calculator: {0:MM-dd HH:mm}</value>
|
||||
<value>Avatar Calculator: {0:MM-dd HH:mm}</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
|
||||
<value>Battle Chronicle: Not Refreshed</value>
|
||||
@@ -873,7 +855,7 @@
|
||||
<value>SToken refresh does not support HoYoLab account</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale" xml:space="preserve">
|
||||
<value>URL language: {0} does not match Snap Hutao language: {1}, please retry after switching client language to target URL language</value>
|
||||
<value>UIGF file language: {0} does not match Snap Hutao language: {1}, please retry after switching client language to target UIGF file language</value>
|
||||
</data>
|
||||
<data name="ServiceGachaStatisticsFactoryItemIdInvalid" xml:space="preserve">
|
||||
<value>Unsupported Item ID: {0}</value>
|
||||
@@ -1005,7 +987,7 @@
|
||||
<value>Failed to download Metadata validation file</value>
|
||||
</data>
|
||||
<data name="ServiceMetadataVersionNotSupported" xml:space="preserve">
|
||||
<value>Your Snap Hutao is outdated. Please update as soon as possible.</value>
|
||||
<value>Your Snap Hutao version is too low. Please update as soon as possible.</value>
|
||||
</data>
|
||||
<data name="ServiceSignInClaimRewardFailedFormat" xml:space="preserve">
|
||||
<value>Check-in failed, {0}</value>
|
||||
@@ -1017,7 +999,7 @@
|
||||
<value>Fetch Signin reward list failed</value>
|
||||
</data>
|
||||
<data name="ServiceSignInRiskVerificationFailed" xml:space="preserve">
|
||||
<value>Verification failed, please visit miHoYo BBS checkin page to manually claim reward</value>
|
||||
<value>Verification failed, please visit MiYouShe checkin page to manually claim reward</value>
|
||||
</data>
|
||||
<data name="ServiceSignInSuccessRewardFormat" xml:space="preserve">
|
||||
<value>Check-in successfully, {0} x {1}</value>
|
||||
@@ -1095,10 +1077,10 @@
|
||||
<value>4 Stars</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardToLastOrangeText" xml:space="preserve">
|
||||
<value>Last 5 Star</value>
|
||||
<value>Last 5 Stars</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardToLastPurpleText" xml:space="preserve">
|
||||
<value>Last 4 Star</value>
|
||||
<value>Last 4 Stars</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardUpAveragePullText" xml:space="preserve">
|
||||
<value>Luckiness Avg</value>
|
||||
@@ -1176,19 +1158,19 @@
|
||||
<value>Batch add or update to current Dev Plan</value>
|
||||
</data>
|
||||
<data name="ViewDialogCultivatePromotionDeltaTitle" xml:space="preserve">
|
||||
<value>Add or update to current Enhancement Progression Plan</value>
|
||||
<value>Add or update to current Dev Plan</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteNotificationDailyTaskNotify" xml:space="preserve">
|
||||
<value>Daily Commission Availability Notification</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteNotificationExpeditionNotify" xml:space="preserve">
|
||||
<value>Expedition Completion Notification</value>
|
||||
<value>Expedition Reward Notification</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteNotificationHomeCoinNotifyThreshold" xml:space="preserve">
|
||||
<value>Realm Currency Notification Threshold</value>
|
||||
<value>Realm Currency Alarm Threshold</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteNotificationResinNotifyThreshold" xml:space="preserve">
|
||||
<value>Original Resin Notification Threshold</value>
|
||||
<value>Original Resin Alarm Threshold</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteNotificationShowInHomeWidget" xml:space="preserve">
|
||||
<value>Show widget on Home page</value>
|
||||
@@ -1242,10 +1224,10 @@
|
||||
<value>Returned data</value>
|
||||
</data>
|
||||
<data name="ViewDialogGeetestCustomUrlSampleDescription1" xml:space="preserve">
|
||||
<value>{0} will be replaced with "gt" during a request</value>
|
||||
<value>{0} will be replaced with gt during a request</value>
|
||||
</data>
|
||||
<data name="ViewDialogGeetestCustomUrlSampleDescription2" xml:space="preserve">
|
||||
<value>{1} will be replaced with "challenge" during a request</value>
|
||||
<value>{1} will be replaced with challenge during a request</value>
|
||||
</data>
|
||||
<data name="ViewDialogGeetestCustomUrlSampleDescription3" xml:space="preserve">
|
||||
<value>Requests will be sent to interfaces via GET</value>
|
||||
@@ -1299,7 +1281,7 @@
|
||||
<value>Name the account</value>
|
||||
</data>
|
||||
<data name="ViewDialogLaunchGamePackageConvertHint" xml:space="preserve">
|
||||
<value>Conversion may take some time. Please don't close HuTao.</value>
|
||||
<value>Conversion may take some time. Do not turn off the software.</value>
|
||||
</data>
|
||||
<data name="ViewDialogLaunchGamePackageConvertTitle" xml:space="preserve">
|
||||
<value>Converting Game Client</value>
|
||||
@@ -1308,7 +1290,7 @@
|
||||
<value>Scan the QR code with MiHoYo BBS App</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTextHeader" xml:space="preserve">
|
||||
<value>To avoid enabling unintentionally, please input the <b>title name</b> of feature you are enabling</value>
|
||||
<value>To avoid enable in a mistake, please input <b>title name</b> of feature you are enabling</value>
|
||||
</data>
|
||||
<data name="ViewDialogReconfirmTitle" xml:space="preserve">
|
||||
<value>You are Enabling a Dangerous Feature</value>
|
||||
@@ -1350,13 +1332,13 @@
|
||||
<value>Snap Hutao Open Source License</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepAgreementPrivacyPolicy" xml:space="preserve">
|
||||
<value>User Data and Privacy Policy</value>
|
||||
<value>User Data and Privacy Notice</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepAgreementTermOfService" xml:space="preserve">
|
||||
<value>User Agreement and Legal Notices</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepDocument" xml:space="preserve">
|
||||
<value>Document</value>
|
||||
<value>Documentation</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepEnvironment" xml:space="preserve">
|
||||
<value>Environments</value>
|
||||
@@ -1443,7 +1425,7 @@
|
||||
<value>No Artifact Set</value>
|
||||
</data>
|
||||
<data name="ViewModelCultivationAddWarning" xml:space="preserve">
|
||||
<value>Failed to add Enhancement Progression plan</value>
|
||||
<value>Failed to add development plan</value>
|
||||
</data>
|
||||
<data name="ViewModelCultivationBatchAddCompletedFormat" xml:space="preserve">
|
||||
<value>Operation completed: added/updated {0}, skipped {1}</value>
|
||||
@@ -1455,7 +1437,7 @@
|
||||
<value>Successfully added to current plan</value>
|
||||
</data>
|
||||
<data name="ViewModelCultivationEntryAddWarning" xml:space="preserve">
|
||||
<value>Please create a plan and select it in the Enhancement Progression Plan feature</value>
|
||||
<value>Please create a plan and select it in the Development Plan feature</value>
|
||||
</data>
|
||||
<data name="ViewModelCultivationEntryViewDescriptionDefault" xml:space="preserve">
|
||||
<value>Add item again to view cultivation description</value>
|
||||
@@ -1590,7 +1572,7 @@
|
||||
<value>Identify Monitors</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameMultiChannelReadFail" xml:space="preserve">
|
||||
<value>Unable to read game config file: {0}, file may be not exist or insufficient permission</value>
|
||||
<value>Unable to read game config file: {0}, file may be not exist not lack of user permission</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGamePathInvalid" xml:space="preserve">
|
||||
<value>Game program path is incorrect. Change it in the Settings Page.</value>
|
||||
@@ -1602,7 +1584,7 @@
|
||||
<value>Switch game account failed</value>
|
||||
</data>
|
||||
<data name="ViewModelLaunchGameUnableToSwitchUidAttachedGameAccount" xml:space="preserve">
|
||||
<value>Cannot select account [{1}] for UID [{0}], the account does not belong to current server</value>
|
||||
<value>Cannot select account [{1}] for Uid [{0}], the account does not belong to current server</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingActionComplete" xml:space="preserve">
|
||||
<value>Operation completed</value>
|
||||
@@ -1656,7 +1638,7 @@
|
||||
<value>User [{0}] removed successfully</value>
|
||||
</data>
|
||||
<data name="ViewModelUserUpdated" xml:space="preserve">
|
||||
<value>Successfully updated Cookie for user [{0}]</value>
|
||||
<value>Successfully update Cookie for user [{0}]</value>
|
||||
</data>
|
||||
<data name="ViewModelViewDisposedOperationCancel" xml:space="preserve">
|
||||
<value>View Resource has been disposed, operation cancelled.</value>
|
||||
@@ -1842,7 +1824,7 @@
|
||||
<value>Refresh</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteRefreshTime" xml:space="preserve">
|
||||
<value>Refresh Interval</value>
|
||||
<value>Refresh Duration</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteReminderDescription" xml:space="preserve">
|
||||
<value>Causes the notification to stay on-screen and expanded until the user takes action</value>
|
||||
@@ -1938,7 +1920,7 @@
|
||||
<value>Buy or Renew Cloud Service</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloudDelete" xml:space="preserve">
|
||||
<value>Delete the Cloud Archive of This UID</value>
|
||||
<value>Delete the Cloud Archive of This Uid</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloudDeveloperHint" xml:space="preserve">
|
||||
<value>Lifetime developer license</value>
|
||||
@@ -1947,7 +1929,7 @@
|
||||
<value>Snap Hutao Cloud Service Expired</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloudRetrieve" xml:space="preserve">
|
||||
<value>Download the Cloud Archive of this UID</value>
|
||||
<value>Download the Cloud Archive of This Uid</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloudSpiralAbyssActivityDescription" xml:space="preserve">
|
||||
<value>Free 3-day license after uploading current schedule Abyss record</value>
|
||||
@@ -2010,7 +1992,7 @@
|
||||
<value>Overview</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
|
||||
<value>Global Wish Stats</value>
|
||||
<value>Statistics</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
|
||||
<value>Weapon</value>
|
||||
@@ -2205,10 +2187,10 @@
|
||||
<value>InterProcess</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>Run the software on the selected monitor</value>
|
||||
<value>Run the software on the selected display</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsHeader" xml:space="preserve">
|
||||
<value>Monitor</value>
|
||||
<value>Display</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMultipleInstancesDescription" xml:space="preserve">
|
||||
<value>Run multiple game processes simultaneously</value>
|
||||
@@ -2220,7 +2202,7 @@
|
||||
<value>Game Options</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGamePlayTimeDescription" xml:space="preserve">
|
||||
<value>Try to start Starward after the game is started for game duration statistics</value>
|
||||
<value>Try to start the game after the game is started and use Starward for game duration statistics</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGamePlayTimeHeader" xml:space="preserve">
|
||||
<value>Hours Played</value>
|
||||
@@ -2289,7 +2271,7 @@
|
||||
<value>Enabled</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRDescription" xml:space="preserve">
|
||||
<value>Take advantage of monitors that support HDR for brighter, more vivid, and more detailed pictures</value>
|
||||
<value>Take advantage of displays that support HDR for brighter, more vivid, and more detailed pictures</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameWindowsHDRHeader" xml:space="preserve">
|
||||
<value>Windows HDR</value>
|
||||
@@ -2333,9 +2315,6 @@
|
||||
<data name="ViewPageSettingCacheFolderHeader" xml:space="preserve">
|
||||
<value>Cache Folder</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
|
||||
<value>Hutao Passport</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
|
||||
<value>Copy</value>
|
||||
</data>
|
||||
@@ -2528,12 +2507,6 @@
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>Official Website</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>Customized background image, support bmp/gif/ico/jpg/jpeg/png/tiff/webp format</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>Open Background Folder</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>Reset</value>
|
||||
</data>
|
||||
@@ -2543,12 +2516,6 @@
|
||||
<data name="ViewPageSettingResetStaticResourceHeader" xml:space="preserve">
|
||||
<value>Reset Image Resource</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>启动游戏-解锁帧率限制</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
|
||||
<value>You need to move data in the directory manually, otherwise new user data will be created.</value>
|
||||
</data>
|
||||
@@ -2706,7 +2673,7 @@
|
||||
<value>Battles Fought</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssDamage" xml:space="preserve">
|
||||
<value>Most Damage Dealt</value>
|
||||
<value>Most Damage Taken</value>
|
||||
</data>
|
||||
<data name="ViewSpiralAbyssDefaultDescription" xml:space="preserve">
|
||||
<value>No Spiral Abyss record</value>
|
||||
@@ -2808,7 +2775,7 @@
|
||||
<value>Document</value>
|
||||
</data>
|
||||
<data name="ViewUserNoUserHint" xml:space="preserve">
|
||||
<value>Haven't logged in</value>
|
||||
<value>No Login</value>
|
||||
</data>
|
||||
<data name="ViewUserRefreshCookieTokenSuccess" xml:space="preserve">
|
||||
<value>Refresh CookieToken successfully</value>
|
||||
@@ -2832,7 +2799,7 @@
|
||||
<value>You can continue to use your computer without any impact</value>
|
||||
</data>
|
||||
<data name="ViewWelcomeSubtitle" xml:space="preserve">
|
||||
<value>Please do not close the software</value>
|
||||
<value>Please do not turn off the software</value>
|
||||
</data>
|
||||
<data name="ViewWelcomeTitle" xml:space="preserve">
|
||||
<value>Welcome to use Snap Hutao</value>
|
||||
@@ -2856,7 +2823,7 @@
|
||||
<value>(?:〓Event Duration〓|Event Wish Duration|【Availability Duration】).*?(\d\.\dAfter the Version update).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||
<value>〓Update Maintenance Duration〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
<value>〓Update Maintenance Duration.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||
</data>
|
||||
<data name="WebAnnouncementMatchVersionUpdateTitle" xml:space="preserve">
|
||||
<value>Version \d\.\d Update Details</value>
|
||||
@@ -2922,7 +2889,7 @@
|
||||
<value>Daily Commission Reward claimed</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteHomeCoinRecoveryFormat" xml:space="preserve">
|
||||
<value>Full in {0} {1:HH:mm}</value>
|
||||
<value>Will be full in {0} {1:HH:mm}</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteHomeLocked" xml:space="preserve">
|
||||
<value>Serenitea Pot not Unlocked</value>
|
||||
@@ -2982,7 +2949,7 @@
|
||||
<value>Wrong UID format</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode404" xml:space="preserve">
|
||||
<value>Character UID does not exist, please try again later</value>
|
||||
<value>Role UID does not exist, please try again later</value>
|
||||
</data>
|
||||
<data name="WebEnkaResponseStatusCode424" xml:space="preserve">
|
||||
<value>Game in maintenance</value>
|
||||
@@ -3059,4 +3026,4 @@
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>Monitor ID</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
||||
@@ -198,21 +198,6 @@
|
||||
<data name="LaunchGameTitle" xml:space="preserve">
|
||||
<value>Pilih akun untuk memulai</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
|
||||
<value><color=#1E90FF>王子</color>/<color=#FFB6C1>公主</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
|
||||
<value><color=#1E90FF>我</color>/<color=#FFB6C1>我</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameNickname" xml:space="preserve">
|
||||
<value>Pengembara</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
|
||||
<value><color=#1E90FF>他</color>/<color=#FFB6C1>她</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameRealNameId1" xml:space="preserve">
|
||||
<value>流浪者</value>
|
||||
</data>
|
||||
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
|
||||
<value>Perbaiki {0}</value>
|
||||
</data>
|
||||
@@ -512,9 +497,6 @@
|
||||
<data name="ModelMetadataTowerWaveTypeWave4" xml:space="preserve">
|
||||
<value>Gelombang 4: Gelombang akan muncul hanya setelah membunuh semua musuh di gelombang sebelumnya</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
|
||||
<value>维持场上 4 个盗宝团怪物,击杀后立即替换,总数 12 个</value>
|
||||
</data>
|
||||
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
|
||||
<value>Silakan perbarui data tampilan.</value>
|
||||
</data>
|
||||
@@ -2010,7 +1992,7 @@
|
||||
<value>Pratinjau</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
|
||||
<value>全球祈愿统计</value>
|
||||
<value>Statistik</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
|
||||
<value>Senjata</value>
|
||||
@@ -2238,7 +2220,7 @@
|
||||
<value>Pengunduhan Sumber Daya</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>Klien</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>Prapengunduhan</value>
|
||||
@@ -2333,9 +2315,6 @@
|
||||
<data name="ViewPageSettingCacheFolderHeader" xml:space="preserve">
|
||||
<value>Berkas Cache</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
|
||||
<value>胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
|
||||
<value>Salin</value>
|
||||
</data>
|
||||
@@ -2528,12 +2507,6 @@
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>Website Resmi</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>打开背景图片文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>Reset</value>
|
||||
</data>
|
||||
@@ -2543,12 +2516,6 @@
|
||||
<data name="ViewPageSettingResetStaticResourceHeader" xml:space="preserve">
|
||||
<value>Setel ulang Sumber Daya Gambar</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>启动游戏-解锁帧率限制</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
|
||||
<value>Anda perlu memindahkan data di direktori secara manual, jika tidak, data pengguna baru akan dibuat.</value>
|
||||
</data>
|
||||
|
||||
@@ -198,21 +198,6 @@
|
||||
<data name="LaunchGameTitle" xml:space="preserve">
|
||||
<value>アカウントを選択して開始します</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
|
||||
<value><color=#1E90FF>王子</color>/<color=#FFB6C1>公主</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
|
||||
<value><color=#1E90FF>我</color>/<color=#FFB6C1>我</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameNickname" xml:space="preserve">
|
||||
<value>旅人</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
|
||||
<value><color=#1E90FF>他</color>/<color=#FFB6C1>她</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameRealNameId1" xml:space="preserve">
|
||||
<value>放浪者</value>
|
||||
</data>
|
||||
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
|
||||
<value>精錬ランク {0}</value>
|
||||
</data>
|
||||
@@ -512,9 +497,6 @@
|
||||
<data name="ModelMetadataTowerWaveTypeWave4" xml:space="preserve">
|
||||
<value>ウェーブ 4: すべての敵を倒すと、次のウェーブの敵が出現する。</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
|
||||
<value>维持场上 4 个盗宝团怪物,击杀后立即替换,总数 12 个</value>
|
||||
</data>
|
||||
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
|
||||
<value>キャラクターラインナップを更新する</value>
|
||||
</data>
|
||||
@@ -2010,7 +1992,7 @@
|
||||
<value>一覧</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
|
||||
<value>全球祈愿统计</value>
|
||||
<value>統計</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
|
||||
<value>武器</value>
|
||||
@@ -2238,7 +2220,7 @@
|
||||
<value>リソースダウンロード</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>クライアント</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>事前ダウンロード</value>
|
||||
@@ -2333,9 +2315,6 @@
|
||||
<data name="ViewPageSettingCacheFolderHeader" xml:space="preserve">
|
||||
<value>キャッシュフォルダ</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
|
||||
<value>胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
|
||||
<value>コピー</value>
|
||||
</data>
|
||||
@@ -2528,12 +2507,6 @@
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>公式サイト</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>打开背景图片文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>リセット</value>
|
||||
</data>
|
||||
@@ -2543,12 +2516,6 @@
|
||||
<data name="ViewPageSettingResetStaticResourceHeader" xml:space="preserve">
|
||||
<value>画像リソースをリセット</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>启动游戏-解锁帧率限制</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
|
||||
<value>ディレクトリを変更した場合、フォルダ内のデータを手動で移動する必要があります。移動しない場合はユーザーデータが新規作成されます。</value>
|
||||
</data>
|
||||
|
||||
@@ -198,21 +198,6 @@
|
||||
<data name="LaunchGameTitle" xml:space="preserve">
|
||||
<value>계정 선택 및 시작</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
|
||||
<value><color=#1E90FF>王子</color>/<color=#FFB6C1>公主</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
|
||||
<value><color=#1E90FF>我</color>/<color=#FFB6C1>我</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameNickname" xml:space="preserve">
|
||||
<value>旅行者</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
|
||||
<value><color=#1E90FF>他</color>/<color=#FFB6C1>她</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameRealNameId1" xml:space="preserve">
|
||||
<value>流浪者</value>
|
||||
</data>
|
||||
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
|
||||
<value>재련 {0}</value>
|
||||
</data>
|
||||
@@ -268,13 +253,13 @@
|
||||
<value>제 {0} 호</value>
|
||||
</data>
|
||||
<data name="ModelInterchangeUIGFItemTypeAvatar" xml:space="preserve">
|
||||
<value>캐릭터</value>
|
||||
<value>角色</value>
|
||||
</data>
|
||||
<data name="ModelInterchangeUIGFItemTypeUnknown" xml:space="preserve">
|
||||
<value>未知</value>
|
||||
</data>
|
||||
<data name="ModelInterchangeUIGFItemTypeWeapon" xml:space="preserve">
|
||||
<value>무기</value>
|
||||
<value>武器</value>
|
||||
</data>
|
||||
<data name="ModelIntrinsicAssociationTypeFatui" xml:space="preserve">
|
||||
<value>우인단</value>
|
||||
@@ -322,7 +307,7 @@
|
||||
<value>여성</value>
|
||||
</data>
|
||||
<data name="ModelIntrinsicBodyTypeLoli" xml:space="preserve">
|
||||
<value>로리</value>
|
||||
<value>萝莉</value>
|
||||
</data>
|
||||
<data name="ModelIntrinsicBodyTypeMale" xml:space="preserve">
|
||||
<value>남성</value>
|
||||
@@ -512,9 +497,6 @@
|
||||
<data name="ModelMetadataTowerWaveTypeWave4" xml:space="preserve">
|
||||
<value>第四波:击败所有怪物,下一波才会出现</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
|
||||
<value>维持场上 4 个盗宝团怪物,击杀后立即替换,总数 12 个</value>
|
||||
</data>
|
||||
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
|
||||
<value>请更新角色橱窗数据</value>
|
||||
</data>
|
||||
@@ -1185,7 +1167,7 @@
|
||||
<value>탐색 파견 완료 알림</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteNotificationHomeCoinNotifyThreshold" xml:space="preserve">
|
||||
<value>原粹树脂提醒阈值</value>
|
||||
<value>洞天宝钱提醒阈值</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteNotificationResinNotifyThreshold" xml:space="preserve">
|
||||
<value>선계 화폐 한도 알림</value>
|
||||
@@ -1200,7 +1182,7 @@
|
||||
<value>매개 변수 변환기 알림</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteWebhookUrlInputPlaceholder" xml:space="preserve">
|
||||
<value>Url 입력</value>
|
||||
<value>请输入 Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url</value>
|
||||
@@ -1356,7 +1338,7 @@
|
||||
<value>用户使用协议与法律声明</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepDocument" xml:space="preserve">
|
||||
<value>문서</value>
|
||||
<value>文档</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepEnvironment" xml:space="preserve">
|
||||
<value>环境</value>
|
||||
@@ -1377,7 +1359,7 @@
|
||||
<value>下载并自行安装运行时</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepLanguage" xml:space="preserve">
|
||||
<value>언어</value>
|
||||
<value>语言</value>
|
||||
</data>
|
||||
<data name="ViewGuideStepStaticResource" xml:space="preserve">
|
||||
<value>资源</value>
|
||||
@@ -1560,7 +1542,7 @@
|
||||
<value>我已阅读并同意上方的条款</value>
|
||||
</data>
|
||||
<data name="ViewModelGuideActionComplete" xml:space="preserve">
|
||||
<value>완료</value>
|
||||
<value>完成</value>
|
||||
</data>
|
||||
<data name="ViewModelGuideActionNext" xml:space="preserve">
|
||||
<value>下一步</value>
|
||||
@@ -1866,7 +1848,7 @@
|
||||
<value>这些选项仅允许在非管理员模式下更改</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteSettingRefreshHeader" xml:space="preserve">
|
||||
<value>동기화</value>
|
||||
<value>刷新</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteSlientModeDescription" xml:space="preserve">
|
||||
<value>원신을 플레이 할 때 알리지 않음</value>
|
||||
@@ -1980,10 +1962,10 @@
|
||||
<value>가져오기</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogRefreshByManualInput" xml:space="preserve">
|
||||
<value>Url 수동 입력</value>
|
||||
<value>手动输入 Url</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogRefreshByManualInputDescription" xml:space="preserve">
|
||||
<value>제공된 Url을 이용해 기원 기록을 동기화합니다</value>
|
||||
<value>使用由你提供的 Url 刷新祈愿记录</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogRefreshBySToken" xml:space="preserve">
|
||||
<value>SToken 동기화</value>
|
||||
@@ -2010,7 +1992,7 @@
|
||||
<value>개요</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
|
||||
<value>全球祈愿统计</value>
|
||||
<value>统计</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
|
||||
<value>무기</value>
|
||||
@@ -2238,7 +2220,7 @@
|
||||
<value>리소스 다운로드</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>클라이언트</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>사전 다운로드</value>
|
||||
@@ -2333,9 +2315,6 @@
|
||||
<data name="ViewPageSettingCacheFolderHeader" xml:space="preserve">
|
||||
<value>缓存 文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
|
||||
<value>胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
|
||||
<value>복사</value>
|
||||
</data>
|
||||
@@ -2445,25 +2424,25 @@
|
||||
<value>主页卡片</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeCardItemAchievementHeader" xml:space="preserve">
|
||||
<value>업적 관리</value>
|
||||
<value>成就管理</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeCardItemDailyNoteHeader" xml:space="preserve">
|
||||
<value>실시간 메모</value>
|
||||
<value>实时便笺</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeCardItemgachaStatisticsHeader" xml:space="preserve">
|
||||
<value>기원 기록</value>
|
||||
<value>祈愿记录</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeCardItemLaunchGameHeader" xml:space="preserve">
|
||||
<value>게임 시작</value>
|
||||
<value>启动游戏</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHomeCardOff" xml:space="preserve">
|
||||
<value>숨김</value>
|
||||
<value>隐藏</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHomeCardOn" xml:space="preserve">
|
||||
<value>표시</value>
|
||||
<value>显示</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeHeader" xml:space="preserve">
|
||||
<value>홈페이지</value>
|
||||
<value>主页</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneDescription" xml:space="preserve">
|
||||
<value>三思而后行</value>
|
||||
@@ -2484,7 +2463,7 @@
|
||||
<value>已认证的合作开发者</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLoginAction" xml:space="preserve">
|
||||
<value>로그인</value>
|
||||
<value>登录</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLogoutAction" xml:space="preserve">
|
||||
<value>退出登录</value>
|
||||
@@ -2502,7 +2481,7 @@
|
||||
<value>使用兑换码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRegisterAction" xml:space="preserve">
|
||||
<value>회원가입</value>
|
||||
<value>注册</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportResetPasswordAction" xml:space="preserve">
|
||||
<value>修改密码</value>
|
||||
@@ -2528,12 +2507,6 @@
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>공식 홈페이지로 이동</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>打开背景图片文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>초기화</value>
|
||||
</data>
|
||||
@@ -2543,12 +2516,6 @@
|
||||
<data name="ViewPageSettingResetStaticResourceHeader" xml:space="preserve">
|
||||
<value>이미지 리소스 재설정</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>启动游戏-解锁帧率限制</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
|
||||
<value>경로를 변경한 후 경로 내의 데이터를 수동으로 이동해야 합니다. 그렇지 않으면 사용자 데이터가 재생성됩니다</value>
|
||||
</data>
|
||||
@@ -2661,7 +2628,7 @@
|
||||
<value>드롭 아이템</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiWeaponAfterAscensionTitle" xml:space="preserve">
|
||||
<value>돌파 후</value>
|
||||
<value>突破后</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiWeaponAutoSuggestBoxPlaceHolder" xml:space="preserve">
|
||||
<value>筛选武器</value>
|
||||
@@ -2805,7 +2772,7 @@
|
||||
<value>먼저 로그인하세요</value>
|
||||
</data>
|
||||
<data name="ViewUserDocumentationHeader" xml:space="preserve">
|
||||
<value>문서</value>
|
||||
<value>文档</value>
|
||||
</data>
|
||||
<data name="ViewUserNoUserHint" xml:space="preserve">
|
||||
<value>尚未登录</value>
|
||||
@@ -2874,7 +2841,7 @@
|
||||
<value>{0}시간 후 종료</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardFailed" xml:space="preserve">
|
||||
<value>클립보드를 열지 못했습니다</value>
|
||||
<value>打开剪贴板失败</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>已复制到剪贴板</value>
|
||||
@@ -2889,7 +2856,7 @@
|
||||
<value>进行中</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
|
||||
<value>완료됨</value>
|
||||
<value>已完成</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusForbid" xml:space="preserve">
|
||||
<value>禁止领取</value>
|
||||
|
||||
@@ -198,21 +198,6 @@
|
||||
<data name="LaunchGameTitle" xml:space="preserve">
|
||||
<value>Selecionar conta para iniciar</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
|
||||
<value><color=#1E90FF>王子</color>/<color=#FFB6C1>公主</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
|
||||
<value><color=#1E90FF>我</color>/<color=#FFB6C1>我</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameNickname" xml:space="preserve">
|
||||
<value>Viajante</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
|
||||
<value><color=#1E90FF>他</color>/<color=#FFB6C1>她</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameRealNameId1" xml:space="preserve">
|
||||
<value>流浪者</value>
|
||||
</data>
|
||||
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
|
||||
<value>Refinar {0}</value>
|
||||
</data>
|
||||
@@ -512,9 +497,6 @@
|
||||
<data name="ModelMetadataTowerWaveTypeWave4" xml:space="preserve">
|
||||
<value>Onda 4: Uma onda surgirá somente depois de matar todos os inimigos da onda anterior</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
|
||||
<value>维持场上 4 个盗宝团怪物,击杀后立即替换,总数 12 个</value>
|
||||
</data>
|
||||
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
|
||||
<value>Atualize os dados de exibição</value>
|
||||
</data>
|
||||
@@ -2010,7 +1992,7 @@
|
||||
<value>Visão geral</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
|
||||
<value>全球祈愿统计</value>
|
||||
<value>Estatísticas</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
|
||||
<value>Arma</value>
|
||||
@@ -2238,7 +2220,7 @@
|
||||
<value>Download de recurso</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>Cliente</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>Pré-download</value>
|
||||
@@ -2333,9 +2315,6 @@
|
||||
<data name="ViewPageSettingCacheFolderHeader" xml:space="preserve">
|
||||
<value>Pasta de cache</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
|
||||
<value>胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
|
||||
<value>Copiar</value>
|
||||
</data>
|
||||
@@ -2528,12 +2507,6 @@
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>Site oficial</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>打开背景图片文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>Resetar</value>
|
||||
</data>
|
||||
@@ -2543,12 +2516,6 @@
|
||||
<data name="ViewPageSettingResetStaticResourceHeader" xml:space="preserve">
|
||||
<value>Redefinir recurso de imagem</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>启动游戏-解锁帧率限制</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
|
||||
<value>É necessário mover os dados no diretório manualmente, caso contrário, serão criados novos dados de usuário.</value>
|
||||
</data>
|
||||
|
||||
@@ -1391,9 +1391,6 @@
|
||||
<data name="ViewLaunchGameHeader" xml:space="preserve">
|
||||
<value>启动游戏</value>
|
||||
</data>
|
||||
<data name="ViewListViewDragElevatedHint" xml:space="preserve">
|
||||
<value>管理员模式下无法拖动排序</value>
|
||||
</data>
|
||||
<data name="ViewModelAchievementArchiveAdded" xml:space="preserve">
|
||||
<value>存档 [{0}] 添加成功</value>
|
||||
</data>
|
||||
@@ -2336,9 +2333,6 @@
|
||||
<data name="ViewPageSettingCacheFolderHeader" xml:space="preserve">
|
||||
<value>缓存 文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
|
||||
<value>胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
|
||||
<value>复制</value>
|
||||
</data>
|
||||
@@ -2531,12 +2525,6 @@
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>前往官网</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>打开背景图片文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>重置</value>
|
||||
</data>
|
||||
@@ -2546,12 +2534,6 @@
|
||||
<data name="ViewPageSettingResetStaticResourceHeader" xml:space="preserve">
|
||||
<value>重置图片资源</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>启动游戏-解锁帧率限制</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
|
||||
<value>更改目录后需要手动移动目录内的数据,否则会重新创建用户数据</value>
|
||||
</data>
|
||||
|
||||
@@ -198,21 +198,6 @@
|
||||
<data name="LaunchGameTitle" xml:space="preserve">
|
||||
<value>Выберите учетную запись для запуска</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
|
||||
<value><color=#1E90FF>Принц</color>/<color=#FFB6C1>Принцесса</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
|
||||
<value><color=#1E90FF>I</color>/<color=#FFB6C1>I</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameNickname" xml:space="preserve">
|
||||
<value>Путешественник</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
|
||||
<value><color=#1E90FF>He</color>/<color=#FFB6C1>She</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameRealNameId1" xml:space="preserve">
|
||||
<value>Путник</value>
|
||||
</data>
|
||||
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
|
||||
<value>Уточнить {0}</value>
|
||||
</data>
|
||||
@@ -512,9 +497,6 @@
|
||||
<data name="ModelMetadataTowerWaveTypeWave4" xml:space="preserve">
|
||||
<value>Четвертая волна: Победите всех монстров, прежде чем появится следующая волна</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
|
||||
<value>Remain 4 Treasure Hoarders on the field, instantly respawn on death. 12 in total.</value>
|
||||
</data>
|
||||
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
|
||||
<value>Обновите данные витрины персонажей</value>
|
||||
</data>
|
||||
@@ -813,28 +795,28 @@
|
||||
<value>Текущая оригинальная смола:{0}</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTitle" xml:space="preserve">
|
||||
<value>Уведомление в реальном времени</value>
|
||||
<value>实时便笺提醒</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTransformer" xml:space="preserve">
|
||||
<value>Преобразователь</value>
|
||||
<value>参量质变仪</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTransformerAdaptiveHint" xml:space="preserve">
|
||||
<value>Готово</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTransformerHint" xml:space="preserve">
|
||||
<value>Преобразователь готов</value>
|
||||
<value>参量质变仪已准备完成</value>
|
||||
</data>
|
||||
<data name="ServiceDiscordGameActivityDetails" xml:space="preserve">
|
||||
<value>Исследование Тейвата</value>
|
||||
<value>正在提瓦特大陆中探索</value>
|
||||
</data>
|
||||
<data name="ServiceDiscordGameLaunchedBy" xml:space="preserve">
|
||||
<value>Started by {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogArchiveCollectionUserdataCorruptedMessage" xml:space="preserve">
|
||||
<value>Не удалось получить историю: {0}</value>
|
||||
<value>无法获取祈愿记录:{0}</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogEndIdUserdataCorruptedMessage" xml:space="preserve">
|
||||
<value>Unable to resolve wish history End Id</value>
|
||||
<value>无法获取祈愿记录 End Id</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
|
||||
<value>角色活动</value>
|
||||
@@ -855,19 +837,19 @@
|
||||
<value>数据包含异常物品, Id:{0}</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderAuthkeyRequestFailed" xml:space="preserve">
|
||||
<value>Не удалось получить ключ авторизации</value>
|
||||
<value>请求验证密钥失败</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderCachePathInvalid" xml:space="preserve">
|
||||
<value>Путь к Genshin Impact не установлен или содержит ошибки</value>
|
||||
<value>未正确提供原神路径,或当前设置的路径不正确</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderCachePathNotFound" xml:space="preserve">
|
||||
<value>找不到原神内置浏览器缓存路径:\n{0}</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderCacheUrlNotFound" xml:space="preserve">
|
||||
<value>Can't find available URL</value>
|
||||
<value>未找到可用的 Url</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderManualInputInvalid" xml:space="preserve">
|
||||
<value>Указан неверный Url-адрес</value>
|
||||
<value>提供的 Url 无效</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderStokenUnsupported" xml:space="preserve">
|
||||
<value>SToken refresh не поддерживает учетную запись HoYoLab</value>
|
||||
@@ -882,43 +864,43 @@
|
||||
<value>Язык файла UIGF: {0} не соответствует языку Snap Hutao: {1}, пожалуйста, повторите попытку после переключения языка клиента на целевой язык файла UIGF</value>
|
||||
</data>
|
||||
<data name="ServiceGameDetectGameAccountMultiMatched" xml:space="preserve">
|
||||
<value>Обнаружено несколько совпадающих учетных записей. Удалите повторяющиеся учетные записи</value>
|
||||
<value>存在多个匹配账号,请删除重复的账号</value>
|
||||
</data>
|
||||
<data name="ServiceGameEnsureGameResourceInsufficientDirectoryPermissions" xml:space="preserve">
|
||||
<value>Недостаточно разрешений файловой системы для запуска сервера</value>
|
||||
<value>文件系统权限不足,无法转换服务器</value>
|
||||
</data>
|
||||
<data name="ServiceGameEnsureGameResourceQueryResourceInformation" xml:space="preserve">
|
||||
<value>Скачать игровые ресурсы</value>
|
||||
<value>下载游戏资源索引</value>
|
||||
</data>
|
||||
<data name="ServiceGameFileOperationExceptionMessage" xml:space="preserve">
|
||||
<value>Не удалось выполнить операцию с файлом: {0}</value>
|
||||
<value>游戏文件操作失败:{0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameFpsUnlockFailed" xml:space="preserve">
|
||||
<value>Не удалось разблокировать ограничение FPS</value>
|
||||
<value>解锁帧率上限失败</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameIsRunning" xml:space="preserve">
|
||||
<value>Процесс игры запущен</value>
|
||||
<value>游戏进程运行中</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGamePathNotValid" xml:space="preserve">
|
||||
<value>Выберите игровой путь</value>
|
||||
<value>请选择游戏路径</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchExecutionGameResourceQueryIndexFailed" xml:space="preserve">
|
||||
<value>Не удалось загрузить игровые ресурсы: {0}</value>
|
||||
<value>下载游戏资源索引失败: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessExited" xml:space="preserve">
|
||||
<value>Процесс игры закрыт</value>
|
||||
<value>游戏进程已退出</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessInitializing" xml:space="preserve">
|
||||
<value>Инициализация игрового процесса</value>
|
||||
<value>正在初始化游戏进程</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseProcessStarted" xml:space="preserve">
|
||||
<value>Игра запускается...</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseUnlockFpsFailed" xml:space="preserve">
|
||||
<value>Не удалось разблокировать ограничение FPS, закрытие игрового процесса…</value>
|
||||
<value>解锁帧率上限失败,正在结束游戏进程</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseUnlockFpsSucceed" xml:space="preserve">
|
||||
<value>Ограничение FPS успешно разблокировано.</value>
|
||||
<value>解锁帧率上限成功</value>
|
||||
</data>
|
||||
<data name="ServiceGameLaunchPhaseUnlockingFps" xml:space="preserve">
|
||||
<value>正在尝试解锁帧率上限</value>
|
||||
@@ -1389,7 +1371,7 @@
|
||||
<value>有新的通知</value>
|
||||
</data>
|
||||
<data name="ViewLaunchGameHeader" xml:space="preserve">
|
||||
<value>Game Launcher</value>
|
||||
<value>启动游戏</value>
|
||||
</data>
|
||||
<data name="ViewModelAchievementArchiveAdded" xml:space="preserve">
|
||||
<value>存档 [{0}] 添加成功</value>
|
||||
@@ -1434,7 +1416,7 @@
|
||||
<value>获取数据中</value>
|
||||
</data>
|
||||
<data name="ViewModelAvatarPropertyOpenClipboardFail" xml:space="preserve">
|
||||
<value>Не удалось открыть буфер обмена</value>
|
||||
<value>打开剪贴板失败</value>
|
||||
</data>
|
||||
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
|
||||
<value>角色展柜尚未开启,请前往游戏操作后重试</value>
|
||||
@@ -1524,7 +1506,7 @@
|
||||
<value>导入数据中包含了不支持的物品</value>
|
||||
</data>
|
||||
<data name="ViewModelGachaLogImportWarningTitle" xml:space="preserve">
|
||||
<value>Ошибка загрузки</value>
|
||||
<value>导入失败</value>
|
||||
</data>
|
||||
<data name="ViewModelGachaLogPredictedPullLeftToOrange" xml:space="preserve">
|
||||
<value>{1:P3} 概率 {0} 抽后获得五星物品</value>
|
||||
@@ -1542,7 +1524,7 @@
|
||||
<value>该操作是不可逆的,该存档和其内的所有祈愿数据会丢失</value>
|
||||
</data>
|
||||
<data name="ViewModelGachaLogRemoveArchiveTitle" xml:space="preserve">
|
||||
<value>Вы уверены, что хотите удалить архив {0}?</value>
|
||||
<value>确定要删除存档 {0} 吗?</value>
|
||||
</data>
|
||||
<data name="ViewModelGachaLogRetrieveFromHutaoCloudProgress" xml:space="preserve">
|
||||
<value>从胡桃云服务同步祈愿记录</value>
|
||||
@@ -1959,7 +1941,7 @@
|
||||
<value>上传当前的祈愿存档</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogImportAction" xml:space="preserve">
|
||||
<value>Импорт</value>
|
||||
<value>导入</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogImportDescription" xml:space="preserve">
|
||||
<value>导入来自其它 App 的数据</value>
|
||||
@@ -1974,7 +1956,7 @@
|
||||
<value>从胡桃云恢复祈愿记录</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogRefresh" xml:space="preserve">
|
||||
<value>Обновить</value>
|
||||
<value>刷新</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogRefreshAction" xml:space="preserve">
|
||||
<value>获取</value>
|
||||
@@ -2001,7 +1983,7 @@
|
||||
<value>删除当前存档</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotAvatar" xml:space="preserve">
|
||||
<value>Персонаж</value>
|
||||
<value>角色</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotHistory" xml:space="preserve">
|
||||
<value>历史</value>
|
||||
@@ -2010,7 +1992,7 @@
|
||||
<value>总览</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
|
||||
<value>全球祈愿统计</value>
|
||||
<value>Статистика</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
|
||||
<value>Оружие</value>
|
||||
@@ -2130,10 +2112,10 @@
|
||||
<value>请输入验证码</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameAction" xml:space="preserve">
|
||||
<value>Game Launcher</value>
|
||||
<value>启动游戏</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameAdvanceHeader" xml:space="preserve">
|
||||
<value>启动高级功能</value>
|
||||
<value>高级功能</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameAppearanceAspectRatioDescription" xml:space="preserve">
|
||||
<value>快速切换到指定的分辨率</value>
|
||||
@@ -2238,7 +2220,7 @@
|
||||
<value>资源下载</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>客户端</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>预下载</value>
|
||||
@@ -2319,7 +2301,7 @@
|
||||
<value>设置呈现语言</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingApperanceLanguageHeader" xml:space="preserve">
|
||||
<value>Язык</value>
|
||||
<value>语言</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingBackdropMaterialDescription" xml:space="preserve">
|
||||
<value>更改窗体的背景材质</value>
|
||||
@@ -2333,9 +2315,6 @@
|
||||
<data name="ViewPageSettingCacheFolderHeader" xml:space="preserve">
|
||||
<value>缓存 文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
|
||||
<value>胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
|
||||
<value>复制</value>
|
||||
</data>
|
||||
@@ -2445,7 +2424,7 @@
|
||||
<value>主页卡片</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeCardItemAchievementHeader" xml:space="preserve">
|
||||
<value>Достижения</value>
|
||||
<value>成就管理</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeCardItemDailyNoteHeader" xml:space="preserve">
|
||||
<value>实时便笺</value>
|
||||
@@ -2454,7 +2433,7 @@
|
||||
<value>祈愿记录</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeCardItemLaunchGameHeader" xml:space="preserve">
|
||||
<value>Game Launcher</value>
|
||||
<value>启动游戏</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHomeCardOff" xml:space="preserve">
|
||||
<value>隐藏</value>
|
||||
@@ -2463,7 +2442,7 @@
|
||||
<value>显示</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeHeader" xml:space="preserve">
|
||||
<value>Главная</value>
|
||||
<value>主页</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneDescription" xml:space="preserve">
|
||||
<value>三思而后行</value>
|
||||
@@ -2484,7 +2463,7 @@
|
||||
<value>已认证的合作开发者</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLoginAction" xml:space="preserve">
|
||||
<value>Войти</value>
|
||||
<value>登录</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLogoutAction" xml:space="preserve">
|
||||
<value>退出登录</value>
|
||||
@@ -2502,7 +2481,7 @@
|
||||
<value>使用兑换码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRegisterAction" xml:space="preserve">
|
||||
<value>Регистрация</value>
|
||||
<value>注册</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportResetPasswordAction" xml:space="preserve">
|
||||
<value>修改密码</value>
|
||||
@@ -2520,7 +2499,7 @@
|
||||
<value>更改自动连点功能的快捷键</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingKeyShortcutAutoClickingHeader" xml:space="preserve">
|
||||
<value>Авто Клики</value>
|
||||
<value>自动连点</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingKeyShortcutHeader" xml:space="preserve">
|
||||
<value>快捷键</value>
|
||||
@@ -2528,12 +2507,6 @@
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>前往官网</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>打开背景图片文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>重置</value>
|
||||
</data>
|
||||
@@ -2543,12 +2516,6 @@
|
||||
<data name="ViewPageSettingResetStaticResourceHeader" xml:space="preserve">
|
||||
<value>重置图片资源</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>启动游戏-解锁帧率限制</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
|
||||
<value>更改目录后需要手动移动目录内的数据,否则会重新创建用户数据</value>
|
||||
</data>
|
||||
@@ -2661,7 +2628,7 @@
|
||||
<value>掉落物品</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiWeaponAfterAscensionTitle" xml:space="preserve">
|
||||
<value>After Ascension</value>
|
||||
<value>突破后</value>
|
||||
</data>
|
||||
<data name="ViewPageWiKiWeaponAutoSuggestBoxPlaceHolder" xml:space="preserve">
|
||||
<value>筛选武器</value>
|
||||
|
||||
@@ -198,21 +198,6 @@
|
||||
<data name="LaunchGameTitle" xml:space="preserve">
|
||||
<value>選擇帳號並啟動</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
|
||||
<value><color=#1E90FF>王子</color>/<color=#FFB6C1>公主</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
|
||||
<value><color=#1E90FF>我</color>/<color=#FFB6C1>我</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameNickname" xml:space="preserve">
|
||||
<value>旅人</value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNamePlayerAvatarSexProInfoPronounHeShe" xml:space="preserve">
|
||||
<value><color=#1E90FF>他</color>/<color=#FFB6C1>她</color></value>
|
||||
</data>
|
||||
<data name="MetadataSpecialNameRealNameId1" xml:space="preserve">
|
||||
<value>流浪者</value>
|
||||
</data>
|
||||
<data name="ModelBindingAvatarPropertyWeaponAffixFormat" xml:space="preserve">
|
||||
<value>精煉 {0}</value>
|
||||
</data>
|
||||
@@ -512,9 +497,6 @@
|
||||
<data name="ModelMetadataTowerWaveTypeWave4" xml:space="preserve">
|
||||
<value>第四波:擊敗所有怪物,下一波才會出現</value>
|
||||
</data>
|
||||
<data name="ModelMetadataTowerWaveTypeWave99999" xml:space="preserve">
|
||||
<value>维持场上 4 个盗宝团怪物,击杀后立即替换,总数 12 个</value>
|
||||
</data>
|
||||
<data name="ModelNameValueDefaultDescription" xml:space="preserve">
|
||||
<value>請更新角色櫥窗數據</value>
|
||||
</data>
|
||||
@@ -2010,7 +1992,7 @@
|
||||
<value>總覽</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotStatistics" xml:space="preserve">
|
||||
<value>全球祈愿统计</value>
|
||||
<value>統計</value>
|
||||
</data>
|
||||
<data name="ViewPageGahcaLogPivotWeapon" xml:space="preserve">
|
||||
<value>武器</value>
|
||||
@@ -2238,7 +2220,7 @@
|
||||
<value>資源下載</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
|
||||
<value>用戶端</value>
|
||||
<value>完整包</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||
<value>預下載</value>
|
||||
@@ -2333,9 +2315,6 @@
|
||||
<data name="ViewPageSettingCacheFolderHeader" xml:space="preserve">
|
||||
<value>暫存檔案夾</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCardHutaoPassportHeader" xml:space="preserve">
|
||||
<value>胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingCopyDeviceIdAction" xml:space="preserve">
|
||||
<value>複製</value>
|
||||
</data>
|
||||
@@ -2528,12 +2507,6 @@
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>前往官網</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderDescription" xml:space="preserve">
|
||||
<value>自定义背景图片,支持 bmp / gif / ico / jpg / jpeg / png / tiff / webp 格式</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOpenBackgroundImageFolderHeader" xml:space="preserve">
|
||||
<value>打开背景图片文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingResetAction" xml:space="preserve">
|
||||
<value>重設</value>
|
||||
</data>
|
||||
@@ -2543,12 +2516,6 @@
|
||||
<data name="ViewPageSettingResetStaticResourceHeader" xml:space="preserve">
|
||||
<value>重設圖片資源</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>在启动游戏页面的进程部分加入解锁帧率限制选项</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>启动游戏-解锁帧率限制</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingSetDataFolderDescription" xml:space="preserve">
|
||||
<value>更改目錄后需要手動移動目錄内的數據,否則會重新創建用戶數據</value>
|
||||
</data>
|
||||
|
||||
BIN
src/Snap.Hutao/Snap.Hutao/Resource/TestBackground.jpg
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Resource/TestBackground.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
@@ -1,21 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Snap.Hutao.Control.Media;
|
||||
using Snap.Hutao.Core;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Service.BackgroundImage;
|
||||
|
||||
internal sealed class BackgroundImage
|
||||
{
|
||||
public BitmapImage ImageSource { get; set; } = default!;
|
||||
|
||||
public Color AccentColor { get; set; }
|
||||
|
||||
public double Luminance { get; set; }
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Control.Media;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.Caching;
|
||||
using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Content;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.IO;
|
||||
using Windows.Graphics.Imaging;
|
||||
|
||||
namespace Snap.Hutao.Service.BackgroundImage;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IBackgroundImageService))]
|
||||
internal sealed partial class BackgroundImageService : IBackgroundImageService
|
||||
{
|
||||
private static readonly HashSet<string> AllowedFormats = [".bmp", ".gif", ".ico", ".jpg", ".jpeg", ".png", ".tiff", ".webp"];
|
||||
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private HashSet<string> backgroundPathSet;
|
||||
|
||||
public async ValueTask<ValueResult<bool, BackgroundImage>> GetNextBackgroundImageAsync(BackgroundImage? previous)
|
||||
{
|
||||
HashSet<string> backgroundSet = await SkipOrInitBackgroundAsync().ConfigureAwait(false);
|
||||
|
||||
if (backgroundSet.Count <= 0)
|
||||
{
|
||||
return new(false, default!);
|
||||
}
|
||||
|
||||
string path = System.Random.Shared.GetItems(backgroundSet.ToArray(), 1)[0];
|
||||
backgroundSet.Remove(path);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
if (string.Equals(path, previous?.ImageSource.UriSource.ToString(), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new(false, default!);
|
||||
}
|
||||
|
||||
using (FileStream fileStream = File.OpenRead(path))
|
||||
{
|
||||
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream.AsRandomAccessStream());
|
||||
SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight);
|
||||
Bgra32 accentColor = softwareBitmap.GetAccentColor();
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
BackgroundImage background = new()
|
||||
{
|
||||
ImageSource = new(path.ToUri()),
|
||||
AccentColor = accentColor,
|
||||
Luminance = accentColor.Luminance,
|
||||
};
|
||||
|
||||
return new(true, background);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<HashSet<string>> SkipOrInitBackgroundAsync()
|
||||
{
|
||||
if (backgroundPathSet is null || backgroundPathSet.Count <= 0)
|
||||
{
|
||||
string backgroundFolder = runtimeOptions.GetDataFolderBackgroundFolder();
|
||||
Directory.CreateDirectory(backgroundFolder);
|
||||
backgroundPathSet = Directory
|
||||
.GetFiles(backgroundFolder, "*.*", SearchOption.AllDirectories)
|
||||
.Where(path => AllowedFormats.Contains(Path.GetExtension(path)))
|
||||
.ToHashSet();
|
||||
|
||||
// No image found
|
||||
if (backgroundPathSet.Count <= 0)
|
||||
{
|
||||
ResourceClient resourceClient = serviceProvider.GetRequiredService<ResourceClient>();
|
||||
string launguageCode = serviceProvider.GetRequiredService<CultureOptions>().LanguageCode;
|
||||
LaunchScheme scheme = launguageCode is "zh-cn"
|
||||
? KnownLaunchSchemes.Get().First(scheme => !scheme.IsOversea && scheme.IsNotCompatOnly)
|
||||
: KnownLaunchSchemes.Get().First(scheme => scheme.IsOversea && scheme.IsNotCompatOnly);
|
||||
Response<GameContent> response = await resourceClient.GetContentAsync(scheme, launguageCode).ConfigureAwait(false);
|
||||
if (response is { Data.Advertisement.Background: string url })
|
||||
{
|
||||
ValueFile file = await serviceProvider.GetRequiredService<IImageCache>().GetFileFromCacheAsync(url.ToUri()).ConfigureAwait(false);
|
||||
backgroundPathSet = [file];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return backgroundPathSet;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.BackgroundImage;
|
||||
|
||||
internal interface IBackgroundImageService
|
||||
{
|
||||
ValueTask<ValueResult<bool, BackgroundImage>> GetNextBackgroundImageAsync(BackgroundImage? previous);
|
||||
}
|
||||
@@ -27,15 +27,9 @@ internal sealed partial class DailyNoteNotificationOperation
|
||||
private readonly IGameServiceFacade gameService;
|
||||
private readonly BindingClient bindingClient;
|
||||
private readonly DailyNoteOptions options;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
|
||||
public async ValueTask SendAsync(DailyNoteEntry entry)
|
||||
{
|
||||
if (!runtimeOptions.IsToastAvailable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.DailyNote is null)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -132,6 +132,13 @@ internal static class DiscordController
|
||||
return;
|
||||
}
|
||||
|
||||
// Actually requires a discord client to be running on Windows platform.
|
||||
// If not, the following creation code will throw.
|
||||
if (System.Diagnostics.Process.GetProcessesByName("Discord").Length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
DiscordCreateParams @params = default;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
|
||||
namespace Snap.Hutao.Service.Discord;
|
||||
|
||||
@@ -10,77 +9,22 @@ namespace Snap.Hutao.Service.Discord;
|
||||
[Injection(InjectAs.Singleton, typeof(IDiscordService))]
|
||||
internal sealed partial class DiscordService : IDiscordService, IDisposable
|
||||
{
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
|
||||
private bool isInitialized;
|
||||
|
||||
public async ValueTask SetPlayingActivityAsync(bool isOversea)
|
||||
{
|
||||
if (CheckDiscordStatus())
|
||||
{
|
||||
_ = isOversea
|
||||
? await DiscordController.SetPlayingGenshinImpactAsync().ConfigureAwait(false)
|
||||
: await DiscordController.SetPlayingYuanShenAsync().ConfigureAwait(false);
|
||||
}
|
||||
_ = isOversea
|
||||
? await DiscordController.SetPlayingGenshinImpactAsync().ConfigureAwait(false)
|
||||
: await DiscordController.SetPlayingYuanShenAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async ValueTask SetNormalActivityAsync()
|
||||
{
|
||||
if (CheckDiscordStatus())
|
||||
{
|
||||
_ = await DiscordController.SetDefaultActivityAsync(runtimeOptions.AppLaunchTime).ConfigureAwait(false);
|
||||
}
|
||||
_ = await DiscordController.SetDefaultActivityAsync(runtimeOptions.AppLaunchTime).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DiscordController.Stop();
|
||||
}
|
||||
|
||||
private bool CheckDiscordStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Actually requires a discord client to be running on Windows platform.
|
||||
// If not, discord core creation code will throw.
|
||||
System.Diagnostics.Process[] discordProcesses = System.Diagnostics.Process.GetProcessesByName("Discord");
|
||||
|
||||
if (discordProcesses.Length <= 0)
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
infoBarService.Warning("Discord 未运行,将无法设置 Discord Activity 状态。");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (System.Diagnostics.Process process in discordProcesses)
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = process.Handle;
|
||||
}
|
||||
catch (Win32Exception)
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
infoBarService.Warning("权限不足,将无法设置 Discord Activity 状态。");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
isInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Factory.ContentDialog;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
@@ -19,9 +18,9 @@ internal sealed partial class GameAccountService : IGameAccountService
|
||||
private readonly IGameDbService gameDbService;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private ObservableReorderableDbCollection<GameAccount>? gameAccounts;
|
||||
private ObservableCollection<GameAccount>? gameAccounts;
|
||||
|
||||
public ObservableReorderableDbCollection<GameAccount> GameAccountCollection
|
||||
public ObservableCollection<GameAccount> GameAccountCollection
|
||||
{
|
||||
get => gameAccounts ??= gameDbService.GetGameAccountCollection();
|
||||
}
|
||||
@@ -121,6 +120,11 @@ internal sealed partial class GameAccountService : IGameAccountService
|
||||
await gameDbService.RemoveGameAccountByIdAsync(gameAccount.InnerId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void ReorderGameAccounts(IEnumerable<GameAccount> reorderedGameAccounts)
|
||||
{
|
||||
gameDbService.ReorderGameAccounts(reorderedGameAccounts);
|
||||
}
|
||||
|
||||
private static GameAccount? SingleGameAccountOrDefault(ObservableCollection<GameAccount> gameAccounts, string registrySdk)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Account;
|
||||
|
||||
internal interface IGameAccountService
|
||||
{
|
||||
ObservableReorderableDbCollection<GameAccount> GameAccountCollection { get; }
|
||||
ObservableCollection<GameAccount> GameAccountCollection { get; }
|
||||
|
||||
void AttachGameAccountToUid(GameAccount gameAccount, string uid);
|
||||
|
||||
@@ -22,4 +22,6 @@ internal interface IGameAccountService
|
||||
ValueTask RemoveGameAccountAsync(GameAccount gameAccount);
|
||||
|
||||
bool SetGameAccount(GameAccount account);
|
||||
|
||||
void ReorderGameAccounts(IEnumerable<GameAccount> reorderedGameAccounts);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
|
||||
@@ -14,12 +15,12 @@ internal sealed partial class GameDbService : IGameDbService
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public ObservableReorderableDbCollection<GameAccount> GetGameAccountCollection()
|
||||
public ObservableCollection<GameAccount> GetGameAccountCollection()
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
return appDbContext.GameAccounts.AsNoTracking().ToObservableReorderableDbCollection(serviceProvider);
|
||||
return appDbContext.GameAccounts.AsNoTracking().ToObservableCollection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,4 +59,19 @@ internal sealed partial class GameDbService : IGameDbService
|
||||
await appDbContext.GameAccounts.ExecuteDeleteWhereAsync(a => a.InnerId == id).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReorderGameAccounts(IEnumerable<GameAccount> reorderedGameAccounts)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
appDbContext.GameAccounts.ExecuteDelete();
|
||||
|
||||
// Use foreach to avoid ORDER BY InnerId
|
||||
foreach (GameAccount gameAccount in reorderedGameAccounts)
|
||||
{
|
||||
appDbContext.GameAccounts.AddAndSave(gameAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using Snap.Hutao.Service.Game.Account;
|
||||
using Snap.Hutao.Service.Game.Configuration;
|
||||
using Snap.Hutao.Service.Game.Launching.Handler;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
|
||||
@@ -24,7 +24,7 @@ internal sealed partial class GameServiceFacade : IGameServiceFacade
|
||||
private readonly IGamePathService gamePathService;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ObservableReorderableDbCollection<GameAccount> GameAccountCollection
|
||||
public ObservableCollection<GameAccount> GameAccountCollection
|
||||
{
|
||||
get => gameAccountService.GameAccountCollection;
|
||||
}
|
||||
@@ -76,4 +76,9 @@ internal sealed partial class GameServiceFacade : IGameServiceFacade
|
||||
{
|
||||
return LaunchExecutionEnsureGameNotRunningHandler.IsGameRunning(out _);
|
||||
}
|
||||
|
||||
public void ReorderGameAccounts(IEnumerable<GameAccount> reorderedGameAccounts)
|
||||
{
|
||||
gameAccountService.ReorderGameAccounts(reorderedGameAccounts);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
|
||||
@@ -12,9 +12,11 @@ internal interface IGameDbService
|
||||
|
||||
ValueTask RemoveGameAccountByIdAsync(Guid id);
|
||||
|
||||
ObservableReorderableDbCollection<GameAccount> GetGameAccountCollection();
|
||||
ObservableCollection<GameAccount> GetGameAccountCollection();
|
||||
|
||||
void UpdateGameAccount(GameAccount gameAccount);
|
||||
|
||||
ValueTask UpdateGameAccountAsync(GameAccount gameAccount);
|
||||
|
||||
void ReorderGameAccounts(IEnumerable<GameAccount> reorderedGameAccounts);
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using Snap.Hutao.Service.Game.Configuration;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
|
||||
@@ -17,7 +17,7 @@ internal interface IGameServiceFacade
|
||||
/// <summary>
|
||||
/// 游戏内账号集合
|
||||
/// </summary>
|
||||
ObservableReorderableDbCollection<GameAccount> GameAccountCollection { get; }
|
||||
ObservableCollection<GameAccount> GameAccountCollection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 将账号绑定到对应的Uid
|
||||
@@ -62,4 +62,6 @@ internal interface IGameServiceFacade
|
||||
ValueTask RemoveGameAccountAsync(GameAccount gameAccount);
|
||||
|
||||
GameAccount? DetectCurrentGameAccount(SchemeType scheme);
|
||||
|
||||
void ReorderGameAccounts(IEnumerable<GameAccount> reorderedGameAccounts);
|
||||
}
|
||||
@@ -207,7 +207,6 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
|
||||
public List<AspectRatio> AspectRatios { get; } =
|
||||
[
|
||||
new(3840, 2160),
|
||||
new(2560, 1600),
|
||||
new(2560, 1440),
|
||||
new(2410, 1080),
|
||||
@@ -219,7 +218,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
get => selectedAspectRatio;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref selectedAspectRatio, value) && value is { } aspectRatio)
|
||||
if (SetProperty(ref selectedAspectRatio, value) && value is AspectRatio aspectRatio)
|
||||
{
|
||||
(ScreenWidth, ScreenHeight) = ((int)aspectRatio.Width, (int)aspectRatio.Height);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Service.Game.Package;
|
||||
using Snap.Hutao.View.Dialog;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.IO;
|
||||
|
||||
@@ -131,6 +130,13 @@ internal sealed class LaunchExecutionEnsureGameResourceHandler : ILaunchExecutio
|
||||
return true;
|
||||
}
|
||||
|
||||
// Program Files has special permissions limitation.
|
||||
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
if (folder.StartsWith(programFiles, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string tempFilePath = Path.Combine(folder, $"{Guid.NewGuid():N}.tmp");
|
||||
|
||||
@@ -8,7 +8,7 @@ using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Core.IO.Hashing;
|
||||
using Snap.Hutao.Core.IO.Http.Sharding;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using System.Collections.ObjectModel;
|
||||
using BindingUser = Snap.Hutao.ViewModel.User.User;
|
||||
using EntityUser = Snap.Hutao.Model.Entity.User;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
@@ -16,7 +14,7 @@ internal interface IUserCollectionService
|
||||
|
||||
ValueTask<ObservableCollection<UserAndUid>> GetUserAndUidCollectionAsync();
|
||||
|
||||
ValueTask<ObservableReorderableDbCollection<BindingUser, EntityUser>> GetUserCollectionAsync();
|
||||
ValueTask<ObservableCollection<BindingUser>> GetUserCollectionAsync();
|
||||
|
||||
UserGameRole? GetUserGameRoleByUid(string uid);
|
||||
|
||||
|
||||
@@ -14,6 +14,4 @@ internal interface IUserDbService
|
||||
ValueTask<List<Model.Entity.User>> GetUserListAsync();
|
||||
|
||||
ValueTask UpdateUserAsync(Model.Entity.User user);
|
||||
|
||||
ValueTask ClearUserSelectionAsync();
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using System.Collections.ObjectModel;
|
||||
using BindingUser = Snap.Hutao.ViewModel.User.User;
|
||||
using EntityUser = Snap.Hutao.Model.Entity.User;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
@@ -34,7 +32,7 @@ internal interface IUserService
|
||||
/// 此操作不能取消
|
||||
/// </summary>
|
||||
/// <returns>准备完成的用户信息集合</returns>
|
||||
ValueTask<ObservableReorderableDbCollection<BindingUser, EntityUser>> GetUserCollectionAsync();
|
||||
ValueTask<ObservableCollection<BindingUser>> GetUserCollectionAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 获取角色信息
|
||||
|
||||
@@ -9,7 +9,6 @@ using Snap.Hutao.ViewModel.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using System.Collections.ObjectModel;
|
||||
using BindingUser = Snap.Hutao.ViewModel.User.User;
|
||||
using EntityUser = Snap.Hutao.Model.Entity.User;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
@@ -19,14 +18,13 @@ internal sealed partial class UserCollectionService : IUserCollectionService, ID
|
||||
{
|
||||
private readonly ScopedDbCurrent<BindingUser, Model.Entity.User, UserChangedMessage> dbCurrent;
|
||||
private readonly IUserInitializationService userInitializationService;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IUserDbService userDbService;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly IMessenger messenger;
|
||||
|
||||
private readonly SemaphoreSlim throttler = new(1);
|
||||
|
||||
private ObservableReorderableDbCollection<BindingUser, EntityUser>? userCollection;
|
||||
private ObservableCollection<BindingUser>? userCollection;
|
||||
private Dictionary<string, BindingUser>? midUserMap;
|
||||
|
||||
private ObservableCollection<UserAndUid>? userAndUidCollection;
|
||||
@@ -38,15 +36,15 @@ internal sealed partial class UserCollectionService : IUserCollectionService, ID
|
||||
set => dbCurrent.Current = value;
|
||||
}
|
||||
|
||||
public async ValueTask<ObservableReorderableDbCollection<BindingUser, EntityUser>> GetUserCollectionAsync()
|
||||
public async ValueTask<ObservableCollection<BindingUser>> GetUserCollectionAsync()
|
||||
{
|
||||
// Force run in background thread, otherwise will cause reentrance
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
||||
using (await throttler.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
if (userCollection is null)
|
||||
{
|
||||
List<EntityUser> entities = await userDbService.GetUserListAsync().ConfigureAwait(false);
|
||||
List<Model.Entity.User> entities = await userDbService.GetUserListAsync().ConfigureAwait(false);
|
||||
List<BindingUser> users = await entities.SelectListAsync(userInitializationService.ResumeUserAsync).ConfigureAwait(false);
|
||||
|
||||
midUserMap = [];
|
||||
@@ -64,20 +62,15 @@ internal sealed partial class UserCollectionService : IUserCollectionService, ID
|
||||
}
|
||||
}
|
||||
|
||||
userCollection = users.ToObservableReorderableDbCollection<BindingUser, EntityUser>(serviceProvider);
|
||||
userCollection = users.ToObservableCollection();
|
||||
|
||||
try
|
||||
{
|
||||
CurrentUser = users.SelectedOrDefault();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
foreach (BindingUser user in users)
|
||||
{
|
||||
user.IsSelected = false;
|
||||
}
|
||||
|
||||
await userDbService.ClearUserSelectionAsync().ConfigureAwait(false);
|
||||
ThrowHelper.UserdataCorrupted(SH.ServiceUserCurrentMultiMatched, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,13 +57,4 @@ internal sealed partial class UserDbService : IUserDbService
|
||||
await appDbContext.Users.ExecuteDeleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask ClearUserSelectionAsync()
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
await appDbContext.Users.ExecuteUpdateAsync(update => update.SetProperty(user => user.IsSelected, user => false)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Core.DependencyInjection.Abstraction;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
@@ -10,7 +9,6 @@ using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Collections.ObjectModel;
|
||||
using BindingUser = Snap.Hutao.ViewModel.User.User;
|
||||
using EntityUser = Snap.Hutao.Model.Entity.User;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
@@ -43,7 +41,7 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
|
||||
await userDbService.RemoveUsersAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public ValueTask<ObservableReorderableDbCollection<BindingUser, EntityUser>> GetUserCollectionAsync()
|
||||
public ValueTask<ObservableCollection<BindingUser>> GetUserCollectionAsync()
|
||||
{
|
||||
return userCollectionService.GetUserCollectionAsync();
|
||||
}
|
||||
|
||||
@@ -103,7 +103,6 @@
|
||||
<None Remove="Control\Theme\PivotOverride.xaml" />
|
||||
<None Remove="Control\Theme\ScrollViewer.xaml" />
|
||||
<None Remove="Control\Theme\SettingsStyle.xaml" />
|
||||
<None Remove="Control\Theme\Thickness.xaml" />
|
||||
<None Remove="Control\Theme\TransitionCollection.xaml" />
|
||||
<None Remove="Control\Theme\Uri.xaml" />
|
||||
<None Remove="Control\Theme\WindowOverride.xaml" />
|
||||
@@ -137,6 +136,7 @@
|
||||
<None Remove="Resource\Navigation\WikiMonster.png" />
|
||||
<None Remove="Resource\Navigation\WikiWeapon.png" />
|
||||
<None Remove="Resource\Segoe Fluent Icons.ttf" />
|
||||
<None Remove="Resource\TestBackground.jpg" />
|
||||
<None Remove="Resource\WelcomeView_Background.png" />
|
||||
<None Remove="stylecop.json" />
|
||||
<None Remove="View\Card\AchievementCard.xaml" />
|
||||
@@ -280,6 +280,7 @@
|
||||
<Content Include="Resource\Navigation\WikiAvatar.png" />
|
||||
<Content Include="Resource\Navigation\WikiMonster.png" />
|
||||
<Content Include="Resource\Navigation\WikiWeapon.png" />
|
||||
<Content Include="Resource\TestBackground.jpg" />
|
||||
<Content Include="Resource\WelcomeView_Background.png" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -319,7 +320,7 @@
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231219000" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
|
||||
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.15.3">
|
||||
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.14.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -347,11 +348,6 @@
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Control\Theme\Thickness.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Page\FeedbackPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -9,12 +9,10 @@
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
xmlns:shvg="using:Snap.Hutao.ViewModel.Game"
|
||||
MinHeight="180"
|
||||
Padding="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
d:DataContext="{d:DesignInstance shvg:LaunchGameViewModelSlim}"
|
||||
Background="Transparent"
|
||||
Command="{Binding LaunchCommand}"
|
||||
@@ -24,6 +22,12 @@
|
||||
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
|
||||
</mxi:Interaction.Behaviors>
|
||||
<Grid CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||
<Image
|
||||
Margin="0,40,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
Opacity="0.4"
|
||||
Source="ms-appx:///Resource/HutaoIconSourceTransparentBackgroundGradient1.png"
|
||||
Stretch="Uniform"/>
|
||||
<Grid Margin="12" ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.Web.WebView2.Core;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Windows.Foundation;
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
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"
|
||||
xmlns:shwhshlr="using:Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource"
|
||||
xmlns:shwhshl="using:Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
d:DataContext="{d:DesignInstance shwhshlr:Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource.Package}"
|
||||
d:DataContext="{d:DesignInstance shwhshl:Package}"
|
||||
IsExpanded="True"
|
||||
ItemsSource="{Binding VoicePacks}"
|
||||
mc:Ignorable="d">
|
||||
@@ -24,7 +24,7 @@
|
||||
</cwc:SettingsExpander.Resources>
|
||||
|
||||
<cwc:SettingsExpander.ItemTemplate>
|
||||
<DataTemplate x:DataType="shwhshlr:VoicePackage">
|
||||
<DataTemplate x:DataType="shwhshl:VoicePackage">
|
||||
<cwc:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
ActionIcon="{shcm:FontIcon Glyph={StaticResource FontIconContentCopy}}"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
x:Class="Snap.Hutao.View.InfoBarView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cw="using:CommunityToolkit.WinUI"
|
||||
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"
|
||||
|
||||
@@ -12,22 +12,20 @@
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<Thickness x:Key="NavigationViewContentMargin">0,44,0,0</Thickness>
|
||||
<Thickness x:Key="NavigationViewContentGridBorderThickness">0,1,0,0</Thickness>
|
||||
<x:Double x:Key="NavigationViewItemOnLeftIconBoxHeight">24</x:Double>
|
||||
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground" Color="Transparent"/>
|
||||
<Thickness x:Key="NavigationViewContentGridBorderThickness">0,1,0,0</Thickness>
|
||||
</UserControl.Resources>
|
||||
<Grid Background="{ThemeResource SolidBackgroundFillColorBaseBrush}" Transitions="{ThemeResource EntranceThemeTransitions}">
|
||||
<Grid Transitions="{ThemeResource EntranceThemeTransitions}">
|
||||
<Image
|
||||
x:Name="BackdroundImagePresenter"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Opacity="0"
|
||||
Opacity="0.45"
|
||||
Source="ms-appx:///Resource/TestBackground.jpg"
|
||||
Stretch="UniformToFill"/>
|
||||
|
||||
<NavigationView
|
||||
x:Name="NavView"
|
||||
Margin="-1,0,0,-1"
|
||||
shch:NavigationViewHelper.PaneCornerRadius="0"
|
||||
shch:NavigationViewHelper.PaneCornerRadius="8,0,0,8"
|
||||
CompactPaneLength="48"
|
||||
IsBackEnabled="{x:Bind ContentFrame.CanGoBack, Mode=OneWay}"
|
||||
IsPaneOpen="True"
|
||||
@@ -111,4 +109,4 @@
|
||||
|
||||
<shv:InfoBarView/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Animations;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
using Snap.Hutao.Service.BackgroundImage;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.View.Page;
|
||||
|
||||
@@ -19,10 +14,6 @@ namespace Snap.Hutao.View;
|
||||
internal sealed partial class MainView : UserControl
|
||||
{
|
||||
private readonly INavigationService navigationService;
|
||||
private readonly IBackgroundImageService backgroundImageService;
|
||||
private TaskCompletionSource acutalThemeChangedTaskCompletionSource = new();
|
||||
private CancellationTokenSource periodicTimerCancellationTokenSource = new();
|
||||
private BackgroundImage? previousBackgroundImage;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的主视图
|
||||
@@ -31,13 +22,8 @@ internal sealed partial class MainView : UserControl
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
ActualThemeChanged += OnActualThemeChanged;
|
||||
|
||||
IServiceProvider serviceProvider = Ioc.Default;
|
||||
|
||||
backgroundImageService = serviceProvider.GetRequiredService<IBackgroundImageService>();
|
||||
RunBackgroundImageLoopAsync(serviceProvider.GetRequiredService<ITaskContext>()).SafeForget();
|
||||
|
||||
navigationService = serviceProvider.GetRequiredService<INavigationService>();
|
||||
navigationService
|
||||
.As<INavigationInitialization>()?
|
||||
@@ -45,54 +31,4 @@ internal sealed partial class MainView : UserControl
|
||||
|
||||
navigationService.Navigate<AnnouncementPage>(INavigationAwaiter.Default, true);
|
||||
}
|
||||
|
||||
private async ValueTask RunBackgroundImageLoopAsync(ITaskContext taskContext)
|
||||
{
|
||||
using (PeriodicTimer timer = new(TimeSpan.FromMinutes(5)))
|
||||
{
|
||||
do
|
||||
{
|
||||
(bool isOk, BackgroundImage backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync(previousBackgroundImage).ConfigureAwait(false);
|
||||
|
||||
if (isOk)
|
||||
{
|
||||
previousBackgroundImage = backgroundImage;
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
await AnimationBuilder
|
||||
.Create()
|
||||
.Opacity(to: 0D, duration: TimeSpan.FromMilliseconds(1000), easingType: EasingType.Sine, easingMode: EasingMode.EaseIn)
|
||||
.StartAsync(BackdroundImagePresenter)
|
||||
.ConfigureAwait(true);
|
||||
|
||||
BackdroundImagePresenter.Source = backgroundImage.ImageSource;
|
||||
double targetOpacity = ThemeHelper.IsDarkMode(ActualTheme) ? 1 - backgroundImage.Luminance : backgroundImage.Luminance;
|
||||
|
||||
await AnimationBuilder
|
||||
.Create()
|
||||
.Opacity(to: targetOpacity, duration: TimeSpan.FromMilliseconds(1000), easingType: EasingType.Sine, easingMode: EasingMode.EaseOut)
|
||||
.StartAsync(BackdroundImagePresenter)
|
||||
.ConfigureAwait(true);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(timer.WaitForNextTickAsync(periodicTimerCancellationTokenSource.Token).AsTask(), acutalThemeChangedTaskCompletionSource.Task).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
acutalThemeChangedTaskCompletionSource = new();
|
||||
periodicTimerCancellationTokenSource = new();
|
||||
}
|
||||
while (true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnActualThemeChanged(FrameworkElement frameworkElement, object args)
|
||||
{
|
||||
acutalThemeChangedTaskCompletionSource.TrySetResult();
|
||||
periodicTimerCancellationTokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
@@ -197,108 +197,104 @@
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="16" Style="{ThemeResource AcrylicBaseHighGridCardStyle}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource CompatGridLength2}"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Margin="16" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Grid Style="{ThemeResource AcrylicSecondaryGridCardStyle}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource CompatGridLength2}"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<shcp:PanelSelector
|
||||
x:Name="ItemsPanelSelector"
|
||||
Margin="8,0,0,0"
|
||||
LocalSettingKeySuffixForCurrent="AchievementPage.AchievementGoals"/>
|
||||
<Viewbox
|
||||
Height="32"
|
||||
MaxWidth="192"
|
||||
Margin="8,0,0,0"
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<shcp:PanelSelector
|
||||
x:Name="ItemsPanelSelector"
|
||||
Margin="8,0,0,0"
|
||||
LocalSettingKeySuffixForCurrent="AchievementPage.AchievementGoals"/>
|
||||
<Viewbox
|
||||
Height="32"
|
||||
MaxWidth="192"
|
||||
Margin="8,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Stretch="Uniform"
|
||||
StretchDirection="Both">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Stretch="Uniform"
|
||||
StretchDirection="Both">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding FinishDescription}"/>
|
||||
</Viewbox>
|
||||
</StackPanel>
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding FinishDescription}"/>
|
||||
</Viewbox>
|
||||
</StackPanel>
|
||||
|
||||
<CommandBar
|
||||
Grid.Column="1"
|
||||
Margin="16,0,0,0"
|
||||
DefaultLabelPosition="Right">
|
||||
<CommandBar.Resources>
|
||||
<StaticResource x:Key="CommandBarBackgroundOpen" ResourceKey="ControlFillColorTransparentBrush"/>
|
||||
<Thickness x:Key="CommandBarBorderThicknessOpen">0</Thickness>
|
||||
</CommandBar.Resources>
|
||||
<CommandBar
|
||||
Grid.Column="1"
|
||||
Margin="16,0,0,0"
|
||||
DefaultLabelPosition="Right">
|
||||
<CommandBar.Resources>
|
||||
<StaticResource x:Key="CommandBarBackgroundOpen" ResourceKey="ControlFillColorTransparentBrush"/>
|
||||
<Thickness x:Key="CommandBarBorderThicknessOpen">0</Thickness>
|
||||
</CommandBar.Resources>
|
||||
|
||||
<CommandBar.Content>
|
||||
<AutoSuggestBox
|
||||
Width="300"
|
||||
Height="36"
|
||||
Margin="3,6,6,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
PlaceholderText="{shcm:ResourceString Name=ViewPageAchievementSearchPlaceholder}"
|
||||
QueryIcon="{shcm:FontIcon Glyph=}"
|
||||
Text="{Binding SearchText, Mode=TwoWay}">
|
||||
<mxi:Interaction.Behaviors>
|
||||
<mxic:EventTriggerBehavior EventName="QuerySubmitted">
|
||||
<mxic:InvokeCommandAction Command="{Binding SearchAchievementCommand}" CommandParameter="{Binding SearchText}"/>
|
||||
</mxic:EventTriggerBehavior>
|
||||
</mxi:Interaction.Behaviors>
|
||||
</AutoSuggestBox>
|
||||
</CommandBar.Content>
|
||||
<CommandBar.Content>
|
||||
<AutoSuggestBox
|
||||
Width="300"
|
||||
Height="36"
|
||||
Margin="3,6,6,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
PlaceholderText="{shcm:ResourceString Name=ViewPageAchievementSearchPlaceholder}"
|
||||
QueryIcon="{shcm:FontIcon Glyph=}"
|
||||
Text="{Binding SearchText, Mode=TwoWay}">
|
||||
<mxi:Interaction.Behaviors>
|
||||
<mxic:EventTriggerBehavior EventName="QuerySubmitted">
|
||||
<mxic:InvokeCommandAction Command="{Binding SearchAchievementCommand}" CommandParameter="{Binding SearchText}"/>
|
||||
</mxic:EventTriggerBehavior>
|
||||
</mxi:Interaction.Behaviors>
|
||||
</AutoSuggestBox>
|
||||
</CommandBar.Content>
|
||||
|
||||
<AppBarElementContainer>
|
||||
<shc:SizeRestrictedContentControl Margin="2,6,3,6">
|
||||
<ComboBox
|
||||
DisplayMemberPath="Name"
|
||||
ItemsSource="{Binding Archives, Mode=OneWay}"
|
||||
SelectedItem="{Binding SelectedArchive, Mode=TwoWay}"
|
||||
Style="{ThemeResource CommandBarComboBoxStyle}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
</AppBarElementContainer>
|
||||
<AppBarButton
|
||||
Command="{Binding AddArchiveCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageAchievementAddArchive}"/>
|
||||
<AppBarButton
|
||||
Command="{Binding RemoveArchiveCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageAchievementRemoveArchive}"/>
|
||||
<AppBarSeparator/>
|
||||
<AppBarElementContainer>
|
||||
<shc:SizeRestrictedContentControl Margin="2,6,3,6">
|
||||
<ComboBox
|
||||
DisplayMemberPath="Name"
|
||||
ItemsSource="{Binding Archives, Mode=OneWay}"
|
||||
SelectedItem="{Binding SelectedArchive, Mode=TwoWay}"
|
||||
Style="{ThemeResource CommandBarComboBoxStyle}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
</AppBarElementContainer>
|
||||
<AppBarButton
|
||||
Command="{Binding AddArchiveCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageAchievementAddArchive}"/>
|
||||
<AppBarButton
|
||||
Command="{Binding RemoveArchiveCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageAchievementRemoveArchive}"/>
|
||||
<AppBarSeparator/>
|
||||
|
||||
<AppBarButton Icon="{shcm:FontIcon Glyph=}" Label="{shcm:ResourceString Name=ViewPageAchievementImportLabel}">
|
||||
<AppBarButton.Flyout>
|
||||
<MenuFlyout Placement="BottomEdgeAlignedRight">
|
||||
<MenuFlyoutItem
|
||||
Command="{Binding ImportUIAFFromClipboardCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Text="{shcm:ResourceString Name=ViewPageAchievementImportFromClipboard}"/>
|
||||
<MenuFlyoutItem
|
||||
Command="{Binding ImportUIAFFromFileCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Text="{shcm:ResourceString Name=ViewPageAchievementImportFromFile}"/>
|
||||
</MenuFlyout>
|
||||
</AppBarButton.Flyout>
|
||||
</AppBarButton>
|
||||
<AppBarButton
|
||||
Command="{Binding ExportAsUIAFToFileCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageAchievementExportLabel}"/>
|
||||
<AppBarSeparator/>
|
||||
|
||||
<AppBarToggleButton
|
||||
Command="{Binding SortUncompletedSwitchCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
IsChecked="{Binding IsUncompletedItemsFirst, Mode=TwoWay}"
|
||||
Label="{shcm:ResourceString Name=ViewPageAchievementSortIncompletedItemsFirst}"/>
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
</Border>
|
||||
<AppBarButton Icon="{shcm:FontIcon Glyph=}" Label="{shcm:ResourceString Name=ViewPageAchievementImportLabel}">
|
||||
<AppBarButton.Flyout>
|
||||
<MenuFlyout Placement="BottomEdgeAlignedRight">
|
||||
<MenuFlyoutItem
|
||||
Command="{Binding ImportUIAFFromClipboardCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Text="{shcm:ResourceString Name=ViewPageAchievementImportFromClipboard}"/>
|
||||
<MenuFlyoutItem
|
||||
Command="{Binding ImportUIAFFromFileCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Text="{shcm:ResourceString Name=ViewPageAchievementImportFromFile}"/>
|
||||
</MenuFlyout>
|
||||
</AppBarButton.Flyout>
|
||||
</AppBarButton>
|
||||
<AppBarButton
|
||||
Command="{Binding ExportAsUIAFToFileCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageAchievementExportLabel}"/>
|
||||
<AppBarSeparator/>
|
||||
|
||||
<AppBarToggleButton
|
||||
Command="{Binding SortUncompletedSwitchCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
IsChecked="{Binding IsUncompletedItemsFirst, Mode=TwoWay}"
|
||||
Label="{shcm:ResourceString Name=ViewPageAchievementSortIncompletedItemsFirst}"/>
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Margin="16,0,16,16"
|
||||
@@ -313,7 +309,7 @@
|
||||
PaneBackground="{ThemeResource CardBackgroundFillColorSecondaryBrush}">
|
||||
<SplitView.Pane>
|
||||
<ListView
|
||||
Padding="{ThemeResource ListViewInSplitPanePadding}"
|
||||
Padding="0,2,0,1"
|
||||
ItemContainerStyle="{StaticResource AchievementGoalListViewItemStyle}"
|
||||
ItemTemplate="{StaticResource AchievementGoalListTemplate}"
|
||||
ItemsSource="{Binding AchievementGoals}"
|
||||
@@ -352,7 +348,7 @@
|
||||
<GridView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<shcp:UniformPanel
|
||||
Margin="16,16,12,4"
|
||||
Margin="16,16,12,0"
|
||||
ColumnSpacing="0"
|
||||
MinItemWidth="200"/>
|
||||
</ItemsPanelTemplate>
|
||||
|
||||
@@ -149,22 +149,20 @@
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="AnnouncementPivotItemContentTemplate">
|
||||
<ScrollViewer>
|
||||
<ItemsRepeater
|
||||
Margin="16,16,16,16"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemTemplate="{StaticResource AnnouncementTemplate}"
|
||||
ItemsSource="{Binding List}">
|
||||
<ItemsRepeater.Layout>
|
||||
<UniformGridLayout
|
||||
ItemsJustification="Start"
|
||||
ItemsStretch="Fill"
|
||||
MinColumnSpacing="12"
|
||||
MinItemWidth="300"
|
||||
MinRowSpacing="12"/>
|
||||
</ItemsRepeater.Layout>
|
||||
</ItemsRepeater>
|
||||
</ScrollViewer>
|
||||
<ItemsRepeater
|
||||
Margin="16,16,16,16"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemTemplate="{StaticResource AnnouncementTemplate}"
|
||||
ItemsSource="{Binding List}">
|
||||
<ItemsRepeater.Layout>
|
||||
<UniformGridLayout
|
||||
ItemsJustification="Start"
|
||||
ItemsStretch="Fill"
|
||||
MinColumnSpacing="12"
|
||||
MinItemWidth="300"
|
||||
MinRowSpacing="12"/>
|
||||
</ItemsRepeater.Layout>
|
||||
</ItemsRepeater>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="HutaoAnnouncementTemplate">
|
||||
@@ -194,7 +192,7 @@
|
||||
<SolidColorBrush x:Key="ItemContainerPointerOverBackground" Color="Transparent"/>
|
||||
</ItemContainer.Resources>
|
||||
|
||||
<Border Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||
<Border Background="{ThemeResource SystemControlAcrylicElementMediumHighBrush}" CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||
<ContentControl HorizontalContentAlignment="Stretch" Content="{Binding Card}"/>
|
||||
</Border>
|
||||
</ItemContainer>
|
||||
@@ -202,7 +200,7 @@
|
||||
</shc:ScopedPage.Resources>
|
||||
|
||||
<Grid>
|
||||
<ScrollViewer Padding="0" Style="{StaticResource DefaultScrollViewerStyle}">
|
||||
<ScrollViewer Padding="0">
|
||||
<StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
@@ -215,38 +213,40 @@
|
||||
ItemTemplate="{StaticResource HutaoAnnouncementTemplate}"
|
||||
ItemsSource="{Binding HutaoAnnouncements}"
|
||||
Visibility="{Binding HutaoAnnouncements.Count, Converter={StaticResource Int32ToVisibilityConverter}}"/>
|
||||
|
||||
<!-- ItemsRepeater will behave abnormal if no direct scrollhost wrapping it -->
|
||||
<ScrollViewer>
|
||||
<ItemsRepeater
|
||||
Margin="16"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemTemplate="{StaticResource CardTemplate}"
|
||||
ItemsSource="{Binding Cards, Mode=OneWay}">
|
||||
<ItemsRepeater.Layout>
|
||||
<UniformGridLayout
|
||||
ItemsJustification="Start"
|
||||
ItemsStretch="Fill"
|
||||
MinColumnSpacing="12"
|
||||
MinItemHeight="180"
|
||||
MinItemWidth="300"
|
||||
MinRowSpacing="12"
|
||||
Orientation="Horizontal"/>
|
||||
</ItemsRepeater.Layout>
|
||||
</ItemsRepeater>
|
||||
</ScrollViewer>
|
||||
<ItemsRepeater
|
||||
Margin="16"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemTemplate="{StaticResource CardTemplate}"
|
||||
ItemsSource="{Binding Cards, Mode=OneWay}">
|
||||
<ItemsRepeater.Layout>
|
||||
<UniformGridLayout
|
||||
ItemsJustification="Start"
|
||||
ItemsStretch="Fill"
|
||||
MinColumnSpacing="12"
|
||||
MinItemHeight="180"
|
||||
MinItemWidth="300"
|
||||
MinRowSpacing="12"
|
||||
Orientation="Horizontal"/>
|
||||
</ItemsRepeater.Layout>
|
||||
</ItemsRepeater>
|
||||
</StackPanel>
|
||||
|
||||
<Pivot Style="{StaticResource DefaultPivotStyle}">
|
||||
<PivotItem
|
||||
Content="{Binding Announcement.List[0]}"
|
||||
ContentTemplate="{StaticResource AnnouncementPivotItemContentTemplate}"
|
||||
Header="{shcm:ResourceString Name=ViewPageAnnouncementActivity}"/>
|
||||
<PivotItem
|
||||
Content="{Binding Announcement.List[1]}"
|
||||
ContentTemplate="{StaticResource AnnouncementPivotItemContentTemplate}"
|
||||
Header="{shcm:ResourceString Name=ViewPageAnnouncementGame}"/>
|
||||
</Pivot>
|
||||
<ScrollViewer
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
HorizontalScrollMode="Disabled"
|
||||
VerticalScrollBarVisibility="Hidden">
|
||||
<Pivot Style="{StaticResource DefaultPivotStyle}">
|
||||
<PivotItem
|
||||
Content="{Binding Announcement.List[0]}"
|
||||
ContentTemplate="{StaticResource AnnouncementPivotItemContentTemplate}"
|
||||
Header="{shcm:ResourceString Name=ViewPageAnnouncementActivity}"/>
|
||||
<PivotItem
|
||||
Content="{Binding Announcement.List[1]}"
|
||||
ContentTemplate="{StaticResource AnnouncementPivotItemContentTemplate}"
|
||||
Header="{shcm:ResourceString Name=ViewPageAnnouncementGame}"/>
|
||||
</Pivot>
|
||||
</ScrollViewer>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
@@ -507,13 +507,13 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Margin="16,16,16,0" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Border Style="{ThemeResource AcrylicSecondaryBorderCardStyle}">
|
||||
<Border Style="{ThemeResource AcrylicBaseHighBorderCardStyle}">
|
||||
<CommandBar Grid.Row="0" DefaultLabelPosition="Right">
|
||||
<CommandBar.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<shcp:PanelSelector
|
||||
x:Name="ItemsPanelSelector"
|
||||
Margin="4,8,0,0"
|
||||
Margin="6,8,0,0"
|
||||
LocalSettingKeySuffixForCurrent="AvatarPropertyPage.Summary.Avatars"/>
|
||||
<ToggleButton
|
||||
x:Name="RefreshTimeToggle"
|
||||
@@ -560,7 +560,7 @@
|
||||
<ScrollViewer>
|
||||
<StackPanel x:Name="GridImageExportPanel" Background="Transparent">
|
||||
<GridView
|
||||
Margin="16,16,4,-4"
|
||||
Margin="16,16,4,-8"
|
||||
cwa:ItemsReorderAnimation.Duration="0:0:0.1"
|
||||
ItemContainerStyle="{StaticResource LargeGridViewItemStyle}"
|
||||
ItemTemplate="{StaticResource AvatarGridViewTemplate}"
|
||||
@@ -578,7 +578,7 @@
|
||||
PaneBackground="{ThemeResource CardBackgroundFillColorSecondaryBrush}">
|
||||
<SplitView.Pane>
|
||||
<ListView
|
||||
Padding="{ThemeResource ListViewInSplitPanePadding}"
|
||||
Padding="0,2,0,1"
|
||||
ItemTemplate="{StaticResource AvatarListViewTemplate}"
|
||||
ItemsSource="{Binding Summary.Avatars}"
|
||||
SelectedItem="{Binding SelectedAvatar, Mode=TwoWay}"/>
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
x:Class="Snap.Hutao.View.Page.CultivationPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cw="using:CommunityToolkit.WinUI"
|
||||
xmlns:cwcont="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:cwconv="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:mxim="using:Microsoft.Xaml.Interactions.Media"
|
||||
xmlns:shc="using:Snap.Hutao.Control"
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||
xmlns:shcl="using:Snap.Hutao.Control.Layout"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
xmlns:shvco="using:Snap.Hutao.View.Control"
|
||||
xmlns:shvcp="using:Snap.Hutao.View.Card.Primitive"
|
||||
xmlns:shvcu="using:Snap.Hutao.ViewModel.Cultivation"
|
||||
d:DataContext="{d:DesignInstance shvcu:CultivationViewModel}"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
@@ -108,12 +108,12 @@
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="CultivateEntryTemplate">
|
||||
<ItemContainer Margin="0,0,0,16" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<ItemContainer Margin="0,0,0,16">
|
||||
<ItemContainer.Resources>
|
||||
<SolidColorBrush x:Key="ItemContainerPointerOverBackground" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ItemContainerPressedBackground" Color="Transparent"/>
|
||||
</ItemContainer.Resources>
|
||||
<Grid Style="{ThemeResource AcrylicGridCardStyle}">
|
||||
<Grid Style="{ThemeResource GridCardStyle}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
@@ -172,35 +172,31 @@
|
||||
<SolidColorBrush x:Key="ItemContainerPointerOverBackground" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ItemContainerPressedBackground" Color="Transparent"/>
|
||||
</ItemContainer.Resources>
|
||||
<shvcp:HorizontalCard>
|
||||
<shvcp:HorizontalCard.Left>
|
||||
<shvco:ItemIcon
|
||||
Grid.Column="0"
|
||||
Width="40"
|
||||
Height="40"
|
||||
Icon="{Binding Inner.Icon, Converter={StaticResource ItemIconConverter}}"
|
||||
Quality="{Binding Inner.RankLevel}"/>
|
||||
</shvcp:HorizontalCard.Left>
|
||||
<shvcp:HorizontalCard.Right>
|
||||
<Grid Margin="16,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Inner.Name}"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding CountFormatted}"/>
|
||||
</Grid>
|
||||
</shvcp:HorizontalCard.Right>
|
||||
</shvcp:HorizontalCard>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<shvco:ItemIcon
|
||||
Grid.Column="0"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Icon="{Binding Inner.Icon, Converter={StaticResource ItemIconConverter}}"
|
||||
Quality="{Binding Inner.RankLevel}"/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="16,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Inner.Name}"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="16,0,4,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding CountFormatted}"/>
|
||||
</Grid>
|
||||
</ItemContainer>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -249,7 +245,12 @@
|
||||
|
||||
<Grid Visibility="{Binding IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid Visibility="{Binding Projects.Count, Converter={StaticResource Int32ToVisibilityConverter}}">
|
||||
<Pivot Style="{ThemeResource CardPivotStyle}">
|
||||
<Rectangle
|
||||
Height="{StaticResource AppBarThemeCompactHeight}"
|
||||
VerticalAlignment="Top"
|
||||
Fill="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsHitTestVisible="False"/>
|
||||
<Pivot>
|
||||
<Pivot.RightHeader>
|
||||
<CommandBar DefaultLabelPosition="Right">
|
||||
<AppBarElementContainer>
|
||||
@@ -260,6 +261,7 @@
|
||||
SelectedItem="{Binding SelectedProject, Mode=TwoWay}"
|
||||
Style="{ThemeResource CommandBarComboBoxStyle}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
|
||||
</AppBarElementContainer>
|
||||
<AppBarButton
|
||||
Command="{Binding AddProjectCommand}"
|
||||
@@ -275,11 +277,11 @@
|
||||
|
||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageCultivationMaterialList}">
|
||||
<Grid>
|
||||
<Pivot Style="{ThemeResource CardPivotStyle}" Visibility="{Binding CultivateEntries.Count, Converter={StaticResource Int32ToVisibilityConverter}}">
|
||||
<Pivot Visibility="{Binding CultivateEntries.Count, Converter={StaticResource Int32ToVisibilityConverter}}">
|
||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageCultivationCultivateEntry}">
|
||||
<ScrollView Padding="0,0">
|
||||
<ScrollView Padding="16,0">
|
||||
<ItemsRepeater
|
||||
Margin="16,16,16,0"
|
||||
Margin="0,16,0,0"
|
||||
ItemTemplate="{StaticResource CultivateEntryTemplate}"
|
||||
ItemsSource="{Binding CultivateEntries}">
|
||||
<ItemsRepeater.Layout>
|
||||
@@ -292,128 +294,97 @@
|
||||
</ScrollView>
|
||||
</PivotItem>
|
||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageCultivationMaterialStatistics}">
|
||||
<Border Margin="16" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Border Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||
<ScrollView Padding="16,0">
|
||||
<ItemsRepeater
|
||||
Margin="0,16,0,0"
|
||||
ItemTemplate="{StaticResource StatisticsItemTemplate}"
|
||||
ItemsSource="{Binding StatisticsItems}">
|
||||
<ItemsRepeater.Layout>
|
||||
<UniformGridLayout
|
||||
ItemsJustification="Start"
|
||||
ItemsStretch="Fill"
|
||||
MinColumnSpacing="12"
|
||||
MinItemWidth="240"
|
||||
MinRowSpacing="-4"/>
|
||||
</ItemsRepeater.Layout>
|
||||
</ItemsRepeater>
|
||||
</ScrollView>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<ScrollView Padding="16,0">
|
||||
<ItemsRepeater
|
||||
Margin="0,16,0,0"
|
||||
ItemTemplate="{StaticResource StatisticsItemTemplate}"
|
||||
ItemsSource="{Binding StatisticsItems}">
|
||||
<ItemsRepeater.Layout>
|
||||
<UniformGridLayout
|
||||
ItemsJustification="Start"
|
||||
ItemsStretch="Fill"
|
||||
MinColumnSpacing="12"
|
||||
MinItemWidth="300"
|
||||
MinRowSpacing="-4"/>
|
||||
</ItemsRepeater.Layout>
|
||||
</ItemsRepeater>
|
||||
</ScrollView>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
<Border Visibility="{Binding CultivateEntries.Count, Converter={StaticResource Int32ToVisibilityRevertConverter}}">
|
||||
<Border
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Border
|
||||
MinWidth="480"
|
||||
Padding="16"
|
||||
Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<StackPanel.Resources>
|
||||
<Thickness x:Key="SettingsCardPadding">16</Thickness>
|
||||
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
|
||||
</StackPanel.Resources>
|
||||
<shci:CachedImage
|
||||
Height="120"
|
||||
Margin="0,0,0,16"
|
||||
Source="{StaticResource UI_EmotionIcon433}"/>
|
||||
<cwcont:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageCultivationNavigateAction}"
|
||||
Command="{Binding NavigateToPageCommand}"
|
||||
CommandParameter="Snap.Hutao.View.Page.WikiAvatarPage"
|
||||
Description="{shcm:ResourceString Name=ViewPageCultivationWikiAvatarDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewWikiAvatarHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/WikiAvatar.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwcont:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageCultivationNavigateAction}"
|
||||
Command="{Binding NavigateToPageCommand}"
|
||||
CommandParameter="Snap.Hutao.View.Page.WikiWeaponPage"
|
||||
Description="{shcm:ResourceString Name=ViewPageCultivationWikiWeaponDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewWikiWeaponHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/WikiWeapon.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwcont:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageCultivationNavigateAction}"
|
||||
Command="{Binding NavigateToPageCommand}"
|
||||
CommandParameter="Snap.Hutao.View.Page.AvatarPropertyPage"
|
||||
Description="{shcm:ResourceString Name=ViewPageCultivationAvatarPropertyDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewAvatarPropertyHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/Cultivation.png}"
|
||||
IsClickEnabled="True"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
</Border>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="{StaticResource SettingsCardSpacing}"
|
||||
Visibility="{Binding CultivateEntries.Count, Converter={StaticResource Int32ToVisibilityRevertConverter}}">
|
||||
<StackPanel.Resources>
|
||||
<Thickness x:Key="SettingsCardPadding">16</Thickness>
|
||||
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
|
||||
</StackPanel.Resources>
|
||||
<cwcont:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageCultivationNavigateAction}"
|
||||
Command="{Binding NavigateToPageCommand}"
|
||||
CommandParameter="Snap.Hutao.View.Page.WikiAvatarPage"
|
||||
Description="{shcm:ResourceString Name=ViewPageCultivationWikiAvatarDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewWikiAvatarHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/WikiAvatar.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwcont:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageCultivationNavigateAction}"
|
||||
Command="{Binding NavigateToPageCommand}"
|
||||
CommandParameter="Snap.Hutao.View.Page.WikiWeaponPage"
|
||||
Description="{shcm:ResourceString Name=ViewPageCultivationWikiWeaponDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewWikiWeaponHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/WikiWeapon.png}"
|
||||
IsClickEnabled="True"/>
|
||||
<cwcont:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageCultivationNavigateAction}"
|
||||
Command="{Binding NavigateToPageCommand}"
|
||||
CommandParameter="Snap.Hutao.View.Page.AvatarPropertyPage"
|
||||
Description="{shcm:ResourceString Name=ViewPageCultivationAvatarPropertyDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewAvatarPropertyHeader}"
|
||||
HeaderIcon="{shcm:BitmapIcon Source=ms-appx:///Resource/Navigation/Cultivation.png}"
|
||||
IsClickEnabled="True"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</PivotItem>
|
||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageCultivationInventoryItem}">
|
||||
<Border Margin="16" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Border Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||
<ScrollView HorizontalScrollBarVisibility="Hidden">
|
||||
<ItemsRepeater
|
||||
Margin="16"
|
||||
ItemTemplate="{StaticResource InventoryItemTemplate}"
|
||||
ItemsSource="{Binding InventoryItems}">
|
||||
<ItemsRepeater.Layout>
|
||||
<cwcont:WrapLayout HorizontalSpacing="12" VerticalSpacing="12"/>
|
||||
</ItemsRepeater.Layout>
|
||||
</ItemsRepeater>
|
||||
</ScrollView>
|
||||
</Border>
|
||||
</Border>
|
||||
<ScrollView HorizontalScrollBarVisibility="Hidden">
|
||||
<ItemsRepeater
|
||||
Margin="16"
|
||||
ItemTemplate="{StaticResource InventoryItemTemplate}"
|
||||
ItemsSource="{Binding InventoryItems}">
|
||||
<ItemsRepeater.Layout>
|
||||
<cwcont:WrapLayout HorizontalSpacing="12" VerticalSpacing="12"/>
|
||||
</ItemsRepeater.Layout>
|
||||
</ItemsRepeater>
|
||||
</ScrollView>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
</Grid>
|
||||
|
||||
<Grid Visibility="{Binding Projects.Count, Converter={StaticResource Int32ToVisibilityRevertConverter}}">
|
||||
<Border
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Border
|
||||
MinWidth="480"
|
||||
Padding="16"
|
||||
Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||
<StackPanel>
|
||||
<shci:CachedImage
|
||||
Width="120"
|
||||
Height="120"
|
||||
Source="{StaticResource UI_EmotionIcon293}"/>
|
||||
<TextBlock
|
||||
Margin="0,16,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewPageCultivationAddProjectContinue}"/>
|
||||
<StackPanel Margin="0,24,0,0">
|
||||
<cwcont:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageCultivationAddProjectAction}"
|
||||
Command="{Binding AddProjectCommand}"
|
||||
Description="{shcm:ResourceString Name=ViewPageCultivationAddProjectDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageCultivationAddProject}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsClickEnabled="True"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<shci:CachedImage
|
||||
Width="120"
|
||||
Height="120"
|
||||
Source="{StaticResource UI_EmotionIcon293}"/>
|
||||
<TextBlock
|
||||
Margin="0,16,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewPageCultivationAddProjectContinue}"/>
|
||||
<StackPanel Margin="0,24,0,0" HorizontalAlignment="Center">
|
||||
<cwcont:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageCultivationAddProjectAction}"
|
||||
Command="{Binding AddProjectCommand}"
|
||||
Description="{shcm:ResourceString Name=ViewPageCultivationAddProjectDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageCultivationAddProject}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsClickEnabled="True"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</shc:ScopedPage>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:mxim="using:Microsoft.Xaml.Interactions.Media"
|
||||
xmlns:shc="using:Snap.Hutao.Control"
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shch="using:Snap.Hutao.Control.Helper"
|
||||
@@ -389,7 +391,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Margin="16,16,16,0" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||
<Border Style="{ThemeResource AcrylicSecondaryBorderCardStyle}">
|
||||
<Border Style="{ThemeResource AcrylicBaseHighBorderCardStyle}">
|
||||
<CommandBar DefaultLabelPosition="Right">
|
||||
<AppBarButton
|
||||
Command="{Binding RefreshCommand}"
|
||||
@@ -490,34 +492,19 @@
|
||||
</StackPanel>
|
||||
</cwc:HeaderedContentControl>
|
||||
|
||||
<cwc:HeaderedContentControl
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
IsEnabled="{Binding RuntimeOptions.IsToastAvailable}">
|
||||
<cwc:HeaderedContentControl.Header>
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageDailyNoteNotificationHeader}"/>
|
||||
</cwc:HeaderedContentControl.Header>
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<InfoBar
|
||||
Title="胡桃的通知权限已被关闭"
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
Severity="Warning"
|
||||
Visibility="{Binding RuntimeOptions.IsToastAvailable, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageDailyNoteSlientModeDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageDailyNoteSlientModeHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<ToggleSwitch Margin="24,0,0,0" IsOn="{Binding DailyNoteOptions.IsSilentWhenPlayingGame, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageDailyNoteReminderDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageDailyNoteReminderHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<ToggleSwitch Margin="24,0,0,0" IsOn="{Binding DailyNoteOptions.IsReminderNotification, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
</StackPanel>
|
||||
</cwc:HeaderedContentControl>
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageDailyNoteNotificationHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageDailyNoteSlientModeDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageDailyNoteSlientModeHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<ToggleSwitch Margin="24,0,0,0" IsOn="{Binding DailyNoteOptions.IsSilentWhenPlayingGame, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageDailyNoteReminderDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageDailyNoteReminderHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<ToggleSwitch Margin="24,0,0,0" IsOn="{Binding DailyNoteOptions.IsReminderNotification, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageDailyNoteDataInteropHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
|
||||
@@ -316,7 +316,7 @@
|
||||
PaneBackground="{ThemeResource CardBackgroundFillColorSecondaryBrush}">
|
||||
<SplitView.Pane>
|
||||
<ListView
|
||||
Padding="{ThemeResource ListViewInSplitPanePadding}"
|
||||
Padding="0,1"
|
||||
ItemTemplate="{StaticResource HistoryWishListTemplate}"
|
||||
ItemsSource="{Binding Statistics.HistoryWishes}"
|
||||
SelectedItem="{Binding SelectedHistoryWish, Mode=TwoWay}"/>
|
||||
@@ -507,7 +507,10 @@
|
||||
HorizontalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewPageGachaLogHint}"/>
|
||||
<StackPanel Margin="0,24,0,0" Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<StackPanel
|
||||
Margin="0,24,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<cwc:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageGachaLogRefreshAction}"
|
||||
Command="{Binding RefreshBySTokenCommand}"
|
||||
|
||||
@@ -200,24 +200,15 @@
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsClickEnabled="True"/>
|
||||
<Border Style="{StaticResource BorderCardStyle}">
|
||||
<Border Padding="0,1" Style="{StaticResource BorderCardStyle}">
|
||||
<ListView
|
||||
Padding="{StaticResource ListViewInSplitPanePadding}"
|
||||
AllowDrop="{Binding RuntimeOptions.IsElevated, Converter={StaticResource BoolNegationConverter}}"
|
||||
CanReorderItems="{Binding RuntimeOptions.IsElevated, Converter={StaticResource BoolNegationConverter}}"
|
||||
AllowDrop="True"
|
||||
CanDragItems="True"
|
||||
CanReorderItems="True"
|
||||
DragItemsCompleted="OnAccountListViewReordered"
|
||||
ItemTemplate="{StaticResource GameAccountListTemplate}"
|
||||
ItemsSource="{Binding GameAccountsView}"
|
||||
SelectedItem="{Binding SelectedGameAccount, Mode=TwoWay}">
|
||||
<ListView.Header>
|
||||
<InfoBar
|
||||
Title="{shcm:ResourceString Name=ViewListViewDragElevatedHint}"
|
||||
Margin="4,2,4,2"
|
||||
IsClosable="True"
|
||||
IsOpen="True"
|
||||
Severity="Warning"
|
||||
Visibility="{Binding RuntimeOptions.IsElevated, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
</ListView.Header>
|
||||
</ListView>
|
||||
SelectedItem="{Binding SelectedGameAccount, Mode=TwoWay}"/>
|
||||
</Border>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameWindowsHDRDescription}"
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.ViewModel.Game;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Snap.Hutao.View.Page;
|
||||
|
||||
@@ -20,4 +24,20 @@ internal sealed partial class LaunchGamePage : ScopedPage
|
||||
InitializeWith<LaunchGameViewModel>();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void OnAccountListViewReordered(object sender, DragItemsCompletedEventArgs e)
|
||||
{
|
||||
ListView listView = (ListView)sender;
|
||||
AdvancedCollectionView advancedCollectionView = (AdvancedCollectionView)listView.ItemsSource;
|
||||
ObservableCollection<GameAccount> gameAccounts = (ObservableCollection<GameAccount>)advancedCollectionView.Source;
|
||||
|
||||
List<GameAccount> newGameAccounts = [];
|
||||
|
||||
foreach (GameAccount gameAccount in gameAccounts)
|
||||
{
|
||||
newGameAccounts.Add(gameAccount.Clone());
|
||||
}
|
||||
|
||||
((LaunchGameViewModel)DataContext).ReorderGameAccounts(newGameAccounts);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user