2021-06-23 15:48:54 +03:00
|
|
|
#include "pch.h"
|
2021-07-07 13:18:52 +03:00
|
|
|
#include "WorkArea.h"
|
2021-06-23 15:48:54 +03:00
|
|
|
|
2021-11-08 13:02:56 +01:00
|
|
|
#include <common/logger/call_tracer.h>
|
2021-06-23 15:48:54 +03:00
|
|
|
#include <common/logger/logger.h>
|
2022-06-14 16:45:45 +02:00
|
|
|
#include <common/utils/winapi_error.h>
|
2021-06-23 15:48:54 +03:00
|
|
|
|
2022-01-27 15:21:15 +03:00
|
|
|
#include "FancyZonesData/AppliedLayouts.h"
|
2022-01-24 14:54:17 +03:00
|
|
|
#include "FancyZonesData/AppZoneHistory.h"
|
2021-06-23 15:48:54 +03:00
|
|
|
#include "FancyZonesDataTypes.h"
|
2022-02-23 17:25:28 +03:00
|
|
|
#include "SettingsObserver.h"
|
2021-11-25 17:39:03 +02:00
|
|
|
#include "ZonesOverlay.h"
|
2021-06-23 15:48:54 +03:00
|
|
|
#include "trace.h"
|
|
|
|
|
#include "on_thread_executor.h"
|
|
|
|
|
#include "Settings.h"
|
2022-06-14 16:45:45 +02:00
|
|
|
#include <FancyZonesLib/FancyZonesWindowProperties.h>
|
|
|
|
|
#include <FancyZonesLib/VirtualDesktop.h>
|
2022-02-23 17:25:28 +03:00
|
|
|
#include <FancyZonesLib/WindowUtils.h>
|
2021-06-23 15:48:54 +03:00
|
|
|
|
|
|
|
|
#include <ShellScalingApi.h>
|
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <fileapi.h>
|
|
|
|
|
|
2022-11-29 14:10:48 +00:00
|
|
|
// disabling warning 4458 - declaration of 'identifier' hides class member
|
|
|
|
|
// to avoid warnings from GDI files - can't add winRT directory to external code
|
|
|
|
|
// in the Cpp.Build.props
|
|
|
|
|
#pragma warning(push)
|
|
|
|
|
#pragma warning(disable : 4458)
|
2021-06-23 15:48:54 +03:00
|
|
|
#include <gdiplus.h>
|
2022-11-29 14:10:48 +00:00
|
|
|
#pragma warning(pop)
|
2021-06-23 15:48:54 +03:00
|
|
|
|
|
|
|
|
// Non-Localizable strings
|
|
|
|
|
namespace NonLocalizable
|
|
|
|
|
{
|
2021-11-25 17:39:03 +02:00
|
|
|
const wchar_t ToolWindowClassName[] = L"FancyZones_ZonesOverlay";
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using namespace FancyZonesUtils;
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
// The reason for using this class is the need to call ShowWindow(window, SW_SHOWNORMAL); on each
|
|
|
|
|
// newly created window for it to be displayed properly. The call sometimes has side effects when
|
|
|
|
|
// a fullscreen app is running, and happens when the resolution change event is triggered
|
|
|
|
|
// (e.g. when running some games).
|
|
|
|
|
// This class will serve as a pool of windows for which this call was already done.
|
|
|
|
|
class WindowPool
|
|
|
|
|
{
|
|
|
|
|
std::vector<HWND> m_pool;
|
|
|
|
|
std::mutex m_mutex;
|
|
|
|
|
|
|
|
|
|
HWND ExtractWindow()
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(m_mutex);
|
|
|
|
|
|
|
|
|
|
if (m_pool.empty())
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HWND window = m_pool.back();
|
|
|
|
|
m_pool.pop_back();
|
|
|
|
|
return window;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2021-11-25 17:39:03 +02:00
|
|
|
HWND NewZonesOverlayWindow(Rect position, HINSTANCE hinstance, WorkArea* owner)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
|
|
|
|
HWND windowFromPool = ExtractWindow();
|
|
|
|
|
if (windowFromPool == NULL)
|
|
|
|
|
{
|
|
|
|
|
HWND window = CreateWindowExW(WS_EX_TOOLWINDOW, NonLocalizable::ToolWindowClassName, L"", WS_POPUP, position.left(), position.top(), position.width(), position.height(), nullptr, nullptr, hinstance, owner);
|
2021-11-25 17:39:03 +02:00
|
|
|
Logger::info("Creating new ZonesOverlay window, hWnd = {}", (void*)window);
|
2022-02-23 17:25:28 +03:00
|
|
|
FancyZonesWindowUtils::MakeWindowTransparent(window);
|
2021-06-23 15:48:54 +03:00
|
|
|
|
|
|
|
|
// According to ShowWindow docs, we must call it with SW_SHOWNORMAL the first time
|
|
|
|
|
ShowWindow(window, SW_SHOWNORMAL);
|
|
|
|
|
ShowWindow(window, SW_HIDE);
|
|
|
|
|
return window;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-11-25 17:39:03 +02:00
|
|
|
Logger::info("Reusing ZonesOverlay window from pool, hWnd = {}", (void*)windowFromPool);
|
2021-06-23 15:48:54 +03:00
|
|
|
SetWindowLongPtrW(windowFromPool, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(owner));
|
|
|
|
|
MoveWindow(windowFromPool, position.left(), position.top(), position.width(), position.height(), TRUE);
|
|
|
|
|
return windowFromPool;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-25 17:39:03 +02:00
|
|
|
void FreeZonesOverlayWindow(HWND window)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2021-11-25 17:39:03 +02:00
|
|
|
Logger::info("Freeing ZonesOverlay window into pool, hWnd = {}", (void*)window);
|
2021-06-23 15:48:54 +03:00
|
|
|
SetWindowLongPtrW(window, GWLP_USERDATA, 0);
|
|
|
|
|
ShowWindow(window, SW_HIDE);
|
|
|
|
|
|
|
|
|
|
std::unique_lock lock(m_mutex);
|
|
|
|
|
m_pool.push_back(window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~WindowPool()
|
|
|
|
|
{
|
|
|
|
|
for (HWND window : m_pool)
|
|
|
|
|
{
|
|
|
|
|
DestroyWindow(window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WindowPool windowPool;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-23 20:13:05 +03:00
|
|
|
WorkArea::WorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesUtils::Rect& workAreaRect) :
|
|
|
|
|
m_uniqueId(uniqueId),
|
|
|
|
|
m_workAreaRect(workAreaRect)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
|
|
|
|
WNDCLASSEXW wcex{};
|
|
|
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
|
|
|
wcex.lpfnWndProc = s_WndProc;
|
|
|
|
|
wcex.hInstance = hinstance;
|
|
|
|
|
wcex.lpszClassName = NonLocalizable::ToolWindowClassName;
|
|
|
|
|
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
|
|
|
|
RegisterClassExW(&wcex);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-07 13:18:52 +03:00
|
|
|
WorkArea::~WorkArea()
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2021-11-25 17:39:03 +02:00
|
|
|
windowPool.FreeZonesOverlayWindow(m_window);
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
void WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
|
|
|
|
MoveWindowIntoZoneByIndexSet(window, { index });
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition /* = true*/)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty() || indexSet.empty())
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
return;
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
2022-10-31 13:44:25 +02:00
|
|
|
|
|
|
|
|
FancyZonesWindowUtils::SaveWindowSizeAndOrigin(window);
|
|
|
|
|
|
2022-11-22 17:58:22 +01:00
|
|
|
if (updatePosition)
|
|
|
|
|
{
|
2023-01-31 12:55:46 +03:00
|
|
|
const auto rect = m_layout->GetCombinedZonesRect(indexSet);
|
2023-02-06 17:15:19 +01:00
|
|
|
if (rect.bottom - rect.top > 0 && rect.right - rect.left > 0)
|
|
|
|
|
{
|
|
|
|
|
const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
|
|
|
|
|
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
|
|
|
|
|
}
|
2022-11-22 17:58:22 +01:00
|
|
|
}
|
2022-11-29 14:10:48 +00:00
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
SnapWindow(window, indexSet);
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto zoneIndexes = m_layoutWindows->GetZoneIndexSetFromWindow(window);
|
2023-01-31 12:55:46 +03:00
|
|
|
const auto numZones = m_layout->Zones().size();
|
2022-10-31 13:44:25 +02:00
|
|
|
|
|
|
|
|
// The window was not assigned to any zone here
|
|
|
|
|
if (zoneIndexes.size() == 0)
|
|
|
|
|
{
|
|
|
|
|
MoveWindowIntoZoneByIndex(window, vkCode == VK_LEFT ? numZones - 1 : 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-01-31 12:55:46 +03:00
|
|
|
const ZoneIndex oldId = zoneIndexes[0];
|
2022-10-31 13:44:25 +02:00
|
|
|
|
|
|
|
|
// We reached the edge
|
2022-11-23 16:18:17 +00:00
|
|
|
if ((vkCode == VK_LEFT && oldId == 0) || (vkCode == VK_RIGHT && oldId == static_cast<int64_t>(numZones) - 1))
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
if (!cycle)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MoveWindowIntoZoneByIndex(window, vkCode == VK_LEFT ? numZones - 1 : 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// We didn't reach the edge
|
|
|
|
|
if (vkCode == VK_LEFT)
|
|
|
|
|
{
|
|
|
|
|
MoveWindowIntoZoneByIndex(window, oldId - 1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MoveWindowIntoZoneByIndex(window, oldId + 1);
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-31 13:44:25 +02:00
|
|
|
|
|
|
|
|
return true;
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto& zones = m_layout->Zones();
|
|
|
|
|
std::vector<bool> usedZoneIndices(zones.size(), false);
|
|
|
|
|
auto windowZones = m_layoutWindows->GetZoneIndexSetFromWindow(window);
|
2022-11-29 14:10:48 +00:00
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
for (const ZoneIndex id : windowZones)
|
2022-10-31 13:44:25 +02:00
|
|
|
{
|
|
|
|
|
usedZoneIndices[id] = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<RECT> zoneRects;
|
|
|
|
|
ZoneIndexSet freeZoneIndices;
|
|
|
|
|
|
|
|
|
|
for (const auto& [zoneId, zone] : zones)
|
|
|
|
|
{
|
|
|
|
|
if (!usedZoneIndices[zoneId])
|
|
|
|
|
{
|
|
|
|
|
zoneRects.emplace_back(zones.at(zoneId).GetZoneRect());
|
|
|
|
|
freeZoneIndices.emplace_back(zoneId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RECT windowRect;
|
|
|
|
|
if (!GetWindowRect(window, &windowRect))
|
|
|
|
|
{
|
|
|
|
|
Logger::error(L"GetWindowRect failed, {}", get_last_error_or_default(GetLastError()));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move to coordinates relative to windowZone
|
|
|
|
|
windowRect.top -= m_workAreaRect.top();
|
|
|
|
|
windowRect.bottom -= m_workAreaRect.top();
|
|
|
|
|
windowRect.left -= m_workAreaRect.left();
|
|
|
|
|
windowRect.right -= m_workAreaRect.left();
|
|
|
|
|
|
|
|
|
|
auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
|
|
|
|
if (result < zoneRects.size())
|
|
|
|
|
{
|
|
|
|
|
MoveWindowIntoZoneByIndex(window, freeZoneIndices[result]);
|
|
|
|
|
Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if (cycle)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
// Try again from the position off the screen in the opposite direction to vkCode
|
|
|
|
|
// Consider all zones as available
|
|
|
|
|
zoneRects.resize(zones.size());
|
|
|
|
|
std::transform(zones.begin(), zones.end(), zoneRects.begin(), [](auto zone) { return zone.second.GetZoneRect(); });
|
|
|
|
|
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, RECT(m_workAreaRect.left(), m_workAreaRect.top(), m_workAreaRect.right(), m_workAreaRect.bottom()), vkCode);
|
|
|
|
|
result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
|
|
|
|
|
|
|
|
|
if (result < zoneRects.size())
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
MoveWindowIntoZoneByIndex(window, result);
|
|
|
|
|
Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get());
|
2021-06-23 15:48:54 +03:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-31 13:44:25 +02:00
|
|
|
|
2021-06-23 15:48:54 +03:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RECT windowRect;
|
|
|
|
|
if (!GetWindowRect(window, &windowRect))
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
Logger::error(L"GetWindowRect failed, {}", get_last_error_or_default(GetLastError()));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto& zones = m_layout->Zones();
|
|
|
|
|
auto appliedZones = m_layoutWindows->GetZoneIndexSetFromWindow(window);
|
|
|
|
|
const auto& extendModeData = m_layoutWindows->ExtendWindowData();
|
|
|
|
|
|
|
|
|
|
std::vector<bool> usedZoneIndices(zones.size(), false);
|
|
|
|
|
std::vector<RECT> zoneRects;
|
|
|
|
|
ZoneIndexSet freeZoneIndices;
|
|
|
|
|
|
|
|
|
|
// If selectManyZones = true for the second time, use the last zone into which we moved
|
|
|
|
|
// instead of the window rect and enable moving to all zones except the old one
|
|
|
|
|
auto finalIndexIt = extendModeData->windowFinalIndex.find(window);
|
|
|
|
|
if (finalIndexIt != extendModeData->windowFinalIndex.end())
|
|
|
|
|
{
|
|
|
|
|
usedZoneIndices[finalIndexIt->second] = true;
|
|
|
|
|
windowRect = zones.at(finalIndexIt->second).GetZoneRect();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-01-31 12:55:46 +03:00
|
|
|
for (const ZoneIndex idx : appliedZones)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
usedZoneIndices[idx] = true;
|
|
|
|
|
}
|
|
|
|
|
// Move to coordinates relative to windowZone
|
|
|
|
|
windowRect.top -= m_workAreaRect.top();
|
|
|
|
|
windowRect.bottom -= m_workAreaRect.top();
|
|
|
|
|
windowRect.left -= m_workAreaRect.left();
|
|
|
|
|
windowRect.right -= m_workAreaRect.left();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < zones.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
if (!usedZoneIndices[i])
|
|
|
|
|
{
|
|
|
|
|
zoneRects.emplace_back(zones.at(i).GetZoneRect());
|
|
|
|
|
freeZoneIndices.emplace_back(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
const auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
2022-10-31 13:44:25 +02:00
|
|
|
if (result < zoneRects.size())
|
|
|
|
|
{
|
|
|
|
|
ZoneIndex targetZone = freeZoneIndices[result];
|
|
|
|
|
ZoneIndexSet resultIndexSet;
|
|
|
|
|
|
|
|
|
|
// First time with selectManyZones = true for this window?
|
|
|
|
|
if (finalIndexIt == extendModeData->windowFinalIndex.end())
|
|
|
|
|
{
|
|
|
|
|
// Already zoned?
|
|
|
|
|
if (appliedZones.size())
|
|
|
|
|
{
|
|
|
|
|
extendModeData->windowInitialIndexSet[window] = appliedZones;
|
|
|
|
|
extendModeData->windowFinalIndex[window] = targetZone;
|
|
|
|
|
resultIndexSet = m_layout->GetCombinedZoneRange(appliedZones, { targetZone });
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
extendModeData->windowInitialIndexSet[window] = { targetZone };
|
|
|
|
|
extendModeData->windowFinalIndex[window] = targetZone;
|
|
|
|
|
resultIndexSet = { targetZone };
|
|
|
|
|
}
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
2022-10-31 13:44:25 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
auto deletethis = extendModeData->windowInitialIndexSet[window];
|
|
|
|
|
extendModeData->windowFinalIndex[window] = targetZone;
|
|
|
|
|
resultIndexSet = m_layout->GetCombinedZoneRange(extendModeData->windowInitialIndexSet[window], { targetZone });
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
const auto rect = m_layout->GetCombinedZonesRect(resultIndexSet);
|
|
|
|
|
const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
|
2022-10-31 13:44:25 +02:00
|
|
|
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
|
|
|
|
|
|
2023-02-06 17:15:19 +01:00
|
|
|
SnapWindow(window, resultIndexSet, true);
|
2022-10-31 13:44:25 +02:00
|
|
|
|
|
|
|
|
return true;
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
2022-10-31 13:44:25 +02:00
|
|
|
|
2021-06-23 15:48:54 +03:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-06 17:15:19 +01:00
|
|
|
void WorkArea::SnapWindow(HWND window, const ZoneIndexSet& zones, bool extend)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2023-01-31 12:55:46 +03:00
|
|
|
if (!m_layoutWindows || !m_layout)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2023-01-31 12:55:46 +03:00
|
|
|
return;
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
2023-01-31 12:55:46 +03:00
|
|
|
|
2023-02-06 17:15:19 +01:00
|
|
|
if (extend)
|
|
|
|
|
{
|
|
|
|
|
m_layoutWindows->Extend(window, zones);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_layoutWindows->Assign(window, zones);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
|
|
|
|
|
if (guidStr.has_value())
|
|
|
|
|
{
|
|
|
|
|
AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, guidStr.value(), zones);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FancyZonesWindowProperties::StampZoneIndexProperty(window, zones);
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
void WorkArea::UnsnapWindow(HWND window)
|
2022-12-02 13:29:11 +03:00
|
|
|
{
|
2023-01-31 12:55:46 +03:00
|
|
|
if (!m_layoutWindows || !m_layout)
|
2022-12-02 13:29:11 +03:00
|
|
|
{
|
2023-01-31 12:55:46 +03:00
|
|
|
return;
|
2022-12-02 13:29:11 +03:00
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
m_layoutWindows->Dismiss(window);
|
|
|
|
|
|
|
|
|
|
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
|
|
|
|
|
if (guidStr.has_value())
|
2022-12-02 13:29:11 +03:00
|
|
|
{
|
2023-01-31 12:55:46 +03:00
|
|
|
AppZoneHistory::instance().RemoveAppLastZone(window, m_uniqueId, guidStr.value());
|
2022-12-02 13:29:11 +03:00
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
FancyZonesWindowProperties::RemoveZoneIndexProperty(window);
|
2022-12-02 13:29:11 +03:00
|
|
|
}
|
|
|
|
|
|
2023-02-27 21:15:52 +01:00
|
|
|
const GUID WorkArea::GetLayoutId() const noexcept
|
|
|
|
|
{
|
|
|
|
|
if (m_layout)
|
|
|
|
|
{
|
|
|
|
|
return m_layout->Id();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GUID{};
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const
|
2021-07-07 13:18:52 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
if (m_layout)
|
2021-07-07 13:18:52 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
|
|
|
|
|
if (guidStr.has_value())
|
2021-07-07 13:18:52 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
return AppZoneHistory::instance().GetAppLastZoneIndexSet(window, m_uniqueId, guidStr.value());
|
2021-07-07 13:18:52 +03:00
|
|
|
}
|
2022-05-13 15:06:11 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Logger::error(L"Failed to convert to string layout GUID on the requested work area");
|
|
|
|
|
}
|
2021-07-07 13:18:52 +03:00
|
|
|
}
|
2022-05-13 15:06:11 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Logger::error(L"No layout initialized on the requested work area");
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-07 13:18:52 +03:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-30 19:39:46 +03:00
|
|
|
void WorkArea::ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow/* = nullptr*/)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2023-01-30 19:39:46 +03:00
|
|
|
if (m_layout && m_zonesOverlay)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2023-01-30 19:39:46 +03:00
|
|
|
SetWorkAreaWindowAsTopmost(draggedWindow);
|
|
|
|
|
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), highlight, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
2021-11-25 17:39:03 +02:00
|
|
|
m_zonesOverlay->Show();
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-30 19:39:46 +03:00
|
|
|
void WorkArea::HideZonesOverlay()
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2023-01-30 19:39:46 +03:00
|
|
|
if (m_zonesOverlay)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2021-11-25 17:39:03 +02:00
|
|
|
m_zonesOverlay->Hide();
|
2023-01-30 19:39:46 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WorkArea::FlashZones()
|
|
|
|
|
{
|
|
|
|
|
if (m_layout && m_zonesOverlay)
|
|
|
|
|
{
|
|
|
|
|
SetWorkAreaWindowAsTopmost(nullptr);
|
|
|
|
|
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
|
|
|
|
m_zonesOverlay->Flash();
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
void WorkArea::UpdateActiveZoneSet()
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2023-01-31 12:55:46 +03:00
|
|
|
const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId);
|
2022-04-20 18:23:49 +03:00
|
|
|
if (!isLayoutAlreadyApplied)
|
|
|
|
|
{
|
|
|
|
|
AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 13:44:25 +02:00
|
|
|
CalculateZoneSet();
|
|
|
|
|
if (m_window && m_layout)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2023-01-23 20:13:05 +03:00
|
|
|
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-06 17:15:19 +01:00
|
|
|
void WorkArea::UpdateWindowPositions()
|
|
|
|
|
{
|
|
|
|
|
if (!m_layoutWindows)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto& snappedWindows = m_layoutWindows->SnappedWindows();
|
|
|
|
|
for (const auto& [window, zones] : snappedWindows)
|
|
|
|
|
{
|
|
|
|
|
MoveWindowIntoZoneByIndexSet(window, zones, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
void WorkArea::CycleWindows(HWND window, bool reverse)
|
2021-11-03 17:11:42 +02:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
if (m_layoutWindows)
|
2021-11-03 17:11:42 +02:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
m_layoutWindows->CycleWindows(window, reverse);
|
2021-11-03 17:11:42 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-23 15:48:54 +03:00
|
|
|
#pragma region private
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
bool WorkArea::InitWindow(HINSTANCE hinstance)
|
2022-08-15 16:40:10 +03:00
|
|
|
{
|
|
|
|
|
m_window = windowPool.NewZonesOverlayWindow(m_workAreaRect, hinstance, this);
|
|
|
|
|
if (!m_window)
|
|
|
|
|
{
|
|
|
|
|
Logger::error(L"No work area window");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_zonesOverlay = std::make_unique<ZonesOverlay>(m_window);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2023-01-31 12:55:46 +03:00
|
|
|
Logger::info(L"Initialize layout on {}, work area rect = {}x{}", m_uniqueId.toString(), m_workAreaRect.width(), m_workAreaRect.height());
|
2022-11-29 14:10:48 +00:00
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId);
|
2022-01-27 15:21:15 +03:00
|
|
|
if (!isLayoutAlreadyApplied)
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-01-27 15:21:15 +03:00
|
|
|
if (parentUniqueId.virtualDesktopId != GUID_NULL)
|
|
|
|
|
{
|
|
|
|
|
AppliedLayouts::instance().CloneLayout(parentUniqueId, m_uniqueId);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId);
|
|
|
|
|
}
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
2022-11-29 14:10:48 +00:00
|
|
|
|
2022-10-31 13:44:25 +02:00
|
|
|
CalculateZoneSet();
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
void WorkArea::CalculateZoneSet()
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-01-27 15:21:15 +03:00
|
|
|
const auto appliedLayout = AppliedLayouts::instance().GetDeviceLayout(m_uniqueId);
|
|
|
|
|
if (!appliedLayout.has_value())
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2022-10-31 13:44:25 +02:00
|
|
|
Logger::error(L"Layout wasn't applied. Can't init layout on work area {}x{}", m_workAreaRect.width(), m_workAreaRect.height());
|
2021-06-23 15:48:54 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 13:44:25 +02:00
|
|
|
m_layout = std::make_unique<Layout>(appliedLayout.value());
|
2023-01-23 20:13:05 +03:00
|
|
|
m_layout->Init(m_workAreaRect, m_uniqueId.monitorId.monitor);
|
2021-06-23 15:48:54 +03:00
|
|
|
|
2022-10-31 13:44:25 +02:00
|
|
|
if (!m_layoutWindows)
|
|
|
|
|
{
|
|
|
|
|
m_layoutWindows = std::make_unique<LayoutAssignedWindows>();
|
|
|
|
|
}
|
2021-06-23 15:48:54 +03:00
|
|
|
}
|
|
|
|
|
|
2021-07-07 13:18:52 +03:00
|
|
|
LRESULT WorkArea::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
|
|
|
|
switch (message)
|
|
|
|
|
{
|
|
|
|
|
case WM_NCDESTROY:
|
|
|
|
|
{
|
|
|
|
|
::DefWindowProc(m_window, message, wparam, lparam);
|
|
|
|
|
SetWindowLongPtr(m_window, GWLP_USERDATA, 0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WM_ERASEBKGND:
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
return DefWindowProc(m_window, message, wparam, lparam);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-30 19:39:46 +03:00
|
|
|
void WorkArea::SetWorkAreaWindowAsTopmost(HWND draggedWindow) noexcept
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
|
|
|
|
if (!m_window)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-30 19:39:46 +03:00
|
|
|
HWND windowInsertAfter = draggedWindow ? draggedWindow : HWND_TOPMOST;
|
2021-06-23 15:48:54 +03:00
|
|
|
|
2023-01-31 12:55:46 +03:00
|
|
|
constexpr UINT flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE;
|
2021-06-23 15:48:54 +03:00
|
|
|
SetWindowPos(m_window, windowInsertAfter, 0, 0, 0, 0, flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
|
2021-07-07 13:18:52 +03:00
|
|
|
LRESULT CALLBACK WorkArea::s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
2021-06-23 15:48:54 +03:00
|
|
|
{
|
2021-07-07 13:18:52 +03:00
|
|
|
auto thisRef = reinterpret_cast<WorkArea*>(GetWindowLongPtr(window, GWLP_USERDATA));
|
2021-06-23 15:48:54 +03:00
|
|
|
if ((thisRef == nullptr) && (message == WM_CREATE))
|
|
|
|
|
{
|
|
|
|
|
auto createStruct = reinterpret_cast<LPCREATESTRUCT>(lparam);
|
2023-01-16 08:18:55 -03:00
|
|
|
thisRef = static_cast<WorkArea*>(createStruct->lpCreateParams);
|
2021-06-23 15:48:54 +03:00
|
|
|
SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(thisRef));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (thisRef != nullptr) ? thisRef->WndProc(message, wparam, lparam) :
|
|
|
|
|
DefWindowProc(window, message, wparam, lparam);
|
2023-01-26 20:02:46 +03:00
|
|
|
}
|