mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-08 12:18:50 +02:00
[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.
This commit is contained in:
@@ -10,7 +10,6 @@ using System.Windows;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Windows.Interop;
|
using System.Windows.Interop;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using Point = System.Windows.Point;
|
|
||||||
|
|
||||||
namespace PowerLauncher.Helper
|
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);
|
_ = NativeMethods.SetWindowLong(hwnd, GWL_EX_STYLE, NativeMethods.GetWindowLong(hwnd, GWL_EX_STYLE) | WS_EX_TOOLWINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static void MoveToScreenCenter(Window window, Screen screen)
|
||||||
/// Transforms pixels to Device Independent Pixels used by WPF
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="visual">current window, required to get presentation source</param>
|
|
||||||
/// <param name="unitX">horizontal position in pixels</param>
|
|
||||||
/// <param name="unitY">vertical position in pixels</param>
|
|
||||||
/// <returns>point containing device independent pixels</returns>
|
|
||||||
public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY)
|
|
||||||
{
|
{
|
||||||
Matrix matrix;
|
var workingArea = screen.WorkingArea;
|
||||||
var source = PresentationSource.FromVisual(visual);
|
var matrix = GetCompositionTarget(window).TransformFromDevice;
|
||||||
if (source != null)
|
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
|
else
|
||||||
{
|
{
|
||||||
using (var src = new HwndSource(default))
|
using var hwndSource = new HwndSource(default);
|
||||||
{
|
return hwndSource.CompositionTarget;
|
||||||
matrix = src.CompositionTarget.TransformFromDevice;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
|||||||
@@ -411,8 +411,8 @@ namespace PowerLauncher
|
|||||||
|
|
||||||
private void InitializePosition()
|
private void InitializePosition()
|
||||||
{
|
{
|
||||||
Top = WindowTop();
|
MoveToDesiredPosition();
|
||||||
Left = WindowLeft();
|
|
||||||
_settings.WindowTop = Top;
|
_settings.WindowTop = Top;
|
||||||
_settings.WindowLeft = Left;
|
_settings.WindowLeft = Left;
|
||||||
}
|
}
|
||||||
@@ -434,11 +434,31 @@ namespace PowerLauncher
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Top = WindowTop();
|
MoveToDesiredPosition();
|
||||||
Left = WindowLeft();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
private void OnLocationChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_settings.RememberLastLaunchLocation)
|
if (_settings.RememberLastLaunchLocation)
|
||||||
@@ -448,28 +468,6 @@ namespace PowerLauncher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates X co-ordinate of main window top left corner.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>X co-ordinate of main window top left corner</returns>
|
|
||||||
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()
|
private Screen GetScreen()
|
||||||
{
|
{
|
||||||
ManagedCommon.StartupPosition position = _settings.StartupPosition;
|
ManagedCommon.StartupPosition position = _settings.StartupPosition;
|
||||||
|
|||||||
Reference in New Issue
Block a user