Browse Source

Merge pull request #3119 from AvaloniaUI/fixes/3095-visualnode-wrong-parent

Ensure VisualNode has correct parent.
release/0.9.0-preview2
danwalmsley 7 years ago
committed by Dan Walmsley
parent
commit
ccb8aa0dae
  1. 15
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  2. 10
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  3. 12
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs
  4. 3
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
  5. 8
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs

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

@ -148,6 +148,19 @@ namespace Avalonia.Rendering.SceneGraph
return (VisualNode)node; return (VisualNode)node;
} }
private static object GetOrCreateChildNode(Scene scene, IVisual child, VisualNode parent)
{
var result = (VisualNode)scene.FindNode(child);
if (result != null && result.Parent != parent)
{
Deindex(scene, result);
result = null;
}
return result ?? CreateNode(scene, child, parent);
}
private static void Update(DrawingContext context, Scene scene, VisualNode node, Rect clip, bool forceRecurse) private static void Update(DrawingContext context, Scene scene, VisualNode node, Rect clip, bool forceRecurse)
{ {
var visual = node.Visual; var visual = node.Visual;
@ -231,7 +244,7 @@ namespace Avalonia.Rendering.SceneGraph
{ {
foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)) foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance))
{ {
var childNode = scene.FindNode(child) ?? CreateNode(scene, child, node); var childNode = GetOrCreateChildNode(scene, child, node);
Update(context, scene, (VisualNode)childNode, clip, forceRecurse); Update(context, scene, (VisualNode)childNode, clip, forceRecurse);
} }

10
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@ -119,6 +119,11 @@ namespace Avalonia.Rendering.SceneGraph
throw new ObjectDisposedException("Visual node for {node.Visual}"); throw new ObjectDisposedException("Visual node for {node.Visual}");
} }
if (child.Parent != this)
{
throw new AvaloniaInternalException("VisualNode added to wrong parent.");
}
EnsureChildrenCreated(); EnsureChildrenCreated();
_children.Add(child); _children.Add(child);
} }
@ -155,6 +160,11 @@ namespace Avalonia.Rendering.SceneGraph
throw new ObjectDisposedException("Visual node for {node.Visual}"); throw new ObjectDisposedException("Visual node for {node.Visual}");
} }
if (node.Parent != this)
{
throw new AvaloniaInternalException("VisualNode added to wrong parent.");
}
EnsureChildrenCreated(); EnsureChildrenCreated();
_children[index] = node; _children[index] = node;
} }

12
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs

@ -17,7 +17,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
public void Should_Add_VisualNode() public void Should_Add_VisualNode()
{ {
var parent = new VisualNode(new TestRoot(), null); var parent = new VisualNode(new TestRoot(), null);
var child = new VisualNode(Mock.Of<IVisual>(), null); var child = new VisualNode(Mock.Of<IVisual>(), parent);
var layers = new SceneLayers(parent.Visual); var layers = new SceneLayers(parent.Visual);
var target = new DeferredDrawingContextImpl(null, layers); var target = new DeferredDrawingContextImpl(null, layers);
@ -32,7 +32,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
public void Should_Not_Replace_Identical_VisualNode() public void Should_Not_Replace_Identical_VisualNode()
{ {
var parent = new VisualNode(new TestRoot(), null); var parent = new VisualNode(new TestRoot(), null);
var child = new VisualNode(Mock.Of<IVisual>(), null); var child = new VisualNode(Mock.Of<IVisual>(), parent);
var layers = new SceneLayers(parent.Visual); var layers = new SceneLayers(parent.Visual);
parent.AddChild(child); parent.AddChild(child);
@ -50,8 +50,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
public void Should_Replace_Different_VisualNode() public void Should_Replace_Different_VisualNode()
{ {
var parent = new VisualNode(new TestRoot(), null); var parent = new VisualNode(new TestRoot(), null);
var child1 = new VisualNode(Mock.Of<IVisual>(), null); var child1 = new VisualNode(Mock.Of<IVisual>(), parent);
var child2 = new VisualNode(Mock.Of<IVisual>(), null); var child2 = new VisualNode(Mock.Of<IVisual>(), parent);
var layers = new SceneLayers(parent.Visual); var layers = new SceneLayers(parent.Visual);
parent.AddChild(child1); parent.AddChild(child1);
@ -78,8 +78,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
var layers = new SceneLayers(root); var layers = new SceneLayers(root);
var target = new DeferredDrawingContextImpl(null, layers); var target = new DeferredDrawingContextImpl(null, layers);
var child1 = new VisualNode(Mock.Of<IVisual>(), null) { LayerRoot = root }; var child1 = new VisualNode(Mock.Of<IVisual>(), node) { LayerRoot = root };
var child2 = new VisualNode(Mock.Of<IVisual>(), null) { LayerRoot = root }; var child2 = new VisualNode(Mock.Of<IVisual>(), node) { LayerRoot = root };
target.BeginUpdate(node); target.BeginUpdate(node);
using (target.BeginUpdate(child1)) { } using (target.BeginUpdate(child1)) { }

3
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@ -620,10 +620,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
border1.IsVisible = false; border1.IsVisible = false;
scene = scene.CloneScene(); scene = scene.CloneScene();
var panelNode = (VisualNode)scene.FindNode(panel);
sceneBuilder.Update(scene, decorator); sceneBuilder.Update(scene, decorator);
var panelNode = (VisualNode)scene.FindNode(panel);
Assert.Equal(2, panelNode.Children.Count); Assert.Equal(2, panelNode.Children.Count);
Assert.False(panelNode.Children[0].Disposed); Assert.False(panelNode.Children[0].Disposed);
Assert.False(panelNode.Children[1].Disposed); Assert.False(panelNode.Children[1].Disposed);

8
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs

@ -24,7 +24,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
var node = new VisualNode(Mock.Of<IVisual>(), null); var node = new VisualNode(Mock.Of<IVisual>(), null);
var collection = node.Children; var collection = node.Children;
node.AddChild(Mock.Of<IVisualNode>()); node.AddChild(Mock.Of<IVisualNode>(x => x.Parent == node));
Assert.NotSame(collection, node.Children); Assert.NotSame(collection, node.Children);
} }
@ -106,9 +106,9 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
public void TrimChildren_Should_Work_Correctly() public void TrimChildren_Should_Work_Correctly()
{ {
var parent = new VisualNode(Mock.Of<IVisual>(), null); var parent = new VisualNode(Mock.Of<IVisual>(), null);
var child1 = new VisualNode(Mock.Of<IVisual>(), null); var child1 = new VisualNode(Mock.Of<IVisual>(), parent);
var child2 = new VisualNode(Mock.Of<IVisual>(), null); var child2 = new VisualNode(Mock.Of<IVisual>(), parent);
var child3 = new VisualNode(Mock.Of<IVisual>(), null); var child3 = new VisualNode(Mock.Of<IVisual>(), parent);
parent.AddChild(child1); parent.AddChild(child1);
parent.AddChild(child2); parent.AddChild(child2);

Loading…
Cancel
Save