Browse Source

Part 5 of n

pull/2563/head
Jumar Macato 8 years ago
parent
commit
d7b3ebb9f7
No known key found for this signature in database GPG Key ID: B19884DAC3A5BF3F
  1. 11
      src/Avalonia.Controls/DefinitionBase.cs
  2. 430
      src/Avalonia.Controls/GridWPF.cs

11
src/Avalonia.Controls/DefinitionBase.cs

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Avalonia.Utilities;
namespace Avalonia.Controls
{
@ -186,7 +187,7 @@ namespace Avalonia.Controls
internal static bool IsUserMinSizePropertyValueValid(object value)
{
double v = (double)value;
return (!DoubleUtil.IsNaN(v) && v >= 0.0d && !Double.IsPositiveInfinity(v));
return (!MathUtilities.IsNaN(v) && v >= 0.0d && !Double.IsPositiveInfinity(v));
}
/// <summary>
@ -215,7 +216,7 @@ namespace Avalonia.Controls
internal static bool IsUserMaxSizePropertyValueValid(object value)
{
double v = (double)value;
return (!DoubleUtil.IsNaN(v) && v >= 0.0d);
return (!MathUtilities.IsNaN(v) && v >= 0.0d);
}
/// <summary>
@ -847,7 +848,7 @@ namespace Avalonia.Controls
sharedMinSize = Math.Max(sharedMinSize, _registry[i].MinSize);
}
bool sharedMinSizeChanged = !DoubleUtil.AreClose(_minSize, sharedMinSize);
bool sharedMinSizeChanged = !MathUtilities.AreClose(_minSize, sharedMinSize);
// compare accumulated min size with min sizes of the individual definitions
for (int i = 0, count = _registry.Count; i < count; ++i)
@ -857,7 +858,7 @@ namespace Avalonia.Controls
if (sharedMinSizeChanged || definitionBase.LayoutWasUpdated)
{
// if definition's min size is different, then need to re-measure
if (!DoubleUtil.AreClose(sharedMinSize, definitionBase.MinSize))
if (!MathUtilities.AreClose(sharedMinSize, definitionBase.MinSize))
{
Grid parentGrid = (Grid)definitionBase.Parent;
parentGrid.InvalidateMeasure();
@ -870,7 +871,7 @@ namespace Avalonia.Controls
// if measure is valid then also need to check arrange.
// Note: definitionBase.SizeCache is volatile but at this point
// it contains up-to-date final size
if (!DoubleUtil.AreClose(sharedMinSize, definitionBase.SizeCache))
if (!MathUtilities.AreClose(sharedMinSize, definitionBase.SizeCache))
{
Grid parentGrid = (Grid)definitionBase.Parent;
parentGrid.InvalidateArrange();

430
src/Avalonia.Controls/GridWPF.cs

@ -16,6 +16,7 @@ using Avalonia.Controls;
using Avalonia.Media;
using Avalonia;
using System.Collections;
using Avalonia.Utilities;
namespace Avalonia.Controls
{
@ -153,7 +154,7 @@ namespace Avalonia.Controls
{
child.Measure(constraint);
gridDesiredSize = new Size(
Math.Max(gridDesiredSize.Width, child.DesiredSize.Width),
Math.Max(gridDesiredSize.Width, child.DesiredSize.Width),
Math.Max(gridDesiredSize.Height, child.DesiredSize.Height));
}
}
@ -199,156 +200,6 @@ namespace Avalonia.Controls
Debug.Assert(DefinitionsU.Length > 0 && DefinitionsV.Length > 0);
// Grid classifies cells into four groups depending on
// the column / row type a cell belongs to (number corresponds to
// group number):
//
// Px Auto Star
// +--------+--------+--------+
// | | | |
// Px | 1 | 1 | 3 |
// | | | |
// +--------+--------+--------+
// | | | |
// Auto | 1 | 1 | 3 |
// | | | |
// +--------+--------+--------+
// | | | |
// Star | 4 | 2 | 4 |
// | | | |
// +--------+--------+--------+
//
// The group number indicates the order in which cells are measured.
// Certain order is necessary to be able to dynamically resolve star
// columns / rows sizes which are used as input for measuring of
// the cells belonging to them.
//
// However, there are cases when topology of a grid causes cyclical
// size dependences. For example:
//
//
// column width="Auto" column width="*"
// +----------------------+----------------------+
// | | |
// | | |
// | | |
// | | |
// row height="Auto" | | cell 1 2 |
// | | |
// | | |
// | | |
// | | |
// +----------------------+----------------------+
// | | |
// | | |
// | | |
// | | |
// row height="*" | cell 2 1 | |
// | | |
// | | |
// | | |
// | | |
// +----------------------+----------------------+
//
// In order to accurately calculate constraint width for "cell 1 2"
// (which is the remaining of grid's available width and calculated
// value of Auto column), "cell 2 1" needs to be calculated first,
// as it contributes to the Auto column's calculated value.
// At the same time in order to accurately calculate constraint
// height for "cell 2 1", "cell 1 2" needs to be calcualted first,
// as it contributes to Auto row height, which is used in the
// computation of Star row resolved height.
//
// to "break" this cyclical dependency we are making (arbitrary)
// decision to treat cells like "cell 2 1" as if they appear in Auto
// rows. And then recalculate them one more time when star row
// heights are resolved.
//
// (Or more strictly) the code below implement the following logic:
//
// +---------+
// | enter |
// +---------+
// |
// V
// +----------------+
// | Measure Group1 |
// +----------------+
// |
// V
// / - \
// / \
// Y / Can \ N
// +--------| Resolve |-----------+
// | \ StarsV? / |
// | \ / |
// | \ - / |
// V V
// +----------------+ / - \
// | Resolve StarsV | / \
// +----------------+ Y / Can \ N
// | +----| Resolve |------+
// V | \ StarsU? / |
// +----------------+ | \ / |
// | Measure Group2 | | \ - / |
// +----------------+ | V
// | | +-----------------+
// V | | Measure Group2' |
// +----------------+ | +-----------------+
// | Resolve StarsU | | |
// +----------------+ V V
// | +----------------+ +----------------+
// V | Resolve StarsU | | Resolve StarsU |
// +----------------+ +----------------+ +----------------+
// | Measure Group3 | | |
// +----------------+ V V
// | +----------------+ +----------------+
// | | Measure Group3 | | Measure Group3 |
// | +----------------+ +----------------+
// | | |
// | V V
// | +----------------+ +----------------+
// | | Resolve StarsV | | Resolve StarsV |
// | +----------------+ +----------------+
// | | |
// | | V
// | | +------------------+
// | | | Measure Group2'' |
// | | +------------------+
// | | |
// +----------------------+-------------------------+
// |
// V
// +----------------+
// | Measure Group4 |
// +----------------+
// |
// V
// +--------+
// | exit |
// +--------+
//
// where:
// * all [Measure GroupN] - regular Children measure process -
// each cell is measured given contraint size as an input
// and each cell's desired size is accumulated on the
// corresponding column / row;
// * [Measure Group2'] - is when each cell is measured with
// infinit height as a constraint and a cell's desired
// height is ignored;
// * [Measure Groups''] - is when each cell is measured (second
// time during single Grid.MeasureOverride) regularly but its
// returned width is ignored;
//
// This algorithm is believed to be as close to ideal as possible.
// It has the following drawbacks:
// * cells belonging to Group2 can be called to measure twice;
// * iff during second measure a cell belonging to Group2 returns
// desired width greater than desired width returned the first
// time, such a cell is going to be clipped, even though it
// appears in Auto column.
//
MeasureCellsGroup(extData.CellGroup1, constraint, false, false);
{
@ -478,7 +329,7 @@ namespace Avalonia.Controls
}
// update render bound on grid lines renderer visual
GridLinesRenderer gridLinesRenderer = EnsureGridLinesRenderer();
var gridLinesRenderer = EnsureGridLinesRenderer();
if (gridLinesRenderer != null)
{
gridLinesRenderer.UpdateRenderBounds(arrangeSize);
@ -624,23 +475,23 @@ namespace Avalonia.Controls
//
// read and cache child positioning properties
//
// read indices from the corresponding properties
// clamp to value < number_of_columns
// column >= 0 is guaranteed by property value validation callback
cell.ColumnIndex = Math.Min(Grid.ColumnProperty (child), DefinitionsU.Length - 1);
cell.ColumnIndex = Math.Min(child.GetValue(Grid.ColumnProperty), DefinitionsU.Length - 1);
// clamp to value < number_of_rows
// row >= 0 is guaranteed by property value validation callback
cell.RowIndex = Math.Min(GetRow(child), DefinitionsV.Length - 1);
cell.RowIndex = Math.Min(child.GetValue(Grid.RowProperty), DefinitionsV.Length - 1);
// read span properties
// clamp to not exceed beyond right side of the grid
// column_span > 0 is guaranteed by property value validation callback
cell.ColumnSpan = Math.Min(GetColumnSpan(child), DefinitionsU.Length - cell.ColumnIndex);
cell.ColumnSpan = Math.Min(child.GetValue(Grid.ColumnSpanProperty), DefinitionsU.Length - cell.ColumnIndex);
// clamp to not exceed beyond bottom side of the grid
// row_span > 0 is guaranteed by property value validation callback
cell.RowSpan = Math.Min(GetRowSpan(child), DefinitionsV.Length - cell.RowIndex);
cell.RowSpan = Math.Min(child.GetValue(Grid.RowSpanProperty), DefinitionsV.Length - cell.RowIndex);
Debug.Assert(0 <= cell.ColumnIndex && cell.ColumnIndex < DefinitionsU.Length);
Debug.Assert(0 <= cell.RowIndex && cell.RowIndex < DefinitionsV.Length);
@ -1063,7 +914,7 @@ namespace Avalonia.Controls
}
var child = Children[cell];
if (child != null)
{
Size childConstraint = new Size(cellMeasureWidth, cellMeasureHeight);
@ -1334,7 +1185,7 @@ namespace Avalonia.Controls
// discrepancy (defined below). This avoids discontinuities - small
// change in available space resulting in large change to one def's allocation.
// 3. Correct handling of large *-values, including Infinity.
private void ResolveStar(
DefinitionBase[] definitions,
double availableSize)
@ -1655,7 +1506,7 @@ namespace Avalonia.Controls
DefinitionBase[] definitions,
double finalSize,
bool columns)
{
{
int defCount = definitions.Length;
int[] definitionIndices = DefinitionIndices;
int minCount = 0, maxCount = 0;
@ -2219,12 +2070,12 @@ namespace Avalonia.Controls
if (ShowGridLines && (_gridLinesRenderer == null))
{
_gridLinesRenderer = new GridLinesRenderer();
this.AddVisualChild(_gridLinesRenderer);
this.VisualChildren.Add(_gridLinesRenderer);
}
if ((!ShowGridLines) && (_gridLinesRenderer != null))
{
this.RemoveVisualChild(_gridLinesRenderer);
this.VisualChildren.Remove(_gridLinesRenderer);
_gridLinesRenderer = null;
}
@ -2287,7 +2138,7 @@ namespace Avalonia.Controls
if (child != null)
{
Grid grid = VisualTreeHelper.GetParent(child) as Grid;
Grid grid = child.GetVisualParent() as Grid;
if (grid != null
&& grid.ExtData != null
&& grid.ListenToNotifications)
@ -2514,7 +2365,7 @@ namespace Avalonia.Controls
/// <returns><c>true</c> if d == 0.</returns>
private static bool _IsZero(double d)
{
return (Math.Abs(d) < c_epsilon);
return (Math.Abs(d) < double.Epsilon);
}
/// <summary>
@ -2525,7 +2376,7 @@ namespace Avalonia.Controls
/// <returns><c>true</c> if d1 == d2</returns>
private static bool _AreClose(double d1, double d2)
{
return (Math.Abs(d1 - d2) < c_epsilon);
return (Math.Abs(d1 - d2) < double.Epsilon);
}
/// <summary>
@ -2554,15 +2405,6 @@ namespace Avalonia.Controls
}
}
#endregion Private Properties
//------------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
#region Private Fields
private ExtendedData _data; // extended data instantiated on demand, for non-trivial case handling only
private Flags _flags; // grid validity / property caches dirtiness flags
private GridLinesRenderer _gridLinesRenderer;
@ -2573,15 +2415,6 @@ namespace Avalonia.Controls
// Stores unrounded values and rounding errors during layout rounding.
double[] _roundingErrors;
#endregion Private Fields
//------------------------------------------------------
//
// Static Fields
//
//------------------------------------------------------
#region Static Fields
private const double c_epsilon = 1e-5; // used in fp calculations
private const double c_starClip = 1e298; // used as maximum for clipping star values during normalization
private const int c_layoutLoopMaxCount = 5; // 5 is an arbitrary constant chosen to end the measure loop
@ -2594,16 +2427,6 @@ namespace Avalonia.Controls
private static readonly IComparer s_maxRatioComparer = new MaxRatioComparer();
private static readonly IComparer s_starWeightComparer = new StarWeightComparer();
#endregion Static Fields
//------------------------------------------------------
//
// Private Structures / Classes
//
//------------------------------------------------------
#region Private Structures Classes
/// <summary>
/// Extended data instantiated on demand, when grid handles non-trivial case.
/// </summary>
@ -2656,140 +2479,6 @@ namespace Avalonia.Controls
ArrangeOverrideInProgress = 0x00080000, // "1" while in the context of Grid.ArrangeOverride
}
#endregion Private Structures Classes
//------------------------------------------------------
//
// Properties
//
//------------------------------------------------------
#region Properties
/// <summary>
/// ShowGridLines property. This property is used mostly
/// for simplification of visual debuggig. When it is set
/// to <c>true</c> grid lines are drawn to visualize location
/// of grid lines.
/// </summary>
public static readonly DependencyProperty ShowGridLinesProperty =
DependencyProperty.Register(
"ShowGridLines",
typeof(bool),
typeof(Grid),
new FrameworkPropertyMetadata(
false,
new PropertyChangedCallback(OnShowGridLinesPropertyChanged)));
/// <summary>
/// Column property. This is an attached property.
/// Grid defines Column property, so that it can be set
/// on any element treated as a cell. Column property
/// specifies child's position with respect to columns.
/// </summary>
/// <remarks>
/// <para> Columns are 0 - based. In order to appear in first column, element
/// should have Column property set to <c>0</c>. </para>
/// <para> Default value for the property is <c>0</c>. </para>
/// </remarks>
[CommonDependencyProperty]
public static readonly DependencyProperty ColumnProperty =
DependencyProperty.RegisterAttached(
"Column",
typeof(int),
typeof(Grid),
new FrameworkPropertyMetadata(
0,
new PropertyChangedCallback(OnCellAttachedPropertyChanged)),
new ValidateValueCallback(IsIntValueNotNegative));
/// <summary>
/// Row property. This is an attached property.
/// Grid defines Row, so that it can be set
/// on any element treated as a cell. Row property
/// specifies child's position with respect to rows.
/// <remarks>
/// <para> Rows are 0 - based. In order to appear in first row, element
/// should have Row property set to <c>0</c>. </para>
/// <para> Default value for the property is <c>0</c>. </para>
/// </remarks>
/// </summary>
[CommonDependencyProperty]
public static readonly DependencyProperty RowProperty =
DependencyProperty.RegisterAttached(
"Row",
typeof(int),
typeof(Grid),
new FrameworkPropertyMetadata(
0,
new PropertyChangedCallback(OnCellAttachedPropertyChanged)),
new ValidateValueCallback(IsIntValueNotNegative));
/// <summary>
/// ColumnSpan property. This is an attached property.
/// Grid defines ColumnSpan, so that it can be set
/// on any element treated as a cell. ColumnSpan property
/// specifies child's width with respect to columns.
/// Example, ColumnSpan == 2 means that child will span across two columns.
/// </summary>
/// <remarks>
/// Default value for the property is <c>1</c>.
/// </remarks>
[CommonDependencyProperty]
public static readonly DependencyProperty ColumnSpanProperty =
DependencyProperty.RegisterAttached(
"ColumnSpan",
typeof(int),
typeof(Grid),
new FrameworkPropertyMetadata(
1,
new PropertyChangedCallback(OnCellAttachedPropertyChanged)),
new ValidateValueCallback(IsIntValueGreaterThanZero));
/// <summary>
/// RowSpan property. This is an attached property.
/// Grid defines RowSpan, so that it can be set
/// on any element treated as a cell. RowSpan property
/// specifies child's height with respect to row grid lines.
/// Example, RowSpan == 3 means that child will span across three rows.
/// </summary>
/// <remarks>
/// Default value for the property is <c>1</c>.
/// </remarks>
[CommonDependencyProperty]
public static readonly DependencyProperty RowSpanProperty =
DependencyProperty.RegisterAttached(
"RowSpan",
typeof(int),
typeof(Grid),
new FrameworkPropertyMetadata(
1,
new PropertyChangedCallback(OnCellAttachedPropertyChanged)),
new ValidateValueCallback(IsIntValueGreaterThanZero));
/// <summary>
/// IsSharedSizeScope property marks scoping element for shared size.
/// </summary>
public static readonly DependencyProperty IsSharedSizeScopeProperty =
DependencyProperty.RegisterAttached(
"IsSharedSizeScope",
typeof(bool),
typeof(Grid),
new FrameworkPropertyMetadata(
false,
new PropertyChangedCallback(DefinitionBase.OnIsSharedSizeScopePropertyChanged)));
#endregion Properties
//------------------------------------------------------
//
// Internal Structures / Classes
//
//------------------------------------------------------
#region Internal Structures Classes
/// <summary>
/// LayoutTimeSizeType is used internally and reflects layout-time size type.
/// </summary>
@ -2802,16 +2491,6 @@ namespace Avalonia.Controls
Star = 0x04,
}
#endregion Internal Structures Classes
//------------------------------------------------------
//
// Private Structures / Classes
//
//------------------------------------------------------
#region Private Structures Classes
/// <summary>
/// CellCache stored calculated values of
/// 1. attached cell positioning properties;
@ -3331,7 +3010,7 @@ namespace Avalonia.Controls
return result;
}
}
/// <summary>
/// Helper to render grid lines.
/// </summary>
@ -3342,53 +3021,52 @@ namespace Avalonia.Controls
/// </summary>
static GridLinesRenderer()
{
s_oddDashPen = new Pen(Brushes.Blue, c_penWidth);
DoubleCollection oddDashArray = new DoubleCollection();
var oddDashArray = new List<double>();
oddDashArray.Add(c_dashLength);
oddDashArray.Add(c_dashLength);
s_oddDashPen.DashStyle = new DashStyle(oddDashArray, 0);
s_oddDashPen.DashCap = PenLineCap.Flat;
s_oddDashPen.Freeze();
var ds1 = new DashStyle(oddDashArray, 0);
s_oddDashPen = new Pen(Brushes.Blue,
c_penWidth,
lineCap: PenLineCap.Flat,
dashStyle: ds1);
s_evenDashPen = new Pen(Brushes.Yellow, c_penWidth);
DoubleCollection evenDashArray = new DoubleCollection();
var evenDashArray = new List<double>();
evenDashArray.Add(c_dashLength);
evenDashArray.Add(c_dashLength);
s_evenDashPen.DashStyle = new DashStyle(evenDashArray, c_dashLength);
s_evenDashPen.DashCap = PenLineCap.Flat;
s_evenDashPen.Freeze();
var ds2 = new DashStyle(evenDashArray, 0);
s_evenDashPen = new Pen(Brushes.Yellow,
c_penWidth,
lineCap: PenLineCap.Flat,
dashStyle: ds2);
}
/// <summary>
/// UpdateRenderBounds.
/// </summary>
/// <param name="boundsSize">Size of render bounds</param>
internal void UpdateRenderBounds(Size boundsSize)
public override void Render(DrawingContext drawingContext)
{
using (DrawingContext drawingContext = RenderOpen())
var grid = this.GetVisualParent() as Grid;
if (grid == null
|| grid.ShowGridLines == false)
{
Grid grid = VisualTreeHelper.GetParent(this) as Grid;
if (grid == null
|| grid.ShowGridLines == false)
{
return;
}
return;
}
for (int i = 1; i < grid.DefinitionsU.Length; ++i)
{
DrawGridLine(
drawingContext,
grid.DefinitionsU[i].FinalOffset, 0.0,
grid.DefinitionsU[i].FinalOffset, boundsSize.Height);
}
for (int i = 1; i < grid.DefinitionsU.Length; ++i)
{
DrawGridLine(
drawingContext,
grid.DefinitionsU[i].FinalOffset, 0.0,
grid.DefinitionsU[i].FinalOffset, lastArrangeSize.Height);
}
for (int i = 1; i < grid.DefinitionsV.Length; ++i)
{
DrawGridLine(
drawingContext,
0.0, grid.DefinitionsV[i].FinalOffset,
boundsSize.Width, grid.DefinitionsV[i].FinalOffset);
}
for (int i = 1; i < grid.DefinitionsV.Length; ++i)
{
DrawGridLine(
drawingContext,
0.0, grid.DefinitionsV[i].FinalOffset,
lastArrangeSize.Width, grid.DefinitionsV[i].FinalOffset);
}
}
@ -3402,12 +3080,18 @@ namespace Avalonia.Controls
double endX,
double endY)
{
Point start = new Point(startX, startY);
Point end = new Point(endX, endY);
var start = new Point(startX, startY);
var end = new Point(endX, endY);
drawingContext.DrawLine(s_oddDashPen, start, end);
drawingContext.DrawLine(s_evenDashPen, start, end);
}
internal void UpdateRenderBounds(Size arrangeSize)
{
lastArrangeSize = arrangeSize;
this.InvalidateVisual();
}
private static Size lastArrangeSize;
private const double c_dashLength = 4.0; //
private const double c_penWidth = 1.0; //
private static readonly Pen s_oddDashPen; // first pen to draw dash

Loading…
Cancel
Save