[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:
Seraphima Zykova
2022-10-31 13:44:25 +02:00
committed by GitHub
parent 6431ccd370
commit ff290eef9d
43 changed files with 2194 additions and 2242 deletions

View File

@@ -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);

View File

@@ -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())

View File

@@ -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);

View File

@@ -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,

View File

@@ -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);

View File

@@ -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
};

View File

@@ -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;

View File

@@ -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{};
}

View File

@@ -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();

View File

@@ -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 &&

View File

@@ -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)
{

View File

@@ -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;
};

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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);

View File

@@ -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);
}

View 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;
}

View 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{};
};

View 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);
}
}

View 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;
};

View File

@@ -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))
{

View File

@@ -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
{

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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 {};
}

View File

@@ -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{};

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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
};

View File

@@ -9,9 +9,9 @@
#include "util.h"
#include "Zone.h"
#include "ZoneSet.h"
#include "FancyZones.h"
#include "Colors.h"
#include "LayoutConfigurator.h"
class ZonesOverlay
{

View File

@@ -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));

View File

@@ -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;
};
};