// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Collections.Generic; using System.Linq; using Avalonia.Rendering; namespace Avalonia.VisualTree { /// /// Provides extension methods for working with the visual tree. /// public static class VisualExtensions { /// /// Calculates the distance from a visual's . /// /// The visual. /// The ancestor visual. /// /// The number of steps from the visual to the ancestor or -1 if /// is not a descendent of . /// public static int CalculateDistanceFromAncestor(this IVisual visual, IVisual ancestor) { Contract.Requires(visual != null); var result = 0; while (visual != null && visual != ancestor) { ++result; visual = visual.VisualParent; } return visual != null ? result : -1; } /// /// Tries to get the first common ancestor of two visuals. /// /// The first visual. /// The second visual. /// The common ancestor, or null if not found. public static IVisual FindCommonVisualAncestor(this IVisual visual, IVisual target) { Contract.Requires(visual != null); return visual.GetSelfAndVisualAncestors().Intersect(target.GetSelfAndVisualAncestors()) .FirstOrDefault(); } /// /// Enumerates the ancestors of an in the visual tree. /// /// The visual. /// The visual's ancestors. public static IEnumerable GetVisualAncestors(this IVisual visual) { Contract.Requires(visual != null); visual = visual.VisualParent; while (visual != null) { yield return visual; visual = visual.VisualParent; } } /// /// Enumerates an and its ancestors in the visual tree. /// /// The visual. /// The visual and its ancestors. public static IEnumerable GetSelfAndVisualAncestors(this IVisual visual) { Contract.Requires(visual != null); yield return visual; foreach (var ancestor in visual.GetVisualAncestors()) { yield return ancestor; } } /// /// Gets the first visual in the visual tree whose bounds contain a point. /// /// The root visual to test. /// The point. /// The visuals at the requested point. public static IVisual GetVisualAt(this IVisual visual, Point p) { Contract.Requires(visual != null); return visual.GetVisualsAt(p).FirstOrDefault(); } /// /// Enumerates the visible visuals in the visual tree whose bounds contain a point. /// /// The root visual to test. /// The point. /// The visuals at the requested point. public static IEnumerable GetVisualsAt( this IVisual visual, Point p) { Contract.Requires(visual != null); return visual.GetVisualsAt(p, x => x.IsVisible); } /// /// Enumerates the visuals in the visual tree whose bounds contain a point. /// /// The root visual to test. /// The point. /// /// A filter predicate. If the predicate returns false then the visual and all its /// children will be excluded from the results. /// /// The visuals at the requested point. public static IEnumerable GetVisualsAt( this IVisual visual, Point p, Func filter) { Contract.Requires(visual != null); var root = visual.GetVisualRoot(); var rootPoint = visual.TranslatePoint(p, root); if (rootPoint.HasValue) { return root.Renderer.HitTest(rootPoint.Value, visual, filter); } return Enumerable.Empty(); } /// /// Enumerates the children of an in the visual tree. /// /// The visual. /// The visual children. public static IEnumerable GetVisualChildren(this IVisual visual) { return visual.VisualChildren; } /// /// Enumerates the descendants of an in the visual tree. /// /// The visual. /// The visual's ancestors. public static IEnumerable GetVisualDescendants(this IVisual visual) { foreach (IVisual child in visual.VisualChildren) { yield return child; foreach (IVisual descendant in child.GetVisualDescendants()) { yield return descendant; } } } /// /// Enumerates an and its descendants in the visual tree. /// /// The visual. /// The visual and its ancestors. public static IEnumerable GetSelfAndVisualDescendants(this IVisual visual) { yield return visual; foreach (var ancestor in visual.GetVisualDescendants()) { yield return ancestor; } } /// /// Gets the visual parent of an . /// /// The visual. /// The parent, or null if the visual is unparented. public static IVisual GetVisualParent(this IVisual visual) { return visual.VisualParent; } /// /// Gets the visual parent of an . /// /// The type of the visual parent. /// The visual. /// /// The parent, or null if the visual is unparented or its parent is not of type . /// public static T GetVisualParent(this IVisual visual) where T : class { return visual.VisualParent as T; } /// /// Gets the root visual for an . /// /// The visual. /// /// The root visual or null if the visual is not rooted. /// public static IRenderRoot GetVisualRoot(this IVisual visual) { Contract.Requires(visual != null); return visual as IRenderRoot ?? visual.VisualRoot; } /// /// Tests whether an is an ancestor of another visual. /// /// The visual. /// The potential descendant. /// /// True if is an ancestor of ; /// otherwise false. /// public static bool IsVisualAncestorOf(this IVisual visual, IVisual target) { return target.GetVisualAncestors().Any(x => x == visual); } public static IEnumerable SortByZIndex(this IEnumerable elements) { return elements .Select((element, index) => new ZOrderElement { Element = element, Index = index, ZIndex = element.ZIndex, }) .OrderBy(x => x, null) .Select(x => x.Element); } private class ZOrderElement : IComparable { public IVisual Element { get; set; } public int Index { get; set; } public int ZIndex { get; set; } public int CompareTo(ZOrderElement other) { var z = other.ZIndex - ZIndex; if (z != 0) { return z; } else { return other.Index - Index; } } } } }