csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
6.1 KiB
173 lines
6.1 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using Avalonia.Platform;
|
|
|
|
namespace Avalonia.Rendering.Composition.Server;
|
|
|
|
partial class ServerCompositionVisual
|
|
{
|
|
// Support for adorners is a rather cancerou^W invasive thing, so we isolate all related code in this file
|
|
// and prefix it with AdornerHelper_.
|
|
|
|
private void AttHelper_OnAdornedVisualWorldTransformChanged() => AdornerHelper_EnqueueForAdornerUpdate();
|
|
|
|
private void AdornerHelper_AttachedToRoot()
|
|
{
|
|
if(AdornedVisual != null)
|
|
AdornerHelper_EnqueueForAdornerUpdate();
|
|
}
|
|
|
|
public void AdornerHelper_EnqueueForAdornerUpdate()
|
|
{
|
|
var helper = GetAttHelper();
|
|
if(helper.EnqueuedForAdornerUpdate)
|
|
return;
|
|
Compositor.EnqueueAdornerUpdate(this);
|
|
helper.EnqueuedForAdornerUpdate = true;
|
|
}
|
|
|
|
partial void OnAdornedVisualChanging() =>
|
|
AdornedVisual?.AttHelper_UnsubscribeFromActNotification(GetAttHelper().AdornedVisualActSubscriptionAction);
|
|
|
|
partial void OnAdornedVisualChanged()
|
|
{
|
|
AdornedVisual?.AttHelper_SubscribeToActNotification(GetAttHelper().AdornedVisualActSubscriptionAction);
|
|
AdornerHelper_EnqueueForAdornerUpdate();
|
|
}
|
|
|
|
private static ServerCompositionVisual? AdornerLayer_GetExpectedSharedAncestor(ServerCompositionVisual adorner)
|
|
{
|
|
// This is hardcoded to VisualLayerManager -> AdornerLayer -> adorner
|
|
// Since AdornedVisual is a private API that's only supposed to be accessible from AdornerLayer
|
|
// it's a safe assumption to make
|
|
return adorner?.Parent?.Parent;
|
|
}
|
|
|
|
public void UpdateAdorner()
|
|
{
|
|
GetAttHelper().EnqueuedForAdornerUpdate = false;
|
|
|
|
if (AdornedVisual != null && Parent != null)
|
|
{
|
|
// We ignore Visual's RenderTransform completely since it's set by AdornerLayer and can be out of sync
|
|
// with compositor-driver animations
|
|
var ownTransform = MatrixUtils.ComputeTransform(Size, AnchorPoint, CenterPoint, Matrix.Identity, Scale,
|
|
RotationAngle, Orientation, Offset + Translation);
|
|
if (
|
|
AdornerLayer_GetExpectedSharedAncestor(this) is {} sharedAncestor
|
|
&& ComputeTransformFromAncestor(AdornedVisual, sharedAncestor, out var adornerLayerToAdornedVisual))
|
|
_ownTransform = (ownTransform ?? Matrix.Identity) * adornerLayerToAdornedVisual;
|
|
else
|
|
_ownTransform = default(Matrix); // Don't render, something is broken
|
|
|
|
}
|
|
else
|
|
_ownTransform = MatrixUtils.ComputeTransform(Size, AnchorPoint, CenterPoint, TransformMatrix, Scale,
|
|
RotationAngle, Orientation, Offset + Translation);
|
|
|
|
|
|
PropagateFlags(true, true);
|
|
}
|
|
|
|
partial struct RenderContext
|
|
{
|
|
private enum Op
|
|
{
|
|
PopClip,
|
|
PopGeometryClip,
|
|
Stop
|
|
}
|
|
private Stack<int>? _adornerPushedClipStack;
|
|
private ServerCompositionVisual? _currentAdornerLayer;
|
|
|
|
private bool AdornerLayer_WalkAdornerParentClipRecursive(ServerCompositionVisual? visual)
|
|
{
|
|
if (visual != _currentAdornerLayer!)
|
|
{
|
|
// AdornedVisual is a part of a different subtree, this is not supported
|
|
if (visual == null)
|
|
return false;
|
|
|
|
if (!AdornerLayer_WalkAdornerParentClipRecursive(visual.Parent))
|
|
return false;
|
|
}
|
|
|
|
if (visual._ownTransform.HasValue)
|
|
_canvas.Transform = visual._ownTransform.Value * _canvas.Transform;
|
|
|
|
if (visual.ClipToBounds)
|
|
{
|
|
_canvas.PushClip(new Rect(0, 0, visual.Size.X, visual.Size.Y));
|
|
_adornerPushedClipStack!.Push((int)Op.PopClip);
|
|
}
|
|
|
|
if (visual.Clip != null)
|
|
{
|
|
_canvas.PushGeometryClip(visual.Clip);
|
|
_adornerPushedClipStack!.Push((int)Op.PopGeometryClip);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SkipAdornerClip(ServerCompositionVisual visual)
|
|
{
|
|
if (!visual.AdornerIsClipped
|
|
|| visual == _rootVisual
|
|
|| visual._parent == _rootVisual // Root visual is AdornerLayer
|
|
|| AdornerLayer_GetExpectedSharedAncestor(visual) == null)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
private void AdornerHelper_RenderPreGraphPushAdornerClip(ServerCompositionVisual visual)
|
|
{
|
|
if (SkipAdornerClip(visual))
|
|
return;
|
|
|
|
_adornerPushedClipStack ??= _pools.IntStackPool.Rent();
|
|
_adornerPushedClipStack.Push((int)Op.Stop);
|
|
|
|
var originalTransform = _canvas.Transform;
|
|
var transform = originalTransform;
|
|
if (visual._ownTransform.HasValue)
|
|
{
|
|
if (!visual._ownTransform.Value.TryInvert(out var transformToAdornerLayer))
|
|
return;
|
|
transform = transformToAdornerLayer * transform;
|
|
}
|
|
|
|
_canvas.Transform = transform;
|
|
_currentAdornerLayer = AdornerLayer_GetExpectedSharedAncestor(visual);
|
|
|
|
AdornerLayer_WalkAdornerParentClipRecursive(visual.AdornedVisual);
|
|
|
|
_canvas.Transform = originalTransform;
|
|
}
|
|
|
|
private void AdornerHelper_RenderPostGraphPushAdornerClip(ServerCompositionVisual visual)
|
|
{
|
|
if (SkipAdornerClip(visual))
|
|
return;
|
|
|
|
if (_adornerPushedClipStack == null)
|
|
return;
|
|
|
|
while (_adornerPushedClipStack.Count > 0)
|
|
{
|
|
var op = (Op)_adornerPushedClipStack.Pop();
|
|
if (op == Op.Stop)
|
|
break;
|
|
if (op == Op.PopGeometryClip)
|
|
_canvas.PopGeometryClip();
|
|
else if (op == Op.PopClip)
|
|
_canvas.PopClip();
|
|
}
|
|
}
|
|
|
|
private void AdornerHelper_Dispose()
|
|
{
|
|
_pools.IntStackPool.Return(ref _adornerPushedClipStack!);
|
|
}
|
|
}
|
|
}
|
|
|