Skip to content

Commit

Permalink
fix #2101
Browse files Browse the repository at this point in the history
  • Loading branch information
Lightczx committed Oct 28, 2024
1 parent cc16aa2 commit a940ae5
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 55 deletions.
14 changes: 12 additions & 2 deletions src/Snap.Hutao/Snap.Hutao/Core/Threading/TaskContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,34 @@
// Licensed under the MIT license.

using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;

namespace Snap.Hutao.Core.Threading;

[Injection(InjectAs.Singleton, typeof(ITaskContext))]
internal sealed class TaskContext : ITaskContext, ITaskContextUnsafe
{
private readonly DispatcherQueueSynchronizationContext synchronizationContext;
private readonly DispatcherQueue dispatcherQueue;

public TaskContext()
{
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
synchronizationContext = new(dispatcherQueue);
DispatcherQueueSynchronizationContext synchronizationContext = new(dispatcherQueue);
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
}

public DispatcherQueue DispatcherQueue { get => dispatcherQueue; }

public static ITaskContext GetForDependencyObject(DependencyObject dependencyObject)
{
return GetForDispatcherQueue(dependencyObject.DispatcherQueue);
}

public static ITaskContext GetForDispatcherQueue(DispatcherQueue dispatcherQueue)
{
return new TaskContextWrapperForDispatcherQueue(dispatcherQueue);
}

public ThreadPoolSwitchOperation SwitchToBackgroundAsync()
{
return new(dispatcherQueue);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.

using Microsoft.UI.Dispatching;

namespace Snap.Hutao.Core.Threading;

internal sealed class TaskContextWrapperForDispatcherQueue : ITaskContext
{
private readonly DispatcherQueue dispatcherQueue;

public TaskContextWrapperForDispatcherQueue(DispatcherQueue dispatcherQueue)
{
this.dispatcherQueue = dispatcherQueue;
}

public void BeginInvokeOnMainThread(Action action)
{
dispatcherQueue.TryEnqueue(() => action());
}

public void InvokeOnMainThread(Action action)
{
dispatcherQueue.Invoke(action);
}

public T InvokeOnMainThread<T>(Func<T> action)
{
return dispatcherQueue.Invoke(action);
}

public ThreadPoolSwitchOperation SwitchToBackgroundAsync()
{
return new(dispatcherQueue);
}

public DispatcherQueueSwitchOperation SwitchToMainThreadAsync()
{
return new(dispatcherQueue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ protected override void OnAssociatedObjectLoaded()

private void OnActualThemeChanged(FrameworkElement sender, object args)
{
if (shouldReactToActualThemeChange)
{
acutalThemeChangedCts.Cancel();
}
acutalThemeChangedCts.Cancel();
}

private void TryExecuteCommand()
Expand All @@ -70,17 +67,14 @@ private async Task RunCoreAsync()
break;
}

// TODO: Reconsider approach to get the ServiceProvider
ITaskContext taskContext = Ioc.Default.GetRequiredService<ITaskContext>();
await taskContext.SwitchToMainThreadAsync();
TryExecuteCommand();

ITaskContext taskContext = TaskContext.GetForDispatcherQueue(AssociatedObject.DispatcherQueue);
await taskContext.SwitchToBackgroundAsync();
try
{
using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(acutalThemeChangedCts.Token, periodicTimerStopCts.Token))
{
await timer.WaitForNextTickAsync(linkedCts.Token).ConfigureAwait(false);
taskContext.BeginInvokeOnMainThread(TryExecuteCommand);
}
}
catch (OperationCanceledException)
Expand All @@ -91,8 +85,6 @@ private async Task RunCoreAsync()
}
}

shouldReactToActualThemeChange = true;

acutalThemeChangedCts.Dispose();
acutalThemeChangedCts = new();
periodicTimerStopCts.Dispose();
Expand Down
4 changes: 0 additions & 4 deletions src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/MainView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@

namespace Snap.Hutao.UI.Xaml.View;

/// <summary>
/// 主视图
/// </summary>
[HighQuality]
internal sealed partial class MainView : UserControl
{
private readonly INavigationService navigationService;
Expand Down
81 changes: 43 additions & 38 deletions src/Snap.Hutao/Snap.Hutao/ViewModel/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Singleton)]
internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewModelInitialization
{
private readonly AsyncLock backgroundImageLock = new();

private readonly IBackgroundImageService backgroundImageService;
private readonly ILogger<MainViewModel> logger;
private readonly ITaskContext taskContext;
Expand Down Expand Up @@ -65,45 +67,48 @@ private async Task UpdateBackgroundCoreAsync(bool forceRefresh)
return;
}

(bool shouldRefresh, BackgroundImage? backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync(forceRefresh ? default : previousBackgroundImage).ConfigureAwait(false);

if (shouldRefresh)
using (await backgroundImageLock.LockAsync().ConfigureAwait(false))
{
previousBackgroundImage = backgroundImage;
await taskContext.SwitchToMainThreadAsync();

await AnimationBuilder
.Create()
.Opacity(
to: 0D,
duration: Constants.ImageOpacityFadeInOut,
easingType: EasingType.Quartic,
easingMode: EasingMode.EaseInOut)
.StartAsync(backgroundImagePresenter)
.ConfigureAwait(true);

backgroundImagePresenter.Source = backgroundImage?.ImageSource;
double targetOpacity = backgroundImage is not null
? ThemeHelper.IsDarkMode(backgroundImagePresenter.ActualTheme)
? 1 - backgroundImage.Luminance
: backgroundImage.Luminance
: 0;

logger.LogInformation(
"Background image: [Accent color: {AccentColor}] [Luminance: {Luminance}] [Opacity: {TargetOpacity}]",
backgroundImage?.AccentColor.ToString(CultureInfo.CurrentCulture),
backgroundImage?.Luminance,
targetOpacity);

await AnimationBuilder
.Create()
.Opacity(
to: targetOpacity,
duration: Constants.ImageOpacityFadeInOut,
easingType: EasingType.Quartic,
easingMode: EasingMode.EaseInOut)
.StartAsync(backgroundImagePresenter)
.ConfigureAwait(true);
(bool shouldRefresh, BackgroundImage? backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync(forceRefresh ? default : previousBackgroundImage).ConfigureAwait(false);

if (shouldRefresh)
{
previousBackgroundImage = backgroundImage;
await taskContext.SwitchToMainThreadAsync();

await AnimationBuilder
.Create()
.Opacity(
to: 0D,
duration: Constants.ImageOpacityFadeInOut,
easingType: EasingType.Quartic,
easingMode: EasingMode.EaseInOut)
.StartAsync(backgroundImagePresenter)
.ConfigureAwait(true);

backgroundImagePresenter.Source = backgroundImage?.ImageSource;
double targetOpacity = backgroundImage is not null
? ThemeHelper.IsDarkMode(backgroundImagePresenter.ActualTheme)
? 1 - backgroundImage.Luminance
: backgroundImage.Luminance
: 0;

logger.LogInformation(
"Background image: [Accent color: {AccentColor}] [Luminance: {Luminance}] [Opacity: {TargetOpacity}]",
backgroundImage?.AccentColor,
backgroundImage?.Luminance,
targetOpacity);

await AnimationBuilder
.Create()
.Opacity(
to: targetOpacity,
duration: Constants.ImageOpacityFadeInOut,
easingType: EasingType.Quartic,
easingMode: EasingMode.EaseInOut)
.StartAsync(backgroundImagePresenter)
.ConfigureAwait(true);
}
}
}
}

0 comments on commit a940ae5

Please sign in to comment.