diff --git a/src/modules/fancyzones/lib/FancyZones.cpp b/src/modules/fancyzones/lib/FancyZones.cpp index 468cfb9b28..d418d7a754 100644 --- a/src/modules/fancyzones/lib/FancyZones.cpp +++ b/src/modules/fancyzones/lib/FancyZones.cpp @@ -7,18 +7,15 @@ #include "lib/ZoneWindow.h" #include "lib/JsonHelpers.h" #include "lib/ZoneSet.h" +#include "lib/WindowMoveHandler.h" #include "trace.h" #include "VirtualDesktopUtils.h" #include -#include #include -#include #include #include -#include - enum class DisplayChangeType { WorkArea, @@ -28,8 +25,6 @@ enum class DisplayChangeType Initialization }; -extern "C" IMAGE_DOS_HEADER __ImageBase; - namespace std { template<> @@ -48,7 +43,8 @@ struct FancyZones : public winrt::implements& settings) noexcept : m_hinstance(hinstance), - m_settings(settings) + m_settings(settings), + m_windowMoveHandler(settings) { m_settings->SetCallback(this); } @@ -64,14 +60,26 @@ public: InMoveSize() noexcept { std::shared_lock readLock(m_lock); - return m_inMoveSize; + return m_windowMoveHandler.InMoveSize(); } IFACEMETHODIMP_(void) - MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept; + MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept + { + std::unique_lock writeLock(m_lock); + m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_zoneWindowMap); + } IFACEMETHODIMP_(void) - MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept; + MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept + { + std::unique_lock writeLock(m_lock); + m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_zoneWindowMap); + } IFACEMETHODIMP_(void) - MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept; + MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept + { + std::unique_lock writeLock(m_lock); + m_windowMoveHandler.MoveSizeEnd(window, ptScreen, m_zoneWindowMap); + } IFACEMETHODIMP_(void) VirtualDesktopChanged() noexcept; IFACEMETHODIMP_(void) @@ -149,9 +157,6 @@ public: void OnDisplayChange(DisplayChangeType changeType) noexcept; void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept; - void MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept; - void MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector& indexSet) noexcept; - protected: static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; @@ -180,17 +185,11 @@ private: } }; - bool IsInterestingWindow(HWND window) noexcept; - bool IsCursorTypeIndicatingSizeEvent(); void UpdateZoneWindows() noexcept; void MoveWindowsOnDisplayChange() noexcept; - void UpdateDragState(HWND window, require_write_lock) noexcept; void CycleActiveZoneSet(DWORD vkCode) noexcept; bool OnSnapHotkey(DWORD vkCode) noexcept; - void MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT const& ptScreen, require_write_lock) noexcept; - void MoveSizeEndInternal(HWND window, POINT const& ptScreen, require_write_lock) noexcept; - void MoveSizeUpdateInternal(HMONITOR monitor, POINT const& ptScreen, require_write_lock) noexcept; - + void HandleVirtualDesktopUpdates(HANDLE fancyZonesDestroyedEvent) noexcept; void RegisterVirtualDesktopUpdates(std::unordered_set& currentVirtualDesktopIds) noexcept; void RegisterNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept; @@ -200,7 +199,6 @@ private: std::vector> GetRawMonitorData() noexcept; std::vector GetMonitorsSorted() noexcept; - bool MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle); const HINSTANCE m_hinstance{}; @@ -208,11 +206,9 @@ private: mutable std::shared_mutex m_lock; HWND m_window{}; - HWND m_windowMoveSize{}; // The window that is being moved/sized - bool m_inMoveSize{}; // Whether or not a move/size operation is currently active - bool m_dragEnabled{}; // True if we should be showing zone hints while dragging + WindowMoveHandler m_windowMoveHandler; + std::map> m_zoneWindowMap; // Map of monitor to ZoneWindow (one per monitor) - winrt::com_ptr m_zoneWindowMoveSize; // "Active" ZoneWindow, where the move/size is happening. Will update as drag moves between monitors. winrt::com_ptr m_settings{}; GUID m_currentVirtualDesktopId{}; // UUID of the current virtual desktop. Is GUID_NULL until first VD switch per session. std::unordered_map> m_processedWorkAreas; // Work area is defined by monitor and virtual desktop id. @@ -298,36 +294,6 @@ FancyZones::Destroy() noexcept } } -// IFancyZonesCallback -IFACEMETHODIMP_(void) -FancyZones::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept -{ - if (IsInterestingWindow(window)) - { - std::unique_lock writeLock(m_lock); - MoveSizeStartInternal(window, monitor, ptScreen, writeLock); - } -} - -// IFancyZonesCallback -IFACEMETHODIMP_(void) -FancyZones::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept -{ - std::unique_lock writeLock(m_lock); - MoveSizeUpdateInternal(monitor, ptScreen, writeLock); -} - -// IFancyZonesCallback -IFACEMETHODIMP_(void) -FancyZones::MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept -{ - if (window == m_windowMoveSize || IsInterestingWindow(window)) - { - std::unique_lock writeLock(m_lock); - MoveSizeEndInternal(window, ptScreen, writeLock); - } -} - // IFancyZonesCallback IFACEMETHODIMP_(void) FancyZones::VirtualDesktopChanged() noexcept @@ -350,7 +316,7 @@ IFACEMETHODIMP_(void) FancyZones::WindowCreated(HWND window) noexcept { std::shared_lock readLock(m_lock); - if (m_settings->GetSettings()->appLastZone_moveWindows && IsInterestingWindow(window)) + if (m_settings->GetSettings()->appLastZone_moveWindows && IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray)) { for (const auto& [monitor, zoneWindow] : m_zoneWindowMap) { @@ -375,7 +341,7 @@ FancyZones::WindowCreated(HWND window) noexcept int zoneIndex = fancyZonesData.GetAppLastZoneIndex(window, zoneWindow->UniqueId(), guidString.get()); if (zoneIndex != -1) { - MoveWindowIntoZoneByIndex(window, monitor, zoneIndex); + m_windowMoveHandler.MoveWindowIntoZoneByIndex(window, monitor, zoneIndex, m_zoneWindowMap); break; } } @@ -423,7 +389,9 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept // CycleActiveZoneSet(info->vkCode); // return false; //} - if (m_dragEnabled && shift) + + std::shared_lock readLock(m_lock); + if (m_windowMoveHandler.IsDragEnabled() && shift) { return true; } @@ -716,29 +684,6 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept } } -void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept -{ - std::shared_lock readLock(m_lock); - MoveWindowIntoZoneByIndexSet(window, monitor, { index }); -} - -void FancyZones::MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector& indexSet) noexcept -{ - std::shared_lock readLock(m_lock); - if (window != m_windowMoveSize) - { - const HMONITOR hm = (monitor != nullptr) ? monitor : MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - if (hm) - { - auto zoneWindow = m_zoneWindowMap.find(hm); - if (zoneWindow != m_zoneWindowMap.end()) - { - const auto& zoneWindowPtr = zoneWindow->second; - zoneWindowPtr->MoveWindowIntoZoneByIndexSet(window, indexSet); - } - } - } -} LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept { @@ -754,53 +699,6 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, DefWindowProc(window, message, wparam, lparam); } -bool FancyZones::IsInterestingWindow(HWND window) noexcept -{ - auto filtered = get_fancyzones_filtered_window(window); - if (!filtered.zonable) - { - return false; - } - // Filter out user specified apps - CharUpperBuffW(filtered.process_path.data(), (DWORD)filtered.process_path.length()); - if (m_settings) - { - const auto& excludedAppsArray = m_settings->GetSettings()->excludedAppsArray; - if (find_app_name_in_path(filtered.process_path, excludedAppsArray)) - { - return false; - } - } - return true; -} - -bool FancyZones::IsCursorTypeIndicatingSizeEvent() -{ - CURSORINFO cursorInfo = { 0 }; - cursorInfo.cbSize = sizeof(cursorInfo); - - if (::GetCursorInfo(&cursorInfo)) - { - if (::LoadCursor(NULL, IDC_SIZENS) == cursorInfo.hCursor) - { - return true; - } - if (::LoadCursor(NULL, IDC_SIZEWE) == cursorInfo.hCursor) - { - return true; - } - if (::LoadCursor(NULL, IDC_SIZENESW) == cursorInfo.hCursor) - { - return true; - } - if (::LoadCursor(NULL, IDC_SIZENWSE) == cursorInfo.hCursor) - { - return true; - } - } - return false; -} - void FancyZones::UpdateZoneWindows() noexcept { auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL { @@ -851,63 +749,18 @@ void FancyZones::MoveWindowsOnDisplayChange() noexcept { // i is off by 1 since 0 is special. auto strongThis = reinterpret_cast(data); - strongThis->MoveWindowIntoZoneByIndex(window, nullptr, i - 1); + std::unique_lock writeLock(strongThis->m_lock); + strongThis->m_windowMoveHandler.MoveWindowIntoZoneByIndex(window, nullptr, i - 1, strongThis->m_zoneWindowMap); } return TRUE; }; EnumWindows(callback, reinterpret_cast(this)); } -void FancyZones::UpdateDragState(HWND window, require_write_lock) noexcept -{ - const bool shift = GetAsyncKeyState(VK_SHIFT) & 0x8000; - const bool mouseL = GetAsyncKeyState(VK_LBUTTON) & 0x8000; - const bool mouseR = GetAsyncKeyState(VK_RBUTTON) & 0x8000; - const bool mouseM = GetAsyncKeyState(VK_MBUTTON) & 0x8000; - const bool mouseX1 = GetAsyncKeyState(VK_XBUTTON1) & 0x8000; - const bool mouseX2 = GetAsyncKeyState(VK_XBUTTON2) & 0x8000; - - // Note, Middle, X1 and X2 can also be used in addition to R/L - bool mouse = mouseM | mouseX1 | mouseX2; - // If the user has swapped their Right and Left Buttons, use the "Right" equivalent - if (GetSystemMetrics(SM_SWAPBUTTON)) - { - mouse |= mouseL; - } - else - { - mouse |= mouseR; - } - - if (m_settings->GetSettings()->shiftDrag) - { - m_dragEnabled = (shift | mouse); - } - else - { - m_dragEnabled = !(shift | mouse); - } - - static bool warning_shown = false; - if (!is_process_elevated() && IsProcessOfWindowElevated(window)) - { - m_dragEnabled = false; - if (!warning_shown && !is_cant_drag_elevated_warning_disabled()) - { - std::vector actions = { - notifications::link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_LEARN_MORE), L"https://aka.ms/powertoysDetectedElevatedHelp" }, - notifications::link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_DIALOG_DONT_SHOW_AGAIN), L"powertoys://cant_drag_elevated_disable/" } - }; - notifications::show_toast_with_activations(GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED), {}, std::move(actions)); - warning_shown = true; - } - } -} - void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept { auto window = GetForegroundWindow(); - if (IsInterestingWindow(window)) + if (IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray)) { const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); if (monitor) @@ -927,7 +780,7 @@ void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept { auto window = GetForegroundWindow(); - if (IsInterestingWindow(window)) + if (IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray)) { const HMONITOR current = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); if (current) @@ -939,7 +792,8 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept auto currMonitorInfo = std::find(std::begin(monitorInfo), std::end(monitorInfo), current); do { - if (MoveWindowIntoZoneByDirection(*currMonitorInfo, window, vkCode, false /* cycle through zones */)) + std::unique_lock writeLock(m_lock); + if (m_windowMoveHandler.MoveWindowIntoZoneByDirection(*currMonitorInfo, window, vkCode, false /* cycle through zones */, m_zoneWindowMap)) { return true; } @@ -965,167 +819,14 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept else { // Single monitor environment. - return MoveWindowIntoZoneByDirection(current, window, vkCode, true /* cycle through zones */); + std::unique_lock writeLock(m_lock); + return m_windowMoveHandler.MoveWindowIntoZoneByDirection(current, window, vkCode, true /* cycle through zones */, m_zoneWindowMap); } } } return false; } -void FancyZones::MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT const& ptScreen, require_write_lock writeLock) noexcept -{ - if (IsCursorTypeIndicatingSizeEvent()) - { - return; - } - m_inMoveSize = true; - - auto iter = m_zoneWindowMap.find(monitor); - if (iter == end(m_zoneWindowMap)) - { - return; - } - - m_windowMoveSize = window; - - // This updates m_dragEnabled depending on if the shift key is being held down. - UpdateDragState(window, writeLock); - - if (m_dragEnabled) - { - m_zoneWindowMoveSize = iter->second; - m_zoneWindowMoveSize->MoveSizeEnter(window, m_dragEnabled); - if (m_settings->GetSettings()->showZonesOnAllMonitors) - { - for (auto [keyMonitor, zoneWindow] : m_zoneWindowMap) - { - // Skip calling ShowZoneWindow for iter->second (m_zoneWindowMoveSize) since it - // was already called in MoveSizeEnter - const bool moveSizeEnterCalled = zoneWindow == m_zoneWindowMoveSize; - if (zoneWindow && !moveSizeEnterCalled) - { - zoneWindow->ShowZoneWindow(); - } - } - } - } - else if (m_zoneWindowMoveSize) - { - m_zoneWindowMoveSize->RestoreOrginalTransparency(); - m_zoneWindowMoveSize = nullptr; - for (auto [keyMonitor, zoneWindow] : m_zoneWindowMap) - { - if (zoneWindow) - { - zoneWindow->HideZoneWindow(); - } - } - } -} - -void FancyZones::MoveSizeEndInternal(HWND window, POINT const& ptScreen, require_write_lock) noexcept -{ - m_inMoveSize = false; - m_dragEnabled = false; - m_windowMoveSize = nullptr; - if (m_zoneWindowMoveSize) - { - auto zoneWindow = std::move(m_zoneWindowMoveSize); - zoneWindow->MoveSizeEnd(window, ptScreen); - } - else - { - ::RemoveProp(window, ZONE_STAMP); - - auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - if (monitor) - { - auto zoneWindow = m_zoneWindowMap.find(monitor); - if (zoneWindow != m_zoneWindowMap.end()) - { - const auto zoneWindowPtr = zoneWindow->second; - const auto activeZoneSet = zoneWindowPtr->ActiveZoneSet(); - if (activeZoneSet) - { - wil::unique_cotaskmem_string guidString; - if (SUCCEEDED_LOG(StringFromCLSID(activeZoneSet->Id(), &guidString))) - { - JSONHelpers::FancyZonesDataInstance().RemoveAppLastZone(window, zoneWindowPtr->UniqueId(), guidString.get()); - } - } - } - } - } - - // Also, hide all windows (regardless of settings) - for (auto [keyMonitor, zoneWindow] : m_zoneWindowMap) - { - if (zoneWindow) - { - zoneWindow->HideZoneWindow(); - } - } -} - -void FancyZones::MoveSizeUpdateInternal(HMONITOR monitor, POINT const& ptScreen, require_write_lock writeLock) noexcept -{ - if (m_inMoveSize) - { - // This updates m_dragEnabled depending on if the shift key is being held down. - UpdateDragState(m_windowMoveSize, writeLock); - - if (m_zoneWindowMoveSize) - { - // Update the ZoneWindow already handling move/size - if (!m_dragEnabled) - { - // Drag got disabled, tell it to cancel and hide all windows - m_zoneWindowMoveSize = nullptr; - - for (auto [keyMonitor, zoneWindow] : m_zoneWindowMap) - { - if (zoneWindow) - { - zoneWindow->RestoreOrginalTransparency(); - zoneWindow->HideZoneWindow(); - } - } - } - else - { - auto iter = m_zoneWindowMap.find(monitor); - if (iter != m_zoneWindowMap.end()) - { - if (iter->second != m_zoneWindowMoveSize) - { - // The drag has moved to a different monitor. - m_zoneWindowMoveSize->RestoreOrginalTransparency(); - - if (!m_settings->GetSettings()->showZonesOnAllMonitors) - { - m_zoneWindowMoveSize->HideZoneWindow(); - } - m_zoneWindowMoveSize = iter->second; - m_zoneWindowMoveSize->MoveSizeEnter(m_windowMoveSize, m_zoneWindowMoveSize->IsDragEnabled()); - } - - for (auto [keyMonitor, zoneWindow] : m_zoneWindowMap) - { - zoneWindow->MoveSizeUpdate(ptScreen, m_dragEnabled); - } - } - } - } - else if (m_dragEnabled) - { - // We'll get here if the user presses/releases shift while dragging. - // Restart the drag on the ZoneWindow that m_windowMoveSize is on - MoveSizeStartInternal(m_windowMoveSize, monitor, ptScreen, writeLock); - MoveSizeUpdateInternal(monitor, ptScreen, writeLock); - } - } -} - void FancyZones::HandleVirtualDesktopUpdates(HANDLE fancyZonesDestroyedEvent) noexcept { HANDLE regKeyEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); @@ -1245,17 +946,6 @@ std::vector> FancyZones::GetRawMonitorData() noexcept return monitorInfo; } -bool FancyZones::MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle) -{ - auto iter = m_zoneWindowMap.find(monitor); - if (iter != std::end(m_zoneWindowMap)) - { - const auto& zoneWindowPtr = iter->second; - return zoneWindowPtr->MoveWindowIntoZoneByDirection(window, vkCode, cycle); - } - return false; -} - winrt::com_ptr MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr& settings) noexcept { if (!settings) diff --git a/src/modules/fancyzones/lib/FancyZonesLib.vcxproj b/src/modules/fancyzones/lib/FancyZonesLib.vcxproj index 26a9bc454f..c4eaecd69a 100644 --- a/src/modules/fancyzones/lib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/lib/FancyZonesLib.vcxproj @@ -102,6 +102,7 @@ + @@ -117,6 +118,7 @@ + diff --git a/src/modules/fancyzones/lib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/lib/FancyZonesLib.vcxproj.filters index 8d758652f3..2080ff54f3 100644 --- a/src/modules/fancyzones/lib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/lib/FancyZonesLib.vcxproj.filters @@ -48,6 +48,9 @@ Header Files + + Header Files + @@ -80,6 +83,9 @@ Source Files + + Source Files + diff --git a/src/modules/fancyzones/lib/WindowMoveHandler.cpp b/src/modules/fancyzones/lib/WindowMoveHandler.cpp new file mode 100644 index 0000000000..2b7ca64aa6 --- /dev/null +++ b/src/modules/fancyzones/lib/WindowMoveHandler.cpp @@ -0,0 +1,362 @@ +#include "pch.h" +#include "WindowMoveHandler.h" + +#include +#include +#include + +#include "lib/Settings.h" +#include "lib/ZoneWindow.h" +#include "lib/util.h" + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +namespace WindowMoveHandlerUtils +{ + bool IsCursorTypeIndicatingSizeEvent() + { + CURSORINFO cursorInfo = { 0 }; + cursorInfo.cbSize = sizeof(cursorInfo); + + if (::GetCursorInfo(&cursorInfo)) + { + if (::LoadCursor(NULL, IDC_SIZENS) == cursorInfo.hCursor) + { + return true; + } + if (::LoadCursor(NULL, IDC_SIZEWE) == cursorInfo.hCursor) + { + return true; + } + if (::LoadCursor(NULL, IDC_SIZENESW) == cursorInfo.hCursor) + { + return true; + } + if (::LoadCursor(NULL, IDC_SIZENWSE) == cursorInfo.hCursor) + { + return true; + } + } + return false; + } +} + + +class WindowMoveHandlerPrivate +{ +public: + WindowMoveHandlerPrivate(const winrt::com_ptr& settings) : + m_settings(settings) + {}; + + bool IsDragEnabled() const noexcept + { + return m_dragEnabled; + } + + bool InMoveSize() const noexcept + { + return m_inMoveSize; + } + + void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept; + void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept; + void MoveSizeEnd(HWND window, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept; + + void MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector& indexSet, const std::map>& zoneWindowMap) noexcept; + bool MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle, const std::map>& zoneWindowMap); + +private: + void UpdateDragState(HWND window) noexcept; + +private: + winrt::com_ptr m_settings{}; + + HWND m_windowMoveSize{}; // The window that is being moved/sized + bool m_inMoveSize{}; // Whether or not a move/size operation is currently active + winrt::com_ptr m_zoneWindowMoveSize; // "Active" ZoneWindow, where the move/size is happening. Will update as drag moves between monitors. + bool m_dragEnabled{}; // True if we should be showing zone hints while dragging +}; + + +WindowMoveHandler::WindowMoveHandler(const winrt::com_ptr& settings) : + pimpl(new WindowMoveHandlerPrivate(settings)) +{ + +} + +WindowMoveHandler::~WindowMoveHandler() +{ + delete pimpl; +} + +bool WindowMoveHandler::InMoveSize() const noexcept +{ + return pimpl->InMoveSize(); +} + +bool WindowMoveHandler::IsDragEnabled() const noexcept +{ + return pimpl->IsDragEnabled(); +} + +void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept +{ + pimpl->MoveSizeStart(window, monitor, ptScreen, zoneWindowMap); +} + +void WindowMoveHandler::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept +{ + pimpl->MoveSizeUpdate(monitor, ptScreen, zoneWindowMap); +} + +void WindowMoveHandler::MoveSizeEnd(HWND window, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept +{ + pimpl->MoveSizeEnd(window, ptScreen, zoneWindowMap); +} + +void WindowMoveHandler::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index, const std::map>& zoneWindowMap) noexcept +{ + pimpl->MoveWindowIntoZoneByIndexSet(window, monitor, { index }, zoneWindowMap); +} + +bool WindowMoveHandler::MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle, const std::map>& zoneWindowMap) +{ + return pimpl->MoveWindowIntoZoneByDirection(monitor, window, vkCode, cycle, zoneWindowMap); +} + +void WindowMoveHandlerPrivate::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept +{ + if (!IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray) || WindowMoveHandlerUtils::IsCursorTypeIndicatingSizeEvent()) + { + return; + } + + m_inMoveSize = true; + + auto iter = zoneWindowMap.find(monitor); + if (iter == end(zoneWindowMap)) + { + return; + } + + m_windowMoveSize = window; + + // This updates m_dragEnabled depending on if the shift key is being held down. + UpdateDragState(window); + + if (m_dragEnabled) + { + m_zoneWindowMoveSize = iter->second; + m_zoneWindowMoveSize->MoveSizeEnter(window, m_dragEnabled); + if (m_settings->GetSettings()->showZonesOnAllMonitors) + { + for (auto [keyMonitor, zoneWindow] : zoneWindowMap) + { + // Skip calling ShowZoneWindow for iter->second (m_zoneWindowMoveSize) since it + // was already called in MoveSizeEnter + const bool moveSizeEnterCalled = zoneWindow == m_zoneWindowMoveSize; + if (zoneWindow && !moveSizeEnterCalled) + { + zoneWindow->ShowZoneWindow(); + } + } + } + } + else if (m_zoneWindowMoveSize) + { + m_zoneWindowMoveSize->RestoreOrginalTransparency(); + m_zoneWindowMoveSize = nullptr; + for (auto [keyMonitor, zoneWindow] : zoneWindowMap) + { + if (zoneWindow) + { + zoneWindow->HideZoneWindow(); + } + } + } +} + +void WindowMoveHandlerPrivate::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept +{ + if (!m_inMoveSize) + { + return; + } + + // This updates m_dragEnabled depending on if the shift key is being held down. + UpdateDragState(m_windowMoveSize); + + if (m_zoneWindowMoveSize) + { + // Update the ZoneWindow already handling move/size + if (!m_dragEnabled) + { + // Drag got disabled, tell it to cancel and hide all windows + m_zoneWindowMoveSize = nullptr; + + for (auto [keyMonitor, zoneWindow] : zoneWindowMap) + { + if (zoneWindow) + { + zoneWindow->RestoreOrginalTransparency(); + zoneWindow->HideZoneWindow(); + } + } + } + else + { + auto iter = zoneWindowMap.find(monitor); + if (iter != zoneWindowMap.end()) + { + if (iter->second != m_zoneWindowMoveSize) + { + // The drag has moved to a different monitor. + m_zoneWindowMoveSize->RestoreOrginalTransparency(); + + if (!m_settings->GetSettings()->showZonesOnAllMonitors) + { + m_zoneWindowMoveSize->HideZoneWindow(); + } + m_zoneWindowMoveSize = iter->second; + m_zoneWindowMoveSize->MoveSizeEnter(m_windowMoveSize, m_zoneWindowMoveSize->IsDragEnabled()); + } + + for (auto [keyMonitor, zoneWindow] : zoneWindowMap) + { + zoneWindow->MoveSizeUpdate(ptScreen, m_dragEnabled); + } + } + } + } + else if (m_dragEnabled) + { + // We'll get here if the user presses/releases shift while dragging. + // Restart the drag on the ZoneWindow that m_windowMoveSize is on + MoveSizeStart(m_windowMoveSize, monitor, ptScreen, zoneWindowMap); + MoveSizeUpdate(monitor, ptScreen, zoneWindowMap); + } +} + +void WindowMoveHandlerPrivate::MoveSizeEnd(HWND window, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept +{ + if (window != m_windowMoveSize && !IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray)) + { + return; + } + + m_inMoveSize = false; + m_dragEnabled = false; + m_windowMoveSize = nullptr; + if (m_zoneWindowMoveSize) + { + auto zoneWindow = std::move(m_zoneWindowMoveSize); + zoneWindow->MoveSizeEnd(window, ptScreen); + } + else + { + ::RemoveProp(window, ZONE_STAMP); + + auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); + if (monitor) + { + auto zoneWindow = zoneWindowMap.find(monitor); + if (zoneWindow != zoneWindowMap.end()) + { + const auto zoneWindowPtr = zoneWindow->second; + const auto activeZoneSet = zoneWindowPtr->ActiveZoneSet(); + if (activeZoneSet) + { + wil::unique_cotaskmem_string guidString; + if (SUCCEEDED_LOG(StringFromCLSID(activeZoneSet->Id(), &guidString))) + { + JSONHelpers::FancyZonesDataInstance().RemoveAppLastZone(window, zoneWindowPtr->UniqueId(), guidString.get()); + } + } + } + } + } + + // Also, hide all windows (regardless of settings) + for (auto [keyMonitor, zoneWindow] : zoneWindowMap) + { + if (zoneWindow) + { + zoneWindow->HideZoneWindow(); + } + } +} + +void WindowMoveHandlerPrivate::MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector& indexSet, const std::map>& zoneWindowMap) noexcept +{ + if (window != m_windowMoveSize) + { + const HMONITOR hm = (monitor != nullptr) ? monitor : MonitorFromWindow(window, MONITOR_DEFAULTTONULL); + if (hm) + { + auto zoneWindow = zoneWindowMap.find(hm); + if (zoneWindow != zoneWindowMap.end()) + { + const auto& zoneWindowPtr = zoneWindow->second; + zoneWindowPtr->MoveWindowIntoZoneByIndexSet(window, indexSet); + } + } + } +} + +bool WindowMoveHandlerPrivate::MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle, const std::map>& zoneWindowMap) +{ + auto iter = zoneWindowMap.find(monitor); + if (iter != std::end(zoneWindowMap)) + { + const auto& zoneWindowPtr = iter->second; + return zoneWindowPtr->MoveWindowIntoZoneByDirection(window, vkCode, cycle); + } + return false; +} + +void WindowMoveHandlerPrivate::UpdateDragState(HWND window) noexcept +{ + const bool shift = GetAsyncKeyState(VK_SHIFT) & 0x8000; + const bool mouseL = GetAsyncKeyState(VK_LBUTTON) & 0x8000; + const bool mouseR = GetAsyncKeyState(VK_RBUTTON) & 0x8000; + const bool mouseM = GetAsyncKeyState(VK_MBUTTON) & 0x8000; + const bool mouseX1 = GetAsyncKeyState(VK_XBUTTON1) & 0x8000; + const bool mouseX2 = GetAsyncKeyState(VK_XBUTTON2) & 0x8000; + + // Note, Middle, X1 and X2 can also be used in addition to R/L + bool mouse = mouseM | mouseX1 | mouseX2; + // If the user has swapped their Right and Left Buttons, use the "Right" equivalent + if (GetSystemMetrics(SM_SWAPBUTTON)) + { + mouse |= mouseL; + } + else + { + mouse |= mouseR; + } + + if (m_settings->GetSettings()->shiftDrag) + { + m_dragEnabled = (shift | mouse); + } + else + { + m_dragEnabled = !(shift | mouse); + } + + static bool warning_shown = false; + if (!is_process_elevated() && IsProcessOfWindowElevated(window)) + { + m_dragEnabled = false; + if (!warning_shown && !is_cant_drag_elevated_warning_disabled()) + { + std::vector actions = { + notifications::link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_LEARN_MORE), L"https://aka.ms/powertoysDetectedElevatedHelp" }, + notifications::link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_DIALOG_DONT_SHOW_AGAIN), L"powertoys://cant_drag_elevated_disable/" } + }; + notifications::show_toast_with_activations(GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED), {}, std::move(actions)); + warning_shown = true; + } + } +} diff --git a/src/modules/fancyzones/lib/WindowMoveHandler.h b/src/modules/fancyzones/lib/WindowMoveHandler.h new file mode 100644 index 0000000000..572990e706 --- /dev/null +++ b/src/modules/fancyzones/lib/WindowMoveHandler.h @@ -0,0 +1,24 @@ +#pragma once + +interface IFancyZonesSettings; +interface IZoneWindow; + +class WindowMoveHandler +{ +public: + WindowMoveHandler(const winrt::com_ptr& settings); + ~WindowMoveHandler(); + + bool InMoveSize() const noexcept; + bool IsDragEnabled() const noexcept; + + void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept; + void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept; + void MoveSizeEnd(HWND window, POINT const& ptScreen, const std::map>& zoneWindowMap) noexcept; + + void MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index, const std::map>& zoneWindowMap) noexcept; + bool MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle, const std::map>& zoneWindowMap); + +private: + class WindowMoveHandlerPrivate* pimpl; +}; \ No newline at end of file diff --git a/src/modules/fancyzones/lib/util.cpp b/src/modules/fancyzones/lib/util.cpp index 675b9f67e1..fa6256e900 100644 --- a/src/modules/fancyzones/lib/util.cpp +++ b/src/modules/fancyzones/lib/util.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "util.h" +#include #include typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*); @@ -135,3 +136,19 @@ void SizeWindowToRect(HWND window, RECT rect) noexcept // This fixes Issue #365 ::SetWindowPlacement(window, &placement); } + +bool IsInterestingWindow(HWND window, const std::vector& excludedApps) noexcept +{ + auto filtered = get_fancyzones_filtered_window(window); + if (!filtered.zonable) + { + return false; + } + // Filter out user specified apps + CharUpperBuffW(filtered.process_path.data(), (DWORD)filtered.process_path.length()); + if (find_app_name_in_path(filtered.process_path, excludedApps)) + { + return false; + } + return true; +} diff --git a/src/modules/fancyzones/lib/util.h b/src/modules/fancyzones/lib/util.h index be654c8555..9f465b2baf 100644 --- a/src/modules/fancyzones/lib/util.h +++ b/src/modules/fancyzones/lib/util.h @@ -119,3 +119,5 @@ inline BYTE OpacitySettingToAlpha(int opacity) UINT GetDpiForMonitor(HMONITOR monitor) noexcept; void OrderMonitors(std::vector>& monitorInfo); void SizeWindowToRect(HWND window, RECT rect) noexcept; + +bool IsInterestingWindow(HWND window, const std::vector& exludedApps) noexcept; \ No newline at end of file