mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[CmdPal] Added recenter window support. (#38943)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Added a new setting option to Command Palette that allows users to choose whether the window should be recentered on every launch or remember its last position. This enhancement improves user experience by maintaining window positioning preferences across sessions. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #38310 - [x] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments This PR adds a "Recenter window on launch" setting to Command Palette with the following changes: 1. Added a new `RecenterWindow` property to `SettingsModel` (default is true for backward compatibility) 2. Added corresponding property to `SettingsViewModel` for binding 3. Created a new `WindowPosition` class to track window position and size 4. Modified `MainWindow.xaml.cs` to: - Track window position and size changes - Update position memory when window is modified or dismissed - Respect the recenter setting when showing the window 5. Added UI controls in the settings page with proper localization strings  This feature allows users who prefer to have the Command Palette appear in a specific screen location to maintain that preference, while others can continue using the centered window behavior. <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed 1. Manually verified that with the setting enabled (default), the window centers on launch 2. Verified that with the setting disabled, the window appears at its last position --------- Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com> Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com>
This commit is contained in:
@@ -63,6 +63,8 @@ public sealed partial class MainWindow : WindowEx,
|
||||
private DesktopAcrylicController? _acrylicController;
|
||||
private SystemBackdropConfiguration? _configurationSource;
|
||||
|
||||
private WindowPosition _currentWindowPosition = new();
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -84,7 +86,9 @@ public sealed partial class MainWindow : WindowEx,
|
||||
|
||||
this.SetIcon();
|
||||
AppWindow.Title = RS_.GetString("AppName");
|
||||
PositionCentered();
|
||||
RestoreWindowPosition();
|
||||
UpdateWindowPositionInMemory();
|
||||
|
||||
SetAcrylic();
|
||||
|
||||
WeakReferenceMessenger.Default.Register<DismissMessage>(this);
|
||||
@@ -155,6 +159,39 @@ public sealed partial class MainWindow : WindowEx,
|
||||
PositionCentered(displayArea);
|
||||
}
|
||||
|
||||
private void RestoreWindowPosition()
|
||||
{
|
||||
var settings = App.Current.Services.GetService<SettingsModel>();
|
||||
if (settings?.LastWindowPosition is not WindowPosition savedPosition)
|
||||
{
|
||||
PositionCentered();
|
||||
return;
|
||||
}
|
||||
|
||||
if (savedPosition.Width <= 0 || savedPosition.Height <= 0)
|
||||
{
|
||||
PositionCentered();
|
||||
return;
|
||||
}
|
||||
|
||||
AppWindow.Resize(new SizeInt32 { Width = savedPosition.Width, Height = savedPosition.Height });
|
||||
|
||||
var savedRect = new RectInt32(savedPosition.X, savedPosition.Y, savedPosition.Width, savedPosition.Height);
|
||||
var displayArea = DisplayArea.GetFromRect(savedRect, DisplayAreaFallback.Nearest);
|
||||
var workArea = displayArea.WorkArea;
|
||||
|
||||
var maxX = workArea.X + Math.Max(0, workArea.Width - savedPosition.Width);
|
||||
var maxY = workArea.Y + Math.Max(0, workArea.Height - savedPosition.Height);
|
||||
|
||||
var targetPoint = new PointInt32
|
||||
{
|
||||
X = Math.Clamp(savedPosition.X, workArea.X, maxX),
|
||||
Y = Math.Clamp(savedPosition.Y, workArea.Y, maxY),
|
||||
};
|
||||
|
||||
AppWindow.Move(targetPoint);
|
||||
}
|
||||
|
||||
private void PositionCentered(DisplayArea displayArea)
|
||||
{
|
||||
if (displayArea is not null)
|
||||
@@ -169,6 +206,17 @@ public sealed partial class MainWindow : WindowEx,
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateWindowPositionInMemory()
|
||||
{
|
||||
_currentWindowPosition = new WindowPosition
|
||||
{
|
||||
X = AppWindow.Position.X,
|
||||
Y = AppWindow.Position.Y,
|
||||
Width = AppWindow.Size.Width,
|
||||
Height = AppWindow.Size.Height,
|
||||
};
|
||||
}
|
||||
|
||||
private void HotReloadSettings()
|
||||
{
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
@@ -251,8 +299,16 @@ public sealed partial class MainWindow : WindowEx,
|
||||
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_RESTORE);
|
||||
}
|
||||
|
||||
var display = GetScreen(hwnd, target);
|
||||
PositionCentered(display);
|
||||
if (target == MonitorBehavior.ToLast)
|
||||
{
|
||||
AppWindow.Resize(new SizeInt32 { Width = _currentWindowPosition.Width, Height = _currentWindowPosition.Height });
|
||||
AppWindow.Move(new PointInt32 { X = _currentWindowPosition.X, Y = _currentWindowPosition.Y });
|
||||
}
|
||||
else
|
||||
{
|
||||
var display = GetScreen(hwnd, target);
|
||||
PositionCentered(display);
|
||||
}
|
||||
|
||||
// Check if the debugger is attached. If it is, we don't want to apply the tool window style,
|
||||
// because that would make it hard to debug the app
|
||||
@@ -413,6 +469,22 @@ public sealed partial class MainWindow : WindowEx,
|
||||
internal void MainWindow_Closed(object sender, WindowEventArgs args)
|
||||
{
|
||||
var serviceProvider = App.Current.Services;
|
||||
UpdateWindowPositionInMemory();
|
||||
|
||||
var settings = serviceProvider.GetService<SettingsModel>();
|
||||
if (settings is not null)
|
||||
{
|
||||
settings.LastWindowPosition = new WindowPosition
|
||||
{
|
||||
X = _currentWindowPosition.X,
|
||||
Y = _currentWindowPosition.Y,
|
||||
Width = _currentWindowPosition.Width,
|
||||
Height = _currentWindowPosition.Height,
|
||||
};
|
||||
|
||||
SettingsModel.SaveSettings(settings);
|
||||
}
|
||||
|
||||
var extensionService = serviceProvider.GetService<IExtensionService>()!;
|
||||
extensionService.SignalStopExtensionsAsync();
|
||||
|
||||
@@ -484,6 +556,9 @@ public sealed partial class MainWindow : WindowEx,
|
||||
{
|
||||
if (args.WindowActivationState == WindowActivationState.Deactivated)
|
||||
{
|
||||
// Save the current window position before hiding the window
|
||||
UpdateWindowPositionInMemory();
|
||||
|
||||
// If there's a debugger attached...
|
||||
if (System.Diagnostics.Debugger.IsAttached)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user