Browse Source

reformat LayoutTransformControl code

pull/468/head
donandren 11 years ago
parent
commit
a530f5b7e7
  1. 279
      src/Perspex.Controls/LayoutTransformControl.cs
  2. 13
      tests/Perspex.Controls.UnitTests/LayoutTransformControlTests.cs

279
src/Perspex.Controls/LayoutTransformControl.cs

@ -9,7 +9,7 @@ using Perspex.Controls.Primitives;
using Perspex.Media;
using Perspex.VisualTree;
using System;
//using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reactive.Linq;
@ -32,99 +32,61 @@ namespace Perspex.Controls
set { SetValue(LayoutTransformProperty, value); }
}
/// <summary>
/// Acceptable difference between two doubles.
/// </summary>
private const double AcceptableDelta = 0.0001;
/// <summary>
/// Number of decimals to round the Matrix to.
/// </summary>
private const int DecimalsAfterRound = 4;
/// <summary>
/// RenderTransform/MatrixTransform applied to TransformRoot.
/// </summary>
private MatrixTransform _matrixTransform;
/// <summary>
/// Transformation matrix corresponding to _matrixTransform.
/// </summary>
private Matrix _transformation;
public Control TransformRoot => _transformRoot ??
(_transformRoot = this.GetVisualChildren().OfType<Control>().FirstOrDefault());
/// <summary>
/// Actual DesiredSize of Child element (the value it returned from its MeasureOverride method).
/// Provides the behavior for the "Arrange" pass of layout.
/// </summary>
private Size _childActualSize = Size.Empty;
private Control _transformRoot;
public Control TransformRoot => _transformRoot ??
(_transformRoot = this.GetVisualChildren().OfType<Control>().FirstOrDefault());
private IDisposable _transformChangedEvent = null;
private void OnLayoutTransformChanged(PerspexPropertyChangedEventArgs e)
/// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
/// <returns>The actual size used.</returns>
protected override Size ArrangeOverride(Size finalSize)
{
var newTransform = e.NewValue as Transform;
if (_transformChangedEvent != null)
if (TransformRoot == null || LayoutTransform == null)
{
_transformChangedEvent.Dispose();
_transformChangedEvent = null;
return base.ArrangeOverride(finalSize);
}
if (newTransform != null)
// Determine the largest available size after the transformation
Size finalSizeTransformed = ComputeLargestTransformedSize(finalSize);
if (IsSizeSmaller(finalSizeTransformed, TransformRoot.DesiredSize))
{
_transformChangedEvent = Observable.FromEventPattern<EventHandler, EventArgs>(
v => newTransform.Changed += v, v => newTransform.Changed -= v)
.Subscribe(onNext: v => ApplyLayoutTransform());
// Some elements do not like being given less space than they asked for (ex: TextBlock)
// Bump the working size up to do the right thing by them
finalSizeTransformed = TransformRoot.DesiredSize;
}
ApplyLayoutTransform();
}
/// <summary>
/// Builds the visual tree for the LayoutTransformerControl when a new
/// template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
// Transform the working size to find its width/height
Rect transformedRect = new Rect(0, 0, finalSizeTransformed.Width, finalSizeTransformed.Height).TransformToAABB(_transformation);
// Create the Arrange rect to center the transformed content
Rect finalRect = new Rect(
-transformedRect.X + ((finalSize.Width - transformedRect.Width) / 2),
-transformedRect.Y + ((finalSize.Height - transformedRect.Height) / 2),
finalSizeTransformed.Width,
finalSizeTransformed.Height);
_matrixTransform = new MatrixTransform();
// Perform an Arrange on TransformRoot (containing Child)
Size arrangedsize;
TransformRoot.Arrange(finalRect);
arrangedsize = TransformRoot.Bounds.Size;
if (null != TransformRoot)
// This is the first opportunity under Silverlight to find out the Child's true DesiredSize
if (IsSizeSmaller(finalSizeTransformed, arrangedsize) && (Size.Empty == _childActualSize))
{
TransformRoot.RenderTransform = _matrixTransform;
TransformRoot.TransformOrigin = new RelativePoint(0, 0, RelativeUnit.Absolute);
//// Unfortunately, all the work so far is invalid because the wrong DesiredSize was used
//// Make a note of the actual DesiredSize
//_childActualSize = arrangedsize;
//// Force a new measure/arrange pass
//InvalidateMeasure();
}
ApplyLayoutTransform();
}
/// <summary>
/// Applies the layout transform on the LayoutTransformerControl content.
/// </summary>
/// <remarks>
/// Only used in advanced scenarios (like animating the LayoutTransform).
/// Should be used to notify the LayoutTransformer control that some aspect
/// of its Transform property has changed.
/// </remarks>
private void ApplyLayoutTransform()
{
if (LayoutTransform == null) return;
// Get the transform matrix and apply it
_transformation = RoundMatrix(LayoutTransform.Value, DecimalsAfterRound);
if (null != _matrixTransform)
else
{
_matrixTransform.Matrix = _transformation;
// Clear the "need to measure/arrange again" flag
_childActualSize = Size.Empty;
}
// New transform means re-layout is necessary
InvalidateMeasure();
// Return result to perform the transformation
return finalSize;
}
/// <summary>
@ -165,57 +127,100 @@ namespace Perspex.Controls
}
/// <summary>
/// Provides the behavior for the "Arrange" pass of layout.
/// Builds the visual tree for the LayoutTransformerControl when a new
/// template is applied.
/// </summary>
/// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
/// <returns>The actual size used.</returns>
protected override Size ArrangeOverride(Size finalSize)
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
if (TransformRoot == null || LayoutTransform == null)
{
return base.ArrangeOverride(finalSize);
}
base.OnTemplateApplied(e);
// Determine the largest available size after the transformation
Size finalSizeTransformed = ComputeLargestTransformedSize(finalSize);
if (IsSizeSmaller(finalSizeTransformed, TransformRoot.DesiredSize))
_matrixTransform = new MatrixTransform();
if (null != TransformRoot)
{
// Some elements do not like being given less space than they asked for (ex: TextBlock)
// Bump the working size up to do the right thing by them
finalSizeTransformed = TransformRoot.DesiredSize;
TransformRoot.RenderTransform = _matrixTransform;
TransformRoot.TransformOrigin = new RelativePoint(0, 0, RelativeUnit.Absolute);
}
// Transform the working size to find its width/height
Rect transformedRect = new Rect(0, 0, finalSizeTransformed.Width, finalSizeTransformed.Height).TransformToAABB(_transformation);
// Create the Arrange rect to center the transformed content
Rect finalRect = new Rect(
-transformedRect.X + ((finalSize.Width - transformedRect.Width) / 2),
-transformedRect.Y + ((finalSize.Height - transformedRect.Height) / 2),
finalSizeTransformed.Width,
finalSizeTransformed.Height);
ApplyLayoutTransform();
}
// Perform an Arrange on TransformRoot (containing Child)
Size arrangedsize;
TransformRoot.Arrange(finalRect);
arrangedsize = TransformRoot.Bounds.Size;
/// <summary>
/// Acceptable difference between two doubles.
/// </summary>
private const double AcceptableDelta = 0.0001;
// This is the first opportunity under Silverlight to find out the Child's true DesiredSize
if (IsSizeSmaller(finalSizeTransformed, arrangedsize) && (Size.Empty == _childActualSize))
{
//// Unfortunately, all the work so far is invalid because the wrong DesiredSize was used
//// Make a note of the actual DesiredSize
//_childActualSize = arrangedsize;
//// Force a new measure/arrange pass
//InvalidateMeasure();
}
else
/// <summary>
/// Number of decimals to round the Matrix to.
/// </summary>
private const int DecimalsAfterRound = 4;
/// <summary>
/// Actual DesiredSize of Child element (the value it returned from its MeasureOverride method).
/// </summary>
private Size _childActualSize = Size.Empty;
/// <summary>
/// RenderTransform/MatrixTransform applied to TransformRoot.
/// </summary>
private MatrixTransform _matrixTransform;
/// <summary>
/// Transformation matrix corresponding to _matrixTransform.
/// </summary>
private Matrix _transformation;
private IDisposable _transformChangedEvent = null;
private Control _transformRoot;
/// <summary>
/// Returns true if Size a is smaller than Size b in either dimension.
/// </summary>
/// <param name="a">Second Size.</param>
/// <param name="b">First Size.</param>
/// <returns>True if Size a is smaller than Size b in either dimension.</returns>
private static bool IsSizeSmaller(Size a, Size b)
{
return (a.Width + AcceptableDelta < b.Width) || (a.Height + AcceptableDelta < b.Height);
}
/// <summary>
/// Rounds the non-offset elements of a Matrix to avoid issues due to floating point imprecision.
/// </summary>
/// <param name="matrix">Matrix to round.</param>
/// <param name="decimals">Number of decimal places to round to.</param>
/// <returns>Rounded Matrix.</returns>
private static Matrix RoundMatrix(Matrix matrix, int decimals)
{
return new Matrix(
Math.Round(matrix.M11, decimals),
Math.Round(matrix.M12, decimals),
Math.Round(matrix.M21, decimals),
Math.Round(matrix.M22, decimals),
matrix.M31,
matrix.M32);
}
/// <summary>
/// Applies the layout transform on the LayoutTransformerControl content.
/// </summary>
/// <remarks>
/// Only used in advanced scenarios (like animating the LayoutTransform).
/// Should be used to notify the LayoutTransformer control that some aspect
/// of its Transform property has changed.
/// </remarks>
private void ApplyLayoutTransform()
{
if (LayoutTransform == null) return;
// Get the transform matrix and apply it
_transformation = RoundMatrix(LayoutTransform.Value, DecimalsAfterRound);
if (null != _matrixTransform)
{
// Clear the "need to measure/arrange again" flag
_childActualSize = Size.Empty;
_matrixTransform.Matrix = _transformation;
}
// Return result to perform the transformation
return finalSize;
// New transform means re-layout is necessary
InvalidateMeasure();
}
/// <summary>
@ -361,32 +366,24 @@ namespace Perspex.Controls
return computedSize;
}
/// <summary>
/// Returns true if Size a is smaller than Size b in either dimension.
/// </summary>
/// <param name="a">Second Size.</param>
/// <param name="b">First Size.</param>
/// <returns>True if Size a is smaller than Size b in either dimension.</returns>
private static bool IsSizeSmaller(Size a, Size b)
private void OnLayoutTransformChanged(PerspexPropertyChangedEventArgs e)
{
return (a.Width + AcceptableDelta < b.Width) || (a.Height + AcceptableDelta < b.Height);
}
var newTransform = e.NewValue as Transform;
/// <summary>
/// Rounds the non-offset elements of a Matrix to avoid issues due to floating point imprecision.
/// </summary>
/// <param name="matrix">Matrix to round.</param>
/// <param name="decimals">Number of decimal places to round to.</param>
/// <returns>Rounded Matrix.</returns>
private static Matrix RoundMatrix(Matrix matrix, int decimals)
{
return new Matrix(
Math.Round(matrix.M11, decimals),
Math.Round(matrix.M12, decimals),
Math.Round(matrix.M21, decimals),
Math.Round(matrix.M22, decimals),
matrix.M31,
matrix.M32);
if (_transformChangedEvent != null)
{
_transformChangedEvent.Dispose();
_transformChangedEvent = null;
}
if (newTransform != null)
{
_transformChangedEvent = Observable.FromEventPattern<EventHandler, EventArgs>(
v => newTransform.Changed += v, v => newTransform.Changed -= v)
.Subscribe(onNext: v => ApplyLayoutTransform());
}
ApplyLayoutTransform();
}
}
}

13
tests/Perspex.Controls.UnitTests/LayoutTransformControlTests.cs

@ -223,20 +223,17 @@ namespace Perspex.Controls.UnitTests
{
LayoutTransform = transform,
Template = new FuncControlTemplate<LayoutTransformControl>(
p =>
{
var c = new ContentPresenter() { Content = p.Content };
//we need to force create visual child
//so the measure after is correct
c.UpdateChild();
return c;
})
p => new ContentPresenter() { Content = p.Content })
};
lt.Content = new Rectangle() { Width = width, Height = height };
lt.ApplyTemplate();
//we need to force create visual child
//so the measure after is correct
(lt.Presenter as ContentPresenter).UpdateChild();
Assert.NotNull(lt.Presenter?.Child);
lt.Measure(Size.Infinity);

Loading…
Cancel
Save