Add experimental features

This commit is contained in:
DismissedLight
2022-07-26 11:13:14 +08:00
parent 7fcb255a21
commit 0d8b6b66df
18 changed files with 375 additions and 321 deletions

View File

@@ -29,6 +29,32 @@ dotnet_style_qualification_for_event = false:silent
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_code_quality_unused_parameters = non_public:suggestion
dotnet_style_readonly_field = true:suggestion
indent_size = 4
tab_width = 4
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_allow_multiple_blank_lines_experimental = false:silent
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = false:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
[*.cs]
#### 命名样式 ####
@@ -96,6 +122,31 @@ dotnet_diagnostic.SA1414.severity = none
# SA0001: XML comment analysis disabled
dotnet_diagnostic.SA0001.severity = none
csharp_style_prefer_parameter_null_checking = true:suggestion
csharp_style_prefer_method_group_conversion = true:silent
csharp_space_around_binary_operators = before_and_after
csharp_indent_labels = one_less_than_current
csharp_style_allow_embedded_statements_on_same_line_experimental = false:silent
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:silent
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
csharp_style_prefer_switch_expression = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_prefer_simple_default_expression = false:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:silent
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
[*.vb]
#### 命名样式 ####

View File

@@ -11,7 +11,6 @@ using Snap.Hutao.Extension;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Metadata;
using System.Diagnostics;
using Windows.ApplicationModel.Activation;
namespace Snap.Hutao;
@@ -32,10 +31,12 @@ public partial class App : Application
InitializeComponent();
InitializeDependencyInjection();
// notice that we already call InitializeDependencyInjection() above
// Notice that we already call InitializeDependencyInjection() above
// so we can use Ioc here.
logger = Ioc.Default.GetRequiredService<ILogger<App>>();
UnhandledException += AppUnhandledException;
DebugSettings.BindingFailed += XamlBindingFailed;
}
/// <summary>
@@ -44,17 +45,19 @@ public partial class App : Application
public static Window? Window { get => window; set => window = value; }
/// <inheritdoc cref="Application"/>
public static new App Current
{
get => (App)Application.Current;
}
public static new App Current => (App)Application.Current;
/// <summary>
/// <inheritdoc cref="Windows.Storage.ApplicationData.Current"/>
/// </summary>
public static Windows.Storage.ApplicationData AppData => Windows.Storage.ApplicationData.Current;
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
[SuppressMessage("", "VSTHRD100")]
protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
AppInstance mainInstance = AppInstance.FindOrRegisterForKey("main");
@@ -67,28 +70,21 @@ public partial class App : Application
}
else
{
Uri? uri = null;
if (activatedEventArgs.Kind == ExtendedActivationKind.Protocol)
{
IProtocolActivatedEventArgs protocolArgs = (activatedEventArgs.Data as IProtocolActivatedEventArgs)!;
uri = protocolArgs.Uri;
}
Window = Ioc.Default.GetRequiredService<MainWindow>();
Window.Activate();
logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", Windows.Storage.ApplicationData.Current.TemporaryFolder.Path);
if (activatedEventArgs.TryGetProtocolActivatedUri(out Uri? uri))
{
Ioc.Default.GetRequiredService<IInfoBarService>().Information(uri.ToString());
}
logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", AppData.TemporaryFolder.Path);
Ioc.Default
.GetRequiredService<IMetadataService>()
.As<IMetadataInitializer>()?
.InitializeInternalAsync()
.SafeForget(logger: logger);
if (uri != null)
{
Ioc.Default.GetRequiredService<IInfoBarService>().Information(uri.ToString());
}
.SafeForget(logger);
}
}
@@ -120,4 +116,9 @@ public partial class App : Application
{
logger.LogError(EventIds.UnhandledException, e.Exception, "未经处理的异常");
}
private void XamlBindingFailed(object sender, BindingFailedEventArgs e)
{
logger.LogCritical(EventIds.XamlBindingError, "XAML绑定失败: {message}", e.Message);
}
}

View File

@@ -1,68 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Context.FileSystem.Location;
using Windows.Storage;
namespace Snap.Hutao.Context.FileSystem;
/// <summary>
/// 缓存目录上下文
/// </summary>
[Injection(InjectAs.Transient)]
internal class CacheContext : FileSystemContext
{
/// <summary>
/// 构造一个新的缓存目录上下文
/// </summary>
/// <param name="cache">缓存位置</param>
public CacheContext(Cache cache)
: base(cache)
{
}
/// <summary>
/// 获取缓存文件夹
/// </summary>
/// <param name="folderName">文件夹名称</param>
/// <param name="token">取消令牌</param>
/// <returns>缓存文件夹</returns>
public static Task<StorageFolder> GetFolderAsync(string folderName, CancellationToken token)
{
StorageFolder tempstate = ApplicationData.Current.TemporaryFolder;
return tempstate.CreateFolderAsync(folderName, CreationCollisionOption.OpenIfExists).AsTask(token);
}
/// <summary>
/// 获取缓存文件的名称
/// </summary>
/// <param name="uri">uri</param>
/// <returns>缓存文件的名称</returns>
public static string GetCacheFileName(Uri uri)
{
return CreateHash64(uri.ToString()).ToString();
}
/// <summary>
/// 获取缓存文件的名称
/// </summary>
/// <param name="url">url</param>
/// <returns>缓存文件的名称</returns>
public static string GetCacheFileName(string url)
{
return CreateHash64(url).ToString();
}
private static ulong CreateHash64(string str)
{
byte[] utf8 = System.Text.Encoding.UTF8.GetBytes(str);
ulong value = (ulong)utf8.Length;
for (int n = 0; n < utf8.Length; n++)
{
value += (ulong)utf8[n] << ((n * 5) % 56);
}
return value;
}
}

View File

@@ -1,24 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Context.FileSystem.Location;
/// <summary>
/// 缓存位置
/// </summary>
[Injection(InjectAs.Transient)]
internal class Cache : IFileSystemLocation
{
private string? path;
/// <inheritdoc/>
public string GetPath()
{
if (string.IsNullOrEmpty(path))
{
path = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
}
return path;
}
}

View File

@@ -0,0 +1,131 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.UI.Animations;
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.Storage;
using Windows.Storage.Streams;
namespace Snap.Hutao.Control.Image;
/// <summary>
/// 合成图像控件
/// 为其他图像类控件提供基类
/// </summary>
public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
{
private static readonly DependencyProperty SourceProperty = Property<CompositionImage>.Depend(nameof(Source), default(Uri), OnSourceChanged);
private static readonly ConcurrentCancellationTokenSource<CompositionImage> LoadingTokenSource = new();
private SpriteVisual? spriteVisual;
/// <summary>
/// 构造一个新的单色图像
/// </summary>
public CompositionImage()
{
IsTabStop = false;
SizeChanged += OnSizeChanged;
}
/// <summary>
/// 源
/// </summary>
public Uri Source
{
get => (Uri)GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
/// <summary>
/// 合成组合视觉
/// </summary>
/// <param name="compositor">合成器</param>
/// <param name="imageSurface">图像表面</param>
/// <returns>拼合视觉</returns>
protected abstract SpriteVisual CompositeSpriteVisual(Compositor compositor, LoadedImageSurface imageSurface);
/// <summary>
/// 异步加载图像表面
/// </summary>
/// <param name="storageFile">文件</param>
/// <param name="token">取消令牌</param>
/// <returns>加载的图像表面</returns>
protected virtual async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
{
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
{
return LoadedImageSurface.StartLoadFromStream(imageStream);
}
}
/// <summary>
/// 更新视觉对象
/// </summary>
/// <param name="spriteVisual">拼合视觉</param>
protected virtual void OnUpdateVisual(SpriteVisual spriteVisual)
{
spriteVisual.Size = ActualSize;
}
private static Task<StorageFile> GetCachedFileAsync(Uri uri)
{
return Ioc.Default.GetRequiredService<IImageCache>().GetFileFromCacheAsync(uri);
}
private static void OnApplyImageFailed(Exception exception)
{
Ioc.Default
.GetRequiredService<IInfoBarService>()
.Error(exception, "应用合成图像时发生异常");
}
private static void OnSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs arg)
{
if (arg.NewValue is Uri uri && uri != (arg.OldValue as Uri) && !string.IsNullOrEmpty(uri.Host))
{
CompositionImage image = (CompositionImage)sender;
ILogger<CompositionImage> logger = Ioc.Default.GetRequiredService<ILogger<CompositionImage>>();
image.ApplyImageInternalAsync(uri, LoadingTokenSource.Register(image)).SafeForget(logger, OnApplyImageFailed);
}
}
private async Task ApplyImageInternalAsync(Uri? uri, CancellationToken token)
{
await AnimationBuilder.Create().Opacity(0d).StartAsync(this, token);
if (uri is null)
{
return;
}
StorageFile storageFile = await GetCachedFileAsync(uri);
Compositor compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
LoadedImageSurface imageSurface = await LoadImageSurfaceAsync(storageFile, token);
spriteVisual = CompositeSpriteVisual(compositor, imageSurface);
OnUpdateVisual(spriteVisual);
ElementCompositionPreview.SetElementChildVisual(this, spriteVisual);
await AnimationBuilder.Create().Opacity(1d).StartAsync(this, token);
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize != e.PreviousSize && spriteVisual is not null)
{
OnUpdateVisual(spriteVisual);
}
}
}

View File

@@ -1,17 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.UI.Animations;
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 System.Numerics;
using Windows.Graphics.Imaging;
using Windows.Storage;
@@ -22,61 +15,12 @@ namespace Snap.Hutao.Control.Image;
/// <summary>
/// 支持渐变的图像
/// </summary>
public class Gradient : Microsoft.UI.Xaml.Controls.Control
public class Gradient : CompositionImage
{
private static readonly DependencyProperty SourceProperty = Property<Gradient>.Depend(nameof(Source), string.Empty, OnSourceChanged);
private static readonly ConcurrentCancellationTokenSource<Gradient> LoadingTokenSource = new();
private SpriteVisual? spriteVisual;
private double imageAspectRatio;
/// <summary>
/// 构造一个新的渐变图像
/// </summary>
public Gradient()
{
SizeChanged += OnSizeChanged;
}
/// <summary>
/// 源
/// </summary>
public string Source
{
get => (string)GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
private static void OnSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs arg)
{
Gradient gradient = (Gradient)sender;
string url = (string)arg.NewValue;
ILogger<Gradient> logger = Ioc.Default.GetRequiredService<ILogger<Gradient>>();
gradient.ApplyImageAsync(url, LoadingTokenSource.Register(gradient)).SafeForget(logger, OnApplyImageFailed);
}
private static void OnApplyImageFailed(Exception exception)
{
Ioc.Default
.GetRequiredService<IInfoBarService>()
.Error(exception, "应用渐变图像时发生异常");
}
private static Task<StorageFile?> GetCachedFileAsync(string url)
{
return Ioc.Default.GetRequiredService<IImageCache>().GetFileFromCacheAsync(new(url));
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize != e.PreviousSize && spriteVisual is not null)
{
UpdateVisual(spriteVisual);
}
}
private void UpdateVisual(SpriteVisual spriteVisual)
/// <inheritdoc/>
protected override void OnUpdateVisual(SpriteVisual spriteVisual)
{
if (spriteVisual is not null)
{
@@ -85,34 +29,8 @@ public class Gradient : Microsoft.UI.Xaml.Controls.Control
}
}
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);
CompositionSurfaceBrush imageSurfaceBrush = compositor.CompositeSurfaceBrush(imageSurface, stretch: CompositionStretch.UniformToFill, vRatio: 0f);
CompositionLinearGradientBrush backgroundBrush = compositor.CompositeLinearGradientBrush(new(1f, 0), Vector2.UnitY, new(0, Colors.White), new(1, Colors.Black));
CompositionLinearGradientBrush foregroundBrush = compositor.CompositeLinearGradientBrush(Vector2.Zero, Vector2.UnitY, new(0, Colors.White), new(0.95f, Colors.Black));
CompositionEffectBrush gradientEffectBrush = compositor.CompositeBlendEffectBrush(backgroundBrush, foregroundBrush);
CompositionEffectBrush opacityMaskEffectBrush = compositor.CompositeLuminanceToAlphaEffectBrush(gradientEffectBrush);
CompositionEffectBrush alphaMaskEffectBrush = compositor.CompositeAlphaMaskEffectBrush(imageSurfaceBrush, opacityMaskEffectBrush);
spriteVisual = compositor.CompositeSpriteVisual(alphaMaskEffectBrush);
UpdateVisual(spriteVisual);
ElementCompositionPreview.SetElementChildVisual(this, spriteVisual);
await AnimationBuilder.Create().Opacity(1d).StartAsync(this, token);
}
private async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
/// <inheritdoc/>
protected override async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
{
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
{
@@ -122,4 +40,19 @@ public class Gradient : Microsoft.UI.Xaml.Controls.Control
return LoadedImageSurface.StartLoadFromStream(imageStream);
}
}
/// <inheritdoc/>
protected override SpriteVisual CompositeSpriteVisual(Compositor compositor, LoadedImageSurface imageSurface)
{
CompositionSurfaceBrush imageSurfaceBrush = compositor.CompositeSurfaceBrush(imageSurface, stretch: CompositionStretch.UniformToFill, vRatio: 0f);
CompositionLinearGradientBrush backgroundBrush = compositor.CompositeLinearGradientBrush(new(1f, 0), Vector2.UnitY, new(0, Colors.White), new(1, Colors.Black));
CompositionLinearGradientBrush foregroundBrush = compositor.CompositeLinearGradientBrush(Vector2.Zero, Vector2.UnitY, new(0, Colors.White), new(0.95f, Colors.Black));
CompositionEffectBrush gradientEffectBrush = compositor.CompositeBlendEffectBrush(backgroundBrush, foregroundBrush);
CompositionEffectBrush opacityMaskEffectBrush = compositor.CompositeLuminanceToAlphaEffectBrush(gradientEffectBrush);
CompositionEffectBrush alphaMaskEffectBrush = compositor.CompositeAlphaMaskEffectBrush(imageSurfaceBrush, opacityMaskEffectBrush);
return compositor.CompositeSpriteVisual(alphaMaskEffectBrush);
}
}

View File

@@ -1,33 +1,20 @@
// 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;
/// <summary>
/// 支持单色的图像
/// </summary>
public class MonoChrome : Microsoft.UI.Xaml.Controls.Control
public class MonoChrome : CompositionImage
{
private static readonly DependencyProperty SourceProperty = Property<MonoChrome>.Depend(nameof(Source), string.Empty, OnSourceChanged);
private static readonly ConcurrentCancellationTokenSource<MonoChrome> LoadingTokenSource = new();
private SpriteVisual? spriteVisual;
private CompositionColorBrush? backgroundBrush;
/// <summary>
@@ -35,55 +22,23 @@ public class MonoChrome : Microsoft.UI.Xaml.Controls.Control
/// </summary>
public MonoChrome()
{
SizeChanged += OnSizeChanged;
ActualThemeChanged += OnActualThemeChanged;
}
/// <summary>
/// 源
/// </summary>
public string Source
/// <inheritdoc/>
protected override SpriteVisual CompositeSpriteVisual(Compositor compositor, LoadedImageSurface imageSurface)
{
get => (string)GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
CompositionColorBrush blackLayerBursh = compositor.CreateColorBrush(Colors.Black);
CompositionSurfaceBrush imageSurfaceBrush = compositor.CompositeSurfaceBrush(imageSurface, stretch: CompositionStretch.Uniform, vRatio: 0f);
private static void OnSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs arg)
{
MonoChrome monoChrome = (MonoChrome)sender;
string url = (string)arg.NewValue;
CompositionEffectBrush overlayBrush = compositor.CompositeBlendEffectBrush(blackLayerBursh, imageSurfaceBrush, BlendEffectMode.Overlay);
CompositionEffectBrush opacityBrush = compositor.CompositeLuminanceToAlphaEffectBrush(overlayBrush);
ILogger<MonoChrome> logger = Ioc.Default.GetRequiredService<ILogger<MonoChrome>>();
monoChrome.ApplyImageAsync(url, LoadingTokenSource.Register(monoChrome)).SafeForget(logger, OnApplyImageFailed);
}
backgroundBrush = compositor.CreateColorBrush();
SetBackgroundColor(backgroundBrush);
CompositionEffectBrush alphaMaskEffectBrush = compositor.CompositeAlphaMaskEffectBrush(backgroundBrush, opacityBrush);
private static void OnApplyImageFailed(Exception exception)
{
Ioc.Default
.GetRequiredService<IInfoBarService>()
.Error(exception, "应用单色背景时发生异常");
}
private static async Task<LoadedImageSurface> 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<StorageFile?> GetCachedFileAsync(string url)
{
return Ioc.Default.GetRequiredService<IImageCache>().GetFileFromCacheAsync(new(url));
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize != e.PreviousSize && spriteVisual is not null)
{
UpdateVisual(spriteVisual);
}
return compositor.CompositeSpriteVisual(alphaMaskEffectBrush);
}
private void OnActualThemeChanged(FrameworkElement sender, object args)
@@ -94,14 +49,6 @@ public class MonoChrome : Microsoft.UI.Xaml.Controls.Control
}
}
private void UpdateVisual(SpriteVisual spriteVisual)
{
if (spriteVisual is not null)
{
spriteVisual.Size = ActualSize;
}
}
private void SetBackgroundColor(CompositionColorBrush backgroundBrush)
{
ApplicationTheme theme = ActualTheme switch
@@ -118,32 +65,4 @@ public class MonoChrome : Microsoft.UI.Xaml.Controls.Control
_ => 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);
}
}
}

View File

@@ -140,7 +140,6 @@ public abstract class CacheBase<T>
/// <returns>a StorageFile</returns>
public async Task<StorageFile> GetFileFromCacheAsync(Uri uri)
{
logger.LogInformation(EventIds.FileCaching, "Begin caching for [{uri}]", uri);
StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
string fileName = GetCacheFileName(uri);
@@ -196,12 +195,16 @@ public abstract class CacheBase<T>
private async Task DownloadFileAsync(Uri uri, StorageFile baseFile)
{
logger.LogInformation(EventIds.FileCaching, "Begin downloading for {uri}", uri);
using (Stream httpStream = await httpClient.GetStreamAsync(uri))
{
using (Stream fs = await baseFile.OpenStreamForWriteAsync())
using (Stream fileStream = await baseFile.OpenStreamForWriteAsync())
{
await httpStream.CopyToAsync(fs);
await fs.FlushAsync();
await httpStream.CopyToAsync(fileStream);
// Call this before dispose fileStream.
await fileStream.FlushAsync();
}
}
}

View File

@@ -35,6 +35,11 @@ internal static class EventIds
/// </summary>
public static readonly EventId CacheException = 100004;
/// <summary>
/// Xaml绑定错误
/// </summary>
public static readonly EventId XamlBindingError = 100005;
// 服务
/// <summary>

View File

@@ -0,0 +1,32 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Windows.AppLifecycle;
using Windows.ApplicationModel.Activation;
namespace Snap.Hutao.Extension;
/// <summary>
/// <see cref="AppActivationArguments"/> 扩展
/// </summary>
public static class AppActivationArgumentsExtensions
{
/// <summary>
/// 尝试获取协议启动的Uri
/// </summary>
/// <param name="activatedEventArgs">应用程序激活参数</param>
/// <param name="uri">协议Uri</param>
/// <returns>是否存在协议Uri</returns>
public static bool TryGetProtocolActivatedUri(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out Uri? uri)
{
uri = null;
if (activatedEventArgs.Kind == ExtendedActivationKind.Protocol)
{
IProtocolActivatedEventArgs protocolArgs = (activatedEventArgs.Data as IProtocolActivatedEventArgs)!;
uri = protocolArgs.Uri;
return true;
}
return false;
}
}

View File

@@ -20,3 +20,4 @@ public static class PackageVersionExtensions
return new Version(packageVersion.Major, packageVersion.Minor, packageVersion.Build, packageVersion.Revision);
}
}

View File

@@ -4,6 +4,7 @@
using Microsoft.Extensions.Caching.Memory;
using Snap.Hutao.Context.FileSystem;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Model.Metadata.Achievement;
using Snap.Hutao.Model.Metadata.Avatar;
@@ -80,7 +81,7 @@ internal class MetadataService : IMetadataService, IMetadataInitializer, ISuppor
public async Task InitializeInternalAsync(CancellationToken token = default)
{
logger.LogInformation(EventIds.MetadataInitialization, "Metadata initializaion begin");
ValueStopwatch stopwatch = ValueStopwatch.StartNew();
IsInitialized = await TryUpdateMetadataAsync(token)
.ConfigureAwait(false);

View File

@@ -24,7 +24,7 @@
<AppxBundle>Never</AppxBundle>
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
<StartupObject>Snap.Hutao.Program</StartupObject>
<DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
<DefineConstants>DISABLE_XAML_GENERATED_MAIN;DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION;DISABLE_XAML_GENERATED_BINDING_DEBUG_OUTPUT</DefineConstants>
</PropertyGroup>
<ItemGroup>

View File

@@ -16,7 +16,6 @@
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shcc="using:Snap.Hutao.Control.Cancellable"
xmlns:shci="using:Snap.Hutao.Control.Image"
xmlns:shvc="using:Snap.Hutao.View.Converter"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<mxi:Interaction.Behaviors>
@@ -24,15 +23,14 @@
</mxi:Interaction.Behaviors>
<shcc:CancellablePage.Resources>
<cwuconv:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
<shc:BindingProxy x:Key="BindingProxy" DataContext="{Binding}"/>
</shcc:CancellablePage.Resources>
<Grid>
<ScrollViewer Padding="0,0,4,0">
<ItemsControl
HorizontalAlignment="Stretch"
ItemsSource="{Binding Announcement.List}"
ItemsSource="{Binding Announcement.List}"
Padding="0"
Margin="12,12,0,-12">
<ItemsControl.ItemTemplate>
@@ -50,7 +48,7 @@
ItemsSource="{Binding List}"
Margin="0,0,2,0">
<cwucont:AdaptiveGridView.ItemContainerStyle>
<Style BasedOn="{StaticResource DefaultGridViewItemStyle}" TargetType="GridViewItem">
<Style TargetType="GridViewItem" BasedOn="{StaticResource DefaultGridViewItemStyle}">
<Setter Property="Margin" Value="0,0,12,12"/>
</Style>
</cwucont:AdaptiveGridView.ItemContainerStyle>
@@ -66,8 +64,7 @@
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<!--Image Layer-->
<Border
cwu:UIElementExtensions.ClipToBounds="True">
<Border cwu:UIElementExtensions.ClipToBounds="True">
<Border
VerticalAlignment="Top"
cwu:VisualExtensions.NormalizedCenterPoint="0.5">
@@ -77,11 +74,6 @@
<shci:CachedImage
Stretch="UniformToFill"
Source="{Binding Banner}"/>
<!--<Border.Background>
<ImageBrush
ImageSource="{Binding Banner}"
Stretch="UniformToFill"/>
</Border.Background>-->
<cwua:Explicit.Animations>
<cwua:AnimationSet x:Name="ImageZoomInAnimation">
<shca:ImageZoomInAnimation/>
@@ -149,6 +141,7 @@
Opacity="0.8"
Margin="4,4,4,4"
Style="{StaticResource CaptionTextBlockStyle}"
Visibility="{Binding ShouldShowTimeDescription,Converter={StaticResource BoolToVisibilityConverter}}"
Text="{Binding TimeDescription}" />
</Grid>
</StackPanel>

View File

@@ -5,17 +5,17 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sc="using:SettingsUI.Controls"
xmlns:shv="using:Snap.Hutao.ViewModel"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
d:DataContext="{d:DesignInstance shv:SettingViewModel}">
<Page.Resources>
<Style TargetType="Expander">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Style TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}">
<Setter Property="MinWidth" Value="160"/>
</Style>
</Page.Resources>
<ScrollViewer>
<StackPanel
Margin="32,0,24,0">
<StackPanel Margin="32,0,24,0">
<sc:SettingsGroup Header="关于 胡桃">
<sc:SettingExpander>
@@ -35,7 +35,25 @@
CornerRadius="0,0,4,4"/>
</sc:SettingExpander>
</sc:SettingsGroup>
<sc:SettingsGroup Header="测试功能">
<sc:Setting
Icon="&#xEC25;"
Header="打开 数据 文件夹">
<sc:Setting.ActionContent>
<Button Content="打开" Command="{Binding Experimental.OpenDataFolderCommand}"/>
</sc:Setting.ActionContent>
</sc:Setting>
<sc:Setting
Icon="&#xE8B7;"
Header="打开 缓存 文件夹">
<sc:Setting.ActionContent>
<Button Content="打开" Command="{Binding Experimental.OpenCacheFolderCommand}"/>
</sc:Setting.ActionContent>
</sc:Setting>
</sc:SettingsGroup>
</StackPanel>
</ScrollViewer>
</Page>

View File

@@ -183,6 +183,7 @@
<shci:Gradient
VerticalAlignment="Top"
Source="{Binding Selected,Converter={StaticResource AvatarNameCardPicConverter}}"/>
<ScrollViewer>
<StackPanel Margin="0,0,16,16">
<!--简介-->
@@ -394,6 +395,7 @@
</ScrollViewer>
</Expander>
<TextBlock Text="天赋" Style="{StaticResource BaseTextBlockStyle}" Margin="16,16,0,0"/>
<ItemsControl
ItemsSource="{Binding Selected.SkillDepot.Skills}"

View File

@@ -0,0 +1,47 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Factory.Abstraction;
using Windows.Storage;
using Windows.System;
namespace Snap.Hutao.ViewModel;
/// <summary>
/// 实验性功能视图模型
/// </summary>
[Injection(InjectAs.Transient)]
internal class ExperimentalFeaturesViewModel : ObservableObject
{
/// <summary>
/// 构造一个新的实验性功能视图模型
/// </summary>
/// <param name="asyncRelayCommandFactory">异步命令工厂</param>
public ExperimentalFeaturesViewModel(IAsyncRelayCommandFactory asyncRelayCommandFactory)
{
OpenCacheFolderCommand = asyncRelayCommandFactory.Create(OpenCacheFolderAsync);
OpenDataFolderCommand = asyncRelayCommandFactory.Create(OpenDataFolderAsync);
}
/// <summary>
/// 打开临时文件夹命令
/// </summary>
public ICommand OpenCacheFolderCommand { get; }
/// <summary>
/// 打开数据文件夹命令
/// </summary>
public ICommand OpenDataFolderCommand { get; }
private Task OpenCacheFolderAsync()
{
return Launcher.LaunchFolderAsync(App.AppData.TemporaryFolder).AsTask();
}
private async Task OpenDataFolderAsync()
{
StorageFolder folder = await KnownFolders.DocumentsLibrary.GetFolderAsync("Hutao").AsTask().ConfigureAwait(false);
await Launcher.LaunchFolderAsync(folder).AsTask().ConfigureAwait(false);
}
}

View File

@@ -1,18 +1,27 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
namespace Snap.Hutao.ViewModel;
/// <summary>
/// 测试视图模型
/// </summary>
[Injection(InjectAs.Transient)]
internal class SettingViewModel
internal class SettingViewModel : ObservableObject
{
/// <summary>
/// 构造一个新的测试视图模型
/// </summary>
public SettingViewModel()
/// <param name="experimental">实验性功能</param>
public SettingViewModel(ExperimentalFeaturesViewModel experimental)
{
Experimental = experimental;
}
}
/// <summary>
/// 实验性功能
/// </summary>
public ExperimentalFeaturesViewModel Experimental { get; }
}