A cross-platform UI framework for .NET
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.
 
 
 

117 lines
3.6 KiB

using System;
using System.Collections.Generic;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Input.TextInput
{
class TransformTrackingHelper : IDisposable
{
private Visual? _visual;
private bool _queuedForUpdate;
private readonly EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChangedHandler;
private readonly List<Visual> _propertyChangedSubscriptions = new List<Visual>();
public TransformTrackingHelper()
{
_propertyChangedHandler = PropertyChangedHandler;
}
public void SetVisual(Visual? visual)
{
Dispose();
_visual = visual;
if (visual != null)
{
visual.AttachedToVisualTree += OnAttachedToVisualTree;
visual.DetachedFromVisualTree -= OnDetachedFromVisualTree;
if (visual.IsAttachedToVisualTree)
SubscribeToParents();
UpdateMatrix();
}
}
public Matrix? Matrix { get; private set; }
public event Action? MatrixChanged;
public void Dispose()
{
if(_visual == null)
return;
UnsubscribeFromParents();
_visual.AttachedToVisualTree -= OnAttachedToVisualTree;
_visual.DetachedFromVisualTree -= OnDetachedFromVisualTree;
_visual = null;
}
private void SubscribeToParents()
{
var visual = _visual;
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
// false positive
while (visual != null)
{
if (visual is Visual v)
{
v.PropertyChanged += _propertyChangedHandler;
_propertyChangedSubscriptions.Add(v);
}
visual = visual.VisualParent;
}
}
private void UnsubscribeFromParents()
{
foreach (var v in _propertyChangedSubscriptions)
v.PropertyChanged -= _propertyChangedHandler;
_propertyChangedSubscriptions.Clear();
}
void UpdateMatrix()
{
Matrix? matrix = null;
if (_visual != null && _visual.VisualRoot != null)
matrix = _visual.TransformToVisual((Visual)_visual.VisualRoot);
if (Matrix != matrix)
{
Matrix = matrix;
MatrixChanged?.Invoke();
}
}
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs visualTreeAttachmentEventArgs)
{
SubscribeToParents();
UpdateMatrix();
}
private void EnqueueForUpdate()
{
if(_queuedForUpdate)
return;
_queuedForUpdate = true;
Dispatcher.UIThread.Post(UpdateMatrix, DispatcherPriority.Render);
}
private void PropertyChangedHandler(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.IsEffectiveValueChange && e.Property == Visual.BoundsProperty)
EnqueueForUpdate();
}
private void OnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs visualTreeAttachmentEventArgs)
{
UnsubscribeFromParents();
UpdateMatrix();
}
public static IDisposable Track(Visual visual, Action<Visual, Matrix?> cb)
{
var rv = new TransformTrackingHelper();
rv.MatrixChanged += () => cb(visual, rv.Matrix);
rv.SetVisual(visual);
return rv;
}
}
}