|
|
|
@ -1,19 +1,33 @@ |
|
|
|
using System; |
|
|
|
using System.Collections.Specialized; |
|
|
|
using System.Linq; |
|
|
|
using System.Resources; |
|
|
|
using Avalonia.Media; |
|
|
|
using Avalonia.Rendering; |
|
|
|
using Avalonia.Utilities; |
|
|
|
using Avalonia.VisualTree; |
|
|
|
|
|
|
|
#nullable enable |
|
|
|
|
|
|
|
namespace Avalonia.Controls.Primitives |
|
|
|
{ |
|
|
|
// TODO: Need to track position of adorned elements and move the adorner if they move.
|
|
|
|
/// <summary>
|
|
|
|
/// Represents a surface for showing adorners.
|
|
|
|
/// Adorners are always on top of the adorned element and are positioned to stay relative to the adorned element.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// TODO: Need to track position of adorned elements and move the adorner if they move.
|
|
|
|
/// </remarks>
|
|
|
|
public class AdornerLayer : Canvas, ICustomSimpleHitTest |
|
|
|
{ |
|
|
|
public static readonly AttachedProperty<Visual> AdornedElementProperty = |
|
|
|
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, Visual>("AdornedElement"); |
|
|
|
/// <summary>
|
|
|
|
/// Allows for getting and setting of the adorned element.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly AttachedProperty<Visual?> AdornedElementProperty = |
|
|
|
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, Visual?>("AdornedElement"); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Allows for controlling clipping of the adorner.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly AttachedProperty<bool> IsClipEnabledProperty = |
|
|
|
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, bool>("IsClipEnabled", true); |
|
|
|
|
|
|
|
private static readonly AttachedProperty<AdornedElementInfo> s_adornedElementInfoProperty = |
|
|
|
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, AdornedElementInfo>("AdornedElementInfo"); |
|
|
|
@ -28,7 +42,7 @@ namespace Avalonia.Controls.Primitives |
|
|
|
Children.CollectionChanged += ChildrenCollectionChanged; |
|
|
|
} |
|
|
|
|
|
|
|
public static Visual GetAdornedElement(Visual adorner) |
|
|
|
public static Visual? GetAdornedElement(Visual adorner) |
|
|
|
{ |
|
|
|
return adorner.GetValue(AdornedElementProperty); |
|
|
|
} |
|
|
|
@ -38,12 +52,19 @@ namespace Avalonia.Controls.Primitives |
|
|
|
adorner.SetValue(AdornedElementProperty, adorned); |
|
|
|
} |
|
|
|
|
|
|
|
public static AdornerLayer GetAdornerLayer(IVisual visual) |
|
|
|
public static AdornerLayer? GetAdornerLayer(IVisual visual) |
|
|
|
{ |
|
|
|
return visual.FindAncestorOfType<VisualLayerManager>()?.AdornerLayer; |
|
|
|
} |
|
|
|
|
|
|
|
public static bool GetIsClipEnabled(Visual adorner) |
|
|
|
{ |
|
|
|
return visual.GetVisualAncestors() |
|
|
|
.OfType<VisualLayerManager>() |
|
|
|
.FirstOrDefault() |
|
|
|
?.AdornerLayer; |
|
|
|
return adorner.GetValue(IsClipEnabledProperty); |
|
|
|
} |
|
|
|
|
|
|
|
public static void SetIsClipEnabled(Visual adorner, bool isClipEnabled) |
|
|
|
{ |
|
|
|
adorner.SetValue(IsClipEnabledProperty, isClipEnabled); |
|
|
|
} |
|
|
|
|
|
|
|
protected override Size MeasureOverride(Size availableSize) |
|
|
|
@ -70,12 +91,13 @@ namespace Avalonia.Controls.Primitives |
|
|
|
foreach (var child in Children) |
|
|
|
{ |
|
|
|
var info = child.GetValue(s_adornedElementInfoProperty); |
|
|
|
var isClipEnabled = child.GetValue(IsClipEnabledProperty); |
|
|
|
|
|
|
|
if (info != null && info.Bounds.HasValue) |
|
|
|
{ |
|
|
|
child.RenderTransform = new MatrixTransform(info.Bounds.Value.Transform); |
|
|
|
child.RenderTransformOrigin = new RelativePoint(new Point(0,0), RelativeUnit.Absolute); |
|
|
|
UpdateClip(child, info.Bounds.Value); |
|
|
|
UpdateClip(child, info.Bounds.Value, isClipEnabled); |
|
|
|
child.Arrange(info.Bounds.Value.Bounds); |
|
|
|
} |
|
|
|
else |
|
|
|
@ -87,16 +109,23 @@ namespace Avalonia.Controls.Primitives |
|
|
|
return finalSize; |
|
|
|
} |
|
|
|
|
|
|
|
private static void AdornedElementChanged(AvaloniaPropertyChangedEventArgs e) |
|
|
|
private static void AdornedElementChanged(AvaloniaPropertyChangedEventArgs<Visual?> e) |
|
|
|
{ |
|
|
|
var adorner = (Visual)e.Sender; |
|
|
|
var adorned = (Visual)e.NewValue; |
|
|
|
var adorned = e.NewValue.GetValueOrDefault(); |
|
|
|
var layer = adorner.GetVisualParent<AdornerLayer>(); |
|
|
|
layer?.UpdateAdornedElement(adorner, adorned); |
|
|
|
} |
|
|
|
|
|
|
|
private void UpdateClip(IControl control, TransformedBounds bounds) |
|
|
|
private void UpdateClip(IControl control, TransformedBounds bounds, bool isEnabled) |
|
|
|
{ |
|
|
|
if (!isEnabled) |
|
|
|
{ |
|
|
|
control.Clip = null; |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (!(control.Clip is RectangleGeometry clip)) |
|
|
|
{ |
|
|
|
clip = new RectangleGeometry(); |
|
|
|
@ -129,13 +158,13 @@ namespace Avalonia.Controls.Primitives |
|
|
|
InvalidateArrange(); |
|
|
|
} |
|
|
|
|
|
|
|
private void UpdateAdornedElement(Visual adorner, Visual adorned) |
|
|
|
private void UpdateAdornedElement(Visual adorner, Visual? adorned) |
|
|
|
{ |
|
|
|
var info = adorner.GetValue(s_adornedElementInfoProperty); |
|
|
|
|
|
|
|
if (info != null) |
|
|
|
{ |
|
|
|
info.Subscription.Dispose(); |
|
|
|
info.Subscription!.Dispose(); |
|
|
|
|
|
|
|
if (adorned == null) |
|
|
|
{ |
|
|
|
@ -163,7 +192,7 @@ namespace Avalonia.Controls.Primitives |
|
|
|
|
|
|
|
private class AdornedElementInfo |
|
|
|
{ |
|
|
|
public IDisposable Subscription { get; set; } |
|
|
|
public IDisposable? Subscription { get; set; } |
|
|
|
|
|
|
|
public TransformedBounds? Bounds { get; set; } |
|
|
|
} |
|
|
|
|