diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
index 7d443117..896cb4cb 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
@@ -6,6 +6,7 @@ using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Snap.Hutao.Core.Caching;
using Snap.Hutao.Extension;
+using Windows.Storage;
namespace Snap.Hutao.Control.Image;
@@ -14,15 +15,11 @@ namespace Snap.Hutao.Control.Image;
///
public class CachedImage : ImageEx
{
- private readonly IImageCache imageCache;
-
///
/// 构造一个新的缓存图像
///
public CachedImage()
{
- imageCache = Ioc.Default.GetRequiredService();
-
IsCacheEnabled = true;
EnableLazyLoading = true;
}
@@ -30,10 +27,15 @@ public class CachedImage : ImageEx
///
protected override async Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
{
- BitmapImage? image;
+ IImageCache imageCache = Ioc.Default.GetRequiredService();
+
try
{
- image = await imageCache.GetFromCacheAsync(imageUri, true);
+ StorageFile file = await imageCache.GetFileFromCacheAsync(imageUri);
+
+ // check token state to determine whether the operation should be canceled.
+ Must.TryThrowOnCanceled(token, "Image source has changed.");
+ return new BitmapImage(new(file.Path));
}
catch (TaskCanceledException)
{
@@ -43,18 +45,8 @@ public class CachedImage : ImageEx
catch
{
// maybe the image is corrupted, remove it.
- await imageCache.RemoveAsync(imageUri.Enumerate());
+ await imageCache.RemoveAsync(imageUri.Enumerate()).ConfigureAwait(false);
throw;
}
-
- // check token state to determine whether the operation should be canceled.
- if (token.IsCancellationRequested)
- {
- throw new TaskCanceledException("Image source has changed.");
- }
- else
- {
- return Must.NotNull(image!);
- }
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
index 801e836e..984e40bf 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
@@ -60,7 +60,7 @@ public class Gradient : Microsoft.UI.Xaml.Controls.Control
{
Ioc.Default
.GetRequiredService()
- .Error(exception, "应用渐变背景时发生异常");
+ .Error(exception, "应用渐变图像时发生异常");
}
private static Task GetCachedFileAsync(string url)
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs
new file mode 100644
index 00000000..1b375540
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs
@@ -0,0 +1,149 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.WinUI.UI.Animations;
+using Microsoft.Graphics.Canvas.Effects;
+using Microsoft.UI;
+using Microsoft.UI.Composition;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Hosting;
+using Microsoft.UI.Xaml.Media;
+using Snap.Hutao.Core;
+using Snap.Hutao.Core.Caching;
+using Snap.Hutao.Core.Threading;
+using Snap.Hutao.Extension;
+using Snap.Hutao.Service.Abstraction;
+using Windows.Graphics.Imaging;
+using Windows.Storage;
+using Windows.Storage.Streams;
+
+namespace Snap.Hutao.Control.Image;
+
+///
+/// 支持单色的图像
+///
+public class MonoChrome : Microsoft.UI.Xaml.Controls.Control
+{
+ private static readonly DependencyProperty SourceProperty = Property.Depend(nameof(Source), string.Empty, OnSourceChanged);
+ private static readonly ConcurrentCancellationTokenSource LoadingTokenSource = new();
+
+ private SpriteVisual? spriteVisual;
+ private CompositionColorBrush? backgroundBrush;
+
+ ///
+ /// 构造一个新的单色图像
+ ///
+ public MonoChrome()
+ {
+ SizeChanged += OnSizeChanged;
+ ActualThemeChanged += OnActualThemeChanged;
+ }
+
+ ///
+ /// 源
+ ///
+ public string Source
+ {
+ get => (string)GetValue(SourceProperty);
+ set => SetValue(SourceProperty, value);
+ }
+
+ private static void OnSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs arg)
+ {
+ MonoChrome monoChrome = (MonoChrome)sender;
+ string url = (string)arg.NewValue;
+
+ ILogger logger = Ioc.Default.GetRequiredService>();
+ monoChrome.ApplyImageAsync(url, LoadingTokenSource.Register(monoChrome)).SafeForget(logger, OnApplyImageFailed);
+ }
+
+ private static void OnApplyImageFailed(Exception exception)
+ {
+ Ioc.Default
+ .GetRequiredService()
+ .Error(exception, "应用单色背景时发生异常");
+ }
+
+ private static async Task LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
+ {
+ using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
+ {
+ BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream).AsTask(token);
+ return LoadedImageSurface.StartLoadFromStream(imageStream);
+ }
+ }
+
+ private static Task GetCachedFileAsync(string url)
+ {
+ return Ioc.Default.GetRequiredService().GetFileFromCacheAsync(new(url));
+ }
+
+ private void OnSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ if (e.NewSize != e.PreviousSize && spriteVisual is not null)
+ {
+ UpdateVisual(spriteVisual);
+ }
+ }
+
+ private void OnActualThemeChanged(FrameworkElement sender, object args)
+ {
+ if (backgroundBrush != null)
+ {
+ SetBackgroundColor(backgroundBrush);
+ }
+ }
+
+ private void UpdateVisual(SpriteVisual spriteVisual)
+ {
+ if (spriteVisual is not null)
+ {
+ spriteVisual.Size = ActualSize;
+ }
+ }
+
+ private void SetBackgroundColor(CompositionColorBrush backgroundBrush)
+ {
+ ApplicationTheme theme = ActualTheme switch
+ {
+ ElementTheme.Light => ApplicationTheme.Light,
+ ElementTheme.Dark => ApplicationTheme.Dark,
+ _ => App.Current.RequestedTheme,
+ };
+
+ backgroundBrush.Color = theme switch
+ {
+ ApplicationTheme.Light => Colors.Black,
+ ApplicationTheme.Dark => Colors.White,
+ _ => throw Must.NeverHappen(),
+ };
+ }
+
+ private async Task ApplyImageAsync(string url, CancellationToken token)
+ {
+ await AnimationBuilder.Create().Opacity(0d).StartAsync(this, token);
+
+ StorageFile? storageFile = Must.NotNull((await GetCachedFileAsync(url))!);
+
+ Compositor compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
+
+ LoadedImageSurface imageSurface = await LoadImageSurfaceAsync(storageFile, token);
+
+ CompositionColorBrush blackLayerBursh = compositor.CreateColorBrush(Colors.Black);
+ CompositionSurfaceBrush imageSurfaceBrush = compositor.CompositeSurfaceBrush(imageSurface, stretch: CompositionStretch.Uniform, vRatio: 0f);
+
+ CompositionEffectBrush overlayBrush = compositor.CompositeBlendEffectBrush(blackLayerBursh, imageSurfaceBrush, BlendEffectMode.Overlay);
+ CompositionEffectBrush opacityBrush = compositor.CompositeLuminanceToAlphaEffectBrush(overlayBrush);
+
+ backgroundBrush = compositor.CreateColorBrush();
+ SetBackgroundColor(backgroundBrush);
+ CompositionEffectBrush alphaMaskEffectBrush = compositor.CompositeAlphaMaskEffectBrush(backgroundBrush, opacityBrush);
+
+ spriteVisual = compositor.CompositeSpriteVisual(alphaMaskEffectBrush);
+ UpdateVisual(spriteVisual);
+
+ ElementCompositionPreview.SetElementChildVisual(this, spriteVisual);
+
+ await AnimationBuilder.Create().Opacity(1d).StartAsync(this, token);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs
index 26f277de..b7add47e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using Snap.Hutao.Core.Logging;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -23,7 +22,6 @@ public abstract class CacheBase
where T : class
{
private readonly SemaphoreSlim cacheFolderSemaphore = new(1);
- private readonly ConcurrentDictionary> concurrentTasks = new();
private readonly ILogger logger;
private readonly HttpClient httpClient;
@@ -48,12 +46,12 @@ public abstract class CacheBase
///
/// Gets or sets the life duration of every cache entry.
///
- public TimeSpan CacheDuration { get; set; }
+ public TimeSpan CacheDuration { get; }
///
/// Gets or sets the number of retries trying to ensure the file is cached.
///
- public uint RetryCount { get; set; }
+ public uint RetryCount { get; }
///
/// Clears all files in the cache
@@ -76,12 +74,12 @@ public abstract class CacheBase
{
TimeSpan expiryDuration = duration ?? CacheDuration;
- StorageFolder? folder = await GetCacheFolderAsync().ConfigureAwait(false);
- IReadOnlyList? files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
+ StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
+ IReadOnlyList files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
- List? filesToDelete = new();
+ List filesToDelete = new();
- foreach (StorageFile? file in files)
+ foreach (StorageFile file in files)
{
if (file == null)
{
@@ -135,24 +133,14 @@ public abstract class CacheBase
await InternalClearAsync(filesToDelete).ConfigureAwait(false);
}
- ///
- /// Retrieves item represented by Uri from the cache. If the item is not found in the cache, it will try to downloaded and saved before returning it to the caller.
- ///
- /// Uri of the item.
- /// Indicates whether or not exception should be thrown if item cannot be found / downloaded.
- /// an instance of Generic type
- public Task GetFromCacheAsync(Uri uri, bool throwOnError = false)
- {
- return GetItemAsync(uri, throwOnError);
- }
-
///
/// Gets the StorageFile containing cached item for given Uri
///
/// Uri of the item.
/// a StorageFile
- public async Task GetFileFromCacheAsync(Uri uri)
+ public async Task GetFileFromCacheAsync(Uri uri)
{
+ logger.LogInformation(EventIds.FileCaching, "Begin caching for [{uri}]", uri);
StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
string fileName = GetCacheFileName(uri);
@@ -161,28 +149,14 @@ public abstract class CacheBase
if (item == null)
{
- StorageFile? baseFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false);
+ StorageFile baseFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false);
await DownloadFileAsync(uri, baseFile).ConfigureAwait(false);
item = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false);
}
- return item as StorageFile;
+ return Must.NotNull((item as StorageFile)!);
}
- ///
- /// Cache specific hooks to process items from HTTP response
- ///
- /// input stream
- /// awaitable task
- protected abstract Task InitializeTypeAsync(Stream stream);
-
- ///
- /// Cache specific hooks to process items from HTTP response
- ///
- /// storage file
- /// awaitable task
- protected abstract Task InitializeTypeAsync(StorageFile baseFile);
-
///
/// Override-able method that checks whether file is valid or not.
///
@@ -220,121 +194,16 @@ public abstract class CacheBase
return value;
}
- private async Task GetItemAsync(Uri uri, bool throwOnError)
+ private async Task DownloadFileAsync(Uri uri, StorageFile baseFile)
{
- T? instance = default(T);
-
- string fileName = GetCacheFileName(uri);
- concurrentTasks.TryGetValue(fileName, out Task? request);
-
- // complete previous task first
- if (request != null)
+ using (Stream httpStream = await httpClient.GetStreamAsync(uri))
{
- await request.ConfigureAwait(false);
- request = null;
- }
-
- if (request == null)
- {
- request = GetFromCacheOrDownloadAsync(uri, fileName);
-
- concurrentTasks[fileName] = request;
- }
-
- try
- {
- instance = await request.ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- logger.LogError(EventIds.CacheException, ex, "Exception happened when caching.");
- if (throwOnError)
+ using (Stream fs = await baseFile.OpenStreamForWriteAsync())
{
- throw;
+ await httpStream.CopyToAsync(fs);
+ await fs.FlushAsync();
}
}
- finally
- {
- concurrentTasks.TryRemove(fileName, out _);
- }
-
- return instance;
- }
-
- private async Task GetFromCacheOrDownloadAsync(Uri uri, string fileName)
- {
- T? instance = default;
-
- StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
- StorageFile? baseFile = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false) as StorageFile;
-
- bool downloadDataFile = baseFile == null || await IsFileOutOfDateAsync(baseFile, CacheDuration).ConfigureAwait(false);
- baseFile ??= await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false);
-
- if (downloadDataFile)
- {
- uint retries = 0;
- try
- {
- while (retries < RetryCount)
- {
- try
- {
- instance = await DownloadFileAsync(uri, baseFile).ConfigureAwait(false);
-
- if (instance != null)
- {
- break;
- }
- }
- catch (FileNotFoundException)
- {
- }
-
- retries++;
- }
- }
- catch (Exception)
- {
- await baseFile.DeleteAsync().AsTask().ConfigureAwait(false);
- throw; // re-throwing the exception changes the stack trace. just throw
- }
- }
-
- if (EqualityComparer.Default.Equals(instance, default(T)))
- {
- instance = await InitializeTypeAsync(baseFile).ConfigureAwait(false);
- }
-
- return instance;
- }
-
- private async Task DownloadFileAsync(Uri uri, StorageFile baseFile)
- {
- T? instance = default;
-
- using (MemoryStream memory = new())
- {
- using (Stream httpStream = await httpClient.GetStreamAsync(uri))
- {
- await httpStream.CopyToAsync(memory);
- await memory.FlushAsync();
-
- memory.Position = 0;
-
- using (Stream fs = await baseFile.OpenStreamForWriteAsync())
- {
- await memory.CopyToAsync(fs);
- await fs.FlushAsync();
-
- memory.Position = 0;
- }
- }
-
- instance = await InitializeTypeAsync(memory).ConfigureAwait(false);
- }
-
- return instance;
}
[SuppressMessage("", "CA1822")]
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs
index a0383a45..41c3c549 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs
@@ -18,15 +18,7 @@ internal interface IImageCache
///
/// Uri of the item.
/// a StorageFile
- Task GetFileFromCacheAsync(Uri uri);
-
- ///
- /// Retrieves item represented by Uri from the cache. If the item is not found in the cache, it will try to downloaded and saved before returning it to the caller.
- ///
- /// Uri of the item.
- /// Indicates whether or not exception should be thrown if item cannot be found / downloaded.
- /// an instance of Generic type
- Task GetFromCacheAsync(Uri uri, bool throwOnError = false);
+ Task GetFileFromCacheAsync(Uri uri);
///
/// Removed items based on uri list passed
@@ -34,4 +26,11 @@ internal interface IImageCache
/// Enumerable uri list
/// awaitable Task
Task RemoveAsync(IEnumerable uriForCachedItems);
+
+ ///
+ /// Removes cached files that have expired
+ ///
+ /// Optional timespan to compute whether file has expired or not. If no value is supplied, is used.
+ /// awaitable task
+ Task RemoveExpiredAsync(TimeSpan? duration = null);
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs
index 4a27b7d2..8dd0f2a4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs
@@ -1,11 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using CommunityToolkit.WinUI;
-using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Media.Imaging;
using System.Collections.Generic;
-using System.IO;
using System.Net.Http;
using Windows.Storage;
using Windows.Storage.FileProperties;
@@ -21,10 +18,7 @@ public class ImageCache : CacheBase, IImageCache
{
private const string DateAccessedProperty = "System.DateAccessed";
- private readonly List extendedPropertyNames = new()
- {
- DateAccessedProperty,
- };
+ private readonly List extendedPropertyNames = new() { DateAccessedProperty };
///
/// Initializes a new instance of the class.
@@ -34,48 +28,6 @@ public class ImageCache : CacheBase, IImageCache
public ImageCache(ILogger logger, HttpClient httpClient)
: base(logger, httpClient)
{
- DispatcherQueue = Program.UIDispatcherQueue;
- }
-
- ///
- /// Gets or sets which DispatcherQueue is used to dispatch UI updates.
- ///
- private DispatcherQueue DispatcherQueue { get; }
-
- ///
- /// Cache specific hooks to process items from HTTP response
- ///
- /// input stream
- /// awaitable task
- protected override Task InitializeTypeAsync(Stream stream)
- {
- if (stream.Length == 0)
- {
- throw new FileNotFoundException();
- }
-
- return DispatcherQueue.EnqueueAsync(async () =>
- {
- BitmapImage image = new();
-
- // This action will run on the UI thread, no need to care which thread to continue with
- await image.SetSourceAsync(stream.AsRandomAccessStream()).AsTask().ConfigureAwait(false);
-
- return image;
- });
- }
-
- ///
- /// Cache specific hooks to process items from HTTP response
- ///
- /// storage file
- /// awaitable task
- protected override async Task InitializeTypeAsync(StorageFile baseFile)
- {
- using (Stream stream = await baseFile.OpenStreamForReadAsync().ConfigureAwait(false))
- {
- return await InitializeTypeAsync(stream).ConfigureAwait(false);
- }
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs
index 274bc70f..cae4373a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs
@@ -57,6 +57,11 @@ internal static class EventIds
///
public static readonly EventId MetadataFileMD5Check = 100111;
+ ///
+ /// 文件缓存
+ ///
+ public static readonly EventId FileCaching = 100120;
+
// 杂项
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs b/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs
index 7abe5d52..f4661e83 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs
@@ -84,4 +84,20 @@ public static class Must
#pragma warning restore CS8763 // 不应返回标记为 [DoesNotReturn] 的方法。
}
}
+
+ ///
+ /// 尝试抛出任务取消异常
+ ///
+ /// 取消令牌
+ /// 取消消息
+ /// 任务被取消
+ [DebuggerStepThrough]
+ [SuppressMessage("", "CA1068")]
+ public static void TryThrowOnCanceled(CancellationToken token, string message)
+ {
+ if (token.IsCancellationRequested)
+ {
+ throw new TaskCanceledException("Image source has changed.");
+ }
+ }
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/CompositionExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Extension/CompositionExtensions.cs
index a4028d3a..52a35d2b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/CompositionExtensions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/CompositionExtensions.cs
@@ -54,6 +54,26 @@ internal static class CompositionExtensions
return brush;
}
+ ///
+ /// 创建灰阶效果画刷
+ ///
+ /// 合成器
+ /// 源
+ /// 合成效果画刷
+ public static CompositionEffectBrush CompositeGrayScaleEffectBrush(this Compositor compositor, CompositionBrush source)
+ {
+ GrayscaleEffect effect = new()
+ {
+ Source = new CompositionEffectSourceParameter("Source"),
+ };
+
+ CompositionEffectBrush brush = compositor.CreateEffectFactory(effect).CreateBrush();
+
+ brush.SetSourceParameter("Source", source);
+
+ return brush;
+ }
+
///
/// 创建亮度转不透明度效果画刷
///
diff --git a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs
index b7d5fa0c..d276af60 100644
--- a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs
@@ -68,14 +68,6 @@ public sealed partial class MainWindow : Window
if (!rect.Size.IsEmpty)
{
WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Create(new POINT(-1, -1), rect, ShowWindowCommand.Normal);
- //WINDOWPLACEMENT windowPlacement = new()
- //{
- // Length = Marshal.SizeOf(),
- // MaxPosition = new POINT(-1, -1),
- // NormalPosition = rect,
- // ShowCmd = ShowWindowCommand.Normal,
- //};
-
User32.SetWindowPlacement(handle, ref windowPlacement);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs
index ead0aad4..08ec486d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs
@@ -44,20 +44,7 @@ internal class DescParamDescriptor : IValueConverter
throw Must.NeverHappen();
}
- private IList GetFormattedParameters(IEnumerable formats, IList param)
- {
- List results = new();
- foreach (DescFormat descFormat in formats)
- {
- string format = descFormat.Format;
- string resultFormatted = Regex.Replace(format, @"{param\d+.*?}", match => EvaluateMatch(match, param));
- results.Add(resultFormatted);
- }
-
- return results;
- }
-
- private string EvaluateMatch(Match match, IList param)
+ private static string EvaluateMatch(Match match, IList param)
{
if (match.Success)
{
@@ -87,6 +74,19 @@ internal class DescParamDescriptor : IValueConverter
}
}
+ private IList GetFormattedParameters(IEnumerable formats, IList param)
+ {
+ List results = new();
+ foreach (DescFormat descFormat in formats)
+ {
+ string format = descFormat.Format;
+ string resultFormatted = Regex.Replace(format, @"{param\d+.*?}", match => EvaluateMatch(match, param));
+ results.Add(resultFormatted);
+ }
+
+ return results;
+ }
+
private class DescFormat
{
public DescFormat(string description, string format)
diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml
index 8d63ca99..fe2e95b2 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml
@@ -25,17 +25,17 @@
+ Icon="{cwu:BitmapIcon ShowAsMonochrome=True,Source=ms-appx:///Resource/Icon/UI_BtnIcon_ActivityEntry.png}"/>
+ Icon="{cwu:BitmapIcon ShowAsMonochrome=True,Source=ms-appx:///Resource/Icon/UI_Icon_Achievement.png}"/>
+ Icon="{cwu:BitmapIcon ShowAsMonochrome=True,Source=ms-appx:///Resource/Icon/UI_BagTabIcon_Avatar.png}"/>
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
index 88fe9533..2d0f700c 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
@@ -5,7 +5,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Controls"
- xmlns:cwum="using:CommunityToolkit.WinUI.UI.Media"
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:shc="using:Snap.Hutao.Control"
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
@@ -200,12 +199,12 @@
-
-