diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs
index 6817858d..02c869c1 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs
@@ -63,12 +63,12 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
///
protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
{
- if (context.ItemCount == 0)
+ if (context.ItemCount is 0)
{
return new Size(availableSize.Width, 0);
}
- if ((context.RealizationRect.Width == 0) && (context.RealizationRect.Height == 0))
+ if ((context.RealizationRect.Width is 0) && (context.RealizationRect.Height is 0))
{
return new Size(availableSize.Width, 0.0f);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapItem.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapItem.cs
index d96e18a8..290afc69 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapItem.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapItem.cs
@@ -8,6 +8,8 @@ namespace Snap.Hutao.Control.Layout;
internal sealed class WrapItem
{
+ public static Point EmptyPosition { get; } = new(float.NegativeInfinity, float.NegativeInfinity);
+
public WrapItem(int index)
{
Index = index;
@@ -15,9 +17,9 @@ internal sealed class WrapItem
public int Index { get; }
- public Size? Size { get; set; }
+ public Size Size { get; set; } = Size.Empty;
- public Point? Position { get; set; }
+ public Point Position { get; set; } = EmptyPosition;
public UIElement? Element { get; set; }
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapLayout.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapLayout.cs
index d3132d8f..9dd89d75 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapLayout.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapLayout.cs
@@ -15,13 +15,11 @@ internal sealed partial class WrapLayout : VirtualizingLayout
protected override void InitializeForContextCore(VirtualizingLayoutContext context)
{
context.LayoutState = new WrapLayoutState(context);
- base.InitializeForContextCore(context);
}
protected override void UninitializeForContextCore(VirtualizingLayoutContext context)
{
context.LayoutState = default;
- base.UninitializeForContextCore(context);
}
protected override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs args)
@@ -60,6 +58,16 @@ internal sealed partial class WrapLayout : VirtualizingLayout
protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
{
+ if (context.ItemCount is 0)
+ {
+ return new Size(availableSize.Width, 0);
+ }
+
+ if ((context.RealizationRect.Width is 0) && (context.RealizationRect.Height is 0))
+ {
+ return new Size(availableSize.Width, 0.0f);
+ }
+
Size spacing = new(HorizontalSpacing, VerticalSpacing);
WrapLayoutState state = (WrapLayoutState)context.LayoutState;
@@ -68,42 +76,42 @@ internal sealed partial class WrapLayout : VirtualizingLayout
{
state.ClearPositions();
state.Spacing = spacing;
- state.AvailableWidth = availableSize.Height;
+ state.AvailableWidth = availableSize.Width;
}
double currentHeight = 0;
- Point position = default;
+ Point itemPosition = default;
for (int i = 0; i < context.ItemCount; ++i)
{
- bool measured = false;
+ bool itemMeasured = false;
WrapItem item = state.GetItemAt(i);
- if (item.Size is null)
+ if (item.Size == Size.Empty)
{
item.Element = context.GetOrCreateElementAt(i);
item.Element.Measure(availableSize);
item.Size = item.Element.DesiredSize;
- measured = true;
+ itemMeasured = true;
}
- Size currentSize = item.Size.Value;
+ Size itemSize = item.Size;
- if (item.Position is null)
+ if (item.Position == WrapItem.EmptyPosition)
{
- if (availableSize.Width < position.X + currentSize.Height)
+ if (availableSize.Width < itemPosition.X + itemSize.Width)
{
// New Row
- position.X = 0;
- position.Y += currentHeight + spacing.Height;
+ itemPosition.X = 0;
+ itemPosition.Y += currentHeight + spacing.Height;
currentHeight = 0;
}
- item.Position = position;
+ item.Position = itemPosition;
}
- position = item.Position.Value;
+ itemPosition = item.Position;
- double vEnd = position.Y + currentSize.Width;
- if (vEnd < context.RealizationRect.Top)
+ double bottom = itemPosition.Y + itemSize.Height;
+ if (bottom < context.RealizationRect.Top)
{
// Item is "above" the bounds
if (item.Element is not null)
@@ -114,7 +122,7 @@ internal sealed partial class WrapLayout : VirtualizingLayout
continue;
}
- else if (position.Y > context.RealizationRect.Bottom)
+ else if (itemPosition.Y > context.RealizationRect.Bottom)
{
// Item is "below" the bounds.
if (item.Element is not null)
@@ -126,34 +134,34 @@ internal sealed partial class WrapLayout : VirtualizingLayout
// We don't need to measure anything below the bounds
break;
}
- else if (!measured)
+ else if (!itemMeasured)
{
// Always measure elements that are within the bounds
item.Element = context.GetOrCreateElementAt(i);
item.Element.Measure(availableSize);
- currentSize = item.Element.DesiredSize;
- if (currentSize != item.Size)
+ itemSize = item.Element.DesiredSize;
+ if (itemSize != item.Size)
{
// this item changed size; we need to recalculate layout for everything after this
state.RemoveFromIndex(i + 1);
- item.Size = currentSize;
+ item.Size = itemSize;
// did the change make it go into the new row?
- if (availableSize.Width < position.X + currentSize.Width)
+ if (availableSize.Width < itemPosition.X + itemSize.Width)
{
// New Row
- position.X = 0;
- position.Y += currentHeight + spacing.Height;
+ itemPosition.X = 0;
+ itemPosition.Y += currentHeight + spacing.Height;
currentHeight = 0;
}
- item.Position = position;
+ item.Position = itemPosition;
}
}
- position.X += currentSize.Width + spacing.Width;
- currentHeight = Math.Max(currentSize.Height, currentHeight);
+ itemPosition.X += itemSize.Width + spacing.Width;
+ currentHeight = Math.Max(itemSize.Height, currentHeight);
}
return new Size(double.IsInfinity(availableSize.Width) ? 0 : Math.Ceiling(availableSize.Width), state.GetHeight());
@@ -165,34 +173,9 @@ internal sealed partial class WrapLayout : VirtualizingLayout
{
WrapLayoutState state = (WrapLayoutState)context.LayoutState;
- bool ArrangeItem(WrapItem item)
- {
- if (item is { Size: null } or { Position: null })
- {
- return false;
- }
-
- Size desiredSize = item.Size.Value;
-
- Point position = item.Position.Value;
-
- if (context.RealizationRect.Top <= position.Y + desiredSize.Height && position.Y <= context.RealizationRect.Bottom)
- {
- // place the item
- UIElement child = context.GetOrCreateElementAt(item.Index);
- child.Arrange(new Rect(position, desiredSize));
- }
- else if (position.Y > context.RealizationRect.Bottom)
- {
- return false;
- }
-
- return true;
- }
-
for (int i = 0; i < context.ItemCount; ++i)
{
- if (!ArrangeItem(state.GetItemAt(i)))
+ if (!ArrangeItem(context, state.GetItemAt(i)))
{
break;
}
@@ -200,14 +183,38 @@ internal sealed partial class WrapLayout : VirtualizingLayout
}
return finalSize;
+
+ static bool ArrangeItem(VirtualizingLayoutContext context, WrapItem item)
+ {
+ if (item.Size == Size.Empty || item.Position == WrapItem.EmptyPosition)
+ {
+ return false;
+ }
+
+ Size size = item.Size;
+ Point position = item.Position;
+
+ if (context.RealizationRect.Top <= position.Y + size.Height && position.Y <= context.RealizationRect.Bottom)
+ {
+ // place the item
+ UIElement child = context.GetOrCreateElementAt(item.Index);
+ child.Arrange(new Rect(position, size));
+ }
+ else if (position.Y > context.RealizationRect.Bottom)
+ {
+ return false;
+ }
+
+ return true;
+ }
}
private static void LayoutPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
- if (d is WrapLayout wp)
+ if (d is WrapLayout layout)
{
- wp.InvalidateMeasure();
- wp.InvalidateArrange();
+ layout.InvalidateMeasure();
+ layout.InvalidateArrange();
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapLayoutState.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapLayoutState.cs
index 395a6e0e..f84fab8c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapLayoutState.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/WrapLayoutState.cs
@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Runtime.InteropServices;
using Windows.Foundation;
@@ -26,7 +25,10 @@ internal sealed class WrapLayoutState
public WrapItem GetItemAt(int index)
{
- ArgumentOutOfRangeException.ThrowIfNegative(index);
+ if (index < 0)
+ {
+ throw new IndexOutOfRangeException();
+ }
if (index <= (items.Count - 1))
{
@@ -80,23 +82,24 @@ internal sealed class WrapLayoutState
Point? lastPosition = default;
double maxHeight = 0;
+ Span itemSpan = CollectionsMarshal.AsSpan(items);
for (int i = items.Count - 1; i >= 0; --i)
{
- WrapItem item = items[i];
+ ref readonly WrapItem item = ref itemSpan[i];
- if (item.Position is null || item.Size is null)
+ if (item.Position == WrapItem.EmptyPosition || item.Size == Size.Empty)
{
continue;
}
- if (lastPosition is not null && lastPosition.Value.Y > item.Position.Value.Y)
+ if (lastPosition is not null && lastPosition.Value.Y > item.Position.Y)
{
// This is a row above the last item.
break;
}
lastPosition = item.Position;
- maxHeight = Math.Max(maxHeight, item.Size.Value.Height);
+ maxHeight = Math.Max(maxHeight, item.Size.Height);
}
return lastPosition?.Y + maxHeight ?? 0;
@@ -104,7 +107,6 @@ internal sealed class WrapLayoutState
public void RecycleElementAt(int index)
{
- UIElement element = context.GetOrCreateElementAt(index);
- context.RecycleElement(element);
+ context.RecycleElement(context.GetOrCreateElementAt(index));
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json b/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json
index a5fb2a14..36a6b395 100644
--- a/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json
+++ b/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json
@@ -2,7 +2,7 @@
"profiles": {
"Snap.Hutao": {
"commandName": "MsixPackage",
- "nativeDebugging": false,
+ "nativeDebugging": true,
"doNotLaunchApp": false,
"allowLocalNetworkLoopbackProperty": true
},