diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/WindowPositionHelper.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/WindowPositionHelper.cs
new file mode 100644
index 0000000000..bf8af589a6
--- /dev/null
+++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/WindowPositionHelper.cs
@@ -0,0 +1,152 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI;
+using Microsoft.UI.Windowing;
+using Windows.Graphics;
+using Windows.Win32;
+using Windows.Win32.Graphics.Gdi;
+using Windows.Win32.UI.HiDpi;
+
+namespace Microsoft.CmdPal.UI.Helpers;
+
+internal static class WindowPositionHelper
+{
+ private const int DefaultWidth = 800;
+ private const int DefaultHeight = 480;
+ private const int MinimumVisibleSize = 100;
+ private const int DefaultDpi = 96;
+
+ public static PointInt32? CalculateCenteredPosition(DisplayArea? displayArea, SizeInt32 windowSize, int windowDpi)
+ {
+ if (displayArea is null)
+ {
+ return null;
+ }
+
+ var workArea = displayArea.WorkArea;
+ if (workArea.Width <= 0 || workArea.Height <= 0)
+ {
+ return null;
+ }
+
+ var targetDpi = GetDpiForDisplay(displayArea);
+ var predictedSize = ScaleSize(windowSize, windowDpi, targetDpi);
+
+ // Clamp to work area
+ var width = Math.Min(predictedSize.Width, workArea.Width);
+ var height = Math.Min(predictedSize.Height, workArea.Height);
+
+ return new PointInt32(
+ workArea.X + ((workArea.Width - width) / 2),
+ workArea.Y + ((workArea.Height - height) / 2));
+ }
+
+ ///
+ /// Adjusts a saved window rect to ensure it's visible on the nearest display,
+ /// accounting for DPI changes and work area differences.
+ ///
+ ///
+ public static RectInt32 AdjustRectForVisibility(RectInt32 savedRect, SizeInt32 savedScreenSize, int savedDpi)
+ {
+ var displayArea = DisplayArea.GetFromRect(savedRect, DisplayAreaFallback.Nearest);
+ if (displayArea is null)
+ {
+ return savedRect;
+ }
+
+ var workArea = displayArea.WorkArea;
+ if (workArea.Width <= 0 || workArea.Height <= 0)
+ {
+ return savedRect;
+ }
+
+ var targetDpi = GetDpiForDisplay(displayArea);
+ if (savedDpi <= 0)
+ {
+ savedDpi = targetDpi;
+ }
+
+ var hasInvalidSize = savedRect.Width <= 0 || savedRect.Height <= 0;
+ if (hasInvalidSize)
+ {
+ savedRect = savedRect with { Width = DefaultWidth, Height = DefaultHeight };
+ }
+
+ if (targetDpi != savedDpi)
+ {
+ savedRect = ScaleRect(savedRect, savedDpi, targetDpi);
+ }
+
+ var clampedSize = ClampSize(savedRect.Width, savedRect.Height, workArea);
+
+ var shouldRecenter = hasInvalidSize ||
+ IsOffscreen(savedRect, workArea) ||
+ savedScreenSize.Width != workArea.Width ||
+ savedScreenSize.Height != workArea.Height;
+
+ if (shouldRecenter)
+ {
+ return CenterRectInWorkArea(clampedSize, workArea);
+ }
+
+ return new RectInt32(savedRect.X, savedRect.Y, clampedSize.Width, clampedSize.Height);
+ }
+
+ private static int GetDpiForDisplay(DisplayArea displayArea)
+ {
+ var hMonitor = Win32Interop.GetMonitorFromDisplayId(displayArea.DisplayId);
+ if (hMonitor == IntPtr.Zero)
+ {
+ return DefaultDpi;
+ }
+
+ var hr = PInvoke.GetDpiForMonitor(
+ new HMONITOR(hMonitor),
+ MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
+ out var dpiX,
+ out _);
+
+ return hr.Succeeded && dpiX > 0 ? (int)dpiX : DefaultDpi;
+ }
+
+ private static SizeInt32 ScaleSize(SizeInt32 size, int fromDpi, int toDpi)
+ {
+ if (fromDpi <= 0 || toDpi <= 0 || fromDpi == toDpi)
+ {
+ return size;
+ }
+
+ var scale = (double)toDpi / fromDpi;
+ return new SizeInt32(
+ (int)Math.Round(size.Width * scale),
+ (int)Math.Round(size.Height * scale));
+ }
+
+ private static RectInt32 ScaleRect(RectInt32 rect, int fromDpi, int toDpi)
+ {
+ var scale = (double)toDpi / fromDpi;
+ return new RectInt32(
+ (int)Math.Round(rect.X * scale),
+ (int)Math.Round(rect.Y * scale),
+ (int)Math.Round(rect.Width * scale),
+ (int)Math.Round(rect.Height * scale));
+ }
+
+ private static SizeInt32 ClampSize(int width, int height, RectInt32 workArea) =>
+ new(Math.Min(width, workArea.Width), Math.Min(height, workArea.Height));
+
+ private static RectInt32 CenterRectInWorkArea(SizeInt32 size, RectInt32 workArea) =>
+ new(
+ workArea.X + ((workArea.Width - size.Width) / 2),
+ workArea.Y + ((workArea.Height - size.Height) / 2),
+ size.Width,
+ size.Height);
+
+ private static bool IsOffscreen(RectInt32 rect, RectInt32 workArea) =>
+ rect.X + MinimumVisibleSize > workArea.X + workArea.Width ||
+ rect.X + rect.Width - MinimumVisibleSize < workArea.X ||
+ rect.Y + MinimumVisibleSize > workArea.Y + workArea.Height ||
+ rect.Y + rect.Height - MinimumVisibleSize < workArea.Y;
+}
diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs
index abaf102a9e..bc083cd1bd 100644
--- a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs
+++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs
@@ -21,7 +21,6 @@ using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.PowerToys.Telemetry;
-using Microsoft.UI;
using Microsoft.UI.Composition;
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Input;
@@ -32,13 +31,9 @@ using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Graphics;
using Windows.System;
-using Windows.UI;
-using Windows.UI.WindowManagement;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;
-using Windows.Win32.Graphics.Gdi;
-using Windows.Win32.UI.HiDpi;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using Windows.Win32.UI.WindowsAndMessaging;
using WinRT;
@@ -60,9 +55,6 @@ public sealed partial class MainWindow : WindowEx,
IRecipient,
IDisposable
{
- private const int DefaultWidth = 800;
- private const int DefaultHeight = 480;
-
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_")]
private readonly uint WM_TASKBAR_RESTART;
@@ -226,39 +218,40 @@ public sealed partial class MainWindow : WindowEx,
PositionCentered(displayArea);
}
+ private void PositionCentered(DisplayArea displayArea)
+ {
+ var position = WindowPositionHelper.CalculateCenteredPosition(
+ displayArea,
+ AppWindow.Size,
+ (int)this.GetDpiForWindow());
+
+ if (position is not null)
+ {
+ // Use Move(), not MoveAndResize(). Windows auto-resizes on DPI change via WM_DPICHANGED;
+ // the helper already accounts for this when calculating the centered position.
+ AppWindow.Move((PointInt32)position);
+ }
+ }
+
private void RestoreWindowPosition()
{
var settings = App.Current.Services.GetService();
- if (settings?.LastWindowPosition is not WindowPosition savedPosition)
+ if (settings?.LastWindowPosition is not { Width: > 0, Height: > 0 } savedPosition)
{
PositionCentered();
return;
}
- if (savedPosition.Width <= 0 || savedPosition.Height <= 0)
- {
- PositionCentered();
- return;
- }
+ // MoveAndResize is safe here—we're restoring a saved state at startup,
+ // not moving a live window between displays.
+ var newRect = WindowPositionHelper.AdjustRectForVisibility(
+ savedPosition.ToPhysicalWindowRectangle(),
+ new SizeInt32(savedPosition.ScreenWidth, savedPosition.ScreenHeight),
+ savedPosition.Dpi);
- var newRect = EnsureWindowIsVisible(savedPosition.ToPhysicalWindowRectangle(), new SizeInt32(savedPosition.ScreenWidth, savedPosition.ScreenHeight), savedPosition.Dpi);
AppWindow.MoveAndResize(newRect);
}
- private void PositionCentered(DisplayArea displayArea)
- {
- if (displayArea is not null)
- {
- var centeredPosition = AppWindow.Position;
- centeredPosition.X = (displayArea.WorkArea.Width - AppWindow.Size.Width) / 2;
- centeredPosition.Y = (displayArea.WorkArea.Height - AppWindow.Size.Height) / 2;
-
- centeredPosition.X += displayArea.WorkArea.X;
- centeredPosition.Y += displayArea.WorkArea.Y;
- AppWindow.Move(centeredPosition);
- }
- }
-
private void UpdateWindowPositionInMemory()
{
var displayArea = DisplayArea.GetFromWindowId(AppWindow.Id, DisplayAreaFallback.Nearest) ?? DisplayArea.Primary;
@@ -352,7 +345,8 @@ public sealed partial class MainWindow : WindowEx,
if (target == MonitorBehavior.ToLast)
{
- var newRect = EnsureWindowIsVisible(_currentWindowPosition.ToPhysicalWindowRectangle(), new SizeInt32(_currentWindowPosition.ScreenWidth, _currentWindowPosition.ScreenHeight), _currentWindowPosition.Dpi);
+ var originalScreen = new SizeInt32(_currentWindowPosition.ScreenWidth, _currentWindowPosition.ScreenHeight);
+ var newRect = WindowPositionHelper.AdjustRectForVisibility(_currentWindowPosition.ToPhysicalWindowRectangle(), originalScreen, _currentWindowPosition.Dpi);
AppWindow.MoveAndResize(newRect);
}
else
@@ -382,115 +376,7 @@ public sealed partial class MainWindow : WindowEx,
PInvoke.SetWindowPos(hwnd, HWND.HWND_TOPMOST, 0, 0, 0, 0, SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE);
}
- ///
- /// Ensures that the window rectangle is visible on-screen.
- ///
- /// The window rectangle in physical pixels.
- /// The desktop area the window was positioned on.
- /// The window's original DPI.
- ///
- /// A window rectangle in physical pixels, moved to the nearest display and resized
- /// if the DPI has changed.
- ///
- private static RectInt32 EnsureWindowIsVisible(RectInt32 windowRect, SizeInt32 originalScreen, int originalDpi)
- {
- var displayArea = DisplayArea.GetFromRect(windowRect, DisplayAreaFallback.Nearest);
- if (displayArea is null)
- {
- return windowRect;
- }
-
- var workArea = displayArea.WorkArea;
- if (workArea.Width <= 0 || workArea.Height <= 0)
- {
- // Fallback, nothing reasonable to do
- return windowRect;
- }
-
- var effectiveDpi = GetEffectiveDpiFromDisplayId(displayArea);
- if (originalDpi <= 0)
- {
- originalDpi = effectiveDpi; // use current DPI as baseline (no scaling adjustment needed)
- }
-
- var hasInvalidSize = windowRect.Width <= 0 || windowRect.Height <= 0;
- if (hasInvalidSize)
- {
- windowRect = new RectInt32(windowRect.X, windowRect.Y, DefaultWidth, DefaultHeight);
- }
-
- // If we have a DPI change, scale the window rectangle accordingly
- if (effectiveDpi != originalDpi)
- {
- var scalingFactor = effectiveDpi / (double)originalDpi;
- windowRect = new RectInt32(
- (int)Math.Round(windowRect.X * scalingFactor),
- (int)Math.Round(windowRect.Y * scalingFactor),
- (int)Math.Round(windowRect.Width * scalingFactor),
- (int)Math.Round(windowRect.Height * scalingFactor));
- }
-
- var targetWidth = Math.Min(windowRect.Width, workArea.Width);
- var targetHeight = Math.Min(windowRect.Height, workArea.Height);
-
- // Ensure at least some minimum visible area (e.g., 100 pixels)
- // This helps prevent the window from being entirely offscreen, regardless of display scaling.
- const int minimumVisibleSize = 100;
- var isOffscreen =
- windowRect.X + minimumVisibleSize > workArea.X + workArea.Width ||
- windowRect.X + windowRect.Width - minimumVisibleSize < workArea.X ||
- windowRect.Y + minimumVisibleSize > workArea.Y + workArea.Height ||
- windowRect.Y + windowRect.Height - minimumVisibleSize < workArea.Y;
-
- // if the work area size has changed, re-center the window
- var workAreaSizeChanged =
- originalScreen.Width != workArea.Width ||
- originalScreen.Height != workArea.Height;
-
- int targetX;
- int targetY;
- var recenter = isOffscreen || workAreaSizeChanged || hasInvalidSize;
- if (recenter)
- {
- targetX = workArea.X + ((workArea.Width - targetWidth) / 2);
- targetY = workArea.Y + ((workArea.Height - targetHeight) / 2);
- }
- else
- {
- targetX = windowRect.X;
- targetY = windowRect.Y;
- }
-
- return new RectInt32(targetX, targetY, targetWidth, targetHeight);
- }
-
- private static int GetEffectiveDpiFromDisplayId(DisplayArea displayArea)
- {
- var effectiveDpi = 96;
-
- var hMonitor = (HMONITOR)Win32Interop.GetMonitorFromDisplayId(displayArea.DisplayId);
- if (!hMonitor.IsNull)
- {
- var hr = PInvoke.GetDpiForMonitor(hMonitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var dpiX, out _);
- if (hr == 0)
- {
- effectiveDpi = (int)dpiX;
- }
- else
- {
- Logger.LogWarning($"GetDpiForMonitor failed with HRESULT: 0x{hr.Value:X8} on display {displayArea.DisplayId}");
- }
- }
-
- if (effectiveDpi <= 0)
- {
- effectiveDpi = 96;
- }
-
- return effectiveDpi;
- }
-
- private DisplayArea GetScreen(HWND currentHwnd, MonitorBehavior target)
+ private static DisplayArea GetScreen(HWND currentHwnd, MonitorBehavior target)
{
// Leaving a note here, in case we ever need it:
// https://github.com/microsoft/microsoft-ui-xaml/issues/6454