Refactor common and fancyzones/utils (#6073)
* Move module-specific functions to module * Refactor FZ utils * Remove 'zonable' from FancyZonesWindowInfo * Address PR comments * Address PR comments * Fix stuff after rebasing
This commit is contained in:
parent
2817bf4d62
commit
9999a2b126
|
@ -56,12 +56,10 @@ std::optional<POINT> get_mouse_pos()
|
|||
}
|
||||
}
|
||||
|
||||
// Test if a window is part of the shell or the task bar.
|
||||
// We compare the HWND against HWND of the desktop and shell windows,
|
||||
// we also filter out some window class names know to belong to
|
||||
// the taskbar.
|
||||
static bool is_system_window(HWND hwnd, const char* class_name)
|
||||
bool is_system_window(HWND hwnd, const char* class_name)
|
||||
{
|
||||
// We compare the HWND against HWND of the desktop and shell windows,
|
||||
// we also filter out some window class names know to belong to the taskbar.
|
||||
static auto system_classes = { "SysListView32", "WorkerW", "Shell_TrayWnd", "Shell_SecondaryTrayWnd", "Progman" };
|
||||
static auto system_hwnds = { GetDesktopWindow(), GetShellWindow() };
|
||||
for (auto system_hwnd : system_hwnds)
|
||||
|
@ -81,125 +79,6 @@ static bool is_system_window(HWND hwnd, const char* class_name)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool no_visible_owner(HWND window) noexcept
|
||||
{
|
||||
auto owner = GetWindow(window, GW_OWNER);
|
||||
if (owner == nullptr)
|
||||
{
|
||||
return true; // There is no owner at all
|
||||
}
|
||||
if (!IsWindowVisible(owner))
|
||||
{
|
||||
return true; // Owner is invisible
|
||||
}
|
||||
RECT rect;
|
||||
if (!GetWindowRect(owner, &rect))
|
||||
{
|
||||
return false; // Could not get the rect, return true (and filter out the window) just in case
|
||||
}
|
||||
// Return false (and allow the window to be zonable) if the owner window size is zero
|
||||
// It is enough that the window is zero-sized in one dimension only.
|
||||
return rect.top == rect.bottom || rect.left == rect.right;
|
||||
}
|
||||
|
||||
FancyZonesFilter get_fancyzones_filtered_window(HWND window)
|
||||
{
|
||||
FancyZonesFilter result;
|
||||
if (GetAncestor(window, GA_ROOT) != window || !IsWindowVisible(window))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
auto style = GetWindowLong(window, GWL_STYLE);
|
||||
auto exStyle = GetWindowLong(window, GWL_EXSTYLE);
|
||||
// WS_POPUP need to have a border or minimize/maximize buttons,
|
||||
// otherwise the window is "not interesting"
|
||||
if ((style & WS_POPUP) == WS_POPUP &&
|
||||
(style & WS_THICKFRAME) == 0 &&
|
||||
(style & WS_MINIMIZEBOX) == 0 &&
|
||||
(style & WS_MAXIMIZEBOX) == 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if ((style & WS_CHILD) == WS_CHILD ||
|
||||
(style & WS_DISABLED) == WS_DISABLED ||
|
||||
(exStyle & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW ||
|
||||
(exStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
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 result;
|
||||
}
|
||||
auto process_path = get_process_path(window);
|
||||
// Check for Cortana:
|
||||
if (strcmp(class_name.data(), "Windows.UI.Core.CoreWindow") == 0 &&
|
||||
process_path.ends_with(L"SearchUI.exe"))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result.process_path = std::move(process_path);
|
||||
result.standard_window = true;
|
||||
result.no_visible_owner = no_visible_owner(window);
|
||||
result.zonable = result.standard_window && result.no_visible_owner;
|
||||
return result;
|
||||
}
|
||||
|
||||
ShortcutGuideFilter get_shortcutguide_filtered_window()
|
||||
{
|
||||
ShortcutGuideFilter result;
|
||||
auto active_window = GetForegroundWindow();
|
||||
active_window = GetAncestor(active_window, GA_ROOT);
|
||||
if (!IsWindowVisible(active_window))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
auto style = GetWindowLong(active_window, GWL_STYLE);
|
||||
auto exStyle = GetWindowLong(active_window, GWL_EXSTYLE);
|
||||
if ((style & WS_CHILD) == WS_CHILD ||
|
||||
(style & WS_DISABLED) == WS_DISABLED ||
|
||||
(exStyle & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW ||
|
||||
(exStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
std::array<char, 256> class_name;
|
||||
GetClassNameA(active_window, class_name.data(), static_cast<int>(class_name.size()));
|
||||
if (is_system_window(active_window, class_name.data()))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
static HWND cortana_hwnd = nullptr;
|
||||
if (cortana_hwnd == nullptr)
|
||||
{
|
||||
if (strcmp(class_name.data(), "Windows.UI.Core.CoreWindow") == 0 &&
|
||||
get_process_path(active_window).ends_with(L"SearchUI.exe"))
|
||||
{
|
||||
cortana_hwnd = active_window;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (cortana_hwnd == active_window)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result.hwnd = active_window;
|
||||
// In reality, Windows Snap works if even one of those styles is set
|
||||
// for a window, it is just limited. If there is no WS_MAXIMIZEBOX using
|
||||
// WinKey + Up just won't maximize the window. Similary, without
|
||||
// WS_MINIMIZEBOX the window will not get minimized. A "Save As..." dialog
|
||||
// is a example of such window - it can be snapped to both sides and to
|
||||
// all screen corners, but will not get maximized nor minimized.
|
||||
// For now, since ShortcutGuide can only disable entire "Windows Controls"
|
||||
// group, we require that the window supports all the options.
|
||||
result.snappable = ((style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX) &&
|
||||
((style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX) &&
|
||||
((style & WS_THICKFRAME) == WS_THICKFRAME);
|
||||
return result;
|
||||
}
|
||||
|
||||
int width(const RECT& rect)
|
||||
{
|
||||
return rect.right - rect.left;
|
||||
|
|
|
@ -13,24 +13,8 @@ std::optional<RECT> get_button_pos(HWND hwnd);
|
|||
std::optional<RECT> get_window_pos(HWND hwnd);
|
||||
// Gets mouse position.
|
||||
std::optional<POINT> get_mouse_pos();
|
||||
|
||||
// Test if window can be zoned by FancyZones
|
||||
struct FancyZonesFilter
|
||||
{
|
||||
bool zonable = false; // If the window is zonable by FancyZones by default - true when both standard_window and no_visible_owner are also true
|
||||
bool standard_window = false; // True if from the styles the window looks like a standard window
|
||||
bool no_visible_owner = false; // True if the window is a top-level window that does not have a visible owner
|
||||
std::wstring process_path; // Path to the executable owning the window
|
||||
};
|
||||
FancyZonesFilter get_fancyzones_filtered_window(HWND window);
|
||||
|
||||
// Gets active foreground window, filtering out all "non standard" windows like the taskbar, etc.
|
||||
struct ShortcutGuideFilter
|
||||
{
|
||||
HWND hwnd = nullptr; // Handle to the top-level foreground window or nullptr if there is no such window
|
||||
bool snappable = false; // True, if the window can react to Windows Snap keys
|
||||
};
|
||||
ShortcutGuideFilter get_shortcutguide_filtered_window();
|
||||
// Check if window is part of the shell or the taskbar.
|
||||
bool is_system_window(HWND hwnd, const char* class_name);
|
||||
|
||||
// Calculate sizes
|
||||
int width(const RECT& rect);
|
||||
|
|
|
@ -350,7 +350,7 @@ bool FancyZones::ShouldProcessNewWindow(HWND window) noexcept
|
|||
// that belong to excluded applications list.
|
||||
if (IsSplashScreen(window) ||
|
||||
(reinterpret_cast<size_t>(::GetProp(window, ZonedWindowProperties::PropertyMultipleZoneID)) != 0) ||
|
||||
!IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||
!FancyZonesUtils::IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -482,7 +482,7 @@ void OpenWindowOnActiveMonitor(HWND window, HMONITOR monitor) noexcept
|
|||
if (GetMonitorInfo(monitor, &destMi))
|
||||
{
|
||||
RECT newPosition = FitOnScreen(placement.rcNormalPosition, originMi.rcWork, destMi.rcWork);
|
||||
SizeWindowToRect(window, newPosition);
|
||||
FancyZonesUtils::SizeWindowToRect(window, newPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -642,7 +642,7 @@ void FancyZones::ToggleEditor() noexcept
|
|||
std::vector<std::pair<HMONITOR, RECT>> allMonitors;
|
||||
|
||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
|
||||
allMonitors = GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
||||
allMonitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
||||
} }).wait();
|
||||
|
||||
for (auto& [monitor, workArea] : allMonitors)
|
||||
|
@ -1031,7 +1031,7 @@ void FancyZones::UpdateWindowsPositions() noexcept
|
|||
void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept
|
||||
{
|
||||
auto window = GetForegroundWindow();
|
||||
if (IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||
if (FancyZonesUtils::IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||
{
|
||||
const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||
if (monitor)
|
||||
|
@ -1100,8 +1100,8 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce
|
|||
bool moved = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */, m_workAreaHandler.GetWorkArea(m_currentDesktopId, current));
|
||||
if (!moved)
|
||||
{
|
||||
RestoreWindowOrigin(window);
|
||||
RestoreWindowSize(window);
|
||||
FancyZonesUtils::RestoreWindowOrigin(window);
|
||||
FancyZonesUtils::RestoreWindowSize(window);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1127,7 +1127,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
|||
current = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||
}
|
||||
|
||||
auto allMonitors = GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
||||
auto allMonitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
||||
|
||||
if (current && allMonitors.size() > 1 && m_settings->GetSettings()->moveWindowAcrossMonitors)
|
||||
{
|
||||
|
@ -1181,7 +1181,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
|||
return false;
|
||||
}
|
||||
|
||||
size_t chosenIdx = ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
size_t chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
|
||||
if (chosenIdx < zoneRects.size())
|
||||
{
|
||||
|
@ -1222,9 +1222,9 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
|||
return false;
|
||||
}
|
||||
|
||||
RECT combinedRect = GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>();
|
||||
windowRect = PrepareRectForCycling(windowRect, combinedRect, vkCode);
|
||||
chosenIdx = ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
RECT combinedRect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>();
|
||||
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, combinedRect, vkCode);
|
||||
chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
if (chosenIdx < zoneRects.size())
|
||||
{
|
||||
// Moving to another monitor succeeded
|
||||
|
@ -1248,7 +1248,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
|||
bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
||||
{
|
||||
auto window = GetForegroundWindow();
|
||||
if (IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||
if (FancyZonesUtils::IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||
{
|
||||
if (m_settings->GetSettings()->moveWindowsBasedOnPosition)
|
||||
{
|
||||
|
@ -1335,7 +1335,7 @@ std::vector<HMONITOR> FancyZones::GetMonitorsSorted() noexcept
|
|||
std::shared_lock readLock(m_lock);
|
||||
|
||||
auto monitorInfo = GetRawMonitorData();
|
||||
OrderMonitors(monitorInfo);
|
||||
FancyZonesUtils::OrderMonitors(monitorInfo);
|
||||
std::vector<HMONITOR> output;
|
||||
std::transform(std::begin(monitorInfo), std::end(monitorInfo), std::back_inserter(output), [](const auto& info) { return info.first; });
|
||||
return output;
|
||||
|
|
|
@ -90,7 +90,7 @@ namespace
|
|||
data.deviceId = json.GetNamedString(NonLocalizable::DeviceIdStr);
|
||||
data.zoneSetUuid = json.GetNamedString(NonLocalizable::ZoneSetUuidStr);
|
||||
|
||||
if (!IsValidGuid(data.zoneSetUuid) || !IsValidDeviceId(data.deviceId))
|
||||
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid) || !FancyZonesUtils::IsValidDeviceId(data.deviceId))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -250,7 +250,7 @@ namespace JSONHelpers
|
|||
CustomZoneSetJSON result;
|
||||
|
||||
result.uuid = customZoneSet.GetNamedString(NonLocalizable::UuidStr);
|
||||
if (!IsValidGuid(result.uuid))
|
||||
if (!FancyZonesUtils::IsValidGuid(result.uuid))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -314,7 +314,7 @@ namespace JSONHelpers
|
|||
zoneSetData.uuid = zoneSet.GetNamedString(NonLocalizable::UuidStr);
|
||||
zoneSetData.type = FancyZonesDataTypes::TypeFromString(std::wstring{ zoneSet.GetNamedString(NonLocalizable::TypeStr) });
|
||||
|
||||
if (!IsValidGuid(zoneSetData.uuid))
|
||||
if (!FancyZonesUtils::IsValidGuid(zoneSetData.uuid))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -415,7 +415,7 @@ namespace JSONHelpers
|
|||
DeviceInfoJSON result;
|
||||
|
||||
result.deviceId = device.GetNamedString(NonLocalizable::DeviceIdStr);
|
||||
if (!IsValidDeviceId(result.deviceId))
|
||||
if (!FancyZonesUtils::IsValidDeviceId(result.deviceId))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ bool WindowMoveHandler::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DW
|
|||
|
||||
void WindowMoveHandlerPrivate::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::unordered_map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept
|
||||
{
|
||||
if (!IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray) || WindowMoveHandlerUtils::IsCursorTypeIndicatingSizeEvent())
|
||||
if (!FancyZonesUtils::IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray) || WindowMoveHandlerUtils::IsCursorTypeIndicatingSizeEvent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ void WindowMoveHandlerPrivate::MoveSizeUpdate(HMONITOR monitor, POINT const& ptS
|
|||
|
||||
void WindowMoveHandlerPrivate::MoveSizeEnd(HWND window, POINT const& ptScreen, const std::unordered_map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept
|
||||
{
|
||||
if (window != m_windowMoveSize && !IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||
if (window != m_windowMoveSize && !FancyZonesUtils::IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -320,9 +320,9 @@ void WindowMoveHandlerPrivate::MoveSizeEnd(HWND window, POINT const& ptScreen, c
|
|||
{
|
||||
::RemoveProp(window, ZonedWindowProperties::PropertyRestoreSizeID);
|
||||
}
|
||||
else if (!IsWindowMaximized(window))
|
||||
else if (!FancyZonesUtils::IsWindowMaximized(window))
|
||||
{
|
||||
RestoreWindowSize(window);
|
||||
FancyZonesUtils::RestoreWindowSize(window);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
using namespace FancyZonesUtils;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int C_MULTIPLIER = 10000;
|
||||
|
@ -399,7 +401,7 @@ ZoneSet::MoveWindowIntoZoneByDirectionAndPosition(HWND window, HWND windowZone,
|
|||
windowRect.left -= windowZoneRect.left;
|
||||
windowRect.right -= windowZoneRect.left;
|
||||
|
||||
size_t result = ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
size_t result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
if (result < zoneRects.size())
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, windowZone, freeZoneIndices[result]);
|
||||
|
@ -408,8 +410,8 @@ ZoneSet::MoveWindowIntoZoneByDirectionAndPosition(HWND window, HWND windowZone,
|
|||
else if (cycle)
|
||||
{
|
||||
// Try again from the position off the screen in the opposite direction to vkCode
|
||||
windowRect = PrepareRectForCycling(windowRect, windowZoneRect, vkCode);
|
||||
result = ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, windowZoneRect, vkCode);
|
||||
result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
|
||||
if (result < zoneRects.size())
|
||||
{
|
||||
|
|
|
@ -22,6 +22,8 @@ namespace NonLocalizable
|
|||
const wchar_t ToolWindowClassName[] = L"SuperFancyZones_ZoneWindow";
|
||||
}
|
||||
|
||||
using namespace FancyZonesUtils;
|
||||
|
||||
namespace ZoneWindowUtils
|
||||
{
|
||||
std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId)
|
||||
|
|
|
@ -15,9 +15,78 @@ namespace NonLocalizable
|
|||
const wchar_t PowerToysAppFZEditor[] = L"FANCYZONESEDITOR.EXE";
|
||||
}
|
||||
|
||||
typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
||||
namespace
|
||||
{
|
||||
bool HasNoVisibleOwner(HWND window) noexcept
|
||||
{
|
||||
auto owner = GetWindow(window, GW_OWNER);
|
||||
if (owner == nullptr)
|
||||
{
|
||||
return true; // There is no owner at all
|
||||
}
|
||||
if (!IsWindowVisible(owner))
|
||||
{
|
||||
return true; // Owner is invisible
|
||||
}
|
||||
RECT rect;
|
||||
if (!GetWindowRect(owner, &rect))
|
||||
{
|
||||
return false; // Could not get the rect, return true (and filter out the window) just in case
|
||||
}
|
||||
// It is enough that the window is zero-sized in one dimension only.
|
||||
return rect.top == rect.bottom || rect.left == rect.right;
|
||||
}
|
||||
|
||||
FancyZonesUtils::FancyZonesWindowInfo GetFancyZonesWindowInfo(HWND window)
|
||||
{
|
||||
FancyZonesUtils::FancyZonesWindowInfo result;
|
||||
if (GetAncestor(window, GA_ROOT) != window || !IsWindowVisible(window))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
auto style = GetWindowLong(window, GWL_STYLE);
|
||||
auto exStyle = GetWindowLong(window, GWL_EXSTYLE);
|
||||
// WS_POPUP need to have a border or minimize/maximize buttons,
|
||||
// otherwise the window is "not interesting"
|
||||
if ((style & WS_POPUP) == WS_POPUP &&
|
||||
(style & WS_THICKFRAME) == 0 &&
|
||||
(style & WS_MINIMIZEBOX) == 0 &&
|
||||
(style & WS_MAXIMIZEBOX) == 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if ((style & WS_CHILD) == WS_CHILD ||
|
||||
(style & WS_DISABLED) == WS_DISABLED ||
|
||||
(exStyle & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW ||
|
||||
(exStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
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 result;
|
||||
}
|
||||
auto process_path = get_process_path(window);
|
||||
// Check for Cortana:
|
||||
if (strcmp(class_name.data(), "Windows.UI.Core.CoreWindow") == 0 &&
|
||||
process_path.ends_with(L"SearchUI.exe"))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result.processPath = std::move(process_path);
|
||||
result.standardWindow = true;
|
||||
result.noVisibleOwner = HasNoVisibleOwner(window);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace FancyZonesUtils
|
||||
{
|
||||
typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
||||
{
|
||||
UINT dpi{};
|
||||
if (wil::unique_hmodule user32{ LoadLibrary(L"user32.dll") })
|
||||
{
|
||||
|
@ -36,10 +105,10 @@ UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
|||
}
|
||||
|
||||
return (dpi == 0) ? DPIAware::DEFAULT_DPI : dpi;
|
||||
}
|
||||
}
|
||||
|
||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
||||
{
|
||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
||||
{
|
||||
const size_t nMonitors = monitorInfo.size();
|
||||
// blocking[i][j] - whether monitor i blocks monitor j in the ordering, i.e. monitor i should go before monitor j
|
||||
std::vector<std::vector<bool>> blocking(nMonitors, std::vector<bool>(nMonitors, false));
|
||||
|
@ -119,10 +188,10 @@ void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
|||
}
|
||||
|
||||
monitorInfo = std::move(sortedMonitorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void SizeWindowToRect(HWND window, RECT rect) noexcept
|
||||
{
|
||||
void SizeWindowToRect(HWND window, RECT rect) noexcept
|
||||
{
|
||||
WINDOWPLACEMENT placement{};
|
||||
::GetWindowPlacement(window, &placement);
|
||||
|
||||
|
@ -154,34 +223,35 @@ void SizeWindowToRect(HWND window, RECT rect) noexcept
|
|||
// Do it again, allowing Windows to resize the window and set correct scaling
|
||||
// This fixes Issue #365
|
||||
::SetWindowPlacement(window, &placement);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsInterestingWindow(HWND window, const std::vector<std::wstring>& excludedApps) noexcept
|
||||
{
|
||||
auto filtered = get_fancyzones_filtered_window(window);
|
||||
if (!filtered.zonable)
|
||||
bool IsInterestingWindow(HWND window, const std::vector<std::wstring>& excludedApps) noexcept
|
||||
{
|
||||
auto windowInfo = GetFancyZonesWindowInfo(window);
|
||||
auto zonable = windowInfo.standardWindow && windowInfo.noVisibleOwner;
|
||||
if (!zonable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Filter out user specified apps
|
||||
CharUpperBuffW(filtered.process_path.data(), (DWORD)filtered.process_path.length());
|
||||
if (find_app_name_in_path(filtered.process_path, excludedApps))
|
||||
CharUpperBuffW(windowInfo.processPath.data(), (DWORD)windowInfo.processPath.length());
|
||||
if (find_app_name_in_path(windowInfo.processPath, excludedApps))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (find_app_name_in_path(filtered.process_path, { NonLocalizable::PowerToysAppPowerLauncher }))
|
||||
if (find_app_name_in_path(windowInfo.processPath, { NonLocalizable::PowerToysAppPowerLauncher }))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (find_app_name_in_path(filtered.process_path, { NonLocalizable::PowerToysAppFZEditor }))
|
||||
if (find_app_name_in_path(windowInfo.processPath, { NonLocalizable::PowerToysAppFZEditor }))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsWindowMaximized(HWND window) noexcept
|
||||
{
|
||||
bool IsWindowMaximized(HWND window) noexcept
|
||||
{
|
||||
WINDOWPLACEMENT placement{};
|
||||
if (GetWindowPlacement(window, &placement) &&
|
||||
placement.showCmd == SW_SHOWMAXIMIZED)
|
||||
|
@ -189,10 +259,10 @@ bool IsWindowMaximized(HWND window) noexcept
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveWindowSizeAndOrigin(HWND window) noexcept
|
||||
{
|
||||
void SaveWindowSizeAndOrigin(HWND window) noexcept
|
||||
{
|
||||
HANDLE handle = GetPropW(window, ZonedWindowProperties::PropertyRestoreSizeID);
|
||||
if (handle)
|
||||
{
|
||||
|
@ -219,10 +289,10 @@ void SaveWindowSizeAndOrigin(HWND window) noexcept
|
|||
memcpy(&rawData, windowOriginData.data(), sizeof rawData);
|
||||
SetPropW(window, ZonedWindowProperties::PropertyRestoreOriginID, rawData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RestoreWindowSize(HWND window) noexcept
|
||||
{
|
||||
void RestoreWindowSize(HWND window) noexcept
|
||||
{
|
||||
auto windowSizeData = GetPropW(window, ZonedWindowProperties::PropertyRestoreSizeID);
|
||||
if (windowSizeData)
|
||||
{
|
||||
|
@ -242,10 +312,10 @@ void RestoreWindowSize(HWND window) noexcept
|
|||
|
||||
::RemoveProp(window, ZonedWindowProperties::PropertyRestoreSizeID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RestoreWindowOrigin(HWND window) noexcept
|
||||
{
|
||||
void RestoreWindowOrigin(HWND window) noexcept
|
||||
{
|
||||
auto windowOriginData = GetPropW(window, ZonedWindowProperties::PropertyRestoreOriginID);
|
||||
if (windowOriginData)
|
||||
{
|
||||
|
@ -270,16 +340,16 @@ void RestoreWindowOrigin(HWND window) noexcept
|
|||
|
||||
::RemoveProp(window, ZonedWindowProperties::PropertyRestoreOriginID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValidGuid(const std::wstring& str)
|
||||
{
|
||||
bool IsValidGuid(const std::wstring& str)
|
||||
{
|
||||
GUID id;
|
||||
return SUCCEEDED(CLSIDFromString(str.c_str(), &id));
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValidDeviceId(const std::wstring& str)
|
||||
{
|
||||
bool IsValidDeviceId(const std::wstring& str)
|
||||
{
|
||||
std::wstring monitorName;
|
||||
std::wstring temp;
|
||||
std::vector<std::wstring> parts;
|
||||
|
@ -348,10 +418,10 @@ bool IsValidDeviceId(const std::wstring& str)
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept
|
||||
{
|
||||
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept
|
||||
{
|
||||
using complex = std::complex<double>;
|
||||
const size_t invalidResult = zoneRects.size();
|
||||
const double inf = 1e100;
|
||||
|
@ -445,10 +515,10 @@ size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector
|
|||
}
|
||||
|
||||
return closestIdx;
|
||||
}
|
||||
}
|
||||
|
||||
RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) noexcept
|
||||
{
|
||||
RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) noexcept
|
||||
{
|
||||
LONG deltaX = 0, deltaY = 0;
|
||||
switch (vkCode)
|
||||
{
|
||||
|
@ -471,4 +541,5 @@ RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) n
|
|||
windowRect.bottom += deltaY;
|
||||
|
||||
return windowRect;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,21 @@
|
|||
|
||||
#include "gdiplus.h"
|
||||
|
||||
struct Rect
|
||||
namespace FancyZonesUtils
|
||||
{
|
||||
// Window properties relevant to FancyZones
|
||||
struct FancyZonesWindowInfo
|
||||
{
|
||||
// True if from the styles the window looks like a standard window
|
||||
bool standardWindow = false;
|
||||
// True if the window is a top-level window that does not have a visible owner
|
||||
bool noVisibleOwner = false;
|
||||
// Path to the executable owning the window
|
||||
std::wstring processPath;
|
||||
};
|
||||
|
||||
struct Rect
|
||||
{
|
||||
Rect() {}
|
||||
|
||||
Rect(RECT rect) :
|
||||
|
@ -28,31 +41,31 @@ struct Rect
|
|||
int bottom() const { return m_rect.bottom; }
|
||||
int aspectRatio() const { return MulDiv(m_rect.bottom - m_rect.top, 100, m_rect.right - m_rect.left); }
|
||||
|
||||
private:
|
||||
private:
|
||||
RECT m_rect{};
|
||||
};
|
||||
};
|
||||
|
||||
inline void MakeWindowTransparent(HWND window)
|
||||
{
|
||||
inline void MakeWindowTransparent(HWND window)
|
||||
{
|
||||
int const pos = -GetSystemMetrics(SM_CXVIRTUALSCREEN) - 8;
|
||||
if (wil::unique_hrgn hrgn{ CreateRectRgn(pos, 0, (pos + 1), 1) })
|
||||
{
|
||||
DWM_BLURBEHIND bh = { DWM_BB_ENABLE | DWM_BB_BLURREGION, TRUE, hrgn.get(), FALSE };
|
||||
DwmEnableBlurBehindWindow(window, &bh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void InitRGB(_Out_ RGBQUAD* quad, BYTE alpha, COLORREF color)
|
||||
{
|
||||
inline void InitRGB(_Out_ RGBQUAD* quad, BYTE alpha, COLORREF color)
|
||||
{
|
||||
ZeroMemory(quad, sizeof(*quad));
|
||||
quad->rgbReserved = alpha;
|
||||
quad->rgbRed = GetRValue(color) * alpha / 255;
|
||||
quad->rgbGreen = GetGValue(color) * alpha / 255;
|
||||
quad->rgbBlue = GetBValue(color) * alpha / 255;
|
||||
}
|
||||
}
|
||||
|
||||
inline void FillRectARGB(wil::unique_hdc& hdc, RECT const* prcFill, BYTE alpha, COLORREF color, bool blendAlpha)
|
||||
{
|
||||
inline void FillRectARGB(wil::unique_hdc& hdc, RECT const* prcFill, BYTE alpha, COLORREF color, bool blendAlpha)
|
||||
{
|
||||
BITMAPINFO bi;
|
||||
ZeroMemory(&bi, sizeof(bi));
|
||||
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
|
@ -81,10 +94,10 @@ inline void FillRectARGB(wil::unique_hdc& hdc, RECT const* prcFill, BYTE alpha,
|
|||
&bi,
|
||||
DIB_RGB_COLORS,
|
||||
SRCCOPY);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ParseDeviceId(PCWSTR deviceId, PWSTR parsedId, size_t size)
|
||||
{
|
||||
inline void ParseDeviceId(PCWSTR deviceId, PWSTR parsedId, size_t size)
|
||||
{
|
||||
// We're interested in the unique part between the first and last #'s
|
||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||
|
@ -109,21 +122,20 @@ inline void ParseDeviceId(PCWSTR deviceId, PWSTR parsedId, size_t size)
|
|||
{
|
||||
StringCchCopy(parsedId, size, defaultDeviceId.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline BYTE OpacitySettingToAlpha(int opacity)
|
||||
{
|
||||
inline BYTE OpacitySettingToAlpha(int opacity)
|
||||
{
|
||||
return static_cast<BYTE>(opacity * 2.55);
|
||||
}
|
||||
}
|
||||
|
||||
template<RECT MONITORINFO::*member>
|
||||
std::vector<std::pair<HMONITOR, RECT>> GetAllMonitorRects()
|
||||
{
|
||||
template<RECT MONITORINFO::*member>
|
||||
std::vector<std::pair<HMONITOR, RECT>> GetAllMonitorRects()
|
||||
{
|
||||
using result_t = std::vector<std::pair<HMONITOR, RECT>>;
|
||||
result_t result;
|
||||
|
||||
auto enumMonitors = [](HMONITOR monitor, HDC hdc, LPRECT pRect, LPARAM param) -> BOOL
|
||||
{
|
||||
auto enumMonitors = [](HMONITOR monitor, HDC hdc, LPRECT pRect, LPARAM param) -> BOOL {
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
result_t& result = *reinterpret_cast<result_t*>(param);
|
||||
|
@ -137,11 +149,11 @@ std::vector<std::pair<HMONITOR, RECT>> GetAllMonitorRects()
|
|||
|
||||
EnumDisplayMonitors(NULL, NULL, enumMonitors, reinterpret_cast<LPARAM>(&result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template<RECT MONITORINFO::*member>
|
||||
RECT GetAllMonitorsCombinedRect()
|
||||
{
|
||||
template<RECT MONITORINFO::*member>
|
||||
RECT GetAllMonitorsCombinedRect()
|
||||
{
|
||||
auto allMonitors = GetAllMonitorRects<member>();
|
||||
bool empty = true;
|
||||
RECT result{ 0, 0, 0, 0 };
|
||||
|
@ -163,19 +175,21 @@ RECT GetAllMonitorsCombinedRect()
|
|||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
|
||||
void SizeWindowToRect(HWND window, RECT rect) noexcept;
|
||||
|
||||
bool IsInterestingWindow(HWND window, const std::vector<std::wstring>& excludedApps) noexcept;
|
||||
bool IsWindowMaximized(HWND window) noexcept;
|
||||
void SaveWindowSizeAndOrigin(HWND window) noexcept;
|
||||
void RestoreWindowSize(HWND window) noexcept;
|
||||
void RestoreWindowOrigin(HWND window) noexcept;
|
||||
|
||||
bool IsValidGuid(const std::wstring& str);
|
||||
bool IsValidDeviceId(const std::wstring& str);
|
||||
|
||||
RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) noexcept;
|
||||
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept;
|
||||
}
|
||||
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
|
||||
void SizeWindowToRect(HWND window, RECT rect) noexcept;
|
||||
|
||||
bool IsInterestingWindow(HWND window, const std::vector<std::wstring>& excludedApps) noexcept;
|
||||
bool IsWindowMaximized(HWND window) noexcept;
|
||||
void SaveWindowSizeAndOrigin(HWND window) noexcept;
|
||||
void RestoreWindowSize(HWND window) noexcept;
|
||||
void RestoreWindowOrigin(HWND window) noexcept;
|
||||
|
||||
bool IsValidGuid(const std::wstring& str);
|
||||
bool IsValidDeviceId(const std::wstring& str);
|
||||
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept;
|
||||
RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) noexcept;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
using namespace JSONHelpers;
|
||||
using namespace FancyZonesDataTypes;
|
||||
using namespace FancyZonesUtils;
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace FancyZonesUnitTests
|
||||
|
|
|
@ -6,6 +6,8 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
|||
|
||||
namespace FancyZonesUnitTests
|
||||
{
|
||||
using namespace FancyZonesUtils;
|
||||
|
||||
void TestMonitorSetPermutations(const std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
||||
{
|
||||
auto monitorInfoPermutation = monitorInfo;
|
||||
|
|
|
@ -29,6 +29,66 @@ namespace
|
|||
}
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
// Window properties relevant to ShortcutGuide
|
||||
struct ShortcutGuideWindowInfo
|
||||
{
|
||||
HWND hwnd = nullptr; // Handle to the top-level foreground window or nullptr if there is no such window
|
||||
bool snappable = false; // True, if the window can react to Windows Snap keys
|
||||
};
|
||||
|
||||
ShortcutGuideWindowInfo GetShortcutGuideWindowInfo()
|
||||
{
|
||||
ShortcutGuideWindowInfo result;
|
||||
auto active_window = GetForegroundWindow();
|
||||
active_window = GetAncestor(active_window, GA_ROOT);
|
||||
if (!IsWindowVisible(active_window))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
auto style = GetWindowLong(active_window, GWL_STYLE);
|
||||
auto exStyle = GetWindowLong(active_window, GWL_EXSTYLE);
|
||||
if ((style & WS_CHILD) == WS_CHILD ||
|
||||
(style & WS_DISABLED) == WS_DISABLED ||
|
||||
(exStyle & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW ||
|
||||
(exStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
std::array<char, 256> class_name;
|
||||
GetClassNameA(active_window, class_name.data(), static_cast<int>(class_name.size()));
|
||||
if (is_system_window(active_window, class_name.data()))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
static HWND cortana_hwnd = nullptr;
|
||||
if (cortana_hwnd == nullptr)
|
||||
{
|
||||
if (strcmp(class_name.data(), "Windows.UI.Core.CoreWindow") == 0 &&
|
||||
get_process_path(active_window).ends_with(L"SearchUI.exe"))
|
||||
{
|
||||
cortana_hwnd = active_window;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (cortana_hwnd == active_window)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result.hwnd = active_window;
|
||||
// In reality, Windows Snap works if even one of those styles is set
|
||||
// for a window, it is just limited. If there is no WS_MAXIMIZEBOX using
|
||||
// WinKey + Up just won't maximize the window. Similary, without
|
||||
// WS_MINIMIZEBOX the window will not get minimized. A "Save As..." dialog
|
||||
// is a example of such window - it can be snapped to both sides and to
|
||||
// all screen corners, but will not get maximized nor minimized.
|
||||
// For now, since ShortcutGuide can only disable entire "Windows Controls"
|
||||
// group, we require that the window supports all the options.
|
||||
result.snappable = ((style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX) &&
|
||||
((style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX) &&
|
||||
((style & WS_THICKFRAME) == WS_THICKFRAME);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
OverlayWindow::OverlayWindow()
|
||||
|
@ -232,8 +292,8 @@ intptr_t OverlayWindow::signal_event(LowlevelKeyboardEvent* event)
|
|||
|
||||
void OverlayWindow::on_held()
|
||||
{
|
||||
auto filter = get_shortcutguide_filtered_window();
|
||||
winkey_popup->show(filter.hwnd, filter.snappable);
|
||||
auto windowInfo = GetShortcutGuideWindowInfo();
|
||||
winkey_popup->show(windowInfo.hwnd, windowInfo.snappable);
|
||||
}
|
||||
|
||||
void OverlayWindow::on_held_press(DWORD vkCode)
|
||||
|
|
Loading…
Reference in a new issue