diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
index c820a83c2a..dd9a795937 100644
--- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj
+++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
@@ -2,6 +2,7 @@
netstandard1.1
false
+ Avalonia
true
diff --git a/src/Avalonia.Visuals/Media/LinearGradientBrush.cs b/src/Avalonia.Visuals/Media/LinearGradientBrush.cs
index 6c0d500343..344e05e16e 100644
--- a/src/Avalonia.Visuals/Media/LinearGradientBrush.cs
+++ b/src/Avalonia.Visuals/Media/LinearGradientBrush.cs
@@ -1,6 +1,8 @@
// 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.Media
{
///
diff --git a/src/Avalonia.Visuals/Media/RadialGradientBrush.cs b/src/Avalonia.Visuals/Media/RadialGradientBrush.cs
index 9f9c2733ec..003e2e05f9 100644
--- a/src/Avalonia.Visuals/Media/RadialGradientBrush.cs
+++ b/src/Avalonia.Visuals/Media/RadialGradientBrush.cs
@@ -4,8 +4,7 @@
namespace Avalonia.Media
{
///
- /// Paints an area with a radial gradient. A focal point defines the beginning of the gradient,
- /// and a circle defines the end point of the gradient.
+ /// Paints an area with a radial gradient.
///
public sealed class RadialGradientBrush : GradientBrush, IRadialGradientBrush, IMutableBrush
{
@@ -56,6 +55,7 @@ namespace Avalonia.Media
/// Gets or sets the horizontal and vertical radius of the outermost circle of the radial
/// gradient.
///
+ // TODO: This appears to always be relative so should use a RelativeSize struct or something.
public double Radius
{
get { return GetValue(RadiusProperty); }
diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs
index ae5e385169..20772d8f0d 100644
--- a/src/Avalonia.Visuals/Visual.cs
+++ b/src/Avalonia.Visuals/Visual.cs
@@ -10,7 +10,6 @@ using Avalonia.Collections;
using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.Media;
-using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.VisualTree;
@@ -20,10 +19,10 @@ namespace Avalonia
/// Base class for controls that provides rendering and related visual properties.
///
///
- /// The class acts as a node in the Avalonia scene graph and holds
- /// all the information needed for an to render the control.
- /// To traverse the scene graph (aka Visual Tree), use the extension methods defined
- /// in .
+ /// The class represents elements that have a visual on-screen
+ /// representation and stores all the information needed for an
+ /// to render the control. To traverse the visual tree, use the
+ /// extension methods defined in .
///
public class Visual : Animatable, IVisual
{
@@ -88,6 +87,7 @@ namespace Avalonia
AvaloniaProperty.Register(nameof(ZIndex));
private Rect _bounds;
+ private IRenderRoot _visualRoot;
private IVisual _visualParent;
///
@@ -95,7 +95,12 @@ namespace Avalonia
///
static Visual()
{
- AffectsRender(BoundsProperty, IsVisibleProperty, OpacityProperty);
+ AffectsRender(
+ BoundsProperty,
+ ClipProperty,
+ ClipToBoundsProperty,
+ IsVisibleProperty,
+ OpacityProperty);
RenderTransformProperty.Changed.Subscribe(RenderTransformChanged);
}
@@ -122,7 +127,7 @@ namespace Avalonia
public event EventHandler DetachedFromVisualTree;
///
- /// Gets the bounds of the scene graph node relative to its parent.
+ /// Gets the bounds of the control relative to its parent.
///
public Rect Bounds
{
@@ -131,7 +136,7 @@ namespace Avalonia
}
///
- /// Gets a value indicating whether the scene graph node should be clipped to its bounds.
+ /// Gets a value indicating whether the control should be clipped to its bounds.
///
public bool ClipToBounds
{
@@ -149,7 +154,7 @@ namespace Avalonia
}
///
- /// Gets a value indicating whether this scene graph node and all its parents are visible.
+ /// Gets a value indicating whether this control and all its parents are visible.
///
public bool IsEffectivelyVisible
{
@@ -157,7 +162,7 @@ namespace Avalonia
}
///
- /// Gets a value indicating whether this scene graph node is visible.
+ /// Gets a value indicating whether this control is visible.
///
public bool IsVisible
{
@@ -166,7 +171,7 @@ namespace Avalonia
}
///
- /// Gets the opacity of the scene graph node.
+ /// Gets the opacity of the control.
///
public double Opacity
{
@@ -174,9 +179,8 @@ namespace Avalonia
set { SetValue(OpacityProperty, value); }
}
-
///
- /// Gets the opacity mask of the scene graph node.
+ /// Gets the opacity mask of the control.
///
public IBrush OpacityMask
{
@@ -185,7 +189,7 @@ namespace Avalonia
}
///
- /// Gets the render transform of the scene graph node.
+ /// Gets the render transform of the control.
///
public Transform RenderTransform
{
@@ -194,7 +198,7 @@ namespace Avalonia
}
///
- /// Gets the transform origin of the scene graph node.
+ /// Gets the transform origin of the control.
///
public RelativePoint RenderTransformOrigin
{
@@ -203,7 +207,7 @@ namespace Avalonia
}
///
- /// Gets the Z index of the node.
+ /// Gets the Z index of the control.
///
///
/// Controls with a higher will appear in front of controls with
@@ -217,7 +221,7 @@ namespace Avalonia
}
///
- /// Gets the control's visual children.
+ /// Gets the control's child visuals.
///
protected IAvaloniaList VisualChildren
{
@@ -228,24 +232,20 @@ namespace Avalonia
///
/// Gets the root of the visual tree, if the control is attached to a visual tree.
///
- protected IRenderRoot VisualRoot
- {
- get;
- private set;
- }
+ protected IRenderRoot VisualRoot => _visualRoot ?? (this as IRenderRoot);
///
- /// Gets a value indicating whether this scene graph node is attached to a visual root.
+ /// Gets a value indicating whether this control is attached to a visual root.
///
bool IVisual.IsAttachedToVisualTree => VisualRoot != null;
///
- /// Gets the scene graph node's child nodes.
+ /// Gets the control's child controls.
///
IAvaloniaReadOnlyList IVisual.VisualChildren => VisualChildren;
///
- /// Gets the scene graph node's parent node.
+ /// Gets the control's parent visual.
///
IVisual IVisual.VisualParent => _visualParent;
@@ -321,7 +321,7 @@ namespace Avalonia
{
Logger.Verbose(LogArea.Visual, this, "Attached to visual tree");
- VisualRoot = e.Root;
+ _visualRoot = e.Root;
if (RenderTransform != null)
{
@@ -329,6 +329,7 @@ namespace Avalonia
}
OnAttachedToVisualTree(e);
+ InvalidateVisual();
if (VisualChildren != null)
{
@@ -348,7 +349,7 @@ namespace Avalonia
{
Logger.Verbose(LogArea.Visual, this, "Detached from visual tree");
- VisualRoot = null;
+ _visualRoot = null;
if (RenderTransform != null)
{
@@ -356,6 +357,7 @@ namespace Avalonia
}
OnDetachedFromVisualTree(e);
+ e.Root?.Renderer?.AddDirty(this);
if (VisualChildren != null)
{
@@ -492,11 +494,11 @@ namespace Avalonia
{
return;
}
-
+
var old = _visualParent;
_visualParent = value;
- if (VisualRoot != null)
+ if (_visualRoot != null)
{
var e = new VisualTreeAttachmentEventArgs(old, VisualRoot);
OnDetachedFromVisualTreeCore(e);
diff --git a/src/Avalonia.Visuals/VisualTree/IVisual.cs b/src/Avalonia.Visuals/VisualTree/IVisual.cs
index b9b37b1a20..2047996c3e 100644
--- a/src/Avalonia.Visuals/VisualTree/IVisual.cs
+++ b/src/Avalonia.Visuals/VisualTree/IVisual.cs
@@ -9,13 +9,13 @@ using Avalonia.Rendering;
namespace Avalonia.VisualTree
{
///
- /// Represents a node in the visual scene graph.
+ /// Represents control that has a visual on-screen representation.
///
///
/// The interface defines the interface required for a renderer to
- /// render a scene graph. You should not usually need to reference this interface unless
+ /// render a control. You should not usually need to reference this interface unless
/// you are writing a renderer; instead use the extension methods defined in
- /// to traverse the scene graph. This interface is
+ /// to traverse the visual tree. This interface is
/// implemented by . It should not be necessary to implement it
/// anywhere else.
///
@@ -32,12 +32,12 @@ namespace Avalonia.VisualTree
event EventHandler DetachedFromVisualTree;
///
- /// Gets the bounds of the scene graph node relative to its parent.
+ /// Gets the bounds of the control relative to its parent.
///
Rect Bounds { get; }
///
- /// Gets a value indicating whether the scene graph node should be clipped to its bounds.
+ /// Gets a value indicating whether the control should be clipped to its bounds.
///
bool ClipToBounds { get; set; }
@@ -47,47 +47,47 @@ namespace Avalonia.VisualTree
Geometry Clip { get; set; }
///
- /// Gets a value indicating whether this scene graph node is attached to a visual root.
+ /// Gets a value indicating whether this control is attached to a visual root.
///
bool IsAttachedToVisualTree { get; }
///
- /// Gets a value indicating whether this scene graph node and all its parents are visible.
+ /// Gets a value indicating whether this control and all its parents are visible.
///
bool IsEffectivelyVisible { get; }
///
- /// Gets or sets a value indicating whether this scene graph node is visible.
+ /// Gets or sets a value indicating whether this control is visible.
///
bool IsVisible { get; set; }
///
- /// Gets or sets the opacity of the scene graph node.
+ /// Gets or sets the opacity of the control.
///
double Opacity { get; set; }
///
- /// Gets or sets the opacity mask of the scene graph node.
+ /// Gets or sets the opacity mask for the control.
///
IBrush OpacityMask { get; set; }
///
- /// Gets or sets the render transform of the scene graph node.
+ /// Gets or sets the render transform of the control.
///
Transform RenderTransform { get; set; }
///
- /// Gets or sets the render transform origin of the scene graph node.
+ /// Gets or sets the render transform origin of the control.
///
RelativePoint RenderTransformOrigin { get; set; }
///
- /// Gets the scene graph node's child nodes.
+ /// Gets the control's child visuals.
///
IAvaloniaReadOnlyList VisualChildren { get; }
///
- /// Gets the scene graph node's parent node.
+ /// Gets the control's parent visual.
///
IVisual VisualParent { get; }
@@ -107,7 +107,7 @@ namespace Avalonia.VisualTree
void InvalidateVisual();
///
- /// Renders the scene graph node to a .
+ /// Renders the control to a .
///
/// The context.
void Render(DrawingContext context);
diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
index e7876b762f..2af267aa97 100644
--- a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
+++ b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Avalonia.Rendering;
namespace Avalonia.VisualTree
{
@@ -20,6 +21,8 @@ namespace Avalonia.VisualTree
/// The common ancestor, or null if not found.
public static IVisual FindCommonVisualAncestor(this IVisual visual, IVisual target)
{
+ Contract.Requires(visual != null);
+
return visual.GetSelfAndVisualAncestors().Intersect(target.GetSelfAndVisualAncestors())
.FirstOrDefault();
}
@@ -49,6 +52,8 @@ namespace Avalonia.VisualTree
/// The visual and its ancestors.
public static IEnumerable GetSelfAndVisualAncestors(this IVisual visual)
{
+ Contract.Requires(visual != null);
+
yield return visual;
foreach (var ancestor in visual.GetVisualAncestors())
@@ -102,26 +107,9 @@ namespace Avalonia.VisualTree
{
Contract.Requires(visual != null);
- if (filter?.Invoke(visual) != false)
- {
- bool containsPoint = BoundsTracker.GetTransformedBounds((Visual)visual)?.Contains(p) == true;
-
- if ((containsPoint || !visual.ClipToBounds) && visual.VisualChildren.Any())
- {
- foreach (var child in visual.VisualChildren.SortByZIndex())
- {
- foreach (var result in child.GetVisualsAt(p, filter))
- {
- yield return result;
- }
- }
- }
-
- if (containsPoint)
- {
- yield return visual;
- }
- }
+ var root = visual.GetVisualRoot();
+ p = visual.TranslatePoint(p, root);
+ return root.Renderer.HitTest(p, filter);
}
///
@@ -197,11 +185,11 @@ namespace Avalonia.VisualTree
///
/// The root visual or null if the visual is not rooted.
///
- public static IVisual GetVisualRoot(this IVisual visual)
+ public static IRenderRoot GetVisualRoot(this IVisual visual)
{
Contract.Requires(visual != null);
- return visual.VisualRoot as IVisual;
+ return visual.VisualRoot as IRenderRoot ?? visual.VisualRoot;
}
///
diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
index d1f8d5b912..d6ffb32d1c 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
@@ -198,7 +198,7 @@ namespace Avalonia.Controls.UnitTests
public bool IsClosed { get; private set; }
public TestWindowBase()
- : base(Mock.Of())
+ : base(Mock.Of(x => x.Scaling == 1))
{
}
diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs
index 1aaba9cf6b..ecd27b63bd 100644
--- a/tests/Avalonia.LeakTests/ControlTests.cs
+++ b/tests/Avalonia.LeakTests/ControlTests.cs
@@ -4,18 +4,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using JetBrains.dotMemoryUnit;
-using Avalonia.Collections;
using Avalonia.Controls;
-using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Diagnostics;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Rendering;
-using Avalonia.Styling;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
+using JetBrains.dotMemoryUnit;
using Moq;
using Xunit;
using Xunit.Abstractions;
@@ -57,7 +54,6 @@ namespace Avalonia.LeakTests
};
var result = run();
- PurgeMoqReferences();
dotMemory.Check(memory =>
Assert.Equal(0, memory.GetObjects(where => where.Type.Is