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.
|
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 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_classes = { "SysListView32", "WorkerW", "Shell_TrayWnd", "Shell_SecondaryTrayWnd", "Progman" };
|
||||||
static auto system_hwnds = { GetDesktopWindow(), GetShellWindow() };
|
static auto system_hwnds = { GetDesktopWindow(), GetShellWindow() };
|
||||||
for (auto system_hwnd : system_hwnds)
|
for (auto system_hwnd : system_hwnds)
|
||||||
|
@ -81,125 +79,6 @@ static bool is_system_window(HWND hwnd, const char* class_name)
|
||||||
return false;
|
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)
|
int width(const RECT& rect)
|
||||||
{
|
{
|
||||||
return rect.right - rect.left;
|
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);
|
std::optional<RECT> get_window_pos(HWND hwnd);
|
||||||
// Gets mouse position.
|
// Gets mouse position.
|
||||||
std::optional<POINT> get_mouse_pos();
|
std::optional<POINT> get_mouse_pos();
|
||||||
|
// Check if window is part of the shell or the taskbar.
|
||||||
// Test if window can be zoned by FancyZones
|
bool is_system_window(HWND hwnd, const char* class_name);
|
||||||
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();
|
|
||||||
|
|
||||||
// Calculate sizes
|
// Calculate sizes
|
||||||
int width(const RECT& rect);
|
int width(const RECT& rect);
|
||||||
|
|
|
@ -350,7 +350,7 @@ bool FancyZones::ShouldProcessNewWindow(HWND window) noexcept
|
||||||
// that belong to excluded applications list.
|
// that belong to excluded applications list.
|
||||||
if (IsSplashScreen(window) ||
|
if (IsSplashScreen(window) ||
|
||||||
(reinterpret_cast<size_t>(::GetProp(window, ZonedWindowProperties::PropertyMultipleZoneID)) != 0) ||
|
(reinterpret_cast<size_t>(::GetProp(window, ZonedWindowProperties::PropertyMultipleZoneID)) != 0) ||
|
||||||
!IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
!FancyZonesUtils::IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -482,7 +482,7 @@ void OpenWindowOnActiveMonitor(HWND window, HMONITOR monitor) noexcept
|
||||||
if (GetMonitorInfo(monitor, &destMi))
|
if (GetMonitorInfo(monitor, &destMi))
|
||||||
{
|
{
|
||||||
RECT newPosition = FitOnScreen(placement.rcNormalPosition, originMi.rcWork, destMi.rcWork);
|
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;
|
std::vector<std::pair<HMONITOR, RECT>> allMonitors;
|
||||||
|
|
||||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
|
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
|
||||||
allMonitors = GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
allMonitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
||||||
} }).wait();
|
} }).wait();
|
||||||
|
|
||||||
for (auto& [monitor, workArea] : allMonitors)
|
for (auto& [monitor, workArea] : allMonitors)
|
||||||
|
@ -1031,7 +1031,7 @@ void FancyZones::UpdateWindowsPositions() noexcept
|
||||||
void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept
|
void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept
|
||||||
{
|
{
|
||||||
auto window = GetForegroundWindow();
|
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);
|
const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||||
if (monitor)
|
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));
|
bool moved = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */, m_workAreaHandler.GetWorkArea(m_currentDesktopId, current));
|
||||||
if (!moved)
|
if (!moved)
|
||||||
{
|
{
|
||||||
RestoreWindowOrigin(window);
|
FancyZonesUtils::RestoreWindowOrigin(window);
|
||||||
RestoreWindowSize(window);
|
FancyZonesUtils::RestoreWindowSize(window);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1127,7 +1127,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
||||||
current = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
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)
|
if (current && allMonitors.size() > 1 && m_settings->GetSettings()->moveWindowAcrossMonitors)
|
||||||
{
|
{
|
||||||
|
@ -1181,7 +1181,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t chosenIdx = ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
size_t chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||||
|
|
||||||
if (chosenIdx < zoneRects.size())
|
if (chosenIdx < zoneRects.size())
|
||||||
{
|
{
|
||||||
|
@ -1222,9 +1222,9 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RECT combinedRect = GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>();
|
RECT combinedRect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>();
|
||||||
windowRect = PrepareRectForCycling(windowRect, combinedRect, vkCode);
|
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, combinedRect, vkCode);
|
||||||
chosenIdx = ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||||
if (chosenIdx < zoneRects.size())
|
if (chosenIdx < zoneRects.size())
|
||||||
{
|
{
|
||||||
// Moving to another monitor succeeded
|
// Moving to another monitor succeeded
|
||||||
|
@ -1248,7 +1248,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
||||||
bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
||||||
{
|
{
|
||||||
auto window = GetForegroundWindow();
|
auto window = GetForegroundWindow();
|
||||||
if (IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
if (FancyZonesUtils::IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||||
{
|
{
|
||||||
if (m_settings->GetSettings()->moveWindowsBasedOnPosition)
|
if (m_settings->GetSettings()->moveWindowsBasedOnPosition)
|
||||||
{
|
{
|
||||||
|
@ -1335,7 +1335,7 @@ std::vector<HMONITOR> FancyZones::GetMonitorsSorted() noexcept
|
||||||
std::shared_lock readLock(m_lock);
|
std::shared_lock readLock(m_lock);
|
||||||
|
|
||||||
auto monitorInfo = GetRawMonitorData();
|
auto monitorInfo = GetRawMonitorData();
|
||||||
OrderMonitors(monitorInfo);
|
FancyZonesUtils::OrderMonitors(monitorInfo);
|
||||||
std::vector<HMONITOR> output;
|
std::vector<HMONITOR> output;
|
||||||
std::transform(std::begin(monitorInfo), std::end(monitorInfo), std::back_inserter(output), [](const auto& info) { return info.first; });
|
std::transform(std::begin(monitorInfo), std::end(monitorInfo), std::back_inserter(output), [](const auto& info) { return info.first; });
|
||||||
return output;
|
return output;
|
||||||
|
|
|
@ -90,7 +90,7 @@ namespace
|
||||||
data.deviceId = json.GetNamedString(NonLocalizable::DeviceIdStr);
|
data.deviceId = json.GetNamedString(NonLocalizable::DeviceIdStr);
|
||||||
data.zoneSetUuid = json.GetNamedString(NonLocalizable::ZoneSetUuidStr);
|
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;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -250,7 +250,7 @@ namespace JSONHelpers
|
||||||
CustomZoneSetJSON result;
|
CustomZoneSetJSON result;
|
||||||
|
|
||||||
result.uuid = customZoneSet.GetNamedString(NonLocalizable::UuidStr);
|
result.uuid = customZoneSet.GetNamedString(NonLocalizable::UuidStr);
|
||||||
if (!IsValidGuid(result.uuid))
|
if (!FancyZonesUtils::IsValidGuid(result.uuid))
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -314,7 +314,7 @@ namespace JSONHelpers
|
||||||
zoneSetData.uuid = zoneSet.GetNamedString(NonLocalizable::UuidStr);
|
zoneSetData.uuid = zoneSet.GetNamedString(NonLocalizable::UuidStr);
|
||||||
zoneSetData.type = FancyZonesDataTypes::TypeFromString(std::wstring{ zoneSet.GetNamedString(NonLocalizable::TypeStr) });
|
zoneSetData.type = FancyZonesDataTypes::TypeFromString(std::wstring{ zoneSet.GetNamedString(NonLocalizable::TypeStr) });
|
||||||
|
|
||||||
if (!IsValidGuid(zoneSetData.uuid))
|
if (!FancyZonesUtils::IsValidGuid(zoneSetData.uuid))
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -415,7 +415,7 @@ namespace JSONHelpers
|
||||||
DeviceInfoJSON result;
|
DeviceInfoJSON result;
|
||||||
|
|
||||||
result.deviceId = device.GetNamedString(NonLocalizable::DeviceIdStr);
|
result.deviceId = device.GetNamedString(NonLocalizable::DeviceIdStr);
|
||||||
if (!IsValidDeviceId(result.deviceId))
|
if (!FancyZonesUtils::IsValidDeviceId(result.deviceId))
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
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
|
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;
|
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
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -320,9 +320,9 @@ void WindowMoveHandlerPrivate::MoveSizeEnd(HWND window, POINT const& ptScreen, c
|
||||||
{
|
{
|
||||||
::RemoveProp(window, ZonedWindowProperties::PropertyRestoreSizeID);
|
::RemoveProp(window, ZonedWindowProperties::PropertyRestoreSizeID);
|
||||||
}
|
}
|
||||||
else if (!IsWindowMaximized(window))
|
else if (!FancyZonesUtils::IsWindowMaximized(window))
|
||||||
{
|
{
|
||||||
RestoreWindowSize(window);
|
FancyZonesUtils::RestoreWindowSize(window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
using namespace FancyZonesUtils;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr int C_MULTIPLIER = 10000;
|
constexpr int C_MULTIPLIER = 10000;
|
||||||
|
@ -399,7 +401,7 @@ ZoneSet::MoveWindowIntoZoneByDirectionAndPosition(HWND window, HWND windowZone,
|
||||||
windowRect.left -= windowZoneRect.left;
|
windowRect.left -= windowZoneRect.left;
|
||||||
windowRect.right -= 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())
|
if (result < zoneRects.size())
|
||||||
{
|
{
|
||||||
MoveWindowIntoZoneByIndex(window, windowZone, freeZoneIndices[result]);
|
MoveWindowIntoZoneByIndex(window, windowZone, freeZoneIndices[result]);
|
||||||
|
@ -408,8 +410,8 @@ ZoneSet::MoveWindowIntoZoneByDirectionAndPosition(HWND window, HWND windowZone,
|
||||||
else if (cycle)
|
else if (cycle)
|
||||||
{
|
{
|
||||||
// Try again from the position off the screen in the opposite direction to vkCode
|
// Try again from the position off the screen in the opposite direction to vkCode
|
||||||
windowRect = PrepareRectForCycling(windowRect, windowZoneRect, vkCode);
|
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, windowZoneRect, vkCode);
|
||||||
result = ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||||
|
|
||||||
if (result < zoneRects.size())
|
if (result < zoneRects.size())
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,6 +22,8 @@ namespace NonLocalizable
|
||||||
const wchar_t ToolWindowClassName[] = L"SuperFancyZones_ZoneWindow";
|
const wchar_t ToolWindowClassName[] = L"SuperFancyZones_ZoneWindow";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using namespace FancyZonesUtils;
|
||||||
|
|
||||||
namespace ZoneWindowUtils
|
namespace ZoneWindowUtils
|
||||||
{
|
{
|
||||||
std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId)
|
std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId)
|
||||||
|
|
|
@ -15,6 +15,75 @@ namespace NonLocalizable
|
||||||
const wchar_t PowerToysAppFZEditor[] = L"FANCYZONESEDITOR.EXE";
|
const wchar_t PowerToysAppFZEditor[] = L"FANCYZONESEDITOR.EXE";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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*);
|
typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
|
||||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
||||||
{
|
{
|
||||||
|
@ -158,22 +227,23 @@ void SizeWindowToRect(HWND window, RECT rect) noexcept
|
||||||
|
|
||||||
bool IsInterestingWindow(HWND window, const std::vector<std::wstring>& excludedApps) noexcept
|
bool IsInterestingWindow(HWND window, const std::vector<std::wstring>& excludedApps) noexcept
|
||||||
{
|
{
|
||||||
auto filtered = get_fancyzones_filtered_window(window);
|
auto windowInfo = GetFancyZonesWindowInfo(window);
|
||||||
if (!filtered.zonable)
|
auto zonable = windowInfo.standardWindow && windowInfo.noVisibleOwner;
|
||||||
|
if (!zonable)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Filter out user specified apps
|
// Filter out user specified apps
|
||||||
CharUpperBuffW(filtered.process_path.data(), (DWORD)filtered.process_path.length());
|
CharUpperBuffW(windowInfo.processPath.data(), (DWORD)windowInfo.processPath.length());
|
||||||
if (find_app_name_in_path(filtered.process_path, excludedApps))
|
if (find_app_name_in_path(windowInfo.processPath, excludedApps))
|
||||||
{
|
{
|
||||||
return false;
|
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;
|
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 false;
|
||||||
}
|
}
|
||||||
|
@ -472,3 +542,4 @@ RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) n
|
||||||
|
|
||||||
return windowRect;
|
return windowRect;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,19 @@
|
||||||
|
|
||||||
#include "gdiplus.h"
|
#include "gdiplus.h"
|
||||||
|
|
||||||
|
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
|
struct Rect
|
||||||
{
|
{
|
||||||
Rect() {}
|
Rect() {}
|
||||||
|
@ -122,8 +135,7 @@ std::vector<std::pair<HMONITOR, RECT>> GetAllMonitorRects()
|
||||||
using result_t = std::vector<std::pair<HMONITOR, RECT>>;
|
using result_t = std::vector<std::pair<HMONITOR, RECT>>;
|
||||||
result_t result;
|
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;
|
MONITORINFOEX mi;
|
||||||
mi.cbSize = sizeof(mi);
|
mi.cbSize = sizeof(mi);
|
||||||
result_t& result = *reinterpret_cast<result_t*>(param);
|
result_t& result = *reinterpret_cast<result_t*>(param);
|
||||||
|
@ -177,5 +189,7 @@ void RestoreWindowOrigin(HWND window) noexcept;
|
||||||
|
|
||||||
bool IsValidGuid(const std::wstring& str);
|
bool IsValidGuid(const std::wstring& str);
|
||||||
bool IsValidDeviceId(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;
|
RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) noexcept;
|
||||||
|
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept;
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
using namespace JSONHelpers;
|
using namespace JSONHelpers;
|
||||||
using namespace FancyZonesDataTypes;
|
using namespace FancyZonesDataTypes;
|
||||||
|
using namespace FancyZonesUtils;
|
||||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
|
|
||||||
namespace FancyZonesUnitTests
|
namespace FancyZonesUnitTests
|
||||||
|
|
|
@ -6,6 +6,8 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
|
|
||||||
namespace FancyZonesUnitTests
|
namespace FancyZonesUnitTests
|
||||||
{
|
{
|
||||||
|
using namespace FancyZonesUtils;
|
||||||
|
|
||||||
void TestMonitorSetPermutations(const std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
void TestMonitorSetPermutations(const std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
||||||
{
|
{
|
||||||
auto monitorInfoPermutation = monitorInfo;
|
auto monitorInfoPermutation = monitorInfo;
|
||||||
|
|
|
@ -29,6 +29,66 @@ namespace
|
||||||
}
|
}
|
||||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
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()
|
OverlayWindow::OverlayWindow()
|
||||||
|
@ -232,8 +292,8 @@ intptr_t OverlayWindow::signal_event(LowlevelKeyboardEvent* event)
|
||||||
|
|
||||||
void OverlayWindow::on_held()
|
void OverlayWindow::on_held()
|
||||||
{
|
{
|
||||||
auto filter = get_shortcutguide_filtered_window();
|
auto windowInfo = GetShortcutGuideWindowInfo();
|
||||||
winkey_popup->show(filter.hwnd, filter.snappable);
|
winkey_popup->show(windowInfo.hwnd, windowInfo.snappable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWindow::on_held_press(DWORD vkCode)
|
void OverlayWindow::on_held_press(DWORD vkCode)
|
||||||
|
|
Loading…
Reference in a new issue