瀏覽代碼

Render changes to child order/zindex.

Fixes #2714.
Steven Kirk 6 年之前
父節點
當前提交
bc5a101faf

+ 1 - 1
src/Avalonia.Controls/Panel.cs

@@ -112,7 +112,7 @@ namespace Avalonia.Controls
                 case NotifyCollectionChangedAction.Add:
                     controls = e.NewItems.OfType<Control>().ToList();
                     LogicalChildren.InsertRange(e.NewStartingIndex, controls);
-                    VisualChildren.AddRange(e.NewItems.OfType<Visual>());
+                    VisualChildren.InsertRange(e.NewStartingIndex, e.NewItems.OfType<Visual>());
                     break;
 
                 case NotifyCollectionChangedAction.Move:

+ 22 - 22
src/Avalonia.Styling/StyledElement.cs

@@ -568,6 +568,28 @@ namespace Avalonia
                 });
         }
 
+        protected virtual void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+        {
+            switch (e.Action)
+            {
+                case NotifyCollectionChangedAction.Add:
+                    SetLogicalParent(e.NewItems.Cast<ILogical>());
+                    break;
+
+                case NotifyCollectionChangedAction.Remove:
+                    ClearLogicalParent(e.OldItems.Cast<ILogical>());
+                    break;
+
+                case NotifyCollectionChangedAction.Replace:
+                    ClearLogicalParent(e.OldItems.Cast<ILogical>());
+                    SetLogicalParent(e.NewItems.Cast<ILogical>());
+                    break;
+
+                case NotifyCollectionChangedAction.Reset:
+                    throw new NotSupportedException("Reset should not be signaled on LogicalChildren collection");
+            }
+        }
+
         /// <summary>
         /// Called when the styled element is added to a rooted logical tree.
         /// </summary>
@@ -736,28 +758,6 @@ namespace Avalonia
             OnDataContextChanged(EventArgs.Empty);
         }
 
-        private void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            switch (e.Action)
-            {
-                case NotifyCollectionChangedAction.Add:
-                    SetLogicalParent(e.NewItems.Cast<ILogical>());
-                    break;
-
-                case NotifyCollectionChangedAction.Remove:
-                    ClearLogicalParent(e.OldItems.Cast<ILogical>());
-                    break;
-
-                case NotifyCollectionChangedAction.Replace:
-                    ClearLogicalParent(e.OldItems.Cast<ILogical>());
-                    SetLogicalParent(e.NewItems.Cast<ILogical>());
-                    break;
-
-                case NotifyCollectionChangedAction.Reset:
-                    throw new NotSupportedException("Reset should not be signaled on LogicalChildren collection");
-            }
-        }
-
         private void SetLogicalParent(IEnumerable<ILogical> children)
         {
             foreach (var i in children)

+ 13 - 1
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@@ -30,6 +30,7 @@ namespace Avalonia.Rendering
         private bool _disposed;
         private volatile IRef<Scene> _scene;
         private DirtyVisuals _dirty;
+        private HashSet<IVisual> _recalculateChildren;
         private IRef<IRenderTargetBitmapImpl> _overlay;
         private int _lastSceneId = -1;
         private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
@@ -135,6 +136,8 @@ namespace Avalonia.Rendering
             DisposeRenderTarget();
         }
 
+        public void RecalculateChildren(IVisual visual) => _recalculateChildren?.Add(visual);
+
         void DisposeRenderTarget()
         {
             using (var l = _lock.TryLock())
@@ -518,10 +521,19 @@ namespace Avalonia.Rendering
                 if (_dirty == null)
                 {
                     _dirty = new DirtyVisuals();
+                    _recalculateChildren = new HashSet<IVisual>();
                     _sceneBuilder.UpdateAll(scene);
                 }
-                else if (_dirty.Count > 0)
+                else
                 {
+                    foreach (var visual in _recalculateChildren)
+                    {
+                        var node = scene.FindNode(visual);
+                        ((VisualNode)node)?.SortChildren(scene);
+                    }
+
+                    _recalculateChildren.Clear();
+
                     foreach (var visual in _dirty)
                     {
                         _sceneBuilder.Update(scene, visual);

+ 6 - 0
src/Avalonia.Visuals/Rendering/IRenderer.cs

@@ -50,6 +50,12 @@ namespace Avalonia.Rendering
         /// <returns>The visuals at the specified point, topmost first.</returns>
         IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter);
 
+        /// <summary>
+        /// Informs the renderer that the z-ordering of a visual's children has changed.
+        /// </summary>
+        /// <param name="visual">The visual.</param>
+        void RecalculateChildren(IVisual visual);
+
         /// <summary>
         /// Called when a resize notification is received by the control being rendered.
         /// </summary>

+ 3 - 0
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@@ -163,6 +163,9 @@ namespace Avalonia.Rendering
             return HitTest(root, p, filter);
         }
 
+        /// <inheritdoc/>
+        public void RecalculateChildren(IVisual visual) => AddDirty(visual);
+
         /// <inheritdoc/>
         public void Start()
         {

+ 31 - 0
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@@ -172,6 +172,37 @@ namespace Avalonia.Rendering.SceneGraph
             old.Dispose();
         }
 
+        /// <summary>
+        /// Sorts the <see cref="Children"/> collection according to the order of the visual's
+        /// children and their z-index.
+        /// </summary>
+        /// <param name="scene">The scene that the node is a part of.</param>
+        public void SortChildren(Scene scene)
+        {
+            var keys = new List<long>();
+
+            for (var i = 0; i < Visual.VisualChildren.Count; ++i)
+            {
+                var child = Visual.VisualChildren[i];
+                var zIndex = child.ZIndex;
+                keys.Add(((long)zIndex << 32) + i);
+            }
+
+            keys.Sort();
+            _children.Clear();
+
+            foreach (var i in keys)
+            {
+                var child = Visual.VisualChildren[(int)(i & 0xffffffff)];
+                var node = scene.FindNode(child);
+
+                if (node != null)
+                {
+                    _children.Add(node);
+                }
+            }
+        }
+
         /// <summary>
         /// Removes items in the <see cref="Children"/> collection from the specified index
         /// to the end.

+ 17 - 0
src/Avalonia.Visuals/Visual.cs

@@ -111,6 +111,7 @@ namespace Avalonia
                 IsVisibleProperty,
                 OpacityProperty);
             RenderTransformProperty.Changed.Subscribe(RenderTransformChanged);
+            ZIndexProperty.Changed.Subscribe(ZIndexChanged);
         }
 
         /// <summary>
@@ -345,6 +346,12 @@ namespace Avalonia
             }
         }
 
+        protected override void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+        {
+            base.LogicalChildrenCollectionChanged(sender, e);
+            VisualRoot?.Renderer?.RecalculateChildren(this);
+        }
+
         /// <summary>
         /// Calls the <see cref="OnAttachedToVisualTree(VisualTreeAttachmentEventArgs)"/> method 
         /// for this control and all of its visual descendants.
@@ -501,6 +508,16 @@ namespace Avalonia
             }
         }
 
+        /// <summary>
+        /// Called when the <see cref="ZIndex"/> property changes on any control.
+        /// </summary>
+        /// <param name="e">The event args.</param>
+        private static void ZIndexChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            var parent = (e.Sender as Visual)?._visualParent;
+            parent?.VisualRoot?.Renderer?.RecalculateChildren(parent);
+        }
+
         /// <summary>
         /// Called when the <see cref="RenderTransform"/>'s <see cref="Transform.Changed"/> event
         /// is fired.

+ 4 - 0
tests/Avalonia.LeakTests/ControlTests.cs

@@ -401,6 +401,10 @@ namespace Avalonia.LeakTests
             {
             }
 
+            public void RecalculateChildren(IVisual visual)
+            {
+            }
+
             public void Resized(Size size)
             {
             }