From 30c055e7bae2255b64c32ae88ea4e1689a92ecd0 Mon Sep 17 00:00:00 2001
From: Lightczx <1686188646@qq.com>
Date: Wed, 21 Feb 2024 11:57:24 +0800
Subject: [PATCH] impl #1373 #1389
---
.../Animation/ControlAnimationConstants.cs | 6 +-
...keCommandOrOnActualThemeChangedBehavior.cs | 86 +++++++++++++++++++
.../Control/Image/CompositionImage.cs | 4 +-
.../Snap.Hutao/Model/CollectionsNameValue.cs | 10 ++-
.../Snap.Hutao/Resource/Localization/SH.resx | 21 +++++
.../Snap.Hutao/Service/AppOptions.cs | 2 +
.../BackgroundImage/BackgroundImageService.cs | 68 +++++++++------
.../BackgroundImage/BackgroundImageType.cs | 10 +++
src/Snap.Hutao/Snap.Hutao/View/MainView.xaml | 12 +++
.../Snap.Hutao/View/MainView.xaml.cs | 69 ++-------------
.../Snap.Hutao/View/Page/SettingPage.xaml | 11 +++
.../Snap.Hutao/ViewModel/MainViewModel.cs | 56 ++++++++++++
.../ViewModel/Setting/SettingViewModel.cs | 14 +++
.../Web/Hutao/Wallpaper/Wallpaper.cs | 2 +-
14 files changed, 275 insertions(+), 96 deletions(-)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Control/Behavior/PeriodicInvokeCommandOrOnActualThemeChangedBehavior.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/ViewModel/MainViewModel.cs
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Animation/ControlAnimationConstants.cs b/src/Snap.Hutao/Snap.Hutao/Control/Animation/ControlAnimationConstants.cs
index e748911f..3c86dca8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Animation/ControlAnimationConstants.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Animation/ControlAnimationConstants.cs
@@ -23,10 +23,12 @@ internal static class ControlAnimationConstants
///
/// 图像淡入
///
- public static readonly TimeSpan ImageFadeIn = TimeSpan.FromSeconds(0.3);
+ public static readonly TimeSpan ImageScaleFadeIn = TimeSpan.FromSeconds(0.3);
///
/// 图像淡出
///
- public static readonly TimeSpan ImageFadeOut = TimeSpan.FromSeconds(0.2);
+ public static readonly TimeSpan ImageScaleFadeOut = TimeSpan.FromSeconds(0.2);
+
+ public static readonly TimeSpan ImageOpacityFadeInOut = TimeSpan.FromSeconds(1);
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Behavior/PeriodicInvokeCommandOrOnActualThemeChangedBehavior.cs b/src/Snap.Hutao/Snap.Hutao/Control/Behavior/PeriodicInvokeCommandOrOnActualThemeChangedBehavior.cs
new file mode 100644
index 00000000..9cfd7a1a
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Behavior/PeriodicInvokeCommandOrOnActualThemeChangedBehavior.cs
@@ -0,0 +1,86 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.WinUI.Behaviors;
+using Microsoft.UI.Xaml;
+
+namespace Snap.Hutao.Control.Behavior;
+
+[DependencyProperty("Period", typeof(TimeSpan))]
+[DependencyProperty("Command", typeof(ICommand))]
+[DependencyProperty("CommandParameter", typeof(object))]
+internal sealed partial class PeriodicInvokeCommandOrOnActualThemeChangedBehavior : BehaviorBase, IDisposable
+{
+ private TaskCompletionSource acutalThemeChangedTaskCompletionSource = new();
+ private CancellationTokenSource periodicTimerCancellationTokenSource = new();
+
+ public void Dispose()
+ {
+ periodicTimerCancellationTokenSource.Dispose();
+ }
+
+ protected override bool Initialize()
+ {
+ AssociatedObject.ActualThemeChanged += OnActualThemeChanged;
+ return true;
+ }
+
+ protected override void OnAssociatedObjectLoaded()
+ {
+ RunCoreAsync().SafeForget();
+ }
+
+ protected override bool Uninitialize()
+ {
+ AssociatedObject.ActualThemeChanged -= OnActualThemeChanged;
+ return true;
+ }
+
+ private void OnActualThemeChanged(FrameworkElement sender, object args)
+ {
+ acutalThemeChangedTaskCompletionSource.TrySetResult();
+ periodicTimerCancellationTokenSource.Cancel();
+ }
+
+ private void TryExecuteCommand()
+ {
+ if (AssociatedObject is null)
+ {
+ return;
+ }
+
+ if (Command is not null && Command.CanExecute(CommandParameter))
+ {
+ Command.Execute(CommandParameter);
+ }
+ }
+
+ private async ValueTask RunCoreAsync()
+ {
+ using (PeriodicTimer timer = new(Period))
+ {
+ do
+ {
+ if (!IsAttached)
+ {
+ break;
+ }
+
+ TryExecuteCommand();
+
+ try
+ {
+ Task nextTickTask = timer.WaitForNextTickAsync(periodicTimerCancellationTokenSource.Token).AsTask();
+ await Task.WhenAny(nextTickTask, acutalThemeChangedTaskCompletionSource.Task).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ }
+
+ acutalThemeChangedTaskCompletionSource = new();
+ periodicTimerCancellationTokenSource = new();
+ }
+ while (true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs
index b35ceec4..270d2333 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs
@@ -192,7 +192,7 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
{
await AnimationBuilder
.Create()
- .Opacity(from: 0D, to: 1D, duration: ControlAnimationConstants.ImageFadeIn)
+ .Opacity(from: 0D, to: 1D, duration: ControlAnimationConstants.ImageScaleFadeIn)
.StartAsync(this, token)
.ConfigureAwait(true);
}
@@ -213,7 +213,7 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
{
await AnimationBuilder
.Create()
- .Opacity(from: 1D, to: 0D, duration: ControlAnimationConstants.ImageFadeOut)
+ .Opacity(from: 1D, to: 0D, duration: ControlAnimationConstants.ImageScaleFadeOut)
.StartAsync(this, token)
.ConfigureAwait(true);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/CollectionsNameValue.cs b/src/Snap.Hutao/Snap.Hutao/Model/CollectionsNameValue.cs
index 92c799ae..605289e3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/CollectionsNameValue.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/CollectionsNameValue.cs
@@ -11,10 +11,16 @@ internal static class CollectionsNameValue
return [.. Enum.GetValues().Select(x => new NameValue(x.ToString(), x))];
}
- public static List> FromEnum(Func codiction)
+ public static List> FromEnum(Func condition)
where TEnum : struct, Enum
{
- return [.. Enum.GetValues().Where(codiction).Select(x => new NameValue(x.ToString(), x))];
+ return [.. Enum.GetValues().Where(condition).Select(x => new NameValue(x.ToString(), x))];
+ }
+
+ public static List> FromEnum(Func nameSelector)
+ where TEnum : struct, Enum
+ {
+ return [.. Enum.GetValues().Select(x => new NameValue(nameSelector(x), x))];
}
public static List> From(IEnumerable sources, Func nameSelector)
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
index edd66c79..8572ee15 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
@@ -767,6 +767,21 @@
角色橱窗:{0:MM-dd HH:mm}
+
+ 必应每日一图
+
+
+ 胡桃每日一图
+
+
+ 官方启动器壁纸
+
+
+ 本地随机图片
+
+
+ 无背景图片
+
保存养成计划状态失败
@@ -2339,6 +2354,12 @@
背景材质
+
+ 更改窗体的背景图片来源,重启胡桃以尽快生效
+
+
+ 背景图片
+
图片缓存 在此处存放
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
index 1a13e560..43ea7cd5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
@@ -34,6 +34,8 @@ internal sealed partial class AppOptions : DbStoreOptions
set => SetOption(ref backdropType, SettingEntry.SystemBackdropType, value, EnumToStringOrEmpty);
}
+ public List> BackgroundImageTypes { get; } = CollectionsNameValue.FromEnum(type => type.GetLocalizedDescription());
+
public BackgroundImageType BackgroundImageType
{
get => GetOption(ref backgroundImageType, SettingEntry.BackgroundImageType, EnumParse, BackgroundImageType.HutaoOfficialLauncher).Value;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/BackgroundImage/BackgroundImageService.cs b/src/Snap.Hutao/Snap.Hutao/Service/BackgroundImage/BackgroundImageService.cs
index 6217450a..530eca28 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/BackgroundImage/BackgroundImageService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/BackgroundImage/BackgroundImageService.cs
@@ -5,9 +5,7 @@ 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.Hutao.Wallpaper;
using Snap.Hutao.Web.Response;
using System.IO;
using Windows.Graphics.Imaging;
@@ -23,8 +21,9 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
private readonly IServiceProvider serviceProvider;
private readonly RuntimeOptions runtimeOptions;
private readonly ITaskContext taskContext;
+ private readonly AppOptions appOptions;
- private HashSet backgroundPathSet;
+ private HashSet currentBackgroundPathSet;
public async ValueTask> GetNextBackgroundImageAsync(BackgroundImage? previous)
{
@@ -35,7 +34,7 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
return new(false, default!);
}
- string path = System.Random.Shared.GetItems(backgroundSet.ToArray(), 1)[0];
+ string path = System.Random.Shared.GetItems([..backgroundSet], 1)[0];
backgroundSet.Remove(path);
if (string.Equals(path, previous?.Path, StringComparison.OrdinalIgnoreCase))
@@ -65,32 +64,47 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
private async ValueTask> SkipOrInitBackgroundAsync()
{
- if (backgroundPathSet is not { Count: > 0 })
+ switch (appOptions.BackgroundImageType)
{
- 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();
- string launguageCode = serviceProvider.GetRequiredService().LanguageCode;
- LaunchScheme scheme = launguageCode is "zh-cn"
- ? KnownLaunchSchemes.Get().First(scheme => !scheme.IsOversea && scheme.IsNotCompatOnly)
- : KnownLaunchSchemes.Get().First(scheme => scheme.IsOversea && scheme.IsNotCompatOnly);
- Response response = await resourceClient.GetContentAsync(scheme, launguageCode).ConfigureAwait(false);
- if (response is { Data.Advertisement.Background: string url })
+ case BackgroundImageType.LocalFolder:
{
- ValueFile file = await serviceProvider.GetRequiredService().GetFileFromCacheAsync(url.ToUri()).ConfigureAwait(false);
- backgroundPathSet = [file];
+ if (currentBackgroundPathSet is not { Count: > 0 })
+ {
+ string backgroundFolder = runtimeOptions.GetDataFolderBackgroundFolder();
+ Directory.CreateDirectory(backgroundFolder);
+
+ currentBackgroundPathSet = Directory
+ .GetFiles(backgroundFolder, "*.*", SearchOption.AllDirectories)
+ .Where(path => AllowedFormats.Contains(Path.GetExtension(path)))
+ .ToHashSet();
+ }
+
+ break;
}
- }
+
+ case BackgroundImageType.HutaoBing:
+ await SetCurrentBackgroundPathSetAsync(client => client.GetBingWallpaperAsync()).ConfigureAwait(false);
+ break;
+ case BackgroundImageType.HutaoDaily:
+ await SetCurrentBackgroundPathSetAsync(client => client.GetTodayWallpaperAsync()).ConfigureAwait(false);
+ break;
+ case BackgroundImageType.HutaoOfficialLauncher:
+ await SetCurrentBackgroundPathSetAsync(client => client.GetLauncherWallpaperAsync()).ConfigureAwait(false);
+ break;
}
- return backgroundPathSet;
+ currentBackgroundPathSet ??= [];
+ return currentBackgroundPathSet;
+
+ async Task SetCurrentBackgroundPathSetAsync(Func>> responseFactory)
+ {
+ HutaoWallpaperClient wallpaperClient = serviceProvider.GetRequiredService();
+ Response response = await responseFactory(wallpaperClient).ConfigureAwait(false);
+ if (response is { Data.Url: Uri url })
+ {
+ ValueFile file = await serviceProvider.GetRequiredService().GetFileFromCacheAsync(url).ConfigureAwait(false);
+ currentBackgroundPathSet = [file];
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/BackgroundImage/BackgroundImageType.cs b/src/Snap.Hutao/Snap.Hutao/Service/BackgroundImage/BackgroundImageType.cs
index 4eee1187..4d6f5a9e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/BackgroundImage/BackgroundImageType.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/BackgroundImage/BackgroundImageType.cs
@@ -3,11 +3,21 @@
namespace Snap.Hutao.Service.BackgroundImage;
+[Localization]
internal enum BackgroundImageType
{
+ [LocalizationKey(nameof(SH.ServiceBackgroundImageTypeNone))]
None,
+
+ [LocalizationKey(nameof(SH.ServiceBackgroundImageTypeLocalFolder))]
LocalFolder,
+
+ [LocalizationKey(nameof(SH.ServiceBackgroundImageTypeBing))]
HutaoBing,
+
+ [LocalizationKey(nameof(SH.ServiceBackgroundImageTypeDaily))]
HutaoDaily,
+
+ [LocalizationKey(nameof(SH.ServiceBackgroundImageTypeLauncher))]
HutaoOfficialLauncher,
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml
index 057659a1..2531acc4 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml
@@ -4,12 +4,24 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shch="using:Snap.Hutao.Control.Helper"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shv="using:Snap.Hutao.View"
xmlns:shvh="using:Snap.Hutao.View.Helper"
+ xmlns:shvm="using:Snap.Hutao.ViewModel"
xmlns:shvp="using:Snap.Hutao.View.Page"
+ d:DataContext="{d:DesignInstance Type=shvm:MainViewModel}"
mc:Ignorable="d">
+
+
+
+
+
0,44,0,0
0,1,0,0
diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs
index 4b4a029f..2688a1d6 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs
@@ -5,10 +5,12 @@ using CommunityToolkit.WinUI.Animations;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
+using Snap.Hutao.Control.Animation;
using Snap.Hutao.Control.Theme;
using Snap.Hutao.Service.BackgroundImage;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.View.Page;
+using Snap.Hutao.ViewModel;
namespace Snap.Hutao.View;
@@ -19,80 +21,23 @@ 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;
///
/// 构造一个新的主视图
///
public MainView()
{
+ DataContext = Ioc.Default.GetRequiredService();
InitializeComponent();
- ActualThemeChanged += OnActualThemeChanged;
-
IServiceProvider serviceProvider = Ioc.Default;
- backgroundImageService = serviceProvider.GetRequiredService();
- RunBackgroundImageLoopAsync(serviceProvider.GetRequiredService()).SafeForget();
-
navigationService = serviceProvider.GetRequiredService();
- navigationService
- .As()?
- .Initialize(NavView, ContentFrame);
+ if (navigationService is INavigationInitialization navigationInitialization)
+ {
+ navigationInitialization.Initialize(NavView, ContentFrame);
+ }
navigationService.Navigate(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();
- }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
index fceeb67f..214288e8 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
@@ -312,6 +312,17 @@
SelectedItem="{Binding SelectedBackdropType, Mode=TwoWay}"/>
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/MainViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/MainViewModel.cs
new file mode 100644
index 00000000..fa333520
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/MainViewModel.cs
@@ -0,0 +1,56 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.WinUI.Animations;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Media.Animation;
+using Snap.Hutao.Control.Animation;
+using Snap.Hutao.Control.Theme;
+using Snap.Hutao.Service.BackgroundImage;
+
+namespace Snap.Hutao.ViewModel;
+
+[ConstructorGenerated]
+[Injection(InjectAs.Singleton)]
+internal sealed partial class MainViewModel : Abstraction.ViewModel
+{
+ private readonly IBackgroundImageService backgroundImageService;
+ private readonly ITaskContext taskContext;
+
+ private BackgroundImage? previousBackgroundImage;
+
+ [Command("UpdateBackgroundCommand")]
+ private async Task UpdateBackgroundAsync(Image presenter)
+ {
+ (bool isOk, BackgroundImage backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync(previousBackgroundImage).ConfigureAwait(false);
+
+ if (isOk)
+ {
+ previousBackgroundImage = backgroundImage;
+ await taskContext.SwitchToMainThreadAsync();
+
+ await AnimationBuilder
+ .Create()
+ .Opacity(
+ to: 0D,
+ duration: ControlAnimationConstants.ImageOpacityFadeInOut,
+ easingType: EasingType.Quartic,
+ easingMode: EasingMode.EaseInOut)
+ .StartAsync(presenter)
+ .ConfigureAwait(true);
+
+ presenter.Source = backgroundImage.ImageSource;
+ double targetOpacity = ThemeHelper.IsDarkMode(presenter.ActualTheme) ? 1 - backgroundImage.Luminance : backgroundImage.Luminance;
+
+ await AnimationBuilder
+ .Create()
+ .Opacity(
+ to: targetOpacity,
+ duration: ControlAnimationConstants.ImageOpacityFadeInOut,
+ easingType: EasingType.Quartic,
+ easingMode: EasingMode.EaseInOut)
+ .StartAsync(presenter)
+ .ConfigureAwait(true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs
index 5f372957..d175ccfa 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs
@@ -12,6 +12,7 @@ using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Factory.Picker;
using Snap.Hutao.Model;
using Snap.Hutao.Service;
+using Snap.Hutao.Service.BackgroundImage;
using Snap.Hutao.Service.GachaLog.QueryProvider;
using Snap.Hutao.Service.Game;
using Snap.Hutao.Service.Hutao;
@@ -54,6 +55,7 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
private readonly AppOptions appOptions;
private NameValue? selectedBackdropType;
+ private NameValue? selectedBackgroundImageType;
private NameValue? selectedCulture;
private NameValue? selectedRegion;
private FolderViewModel? cacheFolderView;
@@ -87,6 +89,18 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
}
}
+ public NameValue? SelectedBackgroundImageType
+ {
+ get => selectedBackgroundImageType ??= AppOptions.BackgroundImageTypes.Single(t => t.Value == AppOptions.BackgroundImageType);
+ set
+ {
+ if (SetProperty(ref selectedBackgroundImageType, value) && value is not null)
+ {
+ AppOptions.BackgroundImageType = value.Value;
+ }
+ }
+ }
+
public NameValue? SelectedCulture
{
get => selectedCulture ??= CultureOptions.GetCurrentCultureForSelectionOrDefault();
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Wallpaper/Wallpaper.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Wallpaper/Wallpaper.cs
index 4ec26f2e..f160f47c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Wallpaper/Wallpaper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Wallpaper/Wallpaper.cs
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Web.Hutao.Wallpaper;
internal sealed class Wallpaper
{
[JsonPropertyName("url")]
- public string Url { get; set; } = default!;
+ public Uri Url { get; set; } = default!;
[JsonPropertyName("source_url")]
public string SourceUrl { get; set; } = default!;