using System; using Avalonia.Input; using Avalonia.Threading; using Avalonia.VisualTree; namespace Avalonia.Controls { /// /// Handles interaction with controls. /// internal sealed class ToolTipService { public static ToolTipService Instance { get; } = new ToolTipService(); private DispatcherTimer _timer; private ToolTipService() { } /// /// called when the property changes on a control. /// /// The event args. 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); } /// /// Called when the pointer enters a control with an attached tooltip. /// /// The event sender. /// The event args. 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); } } /// /// Called when the pointer leaves a control with an attached tooltip. /// /// The event sender. /// The event args. 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; } } }