mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[FancyZones]Modern apps snapping fix, refactor and tests (#29499)
* popup windows check
* use minimize maximize buttons check
* update test utils
* added tests
* define types for easier testing
* changed checks order
* remove option check
* upd test
* remove FZ popup option
* max min buttons -> caption
* calculator test
* updated excluded tests
* add asserts to child window test
* update window creation
* splash screen refactor
* remove hotfix part
* replace style checking functions
* remove no longer necessary check
* tool window check fix
* fix mouse snapping check
* added check and test for non-root window
* spelling
* Revert "remove FZ popup option"
This reverts commit 26732ad683.
* skip child window tests in CI
* remove the option
* remove the constant
* updated tests
This commit is contained in:
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@@ -541,6 +541,7 @@ hashcode
|
|||||||
Hashset
|
Hashset
|
||||||
hashtag
|
hashtag
|
||||||
HASHVAL
|
HASHVAL
|
||||||
|
HASSTRINGS
|
||||||
hbitmap
|
hbitmap
|
||||||
hbm
|
hbm
|
||||||
hbmp
|
hbmp
|
||||||
|
|||||||
@@ -394,15 +394,6 @@ void FancyZones::WindowCreated(HWND window) noexcept
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hotfix
|
|
||||||
// Avoid automatically moving popup windows, as they can be just popup menus.
|
|
||||||
bool isPopup = FancyZonesWindowUtils::IsPopupWindow(window);
|
|
||||||
bool hasThickFrame = FancyZonesWindowUtils::HasThickFrame(window);
|
|
||||||
if (isPopup && !hasThickFrame)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid already stamped (zoned) windows
|
// Avoid already stamped (zoned) windows
|
||||||
const bool isZoned = !FancyZonesWindowProperties::RetrieveZoneIndexProperty(window).empty();
|
const bool isZoned = !FancyZonesWindowProperties::RetrieveZoneIndexProperty(window).empty();
|
||||||
if (isZoned)
|
if (isZoned)
|
||||||
@@ -1005,41 +996,42 @@ void FancyZones::RefreshLayouts() noexcept
|
|||||||
|
|
||||||
bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept
|
bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept
|
||||||
{
|
{
|
||||||
auto window = GetForegroundWindow();
|
if (!FancyZonesSettings::settings().overrideSnapHotkeys)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto window = GetForegroundWindow();
|
||||||
if (!FancyZonesWindowProcessing::IsProcessable(window))
|
if (!FancyZonesWindowProcessing::IsProcessable(window))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FancyZonesSettings::settings().overrideSnapHotkeys)
|
HMONITOR monitor = WorkAreaKeyFromWindow(window);
|
||||||
|
|
||||||
|
auto workArea = m_workAreaConfiguration.GetWorkArea(monitor);
|
||||||
|
if (!workArea)
|
||||||
{
|
{
|
||||||
HMONITOR monitor = WorkAreaKeyFromWindow(window);
|
Logger::error(L"No work area for processing snap hotkey");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto workArea = m_workAreaConfiguration.GetWorkArea(monitor);
|
const auto& layout = workArea->GetLayout();
|
||||||
if (!workArea)
|
if (!layout)
|
||||||
|
{
|
||||||
|
Logger::error(L"No layout for processing snap hotkey");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout->Zones().size() > 0)
|
||||||
|
{
|
||||||
|
if (vkCode == VK_UP || vkCode == VK_DOWN)
|
||||||
{
|
{
|
||||||
Logger::error(L"No work area for processing snap hotkey");
|
return FancyZonesSettings::settings().moveWindowsBasedOnPosition;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
const auto& layout = workArea->GetLayout();
|
|
||||||
if (!layout)
|
|
||||||
{
|
{
|
||||||
Logger::error(L"No layout for processing snap hotkey");
|
return true;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layout->Zones().size() > 0)
|
|
||||||
{
|
|
||||||
if (vkCode == VK_UP || vkCode == VK_DOWN)
|
|
||||||
{
|
|
||||||
return FancyZonesSettings::settings().moveWindowsBasedOnPosition;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,52 +5,68 @@
|
|||||||
#include <FancyZonesLib/VirtualDesktop.h>
|
#include <FancyZonesLib/VirtualDesktop.h>
|
||||||
#include <FancyZonesLib/WindowUtils.h>
|
#include <FancyZonesLib/WindowUtils.h>
|
||||||
|
|
||||||
bool FancyZonesWindowProcessing::IsProcessable(HWND window) noexcept
|
FancyZonesWindowProcessing::ProcessabilityType FancyZonesWindowProcessing::DefineWindowType(HWND window) noexcept
|
||||||
{
|
{
|
||||||
const bool isSplashScreen = FancyZonesWindowUtils::IsSplashScreen(window);
|
|
||||||
if (isSplashScreen)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool windowMinimized = IsIconic(window);
|
const bool windowMinimized = IsIconic(window);
|
||||||
if (windowMinimized)
|
if (windowMinimized)
|
||||||
{
|
{
|
||||||
return false;
|
return ProcessabilityType::Minimized;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool standard = FancyZonesWindowUtils::IsStandardWindow(window);
|
auto style = GetWindowLong(window, GWL_STYLE);
|
||||||
if (!standard)
|
auto exStyle = GetWindowLong(window, GWL_EXSTYLE);
|
||||||
|
|
||||||
|
if (!FancyZonesWindowUtils::HasStyle(style, WS_VISIBLE))
|
||||||
{
|
{
|
||||||
return false;
|
return ProcessabilityType::NotVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
// popup could be the window we don't want to snap: start menu, notification popup, tray window, etc.
|
if (FancyZonesWindowUtils::HasStyle(exStyle, WS_EX_TOOLWINDOW))
|
||||||
// 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;
|
return ProcessabilityType::ToolWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FancyZonesWindowUtils::IsRoot(window))
|
||||||
|
{
|
||||||
|
// child windows such as buttons, combo boxes, etc.
|
||||||
|
return ProcessabilityType::NonRootWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPopup = FancyZonesWindowUtils::HasStyle(style, WS_POPUP);
|
||||||
|
bool hasThickFrame = FancyZonesWindowUtils::HasStyle(style, WS_THICKFRAME);
|
||||||
|
bool hasCaption = FancyZonesWindowUtils::HasStyle(style, WS_CAPTION);
|
||||||
|
if (isPopup && !(hasThickFrame && hasCaption))
|
||||||
|
{
|
||||||
|
// popup windows we want to snap: e.g. Calculator, Telegram
|
||||||
|
// popup windows we don't want to snap: start menu, notification popup, tray window, etc.
|
||||||
|
// WS_CAPTION is used for filtering out menus,
|
||||||
|
// e.g., in Edge "Running as admin" menu when creating a new PowerToys issue.
|
||||||
|
return ProcessabilityType::NonProcessablePopupWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow child windows
|
// allow child windows
|
||||||
auto hasOwner = FancyZonesWindowUtils::HasVisibleOwner(window);
|
auto hasOwner = FancyZonesWindowUtils::HasVisibleOwner(window);
|
||||||
if (hasOwner && !FancyZonesSettings::settings().allowSnapChildWindows)
|
if (hasOwner && !FancyZonesSettings::settings().allowSnapChildWindows)
|
||||||
{
|
{
|
||||||
return false;
|
return ProcessabilityType::ChildWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FancyZonesWindowUtils::IsExcluded(window))
|
if (FancyZonesWindowUtils::IsExcluded(window))
|
||||||
{
|
{
|
||||||
return false;
|
return ProcessabilityType::Excluded;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch between virtual desktops results with posting same windows messages that also indicate
|
// 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.
|
// creation of new window. We need to check if window being processed is on currently active desktop.
|
||||||
if (!VirtualDesktop::instance().IsWindowOnCurrentDesktop(window))
|
if (!VirtualDesktop::instance().IsWindowOnCurrentDesktop(window))
|
||||||
{
|
{
|
||||||
return false;
|
return ProcessabilityType::NotCurrentVirtualDesktop;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return ProcessabilityType::Processable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FancyZonesWindowProcessing::IsProcessable(HWND window) noexcept
|
||||||
|
{
|
||||||
|
return DefineWindowType(window) == ProcessabilityType::Processable;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,20 @@
|
|||||||
|
|
||||||
namespace FancyZonesWindowProcessing
|
namespace FancyZonesWindowProcessing
|
||||||
{
|
{
|
||||||
|
enum class ProcessabilityType
|
||||||
|
{
|
||||||
|
Processable = 0,
|
||||||
|
SplashScreen,
|
||||||
|
Minimized,
|
||||||
|
ToolWindow,
|
||||||
|
NotVisible,
|
||||||
|
NonRootWindow,
|
||||||
|
NonProcessablePopupWindow,
|
||||||
|
ChildWindow,
|
||||||
|
Excluded,
|
||||||
|
NotCurrentVirtualDesktop
|
||||||
|
};
|
||||||
|
|
||||||
|
ProcessabilityType DefineWindowType(HWND window) noexcept;
|
||||||
bool IsProcessable(HWND window) noexcept;
|
bool IsProcessable(HWND window) noexcept;
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,6 @@ namespace NonLocalizable
|
|||||||
const wchar_t ShowOnAllMonitorsID[] = L"fancyzones_show_on_all_monitors";
|
const wchar_t ShowOnAllMonitorsID[] = L"fancyzones_show_on_all_monitors";
|
||||||
const wchar_t SpanZonesAcrossMonitorsID[] = L"fancyzones_span_zones_across_monitors";
|
const wchar_t SpanZonesAcrossMonitorsID[] = L"fancyzones_span_zones_across_monitors";
|
||||||
const wchar_t MakeDraggedWindowTransparentID[] = L"fancyzones_makeDraggedWindowTransparent";
|
const wchar_t MakeDraggedWindowTransparentID[] = L"fancyzones_makeDraggedWindowTransparent";
|
||||||
const wchar_t AllowPopupWindowSnapID[] = L"fancyzones_allowPopupWindowSnap";
|
|
||||||
const wchar_t AllowChildWindowSnapID[] = L"fancyzones_allowChildWindowSnap";
|
const wchar_t AllowChildWindowSnapID[] = L"fancyzones_allowChildWindowSnap";
|
||||||
const wchar_t DisableRoundCornersOnSnapping[] = L"fancyzones_disableRoundCornersOnSnap";
|
const wchar_t DisableRoundCornersOnSnapping[] = L"fancyzones_disableRoundCornersOnSnap";
|
||||||
|
|
||||||
@@ -127,7 +126,6 @@ void FancyZonesSettings::LoadSettings()
|
|||||||
SetBoolFlag(values, NonLocalizable::WindowSwitchingToggleID, SettingId::WindowSwitching, m_settings.windowSwitching);
|
SetBoolFlag(values, NonLocalizable::WindowSwitchingToggleID, SettingId::WindowSwitching, m_settings.windowSwitching);
|
||||||
SetBoolFlag(values, NonLocalizable::SystemThemeID, SettingId::SystemTheme, m_settings.systemTheme);
|
SetBoolFlag(values, NonLocalizable::SystemThemeID, SettingId::SystemTheme, m_settings.systemTheme);
|
||||||
SetBoolFlag(values, NonLocalizable::ShowZoneNumberID, SettingId::ShowZoneNumber, m_settings.showZoneNumber);
|
SetBoolFlag(values, NonLocalizable::ShowZoneNumberID, SettingId::ShowZoneNumber, m_settings.showZoneNumber);
|
||||||
SetBoolFlag(values, NonLocalizable::AllowPopupWindowSnapID, SettingId::AllowSnapPopupWindows, m_settings.allowSnapPopupWindows);
|
|
||||||
SetBoolFlag(values, NonLocalizable::AllowChildWindowSnapID, SettingId::AllowSnapChildWindows, m_settings.allowSnapChildWindows);
|
SetBoolFlag(values, NonLocalizable::AllowChildWindowSnapID, SettingId::AllowSnapChildWindows, m_settings.allowSnapChildWindows);
|
||||||
SetBoolFlag(values, NonLocalizable::DisableRoundCornersOnSnapping, SettingId::DisableRoundCornersOnSnapping, m_settings.disableRoundCorners);
|
SetBoolFlag(values, NonLocalizable::DisableRoundCornersOnSnapping, SettingId::DisableRoundCornersOnSnapping, m_settings.disableRoundCorners);
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ struct Settings
|
|||||||
bool makeDraggedWindowTransparent = true;
|
bool makeDraggedWindowTransparent = true;
|
||||||
bool systemTheme = true;
|
bool systemTheme = true;
|
||||||
bool showZoneNumber = true;
|
bool showZoneNumber = true;
|
||||||
bool allowSnapPopupWindows = false;
|
|
||||||
bool allowSnapChildWindows = false;
|
bool allowSnapChildWindows = false;
|
||||||
bool disableRoundCorners = false;
|
bool disableRoundCorners = false;
|
||||||
std::wstring zoneColor = L"#AACDFF";
|
std::wstring zoneColor = L"#AACDFF";
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ enum class SettingId
|
|||||||
NextTabHotkey,
|
NextTabHotkey,
|
||||||
PrevTabHotkey,
|
PrevTabHotkey,
|
||||||
ExcludedApps,
|
ExcludedApps,
|
||||||
AllowSnapPopupWindows,
|
|
||||||
AllowSnapChildWindows,
|
AllowSnapChildWindows,
|
||||||
DisableRoundCornersOnSnapping,
|
DisableRoundCornersOnSnapping,
|
||||||
};
|
};
|
||||||
@@ -20,8 +20,6 @@ WindowMouseSnap::WindowMouseSnap(HWND window, const std::unordered_map<HMONITOR,
|
|||||||
m_snappingMode(false)
|
m_snappingMode(false)
|
||||||
{
|
{
|
||||||
m_windowProperties.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window);
|
m_windowProperties.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window);
|
||||||
m_windowProperties.isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window) &&
|
|
||||||
(!FancyZonesWindowUtils::IsPopupWindow(m_window) || FancyZonesSettings::settings().allowSnapPopupWindows);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowMouseSnap::~WindowMouseSnap()
|
WindowMouseSnap::~WindowMouseSnap()
|
||||||
@@ -31,8 +29,7 @@ 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 (FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent() || !FancyZonesWindowProcessing::IsProcessable(window))
|
||||||
FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent())
|
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -111,13 +108,8 @@ void WindowMouseSnap::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bo
|
|||||||
void WindowMouseSnap::MoveSizeEnd()
|
void WindowMouseSnap::MoveSizeEnd()
|
||||||
{
|
{
|
||||||
if (m_snappingMode)
|
if (m_snappingMode)
|
||||||
{
|
{
|
||||||
const bool hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window);
|
if (FancyZonesWindowUtils::IsWindowMaximized(m_window))
|
||||||
const bool isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window);
|
|
||||||
|
|
||||||
if ((isStandardWindow == false && hasNoVisibleOwner == true &&
|
|
||||||
m_windowProperties.isStandardWindow == true && m_windowProperties.hasNoVisibleOwner == true) ||
|
|
||||||
FancyZonesWindowUtils::IsWindowMaximized(m_window))
|
|
||||||
{
|
{
|
||||||
// Abort the zoning, this is a Chromium based tab that is merged back with an existing window
|
// Abort the zoning, this is a Chromium based tab that is merged back with an existing window
|
||||||
// or if the window is maximized by Windows when the cursor hits the screen top border
|
// or if the window is maximized by Windows when the cursor hits the screen top border
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ private:
|
|||||||
|
|
||||||
struct WindowProperties
|
struct WindowProperties
|
||||||
{
|
{
|
||||||
// True if from the styles the window looks like a standard window
|
|
||||||
bool isStandardWindow = false;
|
|
||||||
// True if the window is a top-level window that does not have a visible owner
|
// True if the window is a top-level window that does not have a visible owner
|
||||||
bool hasNoVisibleOwner = false;
|
bool hasNoVisibleOwner = false;
|
||||||
// Properties to restore after dragging
|
// Properties to restore after dragging
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
namespace NonLocalizable
|
namespace NonLocalizable
|
||||||
{
|
{
|
||||||
const wchar_t PowerToysAppFZEditor[] = L"POWERTOYS.FANCYZONESEDITOR.EXE";
|
const wchar_t PowerToysAppFZEditor[] = L"POWERTOYS.FANCYZONESEDITOR.EXE";
|
||||||
const wchar_t SplashClassName[] = L"MsoSplash";
|
const char SplashClassName[] = "MsoSplash";
|
||||||
const wchar_t CoreWindow[] = L"Windows.UI.Core.CoreWindow";
|
const wchar_t CoreWindow[] = L"Windows.UI.Core.CoreWindow";
|
||||||
const wchar_t SearchUI[] = L"SearchUI.exe";
|
const wchar_t SearchUI[] = L"SearchUI.exe";
|
||||||
const wchar_t SystemAppsFolder[] = L"SYSTEMAPPS";
|
const wchar_t SystemAppsFolder[] = L"SYSTEMAPPS";
|
||||||
@@ -122,17 +122,6 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FancyZonesWindowUtils::IsSplashScreen(HWND window)
|
|
||||||
{
|
|
||||||
wchar_t className[MAX_PATH];
|
|
||||||
if (GetClassName(window, className, MAX_PATH) == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wcscmp(NonLocalizable::SplashClassName, className) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FancyZonesWindowUtils::IsWindowMaximized(HWND window) noexcept
|
bool FancyZonesWindowUtils::IsWindowMaximized(HWND window) noexcept
|
||||||
{
|
{
|
||||||
WINDOWPLACEMENT placement{};
|
WINDOWPLACEMENT placement{};
|
||||||
@@ -164,44 +153,9 @@ bool FancyZonesWindowUtils::HasVisibleOwner(HWND window) noexcept
|
|||||||
return rect.top != rect.bottom && rect.left != rect.right;
|
return rect.top != rect.bottom && rect.left != rect.right;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FancyZonesWindowUtils::IsStandardWindow(HWND window)
|
bool FancyZonesWindowUtils::IsRoot(HWND window) noexcept
|
||||||
{
|
{
|
||||||
// True if from the styles the window looks like a standard window
|
return GetAncestor(window, GA_ROOT) == window;
|
||||||
|
|
||||||
if (GetAncestor(window, GA_ROOT) != window)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto style = GetWindowLong(window, GWL_STYLE);
|
|
||||||
auto exStyle = GetWindowLong(window, GWL_EXSTYLE);
|
|
||||||
|
|
||||||
bool isToolWindow = (exStyle & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW;
|
|
||||||
bool isVisible = (style & WS_VISIBLE) == WS_VISIBLE;
|
|
||||||
if (isToolWindow || !isVisible)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FancyZonesWindowUtils::IsPopupWindow(HWND window) noexcept
|
|
||||||
{
|
|
||||||
auto style = GetWindowLong(window, GWL_STYLE);
|
|
||||||
return ((style & WS_POPUP) == WS_POPUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FancyZonesWindowUtils::HasThickFrame(HWND window) noexcept
|
|
||||||
{
|
|
||||||
auto style = GetWindowLong(window, GWL_STYLE);
|
|
||||||
return ((style & WS_THICKFRAME) == WS_THICKFRAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FancyZonesWindowUtils::HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept
|
|
||||||
{
|
|
||||||
auto style = GetWindowLong(window, GWL_STYLE);
|
|
||||||
return ((style & WS_THICKFRAME) == WS_THICKFRAME && (style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX && (style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window)
|
bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window)
|
||||||
@@ -261,9 +215,14 @@ bool FancyZonesWindowUtils::IsExcludedByDefault(const HWND& hwnd, std::wstring&
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<char, 256> class_name;
|
std::array<char, 256> className;
|
||||||
GetClassNameA(hwnd, class_name.data(), static_cast<int>(class_name.size()));
|
GetClassNameA(hwnd, className.data(), static_cast<int>(className.size()));
|
||||||
if (is_system_window(hwnd, class_name.data()))
|
if (is_system_window(hwnd, className.data()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(NonLocalizable::SplashClassName, className.data()) == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,15 @@
|
|||||||
|
|
||||||
namespace FancyZonesWindowUtils
|
namespace FancyZonesWindowUtils
|
||||||
{
|
{
|
||||||
bool IsSplashScreen(HWND window);
|
|
||||||
bool IsWindowMaximized(HWND window) noexcept;
|
bool IsWindowMaximized(HWND window) noexcept;
|
||||||
bool HasVisibleOwner(HWND window) noexcept;
|
bool HasVisibleOwner(HWND window) noexcept;
|
||||||
bool IsStandardWindow(HWND window);
|
bool IsRoot(HWND window) noexcept;
|
||||||
bool IsPopupWindow(HWND window) noexcept;
|
|
||||||
bool HasThickFrame(HWND window) noexcept;
|
constexpr bool HasStyle(LONG style, LONG styleToCheck) noexcept
|
||||||
bool HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept;
|
{
|
||||||
|
return ((style & styleToCheck) == styleToCheck);
|
||||||
|
}
|
||||||
|
|
||||||
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 IsExcluded(HWND window);
|
||||||
|
|||||||
@@ -57,7 +57,6 @@
|
|||||||
#define SpanZonesAcrossMonitorsKey "SpanZonesAcrossMonitors"
|
#define SpanZonesAcrossMonitorsKey "SpanZonesAcrossMonitors"
|
||||||
#define MakeDraggedWindowTransparentKey "MakeDraggedWindowTransparent"
|
#define MakeDraggedWindowTransparentKey "MakeDraggedWindowTransparent"
|
||||||
#define AllowSnapChildWindows "AllowSnapChildWindows"
|
#define AllowSnapChildWindows "AllowSnapChildWindows"
|
||||||
#define AllowSnapPopupWindows "AllowSnapPopupWindows"
|
|
||||||
#define DisableRoundCornersOnSnapping "DisableRoundCornersOnSnapping"
|
#define DisableRoundCornersOnSnapping "DisableRoundCornersOnSnapping"
|
||||||
#define ZoneColorKey "ZoneColor"
|
#define ZoneColorKey "ZoneColor"
|
||||||
#define ZoneBorderColorKey "ZoneBorderColor"
|
#define ZoneBorderColorKey "ZoneBorderColor"
|
||||||
@@ -323,7 +322,6 @@ void Trace::SettingsTelemetry(const Settings& settings) noexcept
|
|||||||
TraceLoggingBoolean(settings.spanZonesAcrossMonitors, SpanZonesAcrossMonitorsKey),
|
TraceLoggingBoolean(settings.spanZonesAcrossMonitors, SpanZonesAcrossMonitorsKey),
|
||||||
TraceLoggingBoolean(settings.makeDraggedWindowTransparent, MakeDraggedWindowTransparentKey),
|
TraceLoggingBoolean(settings.makeDraggedWindowTransparent, MakeDraggedWindowTransparentKey),
|
||||||
TraceLoggingBoolean(settings.allowSnapChildWindows, AllowSnapChildWindows),
|
TraceLoggingBoolean(settings.allowSnapChildWindows, AllowSnapChildWindows),
|
||||||
TraceLoggingBoolean(settings.allowSnapPopupWindows, AllowSnapPopupWindows),
|
|
||||||
TraceLoggingBoolean(settings.disableRoundCorners, DisableRoundCornersOnSnapping),
|
TraceLoggingBoolean(settings.disableRoundCorners, DisableRoundCornersOnSnapping),
|
||||||
TraceLoggingWideString(settings.zoneColor.c_str(), ZoneColorKey),
|
TraceLoggingWideString(settings.zoneColor.c_str(), ZoneColorKey),
|
||||||
TraceLoggingWideString(settings.zoneBorderColor.c_str(), ZoneBorderColorKey),
|
TraceLoggingWideString(settings.zoneBorderColor.c_str(), ZoneBorderColorKey),
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
<ClCompile Include="Util.Spec.cpp" />
|
<ClCompile Include="Util.Spec.cpp" />
|
||||||
<ClCompile Include="Util.cpp" />
|
<ClCompile Include="Util.cpp" />
|
||||||
<ClCompile Include="WindowKeyboardSnap.Spec.cpp" />
|
<ClCompile Include="WindowKeyboardSnap.Spec.cpp" />
|
||||||
|
<ClCompile Include="WindowProcessingTests.Spec.cpp" />
|
||||||
<ClCompile Include="WorkArea.Spec.cpp" />
|
<ClCompile Include="WorkArea.Spec.cpp" />
|
||||||
<ClCompile Include="WorkAreaIdTests.Spec.cpp" />
|
<ClCompile Include="WorkAreaIdTests.Spec.cpp" />
|
||||||
<ClCompile Include="Zone.Spec.cpp" />
|
<ClCompile Include="Zone.Spec.cpp" />
|
||||||
|
|||||||
@@ -66,6 +66,9 @@
|
|||||||
<ClCompile Include="WindowKeyboardSnap.Spec.cpp">
|
<ClCompile Include="WindowKeyboardSnap.Spec.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="WindowProcessingTests.Spec.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="pch.h">
|
<ClInclude Include="pch.h">
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ namespace Mocks
|
|||||||
class HwndCreator
|
class HwndCreator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HwndCreator(const std::wstring& title = L"");
|
HwndCreator(const std::wstring& title, const std::wstring& className, DWORD exStyle, DWORD style, HWND parentWindow);
|
||||||
|
|
||||||
~HwndCreator();
|
~HwndCreator();
|
||||||
|
|
||||||
HWND operator()(HINSTANCE hInst);
|
HWND operator()(HINSTANCE hInst);
|
||||||
@@ -20,23 +19,30 @@ namespace Mocks
|
|||||||
inline HINSTANCE getHInstance() const { return m_hInst; }
|
inline HINSTANCE getHInstance() const { return m_hInst; }
|
||||||
inline const std::wstring& getTitle() const { return m_windowTitle; }
|
inline const std::wstring& getTitle() const { return m_windowTitle; }
|
||||||
inline const std::wstring& getWindowClassName() const { return m_windowClassName; }
|
inline const std::wstring& getWindowClassName() const { return m_windowClassName; }
|
||||||
|
inline DWORD getExStyle() const noexcept { return m_exStyle; }
|
||||||
|
inline DWORD getStyle() const noexcept { return m_style; }
|
||||||
|
inline HWND getParentWindow() const noexcept { return m_parentWindow; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::wstring m_windowTitle;
|
std::wstring m_windowTitle;
|
||||||
std::wstring m_windowClassName;
|
std::wstring m_windowClassName;
|
||||||
|
DWORD m_exStyle{ 0 };
|
||||||
|
DWORD m_style{ 0 };
|
||||||
|
HWND m_parentWindow{ NULL };
|
||||||
|
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::condition_variable m_conditionVar;
|
std::condition_variable m_conditionVar;
|
||||||
bool m_conditionFlag;
|
bool m_conditionFlag;
|
||||||
HANDLE m_thread;
|
HANDLE m_thread;
|
||||||
|
|
||||||
HINSTANCE m_hInst;
|
HINSTANCE m_hInst{};
|
||||||
HWND m_hWnd;
|
HWND m_hWnd;
|
||||||
};
|
};
|
||||||
|
|
||||||
HWND WindowCreate(HINSTANCE hInst)
|
HWND WindowCreate(HINSTANCE hInst, const std::wstring& title /*= L""*/, const std::wstring& className /*= L""*/,
|
||||||
|
DWORD exStyle, DWORD style, HWND parentWindow)
|
||||||
{
|
{
|
||||||
return HwndCreator()(hInst);
|
return HwndCreator(title, className, exStyle, style, parentWindow)(hInst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +91,21 @@ DWORD WINAPI ThreadProc(LPVOID lpParam)
|
|||||||
|
|
||||||
if (RegisterDLLWindowClass(creator->getWindowClassName().c_str(), creator) != 0)
|
if (RegisterDLLWindowClass(creator->getWindowClassName().c_str(), creator) != 0)
|
||||||
{
|
{
|
||||||
auto hWnd = CreateWindowEx(0, creator->getWindowClassName().c_str(), creator->getTitle().c_str(), WS_EX_APPWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, nullptr, nullptr, creator->getHInstance(), NULL);
|
auto hWnd = CreateWindowEx(creator->getExStyle(),
|
||||||
SetWindowPos(hWnd, HWND_TOPMOST, 10, 10, 100, 100, SWP_SHOWWINDOW);
|
creator->getWindowClassName().c_str(),
|
||||||
|
creator->getTitle().c_str(),
|
||||||
|
creator->getStyle(),
|
||||||
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
|
creator->getParentWindow(),
|
||||||
|
nullptr,
|
||||||
|
creator->getHInstance(),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
ShowWindow(hWnd, SW_SHOW);
|
||||||
|
// wait after ShowWindow to make sure that it's finished and shown
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
creator->setHwnd(hWnd);
|
creator->setHwnd(hWnd);
|
||||||
creator->setCondition(true);
|
creator->setCondition(true);
|
||||||
|
|
||||||
@@ -95,8 +114,6 @@ DWORD WINAPI ThreadProc(LPVOID lpParam)
|
|||||||
TranslateMessage(&messages);
|
TranslateMessage(&messages);
|
||||||
DispatchMessage(&messages);
|
DispatchMessage(&messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
creator->setHwnd(hWnd);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -108,8 +125,16 @@ DWORD WINAPI ThreadProc(LPVOID lpParam)
|
|||||||
|
|
||||||
namespace Mocks
|
namespace Mocks
|
||||||
{
|
{
|
||||||
HwndCreator::HwndCreator(const std::wstring& title) :
|
HwndCreator::HwndCreator(const std::wstring& title, const std::wstring& className, DWORD exStyle, DWORD style, HWND parentWindow) :
|
||||||
m_windowTitle(title), m_windowClassName(std::to_wstring(++s_classId)), m_conditionFlag(false), m_thread(nullptr), m_hInst(HINSTANCE{}), m_hWnd(nullptr)
|
m_windowTitle(title),
|
||||||
|
m_windowClassName(className.empty() ? std::to_wstring(++s_classId) : className),
|
||||||
|
m_exStyle(exStyle),
|
||||||
|
m_style(style),
|
||||||
|
m_parentWindow(parentWindow),
|
||||||
|
m_conditionFlag(false),
|
||||||
|
m_thread(nullptr),
|
||||||
|
m_hInst(HINSTANCE{}),
|
||||||
|
m_hWnd(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,8 @@ namespace Mocks
|
|||||||
return reinterpret_cast<HINSTANCE>(++s_nextInstance);
|
return reinterpret_cast<HINSTANCE>(++s_nextInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
HWND WindowCreate(HINSTANCE hInst);
|
HWND WindowCreate(HINSTANCE hInst, const std::wstring& title = L"", const std::wstring& className = L""
|
||||||
|
, DWORD exStyle = 0, DWORD style = 0, HWND parentWindow = nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Helpers
|
namespace Helpers
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include <FancyZonesLib/FancyZonesWindowProcessing.h>
|
||||||
|
#include <FancyZonesLib/Settings.h>
|
||||||
|
#include <FancyZonesLib/WindowUtils.h>
|
||||||
|
|
||||||
|
#include "Util.h"
|
||||||
|
|
||||||
|
#include <CppUnitTestLogger.h>
|
||||||
|
|
||||||
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
|
|
||||||
|
namespace Microsoft
|
||||||
|
{
|
||||||
|
namespace VisualStudio
|
||||||
|
{
|
||||||
|
namespace CppUnitTestFramework
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
std::wstring ToString<FancyZonesWindowProcessing::ProcessabilityType>(const FancyZonesWindowProcessing::ProcessabilityType& type)
|
||||||
|
{
|
||||||
|
return std::to_wstring((int)type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace FancyZonesUnitTests
|
||||||
|
{
|
||||||
|
TEST_CLASS (WindowProcessingUnitTests)
|
||||||
|
{
|
||||||
|
HINSTANCE hInst{};
|
||||||
|
|
||||||
|
TEST_METHOD_CLEANUP(CleanUp)
|
||||||
|
{
|
||||||
|
FancyZonesSettings::instance().SetSettings(Settings{});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (MinimizedWindow)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(hInst);
|
||||||
|
ShowWindow(window, SW_MINIMIZE);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // let ShowWindow finish
|
||||||
|
Assert::IsTrue(IsIconic(window));
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::Minimized, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (ToolWindow)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"", WS_EX_TOOLWINDOW);
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::ToolWindow, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (InvisibleWindow)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(hInst);
|
||||||
|
ShowWindow(window, SW_HIDE);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // let ShowWindow finish
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::NotVisible, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(NonRootWindow)
|
||||||
|
{
|
||||||
|
HWND rootWindow = Mocks::WindowCreate(hInst, L"RootWindow", L"", 0, WS_TILEDWINDOW | WS_CLIPCHILDREN);
|
||||||
|
Assert::IsTrue(FancyZonesWindowUtils::IsRoot(rootWindow));
|
||||||
|
|
||||||
|
HWND window = CreateWindow(WC_COMBOBOX, TEXT(""), CBS_DROPDOWN | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE, 0, 0, 10, 10, rootWindow, NULL, hInst, NULL);
|
||||||
|
Assert::IsFalse(FancyZonesWindowUtils::IsRoot(window));
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::NonRootWindow, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (Popup_App)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"", 0, WS_TILEDWINDOW | WS_POPUP);
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::Processable, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsTrue(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (Popup_Menu)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"", 0, WS_POPUP | WS_TILED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::NonProcessablePopupWindow, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (Popup_MenuEdge)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"", 0, WS_POPUP | WS_TILED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_THICKFRAME | WS_SIZEBOX);
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::NonProcessablePopupWindow, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (Popup_Calculator)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"", 0, WS_BORDER | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_GROUP | WS_POPUP | WS_POPUPWINDOW | WS_SIZEBOX | WS_TABSTOP | WS_TILEDWINDOW);
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::Processable, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsTrue(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (Popup_CalculatorTopmost)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"", 0, WS_BORDER | WS_CAPTION | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_OVERLAPPED | WS_POPUP | WS_POPUPWINDOW | WS_SIZEBOX | WS_SYSMENU | WS_THICKFRAME);
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::Processable, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsTrue(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (ChildWindow_OptionDisabled)
|
||||||
|
{
|
||||||
|
FancyZonesSettings::instance().SetSettings(Settings{ .allowSnapChildWindows = false });
|
||||||
|
HWND parentWindow = Mocks::WindowCreate(hInst, L"", L"", 0, WS_TILEDWINDOW);
|
||||||
|
if (!IsWindowVisible(parentWindow))
|
||||||
|
{
|
||||||
|
// skip the test if the parent window isn't visible.
|
||||||
|
// test can run locally, but will fail in CI because of the configuration
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"", 0, 0, parentWindow);
|
||||||
|
Assert::IsTrue(IsWindowVisible(window), L"Child window not visible");
|
||||||
|
Assert::IsTrue(FancyZonesWindowUtils::HasVisibleOwner(window), L"Child window doesn't have visible owner");
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::ChildWindow, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (ChildWindow_OptionEnabled)
|
||||||
|
{
|
||||||
|
FancyZonesSettings::instance().SetSettings(Settings{ .allowSnapChildWindows = true });
|
||||||
|
HWND parentWindow = Mocks::WindowCreate(hInst, L"", L"", 0, WS_TILEDWINDOW);
|
||||||
|
if (!IsWindowVisible(parentWindow))
|
||||||
|
{
|
||||||
|
// skip the test if the parent window isn't visible.
|
||||||
|
// test can run locally, but will fail in CI because of the configuration
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"", 0, 0, parentWindow);
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::Processable, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsTrue(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (ExcludedApp_ByDefault)
|
||||||
|
{
|
||||||
|
// set class from the excluded list
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"SysListView32");
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::Excluded, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (ExcludedApp_ByDefault_SplashScreen)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"MsoSplash");
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::Excluded, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (ExcludedApp_ByUser)
|
||||||
|
{
|
||||||
|
// case sensitive, should be uppercase
|
||||||
|
FancyZonesSettings::instance().SetSettings(Settings{ .excludedAppsArray = { L"TEST_EXCLUDED" } });
|
||||||
|
|
||||||
|
// exclude by window title
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"Test_Excluded");
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::Excluded, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD (ProcessableWindow)
|
||||||
|
{
|
||||||
|
HWND window = Mocks::WindowCreate(hInst, L"", L"", 0, WS_TILEDWINDOW);
|
||||||
|
|
||||||
|
Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::Processable, FancyZonesWindowProcessing::DefineWindowType(window));
|
||||||
|
Assert::IsTrue(FancyZonesWindowProcessing::IsProcessable(window));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -249,7 +249,7 @@ namespace FancyZonesUnitTests
|
|||||||
AppliedLayouts::instance().ApplyLayout(m_workAreaId, layout);
|
AppliedLayouts::instance().ApplyLayout(m_workAreaId, layout);
|
||||||
|
|
||||||
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
|
||||||
const auto window = Mocks::WindowCreate(m_hInst);
|
const auto window = Mocks::WindowCreate(m_hInst, L"", L"", 0, WS_THICKFRAME);
|
||||||
|
|
||||||
SetWindowPos(window, nullptr, 150, 150, 450, 550, SWP_SHOWWINDOW);
|
SetWindowPos(window, nullptr, 150, 150, 450, 550, SWP_SHOWWINDOW);
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ namespace ViewModelTests
|
|||||||
Assert.AreEqual(originalSettings.Properties.FancyzonesZoneHighlightColor.Value, viewModel.ZoneHighlightColor);
|
Assert.AreEqual(originalSettings.Properties.FancyzonesZoneHighlightColor.Value, viewModel.ZoneHighlightColor);
|
||||||
Assert.AreEqual(originalSettings.Properties.FancyzonesZoneSetChangeMoveWindows.Value, viewModel.ZoneSetChangeMoveWindows);
|
Assert.AreEqual(originalSettings.Properties.FancyzonesZoneSetChangeMoveWindows.Value, viewModel.ZoneSetChangeMoveWindows);
|
||||||
Assert.AreEqual(originalSettings.Properties.UseCursorposEditorStartupscreen.Value, viewModel.UseCursorPosEditorStartupScreen);
|
Assert.AreEqual(originalSettings.Properties.UseCursorposEditorStartupscreen.Value, viewModel.UseCursorPosEditorStartupScreen);
|
||||||
Assert.AreEqual(originalSettings.Properties.FancyzonesAllowPopupWindowSnap.Value, viewModel.AllowPopupWindowSnap);
|
|
||||||
Assert.AreEqual(originalSettings.Properties.FancyzonesAllowChildWindowSnap.Value, viewModel.AllowChildWindowSnap);
|
Assert.AreEqual(originalSettings.Properties.FancyzonesAllowChildWindowSnap.Value, viewModel.AllowChildWindowSnap);
|
||||||
Assert.AreEqual(originalSettings.Properties.FancyzonesDisableRoundCornersOnSnap.Value, viewModel.DisableRoundCornersOnWindowSnap);
|
Assert.AreEqual(originalSettings.Properties.FancyzonesDisableRoundCornersOnSnap.Value, viewModel.DisableRoundCornersOnWindowSnap);
|
||||||
|
|
||||||
@@ -449,24 +448,6 @@ namespace ViewModelTests
|
|||||||
Assert.AreEqual(expected, actual);
|
Assert.AreEqual(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void AllowPopupWindowsToSnapShouldSetValue2TrueWhenSuccessful()
|
|
||||||
{
|
|
||||||
Mock<SettingsUtils> mockSettingsUtils = new Mock<SettingsUtils>();
|
|
||||||
|
|
||||||
// arrange
|
|
||||||
FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository<FancyZonesSettings>.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName);
|
|
||||||
Assert.IsFalse(viewModel.AllowPopupWindowSnap); // check if value was initialized to false.
|
|
||||||
|
|
||||||
// act
|
|
||||||
viewModel.AllowPopupWindowSnap = true;
|
|
||||||
|
|
||||||
// assert
|
|
||||||
var expected = viewModel.AllowPopupWindowSnap;
|
|
||||||
var actual = SettingsRepository<FancyZonesSettings>.GetInstance(mockFancyZonesSettingsUtils.Object).SettingsConfig.Properties.FancyzonesAllowPopupWindowSnap.Value;
|
|
||||||
Assert.AreEqual(expected, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void DisableRoundCornersOnSnapShouldSetValue2TrueWhenSuccessful()
|
public void DisableRoundCornersOnSnapShouldSetValue2TrueWhenSuccessful()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -150,12 +150,6 @@
|
|||||||
<controls:SettingsCard ContentAlignment="Left">
|
<controls:SettingsCard ContentAlignment="Left">
|
||||||
<CheckBox x:Uid="FancyZones_MakeDraggedWindowTransparentCheckBoxControl" IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.MakeDraggedWindowsTransparent}" />
|
<CheckBox x:Uid="FancyZones_MakeDraggedWindowTransparentCheckBoxControl" IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.MakeDraggedWindowsTransparent}" />
|
||||||
</controls:SettingsCard>
|
</controls:SettingsCard>
|
||||||
<controls:SettingsCard ContentAlignment="Left">
|
|
||||||
<custom:CheckBoxWithDescriptionControl
|
|
||||||
x:Uid="FancyZones_AllowPopupWindowSnap"
|
|
||||||
Margin="0,0,0,6"
|
|
||||||
IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.AllowPopupWindowSnap}" />
|
|
||||||
</controls:SettingsCard>
|
|
||||||
<controls:SettingsCard ContentAlignment="Left">
|
<controls:SettingsCard ContentAlignment="Left">
|
||||||
<CheckBox x:Uid="FancyZones_AllowChildWindowSnap" IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.AllowChildWindowSnap}" />
|
<CheckBox x:Uid="FancyZones_AllowChildWindowSnap" IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.AllowChildWindowSnap}" />
|
||||||
</controls:SettingsCard>
|
</controls:SettingsCard>
|
||||||
|
|||||||
@@ -629,24 +629,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AllowPopupWindowSnap
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _allowPopupWindowSnap;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != _allowPopupWindowSnap)
|
|
||||||
{
|
|
||||||
_allowPopupWindowSnap = value;
|
|
||||||
Settings.Properties.FancyzonesAllowPopupWindowSnap.Value = value;
|
|
||||||
NotifyPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AllowChildWindowSnap
|
public bool AllowChildWindowSnap
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|||||||
Reference in New Issue
Block a user