mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 10:46:33 +02:00
[FancyZones] Fix handling newly created windows on Windows 11 (#28646)
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
#include <FancyZonesLib/Settings.h>
|
#include <FancyZonesLib/Settings.h>
|
||||||
#include <FancyZonesLib/SettingsObserver.h>
|
#include <FancyZonesLib/SettingsObserver.h>
|
||||||
#include <FancyZonesLib/trace.h>
|
#include <FancyZonesLib/trace.h>
|
||||||
|
#include <FancyZonesLib/VirtualDesktop.h>
|
||||||
#include <FancyZonesLib/WindowKeyboardSnap.h>
|
#include <FancyZonesLib/WindowKeyboardSnap.h>
|
||||||
#include <FancyZonesLib/WindowMouseSnap.h>
|
#include <FancyZonesLib/WindowMouseSnap.h>
|
||||||
#include <FancyZonesLib/WorkArea.h>
|
#include <FancyZonesLib/WorkArea.h>
|
||||||
@@ -397,12 +398,6 @@ void FancyZones::WindowCreated(HWND window) noexcept
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool isCandidateForLastKnownZone = FancyZonesWindowUtils::IsCandidateForZoning(window);
|
|
||||||
if (!isCandidateForLastKnownZone)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HMONITOR primary = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
|
HMONITOR primary = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
|
||||||
HMONITOR active = primary;
|
HMONITOR active = primary;
|
||||||
|
|
||||||
@@ -994,7 +989,7 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FancyZonesSettings::settings().overrideSnapHotkeys && FancyZonesWindowUtils::IsCandidateForZoning(window))
|
if (FancyZonesSettings::settings().overrideSnapHotkeys)
|
||||||
{
|
{
|
||||||
HMONITOR monitor = WorkAreaKeyFromWindow(window);
|
HMONITOR monitor = WorkAreaKeyFromWindow(window);
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,7 @@
|
|||||||
<ClCompile Include="FancyZonesData\LayoutTemplates.cpp">
|
<ClCompile Include="FancyZonesData\LayoutTemplates.cpp">
|
||||||
<PrecompiledHeaderFile>../pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>../pch.h</PrecompiledHeaderFile>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="FancyZonesWindowProcessing.cpp" />
|
||||||
<ClCompile Include="FancyZonesWindowProperties.cpp" />
|
<ClCompile Include="FancyZonesWindowProperties.cpp" />
|
||||||
<ClCompile Include="FancyZonesWinHookEventIDs.cpp" />
|
<ClCompile Include="FancyZonesWinHookEventIDs.cpp" />
|
||||||
<ClCompile Include="FancyZonesData.cpp" />
|
<ClCompile Include="FancyZonesData.cpp" />
|
||||||
|
|||||||
@@ -272,6 +272,9 @@
|
|||||||
<ClCompile Include="WindowKeyboardSnap.cpp">
|
<ClCompile Include="WindowKeyboardSnap.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="FancyZonesWindowProcessing.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "FancyZonesWindowProcessing.h"
|
||||||
|
|
||||||
|
#include <FancyZonesLib/Settings.h>
|
||||||
|
#include <FancyZonesLib/VirtualDesktop.h>
|
||||||
|
#include <FancyZonesLib/WindowUtils.h>
|
||||||
|
|
||||||
|
bool FancyZonesWindowProcessing::IsProcessable(HWND window) noexcept
|
||||||
|
{
|
||||||
|
const bool isSplashScreen = FancyZonesWindowUtils::IsSplashScreen(window);
|
||||||
|
if (isSplashScreen)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool windowMinimized = IsIconic(window);
|
||||||
|
if (windowMinimized)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool standard = FancyZonesWindowUtils::IsStandardWindow(window);
|
||||||
|
if (!standard)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// popup could be the window we don't want to snap: start menu, notification popup, tray window, etc.
|
||||||
|
// also, popup could be the windows we want to snap disregarding the "allowSnapPopupWindows" setting, e.g. Telegram
|
||||||
|
bool isPopup = FancyZonesWindowUtils::IsPopupWindow(window) && !FancyZonesWindowUtils::HasThickFrameAndMinimizeMaximizeButtons(window);
|
||||||
|
if (isPopup && !FancyZonesSettings::settings().allowSnapPopupWindows)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow child windows
|
||||||
|
auto hasOwner = FancyZonesWindowUtils::HasVisibleOwner(window);
|
||||||
|
if (hasOwner && !FancyZonesSettings::settings().allowSnapChildWindows)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FancyZonesWindowUtils::IsExcluded(window))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch between virtual desktops results with posting same windows messages that also indicate
|
||||||
|
// creation of new window. We need to check if window being processed is on currently active desktop.
|
||||||
|
if (!VirtualDesktop::instance().IsWindowOnCurrentDesktop(window))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -1,39 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <FancyZonesLib/VirtualDesktop.h>
|
|
||||||
#include <FancyZonesLib/WindowUtils.h>
|
|
||||||
|
|
||||||
namespace FancyZonesWindowProcessing
|
namespace FancyZonesWindowProcessing
|
||||||
{
|
{
|
||||||
inline bool IsProcessable(HWND window) noexcept
|
bool IsProcessable(HWND window) noexcept;
|
||||||
{
|
|
||||||
const bool isSplashScreen = FancyZonesWindowUtils::IsSplashScreen(window);
|
|
||||||
if (isSplashScreen)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool windowMinimized = IsIconic(window);
|
|
||||||
if (windowMinimized)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For windows that FancyZones shouldn't process (start menu, tray, popup menus)
|
|
||||||
// VirtualDesktopManager is unable to retrieve virtual desktop id and returns an error.
|
|
||||||
auto desktopId = VirtualDesktop::instance().GetDesktopId(window);
|
|
||||||
if (!desktopId.has_value())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switch between virtual desktops results with posting same windows messages that also indicate
|
|
||||||
// creation of new window. We need to check if window being processed is on currently active desktop.
|
|
||||||
if (!VirtualDesktop::instance().IsWindowOnCurrentDesktop(window))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,6 @@ WindowMouseSnap::~WindowMouseSnap()
|
|||||||
std::unique_ptr<WindowMouseSnap> WindowMouseSnap::Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas)
|
std::unique_ptr<WindowMouseSnap> WindowMouseSnap::Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas)
|
||||||
{
|
{
|
||||||
if (!FancyZonesWindowProcessing::IsProcessable(window) ||
|
if (!FancyZonesWindowProcessing::IsProcessable(window) ||
|
||||||
!FancyZonesWindowUtils::IsCandidateForZoning(window) ||
|
|
||||||
FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent())
|
FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent())
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -166,6 +166,8 @@ bool FancyZonesWindowUtils::HasVisibleOwner(HWND window) noexcept
|
|||||||
|
|
||||||
bool FancyZonesWindowUtils::IsStandardWindow(HWND window)
|
bool FancyZonesWindowUtils::IsStandardWindow(HWND window)
|
||||||
{
|
{
|
||||||
|
// True if from the styles the window looks like a standard window
|
||||||
|
|
||||||
if (GetAncestor(window, GA_ROOT) != window)
|
if (GetAncestor(window, GA_ROOT) != window)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -181,13 +183,6 @@ bool FancyZonesWindowUtils::IsStandardWindow(HWND window)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<char, 256> class_name;
|
|
||||||
GetClassNameA(window, class_name.data(), static_cast<int>(class_name.size()));
|
|
||||||
if (is_system_window(window, class_name.data()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,44 +198,6 @@ bool FancyZonesWindowUtils::HasThickFrameAndMinimizeMaximizeButtons(HWND window)
|
|||||||
return ((style & WS_THICKFRAME) == WS_THICKFRAME && (style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX && (style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX);
|
return ((style & WS_THICKFRAME) == WS_THICKFRAME && (style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX && (style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FancyZonesWindowUtils::IsCandidateForZoning(HWND window)
|
|
||||||
{
|
|
||||||
bool isStandard = IsStandardWindow(window);
|
|
||||||
if (!isStandard)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// popup could be the window we don't want to snap: start menu, notification popup, tray window, etc.
|
|
||||||
// also, popup could be the windows we want to snap disregarding the "allowSnapPopupWindows" setting, e.g. Telegram
|
|
||||||
bool isPopup = IsPopupWindow(window);
|
|
||||||
if (isPopup && !HasThickFrameAndMinimizeMaximizeButtons(window) && !FancyZonesSettings::settings().allowSnapPopupWindows)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow child windows
|
|
||||||
auto hasOwner = HasVisibleOwner(window);
|
|
||||||
if (hasOwner && !FancyZonesSettings::settings().allowSnapChildWindows)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring processPath = get_process_path_waiting_uwp(window);
|
|
||||||
CharUpperBuffW(const_cast<std::wstring&>(processPath).data(), static_cast<DWORD>(processPath.length()));
|
|
||||||
if (IsExcludedByUser(window, processPath))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsExcludedByDefault(window, processPath))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window)
|
bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window)
|
||||||
{
|
{
|
||||||
DWORD pid = 0;
|
DWORD pid = 0;
|
||||||
@@ -268,6 +225,23 @@ bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FancyZonesWindowUtils::IsExcluded(HWND window)
|
||||||
|
{
|
||||||
|
std::wstring processPath = get_process_path_waiting_uwp(window);
|
||||||
|
CharUpperBuffW(const_cast<std::wstring&>(processPath).data(), static_cast<DWORD>(processPath.length()));
|
||||||
|
if (IsExcludedByUser(window, processPath))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsExcludedByDefault(window, processPath))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool FancyZonesWindowUtils::IsExcludedByUser(const HWND& hwnd, std::wstring& processPath) noexcept
|
bool FancyZonesWindowUtils::IsExcludedByUser(const HWND& hwnd, std::wstring& processPath) noexcept
|
||||||
{
|
{
|
||||||
return (check_excluded_app(hwnd, processPath, FancyZonesSettings::settings().excludedAppsArray));
|
return (check_excluded_app(hwnd, processPath, FancyZonesSettings::settings().excludedAppsArray));
|
||||||
@@ -281,6 +255,13 @@ bool FancyZonesWindowUtils::IsExcludedByDefault(const HWND& hwnd, std::wstring&
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::array<char, 256> class_name;
|
||||||
|
GetClassNameA(hwnd, class_name.data(), static_cast<int>(class_name.size()));
|
||||||
|
if (is_system_window(hwnd, class_name.data()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static std::vector<std::wstring> defaultExcludedApps = { NonLocalizable::PowerToysAppFZEditor, NonLocalizable::CoreWindow, NonLocalizable::SearchUI };
|
static std::vector<std::wstring> defaultExcludedApps = { NonLocalizable::PowerToysAppFZEditor, NonLocalizable::CoreWindow, NonLocalizable::SearchUI };
|
||||||
return (check_excluded_app(hwnd, processPath, defaultExcludedApps));
|
return (check_excluded_app(hwnd, processPath, defaultExcludedApps));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ namespace FancyZonesWindowUtils
|
|||||||
bool IsStandardWindow(HWND window);
|
bool IsStandardWindow(HWND window);
|
||||||
bool IsPopupWindow(HWND window) noexcept;
|
bool IsPopupWindow(HWND window) noexcept;
|
||||||
bool HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept;
|
bool HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept;
|
||||||
bool IsCandidateForZoning(HWND window);
|
|
||||||
bool IsProcessOfWindowElevated(HWND window); // If HWND is already dead, we assume it wasn't elevated
|
bool IsProcessOfWindowElevated(HWND window); // If HWND is already dead, we assume it wasn't elevated
|
||||||
|
|
||||||
|
bool IsExcluded(HWND window);
|
||||||
bool IsExcludedByUser(const HWND& hwnd, std::wstring& processPath) noexcept;
|
bool IsExcludedByUser(const HWND& hwnd, std::wstring& processPath) noexcept;
|
||||||
bool IsExcludedByDefault(const HWND& hwnd, std::wstring& processPath) noexcept;
|
bool IsExcludedByDefault(const HWND& hwnd, std::wstring& processPath) noexcept;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user