using System; using System.Reactive.Linq; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.VisualTree; namespace Avalonia.Controls { /// /// A control which pops up a hint when a control is hovered. /// /// /// You will probably not want to create a control directly: if added to /// the tree it will act as a simple styled to look like a tooltip. /// To add a tooltip to a control, use the attached property, /// assigning the content that you want displayed. /// [PseudoClasses(":open")] public class ToolTip : ContentControl { /// /// Defines the ToolTip.Tip attached property. /// public static readonly AttachedProperty TipProperty = AvaloniaProperty.RegisterAttached("Tip"); /// /// Defines the ToolTip.IsOpen attached property. /// public static readonly AttachedProperty IsOpenProperty = AvaloniaProperty.RegisterAttached("IsOpen"); /// /// Defines the ToolTip.Placement property. /// public static readonly AttachedProperty PlacementProperty = AvaloniaProperty.RegisterAttached("Placement", defaultValue: PlacementMode.Pointer); /// /// Defines the ToolTip.HorizontalOffset property. /// public static readonly AttachedProperty HorizontalOffsetProperty = AvaloniaProperty.RegisterAttached("HorizontalOffset"); /// /// Defines the ToolTip.VerticalOffset property. /// public static readonly AttachedProperty VerticalOffsetProperty = AvaloniaProperty.RegisterAttached("VerticalOffset", 20); /// /// Defines the ToolTip.ShowDelay property. /// public static readonly AttachedProperty ShowDelayProperty = AvaloniaProperty.RegisterAttached("ShowDelay", 400); /// /// Stores the current instance in the control. /// internal static readonly AttachedProperty ToolTipProperty = AvaloniaProperty.RegisterAttached("ToolTip"); private IPopupHost _popup; /// /// Initializes static members of the class. /// static ToolTip() { TipProperty.Changed.Subscribe(ToolTipService.Instance.TipChanged); IsOpenProperty.Changed.Subscribe(ToolTipService.Instance.TipOpenChanged); IsOpenProperty.Changed.Subscribe(IsOpenChanged); } /// /// Gets the value of the ToolTip.Tip attached property. /// /// The control to get the property from. /// /// The content to be displayed in the control's tooltip. /// public static object GetTip(Control element) { return element.GetValue(TipProperty); } /// /// Sets the value of the ToolTip.Tip attached property. /// /// The control to get the property from. /// The content to be displayed in the control's tooltip. public static void SetTip(Control element, object value) { element.SetValue(TipProperty, value); } /// /// Gets the value of the ToolTip.IsOpen attached property. /// /// The control to get the property from. /// /// A value indicating whether the tool tip is visible. /// public static bool GetIsOpen(Control element) { return element.GetValue(IsOpenProperty); } /// /// Sets the value of the ToolTip.IsOpen attached property. /// /// The control to get the property from. /// A value indicating whether the tool tip is visible. public static void SetIsOpen(Control element, bool value) { element.SetValue(IsOpenProperty, value); } /// /// Gets the value of the ToolTip.Placement attached property. /// /// The control to get the property from. /// /// A value indicating how the tool tip is positioned. /// public static PlacementMode GetPlacement(Control element) { return element.GetValue(PlacementProperty); } /// /// Sets the value of the ToolTip.Placement attached property. /// /// The control to get the property from. /// A value indicating how the tool tip is positioned. public static void SetPlacement(Control element, PlacementMode value) { element.SetValue(PlacementProperty, value); } /// /// Gets the value of the ToolTip.HorizontalOffset attached property. /// /// The control to get the property from. /// /// A value indicating how the tool tip is positioned. /// public static double GetHorizontalOffset(Control element) { return element.GetValue(HorizontalOffsetProperty); } /// /// Sets the value of the ToolTip.HorizontalOffset attached property. /// /// The control to get the property from. /// A value indicating how the tool tip is positioned. public static void SetHorizontalOffset(Control element, double value) { element.SetValue(HorizontalOffsetProperty, value); } /// /// Gets the value of the ToolTip.VerticalOffset attached property. /// /// The control to get the property from. /// /// A value indicating how the tool tip is positioned. /// public static double GetVerticalOffset(Control element) { return element.GetValue(VerticalOffsetProperty); } /// /// Sets the value of the ToolTip.VerticalOffset attached property. /// /// The control to get the property from. /// A value indicating how the tool tip is positioned. public static void SetVerticalOffset(Control element, double value) { element.SetValue(VerticalOffsetProperty, value); } /// /// Gets the value of the ToolTip.ShowDelay attached property. /// /// The control to get the property from. /// /// A value indicating the time, in milliseconds, before a tool tip opens. /// public static int GetShowDelay(Control element) { return element.GetValue(ShowDelayProperty); } /// /// Sets the value of the ToolTip.ShowDelay attached property. /// /// The control to get the property from. /// A value indicating the time, in milliseconds, before a tool tip opens. public static void SetShowDelay(Control element, int value) { element.SetValue(ShowDelayProperty, value); } private static void IsOpenChanged(AvaloniaPropertyChangedEventArgs e) { var control = (Control)e.Sender; var newValue = (bool)e.NewValue; ToolTip toolTip; if (newValue) { var tip = GetTip(control); if (tip == null) return; toolTip = control.GetValue(ToolTipProperty); if (toolTip == null || (tip != toolTip && tip != toolTip.Content)) { toolTip?.Close(); toolTip = tip as ToolTip ?? new ToolTip { Content = tip }; control.SetValue(ToolTipProperty, toolTip); } toolTip.Open(control); } else { toolTip = control.GetValue(ToolTipProperty); toolTip?.Close(); } toolTip?.UpdatePseudoClasses(newValue); } private void Open(Control control) { Close(); _popup = OverlayPopupHost.CreatePopupHost(control, null); _popup.SetChild(this); ((ISetLogicalParent)_popup).SetParent(control); _popup.ConfigurePosition(control, GetPlacement(control), new Point(GetHorizontalOffset(control), GetVerticalOffset(control))); WindowManagerAddShadowHintChanged(_popup, false); _popup.Show(); } private void Close() { if (_popup != null) { _popup.SetChild(null); _popup.Dispose(); _popup = null; } } private void WindowManagerAddShadowHintChanged(IPopupHost host, bool hint) { if (host is PopupRoot pr) { pr.PlatformImpl.SetWindowManagerAddShadowHint(hint); } } private void UpdatePseudoClasses(bool newValue) { PseudoClasses.Set(":open", newValue); } } }