diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Image/CompositionImage.cs b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Image/CompositionImage.cs index 6dda8189..5ac652f5 100644 --- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Image/CompositionImage.cs +++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Image/CompositionImage.cs @@ -10,6 +10,7 @@ using Snap.Hutao.Core.Caching; using Snap.Hutao.Core.Graphics; using Snap.Hutao.Service.Notification; using Snap.Hutao.UI.Xaml.Media.Animation; +using System.Diagnostics; using System.IO; using System.Net.Http; using System.Runtime.InteropServices; @@ -17,11 +18,6 @@ using Windows.Foundation; namespace Snap.Hutao.UI.Xaml.Control.Image; -/// -/// 合成图像控件 -/// 为其他图像类控件提供基类 -/// -[HighQuality] [DependencyProperty("EnableShowHideAnimation", typeof(bool), true)] [DependencyProperty("Source", typeof(Uri), default!, nameof(OnSourceChanged))] internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Control @@ -30,48 +26,38 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co private readonly IServiceProvider serviceProvider; - private readonly SizeChangedEventHandler sizeChangedEventHandler; - private readonly TypedEventHandler loadedImageSourceLoadCompletedEventHandler; - private TaskCompletionSource? surfaceLoadTaskCompletionSource; private SpriteVisual? spriteVisual; private bool isShow = true; - /// - /// 构造一个新的单色图像 - /// protected CompositionImage() { serviceProvider = this.ServiceProvider(); this.DisableInteraction(); - sizeChangedEventHandler = OnSizeChanged; - SizeChanged += sizeChangedEventHandler; - - loadedImageSourceLoadCompletedEventHandler = OnLoadImageSurfaceLoadCompleted; + SizeChanged += OnSizeChanged; } - /// - /// 合成组合视觉 - /// - /// 合成器 - /// 图像表面 - /// 拼合视觉 protected abstract SpriteVisual CompositeSpriteVisual(Compositor compositor, LoadedImageSurface imageSurface); protected virtual void LoadImageSurfaceCompleted(LoadedImageSurface surface) { } - /// - /// 更新视觉对象 - /// - /// 拼合视觉 protected virtual void UpdateVisual(SpriteVisual spriteVisual) { spriteVisual.Size = ActualSize; } + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + if (!string.IsNullOrEmpty(Source.OriginalString)) + { + OnSourceChangedCore(Source); + } + } + private static void OnSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs arg) { CompositionImage image = (CompositionImage)sender; @@ -87,9 +73,7 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co // value is different from old one if (inner != (arg.OldValue as Uri)) { - image - .ApplyImageAsync(inner, token) - .SafeForget(logger, ex => OnApplyImageFailed(serviceProvider, inner, ex)); + image.OnSourceChangedCore(inner); } } else @@ -101,6 +85,7 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co private static void OnApplyImageFailed(IServiceProvider serviceProvider, Uri? uri, Exception exception) { + Debugger.Break(); IInfoBarService infoBarService = serviceProvider.GetRequiredService(); if (exception is HttpRequestException httpRequestException) @@ -117,6 +102,13 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co } } + private void OnSourceChangedCore(Uri? uri) + { + CancellationToken token = loadingTokenSource.Register(); + ILogger logger = serviceProvider.GetRequiredService>(); + ApplyImageAsync(uri, token).SafeForget(logger, ex => OnApplyImageFailed(serviceProvider, uri, ex)); + } + private async ValueTask ApplyImageAsync(Uri? uri, CancellationToken token) { await HideAsync(token).ConfigureAwait(true); @@ -154,21 +146,28 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co await ShowAsync(token).ConfigureAwait(true); } } + else + { + Debugger.Break(); + } } } private async ValueTask LoadImageSurfaceAsync(string file, CancellationToken token) { + token.ThrowIfCancellationRequested(); + TaskCompletionSource cancelTcs = new(); + CancellationTokenRegistration registration = token.Register(() => cancelTcs.TrySetResult(), false); + surfaceLoadTaskCompletionSource = new(); LoadedImageSurface? surface = default; try { surface = LoadedImageSurface.StartLoadFromUri(file.ToUri()); - surface.LoadCompleted += loadedImageSourceLoadCompletedEventHandler; + surface.LoadCompleted += OnLoadImageSurfaceLoadCompleted; if (surface.DecodedPhysicalSize.Size() <= 0D) { - await Task.WhenAny(surfaceLoadTaskCompletionSource.Task, Task.Delay(5000, token)).ConfigureAwait(true); - await Task.Delay(50, token).ConfigureAwait(true); + await Task.WhenAny(surfaceLoadTaskCompletionSource.Task, cancelTcs.Task).ConfigureAwait(true); } LoadImageSurfaceCompleted(surface); @@ -178,8 +177,10 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co { if (surface is not null) { - surface.LoadCompleted -= loadedImageSourceLoadCompletedEventHandler; + surface.LoadCompleted -= OnLoadImageSurfaceLoadCompleted; } + + registration.Dispose(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Image/MonoChrome.cs b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Image/MonoChrome.cs index 0c016816..747efbb4 100644 --- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Image/MonoChrome.cs +++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Image/MonoChrome.cs @@ -12,26 +12,15 @@ using Windows.Foundation; namespace Snap.Hutao.UI.Xaml.Control.Image; -/// -/// 支持单色的图像 -/// -[HighQuality] internal sealed class MonoChrome : CompositionImage { - private readonly TypedEventHandler actualThemeChangedEventHandler; - private CompositionColorBrush? backgroundBrush; - /// - /// 构造一个新的单色图像 - /// public MonoChrome() { - actualThemeChangedEventHandler = OnActualThemeChanged; - ActualThemeChanged += actualThemeChangedEventHandler; + ActualThemeChanged += OnActualThemeChanged; } - /// protected override SpriteVisual CompositeSpriteVisual(Compositor compositor, LoadedImageSurface imageSurface) { CompositionColorBrush blackLayerBrush = compositor.CreateColorBrush(Colors.Black); diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Layout/UniformStaggeredLayout.cs b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Layout/UniformStaggeredLayout.cs index db8bc6ab..cf505acb 100644 --- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Layout/UniformStaggeredLayout.cs +++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/Layout/UniformStaggeredLayout.cs @@ -188,6 +188,7 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout } UniformStaggeredLayoutState state = (UniformStaggeredLayoutState)context.LayoutState; + int virtualColumnCount = (int)(finalSize.Width / state.ColumnWidth); // Cycle through each column and arrange the items that are within the realization bounds for (int columnIndex = 0; columnIndex < state.NumberOfColumns; columnIndex++) @@ -204,9 +205,9 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout // Partial or fully in the view if (item.Top <= context.RealizationRect.Bottom) { - double itemHorizontalOffset = (state.ColumnWidth * columnIndex) + (MinColumnSpacing * columnIndex); + double itemHorizontalOffset = (state.ColumnWidth + MinColumnSpacing) * columnIndex; - double width = columnIndex == state.NumberOfColumns - 1 + double width = columnIndex == virtualColumnCount - 1 ? finalSize.Width - itemHorizontalOffset : state.ColumnWidth; diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/SizeRestrictedContentControl.cs b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/SizeRestrictedContentControl.cs index f9ca831d..ec9dd5a2 100644 --- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/SizeRestrictedContentControl.cs +++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/SizeRestrictedContentControl.cs @@ -20,9 +20,20 @@ internal sealed partial class SizeRestrictedContentControl : ContentControl { element.Measure(availableSize); Size contentDesiredSize = element.DesiredSize; + Size contentActualOrDesiredSize = new( - Math.Min(Math.Max(element.ActualWidth, contentDesiredSize.Width), availableSize.Width), - Math.Min(Math.Max(element.ActualHeight, contentDesiredSize.Height), availableSize.Height)); + Math.Clamp(element.ActualWidth, contentDesiredSize.Width, availableSize.Width), + Math.Clamp(element.ActualHeight, contentDesiredSize.Height, availableSize.Height)); + + if (minContentWidth > availableSize.Width) + { + minContentWidth = 0; + } + + if (minContentHeight > availableSize.Height) + { + minContentHeight = 0; + } if (IsWidthRestricted) { diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/AnnouncementPage.xaml b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/AnnouncementPage.xaml index 95fbb711..60c18100 100644 --- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/AnnouncementPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/AnnouncementPage.xaml @@ -154,7 +154,7 @@ ItemsSource="{Binding List}"> - + @@ -547,7 +550,7 @@ ItemsSource="{Binding DailyNoteEntries}"> - + + + + @@ -524,19 +527,21 @@ - - - - - - - + + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/WikiWeaponPage.xaml b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/WikiWeaponPage.xaml index 78e85aec..5d4a3fd3 100644 --- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/WikiWeaponPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/WikiWeaponPage.xaml @@ -91,7 +91,7 @@ - + - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + HorizontalAlignment="Center" + VerticalAlignment="Stretch" + Source="{Binding Selected.Icon, Converter={StaticResource GachaEquipIconConverter}}"/> - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserInfo.cs index 0f5d43a8..340ff1fc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserInfo.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserInfo.cs @@ -104,9 +104,10 @@ internal sealed class UserInfo { get { - string source = AvatarUrl.OriginalString; + string? source = AvatarUrl?.OriginalString; if (!string.IsNullOrEmpty(source)) { + ArgumentNullException.ThrowIfNull(AvatarUrl); return AvatarUrl; }