From 366eea50997c65bf7b2a8880455257a02ef42ad9 Mon Sep 17 00:00:00 2001 From: Curtis Wensley Date: Wed, 3 Apr 2024 15:25:26 -0700 Subject: [PATCH] Add WpfHelpers.ToEtoScreen/ToNativeScreen helper methods --- src/Eto.Mac/SDConversions.cs | 5 + src/Eto.WinForms/Win32.dpi.cs | 18 ++-- src/Eto.WinForms/WinConversions.shared.cs | 70 ------------- src/Eto.Wpf/WpfHelpers.cs | 28 ++++++ .../UnitTests/ScreenToClientTests.cs | 99 +++++++++++++++++++ 5 files changed, 141 insertions(+), 79 deletions(-) mode change 100644 => 100755 src/Eto.Wpf/WpfHelpers.cs create mode 100755 test/Eto.Test.Wpf/UnitTests/ScreenToClientTests.cs diff --git a/src/Eto.Mac/SDConversions.cs b/src/Eto.Mac/SDConversions.cs index 1cd213ced4..8f6380deef 100644 --- a/src/Eto.Mac/SDConversions.cs +++ b/src/Eto.Mac/SDConversions.cs @@ -99,6 +99,11 @@ public static sd.PointF ToSD (this PointF point) return new sd.PointF (point.X, point.Y); } + public static sd.Point ToSDPoint (this PointF point) + { + return new sd.Point ((int)point.X, (int)point.Y); + } + internal static sd.PointF[] ToSD (this PointF[] points) { var result = new sd.PointF[points.Length]; diff --git a/src/Eto.WinForms/Win32.dpi.cs b/src/Eto.WinForms/Win32.dpi.cs index 679af4b3a1..7f6427e882 100644 --- a/src/Eto.WinForms/Win32.dpi.cs +++ b/src/Eto.WinForms/Win32.dpi.cs @@ -59,12 +59,12 @@ public static Eto.Drawing.Point LogicalToScreen(this Eto.Drawing.PointF point, E { screen = screen ?? Eto.Forms.Screen.FromPoint(point); var sdscreen = ScreenHandler.GetControl(screen); + var location = sdscreen.GetLogicalLocation(); var pixelSize = sdscreen.GetLogicalPixelSize(usePerMonitor); - var location = sdscreen.GetBounds().Location; - var screenBounds = screen.Bounds; + var sdscreenBounds = usePerMonitor ? sdscreen.GetBounds() : sdscreen.Bounds.ToEto(); - var x = location.X + (point.X - screenBounds.X) * pixelSize; - var y = location.Y + (point.Y - screenBounds.Y) * pixelSize; + var x = sdscreenBounds.X + (point.X - location.X) * pixelSize; + var y = sdscreenBounds.Y + (point.Y - location.Y) * pixelSize; return Drawing.Point.Round(new Drawing.PointF(x, y)); } @@ -76,10 +76,10 @@ public static Eto.Drawing.PointF ScreenToLogical(this Eto.Drawing.Point point, s public static Eto.Drawing.PointF ScreenToLogical(this sd.Point point, swf.Screen sdscreen = null, bool usePerMonitor = true) { - sdscreen = sdscreen ?? swf.Screen.FromPoint(point); + sdscreen ??= swf.Screen.FromPoint(point); var location = sdscreen.GetLogicalLocation(); var pixelSize = sdscreen.GetLogicalPixelSize(usePerMonitor); - var sdscreenBounds = sdscreen.GetBounds(); + var sdscreenBounds = usePerMonitor ? sdscreen.GetBounds() : sdscreen.Bounds.ToEto(); var x = location.X + (point.X - sdscreenBounds.X) / pixelSize; var y = location.Y + (point.Y - sdscreenBounds.Y) / pixelSize; @@ -93,10 +93,10 @@ public static Eto.Drawing.RectangleF ScreenToLogical(this Eto.Drawing.Rectangle sdscreen = sdscreen ?? swf.Screen.FromPoint(rect.Location.ToSD()); var location = sdscreen.GetLogicalLocation(); var pixelSize = sdscreen.GetLogicalPixelSize(usePerMonitor); - var screenBounds = sdscreen.GetBounds(); + var sdscreenBounds = usePerMonitor ? sdscreen.GetBounds() : sdscreen.Bounds.ToEto(); return new Eto.Drawing.RectangleF( - location.X + (rect.X - screenBounds.X) / pixelSize, - location.Y + (rect.Y - screenBounds.Y) / pixelSize, + location.X + (rect.X - sdscreenBounds.X) / pixelSize, + location.Y + (rect.Y - sdscreenBounds.Y) / pixelSize, rect.Width / pixelSize, rect.Height / pixelSize ); diff --git a/src/Eto.WinForms/WinConversions.shared.cs b/src/Eto.WinForms/WinConversions.shared.cs index ce67ee6f64..7434b1968e 100644 --- a/src/Eto.WinForms/WinConversions.shared.cs +++ b/src/Eto.WinForms/WinConversions.shared.cs @@ -59,76 +59,6 @@ public static sd.Color ToSD(this Color color) return sd.Color.FromArgb((byte)(color.A * 255), (byte)(color.R * 255), (byte)(color.G * 255), (byte)(color.B * 255)); } - public static Point ToEto(this sd.Point point) - { - return new Point(point.X, point.Y); - } - - public static PointF ToEto(this sd.PointF point) - { - return new PointF(point.X, point.Y); - } - - public static sd.PointF ToSD(this PointF point) - { - return new sd.PointF(point.X, point.Y); - } - - public static sd.Point ToSDPoint(this PointF point) - { - return new sd.Point((int)point.X, (int)point.Y); - } - - public static Size ToEto(this sd.Size size) - { - return new Size(size.Width, size.Height); - } - - public static sd.Size ToSD(this Size size) - { - return new sd.Size(size.Width, size.Height); - } - - public static Size ToEtoF(this sd.SizeF size) - { - return new Size((int)size.Width, (int)size.Height); - } - - public static SizeF ToEto(this sd.SizeF size) - { - return new SizeF(size.Width, size.Height); - } - - public static sd.SizeF ToSD(this SizeF size) - { - return new sd.SizeF(size.Width, size.Height); - } - - public static Rectangle ToEto(this sd.Rectangle rect) - { - return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height); - } - - public static RectangleF ToEto(this sd.RectangleF rect) - { - return new RectangleF(rect.X, rect.Y, rect.Width, rect.Height); - } - - public static sd.Rectangle ToSD(this Rectangle rect) - { - return new sd.Rectangle(rect.X, rect.Y, rect.Width, rect.Height); - } - - public static sd.RectangleF ToSD(this RectangleF rect) - { - return new sd.RectangleF(rect.X, rect.Y, rect.Width, rect.Height); - } - - public static sd.Rectangle ToSDRectangle(this RectangleF rect) - { - return new sd.Rectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height); - } - internal static sd.Point[] ToSD(this Point[] points) { var result = diff --git a/src/Eto.Wpf/WpfHelpers.cs b/src/Eto.Wpf/WpfHelpers.cs old mode 100644 new mode 100755 index f206f24050..9d3c39842a --- a/src/Eto.Wpf/WpfHelpers.cs +++ b/src/Eto.Wpf/WpfHelpers.cs @@ -83,5 +83,33 @@ public static Window ToEtoWindow(IntPtr windowHandle) return null; return new Form(new HwndFormHandler(windowHandle)); } + + /// + /// Converts a System.Drawing.Point in screen coordinates to an Eto point in screen coordinates. + /// + /// A point in Windows Forms/win32 screen coordinates. + /// A point in Eto screen coordinates. + public static PointF ToEtoScreen(this sd.Point point, swf.Screen sdscreen = null, bool perMonitor = false) => Win32.ScreenToLogical(point, sdscreen, perMonitor); + + public static RectangleF ToEtoScreen(this sd.Rectangle rect, swf.Screen sdscreen = null, bool perMonitor = false) + { + var topLeft = ToEtoScreen(rect.Location, sdscreen, perMonitor); + var bottomRight = ToEtoScreen(new sd.Point(rect.X + rect.Width, rect.Y + rect.Height), sdscreen, perMonitor); + return new RectangleF(topLeft, new SizeF(bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y)); + } + + /// + /// Converts a point in Eto screen coordinates to a point in Windows Forms/win32 screen coordinates. + /// + /// A point in Eto screen coordinates. + /// A point in Windows Forms/win32 screen coordinates. + public static Point ToNativeScreen(this PointF point, Screen screen = null, bool perMonitor = false) => Win32.LogicalToScreen(point, screen, perMonitor); + public static Rectangle ToNativeScreen(this RectangleF rect, Screen screen = null, bool perMonitor = false) + { + var topLeft = rect.TopLeft.ToNativeScreen(screen, perMonitor); + var bottomRight = rect.BottomRight.ToNativeScreen(screen, perMonitor); + return new Rectangle(topLeft, new Size(bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y)); + } + } } \ No newline at end of file diff --git a/test/Eto.Test.Wpf/UnitTests/ScreenToClientTests.cs b/test/Eto.Test.Wpf/UnitTests/ScreenToClientTests.cs new file mode 100755 index 0000000000..d191edf770 --- /dev/null +++ b/test/Eto.Test.Wpf/UnitTests/ScreenToClientTests.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Eto.Test.UnitTests; +using NUnit.Framework; +using swf = System.Windows.Forms; +using sd = System.Drawing; + +namespace Eto.Test.Wpf.UnitTests +{ + [TestFixture] + public class ScreenToClientTests : TestBase + { + + class NonActivatingForm : swf.Form + { + protected override bool ShowWithoutActivation => true; + } + + [Test, ManualTest] + public void ScreenLocationsShouldBeCorrect() + { + var mre = new ManualResetEvent(false); + Invoke(() => + { + var wfwindows = new List(); + var windows = new List(); + + void CreateWindow(Rectangle rect) + { + var window = new Form + { + ShowActivated = false, + ShowInTaskbar = false, + Minimizable = false, + Maximizable = false, + Bounds = rect, + Content = TableLayout.AutoSized("Click to dismiss", centered: true) + }; + window.MouseDown += (sender, e) => + { + foreach (var w in windows) + { + w.Close(); + } + foreach (var w in wfwindows) + { + w.Close(); + } + mre.Set(); + }; + window.Show(); + windows.Add(window); + + bool perMonitor = true; + + var sdrect = WpfHelpers.ToNativeScreen(rect, window.Screen, perMonitor: perMonitor).ToSD(); + + var wfwindow = new NonActivatingForm + { + Bounds = sdrect, + ShowInTaskbar = false, + MinimizeBox = false, + MaximizeBox = false, + StartPosition = swf.FormStartPosition.Manual, + BackColor = sd.Color.Blue + }; + wfwindow.Controls.Add(new swf.Label { Text = "Move me", Dock = swf.DockStyle.Fill, ForeColor = sd.Color.White }); + wfwindow.LocationChanged += (sender, e) => + { + var loc = wfwindow.RectangleToScreen(wfwindow.ClientRectangle); + window.Bounds = (Rectangle)WpfHelpers.ToEtoScreen(loc, swf.Screen.FromControl(wfwindow), perMonitor: perMonitor); + }; + wfwindow.SizeChanged += (sender, e) => + { + var loc = wfwindow.RectangleToScreen(wfwindow.ClientRectangle); + window.Bounds = (Rectangle)WpfHelpers.ToEtoScreen(loc, swf.Screen.FromControl(wfwindow), perMonitor: perMonitor); + }; + wfwindow.Show(); + wfwindows.Add(wfwindow); + + } + + foreach (var screen in Screen.Screens) + { + var bounds = (Rectangle)screen.WorkingArea; + var size = new Size(200, 200); + CreateWindow(new Rectangle(bounds.TopLeft, size)); + CreateWindow(new Rectangle(bounds.TopRight - new Size(size.Width, 0), size)); + CreateWindow(new Rectangle(bounds.BottomLeft - new Size(0, size.Height), size)); + CreateWindow(new Rectangle(bounds.BottomRight - size, size)); + } + }); + mre.WaitOne(); + } + + } +} \ No newline at end of file