@ -2,7 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System ;
using Avalonia.Rendering ;
using Avalonia.VisualTree ;
namespace Avalonia
@ -20,10 +19,8 @@ namespace Avalonia
/// <returns>The point in client coordinates.</returns>
public static Point PointToClient ( this IVisual visual , PixelPoint point )
{
var ( root , offset ) = GetRootAndPosition ( visual ) ;
var screenOffset = PixelPoint . FromPoint ( ( Point ) offset , root . RenderScaling ) ;
var screenPoint = new PixelPoint ( point . X - screenOffset . X , point . Y - screenOffset . Y ) ;
return root . PointToClient ( screenPoint ) ;
var rootPoint = visual . VisualRoot . PointToClient ( point ) ;
return visual . VisualRoot . TranslatePoint ( rootPoint , visual ) . Value ;
}
/// <summary>
@ -34,48 +31,93 @@ namespace Avalonia
/// <returns>The point in screen coordinates.</returns>
public static PixelPoint PointToScreen ( this IVisual visual , Point point )
{
var p = GetRootAndPosition ( visual ) ;
return p . Item1 . PointToScreen ( point + p . Item2 ) ;
var p = visual . TranslatePoint ( point , visual . VisualRoot ) ;
return visual . VisualRoot . PointToScreen ( p . Value ) ;
}
/// <summary>
/// Returns a transform that transforms the visual's coordinates into the coordinates
/// of the specified <paramref name="to"/>.
/// </summary>
/// <param name="from">The visual whose coordinates are to be transformed.</param>
/// <param name="to">The visual to translate the coordinates to.</param>
/// <returns>
/// A <see cref="Matrix"/> containing the transform or null if the visuals don't share a
/// common ancestor.
/// </returns>
public static Matrix ? TransformToVisual ( this IVisual from , IVisual to )
{
var common = from . FindCommonVisualAncestor ( to ) ;
if ( common ! = null )
{
var thisOffset = GetOffsetFrom ( common , from ) ;
var thatOffset = GetOffsetFrom ( common , to ) ;
return - thatOffset * thisOffset ;
}
return null ;
}
/// <summary>
/// Translates a point relative to this visual to coordinates that are relative to the specified visual.
/// The visual and relativeTo should be descendants of the same root window
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="point">The point value, as relative to this visual.</param>
/// <param name="relativeTo">The visual to translate the given point into.</param>
/// <returns>A point value, now relative to the target visual rather than this source element.</returns>
public static Point TranslatePoint ( this IVisual visual , Point point , IVisual relativeTo )
/// <returns>
/// A point value, now relative to the target visual rather than this source element, or null if the
/// two elements have no common ancestor.
/// </returns>
public static Point ? TranslatePoint ( this IVisual visual , Point point , IVisual relativeTo )
{
var pos = GetRootAndPosition ( visual ) ;
var relToPos = GetRootAndPosition ( relativeTo ) ;
var transform = visual . TransformToVisual ( relativeTo ) ;
return point - ( relToPos . Item2 - pos . Item2 ) ;
if ( transform . HasValue )
{
return point . Transform ( transform . Value ) ;
}
return null ;
}
/// <summary>
/// Gets the root of the control's visual tree and the position of the control
/// in the root's coordinate space.
/// Gets a transform from an ancestor to a descendent.
/// </summary>
/// <param name="v">The visual.</param>
/// <returns>A tuple containing the root and the position of the control.</returns>
private static Tuple < IRenderRoot , Vector > GetRootAndPosition ( IVisual v )
/// <param name="ancestor">The ancestor visual.</param>
/// <param name="visual">The visual.</param>
/// <returns>The transform.</returns>
private static Matrix GetOffsetFrom ( IVisual ancestor , IVisual visual )
{
var result = new Vector ( ) ;
var result = Matrix . Identity ;
while ( ! ( v is IRenderRoot ) )
while ( visual ! = ancestor )
{
result = new Vector ( result . X + v . Bounds . X , result . Y + v . Bounds . Y ) ;
v = v . VisualParent ;
if ( visual . RenderTransform ? . Value ! = null )
{
var origin = visual . RenderTransformOrigin . ToPixels ( visual . Bounds . Size ) ;
var offset = Matrix . CreateTranslation ( origin ) ;
var renderTransform = ( - offset ) * visual . RenderTransform . Value * ( offset ) ;
result * = renderTransform ;
}
var topLeft = visual . Bounds . TopLeft ;
if ( topLeft ! = default )
{
result * = Matrix . CreateTranslation ( topLeft ) ;
}
visual = visual . VisualParent ;
if ( v = = null )
if ( visual = = null )
{
throw new InvalidOperationException ( "Control is not attached to visual tree." ) ;
throw new ArgumentException ( "'visual' is not a descendant of 'ancestor' .") ;
}
}
return Tuple . Create ( ( IRenderRoot ) v , result ) ;
return result ;
}
}
}