Skip to content

Commit

Permalink
feat(dragdrop): Fix the drag and drop hit testing
Browse files Browse the repository at this point in the history
  • Loading branch information
dr1rrb committed Dec 1, 2020
1 parent aaf4309 commit c2003d4
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 13 deletions.
16 changes: 9 additions & 7 deletions src/Uno.UI/UI/Xaml/DragDrop/DropUITarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ namespace Windows.UI.Xaml
{
internal class DropUITarget : ICoreDropOperationTarget
{
private static readonly GetHitTestability _getDropHitTestability = elt =>
private static GetHitTestability? _getDropHitTestability;
private static GetHitTestability GetDropHitTestability => _getDropHitTestability ??= (elt =>
{
var visiblity = elt.GetHitTestVisibility();
return visiblity switch
{
HitTestability.Collapsed => HitTestability.Collapsed,
_ when !elt.AllowDrop => HitTestability.Invisible,
_ => visiblity
HitTestability.Collapsed => (HitTestability.Collapsed, _getDropHitTestability!),
// Once we reached an element that AllowDrop, we only validate the hit testability for its children
_ when elt.AllowDrop => (visiblity, VisualTreeHelper.DefaultGetTestability),
_ => (HitTestability.Invisible, _getDropHitTestability!)
};
};
});


// Note: As drag events are routed (so they may be received by multiple elements), we might not have an entry for each drop targets.
// We will instead have entry only for leaf (a.k.a. OriginalSource).
Expand Down Expand Up @@ -124,7 +126,7 @@ public IAsyncOperation<DataPackageOperation> DropAsync(CoreDragInfo dragInfo)
{
var target = VisualTreeHelper.HitTest(
dragInfo.Position,
getTestability: _getDropHitTestability,
getTestability: GetDropHitTestability,
isStale: elt => elt.IsDragOver(dragInfo.SourceId));

// First raise the drag leave event on stale branch if any.
Expand Down
6 changes: 4 additions & 2 deletions src/Uno.UI/UI/Xaml/HitTestability.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
#nullable enable

using System;
using System.Linq;

namespace Windows.UI.Xaml
{
internal delegate HitTestability GetHitTestability(UIElement element);
internal delegate (HitTestability elementTestability, GetHitTestability childrenGetHitTestability) GetHitTestability(UIElement element);

internal enum HitTestability
{
Expand Down
11 changes: 8 additions & 3 deletions src/Uno.UI/UI/Xaml/Media/VisualTreeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,11 @@ internal static IReadOnlyList<_View> ClearChildren(UIElement view)
#endif
}

private static readonly GetHitTestability _defaultGetTestability = elt => elt.GetHitTestVisibility();
internal static readonly GetHitTestability DefaultGetTestability;
static VisualTreeHelper()
{
DefaultGetTestability = elt => (elt.GetHitTestVisibility(), DefaultGetTestability);
}

internal static (UIElement? element, Branch? stale) HitTest(
Point position,
Expand All @@ -267,7 +271,7 @@ internal static IReadOnlyList<_View> ClearChildren(UIElement view)
#endif
if (Window.Current.RootElement is UIElement root)
{
return SearchDownForTopMostElementAt(position, root, getTestability ?? _defaultGetTestability, isStale);
return SearchDownForTopMostElementAt(position, root, getTestability ?? DefaultGetTestability, isStale);
}

return default;
Expand All @@ -281,7 +285,8 @@ internal static IReadOnlyList<_View> ClearChildren(UIElement view)
Func<IEnumerable<UIElement>, IEnumerable<UIElement>>? childrenFilter = null)
{
var stale = default(Branch?);
var elementHitTestVisibility = getVisibility(element);
HitTestability elementHitTestVisibility;
(elementHitTestVisibility, getVisibility) = getVisibility(element);

#if TRACE_HIT_TESTING
using var _ = SET_TRACE_SUBJECT(element);
Expand Down
6 changes: 5 additions & 1 deletion src/Uno.UI/UI/Xaml/UIElement.netstd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ public partial class UIElement : DependencyObject
internal bool IsLoading { get; private set; }
#endif

private protected int Depth { get; private set; } = int.MinValue;
/// <summary>
/// Gets the element depth in the visual tree.
/// ** WARNING** This is set before the FrameworkElement loading event and cleared on unload.
/// </summary>
internal int Depth { get; private set; } = int.MinValue;

internal static void LoadingRootElement(UIElement visualTreeRoot)
=> visualTreeRoot.OnElementLoading(1);
Expand Down

0 comments on commit c2003d4

Please sign in to comment.