// Copyright (c) The Perspex Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Reactive.Linq; using System.Reactive.Subjects; using Perspex.Controls.Primitives; using Perspex.Input; using Perspex.Threading; namespace Perspex.Controls { /// /// A tooltip control. /// /// /// 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. /// public class ToolTip : ContentControl { /// /// Defines the ToolTip.Tip attached property. /// public static readonly AttachedProperty TipProperty = PerspexProperty.RegisterAttached("Tip"); /// /// The popup window used to display the active tooltip. /// private static PopupRoot s_popup; /// /// The control that the currently visible tooltip is attached to. /// private static Control s_current; /// /// Observable fired when a tooltip should be displayed for a control. The output from this /// observable is throttled and calls when the time /// period expires. /// private static readonly Subject s_show = new Subject(); /// /// Initializes static members of the class. /// static ToolTip() { TipProperty.Changed.Subscribe(TipChanged); s_show.Throttle(TimeSpan.FromSeconds(0.5), PerspexScheduler.Instance).Subscribe(ShowToolTip); } /// /// 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); } /// /// called when the property changes on a control. /// /// The event args. private static void TipChanged(PerspexPropertyChangedEventArgs 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; } } /// /// Shows a tooltip for the specified control. /// /// The control. private static void ShowToolTip(Control control) { if (control != null) { if (s_popup == null) { s_popup = new PopupRoot { Content = new ToolTip(), }; ((ISetLogicalParent)s_popup).SetParent(control); } var cp = MouseDevice.Instance?.GetPosition(control); var position = control.PointToScreen(cp ?? new Point(0, 0)) + new Vector(0, 22); ((ToolTip)s_popup.Content).Content = GetTip(control); s_popup.Position = position; s_popup.Show(); s_current = control; } } /// /// Called when the pointer enters a control with an attached tooltip. /// /// The event sender. /// The event args. private static void ControlPointerEnter(object sender, PointerEventArgs e) { s_current = (Control)sender; s_show.OnNext(s_current); } /// /// Called when the pointer leaves a control with an attached tooltip. /// /// The event sender. /// The event args. private static void ControlPointerLeave(object sender, PointerEventArgs e) { var control = (Control)sender; if (control == s_current) { if (s_popup != null && s_popup.IsVisible) { s_popup.Hide(); } s_show.OnNext(null); } } } }