mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 18:57:19 +02:00
[FancyZones] Window switch shortcut fix (#21426)
* rename Layout -> LayoutData * simplify zone * split ZoneSet: Layout * refactoring * split ZoneSet: LayoutWindows * update trace * split ZoneSet: remove ZoneSet * fix initialization * split unit tests * remove unused * warning * nullptr check * use current rect * update work area tests * use current rect * simplify * more meaningful name * dismiss * safety checks * resolve conflicts * reassign windows after switching vd * avoid double-processing for window on switching vd * extend windows fix * check if window is on current desktop before cycling * separated extend * not reinit layout windows
This commit is contained in:
@@ -25,7 +25,6 @@
|
||||
#include <FancyZonesLib/MonitorUtils.h>
|
||||
#include <FancyZonesLib/Settings.h>
|
||||
#include <FancyZonesLib/SettingsObserver.h>
|
||||
#include <FancyZonesLib/ZoneSet.h>
|
||||
#include <FancyZonesLib/WorkArea.h>
|
||||
#include <FancyZonesLib/WindowMoveHandler.h>
|
||||
#include <FancyZonesLib/WindowUtils.h>
|
||||
@@ -102,9 +101,9 @@ public:
|
||||
m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()));
|
||||
}
|
||||
|
||||
void MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
|
||||
void MoveSizeEnd(HWND window) noexcept
|
||||
{
|
||||
m_windowMoveHandler.MoveSizeEnd(window, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()));
|
||||
m_windowMoveHandler.MoveSizeEnd(window, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()));
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
@@ -155,7 +154,7 @@ protected:
|
||||
|
||||
private:
|
||||
void UpdateWorkAreas() noexcept;
|
||||
void CycleTabs(bool reverse) noexcept;
|
||||
void CycleWindows(bool reverse) noexcept;
|
||||
bool OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept;
|
||||
bool OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept;
|
||||
bool OnSnapHotkey(DWORD vkCode) noexcept;
|
||||
@@ -342,7 +341,7 @@ void FancyZones::MoveWindowIntoZone(HWND window, std::shared_ptr<WorkArea> workA
|
||||
{
|
||||
if (workArea)
|
||||
{
|
||||
Trace::FancyZones::SnapNewWindowIntoZone(workArea->ZoneSet());
|
||||
Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, workArea);
|
||||
AppZoneHistory::instance().UpdateProcessIdToHandleMap(window, workArea->UniqueId());
|
||||
@@ -434,7 +433,14 @@ void FancyZones::WindowCreated(HWND window) noexcept
|
||||
// Open on active monitor if window wasn't zoned
|
||||
if (openOnActiveMonitor && !movedToAppLastZone)
|
||||
{
|
||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { MonitorUtils::OpenWindowOnActiveMonitor(window, active); } }).wait();
|
||||
// window is recreated after switching virtual desktop
|
||||
// avoid moving already opened windows after switching vd
|
||||
bool isMoved = FancyZonesWindowProperties::RetreiveMovedOnOpeningProperty(window);
|
||||
if (!isMoved)
|
||||
{
|
||||
FancyZonesWindowProperties::StampMovedOnOpeningProperty(window);
|
||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { MonitorUtils::OpenWindowOnActiveMonitor(window, active); } }).wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,7 +567,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
||||
else if (wparam == static_cast<WPARAM>(HotkeyId::NextTab) || wparam == static_cast<WPARAM>(HotkeyId::PrevTab))
|
||||
{
|
||||
bool reverse = wparam == static_cast<WPARAM>(HotkeyId::PrevTab);
|
||||
CycleTabs(reverse);
|
||||
CycleWindows(reverse);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -630,7 +636,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
||||
else if (message == WM_PRIV_MOVESIZEEND)
|
||||
{
|
||||
auto hwnd = reinterpret_cast<HWND>(wparam);
|
||||
MoveSizeEnd(hwnd, ptScreen);
|
||||
MoveSizeEnd(hwnd);
|
||||
}
|
||||
else if (message == WM_PRIV_LOCATIONCHANGE)
|
||||
{
|
||||
@@ -708,13 +714,10 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
|
||||
|
||||
UpdateWorkAreas();
|
||||
|
||||
if ((changeType == DisplayChangeType::WorkArea) || (changeType == DisplayChangeType::DisplayChange))
|
||||
if (FancyZonesSettings::settings().displayChange_moveWindows)
|
||||
{
|
||||
if (FancyZonesSettings::settings().displayChange_moveWindows)
|
||||
{
|
||||
auto activeWorkAreas = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId());
|
||||
m_windowMoveHandler.UpdateWindowsPositions(activeWorkAreas);
|
||||
}
|
||||
auto activeWorkAreas = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId());
|
||||
m_windowMoveHandler.UpdateWindowsPositions(activeWorkAreas);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,7 +785,7 @@ void FancyZones::UpdateWorkAreas() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::CycleTabs(bool reverse) noexcept
|
||||
void FancyZones::CycleWindows(bool reverse) noexcept
|
||||
{
|
||||
auto window = GetForegroundWindow();
|
||||
HMONITOR current = WorkAreaKeyFromWindow(window);
|
||||
@@ -790,7 +793,7 @@ void FancyZones::CycleTabs(bool reverse) noexcept
|
||||
auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current);
|
||||
if (workArea)
|
||||
{
|
||||
workArea->CycleTabs(window, reverse);
|
||||
workArea->CycleWindows(window, reverse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -808,7 +811,7 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce
|
||||
auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), *currMonitorInfo);
|
||||
if (m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */, workArea))
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->ZoneSet());
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
return true;
|
||||
}
|
||||
// We iterated through all zones in current monitor zone layout, move on to next one (or previous depending on direction).
|
||||
@@ -844,7 +847,7 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->ZoneSet());
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
@@ -853,7 +856,7 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce
|
||||
bool moved = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, true /* cycle through zones */, workArea);
|
||||
if (moved)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->ZoneSet());
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
@@ -894,13 +897,13 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
||||
auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor);
|
||||
if (workArea)
|
||||
{
|
||||
auto zoneSet = workArea->ZoneSet();
|
||||
if (zoneSet)
|
||||
const auto& layout = workArea->GetLayout();
|
||||
if (layout)
|
||||
{
|
||||
const auto zones = zoneSet->GetZones();
|
||||
const auto& zones = layout->Zones();
|
||||
for (const auto& [zoneId, zone] : zones)
|
||||
{
|
||||
RECT zoneRect = zone->GetZoneRect();
|
||||
RECT zoneRect = zone.GetZoneRect();
|
||||
|
||||
zoneRect.left += monitorRect.left;
|
||||
zoneRect.right += monitorRect.left;
|
||||
@@ -929,7 +932,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
||||
// Moving to another monitor succeeded
|
||||
const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx];
|
||||
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }, workArea);
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->ZoneSet());
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -942,13 +945,13 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
||||
auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current);
|
||||
if (workArea)
|
||||
{
|
||||
auto zoneSet = workArea->ZoneSet();
|
||||
if (zoneSet)
|
||||
const auto& layout = workArea->GetLayout();
|
||||
if (layout)
|
||||
{
|
||||
const auto zones = zoneSet->GetZones();
|
||||
const auto& zones = layout->Zones();
|
||||
for (const auto& [zoneId, zone] : zones)
|
||||
{
|
||||
RECT zoneRect = zone->GetZoneRect();
|
||||
RECT zoneRect = zone.GetZoneRect();
|
||||
|
||||
zoneRect.left += currentMonitorRect.left;
|
||||
zoneRect.right += currentMonitorRect.left;
|
||||
@@ -974,7 +977,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
|
||||
// Moving to another monitor succeeded
|
||||
const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx];
|
||||
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }, workArea);
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->ZoneSet());
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -1013,7 +1016,7 @@ bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle
|
||||
bool result = m_windowMoveHandler.ExtendWindowByDirectionAndPosition(window, vkCode, workArea);
|
||||
if (result)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->ZoneSet());
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1022,7 +1025,7 @@ bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle
|
||||
bool result = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndPosition(window, vkCode, cycle, workArea);
|
||||
if (result)
|
||||
{
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->ZoneSet());
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1130,7 +1133,20 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept
|
||||
HMONITOR monitor = WorkAreaKeyFromWindow(window);
|
||||
|
||||
auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor);
|
||||
if (workArea && workArea->ZoneSet() && workArea->ZoneSet()->LayoutType() != FancyZonesDataTypes::ZoneSetLayoutType::Blank)
|
||||
if (!workArea)
|
||||
{
|
||||
Logger::error(L"No work area for processing snap hotkey");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& layout = workArea->GetLayout();
|
||||
if (!layout)
|
||||
{
|
||||
Logger::error(L"No layout for processing snap hotkey");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (layout->Type() != FancyZonesDataTypes::ZoneSetLayoutType::Blank)
|
||||
{
|
||||
if (vkCode == VK_UP || vkCode == VK_DOWN)
|
||||
{
|
||||
@@ -1142,6 +1158,7 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1193,7 +1210,7 @@ std::vector<std::pair<HMONITOR, RECT>> FancyZones::GetRawMonitorData() noexcept
|
||||
const auto& activeWorkAreaMap = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId());
|
||||
for (const auto& [monitor, workArea] : activeWorkAreaMap)
|
||||
{
|
||||
if (workArea->ZoneSet() != nullptr)
|
||||
if (workArea->GetLayout() != nullptr)
|
||||
{
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
|
||||
@@ -556,7 +556,7 @@ void AppZoneHistory::UpdateProcessIdToHandleMap(HWND window, const FancyZonesDat
|
||||
}
|
||||
}
|
||||
|
||||
ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring_view& zoneSetId) const
|
||||
ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId) const
|
||||
{
|
||||
auto processPath = get_process_path_waiting_uwp(window);
|
||||
if (processPath.empty())
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
|
||||
bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept;
|
||||
void UpdateProcessIdToHandleMap(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId);
|
||||
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring_view& zoneSetId) const;
|
||||
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId) const;
|
||||
|
||||
void SyncVirtualDesktops();
|
||||
void RemoveDeletedVirtualDesktops(const std::vector<GUID>& activeDesktops);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
namespace
|
||||
{
|
||||
// didn't use default constants since if they'll be changed later, it'll break this function
|
||||
bool isLayoutDefault(const Layout& layout)
|
||||
bool isLayoutDefault(const LayoutData& layout)
|
||||
{
|
||||
return layout.type == FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid &&
|
||||
layout.zoneCount == 3 &&
|
||||
@@ -31,11 +31,11 @@ namespace JsonUtils
|
||||
{
|
||||
struct LayoutJSON
|
||||
{
|
||||
static std::optional<Layout> FromJson(const json::JsonObject& json)
|
||||
static std::optional<LayoutData> FromJson(const json::JsonObject& json)
|
||||
{
|
||||
try
|
||||
{
|
||||
Layout data{};
|
||||
LayoutData data{};
|
||||
auto idStr = json.GetNamedString(NonLocalizable::AppliedLayoutsIds::UuidID);
|
||||
auto id = FancyZonesUtils::GuidFromString(idStr.c_str());
|
||||
if (!id.has_value())
|
||||
@@ -58,7 +58,7 @@ namespace JsonUtils
|
||||
}
|
||||
}
|
||||
|
||||
static json::JsonObject ToJson(const Layout& data)
|
||||
static json::JsonObject ToJson(const LayoutData& data)
|
||||
{
|
||||
json::JsonObject result{};
|
||||
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::UuidID, json::value(FancyZonesUtils::GuidToString(data.uuid).value()));
|
||||
@@ -141,7 +141,7 @@ namespace JsonUtils
|
||||
|
||||
public:
|
||||
FancyZonesDataTypes::WorkAreaId workAreaId;
|
||||
Layout data{};
|
||||
LayoutData data{};
|
||||
bool hasResolutionInId = false;
|
||||
|
||||
static std::optional<AppliedLayoutsJSON> FromJson(const json::JsonObject& json)
|
||||
@@ -427,7 +427,7 @@ void AppliedLayouts::RemoveDeletedVirtualDesktops(const std::vector<GUID>& activ
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Layout> AppliedLayouts::GetDeviceLayout(const FancyZonesDataTypes::WorkAreaId& id) const noexcept
|
||||
std::optional<LayoutData> AppliedLayouts::GetDeviceLayout(const FancyZonesDataTypes::WorkAreaId& id) const noexcept
|
||||
{
|
||||
auto iter = m_layouts.find(id);
|
||||
if (iter != m_layouts.end())
|
||||
@@ -449,7 +449,7 @@ bool AppliedLayouts::IsLayoutApplied(const FancyZonesDataTypes::WorkAreaId& id)
|
||||
return iter != m_layouts.end();
|
||||
}
|
||||
|
||||
bool AppliedLayouts::ApplyLayout(const FancyZonesDataTypes::WorkAreaId& deviceId, Layout layout)
|
||||
bool AppliedLayouts::ApplyLayout(const FancyZonesDataTypes::WorkAreaId& deviceId, LayoutData layout)
|
||||
{
|
||||
m_layouts[deviceId] = std::move(layout);
|
||||
return true;
|
||||
@@ -467,8 +467,20 @@ bool AppliedLayouts::ApplyDefaultLayout(const FancyZonesDataTypes::WorkAreaId& d
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: vertical or horizontal
|
||||
m_layouts[deviceId] = DefaultLayouts::instance().GetDefaultLayout();
|
||||
MonitorConfiguraionType type = MonitorConfiguraionType::Horizontal;
|
||||
MONITORINFOEX monitorInfo;
|
||||
monitorInfo.cbSize = sizeof(monitorInfo);
|
||||
if (GetMonitorInfo(deviceId.monitorId.monitor, &monitorInfo))
|
||||
{
|
||||
LONG width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
|
||||
LONG height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;
|
||||
if (height > width)
|
||||
{
|
||||
type = MonitorConfiguraionType::Vertical;
|
||||
}
|
||||
}
|
||||
|
||||
m_layouts[deviceId] = DefaultLayouts::instance().GetDefaultLayout(type);
|
||||
|
||||
// Saving default layout data doesn't make sense, since it's ignored on parsing.
|
||||
// Given that default layouts are ignored when parsing,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <FancyZonesLib/FancyZonesData/Layout.h>
|
||||
#include <FancyZonesLib/FancyZonesData/LayoutData.h>
|
||||
#include <FancyZonesLib/ModuleConstants.h>
|
||||
|
||||
#include <common/SettingsAPI/FileWatcher.h>
|
||||
@@ -35,7 +35,7 @@ namespace NonLocalizable
|
||||
class AppliedLayouts
|
||||
{
|
||||
public:
|
||||
using TAppliedLayoutsMap = std::unordered_map<FancyZonesDataTypes::WorkAreaId, Layout>;
|
||||
using TAppliedLayoutsMap = std::unordered_map<FancyZonesDataTypes::WorkAreaId, LayoutData>;
|
||||
|
||||
static AppliedLayouts& instance();
|
||||
|
||||
@@ -55,12 +55,12 @@ public:
|
||||
void SyncVirtualDesktops();
|
||||
void RemoveDeletedVirtualDesktops(const std::vector<GUID>& activeDesktops);
|
||||
|
||||
std::optional<Layout> GetDeviceLayout(const FancyZonesDataTypes::WorkAreaId& id) const noexcept;
|
||||
std::optional<LayoutData> GetDeviceLayout(const FancyZonesDataTypes::WorkAreaId& id) const noexcept;
|
||||
const TAppliedLayoutsMap& GetAppliedLayoutMap() const noexcept;
|
||||
|
||||
bool IsLayoutApplied(const FancyZonesDataTypes::WorkAreaId& id) const noexcept;
|
||||
|
||||
bool ApplyLayout(const FancyZonesDataTypes::WorkAreaId& deviceId, Layout layout);
|
||||
bool ApplyLayout(const FancyZonesDataTypes::WorkAreaId& deviceId, LayoutData layout);
|
||||
bool ApplyDefaultLayout(const FancyZonesDataTypes::WorkAreaId& deviceId);
|
||||
bool CloneLayout(const FancyZonesDataTypes::WorkAreaId& srcId, const FancyZonesDataTypes::WorkAreaId& dstId);
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ void CustomLayouts::LoadData()
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Layout> CustomLayouts::GetLayout(const GUID& id) const noexcept
|
||||
std::optional<LayoutData> CustomLayouts::GetLayout(const GUID& id) const noexcept
|
||||
{
|
||||
auto iter = m_layouts.find(id);
|
||||
if (iter == m_layouts.end())
|
||||
@@ -225,7 +225,7 @@ std::optional<Layout> CustomLayouts::GetLayout(const GUID& id) const noexcept
|
||||
}
|
||||
|
||||
FancyZonesDataTypes::CustomLayoutData customLayout = iter->second;
|
||||
Layout layout{
|
||||
LayoutData layout{
|
||||
.uuid = id,
|
||||
.type = FancyZonesDataTypes::ZoneSetLayoutType::Custom
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <FancyZonesLib/FancyZonesData/Layout.h>
|
||||
#include <FancyZonesLib/FancyZonesData/LayoutData.h>
|
||||
#include <FancyZonesLib/FancyZonesDataTypes.h>
|
||||
#include <FancyZonesLib/GuidUtils.h>
|
||||
#include <FancyZonesLib/ModuleConstants.h>
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
|
||||
void LoadData();
|
||||
|
||||
std::optional<Layout> GetLayout(const GUID& id) const noexcept;
|
||||
std::optional<LayoutData> GetLayout(const GUID& id) const noexcept;
|
||||
std::optional<FancyZonesDataTypes::CustomLayoutData> GetCustomLayoutData(const GUID& id) const noexcept;
|
||||
const TCustomLayoutMap& GetAllLayouts() const noexcept;
|
||||
|
||||
|
||||
@@ -34,11 +34,11 @@ namespace DefaultLayoutsJsonUtils
|
||||
|
||||
struct LayoutJSON
|
||||
{
|
||||
static std::optional<Layout> FromJson(const json::JsonObject& json)
|
||||
static std::optional<LayoutData> FromJson(const json::JsonObject& json)
|
||||
{
|
||||
try
|
||||
{
|
||||
Layout data{};
|
||||
LayoutData data{};
|
||||
auto idStr = json.GetNamedString(NonLocalizable::DefaultLayoutsIds::UuidID, L"");
|
||||
if (!idStr.empty())
|
||||
{
|
||||
@@ -65,7 +65,7 @@ namespace DefaultLayoutsJsonUtils
|
||||
}
|
||||
}
|
||||
|
||||
static json::JsonObject ToJson(const Layout& data)
|
||||
static json::JsonObject ToJson(const LayoutData& data)
|
||||
{
|
||||
json::JsonObject result{};
|
||||
result.SetNamedValue(NonLocalizable::DefaultLayoutsIds::UuidID, json::value(FancyZonesUtils::GuidToString(data.uuid).value()));
|
||||
@@ -81,7 +81,7 @@ namespace DefaultLayoutsJsonUtils
|
||||
struct DefaultLayoutJSON
|
||||
{
|
||||
MonitorConfiguraionType monitorConfigurationType{ MonitorConfiguraionType::Horizontal };
|
||||
Layout layout{};
|
||||
LayoutData layout{};
|
||||
|
||||
static std::optional<DefaultLayoutJSON> FromJson(const json::JsonObject& json)
|
||||
{
|
||||
@@ -163,7 +163,7 @@ void DefaultLayouts::LoadData()
|
||||
}
|
||||
}
|
||||
|
||||
Layout DefaultLayouts::GetDefaultLayout(MonitorConfiguraionType type) const noexcept
|
||||
LayoutData DefaultLayouts::GetDefaultLayout(MonitorConfiguraionType type) const noexcept
|
||||
{
|
||||
auto iter = m_layouts.find(type);
|
||||
if (iter != m_layouts.end())
|
||||
@@ -171,5 +171,5 @@ Layout DefaultLayouts::GetDefaultLayout(MonitorConfiguraionType type) const noex
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
return Layout{};
|
||||
return LayoutData{};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <FancyZonesLib/FancyZonesData/Layout.h>
|
||||
#include <FancyZonesLib/FancyZonesData/LayoutData.h>
|
||||
#include <FancyZonesLib/ModuleConstants.h>
|
||||
|
||||
#include <common/SettingsAPI/FileWatcher.h>
|
||||
@@ -31,7 +31,7 @@ enum class MonitorConfiguraionType
|
||||
class DefaultLayouts
|
||||
{
|
||||
public:
|
||||
using TDefaultLayoutsContainer = std::map<MonitorConfiguraionType, Layout>;
|
||||
using TDefaultLayoutsContainer = std::map<MonitorConfiguraionType, LayoutData>;
|
||||
|
||||
static DefaultLayouts& instance();
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
|
||||
void LoadData();
|
||||
|
||||
Layout GetDefaultLayout(MonitorConfiguraionType type = MonitorConfiguraionType::Horizontal) const noexcept;
|
||||
LayoutData GetDefaultLayout(MonitorConfiguraionType type = MonitorConfiguraionType::Horizontal) const noexcept;
|
||||
|
||||
private:
|
||||
DefaultLayouts();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
|
||||
#include <FancyZonesLib/FancyZonesDataTypes.h>
|
||||
|
||||
struct Layout
|
||||
struct LayoutData
|
||||
{
|
||||
GUID uuid = GUID_NULL;
|
||||
FancyZonesDataTypes::ZoneSetLayoutType type = FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid;
|
||||
@@ -15,7 +15,7 @@ struct Layout
|
||||
int sensitivityRadius = DefaultValues::SensitivityRadius;
|
||||
};
|
||||
|
||||
inline bool operator==(const Layout& lhs, const Layout& rhs)
|
||||
inline bool operator==(const LayoutData& lhs, const LayoutData& rhs)
|
||||
{
|
||||
return lhs.uuid == rhs.uuid &&
|
||||
lhs.type == rhs.type &&
|
||||
@@ -10,11 +10,11 @@ namespace JsonUtils
|
||||
{
|
||||
struct TemplateLayoutJSON
|
||||
{
|
||||
static std::optional<Layout> FromJson(const json::JsonObject& json)
|
||||
static std::optional<LayoutData> FromJson(const json::JsonObject& json)
|
||||
{
|
||||
try
|
||||
{
|
||||
Layout data;
|
||||
LayoutData data;
|
||||
|
||||
data.uuid = GUID_NULL;
|
||||
data.type = FancyZonesDataTypes::TypeFromString(std::wstring{ json.GetNamedString(NonLocalizable::LayoutTemplatesIds::TypeID) });
|
||||
@@ -32,9 +32,9 @@ namespace JsonUtils
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<Layout> ParseJson(const json::JsonObject& json)
|
||||
std::vector<LayoutData> ParseJson(const json::JsonObject& json)
|
||||
{
|
||||
std::vector<Layout> vec{};
|
||||
std::vector<LayoutData> vec{};
|
||||
auto layouts = json.GetNamedArray(NonLocalizable::LayoutTemplatesIds::LayoutTemplatesArrayID);
|
||||
|
||||
for (uint32_t i = 0; i < layouts.Size(); ++i)
|
||||
@@ -85,7 +85,7 @@ void LayoutTemplates::LoadData()
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Layout> LayoutTemplates::GetLayout(FancyZonesDataTypes::ZoneSetLayoutType type) const noexcept
|
||||
std::optional<LayoutData> LayoutTemplates::GetLayout(FancyZonesDataTypes::ZoneSetLayoutType type) const noexcept
|
||||
{
|
||||
for (const auto& layout : m_layouts)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <FancyZonesLib/FancyZonesData/Layout.h>
|
||||
#include <FancyZonesLib/FancyZonesData/LayoutData.h>
|
||||
#include <FancyZonesLib/ModuleConstants.h>
|
||||
|
||||
#include <common/SettingsAPI/FileWatcher.h>
|
||||
@@ -35,12 +35,12 @@ public:
|
||||
|
||||
void LoadData();
|
||||
|
||||
std::optional<Layout> GetLayout(FancyZonesDataTypes::ZoneSetLayoutType type) const noexcept;
|
||||
std::optional<LayoutData> GetLayout(FancyZonesDataTypes::ZoneSetLayoutType type) const noexcept;
|
||||
|
||||
private:
|
||||
LayoutTemplates();
|
||||
~LayoutTemplates() = default;
|
||||
|
||||
std::unique_ptr<FileWatcher> m_fileWatcher;
|
||||
std::vector<Layout> m_layouts;
|
||||
std::vector<LayoutData> m_layouts;
|
||||
};
|
||||
@@ -42,7 +42,7 @@
|
||||
<ClInclude Include="FancyZones.h" />
|
||||
<ClInclude Include="FancyZonesDataTypes.h" />
|
||||
<ClInclude Include="FancyZonesData\DefaultLayouts.h" />
|
||||
<ClInclude Include="FancyZonesData\Layout.h" />
|
||||
<ClInclude Include="FancyZonesData\LayoutData.h" />
|
||||
<ClInclude Include="FancyZonesData\LayoutDefaults.h" />
|
||||
<ClInclude Include="FancyZonesData\LayoutTemplates.h" />
|
||||
<ClInclude Include="FancyZonesWindowProcessing.h" />
|
||||
@@ -53,7 +53,9 @@
|
||||
<ClInclude Include="JsonHelpers.h" />
|
||||
<ClInclude Include="KeyState.h" />
|
||||
<ClInclude Include="FancyZonesData\LayoutHotkeys.h" />
|
||||
<ClInclude Include="Layout.h" />
|
||||
<ClInclude Include="LayoutConfigurator.h" />
|
||||
<ClInclude Include="LayoutAssignedWindows.h" />
|
||||
<ClInclude Include="ModuleConstants.h" />
|
||||
<ClInclude Include="MonitorUtils.h" />
|
||||
<ClInclude Include="MonitorWorkAreaHandler.h" />
|
||||
@@ -73,7 +75,6 @@
|
||||
<ClInclude Include="Zone.h" />
|
||||
<ClInclude Include="Colors.h" />
|
||||
<ClInclude Include="ZoneIndexSetBitmask.h" />
|
||||
<ClInclude Include="ZoneSet.h" />
|
||||
<ClInclude Include="WorkArea.h" />
|
||||
<ClInclude Include="ZonesOverlay.h" />
|
||||
</ItemGroup>
|
||||
@@ -104,7 +105,9 @@
|
||||
<ClCompile Include="FancyZonesData\LayoutHotkeys.cpp">
|
||||
<PrecompiledHeaderFile>../pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Layout.cpp" />
|
||||
<ClCompile Include="LayoutConfigurator.cpp" />
|
||||
<ClCompile Include="LayoutAssignedWindows.cpp" />
|
||||
<ClCompile Include="MonitorUtils.cpp" />
|
||||
<ClCompile Include="MonitorWorkAreaHandler.cpp" />
|
||||
<ClCompile Include="OnThreadExecutor.cpp" />
|
||||
@@ -119,7 +122,6 @@
|
||||
<ClCompile Include="WindowMoveHandler.cpp" />
|
||||
<ClCompile Include="WindowUtils.cpp" />
|
||||
<ClCompile Include="Zone.cpp" />
|
||||
<ClCompile Include="ZoneSet.cpp" />
|
||||
<ClCompile Include="WorkArea.cpp" />
|
||||
<ClCompile Include="ZonesOverlay.cpp" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -30,9 +30,6 @@
|
||||
<ClInclude Include="Zone.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ZoneSet.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WorkArea.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -99,6 +96,12 @@
|
||||
<ClInclude Include="ModuleConstants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FancyZonesData\LayoutTemplates.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FancyZonesData\LayoutDefaults.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ZoneIndexSetBitmask.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -129,9 +132,6 @@
|
||||
<ClInclude Include="FancyZonesData\DefaultLayouts.h">
|
||||
<Filter>Header Files\FancyZonesData</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FancyZonesData\Layout.h">
|
||||
<Filter>Header Files\FancyZonesData</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FancyZonesData\LayoutDefaults.h">
|
||||
<Filter>Header Files\FancyZonesData</Filter>
|
||||
</ClInclude>
|
||||
@@ -144,6 +144,15 @@
|
||||
<ClInclude Include="EditorParameters.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LayoutAssignedWindows.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Layout.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FancyZonesData\LayoutData.h">
|
||||
<Filter>Header Files\FancyZonesData</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -152,9 +161,6 @@
|
||||
<ClCompile Include="Zone.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ZoneSet.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WorkArea.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -236,6 +242,12 @@
|
||||
<ClCompile Include="FancyZonesData\LayoutTemplates.cpp">
|
||||
<Filter>Source Files\FancyZonesData</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Layout.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LayoutAssignedWindows.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
@@ -83,6 +83,17 @@ ZoneIndexSet FancyZonesWindowProperties::RetrieveZoneIndexProperty(HWND window)
|
||||
return bitmask.ToIndexSet();
|
||||
}
|
||||
|
||||
void FancyZonesWindowProperties::StampMovedOnOpeningProperty(HWND window)
|
||||
{
|
||||
::SetPropW(window, ZonedWindowProperties::PropertyMovedOnOpening, (HANDLE)1);
|
||||
}
|
||||
|
||||
bool FancyZonesWindowProperties::RetreiveMovedOnOpeningProperty(HWND window)
|
||||
{
|
||||
HANDLE handle = ::GetProp(window, ZonedWindowProperties::PropertyMovedOnOpening);
|
||||
return handle != nullptr;
|
||||
}
|
||||
|
||||
std::optional<size_t> FancyZonesWindowProperties::GetTabSortKeyWithinZone(HWND window)
|
||||
{
|
||||
auto rawTabSortKeyWithinZone = ::GetPropW(window, ZonedWindowProperties::PropertySortKeyWithinZone);
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace ZonedWindowProperties
|
||||
const wchar_t PropertyRestoreSizeID[] = L"FancyZones_RestoreSize";
|
||||
const wchar_t PropertyRestoreOriginID[] = L"FancyZones_RestoreOrigin";
|
||||
const wchar_t PropertyCornerPreference[] = L"FancyZones_CornerPreference";
|
||||
const wchar_t PropertyMovedOnOpening[] = L"FancyZones_MovedOnOpening";
|
||||
|
||||
const wchar_t MultiMonitorName[] = L"FancyZones";
|
||||
const wchar_t MultiMonitorInstance[] = L"MultiMonitorDevice";
|
||||
@@ -21,6 +22,9 @@ namespace FancyZonesWindowProperties
|
||||
void RemoveZoneIndexProperty(HWND window);
|
||||
ZoneIndexSet RetrieveZoneIndexProperty(HWND window);
|
||||
|
||||
void StampMovedOnOpeningProperty(HWND window);
|
||||
bool RetreiveMovedOnOpeningProperty(HWND window);
|
||||
|
||||
std::optional<size_t> GetTabSortKeyWithinZone(HWND window);
|
||||
void SetTabSortKeyWithinZone(HWND window, std::optional<size_t> tabSortKeyWithinZone);
|
||||
}
|
||||
|
||||
334
src/modules/fancyzones/FancyZonesLib/Layout.cpp
Normal file
334
src/modules/fancyzones/FancyZonesLib/Layout.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
#include "pch.h"
|
||||
#include "Layout.h"
|
||||
|
||||
#include <FancyZonesLib/FancyZonesData/CustomLayouts.h>
|
||||
#include <FancyZonesLib/FancyZonesWindowProperties.h>
|
||||
#include <FancyZonesLib/LayoutConfigurator.h>
|
||||
#include <FancyZonesLib/Settings.h>
|
||||
#include <FancyZonesLib/WindowUtils.h>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
|
||||
namespace ZoneSelectionAlgorithms
|
||||
{
|
||||
constexpr int OVERLAPPING_CENTERS_SENSITIVITY = 75;
|
||||
|
||||
template<class CompareF>
|
||||
ZoneIndexSet ZoneSelectPriority(const ZonesMap& zones, const ZoneIndexSet& capturedZones, CompareF compare)
|
||||
{
|
||||
size_t chosen = 0;
|
||||
|
||||
for (size_t i = 1; i < capturedZones.size(); ++i)
|
||||
{
|
||||
if (compare(zones.at(capturedZones[i]), zones.at(capturedZones[chosen])))
|
||||
{
|
||||
chosen = i;
|
||||
}
|
||||
}
|
||||
|
||||
return { capturedZones[chosen] };
|
||||
}
|
||||
|
||||
ZoneIndexSet ZoneSelectSubregion(const ZonesMap& zones, const ZoneIndexSet& capturedZones, POINT pt, int sensitivityRadius)
|
||||
{
|
||||
auto expand = [&](RECT& rect) {
|
||||
rect.top -= sensitivityRadius / 2;
|
||||
rect.bottom += sensitivityRadius / 2;
|
||||
rect.left -= sensitivityRadius / 2;
|
||||
rect.right += sensitivityRadius / 2;
|
||||
};
|
||||
|
||||
// Compute the overlapped rectangle.
|
||||
RECT overlap = zones.at(capturedZones[0]).GetZoneRect();
|
||||
expand(overlap);
|
||||
|
||||
for (size_t i = 1; i < capturedZones.size(); ++i)
|
||||
{
|
||||
RECT current = zones.at(capturedZones[i]).GetZoneRect();
|
||||
expand(current);
|
||||
|
||||
overlap.top = max(overlap.top, current.top);
|
||||
overlap.left = max(overlap.left, current.left);
|
||||
overlap.bottom = min(overlap.bottom, current.bottom);
|
||||
overlap.right = min(overlap.right, current.right);
|
||||
}
|
||||
|
||||
// Avoid division by zero
|
||||
int width = max(overlap.right - overlap.left, 1);
|
||||
int height = max(overlap.bottom - overlap.top, 1);
|
||||
|
||||
bool verticalSplit = height > width;
|
||||
ZoneIndex zoneIndex;
|
||||
|
||||
if (verticalSplit)
|
||||
{
|
||||
zoneIndex = (static_cast<ZoneIndex>(pt.y) - overlap.top) * capturedZones.size() / height;
|
||||
}
|
||||
else
|
||||
{
|
||||
zoneIndex = (static_cast<ZoneIndex>(pt.x) - overlap.left) * capturedZones.size() / width;
|
||||
}
|
||||
|
||||
zoneIndex = std::clamp(zoneIndex, ZoneIndex(0), static_cast<ZoneIndex>(capturedZones.size()) - 1);
|
||||
|
||||
return { capturedZones[zoneIndex] };
|
||||
}
|
||||
|
||||
ZoneIndexSet ZoneSelectClosestCenter(const ZonesMap& zones, const ZoneIndexSet& capturedZones, POINT pt)
|
||||
{
|
||||
auto getCenter = [](auto zone) {
|
||||
RECT rect = zone.GetZoneRect();
|
||||
return POINT{ (rect.right + rect.left) / 2, (rect.top + rect.bottom) / 2 };
|
||||
};
|
||||
auto pointDifference = [](POINT pt1, POINT pt2) {
|
||||
return (pt1.x - pt2.x) * (pt1.x - pt2.x) + (pt1.y - pt2.y) * (pt1.y - pt2.y);
|
||||
};
|
||||
auto distanceFromCenter = [&](auto zone) {
|
||||
POINT center = getCenter(zone);
|
||||
return pointDifference(center, pt);
|
||||
};
|
||||
auto closerToCenter = [&](auto zone1, auto zone2) {
|
||||
if (pointDifference(getCenter(zone1), getCenter(zone2)) > OVERLAPPING_CENTERS_SENSITIVITY)
|
||||
{
|
||||
return distanceFromCenter(zone1) < distanceFromCenter(zone2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return zone1.GetZoneArea() < zone2.GetZoneArea();
|
||||
};
|
||||
};
|
||||
return ZoneSelectPriority(zones, capturedZones, closerToCenter);
|
||||
}
|
||||
}
|
||||
|
||||
Layout::Layout(const LayoutData& data) :
|
||||
m_data(data)
|
||||
{
|
||||
}
|
||||
|
||||
bool Layout::Init(const FancyZonesUtils::Rect& workArea, HMONITOR monitor) noexcept
|
||||
{
|
||||
//invalid work area
|
||||
if (workArea.width() == 0 || workArea.height() == 0)
|
||||
{
|
||||
Logger::error(L"Layout initialization: invalid work area");
|
||||
return false;
|
||||
}
|
||||
|
||||
//invalid zoneCount, may cause division by zero
|
||||
if (m_data.zoneCount <= 0 && m_data.type != FancyZonesDataTypes::ZoneSetLayoutType::Custom)
|
||||
{
|
||||
Logger::error(L"Layout initialization: invalid zone count");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (m_data.type)
|
||||
{
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::Focus:
|
||||
m_zones = LayoutConfigurator::Focus(workArea, m_data.zoneCount);
|
||||
break;
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::Columns:
|
||||
m_zones = LayoutConfigurator::Columns(workArea, m_data.zoneCount, m_data.spacing);
|
||||
break;
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::Rows:
|
||||
m_zones = LayoutConfigurator::Rows(workArea, m_data.zoneCount, m_data.spacing);
|
||||
break;
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::Grid:
|
||||
m_zones = LayoutConfigurator::Grid(workArea, m_data.zoneCount, m_data.spacing);
|
||||
break;
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid:
|
||||
m_zones = LayoutConfigurator::PriorityGrid(workArea, m_data.zoneCount, m_data.spacing);
|
||||
break;
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::Custom:
|
||||
{
|
||||
const auto customLayoutData = CustomLayouts::instance().GetCustomLayoutData(m_data.uuid);
|
||||
if (customLayoutData.has_value())
|
||||
{
|
||||
m_zones = LayoutConfigurator::Custom(workArea, monitor, customLayoutData.value(), m_data.spacing);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error(L"Custom layout not found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return m_zones.size() == m_data.zoneCount;
|
||||
}
|
||||
|
||||
GUID Layout::Id() const noexcept
|
||||
{
|
||||
return m_data.uuid;
|
||||
}
|
||||
|
||||
FancyZonesDataTypes::ZoneSetLayoutType Layout::Type() const noexcept
|
||||
{
|
||||
return m_data.type;
|
||||
}
|
||||
|
||||
const ZonesMap& Layout::Zones() const noexcept
|
||||
{
|
||||
return m_zones;
|
||||
}
|
||||
|
||||
ZoneIndexSet Layout::ZonesFromPoint(POINT pt) const noexcept
|
||||
{
|
||||
ZoneIndexSet capturedZones;
|
||||
ZoneIndexSet strictlyCapturedZones;
|
||||
for (const auto& [zoneId, zone] : m_zones)
|
||||
{
|
||||
const RECT& zoneRect = zone.GetZoneRect();
|
||||
if (zoneRect.left - m_data.sensitivityRadius <= pt.x && pt.x <= zoneRect.right + m_data.sensitivityRadius &&
|
||||
zoneRect.top - m_data.sensitivityRadius <= pt.y && pt.y <= zoneRect.bottom + m_data.sensitivityRadius)
|
||||
{
|
||||
capturedZones.emplace_back(zoneId);
|
||||
}
|
||||
|
||||
if (zoneRect.left <= pt.x && pt.x < zoneRect.right &&
|
||||
zoneRect.top <= pt.y && pt.y < zoneRect.bottom)
|
||||
{
|
||||
strictlyCapturedZones.emplace_back(zoneId);
|
||||
}
|
||||
}
|
||||
|
||||
// If only one zone is captured, but it's not strictly captured
|
||||
// don't consider it as captured
|
||||
if (capturedZones.size() == 1 && strictlyCapturedZones.size() == 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// If captured zones do not overlap, return all of them
|
||||
// Otherwise, return one of them based on the chosen selection algorithm.
|
||||
bool overlap = false;
|
||||
for (size_t i = 0; i < capturedZones.size(); ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j < capturedZones.size(); ++j)
|
||||
{
|
||||
RECT rectI;
|
||||
RECT rectJ;
|
||||
try
|
||||
{
|
||||
rectI = m_zones.at(capturedZones[i]).GetZoneRect();
|
||||
rectJ = m_zones.at(capturedZones[j]).GetZoneRect();
|
||||
}
|
||||
catch (std::out_of_range)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (max(rectI.top, rectJ.top) + m_data.sensitivityRadius < min(rectI.bottom, rectJ.bottom) &&
|
||||
max(rectI.left, rectJ.left) + m_data.sensitivityRadius < min(rectI.right, rectJ.right))
|
||||
{
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (overlap)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
try
|
||||
{
|
||||
using Algorithm = OverlappingZonesAlgorithm;
|
||||
|
||||
switch (FancyZonesSettings::settings().overlappingZonesAlgorithm)
|
||||
{
|
||||
case Algorithm::Smallest:
|
||||
return ZoneSelectionAlgorithms::ZoneSelectPriority(m_zones, capturedZones, [&](auto zone1, auto zone2) { return zone1.GetZoneArea() < zone2.GetZoneArea(); });
|
||||
case Algorithm::Largest:
|
||||
return ZoneSelectionAlgorithms::ZoneSelectPriority(m_zones, capturedZones, [&](auto zone1, auto zone2) { return zone1.GetZoneArea() > zone2.GetZoneArea(); });
|
||||
case Algorithm::Positional:
|
||||
return ZoneSelectionAlgorithms::ZoneSelectSubregion(m_zones, capturedZones, pt, m_data.sensitivityRadius);
|
||||
case Algorithm::ClosestCenter:
|
||||
return ZoneSelectionAlgorithms::ZoneSelectClosestCenter(m_zones, capturedZones, pt);
|
||||
}
|
||||
}
|
||||
catch (std::out_of_range)
|
||||
{
|
||||
Logger::error("Exception out_of_range was thrown in ZoneSet::ZonesFromPoint");
|
||||
return { capturedZones[0] };
|
||||
}
|
||||
}
|
||||
|
||||
return capturedZones;
|
||||
}
|
||||
|
||||
ZoneIndexSet Layout::GetCombinedZoneRange(const ZoneIndexSet& initialZones, const ZoneIndexSet& finalZones) const noexcept
|
||||
{
|
||||
ZoneIndexSet combinedZones, result;
|
||||
std::set_union(begin(initialZones), end(initialZones), begin(finalZones), end(finalZones), std::back_inserter(combinedZones));
|
||||
|
||||
RECT boundingRect;
|
||||
bool boundingRectEmpty = true;
|
||||
|
||||
for (ZoneIndex zoneId : combinedZones)
|
||||
{
|
||||
if (m_zones.contains(zoneId))
|
||||
{
|
||||
const RECT rect = m_zones.at(zoneId).GetZoneRect();
|
||||
if (boundingRectEmpty)
|
||||
{
|
||||
boundingRect = rect;
|
||||
boundingRectEmpty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
boundingRect.left = min(boundingRect.left, rect.left);
|
||||
boundingRect.top = min(boundingRect.top, rect.top);
|
||||
boundingRect.right = max(boundingRect.right, rect.right);
|
||||
boundingRect.bottom = max(boundingRect.bottom, rect.bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!boundingRectEmpty)
|
||||
{
|
||||
for (const auto& [zoneId, zone] : m_zones)
|
||||
{
|
||||
const RECT rect = zone.GetZoneRect();
|
||||
if (boundingRect.left <= rect.left && rect.right <= boundingRect.right &&
|
||||
boundingRect.top <= rect.top && rect.bottom <= boundingRect.bottom)
|
||||
{
|
||||
result.push_back(zoneId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
RECT Layout::GetCombinedZonesRect(const ZoneIndexSet& zones)
|
||||
{
|
||||
RECT size{};
|
||||
bool sizeEmpty = true;
|
||||
|
||||
for (ZoneIndex id : zones)
|
||||
{
|
||||
if (m_zones.contains(id))
|
||||
{
|
||||
const auto& zone = m_zones.at(id);
|
||||
const RECT newSize = zone.GetZoneRect();
|
||||
if (!sizeEmpty)
|
||||
{
|
||||
size.left = min(size.left, newSize.left);
|
||||
size.top = min(size.top, newSize.top);
|
||||
size.right = max(size.right, newSize.right);
|
||||
size.bottom = max(size.bottom, newSize.bottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = newSize;
|
||||
sizeEmpty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
31
src/modules/fancyzones/FancyZonesLib/Layout.h
Normal file
31
src/modules/fancyzones/FancyZonesLib/Layout.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <FancyZonesLib/FancyZonesData/LayoutData.h>
|
||||
#include <FancyZonesLib/util.h>
|
||||
|
||||
#include <FancyZonesLib/LayoutConfigurator.h> // ZonesMap
|
||||
|
||||
class Layout
|
||||
{
|
||||
public:
|
||||
Layout(const LayoutData& data);
|
||||
~Layout() = default;
|
||||
|
||||
bool Init(const FancyZonesUtils::Rect& workAreaRect, HMONITOR monitor) noexcept;
|
||||
|
||||
GUID Id() const noexcept;
|
||||
FancyZonesDataTypes::ZoneSetLayoutType Type() const noexcept;
|
||||
|
||||
const ZonesMap& Zones() const noexcept;
|
||||
ZoneIndexSet ZonesFromPoint(POINT pt) const noexcept;
|
||||
/**
|
||||
* Returns all zones spanned by the minimum bounding rectangle containing the two given zone index sets.
|
||||
*/
|
||||
ZoneIndexSet GetCombinedZoneRange(const ZoneIndexSet& initialZones, const ZoneIndexSet& finalZones) const noexcept;
|
||||
|
||||
RECT GetCombinedZonesRect(const ZoneIndexSet& zones);
|
||||
|
||||
private:
|
||||
const LayoutData m_data;
|
||||
ZonesMap m_zones{};
|
||||
};
|
||||
199
src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.cpp
Normal file
199
src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include "pch.h"
|
||||
#include "LayoutAssignedWindows.h"
|
||||
|
||||
#include <FancyZonesLib/FancyZonesWindowProperties.h>
|
||||
#include <FancyZonesLib/Settings.h>
|
||||
#include <FancyZonesLib/VirtualDesktop.h>
|
||||
#include <FancyZonesLib/WindowUtils.h>
|
||||
|
||||
LayoutAssignedWindows::LayoutAssignedWindows()
|
||||
{
|
||||
m_extendData = std::make_unique<ExtendWindowModeData>();
|
||||
}
|
||||
|
||||
void LayoutAssignedWindows::Assign(HWND window, const ZoneIndexSet& zones)
|
||||
{
|
||||
Dismiss(window);
|
||||
|
||||
// clear info about extention
|
||||
std::erase_if(m_extendData->windowInitialIndexSet, [window](const auto& item) { return item.first == window; });
|
||||
std::erase_if(m_extendData->windowFinalIndex, [window](const auto& item) { return item.first == window; });
|
||||
|
||||
for (const auto& index : zones)
|
||||
{
|
||||
m_windowIndexSet[window].push_back(index);
|
||||
}
|
||||
|
||||
if (FancyZonesSettings::settings().disableRoundCorners)
|
||||
{
|
||||
FancyZonesWindowUtils::DisableRoundCorners(window);
|
||||
}
|
||||
|
||||
auto tabSortKeyWithinZone = FancyZonesWindowProperties::GetTabSortKeyWithinZone(window);
|
||||
InsertWindowIntoZone(window, tabSortKeyWithinZone, zones);
|
||||
}
|
||||
|
||||
void LayoutAssignedWindows::Extend(HWND window, const ZoneIndexSet& zones)
|
||||
{
|
||||
Dismiss(window);
|
||||
|
||||
for (const auto& index : zones)
|
||||
{
|
||||
m_windowIndexSet[window].push_back(index);
|
||||
}
|
||||
|
||||
if (FancyZonesSettings::settings().disableRoundCorners)
|
||||
{
|
||||
FancyZonesWindowUtils::DisableRoundCorners(window);
|
||||
}
|
||||
|
||||
auto tabSortKeyWithinZone = FancyZonesWindowProperties::GetTabSortKeyWithinZone(window);
|
||||
InsertWindowIntoZone(window, tabSortKeyWithinZone, zones);
|
||||
}
|
||||
|
||||
void LayoutAssignedWindows::Dismiss(HWND window)
|
||||
{
|
||||
if (m_windowIndexSet.contains(window))
|
||||
{
|
||||
const auto& indexSet = m_windowIndexSet.at(window);
|
||||
auto& windows = m_windowsByIndexSets[indexSet];
|
||||
windows.erase(find(begin(windows), end(windows), window));
|
||||
if (windows.empty())
|
||||
{
|
||||
m_windowsByIndexSets.erase(m_windowIndexSet[window]);
|
||||
}
|
||||
|
||||
m_windowIndexSet.erase(window);
|
||||
}
|
||||
|
||||
FancyZonesWindowProperties::SetTabSortKeyWithinZone(window, std::nullopt);
|
||||
}
|
||||
|
||||
ZoneIndexSet LayoutAssignedWindows::GetZoneIndexSetFromWindow(HWND window) const noexcept
|
||||
{
|
||||
auto it = m_windowIndexSet.find(window);
|
||||
if (it != m_windowIndexSet.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool LayoutAssignedWindows::IsZoneEmpty(ZoneIndex zoneIndex) const noexcept
|
||||
{
|
||||
for (auto& [window, zones] : m_windowIndexSet)
|
||||
{
|
||||
if (find(begin(zones), end(zones), zoneIndex) != end(zones))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayoutAssignedWindows::CycleWindows(HWND window, bool reverse)
|
||||
{
|
||||
auto indexSet = GetZoneIndexSetFromWindow(window);
|
||||
|
||||
// Do nothing in case the window is not recognized
|
||||
if (indexSet.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto next = GetNextZoneWindow(indexSet, window, reverse);
|
||||
if (!next)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Determine whether the window still exists
|
||||
if (!IsWindow(next))
|
||||
{
|
||||
// Dismiss the encountered window since it was probably closed
|
||||
Dismiss(next);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (VirtualDesktop::instance().IsWindowOnCurrentDesktop(next))
|
||||
{
|
||||
FancyZonesWindowUtils::SwitchToWindow(next);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const std::unique_ptr<LayoutAssignedWindows::ExtendWindowModeData>& LayoutAssignedWindows::ExtendWindowData()
|
||||
{
|
||||
return m_extendData;
|
||||
}
|
||||
|
||||
void LayoutAssignedWindows::InsertWindowIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet)
|
||||
{
|
||||
if (tabSortKeyWithinZone.has_value())
|
||||
{
|
||||
// Insert the tab using the provided sort key
|
||||
auto predicate = [tabSortKeyWithinZone](HWND tab) {
|
||||
auto currentTabSortKeyWithinZone = FancyZonesWindowProperties::GetTabSortKeyWithinZone(tab);
|
||||
if (currentTabSortKeyWithinZone.has_value())
|
||||
{
|
||||
return currentTabSortKeyWithinZone.value() > tabSortKeyWithinZone;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto position = std::find_if(m_windowsByIndexSets[indexSet].begin(), m_windowsByIndexSets[indexSet].end(), predicate);
|
||||
m_windowsByIndexSets[indexSet].insert(position, window);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert the tab at the end
|
||||
tabSortKeyWithinZone = 0;
|
||||
if (!m_windowsByIndexSets[indexSet].empty())
|
||||
{
|
||||
auto prevTab = m_windowsByIndexSets[indexSet].back();
|
||||
auto prevTabSortKeyWithinZone = FancyZonesWindowProperties::GetTabSortKeyWithinZone(prevTab);
|
||||
if (prevTabSortKeyWithinZone.has_value())
|
||||
{
|
||||
tabSortKeyWithinZone = prevTabSortKeyWithinZone.value() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_windowsByIndexSets[indexSet].push_back(window);
|
||||
}
|
||||
|
||||
FancyZonesWindowProperties::SetTabSortKeyWithinZone(window, tabSortKeyWithinZone);
|
||||
}
|
||||
|
||||
HWND LayoutAssignedWindows::GetNextZoneWindow(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept
|
||||
{
|
||||
if (!m_windowsByIndexSets.contains(indexSet))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto& assignedWindows = m_windowsByIndexSets[indexSet];
|
||||
if (assignedWindows.empty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto iter = std::find(assignedWindows.begin(), assignedWindows.end(), current);
|
||||
if (!reverse)
|
||||
{
|
||||
++iter;
|
||||
return iter == assignedWindows.end() ? assignedWindows.front() : *iter;
|
||||
}
|
||||
else
|
||||
{
|
||||
return iter == assignedWindows.begin() ? assignedWindows.back() : *(--iter);
|
||||
}
|
||||
}
|
||||
36
src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.h
Normal file
36
src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <FancyZonesLib/Zone.h>
|
||||
|
||||
class LayoutAssignedWindows
|
||||
{
|
||||
public:
|
||||
struct ExtendWindowModeData
|
||||
{
|
||||
std::map<HWND, ZoneIndexSet> windowInitialIndexSet;
|
||||
std::map<HWND, ZoneIndex> windowFinalIndex;
|
||||
};
|
||||
|
||||
public :
|
||||
LayoutAssignedWindows();
|
||||
~LayoutAssignedWindows() = default;
|
||||
|
||||
void Assign(HWND window, const ZoneIndexSet& zones);
|
||||
void Extend(HWND window, const ZoneIndexSet& zones);
|
||||
void Dismiss(HWND window);
|
||||
|
||||
ZoneIndexSet GetZoneIndexSetFromWindow(HWND window) const noexcept;
|
||||
bool IsZoneEmpty(ZoneIndex zoneIndex) const noexcept;
|
||||
|
||||
void CycleWindows(HWND window, bool reverse);
|
||||
|
||||
const std::unique_ptr<ExtendWindowModeData>& ExtendWindowData();
|
||||
|
||||
private:
|
||||
std::map<HWND, ZoneIndexSet> m_windowIndexSet{};
|
||||
std::map<ZoneIndexSet, std::vector<HWND>> m_windowsByIndexSets{};
|
||||
std::unique_ptr<ExtendWindowModeData> m_extendData{}; // Needed for ExtendWindowByDirectionAndPosition
|
||||
|
||||
void InsertWindowIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet);
|
||||
HWND GetNextZoneWindow(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept;
|
||||
};
|
||||
@@ -92,15 +92,15 @@ namespace
|
||||
};
|
||||
}
|
||||
|
||||
bool AddZone(winrt::com_ptr<IZone> zone, ZonesMap& zones) noexcept
|
||||
bool AddZone(Zone zone, ZonesMap& zones) noexcept
|
||||
{
|
||||
auto zoneId = zone->Id();
|
||||
auto zoneId = zone.Id();
|
||||
if (zones.contains(zoneId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
zones[zoneId] = zone;
|
||||
zones.insert({ zoneId, std::move(zone) });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -169,8 +169,8 @@ ZonesMap CalculateGridZones(FancyZonesUtils::Rect workArea, FancyZonesDataTypes:
|
||||
left += col == 0 ? spacing : spacing / 2;
|
||||
right -= maxCol == static_cast<int64_t>(gridLayoutInfo.columns()) - 1 ? spacing : spacing / 2;
|
||||
|
||||
auto zone = MakeZone(RECT{ left, top, right, bottom }, i);
|
||||
if (zone)
|
||||
Zone zone(RECT{ left, top, right, bottom }, i);
|
||||
if (zone.IsValid())
|
||||
{
|
||||
if (!AddZone(zone, zones))
|
||||
{
|
||||
@@ -207,8 +207,8 @@ ZonesMap LayoutConfigurator::Focus(FancyZonesUtils::Rect workArea, int zoneCount
|
||||
|
||||
for (int i = 0; i < zoneCount; i++)
|
||||
{
|
||||
auto zone = MakeZone(focusZoneRect, zones.size());
|
||||
if (zone)
|
||||
Zone zone(focusZoneRect, zones.size());
|
||||
if (zone.IsValid())
|
||||
{
|
||||
if (!AddZone(zone, zones))
|
||||
{
|
||||
@@ -251,8 +251,8 @@ ZonesMap LayoutConfigurator::Rows(FancyZonesUtils::Rect workArea, int zoneCount,
|
||||
right = totalWidth + spacing;
|
||||
bottom = top + (zoneIndex + 1) * totalHeight / zoneCount - zoneIndex * totalHeight / zoneCount;
|
||||
|
||||
auto zone = MakeZone(RECT{ left, top, right, bottom }, zones.size());
|
||||
if (zone)
|
||||
Zone zone(RECT{ left, top, right, bottom }, zones.size());
|
||||
if (zone.IsValid())
|
||||
{
|
||||
if (!AddZone(zone, zones))
|
||||
{
|
||||
@@ -292,8 +292,8 @@ ZonesMap LayoutConfigurator::Columns(FancyZonesUtils::Rect workArea, int zoneCou
|
||||
right = left + (zoneIndex + 1) * totalWidth / zoneCount - zoneIndex * totalWidth / zoneCount;
|
||||
bottom = totalHeight + spacing;
|
||||
|
||||
auto zone = MakeZone(RECT{ left, top, right, bottom }, zones.size());
|
||||
if (zone)
|
||||
Zone zone(RECT{ left, top, right, bottom }, zones.size());
|
||||
if (zone.IsValid())
|
||||
{
|
||||
if (!AddZone(zone, zones))
|
||||
{
|
||||
@@ -404,8 +404,8 @@ ZonesMap LayoutConfigurator::Custom(FancyZonesUtils::Rect workArea, HMONITOR mon
|
||||
DPIAware::Convert(monitor, x, y);
|
||||
DPIAware::Convert(monitor, zoneWidth, zoneHeight);
|
||||
|
||||
auto zone = MakeZone(RECT{ static_cast<long>(x), static_cast<long>(y), static_cast<long>(x + zoneWidth), static_cast<long>(y + zoneHeight) }, zones.size());
|
||||
if (zone)
|
||||
Zone zone(RECT{ static_cast<long>(x), static_cast<long>(y), static_cast<long>(x + zoneWidth), static_cast<long>(y + zoneHeight) }, zones.size());
|
||||
if (zone.IsValid())
|
||||
{
|
||||
if (!AddZone(zone, zones))
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <FancyZonesLib/util.h>
|
||||
|
||||
// Mapping zone id to zone
|
||||
using ZonesMap = std::map<ZoneIndex, winrt::com_ptr<IZone>>;
|
||||
using ZonesMap = std::map<ZoneIndex, Zone>;
|
||||
|
||||
namespace FancyZonesDataTypes
|
||||
{
|
||||
|
||||
@@ -132,10 +132,10 @@ void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const
|
||||
auto workArea = workAreaMap.find(monitor);
|
||||
if (workArea != workAreaMap.end())
|
||||
{
|
||||
const auto zoneSet = workArea->second->ZoneSet();
|
||||
if (zoneSet)
|
||||
const auto& layoutWindows = workArea->second->GetLayoutWindows();
|
||||
if (layoutWindows)
|
||||
{
|
||||
zoneSet->DismissWindow(window);
|
||||
layoutWindows->Dismiss(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ void WindowMoveHandler::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen,
|
||||
}
|
||||
}
|
||||
|
||||
void WindowMoveHandler::MoveSizeEnd(HWND window, POINT const& ptScreen, const std::unordered_map<HMONITOR, std::shared_ptr<WorkArea>>& workAreaMap) noexcept
|
||||
void WindowMoveHandler::MoveSizeEnd(HWND window, const std::unordered_map<HMONITOR, std::shared_ptr<WorkArea>>& workAreaMap) noexcept
|
||||
{
|
||||
if (window != m_draggedWindow)
|
||||
{
|
||||
@@ -235,7 +235,7 @@ void WindowMoveHandler::MoveSizeEnd(HWND window, POINT const& ptScreen, const st
|
||||
}
|
||||
else
|
||||
{
|
||||
workArea->MoveSizeEnd(m_draggedWindow, ptScreen);
|
||||
workArea->MoveSizeEnd(m_draggedWindow);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -261,13 +261,13 @@ void WindowMoveHandler::MoveSizeEnd(HWND window, POINT const& ptScreen, const st
|
||||
if (workArea != workAreaMap.end())
|
||||
{
|
||||
const auto workAreaPtr = workArea->second;
|
||||
const auto zoneSet = workAreaPtr->ZoneSet();
|
||||
if (zoneSet)
|
||||
const auto& layout = workAreaPtr->GetLayout();
|
||||
if (layout)
|
||||
{
|
||||
wil::unique_cotaskmem_string guidString;
|
||||
if (SUCCEEDED_LOG(StringFromCLSID(zoneSet->Id(), &guidString)))
|
||||
auto guidStr = FancyZonesUtils::GuidToString(layout->Id());
|
||||
if (guidStr.has_value())
|
||||
{
|
||||
AppZoneHistory::instance().RemoveAppLastZone(window, workAreaPtr->UniqueId(), guidString.get());
|
||||
AppZoneHistory::instance().RemoveAppLastZone(window, workAreaPtr->UniqueId(), guidStr.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -324,12 +324,10 @@ void WindowMoveHandler::UpdateWindowsPositions(const std::unordered_map<HMONITOR
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& [monitor, workArea] : activeWorkAreas)
|
||||
auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||
if (monitor && activeWorkAreas.contains(monitor))
|
||||
{
|
||||
if (MonitorFromWindow(window, MONITOR_DEFAULTTONULL) == monitor)
|
||||
{
|
||||
workArea->MoveWindowIntoZoneByIndexSet(window, zoneIndexSet);
|
||||
}
|
||||
activeWorkAreas.at(monitor)->MoveWindowIntoZoneByIndexSet(window, zoneIndexSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
|
||||
void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::unordered_map<HMONITOR, std::shared_ptr<WorkArea>>& workAreaMap) noexcept;
|
||||
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map<HMONITOR, std::shared_ptr<WorkArea>>& workAreaMap) noexcept;
|
||||
void MoveSizeEnd(HWND window, POINT const& ptScreen, const std::unordered_map<HMONITOR, std::shared_ptr<WorkArea>>& workAreaMap) noexcept;
|
||||
void MoveSizeEnd(HWND window, const std::unordered_map<HMONITOR, std::shared_ptr<WorkArea>>& workAreaMap) noexcept;
|
||||
|
||||
void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, std::shared_ptr<WorkArea> workArea) noexcept;
|
||||
bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle, std::shared_ptr<WorkArea> workArea) noexcept;
|
||||
|
||||
@@ -127,12 +127,17 @@ HRESULT WorkArea::MoveSizeEnter(HWND window) noexcept
|
||||
m_highlightZone = {};
|
||||
m_initialHighlightZone = {};
|
||||
ShowZonesOverlay();
|
||||
Trace::WorkArea::MoveOrResizeStarted(m_zoneSet);
|
||||
Trace::WorkArea::MoveOrResizeStarted(m_layout.get(), m_layoutWindows.get());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WorkArea::MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool selectManyZones) noexcept
|
||||
{
|
||||
if (!m_layout)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool redraw = false;
|
||||
POINT ptClient = ptScreen;
|
||||
MapWindowPoints(nullptr, m_window, &ptClient, 1);
|
||||
@@ -150,7 +155,7 @@ HRESULT WorkArea::MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool s
|
||||
}
|
||||
else
|
||||
{
|
||||
highlightZone = m_zoneSet->GetCombinedZoneRange(m_initialHighlightZone, highlightZone);
|
||||
highlightZone = m_layout->GetCombinedZoneRange(m_initialHighlightZone, highlightZone);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -167,33 +172,24 @@ HRESULT WorkArea::MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool s
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
if (redraw && m_zonesOverlay)
|
||||
{
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WorkArea::MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
|
||||
HRESULT WorkArea::MoveSizeEnd(HWND window) noexcept
|
||||
{
|
||||
if (m_windowMoveSize != window)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (m_zoneSet)
|
||||
{
|
||||
POINT ptClient = ptScreen;
|
||||
MapWindowPoints(nullptr, m_window, &ptClient, 1);
|
||||
m_zoneSet->MoveWindowIntoZoneByIndexSet(window, m_window, m_highlightZone);
|
||||
MoveWindowIntoZoneByIndexSet(window, m_highlightZone);
|
||||
|
||||
if (!FancyZonesWindowUtils::HasVisibleOwner(window))
|
||||
{
|
||||
SaveWindowProcessToZoneIndex(window);
|
||||
}
|
||||
}
|
||||
Trace::WorkArea::MoveOrResizeEnd(m_zoneSet);
|
||||
Trace::WorkArea::MoveOrResizeEnd(m_layout.get(), m_layoutWindows.get());
|
||||
|
||||
HideZonesOverlay();
|
||||
m_windowMoveSize = nullptr;
|
||||
@@ -207,80 +203,264 @@ void WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) noexcept
|
||||
|
||||
void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet) noexcept
|
||||
{
|
||||
if (m_zoneSet)
|
||||
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty() || indexSet.empty())
|
||||
{
|
||||
m_zoneSet->MoveWindowIntoZoneByIndexSet(window, m_window, indexSet);
|
||||
return;
|
||||
}
|
||||
|
||||
FancyZonesWindowUtils::SaveWindowSizeAndOrigin(window);
|
||||
auto rect = m_layout->GetCombinedZonesRect(indexSet);
|
||||
auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
|
||||
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
|
||||
|
||||
m_layoutWindows->Assign(window, indexSet);
|
||||
FancyZonesWindowProperties::StampZoneIndexProperty(window, indexSet);
|
||||
|
||||
SaveWindowProcessToZoneIndex(window);
|
||||
}
|
||||
|
||||
bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle) noexcept
|
||||
{
|
||||
if (m_zoneSet)
|
||||
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
|
||||
{
|
||||
if (m_zoneSet->MoveWindowIntoZoneByDirectionAndIndex(window, m_window, vkCode, cycle))
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zoneIndexes = m_layoutWindows->GetZoneIndexSetFromWindow(window);
|
||||
auto numZones = m_layout->Zones().size();
|
||||
|
||||
// The window was not assigned to any zone here
|
||||
if (zoneIndexes.size() == 0)
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, vkCode == VK_LEFT ? numZones - 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ZoneIndex oldId = zoneIndexes[0];
|
||||
|
||||
// We reached the edge
|
||||
if ((vkCode == VK_LEFT && oldId == 0) || (vkCode == VK_RIGHT && oldId == numZones - 1))
|
||||
{
|
||||
if (!FancyZonesWindowUtils::HasVisibleOwner(window))
|
||||
if (!cycle)
|
||||
{
|
||||
SaveWindowProcessToZoneIndex(window);
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
if (!FancyZonesWindowUtils::HasVisibleOwner(window))
|
||||
{
|
||||
SaveWindowProcessToZoneIndex(window);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle) noexcept
|
||||
{
|
||||
if (m_zoneSet)
|
||||
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
|
||||
{
|
||||
if (m_zoneSet->MoveWindowIntoZoneByDirectionAndPosition(window, m_window, vkCode, cycle))
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& zones = m_layout->Zones();
|
||||
std::vector<bool> usedZoneIndices(zones.size(), false);
|
||||
auto windowZones = m_layoutWindows->GetZoneIndexSetFromWindow(window);
|
||||
|
||||
for (ZoneIndex id : windowZones)
|
||||
{
|
||||
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]);
|
||||
SaveWindowProcessToZoneIndex(window);
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get());
|
||||
return true;
|
||||
}
|
||||
else if (cycle)
|
||||
{
|
||||
// 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())
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, result);
|
||||
SaveWindowProcessToZoneIndex(window);
|
||||
Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noexcept
|
||||
{
|
||||
if (m_zoneSet)
|
||||
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
|
||||
{
|
||||
if (m_zoneSet->ExtendWindowByDirectionAndPosition(window, m_window, vkCode))
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT windowRect;
|
||||
if (!GetWindowRect(window, &windowRect))
|
||||
{
|
||||
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
|
||||
{
|
||||
for (ZoneIndex idx : appliedZones)
|
||||
{
|
||||
SaveWindowProcessToZoneIndex(window);
|
||||
return true;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
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 };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto deletethis = extendModeData->windowInitialIndexSet[window];
|
||||
extendModeData->windowFinalIndex[window] = targetZone;
|
||||
resultIndexSet = m_layout->GetCombinedZoneRange(extendModeData->windowInitialIndexSet[window], { targetZone });
|
||||
}
|
||||
|
||||
auto rect = m_layout->GetCombinedZonesRect(resultIndexSet);
|
||||
auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
|
||||
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
|
||||
|
||||
m_layoutWindows->Extend(window, resultIndexSet);
|
||||
FancyZonesWindowProperties::StampZoneIndexProperty(window, resultIndexSet);
|
||||
|
||||
SaveWindowProcessToZoneIndex(window);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WorkArea::SaveWindowProcessToZoneIndex(HWND window) noexcept
|
||||
{
|
||||
if (m_zoneSet)
|
||||
if (m_layout && m_layoutWindows)
|
||||
{
|
||||
auto zoneIndexSet = m_zoneSet->GetZoneIndexSetFromWindow(window);
|
||||
auto zoneIndexSet = m_layoutWindows->GetZoneIndexSetFromWindow(window);
|
||||
if (zoneIndexSet.size())
|
||||
{
|
||||
OLECHAR* guidString;
|
||||
if (StringFromCLSID(m_zoneSet->Id(), &guidString) == S_OK)
|
||||
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
|
||||
if (guidStr.has_value())
|
||||
{
|
||||
AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, guidString, zoneIndexSet);
|
||||
AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, guidStr.value(), zoneIndexSet);
|
||||
}
|
||||
|
||||
CoTaskMemFree(guidString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const noexcept
|
||||
{
|
||||
if (m_zoneSet)
|
||||
if (m_layout)
|
||||
{
|
||||
wil::unique_cotaskmem_string zoneSetId;
|
||||
if (SUCCEEDED(StringFromCLSID(m_zoneSet->Id(), &zoneSetId)))
|
||||
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
|
||||
if (guidStr.has_value())
|
||||
{
|
||||
return AppZoneHistory::instance().GetAppLastZoneIndexSet(window, m_uniqueId, zoneSetId.get());
|
||||
return AppZoneHistory::instance().GetAppLastZoneIndexSet(window, m_uniqueId, guidStr.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -297,10 +477,10 @@ ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const noexcept
|
||||
|
||||
void WorkArea::ShowZonesOverlay() noexcept
|
||||
{
|
||||
if (m_window)
|
||||
if (m_window && m_layout)
|
||||
{
|
||||
SetAsTopmostWindow();
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
m_zonesOverlay->Show();
|
||||
}
|
||||
}
|
||||
@@ -324,37 +504,37 @@ void WorkArea::UpdateActiveZoneSet() noexcept
|
||||
AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId);
|
||||
}
|
||||
|
||||
CalculateZoneSet(FancyZonesSettings::settings().overlappingZonesAlgorithm);
|
||||
if (m_window && m_zoneSet)
|
||||
CalculateZoneSet();
|
||||
if (m_window && m_layout)
|
||||
{
|
||||
m_highlightZone.clear();
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
}
|
||||
}
|
||||
|
||||
void WorkArea::CycleTabs(HWND window, bool reverse) noexcept
|
||||
void WorkArea::CycleWindows(HWND window, bool reverse) noexcept
|
||||
{
|
||||
if (m_zoneSet)
|
||||
if (m_layoutWindows)
|
||||
{
|
||||
m_zoneSet->CycleTabs(window, reverse);
|
||||
m_layoutWindows->CycleWindows(window, reverse);
|
||||
}
|
||||
}
|
||||
|
||||
void WorkArea::ClearSelectedZones() noexcept
|
||||
{
|
||||
if (m_highlightZone.size())
|
||||
if (m_highlightZone.size() && m_layout)
|
||||
{
|
||||
m_highlightZone.clear();
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
}
|
||||
}
|
||||
|
||||
void WorkArea::FlashZones() noexcept
|
||||
{
|
||||
if (m_window)
|
||||
if (m_window && m_layout)
|
||||
{
|
||||
SetAsTopmostWindow();
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_zoneSet->GetZones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
|
||||
m_zonesOverlay->Flash();
|
||||
}
|
||||
}
|
||||
@@ -391,36 +571,25 @@ void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId)
|
||||
}
|
||||
}
|
||||
|
||||
CalculateZoneSet(FancyZonesSettings::settings().overlappingZonesAlgorithm);
|
||||
CalculateZoneSet();
|
||||
}
|
||||
|
||||
void WorkArea::CalculateZoneSet(OverlappingZonesAlgorithm overlappingAlgorithm) noexcept
|
||||
void WorkArea::CalculateZoneSet() noexcept
|
||||
{
|
||||
const auto appliedLayout = AppliedLayouts::instance().GetDeviceLayout(m_uniqueId);
|
||||
if (!appliedLayout.has_value())
|
||||
{
|
||||
Logger::error(L"Layout wasn't applied. Can't init zone set");
|
||||
Logger::error(L"Layout wasn't applied. Can't init layout on work area {}x{}", m_workAreaRect.width(), m_workAreaRect.height());
|
||||
return;
|
||||
}
|
||||
|
||||
auto zoneSet = MakeZoneSet(ZoneSetConfig(
|
||||
appliedLayout->uuid,
|
||||
appliedLayout->type,
|
||||
m_monitor,
|
||||
appliedLayout->sensitivityRadius,
|
||||
overlappingAlgorithm));
|
||||
m_layout = std::make_unique<Layout>(appliedLayout.value());
|
||||
m_layout->Init(m_workAreaRect, m_monitor);
|
||||
|
||||
bool showSpacing = appliedLayout->showSpacing;
|
||||
int spacing = showSpacing ? appliedLayout->spacing : 0;
|
||||
int zoneCount = appliedLayout->zoneCount;
|
||||
|
||||
zoneSet->CalculateZones(m_workAreaRect, zoneCount, spacing);
|
||||
UpdateActiveZoneSet(zoneSet.get());
|
||||
}
|
||||
|
||||
void WorkArea::UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept
|
||||
{
|
||||
m_zoneSet.copy_from(zoneSet);
|
||||
if (!m_layoutWindows)
|
||||
{
|
||||
m_layoutWindows = std::make_unique<LayoutAssignedWindows>();
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT WorkArea::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||
@@ -447,10 +616,11 @@ LRESULT WorkArea::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||
|
||||
ZoneIndexSet WorkArea::ZonesFromPoint(POINT pt) noexcept
|
||||
{
|
||||
if (m_zoneSet)
|
||||
if (m_layout)
|
||||
{
|
||||
return m_zoneSet->ZonesFromPoint(pt);
|
||||
return m_layout->ZonesFromPoint(pt);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <FancyZonesLib/FancyZonesDataTypes.h>
|
||||
#include <FancyZonesLib/ZoneSet.h>
|
||||
#include <FancyZonesLib/Layout.h>
|
||||
#include <FancyZonesLib/LayoutAssignedWindows.h>
|
||||
#include <FancyZonesLib/util.h>
|
||||
|
||||
class ZonesOverlay;
|
||||
@@ -55,13 +56,14 @@ public:
|
||||
}
|
||||
|
||||
FancyZonesDataTypes::WorkAreaId UniqueId() const noexcept { return { m_uniqueId }; }
|
||||
IZoneSet* ZoneSet() const noexcept { return m_zoneSet.get(); }
|
||||
const std::unique_ptr<Layout>& GetLayout() const noexcept { return m_layout; }
|
||||
const std::unique_ptr<LayoutAssignedWindows>& GetLayoutWindows() const noexcept { return m_layoutWindows; }
|
||||
|
||||
ZoneIndexSet GetWindowZoneIndexes(HWND window) const noexcept;
|
||||
|
||||
HRESULT MoveSizeEnter(HWND window) noexcept;
|
||||
HRESULT MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool selectManyZones) noexcept;
|
||||
HRESULT MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept;
|
||||
HRESULT MoveSizeEnd(HWND window) noexcept;
|
||||
void MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) noexcept;
|
||||
void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet) noexcept;
|
||||
bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle) noexcept;
|
||||
@@ -76,7 +78,7 @@ public:
|
||||
void FlashZones() noexcept;
|
||||
void ClearSelectedZones() noexcept;
|
||||
|
||||
void CycleTabs(HWND window, bool reverse) noexcept;
|
||||
void CycleWindows(HWND window, bool reverse) noexcept;
|
||||
|
||||
void LogInitializationError();
|
||||
|
||||
@@ -86,8 +88,7 @@ protected:
|
||||
private:
|
||||
bool InitWindow(HINSTANCE hinstance) noexcept;
|
||||
void InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) noexcept;
|
||||
void CalculateZoneSet(OverlappingZonesAlgorithm overlappingAlgorithm) noexcept;
|
||||
void UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept;
|
||||
void CalculateZoneSet() noexcept;
|
||||
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept;
|
||||
ZoneIndexSet ZonesFromPoint(POINT pt) noexcept;
|
||||
void SetAsTopmostWindow() noexcept;
|
||||
@@ -97,7 +98,8 @@ private:
|
||||
const FancyZonesDataTypes::WorkAreaId m_uniqueId;
|
||||
HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area.
|
||||
HWND m_windowMoveSize{};
|
||||
winrt::com_ptr<IZoneSet> m_zoneSet;
|
||||
std::unique_ptr<Layout> m_layout;
|
||||
std::unique_ptr<LayoutAssignedWindows> m_layoutWindows;
|
||||
ZoneIndexSet m_initialHighlightZone;
|
||||
ZoneIndexSet m_highlightZone;
|
||||
WPARAM m_keyLast{};
|
||||
|
||||
@@ -1,54 +1,45 @@
|
||||
#include "pch.h"
|
||||
|
||||
|
||||
#include <Shellscalingapi.h>
|
||||
|
||||
#include <common/display/dpi_aware.h>
|
||||
#include <common/display/monitors.h>
|
||||
#include "Zone.h"
|
||||
#include "Settings.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace
|
||||
Zone::Zone(const RECT& zoneRect, const ZoneIndex zoneIndex) :
|
||||
m_rect(zoneRect),
|
||||
m_index(zoneIndex)
|
||||
{
|
||||
bool ValidateZoneRect(const RECT& rect)
|
||||
{
|
||||
int width = rect.right - rect.left;
|
||||
int height = rect.bottom - rect.top;
|
||||
return rect.left >= ZoneConstants::MAX_NEGATIVE_SPACING &&
|
||||
rect.right >= ZoneConstants::MAX_NEGATIVE_SPACING &&
|
||||
rect.top >= ZoneConstants::MAX_NEGATIVE_SPACING &&
|
||||
rect.bottom >= ZoneConstants::MAX_NEGATIVE_SPACING &&
|
||||
width >= 0 && height >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct Zone : winrt::implements<Zone, IZone>
|
||||
Zone::Zone(const Zone& other) :
|
||||
m_rect(other.m_rect),
|
||||
m_index(other.m_index)
|
||||
{
|
||||
public:
|
||||
Zone(RECT zoneRect, const ZoneIndex zoneId) :
|
||||
m_zoneRect(zoneRect),
|
||||
m_id(zoneId)
|
||||
{
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(RECT) GetZoneRect() const noexcept { return m_zoneRect; }
|
||||
IFACEMETHODIMP_(long) GetZoneArea() const noexcept { return max(m_zoneRect.bottom - m_zoneRect.top, 0) * max(m_zoneRect.right - m_zoneRect.left, 0); }
|
||||
IFACEMETHODIMP_(ZoneIndex) Id() const noexcept { return m_id; }
|
||||
|
||||
private:
|
||||
RECT m_zoneRect{};
|
||||
const ZoneIndex m_id{};
|
||||
};
|
||||
|
||||
winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect, const ZoneIndex zoneId) noexcept
|
||||
{
|
||||
if (ValidateZoneRect(zoneRect) && zoneId >= 0)
|
||||
{
|
||||
return winrt::make_self<Zone>(zoneRect, zoneId);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ZoneIndex Zone::Id() const noexcept
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
bool Zone::IsValid() const noexcept
|
||||
{
|
||||
return m_index >= 0 && isValid();
|
||||
}
|
||||
|
||||
RECT Zone::GetZoneRect() const noexcept
|
||||
{
|
||||
return m_rect;
|
||||
}
|
||||
|
||||
long Zone::GetZoneArea() const noexcept
|
||||
{
|
||||
return max(m_rect.bottom - m_rect.top, 0) * max(m_rect.right - m_rect.left, 0);
|
||||
}
|
||||
|
||||
bool Zone::isValid() const noexcept
|
||||
{
|
||||
int width = m_rect.right - m_rect.left;
|
||||
int height = m_rect.bottom - m_rect.top;
|
||||
return m_rect.left >= ZoneConstants::MAX_NEGATIVE_SPACING &&
|
||||
m_rect.right >= ZoneConstants::MAX_NEGATIVE_SPACING &&
|
||||
m_rect.top >= ZoneConstants::MAX_NEGATIVE_SPACING &&
|
||||
m_rect.bottom >= ZoneConstants::MAX_NEGATIVE_SPACING &&
|
||||
width >= 0 && height >= 0;
|
||||
}
|
||||
@@ -11,20 +11,21 @@ using ZoneIndexSet = std::vector<ZoneIndex>;
|
||||
/**
|
||||
* Class representing one zone inside applied zone layout, which is basically wrapper around rectangle structure.
|
||||
*/
|
||||
interface __declspec(uuid("{8228E934-B6EF-402A-9892-15A1441BF8B0}")) IZone : public IUnknown
|
||||
class Zone
|
||||
{
|
||||
/**
|
||||
* @returns Zone coordinates (top-left and bottom-right corner) represented as RECT structure.
|
||||
*/
|
||||
IFACEMETHOD_(RECT, GetZoneRect)() const = 0;
|
||||
/**
|
||||
* @returns Zone area calculated from zone rect
|
||||
*/
|
||||
IFACEMETHOD_(long, GetZoneArea)() const = 0;
|
||||
/**
|
||||
* @returns Zone identifier.
|
||||
*/
|
||||
IFACEMETHOD_(ZoneIndex, Id)() const = 0;
|
||||
};
|
||||
public:
|
||||
Zone(const RECT& zoneRect, const ZoneIndex zoneIndex);
|
||||
Zone(const Zone& other);
|
||||
~Zone() = default;
|
||||
|
||||
winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect, const ZoneIndex zoneId) noexcept;
|
||||
ZoneIndex Id() const noexcept;
|
||||
bool IsValid() const noexcept;
|
||||
RECT GetZoneRect() const noexcept;
|
||||
long GetZoneArea() const noexcept;
|
||||
|
||||
private:
|
||||
const RECT m_rect;
|
||||
const ZoneIndex m_index;
|
||||
|
||||
bool isValid() const noexcept;
|
||||
};
|
||||
|
||||
@@ -1,770 +0,0 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "ZoneSet.h"
|
||||
|
||||
#include <FancyZonesLib/FancyZonesData/CustomLayouts.h>
|
||||
#include <FancyZonesLib/FancyZonesWindowProperties.h>
|
||||
#include <FancyZonesLib/WindowUtils.h>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
|
||||
using namespace FancyZonesUtils;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int OVERLAPPING_CENTERS_SENSITIVITY = 75;
|
||||
}
|
||||
|
||||
struct ZoneSet : winrt::implements<ZoneSet, IZoneSet>
|
||||
{
|
||||
public:
|
||||
ZoneSet(ZoneSetConfig const& config) :
|
||||
m_config(config)
|
||||
{
|
||||
}
|
||||
|
||||
ZoneSet(ZoneSetConfig const& config, ZonesMap zones) :
|
||||
m_config(config),
|
||||
m_zones(zones)
|
||||
{
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(GUID)
|
||||
Id() const noexcept { return m_config.Id; }
|
||||
IFACEMETHODIMP_(FancyZonesDataTypes::ZoneSetLayoutType)
|
||||
LayoutType() const noexcept { return m_config.LayoutType; }
|
||||
IFACEMETHODIMP_(ZoneIndexSet) ZonesFromPoint(POINT pt) const noexcept;
|
||||
IFACEMETHODIMP_(ZoneIndexSet) GetZoneIndexSetFromWindow(HWND window) const noexcept;
|
||||
IFACEMETHODIMP_(ZonesMap) GetZones()const noexcept override { return m_zones; }
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndex(HWND window, HWND workAreaWindow, ZoneIndex index) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndexSet(HWND window, HWND workAreaWindow, const ZoneIndexSet& indexSet) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
MoveWindowIntoZoneByDirectionAndIndex(HWND window, HWND workAreaWindow, DWORD vkCode, bool cycle) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
MoveWindowIntoZoneByDirectionAndPosition(HWND window, HWND workAreaWindow, DWORD vkCode, bool cycle) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
ExtendWindowByDirectionAndPosition(HWND window, HWND workAreaWindow, DWORD vkCode) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByPoint(HWND window, HWND workAreaWindow, POINT ptClient) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
DismissWindow(HWND window) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
CycleTabs(HWND window, bool reverse) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
CalculateZones(FancyZonesUtils::Rect workArea, int zoneCount, int spacing) noexcept;
|
||||
IFACEMETHODIMP_(bool) IsZoneEmpty(ZoneIndex zoneIndex) const noexcept;
|
||||
IFACEMETHODIMP_(ZoneIndexSet) GetCombinedZoneRange(const ZoneIndexSet& initialZones, const ZoneIndexSet& finalZones) const noexcept;
|
||||
|
||||
private:
|
||||
HWND GetNextTab(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept;
|
||||
void InsertTabIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet);
|
||||
ZoneIndexSet ZoneSelectSubregion(const ZoneIndexSet& capturedZones, POINT pt) const;
|
||||
ZoneIndexSet ZoneSelectClosestCenter(const ZoneIndexSet& capturedZones, POINT pt) const;
|
||||
|
||||
// `compare` should return true if the first argument is a better choice than the second argument.
|
||||
template<class CompareF>
|
||||
ZoneIndexSet ZoneSelectPriority(const ZoneIndexSet& capturedZones, CompareF compare) const;
|
||||
|
||||
ZonesMap m_zones;
|
||||
std::map<HWND, ZoneIndexSet> m_windowIndexSet;
|
||||
std::map<ZoneIndexSet, std::vector<HWND>> m_windowsByIndexSets;
|
||||
|
||||
// Needed for ExtendWindowByDirectionAndPosition
|
||||
std::map<HWND, ZoneIndexSet> m_windowInitialIndexSet;
|
||||
std::map<HWND, ZoneIndex> m_windowFinalIndex;
|
||||
bool m_inExtendWindow = false;
|
||||
|
||||
ZoneSetConfig m_config;
|
||||
};
|
||||
|
||||
IFACEMETHODIMP_(ZoneIndexSet)
|
||||
ZoneSet::ZonesFromPoint(POINT pt) const noexcept
|
||||
{
|
||||
ZoneIndexSet capturedZones;
|
||||
ZoneIndexSet strictlyCapturedZones;
|
||||
for (const auto& [zoneId, zone] : m_zones)
|
||||
{
|
||||
const RECT& zoneRect = zone->GetZoneRect();
|
||||
if (zoneRect.left - m_config.SensitivityRadius <= pt.x && pt.x <= zoneRect.right + m_config.SensitivityRadius &&
|
||||
zoneRect.top - m_config.SensitivityRadius <= pt.y && pt.y <= zoneRect.bottom + m_config.SensitivityRadius)
|
||||
{
|
||||
capturedZones.emplace_back(zoneId);
|
||||
}
|
||||
|
||||
if (zoneRect.left <= pt.x && pt.x < zoneRect.right &&
|
||||
zoneRect.top <= pt.y && pt.y < zoneRect.bottom)
|
||||
{
|
||||
strictlyCapturedZones.emplace_back(zoneId);
|
||||
}
|
||||
}
|
||||
|
||||
// If only one zone is captured, but it's not strictly captured
|
||||
// don't consider it as captured
|
||||
if (capturedZones.size() == 1 && strictlyCapturedZones.size() == 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// If captured zones do not overlap, return all of them
|
||||
// Otherwise, return one of them based on the chosen selection algorithm.
|
||||
bool overlap = false;
|
||||
for (size_t i = 0; i < capturedZones.size(); ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j < capturedZones.size(); ++j)
|
||||
{
|
||||
RECT rectI;
|
||||
RECT rectJ;
|
||||
try
|
||||
{
|
||||
rectI = m_zones.at(capturedZones[i])->GetZoneRect();
|
||||
rectJ = m_zones.at(capturedZones[j])->GetZoneRect();
|
||||
}
|
||||
catch (std::out_of_range)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (max(rectI.top, rectJ.top) + m_config.SensitivityRadius < min(rectI.bottom, rectJ.bottom) &&
|
||||
max(rectI.left, rectJ.left) + m_config.SensitivityRadius < min(rectI.right, rectJ.right))
|
||||
{
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (overlap)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
try
|
||||
{
|
||||
using Algorithm = OverlappingZonesAlgorithm;
|
||||
|
||||
switch (m_config.SelectionAlgorithm)
|
||||
{
|
||||
case Algorithm::Smallest:
|
||||
return ZoneSelectPriority(capturedZones, [&](auto zone1, auto zone2) { return zone1->GetZoneArea() < zone2->GetZoneArea(); });
|
||||
case Algorithm::Largest:
|
||||
return ZoneSelectPriority(capturedZones, [&](auto zone1, auto zone2) { return zone1->GetZoneArea() > zone2->GetZoneArea(); });
|
||||
case Algorithm::Positional:
|
||||
return ZoneSelectSubregion(capturedZones, pt);
|
||||
case Algorithm::ClosestCenter:
|
||||
return ZoneSelectClosestCenter(capturedZones, pt);
|
||||
}
|
||||
}
|
||||
catch (std::out_of_range)
|
||||
{
|
||||
Logger::error("Exception out_of_range was thrown in ZoneSet::ZonesFromPoint");
|
||||
return { capturedZones[0] };
|
||||
}
|
||||
}
|
||||
|
||||
return capturedZones;
|
||||
}
|
||||
|
||||
ZoneIndexSet ZoneSet::GetZoneIndexSetFromWindow(HWND window) const noexcept
|
||||
{
|
||||
auto it = m_windowIndexSet.find(window);
|
||||
if (it == m_windowIndexSet.end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND workAreaWindow, ZoneIndex index) noexcept
|
||||
{
|
||||
MoveWindowIntoZoneByIndexSet(window, workAreaWindow, { index });
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND workAreaWindow, const ZoneIndexSet& zoneIds) noexcept
|
||||
{
|
||||
if (m_zones.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!zoneIds.empty())
|
||||
{
|
||||
Logger::trace(L"Move window into zones {} - {}", zoneIds.front(), zoneIds.back());
|
||||
}
|
||||
|
||||
// Always clear the info related to SelectManyZones if it's not being used
|
||||
if (!m_inExtendWindow)
|
||||
{
|
||||
m_windowFinalIndex.erase(window);
|
||||
m_windowInitialIndexSet.erase(window);
|
||||
}
|
||||
|
||||
auto tabSortKeyWithinZone = FancyZonesWindowProperties::GetTabSortKeyWithinZone(window);
|
||||
DismissWindow(window);
|
||||
|
||||
RECT size;
|
||||
bool sizeEmpty = true;
|
||||
auto& indexSet = m_windowIndexSet[window];
|
||||
|
||||
for (ZoneIndex id : zoneIds)
|
||||
{
|
||||
if (m_zones.contains(id))
|
||||
{
|
||||
const auto& zone = m_zones.at(id);
|
||||
const RECT newSize = zone->GetZoneRect();
|
||||
if (!sizeEmpty)
|
||||
{
|
||||
size.left = min(size.left, newSize.left);
|
||||
size.top = min(size.top, newSize.top);
|
||||
size.right = max(size.right, newSize.right);
|
||||
size.bottom = max(size.bottom, newSize.bottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = newSize;
|
||||
sizeEmpty = false;
|
||||
}
|
||||
|
||||
indexSet.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!sizeEmpty)
|
||||
{
|
||||
FancyZonesWindowUtils::SaveWindowSizeAndOrigin(window);
|
||||
|
||||
auto rect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, size, workAreaWindow);
|
||||
FancyZonesWindowUtils::SizeWindowToRect(window, rect);
|
||||
|
||||
if (FancyZonesSettings::settings().disableRoundCorners)
|
||||
{
|
||||
FancyZonesWindowUtils::DisableRoundCorners(window);
|
||||
}
|
||||
|
||||
FancyZonesWindowProperties::StampZoneIndexProperty(window, indexSet);
|
||||
InsertTabIntoZone(window, tabSortKeyWithinZone, indexSet);
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(bool)
|
||||
ZoneSet::MoveWindowIntoZoneByDirectionAndIndex(HWND window, HWND workAreaWindow, DWORD vkCode, bool cycle) noexcept
|
||||
{
|
||||
if (m_zones.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto indexSet = GetZoneIndexSetFromWindow(window);
|
||||
auto numZones = m_zones.size();
|
||||
|
||||
// The window was not assigned to any zone here
|
||||
if (indexSet.size() == 0)
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, workAreaWindow, vkCode == VK_LEFT ? numZones - 1 : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
ZoneIndex oldId = indexSet[0];
|
||||
|
||||
// We reached the edge
|
||||
if ((vkCode == VK_LEFT && oldId == 0) || (vkCode == VK_RIGHT && oldId == numZones - 1))
|
||||
{
|
||||
if (!cycle)
|
||||
{
|
||||
MoveWindowIntoZoneByIndexSet(window, workAreaWindow, {});
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, workAreaWindow, vkCode == VK_LEFT ? numZones - 1 : 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't reach the edge
|
||||
if (vkCode == VK_LEFT)
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, workAreaWindow, oldId - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, workAreaWindow, oldId + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(bool)
|
||||
ZoneSet::MoveWindowIntoZoneByDirectionAndPosition(HWND window, HWND workAreaWindow, DWORD vkCode, bool cycle) noexcept
|
||||
{
|
||||
if (m_zones.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<bool> usedZoneIndices(m_zones.size(), false);
|
||||
for (ZoneIndex id : GetZoneIndexSetFromWindow(window))
|
||||
{
|
||||
usedZoneIndices[id] = true;
|
||||
}
|
||||
|
||||
std::vector<RECT> zoneRects;
|
||||
ZoneIndexSet freeZoneIndices;
|
||||
|
||||
for (const auto& [zoneId, zone] : m_zones)
|
||||
{
|
||||
if (!usedZoneIndices[zoneId])
|
||||
{
|
||||
zoneRects.emplace_back(m_zones[zoneId]->GetZoneRect());
|
||||
freeZoneIndices.emplace_back(zoneId);
|
||||
}
|
||||
}
|
||||
|
||||
RECT windowRect, workAreaRect;
|
||||
if (GetWindowRect(window, &windowRect) && GetWindowRect(workAreaWindow, &workAreaRect))
|
||||
{
|
||||
// Move to coordinates relative to windowZone
|
||||
windowRect.top -= workAreaRect.top;
|
||||
windowRect.bottom -= workAreaRect.top;
|
||||
windowRect.left -= workAreaRect.left;
|
||||
windowRect.right -= workAreaRect.left;
|
||||
|
||||
auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
if (result < zoneRects.size())
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, workAreaWindow, freeZoneIndices[result]);
|
||||
return true;
|
||||
}
|
||||
else if (cycle)
|
||||
{
|
||||
// Try again from the position off the screen in the opposite direction to vkCode
|
||||
// Consider all zones as available
|
||||
zoneRects.resize(m_zones.size());
|
||||
std::transform(m_zones.begin(), m_zones.end(), zoneRects.begin(), [](auto zone) { return zone.second->GetZoneRect(); });
|
||||
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, workAreaRect, vkCode);
|
||||
result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
|
||||
if (result < zoneRects.size())
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, workAreaWindow, result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error(L"GetWindowRect failed, {}", get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(bool)
|
||||
ZoneSet::ExtendWindowByDirectionAndPosition(HWND window, HWND workAreaWindow, DWORD vkCode) noexcept
|
||||
{
|
||||
if (m_zones.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT windowRect, windowZoneRect;
|
||||
if (GetWindowRect(window, &windowRect) && GetWindowRect(workAreaWindow, &windowZoneRect))
|
||||
{
|
||||
auto oldZones = GetZoneIndexSetFromWindow(window);
|
||||
std::vector<bool> usedZoneIndices(m_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 = m_windowFinalIndex.find(window);
|
||||
if (finalIndexIt != m_windowFinalIndex.end())
|
||||
{
|
||||
usedZoneIndices[finalIndexIt->second] = true;
|
||||
windowRect = m_zones[finalIndexIt->second]->GetZoneRect();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ZoneIndex idx : oldZones)
|
||||
{
|
||||
usedZoneIndices[idx] = true;
|
||||
}
|
||||
// Move to coordinates relative to windowZone
|
||||
windowRect.top -= windowZoneRect.top;
|
||||
windowRect.bottom -= windowZoneRect.top;
|
||||
windowRect.left -= windowZoneRect.left;
|
||||
windowRect.right -= windowZoneRect.left;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_zones.size(); i++)
|
||||
{
|
||||
if (!usedZoneIndices[i])
|
||||
{
|
||||
zoneRects.emplace_back(m_zones[i]->GetZoneRect());
|
||||
freeZoneIndices.emplace_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
|
||||
if (result < zoneRects.size())
|
||||
{
|
||||
ZoneIndex targetZone = freeZoneIndices[result];
|
||||
ZoneIndexSet resultIndexSet;
|
||||
|
||||
// First time with selectManyZones = true for this window?
|
||||
if (finalIndexIt == m_windowFinalIndex.end())
|
||||
{
|
||||
// Already zoned?
|
||||
if (oldZones.size())
|
||||
{
|
||||
m_windowInitialIndexSet[window] = oldZones;
|
||||
m_windowFinalIndex[window] = targetZone;
|
||||
resultIndexSet = GetCombinedZoneRange(oldZones, { targetZone });
|
||||
}
|
||||
else
|
||||
{
|
||||
m_windowInitialIndexSet[window] = { targetZone };
|
||||
m_windowFinalIndex[window] = targetZone;
|
||||
resultIndexSet = { targetZone };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto deletethis = m_windowInitialIndexSet[window];
|
||||
m_windowFinalIndex[window] = targetZone;
|
||||
resultIndexSet = GetCombinedZoneRange(m_windowInitialIndexSet[window], { targetZone });
|
||||
}
|
||||
|
||||
m_inExtendWindow = true;
|
||||
MoveWindowIntoZoneByIndexSet(window, workAreaWindow, resultIndexSet);
|
||||
m_inExtendWindow = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error(L"GetWindowRect failed, {}", get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneSet::MoveWindowIntoZoneByPoint(HWND window, HWND workAreaWindow, POINT ptClient) noexcept
|
||||
{
|
||||
const auto& zones = ZonesFromPoint(ptClient);
|
||||
MoveWindowIntoZoneByIndexSet(window, workAreaWindow, zones);
|
||||
}
|
||||
|
||||
void ZoneSet::DismissWindow(HWND window) noexcept
|
||||
{
|
||||
auto& indexSet = m_windowIndexSet[window];
|
||||
if (!indexSet.empty())
|
||||
{
|
||||
auto& windows = m_windowsByIndexSets[indexSet];
|
||||
windows.erase(find(begin(windows), end(windows), window));
|
||||
if (windows.empty())
|
||||
{
|
||||
m_windowsByIndexSets.erase(indexSet);
|
||||
}
|
||||
|
||||
indexSet.clear();
|
||||
}
|
||||
|
||||
FancyZonesWindowProperties::SetTabSortKeyWithinZone(window, std::nullopt);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneSet::CycleTabs(HWND window, bool reverse) noexcept
|
||||
{
|
||||
auto indexSet = GetZoneIndexSetFromWindow(window);
|
||||
|
||||
// Do nothing in case the window is not recognized
|
||||
if (indexSet.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto next = GetNextTab(indexSet, window, reverse);
|
||||
|
||||
// Determine whether the window still exists
|
||||
if (!IsWindow(next))
|
||||
{
|
||||
// Dismiss the encountered window since it was probably closed
|
||||
DismissWindow(next);
|
||||
continue;
|
||||
}
|
||||
|
||||
FancyZonesWindowUtils::SwitchToWindow(next);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HWND ZoneSet::GetNextTab(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept
|
||||
{
|
||||
const auto& tabs = m_windowsByIndexSets[indexSet];
|
||||
auto tabIt = std::find(tabs.begin(), tabs.end(), current);
|
||||
if (!reverse)
|
||||
{
|
||||
++tabIt;
|
||||
return tabIt == tabs.end() ? tabs.front() : *tabIt;
|
||||
}
|
||||
else
|
||||
{
|
||||
return tabIt == tabs.begin() ? tabs.back() : *(--tabIt);
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneSet::InsertTabIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet)
|
||||
{
|
||||
if (tabSortKeyWithinZone.has_value())
|
||||
{
|
||||
// Insert the tab using the provided sort key
|
||||
auto predicate = [tabSortKeyWithinZone](HWND tab) {
|
||||
auto currentTabSortKeyWithinZone = FancyZonesWindowProperties::GetTabSortKeyWithinZone(tab);
|
||||
if (currentTabSortKeyWithinZone.has_value())
|
||||
{
|
||||
return currentTabSortKeyWithinZone.value() > tabSortKeyWithinZone;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto position = std::find_if(m_windowsByIndexSets[indexSet].begin(), m_windowsByIndexSets[indexSet].end(), predicate);
|
||||
m_windowsByIndexSets[indexSet].insert(position, window);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert the tab at the end
|
||||
tabSortKeyWithinZone = 0;
|
||||
if (!m_windowsByIndexSets[indexSet].empty())
|
||||
{
|
||||
auto prevTab = m_windowsByIndexSets[indexSet].back();
|
||||
auto prevTabSortKeyWithinZone = FancyZonesWindowProperties::GetTabSortKeyWithinZone(prevTab);
|
||||
if (prevTabSortKeyWithinZone.has_value())
|
||||
{
|
||||
tabSortKeyWithinZone = prevTabSortKeyWithinZone.value() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_windowsByIndexSets[indexSet].push_back(window);
|
||||
}
|
||||
|
||||
FancyZonesWindowProperties::SetTabSortKeyWithinZone(window, tabSortKeyWithinZone);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(bool)
|
||||
ZoneSet::CalculateZones(FancyZonesUtils::Rect workAreaRect, int zoneCount, int spacing) noexcept
|
||||
{
|
||||
Rect workArea(workAreaRect);
|
||||
//invalid work area
|
||||
if (workArea.width() == 0 || workArea.height() == 0)
|
||||
{
|
||||
Logger::error(L"CalculateZones: invalid work area");
|
||||
return false;
|
||||
}
|
||||
|
||||
//invalid zoneCount, may cause division by zero
|
||||
if (zoneCount <= 0 && m_config.LayoutType != FancyZonesDataTypes::ZoneSetLayoutType::Custom)
|
||||
{
|
||||
Logger::error(L"CalculateZones: invalid zone count");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (m_config.LayoutType)
|
||||
{
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::Focus:
|
||||
m_zones = LayoutConfigurator::Focus(workArea, zoneCount);
|
||||
break;
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::Columns:
|
||||
m_zones = LayoutConfigurator::Columns(workArea, zoneCount, spacing);
|
||||
break;
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::Rows:
|
||||
m_zones = LayoutConfigurator::Rows(workArea, zoneCount, spacing);
|
||||
break;
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::Grid:
|
||||
m_zones = LayoutConfigurator::Grid(workArea, zoneCount, spacing);
|
||||
break;
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid:
|
||||
m_zones = LayoutConfigurator::PriorityGrid(workArea, zoneCount, spacing);
|
||||
break;
|
||||
case FancyZonesDataTypes::ZoneSetLayoutType::Custom:
|
||||
{
|
||||
const auto zoneSetSearchResult = CustomLayouts::instance().GetCustomLayoutData(m_config.Id);
|
||||
if (zoneSetSearchResult.has_value())
|
||||
{
|
||||
m_zones = LayoutConfigurator::Custom(workArea, m_config.Monitor, zoneSetSearchResult.value(), spacing);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error(L"Custom layout not found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return m_zones.size() == zoneCount;
|
||||
}
|
||||
|
||||
bool ZoneSet::IsZoneEmpty(ZoneIndex zoneIndex) const noexcept
|
||||
{
|
||||
for (auto& [window, zones] : m_windowIndexSet)
|
||||
{
|
||||
if (find(begin(zones), end(zones), zoneIndex) != end(zones))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ZoneIndexSet ZoneSet::GetCombinedZoneRange(const ZoneIndexSet& initialZones, const ZoneIndexSet& finalZones) const noexcept
|
||||
{
|
||||
ZoneIndexSet combinedZones, result;
|
||||
std::set_union(begin(initialZones), end(initialZones), begin(finalZones), end(finalZones), std::back_inserter(combinedZones));
|
||||
|
||||
RECT boundingRect;
|
||||
bool boundingRectEmpty = true;
|
||||
|
||||
for (ZoneIndex zoneId : combinedZones)
|
||||
{
|
||||
if (m_zones.contains(zoneId))
|
||||
{
|
||||
const RECT rect = m_zones.at(zoneId)->GetZoneRect();
|
||||
if (boundingRectEmpty)
|
||||
{
|
||||
boundingRect = rect;
|
||||
boundingRectEmpty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
boundingRect.left = min(boundingRect.left, rect.left);
|
||||
boundingRect.top = min(boundingRect.top, rect.top);
|
||||
boundingRect.right = max(boundingRect.right, rect.right);
|
||||
boundingRect.bottom = max(boundingRect.bottom, rect.bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!boundingRectEmpty)
|
||||
{
|
||||
for (const auto& [zoneId, zone] : m_zones)
|
||||
{
|
||||
const RECT rect = zone->GetZoneRect();
|
||||
if (boundingRect.left <= rect.left && rect.right <= boundingRect.right &&
|
||||
boundingRect.top <= rect.top && rect.bottom <= boundingRect.bottom)
|
||||
{
|
||||
result.push_back(zoneId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ZoneIndexSet ZoneSet::ZoneSelectSubregion(const ZoneIndexSet& capturedZones, POINT pt) const
|
||||
{
|
||||
auto expand = [&](RECT& rect) {
|
||||
rect.top -= m_config.SensitivityRadius / 2;
|
||||
rect.bottom += m_config.SensitivityRadius / 2;
|
||||
rect.left -= m_config.SensitivityRadius / 2;
|
||||
rect.right += m_config.SensitivityRadius / 2;
|
||||
};
|
||||
|
||||
// Compute the overlapped rectangle.
|
||||
RECT overlap = m_zones.at(capturedZones[0])->GetZoneRect();
|
||||
expand(overlap);
|
||||
|
||||
for (size_t i = 1; i < capturedZones.size(); ++i)
|
||||
{
|
||||
RECT current = m_zones.at(capturedZones[i])->GetZoneRect();
|
||||
expand(current);
|
||||
|
||||
overlap.top = max(overlap.top, current.top);
|
||||
overlap.left = max(overlap.left, current.left);
|
||||
overlap.bottom = min(overlap.bottom, current.bottom);
|
||||
overlap.right = min(overlap.right, current.right);
|
||||
}
|
||||
|
||||
// Avoid division by zero
|
||||
int width = max(overlap.right - overlap.left, 1);
|
||||
int height = max(overlap.bottom - overlap.top, 1);
|
||||
|
||||
bool verticalSplit = height > width;
|
||||
ZoneIndex zoneIndex;
|
||||
|
||||
if (verticalSplit)
|
||||
{
|
||||
zoneIndex = (static_cast<int64_t>(pt.y) - overlap.top) * capturedZones.size() / height;
|
||||
}
|
||||
else
|
||||
{
|
||||
zoneIndex = (static_cast<int64_t>(pt.x) - overlap.left) * capturedZones.size() / width;
|
||||
}
|
||||
|
||||
zoneIndex = std::clamp(zoneIndex, ZoneIndex(0), static_cast<ZoneIndex>(capturedZones.size()) - 1);
|
||||
|
||||
return { capturedZones[zoneIndex] };
|
||||
}
|
||||
|
||||
ZoneIndexSet ZoneSet::ZoneSelectClosestCenter(const ZoneIndexSet& capturedZones, POINT pt) const
|
||||
{
|
||||
auto getCenter = [](auto zone) {
|
||||
RECT rect = zone->GetZoneRect();
|
||||
return POINT{ (rect.right + rect.left) / 2, (rect.top + rect.bottom) / 2 };
|
||||
};
|
||||
auto pointDifference = [](POINT pt1, POINT pt2) {
|
||||
return (pt1.x - pt2.x) * (pt1.x - pt2.x) + (pt1.y - pt2.y) * (pt1.y - pt2.y);
|
||||
};
|
||||
auto distanceFromCenter = [&](auto zone) {
|
||||
POINT center = getCenter(zone);
|
||||
return pointDifference(center, pt);
|
||||
};
|
||||
auto closerToCenter = [&](auto zone1, auto zone2) {
|
||||
if (pointDifference(getCenter(zone1), getCenter(zone2)) > OVERLAPPING_CENTERS_SENSITIVITY)
|
||||
{
|
||||
return distanceFromCenter(zone1) < distanceFromCenter(zone2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return zone1->GetZoneArea() < zone2->GetZoneArea();
|
||||
};
|
||||
};
|
||||
return ZoneSelectPriority(capturedZones, closerToCenter);
|
||||
}
|
||||
|
||||
template<class CompareF>
|
||||
ZoneIndexSet ZoneSet::ZoneSelectPriority(const ZoneIndexSet& capturedZones, CompareF compare) const
|
||||
{
|
||||
size_t chosen = 0;
|
||||
|
||||
for (size_t i = 1; i < capturedZones.size(); ++i)
|
||||
{
|
||||
if (compare(m_zones.at(capturedZones[i]), m_zones.at(capturedZones[chosen])))
|
||||
{
|
||||
chosen = i;
|
||||
}
|
||||
}
|
||||
|
||||
return { capturedZones[chosen] };
|
||||
}
|
||||
|
||||
winrt::com_ptr<IZoneSet> MakeZoneSet(ZoneSetConfig const& config) noexcept
|
||||
{
|
||||
return winrt::make_self<ZoneSet>(config);
|
||||
}
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <FancyZonesLib/LayoutConfigurator.h>
|
||||
#include "Settings.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace FancyZonesDataTypes
|
||||
{
|
||||
enum class ZoneSetLayoutType;
|
||||
}
|
||||
/**
|
||||
* Class representing single zone layout. ZoneSet is responsible for actual calculation of rectangle coordinates
|
||||
* (whether is grid or canvas layout) and moving windows through them.
|
||||
*/
|
||||
interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet : public IUnknown
|
||||
{
|
||||
/**
|
||||
* @returns Unique identifier of zone layout.
|
||||
*/
|
||||
IFACEMETHOD_(GUID, Id)() const = 0;
|
||||
/**
|
||||
* @returns Type of the zone layout. Layout type can be focus, columns, rows, grid, priority grid or custom.
|
||||
*/
|
||||
IFACEMETHOD_(FancyZonesDataTypes::ZoneSetLayoutType, LayoutType)() const = 0;
|
||||
/**
|
||||
* Get zones from cursor coordinates.
|
||||
*
|
||||
* @param pt Cursor coordinates.
|
||||
* @returns Vector of indices, corresponding to the current set of zones - the zones considered active.
|
||||
*/
|
||||
IFACEMETHOD_(ZoneIndexSet, ZonesFromPoint)(POINT pt) const = 0;
|
||||
/**
|
||||
* Get index set of the zones to which the window was assigned.
|
||||
*
|
||||
* @param window Handle of the window.
|
||||
* @returns A vector of ZoneIndex, 0-based index set.
|
||||
*/
|
||||
IFACEMETHOD_(ZoneIndexSet, GetZoneIndexSetFromWindow)(HWND window) const = 0;
|
||||
/**
|
||||
* @returns Array of zone objects (defining coordinates of the zone) inside this zone layout.
|
||||
*/
|
||||
IFACEMETHOD_(ZonesMap, GetZones) () const = 0;
|
||||
/**
|
||||
* Assign window to the zone based on zone index inside zone layout.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param workAreaWindow The m_window of a WorkArea, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param index Zone index within zone layout.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND workAreaWindow, ZoneIndex index) = 0;
|
||||
/**
|
||||
* Assign window to the zones based on the set of zone indices inside zone layout.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param workAreaWindow The m_window of a WorkArea, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param indexSet The set of zone indices within zone layout.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, HWND workAreaWindow, const ZoneIndexSet& indexSet) = 0;
|
||||
/**
|
||||
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow), based on zone index numbers,
|
||||
* not their on-screen position.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param workAreaWindow The m_window of a WorkArea, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param vkCode Pressed arrow key.
|
||||
* @param cycle Whether we should move window to the first zone if we reached last zone in layout.
|
||||
*
|
||||
* @returns Boolean which is always true if cycle argument is set, otherwise indicating if there is more
|
||||
* zones left in the zone layout in which window can move.
|
||||
*/
|
||||
IFACEMETHOD_(bool, MoveWindowIntoZoneByDirectionAndIndex)(HWND window, HWND workAreaWindow, DWORD vkCode, bool cycle) = 0;
|
||||
/**
|
||||
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT/UP/DOWN arrow), based on
|
||||
* their on-screen position.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param workAreaWindow The m_window of a WorkArea, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param vkCode Pressed arrow key.
|
||||
* @param cycle Whether we should move window to the first zone if we reached last zone in layout.
|
||||
*
|
||||
* @returns Boolean which is always true if cycle argument is set, otherwise indicating if there is more
|
||||
* zones left in the zone layout in which window can move.
|
||||
*/
|
||||
IFACEMETHOD_(bool, MoveWindowIntoZoneByDirectionAndPosition)
|
||||
(HWND window, HWND workAreaWindow, DWORD vkCode, bool cycle) = 0;
|
||||
/**
|
||||
* Extend or shrink the window to an adjacent zone based on direction (using CTRL+WIN+ALT + LEFT/RIGHT/UP/DOWN arrow), based on
|
||||
* their on-screen position.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param workAreaWindow The m_window of a WorkArea, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param vkCode Pressed arrow key.
|
||||
*
|
||||
* @returns Boolean indicating whether the window was rezoned. False could be returned when there are no more
|
||||
* zones available in the given direction.
|
||||
*/
|
||||
IFACEMETHOD_(bool, ExtendWindowByDirectionAndPosition)
|
||||
(HWND window, HWND workAreaWindow, DWORD vkCode) = 0;
|
||||
/**
|
||||
* Assign window to the zone based on cursor coordinates.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param workAreaWindow The m_window of a WorkArea, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param pt Cursor coordinates.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByPoint)
|
||||
(HWND window, HWND workAreaWindow, POINT ptClient) = 0;
|
||||
/**
|
||||
* Dismiss window from zone.
|
||||
*
|
||||
* @param window Handle of window which should be dismissed from zone.
|
||||
*/
|
||||
IFACEMETHOD_(void, DismissWindow)
|
||||
(HWND window) = 0;
|
||||
/**
|
||||
* Cycle through tabs in the zone that the window is in.
|
||||
*
|
||||
* @param window Handle of window which is cycled from (the current tab).
|
||||
* @param reverse Whether to cycle in reverse order (to the previous tab) or to move to the next tab.
|
||||
*/
|
||||
IFACEMETHOD_(void, CycleTabs)
|
||||
(HWND window, bool reverse) = 0;
|
||||
/**
|
||||
* Calculate zone coordinates within zone layout based on number of zones and spacing.
|
||||
*
|
||||
* @param workAreaRect The rectangular area on the screen on which the zone layout is applied.
|
||||
* @param zoneCount Number of zones inside zone layout.
|
||||
* @param spacing Spacing between zones in pixels.
|
||||
*
|
||||
* @returns Boolean indicating if calculation was successful.
|
||||
*/
|
||||
IFACEMETHOD_(bool, CalculateZones)(FancyZonesUtils::Rect workAreaRect, int zoneCount, int spacing) = 0;
|
||||
/**
|
||||
* Check if the zone with the specified index is empty. Returns true if the zone with passed zoneIndex does not exist.
|
||||
*
|
||||
* @param zoneIndex The index of of the zone within this zone set.
|
||||
*
|
||||
* @returns Boolean indicating whether the zone is empty.
|
||||
*/
|
||||
IFACEMETHOD_(bool, IsZoneEmpty)(ZoneIndex zoneIndex) const = 0;
|
||||
/**
|
||||
* Returns all zones spanned by the minimum bounding rectangle containing the two given zone index sets.
|
||||
*
|
||||
* @param initialZones The indices of the first chosen zone (the anchor).
|
||||
* @param finalZones The indices of the last chosen zone (the current window position).
|
||||
*
|
||||
* @returns A vector indicating describing the chosen zone index set.
|
||||
*/
|
||||
IFACEMETHOD_(ZoneIndexSet, GetCombinedZoneRange)(const ZoneIndexSet& initialZones, const ZoneIndexSet& finalZones) const = 0;
|
||||
};
|
||||
|
||||
struct ZoneSetConfig
|
||||
{
|
||||
ZoneSetConfig(
|
||||
GUID id,
|
||||
FancyZonesDataTypes::ZoneSetLayoutType layoutType,
|
||||
HMONITOR monitor,
|
||||
int sensitivityRadius,
|
||||
OverlappingZonesAlgorithm selectionAlgorithm = {}) noexcept :
|
||||
Id(id),
|
||||
LayoutType(layoutType),
|
||||
Monitor(monitor),
|
||||
SensitivityRadius(sensitivityRadius),
|
||||
SelectionAlgorithm(selectionAlgorithm)
|
||||
{
|
||||
}
|
||||
|
||||
GUID Id{};
|
||||
FancyZonesDataTypes::ZoneSetLayoutType LayoutType{};
|
||||
HMONITOR Monitor{};
|
||||
int SensitivityRadius;
|
||||
OverlappingZonesAlgorithm SelectionAlgorithm = OverlappingZonesAlgorithm::Smallest;
|
||||
};
|
||||
|
||||
winrt::com_ptr<IZoneSet> MakeZoneSet(ZoneSetConfig const& config) noexcept;
|
||||
@@ -303,19 +303,14 @@ void ZonesOverlay::DrawActiveZoneSet(const ZonesMap& zones,
|
||||
// First draw the inactive zones
|
||||
for (const auto& [zoneId, zone] : zones)
|
||||
{
|
||||
if (!zone)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isHighlighted[zoneId])
|
||||
{
|
||||
DrawableRect drawableRect{
|
||||
.rect = ConvertRect(zone->GetZoneRect()),
|
||||
.rect = ConvertRect(zone.GetZoneRect()),
|
||||
.borderColor = borderColor,
|
||||
.fillColor = inactiveColor,
|
||||
.textColor = numberColor,
|
||||
.id = zone->Id(),
|
||||
.id = zone.Id(),
|
||||
.showText = showZoneText
|
||||
};
|
||||
|
||||
@@ -326,19 +321,14 @@ void ZonesOverlay::DrawActiveZoneSet(const ZonesMap& zones,
|
||||
// Draw the active zones on top of the inactive zones
|
||||
for (const auto& [zoneId, zone] : zones)
|
||||
{
|
||||
if (!zone)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isHighlighted[zoneId])
|
||||
{
|
||||
DrawableRect drawableRect{
|
||||
.rect = ConvertRect(zone->GetZoneRect()),
|
||||
.rect = ConvertRect(zone.GetZoneRect()),
|
||||
.borderColor = borderColor,
|
||||
.fillColor = highlightColor,
|
||||
.textColor = numberColor,
|
||||
.id = zone->Id(),
|
||||
.id = zone.Id(),
|
||||
.showText = showZoneText
|
||||
};
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
#include "util.h"
|
||||
#include "Zone.h"
|
||||
#include "ZoneSet.h"
|
||||
#include "FancyZones.h"
|
||||
#include "Colors.h"
|
||||
#include "LayoutConfigurator.h"
|
||||
|
||||
class ZonesOverlay
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "pch.h"
|
||||
#include "trace.h"
|
||||
#include "FancyZonesLib/ZoneSet.h"
|
||||
#include "FancyZonesLib/Layout.h"
|
||||
#include "FancyZonesLib/LayoutAssignedWindows.h"
|
||||
#include "FancyZonesLib/Settings.h"
|
||||
#include "FancyZonesData/AppZoneHistory.h"
|
||||
#include "FancyZonesLib/FancyZonesData/AppliedLayouts.h"
|
||||
@@ -89,17 +90,17 @@ struct ZoneSetInfo
|
||||
};
|
||||
|
||||
|
||||
ZoneSetInfo GetZoneSetInfo(_In_opt_ IZoneSet* set) noexcept
|
||||
ZoneSetInfo GetZoneSetInfo(_In_opt_ Layout* layout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept
|
||||
{
|
||||
ZoneSetInfo info;
|
||||
if (set)
|
||||
if (layout && layoutWindows)
|
||||
{
|
||||
auto zones = set->GetZones();
|
||||
auto zones = layout->Zones();
|
||||
info.NumberOfZones = zones.size();
|
||||
info.NumberOfWindows = 0;
|
||||
for (int i = 0; i < static_cast<int>(zones.size()); i++)
|
||||
{
|
||||
if (!set->IsZoneEmpty(i))
|
||||
if (!layoutWindows->IsZoneEmpty(i))
|
||||
{
|
||||
info.NumberOfWindows++;
|
||||
}
|
||||
@@ -108,11 +109,6 @@ ZoneSetInfo GetZoneSetInfo(_In_opt_ IZoneSet* set) noexcept
|
||||
return info;
|
||||
}
|
||||
|
||||
ZoneSetInfo GetZoneSetInfo(_In_opt_ winrt::com_ptr<IZoneSet> set) noexcept
|
||||
{
|
||||
return GetZoneSetInfo(set.get());
|
||||
}
|
||||
|
||||
void Trace::RegisterProvider() noexcept
|
||||
{
|
||||
TraceLoggingRegister(g_hProvider);
|
||||
@@ -263,28 +259,28 @@ void Trace::FancyZones::QuickLayoutSwitched(bool shortcutUsed) noexcept
|
||||
TraceLoggingBoolean(shortcutUsed, QuickLayoutSwitchedWithShortcutUsed));
|
||||
}
|
||||
|
||||
void Trace::FancyZones::SnapNewWindowIntoZone(IZoneSet* activeSet) noexcept
|
||||
void Trace::FancyZones::SnapNewWindowIntoZone(Layout* activeLayout, LayoutAssignedWindows* layoutWindows) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeSet);
|
||||
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
EventSnapNewWindowIntoZone,
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeSet), ActiveSetKey),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeLayout), ActiveSetKey),
|
||||
TraceLoggingValue(zoneInfo.NumberOfZones, NumberOfZonesKey),
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
|
||||
}
|
||||
|
||||
void Trace::FancyZones::KeyboardSnapWindowToZone(IZoneSet* activeSet) noexcept
|
||||
void Trace::FancyZones::KeyboardSnapWindowToZone(Layout* activeLayout, LayoutAssignedWindows* layoutWindows) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeSet);
|
||||
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
EventKeyboardSnapWindowToZone,
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeSet), ActiveSetKey),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeLayout), ActiveSetKey),
|
||||
TraceLoggingValue(zoneInfo.NumberOfZones, NumberOfZonesKey),
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
|
||||
}
|
||||
@@ -361,41 +357,41 @@ void Trace::WorkArea::KeyUp(WPARAM wParam) noexcept
|
||||
TraceLoggingValue(wParam, KeyboardValueKey));
|
||||
}
|
||||
|
||||
void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ winrt::com_ptr<IZoneSet> activeSet) noexcept
|
||||
void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeSet);
|
||||
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
EventMoveOrResizeStartedKey,
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeSet.get()), ActiveSetKey),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeLayout), ActiveSetKey),
|
||||
TraceLoggingValue(zoneInfo.NumberOfZones, NumberOfZonesKey),
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
|
||||
}
|
||||
|
||||
void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ winrt::com_ptr<IZoneSet> activeSet) noexcept
|
||||
void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeSet);
|
||||
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
EventMoveOrResizeEndedKey,
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeSet.get()), ActiveSetKey),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeLayout), ActiveSetKey),
|
||||
TraceLoggingValue(zoneInfo.NumberOfZones, NumberOfZonesKey),
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
|
||||
}
|
||||
|
||||
void Trace::WorkArea::CycleActiveZoneSet(_In_opt_ winrt::com_ptr<IZoneSet> activeSet, InputMode mode) noexcept
|
||||
void Trace::WorkArea::CycleActiveZoneSet(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows, InputMode mode) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeSet);
|
||||
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
EventCycleActiveZoneSetKey,
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeSet.get()), ActiveSetKey),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeLayout), ActiveSetKey),
|
||||
TraceLoggingValue(zoneInfo.NumberOfZones, NumberOfZonesKey),
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey),
|
||||
TraceLoggingValue(static_cast<int>(mode), InputModeKey));
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
struct Settings;
|
||||
interface IZoneSet;
|
||||
class Layout;
|
||||
class LayoutAssignedWindows;
|
||||
|
||||
class Trace
|
||||
{
|
||||
@@ -18,8 +19,8 @@ public:
|
||||
static void EditorLaunched(int value) noexcept;
|
||||
static void Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept;
|
||||
static void QuickLayoutSwitched(bool shortcutUsed) noexcept;
|
||||
static void SnapNewWindowIntoZone(IZoneSet* activeSet) noexcept;
|
||||
static void KeyboardSnapWindowToZone(IZoneSet* activeSet) noexcept;
|
||||
static void SnapNewWindowIntoZone(Layout* activeaLayout, LayoutAssignedWindows* layoutWindows) noexcept;
|
||||
static void KeyboardSnapWindowToZone(Layout* activeaLayout, LayoutAssignedWindows* layoutWindows) noexcept;
|
||||
};
|
||||
|
||||
static void SettingsTelemetry(const Settings& settings) noexcept;
|
||||
@@ -35,8 +36,8 @@ public:
|
||||
};
|
||||
|
||||
static void KeyUp(WPARAM wparam) noexcept;
|
||||
static void MoveOrResizeStarted(_In_opt_ winrt::com_ptr<IZoneSet> activeSet) noexcept;
|
||||
static void MoveOrResizeEnd(_In_opt_ winrt::com_ptr<IZoneSet> activeSet) noexcept;
|
||||
static void CycleActiveZoneSet(_In_opt_ winrt::com_ptr<IZoneSet> activeSet, InputMode mode) noexcept;
|
||||
static void MoveOrResizeStarted(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept;
|
||||
static void MoveOrResizeEnd(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept;
|
||||
static void CycleActiveZoneSet(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows, InputMode mode) noexcept;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user