Skip to content

Commit 2f8825c

Browse files
authored
Merge branch 'master' into wrapPanel.verticalAlignment
2 parents 959e732 + 27c66f4 commit 2f8825c

File tree

6 files changed

+215
-341
lines changed

6 files changed

+215
-341
lines changed

Microsoft.Toolkit.Uwp.UI.Controls/UniformGrid/TakenSpotsReferenceHolder.cs

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using System;
6-
using System.Collections.Generic;
7-
using System.Linq;
8-
using System.Text;
9-
using System.Threading.Tasks;
5+
using System.Collections;
6+
using System.Drawing;
7+
using Microsoft.Toolkit.Diagnostics;
108

119
namespace Microsoft.Toolkit.Uwp.UI.Controls
1210
{
@@ -16,23 +14,81 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
1614
/// <see cref="UniformGrid.GetFreeSpot"/> iterator.
1715
/// This is used so we can better isolate our logic and make it easier to test.
1816
/// </summary>
19-
internal class TakenSpotsReferenceHolder
17+
internal sealed class TakenSpotsReferenceHolder
2018
{
2119
/// <summary>
22-
/// Gets or sets the array to hold taken spots.
23-
/// True value indicates an item in the layout is fixed to that position.
24-
/// False values indicate free openings where an item can be placed.
20+
/// The <see cref="BitArray"/> instance used to efficiently track empty spots.
2521
/// </summary>
26-
public bool[,] SpotsTaken { get; set; }
22+
private readonly BitArray spotsTaken;
2723

24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="TakenSpotsReferenceHolder"/> class.
26+
/// </summary>
27+
/// <param name="rows">The number of rows to track.</param>
28+
/// <param name="columns">The number of columns to track.</param>
2829
public TakenSpotsReferenceHolder(int rows, int columns)
2930
{
30-
SpotsTaken = new bool[rows, columns];
31+
Guard.IsGreaterThanOrEqualTo(rows, 0, nameof(rows));
32+
Guard.IsGreaterThanOrEqualTo(columns, 0, nameof(columns));
33+
34+
Height = rows;
35+
Width = columns;
36+
37+
this.spotsTaken = new BitArray(rows * columns);
38+
}
39+
40+
/// <summary>
41+
/// Gets the height of the grid to monitor.
42+
/// </summary>
43+
public int Height { get; }
44+
45+
/// <summary>
46+
/// Gets the width of the grid to monitor.
47+
/// </summary>
48+
public int Width { get; }
49+
50+
/// <summary>
51+
/// Gets or sets the value of a specified grid cell.
52+
/// </summary>
53+
/// <param name="i">The vertical offset.</param>
54+
/// <param name="j">The horizontal offset.</param>
55+
public bool this[int i, int j]
56+
{
57+
get => this.spotsTaken[(i * Width) + j];
58+
set => this.spotsTaken[(i * Width) + j] = value;
59+
}
60+
61+
/// <summary>
62+
/// Fills the specified area in the current grid with a given value.
63+
/// If invalid coordinates are given, they will simply be ignored and no exception will be thrown.
64+
/// </summary>
65+
/// <param name="value">The value to fill the target area with.</param>
66+
/// <param name="row">The row to start on (inclusive, 0-based index).</param>
67+
/// <param name="column">The column to start on (inclusive, 0-based index).</param>
68+
/// <param name="width">The positive width of area to fill.</param>
69+
/// <param name="height">The positive height of area to fill.</param>
70+
public void Fill(bool value, int row, int column, int width, int height)
71+
{
72+
Rectangle bounds = new Rectangle(0, 0, Width, Height);
73+
74+
// Precompute bounds to skip branching in main loop
75+
bounds.Intersect(new Rectangle(column, row, width, height));
76+
77+
for (int i = bounds.Top; i < bounds.Bottom; i++)
78+
{
79+
for (int j = bounds.Left; j < bounds.Right; j++)
80+
{
81+
this[i, j] = value;
82+
}
83+
}
3184
}
3285

33-
public TakenSpotsReferenceHolder(bool[,] array)
86+
/// <summary>
87+
/// Resets the current reference holder.
88+
/// </summary>
89+
public void Reset()
3490
{
35-
SpotsTaken = array;
91+
this.spotsTaken.SetAll(false);
3692
}
3793
}
3894
}

Microsoft.Toolkit.Uwp.UI.Controls/UniformGrid/UniformGrid.Helpers.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ public partial class UniformGrid : Grid
2222
{
2323
if (topdown)
2424
{
25-
var rows = arrayref.SpotsTaken.GetLength(0);
25+
var rows = arrayref.Height;
2626

2727
// Layout spots from Top-Bottom, Left-Right (right-left handled automatically by Grid with Flow-Direction).
2828
// Effectively transpose the Grid Layout.
29-
for (int c = 0; c < arrayref.SpotsTaken.GetLength(1); c++)
29+
for (int c = 0; c < arrayref.Width; c++)
3030
{
3131
int start = (c == 0 && firstcolumn > 0 && firstcolumn < rows) ? firstcolumn : 0;
3232
for (int r = start; r < rows; r++)
3333
{
34-
if (!arrayref.SpotsTaken[r, c])
34+
if (!arrayref[r, c])
3535
{
3636
yield return (r, c);
3737
}
@@ -40,17 +40,17 @@ public partial class UniformGrid : Grid
4040
}
4141
else
4242
{
43-
var columns = arrayref.SpotsTaken.GetLength(1);
43+
var columns = arrayref.Width;
4444

4545
// Layout spots as normal from Left-Right.
4646
// (right-left handled automatically by Grid with Flow-Direction
4747
// during its layout, internal model is always left-right).
48-
for (int r = 0; r < arrayref.SpotsTaken.GetLength(0); r++)
48+
for (int r = 0; r < arrayref.Height; r++)
4949
{
5050
int start = (r == 0 && firstcolumn > 0 && firstcolumn < columns) ? firstcolumn : 0;
5151
for (int c = start; c < columns; c++)
5252
{
53-
if (!arrayref.SpotsTaken[r, c])
53+
if (!arrayref[r, c])
5454
{
5555
yield return (r, c);
5656
}

Microsoft.Toolkit.Uwp.UI.Controls/UniformGrid/UniformGrid.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Linq;
8-
using Microsoft.Toolkit.Extensions;
98
using Windows.Foundation;
109
using Windows.UI.Xaml;
1110
using Windows.UI.Xaml.Controls;
@@ -20,6 +19,11 @@ public partial class UniformGrid : Grid
2019
// Internal list we use to keep track of items that we don't have space to layout.
2120
private List<UIElement> _overflow = new List<UIElement>();
2221

22+
/// <summary>
23+
/// The <see cref="TakenSpotsReferenceHolder"/> instance in use, if any.
24+
/// </summary>
25+
private TakenSpotsReferenceHolder _spotref;
26+
2327
/// <inheritdoc/>
2428
protected override Size MeasureOverride(Size availableSize)
2529
{
@@ -36,7 +40,20 @@ protected override Size MeasureOverride(Size availableSize)
3640
SetupRowDefinitions(rows);
3741
SetupColumnDefinitions(columns);
3842

39-
var spotref = new TakenSpotsReferenceHolder(rows, columns);
43+
TakenSpotsReferenceHolder spotref;
44+
45+
// If the last spot holder matches the size currently in use, just reset
46+
// that instance and reuse it to avoid allocating a new bit array.
47+
if (_spotref != null && _spotref.Height == rows && _spotref.Width == columns)
48+
{
49+
spotref = _spotref;
50+
51+
spotref.Reset();
52+
}
53+
else
54+
{
55+
spotref = _spotref = new TakenSpotsReferenceHolder(rows, columns);
56+
}
4057

4158
// Figure out which children we should automatically layout and where available openings are.
4259
foreach (var child in visible)
@@ -56,7 +73,8 @@ protected override Size MeasureOverride(Size availableSize)
5673
else
5774
{
5875
SetAutoLayout(child, false);
59-
spotref.SpotsTaken.Fill(true, row, col, colspan, rowspan); // row, col, width, height
76+
77+
spotref.Fill(true, row, col, colspan, rowspan);
6078
}
6179
}
6280

@@ -100,7 +118,7 @@ protected override Size MeasureOverride(Size availableSize)
100118
if (rowspan > 1 || colspan > 1)
101119
{
102120
// TODO: Need to tie this into iterator
103-
spotref.SpotsTaken.Fill(true, row, column, GetColumnSpan(child), GetRowSpan(child)); // row, col, width, height
121+
spotref.Fill(true, row, column, colspan, rowspan);
104122
}
105123
}
106124
else
@@ -135,7 +153,7 @@ protected override Size MeasureOverride(Size availableSize)
135153
}
136154

137155
// Return our desired size based on the largest child we found, our dimensions, and spacing.
138-
var desiredSize = new Size((maxWidth * (double)columns) + columnSpacingSize, (maxHeight * (double)rows) + rowSpacingSize);
156+
var desiredSize = new Size((maxWidth * columns) + columnSpacingSize, (maxHeight * rows) + rowSpacingSize);
139157

140158
// Required to perform regular grid measurement, but ignore result.
141159
base.MeasureOverride(desiredSize);

0 commit comments

Comments
 (0)