Browse Source

Change TransformedBounds to a direct property on Visual (exposed on IVisual) instead of using an attached property in BoundsTracker.

pull/1312/head
Jeremy Koritzinsky 9 years ago
parent
commit
f65ae1918f
  1. 4
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  2. 9
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  3. 12
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  4. 17
      src/Avalonia.Visuals/Visual.cs
  5. 51
      src/Avalonia.Visuals/VisualTree/BoundsTracker.cs
  6. 5
      src/Avalonia.Visuals/VisualTree/IVisual.cs
  7. 4
      tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs

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

@ -18,8 +18,6 @@ namespace Avalonia.Controls.Primitives
private static readonly AttachedProperty<AdornedElementInfo> s_adornedElementInfoProperty = private static readonly AttachedProperty<AdornedElementInfo> s_adornedElementInfoProperty =
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, AdornedElementInfo>("AdornedElementInfo"); AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, AdornedElementInfo>("AdornedElementInfo");
private readonly BoundsTracker _tracker = new BoundsTracker();
static AdornerLayer() static AdornerLayer()
{ {
AdornedElementProperty.Changed.Subscribe(AdornedElementChanged); AdornedElementProperty.Changed.Subscribe(AdornedElementChanged);
@ -118,7 +116,7 @@ namespace Avalonia.Controls.Primitives
adorner.SetValue(s_adornedElementInfoProperty, info); adorner.SetValue(s_adornedElementInfoProperty, info);
} }
info.Subscription = _tracker.Track(adorned).Subscribe(x => info.Subscription = adorned.GetObservable(TransformedBoundsProperty).Subscribe(x =>
{ {
info.Bounds = x; info.Bounds = x;
InvalidateArrange(); InvalidateArrange();

9
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@ -169,7 +169,7 @@ namespace Avalonia.Rendering
{ {
foreach (var e in visual.GetSelfAndVisualDescendants()) foreach (var e in visual.GetSelfAndVisualDescendants())
{ {
BoundsTracker.SetTransformedBounds((Visual)visual, null); visual.TransformedBounds = null;
} }
} }
@ -197,7 +197,7 @@ namespace Avalonia.Rendering
if (filter?.Invoke(visual) != false) if (filter?.Invoke(visual) != false)
{ {
bool containsPoint = BoundsTracker.GetTransformedBounds((Visual)visual)?.Contains(p) == true; bool containsPoint = visual.TransformedBounds?.Contains(p) == true;
if ((containsPoint || !visual.ClipToBounds) && visual.VisualChildren.Count > 0) if ((containsPoint || !visual.ClipToBounds) && visual.VisualChildren.Count > 0)
{ {
@ -257,10 +257,7 @@ namespace Avalonia.Rendering
new TransformedBounds(bounds, new Rect(), context.CurrentContainerTransform); new TransformedBounds(bounds, new Rect(), context.CurrentContainerTransform);
#pragma warning restore 0618 #pragma warning restore 0618
if (visual is Visual) visual.TransformedBounds = transformed;
{
BoundsTracker.SetTransformedBounds((Visual)visual, transformed);
}
foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)) foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance))
{ {

12
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@ -209,11 +209,8 @@ namespace Avalonia.Rendering.SceneGraph
} }
catch { } catch { }
if (visual is Visual) var transformed = new TransformedBounds(new Rect(visual.Bounds.Size), clip, node.Transform);
{ visual.TransformedBounds = transformed;
var transformed = new TransformedBounds(new Rect(visual.Bounds.Size), clip, node.Transform);
BoundsTracker.SetTransformedBounds((Visual)visual, transformed);
}
if (forceRecurse) if (forceRecurse)
{ {
@ -279,10 +276,7 @@ namespace Avalonia.Rendering.SceneGraph
scene.Layers[node.LayerRoot].Dirty.Add(node.Bounds); scene.Layers[node.LayerRoot].Dirty.Add(node.Bounds);
if (node.Visual is Visual v) node.Visual.TransformedBounds = null;
{
BoundsTracker.SetTransformedBounds(v, null);
}
foreach (VisualNode child in node.Children) foreach (VisualNode child in node.Children)
{ {

17
src/Avalonia.Visuals/Visual.cs

@ -32,6 +32,11 @@ namespace Avalonia
public static readonly DirectProperty<Visual, Rect> BoundsProperty = public static readonly DirectProperty<Visual, Rect> BoundsProperty =
AvaloniaProperty.RegisterDirect<Visual, Rect>(nameof(Bounds), o => o.Bounds); AvaloniaProperty.RegisterDirect<Visual, Rect>(nameof(Bounds), o => o.Bounds);
public static readonly DirectProperty<Visual, TransformedBounds?> TransformedBoundsProperty =
AvaloniaProperty.RegisterDirect<Visual, TransformedBounds?>(
nameof(TransformedBounds),
o => o.TransformedBounds);
/// <summary> /// <summary>
/// Defines the <see cref="ClipToBounds"/> property. /// Defines the <see cref="ClipToBounds"/> property.
/// </summary> /// </summary>
@ -87,6 +92,7 @@ namespace Avalonia
AvaloniaProperty.Register<Visual, int>(nameof(ZIndex)); AvaloniaProperty.Register<Visual, int>(nameof(ZIndex));
private Rect _bounds; private Rect _bounds;
private TransformedBounds? _transformedBounds;
private IRenderRoot _visualRoot; private IRenderRoot _visualRoot;
private IVisual _visualParent; private IVisual _visualParent;
@ -135,6 +141,11 @@ namespace Avalonia
protected set { SetAndRaise(BoundsProperty, ref _bounds, value); } protected set { SetAndRaise(BoundsProperty, ref _bounds, value); }
} }
/// <summary>
/// Gets the bounds of the control relative to the window, accounting for rendering transforms.
/// </summary>
public TransformedBounds? TransformedBounds => _transformedBounds;
/// <summary> /// <summary>
/// Gets a value indicating whether the control should be clipped to its bounds. /// Gets a value indicating whether the control should be clipped to its bounds.
/// </summary> /// </summary>
@ -253,6 +264,12 @@ namespace Avalonia
/// Gets the root of the visual tree, if the control is attached to a visual tree. /// Gets the root of the visual tree, if the control is attached to a visual tree.
/// </summary> /// </summary>
IRenderRoot IVisual.VisualRoot => VisualRoot; IRenderRoot IVisual.VisualRoot => VisualRoot;
TransformedBounds? IVisual.TransformedBounds
{
get { return _transformedBounds; }
set { SetAndRaise(TransformedBoundsProperty, ref _transformedBounds, value); }
}
/// <summary> /// <summary>
/// Invalidates the visual and queues a repaint. /// Invalidates the visual and queues a repaint.

51
src/Avalonia.Visuals/VisualTree/BoundsTracker.cs

@ -1,51 +0,0 @@
// 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;
namespace Avalonia.VisualTree
{
/// <summary>
/// Tracks the bounds of a control.
/// </summary>
/// <remarks>
/// 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
{
/// <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)
{
return visual.GetObservable(TransformedBoundsProperty);
}
/// <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, value);
}
/// <summary>
/// Gets the transformed bounds of the visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>The transformed bounds or null if the visual is not visible.</returns>
public static TransformedBounds? GetTransformedBounds(Visual visual) => visual.GetValue(TransformedBoundsProperty);
}
}

5
src/Avalonia.Visuals/VisualTree/IVisual.cs

@ -36,6 +36,11 @@ namespace Avalonia.VisualTree
/// </summary> /// </summary>
Rect Bounds { get; } Rect Bounds { get; }
/// <summary>
/// Gets the bounds of the control relative to the window, accounting for rendering transforms.
/// </summary>
TransformedBounds? TransformedBounds { get; set; }
/// <summary> /// <summary>
/// Gets a value indicating whether the control should be clipped to its bounds. /// Gets a value indicating whether the control should be clipped to its bounds.
/// </summary> /// </summary>

4
tests/Avalonia.Visuals.UnitTests/VisualTree/BoundsTrackerTests.cs → tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs

@ -17,7 +17,7 @@ using Avalonia.Platform;
namespace Avalonia.Visuals.UnitTests.VisualTree namespace Avalonia.Visuals.UnitTests.VisualTree
{ {
public class BoundsTrackerTests public class TransformedBoundsTests
{ {
[Fact] [Fact]
public void Should_Track_Bounds() public void Should_Track_Bounds()
@ -46,7 +46,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
tree.Arrange(new Rect(0, 0, 100, 100)); tree.Arrange(new Rect(0, 0, 100, 100));
ImmediateRenderer.Render(tree, context); ImmediateRenderer.Render(tree, context);
var track = target.Track(control); var track = control.GetObservable(Visual.TransformedBoundsProperty);
var results = new List<TransformedBounds?>(); var results = new List<TransformedBounds?>();
track.Subscribe(results.Add); track.Subscribe(results.Add);
Loading…
Cancel
Save