From 1d8f4714ffafd41914602488443ae1c2b0bdea08 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 7 May 2015 17:45:23 +0200 Subject: [PATCH] Added basic tooltip control. Can be attached to any control using an attached property. --- Perspex.Controls/Perspex.Controls.csproj | 1 + Perspex.Controls/ToolTip.cs | 102 ++++++++++++++++++ Perspex.Input/MouseDevice.cs | 4 +- Perspex.Themes.Default/DefaultTheme.cs | 1 + .../Perspex.Themes.Default.csproj | 1 + Perspex.Themes.Default/ToolTipStyle.cs | 51 +++++++++ TestApplication/Program.cs | 1 + .../Perspex.Win32/Input/WindowsMouseDevice.cs | 14 --- 8 files changed, 158 insertions(+), 17 deletions(-) create mode 100644 Perspex.Controls/ToolTip.cs create mode 100644 Perspex.Themes.Default/ToolTipStyle.cs diff --git a/Perspex.Controls/Perspex.Controls.csproj b/Perspex.Controls/Perspex.Controls.csproj index 5d967ab63e..5c5279b854 100644 --- a/Perspex.Controls/Perspex.Controls.csproj +++ b/Perspex.Controls/Perspex.Controls.csproj @@ -95,6 +95,7 @@ + diff --git a/Perspex.Controls/ToolTip.cs b/Perspex.Controls/ToolTip.cs new file mode 100644 index 0000000000..7114340492 --- /dev/null +++ b/Perspex.Controls/ToolTip.cs @@ -0,0 +1,102 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Controls +{ + using System; + using System.Reactive; + using System.Reactive.Linq; + using System.Reactive.Subjects; + using Perspex.Input; + using Perspex.Threading; + using Perspex.VisualTree; + + public class ToolTip : ContentControl + { + public static readonly PerspexProperty TipProperty = + PerspexProperty.RegisterAttached("Tip"); + + private static PopupRoot popup; + + private static Control current; + + private static Subject show = new Subject(); + + static ToolTip() + { + TipProperty.Changed.Subscribe(TipChanged); + show.Throttle(TimeSpan.FromSeconds(0.5), PerspexScheduler.Instance).Subscribe(ShowToolTip); + } + + public static object GetTip(Control element) + { + return element.GetValue(TipProperty); + } + + public static void SetTip(Control element, object value) + { + element.SetValue(TipProperty, value); + } + + 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; + } + } + + private static void ShowToolTip(Control control) + { + if (control != null) + { + if (popup == null) + { + popup = new PopupRoot + { + Content = new ToolTip(), + }; + } + + var cp = MouseDevice.Instance.GetPosition(control); + var position = control.PointToScreen(cp) + new Vector(0, 22); + + popup.Parent = control; + ((ToolTip)popup.Content).Content = GetTip(control); + popup.SetPosition(position); + popup.Show(); + + current = control; + } + } + + private static void ControlPointerEnter(object sender, PointerEventArgs e) + { + var control = (Control)sender; + show.OnNext(control); + } + + private static void ControlPointerLeave(object sender, PointerEventArgs e) + { + var control = (Control)sender; + + if (control == current) + { + popup.Hide(); + show.OnNext(null); + } + } + } +} diff --git a/Perspex.Input/MouseDevice.cs b/Perspex.Input/MouseDevice.cs index 2be4ad6796..e1102598a3 100644 --- a/Perspex.Input/MouseDevice.cs +++ b/Perspex.Input/MouseDevice.cs @@ -63,7 +63,7 @@ namespace Perspex.Input public Point GetPosition(IVisual relativeTo) { - Point p = this.GetClientPosition(); + Point p = this.Position; IVisual v = relativeTo; while (v != null) @@ -75,8 +75,6 @@ namespace Perspex.Input return p; } - protected abstract Point GetClientPosition(); - private void ProcessRawEvent(RawMouseEventArgs e) { var mouse = (IMouseDevice)e.Device; diff --git a/Perspex.Themes.Default/DefaultTheme.cs b/Perspex.Themes.Default/DefaultTheme.cs index 2cc162b1de..717902161b 100644 --- a/Perspex.Themes.Default/DefaultTheme.cs +++ b/Perspex.Themes.Default/DefaultTheme.cs @@ -32,6 +32,7 @@ namespace Perspex.Themes.Default this.Add(new TabStripStyle()); this.Add(new TextBoxStyle()); this.Add(new ToggleButtonStyle()); + this.Add(new ToolTipStyle()); this.Add(new TreeViewStyle()); this.Add(new TreeViewItemStyle()); this.Add(new WindowStyle()); diff --git a/Perspex.Themes.Default/Perspex.Themes.Default.csproj b/Perspex.Themes.Default/Perspex.Themes.Default.csproj index 0d2958b180..73837ab3b3 100644 --- a/Perspex.Themes.Default/Perspex.Themes.Default.csproj +++ b/Perspex.Themes.Default/Perspex.Themes.Default.csproj @@ -71,6 +71,7 @@ + diff --git a/Perspex.Themes.Default/ToolTipStyle.cs b/Perspex.Themes.Default/ToolTipStyle.cs new file mode 100644 index 0000000000..bc27fdd72c --- /dev/null +++ b/Perspex.Themes.Default/ToolTipStyle.cs @@ -0,0 +1,51 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Themes.Default +{ + using Perspex.Controls; + using Perspex.Controls.Presenters; + using Perspex.Media; + using Perspex.Styling; + using System.Linq; + + public class ToolTipStyle : Styles + { + public ToolTipStyle() + { + this.AddRange(new[] + { + new Style(x => x.OfType()) + { + Setters = new[] + { + new Setter(ToolTip.TemplateProperty, ControlTemplate.Create(this.Template)), + new Setter(ToolTip.BackgroundProperty, new SolidColorBrush(0xffffffe1)), + new Setter(ToolTip.BorderBrushProperty, Brushes.Gray), + new Setter(ToolTip.BorderThicknessProperty, 1.0), + new Setter(ToolTip.PaddingProperty, new Thickness(4, 2)), + }, + }, + }); + } + + private Control Template(ToolTip control) + { + return new Border + { + [~Border.BackgroundProperty] = control[~ToolTip.BackgroundProperty], + [~Border.BorderBrushProperty] = control[~ToolTip.BorderBrushProperty], + [~Border.BorderThicknessProperty] = control[~ToolTip.BorderThicknessProperty], + [~Border.PaddingProperty] = control[~ToolTip.PaddingProperty], + Content = new ContentPresenter + { + Name = "contentPresenter", + [~ContentPresenter.ContentProperty] = control[~ToolTip.ContentProperty], + } + }; + } + } +} diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs index ccc6a2423f..3613d6e77f 100644 --- a/TestApplication/Program.cs +++ b/TestApplication/Program.cs @@ -214,6 +214,7 @@ namespace TestApplication { Content = "Button", Command = showDialog, + [ToolTip.TipProperty] = "Hello World!", }), new Button { diff --git a/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs b/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs index 9cc918bbc0..70efa8d4de 100644 --- a/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs +++ b/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs @@ -26,12 +26,6 @@ namespace Perspex.Win32.Input set; } - public new Point Position - { - get { return base.Position; } - internal set { base.Position = value; } - } - public override void Capture(IInputElement control) { base.Capture(control); @@ -45,13 +39,5 @@ namespace Perspex.Win32.Input UnmanagedMethods.ReleaseCapture(); } } - - protected override Point GetClientPosition() - { - UnmanagedMethods.POINT p; - UnmanagedMethods.GetCursorPos(out p); - UnmanagedMethods.ScreenToClient(this.CurrentWindow.Handle.Handle, ref p); - return new Point(p.X, p.Y); - } } } \ No newline at end of file