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.
 
 
 

147 lines
4.5 KiB

using System;
using Avalonia.Input;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{
/// <summary>
/// Handles <see cref="ToolTip"/> interaction with controls.
/// </summary>
internal sealed class ToolTipService
{
public static ToolTipService Instance { get; } = new ToolTipService();
private DispatcherTimer? _timer;
private ToolTipService() { }
/// <summary>
/// called when the <see cref="ToolTip.TipProperty"/> property changes on a control.
/// </summary>
/// <param name="e">The event args.</param>
internal void TipChanged(AvaloniaPropertyChangedEventArgs e)
{
var control = (Control)e.Sender;
if (e.OldValue != null)
{
control.PointerEnter -= ControlPointerEnter;
control.PointerLeave -= ControlPointerLeave;
}
if (e.NewValue != null)
{
control.PointerEnter += ControlPointerEnter;
control.PointerLeave += ControlPointerLeave;
}
if (ToolTip.GetIsOpen(control) && e.NewValue != e.OldValue && !(e.NewValue is ToolTip))
{
if (e.NewValue is null)
{
Close(control);
}
else
{
var tip = control.GetValue(ToolTip.ToolTipProperty);
tip!.Content = e.NewValue;
}
}
}
internal void TipOpenChanged(AvaloniaPropertyChangedEventArgs e)
{
var control = (Control)e.Sender;
if (e.OldValue is false && e.NewValue is true)
{
control.DetachedFromVisualTree += ControlDetaching;
control.EffectiveViewportChanged += ControlEffectiveViewportChanged;
}
else if(e.OldValue is true && e.NewValue is false)
{
control.DetachedFromVisualTree -= ControlDetaching;
control.EffectiveViewportChanged -= ControlEffectiveViewportChanged;
}
}
private void ControlDetaching(object? sender, VisualTreeAttachmentEventArgs e)
{
var control = (Control)sender!;
control.DetachedFromVisualTree -= ControlDetaching;
control.EffectiveViewportChanged -= ControlEffectiveViewportChanged;
Close(control);
}
/// <summary>
/// Called when the pointer enters a control with an attached tooltip.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
private void ControlPointerEnter(object? sender, PointerEventArgs e)
{
StopTimer();
var control = (Control)sender!;
var showDelay = ToolTip.GetShowDelay(control);
if (showDelay == 0)
{
Open(control);
}
else
{
StartShowTimer(showDelay, control);
}
}
/// <summary>
/// Called when the pointer leaves a control with an attached tooltip.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
private void ControlPointerLeave(object? sender, PointerEventArgs e)
{
var control = (Control)sender!;
Close(control);
}
private void ControlEffectiveViewportChanged(object? sender, Layout.EffectiveViewportChangedEventArgs e)
{
var control = (Control)sender!;
var toolTip = control.GetValue(ToolTip.ToolTipProperty);
toolTip?.RecalculatePosition(control);
}
private void StartShowTimer(int showDelay, Control control)
{
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(showDelay) };
_timer.Tick += (o, e) => Open(control);
_timer.Start();
}
private void Open(Control control)
{
StopTimer();
if ((control as IVisual).IsAttachedToVisualTree)
{
ToolTip.SetIsOpen(control, true);
}
}
private void Close(Control control)
{
StopTimer();
ToolTip.SetIsOpen(control, false);
}
private void StopTimer()
{
_timer?.Stop();
_timer = null;
}
}
}