@ -15,7 +15,7 @@ namespace Avalonia.Controls
/// Positions child elements in sequential position from left to right,
/// breaking content to the next line at the edge of the containing box.
/// Subsequent ordering happens sequentially from top to bottom or from right to left,
/// depending on the value of the Orientation property.
/// depending on the value of the <see cref=" Orientation"/> property.
/// </summary>
public class WrapPanel : Panel , INavigableContainer
{
@ -25,6 +25,18 @@ namespace Avalonia.Controls
public static readonly StyledProperty < Orientation > OrientationProperty =
AvaloniaProperty . Register < WrapPanel , Orientation > ( nameof ( Orientation ) , defaultValue : Orientation . Horizontal ) ;
/// <summary>
/// Defines the <see cref="ItemWidth"/> property.
/// </summary>
public static readonly StyledProperty < double > ItemWidthProperty =
AvaloniaProperty . Register < WrapPanel , double > ( nameof ( ItemWidth ) , double . NaN ) ;
/// <summary>
/// Defines the <see cref="ItemHeight"/> property.
/// </summary>
public static readonly StyledProperty < double > ItemHeightProperty =
AvaloniaProperty . Register < WrapPanel , double > ( nameof ( ItemHeight ) , double . NaN ) ;
/// <summary>
/// Initializes static members of the <see cref="WrapPanel"/> class.
/// </summary>
@ -42,6 +54,24 @@ namespace Avalonia.Controls
set { SetValue ( OrientationProperty , value ) ; }
}
/// <summary>
/// Gets or sets the width of all items in the WrapPanel.
/// </summary>
public double ItemWidth
{
get { return GetValue ( ItemWidthProperty ) ; }
set { SetValue ( ItemWidthProperty , value ) ; }
}
/// <summary>
/// Gets or sets the height of all items in the WrapPanel.
/// </summary>
public double ItemHeight
{
get { return GetValue ( ItemHeightProperty ) ; }
set { SetValue ( ItemHeightProperty , value ) ; }
}
/// <summary>
/// Gets the next control in the specified direction.
/// </summary>
@ -51,7 +81,9 @@ namespace Avalonia.Controls
/// <returns>The control.</returns>
IInputElement INavigableContainer . GetControl ( NavigationDirection direction , IInputElement from , bool wrap )
{
var horiz = Orientation = = Orientation . Horizontal ;
var orientation = Orientation ;
var children = Children ;
bool horiz = orientation = = Orientation . Horizontal ;
int index = Children . IndexOf ( ( IControl ) from ) ;
switch ( direction )
@ -60,7 +92,7 @@ namespace Avalonia.Controls
index = 0 ;
break ;
case NavigationDirection . Last :
index = C hildren. Count - 1 ;
index = c hildren. Count - 1 ;
break ;
case NavigationDirection . Next :
+ + index ;
@ -82,9 +114,9 @@ namespace Avalonia.Controls
break ;
}
if ( index > = 0 & & index < C hildren. Count )
if ( index > = 0 & & index < c hildren. Count )
{
return C hildren[ index ] ;
return c hildren[ index ] ;
}
else
{
@ -95,116 +127,140 @@ namespace Avalonia.Controls
/// <inheritdoc/>
protected override Size MeasureOverride ( Size constraint )
{
var curLineSize = new UVSize ( Orientation ) ;
var panelSize = new UVSize ( Orientation ) ;
var uvConstraint = new UVSize ( Orientation , constraint . Width , constraint . Height ) ;
var childConstraint = new Size ( constraint . Width , constraint . Height ) ;
for ( int i = 0 , count = Children . Count ; i < count ; i + + )
double itemWidth = ItemWidth ;
double itemHeight = ItemHeight ;
var orientation = Orientation ;
var children = Children ;
var curLineSize = new UVSize ( orientation ) ;
var panelSize = new UVSize ( orientation ) ;
var uvConstraint = new UVSize ( orientation , constraint . Width , constraint . Height ) ;
bool itemWidthSet = ! double . IsNaN ( itemWidth ) ;
bool itemHeightSet = ! double . IsNaN ( itemHeight ) ;
var childConstraint = new Size (
itemWidthSet ? itemWidth : constraint . Width ,
itemHeightSet ? itemHeight : constraint . Height ) ;
for ( int i = 0 , count = children . Count ; i < count ; i + + )
{
var child = Children [ i ] ;
if ( child = = null ) continue ;
//Flow passes its own constrint to children
child . Measure ( childConstraint ) ;
//this is the size of the child in UV space
var sz = new UVSize ( Orientation , child . DesiredSize . Width , child . DesiredSize . Height ) ;
if ( MathUtilities . GreaterThan ( curLineSize . U + sz . U , uvConstraint . U ) ) //need to switch to another line
var child = children [ i ] ;
if ( child ! = null )
{
panelSize . U = Max ( curLineSize . U , panelSize . U ) ;
panelSize . V + = curLineSize . V ;
curLineSize = sz ;
// Flow passes its own constraint to children
child . Measure ( childConstraint ) ;
// This is the size of the child in UV space
var sz = new UVSize ( orientation ,
itemWidthSet ? itemWidth : child . DesiredSize . Width ,
itemHeightSet ? itemHeight : child . DesiredSize . Height ) ;
if ( MathUtilities . GreaterThan ( sz . U , uvConstraint . U ) ) //the element is wider then the constrint - give it a separate line
if ( MathUtilities . GreaterThan ( curLineSize . U + sz . U , uvConstraint . U ) ) // Need to switch to another line
{
panelSize . U = Max ( sz . U , panelSize . U ) ;
panelSize . V + = sz . V ;
curLineSize = new UVSize ( Orientation ) ;
panelSize . U = Max ( curLineSize . U , panelSize . U ) ;
panelSize . V + = curLineSize . V ;
curLineSize = sz ;
if ( MathUtilities . GreaterThan ( sz . U , uvConstraint . U ) ) // The element is wider then the constraint - give it a separate line
{
panelSize . U = Max ( sz . U , panelSize . U ) ;
panelSize . V + = sz . V ;
curLineSize = new UVSize ( orientation ) ;
}
}
else // Continue to accumulate a line
{
curLineSize . U + = sz . U ;
curLineSize . V = Max ( sz . V , curLineSize . V ) ;
}
}
else //continue to accumulate a line
{
curLineSize . U + = sz . U ;
curLineSize . V = Max ( sz . V , curLineSize . V ) ;
}
}
//the last line size, if any should be added
// The last line size, if any should be added
panelSize . U = Max ( curLineSize . U , panelSize . U ) ;
panelSize . V + = curLineSize . V ;
//g o from UV space to W/H space
// G o from UV space to W/H space
return new Size ( panelSize . Width , panelSize . Height ) ;
}
/// <inheritdoc/>
protected override Size ArrangeOverride ( Size finalSize )
{
double itemWidth = ItemWidth ;
double itemHeight = ItemHeight ;
var orientation = Orientation ;
var children = Children ;
int firstInLine = 0 ;
double accumulatedV = 0 ;
UVSize curLineSize = new UVSize ( Orientation ) ;
UVSize uvFinalSize = new UVSize ( Orientation , finalSize . Width , finalSize . Height ) ;
for ( int i = 0 ; i < Children . Count ; i + + )
double itemU = orientation = = Orientation . Horizontal ? itemWidth : itemHeight ;
var curLineSize = new UVSize ( orientation ) ;
var uvFinalSize = new UVSize ( orientation , finalSize . Width , finalSize . Height ) ;
bool itemWidthSet = ! double . IsNaN ( itemWidth ) ;
bool itemHeightSet = ! double . IsNaN ( itemHeight ) ;
bool useItemU = orientation = = Orientation . Horizontal ? itemWidthSet : itemHeightSet ;
for ( int i = 0 ; i < children . Count ; i + + )
{
var child = Children [ i ] ;
if ( child = = null ) continue ;
var child = children [ i ] ;
if ( child ! = null )
{
var sz = new UVSize ( orientation ,
itemWidthSet ? itemWidth : child . DesiredSize . Width ,
itemHeightSet ? itemHeight : child . DesiredSize . Height ) ;
var sz = new UVSize ( Orientation , child . DesiredSize . Width , child . DesiredSize . Height ) ;
if ( MathUtilities . GreaterThan ( curLineSize . U + sz . U , uvFinalSize . U ) ) // Need to switch to another line
{
ArrangeLine ( accumulatedV , curLineSize . V , firstInLine , i , useItemU , itemU ) ;
if ( MathUtilities . GreaterThan ( curLineSize . U + sz . U , uvFinalSize . U ) ) //need to switch to another line
{
arrangeLine ( accumulatedV , curLineSize . V , firstInLine , i ) ;
accumulatedV + = curLineSize . V ;
curLineSize = sz ;
accumulatedV + = curLineSize . V ;
curLineSize = sz ;
if ( MathUtilities . GreaterThan ( sz . U , uvFinalSize . U ) ) // The element is wider then the constraint - give it a separate line
{
// Switch to next line which only contain one element
ArrangeLine ( accumulatedV , sz . V , i , + + i , useItemU , itemU ) ;
if ( MathUtilities . GreaterThan ( sz . U , uvFinalSize . U ) ) //the element is wider then the constraint - give it a separate line
accumulatedV + = sz . V ;
curLineSize = new UVSize ( orientation ) ;
}
firstInLine = i ;
}
else // Continue to accumulate a line
{
//switch to next line which only contain one element
arrangeLine ( accumulatedV , sz . V , i , + + i ) ;
accumulatedV + = sz . V ;
curLineSize = new UVSize ( Orientation ) ;
curLineSize . U + = sz . U ;
curLineSize . V = Max ( sz . V , curLineSize . V ) ;
}
firstInLine = i ;
}
else //continue to accumulate a line
{
curLineSize . U + = sz . U ;
curLineSize . V = Max ( sz . V , curLineSize . V ) ;
}
}
//a rrange the last line, if any
if ( firstInLine < C hildren. Count )
// Arrange the last line, if any
if ( firstInLine < children . Count )
{
a rrangeLine( accumulatedV , curLineSize . V , firstInLine , C hildren. Count ) ;
A rrangeLine( accumulatedV , curLineSize . V , firstInLine , c hildren. Count , useItemU , itemU ) ;
}
return finalSize ;
}
private void a rrangeLine( double v , double lineV , int start , int end )
private void A rrangeLine( double v , double lineV , int start , int end , bool useItemU , double itemU )
{
var orientation = Orientation ;
var children = Children ;
double u = 0 ;
bool isHorizontal = ( Orientation = = Orientation . Horizontal ) ;
bool isHorizontal = o rientation = = Orientation . Horizontal ;
for ( int i = start ; i < end ; i + + )
{
var child = C hildren[ i ] ;
var child = c hildren[ i ] ;
if ( child ! = null )
{
UVSize childSize = new UVSize ( O rientation, child . DesiredSize . Width , child . DesiredSize . Height ) ;
double layoutSlotU = childSize . U ;
var childSize = new UVSize ( o rientation, child . DesiredSize . Width , child . DesiredSize . Height ) ;
double layoutSlotU = useItemU ? itemU : childSize . U ;
child . Arrange ( new Rect (
( isHorizontal ? u : v ) ,
( isHorizontal ? v : u ) ,
( isHorizontal ? layoutSlotU : lineV ) ,
( isHorizontal ? lineV : layoutSlotU ) ) ) ;
isHorizontal ? u : v ,
isHorizontal ? v : u ,
isHorizontal ? layoutSlotU : lineV ,
isHorizontal ? lineV : layoutSlotU ) ) ;
u + = layoutSlotU ;
}
}
@ -232,12 +288,12 @@ namespace Avalonia.Controls
internal double Width
{
get { return ( _ orientation = = Orientation . Horizontal ? U : V ) ; }
get { return _ orientation = = Orientation . Horizontal ? U : V ; }
set { if ( _ orientation = = Orientation . Horizontal ) U = value ; else V = value ; }
}
internal double Height
{
get { return ( _ orientation = = Orientation . Horizontal ? V : U ) ; }
get { return _ orientation = = Orientation . Horizontal ? V : U ; }
set { if ( _ orientation = = Orientation . Horizontal ) V = value ; else U = value ; }
}
}