From cd2b26bc949d3a6f347d4dd2e2d27b502423ae73 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 1 May 2020 20:09:36 +0200 Subject: [PATCH 1/3] Add extra methods to LogicalExtensions and optimize a few methods as well. --- .../Platform/DefaultMenuInteractionHandler.cs | 2 +- .../ManagedFileChooser.xaml.cs | 2 +- .../LogicalTree/LogicalExtensions.cs | 93 ++++++++++++++++++- .../VisualTree/VisualExtensions.cs | 14 ++- 4 files changed, 105 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 3715bc52a4..8ad622ba4a 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -392,7 +392,7 @@ namespace Avalonia.Controls.Platform { var control = e.Source as ILogical; - if (!Menu.IsLogicalParentOf(control)) + if (!Menu.IsLogicalAncestorOf(control)) { Menu.Close(); } diff --git a/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs b/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs index b967b40c0d..616f260397 100644 --- a/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs +++ b/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs @@ -34,7 +34,7 @@ namespace Avalonia.Dialogs return; } - var isQuickLink = _quickLinksRoot.IsLogicalParentOf(e.Source as Control); + var isQuickLink = _quickLinksRoot.IsLogicalAncestorOf(e.Source as Control); if (e.ClickCount == 2 || isQuickLink) { if (model.ItemType == ManagedFileChooserItemType.File) diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs index 1003375978..bd05796578 100644 --- a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs +++ b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; namespace Avalonia.LogicalTree { @@ -29,6 +28,35 @@ namespace Avalonia.LogicalTree } } + /// + /// Finds first ancestor of given type. + /// + /// Ancestor type. + /// The logical. + /// If given logical should be included in search. + /// First ancestor of given type. + public static T FindLogicalAncestorOfType(this ILogical logical, bool includeSelf = false) where T : class + { + if (logical is null) + { + return null; + } + + ILogical parent = includeSelf ? logical : logical.LogicalParent; + + while (parent != null) + { + if (parent is T result) + { + return result; + } + + parent = parent.LogicalParent; + } + + return null; + } + public static IEnumerable GetLogicalChildren(this ILogical logical) { return logical.LogicalChildren; @@ -57,6 +85,28 @@ namespace Avalonia.LogicalTree } } + /// + /// Finds first descendant of given type. + /// + /// Descendant type. + /// The logical. + /// If given logical should be included in search. + /// First descendant of given type. + public static T FindLogicalDescendantOfType(this ILogical logical, bool includeSelf = false) where T : class + { + if (logical is null) + { + return null; + } + + if (includeSelf && logical is T result) + { + return result; + } + + return FindDescendantOfTypeCore(logical); + } + public static ILogical GetLogicalParent(this ILogical logical) { return logical.LogicalParent; @@ -80,9 +130,46 @@ namespace Avalonia.LogicalTree } } - public static bool IsLogicalParentOf(this ILogical logical, ILogical target) + public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target) { - return target.GetLogicalAncestors().Any(x => x == logical); + ILogical current = target?.LogicalParent; + + while (current != null) + { + if (current == logical) + { + return true; + } + + current = current.LogicalParent; + } + + return false; + } + + private static T FindDescendantOfTypeCore(ILogical logical) where T : class + { + var logicalChildren = logical.LogicalChildren; + var logicalChildrenCount = logicalChildren.Count; + + for (var i = 0; i < logicalChildrenCount; i++) + { + ILogical child = logicalChildren[i]; + + if (child is T result) + { + return result; + } + + var childResult = FindDescendantOfTypeCore(child); + + if (!(childResult is null)) + { + return childResult; + } + } + + return null; } } } diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs index 4b3e757e7a..636aa6836e 100644 --- a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs @@ -377,7 +377,19 @@ namespace Avalonia.VisualTree /// public static bool IsVisualAncestorOf(this IVisual visual, IVisual target) { - return target.GetVisualAncestors().Any(x => x == visual); + IVisual current = target; + + while (current != null) + { + if (current == visual) + { + return true; + } + + current = current.VisualParent; + } + + return false; } public static IEnumerable SortByZIndex(this IEnumerable elements) From f3dfb5d18cfa9ff375c0bd378fc9a5d990ca8c55 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 2 May 2020 12:31:47 +0200 Subject: [PATCH 2/3] Fix broken visual ancestor. --- src/Avalonia.Visuals/VisualTree/VisualExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs index 636aa6836e..8c4004efdc 100644 --- a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs @@ -377,7 +377,7 @@ namespace Avalonia.VisualTree /// public static bool IsVisualAncestorOf(this IVisual visual, IVisual target) { - IVisual current = target; + IVisual current = target?.VisualParent; while (current != null) { From c9d1685f04a777e864480693626069fca2ec9b4f Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 2 May 2020 12:55:49 +0200 Subject: [PATCH 3/3] Add comments copied from VisualExtensions. --- .../LogicalTree/LogicalExtensions.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs index bd05796578..458ab0fce2 100644 --- a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs +++ b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs @@ -3,8 +3,16 @@ using System.Collections.Generic; namespace Avalonia.LogicalTree { + /// + /// Provides extension methods for working with the logical tree. + /// public static class LogicalExtensions { + /// + /// Enumerates the ancestors of an in the logical tree. + /// + /// The logical. + /// The logical's ancestors. public static IEnumerable GetLogicalAncestors(this ILogical logical) { Contract.Requires(logical != null); @@ -18,6 +26,11 @@ namespace Avalonia.LogicalTree } } + /// + /// Enumerates an and its ancestors in the logical tree. + /// + /// The logical. + /// The logical and its ancestors. public static IEnumerable GetSelfAndLogicalAncestors(this ILogical logical) { yield return logical; @@ -57,11 +70,21 @@ namespace Avalonia.LogicalTree return null; } + /// + /// Enumerates the children of an in the logical tree. + /// + /// The logical. + /// The logical children. public static IEnumerable GetLogicalChildren(this ILogical logical) { return logical.LogicalChildren; } + /// + /// Enumerates the descendants of an in the logical tree. + /// + /// The logical. + /// The logical's ancestors. public static IEnumerable GetLogicalDescendants(this ILogical logical) { foreach (ILogical child in logical.LogicalChildren) @@ -75,6 +98,11 @@ namespace Avalonia.LogicalTree } } + /// + /// Enumerates an and its descendants in the logical tree. + /// + /// The logical. + /// The logical and its ancestors. public static IEnumerable GetSelfAndLogicalDescendants(this ILogical logical) { yield return logical; @@ -107,16 +135,34 @@ namespace Avalonia.LogicalTree return FindDescendantOfTypeCore(logical); } + /// + /// Gets the logical parent of an . + /// + /// The logical. + /// The parent, or null if the logical is unparented. public static ILogical GetLogicalParent(this ILogical logical) { return logical.LogicalParent; } + /// + /// Gets the logical parent of an . + /// + /// The type of the logical parent. + /// The logical. + /// + /// The parent, or null if the logical is unparented or its parent is not of type . + /// public static T GetLogicalParent(this ILogical logical) where T : class { return logical.LogicalParent as T; } + /// + /// Enumerates the siblings of an in the logical tree. + /// + /// The logical. + /// The logical siblings. public static IEnumerable GetLogicalSiblings(this ILogical logical) { ILogical parent = logical.LogicalParent; @@ -130,6 +176,15 @@ namespace Avalonia.LogicalTree } } + /// + /// Tests whether an is an ancestor of another logical. + /// + /// The logical. + /// The potential descendant. + /// + /// True if is an ancestor of ; + /// otherwise false. + /// public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target) { ILogical current = target?.LogicalParent;