FancyZones decomposition (#2485)

* FancyZones refactoring
Co-Authored-By: Andrey Nekrasov <yuyoyuppe@users.noreply.github.com>
This commit is contained in:
Seraphima Zykova 2020-04-30 13:16:08 +03:00 committed by GitHub
parent 313b1b39a7
commit 7903fe52b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 448 additions and 345 deletions

View file

@ -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 <functional>
#include <common/common.h>
#include <common/window_helpers.h>
#include <common/notifications.h>
#include <lib/util.h>
#include <unordered_set>
#include <common/notifications/fancyzones_notifications.h>
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<FancyZones, IFancyZones, IFancyZone
public:
FancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& 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<int>& 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<GUID>& currentVirtualDesktopIds) noexcept;
void RegisterNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept;
@ -200,7 +199,6 @@ private:
std::vector<std::pair<HMONITOR, RECT>> GetRawMonitorData() noexcept;
std::vector<HMONITOR> 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<HMONITOR, winrt::com_ptr<IZoneWindow>> m_zoneWindowMap; // Map of monitor to ZoneWindow (one per monitor)
winrt::com_ptr<IZoneWindow> m_zoneWindowMoveSize; // "Active" ZoneWindow, where the move/size is happening. Will update as drag moves between monitors.
winrt::com_ptr<IFancyZonesSettings> m_settings{};
GUID m_currentVirtualDesktopId{}; // UUID of the current virtual desktop. Is GUID_NULL until first VD switch per session.
std::unordered_map<GUID, std::vector<HMONITOR>> 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<int>& 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<FancyZones*>(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<LPARAM>(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<notifications::action_t> 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<std::pair<HMONITOR, RECT>> 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<IFancyZones> MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& settings) noexcept
{
if (!settings)

View file

@ -102,6 +102,7 @@
<ClInclude Include="trace.h" />
<ClInclude Include="util.h" />
<ClInclude Include="VirtualDesktopUtils.h" />
<ClInclude Include="WindowMoveHandler.h" />
<ClInclude Include="Zone.h" />
<ClInclude Include="ZoneSet.h" />
<ClInclude Include="ZoneWindow.h" />
@ -117,6 +118,7 @@
<ClCompile Include="trace.cpp" />
<ClCompile Include="util.cpp" />
<ClCompile Include="VirtualDesktopUtils.cpp" />
<ClCompile Include="WindowMoveHandler.cpp" />
<ClCompile Include="Zone.cpp" />
<ClCompile Include="ZoneSet.cpp" />
<ClCompile Include="ZoneWindow.cpp" />

View file

@ -48,6 +48,9 @@
<ClInclude Include="VirtualDesktopUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="WindowMoveHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@ -80,6 +83,9 @@
<ClCompile Include="VirtualDesktopUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WindowMoveHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="fancyzones.rc">

View file

@ -0,0 +1,362 @@
#include "pch.h"
#include "WindowMoveHandler.h"
#include <common/notifications.h>
#include <common/notifications/fancyzones_notifications.h>
#include <common/window_helpers.h>
#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<IFancyZonesSettings>& 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<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
void MoveSizeEnd(HWND window, POINT const& ptScreen, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
void MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
bool MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap);
private:
void UpdateDragState(HWND window) noexcept;
private:
winrt::com_ptr<IFancyZonesSettings> 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<IZoneWindow> 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<IFancyZonesSettings>& 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<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept
{
pimpl->MoveSizeStart(window, monitor, ptScreen, zoneWindowMap);
}
void WindowMoveHandler::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept
{
pimpl->MoveSizeUpdate(monitor, ptScreen, zoneWindowMap);
}
void WindowMoveHandler::MoveSizeEnd(HWND window, POINT const& ptScreen, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept
{
pimpl->MoveSizeEnd(window, ptScreen, zoneWindowMap);
}
void WindowMoveHandler::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept
{
pimpl->MoveWindowIntoZoneByIndexSet(window, monitor, { index }, zoneWindowMap);
}
bool WindowMoveHandler::MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap)
{
return pimpl->MoveWindowIntoZoneByDirection(monitor, window, vkCode, cycle, zoneWindowMap);
}
void WindowMoveHandlerPrivate::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& 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<HMONITOR, winrt::com_ptr<IZoneWindow>>& 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<HMONITOR, winrt::com_ptr<IZoneWindow>>& 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<int>& indexSet, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& 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<HMONITOR, winrt::com_ptr<IZoneWindow>>& 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<notifications::action_t> 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;
}
}
}

View file

@ -0,0 +1,24 @@
#pragma once
interface IFancyZonesSettings;
interface IZoneWindow;
class WindowMoveHandler
{
public:
WindowMoveHandler(const winrt::com_ptr<IFancyZonesSettings>& settings);
~WindowMoveHandler();
bool InMoveSize() const noexcept;
bool IsDragEnabled() const noexcept;
void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
void MoveSizeEnd(HWND window, POINT const& ptScreen, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
void MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
bool MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap);
private:
class WindowMoveHandlerPrivate* pimpl;
};

View file

@ -1,6 +1,7 @@
#include "pch.h"
#include "util.h"
#include <common/common.h>
#include <common/dpi_aware.h>
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<std::wstring>& 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;
}

View file

@ -119,3 +119,5 @@ inline BYTE OpacitySettingToAlpha(int opacity)
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
void SizeWindowToRect(HWND window, RECT rect) noexcept;
bool IsInterestingWindow(HWND window, const std::vector<std::wstring>& exludedApps) noexcept;