Skip to content

Commit

Permalink
[FancyZones] Added persistence to app zone history (#3132)
Browse files Browse the repository at this point in the history
* Persist app zone history in a separate file

* Almost ready to be functionally tested

* Now all unit tests pass

* Bug fixes, it seems to work

* Various fixups

* Improved performance of FancyZones::UpdateWindowsPositions()
  • Loading branch information
ivan100sic committed May 26, 2020
1 parent bc9add7 commit 6f22c7a
Show file tree
Hide file tree
Showing 17 changed files with 341 additions and 873 deletions.
23 changes: 16 additions & 7 deletions src/modules/fancyzones/lib/FancyZones.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,10 +363,10 @@ FancyZones::WindowCreated(HWND window) noexcept
wil::unique_cotaskmem_string guidString;
if (SUCCEEDED(StringFromCLSID(activeZoneSet->Id(), &guidString)))
{
int zoneIndex = fancyZonesData.GetAppLastZoneIndex(window, zoneWindow->UniqueId(), guidString.get());
if (zoneIndex != -1)
std::vector<int> zoneIndexSet = fancyZonesData.GetAppLastZoneIndexSet(window, zoneWindow->UniqueId(), guidString.get());
if (zoneIndexSet.size())
{
m_windowMoveHandler.MoveWindowIntoZoneByIndex(window, monitor, zoneIndex, m_zoneWindowMap);
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, monitor, zoneIndexSet, m_zoneWindowMap);
break;
}
}
Expand Down Expand Up @@ -790,13 +790,22 @@ void FancyZones::UpdateZoneWindows() noexcept
void FancyZones::UpdateWindowsPositions() noexcept
{
auto callback = [](HWND window, LPARAM data) -> BOOL {
int i = static_cast<int>(reinterpret_cast<UINT_PTR>(::GetProp(window, ZONE_STAMP)));
if (i != 0)
size_t bitmask = reinterpret_cast<size_t>(::GetProp(window, MULTI_ZONE_STAMP));

if (bitmask != 0)
{
// i is off by 1 since 0 is special.
std::vector<int> indexSet;
for (int i = 0; i < std::numeric_limits<size_t>::digits; i++)
{
if ((1ull << i) & bitmask)
{
indexSet.push_back(i);
}
}

auto strongThis = reinterpret_cast<FancyZones*>(data);
std::unique_lock writeLock(strongThis->m_lock);
strongThis->m_windowMoveHandler.MoveWindowIntoZoneByIndex(window, nullptr, i - 1, strongThis->m_zoneWindowMap);
strongThis->m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, nullptr, indexSet, strongThis->m_zoneWindowMap);
}
return TRUE;
};
Expand Down
51 changes: 43 additions & 8 deletions src/modules/fancyzones/lib/JsonHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace
constexpr int c_blankCustomModelId = 0xFFFA;

const wchar_t* FANCY_ZONES_DATA_FILE = L"zones-settings.json";
const wchar_t* FANCY_ZONES_APP_ZONE_HISTORY_FILE = L"app-zone-history.json";
const wchar_t* DEFAULT_GUID = L"{00000000-0000-0000-0000-000000000000}";
const wchar_t* REG_SETTINGS = L"Software\\SuperFancyZones";

Expand Down Expand Up @@ -221,6 +222,7 @@ namespace JSONHelpers
{
std::wstring result = PTSettingsHelper::get_module_save_folder_location(L"FancyZones");
jsonFilePath = result + L"\\" + std::wstring(FANCY_ZONES_DATA_FILE);
appZoneHistoryFilePath = result + L"\\" + std::wstring(FANCY_ZONES_APP_ZONE_HISTORY_FILE);
}

json::JsonObject FancyZonesData::GetPersistFancyZonesJSON()
Expand All @@ -232,6 +234,18 @@ namespace JSONHelpers
auto result = json::from_file(save_file_path);
if (result)
{
if (!result->HasKey(L"app-zone-history"))
{
auto appZoneHistory = json::from_file(appZoneHistoryFilePath);
if (appZoneHistory)
{
result->SetNamedValue(L"app-zone-history", appZoneHistory->GetNamedArray(L"app-zone-history"));
}
else
{
result->SetNamedValue(L"app-zone-history", json::JsonArray());
}
}
return *result;
}
else
Expand Down Expand Up @@ -368,7 +382,7 @@ namespace JSONHelpers
SaveFancyZonesData();
}

int FancyZonesData::GetAppLastZoneIndex(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const
std::vector<int> FancyZonesData::GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const
{
std::scoped_lock lock{ dataLock };
auto processPath = get_process_path(window);
Expand All @@ -380,12 +394,12 @@ namespace JSONHelpers
const auto& data = history->second;
if (data.zoneSetUuid == zoneSetId && data.deviceId == deviceId)
{
return history->second.zoneIndex;
return history->second.zoneIndexSet;
}
}
}

return -1;
return {};
}

bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId)
Expand All @@ -410,7 +424,7 @@ namespace JSONHelpers
return false;
}

bool FancyZonesData::SetAppLastZone(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, int zoneIndex)
bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const std::vector<int>& zoneIndexSet)
{
std::scoped_lock lock{ dataLock };
auto processPath = get_process_path(window);
Expand All @@ -419,7 +433,7 @@ namespace JSONHelpers
return false;
}

appZoneHistoryMap[processPath] = AppZoneHistoryData{ .zoneSetUuid = zoneSetId, .deviceId = deviceId, .zoneIndex = zoneIndex };
appZoneHistoryMap[processPath] = AppZoneHistoryData{ .zoneSetUuid = zoneSetId, .deviceId = deviceId, .zoneIndexSet = zoneIndexSet };
SaveFancyZonesData();
return true;
}
Expand Down Expand Up @@ -669,8 +683,9 @@ namespace JSONHelpers
{
std::scoped_lock lock{ dataLock };
json::JsonObject root{};
json::JsonObject appZoneHistoryRoot{};

root.SetNamedValue(L"app-zone-history", SerializeAppZoneHistory());
appZoneHistoryRoot.SetNamedValue(L"app-zone-history", SerializeAppZoneHistory());
root.SetNamedValue(L"devices", SerializeDeviceInfos());
root.SetNamedValue(L"custom-zone-sets", SerializeCustomZoneSets());

Expand All @@ -681,6 +696,7 @@ namespace JSONHelpers
}

json::to_file(jsonFilePath, root);
json::to_file(appZoneHistoryFilePath, appZoneHistoryRoot);
}

void FancyZonesData::MigrateCustomZoneSetsFromRegistry()
Expand Down Expand Up @@ -817,7 +833,14 @@ namespace JSONHelpers
json::JsonObject result{};

result.SetNamedValue(L"app-path", json::value(appZoneHistory.appPath));
result.SetNamedValue(L"zone-index", json::value(appZoneHistory.data.zoneIndex));

json::JsonArray jsonIndexSet;
for (int index : appZoneHistory.data.zoneIndexSet)
{
jsonIndexSet.Append(json::value(index));
}

result.SetNamedValue(L"zone-index-set", jsonIndexSet);
result.SetNamedValue(L"device-id", json::value(appZoneHistory.data.deviceId));
result.SetNamedValue(L"zoneset-uuid", json::value(appZoneHistory.data.zoneSetUuid));

Expand All @@ -831,7 +854,19 @@ namespace JSONHelpers
AppZoneHistoryJSON result;

result.appPath = zoneSet.GetNamedString(L"app-path");
result.data.zoneIndex = static_cast<int>(zoneSet.GetNamedNumber(L"zone-index"));
if (zoneSet.HasKey(L"zone-index-set"))
{
result.data.zoneIndexSet = {};
for (auto& value : zoneSet.GetNamedArray(L"zone-index-set"))
{
result.data.zoneIndexSet.push_back(static_cast<int>(value.GetNumber()));
}
}
else if (zoneSet.HasKey(L"zone-index"))
{
result.data.zoneIndexSet = { static_cast<int>(zoneSet.GetNamedNumber(L"zone-index")) };
}

result.data.deviceId = zoneSet.GetNamedString(L"device-id");
result.data.zoneSetUuid = zoneSet.GetNamedString(L"zoneset-uuid");

Expand Down
13 changes: 10 additions & 3 deletions src/modules/fancyzones/lib/JsonHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ namespace JSONHelpers
{
std::wstring zoneSetUuid;
std::wstring deviceId;
int zoneIndex;
std::vector<int> zoneIndexSet;
};

struct AppZoneHistoryJSON
Expand Down Expand Up @@ -174,6 +174,12 @@ namespace JSONHelpers
{
return jsonFilePath;
}

inline const std::wstring& GetPersistAppZoneHistoryFilePath() const
{
return appZoneHistoryFilePath;
}

json::JsonObject GetPersistFancyZonesJSON();

std::optional<DeviceInfoData> FindDeviceInfo(const std::wstring& zoneWindowId) const;
Expand Down Expand Up @@ -236,9 +242,9 @@ namespace JSONHelpers
void UpdatePrimaryDesktopData(const std::wstring& desktopId);
void RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops);

int GetAppLastZoneIndex(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const;
std::vector<int> GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const;
bool RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId);
bool SetAppLastZone(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, int zoneIndex);
bool SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const std::vector<int>& zoneIndexSet);

void SetActiveZoneSet(const std::wstring& deviceId, const ZoneSetData& zoneSet);

Expand Down Expand Up @@ -268,6 +274,7 @@ namespace JSONHelpers

std::wstring activeDeviceId;
std::wstring jsonFilePath;
std::wstring appZoneHistoryFilePath;
};

FancyZonesData& FancyZonesDataInstance();
Expand Down
2 changes: 1 addition & 1 deletion src/modules/fancyzones/lib/Settings.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#define ZONE_STAMP L"FancyZones_zone"
#define MULTI_ZONE_STAMP L"FancyZones_zones"
#include <common/settings_objects.h>

struct Settings
Expand Down
6 changes: 3 additions & 3 deletions src/modules/fancyzones/lib/WindowMoveHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ void WindowMoveHandler::MoveSizeEnd(HWND window, POINT const& ptScreen, const st
pimpl->MoveSizeEnd(window, ptScreen, zoneWindowMap);
}

void WindowMoveHandler::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept
void WindowMoveHandler::MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept
{
pimpl->MoveWindowIntoZoneByIndexSet(window, monitor, { index }, zoneWindowMap);
pimpl->MoveWindowIntoZoneByIndexSet(window, monitor, indexSet, zoneWindowMap);
}

bool WindowMoveHandler::MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap)
Expand Down Expand Up @@ -256,7 +256,7 @@ void WindowMoveHandlerPrivate::MoveSizeEnd(HWND window, POINT const& ptScreen, c
}
else
{
::RemoveProp(window, ZONE_STAMP);
::RemoveProp(window, MULTI_ZONE_STAMP);

auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
if (monitor)
Expand Down
2 changes: 1 addition & 1 deletion src/modules/fancyzones/lib/WindowMoveHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class WindowMoveHandler
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
void MoveSizeEnd(HWND window, POINT const& ptScreen, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;

void MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
void MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap) noexcept;
bool MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle, const std::map<HMONITOR, winrt::com_ptr<IZoneWindow>>& zoneWindowMap);

private:
Expand Down
53 changes: 0 additions & 53 deletions src/modules/fancyzones/lib/Zone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,57 +20,16 @@ struct Zone : winrt::implements<Zone, IZone>
}

IFACEMETHODIMP_(RECT) GetZoneRect() noexcept { return m_zoneRect; }
IFACEMETHODIMP_(bool) IsEmpty() noexcept { return m_windows.empty(); };
IFACEMETHODIMP_(bool) ContainsWindow(HWND window) noexcept;
IFACEMETHODIMP_(void) AddWindowToZone(HWND window, HWND zoneWindow, bool stampZone) noexcept;
IFACEMETHODIMP_(void) RemoveWindowFromZone(HWND window, bool restoreSize) noexcept;
IFACEMETHODIMP_(void) SetId(size_t id) noexcept { m_id = id; }
IFACEMETHODIMP_(size_t) Id() noexcept { return m_id; }
IFACEMETHODIMP_(RECT) ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept;

private:
void SizeWindowToZone(HWND window, HWND zoneWindow) noexcept;
void StampZone(HWND window, bool stamp) noexcept;

RECT m_zoneRect{};
size_t m_id{};
std::map<HWND, RECT> m_windows{};
};

IFACEMETHODIMP_(bool) Zone::ContainsWindow(HWND window) noexcept
{
return (m_windows.find(window) != m_windows.end());
}

IFACEMETHODIMP_(void) Zone::AddWindowToZone(HWND window, HWND zoneWindow, bool stampZone) noexcept
{
WINDOWPLACEMENT placement;
::GetWindowPlacement(window, &placement);
::GetWindowRect(window, &placement.rcNormalPosition);
m_windows.emplace(std::pair<HWND, RECT>(window, placement.rcNormalPosition));

SizeWindowToZone(window, zoneWindow);
if (stampZone)
{
StampZone(window, true);
}
}

IFACEMETHODIMP_(void) Zone::RemoveWindowFromZone(HWND window, bool restoreSize) noexcept
{
auto iter = m_windows.find(window);
if (iter != m_windows.end())
{
m_windows.erase(iter);
StampZone(window, false);
}
}

void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
{
SizeWindowToRect(window, ComputeActualZoneRect(window, zoneWindow));
}

static BOOL CALLBACK saveDisplayToVector(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
{
reinterpret_cast<std::vector<HMONITOR>*>(data)->emplace_back(monitor);
Expand Down Expand Up @@ -161,18 +120,6 @@ RECT Zone::ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept
return newWindowRect;
}

void Zone::StampZone(HWND window, bool stamp) noexcept
{
if (stamp)
{
SetProp(window, ZONE_STAMP, reinterpret_cast<HANDLE>(m_id));
}
else
{
RemoveProp(window, ZONE_STAMP);
}
}

winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect) noexcept
{
return winrt::make_self<Zone>(zoneRect);
Expand Down
29 changes: 0 additions & 29 deletions src/modules/fancyzones/lib/Zone.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,6 @@ interface __declspec(uuid("{8228E934-B6EF-402A-9892-15A1441BF8B0}")) IZone : pub
* @returns Zone coordinates (top-left and bottom-right corner) represented as RECT structure.
*/
IFACEMETHOD_(RECT, GetZoneRect)() = 0;
/**
* @returns Boolean indicating if zone is empty or there are windows assigned to it.
*/
IFACEMETHOD_(bool, IsEmpty)() = 0;
/**
* @param window Window handle.
* @returns Boolean indicating if specified window is assigned to the zone.
*/
IFACEMETHOD_(bool, ContainsWindow)(HWND window) = 0;
/**
* Assign single window to this zone.
*
* @param window Handle of window which should be assigned to zone.
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
* current monitor desktop work area.
* @param stampZone Boolean indicating weather we should add special property on the
* window. This property is used on display change to rearrange windows
* to corresponding zones.
*/
IFACEMETHOD_(void, AddWindowToZone)(HWND window, HWND zoneWindow, bool stampZone) = 0;
/**
* Remove window from this zone (if it is assigned to it).
*
* @param window Handle of window to be removed from this zone.
* @param restoreSize Boolean indicating that window should fall back to dimensions
* before assigning to this zone.
*/
IFACEMETHOD_(void, RemoveWindowFromZone)(HWND window, bool restoreSize) = 0;
/**
* @param id Zone identifier.
*/
Expand All @@ -45,7 +17,6 @@ interface __declspec(uuid("{8228E934-B6EF-402A-9892-15A1441BF8B0}")) IZone : pub
* @returns Zone identifier.
*/
IFACEMETHOD_(size_t, Id)() = 0;

/**
* Compute the coordinates of the rectangle to which a window should be resized.
*
Expand Down

0 comments on commit 6f22c7a

Please sign in to comment.