Browse Source

Render changes to child order/zindex.

Fixes #2714.
pull/2776/head
Steven Kirk 7 years ago
parent
commit
bc5a101faf
  1. 2
      src/Avalonia.Controls/Panel.cs
  2. 44
      src/Avalonia.Styling/StyledElement.cs
  3. 14
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  4. 6
      src/Avalonia.Visuals/Rendering/IRenderer.cs
  5. 3
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  6. 31
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  7. 17
      src/Avalonia.Visuals/Visual.cs
  8. 4
      tests/Avalonia.LeakTests/ControlTests.cs

2
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:

44
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)

14
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
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
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
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
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
tests/Avalonia.LeakTests/ControlTests.cs

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

Loading…
Cancel
Save