From be30362b52a81647a4c71f6ba1d19677ded39465 Mon Sep 17 00:00:00 2001
From: Lightczx <1686188646@qq.com>
Date: Fri, 15 Mar 2024 17:18:25 +0800
Subject: [PATCH] uniformstaggeredlayout optimization
---
.../AutoSuggestBox/AutoSuggestTokenBox.cs | 4 +-
...mmandExtension.cs => CommandInvocation.cs} | 2 +-
.../Extension/DependencyObjectExtension.cs | 2 +
.../Snap.Hutao/Control/Image/CachedImage.cs | 7 +-
.../Control/Image/Implementation/ImageEx.cs | 11 +-
.../Image/Implementation/ImageExBase.cs | 24 +--
...DefaultItemCollectionTransitionProvider.cs | 151 ------------------
.../Control/Layout/UniformStaggeredLayout.cs | 40 ++---
.../Layout/UniformStaggeredLayoutState.cs | 75 ++++-----
.../ExceptionService/HutaoExceptionKind.cs | 1 +
.../Snap.Hutao/Win32/StructMarshal.cs | 30 +---
11 files changed, 85 insertions(+), 262 deletions(-)
rename src/Snap.Hutao/Snap.Hutao/Control/Extension/{CommandExtension.cs => CommandInvocation.cs} (90%)
delete mode 100644 src/Snap.Hutao/Snap.Hutao/Control/Layout/DefaultItemCollectionTransitionProvider.cs
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/AutoSuggestBox/AutoSuggestTokenBox.cs b/src/Snap.Hutao/Snap.Hutao/Control/AutoSuggestBox/AutoSuggestTokenBox.cs
index 05f62895..43d2e518 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/AutoSuggestBox/AutoSuggestTokenBox.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/AutoSuggestBox/AutoSuggestTokenBox.cs
@@ -59,7 +59,7 @@ internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox
return;
}
- CommandExtension.TryExecute(FilterCommand, FilterCommandParameter);
+ CommandInvocation.TryExecute(FilterCommand, FilterCommandParameter);
}
private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
@@ -74,6 +74,6 @@ internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox
private void OnTokenItemModified(TokenizingTextBox sender, object args)
{
- CommandExtension.TryExecute(FilterCommand, FilterCommandParameter);
+ CommandInvocation.TryExecute(FilterCommand, FilterCommandParameter);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Extension/CommandExtension.cs b/src/Snap.Hutao/Snap.Hutao/Control/Extension/CommandInvocation.cs
similarity index 90%
rename from src/Snap.Hutao/Snap.Hutao/Control/Extension/CommandExtension.cs
rename to src/Snap.Hutao/Snap.Hutao/Control/Extension/CommandInvocation.cs
index e616d989..41f83527 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Extension/CommandExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Extension/CommandInvocation.cs
@@ -3,7 +3,7 @@
namespace Snap.Hutao.Control.Extension;
-internal static class CommandExtension
+internal static class CommandInvocation
{
public static bool TryExecute(this ICommand? command, object? parameter = null)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Extension/DependencyObjectExtension.cs b/src/Snap.Hutao/Snap.Hutao/Control/Extension/DependencyObjectExtension.cs
index 87e29747..61e5dddc 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Extension/DependencyObjectExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Extension/DependencyObjectExtension.cs
@@ -2,11 +2,13 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
+using System.Runtime.CompilerServices;
namespace Snap.Hutao.Control.Extension;
internal static class DependencyObjectExtension
{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceProvider ServiceProvider(this DependencyObject obj)
{
return Ioc.Default;
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
index b8a4deb0..caebb559 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
@@ -3,7 +3,9 @@
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
+using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core.Caching;
+using Snap.Hutao.Core.ExceptionService;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Control.Image;
@@ -26,12 +28,11 @@ internal sealed class CachedImage : Implementation.ImageEx
///
protected override async Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
{
- // We can only use Ioc to retrieve IImageCache, no IServiceProvider is available.
- IImageCache imageCache = Ioc.Default.GetRequiredService();
+ IImageCache imageCache = this.ServiceProvider().GetRequiredService();
try
{
- Verify.Operation(!string.IsNullOrEmpty(imageUri.Host), SH.ControlImageCachedImageInvalidResourceUri);
+ HutaoException.ThrowIf(string.IsNullOrEmpty(imageUri.Host), HutaoExceptionKind.ImageCacheInvalidUri, SH.ControlImageCachedImageInvalidResourceUri);
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true); // BitmapImage need to be created by main thread.
token.ThrowIfCancellationRequested(); // check token state to determine whether the operation should be canceled.
return new BitmapImage(file.ToUri()); // BitmapImage initialize with a uri will increase image quality and loading speed.
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageEx.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageEx.cs
index 18121e3e..f1e3accd 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageEx.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageEx.cs
@@ -7,21 +7,14 @@ using Windows.Media.Casting;
namespace Snap.Hutao.Control.Image.Implementation;
-internal class ImageEx : ImageExBase
+[DependencyProperty("NineGrid", typeof(Thickness))]
+internal partial class ImageEx : ImageExBase
{
- private static readonly DependencyProperty NineGridProperty = DependencyProperty.Register(nameof(NineGrid), typeof(Thickness), typeof(ImageEx), new PropertyMetadata(default(Thickness)));
-
public ImageEx()
: base()
{
}
- public Thickness NineGrid
- {
- get => (Thickness)GetValue(NineGridProperty);
- set => SetValue(NineGridProperty, value);
- }
-
public override CompositionBrush GetAlphaMask()
{
if (IsInitialized && Image is Microsoft.UI.Xaml.Controls.Image image)
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageExBase.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageExBase.cs
index ecc33dde..ee41e58f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageExBase.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/Implementation/ImageExBase.cs
@@ -6,6 +6,7 @@ using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
+using Snap.Hutao.Win32;
using System.IO;
using Windows.Foundation;
@@ -158,7 +159,6 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
if (value)
{
control.LayoutUpdated += control.OnImageExBaseLayoutUpdated;
-
control.InvalidateLazyLoading();
}
else
@@ -169,7 +169,7 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
private static void LazyLoadingThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
- if (d is ImageExBase control && control.EnableLazyLoading)
+ if (d is ImageExBase { EnableLazyLoading: true } control)
{
control.InvalidateLazyLoading();
}
@@ -229,9 +229,6 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
private void AttachPlaceholderSource(ImageSource? source)
{
- // Setting the source at this point should call ImageExOpened/VisualStateManager.GoToState
- // as we register to both the ImageOpened/ImageFailed events of the underlying control.
- // We only need to call those methods if we fail in other cases before we get here.
if (PlaceholderImage is Microsoft.UI.Xaml.Controls.Image image)
{
image.Source = source;
@@ -240,6 +237,15 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
{
brush.ImageSource = source;
}
+
+ if (source is null)
+ {
+ VisualStateManager.GoToState(this, UnloadedState, true);
+ }
+ else if (source is BitmapSource { PixelHeight: > 0, PixelWidth: > 0 })
+ {
+ VisualStateManager.GoToState(this, LoadedState, true);
+ }
}
private async void SetSource(object? source)
@@ -311,8 +317,7 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
}
tokenSource?.Cancel();
-
- tokenSource = new CancellationTokenSource();
+ tokenSource = new();
AttachPlaceholderSource(null);
@@ -443,9 +448,10 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
return;
}
- Rect controlRect = TransformToVisual(hostElement)
- .TransformBounds(new Rect(0, 0, ActualWidth, ActualHeight));
+ Rect controlRect = TransformToVisual(hostElement).TransformBounds(StructMarshal.Rect(ActualSize));
double lazyLoadingThreshold = LazyLoadingThreshold;
+
+ // Left/Top 1 Threshold, Right/Bottom 2 Threshold
Rect hostRect = new(
0 - lazyLoadingThreshold,
0 - lazyLoadingThreshold,
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/DefaultItemCollectionTransitionProvider.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/DefaultItemCollectionTransitionProvider.cs
deleted file mode 100644
index 1426af0e..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/DefaultItemCollectionTransitionProvider.cs
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using Microsoft.UI.Composition;
-using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Hosting;
-using System.Numerics;
-using Windows.Foundation;
-
-namespace Snap.Hutao.Control.Layout;
-
-internal sealed class DefaultItemCollectionTransitionProvider : ItemCollectionTransitionProvider
-{
- private const double DefaultAnimationDurationInMs = 300.0;
-
- static DefaultItemCollectionTransitionProvider()
- {
- AnimationSlowdownFactor = 1.0;
- }
-
- public static double AnimationSlowdownFactor { get; set; }
-
- protected override bool ShouldAnimateCore(ItemCollectionTransition transition)
- {
- return true;
- }
-
- protected override void StartTransitions(IList transitions)
- {
- List addTransitions = [];
- List removeTransitions = [];
- List moveTransitions = [];
-
- foreach (ItemCollectionTransition transition in addTransitions)
- {
- switch (transition.Operation)
- {
- case ItemCollectionTransitionOperation.Add:
- addTransitions.Add(transition);
- break;
- case ItemCollectionTransitionOperation.Remove:
- removeTransitions.Add(transition);
- break;
- case ItemCollectionTransitionOperation.Move:
- moveTransitions.Add(transition);
- break;
- }
- }
-
- StartAddTransitions(addTransitions, removeTransitions.Count > 0, moveTransitions.Count > 0);
- StartRemoveTransitions(removeTransitions);
- StartMoveTransitions(moveTransitions, removeTransitions.Count > 0);
- }
-
- private static void StartAddTransitions(IList transitions, bool hasRemoveTransitions, bool hasMoveTransitions)
- {
- foreach (ItemCollectionTransition transition in transitions)
- {
- ItemCollectionTransitionProgress progress = transition.Start();
- Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
- Compositor compositor = visual.Compositor;
-
- ScalarKeyFrameAnimation fadeInAnimation = compositor.CreateScalarKeyFrameAnimation();
- fadeInAnimation.InsertKeyFrame(0.0f, 0.0f);
-
- if (hasMoveTransitions && hasRemoveTransitions)
- {
- fadeInAnimation.InsertKeyFrame(0.66f, 0.0f);
- }
- else if (hasMoveTransitions || hasRemoveTransitions)
- {
- fadeInAnimation.InsertKeyFrame(0.5f, 0.0f);
- }
-
- fadeInAnimation.InsertKeyFrame(1.0f, 1.0f);
- fadeInAnimation.Duration = TimeSpan.FromMilliseconds(
- DefaultAnimationDurationInMs * ((hasRemoveTransitions ? 1 : 0) + (hasMoveTransitions ? 1 : 0) + 1) * AnimationSlowdownFactor);
-
- CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
- visual.StartAnimation("Opacity", fadeInAnimation);
- batch.End();
- batch.Completed += (_, _) => progress.Complete();
- }
- }
-
- private static void StartRemoveTransitions(IList transitions)
- {
- foreach (ItemCollectionTransition transition in transitions)
- {
- ItemCollectionTransitionProgress progress = transition.Start();
- Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
- Compositor compositor = visual.Compositor;
-
- ScalarKeyFrameAnimation fadeOutAnimation = compositor.CreateScalarKeyFrameAnimation();
- fadeOutAnimation.InsertExpressionKeyFrame(0.0f, "this.CurrentValue");
- fadeOutAnimation.InsertKeyFrame(1.0f, 0.0f);
- fadeOutAnimation.Duration = TimeSpan.FromMilliseconds(DefaultAnimationDurationInMs * AnimationSlowdownFactor);
-
- CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
- visual.StartAnimation(nameof(Visual.Opacity), fadeOutAnimation);
- batch.End();
- batch.Completed += (_, _) =>
- {
- visual.Opacity = 1.0f;
- progress.Complete();
- };
- }
- }
-
- private static void StartMoveTransitions(IList transitions, bool hasRemoveAnimations)
- {
- foreach (ItemCollectionTransition transition in transitions)
- {
- ItemCollectionTransitionProgress progress = transition.Start();
- Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
- Compositor compositor = visual.Compositor;
- CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
-
- // Animate offset.
- if (transition.OldBounds.X != transition.NewBounds.X ||
- transition.OldBounds.Y != transition.NewBounds.Y)
- {
- AnimateOffset(visual, compositor, transition.OldBounds, transition.NewBounds, hasRemoveAnimations);
- }
-
- batch.End();
- batch.Completed += (_, _) => progress.Complete();
- }
- }
-
- private static void AnimateOffset(Visual visual, Compositor compositor, Rect oldBounds, Rect newBounds, bool hasRemoveAnimations)
- {
- Vector2KeyFrameAnimation offsetAnimation = compositor.CreateVector2KeyFrameAnimation();
-
- offsetAnimation.SetVector2Parameter("delta", new Vector2(
- (float)(oldBounds.X - newBounds.X),
- (float)(oldBounds.Y - newBounds.Y)));
- offsetAnimation.SetVector2Parameter("final", default);
- offsetAnimation.InsertExpressionKeyFrame(0.0f, "this.CurrentValue + delta");
- if (hasRemoveAnimations)
- {
- offsetAnimation.InsertExpressionKeyFrame(0.5f, "delta");
- }
-
- offsetAnimation.InsertExpressionKeyFrame(1.0f, "final");
- offsetAnimation.Duration = TimeSpan.FromMilliseconds(
- DefaultAnimationDurationInMs * ((hasRemoveAnimations ? 1 : 0) + 1) * AnimationSlowdownFactor);
-
- visual.StartAnimation("TransformMatrix._41_42", offsetAnimation);
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs
index 96f69e44..8edbf364 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs
@@ -4,6 +4,7 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Collections.Specialized;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Foundation;
@@ -18,14 +19,12 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
protected override void InitializeForContextCore(VirtualizingLayoutContext context)
{
context.LayoutState = new UniformStaggeredLayoutState(context);
- base.InitializeForContextCore(context);
}
///
protected override void UninitializeForContextCore(VirtualizingLayoutContext context)
{
context.LayoutState = null;
- base.UninitializeForContextCore(context);
}
///
@@ -84,14 +83,15 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
if (columnWidth != state.ColumnWidth)
{
- // The items will need to be remeasured
+ // Remeasure items later
+ // Rearrange items later
state.Clear();
}
state.ColumnWidth = columnWidth;
- // adjust for column spacing on all columns expect the first
- double totalWidth = state.ColumnWidth + ((numberOfColumns - 1) * (state.ColumnWidth + MinColumnSpacing));
+ double totalWidth = ((state.ColumnWidth + MinColumnSpacing) * numberOfColumns) - MinColumnSpacing;
+
if (totalWidth > availableWidth)
{
numberOfColumns--;
@@ -103,12 +103,15 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
if (numberOfColumns != state.NumberOfColumns)
{
- // The items will not need to be remeasured, but they will need to go into new columns
- state.ClearColumns();
+ // Remeasure items later
+ // Rearrange items later
+ state.Clear();
}
if (MinRowSpacing != state.RowSpacing)
{
+ // Rearrange items later
+
// If the RowSpacing changes the height of the rows will be different.
// The columns stores the height so we'll want to clear them out to
// get the proper height
@@ -170,7 +173,7 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
item.Element.Measure(new Size(state.ColumnWidth, availableHeight));
if (item.Height != item.Element.DesiredSize.Height)
{
- // this item changed size; we need to recalculate layout for everything after this
+ // this item changed size; we need to recalculate layout for everything after this item
state.RemoveFromIndex(i + 1);
item.Height = item.Element.DesiredSize.Height;
columnHeights[columnIndex] = item.Top + item.Height;
@@ -201,8 +204,7 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
// Cycle through each column and arrange the items that are within the realization bounds
for (int columnIndex = 0; columnIndex < state.NumberOfColumns; columnIndex++)
{
- UniformStaggeredColumnLayout layout = state.GetColumnLayout(columnIndex);
- foreach (ref readonly UniformStaggeredItem item in CollectionsMarshal.AsSpan(layout))
+ foreach (ref readonly UniformStaggeredItem item in CollectionsMarshal.AsSpan(state.GetColumnLayout(columnIndex)))
{
double bottom = item.Top + item.Height;
if (bottom < context.RealizationRect.Top)
@@ -211,6 +213,7 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
continue;
}
+ // Partial or fully in the view
if (item.Top <= context.RealizationRect.Bottom)
{
double itemHorizontalOffset = (state.ColumnWidth * columnIndex) + (MinColumnSpacing * columnIndex);
@@ -229,21 +232,22 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
return finalSize;
}
- private static (int NumberOfColumns, double ColumnWidth) GetNumberOfColumnsAndWidth(double availableWidth, double minItemWidth, double minColumnSpacing)
+ private static (int NumberOfColumns, double ColumnWidth) GetNumberOfColumnsAndWidth(double availableWidth, double minItemWidth, double columnSpacing)
{
// test if the width can fit in 2 items
- if ((2 * minItemWidth) + minColumnSpacing > availableWidth)
+ if ((2 * minItemWidth) + columnSpacing > availableWidth)
{
return (1, availableWidth);
}
- int columnCount = Math.Max(1, (int)((availableWidth + minColumnSpacing) / (minItemWidth + minColumnSpacing)));
- double columnWidthAddSpacing = (availableWidth + minColumnSpacing) / columnCount;
- return (columnCount, columnWidthAddSpacing - minColumnSpacing);
+ int columnCount = Math.Max(1, (int)((availableWidth + columnSpacing) / (minItemWidth + columnSpacing)));
+ double columnWidthWithSpacing = (availableWidth + columnSpacing) / columnCount;
+ return (columnCount, columnWidthWithSpacing - columnSpacing);
}
private static int GetLowestColumnIndex(in ReadOnlySpan columnHeights)
{
+ // We want to find the leftest column with the lowest height
int columnIndex = 0;
double height = columnHeights[0];
for (int j = 1; j < columnHeights.Length; j++)
@@ -260,13 +264,11 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
private static void OnMinItemWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
- UniformStaggeredLayout panel = (UniformStaggeredLayout)d;
- panel.InvalidateMeasure();
+ ((UniformStaggeredLayout)d).InvalidateMeasure();
}
private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
- UniformStaggeredLayout panel = (UniformStaggeredLayout)d;
- panel.InvalidateMeasure();
+ ((UniformStaggeredLayout)d).InvalidateMeasure();
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs
index 31f8a0ab..205de7d9 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs
@@ -67,46 +67,6 @@ internal sealed class UniformStaggeredLayoutState
return columnLayout[columnIndex];
}
- ///
- /// Clear everything that has been calculated.
- ///
- internal void Clear()
- {
- // https://github.com/DGP-Studio/Snap.Hutao/issues/1079
- // The first element must be force refreshed otherwise
- // it will use the old one realized
- // https://github.com/DGP-Studio/Snap.Hutao/issues/1099
- // Now we need to refresh the first element of each column
- // https://github.com/DGP-Studio/Snap.Hutao/issues/1099
- // Finally we need to refresh the whole layout when we reset
- if (context.ItemCount > 0)
- {
- for (int i = 0; i < context.ItemCount; i++)
- {
- RecycleElementAt(i);
- }
- }
-
- columnLayout.Clear();
- items.Clear();
- }
-
- ///
- /// Clear the layout columns so they will be recalculated.
- ///
- internal void ClearColumns()
- {
- columnLayout.Clear();
- }
-
- ///
- /// Gets the estimated height of the layout.
- ///
- /// The estimated height of the layout.
- ///
- /// If all of the items have been calculated then the actual height will be returned.
- /// If all of the items have not been calculated then an estimated height will be calculated based on the average height of the items.
- ///
internal double GetHeight()
{
double desiredHeight = columnLayout.Values.Max(c => c.Height);
@@ -139,10 +99,37 @@ internal sealed class UniformStaggeredLayoutState
return desiredHeight;
}
+ internal void Clear()
+ {
+ RecycleElements();
+ ClearColumns();
+ ClearItems();
+ }
+
+ internal void ClearColumns()
+ {
+ columnLayout.Clear();
+ }
+
+ internal void ClearItems()
+ {
+ items.Clear();
+ }
+
+ internal void RecycleElements()
+ {
+ if (context.ItemCount > 0)
+ {
+ for (int i = 0; i < items.Count; i++)
+ {
+ RecycleElementAt(i);
+ }
+ }
+ }
+
internal void RecycleElementAt(int index)
{
- UIElement element = context.GetOrCreateElementAt(index);
- context.RecycleElement(element);
+ context.RecycleElement(context.GetOrCreateElementAt(index));
}
internal void RemoveFromIndex(int index)
@@ -175,7 +162,7 @@ internal sealed class UniformStaggeredLayoutState
{
for (int i = startIndex; i <= endIndex; i++)
{
- if (i > items.Count)
+ if (i >= items.Count)
{
break;
}
@@ -184,7 +171,7 @@ internal sealed class UniformStaggeredLayoutState
item.Height = 0;
item.Top = 0;
- // We must recycle all elements to ensure that it gets the correct context
+ // We must recycle all removed elements to ensure that it gets the correct context
RecycleElementAt(i);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoExceptionKind.cs b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoExceptionKind.cs
index 07fb80d5..80333902 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoExceptionKind.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoExceptionKind.cs
@@ -9,6 +9,7 @@ internal enum HutaoExceptionKind
// Foundation
ServiceTypeCastFailed,
+ ImageCacheInvalidUri,
// IO
FileSystemCreateFileInsufficientPermissions,
diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs b/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs
index afa83041..9cdb3ec2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Win32/StructMarshal.cs
@@ -4,6 +4,7 @@
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
+using Windows.Foundation;
using Windows.Graphics;
namespace Snap.Hutao.Win32;
@@ -26,45 +27,26 @@ internal static class StructMarshal
return color;
}
- ///
- /// 从 0,0 点构造一个新的
- ///
- /// 尺寸
- /// 新的实例
+ public static Rect Rect(Vector2 size)
+ {
+ return new(0, 0, size.X, size.Y);
+ }
+
public static RectInt32 RectInt32(SizeInt32 size)
{
return new(0, 0, size.Width, size.Height);
}
- ///
- /// 构造一个新的
- ///
- /// 左上角位置
- /// 尺寸
- /// 新的实例
public static RectInt32 RectInt32(PointInt32 point, Vector2 size)
{
return RectInt32(point.X, point.Y, size);
}
- ///
- /// 构造一个新的
- ///
- /// X
- /// Y
- /// 尺寸
- /// 新的实例
public static RectInt32 RectInt32(int x, int y, Vector2 size)
{
return new(x, y, (int)size.X, (int)size.Y);
}
- ///
- /// 构造一个新的
- ///
- /// 左上角位置
- /// 尺寸
- /// 新的实例
public static RectInt32 RectInt32(PointInt32 point, SizeInt32 size)
{
return new(point.X, point.Y, size.Width, size.Height);