From 78d53ffb105108c9b6bcdde84dd1ef5272f62197 Mon Sep 17 00:00:00 2001
From: Ani <115020168+drawbyperpetual@users.noreply.github.com>
Date: Thu, 18 Jul 2024 16:16:19 +0200
Subject: [PATCH] [PTRun]Fixed unstable startup position after moving to
PerMonitorV2 (#33784)
## Summary of the Pull Request
Fixes an issue where PowerToys Run can sometimes start up in an
inconvenient position in a multi-monitor / multi-DPI setup.
---
.../Helper/WindowsInteropHelper.cs | 36 +++++++------
.../launcher/PowerLauncher/MainWindow.xaml.cs | 50 +++++++++----------
2 files changed, 41 insertions(+), 45 deletions(-)
diff --git a/src/modules/launcher/PowerLauncher/Helper/WindowsInteropHelper.cs b/src/modules/launcher/PowerLauncher/Helper/WindowsInteropHelper.cs
index 612476daff..1832bbbb8b 100644
--- a/src/modules/launcher/PowerLauncher/Helper/WindowsInteropHelper.cs
+++ b/src/modules/launcher/PowerLauncher/Helper/WindowsInteropHelper.cs
@@ -10,7 +10,6 @@ using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;
using System.Windows.Media;
-using Point = System.Windows.Point;
namespace PowerLauncher.Helper
{
@@ -188,30 +187,29 @@ namespace PowerLauncher.Helper
_ = NativeMethods.SetWindowLong(hwnd, GWL_EX_STYLE, NativeMethods.GetWindowLong(hwnd, GWL_EX_STYLE) | WS_EX_TOOLWINDOW);
}
- ///
- /// Transforms pixels to Device Independent Pixels used by WPF
- ///
- /// current window, required to get presentation source
- /// horizontal position in pixels
- /// vertical position in pixels
- /// point containing device independent pixels
- public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY)
+ public static void MoveToScreenCenter(Window window, Screen screen)
{
- Matrix matrix;
- var source = PresentationSource.FromVisual(visual);
- if (source != null)
+ var workingArea = screen.WorkingArea;
+ var matrix = GetCompositionTarget(window).TransformFromDevice;
+ var dpiX = matrix.M11;
+ var dpiY = matrix.M22;
+
+ window.Left = (dpiX * workingArea.Left) + (((dpiX * workingArea.Width) - window.Width) / 2);
+ window.Top = (dpiY * workingArea.Top) + (((dpiY * workingArea.Height) - window.Height) / 2);
+ }
+
+ private static CompositionTarget GetCompositionTarget(Visual visual)
+ {
+ var presentationSource = PresentationSource.FromVisual(visual);
+ if (presentationSource != null)
{
- matrix = source.CompositionTarget.TransformFromDevice;
+ return presentationSource.CompositionTarget;
}
else
{
- using (var src = new HwndSource(default))
- {
- matrix = src.CompositionTarget.TransformFromDevice;
- }
+ using var hwndSource = new HwndSource(default);
+ return hwndSource.CompositionTarget;
}
-
- return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
}
[StructLayout(LayoutKind.Sequential)]
diff --git a/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs b/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs
index 2fa2699953..600af8dc6d 100644
--- a/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs
+++ b/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs
@@ -411,8 +411,8 @@ namespace PowerLauncher
private void InitializePosition()
{
- Top = WindowTop();
- Left = WindowLeft();
+ MoveToDesiredPosition();
+
_settings.WindowTop = Top;
_settings.WindowLeft = Left;
}
@@ -434,11 +434,31 @@ namespace PowerLauncher
}
else
{
- Top = WindowTop();
- Left = WindowLeft();
+ MoveToDesiredPosition();
}
}
+ private void MoveToDesiredPosition()
+ {
+ // Hack: After switching to PerMonitorV2, this operation seems to require a three-step operation
+ // to ensure a stable position: First move to top-left of desired screen, then centralize twice.
+ // More straightforward ways of doing this don't seem to work well for unclear reasons, but possibly related to
+ // https://github.com/dotnet/wpf/issues/4127
+ // In any case, there does not seem to be any big practical downside to doing it this way. As a bonus, it can be
+ // done in pure WPF without any native calls and without too much DPI-based fiddling.
+ // In terms of the hack itself, removing any of these three steps seems to fail in certain scenarios only,
+ // so be careful with testing!
+ var desiredScreen = GetScreen();
+
+ // Move to top-left of desired screen.
+ Top = desiredScreen.WorkingArea.Top;
+ Left = desiredScreen.WorkingArea.Left;
+
+ // Centralize twice.
+ WindowsInteropHelper.MoveToScreenCenter(this, desiredScreen);
+ WindowsInteropHelper.MoveToScreenCenter(this, desiredScreen);
+ }
+
private void OnLocationChanged(object sender, EventArgs e)
{
if (_settings.RememberLastLaunchLocation)
@@ -448,28 +468,6 @@ namespace PowerLauncher
}
}
- ///
- /// Calculates X co-ordinate of main window top left corner.
- ///
- /// X co-ordinate of main window top left corner
- private double WindowLeft()
- {
- var screen = GetScreen();
- var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
- var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
- var left = ((dip2.X - ActualWidth) / 2) + dip1.X;
- return left;
- }
-
- private double WindowTop()
- {
- var screen = GetScreen();
- var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
- var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
- var top = ((dip2.Y - SearchBox.ActualHeight) / 4) + dip1.Y;
- return top;
- }
-
private Screen GetScreen()
{
ManagedCommon.StartupPosition position = _settings.StartupPosition;