diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index 0ff0285a04..efcc555159 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -540,7 +540,7 @@ namespace Avalonia.Rendering foreach (var visual in _recalculateChildren) { var node = scene.FindNode(visual); - ((VisualNode)node)?.UpdateChildren(scene); + ((VisualNode)node)?.SortChildren(scene); } _recalculateChildren.Clear(); diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 2fcea8c205..161cbc099e 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -53,6 +53,16 @@ namespace Avalonia.Rendering.SceneGraph if (visual.VisualRoot != null) { + if (node?.Parent != null && + visual.VisualParent != null && + node.Parent.Visual != visual.VisualParent) + { + // The control has changed parents. Remove the node and recurse into the new parent node. + ((VisualNode)node.Parent).RemoveChild(node); + Deindex(scene, node); + node = (VisualNode)scene.FindNode(visual.VisualParent); + } + if (visual.IsVisible) { // If the node isn't yet part of the scene, find the nearest ancestor that is. diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index f079023c6d..f579bf0a62 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -174,12 +174,12 @@ namespace Avalonia.Rendering.SceneGraph /// /// Sorts the collection according to the order of the visual's - /// children and their z-index and removes controls that are no longer children. + /// children and their z-index. /// /// The scene that the node is a part of. - public void UpdateChildren(Scene scene) + public void SortChildren(Scene scene) { - if (_children == null || _children.Count == 0) + if (_children == null || _children.Count <= 1) { return; } @@ -193,12 +193,9 @@ namespace Avalonia.Rendering.SceneGraph keys.Add(((long)zIndex << 32) + i); } - var toRemove = _children.ToList(); - keys.Sort(); _children.Clear(); - foreach (var i in keys) { var child = Visual.VisualChildren[(int)(i & 0xffffffff)]; @@ -207,14 +204,8 @@ namespace Avalonia.Rendering.SceneGraph if (node != null) { _children.Add(node); - toRemove.Remove(node); } } - - foreach (var node in toRemove) - { - scene.Remove(node); - } } /// diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs index 568ccb81d8..b4743e900d 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs @@ -96,6 +96,55 @@ namespace Avalonia.Visuals.UnitTests.Rendering Assert.Equal(new List { root, decorator, border, canvas }, result); } + [Fact] + public void Should_Add_Dirty_Rect_On_Child_Remove() + { + var dispatcher = new ImmediateDispatcher(); + var loop = new Mock(); + + Decorator decorator; + Border border; + var root = new TestRoot + { + Width = 100, + Height= 100, + Child = decorator = new Decorator + { + Child = border = new Border + { + Width = 50, + Height = 50, + Background = Brushes.Red, + }, + } + }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); + + var sceneBuilder = new SceneBuilder(); + var target = new DeferredRenderer( + root, + loop.Object, + sceneBuilder: sceneBuilder, + dispatcher: dispatcher); + + root.Renderer = target; + target.Start(); + RunFrame(target); + + decorator.Child = null; + + RunFrame(target); + + var scene = target.UnitTestScene(); + var stackNode = scene.FindNode(decorator); + var dirty = scene.Layers[0].Dirty.ToList(); + + Assert.Equal(1, dirty.Count); + Assert.Equal(new Rect(25, 25, 50, 50), dirty[0]); + } + [Fact] public void Should_Update_VisualNode_Order_On_Child_Remove_Insert() { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index dcf23e94e2..13bcd27240 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -521,8 +521,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph moveFromNode = (VisualNode)scene.FindNode(moveFrom); moveToNode = (VisualNode)scene.FindNode(moveTo); - moveFromNode.UpdateChildren(scene); - moveToNode.UpdateChildren(scene); + moveFromNode.SortChildren(scene); + moveToNode.SortChildren(scene); sceneBuilder.Update(scene, moveFrom); sceneBuilder.Update(scene, moveTo); sceneBuilder.Update(scene, moveMe); diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs index d4f7a6a142..24ba2d1c48 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs @@ -99,7 +99,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph var node = new VisualNode(Mock.Of(), null); var scene = new Scene(Mock.Of()); - node.UpdateChildren(scene); + node.SortChildren(scene); } } }