// 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);
}
}
}
}