Browse Source

Fix TransformedBounds for invisible controls.

Previously the BoundsTracker.TransformedBounds attached property wasn't
being cleared for controls that were invisible, resulting in incorrect
hit testing in certain situations (the newly added TreeView page in
ControlCatalog demonstrated the behavior when fully expanding the first
node and its children).
pull/626/head
Steven Kirk 10 years ago
parent
commit
6b80d51fe6
  1. 2
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  2. 17
      src/Avalonia.SceneGraph/Rendering/RendererMixin.cs
  3. 26
      src/Avalonia.SceneGraph/VisualTree/BoundsTracker.cs
  4. 2
      src/Avalonia.SceneGraph/VisualTree/VisualExtensions.cs
  5. 6
      tests/Avalonia.SceneGraph.UnitTests/VisualTree/BoundsTrackerTests.cs

2
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@ -120,7 +120,7 @@ namespace Avalonia.Controls.Primitives
info.Subscription = _tracker.Track(adorned).Subscribe(x =>
{
info.Bounds = x;
info.Bounds = x.Value;
InvalidateArrange();
});
}

17
src/Avalonia.SceneGraph/Rendering/RendererMixin.cs

@ -142,11 +142,28 @@ namespace Avalonia.Rendering
var childClipRect = clipRect.Translate(-childBounds.Position);
context.Render(child, childClipRect);
}
else
{
ClearTransformedBounds(child);
}
}
ReturnListToPool(lst);
}
}
if (!visual.IsVisible)
{
ClearTransformedBounds(visual);
}
}
private static void ClearTransformedBounds(IVisual visual)
{
foreach (var e in visual.GetSelfAndVisualDescendents())
{
BoundsTracker.SetTransformedBounds((Visual)visual, null);
}
}
private static void ReturnListToPool(List<IVisual> lst)

26
src/Avalonia.SceneGraph/VisualTree/BoundsTracker.cs

@ -9,33 +9,43 @@ namespace Avalonia.VisualTree
/// Tracks the bounds of a control.
/// </summary>
/// <remarks>
/// This class is used by Adorners to track the control that the adorner is attached to.
/// This class is used to track a controls's bounds for hit testing.
/// TODO: This shouldn't be implemented as an attached property: it would be more performant
/// to just store bounds in some sort of central repository.
/// </remarks>
public class BoundsTracker
{
private static AttachedProperty<TransformedBounds> TransformedBoundsProperty =
AvaloniaProperty.RegisterAttached<BoundsTracker, Visual, TransformedBounds>("TransformedBounds");
/// <summary>
/// Defines the TransformedBounds attached property.
/// </summary>
private static AttachedProperty<TransformedBounds?> TransformedBoundsProperty =
AvaloniaProperty.RegisterAttached<BoundsTracker, Visual, TransformedBounds?>("TransformedBounds");
/// <summary>
/// Starts tracking the specified visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>An observable that returns the tracked bounds.</returns>
public IObservable<TransformedBounds> Track(Visual visual)
public IObservable<TransformedBounds?> Track(Visual visual)
{
return visual.GetObservable(TransformedBoundsProperty);
}
internal static void SetTransformedBounds(Visual visual, TransformedBounds bounds)
/// <summary>
/// Sets the transformed bounds of the visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="value">The transformed bounds.</param>
internal static void SetTransformedBounds(Visual visual, TransformedBounds? value)
{
visual.SetValue(TransformedBoundsProperty, bounds);
visual.SetValue(TransformedBoundsProperty, value);
}
/// <summary>
/// Gets the transformed bounds of the visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>The transformed bounds.</returns>
public static TransformedBounds GetTransformedBounds(Visual visual) => visual.GetValue(TransformedBoundsProperty);
/// <returns>The transformed bounds or null if the visual is not visible.</returns>
public static TransformedBounds? GetTransformedBounds(Visual visual) => visual.GetValue(TransformedBoundsProperty);
}
}

2
src/Avalonia.SceneGraph/VisualTree/VisualExtensions.cs

@ -104,7 +104,7 @@ namespace Avalonia.VisualTree
if (filter?.Invoke(visual) != false)
{
bool containsPoint = BoundsTracker.GetTransformedBounds((Visual)visual).Contains(p);
bool containsPoint = BoundsTracker.GetTransformedBounds((Visual)visual)?.Contains(p) == true;
if ((containsPoint || !visual.ClipToBounds) && visual.VisualChildren.Any())
{

6
tests/Avalonia.SceneGraph.UnitTests/VisualTree/BoundsTrackerTests.cs

@ -46,11 +46,11 @@ namespace Avalonia.SceneGraph.UnitTests.VisualTree
context.Render(tree);
var track = target.Track(control);
var results = new List<TransformedBounds>();
var results = new List<TransformedBounds?>();
track.Subscribe(results.Add);
Assert.Equal(new Rect(0, 0, 15, 15), results[0].Bounds);
Assert.Equal(Matrix.CreateTranslation(42, 42), results[0].Transform);
Assert.Equal(new Rect(0, 0, 15, 15), results[0].Value.Bounds);
Assert.Equal(Matrix.CreateTranslation(42, 42), results[0].Value.Transform);
}
}
}

Loading…
Cancel
Save