diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 6781e71eb4..26a270d362 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -2020,6 +2020,7 @@ SYSLIB syslog SYSMENU systemd +SYSTEMAPPS SYSTEMTIME systemverilog Tadele diff --git a/src/common/utils/excluded_apps.h b/src/common/utils/excluded_apps.h index 5ea29a136a..36c42b2d21 100644 --- a/src/common/utils/excluded_apps.h +++ b/src/common/utils/excluded_apps.h @@ -3,7 +3,7 @@ #include // Checks if a process path is included in a list of strings. -bool find_app_name_in_path(const std::wstring& where, const std::vector& what) +inline bool find_app_name_in_path(const std::wstring& where, const std::vector& what) { for (const auto& row : what) { @@ -17,3 +17,16 @@ bool find_app_name_in_path(const std::wstring& where, const std::vector& what) +{ + for (const auto& row : what) + { + const auto pos = where.rfind(row); + if (pos != std::wstring::npos) + { + return true; + } + } + return false; +} diff --git a/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp b/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp index d0a91c2c4c..213d735f7e 100644 --- a/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp +++ b/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp @@ -16,11 +16,9 @@ FancyZonesApp::FancyZonesApp(const std::wstring& appName, const std::wstring& appKey) { DPIAware::EnableDPIAwarenessForThisProcess(); - - m_settings = MakeFancyZonesSettings(reinterpret_cast(&__ImageBase), appName.c_str(), appKey.c_str()); - + InitializeWinhookEventIds(); - m_app = MakeFancyZones(reinterpret_cast(&__ImageBase), m_settings, std::bind(&FancyZonesApp::DisableModule, this)); + m_app = MakeFancyZones(reinterpret_cast(&__ImageBase), std::bind(&FancyZonesApp::DisableModule, this)); InitHooks(); diff --git a/src/modules/fancyzones/FancyZones/FancyZonesApp.h b/src/modules/fancyzones/FancyZones/FancyZonesApp.h index b327d8c052..b66ba5b540 100644 --- a/src/modules/fancyzones/FancyZones/FancyZonesApp.h +++ b/src/modules/fancyzones/FancyZones/FancyZonesApp.h @@ -19,7 +19,6 @@ private: winrt::com_ptr m_app; HWINEVENTHOOK m_objectLocationWinEventHook = nullptr; std::vector m_staticWinEventHooks; - winrt::com_ptr m_settings; void DisableModule() noexcept; diff --git a/src/modules/fancyzones/FancyZonesLib/Colors.cpp b/src/modules/fancyzones/FancyZonesLib/Colors.cpp new file mode 100644 index 0000000000..8aebf9c9bc --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/Colors.cpp @@ -0,0 +1,59 @@ +#include "pch.h" +#include "Colors.h" + +#include + +#include +#include + +namespace Colors +{ + COLORREF currentAccentColor; + COLORREF currentBackgroundColor; + + bool GetSystemTheme() noexcept + { + winrt::Windows::UI::ViewManagement::UISettings settings; + auto accentValue = settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Accent); + auto accentColor = RGB(accentValue.R, accentValue.G, accentValue.B); + + auto backgroundValue = settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Background); + auto backgroundColor = RGB(backgroundValue.R, backgroundValue.G, backgroundValue.B); + + if (currentAccentColor != accentColor || currentBackgroundColor != backgroundColor) + { + currentAccentColor = accentColor; + currentBackgroundColor = backgroundColor; + return true; + } + + return false; + } + + ZoneColors GetZoneColors() noexcept + { + if (FancyZonesSettings::settings().systemTheme) + { + GetSystemTheme(); + auto numberColor = currentBackgroundColor == RGB(0, 0, 0) ? RGB(255, 255, 255) : RGB(0, 0, 0); + + return ZoneColors{ + .primaryColor = currentBackgroundColor, + .borderColor = currentAccentColor, + .highlightColor = currentAccentColor, + .numberColor = numberColor, + .highlightOpacity = FancyZonesSettings::settings().zoneHighlightOpacity + }; + } + else + { + return ZoneColors{ + .primaryColor = FancyZonesUtils::HexToRGB(FancyZonesSettings::settings().zoneColor), + .borderColor = FancyZonesUtils::HexToRGB(FancyZonesSettings::settings().zoneBorderColor), + .highlightColor = FancyZonesUtils::HexToRGB(FancyZonesSettings::settings().zoneHighlightColor), + .numberColor = FancyZonesUtils::HexToRGB(FancyZonesSettings::settings().zoneNumberColor), + .highlightOpacity = FancyZonesSettings::settings().zoneHighlightOpacity + }; + } + } +} diff --git a/src/modules/fancyzones/FancyZonesLib/Colors.h b/src/modules/fancyzones/FancyZonesLib/Colors.h new file mode 100644 index 0000000000..cb47b65f0c --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/Colors.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace Colors +{ + struct ZoneColors + { + COLORREF primaryColor; + COLORREF borderColor; + COLORREF highlightColor; + COLORREF numberColor; + int highlightOpacity; + }; + + ZoneColors GetZoneColors() noexcept; +} \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index 457aa7dc28..b101595485 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -21,9 +21,11 @@ #include #include #include +#include #include #include #include +#include #include #include "on_thread_executor.h" @@ -33,7 +35,6 @@ #include "util.h" #include -#include enum class DisplayChangeType { @@ -50,18 +51,15 @@ namespace NonLocalizable const wchar_t FZEditorExecutablePath[] = L"modules\\FancyZones\\PowerToys.FancyZonesEditor.exe"; } -struct FancyZones : public winrt::implements +struct FancyZones : public winrt::implements, public SettingsObserver { public: - FancyZones(HINSTANCE hinstance, const winrt::com_ptr& settings, std::function disableModuleCallback) noexcept : + FancyZones(HINSTANCE hinstance, std::function disableModuleCallback) noexcept : + SettingsObserver({ SettingId::EditorHotkey, SettingId::PrevTabHotkey, SettingId::NextTabHotkey, SettingId::SpanZonesAcrossMonitors }), m_hinstance(hinstance), - m_settings(settings), - m_windowMoveHandler(settings, [this]() { + m_windowMoveHandler([this]() { PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, NULL, NULL); }), - m_settingsFileWatcher(FancyZonesDataInstance().GetSettingsFileName(), [this]() { - PostMessageW(m_window, WM_PRIV_SETTINGS_CHANGED, NULL, NULL); - }), m_virtualDesktop([this]() { PostMessage(m_window, WM_PRIV_VD_INIT, 0, 0); }, @@ -71,6 +69,8 @@ public: { this->disableModuleCallback = std::move(disableModuleCallback); + FancyZonesSettings::instance().LoadSettings(); + FancyZonesDataInstance().ReplaceZoneSettingsFileFromOlderVersions(); LayoutTemplates::instance().LoadData(); CustomLayouts::instance().LoadData(); @@ -87,23 +87,17 @@ public: void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept { - if (m_settings->GetSettings()->spanZonesAcrossMonitors) + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) { monitor = NULL; } - // If accent color or theme is changed need to update colors for zones - if (m_settings->GetSettings()->systemTheme && GetSystemTheme()) - { - m_workAreaHandler.UpdateZoneColors(GetZoneColors()); - } - m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId)); } void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept { - if (m_settings->GetSettings()->spanZonesAcrossMonitors) + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) { monitor = NULL; } @@ -174,8 +168,7 @@ private: void RegisterVirtualDesktopUpdates() noexcept; void UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObject& hotkeyObject, bool enable) noexcept; - void OnSettingsChanged() noexcept; - + std::pair, ZoneIndexSet> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map>& workAreaMap) noexcept; void MoveWindowIntoZone(HWND window, winrt::com_ptr workArea, const ZoneIndexSet& zoneIndexSet) noexcept; bool MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primary) noexcept; @@ -190,8 +183,7 @@ private: std::vector GetMonitorsSorted() noexcept; HMONITOR WorkAreaKeyFromWindow(HWND window) noexcept; - bool GetSystemTheme() const noexcept; - ZoneColors GetZoneColors() const noexcept; + virtual void SettingsUpdate(SettingId type) override; const HINSTANCE m_hinstance{}; @@ -200,9 +192,6 @@ private: MonitorWorkAreaHandler m_workAreaHandler; VirtualDesktop m_virtualDesktop; - FileWatcher m_settingsFileWatcher; - - winrt::com_ptr m_settings{}; GUID m_previousDesktopId{}; // UUID of previously active virtual desktop. GUID m_currentDesktopId{}; // UUID of the current virtual desktop. wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on @@ -231,8 +220,6 @@ private: }; std::function FancyZones::disableModuleCallback = {}; -COLORREF currentAccentColor; -COLORREF currentBackgroundColor; // IFancyZones IFACEMETHODIMP_(void) @@ -253,11 +240,22 @@ FancyZones::Run() noexcept return; } - RegisterHotKey(m_window, static_cast(HotkeyId::Editor), m_settings->GetSettings()->editorHotkey.get_modifiers(), m_settings->GetSettings()->editorHotkey.get_code()); - if (m_settings->GetSettings()->windowSwitching) + if (!RegisterHotKey(m_window, static_cast(HotkeyId::Editor), FancyZonesSettings::settings().editorHotkey.get_modifiers(), FancyZonesSettings::settings().editorHotkey.get_code())) { - RegisterHotKey(m_window, static_cast(HotkeyId::NextTab), m_settings->GetSettings()->nextTabHotkey.get_modifiers(), m_settings->GetSettings()->nextTabHotkey.get_code()); - RegisterHotKey(m_window, static_cast(HotkeyId::PrevTab), m_settings->GetSettings()->prevTabHotkey.get_modifiers(), m_settings->GetSettings()->prevTabHotkey.get_code()); + Logger::error(L"Failed to register hotkey: {}", get_last_error_or_default(GetLastError())); + } + + if (FancyZonesSettings::settings().windowSwitching) + { + if (!RegisterHotKey(m_window, static_cast(HotkeyId::NextTab), FancyZonesSettings::settings().nextTabHotkey.get_modifiers(), FancyZonesSettings::settings().nextTabHotkey.get_code())) + { + Logger::error(L"Failed to register hotkey: {}", get_last_error_or_default(GetLastError())); + } + + if (!RegisterHotKey(m_window, static_cast(HotkeyId::PrevTab), FancyZonesSettings::settings().prevTabHotkey.get_modifiers(), FancyZonesSettings::settings().prevTabHotkey.get_code())) + { + Logger::error(L"Failed to register hotkey: {}", get_last_error_or_default(GetLastError())); + } } m_virtualDesktop.Init(); @@ -389,8 +387,8 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primar void FancyZones::WindowCreated(HWND window) noexcept { - const bool moveToAppLastZone = m_settings->GetSettings()->appLastZone_moveWindows; - const bool openOnActiveMonitor = m_settings->GetSettings()->openWindowOnActiveMonitor; + const bool moveToAppLastZone = FancyZonesSettings::settings().appLastZone_moveWindows; + const bool openOnActiveMonitor = FancyZonesSettings::settings().openWindowOnActiveMonitor; if (!moveToAppLastZone && !openOnActiveMonitor) { // Nothing to do here then. @@ -407,7 +405,7 @@ void FancyZones::WindowCreated(HWND window) noexcept // Avoid processing splash screens, already stamped (zoned) windows, or those windows // that belong to excluded applications list. - const bool isSplashScreen = FancyZonesUtils::IsSplashScreen(window); + const bool isSplashScreen = FancyZonesWindowUtils::IsSplashScreen(window); if (isSplashScreen) { return; @@ -425,7 +423,7 @@ void FancyZones::WindowCreated(HWND window) noexcept return; } - const bool isCandidateForLastKnownZone = FancyZonesUtils::IsCandidateForZoning(window, m_settings->GetSettings()->excludedAppsArray); + const bool isCandidateForLastKnownZone = FancyZonesWindowUtils::IsCandidateForZoning(window); if (!isCandidateForLastKnownZone) { return; @@ -477,7 +475,7 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept } } - if (m_settings->GetSettings()->quickLayoutSwitch) + if (FancyZonesSettings::settings().quickLayoutSwitch) { int digitPressed = -1; if ('0' <= info->vkCode && info->vkCode <= '9') @@ -526,7 +524,7 @@ void FancyZones::ToggleEditor() noexcept HMONITOR targetMonitor{}; - const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings()->use_cursorpos_editor_startupscreen; + const bool use_cursorpos_editor_startupscreen = FancyZonesSettings::settings().use_cursorpos_editor_startupscreen; if (use_cursorpos_editor_startupscreen) { POINT currentCursorPos{}; @@ -569,7 +567,7 @@ void FancyZones::ToggleEditor() noexcept std::wstring params; const std::wstring divider = L"/"; params += std::to_wstring(GetCurrentProcessId()) + divider; /* Process id */ - const bool spanZonesAcrossMonitors = m_settings->GetSettings()->spanZonesAcrossMonitors; + const bool spanZonesAcrossMonitors = FancyZonesSettings::settings().spanZonesAcrossMonitors; params += std::to_wstring(spanZonesAcrossMonitors) + divider; /* Span zones */ std::vector> allMonitors; @@ -794,7 +792,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa } else if (message == WM_PRIV_SETTINGS_CHANGED) { - OnSettingsChanged(); + FancyZonesSettings::instance().LoadSettings(); } else { @@ -845,7 +843,7 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept if ((changeType == DisplayChangeType::WorkArea) || (changeType == DisplayChangeType::DisplayChange)) { - if (m_settings->GetSettings()->displayChange_moveWindows) + if (FancyZonesSettings::settings().displayChange_moveWindows) { UpdateWindowsPositions(); } @@ -895,7 +893,7 @@ void FancyZones::AddWorkArea(HMONITOR monitor, const std::wstring& deviceId) noe parentId = parentArea->UniqueId(); } - auto workArea = MakeWorkArea(m_hinstance, monitor, uniqueId, parentId, GetZoneColors(), m_settings->GetSettings()->overlappingZonesAlgorithm, m_settings->GetSettings()->showZoneNumber); + auto workArea = MakeWorkArea(m_hinstance, monitor, uniqueId, parentId); if (workArea) { m_workAreaHandler.AddWorkArea(m_currentDesktopId, monitor, workArea); @@ -920,7 +918,7 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, void FancyZones::UpdateWorkAreas() noexcept { - if (m_settings->GetSettings()->spanZonesAcrossMonitors) + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) { AddWorkArea(nullptr, {}); } @@ -984,7 +982,7 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce HMONITOR current = WorkAreaKeyFromWindow(window); std::vector monitorInfo = GetMonitorsSorted(); - if (current && monitorInfo.size() > 1 && m_settings->GetSettings()->moveWindowAcrossMonitors) + if (current && monitorInfo.size() > 1 && FancyZonesSettings::settings().moveWindowAcrossMonitors) { // Multi monitor environment. auto currMonitorInfo = std::find(std::begin(monitorInfo), std::end(monitorInfo), current); @@ -1019,13 +1017,13 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce { auto workArea = m_workAreaHandler.GetWorkArea(m_currentDesktopId, current); // Single monitor environment, or combined multi-monitor environment. - if (m_settings->GetSettings()->restoreSize) + if (FancyZonesSettings::settings().restoreSize) { bool moved = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */, workArea); if (!moved) { - FancyZonesUtils::RestoreWindowOrigin(window); - FancyZonesUtils::RestoreWindowSize(window); + FancyZonesWindowUtils::RestoreWindowOrigin(window); + FancyZonesWindowUtils::RestoreWindowSize(window); } else { @@ -1053,7 +1051,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept auto allMonitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>(); - if (current && allMonitors.size() > 1 && m_settings->GetSettings()->moveWindowAcrossMonitors) + if (current && allMonitors.size() > 1 && FancyZonesSettings::settings().moveWindowAcrossMonitors) { // Multi monitor environment. // First, try to stay on the same monitor @@ -1179,7 +1177,7 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept { // We already checked in ShouldProcessSnapHotkey whether the foreground window is a candidate for zoning auto window = GetForegroundWindow(); - if (m_settings->GetSettings()->moveWindowsBasedOnPosition) + if (FancyZonesSettings::settings().moveWindowsBasedOnPosition) { return OnSnapHotkeyBasedOnPosition(window, vkCode); } @@ -1231,6 +1229,11 @@ void FancyZones::RegisterVirtualDesktopUpdates() noexcept void FancyZones::UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObject& hotkeyObject, bool enable) noexcept { + if (!m_window) + { + return; + } + UnregisterHotKey(m_window, hotkeyId); if (!enable) @@ -1248,28 +1251,34 @@ void FancyZones::UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObjec } } -void FancyZones::OnSettingsChanged() noexcept +void FancyZones::SettingsUpdate(SettingId id) { - _TRACER_; - m_settings->ReloadSettings(); - - // Update the hotkeys - UpdateHotkey(static_cast(HotkeyId::Editor), m_settings->GetSettings()->editorHotkey, true); - - auto windowSwitching = m_settings->GetSettings()->windowSwitching; - UpdateHotkey(static_cast(HotkeyId::NextTab), m_settings->GetSettings()->nextTabHotkey, windowSwitching); - UpdateHotkey(static_cast(HotkeyId::PrevTab), m_settings->GetSettings()->prevTabHotkey, windowSwitching); - - // Needed if we toggled spanZonesAcrossMonitors - m_workAreaHandler.Clear(); - - // update zone colors - m_workAreaHandler.UpdateZoneColors(GetZoneColors()); - - // update overlapping algorithm - m_workAreaHandler.UpdateOverlappingAlgorithm(m_settings->GetSettings()->overlappingZonesAlgorithm); - - PostMessageW(m_window, WM_PRIV_VD_INIT, NULL, NULL); + switch (id) + { + case SettingId::EditorHotkey: + { + UpdateHotkey(static_cast(HotkeyId::Editor), FancyZonesSettings::settings().editorHotkey, true); + } + break; + case SettingId::PrevTabHotkey: + { + UpdateHotkey(static_cast(HotkeyId::PrevTab), FancyZonesSettings::settings().prevTabHotkey, FancyZonesSettings::settings().windowSwitching); + } + break; + case SettingId::NextTabHotkey: + { + UpdateHotkey(static_cast(HotkeyId::NextTab), FancyZonesSettings::settings().nextTabHotkey, FancyZonesSettings::settings().windowSwitching); + } + break; + case SettingId::SpanZonesAcrossMonitors: + { + m_workAreaHandler.Clear(); + PostMessageW(m_window, WM_PRIV_VD_INIT, NULL, NULL); + } + break; + default: + break; + } } void FancyZones::OnEditorExitEvent() noexcept @@ -1287,14 +1296,14 @@ void FancyZones::UpdateZoneSets() noexcept workArea->UpdateActiveZoneSet(); } - auto moveWindows = m_settings->GetSettings()->zoneSetChange_moveWindows; + auto moveWindows = FancyZonesSettings::settings().zoneSetChange_moveWindows; UpdateWindowsPositions(!moveWindows); } bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept { auto window = GetForegroundWindow(); - if (m_settings->GetSettings()->overrideSnapHotkeys && FancyZonesUtils::IsCandidateForZoning(window, m_settings->GetSettings()->excludedAppsArray)) + if (FancyZonesSettings::settings().overrideSnapHotkeys && FancyZonesWindowUtils::IsCandidateForZoning(window)) { HMONITOR monitor = WorkAreaKeyFromWindow(window); @@ -1303,7 +1312,7 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept { if (vkCode == VK_UP || vkCode == VK_DOWN) { - return m_settings->GetSettings()->moveWindowsBasedOnPosition; + return FancyZonesSettings::settings().moveWindowsBasedOnPosition; } else { @@ -1346,7 +1355,7 @@ void FancyZones::ApplyQuickLayout(int key) noexcept void FancyZones::FlashZones() noexcept { - if (m_settings->GetSettings()->flashZonesOnQuickSwitch && !m_windowMoveHandler.IsDragEnabled()) + if (FancyZonesSettings::settings().flashZonesOnQuickSwitch && !m_windowMoveHandler.IsDragEnabled()) { for (auto [monitor, workArea] : m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId)) { @@ -1385,7 +1394,7 @@ std::vector> FancyZones::GetRawMonitorData() noexcept HMONITOR FancyZones::WorkAreaKeyFromWindow(HWND window) noexcept { - if (m_settings->GetSettings()->spanZonesAcrossMonitors) + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) { return NULL; } @@ -1395,61 +1404,7 @@ HMONITOR FancyZones::WorkAreaKeyFromWindow(HWND window) noexcept } } -bool FancyZones::GetSystemTheme() const noexcept +winrt::com_ptr MakeFancyZones(HINSTANCE hinstance, std::function disableCallback) noexcept { - winrt::Windows::UI::ViewManagement::UISettings settings; - auto accentValue = settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Accent); - auto accentColor = RGB(accentValue.R, accentValue.G, accentValue.B); - - auto backgroundValue = settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Background); - auto backgroundColor = RGB(backgroundValue.R, backgroundValue.G, backgroundValue.B); - - if (currentAccentColor != accentColor || currentBackgroundColor != backgroundColor) - { - currentAccentColor = accentColor; - currentBackgroundColor = backgroundColor; - return true; - } - - return false; -} - -ZoneColors FancyZones::GetZoneColors() const noexcept -{ - if (m_settings->GetSettings()->systemTheme) - { - GetSystemTheme(); - auto numberColor = currentBackgroundColor == RGB(0, 0, 0) ? RGB(255, 255, 255) : RGB(0, 0, 0); - - - return ZoneColors{ - .primaryColor = currentBackgroundColor, - .borderColor = currentAccentColor, - .highlightColor = currentAccentColor, - .numberColor = numberColor, - .highlightOpacity = m_settings->GetSettings()->zoneHighlightOpacity - }; - } - else - { - return ZoneColors{ - .primaryColor = FancyZonesUtils::HexToRGB(m_settings->GetSettings()->zoneColor), - .borderColor = FancyZonesUtils::HexToRGB(m_settings->GetSettings()->zoneBorderColor), - .highlightColor = FancyZonesUtils::HexToRGB(m_settings->GetSettings()->zoneHighlightColor), - .numberColor = FancyZonesUtils::HexToRGB(m_settings->GetSettings()->zoneNumberColor), - .highlightOpacity = m_settings->GetSettings()->zoneHighlightOpacity - }; - } -} - -winrt::com_ptr MakeFancyZones(HINSTANCE hinstance, - const winrt::com_ptr& settings, - std::function disableCallback) noexcept -{ - if (!settings) - { - return nullptr; - } - - return winrt::make_self(hinstance, settings, disableCallback); + return winrt::make_self(hinstance, disableCallback); } diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.h b/src/modules/fancyzones/FancyZonesLib/FancyZones.h index 36a196d8aa..6f78116315 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.h @@ -52,4 +52,4 @@ interface __declspec(uuid("{2CB37E8F-87E6-4AEC-B4B2-E0FDC873343F}")) IFancyZones (PKBDLLHOOKSTRUCT info) = 0; }; -winrt::com_ptr MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr& settings, std::function disableCallback) noexcept; +winrt::com_ptr MakeFancyZones(HINSTANCE hinstance, std::function disableCallback) noexcept; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj index 5e040395d3..e59f5e7529 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj @@ -66,19 +66,23 @@ + + + - + + ../pch.h ../pch.h @@ -117,6 +121,7 @@ + diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters index 11c8ced847..218536344a 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters @@ -81,7 +81,7 @@ Header Files - + Header Files @@ -117,6 +117,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -194,6 +203,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp index bd0d444934..b75ab95d4a 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp @@ -1,7 +1,7 @@ #include "pch.h" #include "MonitorUtils.h" -#include +#include namespace MonitorUtils { @@ -68,7 +68,7 @@ namespace MonitorUtils if (GetMonitorInfo(monitor, &destMi)) { RECT newPosition = FitOnScreen(placement.rcNormalPosition, originMi.rcWork, destMi.rcWork); - FancyZonesUtils::SizeWindowToRect(window, newPosition); + FancyZonesWindowUtils::SizeWindowToRect(window, newPosition); } } } diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp index 5582fdb0e3..5bd41d3876 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp +++ b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp @@ -129,25 +129,3 @@ void MonitorWorkAreaHandler::Clear() { workAreaMap.clear(); } - -void MonitorWorkAreaHandler::UpdateZoneColors(const ZoneColors& colors) -{ - for (const auto& workArea : workAreaMap) - { - for (const auto& workAreaPtr : workArea.second) - { - workAreaPtr.second->SetZoneColors(colors); - } - } -} - -void MonitorWorkAreaHandler::UpdateOverlappingAlgorithm(OverlappingZonesAlgorithm overlappingAlgorithm) -{ - for (const auto& workArea : workAreaMap) - { - for (const auto& workAreaPtr : workArea.second) - { - workAreaPtr.second->SetOverlappingZonesAlgorithm(overlappingAlgorithm); - } - } -} diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h index a0e586c1b7..1a0b1029dd 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h +++ b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h @@ -86,16 +86,6 @@ public: * Clear all persisted work area related data. */ void Clear(); - - /** - * Update zone colors after settings changed - */ - void UpdateZoneColors(const ZoneColors& colors); - - /** - * Update overlapping algorithm after settings changed - */ - void UpdateOverlappingAlgorithm(OverlappingZonesAlgorithm overlappingAlgorithm); private: // Work area is uniquely defined by monitor and virtual desktop id. diff --git a/src/modules/fancyzones/FancyZonesLib/Settings.cpp b/src/modules/fancyzones/FancyZonesLib/Settings.cpp index 733f67bbff..438989a94a 100644 --- a/src/modules/fancyzones/FancyZonesLib/Settings.cpp +++ b/src/modules/fancyzones/FancyZonesLib/Settings.cpp @@ -1,10 +1,15 @@ #include "pch.h" -#include -#include +#include "Settings.h" -#include "FancyZonesLib/Settings.h" -#include "FancyZonesLib/FancyZones.h" -#include "trace.h" +#include +#include +#include +#include +#include + +#include +#include +#include // Non-Localizable strings namespace NonLocalizable @@ -27,6 +32,8 @@ namespace NonLocalizable const wchar_t ShowOnAllMonitorsID[] = L"fancyzones_show_on_all_monitors"; const wchar_t SpanZonesAcrossMonitorsID[] = L"fancyzones_span_zones_across_monitors"; const wchar_t MakeDraggedWindowTransparentID[] = L"fancyzones_makeDraggedWindowTransparent"; + const wchar_t AllowPopupWindowSnapID[] = L"fancyzones_allowPopupWindowSnap"; + const wchar_t AllowChildWindowSnapID[] = L"fancyzones_allowChildWindowSnap"; const wchar_t SystemThemeID[] = L"fancyzones_systemTheme"; const wchar_t ZoneColorID[] = L"fancyzones_zoneColor"; @@ -40,214 +47,208 @@ namespace NonLocalizable const wchar_t ExcludedAppsID[] = L"fancyzones_excluded_apps"; const wchar_t ZoneHighlightOpacityID[] = L"fancyzones_highlight_opacity"; const wchar_t ShowZoneNumberID[] = L"fancyzones_showZoneNumber"; - - const wchar_t ToggleEditorActionID[] = L"ToggledFZEditor"; - const wchar_t IconKeyID[] = L"pt-fancy-zones"; - const wchar_t OverviewURL[] = L"https://aka.ms/PowerToysOverview_FancyZones"; - const wchar_t VideoURL[] = L"https://youtu.be/rTtGzZYAXgY"; - const wchar_t PowerToysIssuesURL[] = L"https://aka.ms/powerToysReportBug"; } -struct FancyZonesSettings : winrt::implements +FancyZonesSettings::FancyZonesSettings() { -public: - FancyZonesSettings(HINSTANCE hinstance, PCWSTR name, PCWSTR key) : - m_hinstance(hinstance), - m_moduleName(name), - m_moduleKey(key) + const std::wstring& settingsFileName = GetSettingsFileName(); + m_settingsFileWatcher = std::make_unique(settingsFileName, [&]() { + PostMessageW(HWND_BROADCAST, WM_PRIV_SETTINGS_CHANGED, NULL, NULL); + }); +} + +FancyZonesSettings& FancyZonesSettings::instance() +{ + static FancyZonesSettings instance; + return instance; +} + +void FancyZonesSettings::AddObserver(SettingsObserver& observer) +{ + m_observers.insert(&observer); +} + +void FancyZonesSettings::RemoveObserver(SettingsObserver& observer) +{ + auto iter = m_observers.find(&observer); + if (iter != m_observers.end()) { - LoadSettings(name, true); + m_observers.erase(iter); } +} - IFACEMETHODIMP_(bool) - GetConfig(_Out_ PWSTR buffer, _Out_ int* buffer_sizeg) noexcept; - IFACEMETHODIMP_(void) - SetConfig(PCWSTR config) noexcept; - IFACEMETHODIMP_(void) - ReloadSettings() noexcept; - IFACEMETHODIMP_(const Settings*) - GetSettings() const noexcept { return &m_settings; } - -private: - void LoadSettings(PCWSTR config, bool fromFile) noexcept; - void SaveSettings() noexcept; - - const HINSTANCE m_hinstance; - std::wstring m_moduleName{}; - std::wstring m_moduleKey{}; - - Settings m_settings; - - struct - { - PCWSTR name; - bool* value; - int resourceId; - } m_configBools[19] = { - { NonLocalizable::ShiftDragID, &m_settings.shiftDrag, IDS_SETTING_DESCRIPTION_SHIFTDRAG }, - { NonLocalizable::MouseSwitchID, &m_settings.mouseSwitch, IDS_SETTING_DESCRIPTION_MOUSESWITCH }, - { NonLocalizable::OverrideSnapHotKeysID, &m_settings.overrideSnapHotkeys, IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS }, - { NonLocalizable::MoveWindowAcrossMonitorsID, &m_settings.moveWindowAcrossMonitors, IDS_SETTING_DESCRIPTION_MOVE_WINDOW_ACROSS_MONITORS }, - { NonLocalizable::MoveWindowsBasedOnPositionID, &m_settings.moveWindowsBasedOnPosition, IDS_SETTING_DESCRIPTION_MOVE_WINDOWS_BASED_ON_POSITION }, - { NonLocalizable::DisplayChangeMoveWindowsID, &m_settings.displayChange_moveWindows, IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS }, - { NonLocalizable::ZoneSetChangeMoveWindowsID, &m_settings.zoneSetChange_moveWindows, IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS }, - { NonLocalizable::AppLastZoneMoveWindowsID, &m_settings.appLastZone_moveWindows, IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS }, - { NonLocalizable::OpenWindowOnActiveMonitorID, &m_settings.openWindowOnActiveMonitor, IDS_SETTING_DESCRIPTION_OPEN_WINDOW_ON_ACTIVE_MONITOR }, - { NonLocalizable::RestoreSizeID, &m_settings.restoreSize, IDS_SETTING_DESCRIPTION_RESTORESIZE }, - { NonLocalizable::QuickLayoutSwitch, &m_settings.quickLayoutSwitch, IDS_SETTING_DESCRIPTION_QUICKLAYOUTSWITCH }, - { NonLocalizable::FlashZonesOnQuickSwitch, &m_settings.flashZonesOnQuickSwitch, IDS_SETTING_DESCRIPTION_FLASHZONESONQUICKSWITCH }, - { NonLocalizable::UseCursorPosEditorStartupScreenID, &m_settings.use_cursorpos_editor_startupscreen, IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN }, - { NonLocalizable::ShowOnAllMonitorsID, &m_settings.showZonesOnAllMonitors, IDS_SETTING_DESCRIPTION_SHOW_FANCY_ZONES_ON_ALL_MONITORS }, - { NonLocalizable::SpanZonesAcrossMonitorsID, &m_settings.spanZonesAcrossMonitors, IDS_SETTING_DESCRIPTION_SPAN_ZONES_ACROSS_MONITORS }, - { NonLocalizable::MakeDraggedWindowTransparentID, &m_settings.makeDraggedWindowTransparent, IDS_SETTING_DESCRIPTION_MAKE_DRAGGED_WINDOW_TRANSPARENT }, - { NonLocalizable::WindowSwitchingToggleID, &m_settings.windowSwitching, IDS_SETTING_WINDOW_SWITCHING_TOGGLE_LABEL }, - { NonLocalizable::SystemThemeID, &m_settings.systemTheme, IDS_SETTING_DESCRIPTION_SYSTEM_THEME }, - { NonLocalizable::ShowZoneNumberID, &m_settings.showZoneNumber, IDS_SETTING_DESCRIPTION_SHOW_ZONE_NUMBER }, - }; -}; - -IFACEMETHODIMP_(bool) -FancyZonesSettings::GetConfig(_Out_ PWSTR buffer, _Out_ int* buffer_size) noexcept +void FancyZonesSettings::SetBoolFlag(const PowerToysSettings::PowerToyValues& values, const wchar_t* id, SettingId notificationId, bool& out) { - PowerToysSettings::Settings settings(m_hinstance, m_moduleName); - - // Pass a string literal or a resource id to Settings::set_description(). - settings.set_description(IDS_SETTING_DESCRIPTION); - settings.set_icon_key(NonLocalizable::IconKeyID); - settings.set_overview_link(NonLocalizable::OverviewURL); - settings.set_video_link(NonLocalizable::VideoURL); - - // Add a custom action property. When using this settings type, the "PowertoyModuleIface::call_custom_action()" - // method should be overridden as well. - settings.add_custom_action( - NonLocalizable::ToggleEditorActionID, // action name. - IDS_SETTING_LAUNCH_EDITOR_LABEL, - IDS_SETTING_LAUNCH_EDITOR_BUTTON, - IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION); - settings.add_hotkey(NonLocalizable::EditorHotkeyID, IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, m_settings.editorHotkey); - settings.add_hotkey(NonLocalizable::NextTabHotkeyID, IDS_SETTING_NEXT_TAB_HOTKEY_LABEL, m_settings.nextTabHotkey); - settings.add_hotkey(NonLocalizable::PrevTabHotkeyID, IDS_SETTING_PREV_TAB_HOTKEY_LABEL, m_settings.prevTabHotkey); - - for (auto const& setting : m_configBools) + if (const auto val = values.get_bool_value(id)) { - settings.add_bool_toggle(setting.name, setting.resourceId, *setting.value); + if (out != *val) + { + out = *val; + NotifyObservers(notificationId); + } } - - settings.add_color_picker(NonLocalizable::ZoneHighlightColorID, IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, m_settings.zoneHighlightColor); - settings.add_color_picker(NonLocalizable::ZoneColorID, IDS_SETTING_DESCRIPTION_ZONECOLOR, m_settings.zoneColor); - settings.add_color_picker(NonLocalizable::ZoneBorderColorID, IDS_SETTING_DESCRIPTION_ZONE_BORDER_COLOR, m_settings.zoneBorderColor); - - settings.add_int_spinner(NonLocalizable::ZoneHighlightOpacityID, IDS_SETTINGS_HIGHLIGHT_OPACITY, m_settings.zoneHighlightOpacity, 0, 100, 1); - - settings.add_multiline_string(NonLocalizable::ExcludedAppsID, IDS_SETTING_EXCLUDED_APPS_DESCRIPTION, m_settings.excludedApps); - - return settings.serialize_to_buffer(buffer, buffer_size); } -IFACEMETHODIMP_(void) -FancyZonesSettings::SetConfig(PCWSTR serializedPowerToysSettingsJson) noexcept +void FancyZonesSettings::LoadSettings() { - LoadSettings(serializedPowerToysSettingsJson, false /*fromFile*/); - SaveSettings(); -} + _TRACER_; -IFACEMETHODIMP_(void) -FancyZonesSettings::ReloadSettings() noexcept -{ - LoadSettings(m_moduleKey.c_str(), true /*fromFile*/); -} - -void FancyZonesSettings::LoadSettings(PCWSTR config, bool fromFile) noexcept -{ try { - PowerToysSettings::PowerToyValues values = fromFile ? - PowerToysSettings::PowerToyValues::load_from_settings_file(m_moduleKey) : - PowerToysSettings::PowerToyValues::from_json_string(config, m_moduleKey); - - for (auto const& setting : m_configBools) + auto jsonOpt = json::from_file(GetSettingsFileName()); + if (!jsonOpt) { - if (const auto val = values.get_bool_value(setting.name)) - { - *setting.value = *val; - } + Logger::warn(L"Failed to read from settings file"); + return; } + PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(jsonOpt->Stringify(), NonLocalizable::ModuleKey); + + // flags + SetBoolFlag(values, NonLocalizable::ShiftDragID, SettingId::ShiftDrag, m_settings.shiftDrag); + SetBoolFlag(values, NonLocalizable::MouseSwitchID, SettingId::MouseSwitch, m_settings.mouseSwitch); + SetBoolFlag(values, NonLocalizable::OverrideSnapHotKeysID, SettingId::OverrideSnapHotkeys, m_settings.overrideSnapHotkeys); + SetBoolFlag(values, NonLocalizable::MoveWindowAcrossMonitorsID, SettingId::MoveWindowAcrossMonitors, m_settings.moveWindowAcrossMonitors); + SetBoolFlag(values, NonLocalizable::MoveWindowsBasedOnPositionID, SettingId::MoveWindowsBasedOnPosition, m_settings.moveWindowsBasedOnPosition); + SetBoolFlag(values, NonLocalizable::DisplayChangeMoveWindowsID, SettingId::DisplayChangeMoveWindows, m_settings.displayChange_moveWindows); + SetBoolFlag(values, NonLocalizable::ZoneSetChangeMoveWindowsID, SettingId::ZoneSetChangeMoveWindows, m_settings.zoneSetChange_moveWindows); + SetBoolFlag(values, NonLocalizable::AppLastZoneMoveWindowsID, SettingId::AppLastZoneMoveWindows, m_settings.appLastZone_moveWindows); + SetBoolFlag(values, NonLocalizable::OpenWindowOnActiveMonitorID, SettingId::OpenWindowOnActiveMonitor, m_settings.openWindowOnActiveMonitor); + SetBoolFlag(values, NonLocalizable::RestoreSizeID, SettingId::RestoreWindowSize, m_settings.restoreSize); + SetBoolFlag(values, NonLocalizable::QuickLayoutSwitch, SettingId::QuickLayoutSwitch, m_settings.quickLayoutSwitch); + SetBoolFlag(values, NonLocalizable::FlashZonesOnQuickSwitch, SettingId::FlashZonesOnQuickSwitch, m_settings.flashZonesOnQuickSwitch); + SetBoolFlag(values, NonLocalizable::UseCursorPosEditorStartupScreenID, SettingId::LaunchEditorOnScreenWhereCursorPlaced, m_settings.use_cursorpos_editor_startupscreen); + SetBoolFlag(values, NonLocalizable::ShowOnAllMonitorsID, SettingId::ShowOnAllMonitors, m_settings.showZonesOnAllMonitors); + SetBoolFlag(values, NonLocalizable::SpanZonesAcrossMonitorsID, SettingId::SpanZonesAcrossMonitors, m_settings.spanZonesAcrossMonitors); + SetBoolFlag(values, NonLocalizable::MakeDraggedWindowTransparentID, SettingId::MakeDraggedWindowsTransparent, m_settings.makeDraggedWindowTransparent); + SetBoolFlag(values, NonLocalizable::WindowSwitchingToggleID, SettingId::WindowSwitching, m_settings.windowSwitching); + SetBoolFlag(values, NonLocalizable::SystemThemeID, SettingId::SystemTheme, m_settings.systemTheme); + 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); + + // colors if (auto val = values.get_string_value(NonLocalizable::ZoneColorID)) { - m_settings.zoneColor = std::move(*val); + if (m_settings.zoneColor != *val) + { + m_settings.zoneColor = std::move(*val); + NotifyObservers(SettingId::ZoneColor); + } } if (auto val = values.get_string_value(NonLocalizable::ZoneBorderColorID)) { - m_settings.zoneBorderColor = std::move(*val); + if (m_settings.zoneBorderColor != *val) + { + m_settings.zoneBorderColor = std::move(*val); + NotifyObservers(SettingId::ZoneBorderColor); + } } if (auto val = values.get_string_value(NonLocalizable::ZoneHighlightColorID)) { - m_settings.zoneHighlightColor = std::move(*val); + if (m_settings.zoneHighlightColor != *val) + { + m_settings.zoneHighlightColor = std::move(*val); + NotifyObservers(SettingId::ZoneHighlightColor); + } } if (auto val = values.get_string_value(NonLocalizable::ZoneNumberColorID)) { - m_settings.zoneNumberColor = std::move(*val); - } - - if (const auto val = values.get_json(NonLocalizable::EditorHotkeyID)) - { - m_settings.editorHotkey = PowerToysSettings::HotkeyObject::from_json(*val); - } - - if (const auto val = values.get_json(NonLocalizable::NextTabHotkeyID)) - { - m_settings.nextTabHotkey = PowerToysSettings::HotkeyObject::from_json(*val); - } - - if (const auto val = values.get_json(NonLocalizable::PrevTabHotkeyID)) - { - m_settings.prevTabHotkey = PowerToysSettings::HotkeyObject::from_json(*val); - } - - if (auto val = values.get_string_value(NonLocalizable::ExcludedAppsID)) - { - m_settings.excludedApps = std::move(*val); - m_settings.excludedAppsArray.clear(); - auto excludedUppercase = m_settings.excludedApps; - CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); - std::wstring_view view(excludedUppercase); - while (view.starts_with('\n') || view.starts_with('\r')) + if (m_settings.zoneNumberColor != *val) { - view.remove_prefix(1); - } - while (!view.empty()) - { - auto pos = (std::min)(view.find_first_of(L"\r\n"), view.length()); - m_settings.excludedAppsArray.emplace_back(view.substr(0, pos)); - view.remove_prefix(pos); - while (view.starts_with('\n') || view.starts_with('\r')) - { - view.remove_prefix(1); - } + m_settings.zoneNumberColor = std::move(*val); + NotifyObservers(SettingId::ZoneNumberColor); } } if (auto val = values.get_int_value(NonLocalizable::ZoneHighlightOpacityID)) { - m_settings.zoneHighlightOpacity = *val; + if (m_settings.zoneHighlightOpacity != *val) + { + m_settings.zoneHighlightOpacity = std::move(*val); + NotifyObservers(SettingId::ZoneHighlightOpacity); + } } + // hotkeys + if (const auto val = values.get_json(NonLocalizable::EditorHotkeyID)) + { + auto hotkey = PowerToysSettings::HotkeyObject::from_json(*val); + if (m_settings.editorHotkey.get_modifiers() != hotkey.get_modifiers() || m_settings.editorHotkey.get_key() != hotkey.get_key() || m_settings.editorHotkey.get_code() != hotkey.get_code()) + { + m_settings.editorHotkey = std::move(hotkey); + NotifyObservers(SettingId::EditorHotkey); + } + } + + if (const auto val = values.get_json(NonLocalizable::NextTabHotkeyID)) + { + auto hotkey = PowerToysSettings::HotkeyObject::from_json(*val); + if (m_settings.nextTabHotkey.get_modifiers() != hotkey.get_modifiers() || m_settings.nextTabHotkey.get_key() != hotkey.get_key() || m_settings.nextTabHotkey.get_code() != hotkey.get_code()) + { + m_settings.nextTabHotkey = PowerToysSettings::HotkeyObject::from_json(*val); + NotifyObservers(SettingId::NextTabHotkey); + } + } + + if (const auto val = values.get_json(NonLocalizable::PrevTabHotkeyID)) + { + auto hotkey = PowerToysSettings::HotkeyObject::from_json(*val); + if (m_settings.prevTabHotkey.get_modifiers() != hotkey.get_modifiers() || m_settings.prevTabHotkey.get_key() != hotkey.get_key() || m_settings.prevTabHotkey.get_code() != hotkey.get_code()) + { + m_settings.prevTabHotkey = PowerToysSettings::HotkeyObject::from_json(*val); + NotifyObservers(SettingId::PrevTabHotkey); + } + } + + // excluded apps + if (auto val = values.get_string_value(NonLocalizable::ExcludedAppsID)) + { + std::wstring apps = std::move(*val); + std::vector excludedApps; + auto excludedUppercase = apps; + CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); + std::wstring_view view(excludedUppercase); + view = left_trim(trim(view)); + + while (!view.empty()) + { + auto pos = (std::min)(view.find_first_of(L"\r\n"), view.length()); + excludedApps.emplace_back(view.substr(0, pos)); + view.remove_prefix(pos); + view = left_trim(trim(view)); + } + + if (m_settings.excludedAppsArray != excludedApps) + { + m_settings.excludedApps = apps; + m_settings.excludedAppsArray = excludedApps; + NotifyObservers(SettingId::ExcludedApps); + } + } + + // algorithms if (auto val = values.get_int_value(NonLocalizable::OverlappingZonesAlgorithmID)) { // Avoid undefined behavior if (*val >= 0 || *val < (int)OverlappingZonesAlgorithm::EnumElements) { - m_settings.overlappingZonesAlgorithm = (OverlappingZonesAlgorithm)*val; + auto algorithm = (OverlappingZonesAlgorithm)*val; + if (m_settings.overlappingZonesAlgorithm != algorithm) + { + m_settings.overlappingZonesAlgorithm = algorithm; + NotifyObservers(SettingId::OverlappingZonesAlgorithm); + } } } } - catch (...) + catch (const winrt::hresult_error& e) { // Failure to load settings does not break FancyZones functionality. Display error message and continue with default settings. + Logger::error(L"Failed to read settings. {}", e.message()); MessageBox(NULL, GET_RESOURCE_STRING(IDS_FANCYZONES_SETTINGS_LOAD_ERROR).c_str(), GET_RESOURCE_STRING(IDS_POWERTOYS_FANCYZONES).c_str(), @@ -255,42 +256,13 @@ void FancyZonesSettings::LoadSettings(PCWSTR config, bool fromFile) noexcept } } -void FancyZonesSettings::SaveSettings() noexcept +void FancyZonesSettings::NotifyObservers(SettingId id) const { - try + for (auto observer : m_observers) { - PowerToysSettings::PowerToyValues values(m_moduleName, m_moduleKey); - - for (auto const& setting : m_configBools) + if (observer->WantsToBeNotified(id)) { - values.add_property(setting.name, *setting.value); + observer->SettingsUpdate(id); } - - values.add_property(NonLocalizable::ZoneColorID, m_settings.zoneColor); - values.add_property(NonLocalizable::ZoneBorderColorID, m_settings.zoneBorderColor); - values.add_property(NonLocalizable::ZoneHighlightColorID, m_settings.zoneHighlightColor); - values.add_property(NonLocalizable::ZoneNumberColorID, m_settings.zoneNumberColor); - values.add_property(NonLocalizable::ZoneHighlightOpacityID, m_settings.zoneHighlightOpacity); - values.add_property(NonLocalizable::OverlappingZonesAlgorithmID, (int)m_settings.overlappingZonesAlgorithm); - values.add_property(NonLocalizable::EditorHotkeyID, m_settings.editorHotkey.get_json()); - values.add_property(NonLocalizable::NextTabHotkeyID, m_settings.nextTabHotkey.get_json()); - values.add_property(NonLocalizable::PrevTabHotkeyID, m_settings.prevTabHotkey.get_json()); - values.add_property(NonLocalizable::ExcludedAppsID, m_settings.excludedApps); - - values.save_to_settings_file(); - } - catch (...) - { - // Failure to save settings does not break FancyZones functionality. Display error message and continue with currently cached settings. - std::wstring errorMessage = GET_RESOURCE_STRING(IDS_FANCYZONES_SETTINGS_LOAD_ERROR) + L" " + NonLocalizable::PowerToysIssuesURL; - MessageBox(NULL, - errorMessage.c_str(), - GET_RESOURCE_STRING(IDS_POWERTOYS_FANCYZONES).c_str(), - MB_OK); } } - -winrt::com_ptr MakeFancyZonesSettings(HINSTANCE hinstance, PCWSTR name, PCWSTR key) noexcept -{ - return winrt::make_self(hinstance, name, key); -} diff --git a/src/modules/fancyzones/FancyZonesLib/Settings.h b/src/modules/fancyzones/FancyZonesLib/Settings.h index bc8917b57b..527e2ed23e 100644 --- a/src/modules/fancyzones/FancyZonesLib/Settings.h +++ b/src/modules/fancyzones/FancyZonesLib/Settings.h @@ -1,7 +1,16 @@ #pragma once +#include + +#include #include +#include +#include + +class SettingsObserver; +class FileWatcher; + enum struct OverlappingZonesAlgorithm : int { Smallest = 0, @@ -34,6 +43,8 @@ struct Settings bool makeDraggedWindowTransparent = true; bool systemTheme = true; bool showZoneNumber = true; + bool allowSnapPopupWindows = false; + bool allowSnapChildWindows = false; std::wstring zoneColor = L"#AACDFF"; std::wstring zoneBorderColor = L"#FFFFFF"; std::wstring zoneHighlightColor = L"#008CFF"; @@ -48,12 +59,38 @@ struct Settings std::vector excludedAppsArray; }; -interface __declspec(uuid("{BA4E77C4-6F44-4C5D-93D3-CBDE880495C2}")) IFancyZonesSettings : public IUnknown +class FancyZonesSettings { - IFACEMETHOD_(bool, GetConfig)(_Out_ PWSTR buffer, _Out_ int *buffer_size) = 0; - IFACEMETHOD_(void, SetConfig)(PCWSTR serializedPowerToysSettings) = 0; - IFACEMETHOD_(void, ReloadSettings)() = 0; - IFACEMETHOD_(const Settings*, GetSettings)() const = 0; -}; +public: + static FancyZonesSettings& instance(); + static inline const Settings& settings() + { + return instance().m_settings; + } -winrt::com_ptr MakeFancyZonesSettings(HINSTANCE hinstance, PCWSTR name, PCWSTR key) noexcept; + inline static std::wstring GetSettingsFileName() + { + std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey); +#if defined(UNIT_TESTS) + return saveFolderPath + L"\\test-settings.json"; +#endif + return saveFolderPath + L"\\settings.json"; + } + + void AddObserver(SettingsObserver& observer); + void RemoveObserver(SettingsObserver& observer); + + void LoadSettings(); + +private: + FancyZonesSettings(); + ~FancyZonesSettings() = default; + + Settings m_settings; + std::unique_ptr m_settingsFileWatcher; + std::unordered_set m_observers; + + void SetBoolFlag(const PowerToysSettings::PowerToyValues& values, const wchar_t* id, SettingId notificationId, bool& out); + + void NotifyObservers(SettingId id) const; +}; diff --git a/src/modules/fancyzones/FancyZonesLib/SettingsConstants.h b/src/modules/fancyzones/FancyZonesLib/SettingsConstants.h new file mode 100644 index 0000000000..99651bb2ec --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/SettingsConstants.h @@ -0,0 +1,36 @@ +#pragma once + +enum class SettingId +{ + ShiftDrag = 0, + MouseSwitch, + OverrideSnapHotkeys, + MoveWindowAcrossMonitors, + MoveWindowsBasedOnPosition, + OverlappingZonesAlgorithm, + DisplayChangeMoveWindows, + ZoneSetChangeMoveWindows, + AppLastZoneMoveWindows, + OpenWindowOnActiveMonitor, + RestoreWindowSize, + QuickLayoutSwitch, + FlashZonesOnQuickSwitch, + LaunchEditorOnScreenWhereCursorPlaced, + ShowOnAllMonitors, + SpanZonesAcrossMonitors, + MakeDraggedWindowsTransparent, + ZoneHighlightColor, + ZoneHighlightOpacity, + ZoneBorderColor, + ZoneColor, + ZoneNumberColor, + SystemTheme, + ShowZoneNumber, + EditorHotkey, + WindowSwitching, + NextTabHotkey, + PrevTabHotkey, + ExcludedApps, + AllowSnapPopupWindows, + AllowSnapChildWindows, +}; \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/SettingsObserver.h b/src/modules/fancyzones/FancyZonesLib/SettingsObserver.h new file mode 100644 index 0000000000..834c91cd78 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/SettingsObserver.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include +#include + +class SettingsObserver +{ +public: + SettingsObserver(std::unordered_set observedSettings) : + m_observedSettings(std::move(observedSettings)) + { + FancyZonesSettings::instance().AddObserver(*this); + } + + virtual ~SettingsObserver() + { + FancyZonesSettings::instance().RemoveObserver(*this); + } + + virtual void SettingsUpdate(SettingId type) {} + + bool WantsToBeNotified(SettingId type) const noexcept + { + return m_observedSettings.contains(type); + } + +protected: + std::unordered_set m_observedSettings; +}; \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp b/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp index dab2e1b3e6..09a33e3370 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp @@ -10,7 +10,7 @@ #include "FancyZonesData/AppZoneHistory.h" #include "Settings.h" #include "WorkArea.h" -#include "util.h" +#include // Non-Localizable strings namespace NonLocalizable @@ -49,8 +49,7 @@ namespace WindowMoveHandlerUtils } } -WindowMoveHandler::WindowMoveHandler(const winrt::com_ptr& settings, const std::function& keyUpdateCallback) : - m_settings(settings), +WindowMoveHandler::WindowMoveHandler(const std::function& keyUpdateCallback) : m_mouseState(false), m_mouseHook(std::bind(&WindowMoveHandler::OnMouseDown, this)), m_shiftKeyState(keyUpdateCallback), @@ -61,13 +60,13 @@ WindowMoveHandler::WindowMoveHandler(const winrt::com_ptr& void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap) noexcept { - if (!FancyZonesUtils::IsCandidateForZoning(window, m_settings->GetSettings()->excludedAppsArray) || WindowMoveHandlerUtils::IsCursorTypeIndicatingSizeEvent()) + if (!FancyZonesWindowUtils::IsCandidateForZoning(window) || WindowMoveHandlerUtils::IsCursorTypeIndicatingSizeEvent()) { return; } - m_draggedWindowInfo.hasNoVisibleOwner = FancyZonesUtils::HasNoVisibleOwner(window); - m_draggedWindowInfo.isStandardWindow = FancyZonesUtils::IsStandardWindow(window); + m_draggedWindowInfo.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(window); + m_draggedWindowInfo.isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(window) && (!FancyZonesWindowUtils::IsPopupWindow(window) || FancyZonesSettings::settings().allowSnapPopupWindows); m_inDragging = true; auto iter = workAreaMap.find(monitor); @@ -78,7 +77,7 @@ void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const m_draggedWindow = window; - if (m_settings->GetSettings()->mouseSwitch) + if (FancyZonesSettings::settings().mouseSwitch) { m_mouseHook.enable(); } @@ -97,7 +96,7 @@ void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const m_draggedWindowWorkArea = iter->second; SetWindowTransparency(m_draggedWindow); m_draggedWindowWorkArea->MoveSizeEnter(m_draggedWindow); - if (m_settings->GetSettings()->showZonesOnAllMonitors) + if (FancyZonesSettings::settings().showZonesOnAllMonitors) { for (auto [keyMonitor, workArea] : workAreaMap) { @@ -172,7 +171,7 @@ void WindowMoveHandler::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, { // The drag has moved to a different monitor. m_draggedWindowWorkArea->ClearSelectedZones(); - if (!m_settings->GetSettings()->showZonesOnAllMonitors) + if (!FancyZonesSettings::settings().showZonesOnAllMonitors) { m_draggedWindowWorkArea->HideZonesOverlay(); } @@ -219,12 +218,12 @@ void WindowMoveHandler::MoveSizeEnd(HWND window, POINT const& ptScreen, const st auto workArea = std::move(m_draggedWindowWorkArea); ResetWindowTransparency(); - bool hasNoVisibleOwner = FancyZonesUtils::HasNoVisibleOwner(window); - bool isStandardWindow = FancyZonesUtils::IsStandardWindow(window); + bool hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(window); + bool isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(window); if ((isStandardWindow == false && hasNoVisibleOwner == true && m_draggedWindowInfo.isStandardWindow == true && m_draggedWindowInfo.hasNoVisibleOwner == true) || - FancyZonesUtils::IsWindowMaximized(window)) + FancyZonesWindowUtils::IsWindowMaximized(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 @@ -236,15 +235,15 @@ void WindowMoveHandler::MoveSizeEnd(HWND window, POINT const& ptScreen, const st } else { - if (m_settings->GetSettings()->restoreSize) + if (FancyZonesSettings::settings().restoreSize) { if (WindowMoveHandlerUtils::IsCursorTypeIndicatingSizeEvent()) { ::RemoveProp(window, ZonedWindowProperties::PropertyRestoreSizeID); } - else if (!FancyZonesUtils::IsWindowMaximized(window)) + else if (!FancyZonesWindowUtils::IsWindowMaximized(window)) { - FancyZonesUtils::RestoreWindowSize(window); + FancyZonesWindowUtils::RestoreWindowSize(window); } } @@ -312,10 +311,9 @@ void WindowMoveHandler::WarnIfElevationIsRequired(HWND window) noexcept { using namespace notifications; using namespace NonLocalizable; - using namespace FancyZonesUtils; static bool warning_shown = false; - if (!is_process_elevated() && IsProcessOfWindowElevated(window)) + if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window)) { m_dragEnabled = false; if (!warning_shown && !is_toast_disabled(CantDragElevatedDontShowAgainRegistryPath, CantDragElevatedDisableIntervalInDays)) @@ -335,7 +333,7 @@ void WindowMoveHandler::WarnIfElevationIsRequired(HWND window) noexcept void WindowMoveHandler::UpdateDragState() noexcept { - if (m_settings->GetSettings()->shiftDrag) + if (FancyZonesSettings::settings().shiftDrag) { m_dragEnabled = (m_shiftKeyState.state() ^ m_mouseState); } @@ -347,7 +345,7 @@ void WindowMoveHandler::UpdateDragState() noexcept void WindowMoveHandler::SetWindowTransparency(HWND window) noexcept { - if (m_settings->GetSettings()->makeDraggedWindowTransparent) + if (FancyZonesSettings::settings().makeDraggedWindowTransparent) { m_windowTransparencyProperties.draggedWindowExstyle = GetWindowLong(window, GWL_EXSTYLE); @@ -364,7 +362,7 @@ void WindowMoveHandler::SetWindowTransparency(HWND window) noexcept void WindowMoveHandler::ResetWindowTransparency() noexcept { - if (m_settings->GetSettings()->makeDraggedWindowTransparent && m_windowTransparencyProperties.draggedWindow != nullptr) + if (FancyZonesSettings::settings().makeDraggedWindowTransparent && m_windowTransparencyProperties.draggedWindow != nullptr) { SetLayeredWindowAttributes(m_windowTransparencyProperties.draggedWindow, m_windowTransparencyProperties.draggedWindowCrKey, m_windowTransparencyProperties.draggedWindowInitialAlpha, m_windowTransparencyProperties.draggedWindowDwFlags); SetWindowLong(m_windowTransparencyProperties.draggedWindow, GWL_EXSTYLE, m_windowTransparencyProperties.draggedWindowExstyle); diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h b/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h index b94ab02316..c3b182578f 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h +++ b/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h @@ -12,7 +12,7 @@ interface IWorkArea; class WindowMoveHandler { public: - WindowMoveHandler(const winrt::com_ptr& settings, const std::function& keyUpdateCallback); + WindowMoveHandler(const std::function& keyUpdateCallback); void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap) noexcept; void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap) noexcept; @@ -64,8 +64,6 @@ private: void SetWindowTransparency(HWND window) noexcept; void ResetWindowTransparency() noexcept; - winrt::com_ptr m_settings{}; - bool m_inDragging{}; // Whether or not a move/size operation is currently active HWND m_draggedWindow{}; // The window that is being moved/sized MoveSizeWindowInfo m_draggedWindowInfo; // MoveSizeWindowInfo of the window at the moment when dragging started diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp new file mode 100644 index 0000000000..a21eef459a --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp @@ -0,0 +1,448 @@ +#include "pch.h" +#include "WindowUtils.h" + +#include +#include +#include +#include + +#include +#include + +// Non-Localizable strings +namespace NonLocalizable +{ + const wchar_t PowerToysAppFZEditor[] = L"POWERTOYS.FANCYZONESEDITOR.EXE"; + const wchar_t SplashClassName[] = L"MsoSplash"; + const wchar_t CoreWindow[] = L"Windows.UI.Core.CoreWindow"; + const wchar_t SearchUI[] = L"SearchUI.exe"; + const wchar_t SystemAppsFolder[] = L"SYSTEMAPPS"; +} + +namespace +{ + BOOL CALLBACK saveDisplayToVector(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data) + { + reinterpret_cast*>(data)->emplace_back(monitor); + return true; + } + + bool allMonitorsHaveSameDpiScaling() + { + std::vector monitors; + EnumDisplayMonitors(NULL, NULL, saveDisplayToVector, reinterpret_cast(&monitors)); + + if (monitors.size() < 2) + { + return true; + } + + UINT firstMonitorDpiX; + UINT firstMonitorDpiY; + + if (S_OK != GetDpiForMonitor(monitors[0], MDT_EFFECTIVE_DPI, &firstMonitorDpiX, &firstMonitorDpiY)) + { + return false; + } + + for (int i = 1; i < monitors.size(); i++) + { + UINT iteratedMonitorDpiX; + UINT iteratedMonitorDpiY; + + if (S_OK != GetDpiForMonitor(monitors[i], MDT_EFFECTIVE_DPI, &iteratedMonitorDpiX, &iteratedMonitorDpiY) || + iteratedMonitorDpiX != firstMonitorDpiX) + { + return false; + } + } + + return true; + } + + void ScreenToWorkAreaCoords(HWND window, RECT& rect) + { + // First, find the correct monitor. The monitor cannot be found using the given rect itself, we must first + // translate it to relative workspace coordinates. + HMONITOR monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTOPRIMARY); + MONITORINFOEXW monitorInfo{ sizeof(MONITORINFOEXW) }; + GetMonitorInfoW(monitor, &monitorInfo); + + auto xOffset = monitorInfo.rcWork.left - monitorInfo.rcMonitor.left; + auto yOffset = monitorInfo.rcWork.top - monitorInfo.rcMonitor.top; + + auto referenceRect = rect; + + referenceRect.left -= xOffset; + referenceRect.right -= xOffset; + referenceRect.top -= yOffset; + referenceRect.bottom -= yOffset; + + // Now, this rect should be used to determine the monitor and thus taskbar size. This fixes + // scenarios where the zone lies approximately between two monitors, and the taskbar is on the left. + monitor = MonitorFromRect(&referenceRect, MONITOR_DEFAULTTOPRIMARY); + GetMonitorInfoW(monitor, &monitorInfo); + + xOffset = monitorInfo.rcWork.left - monitorInfo.rcMonitor.left; + yOffset = monitorInfo.rcWork.top - monitorInfo.rcMonitor.top; + + rect.left -= xOffset; + rect.right -= xOffset; + rect.top -= yOffset; + rect.bottom -= yOffset; + + const auto level = DPIAware::GetAwarenessLevel(GetWindowDpiAwarenessContext(window)); + const bool accountForUnawareness = level < DPIAware::PER_MONITOR_AWARE; + + if (accountForUnawareness && !allMonitorsHaveSameDpiScaling()) + { + rect.left = max(monitorInfo.rcMonitor.left, rect.left); + rect.right = min(monitorInfo.rcMonitor.right - xOffset, rect.right); + rect.top = max(monitorInfo.rcMonitor.top, rect.top); + rect.bottom = min(monitorInfo.rcMonitor.bottom - yOffset, rect.bottom); + } + } +} + + +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 +{ + WINDOWPLACEMENT placement{}; + if (GetWindowPlacement(window, &placement) && + placement.showCmd == SW_SHOWMAXIMIZED) + { + return true; + } + return false; +} + +bool FancyZonesWindowUtils::HasVisibleOwner(HWND window) noexcept +{ + auto owner = GetWindow(window, GW_OWNER); + if (owner == nullptr) + { + return false; // There is no owner at all + } + if (!IsWindowVisible(owner)) + { + return false; // Owner is invisible + } + RECT rect; + if (!GetWindowRect(owner, &rect)) + { + return true; // 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; +} + +bool FancyZonesWindowUtils::IsStandardWindow(HWND 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; + } + + std::array class_name; + GetClassNameA(window, class_name.data(), static_cast(class_name.size())); + if (is_system_window(window, class_name.data())) + { + return false; + } + + return true; +} + +bool FancyZonesWindowUtils::IsPopupWindow(HWND window) noexcept +{ + auto style = GetWindowLong(window, GWL_STYLE); + return ((style & WS_POPUP) == WS_POPUP); +} + +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::IsCandidateForZoning(HWND window) +{ + bool isStandard = IsStandardWindow(window); + if (!isStandard) + { + return false; + } + + // popup could be the window we don't want to snap: start menu, notification popup, tray window, etc. + // also, popup could be the windows we want to snap disregarding the "allowSnapPopupWindows" setting, e.g. Telegram + bool isPopup = IsPopupWindow(window); + if (isPopup && !HasThickFrameAndMinimizeMaximizeButtons(window) && !FancyZonesSettings::settings().allowSnapPopupWindows) + { + return false; + } + + // allow child windows + auto hasOwner = HasVisibleOwner(window); + if (hasOwner && !FancyZonesSettings::settings().allowSnapChildWindows) + { + return false; + } + + std::wstring processPath = get_process_path(window); + CharUpperBuffW(const_cast(processPath).data(), (DWORD)processPath.length()); + if (IsExcludedByUser(processPath)) + { + return false; + } + + if (IsExcludedByDefault(processPath)) + { + return false; + } + + return true; +} + +bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window) +{ + DWORD pid = 0; + GetWindowThreadProcessId(window, &pid); + if (!pid) + { + return false; + } + + wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, + FALSE, + pid) }; + + wil::unique_handle token; + bool elevated = false; + + if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token)) + { + TOKEN_ELEVATION elevation; + DWORD size; + if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size)) + { + return elevation.TokenIsElevated != 0; + } + } + return false; +} + +bool FancyZonesWindowUtils::IsExcludedByUser(const std::wstring& processPath) noexcept +{ + return (find_app_name_in_path(processPath, FancyZonesSettings::settings().excludedAppsArray)); +} + +bool FancyZonesWindowUtils::IsExcludedByDefault(const std::wstring& processPath) noexcept +{ + static std::vector defaultExcludedFolders = { NonLocalizable::SystemAppsFolder }; + if (find_folder_in_path(processPath, defaultExcludedFolders)) + { + return true; + } + + static std::vector defaultExcludedApps = { NonLocalizable::PowerToysAppFZEditor, NonLocalizable::CoreWindow, NonLocalizable::SearchUI }; + return (find_app_name_in_path(processPath, defaultExcludedApps)); +} + +void FancyZonesWindowUtils::SwitchToWindow(HWND window) noexcept +{ + // Check if the window is minimized + if (IsIconic(window)) + { + // Show the window since SetForegroundWindow fails on minimized windows + ShowWindow(window, SW_RESTORE); + } + + // This is a hack to bypass the restriction on setting the foreground window + INPUT inputs[1] = { { .type = INPUT_MOUSE } }; + SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)); + + SetForegroundWindow(window); +} + +void FancyZonesWindowUtils::SizeWindowToRect(HWND window, RECT rect) noexcept +{ + WINDOWPLACEMENT placement{}; + ::GetWindowPlacement(window, &placement); + + // Wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685) + for (int i = 0; i < 5 && (placement.showCmd == SW_SHOWMINIMIZED); ++i) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + ::GetWindowPlacement(window, &placement); + } + + // Do not restore minimized windows. We change their placement though so they restore to the correct zone. + if ((placement.showCmd != SW_SHOWMINIMIZED) && + (placement.showCmd != SW_MINIMIZE)) + { + placement.showCmd = SW_RESTORE; + } + + // Remove maximized show command to make sure window is moved to the correct zone. + if (placement.showCmd == SW_SHOWMAXIMIZED) + { + placement.showCmd = SW_RESTORE; + placement.flags &= ~WPF_RESTORETOMAXIMIZED; + } + + ScreenToWorkAreaCoords(window, rect); + + placement.rcNormalPosition = rect; + placement.flags |= WPF_ASYNCWINDOWPLACEMENT; + + ::SetWindowPlacement(window, &placement); + // Do it again, allowing Windows to resize the window and set correct scaling + // This fixes Issue #365 + ::SetWindowPlacement(window, &placement); +} + +void FancyZonesWindowUtils::SaveWindowSizeAndOrigin(HWND window) noexcept +{ + HANDLE handle = GetPropW(window, ZonedWindowProperties::PropertyRestoreSizeID); + if (handle) + { + // Size already set, skip + return; + } + + RECT rect; + if (GetWindowRect(window, &rect)) + { + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + int originX = rect.left; + int originY = rect.top; + + DPIAware::InverseConvert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), width, height); + DPIAware::InverseConvert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), originX, originY); + + std::array windowSizeData = { width, height }; + std::array windowOriginData = { originX, originY }; + HANDLE rawData; + memcpy(&rawData, windowSizeData.data(), sizeof rawData); + SetPropW(window, ZonedWindowProperties::PropertyRestoreSizeID, rawData); + memcpy(&rawData, windowOriginData.data(), sizeof rawData); + SetPropW(window, ZonedWindowProperties::PropertyRestoreOriginID, rawData); + } +} + +void FancyZonesWindowUtils::RestoreWindowSize(HWND window) noexcept +{ + auto windowSizeData = GetPropW(window, ZonedWindowProperties::PropertyRestoreSizeID); + if (windowSizeData) + { + std::array windowSize; + memcpy(windowSize.data(), &windowSizeData, sizeof windowSize); + + // {width, height} + DPIAware::Convert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), windowSize[0], windowSize[1]); + + RECT rect; + if (GetWindowRect(window, &rect)) + { + rect.right = rect.left + windowSize[0]; + rect.bottom = rect.top + windowSize[1]; + SizeWindowToRect(window, rect); + } + + ::RemoveProp(window, ZonedWindowProperties::PropertyRestoreSizeID); + } +} + +void FancyZonesWindowUtils::RestoreWindowOrigin(HWND window) noexcept +{ + auto windowOriginData = GetPropW(window, ZonedWindowProperties::PropertyRestoreOriginID); + if (windowOriginData) + { + std::array windowOrigin; + memcpy(windowOrigin.data(), &windowOriginData, sizeof windowOrigin); + + // {width, height} + DPIAware::Convert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), windowOrigin[0], windowOrigin[1]); + + RECT rect; + if (GetWindowRect(window, &rect)) + { + int xOffset = windowOrigin[0] - rect.left; + int yOffset = windowOrigin[1] - rect.top; + + rect.left += xOffset; + rect.right += xOffset; + rect.top += yOffset; + rect.bottom += yOffset; + SizeWindowToRect(window, rect); + } + + ::RemoveProp(window, ZonedWindowProperties::PropertyRestoreOriginID); + } +} + +RECT FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(HWND window, RECT rect, HWND windowOfRect) noexcept +{ + RECT newWindowRect = rect; + + RECT windowRect{}; + ::GetWindowRect(window, &windowRect); + + // Take care of borders + RECT frameRect{}; + if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect)))) + { + LONG leftMargin = frameRect.left - windowRect.left; + LONG rightMargin = frameRect.right - windowRect.right; + LONG bottomMargin = frameRect.bottom - windowRect.bottom; + newWindowRect.left -= leftMargin; + newWindowRect.right -= rightMargin; + newWindowRect.bottom -= bottomMargin; + } + + // Take care of windows that cannot be resized + if ((::GetWindowLong(window, GWL_STYLE) & WS_SIZEBOX) == 0) + { + newWindowRect.right = newWindowRect.left + (windowRect.right - windowRect.left); + newWindowRect.bottom = newWindowRect.top + (windowRect.bottom - windowRect.top); + } + + // Convert to screen coordinates + MapWindowRect(windowOfRect, nullptr, &newWindowRect); + + return newWindowRect; +} + +void FancyZonesWindowUtils::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); + } +} diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.h b/src/modules/fancyzones/FancyZonesLib/WindowUtils.h new file mode 100644 index 0000000000..9dd83ab342 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "gdiplus.h" + +#include +#include + +namespace FancyZonesWindowUtils +{ + bool IsSplashScreen(HWND window); + bool IsWindowMaximized(HWND window) noexcept; + bool HasVisibleOwner(HWND window) noexcept; + bool IsStandardWindow(HWND window); + bool IsPopupWindow(HWND window) noexcept; + bool HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept; + bool IsCandidateForZoning(HWND window); + bool IsProcessOfWindowElevated(HWND window); // If HWND is already dead, we assume it wasn't elevated + bool IsExcludedByUser(const std::wstring& processPath) noexcept; + bool IsExcludedByDefault(const std::wstring& processPath) noexcept; + + void SwitchToWindow(HWND window) noexcept; + void SizeWindowToRect(HWND window, RECT rect) noexcept; // Parameter rect must be in screen coordinates (e.g. obtained from GetWindowRect) + void SaveWindowSizeAndOrigin(HWND window) noexcept; + void RestoreWindowSize(HWND window) noexcept; + void RestoreWindowOrigin(HWND window) noexcept; + void MakeWindowTransparent(HWND window); + RECT AdjustRectForSizeWindowToRect(HWND window, RECT rect, HWND windowOfRect) noexcept; // Parameter rect is in windowOfRect coordinates +} \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp index d296820b2b..e9736a0425 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp @@ -7,11 +7,12 @@ #include "FancyZonesData/AppliedLayouts.h" #include "FancyZonesData/AppZoneHistory.h" #include "FancyZonesDataTypes.h" +#include "SettingsObserver.h" #include "ZonesOverlay.h" #include "trace.h" -#include "util.h" #include "on_thread_executor.h" #include "Settings.h" +#include #include #include @@ -65,7 +66,7 @@ namespace { HWND window = CreateWindowExW(WS_EX_TOOLWINDOW, NonLocalizable::ToolWindowClassName, L"", WS_POPUP, position.left(), position.top(), position.width(), position.height(), nullptr, nullptr, hinstance, owner); Logger::info("Creating new ZonesOverlay window, hWnd = {}", (void*)window); - MakeWindowTransparent(window); + FancyZonesWindowUtils::MakeWindowTransparent(window); // According to ShowWindow docs, we must call it with SW_SHOWNORMAL the first time ShowWindow(window, SW_SHOWNORMAL); @@ -110,7 +111,7 @@ public: WorkArea(HINSTANCE hinstance); ~WorkArea(); - bool Init(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm, const bool showZoneText); + bool Init(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId); IFACEMETHODIMP MoveSizeEnter(HWND window) noexcept; IFACEMETHODIMP MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool selectManyZones) noexcept; @@ -145,10 +146,6 @@ public: ClearSelectedZones() noexcept; IFACEMETHODIMP_(void) FlashZones() noexcept; - IFACEMETHODIMP_(void) - SetZoneColors(const ZoneColors& colors) noexcept; - IFACEMETHODIMP_(void) - SetOverlappingZonesAlgorithm(OverlappingZonesAlgorithm overlappingAlgorithm) noexcept; protected: static LRESULT CALLBACK s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept; @@ -171,9 +168,6 @@ private: WPARAM m_keyLast{}; size_t m_keyCycle{}; std::unique_ptr m_zonesOverlay; - ZoneColors m_zoneColors; - OverlappingZonesAlgorithm m_overlappingAlgorithm; - bool m_showZoneText; }; WorkArea::WorkArea(HINSTANCE hinstance) @@ -192,12 +186,8 @@ WorkArea::~WorkArea() windowPool.FreeZonesOverlayWindow(m_window); } -bool WorkArea::Init(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm, const bool showZoneText) +bool WorkArea::Init(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId) { - m_zoneColors = zoneColors; - m_overlappingAlgorithm = overlappingAlgorithm; - m_showZoneText = showZoneText; - Rect workAreaRect; m_monitor = monitor; if (monitor) @@ -278,7 +268,7 @@ IFACEMETHODIMP WorkArea::MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, if (redraw) { - m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, m_zoneColors, m_showZoneText); + m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); } return S_OK; @@ -297,7 +287,7 @@ IFACEMETHODIMP WorkArea::MoveSizeEnd(HWND window, POINT const& ptScreen) noexcep MapWindowPoints(nullptr, m_window, &ptClient, 1); m_zoneSet->MoveWindowIntoZoneByIndexSet(window, m_window, m_highlightZone); - if (FancyZonesUtils::HasNoVisibleOwner(window)) + if (!FancyZonesWindowUtils::HasVisibleOwner(window)) { SaveWindowProcessToZoneIndex(window); } @@ -331,7 +321,7 @@ WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool { if (m_zoneSet->MoveWindowIntoZoneByDirectionAndIndex(window, m_window, vkCode, cycle)) { - if (FancyZonesUtils::HasNoVisibleOwner(window)) + if (!FancyZonesWindowUtils::HasVisibleOwner(window)) { SaveWindowProcessToZoneIndex(window); } @@ -408,7 +398,7 @@ WorkArea::ShowZonesOverlay() noexcept if (m_window) { SetAsTopmostWindow(); - m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, m_zoneColors, m_showZoneText); + m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); m_zonesOverlay->Show(); } } @@ -428,11 +418,11 @@ WorkArea::HideZonesOverlay() noexcept IFACEMETHODIMP_(void) WorkArea::UpdateActiveZoneSet() noexcept { - CalculateZoneSet(m_overlappingAlgorithm); + CalculateZoneSet(FancyZonesSettings::settings().overlappingZonesAlgorithm); if (m_window) { m_highlightZone.clear(); - m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, m_zoneColors, m_showZoneText); + m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); } } @@ -451,7 +441,7 @@ WorkArea::ClearSelectedZones() noexcept if (m_highlightZone.size()) { m_highlightZone.clear(); - m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, m_zoneColors, m_showZoneText); + m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); } } @@ -461,24 +451,11 @@ WorkArea::FlashZones() noexcept if (m_window) { SetAsTopmostWindow(); - m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), {}, m_zoneColors, m_showZoneText); + m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); m_zonesOverlay->Flash(); } } -IFACEMETHODIMP_(void) -WorkArea::SetZoneColors(const ZoneColors& colors) noexcept -{ - m_zoneColors = colors; -} - -IFACEMETHODIMP_(void) -WorkArea::SetOverlappingZonesAlgorithm(OverlappingZonesAlgorithm overlappingAlgorithm) noexcept -{ - m_overlappingAlgorithm = overlappingAlgorithm; -} - - #pragma region private void WorkArea::InitializeZoneSets(const FancyZonesDataTypes::DeviceIdData& parentUniqueId) noexcept @@ -502,7 +479,7 @@ void WorkArea::InitializeZoneSets(const FancyZonesDataTypes::DeviceIdData& paren } } - CalculateZoneSet(m_overlappingAlgorithm); + CalculateZoneSet(FancyZonesSettings::settings().overlappingZonesAlgorithm); } void WorkArea::CalculateZoneSet(OverlappingZonesAlgorithm overlappingAlgorithm) noexcept @@ -631,10 +608,10 @@ LRESULT CALLBACK WorkArea::s_WndProc(HWND window, UINT message, WPARAM wparam, L DefWindowProc(window, message, wparam, lparam); } -winrt::com_ptr MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm, const bool showZoneText) noexcept +winrt::com_ptr MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId) noexcept { auto self = winrt::make_self(hinstance); - if (self->Init(hinstance, monitor, uniqueId, parentUniqueId, zoneColors, overlappingAlgorithm, showZoneText)) + if (self->Init(hinstance, monitor, uniqueId, parentUniqueId)) { return self; } diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.h b/src/modules/fancyzones/FancyZonesLib/WorkArea.h index 4ba5680593..1ecc9fc30c 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.h +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.h @@ -1,7 +1,6 @@ #pragma once #include "FancyZones.h" #include "FancyZonesLib/ZoneSet.h" -#include "FancyZonesLib/ZoneColors.h" #include "FancyZonesLib/FancyZonesDataTypes.h" /** @@ -135,14 +134,6 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IWorkArea : * Display the layout on the screen and then hide it. */ IFACEMETHOD_(void, FlashZones)() = 0; - /* - * Set zone colors - */ - IFACEMETHOD_(void, SetZoneColors)(const ZoneColors& colors) = 0; - /* - * Set overlapping algorithm - */ - IFACEMETHOD_(void, SetOverlappingZonesAlgorithm)(OverlappingZonesAlgorithm overlappingAlgorithm) = 0; }; -winrt::com_ptr MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm, const bool showZoneText) noexcept; +winrt::com_ptr MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId) noexcept; diff --git a/src/modules/fancyzones/FancyZonesLib/ZoneColors.h b/src/modules/fancyzones/FancyZonesLib/ZoneColors.h deleted file mode 100644 index 1cc4cc1030..0000000000 --- a/src/modules/fancyzones/FancyZonesLib/ZoneColors.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include - -struct ZoneColors -{ - COLORREF primaryColor; - COLORREF borderColor; - COLORREF highlightColor; - COLORREF numberColor; - int highlightOpacity; -}; diff --git a/src/modules/fancyzones/FancyZonesLib/ZoneSet.cpp b/src/modules/fancyzones/FancyZonesLib/ZoneSet.cpp index 4d16d9ee5a..0f52b25a54 100644 --- a/src/modules/fancyzones/FancyZonesLib/ZoneSet.cpp +++ b/src/modules/fancyzones/FancyZonesLib/ZoneSet.cpp @@ -7,7 +7,8 @@ #include "FancyZonesWindowProperties.h" #include "Settings.h" #include "Zone.h" -#include "util.h" +#include +#include #include #include @@ -344,10 +345,10 @@ ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND workAreaWindow, const Zo { if (!suppressMove) { - SaveWindowSizeAndOrigin(window); + FancyZonesWindowUtils::SaveWindowSizeAndOrigin(window); - auto rect = AdjustRectForSizeWindowToRect(window, size, workAreaWindow); - SizeWindowToRect(window, rect); + auto rect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, size, workAreaWindow); + FancyZonesWindowUtils::SizeWindowToRect(window, rect); } FancyZonesWindowProperties::StampZoneIndexProperty(window, indexSet); @@ -597,7 +598,7 @@ ZoneSet::CycleTabs(HWND window, bool reverse) noexcept continue; } - SwitchToWindow(next); + FancyZonesWindowUtils::SwitchToWindow(next); break; } diff --git a/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp b/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp index 1c856e160a..263d72ef5f 100644 --- a/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp +++ b/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp @@ -282,7 +282,7 @@ void ZonesOverlay::Flash() void ZonesOverlay::DrawActiveZoneSet(const IZoneSet::ZonesMap& zones, const ZoneIndexSet& highlightZones, - const ZoneColors& colors, + const Colors::ZoneColors& colors, const bool showZoneText) { _TRACER_; diff --git a/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.h b/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.h index 1d076d00ca..adc51ecc87 100644 --- a/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.h +++ b/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.h @@ -11,7 +11,7 @@ #include "Zone.h" #include "ZoneSet.h" #include "FancyZones.h" -#include "ZoneColors.h" +#include "Colors.h" class ZonesOverlay { @@ -68,6 +68,6 @@ public: void Flash(); void DrawActiveZoneSet(const IZoneSet::ZonesMap& zones, const ZoneIndexSet& highlightZones, - const ZoneColors& colors, + const Colors::ZoneColors& colors, const bool showZoneText); }; diff --git a/src/modules/fancyzones/FancyZonesLib/util.cpp b/src/modules/fancyzones/FancyZonesLib/util.cpp index afaf2ea45d..216b768f46 100644 --- a/src/modules/fancyzones/FancyZonesLib/util.cpp +++ b/src/modules/fancyzones/FancyZonesLib/util.cpp @@ -1,6 +1,5 @@ #include "pch.h" #include "util.h" -#include "Settings.h" #include #include @@ -9,35 +8,10 @@ #include #include -#include -#include -#include +#include -// Non-Localizable strings -namespace NonLocalizable -{ - const wchar_t PowerToysAppFZEditor[] = L"POWERTOYS.FANCYZONESEDITOR.EXE"; - const wchar_t SplashClassName[] = L"MsoSplash"; -} - -namespace -{ - bool IsZonableByProcessPath(const std::wstring& processPath, const std::vector& excludedApps) - { - // Filter out user specified apps - CharUpperBuffW(const_cast(processPath).data(), (DWORD)processPath.length()); - if (find_app_name_in_path(processPath, excludedApps)) - { - return false; - } - if (find_app_name_in_path(processPath, { NonLocalizable::PowerToysAppFZEditor })) - { - return false; - } - return true; - } -} +#include namespace FancyZonesUtils { @@ -200,337 +174,6 @@ namespace FancyZonesUtils monitorInfo = std::move(sortedMonitorInfo); } - BOOL CALLBACK saveDisplayToVector(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data) - { - reinterpret_cast*>(data)->emplace_back(monitor); - return true; - } - - bool allMonitorsHaveSameDpiScaling() - { - std::vector monitors; - EnumDisplayMonitors(NULL, NULL, saveDisplayToVector, reinterpret_cast(&monitors)); - - if (monitors.size() < 2) - { - return true; - } - - UINT firstMonitorDpiX; - UINT firstMonitorDpiY; - - if (S_OK != GetDpiForMonitor(monitors[0], MDT_EFFECTIVE_DPI, &firstMonitorDpiX, &firstMonitorDpiY)) - { - return false; - } - - for (int i = 1; i < monitors.size(); i++) - { - UINT iteratedMonitorDpiX; - UINT iteratedMonitorDpiY; - - if (S_OK != GetDpiForMonitor(monitors[i], MDT_EFFECTIVE_DPI, &iteratedMonitorDpiX, &iteratedMonitorDpiY) || - iteratedMonitorDpiX != firstMonitorDpiX) - { - return false; - } - } - - return true; - } - - void ScreenToWorkAreaCoords(HWND window, RECT& rect) - { - // First, find the correct monitor. The monitor cannot be found using the given rect itself, we must first - // translate it to relative workspace coordinates. - HMONITOR monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTOPRIMARY); - MONITORINFOEXW monitorInfo{ sizeof(MONITORINFOEXW) }; - GetMonitorInfoW(monitor, &monitorInfo); - - auto xOffset = monitorInfo.rcWork.left - monitorInfo.rcMonitor.left; - auto yOffset = monitorInfo.rcWork.top - monitorInfo.rcMonitor.top; - - auto referenceRect = rect; - - referenceRect.left -= xOffset; - referenceRect.right -= xOffset; - referenceRect.top -= yOffset; - referenceRect.bottom -= yOffset; - - // Now, this rect should be used to determine the monitor and thus taskbar size. This fixes - // scenarios where the zone lies approximately between two monitors, and the taskbar is on the left. - monitor = MonitorFromRect(&referenceRect, MONITOR_DEFAULTTOPRIMARY); - GetMonitorInfoW(monitor, &monitorInfo); - - xOffset = monitorInfo.rcWork.left - monitorInfo.rcMonitor.left; - yOffset = monitorInfo.rcWork.top - monitorInfo.rcMonitor.top; - - rect.left -= xOffset; - rect.right -= xOffset; - rect.top -= yOffset; - rect.bottom -= yOffset; - - const auto level = DPIAware::GetAwarenessLevel(GetWindowDpiAwarenessContext(window)); - const bool accountForUnawareness = level < DPIAware::PER_MONITOR_AWARE; - - if (accountForUnawareness && !allMonitorsHaveSameDpiScaling()) - { - rect.left = max(monitorInfo.rcMonitor.left, rect.left); - rect.right = min(monitorInfo.rcMonitor.right - xOffset, rect.right); - rect.top = max(monitorInfo.rcMonitor.top, rect.top); - rect.bottom = min(monitorInfo.rcMonitor.bottom - yOffset, rect.bottom); - } - } - - RECT AdjustRectForSizeWindowToRect(HWND window, RECT rect, HWND windowOfRect) noexcept - { - RECT newWindowRect = rect; - - RECT windowRect{}; - ::GetWindowRect(window, &windowRect); - - // Take care of borders - RECT frameRect{}; - if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect)))) - { - LONG leftMargin = frameRect.left - windowRect.left; - LONG rightMargin = frameRect.right - windowRect.right; - LONG bottomMargin = frameRect.bottom - windowRect.bottom; - newWindowRect.left -= leftMargin; - newWindowRect.right -= rightMargin; - newWindowRect.bottom -= bottomMargin; - } - - // Take care of windows that cannot be resized - if ((::GetWindowLong(window, GWL_STYLE) & WS_SIZEBOX) == 0) - { - newWindowRect.right = newWindowRect.left + (windowRect.right - windowRect.left); - newWindowRect.bottom = newWindowRect.top + (windowRect.bottom - windowRect.top); - } - - // Convert to screen coordinates - MapWindowRect(windowOfRect, nullptr, &newWindowRect); - - return newWindowRect; - } - - void SizeWindowToRect(HWND window, RECT rect) noexcept - { - WINDOWPLACEMENT placement{}; - ::GetWindowPlacement(window, &placement); - - // Wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685) - for (int i = 0; i < 5 && (placement.showCmd == SW_SHOWMINIMIZED); ++i) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - ::GetWindowPlacement(window, &placement); - } - - // Do not restore minimized windows. We change their placement though so they restore to the correct zone. - if ((placement.showCmd != SW_SHOWMINIMIZED) && - (placement.showCmd != SW_MINIMIZE)) - { - placement.showCmd = SW_RESTORE; - } - - // Remove maximized show command to make sure window is moved to the correct zone. - if (placement.showCmd == SW_SHOWMAXIMIZED) - { - placement.showCmd = SW_RESTORE; - placement.flags &= ~WPF_RESTORETOMAXIMIZED; - } - - ScreenToWorkAreaCoords(window, rect); - - placement.rcNormalPosition = rect; - placement.flags |= WPF_ASYNCWINDOWPLACEMENT; - - ::SetWindowPlacement(window, &placement); - // Do it again, allowing Windows to resize the window and set correct scaling - // This fixes Issue #365 - ::SetWindowPlacement(window, &placement); - } - - void SwitchToWindow(HWND window) noexcept - { - // Check if the window is minimized - if (IsIconic(window)) - { - // Show the window since SetForegroundWindow fails on minimized windows - ShowWindow(window, SW_RESTORE); - } - - // This is a hack to bypass the restriction on setting the foreground window - INPUT inputs[1] = { { .type = INPUT_MOUSE } }; - SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)); - - SetForegroundWindow(window); - } - - 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; - } - - bool IsStandardWindow(HWND window) - { - if (GetAncestor(window, GA_ROOT) != window || !IsWindowVisible(window)) - { - return false; - } - 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 false; - } - 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 false; - } - std::array class_name; - GetClassNameA(window, class_name.data(), static_cast(class_name.size())); - if (is_system_window(window, class_name.data())) - { - return false; - } - 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 false; - } - - return true; - } - - bool IsCandidateForZoning(HWND window, const std::vector& excludedApps) noexcept - { - auto zonable = IsStandardWindow(window) && HasNoVisibleOwner(window); - if (!zonable) - { - return false; - } - - return IsZonableByProcessPath(get_process_path(window), excludedApps); - } - - bool IsWindowMaximized(HWND window) noexcept - { - WINDOWPLACEMENT placement{}; - if (GetWindowPlacement(window, &placement) && - placement.showCmd == SW_SHOWMAXIMIZED) - { - return true; - } - return false; - } - - void SaveWindowSizeAndOrigin(HWND window) noexcept - { - HANDLE handle = GetPropW(window, ZonedWindowProperties::PropertyRestoreSizeID); - if (handle) - { - // Size already set, skip - return; - } - - RECT rect; - if (GetWindowRect(window, &rect)) - { - int width = rect.right - rect.left; - int height = rect.bottom - rect.top; - int originX = rect.left; - int originY = rect.top; - - DPIAware::InverseConvert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), width, height); - DPIAware::InverseConvert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), originX, originY); - - std::array windowSizeData = { width, height }; - std::array windowOriginData = { originX, originY }; - HANDLE rawData; - memcpy(&rawData, windowSizeData.data(), sizeof rawData); - SetPropW(window, ZonedWindowProperties::PropertyRestoreSizeID, rawData); - memcpy(&rawData, windowOriginData.data(), sizeof rawData); - SetPropW(window, ZonedWindowProperties::PropertyRestoreOriginID, rawData); - } - } - - void RestoreWindowSize(HWND window) noexcept - { - auto windowSizeData = GetPropW(window, ZonedWindowProperties::PropertyRestoreSizeID); - if (windowSizeData) - { - std::array windowSize; - memcpy(windowSize.data(), &windowSizeData, sizeof windowSize); - - // {width, height} - DPIAware::Convert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), windowSize[0], windowSize[1]); - - RECT rect; - if (GetWindowRect(window, &rect)) - { - rect.right = rect.left + windowSize[0]; - rect.bottom = rect.top + windowSize[1]; - SizeWindowToRect(window, rect); - } - - ::RemoveProp(window, ZonedWindowProperties::PropertyRestoreSizeID); - } - } - - void RestoreWindowOrigin(HWND window) noexcept - { - auto windowOriginData = GetPropW(window, ZonedWindowProperties::PropertyRestoreOriginID); - if (windowOriginData) - { - std::array windowOrigin; - memcpy(windowOrigin.data(), &windowOriginData, sizeof windowOrigin); - - // {width, height} - DPIAware::Convert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), windowOrigin[0], windowOrigin[1]); - - RECT rect; - if (GetWindowRect(window, &rect)) - { - int xOffset = windowOrigin[0] - rect.left; - int yOffset = windowOrigin[1] - rect.top; - - rect.left += xOffset; - rect.right += xOffset; - rect.top += yOffset; - rect.bottom += yOffset; - SizeWindowToRect(window, rect); - } - - ::RemoveProp(window, ZonedWindowProperties::PropertyRestoreOriginID); - } - } - bool IsValidGuid(const std::wstring& str) { GUID id; @@ -716,44 +359,4 @@ namespace FancyZonesUtils return windowRect; } - - bool IsProcessOfWindowElevated(HWND window) - { - DWORD pid = 0; - GetWindowThreadProcessId(window, &pid); - if (!pid) - { - return false; - } - - wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, - FALSE, - pid) }; - - wil::unique_handle token; - bool elevated = false; - - if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token)) - { - TOKEN_ELEVATION elevation; - DWORD size; - if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size)) - { - return elevation.TokenIsElevated != 0; - } - } - return false; - } - - bool IsSplashScreen(HWND window) - { - wchar_t className[MAX_PATH]; - if (GetClassName(window, className, MAX_PATH) == 0) - { - return false; - } - - return wcscmp(NonLocalizable::SplashClassName, className) == 0; - } - } diff --git a/src/modules/fancyzones/FancyZonesLib/util.h b/src/modules/fancyzones/FancyZonesLib/util.h index 0c19b8449d..6818ea2d67 100644 --- a/src/modules/fancyzones/FancyZonesLib/util.h +++ b/src/modules/fancyzones/FancyZonesLib/util.h @@ -1,13 +1,7 @@ #pragma once -#include "gdiplus.h" #include -namespace FancyZonesDataTypes -{ - struct DeviceIdData; -} - namespace FancyZonesUtils { struct Rect @@ -40,16 +34,6 @@ namespace FancyZonesUtils RECT m_rect{}; }; - 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) { ZeroMemory(quad, sizeof(*quad)); @@ -189,23 +173,6 @@ namespace FancyZonesUtils UINT GetDpiForMonitor(HMONITOR monitor) noexcept; void OrderMonitors(std::vector>& monitorInfo); - // Parameter rect is in windowOfRect coordinates - RECT AdjustRectForSizeWindowToRect(HWND window, RECT rect, HWND windowOfRect) noexcept; - - // Parameter rect must be in screen coordinates (e.g. obtained from GetWindowRect) - void SizeWindowToRect(HWND window, RECT rect) noexcept; - - void SwitchToWindow(HWND window) noexcept; - - bool HasNoVisibleOwner(HWND window) noexcept; - bool IsStandardWindow(HWND window); - bool IsCandidateForZoning(HWND window, const std::vector& 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); std::optional GuidFromString(const std::wstring& str) noexcept; std::optional GuidToString(const GUID& guid) noexcept; @@ -215,10 +182,5 @@ namespace FancyZonesUtils std::wstring TrimDeviceId(const std::wstring& deviceId); RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept; - size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector& zoneRects) noexcept; - - // If HWND is already dead, we assume it wasn't elevated - bool IsProcessOfWindowElevated(HWND window); - - bool IsSplashScreen(HWND window); + size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector& zoneRects) noexcept; } diff --git a/src/modules/fancyzones/FancyZonesModuleInterface/dllmain.cpp b/src/modules/fancyzones/FancyZonesModuleInterface/dllmain.cpp index 02000f3b33..92d11c242a 100644 --- a/src/modules/fancyzones/FancyZonesModuleInterface/dllmain.cpp +++ b/src/modules/fancyzones/FancyZonesModuleInterface/dllmain.cpp @@ -55,14 +55,13 @@ public: // These are the settings shown on the settings page along with their current values. virtual bool get_config(_Out_ PWSTR buffer, _Out_ int* buffer_size) override { - return m_settings->GetConfig(buffer, buffer_size); + return false; } // Passes JSON with the configuration settings for the powertoy. // This is called when the user hits Save on the settings page. virtual void set_config(PCWSTR config) override { - m_settings->SetConfig(config); } // Signal from the Settings editor to call a custom action. @@ -111,15 +110,15 @@ public: virtual void send_settings_telemetry() override { Logger::info("Send settings telemetry"); - Trace::SettingsTelemetry(*m_settings->GetSettings()); + FancyZonesSettings::instance().LoadSettings(); + Trace::SettingsTelemetry(FancyZonesSettings::settings()); } FancyZonesModuleInterface() { app_name = GET_RESOURCE_STRING(IDS_FANCYZONES); app_key = NonLocalizable::ModuleKey; - m_settings = MakeFancyZonesSettings(reinterpret_cast(&__ImageBase), FancyZonesModuleInterface::get_name(), FancyZonesModuleInterface::get_key()); - + m_toggleEditorEvent = CreateDefaultEvent(CommonSharedConstants::FANCY_ZONES_EDITOR_TOGGLE_EVENT); if (!m_toggleEditorEvent) { @@ -215,8 +214,6 @@ private: // Handle to event used to invoke FancyZones Editor HANDLE m_toggleEditorEvent; - - winrt::com_ptr m_settings; }; extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZones.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZones.Spec.cpp index cfc160d751..8e3b0e8bdd 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZones.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZones.Spec.cpp @@ -16,44 +16,29 @@ namespace FancyZonesUnitTests TEST_CLASS (FancyZonesUnitTests) { HINSTANCE m_hInst; - winrt::com_ptr m_settings; - const std::wstring_view m_moduleName = L"FancyZonesUnitTests"; - const std::wstring_view m_modulekey = L"FancyZonesUnitTests"; TEST_METHOD_INITIALIZE(Init) - { - m_hInst = (HINSTANCE)GetModuleHandleW(nullptr); - m_settings = MakeFancyZonesSettings(m_hInst, m_moduleName.data(), m_modulekey.data()); - Assert::IsTrue(m_settings != nullptr); - } - - TEST_METHOD_CLEANUP(CleanUp) { - std::filesystem::remove_all(PTSettingsHelper::get_module_save_folder_location(m_moduleName)); + m_hInst = (HINSTANCE)GetModuleHandleW(nullptr); } - TEST_METHOD (Create) - { - auto actual = MakeFancyZones(m_hInst, m_settings, nullptr); - Assert::IsNotNull(actual.get()); - } - TEST_METHOD (CreateWithEmptyHinstance) - { - auto actual = MakeFancyZones({}, m_settings, nullptr); - Assert::IsNotNull(actual.get()); - } + TEST_METHOD (Create) + { + auto actual = MakeFancyZones(m_hInst, nullptr); + Assert::IsNotNull(actual.get()); + } - TEST_METHOD (CreateWithNullHinstance) - { - auto actual = MakeFancyZones(nullptr, m_settings, nullptr); - Assert::IsNotNull(actual.get()); - } + TEST_METHOD (CreateWithEmptyHinstance) + { + auto actual = MakeFancyZones({}, nullptr); + Assert::IsNotNull(actual.get()); + } - TEST_METHOD (CreateWithNullSettings) - { - auto actual = MakeFancyZones(m_hInst, nullptr, nullptr); - Assert::IsNull(actual.get()); - } + TEST_METHOD (CreateWithNullHinstance) + { + auto actual = MakeFancyZones(nullptr, nullptr); + Assert::IsNotNull(actual.get()); + } }; TEST_CLASS (FancyZonesIFancyZonesCallbackUnitTests) @@ -61,7 +46,6 @@ namespace FancyZonesUnitTests HINSTANCE m_hInst{}; std::wstring m_moduleName = L"FancyZonesUnitTests"; std::wstring m_moduleKey = L"FancyZonesUnitTests"; - winrt::com_ptr m_settings = nullptr; winrt::com_ptr m_fzCallback = nullptr; std::wstring serializedPowerToySettings(const Settings& settings) @@ -112,10 +96,7 @@ namespace FancyZonesUnitTests TEST_METHOD_INITIALIZE(Init) { m_hInst = (HINSTANCE)GetModuleHandleW(nullptr); - m_settings = MakeFancyZonesSettings(m_hInst, m_moduleName.c_str(), m_moduleKey.c_str()); - Assert::IsTrue(m_settings != nullptr); - - auto fancyZones = MakeFancyZones(m_hInst, m_settings, nullptr); + auto fancyZones = MakeFancyZones(m_hInst, nullptr); Assert::IsTrue(fancyZones != nullptr); m_fzCallback = fancyZones.as(); diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZonesSettings.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZonesSettings.Spec.cpp index 3e6b4c530a..f9a06db000 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZonesSettings.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZonesSettings.Spec.cpp @@ -4,6 +4,7 @@ #include #include +#include #include using namespace Microsoft::VisualStudio::CppUnitTestFramework; @@ -58,503 +59,106 @@ namespace FancyZonesUnitTests compareHotkeyObjects(expected.prevTabHotkey, actual.prevTabHotkey); } - TEST_CLASS (FancyZonesSettingsCreationUnitTest) + TEST_CLASS (FancyZonesSettingsUnitTest) { - HINSTANCE m_hInst; - PCWSTR m_moduleName = L"FancyZonesUnitTests"; - PCWSTR m_moduleKey = L"FancyZonesUnitTests"; - std::wstring m_tmpName; - const Settings m_defaultSettings; TEST_METHOD_INITIALIZE(Init) - { - m_hInst = (HINSTANCE)GetModuleHandleW(nullptr); - m_tmpName = PTSettingsHelper::get_module_save_folder_location(m_moduleName) + L"\\settings.json"; - } - TEST_METHOD_CLEANUP(Cleanup) - { - std::filesystem::remove_all(PTSettingsHelper::get_module_save_folder_location(m_moduleName)); - } - - TEST_METHOD (CreateWithHinstanceDefault) - { - auto actual = MakeFancyZonesSettings({}, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(m_defaultSettings, *actualSettings); - } - - TEST_METHOD (CreateWithHinstanceNullptr) - { - auto actual = MakeFancyZonesSettings(nullptr, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(m_defaultSettings, *actualSettings); - } - - TEST_METHOD (CreateWithNameEmpty) - { - auto actual = MakeFancyZonesSettings(m_hInst, L"", m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(m_defaultSettings, *actualSettings); - } - - TEST_METHOD (Create) - { - //prepare data - const Settings expected; - - PowerToysSettings::PowerToyValues values(m_moduleName, m_moduleKey); - values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag); - values.add_property(L"fancyzones_mouseSwitch", expected.mouseSwitch); - values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows); - values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones); - values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows); - values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys); - values.add_property(L"fancyzones_moveWindowAcrossMonitors", expected.moveWindowAcrossMonitors); - values.add_property(L"fancyzones_moveWindowsBasedOnPosition", expected.moveWindowsBasedOnPosition); - values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows); - values.add_property(L"fancyzones_openWindowOnActiveMonitor", expected.openWindowOnActiveMonitor); - values.add_property(L"fancyzones_restoreSize", expected.restoreSize); - values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen); - values.add_property(L"fancyzones_show_on_all_monitors", expected.showZonesOnAllMonitors); - values.add_property(L"fancyzones_multi_monitor_mode", expected.spanZonesAcrossMonitors); - values.add_property(L"fancyzones_makeDraggedWindowTransparent", expected.makeDraggedWindowTransparent); - values.add_property(L"fancyzones_zoneColor", expected.zoneColor); - values.add_property(L"fancyzones_zoneBorderColor", expected.zoneBorderColor); - values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor); - values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity); - values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json()); - values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching); - values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json()); - values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json()); - values.add_property(L"fancyzones_excluded_apps", expected.excludedApps); - - values.save_to_settings_file(); - - auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(expected, *actualSettings); - } - - TEST_METHOD (CreateWithMultipleApps) - { - //prepare data - const Settings expected{ - .excludedApps = L"app\r\napp1\r\napp2\r\nanother app", - .excludedAppsArray = { L"APP", L"APP1", L"APP2", L"ANOTHER APP" }, - }; - - PowerToysSettings::PowerToyValues values(m_moduleName, m_moduleKey); - values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag); - values.add_property(L"fancyzones_mouseSwitch", expected.mouseSwitch); - values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows); - values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones); - values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows); - values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys); - values.add_property(L"fancyzones_moveWindowAcrossMonitors", expected.moveWindowAcrossMonitors); - values.add_property(L"fancyzones_moveWindowsBasedOnPosition", expected.moveWindowsBasedOnPosition); - values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows); - values.add_property(L"fancyzones_openWindowOnActiveMonitor", expected.openWindowOnActiveMonitor); - values.add_property(L"fancyzones_restoreSize", expected.restoreSize); - values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen); - values.add_property(L"fancyzones_show_on_all_monitors", expected.showZonesOnAllMonitors); - values.add_property(L"fancyzones_multi_monitor_mode", expected.spanZonesAcrossMonitors); - values.add_property(L"fancyzones_makeDraggedWindowTransparent", expected.makeDraggedWindowTransparent); - values.add_property(L"fancyzones_zoneColor", expected.zoneColor); - values.add_property(L"fancyzones_zoneBorderColor", expected.zoneBorderColor); - values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor); - values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity); - values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json()); - values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching); - values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json()); - values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json()); - values.add_property(L"fancyzones_excluded_apps", expected.excludedApps); - - values.save_to_settings_file(); - - auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(expected, *actualSettings); - } - - TEST_METHOD (CreateWithBoolValuesMissed) - { - const Settings expected{ - .shiftDrag = m_defaultSettings.shiftDrag, - .mouseSwitch = m_defaultSettings.mouseSwitch, - .displayChange_moveWindows = m_defaultSettings.displayChange_moveWindows, - .zoneSetChange_flashZones = m_defaultSettings.zoneSetChange_flashZones, - .zoneSetChange_moveWindows = m_defaultSettings.zoneSetChange_moveWindows, - .overrideSnapHotkeys = m_defaultSettings.overrideSnapHotkeys, - .moveWindowAcrossMonitors = m_defaultSettings.moveWindowAcrossMonitors, - .moveWindowsBasedOnPosition = m_defaultSettings.moveWindowsBasedOnPosition, - .appLastZone_moveWindows = m_defaultSettings.appLastZone_moveWindows, - .openWindowOnActiveMonitor = m_defaultSettings.openWindowOnActiveMonitor, - .restoreSize = m_defaultSettings.restoreSize, - .use_cursorpos_editor_startupscreen = m_defaultSettings.use_cursorpos_editor_startupscreen, - .showZonesOnAllMonitors = m_defaultSettings.showZonesOnAllMonitors, - .spanZonesAcrossMonitors = m_defaultSettings.spanZonesAcrossMonitors, - .makeDraggedWindowTransparent = m_defaultSettings.makeDraggedWindowTransparent, - .zoneColor = L"FAFAFA", - .zoneBorderColor = L"CCDDEE", - .zoneHighlightColor = L"#00FFD7", - .zoneHighlightOpacity = 45, - .editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_OEM_3), - .nextTabHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_NEXT), - .prevTabHotkey = PowerToysSettings::HotkeyObject::from_settings(false, true, true, false, VK_PRIOR), - .excludedApps = L"app", - .excludedAppsArray = { L"APP" }, - }; - - PowerToysSettings::PowerToyValues values(m_moduleName, m_moduleKey); - values.add_property(L"fancyzones_zoneColor", expected.zoneColor); - values.add_property(L"fancyzones_zoneBorderColor", expected.zoneBorderColor); - values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor); - values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity); - values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json()); - values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching); - values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json()); - values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json()); - values.add_property(L"fancyzones_excluded_apps", expected.excludedApps); - - values.save_to_settings_file(); - - auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(expected, *actualSettings); - } - - TEST_METHOD (CreateColorMissed) - { - //prepare data - const Settings expected; - - PowerToysSettings::PowerToyValues values(m_moduleName, m_moduleKey); - values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag); - values.add_property(L"fancyzones_mouseSwitch", expected.mouseSwitch); - values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows); - values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones); - values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows); - values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys); - values.add_property(L"fancyzones_moveWindowAcrossMonitors", expected.moveWindowAcrossMonitors); - values.add_property(L"fancyzones_moveWindowsBasedOnPosition", expected.moveWindowsBasedOnPosition); - values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows); - values.add_property(L"fancyzones_openWindowOnActiveMonitor", expected.openWindowOnActiveMonitor); - values.add_property(L"fancyzones_restoreSize", expected.restoreSize); - values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen); - values.add_property(L"fancyzones_show_on_all_monitors", expected.showZonesOnAllMonitors); - values.add_property(L"fancyzones_multi_monitor_mode", expected.spanZonesAcrossMonitors); - values.add_property(L"fancyzones_makeDraggedWindowTransparent", expected.makeDraggedWindowTransparent); - values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity); - values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json()); - values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching); - values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json()); - values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json()); - values.add_property(L"fancyzones_excluded_apps", expected.excludedApps); - - values.save_to_settings_file(); - - auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(expected, *actualSettings); - } - - TEST_METHOD (CreateOpacityMissed) - { - //prepare data - const Settings expected; - - PowerToysSettings::PowerToyValues values(m_moduleName, m_moduleKey); - values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag); - values.add_property(L"fancyzones_mouseSwitch", expected.mouseSwitch); - values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows); - values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones); - values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows); - values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys); - values.add_property(L"fancyzones_moveWindowAcrossMonitors", expected.moveWindowAcrossMonitors); - values.add_property(L"fancyzones_moveWindowsBasedOnPosition", expected.moveWindowsBasedOnPosition); - values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows); - values.add_property(L"fancyzones_openWindowOnActiveMonitor", expected.openWindowOnActiveMonitor); - values.add_property(L"fancyzones_restoreSize", expected.restoreSize); - values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen); - values.add_property(L"fancyzones_show_on_all_monitors", expected.showZonesOnAllMonitors); - values.add_property(L"fancyzones_multi_monitor_mode", expected.spanZonesAcrossMonitors); - values.add_property(L"fancyzones_makeDraggedWindowTransparent", expected.makeDraggedWindowTransparent); - values.add_property(L"fancyzones_zoneColor", expected.zoneColor); - values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor); - values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json()); - values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching); - values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json()); - values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json()); - values.add_property(L"fancyzones_excluded_apps", expected.excludedApps); - - values.save_to_settings_file(); - - auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(expected, *actualSettings); - } - - TEST_METHOD (CreateHotkeyMissed) - { - //prepare data - const Settings expected; - - PowerToysSettings::PowerToyValues values(m_moduleName, m_moduleKey); - values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag); - values.add_property(L"fancyzones_mouseSwitch", expected.mouseSwitch); - values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows); - values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones); - values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows); - values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys); - values.add_property(L"fancyzones_moveWindowAcrossMonitors", expected.moveWindowAcrossMonitors); - values.add_property(L"fancyzones_moveWindowsBasedOnPosition", expected.moveWindowsBasedOnPosition); - values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows); - values.add_property(L"fancyzones_openWindowOnActiveMonitor", expected.openWindowOnActiveMonitor); - values.add_property(L"fancyzones_restoreSize", expected.restoreSize); - values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen); - values.add_property(L"fancyzones_show_on_all_monitors", expected.showZonesOnAllMonitors); - values.add_property(L"fancyzones_multi_monitor_mode", expected.spanZonesAcrossMonitors); - values.add_property(L"fancyzones_makeDraggedWindowTransparent", expected.makeDraggedWindowTransparent); - values.add_property(L"fancyzones_zoneColor", expected.zoneColor); - values.add_property(L"fancyzones_zoneBorderColor", expected.zoneBorderColor); - values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor); - values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity); - values.add_property(L"fancyzones_excluded_apps", expected.excludedApps); - - values.save_to_settings_file(); - - auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(expected, *actualSettings); - } - - TEST_METHOD (CreateAppsMissed) - { - //prepare data - const Settings expected; - - PowerToysSettings::PowerToyValues values(m_moduleName, m_moduleKey); - values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag); - values.add_property(L"fancyzones_mouseSwitch", expected.mouseSwitch); - values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows); - values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones); - values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows); - values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys); - values.add_property(L"fancyzones_moveWindowAcrossMonitors", expected.moveWindowAcrossMonitors); - values.add_property(L"fancyzones_moveWindowsBasedOnPosition", expected.moveWindowsBasedOnPosition); - values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows); - values.add_property(L"fancyzones_openWindowOnActiveMonitor", expected.openWindowOnActiveMonitor); - values.add_property(L"fancyzones_restoreSize", expected.restoreSize); - values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen); - values.add_property(L"fancyzones_show_on_all_monitors", expected.showZonesOnAllMonitors); - values.add_property(L"fancyzones_multi_monitor_mode", expected.spanZonesAcrossMonitors); - values.add_property(L"fancyzones_makeDraggedWindowTransparent", expected.makeDraggedWindowTransparent); - values.add_property(L"fancyzones_zoneColor", expected.zoneColor); - values.add_property(L"fancyzones_zoneBorderColor", expected.zoneBorderColor); - values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor); - values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity); - values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json()); - values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching); - values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json()); - values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json()); - - values.save_to_settings_file(); - - auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(expected, *actualSettings); - } - - TEST_METHOD (CreateWithEmptyJson) - { - json::to_file(m_tmpName, json::JsonObject()); - auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(m_defaultSettings, *actualSettings); - } - - TEST_METHOD (CreateWithCorruptedJson) - { - std::wofstream{ m_tmpName.data(), std::ios::binary } << L"{ \"version\": \"1.0\", \"name\": \""; - - auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName, m_moduleKey); - - Assert::IsTrue(actual != nullptr); - auto actualSettings = actual->GetSettings(); - compareSettings(m_defaultSettings, *actualSettings); - } - - TEST_METHOD (CreateWithCyrillicSymbolsInJson) - { - std::wofstream{ m_tmpName.data(), std::ios::binary } << L"{ \"version\": \"1.0\", \"name\": \"ФансиЗонс\"}"; - auto actual = MakeFancyZonesSettings(m_hInst, m_moduleName, m_moduleKey); - Assert::IsTrue(actual != nullptr); - - auto actualSettings = actual->GetSettings(); - compareSettings(m_defaultSettings, *actualSettings); - } - }; - - TEST_CLASS (FancyZonesSettingsUnitTests) - { - winrt::com_ptr m_settings = nullptr; - PCWSTR m_moduleName = L"FancyZonesUnitTests"; - PCWSTR m_moduleKey = L"FancyZonesUnitTests"; - - std::wstring serializedPowerToySettings(const Settings& settings) { - PowerToysSettings::Settings ptSettings(HINSTANCE{}, m_moduleName); - ptSettings.set_description(IDS_SETTING_DESCRIPTION); - ptSettings.set_icon_key(L"pt-fancy-zones"); - ptSettings.set_overview_link(L"https://aka.ms/PowerToysOverview_FancyZones"); - ptSettings.set_video_link(L"https://youtu.be/rTtGzZYAXgY"); + // reset to defaults + PowerToysSettings::PowerToyValues values(NonLocalizable::ModuleKey, NonLocalizable::ModuleKey); + values.add_property(L"fancyzones_shiftDrag", m_defaultSettings.shiftDrag); + values.add_property(L"fancyzones_mouseSwitch", m_defaultSettings.mouseSwitch); + values.add_property(L"fancyzones_displayChange_moveWindows", m_defaultSettings.displayChange_moveWindows); + values.add_property(L"fancyzones_zoneSetChange_flashZones", m_defaultSettings.zoneSetChange_flashZones); + values.add_property(L"fancyzones_zoneSetChange_moveWindows", m_defaultSettings.zoneSetChange_moveWindows); + values.add_property(L"fancyzones_overrideSnapHotkeys", m_defaultSettings.overrideSnapHotkeys); + values.add_property(L"fancyzones_moveWindowAcrossMonitors", m_defaultSettings.moveWindowAcrossMonitors); + values.add_property(L"fancyzones_moveWindowsBasedOnPosition", m_defaultSettings.moveWindowsBasedOnPosition); + values.add_property(L"fancyzones_appLastZone_moveWindows", m_defaultSettings.appLastZone_moveWindows); + values.add_property(L"fancyzones_openWindowOnActiveMonitor", m_defaultSettings.openWindowOnActiveMonitor); + values.add_property(L"fancyzones_restoreSize", m_defaultSettings.restoreSize); + values.add_property(L"use_cursorpos_editor_startupscreen", m_defaultSettings.use_cursorpos_editor_startupscreen); + values.add_property(L"fancyzones_show_on_all_monitors", m_defaultSettings.showZonesOnAllMonitors); + values.add_property(L"fancyzones_multi_monitor_mode", m_defaultSettings.spanZonesAcrossMonitors); + values.add_property(L"fancyzones_makeDraggedWindowTransparent", m_defaultSettings.makeDraggedWindowTransparent); + values.add_property(L"fancyzones_zoneColor", m_defaultSettings.zoneColor); + values.add_property(L"fancyzones_zoneBorderColor", m_defaultSettings.zoneBorderColor); + values.add_property(L"fancyzones_zoneHighlightColor", m_defaultSettings.zoneHighlightColor); + values.add_property(L"fancyzones_highlight_opacity", m_defaultSettings.zoneHighlightOpacity); + values.add_property(L"fancyzones_editor_hotkey", m_defaultSettings.editorHotkey.get_json()); + values.add_property(L"fancyzones_windowSwitching", m_defaultSettings.windowSwitching); + values.add_property(L"fancyzones_nextTab_hotkey", m_defaultSettings.nextTabHotkey.get_json()); + values.add_property(L"fancyzones_prevTab_hotkey", m_defaultSettings.prevTabHotkey.get_json()); + values.add_property(L"fancyzones_excluded_apps", m_defaultSettings.excludedApps); - ptSettings.add_custom_action( - L"ToggledFZEditor", // action name. - IDS_SETTING_LAUNCH_EDITOR_LABEL, - IDS_SETTING_LAUNCH_EDITOR_BUTTON, - IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION); - ptSettings.add_hotkey(L"fancyzones_editor_hotkey", IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL, settings.editorHotkey); - ptSettings.add_bool_toggle(L"fancyzones_windowSwitching", IDS_SETTING_WINDOW_SWITCHING_TOGGLE_LABEL, settings.windowSwitching); - ptSettings.add_hotkey(L"fancyzones_nextTab_hotkey", IDS_SETTING_NEXT_TAB_HOTKEY_LABEL, settings.nextTabHotkey); - ptSettings.add_hotkey(L"fancyzones_prevTab_hotkey", IDS_SETTING_PREV_TAB_HOTKEY_LABEL, settings.prevTabHotkey); - ptSettings.add_bool_toggle(L"fancyzones_shiftDrag", IDS_SETTING_DESCRIPTION_SHIFTDRAG, settings.shiftDrag); - ptSettings.add_bool_toggle(L"fancyzones_mouseSwitch", IDS_SETTING_DESCRIPTION_MOUSESWITCH, settings.mouseSwitch); - ptSettings.add_bool_toggle(L"fancyzones_overrideSnapHotkeys", IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS, settings.overrideSnapHotkeys); - ptSettings.add_bool_toggle(L"fancyzones_moveWindowAcrossMonitors", IDS_SETTING_DESCRIPTION_MOVE_WINDOW_ACROSS_MONITORS, settings.moveWindowAcrossMonitors); - ptSettings.add_bool_toggle(L"fancyzones_moveWindowsBasedOnPosition", IDS_SETTING_DESCRIPTION_MOVE_WINDOWS_BASED_ON_POSITION, settings.moveWindowsBasedOnPosition); - ptSettings.add_bool_toggle(L"fancyzones_zoneSetChange_flashZones", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES, settings.zoneSetChange_flashZones); - ptSettings.add_bool_toggle(L"fancyzones_displayChange_moveWindows", IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS, settings.displayChange_moveWindows); - ptSettings.add_bool_toggle(L"fancyzones_zoneSetChange_moveWindows", IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS, settings.zoneSetChange_moveWindows); - ptSettings.add_bool_toggle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, settings.appLastZone_moveWindows); - ptSettings.add_bool_toggle(L"fancyzones_openWindowOnActiveMonitor", IDS_SETTING_DESCRIPTION_OPEN_WINDOW_ON_ACTIVE_MONITOR, settings.openWindowOnActiveMonitor); - ptSettings.add_bool_toggle(L"fancyzones_restoreSize", IDS_SETTING_DESCRIPTION_RESTORESIZE, settings.restoreSize); - ptSettings.add_bool_toggle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, settings.use_cursorpos_editor_startupscreen); - ptSettings.add_bool_toggle(L"fancyzones_show_on_all_monitors", IDS_SETTING_DESCRIPTION_SHOW_FANCY_ZONES_ON_ALL_MONITORS, settings.showZonesOnAllMonitors); - ptSettings.add_bool_toggle(L"fancyzones_multi_monitor_mode", IDS_SETTING_DESCRIPTION_SPAN_ZONES_ACROSS_MONITORS, settings.spanZonesAcrossMonitors); - ptSettings.add_bool_toggle(L"fancyzones_makeDraggedWindowTransparent", IDS_SETTING_DESCRIPTION_MAKE_DRAGGED_WINDOW_TRANSPARENT, settings.makeDraggedWindowTransparent); - ptSettings.add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, settings.zoneHighlightOpacity, 0, 100, 1); - ptSettings.add_color_picker(L"fancyzones_zoneColor", IDS_SETTING_DESCRIPTION_ZONECOLOR, settings.zoneColor); - ptSettings.add_color_picker(L"fancyzones_zoneBorderColor", IDS_SETTING_DESCRIPTION_ZONE_BORDER_COLOR, settings.zoneBorderColor); - ptSettings.add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, settings.zoneHighlightColor); - ptSettings.add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLUDED_APPS_DESCRIPTION, settings.excludedApps); - - return ptSettings.serialize(); + json::to_file(FancyZonesSettings::GetSettingsFileName(), values.get_raw_json()); + FancyZonesSettings::instance().LoadSettings(); + } + + TEST_METHOD_CLEANUP(Cleanup) + { + std::filesystem::remove(FancyZonesSettings::GetSettingsFileName()); } - TEST_METHOD_INITIALIZE(Init) - { - HINSTANCE hInst = (HINSTANCE)GetModuleHandleW(nullptr); + TEST_METHOD (Parse) + { + //prepare data + const Settings expected{ + .excludedApps = L"app\r\napp1\r\napp2\r\nanother app", + .excludedAppsArray = { L"APP", L"APP1", L"APP2", L"ANOTHER APP" }, + }; - m_settings = MakeFancyZonesSettings(hInst, m_moduleName, m_moduleKey); - Assert::IsTrue(m_settings != nullptr); - } + PowerToysSettings::PowerToyValues values(NonLocalizable::ModuleKey, NonLocalizable::ModuleKey); + values.add_property(L"fancyzones_shiftDrag", expected.shiftDrag); + values.add_property(L"fancyzones_mouseSwitch", expected.mouseSwitch); + values.add_property(L"fancyzones_displayChange_moveWindows", expected.displayChange_moveWindows); + values.add_property(L"fancyzones_zoneSetChange_flashZones", expected.zoneSetChange_flashZones); + values.add_property(L"fancyzones_zoneSetChange_moveWindows", expected.zoneSetChange_moveWindows); + values.add_property(L"fancyzones_overrideSnapHotkeys", expected.overrideSnapHotkeys); + values.add_property(L"fancyzones_moveWindowAcrossMonitors", expected.moveWindowAcrossMonitors); + values.add_property(L"fancyzones_moveWindowsBasedOnPosition", expected.moveWindowsBasedOnPosition); + values.add_property(L"fancyzones_appLastZone_moveWindows", expected.appLastZone_moveWindows); + values.add_property(L"fancyzones_openWindowOnActiveMonitor", expected.openWindowOnActiveMonitor); + values.add_property(L"fancyzones_restoreSize", expected.restoreSize); + values.add_property(L"use_cursorpos_editor_startupscreen", expected.use_cursorpos_editor_startupscreen); + values.add_property(L"fancyzones_show_on_all_monitors", expected.showZonesOnAllMonitors); + values.add_property(L"fancyzones_multi_monitor_mode", expected.spanZonesAcrossMonitors); + values.add_property(L"fancyzones_makeDraggedWindowTransparent", expected.makeDraggedWindowTransparent); + values.add_property(L"fancyzones_zoneColor", expected.zoneColor); + values.add_property(L"fancyzones_zoneBorderColor", expected.zoneBorderColor); + values.add_property(L"fancyzones_zoneHighlightColor", expected.zoneHighlightColor); + values.add_property(L"fancyzones_highlight_opacity", expected.zoneHighlightOpacity); + values.add_property(L"fancyzones_editor_hotkey", expected.editorHotkey.get_json()); + values.add_property(L"fancyzones_windowSwitching", expected.windowSwitching); + values.add_property(L"fancyzones_nextTab_hotkey", expected.nextTabHotkey.get_json()); + values.add_property(L"fancyzones_prevTab_hotkey", expected.prevTabHotkey.get_json()); + values.add_property(L"fancyzones_excluded_apps", expected.excludedApps); - TEST_METHOD_CLEANUP(Cleanup) - { - std::filesystem::remove_all(PTSettingsHelper::get_module_save_folder_location(m_moduleName)); - } + json::to_file(FancyZonesSettings::GetSettingsFileName(), values.get_raw_json()); + + FancyZonesSettings::instance().LoadSettings(); + auto actual = FancyZonesSettings::settings(); + compareSettings(expected, actual); + } - TEST_METHOD (GetConfig) - { - int expectedSize = 0; - m_settings->GetConfig(nullptr, &expectedSize); - Assert::AreNotEqual(0, expectedSize); + TEST_METHOD (ParseInvalid) + { + PowerToysSettings::PowerToyValues values(NonLocalizable::ModuleKey, NonLocalizable::ModuleKey); + values.add_property(L"non_fancyzones_value", false); - int actualBufferSize = expectedSize; - PWSTR actualBuffer = new wchar_t[actualBufferSize]; + json::to_file(FancyZonesSettings::GetSettingsFileName(), values.get_raw_json()); - Assert::IsTrue(m_settings->GetConfig(actualBuffer, &actualBufferSize)); - Assert::AreEqual(expectedSize, actualBufferSize); - } + FancyZonesSettings::instance().LoadSettings(); + auto actual = FancyZonesSettings::settings(); + compareSettings(m_defaultSettings, actual); + } - TEST_METHOD (GetConfigSmallBuffer) - { - int size = 0; - m_settings->GetConfig(nullptr, &size); - Assert::AreNotEqual(0, size); - - int actualBufferSize = size - 1; - PWSTR actualBuffer = new wchar_t[actualBufferSize]; - - Assert::IsFalse(m_settings->GetConfig(actualBuffer, &actualBufferSize)); - Assert::AreEqual(size, actualBufferSize); - } - - TEST_METHOD (GetConfigNullBuffer) - { - int expectedSize = 0; - m_settings->GetConfig(nullptr, &expectedSize); - Assert::AreNotEqual(0, expectedSize); - - int actualBufferSize = 0; - - Assert::IsFalse(m_settings->GetConfig(nullptr, &actualBufferSize)); - Assert::AreEqual(expectedSize, actualBufferSize); - } - - TEST_METHOD (SetConfig) - { - //cleanup file before call set config - const auto settingsFile = PTSettingsHelper::get_module_save_folder_location(m_moduleName) + L"\\settings.json"; - std::filesystem::remove(settingsFile); - - const Settings expected{ - .shiftDrag = true, - .mouseSwitch = true, - .displayChange_moveWindows = true, - .zoneSetChange_flashZones = false, - .zoneSetChange_moveWindows = true, - .overrideSnapHotkeys = false, - .moveWindowAcrossMonitors = false, - .appLastZone_moveWindows = true, - .openWindowOnActiveMonitor = false, - .restoreSize = false, - .use_cursorpos_editor_startupscreen = true, - .showZonesOnAllMonitors = false, - .spanZonesAcrossMonitors = false, - .makeDraggedWindowTransparent = true, - .zoneColor = L"#FAFAFA", - .zoneBorderColor = L"CCDDEE", - .zoneHighlightColor = L"#00AABB", - .zoneHighlightOpacity = 45, - .editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3), - .nextTabHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_NEXT), - .prevTabHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_PRIOR), - .excludedApps = L"app\r\napp2", - .excludedAppsArray = { L"APP", L"APP2" }, - }; - - auto config = serializedPowerToySettings(expected); - m_settings->SetConfig(config.c_str()); - - auto actual = m_settings->GetSettings(); - compareSettings(expected, *actual); - - Assert::IsTrue(std::filesystem::exists(settingsFile)); - } + TEST_METHOD (ParseEmpty) + { + FancyZonesSettings::instance().LoadSettings(); + auto actual = FancyZonesSettings::settings(); + compareSettings(m_defaultSettings, actual); + } }; } diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp index c80a64afb8..0aecf6d23b 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "Util.h" #include @@ -31,9 +30,6 @@ namespace FancyZonesUnitTests HMONITOR m_monitor{}; MONITORINFOEX m_monitorInfo{}; GUID m_virtualDesktopGuid{}; - ZoneColors m_zoneColors{}; - OverlappingZonesAlgorithm m_overlappingAlgorithm = OverlappingZonesAlgorithm::Positional; - bool m_showZoneText = true; void testWorkArea(winrt::com_ptr workArea) { @@ -65,13 +61,6 @@ namespace FancyZonesUnitTests Assert::IsTrue(guid.has_value()); m_virtualDesktopGuid = *guid; - m_zoneColors = ZoneColors{ - .primaryColor = FancyZonesUtils::HexToRGB(L"#4287f5"), - .borderColor = FancyZonesUtils::HexToRGB(L"#FFFFFF"), - .highlightColor = FancyZonesUtils::HexToRGB(L"#42eff5"), - .highlightOpacity = 50, - }; - AppZoneHistory::instance().LoadData(); AppliedLayouts::instance().LoadData(); } @@ -84,7 +73,7 @@ namespace FancyZonesUnitTests TEST_METHOD (CreateWorkArea) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); testWorkArea(workArea); auto* zoneSet{ workArea->ZoneSet() }; @@ -95,7 +84,7 @@ namespace FancyZonesUnitTests TEST_METHOD (CreateWorkAreaNoHinst) { - auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId, {}); testWorkArea(workArea); auto* zoneSet{ workArea->ZoneSet() }; @@ -106,7 +95,7 @@ namespace FancyZonesUnitTests TEST_METHOD (CreateWorkAreaNoHinstFlashZones) { - auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId, {}); testWorkArea(workArea); auto* zoneSet{ workArea->ZoneSet() }; @@ -117,7 +106,7 @@ namespace FancyZonesUnitTests TEST_METHOD (CreateWorkAreaNoMonitor) { - auto workArea = MakeWorkArea(m_hInst, {}, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, {}, m_uniqueId, {}); testWorkArea(workArea); } @@ -136,7 +125,7 @@ namespace FancyZonesUnitTests uniqueIdData.height = monitorRect.height(); } - auto workArea = MakeWorkArea(m_hInst, m_monitor, uniqueIdData, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, uniqueIdData, {}); const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom); const FancyZonesDataTypes::DeviceIdData expectedUniqueId{ L"FallbackDevice", m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left, m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top, m_virtualDesktopGuid }; @@ -165,7 +154,7 @@ namespace FancyZonesUnitTests uniqueId.height = monitorRect.height(); } - auto workArea = MakeWorkArea(m_hInst, m_monitor, uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, uniqueId, {}); const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom); Assert::IsNotNull(workArea.get()); @@ -185,10 +174,10 @@ namespace FancyZonesUnitTests const int zoneCount = 5; const auto customSetGuid = Helpers::CreateGuidString(); - auto parentWorkArea = MakeWorkArea(m_hInst, m_monitor, m_parentUniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto parentWorkArea = MakeWorkArea(m_hInst, m_monitor, m_parentUniqueId, {}); // newWorkArea = false - workArea won't be cloned from parent - auto actualWorkArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto actualWorkArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); Assert::IsNotNull(actualWorkArea->ZoneSet()); @@ -209,9 +198,6 @@ namespace FancyZonesUnitTests HINSTANCE m_hInst{}; HMONITOR m_monitor{}; MONITORINFO m_monitorInfo{}; - ZoneColors m_zoneColors{}; - OverlappingZonesAlgorithm m_overlappingAlgorithm = OverlappingZonesAlgorithm::Positional; - bool m_showZoneText = true; TEST_METHOD_INITIALIZE(Init) { @@ -225,13 +211,6 @@ namespace FancyZonesUnitTests m_uniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left; m_uniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top; CLSIDFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}", &m_uniqueId.virtualDesktopId); - - m_zoneColors = ZoneColors{ - .primaryColor = FancyZonesUtils::HexToRGB(L"#4287f5"), - .borderColor = FancyZonesUtils::HexToRGB(L"#FFFFFF"), - .highlightColor = FancyZonesUtils::HexToRGB(L"#42eff5"), - .highlightOpacity = 50, - }; AppZoneHistory::instance().LoadData(); AppliedLayouts::instance().LoadData(); @@ -246,7 +225,7 @@ namespace FancyZonesUnitTests public: TEST_METHOD (MoveSizeEnter) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); const auto expected = S_OK; const auto actual = workArea->MoveSizeEnter(Mocks::Window()); @@ -256,7 +235,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeEnterTwice) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); const auto expected = S_OK; @@ -268,7 +247,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeUpdate) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); const auto expected = S_OK; const auto actual = workArea->MoveSizeUpdate(POINT{ 0, 0 }, true, false); @@ -278,7 +257,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeUpdatePointNegativeCoordinates) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); const auto expected = S_OK; const auto actual = workArea->MoveSizeUpdate(POINT{ -10, -10 }, true, false); @@ -288,7 +267,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeUpdatePointBigCoordinates) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); const auto expected = S_OK; const auto actual = workArea->MoveSizeUpdate(POINT{ m_monitorInfo.rcMonitor.right + 1, m_monitorInfo.rcMonitor.bottom + 1 }, true, false); @@ -298,7 +277,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeEnd) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); const auto window = Mocks::Window(); workArea->MoveSizeEnter(window); @@ -315,7 +294,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeEndWindowNotAdded) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); const auto window = Mocks::Window(); workArea->MoveSizeEnter(window); @@ -331,7 +310,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeEndDifferentWindows) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); const auto window = Mocks::Window(); workArea->MoveSizeEnter(window); @@ -344,7 +323,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeEndWindowNotSet) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); const auto expected = E_INVALIDARG; const auto actual = workArea->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 }); @@ -354,7 +333,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeEndInvalidPoint) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); const auto window = Mocks::Window(); workArea->MoveSizeEnter(window); @@ -371,7 +350,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveWindowIntoZoneByIndex) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); Assert::IsNotNull(workArea->ZoneSet()); workArea->MoveWindowIntoZoneByIndex(Mocks::Window(), 0); @@ -381,7 +360,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveWindowIntoZoneByDirectionAndIndex) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); Assert::IsNotNull(workArea->ZoneSet()); const auto window = Mocks::WindowCreate(m_hInst); @@ -396,7 +375,7 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveWindowIntoZoneByDirectionManyTimes) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); Assert::IsNotNull(workArea->ZoneSet()); const auto window = Mocks::WindowCreate(m_hInst); @@ -413,7 +392,7 @@ namespace FancyZonesUnitTests TEST_METHOD (SaveWindowProcessToZoneIndexNullptrWindow) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); Assert::IsNotNull(workArea->ZoneSet()); workArea->SaveWindowProcessToZoneIndex(nullptr); @@ -424,7 +403,7 @@ namespace FancyZonesUnitTests TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAdded) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); Assert::IsNotNull(workArea->ZoneSet()); auto window = Mocks::WindowCreate(m_hInst); @@ -439,7 +418,7 @@ namespace FancyZonesUnitTests TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAddedWithFilledAppZoneHistory) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); Assert::IsNotNull(workArea->ZoneSet()); const auto window = Mocks::WindowCreate(m_hInst); @@ -467,7 +446,7 @@ namespace FancyZonesUnitTests TEST_METHOD (SaveWindowProcessToZoneIndexWindowAdded) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); Assert::IsNotNull(workArea->ZoneSet()); auto window = Mocks::WindowCreate(m_hInst); @@ -497,7 +476,7 @@ namespace FancyZonesUnitTests TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText); + auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}); Assert::IsNotNull(workArea->ZoneSet()); auto window = Mocks::WindowCreate(m_hInst); diff --git a/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs b/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs index 5e58fa4b8b..12304dd1ef 100644 --- a/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs +++ b/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs @@ -43,6 +43,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library FancyzonesNextTabHotkey = new KeyboardKeysProperty(DefaultNextTabHotkeyValue); FancyzonesPrevTabHotkey = new KeyboardKeysProperty(DefaultPrevTabHotkeyValue); FancyzonesMakeDraggedWindowTransparent = new BoolProperty(); + FancyzonesAllowPopupWindowSnap = new BoolProperty(); + FancyzonesAllowChildWindowSnap = new BoolProperty(); FancyzonesExcludedApps = new StringProperty(); FancyzonesInActiveColor = new StringProperty(ConfigDefaults.DefaultFancyZonesInActiveColor); FancyzonesBorderColor = new StringProperty(ConfigDefaults.DefaultFancyzonesBorderColor); @@ -102,6 +104,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("fancyzones_makeDraggedWindowTransparent")] public BoolProperty FancyzonesMakeDraggedWindowTransparent { get; set; } + [JsonPropertyName("fancyzones_allowPopupWindowSnap")] + public BoolProperty FancyzonesAllowPopupWindowSnap { get; set; } + + [JsonPropertyName("fancyzones_allowChildWindowSnap")] + public BoolProperty FancyzonesAllowChildWindowSnap { get; set; } + [JsonPropertyName("fancyzones_zoneHighlightColor")] public StringProperty FancyzonesZoneHighlightColor { get; set; } diff --git a/src/settings-ui/Settings.UI.Library/ViewModels/FancyZonesViewModel.cs b/src/settings-ui/Settings.UI.Library/ViewModels/FancyZonesViewModel.cs index d090a24ec5..872db2cada 100644 --- a/src/settings-ui/Settings.UI.Library/ViewModels/FancyZonesViewModel.cs +++ b/src/settings-ui/Settings.UI.Library/ViewModels/FancyZonesViewModel.cs @@ -84,6 +84,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels _showOnAllMonitors = Settings.Properties.FancyzonesShowOnAllMonitors.Value; _spanZonesAcrossMonitors = Settings.Properties.FancyzonesSpanZonesAcrossMonitors.Value; _makeDraggedWindowTransparent = Settings.Properties.FancyzonesMakeDraggedWindowTransparent.Value; + _allowPopupWindowSnap = Settings.Properties.FancyzonesAllowPopupWindowSnap.Value; + _allowChildWindowSnap = Settings.Properties.FancyzonesAllowChildWindowSnap.Value; _highlightOpacity = Settings.Properties.FancyzonesHighlightOpacity.Value; _excludedApps = Settings.Properties.FancyzonesExcludedApps.Value; _systemTheme = Settings.Properties.FancyzonesSystemTheme.Value; @@ -131,6 +133,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels private bool _makeDraggedWindowTransparent; private bool _systemTheme; private bool _showZoneNumber; + private bool _allowPopupWindowSnap; + private bool _allowChildWindowSnap; private int _highlightOpacity; private string _excludedApps; @@ -562,6 +566,42 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels } } + public bool AllowPopupWindowSnap + { + get + { + return _allowPopupWindowSnap; + } + + set + { + if (value != _allowPopupWindowSnap) + { + _allowPopupWindowSnap = value; + Settings.Properties.FancyzonesAllowPopupWindowSnap.Value = value; + NotifyPropertyChanged(); + } + } + } + + public bool AllowChildWindowSnap + { + get + { + return _allowChildWindowSnap; + } + + set + { + if (value != _allowChildWindowSnap) + { + _allowChildWindowSnap = value; + Settings.Properties.FancyzonesAllowChildWindowSnap.Value = value; + NotifyPropertyChanged(); + } + } + } + // For the following setters we use OrdinalIgnoreCase string comparison since // we expect value to be a hex code. public string ZoneHighlightColor diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index bfe4ac558d..83c70fe8ef 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -2050,4 +2050,13 @@ From there, simply click on one of the supported files in the File Explorer and Attribution giving credit to the projects this utility was based on + + This setting can affect all popup windows including notifications + + + Allow popup windows snapping + + + Allow child windows snapping + diff --git a/src/settings-ui/Settings.UI/Views/FancyZonesPage.xaml b/src/settings-ui/Settings.UI/Views/FancyZonesPage.xaml index 3394dafc6e..7afc58a2ac 100644 --- a/src/settings-ui/Settings.UI/Views/FancyZonesPage.xaml +++ b/src/settings-ui/Settings.UI/Views/FancyZonesPage.xaml @@ -183,6 +183,12 @@ + + + +