3 changed files with 170 additions and 145 deletions
@ -0,0 +1,165 @@ |
|||
using Avalonia.Collections; |
|||
using Avalonia.Collections.Pooled; |
|||
using Avalonia.Media; |
|||
using Avalonia.Rendering.Composition; |
|||
using Avalonia.Rendering.Composition.Server; |
|||
using Avalonia.VisualTree; |
|||
|
|||
namespace Avalonia; |
|||
|
|||
public partial class Visual |
|||
{ |
|||
internal CompositionDrawListVisual? CompositionVisual { get; private set; } |
|||
internal CompositionVisual? ChildCompositionVisual { get; set; } |
|||
|
|||
|
|||
private protected virtual CompositionDrawListVisual CreateCompositionVisual(Compositor compositor) |
|||
=> new CompositionDrawListVisual(compositor, |
|||
new ServerCompositionDrawListVisual(compositor.Server, this), this); |
|||
|
|||
internal CompositionVisual AttachToCompositor(Compositor compositor) |
|||
{ |
|||
if (CompositionVisual == null || CompositionVisual.Compositor != compositor) |
|||
{ |
|||
CompositionVisual = CreateCompositionVisual(compositor); |
|||
} |
|||
|
|||
return CompositionVisual; |
|||
} |
|||
|
|||
internal virtual void DetachFromCompositor() |
|||
{ |
|||
if (CompositionVisual != null) |
|||
{ |
|||
if (ChildCompositionVisual != null) |
|||
CompositionVisual.Children.Remove(ChildCompositionVisual); |
|||
|
|||
CompositionVisual.DrawList = null; |
|||
CompositionVisual = null; |
|||
} |
|||
} |
|||
|
|||
internal virtual void SynchronizeCompositionChildVisuals() |
|||
{ |
|||
if(CompositionVisual == null) |
|||
return; |
|||
var compositionChildren = CompositionVisual.Children; |
|||
var visualChildren = (AvaloniaList<Visual>)VisualChildren; |
|||
|
|||
PooledList<(Visual visual, int index)>? sortedChildren = null; |
|||
if (HasNonUniformZIndexChildren && visualChildren.Count > 1) |
|||
{ |
|||
sortedChildren = new (visualChildren.Count); |
|||
for (var c = 0; c < visualChildren.Count; c++) |
|||
sortedChildren.Add((visualChildren[c], c)); |
|||
|
|||
// Regular Array.Sort is unstable, we need to provide indices as well to avoid reshuffling elements.
|
|||
sortedChildren.Sort(static (lhs, rhs) => |
|||
{ |
|||
var result = lhs.visual.ZIndex.CompareTo(rhs.visual.ZIndex); |
|||
return result == 0 ? lhs.index.CompareTo(rhs.index) : result; |
|||
}); |
|||
} |
|||
|
|||
var childVisual = ChildCompositionVisual; |
|||
|
|||
// Check if the current visual somehow got migrated to another compositor
|
|||
if (childVisual != null && childVisual.Compositor != CompositionVisual.Compositor) |
|||
childVisual = null; |
|||
|
|||
var expectedCount = visualChildren.Count; |
|||
if (childVisual != null) |
|||
expectedCount++; |
|||
|
|||
if (compositionChildren.Count == expectedCount) |
|||
{ |
|||
bool mismatch = false; |
|||
if (sortedChildren != null) |
|||
for (var c = 0; c < visualChildren.Count; c++) |
|||
{ |
|||
if (!ReferenceEquals(compositionChildren[c], sortedChildren[c].visual.CompositionVisual)) |
|||
{ |
|||
mismatch = true; |
|||
break; |
|||
} |
|||
} |
|||
else |
|||
for (var c = 0; c < visualChildren.Count; c++) |
|||
if (!ReferenceEquals(compositionChildren[c], visualChildren[c].CompositionVisual)) |
|||
{ |
|||
mismatch = true; |
|||
break; |
|||
} |
|||
|
|||
if (childVisual != null && |
|||
!ReferenceEquals(compositionChildren[compositionChildren.Count - 1], childVisual)) |
|||
mismatch = true; |
|||
|
|||
if (!mismatch) |
|||
{ |
|||
sortedChildren?.Dispose(); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
compositionChildren.Clear(); |
|||
if (sortedChildren != null) |
|||
{ |
|||
foreach (var ch in sortedChildren) |
|||
{ |
|||
var compositionChild = ch.visual.CompositionVisual; |
|||
if (compositionChild != null) |
|||
compositionChildren.Add(compositionChild); |
|||
} |
|||
sortedChildren.Dispose(); |
|||
} |
|||
else |
|||
foreach (var ch in visualChildren) |
|||
{ |
|||
var compositionChild = ch.CompositionVisual; |
|||
if (compositionChild != null) |
|||
compositionChildren.Add(compositionChild); |
|||
} |
|||
|
|||
if (childVisual != null) |
|||
compositionChildren.Add(childVisual); |
|||
} |
|||
|
|||
internal virtual void SynchronizeCompositionProperties() |
|||
{ |
|||
if(CompositionVisual == null) |
|||
return; |
|||
var comp = CompositionVisual; |
|||
|
|||
// TODO: Introduce a dirty mask like WPF has, so we don't overwrite properties every time
|
|||
|
|||
comp.Offset = new (Bounds.Left, Bounds.Top, 0); |
|||
comp.Size = new (Bounds.Width, Bounds.Height); |
|||
comp.Visible = IsVisible; |
|||
comp.Opacity = (float)Opacity; |
|||
comp.ClipToBounds = ClipToBounds; |
|||
comp.Clip = Clip?.PlatformImpl; |
|||
|
|||
if (!Equals(comp.OpacityMask, OpacityMask)) |
|||
comp.OpacityMask = OpacityMask?.ToImmutable(); |
|||
|
|||
if (!comp.Effect.EffectEquals(Effect)) |
|||
comp.Effect = Effect?.ToImmutable(); |
|||
|
|||
comp.RenderOptions = RenderOptions; |
|||
|
|||
var renderTransform = Matrix.Identity; |
|||
|
|||
if (HasMirrorTransform) |
|||
renderTransform = new Matrix(-1.0, 0.0, 0.0, 1.0, Bounds.Width, 0); |
|||
|
|||
if (RenderTransform != null) |
|||
{ |
|||
var origin = RenderTransformOrigin.ToPixels(new Size(Bounds.Width, Bounds.Height)); |
|||
var offset = Matrix.CreateTranslation(origin); |
|||
renderTransform *= (-offset) * RenderTransform.Value * (offset); |
|||
} |
|||
|
|||
comp.TransformMatrix = renderTransform; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue