-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #29 from UnicordDev/feature/modern-navigation
Improve navigation
- Loading branch information
Showing
33 changed files
with
1,388 additions
and
570 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System.Collections; | ||
using System.Drawing; | ||
|
||
namespace Microsoft.Toolkit.Uwp.UI.Controls | ||
{ | ||
/// <summary> | ||
/// Referencable class object we can use to have a reference shared between | ||
/// our <see cref="UniformGrid.MeasureOverride"/> and | ||
/// <see cref="UniformGrid.GetFreeSpot"/> iterator. | ||
/// This is used so we can better isolate our logic and make it easier to test. | ||
/// </summary> | ||
internal sealed class TakenSpotsReferenceHolder | ||
{ | ||
/// <summary> | ||
/// The <see cref="BitArray"/> instance used to efficiently track empty spots. | ||
/// </summary> | ||
private readonly BitArray spotsTaken; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="TakenSpotsReferenceHolder"/> class. | ||
/// </summary> | ||
/// <param name="rows">The number of rows to track.</param> | ||
/// <param name="columns">The number of columns to track.</param> | ||
public TakenSpotsReferenceHolder(int rows, int columns) | ||
{ | ||
Height = rows; | ||
Width = columns; | ||
|
||
this.spotsTaken = new BitArray(rows * columns); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the height of the grid to monitor. | ||
/// </summary> | ||
public int Height { get; } | ||
|
||
/// <summary> | ||
/// Gets the width of the grid to monitor. | ||
/// </summary> | ||
public int Width { get; } | ||
|
||
/// <summary> | ||
/// Gets or sets the value of a specified grid cell. | ||
/// </summary> | ||
/// <param name="i">The vertical offset.</param> | ||
/// <param name="j">The horizontal offset.</param> | ||
public bool this[int i, int j] | ||
{ | ||
get => this.spotsTaken[(i * Width) + j]; | ||
set => this.spotsTaken[(i * Width) + j] = value; | ||
} | ||
|
||
/// <summary> | ||
/// Fills the specified area in the current grid with a given value. | ||
/// If invalid coordinates are given, they will simply be ignored and no exception will be thrown. | ||
/// </summary> | ||
/// <param name="value">The value to fill the target area with.</param> | ||
/// <param name="row">The row to start on (inclusive, 0-based index).</param> | ||
/// <param name="column">The column to start on (inclusive, 0-based index).</param> | ||
/// <param name="width">The positive width of area to fill.</param> | ||
/// <param name="height">The positive height of area to fill.</param> | ||
public void Fill(bool value, int row, int column, int width, int height) | ||
{ | ||
Rectangle bounds = new Rectangle(0, 0, Width, Height); | ||
|
||
// Precompute bounds to skip branching in main loop | ||
bounds.Intersect(new Rectangle(column, row, width, height)); | ||
|
||
for (int i = bounds.Top; i < bounds.Bottom; i++) | ||
{ | ||
for (int j = bounds.Left; j < bounds.Right; j++) | ||
{ | ||
this[i, j] = value; | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Resets the current reference holder. | ||
/// </summary> | ||
public void Reset() | ||
{ | ||
this.spotsTaken.SetAll(false); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Windows.UI.Xaml; | ||
using Windows.UI.Xaml.Controls; | ||
|
||
namespace Microsoft.Toolkit.Uwp.UI.Controls | ||
{ | ||
/// <summary> | ||
/// The UniformGrid control presents information within a Grid with even spacing. | ||
/// </summary> | ||
public partial class UniformGrid : Grid | ||
{ | ||
// Provides the next spot in the boolean array with a 'false' value. | ||
#pragma warning disable SA1009 // Closing parenthesis must be followed by a space. | ||
internal static IEnumerable<(int row, int column)> GetFreeSpot(TakenSpotsReferenceHolder arrayref, int firstcolumn, bool topdown) | ||
#pragma warning restore SA1009 // Closing parenthesis must be followed by a space. | ||
{ | ||
if (topdown) | ||
{ | ||
var rows = arrayref.Height; | ||
|
||
// Layout spots from Top-Bottom, Left-Right (right-left handled automatically by Grid with Flow-Direction). | ||
// Effectively transpose the Grid Layout. | ||
for (int c = 0; c < arrayref.Width; c++) | ||
{ | ||
int start = (c == 0 && firstcolumn > 0 && firstcolumn < rows) ? firstcolumn : 0; | ||
for (int r = start; r < rows; r++) | ||
{ | ||
if (!arrayref[r, c]) | ||
{ | ||
yield return (r, c); | ||
} | ||
} | ||
} | ||
} | ||
else | ||
{ | ||
var columns = arrayref.Width; | ||
|
||
// Layout spots as normal from Left-Right. | ||
// (right-left handled automatically by Grid with Flow-Direction | ||
// during its layout, internal model is always left-right). | ||
for (int r = 0; r < arrayref.Height; r++) | ||
{ | ||
int start = (r == 0 && firstcolumn > 0 && firstcolumn < columns) ? firstcolumn : 0; | ||
for (int c = start; c < columns; c++) | ||
{ | ||
if (!arrayref[r, c]) | ||
{ | ||
yield return (r, c); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Based on the number of visible children, | ||
// returns the dimensions of the | ||
// grid we need to hold all elements. | ||
#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly | ||
internal static (int rows, int columns) GetDimensions(FrameworkElement[] visible, int rows, int cols, int firstColumn) | ||
#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly | ||
{ | ||
// If a dimension isn't specified, we need to figure out the other one (or both). | ||
if (rows == 0 || cols == 0) | ||
{ | ||
// Calculate the size & area of all objects in the grid to know how much space we need. | ||
var count = Math.Max(1, visible.Sum(item => GetRowSpan(item) * GetColumnSpan(item))); | ||
|
||
if (rows == 0) | ||
{ | ||
if (cols > 0) | ||
{ | ||
// Bound check | ||
var first = (firstColumn >= cols || firstColumn < 0) ? 0 : firstColumn; | ||
|
||
// If we have columns but no rows, calculate rows based on column offset and number of children. | ||
rows = (count + first + (cols - 1)) / cols; | ||
return (rows, cols); | ||
} | ||
else | ||
{ | ||
// Otherwise, determine square layout if both are zero. | ||
var size = (int)Math.Ceiling(Math.Sqrt(count)); | ||
|
||
// Figure out if firstColumn is in bounds | ||
var first = (firstColumn >= size || firstColumn < 0) ? 0 : firstColumn; | ||
|
||
rows = (int)Math.Ceiling(Math.Sqrt(count + first)); | ||
return (rows, rows); | ||
} | ||
} | ||
else if (cols == 0) | ||
{ | ||
// If we have rows and no columns, then calculate columns needed based on rows | ||
cols = (count + (rows - 1)) / rows; | ||
|
||
// Now that we know a rough size of our shape, see if the FirstColumn effects that: | ||
var first = (firstColumn >= cols || firstColumn < 0) ? 0 : firstColumn; | ||
|
||
cols = (count + first + (rows - 1)) / rows; | ||
} | ||
} | ||
|
||
return (rows, cols); | ||
} | ||
|
||
// Used to interleave specified row dimensions with automatic rows added to use | ||
// underlying Grid layout for main arrange of UniformGrid. | ||
internal void SetupRowDefinitions(int rows) | ||
{ | ||
// Mark initial definitions so we don't erase them. | ||
foreach (var rd in RowDefinitions) | ||
{ | ||
if (GetAutoLayout(rd) == null) | ||
{ | ||
SetAutoLayout(rd, false); | ||
} | ||
} | ||
|
||
// Remove non-autolayout rows we've added and then add them in the right spots. | ||
if (rows != RowDefinitions.Count) | ||
{ | ||
for (int r = RowDefinitions.Count - 1; r >= 0; r--) | ||
{ | ||
if (GetAutoLayout(RowDefinitions[r]) == true) | ||
{ | ||
RowDefinitions.RemoveAt(r); | ||
} | ||
} | ||
|
||
for (int r = this.RowDefinitions.Count; r < rows; r++) | ||
{ | ||
var rd = new RowDefinition(); | ||
SetAutoLayout(rd, true); | ||
this.RowDefinitions.Insert(r, rd); | ||
} | ||
} | ||
} | ||
|
||
// Used to interleave specified column dimensions with automatic columns added to use | ||
// underlying Grid layout for main arrange of UniformGrid. | ||
internal void SetupColumnDefinitions(int columns) | ||
{ | ||
// Mark initial definitions so we don't erase them. | ||
foreach (var cd in ColumnDefinitions) | ||
{ | ||
if (GetAutoLayout(cd) == null) | ||
{ | ||
SetAutoLayout(cd, false); | ||
} | ||
} | ||
|
||
// Remove non-autolayout columns we've added and then add them in the right spots. | ||
if (columns != ColumnDefinitions.Count) | ||
{ | ||
for (int c = ColumnDefinitions.Count - 1; c >= 0; c--) | ||
{ | ||
if (GetAutoLayout(ColumnDefinitions[c]) == true) | ||
{ | ||
this.ColumnDefinitions.RemoveAt(c); | ||
} | ||
} | ||
|
||
for (int c = ColumnDefinitions.Count; c < columns; c++) | ||
{ | ||
var cd = new ColumnDefinition(); | ||
SetAutoLayout(cd, true); | ||
ColumnDefinitions.Insert(c, cd); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.