提交 027323be 编写于 作者: D David

fix(pointers): Make sure to cleanup pointers state when an element is being recycled

上级 be89404c
......@@ -1129,6 +1129,8 @@ namespace Windows.UI.Xaml.Controls
ClearContainerForItemOverride(element, item);
ContainerClearedForItem(item, element as SelectorItem);
UIElement.PrepareForRecycle(element);
if (element is ContentPresenter presenter
&& (
presenter.ContentTemplate == ItemTemplate
......
......@@ -257,6 +257,11 @@ namespace Windows.UI.Xaml.Controls
if (vh != null)
{
vh.IsDetached = true;
// This should be invoked only from the LV.CleanContainer()
// **BUT** the container is not Cleaned/Prepared by the LV on Android
// https://github.com/unoplatform/uno/issues/11957
UIElement.PrepareForRecycle(view);
}
}
base.DetachViewFromParent(index);
......
......@@ -216,24 +216,55 @@ namespace Windows.UI.Xaml
if (sender is UIElement elt && elt.GetHitTestVisibility() == HitTestability.Collapsed)
{
elt.Release(PointerCaptureKind.Any);
elt.ClearPressed();
elt.SetOver(null, false, ctx: BubblingContext.NoBubbling);
elt.ClearDragOver();
_currentPointerEventDispatch.VisualTreeAltered = true;
elt.ClearPointerState();
}
};
private static readonly RoutedEventHandler ClearPointersStateOnUnload = (object sender, RoutedEventArgs args) =>
{
if (sender is UIElement elt)
{
elt.Release(PointerCaptureKind.Any);
elt.ClearPressed();
elt.SetOver(null, false, ctx: BubblingContext.NoBubbling);
elt.ClearDragOver();
}
_currentPointerEventDispatch.VisualTreeAltered = true;
(sender as UIElement)?.ClearPointerState();
};
private partial void ClearPointerStateOnRecycle()
{
_currentPointerEventDispatch.VisualTreeAltered = true;
ClearPointerState();
}
internal void ClearPointerState()
{
Release(PointerCaptureKind.Any);
ClearPressed();
SetOver(null, false, ctx: BubblingContext.NoBubbling);
ClearDragOver();
}
[ThreadStatic]
private static PointerEventDispatchResult _currentPointerEventDispatch;
internal static void BeginPointerEventDispatch()
=> _currentPointerEventDispatch = new();
internal static PointerEventDispatchResult EndPointerEventDispatch()
=> _currentPointerEventDispatch; // No need to clean it right now, we can safely wait for the next sequence to do it.
internal struct PointerEventDispatchResult
{
/// <summary>
/// Indicates that the visual tree has been modified in a way that the input manager must perform a complete hit testing sequence before dispatching a new event.
/// </summary>
/// <remarks>
/// This is designed for the case where for a single native pointer event, we are dispatching multiple managed events (e.g. managed Enter/Exit when we get only a native Move)
/// for all other cases **a full hit test must be perform**.
/// This means that we must not "capture"/cache the current top-most-element (a.k.a. OriginalSource) and try to update it on next event
/// as this flag does not take in consideration RenderTransform and other layout modification that does not alter the state of the pointer.
/// </remarks>
/// <remarks>This is used only for managed dispatch.</remarks>
public bool VisualTreeAltered { get; set; }
}
/// <summary>
/// Indicates if this element or one of its child might be target pointer pointer events.
/// Be aware this doesn't means that the element itself can be actually touched by user,
......
......@@ -910,6 +910,45 @@ namespace Windows.UI.Xaml
}
#endif
/// <summary>
/// This method has to be invoked for element that are going to be recycled WITHOUT necessarily being unloaded / loaded.
/// For instance, this is is not expected to be invoked for elements recycled by the the template pool as they are always unloaded.
/// The main use case is for ListView and is expected to be invoked by the ListView.CleanUpContainer.
/// </summary>
/// <remarks>This will walk the tree down to invoke this on all children!</remarks>
internal static void PrepareForRecycle(object view)
{
if (view is UIElement elt)
{
elt.PrepareForRecycle();
}
else
{
foreach (var child in VisualTreeHelper.GetManagedVisualChildren(view))
{
child.PrepareForRecycle();
}
}
}
/// <summary>
/// This method has to be invoked on element that are going to be recycled WITHOUT necessarily being unloaded / loaded.
/// For instance, this is is not expected to be invoked for elements recycled by the the template pool as they are always unloaded.
/// The main use case is for ListView and is expected to be invoked by the ListView.CleanUpContainer.
/// </summary>
/// <remarks>This will walk the tree down to invoke this on all children!</remarks>
internal virtual void PrepareForRecycle()
{
ClearPointerStateOnRecycle();
foreach (var child in VisualTreeHelper.GetManagedVisualChildren(this))
{
child.PrepareForRecycle();
}
}
private partial void ClearPointerStateOnRecycle();
internal virtual bool IsViewHit() => true;
internal virtual bool IsEnabledOverride() => true;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册