Make sure that adorners are actually correctly positioned in visual tree for TransformToVisual and friends (#20691)
* Make sure that adorners are actually correctly positioned in visual tree for TransformToVisual and friends * Fixes
This commit is contained in:
@@ -44,21 +44,31 @@
|
|||||||
VerticalContentAlignment="Center" VerticalAlignment="Stretch"
|
VerticalContentAlignment="Center" VerticalAlignment="Stretch"
|
||||||
Width="200" Height="42">
|
Width="200" Height="42">
|
||||||
<AdornerLayer.Adorner>
|
<AdornerLayer.Adorner>
|
||||||
<Canvas x:Name="AdornerCanvas"
|
<Border AdornerLayer.IsClipEnabled="False" ClipToBounds="False" Background="Red" Opacity="0.3" Margin="-5" Padding="5"
|
||||||
HorizontalAlignment="Stretch"
|
IsHitTestVisible="False">
|
||||||
VerticalAlignment="Stretch"
|
<Canvas
|
||||||
Background="Cyan"
|
x:Name="AdornerCanvas"
|
||||||
IsHitTestVisible="False"
|
HorizontalAlignment="Stretch"
|
||||||
Opacity="0.3"
|
VerticalAlignment="Stretch"
|
||||||
IsVisible="True"
|
Background="Cyan"
|
||||||
AdornerLayer.IsClipEnabled="False">
|
IsHitTestVisible="False"
|
||||||
<Line StartPoint="-10000,0" EndPoint="10000,0" Stroke="Cyan" StrokeThickness="1" />
|
IsVisible="True"
|
||||||
<Line StartPoint="-10000,42" EndPoint="10000,42" Stroke="Cyan" StrokeThickness="1" />
|
AdornerLayer.IsClipEnabled="False">
|
||||||
<Line StartPoint="0,-10000" EndPoint="0,10000" Stroke="Cyan" StrokeThickness="1" />
|
<Line StartPoint="-10000,0" EndPoint="10000,0" Stroke="Cyan" StrokeThickness="1" />
|
||||||
<Line StartPoint="200,-10000" EndPoint="200,10000" Stroke="Cyan" StrokeThickness="1" />
|
<Line StartPoint="-10000,42" EndPoint="10000,42" Stroke="Cyan" StrokeThickness="1" />
|
||||||
</Canvas>
|
<Line StartPoint="0,-10000" EndPoint="0,10000" Stroke="Cyan" StrokeThickness="1" />
|
||||||
|
<Line StartPoint="200,-10000" EndPoint="200,10000" Stroke="Cyan" StrokeThickness="1" />
|
||||||
|
</Canvas>
|
||||||
|
</Border>
|
||||||
</AdornerLayer.Adorner>
|
</AdornerLayer.Adorner>
|
||||||
|
<Button.ContextMenu>
|
||||||
|
<ContextMenu PlacementTarget="{ResolveByName AdornerCanvas}" Placement="TopEdgeAlignedLeft">
|
||||||
|
<MenuItem Header="Menu 1"/>
|
||||||
|
<MenuItem Header="Menu 2"/>
|
||||||
|
</ContextMenu>
|
||||||
|
</Button.ContextMenu>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
||||||
</LayoutTransformControl>
|
</LayoutTransformControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Avalonia.Input.TextInput
|
|||||||
private IInputElement? _focusedElement;
|
private IInputElement? _focusedElement;
|
||||||
private Interactive? _visualRoot;
|
private Interactive? _visualRoot;
|
||||||
private TextInputMethodClient? _client;
|
private TextInputMethodClient? _client;
|
||||||
private readonly TransformTrackingHelper _transformTracker = new TransformTrackingHelper();
|
private readonly TransformTrackingHelper _transformTracker = new TransformTrackingHelper(true);
|
||||||
|
|
||||||
public TextInputMethodManager()
|
public TextInputMethodManager()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Reactive;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
|
|
||||||
@@ -7,13 +9,15 @@ namespace Avalonia.Input.TextInput
|
|||||||
{
|
{
|
||||||
class TransformTrackingHelper : IDisposable
|
class TransformTrackingHelper : IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly bool _deferAfterRenderPass;
|
||||||
private Visual? _visual;
|
private Visual? _visual;
|
||||||
private bool _queuedForUpdate;
|
private bool _queuedForUpdate;
|
||||||
private readonly EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChangedHandler;
|
private readonly EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChangedHandler;
|
||||||
private readonly List<Visual> _propertyChangedSubscriptions = new List<Visual>();
|
private readonly List<Visual> _propertyChangedSubscriptions = new List<Visual>();
|
||||||
|
|
||||||
public TransformTrackingHelper()
|
public TransformTrackingHelper(bool deferAfterRenderPass)
|
||||||
{
|
{
|
||||||
|
_deferAfterRenderPass = deferAfterRenderPass;
|
||||||
_propertyChangedHandler = PropertyChangedHandler;
|
_propertyChangedHandler = PropertyChangedHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +74,7 @@ namespace Avalonia.Input.TextInput
|
|||||||
|
|
||||||
void UpdateMatrix()
|
void UpdateMatrix()
|
||||||
{
|
{
|
||||||
|
_queuedForUpdate = false;
|
||||||
Matrix? matrix = null;
|
Matrix? matrix = null;
|
||||||
if (_visual != null && _visual.VisualRoot != null)
|
if (_visual != null && _visual.VisualRoot != null)
|
||||||
matrix = _visual.TransformToVisual((Visual)_visual.VisualRoot);
|
matrix = _visual.TransformToVisual((Visual)_visual.VisualRoot);
|
||||||
@@ -91,7 +96,10 @@ namespace Avalonia.Input.TextInput
|
|||||||
if(_queuedForUpdate)
|
if(_queuedForUpdate)
|
||||||
return;
|
return;
|
||||||
_queuedForUpdate = true;
|
_queuedForUpdate = true;
|
||||||
Dispatcher.UIThread.Post(UpdateMatrix, DispatcherPriority.AfterRender);
|
if (_deferAfterRenderPass)
|
||||||
|
Dispatcher.UIThread.Post(UpdateMatrix, DispatcherPriority.AfterRender);
|
||||||
|
else
|
||||||
|
MediaContext.Instance.BeginInvokeOnRender(UpdateMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PropertyChangedHandler(object? sender, AvaloniaPropertyChangedEventArgs e)
|
private void PropertyChangedHandler(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||||
@@ -106,12 +114,23 @@ namespace Avalonia.Input.TextInput
|
|||||||
UpdateMatrix();
|
UpdateMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IDisposable Track(Visual visual, Action<Visual, Matrix?> cb)
|
public static IDisposable Track(Visual visual, bool deferAfterRenderPass, Action<Visual, Matrix?> cb)
|
||||||
{
|
{
|
||||||
var rv = new TransformTrackingHelper();
|
var rv = new TransformTrackingHelper(deferAfterRenderPass);
|
||||||
rv.MatrixChanged += () => cb(visual, rv.Matrix);
|
rv.MatrixChanged += () => cb(visual, rv.Matrix);
|
||||||
rv.SetVisual(visual);
|
rv.SetVisual(visual);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IObservable<Matrix?> Observe(Visual visual, bool deferAfterRenderPass)
|
||||||
|
{
|
||||||
|
return Observable.Create<Matrix?>(observer =>
|
||||||
|
{
|
||||||
|
var rv = new TransformTrackingHelper(deferAfterRenderPass);
|
||||||
|
rv.MatrixChanged += () => observer.OnNext(rv.Matrix);
|
||||||
|
rv.SetVisual(visual);
|
||||||
|
return rv;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-6
@@ -46,20 +46,25 @@ partial class ServerCompositionVisual
|
|||||||
public void UpdateAdorner()
|
public void UpdateAdorner()
|
||||||
{
|
{
|
||||||
GetAttHelper().EnqueuedForAdornerUpdate = false;
|
GetAttHelper().EnqueuedForAdornerUpdate = false;
|
||||||
var ownTransform = MatrixUtils.ComputeTransform(Size, AnchorPoint, CenterPoint, TransformMatrix, Scale,
|
|
||||||
RotationAngle, Orientation, Offset);
|
|
||||||
|
|
||||||
if (AdornedVisual != null && Parent != null)
|
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);
|
||||||
if (
|
if (
|
||||||
AdornerLayer_GetExpectedSharedAncestor(this) is {} sharedAncestor
|
AdornerLayer_GetExpectedSharedAncestor(this) is {} sharedAncestor
|
||||||
&& ComputeTransformFromAncestor(AdornedVisual, sharedAncestor, out var adornerLayerToAdornedVisual))
|
&& ComputeTransformFromAncestor(AdornedVisual, sharedAncestor, out var adornerLayerToAdornedVisual))
|
||||||
ownTransform = (ownTransform ?? Matrix.Identity) * adornerLayerToAdornedVisual;
|
_ownTransform = (ownTransform ?? Matrix.Identity) * adornerLayerToAdornedVisual;
|
||||||
else
|
else
|
||||||
ownTransform = default(Matrix); // Don't render, something is broken
|
_ownTransform = default(Matrix); // Don't render, something is broken
|
||||||
|
|
||||||
}
|
}
|
||||||
_ownTransform = ownTransform;
|
else
|
||||||
|
_ownTransform = MatrixUtils.ComputeTransform(Size, AnchorPoint, CenterPoint, TransformMatrix, Scale,
|
||||||
|
RotationAngle, Orientation, Offset);
|
||||||
|
|
||||||
|
|
||||||
PropagateFlags(true, true);
|
PropagateFlags(true, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
|
|
||||||
|
namespace Avalonia.Controls.Primitives;
|
||||||
|
|
||||||
|
class AdornerHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
public static IDisposable SubscribeToAncestorPropertyChanges(Visual visual,
|
||||||
|
bool includeClip, Action changed)
|
||||||
|
{
|
||||||
|
return new AncestorPropertyChangesSubscription(visual, includeClip, changed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AncestorPropertyChangesSubscription : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Visual _visual;
|
||||||
|
private readonly bool _includeClip;
|
||||||
|
private readonly Action _changed;
|
||||||
|
private readonly EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChangedHandler;
|
||||||
|
private readonly List<Visual> _subscriptions = new List<Visual>();
|
||||||
|
private bool _isDisposed;
|
||||||
|
|
||||||
|
public AncestorPropertyChangesSubscription(Visual visual, bool includeClip, Action changed)
|
||||||
|
{
|
||||||
|
_visual = visual;
|
||||||
|
_includeClip = includeClip;
|
||||||
|
_changed = changed;
|
||||||
|
_propertyChangedHandler = OnPropertyChanged;
|
||||||
|
|
||||||
|
_visual.AttachedToVisualTree += OnAttachedToVisualTree;
|
||||||
|
_visual.DetachedFromVisualTree += OnDetachedFromVisualTree;
|
||||||
|
|
||||||
|
if (_visual.IsAttachedToVisualTree)
|
||||||
|
{
|
||||||
|
SubscribeToAncestors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SubscribeToAncestors()
|
||||||
|
{
|
||||||
|
UnsubscribeFromAncestors();
|
||||||
|
|
||||||
|
// Subscribe to the visual's own Bounds property
|
||||||
|
_visual.PropertyChanged += _propertyChangedHandler;
|
||||||
|
_subscriptions.Add(_visual);
|
||||||
|
|
||||||
|
// Walk up the ancestor chain
|
||||||
|
var ancestor = _visual.VisualParent;
|
||||||
|
while (ancestor != null)
|
||||||
|
{
|
||||||
|
if (ancestor is Visual visualAncestor)
|
||||||
|
{
|
||||||
|
visualAncestor.PropertyChanged += _propertyChangedHandler;
|
||||||
|
_subscriptions.Add(visualAncestor);
|
||||||
|
}
|
||||||
|
ancestor = ancestor.VisualParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnsubscribeFromAncestors()
|
||||||
|
{
|
||||||
|
foreach (var subscription in _subscriptions)
|
||||||
|
{
|
||||||
|
subscription.PropertyChanged -= _propertyChangedHandler;
|
||||||
|
}
|
||||||
|
_subscriptions.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.IsEffectiveValueChange)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool shouldNotify = false;
|
||||||
|
|
||||||
|
if (e.Property == Visual.RenderTransformProperty || e.Property == Visual.BoundsProperty)
|
||||||
|
{
|
||||||
|
shouldNotify = true;
|
||||||
|
}
|
||||||
|
else if (_includeClip)
|
||||||
|
{
|
||||||
|
if (e.Property == Visual.ClipToBoundsProperty ||
|
||||||
|
e.Property == Visual.ClipProperty) shouldNotify = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldNotify)
|
||||||
|
{
|
||||||
|
_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
SubscribeToAncestors();
|
||||||
|
_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
UnsubscribeFromAncestors();
|
||||||
|
_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDisposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isDisposed = true;
|
||||||
|
UnsubscribeFromAncestors();
|
||||||
|
_visual.AttachedToVisualTree -= OnAttachedToVisualTree;
|
||||||
|
_visual.DetachedFromVisualTree -= OnDetachedFromVisualTree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Geometry? CalculateAdornerClip(Visual adornedElement)
|
||||||
|
{
|
||||||
|
// Walk ancestor stack and calculate clip geometry relative to the current visual.
|
||||||
|
// If ClipToBounds = true, add extra RectangleGeometry for Bounds.Size
|
||||||
|
|
||||||
|
Geometry? result = null;
|
||||||
|
var ancestor = adornedElement;
|
||||||
|
|
||||||
|
while (ancestor != null)
|
||||||
|
{
|
||||||
|
if (ancestor is Visual visualAncestor)
|
||||||
|
{
|
||||||
|
Geometry? ancestorClip = null;
|
||||||
|
|
||||||
|
// Check if ancestor has ClipToBounds enabled
|
||||||
|
if (visualAncestor.ClipToBounds)
|
||||||
|
{
|
||||||
|
ancestorClip = new RectangleGeometry(new Rect(visualAncestor.Bounds.Size));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if ancestor has explicit Clip geometry
|
||||||
|
if (visualAncestor.Clip != null)
|
||||||
|
{
|
||||||
|
if (ancestorClip != null)
|
||||||
|
{
|
||||||
|
ancestorClip = new CombinedGeometry(GeometryCombineMode.Intersect, ancestorClip, visualAncestor.Clip);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ancestorClip = visualAncestor.Clip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform the clip geometry to adorned element's coordinate space
|
||||||
|
if (ancestorClip != null)
|
||||||
|
{
|
||||||
|
var transform = visualAncestor.TransformToVisual(adornedElement);
|
||||||
|
if (transform.HasValue && !transform.Value.IsIdentity)
|
||||||
|
{
|
||||||
|
ancestorClip = ancestorClip.Clone();
|
||||||
|
var matrix = ancestorClip.Transform is { Value.IsIdentity: false }
|
||||||
|
? transform.Value * ancestorClip.Transform.Value
|
||||||
|
: transform.Value;
|
||||||
|
ancestorClip.Transform = new MatrixTransform(matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine with existing result
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
result = new CombinedGeometry(GeometryCombineMode.Intersect, result, ancestorClip);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = ancestorClip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ancestor = ancestor.VisualParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using Avalonia.Input.TextInput;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Reactive;
|
using Avalonia.Reactive;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
@@ -45,15 +46,20 @@ namespace Avalonia.Controls.Primitives
|
|||||||
private static readonly AttachedProperty<AdornerLayer?> s_savedAdornerLayerProperty =
|
private static readonly AttachedProperty<AdornerLayer?> s_savedAdornerLayerProperty =
|
||||||
AvaloniaProperty.RegisterAttached<Visual, Visual, AdornerLayer?>("SavedAdornerLayer");
|
AvaloniaProperty.RegisterAttached<Visual, Visual, AdornerLayer?>("SavedAdornerLayer");
|
||||||
|
|
||||||
|
private TransformTrackingHelper _trackingHelper = new TransformTrackingHelper(false);
|
||||||
|
|
||||||
static AdornerLayer()
|
static AdornerLayer()
|
||||||
{
|
{
|
||||||
AdornedElementProperty.Changed.Subscribe(AdornedElementChanged);
|
AdornedElementProperty.Changed.Subscribe(AdornedElementChanged);
|
||||||
AdornerProperty.Changed.Subscribe(AdornerChanged);
|
AdornerProperty.Changed.Subscribe(AdornerChanged);
|
||||||
|
IsClipEnabledProperty.Changed.Subscribe(AdornerIsClipEnabledChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AdornerLayer()
|
public AdornerLayer()
|
||||||
{
|
{
|
||||||
Children.CollectionChanged += ChildrenCollectionChanged;
|
Children.CollectionChanged += ChildrenCollectionChanged;
|
||||||
|
_trackingHelper.SetVisual(this);
|
||||||
|
_trackingHelper.MatrixChanged += delegate { InvalidateMeasure(); };
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Visual? GetAdornedElement(Visual adorner)
|
public static Visual? GetAdornedElement(Visual adorner)
|
||||||
@@ -199,9 +205,9 @@ namespace Avalonia.Controls.Primitives
|
|||||||
{
|
{
|
||||||
var info = ao.GetValue(s_adornedElementInfoProperty);
|
var info = ao.GetValue(s_adornedElementInfoProperty);
|
||||||
|
|
||||||
if (info != null && info.Bounds.HasValue)
|
if (info is { AdornedElement: not null })
|
||||||
{
|
{
|
||||||
child.Measure(info.Bounds.Value.Bounds.Size);
|
child.Measure(info.AdornedElement.Bounds.Size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -223,12 +229,22 @@ namespace Avalonia.Controls.Primitives
|
|||||||
var info = ao.GetValue(s_adornedElementInfoProperty);
|
var info = ao.GetValue(s_adornedElementInfoProperty);
|
||||||
var isClipEnabled = ao.GetValue(IsClipEnabledProperty);
|
var isClipEnabled = ao.GetValue(IsClipEnabledProperty);
|
||||||
|
|
||||||
if (info != null && info.Bounds.HasValue)
|
var adorned = info?.AdornedElement;
|
||||||
|
|
||||||
|
if (adorned != null)
|
||||||
{
|
{
|
||||||
child.RenderTransform = new MatrixTransform(info.Bounds.Value.Transform);
|
child.Arrange(new(adorned.Bounds.Size));
|
||||||
|
var transform = adorned.TransformToVisual(this);
|
||||||
|
// If somebody decides that having Margin on an adorner is a good idea,
|
||||||
|
// we need to compensate for element being positioned at non-(0,0) coords.
|
||||||
|
if (transform != null && child.Bounds.Position != default)
|
||||||
|
{
|
||||||
|
transform = Matrix.CreateTranslation(child.Bounds.Position) * transform.Value *
|
||||||
|
Matrix.CreateTranslation(-child.Bounds.Position);
|
||||||
|
}
|
||||||
|
child.RenderTransform = new MatrixTransform(transform ?? default);
|
||||||
child.RenderTransformOrigin = new RelativePoint(new Point(0, 0), RelativeUnit.Absolute);
|
child.RenderTransformOrigin = new RelativePoint(new Point(0, 0), RelativeUnit.Absolute);
|
||||||
UpdateClip(child, info.Bounds.Value, isClipEnabled);
|
UpdateClip(child, adorned, isClipEnabled);
|
||||||
child.Arrange(info.Bounds.Value.Bounds);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -248,29 +264,22 @@ namespace Avalonia.Controls.Primitives
|
|||||||
layer?.UpdateAdornedElement(adorner, adorned);
|
layer?.UpdateAdornedElement(adorner, adorned);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateClip(Control control, TransformedBounds bounds, bool isEnabled)
|
private static void AdornerIsClipEnabledChanged(AvaloniaPropertyChangedEventArgs<bool> e)
|
||||||
|
{
|
||||||
|
var info = ((Visual)e.Sender).GetValue(s_adornedElementInfoProperty);
|
||||||
|
info?.UpdateSubscription();
|
||||||
|
info?.Layer?.InvalidateMeasure();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateClip(Control control, Visual adorned, bool isEnabled)
|
||||||
{
|
{
|
||||||
if (!isEnabled)
|
if (!isEnabled)
|
||||||
{
|
{
|
||||||
control.Clip = null;
|
control.Clip = null;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(control.Clip is RectangleGeometry clip))
|
control.Clip = AdornerHelper.CalculateAdornerClip(adorned);
|
||||||
{
|
|
||||||
clip = new RectangleGeometry();
|
|
||||||
control.Clip = clip;
|
|
||||||
}
|
|
||||||
|
|
||||||
var clipBounds = bounds.Bounds;
|
|
||||||
|
|
||||||
if (bounds.Transform.HasInverse)
|
|
||||||
{
|
|
||||||
clipBounds = bounds.Clip.TransformToAABB(bounds.Transform.Invert());
|
|
||||||
}
|
|
||||||
|
|
||||||
clip.Rect = clipBounds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
private void ChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||||
@@ -313,24 +322,35 @@ namespace Avalonia.Controls.Primitives
|
|||||||
{
|
{
|
||||||
if (info == null)
|
if (info == null)
|
||||||
{
|
{
|
||||||
info = new AdornedElementInfo();
|
info = new AdornedElementInfo(adorner);
|
||||||
adorner.SetValue(s_adornedElementInfoProperty, info);
|
adorner.SetValue(s_adornedElementInfoProperty, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adorner.CompositionVisual != null)
|
info.Layer = this;
|
||||||
info.Subscription = adorned.GetObservable(BoundsProperty).Subscribe(x =>
|
info.AdornedElement = adorned;
|
||||||
{
|
info.UpdateSubscription();
|
||||||
info.Bounds = new TransformedBounds(new Rect(adorned.Bounds.Size), new Rect(adorned.Bounds.Size), Matrix.Identity);
|
|
||||||
InvalidateMeasure();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AdornedElementInfo
|
private class AdornedElementInfo(Visual adorner)
|
||||||
{
|
{
|
||||||
|
public AdornerLayer? Layer { get; set; }
|
||||||
public IDisposable? Subscription { get; set; }
|
public IDisposable? Subscription { get; set; }
|
||||||
|
public Visual? AdornedElement { get; set; }
|
||||||
|
|
||||||
public TransformedBounds? Bounds { get; set; }
|
public void UpdateSubscription()
|
||||||
|
{
|
||||||
|
Subscription?.Dispose();
|
||||||
|
Subscription = null;
|
||||||
|
if (AdornedElement != null)
|
||||||
|
{
|
||||||
|
Subscription = AdornerHelper.SubscribeToAncestorPropertyChanges(AdornedElement,
|
||||||
|
GetIsClipEnabled(adorner), () =>
|
||||||
|
{
|
||||||
|
Layer?.InvalidateMeasure();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ namespace Avalonia.Controls.Primitives
|
|||||||
|
|
||||||
if (InheritsTransform)
|
if (InheritsTransform)
|
||||||
{
|
{
|
||||||
TransformTrackingHelper.Track(placementTarget, PlacementTargetTransformChanged)
|
TransformTrackingHelper.Track(placementTarget, true, PlacementTargetTransformChanged)
|
||||||
.DisposeWith(handlerCleanup);
|
.DisposeWith(handlerCleanup);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -570,16 +570,8 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
|
|||||||
var target = positionRequest.Target;
|
var target = positionRequest.Target;
|
||||||
if (target == null)
|
if (target == null)
|
||||||
throw new InvalidOperationException("Placement mode is not Pointer and PlacementTarget is null");
|
throw new InvalidOperationException("Placement mode is not Pointer and PlacementTarget is null");
|
||||||
Matrix? matrix;
|
Matrix? matrix = target.TransformToVisual(topLevel);
|
||||||
if (TryGetAdorner(target, out var adorned, out var adornerLayer))
|
|
||||||
{
|
|
||||||
matrix = adorned!.TransformToVisual(topLevel) * target.TransformToVisual(adornerLayer!);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
matrix = target.TransformToVisual(topLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matrix == null)
|
if (matrix == null)
|
||||||
{
|
{
|
||||||
if (target.GetVisualRoot() == null)
|
if (target.GetVisualRoot() == null)
|
||||||
@@ -591,25 +583,6 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
|
|||||||
var anchorRect = positionRequest.AnchorRect ?? bounds;
|
var anchorRect = positionRequest.AnchorRect ?? bounds;
|
||||||
return anchorRect.Intersect(bounds).TransformToAABB(matrix.Value);
|
return anchorRect.Intersect(bounds).TransformToAABB(matrix.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryGetAdorner(Visual target, out Visual? adorned, out Visual? adornerLayer)
|
|
||||||
{
|
|
||||||
var element = target;
|
|
||||||
while (element != null)
|
|
||||||
{
|
|
||||||
if (AdornerLayer.GetAdornedElement(element) is { } adornedElement)
|
|
||||||
{
|
|
||||||
adorned = adornedElement;
|
|
||||||
adornerLayer = AdornerLayer.GetAdornerLayer(adorned);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
element = element.VisualParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
adorned = null;
|
|
||||||
adornerLayer = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user