mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 02:06:36 +02:00
## Summary of the Pull Request Replaces the `SetVisibilityInSwitchers` method with `WS_EX_TOOLWINDOW` style, removing reliance on the fragile `IsShownInSwitchers` property. Explicitly overrides window corner preference so tool windows keep the same rounded corners as regular app windows (tool windows otherwise default to a smaller corner radius, which would change the look). Benefits: - Avoids use of the unreliable `IsShownInSwitchers` property, which has multiple known failure cases. - Allows window managers to correctly treat Command Palette as a popup window rather than a normal app window, preventing unwanted tiling, grid placement, or similar behaviors. This better matches the intended design of CmdPal as an overlay UI. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] Closes: #41772 - [x] Closes: #41593 - [ ] **Communication:** I've discussed this with core contributors already. If the 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 <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed
115 lines
3.9 KiB
C#
115 lines
3.9 KiB
C#
// 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 CommunityToolkit.Mvvm.Messaging;
|
|
using CommunityToolkit.WinUI;
|
|
using ManagedCommon;
|
|
using Microsoft.CmdPal.Core.ViewModels;
|
|
using Microsoft.CmdPal.UI.Helpers;
|
|
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
|
using Microsoft.UI.Dispatching;
|
|
using Microsoft.UI.Windowing;
|
|
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.WindowsAndMessaging;
|
|
using WinUIEx;
|
|
using RS_ = Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance;
|
|
|
|
namespace Microsoft.CmdPal.UI;
|
|
|
|
public sealed partial class ToastWindow : WindowEx,
|
|
IRecipient<QuitMessage>
|
|
{
|
|
private readonly HWND _hwnd;
|
|
|
|
public ToastViewModel ViewModel { get; } = new();
|
|
|
|
private readonly DispatcherQueueTimer _debounceTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
|
|
|
public ToastWindow()
|
|
{
|
|
this.InitializeComponent();
|
|
AppWindow.Hide();
|
|
ExtendsContentIntoTitleBar = true;
|
|
AppWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay);
|
|
this.SetIcon();
|
|
AppWindow.Title = RS_.GetString("ToastWindowTitle");
|
|
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
|
|
|
|
// Tool windows don't show up in ALT+TAB, and don't show up in the taskbar
|
|
// Since tool windows have smaller corner radii, we need to force the normal ones
|
|
// to visually match system toasts.
|
|
this.ToggleExtendedWindowStyle(WINDOW_EX_STYLE.WS_EX_TOOLWINDOW, true);
|
|
this.SetCornerPreference(DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND);
|
|
|
|
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
|
|
PInvoke.EnableWindow(_hwnd, false);
|
|
|
|
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
|
|
}
|
|
|
|
private static double GetScaleFactor(HWND hwnd)
|
|
{
|
|
try
|
|
{
|
|
var monitor = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
|
_ = PInvoke.GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var dpiX, out _);
|
|
return dpiX / 96.0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError($"Failed to get scale factor, error: {ex.Message}");
|
|
return 1.0;
|
|
}
|
|
}
|
|
|
|
private void PositionCentered()
|
|
{
|
|
this.SetWindowSize(ToastText.ActualWidth, ToastText.ActualHeight);
|
|
|
|
var displayArea = DisplayArea.GetFromWindowId(AppWindow.Id, DisplayAreaFallback.Nearest);
|
|
if (displayArea is not null)
|
|
{
|
|
var centeredPosition = AppWindow.Position;
|
|
centeredPosition.X = (displayArea.WorkArea.Width - AppWindow.Size.Width) / 2;
|
|
|
|
var monitorHeight = displayArea.WorkArea.Height;
|
|
var windowHeight = AppWindow.Size.Height;
|
|
centeredPosition.Y = monitorHeight - (windowHeight + 8); // Align with other shell toasts, like the volume indicator.
|
|
AppWindow.Move(centeredPosition);
|
|
}
|
|
}
|
|
|
|
public void ShowToast(string message)
|
|
{
|
|
ViewModel.ToastMessage = message;
|
|
DispatcherQueue.TryEnqueue(
|
|
DispatcherQueuePriority.Low,
|
|
() =>
|
|
{
|
|
PositionCentered();
|
|
|
|
// SW_SHOWNA prevents us from getting activated (and stealing FG)
|
|
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_SHOWNA);
|
|
|
|
_debounceTimer.Debounce(
|
|
() =>
|
|
{
|
|
AppWindow.Hide();
|
|
},
|
|
interval: TimeSpan.FromMilliseconds(2500),
|
|
immediate: false);
|
|
});
|
|
}
|
|
|
|
public void Receive(QuitMessage message)
|
|
{
|
|
// This might come in on a background thread
|
|
DispatcherQueue.TryEnqueue(() => Close());
|
|
}
|
|
}
|