From 1b1d60d0c0d9b286d1bb070911c65aba5225df0b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 23 Jan 2015 03:06:30 +0100 Subject: [PATCH] Make popup appear in right place. --- Perspex.Controls.UnitTests/ControlTests.cs | 5 +++++ Perspex.Controls.UnitTests/TestRoot.cs | 5 +++++ Perspex.Controls/Platform/IPopupImpl.cs | 2 ++ Perspex.Controls/Platform/ITopLevelImpl.cs | 2 ++ Perspex.Controls/Popup.cs | 22 +++++++++++++++++++++ Perspex.Controls/PopupRoot.cs | 5 +++++ Perspex.Controls/TopLevel.cs | 7 ++++++- Perspex.SceneGraph.UnitTests/TestRoot.cs | 5 +++++ Perspex.SceneGraph/Point.cs | 10 ++++++++++ Perspex.SceneGraph/Rendering/IRenderRoot.cs | 2 ++ Perspex.SceneGraph/Visual.cs | 20 +++++++++++++++---- Perspex.Themes.Default/DropDownStyle.cs | 1 + Windows/Perspex.Win32/PopupImpl.cs | 12 +++++++++++ Windows/Perspex.Win32/WindowImpl.cs | 7 +++++++ 14 files changed, 100 insertions(+), 5 deletions(-) diff --git a/Perspex.Controls.UnitTests/ControlTests.cs b/Perspex.Controls.UnitTests/ControlTests.cs index 8262033309..386595379f 100644 --- a/Perspex.Controls.UnitTests/ControlTests.cs +++ b/Perspex.Controls.UnitTests/ControlTests.cs @@ -92,6 +92,11 @@ namespace Perspex.Controls.UnitTests { get { throw new NotImplementedException(); } } + + public Point TranslatePointToScreen(Point p) + { + throw new NotImplementedException(); + } } } } diff --git a/Perspex.Controls.UnitTests/TestRoot.cs b/Perspex.Controls.UnitTests/TestRoot.cs index 7859213b08..e53fb1f716 100644 --- a/Perspex.Controls.UnitTests/TestRoot.cs +++ b/Perspex.Controls.UnitTests/TestRoot.cs @@ -33,5 +33,10 @@ namespace Perspex.Controls.UnitTests { get { throw new NotImplementedException(); } } + + public Point TranslatePointToScreen(Point p) + { + throw new NotImplementedException(); + } } } diff --git a/Perspex.Controls/Platform/IPopupImpl.cs b/Perspex.Controls/Platform/IPopupImpl.cs index f25472063e..f5bfaf75f7 100644 --- a/Perspex.Controls/Platform/IPopupImpl.cs +++ b/Perspex.Controls/Platform/IPopupImpl.cs @@ -12,6 +12,8 @@ namespace Perspex.Platform public interface IPopupImpl : ITopLevelImpl { + void SetPosition(Point p); + void Show(); void Hide(); diff --git a/Perspex.Controls/Platform/ITopLevelImpl.cs b/Perspex.Controls/Platform/ITopLevelImpl.cs index 782967dea8..c0737c03c3 100644 --- a/Perspex.Controls/Platform/ITopLevelImpl.cs +++ b/Perspex.Controls/Platform/ITopLevelImpl.cs @@ -30,5 +30,7 @@ namespace Perspex.Platform void Invalidate(Rect rect); void SetOwner(TopLevel owner); + + Point PointToScreen(Point point); } } diff --git a/Perspex.Controls/Popup.cs b/Perspex.Controls/Popup.cs index d7488bc16b..1a8fba46b5 100644 --- a/Perspex.Controls/Popup.cs +++ b/Perspex.Controls/Popup.cs @@ -18,6 +18,9 @@ namespace Perspex.Controls public static readonly PerspexProperty IsOpenProperty = PerspexProperty.Register("IsOpen"); + public static readonly PerspexProperty PlacementTargetProperty = + PerspexProperty.Register("PlacementTarget"); + private PopupRoot root; private Window window; @@ -54,6 +57,12 @@ namespace Perspex.Controls set { this.SetValue(IsOpenProperty, value); } } + public Control PlacementTarget + { + get { return this.GetValue(PlacementTargetProperty); } + set { this.SetValue(PlacementTargetProperty, value); } + } + public void Open() { if (this.root == null) @@ -62,6 +71,7 @@ namespace Perspex.Controls this.root[~PopupRoot.ContentProperty] = this[~ChildProperty]; } + this.root.SetPosition(this.GetPosition()); this.root.Show(); } @@ -84,5 +94,17 @@ namespace Perspex.Controls base.OnDetachedFromVisualTree(oldRoot); this.window = null; } + + private Point GetPosition() + { + if (this.PlacementTarget != null) + { + return this.PlacementTarget.PointToScreen(new Point(0, this.PlacementTarget.ActualSize.Height)); + } + else + { + return new Point(); + } + } } } diff --git a/Perspex.Controls/PopupRoot.cs b/Perspex.Controls/PopupRoot.cs index e55a6ec405..ed8b051f32 100644 --- a/Perspex.Controls/PopupRoot.cs +++ b/Perspex.Controls/PopupRoot.cs @@ -27,6 +27,11 @@ namespace Perspex.Controls get { return (IPopupImpl)base.PlatformImpl; } } + public void SetPosition(Point p) + { + this.PlatformImpl.SetPosition(p); + } + public void Hide() { this.PlatformImpl.Hide(); diff --git a/Perspex.Controls/TopLevel.cs b/Perspex.Controls/TopLevel.cs index b8de32aa83..5772109a50 100644 --- a/Perspex.Controls/TopLevel.cs +++ b/Perspex.Controls/TopLevel.cs @@ -131,7 +131,12 @@ namespace Perspex.Controls { get { return this.renderManager; } } - + + Point IRenderRoot.TranslatePointToScreen(Point p) + { + return this.PlatformImpl.PointToScreen(p); + } + protected IDisposable BeginAutoSizing() { this.autoSizing = true; diff --git a/Perspex.SceneGraph.UnitTests/TestRoot.cs b/Perspex.SceneGraph.UnitTests/TestRoot.cs index 6bae5c7352..fda78ccd42 100644 --- a/Perspex.SceneGraph.UnitTests/TestRoot.cs +++ b/Perspex.SceneGraph.UnitTests/TestRoot.cs @@ -20,5 +20,10 @@ namespace Perspex.SceneGraph.UnitTests { get { throw new NotImplementedException(); } } + + public Point TranslatePointToScreen(Point p) + { + throw new NotImplementedException(); + } } } diff --git a/Perspex.SceneGraph/Point.cs b/Perspex.SceneGraph/Point.cs index 1a60774440..af080d86c6 100644 --- a/Perspex.SceneGraph/Point.cs +++ b/Perspex.SceneGraph/Point.cs @@ -77,11 +77,21 @@ namespace Perspex return new Point(a.x + b.x, a.y + b.y); } + public static Point operator +(Point a, Vector b) + { + return new Point(a.x + b.X, a.y + b.Y); + } + public static Point operator -(Point a, Point b) { return new Point(a.x - b.x, a.y - b.y); } + public static Point operator -(Point a, Vector b) + { + return new Point(a.x - b.X, a.y - b.Y); + } + public static Point operator *(Point point, Matrix matrix) { return new Point( diff --git a/Perspex.SceneGraph/Rendering/IRenderRoot.cs b/Perspex.SceneGraph/Rendering/IRenderRoot.cs index 9edd3afa34..aff19286e1 100644 --- a/Perspex.SceneGraph/Rendering/IRenderRoot.cs +++ b/Perspex.SceneGraph/Rendering/IRenderRoot.cs @@ -13,5 +13,7 @@ namespace Perspex.Rendering IRenderer Renderer { get; } IRenderManager RenderManager { get; } + + Point TranslatePointToScreen(Point p); } } diff --git a/Perspex.SceneGraph/Visual.cs b/Perspex.SceneGraph/Visual.cs index 5d9900e010..1ceeb78e59 100644 --- a/Perspex.SceneGraph/Visual.cs +++ b/Perspex.SceneGraph/Visual.cs @@ -14,6 +14,7 @@ namespace Perspex using Perspex.Animation; using Perspex.Collections; using Perspex.Media; + using Perspex.Platform; using Perspex.Rendering; using Perspex.VisualTree; using Splat; @@ -118,10 +119,16 @@ namespace Perspex Contract.Requires(context != null); } + public Point PointToScreen(Point point) + { + var p = GetOffsetFromRoot(this); + return p.Item1.TranslatePointToScreen(point + p.Item2); + } + public Matrix TransformToVisual(IVisual visual) { - var thisOffset = GetOffsetFromRoot(this); - var thatOffset = GetOffsetFromRoot(visual); + var thisOffset = GetOffsetFromRoot(this).Item2; + var thatOffset = GetOffsetFromRoot(visual).Item2; return Matrix.Translation(-thisOffset) * Matrix.Translation(thatOffset); } @@ -198,7 +205,7 @@ namespace Perspex } } - private static Vector GetOffsetFromRoot(IVisual v) + private static Tuple GetOffsetFromRoot(IVisual v) { var result = new Vector(); @@ -206,9 +213,14 @@ namespace Perspex { result = new Vector(result.X + v.Bounds.X, result.Y + v.Bounds.Y); v = v.VisualParent; + + if (v == null) + { + throw new InvalidOperationException("Control is not attached to visual tree."); + } } - return result; + return Tuple.Create((IRenderRoot)v, result); } private static void RenderTransformChanged(PerspexPropertyChangedEventArgs e) diff --git a/Perspex.Themes.Default/DropDownStyle.cs b/Perspex.Themes.Default/DropDownStyle.cs index cd9d5933a6..6b448759c2 100644 --- a/Perspex.Themes.Default/DropDownStyle.cs +++ b/Perspex.Themes.Default/DropDownStyle.cs @@ -82,6 +82,7 @@ namespace Perspex.Themes.Default new Popup { Child = new TextBlock { Text = "Hello World" }, + PlacementTarget = control, [~Popup.IsOpenProperty] = control[~DropDown.IsDropDownOpenProperty], } }, diff --git a/Windows/Perspex.Win32/PopupImpl.cs b/Windows/Perspex.Win32/PopupImpl.cs index e775bbc1d4..b5b1b8d93b 100644 --- a/Windows/Perspex.Win32/PopupImpl.cs +++ b/Windows/Perspex.Win32/PopupImpl.cs @@ -12,6 +12,18 @@ namespace Perspex.Win32 public class PopupImpl : WindowImpl, IPopupImpl { + public void SetPosition(Point p) + { + UnmanagedMethods.SetWindowPos( + this.Handle.Handle, + IntPtr.Zero, + (int)p.X, + (int)p.Y, + 0, + 0, + UnmanagedMethods.SetWindowPosFlags.SWP_NOSIZE); + } + protected override IntPtr CreateWindowOverride(ushort atom) { UnmanagedMethods.WindowStyles style = diff --git a/Windows/Perspex.Win32/WindowImpl.cs b/Windows/Perspex.Win32/WindowImpl.cs index c5fe26e194..2261d1a1d9 100644 --- a/Windows/Perspex.Win32/WindowImpl.cs +++ b/Windows/Perspex.Win32/WindowImpl.cs @@ -111,6 +111,13 @@ namespace Perspex.Win32 UnmanagedMethods.InvalidateRect(this.hwnd, ref r, false); } + public Point PointToScreen(Point point) + { + var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y }; + UnmanagedMethods.ClientToScreen(this.hwnd, ref p); + return new Point(p.X, p.Y); + } + public void SetOwner(TopLevel owner) { this.owner = owner;