using System;
using Avalonia.VisualTree;
namespace Avalonia
{
///
/// Extension methods for .
///
public static class VisualExtensions
{
///
/// Converts a point from screen to client coordinates.
///
/// The visual.
/// The point in screen coordinates.
/// The point in client coordinates.
public static Point PointToClient(this IVisual visual, PixelPoint point)
{
var root = visual.VisualRoot ??
throw new ArgumentException("Control does not belong to a visual tree.", nameof(visual));
var rootPoint = root.PointToClient(point);
return root.TranslatePoint(rootPoint, visual)!.Value;
}
///
/// Converts a point from client to screen coordinates.
///
/// The visual.
/// The point in client coordinates.
/// The point in screen coordinates.
public static PixelPoint PointToScreen(this IVisual visual, Point point)
{
var root = visual.VisualRoot ??
throw new ArgumentException("Control does not belong to a visual tree.", nameof(visual));
var p = visual.TranslatePoint(point, root);
return visual.VisualRoot.PointToScreen(p!.Value);
}
///
/// Returns a transform that transforms the visual's coordinates into the coordinates
/// of the specified .
///
/// The visual whose coordinates are to be transformed.
/// The visual to translate the coordinates to.
///
/// A containing the transform or null if the visuals don't share a
/// common ancestor.
///
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);
if (!thatOffset.TryInvert(out var thatOffsetInverted))
{
return null;
}
return thatOffsetInverted * thisOffset;
}
return null;
}
///
/// Translates a point relative to this visual to coordinates that are relative to the specified visual.
///
/// The visual.
/// The point value, as relative to this visual.
/// The visual to translate the given point into.
///
/// A point value, now relative to the target visual rather than this source element, or null if the
/// two elements have no common ancestor.
///
public static Point? TranslatePoint(this IVisual visual, Point point, IVisual relativeTo)
{
var transform = visual.TransformToVisual(relativeTo);
if (transform.HasValue)
{
return point.Transform(transform.Value);
}
return null;
}
///
/// Gets a transform from an ancestor to a descendent.
///
/// The ancestor visual.
/// The visual.
/// The transform.
private static Matrix GetOffsetFrom(IVisual ancestor, IVisual visual)
{
var result = Matrix.Identity;
IVisual? v = visual;
while (v != ancestor)
{
// this should be calculated BEFORE renderTransform
if (v.HasMirrorTransform)
{
var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, v.Bounds.Width, 0);
result *= mirrorMatrix;
}
if (v.RenderTransform?.Value != null)
{
var origin = v.RenderTransformOrigin.ToPixels(v.Bounds.Size);
var offset = Matrix.CreateTranslation(origin);
var renderTransform = (-offset) * v.RenderTransform.Value * (offset);
result *= renderTransform;
}
var topLeft = v.Bounds.TopLeft;
if (topLeft != default)
{
result *= Matrix.CreateTranslation(topLeft);
}
v = v.VisualParent;
if (v == null)
{
throw new ArgumentException("'visual' is not a descendant of 'ancestor'.");
}
}
return result;
}
}
}