using System;
using System.Collections.Generic;
using System.Text;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace StrixMusic.Shells.ZuneDesktop.Controls.Views.Quickplay
{
///
/// A panel that arranges objects in a very **very** visualy striking way.
///
public partial class QuickplayPanel
: Panel
{
private struct ArrangeConfig
{
///
/// An array of potential item sizes.
///
private static readonly double[] _itemHeights = { 1, 2, 4, 5.2 };
private Random _rand;
private int _minSizeIndex;
private int _maxSizeIndex;
private int _newColumnMinSizeIndex;
private int _newColumnMaxSizeIndex;
public ArrangeConfig(
int seed,
int initialMaxSizeIndex = 1,
int initialMinSizeIndex = 1,
int newColumnMaxSizeIndex = 1,
int newColumnMinSizeIndex = 1)
{
if (seed != -1)
_rand = new Random(seed);
else
_rand = new Random();
_maxSizeIndex = initialMaxSizeIndex;
_minSizeIndex = initialMinSizeIndex;
_newColumnMinSizeIndex = newColumnMinSizeIndex;
_newColumnMaxSizeIndex = newColumnMaxSizeIndex;
}
public double GetNextRelativeHeight()
{
/*if (_itemHeights.Length - _maxSizeIndex <= _minSizeIndex)
{
if (_minSizeIndex != 0)
{
_minSizeIndex = 0;
}
else
{
return double.PositiveInfinity;
}
}*/
int sizeIndex = _rand.Next(_minSizeIndex, _itemHeights.Length - _maxSizeIndex);
/* if (_maxSizeIndex == sizeIndex)
{
// Only allow two of the same item sizes in a row (by column)
_maxSizeIndex++;
}
else
{
// Larger items cannot go below smaller items
_maxSizeIndex = _itemHeights.Length - sizeIndex;
}
// Decrease the max offest each iteration by column till 0
if (_minSizeIndex > 0)
{
_minSizeIndex--;
}*/
return _itemHeights[sizeIndex];
}
public void IncerementColumn()
{
_minSizeIndex = _newColumnMinSizeIndex;
_maxSizeIndex = _newColumnMaxSizeIndex;
}
}
private struct ArrangeState
{
private Point _point;
private double _maxX;
private double _maxY;
private double _itemMargin;
private double _finalHeight;
public ArrangeState(double finalHeight, double itemMargin)
{
_finalHeight = finalHeight;
_itemMargin = itemMargin;
_point = new Point(0, 0);
_maxX = 0;
_maxY = 0;
}
public bool TryAddChild(UIElement child, Size childSize, double relativeSize)
{
if (_point.Y + childSize.Height > _finalHeight)
{
// Item won't fit
return false;
}
// Arrange child
Rect rect = new Rect(_point, childSize);
if (_maxY < rect.Bottom && relativeSize < 2)
{
// Removes tailing small tiles, because they are un-appealing
return false;
}
child.Arrange(rect);
// Update position and maxX
_maxX = Math.Max(rect.Right, _maxX);
_maxY = Math.Max(rect.Bottom, _maxY);
_point.Y += childSize.Height;
_point.Y += _itemMargin;
return true;
}
public void IncerementColumn()
{
_maxX += _itemMargin;
_point.X = _maxX;
_point.Y = 0;
}
public Size FinalSize => new Size(_point.X, _point.Y);
}
///
/// Gets or sets the base size of a tile.
///
public double BaseTileHeight { get; set; } = 48;
///
/// Gets or sets the direction the elements move.
///
///
/// Will be LeftToRight if flow direction is RightToLeft.
///
public bool RightToLeft { get; set; }
///
/// Gets or sets the size of the margin between the items.
///
public double ItemMargin { get; set; } = 4;
///
/// The seed to use for randomizing the layout.
///
public int Seed { get; set; } = 1;
///
protected override Size MeasureOverride(Size availableSize)
{
foreach (var child in Children)
{
child.Measure(availableSize);
}
return ArrangeTiles(availableSize);
}
///
protected override Size ArrangeOverride(Size finalSize)
{
return ArrangeTiles(finalSize);
}
private Size ArrangeTiles(Size finalSize)
{
ArrangeConfig arrangeConfig = new ArrangeConfig(Seed);
ArrangeState arrangeState = new ArrangeState(finalSize.Height, ItemMargin);
// Layout each child
foreach (var child in Children)
{
// Find the child's ratio
Size desiredSize = child.DesiredSize;
double widthRatio = 1;
if (desiredSize.Height + desiredSize.Height != 0)
{
widthRatio = desiredSize.Width / desiredSize.Height;
}
// This marks the beginning of an attempted tile making. If a tile does not fit in a column, it will adjust the status and return here.
attemptLayout:
// Set the child's height while respecting the ratio
Size actualSize = Size.Empty;
double relativeSize = arrangeConfig.GetNextRelativeHeight();
actualSize.Height = GetRealHeight(relativeSize);
actualSize.Width = actualSize.Height * widthRatio;
if (double.IsNegativeInfinity(actualSize.Height))
{
// No more sizes were valid for this column.
arrangeState.IncerementColumn();
arrangeConfig.IncerementColumn();
goto attemptLayout;
}
if (!arrangeState.TryAddChild(child, actualSize, relativeSize))
{
// The item would not fit in this column, iterate columns
arrangeState.IncerementColumn();
arrangeConfig.IncerementColumn();
goto attemptLayout;
}
}
return arrangeState.FinalSize;
}
///
/// Converts factors of heights to real heights, accounting for factor and margins.
///
private double GetRealHeight(double factor)
{
return (factor * BaseTileHeight) + ((factor - 1) * ItemMargin);
}
}
}